/* 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 #include 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; }