summaryrefslogtreecommitdiffstats
path: root/tcllib/modules/stringprep/stringprep.tcl
diff options
context:
space:
mode:
Diffstat (limited to 'tcllib/modules/stringprep/stringprep.tcl')
-rw-r--r--tcllib/modules/stringprep/stringprep.tcl278
1 files changed, 278 insertions, 0 deletions
diff --git a/tcllib/modules/stringprep/stringprep.tcl b/tcllib/modules/stringprep/stringprep.tcl
new file mode 100644
index 0000000..70d14f6
--- /dev/null
+++ b/tcllib/modules/stringprep/stringprep.tcl
@@ -0,0 +1,278 @@
+# stringprep.tcl -*- tcl -*-
+#
+# Implementation of RFC 3454 "Preparation of Internationalized Strings"
+#
+# Copyright (c) 2007 Sergei Golovan
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# RCS: @(#) $Id: stringprep.tcl,v 1.2 2009/11/02 00:26:44 patthoyts Exp $
+
+package require stringprep::data 1.0
+package require unicode 1.0
+
+namespace eval ::stringprep {
+ variable profiles
+ array unset profiles
+}
+
+########################################################################
+# Register new stringprep profile
+
+proc ::stringprep::register {profile args} {
+ variable profiles
+
+ array set props [list -mapping "" \
+ -normalization "" \
+ -prohibited 0 \
+ -prohibitedList {} \
+ -prohibitedCommand "" \
+ -prohibitedBidi 0]
+
+ foreach {opt val} $args {
+ switch -- $opt {
+ -mapping {
+ foreach tab $val {
+ switch -- $tab {
+ B.1 - B.2 - B.3 {}
+ default {
+ return -code error \
+ "::stringprep::register -mapping: Only\
+ B.1, B.2, B.3 tables are allowed"
+ }
+ }
+ }
+ set props(-mapping) $val
+ }
+ -normalization {
+ switch -- $val {
+ D - C - KD - KC - "" {
+ set props(-normalization) $val
+ }
+ default {
+ return -code error \
+ "::stringprep::register -normalization: Only\
+ D, C, KD, KC or empty normalization is allowed"
+ }
+ }
+ }
+ -prohibited {
+ set mask 0
+ set c39count 0
+ foreach tab $val {
+ switch -- $tab {
+ A.1 { set mask [expr {$mask | $data::A1Mask}] }
+ C.1.1 { set mask [expr {$mask | $data::C11Mask}] }
+ C.1.2 { set mask [expr {$mask | $data::C12Mask}] }
+ C.2.1 { set mask [expr {$mask | $data::C21Mask}] }
+ C.2.2 { set mask [expr {$mask | $data::C22Mask}] }
+ C.3 - C.4 - C.5 - C.6 - C.7 - C.8 -
+ C.9 { incr c39count }
+ default {
+ return -code error \
+ "::stringprep::register -prohibited: Only\
+ tables A.1, C.* are allowed to prohibit"
+ }
+ }
+ }
+ if {$c39count > 0 && $c39count < 7} {
+ return -code error \
+ "::stringprep::register -prohibited: Must prohibit\
+ all C.3--C.9 tables or none of them"
+ }
+ if {$c39count > 0} {
+ set mask [expr {$mask | $data::C39Mask}]
+ }
+ set props(-prohibited) $mask
+ }
+ -prohibitedList {
+ if {[catch {
+ foreach uc $val {
+ if {![string is integer -strict $uc]} {
+ error not_integer
+ } else {
+ lappend props(-prohibitedList) [expr {$uc}]
+ }
+ }}]} {
+ return -code error \
+ "::stringprep::register -prohibitedList: List\
+ of integers expected"
+ }
+ }
+ -prohibitedCommand {
+ set props(-prohibitedCommand) $val
+ }
+ -prohibitedBidi {
+ if {[string is true -strict $val]} {
+ set props(-prohibitedBidi) 1
+ } elseif {[string is false -strict $val]} {
+ set props(-prohibitedBidi) 0
+ } else {
+ return -code error \
+ "::stringprep::register -prohibitedBidi: Boolean\
+ value expected"
+ }
+ }
+ }
+ }
+ set profiles($profile) [array get props]
+}
+
+########################################################################
+# Register identity profile
+
+::stringprep::register none \
+ -mapping {} \
+ -normalization {} \
+ -prohibited {} \
+ -prohibitedBidi 0
+
+########################################################################
+
+proc ::stringprep::stringprep {profile str} {
+ variable profiles
+
+ if {![info exists profiles($profile)]} {
+ return -code error invalid_profile
+ }
+
+ set uclist [::unicode::fromstring $str]
+
+ set uclist [map $profile $uclist]
+ if {[llength $uclist] == 0} {
+ return ""
+ }
+
+ set uclist [normalize $profile $uclist]
+
+ if {[prohibited $profile $uclist]} {
+ return -code error prohibited_character
+ }
+
+ if {[prohibited_bidi $profile $uclist]} {
+ return -code error prohibited_bidi
+ }
+
+ ::unicode::tostring $uclist
+}
+
+########################################################################
+
+proc ::stringprep::compare {profile str1 str2} {
+ string compare [stringprep $profile $str1] [stringprep $profile $str2]
+}
+
+########################################################################
+# Mapping (section 3)
+
+proc ::stringprep::map {profile uclist} {
+ variable profiles
+
+ array set props $profiles($profile)
+
+ set B1Mask 0
+ set B3Mask 0
+ set B2 0
+ foreach tab $props(-mapping) {
+ switch -- $tab {
+ B.1 { set B1Mask $data::B1Mask }
+ B.2 { set B2 1 }
+ B.3 { set B3Mask $data::B3Mask }
+ }
+ }
+
+ set res {}
+ foreach uc $uclist {
+ set info [data::GetUniCharInfo $uc]
+
+ if {$info & $B1Mask} {
+ # Map to nothing
+ continue
+ }
+
+ if {$B2 || ($info & $B3Mask)} {
+ if {$info & $data::MCMask} {
+ set res [concat $res [data::GetMC $info]]
+ } else {
+ lappend res [expr {$uc + [data::GetDelta $info]}]
+ }
+ } else {
+ lappend res $uc
+ }
+ }
+ return $res
+}
+
+########################################################################
+# Normalization (section 4)
+
+proc ::stringprep::normalize {profile uclist} {
+ variable profiles
+
+ array set props $profiles($profile)
+
+ switch -- $props(-normalization) {
+ D - C - KD - KC {
+ return [::unicode::normalize $props(-normalization) $uclist]
+ }
+ default { return $uclist }
+ }
+}
+
+########################################################################
+# Prohibit (section 5)
+
+proc ::stringprep::prohibited {profile uclist} {
+ variable profiles
+
+ array set props $profiles($profile)
+
+ foreach uc $uclist {
+ set info [data::GetUniCharInfo $uc]
+ if {($info & $props(-prohibited)) || \
+ [lsearch -exact $props(-prohibitedList) $uc] >= 0} {
+ return 1
+ } elseif {$props(-prohibitedCommand) != "" && \
+ [uplevel #0 $props(-prohibitedCommand) [list $uc]]} {
+ return 1
+ }
+ }
+ return 0
+}
+
+########################################################################
+# Check bidi (section 6)
+
+proc ::stringprep::prohibited_bidi {profile uclist} {
+ variable profiles
+
+ array set props $profiles($profile)
+
+ if {!$props(-prohibitedBidi)} {
+ return 0
+ }
+
+ set info [data::GetUniCharInfo [lindex $uclist 0]]
+ set first_ral [expr {$info & $data::D1Mask}]
+ set last_ral 0
+ set have_ral 0
+ set have_l 0
+ foreach uc $uclist {
+ set info [data::GetUniCharInfo $uc]
+ set last_ral [expr {$info & $data::D1Mask}]
+ set have_ral [expr {$have_ral || $last_ral}]
+ set have_l [expr {$have_l || ($info & $data::D2Mask)}]
+ }
+ if {$have_ral && (!$first_ral || !$last_ral || $have_l)} {
+ return 1
+ } else {
+ return 0
+ }
+}
+
+########################################################################
+
+package provide stringprep 1.0.1
+
+########################################################################