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