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