#include <stdlib.h>
#include <stdio.h>

/*

a2b.c  file

Convert ASCII to U.S. TTY/ITA2. Reads from input file, outputs to stdout.
Written for Borland C, but probably any C compiler will work.

This will not work as a command line filter as DOS always pre-pends a CR to each LF output!
(It assumes character value 10 is a linefeed, but it's TTY/ITA2). Eg. console always in cooked 
mode, ioctl to raw mode obscure and non-portable. Another reason to hate microsoft.

Tom Jennings
new 8 Nov 1998


mru 30 Aug 1999
Changed "baudot" to "ITA" as it should be.

*/

#define FIGS 27	/* TTY/ITA2 FIGS character and case machine state */
#define LTRS 31	/* TTY/ITA2 LTRS character and case machine state */
#define BOTH 0	/* case machine state */
#define NEITHER 99 /* initial case machine state */

#define UU 127	/* untranslatable character */

/* Table of TTY/ITA2 characters in ASCII order, in a crude double-character format. Index is
[2 * i] for the case indicator; [2 * i + 1] is the TTY/ITA2 character. The first symbol
defines which code set (FIGS, LTRS) the character is in. */

char table[] = {
    BOTH, 0,  /* NULL */
    BOTH, UU,  /* soh */
    BOTH, UU,  /* stx */
    BOTH, UU,  /* etx */
    BOTH, UU,  /* eot */
    BOTH, UU,  /* enq */
    BOTH, UU,  /* ack */
    FIGS, 5,  /* bell */
    BOTH, UU,  /* bs */
    BOTH, UU,  /* ht */
    BOTH, 2,  /* LF */
    BOTH, UU,  /* vt */
    BOTH, UU,  /* ff */
    BOTH, 8,  /* CR */
    BOTH, UU,  /* so */
    BOTH, UU,  /* si */
    BOTH, UU,  /* dle */
    BOTH, UU,  /* dc1 */
    BOTH, UU,  /* dc2 */
    BOTH, UU,  /* dc3 */
    BOTH, UU,  /* dc4 */
    BOTH, UU,  /* nak */
    BOTH, UU,  /* syn */
    BOTH, UU,  /* etb */
    BOTH, UU,  /* can */
    BOTH, UU,  /* em */
    BOTH, UU,  /* sub */
    BOTH, FIGS,  /* FIGS */
    BOTH, UU,  /* fs */
    BOTH, UU,  /* gs */
    BOTH, UU,  /* rs */
    BOTH, LTRS,  /* LTRS */
    BOTH, 4,  /* SPACE */
    FIGS, 13,  /*  (exclamation) */
    FIGS, 17,  /* " */
    FIGS, 20,  /* # */
    FIGS, 9,  /*  (dollar) */
    BOTH, UU,  /*  (percent) */
    FIGS, 26,  /*  */
    FIGS, 11,  /*  (sgl quote) */
    FIGS, 15,  /* ( */
    FIGS, 18,  /* ) */
    BOTH, UU,  /* * (asterisk) */
    BOTH, UU,  /* + (plus) */
    FIGS, 12,  /* , */
    FIGS, 3,  /* - */
    FIGS, 28,  /* . */
    FIGS, 29,   /* / */
    FIGS, 22,  /* 0 */
    FIGS, 23,  /* 1 */
    FIGS, 19,  /* 2 */
    FIGS, 1,  /* 3 */
    FIGS, 10,  /* 4 */
    FIGS, 16,  /* 5 */
    FIGS, 21,  /* 6 */
    FIGS, 7,  /* 7 */
    FIGS, 6,  /* 8 */
    FIGS, 24,  /* 9 */
    FIGS, 14,  /* : */
    FIGS, 30,  /* ; */
    BOTH, UU,  /* (.LT.) */
    BOTH, UU,  /* (equal) */
    BOTH, UU,  /* (.GT.) */
    FIGS, 25,  /* ? */
    BOTH, UU,  /* (at) */
    LTRS, 3,  /* A */
    LTRS, 25,  /* B */
    LTRS, 14,  /* C */
    LTRS, 9,  /* D */
    LTRS, 1,  /* E */
    LTRS, 13,  /* F */
    LTRS, 26,  /* G */
    LTRS, 20,  /* H */
    LTRS, 6,  /* I */
    LTRS, 11,  /* J */
    LTRS, 15,  /* K */
    LTRS, 18,  /* L */
    LTRS, 28,  /* M */
    LTRS, 12,  /* N */
    LTRS, 24,  /* O */
    LTRS, 22,  /* P */
    LTRS, 23,  /* Q */
    LTRS, 10,  /* R */
    LTRS, 5,  /* S */
    LTRS, 16,  /* T */
    LTRS, 7,  /* U */
    LTRS, 30,  /* V */
    LTRS, 19,  /* W */
    LTRS, 29,  /* X */
    LTRS, 21,  /* Y */
    LTRS, 17,  /* Z */
    BOTH, UU,  /* [ (L bkt) */
    BOTH, UU,  /* \ (back sl) */
    BOTH, UU,  /* ] (R bkt) */
    BOTH, UU,  /* ^ (caret) */
    BOTH, UU,  /* _ (underscore) */
};

