1
2#
3# Parameter $zName must be a path to the file UnicodeData.txt. This command
4# reads the file and returns a list of mappings required to remove all
5# diacritical marks from a unicode string. Each mapping is itself a list
6# consisting of two elements - the unicode codepoint and the single ASCII
7# character that it should be replaced with, or an empty string if the
8# codepoint should simply be removed from the input. Examples:
9#
10#   { 224 a  }     (replace codepoint 224 to "a")
11#   { 769 "" }     (remove codepoint 769 from input)
12#
13# Mappings are only returned for non-upper case codepoints. It is assumed
14# that the input has already been folded to lower case.
15#
16proc rd_load_unicodedata_text {zName} {
17  global tl_lookup_table
18
19  set fd [open $zName]
20  set lField {
21    code
22    character_name
23    general_category
24    canonical_combining_classes
25    bidirectional_category
26    character_decomposition_mapping
27    decimal_digit_value
28    digit_value
29    numeric_value
30    mirrored
31    unicode_1_name
32    iso10646_comment_field
33    uppercase_mapping
34    lowercase_mapping
35    titlecase_mapping
36  }
37  set lRet [list]
38
39  while { ![eof $fd] } {
40    set line [gets $fd]
41    if {$line == ""} continue
42
43    set fields [split $line ";"]
44    if {[llength $fields] != [llength $lField]} { error "parse error: $line" }
45    foreach $lField $fields {}
46    if { [llength $character_decomposition_mapping]!=2
47      || [string is xdigit [lindex $character_decomposition_mapping 0]]==0
48    } {
49      continue
50    }
51
52    set iCode  [expr "0x$code"]
53    set iAscii [expr "0x[lindex $character_decomposition_mapping 0]"]
54    set iDia   [expr "0x[lindex $character_decomposition_mapping 1]"]
55
56    if {[info exists tl_lookup_table($iCode)]} continue
57
58    if { ($iAscii >= 97 && $iAscii <= 122)
59      || ($iAscii >= 65 && $iAscii <= 90)
60    } {
61      lappend lRet [list $iCode [string tolower [format %c $iAscii]]]
62      set dia($iDia) 1
63    }
64  }
65
66  foreach d [array names dia] {
67    lappend lRet [list $d ""]
68  }
69  set lRet [lsort -integer -index 0 $lRet]
70
71  close $fd
72  set lRet
73}
74
75
76proc print_rd {map} {
77  global tl_lookup_table
78  set aChar [list]
79  set lRange [list]
80
81  set nRange 1
82  set iFirst  [lindex $map 0 0]
83  set cPrev   [lindex $map 0 1]
84
85  foreach m [lrange $map 1 end] {
86    foreach {i c} $m {}
87
88    if {$cPrev == $c} {
89      for {set j [expr $iFirst+$nRange]} {$j<$i} {incr j} {
90        if {[info exists tl_lookup_table($j)]==0} break
91      }
92
93      if {$j==$i} {
94        set nNew [expr {(1 + $i - $iFirst)}]
95        if {$nNew<=8} {
96          set nRange $nNew
97          continue
98        }
99      }
100    }
101
102    lappend lRange [list $iFirst $nRange]
103    lappend aChar  $cPrev
104
105    set iFirst $i
106    set cPrev  $c
107    set nRange 1
108  }
109  lappend lRange [list $iFirst $nRange]
110  lappend aChar $cPrev
111
112  puts "/*"
113  puts "** If the argument is a codepoint corresponding to a lowercase letter"
114  puts "** in the ASCII range with a diacritic added, return the codepoint"
115  puts "** of the ASCII letter only. For example, if passed 235 - \"LATIN"
116  puts "** SMALL LETTER E WITH DIAERESIS\" - return 65 (\"LATIN SMALL LETTER"
117  puts "** E\"). The resuls of passing a codepoint that corresponds to an"
118  puts "** uppercase letter are undefined."
119  puts "*/"
120  puts "static int remove_diacritic(int c)\{"
121  puts "  unsigned short aDia\[\] = \{"
122  puts -nonewline "        0, "
123  set i 1
124  foreach r $lRange {
125    foreach {iCode nRange} $r {}
126    if {($i % 8)==0} {puts "" ; puts -nonewline "    " }
127    incr i
128
129    puts -nonewline [format "%5d" [expr ($iCode<<3) + $nRange-1]]
130    puts -nonewline ", "
131  }
132  puts ""
133  puts "  \};"
134  puts "  char aChar\[\] = \{"
135  puts -nonewline "    '\\0', "
136  set i 1
137  foreach c $aChar {
138    set str "'$c',  "
139    if {$c == ""} { set str "'\\0', " }
140
141    if {($i % 12)==0} {puts "" ; puts -nonewline "    " }
142    incr i
143    puts -nonewline "$str"
144  }
145  puts ""
146  puts "  \};"
147  puts {
148  unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
149  int iRes = 0;
150  int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1;
151  int iLo = 0;
152  while( iHi>=iLo ){
153    int iTest = (iHi + iLo) / 2;
154    if( key >= aDia[iTest] ){
155      iRes = iTest;
156      iLo = iTest+1;
157    }else{
158      iHi = iTest-1;
159    }
160  }
161  assert( key>=aDia[iRes] );
162  return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);}
163  puts "\};"
164}
165
166proc print_isdiacritic {zFunc map} {
167
168  set lCode [list]
169  foreach m $map {
170    foreach {code char} $m {}
171    if {$code && $char == ""} { lappend lCode $code }
172  }
173  set lCode [lsort -integer $lCode]
174  set iFirst [lindex $lCode 0]
175  set iLast [lindex $lCode end]
176
177  set i1 0
178  set i2 0
179
180  foreach c $lCode {
181    set i [expr $c - $iFirst]
182    if {$i < 32} {
183      set i1 [expr {$i1 | (1<<$i)}]
184    } else {
185      set i2 [expr {$i2 | (1<<($i-32))}]
186    }
187  }
188
189  puts "/*"
190  puts "** Return true if the argument interpreted as a unicode codepoint"
191  puts "** is a diacritical modifier character."
192  puts "*/"
193  puts "int ${zFunc}\(int c)\{"
194  puts "  unsigned int mask0 = [format "0x%08X" $i1];"
195  puts "  unsigned int mask1 = [format "0x%08X" $i2];"
196
197  puts "  if( c<$iFirst || c>$iLast ) return 0;"
198  puts "  return (c < $iFirst+32) ?"
199  puts "      (mask0 & (1 << (c-$iFirst))) :"
200  puts "      (mask1 & (1 << (c-$iFirst-32)));"
201  puts "\}"
202}
203
204
205#-------------------------------------------------------------------------
206
207# Parameter $zName must be a path to the file UnicodeData.txt. This command
208# reads the file and returns a list of codepoints (integers). The list
209# contains all codepoints in the UnicodeData.txt assigned to any "General
210# Category" that is not a "Letter" or "Number".
211#
212proc an_load_unicodedata_text {zName} {
213  set fd [open $zName]
214  set lField {
215    code
216    character_name
217    general_category
218    canonical_combining_classes
219    bidirectional_category
220    character_decomposition_mapping
221    decimal_digit_value
222    digit_value
223    numeric_value
224    mirrored
225    unicode_1_name
226    iso10646_comment_field
227    uppercase_mapping
228    lowercase_mapping
229    titlecase_mapping
230  }
231  set lRet [list]
232
233  while { ![eof $fd] } {
234    set line [gets $fd]
235    if {$line == ""} continue
236
237    set fields [split $line ";"]
238    if {[llength $fields] != [llength $lField]} { error "parse error: $line" }
239    foreach $lField $fields {}
240
241    set iCode [expr "0x$code"]
242    set bAlnum [expr {
243         [lsearch {L N} [string range $general_category 0 0]] >= 0
244      || $general_category=="Co"
245    }]
246
247    if { !$bAlnum } { lappend lRet $iCode }
248  }
249
250  close $fd
251  set lRet
252}
253
254proc an_load_separator_ranges {} {
255  global unicodedata.txt
256  set lSep [an_load_unicodedata_text ${unicodedata.txt}]
257  unset -nocomplain iFirst
258  unset -nocomplain nRange
259  set lRange [list]
260  foreach sep $lSep {
261    if {0==[info exists iFirst]} {
262      set iFirst $sep
263      set nRange 1
264    } elseif { $sep == ($iFirst+$nRange) } {
265      incr nRange
266    } else {
267      lappend lRange [list $iFirst $nRange]
268      set iFirst $sep
269      set nRange 1
270    }
271  }
272  lappend lRange [list $iFirst $nRange]
273  set lRange
274}
275
276proc an_print_range_array {lRange} {
277  set iFirstMax 0
278  set nRangeMax 0
279  foreach range $lRange {
280    foreach {iFirst nRange} $range {}
281    if {$iFirst > $iFirstMax} {set iFirstMax $iFirst}
282    if {$nRange > $nRangeMax} {set nRangeMax $nRange}
283  }
284  if {$iFirstMax >= (1<<22)} {error "first-max is too large for format"}
285  if {$nRangeMax >= (1<<10)} {error "range-max is too large for format"}
286
287  puts -nonewline "  "
288  puts [string trim {
289  /* Each unsigned integer in the following array corresponds to a contiguous
290  ** range of unicode codepoints that are not either letters or numbers (i.e.
291  ** codepoints for which this function should return 0).
292  **
293  ** The most significant 22 bits in each 32-bit value contain the first
294  ** codepoint in the range. The least significant 10 bits are used to store
295  ** the size of the range (always at least 1). In other words, the value
296  ** ((C<<22) + N) represents a range of N codepoints starting with codepoint
297  ** C. It is not possible to represent a range larger than 1023 codepoints
298  ** using this format.
299  */
300  }]
301  puts -nonewline "  const static unsigned int aEntry\[\] = \{"
302  set i 0
303  foreach range $lRange {
304    foreach {iFirst nRange} $range {}
305    set u32 [format "0x%08X" [expr ($iFirst<<10) + $nRange]]
306
307    if {($i % 5)==0} {puts "" ; puts -nonewline "   "}
308    puts -nonewline " $u32,"
309    incr i
310  }
311  puts ""
312  puts "  \};"
313}
314
315proc an_print_ascii_bitmap {lRange} {
316  foreach range $lRange {
317    foreach {iFirst nRange} $range {}
318    for {set i $iFirst} {$i < ($iFirst+$nRange)} {incr i} {
319      if {$i<=127} { set a($i) 1 }
320    }
321  }
322
323  set aAscii [list 0 0 0 0]
324  foreach key [array names a] {
325    set idx [expr $key >> 5]
326    lset aAscii $idx [expr [lindex $aAscii $idx] | (1 << ($key&0x001F))]
327  }
328
329  puts "  static const unsigned int aAscii\[4\] = \{"
330  puts -nonewline "   "
331  foreach v $aAscii { puts -nonewline [format " 0x%08X," $v] }
332  puts ""
333  puts "  \};"
334}
335
336proc print_isalnum {zFunc lRange} {
337  puts "/*"
338  puts "** Return true if the argument corresponds to a unicode codepoint"
339  puts "** classified as either a letter or a number. Otherwise false."
340  puts "**"
341  puts "** The results are undefined if the value passed to this function"
342  puts "** is less than zero."
343  puts "*/"
344  puts "int ${zFunc}\(int c)\{"
345  an_print_range_array $lRange
346  an_print_ascii_bitmap $lRange
347  puts {
348  if( c<128 ){
349    return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
350  }else if( c<(1<<22) ){
351    unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
352    int iRes;
353    int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
354    int iLo = 0;
355    while( iHi>=iLo ){
356      int iTest = (iHi + iLo) / 2;
357      if( key >= aEntry[iTest] ){
358        iRes = iTest;
359        iLo = iTest+1;
360      }else{
361        iHi = iTest-1;
362      }
363    }
364    assert( aEntry[0]<key );
365    assert( key>=aEntry[iRes] );
366    return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
367  }
368  return 1;}
369  puts "\}"
370}
371
372proc print_test_isalnum {zFunc lRange} {
373  foreach range $lRange {
374    foreach {iFirst nRange} $range {}
375    for {set i $iFirst} {$i < ($iFirst+$nRange)} {incr i} { set a($i) 1 }
376  }
377
378  puts "static int isalnum_test(int *piCode)\{"
379  puts -nonewline "  unsigned char aAlnum\[\] = \{"
380  for {set i 0} {$i < 70000} {incr i} {
381    if {($i % 32)==0} { puts "" ; puts -nonewline "    " }
382    set bFlag [expr ![info exists a($i)]]
383    puts -nonewline "${bFlag},"
384  }
385  puts ""
386  puts "  \};"
387
388  puts -nonewline "  int aLargeSep\[\] = \{"
389  set i 0
390  foreach iSep [lsort -integer [array names a]] {
391    if {$iSep<70000} continue
392    if {($i % 8)==0} { puts "" ; puts -nonewline "   " }
393    puts -nonewline " $iSep,"
394    incr i
395  }
396  puts ""
397  puts "  \};"
398  puts -nonewline "  int aLargeOther\[\] = \{"
399  set i 0
400  foreach iSep [lsort -integer [array names a]] {
401    if {$iSep<70000} continue
402    if {[info exists a([expr $iSep-1])]==0} {
403      if {($i % 8)==0} { puts "" ; puts -nonewline "   " }
404      puts -nonewline " [expr $iSep-1],"
405      incr i
406    }
407    if {[info exists a([expr $iSep+1])]==0} {
408      if {($i % 8)==0} { puts "" ; puts -nonewline "   " }
409      puts -nonewline " [expr $iSep+1],"
410      incr i
411    }
412  }
413  puts ""
414  puts "  \};"
415
416  puts [subst -nocommands {
417  int i;
418  for(i=0; i<sizeof(aAlnum)/sizeof(aAlnum[0]); i++){
419    if( ${zFunc}(i)!=aAlnum[i] ){
420      *piCode = i;
421      return 1;
422    }
423  }
424  for(i=0; i<sizeof(aLargeSep)/sizeof(aLargeSep[0]); i++){
425    if( ${zFunc}(aLargeSep[i])!=0 ){
426      *piCode = aLargeSep[i];
427      return 1;
428    }
429  }
430  for(i=0; i<sizeof(aLargeOther)/sizeof(aLargeOther[0]); i++){
431    if( ${zFunc}(aLargeOther[i])!=1 ){
432      *piCode = aLargeOther[i];
433      return 1;
434    }
435  }
436  }]
437  puts "  return 0;"
438  puts "\}"
439}
440
441#-------------------------------------------------------------------------
442
443proc tl_load_casefolding_txt {zName} {
444  global tl_lookup_table
445
446  set fd [open $zName]
447  while { ![eof $fd] } {
448    set line [gets $fd]
449    if {[string range $line 0 0] == "#"} continue
450    if {$line == ""} continue
451
452    foreach x {a b c d} {unset -nocomplain $x}
453    foreach {a b c d} [split $line ";"] {}
454
455    set a2 [list]
456    set c2 [list]
457    foreach elem $a { lappend a2 [expr "0x[string trim $elem]"] }
458    foreach elem $c { lappend c2 [expr "0x[string trim $elem]"] }
459    set b [string trim $b]
460    set d [string trim $d]
461
462    if {$b=="C" || $b=="S"} { set tl_lookup_table($a2) $c2 }
463  }
464}
465
466proc tl_create_records {} {
467  global tl_lookup_table
468
469  set iFirst ""
470  set nOff 0
471  set nRange 0
472  set nIncr 0
473
474  set lRecord [list]
475  foreach code [lsort -integer [array names tl_lookup_table]] {
476    set mapping $tl_lookup_table($code)
477    if {$iFirst == ""} {
478      set iFirst $code
479      set nOff   [expr $mapping - $code]
480      set nRange 1
481      set nIncr 1
482    } else {
483      set diff [expr $code - ($iFirst + ($nIncr * ($nRange - 1)))]
484      if { $nRange==1 && ($diff==1 || $diff==2) } {
485        set nIncr $diff
486      }
487
488      if {$diff != $nIncr || ($mapping - $code)!=$nOff} {
489        if { $nRange==1 } {set nIncr 1}
490        lappend lRecord [list $iFirst $nIncr $nRange $nOff]
491        set iFirst $code
492        set nOff   [expr $mapping - $code]
493        set nRange 1
494        set nIncr 1
495      } else {
496        incr nRange
497      }
498    }
499  }
500
501  lappend lRecord [list $iFirst $nIncr $nRange $nOff]
502
503  set lRecord
504}
505
506proc tl_print_table_header {} {
507  puts -nonewline "  "
508  puts [string trim {
509  /* Each entry in the following array defines a rule for folding a range
510  ** of codepoints to lower case. The rule applies to a range of nRange
511  ** codepoints starting at codepoint iCode.
512  **
513  ** If the least significant bit in flags is clear, then the rule applies
514  ** to all nRange codepoints (i.e. all nRange codepoints are upper case and
515  ** need to be folded). Or, if it is set, then the rule only applies to
516  ** every second codepoint in the range, starting with codepoint C.
517  **
518  ** The 7 most significant bits in flags are an index into the aiOff[]
519  ** array. If a specific codepoint C does require folding, then its lower
520  ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF).
521  **
522  ** The contents of this array are generated by parsing the CaseFolding.txt
523  ** file distributed as part of the "Unicode Character Database". See
524  ** http://www.unicode.org for details.
525  */
526  }]
527  puts "  static const struct TableEntry \{"
528  puts "    unsigned short iCode;"
529  puts "    unsigned char flags;"
530  puts "    unsigned char nRange;"
531  puts "  \} aEntry\[\] = \{"
532}
533
534proc tl_print_table_entry {togglevar entry liOff} {
535  upvar $togglevar t
536  foreach {iFirst nIncr nRange nOff} $entry {}
537
538  if {$iFirst > (1<<16)} { return 1 }
539
540  if {[info exists t]==0} {set t 0}
541  if {$t==0} { puts -nonewline "    " }
542
543  set flags 0
544  if {$nIncr==2} { set flags 1 ; set nRange [expr $nRange * 2]}
545  if {$nOff<0}   { incr nOff [expr (1<<16)] }
546
547  set idx [lsearch $liOff $nOff]
548  if {$idx<0} {error "malfunction generating aiOff"}
549  set flags [expr $flags + $idx*2]
550
551  set txt "{$iFirst, $flags, $nRange},"
552  if {$t==2} {
553    puts $txt
554  } else {
555    puts -nonewline [format "% -23s" $txt]
556  }
557  set t [expr ($t+1)%3]
558
559  return 0
560}
561
562proc tl_print_table_footer {togglevar} {
563  upvar $togglevar t
564  if {$t!=0} {puts ""}
565  puts "  \};"
566}
567
568proc tl_print_if_entry {entry} {
569  foreach {iFirst nIncr nRange nOff} $entry {}
570  if {$nIncr==2} {error "tl_print_if_entry needs improvement!"}
571
572  puts "  else if( c>=$iFirst && c<[expr $iFirst+$nRange] )\{"
573  puts "    ret = c + $nOff;"
574  puts "  \}"
575}
576
577proc tl_generate_ioff_table {lRecord} {
578  foreach entry $lRecord {
579    foreach {iFirst nIncr nRange iOff} $entry {}
580    if {$iOff<0}   { incr iOff [expr (1<<16)] }
581    if {[info exists a($iOff)]} continue
582    set a($iOff) 1
583  }
584
585  set liOff [lsort -integer [array names a]]
586  if {[llength $liOff]>128} { error "Too many distinct ioffs" }
587  return $liOff
588}
589
590proc tl_print_ioff_table {liOff} {
591  puts -nonewline "  static const unsigned short aiOff\[\] = \{"
592  set i 0
593  foreach off $liOff {
594    if {($i % 8)==0} {puts "" ; puts -nonewline "   "}
595    puts -nonewline [format "% -7s" "$off,"]
596    incr i
597  }
598  puts ""
599  puts "  \};"
600
601}
602
603proc print_fold {zFunc} {
604
605  set lRecord [tl_create_records]
606
607  set lHigh [list]
608  puts "/*"
609  puts "** Interpret the argument as a unicode codepoint. If the codepoint"
610  puts "** is an upper case character that has a lower case equivalent,"
611  puts "** return the codepoint corresponding to the lower case version."
612  puts "** Otherwise, return a copy of the argument."
613  puts "**"
614  puts "** The results are undefined if the value passed to this function"
615  puts "** is less than zero."
616  puts "*/"
617  puts "int ${zFunc}\(int c, int bRemoveDiacritic)\{"
618
619  set liOff [tl_generate_ioff_table $lRecord]
620  tl_print_table_header
621  foreach entry $lRecord {
622    if {[tl_print_table_entry toggle $entry $liOff]} {
623      lappend lHigh $entry
624    }
625  }
626  tl_print_table_footer toggle
627  tl_print_ioff_table $liOff
628
629  puts {
630  int ret = c;
631
632  assert( c>=0 );
633  assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 );
634
635  if( c<128 ){
636    if( c>='A' && c<='Z' ) ret = c + ('a' - 'A');
637  }else if( c<65536 ){
638    int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
639    int iLo = 0;
640    int iRes = -1;
641
642    while( iHi>=iLo ){
643      int iTest = (iHi + iLo) / 2;
644      int cmp = (c - aEntry[iTest].iCode);
645      if( cmp>=0 ){
646        iRes = iTest;
647        iLo = iTest+1;
648      }else{
649        iHi = iTest-1;
650      }
651    }
652    assert( iRes<0 || c>=aEntry[iRes].iCode );
653
654    if( iRes>=0 ){
655      const struct TableEntry *p = &aEntry[iRes];
656      if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
657        ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
658        assert( ret>0 );
659      }
660    }
661
662    if( bRemoveDiacritic ) ret = remove_diacritic(ret);
663  }
664  }
665
666  foreach entry $lHigh {
667    tl_print_if_entry $entry
668  }
669
670  puts ""
671  puts "  return ret;"
672  puts "\}"
673}
674
675proc print_fold_test {zFunc mappings} {
676  global tl_lookup_table
677
678  foreach m $mappings {
679    set c [lindex $m 1]
680    if {$c == ""} {
681      set extra([lindex $m 0]) 0
682    } else {
683      scan $c %c i
684      set extra([lindex $m 0]) $i
685    }
686  }
687
688  puts "static int fold_test(int *piCode)\{"
689  puts -nonewline "  static int aLookup\[\] = \{"
690  for {set i 0} {$i < 70000} {incr i} {
691
692    set expected $i
693    catch { set expected $tl_lookup_table($i) }
694    set expected2 $expected
695    catch { set expected2 $extra($expected2) }
696
697    if {($i % 4)==0}  { puts "" ; puts -nonewline "    " }
698    puts -nonewline "$expected, $expected2, "
699  }
700  puts "  \};"
701  puts "  int i;"
702  puts "  for(i=0; i<sizeof(aLookup)/sizeof(aLookup\[0\]); i++)\{"
703  puts "    int iCode = (i/2);"
704  puts "    int bFlag = i & 0x0001;"
705  puts "    if( ${zFunc}\(iCode, bFlag)!=aLookup\[i\] )\{"
706  puts "      *piCode = iCode;"
707  puts "      return 1;"
708  puts "    \}"
709  puts "  \}"
710  puts "  return 0;"
711  puts "\}"
712}
713
714
715proc print_fileheader {} {
716  puts [string trim {
717/*
718** 2012 May 25
719**
720** The author disclaims copyright to this source code.  In place of
721** a legal notice, here is a blessing:
722**
723**    May you do good and not evil.
724**    May you find forgiveness for yourself and forgive others.
725**    May you share freely, never taking more than you give.
726**
727******************************************************************************
728*/
729
730/*
731** DO NOT EDIT THIS MACHINE GENERATED FILE.
732*/
733  }]
734  puts ""
735  puts "#if defined(SQLITE_ENABLE_FTS4_UNICODE61)"
736  puts "#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)"
737  puts ""
738  puts "#include <assert.h>"
739  puts ""
740}
741
742proc print_test_main {} {
743  puts ""
744  puts "#include <stdio.h>"
745  puts ""
746  puts "int main(int argc, char **argv)\{"
747  puts "  int r1, r2;"
748  puts "  int code;"
749  puts "  r1 = isalnum_test(&code);"
750  puts "  if( r1 ) printf(\"isalnum(): Problem with code %d\\n\",code);"
751  puts "  else printf(\"isalnum(): test passed\\n\");"
752  puts "  r2 = fold_test(&code);"
753  puts "  if( r2 ) printf(\"fold(): Problem with code %d\\n\",code);"
754  puts "  else printf(\"fold(): test passed\\n\");"
755  puts "  return (r1 || r2);"
756  puts "\}"
757}
758
759# Proces the command line arguments. Exit early if they are not to
760# our liking.
761#
762proc usage {} {
763  puts -nonewline stderr "Usage: $::argv0 ?-test? "
764  puts            stderr "<CaseFolding.txt file> <UnicodeData.txt file>"
765  exit 1
766}
767if {[llength $argv]!=2 && [llength $argv]!=3} usage
768if {[llength $argv]==3 && [lindex $argv 0]!="-test"} usage
769set unicodedata.txt [lindex $argv end]
770set casefolding.txt [lindex $argv end-1]
771set generate_test_code [expr {[llength $argv]==3}]
772
773print_fileheader
774
775# Print the isalnum() function to stdout.
776#
777set lRange [an_load_separator_ranges]
778print_isalnum sqlite3FtsUnicodeIsalnum $lRange
779
780# Leave a gap between the two generated C functions.
781#
782puts ""
783puts ""
784
785# Load the fold data. This is used by the [rd_XXX] commands
786# as well as [print_fold].
787tl_load_casefolding_txt ${casefolding.txt}
788
789set mappings [rd_load_unicodedata_text ${unicodedata.txt}]
790print_rd $mappings
791puts ""
792puts ""
793print_isdiacritic sqlite3FtsUnicodeIsdiacritic $mappings
794puts ""
795puts ""
796
797# Print the fold() function to stdout.
798#
799print_fold sqlite3FtsUnicodeFold
800
801# Print the test routines and main() function to stdout, if -test
802# was specified.
803#
804if {$::generate_test_code} {
805  print_test_isalnum sqlite3FtsUnicodeIsalnum $lRange
806  print_fold_test sqlite3FtsUnicodeFold $mappings
807  print_test_main
808}
809
810puts "#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */"
811puts "#endif /* !defined(SQLITE_ENABLE_FTS4_UNICODE61) */"
812