]> git.sur5r.net Git - openldap/blob - libraries/liblunicode/ucdata/ucdata.c
457a8a88359e624bf4adc05b9c6a0421d28f3f2b
[openldap] / libraries / liblunicode / ucdata / ucdata.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 2000 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /*
7  * Copyright 2001 Computing Research Labs, New Mexico State University
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13  * and/or sell copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
22  * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
23  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
24  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
25  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27 /* $Id: ucdata.c,v 1.4 2001/01/02 18:46:20 mleisher Exp $" */
28
29 #include "portable.h"
30 #include "ldap_config.h"
31
32 #include <stdio.h>
33 #include <ac/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         0x00000001UL, 0x00000002UL, 0x00000004UL, 0x00000008UL,
60         0x00000010UL, 0x00000020UL, 0x00000040UL, 0x00000080UL,
61         0x00000100UL, 0x00000200UL, 0x00000400UL, 0x00000800UL,
62         0x00001000UL, 0x00002000UL, 0x00004000UL, 0x00008000UL,
63         0x00010000UL, 0x00020000UL, 0x00040000UL, 0x00080000UL,
64         0x00100000UL, 0x00200000UL, 0x00400000UL, 0x00800000UL,
65         0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL,
66         0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL
67 };
68
69 #define endian_short(cc) (((cc) >> 8) | (((cc) & 0xff) << 8))
70 #define endian_long(cc) ((((cc) & 0xff) << 24)|((((cc) >> 8) & 0xff) << 16)|\
71                         ((((cc) >> 16) & 0xff) << 8)|((cc) >> 24))
72
73 static FILE *
74 _ucopenfile(char *paths, char *filename, char *mode)
75 {
76     FILE *f;
77     char *fp, *dp, *pp, path[BUFSIZ];
78
79     if (filename == 0 || *filename == 0)
80       return 0;
81
82     dp = paths;
83     while (dp && *dp) {
84         pp = path;
85         while (*dp && *dp != ':')
86           *pp++ = *dp++;
87         *pp++ = *LDAP_DIRSEP;
88
89         fp = filename;
90         while (*fp)
91           *pp++ = *fp++;
92         *pp = 0;
93
94         if ((f = fopen(path, mode)) != 0)
95           return f;
96
97         if (*dp == ':')
98           dp++;
99     }
100
101     return 0;
102 }
103
104 /**************************************************************************
105  *
106  * Support for the character properties.
107  *
108  **************************************************************************/
109
110 static unsigned long  _ucprop_size;
111 static unsigned short *_ucprop_offsets;
112 static unsigned long  *_ucprop_ranges;
113
114 /*
115  * Return -1 on error, 0 if okay
116  */
117 static int
118 _ucprop_load(char *paths, int reload)
119 {
120     FILE *in;
121     unsigned long size, i;
122     _ucheader_t hdr;
123
124     if (_ucprop_size > 0) {
125         if (!reload)
126           /*
127            * The character properties have already been loaded.
128            */
129           return 0;
130
131         /*
132          * Unload the current character property data in preparation for
133          * loading a new copy.  Only the first array has to be deallocated
134          * because all the memory for the arrays is allocated as a single
135          * block.
136          */
137         free((char *) _ucprop_offsets);
138         _ucprop_size = 0;
139     }
140
141     if ((in = _ucopenfile(paths, "ctype.dat", "rb")) == 0)
142       return -1;
143
144     /*
145      * Load the header.
146      */
147     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
148
149     if (hdr.bom == 0xfffe) {
150         hdr.cnt = endian_short(hdr.cnt);
151         hdr.size.bytes = endian_long(hdr.size.bytes);
152     }
153
154     if ((_ucprop_size = hdr.cnt) == 0) {
155         fclose(in);
156         return -1;
157     }
158
159     /*
160      * Allocate all the storage needed for the lookup table.
161      */
162     _ucprop_offsets = (unsigned short *) malloc(hdr.size.bytes);
163
164     /*
165      * Calculate the offset into the storage for the ranges.  The offsets
166      * array is on a 4-byte boundary and one larger than the value provided in
167      * the header count field.  This means the offset to the ranges must be
168      * calculated after aligning the count to a 4-byte boundary.
169      */
170     if ((size = ((hdr.cnt + 1) * sizeof(unsigned short))) & 3)
171       size += 4 - (size & 3);
172     size >>= 1;
173     _ucprop_ranges = (unsigned long *) (_ucprop_offsets + size);
174
175     /*
176      * Load the offset array.
177      */
178     fread((char *) _ucprop_offsets, sizeof(unsigned short), size, in);
179
180     /*
181      * Do an endian swap if necessary.  Don't forget there is an extra node on
182      * the end with the final index.
183      */
184     if (hdr.bom == 0xfffe) {
185         for (i = 0; i <= _ucprop_size; i++)
186           _ucprop_offsets[i] = endian_short(_ucprop_offsets[i]);
187     }
188
189     /*
190      * Load the ranges.  The number of elements is in the last array position
191      * of the offsets.
192      */
193     fread((char *) _ucprop_ranges, sizeof(unsigned long),
194           _ucprop_offsets[_ucprop_size], in);
195
196     fclose(in);
197
198     /*
199      * Do an endian swap if necessary.
200      */
201     if (hdr.bom == 0xfffe) {
202         for (i = 0; i < _ucprop_offsets[_ucprop_size]; i++)
203           _ucprop_ranges[i] = endian_long(_ucprop_ranges[i]);
204     }
205     return 0;
206 }
207
208 static void
209 _ucprop_unload(void)
210 {
211     if (_ucprop_size == 0)
212       return;
213
214     /*
215      * Only need to free the offsets because the memory is allocated as a
216      * single block.
217      */
218     free((char *) _ucprop_offsets);
219     _ucprop_size = 0;
220 }
221
222 static int
223 _ucprop_lookup(unsigned long code, unsigned long n)
224 {
225     long l, r, m;
226
227     if (_ucprop_size == 0)
228       return 0;
229
230     /*
231      * There is an extra node on the end of the offsets to allow this routine
232      * to work right.  If the index is 0xffff, then there are no nodes for the
233      * property.
234      */
235     if ((l = _ucprop_offsets[n]) == 0xffff)
236       return 0;
237
238     /*
239      * Locate the next offset that is not 0xffff.  The sentinel at the end of
240      * the array is the max index value.
241      */
242     for (m = 1;
243          n + m < _ucprop_size && _ucprop_offsets[n + m] == 0xffff; m++) ;
244
245     r = _ucprop_offsets[n + m] - 1;
246
247     while (l <= r) {
248         /*
249          * Determine a "mid" point and adjust to make sure the mid point is at
250          * the beginning of a range pair.
251          */
252         m = (l + r) >> 1;
253         m -= (m & 1);
254         if (code > _ucprop_ranges[m + 1])
255           l = m + 2;
256         else if (code < _ucprop_ranges[m])
257           r = m - 2;
258         else if (code >= _ucprop_ranges[m] && code <= _ucprop_ranges[m + 1])
259           return 1;
260     }
261     return 0;
262 }
263
264 int
265 ucisprop(unsigned long code, unsigned long mask1, unsigned long mask2)
266 {
267     unsigned long i;
268
269     if (mask1 == 0 && mask2 == 0)
270       return 0;
271
272     for (i = 0; mask1 && i < 32; i++) {
273         if ((mask1 & masks32[i]) && _ucprop_lookup(code, i))
274           return 1;
275     }
276
277     for (i = 32; mask2 && i < _ucprop_size; i++) {
278         if ((mask2 & masks32[i & 31]) && _ucprop_lookup(code, i))
279           return 1;
280     }
281
282     return 0;
283 }
284
285 /**************************************************************************
286  *
287  * Support for case mapping.
288  *
289  **************************************************************************/
290
291 static unsigned long _uccase_size;
292 static unsigned short _uccase_len[2];
293 static unsigned long *_uccase_map;
294
295 /*
296  * Return -1 on error, 0 if okay
297  */
298 static int
299 _uccase_load(char *paths, int reload)
300 {
301     FILE *in;
302     unsigned long i;
303     _ucheader_t hdr;
304
305     if (_uccase_size > 0) {
306         if (!reload)
307           /*
308            * The case mappings have already been loaded.
309            */
310           return 0;
311
312         free((char *) _uccase_map);
313         _uccase_size = 0;
314     }
315
316     if ((in = _ucopenfile(paths, "case.dat", "rb")) == 0)
317       return -1;
318
319     /*
320      * Load the header.
321      */
322     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
323
324     if (hdr.bom == 0xfffe) {
325         hdr.cnt = endian_short(hdr.cnt);
326         hdr.size.len[0] = endian_short(hdr.size.len[0]);
327         hdr.size.len[1] = endian_short(hdr.size.len[1]);
328     }
329
330     /*
331      * Set the node count and lengths of the upper and lower case mapping
332      * tables.
333      */
334     _uccase_size = hdr.cnt * 3;
335     _uccase_len[0] = hdr.size.len[0] * 3;
336     _uccase_len[1] = hdr.size.len[1] * 3;
337
338     _uccase_map = (unsigned long *)
339         malloc(_uccase_size * sizeof(unsigned long));
340
341     /*
342      * Load the case mapping table.
343      */
344     fread((char *) _uccase_map, sizeof(unsigned long), _uccase_size, in);
345
346     /*
347      * Do an endian swap if necessary.
348      */
349     if (hdr.bom == 0xfffe) {
350         for (i = 0; i < _uccase_size; i++)
351           _uccase_map[i] = endian_long(_uccase_map[i]);
352     }
353     fclose(in);
354     return 0;
355 }
356
357 static void
358 _uccase_unload(void)
359 {
360     if (_uccase_size == 0)
361       return;
362
363     free((char *) _uccase_map);
364     _uccase_size = 0;
365 }
366
367 static unsigned long
368 _uccase_lookup(unsigned long code, long l, long r, int field)
369 {
370     long m;
371
372     /*
373      * Do the binary search.
374      */
375     while (l <= r) {
376         /*
377          * Determine a "mid" point and adjust to make sure the mid point is at
378          * the beginning of a case mapping triple.
379          */
380         m = (l + r) >> 1;
381         m -= (m % 3);
382         if (code > _uccase_map[m])
383           l = m + 3;
384         else if (code < _uccase_map[m])
385           r = m - 3;
386         else if (code == _uccase_map[m])
387           return _uccase_map[m + field];
388     }
389
390     return code;
391 }
392
393 unsigned long
394 uctoupper(unsigned long code)
395 {
396     int field;
397     long l, r;
398
399     if (ucisupper(code))
400       return code;
401
402     if (ucislower(code)) {
403         /*
404          * The character is lower case.
405          */
406         field = 2;
407         l = _uccase_len[0];
408         r = (l + _uccase_len[1]) - 3;
409     } else {
410         /*
411          * The character is title case.
412          */
413         field = 1;
414         l = _uccase_len[0] + _uccase_len[1];
415         r = _uccase_size - 3;
416     }
417     return _uccase_lookup(code, l, r, field);
418 }
419
420 unsigned long
421 uctolower(unsigned long code)
422 {
423     int field;
424     long l, r;
425
426     if (ucislower(code))
427       return code;
428
429     if (ucisupper(code)) {
430         /*
431          * The character is upper case.
432          */
433         field = 1;
434         l = 0;
435         r = _uccase_len[0] - 3;
436     } else {
437         /*
438          * The character is title case.
439          */
440         field = 2;
441         l = _uccase_len[0] + _uccase_len[1];
442         r = _uccase_size - 3;
443     }
444     return _uccase_lookup(code, l, r, field);
445 }
446
447 unsigned long
448 uctotitle(unsigned long code)
449 {
450     int field;
451     long l, r;
452
453     if (ucistitle(code))
454       return code;
455
456     /*
457      * The offset will always be the same for converting to title case.
458      */
459     field = 2;
460
461     if (ucisupper(code)) {
462         /*
463          * The character is upper case.
464          */
465         l = 0;
466         r = _uccase_len[0] - 3;
467     } else {
468         /*
469          * The character is lower case.
470          */
471         l = _uccase_len[0];
472         r = (l + _uccase_len[1]) - 3;
473     }
474     return _uccase_lookup(code, l, r, field);
475 }
476
477 /**************************************************************************
478  *
479  * Support for compositions.
480  *
481  **************************************************************************/
482
483 static unsigned long  _uccomp_size;
484 static unsigned long *_uccomp_data;
485
486 /*
487  * Return -1 on error, 0 if okay
488  */
489 static int
490 _uccomp_load(char *paths, int reload)
491 {
492     FILE *in;
493     unsigned long size, i;
494     _ucheader_t hdr;
495
496     if (_uccomp_size > 0) {
497         if (!reload)
498             /*
499              * The compositions have already been loaded.
500              */
501             return 0;
502
503         free((char *) _uccomp_data);
504         _uccomp_size = 0;
505     }
506
507     if ((in = _ucopenfile(paths, "comp.dat", "rb")) == 0)
508         return -1;
509
510     /*
511      * Load the header.
512      */
513     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
514
515     if (hdr.bom == 0xfffe) {
516         hdr.cnt = endian_short(hdr.cnt);
517         hdr.size.bytes = endian_long(hdr.size.bytes);
518     }
519
520     _uccomp_size = hdr.cnt;
521     _uccomp_data = (unsigned long *) malloc(hdr.size.bytes);
522
523     /*
524      * Read the composition data in.
525      */
526     size = hdr.size.bytes / sizeof(unsigned long);
527     fread((char *) _uccomp_data, sizeof(unsigned long), size, in);
528
529     /*
530      * Do an endian swap if necessary.
531      */
532     if (hdr.bom == 0xfffe) {
533         for (i = 0; i < size; i++)
534             _uccomp_data[i] = endian_long(_uccomp_data[i]);
535     }
536
537     /*
538      * Assume that the data is ordered on count, so that all compositions
539      * of length 2 come first. Only handling length 2 for now.
540      */
541     for (i = 1; i < size; i += 4)
542       if (_uccomp_data[i] != 2)
543         break;
544     _uccomp_size = i - 1;
545
546     fclose(in);
547     return 0;
548 }
549
550 static void
551 _uccomp_unload(void)
552 {
553     if (_uccomp_size == 0)
554         return;
555
556     free((char *) _uccomp_data);
557     _uccomp_size = 0;
558 }
559
560 int
561 uccomp(unsigned long node1, unsigned long node2, unsigned long *comp)
562 {
563     int l, r, m;
564
565     l = 0;
566     r = _uccomp_size - 1;
567
568     while (l <= r) {
569         m = ((r + l) >> 1);
570         m -= m & 3;
571         if (node1 > _uccomp_data[m+2])
572           l = m + 4;
573         else if (node1 < _uccomp_data[m+2])
574           r = m - 4;
575         else if (node2 > _uccomp_data[m+3])
576           l = m + 4;
577         else if (node2 < _uccomp_data[m+3])
578           r = m - 4;
579         else {
580             *comp = _uccomp_data[m];
581             return 1;
582         }
583     }
584     return 0;
585 }
586
587 int
588 uccomp_hangul(unsigned long *str, int len)
589 {
590     const int SBase = 0xAC00, LBase = 0x1100,
591         VBase = 0x1161, TBase = 0x11A7,
592         LCount = 19, VCount = 21, TCount = 28,
593         NCount = VCount * TCount,   /* 588 */
594         SCount = LCount * NCount;   /* 11172 */
595     
596     int i, rlen;
597     unsigned long ch, last, lindex, sindex;
598
599     last = str[0];
600     rlen = 1;
601     for ( i = 1; i < len; i++ ) {
602         ch = str[i];
603
604         /* check if two current characters are L and V */
605         lindex = last - LBase;
606         if (0 <= lindex && lindex < LCount) {
607             unsigned long vindex = ch - VBase;
608             if (0 <= vindex && vindex < VCount) {
609                 /* make syllable of form LV */
610                 last = SBase + (lindex * VCount + vindex) * TCount;
611                 str[rlen-1] = last; /* reset last */
612                 continue;
613             }
614         }
615         
616         /* check if two current characters are LV and T */
617         sindex = last - SBase;
618         if (0 <= sindex && sindex < SCount && (sindex % TCount) == 0) {
619             unsigned long tindex = ch - TBase;
620             if (0 <= tindex && tindex <= TCount) {
621                 /* make syllable of form LVT */
622                 last += tindex;
623                 str[rlen-1] = last; /* reset last */
624                 continue;
625             }
626         }
627
628         /* if neither case was true, just add the character */
629         last = ch;
630         str[rlen] = ch;
631         rlen++;
632     }
633     return rlen;
634 }
635
636 int
637 uccanoncomp(unsigned long *str, int len)
638 {
639     int i, stpos, copos;
640     unsigned long cl, prevcl, st, ch, co;
641
642     st = str[0];
643     stpos = 0;
644     copos = 1;
645     prevcl = uccombining_class(st) == 0 ? 0 : 256;
646         
647     for (i = 1; i < len; i++) {
648         ch = str[i];
649         cl = uccombining_class(ch);
650         if (uccomp(st, ch, &co) && (prevcl < cl || prevcl == 0))
651           st = str[stpos] = co;
652         else {
653             if (cl == 0) {
654                 stpos = copos;
655                 st = ch;
656             }
657             prevcl = cl;
658             str[copos++] = ch;
659         }
660     }
661
662     return uccomp_hangul(str, copos);
663 }
664
665 /**************************************************************************
666  *
667  * Support for decompositions.
668  *
669  **************************************************************************/
670
671 static unsigned long  _ucdcmp_size;
672 static unsigned long *_ucdcmp_nodes;
673 static unsigned long *_ucdcmp_decomp;
674
675 /*
676  * Return -1 on error, 0 if okay
677  */
678 static int
679 _ucdcmp_load(char *paths, int reload)
680 {
681     FILE *in;
682     unsigned long size, i;
683     _ucheader_t hdr;
684
685     if (_ucdcmp_size > 0) {
686         if (!reload)
687             /*
688              * The decompositions have already been loaded.
689              */
690           return 0;
691
692         free((char *) _ucdcmp_nodes);
693         _ucdcmp_size = 0;
694     }
695
696     if ((in = _ucopenfile(paths, "decomp.dat", "rb")) == 0)
697         return -1;
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     _ucdcmp_size = hdr.cnt << 1;
710     _ucdcmp_nodes = (unsigned long *) malloc(hdr.size.bytes);
711     _ucdcmp_decomp = _ucdcmp_nodes + (_ucdcmp_size + 1);
712
713     /*
714      * Read the decomposition data in.
715      */
716     size = hdr.size.bytes / sizeof(unsigned long);
717     fread((char *) _ucdcmp_nodes, sizeof(unsigned long), size, in);
718
719     /*
720      * Do an endian swap if necessary.
721      */
722     if (hdr.bom == 0xfffe) {
723         for (i = 0; i < size; i++)
724             _ucdcmp_nodes[i] = endian_long(_ucdcmp_nodes[i]);
725     }
726     fclose(in);
727     return 0;
728 }
729
730 static void
731 _ucdcmp_unload(void)
732 {
733     if (_ucdcmp_size == 0)
734       return;
735
736     /*
737      * Only need to free the offsets because the memory is allocated as a
738      * single block.
739      */
740     free((char *) _ucdcmp_nodes);
741     _ucdcmp_size = 0;
742 }
743
744 int
745 ucdecomp(unsigned long code, unsigned long *num, unsigned long **decomp)
746 {
747     long l, r, m;
748
749     l = 0;
750     r = _ucdcmp_nodes[_ucdcmp_size] - 1;
751
752     while (l <= r) {
753         /*
754          * Determine a "mid" point and adjust to make sure the mid point is at
755          * the beginning of a code+offset pair.
756          */
757         m = (l + r) >> 1;
758         m -= (m & 1);
759         if (code > _ucdcmp_nodes[m])
760           l = m + 2;
761         else if (code < _ucdcmp_nodes[m])
762           r = m - 2;
763         else if (code == _ucdcmp_nodes[m]) {
764             *num = _ucdcmp_nodes[m + 3] - _ucdcmp_nodes[m + 1];
765             *decomp = &_ucdcmp_decomp[_ucdcmp_nodes[m + 1]];
766             return 1;
767         }
768     }
769     return 0;
770 }
771
772 int
773 ucdecomp_hangul(unsigned long code, unsigned long *num, unsigned long decomp[])
774 {
775     if (!ucishangul(code))
776       return 0;
777
778     code -= 0xac00;
779     decomp[0] = 0x1100 + (unsigned long) (code / 588);
780     decomp[1] = 0x1161 + (unsigned long) ((code % 588) / 28);
781     decomp[2] = 0x11a7 + (unsigned long) (code % 28);
782     *num = (decomp[2] != 0x11a7) ? 3 : 2;
783
784     return 1;
785 }
786
787 int
788 uccanondecomp(const unsigned long *in, int inlen,
789               unsigned long **out, int *outlen)
790 {
791     int i, j, k, l, size;
792     unsigned long num, class, *decomp, hangdecomp[3];
793
794     size = inlen;
795     *out = (unsigned long *) malloc(size * sizeof(**out));
796     if (*out == NULL)
797         return *outlen = -1;
798
799     i = 0;
800     for (j = 0; j < inlen; j++) {
801         if (ucdecomp(in[j], &num, &decomp)) {
802             if (size - i < num) {
803                 size = inlen + i - j + num - 1;
804                 *out = (unsigned long *) realloc(*out, size * sizeof(**out));
805                 if (*out == NULL)
806                     return *outlen = -1;
807             }
808             for (k = 0; k < num; k++) {
809                 class = uccombining_class(decomp[k]);
810                 if (class == 0) {
811                     (*out)[i] = decomp[k];
812                 } else {
813                     for (l = i; l > 0; l--)
814                         if (class >= uccombining_class((*out)[l-1]))
815                             break;
816                     memmove(*out + l + 1, *out + l, (i - l) * sizeof(**out));
817                     (*out)[l] = decomp[k];
818                 }
819                 i++;
820             }
821         } else if (ucdecomp_hangul(in[j], &num, hangdecomp)) {
822             if (size - i < num) {
823                 size = inlen + i - j + num - 1;
824                 *out = (unsigned long *) realloc(*out, size * sizeof(**out));
825                 if (*out == NULL)
826                     return *outlen = -1;
827             }
828             for (k = 0; k < num; k++) {
829                 (*out)[i] = hangdecomp[k];
830                 i++;
831             }
832         } else {
833             if (size - i < 1) {
834                 size = inlen + i - j;
835                 *out = (unsigned long *) realloc(*out, size * sizeof(**out));
836                 if (*out == NULL)
837                     return *outlen = -1;
838             }
839             class = uccombining_class(in[j]);
840             if (class == 0) {
841                 (*out)[i] = in[j];
842             } else {
843                 for (l = i; l > 0; l--)
844                     if (class >= uccombining_class((*out)[l-1]))
845                         break;
846                 memmove(*out + l + 1, *out + l, (i - l) * sizeof(**out));
847                 (*out)[l] = in[j];
848             }
849             i++;
850         }
851     }
852     return *outlen = i;
853 }
854
855 /**************************************************************************
856  *
857  * Support for combining classes.
858  *
859  **************************************************************************/
860
861 static unsigned long  _uccmcl_size;
862 static unsigned long *_uccmcl_nodes;
863
864 /*
865  * Return -1 on error, 0 if okay
866  */
867 static int
868 _uccmcl_load(char *paths, int reload)
869 {
870     FILE *in;
871     unsigned long i;
872     _ucheader_t hdr;
873
874     if (_uccmcl_size > 0) {
875         if (!reload)
876             /*
877              * The combining classes have already been loaded.
878              */
879             return 0;
880
881         free((char *) _uccmcl_nodes);
882         _uccmcl_size = 0;
883     }
884
885     if ((in = _ucopenfile(paths, "cmbcl.dat", "rb")) == 0)
886         return -1;
887
888     /*
889      * Load the header.
890      */
891     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
892
893     if (hdr.bom == 0xfffe) {
894         hdr.cnt = endian_short(hdr.cnt);
895         hdr.size.bytes = endian_long(hdr.size.bytes);
896     }
897
898     _uccmcl_size = hdr.cnt * 3;
899     _uccmcl_nodes = (unsigned long *) malloc(hdr.size.bytes);
900
901     /*
902      * Read the combining classes in.
903      */
904     fread((char *) _uccmcl_nodes, sizeof(unsigned long), _uccmcl_size, in);
905
906     /*
907      * Do an endian swap if necessary.
908      */
909     if (hdr.bom == 0xfffe) {
910         for (i = 0; i < _uccmcl_size; i++)
911             _uccmcl_nodes[i] = endian_long(_uccmcl_nodes[i]);
912     }
913     fclose(in);
914     return 0;
915 }
916
917 static void
918 _uccmcl_unload(void)
919 {
920     if (_uccmcl_size == 0)
921       return;
922
923     free((char *) _uccmcl_nodes);
924     _uccmcl_size = 0;
925 }
926
927 unsigned long
928 uccombining_class(unsigned long code)
929 {
930     long l, r, m;
931
932     l = 0;
933     r = _uccmcl_size - 1;
934
935     while (l <= r) {
936         m = (l + r) >> 1;
937         m -= (m % 3);
938         if (code > _uccmcl_nodes[m + 1])
939           l = m + 3;
940         else if (code < _uccmcl_nodes[m])
941           r = m - 3;
942         else if (code >= _uccmcl_nodes[m] && code <= _uccmcl_nodes[m + 1])
943           return _uccmcl_nodes[m + 2];
944     }
945     return 0;
946 }
947
948 /**************************************************************************
949  *
950  * Support for numeric values.
951  *
952  **************************************************************************/
953
954 static unsigned long *_ucnum_nodes;
955 static unsigned long _ucnum_size;
956 static short *_ucnum_vals;
957
958 /*
959  * Return -1 on error, 0 if okay
960  */
961 static int
962 _ucnumb_load(char *paths, int reload)
963 {
964     FILE *in;
965     unsigned long size, i;
966     _ucheader_t hdr;
967
968     if (_ucnum_size > 0) {
969         if (!reload)
970           /*
971            * The numbers have already been loaded.
972            */
973           return 0;
974
975         free((char *) _ucnum_nodes);
976         _ucnum_size = 0;
977     }
978
979     if ((in = _ucopenfile(paths, "num.dat", "rb")) == 0)
980       return -1;
981
982     /*
983      * Load the header.
984      */
985     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
986
987     if (hdr.bom == 0xfffe) {
988         hdr.cnt = endian_short(hdr.cnt);
989         hdr.size.bytes = endian_long(hdr.size.bytes);
990     }
991
992     _ucnum_size = hdr.cnt;
993     _ucnum_nodes = (unsigned long *) malloc(hdr.size.bytes);
994     _ucnum_vals = (short *) (_ucnum_nodes + _ucnum_size);
995
996     /*
997      * Read the combining classes in.
998      */
999     fread((char *) _ucnum_nodes, sizeof(unsigned char), hdr.size.bytes, in);
1000
1001     /*
1002      * Do an endian swap if necessary.
1003      */
1004     if (hdr.bom == 0xfffe) {
1005         for (i = 0; i < _ucnum_size; i++)
1006           _ucnum_nodes[i] = endian_long(_ucnum_nodes[i]);
1007
1008         /*
1009          * Determine the number of values that have to be adjusted.
1010          */
1011         size = (hdr.size.bytes -
1012                 (_ucnum_size * (sizeof(unsigned long) << 1))) /
1013             sizeof(short);
1014
1015         for (i = 0; i < size; i++)
1016           _ucnum_vals[i] = endian_short(_ucnum_vals[i]);
1017     }
1018     fclose(in);
1019     return 0;
1020 }
1021
1022 static void
1023 _ucnumb_unload(void)
1024 {
1025     if (_ucnum_size == 0)
1026       return;
1027
1028     free((char *) _ucnum_nodes);
1029     _ucnum_size = 0;
1030 }
1031
1032 int
1033 ucnumber_lookup(unsigned long code, struct ucnumber *num)
1034 {
1035     long l, r, m;
1036     short *vp;
1037
1038     l = 0;
1039     r = _ucnum_size - 1;
1040     while (l <= r) {
1041         /*
1042          * Determine a "mid" point and adjust to make sure the mid point is at
1043          * the beginning of a code+offset pair.
1044          */
1045         m = (l + r) >> 1;
1046         m -= (m & 1);
1047         if (code > _ucnum_nodes[m])
1048           l = m + 2;
1049         else if (code < _ucnum_nodes[m])
1050           r = m - 2;
1051         else {
1052             vp = _ucnum_vals + _ucnum_nodes[m + 1];
1053             num->numerator = (int) *vp++;
1054             num->denominator = (int) *vp;
1055             return 1;
1056         }
1057     }
1058     return 0;
1059 }
1060
1061 int
1062 ucdigit_lookup(unsigned long code, int *digit)
1063 {
1064     long l, r, m;
1065     short *vp;
1066
1067     l = 0;
1068     r = _ucnum_size - 1;
1069     while (l <= r) {
1070         /*
1071          * Determine a "mid" point and adjust to make sure the mid point is at
1072          * the beginning of a code+offset pair.
1073          */
1074         m = (l + r) >> 1;
1075         m -= (m & 1);
1076         if (code > _ucnum_nodes[m])
1077           l = m + 2;
1078         else if (code < _ucnum_nodes[m])
1079           r = m - 2;
1080         else {
1081             vp = _ucnum_vals + _ucnum_nodes[m + 1];
1082             if (*vp == *(vp + 1)) {
1083               *digit = *vp;
1084               return 1;
1085             }
1086             return 0;
1087         }
1088     }
1089     return 0;
1090 }
1091
1092 struct ucnumber
1093 ucgetnumber(unsigned long code)
1094 {
1095     struct ucnumber num;
1096
1097     /*
1098      * Initialize with some arbitrary value, because the caller simply cannot
1099      * tell for sure if the code is a number without calling the ucisnumber()
1100      * macro before calling this function.
1101      */
1102     num.numerator = num.denominator = -111;
1103
1104     (void) ucnumber_lookup(code, &num);
1105
1106     return num;
1107 }
1108
1109 int
1110 ucgetdigit(unsigned long code)
1111 {
1112     int dig;
1113
1114     /*
1115      * Initialize with some arbitrary value, because the caller simply cannot
1116      * tell for sure if the code is a number without calling the ucisdigit()
1117      * macro before calling this function.
1118      */
1119     dig = -111;
1120
1121     (void) ucdigit_lookup(code, &dig);
1122
1123     return dig;
1124 }
1125
1126 /**************************************************************************
1127  *
1128  * Setup and cleanup routines.
1129  *
1130  **************************************************************************/
1131
1132 /*
1133  * Return 0 if okay, negative on error
1134  */
1135 int
1136 ucdata_load(char *paths, int masks)
1137 {
1138     int error = 0;
1139
1140     if (masks & UCDATA_CTYPE)
1141       error |= _ucprop_load(paths, 0) < 0 ? UCDATA_CTYPE : 0;
1142     if (masks & UCDATA_CASE)
1143       error |= _uccase_load(paths, 0) < 0 ? UCDATA_CASE : 0;
1144     if (masks & UCDATA_DECOMP)
1145       error |= _ucdcmp_load(paths, 0) < 0 ? UCDATA_DECOMP : 0;
1146     if (masks & UCDATA_CMBCL)
1147       error |= _uccmcl_load(paths, 0) < 0 ? UCDATA_CMBCL : 0;
1148     if (masks & UCDATA_NUM)
1149       error |= _ucnumb_load(paths, 0) < 0 ? UCDATA_NUM : 0;
1150     if (masks & UCDATA_COMP)
1151       error |= _uccomp_load(paths, 0) < 0 ? UCDATA_COMP : 0;
1152
1153     return -error;
1154 }
1155
1156 void
1157 ucdata_unload(int masks)
1158 {
1159     if (masks & UCDATA_CTYPE)
1160       _ucprop_unload();
1161     if (masks & UCDATA_CASE)
1162       _uccase_unload();
1163     if (masks & UCDATA_DECOMP)
1164       _ucdcmp_unload();
1165     if (masks & UCDATA_CMBCL)
1166       _uccmcl_unload();
1167     if (masks & UCDATA_NUM)
1168       _ucnumb_unload();
1169     if (masks & UCDATA_COMP)
1170       _uccomp_unload();
1171 }
1172
1173 /*
1174  * Return 0 if okay, negative on error
1175  */
1176 int
1177 ucdata_reload(char *paths, int masks)
1178 {
1179     int error = 0;
1180
1181     if (masks & UCDATA_CTYPE)
1182         error |= _ucprop_load(paths, 1) < 0 ? UCDATA_CTYPE : 0;
1183     if (masks & UCDATA_CASE)
1184         error |= _uccase_load(paths, 1) < 0 ? UCDATA_CASE : 0;
1185     if (masks & UCDATA_DECOMP)
1186         error |= _ucdcmp_load(paths, 1) < 0 ? UCDATA_DECOMP : 0;
1187     if (masks & UCDATA_CMBCL)
1188         error |= _uccmcl_load(paths, 1) < 0 ? UCDATA_CMBCL : 0;
1189     if (masks & UCDATA_NUM)
1190         error |= _ucnumb_load(paths, 1) < 0 ? UCDATA_NUM : 0;
1191     if (masks & UCDATA_COMP)
1192         error |= _uccomp_load(paths, 1) < 0 ? UCDATA_COMP : 0;
1193
1194     return -error;
1195 }
1196
1197 #ifdef TEST
1198
1199 void
1200 main(void)
1201 {
1202     int dig;
1203     unsigned long i, lo, *dec;
1204     struct ucnumber num;
1205
1206     ucdata_setup(".");
1207
1208     if (ucisweak(0x30))
1209       printf("WEAK\n");
1210     else
1211       printf("NOT WEAK\n");
1212
1213     printf("LOWER 0x%04lX\n", uctolower(0xff3a));
1214     printf("UPPER 0x%04lX\n", uctoupper(0xff5a));
1215
1216     if (ucisalpha(0x1d5))
1217       printf("ALPHA\n");
1218     else
1219       printf("NOT ALPHA\n");
1220
1221     if (ucisupper(0x1d5)) {
1222         printf("UPPER\n");
1223         lo = uctolower(0x1d5);
1224         printf("0x%04lx\n", lo);
1225         lo = uctotitle(0x1d5);
1226         printf("0x%04lx\n", lo);
1227     } else
1228       printf("NOT UPPER\n");
1229
1230     if (ucistitle(0x1d5))
1231       printf("TITLE\n");
1232     else
1233       printf("NOT TITLE\n");
1234
1235     if (uciscomposite(0x1d5))
1236       printf("COMPOSITE\n");
1237     else
1238       printf("NOT COMPOSITE\n");
1239
1240     if (ucdecomp(0x1d5, &lo, &dec)) {
1241         for (i = 0; i < lo; i++)
1242           printf("0x%04lx ", dec[i]);
1243         putchar('\n');
1244     }
1245
1246     if ((lo = uccombining_class(0x41)) != 0)
1247       printf("0x41 CCL %ld\n", lo);
1248
1249     if (ucisxdigit(0xfeff))
1250       printf("0xFEFF HEX DIGIT\n");
1251     else
1252       printf("0xFEFF NOT HEX DIGIT\n");
1253
1254     if (ucisdefined(0x10000))
1255       printf("0x10000 DEFINED\n");
1256     else
1257       printf("0x10000 NOT DEFINED\n");
1258
1259     if (ucnumber_lookup(0x30, &num)) {
1260         if (num.numerator != num.denominator)
1261           printf("UCNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
1262         else
1263           printf("UCNUMBER: 0x30 = %d\n", num.numerator);
1264     } else
1265       printf("UCNUMBER: 0x30 NOT A NUMBER\n");
1266
1267     if (ucnumber_lookup(0xbc, &num)) {
1268         if (num.numerator != num.denominator)
1269           printf("UCNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
1270         else
1271           printf("UCNUMBER: 0xbc = %d\n", num.numerator);
1272     } else
1273       printf("UCNUMBER: 0xbc NOT A NUMBER\n");
1274
1275
1276     if (ucnumber_lookup(0xff19, &num)) {
1277         if (num.numerator != num.denominator)
1278           printf("UCNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator);
1279         else
1280           printf("UCNUMBER: 0xff19 = %d\n", num.numerator);
1281     } else
1282       printf("UCNUMBER: 0xff19 NOT A NUMBER\n");
1283
1284     if (ucnumber_lookup(0x4e00, &num)) {
1285         if (num.numerator != num.denominator)
1286           printf("UCNUMBER: 0x4e00 = %d/%d\n", num.numerator, num.denominator);
1287         else
1288           printf("UCNUMBER: 0x4e00 = %d\n", num.numerator);
1289     } else
1290       printf("UCNUMBER: 0x4e00 NOT A NUMBER\n");
1291
1292     if (ucdigit_lookup(0x06f9, &dig))
1293       printf("UCDIGIT: 0x6f9 = %d\n", dig);
1294     else
1295       printf("UCDIGIT: 0x6f9 NOT A NUMBER\n");
1296
1297     dig = ucgetdigit(0x0969);
1298     printf("UCGETDIGIT: 0x969 = %d\n", dig);
1299
1300     num = ucgetnumber(0x30);
1301     if (num.numerator != num.denominator)
1302       printf("UCGETNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
1303     else
1304       printf("UCGETNUMBER: 0x30 = %d\n", num.numerator);
1305
1306     num = ucgetnumber(0xbc);
1307     if (num.numerator != num.denominator)
1308       printf("UCGETNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
1309     else
1310       printf("UCGETNUMBER: 0xbc = %d\n", num.numerator);
1311
1312     num = ucgetnumber(0xff19);
1313     if (num.numerator != num.denominator)
1314       printf("UCGETNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator);
1315     else
1316       printf("UCGETNUMBER: 0xff19 = %d\n", num.numerator);
1317
1318     ucdata_cleanup();
1319     exit(0);
1320 }
1321
1322 #endif /* TEST */