char last_state= NEITHER;			/* unspecified state */
FILE *fi;					/* input file */
FILE *fo;					/* output file */
char fn[256];					/* outptu filename */

void a2b(int);
void mf(char *, char *, char *);
void fchar(int);



int
main(argc, argv)
int argc;
char *argv[];
{
int c;

	if (argc < 2) {
		fprintf (stderr, "a2b -- ASCII to TTY/ITA2. 'a2b file.txt' converts ASCII\n");
		fprintf (stderr, "to TTY/ITA2 and outputs to file.ita. Tom Jennings tomj@wps.com\n");
		return(1);
	}
	if ((fi= fopen(argv[1], "rb")) == NULL) {	/* MODE = READ-ONLY, BINARY! */
		printf ("File %s doesn't exist!\n", argv[1]);
		exit(1);
	}
	mf(argv[1],fn,".ita");				/* make output file file.ita */
	if ((fo= fopen(fn, "wb")) == NULL) {		/* MODE = WRITE, BINARY */
		fprintf (stderr, "Can't create file %s!\n", fn);
		fclose(fi);
		exit(2);
	}
	while ((c= fgetc(fi)) != EOF) {			/* for each character in the input file, */
		a2b(c);					/* convert, */
	}
	fclose(fi); fclose(fo);
	return(0);
}

/* Make a filename with specified extention. */

void
mf(in, out, ext)
char *in, *out, *ext;
{
	while (*out= *in++) {				/* copy filename characters */
		if (*out == '.') break;			/* up til the dot, if any */
		++out;
	}
	while (*out++= *ext++);				/* tack on extention */
}


/* Convert an ASCII character to TTY/ITA2 and output to stdout. We track FIGS or LTRS case with
a simple state machine. */

void
a2b(c)
int c;
{
char state;

	if (c > 96) c -= 32;				/* lower case becomes upper case */
	c += c;						/* makes char index into table */
	state= table[c]; c= table[++c];			/* get TTY/ITA2 case and character, */
	if (c == UU) return;				/* untranslatable! */
	if ((state != BOTH) &&				/* if character is in a different case */
	    (state != last_state)) fchar(state);	/* than the current one, output case character */
	last_state= state;				/* remember new (or same as last time) */
	fchar(c);
}

/* Output a character to a file. */

void
fchar(c)
int c;
{
	if (fputc(c, fo) == EOF) {		/* write to output file */
		fprintf (stderr, "ERROR: Write to %s failed! (disk full?)\n", fn);
		fclose(fi); fclose(fo);
		exit(3);
	}
}

