1 /* t1unmac/unpost
2  *
3  * This program converts Macintosh Type 1 fonts stored in MacBinary (I or II),
4  * AppleSingle, AppleDouble, BinHex, or raw resource fork format to PFA and
5  * PFB formats.
6  *
7  * Copyright (c) 1992 by I. Lee Hetherington, all rights reserved.
8  *
9  * Permission is hereby granted to use, modify, and distribute this program
10  * for any purpose provided this copyright notice and the one below remain
11  * intact.
12  *
13  * I. Lee Hetherington (ilh@lcs.mit.edu)
14  *
15  * 1.5 and later versions contain changes by, and are maintained by,
16  * Eddie Kohler <kohler@icir.org>.
17  *
18  * New change log in `NEWS'. Old change log:
19  *
20  * Revision 1.2  92/06/23  10:57:33  ilh
21  * MSDOS porting by Kai-Uwe Herbing (herbing@netmbx.netmbx.de)
22  * incoporated.
23  *
24  * Revision 1.1  92/05/22  12:07:49  ilh
25  * initial version
26  *
27  * Ported to Microsoft C/C++ Compiler and MS-DOS operating system by
28  * Kai-Uwe Herbing (herbing@netmbx.netmbx.de) on June 12, 1992. Code
29  * specific to the MS-DOS version is encapsulated with #ifdef _MSDOS
30  * ... #endif, where _MSDOS is an identifier, which is automatically
31  * defined, if you compile with the Microsoft C/C++ Compiler.
32  * */
33 
34 /* Note: this is ANSI C. */
35 
36 #ifdef HAVE_CONFIG_H
37 # include <config.h>
38 #endif
39 #if defined(_MSDOS) || defined(_WIN32)
40 # include <fcntl.h>
41 # include <getopt.h>
42 # include <io.h>
43 #endif
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <limits.h>
47 #include <stdarg.h>
48 #include <string.h>
49 #include <errno.h>
50 #include <ctype.h>
51 #include <lcdf/clp.h>
52 #include "t1lib.h"
53 
54 #ifdef __cplusplus
55 extern "C" {
56 #endif
57 
58 /* for PFB block buffering */
59 void fatal_error(const char *message, ...);
60 void error(const char *message, ...);
61 
62 
63 /* Some functions to read one, two, three, and four byte integers in 68000
64    byte order (most significant byte first). */
65 
66 static int
read_one(FILE * fi)67 read_one(FILE *fi)
68 {
69   return getc(fi);
70 }
71 
72 static int
read_two(FILE * fi)73 read_two(FILE *fi)
74 {
75   int val;
76 
77   val = getc(fi);
78   val = (val << 8) | getc(fi);
79 
80   return val;
81 }
82 
83 static int32_t
read_three(FILE * fi)84 read_three(FILE *fi)
85 {
86   int32_t val;
87 
88   val = getc(fi);
89   val = (val << 8) | getc(fi);
90   val = (val << 8) | getc(fi);
91 
92   return val;
93 }
94 
95 static int32_t
read_four(FILE * fi)96 read_four(FILE *fi)
97 {
98   int32_t val;
99 
100   val = getc(fi);
101   val = (val << 8) | getc(fi);
102   val = (val << 8) | getc(fi);
103   val = (val << 8) | getc(fi);
104 
105   return val;
106 }
107 
108 /* reposition a file with error messages */
109 
110 static void
reposition(FILE * fi,int32_t absolute)111 reposition(FILE *fi, int32_t absolute)
112 {
113   if (fseek(fi, absolute, 0) == -1)
114     fatal_error("can't seek to position %d\n\
115    (The Mac file may be corrupted, or you may need the `-r' option.)",
116 		absolute);
117 }
118 
119 static int blocklen = -1;
120 
121 static int hex_column = 0;	/* current column of hex ASCII output */
122 
123 static void
output_hex_byte(FILE * fo,int b)124 output_hex_byte(FILE *fo, int b)
125 {
126   static const char *hex = "0123456789abcdef";
127 
128   if (hex_column >= blocklen) {
129     putc('\n', fo);
130     hex_column = 0;
131   }
132   putc(hex[b >> 4], fo);
133   putc(hex[b & 0xf], fo);
134   hex_column += 2;
135 }
136 
137 /* Function to extract a particular POST resource.  Offset points to the four
138    byte length which is followed by the data.  The first byte of the POST data
139    specifies resource type: 1 for ASCII, 2 for binary, and 5 for end.  The
140    second byte is always zero. */
141 
142 
143 /* Function to write four byte length to PFB file: least significant byte
144    first. */
145 
146 static int
extract_data(FILE * fi,FILE * fo,struct pfb_writer * w,int32_t offset,int pfb)147 extract_data(FILE *fi, FILE *fo, struct pfb_writer *w, int32_t offset, int pfb)
148 {
149   enum PS_type { PS_ascii = 1, PS_binary = 2, PS_end = 5 };
150   static int last_type = -1;
151   static int skip_newline = 0;
152   int32_t len;
153   int more = 1;
154   int i, c;
155 
156   reposition(fi, offset);
157   len = read_four(fi) - 2;	/* subtract type field */
158 
159   switch ((enum PS_type)read_one(fi)) {
160 
161    case PS_ascii: {
162      (void) read_one(fi);
163      if (last_type != PFB_ASCII && pfb) {
164        pfb_writer_output_block(w);
165        w->blocktyp = PFB_ASCII;
166      }
167      for (i = 0; i < len; i++) {
168        c = read_one(fi);
169        if (c == '\n' && skip_newline) {
170 	 skip_newline = 0;
171 	 continue;
172        }
173        if (c == '\r') {
174 	 c = '\n';
175 	 skip_newline = 1;
176        } else
177 	 skip_newline = 0;
178        if (pfb)
179 	 PFB_OUTPUT_BYTE(w, c);
180        else
181 	 putc(c, fo);
182      }
183      last_type = PFB_ASCII;
184      break;
185    }
186 
187    case PS_binary: {
188      (void) read_one(fi);
189      if (last_type != PFB_BINARY && pfb) {
190        pfb_writer_output_block(w);
191        w->blocktyp = PFB_BINARY;
192      } else if (last_type != PFB_BINARY)
193        hex_column = 0;
194      if (pfb) {
195        while (len--)
196 	 PFB_OUTPUT_BYTE(w, read_one(fi));
197      } else {
198        while (len--)
199 	 output_hex_byte(fo, read_one(fi));
200      }
201      last_type = PFB_BINARY;
202      break;
203    }
204 
205    case PS_end:
206     more = 0;
207     break;
208 
209   }
210 
211   return more;
212 }
213 
214 
215 /*****
216  * Command line
217  **/
218 
219 #define OUTPUT_OPT	301
220 #define VERSION_OPT	302
221 #define HELP_OPT	303
222 #define PFB_OPT		304
223 #define PFA_OPT		305
224 #define MACBINARY_OPT	306
225 #define RAW_OPT		307
226 #define LINE_LEN_OPT	308
227 #define APPLEDOUBLE_OPT	309
228 #define BINHEX_OPT	310
229 
230 static Clp_Option options[] = {
231   { "applesingle", 0, APPLEDOUBLE_OPT, 0, 0 },
232   { "appledouble", 0, APPLEDOUBLE_OPT, 0, 0 },
233   { "binhex", 0, BINHEX_OPT, 0, 0 },
234   { "block-length", 0, LINE_LEN_OPT, Clp_ArgUnsigned, 0 },
235   { "help", 0, HELP_OPT, 0, 0 },
236   { "line-length", 'l', LINE_LEN_OPT, Clp_ArgUnsigned, 0 },
237   { "macbinary", 0, MACBINARY_OPT, 0, 0 },
238   { "output", 'o', OUTPUT_OPT, Clp_ArgString, 0 },
239   { "pfa", 'a', PFA_OPT, 0, 0 },
240   { "pfb", 'b', PFB_OPT, 0, 0 },
241   { "raw", 'r', RAW_OPT, 0, 0 },
242   { "version", 0, VERSION_OPT, 0, 0 },
243 };
244 static const char *program_name;
245 
246 void
fatal_error(const char * message,...)247 fatal_error(const char *message, ...)
248 {
249   va_list val;
250   va_start(val, message);
251   fprintf(stderr, "%s: ", program_name);
252   vfprintf(stderr, message, val);
253   putc('\n', stderr);
254   exit(1);
255 }
256 
257 void
error(const char * message,...)258 error(const char *message, ...)
259 {
260   va_list val;
261   va_start(val, message);
262   fprintf(stderr, "%s: ", program_name);
263   vfprintf(stderr, message, val);
264   putc('\n', stderr);
265 }
266 
267 void
short_usage(void)268 short_usage(void)
269 {
270   fprintf(stderr, "Usage: %s [OPTION]... INPUT [OUTPUT]\n\
271 Try `%s --help' for more information.\n",
272 	  program_name, program_name);
273 }
274 
275 void
usage(void)276 usage(void)
277 {
278   printf("\
279 `T1unmac' extracts a PostScript Type 1 font from a Macintosh font file. It can\n\
280 read MacBinary, AppleSingle, AppleDouble, or BinHex files, or raw Macintosh\n\
281 resource forks. The result is written to the standard output unless an OUTPUT\n\
282 file is given.\n\
283 \n\
284 Usage: %s [OPTION]... INPUT [OUTPUT]\n\
285 \n\
286 Options:\n\
287   -r, --raw                   Input is a raw Macintosh resource fork.\n\
288       --macbinary             Input is in MacBinary format.\n\
289       --applesingle           Input is in AppleSingle format.\n\
290       --appledouble           Input is in AppleDouble format.\n\
291       --binhex                Input is in BinHex format.\n\
292   -a, --pfa                   Output font in ASCII (PFA) format.\n\
293   -b, --pfb                   Output font in binary (PFB) format. This is\n\
294                               the default.\n\
295   -l, --block-length NUM      Set max block length for PFB output.\n\
296   -l, --line-length NUM       Set max encrypted line length for PFA output.\n\
297   -o, --output FILE           Write output to FILE.\n\
298   -h, --help                  Print this message and exit.\n\
299       --version               Print version number and warranty and exit.\n\
300 \n\
301 Report bugs to <kohler@icir.org>.\n", program_name);
302 }
303 
304 
305 static const char *
check_macbinary(FILE * ifp)306 check_macbinary(FILE *ifp)
307 {
308   int i, j;
309   char buf[124];
310 
311   /* check "version" bytes at offsets 0 and 74 */
312   reposition(ifp, 0);
313   if (read_one(ifp) != 0)
314     return "bad version byte";
315   reposition(ifp, 74);
316   if (read_one(ifp) != 0)
317     return "bad version byte";
318 
319 #if 0
320   /* write out bullshit */
321   {int t;
322     reposition(ifp, 65);
323     t = read_four(ifp);
324     fprintf(stderr, "Type %c%c%c%c\n", (t>>24)&255, (t>>16)&255, (t>>8)&255, t&255);
325     t = read_four(ifp);
326     fprintf(stderr, "Creator %c%c%c%c\n", (t>>24)&255, (t>>16)&255, (t>>8)&255, t&255);
327     t = read_one(ifp);
328     fprintf(stderr, "Finder flags 1 %02x\n", t);
329     read_one(ifp);
330     t = read_two(ifp);
331     fprintf(stderr, "horizpos %04x\n", t);
332     t = read_two(ifp);
333     fprintf(stderr, "vertpos %04x\n", t);
334     t = read_two(ifp);
335     fprintf(stderr, "folder id %d\n", t);
336     t = read_one(ifp);
337     fprintf(stderr, "protected? %x\n", t);
338     t = read_one(ifp);
339     t = read_four(ifp);
340     fprintf(stderr, "data len %d\n", t);
341     t = read_four(ifp);
342     fprintf(stderr, "rsrc len %d\n", t);
343     t = read_four(ifp);
344     {
345       struct tm *crap;
346       fprintf(stderr, "creation date %x\n", t);
347       t -= 2082844800;
348       fprintf(stderr, "   %s\n", ctime(&t));
349       t = read_four(ifp);
350       fprintf(stderr, "modification date %x\n", t);
351       t -= 2082844800;
352       fprintf(stderr, "   %s\n", ctime(&t));
353       t = read_two(ifp);
354     }
355     fprintf(stderr, "getinfo len %d\n", t);
356     t = read_one(ifp);
357     fprintf(stderr, "finderflags 2 %02x\n", t);
358     reposition(ifp, 116);
359     t = read_four(ifp);
360     fprintf(stderr, "total len %d\n", t);
361     t = read_two(ifp);
362     fprintf(stderr, "secondary header len %d\n", t);
363     t = read_one(ifp);
364     fprintf(stderr, "version %d\n", t);
365     t = read_one(ifp);
366     fprintf(stderr, "version %d\n", t);
367   }
368 #endif
369 
370   /* check file length */
371   reposition(ifp, 1);
372   i = read_one(ifp);
373   if (i > 63)
374     return "bad length";
375   reposition(ifp, 83);
376   i = read_four(ifp);
377   j = read_four(ifp);
378   if (i < 0 || j < 0 || i >= 0x800000 || j >= 0x800000)
379     return "bad length";
380 
381   /* check CRC */
382   reposition(ifp, 0);
383   fread(buf, 1, 124, ifp);
384   if (crcbuf(0, 124, buf) != read_two(ifp)) {
385     reposition(ifp, 82);
386     if (read_one(ifp) != 0)
387       return "bad checksum";
388   }
389 
390   return 0;
391 }
392 
393 #define APPLESINGLE_MAGIC 0x00051600
394 #define APPLEDOUBLE_MAGIC 0x00051607
395 
396 const char *
check_appledouble(FILE * ifp)397 check_appledouble(FILE *ifp)
398 {
399   int i;
400   reposition(ifp, 0);
401   i = read_four(ifp);
402   if (i != APPLEDOUBLE_MAGIC && i != APPLESINGLE_MAGIC)
403     return "bad magic number";
404 
405   return 0;
406 }
407 
408 const char *
translate_binhex(FILE * f,FILE * tmpf)409 translate_binhex(FILE *f, FILE *tmpf)
410 {
411   int i, c = 0, last_char, after_x90, bits, bitpos;
412   unsigned char value_table[256];
413 
414   /* prepare value table */
415   {
416     const char *table = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
417     for (i = 0; i < 256; i++)
418       value_table[i] = 255;
419     for (i = 0; *table; i++, table++)
420       value_table[(unsigned char)*table] = i;
421   }
422 
423   /* skip to comment */
424   {
425     const char *comment = "(This file must be converted with BinHex";
426     while (!feof(f)) {
427       const char *s;
428       for (s = comment; *s; s++) {
429 	c = getc(f);
430 	if (c != *s)
431 	  break;
432       }
433       /* skip to end of line */
434       while (c >= 0 && c != '\n' && c != '\r')
435 	c = getc(f);
436       /* stop if read comment */
437       if (!*s)
438 	goto found_comment;
439     }
440     /* failed */
441     return "no comment";
442   }
443 
444  found_comment:
445   /* skip spaces, look for ':' */
446   for (c = ' '; isspace(c); c = getc(f)) ;
447   if (c != ':')
448     return "no file-start character";
449 
450   /* found ':', process until you find another ':' */
451   last_char = -1;
452   after_x90 = bits = 0;
453   bitpos = 10;
454   for (c = getc(f); c >= 0; c = getc(f))
455     if (!isspace(c)) {
456       /* add 6 bits to bits */
457       if (value_table[c] == 255)
458 	break;
459       bits |= (value_table[c] << bitpos);
460       bitpos -= 6;
461 
462       /* output character(s) */
463       if (bitpos <= 2) {
464 	int d = bits >> 8;
465 	bits = (bits << 8) & 0xFF00;
466 	bitpos += 8;
467 	if (after_x90) {
468 	  /* handle compression */
469 	  if (d == 0) {
470 	    last_char = 0x90;
471 	    putc(0x90, tmpf);
472 	  } else
473 	    for (i = 1; i < d; i++)
474 	      putc(last_char, tmpf);
475 	  after_x90 = 0;
476 	} else if (d == 0x90)
477 	  after_x90 = 1;
478 	else {
479 	  last_char = d;
480 	  putc(d, tmpf);
481 	}
482       }
483     }
484 
485   if (c < 0)
486     return "unexpected EOF";
487   else if (c != ':')
488     return "bad character";
489 
490   fflush(tmpf);
491   return 0;
492 }
493 
494 int
check_binhex_crc(FILE * f,int offset,int length)495 check_binhex_crc(FILE *f, int offset, int length)
496 {
497   int crc = 0;
498   char buf[2048];
499   reposition(f, offset);
500   while (length > 0) {
501     int n = (length < 2048 ? length : 2048);
502     fread(buf, 1, n, f);
503     crc = crcbuf(crc, n, buf);
504     length -= n;
505   }
506   return crc == 0;
507 }
508 
509 const char *
check_binhex(FILE * f)510 check_binhex(FILE *f)
511 {
512   int fname_len, data_len, rsrc_len, off;
513 
514   /* check lengths */
515   reposition(f, 0);
516   fname_len = read_one(f);
517   if (fname_len < 1 || fname_len > 63)
518     return "bad length";
519   reposition(f, 1 + fname_len + 11);
520   data_len = read_four(f);
521   rsrc_len = read_four(f);
522   if (data_len < 0 || rsrc_len < 0 || data_len >= 0x800000 || rsrc_len >= 0x800000)
523     return "bad length";
524 
525   /* check version */
526   reposition(f, 1 + fname_len);
527   if (read_one(f) != 0)
528     return "bad version";
529 
530   /* check CRC */
531   off = 1 + fname_len + 21;
532   if (!check_binhex_crc(f, 0, off))
533     return "bad header CRC";
534   if (!check_binhex_crc(f, off, data_len + 2))
535     return "bad data CRC";
536   if (!check_binhex_crc(f, off + data_len + 2, rsrc_len + 2))
537     return "bad resource fork CRC";
538 
539   return 0;
540 }
541 
542 #ifdef __cplusplus
543 }
544 #endif
545 
546 
547 int
main(int argc,char * argv[])548 main(int argc, char *argv[])
549 {
550   FILE *ifp = 0;
551   FILE *ofp = 0;
552   struct pfb_writer w;
553   const char *ifp_name = "<stdin>";
554   int32_t res_offset, res_data_offset, res_map_offset, type_list_offset;
555   int32_t post_type;
556   int num_types, num_extracted = 0, pfb = 1;
557   int raw = 0, appledouble = 0, binhex = 0, macbinary = 0;
558 
559   Clp_Parser *clp =
560     Clp_NewParser(argc, (const char * const *)argv, sizeof(options) / sizeof(options[0]), options);
561   program_name = Clp_ProgramName(clp);
562 
563   /* interpret command line arguments using CLP */
564   while (1) {
565     int opt = Clp_Next(clp);
566     switch (opt) {
567 
568      case RAW_OPT:
569       raw = 1;
570       appledouble = binhex = macbinary = 0;
571       break;
572 
573      case MACBINARY_OPT:
574       macbinary = 1;
575       raw = appledouble = binhex = 0;
576       break;
577 
578      case APPLEDOUBLE_OPT:
579       appledouble = 1;
580       raw = binhex = macbinary = 0;
581       break;
582 
583      case BINHEX_OPT:
584       binhex = 1;
585       raw = appledouble = macbinary = 0;
586       break;
587 
588      output_file:
589      case OUTPUT_OPT:
590       if (ofp)
591 	fatal_error("output file already specified");
592       if (strcmp(clp->arg, "-") == 0)
593 	ofp = stdout;
594       else {
595 	ofp = fopen(clp->arg, "w");
596 	if (!ofp) fatal_error("%s: %s", clp->arg, strerror(errno));
597       }
598       break;
599 
600      case PFB_OPT:
601       pfb = 1;
602       break;
603 
604      case PFA_OPT:
605       pfb = 0;
606       break;
607 
608      case LINE_LEN_OPT:
609       blocklen = clp->val.i;
610       break;
611 
612      case HELP_OPT:
613       usage();
614       exit(0);
615       break;
616 
617      case VERSION_OPT:
618       printf("t1unmac (LCDF t1utils) %s\n", VERSION);
619       printf("Copyright (C) 1992-2003 I. Lee Hetherington, Eddie Kohler et al.\n\
620 This is free software; see the source for copying conditions.\n\
621 There is NO warranty, not even for merchantability or fitness for a\n\
622 particular purpose.\n");
623       exit(0);
624       break;
625 
626      case Clp_NotOption:
627       if (ifp && ofp)
628 	fatal_error("too many arguments");
629       else if (ifp)
630 	goto output_file;
631       if (strcmp(clp->arg, "-") == 0)
632 	ifp = stdin;
633       else {
634 	ifp_name = clp->arg;
635 	ifp = fopen(clp->arg, "rb");
636 	if (!ifp) fatal_error("%s: %s", clp->arg, strerror(errno));
637       }
638       break;
639 
640      case Clp_Done:
641       goto done;
642 
643      case Clp_BadOption:
644       short_usage();
645       exit(1);
646       break;
647 
648     }
649   }
650 
651  done:
652   if (!ifp) ifp = stdin;
653   if (!ofp) ofp = stdout;
654 
655 #if defined(_MSDOS) || defined(_WIN32)
656   _setmode(_fileno(ifp), _O_BINARY);
657   /* If we are processing a PFB (binary) output */
658   /* file, we must set its file mode to binary. */
659   if (pfb)
660     _setmode(_fileno(ofp), _O_BINARY);
661 #endif
662 
663   if (pfb)
664     init_pfb_writer(&w, blocklen, ofp);
665   else {
666     if (blocklen == -1)
667       blocklen = 64;
668     else if (blocklen < 8) {
669       blocklen = 8;
670       error("warning: line length raised to %d", blocklen);
671     } else if (blocklen > 1024) {
672       blocklen = 1024;
673       error("warning: line length lowered to %d", blocklen);
674     }
675   }
676 
677   /* check for non-seekable input */
678   if (fseek(ifp, 0, 0)) {
679     char buf[2048];
680     FILE *tmp = tmpfile();
681     if (!tmp)
682       fatal_error("cannot open temporary file: %s", strerror(errno));
683     while (!feof(ifp)) {
684       int i = fread(buf, 1, 2048, ifp);
685       if (i > 0)
686 	fwrite(buf, 1, i, tmp);
687     }
688     if (ferror(ifp))
689       fatal_error("%s: %s", ifp_name, strerror(errno));
690     reposition(tmp, 0);
691     fflush(tmp);
692     if (ifp != stdin)
693       fclose(ifp);
694     ifp = tmp;
695   }
696 
697   /* check for empty file */
698   fseek(ifp, 0, 2);
699   if (ftell(ifp) == 0)
700     fatal_error("%s: empty file\n\
701   (Try re-transferring the files using MacBinary format.)",
702 		ifp_name);
703 
704   reposition(ifp, 0);
705   if (!raw && !appledouble && !binhex && !macbinary) {
706     /* check magic number, try to figure out what it is */
707     int i, magic;
708     magic = read_four(ifp);
709     reposition(ifp, 0);
710 
711     if (magic == APPLESINGLE_MAGIC || magic == APPLEDOUBLE_MAGIC)
712       appledouble = 1;
713     else if ((magic & 0xFF000000) == 0)
714       macbinary = 1;
715     else {
716       binhex = 1;
717       for (i = 0; i < 4; i++, magic >>= 8)
718 	if (!isprint(magic & 0xFF) && !isspace(magic & 0xFF))
719 	  /* not an ASCII character, assume not BinHex */
720 	  binhex = 0;
721     }
722 
723     if (!appledouble && !macbinary && !binhex)
724       fatal_error("%s: unknown file type", ifp_name);
725   }
726 
727   if (raw) {
728     /* raw resource file */
729     res_offset = 0;
730 
731   } else if (macbinary) {	/* MacBinary (I or II) file */
732     const char *check;
733     int32_t data_fork_size;
734 
735     /* check integrity of file */
736     check = check_macbinary(ifp);
737     if (check)
738       fatal_error("%s: not a MacBinary file (%s)", ifp_name, check);
739 
740     /* read data and resource fork sizes in MacBinary header */
741     reposition(ifp, 83);
742     data_fork_size = read_four(ifp);
743     (void) read_four(ifp);
744 
745     /* round data_fork_size up to multiple of 128 */
746     if (data_fork_size % 128)
747       data_fork_size += 128 - data_fork_size % 128;
748 
749     res_offset = 128 + data_fork_size;
750 
751   } else if (appledouble) {	/* AppleDouble file */
752     const char *check;
753     const char *applewhat;
754     int i, n;
755 
756     /* check integrity */
757     check = check_appledouble(ifp);
758     if (check)
759       fatal_error("%s: not an AppleDouble file (%s)", ifp_name, check);
760     reposition(ifp, 0);
761     if (read_four(ifp) == APPLESINGLE_MAGIC)
762       applewhat = "AppleSingle";
763     else
764       applewhat = "AppleDouble";
765 
766     /* find offset to resource and/or data fork */
767     reposition(ifp, 24);
768     n = read_two(ifp);
769     res_offset = -1;
770     for (i = 0; i < n; i++) {
771       int type = read_four(ifp);
772       if (type == 0)
773 	fatal_error("%s: bad %s file (bad entry descriptor)", ifp_name, applewhat);
774       if (type == 2)		/* resource fork entry */
775 	res_offset = read_four(ifp);
776       else
777 	(void) read_four(ifp);
778       (void) read_four(ifp);
779     }
780     if (res_offset < 0)
781       fatal_error("%s: bad %s file (no resource fork)", ifp_name, applewhat);
782 
783   } else if (binhex) {		/* BinHex file */
784     const char *check;
785     FILE *tmpf = tmpfile();
786     if (!tmpf)
787       fatal_error("cannot open temporary file: %s", strerror(errno));
788 
789     /* check integrity */
790     check = translate_binhex(ifp, tmpf);
791     if (check)
792       fatal_error("%s: not a BinHex file (%s)", ifp_name, check);
793     check = check_binhex(tmpf);
794     if (check)
795       fatal_error("%s: bad BinHex file (%s)", ifp_name, check);
796 
797     /* find resource offset */
798     reposition(tmpf, 0);
799     res_offset = read_one(tmpf);
800     reposition(tmpf, 1 + res_offset + 11);
801     res_offset += 22 + read_four(tmpf) + 2;
802     if (ifp != stdin)
803       fclose(ifp);
804     ifp = tmpf;
805 
806   } else {
807     fatal_error("%s: strange format", ifp_name);
808     exit(1);
809   }
810 
811   /* read offsets from resource fork header */
812   reposition(ifp, res_offset);
813   res_data_offset = res_offset + read_four(ifp);
814   res_map_offset = res_offset + read_four(ifp);
815 
816   /* read type list offset from resource map header */
817   reposition(ifp, res_map_offset + 24);
818   type_list_offset = res_map_offset + read_two(ifp);
819 
820   /* read type list */
821   reposition(ifp, type_list_offset);
822   num_types = read_two(ifp) + 1;
823 
824   /* find POST type */
825   post_type =  (int32_t)('P' & 0xff) << 24;
826   post_type |= (int32_t)('O' & 0xff) << 16;
827   post_type |= (int32_t)('S' & 0xff) << 8;
828   post_type |= (int32_t)('T' & 0xff);
829 
830   while (num_types--) {
831     if (read_four(ifp) == post_type) {
832       int nrsrc = 1 + read_two(ifp);
833       int list_offset = type_list_offset + read_two(ifp);
834       int rsrc_pos = 0;
835       int want_id = 501;
836       int second_time = 1;
837       reposition(ifp, list_offset);
838       /* read resources sequentially, starting with ID 501, until we encounter
839 	 an "end" resource or we can't find the next resource */
840       while (rsrc_pos < nrsrc) {
841 	int offset = ftell(ifp);
842 	int id = read_two(ifp);
843 	if (id == want_id) {
844 	  (void) read_two(ifp);
845 	  (void) read_one(ifp);
846 	  num_extracted++;
847 	  if (!extract_data(ifp, ofp, &w, res_data_offset + read_three(ifp), pfb))
848 	    break;
849 	  second_time = 0;
850 	  want_id++;
851 	}
852 	reposition(ifp, offset + 12);
853 	rsrc_pos++;
854 	if (rsrc_pos >= nrsrc && !second_time) {
855 	  reposition(ifp, list_offset);
856 	  rsrc_pos = 0;
857 	}
858       }
859       break;
860     } else {
861       (void) read_two(ifp);
862       (void) read_two(ifp);
863     }
864   }
865 
866 #if 0
867   system("/bin/rm -f /tmp/x.*");
868   {
869     FILE *f;
870     int i;
871     reposition(ifp, res_offset + 16);
872     if ((f = fopen("/tmp/x.systemarea", "wb"))) {
873       for (i = res_offset + 16; i < res_data_offset; i++) {
874 	putc(getc(ifp), f);
875       }
876       fclose(f);
877     }
878   }
879   reposition(ifp, type_list_offset);
880   num_types = read_two(ifp) + 1;
881   while (num_types--) {
882     int t = read_four(ifp);
883     int num_of_type = 1 + read_two(ifp);
884     int32_t save_offset = ftell(ifp) + 2;
885     reposition(ifp, type_list_offset + read_two(ifp));
886     while (num_of_type--) {
887       FILE *f;
888       char buf[2048];
889       int x, i, attrs;
890       x = ftell(ifp);
891       i = read_two(ifp);	/* ID */
892       read_two(ifp);
893       attrs = read_one(ifp);
894       sprintf(buf, "/tmp/x.%c%c%c%c.%d", (t>>24)&255, (t>>16)&255, (t>>8)&255, t&255, i);
895       fprintf(stderr, "%c%c%c%c.%d %d", (t>>24)&255, (t>>16)&255, (t>>8)&255, t&255, i, attrs);
896       if ((f = fopen(buf, "wb"))) {
897 	int l;
898 	reposition(ifp, res_data_offset + read_three(ifp));
899 	l = read_four(ifp);
900 	fprintf(stderr, " %d\n", l);
901 	while (l > 0) {
902 	  int n = (l < 2048 ? l : 2048);
903 	  fread(buf, 1, n, ifp);
904 	  fwrite(buf, 1, n, f);
905 	  l -= n;
906 	}
907 	fclose(f);
908       }
909       reposition(ifp, x + 12);
910     }
911     reposition(ifp, save_offset);
912   }
913 #endif
914 
915   if (pfb)
916     pfb_writer_end(&w);
917   if (num_extracted == 0)
918     error("%s: not a Type 1 font (no POST resources)", ifp_name);
919 
920   fclose(ifp);
921   fclose(ofp);
922   return 0;
923 }
924