From: jonhiga@phoenix.Princeton.EDU (Jonathan Tomohiko Higa)
Subject: tasc v3beta: ->ASC / ASC->
Originator: news@nimaster
Organization: Princeton AdTech
Date: Tue, 5 Jan 1993 19:38:59 GMT

BEGIN_DOC README
TASC (version 3-beta) is an ASC <-> HP48 binary translator.

Why do you want TASC at all?
1. Do the translation on another computer instead of on the HP48.  A binary
is approximately half the size of its ASC equivalent; therefore, you
will require less battery power.
2. It behaves like uuencode to the extent that you don't have to strip off
the non-ASC parts of a post or message if it contains only one ASCed object.
I've been using commands like | "tasc - newgame.bin" within my mail reader
to extract HP48 programs with ease.
3. There's no memory limit.  Some programs are too large to be
downloaded in ASC form but can still be downloaded in binary form.

Why is it better than previous versions?
1. It determines the file type by inspecting the file instead of expecting
a filename extension.
2. It determines how to invent a target file name by knowing the source
file's type.
3. The target file is overwritten when translation begins, not upon
program startup as before.
END_DOC README

BEGIN_DOC tasc.1
TASC(1)                  USER COMMANDS                    TASC(1)

NAMES
     tasc - translate HP48 binary files to and from ASC format

SYNOPSIS
     tasc [opt ...] source [target]

DESCRIPTION
     This program translates HP48 binary files to  and  from  ASC
     format.   It  should  be fully compatible with Bill Wickes's
     original ->ASC-> programs.  The symbol "-"  in  place  of  a
     filename  represents  the terminal.  If a target filename is
     not provided, one will be invented by  changing  the  source
     filename's  suffix  to ".asc" or ".bin".  Note that a target
     filename cannot be invented if the  standard  input  is  the
     source.

OPTIONS
     -d   Force ASC->bin (ASC decoding) mode.

     -e   Force bin->ASC (ASC encoding) mode.

     -q   Don't print reports (quiet).

DIAGNOSTICS
     Returns 0 on success, 1 on error.   On  successful  exit,  a
     BYTES statement is given if not in quiet mode.

AUTHOR
     Jonathan T. Higa (jonhiga@phoenix.princeton.edu)

CREDITS
     - ASC:  Bill Wickes (billw@hpcvdw.cv.hp.com).
     - Cyclic Redundancy Check: da Cruz, Frank.  _K_e_r_m_i_t:  _A  _F_i_l_e
     _T_r_a_n_s_f_e_r _P_r_o_t_o_c_o_l. Bedford, MA:  Digital Press, 1987.
     - Object structure: HP48 Tools Manual;  "HP48SX  Internals,"
     by Derek S. Nickel.
     - Guidance:  Joe Horn (akcs.joehorn@hpcvbbs.cv.hp.com)
     - Original inspiration: miscellaneous  asc2bin  programs  on
     seq.uncwil.edu

Sun Release 4.1   Last change: 1992 December 26                 1
END_DOC

BEGIN_SRC tasc.c
/* tasc (version 3):  ASC-encoder/decoder for HP48 files
Copyright 1992 by Jonathan T. Higa
Credits:
- ASC:  Bill Wickes (billw@hpcvdw.cv.hp.com).
- Cyclic Redundancy Check:
da Cruz, Frank.  _Kermit:  A File Transfer Protocol._
Bedford, MA:  Digital Press, 1987.
- Object structure:
HP48 Tools Manual;
"HP48SX Internals," by Derek S. Nickel.
- Guidance:  Joe Horn (akcs.joehorn@hpcvbbs.cv.hp.com)
- Original inspiration:
miscellaneous asc2bin programs on seq.uncwil.edu
*/

#include <stdio.h>
#include <string.h>

struct hexbuf {
   unsigned long bits;
   long nibs;
   int bsize;
   unsigned short crc;
};

enum hpdatatype { HPBIN, HPASC, UNKNOWN = -1 };

int verbose = 1;

void pushhex(struct hexbuf *b, int h)
/* Add a hex digit to the bit buffer. */
{
   b->bits |= (h & 0xfuL) << b->bsize;
   b->bsize += 4;
}

int pophex(struct hexbuf *b)
/* Remove and return a hex digit from the bit buffer, and
   include it in the CRC. */
{
   int h = b->bits & 0xf;
   b->crc = (b->crc >> 4) ^ (((b->crc ^ h) & 0xf) * 0x1081);
   b->nibs++;
   b->bits >>= 4;
   b->bsize -= 4;
   return h;
}

