Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Pulling changes from trunk |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | hypnotoad |
Files: | files | file ages | folders |
SHA3-256: |
17ac21f611de36967f1a52e2eddb86a8 |
User & Date: | hypnotoad 2018-12-05 16:27:08.697 |
Context
2018-12-06
| ||
03:00 | Pulling changes from trunk check-in: ed4cd1b325 user: hypnotoad tags: hypnotoad | |
2018-12-05
| ||
16:27 | Pulling changes from trunk check-in: 17ac21f611 user: hypnotoad tags: hypnotoad | |
2018-12-03
| ||
16:51 | Removed the comments from the dynamically generated bodies. They just clog up scm checkins, and if someone is curious, the table itself is in the build directory check-in: f8025492fa user: hypnotoad tags: pooryorick | |
15:15 | Added wcswidth calculations to the textutil modules. This mechanism allows screen rendering agents to know how many columns to reserve for certain (predominately Eastern) characters. Per: https://en.wikipedia.org/wiki/Halfwidth_and_fullwidth_forms check-in: 28aec2e16b user: hypnotoad tags: hypnotoad | |
Changes
Added modules/chan/base.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | #! /usr/bin/env tclsh # # ## ### ##### ######## ############# # copyright # # 2018 # # Poor Yorick # # ## ### ##### ######## ############# proc .init {_ channame args} { $_ .vars chan close if {$channame ni [::chan names]} { error [list {unknown channel} $channame] } set chan $channame $_ .ondelete [list ::apply {{_ channame} { $_ .vars close if {$close} { ::close $channame } }} $_ $channame] set close 1 if {[llength $args]} { $_ configure {*}$args } return $_ } .my .method .init proc configure {_ args} { $_ .vars chan if {[llength $args] == 1} { lassign $args key switch $key { -chan { return $chan } } set res [::chan configure $chan {*}$args] } elseif {[llength $args]} { dict size $args foreach {key val} $args[set args {}] { switch $key { -chan { set chan $val } -close { $_ $ close [expr {!!$val}] } default { lappend args $key $val } } } if {[llength $args]} { ::chan configure $chan {*}$args } set res {} } else { set res [list {*}[::chan configure $chan {*}$args] -chan $chan] } return $res } .my .method configure proc copy {_ target} { ::chan copy [$_ $ chan] $target } .my .method copy proc gets {_ args} { uplevel 1 [list ::gets [$_ $ chan] {*}$args] } .my .method gets proc pending {_ args} { uplevel 1 [list ::pending {*}$args [$_ $ chan]] } .my .method pending proc puts {_ args} { uplevel 1 [list ::puts {*}[lrange $args 0 end-1] [$_ $ chan] {*}[ lrange $args end end]] } .my .method puts proc read {_ args} { uplevel 1 [list ::read {*}[lrange $args 0 end-1] [$_ $ chan] {*}[ lrange $args end end]] } .my .method read apply [list {} { foreach name { blocked close eof event flush names pop posteven push seek tell truncate } { proc $name {_ args} [string map [ list @name@ [list $name]] { ::chan @name@ [$_ $ chan] {*}$args }] .my .method $name } } [namespace current]] |
Added modules/chan/base.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #! /usr/bin/env tclsh source [file join \ [file dirname [file dirname [file dirname [ file normalize [info script]/...]]]]/devtools/testutilities.tcl] testsNeedTcl 8.5 testsNeedTcltest 1.0- package require tcl::chan::string package require {chan base} namespace import ::tcllib::chan::base proc main {} { variable done set data1 abcdefghijklmnopqrstuvwxyz test chan-object-closeondelete { } -body { base .new chan1 [::tcl::chan::string $data1] set name [chan1 $ chan] lappend res [expr {$name in [chan names]}] chan1 close lappend res [expr {$name in [chan names]}] } -result {1 0} testsuiteCleanup set done 1 } after 0 coroutine [info cmdcount]_main main vwait [namespace current]::done |
Added modules/chan/coroutine.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #! /usr/bin/tclsh # # ## ### ##### ######## ############# # copyright # # 2018 # # Poor Yorick # # ## ### ##### ######## ############# package require coroutine proc [namespace current] chan { if {![string match ::* $chan]} { set chan [uplevel 1 [list ::namespace which $chan]] } $chan .specialize foreach name { gets read } { $chan .method $name coroutine::$name } return $chan } proc gets {_ args} { $_ .vars chan tailcall ::coroutine::util::gets $chan {*}$args } proc read {_ args} { $_ .vars chan tailcall ::coroutine::util::read $chan {*}$args } |
Added modules/chan/coroutine.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #! /usr/bin/tclsh source [file join \ [file dirname [file dirname [file dirname [ file normalize [info script]/...]]]]/devtools/testutilities.tcl] testsNeedTcl 8.5 testsNeedTcltest 1.0- package require {chan base} package require {chan coroutine} package require tcl::chan::string proc main {} {try { variable done test getslimit-configure {} -body { ::tcllib::chan::base .new chan1 ::tcllib::chan::coroutine chan1 chan1 .init [::tcl::chan::string \ {Curiosity governs the first moment}] chan1 read } -cleanup { rename chan1 {} } -result {Curiosity governs the first moment} testsuiteCleanup } finally { set done 1 }} after 0 coroutine [info cmdcount]_main main vwait [namespace current]::done |
Added modules/chan/getslimit.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | #! /usr/bin/env tclsh # # ## ### ##### ######## ############# # copyright # # 2018 # # Poor Yorick # # ## ### ##### ######## ############# variable buf bufcount eof getslimit proc [namespace current] chan { if {![string match ::* $chan]} { set chan [uplevel 1 [list ::namespace which $chan]] } $chan .specialize foreach name { .init configure eof gets read } { $chan .method $name getslimit::$name } return $chan } proc .init {_ args} { $_ .vars eof buf bufcount getslimit set eof 0 set buf {} set bufcount 0 set getslimit -1 uplevel 1 [list $_ .prototype .init {*}$args] } proc configure {_ args} { $_ .vars getslimit if {[llength $args] == 1} { switch [lindex $args 0] { -getslimit { set res $getslimit } default { set res [uplevel 1 [list $_ .prototype configure {*}$args]] } } } elseif {[llength $args]} { dict size $args foreach {key val} $args[set args {}] { if {$key eq {-getslimit}} { set getslimit $val } else { lappend args $key $val } } if {[llength $args]} { uplevel 1 [list $_ .prototype configure {*}$args] } set res {} } else { set res [list {*}[uplevel 1 [ list $_ .prototype configure {*}$args]] -getslimit $getslimit] } return $res } proc eof _ { $_ .vars bufcount eof return [expr {$eof || ( [$_ .prototype eof] && $bufcount == 0 )}] } proc gets {_ args} { $_ .vars buf bufcount chan eof getslimit switch [llength $args] { 1 { lassign $args varname upvar 1 $varname resvar } 0 {} default { #this is just to generate the error message ::gets [$_ $ chan] {*}$args } } if {$eof} { if {[info exists varname]} { set resvar {} return -1 } return {} } if {[string first \n $buf] < 0 && ![::eof $chan]} { if {$getslimit >= 0} { append buf [$_ read $getslimit] } else { append buf [$_ read] } } if {[regexp {^(.*?)\n(.*)$} $buf -> res remainder]} { set buf $remainder set bufcount [expr {$bufcount - [string length $res] - 1}] } else { # must be at eof set res $buf set buf {} set bufcount 0 if {[::eof $chan]} { set eof 1 } } if {[llength $args]} { set args [lassign $args[set args {}] varname] set resvar $res if {$res eq {} && $eof} { return -1 } else { return [string length $res] } } else { return $res } } proc read {_ args} { $_ .vars buf eof bufcount if {$eof} { return {} } if {$bufcount} { if {[llength $args]} { lassign $args size if {$size <= $bufcount} { set res [string range $buf 0 [expr {$size - 1}]] set buf [string range $buf $size end] incr bufcount -[string length $res] } else { set readsize [expr {$size - $bufcount}] set res $buf[set buf {}][$_ .prototype read $readsize] set bufcount 0 } } else { set bufcount 0 set res $buf[set buf {}][$_ .prototype read {*}$args] } } else { set res [$_ .prototype read {*}$args] } return $res } package provide tcllib::chan::getslimit 1 |
Added modules/chan/getslimit.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | #! /usr/bin/tclsh source [file join \ [file dirname [file dirname [file dirname [ file normalize [info script]/...]]]]/devtools/testutilities.tcl] testsNeedTcl 8.5 testsNeedTcltest 1.0- package require {chan base} package require {chan getslimit} package require tcl::chan::memchan package require tcl::chan::string namespace import ::tcllib::chan::getslimit proc main {} { variable done set data1 abcdefghijklmnopqrstuvwxyz test getslimit-configure {} -body { ::tcllib::chan::base .new chan1 getslimit chan1 chan1 .init [::tcl::chan::memchan] -getslimit 33 chan1 configure -getslimit } -cleanup { rename chan1 {} } -result 33 test getslimit-under { } -body { ::tcllib::chan::base .new chan1 getslimit chan1 chan1 .init [::tcl::chan::string $data1] -getslimit 1000 chan1 gets } -cleanup { rename chan1 {} } -result $data1 test getslimit-exceeded { } -body { ::tcllib::chan::base .new chan1 getslimit chan1 chan1 .init [::tcl::chan::string [ string repeat $data1 100]] -getslimit 10 chan1 gets } -cleanup { rename chan1 {} } -result {abcdefghij} test getslimit-read { read is unaffacted by the limit } -body { ::tcllib::chan::base .new chan1 getslimit chan1 chan1 .init [::tcl::chan::string $data1] chan1 configure -getslimit 10 lappend res [chan1 read 15] lappend res [chan1 read 3] lappend res [chan1 read 1] lappend res [chan1 read] lappend res [chan1 eof] lappend res [chan1 read] lappend res [chan1 eof] } -cleanup { rename chan1 {} } -result {abcdefghijklmno pqr s tuvwxyz 1 {} 1} testsuiteCleanup set done 1 } after 0 coroutine [info cmdcount]_main main vwait [namespace current]::done |
Added modules/chan/pkgIndex.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #! /usr/bin/env tclsh package ifneeded {chan getslimit} 0.1 [list ::apply {dir { package require ego namespace eval ::tcllib::chan::getslimit [list ::source $dir/getslimit.tcl] package provide {chan getslimit} 0.1 namespace eval ::tcllib::chan { namespace export getslimit } }} $dir] package ifneeded {chan base} 0.1 [list ::apply {dir { package require ego tcllib::ego .new ::tcllib::chan::base ::tcllib::chan::base .eval [list ::source $dir/base.tcl] namespace eval ::tcllib::chan { namespace export base } package provide {chan base} 0.1 }} $dir] package ifneeded {chan coroutine} 0.1 [list ::apply {dir { package require ego namespace eval ::tcllib::chan::coroutine [list ::source $dir/coroutine.tcl] package provide {chan coroutine} 0.1 namespace eval ::tcllib::chan { namespace export coroutine } }} $dir] |
Changes to modules/devtools/testutilities.tcl.
︙ | ︙ | |||
469 470 471 472 473 474 475 | proc useLocal {fname pname args} { set nsname ::$pname if {[llength $args]} {set nsname [lindex $args 0]} package forget $pname catch {namespace delete $nsname} | | > > | < < < < | | > | | 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 | proc useLocal {fname pname args} { set nsname ::$pname if {[llength $args]} {set nsname [lindex $args 0]} package forget $pname catch {namespace delete $nsname} set unique [info cmdcount] set cresvar [namespace current]::${unique}_cres set coptsvar [namespace current]::${unique}_copts if {[uplevel 1 [list ::catch [list useLocalFile $fname] $cresvar $coptsvar]]} { puts " Aborting the tests found in \"[file tail [info script]]\"" } else { puts "$::tcllib::testutils::tag [list $pname] [package present $pname]" } return -options [set $coptsvar][unset $coptsvar] [set $cresvar][unset $cresvar] } proc useLocalKeep {fname pname args} { set nsname ::$pname if {[llength $args]} {set nsname [lindex $args 0]} package forget $pname |
︙ | ︙ | |||
528 529 530 531 532 533 534 | return -code return } return } proc testing {script} { InitializeTclTest | | | > > | < < < | < < | | 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | return -code return } return } proc testing {script} { InitializeTclTest set ::tcllib::testutils::tag * set unique [info cmdcount] set cresvar [namespace current]::${unique}_cres set coptsvar [namespace current]::${unique}_copts uplevel 1 [list ::catch $script $cresvar $coptsvar] return -options [set $coptsvar][unset $coptsvar] [set $cresvar][ unset $cresvar] } proc useTcllibC {} { set index [tcllibPath tcllibc/pkgIndex.tcl] if {![file exists $index]} { # Might have an external tcllibc if {![catch { |
︙ | ︙ |
Changes to modules/dicttool/build/core.tcl.
1 2 3 4 5 6 | namespace eval ::dicttool {} namespace eval ::tcllib {} ### # Because many features in this package may be added as # commands to future tcl cores, or be provided in binary | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | namespace eval ::dicttool {} namespace eval ::tcllib {} ### # Because many features in this package may be added as # commands to future tcl cores, or be provided in binary # form by packages, I need a declarative way of saying # [emph {Create this command if there isn't one already}]. # The [emph ninja] argument is a script to execute if the # command is created by this mechanism. ### proc ::tcllib::PROC {name arglist body {ninja {}}} { if {[info commands $name] ne {}} return proc $name $arglist $body |
︙ | ︙ |
Added modules/ego/ego.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | #! /bin/env tclsh # # ## ### ##### ######## ############# # copyright # # 2018 # # Poor Yorick # # ## ### ##### ######## ############# namespace ensemble create namespace export * proc .method {_ name args} { if {![llength $args]} { lappend args $name } set args [linsert $args[set args {}] 1 $_] set map [namespace ensemble configure $_ -map] dict set map $name $args uplevel 1 [list ::namespace ensemble configure $_ -map $map] return } .method [namespace current] .method proc $ {_ name args} { namespace upvar [$_ .namespace] $name var if {[llength $args]} { if {[llength $args] > 1} { error [list {wrong # args}] } set var [lindex $args 0] } return $var } .method [namespace current] $ proc .as {_ other name args} { set map [namespace ensemble configure $_ -map] set cmd [dict get $map $name] if {[lindex $name 1] ne $_} { error [list {not a method} $name] } set cmd [lreplace $cmd 1 1 $other] ::tailcall {*}$cmd } .method [namespace current] .as proc .eval {_ args} { ::tailcall ::namespace eval [$_ .namespace] {*}$args } .method [namespace current] .eval proc .insert {_ name} { set unknown1 [namespace ensemble configure $_ -unknown] set prototype1 [namespace ensemble configure $_ -prototype] if {[llength $unknown1]} { namespace ensemble configure $name -prototype $prototype1 \ -unknown $unknown1 } namespace enemble configure $_ -prototype [list ::lindex $name] -unknown $unknown1 return } proc .name _ { return $_ } .method [namespace current] .name proc .namespace _ { namespace ensemble configure $_ -namespace } .method [namespace current] .namespace proc .new {_ name args} { global env set ns [uplevel 1 [list ::namespace eval $name { ::namespace ensemble create ::variable configured 0 ::namespace current }]] ::trace add command $ns delete [list ::apply {{ns oldname newname op} { if {[namespace exists $ns]} { namespace delete $ns } }} $ns] set prototype $_ set map [namespace ensemble configure $_ -map] set prototypes {} while {[dict exists $map .prototype]} { set prototypes [list $map {*}$prototypes[set prototypes {}]] lassign [dict get $map .prototype] prototype set map [namespace ensemble configure $prototype -map] } set map {} foreach {key val} [namespace ensemble configure $prototype -map] { if {$key ne {.prototype}} { if {[lindex $val 1] eq $_} { set val [lreplace $val[set val {}] 1 1 $ns] } } else { error [list {how did we get to here?}] } lappend map $key $val } namespace ensemble configure $ns -map $map set prototype $ns foreach map $prototypes { $ns .specialize dict unset map .prototype dict for {name cmd} $map { if {[lindex $cmd 1] eq $_} { # remove the original name from index 1 because .method is # going to add it back $ns .method $name {*}[lreplace $cmd[set cmd {}] 1 1] } else { $ns .routine $name {*}$cmd } } } interp alias {} ${ns}::.my {} $ns if {[llength $args]} { tailcall $ns .init {*}$args } else { return $ns } } .method [namespace current] .new proc .ondelete {_ trace args} { if {[llength $args] == 1} { lassign $args script trace remove command $_ delete $trace set trace {} if {$script ne {}} { set trace [list apply {{script args} { try $script }} $script] trace add command $_ delete $trace } $_ .method .ondelete .ondelete $trace } elseif {[llength $args]} { error [list {wrong # args}] } return $trace } .method [namespace current] .ondelete .ondelete {} proc .routine {_ name args} { if {![llength $args]} { lappend args $name } set map [namespace ensemble configure $_ -map] dict set map $name $args uplevel 1 [list ::namespace ensemble configure $_ -map $map] return } .method [namespace current] .routine proc .specialize {_ args} { set ns [$_ .namespace] while {[namespace which [set name ${ns}::[ info cmdcount]_prototype]] ne {}} {} rename $_ $name set new [namespace eval ${ns} [ list namespace ensemble create -command $_ -map [list \ .prototype [list $name] ] -unknown [ list ::apply {{_ name args} { set prototype [lindex [dict get [namespace ensemble configure $_ -map] .prototype] 0] list $prototype $name }}]]] ::trace add command $new delete [list ::apply {{ns oldname newname op} { if {[namespace exists $ns]} { namespace delete $ns } }} $ns] return } .method [namespace current] .specialize proc .vars {_ args} { set vars {} foreach arg $args { lassign $arg source target if {[llength $arg] == 1} { set target $source } lappend vars $source $target } uplevel 1 [list ::namespace upvar $_ {*}$vars] } .method [namespace current] .vars proc = {_ name val} { set [$_ .namespace]::$name $val } .method [namespace current] = |
Added modules/ego/pkgIndex.tcl.
> > > > > > | 1 2 3 4 5 6 | #! /usr/bin/env tclsh package ifneeded ego 0.1 [list ::apply {dir { namespace eval ::tcllib::ego [list ::source $dir/ego.tcl] package provide ego 0.1 }} $dir] |
Changes to modules/mime/mime.man.
︙ | ︙ | |||
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | [keywords {rfc 821}] [keywords {rfc 822}] [keywords {rfc 2045}] [keywords {rfc 2046}] [keywords {rfc 2049}] [keywords smtp] [copyright {1999-2000 Marshall T. Rose}] [moddesc {Mime}] [titledesc {Manipulation of Internet messages}] [category {Text processing}] [require Tcl 8.5] [require mime [opt 1.6]] [description] [para] Provides commands to create and manipulate Internet messages. [list_begin definitions] | > | < < < < < < | > > | > > > > > > > > > > > > > > > | | > | > | | | | | | > | > | > | > | | | > > > > > > > > > > > > > > | | | | | | | > | > | > > | > | | < > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 | [keywords {rfc 821}] [keywords {rfc 822}] [keywords {rfc 2045}] [keywords {rfc 2046}] [keywords {rfc 2049}] [keywords smtp] [copyright {1999-2000 Marshall T. Rose}] [copyright {2018 Poor Yorick}] [moddesc {Mime}] [titledesc {Manipulation of Internet messages}] [category {Text processing}] [require Tcl 8.5] [require mime [opt 1.6]] [description] [para] Provides commands to create and manipulate Internet messages. [list_begin definitions] [call [cmd ::mime::.new] [arg name] [opt [arg options]]] Parses a message, creates an routine named [arg name] as a handle for the message, and returns the name of the new routine. If [arg name] is the empty string, a name is automatically generated. One and only one of [ option -chan ], [ option -file ], or [ option -string ] must be provided as the source of the input. Optional arguments are processed such that later arguments override earlier arguments. For example, provide [option -addcontentid] [const 1] after [option "-spec [const http]"] has disabled that option. The optional arguments are [list_begin definitions] [def "[option -addcontentid] [arg boolean]"] Indicates whether a [const Content-ID] header field should be added to the message. By default, this header is added to non-canonical messages. [def "[option -addmimeversion] [arg boolean]"] Indicates whether a [const MIME-Version] header should be added to non-canonical messages. By default, this header is added to non-canonical messages. [def "[option -canonical] [arg type/subtype]"] The input message is the body only and is in the format specified by the provided [ arg type/subtype ], and therefore should not be parsed. [def "[option -chan] [arg channel]"] The name of an ensemble command providing the standard channel commands. [def "[option -encoding] [arg encoding]"] sets the [const Content-Transfer-Encoding]. [def "[option -file] [arg string]"] The file providing the input message. [def "[option -headers] [arg headers]"] [arg headers] is a multidict of headers. [def "[option -spec] [arg spec]"] [arg spec] is one of [const mime] (the default) or [const http], indicating what specification to conform to. For [const mime], the default [const Content-Type] is [const text/plain] and the character set is assumed to be [const US-ASCII]. For [const http], the default [const Content-Type] is [const text/html], the [const charset] is [const utf-8], and [const MIME-Version] and [const Content-ID] headers are by default not automatically added. [def "[option -params] [arg params]"] [arg params] is a multidict (a dictionary where the keys may not be unique) of parameters for the [ const Content-Type ] header. [def "[option -parts] [arg parts]"] [arg parts] is list of tokens for messages that comprise a [ const multipart/mixed ] message body. [def "[option -string] [arg string]"] The string providing the input message. [list_end] [call [arg message] [method body] [opt [option -decode]] [opt "[option -blocksize] [arg octets]"]"] Sets to [const 0] the cursor for the channel holding the body of the message and returns the name of a channel command for that channel. [para] If [option -blocksize] is provided, returns a command that itself returns up to the next [arg octets] of the message each time it's called, and returns a code of [const break] when finished, deleting itself as well. If [arg octets] is the empty string, a default value is used. [para] [option -decode] converts the message body from the character set it is encoded in. [call [arg message] [method {cookie delete}] [arg args]] Deletes the specifed cookie. [arg args] is the same as for [method {cookie set}], except that [arg -value] is not needed and [arg -expires] is ignored. [call [arg message] [method {cookie set}] [arg name] [arg value] [arg args]] Sets a cookie header. [arg args] is a dictionary options: [list_begin definitions] [def "[option expires] [arg date]"] [def "[option path] [arg {path restriction}]"] [def "[option domain] [arg {domain restriction}]"] [def "[option httponly] [arg boolean]"] [list_end] [call [arg message] [method .destroy] [opt "[option -subordinates] [const all] | [const dynamic] | [const none]"]] Destroys the message and returns the empty string. [para] [option -subordinates] specifies which messages comprising the body should also be destroyed. The default value of [const dynamic] indicates all component messages that were created while parsing a message. [const all] indicates all component messages. [const none] indicates that no component messages should be destroyed. [call [arg message] [method header] [method serialize] [arg value] [arg parameters]] Returns the the serialization of a header. [call [arg message] [method header] [method get] [opt "[arg key] | [option -names]"]] Returns the header of a message as a multidict where each value is a list containing the header value and a dictionary of parameters for that header. [para] If [arg name] is provided, returns the value and parameters of the last entry matching that name, without regard to case. [para] If [option -names] is provided, returns a list of all header names. [call [arg message] [method header] [method set] [arg {name value}] [ \ opt "[arg parameters] [opt "[option -mode] [const write] | [\ const append] | [const delete]"]"]] [arg parameters] is a dictionary of parameters for the header. If parameters contains an odd number of items, the last item is a list of flag parameters. If [const append] is provided, creates a new header named [arg name] with the value of [arg value] and any provided [arg parameters]. If [const write] is provided, first deletes any existing headers matching [arg name]. If [const delete] is provided, deletes any existing header matching [arg name]. Returns a list of strings containing the previous value associated with the key. [para] The value for [option -mode] is one of: [list_begin definitions] [def [const write]] The [arg key]/[arg value] is either created or overwritten (the default). [def [const append]] Appends a new [arg key]/[arg value]. [def [const delete]] Removes all values associated with the key. [arg value] is ignored. [list_end] [call [arg message] [method property] [opt "[arg name] | [option -names]"]] Returns a dictionary of message properties. If [arg name] is provided, only the corresponding value is returned. If [option -names] is provided, a list of all property names is returned. [para] properties: [list_begin definitions] [def [const content]] The type/subtype of the content [def [const encoding]] The "Content-Transfer-Encoding" [def [const params]] A list of "Content-Type" parameters [def [const parts]] A list of tokens for messages that comprise a multipart body. Only exists if there are any such messages. [def [const size]] The approximate size of the unencoded content. [list_end] [call [arg message] [method serialize] [opt [option -level]] [ opt "[option -chan] [arg channel]"]] Returns the serialization of the message. If [option -chan] is provided, writes the serialization to [arg channel] and returns the empty string. [option -level], if provided, indicates the level of the part in the message hierarchy. The [const MIME-Version] header is only included at level [const 0]. [call [cmd ::mime::datetime] ([arg time] | [option -now]) [arg property]] Returns the [arg property] of [arg time], which is an 822-style date-time value. [para] Available properties and their ranges are: [list_begin definitions] |
︙ | ︙ | |||
178 179 180 181 182 183 184 185 | [def [const zone]] -720 .. 720 (minutes east of GMT) [list_end] | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | [def [const zone]] -720 .. 720 (minutes east of GMT) [list_end] [call [cmd ::mime::parseaddress] [arg addresses]] Returns a list describing the comma-separated 822-style [arg addresses]. [para] Each dictionary contains the following keys, whose values may be the empty string: |
︙ | ︙ | |||
374 375 376 377 378 379 380 | [call [cmd ::mime::reversemapencoding] [arg charset_type]] Maps MIME charset types onto tcl encoding names. Those that are unknown return "". [list_end] | < < < < < < < < < < < < < < < < < < < < < < < < | 433 434 435 436 437 438 439 440 441 442 443 | [call [cmd ::mime::reversemapencoding] [arg charset_type]] Maps MIME charset types onto tcl encoding names. Those that are unknown return "". [list_end] [vset CATEGORY mime] [include ../doctools2base/include/feedback.inc] [manpage_end] |
Changes to modules/mime/mime.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 | # mime.tcl - MIME body parts # # (c) 1999-2000 Marshall T. Rose # (c) 2000 Brent Welch # (c) 2000 Sandeep Tamhankar # (c) 2000 Dan Kuchler # (c) 2000-2001 Eric Melski # (c) 2001 Jeff Hobbs # (c) 2001-2008 Andreas Kupries # (c) 2002-2003 David Welton # (c) 2003-2008 Pat Thoyts # (c) 2005 Benjamin Riefenstahl | | | > > > | | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | # mime.tcl - MIME body parts # # (c) 1999-2000 Marshall T. Rose # (c) 2000 Brent Welch # (c) 2000 Sandeep Tamhankar # (c) 2000 Dan Kuchler # (c) 2000-2001 Eric Melski # (c) 2001 Jeff Hobbs # (c) 2001-2008 Andreas Kupries # (c) 2002-2003 David Welton # (c) 2003-2008 Pat Thoyts # (c) 2005 Benjamin Riefenstahl # (c) 2013-2018 Poor Yorick # # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # # Influenced by Borenstein's/Rose's safe-tcl (circa 1993) and Darren New's # unpublished package of 1999. # # new string features and inline scan are used, requiring 8.3. package require Tcl 8.6.9 package require {mime qp} package require namespacex package require tcl::chan::cat package require tcl::chan::memchan package require tcl::chan::string package require {chan base} package require {chan getslimit} package require sha256 package provide mime 1.7 if {[catch {package require Trf 2.0}]} { # Fall-back to tcl-based procedures of base64 and quoted-printable encoders |
︙ | ︙ | |||
72 73 74 75 76 77 78 | # # state variables: # # canonicalP: input is in its canonical form # encoding: transfer encoding # version: MIME-version | | < < | < < < < < < | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | # # state variables: # # canonicalP: input is in its canonical form # encoding: transfer encoding # version: MIME-version # header: dictionary (keys are lower-case) # value: either "chan" or "parts" # # file: input file # fd: cached file-descriptor, typically for root # root: token for top-level part, for (distant) subordinates # count: length in octets of (encoded) content # # parts: list of bodies (tokens) namespace eval ::mime { variable mime array set mime {uid 0} # RFC 822 lexemes variable addrtokenL lappend addrtokenL \; , < > : . ( ) @ \" \[ ] \\ variable addrlexemeL { LX_SEMICOLON LX_COMMA |
︙ | ︙ | |||
318 319 320 321 322 323 324 | ksc5601 KSC5601 ksc5601 korean shiftjis MS_Kanji utf-8 UTF8 } namespace export {*}{ | < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < < < | | | 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 | ksc5601 KSC5601 ksc5601 korean shiftjis MS_Kanji utf-8 UTF8 } namespace export {*}{ .destroy .new body cookie datetime field_decode header mapencoding qp parseaddress property reversemapencoding serialize setheader uniqueID word_decode word_encode } } proc ::mime::addchan {token chan} { variable channels upvar 0 $token state upvar 0 state(fd) fd if {[info exists fd]} { error [list {a channel is already present}] } if {[$chan configure -encoding] ne {binary}} { $chan configure -translation auto } set fd $chan incr channels($fd) return } # ::mime::addr_next -- # # Locate the next address in a mime token. # |
︙ | ︙ | |||
3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 | if {[set x [string first / $mbox]] > 0} { set mbox [string range $mbox 0 [expr {$x - 1}]] } return [string trim $mbox \"] } # ::mime::datetime -- # # Fortunately the clock command in the Tcl 8.x core does all the heavy # lifting for us (except for timezone calculations). # # mime::datetime takes a string containing an 822-style date-time | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 | if {[set x [string first / $mbox]] > 0} { set mbox [string range $mbox 0 [expr {$x - 1}]] } return [string trim $mbox \"] } proc ::mime::checkinputs {} { upvar 1 inputs inputs if {[incr inputs] > 1} { error [list {more than one input source provided}] } } proc ::mime::body_decoded _ { set token [$_ token] upvar 0 $token state upvar 0 state(bodychandecoded) bodychandecoded $_ parsepart if {[info exists state(parts)]} { error [list {not a leaf part} $token] } if {$state(canonicalP)} { $state(fd) seek 0 return $state(fd) } else { if {![info exists bodychandecoded]} { set bodychandecoded [::tcllib::chan::base .new [ info cmdcount]_bodydecoded [file tempfile]] $bodychandecoded configure -translation binary $state(bodychan) seek 0 $state(bodychan) copy [$bodychandecoded $ chan] $bodychandecoded seek 0 $state(bodychan) seek 0 setencoding $token $bodychandecoded setcharset $_ $bodychandecoded } $bodychandecoded seek 0 return $bodychandecoded } } proc ::mime::body_raw _ { set token [$_ token] upvar 0 $token state $_ parsepart if {[info exists state(parts)]} { error [list {not a leaf part} $token] } if {$state(canonicalP)} { $state(fd) seek 0 return $state(fd) } else { $state(bodychan) seek 0 return $state(bodychan) } } namespace eval ::mime::body { namespace ensemble create -parameters token namespace export * namespacex import [namespace parent] body_decoded decoded body_raw raw } proc ::mime::contenttype _ { set token [$_ token] upvar 0 $token state try { $_ header get content-type } on error {cres copts} { # rfc 2045 5.2 try { if {header::exists $token MIME-Version} { return text/plain } else { switch $state(spec) { cgi - http { return {text/html {charset UTF-8}} } mime { # do not specify US-ASCII here as it is the default return text/plain } } } } on error {} { return application/octet-stream } } } proc ::mime::cookie_delete {_ name args} { # this -expires overrides any that might be in $args $_ cookie set value {} {*}$args expires [ format 0 -timezone :UTC -format {%a, %d %b %Y %H:%M:%S %z}] return } # ::mime::cookie_set # # Set a return cookie. You must call this before you call # ncgi::header or ncgi::redirect # # Arguments: # args Name value pairs, where the names are: # name Cookie name # value Cookie value # ?path? Path restriction # ?domain? domain restriction # ?expires? Time restriction # ?httponly? boolean, default true # # Side Effects: # Formats and stores the Set-Cookie header for the reply. proc ::mime::cookie_set {_ name value args} { dict size $args foreach {key val} $args { switch $key { domain - expires - httponly - path { set $key $val } default { error [list {wrong # args} {should be} \ [list name value ?path path? ?domain domain? \ ?expires date? ?httponly boolean?]] } } } set line $name=$value set params {} set flags {} foreach extra {path domain} { if {[info exists $extra]} { lappend params $extra [set $extra] } } if {[info exists expires]} { switch -glob $expires { *GMT { # do nothing } default { set expires [clock format [datetimescan $expires] \ -format {%A, %d-%b-%Y %H:%M:%S GMT} -gmt 1] } } lappend params expires $expires } if {[info exists secure]} { lappend flags secure } if {![info exists httponly] || $httponly} { lappend flags HttpOnly } if {[llength $flags]} { lappend params $flags } $_ header set Set-Cookie $line $params return } # ::mime::datetime -- # # Fortunately the clock command in the Tcl 8.x core does all the heavy # lifting for us (except for timezone calculations). # # mime::datetime takes a string containing an 822-style date-time |
︙ | ︙ | |||
3165 3166 3167 3168 3169 3170 3171 | Jan Feb Mar Apr May Jun \ Jul Aug Sep Oct Nov Dec] variable MONTHS_LONG [list {} \ January February March April May June July \ August Sepember October November December] } proc ::mime::datetime {value property} { | | < < < < < < < < < < < < < | | 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 | Jan Feb Mar Apr May Jun \ Jul Aug Sep Oct Nov Dec] variable MONTHS_LONG [list {} \ January February March April May June July \ August Sepember October November December] } proc ::mime::datetime {value property} { if {$value eq {-now}} { set clock [clock seconds] } else { set clock [datetimescan $value] } switch $property { clock { return $clock } |
︙ | ︙ | |||
3339 3340 3341 3342 3343 3344 3345 | #TODO: this path is not covered by tests set value 0 } return $value } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < > > | | | > > | > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 | #TODO: this path is not covered by tests set value 0 } return $value } proc ::mime::datetimescan value { variable timeformats foreach format $timeformats { if {![catch {clock scan $value -format $format} cres copts]} break } return -options $copts $cres } # ::mime::encoding -- # # Determines how a token is encoded. # # Arguments: # token # The MIME token to parse. # # Results: # Returns the encoding of the message (the null string, base64, # or quoted-printable). proc ::mime::encoding _ { set token [$_ token] # FRINK: nocheck upvar 0 $token state upvar 0 state(fd) chan state(params) params if {[info exists state(encoding)]} { return $state(encoding) } lassign [$_ contenttype] content switch -glob $content { audio/* - image/* - video/* { return [set state(encoding) base64] } message/* - multipart/* { return [set state(encoding) {}] } default {# Skip} } set asciiP 1 set lineP 1 if {[info exists state(parts)]} { return [set state(encoding) {}] } if {[set current [$chan tell]] < 0} { makeseekable $token set current [$chan tell] } set chanconfig [$chan configure] try { set buf {} set dataend 0 while {[set data [$chan read 8192]] ne {} || $buf ne {}} { if {$data eq {}} { set dataend 1 } set data $buf[set buf {}]$data[set data{}] set lines [split $data \n] if {!$dataend} { set buf [lindex $lines end] set lines [lrange $lines[set lines {}] 0 end-1] } if {[llength $lines]} { if {$asciiP} { set asciiP [encodingasciiP $lines] } if {$lineP} { set lineP [encodinglineP $lines] } if {(!$asciiP) && (!$lineP)} { break } } } } finally { $chan configure {*}$chanconfig $chan seek 0 } switch -glob $content { text/* { if {!$asciiP} { #TODO: this path is not covered by tests if {[dict exists $params charset]} { set v [string tolower [dict get $params $charset]] if {($v ne {us-ascii}) \ && (![string match {iso-8859-[1-8]} $v])} { return [set state(encoding) base64] } } } if {!$lineP} { return [set state(encoding) quoted-printable] } } default { if {(!$asciiP) || (!$lineP)} { return [set state(encoding) base64] } } } return [set state(encoding) {}] } # ::mime::encodingasciiP -- # # Checks if a string is a pure ascii string, or if it has a non-standard # form. # # Arguments: # line The line to check. # # Results: # Returns 1 if \r only occurs at the end of lines, and if all # characters in the line are between the ASCII codes of 32 and 126. proc ::mime::encodingasciiP lines { foreach line $lines { set firstr [string first \r $line]] if { $firstr > 0 && $first != [string length $line] } { return 0 } foreach c [split $line {}] { switch $c { { } - \t - \r - \n { } default { binary scan $c c c if {($c < 32) || ($c > 126)} { return 0 } } } } } return 1 } # ::mime::encodinglineP -- # # Checks if a string is a line is valid to be processed. # # Arguments: # line The line to check. # # Results: # Returns 1 the line is less than 76 characters long, the line # contains more characters than just whitespace, the line does # not start with a '.', and the line does not start with 'From '. proc ::mime::encodinglineP lines { foreach line $line { if {([string length $line] > 76) \ || ($line ne [string trimright $line]) \ || ([string first . $line] == 0) \ || ([string first {From } $line] == 0)} { return 0 } } return 1 } proc ::mime::contentid _ { set token [$_ token] upvar 0 $token state upvar 0 state(parts) parts $_ parsepart if {[info exists parts]} { foreach part $parts { upvar 0 $part childpart set created 0 if {![header::exists $part message-id]} { set created 1 header::setinternal $part Message-ID [messageid $part] } # use message-id here, not content-id, to account for header info # in the parts append ids [$part header get message-id] if {$created} { if {!$childpart(addmessageid)} { header::unset $part message-id } } } set id [::sha2::sha256 -hex $ids] } else { set chan [$_ body decoded] set config [$chan configure] if {[dict exists $config -chan]} { dict unset config -chan } try { $chan seek 0 set id [::sha2::sha256 -hex -channel [$chan configure -chan]] $chan seek 0 } finally { $chan configure {*}$config } } return $id@| } proc ::mime::dropchan token { variable channels upvar 0 $token state upvar 0 state(fd) fd if {[info exists fd]} { if {[incr channels($fd) -1] == 0} { unset channels($fd) if {$state(closechan)} { $fd close } } unset state(fd) } } # ::mime::.destroy -- # # mime::.destroy destroys a MIME part. # # If the -subordinates option is present, it specifies which # subordinates should also be destroyed. The default value is # "dynamic". # # Arguments: # token The MIME token to parse. # args Args can be optionally be of the following form: # ?-subordinates "all" | "dynamic" | "none"? # # Results: # Returns an empty string. proc ::mime::.destroy {token args} { # FRINK: nocheck upvar 0 $token state array set options [list -subordinates dynamic] array set options $args set ensemble $state(ensemble) switch $options(-subordinates) { all { #TODO: this code path is untested if {[info exists state(parts)]} { foreach part $state(parts) { $part .destroy } } } dynamic { foreach part $state(dynamic) { $part .destroy } } none { } default { error "unknown value for -subordinates $options(-subordinates)" } } dropchan $token if {$state(bodychan) ne {}} { if {[$state(bodychan) configure -chan] in [chan names]} { rename $state(bodychan) {} } } if {[info exists state(bodychandecoded)]} { rename $state(bodychandecoded) {} } foreach name [array names state] { unset state($name) } if {[namespace which $ensemble] ne {}} { rename $ensemble {} # FRINK: nocheck } unset $token } proc ::mime::messageid _ { set token [$_ token] upvar 0 $token state #set unique [uniqueID] if {![header::exists $token content-id] && $state(addcontentid)} { header::setinternal $token Content-ID [contentid $_] } set sha [::sha2::SHA256Init] foreach {key val} [lsort -stride 2 [$_ header get]] { lassign $val value params ::sha2::SHA256Update $sha $key$value foreach {pkey pval} $params { ::sha2::SHA256Update $sha $pkey$pval } } set hash [::sha2::SHA256Final $sha] binary scan $hash H* hex return $hex@| } # ::mime::mimegets -- # # like [gets] but does not run over multipart boundaries # # only needed during the parsing stage, since after that the content of each # part is in a separate file # # Arguments: # token The MIME token to parse. # # Results: # Returns the size in bytes of the MIME token. proc ::mime::mimegets {token varname} { upvar 0 $token state upvar 0 state(boundary) boundary state(eof) eof upvar 1 $varname line if {$eof} { set line {} return -1 } set res [$state(fd) gets line] if {$res == -1} { set eof 1 set line {} return -1 } else { set found [string first --$boundary $line] if {$found == 0} { if {[string first --$boundary-- $line] >= 0} { set state(sawclosing) 1 set eof 1 } set line {} return -1 } return $res } } # ::mime::getsize -- # # Determine the size (in bytes) of a MIME part/token # # Arguments: # token The MIME token to parse. # # Results: # Returns the size in bytes of the MIME token. proc ::mime::getsize _ { set token [$_ token] # FRINK: nocheck upvar 0 $token state upvar 0 state(bodychan) bodychan state(fd) inputchan $_ parsepart if {[info exists state(parts)]} { set size 0 foreach part $state(parts) { incr size [getsize $part] } } else { set size 0 if {$state(canonicalP)} { if {[set current [$inputchan tell]] < 0} { makeseekable $token } set current [$inputchan tell] $inputchan seek 0 end set size [$inputchan tell] $inputchan seek $current } else { set size $state(size) } } # no longer needed since size is calculated during parsing #if {$state(encoding) eq {base64}} { # set size [expr {($size * 3 + 2) / 4}] #} return $size } proc ::mime::getTransferEncoding _ { set token [$_ token] upvar 0 $token state # not the global [encoding] set encoding [encoding $_] # See also issues [#477088] and [#539952] switch $encoding { base64 - quoted-printable - 7bit - 8bit - binary - {} { } default { error [list {Can't handle content encoding} $encoding] } } return $encoding } namespace eval ::mime::header { variable tchar # hypen is first for inclusion in brackets variable tchar_re {-!#$%&'*+.^`|~0-9A-Za-z} variable token_re "^(\[$tchar_re]*)\\s*(?:;|$)?" variable notattchar_re "\[^[string map {* {} ' {} % {}} $tchar_re]]" # RFC 2045 lexemes variable typetokenL lappend typetokenL \; , < > : ? ( ) @ \" \[ ] = / \\ variable typelexemeL { LX_SEMICOLON LX_COMMA LX_LBRACKET LX_RBRACKET LX_COLON LX_QUESTION LX_LPAREN LX_RPAREN LX_ATSIGN LX_QUOTE LX_LSQUARE LX_RSQUARE LX_EQUALS LX_SOLIDUS LX_QUOTE } variable internal 0 } proc ::mime::header::boundary {} { return [uniqueID] } # ::mime::dunset -- # # Unset all values for $key, without "normalizing" other redundant keys proc ::mime::header::dunset {dictname key} { upvar 1 $dictname dict set dict [join [lmap {key1 val} $dict[set dict {}] { if {$key1 eq $key} continue list $key1 $val }]] } proc ::mime::header::exists {token name} { upvar 0 $token state set lname [string tolower $name] expr {[dict exists $state(headerlower) $lname] || [dict exists $state(headerinternallower) $lname] || [dict exists $state(contentidlower) $lname] || [dict exists $state(messageidlower) $lname] } } # ::mime::header get -- # # Returns the header of a message as a multidict where each value is a list # containing the header value and a dictionary parameters for that header. # If $key is provided, returns only the value and paramemters of the last # maching header, without regard for case. # # If -names is specified, a list of all header names is returned. # proc ::mime::header::get {_ {key {}}} { set token [$_ token] # FRINK: nocheck upvar 0 $token state parse $token set contentid $state(contentid) set contentidlower $state(contentidlower) set header $state(header) set headerlower $state(headerlower) set headerinternal $state(headerinternal) set headerinternallower $state(headerinternallower) set messageid $state(messageid) set messageidlower $state(messageidlower) switch $key { {} { set result [list {*}$messageid {*}$contentid {*}$headerinternal \ {*}$header] if {![dict exists $headerlower content-transfer-encoding] && !$state(canonicalP)} { set tencoding [getTransferEncoding $_] if {$tencoding ne {}} { lappend result Content-Transfer-Encoding [list $tencoding {}] } } return $result } -names { return [dict keys $header] } default { set lower [string tolower $key] switch $lower { content-id { if {![dict size $contentidlower]} { contentid $token } return [dict get $contentidlower content-id] } content-transfer-encoding { if {[dict exists $headerinternallower $lower]} { return [dict get $headerinternallower $lower] } elseif {!$state(canonicalP)} { return [list [getTransferEncoding $_] {}] } else { error [list {no such header} $key] } } message-id { if {![dict size $messageidlower]} { setinternal $token Message-ID [[namespace parent]::messageid $_] } return [dict get $messageidlower message-id] } mime-version { return [list $state(version) {}] } default { set res {} if {[dict exists $headerinternallower $lower]} { return [dict get $headerinternallower $lower] } elseif {[dict exists $headerlower $lower]} { return [dict get $headerlower $lower] } else { error [list {no such header} $key] } } } } } } proc ::mime::header::parse token { # FRINK: nocheck upvar 0 $token state upvar 0 state(fd) fd state(boundary) boundary if {$state(canonicalP) || $state(headerparsed)} { return } set state(headerparsed) 1 if {[info exists boundary]} { set gets [list [namespace parent]::mimegets $token line] } else { set gets [list $fd gets line] } set vline {} while 1 { set blankP 0 set x [{*}$gets] if {$x <= 0} { set blankP 1 } # to do 2018-11-13: probably remove this now that line translation # happens automatically, if {!$blankP && [string match *\r $line]} { set line [string range $line 0 $x-2] if {$x == 1} { set blankP 1 } } # there is a space and a tab between the brackets in next line if {!$blankP && [string match {[ ]*} $line]} { append vline { } [string trimleft $line " \t"] continue } if {$vline eq {}} { if {$blankP} { break } set vline $line continue } if { [set x [string first : $vline]] <= 0 || [set mixed [string trimright [ string range $vline 0 [expr {$x - 1}]]]] eq {} } { error [list {improper line in header} $vline] } set value [string trim [string range $vline [expr {$x + 1}] end]] switch [set lower [string tolower $mixed]] { content-disposition { set_ $token $mixed {*}[parseparts $token $value] } content-id { setinternal $token $mixed $value } content-type { if {[exists $token content-type]} { error [list {multiple Content-Type fields starting with} \ $vline] } set x [parsetype $token $value] setinternal $token Content-Type {*}$x } content-md5 { } content-transfer-encoding { if { $state(encoding) ne {} && $state(encoding) ne [string tolower $value] } { error [list [list multiple Content-Transfer-Encoding \ fields starting with] $vline] } set state(encoding) [string tolower $value] } mime-version { set state(version) $value } default { set_ $token $mixed $value -mode append } } if {$blankP} { break } set vline $line } } proc ::mime::header::parseparams token { # FRINK: nocheck upvar 0 $token state set params {} while 1 { switch [parselexeme $token] { LX_END { return [processparams $params[set params {}]] } LX_SEMICOLON { if {[dict size $params]} { continue } else { error [list {expecting attribute} not $state(buffer)] } } LX_ATOM { } default { error [list {expecting attribute} not $state(buffer)] } } set attribute [string tolower $state(buffer)] if {[parselexeme $token] ne {LX_EQUALS}} { error [list expecting = found $state(buffer)] } switch [parselexeme $token] { LX_ATOM { } LX_QSTRING { set state(buffer) [ string range $state(buffer) 1 [ expr {[string length $state(buffer)] - 2}]] set state(buffer) [unquote $state(buffer)] } default { error [list expecting value found $state(buffer)] } } dict set params $attribute $state(buffer) } } proc ::mime::header::parseparts {token value} { variable token_re upvar 0 $token state if {![regexp $token_re $value match type]} { error [list {expected disposition-type}] } variable typetokenL variable typelexemeL set value [string range $value[set value {}] [string length $match] end] set state(input) $value set state(buffer) {} set state(lastC) LX_END set state(comment) {} set state(tokenL) $typetokenL set state(lexemeL) $typelexemeL set code [catch {parseparams $token} result copts] unset {*}{ state(input) state(buffer) state(lastC) state(comment) state(tokenL) state(lexemeL) } return -options $copts [list $type $result] } # ::mime::header::parsetype -- # # Parses the string passed in and identifies the content-type and # params strings. # # Arguments: # token The MIME token to parse. # string The content-type string that should be parsed. # # Results: # Returns the content and params for the string as a two element # tcl list. proc ::mime::header::parsetype {token string} { # FRINK: nocheck upvar 0 $token state variable typetokenL variable typelexemeL set state(input) $string set state(buffer) {} set state(lastC) LX_END set state(comment) {} set state(tokenL) $typetokenL set state(lexemeL) $typelexemeL catch {parsetypeaux $token} result copts unset {*}{ state(input) state(buffer) state(lastC) state(comment) state(tokenL) state(lexemeL) } return -options $copts $result } # ::mime::header::parsetypeaux -- # # A helper function for mime::parsetype. Parses the specified # string looking for the content type and params. # # Arguments: # token The MIME token to parse. # string The content-type string that should be parsed. # # Results: # Returns the content and params for the string as a two element # tcl list. proc ::mime::header::parsetypeaux token { # FRINK: nocheck upvar 0 $token state set params {} if {[parselexeme $token] ne {LX_ATOM}} { error [list expecting type found $state(buffer)] } set type [string tolower $state(buffer)] switch [parselexeme $token] { LX_SOLIDUS { } LX_END { if {$type ne {message}} { error [list expecting type/subtype found $type] } return [list message/rfc822 {}] } default { error [list expecting / found $state(buffer)] } } if {[parselexeme $token] ne {LX_ATOM}} { error [list expecting subtype found $state(buffer)] } append type [string tolower /$state(buffer)] switch [parselexeme $token] { LX_END { } LX_SEMICOLON { set params [parseparams $token] } default { error [list expecting {; or end} found $state(buffer)] } } list $type $params } proc ::mime::header::processparams params { set info {} foreach key [lsort -dictionary [dict keys $params]] { set pvalue [dict get $params $key] # a trailing asterisk is ignored if this is not the first field in an # identically-named series # this expression can't fail regexp {^([^*]+?)(?:([*])([0-9]+))?([*])?$} $key -> name star1 counter star2 dict update info $name dict1 { if {![info exists dict1]} { set dict1 {} } dict update dict1 encoding encoding value value { if {$star1 ne {}} { if {$star2 ne {} || $counter eq {}} { if {![regexp {^([^']*)'([^']*)'(.*)$} $pvalue \ -> charset lang pvalue]} { error [list [list malformed language information in \ extended parameter name]] } if {$charset ne {}} { set encoding [reversemapencoding $charset] } } } append value $pvalue } } } set params {} dict for {key pinfo} $info[set info {}] { dict update pinfo encoding encoding value value {} if {[info exists encoding]} { set value [string map {% {\x}} $value[set value {}]] set value [subst -novariables -nocommands $value[set value {}]] set value [::encoding convertfrom $encoding $value] } dict set params $key $value } return $params } proc ::mime::header::serialize {name value params} { variable notattchar_re set lname [string tolower $name] # to do: check key for conformance # to do: properly quote/process $value for interpolation if {[regexp {[^\x21-\x39\x3b-\x7e]} $name]} { error [ list {non-printing character or colon character in header name} $name] } if {[regexp {[^\t\x20-\x7e]} $value]} { error [ list {non-printing character in header value}] } switch $lname { content-id - message-id { set value <$value> } } set res "$name: $value" if {[llength $params] % 2} { set extra [lindex $params end] set params [lreplace $params[set params {}] end end] } else { set extra {} } foreach {key value} $params { if {[regexp $notattchar_re $key]} { error [list {illegal character found in attribute name}] } set len [expr {[string length $key]} + 1 + [string length $value]] # save one byte for the folding white space continuation space # and two bytes for "; " if {$len > 73 || ![regexp {[^-!#$%&'*+,.\w`~^@{}|]+$} $value]} { # save two bytes for the quotes if {$len <= 71 && ![regexp {[^\x20-\x7e]} $value]} { set value "[string map [list \\ \\\\ \" \\\"] $value[set value {}]]" append res "\n\t; $key=$value" } else { set value [::encoding convertto utf-8 $value] regsub -all -- $notattchar_re $value {[format %%%02X [scan "\\&" %c]]} value set value [subst -novariables $value] set partnum 0 set start 0 set param $key*$partnum*=utf-8'' while {$start < [string length $value]} { # subtract one from the limit to ensure that at least one byte # is included in the part value if {[string length $param] > 72} { error [list {parameter name is too long}] } set end [expr {$start + 72 - [string length $param]}] set part [string range $value $start $end] incr start [string length $part] append res "\n\t; $param$part" set param $key*$partnum= incr partnum } } } else { append res "\n\t; $key=$value" } } foreach item $extra { append res "\n\t; $item" } return $res } # ::mime::header::set -- # # mime::header::set writes, appends to, or deletes the value associated # with a key in the header. # # The value for -mode is one of: # # write: the key/value is either created or overwritten (the # default); # # append: a new value is appended for the key (creating it as # necessary); or, # # delete: all values associated with the key are removed (the # "value" parameter is ignored). # # Regardless, mime::setheader returns the previous value associated # with the key. # # Arguments: # token The MIME token to parse. # key The name of the key whose value should be set. # value The value for the header key to be set to. # ?params? A dictionary of parameters for the header. # ?args? An optional argument of the form: # ?-mode "write" | "append" | "delete"? # # Results: # Returns previous value associated with the specified key. proc ::mime::header::set_ {token key value args} { variable internal # FRINK: nocheck upvar 0 $token state upvar 0 \ state(contentid) contentid \ state(contentidlower) contentidlower \ state(header) header \ state(headerinternal) headerinternal \ state(headerinternallower) headerinternallower \ state(headerlower) headerlower \ state(messageid) messageid \ state(messageidlower) messageidlower parse $token set params {} switch [llength $args] { 1 - 3 { set args [lassign $args[set args {}] params] } 0 - 2 { # carry on } default { error [list {wrong # args}] } } array set options [list -mode write] array set options {} dict for {opt val} $args { switch $opt { -mode { set options($opt) $val } default { error [list {unknon option} $opt] } } } set lower [string tolower $key] set result {} switch $options(-mode) { append - write { switch $lower { content-md5 - content-transfer-encoding - mime-version - content-type { if {!$internal} { switch $lower { default { if {[exists $token $lower]} { lassign [get $token $lower] values params1 if {$value ni $values} { error "key $key may not be set" } } } } } switch $lower { content-type { if {[string match multipart/* $value] && ![dict exists $params boundary] } { dict set params boundary [boundary] } } default { #carry on } } } } if {$options(-mode) eq {write}} { if {[dict exists $header $key]} { dunset header $key } if {[dict exists $headerlower $lower]} { dunset headerlower $lower } if {[dict exists headerinternal $key]} { dunset headerinternal $key } if {[dict exists $headerinternallower $lower]} { dunset headerinternallower $lower } } set newval [list $value $params] if {$internal} { switch $lower { content-id { lappend contentid $key $newval lappend contentidlower $lower $newval } message-id { lappend messageid $key $newval lappend messageidlower $lower $newval } default { lappend headerinternal $key $newval lappend headerinternallower $lower $newval } } } else { lappend header $key $newval lappend headerlower $lower $newval } } delete { dunset headerlower $lower dunset headerinternallower $lower dunset header $key dunset headerinternal $key } default { error "unknown value for -mode $options(-mode)" } } return $result } proc ::mime::header::setinternal args { variable internal 1 try { set_ {*}$args } finally { set internal 0 } } # ::mime::.new -- # # the public interface for initializeaux proc ::mime::.new args { variable mime if {[llength $args] % 2} { set args [lassign $args[set args {}] name] } elseif {[llength $args]} { error [list {wrong # args}] } else { set name {} } set mimeid [incr mime(uid)] set token [namespace current]::$mimeid if {$name eq {}} { set name $token } elseif {![string match ::* $name]} { set name [uplevel 1 {namespace current}]::$name } set cookiecmd [namespace current]::${mimeid}_cookie namespace ensemble create -command $cookiecmd -map [list \ delete [list cookie_delete $name] \ set [list cookie_set $name] ] set headercmd [namespace current]::${mimeid}_header namespace ensemble create -command $headercmd -map [list \ get [list header::get $name] \ exists [list header::exists $token] \ parse [list header::parse $token] \ set [list header::set_ $token] \ serialize header::serialize \ setinternal [list header::setinternal $token] ] namespace ensemble create -command $name -map [list \ .destroy [list .destroy $token] \ body [list body $name] \ contenttype [list contenttype $name] \ cookie [list $cookiecmd] \ datetime [list datetime $token] \ field_decode [list field_decode $token] \ header [list $headercmd] \ mapencoding [list mapencoding $token] \ qp [list qp $token] \ parseaddress [list parseaddress $token] \ parsepart [list parsepart $name] \ property [list property $name] \ reversemapencoding [list reversemapencoding $token] \ serialize [list serialize $name] \ setheader [list setheader $token] \ token [list ::lindex $token] \ uniqueID [list uniqueID $token] \ word_decode [list word_decode $token] \ word_encode [list word_encode $token] ] trace add command $name delete [list apply [list { token cookiecmd headercmd old new op} { ::mime::.destroy $token rename $cookiecmd {} rename $headercmd {} }] $token $cookiecmd $headercmd] # FRINK: nocheck upvar 0 $token state set state(ensemble) $name if {[catch {uplevel 1 [ list mime::initializeaux $name {*}$args]} result eopts]} { catch {mime::.destroy $token -subordinates dynamic} return -options $eopts $result } return $name } # ::mime::initializeaux -- # # Creates a MIME part and returns the MIME token for that part. # # Arguments: # args Args can be any one of the following: # ?-canonical type/subtype # ?-params {?key value? ...} # ?-encoding value? # ?-headers {?key value? ...} # ?-spec ?mime | cgi | http? # (-chan value | -parts {token1 ... tokenN}) # # If the -canonical option is present, then the body is in # canonical (raw) form and is found by consulting either the, # -chan, or -parts option. # # -header # a dictionary of headers # with possibliy-redundant keys # # -params # a dictionary of parameters # with possibly-redundant keys # # # Also, -encoding, if present, specifies the # "Content-Transfer-Encoding" when copying the body. # # If the -canonical option is not present, then the MIME part # contained in -chan option is parsed, # dynamically generating subordinates as appropriate. # # Results: # An initialized mime token. proc ::mime::initializeaux {_ args} { set token [$_ token] variable channels # FRINK: nocheck upvar 0 $token state upvar 0 state(canonicalP) canonicalP state(params) params \ state(relax) relax set ipnuts 0 set params {} set state(addcontentid) 1 set state(addmessageid) 1 set state(addmimeversion) 1 # contains the decoded message body set state(bodychan) {} set state(bodydecoded) 0 set state(bodyparsed) 0 set state(dynamic) {} set canonicalP 0 set state(closechan) 1 set state(contentid) {} set state(contentidlower) {} set state(encoding) {} set state(encodingdone) 0 set state(eof) 0 set state(header) {} set state(headerinternal) {} set state(headerinternallower) {} set state(headerlower) {} set state(headerparsed) 0 set state(isstring) 0 set relax [dict create finalboundary 0] set state(messageid) {} set state(messageidlower) {} set state(root) $token set state(sawclosing) 0 set state(spec) mime set state(size) 0 set state(usemem) 0 set state(version) 1.0 set state(warnings) {} set userparams 0 set argc [llength $args] for {set argx 0} {$argx < $argc} {incr argx} { set option [lindex $args $argx] if {[incr argx] >= $argc} { error "missing argument to $option" } set value [lindex $args $argx] switch $option { -addcontentid { set state(addcontentid) [expr {!!$value}] } -addmessageid { set state(addmessageid) [expr {!!$value}] } -addmimeversion { set state(addmimeversion) [expr {!!$value}] } -boundary { set state(boundary) $value } -canonical { set canonicalP 1 set type [string tolower $value] } -chan { checkinputs addchan $token [uplevel 1 [list namespace which $value]] } -close { set state(closechan) [expr {!!$value}] } -encoding { set value [string tolower $value[set value {}]] switch $value { 7bit - 8bit - binary - quoted-printable - base64 { } default { error "unknown value for -encoding $state(encoding)" } } set state(encoding) [string tolower $value] } -file { checkinputs addchan $token [tcllib::chan::base .new [ info cmdcount]_chan [open $value]] } -headers { # process headers later in order to assure that content-id and # content-type occur first if {[info exists headers]} { error [list {-headers option occurred more than once}] } if {[llength $value] % 2} { error [list -headers expects a dictionary] } set headers $value } -params { if {$userparams} { error [list {-params can only be provided once}] } set userparams 1 if {[llength $value] % 2} { error [list -params expects a dictionary] } foreach {mixed pvalue} $value { set lower [string tolower $mixed] if {[dict exists params $lower]} { error "the $mixed parameter may be specified at most once" } dict set params $lower $pvalue } } -parts { checkinputs set canonicalP 1 set state(parts) $value } -relax { relax $token $value 1 } -root { # the following are internal options set state(root) $value } -spec { switch $value { cgi - http { set state(addcontentid) 0 set state(addmimeversion) 0 set state(addmessageid) 0 set state(spec) http } mime { set state(addcontentid) 1 set state(addmimeversion) 1 set state(spec) mime } default { error [list {unknown protocol}] } } } -strict { relax $token $value [expr {!$value}] } -string { checkinputs addchan $token [tcllib::chan::base .new [ info cmdcount]_chan [::tcl::chan::string $value]] } -usemem { set state(usemem) [expr {!!$value}] } default { error [list {unknown option} $option] } } } if {![info exists inputs]} { error [list {specify exactly one of} {-chan -file -parts -string}] } if {$canonicalP} { if {![info exists type]} { set type multipart/mixed } header::setinternal $token Content-Type $type $params if {[info exists headers]} { foreach {name hvalue} $headers { set lname [string tolower $name] if {$lname eq {content-type}} { error [list {use -canonical instead of -headers} $hkey $name] } if {$lname eq {content-transfer-encoding}} { error [list {use -encoding instead of -headers} $hkey $name] } if {$lname in {content-md5 mime-version}} { error [list {don't go there...}] } header::setinternal $token $name $hvalue } } lassign [$_ header get content-type] content dummy if {[info exists state(parts)]} { switch -glob $content { text/* - image/* - audio/* - video/* { error "-canonical $content and -parts do not mix" } default { if {$state(encoding) ne {}} { error "-encoding and -parts do not mix" } } } } set state(version) 1.0 return } if {[dict size $params]} { error "-param requires -canonical" } if {$state(encoding) ne {}} { error "-encoding requires -canonical" } if {[info exists headers]} { error "-header requires -canonical" } } proc mime::makeseekable token { upvar 0 $token state upvar 0 state(bodychan) bodychan state(fd) inputchan set chan2 [::tcllib::chan::base [info cmdcount]_chan [file tempfile]] chan configure $chan2 -translation binary chan copy $inputchan $chan2 incr size [tell $chan2] seek $chan2 0 close $inputchan set inputchan [::tcllib::chan::base [info cmdcount]_chan $chan2] return } # ::mime::mapencoding -- # # mime::mapencodings maps tcl encodings onto the proper names for their # MIME charset type. This is only done for encodings whose charset types # were known. The remaining encodings return {} for now. # # Arguments: # enc The tcl encoding to map. # # Results: # Returns the MIME charset type for the specified tcl encoding, or {} # if none is known. proc ::mime::mapencoding {enc} { variable encodings if {[info exists encodings($enc)]} { return $encodings($enc) } return {} } # ::mime::parsepart -- # # Parses the MIME headers and attempts to break up the message # into its various parts, creating a MIME token for each part. # # Arguments: # token The MIME token to parse. # # Results: # Throws an error if it has problems parsing the MIME token, # otherwise it just sets up the appropriate variables. proc ::mime::parsepart _ { set token [$_ token] upvar 0 $token state if {$state(canonicalP) || $state(bodyparsed)} { return } set state(bodyparsed) 1 parsepartaux $_ } proc ::mime::parsepartaux _ { set token [$_ token] # FRINK: nocheck upvar 0 $token state upvar 0 state(bodychan) bodychan state(eof) eof \ state(fd) fd state(size) size state(usemem) usemem state(relax) relax header::parse $token # although rfc 2045 5.2 defines a default treatment for content without a # type, don't automatically add an explicit content-type field #if {![header::exists $token content-type]} { # # rfc 2045 5.2 # header::setinternal $token Content-Type text/plain [ # dict create charset us-ascii] #} lassign [$_ contenttype] content params if {$usemem} { set bodychan [tcllib::chan::base .new [info cmdcount]_bodychan [ ::tcl::chan::memchan]] } else { set bodychan [tcllib::chan::base .new [info cmdcount]_bodychan] tcllib::chan::getslimit $bodychan $bodychan .init [file tempfile] } if {[dict exists $params charset]} { set charset [reversemapencoding [dict get $params charset]] if {$charset eq {}} { upvar 0 state(warnings) warnings lappend warnings [list {unknown charset} [ dict get $params charset] {using binary translation instead}] # but still do line automatic translation $fd configure -encoding binary -translation auto } else { $fd configure -encoding [reversemapencoding [dict get $params charset]] } } $bodychan configure -translation binary if {[info exists state(boundary)]} { set gets [list mimegets $token line] set iseof {$eof} } else { set gets [list $fd gets line] set iseof {[$fd eof] || $eof} } if {[string match multipart/* $content]} { set state(parts) {} dict update params boundary boundary {} if {![info exists state(boundary)]} { if {![info exists boundary]} { error "boundary parameter is missing in $content" } if {[string trim $boundary] eq {}} { error "boundary parameter is empty in $content" } } while 1 { if $iseof { break } if {![llength $state(parts)]} { set x [{*}$gets] if {$x == -1} { break } } if {[string first --$boundary-- $line] >= 0} { # No starting boundary was seen prior to the terminating boundary. # Interpret this to mean there are no more parts, and also attempt # to make a part from data already seen. # Covered by by test case mime-3.7, using "badmail1.txt". set state(sawclosing) 1 $bodychan puts $line $bodychan seek 0 set child [.new {} -chan $bodychan \ -root $state(root) -boundary $boundary -usemem $usemem] $child parsepart lappend state(parts) $child lappend state(dynamic) $child $child header setinternal Content-Type application/octet-stream break } elseif {[llength $state(parts)] || [string first --$boundary $line] == 0} { # either just saw the first boundary or saw a boundary between parts # do not brace this expression if $iseof { # either saw the closing boundary or reached the end of the file break } elseif {[string first --$boundary-- $line] >= 0} { set state(sawclosing) 1 break } else { #mimegets returned 0 because it found a border set child [.new {} -chan $fd \ -root $state(root) -boundary $boundary -usemem $usemem] $child parsepart lappend state(parts) $child lappend state(dynamic) $child upvar 0 $child childstate set state(sawclosing) $childstate(sawclosing) if {$childstate(eof)} break } } else { # Accumulate data in case the terminating boundary occurs starting # boundary was found, so that a part can be generated from data # seen so far. if $iseof { $bodychan puts -nonewline $line } else { $bodychan puts $line } set size [expr {$size + [ string length $line] + 1}] } } if {!$state(sawclosing) && ![dict get $relax finalboundary]} { error {end-of-string encountered while parsing multipart/form-data} } } else { if {[info exists state(boundary)]} { while 1 { set x [{*}$gets] if {$x == -1} { break } else { if {[incr linesout] > 1} { $bodychan puts -nonewline \n$line } else { $bodychan puts -nonewline $line } set size [expr {$size + [ string length $line] + 1}] } } } else { $fd copy [$bodychan configure -chan] set size [$bodychan tell] } $bodychan seek 0 if {[string match message/* $content]} { # FRINK: nocheck setencoding $token $bodychan setcharset $_ $bodychan set child [.new {} -chan $bodychan -usemem $usemem] lappend state(parts) $child lappend state(dynamic) $child $child parsepart } else { # this is undtrusted data, so keep the getslimit enabled on the # assumption that no one else wants to get hit by a long-line # attack either. #$bodychan configure -getslimit -1 } } return } # ::mime::property -- # # mime::property returns the properties of a MIME part. # # The properties are: # # property value # ======== ===== # content the type/subtype describing the content # encoding the "Content-Transfer-Encoding" # params a list of "Content-Type" parameters # parts a list of tokens for the part's subordinates # size the approximate size of the content {before decoding} # # The "parts" property is present only if the MIME part has # subordinates. # # If mime::property is invoked with the name of a specific # property, then the corresponding value is returned; instead, if # -names is specified, a list of all properties is returned; # otherwise, a dictionary of properties is returned. # # Arguments: # token The MIME token to parse. # property One of 'content', 'encoding', 'params', 'parts', and # 'size'. Defaults to returning a dictionary of # properties. # # Results: # Returns the properties of a MIME part proc ::mime::property {_ {property {}}} { set token [$_ token] # FRINK: nocheck upvar 0 $token state $_ parsepart lassign [$_ contenttype] content params switch $property { {} { array set properties [list content $content \ encoding $state(encoding) \ params $params \ size [getsize $_]] if {[info exists state(parts)]} { set properties(parts) $state(parts) } return [array get properties] } -names { set names [list content encoding params] if {[info exists state(parts)]} { lappend names parts } lappend nams size return $names } content - params { return [set $property] } encoding { return $state($property) } parts { if {![info exists state(parts)]} { error [list not a multipart message] } return $state(parts) } size { return [getsize $_] } default { error [list {unknown property} $property] } } } # ::mime::parseaddress -- # # This was originally written circa 1982 in C. we're still using it # because it recognizes virtually every buggy address syntax ever # generated! # # mime::parseaddress takes a string containing one or more 822-style # address specifications and returns a list of dictionaries, for each # address specified in the argument. # # Each dictionary contains these properties: # # property value # ======== ===== # address local@domain # comment 822-style comment # domain the domain part (rhs) # error non-empty on a parse error # group this address begins a group # friendly user-friendly rendering # local the local part (lhs) # memberP this address belongs to a group # phrase the phrase part # proper 822-style address specification # route 822-style route specification (obsolete) # # Note that one or more of these properties may be empty. # # Arguments: # string The address string to parse # # Results: # Returns a list of dictionaries, one element for each address # specified in the argument. proc ::mime::parseaddress {string args} { variable mime set token [namespace current]::[incr mime(uid)] # FRINK: nocheck upvar 0 $token state if {[llength $args]} { set string2 [lindex $args end] set args [list $string {*}[lrange $args 0 end-1]] set string $string2 } dict for {opt val} $args { switch $opt { hostname { set state(default_host) $val } } } catch {mime::parseaddressaux $token $string} result copts foreach name [array names state] { unset state($name) } # FRINK: nocheck catch {unset $token} return -options $copts $result } # ::mime::parseaddressaux -- # # This was originally written circa 1982 in C. we're still using it # because it recognizes virtually every buggy address syntax ever # generated! # # mime::parseaddressaux does the actually parsing for mime::parseaddress # # Each dictionary contains these properties: # # property value # ======== ===== # address local@domain # comment 822-style comment # domain the domain part (rhs) # error non-empty on a parse error # group this address begins a group # friendly user-friendly rendering # local the local part (lhs) # memberP this address belongs to a group # phrase the phrase part # proper 822-style address specification # route 822-style route specification (obsolete) # # Note that one or more of these properties may be empty. # # Arguments: # token The MIME token to work from. # string The address string to parse # # Results: # Returns a list of dictionaries, one for each address specified in the # argument. proc ::mime::parseaddressaux {token string} { # FRINK: nocheck upvar 0 $token state variable addrtokenL variable addrlexemeL set state(input) $string set state(glevel) 0 set state(buffer) {} set state(lastC) LX_END set state(tokenL) $addrtokenL set state(lexemeL) $addrlexemeL set result {} while {[addr_next $token]} { if {[set tail $state(domain)] ne {}} { set tail @$state(domain) } else { if {![info exists state(default_host)]} { set state(default_host) [info hostname] } set tail @$state(default_host) } if {[set address $state(local)] ne {}} { #TODO: this path is not covered by tests append address $tail } if {$state(phrase) ne {}} { #TODO: this path is not covered by tests set state(phrase) [string trim $state(phrase) \"] foreach t $state(tokenL) { if {[string first $t $state(phrase)] >= 0} { #TODO: is this quoting robust enough? set state(phrase) \"$state(phrase)\" break } } set proper "$state(phrase) <$address>" } else { set proper $address } if {[set friendly $state(phrase)] eq {}} { #TODO: this path is not covered by tests if {[set note $state(comment)] ne {}} { if {[string first ( $note] == 0} { set note [string trimleft [string range $note 1 end]] } if { [string last ) $note] == [set len [expr {[string length $note] - 1}]] } { set note [string range $note 0 [expr {$len - 1}]] } set friendly $note } if { $friendly eq {} && [set mbox $state(local)] ne {} } { #TODO: this path is not covered by tests set mbox [string trim $mbox \"] if {[string first / $mbox] != 0} { set friendly $mbox } elseif {[set friendly [addr_x400 $mbox PN]] ne {}} { } elseif { [set friendly [addr_x400 $mbox S]] ne {} && [set g [addr_x400 $mbox G]] ne {} } { set friendly "$g $friendly" } if {$friendly eq {}} { set friendly $mbox } } } set friendly [string trim $friendly \"] lappend result [list address $address \ comment $state(comment) \ domain $state(domain) \ error $state(error) \ friendly $friendly \ group $state(group) \ local $state(local) \ memberP $state(memberP) \ phrase $state(phrase) \ proper $proper \ route $state(route)] } unset {*}{ state(input) state(glevel) state(buffer) state(lastC) state(tokenL) state(lexemeL) } return $result } # ::mime::parselexeme -- # # Used to implement a lookahead parser. # |
︙ | ︙ | |||
3383 3384 3385 3386 3387 3388 3389 | set state(buffer) end-of-input return [set state(lastC) LX_END] } set c [string index $state(input) 0] set state(input) [string range $state(input) 1 end] | | | 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 | set state(buffer) end-of-input return [set state(lastC) LX_END] } set c [string index $state(input) 0] set state(input) [string range $state(input) 1 end] if {$c eq {(}} { set noteP 0 set quoteP 0 while 1 { append state(buffer) $c #TODO: some of these paths are not covered by tests |
︙ | ︙ | |||
3510 3511 3512 3513 3514 3515 3516 | set state(input) [string range $state(input) 1 end] } return [set state(lastC) LX_ATOM] } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < > > > > > > > > > > > > > > > > > > > > > | > > | > > > > > > | > | | < | | | < < < | < | < | < | > | | > > > > > > > > > > | > | | < | < | < < < < < < < < < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < < < | | < < | < | | | | < < < | | < < < | | | | > | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555 3556 3557 3558 3559 3560 3561 3562 3563 3564 3565 3566 3567 3568 3569 3570 3571 3572 3573 3574 3575 3576 3577 3578 3579 3580 3581 3582 3583 3584 3585 3586 3587 3588 3589 3590 3591 3592 3593 3594 3595 3596 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 3617 3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 | set state(input) [string range $state(input) 1 end] } return [set state(lastC) LX_ATOM] } # ::mime::reversemapencoding -- # # mime::reversemapencodings maps MIME charset types onto tcl encoding names. # Those that are unknown return {}. # # Arguments: # mimeType The MIME charset to convert into a tcl encoding type. # # Results: # Returns the tcl encoding name for the specified mime charset, or {} # if none is known. proc ::mime::reversemapencoding mimeType { variable reversemap set lmimeType [string tolower $mimeType] if {[info exists reversemap($lmimeType)]} { return $reversemap($lmimeType) } return {} } proc ::mime::relax {token args} { upvar 0 $token state upvar 0 state(relax) relax foreach {key val} $args { switch $key { all { dict set relax finalboundary 1 } finalboundary { dict set relax $key [expr {!!$val}] } default { error [list {unknown value for -relax} $key {should be one of} { all finalboundary }] } } } } # ::mime::serialize -- # # Serializes a message to a value or a channel. # # Arguments: # token The MIME token to parse. # channel The channel to copy the message to. # # Results: # Returns nothing unless an error is thrown while the message # is being written to the channel. proc ::mime::serialize {_ args} { set level 0 set chan {} dict for {arg val} $args { switch $arg { -chan { if {$val eq {}} { error [list {chan must not be the empty string}] } set chan [uplevel 1 [list ::namespace which $val]] } -level { set level [expr {$val + 0}] } default { error [list {unknown option} $arg] } } } if {$chan eq {}} { set token [$_ token] upvar 0 $token state set code [catch {serialize_value $_ $level} result copts] return -options $copts $result } else { return [serialize_chan $_ $chan $level] } } proc ::mime::serialize_chan {_ channel level} { set token [$_ token] # FRINK: nocheck upvar 0 $token state upvar 0 state(bodychan) bodychan $_ parsepart set result {} if {!$level && $state(addmimeversion)} { $channel puts [header::serialize MIME-Version $state(version) {}] } contentid $_ if {![header::exists $token content-id] && $state(addcontentid)} { header::setinternal $token Content-ID [contentid $_] } if {![header::exists $token message-id] && $state(addmessageid)} { header::setinternal $token Message-ID [messageid $_] } foreach {name value} [$_ header get] { $channel puts [header::serialize $name {*}$value] } set converter {} set encoding {} if {![info exists state(parts)]} { if {$state(canonicalP)} { set encoding [getTransferEncoding $_] if {$encoding ne {}} { $channel puts "Content-Transfer-Encoding: $encoding" } } } if {[info exists state(error)]} { unset state(error) } if {[info exists state(parts)]} { lassign [$_ contenttype] content params set boundary [dict get $params boundary] switch -glob $content { message/* { $channel puts {} foreach part $state(parts) { serialize_chan $part $channel 1 break } } default { # Note RFC 2046: See serialize_value for details. # # The boundary delimiter MUST occur at the # beginning of a line, i.e., following a CRLF, and # the initial CRLF is considered to be attached to # the boundary delimiter line rather than part of # the preceding part. # # - The above means that the CRLF before $boundary # is needed per the RFC, and the parts must not # have a closing CRLF of their own. See Tcllib bug # 1213527, and patch 1254934 for the problems when # both file/string branches added CRLF after the # body parts. foreach part $state(parts) { $channel puts \n--$boundary serialize_chan $part $channel 1 } $channel puts \n--$boundary-- } } } else { $channel puts {} if {$state(canonicalP)} { set transforms [setencoding $token $channel] $state(fd) seek 0 $state(fd) copy [$channel $ chan] while {[incr transforms -1] >= 0} { $channel $channel } } else { $state(bodychan) seek 0 $state(bodychan) copy [$channel $ chan] } } $channel flush if {[info exists state(error)]} { error $state(error) } } proc ::mime::serialize_value {_ level} { set chan [::tcllib::chan::base .new [info cmdcount]_serialize_value [ tcl::chan::memchan]] $chan configure -translation crlf serialize_chan $_ $chan $level $chan seek 0 $chan configure -translation binary set res [$chan read] $chan close return $res } proc ::mime::setencoding {token chan} { upvar 0 $token state set transforms 0 if {[info exists state(encoding)]} { switch $state(encoding) { base64 { package require tcl::transform::base64 ::tcl::transform::base64 [$chan configure -chan] incr transforms } quoted-printable { package require {tcl transform qp} ::tcl::transform::qp [$chan configure -chan] incr transforms } 7bit - 8bit - binary - {} { # Bugfix for [#477088] # Go ahead, leave chunk alone } default { error [list {Can't handle content encoding} $state(encoding)] } } } return $transforms } proc ::mime::setcharset {_ chan} { set token [$_ token] upvar 0 $token state lassign [$_ contenttype] content params if {[dict exists $params charset]} { set mcharset [dict get $params charset] } else { switch $state(spec) { cgi - http { set mcharset UTF-8 } mime - default { # mime set mcharset US-ASCII } } } set encoding [reversemapencoding $mcharset] if {$encoding eq {}} { $chan configure -translation binary } else { $chan configure -encoding $encoding } return } # ::mime::uniqueID -- # # Used to generate a 'globally unique identifier' for the content-id. # The id is built from the pid, the current time, the hostname, and # a counter that is incremented each time a message is sent. # # Arguments: # # Results: # Returns the a string that contains the globally unique identifier # that should be used for the Content-ID of an e-mail message. proc ::mime::uniqueID {} { set id [base64 -mode encode -- [ sha2::sha256 -bin [expr {rand()}][pid][clock clicks][array get state]]] return $id } # ::mime::unquote # # Removes any enclosing quotes and unquotes quoted pairs in a string. proc ::mime::unquote string { set qstring [string match "\"*" $string] regsub -all {\\(.)} $string[set string {}] {\1} string # this isn't exactly right because it doesn't validate that a quote at the # end of the string wsan't just replaced as part of a quoted pair. if {$qstring} { regexp {^["]?(.*?)["]?$} $string[set string {}] -> string # a quote for vim syntax coloring: " } return $string } # ::mime::word_encode -- # # Word encodes strings as per RFC 2047. # # Arguments: # charset The character set to encode the message to. |
︙ | ︙ | |||
3873 3874 3875 3876 3877 3878 3879 | error [list {unknown charset} $charset] } if {$encodings($charset) eq {}} { error [list {invalid charset} $charset] } | | | 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 | error [list {unknown charset} $charset] } if {$encodings($charset) eq {}} { error [list {invalid charset} $charset] } if {$method ne {base64} && $method ne {quoted-printable}} { error [list {unknown method} $method {must be one of} \ {base64 quoted-printable}] } # default to encoded and a length that won't make the Subject header to long array set options [list -charset_encoded 1 -maxlength 66] array set options $args |
︙ | ︙ | |||
3941 3942 3943 3944 3945 3946 3947 | set count 0 while {$count < $string_length} { set length 0 set encoded_word {} while {$length < $maxlength && $count < $string_length} { set char [string range $unencoded_string $count $count] set enc_char [::encoding convertto $charset $char] | | | 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 | set count 0 while {$count < $string_length} { set length 0 set encoded_word {} while {$length < $maxlength && $count < $string_length} { set char [string range $unencoded_string $count $count] set enc_char [::encoding convertto $charset $char] set qp_enc_char [qp::encode $enc_char 1] set qp_enc_char_length [string length $qp_enc_char] if {$qp_enc_char_length > $maxlength} { error [list maxlength $options(-maxlength) \ {too short for chosen charset and encoding}] } if { $length + [string length $qp_enc_char] > $maxlength |
︙ | ︙ | |||
4019 4020 4021 4022 4023 4024 4025 | } switch -exact $method { base64 { set result [base64 -mode decode -- $string] } quoted-printable { | | | 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 | } switch -exact $method { base64 { set result [base64 -mode decode -- $string] } quoted-printable { set result [qp::decode $string 1] } {} { # Go ahead } default { error "Can't handle content encoding \"$method\"" } |
︙ | ︙ | |||
4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 | namespace eval [namespace parent] { namespace export * } namespace import [namespace parent]::getTransferEncoding namespace import [namespace parent]::parselexeme namespace import [namespace parent]::reversemapencoding namespace import [namespace parent]::uniqueID namespace eval [namespace parent] [ list namespace export -clear {*}$saved ] } [namespace current]] } ## One-Shot Initialization ::apply {{} { variable encList variable encAliasList variable reversemap foreach {enc mimeType} $encList { if {$mimeType eq {}} continue set reversemap([string tolower $mimeType]) $enc } foreach {enc mimeType} $encAliasList { set reversemap([string tolower $mimeType]) $enc } # Drop the helper variables unset encList encAliasList } ::mime} | > > > > > > > > > > > > > | 4041 4042 4043 4044 4045 4046 4047 4048 4049 4050 4051 4052 4053 4054 4055 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073 4074 4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 | namespace eval [namespace parent] { namespace export * } namespace import [namespace parent]::getTransferEncoding namespace import [namespace parent]::parselexeme namespace import [namespace parent]::reversemapencoding namespace import [namespace parent]::uniqueID namespace import [namespace parent]::unquote namespace eval [namespace parent] [ list namespace export -clear {*}$saved ] } [namespace current]] } ## One-Shot Initialization ::apply {{} { variable encList variable encAliasList variable reversemap variable timeformats foreach {enc mimeType} $encList { if {$mimeType eq {}} continue set reversemap([string tolower $mimeType]) $enc } foreach {enc mimeType} $encAliasList { set reversemap([string tolower $mimeType]) $enc } set formats1 { {%a, %d %b %Y %H:%M:%S %z} {%a, %d %b %Y %H:%M %z} {%a , %d %b %Y %H:%M:%S %z} {%a , %d %b %Y %H:%M %z} {%d %b %Y %H:%M:%S %z} {%d %b %Y %H:%M %z} } set timeformats [list {*}$formats1 {*}[string map {%Y %y} $formats1]] # Drop the helper variables unset encList encAliasList } ::mime} |
Changes to modules/mime/mime.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # mime.test - Test suite for TclMIME -*- tcl -*- # # This file contains a collection of tests for one or more of the Tcl # built-in commands. Sourcing this file into Tcl runs the tests and # genere totes output for errors. No output means no errors were found. # # Copyright (c) 2000 by Ajuba Solutions # All rights reserved. # # RCS: @(#) $Id: mime.test,v 1.31 2012/02/23 17:35:17 andreas_kupries Exp $ # ------------------------------------------------------------------------- source [file join \ | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # mime.test - Test suite for TclMIME -*- tcl -*- # # This file contains a collection of tests for one or more of the Tcl # built-in commands. Sourcing this file into Tcl runs the tests and # genere totes output for errors. No output means no errors were found. # # Copyright (c) 2000 by Ajuba Solutions # Copyright (c) 2018 by Poor Yorick # All rights reserved. # # RCS: @(#) $Id: mime.test,v 1.31 2012/02/23 17:35:17 andreas_kupries Exp $ # ------------------------------------------------------------------------- source [file join \ |
︙ | ︙ | |||
25 26 27 28 29 30 31 | use namespacex/namespacex.tcl namespacex } testing { useLocal mime.tcl mime } | < | < > > > > > > > > > > > > > > > | | > | > > | > > | > > > > > > > | | | < | | < < | > | | < < | | | | | | | | | | | | < | | | | < < | > | | | < > | < > | < > < < | | | | < | > | | | < > | < > | < < > | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | use namespacex/namespacex.tcl namespacex } testing { useLocal mime.tcl mime } package require {chan base} # ------------------------------------------------------------------------- namespace import mime::datetime mime::field_decode mime::mapencoding \ mime::.new mime::parseaddress mime::qp mime::reversemapencoding \ mime::word_decode mime::word_encode # ------------------------------------------------------------------------- proc channamescmp names { expr {[llength $names] == [llength [chan names]]} } proc cleanly script { set ns [info cmdcount] namespace eval $ns { namespace path [namespace parent] } catch {namespace eval $ns $script} cres copts foreach name [info vars ${ns}::*] { set val [set $name] if {[namespace which $val] ne {} && [string match ::mime* $val]} { rename $val {} } } namespace delete $ns return -options $copts $cres } proc setup1 {} { uplevel 1 { set channames [chan names] } } proc with.chan {name args} { set body [lindex $args end] set args [lrange $args 0 end-1] set chan chan_[info cmdcount] ::tcllib::chan::base .new $chan [open $name] uplevel 1 [list set tok [.new {} {*}$args -chan $chan]] try { uplevel 1 $body } finally { rename $chan {} } } proc with.file {name args} { set body [lindex $args end] set args [lrange $args 0 end-1] uplevel 1 [list set tok [.new {} {*}$args -file $name]] uplevel 1 $body } proc main {} { variable encoded variable name variable n set message1 {MIME-Version: 1.0 Content-Type: Text/plain I'm the message.} test mime-1.1 {.new with no args} {cleanly { catch .new res subst $res }} {{specify exactly one of} {-chan -file -parts -string}} test mime-2.1 {Generate a MIME message} {cleanly { set tok [.new {} -canonical Text/plain -string {jack and jill}] set msg [$tok serialize] }} "MIME-Version: 1.0\r Message-ID: <ac7319c5a872e80af7fe7fb7efa5fd7ac7356ec74eb4410d23287b6cf7fa0129@|>\r Content-ID: <8e84af0326e6170dfb2720eeb49b23337250b571c563247605c9ec6910772d2c@|>\r Content-Type: text/plain\r \r jack and jill" test mime-2.1.1 {Generate a MIME message} {cleanly { setup1 with.chan [makeFile {jack and jill} input.txt] -canonical Text/plain { set msg [[$tok body raw] read] $tok .destroy # The generated message is predictable except for the Content-ID lappend res $msg lappend res [channamescmp $channames] return $res } }} [list "jack and jill\n" 1] test mime-2.2 {Generate a multi-part MIME message} {cleanly { set tok1 [.new {} -canonical Text/plain -string {jack and jill}] set tok2 [.new {} -canonical Text/plain -string james] set bigTok [.new {} -canonical Multipart/MyType \ -params [list MyParam foo boundary bndry] \ -headers [list Content-Description {Test Multipart}] \ -parts [list $tok1 $tok2]] $bigTok serialize }} "MIME-Version: 1.0\r Message-ID: <0b1ce38bc23a7af000d136b6227d7e47af437c850c83145d8ae807bb4ab4d748@|>\r Content-ID: <b534a48db81537371fd048c0fc6da3047924c773b8f1b1691df58d032ed717c1@|>\r Content-Type: multipart/mytype\r ; myparam=foo\r ; boundary=bndry\r Content-Description: Test Multipart\r \r --bndry\r Message-ID: <ac7319c5a872e80af7fe7fb7efa5fd7ac7356ec74eb4410d23287b6cf7fa0129@|>\r Content-ID: <8e84af0326e6170dfb2720eeb49b23337250b571c563247605c9ec6910772d2c@|>\r Content-Type: text/plain\r \r jack and jill\r --bndry\r Message-ID: <ab7737bc7bbff0d4096ba7e130fd69e77ea5a1e1d7618d17e15f4914bae8600f@|>\r Content-ID: <119c9ae6f9ca741bd0a76f87fba0b22cab5413187afb2906aa2875c38e213603@|>\r Content-Type: text/plain\r \r james\r --bndry--\r " test mime-2.3 {Generate a multi-part MIME message} {cleanly { set tok1 [.new {} -canonical Text/plain -string {jack and jill}] set tok2 [.new {} -canonical Text/plain -string james] set bigTok [.new {} \ -params [list MyParam foo boundary bndry] \ -headers [list Content-Description {Test Multipart}] \ -parts [list $tok1 $tok2]] $bigTok serialize }} "MIME-Version: 1.0\r Message-ID: <6fd7e9b06fe3961dbc67d3bcc058451c3674403801ed18714d11aa200aed02a2@|>\r Content-ID: <b534a48db81537371fd048c0fc6da3047924c773b8f1b1691df58d032ed717c1@|>\r Content-Type: multipart/mixed\r ; myparam=foo\r ; boundary=bndry\r Content-Description: Test Multipart\r \r --bndry\r Message-ID: <ac7319c5a872e80af7fe7fb7efa5fd7ac7356ec74eb4410d23287b6cf7fa0129@|>\r Content-ID: <8e84af0326e6170dfb2720eeb49b23337250b571c563247605c9ec6910772d2c@|>\r Content-Type: text/plain\r \r jack and jill\r --bndry\r Message-ID: <ab7737bc7bbff0d4096ba7e130fd69e77ea5a1e1d7618d17e15f4914bae8600f@|>\r Content-ID: <119c9ae6f9ca741bd0a76f87fba0b22cab5413187afb2906aa2875c38e213603@|>\r Content-Type: text/plain\r \r james\r --bndry--\r " test mime-3.1 {Parse a MIME message} {cleanly { set msg {MIME-Version: 1.0 Content-Type: Text/plain I'm the message.} set tok [.new {} -string $msg] [$tok body raw] read }} {I'm the message.} test mime-3.2 {Parse a multi-part MIME message} {cleanly { set msg {MIME-Version: 1.0 Content-Type: Multipart/foo; boundary="bar" |
︙ | ︙ | |||
207 208 209 210 211 212 213 | MIME-Version: 1.0 Content-Type: Text/plain part3 --bar-- } | | | | | | | | | | | | > > | | < < | | | > > > | | | | | | > > > > > > > > > > > > | | | | | | | < < | | | | | | | | | | | | < < | | < < < < | | | < < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 | MIME-Version: 1.0 Content-Type: Text/plain part3 --bar-- } set tok [.new {} -string $msg] set partToks [$tok property parts] set res {} foreach childTok $partToks { lappend res [[$childTok body raw] read] } set res }} [list part1 part2 part3] test mime-3.3 {Try to parse a totally invalid message} {cleanly { set token [.new {} -string blah] catch {$token header get} err0 set err0 }} {{improper line in header} blah} test mime-3.4 {Try to parse a MIME message with an invalid version} {cleanly { set msg1 {MIME-Version: 2.0 Content-Type: text/plain msg1} set tok [.new {} -string $msg1] catch {[$tok body raw] read} err1 catch {$tok serialize} err1a list $err1 $err1a }} "msg1 {MIME-Version: 2.0\r Message-ID: <d956986793d51d614044c01a7e7650665330a4a170d071aff1de6dceda8c8b0d@|>\r Content-ID: <289e5175e02c788c2d442cfe81d6be0533d8c13e253ef763fda45d37accfe4d4@|>\r Content-Type: text/plain\r \r msg1}" test mime-3.5 {Try to parse a MIME message with no newline between headers and data} {cleanly { set msg2 {MIME-Version: 1.0 Content-Type: foobar data without newline} .new mime1 -string $msg2 catch {mime1 header get} err2 set err2 }} {expecting type/subtype found foobar} test mime-3.6 {Try to parse a MIME message with no MIME version and generate a new message from it} {cleanly { # No MIME version set msg3 {Content-Type: text/plain foo} .new mime1 -string $msg3 catch {[mime1 body raw] read} err3 catch {mime1 serialize} err3a copts list $err3 $err3a }} "foo {MIME-Version: 1.0\r Message-ID: <fb8bfc091f5ff55264834b7ea21278fbcb7bf875b20ddeae2f5e4eb662de2129@|>\r Content-ID: <2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae@|>\r Content-Type: text/plain\r \r foo}" foreach name {file chan} { test mime-3.7.$name {Test mime with a bad email [SF Bug 631314 ]} {cleanly { with.$name $tcltest::testsDirectory/badmail1.txt { set res {} lappend res [llength [$tok property parts]] set ctok [lindex [$tok property parts] 0] lappend res [dictsort [$tok property]] lappend res [dictsort [$ctok property]] $tok .destroy string map [list $ctok CHILD] $res } }} {1 {content multipart/mixed encoding {} params {boundary ----------CSFNU9QKPGZL79} parts CHILD size 0} {content application/octet-stream encoding {} params {} size 0}} } foreach name {file chan} { test mime-3.8.1.$name {Test mime with another bad email [SF Bug 631314 ]} -body {cleanly { with.$name $tcltest::testsDirectory/badmail2.txt { set ctok [lindex [$tok property parts] 0] } }} -returnCodes 1 -result {end-of-string encountered while parsing multipart/form-data} } foreach name {file chan} { test mime-3.8.2.$name {Test mime with another bad email [SF Bug 631314 ]} {cleanly { with.$name $tcltest::testsDirectory/badmail2.txt -relax finalboundary { set res {} set ctok [lindex [$tok property parts] 0] lappend res [dictsort [$tok property]] lappend res [dictsort [$ctok property]] $tok .destroy string map [list $ctok CHILD] $res } }} {{content multipart/related encoding {} params {boundary ----=_NextPart_000_0000_2CBA2CBA.150C56D2} parts CHILD size 879} {content text/html encoding base64 params {} size 879}} } test mime-3.9 {Parse a MIME message with a charset encoded body and use [body decoded] to get it back} {cleanly { set msg {MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Fran\xE7ois } set tok [.new {} -string $msg] [$tok body decoded] read }} {Fran\xE7ois } test mime-3.10 {Parse a MIME message with a charset encoded body and use [body decoded] to get it back (example from encoding man page)} {cleanly { set msg {MIME-Version: 1.0 Content-Type: text/plain; charset=EUC-JP Content-Transfer-Encoding: quoted-printable =A4=CF} set tok [.new {} -string $msg] [$tok body decoded] read }} \u306F test mime-3.11 {Parse a MIME message without a charset encoded body and use [body decoded] to get it back} {cleanly { set msg {MIME-Version: 1.0 Content-Type: text/plain Content-Transfer-Encoding: quoted-printable A plain text message.} set tok [.new {} -string $msg] [$tok body decoded] read }} {A plain text message.} test mime-3.12 {Parse a MIME message with a charset encoded body in an unrecognised charset and use [body decoded] to attempt to get it back} {cleanly { set msg {MIME-Version: 1.0 Content-Type: text/plain; charset=SCRIBBLE Content-Transfer-Encoding: quoted-printable This is a message in the scribble charset that tcl does not recognise.} set tok [.new {} -string $msg] lappend res [$tok header get content-type] lappend res [[$tok body decoded] configure -encoding] catch {[$tok body decoded] read} errmsg lappend res $errmsg }} {{text/plain {charset SCRIBBLE}} binary {This is a message in the scribble charset that tcl does not recognise.}} test mime-4.1 {Test qp::encode with a > 76 character string containing special chars.} {cleanly { set str1 "foo!\"\t barbaz \$ ` \{ # jack and jill went up a hill to fetch a pail of water. Jack fell down and said !\"\#\$@\[\\\]^`\{\|\}\~ \nJill said, \"Oh my\"" qp encode $str1 }} "foo=21=22\t barbaz =24 =60 =7B =23 jack and jill went up a hill to fetch a=\n pail of water. Jack fell down and said =21=22=23=24=40=5B=5C=5D=5E=60=7B=\n=7C=7D=7E =20\nJill said, =22Oh my=22" test mime-4.2 {Check that encode/decode yields original string} {cleanly { set str1 "foo!\"\t barbaz \$ ` \{ # jack and jill went up a hill to fetch a pail of water. Jack fell down and said !\"\#\$@\[\\\]^`\{\|\}\~ \nJill said, \"Oh my\" " set enc [qp encode $str1] set dec [qp decode $enc] string equal $dec $str1 }} 1 test mime-4.3 {decode data that might come from an MUA} {cleanly { set enc "I'm the =22 message =\nwith some new lines= \n but with some extra space, too. " qp decode $enc }} "I'm the \" message with some new lines but with some extra space, too." test mime-4.4 {Test qp::encode with non-US_ASCCI characters.} {cleanly { set str1 "Test de caractères accentués : â î é ç et quelques contrôles \"\[|\]()\"" qp encode $str1 }} "Test de caract=E8res accentu=E9s : =E2 =EE =E9 =E7 et quelques contr=F4le=\ns =22=5B=7C=5D()=22" test mime-4.5 {Test qp::encode with softbreak} {cleanly { set str1 [string repeat abc 40] qp encode $str1 }} "abcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabca= bcabcabcabcabcabcabcabcabcabcabcabcabcabcabcabc" test mime-4.6 {Test qp::encode with softbreak} {cleanly { set str1 [string repeat abc 40] qp encode $str1 0 1 }} [string repeat abc 40] test mime-4.7 {Test qp::encode/decode in encoded_word mode} {cleanly { set enc [qp encode {jack and jill went up the hill} 1] qp decode $enc 1 }} {jack and jill went up the hill} test mime-4.8 {Test qp::encode in encoded_word mode with equal signs} {cleanly { qp encode 1and1=2 1 }} 1and1=3D2 test mime-4.9 {Test qp::encode in encoded_word mode with tabs and spaces} {cleanly { qp encode "1 and 1 =\t2" 1 }} 1_and_1_=3D=092 test mime-4.10 {Test qp::encode in encoded_word mode with underscores} {cleanly { qp encode 2003_06_30 1 }} 2003=5F06=5F30 test mime-4.11 {Test qp::encode in encoded_word mode with underscores and spaces} {cleanly { qp encode {2003_06_30 is 30 June 2003} 1 }} 2003=5F06=5F30_is_30_June_2003 test mime-4.12 {Test qp::encode in encoded_word mode with question marks} {cleanly { qp encode {How long is a piece of string ?} 1 }} How_long_is_a_piece_of_string_=3F test mime-4.13 {Test qp::encode in no_softbreak mode} {cleanly { qp encode {This is a very long string into which we do not want inserted softbreaks as we want one very long line returned even though that's probably not how we whould be doing it (see RFC2047) but we don't want to break backward compatibility} 0 1 }} {This is a very long string into which we do not want inserted softbreaks as we want one very long line returned even though that's probably not how we whould be doing it (see RFC2047) but we don't want to break backward compatibility} test mime-5.1 {Test word_encode with quoted-printable method} {cleanly { word_encode iso8859-1 quoted-printable {Test de contrôle effectué} }} =?ISO-8859-1?Q?Test_de_contr=F4le_effectu=E9?= test mime-5.2 {Test word_encode with base64 method} {cleanly { word_encode iso8859-1 base64 {Test de contrôle effectué} }} =?ISO-8859-1?B?VGVzdCBkZSBjb250cvRsZSBlZmZlY3R16Q==?= test mime-5.3 {Test encode+decode with quoted-printable method} {cleanly { set enc [word_encode iso8859-1 quoted-printable {Test de contrôle effectué}] word_decode $enc }} {iso8859-1 quoted-printable {Test de contrôle effectué}} test mime-5.4 {Test encode+decode with base64 method} {cleanly { set enc [word_encode iso8859-1 base64 {Test de contrôle effectué}] word_decode $enc }} {iso8859-1 base64 {Test de contrôle effectué}} test mime-5.5 {Test decode with lowercase quoted-printable method} {cleanly { word_decode =?ISO-8859-1?q?Test_lowercase_q?= }} {iso8859-1 quoted-printable {Test lowercase q}} test mime-5.6 {Test decode with lowercase base64 method} {cleanly { word_decode =?ISO-8859-1?b?VGVzdCBsb3dlcmNhc2UgYg==?= }} {iso8859-1 base64 {Test lowercase b}} test mime-5.7 {Test word_encode with quoted-printable method across encoded word boundaries} {cleanly { word_encode iso8859-1 quoted-printable {Test de contrôle effectué} -maxlength 31 }} "=?ISO-8859-1?Q?Test_de_contr?= =?ISO-8859-1?Q?=F4le_effectu?= =?ISO-8859-1?Q?=E9?=" test mime-5.8 {Test word_encode with quoted-printable method across encoded word boundaries} {cleanly { word_encode iso8859-1 quoted-printable {Test de contrôle effectué} -maxlength 32 }} "=?ISO-8859-1?Q?Test_de_contr?= =?ISO-8859-1?Q?=F4le_effectu?= =?ISO-8859-1?Q?=E9?=" test mime-5.9 {Test word_encode with quoted-printable method and multibyte character} {cleanly { word_encode euc-jp quoted-printable "Following me is a multibyte character \xA4\xCF" }} =?EUC-JP?Q?Following_me_is_a_multibyte_character_=A4=CF?= set n 10 while {$n < 14} { test mime-5.$n {Test word_encode with quoted-printable method and multibyte character across encoded word boundary} {cleanly { word_encode euc-jp quoted-printable "Following me is a multibyte character \xA4\xCF" -maxlength [expr 42 + $n] }} "=?EUC-JP?Q?Following_me_is_a_multibyte_character_?= =?EUC-JP?Q?=A4=CF?=" incr n } test mime-5.14 {Test word_encode with quoted-printable method and multibyte character (triple)} {cleanly { word_encode utf-8 quoted-printable "Here is a triple byte encoded character \xE3\x81\xAF" }} =?UTF-8?Q?Here_is_a_triple_byte_encoded_character_=E3=81=AF?= set n 15 while {$n < 23} { test mime-5.$n {Test word_encode with quoted-printable method and triple byte character across encoded word boundary} {cleanly { word_encode utf-8 quoted-printable "Here is a triple byte encoded character \xE3\x81\xAF" -maxlength [expr 38 + $n] }} "=?UTF-8?Q?Here_is_a_triple_byte_encoded_character_?= =?UTF-8?Q?=E3=81=AF?=" incr n } while {$n < 25} { test mime-5.$n {Test word_encode with quoted-printable method and triple byte character across encoded word boundary} {cleanly { word_encode utf-8 quoted-printable "Here is a triple byte encoded character \xE3\x81\xAF" -maxlength [expr 38 + $n] }} =?UTF-8?Q?Here_is_a_triple_byte_encoded_character_=E3=81=AF?= incr n } while {$n < 29} { test mime-5.$n {Test word_encode with base64 method across encoded word boundaries} {cleanly { word_encode euc-jp base64 "There is a multibyte character \xA4\xCF" -maxlength [expr 28 + $n] }} "=?EUC-JP?B?VGhlcmUgaXMgYSBtdWx0aWJ5dGUgY2hhcmFjdGVy?= =?EUC-JP?B?IKTP?=" incr n } while {$n < 33} { test mime-5.$n {Test word_encode with base64 method and triple byte character across encoded word boundary} {cleanly { word_encode utf-8 base64 "Here is a multibyte character \xE3\x81\xAF" -maxlength [expr 23 + $n] }} "=?UTF-8?B?SGVyZSBpcyBhIG11bHRpYnl0ZSBjaGFyYWN0ZXIg?= =?UTF-8?B?44Gv?=" incr n } test mime-5.33 {Test word_encode with quoted-printable method and -maxlength set to same length as will the result} {cleanly { word_encode iso8859-1 quoted-printable 123 -maxlength 20 }} =?ISO-8859-1?Q?123?= test mime-5.34 {Test word_encode with base64 method and -maxlength set to same length as will the result} {cleanly { word_encode iso8859-1 base64 123 -maxlength 21 }} =?ISO-8859-1?B?MTIz?= test mime-5.35 {Test word_encode with quoted-printable method and non charset encoded string} {cleanly { word_encode utf-8 quoted-printable \u306F -charset_encoded 0 }} =?UTF-8?Q?=E3=81=AF?= test mime-5.36 {Test word_encode with base64 method and non charset encoded string} {cleanly { word_encode utf-8 base64 \u306F -charset_encoded 0 }} =?UTF-8?B?44Gv?= test mime-5.36 {Test word_encode with base64 method and one byte} {cleanly { word_encode iso8859-1 base64 a }} =?ISO-8859-1?B?YQ==?= test mime-5.37 {Test word_encode with base64 method and two bytes} {cleanly { word_encode euc-jp base64 \xA4\xCF }} =?EUC-JP?B?pM8=?= test mime-5.38 {Test word_encode with unknown charset} {cleanly { catch {word_encode scribble quoted-printable {scribble is an unknown charset}} errmsg set errmsg }} {{unknown charset} scribble} test mime-5.39 {Test word_encode with invalid charset} {cleanly { catch {word_encode unicode quoted-printable {unicode is not a valid charset}} errmsg set errmsg }} {{invalid charset} unicode} test mime-5.40 {Test word_encode with invalid method} {cleanly { catch {word_encode iso8859-1 tea-leaf {tea-leaf is not a valid method}} errmsg set errmsg }} {{unknown method} tea-leaf {must be one of} {base64 quoted-printable}} test mime-5.41 {Test word_encode with maxlength to short for method quoted-printable} {cleanly { catch {word_encode iso8859-1 quoted-printable 1 -maxlength 17} errmsg set errmsg }} {maxlength 17 {too short for chosen charset and encoding}} test mime-5.42 {Test word_encode with maxlength on the limit for quoted_printable and an unquoted character} {cleanly { catch {word_encode iso8859-1 quoted-printable _ -maxlength 20} errmsg set errmsg }} =?ISO-8859-1?Q?=5F?= test mime-5.43 {Test word_encode with maxlength to short for method quoted_printable and a character to be quoted} {cleanly { catch {word_encode iso8859-1 quoted-printable = -maxlength 18} errmsg set errmsg }} {maxlength 18 {too short for chosen charset and encoding}} test mime-5.44 {Test word_encode with maxlength to short for method quoted-printable and multibyte character} {cleanly { catch {word_encode euc-jp quoted-printable \xA4\xCF -maxlength 17} errmsg set errmsg }} {maxlength 17 {too short for chosen charset and encoding}} test mime-5.45 {Test word_encode with maxlength to short for method base64} {cleanly { catch {word_encode iso8859-1 base64 1 -maxlength 20} errmsg set errmsg }} {maxlength 20 {too short for chosen charset and encoding}} test mime-6.1 {Test field_decode (from RFC 2047, part 8)} {cleanly { field_decode {=?US-ASCII?Q?Keith_Moore?= <[email protected]>} }} {Keith Moore <[email protected]>} test mime-6.2 {Test field_decode (from RFC 2047, part 8)} {cleanly { field_decode {=?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <[email protected]>} }} {Patrik Fältström <[email protected]>} test mime-6.3 {Test field_decode (from RFC 2047, part 8)} {cleanly { field_decode {=?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?= =?ISO-8859-2?B?dSB1bmRlcnN0YW5kIHRoZSBleGFtcGxlLg==?=} }} {If you can read this you understand the example.} foreach {n encoded expected} { 4 (=?ISO-8859-1?Q?a?=) (a) 5 {(=?ISO-8859-1?Q?a?= b)} |
︙ | ︙ | |||
642 643 644 645 646 647 648 | {(ax b)} 12 {a b c} {a b c} 13 {} {} } { test mime-6.$n {Test field_decode (from RFC 2047, part 8)} {cleanly { | | | | | | | | | | | | | | | | 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 | {(ax b)} 12 {a b c} {a b c} 13 {} {} } { test mime-6.$n {Test field_decode (from RFC 2047, part 8)} {cleanly { field_decode $encoded }} $expected ; # {} } foreach {bug n encoded expected} { 764702 1 {(=?utf-8?Q?H=C3=BCrz?=)} {(Hürz)} } { test mime-7.$n "Test field_decode (from SF Tcllib bug $bug)" {cleanly { field_decode $encoded }} $expected ; # {} } test mime-8.1 {Test reversemapencoding+mapencoding with preferred name} {cleanly { set charset [reversemapencoding US-ASCII] mapencoding $charset }} US-ASCII test mime-8.2 {Test reversemapencoding+mapencoding with alias} {cleanly { set charset [reversemapencoding UTF8] mapencoding $charset }} UTF-8 foreach name {file chan} { test mime-9.0.$name {Test chunk handling of serialize and helpers} {cleanly { set in [makeFile [set data [string repeat [string repeat {123456789 } 10]\n 350]] input.txt] set mi [makeFile {} mime.txt] with.$name $in -canonical text/plain { ::tcllib::chan::base .new chan1 [open $mi w] chan1 configure -translation binary $tok serialize -chan chan1 chan1 close with.$name $mi { set newdata [[$tok body raw] read] set res [string compare $data $newdata] removeFile input.txt removeFile mime.txt unset data newdata tok in mi set res } } }} 0 } set ::env(TZ) UTC0 set epoch [clock scan 2000-01-01] foreach {n stamp date} { 1 86340 {Sat, 01 Jan 2000 23:59:00 +0000} 2 5176620 {Tue, 29 Feb 2000 21:57:00 +0000} 3 31610520 {Sun, 31 Dec 2000 20:42:00 +0000} 4 31708740 {Mon, 01 Jan 2001 23:59:00 +0000} 5 68248620 {Thu, 28 Feb 2002 21:57:00 +0000} 6 126218520 {Wed, 31 Dec 2003 20:42:00 +0000} } { test mime-10.$n "Test formatting dates (RFC 822)" { # To verify that clock scan gets the expected value. set stamp_test [expr {[datetime $date clock] - $epoch}] # Parse and re-format should get us the original. set parsed_test [datetime $date proper] list $stamp_test $parsed_test } [list $stamp $date] } test mime-11.0 {Bug 1825092} {cleanly { set in [makeFile {From [email protected] Sat Oct 20 17:58:49 2007 |
︙ | ︙ | |||
740 741 742 743 744 745 746 | Content-Type: application/octet-stream; name="a0036.dss" BGRzcwEAAQABAAAAYQAAAAAAAAAAAAAAAAAAACQAAAD+//7/+/8wNzA2MTYwODE1MjQwNzA2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ --------------090305080603000703000106-- } mail_part] | | | | | | | | | < > > | | | | | | | | > > > > > > > > > > | | < | | | | | | | < | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 | Content-Type: application/octet-stream; name="a0036.dss" BGRzcwEAAQABAAAAYQAAAAAAAAAAAAAAAAAAACQAAAD+//7/+/8wNzA2MTYwODE1MjQwNzA2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ --------------090305080603000703000106-- } mail_part] set token [.new {} -file $in] set allparts [$token property parts] set attachment [lindex $allparts 1] set out [makeFile {} mail_att] ::tcllib::chan::base .new chan1 [open $out w] chan1 configure -translation binary $attachment serialize -chan chan1 -level 1 chan1 close set data [viewFile $out] file delete $in $out set data }} {Message-ID: <f60c78370648168a30158b8cda876db2f875d1531e86e5594c5a108dcf5209db@|> Content-ID: <93755e1312eacd488b6170673caa5e7a9a9445ce82ff11c934055bd1f907b229@|> Content-Type: application/octet-stream ; name=a0036.dss Content-Disposition: attachment ; filename=a0036.dss Content-Transfer-Encoding: base64 BGRzcwEAAQABAAAAYQAAAAAAAAAAAAAAAAAAACQAAAD+//7/+/8wNzA2MTYwODE1MjQwNzA2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ} # ------------------------------------------------------------------------- test mime-12.0 {Bug 3483716} {cleanly { set token [.new {} -string {Content-Type: message/delivery-status; name="deliverystatus.txt" Content-Disposition: attachment; filename="deliverystatus.txt"; size=138; creation-date="Thu, 02 Feb 2012 13:50:05 GMT"; modification-date="Thu, 02 Feb 2012 13:50:05 GMT" Content-Description: deliverystatus.txt Content-Transfer-Encoding: base64 T3JpZ2luYWwtUmVjaXBpZW50OiA8L2ZheD1ibHViYkBndW1taS5ib290PgpBY3Rpb246IGZhaWxl ZApEaWFnbm9zdGljLUNvZGU6IHNtdHA7IDU1MCAjNS4xLjAgQWRkcmVzcyByZWplY3RlZC4KUmVt b3RlLU1UQTogNTMuMjQuMjgyLjE1MA== }] set parts [$token property parts] set result [[lindex $parts end] header get Remote-MTA] return $result }} {53.24.282.150 {}} # ------------------------------------------------------------------------- test mime-13.0 {cleanly { issue a16b1095974e071d }} { set msg "MIME-Version: 1.0 Content-Type: text/plain\r \r so plain " set tok [.new {} -string $msg] [$tok body raw] read } "so plain\n" # ------------------------------------------------------------------------- test mime-14.0 {cleanly { hostname argument to parseaddress }} { set parsed [parseaddress hostname fakedomain.fake {Here <h>}] list [llength $parsed] [lindex $parsed 0] } [list 1 [list address [email protected] comment {} domain {} error {} \ friendly Here group {} local h memberP 0 phrase Here \ proper {Here <[email protected]>} route {}]] test mime-14.1 {cleanly { special characters in local part }} { set parsed [parseaddress hostname fakedomain.fake foo<>[email protected]] list [llength $parsed] [lindex $parsed 0] } [list 1 [list address {} comment {} domain {} \ error {expecting mailbox in local-part (found >)} friendly foo group {} \ local {} memberP 0 phrase foo proper {foo <>} route {}]] test mime-14.2 {cleanly { special characters in local part }} { set parsed [parseaddress hostname fakedomain.fake {"foobar"@grill.com}] list [llength $parsed] [lindex $parsed 0] } [list 1 [ list address {"foobar"@grill.com} comment {} domain grill.com error {} \ friendly foobar group {} local {"foobar"} memberP 0 phrase {} \ proper {"foobar"@grill.com} route {}]] # ------------------------------------------------------------------------- test mime-15.0 {cleanly { a multipart/mixed message with an invalid body }} { set msg "MIME-Version: 1.0 Content-Type: multipart/mixed; boundary=\"something\"\r \r so plain " set tok [.new {} -string $msg] $tok header get } [list Content-Type {multipart/mixed {boundary something}}] # ------------------------------------------------------------------------- test mime-16.0 {cleanly { }} { set msg "MIME-Version: 1.0 Content-Type: text/plain\r Content-Disposition: attachment ; param1=\"a parameter value\"; param2*1=\"another param\"; param2*2=\"eter value\" \r \r so plain " set tok [.new {} -string $msg] $tok header get } [list Content-Type {text/plain {}} Content-Disposition [ list attachment [list param1 {a parameter value} \ param2 {another parameter value}]]] set char [encoding convertfrom utf-8 \xE3\x81\xAF] test mime-16.1 {cleanly { }} { set res {} set mime [.new {} -canonical text/plain -string {dawg one}] $mime header set Content-Disposition attachment [list param1 $char] set msg [$mime serialize] lappend res $msg set mime2 [.new {} -string $msg] lappend res [$mime2 header get] return $res } [list "MIME-Version: 1.0\r Message-ID: <c7aa6d4b48aa9fcfe18d9c24adf71de96efbaaf2e261eb1de738ef45f453d1a4@|>\r Content-ID: <086570e97284c5bc5145f32689a5342363b10b64963446293b83930fa8a9fa45@|>\r Content-Type: text/plain\r Content-Disposition: attachment\r ; param1*0*=utf-8''%E3%81%AF\r \r dawg one" \ [list Content-ID {<086570e97284c5bc5145f32689a5342363b10b64963446293b83930fa8a9fa45@|> {}} Content-Type {text/plain {}} \ Message-ID {<c7aa6d4b48aa9fcfe18d9c24adf71de96efbaaf2e261eb1de738ef45f453d1a4@|> {}} \ Content-Disposition [list attachment [list param1 $char]]] ] # ------------------------------------------------------------------------- test mime-17.1 {header parsing} {cleanly { set mime [.new {} -string {Content-Type: text/html}] $mime header get Content-Type }} {text/html {}} test mime-17.2 {header parsing} {cleanly { set mime [.new {} -string {Content-Type: text/html; charset=iso-8859-1}] $mime header get Content-Type }} {text/html {charset iso-8859-1}} test mime-17.3 {header parsing} { set mime [.new {} -string {Content-Type: text/html; charset='iso-8859-1'}] $mime header get Content-Type } {text/html {charset 'iso-8859-1'}} test mime-17.4 {header parsing} { set mime [.new {} -string {Content-Type: text/html; charset="iso-8859-1"}] $mime header get Content-Type } {text/html {charset iso-8859-1}} test mime-17.5 {header parsing} -body { set mime [.new {} -string {Content-Type: text/html; charset="iso-8859-1"; ignored}] $mime header get Content-Type } -returnCodes 1 -result {expecting = found end-of-input} test mime-17.6 {header parsing} -body { set mime [.new {} -string {Content-Type: text/html; charset="iso-8859-1"morecrap}] $mime header get Content-Type } -returnCodes 1 -result {expecting = found end-of-input} test mime-17.7 {header parsing} { set mime [.new {} -string {Content-Type: test/test; foo="bar\"baz\""}] $mime header get Content-Type } [list test/test [list foo bar"baz"]] test mime-17.8 {header parsing} { set mime [.new {} -string {Content-Type: test/test; foo=""}] $mime header get Content-Type } {test/test {foo {}}} test mime-17.9 { header supplied by a component message, retrieved by lowercase name } { set mime [.new {} -string {Content-Disposition: form-data; name="field2"}] $mime header get content-disposition } {form-data {name field2}} test mime-17.10 { Content-Type is not automatically added to a subordinate } { set mime [.new {} -string {Content-Disposition: form-data; name="field2"}] $mime header get content-disposition } {form-data {name field2}} test mime-18.1 { non-seekable channel } { set script [list puts -nonewline $message1] set chan [open |[list [info nameofexecutable] <<$script]] tcllib::chan::base .new chan1 $chan set mime [.new {} -spec http -chan chan1] $mime serialize } "Content-Type: text/plain\r \r I'm the message." test mime-19.1 { -http } { set mime [.new {} -spec http -string {}] $mime serialize } "\r " test mime-19.1 { cookie serialization } { set mime [.new {} -spec http -string {}] $mime cookie set one two set res [$mime serialize] $mime .destroy return $res } "Set-Cookie: one=two\r \t; HttpOnly\r \r " test mime-19.2 { cookie serialization } { set mime [.new {} -spec http -string {}] $mime cookie set one two path /three/four set res [$mime serialize] $mime .destroy return $res } "Set-Cookie: one=two\r \t; path=/three/four\r \t; HttpOnly\r \r " testsuiteCleanup set [namespace current]::done 1 return } after 0 [list ::coroutine [info cmdcount]_main [namespace which main]] vwait [namespace current]::done return |
Changes to modules/mime/pkgIndex.tcl.
1 2 3 4 | if {![package vsatisfies [package provide Tcl] 8.3]} {return} package ifneeded smtp 1.5 [list source [file join $dir smtp.tcl]] if {![package vsatisfies [package provide Tcl] 8.5]} {return} package ifneeded mime 1.7 [list source [file join $dir mime.tcl]] | > | 1 2 3 4 5 | if {![package vsatisfies [package provide Tcl] 8.3]} {return} package ifneeded smtp 1.5 [list source [file join $dir smtp.tcl]] if {![package vsatisfies [package provide Tcl] 8.5]} {return} package ifneeded mime 1.7 [list source [file join $dir mime.tcl]] package ifneeded {mime qp} 1.7 [list source [file join $dir qp.tcl]] |
Added modules/mime/qp.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | #! /usr/bin/env tclsh # ::mime::qp::encode -- # # Tcl version of quote-printable encode # # Arguments: # string The string to quote. # encoded_word Boolean value to determine whether or not encoded words # (RFC 2047) should be handled or not. (optional) # # Results: # The properly quoted string is returned. namespace eval ::mime::qp { namespace ensemble create namespace export decode encode } proc ::mime::qp::encode {string {encoded_word 0} {no_softbreak 0}} { # 8.1+ improved string manipulation routines used. # Replace outlying characters, characters that would normally # be munged by EBCDIC gateways, and special Tcl characters "[\]{} # with =xx sequence if {$encoded_word} { # Special processing for encoded words (RFC 2047) set regexp {[\x00-\x08\x0B-\x1E\x21-\x24\x3D\x40\x5B-\x5E\x60\x7B-\xFF\x09\x5F\x3F]} lappend mapChars { } _ } else { set regexp {[\x00-\x08\x0B-\x1E\x21-\x24\x3D\x40\x5B-\x5E\x60\x7B-\xFF]} } regsub -all -- $regexp $string {[format =%02X [scan "\\&" %c]]} string # Replace the format commands with their result set string [subst -novariables $string] # soft/hard newlines and other # Funky cases for SMTP compatibility lappend mapChars " \n" =20\n \t\n =09\n \n\.\n =2E\n "\nFrom " "\n=46rom " set string [string map $mapChars $string] # Break long lines - ugh # Implementation of FR #503336 if {$no_softbreak} { set result $string } else { set result {} foreach line [split $string \n] { while {[string length $line] > 72} { set chunk [string range $line 0 72] if {[regexp -- (=|=.)$ $chunk dummy end]} { # Don't break in the middle of a code set len [expr {72 - [string length $end]}] set chunk [string range $line 0 $len] incr len set line [string range $line $len end] } else { set line [string range $line 73 end] } append result $chunk=\n } append result $line\n } # Trim off last \n, since the above code has the side-effect # of adding an extra \n to the encoded string and return the # result. set result [string range $result 0 end-1] } # If the string ends in space or tab, replace with =xx set lastChar [string index $result end] if {$lastChar eq { }} { set result [string replace $result end end =20] } elseif {$lastChar eq "\t"} { set result [string replace $result end end =09] } return $result } # ::mime::qp_decode -- # # Tcl version of quote-printable decode # # Arguments: # string The quoted-printable string to decode. # encoded_word Boolean value to determine whether or not encoded words # (RFC 2047) should be handled or not. (optional) # # Results: # The decoded string is returned. proc ::mime::qp::decode {string {encoded_word 0}} { # 8.1+ improved string manipulation routines used. # Special processing for encoded words (RFC 2047) if {$encoded_word} { # _ == \x20, even if SPACE occupies a different code position set string [string map [list _ \u0020] $string] } # smash the white-space at the ends of lines since that must've been # generated by an MUA. regsub -all -- {[ \t]+\n} $string \n string set string [string trimright $string " \t"] # Protect the backslash for later subst and # smash soft newlines, has to occur after white-space smash # and any encoded word modification. #TODO: codepath not tested set string [string map [list \\ {\\} =\n {}] $string] # Decode specials regsub -all -nocase {=([a-f0-9][a-f0-9])} $string {\\u00\1} string # process \u unicode mapped chars return [subst -novariables -nocommands $string] } package provide {mime qp} 1.7 |
Changes to modules/mime/smtp.tcl.
1 2 3 4 5 6 7 8 9 10 11 | # smtp.tcl - SMTP client # # Copyright (c) 1999-2000 Marshall T. Rose # Copyright (c) 2003-2006 Pat Thoyts # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # package require Tcl 8.3 package require mime 1.7- | > | 1 2 3 4 5 6 7 8 9 10 11 12 | # smtp.tcl - SMTP client # # Copyright (c) 1999-2000 Marshall T. Rose # Copyright (c) 2003-2006 Pat Thoyts # Copyright (c) 2003-2018 Poor Yorick # # See the file "license.terms" for information on usage and redistribution # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # package require Tcl 8.3 package require mime 1.7- |
︙ | ︙ | |||
107 108 109 110 111 112 113 | # -password authentication. # # Results: # Message is sent. On success, return "". On failure, throw an # exception with an error code and error message. proc ::smtp::sendmessage {part args} { | < < | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | # -password authentication. # # Results: # Message is sent. On success, return "". On failure, throw an # exception with an error code and error message. proc ::smtp::sendmessage {part args} { # Here are the meanings of the following boolean variables: # aloP -- value of -atleastone option above. # debugP -- value of -debug option above. # origP -- 1 if -originator option was specified, 0 otherwise. # queueP -- value of -queue option above. set aloP 0 |
︙ | ︙ | |||
304 305 306 307 308 309 310 | lappend header($senderL) $aprops(address) } } } # We're done parsing the arguments. | | | 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | lappend header($senderL) $aprops(address) } } } # We're done parsing the arguments. if {$recipients ne {}} { set who -recipients } elseif {![info exists header($toL)]} { error "need -header \"$toM ...\"" } else { set recipients [join $header($toL) ,] # Add Cc values to recipients list set who $toM |
︙ | ︙ | |||
382 383 384 385 386 387 388 | lappend vrecipients $aprops(address) } # If there's no date header, get the date from the mime message. Same for # the message-id. if {([lsearch -exact $lowerL $dateL] < 0) \ | | | | | < | > | | | | | < > | < < | | | | | | | < < < < < | | < | | | > > | | | | | | < < | > | | | > | | | | > | > > > | > | | | | | | | 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | lappend vrecipients $aprops(address) } # If there's no date header, get the date from the mime message. Same for # the message-id. if {([lsearch -exact $lowerL $dateL] < 0) \ && ([catch {$part header get $dateL}])} { lappend lowerL $dateL lappend mixedL $dateM lappend header($dateL) [::mime::datetime -now proper] } if {([lsearch -exact $lowerL ${message-idL}] < 0) \ && ([catch {$part header get ${message-idL}}])} { lappend lowerL ${message-idL} lappend mixedL ${message-idM} lappend header(${message-idL}) [::mime::uniqueID] } # Get all the headers from the MIME object and save them so that they can # later be restored. set savedH [$part header get] # Take all the headers defined earlier and add them to the MIME message. foreach lower $lowerL mixed $mixedL { foreach value $header($lower) { $part header set $mixed $value -mode append } } if {[string length $client] < 1} { if {![string compare $servers localhost]} { set client localhost } else { set client [info hostname] } } # Create smtp token, which essentially means begin talking to the SMTP # server. set token [initialize -debug $debugP -client $client \ -maxsecs $maxsecs -usetls $tlsP \ -multiple $bccP -queue $queueP \ -servers $servers -ports $ports \ -tlspolicy $tlspolicy -tlsimport $tlsimport \ -username $username -password $password] if {![string match ::smtp::* $token]} { # An error occurred and $token contains the error info array set respArr $token return -code error $respArr(diagnostic) } set code1 [catch { sendmessageaux $token $part $sender $vrecipients $aloP } cres1 copts1] lappend results $code1 $cres1 $copts1 if {!$code1 && $bccP} { # Send the message to bcc recipients as a MIME attachment. set inner [::mime::.new {} -canonical message/rfc822 \ -headers [list Content-Description \ {Original Message}] \ -parts [list $part]] set subject "\[$bccM\]" if {[info exists header(subject)]} { append subject " " [lindex $header(subject) 0] } set outer [::mime::.new {} \ -canonical multipart/digest \ -headers [list \ From [list $originator {}] \ Bcc Date [::mime::datetime -now proper] \ Subject $subject \ Message-ID [::mime::uniqueID] \ Content-Description {Blind Carbon Copy} \ -parts [list $inner]]] set code2 [catch { sendmessageaux $token $outer $sender $brecipients $aloP } cres2 copts2] lappend results $code2 $cres2 $copts2 catch {$inner .destroy -subordinates none} catch {$outer .destroy -subordinates none} } # Determine if there was any error in prior operations and set errorcodes # and error messages appropriately. foreach {code cres copts} $results { # handle just the first one switch -- $code { 0 { set status orderly } 7 { dict set copts -code 1 set status abort break } default { set status abort break } } } set code3 [catch {finalize $token -close $status} cres3 copts3] if {$code3 && !$code} { lassign [list $cres3 $copts3] cres copts } # Destroy SMTP token 'cause we're done with it. # Restore provided MIME object to original state (without the SMTP headers). foreach {key val} [$part header get] { $part header set $key "" -mode delete } foreach {key value} $savedH { $part header set $key {*}$value -mode append } return -options $copts $cres } # ::smtp::sendmessageaux -- # # Sends a mime object (containing a message) to some recipients using an # existing SMTP token. # |
︙ | ︙ | |||
911 912 913 914 915 916 917 | # # Results: # SMTP connection is closed and state variables are cleared. If there's # an error while attempting to close the connection to the SMTP server, # throw an exception with the error code and error message. proc ::smtp::finalize {token args} { | < > | | | | | | | | | | | | | | | | | | < | | | | > | | | | | | | | | < | 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 | # # Results: # SMTP connection is closed and state variables are cleared. If there's # an error while attempting to close the connection to the SMTP server, # throw an exception with the error code and error message. proc ::smtp::finalize {token args} { # FRINK: nocheck variable $token upvar 0 $token state array set options [list -close orderly] array set options $args try { switch -- $options(-close) { orderly { set code [catch { talk $token 120 QUIT } result] } abort { set code [catch { talk $token 0 RSET talk $token 0 QUIT } result] } drop { set code 0 set result "" } default { error "unknown value for -close $options(-close)" } } } finally { if {$state(sd) in [chan names]} { close $state(sd) } if {$state(afterID) ne {}} { after cancel $state(afterID) } foreach name [array names state] { unset state($name) } # FRINK: nocheck unset $token } } # ::smtp::winit -- # # Send originator info to SMTP server. This occurs after HELO/EHLO # command has completed successfully (in ::smtp::initialize). This function # is called by ::smtp::sendmessageaux. |
︙ | ︙ | |||
992 993 994 995 996 997 998 | set from "$mode FROM:<$originator>" # RFC 1870 - SMTP Service Extension for Message Size Declaration if {[info exists state(esmtp)] && [lsearch -glob $state(esmtp) "SIZE*"] != -1} { catch { | | | 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 | set from "$mode FROM:<$originator>" # RFC 1870 - SMTP Service Extension for Message Size Declaration if {[info exists state(esmtp)] && [lsearch -glob $state(esmtp) "SIZE*"] != -1} { catch { set size [string length [$part serialize]] append from " SIZE=$size" } } array set response [set result [talk $token 600 $from]] if {$response(code) == 250} { |
︙ | ︙ | |||
1133 1134 1135 1136 1137 1138 1139 | # replace all '.'s that start their own line with '..'s, and # then write the mime body out to the filehandle. Do not forget to # deal with bare LF's here too (SF bug #499242). if {$trf} { set code [catch { ::mime::copymessage $part $state(sd) } result] } else { | | | 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 | # replace all '.'s that start their own line with '..'s, and # then write the mime body out to the filehandle. Do not forget to # deal with bare LF's here too (SF bug #499242). if {$trf} { set code [catch { ::mime::copymessage $part $state(sd) } result] } else { set code [catch {$part serialize} result] if {$code == 0} { # Detect and transform bare LF's into proper CR/LF # sequences. while {[regsub -all -- {([^\r])\n} $result "\\1\r\n" result]} {} regsub -all -- {\n\.} $result "\n.." result |
︙ | ︙ | |||
1298 1299 1300 1301 1302 1303 1304 | array set options $state(options) if {$options(-debug)} { puts stderr "--> $command (wait upto $secs seconds)" flush stderr } | > | | > | 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 | array set options $state(options) if {$options(-debug)} { puts stderr "--> $command (wait upto $secs seconds)" flush stderr } if {[catch { puts -nonewline $state(sd) $command\r\n flush $state(sd) } result] } { return [list code 400 diagnostic $result] } if {$secs == 0} { return "" } |
︙ | ︙ | |||
1328 1329 1330 1331 1332 1333 1334 | proc ::smtp::hear {token secs} { # FRINK: nocheck variable $token upvar 0 $token state array set options $state(options) | | | | 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 | proc ::smtp::hear {token secs} { # FRINK: nocheck variable $token upvar 0 $token state array set options $state(options) array set response [list args {}] set firstP 1 while 1 { if {$secs >= 0} { ## SF [ 836442 ] timeout with large data ## correction, aotto 031105 - if {$secs > 600} {set secs 600} set state(afterID) [after [expr {$secs*1000}] \ [list ::smtp::timer $token]] } |
︙ | ︙ |
Added modules/mime/smtp.test.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | #! /usr/bin/env tclsh # copyright 2018 Poor Yorick source [file join \ [file dirname [file dirname [file dirname [ file normalize [info script]/...]]]]/devtools/testutilities.tcl] testsNeedTcl 8.5 testsNeedTcltest 1.0- testing { useLocal smtp.tcl smtp } package require nettool package require smtpd variable port [nettool::allocate_port 1025] smtpd::start localhost $port proc accept msg { variable result set headers [$msg header get] lappend result [lsort [dict keys $headers]] lappend result [$msg header get From] return } proc main {} { variable done variable result test smtp-1.1 {} -body { variable port smtpd::configure -deliverMIME [namespace which accept] set msg [mime::.new {} -canonical text/plain \ -string {a door is ajar}] set res [smtp::sendmessage $msg -ports $port -originator Slawkenbergius -recipients {[email protected]}] lappend result $res return $result } -cleanup { unset result } -result [list \ {Bcc Content-ID Content-Type Date From Message-ID Received Return-Path} {Slawkenbergius {}} {} ] testsuiteCleanup set done 1 return } after 0 [list coroutine [info cmdcount]_main [namespace which main]] vwait [namespace current]::done |
Changes to modules/namespacex/namespacex.tcl.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # # ## ### ##### ######## ############# ###################### ## Requisites package require Tcl 8.5 ; # namespace ensembles, {*} namespace eval ::namespacex { | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # # ## ### ##### ######## ############# ###################### ## Requisites package require Tcl 8.5 ; # namespace ensembles, {*} namespace eval ::namespacex { namespace export add hook import info normalize strip state namespace ensemble create namespace eval hook { namespace export add proc on next namespace ensemble create # add - hook a command prefix into the chain of unknown handlers for a |
︙ | ︙ | |||
179 180 181 182 183 184 185 186 187 188 189 190 191 192 | return -code $rc $result } } # # ## ### ##### ######## ############# ###################### ## Implementation :: Info - Visible API proc ::namespacex::info::allvars ns { set ns [uplevel 1 [list [namespace parent] normalize $ns]] ::set result [::info vars ${ns}::*] foreach cns [allchildren $ns] { lappend result {*}[::info vars ${cns}::*] } | > > > > > > > > > > > > > > > > > > > > > > > > > | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | return -code $rc $result } } # # ## ### ##### ######## ############# ###################### ## Implementation :: Info - Visible API proc ::namespacex::import {from args} { set upns [uplevel 1 {namespace current}] if {![string match ::* $from]} { set from $upns::$from[set from {}] } set orig [namespace eval $from {namespace export}] try { namespace eval $from {namespace export *} set tmp [namespace current]::[::info cmdcount] namespace eval $tmp [list ::namespace import ${from}::*] if {[llength $args] == 1} { lappend args [lindex $args 0] } dict size $args foreach {old new} $args { rename ${tmp}::$old ${upns}::$new } namespace delete $tmp } finally { namespace eval $from [list ::namespace export -clear {*}$orig] } return } proc ::namespacex::info::allvars ns { set ns [uplevel 1 [list [namespace parent] normalize $ns]] ::set result [::info vars ${ns}::*] foreach cns [allchildren $ns] { lappend result {*}[::info vars ${cns}::*] } |
︙ | ︙ |
Changes to modules/ncgi/ncgi.man.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | [vset VERSION 1.4.3] [manpage_begin ncgi n [vset VERSION]] [see_also html] [keywords CGI] [keywords cookie] [keywords form] [keywords html] [comment {-*- tcl -*- doctools manpage}] [moddesc {CGI Support}] [titledesc {Procedures to manipulate CGI values.}] [category {CGI programming}] [require Tcl 8.4] [require ncgi [opt [vset VERSION]]] [description] [para] | > | | < > | | | | | < > | > > < < > > < < | < < < < < < | < > | > > | > > > | | | | < | | | > > | < | | | | < < | < < > < < | | > | < | | > | | | < > < | > | | | > | > > > | | | < < < | < | | | | < < < < < | < | | > < | > | | | < > > > | > > < < < > > < > | | < > > | < < < | | < < | < < > < > > < > | | | > | | | | < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | [vset VERSION 1.4.3] [manpage_begin ncgi n [vset VERSION]] [see_also html] [keywords CGI] [keywords cookie] [keywords form] [keywords html] [comment {-*- tcl -*- doctools manpage}] [copyright {2018 Poor Yorick}] [moddesc {CGI Support}] [titledesc {Procedures to manipulate CGI values.}] [category {CGI programming}] [require Tcl 8.4] [require ncgi [opt [vset VERSION]]] [description] [para] [package ncgi] provides routines to manipulate [ uri https://tools.ietf.org/html/rfc3875 CGI ] request arguments provided in the query string and form data, to set and get cookies, and to encode and decode www-url-encoded values. [para] [cmd ::ncgi] is a routine whose subroutines are the same as those documented below for the [const ::ncgi] namespace. When calling these routines as subroutines, omit [arg session] as it is passed automatically. To create a new CGI session, call [cmd {::ncgi .new}], and then use command it creates returns to work with request arguments. Use [cmd {::ncgi get}] to retrieve argument values, or use [cmd {::ncgi all}] for arguments that might occur multiple times. If only one CGI session is needed in the interpreter, [const ::ncgi] may be used directly. [para] [section Definitions] [list_begin definitions] [def multidict] A dictionary where the keys may not be unique [def {argument multidict}] A multidict where keys are names of request arguments provided in the query string and form data, and each value is itself a list containing the value for the argument and the corresponding dictionary of parameters. [list_end] [section Procedures] [list_begin definitions] [call [cmd ::ncgi] [method .new] [arg name] [opt "[arg option] [arg value]"]] Creates a routine named [arg name] that represents a new CGI session, and returns the name the new routine. The available options, primarily for use in the test suite, are [list_begin options] [opt_def body [arg body]] Use [arg body] as the body for the purpose of processing form data. [opt_def contenttype [arg contenttype]] Use [arg contenttype] as the contenttype for the purpose of processing form data. [opt_def form [arg formdata]] Use [arg formdata] as the form data. [opt_def querystring [arg querystring]] Use [arg querystring] as the query string. [list_end] [call [arg session] [method all] [arg name] [opt [arg default]]] Like [cmd ::ncgi::get], but returns a list of values matching [arg name] in the argument multidict. [call [arg session] [method body]] Returns the raw request body. [call [arg session] [method {cookies all}]] Returns a multidict of all cookie names and values. [call [arg session] [method {cookies get}] [opt [arg name]] [opt [arg default]]] Returns the value of the last cookiet named [arg name], or [arg default] it is provided and no such cookie exists. [call [arg session] [method decode] [arg str]] Decodes the www-url-encoded [arg str]. In this encoding special characters are represented with a %xx sequence, where xx is the character code in hex. [call [arg session] [method encode] [arg string]] Encodes [arg string] into www-url-encoded format. [call [arg session] [method exists] [arg name]] Returns [const true] if an CGI argument matching [arg name] is present and is [const false] otherwise. [call [arg session] [method form] [cmd get] [opt [arg name]]] Like [cmd ::ncgi::get], but only considers form arguments, not arguments in the query string. [call [arg session] [method get] [opt [arg name]] [opt [arg default]]] Returns a list containing the values matching [arg name] in the argument multidict. If [arg name] is not provided, returns the entire argument multidict. [call [arg session] [method {header send}] [opt [arg type]]] Serialize the response header to the session output. Produces a [ const Content-Type ] header and additional headers based on [arg args], which is a multidict of names and values. [arg type] defaults to [const text/html]. [call [arg session] [cmd importFile] [arg cmd] [arg name] [ opt [arg filename]]] Provides information about an uploaded file from a form field. Possible values for [arg cmd] are [list_begin definitions] [def "[option -client] [arg name]"] Returns the filename as sent by the client. [def "[option -type] [arg name]"] Returns the mime type of the uploaded file. [def "[option -data] [arg name]"] Returns the contents of the file. [def "[option -server] [arg name]"] Returns the name of a channel routine for the contents of the field named [arg name]. [list_end] [call [arg session] [method {query get}]] Returns the query data as a multidict. [call [arg session] [method {query set}] [arg {name value}]] Sets a query value. [call [arg session] [method {query string}]] Returns the raw query data. [call [arg session] [method redirect] [arg url]] Generates a response that causes a 302 redirect by the Web server. The [arg url] is the new URL that is the target of the redirect. The URL will be qualified with the current server and current directory, if necessary, to convert it into a full URL. [call [arg session] [method type]] Returns the Content-Type of the current CGI values. [call [cmd ::ncgi::urlStub] [opt [arg url]]] Returns the current URL, but without the protocol, server, and port. If [arg url] is specified, then it defines the URL for the current session. That value will be returned by future calls to [cmd ::ncgi::urlStub] [list_end] [section Examples] Uploading a file [example { HTML: <html> <form action="/cgi-bin/upload.cgi" method="POST" enctype="multipart/form-data"> Path: <input type="file" name="filedata"><br> |
︙ | ︙ |
Changes to modules/ncgi/ncgi.tcl.
︙ | ︙ | |||
11 12 13 14 15 16 17 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # Please note that Don Libes' has a "cgi.tcl" that implements version 1.0 # of the cgi package. That implementation provides a bunch of cgi_ procedures # (it doesn't use the ::cgi:: namespace) and has a wealth of procedures for # generating HTML. In contrast, the package provided here is primarly | | < < < | > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | | > | | | | > | | < | | > > > > > | | | > > > > | < | | | < < | < < < | > > > | < | | | > | > | > | < < > | | | | > | > | < < | < | > | | < | | > | > | < | < < | > | > | | > > > | > | | < | < < | | < | < < > > > > > > | > | > | < < | | | | | < | < < < < < | | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < | < < < < < | | < < | | | < < < | | | < < < < | < < | > | | < | < < < < < < < < < < < | | < < < < < < < | < < < < < < < < < < < < | < < | < | | | | | > > > > | > | | | | | | | | < < < < < < < < < < < < < | < > | | < | < < < < | | < < | | | < < < < < < < < < < | < | | < < < < | < < < < < | < > | | > > > | < < < < < < < < < < | | < < < < < < < < | | | < < < < < < < | < < < < < < < | < < | < < < < < < < < < < | < < < < < < < | < < < | > | < < < | | < < < < < < < < < < | < < < | < < < | | > > | < < | < | < | | | | | | < | < > | > | | > > > > > > | < | > | < | | | < > | | | > > > > > > > > > > | > > > > | < < < | > | | < | | > | < < < > | > | > > > > > > > > > > > > > > > | < < | > | > > > | > > | > | < < | < > | < < | < < < | < | > > > > > > | > > | > > > | > > | > | | > > > | > | | > > > | > > | | < | | > | | < < | > > | > > > > > > > > > > > < | < < < | > > | | > | | > | < | | | > | > > > | | | > | < > > > | < | < < | > > | | < > > > > > > > | | | > | > | | > | > | | | > | < | < < < > > > > | | > > > > | | > > | > > > > | > > > | | | | | < > > > | < | < > > > | | > | > > > > > > > > > > > > > | < > > > > > > > > | < | | < < | | | < > > > | > | | | < < < > < | < < | | < | > > > > > > > < > | > | < < > > < | | > > > | > | | > > | | | > | > > | < | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # Please note that Don Libes' has a "cgi.tcl" that implements version 1.0 # of the cgi package. That implementation provides a bunch of cgi_ procedures # (it doesn't use the ::cgi:: namespace) and has a wealth of procedures for # generating HTML. In contrast, the package provided here is primarly # concerned with processing input to CGI programs. # Note, I use the term "query data" to refer to the data that is passed in # to a CGI program. Typically this comes from a Form in an HTML browser. # The query data is composed of names and values, and the names can be # repeated. The names and values are encoded, and this module takes care # of decoding them. # We use newer string routines package require Tcl 8.6 package require {chan base} package require fileutil ; # Required by importFile. package require mime package require uri package provide ncgi 1.5.0 namespace eval ::ncgi { # Support for x-www-urlencoded character mapping # The spec says: "non-alphanumeric characters are replaced by '%HH'" variable i variable c variable map for {set i 1} {$i <= 256} {incr i} { set c [format %c $i] if {![string match \[a-zA-Z0-9\] $c]} { set map($c) %[format %.2X $i] } } # These are handled specially array set map { { } + \n %0D%0A } } proc ::ncgi::.namespace _ { namespace ensemble configure $_ -namespace } # ::ncgi::all # # Returns all the values of a named query element as a list, or # the empty list if $name was not not specified. Always returns # lists. Consider using ncgi::get instead. # # Arguments: # key The name of the query element # # Results: # The first value of the named element, or "" proc ::ncgi::all {_ name} { namespace upvar $_ query query form form $_ query get if {[form $_ exists]} { $_ form get } set result {} foreach {qname val} $query { if {$qname eq $name} { lappend result $val } } if {[form $_ exists]} { foreach {fname val} $form { if {$fname eq $name} { lappend result [lindex $val 0] } } } return $result } proc ::ncgi::body _ { global env namespace upvar $_ {*}{ body body content_length content_length method method } if {![info exists body]} { if {([info exists method] && $method eq {post}) && [info exist content_length] } { chan configure stdin -translation binary set body [read stdin $env(CONTENT_LENGTH)] } else { set body {} } } chan configure stdout -translation binary return $body } # ::ncgi::cookies # # Returns a multidict of incoming cookies. namespace eval ::ncgi::cookies { namespace ensemble create -parameters _ namespace export all get proc all {_ name} { namespace upvar $_ cookies cookies init $_ lmap {name1 val} $cookies { if {$name1 ne $name} continue lindex $val } } proc init _ { global env namespace upvar $_ cookies cookies if {![info exists cookies]} { if {[info exists env(HTTP_COOKIE)]} { set cookies [join [lmap pair [split $env(HTTP_COOKIE) \;] { split [string trim $pair] = }]] } else { set cookies {} } } return } proc get {_ args} { init $_ namespace upvar $_ cookies cookies switch [llength $args] { 2 { lassign $args key default if {[dict exists $cookies $key]} { return [dict get $cookies $key] } else { return $default } } 1 { return [dict get $cookies [lindex $args 0]] } 0 { return $cookies } default { error [list {wrong # args}] } } } } proc ::ncgi::delete _ { namespace delete [namespace ensemble configure $_ -namespace] } # ::ncgi::decode # # Decodes data in www-url-encoded format. # # Arguments: # An encoded value. # # Results: # The decoded value. proc ::ncgi::DecodeHex {hex} { return [binary decode hex $hex] } proc ::ncgi::decode str { # rewrite "+" back to space # protect \ from quoting another '\' set str [string map [list + { } "\\" "\\\\" \[ \\\[ \] \\\]] $str] # prepare to process all %-escapes regsub -all -- {%([Ee][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])} \ $str {[encoding convertfrom utf-8 [DecodeHex \1\2\3]]} str regsub -all -- {%([CDcd][A-Fa-f0-9])%([89ABab][A-Fa-f0-9])} \ $str {[encoding convertfrom utf-8 [DecodeHex \1\2]]} str regsub -all -- {%([0-7][A-Fa-f0-9])} $str {\\u00\1} str # process \u unicode mapped chars return [subst -novar $str] } # ::ncgi::encode # # Encodes data in www-url-encoded format. # # Arguments: # A string # # Results: # The encoded value proc ::ncgi::encode string { variable map # 1 leave alphanumerics characters alone # 2 Convert every other character to an array lookup # 3 Escape constructs that are "special" to the tcl parser # 4 "subst" the result, doing all the array substitutions regsub -all -- \[^a-zA-Z0-9\] $string {$map(&)} string # This quotes cases like $map([) or $map($) => $map(\[) ... regsub -all -- {[][{})\\]\)} $string {\\&} string return [subst -nocommand $string] } proc ::ncgi::form_get {_ args} { namespace upvar $_ form form set type [$_ type] if {![info exists form]} { set form {} switch -glob $type { {} - text/xml* - application/x-www-form-urlencoded* - application/x-www-urlencoded* { foreach {key val} [urlquery [$_ body]] { lappend form $key [list $val {}] } } multipart/* { $_ multipart } default { return -code error "Unknown Content-Type: $type" } } } if {[llength $args] == 1} { lindex [dict get $form {*}$args] 0 } elseif {[llength $args] == 2} { set args [lassign $args[set args {}] key] lassign [dict get $form $key] value params dict get $params {*}$args } elseif {[llength $args]} { error [list wrong # args] } else { return $form } } proc ::ncgi::form_exists _ { namespace upvar $_ content_length content_length if {[info exists content_length]} { switch -glob [type $_] { {} - text/xml* - application/x-www-form-urlencoded* - application/x-www-urlencoded* - multipart/* { return 1 } } } return 0 } proc ::ncgi::headerfilter headers { join [lmap {key val} $headers[set headers {}] { if {[string tolower $key] in {content-type}} continue list $key $val }] } proc ::ncgi::header_send {_ type args} { namespace upvar $_ respons response set mimeout [mime::.new {} -canonical $type -params $args \ -addcontentid 0 -addmimeversion 0 -addmessageid 0 -string {} ] foreach {n v} [headerfilter [$_ response header get]] { $mimeout header set $n {*}$v } $_ response .destroy $mimeout serialize -chan ${_}::stdout ${_}::stdout flush $mimeout .destroy } # ::ncgi::get # # Returns the value of a named query element, or the empty string if # it was not not specified. This only returns the first value of # associated with the name. If you want them all (like all values # of a checkbox), use ncgi::all # # Arguments: # key The name of the query element # default The value to return if the value is not present # # Results: # The first value of the named element, or the default proc ::ncgi::get {_ args} { namespace upvar $_ form form query query $_ query get if {[form $_ exists]} { $_ form get } set merged [merge $_] if {![llength $args]} { return $merged } elseif {[llength $args] <= 2} { lassign $args key default try { return [lindex [dict get $merged $key] 0] } on error {} { return $default } } else { error [list {wrong # args}] } } # ::ncgi::importFile -- # # get information about a file upload field # # Arguments: # cmd one of '-server' '-client' '-type' '-data' # var cgi variable name for the file field # Results: # -server returns the name of the file on the server: side effect # is that the file gets stored on the server and the # script is responsible for deleting/moving the file # -client returns the name of the file sent from the client # -type returns the mime type of the file # -data returns a channel command for the contents of the file proc ::ncgi::importFile {_ cmd var {filename {}}} { namespace upvar $_ mimeparts mimeparts if {[$_ form exists]} { set form [$_ form get] } lassign [dict get $mimeparts $var] mime lassign [$mime header get content-disposition] cdisp dispparams switch -exact -- $cmd { -server { return [$mime body decode] } -client { if {[dict exists $dispparams filename]} { return [dict get $dispparams filename] } return {} } -type { lassign [$mime header get content-type] ctype params return $ctype } -data { return [$mime body decoded] } default { error "Unknown subcommand to ncgi::import_file: $cmd" } } } proc ::ncgi::merge _ { namespace upvar $_ form form query query $_ query get set query2 [join [lmap {key val} $query { list $key [list $val {}] }]] if {[$_ form exists]} { # form overrides query in a multidict list {*}$query2 {*}[join [lmap {key val} $form { list $key $val }]] } else { return $query2 } } # ::ncgi::multipart # # Parses $data into a multidict using the boundary provided in $type, # which is a complete Content-type value. Each value in the resulting # multi dict is a list where the first item is the value and the the # second item is a multidict where each key is the name of a header and # each value is a list containing the header value and a dictionary of # parameters for that header. proc ::ncgi::multipart _ { namespace upvar $_ form form mime mime mimeparts mimeparts set type [$_ type] set data [$_ body] set mime [mime::.new {} -string "Content-Type: $type\n\n$data"] set parts [$mime property parts] trace add variable mime unset [list apply [list {mime args} { if {[namespace which $mime] ne {}} { $mime .destroy } } $mime]] set results [list] foreach part $parts { set value [[$part body decoded] read] lassign [$part header get content-disposition] hvalue params if {$hvalue eq {form-data} && [dict exists $params name]} { set name [dict get $params name] dict unset params name } else { set name {} } lappend mimeparts $name $part lappend form $name [list $value $params] } return $form } # ::ncgi::.new # Creates a command representing a new cgi session and return the name of # that command. # arguments # name # The name of the command to create, or the empty string if a command # name should be automatically generated. # effects # Resets the cached query data and wipes any environment variables # associated with CGI inputs (like QUERY_STRING). proc ::ncgi::.new {_ name args} { if {$name eq {}} { set name [namespace current]::[info cmdcount] } elseif {![string match ::* $name]} { set name [uplevel 1 {namespace current}]::$name } set ns [namespace eval $name { namespace ensemble create namespace current }] # normalize $name set name [namespace which $name] ::tcllib::chan::base .new ${ns}::stdout stdout -close 0 namespace ensemble create -command ${ns}::header -map [list \ send [list header_send $name] ] mime::.new ${ns}::response -canonical text/html -spec cgi -string {} set map [dict merge [list decode decode encode encode {*}[join [lmap cmdname { .namespace .new all body cookies delete form get importFile input merge method multipart query redirect type urlStub } { list $cmdname [list $cmdname $name] }]]] [list \ header [list ${ns}::header] \ response [list ${ns}::response] \ stdout [list ${ns}::stdout] ]] namespace ensemble configure $name -map $map trace add command $name delete [list apply [list {old new op} { $old .destroy }]] # $query holds the raw query (i.e., form) data # This is treated as a cache, too, so you can call ncgi::query more than # once # $contenttype is the content-type, which affects how the query is parsed # $urlStub holds the URL corresponding to the current request # This does not include the server name. namespace upvar $ns \ _tmpfiles _tmpfiles \ body body \ content_length content_length \ contenttype contenttype \ env env \ form form \ listRestrict listRestrict \ method method \ query query \ querystring querystring \ urlStub urlStub \ # Map of transient files array set _tmpfiles {} # $listRestrict flags compatibility with Don Libes cgi.tcl when dealing # with form values that appear more than once. This bit gets flipped when # you use the ncgi::input procedure to parse inputs. set listRestrict 0 dict for {opt val} $args { switch $opt { body { set $opt $val set content_length [string length $body] } contenttype - env - form - querystring { set $opt $val } default { error [list {unknown reset option} $opt] } } } if {![info exists env]} { array set env [array get ::env] } if {[info exists env(CONTENT_LENGTH)] && [ string length $env(CONTENT_LENGTH)] != 0} { set content_length [expr {$env(CONTENT_LENGTH)}] } if {[info exists env(REQUEST_METHOD)]} { set method [string tolower $env(REQUEST_METHOD)] } return $name } # ::ncgi::parseMimeValue # # Parse a MIME header value, which has the form # value; param=value; param2="value2"; param3='value3' # # Arguments: # value The mime header value. This does not include the mime # header field name, but everything after it. # # Results: # A two-element list, the first is the primary value, # the second is in turn a name-value list corresponding to the # parameters. Given the above example, the return value is # { # value # {param value param2 value2 param3 value3} # } proc ::ncgi::parseMimeValue value { set parts [split $value \;] set results [list [string trim [lindex $parts 0]]] set paramList [list] foreach sub [lrange $parts 1 end] { if {[regexp -- {([^=]+)=(.+)} $sub match key val]} { set key [string trim [string tolower $key]] set val [string trim $val] # Allow single as well as double quotes if {[regexp -- {^(['"])(.*)\1} $val x quote val2]} { ; # need a " for balance # Trim quotes and any extra crap after close quote # remove quoted quotation marks set val [string map {\\" "\"" \\' "\'"} $val2] } lappend paramList $key $val } } if {[llength $paramList]} { lappend results $paramList } return $results } # ::ncgi::query_get # # Returns the query part of the URI # proc ::ncgi::query_get _ { namespace upvar $_ query query if {![info exists query]} { set query [urlquery [$_ query string]] } return $query } # ::ncgi::query_set # # set the value of $key in the query dictionary to $value # proc ::ncgi::query_set {_ key value} { namespace upvar $_ query query $_ query get set idx [lindex [lmap idx [lsearch -exact -all $key $query] { if {[$idx % 2]} continue set idx }] end] if {$idx >= 0} { set query [lreplace $query[set query {}] $idx $idx $value] } else { lappend query $key $value } return $value } # ::ncgi::query_string # # Reads the query data from the QUERY_STRING environment variable if # needed. # # Arguments: # none # # Results: # The raw query data. proc ::ncgi::query_string _ { namespace upvar $_ env env querystring querystring if {[info exists querystring]} { # This ensures you can call ncgi::query more than once, # and that you can use it with ncgi::reset return $querystring } set querystring {} if {[info exists env(QUERY_STRING)]} { set querystring $env(QUERY_STRING) } return $querystring } # ::ncgi::redirect # # Generate a redirect by returning a header that has a Location: field. # If the URL is not absolute, this automatically qualifies it to # the current server # # Arguments: # url The url to which to redirect # # Side Effects: # Outputs a redirect header proc ::ncgi::redirect {_ url} { namespace upvar $_ env env if {![regexp -- {^[^:]+://} $url]} { # The url is relative (no protocol/server spec in it), so # here we create a canonical URL. # request_uri The current URL used when dealing with relative URLs. # proto http or https |
︙ | ︙ | |||
839 840 841 842 843 844 845 | if {[string match /* $url]} { set url $proto://$server$port$url } else { regexp -- {^(.*/)[^/]*$} $request_uri match dirname set url $proto://$server$port$dirname$url } } | | > > | < | < | < < < < < < < < < < < < | < < < < < < < < | | | < < < < < < < < | < < < < < < < < | < < < > | < < < | < < < < < < < < < < < < | < < < < | | > > < | < < < < < < < < < < < < < < < < < < | | < | < < < < < < < | < < < < < < < | < < < | < < < | < < < | | < | | < > | < < < < < < < < < < < | < < < < < < < < < | < | < < < < | < < < < < < | | | < < < < < < < | < < > | | | | | < < | < | < < | < | < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < | < < < < < > | < | | < < < | < < < < < < < < < < < < < | < | < < | > | > | | > | < | < < | | < < < < < < < < < < | | < < < < < < < | < < > | | < > > < < | | < < | | | > | > | < > > > | | < > > > > < < < < | | > > > | | | | > > | > > > | < > | < | | 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 | if {[string match /* $url]} { set url $proto://$server$port$url } else { regexp -- {^(.*/)[^/]*$} $request_uri match dirname set url $proto://$server$port$dirname$url } } set mimeout [mime::.new {} -canonical text/html -addcontentid 0 \ -addmimeversion 0 -addmessageid 0 \ -string "Please go to <a href=\"$url\">$url</a>\n" ] foreach {n v} [headerfilter [$_ response header get]] { $mimeout header set $n {*}$v } $_ response .destroy $mimeout header set Location $url $mimeout serialize -chan ${_}::stdout ${_}::stdout flush $mimeout .destroy return } # ::ncgi::type # # This returns the content type of the query data. # # Arguments: # none # # Results: # The content type of the query data. proc ::ncgi::type _ { namespace upvar $_ contenttype contenttype env env if {![info exists contenttype]} { if {[info exists env(CONTENT_TYPE)]} { set contenttype $env(CONTENT_TYPE) } else { return {} } } return $contenttype } # ::ncgi::urlquery # # Parses $data as a url-encoded query and returns a multidict containing # the query. # proc ::ncgi::urlquery data { set result {} # Any whitespace at the beginning or end of urlquery data is not # considered to be part of that data, so we trim it off. One special # case in which post data is preceded by a \n occurs when posting # with HTTPS in Netscape. foreach x [split [string trim $data] &] { # Turns out you might not get an = sign, # especially with <isindex> forms. set pos [string first = $x] set len [string length $x] if {$pos>=0} { if {$pos == 0} { # if the = is at the beginning ... if {$len>1} { # ... and there is something to the right ... set varname [string range $x 1 end] set val {} } else { # ... otherwise, all we have is an = set varname {} set val {} } } elseif {$pos==[expr {$len-1}]} { # if the = is at the end ... set varname [string range $x 0 [expr {$pos-1}]] set val "" } else { set varname [string range $x 0 [expr {$pos-1}]] set val [string range $x [expr {$pos+1}] end] } } else { # no = was found ... set varname $x set val {} } lappend result [decode $varname] [decode $val] } return $result } # ::ncgi::urlStub # # Set or return the URL associated with the current page. # This is for use by TclHttpd to override the default value # that otherwise comes from the CGI environment # # Arguments: # url (option) The url of the page, not counting the server name. # If not specified, the current urlStub is returned # # Side Effects: # May affects future calls to ncgi::urlStub # proc ::ncgi::urlStub {_ {url {}}} { global env namespace upvar $_ urlStub urlStub if {[string length $url]} { set urlStub $url return {} } elseif {[info exists urlStub]} { return $urlStub } else { if {[info exists env(SCRIPT_NAME)]} { set urlStub $env(SCRIPT_NAME) } else { set urlStub {} } return $urlStub } } namespace eval ::ncgi { namespace ensemble create -command [namespace current]::form \ -parameters _ -map { exists form_exists get form_get } namespace ensemble create -command [namespace current]::query \ -parameters _ -map { get query_get set query_set string query_string } .new dummy [namespace current] } |
Changes to modules/ncgi/ncgi.test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # -*- tcl -*- # Tests for the cgi module. # # This file contains a collection of tests for one or more of the Tcl # built-in commands. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # # Copyright (c) 1998-2000 by Ajuba Solutions # # RCS: @(#) $Id: ncgi.test,v 1.28 2012/05/03 17:56:07 andreas_kupries Exp $ # ------------------------------------------------------------------------- source [file join \ [file dirname [file dirname [file join [pwd] [info script]]]] \ devtools testutilities.tcl] testsNeedTcl 8.4 testsNeedTcltest 2 testing { useLocal ncgi.tcl ncgi } | > | > > | | | | | | > > > > > > > > > > | > | > | < > > > > | > > | > > | | > > > > > | | > | | | > > > | < | | | | | < > > > | > | | > > > > > > > > > > > > > > > > > > | | | | > > > | | | | < | | > > | | | < | | > | | | | | | | > | | | | | | | | | > | > | < | | > > | > | < | > | > | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | | | > | > | | | > > | | > | | > > | | | | | < | < > | | | | | < < < | | | | < | | | | | | < | | | > | | | | | > | | | | | < < | | | < < < | | | > > | | | | | > | | | | < < | > | | < | | | > | | > | | > > | > > > | > | | < < < | | | > | | | | < < | < < > | > | | | | | | | | | | | | | > | > | > | | < | | | | | | | > | | | | | | | | | | | | | | > | | > | | | | | | | | | | | > | | | | | | | | | | | | | | > | > | | | | | | | | | | | > | | | | | | | | | | | | | | > | > | | | | | | | | | | | > | | | | | | | | | | | | | | > | > | | | | | | | | | | | > | | | | | | | | | | | | | | | > | > | | | | | | | | | | | > | | | | | | | | | | | | | | | > | > | | > | | | | | | | | | | | | | | | | > > | | | > | | | | | | | | | > | | | | | | | > | > | | > | | | | | | | | | > | | | | | | | > | > > | | > | | | | | | | | | > | | | | | | | > | | | | | | | | | | | | | < < < < < < | < < < | < < < | < < < | < < < < < < < < < < < < < < < | | < < < < | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | > > > | > | > > > > > > > | | > > > > > > > > | | > > > > | > | | > > > > > > > | | > > > > > > > > > > | | > > > > > | > > > > > | | > > > > > > > > > > > > > > | > | | > | > | > > > > > | | > > > > > | | > > > > > > | | > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 | # -*- tcl -*- # Tests for the cgi module. # # This file contains a collection of tests for one or more of the Tcl # built-in commands. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # # Copyright (c) 1998-2000 by Ajuba Solutions # Copyright (c) 2018 Poor Yorick # # RCS: @(#) $Id: ncgi.test,v 1.28 2012/05/03 17:56:07 andreas_kupries Exp $ # ------------------------------------------------------------------------- source [file join \ [file dirname [file dirname [file join [pwd] [info script]]]] \ devtools testutilities.tcl] testsNeedTcl 8.4 testsNeedTcltest 2 testing { useLocal ncgi.tcl ncgi } proc main {} {try { global env global auto_path set sub_ap $auto_path lappend sub_ap $::tcltest::testsDirectory set ncgiFile [localPath ncgi.tcl] set futlFile [tcllibPath fileutil/fileutil.tcl] set cmdlFile [tcllibPath cmdline/cmdline.tcl] proc makescript script { string map [list @script@ [list $script]] { after 0 [list coroutine main try @script@ \ on error {tres topts} { exit 1 } finally { set done 1 }] vwait done exit } } proc resetenv {} { global env foreach varname { CONTENT_LENGTH CONTENT_TYPE HTTP_COOKIE HTTPS QUERY_STRING REQUEST_METHOD REQUEST_URI SERVER_NAME SERVER_PORT } { if {[info exists env($varname)]} { unset env($varname) } } } proc withncgi args { set script [lindex $args end] set args [lreplace $args end end] ncgi .new ncgi1 {*}$args catch [list uplevel 1 $script] cres copts ncgi1 delete resetenv return -options $copts $cres } test ncgi-1.1 {[ncgi .new]} { withncgi { list [info exist [ncgi1 .namespace]::query] [ info exist [ncgi1 .namespace]::contenttype] } } {0 0} test ncgi-1.2 {[ncgi reset]} { withncgi querystring query=reset { list [set [ncgi1 .namespace]::querystring] [info exists [ ncgi1 .namespace]::contenttype] } } {query=reset 0} test ncgi-1.3 {[ncgi reset]} { withncgi querystring query=reset contenttype text/plain { list [set [ncgi1 .namespace]::querystring] [ set [ncgi1 .namespace]::contenttype] } } {query=reset text/plain} test ncgi-2.1 {[ncgi query] fake query data} { withncgi querystring fake=query { ncgi1 query get list [set [ncgi1 .namespace]::querystring] [ set [ncgi1 .namespace]::query] } } {fake=query {fake query}} test ncgi-2.2 {[ncgi query] GET} { set env(REQUEST_METHOD) GET set env(QUERY_STRING) name=value withncgi { ncgi1 query get list [set [ncgi1 .namespace]::querystring] [ set [ncgi1 .namespace]::query] } } {name=value {name value}} test ncgi-2.3 {[ncgi query] HEAD} { set env(REQUEST_METHOD) HEAD withncgi { ncgi1 query get set [ncgi1 .namespace]::query } } {} test ncgi-2.4 {[ncgi query] POST} { set env(REQUEST_METHOD) POST set env(CONTENT_LENGTH) 10 withncgi { makeFile [format { set auto_path {%s} source {%s} source {%s} source {%s} ncgi .new ncgi1 ncgi1 body puts [set [ncgi1 .namespace]::body] ncgi1 delete } $sub_ap $cmdlFile $futlFile $ncgiFile] test1 ; # {} set f [open |[list $::tcltest::tcltest test1] r+] puts $f name=value flush $f gets $f line close $f removeFile test1 set line } } name=value test ncgi-2.5 {ncgi::test} { set env(CONTENT_TYPE) text/html withncgi { ncgi1 type } } text/html test ncgi-2.6 {ncgi::test} { set env(CONTENT_TYPE) text/html withncgi querystring foo=bar contenttype text/plain { ncgi1 type } } text/plain test ncgi-3.1 {ncgi::decode} { ncgi decode abcdef0123 } abcdef0123 test ncgi-3.2 {ncgi::decode} { ncgi decode {[abc]def$0123\x} } {[abc]def$0123\x} test ncgi-3.3 {ncgi::decode} { ncgi decode {[a%25c]def$01%7E3\x%3D} } {[a%c]def$01~3\x=} test ncgi-3.4 {ncgi::decode} { ncgi decode {hello+world} } {hello world} test ncgi-3.5 {ncgi::decode} { ncgi decode {aik%C5%ABloa} } "aik\u016Bloa" ; # u+macron test ncgi-3.6 {ncgi::decode} { ncgi decode {paran%C3%A1} } "paran\u00E1" ; # a+acute test ncgi-3.7 {ncgi::decode, bug 3601995} { ncgi decode {%C4%85} } "\u0105" ; # a+ogonek test ncgi-3.8 {ncgi::decode, bug 3601995} { ncgi decode {%E2%80%A0} } "\u2020" ; # dagger test ncgi-3.9 {ncgi::decode, bug 3601995} { ncgi decode {%E2%A0%90} } "\u2810" ; # a braille pattern test ncgi-3.10 {ncgi::decode, bug 3601995} { ncgi decode {%E2%B1} } "%E2%B1" ; # missing byte trailing %A0, do not accept/decode, pass through. test ncgi-4.1 {ncgi::encode} { ncgi encode abcdef0123 } abcdef0123 test ncgi-4.2 {ncgi::encode} { ncgi encode "\[abc\]def\$0123\\x" } {%5Babc%5Ddef%240123%5Cx} test ncgi-4.3 {ncgi::encode} { ncgi encode {hello world} } {hello+world} test ncgi-4.4 {ncgi::encode} { ncgi encode "hello\nworld\r\tbar" } {hello%0D%0Aworld%0D%09bar} test ncgi-5.1 {ncgi::query get} { withncgi querystring name=hello+world&name2=%7ewelch { ncgi1 query get } } {name {hello world} name2 ~welch} test ncgi-5.2 {ncgi::merge} { withncgi querystring name=&name2 contenttype application/x-www-urlencoded { ncgi1 merge } } {name {{} {}} name2 {{} {}}} test ncgi-5.3 {ncgi::merge} { withncgi querystring name=&name2 \ contenttype application/x-www-form-urlencoded { ncgi1 merge } } {name {{} {}} name2 {{} {}}} test ncgi-5.4.1 {ncgi::merge} { withncgi querystring name=&name2 contenttype application/xyzzy { set code [catch {ncgi1 merge} err] list $code $err } } {0 {name {{} {}} name2 {{} {}}}} test ncgi-5.4.2 {ncgi::merge} { withncgi body name=&name2 contenttype application/xyzzy { set code [catch {ncgi1 merge} err] list $code $err } } {0 {}} test ncgi-5.4.3 {ncgi::merge} { withncgi body name=&name2 contenttype application/xyzzy { set code [catch {ncgi1 form get} err] list $code $err } } {1 {Unknown Content-Type: application/xyzzy}} # multipart tests at the end because I'm too lazy to renumber the tests test ncgi-6.1 {query get, anonymous values, redundant keys} { withncgi querystring name=&name2 { ncgi1 query get } } {name {} name2 {}} test ncgi-7.1 {ncgi::get} { withncgi querystring name=value&name2=value2 { ncgi1 get } } {name {value {}} name2 {value2 {}}} test ncgi-7.2 {ncgi::get} { withncgi querystring nameList=value1+stuff&nameList=value2+more { ncgi1 all nameList } } {{value1 stuff} {value2 more}} test ncgi-7.3 {ncgi::get} { withncgi querystring name=value&name=value2 { catch {ncgi1 get} err set err } } {name {value {}} name {value2 {}}} test ncgi-8.1.1 {ncgi::value} { withncgi querystring nameList=val+ue&nameList=value2 { ncgi1 get nameList } } value2 test ncgi-8.1.2 {ncgi::value} { withncgi querystring nameList=val+ue&nameList=value2 { ncgi1 all nameList } } {{val ue} value2} test ncgi-8.2.1 {ncgi::value} { withncgi querystring name=val+ue&name=value2 { ncgi1 get name } } value2 test ncgi-8.2.2 {ncgi::value} { withncgi querystring name=val+ue&name=value2 { ncgi1 all name } } {{val ue} value2} test ncgi-8.3 {ncgi::get default} { withncgi querystring name=val+ue&name=value2 { ncgi get noname } } {} test ncgi-9.1 {ncgi::valueList} { withncgi querystring name=val+ue&name=value2 { ncgi1 all name } } {{val ue} value2} test ncgi-9.2 {ncgi::valueList} { withncgi querystring name=val+ue&name=value2 { ncgi1 all noname } } {} set URL http://www.tcltk.com/index.html test ncgi-11.1 {ncgi::redirect} { set env(REQUEST_URI) http://www.scriptics.com/cgi-bin/test.cgi set env(REQUEST_METHOD) GET set env(QUERY_STRING) {} set env(SERVER_NAME) www.scriptics.com set env(SERVER_PORT) 80 makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 redirect %s ncgi1 delete } err eopts]} { puts stderr [dict get $eopts -errorinfo] puts $err } } $sub_ap $cmdlFile $futlFile $ncgiFile $URL]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nLocation: $URL\n\nPlease go to <a href=\"$URL\">$URL</a>\n" set URL /elsewhere/foo.html set URL2 http://www/elsewhere/foo.html test ncgi-11.2 {ncgi::redirect} { set env(REQUEST_URI) http://www/cgi-bin/test.cgi set env(REQUEST_METHOD) GET set env(QUERY_STRING) {} set env(SERVER_NAME) www.scriptics.com set env(SERVER_PORT) 80 makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s set ncgi [ncgi .new {}] $ncgi response cookie set CookieName 12345 $ncgi redirect %s $ncgi delete } err copts]} { puts [dict get $copts -errorinfo] } exit } $sub_ap $cmdlFile $futlFile $ncgiFile $URL]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nSet-Cookie: CookieName=12345 ;\nLocation: $URL2\n\nPlease go to <a href=\"$URL2\">$URL2</a>\n" set URL foo.html set URL2 http://www.scriptics.com/cgi-bin/foo.html test ncgi-11.3 {ncgi::redirect} { set env(REQUEST_URI) http://www.scriptics.com/cgi-bin/test.cgi set env(REQUEST_METHOD) GET set env(QUERY_STRING) {} set env(SERVER_NAME) www.scriptics.com set env(SERVER_PORT) 80 makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 redirect %s ncgi1 delete } err]} { puts $err } exit } $sub_ap $cmdlFile $futlFile $ncgiFile $URL]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nLocation: $URL2\n\nPlease go to <a href=\"$URL2\">$URL2</a>\n" set URL foo.html set URL2 http://www.scriptics.com/cgi-bin/foo.html test ncgi-11.4 {ncgi::redirect} { set env(REQUEST_URI) /cgi-bin/test.cgi set env(REQUEST_METHOD) GET set env(QUERY_STRING) {} set env(SERVER_NAME) www.scriptics.com set env(SERVER_PORT) 80 makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 redirect %s ncgi delete } err]} { puts $err } exit } $sub_ap $cmdlFile $futlFile $ncgiFile $URL]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nLocation: $URL2\n\nPlease go to <a href=\"$URL2\">$URL2</a>\n" set URL foo.html set URL2 http://www.scriptics.com:8000/cgi-bin/foo.html test ncgi-11.5 {ncgi::redirect} { set env(REQUEST_URI) /cgi-bin/test.cgi set env(REQUEST_METHOD) GET set env(QUERY_STRING) {} set env(SERVER_NAME) www.scriptics.com set env(SERVER_PORT) 8000 makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 redirect %s ncgi1 delete } err]} { puts $err } exit } $sub_ap $cmdlFile $futlFile $ncgiFile $URL]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nLocation: $URL2\n\nPlease go to <a href=\"$URL2\">$URL2</a>\n" set URL foo.html set URL2 https://www.scriptics.com/cgi-bin/foo.html test ncgi-11.6 {ncgi::redirect} { set env(REQUEST_URI) /cgi-bin/test.cgi set env(REQUEST_METHOD) GET set env(QUERY_STRING) {} set env(SERVER_NAME) www.scriptics.com set env(SERVER_PORT) 443 set env(HTTPS) on makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 redirect %s ncgi1 delete } err]} { puts $err } exit } $sub_ap $cmdlFile $futlFile $ncgiFile $URL]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nLocation: $URL2\n\nPlease go to <a href=\"$URL2\">$URL2</a>\n" set URL login.tcl set URL2 https://foo.com/cgi-bin/login.tcl test ncgi-11.7 {ncgi::redirect} { set env(REQUEST_URI) https://foo.com/cgi-bin/view.tcl?path=/a/b/c set env(REQUEST_METHOD) GET set env(QUERY_STRING) {} set env(SERVER_NAME) foo.com set env(SERVER_PORT) 443 set env(HTTPS) on makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 redirect %s ncgi1 delete } cres copts]} { puts stderr [dict get $copts -errorinfo] exit 1 } exit } $sub_ap $cmdlFile $futlFile $ncgiFile $URL]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nLocation: $URL2\n\nPlease go to <a href=\"$URL2\">$URL2</a>\n" test ncgi-12.1 {ncgi::header} { makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 header send text/html ncgi1 delete } err copts]} { puts stderr [dict get $copts -errorinfo] exit 1 } exit } $sub_ap $cmdlFile $futlFile $ncgiFile]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\n\n" test ncgi-12.2 {ncgi::header} { makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 header send text/plain ncgi1 delete } err copts]} { puts stderr [dict get $copts -errorinfo] exit 1 } exit } $sub_ap $cmdlFile $futlFile $ncgiFile]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/plain\n\n" test ncgi-12.3 {ncgi::header} { makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 response header set X-Comment {This is a test} ncgi1 header send text/html ncgi1 delete } cres copts]} { puts stderr [dict get $copts -errorinfo] exit 1 } exit } $sub_ap $cmdlFile $futlFile $ncgiFile]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nX-Comment: This is a test\n\n" test ncgi-12.4 {ncgi::header} { makeFile [makescript [format { set auto_path {%s} if {[catch { source %s source %s source %s ncgi .new ncgi1 ncgi1 response cookie set Name The+Value ncgi1 header send text/html } err]} { puts $err } exit } $sub_ap $cmdlFile $futlFile $ncgiFile]] test1 set f [open |[list $::tcltest::tcltest test1] r+] set res [read $f] close $f removeFile test1 set res } "Content-Type: text/html\nSet-Cookie: Name=The+Value ;\n\n" test ncgi-14.2 {ncgi::multipart} { withncgi contenttype multipart/form-data body {} { catch {ncgi1 get} err } set err } {end-of-string encountered while parsing multipart/form-data} test ncgi-14.3 {ncgi::multipart} { set in [open [file join [file dirname [info script]] formdata.txt]] set X [read $in] close $in foreach line [split $X \n] { if {[string length $line] == 0} { break } if {[regexp {^Content-Type: (.*)$} $line x type]} { break } } regsub ".*?\n\n" $X {} X withncgi contenttype $type body $X { ncgi1 get } } [list \ field1 [list value {}] \ field2 [list {another value} {}] \ the_file_name [list { <center><h1> Netscape Address Book Sync for Palm Pilot User Guide </h1></center> } \ [list \ filename {C:Program FilesNetscapeCommunicatorProgramnareadme.htm} ]]] test ncgi-14.4 {ncgi::multipart} { set in [open [file join [file dirname [info script]] formdata.txt]] set X [read $in] close $in foreach line [split $X \n] { if {[string length $line] == 0} { break } if {[regexp {^Content-Type: (.*)$} $line x type]} { break } } regsub ".*?\n\n" $X {} X withncgi body $X contenttype $type { list [ncgi1 get field1] [ncgi1 get field2] [ ncgi1 get the_file_name] } } {value {another value} { <center><h1> Netscape Address Book Sync for Palm Pilot User Guide </h1></center> }} ## ------------ tests for binary content and file upload ---------------- ## some utility procedures to generate content variable binary_content " <center><h1> Netscape Address Book Sync for Palm Pilot User Guide </h1></center> " variable form_boundary {17661509020136} proc genformcontent_type {} { variable form_boundary return "multipart/form-data; boundary=\"$form_boundary\"" } proc genformdatapart {name cd value} { variable form_boundary return "--$form_boundary\nContent-Disposition: form-data; name=\"$name\"$cd\n\n$value\n" } proc genformdata {bcontent} { variable form_boundary set a [genformdatapart field1 "" {value}] set b [genformdatapart field2 "" {another value}] set c [genformdatapart the_file_name "; filename=\"C:\\Program Files\\Netscape\\Communicator\\Program\\nareadme.htm\"\nContent-Type: text/html" $bcontent] return "$a$b$c--$form_boundary--\n" } test ncgi-14.5 {ncgi::multipart--check binary file} { set X [genformdata $binary_content] withncgi body $X contenttype [genformcontent_type] { set content [ncgi1 get the_file_name] list [ncgi1 get field1] [ncgi1 get field2] $content } } [list value {another value} [string map [list \r {}] $binary_content]] test ncgi-14.6 {ncgi::multipart [query set]} { set in [open [file join [file dirname [info script]] formdata.txt]] set X [read $in] close $in foreach line [split $X \n] { if {[string length $line] == 0} { break } if {[regexp {^Content-Type: (.*)$} $line x type]} { break } } regsub ".*?\n\n" $X {} X withncgi body $X contenttype $type { ncgi1 query set userval1 foo ncgi1 query set userval2 {a b} list [ncgi1 get field1] [ncgi1 get field2] [ ncgi1 get userval1] [ncgi1 get userval2] [ ncgi1 get the_file_name] } } {value {another value} foo {a b} { <center><h1> Netscape Address Book Sync for Palm Pilot User Guide </h1></center> }} test ncgi-15.1.1 {ncgi query set} { withncgi querystring nameList=val+ue&nameList=value2 { ncgi1 query set foo 1 ncgi1 query set bar {a b} list [ncgi1 get nameList] [ncgi1 get foo] [ncgi1 get bar] } } {value2 1 {a b}} test ncgi-15.1.2 {ncgi query set} { withncgi querystring nameList=val+ue&nameList=value2 { ncgi1 query set foo 1 ncgi1 query set bar {a b} list [ncgi1 all nameList] [ncgi1 get foo] [ ncgi1 get bar] } } {{{val ue} value2} 1 {a b}} test ncgi-16.1 {ncgi::importFile} { set X [genformdata $binary_content] withncgi body $X contenttype [genformcontent_type] { ncgi1 importFile -client the_file_name } } {C:Program FilesNetscapeCommunicatorProgramnareadme.htm} test ncgi-16.2 {ncgi::importFile - content type} { global binary_content set X [genformdata $binary_content] withncgi body $X contenttype [genformcontent_type] { ncgi1 importFile -type the_file_name } } text/html test ncgi-16.3 {ncgi::importFile -- file contents} { global binary_content set X [genformdata $binary_content] withncgi contenttype [genformcontent_type] body $X { set chan [ncgi1 importFile -data the_file_name] set res [$chan read] $chan close return $res } } $binary_content test ncgi-16.4 {ncgi::importFile -- save file} { global binary_content set X [genformdata $binary_content] withncgi contenttype [genformcontent_type] body $X { set chan [ncgi1 importFile -server the_file_name] # get the contents of the local file to verify $chan configure -translation binary set content [$chan read] $chan close return $content } } $binary_content test ncgi-16.5 {ncgi::importFile -- save file, given name} { global binary_content set X [genformdata $binary_content] withncgi contenttype [genformcontent_type] body $X { set chan [ncgi1 importFile -server the_file_name] # get the contents of the local file to verify $chan configure -translation binary set content [$chan read] $chan close return $content } } $binary_content test ncgi-16.6 {ncgi::importFile -- bad input} { set X "bad multipart data" withncgi contenttype [genformcontent_type] body $X { catch {ncgi1 importFile -client the_file_name} res return $res } } {end-of-string encountered while parsing multipart/form-data} test ncgi-17.1 {ncgi::names} { withncgi querystring name=hello+world&name2=%7ewelch { dict keys [ncgi1 get] } } {name name2} test ncgi-17.2 {ncgi::names} { withncgi querystring name=&name2 \ contenttype application/x-www-urlencoded { dict keys [ncgi1 get] } } {name name2} test ncgi-17.3 {ncgi::names} { withncgi querystring name=&name2 \ contenttype application/x-www-form-urlencoded { dict keys [ncgi1 get] } } {name name2} test ncgi-17.4 {ncgi::names} { withncgi querystring name=&name2 contenttype application/xyzzy { set code [catch {ncgi1 get} err] list $code $err } } {0 {name {{} {}} name2 {{} {}}}} # ------------------------------------------------------------------------- test ncgi-18.0 {ncgi::cookie::get} { set env(HTTP_COOKIE) {one=1;two=2;two=3;three=4} withncgi { ncgi1 cookies get } } {one 1 two 2 two 3 three 4} test ncgi-18.1 {ncgi::cookie::get} { set env(HTTP_COOKIE) {one=1;two=2;two=3;three=4} withncgi { ncgi1 cookies get two } } 3 test ncgi-18.2 {ncgi::cookie::all} { set env(HTTP_COOKIE) {one=1;two=2;two=3;three=4} withncgi { ncgi1 cookies all two } } {2 3} # ------------------------------------------------------------------------- testsuiteCleanup return } finally { set [namespace current]::done 1 }} after 0 [list ::coroutine [info cmdcount]_main [namespace current]::main] vwait [namespace current]::done return |
Changes to modules/smtpd/clients/mail-test.tcl.
1 2 3 4 5 6 7 8 9 | package require mime package require smtp set sndr "tcl-test-script@localhost" set rcpt "tcllib-test@localhost" set msg "This is a sample message send from Tcl.\nAs\ always, let us check the transparency function:\n. <-- there\ should be a dot there.\nBye" | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package require mime package require smtp set sndr "tcl-test-script@localhost" set rcpt "tcllib-test@localhost" set msg "This is a sample message send from Tcl.\nAs\ always, let us check the transparency function:\n. <-- there\ should be a dot there.\nBye" set tok [mime::.new {} -canonical text/plain -encoding 7bit -string $msg] $tok header set Subject "Testing from Tcl" smtp::sendmessage $tok -servers localhost \ -header [list To $rcpt] \ -header [list From $sndr] $tok .destroy |
Changes to modules/smtpd/smtpd.tcl.
︙ | ︙ | |||
489 490 491 492 493 494 495 | set deliverMIME [cget deliverMIME] if { $deliverMIME != {} \ && [state $channel from] != {} \ && [state $channel to] != {} \ && [state $channel data] != {} } { # create a MIME token from the mail message. | | | | | 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 | set deliverMIME [cget deliverMIME] if { $deliverMIME != {} \ && [state $channel from] != {} \ && [state $channel to] != {} \ && [state $channel data] != {} } { # create a MIME token from the mail message. set tok [mime::.new {} -string \ [join [state $channel data] \n]] # mime::setheader $tok "From" [state $channel from] # foreach recipient [state $channel to] { # mime::setheader $tok "To" $recipient -mode append # } # catch and rethrow any errors. set err [catch {eval $deliverMIME [list $tok]} msg] $tok .destroy -subordinates all if {$err} { Log debug "error in deliver: $msg" return -code error -errorcode $::errorCode \ -errorinfo $::errorInfo $msg } } else { |
︙ | ︙ |
Changes to modules/struct/disjointset.man.
|
| > | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | [vset VERSION 1.1] [manpage_begin struct::disjointset n [vset VERSION]] [keywords {disjoint set}] [keywords {equivalence class}] [keywords find] [keywords {merge find}] [keywords partition] [keywords {partitioned set}] [keywords union] [moddesc {Tcl Data Structures}] [titledesc {Disjoint set data structure}] [category {Data structures}] [require Tcl 8.6] [require struct::disjointset [opt [vset VERSION]]] [description] [para] This package provides [term {disjoint sets}]. An alternative name for this kind of structure is [term {merge-find}]. [para] |
︙ | ︙ | |||
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | [call [arg disjointsetName] [arg option] [opt [arg {arg arg ...}]]] The [cmd option] and the [arg arg]s determine the exact behavior of the command. The following commands are possible for disjointset objects: [list_end] [call [arg disjointsetName] [method add-partition] [arg elements]] Creates a new partition in specified disjoint set, and fills it with the values found in the set of [arg elements]. The command maintains the integrity of the disjoint set, i.e. it verifies that none of the [arg elements] are already part of the disjoint set and throws an error otherwise. [para] The result of the command is the empty string. [call [arg disjointsetName] [method partitions]] Returns the set of partitions the named disjoint set currently | > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | [call [arg disjointsetName] [arg option] [opt [arg {arg arg ...}]]] The [cmd option] and the [arg arg]s determine the exact behavior of the command. The following commands are possible for disjointset objects: [list_end] [call [arg disjointsetName] [method add-element] [arg item]] Creates a new partition in the specified disjoint set, and fills it with the single item [arg item]. The command maintains the integrity of the disjoint set, i.e. it verifies that none of the [arg elements] are already part of the disjoint set and throws an error otherwise. [para] The result of this method is the empty string. [para] This method runs in constant time. [call [arg disjointsetName] [method add-partition] [arg elements]] Creates a new partition in specified disjoint set, and fills it with the values found in the set of [arg elements]. The command maintains the integrity of the disjoint set, i.e. it verifies that none of the [arg elements] are already part of the disjoint set and throws an error otherwise. [para] The result of the command is the empty string. [para] This method runs in time proportional to the size of [arg elements]]. [call [arg disjointsetName] [method partitions]] Returns the set of partitions the named disjoint set currently consists of. The form of the result is a list of lists; the inner lists contain the elements of the partitions. [para] This method runs in time O(N*alpha(N)), where N is the number of elements in the disjoint set and alpha is the inverse Ackermann function. [call [arg disjointsetName] [method num-partitions]] Returns the number of partitions the named disjoint set currently consists of. [para] This method runs in constant time. [call [arg disjointsetName] [method equal] [arg a] [arg b]] Determines if the two elements [arg a] and [arg b] of the disjoint set belong to the same partition. The result of the method is a boolean value, [const True] if the two elements are contained in the same partition, and [const False] otherwise. [para] An error will be thrown if either [arg a] or [arg b] are not elements of the disjoint set. [para] This method runs in amortized time O(alpha(N)), where N is the number of elements in the larger partition and alpha is the inverse Ackermann function. [call [arg disjointsetName] [method merge] [arg a] [arg b]] Determines the partitions the elements [arg a] and [arg b] are contained in and merges them into a single partition. If the two elements were already contained in the same partition nothing will change. [para] The result of the method is the empty string. [para] This method runs in amortized time O(alpha(N)), where N is the number of items in the larger of the partitions being merged. The worst case time is O(N). [call [arg disjointsetName] [method find] [arg e]] Returns a list of the members of the partition of the disjoint set which contains the element [arg e]. [para] This method runs in O(N*alpha(N)) time, where N is the total number of items in the disjoint set and alpha is the inverse Ackermann function, See [method find-exemplar] for a faster method, if all that is needed is a unique identifier for the partition, rather than an enumeration of all its elements. [call [arg disjointsetName] [method exemplars]] Returns a list containing an exemplar of each partition in the disjoint set. The exemplar is a member of the partition, chosen arbitrarily. [para] This method runs in O(N*alpha(N)) time, where N is the total number of items in the disjoint set and alpha is the inverse Ackermann function. [call [arg disjointsetName] [method find-exemplar] [arg e]] Returns the exemplar of the partition of the disjoint set containing the element [arg e]. Throws an error if [arg e] is not found in the disjoint set. The exemplar is an arbitrarily chosen member of the partition. The only operation that will change the exemplar of any partition is [method merge]. [para] This method runs in O(alpha(N)) time, where N is the number of items in the partition containing E, and alpha is the inverse Ackermann function. [call [arg disjointsetName] [method destroy]] Destroys the disjoint set object and all associated memory. [list_end] [vset CATEGORY {struct :: disjointset}] [include ../doctools2base/include/feedback.inc] [manpage_end] |
Changes to modules/struct/disjointset.tcl.
1 2 3 4 5 6 7 | # disjointset.tcl -- # # Implementation of a Disjoint Set for Tcl. # # Copyright (c) Google Summer of Code 2008 Alejandro Eduardo Cruz Paz # Copyright (c) 2008 Andreas Kupries (API redesign and simplification) | > > > > > > > > > > > | | < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < | < < < < < | < | | | | < | < < < < < < < < < < < < < < | < < < | < < < > < < < | < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | # disjointset.tcl -- # # Implementation of a Disjoint Set for Tcl. # # Copyright (c) Google Summer of Code 2008 Alejandro Eduardo Cruz Paz # Copyright (c) 2008 Andreas Kupries (API redesign and simplification) # Copyright (c) 2018 by Kevin B. Kenny - reworked to a proper disjoint-sets # data structure, added 'add-element', 'exemplars' and 'find-exemplar'. # References # # - General overview # - https://en.wikipedia.org/wiki/Disjoint-set_data_structure # # - Time/Complexity proofs # - https://dl.acm.org/citation.cfm?doid=62.2160 # - https://dl.acm.org/citation.cfm?doid=364099.364331 # package require Tcl 8.6 # Initialize the disjointset structure namespace. Note that any # missing parent namespace (::struct) will be automatically created as # well. namespace eval ::struct::disjointset { # Only export one command, the one used to instantiate a new # disjoint set namespace export disjointset } # class struct::disjointset::_disjointset -- # # Implementation of a disjoint-sets data structure oo::class create struct::disjointset::_disjointset { # elements - Dictionary whose keys are all the elements in the structure, # and whose values are element numbers. # tree - List indexed by element number whose members are # ordered triples consisting of the element's name, # the element number of the element's parent (or the element's # own index if the element is a root), and the rank of # the element. # nParts - Number of partitions in the structure. Maintained only # so that num_partitions will work. variable elements tree nParts constructor {} { set elements {} set tree {} set nParts 0 } # add-element -- # # Adds an element to the structure # # Parameters: # item - Name of the element to add # # Results: # None. # # Side effects: # Element is added method add-element {item} { if {[dict exists $elements $item]} { return -code error \ -errorcode [list STRUCT DISJOINTSET DUPLICATE $item [self]] \ "The element \"$item\" is already known to the disjoint\ set [self]" } set n [llength $tree] dict set elements $item $n lappend tree [list $item $n 0] incr nParts return } # add-partition -- # # Adds a collection of new elements to a disjoint-sets structure and # makes them all one partition. # # Parameters: # items - List of elements to add. # # Results: # None. # # Side effects: # Adds all the elements, and groups them into a single partition. method add-partition {items} { # Integrity check - make sure that none of the elements have yet # been added foreach name $items { if {[dict exists $elements $name]} { return -code error \ -errorcode [list STRUCT DISJOINTSET DUPLICATE \ $name [self]] \ "The element \"$name\" is already known to the disjoint\ set [self]" } } # Add all the elements in one go, and establish parent links for all # but the first set first -1 foreach n $items { set idx [llength $tree] dict set elements $n $idx if {$first < 0} { set first $idx set rank 1 } else { set rank 0 } lappend tree [list $n $first $rank] } incr nParts return } # equal -- # # Test if two elements belong to the same partition in a disjoint-sets # data structure. # # Parameters: # a - Name of the first element # b - Name of the second element # # Results: # Returns 1 if the elements are in the same partition, and 0 otherwise. method equal {a b} { expr {[my FindNum $a] == [my FindNum $b]} } # exemplars -- # # Find one representative element for each partition in a disjoint-sets # data structure. # # Results: # Returns a list of element names method exemplars {} { set result {} set n -1 foreach row $tree { if {[lindex $row 1] == [incr n]} { lappend result [lindex $row 0] } } return $result } # find -- # # Find the partition to which a given element belongs. # # Parameters: # item - Item to find # # Results: # Returns a list of the partition's members # # Notes: # This operation takes time proportional to the total number of elements # in the disjoint-sets structure. If a simple name of the partition # is all that is required, use "find-exemplar" instead, which runs # in amortized time proportional to the inverse Ackermann function of # the size of the partition. method find {item} { set result {} # No error on a nonexistent item if {![dict exists $elements $item]} { return {} } set pnum [my FindNum $item] set n -1 foreach row $tree { if {[my FindByNum [incr n]] eq $pnum} { lappend result [lindex $row 0] } } return $result } # find-exemplar -- # # Find a representative element of the partition that contains a given # element. # # parameters: # item - Item to examine # # Results: # Returns the exemplar # # Notes: # Takes O(alpha(|P|)) amortized time, where |P| is the size of the # partition, and alpha is the inverse Ackermann function method find-exemplar {item} { return [lindex $tree [my FindNum $item] 0] } # merge -- # # Merges the partitions that two elements are in. # # Results: # None. method merge {a b} { my MergeByNum [my FindNum $a] [my FindNum $b] } # num-partitions -- # # Counts the partitions of a disjoint-sets data structure # # Results: # Returns the partition count. method num-partitions {} { return $nParts } # partitions -- # # Enumerates the partitions of a disjoint-sets data structure # # Results: # Returns a list of lists. Each list is one of the partitions # in the disjoint set, and each member of the sublist is one # of the elements added to the structure. method partitions {} { # Find the partition number for each element, and accumulate a # list per partition set parts {} dict for {element eltNo} $elements { set partNo [my FindByNum $eltNo] dict lappend parts $partNo $element } return [dict values $parts] } # FindNum -- # # Finds the partition number for an element. # # Parameters: # item - Item to look up # # Results: # Returns the partition number method FindNum {item} { if {![dict exists $elements $item]} { return -code error \ -errorcode [list STRUCT DISJOINTSET NOTFOUND $item [self]] \ "The element \"$item\" is not known to the disjoint\ set [self]" } return [my FindByNum [dict get $elements $item]] } # FindByNum -- # # Finds the partition number for an element, given the element's # index # # Parameters: # idx - Index of the item to look up # # Results: # Returns the partition number # # Side effects: # Performs path splitting method FindByNum {idx} { while {1} { set parent [lindex $tree $idx 1] if {$parent == $idx} { return $idx } set prev $idx set idx $parent lset tree $prev 1 [lindex $tree $idx 1] } } # MergeByNum -- # # Merges two partitions in a disjoint-sets data structure # # Parameters: # x - Index of an element in the first partition # y - Index of an element in the second partition # # Results: # None # # Side effects: # Merges the partition of the lower rank into the one of the # higher rank. method MergeByNum {x y} { set xroot [my FindByNum $x] set yroot [my FindByNum $y] if {$xroot == $yroot} { # The elements are already in the same partition return } incr nParts -1 # Make xroot the taller tree if {[lindex $tree $xroot 2] < [lindex $tree $yroot 2]} { set t $xroot; set xroot $yroot; set yroot $t } # Merge yroot into xroot set xrank [lindex $tree $xroot 2] set yrank [lindex $tree $yroot 2] lset tree $yroot 1 $xroot if {$xrank == $yrank} { lset tree $xroot 2 [expr {$xrank + 1}] } } } # ::struct::disjointset::disjointset -- # # Create a new disjoint set with a given name; if no name is # given, use disjointsetX, where X is a number. # # Arguments: # name Optional name of the disjoint set; if not specified, generate one. # # Results: # name Name of the disjoint set created proc ::struct::disjointset::disjointset {args} { switch -exact -- [llength $args] { 0 { return [_disjointset new] } 1 { # Name supplied by user return [uplevel 1 [list [namespace which _disjointset] \ create [lindex $args 0]]] } default { # Too many args return -code error \ -errorcode {TCL WRONGARGS} \ "wrong # args: should be \"[lindex [info level 0] 0] ?name?\"" } } } namespace eval ::struct { namespace import disjointset::disjointset namespace export disjointset } package provide struct::disjointset 1.1 return |
Changes to modules/struct/disjointset.test.
1 2 3 4 5 6 7 8 9 10 | # -*- tcl -*- # Test procedures for the disjoint set structure implementation # Author: Alejandro Eduardo Cruz Paz # 5 August 2008 package require tcltest source [file join \ [file dirname [file dirname [file join [pwd] [info script]]]] \ devtools testutilities.tcl] | > > | < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # -*- tcl -*- # Test procedures for the disjoint set structure implementation # Author: Alejandro Eduardo Cruz Paz # 5 August 2008 # Copyright (c) 2018 by Kevin B. Kenny - reworked to a proper disjoint-sets # data structure, added 'add-element', 'exemplars' and 'find-exemplar'. package require tcltest source [file join \ [file dirname [file dirname [file join [pwd] [info script]]]] \ devtools testutilities.tcl] testsNeedTcl 8.6 testsNeedTcltest 1.0 support { } testing { useLocal disjointset.tcl struct::disjointset } ############################################################ # Helper functions |
︙ | ︙ | |||
42 43 44 45 46 47 48 | return [lsort -dict $res] } proc djstate {ds} { list [canonset [$ds partitions]] [$ds num-partitions] } | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < < < < | 42 43 44 45 46 47 48 49 50 51 | return [lsort -dict $res] } proc djstate {ds} { list [canonset [$ds partitions]] [$ds num-partitions] } source [localPath disjointset.testsuite] testsuiteCleanup |
Changes to modules/struct/disjointset.testsuite.
1 2 3 4 5 6 7 8 | # -*- tcl -*- # Tests for the 'disjointset' module in the 'struct' library. -*- tcl -*- # # This file contains a collection of tests for one or more of the Tcllib # procedures. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # # Copyright (c) 2008 by Alejandro Eduardo Cruz Paz | | > > > | | | | | | | | | | | > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 | # -*- tcl -*- # Tests for the 'disjointset' module in the 'struct' library. -*- tcl -*- # # This file contains a collection of tests for one or more of the Tcllib # procedures. Sourcing this file into Tcl runs the tests and # generates output for errors. No output means no errors were found. # # Copyright (c) 2008 by Alejandro Eduardo Cruz Paz # Copyright (c) 2008 by Andreas Kupries (extended for API changes and # error conditions) # Copyright (c) 2018 by Kevin B. Kenny - reworked to a proper disjoint-sets # data structure, added 'add-element', 'exemplars' and 'find-exemplar'. # # RCS: @(#) $Id: disjointset.testsuite,v 1.1 2008/09/10 16:23:14 andreas_kupries Exp $ #---------------------------------------------------------------------- test disjointset-1.0 {disjointset creation} { ::struct::disjointset DS set result [djstate DS] DS destroy set result } {{} 0} test disjointset-1.1 {disjointset creation error} { catch {::struct::disjointset DS other} msg set result $msg } {wrong # args: should be "::struct::disjointset ?name?"} #---------------------------------------------------------------------- test disjointset-2.0 {disjointset add-partition error, wrong#args, missing} { ::struct::disjointset DS catch {DS add-partition} msg DS destroy set msg } [tcltest::wrongNumArgs "DS add-partition" {items} 1] test disjointset-2.1 {disjointset add-partition error, wrong#args, too many} { ::struct::disjointset DS catch {DS add-partition x y} msg DS destroy set msg } [tcltest::tooManyArgs "DS add-partition" {items}] test disjointset-2.2 {disjointset add-partition error, elements already known} { testset catch {DS add-partition {1}} msg DS destroy set msg } {The element "1" is already known to the disjoint set ::DS} test disjointset-2.3 {disjointset add-partition, ok} { testset set result [list [DS add-partition {11 14}] [djstate DS]] DS destroy set result } {{} {{0 {1 2 3 4} {5 6} {7 10} {8 9} {11 14}} 6}} #---------------------------------------------------------------------- test disjointset-3.0 {disjointset partitions error, wrong#args, too many} { ::struct::disjointset DS catch {DS partitions x} msg DS destroy set msg } [tcltest::tooManyArgs "DS partitions" {}] test disjointset-3.1 {disjointset partitions, ok} { testset set result [djstate DS] DS destroy set result } {{0 {1 2 3 4} {5 6} {7 10} {8 9}} 5} test disjointset-3.2 {disjointset partitions, empty} { ::struct::disjointset DS set result [DS partitions] DS destroy set result } {} #---------------------------------------------------------------------- test disjointset-4.0 {disjointset equal error, wrong#args, missing} { ::struct::disjointset DS catch {DS equal} msg DS destroy set msg } [tcltest::wrongNumArgs "DS equal" {a b} 1] test disjointset-4.1 {disjointset equal error, wrong#args, missing} { ::struct::disjointset DS catch {DS equal x} msg DS destroy set msg } [tcltest::wrongNumArgs "DS equal" {a b} 2] test disjointset-4.2 {disjointset equal error, wrong#args, too many} { ::struct::disjointset DS catch {DS equal x y z} msg DS destroy set msg } [tcltest::tooManyArgs "DS equal" {a b}] test disjointset-4.3 {disjointset equal error, unknown elements} { testset catch {DS equal x 1} msg DS destroy set msg } {The element "x" is not known to the disjoint set ::DS} test disjointset-4.4 {disjointset equal error, unknown elements} { testset catch {DS equal 1 x} msg DS destroy set msg } {The element "x" is not known to the disjoint set ::DS} test disjointset-4.5 {disjointset equal ok, unequal elements} { testset set res [DS equal 1 5] DS destroy set res } 0 test disjointset-4.6 {disjointset equal ok, equal elements} { testset set res [DS equal 4 1] DS destroy set res } 1 #---------------------------------------------------------------------- test disjointset-5.0 {disjointset merge error, wrong#args, missing} { ::struct::disjointset DS catch {DS merge} msg DS destroy set msg } [tcltest::wrongNumArgs "DS merge" {a b} 1] test disjointset-5.1 {disjointset merge error, wrong#args, missing} { ::struct::disjointset DS catch {DS merge x} msg DS destroy set msg } [tcltest::wrongNumArgs "DS merge" {a b} 2] test disjointset-5.2 {disjointset merge error, wrong#args, too many} { ::struct::disjointset DS catch {DS merge x y z} msg DS destroy set msg } [tcltest::tooManyArgs "DS merge" {a b}] test disjointset-5.3 {disjointset merge error, unknown elements} { testset catch {DS merge x 1} msg DS destroy set msg } {The element "x" is not known to the disjoint set ::DS} test disjointset-5.4 {disjointset merge error, unknown elements} { testset catch {DS merge 1 x} msg DS destroy set msg } {The element "x" is not known to the disjoint set ::DS} test disjointset-5.5 {disjointset merge ok, different partitions} { testset DS merge 1 5 set result [djstate DS] DS destroy set result } {{0 {1 2 3 4 5 6} {7 10} {8 9}} 4} test disjointset-5.6 {disjointset merge ok, same partition, no change} { testset DS merge 4 3 set result [djstate DS] DS destroy set result } {{0 {1 2 3 4} {5 6} {7 10} {8 9}} 5} #---------------------------------------------------------------------- test disjointset-6.0 {disjointset find error, wrong#args, missing} { ::struct::disjointset DS catch {DS find} msg DS destroy set msg } [tcltest::wrongNumArgs "DS find" {item} 1] test disjointset-6.1 {disjointset find error, wrong#args, too many} { ::struct::disjointset DS catch {DS find x y} msg DS destroy set msg } [tcltest::tooManyArgs "DS find" {item}] test disjointset-6.2 {disjointset find, unknown element} { testset set result [DS find 11] DS destroy set result } {} test disjointset-6.3 {disjointset find, known element} { testset set result [lsort -dict [DS find 3]] DS destroy set result } {1 2 3 4} #---------------------------------------------------------------------- test disjointset-7.0 {disjointset num-partitions error, wrong#args, too many} { ::struct::disjointset DS catch {DS num-partitions x} msg DS destroy set msg } [tcltest::tooManyArgs "DS num-partitions" {}] test disjointset-7.1 {disjointset num-partitions, ok} { testset set result [DS num-partitions] DS destroy set result } 5 #---------------------------------------------------------------------- test disjointset-8.0 {disjointset add-element error, wrongArgs, none} { ::struct::disjointset DS catch {DS add-element} msg DS destroy set msg } [tcltest::wrongNumArgs "DS add-element" {item} 1] test disjointset-8.1 {disjointset add-element error, wrongArgs, too many} { ::struct::disjointset DS catch {DS add-element p q} msg DS destroy set msg } [tcltest::tooManyArgs "DS add-element" {item}] test disjointset-8.2 {disjointset add-element error, duplicate element} { testset catch {DS add-element 0} message DS destroy set message } {The element "0" is already known to the disjoint set ::DS} test disjointset-8.3 {disjointset add-element ok} { testset DS add-element 11 set result [djstate DS] DS destroy set result } {{0 {1 2 3 4} {5 6} {7 10} {8 9} 11} 6} #---------------------------------------------------------------------- test disjointset-9.0 {disjointset find-exemplar error, wrongArgs, none} { ::struct::disjointset DS catch {DS find-exemplar} msg DS destroy set msg } [tcltest::wrongNumArgs "DS find-exemplar" {item} 1] test disjointset-9.1 {disjointset find-exemplar error, wrongArgs, too many} { ::struct::disjointset DS catch {DS find-exemplar p q} msg DS destroy set msg } [tcltest::tooManyArgs "DS find-exemplar" {item}] test disjointset-9.2 {disjointset find-exemplar error, not found} { testset catch {DS find-exemplar x} message DS destroy set message } {The element "x" is not known to the disjoint set ::DS} test disjointset-9.3 {disjointset find-exemplar ok} { testset set result [DS find-exemplar 3] DS destroy expr {$result in {1 2 3 4}} } {1} #---------------------------------------------------------------------- test disjointset-10.0 {disjointset exemplars error, wrong#args, too many} { ::struct::disjointset DS catch {DS exemplars x} msg DS destroy set msg } [tcltest::tooManyArgs "DS exemplars" {}] test disjointset-10.1 {disjointset exemplars, ok} { ::struct::disjointset DS DS add-element 0 set result [DS exemplars] DS destroy set result } 0 test disjointset-10.2 {disjointset exemplars, empty} { ::struct::disjointset DS set result [DS exemplars] DS destroy set result } {} #---------------------------------------------------------------------- test disjointset-11.0 {disjointset merge - larger randomized set of merges} { struct::disjointset DS foreach item {a b c d e f g h i j k l m n o p q r s t u v w x y z} { DS add-partition [list $item] } DS merge g a DS merge o n DS merge v o DS merge c w DS merge r h DS merge s y DS merge g i DS merge d f DS merge m q DS merge a z DS merge k e DS merge x k DS merge r s DS merge h m DS merge d l DS merge e a DS merge o t DS merge q p DS merge u c DS merge o a DS merge p j DS merge b l DS merge p c DS merge f e set result [lsort [lmap x [DS partitions] {lsort $x}]] DS destroy set result } {{a b d e f g i k l n o t v x z} {c h j m p q r s u w y}} test disjointset-11.1 {disjointset merge - larger randomized set of merges} { struct::disjointset DS foreach item {a b c d e f g h i j k l m n o p q r s t u v w x y z} { DS add-partition [list $item] } DS merge g a DS merge o n DS merge v o DS merge c w DS merge r h DS merge s y DS merge g i DS merge d f DS merge m q DS merge a z DS merge k e DS merge x k DS merge r s DS merge h m DS merge d l DS merge e a DS merge o t DS merge q p DS merge u c DS merge o a DS merge p j DS merge b l DS merge p c DS merge f e set result [DS exemplars] DS destroy set trouble {} if {[llength $result] ne 2} { append trouble "\nShould be two exemplars, found $result" } lassign $result e1 e2 set l1 {a b d e f g i k l n o t v x z} set l2 {c h j m p q r s u w y} if {!(($e1 in $l1) ^ ($e2 in $l1))} { append trouble "\nExactly one of $e1 and $e2\ should be in the first set" } if {!(($e1 in $l2) ^ ($e2 in $l2))} { append trouble "\nExactly one of $e1 and $e2\ should be in the second set" } set trouble } {} |
Changes to modules/struct/graphops.man.
1 | [comment {-*- tcl -*-}] | > | | 1 2 3 4 5 6 7 8 9 10 | [comment {-*- tcl -*-}] [vset VERSION 0.11.3] [manpage_begin struct::graph::op n [vset VERSION]] [keywords {adjacency list}] [keywords {adjacency matrix}] [keywords adjacent] [keywords {approximation algorithm}] [keywords arc] [keywords {articulation point}] [keywords {augmenting network}] |
︙ | ︙ | |||
51 52 53 54 55 56 57 | [keywords {vertex cover}] [copyright {2008 Alejandro Paz <[email protected]>}] [copyright {2008 (docs) Andreas Kupries <[email protected]>}] [copyright {2009 Michal Antoniewski <[email protected]>}] [moddesc {Tcl Data Structures}] [titledesc {Operation for (un)directed graph objects}] [category {Data structures}] | | | | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | [keywords {vertex cover}] [copyright {2008 Alejandro Paz <[email protected]>}] [copyright {2008 (docs) Andreas Kupries <[email protected]>}] [copyright {2009 Michal Antoniewski <[email protected]>}] [moddesc {Tcl Data Structures}] [titledesc {Operation for (un)directed graph objects}] [category {Data structures}] [require Tcl 8.6] [require struct::graph::op [opt [vset VERSION]]] [comment {[require struct::graph [opt 2.3]] }] [comment {[require struct::list [opt 1.5]] }] [comment {[require struct::set [opt 2.2.3]] }] [description] [para] The package described by this document, [package struct::graph::op], |
︙ | ︙ |
Changes to modules/struct/graphops.tcl.
︙ | ︙ | |||
9 10 11 12 13 14 15 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: graphops.tcl,v 1.19 2009/09/24 19:30:10 andreas_kupries Exp $ # ### ### ### ######### ######### ######### ## Requisites | | | | 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | # of this file, and for a DISCLAIMER OF ALL WARRANTIES. # # RCS: @(#) $Id: graphops.tcl,v 1.19 2009/09/24 19:30:10 andreas_kupries Exp $ # ### ### ### ######### ######### ######### ## Requisites package require Tcl 8.6 package require struct::disjointset ; # Used by kruskal -- 8.6 required package require struct::prioqueue ; # Used by kruskal, prim package require struct::queue ; # Used by isBipartite?, connectedComponent(Of) package require struct::stack ; # Used by tarjan package require struct::graph ; # isBridge, isCutVertex package require struct::tree ; # Used by BFS # ### ### ### ######### ######### ######### |
︙ | ︙ |
Changes to modules/struct/graphops.test.
︙ | ︙ | |||
8 9 10 11 12 13 14 | # ------------------------------------------------------------------------- source [file join \ [file dirname [file dirname [file join [pwd] [info script]]]] \ devtools testutilities.tcl] | | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # ------------------------------------------------------------------------- source [file join \ [file dirname [file dirname [file join [pwd] [info script]]]] \ devtools testutilities.tcl] testsNeedTcl 8.6 testsNeedTcltest 2.0 support { useLocal list.tcl struct::list useAccel [useTcllibC] struct/tree.tcl struct::tree TestAccelInit struct::tree |
︙ | ︙ |
Changes to modules/struct/pkgIndex.tcl.
1 2 3 4 5 6 7 8 9 10 11 | if {![package vsatisfies [package provide Tcl] 8.2]} {return} package ifneeded struct 2.1 [list source [file join $dir struct.tcl]] package ifneeded struct 1.4 [list source [file join $dir struct1.tcl]] package ifneeded struct::queue 1.4.5 [list source [file join $dir queue.tcl]] package ifneeded struct::stack 1.5.3 [list source [file join $dir stack.tcl]] package ifneeded struct::tree 2.1.2 [list source [file join $dir tree.tcl]] package ifneeded struct::matrix 2.0.3 [list source [file join $dir matrix.tcl]] package ifneeded struct::pool 1.2.3 [list source [file join $dir pool.tcl]] package ifneeded struct::record 1.2.1 [list source [file join $dir record.tcl]] package ifneeded struct::set 2.2.3 [list source [file join $dir sets.tcl]] | < > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | if {![package vsatisfies [package provide Tcl] 8.2]} {return} package ifneeded struct 2.1 [list source [file join $dir struct.tcl]] package ifneeded struct 1.4 [list source [file join $dir struct1.tcl]] package ifneeded struct::queue 1.4.5 [list source [file join $dir queue.tcl]] package ifneeded struct::stack 1.5.3 [list source [file join $dir stack.tcl]] package ifneeded struct::tree 2.1.2 [list source [file join $dir tree.tcl]] package ifneeded struct::matrix 2.0.3 [list source [file join $dir matrix.tcl]] package ifneeded struct::pool 1.2.3 [list source [file join $dir pool.tcl]] package ifneeded struct::record 1.2.1 [list source [file join $dir record.tcl]] package ifneeded struct::set 2.2.3 [list source [file join $dir sets.tcl]] package ifneeded struct::prioqueue 1.4 [list source [file join $dir prioqueue.tcl]] package ifneeded struct::skiplist 1.3 [list source [file join $dir skiplist.tcl]] package ifneeded struct::graph 1.2.1 [list source [file join $dir graph1.tcl]] package ifneeded struct::tree 1.2.2 [list source [file join $dir tree1.tcl]] package ifneeded struct::matrix 1.2.1 [list source [file join $dir matrix1.tcl]] if {![package vsatisfies [package provide Tcl] 8.4]} {return} package ifneeded struct::list 1.8.3 [list source [file join $dir list.tcl]] package ifneeded struct::graph 2.4.1 [list source [file join $dir graph.tcl]] if {![package vsatisfies [package provide Tcl] 8.5]} {return} if {![package vsatisfies [package provide Tcl] 8.6]} {return} package ifneeded struct::disjointset 1.1 [list source [file join $dir disjointset.tcl]] package ifneeded struct::graph::op 0.11.3 [list source [file join $dir graphops.tcl]] |
Added modules/textutil/build/EastAsianWidth.txt.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 | # EastAsianWidth-11.0.0.txt # Date: 2018-05-14, 09:41:59 GMT [KW, LI] # © 2018 Unicode®, Inc. # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. # For terms of use, see http://www.unicode.org/terms_of_use.html # # Unicode Character Database # For documentation, see http://www.unicode.org/reports/tr44/ # # East_Asian_Width Property # # This file is an informative contributory data file in the # Unicode Character Database. # # The format is two fields separated by a semicolon. # Field 0: Unicode code point value or range of code point values # Field 1: East_Asian_Width property, consisting of one of the following values: # "A", "F", "H", "N", "Na", "W" # - All code points, assigned or unassigned, that are not listed # explicitly are given the value "N". # - The unassigned code points in the following blocks default to "W": # CJK Unified Ideographs Extension A: U+3400..U+4DBF # CJK Unified Ideographs: U+4E00..U+9FFF # CJK Compatibility Ideographs: U+F900..U+FAFF # - All undesignated code points in Planes 2 and 3, whether inside or # outside of allocated blocks, default to "W": # Plane 2: U+20000..U+2FFFD # Plane 3: U+30000..U+3FFFD # # Character ranges are specified as for other property files in the # Unicode Character Database. # # For legacy reasons, there are no spaces before or after the semicolon # which separates the two fields. The comments following the number sign # "#" list the General_Category property value or the L& alias of the # derived value LC, the Unicode character name or names, and, in lines # with ranges of code points, the code point count in square brackets. # # For more information, see UAX #11: East Asian Width, # at http://www.unicode.org/reports/tr11/ # # @missing: 0000..10FFFF; N 0000..001F;N # Cc [32] <control-0000>..<control-001F> 0020;Na # Zs SPACE 0021..0023;Na # Po [3] EXCLAMATION MARK..NUMBER SIGN 0024;Na # Sc DOLLAR SIGN 0025..0027;Na # Po [3] PERCENT SIGN..APOSTROPHE 0028;Na # Ps LEFT PARENTHESIS 0029;Na # Pe RIGHT PARENTHESIS 002A;Na # Po ASTERISK 002B;Na # Sm PLUS SIGN 002C;Na # Po COMMA 002D;Na # Pd HYPHEN-MINUS 002E..002F;Na # Po [2] FULL STOP..SOLIDUS 0030..0039;Na # Nd [10] DIGIT ZERO..DIGIT NINE 003A..003B;Na # Po [2] COLON..SEMICOLON 003C..003E;Na # Sm [3] LESS-THAN SIGN..GREATER-THAN SIGN 003F..0040;Na # Po [2] QUESTION MARK..COMMERCIAL AT 0041..005A;Na # Lu [26] LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z 005B;Na # Ps LEFT SQUARE BRACKET 005C;Na # Po REVERSE SOLIDUS 005D;Na # Pe RIGHT SQUARE BRACKET 005E;Na # Sk CIRCUMFLEX ACCENT 005F;Na # Pc LOW LINE 0060;Na # Sk GRAVE ACCENT 0061..007A;Na # Ll [26] LATIN SMALL LETTER A..LATIN SMALL LETTER Z 007B;Na # Ps LEFT CURLY BRACKET 007C;Na # Sm VERTICAL LINE 007D;Na # Pe RIGHT CURLY BRACKET 007E;Na # Sm TILDE 007F;N # Cc <control-007F> 0080..009F;N # Cc [32] <control-0080>..<control-009F> 00A0;N # Zs NO-BREAK SPACE 00A1;A # Po INVERTED EXCLAMATION MARK 00A2..00A3;Na # Sc [2] CENT SIGN..POUND SIGN 00A4;A # Sc CURRENCY SIGN 00A5;Na # Sc YEN SIGN 00A6;Na # So BROKEN BAR 00A7;A # Po SECTION SIGN 00A8;A # Sk DIAERESIS 00A9;N # So COPYRIGHT SIGN 00AA;A # Lo FEMININE ORDINAL INDICATOR 00AB;N # Pi LEFT-POINTING DOUBLE ANGLE QUOTATION MARK 00AC;Na # Sm NOT SIGN 00AD;A # Cf SOFT HYPHEN 00AE;A # So REGISTERED SIGN 00AF;Na # Sk MACRON 00B0;A # So DEGREE SIGN 00B1;A # Sm PLUS-MINUS SIGN 00B2..00B3;A # No [2] SUPERSCRIPT TWO..SUPERSCRIPT THREE 00B4;A # Sk ACUTE ACCENT 00B5;N # Ll MICRO SIGN 00B6..00B7;A # Po [2] PILCROW SIGN..MIDDLE DOT 00B8;A # Sk CEDILLA 00B9;A # No SUPERSCRIPT ONE 00BA;A # Lo MASCULINE ORDINAL INDICATOR 00BB;N # Pf RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK 00BC..00BE;A # No [3] VULGAR FRACTION ONE QUARTER..VULGAR FRACTION THREE QUARTERS 00BF;A # Po INVERTED QUESTION MARK 00C0..00C5;N # Lu [6] LATIN CAPITAL LETTER A WITH GRAVE..LATIN CAPITAL LETTER A WITH RING ABOVE 00C6;A # Lu LATIN CAPITAL LETTER AE 00C7..00CF;N # Lu [9] LATIN CAPITAL LETTER C WITH CEDILLA..LATIN CAPITAL LETTER I WITH DIAERESIS 00D0;A # Lu LATIN CAPITAL LETTER ETH 00D1..00D6;N # Lu [6] LATIN CAPITAL LETTER N WITH TILDE..LATIN CAPITAL LETTER O WITH DIAERESIS 00D7;A # Sm MULTIPLICATION SIGN 00D8;A # Lu LATIN CAPITAL LETTER O WITH STROKE 00D9..00DD;N # Lu [5] LATIN CAPITAL LETTER U WITH GRAVE..LATIN CAPITAL LETTER Y WITH ACUTE 00DE..00E1;A # L& [4] LATIN CAPITAL LETTER THORN..LATIN SMALL LETTER A WITH ACUTE 00E2..00E5;N # Ll [4] LATIN SMALL LETTER A WITH CIRCUMFLEX..LATIN SMALL LETTER A WITH RING ABOVE 00E6;A # Ll LATIN SMALL LETTER AE 00E7;N # Ll LATIN SMALL LETTER C WITH CEDILLA 00E8..00EA;A # Ll [3] LATIN SMALL LETTER E WITH GRAVE..LATIN SMALL LETTER E WITH CIRCUMFLEX 00EB;N # Ll LATIN SMALL LETTER E WITH DIAERESIS 00EC..00ED;A # Ll [2] LATIN SMALL LETTER I WITH GRAVE..LATIN SMALL LETTER I WITH ACUTE 00EE..00EF;N # Ll [2] LATIN SMALL LETTER I WITH CIRCUMFLEX..LATIN SMALL LETTER I WITH DIAERESIS 00F0;A # Ll LATIN SMALL LETTER ETH 00F1;N # Ll LATIN SMALL LETTER N WITH TILDE 00F2..00F3;A # Ll [2] LATIN SMALL LETTER O WITH GRAVE..LATIN SMALL LETTER O WITH ACUTE 00F4..00F6;N # Ll [3] LATIN SMALL LETTER O WITH CIRCUMFLEX..LATIN SMALL LETTER O WITH DIAERESIS 00F7;A # Sm DIVISION SIGN 00F8..00FA;A # Ll [3] LATIN SMALL LETTER O WITH STROKE..LATIN SMALL LETTER U WITH ACUTE 00FB;N # Ll LATIN SMALL LETTER U WITH CIRCUMFLEX 00FC;A # Ll LATIN SMALL LETTER U WITH DIAERESIS 00FD;N # Ll LATIN SMALL LETTER Y WITH ACUTE 00FE;A # Ll LATIN SMALL LETTER THORN 00FF;N # Ll LATIN SMALL LETTER Y WITH DIAERESIS 0100;N # Lu LATIN CAPITAL LETTER A WITH MACRON 0101;A # Ll LATIN SMALL LETTER A WITH MACRON 0102..0110;N # L& [15] LATIN CAPITAL LETTER A WITH BREVE..LATIN CAPITAL LETTER D WITH STROKE 0111;A # Ll LATIN SMALL LETTER D WITH STROKE 0112;N # Lu LATIN CAPITAL LETTER E WITH MACRON 0113;A # Ll LATIN SMALL LETTER E WITH MACRON 0114..011A;N # L& [7] LATIN CAPITAL LETTER E WITH BREVE..LATIN CAPITAL LETTER E WITH CARON 011B;A # Ll LATIN SMALL LETTER E WITH CARON 011C..0125;N # L& [10] LATIN CAPITAL LETTER G WITH CIRCUMFLEX..LATIN SMALL LETTER H WITH CIRCUMFLEX 0126..0127;A # L& [2] LATIN CAPITAL LETTER H WITH STROKE..LATIN SMALL LETTER H WITH STROKE 0128..012A;N # L& [3] LATIN CAPITAL LETTER I WITH TILDE..LATIN CAPITAL LETTER I WITH MACRON 012B;A # Ll LATIN SMALL LETTER I WITH MACRON 012C..0130;N # L& [5] LATIN CAPITAL LETTER I WITH BREVE..LATIN CAPITAL LETTER I WITH DOT ABOVE 0131..0133;A # L& [3] LATIN SMALL LETTER DOTLESS I..LATIN SMALL LIGATURE IJ 0134..0137;N # L& [4] LATIN CAPITAL LETTER J WITH CIRCUMFLEX..LATIN SMALL LETTER K WITH CEDILLA 0138;A # Ll LATIN SMALL LETTER KRA 0139..013E;N # L& [6] LATIN CAPITAL LETTER L WITH ACUTE..LATIN SMALL LETTER L WITH CARON 013F..0142;A # L& [4] LATIN CAPITAL LETTER L WITH MIDDLE DOT..LATIN SMALL LETTER L WITH STROKE 0143;N # Lu LATIN CAPITAL LETTER N WITH ACUTE 0144;A # Ll LATIN SMALL LETTER N WITH ACUTE 0145..0147;N # L& [3] LATIN CAPITAL LETTER N WITH CEDILLA..LATIN CAPITAL LETTER N WITH CARON 0148..014B;A # L& [4] LATIN SMALL LETTER N WITH CARON..LATIN SMALL LETTER ENG 014C;N # Lu LATIN CAPITAL LETTER O WITH MACRON 014D;A # Ll LATIN SMALL LETTER O WITH MACRON 014E..0151;N # L& [4] LATIN CAPITAL LETTER O WITH BREVE..LATIN SMALL LETTER O WITH DOUBLE ACUTE 0152..0153;A # L& [2] LATIN CAPITAL LIGATURE OE..LATIN SMALL LIGATURE OE 0154..0165;N # L& [18] LATIN CAPITAL LETTER R WITH ACUTE..LATIN SMALL LETTER T WITH CARON 0166..0167;A # L& [2] LATIN CAPITAL LETTER T WITH STROKE..LATIN SMALL LETTER T WITH STROKE 0168..016A;N # L& [3] LATIN CAPITAL LETTER U WITH TILDE..LATIN CAPITAL LETTER U WITH MACRON 016B;A # Ll LATIN SMALL LETTER U WITH MACRON 016C..017F;N # L& [20] LATIN CAPITAL LETTER U WITH BREVE..LATIN SMALL LETTER LONG S 0180..01BA;N # L& [59] LATIN SMALL LETTER B WITH STROKE..LATIN SMALL LETTER EZH WITH TAIL 01BB;N # Lo LATIN LETTER TWO WITH STROKE 01BC..01BF;N # L& [4] LATIN CAPITAL LETTER TONE FIVE..LATIN LETTER WYNN 01C0..01C3;N # Lo [4] LATIN LETTER DENTAL CLICK..LATIN LETTER RETROFLEX CLICK 01C4..01CD;N # L& [10] LATIN CAPITAL LETTER DZ WITH CARON..LATIN CAPITAL LETTER A WITH CARON 01CE;A # Ll LATIN SMALL LETTER A WITH CARON 01CF;N # Lu LATIN CAPITAL LETTER I WITH CARON 01D0;A # Ll LATIN SMALL LETTER I WITH CARON 01D1;N # Lu LATIN CAPITAL LETTER O WITH CARON 01D2;A # Ll LATIN SMALL LETTER O WITH CARON 01D3;N # Lu LATIN CAPITAL LETTER U WITH CARON 01D4;A # Ll LATIN SMALL LETTER U WITH CARON 01D5;N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON 01D6;A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND MACRON 01D7;N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE 01D8;A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND ACUTE 01D9;N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON 01DA;A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND CARON 01DB;N # Lu LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE 01DC;A # Ll LATIN SMALL LETTER U WITH DIAERESIS AND GRAVE 01DD..024F;N # L& [115] LATIN SMALL LETTER TURNED E..LATIN SMALL LETTER Y WITH STROKE 0250;N # Ll LATIN SMALL LETTER TURNED A 0251;A # Ll LATIN SMALL LETTER ALPHA 0252..0260;N # Ll [15] LATIN SMALL LETTER TURNED ALPHA..LATIN SMALL LETTER G WITH HOOK 0261;A # Ll LATIN SMALL LETTER SCRIPT G 0262..0293;N # Ll [50] LATIN LETTER SMALL CAPITAL G..LATIN SMALL LETTER EZH WITH CURL 0294;N # Lo LATIN LETTER GLOTTAL STOP 0295..02AF;N # Ll [27] LATIN LETTER PHARYNGEAL VOICED FRICATIVE..LATIN SMALL LETTER TURNED H WITH FISHHOOK AND TAIL 02B0..02C1;N # Lm [18] MODIFIER LETTER SMALL H..MODIFIER LETTER REVERSED GLOTTAL STOP 02C2..02C3;N # Sk [2] MODIFIER LETTER LEFT ARROWHEAD..MODIFIER LETTER RIGHT ARROWHEAD 02C4;A # Sk MODIFIER LETTER UP ARROWHEAD 02C5;N # Sk MODIFIER LETTER DOWN ARROWHEAD 02C6;N # Lm MODIFIER LETTER CIRCUMFLEX ACCENT 02C7;A # Lm CARON 02C8;N # Lm MODIFIER LETTER VERTICAL LINE 02C9..02CB;A # Lm [3] MODIFIER LETTER MACRON..MODIFIER LETTER GRAVE ACCENT 02CC;N # Lm MODIFIER LETTER LOW VERTICAL LINE 02CD;A # Lm MODIFIER LETTER LOW MACRON 02CE..02CF;N # Lm [2] MODIFIER LETTER LOW GRAVE ACCENT..MODIFIER LETTER LOW ACUTE ACCENT 02D0;A # Lm MODIFIER LETTER TRIANGULAR COLON 02D1;N # Lm MODIFIER LETTER HALF TRIANGULAR COLON 02D2..02D7;N # Sk [6] MODIFIER LETTER CENTRED RIGHT HALF RING..MODIFIER LETTER MINUS SIGN 02D8..02DB;A # Sk [4] BREVE..OGONEK 02DC;N # Sk SMALL TILDE 02DD;A # Sk DOUBLE ACUTE ACCENT 02DE;N # Sk MODIFIER LETTER RHOTIC HOOK 02DF;A # Sk MODIFIER LETTER CROSS ACCENT 02E0..02E4;N # Lm [5] MODIFIER LETTER SMALL GAMMA..MODIFIER LETTER SMALL REVERSED GLOTTAL STOP 02E5..02EB;N # Sk [7] MODIFIER LETTER EXTRA-HIGH TONE BAR..MODIFIER LETTER YANG DEPARTING TONE MARK 02EC;N # Lm MODIFIER LETTER VOICING 02ED;N # Sk MODIFIER LETTER UNASPIRATED 02EE;N # Lm MODIFIER LETTER DOUBLE APOSTROPHE 02EF..02FF;N # Sk [17] MODIFIER LETTER LOW DOWN ARROWHEAD..MODIFIER LETTER LOW LEFT ARROW 0300..036F;A # Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X 0370..0373;N # L& [4] GREEK CAPITAL LETTER HETA..GREEK SMALL LETTER ARCHAIC SAMPI 0374;N # Lm GREEK NUMERAL SIGN 0375;N # Sk GREEK LOWER NUMERAL SIGN 0376..0377;N # L& [2] GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA..GREEK SMALL LETTER PAMPHYLIAN DIGAMMA 037A;N # Lm GREEK YPOGEGRAMMENI 037B..037D;N # Ll [3] GREEK SMALL REVERSED LUNATE SIGMA SYMBOL..GREEK SMALL REVERSED DOTTED LUNATE SIGMA SYMBOL 037E;N # Po GREEK QUESTION MARK 037F;N # Lu GREEK CAPITAL LETTER YOT 0384..0385;N # Sk [2] GREEK TONOS..GREEK DIALYTIKA TONOS 0386;N # Lu GREEK CAPITAL LETTER ALPHA WITH TONOS 0387;N # Po GREEK ANO TELEIA 0388..038A;N # Lu [3] GREEK CAPITAL LETTER EPSILON WITH TONOS..GREEK CAPITAL LETTER IOTA WITH TONOS 038C;N # Lu GREEK CAPITAL LETTER OMICRON WITH TONOS 038E..0390;N # L& [3] GREEK CAPITAL LETTER UPSILON WITH TONOS..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS 0391..03A1;A # Lu [17] GREEK CAPITAL LETTER ALPHA..GREEK CAPITAL LETTER RHO 03A3..03A9;A # Lu [7] GREEK CAPITAL LETTER SIGMA..GREEK CAPITAL LETTER OMEGA 03AA..03B0;N # L& [7] GREEK CAPITAL LETTER IOTA WITH DIALYTIKA..GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS 03B1..03C1;A # Ll [17] GREEK SMALL LETTER ALPHA..GREEK SMALL LETTER RHO 03C2;N # Ll GREEK SMALL LETTER FINAL SIGMA 03C3..03C9;A # Ll [7] GREEK SMALL LETTER SIGMA..GREEK SMALL LETTER OMEGA 03CA..03F5;N # L& [44] GREEK SMALL LETTER IOTA WITH DIALYTIKA..GREEK LUNATE EPSILON SYMBOL 03F6;N # Sm GREEK REVERSED LUNATE EPSILON SYMBOL 03F7..03FF;N # L& [9] GREEK CAPITAL LETTER SHO..GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL 0400;N # Lu CYRILLIC CAPITAL LETTER IE WITH GRAVE 0401;A # Lu CYRILLIC CAPITAL LETTER IO 0402..040F;N # Lu [14] CYRILLIC CAPITAL LETTER DJE..CYRILLIC CAPITAL LETTER DZHE 0410..044F;A # L& [64] CYRILLIC CAPITAL LETTER A..CYRILLIC SMALL LETTER YA 0450;N # Ll CYRILLIC SMALL LETTER IE WITH GRAVE 0451;A # Ll CYRILLIC SMALL LETTER IO 0452..0481;N # L& [48] CYRILLIC SMALL LETTER DJE..CYRILLIC SMALL LETTER KOPPA 0482;N # So CYRILLIC THOUSANDS SIGN 0483..0487;N # Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE 0488..0489;N # Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN 048A..04FF;N # L& [118] CYRILLIC CAPITAL LETTER SHORT I WITH TAIL..CYRILLIC SMALL LETTER HA WITH STROKE 0500..052F;N # L& [48] CYRILLIC CAPITAL LETTER KOMI DE..CYRILLIC SMALL LETTER EL WITH DESCENDER 0531..0556;N # Lu [38] ARMENIAN CAPITAL LETTER AYB..ARMENIAN CAPITAL LETTER FEH 0559;N # Lm ARMENIAN MODIFIER LETTER LEFT HALF RING 055A..055F;N # Po [6] ARMENIAN APOSTROPHE..ARMENIAN ABBREVIATION MARK 0560..0588;N # Ll [41] ARMENIAN SMALL LETTER TURNED AYB..ARMENIAN SMALL LETTER YI WITH STROKE 0589;N # Po ARMENIAN FULL STOP 058A;N # Pd ARMENIAN HYPHEN 058D..058E;N # So [2] RIGHT-FACING ARMENIAN ETERNITY SIGN..LEFT-FACING ARMENIAN ETERNITY SIGN 058F;N # Sc ARMENIAN DRAM SIGN 0591..05BD;N # Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG 05BE;N # Pd HEBREW PUNCTUATION MAQAF 05BF;N # Mn HEBREW POINT RAFE 05C0;N # Po HEBREW PUNCTUATION PASEQ 05C1..05C2;N # Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT 05C3;N # Po HEBREW PUNCTUATION SOF PASUQ 05C4..05C5;N # Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT 05C6;N # Po HEBREW PUNCTUATION NUN HAFUKHA 05C7;N # Mn HEBREW POINT QAMATS QATAN 05D0..05EA;N # Lo [27] HEBREW LETTER ALEF..HEBREW LETTER TAV 05EF..05F2;N # Lo [4] HEBREW YOD TRIANGLE..HEBREW LIGATURE YIDDISH DOUBLE YOD 05F3..05F4;N # Po [2] HEBREW PUNCTUATION GERESH..HEBREW PUNCTUATION GERSHAYIM 0600..0605;N # Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE 0606..0608;N # Sm [3] ARABIC-INDIC CUBE ROOT..ARABIC RAY 0609..060A;N # Po [2] ARABIC-INDIC PER MILLE SIGN..ARABIC-INDIC PER TEN THOUSAND SIGN 060B;N # Sc AFGHANI SIGN 060C..060D;N # Po [2] ARABIC COMMA..ARABIC DATE SEPARATOR 060E..060F;N # So [2] ARABIC POETIC VERSE SIGN..ARABIC SIGN MISRA 0610..061A;N # Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA 061B;N # Po ARABIC SEMICOLON 061C;N # Cf ARABIC LETTER MARK 061E..061F;N # Po [2] ARABIC TRIPLE DOT PUNCTUATION MARK..ARABIC QUESTION MARK 0620..063F;N # Lo [32] ARABIC LETTER KASHMIRI YEH..ARABIC LETTER FARSI YEH WITH THREE DOTS ABOVE 0640;N # Lm ARABIC TATWEEL 0641..064A;N # Lo [10] ARABIC LETTER FEH..ARABIC LETTER YEH 064B..065F;N # Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW 0660..0669;N # Nd [10] ARABIC-INDIC DIGIT ZERO..ARABIC-INDIC DIGIT NINE 066A..066D;N # Po [4] ARABIC PERCENT SIGN..ARABIC FIVE POINTED STAR 066E..066F;N # Lo [2] ARABIC LETTER DOTLESS BEH..ARABIC LETTER DOTLESS QAF 0670;N # Mn ARABIC LETTER SUPERSCRIPT ALEF 0671..06D3;N # Lo [99] ARABIC LETTER ALEF WASLA..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE 06D4;N # Po ARABIC FULL STOP 06D5;N # Lo ARABIC LETTER AE 06D6..06DC;N # Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN 06DD;N # Cf ARABIC END OF AYAH 06DE;N # So ARABIC START OF RUB EL HIZB 06DF..06E4;N # Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA 06E5..06E6;N # Lm [2] ARABIC SMALL WAW..ARABIC SMALL YEH 06E7..06E8;N # Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON 06E9;N # So ARABIC PLACE OF SAJDAH 06EA..06ED;N # Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM 06EE..06EF;N # Lo [2] ARABIC LETTER DAL WITH INVERTED V..ARABIC LETTER REH WITH INVERTED V 06F0..06F9;N # Nd [10] EXTENDED ARABIC-INDIC DIGIT ZERO..EXTENDED ARABIC-INDIC DIGIT NINE 06FA..06FC;N # Lo [3] ARABIC LETTER SHEEN WITH DOT BELOW..ARABIC LETTER GHAIN WITH DOT BELOW 06FD..06FE;N # So [2] ARABIC SIGN SINDHI AMPERSAND..ARABIC SIGN SINDHI POSTPOSITION MEN 06FF;N # Lo ARABIC LETTER HEH WITH INVERTED V 0700..070D;N # Po [14] SYRIAC END OF PARAGRAPH..SYRIAC HARKLEAN ASTERISCUS 070F;N # Cf SYRIAC ABBREVIATION MARK 0710;N # Lo SYRIAC LETTER ALAPH 0711;N # Mn SYRIAC LETTER SUPERSCRIPT ALAPH 0712..072F;N # Lo [30] SYRIAC LETTER BETH..SYRIAC LETTER PERSIAN DHALATH 0730..074A;N # Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH 074D..074F;N # Lo [3] SYRIAC LETTER SOGDIAN ZHAIN..SYRIAC LETTER SOGDIAN FE 0750..077F;N # Lo [48] ARABIC LETTER BEH WITH THREE DOTS HORIZONTALLY BELOW..ARABIC LETTER KAF WITH TWO DOTS ABOVE 0780..07A5;N # Lo [38] THAANA LETTER HAA..THAANA LETTER WAAVU 07A6..07B0;N # Mn [11] THAANA ABAFILI..THAANA SUKUN 07B1;N # Lo THAANA LETTER NAA 07C0..07C9;N # Nd [10] NKO DIGIT ZERO..NKO DIGIT NINE 07CA..07EA;N # Lo [33] NKO LETTER A..NKO LETTER JONA RA 07EB..07F3;N # Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE 07F4..07F5;N # Lm [2] NKO HIGH TONE APOSTROPHE..NKO LOW TONE APOSTROPHE 07F6;N # So NKO SYMBOL OO DENNEN 07F7..07F9;N # Po [3] NKO SYMBOL GBAKURUNEN..NKO EXCLAMATION MARK 07FA;N # Lm NKO LAJANYALAN 07FD;N # Mn NKO DANTAYALAN 07FE..07FF;N # Sc [2] NKO DOROME SIGN..NKO TAMAN SIGN 0800..0815;N # Lo [22] SAMARITAN LETTER ALAF..SAMARITAN LETTER TAAF 0816..0819;N # Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH 081A;N # Lm SAMARITAN MODIFIER LETTER EPENTHETIC YUT 081B..0823;N # Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A 0824;N # Lm SAMARITAN MODIFIER LETTER SHORT A 0825..0827;N # Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U 0828;N # Lm SAMARITAN MODIFIER LETTER I 0829..082D;N # Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA 0830..083E;N # Po [15] SAMARITAN PUNCTUATION NEQUDAA..SAMARITAN PUNCTUATION ANNAAU 0840..0858;N # Lo [25] MANDAIC LETTER HALQA..MANDAIC LETTER AIN 0859..085B;N # Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK 085E;N # Po MANDAIC PUNCTUATION 0860..086A;N # Lo [11] SYRIAC LETTER MALAYALAM NGA..SYRIAC LETTER MALAYALAM SSA 08A0..08B4;N # Lo [21] ARABIC LETTER BEH WITH SMALL V BELOW..ARABIC LETTER KAF WITH DOT BELOW 08B6..08BD;N # Lo [8] ARABIC LETTER BEH WITH SMALL MEEM ABOVE..ARABIC LETTER AFRICAN NOON 08D3..08E1;N # Mn [15] ARABIC SMALL LOW WAW..ARABIC SMALL HIGH SIGN SAFHA 08E2;N # Cf ARABIC DISPUTED END OF AYAH 08E3..08FF;N # Mn [29] ARABIC TURNED DAMMA BELOW..ARABIC MARK SIDEWAYS NOON GHUNNA 0900..0902;N # Mn [3] DEVANAGARI SIGN INVERTED CANDRABINDU..DEVANAGARI SIGN ANUSVARA 0903;N # Mc DEVANAGARI SIGN VISARGA 0904..0939;N # Lo [54] DEVANAGARI LETTER SHORT A..DEVANAGARI LETTER HA 093A;N # Mn DEVANAGARI VOWEL SIGN OE 093B;N # Mc DEVANAGARI VOWEL SIGN OOE 093C;N # Mn DEVANAGARI SIGN NUKTA 093D;N # Lo DEVANAGARI SIGN AVAGRAHA 093E..0940;N # Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II 0941..0948;N # Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI 0949..094C;N # Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU 094D;N # Mn DEVANAGARI SIGN VIRAMA 094E..094F;N # Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW 0950;N # Lo DEVANAGARI OM 0951..0957;N # Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE 0958..0961;N # Lo [10] DEVANAGARI LETTER QA..DEVANAGARI LETTER VOCALIC LL 0962..0963;N # Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL 0964..0965;N # Po [2] DEVANAGARI DANDA..DEVANAGARI DOUBLE DANDA 0966..096F;N # Nd [10] DEVANAGARI DIGIT ZERO..DEVANAGARI DIGIT NINE 0970;N # Po DEVANAGARI ABBREVIATION SIGN 0971;N # Lm DEVANAGARI SIGN HIGH SPACING DOT 0972..097F;N # Lo [14] DEVANAGARI LETTER CANDRA A..DEVANAGARI LETTER BBA 0980;N # Lo BENGALI ANJI 0981;N # Mn BENGALI SIGN CANDRABINDU 0982..0983;N # Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA 0985..098C;N # Lo [8] BENGALI LETTER A..BENGALI LETTER VOCALIC L 098F..0990;N # Lo [2] BENGALI LETTER E..BENGALI LETTER AI 0993..09A8;N # Lo [22] BENGALI LETTER O..BENGALI LETTER NA 09AA..09B0;N # Lo [7] BENGALI LETTER PA..BENGALI LETTER RA 09B2;N # Lo BENGALI LETTER LA 09B6..09B9;N # Lo [4] BENGALI LETTER SHA..BENGALI LETTER HA 09BC;N # Mn BENGALI SIGN NUKTA 09BD;N # Lo BENGALI SIGN AVAGRAHA 09BE..09C0;N # Mc [3] BENGALI VOWEL SIGN AA..BENGALI VOWEL SIGN II 09C1..09C4;N # Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR 09C7..09C8;N # Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI 09CB..09CC;N # Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU 09CD;N # Mn BENGALI SIGN VIRAMA 09CE;N # Lo BENGALI LETTER KHANDA TA 09D7;N # Mc BENGALI AU LENGTH MARK 09DC..09DD;N # Lo [2] BENGALI LETTER RRA..BENGALI LETTER RHA 09DF..09E1;N # Lo [3] BENGALI LETTER YYA..BENGALI LETTER VOCALIC LL 09E2..09E3;N # Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL 09E6..09EF;N # Nd [10] BENGALI DIGIT ZERO..BENGALI DIGIT NINE 09F0..09F1;N # Lo [2] BENGALI LETTER RA WITH MIDDLE DIAGONAL..BENGALI LETTER RA WITH LOWER DIAGONAL 09F2..09F3;N # Sc [2] BENGALI RUPEE MARK..BENGALI RUPEE SIGN 09F4..09F9;N # No [6] BENGALI CURRENCY NUMERATOR ONE..BENGALI CURRENCY DENOMINATOR SIXTEEN 09FA;N # So BENGALI ISSHAR 09FB;N # Sc BENGALI GANDA MARK 09FC;N # Lo BENGALI LETTER VEDIC ANUSVARA 09FD;N # Po BENGALI ABBREVIATION SIGN 09FE;N # Mn BENGALI SANDHI MARK 0A01..0A02;N # Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI 0A03;N # Mc GURMUKHI SIGN VISARGA 0A05..0A0A;N # Lo [6] GURMUKHI LETTER A..GURMUKHI LETTER UU 0A0F..0A10;N # Lo [2] GURMUKHI LETTER EE..GURMUKHI LETTER AI 0A13..0A28;N # Lo [22] GURMUKHI LETTER OO..GURMUKHI LETTER NA 0A2A..0A30;N # Lo [7] GURMUKHI LETTER PA..GURMUKHI LETTER RA 0A32..0A33;N # Lo [2] GURMUKHI LETTER LA..GURMUKHI LETTER LLA 0A35..0A36;N # Lo [2] GURMUKHI LETTER VA..GURMUKHI LETTER SHA 0A38..0A39;N # Lo [2] GURMUKHI LETTER SA..GURMUKHI LETTER HA 0A3C;N # Mn GURMUKHI SIGN NUKTA 0A3E..0A40;N # Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II 0A41..0A42;N # Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU 0A47..0A48;N # Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI 0A4B..0A4D;N # Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA 0A51;N # Mn GURMUKHI SIGN UDAAT 0A59..0A5C;N # Lo [4] GURMUKHI LETTER KHHA..GURMUKHI LETTER RRA 0A5E;N # Lo GURMUKHI LETTER FA 0A66..0A6F;N # Nd [10] GURMUKHI DIGIT ZERO..GURMUKHI DIGIT NINE 0A70..0A71;N # Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK 0A72..0A74;N # Lo [3] GURMUKHI IRI..GURMUKHI EK ONKAR 0A75;N # Mn GURMUKHI SIGN YAKASH 0A76;N # Po GURMUKHI ABBREVIATION SIGN 0A81..0A82;N # Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA 0A83;N # Mc GUJARATI SIGN VISARGA 0A85..0A8D;N # Lo [9] GUJARATI LETTER A..GUJARATI VOWEL CANDRA E 0A8F..0A91;N # Lo [3] GUJARATI LETTER E..GUJARATI VOWEL CANDRA O 0A93..0AA8;N # Lo [22] GUJARATI LETTER O..GUJARATI LETTER NA 0AAA..0AB0;N # Lo [7] GUJARATI LETTER PA..GUJARATI LETTER RA 0AB2..0AB3;N # Lo [2] GUJARATI LETTER LA..GUJARATI LETTER LLA 0AB5..0AB9;N # Lo [5] GUJARATI LETTER VA..GUJARATI LETTER HA 0ABC;N # Mn GUJARATI SIGN NUKTA 0ABD;N # Lo GUJARATI SIGN AVAGRAHA 0ABE..0AC0;N # Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II 0AC1..0AC5;N # Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E 0AC7..0AC8;N # Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI 0AC9;N # Mc GUJARATI VOWEL SIGN CANDRA O 0ACB..0ACC;N # Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU 0ACD;N # Mn GUJARATI SIGN VIRAMA 0AD0;N # Lo GUJARATI OM 0AE0..0AE1;N # Lo [2] GUJARATI LETTER VOCALIC RR..GUJARATI LETTER VOCALIC LL 0AE2..0AE3;N # Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL 0AE6..0AEF;N # Nd [10] GUJARATI DIGIT ZERO..GUJARATI DIGIT NINE 0AF0;N # Po GUJARATI ABBREVIATION SIGN 0AF1;N # Sc GUJARATI RUPEE SIGN 0AF9;N # Lo GUJARATI LETTER ZHA 0AFA..0AFF;N # Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE 0B01;N # Mn ORIYA SIGN CANDRABINDU 0B02..0B03;N # Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA 0B05..0B0C;N # Lo [8] ORIYA LETTER A..ORIYA LETTER VOCALIC L 0B0F..0B10;N # Lo [2] ORIYA LETTER E..ORIYA LETTER AI 0B13..0B28;N # Lo [22] ORIYA LETTER O..ORIYA LETTER NA 0B2A..0B30;N # Lo [7] ORIYA LETTER PA..ORIYA LETTER RA 0B32..0B33;N # Lo [2] ORIYA LETTER LA..ORIYA LETTER LLA 0B35..0B39;N # Lo [5] ORIYA LETTER VA..ORIYA LETTER HA 0B3C;N # Mn ORIYA SIGN NUKTA 0B3D;N # Lo ORIYA SIGN AVAGRAHA 0B3E;N # Mc ORIYA VOWEL SIGN AA 0B3F;N # Mn ORIYA VOWEL SIGN I 0B40;N # Mc ORIYA VOWEL SIGN II 0B41..0B44;N # Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR 0B47..0B48;N # Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI 0B4B..0B4C;N # Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU 0B4D;N # Mn ORIYA SIGN VIRAMA 0B56;N # Mn ORIYA AI LENGTH MARK 0B57;N # Mc ORIYA AU LENGTH MARK 0B5C..0B5D;N # Lo [2] ORIYA LETTER RRA..ORIYA LETTER RHA 0B5F..0B61;N # Lo [3] ORIYA LETTER YYA..ORIYA LETTER VOCALIC LL 0B62..0B63;N # Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL 0B66..0B6F;N # Nd [10] ORIYA DIGIT ZERO..ORIYA DIGIT NINE 0B70;N # So ORIYA ISSHAR 0B71;N # Lo ORIYA LETTER WA 0B72..0B77;N # No [6] ORIYA FRACTION ONE QUARTER..ORIYA FRACTION THREE SIXTEENTHS 0B82;N # Mn TAMIL SIGN ANUSVARA 0B83;N # Lo TAMIL SIGN VISARGA 0B85..0B8A;N # Lo [6] TAMIL LETTER A..TAMIL LETTER UU 0B8E..0B90;N # Lo [3] TAMIL LETTER E..TAMIL LETTER AI 0B92..0B95;N # Lo [4] TAMIL LETTER O..TAMIL LETTER KA 0B99..0B9A;N # Lo [2] TAMIL LETTER NGA..TAMIL LETTER CA 0B9C;N # Lo TAMIL LETTER JA 0B9E..0B9F;N # Lo [2] TAMIL LETTER NYA..TAMIL LETTER TTA 0BA3..0BA4;N # Lo [2] TAMIL LETTER NNA..TAMIL LETTER TA 0BA8..0BAA;N # Lo [3] TAMIL LETTER NA..TAMIL LETTER PA 0BAE..0BB9;N # Lo [12] TAMIL LETTER MA..TAMIL LETTER HA 0BBE..0BBF;N # Mc [2] TAMIL VOWEL SIGN AA..TAMIL VOWEL SIGN I 0BC0;N # Mn TAMIL VOWEL SIGN II 0BC1..0BC2;N # Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU 0BC6..0BC8;N # Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI 0BCA..0BCC;N # Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU 0BCD;N # Mn TAMIL SIGN VIRAMA 0BD0;N # Lo TAMIL OM 0BD7;N # Mc TAMIL AU LENGTH MARK 0BE6..0BEF;N # Nd [10] TAMIL DIGIT ZERO..TAMIL DIGIT NINE 0BF0..0BF2;N # No [3] TAMIL NUMBER TEN..TAMIL NUMBER ONE THOUSAND 0BF3..0BF8;N # So [6] TAMIL DAY SIGN..TAMIL AS ABOVE SIGN 0BF9;N # Sc TAMIL RUPEE SIGN 0BFA;N # So TAMIL NUMBER SIGN 0C00;N # Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE 0C01..0C03;N # Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA 0C04;N # Mn TELUGU SIGN COMBINING ANUSVARA ABOVE 0C05..0C0C;N # Lo [8] TELUGU LETTER A..TELUGU LETTER VOCALIC L 0C0E..0C10;N # Lo [3] TELUGU LETTER E..TELUGU LETTER AI 0C12..0C28;N # Lo [23] TELUGU LETTER O..TELUGU LETTER NA 0C2A..0C39;N # Lo [16] TELUGU LETTER PA..TELUGU LETTER HA 0C3D;N # Lo TELUGU SIGN AVAGRAHA 0C3E..0C40;N # Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II 0C41..0C44;N # Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR 0C46..0C48;N # Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI 0C4A..0C4D;N # Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA 0C55..0C56;N # Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK 0C58..0C5A;N # Lo [3] TELUGU LETTER TSA..TELUGU LETTER RRRA 0C60..0C61;N # Lo [2] TELUGU LETTER VOCALIC RR..TELUGU LETTER VOCALIC LL 0C62..0C63;N # Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL 0C66..0C6F;N # Nd [10] TELUGU DIGIT ZERO..TELUGU DIGIT NINE 0C78..0C7E;N # No [7] TELUGU FRACTION DIGIT ZERO FOR ODD POWERS OF FOUR..TELUGU FRACTION DIGIT THREE FOR EVEN POWERS OF FOUR 0C7F;N # So TELUGU SIGN TUUMU 0C80;N # Lo KANNADA SIGN SPACING CANDRABINDU 0C81;N # Mn KANNADA SIGN CANDRABINDU 0C82..0C83;N # Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA 0C84;N # Po KANNADA SIGN SIDDHAM 0C85..0C8C;N # Lo [8] KANNADA LETTER A..KANNADA LETTER VOCALIC L 0C8E..0C90;N # Lo [3] KANNADA LETTER E..KANNADA LETTER AI 0C92..0CA8;N # Lo [23] KANNADA LETTER O..KANNADA LETTER NA 0CAA..0CB3;N # Lo [10] KANNADA LETTER PA..KANNADA LETTER LLA 0CB5..0CB9;N # Lo [5] KANNADA LETTER VA..KANNADA LETTER HA 0CBC;N # Mn KANNADA SIGN NUKTA 0CBD;N # Lo KANNADA SIGN AVAGRAHA 0CBE;N # Mc KANNADA VOWEL SIGN AA 0CBF;N # Mn KANNADA VOWEL SIGN I 0CC0..0CC4;N # Mc [5] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN VOCALIC RR 0CC6;N # Mn KANNADA VOWEL SIGN E 0CC7..0CC8;N # Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI 0CCA..0CCB;N # Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO 0CCC..0CCD;N # Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA 0CD5..0CD6;N # Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK 0CDE;N # Lo KANNADA LETTER FA 0CE0..0CE1;N # Lo [2] KANNADA LETTER VOCALIC RR..KANNADA LETTER VOCALIC LL 0CE2..0CE3;N # Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL 0CE6..0CEF;N # Nd [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE 0CF1..0CF2;N # Lo [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN UPADHMANIYA 0D00..0D01;N # Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU 0D02..0D03;N # Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA 0D05..0D0C;N # Lo [8] MALAYALAM LETTER A..MALAYALAM LETTER VOCALIC L 0D0E..0D10;N # Lo [3] MALAYALAM LETTER E..MALAYALAM LETTER AI 0D12..0D3A;N # Lo [41] MALAYALAM LETTER O..MALAYALAM LETTER TTTA 0D3B..0D3C;N # Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA 0D3D;N # Lo MALAYALAM SIGN AVAGRAHA 0D3E..0D40;N # Mc [3] MALAYALAM VOWEL SIGN AA..MALAYALAM VOWEL SIGN II 0D41..0D44;N # Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR 0D46..0D48;N # Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI 0D4A..0D4C;N # Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU 0D4D;N # Mn MALAYALAM SIGN VIRAMA 0D4E;N # Lo MALAYALAM LETTER DOT REPH 0D4F;N # So MALAYALAM SIGN PARA 0D54..0D56;N # Lo [3] MALAYALAM LETTER CHILLU M..MALAYALAM LETTER CHILLU LLL 0D57;N # Mc MALAYALAM AU LENGTH MARK 0D58..0D5E;N # No [7] MALAYALAM FRACTION ONE ONE-HUNDRED-AND-SIXTIETH..MALAYALAM FRACTION ONE FIFTH 0D5F..0D61;N # Lo [3] MALAYALAM LETTER ARCHAIC II..MALAYALAM LETTER VOCALIC LL 0D62..0D63;N # Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL 0D66..0D6F;N # Nd [10] MALAYALAM DIGIT ZERO..MALAYALAM DIGIT NINE 0D70..0D78;N # No [9] MALAYALAM NUMBER TEN..MALAYALAM FRACTION THREE SIXTEENTHS 0D79;N # So MALAYALAM DATE MARK 0D7A..0D7F;N # Lo [6] MALAYALAM LETTER CHILLU NN..MALAYALAM LETTER CHILLU K 0D82..0D83;N # Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA 0D85..0D96;N # Lo [18] SINHALA LETTER AYANNA..SINHALA LETTER AUYANNA 0D9A..0DB1;N # Lo [24] SINHALA LETTER ALPAPRAANA KAYANNA..SINHALA LETTER DANTAJA NAYANNA 0DB3..0DBB;N # Lo [9] SINHALA LETTER SANYAKA DAYANNA..SINHALA LETTER RAYANNA 0DBD;N # Lo SINHALA LETTER DANTAJA LAYANNA 0DC0..0DC6;N # Lo [7] SINHALA LETTER VAYANNA..SINHALA LETTER FAYANNA 0DCA;N # Mn SINHALA SIGN AL-LAKUNA 0DCF..0DD1;N # Mc [3] SINHALA VOWEL SIGN AELA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA 0DD2..0DD4;N # Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA 0DD6;N # Mn SINHALA VOWEL SIGN DIGA PAA-PILLA 0DD8..0DDF;N # Mc [8] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN GAYANUKITTA 0DE6..0DEF;N # Nd [10] SINHALA LITH DIGIT ZERO..SINHALA LITH DIGIT NINE 0DF2..0DF3;N # Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA 0DF4;N # Po SINHALA PUNCTUATION KUNDDALIYA 0E01..0E30;N # Lo [48] THAI CHARACTER KO KAI..THAI CHARACTER SARA A 0E31;N # Mn THAI CHARACTER MAI HAN-AKAT 0E32..0E33;N # Lo [2] THAI CHARACTER SARA AA..THAI CHARACTER SARA AM 0E34..0E3A;N # Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU 0E3F;N # Sc THAI CURRENCY SYMBOL BAHT 0E40..0E45;N # Lo [6] THAI CHARACTER SARA E..THAI CHARACTER LAKKHANGYAO 0E46;N # Lm THAI CHARACTER MAIYAMOK 0E47..0E4E;N # Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN 0E4F;N # Po THAI CHARACTER FONGMAN 0E50..0E59;N # Nd [10] THAI DIGIT ZERO..THAI DIGIT NINE 0E5A..0E5B;N # Po [2] THAI CHARACTER ANGKHANKHU..THAI CHARACTER KHOMUT 0E81..0E82;N # Lo [2] LAO LETTER KO..LAO LETTER KHO SUNG 0E84;N # Lo LAO LETTER KHO TAM 0E87..0E88;N # Lo [2] LAO LETTER NGO..LAO LETTER CO 0E8A;N # Lo LAO LETTER SO TAM 0E8D;N # Lo LAO LETTER NYO 0E94..0E97;N # Lo [4] LAO LETTER DO..LAO LETTER THO TAM 0E99..0E9F;N # Lo [7] LAO LETTER NO..LAO LETTER FO SUNG 0EA1..0EA3;N # Lo [3] LAO LETTER MO..LAO LETTER LO LING 0EA5;N # Lo LAO LETTER LO LOOT 0EA7;N # Lo LAO LETTER WO 0EAA..0EAB;N # Lo [2] LAO LETTER SO SUNG..LAO LETTER HO SUNG 0EAD..0EB0;N # Lo [4] LAO LETTER O..LAO VOWEL SIGN A 0EB1;N # Mn LAO VOWEL SIGN MAI KAN 0EB2..0EB3;N # Lo [2] LAO VOWEL SIGN AA..LAO VOWEL SIGN AM 0EB4..0EB9;N # Mn [6] LAO VOWEL SIGN I..LAO VOWEL SIGN UU 0EBB..0EBC;N # Mn [2] LAO VOWEL SIGN MAI KON..LAO SEMIVOWEL SIGN LO 0EBD;N # Lo LAO SEMIVOWEL SIGN NYO 0EC0..0EC4;N # Lo [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI 0EC6;N # Lm LAO KO LA 0EC8..0ECD;N # Mn [6] LAO TONE MAI EK..LAO NIGGAHITA 0ED0..0ED9;N # Nd [10] LAO DIGIT ZERO..LAO DIGIT NINE 0EDC..0EDF;N # Lo [4] LAO HO NO..LAO LETTER KHMU NYO 0F00;N # Lo TIBETAN SYLLABLE OM 0F01..0F03;N # So [3] TIBETAN MARK GTER YIG MGO TRUNCATED A..TIBETAN MARK GTER YIG MGO -UM GTER TSHEG MA 0F04..0F12;N # Po [15] TIBETAN MARK INITIAL YIG MGO MDUN MA..TIBETAN MARK RGYA GRAM SHAD 0F13;N # So TIBETAN MARK CARET -DZUD RTAGS ME LONG CAN 0F14;N # Po TIBETAN MARK GTER TSHEG 0F15..0F17;N # So [3] TIBETAN LOGOTYPE SIGN CHAD RTAGS..TIBETAN ASTROLOGICAL SIGN SGRA GCAN -CHAR RTAGS 0F18..0F19;N # Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS 0F1A..0F1F;N # So [6] TIBETAN SIGN RDEL DKAR GCIG..TIBETAN SIGN RDEL DKAR RDEL NAG 0F20..0F29;N # Nd [10] TIBETAN DIGIT ZERO..TIBETAN DIGIT NINE 0F2A..0F33;N # No [10] TIBETAN DIGIT HALF ONE..TIBETAN DIGIT HALF ZERO 0F34;N # So TIBETAN MARK BSDUS RTAGS 0F35;N # Mn TIBETAN MARK NGAS BZUNG NYI ZLA 0F36;N # So TIBETAN MARK CARET -DZUD RTAGS BZHI MIG CAN 0F37;N # Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS 0F38;N # So TIBETAN MARK CHE MGO 0F39;N # Mn TIBETAN MARK TSA -PHRU 0F3A;N # Ps TIBETAN MARK GUG RTAGS GYON 0F3B;N # Pe TIBETAN MARK GUG RTAGS GYAS 0F3C;N # Ps TIBETAN MARK ANG KHANG GYON 0F3D;N # Pe TIBETAN MARK ANG KHANG GYAS 0F3E..0F3F;N # Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES 0F40..0F47;N # Lo [8] TIBETAN LETTER KA..TIBETAN LETTER JA 0F49..0F6C;N # Lo [36] TIBETAN LETTER NYA..TIBETAN LETTER RRA 0F71..0F7E;N # Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO 0F7F;N # Mc TIBETAN SIGN RNAM BCAD 0F80..0F84;N # Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA 0F85;N # Po TIBETAN MARK PALUTA 0F86..0F87;N # Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS 0F88..0F8C;N # Lo [5] TIBETAN SIGN LCE TSA CAN..TIBETAN SIGN INVERTED MCHU CAN 0F8D..0F97;N # Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA 0F99..0FBC;N # Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA 0FBE..0FC5;N # So [8] TIBETAN KU RU KHA..TIBETAN SYMBOL RDO RJE 0FC6;N # Mn TIBETAN SYMBOL PADMA GDAN 0FC7..0FCC;N # So [6] TIBETAN SYMBOL RDO RJE RGYA GRAM..TIBETAN SYMBOL NOR BU BZHI -KHYIL 0FCE..0FCF;N # So [2] TIBETAN SIGN RDEL NAG RDEL DKAR..TIBETAN SIGN RDEL NAG GSUM 0FD0..0FD4;N # Po [5] TIBETAN MARK BSKA- SHOG GI MGO RGYAN..TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA 0FD5..0FD8;N # So [4] RIGHT-FACING SVASTI SIGN..LEFT-FACING SVASTI SIGN WITH DOTS 0FD9..0FDA;N # Po [2] TIBETAN MARK LEADING MCHAN RTAGS..TIBETAN MARK TRAILING MCHAN RTAGS 1000..102A;N # Lo [43] MYANMAR LETTER KA..MYANMAR LETTER AU 102B..102C;N # Mc [2] MYANMAR VOWEL SIGN TALL AA..MYANMAR VOWEL SIGN AA 102D..1030;N # Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU 1031;N # Mc MYANMAR VOWEL SIGN E 1032..1037;N # Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW 1038;N # Mc MYANMAR SIGN VISARGA 1039..103A;N # Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT 103B..103C;N # Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA 103D..103E;N # Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA 103F;N # Lo MYANMAR LETTER GREAT SA 1040..1049;N # Nd [10] MYANMAR DIGIT ZERO..MYANMAR DIGIT NINE 104A..104F;N # Po [6] MYANMAR SIGN LITTLE SECTION..MYANMAR SYMBOL GENITIVE 1050..1055;N # Lo [6] MYANMAR LETTER SHA..MYANMAR LETTER VOCALIC LL 1056..1057;N # Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR 1058..1059;N # Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL 105A..105D;N # Lo [4] MYANMAR LETTER MON NGA..MYANMAR LETTER MON BBE 105E..1060;N # Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA 1061;N # Lo MYANMAR LETTER SGAW KAREN SHA 1062..1064;N # Mc [3] MYANMAR VOWEL SIGN SGAW KAREN EU..MYANMAR TONE MARK SGAW KAREN KE PHO 1065..1066;N # Lo [2] MYANMAR LETTER WESTERN PWO KAREN THA..MYANMAR LETTER WESTERN PWO KAREN PWA 1067..106D;N # Mc [7] MYANMAR VOWEL SIGN WESTERN PWO KAREN EU..MYANMAR SIGN WESTERN PWO KAREN TONE-5 106E..1070;N # Lo [3] MYANMAR LETTER EASTERN PWO KAREN NNA..MYANMAR LETTER EASTERN PWO KAREN GHWA 1071..1074;N # Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE 1075..1081;N # Lo [13] MYANMAR LETTER SHAN KA..MYANMAR LETTER SHAN HA 1082;N # Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA 1083..1084;N # Mc [2] MYANMAR VOWEL SIGN SHAN AA..MYANMAR VOWEL SIGN SHAN E 1085..1086;N # Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y 1087..108C;N # Mc [6] MYANMAR SIGN SHAN TONE-2..MYANMAR SIGN SHAN COUNCIL TONE-3 108D;N # Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE 108E;N # Lo MYANMAR LETTER RUMAI PALAUNG FA 108F;N # Mc MYANMAR SIGN RUMAI PALAUNG TONE-5 1090..1099;N # Nd [10] MYANMAR SHAN DIGIT ZERO..MYANMAR SHAN DIGIT NINE 109A..109C;N # Mc [3] MYANMAR SIGN KHAMTI TONE-1..MYANMAR VOWEL SIGN AITON A 109D;N # Mn MYANMAR VOWEL SIGN AITON AI 109E..109F;N # So [2] MYANMAR SYMBOL SHAN ONE..MYANMAR SYMBOL SHAN EXCLAMATION 10A0..10C5;N # Lu [38] GEORGIAN CAPITAL LETTER AN..GEORGIAN CAPITAL LETTER HOE 10C7;N # Lu GEORGIAN CAPITAL LETTER YN 10CD;N # Lu GEORGIAN CAPITAL LETTER AEN 10D0..10FA;N # Ll [43] GEORGIAN LETTER AN..GEORGIAN LETTER AIN 10FB;N # Po GEORGIAN PARAGRAPH SEPARATOR 10FC;N # Lm MODIFIER LETTER GEORGIAN NAR 10FD..10FF;N # Ll [3] GEORGIAN LETTER AEN..GEORGIAN LETTER LABIAL SIGN 1100..115F;W # Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER 1160..11FF;N # Lo [160] HANGUL JUNGSEONG FILLER..HANGUL JONGSEONG SSANGNIEUN 1200..1248;N # Lo [73] ETHIOPIC SYLLABLE HA..ETHIOPIC SYLLABLE QWA 124A..124D;N # Lo [4] ETHIOPIC SYLLABLE QWI..ETHIOPIC SYLLABLE QWE 1250..1256;N # Lo [7] ETHIOPIC SYLLABLE QHA..ETHIOPIC SYLLABLE QHO 1258;N # Lo ETHIOPIC SYLLABLE QHWA 125A..125D;N # Lo [4] ETHIOPIC SYLLABLE QHWI..ETHIOPIC SYLLABLE QHWE 1260..1288;N # Lo [41] ETHIOPIC SYLLABLE BA..ETHIOPIC SYLLABLE XWA 128A..128D;N # Lo [4] ETHIOPIC SYLLABLE XWI..ETHIOPIC SYLLABLE XWE 1290..12B0;N # Lo [33] ETHIOPIC SYLLABLE NA..ETHIOPIC SYLLABLE KWA 12B2..12B5;N # Lo [4] ETHIOPIC SYLLABLE KWI..ETHIOPIC SYLLABLE KWE 12B8..12BE;N # Lo [7] ETHIOPIC SYLLABLE KXA..ETHIOPIC SYLLABLE KXO 12C0;N # Lo ETHIOPIC SYLLABLE KXWA 12C2..12C5;N # Lo [4] ETHIOPIC SYLLABLE KXWI..ETHIOPIC SYLLABLE KXWE 12C8..12D6;N # Lo [15] ETHIOPIC SYLLABLE WA..ETHIOPIC SYLLABLE PHARYNGEAL O 12D8..1310;N # Lo [57] ETHIOPIC SYLLABLE ZA..ETHIOPIC SYLLABLE GWA 1312..1315;N # Lo [4] ETHIOPIC SYLLABLE GWI..ETHIOPIC SYLLABLE GWE 1318..135A;N # Lo [67] ETHIOPIC SYLLABLE GGA..ETHIOPIC SYLLABLE FYA 135D..135F;N # Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK 1360..1368;N # Po [9] ETHIOPIC SECTION MARK..ETHIOPIC PARAGRAPH SEPARATOR 1369..137C;N # No [20] ETHIOPIC DIGIT ONE..ETHIOPIC NUMBER TEN THOUSAND 1380..138F;N # Lo [16] ETHIOPIC SYLLABLE SEBATBEIT MWA..ETHIOPIC SYLLABLE PWE 1390..1399;N # So [10] ETHIOPIC TONAL MARK YIZET..ETHIOPIC TONAL MARK KURT 13A0..13F5;N # Lu [86] CHEROKEE LETTER A..CHEROKEE LETTER MV 13F8..13FD;N # Ll [6] CHEROKEE SMALL LETTER YE..CHEROKEE SMALL LETTER MV 1400;N # Pd CANADIAN SYLLABICS HYPHEN 1401..166C;N # Lo [620] CANADIAN SYLLABICS E..CANADIAN SYLLABICS CARRIER TTSA 166D..166E;N # Po [2] CANADIAN SYLLABICS CHI SIGN..CANADIAN SYLLABICS FULL STOP 166F..167F;N # Lo [17] CANADIAN SYLLABICS QAI..CANADIAN SYLLABICS BLACKFOOT W 1680;N # Zs OGHAM SPACE MARK 1681..169A;N # Lo [26] OGHAM LETTER BEITH..OGHAM LETTER PEITH 169B;N # Ps OGHAM FEATHER MARK 169C;N # Pe OGHAM REVERSED FEATHER MARK 16A0..16EA;N # Lo [75] RUNIC LETTER FEHU FEOH FE F..RUNIC LETTER X 16EB..16ED;N # Po [3] RUNIC SINGLE PUNCTUATION..RUNIC CROSS PUNCTUATION 16EE..16F0;N # Nl [3] RUNIC ARLAUG SYMBOL..RUNIC BELGTHOR SYMBOL 16F1..16F8;N # Lo [8] RUNIC LETTER K..RUNIC LETTER FRANKS CASKET AESC 1700..170C;N # Lo [13] TAGALOG LETTER A..TAGALOG LETTER YA 170E..1711;N # Lo [4] TAGALOG LETTER LA..TAGALOG LETTER HA 1712..1714;N # Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA 1720..1731;N # Lo [18] HANUNOO LETTER A..HANUNOO LETTER HA 1732..1734;N # Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD 1735..1736;N # Po [2] PHILIPPINE SINGLE PUNCTUATION..PHILIPPINE DOUBLE PUNCTUATION 1740..1751;N # Lo [18] BUHID LETTER A..BUHID LETTER HA 1752..1753;N # Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U 1760..176C;N # Lo [13] TAGBANWA LETTER A..TAGBANWA LETTER YA 176E..1770;N # Lo [3] TAGBANWA LETTER LA..TAGBANWA LETTER SA 1772..1773;N # Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U 1780..17B3;N # Lo [52] KHMER LETTER KA..KHMER INDEPENDENT VOWEL QAU 17B4..17B5;N # Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA 17B6;N # Mc KHMER VOWEL SIGN AA 17B7..17BD;N # Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA 17BE..17C5;N # Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU 17C6;N # Mn KHMER SIGN NIKAHIT 17C7..17C8;N # Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU 17C9..17D3;N # Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT 17D4..17D6;N # Po [3] KHMER SIGN KHAN..KHMER SIGN CAMNUC PII KUUH 17D7;N # Lm KHMER SIGN LEK TOO 17D8..17DA;N # Po [3] KHMER SIGN BEYYAL..KHMER SIGN KOOMUUT 17DB;N # Sc KHMER CURRENCY SYMBOL RIEL 17DC;N # Lo KHMER SIGN AVAKRAHASANYA 17DD;N # Mn KHMER SIGN ATTHACAN 17E0..17E9;N # Nd [10] KHMER DIGIT ZERO..KHMER DIGIT NINE 17F0..17F9;N # No [10] KHMER SYMBOL LEK ATTAK SON..KHMER SYMBOL LEK ATTAK PRAM-BUON 1800..1805;N # Po [6] MONGOLIAN BIRGA..MONGOLIAN FOUR DOTS 1806;N # Pd MONGOLIAN TODO SOFT HYPHEN 1807..180A;N # Po [4] MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER..MONGOLIAN NIRUGU 180B..180D;N # Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE 180E;N # Cf MONGOLIAN VOWEL SEPARATOR 1810..1819;N # Nd [10] MONGOLIAN DIGIT ZERO..MONGOLIAN DIGIT NINE 1820..1842;N # Lo [35] MONGOLIAN LETTER A..MONGOLIAN LETTER CHI 1843;N # Lm MONGOLIAN LETTER TODO LONG VOWEL SIGN 1844..1878;N # Lo [53] MONGOLIAN LETTER TODO E..MONGOLIAN LETTER CHA WITH TWO DOTS 1880..1884;N # Lo [5] MONGOLIAN LETTER ALI GALI ANUSVARA ONE..MONGOLIAN LETTER ALI GALI INVERTED UBADAMA 1885..1886;N # Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA 1887..18A8;N # Lo [34] MONGOLIAN LETTER ALI GALI A..MONGOLIAN LETTER MANCHU ALI GALI BHA 18A9;N # Mn MONGOLIAN LETTER ALI GALI DAGALGA 18AA;N # Lo MONGOLIAN LETTER MANCHU ALI GALI LHA 18B0..18F5;N # Lo [70] CANADIAN SYLLABICS OY..CANADIAN SYLLABICS CARRIER DENTAL S 1900..191E;N # Lo [31] LIMBU VOWEL-CARRIER LETTER..LIMBU LETTER TRA 1920..1922;N # Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U 1923..1926;N # Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU 1927..1928;N # Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O 1929..192B;N # Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA 1930..1931;N # Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA 1932;N # Mn LIMBU SMALL LETTER ANUSVARA 1933..1938;N # Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA 1939..193B;N # Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I 1940;N # So LIMBU SIGN LOO 1944..1945;N # Po [2] LIMBU EXCLAMATION MARK..LIMBU QUESTION MARK 1946..194F;N # Nd [10] LIMBU DIGIT ZERO..LIMBU DIGIT NINE 1950..196D;N # Lo [30] TAI LE LETTER KA..TAI LE LETTER AI 1970..1974;N # Lo [5] TAI LE LETTER TONE-2..TAI LE LETTER TONE-6 1980..19AB;N # Lo [44] NEW TAI LUE LETTER HIGH QA..NEW TAI LUE LETTER LOW SUA 19B0..19C9;N # Lo [26] NEW TAI LUE VOWEL SIGN VOWEL SHORTENER..NEW TAI LUE TONE MARK-2 19D0..19D9;N # Nd [10] NEW TAI LUE DIGIT ZERO..NEW TAI LUE DIGIT NINE 19DA;N # No NEW TAI LUE THAM DIGIT ONE 19DE..19DF;N # So [2] NEW TAI LUE SIGN LAE..NEW TAI LUE SIGN LAEV 19E0..19FF;N # So [32] KHMER SYMBOL PATHAMASAT..KHMER SYMBOL DAP-PRAM ROC 1A00..1A16;N # Lo [23] BUGINESE LETTER KA..BUGINESE LETTER HA 1A17..1A18;N # Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U 1A19..1A1A;N # Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O 1A1B;N # Mn BUGINESE VOWEL SIGN AE 1A1E..1A1F;N # Po [2] BUGINESE PALLAWA..BUGINESE END OF SECTION 1A20..1A54;N # Lo [53] TAI THAM LETTER HIGH KA..TAI THAM LETTER GREAT SA 1A55;N # Mc TAI THAM CONSONANT SIGN MEDIAL RA 1A56;N # Mn TAI THAM CONSONANT SIGN MEDIAL LA 1A57;N # Mc TAI THAM CONSONANT SIGN LA TANG LAI 1A58..1A5E;N # Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA 1A60;N # Mn TAI THAM SIGN SAKOT 1A61;N # Mc TAI THAM VOWEL SIGN A 1A62;N # Mn TAI THAM VOWEL SIGN MAI SAT 1A63..1A64;N # Mc [2] TAI THAM VOWEL SIGN AA..TAI THAM VOWEL SIGN TALL AA 1A65..1A6C;N # Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW 1A6D..1A72;N # Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI 1A73..1A7C;N # Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN 1A7F;N # Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT 1A80..1A89;N # Nd [10] TAI THAM HORA DIGIT ZERO..TAI THAM HORA DIGIT NINE 1A90..1A99;N # Nd [10] TAI THAM THAM DIGIT ZERO..TAI THAM THAM DIGIT NINE 1AA0..1AA6;N # Po [7] TAI THAM SIGN WIANG..TAI THAM SIGN REVERSED ROTATED RANA 1AA7;N # Lm TAI THAM SIGN MAI YAMOK 1AA8..1AAD;N # Po [6] TAI THAM SIGN KAAN..TAI THAM SIGN CAANG 1AB0..1ABD;N # Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW 1ABE;N # Me COMBINING PARENTHESES OVERLAY 1B00..1B03;N # Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG 1B04;N # Mc BALINESE SIGN BISAH 1B05..1B33;N # Lo [47] BALINESE LETTER AKARA..BALINESE LETTER HA 1B34;N # Mn BALINESE SIGN REREKAN 1B35;N # Mc BALINESE VOWEL SIGN TEDUNG 1B36..1B3A;N # Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA 1B3B;N # Mc BALINESE VOWEL SIGN RA REPA TEDUNG 1B3C;N # Mn BALINESE VOWEL SIGN LA LENGA 1B3D..1B41;N # Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG 1B42;N # Mn BALINESE VOWEL SIGN PEPET 1B43..1B44;N # Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG 1B45..1B4B;N # Lo [7] BALINESE LETTER KAF SASAK..BALINESE LETTER ASYURA SASAK 1B50..1B59;N # Nd [10] BALINESE DIGIT ZERO..BALINESE DIGIT NINE 1B5A..1B60;N # Po [7] BALINESE PANTI..BALINESE PAMENENG 1B61..1B6A;N # So [10] BALINESE MUSICAL SYMBOL DONG..BALINESE MUSICAL SYMBOL DANG GEDE 1B6B..1B73;N # Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG 1B74..1B7C;N # So [9] BALINESE MUSICAL SYMBOL RIGHT-HAND OPEN DUG..BALINESE MUSICAL SYMBOL LEFT-HAND OPEN PING 1B80..1B81;N # Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR 1B82;N # Mc SUNDANESE SIGN PANGWISAD 1B83..1BA0;N # Lo [30] SUNDANESE LETTER A..SUNDANESE LETTER HA 1BA1;N # Mc SUNDANESE CONSONANT SIGN PAMINGKAL 1BA2..1BA5;N # Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU 1BA6..1BA7;N # Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG 1BA8..1BA9;N # Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG 1BAA;N # Mc SUNDANESE SIGN PAMAAEH 1BAB..1BAD;N # Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA 1BAE..1BAF;N # Lo [2] SUNDANESE LETTER KHA..SUNDANESE LETTER SYA 1BB0..1BB9;N # Nd [10] SUNDANESE DIGIT ZERO..SUNDANESE DIGIT NINE 1BBA..1BBF;N # Lo [6] SUNDANESE AVAGRAHA..SUNDANESE LETTER FINAL M 1BC0..1BE5;N # Lo [38] BATAK LETTER A..BATAK LETTER U 1BE6;N # Mn BATAK SIGN TOMPI 1BE7;N # Mc BATAK VOWEL SIGN E 1BE8..1BE9;N # Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE 1BEA..1BEC;N # Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O 1BED;N # Mn BATAK VOWEL SIGN KARO O 1BEE;N # Mc BATAK VOWEL SIGN U 1BEF..1BF1;N # Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H 1BF2..1BF3;N # Mc [2] BATAK PANGOLAT..BATAK PANONGONAN 1BFC..1BFF;N # Po [4] BATAK SYMBOL BINDU NA METEK..BATAK SYMBOL BINDU PANGOLAT 1C00..1C23;N # Lo [36] LEPCHA LETTER KA..LEPCHA LETTER A 1C24..1C2B;N # Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU 1C2C..1C33;N # Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T 1C34..1C35;N # Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG 1C36..1C37;N # Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA 1C3B..1C3F;N # Po [5] LEPCHA PUNCTUATION TA-ROL..LEPCHA PUNCTUATION TSHOOK 1C40..1C49;N # Nd [10] LEPCHA DIGIT ZERO..LEPCHA DIGIT NINE 1C4D..1C4F;N # Lo [3] LEPCHA LETTER TTA..LEPCHA LETTER DDA 1C50..1C59;N # Nd [10] OL CHIKI DIGIT ZERO..OL CHIKI DIGIT NINE 1C5A..1C77;N # Lo [30] OL CHIKI LETTER LA..OL CHIKI LETTER OH 1C78..1C7D;N # Lm [6] OL CHIKI MU TTUDDAG..OL CHIKI AHAD 1C7E..1C7F;N # Po [2] OL CHIKI PUNCTUATION MUCAAD..OL CHIKI PUNCTUATION DOUBLE MUCAAD 1C80..1C88;N # Ll [9] CYRILLIC SMALL LETTER ROUNDED VE..CYRILLIC SMALL LETTER UNBLENDED UK 1C90..1CBA;N # Lu [43] GEORGIAN MTAVRULI CAPITAL LETTER AN..GEORGIAN MTAVRULI CAPITAL LETTER AIN 1CBD..1CBF;N # Lu [3] GEORGIAN MTAVRULI CAPITAL LETTER AEN..GEORGIAN MTAVRULI CAPITAL LETTER LABIAL SIGN 1CC0..1CC7;N # Po [8] SUNDANESE PUNCTUATION BINDU SURYA..SUNDANESE PUNCTUATION BINDU BA SATANGA 1CD0..1CD2;N # Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA 1CD3;N # Po VEDIC SIGN NIHSHVASA 1CD4..1CE0;N # Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA 1CE1;N # Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA 1CE2..1CE8;N # Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL 1CE9..1CEC;N # Lo [4] VEDIC SIGN ANUSVARA ANTARGOMUKHA..VEDIC SIGN ANUSVARA VAMAGOMUKHA WITH TAIL 1CED;N # Mn VEDIC SIGN TIRYAK 1CEE..1CF1;N # Lo [4] VEDIC SIGN HEXIFORM LONG ANUSVARA..VEDIC SIGN ANUSVARA UBHAYATO MUKHA 1CF2..1CF3;N # Mc [2] VEDIC SIGN ARDHAVISARGA..VEDIC SIGN ROTATED ARDHAVISARGA 1CF4;N # Mn VEDIC TONE CANDRA ABOVE 1CF5..1CF6;N # Lo [2] VEDIC SIGN JIHVAMULIYA..VEDIC SIGN UPADHMANIYA 1CF7;N # Mc VEDIC SIGN ATIKRAMA 1CF8..1CF9;N # Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE 1D00..1D2B;N # Ll [44] LATIN LETTER SMALL CAPITAL A..CYRILLIC LETTER SMALL CAPITAL EL 1D2C..1D6A;N # Lm [63] MODIFIER LETTER CAPITAL A..GREEK SUBSCRIPT SMALL LETTER CHI 1D6B..1D77;N # Ll [13] LATIN SMALL LETTER UE..LATIN SMALL LETTER TURNED G 1D78;N # Lm MODIFIER LETTER CYRILLIC EN 1D79..1D7F;N # Ll [7] LATIN SMALL LETTER INSULAR G..LATIN SMALL LETTER UPSILON WITH STROKE 1D80..1D9A;N # Ll [27] LATIN SMALL LETTER B WITH PALATAL HOOK..LATIN SMALL LETTER EZH WITH RETROFLEX HOOK 1D9B..1DBF;N # Lm [37] MODIFIER LETTER SMALL TURNED ALPHA..MODIFIER LETTER SMALL THETA 1DC0..1DF9;N # Mn [58] COMBINING DOTTED GRAVE ACCENT..COMBINING WIDE INVERTED BRIDGE BELOW 1DFB..1DFF;N # Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW 1E00..1EFF;N # L& [256] LATIN CAPITAL LETTER A WITH RING BELOW..LATIN SMALL LETTER Y WITH LOOP 1F00..1F15;N # L& [22] GREEK SMALL LETTER ALPHA WITH PSILI..GREEK SMALL LETTER EPSILON WITH DASIA AND OXIA 1F18..1F1D;N # Lu [6] GREEK CAPITAL LETTER EPSILON WITH PSILI..GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA 1F20..1F45;N # L& [38] GREEK SMALL LETTER ETA WITH PSILI..GREEK SMALL LETTER OMICRON WITH DASIA AND OXIA 1F48..1F4D;N # Lu [6] GREEK CAPITAL LETTER OMICRON WITH PSILI..GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA 1F50..1F57;N # Ll [8] GREEK SMALL LETTER UPSILON WITH PSILI..GREEK SMALL LETTER UPSILON WITH DASIA AND PERISPOMENI 1F59;N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA 1F5B;N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA 1F5D;N # Lu GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA 1F5F..1F7D;N # L& [31] GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI..GREEK SMALL LETTER OMEGA WITH OXIA 1F80..1FB4;N # L& [53] GREEK SMALL LETTER ALPHA WITH PSILI AND YPOGEGRAMMENI..GREEK SMALL LETTER ALPHA WITH OXIA AND YPOGEGRAMMENI 1FB6..1FBC;N # L& [7] GREEK SMALL LETTER ALPHA WITH PERISPOMENI..GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI 1FBD;N # Sk GREEK KORONIS 1FBE;N # Ll GREEK PROSGEGRAMMENI 1FBF..1FC1;N # Sk [3] GREEK PSILI..GREEK DIALYTIKA AND PERISPOMENI 1FC2..1FC4;N # Ll [3] GREEK SMALL LETTER ETA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER ETA WITH OXIA AND YPOGEGRAMMENI 1FC6..1FCC;N # L& [7] GREEK SMALL LETTER ETA WITH PERISPOMENI..GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI 1FCD..1FCF;N # Sk [3] GREEK PSILI AND VARIA..GREEK PSILI AND PERISPOMENI 1FD0..1FD3;N # Ll [4] GREEK SMALL LETTER IOTA WITH VRACHY..GREEK SMALL LETTER IOTA WITH DIALYTIKA AND OXIA 1FD6..1FDB;N # L& [6] GREEK SMALL LETTER IOTA WITH PERISPOMENI..GREEK CAPITAL LETTER IOTA WITH OXIA 1FDD..1FDF;N # Sk [3] GREEK DASIA AND VARIA..GREEK DASIA AND PERISPOMENI 1FE0..1FEC;N # L& [13] GREEK SMALL LETTER UPSILON WITH VRACHY..GREEK CAPITAL LETTER RHO WITH DASIA 1FED..1FEF;N # Sk [3] GREEK DIALYTIKA AND VARIA..GREEK VARIA 1FF2..1FF4;N # Ll [3] GREEK SMALL LETTER OMEGA WITH VARIA AND YPOGEGRAMMENI..GREEK SMALL LETTER OMEGA WITH OXIA AND YPOGEGRAMMENI 1FF6..1FFC;N # L& [7] GREEK SMALL LETTER OMEGA WITH PERISPOMENI..GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI 1FFD..1FFE;N # Sk [2] GREEK OXIA..GREEK DASIA 2000..200A;N # Zs [11] EN QUAD..HAIR SPACE 200B..200F;N # Cf [5] ZERO WIDTH SPACE..RIGHT-TO-LEFT MARK 2010;A # Pd HYPHEN 2011..2012;N # Pd [2] NON-BREAKING HYPHEN..FIGURE DASH 2013..2015;A # Pd [3] EN DASH..HORIZONTAL BAR 2016;A # Po DOUBLE VERTICAL LINE 2017;N # Po DOUBLE LOW LINE 2018;A # Pi LEFT SINGLE QUOTATION MARK 2019;A # Pf RIGHT SINGLE QUOTATION MARK 201A;N # Ps SINGLE LOW-9 QUOTATION MARK 201B;N # Pi SINGLE HIGH-REVERSED-9 QUOTATION MARK 201C;A # Pi LEFT DOUBLE QUOTATION MARK 201D;A # Pf RIGHT DOUBLE QUOTATION MARK 201E;N # Ps DOUBLE LOW-9 QUOTATION MARK 201F;N # Pi DOUBLE HIGH-REVERSED-9 QUOTATION MARK 2020..2022;A # Po [3] DAGGER..BULLET 2023;N # Po TRIANGULAR BULLET 2024..2027;A # Po [4] ONE DOT LEADER..HYPHENATION POINT 2028;N # Zl LINE SEPARATOR 2029;N # Zp PARAGRAPH SEPARATOR 202A..202E;N # Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE 202F;N # Zs NARROW NO-BREAK SPACE 2030;A # Po PER MILLE SIGN 2031;N # Po PER TEN THOUSAND SIGN 2032..2033;A # Po [2] PRIME..DOUBLE PRIME 2034;N # Po TRIPLE PRIME 2035;A # Po REVERSED PRIME 2036..2038;N # Po [3] REVERSED DOUBLE PRIME..CARET 2039;N # Pi SINGLE LEFT-POINTING ANGLE QUOTATION MARK 203A;N # Pf SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 203B;A # Po REFERENCE MARK 203C..203D;N # Po [2] DOUBLE EXCLAMATION MARK..INTERROBANG 203E;A # Po OVERLINE 203F..2040;N # Pc [2] UNDERTIE..CHARACTER TIE 2041..2043;N # Po [3] CARET INSERTION POINT..HYPHEN BULLET 2044;N # Sm FRACTION SLASH 2045;N # Ps LEFT SQUARE BRACKET WITH QUILL 2046;N # Pe RIGHT SQUARE BRACKET WITH QUILL 2047..2051;N # Po [11] DOUBLE QUESTION MARK..TWO ASTERISKS ALIGNED VERTICALLY 2052;N # Sm COMMERCIAL MINUS SIGN 2053;N # Po SWUNG DASH 2054;N # Pc INVERTED UNDERTIE 2055..205E;N # Po [10] FLOWER PUNCTUATION MARK..VERTICAL FOUR DOTS 205F;N # Zs MEDIUM MATHEMATICAL SPACE 2060..2064;N # Cf [5] WORD JOINER..INVISIBLE PLUS 2066..206F;N # Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES 2070;N # No SUPERSCRIPT ZERO 2071;N # Lm SUPERSCRIPT LATIN SMALL LETTER I 2074;A # No SUPERSCRIPT FOUR 2075..2079;N # No [5] SUPERSCRIPT FIVE..SUPERSCRIPT NINE 207A..207C;N # Sm [3] SUPERSCRIPT PLUS SIGN..SUPERSCRIPT EQUALS SIGN 207D;N # Ps SUPERSCRIPT LEFT PARENTHESIS 207E;N # Pe SUPERSCRIPT RIGHT PARENTHESIS 207F;A # Lm SUPERSCRIPT LATIN SMALL LETTER N 2080;N # No SUBSCRIPT ZERO 2081..2084;A # No [4] SUBSCRIPT ONE..SUBSCRIPT FOUR 2085..2089;N # No [5] SUBSCRIPT FIVE..SUBSCRIPT NINE 208A..208C;N # Sm [3] SUBSCRIPT PLUS SIGN..SUBSCRIPT EQUALS SIGN 208D;N # Ps SUBSCRIPT LEFT PARENTHESIS 208E;N # Pe SUBSCRIPT RIGHT PARENTHESIS 2090..209C;N # Lm [13] LATIN SUBSCRIPT SMALL LETTER A..LATIN SUBSCRIPT SMALL LETTER T 20A0..20A8;N # Sc [9] EURO-CURRENCY SIGN..RUPEE SIGN 20A9;H # Sc WON SIGN 20AA..20AB;N # Sc [2] NEW SHEQEL SIGN..DONG SIGN 20AC;A # Sc EURO SIGN 20AD..20BF;N # Sc [19] KIP SIGN..BITCOIN SIGN 20D0..20DC;N # Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE 20DD..20E0;N # Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH 20E1;N # Mn COMBINING LEFT RIGHT ARROW ABOVE 20E2..20E4;N # Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE 20E5..20F0;N # Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE 2100..2101;N # So [2] ACCOUNT OF..ADDRESSED TO THE SUBJECT 2102;N # Lu DOUBLE-STRUCK CAPITAL C 2103;A # So DEGREE CELSIUS 2104;N # So CENTRE LINE SYMBOL 2105;A # So CARE OF 2106;N # So CADA UNA 2107;N # Lu EULER CONSTANT 2108;N # So SCRUPLE 2109;A # So DEGREE FAHRENHEIT 210A..2112;N # L& [9] SCRIPT SMALL G..SCRIPT CAPITAL L 2113;A # Ll SCRIPT SMALL L 2114;N # So L B BAR SYMBOL 2115;N # Lu DOUBLE-STRUCK CAPITAL N 2116;A # So NUMERO SIGN 2117;N # So SOUND RECORDING COPYRIGHT 2118;N # Sm SCRIPT CAPITAL P 2119..211D;N # Lu [5] DOUBLE-STRUCK CAPITAL P..DOUBLE-STRUCK CAPITAL R 211E..2120;N # So [3] PRESCRIPTION TAKE..SERVICE MARK 2121..2122;A # So [2] TELEPHONE SIGN..TRADE MARK SIGN 2123;N # So VERSICLE 2124;N # Lu DOUBLE-STRUCK CAPITAL Z 2125;N # So OUNCE SIGN 2126;A # Lu OHM SIGN 2127;N # So INVERTED OHM SIGN 2128;N # Lu BLACK-LETTER CAPITAL Z 2129;N # So TURNED GREEK SMALL LETTER IOTA 212A;N # Lu KELVIN SIGN 212B;A # Lu ANGSTROM SIGN 212C..212D;N # Lu [2] SCRIPT CAPITAL B..BLACK-LETTER CAPITAL C 212E;N # So ESTIMATED SYMBOL 212F..2134;N # L& [6] SCRIPT SMALL E..SCRIPT SMALL O 2135..2138;N # Lo [4] ALEF SYMBOL..DALET SYMBOL 2139;N # Ll INFORMATION SOURCE 213A..213B;N # So [2] ROTATED CAPITAL Q..FACSIMILE SIGN 213C..213F;N # L& [4] DOUBLE-STRUCK SMALL PI..DOUBLE-STRUCK CAPITAL PI 2140..2144;N # Sm [5] DOUBLE-STRUCK N-ARY SUMMATION..TURNED SANS-SERIF CAPITAL Y 2145..2149;N # L& [5] DOUBLE-STRUCK ITALIC CAPITAL D..DOUBLE-STRUCK ITALIC SMALL J 214A;N # So PROPERTY LINE 214B;N # Sm TURNED AMPERSAND 214C..214D;N # So [2] PER SIGN..AKTIESELSKAB 214E;N # Ll TURNED SMALL F 214F;N # So SYMBOL FOR SAMARITAN SOURCE 2150..2152;N # No [3] VULGAR FRACTION ONE SEVENTH..VULGAR FRACTION ONE TENTH 2153..2154;A # No [2] VULGAR FRACTION ONE THIRD..VULGAR FRACTION TWO THIRDS 2155..215A;N # No [6] VULGAR FRACTION ONE FIFTH..VULGAR FRACTION FIVE SIXTHS 215B..215E;A # No [4] VULGAR FRACTION ONE EIGHTH..VULGAR FRACTION SEVEN EIGHTHS 215F;N # No FRACTION NUMERATOR ONE 2160..216B;A # Nl [12] ROMAN NUMERAL ONE..ROMAN NUMERAL TWELVE 216C..216F;N # Nl [4] ROMAN NUMERAL FIFTY..ROMAN NUMERAL ONE THOUSAND 2170..2179;A # Nl [10] SMALL ROMAN NUMERAL ONE..SMALL ROMAN NUMERAL TEN 217A..2182;N # Nl [9] SMALL ROMAN NUMERAL ELEVEN..ROMAN NUMERAL TEN THOUSAND 2183..2184;N # L& [2] ROMAN NUMERAL REVERSED ONE HUNDRED..LATIN SMALL LETTER REVERSED C 2185..2188;N # Nl [4] ROMAN NUMERAL SIX LATE FORM..ROMAN NUMERAL ONE HUNDRED THOUSAND 2189;A # No VULGAR FRACTION ZERO THIRDS 218A..218B;N # So [2] TURNED DIGIT TWO..TURNED DIGIT THREE 2190..2194;A # Sm [5] LEFTWARDS ARROW..LEFT RIGHT ARROW 2195..2199;A # So [5] UP DOWN ARROW..SOUTH WEST ARROW 219A..219B;N # Sm [2] LEFTWARDS ARROW WITH STROKE..RIGHTWARDS ARROW WITH STROKE 219C..219F;N # So [4] LEFTWARDS WAVE ARROW..UPWARDS TWO HEADED ARROW 21A0;N # Sm RIGHTWARDS TWO HEADED ARROW 21A1..21A2;N # So [2] DOWNWARDS TWO HEADED ARROW..LEFTWARDS ARROW WITH TAIL 21A3;N # Sm RIGHTWARDS ARROW WITH TAIL 21A4..21A5;N # So [2] LEFTWARDS ARROW FROM BAR..UPWARDS ARROW FROM BAR 21A6;N # Sm RIGHTWARDS ARROW FROM BAR 21A7..21AD;N # So [7] DOWNWARDS ARROW FROM BAR..LEFT RIGHT WAVE ARROW 21AE;N # Sm LEFT RIGHT ARROW WITH STROKE 21AF..21B7;N # So [9] DOWNWARDS ZIGZAG ARROW..CLOCKWISE TOP SEMICIRCLE ARROW 21B8..21B9;A # So [2] NORTH WEST ARROW TO LONG BAR..LEFTWARDS ARROW TO BAR OVER RIGHTWARDS ARROW TO BAR 21BA..21CD;N # So [20] ANTICLOCKWISE OPEN CIRCLE ARROW..LEFTWARDS DOUBLE ARROW WITH STROKE 21CE..21CF;N # Sm [2] LEFT RIGHT DOUBLE ARROW WITH STROKE..RIGHTWARDS DOUBLE ARROW WITH STROKE 21D0..21D1;N # So [2] LEFTWARDS DOUBLE ARROW..UPWARDS DOUBLE ARROW 21D2;A # Sm RIGHTWARDS DOUBLE ARROW 21D3;N # So DOWNWARDS DOUBLE ARROW 21D4;A # Sm LEFT RIGHT DOUBLE ARROW 21D5..21E6;N # So [18] UP DOWN DOUBLE ARROW..LEFTWARDS WHITE ARROW 21E7;A # So UPWARDS WHITE ARROW 21E8..21F3;N # So [12] RIGHTWARDS WHITE ARROW..UP DOWN WHITE ARROW 21F4..21FF;N # Sm [12] RIGHT ARROW WITH SMALL CIRCLE..LEFT RIGHT OPEN-HEADED ARROW 2200;A # Sm FOR ALL 2201;N # Sm COMPLEMENT 2202..2203;A # Sm [2] PARTIAL DIFFERENTIAL..THERE EXISTS 2204..2206;N # Sm [3] THERE DOES NOT EXIST..INCREMENT 2207..2208;A # Sm [2] NABLA..ELEMENT OF 2209..220A;N # Sm [2] NOT AN ELEMENT OF..SMALL ELEMENT OF 220B;A # Sm CONTAINS AS MEMBER 220C..220E;N # Sm [3] DOES NOT CONTAIN AS MEMBER..END OF PROOF 220F;A # Sm N-ARY PRODUCT 2210;N # Sm N-ARY COPRODUCT 2211;A # Sm N-ARY SUMMATION 2212..2214;N # Sm [3] MINUS SIGN..DOT PLUS 2215;A # Sm DIVISION SLASH 2216..2219;N # Sm [4] SET MINUS..BULLET OPERATOR 221A;A # Sm SQUARE ROOT 221B..221C;N # Sm [2] CUBE ROOT..FOURTH ROOT 221D..2220;A # Sm [4] PROPORTIONAL TO..ANGLE 2221..2222;N # Sm [2] MEASURED ANGLE..SPHERICAL ANGLE 2223;A # Sm DIVIDES 2224;N # Sm DOES NOT DIVIDE 2225;A # Sm PARALLEL TO 2226;N # Sm NOT PARALLEL TO 2227..222C;A # Sm [6] LOGICAL AND..DOUBLE INTEGRAL 222D;N # Sm TRIPLE INTEGRAL 222E;A # Sm CONTOUR INTEGRAL 222F..2233;N # Sm [5] SURFACE INTEGRAL..ANTICLOCKWISE CONTOUR INTEGRAL 2234..2237;A # Sm [4] THEREFORE..PROPORTION 2238..223B;N # Sm [4] DOT MINUS..HOMOTHETIC 223C..223D;A # Sm [2] TILDE OPERATOR..REVERSED TILDE 223E..2247;N # Sm [10] INVERTED LAZY S..NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO 2248;A # Sm ALMOST EQUAL TO 2249..224B;N # Sm [3] NOT ALMOST EQUAL TO..TRIPLE TILDE 224C;A # Sm ALL EQUAL TO 224D..2251;N # Sm [5] EQUIVALENT TO..GEOMETRICALLY EQUAL TO 2252;A # Sm APPROXIMATELY EQUAL TO OR THE IMAGE OF 2253..225F;N # Sm [13] IMAGE OF OR APPROXIMATELY EQUAL TO..QUESTIONED EQUAL TO 2260..2261;A # Sm [2] NOT EQUAL TO..IDENTICAL TO 2262..2263;N # Sm [2] NOT IDENTICAL TO..STRICTLY EQUIVALENT TO 2264..2267;A # Sm [4] LESS-THAN OR EQUAL TO..GREATER-THAN OVER EQUAL TO 2268..2269;N # Sm [2] LESS-THAN BUT NOT EQUAL TO..GREATER-THAN BUT NOT EQUAL TO 226A..226B;A # Sm [2] MUCH LESS-THAN..MUCH GREATER-THAN 226C..226D;N # Sm [2] BETWEEN..NOT EQUIVALENT TO 226E..226F;A # Sm [2] NOT LESS-THAN..NOT GREATER-THAN 2270..2281;N # Sm [18] NEITHER LESS-THAN NOR EQUAL TO..DOES NOT SUCCEED 2282..2283;A # Sm [2] SUBSET OF..SUPERSET OF 2284..2285;N # Sm [2] NOT A SUBSET OF..NOT A SUPERSET OF 2286..2287;A # Sm [2] SUBSET OF OR EQUAL TO..SUPERSET OF OR EQUAL TO 2288..2294;N # Sm [13] NEITHER A SUBSET OF NOR EQUAL TO..SQUARE CUP 2295;A # Sm CIRCLED PLUS 2296..2298;N # Sm [3] CIRCLED MINUS..CIRCLED DIVISION SLASH 2299;A # Sm CIRCLED DOT OPERATOR 229A..22A4;N # Sm [11] CIRCLED RING OPERATOR..DOWN TACK 22A5;A # Sm UP TACK 22A6..22BE;N # Sm [25] ASSERTION..RIGHT ANGLE WITH ARC 22BF;A # Sm RIGHT TRIANGLE 22C0..22FF;N # Sm [64] N-ARY LOGICAL AND..Z NOTATION BAG MEMBERSHIP 2300..2307;N # So [8] DIAMETER SIGN..WAVY LINE 2308;N # Ps LEFT CEILING 2309;N # Pe RIGHT CEILING 230A;N # Ps LEFT FLOOR 230B;N # Pe RIGHT FLOOR 230C..2311;N # So [6] BOTTOM RIGHT CROP..SQUARE LOZENGE 2312;A # So ARC 2313..2319;N # So [7] SEGMENT..TURNED NOT SIGN 231A..231B;W # So [2] WATCH..HOURGLASS 231C..231F;N # So [4] TOP LEFT CORNER..BOTTOM RIGHT CORNER 2320..2321;N # Sm [2] TOP HALF INTEGRAL..BOTTOM HALF INTEGRAL 2322..2328;N # So [7] FROWN..KEYBOARD 2329;W # Ps LEFT-POINTING ANGLE BRACKET 232A;W # Pe RIGHT-POINTING ANGLE BRACKET 232B..237B;N # So [81] ERASE TO THE LEFT..NOT CHECK MARK 237C;N # Sm RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW 237D..239A;N # So [30] SHOULDERED OPEN BOX..CLEAR SCREEN SYMBOL 239B..23B3;N # Sm [25] LEFT PARENTHESIS UPPER HOOK..SUMMATION BOTTOM 23B4..23DB;N # So [40] TOP SQUARE BRACKET..FUSE 23DC..23E1;N # Sm [6] TOP PARENTHESIS..BOTTOM TORTOISE SHELL BRACKET 23E2..23E8;N # So [7] WHITE TRAPEZIUM..DECIMAL EXPONENT SYMBOL 23E9..23EC;W # So [4] BLACK RIGHT-POINTING DOUBLE TRIANGLE..BLACK DOWN-POINTING DOUBLE TRIANGLE 23ED..23EF;N # So [3] BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR..BLACK RIGHT-POINTING TRIANGLE WITH DOUBLE VERTICAL BAR 23F0;W # So ALARM CLOCK 23F1..23F2;N # So [2] STOPWATCH..TIMER CLOCK 23F3;W # So HOURGLASS WITH FLOWING SAND 23F4..23FF;N # So [12] BLACK MEDIUM LEFT-POINTING TRIANGLE..OBSERVER EYE SYMBOL 2400..2426;N # So [39] SYMBOL FOR NULL..SYMBOL FOR SUBSTITUTE FORM TWO 2440..244A;N # So [11] OCR HOOK..OCR DOUBLE BACKSLASH 2460..249B;A # No [60] CIRCLED DIGIT ONE..NUMBER TWENTY FULL STOP 249C..24E9;A # So [78] PARENTHESIZED LATIN SMALL LETTER A..CIRCLED LATIN SMALL LETTER Z 24EA;N # No CIRCLED DIGIT ZERO 24EB..24FF;A # No [21] NEGATIVE CIRCLED NUMBER ELEVEN..NEGATIVE CIRCLED DIGIT ZERO 2500..254B;A # So [76] BOX DRAWINGS LIGHT HORIZONTAL..BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL 254C..254F;N # So [4] BOX DRAWINGS LIGHT DOUBLE DASH HORIZONTAL..BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL 2550..2573;A # So [36] BOX DRAWINGS DOUBLE HORIZONTAL..BOX DRAWINGS LIGHT DIAGONAL CROSS 2574..257F;N # So [12] BOX DRAWINGS LIGHT LEFT..BOX DRAWINGS HEAVY UP AND LIGHT DOWN 2580..258F;A # So [16] UPPER HALF BLOCK..LEFT ONE EIGHTH BLOCK 2590..2591;N # So [2] RIGHT HALF BLOCK..LIGHT SHADE 2592..2595;A # So [4] MEDIUM SHADE..RIGHT ONE EIGHTH BLOCK 2596..259F;N # So [10] QUADRANT LOWER LEFT..QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT 25A0..25A1;A # So [2] BLACK SQUARE..WHITE SQUARE 25A2;N # So WHITE SQUARE WITH ROUNDED CORNERS 25A3..25A9;A # So [7] WHITE SQUARE CONTAINING BLACK SMALL SQUARE..SQUARE WITH DIAGONAL CROSSHATCH FILL 25AA..25B1;N # So [8] BLACK SMALL SQUARE..WHITE PARALLELOGRAM 25B2..25B3;A # So [2] BLACK UP-POINTING TRIANGLE..WHITE UP-POINTING TRIANGLE 25B4..25B5;N # So [2] BLACK UP-POINTING SMALL TRIANGLE..WHITE UP-POINTING SMALL TRIANGLE 25B6;A # So BLACK RIGHT-POINTING TRIANGLE 25B7;A # Sm WHITE RIGHT-POINTING TRIANGLE 25B8..25BB;N # So [4] BLACK RIGHT-POINTING SMALL TRIANGLE..WHITE RIGHT-POINTING POINTER 25BC..25BD;A # So [2] BLACK DOWN-POINTING TRIANGLE..WHITE DOWN-POINTING TRIANGLE 25BE..25BF;N # So [2] BLACK DOWN-POINTING SMALL TRIANGLE..WHITE DOWN-POINTING SMALL TRIANGLE 25C0;A # So BLACK LEFT-POINTING TRIANGLE 25C1;A # Sm WHITE LEFT-POINTING TRIANGLE 25C2..25C5;N # So [4] BLACK LEFT-POINTING SMALL TRIANGLE..WHITE LEFT-POINTING POINTER 25C6..25C8;A # So [3] BLACK DIAMOND..WHITE DIAMOND CONTAINING BLACK SMALL DIAMOND 25C9..25CA;N # So [2] FISHEYE..LOZENGE 25CB;A # So WHITE CIRCLE 25CC..25CD;N # So [2] DOTTED CIRCLE..CIRCLE WITH VERTICAL FILL 25CE..25D1;A # So [4] BULLSEYE..CIRCLE WITH RIGHT HALF BLACK 25D2..25E1;N # So [16] CIRCLE WITH LOWER HALF BLACK..LOWER HALF CIRCLE 25E2..25E5;A # So [4] BLACK LOWER RIGHT TRIANGLE..BLACK UPPER RIGHT TRIANGLE 25E6..25EE;N # So [9] WHITE BULLET..UP-POINTING TRIANGLE WITH RIGHT HALF BLACK 25EF;A # So LARGE CIRCLE 25F0..25F7;N # So [8] WHITE SQUARE WITH UPPER LEFT QUADRANT..WHITE CIRCLE WITH UPPER RIGHT QUADRANT 25F8..25FC;N # Sm [5] UPPER LEFT TRIANGLE..BLACK MEDIUM SQUARE 25FD..25FE;W # Sm [2] WHITE MEDIUM SMALL SQUARE..BLACK MEDIUM SMALL SQUARE 25FF;N # Sm LOWER RIGHT TRIANGLE 2600..2604;N # So [5] BLACK SUN WITH RAYS..COMET 2605..2606;A # So [2] BLACK STAR..WHITE STAR 2607..2608;N # So [2] LIGHTNING..THUNDERSTORM 2609;A # So SUN 260A..260D;N # So [4] ASCENDING NODE..OPPOSITION 260E..260F;A # So [2] BLACK TELEPHONE..WHITE TELEPHONE 2610..2613;N # So [4] BALLOT BOX..SALTIRE 2614..2615;W # So [2] UMBRELLA WITH RAIN DROPS..HOT BEVERAGE 2616..261B;N # So [6] WHITE SHOGI PIECE..BLACK RIGHT POINTING INDEX 261C;A # So WHITE LEFT POINTING INDEX 261D;N # So WHITE UP POINTING INDEX 261E;A # So WHITE RIGHT POINTING INDEX 261F..263F;N # So [33] WHITE DOWN POINTING INDEX..MERCURY 2640;A # So FEMALE SIGN 2641;N # So EARTH 2642;A # So MALE SIGN 2643..2647;N # So [5] JUPITER..PLUTO 2648..2653;W # So [12] ARIES..PISCES 2654..265F;N # So [12] WHITE CHESS KING..BLACK CHESS PAWN 2660..2661;A # So [2] BLACK SPADE SUIT..WHITE HEART SUIT 2662;N # So WHITE DIAMOND SUIT 2663..2665;A # So [3] BLACK CLUB SUIT..BLACK HEART SUIT 2666;N # So BLACK DIAMOND SUIT 2667..266A;A # So [4] WHITE CLUB SUIT..EIGHTH NOTE 266B;N # So BEAMED EIGHTH NOTES 266C..266D;A # So [2] BEAMED SIXTEENTH NOTES..MUSIC FLAT SIGN 266E;N # So MUSIC NATURAL SIGN 266F;A # Sm MUSIC SHARP SIGN 2670..267E;N # So [15] WEST SYRIAC CROSS..PERMANENT PAPER SIGN 267F;W # So WHEELCHAIR SYMBOL 2680..2692;N # So [19] DIE FACE-1..HAMMER AND PICK 2693;W # So ANCHOR 2694..269D;N # So [10] CROSSED SWORDS..OUTLINED WHITE STAR 269E..269F;A # So [2] THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT 26A0;N # So WARNING SIGN 26A1;W # So HIGH VOLTAGE SIGN 26A2..26A9;N # So [8] DOUBLED FEMALE SIGN..HORIZONTAL MALE WITH STROKE SIGN 26AA..26AB;W # So [2] MEDIUM WHITE CIRCLE..MEDIUM BLACK CIRCLE 26AC..26BC;N # So [17] MEDIUM SMALL WHITE CIRCLE..SESQUIQUADRATE 26BD..26BE;W # So [2] SOCCER BALL..BASEBALL 26BF;A # So SQUARED KEY 26C0..26C3;N # So [4] WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING 26C4..26C5;W # So [2] SNOWMAN WITHOUT SNOW..SUN BEHIND CLOUD 26C6..26CD;A # So [8] RAIN..DISABLED CAR 26CE;W # So OPHIUCHUS 26CF..26D3;A # So [5] PICK..CHAINS 26D4;W # So NO ENTRY 26D5..26E1;A # So [13] ALTERNATE ONE-WAY LEFT WAY TRAFFIC..RESTRICTED LEFT ENTRY-2 26E2;N # So ASTRONOMICAL SYMBOL FOR URANUS 26E3;A # So HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE 26E4..26E7;N # So [4] PENTAGRAM..INVERTED PENTAGRAM 26E8..26E9;A # So [2] BLACK CROSS ON SHIELD..SHINTO SHRINE 26EA;W # So CHURCH 26EB..26F1;A # So [7] CASTLE..UMBRELLA ON GROUND 26F2..26F3;W # So [2] FOUNTAIN..FLAG IN HOLE 26F4;A # So FERRY 26F5;W # So SAILBOAT 26F6..26F9;A # So [4] SQUARE FOUR CORNERS..PERSON WITH BALL 26FA;W # So TENT 26FB..26FC;A # So [2] JAPANESE BANK SYMBOL..HEADSTONE GRAVEYARD SYMBOL 26FD;W # So FUEL PUMP 26FE..26FF;A # So [2] CUP ON BLACK SQUARE..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE 2700..2704;N # So [5] BLACK SAFETY SCISSORS..WHITE SCISSORS 2705;W # So WHITE HEAVY CHECK MARK 2706..2709;N # So [4] TELEPHONE LOCATION SIGN..ENVELOPE 270A..270B;W # So [2] RAISED FIST..RAISED HAND 270C..2727;N # So [28] VICTORY HAND..WHITE FOUR POINTED STAR 2728;W # So SPARKLES 2729..273C;N # So [20] STRESS OUTLINED WHITE STAR..OPEN CENTRE TEARDROP-SPOKED ASTERISK 273D;A # So HEAVY TEARDROP-SPOKED ASTERISK 273E..274B;N # So [14] SIX PETALLED BLACK AND WHITE FLORETTE..HEAVY EIGHT TEARDROP-SPOKED PROPELLER ASTERISK 274C;W # So CROSS MARK 274D;N # So SHADOWED WHITE CIRCLE 274E;W # So NEGATIVE SQUARED CROSS MARK 274F..2752;N # So [4] LOWER RIGHT DROP-SHADOWED WHITE SQUARE..UPPER RIGHT SHADOWED WHITE SQUARE 2753..2755;W # So [3] BLACK QUESTION MARK ORNAMENT..WHITE EXCLAMATION MARK ORNAMENT 2756;N # So BLACK DIAMOND MINUS WHITE X 2757;W # So HEAVY EXCLAMATION MARK SYMBOL 2758..2767;N # So [16] LIGHT VERTICAL BAR..ROTATED FLORAL HEART BULLET 2768;N # Ps MEDIUM LEFT PARENTHESIS ORNAMENT 2769;N # Pe MEDIUM RIGHT PARENTHESIS ORNAMENT 276A;N # Ps MEDIUM FLATTENED LEFT PARENTHESIS ORNAMENT 276B;N # Pe MEDIUM FLATTENED RIGHT PARENTHESIS ORNAMENT 276C;N # Ps MEDIUM LEFT-POINTING ANGLE BRACKET ORNAMENT 276D;N # Pe MEDIUM RIGHT-POINTING ANGLE BRACKET ORNAMENT 276E;N # Ps HEAVY LEFT-POINTING ANGLE QUOTATION MARK ORNAMENT 276F;N # Pe HEAVY RIGHT-POINTING ANGLE QUOTATION MARK ORNAMENT 2770;N # Ps HEAVY LEFT-POINTING ANGLE BRACKET ORNAMENT 2771;N # Pe HEAVY RIGHT-POINTING ANGLE BRACKET ORNAMENT 2772;N # Ps LIGHT LEFT TORTOISE SHELL BRACKET ORNAMENT 2773;N # Pe LIGHT RIGHT TORTOISE SHELL BRACKET ORNAMENT 2774;N # Ps MEDIUM LEFT CURLY BRACKET ORNAMENT 2775;N # Pe MEDIUM RIGHT CURLY BRACKET ORNAMENT 2776..277F;A # No [10] DINGBAT NEGATIVE CIRCLED DIGIT ONE..DINGBAT NEGATIVE CIRCLED NUMBER TEN 2780..2793;N # No [20] DINGBAT CIRCLED SANS-SERIF DIGIT ONE..DINGBAT NEGATIVE CIRCLED SANS-SERIF NUMBER TEN 2794;N # So HEAVY WIDE-HEADED RIGHTWARDS ARROW 2795..2797;W # So [3] HEAVY PLUS SIGN..HEAVY DIVISION SIGN 2798..27AF;N # So [24] HEAVY SOUTH EAST ARROW..NOTCHED LOWER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW 27B0;W # So CURLY LOOP 27B1..27BE;N # So [14] NOTCHED UPPER RIGHT-SHADOWED WHITE RIGHTWARDS ARROW..OPEN-OUTLINED RIGHTWARDS ARROW 27BF;W # So DOUBLE CURLY LOOP 27C0..27C4;N # Sm [5] THREE DIMENSIONAL ANGLE..OPEN SUPERSET 27C5;N # Ps LEFT S-SHAPED BAG DELIMITER 27C6;N # Pe RIGHT S-SHAPED BAG DELIMITER 27C7..27E5;N # Sm [31] OR WITH DOT INSIDE..WHITE SQUARE WITH RIGHTWARDS TICK 27E6;Na # Ps MATHEMATICAL LEFT WHITE SQUARE BRACKET 27E7;Na # Pe MATHEMATICAL RIGHT WHITE SQUARE BRACKET 27E8;Na # Ps MATHEMATICAL LEFT ANGLE BRACKET 27E9;Na # Pe MATHEMATICAL RIGHT ANGLE BRACKET 27EA;Na # Ps MATHEMATICAL LEFT DOUBLE ANGLE BRACKET 27EB;Na # Pe MATHEMATICAL RIGHT DOUBLE ANGLE BRACKET 27EC;Na # Ps MATHEMATICAL LEFT WHITE TORTOISE SHELL BRACKET 27ED;Na # Pe MATHEMATICAL RIGHT WHITE TORTOISE SHELL BRACKET 27EE;N # Ps MATHEMATICAL LEFT FLATTENED PARENTHESIS 27EF;N # Pe MATHEMATICAL RIGHT FLATTENED PARENTHESIS 27F0..27FF;N # Sm [16] UPWARDS QUADRUPLE ARROW..LONG RIGHTWARDS SQUIGGLE ARROW 2800..28FF;N # So [256] BRAILLE PATTERN BLANK..BRAILLE PATTERN DOTS-12345678 2900..297F;N # Sm [128] RIGHTWARDS TWO-HEADED ARROW WITH VERTICAL STROKE..DOWN FISH TAIL 2980..2982;N # Sm [3] TRIPLE VERTICAL BAR DELIMITER..Z NOTATION TYPE COLON 2983;N # Ps LEFT WHITE CURLY BRACKET 2984;N # Pe RIGHT WHITE CURLY BRACKET 2985;Na # Ps LEFT WHITE PARENTHESIS 2986;Na # Pe RIGHT WHITE PARENTHESIS 2987;N # Ps Z NOTATION LEFT IMAGE BRACKET 2988;N # Pe Z NOTATION RIGHT IMAGE BRACKET 2989;N # Ps Z NOTATION LEFT BINDING BRACKET 298A;N # Pe Z NOTATION RIGHT BINDING BRACKET 298B;N # Ps LEFT SQUARE BRACKET WITH UNDERBAR 298C;N # Pe RIGHT SQUARE BRACKET WITH UNDERBAR 298D;N # Ps LEFT SQUARE BRACKET WITH TICK IN TOP CORNER 298E;N # Pe RIGHT SQUARE BRACKET WITH TICK IN BOTTOM CORNER 298F;N # Ps LEFT SQUARE BRACKET WITH TICK IN BOTTOM CORNER 2990;N # Pe RIGHT SQUARE BRACKET WITH TICK IN TOP CORNER 2991;N # Ps LEFT ANGLE BRACKET WITH DOT 2992;N # Pe RIGHT ANGLE BRACKET WITH DOT 2993;N # Ps LEFT ARC LESS-THAN BRACKET 2994;N # Pe RIGHT ARC GREATER-THAN BRACKET 2995;N # Ps DOUBLE LEFT ARC GREATER-THAN BRACKET 2996;N # Pe DOUBLE RIGHT ARC LESS-THAN BRACKET 2997;N # Ps LEFT BLACK TORTOISE SHELL BRACKET 2998;N # Pe RIGHT BLACK TORTOISE SHELL BRACKET 2999..29D7;N # Sm [63] DOTTED FENCE..BLACK HOURGLASS 29D8;N # Ps LEFT WIGGLY FENCE 29D9;N # Pe RIGHT WIGGLY FENCE 29DA;N # Ps LEFT DOUBLE WIGGLY FENCE 29DB;N # Pe RIGHT DOUBLE WIGGLY FENCE 29DC..29FB;N # Sm [32] INCOMPLETE INFINITY..TRIPLE PLUS 29FC;N # Ps LEFT-POINTING CURVED ANGLE BRACKET 29FD;N # Pe RIGHT-POINTING CURVED ANGLE BRACKET 29FE..29FF;N # Sm [2] TINY..MINY 2A00..2AFF;N # Sm [256] N-ARY CIRCLED DOT OPERATOR..N-ARY WHITE VERTICAL BAR 2B00..2B1A;N # So [27] NORTH EAST WHITE ARROW..DOTTED SQUARE 2B1B..2B1C;W # So [2] BLACK LARGE SQUARE..WHITE LARGE SQUARE 2B1D..2B2F;N # So [19] BLACK VERY SMALL SQUARE..WHITE VERTICAL ELLIPSE 2B30..2B44;N # Sm [21] LEFT ARROW WITH SMALL CIRCLE..RIGHTWARDS ARROW THROUGH SUPERSET 2B45..2B46;N # So [2] LEFTWARDS QUADRUPLE ARROW..RIGHTWARDS QUADRUPLE ARROW 2B47..2B4C;N # Sm [6] REVERSE TILDE OPERATOR ABOVE RIGHTWARDS ARROW..RIGHTWARDS ARROW ABOVE REVERSE TILDE OPERATOR 2B4D..2B4F;N # So [3] DOWNWARDS TRIANGLE-HEADED ZIGZAG ARROW..SHORT BACKSLANTED SOUTH ARROW 2B50;W # So WHITE MEDIUM STAR 2B51..2B54;N # So [4] BLACK SMALL STAR..WHITE RIGHT-POINTING PENTAGON 2B55;W # So HEAVY LARGE CIRCLE 2B56..2B59;A # So [4] HEAVY OVAL WITH OVAL INSIDE..HEAVY CIRCLED SALTIRE 2B5A..2B73;N # So [26] SLANTED NORTH ARROW WITH HOOKED HEAD..DOWNWARDS TRIANGLE-HEADED ARROW TO BAR 2B76..2B95;N # So [32] NORTH WEST TRIANGLE-HEADED ARROW TO BAR..RIGHTWARDS BLACK ARROW 2B98..2BC8;N # So [49] THREE-D TOP-LIGHTED LEFTWARDS EQUILATERAL ARROWHEAD..BLACK MEDIUM RIGHT-POINTING TRIANGLE CENTRED 2BCA..2BFE;N # So [53] TOP HALF BLACK CIRCLE..REVERSED RIGHT ANGLE 2C00..2C2E;N # Lu [47] GLAGOLITIC CAPITAL LETTER AZU..GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE 2C30..2C5E;N # Ll [47] GLAGOLITIC SMALL LETTER AZU..GLAGOLITIC SMALL LETTER LATINATE MYSLITE 2C60..2C7B;N # L& [28] LATIN CAPITAL LETTER L WITH DOUBLE BAR..LATIN LETTER SMALL CAPITAL TURNED E 2C7C..2C7D;N # Lm [2] LATIN SUBSCRIPT SMALL LETTER J..MODIFIER LETTER CAPITAL V 2C7E..2C7F;N # Lu [2] LATIN CAPITAL LETTER S WITH SWASH TAIL..LATIN CAPITAL LETTER Z WITH SWASH TAIL 2C80..2CE4;N # L& [101] COPTIC CAPITAL LETTER ALFA..COPTIC SYMBOL KAI 2CE5..2CEA;N # So [6] COPTIC SYMBOL MI RO..COPTIC SYMBOL SHIMA SIMA 2CEB..2CEE;N # L& [4] COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI..COPTIC SMALL LETTER CRYPTOGRAMMIC GANGIA 2CEF..2CF1;N # Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS 2CF2..2CF3;N # L& [2] COPTIC CAPITAL LETTER BOHAIRIC KHEI..COPTIC SMALL LETTER BOHAIRIC KHEI 2CF9..2CFC;N # Po [4] COPTIC OLD NUBIAN FULL STOP..COPTIC OLD NUBIAN VERSE DIVIDER 2CFD;N # No COPTIC FRACTION ONE HALF 2CFE..2CFF;N # Po [2] COPTIC FULL STOP..COPTIC MORPHOLOGICAL DIVIDER 2D00..2D25;N # Ll [38] GEORGIAN SMALL LETTER AN..GEORGIAN SMALL LETTER HOE 2D27;N # Ll GEORGIAN SMALL LETTER YN 2D2D;N # Ll GEORGIAN SMALL LETTER AEN 2D30..2D67;N # Lo [56] TIFINAGH LETTER YA..TIFINAGH LETTER YO 2D6F;N # Lm TIFINAGH MODIFIER LETTER LABIALIZATION MARK 2D70;N # Po TIFINAGH SEPARATOR MARK 2D7F;N # Mn TIFINAGH CONSONANT JOINER 2D80..2D96;N # Lo [23] ETHIOPIC SYLLABLE LOA..ETHIOPIC SYLLABLE GGWE 2DA0..2DA6;N # Lo [7] ETHIOPIC SYLLABLE SSA..ETHIOPIC SYLLABLE SSO 2DA8..2DAE;N # Lo [7] ETHIOPIC SYLLABLE CCA..ETHIOPIC SYLLABLE CCO 2DB0..2DB6;N # Lo [7] ETHIOPIC SYLLABLE ZZA..ETHIOPIC SYLLABLE ZZO 2DB8..2DBE;N # Lo [7] ETHIOPIC SYLLABLE CCHA..ETHIOPIC SYLLABLE CCHO 2DC0..2DC6;N # Lo [7] ETHIOPIC SYLLABLE QYA..ETHIOPIC SYLLABLE QYO 2DC8..2DCE;N # Lo [7] ETHIOPIC SYLLABLE KYA..ETHIOPIC SYLLABLE KYO 2DD0..2DD6;N # Lo [7] ETHIOPIC SYLLABLE XYA..ETHIOPIC SYLLABLE XYO 2DD8..2DDE;N # Lo [7] ETHIOPIC SYLLABLE GYA..ETHIOPIC SYLLABLE GYO 2DE0..2DFF;N # Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS 2E00..2E01;N # Po [2] RIGHT ANGLE SUBSTITUTION MARKER..RIGHT ANGLE DOTTED SUBSTITUTION MARKER 2E02;N # Pi LEFT SUBSTITUTION BRACKET 2E03;N # Pf RIGHT SUBSTITUTION BRACKET 2E04;N # Pi LEFT DOTTED SUBSTITUTION BRACKET 2E05;N # Pf RIGHT DOTTED SUBSTITUTION BRACKET 2E06..2E08;N # Po [3] RAISED INTERPOLATION MARKER..DOTTED TRANSPOSITION MARKER 2E09;N # Pi LEFT TRANSPOSITION BRACKET 2E0A;N # Pf RIGHT TRANSPOSITION BRACKET 2E0B;N # Po RAISED SQUARE 2E0C;N # Pi LEFT RAISED OMISSION BRACKET 2E0D;N # Pf RIGHT RAISED OMISSION BRACKET 2E0E..2E16;N # Po [9] EDITORIAL CORONIS..DOTTED RIGHT-POINTING ANGLE 2E17;N # Pd DOUBLE OBLIQUE HYPHEN 2E18..2E19;N # Po [2] INVERTED INTERROBANG..PALM BRANCH 2E1A;N # Pd HYPHEN WITH DIAERESIS 2E1B;N # Po TILDE WITH RING ABOVE 2E1C;N # Pi LEFT LOW PARAPHRASE BRACKET 2E1D;N # Pf RIGHT LOW PARAPHRASE BRACKET 2E1E..2E1F;N # Po [2] TILDE WITH DOT ABOVE..TILDE WITH DOT BELOW 2E20;N # Pi LEFT VERTICAL BAR WITH QUILL 2E21;N # Pf RIGHT VERTICAL BAR WITH QUILL 2E22;N # Ps TOP LEFT HALF BRACKET 2E23;N # Pe TOP RIGHT HALF BRACKET 2E24;N # Ps BOTTOM LEFT HALF BRACKET 2E25;N # Pe BOTTOM RIGHT HALF BRACKET 2E26;N # Ps LEFT SIDEWAYS U BRACKET 2E27;N # Pe RIGHT SIDEWAYS U BRACKET 2E28;N # Ps LEFT DOUBLE PARENTHESIS 2E29;N # Pe RIGHT DOUBLE PARENTHESIS 2E2A..2E2E;N # Po [5] TWO DOTS OVER ONE DOT PUNCTUATION..REVERSED QUESTION MARK 2E2F;N # Lm VERTICAL TILDE 2E30..2E39;N # Po [10] RING POINT..TOP HALF SECTION SIGN 2E3A..2E3B;N # Pd [2] TWO-EM DASH..THREE-EM DASH 2E3C..2E3F;N # Po [4] STENOGRAPHIC FULL STOP..CAPITULUM 2E40;N # Pd DOUBLE HYPHEN 2E41;N # Po REVERSED COMMA 2E42;N # Ps DOUBLE LOW-REVERSED-9 QUOTATION MARK 2E43..2E4E;N # Po [12] DASH WITH LEFT UPTURN..PUNCTUS ELEVATUS MARK 2E80..2E99;W # So [26] CJK RADICAL REPEAT..CJK RADICAL RAP 2E9B..2EF3;W # So [89] CJK RADICAL CHOKE..CJK RADICAL C-SIMPLIFIED TURTLE 2F00..2FD5;W # So [214] KANGXI RADICAL ONE..KANGXI RADICAL FLUTE 2FF0..2FFB;W # So [12] IDEOGRAPHIC DESCRIPTION CHARACTER LEFT TO RIGHT..IDEOGRAPHIC DESCRIPTION CHARACTER OVERLAID 3000;F # Zs IDEOGRAPHIC SPACE 3001..3003;W # Po [3] IDEOGRAPHIC COMMA..DITTO MARK 3004;W # So JAPANESE INDUSTRIAL STANDARD SYMBOL 3005;W # Lm IDEOGRAPHIC ITERATION MARK 3006;W # Lo IDEOGRAPHIC CLOSING MARK 3007;W # Nl IDEOGRAPHIC NUMBER ZERO 3008;W # Ps LEFT ANGLE BRACKET 3009;W # Pe RIGHT ANGLE BRACKET 300A;W # Ps LEFT DOUBLE ANGLE BRACKET 300B;W # Pe RIGHT DOUBLE ANGLE BRACKET 300C;W # Ps LEFT CORNER BRACKET 300D;W # Pe RIGHT CORNER BRACKET 300E;W # Ps LEFT WHITE CORNER BRACKET 300F;W # Pe RIGHT WHITE CORNER BRACKET 3010;W # Ps LEFT BLACK LENTICULAR BRACKET 3011;W # Pe RIGHT BLACK LENTICULAR BRACKET 3012..3013;W # So [2] POSTAL MARK..GETA MARK 3014;W # Ps LEFT TORTOISE SHELL BRACKET 3015;W # Pe RIGHT TORTOISE SHELL BRACKET 3016;W # Ps LEFT WHITE LENTICULAR BRACKET 3017;W # Pe RIGHT WHITE LENTICULAR BRACKET 3018;W # Ps LEFT WHITE TORTOISE SHELL BRACKET 3019;W # Pe RIGHT WHITE TORTOISE SHELL BRACKET 301A;W # Ps LEFT WHITE SQUARE BRACKET 301B;W # Pe RIGHT WHITE SQUARE BRACKET 301C;W # Pd WAVE DASH 301D;W # Ps REVERSED DOUBLE PRIME QUOTATION MARK 301E..301F;W # Pe [2] DOUBLE PRIME QUOTATION MARK..LOW DOUBLE PRIME QUOTATION MARK 3020;W # So POSTAL MARK FACE 3021..3029;W # Nl [9] HANGZHOU NUMERAL ONE..HANGZHOU NUMERAL NINE 302A..302D;W # Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK 302E..302F;W # Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK 3030;W # Pd WAVY DASH 3031..3035;W # Lm [5] VERTICAL KANA REPEAT MARK..VERTICAL KANA REPEAT MARK LOWER HALF 3036..3037;W # So [2] CIRCLED POSTAL MARK..IDEOGRAPHIC TELEGRAPH LINE FEED SEPARATOR SYMBOL 3038..303A;W # Nl [3] HANGZHOU NUMERAL TEN..HANGZHOU NUMERAL THIRTY 303B;W # Lm VERTICAL IDEOGRAPHIC ITERATION MARK 303C;W # Lo MASU MARK 303D;W # Po PART ALTERNATION MARK 303E;W # So IDEOGRAPHIC VARIATION INDICATOR 303F;N # So IDEOGRAPHIC HALF FILL SPACE 3041..3096;W # Lo [86] HIRAGANA LETTER SMALL A..HIRAGANA LETTER SMALL KE 3099..309A;W # Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK 309B..309C;W # Sk [2] KATAKANA-HIRAGANA VOICED SOUND MARK..KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK 309D..309E;W # Lm [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED ITERATION MARK 309F;W # Lo HIRAGANA DIGRAPH YORI 30A0;W # Pd KATAKANA-HIRAGANA DOUBLE HYPHEN 30A1..30FA;W # Lo [90] KATAKANA LETTER SMALL A..KATAKANA LETTER VO 30FB;W # Po KATAKANA MIDDLE DOT 30FC..30FE;W # Lm [3] KATAKANA-HIRAGANA PROLONGED SOUND MARK..KATAKANA VOICED ITERATION MARK 30FF;W # Lo KATAKANA DIGRAPH KOTO 3105..312F;W # Lo [43] BOPOMOFO LETTER B..BOPOMOFO LETTER NN 3131..318E;W # Lo [94] HANGUL LETTER KIYEOK..HANGUL LETTER ARAEAE 3190..3191;W # So [2] IDEOGRAPHIC ANNOTATION LINKING MARK..IDEOGRAPHIC ANNOTATION REVERSE MARK 3192..3195;W # No [4] IDEOGRAPHIC ANNOTATION ONE MARK..IDEOGRAPHIC ANNOTATION FOUR MARK 3196..319F;W # So [10] IDEOGRAPHIC ANNOTATION TOP MARK..IDEOGRAPHIC ANNOTATION MAN MARK 31A0..31BA;W # Lo [27] BOPOMOFO LETTER BU..BOPOMOFO LETTER ZY 31C0..31E3;W # So [36] CJK STROKE T..CJK STROKE Q 31F0..31FF;W # Lo [16] KATAKANA LETTER SMALL KU..KATAKANA LETTER SMALL RO 3200..321E;W # So [31] PARENTHESIZED HANGUL KIYEOK..PARENTHESIZED KOREAN CHARACTER O HU 3220..3229;W # No [10] PARENTHESIZED IDEOGRAPH ONE..PARENTHESIZED IDEOGRAPH TEN 322A..3247;W # So [30] PARENTHESIZED IDEOGRAPH MOON..CIRCLED IDEOGRAPH KOTO 3248..324F;A # No [8] CIRCLED NUMBER TEN ON BLACK SQUARE..CIRCLED NUMBER EIGHTY ON BLACK SQUARE 3250;W # So PARTNERSHIP SIGN 3251..325F;W # No [15] CIRCLED NUMBER TWENTY ONE..CIRCLED NUMBER THIRTY FIVE 3260..327F;W # So [32] CIRCLED HANGUL KIYEOK..KOREAN STANDARD SYMBOL 3280..3289;W # No [10] CIRCLED IDEOGRAPH ONE..CIRCLED IDEOGRAPH TEN 328A..32B0;W # So [39] CIRCLED IDEOGRAPH MOON..CIRCLED IDEOGRAPH NIGHT 32B1..32BF;W # No [15] CIRCLED NUMBER THIRTY SIX..CIRCLED NUMBER FIFTY 32C0..32FE;W # So [63] IDEOGRAPHIC TELEGRAPH SYMBOL FOR JANUARY..CIRCLED KATAKANA WO 3300..33FF;W # So [256] SQUARE APAATO..SQUARE GAL 3400..4DB5;W # Lo [6582] CJK UNIFIED IDEOGRAPH-3400..CJK UNIFIED IDEOGRAPH-4DB5 4DB6..4DBF;W # Cn [10] <reserved-4DB6>..<reserved-4DBF> 4DC0..4DFF;N # So [64] HEXAGRAM FOR THE CREATIVE HEAVEN..HEXAGRAM FOR BEFORE COMPLETION 4E00..9FEF;W # Lo [20976] CJK UNIFIED IDEOGRAPH-4E00..CJK UNIFIED IDEOGRAPH-9FEF 9FF0..9FFF;W # Cn [16] <reserved-9FF0>..<reserved-9FFF> A000..A014;W # Lo [21] YI SYLLABLE IT..YI SYLLABLE E A015;W # Lm YI SYLLABLE WU A016..A48C;W # Lo [1143] YI SYLLABLE BIT..YI SYLLABLE YYR A490..A4C6;W # So [55] YI RADICAL QOT..YI RADICAL KE A4D0..A4F7;N # Lo [40] LISU LETTER BA..LISU LETTER OE A4F8..A4FD;N # Lm [6] LISU LETTER TONE MYA TI..LISU LETTER TONE MYA JEU A4FE..A4FF;N # Po [2] LISU PUNCTUATION COMMA..LISU PUNCTUATION FULL STOP A500..A60B;N # Lo [268] VAI SYLLABLE EE..VAI SYLLABLE NG A60C;N # Lm VAI SYLLABLE LENGTHENER A60D..A60F;N # Po [3] VAI COMMA..VAI QUESTION MARK A610..A61F;N # Lo [16] VAI SYLLABLE NDOLE FA..VAI SYMBOL JONG A620..A629;N # Nd [10] VAI DIGIT ZERO..VAI DIGIT NINE A62A..A62B;N # Lo [2] VAI SYLLABLE NDOLE MA..VAI SYLLABLE NDOLE DO A640..A66D;N # L& [46] CYRILLIC CAPITAL LETTER ZEMLYA..CYRILLIC SMALL LETTER DOUBLE MONOCULAR O A66E;N # Lo CYRILLIC LETTER MULTIOCULAR O A66F;N # Mn COMBINING CYRILLIC VZMET A670..A672;N # Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN A673;N # Po SLAVONIC ASTERISK A674..A67D;N # Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK A67E;N # Po CYRILLIC KAVYKA A67F;N # Lm CYRILLIC PAYEROK A680..A69B;N # L& [28] CYRILLIC CAPITAL LETTER DWE..CYRILLIC SMALL LETTER CROSSED O A69C..A69D;N # Lm [2] MODIFIER LETTER CYRILLIC HARD SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN A69E..A69F;N # Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E A6A0..A6E5;N # Lo [70] BAMUM LETTER A..BAMUM LETTER KI A6E6..A6EF;N # Nl [10] BAMUM LETTER MO..BAMUM LETTER KOGHOM A6F0..A6F1;N # Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS A6F2..A6F7;N # Po [6] BAMUM NJAEMLI..BAMUM QUESTION MARK A700..A716;N # Sk [23] MODIFIER LETTER CHINESE TONE YIN PING..MODIFIER LETTER EXTRA-LOW LEFT-STEM TONE BAR A717..A71F;N # Lm [9] MODIFIER LETTER DOT VERTICAL BAR..MODIFIER LETTER LOW INVERTED EXCLAMATION MARK A720..A721;N # Sk [2] MODIFIER LETTER STRESS AND HIGH TONE..MODIFIER LETTER STRESS AND LOW TONE A722..A76F;N # L& [78] LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF..LATIN SMALL LETTER CON A770;N # Lm MODIFIER LETTER US A771..A787;N # L& [23] LATIN SMALL LETTER DUM..LATIN SMALL LETTER INSULAR T A788;N # Lm MODIFIER LETTER LOW CIRCUMFLEX ACCENT A789..A78A;N # Sk [2] MODIFIER LETTER COLON..MODIFIER LETTER SHORT EQUALS SIGN A78B..A78E;N # L& [4] LATIN CAPITAL LETTER SALTILLO..LATIN SMALL LETTER L WITH RETROFLEX HOOK AND BELT A78F;N # Lo LATIN LETTER SINOLOGICAL DOT A790..A7B9;N # L& [42] LATIN CAPITAL LETTER N WITH DESCENDER..LATIN SMALL LETTER U WITH STROKE A7F7;N # Lo LATIN EPIGRAPHIC LETTER SIDEWAYS I A7F8..A7F9;N # Lm [2] MODIFIER LETTER CAPITAL H WITH STROKE..MODIFIER LETTER SMALL LIGATURE OE A7FA;N # Ll LATIN LETTER SMALL CAPITAL TURNED M A7FB..A7FF;N # Lo [5] LATIN EPIGRAPHIC LETTER REVERSED F..LATIN EPIGRAPHIC LETTER ARCHAIC M A800..A801;N # Lo [2] SYLOTI NAGRI LETTER A..SYLOTI NAGRI LETTER I A802;N # Mn SYLOTI NAGRI SIGN DVISVARA A803..A805;N # Lo [3] SYLOTI NAGRI LETTER U..SYLOTI NAGRI LETTER O A806;N # Mn SYLOTI NAGRI SIGN HASANTA A807..A80A;N # Lo [4] SYLOTI NAGRI LETTER KO..SYLOTI NAGRI LETTER GHO A80B;N # Mn SYLOTI NAGRI SIGN ANUSVARA A80C..A822;N # Lo [23] SYLOTI NAGRI LETTER CO..SYLOTI NAGRI LETTER HO A823..A824;N # Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I A825..A826;N # Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E A827;N # Mc SYLOTI NAGRI VOWEL SIGN OO A828..A82B;N # So [4] SYLOTI NAGRI POETRY MARK-1..SYLOTI NAGRI POETRY MARK-4 A830..A835;N # No [6] NORTH INDIC FRACTION ONE QUARTER..NORTH INDIC FRACTION THREE SIXTEENTHS A836..A837;N # So [2] NORTH INDIC QUARTER MARK..NORTH INDIC PLACEHOLDER MARK A838;N # Sc NORTH INDIC RUPEE MARK A839;N # So NORTH INDIC QUANTITY MARK A840..A873;N # Lo [52] PHAGS-PA LETTER KA..PHAGS-PA LETTER CANDRABINDU A874..A877;N # Po [4] PHAGS-PA SINGLE HEAD MARK..PHAGS-PA MARK DOUBLE SHAD A880..A881;N # Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA A882..A8B3;N # Lo [50] SAURASHTRA LETTER A..SAURASHTRA LETTER LLA A8B4..A8C3;N # Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU A8C4..A8C5;N # Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU A8CE..A8CF;N # Po [2] SAURASHTRA DANDA..SAURASHTRA DOUBLE DANDA A8D0..A8D9;N # Nd [10] SAURASHTRA DIGIT ZERO..SAURASHTRA DIGIT NINE A8E0..A8F1;N # Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA A8F2..A8F7;N # Lo [6] DEVANAGARI SIGN SPACING CANDRABINDU..DEVANAGARI SIGN CANDRABINDU AVAGRAHA A8F8..A8FA;N # Po [3] DEVANAGARI SIGN PUSHPIKA..DEVANAGARI CARET A8FB;N # Lo DEVANAGARI HEADSTROKE A8FC;N # Po DEVANAGARI SIGN SIDDHAM A8FD..A8FE;N # Lo [2] DEVANAGARI JAIN OM..DEVANAGARI LETTER AY A8FF;N # Mn DEVANAGARI VOWEL SIGN AY A900..A909;N # Nd [10] KAYAH LI DIGIT ZERO..KAYAH LI DIGIT NINE A90A..A925;N # Lo [28] KAYAH LI LETTER KA..KAYAH LI LETTER OO A926..A92D;N # Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU A92E..A92F;N # Po [2] KAYAH LI SIGN CWI..KAYAH LI SIGN SHYA A930..A946;N # Lo [23] REJANG LETTER KA..REJANG LETTER A A947..A951;N # Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R A952..A953;N # Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA A95F;N # Po REJANG SECTION MARK A960..A97C;W # Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH A980..A982;N # Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR A983;N # Mc JAVANESE SIGN WIGNYAN A984..A9B2;N # Lo [47] JAVANESE LETTER A..JAVANESE LETTER HA A9B3;N # Mn JAVANESE SIGN CECAK TELU A9B4..A9B5;N # Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG A9B6..A9B9;N # Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT A9BA..A9BB;N # Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE A9BC;N # Mn JAVANESE VOWEL SIGN PEPET A9BD..A9C0;N # Mc [4] JAVANESE CONSONANT SIGN KERET..JAVANESE PANGKON A9C1..A9CD;N # Po [13] JAVANESE LEFT RERENGGAN..JAVANESE TURNED PADA PISELEH A9CF;N # Lm JAVANESE PANGRANGKEP A9D0..A9D9;N # Nd [10] JAVANESE DIGIT ZERO..JAVANESE DIGIT NINE A9DE..A9DF;N # Po [2] JAVANESE PADA TIRTA TUMETES..JAVANESE PADA ISEN-ISEN A9E0..A9E4;N # Lo [5] MYANMAR LETTER SHAN GHA..MYANMAR LETTER SHAN BHA A9E5;N # Mn MYANMAR SIGN SHAN SAW A9E6;N # Lm MYANMAR MODIFIER LETTER SHAN REDUPLICATION A9E7..A9EF;N # Lo [9] MYANMAR LETTER TAI LAING NYA..MYANMAR LETTER TAI LAING NNA A9F0..A9F9;N # Nd [10] MYANMAR TAI LAING DIGIT ZERO..MYANMAR TAI LAING DIGIT NINE A9FA..A9FE;N # Lo [5] MYANMAR LETTER TAI LAING LLA..MYANMAR LETTER TAI LAING BHA AA00..AA28;N # Lo [41] CHAM LETTER A..CHAM LETTER HA AA29..AA2E;N # Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE AA2F..AA30;N # Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI AA31..AA32;N # Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE AA33..AA34;N # Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA AA35..AA36;N # Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA AA40..AA42;N # Lo [3] CHAM LETTER FINAL K..CHAM LETTER FINAL NG AA43;N # Mn CHAM CONSONANT SIGN FINAL NG AA44..AA4B;N # Lo [8] CHAM LETTER FINAL CH..CHAM LETTER FINAL SS AA4C;N # Mn CHAM CONSONANT SIGN FINAL M AA4D;N # Mc CHAM CONSONANT SIGN FINAL H AA50..AA59;N # Nd [10] CHAM DIGIT ZERO..CHAM DIGIT NINE AA5C..AA5F;N # Po [4] CHAM PUNCTUATION SPIRAL..CHAM PUNCTUATION TRIPLE DANDA AA60..AA6F;N # Lo [16] MYANMAR LETTER KHAMTI GA..MYANMAR LETTER KHAMTI FA AA70;N # Lm MYANMAR MODIFIER LETTER KHAMTI REDUPLICATION AA71..AA76;N # Lo [6] MYANMAR LETTER KHAMTI XA..MYANMAR LOGOGRAM KHAMTI HM AA77..AA79;N # So [3] MYANMAR SYMBOL AITON EXCLAMATION..MYANMAR SYMBOL AITON TWO AA7A;N # Lo MYANMAR LETTER AITON RA AA7B;N # Mc MYANMAR SIGN PAO KAREN TONE AA7C;N # Mn MYANMAR SIGN TAI LAING TONE-2 AA7D;N # Mc MYANMAR SIGN TAI LAING TONE-5 AA7E..AA7F;N # Lo [2] MYANMAR LETTER SHWE PALAUNG CHA..MYANMAR LETTER SHWE PALAUNG SHA AA80..AAAF;N # Lo [48] TAI VIET LETTER LOW KO..TAI VIET LETTER HIGH O AAB0;N # Mn TAI VIET MAI KANG AAB1;N # Lo TAI VIET VOWEL AA AAB2..AAB4;N # Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U AAB5..AAB6;N # Lo [2] TAI VIET VOWEL E..TAI VIET VOWEL O AAB7..AAB8;N # Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA AAB9..AABD;N # Lo [5] TAI VIET VOWEL UEA..TAI VIET VOWEL AN AABE..AABF;N # Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK AAC0;N # Lo TAI VIET TONE MAI NUENG AAC1;N # Mn TAI VIET TONE MAI THO AAC2;N # Lo TAI VIET TONE MAI SONG AADB..AADC;N # Lo [2] TAI VIET SYMBOL KON..TAI VIET SYMBOL NUENG AADD;N # Lm TAI VIET SYMBOL SAM AADE..AADF;N # Po [2] TAI VIET SYMBOL HO HOI..TAI VIET SYMBOL KOI KOI AAE0..AAEA;N # Lo [11] MEETEI MAYEK LETTER E..MEETEI MAYEK LETTER SSA AAEB;N # Mc MEETEI MAYEK VOWEL SIGN II AAEC..AAED;N # Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI AAEE..AAEF;N # Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU AAF0..AAF1;N # Po [2] MEETEI MAYEK CHEIKHAN..MEETEI MAYEK AHANG KHUDAM AAF2;N # Lo MEETEI MAYEK ANJI AAF3..AAF4;N # Lm [2] MEETEI MAYEK SYLLABLE REPETITION MARK..MEETEI MAYEK WORD REPETITION MARK AAF5;N # Mc MEETEI MAYEK VOWEL SIGN VISARGA AAF6;N # Mn MEETEI MAYEK VIRAMA AB01..AB06;N # Lo [6] ETHIOPIC SYLLABLE TTHU..ETHIOPIC SYLLABLE TTHO AB09..AB0E;N # Lo [6] ETHIOPIC SYLLABLE DDHU..ETHIOPIC SYLLABLE DDHO AB11..AB16;N # Lo [6] ETHIOPIC SYLLABLE DZU..ETHIOPIC SYLLABLE DZO AB20..AB26;N # Lo [7] ETHIOPIC SYLLABLE CCHHA..ETHIOPIC SYLLABLE CCHHO AB28..AB2E;N # Lo [7] ETHIOPIC SYLLABLE BBA..ETHIOPIC SYLLABLE BBO AB30..AB5A;N # Ll [43] LATIN SMALL LETTER BARRED ALPHA..LATIN SMALL LETTER Y WITH SHORT RIGHT LEG AB5B;N # Sk MODIFIER BREVE WITH INVERTED BREVE AB5C..AB5F;N # Lm [4] MODIFIER LETTER SMALL HENG..MODIFIER LETTER SMALL U WITH LEFT HOOK AB60..AB65;N # Ll [6] LATIN SMALL LETTER SAKHA YAT..GREEK LETTER SMALL CAPITAL OMEGA AB70..ABBF;N # Ll [80] CHEROKEE SMALL LETTER A..CHEROKEE SMALL LETTER YA ABC0..ABE2;N # Lo [35] MEETEI MAYEK LETTER KOK..MEETEI MAYEK LETTER I LONSUM ABE3..ABE4;N # Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP ABE5;N # Mn MEETEI MAYEK VOWEL SIGN ANAP ABE6..ABE7;N # Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP ABE8;N # Mn MEETEI MAYEK VOWEL SIGN UNAP ABE9..ABEA;N # Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG ABEB;N # Po MEETEI MAYEK CHEIKHEI ABEC;N # Mc MEETEI MAYEK LUM IYEK ABED;N # Mn MEETEI MAYEK APUN IYEK ABF0..ABF9;N # Nd [10] MEETEI MAYEK DIGIT ZERO..MEETEI MAYEK DIGIT NINE AC00..D7A3;W # Lo [11172] HANGUL SYLLABLE GA..HANGUL SYLLABLE HIH D7B0..D7C6;N # Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E D7CB..D7FB;N # Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH D800..DB7F;N # Cs [896] <surrogate-D800>..<surrogate-DB7F> DB80..DBFF;N # Cs [128] <surrogate-DB80>..<surrogate-DBFF> DC00..DFFF;N # Cs [1024] <surrogate-DC00>..<surrogate-DFFF> E000..F8FF;A # Co [6400] <private-use-E000>..<private-use-F8FF> F900..FA6D;W # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900..CJK COMPATIBILITY IDEOGRAPH-FA6D FA6E..FA6F;W # Cn [2] <reserved-FA6E>..<reserved-FA6F> FA70..FAD9;W # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70..CJK COMPATIBILITY IDEOGRAPH-FAD9 FADA..FAFF;W # Cn [38] <reserved-FADA>..<reserved-FAFF> FB00..FB06;N # Ll [7] LATIN SMALL LIGATURE FF..LATIN SMALL LIGATURE ST FB13..FB17;N # Ll [5] ARMENIAN SMALL LIGATURE MEN NOW..ARMENIAN SMALL LIGATURE MEN XEH FB1D;N # Lo HEBREW LETTER YOD WITH HIRIQ FB1E;N # Mn HEBREW POINT JUDEO-SPANISH VARIKA FB1F..FB28;N # Lo [10] HEBREW LIGATURE YIDDISH YOD YOD PATAH..HEBREW LETTER WIDE TAV FB29;N # Sm HEBREW LETTER ALTERNATIVE PLUS SIGN FB2A..FB36;N # Lo [13] HEBREW LETTER SHIN WITH SHIN DOT..HEBREW LETTER ZAYIN WITH DAGESH FB38..FB3C;N # Lo [5] HEBREW LETTER TET WITH DAGESH..HEBREW LETTER LAMED WITH DAGESH FB3E;N # Lo HEBREW LETTER MEM WITH DAGESH FB40..FB41;N # Lo [2] HEBREW LETTER NUN WITH DAGESH..HEBREW LETTER SAMEKH WITH DAGESH FB43..FB44;N # Lo [2] HEBREW LETTER FINAL PE WITH DAGESH..HEBREW LETTER PE WITH DAGESH FB46..FB4F;N # Lo [10] HEBREW LETTER TSADI WITH DAGESH..HEBREW LIGATURE ALEF LAMED FB50..FBB1;N # Lo [98] ARABIC LETTER ALEF WASLA ISOLATED FORM..ARABIC LETTER YEH BARREE WITH HAMZA ABOVE FINAL FORM FBB2..FBC1;N # Sk [16] ARABIC SYMBOL DOT ABOVE..ARABIC SYMBOL SMALL TAH BELOW FBD3..FD3D;N # Lo [363] ARABIC LETTER NG ISOLATED FORM..ARABIC LIGATURE ALEF WITH FATHATAN ISOLATED FORM FD3E;N # Pe ORNATE LEFT PARENTHESIS FD3F;N # Ps ORNATE RIGHT PARENTHESIS FD50..FD8F;N # Lo [64] ARABIC LIGATURE TEH WITH JEEM WITH MEEM INITIAL FORM..ARABIC LIGATURE MEEM WITH KHAH WITH MEEM INITIAL FORM FD92..FDC7;N # Lo [54] ARABIC LIGATURE MEEM WITH JEEM WITH KHAH INITIAL FORM..ARABIC LIGATURE NOON WITH JEEM WITH YEH FINAL FORM FDF0..FDFB;N # Lo [12] ARABIC LIGATURE SALLA USED AS KORANIC STOP SIGN ISOLATED FORM..ARABIC LIGATURE JALLAJALALOUHOU FDFC;N # Sc RIAL SIGN FDFD;N # So ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM FE00..FE0F;A # Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16 FE10..FE16;W # Po [7] PRESENTATION FORM FOR VERTICAL COMMA..PRESENTATION FORM FOR VERTICAL QUESTION MARK FE17;W # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE LENTICULAR BRACKET FE18;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE LENTICULAR BRAKCET FE19;W # Po PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS FE20..FE2F;N # Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF FE30;W # Po PRESENTATION FORM FOR VERTICAL TWO DOT LEADER FE31..FE32;W # Pd [2] PRESENTATION FORM FOR VERTICAL EM DASH..PRESENTATION FORM FOR VERTICAL EN DASH FE33..FE34;W # Pc [2] PRESENTATION FORM FOR VERTICAL LOW LINE..PRESENTATION FORM FOR VERTICAL WAVY LOW LINE FE35;W # Ps PRESENTATION FORM FOR VERTICAL LEFT PARENTHESIS FE36;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT PARENTHESIS FE37;W # Ps PRESENTATION FORM FOR VERTICAL LEFT CURLY BRACKET FE38;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT CURLY BRACKET FE39;W # Ps PRESENTATION FORM FOR VERTICAL LEFT TORTOISE SHELL BRACKET FE3A;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT TORTOISE SHELL BRACKET FE3B;W # Ps PRESENTATION FORM FOR VERTICAL LEFT BLACK LENTICULAR BRACKET FE3C;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT BLACK LENTICULAR BRACKET FE3D;W # Ps PRESENTATION FORM FOR VERTICAL LEFT DOUBLE ANGLE BRACKET FE3E;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT DOUBLE ANGLE BRACKET FE3F;W # Ps PRESENTATION FORM FOR VERTICAL LEFT ANGLE BRACKET FE40;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT ANGLE BRACKET FE41;W # Ps PRESENTATION FORM FOR VERTICAL LEFT CORNER BRACKET FE42;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT CORNER BRACKET FE43;W # Ps PRESENTATION FORM FOR VERTICAL LEFT WHITE CORNER BRACKET FE44;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT WHITE CORNER BRACKET FE45..FE46;W # Po [2] SESAME DOT..WHITE SESAME DOT FE47;W # Ps PRESENTATION FORM FOR VERTICAL LEFT SQUARE BRACKET FE48;W # Pe PRESENTATION FORM FOR VERTICAL RIGHT SQUARE BRACKET FE49..FE4C;W # Po [4] DASHED OVERLINE..DOUBLE WAVY OVERLINE FE4D..FE4F;W # Pc [3] DASHED LOW LINE..WAVY LOW LINE FE50..FE52;W # Po [3] SMALL COMMA..SMALL FULL STOP FE54..FE57;W # Po [4] SMALL SEMICOLON..SMALL EXCLAMATION MARK FE58;W # Pd SMALL EM DASH FE59;W # Ps SMALL LEFT PARENTHESIS FE5A;W # Pe SMALL RIGHT PARENTHESIS FE5B;W # Ps SMALL LEFT CURLY BRACKET FE5C;W # Pe SMALL RIGHT CURLY BRACKET FE5D;W # Ps SMALL LEFT TORTOISE SHELL BRACKET FE5E;W # Pe SMALL RIGHT TORTOISE SHELL BRACKET FE5F..FE61;W # Po [3] SMALL NUMBER SIGN..SMALL ASTERISK FE62;W # Sm SMALL PLUS SIGN FE63;W # Pd SMALL HYPHEN-MINUS FE64..FE66;W # Sm [3] SMALL LESS-THAN SIGN..SMALL EQUALS SIGN FE68;W # Po SMALL REVERSE SOLIDUS FE69;W # Sc SMALL DOLLAR SIGN FE6A..FE6B;W # Po [2] SMALL PERCENT SIGN..SMALL COMMERCIAL AT FE70..FE74;N # Lo [5] ARABIC FATHATAN ISOLATED FORM..ARABIC KASRATAN ISOLATED FORM FE76..FEFC;N # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE LAM WITH ALEF FINAL FORM FEFF;N # Cf ZERO WIDTH NO-BREAK SPACE FF01..FF03;F # Po [3] FULLWIDTH EXCLAMATION MARK..FULLWIDTH NUMBER SIGN FF04;F # Sc FULLWIDTH DOLLAR SIGN FF05..FF07;F # Po [3] FULLWIDTH PERCENT SIGN..FULLWIDTH APOSTROPHE FF08;F # Ps FULLWIDTH LEFT PARENTHESIS FF09;F # Pe FULLWIDTH RIGHT PARENTHESIS FF0A;F # Po FULLWIDTH ASTERISK FF0B;F # Sm FULLWIDTH PLUS SIGN FF0C;F # Po FULLWIDTH COMMA FF0D;F # Pd FULLWIDTH HYPHEN-MINUS FF0E..FF0F;F # Po [2] FULLWIDTH FULL STOP..FULLWIDTH SOLIDUS FF10..FF19;F # Nd [10] FULLWIDTH DIGIT ZERO..FULLWIDTH DIGIT NINE FF1A..FF1B;F # Po [2] FULLWIDTH COLON..FULLWIDTH SEMICOLON FF1C..FF1E;F # Sm [3] FULLWIDTH LESS-THAN SIGN..FULLWIDTH GREATER-THAN SIGN FF1F..FF20;F # Po [2] FULLWIDTH QUESTION MARK..FULLWIDTH COMMERCIAL AT FF21..FF3A;F # Lu [26] FULLWIDTH LATIN CAPITAL LETTER A..FULLWIDTH LATIN CAPITAL LETTER Z FF3B;F # Ps FULLWIDTH LEFT SQUARE BRACKET FF3C;F # Po FULLWIDTH REVERSE SOLIDUS FF3D;F # Pe FULLWIDTH RIGHT SQUARE BRACKET FF3E;F # Sk FULLWIDTH CIRCUMFLEX ACCENT FF3F;F # Pc FULLWIDTH LOW LINE FF40;F # Sk FULLWIDTH GRAVE ACCENT FF41..FF5A;F # Ll [26] FULLWIDTH LATIN SMALL LETTER A..FULLWIDTH LATIN SMALL LETTER Z FF5B;F # Ps FULLWIDTH LEFT CURLY BRACKET FF5C;F # Sm FULLWIDTH VERTICAL LINE FF5D;F # Pe FULLWIDTH RIGHT CURLY BRACKET FF5E;F # Sm FULLWIDTH TILDE FF5F;F # Ps FULLWIDTH LEFT WHITE PARENTHESIS FF60;F # Pe FULLWIDTH RIGHT WHITE PARENTHESIS FF61;H # Po HALFWIDTH IDEOGRAPHIC FULL STOP FF62;H # Ps HALFWIDTH LEFT CORNER BRACKET FF63;H # Pe HALFWIDTH RIGHT CORNER BRACKET FF64..FF65;H # Po [2] HALFWIDTH IDEOGRAPHIC COMMA..HALFWIDTH KATAKANA MIDDLE DOT FF66..FF6F;H # Lo [10] HALFWIDTH KATAKANA LETTER WO..HALFWIDTH KATAKANA LETTER SMALL TU FF70;H # Lm HALFWIDTH KATAKANA-HIRAGANA PROLONGED SOUND MARK FF71..FF9D;H # Lo [45] HALFWIDTH KATAKANA LETTER A..HALFWIDTH KATAKANA LETTER N FF9E..FF9F;H # Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK FFA0..FFBE;H # Lo [31] HALFWIDTH HANGUL FILLER..HALFWIDTH HANGUL LETTER HIEUH FFC2..FFC7;H # Lo [6] HALFWIDTH HANGUL LETTER A..HALFWIDTH HANGUL LETTER E FFCA..FFCF;H # Lo [6] HALFWIDTH HANGUL LETTER YEO..HALFWIDTH HANGUL LETTER OE FFD2..FFD7;H # Lo [6] HALFWIDTH HANGUL LETTER YO..HALFWIDTH HANGUL LETTER YU FFDA..FFDC;H # Lo [3] HALFWIDTH HANGUL LETTER EU..HALFWIDTH HANGUL LETTER I FFE0..FFE1;F # Sc [2] FULLWIDTH CENT SIGN..FULLWIDTH POUND SIGN FFE2;F # Sm FULLWIDTH NOT SIGN FFE3;F # Sk FULLWIDTH MACRON FFE4;F # So FULLWIDTH BROKEN BAR FFE5..FFE6;F # Sc [2] FULLWIDTH YEN SIGN..FULLWIDTH WON SIGN FFE8;H # So HALFWIDTH FORMS LIGHT VERTICAL FFE9..FFEC;H # Sm [4] HALFWIDTH LEFTWARDS ARROW..HALFWIDTH DOWNWARDS ARROW FFED..FFEE;H # So [2] HALFWIDTH BLACK SQUARE..HALFWIDTH WHITE CIRCLE FFF9..FFFB;N # Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR FFFC;N # So OBJECT REPLACEMENT CHARACTER FFFD;A # So REPLACEMENT CHARACTER 10000..1000B;N # Lo [12] LINEAR B SYLLABLE B008 A..LINEAR B SYLLABLE B046 JE 1000D..10026;N # Lo [26] LINEAR B SYLLABLE B036 JO..LINEAR B SYLLABLE B032 QO 10028..1003A;N # Lo [19] LINEAR B SYLLABLE B060 RA..LINEAR B SYLLABLE B042 WO 1003C..1003D;N # Lo [2] LINEAR B SYLLABLE B017 ZA..LINEAR B SYLLABLE B074 ZE 1003F..1004D;N # Lo [15] LINEAR B SYLLABLE B020 ZO..LINEAR B SYLLABLE B091 TWO 10050..1005D;N # Lo [14] LINEAR B SYMBOL B018..LINEAR B SYMBOL B089 10080..100FA;N # Lo [123] LINEAR B IDEOGRAM B100 MAN..LINEAR B IDEOGRAM VESSEL B305 10100..10102;N # Po [3] AEGEAN WORD SEPARATOR LINE..AEGEAN CHECK MARK 10107..10133;N # No [45] AEGEAN NUMBER ONE..AEGEAN NUMBER NINETY THOUSAND 10137..1013F;N # So [9] AEGEAN WEIGHT BASE UNIT..AEGEAN MEASURE THIRD SUBUNIT 10140..10174;N # Nl [53] GREEK ACROPHONIC ATTIC ONE QUARTER..GREEK ACROPHONIC STRATIAN FIFTY MNAS 10175..10178;N # No [4] GREEK ONE HALF SIGN..GREEK THREE QUARTERS SIGN 10179..10189;N # So [17] GREEK YEAR SIGN..GREEK TRYBLION BASE SIGN 1018A..1018B;N # No [2] GREEK ZERO SIGN..GREEK ONE QUARTER SIGN 1018C..1018E;N # So [3] GREEK SINUSOID SIGN..NOMISMA SIGN 10190..1019B;N # So [12] ROMAN SEXTANS SIGN..ROMAN CENTURIAL SIGN 101A0;N # So GREEK SYMBOL TAU RHO 101D0..101FC;N # So [45] PHAISTOS DISC SIGN PEDESTRIAN..PHAISTOS DISC SIGN WAVY BAND 101FD;N # Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE 10280..1029C;N # Lo [29] LYCIAN LETTER A..LYCIAN LETTER X 102A0..102D0;N # Lo [49] CARIAN LETTER A..CARIAN LETTER UUU3 102E0;N # Mn COPTIC EPACT THOUSANDS MARK 102E1..102FB;N # No [27] COPTIC EPACT DIGIT ONE..COPTIC EPACT NUMBER NINE HUNDRED 10300..1031F;N # Lo [32] OLD ITALIC LETTER A..OLD ITALIC LETTER ESS 10320..10323;N # No [4] OLD ITALIC NUMERAL ONE..OLD ITALIC NUMERAL FIFTY 1032D..1032F;N # Lo [3] OLD ITALIC LETTER YE..OLD ITALIC LETTER SOUTHERN TSE 10330..10340;N # Lo [17] GOTHIC LETTER AHSA..GOTHIC LETTER PAIRTHRA 10341;N # Nl GOTHIC LETTER NINETY 10342..10349;N # Lo [8] GOTHIC LETTER RAIDA..GOTHIC LETTER OTHAL 1034A;N # Nl GOTHIC LETTER NINE HUNDRED 10350..10375;N # Lo [38] OLD PERMIC LETTER AN..OLD PERMIC LETTER IA 10376..1037A;N # Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII 10380..1039D;N # Lo [30] UGARITIC LETTER ALPA..UGARITIC LETTER SSU 1039F;N # Po UGARITIC WORD DIVIDER 103A0..103C3;N # Lo [36] OLD PERSIAN SIGN A..OLD PERSIAN SIGN HA 103C8..103CF;N # Lo [8] OLD PERSIAN SIGN AURAMAZDAA..OLD PERSIAN SIGN BUUMISH 103D0;N # Po OLD PERSIAN WORD DIVIDER 103D1..103D5;N # Nl [5] OLD PERSIAN NUMBER ONE..OLD PERSIAN NUMBER HUNDRED 10400..1044F;N # L& [80] DESERET CAPITAL LETTER LONG I..DESERET SMALL LETTER EW 10450..1047F;N # Lo [48] SHAVIAN LETTER PEEP..SHAVIAN LETTER YEW 10480..1049D;N # Lo [30] OSMANYA LETTER ALEF..OSMANYA LETTER OO 104A0..104A9;N # Nd [10] OSMANYA DIGIT ZERO..OSMANYA DIGIT NINE 104B0..104D3;N # Lu [36] OSAGE CAPITAL LETTER A..OSAGE CAPITAL LETTER ZHA 104D8..104FB;N # Ll [36] OSAGE SMALL LETTER A..OSAGE SMALL LETTER ZHA 10500..10527;N # Lo [40] ELBASAN LETTER A..ELBASAN LETTER KHE 10530..10563;N # Lo [52] CAUCASIAN ALBANIAN LETTER ALT..CAUCASIAN ALBANIAN LETTER KIW 1056F;N # Po CAUCASIAN ALBANIAN CITATION MARK 10600..10736;N # Lo [311] LINEAR A SIGN AB001..LINEAR A SIGN A664 10740..10755;N # Lo [22] LINEAR A SIGN A701 A..LINEAR A SIGN A732 JE 10760..10767;N # Lo [8] LINEAR A SIGN A800..LINEAR A SIGN A807 10800..10805;N # Lo [6] CYPRIOT SYLLABLE A..CYPRIOT SYLLABLE JA 10808;N # Lo CYPRIOT SYLLABLE JO 1080A..10835;N # Lo [44] CYPRIOT SYLLABLE KA..CYPRIOT SYLLABLE WO 10837..10838;N # Lo [2] CYPRIOT SYLLABLE XA..CYPRIOT SYLLABLE XE 1083C;N # Lo CYPRIOT SYLLABLE ZA 1083F;N # Lo CYPRIOT SYLLABLE ZO 10840..10855;N # Lo [22] IMPERIAL ARAMAIC LETTER ALEPH..IMPERIAL ARAMAIC LETTER TAW 10857;N # Po IMPERIAL ARAMAIC SECTION SIGN 10858..1085F;N # No [8] IMPERIAL ARAMAIC NUMBER ONE..IMPERIAL ARAMAIC NUMBER TEN THOUSAND 10860..10876;N # Lo [23] PALMYRENE LETTER ALEPH..PALMYRENE LETTER TAW 10877..10878;N # So [2] PALMYRENE LEFT-POINTING FLEURON..PALMYRENE RIGHT-POINTING FLEURON 10879..1087F;N # No [7] PALMYRENE NUMBER ONE..PALMYRENE NUMBER TWENTY 10880..1089E;N # Lo [31] NABATAEAN LETTER FINAL ALEPH..NABATAEAN LETTER TAW 108A7..108AF;N # No [9] NABATAEAN NUMBER ONE..NABATAEAN NUMBER ONE HUNDRED 108E0..108F2;N # Lo [19] HATRAN LETTER ALEPH..HATRAN LETTER QOPH 108F4..108F5;N # Lo [2] HATRAN LETTER SHIN..HATRAN LETTER TAW 108FB..108FF;N # No [5] HATRAN NUMBER ONE..HATRAN NUMBER ONE HUNDRED 10900..10915;N # Lo [22] PHOENICIAN LETTER ALF..PHOENICIAN LETTER TAU 10916..1091B;N # No [6] PHOENICIAN NUMBER ONE..PHOENICIAN NUMBER THREE 1091F;N # Po PHOENICIAN WORD SEPARATOR 10920..10939;N # Lo [26] LYDIAN LETTER A..LYDIAN LETTER C 1093F;N # Po LYDIAN TRIANGULAR MARK 10980..1099F;N # Lo [32] MEROITIC HIEROGLYPHIC LETTER A..MEROITIC HIEROGLYPHIC SYMBOL VIDJ-2 109A0..109B7;N # Lo [24] MEROITIC CURSIVE LETTER A..MEROITIC CURSIVE LETTER DA 109BC..109BD;N # No [2] MEROITIC CURSIVE FRACTION ELEVEN TWELFTHS..MEROITIC CURSIVE FRACTION ONE HALF 109BE..109BF;N # Lo [2] MEROITIC CURSIVE LOGOGRAM RMT..MEROITIC CURSIVE LOGOGRAM IMN 109C0..109CF;N # No [16] MEROITIC CURSIVE NUMBER ONE..MEROITIC CURSIVE NUMBER SEVENTY 109D2..109FF;N # No [46] MEROITIC CURSIVE NUMBER ONE HUNDRED..MEROITIC CURSIVE FRACTION TEN TWELFTHS 10A00;N # Lo KHAROSHTHI LETTER A 10A01..10A03;N # Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R 10A05..10A06;N # Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O 10A0C..10A0F;N # Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA 10A10..10A13;N # Lo [4] KHAROSHTHI LETTER KA..KHAROSHTHI LETTER GHA 10A15..10A17;N # Lo [3] KHAROSHTHI LETTER CA..KHAROSHTHI LETTER JA 10A19..10A35;N # Lo [29] KHAROSHTHI LETTER NYA..KHAROSHTHI LETTER VHA 10A38..10A3A;N # Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW 10A3F;N # Mn KHAROSHTHI VIRAMA 10A40..10A48;N # No [9] KHAROSHTHI DIGIT ONE..KHAROSHTHI FRACTION ONE HALF 10A50..10A58;N # Po [9] KHAROSHTHI PUNCTUATION DOT..KHAROSHTHI PUNCTUATION LINES 10A60..10A7C;N # Lo [29] OLD SOUTH ARABIAN LETTER HE..OLD SOUTH ARABIAN LETTER THETH 10A7D..10A7E;N # No [2] OLD SOUTH ARABIAN NUMBER ONE..OLD SOUTH ARABIAN NUMBER FIFTY 10A7F;N # Po OLD SOUTH ARABIAN NUMERIC INDICATOR 10A80..10A9C;N # Lo [29] OLD NORTH ARABIAN LETTER HEH..OLD NORTH ARABIAN LETTER ZAH 10A9D..10A9F;N # No [3] OLD NORTH ARABIAN NUMBER ONE..OLD NORTH ARABIAN NUMBER TWENTY 10AC0..10AC7;N # Lo [8] MANICHAEAN LETTER ALEPH..MANICHAEAN LETTER WAW 10AC8;N # So MANICHAEAN SIGN UD 10AC9..10AE4;N # Lo [28] MANICHAEAN LETTER ZAYIN..MANICHAEAN LETTER TAW 10AE5..10AE6;N # Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW 10AEB..10AEF;N # No [5] MANICHAEAN NUMBER ONE..MANICHAEAN NUMBER ONE HUNDRED 10AF0..10AF6;N # Po [7] MANICHAEAN PUNCTUATION STAR..MANICHAEAN PUNCTUATION LINE FILLER 10B00..10B35;N # Lo [54] AVESTAN LETTER A..AVESTAN LETTER HE 10B39..10B3F;N # Po [7] AVESTAN ABBREVIATION MARK..LARGE ONE RING OVER TWO RINGS PUNCTUATION 10B40..10B55;N # Lo [22] INSCRIPTIONAL PARTHIAN LETTER ALEPH..INSCRIPTIONAL PARTHIAN LETTER TAW 10B58..10B5F;N # No [8] INSCRIPTIONAL PARTHIAN NUMBER ONE..INSCRIPTIONAL PARTHIAN NUMBER ONE THOUSAND 10B60..10B72;N # Lo [19] INSCRIPTIONAL PAHLAVI LETTER ALEPH..INSCRIPTIONAL PAHLAVI LETTER TAW 10B78..10B7F;N # No [8] INSCRIPTIONAL PAHLAVI NUMBER ONE..INSCRIPTIONAL PAHLAVI NUMBER ONE THOUSAND 10B80..10B91;N # Lo [18] PSALTER PAHLAVI LETTER ALEPH..PSALTER PAHLAVI LETTER TAW 10B99..10B9C;N # Po [4] PSALTER PAHLAVI SECTION MARK..PSALTER PAHLAVI FOUR DOTS WITH DOT 10BA9..10BAF;N # No [7] PSALTER PAHLAVI NUMBER ONE..PSALTER PAHLAVI NUMBER ONE HUNDRED 10C00..10C48;N # Lo [73] OLD TURKIC LETTER ORKHON A..OLD TURKIC LETTER ORKHON BASH 10C80..10CB2;N # Lu [51] OLD HUNGARIAN CAPITAL LETTER A..OLD HUNGARIAN CAPITAL LETTER US 10CC0..10CF2;N # Ll [51] OLD HUNGARIAN SMALL LETTER A..OLD HUNGARIAN SMALL LETTER US 10CFA..10CFF;N # No [6] OLD HUNGARIAN NUMBER ONE..OLD HUNGARIAN NUMBER ONE THOUSAND 10D00..10D23;N # Lo [36] HANIFI ROHINGYA LETTER A..HANIFI ROHINGYA MARK NA KHONNA 10D24..10D27;N # Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI 10D30..10D39;N # Nd [10] HANIFI ROHINGYA DIGIT ZERO..HANIFI ROHINGYA DIGIT NINE 10E60..10E7E;N # No [31] RUMI DIGIT ONE..RUMI FRACTION TWO THIRDS 10F00..10F1C;N # Lo [29] OLD SOGDIAN LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL 10F1D..10F26;N # No [10] OLD SOGDIAN NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF 10F27;N # Lo OLD SOGDIAN LIGATURE AYIN-DALETH 10F30..10F45;N # Lo [22] SOGDIAN LETTER ALEPH..SOGDIAN INDEPENDENT SHIN 10F46..10F50;N # Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW 10F51..10F54;N # No [4] SOGDIAN NUMBER ONE..SOGDIAN NUMBER ONE HUNDRED 10F55..10F59;N # Po [5] SOGDIAN PUNCTUATION TWO VERTICAL BARS..SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT 11000;N # Mc BRAHMI SIGN CANDRABINDU 11001;N # Mn BRAHMI SIGN ANUSVARA 11002;N # Mc BRAHMI SIGN VISARGA 11003..11037;N # Lo [53] BRAHMI SIGN JIHVAMULIYA..BRAHMI LETTER OLD TAMIL NNNA 11038..11046;N # Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA 11047..1104D;N # Po [7] BRAHMI DANDA..BRAHMI PUNCTUATION LOTUS 11052..11065;N # No [20] BRAHMI NUMBER ONE..BRAHMI NUMBER ONE THOUSAND 11066..1106F;N # Nd [10] BRAHMI DIGIT ZERO..BRAHMI DIGIT NINE 1107F;N # Mn BRAHMI NUMBER JOINER 11080..11081;N # Mn [2] KAITHI SIGN CANDRABINDU..KAITHI SIGN ANUSVARA 11082;N # Mc KAITHI SIGN VISARGA 11083..110AF;N # Lo [45] KAITHI LETTER A..KAITHI LETTER HA 110B0..110B2;N # Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II 110B3..110B6;N # Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI 110B7..110B8;N # Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU 110B9..110BA;N # Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA 110BB..110BC;N # Po [2] KAITHI ABBREVIATION SIGN..KAITHI ENUMERATION SIGN 110BD;N # Cf KAITHI NUMBER SIGN 110BE..110C1;N # Po [4] KAITHI SECTION MARK..KAITHI DOUBLE DANDA 110CD;N # Cf KAITHI NUMBER SIGN ABOVE 110D0..110E8;N # Lo [25] SORA SOMPENG LETTER SAH..SORA SOMPENG LETTER MAE 110F0..110F9;N # Nd [10] SORA SOMPENG DIGIT ZERO..SORA SOMPENG DIGIT NINE 11100..11102;N # Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA 11103..11126;N # Lo [36] CHAKMA LETTER AA..CHAKMA LETTER HAA 11127..1112B;N # Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU 1112C;N # Mc CHAKMA VOWEL SIGN E 1112D..11134;N # Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA 11136..1113F;N # Nd [10] CHAKMA DIGIT ZERO..CHAKMA DIGIT NINE 11140..11143;N # Po [4] CHAKMA SECTION MARK..CHAKMA QUESTION MARK 11144;N # Lo CHAKMA LETTER LHAA 11145..11146;N # Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI 11150..11172;N # Lo [35] MAHAJANI LETTER A..MAHAJANI LETTER RRA 11173;N # Mn MAHAJANI SIGN NUKTA 11174..11175;N # Po [2] MAHAJANI ABBREVIATION SIGN..MAHAJANI SECTION MARK 11176;N # Lo MAHAJANI LIGATURE SHRI 11180..11181;N # Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA 11182;N # Mc SHARADA SIGN VISARGA 11183..111B2;N # Lo [48] SHARADA LETTER A..SHARADA LETTER HA 111B3..111B5;N # Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II 111B6..111BE;N # Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O 111BF..111C0;N # Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA 111C1..111C4;N # Lo [4] SHARADA SIGN AVAGRAHA..SHARADA OM 111C5..111C8;N # Po [4] SHARADA DANDA..SHARADA SEPARATOR 111C9..111CC;N # Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK 111CD;N # Po SHARADA SUTRA MARK 111D0..111D9;N # Nd [10] SHARADA DIGIT ZERO..SHARADA DIGIT NINE 111DA;N # Lo SHARADA EKAM 111DB;N # Po SHARADA SIGN SIDDHAM 111DC;N # Lo SHARADA HEADSTROKE 111DD..111DF;N # Po [3] SHARADA CONTINUATION SIGN..SHARADA SECTION MARK-2 111E1..111F4;N # No [20] SINHALA ARCHAIC DIGIT ONE..SINHALA ARCHAIC NUMBER ONE THOUSAND 11200..11211;N # Lo [18] KHOJKI LETTER A..KHOJKI LETTER JJA 11213..1122B;N # Lo [25] KHOJKI LETTER NYA..KHOJKI LETTER LLA 1122C..1122E;N # Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II 1122F..11231;N # Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI 11232..11233;N # Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU 11234;N # Mn KHOJKI SIGN ANUSVARA 11235;N # Mc KHOJKI SIGN VIRAMA 11236..11237;N # Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA 11238..1123D;N # Po [6] KHOJKI DANDA..KHOJKI ABBREVIATION SIGN 1123E;N # Mn KHOJKI SIGN SUKUN 11280..11286;N # Lo [7] MULTANI LETTER A..MULTANI LETTER GA 11288;N # Lo MULTANI LETTER GHA 1128A..1128D;N # Lo [4] MULTANI LETTER CA..MULTANI LETTER JJA 1128F..1129D;N # Lo [15] MULTANI LETTER NYA..MULTANI LETTER BA 1129F..112A8;N # Lo [10] MULTANI LETTER BHA..MULTANI LETTER RHA 112A9;N # Po MULTANI SECTION MARK 112B0..112DE;N # Lo [47] KHUDAWADI LETTER A..KHUDAWADI LETTER HA 112DF;N # Mn KHUDAWADI SIGN ANUSVARA 112E0..112E2;N # Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II 112E3..112EA;N # Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA 112F0..112F9;N # Nd [10] KHUDAWADI DIGIT ZERO..KHUDAWADI DIGIT NINE 11300..11301;N # Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU 11302..11303;N # Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA 11305..1130C;N # Lo [8] GRANTHA LETTER A..GRANTHA LETTER VOCALIC L 1130F..11310;N # Lo [2] GRANTHA LETTER EE..GRANTHA LETTER AI 11313..11328;N # Lo [22] GRANTHA LETTER OO..GRANTHA LETTER NA 1132A..11330;N # Lo [7] GRANTHA LETTER PA..GRANTHA LETTER RA 11332..11333;N # Lo [2] GRANTHA LETTER LA..GRANTHA LETTER LLA 11335..11339;N # Lo [5] GRANTHA LETTER VA..GRANTHA LETTER HA 1133B..1133C;N # Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA 1133D;N # Lo GRANTHA SIGN AVAGRAHA 1133E..1133F;N # Mc [2] GRANTHA VOWEL SIGN AA..GRANTHA VOWEL SIGN I 11340;N # Mn GRANTHA VOWEL SIGN II 11341..11344;N # Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR 11347..11348;N # Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI 1134B..1134D;N # Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA 11350;N # Lo GRANTHA OM 11357;N # Mc GRANTHA AU LENGTH MARK 1135D..11361;N # Lo [5] GRANTHA SIGN PLUTA..GRANTHA LETTER VOCALIC LL 11362..11363;N # Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL 11366..1136C;N # Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX 11370..11374;N # Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA 11400..11434;N # Lo [53] NEWA LETTER A..NEWA LETTER HA 11435..11437;N # Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II 11438..1143F;N # Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI 11440..11441;N # Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU 11442..11444;N # Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA 11445;N # Mc NEWA SIGN VISARGA 11446;N # Mn NEWA SIGN NUKTA 11447..1144A;N # Lo [4] NEWA SIGN AVAGRAHA..NEWA SIDDHI 1144B..1144F;N # Po [5] NEWA DANDA..NEWA ABBREVIATION SIGN 11450..11459;N # Nd [10] NEWA DIGIT ZERO..NEWA DIGIT NINE 1145B;N # Po NEWA PLACEHOLDER MARK 1145D;N # Po NEWA INSERTION SIGN 1145E;N # Mn NEWA SANDHI MARK 11480..114AF;N # Lo [48] TIRHUTA ANJI..TIRHUTA LETTER HA 114B0..114B2;N # Mc [3] TIRHUTA VOWEL SIGN AA..TIRHUTA VOWEL SIGN II 114B3..114B8;N # Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL 114B9;N # Mc TIRHUTA VOWEL SIGN E 114BA;N # Mn TIRHUTA VOWEL SIGN SHORT E 114BB..114BE;N # Mc [4] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN AU 114BF..114C0;N # Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA 114C1;N # Mc TIRHUTA SIGN VISARGA 114C2..114C3;N # Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA 114C4..114C5;N # Lo [2] TIRHUTA SIGN AVAGRAHA..TIRHUTA GVANG 114C6;N # Po TIRHUTA ABBREVIATION SIGN 114C7;N # Lo TIRHUTA OM 114D0..114D9;N # Nd [10] TIRHUTA DIGIT ZERO..TIRHUTA DIGIT NINE 11580..115AE;N # Lo [47] SIDDHAM LETTER A..SIDDHAM LETTER HA 115AF..115B1;N # Mc [3] SIDDHAM VOWEL SIGN AA..SIDDHAM VOWEL SIGN II 115B2..115B5;N # Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR 115B8..115BB;N # Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU 115BC..115BD;N # Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA 115BE;N # Mc SIDDHAM SIGN VISARGA 115BF..115C0;N # Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA 115C1..115D7;N # Po [23] SIDDHAM SIGN SIDDHAM..SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES 115D8..115DB;N # Lo [4] SIDDHAM LETTER THREE-CIRCLE ALTERNATE I..SIDDHAM LETTER ALTERNATE U 115DC..115DD;N # Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU 11600..1162F;N # Lo [48] MODI LETTER A..MODI LETTER LLA 11630..11632;N # Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II 11633..1163A;N # Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI 1163B..1163C;N # Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU 1163D;N # Mn MODI SIGN ANUSVARA 1163E;N # Mc MODI SIGN VISARGA 1163F..11640;N # Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA 11641..11643;N # Po [3] MODI DANDA..MODI ABBREVIATION SIGN 11644;N # Lo MODI SIGN HUVA 11650..11659;N # Nd [10] MODI DIGIT ZERO..MODI DIGIT NINE 11660..1166C;N # Po [13] MONGOLIAN BIRGA WITH ORNAMENT..MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT 11680..116AA;N # Lo [43] TAKRI LETTER A..TAKRI LETTER RRA 116AB;N # Mn TAKRI SIGN ANUSVARA 116AC;N # Mc TAKRI SIGN VISARGA 116AD;N # Mn TAKRI VOWEL SIGN AA 116AE..116AF;N # Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II 116B0..116B5;N # Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU 116B6;N # Mc TAKRI SIGN VIRAMA 116B7;N # Mn TAKRI SIGN NUKTA 116C0..116C9;N # Nd [10] TAKRI DIGIT ZERO..TAKRI DIGIT NINE 11700..1171A;N # Lo [27] AHOM LETTER KA..AHOM LETTER ALTERNATE BA 1171D..1171F;N # Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA 11720..11721;N # Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA 11722..11725;N # Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU 11726;N # Mc AHOM VOWEL SIGN E 11727..1172B;N # Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER 11730..11739;N # Nd [10] AHOM DIGIT ZERO..AHOM DIGIT NINE 1173A..1173B;N # No [2] AHOM NUMBER TEN..AHOM NUMBER TWENTY 1173C..1173E;N # Po [3] AHOM SIGN SMALL SECTION..AHOM SIGN RULAI 1173F;N # So AHOM SYMBOL VI 11800..1182B;N # Lo [44] DOGRA LETTER A..DOGRA LETTER RRA 1182C..1182E;N # Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II 1182F..11837;N # Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA 11838;N # Mc DOGRA SIGN VISARGA 11839..1183A;N # Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA 1183B;N # Po DOGRA ABBREVIATION SIGN 118A0..118DF;N # L& [64] WARANG CITI CAPITAL LETTER NGAA..WARANG CITI SMALL LETTER VIYO 118E0..118E9;N # Nd [10] WARANG CITI DIGIT ZERO..WARANG CITI DIGIT NINE 118EA..118F2;N # No [9] WARANG CITI NUMBER TEN..WARANG CITI NUMBER NINETY 118FF;N # Lo WARANG CITI OM 11A00;N # Lo ZANABAZAR SQUARE LETTER A 11A01..11A0A;N # Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK 11A0B..11A32;N # Lo [40] ZANABAZAR SQUARE LETTER KA..ZANABAZAR SQUARE LETTER KSSA 11A33..11A38;N # Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA 11A39;N # Mc ZANABAZAR SQUARE SIGN VISARGA 11A3A;N # Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA 11A3B..11A3E;N # Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA 11A3F..11A46;N # Po [8] ZANABAZAR SQUARE INITIAL HEAD MARK..ZANABAZAR SQUARE CLOSING DOUBLE-LINED HEAD MARK 11A47;N # Mn ZANABAZAR SQUARE SUBJOINER 11A50;N # Lo SOYOMBO LETTER A 11A51..11A56;N # Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE 11A57..11A58;N # Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU 11A59..11A5B;N # Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK 11A5C..11A83;N # Lo [40] SOYOMBO LETTER KA..SOYOMBO LETTER KSSA 11A86..11A89;N # Lo [4] SOYOMBO CLUSTER-INITIAL LETTER RA..SOYOMBO CLUSTER-INITIAL LETTER SA 11A8A..11A96;N # Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA 11A97;N # Mc SOYOMBO SIGN VISARGA 11A98..11A99;N # Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER 11A9A..11A9C;N # Po [3] SOYOMBO MARK TSHEG..SOYOMBO MARK DOUBLE SHAD 11A9D;N # Lo SOYOMBO MARK PLUTA 11A9E..11AA2;N # Po [5] SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME..SOYOMBO TERMINAL MARK-2 11AC0..11AF8;N # Lo [57] PAU CIN HAU LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL 11C00..11C08;N # Lo [9] BHAIKSUKI LETTER A..BHAIKSUKI LETTER VOCALIC L 11C0A..11C2E;N # Lo [37] BHAIKSUKI LETTER E..BHAIKSUKI LETTER HA 11C2F;N # Mc BHAIKSUKI VOWEL SIGN AA 11C30..11C36;N # Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L 11C38..11C3D;N # Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA 11C3E;N # Mc BHAIKSUKI SIGN VISARGA 11C3F;N # Mn BHAIKSUKI SIGN VIRAMA 11C40;N # Lo BHAIKSUKI SIGN AVAGRAHA 11C41..11C45;N # Po [5] BHAIKSUKI DANDA..BHAIKSUKI GAP FILLER-2 11C50..11C59;N # Nd [10] BHAIKSUKI DIGIT ZERO..BHAIKSUKI DIGIT NINE 11C5A..11C6C;N # No [19] BHAIKSUKI NUMBER ONE..BHAIKSUKI HUNDREDS UNIT MARK 11C70..11C71;N # Po [2] MARCHEN HEAD MARK..MARCHEN MARK SHAD 11C72..11C8F;N # Lo [30] MARCHEN LETTER KA..MARCHEN LETTER A 11C92..11CA7;N # Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA 11CA9;N # Mc MARCHEN SUBJOINED LETTER YA 11CAA..11CB0;N # Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA 11CB1;N # Mc MARCHEN VOWEL SIGN I 11CB2..11CB3;N # Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E 11CB4;N # Mc MARCHEN VOWEL SIGN O 11CB5..11CB6;N # Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU 11D00..11D06;N # Lo [7] MASARAM GONDI LETTER A..MASARAM GONDI LETTER E 11D08..11D09;N # Lo [2] MASARAM GONDI LETTER AI..MASARAM GONDI LETTER O 11D0B..11D30;N # Lo [38] MASARAM GONDI LETTER AU..MASARAM GONDI LETTER TRA 11D31..11D36;N # Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R 11D3A;N # Mn MASARAM GONDI VOWEL SIGN E 11D3C..11D3D;N # Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O 11D3F..11D45;N # Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA 11D46;N # Lo MASARAM GONDI REPHA 11D47;N # Mn MASARAM GONDI RA-KARA 11D50..11D59;N # Nd [10] MASARAM GONDI DIGIT ZERO..MASARAM GONDI DIGIT NINE 11D60..11D65;N # Lo [6] GUNJALA GONDI LETTER A..GUNJALA GONDI LETTER UU 11D67..11D68;N # Lo [2] GUNJALA GONDI LETTER EE..GUNJALA GONDI LETTER AI 11D6A..11D89;N # Lo [32] GUNJALA GONDI LETTER OO..GUNJALA GONDI LETTER SA 11D8A..11D8E;N # Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU 11D90..11D91;N # Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI 11D93..11D94;N # Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU 11D95;N # Mn GUNJALA GONDI SIGN ANUSVARA 11D96;N # Mc GUNJALA GONDI SIGN VISARGA 11D97;N # Mn GUNJALA GONDI VIRAMA 11D98;N # Lo GUNJALA GONDI OM 11DA0..11DA9;N # Nd [10] GUNJALA GONDI DIGIT ZERO..GUNJALA GONDI DIGIT NINE 11EE0..11EF2;N # Lo [19] MAKASAR LETTER KA..MAKASAR ANGKA 11EF3..11EF4;N # Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U 11EF5..11EF6;N # Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O 11EF7..11EF8;N # Po [2] MAKASAR PASSIMBANG..MAKASAR END OF SECTION 12000..12399;N # Lo [922] CUNEIFORM SIGN A..CUNEIFORM SIGN U U 12400..1246E;N # Nl [111] CUNEIFORM NUMERIC SIGN TWO ASH..CUNEIFORM NUMERIC SIGN NINE U VARIANT FORM 12470..12474;N # Po [5] CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER..CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON 12480..12543;N # Lo [196] CUNEIFORM SIGN AB TIMES NUN TENU..CUNEIFORM SIGN ZU5 TIMES THREE DISH TENU 13000..1342E;N # Lo [1071] EGYPTIAN HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032 14400..14646;N # Lo [583] ANATOLIAN HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530 16800..16A38;N # Lo [569] BAMUM LETTER PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ 16A40..16A5E;N # Lo [31] MRO LETTER TA..MRO LETTER TEK 16A60..16A69;N # Nd [10] MRO DIGIT ZERO..MRO DIGIT NINE 16A6E..16A6F;N # Po [2] MRO DANDA..MRO DOUBLE DANDA 16AD0..16AED;N # Lo [30] BASSA VAH LETTER ENNI..BASSA VAH LETTER I 16AF0..16AF4;N # Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE 16AF5;N # Po BASSA VAH FULL STOP 16B00..16B2F;N # Lo [48] PAHAWH HMONG VOWEL KEEB..PAHAWH HMONG CONSONANT CAU 16B30..16B36;N # Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM 16B37..16B3B;N # Po [5] PAHAWH HMONG SIGN VOS THOM..PAHAWH HMONG SIGN VOS FEEM 16B3C..16B3F;N # So [4] PAHAWH HMONG SIGN XYEEM NTXIV..PAHAWH HMONG SIGN XYEEM FAIB 16B40..16B43;N # Lm [4] PAHAWH HMONG SIGN VOS SEEV..PAHAWH HMONG SIGN IB YAM 16B44;N # Po PAHAWH HMONG SIGN XAUS 16B45;N # So PAHAWH HMONG SIGN CIM TSOV ROG 16B50..16B59;N # Nd [10] PAHAWH HMONG DIGIT ZERO..PAHAWH HMONG DIGIT NINE 16B5B..16B61;N # No [7] PAHAWH HMONG NUMBER TENS..PAHAWH HMONG NUMBER TRILLIONS 16B63..16B77;N # Lo [21] PAHAWH HMONG SIGN VOS LUB..PAHAWH HMONG SIGN CIM NRES TOS 16B7D..16B8F;N # Lo [19] PAHAWH HMONG CLAN SIGN TSHEEJ..PAHAWH HMONG CLAN SIGN VWJ 16E40..16E7F;N # L& [64] MEDEFAIDRIN CAPITAL LETTER M..MEDEFAIDRIN SMALL LETTER Y 16E80..16E96;N # No [23] MEDEFAIDRIN DIGIT ZERO..MEDEFAIDRIN DIGIT THREE ALTERNATE FORM 16E97..16E9A;N # Po [4] MEDEFAIDRIN COMMA..MEDEFAIDRIN EXCLAMATION OH 16F00..16F44;N # Lo [69] MIAO LETTER PA..MIAO LETTER HHA 16F50;N # Lo MIAO LETTER NASALIZATION 16F51..16F7E;N # Mc [46] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN NG 16F8F..16F92;N # Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW 16F93..16F9F;N # Lm [13] MIAO LETTER TONE-2..MIAO LETTER REFORMED TONE-8 16FE0..16FE1;W # Lm [2] TANGUT ITERATION MARK..NUSHU ITERATION MARK 17000..187F1;W # Lo [6130] TANGUT IDEOGRAPH-17000..TANGUT IDEOGRAPH-187F1 18800..18AF2;W # Lo [755] TANGUT COMPONENT-001..TANGUT COMPONENT-755 1B000..1B0FF;W # Lo [256] KATAKANA LETTER ARCHAIC E..HENTAIGANA LETTER RE-2 1B100..1B11E;W # Lo [31] HENTAIGANA LETTER RE-3..HENTAIGANA LETTER N-MU-MO-2 1B170..1B2FB;W # Lo [396] NUSHU CHARACTER-1B170..NUSHU CHARACTER-1B2FB 1BC00..1BC6A;N # Lo [107] DUPLOYAN LETTER H..DUPLOYAN LETTER VOCALIC M 1BC70..1BC7C;N # Lo [13] DUPLOYAN AFFIX LEFT HORIZONTAL SECANT..DUPLOYAN AFFIX ATTACHED TANGENT HOOK 1BC80..1BC88;N # Lo [9] DUPLOYAN AFFIX HIGH ACUTE..DUPLOYAN AFFIX HIGH VERTICAL 1BC90..1BC99;N # Lo [10] DUPLOYAN AFFIX LOW ACUTE..DUPLOYAN AFFIX LOW ARROW 1BC9C;N # So DUPLOYAN SIGN O WITH CROSS 1BC9D..1BC9E;N # Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK 1BC9F;N # Po DUPLOYAN PUNCTUATION CHINOOK FULL STOP 1BCA0..1BCA3;N # Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP 1D000..1D0F5;N # So [246] BYZANTINE MUSICAL SYMBOL PSILI..BYZANTINE MUSICAL SYMBOL GORGON NEO KATO 1D100..1D126;N # So [39] MUSICAL SYMBOL SINGLE BARLINE..MUSICAL SYMBOL DRUM CLEF-2 1D129..1D164;N # So [60] MUSICAL SYMBOL MULTIPLE MEASURE REST..MUSICAL SYMBOL ONE HUNDRED TWENTY-EIGHTH NOTE 1D165..1D166;N # Mc [2] MUSICAL SYMBOL COMBINING STEM..MUSICAL SYMBOL COMBINING SPRECHGESANG STEM 1D167..1D169;N # Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3 1D16A..1D16C;N # So [3] MUSICAL SYMBOL FINGERED TREMOLO-1..MUSICAL SYMBOL FINGERED TREMOLO-3 1D16D..1D172;N # Mc [6] MUSICAL SYMBOL COMBINING AUGMENTATION DOT..MUSICAL SYMBOL COMBINING FLAG-5 1D173..1D17A;N # Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE 1D17B..1D182;N # Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE 1D183..1D184;N # So [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL ARPEGGIATO DOWN 1D185..1D18B;N # Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE 1D18C..1D1A9;N # So [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL DEGREE SLASH 1D1AA..1D1AD;N # Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO 1D1AE..1D1E8;N # So [59] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL KIEVAN FLAT SIGN 1D200..1D241;N # So [66] GREEK VOCAL NOTATION SYMBOL-1..GREEK INSTRUMENTAL NOTATION SYMBOL-54 1D242..1D244;N # Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME 1D245;N # So GREEK MUSICAL LEIMMA 1D2E0..1D2F3;N # No [20] MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN 1D300..1D356;N # So [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING 1D360..1D378;N # No [25] COUNTING ROD UNIT DIGIT ONE..TALLY MARK FIVE 1D400..1D454;N # L& [85] MATHEMATICAL BOLD CAPITAL A..MATHEMATICAL ITALIC SMALL G 1D456..1D49C;N # L& [71] MATHEMATICAL ITALIC SMALL I..MATHEMATICAL SCRIPT CAPITAL A 1D49E..1D49F;N # Lu [2] MATHEMATICAL SCRIPT CAPITAL C..MATHEMATICAL SCRIPT CAPITAL D 1D4A2;N # Lu MATHEMATICAL SCRIPT CAPITAL G 1D4A5..1D4A6;N # Lu [2] MATHEMATICAL SCRIPT CAPITAL J..MATHEMATICAL SCRIPT CAPITAL K 1D4A9..1D4AC;N # Lu [4] MATHEMATICAL SCRIPT CAPITAL N..MATHEMATICAL SCRIPT CAPITAL Q 1D4AE..1D4B9;N # L& [12] MATHEMATICAL SCRIPT CAPITAL S..MATHEMATICAL SCRIPT SMALL D 1D4BB;N # Ll MATHEMATICAL SCRIPT SMALL F 1D4BD..1D4C3;N # Ll [7] MATHEMATICAL SCRIPT SMALL H..MATHEMATICAL SCRIPT SMALL N 1D4C5..1D505;N # L& [65] MATHEMATICAL SCRIPT SMALL P..MATHEMATICAL FRAKTUR CAPITAL B 1D507..1D50A;N # Lu [4] MATHEMATICAL FRAKTUR CAPITAL D..MATHEMATICAL FRAKTUR CAPITAL G 1D50D..1D514;N # Lu [8] MATHEMATICAL FRAKTUR CAPITAL J..MATHEMATICAL FRAKTUR CAPITAL Q 1D516..1D51C;N # Lu [7] MATHEMATICAL FRAKTUR CAPITAL S..MATHEMATICAL FRAKTUR CAPITAL Y 1D51E..1D539;N # L& [28] MATHEMATICAL FRAKTUR SMALL A..MATHEMATICAL DOUBLE-STRUCK CAPITAL B 1D53B..1D53E;N # Lu [4] MATHEMATICAL DOUBLE-STRUCK CAPITAL D..MATHEMATICAL DOUBLE-STRUCK CAPITAL G 1D540..1D544;N # Lu [5] MATHEMATICAL DOUBLE-STRUCK CAPITAL I..MATHEMATICAL DOUBLE-STRUCK CAPITAL M 1D546;N # Lu MATHEMATICAL DOUBLE-STRUCK CAPITAL O 1D54A..1D550;N # Lu [7] MATHEMATICAL DOUBLE-STRUCK CAPITAL S..MATHEMATICAL DOUBLE-STRUCK CAPITAL Y 1D552..1D6A5;N # L& [340] MATHEMATICAL DOUBLE-STRUCK SMALL A..MATHEMATICAL ITALIC SMALL DOTLESS J 1D6A8..1D6C0;N # Lu [25] MATHEMATICAL BOLD CAPITAL ALPHA..MATHEMATICAL BOLD CAPITAL OMEGA 1D6C1;N # Sm MATHEMATICAL BOLD NABLA 1D6C2..1D6DA;N # Ll [25] MATHEMATICAL BOLD SMALL ALPHA..MATHEMATICAL BOLD SMALL OMEGA 1D6DB;N # Sm MATHEMATICAL BOLD PARTIAL DIFFERENTIAL 1D6DC..1D6FA;N # L& [31] MATHEMATICAL BOLD EPSILON SYMBOL..MATHEMATICAL ITALIC CAPITAL OMEGA 1D6FB;N # Sm MATHEMATICAL ITALIC NABLA 1D6FC..1D714;N # Ll [25] MATHEMATICAL ITALIC SMALL ALPHA..MATHEMATICAL ITALIC SMALL OMEGA 1D715;N # Sm MATHEMATICAL ITALIC PARTIAL DIFFERENTIAL 1D716..1D734;N # L& [31] MATHEMATICAL ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD ITALIC CAPITAL OMEGA 1D735;N # Sm MATHEMATICAL BOLD ITALIC NABLA 1D736..1D74E;N # Ll [25] MATHEMATICAL BOLD ITALIC SMALL ALPHA..MATHEMATICAL BOLD ITALIC SMALL OMEGA 1D74F;N # Sm MATHEMATICAL BOLD ITALIC PARTIAL DIFFERENTIAL 1D750..1D76E;N # L& [31] MATHEMATICAL BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD CAPITAL OMEGA 1D76F;N # Sm MATHEMATICAL SANS-SERIF BOLD NABLA 1D770..1D788;N # Ll [25] MATHEMATICAL SANS-SERIF BOLD SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD SMALL OMEGA 1D789;N # Sm MATHEMATICAL SANS-SERIF BOLD PARTIAL DIFFERENTIAL 1D78A..1D7A8;N # L& [31] MATHEMATICAL SANS-SERIF BOLD EPSILON SYMBOL..MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL OMEGA 1D7A9;N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC NABLA 1D7AA..1D7C2;N # Ll [25] MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL ALPHA..MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL OMEGA 1D7C3;N # Sm MATHEMATICAL SANS-SERIF BOLD ITALIC PARTIAL DIFFERENTIAL 1D7C4..1D7CB;N # L& [8] MATHEMATICAL SANS-SERIF BOLD ITALIC EPSILON SYMBOL..MATHEMATICAL BOLD SMALL DIGAMMA 1D7CE..1D7FF;N # Nd [50] MATHEMATICAL BOLD DIGIT ZERO..MATHEMATICAL MONOSPACE DIGIT NINE 1D800..1D9FF;N # So [512] SIGNWRITING HAND-FIST INDEX..SIGNWRITING HEAD 1DA00..1DA36;N # Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN 1DA37..1DA3A;N # So [4] SIGNWRITING AIR BLOW SMALL ROTATIONS..SIGNWRITING BREATH EXHALE 1DA3B..1DA6C;N # Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT 1DA6D..1DA74;N # So [8] SIGNWRITING SHOULDER HIP SPINE..SIGNWRITING TORSO-FLOORPLANE TWISTING 1DA75;N # Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS 1DA76..1DA83;N # So [14] SIGNWRITING LIMB COMBINATION..SIGNWRITING LOCATION DEPTH 1DA84;N # Mn SIGNWRITING LOCATION HEAD NECK 1DA85..1DA86;N # So [2] SIGNWRITING LOCATION TORSO..SIGNWRITING LOCATION LIMBS DIGITS 1DA87..1DA8B;N # Po [5] SIGNWRITING COMMA..SIGNWRITING PARENTHESIS 1DA9B..1DA9F;N # Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6 1DAA1..1DAAF;N # Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16 1E000..1E006;N # Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE 1E008..1E018;N # Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU 1E01B..1E021;N # Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI 1E023..1E024;N # Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS 1E026..1E02A;N # Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA 1E800..1E8C4;N # Lo [197] MENDE KIKAKUI SYLLABLE M001 KI..MENDE KIKAKUI SYLLABLE M060 NYON 1E8C7..1E8CF;N # No [9] MENDE KIKAKUI DIGIT ONE..MENDE KIKAKUI DIGIT NINE 1E8D0..1E8D6;N # Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS 1E900..1E943;N # L& [68] ADLAM CAPITAL LETTER ALIF..ADLAM SMALL LETTER SHA 1E944..1E94A;N # Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA 1E950..1E959;N # Nd [10] ADLAM DIGIT ZERO..ADLAM DIGIT NINE 1E95E..1E95F;N # Po [2] ADLAM INITIAL EXCLAMATION MARK..ADLAM INITIAL QUESTION MARK 1EC71..1ECAB;N # No [59] INDIC SIYAQ NUMBER ONE..INDIC SIYAQ NUMBER PREFIXED NINE 1ECAC;N # So INDIC SIYAQ PLACEHOLDER 1ECAD..1ECAF;N # No [3] INDIC SIYAQ FRACTION ONE QUARTER..INDIC SIYAQ FRACTION THREE QUARTERS 1ECB0;N # Sc INDIC SIYAQ RUPEE MARK 1ECB1..1ECB4;N # No [4] INDIC SIYAQ NUMBER ALTERNATE ONE..INDIC SIYAQ ALTERNATE LAKH MARK 1EE00..1EE03;N # Lo [4] ARABIC MATHEMATICAL ALEF..ARABIC MATHEMATICAL DAL 1EE05..1EE1F;N # Lo [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL DOTLESS QAF 1EE21..1EE22;N # Lo [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC MATHEMATICAL INITIAL JEEM 1EE24;N # Lo ARABIC MATHEMATICAL INITIAL HEH 1EE27;N # Lo ARABIC MATHEMATICAL INITIAL HAH 1EE29..1EE32;N # Lo [10] ARABIC MATHEMATICAL INITIAL YEH..ARABIC MATHEMATICAL INITIAL QAF 1EE34..1EE37;N # Lo [4] ARABIC MATHEMATICAL INITIAL SHEEN..ARABIC MATHEMATICAL INITIAL KHAH 1EE39;N # Lo ARABIC MATHEMATICAL INITIAL DAD 1EE3B;N # Lo ARABIC MATHEMATICAL INITIAL GHAIN 1EE42;N # Lo ARABIC MATHEMATICAL TAILED JEEM 1EE47;N # Lo ARABIC MATHEMATICAL TAILED HAH 1EE49;N # Lo ARABIC MATHEMATICAL TAILED YEH 1EE4B;N # Lo ARABIC MATHEMATICAL TAILED LAM 1EE4D..1EE4F;N # Lo [3] ARABIC MATHEMATICAL TAILED NOON..ARABIC MATHEMATICAL TAILED AIN 1EE51..1EE52;N # Lo [2] ARABIC MATHEMATICAL TAILED SAD..ARABIC MATHEMATICAL TAILED QAF 1EE54;N # Lo ARABIC MATHEMATICAL TAILED SHEEN 1EE57;N # Lo ARABIC MATHEMATICAL TAILED KHAH 1EE59;N # Lo ARABIC MATHEMATICAL TAILED DAD 1EE5B;N # Lo ARABIC MATHEMATICAL TAILED GHAIN 1EE5D;N # Lo ARABIC MATHEMATICAL TAILED DOTLESS NOON 1EE5F;N # Lo ARABIC MATHEMATICAL TAILED DOTLESS QAF 1EE61..1EE62;N # Lo [2] ARABIC MATHEMATICAL STRETCHED BEH..ARABIC MATHEMATICAL STRETCHED JEEM 1EE64;N # Lo ARABIC MATHEMATICAL STRETCHED HEH 1EE67..1EE6A;N # Lo [4] ARABIC MATHEMATICAL STRETCHED HAH..ARABIC MATHEMATICAL STRETCHED KAF 1EE6C..1EE72;N # Lo [7] ARABIC MATHEMATICAL STRETCHED MEEM..ARABIC MATHEMATICAL STRETCHED QAF 1EE74..1EE77;N # Lo [4] ARABIC MATHEMATICAL STRETCHED SHEEN..ARABIC MATHEMATICAL STRETCHED KHAH 1EE79..1EE7C;N # Lo [4] ARABIC MATHEMATICAL STRETCHED DAD..ARABIC MATHEMATICAL STRETCHED DOTLESS BEH 1EE7E;N # Lo ARABIC MATHEMATICAL STRETCHED DOTLESS FEH 1EE80..1EE89;N # Lo [10] ARABIC MATHEMATICAL LOOPED ALEF..ARABIC MATHEMATICAL LOOPED YEH 1EE8B..1EE9B;N # Lo [17] ARABIC MATHEMATICAL LOOPED LAM..ARABIC MATHEMATICAL LOOPED GHAIN 1EEA1..1EEA3;N # Lo [3] ARABIC MATHEMATICAL DOUBLE-STRUCK BEH..ARABIC MATHEMATICAL DOUBLE-STRUCK DAL 1EEA5..1EEA9;N # Lo [5] ARABIC MATHEMATICAL DOUBLE-STRUCK WAW..ARABIC MATHEMATICAL DOUBLE-STRUCK YEH 1EEAB..1EEBB;N # Lo [17] ARABIC MATHEMATICAL DOUBLE-STRUCK LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN 1EEF0..1EEF1;N # Sm [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL 1F000..1F003;N # So [4] MAHJONG TILE EAST WIND..MAHJONG TILE NORTH WIND 1F004;W # So MAHJONG TILE RED DRAGON 1F005..1F02B;N # So [39] MAHJONG TILE GREEN DRAGON..MAHJONG TILE BACK 1F030..1F093;N # So [100] DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06 1F0A0..1F0AE;N # So [15] PLAYING CARD BACK..PLAYING CARD KING OF SPADES 1F0B1..1F0BF;N # So [15] PLAYING CARD ACE OF HEARTS..PLAYING CARD RED JOKER 1F0C1..1F0CE;N # So [14] PLAYING CARD ACE OF DIAMONDS..PLAYING CARD KING OF DIAMONDS 1F0CF;W # So PLAYING CARD BLACK JOKER 1F0D1..1F0F5;N # So [37] PLAYING CARD ACE OF CLUBS..PLAYING CARD TRUMP-21 1F100..1F10A;A # No [11] DIGIT ZERO FULL STOP..DIGIT NINE COMMA 1F10B..1F10C;N # No [2] DINGBAT CIRCLED SANS-SERIF DIGIT ZERO..DINGBAT NEGATIVE CIRCLED SANS-SERIF DIGIT ZERO 1F110..1F12D;A # So [30] PARENTHESIZED LATIN CAPITAL LETTER A..CIRCLED CD 1F12E..1F12F;N # So [2] CIRCLED WZ..COPYLEFT SYMBOL 1F130..1F169;A # So [58] SQUARED LATIN CAPITAL LETTER A..NEGATIVE CIRCLED LATIN CAPITAL LETTER Z 1F16A..1F16B;N # So [2] RAISED MC SIGN..RAISED MD SIGN 1F170..1F18D;A # So [30] NEGATIVE SQUARED LATIN CAPITAL LETTER A..NEGATIVE SQUARED SA 1F18E;W # So NEGATIVE SQUARED AB 1F18F..1F190;A # So [2] NEGATIVE SQUARED WC..SQUARE DJ 1F191..1F19A;W # So [10] SQUARED CL..SQUARED VS 1F19B..1F1AC;A # So [18] SQUARED THREE D..SQUARED VOD 1F1E6..1F1FF;N # So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z 1F200..1F202;W # So [3] SQUARE HIRAGANA HOKA..SQUARED KATAKANA SA 1F210..1F23B;W # So [44] SQUARED CJK UNIFIED IDEOGRAPH-624B..SQUARED CJK UNIFIED IDEOGRAPH-914D 1F240..1F248;W # So [9] TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-672C..TORTOISE SHELL BRACKETED CJK UNIFIED IDEOGRAPH-6557 1F250..1F251;W # So [2] CIRCLED IDEOGRAPH ADVANTAGE..CIRCLED IDEOGRAPH ACCEPT 1F260..1F265;W # So [6] ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI 1F300..1F320;W # So [33] CYCLONE..SHOOTING STAR 1F321..1F32C;N # So [12] THERMOMETER..WIND BLOWING FACE 1F32D..1F335;W # So [9] HOT DOG..CACTUS 1F336;N # So HOT PEPPER 1F337..1F37C;W # So [70] TULIP..BABY BOTTLE 1F37D;N # So FORK AND KNIFE WITH PLATE 1F37E..1F393;W # So [22] BOTTLE WITH POPPING CORK..GRADUATION CAP 1F394..1F39F;N # So [12] HEART WITH TIP ON THE LEFT..ADMISSION TICKETS 1F3A0..1F3CA;W # So [43] CAROUSEL HORSE..SWIMMER 1F3CB..1F3CE;N # So [4] WEIGHT LIFTER..RACING CAR 1F3CF..1F3D3;W # So [5] CRICKET BAT AND BALL..TABLE TENNIS PADDLE AND BALL 1F3D4..1F3DF;N # So [12] SNOW CAPPED MOUNTAIN..STADIUM 1F3E0..1F3F0;W # So [17] HOUSE BUILDING..EUROPEAN CASTLE 1F3F1..1F3F3;N # So [3] WHITE PENNANT..WAVING WHITE FLAG 1F3F4;W # So WAVING BLACK FLAG 1F3F5..1F3F7;N # So [3] ROSETTE..LABEL 1F3F8..1F3FA;W # So [3] BADMINTON RACQUET AND SHUTTLECOCK..AMPHORA 1F3FB..1F3FF;W # Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6 1F400..1F43E;W # So [63] RAT..PAW PRINTS 1F43F;N # So CHIPMUNK 1F440;W # So EYES 1F441;N # So EYE 1F442..1F4FC;W # So [187] EAR..VIDEOCASSETTE 1F4FD..1F4FE;N # So [2] FILM PROJECTOR..PORTABLE STEREO 1F4FF..1F53D;W # So [63] PRAYER BEADS..DOWN-POINTING SMALL RED TRIANGLE 1F53E..1F54A;N # So [13] LOWER RIGHT SHADOWED WHITE CIRCLE..DOVE OF PEACE 1F54B..1F54E;W # So [4] KAABA..MENORAH WITH NINE BRANCHES 1F54F;N # So BOWL OF HYGIEIA 1F550..1F567;W # So [24] CLOCK FACE ONE OCLOCK..CLOCK FACE TWELVE-THIRTY 1F568..1F579;N # So [18] RIGHT SPEAKER..JOYSTICK 1F57A;W # So MAN DANCING 1F57B..1F594;N # So [26] LEFT HAND TELEPHONE RECEIVER..REVERSED VICTORY HAND 1F595..1F596;W # So [2] REVERSED HAND WITH MIDDLE FINGER EXTENDED..RAISED HAND WITH PART BETWEEN MIDDLE AND RING FINGERS 1F597..1F5A3;N # So [13] WHITE DOWN POINTING LEFT HAND INDEX..BLACK DOWN POINTING BACKHAND INDEX 1F5A4;W # So BLACK HEART 1F5A5..1F5FA;N # So [86] DESKTOP COMPUTER..WORLD MAP 1F5FB..1F5FF;W # So [5] MOUNT FUJI..MOYAI 1F600..1F64F;W # So [80] GRINNING FACE..PERSON WITH FOLDED HANDS 1F650..1F67F;N # So [48] NORTH WEST POINTING LEAF..REVERSE CHECKER BOARD 1F680..1F6C5;W # So [70] ROCKET..LEFT LUGGAGE 1F6C6..1F6CB;N # So [6] TRIANGLE WITH ROUNDED CORNERS..COUCH AND LAMP 1F6CC;W # So SLEEPING ACCOMMODATION 1F6CD..1F6CF;N # So [3] SHOPPING BAGS..BED 1F6D0..1F6D2;W # So [3] PLACE OF WORSHIP..SHOPPING TROLLEY 1F6D3..1F6D4;N # So [2] STUPA..PAGODA 1F6E0..1F6EA;N # So [11] HAMMER AND WRENCH..NORTHEAST-POINTING AIRPLANE 1F6EB..1F6EC;W # So [2] AIRPLANE DEPARTURE..AIRPLANE ARRIVING 1F6F0..1F6F3;N # So [4] SATELLITE..PASSENGER SHIP 1F6F4..1F6F9;W # So [6] SCOOTER..SKATEBOARD 1F700..1F773;N # So [116] ALCHEMICAL SYMBOL FOR QUINTESSENCE..ALCHEMICAL SYMBOL FOR HALF OUNCE 1F780..1F7D8;N # So [89] BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE..NEGATIVE CIRCLED SQUARE 1F800..1F80B;N # So [12] LEFTWARDS ARROW WITH SMALL TRIANGLE ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD 1F810..1F847;N # So [56] LEFTWARDS ARROW WITH SMALL EQUILATERAL ARROWHEAD..DOWNWARDS HEAVY ARROW 1F850..1F859;N # So [10] LEFTWARDS SANS-SERIF ARROW..UP DOWN SANS-SERIF ARROW 1F860..1F887;N # So [40] WIDE-HEADED LEFTWARDS LIGHT BARB ARROW..WIDE-HEADED SOUTH WEST VERY HEAVY BARB ARROW 1F890..1F8AD;N # So [30] LEFTWARDS TRIANGLE ARROWHEAD..WHITE ARROW SHAFT WIDTH TWO THIRDS 1F900..1F90B;N # So [12] CIRCLED CROSS FORMEE WITH FOUR DOTS..DOWNWARD FACING NOTCHED HOOK WITH DOT 1F910..1F93E;W # So [47] ZIPPER-MOUTH FACE..HANDBALL 1F940..1F970;W # So [49] WILTED FLOWER..SMILING FACE WITH SMILING EYES AND THREE HEARTS 1F973..1F976;W # So [4] FACE WITH PARTY HORN AND PARTY HAT..FREEZING FACE 1F97A;W # So FACE WITH PLEADING EYES 1F97C..1F9A2;W # So [39] LAB COAT..SWAN 1F9B0..1F9B9;W # So [10] EMOJI COMPONENT RED HAIR..SUPERVILLAIN 1F9C0..1F9C2;W # So [3] CHEESE WEDGE..SALT SHAKER 1F9D0..1F9FF;W # So [48] FACE WITH MONOCLE..NAZAR AMULET 1FA60..1FA6D;N # So [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER 20000..2A6D6;W # Lo [42711] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED IDEOGRAPH-2A6D6 2A6D7..2A6FF;W # Cn [41] <reserved-2A6D7>..<reserved-2A6FF> 2A700..2B734;W # Lo [4149] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B734 2B735..2B73F;W # Cn [11] <reserved-2B735>..<reserved-2B73F> 2B740..2B81D;W # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D 2B81E..2B81F;W # Cn [2] <reserved-2B81E>..<reserved-2B81F> 2B820..2CEA1;W # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1 2CEA2..2CEAF;W # Cn [14] <reserved-2CEA2>..<reserved-2CEAF> 2CEB0..2EBE0;W # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED IDEOGRAPH-2EBE0 2EBE1..2F7FF;W # Cn [3103] <reserved-2EBE1>..<reserved-2F7FF> 2F800..2FA1D;W # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK COMPATIBILITY IDEOGRAPH-2FA1D 2FA1E..2FA1F;W # Cn [2] <reserved-2FA1E>..<reserved-2FA1F> 2FA20..2FFFD;W # Cn [1502] <reserved-2FA20>..<reserved-2FFFD> 30000..3FFFD;W # Cn [65534] <reserved-30000>..<reserved-3FFFD> E0001;N # Cf LANGUAGE TAG E0020..E007F;N # Cf [96] TAG SPACE..CANCEL TAG E0100..E01EF;A # Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256 F0000..FFFFD;A # Co [65534] <private-use-F0000>..<private-use-FFFFD> 100000..10FFFD;A # Co [65534] <private-use-100000>..<private-use-10FFFD> # EOF |
Added modules/textutil/build/build.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | set srcdir [file dirname [file normalize [file join [pwd] [info script]]]] set moddir [file dirname $srcdir] set fout [open [file join $moddir wcswidth.tcl] w] puts $fout {### # This file is automatically generated by the build/build.tcl file # based on information in the following database: # http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt # # (This is the 35th edition, thus version 35 for our package) # # Author: Sean Woods <[email protected]> ### package provide textutil::wcswidth 35.0} set fin [open [file join $srcdir EastAsianWidth.txt] r] puts $fout "proc ::textutil::wcswidth_type char \{" set hash # while {[gets $fin line]>=0} { set commentidx [string first $hash $line] if {$commentidx==0} continue set data [string trim [string range $line 0 [expr $commentidx-1]]] set comment [string range $line [expr {$commentidx+1}] end] if {[scan $line {%6x..%6x;%1s} start end code]==3} { } elseif {[scan $line {%5x..%5x;%1s} start end code]==3} { } elseif {[scan $line {%4x..%4x;%1s} start end code]==3} { } elseif {[scan $line {%5x;%1s} start code]==2} { set end $start } elseif {[scan $line {%4x;%1s} start code]==2} { set end $start } else { puts "Ignored line $line" continue } if {$code eq "N"} continue dict set map %start% $start dict set map %end% $end dict set map %code% $code dict set map %comment% [string trim $comment] #puts $fout " $hash $comment" if {$start eq $end} { puts $fout [string map $map { if {$char == %start%} { return %code% }}] } else { puts $fout [string map $map { if {$char >= %start% && $char <= %end% } { return %code% }}] } } puts $fout { return N} puts $fout "\}" seek $fin 0 puts $fout "proc ::textutil::wcswidth_char char \{" while {[gets $fin line]>=0} { set commentidx [string first $hash $line] if {$commentidx==0} continue set data [string trim [string range $line 0 [expr $commentidx-1]]] set comment [string range $line [expr {$commentidx+1}] end] if {[scan $line {%6x..%6x;%1s} start end code]==3} { } elseif {[scan $line {%5x..%5x;%1s} start end code]==3} { } elseif {[scan $line {%4x..%4x;%1s} start end code]==3} { } elseif {[scan $line {%5x;%1s} start code]==2} { set end $start } elseif {[scan $line {%4x;%1s} start code]==2} { set end $start } else { puts "Ignored line $line" continue } dict set map %start% $start dict set map %end% $end dict set map %width% 1 ### # Per the unicode recommendations: # http://www.unicode.org/reports/tr11/ # #When processing or displaying data: # # * Wide characters behave like ideographs in important ways, such as layout. Except for # certain punctuation characters, they are not rotated when appearing in vertical text # runs. In fixed-pitch fonts, they take up one Em of space. # * Halfwidth characters behave like ideographs in some ways, however, they are rotated # like narrow characters when appearing in vertical text runs. In fixed-pitch fonts, # they take up 1/2 Em of space. # * Narrow characters behave like Western characters, for example, in line breaking. # They are rotated sideways, when appearing in vertical text. In fixed-pitch East # Asian fonts, they take up 1/2 Em of space, but in rendering, a non-East Asian, # proportional font is often substituted. # * Ambiguous characters behave like wide or narrow characters depending on the context # (language tag, script identification, associated font, source of data, or explicit # markup; all can provide the context). If the context cannot be established reliably, # they should be treated as narrow characters by default. # * [UTS51] emoji presentation sequences behave as though they were East Asian Wide, # regardless of their assigned East_Asian_Width property value. (Not implemented here) ### switch $code { W - F { dict set map %width% 2 } A - N - Na - H { continue } } dict set map %code% $code dict set map %comment% [string trim $comment] #puts $fout " # $comment" if {$start eq $end} { puts $fout [string map $map { if {$char == %start%} { return %width% }}] } else { puts $fout [string map $map { if {$char >= %start% && $char <= %end% } { return %width% }}] } } puts $fout { return 1} puts $fout "\}" puts $fout { proc ::textutil::wcswidth {string} { set width 0 set len [string length $string] for {set i 0} {$i < $len} {incr i} { scan [string index $string $i] %c char set n [::textutil::wcswidth_char $char] if {$n < 0} { return -1 } incr width $n } return $width } } |
Changes to modules/textutil/wcswidth.tcl.
|
| < > | | > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > > > > > > > > > > > | < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 | ### # This file is automatically generated by the build/build.tcl file # based on information in the following database: # http://www.unicode.org/Public/UCD/latest/ucd/EastAsianWidth.txt # # (This is the 35th edition, thus version 35 for our package) # # Author: Sean Woods <[email protected]> ### package provide textutil::wcswidth 35.0 proc ::textutil::wcswidth_type char { if {$char == 161} { return A } if {$char == 164} { return A } if {$char == 167} { return A } if {$char == 168} { return A } if {$char == 170} { return A } if {$char == 173} { return A } if {$char == 174} { return A } if {$char == 176} { return A } if {$char == 177} { return A } if {$char >= 178 && $char <= 179 } { return A } if {$char == 180} { return A } if {$char >= 182 && $char <= 183 } { return A } if {$char == 184} { return A } if {$char == 185} { return A } if {$char == 186} { return A } if {$char >= 188 && $char <= 190 } { return A } if {$char == 191} { return A } if {$char == 198} { return A } if {$char == 208} { return A } if {$char == 215} { return A } if {$char == 216} { return A } if {$char >= 222 && $char <= 225 } { return A } if {$char == 230} { return A } if {$char >= 232 && $char <= 234 } { return A } if {$char >= 236 && $char <= 237 } { return A } if {$char == 240} { return A } if {$char >= 242 && $char <= 243 } { return A } if {$char == 247} { return A } if {$char >= 248 && $char <= 250 } { return A } if {$char == 252} { return A } if {$char == 254} { return A } if {$char == 257} { return A } if {$char == 273} { return A } if {$char == 275} { return A } if {$char == 283} { return A } if {$char >= 294 && $char <= 295 } { return A } if {$char == 299} { return A } if {$char >= 305 && $char <= 307 } { return A } if {$char == 312} { return A } if {$char >= 319 && $char <= 322 } { return A } if {$char == 324} { return A } if {$char >= 328 && $char <= 331 } { return A } if {$char == 333} { return A } if {$char >= 338 && $char <= 339 } { return A } if {$char >= 358 && $char <= 359 } { return A } if {$char == 363} { return A } if {$char == 462} { return A } if {$char == 464} { return A } if {$char == 466} { return A } if {$char == 468} { return A } if {$char == 470} { return A } if {$char == 472} { return A } if {$char == 474} { return A } if {$char == 476} { return A } if {$char == 593} { return A } if {$char == 609} { return A } if {$char == 708} { return A } if {$char == 711} { return A } if {$char >= 713 && $char <= 715 } { return A } if {$char == 717} { return A } if {$char == 720} { return A } if {$char >= 728 && $char <= 731 } { return A } if {$char == 733} { return A } if {$char == 735} { return A } if {$char >= 768 && $char <= 879 } { return A } if {$char >= 913 && $char <= 929 } { return A } if {$char >= 931 && $char <= 937 } { return A } if {$char >= 945 && $char <= 961 } { return A } if {$char >= 963 && $char <= 969 } { return A } if {$char == 1025} { return A } if {$char >= 1040 && $char <= 1103 } { return A } if {$char == 1105} { return A } if {$char >= 4352 && $char <= 4447 } { return W } if {$char == 8208} { return A } if {$char >= 8211 && $char <= 8213 } { return A } if {$char == 8214} { return A } if {$char == 8216} { return A } if {$char == 8217} { return A } if {$char == 8220} { return A } if {$char == 8221} { return A } if {$char >= 8224 && $char <= 8226 } { return A } if {$char >= 8228 && $char <= 8231 } { return A } if {$char == 8240} { return A } if {$char >= 8242 && $char <= 8243 } { return A } if {$char == 8245} { return A } if {$char == 8251} { return A } if {$char == 8254} { return A } if {$char == 8308} { return A } if {$char == 8319} { return A } if {$char >= 8321 && $char <= 8324 } { return A } if {$char == 8361} { return H } if {$char == 8364} { return A } if {$char == 8451} { return A } if {$char == 8453} { return A } if {$char == 8457} { return A } if {$char == 8467} { return A } if {$char == 8470} { return A } if {$char >= 8481 && $char <= 8482 } { return A } if {$char == 8486} { return A } if {$char == 8491} { return A } if {$char >= 8531 && $char <= 8532 } { return A } if {$char >= 8539 && $char <= 8542 } { return A } if {$char >= 8544 && $char <= 8555 } { return A } if {$char >= 8560 && $char <= 8569 } { return A } if {$char == 8585} { return A } if {$char >= 8592 && $char <= 8596 } { return A } if {$char >= 8597 && $char <= 8601 } { return A } if {$char >= 8632 && $char <= 8633 } { return A } if {$char == 8658} { return A } if {$char == 8660} { return A } if {$char == 8679} { return A } if {$char == 8704} { return A } if {$char >= 8706 && $char <= 8707 } { return A } if {$char >= 8711 && $char <= 8712 } { return A } if {$char == 8715} { return A } if {$char == 8719} { return A } if {$char == 8721} { return A } if {$char == 8725} { return A } if {$char == 8730} { return A } if {$char >= 8733 && $char <= 8736 } { return A } if {$char == 8739} { return A } if {$char == 8741} { return A } if {$char >= 8743 && $char <= 8748 } { return A } if {$char == 8750} { return A } if {$char >= 8756 && $char <= 8759 } { return A } if {$char >= 8764 && $char <= 8765 } { return A } if {$char == 8776} { return A } if {$char == 8780} { return A } if {$char == 8786} { return A } if {$char >= 8800 && $char <= 8801 } { return A } if {$char >= 8804 && $char <= 8807 } { return A } if {$char >= 8810 && $char <= 8811 } { return A } if {$char >= 8814 && $char <= 8815 } { return A } if {$char >= 8834 && $char <= 8835 } { return A } if {$char >= 8838 && $char <= 8839 } { return A } if {$char == 8853} { return A } if {$char == 8857} { return A } if {$char == 8869} { return A } if {$char == 8895} { return A } if {$char == 8978} { return A } if {$char >= 8986 && $char <= 8987 } { return W } if {$char == 9001} { return W } if {$char == 9002} { return W } if {$char >= 9193 && $char <= 9196 } { return W } if {$char == 9200} { return W } if {$char == 9203} { return W } if {$char >= 9312 && $char <= 9371 } { return A } if {$char >= 9372 && $char <= 9449 } { return A } if {$char >= 9451 && $char <= 9471 } { return A } if {$char >= 9472 && $char <= 9547 } { return A } if {$char >= 9552 && $char <= 9587 } { return A } if {$char >= 9600 && $char <= 9615 } { return A } if {$char >= 9618 && $char <= 9621 } { return A } if {$char >= 9632 && $char <= 9633 } { return A } if {$char >= 9635 && $char <= 9641 } { return A } if {$char >= 9650 && $char <= 9651 } { return A } if {$char == 9654} { return A } if {$char == 9655} { return A } if {$char >= 9660 && $char <= 9661 } { return A } if {$char == 9664} { return A } if {$char == 9665} { return A } if {$char >= 9670 && $char <= 9672 } { return A } if {$char == 9675} { return A } if {$char >= 9678 && $char <= 9681 } { return A } if {$char >= 9698 && $char <= 9701 } { return A } if {$char == 9711} { return A } if {$char >= 9725 && $char <= 9726 } { return W } if {$char >= 9733 && $char <= 9734 } { return A } if {$char == 9737} { return A } if {$char >= 9742 && $char <= 9743 } { return A } if {$char >= 9748 && $char <= 9749 } { return W } if {$char == 9756} { return A } if {$char == 9758} { return A } if {$char == 9792} { return A } if {$char == 9794} { return A } if {$char >= 9800 && $char <= 9811 } { return W } if {$char >= 9824 && $char <= 9825 } { return A } if {$char >= 9827 && $char <= 9829 } { return A } if {$char >= 9831 && $char <= 9834 } { return A } if {$char >= 9836 && $char <= 9837 } { return A } if {$char == 9839} { return A } if {$char == 9855} { return W } if {$char == 9875} { return W } if {$char >= 9886 && $char <= 9887 } { return A } if {$char == 9889} { return W } if {$char >= 9898 && $char <= 9899 } { return W } if {$char >= 9917 && $char <= 9918 } { return W } if {$char == 9919} { return A } if {$char >= 9924 && $char <= 9925 } { return W } if {$char >= 9926 && $char <= 9933 } { return A } if {$char == 9934} { return W } if {$char >= 9935 && $char <= 9939 } { return A } if {$char == 9940} { return W } if {$char >= 9941 && $char <= 9953 } { return A } if {$char == 9955} { return A } if {$char >= 9960 && $char <= 9961 } { return A } if {$char == 9962} { return W } if {$char >= 9963 && $char <= 9969 } { return A } if {$char >= 9970 && $char <= 9971 } { return W } if {$char == 9972} { return A } if {$char == 9973} { return W } if {$char >= 9974 && $char <= 9977 } { return A } if {$char == 9978} { return W } if {$char >= 9979 && $char <= 9980 } { return A } if {$char == 9981} { return W } if {$char >= 9982 && $char <= 9983 } { return A } if {$char == 9989} { return W } if {$char >= 9994 && $char <= 9995 } { return W } if {$char == 10024} { return W } if {$char == 10045} { return A } if {$char == 10060} { return W } if {$char == 10062} { return W } if {$char >= 10067 && $char <= 10069 } { return W } if {$char == 10071} { return W } if {$char >= 10102 && $char <= 10111 } { return A } if {$char >= 10133 && $char <= 10135 } { return W } if {$char == 10160} { return W } if {$char == 10175} { return W } if {$char >= 11035 && $char <= 11036 } { return W } if {$char == 11088} { return W } if {$char == 11093} { return W } if {$char >= 11094 && $char <= 11097 } { return A } if {$char >= 11904 && $char <= 11929 } { return W } if {$char >= 11931 && $char <= 12019 } { return W } if {$char >= 12032 && $char <= 12245 } { return W } if {$char >= 12272 && $char <= 12283 } { return W } if {$char == 12288} { return F } if {$char >= 12289 && $char <= 12291 } { return W } if {$char == 12292} { return W } if {$char == 12293} { return W } if {$char == 12294} { return W } if {$char == 12295} { return W } if {$char == 12296} { return W } if {$char == 12297} { return W } if {$char == 12298} { return W } if {$char == 12299} { return W } if {$char == 12300} { return W } if {$char == 12301} { return W } if {$char == 12302} { return W } if {$char == 12303} { return W } if {$char == 12304} { return W } if {$char == 12305} { return W } if {$char >= 12306 && $char <= 12307 } { return W } if {$char == 12308} { return W } if {$char == 12309} { return W } if {$char == 12310} { return W } if {$char == 12311} { return W } if {$char == 12312} { return W } if {$char == 12313} { return W } if {$char == 12314} { return W } if {$char == 12315} { return W } if {$char == 12316} { return W } if {$char == 12317} { return W } if {$char >= 12318 && $char <= 12319 } { return W } if {$char == 12320} { return W } if {$char >= 12321 && $char <= 12329 } { return W } if {$char >= 12330 && $char <= 12333 } { return W } if {$char >= 12334 && $char <= 12335 } { return W } if {$char == 12336} { return W } if {$char >= 12337 && $char <= 12341 } { return W } if {$char >= 12342 && $char <= 12343 } { return W } if {$char >= 12344 && $char <= 12346 } { return W } if {$char == 12347} { return W } if {$char == 12348} { return W } if {$char == 12349} { return W } if {$char == 12350} { return W } if {$char >= 12353 && $char <= 12438 } { return W } if {$char >= 12441 && $char <= 12442 } { return W } if {$char >= 12443 && $char <= 12444 } { return W } if {$char >= 12445 && $char <= 12446 } { return W } if {$char == 12447} { return W } if {$char == 12448} { return W } if {$char >= 12449 && $char <= 12538 } { return W } if {$char == 12539} { return W } if {$char >= 12540 && $char <= 12542 } { return W } if {$char == 12543} { return W } if {$char >= 12549 && $char <= 12591 } { return W } if {$char >= 12593 && $char <= 12686 } { return W } if {$char >= 12688 && $char <= 12689 } { return W } if {$char >= 12690 && $char <= 12693 } { return W } if {$char >= 12694 && $char <= 12703 } { return W } if {$char >= 12704 && $char <= 12730 } { return W } if {$char >= 12736 && $char <= 12771 } { return W } if {$char >= 12784 && $char <= 12799 } { return W } if {$char >= 12800 && $char <= 12830 } { return W } if {$char >= 12832 && $char <= 12841 } { return W } if {$char >= 12842 && $char <= 12871 } { return W } if {$char >= 12872 && $char <= 12879 } { return A } if {$char == 12880} { return W } if {$char >= 12881 && $char <= 12895 } { return W } if {$char >= 12896 && $char <= 12927 } { return W } if {$char >= 12928 && $char <= 12937 } { return W } if {$char >= 12938 && $char <= 12976 } { return W } if {$char >= 12977 && $char <= 12991 } { return W } if {$char >= 12992 && $char <= 13054 } { return W } if {$char >= 13056 && $char <= 13311 } { return W } if {$char >= 13312 && $char <= 19893 } { return W } if {$char >= 19894 && $char <= 19903 } { return W } if {$char >= 19968 && $char <= 40943 } { return W } if {$char >= 40944 && $char <= 40959 } { return W } if {$char >= 40960 && $char <= 40980 } { return W } if {$char == 40981} { return W } if {$char >= 40982 && $char <= 42124 } { return W } if {$char >= 42128 && $char <= 42182 } { return W } if {$char >= 43360 && $char <= 43388 } { return W } if {$char >= 44032 && $char <= 55203 } { return W } if {$char >= 57344 && $char <= 63743 } { return A } if {$char >= 63744 && $char <= 64109 } { return W } if {$char >= 64110 && $char <= 64111 } { return W } if {$char >= 64112 && $char <= 64217 } { return W } if {$char >= 64218 && $char <= 64255 } { return W } if {$char >= 65024 && $char <= 65039 } { return A } if {$char >= 65040 && $char <= 65046 } { return W } if {$char == 65047} { return W } if {$char == 65048} { return W } if {$char == 65049} { return W } if {$char == 65072} { return W } if {$char >= 65073 && $char <= 65074 } { return W } if {$char >= 65075 && $char <= 65076 } { return W } if {$char == 65077} { return W } if {$char == 65078} { return W } if {$char == 65079} { return W } if {$char == 65080} { return W } if {$char == 65081} { return W } if {$char == 65082} { return W } if {$char == 65083} { return W } if {$char == 65084} { return W } if {$char == 65085} { return W } if {$char == 65086} { return W } if {$char == 65087} { return W } if {$char == 65088} { return W } if {$char == 65089} { return W } if {$char == 65090} { return W } if {$char == 65091} { return W } if {$char == 65092} { return W } if {$char >= 65093 && $char <= 65094 } { return W } if {$char == 65095} { return W } if {$char == 65096} { return W } if {$char >= 65097 && $char <= 65100 } { return W } if {$char >= 65101 && $char <= 65103 } { return W } if {$char >= 65104 && $char <= 65106 } { return W } if {$char >= 65108 && $char <= 65111 } { return W } if {$char == 65112} { return W } if {$char == 65113} { return W } if {$char == 65114} { return W } if {$char == 65115} { return W } if {$char == 65116} { return W } if {$char == 65117} { return W } if {$char == 65118} { return W } if {$char >= 65119 && $char <= 65121 } { return W } if {$char == 65122} { return W } if {$char == 65123} { return W } if {$char >= 65124 && $char <= 65126 } { return W } if {$char == 65128} { return W } if {$char == 65129} { return W } if {$char >= 65130 && $char <= 65131 } { return W } if {$char >= 65281 && $char <= 65283 } { return F } if {$char == 65284} { return F } if {$char >= 65285 && $char <= 65287 } { return F } if {$char == 65288} { return F } if {$char == 65289} { return F } if {$char == 65290} { return F } if {$char == 65291} { return F } if {$char == 65292} { return F } if {$char == 65293} { return F } if {$char >= 65294 && $char <= 65295 } { return F } if {$char >= 65296 && $char <= 65305 } { return F } if {$char >= 65306 && $char <= 65307 } { return F } if {$char >= 65308 && $char <= 65310 } { return F } if {$char >= 65311 && $char <= 65312 } { return F } if {$char >= 65313 && $char <= 65338 } { return F } if {$char == 65339} { return F } if {$char == 65340} { return F } if {$char == 65341} { return F } if {$char == 65342} { return F } if {$char == 65343} { return F } if {$char == 65344} { return F } if {$char >= 65345 && $char <= 65370 } { return F } if {$char == 65371} { return F } if {$char == 65372} { return F } if {$char == 65373} { return F } if {$char == 65374} { return F } if {$char == 65375} { return F } if {$char == 65376} { return F } if {$char == 65377} { return H } if {$char == 65378} { return H } if {$char == 65379} { return H } if {$char >= 65380 && $char <= 65381 } { return H } if {$char >= 65382 && $char <= 65391 } { return H } if {$char == 65392} { return H } if {$char >= 65393 && $char <= 65437 } { return H } if {$char >= 65438 && $char <= 65439 } { return H } if {$char >= 65440 && $char <= 65470 } { return H } if {$char >= 65474 && $char <= 65479 } { return H } if {$char >= 65482 && $char <= 65487 } { return H } if {$char >= 65490 && $char <= 65495 } { return H } if {$char >= 65498 && $char <= 65500 } { return H } if {$char >= 65504 && $char <= 65505 } { return F } if {$char == 65506} { return F } if {$char == 65507} { return F } if {$char == 65508} { return F } if {$char >= 65509 && $char <= 65510 } { return F } if {$char == 65512} { return H } if {$char >= 65513 && $char <= 65516 } { return H } if {$char >= 65517 && $char <= 65518 } { return H } if {$char == 65533} { return A } if {$char >= 94176 && $char <= 94177 } { return W } if {$char >= 94208 && $char <= 100337 } { return W } if {$char >= 100352 && $char <= 101106 } { return W } if {$char >= 110592 && $char <= 110847 } { return W } if {$char >= 110848 && $char <= 110878 } { return W } if {$char >= 110960 && $char <= 111355 } { return W } if {$char == 126980} { return W } if {$char == 127183} { return W } if {$char >= 127232 && $char <= 127242 } { return A } if {$char >= 127248 && $char <= 127277 } { return A } if {$char >= 127280 && $char <= 127337 } { return A } if {$char >= 127344 && $char <= 127373 } { return A } if {$char == 127374} { return W } if {$char >= 127375 && $char <= 127376 } { return A } if {$char >= 127377 && $char <= 127386 } { return W } if {$char >= 127387 && $char <= 127404 } { return A } if {$char >= 127488 && $char <= 127490 } { return W } if {$char >= 127504 && $char <= 127547 } { return W } if {$char >= 127552 && $char <= 127560 } { return W } if {$char >= 127568 && $char <= 127569 } { return W } if {$char >= 127584 && $char <= 127589 } { return W } if {$char >= 127744 && $char <= 127776 } { return W } if {$char >= 127789 && $char <= 127797 } { return W } if {$char >= 127799 && $char <= 127868 } { return W } if {$char >= 127870 && $char <= 127891 } { return W } if {$char >= 127904 && $char <= 127946 } { return W } if {$char >= 127951 && $char <= 127955 } { return W } if {$char >= 127968 && $char <= 127984 } { return W } if {$char == 127988} { return W } if {$char >= 127992 && $char <= 127994 } { return W } if {$char >= 127995 && $char <= 127999 } { return W } if {$char >= 128000 && $char <= 128062 } { return W } if {$char == 128064} { return W } if {$char >= 128066 && $char <= 128252 } { return W } if {$char >= 128255 && $char <= 128317 } { return W } if {$char >= 128331 && $char <= 128334 } { return W } if {$char >= 128336 && $char <= 128359 } { return W } if {$char == 128378} { return W } if {$char >= 128405 && $char <= 128406 } { return W } if {$char == 128420} { return W } if {$char >= 128507 && $char <= 128511 } { return W } if {$char >= 128512 && $char <= 128591 } { return W } if {$char >= 128640 && $char <= 128709 } { return W } if {$char == 128716} { return W } if {$char >= 128720 && $char <= 128722 } { return W } if {$char >= 128747 && $char <= 128748 } { return W } if {$char >= 128756 && $char <= 128761 } { return W } if {$char >= 129296 && $char <= 129342 } { return W } if {$char >= 129344 && $char <= 129392 } { return W } if {$char >= 129395 && $char <= 129398 } { return W } if {$char == 129402} { return W } if {$char >= 129404 && $char <= 129442 } { return W } if {$char >= 129456 && $char <= 129465 } { return W } if {$char >= 129472 && $char <= 129474 } { return W } if {$char >= 129488 && $char <= 129535 } { return W } if {$char >= 131072 && $char <= 173782 } { return W } if {$char >= 173783 && $char <= 173823 } { return W } if {$char >= 173824 && $char <= 177972 } { return W } if {$char >= 177973 && $char <= 177983 } { return W } if {$char >= 177984 && $char <= 178205 } { return W } if {$char >= 178206 && $char <= 178207 } { return W } if {$char >= 178208 && $char <= 183969 } { return W } if {$char >= 183970 && $char <= 183983 } { return W } if {$char >= 183984 && $char <= 191456 } { return W } if {$char >= 191457 && $char <= 194559 } { return W } if {$char >= 194560 && $char <= 195101 } { return W } if {$char >= 195102 && $char <= 195103 } { return W } if {$char >= 195104 && $char <= 196605 } { return W } if {$char >= 196608 && $char <= 262141 } { return W } if {$char >= 917760 && $char <= 917999 } { return A } if {$char >= 983040 && $char <= 1048573 } { return A } if {$char >= 1048576 && $char <= 1114109 } { return A } return N } proc ::textutil::wcswidth_char char { if {$char >= 4352 && $char <= 4447 } { return 2 } if {$char >= 8986 && $char <= 8987 } { return 2 } if {$char == 9001} { return 2 } if {$char == 9002} { return 2 } if {$char >= 9193 && $char <= 9196 } { return 2 } if {$char == 9200} { return 2 } if {$char == 9203} { return 2 } if {$char >= 9725 && $char <= 9726 } { return 2 } if {$char >= 9748 && $char <= 9749 } { return 2 } if {$char >= 9800 && $char <= 9811 } { return 2 } if {$char == 9855} { return 2 } if {$char == 9875} { return 2 } if {$char == 9889} { return 2 } if {$char >= 9898 && $char <= 9899 } { return 2 } if {$char >= 9917 && $char <= 9918 } { return 2 } if {$char >= 9924 && $char <= 9925 } { return 2 } if {$char == 9934} { return 2 } if {$char == 9940} { return 2 } if {$char == 9962} { return 2 } if {$char >= 9970 && $char <= 9971 } { return 2 } if {$char == 9973} { return 2 } if {$char == 9978} { return 2 } if {$char == 9981} { return 2 } if {$char == 9989} { return 2 } if {$char >= 9994 && $char <= 9995 } { return 2 } if {$char == 10024} { return 2 } if {$char == 10060} { return 2 } if {$char == 10062} { return 2 } if {$char >= 10067 && $char <= 10069 } { return 2 } if {$char == 10071} { return 2 } if {$char >= 10133 && $char <= 10135 } { return 2 } if {$char == 10160} { return 2 } if {$char == 10175} { return 2 } if {$char >= 11035 && $char <= 11036 } { return 2 } if {$char == 11088} { return 2 } if {$char == 11093} { return 2 } if {$char >= 11904 && $char <= 11929 } { return 2 } if {$char >= 11931 && $char <= 12019 } { return 2 } if {$char >= 12032 && $char <= 12245 } { return 2 } if {$char >= 12272 && $char <= 12283 } { return 2 } if {$char == 12288} { return 2 } if {$char >= 12289 && $char <= 12291 } { return 2 } if {$char == 12292} { return 2 } if {$char == 12293} { return 2 } if {$char == 12294} { return 2 } if {$char == 12295} { return 2 } if {$char == 12296} { return 2 } if {$char == 12297} { return 2 } if {$char == 12298} { return 2 } if {$char == 12299} { return 2 } if {$char == 12300} { return 2 } if {$char == 12301} { return 2 } if {$char == 12302} { return 2 } if {$char == 12303} { return 2 } if {$char == 12304} { return 2 } if {$char == 12305} { return 2 } if {$char >= 12306 && $char <= 12307 } { return 2 } if {$char == 12308} { return 2 } if {$char == 12309} { return 2 } if {$char == 12310} { return 2 } if {$char == 12311} { return 2 } if {$char == 12312} { return 2 } if {$char == 12313} { return 2 } if {$char == 12314} { return 2 } if {$char == 12315} { return 2 } if {$char == 12316} { return 2 } if {$char == 12317} { return 2 } if {$char >= 12318 && $char <= 12319 } { return 2 } if {$char == 12320} { return 2 } if {$char >= 12321 && $char <= 12329 } { return 2 } if {$char >= 12330 && $char <= 12333 } { return 2 } if {$char >= 12334 && $char <= 12335 } { return 2 } if {$char == 12336} { return 2 } if {$char >= 12337 && $char <= 12341 } { return 2 } if {$char >= 12342 && $char <= 12343 } { return 2 } if {$char >= 12344 && $char <= 12346 } { return 2 } if {$char == 12347} { return 2 } if {$char == 12348} { return 2 } if {$char == 12349} { return 2 } if {$char == 12350} { return 2 } if {$char >= 12353 && $char <= 12438 } { return 2 } if {$char >= 12441 && $char <= 12442 } { return 2 } if {$char >= 12443 && $char <= 12444 } { return 2 } if {$char >= 12445 && $char <= 12446 } { return 2 } if {$char == 12447} { return 2 } if {$char == 12448} { return 2 } if {$char >= 12449 && $char <= 12538 } { return 2 } if {$char == 12539} { return 2 } if {$char >= 12540 && $char <= 12542 } { return 2 } if {$char == 12543} { return 2 } if {$char >= 12549 && $char <= 12591 } { return 2 } if {$char >= 12593 && $char <= 12686 } { return 2 } if {$char >= 12688 && $char <= 12689 } { return 2 } if {$char >= 12690 && $char <= 12693 } { return 2 } if {$char >= 12694 && $char <= 12703 } { return 2 } if {$char >= 12704 && $char <= 12730 } { return 2 } if {$char >= 12736 && $char <= 12771 } { return 2 } if {$char >= 12784 && $char <= 12799 } { return 2 } if {$char >= 12800 && $char <= 12830 } { return 2 } if {$char >= 12832 && $char <= 12841 } { return 2 } if {$char >= 12842 && $char <= 12871 } { return 2 } if {$char == 12880} { return 2 } if {$char >= 12881 && $char <= 12895 } { return 2 } if {$char >= 12896 && $char <= 12927 } { return 2 } if {$char >= 12928 && $char <= 12937 } { return 2 } if {$char >= 12938 && $char <= 12976 } { return 2 } if {$char >= 12977 && $char <= 12991 } { return 2 } if {$char >= 12992 && $char <= 13054 } { return 2 } if {$char >= 13056 && $char <= 13311 } { return 2 } if {$char >= 13312 && $char <= 19893 } { return 2 } if {$char >= 19894 && $char <= 19903 } { return 2 } if {$char >= 19968 && $char <= 40943 } { return 2 } if {$char >= 40944 && $char <= 40959 } { return 2 } if {$char >= 40960 && $char <= 40980 } { return 2 } if {$char == 40981} { return 2 } if {$char >= 40982 && $char <= 42124 } { return 2 } if {$char >= 42128 && $char <= 42182 } { return 2 } if {$char >= 43360 && $char <= 43388 } { return 2 } if {$char >= 44032 && $char <= 55203 } { return 2 } if {$char >= 63744 && $char <= 64109 } { return 2 } if {$char >= 64110 && $char <= 64111 } { return 2 } if {$char >= 64112 && $char <= 64217 } { return 2 } if {$char >= 64218 && $char <= 64255 } { return 2 } if {$char >= 65040 && $char <= 65046 } { return 2 } if {$char == 65047} { return 2 } if {$char == 65048} { return 2 } if {$char == 65049} { return 2 } if {$char == 65072} { return 2 } if {$char >= 65073 && $char <= 65074 } { return 2 } if {$char >= 65075 && $char <= 65076 } { return 2 } if {$char == 65077} { return 2 } if {$char == 65078} { return 2 } if {$char == 65079} { return 2 } if {$char == 65080} { return 2 } if {$char == 65081} { return 2 } if {$char == 65082} { return 2 } if {$char == 65083} { return 2 } if {$char == 65084} { return 2 } if {$char == 65085} { return 2 } if {$char == 65086} { return 2 } if {$char == 65087} { return 2 } if {$char == 65088} { return 2 } if {$char == 65089} { return 2 } if {$char == 65090} { return 2 } if {$char == 65091} { return 2 } if {$char == 65092} { return 2 } if {$char >= 65093 && $char <= 65094 } { return 2 } if {$char == 65095} { return 2 } if {$char == 65096} { return 2 } if {$char >= 65097 && $char <= 65100 } { return 2 } if {$char >= 65101 && $char <= 65103 } { return 2 } if {$char >= 65104 && $char <= 65106 } { return 2 } if {$char >= 65108 && $char <= 65111 } { return 2 } if {$char == 65112} { return 2 } if {$char == 65113} { return 2 } if {$char == 65114} { return 2 } if {$char == 65115} { return 2 } if {$char == 65116} { return 2 } if {$char == 65117} { return 2 } if {$char == 65118} { return 2 } if {$char >= 65119 && $char <= 65121 } { return 2 } if {$char == 65122} { return 2 } if {$char == 65123} { return 2 } if {$char >= 65124 && $char <= 65126 } { return 2 } if {$char == 65128} { return 2 } if {$char == 65129} { return 2 } if {$char >= 65130 && $char <= 65131 } { return 2 } if {$char >= 65281 && $char <= 65283 } { return 2 } if {$char == 65284} { return 2 } if {$char >= 65285 && $char <= 65287 } { return 2 } if {$char == 65288} { return 2 } if {$char == 65289} { return 2 } if {$char == 65290} { return 2 } if {$char == 65291} { return 2 } if {$char == 65292} { return 2 } if {$char == 65293} { return 2 } if {$char >= 65294 && $char <= 65295 } { return 2 } if {$char >= 65296 && $char <= 65305 } { return 2 } if {$char >= 65306 && $char <= 65307 } { return 2 } if {$char >= 65308 && $char <= 65310 } { return 2 } if {$char >= 65311 && $char <= 65312 } { return 2 } if {$char >= 65313 && $char <= 65338 } { return 2 } if {$char == 65339} { return 2 } if {$char == 65340} { return 2 } if {$char == 65341} { return 2 } if {$char == 65342} { return 2 } if {$char == 65343} { return 2 } if {$char == 65344} { return 2 } if {$char >= 65345 && $char <= 65370 } { return 2 } if {$char == 65371} { return 2 } if {$char == 65372} { return 2 } if {$char == 65373} { return 2 } if {$char == 65374} { return 2 } if {$char == 65375} { return 2 } if {$char == 65376} { return 2 } if {$char >= 65504 && $char <= 65505 } { return 2 } if {$char == 65506} { return 2 } if {$char == 65507} { return 2 } if {$char == 65508} { return 2 } if {$char >= 65509 && $char <= 65510 } { return 2 } if {$char >= 94176 && $char <= 94177 } { return 2 } if {$char >= 94208 && $char <= 100337 } { return 2 } if {$char >= 100352 && $char <= 101106 } { return 2 } if {$char >= 110592 && $char <= 110847 } { return 2 } if {$char >= 110848 && $char <= 110878 } { return 2 } if {$char >= 110960 && $char <= 111355 } { return 2 } if {$char == 126980} { return 2 } if {$char == 127183} { return 2 } if {$char == 127374} { return 2 } if {$char >= 127377 && $char <= 127386 } { return 2 } if {$char >= 127488 && $char <= 127490 } { return 2 } if {$char >= 127504 && $char <= 127547 } { return 2 } if {$char >= 127552 && $char <= 127560 } { return 2 } if {$char >= 127568 && $char <= 127569 } { return 2 } if {$char >= 127584 && $char <= 127589 } { return 2 } if {$char >= 127744 && $char <= 127776 } { return 2 } if {$char >= 127789 && $char <= 127797 } { return 2 } if {$char >= 127799 && $char <= 127868 } { return 2 } if {$char >= 127870 && $char <= 127891 } { return 2 } if {$char >= 127904 && $char <= 127946 } { return 2 } if {$char >= 127951 && $char <= 127955 } { return 2 } if {$char >= 127968 && $char <= 127984 } { return 2 } if {$char == 127988} { return 2 } if {$char >= 127992 && $char <= 127994 } { return 2 } if {$char >= 127995 && $char <= 127999 } { return 2 } if {$char >= 128000 && $char <= 128062 } { return 2 } if {$char == 128064} { return 2 } if {$char >= 128066 && $char <= 128252 } { return 2 } if {$char >= 128255 && $char <= 128317 } { return 2 } if {$char >= 128331 && $char <= 128334 } { return 2 } if {$char >= 128336 && $char <= 128359 } { return 2 } if {$char == 128378} { return 2 } if {$char >= 128405 && $char <= 128406 } { return 2 } if {$char == 128420} { return 2 } if {$char >= 128507 && $char <= 128511 } { return 2 } if {$char >= 128512 && $char <= 128591 } { return 2 } if {$char >= 128640 && $char <= 128709 } { return 2 } if {$char == 128716} { return 2 } if {$char >= 128720 && $char <= 128722 } { return 2 } if {$char >= 128747 && $char <= 128748 } { return 2 } if {$char >= 128756 && $char <= 128761 } { return 2 } if {$char >= 129296 && $char <= 129342 } { return 2 } if {$char >= 129344 && $char <= 129392 } { return 2 } if {$char >= 129395 && $char <= 129398 } { return 2 } if {$char == 129402} { return 2 } if {$char >= 129404 && $char <= 129442 } { return 2 } if {$char >= 129456 && $char <= 129465 } { return 2 } if {$char >= 129472 && $char <= 129474 } { return 2 } if {$char >= 129488 && $char <= 129535 } { return 2 } if {$char >= 131072 && $char <= 173782 } { return 2 } if {$char >= 173783 && $char <= 173823 } { return 2 } if {$char >= 173824 && $char <= 177972 } { return 2 } if {$char >= 177973 && $char <= 177983 } { return 2 } if {$char >= 177984 && $char <= 178205 } { return 2 } if {$char >= 178206 && $char <= 178207 } { return 2 } if {$char >= 178208 && $char <= 183969 } { return 2 } if {$char >= 183970 && $char <= 183983 } { return 2 } if {$char >= 183984 && $char <= 191456 } { return 2 } if {$char >= 191457 && $char <= 194559 } { return 2 } if {$char >= 194560 && $char <= 195101 } { return 2 } if {$char >= 195102 && $char <= 195103 } { return 2 } if {$char >= 195104 && $char <= 196605 } { return 2 } if {$char >= 196608 && $char <= 262141 } { return 2 } return 1 } proc ::textutil::wcswidth {string} { set width 0 set len [string length $string] for {set i 0} {$i < $len} {incr i} { scan [string index $string $i] %c char set n [::textutil::wcswidth_char $char] if {$n < 0} { return -1 } incr width $n } return $width } |
Changes to modules/virtchannel_base/pkgIndex.tcl.
︙ | ︙ | |||
9 10 11 12 13 14 15 16 17 | package ifneeded tcl::chan::null 1 [list source [file join $dir null.tcl]] package ifneeded tcl::chan::nullzero 1 [list source [file join $dir nullzero.tcl]] package ifneeded tcl::chan::random 1 [list source [file join $dir random.tcl]] package ifneeded tcl::chan::std 1.0.1 [list source [file join $dir std.tcl]] package ifneeded tcl::chan::string 1.0.3 [list source [file join $dir string.tcl]] package ifneeded tcl::chan::textwindow 1 [list source [file join $dir textwindow.tcl]] package ifneeded tcl::chan::variable 1.0.4 [list source [file join $dir variable.tcl]] package ifneeded tcl::chan::zero 1 [list source [file join $dir zero.tcl]] package ifneeded tcl::randomseed 1 [list source [file join $dir randseed.tcl]] | > | 9 10 11 12 13 14 15 16 17 18 | package ifneeded tcl::chan::null 1 [list source [file join $dir null.tcl]] package ifneeded tcl::chan::nullzero 1 [list source [file join $dir nullzero.tcl]] package ifneeded tcl::chan::random 1 [list source [file join $dir random.tcl]] package ifneeded tcl::chan::std 1.0.1 [list source [file join $dir std.tcl]] package ifneeded tcl::chan::string 1.0.3 [list source [file join $dir string.tcl]] package ifneeded tcl::chan::textwindow 1 [list source [file join $dir textwindow.tcl]] package ifneeded tcl::chan::variable 1.0.4 [list source [file join $dir variable.tcl]] package ifneeded tcl::chan::wrapper 1 [list source [file join $dir wrapper.tcl]] package ifneeded tcl::chan::zero 1 [list source [file join $dir zero.tcl]] package ifneeded tcl::randomseed 1 [list source [file join $dir randseed.tcl]] |
Added modules/virtchannel_base/wrapper.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #! /usr/bin/env tclsh # # ## ### ##### ######## ############# # (C) 2018 Poor Yorick # # ## ### ##### ######## ############# package require Tcl 8.6 package require coroutine package require tcl::chan::events oo::class create ::tcl::chan::wrapper { superclass ::tcl::chan::events ; # -> initialize, finalize, watch constructor args { namespace path [list ::coroutine::util {*}[namespace path]] dict size $args foreach {key val} $args { switch $key { chan { set chan $chan } } } next {*}$args } method blocking {c m} { chan blocking $c $m } method cget {c o} { chan configure $chan $o } method cgetall c { chan configure $chan } method configure {c o v} { chan configure $chan $o $v } method finalize {} { close $chan next } method read {c n} { read $c $n } method seek {c o b} { seek $c $o $b } method write {c d} { puts -nonwline $chan $d } variable chan } package provide tcl::chan::wrapper 1 |
Changes to modules/virtchannel_core/transformcore.tcl.
︙ | ︙ | |||
49 50 51 52 53 54 55 | lappend supported $m } } return $supported } method finalize {c} { | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | lappend supported $m } } return $supported } method finalize {c} { set channel {} ; # Prevent destructor from calling close. my destroy return } # # ## ### ##### ######## ############# variable channel |
︙ | ︙ |
Changes to modules/virtchannel_transform/base64.tcl.
︙ | ︙ | |||
38 39 40 41 42 43 44 | return } oo::class create ::tcl::transform::base64::implementation { superclass tcl::transform::core ;# -> initialize, finalize, destructor method write {c data} { | | > > > > > > | > > > > > > > > | | | | > > > > < | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | return } oo::class create ::tcl::transform::base64::implementation { superclass tcl::transform::core ;# -> initialize, finalize, destructor method write {c data} { set res [my Code encodebuf encode $data 3] incr encdount [string length $data] return $res } method read {c data} { set length [string length $data] # remove whitespace to make framing calculations in [Code] accurate # to do: Add a -strict configuration to disallow whitespace? regsub -all {[[:space:]]} $data[set data {}] {} data set res [my Code decodebuf decode $data 4] incr deccount $length return $res } method flush {c} { set data [binary encode base64 $encodebuf] set encodebuf {} return $data } method drain {c} { set length [string length $decodebuf] set data [binary decode base64 $decodebuf] if {$data eq {} && $length} { error [list {invalid input after } $deccount] } set decodebuf {} return $data } method clear {c} { set decodebuf {} return } # # ## ### ##### ######## ############# constructor {} { set encodebuf {} set deccount 0 set enccount 0 set decodebuf {} return } # # ## ### ##### ######## ############# variable enccount encodebuf deccount decodebuf # # ## ### ##### ######## ############# method Code {bufvar op data n} { upvar 1 $bufvar buffer append buffer $data set n [my Complete $buffer $n] if {$n < 0} { return {} } set chunk [string range $buffer 0 $n] set result [binary $op base64 $chunk] if {$result eq {} && $chunk ne {}} { error [list {invalid input after} [ expr {$op eq {encode} ? $enccount : $deccount}]] } incr n set buffer [string range $buffer $n end] return $result } method Complete {buffer n} { set len [string length $buffer] return [expr {(($len / $n) * $n)-1}] } |
︙ | ︙ |
Changes to modules/virtchannel_transform/pkgIndex.tcl.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | if {![package vsatisfies [package provide Tcl] 8.6]} {return} package ifneeded tcl::transform::adler32 1 [list source [file join $dir adler32.tcl]] package ifneeded tcl::transform::base64 1 [list source [file join $dir base64.tcl]] package ifneeded tcl::transform::counter 1 [list source [file join $dir counter.tcl]] package ifneeded tcl::transform::crc32 1 [list source [file join $dir crc32.tcl]] package ifneeded tcl::transform::hex 1 [list source [file join $dir hex.tcl]] package ifneeded tcl::transform::identity 1 [list source [file join $dir identity.tcl]] package ifneeded tcl::transform::limitsize 1 [list source [file join $dir limitsize.tcl]] package ifneeded tcl::transform::observe 1 [list source [file join $dir observe.tcl]] package ifneeded tcl::transform::otp 1 [list source [file join $dir otp.tcl]] package ifneeded tcl::transform::rot 1 [list source [file join $dir rot.tcl]] package ifneeded tcl::transform::spacer 1 [list source [file join $dir spacer.tcl]] package ifneeded tcl::transform::zlib 1.0.1 [list source [file join $dir zlib.tcl]] | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | if {![package vsatisfies [package provide Tcl] 8.6]} {return} package ifneeded tcl::transform::adler32 1 [list source [file join $dir adler32.tcl]] package ifneeded tcl::transform::base64 1 [list source [file join $dir base64.tcl]] package ifneeded tcl::transform::counter 1 [list source [file join $dir counter.tcl]] package ifneeded tcl::transform::crc32 1 [list source [file join $dir crc32.tcl]] package ifneeded tcl::transform::hex 1 [list source [file join $dir hex.tcl]] package ifneeded tcl::transform::identity 1 [list source [file join $dir identity.tcl]] package ifneeded tcl::transform::limitsize 1 [list source [file join $dir limitsize.tcl]] package ifneeded tcl::transform::observe 1 [list source [file join $dir observe.tcl]] package ifneeded tcl::transform::otp 1 [list source [file join $dir otp.tcl]] package ifneeded {tcl transform qp} 0.1 [list source [file join $dir qp.tcl]] package ifneeded tcl::transform::rot 1 [list source [file join $dir rot.tcl]] package ifneeded tcl::transform::spacer 1 [list source [file join $dir spacer.tcl]] package ifneeded tcl::transform::zlib 1.0.1 [list source [file join $dir zlib.tcl]] |
Added modules/virtchannel_transform/qp.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #! /usr/bin/env tclsh # # ## ### ##### ######## ############# # (C) 2018 Poor Yorick # # ## ### ##### ######## ############# package require {mime qp} package require tcl::transform::core namespace eval ::tcl::transform {} proc ::tcl::transform::qp chan { ::chan push $chan [qp::implementation new] return } oo::class create ::tcl::transform::qp::implementation { superclass tcl::transform::core ;# -> initialize, finalize, destructor method write {c data} { ::mime::qp::encode $data } method read {c data} { ::mime::qp::decode $data } } package provide {tcl transform qp} 0.1 |
Changes to support/installation/modules.tcl.
︙ | ︙ | |||
41 42 43 44 45 46 47 48 49 50 51 52 53 54 | Module base64 _tcl _man _null Module bee _tcl _man _null Module bench _tcl _null _null Module bibtex _tcl _man _exa Module blowfish _tcl _man _null Module cache _tcl _man _null Module calendar _tci _man _null Module clay _tcl _man _null Module clock _tcl _man _null Module cmdline _tcl _man _null Module comm _tcl _man _null Module control _tci _man _null Module coroutine _tcl _null _null Module counter _tcl _man _null | > | 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | Module base64 _tcl _man _null Module bee _tcl _man _null Module bench _tcl _null _null Module bibtex _tcl _man _exa Module blowfish _tcl _man _null Module cache _tcl _man _null Module calendar _tci _man _null Module chan _tcl _man _null Module clay _tcl _man _null Module clock _tcl _man _null Module cmdline _tcl _man _null Module comm _tcl _man _null Module control _tci _man _null Module coroutine _tcl _null _null Module counter _tcl _man _null |
︙ | ︙ | |||
63 64 65 66 67 68 69 70 71 72 73 74 75 76 | Module dns _msg _man _exa Module docstrip _tcl _man _null Module doctools _doc _man _exa Module doctools2base _tcl _man _null Module doctools2idx _tcl _man _null Module doctools2toc _tcl _man _null Module dtplite _tcl _man _null Module exif _tcl _man _null Module fileutil _tcl _man _null Module ftp _tcl _man _exa Module ftpd _tcl _man _exa Module fumagic _tcl _man _null Module generator _tcl _man _null Module gpx _tcl _null _null | > | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | Module dns _msg _man _exa Module docstrip _tcl _man _null Module doctools _doc _man _exa Module doctools2base _tcl _man _null Module doctools2idx _tcl _man _null Module doctools2toc _tcl _man _null Module dtplite _tcl _man _null Module ego _tcl _man _null Module exif _tcl _man _null Module fileutil _tcl _man _null Module ftp _tcl _man _exa Module ftpd _tcl _man _exa Module fumagic _tcl _man _null Module generator _tcl _man _null Module gpx _tcl _null _null |
︙ | ︙ |