icu.tcl 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #----------------------------------------------------------------------
  2. #
  3. # icu.tcl --
  4. #
  5. # This file implements the portions of the [tcl::unsupported::icu]
  6. # ensemble that are coded in Tcl.
  7. #
  8. #----------------------------------------------------------------------
  9. #
  10. # Copyright © 2024 Ashok P. Nadkarni
  11. # See the file "license.terms" for information on usage and redistribution
  12. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13. #
  14. #----------------------------------------------------------------------
  15. ::tcl::unsupported::loadIcu
  16. namespace eval ::tcl::unsupported::icu {
  17. # Map Tcl encoding names to ICU and back. Note ICU has multiple aliases
  18. # for the same encoding.
  19. variable tclToIcu
  20. variable icuToTcl
  21. proc LogError {message} {
  22. puts stderr $message
  23. }
  24. proc Init {} {
  25. variable tclToIcu
  26. variable icuToTcl
  27. # There are some special cases where names do not line up
  28. # at all. Map Tcl -> ICU
  29. array set specialCases {
  30. ebcdic ebcdic-cp-us
  31. macCentEuro maccentraleurope
  32. utf16 UTF16_PlatformEndian
  33. utf-16be UnicodeBig
  34. utf-16le UnicodeLittle
  35. utf32 UTF32_PlatformEndian
  36. }
  37. # Ignore all errors. Do not want to hold up Tcl
  38. # if ICU not available
  39. if {[catch {
  40. foreach tclName [encoding names] {
  41. if {[catch {
  42. set icuNames [aliases $tclName]
  43. } erMsg]} {
  44. LogError "Could not get aliases for $tclName: $erMsg"
  45. continue
  46. }
  47. if {[llength $icuNames] == 0} {
  48. # E.g. macGreek -> x-MacGreek
  49. set icuNames [aliases x-$tclName]
  50. if {[llength $icuNames] == 0} {
  51. # Still no joy, check for special cases
  52. if {[info exists specialCases($tclName)]} {
  53. set icuNames [aliases $specialCases($tclName)]
  54. }
  55. }
  56. }
  57. # If the Tcl name is also an ICU name use it else use
  58. # the first name which is the canonical ICU name
  59. set pos [lsearch -exact -nocase $icuNames $tclName]
  60. if {$pos >= 0} {
  61. lappend tclToIcu($tclName) [lindex $icuNames $pos] {*}[lreplace $icuNames $pos $pos]
  62. } else {
  63. set tclToIcu($tclName) $icuNames
  64. }
  65. foreach icuName $icuNames {
  66. lappend icuToTcl($icuName) $tclName
  67. }
  68. }
  69. } errMsg]} {
  70. LogError $errMsg
  71. }
  72. array default set tclToIcu ""
  73. array default set icuToTcl ""
  74. # Redefine ourselves to no-op.
  75. proc Init {} {}
  76. }
  77. # Primarily used during development
  78. proc MappedIcuNames {{pat *}} {
  79. Init
  80. variable icuToTcl
  81. return [array names icuToTcl $pat]
  82. }
  83. # Primarily used during development
  84. proc UnmappedIcuNames {{pat *}} {
  85. Init
  86. variable icuToTcl
  87. set unmappedNames {}
  88. foreach icuName [converters] {
  89. if {[llength [icuToTcl $icuName]] == 0} {
  90. lappend unmappedNames $icuName
  91. }
  92. foreach alias [aliases $icuName] {
  93. if {[llength [icuToTcl $alias]] == 0} {
  94. lappend unmappedNames $alias
  95. }
  96. }
  97. }
  98. # Aliases can be duplicates. Remove
  99. return [lsort -unique [lsearch -inline -all $unmappedNames $pat]]
  100. }
  101. # Primarily used during development
  102. proc UnmappedTclNames {{pat *}} {
  103. Init
  104. variable tclToIcu
  105. set unmappedNames {}
  106. foreach tclName [encoding names] {
  107. # Note entry will always exist. Check if empty
  108. if {[llength [tclToIcu $tclName]] == 0} {
  109. lappend unmappedNames $tclName
  110. }
  111. }
  112. return [lsearch -inline -all $unmappedNames $pat]
  113. }
  114. # Returns the Tcl equivalent of an ICU encoding name or
  115. # the empty string in case not found.
  116. proc icuToTcl {icuName} {
  117. Init
  118. proc icuToTcl {icuName} {
  119. variable icuToTcl
  120. return [lindex $icuToTcl($icuName) 0]
  121. }
  122. icuToTcl $icuName
  123. }
  124. # Returns the ICU equivalent of an Tcl encoding name or
  125. # the empty string in case not found.
  126. proc tclToIcu {tclName} {
  127. Init
  128. proc tclToIcu {tclName} {
  129. variable tclToIcu
  130. return [lindex $tclToIcu($tclName) 0]
  131. }
  132. tclToIcu $tclName
  133. }
  134. namespace export {[a-z]*}
  135. namespace ensemble create
  136. }