hpftodit.cpp revision 114402
197403Sobrien// -*- C++ -*-
297403Sobrien/* Copyright (C) 1994, 2000, 2001, 2003 Free Software Foundation, Inc.
397403Sobrien     Written by James Clark (jjc@jclark.com)
497403Sobrien
597403SobrienThis file is part of groff.
697403Sobrien
797403Sobriengroff is free software; you can redistribute it and/or modify it under
897403Sobrienthe terms of the GNU General Public License as published by the Free
997403SobrienSoftware Foundation; either version 2, or (at your option) any later
1097403Sobrienversion.
1197403Sobrien
1297403Sobriengroff is distributed in the hope that it will be useful, but WITHOUT ANY
1397403SobrienWARRANTY; without even the implied warranty of MERCHANTABILITY or
1497403SobrienFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1597403Sobrienfor more details.
1697403Sobrien
1797403SobrienYou should have received a copy of the GNU General Public License along
1897403Sobrienwith groff; see the file COPYING.  If not, write to the Free Software
1997403SobrienFoundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
2097403Sobrien
2197403Sobrien/*
2297403SobrienTODO
2397403Sobrienput human readable font name in device file
2497403Sobriendevise new names for useful characters
2597403Sobrienuse --- for unnamed characters
2697403Sobrienoption to specify symbol sets to look in
2797403Sobrienmake it work with TrueType fonts
2897403Sobrienput filename in error messages (or fix lib)
2997403Sobrien*/
3097403Sobrien
3197403Sobrien#include "lib.h"
3297403Sobrien
3397403Sobrien#include <stdlib.h>
3497403Sobrien#include <math.h>
3597403Sobrien#include <errno.h>
3697403Sobrien#include "assert.h"
3797403Sobrien#include "posix.h"
3897403Sobrien#include "errarg.h"
3997403Sobrien#include "error.h"
4097403Sobrien#include "cset.h"
4197403Sobrien#include "nonposix.h"
4297403Sobrien
4397403Sobrienextern "C" const char *Version_string;
4497403Sobrien
4597403Sobrien#define SIZEOF(v) (sizeof(v)/sizeof(v[0]))
4697403Sobrien
4797403Sobrienconst int MULTIPLIER = 3;
4897403Sobrien
4997403Sobrieninline
5097403Sobrienint scale(int n)
5197403Sobrien{
5297403Sobrien  return n * MULTIPLIER;
5397403Sobrien}
5497403Sobrien
5597403Sobrien// tags in TFM file
5697403Sobrien
5797403Sobrienenum tag_type {
5897403Sobrien  min_tag = 400,
5997403Sobrien  type_tag = 400,
6097403Sobrien  symbol_set_tag = 404,
6197403Sobrien  msl_tag = 403,
6297403Sobrien  inches_per_point_tag = 406,
6397403Sobrien  design_units_per_em_tag = 408,
6497403Sobrien  posture_tag = 409,
6597403Sobrien  stroke_weight_tag = 411,
6697403Sobrien  spacing_tag = 412,
6797403Sobrien  slant_tag = 413,
6897403Sobrien  appearance_width_tag = 414,
6997403Sobrien  word_spacing_tag = 421,
7097403Sobrien  x_height_tag = 424,
7197403Sobrien  lower_ascent_tag = 427,
7297403Sobrien  lower_descent_tag = 428,
7397403Sobrien  width_tag = 433,
7497403Sobrien  left_extent_tag = 435,
7597403Sobrien  right_extent_tag = 436,
7697403Sobrien  ascent_tag = 437,
7797403Sobrien  descent_tag = 438,
7897403Sobrien  pair_kern_tag = 439,
7997403Sobrien  typeface_tag = 442,
8097403Sobrien  max_tag = 443
8197403Sobrien  };
8297403Sobrien
8397403Sobrien// types in TFM file
8497403Sobrien
8597403Sobrienenum {
8697403Sobrien  ENUM_TYPE = 1,
8797403Sobrien  BYTE_TYPE = 2,
8897403Sobrien  USHORT_TYPE = 3,
8997403Sobrien  FLOAT_TYPE = 5,
9097403Sobrien  SIGNED_SHORT_TYPE = 17
9197403Sobrien  };
9297403Sobrien
9397403Sobrien
9497403Sobrientypedef unsigned char byte;
9597403Sobrientypedef unsigned short uint16;
9697403Sobrientypedef short int16;
9797403Sobrientypedef unsigned int uint32;
9897403Sobrien
9997403Sobrienclass File {
10097403Sobrienpublic:
10197403Sobrien  File(const char *);
10297403Sobrien  void skip(int n);
10397403Sobrien  byte get_byte();
10497403Sobrien  uint16 get_uint16();
10597403Sobrien  uint32 get_uint32();
10697403Sobrien  void seek(uint32 n);
10797403Sobrienprivate:
10897403Sobrien  unsigned char *buf_;
10997403Sobrien  const unsigned char *ptr_;
11097403Sobrien  const unsigned char *end_;
11197403Sobrien};
11297403Sobrien
11397403Sobrienstruct entry {
11497403Sobrien  char present;
11597403Sobrien  uint16 type;
11697403Sobrien  uint32 count;
11797403Sobrien  uint32 value;
11897403Sobrien  entry() : present(0) { }
11997403Sobrien};
12097403Sobrien
12197403Sobrienstruct char_info {
12297403Sobrien  uint16 msl;
12397403Sobrien  uint16 width;
12497403Sobrien  int16 ascent;
12597403Sobrien  int16 descent;
12697403Sobrien  int16 left_extent;
12797403Sobrien  uint16 right_extent;
12897403Sobrien  uint16 symbol_set;
12997403Sobrien  unsigned char code;
13097403Sobrien};
13197403Sobrien
13297403Sobrienconst uint16 NO_SYMBOL_SET = 0;
13397403Sobrien
13497403Sobrienstruct name_list {
13597403Sobrien  char *name;
13697403Sobrien  name_list *next;
13797403Sobrien  name_list(const char *s, name_list *p) : name(strsave(s)), next(p) { }
13897403Sobrien  ~name_list() { a_delete name; }
13997403Sobrien};
14097403Sobrien
14197403Sobrienstruct symbol_set {
14297403Sobrien  uint16 select;
14397403Sobrien  uint16 index[256];
14497403Sobrien};
14597403Sobrien
14697403Sobrien#define SYMBOL_SET(n, c) ((n) * 32 + ((c) - 64))
14797403Sobrien
14897403Sobrienuint16 text_symbol_sets[] = {
14997403Sobrien  SYMBOL_SET(0, 'N'),		// Latin 1
15097403Sobrien  SYMBOL_SET(6, 'J'),		// Microsoft Publishing
15197403Sobrien  SYMBOL_SET(2, 'N'),		// Latin 2
15297403Sobrien  0
15397403Sobrien  };
15497403Sobrien
15597403Sobrienuint16 special_symbol_sets[] = {
15697403Sobrien  SYMBOL_SET(8, 'M'),
15797403Sobrien  SYMBOL_SET(5, 'M'),
15897403Sobrien  SYMBOL_SET(15, 'U'),
15997403Sobrien  0
16097403Sobrien  };
16197403Sobrien
16297403Sobrienentry tags[max_tag + 1 - min_tag];
16397403Sobrien
16497403Sobrienchar_info *char_table;
16597403Sobrienuint32 nchars;
16697403Sobrien
16797403Sobrienunsigned int msl_name_table_size = 0;
16897403Sobrienname_list **msl_name_table = 0;
16997403Sobrien
17097403Sobrienunsigned int n_symbol_sets;
17197403Sobriensymbol_set *symbol_set_table;
17297403Sobrien
173static int special_flag = 0;
174static int italic_flag = 0;
175static int italic_sep;
176
177static void usage(FILE *stream);
178static void usage();
179static const char *xbasename(const char *);
180static void read_tags(File &);
181static void check_type();
182static void check_units(File &);
183static int read_map(const char *);
184static void require_tag(tag_type);
185static void dump_tags(File &f);
186static void output_spacewidth();
187static void output_pclweight();
188static void output_pclproportional();
189static void read_and_output_pcltypeface(File &);
190static void output_pclstyle();
191static void output_slant();
192static void output_ligatures();
193static void read_symbol_sets(File &);
194static void read_and_output_kernpairs(File &);
195static void output_charset();
196static void read_char_table(File &f);
197
198inline
199entry &tag_info(tag_type t)
200{
201  return tags[t - min_tag];
202}
203
204int main(int argc, char **argv)
205{
206  program_name = argv[0];
207
208  int opt;
209  int debug_flag = 0;
210
211  static const struct option long_options[] = {
212    { "help", no_argument, 0, CHAR_MAX + 1 },
213    { "version", no_argument, 0, 'v' },
214    { NULL, 0, 0, 0 }
215  };
216  while ((opt = getopt_long(argc, argv, "dsvi:", long_options, NULL)) != EOF) {
217    switch (opt) {
218    case 'd':
219      debug_flag = 1;
220      break;
221    case 's':
222      special_flag = 1;
223      break;
224    case 'i':
225      italic_flag = 1;
226      italic_sep = atoi(optarg);
227      break;
228    case 'v':
229      {
230	printf("GNU hpftodit (groff) version %s\n", Version_string);
231	exit(0);
232      }
233      break;
234    case CHAR_MAX + 1: // --help
235      usage(stdout);
236      exit(0);
237      break;
238    case '?':
239      usage();
240      break;
241    default:
242      assert(0);
243    }
244  }
245  if (argc - optind != 3)
246    usage();
247  File f(argv[optind]);
248  if (!read_map(argv[optind + 1]))
249    exit(1);
250  current_filename = 0;
251  current_lineno = -1;		// no line numbers
252  if (freopen(argv[optind + 2], "w", stdout) == 0)
253    fatal("cannot open `%1': %2", argv[optind + 2], strerror(errno));
254  current_filename = argv[optind];
255  printf("name %s\n", xbasename(argv[optind + 2]));
256  if (special_flag)
257    printf("special\n");
258  read_tags(f);
259  check_type();
260  check_units(f);
261  if (debug_flag)
262    dump_tags(f);
263  read_char_table(f);
264  output_spacewidth();
265  output_slant();
266  read_and_output_pcltypeface(f);
267  output_pclproportional();
268  output_pclweight();
269  output_pclstyle();
270  read_symbol_sets(f);
271  output_ligatures();
272  read_and_output_kernpairs(f);
273  output_charset();
274  return 0;
275}
276
277static
278void usage(FILE *stream)
279{
280  fprintf(stream, "usage: %s [-s] [-i n] tfm_file map_file output_font\n",
281	  program_name);
282}
283static
284void usage()
285{
286  usage(stderr);
287  exit(1);
288}
289
290File::File(const char *s)
291{
292  // We need to read the file in binary mode because hpftodit relies
293  // on byte counts.
294  int fd = open(s, O_RDONLY | O_BINARY);
295  if (fd < 0)
296    fatal("cannot open `%1': %2", s, strerror(errno));
297  current_filename = s;
298  struct stat sb;
299  if (fstat(fd, &sb) < 0)
300    fatal("cannot stat: %1", strerror(errno));
301  if (!S_ISREG(sb.st_mode))
302    fatal("not a regular file");
303  buf_ = new unsigned char[sb.st_size];
304  long nread = read(fd, buf_, sb.st_size);
305  if (nread < 0)
306    fatal("read error: %1", strerror(errno));
307  if (nread != sb.st_size)
308    fatal("read unexpected number of bytes");
309  ptr_ = buf_;
310  end_ = buf_ + sb.st_size;
311}
312
313void File::skip(int n)
314{
315  if (end_ - ptr_ < n)
316    fatal("unexpected end of file");
317  ptr_ += n;
318}
319
320void File::seek(uint32 n)
321{
322  if ((uint32)(end_ - buf_) < n)
323    fatal("unexpected end of file");
324  ptr_ = buf_ + n;
325}
326
327byte File::get_byte()
328{
329  if (ptr_ >= end_)
330    fatal("unexpected end of file");
331  return *ptr_++;
332}
333
334uint16 File::get_uint16()
335{
336  if (end_ - ptr_ < 2)
337    fatal("unexpected end of file");
338  uint16 n = *ptr_++;
339  return n + (*ptr_++ << 8);
340}
341
342uint32 File::get_uint32()
343{
344  if (end_ - ptr_ < 4)
345    fatal("unexpected end of file");
346  uint32 n = *ptr_++;
347  for (int i = 0; i < 3; i++)
348    n += *ptr_++ << (i + 1)*8;
349  return n;
350}
351
352static
353void read_tags(File &f)
354{
355  if (f.get_byte() != 'I' || f.get_byte() != 'I')
356    fatal("not an Intel format TFM file");
357  f.skip(6);
358  uint16 ntags = f.get_uint16();
359  entry dummy;
360  for (uint16 i = 0; i < ntags; i++) {
361    uint16 tag = f.get_uint16();
362    entry *p;
363    if (min_tag <= tag && tag <= max_tag)
364      p = tags + (tag - min_tag);
365    else
366      p = &dummy;
367    p->present = 1;
368    p->type = f.get_uint16();
369    p->count = f.get_uint32();
370    p->value = f.get_uint32();
371  }
372}
373
374static
375void check_type()
376{
377  require_tag(type_tag);
378  if (tag_info(type_tag).value != 0) {
379    if (tag_info(type_tag).value == 2)
380      fatal("cannot handle TrueType tfm files");
381    fatal("unknown type tag %1", int(tag_info(type_tag).value));
382  }
383}
384
385static
386void check_units(File &f)
387{
388  require_tag(design_units_per_em_tag);
389  f.seek(tag_info(design_units_per_em_tag).value);
390  uint32 num = f.get_uint32();
391  uint32 den = f.get_uint32();
392  if (num != 8782 || den != 1)
393    fatal("design units per em != 8782/1");
394  require_tag(inches_per_point_tag);
395  f.seek(tag_info(inches_per_point_tag).value);
396  num = f.get_uint32();
397  den = f.get_uint32();
398  if (num != 100 || den != 7231)
399    fatal("inches per point not 100/7231");
400}
401
402static
403void require_tag(tag_type t)
404{
405  if (!tag_info(t).present)
406    fatal("tag %1 missing", int(t));
407}
408
409static
410void output_spacewidth()
411{
412  require_tag(word_spacing_tag);
413  printf("spacewidth %d\n", scale(tag_info(word_spacing_tag).value));
414}
415
416static
417void read_symbol_sets(File &f)
418{
419  uint32 symbol_set_dir_length = tag_info(symbol_set_tag).count;
420  n_symbol_sets = symbol_set_dir_length/14;
421  symbol_set_table = new symbol_set[n_symbol_sets];
422  unsigned int i;
423  for (i = 0; i < n_symbol_sets; i++) {
424    f.seek(tag_info(symbol_set_tag).value + i*14);
425    (void)f.get_uint32();
426    uint32 off1 = f.get_uint32();
427    uint32 off2 = f.get_uint32();
428    (void)f.get_uint16();		// what's this for?
429    f.seek(off1);
430    unsigned int j;
431    uint16 kind = 0;
432    for (j = 0; j < off2 - off1; j++) {
433      unsigned char c = f.get_byte();
434      if ('0' <= c && c <= '9')
435	kind = kind*10 + (c - '0');
436      else if ('A' <= c && c <= 'Z')
437	kind = kind*32 + (c - 64);
438    }
439    symbol_set_table[i].select = kind;
440    for (j = 0; j < 256; j++)
441      symbol_set_table[i].index[j] = f.get_uint16();
442  }
443  for (i = 0; i < nchars; i++)
444    char_table[i].symbol_set = NO_SYMBOL_SET;
445
446  uint16 *symbol_set_selectors = (special_flag
447				  ? special_symbol_sets
448				  : text_symbol_sets);
449  for (i = 0; symbol_set_selectors[i] != 0; i++) {
450    unsigned int j;
451    for (j = 0; j < n_symbol_sets; j++)
452      if (symbol_set_table[j].select == symbol_set_selectors[i])
453	break;
454    if (j < n_symbol_sets) {
455      for (int k = 0; k < 256; k++) {
456	uint16 index = symbol_set_table[j].index[k];
457	if (index != 0xffff
458	    && char_table[index].symbol_set == NO_SYMBOL_SET) {
459	  char_table[index].symbol_set = symbol_set_table[j].select;
460	  char_table[index].code = k;
461	}
462      }
463    }
464  }
465}
466
467static
468void read_char_table(File &f)
469{
470  require_tag(msl_tag);
471  nchars = tag_info(msl_tag).count;
472  char_table = new char_info[nchars];
473
474  f.seek(tag_info(msl_tag).value);
475  uint32 i;
476  for (i = 0; i < nchars; i++)
477    char_table[i].msl = f.get_uint16();
478
479  require_tag(width_tag);
480  f.seek(tag_info(width_tag).value);
481  for (i = 0; i < nchars; i++)
482    char_table[i].width = f.get_uint16();
483
484  require_tag(ascent_tag);
485  f.seek(tag_info(ascent_tag).value);
486  for (i = 0; i < nchars; i++) {
487    char_table[i].ascent = f.get_uint16();
488    if (char_table[i].ascent < 0)
489      char_table[i].ascent = 0;
490  }
491
492  require_tag(descent_tag);
493  f.seek(tag_info(descent_tag).value);
494  for (i = 0; i < nchars; i++) {
495    char_table[i].descent = f.get_uint16();
496    if (char_table[i].descent > 0)
497      char_table[i].descent = 0;
498  }
499
500  require_tag(left_extent_tag);
501  f.seek(tag_info(left_extent_tag).value);
502  for (i = 0; i < nchars; i++)
503    char_table[i].left_extent = int16(f.get_uint16());
504
505  require_tag(right_extent_tag);
506  f.seek(tag_info(right_extent_tag).value);
507  for (i = 0; i < nchars; i++)
508    char_table[i].right_extent = f.get_uint16();
509}
510
511static
512void output_pclweight()
513{
514  require_tag(stroke_weight_tag);
515  int stroke_weight = tag_info(stroke_weight_tag).value;
516  int pcl_stroke_weight;
517  if (stroke_weight < 128)
518    pcl_stroke_weight = -3;
519  else if (stroke_weight == 128)
520    pcl_stroke_weight = 0;
521  else if (stroke_weight <= 145)
522    pcl_stroke_weight = 1;
523  else if (stroke_weight <= 179)
524    pcl_stroke_weight = 3;
525  else
526    pcl_stroke_weight = 4;
527  printf("pclweight %d\n", pcl_stroke_weight);
528}
529
530static
531void output_pclproportional()
532{
533  require_tag(spacing_tag);
534  printf("pclproportional %d\n", tag_info(spacing_tag).value == 0);
535}
536
537static
538void read_and_output_pcltypeface(File &f)
539{
540  printf("pcltypeface ");
541  require_tag(typeface_tag);
542  f.seek(tag_info(typeface_tag).value);
543  for (uint32 i = 0; i < tag_info(typeface_tag).count; i++) {
544    unsigned char c = f.get_byte();
545    if (c == '\0')
546      break;
547    putchar(c);
548  }
549  printf("\n");
550}
551
552static
553void output_pclstyle()
554{
555  unsigned pcl_style = 0;
556  // older tfms don't have the posture tag
557  if (tag_info(posture_tag).present) {
558    if (tag_info(posture_tag).value)
559      pcl_style |= 1;
560  }
561  else {
562    require_tag(slant_tag);
563    if (tag_info(slant_tag).value != 0)
564      pcl_style |= 1;
565  }
566  require_tag(appearance_width_tag);
567  if (tag_info(appearance_width_tag).value < 100) // guess
568    pcl_style |= 4;
569  printf("pclstyle %d\n", pcl_style);
570}
571
572static
573void output_slant()
574{
575  require_tag(slant_tag);
576  int slant = int16(tag_info(slant_tag).value);
577  if (slant != 0)
578    printf("slant %f\n", slant/100.0);
579}
580
581static
582void output_ligatures()
583{
584  // don't use ligatures for fixed space font
585  require_tag(spacing_tag);
586  if (tag_info(spacing_tag).value != 0)
587    return;
588  static const char *ligature_names[] = {
589    "fi", "fl", "ff", "ffi", "ffl"
590    };
591
592  static const char *ligature_chars[] = {
593    "fi", "fl", "ff", "Fi", "Fl"
594    };
595
596  unsigned ligature_mask = 0;
597  unsigned int i;
598  for (i = 0; i < nchars; i++) {
599    uint16 msl = char_table[i].msl;
600    if (msl < msl_name_table_size
601	&& char_table[i].symbol_set != NO_SYMBOL_SET) {
602      for (name_list *p = msl_name_table[msl]; p; p = p->next)
603	for (unsigned int j = 0; j < SIZEOF(ligature_chars); j++)
604	  if (strcmp(p->name, ligature_chars[j]) == 0) {
605	    ligature_mask |= 1 << j;
606	    break;
607	  }
608      }
609    }
610  if (ligature_mask) {
611    printf("ligatures");
612    for (i = 0; i < SIZEOF(ligature_names); i++)
613      if (ligature_mask & (1 << i))
614	printf(" %s", ligature_names[i]);
615    printf(" 0\n");
616  }
617}
618
619static
620void read_and_output_kernpairs(File &f)
621{
622  if (tag_info(pair_kern_tag).present) {
623    printf("kernpairs\n");
624    f.seek(tag_info(pair_kern_tag).value);
625    uint16 n_pairs = f.get_uint16();
626    for (int i = 0; i < n_pairs; i++) {
627      uint16 i1 = f.get_uint16();
628      uint16 i2 = f.get_uint16();
629      int16 val = int16(f.get_uint16());
630      if (char_table[i1].symbol_set != NO_SYMBOL_SET
631	  && char_table[i2].symbol_set != NO_SYMBOL_SET
632	  && char_table[i1].msl < msl_name_table_size
633	  && char_table[i2].msl < msl_name_table_size) {
634	for (name_list *p = msl_name_table[char_table[i1].msl];
635	     p;
636	     p = p->next)
637	  for (name_list *q = msl_name_table[char_table[i2].msl];
638	       q;
639	       q = q->next)
640	    printf("%s %s %d\n", p->name, q->name, scale(val));
641      }
642    }
643  }
644}
645
646static
647void output_charset()
648{
649  require_tag(slant_tag);
650  double slant_angle = int16(tag_info(slant_tag).value)*PI/18000.0;
651  double slant = sin(slant_angle)/cos(slant_angle);
652
653  require_tag(x_height_tag);
654  require_tag(lower_ascent_tag);
655  require_tag(lower_descent_tag);
656
657  printf("charset\n");
658  unsigned int i;
659  for (i = 0; i < nchars; i++) {
660    uint16 msl = char_table[i].msl;
661    if (msl < msl_name_table_size
662	&& msl_name_table[msl]) {
663      if (char_table[i].symbol_set != NO_SYMBOL_SET) {
664	printf("%s\t%d,%d",
665	       msl_name_table[msl]->name,
666	       scale(char_table[i].width),
667	       scale(char_table[i].ascent));
668	int depth = scale(- char_table[i].descent);
669	if (depth < 0)
670	  depth = 0;
671	int italic_correction = 0;
672	int left_italic_correction = 0;
673	int subscript_correction = 0;
674	if (italic_flag) {
675	  italic_correction = scale(char_table[i].right_extent
676				    - char_table[i].width
677				    + italic_sep);
678	  if (italic_correction < 0)
679	    italic_correction = 0;
680	  subscript_correction = int((tag_info(x_height_tag).value
681				      * slant * .8) + .5);
682	  if (subscript_correction > italic_correction)
683	    subscript_correction = italic_correction;
684	  left_italic_correction = scale(italic_sep
685					 - char_table[i].left_extent);
686	}
687	if (subscript_correction != 0)
688	  printf(",%d,%d,%d,%d",
689		 depth, italic_correction, left_italic_correction,
690		 subscript_correction);
691	else if (left_italic_correction != 0)
692	  printf(",%d,%d,%d", depth, italic_correction, left_italic_correction);
693	else if (italic_correction != 0)
694	  printf(",%d,%d", depth, italic_correction);
695	else if (depth != 0)
696	  printf(",%d", depth);
697	// This is fairly arbitrary.  Fortunately it doesn't much matter.
698	unsigned type = 0;
699	if (char_table[i].ascent > (int16(tag_info(lower_ascent_tag).value)*9)/10)
700	  type |= 2;
701	if (char_table[i].descent < (int16(tag_info(lower_descent_tag).value)*9)/10)
702	  type |= 1;
703	printf("\t%d\t%d\n",
704	       type,
705	       char_table[i].symbol_set*256 + char_table[i].code);
706	for (name_list *p = msl_name_table[msl]->next; p; p = p->next)
707	  printf("%s\t\"\n", p->name);
708      }
709      else
710	warning("MSL %1 not in any of the searched symbol sets", msl);
711    }
712  }
713}
714
715static
716void dump_tags(File &f)
717{
718  int i;
719  for (i = min_tag; i <= max_tag; i++) {
720    enum tag_type t = tag_type(i);
721    if (tag_info(t).present) {
722      fprintf(stderr,
723	      "%d %d %d %d\n", i, tag_info(t).type, tag_info(t).count,
724	      tag_info(t).value);
725      if (tag_info(t).type == FLOAT_TYPE
726	  && tag_info(t).count == 1) {
727	f.seek(tag_info(t).value);
728	uint32 num = f.get_uint32();
729	uint32 den = f.get_uint32();
730	fprintf(stderr, "(%u/%u = %g)\n", num, den, (double)num/den);
731      }
732    }
733  }
734}
735
736static
737int read_map(const char *file)
738{
739  errno = 0;
740  FILE *fp = fopen(file, "r");
741  if (!fp) {
742    error("can't open `%1': %2", file, strerror(errno));
743    return 0;
744  }
745  current_filename = file;
746  char buf[512];
747  current_lineno = 0;
748  while (fgets(buf, int(sizeof(buf)), fp)) {
749    current_lineno++;
750    char *ptr = buf;
751    while (csspace(*ptr))
752      ptr++;
753    if (*ptr == '\0' || *ptr == '#')
754      continue;
755    ptr = strtok(ptr, " \n\t");
756    if (!ptr)
757      continue;
758    int n;
759    if (sscanf(ptr, "%d", &n) != 1) {
760      error("bad map file");
761      fclose(fp);
762      return 0;
763    }
764    if (n < 0) {
765      error("negative code");
766      fclose(fp);
767      return 0;
768    }
769    if ((size_t)n >= msl_name_table_size) {
770      size_t old_size = msl_name_table_size;
771      name_list **old_table = msl_name_table;
772      msl_name_table_size = n + 256;
773      msl_name_table = new name_list *[msl_name_table_size];
774      if (old_table) {
775	memcpy(msl_name_table, old_table, old_size*sizeof(name_list *));
776	a_delete old_table;
777      }
778      for (size_t i = old_size; i < msl_name_table_size; i++)
779	msl_name_table[i] = 0;
780    }
781    ptr = strtok(0, " \n\t");
782    if (!ptr) {
783      error("missing names");
784      fclose(fp);
785      return 0;
786    }
787    for (; ptr; ptr = strtok(0, " \n\t"))
788      msl_name_table[n] = new name_list(ptr, msl_name_table[n]);
789  }
790  fclose(fp);
791  return 1;
792}
793
794static
795const char *xbasename(const char *s)
796{
797  // DIR_SEPS[] are possible directory separator characters, see
798  // nonposix.h.  We want the rightmost separator of all possible
799  // ones.  Example: d:/foo\\bar.
800  const char *b = strrchr(s, DIR_SEPS[0]), *b1;
801  const char *sep = &DIR_SEPS[1];
802
803  while (*sep)
804    {
805      b1 = strrchr(s, *sep);
806      if (b1 && (!b || b1 > b))
807	b = b1;
808      sep++;
809    }
810  return b ? b + 1 : s;
811}
812