enum hpdatatype hptype(FILE *f)
/* Determine the type of HP file by scanning the beginning of the file.
   If the type is known, put the file pointer at the start of data. */
{
   char c;
   if (fscanf(f, "HPHP48-%c", &c) == 1)
      return HPBIN;
   else
      for (; ; )
	 switch (fscanf(f, "%*[%]HP:%*[^;]%c", &c)) {
	 case 1:
	    while (fscanf(f, " @%c", &c) == 1) {
	       if (c != '\n') {
		  fscanf(f, "%*[^\n]");
		  fscanf(f, "%*1[\n]");
	       }
	    }
	    return fscanf(f, "%c", &c) == 1 && c == '"' ? HPASC : UNKNOWN;
	 case 0:
	    fscanf(f, "%*[^\n]");
	    fscanf(f, "%*1[\n]");
	    break;
	 case EOF:
	    return UNKNOWN;
	 }
}

char *newext(char *new, const char *old, const char *ext)
/* Create a new filename from the old filename and extension. */
{
   char *p = strrchr(strcpy(new, old), ext[0]);
   if (p) strcpy(p, ext);
   else strcat(new, ext);
   if (verbose) fprintf(stderr, "tasc: inventing filename \"%s\"\n", new);
   return new;
}

int asctobin(FILE *fasc, FILE *fbin)
{
   struct hexbuf hb = {0, 0, 0, 0};
   int d;

   /* write header into binary file */
   fputs("HPHP48-E", fbin);

   /* translate data */
   while (fscanf(fasc, "%1x", &d) == 1) {
      pushhex(&hb, d);
      if (hb.bsize >= 24) {
	 d = pophex(&hb);
	 d |= pophex(&hb) << 4;
	 putc(d, fbin);
      }
   }
   if (hb.bsize > 16) {
      d = pophex(&hb);
      putc(d, fbin);
   }

   /* check CRC */
   if (hb.crc != hb.bits || hb.bsize != 16) {
      fprintf(stderr, "tasc: ASC->bin: CRC is incorrect\n");
      return 1;
   }
   if (verbose)
      fprintf(stderr, "tasc: ASC->bin: BYTES: #%hXh %ld%s\n",
              hb.crc, hb.nibs/2, hb.nibs & 1 ? ".5" : "");
   return 0;
}

int bintoasc(FILE *fbin, FILE *fasc)
{
   struct hexbuf hb = {0, 0, 0, 0};
   unsigned long fldlen = 0;
   enum { SIZE, ASCIC, ASCIX, DIR, ANY = -1 } fldnxt = ANY;
   int c, width = 0;

   /* write header into ASC file */
   fprintf(fasc, "%%%%HP: T(1);\n\"");

   /* parse binary */
   while ((c = getc(fbin)) != EOF) {
      pushhex(&hb, c);
      pushhex(&hb, c >> 4);
      if (!fldlen) /* done with previous field */
	 switch (fldnxt) { /* now for the current field */
	 case SIZE:
	    if (hb.bsize >= 20) {
	       /* this object's length is known by the size field */
	       fldlen = hb.bits & 0xfffff;
	       fldnxt = ANY;
	    }
	    break;
	 case ASCIC:
	    if (hb.bsize >= 8) {
	       /* ASCII-char identifier */
	       fldlen = 2 + 2 * (hb.bits & 0xff);
	       fldnxt = ANY;
	    }
	    break;
	 case ASCIX:
	    if (hb.bsize >= 8) {
	       /* ASCII-extended identifier */
	       fldlen = 4 + 2 * (hb.bits & 0xff);
	       fldnxt = ANY;
	    }
	    break;
	 case DIR:
	    if (hb.bsize >= 20) {
	       /* first object pointer in a directory, to an ASCIX name */
	       fldlen = hb.bits & 0xfffff;
	       fldnxt = ASCIX;
	    }
	    break;
	 default:
	    if (hb.bsize >= 20) {
	       unsigned long pro = hb.bits & 0xfffff;
	       fldlen = 5; /* the prolog field is 5 nibbles long */
	       if (pro == 0x29e8uL || pro == 0x2a0auL || pro == 0x2a2cuL
		   || pro == 0x2a4euL || pro == 0x2b1euL || pro == 0x2b40uL
		   || pro == 0x2b62uL || pro == 0x2b88uL || pro == 0x2dccuL)
		  fldnxt = SIZE; /* expect a size field */
	       else if (pro == 0x2e48uL || pro == 0x2e6duL || pro == 0x2afcuL)
		  fldnxt = ASCIC; /* expect an ASCIC object */
	       else if (pro == 0x2a96uL) {
		  /* expect the directory pointer after the first 8 nibbles */
		  fldlen = 8;
		  fldnxt = DIR;
	       }
	       else if (pro == 0x2911uL) fldlen = 10; /* is system binary */
	       else if (pro == 0x2933uL) fldlen = 21; /* is real */
	       else if (pro == 0x2955uL) fldlen = 26; /* is long real */
	       else if (pro == 0x2977uL) fldlen = 37; /* is complex */
	       else if (pro == 0x299duL) fldlen = 47; /* is long complex */
	       else if (pro == 0x29bfuL) fldlen = 7; /* is char */
	       else if (pro == 0x2e92uL) fldlen = 11; /* is XLIB name */
	    }
	    break;
	 }
      
      /* write out the current field */
      while (fldlen && hb.bsize) {
	 c = pophex(&hb);
	 if (width == 64) {
	    putc('\n', fasc);
	    width = 0;
	 }
	 fprintf(fasc, "%X", c);
	 width++;
	 fldlen--;
      }
   }
   if (hb.bits) {
      fprintf(stderr, "tasc: bin->ASC: end of last object not found\n");
      return 1;
   }

   /* append CRC */
   if (verbose)
      fprintf(stderr, "tasc: bin->ASC: BYTES: #%hXh %ld%s\n",
              hb.crc, hb.nibs / 2, hb.nibs & 1 ? ".5" : "");
   hb.bits = hb.crc;
   hb.bsize = 16;
   while (hb.bsize) {
      if (width == 64) {
	 putc('\n', fasc);
	 width = 0;
      }
      fprintf(fasc, "%X", pophex(&hb));
      width++;
   }
   fprintf(fasc, "\"\n");
   return 0;
}

