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