/* | |
* punches - convert beteen IBM1130 simulator binary card image format and ascii text lists of punch numbers | |
* | |
* Usage: | |
* punches -b [infile [outfile]] | |
* Converts from ascii to binary. Reads stdin/writes stdout if infile/outfile not specified | |
* | |
* punches -a [infile [outfile]] | |
* Converts from binary to ascii. | |
* | |
* The ASCII format consists of an arbitrary number of card images. Each card image consists of | |
* a line with the word "start", followed by 80 lines each containing the punch data for one card | |
* column, followe by a line with the word "end". | |
* | |
* A column specification line consists of the word "blank", for a column with no punches, | |
* or an arbitrary number of integer row names separated by hyphens. The row names are 12, 11, 0, 1, 2, ..., 9. | |
* | |
* The character #, * or ; terminates an input line and the remainder of the line is ignored as a comment. | |
* Blank lines are ignored and may occur at any place in the input file. | |
* | |
* A typical card specification might look like this: | |
start | |
* This is a comment line | |
12-1-2 | |
blank | |
5 # this is a comment after the data for column 3 | |
4 | |
2 | |
blank | |
blank | |
11-5 | |
2-6-3 | |
. \ | |
. | not all lines shown. Exactly 80 data lines are required | |
. / | |
blank | |
12-0-2-6-7-8 | |
end | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "util_io.h" | |
#ifdef WIN32 // for Windows binary file mode setting | |
# include <io.h> | |
# include <fcntl.h> | |
#endif | |
#define TRUE 1 | |
#define FALSE 0 | |
typedef int BOOL; | |
#define BETWEEN(v,a,b) (((v) >= (a)) && ((v) <= (b))) | |
BOOL failed = FALSE; | |
int ncards = 0; | |
void tobinary (char *fnin, char *fnout); | |
void toascii (char *fnin, char *fnout); | |
void bail (char *msg); | |
int main (int argc, char **argv) | |
{ | |
enum {MODE_UNKNOWN, MODE_TOBINARY, MODE_TOASCII} mode = MODE_UNKNOWN; | |
int i; | |
char *arg, *fnin = NULL, *fnout = NULL; | |
static char usestr[] = "Usage: punches -b|-a [infile [outfile]]"; | |
for (i = 1; i < argc; i++) { | |
arg = argv[i]; | |
if (*arg == '-') { | |
arg++; | |
while (*arg) { | |
switch (*arg++) { | |
case 'b': | |
mode = MODE_TOBINARY; | |
break; | |
case 'a': | |
mode = MODE_TOASCII; | |
break; | |
default: | |
bail(usestr); | |
} | |
} | |
} | |
else if (fnin == NULL) | |
fnin = arg; | |
else if (fnout == NULL) | |
fnout = arg; | |
else | |
bail(usestr); | |
} | |
util_io_init(); // check CPU for big/little endianness | |
if (mode == MODE_TOBINARY) | |
tobinary(fnin, fnout); | |
else if (mode == MODE_TOASCII) | |
toascii(fnin, fnout); | |
else | |
bail(usestr); | |
if (failed) { | |
if (fnin != NULL) { // if there was an error, delete output file if possible | |
unlink(fnout); | |
fprintf(stderr, "Output file \"%s\" deleted\n", fnout); | |
exit(1); | |
} | |
else | |
bail("Output file is incorrect"); | |
} | |
else // if no error, tell how many cards we converted | |
fprintf(stderr, "* %d card%s converted\n", ncards, (ncards == 1) ? "" : "s"); | |
return 0; | |
} | |
// alltrim - remove string's leading and trailing whitespace | |
char *alltrim (char *str) | |
{ | |
char *c, *e; | |
for (c = str; *c && *c <= ' '; c++) // skip over leading whitespace | |
; | |
if (c > str) // if there was some, copy string down over it | |
strcpy(str, c); | |
for (e = str-1, c = str; *c; c++) // find last non-white character | |
if (*c > ' ') | |
e = c; | |
e[1] = '\0'; // terminate string immediately after last nonwhite character | |
return str; // return pointer to string | |
} | |
void tobinary (char *fnin, char *fnout) | |
{ | |
FILE *fin, *fout; | |
BOOL gotnum; | |
int col, v, lineno = 0; | |
char str[256], *c; | |
unsigned short buf[80], punches; | |
static unsigned short punchval[13] = { | |
0x2000, 0x1000, 0x0800, 0x0400, 0x0200, // 0, 1, 2, 3, 4 | |
0x0100, 0x0080, 0x0040, 0x0020, 0x0010, // 5, 6, 7, 8, 9 | |
0x0000, // there is no 10 punch | |
0x4000, 0x8000}; // 11 and 12. | |
if (fnin == NULL) { | |
fin = stdin; | |
} | |
else if ((fin = fopen(fnin, "r")) == NULL) { | |
perror(fnin); | |
exit(1); | |
} | |
if (fnout == NULL) { | |
fout = stdout; | |
#ifdef WIN32 | |
_setmode(_fileno(stdout), _O_BINARY); | |
#endif | |
} | |
else if ((fout = fopen(fnout, "wb")) == NULL) { | |
perror(fnout); | |
exit(1); | |
} | |
col = 0; // we are starting between cards, expect start as first data line | |
while (fgets(str, sizeof(str), fin) != NULL && ! failed) { | |
alltrim(str); // trim leading/trailing blanks (including newline) | |
lineno++; // count input line | |
if (*str == ';' || *str == '#'|| *str == '*' || ! *str) | |
continue; // ignore comment or blank line | |
if (strnicmp(str, "start", 5) == 0) { // start marks new card, proceed to column 1 (strnicmp so trailing comment is ignored) | |
if (col == 0) | |
col = 1; | |
else { | |
fprintf(stderr, "\"start\" encountered where column %d was expected, at line %d\n", lineno); | |
failed = TRUE; | |
} | |
} | |
else if (strnicmp(str, "end", 3) == 0) { // end is expected as 81'st data line | |
if (col == 81) { | |
fxwrite(buf, 2, 80, fout); // write binary card image to output file | |
ncards++; // increment card count | |
col = 0; // reset, expect start next | |
} | |
else { | |
fprintf(stderr, "\"end\" encountered where "); | |
if (col == 0) | |
fprintf(stderr, "\"start\""); | |
else | |
fprintf(stderr, "column %d", col); | |
fprintf(stderr, " was expected, at line %d\n", lineno); | |
failed = TRUE; | |
} | |
} | |
else if (BETWEEN(col, 1, 80)) { // for column 1 to 80, we expect a data line | |
if (strnicmp(str, "blank", 5) == 0) { // blank indicates an unpunched column | |
buf[col-1] = 0; | |
col++; | |
} | |
else { | |
punches = 0; // prepare to parse a data line. Punches is output binary value for column | |
v = 0; // v is current punch number | |
gotnum = FALSE; // gotnum indicates we've seen a punch number | |
for (c = str; ! failed; c++) { | |
if (BETWEEN(*c, '0', '9')) { // this is a digit, accumulate into current punch number | |
v = v*10 + *c - '0'; | |
gotnum = TRUE; // note that we've seen a value | |
} | |
else if (*c == '-' || *c == '\0') { // at - separator or at end of string | |
if (gotnum && BETWEEN(v, 0, 12) && v != 10) | |
punches |= punchval[v]; // add correct bit to column binary value | |
else { // error if number not seen or punch number not 0..9, 11, or 12 | |
fprintf(stderr, "Invalid punch value %d at line %d\n", v, lineno); | |
failed = TRUE; | |
break; | |
} | |
if (*c == '\0') { // at end of string store value and advance column count | |
buf[col-1] = punches; | |
col++; | |
break; | |
} | |
else { | |
v = 0; // at separator, reset for next punch value | |
gotnum = FALSE; | |
} | |
} | |
else if (*c == '#' || *c == ';' || *c == '*') { | |
break; // terminate line parsing at comment character | |
} | |
else { // invalid character | |
fprintf(stderr, "Unexpected character '%c' at line %d\n", *c, lineno); | |
failed = TRUE; | |
break; | |
} | |
} | |
} | |
} | |
else { // we expected start or end when not expecting column data | |
fprintf(stderr, "\"%s\" encountered where \"%s\" was expected, at line %d\n", str, | |
(col == 0) ? "start" : "end", lineno); | |
failed = TRUE; | |
} | |
} | |
fclose(fin); | |
fclose(fout); | |
} | |
void toascii (char *fnin, char *fnout) | |
{ | |
FILE *fin, *fout; | |
unsigned short buf[80], mask; | |
int nread, col, row; | |
BOOL first; | |
static char *punchname[] = {"12", "11", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; | |
if (fnin == NULL) { | |
fin = stdin; // no input file named, read from stdin | |
#ifdef WIN32 | |
_setmode(_fileno(stdin), _O_BINARY); // (on Windows, must set binary mode) | |
#endif | |
} | |
else if ((fin = fopen(fnin, "rb")) == NULL) { // open named input file | |
perror(fnin); | |
exit(1); | |
} | |
if (fnout == NULL) { // no output file named, write to stdout | |
fout = stdout; | |
} | |
else if ((fout = fopen(fnout, "wb")) == NULL) { // open named output file | |
perror(fnout); | |
exit(1); | |
} | |
// write comment with input file name | |
fprintf(fout, "* converted from %s\n", (fnin == NULL) ? "<stdin>" : fnin); | |
while ((nread = fxread(buf, 2, 80, fin)) == 80) { // pull cards from binary file | |
ncards++; // increment card count | |
fprintf(fout, "**** card %d\nstart\n", ncards); // write comment with card number and start statement | |
for (col = 0; col < 80; col++) { // dump 80 columns | |
if (buf[col] == 0) { | |
fprintf(fout, "blank\n"); // no punches this column | |
} | |
else if (buf[col] & 0x000F) { // if low bits are set it is not a valid IBM1130 card image | |
fprintf(stderr, "Input file is not an IBM 1130 card image, low bits set found at card image %d\n", ncards); | |
failed = TRUE; | |
break; | |
} | |
else { | |
first = TRUE; // scan the 12 punch bits | |
for (mask = 0x8000, row = 0; row < 12; row++, mask >>= 1) { | |
if (buf[col] & mask) { // output name of punch row for each bit set (12, 10, 0, ..., 9) | |
fprintf(fout, "%s%s", first ? "" : "-", punchname[row]); | |
first = FALSE; // next punch will need a hyphen | |
} | |
} | |
putc('\n', fout); | |
} | |
} | |
fprintf(fout, "end\n"); | |
} | |
if (nread != 0) { // oops, file wasn't a multiple of 160 bytes in length | |
fprintf(stderr, "Input file invalid or contained a partial card image\n"); | |
failed = TRUE; | |
} | |
fclose(fin); | |
fclose(fout); | |
} | |
void bail (char *msg) | |
{ | |
fprintf(stderr, "%s\n", msg); | |
exit(1); | |
} |