38fc6973558346b682e06aacf51e60828274f438
[platform/upstream/fontconfig.git] / fc-lang / fc-lang.c
1 /*
2  * fontconfig/fc-lang/fc-lang.c
3  *
4  * Copyright © 2002 Keith Packard
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of the author(s) not be used in
11  * advertising or publicity pertaining to distribution of the software without
12  * specific, written prior permission.  The authors make no
13  * representations about the suitability of this software for any purpose.  It
14  * is provided "as is" without express or implied warranty.
15  *
16  * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24
25 #include "fccharset.c"
26 #include "fcstr.c"
27 #include "fcserialize.c"
28
29 /*
30  * fc-lang
31  *
32  * Read a set of language orthographies and build C declarations for
33  * charsets which can then be used to identify which languages are
34  * supported by a given font.  Note that this uses some utilities
35  * from the fontconfig library, so the necessary file is simply
36  * included in this compilation.  A couple of extra utility
37  * functions are also needed in slightly modified form
38  */
39
40 FcPrivate void
41 FcCacheObjectReference (void *object FC_UNUSED)
42 {
43 }
44
45 FcPrivate void
46 FcCacheObjectDereference (void *object FC_UNUSED)
47 {
48 }
49
50 FcPrivate FcChar8 *
51 FcLangNormalize (const FcChar8 *lang FC_UNUSED)
52 {
53     return NULL;
54 }
55
56 int FcDebugVal;
57
58 FcChar8 *
59 FcConfigHome (void)
60 {
61     return (FcChar8 *) getenv ("HOME");
62 }
63
64 static void 
65 fatal (const char *file, int lineno, const char *msg)
66 {
67     if (lineno)
68         fprintf (stderr, "%s:%d: %s\n", file, lineno, msg);
69     else
70         fprintf (stderr, "%s: %s\n", file, msg);
71     exit (1);
72 }
73
74 static char *
75 get_line (FILE *f, char *buf, int *lineno)
76 {
77     char    *hash;
78     char    *line;
79     int     end;
80
81 next:
82     line = buf;
83     if (!fgets (line, 1024, f))
84         return 0;
85     ++(*lineno);
86     hash = strchr (line, '#');
87     if (hash)
88         *hash = '\0';
89
90     while (line[0] && isspace (line[0]))
91       line++;
92     end = strlen (line);
93     while (end > 0 && isspace (line[end-1]))
94       line[--end] = '\0';
95
96     if (line[0] == '\0' || line[0] == '\n' || line[0] == '\r')
97       goto next;
98
99     return line;
100 }
101
102 static char     *dir = 0;
103
104 static FILE *
105 scanopen (char *file)
106 {
107     FILE    *f;
108
109     f = fopen (file, "r");
110     if (!f && dir)
111     {
112         char    path[1024];
113         
114         strcpy (path, dir);
115         strcat (path, "/");
116         strcat (path, file);
117         f = fopen (path, "r");
118     }
119     return f;
120 }
121
122 /*
123  * build a single charset from a source file
124  *
125  * The file format is quite simple, either
126  * a single hex value or a pair separated with a dash
127  *
128  * Comments begin with '#'
129  */
130
131 static FcCharSet *
132 scan (FILE *f, char *file, FcCharSetFreezer *freezer)
133 {
134     FcCharSet       *c = 0;
135     FcCharSet       *n;
136     FcBool          del;
137     int             start, end, ucs4;
138     char            buf[1024];
139     char            *line;
140     int             lineno = 0;
141
142     while ((line = get_line (f, buf, &lineno)))
143     {
144         if (!strncmp (line, "include", 7))
145         {
146             FILE *included_f;
147             char *included_file;
148             included_file = strchr (line, ' ');
149             if (!included_file)
150                 fatal (file, lineno,
151                        "invalid syntax, expected: include filename");
152             while (isspace(*included_file))
153                 included_file++;
154             included_f = scanopen (included_file);
155             if (!included_f)
156                 fatal (included_file, 0, "can't open");
157             n = scan (included_f, included_file, freezer);
158             fclose (included_f);
159             if (!c)
160                 c = FcCharSetCreate ();
161             if (!FcCharSetMerge (c, n, NULL))
162                 fatal (file, lineno, "out of memory");
163             FcCharSetDestroy (n);
164             continue;
165         }
166         del = FcFalse;
167         if (line[0] == '-')
168         {
169           del = FcTrue;
170           line++;
171         }
172         if (strchr (line, '-'))
173         {
174             if (sscanf (line, "%x-%x", &start, &end) != 2)
175                 fatal (file, lineno, "parse error");
176         }
177         else
178         {
179             if (sscanf (line, "%x", &start) != 1)
180                 fatal (file, lineno, "parse error");
181             end = start;
182         }
183         if (!c)
184             c = FcCharSetCreate ();
185         for (ucs4 = start; ucs4 <= end; ucs4++)
186         {
187             if (!((del ? FcCharSetDelChar : FcCharSetAddChar) (c, ucs4)))
188                 fatal (file, lineno, "out of memory");
189         }
190     }
191     n = (FcCharSet *) FcCharSetFreeze (freezer, c);
192     FcCharSetDestroy (c);
193     return n;
194 }
195
196 /*
197  * Convert a file name into a name suitable for C declarations
198  */
199 static char *
200 get_name (char *file)
201 {
202     char    *name;
203     char    *dot;
204
205     dot = strchr (file, '.');
206     if (!dot)
207         dot = file + strlen(file);
208     name = malloc (dot - file + 1);
209     strncpy (name, file, dot - file);
210     name[dot-file] = '\0';
211     return name;
212 }
213
214 /*
215  * Convert a C name into a language name
216  */
217 static char *
218 get_lang (char *name)
219 {
220     char    *lang = malloc (strlen (name) + 1);
221     char    *l = lang;
222     char    c;
223
224     while ((c = *name++))
225     {
226         if (isupper ((int) (unsigned char) c))
227             c = tolower ((int) (unsigned char) c);
228         if (c == '_')
229             c = '-';
230         if (c == ' ')
231             continue;
232         *l++ = c;
233     }
234     *l++ = '\0';
235     return lang;
236 }
237
238 typedef struct _Entry {
239     int id;
240     char *file;
241 } Entry;
242
243 static int compare (const void *a, const void *b)
244 {
245     const Entry *as = a, *bs = b;
246     return FcStrCmpIgnoreCase ((const FcChar8 *) as->file, (const FcChar8 *) bs->file);
247 }
248
249 #define MAX_LANG            1024
250 #define MAX_LANG_SET_MAP    ((MAX_LANG + 31) / 32)
251
252 #define BitSet(map, i)   ((map)[(entries[i].id)>>5] |= ((FcChar32) 1 << ((entries[i].id) & 0x1f)))
253
254 int
255 main (int argc FC_UNUSED, char **argv)
256 {
257     static Entry        entries[MAX_LANG + 1];
258     static FcCharSet    *sets[MAX_LANG];
259     static int          duplicate[MAX_LANG];
260     static int          country[MAX_LANG];
261     static char         *names[MAX_LANG];
262     static char         *langs[MAX_LANG];
263     static int          off[MAX_LANG];
264     FILE        *f;
265     int         ncountry = 0;
266     int         i = 0;
267     int         nsets = 0;
268     int         argi;
269     FcCharLeaf  **leaves;
270     int         total_leaves = 0;
271     int         l, sl, tl, tn;
272     static char         line[1024];
273     static FcChar32     map[MAX_LANG_SET_MAP];
274     int         num_lang_set_map;
275     int         setRangeStart[26];
276     int         setRangeEnd[26];
277     FcChar8     setRangeChar;
278     FcCharSetFreezer    *freezer;
279     
280     freezer = FcCharSetFreezerCreate ();
281     if (!freezer)
282         fatal (argv[0], 0, "out of memory");
283     argi = 1;
284     while (argv[argi])
285     {
286         if (!strcmp (argv[argi], "-d"))
287         {
288             argi++;
289             dir = argv[argi++];
290             continue;
291         }
292         if (i == MAX_LANG)
293             fatal (argv[0], 0, "Too many languages");
294         entries[i].id = i;
295         entries[i].file = argv[argi++];
296         i++;
297     }
298     entries[i].file = 0;
299     qsort (entries, i, sizeof (Entry), compare);
300     i = 0;
301     while (entries[i].file)
302     {
303         f = scanopen (entries[i].file);
304         if (!f)
305             fatal (entries[i].file, 0, strerror (errno));
306         sets[i] = scan (f, entries[i].file, freezer);
307         names[i] = get_name (entries[i].file);
308         langs[i] = get_lang(names[i]);
309         if (strchr (langs[i], '-'))
310             country[ncountry++] = i;
311
312         total_leaves += sets[i]->num;
313         i++;
314         fclose (f);
315     }
316     nsets = i;
317     sets[i] = 0;
318     leaves = malloc (total_leaves * sizeof (FcCharLeaf *));
319     tl = 0;
320     /*
321      * Find unique leaves
322      */
323     for (i = 0; sets[i]; i++)
324     {
325         for (sl = 0; sl < sets[i]->num; sl++)
326         {
327             for (l = 0; l < tl; l++)
328                 if (leaves[l] == FcCharSetLeaf(sets[i], sl))
329                     break;
330             if (l == tl)
331                 leaves[tl++] = FcCharSetLeaf(sets[i], sl);
332         }
333     }
334
335     /*
336      * Scan the input until the marker is found
337      */
338     
339     while (fgets (line, sizeof (line), stdin))
340     {
341         if (!strncmp (line, "@@@", 3))
342             break;
343         fputs (line, stdout);
344     }
345     
346     printf ("/* total size: %d unique leaves: %d */\n\n",
347             total_leaves, tl);
348
349     /*
350      * Find duplicate charsets
351      */
352     duplicate[0] = -1;
353     for (i = 1; sets[i]; i++)
354     {
355         int j;
356
357         duplicate[i] = -1;
358         for (j = 0; j < i; j++)
359             if (sets[j] == sets[i])
360             {
361                 duplicate[i] = j;
362                 break;
363             }
364     }
365
366     tn = 0;
367     for (i = 0; sets[i]; i++) {
368         if (duplicate[i] >= 0)
369             continue;
370         off[i] = tn;
371         tn += sets[i]->num;
372     }
373
374     printf ("#define LEAF0       (%d * sizeof (FcLangCharSet))\n", nsets);
375     printf ("#define OFF0        (LEAF0 + %d * sizeof (FcCharLeaf))\n", tl);
376     printf ("#define NUM0        (OFF0 + %d * sizeof (uintptr_t))\n", tn);
377     printf ("#define SET(n)      (n * sizeof (FcLangCharSet) + offsetof (FcLangCharSet, charset))\n");
378     printf ("#define OFF(s,o)    (OFF0 + o * sizeof (uintptr_t) - SET(s))\n");
379     printf ("#define NUM(s,n)    (NUM0 + n * sizeof (FcChar16) - SET(s))\n");
380     printf ("#define LEAF(o,l)   (LEAF0 + l * sizeof (FcCharLeaf) - (OFF0 + o * sizeof (intptr_t)))\n");
381     printf ("#define fcLangCharSets (fcLangData.langCharSets)\n");
382     printf ("#define fcLangCharSetIndices (fcLangData.langIndices)\n");
383     printf ("#define fcLangCharSetIndicesInv (fcLangData.langIndicesInv)\n");
384     printf ("\n");
385     
386     printf ("static const struct {\n"
387             "    FcLangCharSet  langCharSets[%d];\n"
388             "    FcCharLeaf     leaves[%d];\n"
389             "    uintptr_t      leaf_offsets[%d];\n"
390             "    FcChar16       numbers[%d];\n"
391             "    FcChar%s       langIndices[%d];\n"
392             "    FcChar%s       langIndicesInv[%d];\n"
393             "} fcLangData = {\n",
394             nsets, tl, tn, tn,
395             nsets < 256 ? "8 " : "16", nsets, nsets < 256 ? "8 " : "16", nsets);
396         
397     /*
398      * Dump sets
399      */
400
401     printf ("{\n");
402     for (i = 0; sets[i]; i++)
403     {
404         int     j = duplicate[i];
405
406         if (j < 0)
407             j = i;
408
409         printf ("    { \"%s\", "
410                 " { FC_REF_CONSTANT, %d, OFF(%d,%d), NUM(%d,%d) } }, /* %d */\n",
411                 langs[i],
412                 sets[j]->num, i, off[j], i, off[j], i);
413     }
414     printf ("},\n");
415     
416     /*
417      * Dump leaves
418      */
419     printf ("{\n");
420     for (l = 0; l < tl; l++)
421     {
422         printf ("    { { /* %d */", l);
423         for (i = 0; i < 256/32; i++)
424         {
425             if (i % 4 == 0)
426                 printf ("\n   ");
427             printf (" 0x%08x,", leaves[l]->map[i]);
428         }
429         printf ("\n    } },\n");
430     }
431     printf ("},\n");
432
433     /*
434      * Dump leaves
435      */
436     printf ("{\n");
437     for (i = 0; sets[i]; i++)
438     {
439         int n;
440         
441         if (duplicate[i] >= 0)
442             continue;
443         printf ("    /* %s */\n", names[i]);
444         for (n = 0; n < sets[i]->num; n++)
445         {
446             if (n % 4 == 0)
447                 printf ("   ");
448             for (l = 0; l < tl; l++)
449                 if (leaves[l] == FcCharSetLeaf(sets[i], n))
450                     break;
451             if (l == tl)
452                 fatal (names[i], 0, "can't find leaf");
453             printf (" LEAF(%3d,%3d),", off[i], l);
454             if (n % 4 == 3)
455                 printf ("\n");
456         }
457         if (n % 4 != 0)
458             printf ("\n");
459     }
460     printf ("},\n");
461         
462
463     printf ("{\n");
464     for (i = 0; sets[i]; i++)
465     {
466         int n;
467         
468         if (duplicate[i] >= 0)
469             continue;
470         printf ("    /* %s */\n", names[i]);
471         for (n = 0; n < sets[i]->num; n++)
472         {
473             if (n % 8 == 0)
474                 printf ("   ");
475             printf (" 0x%04x,", FcCharSetNumbers (sets[i])[n]);
476             if (n % 8 == 7)
477                 printf ("\n");
478         }
479         if (n % 8 != 0)
480             printf ("\n");
481     }
482     printf ("},\n");
483
484     /* langIndices */
485     printf ("{\n");
486     for (i = 0; sets[i]; i++)
487     {
488         printf ("    %d, /* %s */\n", entries[i].id, names[i]);
489     }
490     printf ("},\n");
491
492     /* langIndicesInv */
493     printf ("{\n");
494     {
495         static int              entries_inv[MAX_LANG];
496         for (i = 0; sets[i]; i++)
497           entries_inv[entries[i].id] = i;
498         for (i = 0; sets[i]; i++)
499             printf ("    %d, /* %s */\n", entries_inv[i], names[entries_inv[i]]);
500     }
501     printf ("}\n");
502
503     printf ("};\n\n");
504
505     printf ("#define NUM_LANG_CHAR_SET  %d\n", i);
506     num_lang_set_map = (i + 31) / 32;
507     printf ("#define NUM_LANG_SET_MAP   %d\n", num_lang_set_map);
508     /*
509      * Dump indices with country codes
510      */
511     if (ncountry)
512     {
513         int     c;
514         int     ncountry_ent = 0;
515         printf ("\n");
516         printf ("static const FcChar32 fcLangCountrySets[][NUM_LANG_SET_MAP] = {\n");
517         for (c = 0; c < ncountry; c++)
518         {
519             i = country[c];
520             if (i >= 0)
521             {
522                 int lang = strchr (langs[i], '-') - langs[i];
523                 int d, k;
524
525                 for (k = 0; k < num_lang_set_map; k++)
526                     map[k] = 0;
527
528                 BitSet (map, i);
529                 for (d = c + 1; d < ncountry; d++)
530                 {
531                     int j = country[d];
532                     if (j >= 0 && !strncmp (langs[j], langs[i], lang + 1))
533                     {
534                         BitSet(map, j);
535                         country[d] = -1;
536                     }
537                 }
538                 printf ("    {");
539                 for (k = 0; k < num_lang_set_map; k++)
540                     printf (" 0x%08x,", map[k]);
541                 printf (" }, /* %*.*s */\n",
542                         lang, lang, langs[i]);
543                 ++ncountry_ent;
544             }
545         }
546         printf ("};\n\n");
547         printf ("#define NUM_COUNTRY_SET %d\n", ncountry_ent);
548     }
549     
550
551     /*
552      * Find ranges for each letter for faster searching
553      */
554     setRangeChar = 'a';
555     memset(setRangeStart, '\0', sizeof (setRangeStart));
556     memset(setRangeEnd, '\0', sizeof (setRangeEnd));
557     for (i = 0; sets[i]; i++)
558     {
559         char    c = names[i][0];
560         
561         while (setRangeChar <= c && c <= 'z')
562             setRangeStart[setRangeChar++ - 'a'] = i;
563     }
564     for (setRangeChar = 'a'; setRangeChar < 'z'; setRangeChar++)
565         setRangeEnd[setRangeChar - 'a'] = setRangeStart[setRangeChar+1-'a'] - 1;
566     setRangeEnd[setRangeChar - 'a'] = i - 1;
567     
568     /*
569      * Dump sets start/finish for the fastpath
570      */
571     printf ("\n");
572     printf ("static const FcLangCharSetRange  fcLangCharSetRanges[] = {\n");
573         printf ("\n");
574     for (setRangeChar = 'a'; setRangeChar <= 'z' ; setRangeChar++)
575     {
576         printf ("    { %d, %d }, /* %c */\n",
577                 setRangeStart[setRangeChar - 'a'],
578                 setRangeEnd[setRangeChar - 'a'], setRangeChar);
579     }
580     printf ("};\n\n");
581  
582     while (fgets (line, sizeof (line), stdin))
583         fputs (line, stdout);
584     
585     fflush (stdout);
586     exit (ferror (stdout));
587 }