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