Imported Upstream version 2.9.5_rc2
[platform/upstream/libxml2.git] / genUnicode.py
1 #!/usr/bin/python -u
2 #
3 # Original script modified in November 2003 to take advantage of
4 # the character-validation range routines, and updated to the
5 # current Unicode information (Version 4.0.1)
6 #
7 # NOTE: there is an 'alias' facility for blocks which are not present in
8 #       the current release, but are needed for ABI compatibility.  This
9 #       must be accomplished MANUALLY!  Please see the comments below under
10 #     'blockAliases'
11 #
12 import sys
13 import string
14 import time
15
16 webpage = "http://www.unicode.org/Public/4.0-Update1/UCD-4.0.1.html"
17 sources = "Blocks-4.0.1.txt UnicodeData-4.0.1.txt"
18
19 #
20 # blockAliases is a small hack - it is used for mapping block names which
21 # were were used in the 3.1 release, but are missing or changed in the current
22 # release.  The format is "OldBlockName:NewBlockName1[,NewBlockName2[,...]]"
23 blockAliases = []
24 blockAliases.append("CombiningMarksforSymbols:CombiningDiacriticalMarksforSymbols")
25 blockAliases.append("Greek:GreekandCoptic")
26 blockAliases.append("PrivateUse:PrivateUseArea,SupplementaryPrivateUseArea-A," + 
27         "SupplementaryPrivateUseArea-B")
28
29 # minTableSize gives the minimum number of ranges which must be present
30 # before a range table is produced.  If there are less than this
31 # number, inline comparisons are generated
32 minTableSize = 8
33
34 (blockfile, catfile) = string.split(sources)
35
36
37 #
38 # Now process the "blocks" file, reducing it to a dictionary
39 # indexed by blockname, containing a tuple with the applicable
40 # block range
41 #
42 BlockNames = {}
43 try:
44     blocks = open(blockfile, "r")
45 except:
46     print "Missing %s, aborting ..." % blockfile
47     sys.exit(1)
48
49 for line in blocks.readlines():
50     if line[0] == '#':
51         continue
52     line = string.strip(line)
53     if line == '':
54         continue
55     try:
56         fields = string.split(line, ';')
57         range = string.strip(fields[0])
58         (start, end) = string.split(range, "..")
59         name = string.strip(fields[1])
60         name = string.replace(name, ' ', '')
61     except:
62         print "Failed to process line: %s" % (line)
63         continue
64     start = "0x" + start
65     end = "0x" + end
66     try:
67         BlockNames[name].append((start, end))
68     except:
69         BlockNames[name] = [(start, end)]
70 blocks.close()
71 print "Parsed %d blocks descriptions" % (len(BlockNames.keys()))
72
73 for block in blockAliases:
74     alias = string.split(block,':')
75     alist = string.split(alias[1],',')
76     for comp in alist:
77         if BlockNames.has_key(comp):
78             if alias[0] not in BlockNames:
79                 BlockNames[alias[0]] = []
80             for r in BlockNames[comp]:
81                 BlockNames[alias[0]].append(r)
82         else:
83             print "Alias %s: %s not in Blocks" % (alias[0], comp)
84             continue
85
86 #
87 # Next process the Categories file. This is more complex, since
88 # the file is in code sequence, and we need to invert it.  We use
89 # a dictionary with index category-name, with each entry containing
90 # all the ranges (codepoints) of that category.  Note that category
91 # names comprise two parts - the general category, and the "subclass"
92 # within that category.  Therefore, both "general category" (which is
93 # the first character of the 2-character category-name) and the full
94 # (2-character) name are entered into this dictionary.
95 #
96 try:
97     data = open(catfile, "r")
98 except:
99     print "Missing %s, aborting ..." % catfile
100     sys.exit(1)
101
102 nbchar = 0;
103 Categories = {}
104 for line in data.readlines():
105     if line[0] == '#':
106         continue
107     line = string.strip(line)
108     if line == '':
109         continue
110     try:
111         fields = string.split(line, ';')
112         point = string.strip(fields[0])
113         value = 0
114         while point != '':
115             value = value * 16
116             if point[0] >= '0' and point[0] <= '9':
117                 value = value + ord(point[0]) - ord('0')
118             elif point[0] >= 'A' and point[0] <= 'F':
119                 value = value + 10 + ord(point[0]) - ord('A')
120             elif point[0] >= 'a' and point[0] <= 'f':
121                 value = value + 10 + ord(point[0]) - ord('a')
122             point = point[1:]
123         name = fields[2]
124     except:
125         print "Failed to process line: %s" % (line)
126         continue
127     
128     nbchar = nbchar + 1
129     # update entry for "full name"
130     try:
131         Categories[name].append(value)
132     except:
133         try:
134             Categories[name] = [value]
135         except:
136             print "Failed to process line: %s" % (line)
137     # update "general category" name
138     try:
139         Categories[name[0]].append(value)
140     except:
141         try:
142             Categories[name[0]] = [value]
143         except:
144             print "Failed to process line: %s" % (line)
145
146 blocks.close()
147 print "Parsed %d char generating %d categories" % (nbchar, len(Categories.keys()))
148
149 #
150 # The data is now all read.  Time to process it into a more useful form.
151 #
152 # reduce the number list into ranges
153 for cat in Categories.keys():
154     list = Categories[cat]
155     start = -1
156     prev = -1
157     end = -1
158     ranges = []
159     for val in list:
160         if start == -1:
161             start = val
162             prev = val
163             continue
164         elif val == prev + 1:
165             prev = val
166             continue
167         elif prev == start:
168             ranges.append((prev, prev))
169             start = val
170             prev = val
171             continue
172         else:
173             ranges.append((start, prev))
174             start = val
175             prev = val
176             continue
177     if prev == start:
178         ranges.append((prev, prev))
179     else:
180         ranges.append((start, prev))
181     Categories[cat] = ranges
182
183 #
184 # Assure all data is in alphabetic order, since we will be doing binary
185 # searches on the tables.
186 #
187 bkeys = BlockNames.keys()
188 bkeys.sort()
189
190 ckeys = Categories.keys()
191 ckeys.sort()
192
193 #
194 # Generate the resulting files
195 #
196 try:
197     header = open("include/libxml/xmlunicode.h", "w")
198 except:
199     print "Failed to open include/libxml/xmlunicode.h"
200     sys.exit(1)
201
202 try:
203     output = open("xmlunicode.c", "w")
204 except:
205     print "Failed to open xmlunicode.c"
206     sys.exit(1)
207
208 date = time.asctime(time.localtime(time.time()))
209
210 header.write(
211 """/*
212  * Summary: Unicode character APIs
213  * Description: API for the Unicode character APIs
214  *
215  * This file is automatically generated from the
216  * UCS description files of the Unicode Character Database
217  * %s
218  * using the genUnicode.py Python script.
219  *
220  * Generation date: %s
221  * Sources: %s
222  * Author: Daniel Veillard
223  */
224
225 #ifndef __XML_UNICODE_H__
226 #define __XML_UNICODE_H__
227
228 #include <libxml/xmlversion.h>
229
230 #ifdef LIBXML_UNICODE_ENABLED
231
232 #ifdef __cplusplus
233 extern "C" {
234 #endif
235
236 """ % (webpage, date, sources));
237
238 output.write(
239 """/*
240  * xmlunicode.c: this module implements the Unicode character APIs
241  *
242  * This file is automatically generated from the
243  * UCS description files of the Unicode Character Database
244  * %s
245  * using the genUnicode.py Python script.
246  *
247  * Generation date: %s
248  * Sources: %s
249  * Daniel Veillard <veillard@redhat.com>
250  */
251
252 #define IN_LIBXML
253 #include "libxml.h"
254
255 #ifdef LIBXML_UNICODE_ENABLED
256
257 #include <string.h>
258 #include <libxml/xmlversion.h>
259 #include <libxml/xmlunicode.h>
260 #include <libxml/chvalid.h>
261
262 typedef int (xmlIntFunc)(int);  /* just to keep one's mind untwisted */
263
264 typedef struct {
265     const char *rangename;
266     xmlIntFunc *func;
267 } xmlUnicodeRange;
268
269 typedef struct {
270     const xmlUnicodeRange *table;
271     int             numentries;
272 } xmlUnicodeNameTable;
273
274
275 static xmlIntFunc *xmlUnicodeLookup(xmlUnicodeNameTable *tptr, const char *tname);
276
277 static const xmlUnicodeRange xmlUnicodeBlocks[] = {
278 """ % (webpage, date, sources));
279
280 flag = 0
281 for block in bkeys:
282     name = string.replace(block, '-', '')
283     if flag:
284         output.write(',\n')
285     else:
286         flag = 1
287     output.write('  {"%s", xmlUCSIs%s}' % (block, name))
288 output.write('};\n\n')
289
290 output.write('static xmlUnicodeRange xmlUnicodeCats[] = {\n')
291 flag = 0;
292 for name in ckeys:
293     if flag:
294         output.write(',\n')
295     else:
296         flag = 1
297     output.write('  {"%s", xmlUCSIsCat%s}' % (name, name))
298 output.write('};\n\n')
299
300 #
301 # For any categories with more than minTableSize ranges we generate
302 # a range table suitable for xmlCharInRange
303 #
304 for name in ckeys:
305   if len(Categories[name]) > minTableSize:
306     numshort = 0
307     numlong = 0
308     ranges = Categories[name]
309     sptr = "NULL"
310     lptr = "NULL"
311     for range in ranges:
312       (low, high) = range
313       if high < 0x10000:
314         if numshort == 0:
315           pline = "static const xmlChSRange xml%sS[] = {" % name
316           sptr = "xml%sS" % name
317         else:
318           pline += ", "
319         numshort += 1
320       else:
321         if numlong == 0:
322           if numshort > 0:
323             output.write(pline + " };\n")
324           pline = "static const xmlChLRange xml%sL[] = {" % name
325           lptr = "xml%sL" % name
326         else:
327           pline += ", "
328         numlong += 1
329       if len(pline) > 60:
330         output.write(pline + "\n")
331         pline = "    "
332       pline += "{%s, %s}" % (hex(low), hex(high))
333     output.write(pline + " };\nstatic xmlChRangeGroup xml%sG = {%s,%s,%s,%s};\n\n"
334          % (name, numshort, numlong, sptr, lptr))
335
336
337 output.write(
338 """static xmlUnicodeNameTable xmlUnicodeBlockTbl = {xmlUnicodeBlocks, %s};
339 static xmlUnicodeNameTable xmlUnicodeCatTbl = {xmlUnicodeCats, %s};
340
341 /**
342  * xmlUnicodeLookup:
343  * @tptr: pointer to the name table
344  * @name: name to be found
345  *
346  * binary table lookup for user-supplied name
347  *
348  * Returns pointer to range function if found, otherwise NULL
349  */
350 static xmlIntFunc
351 *xmlUnicodeLookup(xmlUnicodeNameTable *tptr, const char *tname) {
352     int low, high, mid, cmp;
353     xmlUnicodeRange *sptr;
354
355     if ((tptr == NULL) || (tname == NULL)) return(NULL);
356
357     low = 0;
358     high = tptr->numentries - 1;
359     sptr = tptr->table;
360     while (low <= high) {
361         mid = (low + high) / 2;
362         if ((cmp=strcmp(tname, sptr[mid].rangename)) == 0)
363             return (sptr[mid].func);
364         if (cmp < 0)
365             high = mid - 1;
366         else
367             low = mid + 1;
368     }
369     return (NULL);    
370 }
371
372 """ % (len(BlockNames), len(Categories)) )
373
374 for block in bkeys:
375     name = string.replace(block, '-', '')
376     header.write("XMLPUBFUN int XMLCALL xmlUCSIs%s\t(int code);\n" % name)
377     output.write("/**\n * xmlUCSIs%s:\n * @code: UCS code point\n" % (name))
378     output.write(" *\n * Check whether the character is part of %s UCS Block\n"%
379                  (block))
380     output.write(" *\n * Returns 1 if true 0 otherwise\n */\n");
381     output.write("int\nxmlUCSIs%s(int code) {\n    return(" % name)
382     flag = 0
383     for (start, end) in BlockNames[block]:
384         if flag:
385             output.write(" ||\n           ")
386         else:
387             flag = 1
388         output.write("((code >= %s) && (code <= %s))" % (start, end))
389     output.write(");\n}\n\n")
390
391 header.write("\nXMLPUBFUN int XMLCALL xmlUCSIsBlock\t(int code, const char *block);\n\n")
392 output.write(
393 """/**
394  * xmlUCSIsBlock:
395  * @code: UCS code point
396  * @block: UCS block name
397  *
398  * Check whether the character is part of the UCS Block
399  *
400  * Returns 1 if true, 0 if false and -1 on unknown block
401  */
402 int
403 xmlUCSIsBlock(int code, const char *block) {
404     xmlIntFunc *func;
405
406     func = xmlUnicodeLookup(&xmlUnicodeBlockTbl, block);
407     if (func == NULL)
408         return (-1);
409     return (func(code));
410 }
411
412 """)
413
414 for name in ckeys:
415     ranges = Categories[name]
416     header.write("XMLPUBFUN int XMLCALL xmlUCSIsCat%s\t(int code);\n" % name)
417     output.write("/**\n * xmlUCSIsCat%s:\n * @code: UCS code point\n" % (name))
418     output.write(" *\n * Check whether the character is part of %s UCS Category\n"%
419                  (name))
420     output.write(" *\n * Returns 1 if true 0 otherwise\n */\n");
421     output.write("int\nxmlUCSIsCat%s(int code) {\n" % name)
422     if len(Categories[name]) > minTableSize:
423         output.write("    return(xmlCharInRange((unsigned int)code, &xml%sG)"
424             % name)
425     else:
426         start = 1
427         for range in ranges:
428             (begin, end) = range;
429             if start:
430                 output.write("    return(");
431                 start = 0
432             else:
433                 output.write(" ||\n           ");
434             if (begin == end):
435                 output.write("(code == %s)" % (hex(begin)))
436             else:
437                 output.write("((code >= %s) && (code <= %s))" % (
438                          hex(begin), hex(end)))
439     output.write(");\n}\n\n")
440
441 header.write("\nXMLPUBFUN int XMLCALL xmlUCSIsCat\t(int code, const char *cat);\n")
442 output.write(
443 """/**
444  * xmlUCSIsCat:
445  * @code: UCS code point
446  * @cat: UCS Category name
447  *
448  * Check whether the character is part of the UCS Category
449  *
450  * Returns 1 if true, 0 if false and -1 on unknown category
451  */
452 int
453 xmlUCSIsCat(int code, const char *cat) {
454     xmlIntFunc *func;
455
456     func = xmlUnicodeLookup(&xmlUnicodeCatTbl, cat);
457     if (func == NULL)
458         return (-1);
459     return (func(code));
460 }
461
462 #define bottom_xmlunicode
463 #include "elfgcchack.h"
464 #endif /* LIBXML_UNICODE_ENABLED */
465 """)
466
467 header.write("""
468 #ifdef __cplusplus
469 }
470 #endif
471
472 #endif /* LIBXML_UNICODE_ENABLED */
473
474 #endif /* __XML_UNICODE_H__ */
475 """);
476
477 header.close()
478 output.close()