1 /*
2  * $LynxId: makeuctb.c,v 1.48 2013/07/21 00:42:39 tom Exp $
3  *
4  *  makeuctb.c, derived from conmakehash.c   - kw
5  *
6  *    Original comments from conmakehash.c:
7  *
8  *  Create arrays for initializing the kernel folded tables (using a hash
9  *  table turned out to be to limiting...)  Unfortunately we can't simply
10  *  preinitialize the tables at compile time since kfree() cannot accept
11  *  memory not allocated by kmalloc(), and doing our own memory management
12  *  just for this seems like massive overkill.
13  *
14  *  Copyright (C) 1995 H. Peter Anvin
15  *
16  *  This program is a part of the Linux kernel, and may be freely
17  *  copied under the terms of the GNU General Public License (GPL),
18  *  version 2, or at your option any later version.
19  */
20 
21 #ifndef HAVE_CONFIG_H
22 /* override HTUtils.h fallbacks for cross-compiling */
23 #undef HAVE_LSTAT
24 #undef NO_FILIO_H
25 #define HAVE_LSTAT 1
26 #define NO_FILIO_H 1
27 #endif
28 
29 #define DONT_USE_GETTEXT
30 #define DONT_USE_SOCKS5
31 #include <UCDefs.h>
32 #include <UCkd.h>
33 
34 /*
35  *  Don't try to use LYexit() since this is a standalone file.
36  */
37 #ifdef exit
38 #undef exit
39 #endif /* exit */
40 
41 #include <stdlib.h>
42 #include <string.h>
43 #include <ctype.h>
44 
45 #define MAX_FONTLEN 256
46 
47 /*
48  *  We don't deal with UCS4 here. - KW
49  */
50 typedef u16 unicode;
51 
52 static FILE *chdr = 0;
53 
54 /*
55  * Since we may be writing the formatted file to stdout, ensure that we flush
56  * everything before leaving, since some old (and a few not-so-old) platforms
57  * do not properly implement POSIX 'exit()'.
58  */
59 static void done(int code) GCC_NORETURN;
60 
done(int code)61 static void done(int code)
62 {
63     if (chdr != 0) {
64 	fflush(chdr);
65 	fclose(chdr);
66     }
67     fflush(stderr);
68     exit(code);
69 }
70 
usage(void)71 static void usage(void)
72 {
73     static const char *tbl[] =
74     {
75 	"Usage: makeuctb [parameters]",
76 	"",
77 	"Utility to convert .tbl into .h files for Lynx compilation.",
78 	"",
79 	"Parameters (all are optional):",
80 	"  1: the input file (normally {filename}.tbl, but \"-\" for stdin",
81 	"  2: the output file (normally {filename}.tbl but \"-\" for stdout",
82 	"  3: charset mime name",
83 	"  4: charset display name"
84     };
85     unsigned n;
86 
87     for (n = 0; n < TABLESIZE(tbl); n++) {
88 	fprintf(stderr, "%s\n", tbl[n]);
89     };
90     done(EX_USAGE);
91 }
92 
93 #ifdef USE_ASCII_CTYPES
ascii_tolower(int i)94 static int ascii_tolower(int i)
95 {
96     if (91 > i && i > 64)
97 	return (i + 32);
98     else
99 	return i;
100 }
101 #endif
102 
103 /* copied from HTString.c, not everybody has strncasecmp */
strncasecomp(const char * a,const char * b,int n)104 int strncasecomp(const char *a, const char *b, int n)
105 {
106     const char *p;
107     const char *q;
108 
109     for (p = a, q = b;; p++, q++) {
110 	int diff;
111 
112 	if (p == (a + n))
113 	    return 0;		/*   Match up to n characters */
114 	if (!(*p && *q))
115 	    return (*p - *q);
116 	diff = TOLOWER(*p) - TOLOWER(*q);
117 	if (diff)
118 	    return diff;
119     }
120     /*NOTREACHED */
121 }
122 
getunicode(char ** p0)123 static int getunicode(char **p0)
124 {
125     char *p = *p0;
126 
127     while (*p == ' ' || *p == '\t')
128 	p++;
129 
130     if (*p == '-') {
131 	return -2;
132     } else if (*p != 'U' || p[1] != '+' ||
133 	       !isxdigit(UCH(p[2])) ||
134 	       !isxdigit(UCH(p[3])) ||
135 	       !isxdigit(UCH(p[4])) ||
136 	       !isxdigit(UCH(p[5])) ||
137 	       isxdigit(UCH(p[6]))) {
138 	return -1;
139     }
140     *p0 = p + 6;
141     return (int) strtol((p + 2), 0, 16);
142 }
143 
144 /*
145  *  Massive overkill, but who cares?
146  */
147 static unicode unitable[MAX_FONTLEN][255];
148 static int unicount[MAX_FONTLEN];
149 
150 static struct unimapdesc_str themap_str =
151 {0, NULL, 0, 0};
152 
153 static const char *tblname;
154 static const char *hdrname;
155 
156 static int RawOrEnc = 0;
157 static int Raw_found = 0;	/* whether explicit R directive found */
158 static int CodePage = 0;
159 
160 #define MAX_UNIPAIRS 4500
161 
addpair_str(char * str,int un)162 static void addpair_str(char *str, int un)
163 {
164     int i = 0;
165 
166     if (un <= 0xfffe) {
167 	if (!themap_str.entry_ct) {
168 	    /*
169 	     *  Initialize the map for replacement strings.
170 	     */
171 	    themap_str.entries = (struct unipair_str *) malloc(MAX_UNIPAIRS
172 							       * sizeof(struct unipair_str));
173 
174 	    if (!themap_str.entries) {
175 		fprintf(stderr,
176 			"%s: Out of memory\n", tblname);
177 		done(EX_DATAERR);
178 	    }
179 	} else {
180 	    /*
181 	     *  Check that it isn't a duplicate.
182 	     */
183 	    for (i = 0; i < themap_str.entry_ct; i++) {
184 		if (themap_str.entries[i].unicode == un) {
185 		    themap_str.entries[i].replace_str = str;
186 		    return;
187 		}
188 	    }
189 	}
190 
191 	/*
192 	 *  Add to list.
193 	 */
194 	if (themap_str.entry_ct > MAX_UNIPAIRS - 1) {
195 	    fprintf(stderr,
196 		    "ERROR: Only %d unicode replacement strings permitted!\n",
197 		    MAX_UNIPAIRS);
198 	    done(EX_DATAERR);
199 	}
200 	themap_str.entries[themap_str.entry_ct].unicode = (u16) un;
201 	themap_str.entries[themap_str.entry_ct].replace_str = str;
202 	themap_str.entry_ct++;
203     }
204     /* otherwise: ignore */
205 }
206 
addpair(int fp,int un)207 static void addpair(int fp, int un)
208 {
209     int i;
210 
211     if (!Raw_found) {		/* enc not (yet) explicitly given with 'R' */
212 	if (fp >= 128) {
213 	    if (RawOrEnc != UCT_ENC_8BIT && RawOrEnc <= UCT_ENC_8859) {
214 		if (fp < 160) {	/* cannot be 8859 */
215 		    RawOrEnc = UCT_ENC_8BIT;
216 		} else if (fp != 160 && fp != 173) {
217 		    RawOrEnc = UCT_ENC_8859;	/* hmmm.. more tests needed? */
218 		} else if (unicount[fp] == 0 && fp != un) {
219 		    /* first unicode for fp doesn't map to itself */
220 		    RawOrEnc = UCT_ENC_8BIT;
221 		} else {
222 		    RawOrEnc = UCT_ENC_8859;	/* hmmm.. more tests needed? */
223 		}
224 	    }
225 	}
226     }
227     if (un <= 0xfffe) {
228 	/*
229 	 *  Check that it isn't a duplicate.
230 	 */
231 	for (i = 0; i < unicount[fp]; i++) {
232 	    if (unitable[fp][i] == un) {
233 		return;
234 	    }
235 	}
236 
237 	/*
238 	 *  Add to list.
239 	 */
240 	if (unicount[fp] > 254) {
241 	    fprintf(stderr, "ERROR: Only 255 unicodes/glyph permitted!\n");
242 	    done(EX_DATAERR);
243 	}
244 	unitable[fp][unicount[fp]] = (u16) un;
245 	unicount[fp]++;
246     }
247     /* otherwise: ignore */
248 }
249 
250 static char this_MIMEcharset[UC_MAXLEN_MIMECSNAME + 1];
251 static char this_LYNXcharset[UC_MAXLEN_LYNXCSNAME + 1];
252 static char id_append[UC_MAXLEN_ID_APPEND + 1] = "_";
253 static int this_isDefaultMap = -1;
254 static int useDefaultMap = 1;
255 static int lowest_eight = 999;
256 
main(int argc,char ** argv)257 int main(int argc, char **argv)
258 {
259     static const char *first_ifdefs[] =
260     {
261 	"/*",
262 	" * Compile-in this chunk of code unless we've turned it off specifically",
263 	" * or in general (id=%s).",
264 	" */",
265 	"",
266 	"#ifndef INCL_CHARSET%s",
267 	"#define INCL_CHARSET%s 1",
268 	"",
269 	"/*ifdef NO_CHARSET*/",
270 	"#ifdef  NO_CHARSET",
271 	"#undef  NO_CHARSET",
272 	"#endif",
273 	"#define NO_CHARSET 0 /* force default to always be active */",
274 	"",
275 	"/*ifndef NO_CHARSET%s*/",
276 	"#ifndef NO_CHARSET%s",
277 	"",
278 	"#if    ALL_CHARSETS",
279 	"#define NO_CHARSET%s 0",
280 	"#else",
281 	"#define NO_CHARSET%s 1",
282 	"#endif",
283 	"",
284 	"#endif /* ndef(NO_CHARSET%s) */",
285 	"",
286 	"#if NO_CHARSET%s",
287 	"#define UC_CHARSET_SETUP%s /*nothing*/",
288 	"#else"
289     };
290     static const char *last_ifdefs[] =
291     {
292 	"",
293 	"#endif /* NO_CHARSET%s */",
294 	"",
295 	"#endif /* INCL_CHARSET%s */"
296     };
297 
298     FILE *ctbl;
299     char buffer[65536];
300     char *outname = 0;
301     unsigned n;
302     int fontlen;
303     int i, nuni, nent;
304     int fp0 = 0, fp1 = 0, un0, un1;
305     char *p, *p1;
306     char *tbuf, ch;
307     size_t outnamesz, plen;
308 
309     if (argc < 2 || argc > 5) {
310 	usage();
311     }
312 
313     if (!strcmp(argv[1], "-")) {
314 	ctbl = stdin;
315 	tblname = "stdin";
316     } else {
317 	ctbl = fopen(tblname = argv[1], "r");
318 	if (!ctbl) {
319 	    perror(tblname);
320 	    done(EX_NOINPUT);
321 	}
322     }
323 
324     if (argc > 2) {
325 	if (!strcmp(argv[2], "-")) {
326 	    chdr = stdout;
327 	    hdrname = "stdout";
328 	} else {
329 	    hdrname = argv[2];
330 	}
331     } else if (ctbl == stdin) {
332 	chdr = stdout;
333 	hdrname = "stdout";
334     } else if ((outname = (char *) malloc(outnamesz = strlen(tblname) + 3)) != 0) {
335 	strlcpy(outname, tblname, outnamesz);
336 	hdrname = outname;
337 	if ((p = strrchr(outname, '.')) == 0)
338 	    p = outname + strlen(outname);
339 	strlcpy(p, ".h", outnamesz - (p - outname));
340     } else {
341 	perror("malloc");
342 	done(EX_NOINPUT);
343     }
344 
345     if (chdr == 0) {
346 	chdr = fopen(hdrname, "w");
347 	if (!chdr) {
348 	    perror(hdrname);
349 	    done(EX_NOINPUT);
350 	}
351     }
352 
353     /*
354      *  For now we assume the default font is always 256 characters.
355      */
356     fontlen = 256;
357 
358     /*
359      *  Initialize table.
360      */
361     for (i = 0; i < fontlen; i++) {
362 	unicount[i] = 0;
363     }
364 
365     /*
366      *  Now we comes to the tricky part.  Parse the input table.
367      */
368     while (fgets(buffer, (int) sizeof(buffer), ctbl) != NULL) {
369 	if ((p = strchr(buffer, '\n')) != NULL) {
370 	    *p = '\0';
371 	} else {
372 	    fprintf(stderr,
373 		    "%s: Warning: line too long or incomplete.\n",
374 		    tblname);
375 	}
376 
377 	/*
378 	 *  Syntax accepted:
379 	 *      <fontpos>       <unicode> <unicode> ...
380 	 *      <fontpos>       <unicode range> <unicode range> ...
381 	 *      <fontpos>       idem
382 	 *      <range>         idem
383 	 *      <range>         <unicode range>
384 	 *      <unicode>       :<replace>
385 	 *      <unicode range> :<replace>
386 	 *      <unicode>       "<C replace>"
387 	 *      <unicode range> "<C replace>"
388 	 *
389 	 *  where <range> ::= <fontpos>-<fontpos>
390 	 *  and <unicode> ::= U+<h><h><h><h>
391 	 *  and <h> ::= <hexadecimal digit>
392 	 *  and <replace> any string not containing '\n' or '\0'
393 	 *  and <C replace> any string with C backslash escapes.
394 	 */
395 	p = buffer;
396 	while (*p == ' ' || *p == '\t') {
397 	    p++;
398 	}
399 	if (!(*p) || *p == '#') {
400 	    /*
401 	     *  Skip comment or blank line.
402 	     */
403 	    continue;
404 	}
405 
406 	switch (*p) {
407 	    /*
408 	     *  Raw Unicode?  I.e. needs some special
409 	     *  processing.  One digit code.
410 	     */
411 	case 'R':
412 	    if (p[1] == 'a' || p[1] == 'A') {
413 		buffer[sizeof(buffer) - 1] = '\0';
414 		if (!strncasecomp(p, "RawOrEnc", 8)) {
415 		    p += 8;
416 		}
417 	    }
418 	    p++;
419 	    while (*p == ' ' || *p == '\t') {
420 		p++;
421 	    }
422 	    RawOrEnc = (int) strtol(p, 0, 10);
423 	    Raw_found = 1;
424 	    continue;
425 
426 	    /*
427 	     *  Is this the default table?
428 	     */
429 	case 'D':
430 	    if (p[1] == 'e' || p[1] == 'E') {
431 		buffer[sizeof(buffer) - 1] = '\0';
432 		if (!strncasecomp(p, "Default", 7)) {
433 		    p += 7;
434 		}
435 	    }
436 	    p++;
437 	    while (*p == ' ' || *p == '\t') {
438 		p++;
439 	    }
440 	    this_isDefaultMap = (*p == '1' || TOLOWER(*p) == 'y');
441 	    continue;
442 
443 	    /*
444 	     *  Is this the default table?
445 	     */
446 	case 'F':
447 	    if (p[1] == 'a' || p[1] == 'A') {
448 		buffer[sizeof(buffer) - 1] = '\0';
449 		if (!strncasecomp(p, "FallBack", 8)) {
450 		    p += 8;
451 		}
452 	    }
453 	    p++;
454 	    while (*p == ' ' || *p == '\t') {
455 		p++;
456 	    }
457 	    useDefaultMap = (*p == '1' || TOLOWER(*p) == 'y');
458 	    continue;
459 
460 	case 'M':
461 	    if (p[1] == 'i' || p[1] == 'I') {
462 		buffer[sizeof(buffer) - 1] = '\0';
463 		if (!strncasecomp(p, "MIMEName", 8)) {
464 		    p += 8;
465 		}
466 	    }
467 	    p++;
468 	    while (*p == ' ' || *p == '\t') {
469 		p++;
470 	    }
471 	    sscanf(p, "%40s", this_MIMEcharset);
472 	    continue;
473 
474 	    /*
475 	     *  Display charset name for options screen.
476 	     */
477 	case 'O':
478 	    if (p[1] == 'p' || p[1] == 'P') {
479 		buffer[sizeof(buffer) - 1] = '\0';
480 		if (!strncasecomp(p, "OptionName", 10)) {
481 		    p += 10;
482 		}
483 	    }
484 	    p++;
485 	    while (*p == ' ' || *p == '\t') {
486 		p++;
487 	    }
488 	    for (i = 0; *p && i < UC_MAXLEN_LYNXCSNAME; p++, i++) {
489 		this_LYNXcharset[i] = *p;
490 	    }
491 	    this_LYNXcharset[i] = '\0';
492 	    continue;
493 
494 	    /*
495 	     *  Codepage number.  Three or four digit code.
496 	     */
497 	case 'C':
498 	    if (p[1] == 'o' || p[1] == 'O') {
499 		buffer[sizeof(buffer) - 1] = '\0';
500 		if (!strncasecomp(p, "CodePage", 8)) {
501 		    p += 8;
502 		}
503 	    }
504 	    p++;
505 	    while (*p == ' ' || *p == '\t') {
506 		p++;
507 	    }
508 	    CodePage = (int) strtol(p, 0, 10);
509 	    continue;
510 	}
511 
512 	if (*p == 'U') {
513 	    un0 = getunicode(&p);
514 	    if (un0 < 0) {
515 		fprintf(stderr, "Bad input line: %s\n", buffer);
516 		done(EX_DATAERR);
517 		fprintf(stderr,
518 			"%s: Bad Unicode range corresponding to font position range 0x%x-0x%x\n",
519 			tblname, fp0, fp1);
520 		done(EX_DATAERR);
521 	    }
522 	    un1 = un0;
523 	    while (*p == ' ' || *p == '\t') {
524 		p++;
525 	    }
526 	    if (*p == '-') {
527 		p++;
528 		while (*p == ' ' || *p == '\t') {
529 		    p++;
530 		}
531 		un1 = getunicode(&p);
532 		if (un1 < 0 || un1 < un0) {
533 		    fprintf(stderr,
534 			    "%s: Bad Unicode range U+%x-U+%x\n",
535 			    tblname, un0, un1);
536 		    fprintf(stderr, "Bad input line: %s\n", buffer);
537 		    done(EX_DATAERR);
538 		}
539 		while (*p == ' ' || *p == '\t') {
540 		    p++;
541 		}
542 	    }
543 
544 	    if (*p != ':' && *p != '"') {
545 		fprintf(stderr, "No ':' or '\"' where expected: %s\n",
546 			buffer);
547 		continue;
548 	    }
549 
550 	    /*
551 	     * Allocate a string large enough for the worst-case use in the
552 	     * loop using sprintf.
553 	     */
554 	    tbuf = (char *) malloc(plen = 5 * strlen(p));
555 
556 	    if (!(p1 = tbuf)) {
557 		fprintf(stderr, "%s: Out of memory\n", tblname);
558 		done(EX_DATAERR);
559 	    }
560 	    if (*p == '"') {
561 		/*
562 		 *  Handle "<C replace>".
563 		 *  Copy chars verbatim until first '"' not \-escaped or
564 		 *  end of buffer.
565 		 */
566 		int escaped = 0;
567 
568 		ch = 0;
569 		for (++p; *p != '\0'; p++) {
570 		    ch = *p;
571 		    if (escaped) {
572 			escaped = 0;
573 		    } else if (ch == '"') {
574 			break;
575 		    } else if (ch == '\\') {
576 			escaped = 1;
577 		    }
578 		    *p1++ = ch;
579 		}
580 		if (escaped || ch != '"') {
581 		    fprintf(stderr, "Warning: String not terminated: %s\n",
582 			    buffer);
583 		    if (escaped)
584 			*p1++ = '\n';
585 		}
586 	    } else {
587 		/*
588 		 *  We had ':'.
589 		 */
590 		for (++p; *p != '\0'; p++, p1++) {
591 		    ch = *p;
592 		    if (UCH(ch) < 32 || ch == '\\' || ch == '\"' ||
593 			UCH(ch) >= 127) {
594 			snprintf(p1, plen - (p1 - tbuf), "\\%.3o", UCH(ch));
595 			p1 += 3;
596 		    } else {
597 			*p1 = ch;
598 		    }
599 		}
600 	    }
601 	    *p1 = '\0';
602 	    for (i = un0; i <= un1; i++) {
603 		addpair_str(tbuf, i);
604 	    }
605 	    continue;
606 	}
607 
608 	/*
609 	 *  Input line (after skipping spaces) doesn't start with one
610 	 *  of the specially recognized characters, so try to interpret
611 	 *  it as starting with a fontpos.
612 	 */
613 	fp0 = (int) strtol(p, &p1, 0);
614 	if (p1 == p) {
615 	    fprintf(stderr, "Bad input line: %s\n", buffer);
616 	    done(EX_DATAERR);
617 	}
618 	p = p1;
619 
620 	while (*p == ' ' || *p == '\t') {
621 	    p++;
622 	}
623 	if (*p == '-') {
624 	    p++;
625 	    fp1 = (int) strtol(p, &p1, 0);
626 	    if (p1 == p) {
627 		fprintf(stderr, "Bad input line: %s\n", buffer);
628 		done(EX_DATAERR);
629 	    }
630 	    p = p1;
631 	} else {
632 	    fp1 = 0;
633 	}
634 
635 	if (fp0 < 0 || fp0 >= fontlen) {
636 	    fprintf(stderr,
637 		    "%s: Glyph number (0x%x) larger than font length\n",
638 		    tblname, fp0);
639 	    done(EX_DATAERR);
640 	}
641 	if (fp1 && (fp1 < fp0 || fp1 >= fontlen)) {
642 	    fprintf(stderr,
643 		    "%s: Bad end of range (0x%x)\n",
644 		    tblname, fp1);
645 	    done(EX_DATAERR);
646 	}
647 
648 	if (fp1) {
649 	    /*
650 	     *  We have a range; expect the word "idem"
651 	     *  or a Unicode range of the same length.
652 	     */
653 	    while (*p == ' ' || *p == '\t') {
654 		p++;
655 	    }
656 	    if (!StrNCmp(p, "idem", 4)) {
657 		for (i = fp0; i <= fp1; i++) {
658 		    addpair(i, i);
659 		}
660 		p += 4;
661 	    } else {
662 		un0 = getunicode(&p);
663 		while (*p == ' ' || *p == '\t') {
664 		    p++;
665 		}
666 		if (*p != '-') {
667 		    fprintf(stderr,
668 			    "%s: Corresponding to a range of font positions,",
669 			    tblname);
670 		    fprintf(stderr,
671 			    " there should be a Unicode range.\n");
672 		    done(EX_DATAERR);
673 		}
674 		p++;
675 		un1 = getunicode(&p);
676 		if (un0 < 0 || un1 < 0) {
677 		    fprintf(stderr,
678 			    "%s: Bad Unicode range corresponding to font position range 0x%x-0x%x\n",
679 			    tblname, fp0, fp1);
680 		    done(EX_DATAERR);
681 		}
682 		if (un1 - un0 != fp1 - fp0) {
683 		    fprintf(stderr,
684 			    "%s: Unicode range U+%x-U+%x not of the same length",
685 			    tblname, un0, un1);
686 		    fprintf(stderr,
687 			    " as font position range 0x%x-0x%x\n",
688 			    fp0, fp1);
689 		    done(EX_DATAERR);
690 		}
691 		for (i = fp0; i <= fp1; i++) {
692 		    addpair(i, un0 - fp0 + i);
693 		}
694 	    }
695 	} else {
696 	    /*
697 	     *  No range; expect a list of unicode values
698 	     *  or unicode ranges for a single font position,
699 	     *  or the word "idem"
700 	     */
701 	    while (*p == ' ' || *p == '\t') {
702 		p++;
703 	    }
704 	    if (!StrNCmp(p, "idem", 4)) {
705 		addpair(fp0, fp0);
706 		p += 4;
707 	    }
708 	    while ((un0 = getunicode(&p)) >= 0) {
709 		addpair(fp0, un0);
710 		while (*p == ' ' || *p == '\t') {
711 		    p++;
712 		}
713 		if (*p == '-') {
714 		    p++;
715 		    un1 = getunicode(&p);
716 		    if (un1 < un0) {
717 			fprintf(stderr,
718 				"%s: Bad Unicode range 0x%x-0x%x\n",
719 				tblname, un0, un1);
720 			done(EX_DATAERR);
721 		    }
722 		    for (un0++; un0 <= un1; un0++) {
723 			addpair(fp0, un0);
724 		    }
725 		}
726 	    }
727 	}
728 	while (*p == ' ' || *p == '\t') {
729 	    p++;
730 	}
731 	if (*p && *p != '#') {
732 	    fprintf(stderr, "%s: trailing junk (%s) ignored\n", tblname, p);
733 	}
734     }
735 
736     /*
737      *  Okay, we hit EOF, now output tables.
738      */
739     fclose(ctbl);
740 
741     /*
742      *  Compute total size of Unicode list.
743      */
744     nuni = 0;
745     for (i = 0; i < fontlen; i++) {
746 	nuni += unicount[i];
747     }
748 
749     if (argc > 3) {
750 	StrNCpy(this_MIMEcharset, argv[3], UC_MAXLEN_MIMECSNAME);
751     } else if (this_MIMEcharset[0] == '\0') {
752 	StrNCpy(this_MIMEcharset, tblname, UC_MAXLEN_MIMECSNAME);
753 	if ((p = strchr(this_MIMEcharset, '.')) != 0) {
754 	    *p = '\0';
755 	}
756     }
757     for (p = this_MIMEcharset; *p; p++) {
758 	*p = (char) TOLOWER(*p);
759     }
760     if (argc > 4) {
761 	StrNCpy(this_LYNXcharset, argv[4], UC_MAXLEN_LYNXCSNAME);
762     } else if (this_LYNXcharset[0] == '\0') {
763 	StrNCpy(this_LYNXcharset, this_MIMEcharset, UC_MAXLEN_LYNXCSNAME);
764     }
765 
766     if (this_isDefaultMap == -1) {
767 	this_isDefaultMap = !StrNCmp(this_MIMEcharset, "iso-8859-1", 10);
768     }
769     fprintf(stderr,
770 	    "makeuctb: %s: %stranslation map",
771 	    this_MIMEcharset, (this_isDefaultMap ? "default " : ""));
772     if (this_isDefaultMap == 1) {
773 	*id_append = '\0';
774     } else {
775 	for (i = 0, p = this_MIMEcharset;
776 	     *p && (i < UC_MAXLEN_ID_APPEND - 1);
777 	     p++, i++) {
778 	    id_append[i + 1] = (char) (isalnum(UCH(*p)) ? *p : '_');
779 	}
780 	id_append[i + 1] = '\0';
781     }
782     fprintf(stderr, " (%s).\n", id_append);
783 
784     for (n = 0; n < TABLESIZE(first_ifdefs); n++) {
785 	fprintf(chdr, first_ifdefs[n], id_append);
786 	fprintf(chdr, "\n");
787     }
788 
789     fprintf(chdr, "\n\
790 /*\n\
791  *  uni_hash.tbl\n\
792  *\n\
793  *  Do not edit this file; it was automatically generated by\n\
794  *\n\
795  *  %s %s\n\
796  *\n\
797  */\n\
798 \n\
799 static const u8 dfont_unicount%s[%d] = \n\
800 {\n\t", argv[0], argv[1], id_append, fontlen);
801 
802     for (i = 0; i < fontlen; i++) {
803 	if (i >= 128 && unicount[i] > 0 && i < lowest_eight) {
804 	    lowest_eight = i;
805 	}
806 	fprintf(chdr, "%3d", unicount[i]);
807 	if (i == (fontlen - 1)) {
808 	    fprintf(chdr, "\n};\n");
809 	} else if ((i % 8) == 7) {
810 	    fprintf(chdr, ",\n\t");
811 	} else {
812 	    fprintf(chdr, ", ");
813 	}
814     }
815 
816     /*
817      *  If lowest_eightbit is anything else but 999,
818      *  this can't be 7-bit only.
819      */
820     if (lowest_eight != 999 && !RawOrEnc) {
821 	RawOrEnc = UCT_ENC_8BIT;
822     }
823 
824     if (nuni) {
825 	fprintf(chdr, "\nstatic const u16 dfont_unitable%s[%d] = \n{\n\t",
826 		id_append, nuni);
827     } else {
828 	fprintf(chdr,
829 		"\nstatic const u16 dfont_unitable%s[1] = {0}; /* dummy */\n", id_append);
830     }
831 
832     fp0 = 0;
833     nent = 0;
834     for (i = 0; i < nuni; i++) {
835 	while (nent >= unicount[fp0]) {
836 	    fp0++;
837 	    nent = 0;
838 	}
839 	fprintf(chdr, "0x%04x", unitable[fp0][nent++]);
840 	if (i == (nuni - 1)) {
841 	    fprintf(chdr, "\n};\n");
842 	} else if ((i % 8) == 7) {
843 	    fprintf(chdr, ",\n\t");
844 	} else {
845 	    fprintf(chdr, ", ");
846 	}
847     }
848 
849     if (themap_str.entry_ct) {
850 	fprintf(chdr, "\n\
851 static struct unipair_str repl_map%s[%d] = \n\
852 {\n\t", id_append, themap_str.entry_ct);
853     } else {
854 	fprintf(chdr, "\n\
855 /* static struct unipair_str repl_map%s[]; */\n", id_append);
856     }
857 
858     for (i = 0; i < themap_str.entry_ct; i++) {
859 	fprintf(chdr, "{0x%x,\"%s\"}",
860 		themap_str.entries[i].unicode,
861 		themap_str.entries[i].replace_str);
862 	if (i == (themap_str.entry_ct - 1)) {
863 	    fprintf(chdr, "\n};\n");
864 	} else if ((i % 4) == 3) {
865 	    fprintf(chdr, ",\n\t");
866 	} else {
867 	    fprintf(chdr, ", ");
868 	}
869     }
870     if (themap_str.entry_ct) {
871 	fprintf(chdr, "\n\
872 static const struct unimapdesc_str dfont_replacedesc%s = {%d,repl_map%s,",
873 		id_append, themap_str.entry_ct, id_append);
874     } else {
875 	fprintf(chdr, "\n\
876 static const struct unimapdesc_str dfont_replacedesc%s = {0,NULL,", id_append);
877     }
878     fprintf(chdr, "%d,%d};\n",
879 	    this_isDefaultMap ? 1 : 0,
880 	    (useDefaultMap && !this_isDefaultMap) ? 1 : 0
881 	);
882 
883     fprintf(chdr, "#define UC_CHARSET_SETUP%s UC_Charset_Setup(\
884 \"%s\",\\\n\"%s\",\\\n\
885 dfont_unicount%s,dfont_unitable%s,%d,\\\n\
886 dfont_replacedesc%s,%d,%d,%d)\n",
887 	    id_append, this_MIMEcharset, this_LYNXcharset,
888 	    id_append, id_append, nuni, id_append, lowest_eight, RawOrEnc, CodePage);
889 
890     for (n = 0; n < TABLESIZE(last_ifdefs); n++) {
891 	fprintf(chdr, last_ifdefs[n], id_append);
892 	fprintf(chdr, "\n");
893     }
894 
895     done(EX_OK);
896     return 0;
897 }
898