]> git.sur5r.net Git - openldap/blob - libraries/liblunicode/ucdata/ucdata.c
Cyrus SASL uses screwy terms.
[openldap] / libraries / liblunicode / ucdata / ucdata.c
1 /*
2  * Copyright 1999 Computing Research Labs, New Mexico State University
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
19  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
20  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 #ifndef lint
23 #ifdef __GNUC__
24 static char rcsid[] __attribute__ ((unused)) = "$Id: ucdata.c,v 1.3 1999/08/23 16:14:09 mleisher Exp $";
25 #else
26 static char rcsid[] = "$Id: ucdata.c,v 1.3 1999/08/23 16:14:09 mleisher Exp $";
27 #endif
28 #endif
29
30 #include "portable.h"
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <ac/string.h>
35 #include <ac/unistd.h>
36
37
38 #include "ucdata.h"
39
40 /**************************************************************************
41  *
42  * Miscellaneous types, data, and support functions.
43  *
44  **************************************************************************/
45
46 typedef struct {
47     unsigned short bom;
48     unsigned short cnt;
49     union {
50         unsigned long bytes;
51         unsigned short len[2];
52     } size;
53 } _ucheader_t;
54
55 /*
56  * A simple array of 32-bit masks for lookup.
57  */
58 static unsigned long masks32[32] = {
59     0x00000001, 0x00000002, 0x00000004, 0x00000008, 0x00000010, 0x00000020,
60     0x00000040, 0x00000080, 0x00000100, 0x00000200, 0x00000400, 0x00000800,
61     0x00001000, 0x00002000, 0x00004000, 0x00008000, 0x00010000, 0x00020000,
62     0x00040000, 0x00080000, 0x00100000, 0x00200000, 0x00400000, 0x00800000,
63     0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000,
64     0x40000000, 0x80000000
65 };
66
67 #define endian_short(cc) (((cc) >> 8) | (((cc) & 0xff) << 8))
68 #define endian_long(cc) ((((cc) & 0xff) << 24)|((((cc) >> 8) & 0xff) << 16)|\
69                         ((((cc) >> 16) & 0xff) << 8)|((cc) >> 24))
70
71 static FILE *
72 _ucopenfile(char *paths, char *filename, char *mode)
73 {
74     FILE *f;
75     char *fp, *dp, *pp, path[BUFSIZ];
76
77     if (filename == 0 || *filename == 0)
78       return 0;
79
80     dp = paths;
81     while (dp && *dp) {
82         pp = path;
83         while (*dp && *dp != ':')
84           *pp++ = *dp++;
85         *pp++ = '/';
86
87         fp = filename;
88         while (*fp)
89           *pp++ = *fp++;
90         *pp = 0;
91
92         if ((f = fopen(path, mode)) != 0)
93           return f;
94
95         if (*dp == ':')
96           dp++;
97     }
98
99     return 0;
100 }
101
102 /**************************************************************************
103  *
104  * Support for the character properties.
105  *
106  **************************************************************************/
107
108 static unsigned long  _ucprop_size;
109 static unsigned short *_ucprop_offsets;
110 static unsigned long  *_ucprop_ranges;
111
112 static void
113 _ucprop_load(char *paths, int reload)
114 {
115     FILE *in;
116     unsigned long size, i;
117     _ucheader_t hdr;
118
119     if (_ucprop_size > 0) {
120         if (!reload)
121           /*
122            * The character properties have already been loaded.
123            */
124           return;
125
126         /*
127          * Unload the current character property data in preparation for
128          * loading a new copy.  Only the first array has to be deallocated
129          * because all the memory for the arrays is allocated as a single
130          * block.
131          */
132         free((char *) _ucprop_offsets);
133         _ucprop_size = 0;
134     }
135
136     if ((in = _ucopenfile(paths, "ctype.dat", "rb")) == 0)
137       return;
138
139     /*
140      * Load the header.
141      */
142     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
143
144     if (hdr.bom == 0xfffe) {
145         hdr.cnt = endian_short(hdr.cnt);
146         hdr.size.bytes = endian_long(hdr.size.bytes);
147     }
148
149     if ((_ucprop_size = hdr.cnt) == 0) {
150         fclose(in);
151         return;
152     }
153
154     /*
155      * Allocate all the storage needed for the lookup table.
156      */
157     _ucprop_offsets = (unsigned short *) malloc(hdr.size.bytes);
158
159     /*
160      * Calculate the offset into the storage for the ranges.  The offsets
161      * array is on a 4-byte boundary and one larger than the value provided in
162      * the header count field.  This means the offset to the ranges must be
163      * calculated after aligning the count to a 4-byte boundary.
164      */
165     if ((size = ((hdr.cnt + 1) * sizeof(unsigned short))) & 3)
166       size += 4 - (size & 3);
167     size >>= 1;
168     _ucprop_ranges = (unsigned long *) (_ucprop_offsets + size);
169
170     /*
171      * Load the offset array.
172      */
173     fread((char *) _ucprop_offsets, sizeof(unsigned short), size, in);
174
175     /*
176      * Do an endian swap if necessary.  Don't forget there is an extra node on
177      * the end with the final index.
178      */
179     if (hdr.bom == 0xfffe) {
180         for (i = 0; i <= _ucprop_size; i++)
181           _ucprop_offsets[i] = endian_short(_ucprop_offsets[i]);
182     }
183
184     /*
185      * Load the ranges.  The number of elements is in the last array position
186      * of the offsets.
187      */
188     fread((char *) _ucprop_ranges, sizeof(unsigned long),
189           _ucprop_offsets[_ucprop_size], in);
190
191     fclose(in);
192
193     /*
194      * Do an endian swap if necessary.
195      */
196     if (hdr.bom == 0xfffe) {
197         for (i = 0; i < _ucprop_offsets[_ucprop_size]; i++)
198           _ucprop_ranges[i] = endian_long(_ucprop_ranges[i]);
199     }
200 }
201
202 static void
203 _ucprop_unload(void)
204 {
205     if (_ucprop_size == 0)
206       return;
207
208     /*
209      * Only need to free the offsets because the memory is allocated as a
210      * single block.
211      */
212     free((char *) _ucprop_offsets);
213     _ucprop_size = 0;
214 }
215
216 static int
217 _ucprop_lookup(unsigned long code, unsigned long n)
218 {
219     long l, r, m;
220
221     /*
222      * There is an extra node on the end of the offsets to allow this routine
223      * to work right.  If the index is 0xffff, then there are no nodes for the
224      * property.
225      */
226     if ((l = _ucprop_offsets[n]) == 0xffff)
227       return 0;
228
229     /*
230      * Locate the next offset that is not 0xffff.  The sentinel at the end of
231      * the array is the max index value.
232      */
233     for (m = 1;
234          n + m < _ucprop_size && _ucprop_offsets[n + m] == 0xffff; m++) ;
235
236     r = _ucprop_offsets[n + m] - 1;
237
238     while (l <= r) {
239         /*
240          * Determine a "mid" point and adjust to make sure the mid point is at
241          * the beginning of a range pair.
242          */
243         m = (l + r) >> 1;
244         m -= (m & 1);
245         if (code > _ucprop_ranges[m + 1])
246           l = m + 2;
247         else if (code < _ucprop_ranges[m])
248           r = m - 2;
249         else if (code >= _ucprop_ranges[m] && code <= _ucprop_ranges[m + 1])
250           return 1;
251     }
252     return 0;
253 }
254
255 int
256 ucisprop(unsigned long code, unsigned long mask1, unsigned long mask2)
257 {
258     unsigned long i;
259
260     if (mask1 == 0 && mask2 == 0)
261       return 0;
262
263     for (i = 0; mask1 && i < 32; i++) {
264         if ((mask1 & masks32[i]) && _ucprop_lookup(code, i))
265           return 1;
266     }
267
268     for (i = 32; mask2 && i < _ucprop_size; i++) {
269         if ((mask2 & masks32[i & 31]) && _ucprop_lookup(code, i))
270           return 1;
271     }
272
273     return 0;
274 }
275
276 /**************************************************************************
277  *
278  * Support for case mapping.
279  *
280  **************************************************************************/
281
282 static unsigned long _uccase_size;
283 static unsigned short _uccase_len[2];
284 static unsigned long *_uccase_map;
285
286 static void
287 _uccase_load(char *paths, int reload)
288 {
289     FILE *in;
290     unsigned long i;
291     _ucheader_t hdr;
292
293     if (_uccase_size > 0) {
294         if (!reload)
295           /*
296            * The case mappings have already been loaded.
297            */
298           return;
299
300         free((char *) _uccase_map);
301         _uccase_size = 0;
302     }
303
304     if ((in = _ucopenfile(paths, "case.dat", "rb")) == 0)
305       return;
306
307     /*
308      * Load the header.
309      */
310     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
311
312     if (hdr.bom == 0xfffe) {
313         hdr.cnt = endian_short(hdr.cnt);
314         hdr.size.len[0] = endian_short(hdr.size.len[0]);
315         hdr.size.len[1] = endian_short(hdr.size.len[1]);
316     }
317
318     /*
319      * Set the node count and lengths of the upper and lower case mapping
320      * tables.
321      */
322     _uccase_size = hdr.cnt * 3;
323     _uccase_len[0] = hdr.size.len[0] * 3;
324     _uccase_len[1] = hdr.size.len[1] * 3;
325
326     _uccase_map = (unsigned long *)
327         malloc(_uccase_size * sizeof(unsigned long));
328
329     /*
330      * Load the case mapping table.
331      */
332     fread((char *) _uccase_map, sizeof(unsigned long), _uccase_size, in);
333
334     /*
335      * Do an endian swap if necessary.
336      */
337     if (hdr.bom == 0xfffe) {
338         for (i = 0; i < _uccase_size; i++)
339           _uccase_map[i] = endian_long(_uccase_map[i]);
340     }
341 }
342
343 static void
344 _uccase_unload(void)
345 {
346     if (_uccase_size == 0)
347       return;
348
349     free((char *) _uccase_map);
350     _uccase_size = 0;
351 }
352
353 static unsigned long
354 _uccase_lookup(unsigned long code, long l, long r, int field)
355 {
356     long m;
357
358     /*
359      * Do the binary search.
360      */
361     while (l <= r) {
362         /*
363          * Determine a "mid" point and adjust to make sure the mid point is at
364          * the beginning of a case mapping triple.
365          */
366         m = (l + r) >> 1;
367         m -= (m % 3);
368         if (code > _uccase_map[m])
369           l = m + 3;
370         else if (code < _uccase_map[m])
371           r = m - 3;
372         else if (code == _uccase_map[m])
373           return _uccase_map[m + field];
374     }
375
376     return code;
377 }
378
379 unsigned long
380 uctoupper(unsigned long code)
381 {
382     int field;
383     long l, r;
384
385     if (ucisupper(code))
386       return code;
387
388     if (ucislower(code)) {
389         /*
390          * The character is lower case.
391          */
392         field = 2;
393         l = _uccase_len[0];
394         r = (l + _uccase_len[1]) - 3;
395     } else {
396         /*
397          * The character is title case.
398          */
399         field = 1;
400         l = _uccase_len[0] + _uccase_len[1];
401         r = _uccase_size - 3;
402     }
403     return _uccase_lookup(code, l, r, field);
404 }
405
406 unsigned long
407 uctolower(unsigned long code)
408 {
409     int field;
410     long l, r;
411
412     if (ucislower(code))
413       return code;
414
415     if (ucisupper(code)) {
416         /*
417          * The character is upper case.
418          */
419         field = 1;
420         l = 0;
421         r = _uccase_len[0] - 3;
422     } else {
423         /*
424          * The character is title case.
425          */
426         field = 2;
427         l = _uccase_len[0] + _uccase_len[1];
428         r = _uccase_size - 3;
429     }
430     return _uccase_lookup(code, l, r, field);
431 }
432
433 unsigned long
434 uctotitle(unsigned long code)
435 {
436     int field;
437     long l, r;
438
439     if (ucistitle(code))
440       return code;
441
442     /*
443      * The offset will always be the same for converting to title case.
444      */
445     field = 2;
446
447     if (ucisupper(code)) {
448         /*
449          * The character is upper case.
450          */
451         l = 0;
452         r = _uccase_len[0] - 3;
453     } else {
454         /*
455          * The character is lower case.
456          */
457         l = _uccase_len[0];
458         r = (l + _uccase_len[1]) - 3;
459     }
460     return _uccase_lookup(code, l, r, field);
461 }
462
463 /**************************************************************************
464  *
465  * Support for decompositions.
466  *
467  **************************************************************************/
468
469 static unsigned long  _ucdcmp_size;
470 static unsigned long *_ucdcmp_nodes;
471 static unsigned long *_ucdcmp_decomp;
472
473 static void
474 _ucdcmp_load(char *paths, int reload)
475 {
476     FILE *in;
477     unsigned long size, i;
478     _ucheader_t hdr;
479
480     if (_ucdcmp_size > 0) {
481         if (!reload)
482           /*
483            * The decompositions have already been loaded.
484            */
485           return;
486
487         free((char *) _ucdcmp_nodes);
488         _ucdcmp_size = 0;
489     }
490
491     if ((in = _ucopenfile(paths, "decomp.dat", "rb")) == 0)
492       return;
493
494     /*
495      * Load the header.
496      */
497     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
498
499     if (hdr.bom == 0xfffe) {
500         hdr.cnt = endian_short(hdr.cnt);
501         hdr.size.bytes = endian_long(hdr.size.bytes);
502     }
503
504     _ucdcmp_size = hdr.cnt << 1;
505     _ucdcmp_nodes = (unsigned long *) malloc(hdr.size.bytes);
506     _ucdcmp_decomp = _ucdcmp_nodes + (_ucdcmp_size + 1);
507
508     /*
509      * Read the decomposition data in.
510      */
511     size = hdr.size.bytes / sizeof(unsigned long);
512     fread((char *) _ucdcmp_nodes, sizeof(unsigned long), size, in);
513
514     /*
515      * Do an endian swap if necessary.
516      */
517     if (hdr.bom == 0xfffe) {
518         for (i = 0; i < size; i++)
519           _ucdcmp_nodes[i] = endian_long(_ucdcmp_nodes[i]);
520     }        
521 }
522
523 static void
524 _ucdcmp_unload(void)
525 {
526     if (_ucdcmp_size == 0)
527       return;
528
529     /*
530      * Only need to free the offsets because the memory is allocated as a
531      * single block.
532      */
533     free((char *) _ucdcmp_nodes);
534     _ucdcmp_size = 0;
535 }
536
537 int
538 ucdecomp(unsigned long code, unsigned long *num, unsigned long **decomp)
539 {
540     long l, r, m;
541
542     l = 0;
543     r = _ucdcmp_nodes[_ucdcmp_size] - 1;
544
545     while (l <= r) {
546         /*
547          * Determine a "mid" point and adjust to make sure the mid point is at
548          * the beginning of a code+offset pair.
549          */
550         m = (l + r) >> 1;
551         m -= (m & 1);
552         if (code > _ucdcmp_nodes[m])
553           l = m + 2;
554         else if (code < _ucdcmp_nodes[m])
555           r = m - 2;
556         else if (code == _ucdcmp_nodes[m]) {
557             *num = _ucdcmp_nodes[m + 3] - _ucdcmp_nodes[m + 1];
558             *decomp = &_ucdcmp_decomp[_ucdcmp_nodes[m + 1]];
559             return 1;
560         }
561     }
562     return 0;
563 }
564
565 int
566 ucdecomp_hangul(unsigned long code, unsigned long *num, unsigned long decomp[])
567 {
568     if (!ucishangul(code))
569       return 0;
570
571     code -= 0xac00;
572     decomp[0] = 0x1100 + (unsigned long) (code / 588);
573     decomp[1] = 0x1161 + (unsigned long) ((code % 588) / 28);
574     decomp[2] = 0x11a7 + (unsigned long) (code % 28);
575     *num = (decomp[2] != 0x11a7) ? 3 : 2;
576
577     return 1;
578 }
579
580 /**************************************************************************
581  *
582  * Support for combining classes.
583  *
584  **************************************************************************/
585
586 static unsigned long  _uccmcl_size;
587 static unsigned long *_uccmcl_nodes;
588
589 static void
590 _uccmcl_load(char *paths, int reload)
591 {
592     FILE *in;
593     unsigned long i;
594     _ucheader_t hdr;
595
596     if (_uccmcl_size > 0) {
597         if (!reload)
598           /*
599            * The combining classes have already been loaded.
600            */
601           return;
602
603         free((char *) _uccmcl_nodes);
604         _uccmcl_size = 0;
605     }
606
607     if ((in = _ucopenfile(paths, "cmbcl.dat", "rb")) == 0)
608       return;
609
610     /*
611      * Load the header.
612      */
613     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
614
615     if (hdr.bom == 0xfffe) {
616         hdr.cnt = endian_short(hdr.cnt);
617         hdr.size.bytes = endian_long(hdr.size.bytes);
618     }
619
620     _uccmcl_size = hdr.cnt * 3;
621     _uccmcl_nodes = (unsigned long *) malloc(hdr.size.bytes);
622
623     /*
624      * Read the combining classes in.
625      */
626     fread((char *) _uccmcl_nodes, sizeof(unsigned long), _uccmcl_size, in);
627
628     /*
629      * Do an endian swap if necessary.
630      */
631     if (hdr.bom == 0xfffe) {
632         for (i = 0; i < _uccmcl_size; i++)
633           _uccmcl_nodes[i] = endian_long(_uccmcl_nodes[i]);
634     }        
635 }
636
637 static void
638 _uccmcl_unload(void)
639 {
640     if (_uccmcl_size == 0)
641       return;
642
643     free((char *) _uccmcl_nodes);
644     _uccmcl_size = 0;
645 }
646
647 unsigned long
648 uccombining_class(unsigned long code)
649 {
650     long l, r, m;
651
652     l = 0;
653     r = _uccmcl_size - 1;
654
655     while (l <= r) {
656         m = (l + r) >> 1;
657         m -= (m % 3);
658         if (code > _uccmcl_nodes[m + 1])
659           l = m + 3;
660         else if (code < _uccmcl_nodes[m])
661           r = m - 3;
662         else if (code >= _uccmcl_nodes[m] && code <= _uccmcl_nodes[m + 1])
663           return _uccmcl_nodes[m + 2];
664     }
665     return 0;
666 }
667
668 /**************************************************************************
669  *
670  * Support for numeric values.
671  *
672  **************************************************************************/
673
674 static unsigned long *_ucnum_nodes;
675 static unsigned long _ucnum_size;
676 static short *_ucnum_vals;
677
678 void
679 _ucnumb_load(char *paths, int reload)
680 {
681     FILE *in;
682     unsigned long size, i;
683     _ucheader_t hdr;
684
685     if (_ucnum_size > 0) {
686         if (!reload)
687           /*
688            * The numbers have already been loaded.
689            */
690           return;
691
692         free((char *) _ucnum_nodes);
693         _ucnum_size = 0;
694     }
695
696     if ((in = _ucopenfile(paths, "num.dat", "rb")) == 0)
697       return;
698
699     /*
700      * Load the header.
701      */
702     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
703
704     if (hdr.bom == 0xfffe) {
705         hdr.cnt = endian_short(hdr.cnt);
706         hdr.size.bytes = endian_long(hdr.size.bytes);
707     }
708
709     _ucnum_size = hdr.cnt;
710     _ucnum_nodes = (unsigned long *) malloc(hdr.size.bytes);
711     _ucnum_vals = (short *) (_ucnum_nodes + _ucnum_size);
712
713     /*
714      * Read the combining classes in.
715      */
716     fread((char *) _ucnum_nodes, sizeof(unsigned char), hdr.size.bytes, in);
717
718     /*
719      * Do an endian swap if necessary.
720      */
721     if (hdr.bom == 0xfffe) {
722         for (i = 0; i < _ucnum_size; i++)
723           _ucnum_nodes[i] = endian_long(_ucnum_nodes[i]);
724
725         /*
726          * Determine the number of values that have to be adjusted.
727          */
728         size = (hdr.size.bytes -
729                 (_ucnum_size * (sizeof(unsigned long) << 1))) /
730             sizeof(short);
731
732         for (i = 0; i < size; i++)
733           _ucnum_vals[i] = endian_short(_ucnum_vals[i]);
734     }        
735 }
736
737 static void
738 _ucnumb_unload(void)
739 {
740     if (_ucnum_size == 0)
741       return;
742
743     free((char *) _ucnum_nodes);
744     _ucnum_size = 0;
745 }
746
747 int
748 ucnumber_lookup(unsigned long code, struct ucnumber *num)
749 {
750     long l, r, m;
751     short *vp;
752
753     l = 0;
754     r = _ucnum_size - 1;
755     while (l <= r) {
756         /*
757          * Determine a "mid" point and adjust to make sure the mid point is at
758          * the beginning of a code+offset pair.
759          */
760         m = (l + r) >> 1;
761         m -= (m & 1);
762         if (code > _ucnum_nodes[m])
763           l = m + 2;
764         else if (code < _ucnum_nodes[m])
765           r = m - 2;
766         else {
767             vp = _ucnum_vals + _ucnum_nodes[m + 1];
768             num->numerator = (int) *vp++;
769             num->denominator = (int) *vp;
770             return 1;
771         }
772     }
773     return 0;
774 }
775
776 int
777 ucdigit_lookup(unsigned long code, int *digit)
778 {
779     long l, r, m;
780     short *vp;
781
782     l = 0;
783     r = _ucnum_size - 1;
784     while (l <= r) {
785         /*
786          * Determine a "mid" point and adjust to make sure the mid point is at
787          * the beginning of a code+offset pair.
788          */
789         m = (l + r) >> 1;
790         m -= (m & 1);
791         if (code > _ucnum_nodes[m])
792           l = m + 2;
793         else if (code < _ucnum_nodes[m])
794           r = m - 2;
795         else {
796             vp = _ucnum_vals + _ucnum_nodes[m + 1];
797             if (*vp == *(vp + 1)) {
798               *digit = *vp;
799               return 1;
800             }
801             return 0;
802         }
803     }
804     return 0;
805 }
806
807 struct ucnumber
808 ucgetnumber(unsigned long code)
809 {
810     struct ucnumber num;
811
812     /*
813      * Initialize with some arbitrary value, because the caller simply cannot
814      * tell for sure if the code is a number without calling the ucisnumber()
815      * macro before calling this function.
816      */
817     num.numerator = num.denominator = -111;
818
819     (void) ucnumber_lookup(code, &num);
820
821     return num;
822 }
823
824 int
825 ucgetdigit(unsigned long code)
826 {
827     int dig;
828
829     /*
830      * Initialize with some arbitrary value, because the caller simply cannot
831      * tell for sure if the code is a number without calling the ucisdigit()
832      * macro before calling this function.
833      */
834     dig = -111;
835
836     (void) ucdigit_lookup(code, &dig);
837
838     return dig;
839 }
840
841 /**************************************************************************
842  *
843  * Setup and cleanup routines.
844  *
845  **************************************************************************/
846
847 void
848 ucdata_load(char *paths, int masks)
849 {
850     if (masks & UCDATA_CTYPE)
851       _ucprop_load(paths, 0);
852     if (masks & UCDATA_CASE)
853       _uccase_load(paths, 0);
854     if (masks & UCDATA_DECOMP)
855       _ucdcmp_load(paths, 0);
856     if (masks & UCDATA_CMBCL)
857       _uccmcl_load(paths, 0);
858     if (masks & UCDATA_NUM)
859       _ucnumb_load(paths, 0);
860 }
861
862 void
863 ucdata_unload(int masks)
864 {
865     if (masks & UCDATA_CTYPE)
866       _ucprop_unload();
867     if (masks & UCDATA_CASE)
868       _uccase_unload();
869     if (masks & UCDATA_DECOMP)
870       _ucdcmp_unload();
871     if (masks & UCDATA_CMBCL)
872       _uccmcl_unload();
873     if (masks & UCDATA_NUM)
874       _ucnumb_unload();
875 }
876
877 void
878 ucdata_reload(char *paths, int masks)
879 {
880     if (masks & UCDATA_CTYPE)
881       _ucprop_load(paths, 1);
882     if (masks & UCDATA_CASE)
883       _uccase_load(paths, 1);
884     if (masks & UCDATA_DECOMP)
885       _ucdcmp_load(paths, 1);
886     if (masks & UCDATA_CMBCL)
887       _uccmcl_load(paths, 1);
888     if (masks & UCDATA_NUM)
889       _ucnumb_load(paths, 1);
890 }
891
892 #ifdef TEST
893
894 void
895 main(void)
896 {
897     int dig;
898     unsigned long i, lo, *dec;
899     struct ucnumber num;
900
901     ucdata_setup(".");
902
903     if (ucisweak(0x30))
904       printf("WEAK\n");
905     else
906       printf("NOT WEAK\n");
907
908     printf("LOWER 0x%04lX\n", uctolower(0xff3a));
909     printf("UPPER 0x%04lX\n", uctoupper(0xff5a));
910
911     if (ucisalpha(0x1d5))
912       printf("ALPHA\n");
913     else
914       printf("NOT ALPHA\n");
915
916     if (ucisupper(0x1d5)) {
917         printf("UPPER\n");
918         lo = uctolower(0x1d5);
919         printf("0x%04lx\n", lo);
920         lo = uctotitle(0x1d5);
921         printf("0x%04lx\n", lo);
922     } else
923       printf("NOT UPPER\n");
924
925     if (ucistitle(0x1d5))
926       printf("TITLE\n");
927     else
928       printf("NOT TITLE\n");
929
930     if (uciscomposite(0x1d5))
931       printf("COMPOSITE\n");
932     else
933       printf("NOT COMPOSITE\n");
934
935     if (ucdecomp(0x1d5, &lo, &dec)) {
936         for (i = 0; i < lo; i++)
937           printf("0x%04lx ", dec[i]);
938         putchar('\n');
939     }
940
941     if ((lo = uccombining_class(0x41)) != 0)
942       printf("0x41 CCL %ld\n", lo);
943
944     if (ucisxdigit(0xfeff))
945       printf("0xFEFF HEX DIGIT\n");
946     else
947       printf("0xFEFF NOT HEX DIGIT\n");
948
949     if (ucisdefined(0x10000))
950       printf("0x10000 DEFINED\n");
951     else
952       printf("0x10000 NOT DEFINED\n");
953
954     if (ucnumber_lookup(0x30, &num)) {
955         if (num.numerator != num.denominator)
956           printf("UCNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
957         else
958           printf("UCNUMBER: 0x30 = %d\n", num.numerator);
959     } else
960       printf("UCNUMBER: 0x30 NOT A NUMBER\n");
961
962     if (ucnumber_lookup(0xbc, &num)) {
963         if (num.numerator != num.denominator)
964           printf("UCNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
965         else
966           printf("UCNUMBER: 0xbc = %d\n", num.numerator);
967     } else
968       printf("UCNUMBER: 0xbc NOT A NUMBER\n");
969
970
971     if (ucnumber_lookup(0xff19, &num)) {
972         if (num.numerator != num.denominator)
973           printf("UCNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator);
974         else
975           printf("UCNUMBER: 0xff19 = %d\n", num.numerator);
976     } else
977       printf("UCNUMBER: 0xff19 NOT A NUMBER\n");
978
979     if (ucnumber_lookup(0x4e00, &num)) {
980         if (num.numerator != num.denominator)
981           printf("UCNUMBER: 0x4e00 = %d/%d\n", num.numerator, num.denominator);
982         else
983           printf("UCNUMBER: 0x4e00 = %d\n", num.numerator);
984     } else
985       printf("UCNUMBER: 0x4e00 NOT A NUMBER\n");
986
987     if (ucdigit_lookup(0x06f9, &dig))
988       printf("UCDIGIT: 0x6f9 = %d\n", dig);
989     else
990       printf("UCDIGIT: 0x6f9 NOT A NUMBER\n");
991
992     dig = ucgetdigit(0x0969);
993     printf("UCGETDIGIT: 0x969 = %d\n", dig);
994
995     num = ucgetnumber(0x30);
996     if (num.numerator != num.denominator)
997       printf("UCGETNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
998     else
999       printf("UCGETNUMBER: 0x30 = %d\n", num.numerator);
1000
1001     num = ucgetnumber(0xbc);
1002     if (num.numerator != num.denominator)
1003       printf("UCGETNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
1004     else
1005       printf("UCGETNUMBER: 0xbc = %d\n", num.numerator);
1006
1007     num = ucgetnumber(0xff19);
1008     if (num.numerator != num.denominator)
1009       printf("UCGETNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator);
1010     else
1011       printf("UCGETNUMBER: 0xff19 = %d\n", num.numerator);
1012
1013     ucdata_cleanup();
1014     exit(0);
1015 }
1016
1017 #endif /* TEST */