resetting manifest requested domain to floor
[platform/upstream/font-util.git] / ucs2any.c
1 /*-
2  * Copyright (c) 2003 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Ben Collver <collver1@attbi.com>.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 /*
30  * This utility allows you to generate from an ISO10646-1 encoded
31  * BDF font other BDF fonts in any possible encoding. This way, you can
32  * derive from a single ISO10646-1 master font a whole set of 8-bit
33  * fonts in all ISO 8859 and various other encodings. (Hopefully
34  * a future XFree86 release will have a similar facility built into
35  * the server, which can reencode ISO10646-1 on the fly, because
36  * storing the same fonts in many different encodings is clearly
37  * a waste of storage capacity).
38 */
39
40 #include <ctype.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #if !defined(NEED_BASENAME) && !defined(Lynx)
44 #include <libgen.h>
45 #endif
46 #include <limits.h>
47 #include <stdarg.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52
53 /* global variable for argv[0] */
54 static const char *my_name = NULL;
55
56 #ifdef NEED_BASENAME
57 static char *
58 basename(char *pathname)
59 {
60         char    *ptr;
61
62         ptr = strrchr(pathname, '/');
63         return ((ptr == NULL) ? pathname : &ptr[1]);
64 }
65 #endif
66
67 /* "CLASS" "z" string and memory manipulation */
68
69 static void *
70 zmalloc(size_t size)
71 {
72         void *r;
73         r = malloc(size);
74         if (r == NULL) {
75                 perror(my_name);
76                 exit(errno);
77         }
78         memset(r, 0, size);
79         return r;
80 }
81
82 static void *
83 zrealloc(void *ptr, size_t size)
84 {
85         void *temp;
86         temp = realloc(ptr, size);
87         if (temp == NULL) {
88                 perror(my_name);
89                 exit(errno);
90         }
91         return temp;
92 }
93
94 static char *
95 zstrdup(const char *str)
96 {
97         char *retval;
98
99         if (str == NULL) {
100                 fprintf(stderr, "%s: zstrdup(NULL)\n", my_name);
101                 exit(1);
102         }
103         retval = strdup(str);
104         if (retval == NULL) {
105                 perror(my_name);
106                 exit(errno);
107         }
108         return retval;
109 }
110
111 static void 
112 zstrcpy(char **dest, const char *source)
113 {
114         if (*dest != NULL)
115                 free(*dest);
116         *dest = zstrdup(source);
117 }
118
119 static void 
120 zquotedcpy(char **dest, const char *source)
121 {
122         const char *start, *end;
123
124         if (*dest != NULL)
125                 free(*dest);
126         *dest = NULL;
127         start = source;
128         if (*start == '"') {
129                 start = source+1;
130                 end = strrchr(start, '"');
131                 if (!end) return;
132                 *dest = zmalloc(end-start+1);
133                 strncpy(*dest, start, end-start);
134                 (*dest)[end-start] = '\0';
135         } else {
136                 *dest = zstrdup(source);
137         }
138 }
139
140 static void 
141 zstrcat(char **dest, const char *source)
142 {
143         int dest_size = 1;
144         int source_size;
145
146         if (*dest != NULL)
147                 dest_size = strlen(*dest) + 1;
148         source_size = strlen(source);
149         *dest = zrealloc(*dest, dest_size + source_size);
150         strcpy(*dest + dest_size - 1, source);
151 }
152
153 static void 
154 zstrtoupper(char *s)
155 {
156         char *t;
157
158         for (t = s; *t != '\000'; t++)
159                 *t = toupper(*t);
160 }
161
162 #define zs_true(x)      (x != NULL && strcmp(x, "0") != 0)
163 #define zi_true(x)      (x == 1)
164
165 /* "CLASS" "dynamic array" */
166
167 typedef struct {
168         char *name;
169         int size;
170         int count;
171         void **values;
172         void *nv;
173 } da_t;
174
175 static da_t *
176 da_new(const char *name)
177 {
178         da_t *da;
179
180         da = zmalloc(sizeof(da_t));
181         da->size = 0;
182         da->count = 0;
183         da->values = NULL;
184         da->nv = NULL;
185         da->name = NULL;
186         zstrcpy(&(da->name), name);
187         return da;
188 }
189
190 static void *
191 da_fetch(da_t *da, int key)
192 {
193         void *r = NULL;
194
195         if (key >= 0 && key < da->size && da->values[key] != NULL)
196                 r = da->values[key];
197         else
198                 if (key == -1 && da->nv != NULL)
199                         r = da->nv;
200
201         return r;
202 }
203
204 static int 
205 da_fetch_int(da_t *da, int key)
206 {
207         int *t;
208         int r = -1;
209         t = da_fetch(da, key);
210         if (t != NULL)
211                 r = *t;
212         return r;
213 }
214
215 #define da_fetch_str(a,k)       \
216         (char *)da_fetch(a,k)
217
218 static void 
219 da_add(da_t *da, int key, void *value)
220 {
221         int i = da->size;
222         if (key >= 0) {
223                 if (key >= da->size) {
224                         da->size = key + 1;
225                         da->values = zrealloc(da->values,
226                                 da->size * sizeof(void *));
227                         for (; i < da->size; i++)
228                                 da->values[i] = NULL;
229                 }
230                 if (da->values[key] != NULL) {
231                         free(da->values[key]);
232                 } else {
233                         if (value == NULL) {
234                                 if (da->count > 0)
235                                         da->count--;
236                         } else {
237                                 da->count++;
238                         }
239                 }
240                 da->values[key] = value;
241         } else if (key == -1) {
242                 if (da->nv != NULL)
243                         free(da->nv);
244                 da->nv = value;
245         }
246 }
247
248 static void 
249 da_add_str(da_t *da, int key, const char *value)
250 {
251         da_add(da, key, value?zstrdup(value):NULL);
252 }
253
254 static void 
255 da_add_int(da_t *da, int key, int value)
256 {
257         int *v;
258
259         v = zmalloc(sizeof(int));
260         *v = value;
261         da_add(da, key, v);
262 }
263
264 #define da_count(da) (da->count)
265 #define da_size(da) (da->size)
266
267 static void 
268 da_clear(da_t *da)
269 {
270         int i;
271
272         for (i = da->size; i; i--)
273                 free(da->values[i]);
274         if (da->values != NULL)
275                 free(da->values);
276         da->size = 0;
277         da->count = 0;
278         da->values = NULL;
279 }
280
281 /* "CLASS" file input */
282
283 #define TYPICAL_LINE_SIZE (80)
284
285 /* read a line and strip trailing whitespace */
286 static int 
287 read_line(FILE *fp, char **buffer)
288 {
289         int buffer_size = TYPICAL_LINE_SIZE;
290         int eof = 0;
291         int position = 0;
292         int c;
293
294         *buffer = zmalloc(TYPICAL_LINE_SIZE);
295         (*buffer)[0] = '\0';
296
297         if ((c = getc(fp)) == EOF)
298                 eof = 1;
299
300         while (c != '\n' && !eof) {
301                 if (position + 1 >= buffer_size) {
302                         buffer_size = buffer_size * 2 + 1;
303                         *buffer = zrealloc(*buffer, buffer_size);
304                 }
305                 (*buffer)[position++] = c;
306                 (*buffer)[position] = '\0';
307                 c = getc(fp);
308                 if (c == EOF)
309                         eof = 1;
310         }
311
312         if (eof) {
313                 free(*buffer);
314                 *buffer = NULL;
315                 return 0;
316         }
317
318         while (position > 1) {
319                 position--;
320                 if (!isspace((*buffer)[position]))
321                         break;
322                 (*buffer)[position] = '\0';
323         }
324
325         return 1;
326 }
327
328 /* BEGIN */
329
330 /*
331 DEC VT100 graphics characters in the range 1-31 (as expected by
332 some old xterm versions and a few other applications)
333 */
334 #define decmap_size 31
335 static int decmap[decmap_size] = {
336         0x25C6, /* BLACK DIAMOND */
337         0x2592, /* MEDIUM SHADE */
338         0x2409, /* SYMBOL FOR HORIZONTAL TABULATION */
339         0x240C, /* SYMBOL FOR FORM FEED */
340         0x240D, /* SYMBOL FOR CARRIAGE RETURN */
341         0x240A, /* SYMBOL FOR LINE FEED */
342         0x00B0, /* DEGREE SIGN */
343         0x00B1, /* PLUS-MINUS SIGN */
344         0x2424, /* SYMBOL FOR NEWLINE */
345         0x240B, /* SYMBOL FOR VERTICAL TABULATION */
346         0x2518, /* BOX DRAWINGS LIGHT UP AND LEFT */
347         0x2510, /* BOX DRAWINGS LIGHT DOWN AND LEFT */
348         0x250C, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */
349         0x2514, /* BOX DRAWINGS LIGHT UP AND RIGHT */
350         0x253C, /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
351         0x23BA, /* HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
352         0x23BB, /* HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
353         0x2500, /* BOX DRAWINGS LIGHT HORIZONTAL */
354         0x23BC, /* HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
355         0x23BD, /* HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
356         0x251C, /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
357         0x2524, /* BOX DRAWINGS LIGHT VERTICAL AND LEFT */
358         0x2534, /* BOX DRAWINGS LIGHT UP AND HORIZONTAL */
359         0x252C, /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
360         0x2502, /* BOX DRAWINGS LIGHT VERTICAL */
361         0x2264, /* LESS-THAN OR EQUAL TO */
362         0x2265, /* GREATER-THAN OR EQUAL TO */
363         0x03C0, /* GREEK SMALL LETTER PI */
364         0x2260, /* NOT EQUAL TO */
365         0x00A3, /* POUND SIGN */
366         0x00B7  /* MIDDLE DOT */
367 };
368
369 static int 
370 is_control(int ucs)
371 {
372         return ((ucs >= 0x00 && ucs <= 0x1f) ||
373                 (ucs >= 0x7f && ucs <= 0x9f));
374 }
375
376 static int 
377 is_blockgraphics(int ucs)
378 {
379         return ucs >= 0x2500 && ucs <= 0x25FF;
380 }
381
382 /* calculate the bounding box that covers both provided bounding boxes */
383 typedef struct {
384         int cwidth;
385         int cheight;
386         int cxoff;
387         int cyoff;
388 } bbx_t;
389
390 static bbx_t *
391 combine_bbx(int awidth, int aheight, int axoff, int ayoff,
392         int cwidth, int cheight, int cxoff, int cyoff, bbx_t *r)
393 {
394         r->cwidth = cwidth;
395         r->cheight = cheight;
396         r->cxoff = cxoff;
397         r->cyoff = cyoff;
398
399         if (axoff < r->cxoff) {
400                 r->cwidth += r->cxoff - axoff;
401                 r->cxoff = axoff;
402         }
403         if (ayoff < r->cyoff) {
404                 r->cheight += r->cyoff - ayoff;
405                 r->cyoff = ayoff;
406         }
407         if (awidth + axoff > r->cwidth + r->cxoff) {
408                 r->cwidth = awidth + axoff - r->cxoff;
409         }
410         if (aheight + ayoff > r->cheight + r->cyoff) {
411                 r->cheight = aheight + ayoff - r->cyoff;
412         }
413
414         return r;
415 }
416
417 static void 
418 usage(void) {
419         printf("%s", "\n"
420 "Usage: ucs2any [+d|-d] <source-name> { <mapping-file> <registry-encoding> }\n"
421 "\n"
422 "where\n"
423 "\n"
424 "   +d                   put DEC VT100 graphics characters in the C0 range\n"
425 "                        (default for upright charcell fonts)\n"
426 "\n"
427 "   -d                   do not put DEC VT100 graphics characters in the\n"
428 "                        C0 range (default for all other font types)\n"
429 "\n"
430 "   <source-name>        is the name of an ISO10646-1 encoded BDF file\n"
431 "\n"
432 "   <mapping-file>       is the name of a character set table like those on\n"
433 "                        <ftp://ftp.unicode.org/Public/MAPPINGS/>\n"
434 "\n"
435 "   <registry-encoding>  are the CHARSET_REGISTRY and CHARSET_ENCODING\n"
436 "                        field values for the font name (XLFD) of the\n"
437 "                        target font, separated by a hyphen\n"
438 "\n"
439 "Example:\n"
440 "\n"
441 "   ucs2any 6x13.bdf 8859-1.TXT iso8859-1 8859-2.TXT iso8859-2\n"
442 "\n"
443 "will generate the files 6x13-iso8859-1.bdf and 6x13-iso8859-2.bdf\n"
444 "\n");
445 }
446
447 static int 
448 chars_compare(const void *aa, const void *bb)
449 {
450         int a = *(const int *)aa;
451         int b = *(const int *)bb;
452
453         return a - b;
454 }
455
456 /*
457  * Return != 0 if "string" starts with "pattern" followed by whitespace.
458  * If it does, return a pointer to the first non space char.
459  */
460 static const char *
461 startswith(const char *string, const char *pattern)
462 {
463         int l = strlen(pattern);
464
465         if (strlen(string) <= l) return NULL;
466         if (strncmp(string, pattern, l) != 0) return NULL;
467         string += l;
468         if (!isspace(*string)) return NULL;
469         while (isspace(*string))
470                 string++;
471         return string;
472 }
473
474 int 
475 main(int argc, char *argv[])
476 {
477         int ai = 1;
478         int dec_chars = -1;
479         char *fsource = NULL;
480         FILE *fsource_fp;
481         int properties;
482         int default_char;
483         char *l = NULL;
484         char *t = NULL;
485         const char *nextc = NULL;
486         char *startfont = NULL;
487         char *slant = NULL;
488         char *spacing = NULL;
489         char *sc = NULL;
490         int code = -1;
491         da_t *startchar;
492         da_t *my_char;
493         char *fmap = NULL;
494         char *registry = NULL;
495         char *encoding = NULL;
496         char *fontname = NULL;
497         FILE *fmap_fp;
498         da_t *map;
499         da_t *headers;
500         int nextheader = -1;
501         int default_char_index = -1;
502         int startproperties_index = -1;
503         int fontname_index = -1;
504         int charset_registry_index = -1;
505         int slant_index = -1;
506         int spacing_index = -1;
507         int charset_encoding_index = -1;
508         int fontboundingbox_index = -1;
509         int target;
510         int ucs;
511         int i;
512         int j;
513         int *chars = NULL;
514         bbx_t bbx;
515         char *fout = NULL;
516         FILE *fout_fp;
517         int k;
518         char *registry_encoding = NULL;
519
520         my_name = argv[0];
521         bbx.cheight = bbx.cxoff = bbx.cyoff = -1;
522
523         startchar = da_new("startchar");
524         my_char = da_new("my_char");
525         map = da_new("map");
526         headers = da_new("headers");
527
528         if (argc < 2) {
529                 usage();
530                 exit(0);
531         }
532
533         /* check options */
534         if (strcmp(argv[ai], "+d") == 0) {
535                 ai++;
536                 dec_chars = 1;
537         } else if (strcmp(argv[ai], "-d") == 0) {
538                 ai++;
539                 dec_chars = 0;
540         }
541         if (ai >= argc) {
542                 usage();
543                 exit(0);
544         }
545
546         /* open and read source file */
547         fsource = argv[ai];
548         fsource_fp = fopen(fsource, "r");
549         if (fsource_fp == NULL) {
550                 fprintf(stderr, "%s: Can't read file '%s': %s!\n", my_name,
551                         fsource, strerror(errno));
552                 exit(1);
553         }
554
555         /* read header */
556         properties = 0;
557         default_char = 0;
558         while (read_line(fsource_fp, &l)) {
559                 if (startswith(l, "CHARS"))
560                         break;
561                 if (startswith(l, "STARTFONT")) {
562                         zstrcpy(&startfont, l);
563                 } else if (startswith(l, "_XMBDFED_INFO") ||
564                         startswith(l, "XFREE86_GLYPH_RANGES"))
565                 {
566                         properties--;
567                 } else if ((nextc = startswith(l, "DEFAULT_CHAR")) != NULL)
568                 {
569                         default_char = atoi(nextc);
570                         default_char_index = ++nextheader;
571                         da_add_str(headers, default_char_index, NULL);
572                 } else {
573                         if ((nextc = startswith(l, "STARTPROPERTIES")) != NULL)
574                         {
575                                 properties = atoi(nextc);
576                                 startproperties_index = ++nextheader;
577                                 da_add_str(headers, startproperties_index, NULL);
578                         } else if ((nextc = startswith(l, "FONT")) != NULL)
579                         {
580                                 char * term;
581                                 /* slightly simplistic check ... */
582                                 zquotedcpy(&fontname, nextc);
583                                 if ((term = strstr(fontname, "-ISO10646-1")) == NULL) {
584                                         fprintf(stderr,
585                                                 "%s: FONT name in '%s' is '%s' and not '*-ISO10646-1'!\n",
586                                                 my_name, fsource, fontname);
587                                         exit(1);
588                                 }
589                                 *term = '\0';
590                                 fontname_index = ++nextheader;
591                                 da_add_str(headers, fontname_index, NULL);
592                         } else if ((nextc = startswith(l, "CHARSET_REGISTRY")) != NULL)
593                         {
594                                 if (strcmp(nextc, "\"ISO10646\"") != 0) {
595                                         fprintf(stderr,
596                                                 "%s: CHARSET_REGISTRY in '%s' is '%s' and not 'ISO10646'!\n",
597                                                 my_name, fsource, nextc);
598                                         exit(1);
599                                 }
600                                 charset_registry_index = ++nextheader;
601                                 da_add_str(headers, charset_registry_index, NULL);
602                         } else if ((nextc = startswith(l, "CHARSET_ENCODING")) != NULL)
603                         {
604                                 if (strcmp(nextc, "\"1\"") != 0) {
605                                         fprintf(stderr,
606                                                 "%s: CHARSET_ENCODING in '%s' is '%s' and not '1'!\n",
607                                                 my_name, fsource, nextc);
608                                         exit(1);
609                                 }
610                                 charset_encoding_index = ++nextheader;
611                                 da_add_str(headers, charset_encoding_index, NULL);
612                         } else if (startswith(l, "FONTBOUNDINGBOX")) {
613                                 fontboundingbox_index = ++nextheader;
614                                 da_add_str(headers, fontboundingbox_index, NULL);
615                         } else if ((nextc = startswith(l, "SLANT")) != NULL)
616                         {
617                                 zquotedcpy(&slant, nextc);
618                                 slant_index = ++nextheader;
619                                 da_add_str(headers, slant_index, NULL);
620                         } else if ((nextc = startswith(l, "SPACING")) != NULL)
621                         {
622                                 zquotedcpy(&spacing, nextc);
623                                 zstrtoupper(spacing);
624                                 spacing_index = ++nextheader;
625                                 da_add_str(headers, spacing_index, NULL);
626                         } else if ((nextc = startswith(l, "COMMENT")) != NULL) {
627                                 if (strncmp(nextc, "$Id: ", 5)==0) {
628                                         char *header = NULL;
629                                         char *id = NULL, *end = NULL;
630                                         id = zstrdup(nextc + 5);
631                                         end = strrchr(id, '$');
632                                         if (end) *end = '\0';
633                                         zstrcpy(&header, "COMMENT Derived from ");
634                                         zstrcat(&header, id);
635                                         zstrcat(&header, "\n");
636                                         free(id);
637                                         da_add_str(headers, ++nextheader, header);
638                                         free(header);
639                                 } else {
640                                         da_add_str(headers, ++nextheader, l);
641                                 }
642                         } else {
643                                 da_add_str(headers, ++nextheader, l);
644                         }
645                 }
646                 free(l);
647         }
648
649         if (startfont == NULL) {
650                 fprintf(stderr, "%s: No STARTFONT line found in '%s'!\n",
651                         my_name, fsource);
652                 exit(1);
653         }
654
655         /* read characters */
656         while (read_line(fsource_fp, &l)) {
657                 if (startswith(l, "STARTCHAR")) {
658                         zstrcpy(&sc, l);
659                         zstrcat(&sc, "\n");
660                         code = -1;
661                 } else if ((nextc = startswith(l, "ENCODING")) != NULL) {
662                         code = atoi(nextc);
663                         da_add_str(startchar, code, sc);
664                         da_add_str(my_char, code, "");
665                 } else if (strcmp(l, "ENDFONT")==0) {
666                         code = -1;
667                         zstrcpy(&sc, "STARTCHAR ???\n");
668                 } else {
669                         zstrcpy(&t, da_fetch_str(my_char, code));
670                         zstrcat(&t, l);
671                         zstrcat(&t, "\n");
672                         da_add_str(my_char, code, t);
673                         if (strcmp(l, "ENDCHAR")==0) {
674                                 code = -1;
675                                 zstrcpy(&sc, "STARTCHAR ???\n");
676                         }
677                 }
678                 free(l);
679         }
680
681         fclose(fsource_fp);
682
683         ai++;
684         while (ai < argc) {
685                 zstrcpy(&fmap, argv[ai]);
686                 i = ai + 1;
687                 if (i < argc) {
688                         char *temp = NULL;
689                         char * hyphen = strchr(argv[i], '-');
690                         if (!hyphen || strchr(hyphen+1, '-') != NULL) {
691                                 fprintf(stderr,
692                                         "%s: Argument registry-encoding '%s' not in expected format!\n",
693                                         my_name, i < argc ? fmap : "");
694                                 exit(1);
695                         }
696                         temp = zstrdup(argv[i]);
697                         hyphen = strchr(temp, '-');
698                         if (hyphen) *hyphen = 0;
699                         zstrcpy(&registry, temp);
700                         zstrcpy(&encoding, hyphen+1);
701                         free(temp);
702                 } else {
703                         fprintf(stderr, "map file argument \"%s\" needs a "
704                             "coresponding registry-encoding argument\n", fmap);
705                         exit(0);
706                 }
707
708                 ai++;
709                 ai++;
710
711                 /* open and read source file */
712                 fmap_fp = fopen(fmap, "r");
713                 if (fmap_fp == NULL) {
714                         fprintf(stderr,
715                                 "%s: Can't read mapping file '%s': %s!\n",
716                                 my_name, fmap, strerror(errno));
717                         exit(1);
718                 }
719
720                 da_clear(map);
721
722                 for (;read_line(fmap_fp, &l); free(l)) {
723                         char *p, *endp;
724
725                         for (p = l; isspace(p[0]); p++)
726                                 ;
727                         if (p[0] == '\0' || p[0] == '#')
728                                 continue;
729                         if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
730                                 target = strtol(p+2, &endp, 16);
731                                 if (*endp == '\0') goto bad;
732                                 p = endp;
733                         } else
734                                 goto bad;
735                         for (; isspace(p[0]); p++)
736                                 ;
737                         if (p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
738                                 ucs = strtol(p+2, &endp, 16);
739                                 if (*endp == '\0') goto bad;
740                         } else
741                                 goto bad;
742
743                         if (!is_control(ucs)) {
744                                 if (zs_true(da_fetch_str(startchar, ucs)))
745                                 {
746                                         da_add_int(map, target, ucs);
747                                 } else {
748                                         if (!((is_blockgraphics(ucs) &&
749                                                 strcmp(slant, "R") != 0) ||
750                                                 (ucs >= 0x200e &&
751                                                 ucs <= 0x200f)))                                                        {
752                                                 fprintf(stderr,
753                                                         "No glyph for character U+%04X (0x%02x) available.\n",
754                                                         ucs, target);
755                                         }
756                                 }
757                         }
758                         continue;
759                 bad:
760                         fprintf(stderr, "Unrecognized line in '%s':\n%s\n", fmap, l);
761                 }
762                 fclose(fmap_fp);
763
764                 /* add default character */
765                 if (!zi_true(da_fetch_int(map, 0))) {
766                         if (zs_true(da_fetch_str(startchar, default_char))) {
767                                 da_add_int(map, 0, default_char);
768                                 da_add_str(startchar, default_char,
769                                         "STARTCHAR defaultchar\n");
770                         } else {
771                                 fprintf(stderr, "%s",
772                                         "No default character defined.\n");
773                         }
774                 }
775
776                 if (dec_chars == 1 ||
777                         (dec_chars == -1 && strcmp(slant, "R") == 0 &&
778                         strcmp(spacing, "C") == 0))
779                 {
780                         /* add DEC VT100 graphics characters in the range 1-31
781                            (as expected by some old xterm versions) */
782                         for (i = 0; i < decmap_size; i++) {
783                                 if (zs_true(da_fetch_str(startchar, decmap[i])))
784                                 {
785                                         da_add_int(map, i + 1, decmap[i]);
786                                 }
787                         }
788                 }
789
790                 /* list of characters that will be written out */
791                 j = da_count(map);
792                 if (j < 0) {
793                         fprintf(stderr,
794                                 "No characters found for %s-%s.\n",
795                                 registry, encoding);
796                         continue;
797                 }
798                 if (chars != NULL)
799                         free(chars);
800                 chars = zmalloc(j * sizeof(int));
801                 memset(chars, 0, j * sizeof(int));
802                 for (k = 0, i = 0; k < da_count(map) && i < da_size(map); i++) {
803                         if (da_fetch(map, i) != NULL)
804                                 chars[k++] = i;
805                 }
806                 qsort(chars, j, sizeof(int), chars_compare);
807
808                 /* find overall font bounding box */
809                 bbx.cwidth = -1;
810                 for (i = 0; i < j; i++) {
811                         ucs = da_fetch_int(map, chars[i]);
812                         zstrcpy(&t, da_fetch_str(my_char, ucs));
813                         if ((nextc = startswith(t, "BBX")) != NULL 
814                             || (nextc = strstr(t, "\nBBX")) != NULL)
815                         {
816                                 char *endp;
817                                 long w, h, x, y;
818
819                                 if (*nextc == '\n') {
820                                         nextc += 4;
821                                         while (isspace(*nextc))
822                                                 nextc++;
823                                 }
824                                 for (;isspace(*nextc);)
825                                         nextc++;
826                                 w = strtol(nextc, &endp, 10);
827                                 nextc = endp;
828                                 if (*nextc == '\0') goto bbxbad;
829                                 for (;isspace(*nextc);)
830                                         nextc++;
831                                 h = strtol(nextc, &endp, 10);
832                                 nextc = endp;
833                                 if (*nextc == '\0') goto bbxbad;
834                                 for (;isspace(*nextc);)
835                                         nextc++;
836                                 x = strtol(nextc, &endp, 10);
837                                 nextc = endp;
838                                 if (*nextc == '\0') goto bbxbad;
839                                 for (;isspace(*nextc);)
840                                         nextc++;
841                                 y = strtol(nextc, &endp, 10);
842                                 if (bbx.cwidth == -1) {
843                                         bbx.cwidth = w;
844                                         bbx.cheight = h;
845                                         bbx.cxoff = x;
846                                         bbx.cyoff = y;
847                                 } else {
848                                         combine_bbx(bbx.cwidth, bbx.cheight,
849                                                 bbx.cxoff, bbx.cyoff,
850                                                 w, h, x, y, &bbx);
851                                 }
852                                 continue;
853                         bbxbad:
854                                 fprintf(stderr, "Unparsable BBX found for U+%04x!\n", ucs);
855                         } else {
856                                 fprintf(stderr,
857                                         "Warning: No BBX found for U+%04X!\n",
858                                         ucs);
859                         }
860                 }
861
862                 if (!registry) registry = zstrdup("");
863                 if (!encoding) encoding = zstrdup("");
864
865                 /* generate output file name */
866                 zstrcpy(&registry_encoding, "-");
867                 zstrcat(&registry_encoding, registry);
868                 zstrcat(&registry_encoding, "-");
869                 zstrcat(&registry_encoding, encoding);
870
871                 {
872                         char * p = strstr(fsource, ".bdf");
873                         if (p) {
874                                 zstrcpy(&fout, fsource);
875                                 p = strstr(fout, ".bdf");
876                                 *p = 0;
877                                 zstrcat(&fout, registry_encoding);
878                                 zstrcat(&fout, ".bdf");
879                         } else {
880                                 zstrcpy(&fout, fsource);
881                                 zstrcat(&fout, registry_encoding);
882                         }
883                 }
884
885                 /* remove path prefix */
886                 zstrcpy(&t, basename(fout));
887                 zstrcpy(&fout, t);
888
889                 /* write new BDF file */
890                 fprintf(stderr, "Writing %d characters into file '%s'.\n",
891                         j, fout);
892                 fout_fp = fopen(fout, "w");
893                 if (fout_fp == NULL) {
894                         fprintf(stderr, "%s: Can't write file '%s': %s!\n",
895                                 my_name, fout, strerror(errno));
896                         exit(1);
897                 }
898
899                 fprintf(fout_fp, "%s\n", startfont);
900                 fprintf(fout_fp, "%s",
901                         "COMMENT AUTOMATICALLY GENERATED FILE. DO NOT EDIT!\n");
902                 fprintf(fout_fp,
903                         "COMMENT Generated with 'ucs2any %s %s %s-%s'\n",
904                         fsource, fmap, registry, encoding);
905                 fprintf(fout_fp, "%s",
906                         "COMMENT from an ISO10646-1 encoded source BDF font.\n");
907                 fprintf(fout_fp, "%s",
908                         "COMMENT ucs2any by Ben Collver <collver1@attbi.com>, 2003, based on\n");
909                 fprintf(fout_fp, "%s",
910                         "COMMENT ucs2any.pl by Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/>, 2000.\n");
911
912                 for (i = 0; i <= nextheader; i++) {
913                         if (i == default_char_index)
914                                 fprintf(fout_fp, "DEFAULT_CHAR %d\n", default_char);
915                         else if (i == startproperties_index)
916                                 fprintf(fout_fp, "STARTPROPERTIES %d\n", properties);
917                         else if (i == fontname_index) {
918                                 fprintf(fout_fp, "FONT %s%s\n", fontname, registry_encoding);
919                         }
920                         else if (i == charset_registry_index)
921                                 fprintf(fout_fp, "CHARSET_REGISTRY \"%s\"\n", registry);
922                         else if (i == slant_index)
923                                 fprintf(fout_fp, "SLANT \"%s\"\n", slant);
924                         else if (i == charset_encoding_index)
925                                 fprintf(fout_fp, "CHARSET_ENCODING \"%s\"\n", encoding);
926                         else if (i == fontboundingbox_index)
927                                 fprintf(fout_fp, "FONTBOUNDINGBOX %d %d %d %d\n", bbx.cwidth, bbx.cheight, bbx.cxoff, bbx.cyoff);
928                         else if (i == spacing_index)
929                                 fprintf(fout_fp, "SPACING \"%s\"\n", spacing);
930                         else
931                                 fprintf(fout_fp, "%s\n", da_fetch_str(headers, i));
932                 }
933
934                 fprintf(fout_fp, "CHARS %d\n", j);
935
936                 /* Write characters */
937                 for (i = 0; i < j; i++) {
938                         ucs = da_fetch_int(map, chars[i]);
939                         fprintf(fout_fp, "%s", da_fetch_str(startchar,
940                                 ucs));
941                         fprintf(fout_fp, "ENCODING %d\n", chars[i]);
942                         fprintf(fout_fp, "%s", da_fetch_str(my_char,
943                                 ucs));
944                 }
945                 fprintf(fout_fp, "%s", "ENDFONT\n");
946                 fclose(fout_fp);
947         }
948
949         exit(0);
950 }