/* unpost
 *
 * This program converts Macintosh type-1 fonts stored in MacBinary (I or II)
 * format or raw resource fork to PFA and PFB formats.
 *
 * Copyright (c) 1992 by I. Lee Hetherington, all rights reserved.
 *
 * Permission is hereby granted to use, modify, and distribute this program
 * for any purpose provided this copyright notice and the one below remain
 * intact. 
 *
 * I. Lee Hetherington (ilh@lcs.mit.edu)
 *
 * $Log:	unpost.c,v $
 * Revision 1.1  92/05/22  12:07:49  ilh
 * initial version
 * 
 */

/* Note: this is ANSI C. */

#ifndef lint
static char rcsid[] =
  "@(#) $Id: unpost.c,v 1.1 92/05/22 12:07:49 ilh Exp Locker: ilh $";
static char copyright[] =
  "@(#) Copyright (c) 1992 by I. Lee Hetherington, all rights reserved.";
#endif

#include <stdio.h>
#include <stdlib.h>

/* Some functions to read one, two, three, and four byte integers in 68000
   byte order (most significant byte first). */

static int read_one(FILE *fi)
{
  return fgetc(fi);
}

static int read_two(FILE *fi)
{
  int val;

  val = read_one(fi);
  val = (val << 8) + read_one(fi);
 
  return val;
}  

static long read_three(FILE *fi)
{
  long val;

  val = read_one(fi);
  val = (val << 8) + read_one(fi);
  val = (val << 8) + read_one(fi);
 
  return val;
}  

static long read_four(FILE *fi)
{
  long val;

  val = read_one(fi);
  val = (val << 8) + read_one(fi);
  val = (val << 8) + read_one(fi);
  val = (val << 8) + read_one(fi);

  return val;
}

/* Function to write four byte length to PFB file: least significant byte
   first. */

static void write_pfb_length(FILE *fo, long len)
{
  fputc(len & 0xff, fo);
  len >>= 8;
  fputc(len & 0xff, fo);
  len >>= 8;
  fputc(len & 0xff, fo);
  len >>= 8;
  fputc(len & 0xff, fo);
}

static void reposition(FILE *fi, long absolute)
{
  if (fseek(fi, absolute, 0) == -1) {
    fprintf(stderr, "error: fseek failed to position %d.\n", absolute);
    fprintf(stderr, "       Is this file seekable?\n");
    exit(1);
  }
}

static int hex_column = 0;			  /* current column of hex */
						  /* ASCII output */

static void output_hex_byte(FILE *fo, int b)
{
  static char *hex = "0123456789ABCDEF";

  if (hex_column > 62) {			  /* 64 column output */
    fputc('\n', fo);
    hex_column = 0;
  }
  fputc(hex[b >> 4], fo);
  fputc(hex[b & 0xf], fo);
  hex_column += 2;
}

/* Function to extract a particular POST resource.  Offset points to the four
   byte length which is followed by the data.  The first byte of the POST data
   specifies resource type: 1 for ASCII, 2 for binary, and 5 for end.  The
   second byte is always zero. */

static void extract_data(FILE *fi, FILE *fo, long offset, int binary)
{
  enum PS_type { PS_ascii = 1, PS_binary = 2, PS_end = 5 };
  static enum PS_type last_type = PS_ascii;
  long len, save_offset = ftell(fi);
  int c;

  reposition(fi, offset);
  len = read_four(fi) - 2;			  /* subtract type field */
  switch ((enum PS_type)read_one(fi)) {
  case PS_ascii:
    (void) read_one(fi);
    if (binary) {
      fputc(128, fo);
      fputc(1, fo);
      write_pfb_length(fo, len);
      while (len--) {
	if ((c = read_one(fi)) == '\r')		  /* change \r to \n */
	  c = '\n';
	fputc(c, fo);
      }
    } else {
      if (last_type == PS_binary)
	fputc('\n', fo);
      while (len--) {
	if ((c = read_one(fi)) == '\r')		  /* change \r to \n */
	  c = '\n';
	fputc(c, fo);
      }
    }
    last_type = 1;
    break;
  case PS_binary:
    (void) read_one(fi);
    if (binary) {
      fputc(128, fo);
      fputc(2, fo);
      write_pfb_length(fo, len);
      while (len--)
	fputc(read_one(fi), fo);
    } else {
      if (last_type != 2)
	hex_column = 0;
      while (len--)
	output_hex_byte(fo, read_one(fi));
      last_type = 2;
    }
    break;
  case PS_end:
    (void) read_one(fi);
    if (binary) {
      fputc(128, fo);
      fputc(3, fo);
    }
    break;
  }
  reposition(fi, save_offset);
}