int main(int argc, char **argv)
{
   const char *STDIO = "-";
   enum hpdatatype coding = UNKNOWN;
   int i = 1;
   char *iname, *oname, temp[256];
   if (argc > i && argv[i][0] == '-') {
      switch (argv[i][1]) {
      case 'd':
	 coding = HPASC;
	 i++;
	 break;
      case 'e':
	 coding = HPBIN;
	 i++;
	 break;
      case 'q':
         verbose = 0;
         i++;
         break;
      }
   }
   argc -= i;
   if (argc < 1 || argc > 2) {
      fprintf(stderr, "Use: %s [opt ...] source [target]\n"
              "opt\taction\n"
              " -d\tForce ASC->bin (ASC decode)\n"
              " -e\tForce bin->ASC (ASC encode)\n"
              " -q\tSuppress non-error messages (quiet)\n"
              "The filename \"-\" represents the terminal.\n",
              argv[0]);
      return 1;
   }
   iname = argv[i];
   if (strcmp(iname, STDIO)) {
      if (!freopen(iname, "rb", stdin)) {
	 fprintf(stderr, "tasc: ");
	 perror(iname);
	 return 1;
      }
   } else if (argc == 1) {
      fprintf(stderr, "tasc: cannot invent output filename for stdin\n");
      return 1;
   }
   switch (hptype(stdin)) {
   case HPASC:
      if (coding == HPBIN) {
	 fprintf(stderr, "tasc: ASC->bin mode was disallowed\n");
	 return 1;
      }
      if (verbose) fprintf(stderr, "tasc: entering ASC->bin mode\n");
      oname = argc == 1 ? newext(temp, iname, ".bin") : argv[i+1];
      if (strcmp(oname, STDIO) && !freopen(oname, "wb", stdout)) {
	 fprintf(stderr, "tasc: ");
	 perror(oname);
	 return 1;
      }
      if (asctobin(stdin, stdout))
	 return 1;
      break;
   case HPBIN:
      if (coding == HPASC) {
	 fprintf(stderr, "tasc: bin->ASC mode was disallowed\n");
	 return 1;
      }
      if (verbose) fprintf(stderr, "tasc: entering bin->ASC mode\n");
      oname = argc == 1 ? newext(temp, iname, ".asc") : argv[i+1];
      if (strcmp(oname, STDIO) && !freopen(oname, "w", stdout)) {
	 fprintf(stderr, "tasc: ");
	 perror(oname);
	 return 1;
      }
      if (bintoasc(stdin, stdout))
	 return 1;
      break;
   default:
      fprintf(stderr, "tasc: unknown input type\n");
      return 1;
   }
   if (fclose(stdin)) {
      fprintf(stderr, "tasc: ");
      perror(iname);
      return 1;
   }
   if (fclose(stdout)) {
      fprintf(stderr, "tasc: ");
      perror(oname);
      return 1;
   }
   return 0;
}
END_SRC
-- 
Jon (jonhiga@phoenix.princeton.edu)