- ibm1130software.zip was contained within ibm1130.zip from ibm1130.org - Tabs converted to spaces
337 lines
12 KiB
C
337 lines
12 KiB
C
/*
|
|
* 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);
|
|
}
|