ucs2any.c revision c6a6acfb
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] */
54static const char *my_name = NULL;
55
56#ifdef NEED_BASENAME
57static char *
58basename(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
69static void *
70zmalloc(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
82static void *
83zrealloc(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
94static char *
95zstrdup(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
111static void
112zstrcpy(char **dest, const char *source)
113{
114	if (*dest != NULL)
115		free(*dest);
116	*dest = zstrdup(source);
117}
118
119static void
120zquotedcpy(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
140static void
141zstrcat(char **dest, const char *source)
142{
143	size_t dest_size = 1;
144	size_t 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
153static void
154zstrtoupper(char *s)
155{
156	char *t;
157
158	for (t = s; *t != '\000'; t++)
159		*t = (char) 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
167typedef struct {
168	char *name;
169	int size;
170	int count;
171	void **values;
172	void *nv;
173} da_t;
174
175static da_t *
176da_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
190static void *
191da_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
204static int
205da_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
218static void
219da_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
248static void
249da_add_str(da_t *da, int key, const char *value)
250{
251	da_add(da, key, value?zstrdup(value):NULL);
252}
253
254static void
255da_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
267static void
268da_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 */
286static int
287read_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++] = (char) 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/*
331DEC VT100 graphics characters in the range 1-31 (as expected by
332some old xterm versions and a few other applications)
333*/
334#define decmap_size 31
335static 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
369static int
370is_control(int ucs)
371{
372	return ((ucs >= 0x00 && ucs <= 0x1f) ||
373		(ucs >= 0x7f && ucs <= 0x9f));
374}
375
376static int
377is_blockgraphics(int ucs)
378{
379	return ucs >= 0x2500 && ucs <= 0x25FF;
380}
381
382/* calculate the bounding box that covers both provided bounding boxes */
383typedef struct {
384	int cwidth;
385	int cheight;
386	int cxoff;
387	int cyoff;
388} bbx_t;
389
390static bbx_t *
391combine_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
417static void
418usage(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
447static int
448chars_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 */
460static const char *
461startswith(const char *string, const char *pattern)
462{
463	size_t 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
474int
475main(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 = (int) 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 = (int) 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				int 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 = (int) strtol(nextc, &endp, 10);
827				nextc = endp;
828				if (*nextc == '\0') goto bbxbad;
829				for (;isspace(*nextc);)
830					nextc++;
831				h = (int) strtol(nextc, &endp, 10);
832				nextc = endp;
833				if (*nextc == '\0') goto bbxbad;
834				for (;isspace(*nextc);)
835					nextc++;
836				x = (int) strtol(nextc, &endp, 10);
837				nextc = endp;
838				if (*nextc == '\0') goto bbxbad;
839				for (;isspace(*nextc);)
840					nextc++;
841				y = (int) 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}
951