`Version_string' as C++ object was not visible to linker from C
[s-roff.git] / src / utils / tfmtodit / tfmtodit.cc
blob06b5d185e94ad4bf045218ba2879a7924ec2d2f6
1 // -*- C++ -*-
2 /* Copyright (C) 1989-1992, 2000, 2001 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 /* I have tried to incorporate the changes needed for TeX 3.0 tfm files,
22 but I haven't tested them. */
24 /* Groff requires more font metric information than TeX. The reason
25 for this is that TeX has separate Math Italic fonts, whereas groff
26 uses normal italic fonts for math. The two additional pieces of
27 information required by groff correspond to the two arguments to the
28 math_fit() macro in the Metafont programs for the CM fonts. In the
29 case of a font for which math_fitting is false, these two arguments
30 are normally ignored by Metafont. We need to get hold of these two
31 parameters and put them in the groff font file.
33 We do this by loading this definition after cmbase when creating cm.base.
35 def ignore_math_fit(expr left_adjustment,right_adjustment) =
36 special "adjustment";
37 numspecial left_adjustment*16/designsize;
38 numspecial right_adjustment*16/designsize;
39 enddef;
41 This puts the two arguments to the math_fit macro into the gf file.
42 (They will appear in the gf file immediately before the character to
43 which they apply.) We then create a gf file using this cm.base. Then
44 we run tfmtodit and specify this gf file with the -g option.
46 This need only be done for a font for which math_fitting is false;
47 When it's true, the left_correction and subscript_correction should
48 both be zero. */
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <math.h>
53 #include <string.h>
54 #include <errno.h>
55 #include "lib.h"
56 #include "errarg.h"
57 #include "error.h"
58 #include "assert.h"
59 #include "cset.h"
60 #include "nonposix.h"
62 extern "C" const char *Version_string;
64 /* Values in the tfm file should be multiplied by this. */
66 #define MULTIPLIER 1
68 struct char_info_word {
69 unsigned char width_index;
70 char height_index;
71 char depth_index;
72 char italic_index;
73 char tag;
74 unsigned char remainder;
77 struct lig_kern_command {
78 unsigned char skip_byte;
79 unsigned char next_char;
80 unsigned char op_byte;
81 unsigned char remainder;
84 class tfm {
85 int bc;
86 int ec;
87 int nw;
88 int nh;
89 int nd;
90 int ni;
91 int nl;
92 int nk;
93 int np;
94 int cs;
95 int ds;
96 char_info_word *char_info;
97 int *width;
98 int *height;
99 int *depth;
100 int *italic;
101 lig_kern_command *lig_kern;
102 int *kern;
103 int *param;
104 public:
105 tfm();
106 ~tfm();
107 int load(const char *);
108 int contains(int);
109 int get_width(int);
110 int get_height(int);
111 int get_depth(int);
112 int get_italic(int);
113 int get_param(int, int *);
114 int get_checksum();
115 int get_design_size();
116 int get_lig(unsigned char, unsigned char, unsigned char *);
117 friend class kern_iterator;
120 class kern_iterator {
121 tfm *t;
122 int c;
123 int i;
124 public:
125 kern_iterator(tfm *);
126 int next(unsigned char *c1, unsigned char *c2, int *k);
130 kern_iterator::kern_iterator(tfm *p)
131 : t(p), c(t->bc), i(-1)
135 int kern_iterator::next(unsigned char *c1, unsigned char *c2, int *k)
137 for (; c <= t->ec; c++)
138 if (t->char_info[c - t->bc].tag == 1) {
139 if (i < 0) {
140 i = t->char_info[c - t->bc].remainder;
141 if (t->lig_kern[i].skip_byte > 128)
142 i = (256*t->lig_kern[i].op_byte
143 + t->lig_kern[i].remainder);
145 for (;;) {
146 int skip = t->lig_kern[i].skip_byte;
147 if (skip <= 128 && t->lig_kern[i].op_byte >= 128) {
148 *c1 = c;
149 *c2 = t->lig_kern[i].next_char;
150 *k = t->kern[256*(t->lig_kern[i].op_byte - 128)
151 + t->lig_kern[i].remainder];
152 if (skip == 128) {
153 c++;
154 i = -1;
156 else
157 i += skip + 1;
158 return 1;
160 if (skip >= 128)
161 break;
162 i += skip + 1;
164 i = -1;
166 return 0;
169 tfm::tfm()
170 : char_info(0), width(0), height(0), depth(0), italic(0), lig_kern(0),
171 kern(0), param(0)
175 int tfm::get_lig(unsigned char c1, unsigned char c2, unsigned char *cp)
177 if (contains(c1) && char_info[c1 - bc].tag == 1) {
178 int i = char_info[c1 - bc].remainder;
179 if (lig_kern[i].skip_byte > 128)
180 i = 256*lig_kern[i].op_byte + lig_kern[i].remainder;
181 for (;;) {
182 int skip = lig_kern[i].skip_byte;
183 if (skip > 128)
184 break;
185 // We are only interested in normal ligatures, for which
186 // op_byte == 0.
187 if (lig_kern[i].op_byte == 0
188 && lig_kern[i].next_char == c2) {
189 *cp = lig_kern[i].remainder;
190 return 1;
192 if (skip == 128)
193 break;
194 i += skip + 1;
197 return 0;
200 int tfm::contains(int i)
202 return i >= bc && i <= ec && char_info[i - bc].width_index != 0;
205 int tfm::get_width(int i)
207 return width[char_info[i - bc].width_index];
210 int tfm::get_height(int i)
212 return height[char_info[i - bc].height_index];
215 int tfm::get_depth(int i)
217 return depth[char_info[i - bc].depth_index];
220 int tfm::get_italic(int i)
222 return italic[char_info[i - bc].italic_index];
225 int tfm::get_param(int i, int *p)
227 if (i <= 0 || i > np)
228 return 0;
229 else {
230 *p = param[i - 1];
231 return 1;
235 int tfm::get_checksum()
237 return cs;
240 int tfm::get_design_size()
242 return ds;
245 tfm::~tfm()
247 a_delete char_info;
248 a_delete width;
249 a_delete height;
250 a_delete depth;
251 a_delete italic;
252 a_delete lig_kern;
253 a_delete kern;
254 a_delete param;
257 int read2(unsigned char *&s)
259 int n;
260 n = *s++ << 8;
261 n |= *s++;
262 return n;
265 int read4(unsigned char *&s)
267 int n;
268 n = *s++ << 24;
269 n |= *s++ << 16;
270 n |= *s++ << 8;
271 n |= *s++;
272 return n;
276 int tfm::load(const char *file)
278 errno = 0;
279 FILE *fp = fopen(file, FOPEN_RB);
280 if (!fp) {
281 error("can't open `%1': %2", file, strerror(errno));
282 return 0;
284 int c1 = getc(fp);
285 int c2 = getc(fp);
286 if (c1 == EOF || c2 == EOF) {
287 fclose(fp);
288 error("unexpected end of file on `%1'", file);
289 return 0;
291 int lf = (c1 << 8) + c2;
292 int toread = lf*4 - 2;
293 unsigned char *buf = new unsigned char[toread];
294 if (fread(buf, 1, toread, fp) != (size_t)toread) {
295 if (feof(fp))
296 error("unexpected end of file on `%1'", file);
297 else
298 error("error on file `%1'", file);
299 a_delete buf;
300 fclose(fp);
301 return 0;
303 fclose(fp);
304 if (lf < 6) {
305 error("bad tfm file `%1': impossibly short", file);
306 a_delete buf;
307 return 0;
309 unsigned char *ptr = buf;
310 int lh = read2(ptr);
311 bc = read2(ptr);
312 ec = read2(ptr);
313 nw = read2(ptr);
314 nh = read2(ptr);
315 nd = read2(ptr);
316 ni = read2(ptr);
317 nl = read2(ptr);
318 nk = read2(ptr);
319 int ne = read2(ptr);
320 np = read2(ptr);
321 if (6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np != lf) {
322 error("bad tfm file `%1': lengths do not sum", file);
323 a_delete buf;
324 return 0;
326 if (lh < 2) {
327 error("bad tfm file `%1': header too short", file);
328 a_delete buf;
329 return 0;
331 char_info = new char_info_word[ec - bc + 1];
332 width = new int[nw];
333 height = new int[nh];
334 depth = new int[nd];
335 italic = new int[ni];
336 lig_kern = new lig_kern_command[nl];
337 kern = new int[nk];
338 param = new int[np];
339 int i;
340 cs = read4(ptr);
341 ds = read4(ptr);
342 ptr += (lh-2)*4;
343 for (i = 0; i < ec - bc + 1; i++) {
344 char_info[i].width_index = *ptr++;
345 unsigned char tem = *ptr++;
346 char_info[i].depth_index = tem & 0xf;
347 char_info[i].height_index = tem >> 4;
348 tem = *ptr++;
349 char_info[i].italic_index = tem >> 2;
350 char_info[i].tag = tem & 3;
351 char_info[i].remainder = *ptr++;
353 for (i = 0; i < nw; i++)
354 width[i] = read4(ptr);
355 for (i = 0; i < nh; i++)
356 height[i] = read4(ptr);
357 for (i = 0; i < nd; i++)
358 depth[i] = read4(ptr);
359 for (i = 0; i < ni; i++)
360 italic[i] = read4(ptr);
361 for (i = 0; i < nl; i++) {
362 lig_kern[i].skip_byte = *ptr++;
363 lig_kern[i].next_char = *ptr++;
364 lig_kern[i].op_byte = *ptr++;
365 lig_kern[i].remainder = *ptr++;
367 for (i = 0; i < nk; i++)
368 kern[i] = read4(ptr);
369 ptr += ne*4;
370 for (i = 0; i < np; i++)
371 param[i] = read4(ptr);
372 assert(ptr == buf + lf*4 - 2);
373 a_delete buf;
374 return 1;
377 class gf {
378 int left[256];
379 int right[256];
380 static int sread4(int *p, FILE *fp);
381 static int uread3(int *p, FILE *fp);
382 static int uread2(int *p, FILE *fp);
383 static int skip(int n, FILE *fp);
384 public:
385 gf();
386 int load(const char *file);
387 int get_left_adjustment(int i) { return left[i]; }
388 int get_right_adjustment(int i) { return right[i]; }
391 gf::gf()
393 for (int i = 0; i < 256; i++)
394 left[i] = right[i] = 0;
397 int gf::load(const char *file)
399 enum {
400 paint_0 = 0,
401 paint1 = 64,
402 boc = 67,
403 boc1 = 68,
404 eoc = 69,
405 skip0 = 70,
406 skip1 = 71,
407 new_row_0 = 74,
408 xxx1 = 239,
409 yyy = 243,
410 no_op = 244,
411 pre = 247,
412 post = 248
414 int got_an_adjustment = 0;
415 int pending_adjustment = 0;
416 int left_adj, right_adj;
417 const int gf_id_byte = 131;
418 errno = 0;
419 FILE *fp = fopen(file, FOPEN_RB);
420 if (!fp) {
421 error("can't open `%1': %2", file, strerror(errno));
422 return 0;
424 if (getc(fp) != pre || getc(fp) != gf_id_byte) {
425 error("bad gf file");
426 return 0;
428 int n = getc(fp);
429 if (n == EOF)
430 goto eof;
431 if (!skip(n, fp))
432 goto eof;
433 for (;;) {
434 int op = getc(fp);
435 if (op == EOF)
436 goto eof;
437 if (op == post)
438 break;
439 if ((op >= paint_0 && op <= paint_0 + 63)
440 || (op >= new_row_0 && op <= new_row_0 + 164))
441 continue;
442 switch (op) {
443 case no_op:
444 case eoc:
445 case skip0:
446 break;
447 case paint1:
448 case skip1:
449 if (!skip(1, fp))
450 goto eof;
451 break;
452 case paint1 + 1:
453 case skip1 + 1:
454 if (!skip(2, fp))
455 goto eof;
456 break;
457 case paint1 + 2:
458 case skip1 + 2:
459 if (!skip(3, fp))
460 goto eof;
461 break;
462 case boc:
464 int code;
465 if (!sread4(&code, fp))
466 goto eof;
467 if (pending_adjustment) {
468 pending_adjustment = 0;
469 left[code & 0377] = left_adj;
470 right[code & 0377] = right_adj;
472 if (!skip(20, fp))
473 goto eof;
474 break;
476 case boc1:
478 int code = getc(fp);
479 if (code == EOF)
480 goto eof;
481 if (pending_adjustment) {
482 pending_adjustment = 0;
483 left[code] = left_adj;
484 right[code] = right_adj;
486 if (!skip(4, fp))
487 goto eof;
488 break;
490 case xxx1:
492 int len = getc(fp);
493 if (len == EOF)
494 goto eof;
495 char buf[256];
496 if (fread(buf, 1, len, fp) != (size_t)len)
497 goto eof;
498 if (len == 10 /* strlen("adjustment") */
499 && memcmp(buf, "adjustment", len) == 0) {
500 int c = getc(fp);
501 if (c != yyy) {
502 if (c != EOF)
503 ungetc(c, fp);
504 break;
506 if (!sread4(&left_adj, fp))
507 goto eof;
508 c = getc(fp);
509 if (c != yyy) {
510 if (c != EOF)
511 ungetc(c, fp);
512 break;
514 if (!sread4(&right_adj, fp))
515 goto eof;
516 got_an_adjustment = 1;
517 pending_adjustment = 1;
519 break;
521 case xxx1 + 1:
522 if (!uread2(&n, fp) || !skip(n, fp))
523 goto eof;
524 break;
525 case xxx1 + 2:
526 if (!uread3(&n, fp) || !skip(n, fp))
527 goto eof;
528 break;
529 case xxx1 + 3:
530 if (!sread4(&n, fp) || !skip(n, fp))
531 goto eof;
532 break;
533 case yyy:
534 if (!skip(4, fp))
535 goto eof;
536 break;
537 default:
538 fatal("unrecognized opcode `%1'", op);
539 break;
542 if (!got_an_adjustment)
543 warning("no adjustment specials found in gf file");
544 return 1;
545 eof:
546 error("unexpected end of file");
547 return 0;
550 int gf::sread4(int *p, FILE *fp)
552 *p = getc(fp);
553 if (*p >= 128)
554 *p -= 256;
555 *p <<= 8;
556 *p |= getc(fp);
557 *p <<= 8;
558 *p |= getc(fp);
559 *p <<= 8;
560 *p |= getc(fp);
561 return !ferror(fp) && !feof(fp);
564 int gf::uread3(int *p, FILE *fp)
566 *p = getc(fp);
567 *p <<= 8;
568 *p |= getc(fp);
569 *p <<= 8;
570 *p |= getc(fp);
571 return !ferror(fp) && !feof(fp);
574 int gf::uread2(int *p, FILE *fp)
576 *p = getc(fp);
577 *p <<= 8;
578 *p |= getc(fp);
579 return !ferror(fp) && !feof(fp);
582 int gf::skip(int n, FILE *fp)
584 while (--n >= 0)
585 if (getc(fp) == EOF)
586 return 0;
587 return 1;
591 struct char_list {
592 char *ch;
593 char_list *next;
594 char_list(const char *, char_list * = 0);
597 char_list::char_list(const char *s, char_list *p) : ch(strsave(s)), next(p)
602 int read_map(const char *file, char_list **table)
604 errno = 0;
605 FILE *fp = fopen(file, "r");
606 if (!fp) {
607 error("can't open `%1': %2", file, strerror(errno));
608 return 0;
610 for (int i = 0; i < 256; i++)
611 table[i] = 0;
612 char buf[512];
613 int lineno = 0;
614 while (fgets(buf, int(sizeof(buf)), fp)) {
615 lineno++;
616 char *ptr = buf;
617 while (csspace(*ptr))
618 ptr++;
619 if (*ptr == '\0' || *ptr == '#')
620 continue;
621 ptr = strtok(ptr, " \n\t");
622 if (!ptr)
623 continue;
624 int n;
625 if (sscanf(ptr, "%d", &n) != 1) {
626 error("%1:%2: bad map file", file, lineno);
627 fclose(fp);
628 return 0;
630 if (n < 0 || n > 255) {
631 error("%1:%2: code out of range", file, lineno);
632 fclose(fp);
633 return 0;
635 ptr = strtok(0, " \n\t");
636 if (!ptr) {
637 error("%1:%2: missing names", file, lineno);
638 fclose(fp);
639 return 0;
641 for (; ptr; ptr = strtok(0, " \n\t"))
642 table[n] = new char_list(ptr, table[n]);
644 fclose(fp);
645 return 1;
649 /* Every character that can participate in a ligature appears in the
650 lig_chars table. `ch' gives the full-name of the character, `name'
651 gives the groff name of the character, `i' gives its index in
652 the encoding, which is filled in later (-1 if it does not appear). */
654 struct {
655 const char *ch;
656 int i;
657 } lig_chars[] = {
658 { "f", -1 },
659 { "i", -1 },
660 { "l", -1 },
661 { "ff", -1 },
662 { "fi", -1 },
663 { "fl", -1 },
664 { "Fi", -1 },
665 { "Fl", -1 },
668 // Indices into lig_chars[].
670 enum { CH_f, CH_i, CH_l, CH_ff, CH_fi, CH_fl, CH_ffi, CH_ffl };
672 // Each possible ligature appears in this table.
674 struct {
675 unsigned char c1, c2, res;
676 const char *ch;
677 } lig_table[] = {
678 { CH_f, CH_f, CH_ff, "ff" },
679 { CH_f, CH_i, CH_fi, "fi" },
680 { CH_f, CH_l, CH_fl, "fl" },
681 { CH_ff, CH_i, CH_ffi, "ffi" },
682 { CH_ff, CH_l, CH_ffl, "ffl" },
685 static void usage(FILE *stream);
687 int main(int argc, char **argv)
689 program_name = argv[0];
690 int special_flag = 0;
691 int skewchar = -1;
692 int opt;
693 const char *gf_file = 0;
694 static const struct option long_options[] = {
695 { "help", no_argument, 0, CHAR_MAX + 1 },
696 { "version", no_argument, 0, 'v' },
697 { NULL, 0, 0, 0 }
699 while ((opt = getopt_long(argc, argv, "svg:k:", long_options, NULL)) != EOF)
700 switch (opt) {
701 case 'g':
702 gf_file = optarg;
703 break;
704 case 's':
705 special_flag = 1;
706 break;
707 case 'k':
709 char *ptr;
710 long n = strtol(optarg, &ptr, 0);
711 if ((n == 0 && ptr == optarg)
712 || *ptr != '\0'
713 || n < 0
714 || n > UCHAR_MAX)
715 error("invalid skewchar");
716 else
717 skewchar = (int)n;
718 break;
720 case 'v':
722 printf("GNU tfmtodit (groff) version %s\n", Version_string);
723 exit(0);
724 break;
726 case CHAR_MAX + 1: // --help
727 usage(stdout);
728 exit(0);
729 break;
730 case '?':
731 usage(stderr);
732 exit(1);
733 break;
734 case EOF:
735 assert(0);
737 if (argc - optind != 3) {
738 usage(stderr);
739 exit(1);
741 gf g;
742 if (gf_file) {
743 if (!g.load(gf_file))
744 return 1;
746 const char *tfm_file = argv[optind];
747 const char *map_file = argv[optind + 1];
748 const char *font_file = argv[optind + 2];
749 tfm t;
750 if (!t.load(tfm_file))
751 return 1;
752 char_list *table[256];
753 if (!read_map(map_file, table))
754 return 1;
755 errno = 0;
756 if (!freopen(font_file, "w", stdout)) {
757 error("can't open `%1' for writing: %2", font_file, strerror(errno));
758 return 1;
760 printf("name %s\n", font_file);
761 if (special_flag)
762 fputs("special\n", stdout);
763 char *internal_name = strsave(argv[optind]);
764 int len = strlen(internal_name);
765 if (len > 4 && strcmp(internal_name + len - 4, ".tfm") == 0)
766 internal_name[len - 4] = '\0';
767 // DIR_SEPS[] are possible directory separator characters, see nonposix.h.
768 // We want the rightmost separator of all possible ones.
769 // Example: d:/foo\\bar.
770 const char *s = strrchr(internal_name, DIR_SEPS[0]), *s1;
771 const char *sep = &DIR_SEPS[1];
772 while (*sep)
774 s1 = strrchr(internal_name, *sep);
775 if (s1 && (!s || s1 > s))
776 s = s1;
777 sep++;
779 printf("internalname %s\n", s ? s + 1 : internal_name);
780 int n;
781 if (t.get_param(2, &n)) {
782 if (n > 0)
783 printf("spacewidth %d\n", n*MULTIPLIER);
785 if (t.get_param(1, &n) && n != 0)
786 printf("slant %f\n", atan2(n/double(1<<20), 1.0)*180.0/PI);
787 int xheight;
788 if (!t.get_param(5, &xheight))
789 xheight = 0;
790 unsigned int i;
791 // Print the list of ligatures.
792 // First find the indices of each character that can participate in
793 // a ligature.
794 for (i = 0; i < 256; i++)
795 for (unsigned int j = 0; j < sizeof(lig_chars)/sizeof(lig_chars[0]); j++)
796 for (char_list *p = table[i]; p; p = p->next)
797 if (strcmp(lig_chars[j].ch, p->ch) == 0)
798 lig_chars[j].i = i;
799 // For each possible ligature, if its participants all exist,
800 // and it appears as a ligature in the tfm file, include in
801 // the list of ligatures.
802 int started = 0;
803 for (i = 0; i < sizeof(lig_table)/sizeof(lig_table[0]); i++) {
804 int i1 = lig_chars[lig_table[i].c1].i;
805 int i2 = lig_chars[lig_table[i].c2].i;
806 int r = lig_chars[lig_table[i].res].i;
807 if (i1 >= 0 && i2 >= 0 && r >= 0) {
808 unsigned char c;
809 if (t.get_lig(i1, i2, &c) && c == r) {
810 if (!started) {
811 started = 1;
812 fputs("ligatures", stdout);
814 printf(" %s", lig_table[i].ch);
818 if (started)
819 fputs(" 0\n", stdout);
820 printf("checksum %d\n", t.get_checksum());
821 printf("designsize %d\n", t.get_design_size());
822 // Now print out the kerning information.
823 int had_kern = 0;
824 kern_iterator iter(&t);
825 unsigned char c1, c2;
826 int k;
827 while (iter.next(&c1, &c2, &k))
828 if (c2 != skewchar) {
829 k *= MULTIPLIER;
830 char_list *q = table[c2];
831 for (char_list *p1 = table[c1]; p1; p1 = p1->next)
832 for (char_list *p2 = q; p2; p2 = p2->next) {
833 if (!had_kern) {
834 printf("kernpairs\n");
835 had_kern = 1;
837 printf("%s %s %d\n", p1->ch, p2->ch, k);
840 printf("charset\n");
841 char_list unnamed("---");
842 for (i = 0; i < 256; i++)
843 if (t.contains(i)) {
844 char_list *p = table[i] ? table[i] : &unnamed;
845 int m[6];
846 m[0] = t.get_width(i);
847 m[1] = t.get_height(i);
848 m[2] = t.get_depth(i);
849 m[3] = t.get_italic(i);
850 m[4] = g.get_left_adjustment(i);
851 m[5] = g.get_right_adjustment(i);
852 printf("%s\t%d", p->ch, m[0]*MULTIPLIER);
853 int j;
854 for (j = int(sizeof(m)/sizeof(m[0])) - 1; j > 0; j--)
855 if (m[j] != 0)
856 break;
857 for (k = 1; k <= j; k++)
858 printf(",%d", m[k]*MULTIPLIER);
859 int type = 0;
860 if (m[2] > 0)
861 type = 1;
862 if (m[1] > xheight)
863 type += 2;
864 printf("\t%d\t%04o\n", type, i);
865 for (p = p->next; p; p = p->next)
866 printf("%s\t\"\n", p->ch);
868 return 0;
871 static void usage(FILE *stream)
873 fprintf(stream, "usage: %s [-sv] [-g gf_file] [-k skewchar] tfm_file map_file font\n",
874 program_name);