#include <stdio.h>
#include <exec/types.h>

/*
* Lit Text Utility -  Filter a file to stdout showing all characters in
* an unambiguous format. See Lit Text Utility Manual for specification.
*
* Version 2.0 - 11/16/86, Copyright (C) 1986 Donald J Irving
*
* Author:	Donald J. Irving
*			9812 Gardenwood Way
*			Sacramento, CA 96827
*			(916) 366-3225
*			CIS:   73547,1335
*			PLINK: ops158
*
* Lit  may be freely distributed for personal use.
* Lit may not be sold or placed on a disk offered for sale without written
* permission from the author. Written permission will normally be granted
* at no charge for inclusion on so called "public domain disks" which are
* sold for nominal medium and handling fees. Lit may be modified for 
* personal use, and trivial modifications such as changing defaults or
* specifying include files may be made for distribution, but lit is not to
* be modified substantially for distribution without written consent of the
* author. The lit.c source, lit manual file , and this copyright notice must 
* be included on all distributions.
* 
* Here is the overall algorithm, minus some of the details:
*
*	if unable to interpret command line arguments
*		print an error message and a Usage line;
*	else
*		set options and open files;
*		if line specified to start on is not the first line
*			throw out input file lines until the line specified;
*		while read next char from input file
*			if the char is printable
*				print it;
*			else
*				represent it in a special format;
*			if the char is a line feed
*				output a linefeed;
*				if there was a number of lines to print specified
*				and ++line count equals that number
*					return;
* 
* Functions not returning values are not typed. Functions returning values
* are explicitly typed.
*  
*/

/* The four possible settings for representation mode. */
#define BSLASH		1		/* use backslash rep else numeric */
#define CONTROL		2		/* use control char reps else numeric */
#define ALLREPS		3		/* use backslash else control else numeric */
#define NUMERIC		4		/* use numeric value reps only */

/* The three possible settings for  numeric number base */
#define OCT	   		1		/* use octal representations as in \033 */
#define HEX			2		/* use hex representations as in \1B */
#define DEC			3		/* use decimal representations as in \027 */

/* The most likely setting for how many lines to print */
#define ALL_LINES	32767	/* print all the lines in the file */

/*
* Here are the default option settings. If you don't like them, please
* change them here. The comments on the define lines show the the default
* settings chosen by the author and upon which the manual is based. Please
* leave the comments as they are.
*/
													/* original		*/
#define DEFAULT_START		1						/* 1			*/
#define DEFAULT_PRINT		ALL_LINES				/* ALL_LINES	*/
#define DEFAULT_OUTMODE 	BSLASH					/* BSLASH		*/
#define DEFAULT_BASE		OCT						/* OCT			*/

/* Non-printing characters which require special handling in the code */
#define DEL		'\177'

struct opt {
	short start;
	short print;
	short outmode;
	short base;
};

typedef struct opt opt_t;

main(argc, argv)  
int argc;  
char *argv[];  
{
	FILE *ifp = NULL;
	register short c;
	static opt_t opt = {
		DEFAULT_START,
		DEFAULT_PRINT,
		DEFAULT_OUTMODE,
		DEFAULT_BASE
	};

	if (! process_args(argc, argv, &ifp, &opt))
		printf("Usage: lit [<filename>] [-s<n>] [-p<n>] [-[bcan][ohd]]\n");
    else {
		if (opt.start != 1  && ! find_start_line(ifp, opt.start))
			return;
        while ((c = getc(ifp)) != EOF) {
			if (! print_char(c, opt.outmode))
				represent_char(c, opt.outmode, opt.base);
			if (c == '\n') {
				putchar('\n');
				if (opt.print != ALL_LINES &&  last_line(opt.print))
					break;
			}
        }
		if (c != '\n')
			putchar('\n');
    }
}


/*
* process_args -- Process all the command line arguments. Each argument is
* expected to be either a minus sign option or an input file name. If an
* input file is already open, a subsequent input file name argument is an
* error.
*/

BOOL process_args(argc, argv, ifp, opt)
int argc;
char *argv[];
FILE **ifp;
opt_t *opt;
{
	register short i;

	for (i = 1; i < argc; i++) {
		if (*argv[i] == '-') {
			if (! process_opt(argv[i]+1, opt))
				return (FALSE);
		}
		else if (*ifp != NULL) {
			printf("Invalid argument  %s, Input file already specified\n", 
			argv[i]);
			return (FALSE);
		}
		else if ((*ifp = fopen(argv[i], "r")) == NULL) {
			printf("Unable to open input file %s\n", argv[i]);
			return (FALSE);
		}
	}

	if (*ifp == NULL)
		*ifp = stdin;
	return (TRUE);
}


/*
* process_opt -- Process a minus-sign command line argument. The options
* s<linenum> and p<numlines> must stand alone with their own minus signs; 
* The options b,c,a,n,o,h,d may be stacked after a single minus sign.
*/