static void usage(void)
{
  fprintf(stderr, "usage: unpost [-b] [-r] in-file [out-file]\n");
  exit(1);
}

static void print_banner()
{
  static char rcs_revision[] = "$Revision: 1.1 $";
  static char revision[20];

  if (sscanf(rcs_revision, "$Revision: %19s", revision) != 1)
    revision[0] = '\0';
  fprintf(stderr, "This is unpost %s.\n", revision);
}

int main(int argc, char **argv)
{
  FILE *fi, *fo;
  long data_fork_size;
  long res_offset, res_data_offset, res_map_offset, type_list_offset;
  long post_type;
  int num_types, num_of_type, num_extracted = 0, binary = 0, raw = 0;
  int c;
  extern int optind;
  extern int getopt(int, char **, char*);
  
  print_banner();

  /* parse command line */
  while ((c = getopt(argc, argv, "br?")) != -1)
    switch(c) {
    case 'b':
      ++binary;
      break;
    case 'r':
      ++raw;
      break;
    default:
      usage();
    }

  /* open files */
  if (argc - optind == 1) {
    fi = fopen(argv[optind++], "r");
    if (!fi) {
      fprintf(stderr, "error: couldn't open `%s' for reading\n", 
	      argv[--optind]);
      exit(1);
    }
    fo = stdout;
  } else if (argc - optind == 2) {
    fi = fopen(argv[optind++], "r");
    if (!fi) {
      fprintf(stderr, "error: couldn't open `%s' for reading\n", 
	      argv[--optind]);
      exit(1);
    }
    fo = fopen(argv[optind++], "w");
    if (!fo) {
      fprintf(stderr, "error: couldn't open `%s' for writing\n", 
	      argv[--optind]);
      exit(1);
    }
  } else {
    usage();
  }

  if (raw) {

    /* raw resource file */

    res_offset = 0;

  } else {

    /* MacBinary (I or II) file */

    /* SHOULD CHECK INTEGRITY OF MACBINARY HEADER HERE TO VERIFY THAT WE
       REALLY HAVE A MACBINARY FILE.  MACBINARY-II-STANDARD.TXT DESCRIBES
       AN APPROPRIATE VERIFICATION PROCEDURE. */

    /* read data and resource fork sizes in MacBinary header */
    reposition(fi, 83);
    data_fork_size = read_four(fi);
    (void) read_four(fi);
    
    /* round data_fork_size up to multiple of 128 */
    if (data_fork_size % 128)
      data_fork_size += 128 - data_fork_size % 128;

    res_offset = 128 + data_fork_size;

  }    

  /* read offsets from resource fork header */
  reposition(fi, res_offset);
  res_data_offset = res_offset + read_four(fi);
  res_map_offset = res_offset + read_four(fi);

  /* read type list offset from resource map header */
  reposition(fi, res_map_offset + 24);
  type_list_offset = res_map_offset + read_two(fi);

  /* read type list */
  reposition(fi, type_list_offset);
  num_types = read_two(fi) + 1;

  /* find POST type */
  post_type = ('P' << 24) + ('O' << 16) + ('S' << 8) + 'T';

  while (num_types--) {
    if (read_four(fi) == post_type) {
      num_of_type = 1 + read_two(fi);
      reposition(fi, type_list_offset + read_two(fi));
      while (num_of_type--) {
	(void) read_two(fi);			  /* ID */
	(void) read_two(fi);
	(void) read_one(fi);
	extract_data(fi, fo, res_data_offset + read_three(fi), binary);
	++num_extracted;
	(void) read_four(fi);
      }
      break;
    } else {
      (void) read_two(fi);
      (void) read_two(fi);
    }
  }

  fclose(fi);
  if (fo != stdout) {
    fclose(fo);
    fprintf(stderr, "Extracted %d POST resource%s.\n", num_extracted,
	    (num_extracted == 1) ? "" : "s");
  }

  return 0;
}

/*
 *  for Emacs...
 *  Local Variables:
 *  mode: C
 *  comment-column: 50
 *  fill-column: 79
 *  c-indent-level: 2
 *  c-continued-statement-offset: 2
 *  c-brace-offset: -2
 *  c-argdecl-indent: 2
 *  c-label-offset: -2
 *  End:
 */
