Back to index

openldap  2.4.31
ucdata.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted only as authorized by the OpenLDAP
00009  * Public License.
00010  *
00011  * A copy of this license is available in file LICENSE in the
00012  * top-level directory of the distribution or, alternatively, at
00013  * <http://www.OpenLDAP.org/license.html>.
00014  */
00015 /* Copyright 2001 Computing Research Labs, New Mexico State University
00016  *
00017  * Permission is hereby granted, free of charge, to any person obtaining a
00018  * copy of this software and associated documentation files (the "Software"),
00019  * to deal in the Software without restriction, including without limitation
00020  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
00021  * and/or sell copies of the Software, and to permit persons to whom the
00022  * Software is furnished to do so, subject to the following conditions:
00023  *
00024  * The above copyright notice and this permission notice shall be included in
00025  * all copies or substantial portions of the Software.
00026  *
00027  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00028  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00029  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00030  * THE COMPUTING RESEARCH LAB OR NEW MEXICO STATE UNIVERSITY BE LIABLE FOR ANY
00031  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
00032  * OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
00033  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00034  */
00035 /* $Id: ucdata.c,v 1.4 2001/01/02 18:46:20 mleisher Exp $" */
00036 
00037 #include "portable.h"
00038 #include "ldap_config.h"
00039 
00040 #include <stdio.h>
00041 #include <ac/stdlib.h>
00042 #include <ac/string.h>
00043 #include <ac/unistd.h>
00044 
00045 #include <ac/bytes.h>
00046 
00047 #include "lber_pvt.h"
00048 #include "ucdata.h"
00049 
00050 #ifndef HARDCODE_DATA
00051 #define       HARDCODE_DATA 1
00052 #endif
00053 
00054 #if HARDCODE_DATA
00055 #include "uctable.h"
00056 #endif
00057 
00058 /**************************************************************************
00059  *
00060  * Miscellaneous types, data, and support functions.
00061  *
00062  **************************************************************************/
00063 
00064 typedef struct {
00065     ac_uint2 bom;
00066     ac_uint2 cnt;
00067     union {
00068         ac_uint4 bytes;
00069         ac_uint2 len[2]; 
00070     } size;
00071 } _ucheader_t;
00072 
00073 /*
00074  * A simple array of 32-bit masks for lookup.
00075  */
00076 static ac_uint4 masks32[32] = {
00077        0x00000001UL, 0x00000002UL, 0x00000004UL, 0x00000008UL,
00078        0x00000010UL, 0x00000020UL, 0x00000040UL, 0x00000080UL,
00079        0x00000100UL, 0x00000200UL, 0x00000400UL, 0x00000800UL,
00080        0x00001000UL, 0x00002000UL, 0x00004000UL, 0x00008000UL,
00081        0x00010000UL, 0x00020000UL, 0x00040000UL, 0x00080000UL,
00082        0x00100000UL, 0x00200000UL, 0x00400000UL, 0x00800000UL,
00083        0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL,
00084        0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL
00085 };
00086 
00087 #define endian_short(cc) (((cc) >> 8) | (((cc) & 0xff) << 8))
00088 #define endian_long(cc) ((((cc) & 0xff) << 24)|((((cc) >> 8) & 0xff) << 16)|\
00089                         ((((cc) >> 16) & 0xff) << 8)|((cc) >> 24))
00090 
00091 #if !HARDCODE_DATA
00092 static FILE *
00093 _ucopenfile(char *paths, char *filename, char *mode)
00094 {
00095     FILE *f;
00096     char *fp, *dp, *pp, path[BUFSIZ];
00097 
00098     if (filename == 0 || *filename == 0)
00099       return 0;
00100 
00101     dp = paths;
00102     while (dp && *dp) {
00103         pp = path;
00104         while (*dp && *dp != ':')
00105           *pp++ = *dp++;
00106         *pp++ = *LDAP_DIRSEP;
00107 
00108         fp = filename;
00109         while (*fp)
00110           *pp++ = *fp++;
00111         *pp = 0;
00112 
00113         if ((f = fopen(path, mode)) != 0)
00114           return f;
00115 
00116         if (*dp == ':')
00117           dp++;
00118     }
00119 
00120     return 0;
00121 }
00122 #endif
00123 
00124 /**************************************************************************
00125  *
00126  * Support for the character properties.
00127  *
00128  **************************************************************************/
00129 
00130 #if !HARDCODE_DATA
00131 
00132 static ac_uint4 _ucprop_size;
00133 static ac_uint2 *_ucprop_offsets;
00134 static ac_uint4 *_ucprop_ranges;
00135 
00136 /*
00137  * Return -1 on error, 0 if okay
00138  */
00139 static int
00140 _ucprop_load(char *paths, int reload)
00141 {
00142     FILE *in;
00143     ac_uint4 size, i;
00144     _ucheader_t hdr;
00145 
00146     if (_ucprop_size > 0) {
00147         if (!reload)
00148           /*
00149            * The character properties have already been loaded.
00150            */
00151           return 0;
00152 
00153         /*
00154          * Unload the current character property data in preparation for
00155          * loading a new copy.  Only the first array has to be deallocated
00156          * because all the memory for the arrays is allocated as a single
00157          * block.
00158          */
00159         free((char *) _ucprop_offsets);
00160         _ucprop_size = 0;
00161     }
00162 
00163     if ((in = _ucopenfile(paths, "ctype.dat", "rb")) == 0)
00164       return -1;
00165 
00166     /*
00167      * Load the header.
00168      */
00169     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
00170 
00171     if (hdr.bom == 0xfffe) {
00172         hdr.cnt = endian_short(hdr.cnt);
00173         hdr.size.bytes = endian_long(hdr.size.bytes);
00174     }
00175 
00176     if ((_ucprop_size = hdr.cnt) == 0) {
00177         fclose(in);
00178         return -1;
00179     }
00180 
00181     /*
00182      * Allocate all the storage needed for the lookup table.
00183      */
00184     _ucprop_offsets = (ac_uint2 *) malloc(hdr.size.bytes);
00185 
00186     /*
00187      * Calculate the offset into the storage for the ranges.  The offsets
00188      * array is on a 4-byte boundary and one larger than the value provided in
00189      * the header count field.  This means the offset to the ranges must be
00190      * calculated after aligning the count to a 4-byte boundary.
00191      */
00192     if ((size = ((hdr.cnt + 1) * sizeof(ac_uint2))) & 3)
00193       size += 4 - (size & 3);
00194     size >>= 1;
00195     _ucprop_ranges = (ac_uint4 *) (_ucprop_offsets + size);
00196 
00197     /*
00198      * Load the offset array.
00199      */
00200     fread((char *) _ucprop_offsets, sizeof(ac_uint2), size, in);
00201 
00202     /*
00203      * Do an endian swap if necessary.  Don't forget there is an extra node on
00204      * the end with the final index.
00205      */
00206     if (hdr.bom == 0xfffe) {
00207         for (i = 0; i <= _ucprop_size; i++)
00208           _ucprop_offsets[i] = endian_short(_ucprop_offsets[i]);
00209     }
00210 
00211     /*
00212      * Load the ranges.  The number of elements is in the last array position
00213      * of the offsets.
00214      */
00215     fread((char *) _ucprop_ranges, sizeof(ac_uint4),
00216           _ucprop_offsets[_ucprop_size], in);
00217 
00218     fclose(in);
00219 
00220     /*
00221      * Do an endian swap if necessary.
00222      */
00223     if (hdr.bom == 0xfffe) {
00224         for (i = 0; i < _ucprop_offsets[_ucprop_size]; i++)
00225           _ucprop_ranges[i] = endian_long(_ucprop_ranges[i]);
00226     }
00227     return 0;
00228 }
00229 
00230 static void
00231 _ucprop_unload(void)
00232 {
00233     if (_ucprop_size == 0)
00234       return;
00235 
00236     /*
00237      * Only need to free the offsets because the memory is allocated as a
00238      * single block.
00239      */
00240     free((char *) _ucprop_offsets);
00241     _ucprop_size = 0;
00242 }
00243 #endif
00244 
00245 static int
00246 _ucprop_lookup(ac_uint4 code, ac_uint4 n)
00247 {
00248     long l, r, m;
00249 
00250     if (_ucprop_size == 0)
00251       return 0;
00252 
00253     /*
00254      * There is an extra node on the end of the offsets to allow this routine
00255      * to work right.  If the index is 0xffff, then there are no nodes for the
00256      * property.
00257      */
00258     if ((l = _ucprop_offsets[n]) == 0xffff)
00259       return 0;
00260 
00261     /*
00262      * Locate the next offset that is not 0xffff.  The sentinel at the end of
00263      * the array is the max index value.
00264      */
00265     for (m = 1;
00266          n + m < _ucprop_size && _ucprop_offsets[n + m] == 0xffff; m++) ;
00267 
00268     r = _ucprop_offsets[n + m] - 1;
00269 
00270     while (l <= r) {
00271         /*
00272          * Determine a "mid" point and adjust to make sure the mid point is at
00273          * the beginning of a range pair.
00274          */
00275         m = (l + r) >> 1;
00276         m -= (m & 1);
00277         if (code > _ucprop_ranges[m + 1])
00278           l = m + 2;
00279         else if (code < _ucprop_ranges[m])
00280           r = m - 2;
00281         else if (code >= _ucprop_ranges[m] && code <= _ucprop_ranges[m + 1])
00282           return 1;
00283     }
00284     return 0;
00285 }
00286 
00287 int
00288 ucisprop(ac_uint4 code, ac_uint4 mask1, ac_uint4 mask2)
00289 {
00290     ac_uint4 i;
00291 
00292     if (mask1 == 0 && mask2 == 0)
00293       return 0;
00294 
00295     for (i = 0; mask1 && i < 32; i++) {
00296         if ((mask1 & masks32[i]) && _ucprop_lookup(code, i))
00297           return 1;
00298     }
00299 
00300     for (i = 32; mask2 && i < _ucprop_size; i++) {
00301         if ((mask2 & masks32[i & 31]) && _ucprop_lookup(code, i))
00302           return 1;
00303     }
00304 
00305     return 0;
00306 }
00307 
00308 /**************************************************************************
00309  *
00310  * Support for case mapping.
00311  *
00312  **************************************************************************/
00313 
00314 #if !HARDCODE_DATA
00315 
00316 /* These record the number of slots in the map.
00317  * There are 3 words per slot.
00318  */
00319 static ac_uint4 _uccase_size;
00320 static ac_uint2 _uccase_len[2];
00321 static ac_uint4 *_uccase_map;
00322 
00323 /*
00324  * Return -1 on error, 0 if okay
00325  */
00326 static int
00327 _uccase_load(char *paths, int reload)
00328 {
00329     FILE *in;
00330     ac_uint4 i;
00331     _ucheader_t hdr;
00332 
00333     if (_uccase_size > 0) {
00334         if (!reload)
00335           /*
00336            * The case mappings have already been loaded.
00337            */
00338           return 0;
00339 
00340         free((char *) _uccase_map);
00341         _uccase_size = 0;
00342     }
00343 
00344     if ((in = _ucopenfile(paths, "case.dat", "rb")) == 0)
00345       return -1;
00346 
00347     /*
00348      * Load the header.
00349      */
00350     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
00351 
00352     if (hdr.bom == 0xfffe) {
00353         hdr.cnt = endian_short(hdr.cnt);
00354         hdr.size.len[0] = endian_short(hdr.size.len[0]);
00355         hdr.size.len[1] = endian_short(hdr.size.len[1]);
00356     }
00357 
00358     /*
00359      * Set the node count and lengths of the upper and lower case mapping
00360      * tables.
00361      */
00362     _uccase_size = hdr.cnt;
00363     _uccase_len[0] = hdr.size.len[0];
00364     _uccase_len[1] = hdr.size.len[1];
00365 
00366     _uccase_map = (ac_uint4 *)
00367         malloc(_uccase_size * 3 * sizeof(ac_uint4));
00368 
00369     /*
00370      * Load the case mapping table.
00371      */
00372     fread((char *) _uccase_map, sizeof(ac_uint4), _uccase_size * 3, in);
00373 
00374     /*
00375      * Do an endian swap if necessary.
00376      */
00377     if (hdr.bom == 0xfffe) {
00378         for (i = 0; i < _uccase_size * 3; i++)
00379           _uccase_map[i] = endian_long(_uccase_map[i]);
00380     }
00381     fclose(in);
00382     return 0;
00383 }
00384 
00385 static void
00386 _uccase_unload(void)
00387 {
00388     if (_uccase_size == 0)
00389       return;
00390 
00391     free((char *) _uccase_map);
00392     _uccase_size = 0;
00393 }
00394 #endif
00395 
00396 static ac_uint4
00397 _uccase_lookup(ac_uint4 code, long l, long r, int field)
00398 {
00399     long m;
00400        const ac_uint4 *tmp;
00401 
00402     /*
00403      * Do the binary search.
00404      */
00405     while (l <= r) {
00406         /*
00407          * Determine a "mid" point and adjust to make sure the mid point is at
00408          * the beginning of a case mapping triple.
00409          */
00410         m = (l + r) >> 1;
00411               tmp = &_uccase_map[m*3];
00412         if (code > *tmp)
00413           l = m + 1;
00414         else if (code < *tmp)
00415           r = m - 1;
00416         else if (code == *tmp)
00417           return tmp[field];
00418     }
00419 
00420     return code;
00421 }
00422 
00423 ac_uint4
00424 uctoupper(ac_uint4 code)
00425 {
00426     int field;
00427     long l, r;
00428 
00429     if (ucisupper(code))
00430       return code;
00431 
00432     if (ucislower(code)) {
00433         /*
00434          * The character is lower case.
00435          */
00436         field = 2;
00437         l = _uccase_len[0];
00438         r = (l + _uccase_len[1]) - 1;
00439     } else {
00440         /*
00441          * The character is title case.
00442          */
00443         field = 1;
00444         l = _uccase_len[0] + _uccase_len[1];
00445         r = _uccase_size - 1;
00446     }
00447     return _uccase_lookup(code, l, r, field);
00448 }
00449 
00450 ac_uint4
00451 uctolower(ac_uint4 code)
00452 {
00453     int field;
00454     long l, r;
00455 
00456     if (ucislower(code))
00457       return code;
00458 
00459     if (ucisupper(code)) {
00460         /*
00461          * The character is upper case.
00462          */
00463         field = 1;
00464         l = 0;
00465         r = _uccase_len[0] - 1;
00466     } else {
00467         /*
00468          * The character is title case.
00469          */
00470         field = 2;
00471         l = _uccase_len[0] + _uccase_len[1];
00472         r = _uccase_size - 1;
00473     }
00474     return _uccase_lookup(code, l, r, field);
00475 }
00476 
00477 ac_uint4
00478 uctotitle(ac_uint4 code)
00479 {
00480     int field;
00481     long l, r;
00482 
00483     if (ucistitle(code))
00484       return code;
00485 
00486     /*
00487      * The offset will always be the same for converting to title case.
00488      */
00489     field = 2;
00490 
00491     if (ucisupper(code)) {
00492         /*
00493          * The character is upper case.
00494          */
00495         l = 0;
00496         r = _uccase_len[0] - 1;
00497     } else {
00498         /*
00499          * The character is lower case.
00500          */
00501         l = _uccase_len[0];
00502         r = (l + _uccase_len[1]) - 1;
00503     }
00504     return _uccase_lookup(code, l, r, field);
00505 }
00506 
00507 /**************************************************************************
00508  *
00509  * Support for compositions.
00510  *
00511  **************************************************************************/
00512 
00513 #if !HARDCODE_DATA
00514 
00515 static ac_uint4  _uccomp_size;
00516 static ac_uint4 *_uccomp_data;
00517 
00518 /*
00519  * Return -1 on error, 0 if okay
00520  */
00521 static int
00522 _uccomp_load(char *paths, int reload)
00523 {
00524     FILE *in;
00525     ac_uint4 size, i;
00526     _ucheader_t hdr;
00527 
00528     if (_uccomp_size > 0) {
00529         if (!reload)
00530             /*
00531              * The compositions have already been loaded.
00532              */
00533             return 0;
00534 
00535         free((char *) _uccomp_data);
00536         _uccomp_size = 0;
00537     }
00538 
00539     if ((in = _ucopenfile(paths, "comp.dat", "rb")) == 0)
00540         return -1;
00541 
00542     /*
00543      * Load the header.
00544      */
00545     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
00546 
00547     if (hdr.bom == 0xfffe) {
00548         hdr.cnt = endian_short(hdr.cnt);
00549         hdr.size.bytes = endian_long(hdr.size.bytes);
00550     }
00551 
00552     _uccomp_size = hdr.cnt;
00553     _uccomp_data = (ac_uint4 *) malloc(hdr.size.bytes);
00554 
00555     /*
00556      * Read the composition data in.
00557      */
00558     size = hdr.size.bytes / sizeof(ac_uint4);
00559     fread((char *) _uccomp_data, sizeof(ac_uint4), size, in);
00560 
00561     /*
00562      * Do an endian swap if necessary.
00563      */
00564     if (hdr.bom == 0xfffe) {
00565         for (i = 0; i < size; i++)
00566             _uccomp_data[i] = endian_long(_uccomp_data[i]);
00567     }
00568 
00569     /*
00570      * Assume that the data is ordered on count, so that all compositions
00571      * of length 2 come first. Only handling length 2 for now.
00572      */
00573     for (i = 1; i < size; i += 4)
00574       if (_uccomp_data[i] != 2)
00575         break;
00576     _uccomp_size = i - 1;
00577 
00578     fclose(in);
00579     return 0;
00580 }
00581 
00582 static void
00583 _uccomp_unload(void)
00584 {
00585     if (_uccomp_size == 0)
00586         return;
00587 
00588     free((char *) _uccomp_data);
00589     _uccomp_size = 0;
00590 }
00591 #endif
00592 
00593 int
00594 uccomp(ac_uint4 node1, ac_uint4 node2, ac_uint4 *comp)
00595 {
00596     int l, r, m;
00597 
00598     l = 0;
00599     r = _uccomp_size - 1;
00600 
00601     while (l <= r) {
00602         m = ((r + l) >> 1);
00603         m -= m & 3;
00604         if (node1 > _uccomp_data[m+2])
00605           l = m + 4;
00606         else if (node1 < _uccomp_data[m+2])
00607           r = m - 4;
00608         else if (node2 > _uccomp_data[m+3])
00609           l = m + 4;
00610         else if (node2 < _uccomp_data[m+3])
00611           r = m - 4;
00612         else {
00613             *comp = _uccomp_data[m];
00614             return 1;
00615         }
00616     }
00617     return 0;
00618 }
00619 
00620 int
00621 uccomp_hangul(ac_uint4 *str, int len)
00622 {
00623     const int SBase = 0xAC00, LBase = 0x1100,
00624         VBase = 0x1161, TBase = 0x11A7,
00625         LCount = 19, VCount = 21, TCount = 28,
00626         NCount = VCount * TCount,   /* 588 */
00627         SCount = LCount * NCount;   /* 11172 */
00628     
00629     int i, rlen;
00630     ac_uint4 ch, last, lindex, sindex;
00631 
00632     last = str[0];
00633     rlen = 1;
00634     for ( i = 1; i < len; i++ ) {
00635         ch = str[i];
00636 
00637         /* check if two current characters are L and V */
00638         lindex = last - LBase;
00639         if (lindex < (ac_uint4) LCount) {
00640             ac_uint4 vindex = ch - VBase;
00641             if (vindex < (ac_uint4) VCount) {
00642                 /* make syllable of form LV */
00643                 last = SBase + (lindex * VCount + vindex) * TCount;
00644                 str[rlen-1] = last; /* reset last */
00645                 continue;
00646             }
00647         }
00648         
00649         /* check if two current characters are LV and T */
00650         sindex = last - SBase;
00651         if (sindex < (ac_uint4) SCount
00652                      && (sindex % TCount) == 0)
00653               {
00654             ac_uint4 tindex = ch - TBase;
00655             if (tindex <= (ac_uint4) TCount) {
00656                 /* make syllable of form LVT */
00657                 last += tindex;
00658                 str[rlen-1] = last; /* reset last */
00659                 continue;
00660             }
00661         }
00662 
00663         /* if neither case was true, just add the character */
00664         last = ch;
00665         str[rlen] = ch;
00666         rlen++;
00667     }
00668     return rlen;
00669 }
00670 
00671 int
00672 uccanoncomp(ac_uint4 *str, int len)
00673 {
00674     int i, stpos, copos;
00675     ac_uint4 cl, prevcl, st, ch, co;
00676 
00677     st = str[0];
00678     stpos = 0;
00679     copos = 1;
00680     prevcl = uccombining_class(st) == 0 ? 0 : 256;
00681         
00682     for (i = 1; i < len; i++) {
00683         ch = str[i];
00684         cl = uccombining_class(ch);
00685         if (uccomp(st, ch, &co) && (prevcl < cl || prevcl == 0))
00686           st = str[stpos] = co;
00687         else {
00688             if (cl == 0) {
00689                 stpos = copos;
00690                 st = ch;
00691             }
00692             prevcl = cl;
00693             str[copos++] = ch;
00694         }
00695     }
00696 
00697     return uccomp_hangul(str, copos);
00698 }
00699 
00700 /**************************************************************************
00701  *
00702  * Support for decompositions.
00703  *
00704  **************************************************************************/
00705 
00706 #if !HARDCODE_DATA
00707 
00708 static ac_uint4  _ucdcmp_size;
00709 static ac_uint4 *_ucdcmp_nodes;
00710 static ac_uint4 *_ucdcmp_decomp;
00711 
00712 static ac_uint4  _uckdcmp_size;
00713 static ac_uint4 *_uckdcmp_nodes;
00714 static ac_uint4 *_uckdcmp_decomp;
00715 
00716 /*
00717  * Return -1 on error, 0 if okay
00718  */
00719 static int
00720 _ucdcmp_load(char *paths, int reload)
00721 {
00722     FILE *in;
00723     ac_uint4 size, i;
00724     _ucheader_t hdr;
00725 
00726     if (_ucdcmp_size > 0) {
00727         if (!reload)
00728             /*
00729              * The decompositions have already been loaded.
00730              */
00731           return 0;
00732 
00733         free((char *) _ucdcmp_nodes);
00734         _ucdcmp_size = 0;
00735     }
00736 
00737     if ((in = _ucopenfile(paths, "decomp.dat", "rb")) == 0)
00738         return -1;
00739 
00740     /*
00741      * Load the header.
00742      */
00743     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
00744 
00745     if (hdr.bom == 0xfffe) {
00746         hdr.cnt = endian_short(hdr.cnt);
00747         hdr.size.bytes = endian_long(hdr.size.bytes);
00748     }
00749 
00750     _ucdcmp_size = hdr.cnt << 1;
00751     _ucdcmp_nodes = (ac_uint4 *) malloc(hdr.size.bytes);
00752     _ucdcmp_decomp = _ucdcmp_nodes + (_ucdcmp_size + 1);
00753 
00754     /*
00755      * Read the decomposition data in.
00756      */
00757     size = hdr.size.bytes / sizeof(ac_uint4);
00758     fread((char *) _ucdcmp_nodes, sizeof(ac_uint4), size, in);
00759 
00760     /*
00761      * Do an endian swap if necessary.
00762      */
00763     if (hdr.bom == 0xfffe) {
00764         for (i = 0; i < size; i++)
00765             _ucdcmp_nodes[i] = endian_long(_ucdcmp_nodes[i]);
00766     }
00767     fclose(in);
00768     return 0;
00769 }
00770 
00771 /*
00772  * Return -1 on error, 0 if okay
00773  */
00774 static int
00775 _uckdcmp_load(char *paths, int reload)
00776 {
00777     FILE *in;
00778     ac_uint4 size, i;
00779     _ucheader_t hdr;
00780 
00781     if (_uckdcmp_size > 0) {
00782         if (!reload)
00783             /*
00784              * The decompositions have already been loaded.
00785              */
00786           return 0;
00787 
00788         free((char *) _uckdcmp_nodes);
00789         _uckdcmp_size = 0;
00790     }
00791 
00792     if ((in = _ucopenfile(paths, "kdecomp.dat", "rb")) == 0)
00793         return -1;
00794 
00795     /*
00796      * Load the header.
00797      */
00798     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
00799 
00800     if (hdr.bom == 0xfffe) {
00801         hdr.cnt = endian_short(hdr.cnt);
00802         hdr.size.bytes = endian_long(hdr.size.bytes);
00803     }
00804 
00805     _uckdcmp_size = hdr.cnt << 1;
00806     _uckdcmp_nodes = (ac_uint4 *) malloc(hdr.size.bytes);
00807     _uckdcmp_decomp = _uckdcmp_nodes + (_uckdcmp_size + 1);
00808 
00809     /*
00810      * Read the decomposition data in.
00811      */
00812     size = hdr.size.bytes / sizeof(ac_uint4);
00813     fread((char *) _uckdcmp_nodes, sizeof(ac_uint4), size, in);
00814 
00815     /*
00816      * Do an endian swap if necessary.
00817      */
00818     if (hdr.bom == 0xfffe) {
00819         for (i = 0; i < size; i++)
00820             _uckdcmp_nodes[i] = endian_long(_uckdcmp_nodes[i]);
00821     }
00822     fclose(in);
00823     return 0;
00824 }
00825 
00826 static void
00827 _ucdcmp_unload(void)
00828 {
00829     if (_ucdcmp_size == 0)
00830       return;
00831 
00832     /*
00833      * Only need to free the offsets because the memory is allocated as a
00834      * single block.
00835      */
00836     free((char *) _ucdcmp_nodes);
00837     _ucdcmp_size = 0;
00838 }
00839 
00840 static void
00841 _uckdcmp_unload(void)
00842 {
00843     if (_uckdcmp_size == 0)
00844       return;
00845 
00846     /*
00847      * Only need to free the offsets because the memory is allocated as a
00848      * single block.
00849      */
00850     free((char *) _uckdcmp_nodes);
00851     _uckdcmp_size = 0;
00852 }
00853 #endif
00854 
00855 int
00856 ucdecomp(ac_uint4 code, ac_uint4 *num, ac_uint4 **decomp)
00857 {
00858     long l, r, m;
00859 
00860     if (code < _ucdcmp_nodes[0]) {
00861        return 0;
00862     }
00863 
00864     l = 0;
00865     r = _ucdcmp_nodes[_ucdcmp_size] - 1;
00866 
00867     while (l <= r) {
00868         /*
00869          * Determine a "mid" point and adjust to make sure the mid point is at
00870          * the beginning of a code+offset pair.
00871          */
00872         m = (l + r) >> 1;
00873         m -= (m & 1);
00874         if (code > _ucdcmp_nodes[m])
00875           l = m + 2;
00876         else if (code < _ucdcmp_nodes[m])
00877           r = m - 2;
00878         else if (code == _ucdcmp_nodes[m]) {
00879             *num = _ucdcmp_nodes[m + 3] - _ucdcmp_nodes[m + 1];
00880             *decomp = (ac_uint4*)&_ucdcmp_decomp[_ucdcmp_nodes[m + 1]];
00881             return 1;
00882         }
00883     }
00884     return 0;
00885 }
00886 
00887 int
00888 uckdecomp(ac_uint4 code, ac_uint4 *num, ac_uint4 **decomp)
00889 {
00890     long l, r, m;
00891 
00892     if (code < _uckdcmp_nodes[0]) {
00893        return 0;
00894     }
00895     
00896     l = 0;
00897     r = _uckdcmp_nodes[_uckdcmp_size] - 1;
00898 
00899     while (l <= r) {
00900         /*
00901          * Determine a "mid" point and adjust to make sure the mid point is at
00902          * the beginning of a code+offset pair.
00903          */
00904         m = (l + r) >> 1;
00905         m -= (m & 1);
00906         if (code > _uckdcmp_nodes[m])
00907           l = m + 2;
00908         else if (code < _uckdcmp_nodes[m])
00909           r = m - 2;
00910         else if (code == _uckdcmp_nodes[m]) {
00911             *num = _uckdcmp_nodes[m + 3] - _uckdcmp_nodes[m + 1];
00912             *decomp = (ac_uint4*)&_uckdcmp_decomp[_uckdcmp_nodes[m + 1]];
00913             return 1;
00914         }
00915     }
00916     return 0;
00917 }
00918 
00919 int
00920 ucdecomp_hangul(ac_uint4 code, ac_uint4 *num, ac_uint4 decomp[])
00921 {
00922     if (!ucishangul(code))
00923       return 0;
00924 
00925     code -= 0xac00;
00926     decomp[0] = 0x1100 + (ac_uint4) (code / 588);
00927     decomp[1] = 0x1161 + (ac_uint4) ((code % 588) / 28);
00928     decomp[2] = 0x11a7 + (ac_uint4) (code % 28);
00929     *num = (decomp[2] != 0x11a7) ? 3 : 2;
00930 
00931     return 1;
00932 }
00933 
00934 /* mode == 0 for canonical, mode == 1 for compatibility */
00935 static int
00936 uccanoncompatdecomp(const ac_uint4 *in, int inlen,
00937                   ac_uint4 **out, int *outlen, short mode, void *ctx)
00938 {
00939     int l, size;
00940        unsigned i, j, k;
00941     ac_uint4 num, class, *decomp, hangdecomp[3];
00942 
00943     size = inlen * 2;
00944     *out = (ac_uint4 *) ber_memalloc_x(size * sizeof(**out), ctx);
00945     if (*out == NULL)
00946         return *outlen = -1;
00947 
00948     i = 0;
00949     for (j = 0; j < (unsigned) inlen; j++) {
00950        if (mode ? uckdecomp(in[j], &num, &decomp) : ucdecomp(in[j], &num, &decomp)) {
00951             if ( size - i < num) {
00952                 size = inlen + i - j + num - 1;
00953                 *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx );
00954                 if (*out == NULL)
00955                     return *outlen = -1;
00956             }
00957             for (k = 0; k < num; k++) {
00958                 class = uccombining_class(decomp[k]);
00959                 if (class == 0) {
00960                     (*out)[i] = decomp[k];
00961                 } else {
00962                     for (l = i; l > 0; l--)
00963                         if (class >= uccombining_class((*out)[l-1]))
00964                             break;
00965                     AC_MEMCPY(*out + l + 1, *out + l, (i - l) * sizeof(**out));
00966                     (*out)[l] = decomp[k];
00967                 }
00968                 i++;
00969             }
00970         } else if (ucdecomp_hangul(in[j], &num, hangdecomp)) {
00971             if (size - i < num) {
00972                 size = inlen + i - j + num - 1;
00973                 *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx);
00974                 if (*out == NULL)
00975                     return *outlen = -1;
00976             }
00977             for (k = 0; k < num; k++) {
00978                 (*out)[i] = hangdecomp[k];
00979                 i++;
00980             }
00981         } else {
00982             if (size - i < 1) {
00983                 size = inlen + i - j;
00984                 *out = (ac_uint4 *) ber_memrealloc_x(*out, size * sizeof(**out), ctx);
00985                 if (*out == NULL)
00986                     return *outlen = -1;
00987             }
00988             class = uccombining_class(in[j]);
00989             if (class == 0) {
00990                 (*out)[i] = in[j];
00991             } else {
00992                 for (l = i; l > 0; l--)
00993                     if (class >= uccombining_class((*out)[l-1]))
00994                         break;
00995                 AC_MEMCPY(*out + l + 1, *out + l, (i - l) * sizeof(**out));
00996                 (*out)[l] = in[j];
00997             }
00998             i++;
00999         }
01000     }
01001     return *outlen = i;
01002 }
01003 
01004 int
01005 uccanondecomp(const ac_uint4 *in, int inlen,
01006               ac_uint4 **out, int *outlen, void *ctx)
01007 {
01008     return uccanoncompatdecomp(in, inlen, out, outlen, 0, ctx);
01009 }
01010 
01011 int
01012 uccompatdecomp(const ac_uint4 *in, int inlen,
01013               ac_uint4 **out, int *outlen, void *ctx)
01014 {
01015     return uccanoncompatdecomp(in, inlen, out, outlen, 1, ctx);
01016 }
01017 
01018 /**************************************************************************
01019  *
01020  * Support for combining classes.
01021  *
01022  **************************************************************************/
01023 
01024 #if !HARDCODE_DATA
01025 static ac_uint4  _uccmcl_size;
01026 static ac_uint4 *_uccmcl_nodes;
01027 
01028 /*
01029  * Return -1 on error, 0 if okay
01030  */
01031 static int
01032 _uccmcl_load(char *paths, int reload)
01033 {
01034     FILE *in;
01035     ac_uint4 i;
01036     _ucheader_t hdr;
01037 
01038     if (_uccmcl_size > 0) {
01039         if (!reload)
01040             /*
01041              * The combining classes have already been loaded.
01042              */
01043             return 0;
01044 
01045         free((char *) _uccmcl_nodes);
01046         _uccmcl_size = 0;
01047     }
01048 
01049     if ((in = _ucopenfile(paths, "cmbcl.dat", "rb")) == 0)
01050         return -1;
01051 
01052     /*
01053      * Load the header.
01054      */
01055     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
01056 
01057     if (hdr.bom == 0xfffe) {
01058         hdr.cnt = endian_short(hdr.cnt);
01059         hdr.size.bytes = endian_long(hdr.size.bytes);
01060     }
01061 
01062     _uccmcl_size = hdr.cnt * 3;
01063     _uccmcl_nodes = (ac_uint4 *) malloc(hdr.size.bytes);
01064 
01065     /*
01066      * Read the combining classes in.
01067      */
01068     fread((char *) _uccmcl_nodes, sizeof(ac_uint4), _uccmcl_size, in);
01069 
01070     /*
01071      * Do an endian swap if necessary.
01072      */
01073     if (hdr.bom == 0xfffe) {
01074         for (i = 0; i < _uccmcl_size; i++)
01075             _uccmcl_nodes[i] = endian_long(_uccmcl_nodes[i]);
01076     }
01077     fclose(in);
01078     return 0;
01079 }
01080 
01081 static void
01082 _uccmcl_unload(void)
01083 {
01084     if (_uccmcl_size == 0)
01085       return;
01086 
01087     free((char *) _uccmcl_nodes);
01088     _uccmcl_size = 0;
01089 }
01090 #endif
01091 
01092 ac_uint4
01093 uccombining_class(ac_uint4 code)
01094 {
01095     long l, r, m;
01096 
01097     l = 0;
01098     r = _uccmcl_size - 1;
01099 
01100     while (l <= r) {
01101         m = (l + r) >> 1;
01102         m -= (m % 3);
01103         if (code > _uccmcl_nodes[m + 1])
01104           l = m + 3;
01105         else if (code < _uccmcl_nodes[m])
01106           r = m - 3;
01107         else if (code >= _uccmcl_nodes[m] && code <= _uccmcl_nodes[m + 1])
01108           return _uccmcl_nodes[m + 2];
01109     }
01110     return 0;
01111 }
01112 
01113 /**************************************************************************
01114  *
01115  * Support for numeric values.
01116  *
01117  **************************************************************************/
01118 
01119 #if !HARDCODE_DATA
01120 static ac_uint4 *_ucnum_nodes;
01121 static ac_uint4 _ucnum_size;
01122 static short *_ucnum_vals;
01123 
01124 /*
01125  * Return -1 on error, 0 if okay
01126  */
01127 static int
01128 _ucnumb_load(char *paths, int reload)
01129 {
01130     FILE *in;
01131     ac_uint4 size, i;
01132     _ucheader_t hdr;
01133 
01134     if (_ucnum_size > 0) {
01135         if (!reload)
01136           /*
01137            * The numbers have already been loaded.
01138            */
01139           return 0;
01140 
01141         free((char *) _ucnum_nodes);
01142         _ucnum_size = 0;
01143     }
01144 
01145     if ((in = _ucopenfile(paths, "num.dat", "rb")) == 0)
01146       return -1;
01147 
01148     /*
01149      * Load the header.
01150      */
01151     fread((char *) &hdr, sizeof(_ucheader_t), 1, in);
01152 
01153     if (hdr.bom == 0xfffe) {
01154         hdr.cnt = endian_short(hdr.cnt);
01155         hdr.size.bytes = endian_long(hdr.size.bytes);
01156     }
01157 
01158     _ucnum_size = hdr.cnt;
01159     _ucnum_nodes = (ac_uint4 *) malloc(hdr.size.bytes);
01160     _ucnum_vals = (short *) (_ucnum_nodes + _ucnum_size);
01161 
01162     /*
01163      * Read the combining classes in.
01164      */
01165     fread((char *) _ucnum_nodes, sizeof(unsigned char), hdr.size.bytes, in);
01166 
01167     /*
01168      * Do an endian swap if necessary.
01169      */
01170     if (hdr.bom == 0xfffe) {
01171         for (i = 0; i < _ucnum_size; i++)
01172           _ucnum_nodes[i] = endian_long(_ucnum_nodes[i]);
01173 
01174         /*
01175          * Determine the number of values that have to be adjusted.
01176          */
01177         size = (hdr.size.bytes -
01178                 (_ucnum_size * (sizeof(ac_uint4) << 1))) /
01179             sizeof(short);
01180 
01181         for (i = 0; i < size; i++)
01182           _ucnum_vals[i] = endian_short(_ucnum_vals[i]);
01183     }
01184     fclose(in);
01185     return 0;
01186 }
01187 
01188 static void
01189 _ucnumb_unload(void)
01190 {
01191     if (_ucnum_size == 0)
01192       return;
01193 
01194     free((char *) _ucnum_nodes);
01195     _ucnum_size = 0;
01196 }
01197 #endif
01198 
01199 int
01200 ucnumber_lookup(ac_uint4 code, struct ucnumber *num)
01201 {
01202     long l, r, m;
01203     short *vp;
01204 
01205     l = 0;
01206     r = _ucnum_size - 1;
01207     while (l <= r) {
01208         /*
01209          * Determine a "mid" point and adjust to make sure the mid point is at
01210          * the beginning of a code+offset pair.
01211          */
01212         m = (l + r) >> 1;
01213         m -= (m & 1);
01214         if (code > _ucnum_nodes[m])
01215           l = m + 2;
01216         else if (code < _ucnum_nodes[m])
01217           r = m - 2;
01218         else {
01219             vp = (short *)_ucnum_vals + _ucnum_nodes[m + 1];
01220             num->numerator = (int) *vp++;
01221             num->denominator = (int) *vp;
01222             return 1;
01223         }
01224     }
01225     return 0;
01226 }
01227 
01228 int
01229 ucdigit_lookup(ac_uint4 code, int *digit)
01230 {
01231     long l, r, m;
01232     short *vp;
01233 
01234     l = 0;
01235     r = _ucnum_size - 1;
01236     while (l <= r) {
01237         /*
01238          * Determine a "mid" point and adjust to make sure the mid point is at
01239          * the beginning of a code+offset pair.
01240          */
01241         m = (l + r) >> 1;
01242         m -= (m & 1);
01243         if (code > _ucnum_nodes[m])
01244           l = m + 2;
01245         else if (code < _ucnum_nodes[m])
01246           r = m - 2;
01247         else {
01248             vp = (short *)_ucnum_vals + _ucnum_nodes[m + 1];
01249             if (*vp == *(vp + 1)) {
01250               *digit = *vp;
01251               return 1;
01252             }
01253             return 0;
01254         }
01255     }
01256     return 0;
01257 }
01258 
01259 struct ucnumber
01260 ucgetnumber(ac_uint4 code)
01261 {
01262     struct ucnumber num;
01263 
01264     /*
01265      * Initialize with some arbitrary value, because the caller simply cannot
01266      * tell for sure if the code is a number without calling the ucisnumber()
01267      * macro before calling this function.
01268      */
01269     num.numerator = num.denominator = -111;
01270 
01271     (void) ucnumber_lookup(code, &num);
01272 
01273     return num;
01274 }
01275 
01276 int
01277 ucgetdigit(ac_uint4 code)
01278 {
01279     int dig;
01280 
01281     /*
01282      * Initialize with some arbitrary value, because the caller simply cannot
01283      * tell for sure if the code is a number without calling the ucisdigit()
01284      * macro before calling this function.
01285      */
01286     dig = -111;
01287 
01288     (void) ucdigit_lookup(code, &dig);
01289 
01290     return dig;
01291 }
01292 
01293 /**************************************************************************
01294  *
01295  * Setup and cleanup routines.
01296  *
01297  **************************************************************************/
01298 
01299 #if HARDCODE_DATA
01300 int ucdata_load(char *paths, int masks) { return 0; }
01301 void ucdata_unload(int masks) { }
01302 int ucdata_reload(char *paths, int masks) { return 0; }
01303 #else
01304 /*
01305  * Return 0 if okay, negative on error
01306  */
01307 int
01308 ucdata_load(char *paths, int masks)
01309 {
01310     int error = 0;
01311 
01312     if (masks & UCDATA_CTYPE)
01313       error |= _ucprop_load(paths, 0) < 0 ? UCDATA_CTYPE : 0;
01314     if (masks & UCDATA_CASE)
01315       error |= _uccase_load(paths, 0) < 0 ? UCDATA_CASE : 0;
01316     if (masks & UCDATA_DECOMP)
01317       error |= _ucdcmp_load(paths, 0) < 0 ? UCDATA_DECOMP : 0;
01318     if (masks & UCDATA_CMBCL)
01319       error |= _uccmcl_load(paths, 0) < 0 ? UCDATA_CMBCL : 0;
01320     if (masks & UCDATA_NUM)
01321       error |= _ucnumb_load(paths, 0) < 0 ? UCDATA_NUM : 0;
01322     if (masks & UCDATA_COMP)
01323       error |= _uccomp_load(paths, 0) < 0 ? UCDATA_COMP : 0;
01324     if (masks & UCDATA_KDECOMP)
01325       error |= _uckdcmp_load(paths, 0) < 0 ? UCDATA_KDECOMP : 0;
01326 
01327     return -error;
01328 }
01329 
01330 void
01331 ucdata_unload(int masks)
01332 {
01333     if (masks & UCDATA_CTYPE)
01334       _ucprop_unload();
01335     if (masks & UCDATA_CASE)
01336       _uccase_unload();
01337     if (masks & UCDATA_DECOMP)
01338       _ucdcmp_unload();
01339     if (masks & UCDATA_CMBCL)
01340       _uccmcl_unload();
01341     if (masks & UCDATA_NUM)
01342       _ucnumb_unload();
01343     if (masks & UCDATA_COMP)
01344       _uccomp_unload();
01345     if (masks & UCDATA_KDECOMP)
01346       _uckdcmp_unload();
01347 }
01348 
01349 /*
01350  * Return 0 if okay, negative on error
01351  */
01352 int
01353 ucdata_reload(char *paths, int masks)
01354 {
01355     int error = 0;
01356 
01357     if (masks & UCDATA_CTYPE)
01358         error |= _ucprop_load(paths, 1) < 0 ? UCDATA_CTYPE : 0;
01359     if (masks & UCDATA_CASE)
01360         error |= _uccase_load(paths, 1) < 0 ? UCDATA_CASE : 0;
01361     if (masks & UCDATA_DECOMP)
01362         error |= _ucdcmp_load(paths, 1) < 0 ? UCDATA_DECOMP : 0;
01363     if (masks & UCDATA_CMBCL)
01364         error |= _uccmcl_load(paths, 1) < 0 ? UCDATA_CMBCL : 0;
01365     if (masks & UCDATA_NUM)
01366         error |= _ucnumb_load(paths, 1) < 0 ? UCDATA_NUM : 0;
01367     if (masks & UCDATA_COMP)
01368         error |= _uccomp_load(paths, 1) < 0 ? UCDATA_COMP : 0;
01369     if (masks & UCDATA_KDECOMP)
01370         error |= _uckdcmp_load(paths, 1) < 0 ? UCDATA_KDECOMP : 0;
01371 
01372     return -error;
01373 }
01374 #endif
01375 
01376 #ifdef TEST
01377 
01378 void
01379 main(void)
01380 {
01381     int dig;
01382     ac_uint4 i, lo, *dec;
01383     struct ucnumber num;
01384 
01385 /*    ucdata_setup("."); */
01386 
01387     if (ucisweak(0x30))
01388       printf("WEAK\n");
01389     else
01390       printf("NOT WEAK\n");
01391 
01392     printf("LOWER 0x%04lX\n", uctolower(0xff3a));
01393     printf("UPPER 0x%04lX\n", uctoupper(0xff5a));
01394 
01395     if (ucisalpha(0x1d5))
01396       printf("ALPHA\n");
01397     else
01398       printf("NOT ALPHA\n");
01399 
01400     if (ucisupper(0x1d5)) {
01401         printf("UPPER\n");
01402         lo = uctolower(0x1d5);
01403         printf("0x%04lx\n", lo);
01404         lo = uctotitle(0x1d5);
01405         printf("0x%04lx\n", lo);
01406     } else
01407       printf("NOT UPPER\n");
01408 
01409     if (ucistitle(0x1d5))
01410       printf("TITLE\n");
01411     else
01412       printf("NOT TITLE\n");
01413 
01414     if (uciscomposite(0x1d5))
01415       printf("COMPOSITE\n");
01416     else
01417       printf("NOT COMPOSITE\n");
01418 
01419     if (ucdecomp(0x1d5, &lo, &dec)) {
01420         for (i = 0; i < lo; i++)
01421           printf("0x%04lx ", dec[i]);
01422         putchar('\n');
01423     }
01424 
01425     if ((lo = uccombining_class(0x41)) != 0)
01426       printf("0x41 CCL %ld\n", lo);
01427 
01428     if (ucisxdigit(0xfeff))
01429       printf("0xFEFF HEX DIGIT\n");
01430     else
01431       printf("0xFEFF NOT HEX DIGIT\n");
01432 
01433     if (ucisdefined(0x10000))
01434       printf("0x10000 DEFINED\n");
01435     else
01436       printf("0x10000 NOT DEFINED\n");
01437 
01438     if (ucnumber_lookup(0x30, &num)) {
01439         if (num.denominator != 1)
01440           printf("UCNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
01441         else
01442           printf("UCNUMBER: 0x30 = %d\n", num.numerator);
01443     } else
01444       printf("UCNUMBER: 0x30 NOT A NUMBER\n");
01445 
01446     if (ucnumber_lookup(0xbc, &num)) {
01447         if (num.denominator != 1)
01448           printf("UCNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
01449         else
01450           printf("UCNUMBER: 0xbc = %d\n", num.numerator);
01451     } else
01452       printf("UCNUMBER: 0xbc NOT A NUMBER\n");
01453 
01454 
01455     if (ucnumber_lookup(0xff19, &num)) {
01456         if (num.denominator != 1)
01457           printf("UCNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator);
01458         else
01459           printf("UCNUMBER: 0xff19 = %d\n", num.numerator);
01460     } else
01461       printf("UCNUMBER: 0xff19 NOT A NUMBER\n");
01462 
01463     if (ucnumber_lookup(0x4e00, &num)) {
01464         if (num.denominator != 1)
01465           printf("UCNUMBER: 0x4e00 = %d/%d\n", num.numerator, num.denominator);
01466         else
01467           printf("UCNUMBER: 0x4e00 = %d\n", num.numerator);
01468     } else
01469       printf("UCNUMBER: 0x4e00 NOT A NUMBER\n");
01470 
01471     if (ucdigit_lookup(0x06f9, &dig))
01472       printf("UCDIGIT: 0x6f9 = %d\n", dig);
01473     else
01474       printf("UCDIGIT: 0x6f9 NOT A NUMBER\n");
01475 
01476     dig = ucgetdigit(0x0969);
01477     printf("UCGETDIGIT: 0x969 = %d\n", dig);
01478 
01479     num = ucgetnumber(0x30);
01480     if (num.denominator != 1)
01481       printf("UCGETNUMBER: 0x30 = %d/%d\n", num.numerator, num.denominator);
01482     else
01483       printf("UCGETNUMBER: 0x30 = %d\n", num.numerator);
01484 
01485     num = ucgetnumber(0xbc);
01486     if (num.denominator != 1)
01487       printf("UCGETNUMBER: 0xbc = %d/%d\n", num.numerator, num.denominator);
01488     else
01489       printf("UCGETNUMBER: 0xbc = %d\n", num.numerator);
01490 
01491     num = ucgetnumber(0xff19);
01492     if (num.denominator != 1)
01493       printf("UCGETNUMBER: 0xff19 = %d/%d\n", num.numerator, num.denominator);
01494     else
01495       printf("UCGETNUMBER: 0xff19 = %d\n", num.numerator);
01496 
01497 /*    ucdata_cleanup(); */
01498     exit(0);
01499 }
01500 
01501 #endif /* TEST */