BOOL process_opt(arg, opt)
char *arg;
opt_t *opt;
{
	switch (*arg) {
	case 's':
		opt->start = atoi(arg+1);			/* set starting linenum */
		break;
	case 'p':
		opt->print = atoi(arg+1);			/* set linenums to print */
		break;
	default:
		for (; *arg; arg++) {
			switch (*arg) {
			case 'b':
				opt->outmode = BSLASH;		/* use backslash rep else */
				break;						/* numeric rep			  */
			case 'c':
				opt->outmode = CONTROL;		/* use control char rep   */
				break;						/* else numeric rep       */
			case 'a':
				opt->outmode = ALLREPS;		/* use backslash else     */
				break;						/* control else numeric   */
			case 'n':
				opt->outmode = NUMERIC;		/* use numeric rep only   */
				break;
			case 'o':
				opt->base = OCT;		/* numeric rep base is octal */
				break;
			case 'h':
				opt->base = HEX;		/* numeric rep base is hex */
				break;
			case 'd':
				opt->base = DEC;		/* numeric rep base is decimal */
				break;
			default:
				printf("Unknown option: %c\n", *arg);
				return (FALSE);
			}
		}
	}
	return (TRUE);
}


/*
* find_start_line -- Find the first line to start printing on, throwing
* out all the lines along the way.
*/

BOOL find_start_line(ifp, start)
FILE *ifp;
short start;
{
	register short c, lineno = 1;

	while (lineno < start) {
		while ((c = getc(ifp)) != EOF  &&  c != '\n')
			;
		if (c == EOF)
			return (FALSE);
		lineno++;
	}
	return (TRUE);
}


/*
* last_line -- Return true if the line just printed was the last line
* to print.
*/

BOOL last_line(print)
short print;
{
	static short printed = 0;

	if (++printed == print)
		return (TRUE);
	return (FALSE);
}


/*
* print_char -- Print a printable character. If it's a \, or if
* it's a ^ and control codes are enabled, then preface it with a \.
*/

BOOL print_char(c, outmode) 
short c, outmode;
{
	if (c < ' ' || c > '~')
		return (FALSE);

	if (c == '\\'
	|| (c == '^' && (outmode == CONTROL || outmode == ALLREPS)))
		putchar('\\');

	putchar(c);
	return (TRUE);
}


/*
* represent_char -- Represent a non-printable character using whichever
* representation format is appropriate for the outmode selected. The
* rep_xxx functions called try to represent the char in a particular
* format, and return false if the the character is not applicable.
*/

represent_char(c, outmode, base)
short c, outmode, base;
{
	switch (outmode) {
	case BSLASH:
		if (rep_bslash(c))
			return;
		break;
	case CONTROL:
		if (rep_control(c))
			return;
		break;
	case ALLREPS:
		if (rep_bslash(c) || rep_control(c))
			return;
		break;
	case NUMERIC:
		break;
	}
	rep_numeric(c, base);
}


/*
* rep_bslash - Represent a non-printable char if possible using its
* C Language backslash construct, else return false. '\0' is not included.
*/

BOOL rep_bslash(c)
short c;
{
	switch (c) {
	case '\n':	putchar('\\');	putchar('n');		break;
	case '\t':	putchar('\\');	putchar('t');		break;
	case '\b':	putchar('\\');	putchar('b');		break;
	case '\r':	putchar('\\');	putchar('r');		break;
	case '\f':	putchar('\\');	putchar('f');		break;
	default  : 
		return (FALSE);
	}
	return (TRUE);
}


/*
* rep_control -- represent a non-printable char if possible using its
* control character representation, else return false. All non-printing
* ascii chars are representable as control chars (with DEL arbitrarily
* assigned the representation ^?). Characters rejected are those outside
* the range of ascii (ie, left most bit on).
*/

BOOL rep_control(c)
short c;
{
	if (c < '\0' || c > '\177')
		return (FALSE);

	putchar('^');

	switch (c) {
	case DEL:
		putchar('?');
		break;
	default:
		putchar(c | 0x40);
		break;
	}
    return (TRUE);
}


/*
* rep_numeric - Unconditionally represent a character as a numeric value
* using the number base specified.
*/

rep_numeric(c, base)
short c, base;
{
	char buf[4];

	switch (base) {
	case OCT:
		printf("\\%03o", c);
		break;
	case HEX:
		sprintf(buf, "\\%02x\0", c);	/* make it upper case, %X	*/
		if (buf[1] >= 'a')				/* doesn't seem to work on	*/
			buf[1] &= 0x5F;				/* my compiler.				*/
		if (buf[2] >= 'a')
			buf[2] &= 0x5F;
		printf("%s", buf);
		break;
	case DEC:
		printf("\\%03d", c);
		break;
	}
}

