/*************************************************************************
 
Programma .... Lit.c

Autore ....... Donald J. Irving, 9812 Gardenwood Way, Sacramento, CA96827

Versione ..... 2.0i, 16/11/86, 9/10/89

Funzione ..... Filtra caratteri in output verso stdout

Sintassi ..... lit [nomefile][-s<numl>] [-p<numl>] [-[bcan][ohd]]

Hardware ..... Amiga 500/2500, Kickstart V1.2/1.3

Software ..... Aztec C V3.6a || Lattice C V5.04

Note ......... Traduzione ed adattamento di Luigi Callegari

***********************************************************************/

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

/* I 4 modi possibili di settaggio rappresentazione */
#define BSLASH       1
#define CONTROL      2
#define ALLREPS      3
#define NUMERIC      4

/* I tre modi di rappresentazione numerica ammessi */
#define OCT          1		/* Rappresentazione ottale: \033 */
#define HEX          2		/* Rappresentazione esadec.: \1B */
#define DEC          3		/* Rappresentazione decimale: \027 */

/* Settaggio per il numero di linee da stampare */
#define ALL_LINES    32767	/* Stampa tutte le linee */

/*
 * Opzioni di regolazione di default, personalizzabili se si desidera. I
 * commenti sulle linee di dedefinizione mostrano le scelte dell'autore del
 * programma, sulle quali sono basate le istruzioni date sul manuale *******************************************************************
 */

#define DEFAULT_START    1	/* 1            */
#define DEFAULT_PRINT    ALL_LINES	/* ALL_LINES    */
#define DEFAULT_OUTMODE  BSLASH	/* BSLASH       */
#define DEFAULT_BASE     OCT	/* OCT          */

/* Caratteri non stampabili, che richiedono trattamento speciale */

#define DEL		'\177'

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

typedef struct opt opt_t;

/*
 * Funzione che rappresenta incondizionatamente un carattere come valore
 * numerico usando la base numerica specificata. ************************
 */

void 
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);	/* rende maiuscolo, %X */
	if (buf[1] >= 'a')
	    buf[1] &= 0x5F;
	if (buf[2] >= 'a')
	    buf[2] &= 0x5F;
	printf("%s", buf);
	break;
    case DEC:
	printf("\\%03d", c);
	break;
    }
}

/*
 * Processa gli argomenti della linea di comando. Si attende che ogni
 * argomento sia un nome di file od una opzione preceduta da "-". Se è gi
 * to aperto un file di input, trovare un altro nome di file come argomento
 * comporta un errore. *********************************************
 */

BOOL 
process_args(argc, argv, ifp, opt)
    int             argc;
    char           *argv[];
    FILE          **ifp;
    opt_t          *opt;
{
    BOOL            process_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);
}

/*
 * Rappresenta un carattere non stampabile, se possibile, usando il formato
 * con carattere di controllo, oppure restituendo il codice di ritorno FALSE.
 * Tutti i caratteri ASCII non stampabili sono comunque rappresentabili con
 * caratteri di controllo. Gli unici caratteri respinti sono quelli esterni
 * alla gamma ASCII, ovvero col bit sinistro attivato. *******************
 */

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);
}

/*
 * Processa un argomento di linea consistente in una opzione preceduta dal
 * segno meno. Le opzioni s<numlinea> e -p<numlinee> devono essere separate
 * con il loro segno meno. Le opzioni b,c,a,n,o,h,d possono invece essere
 * raggruppate dopo un singolo simbolo meno. *****************************
 */

BOOL 
process_opt(arg, opt)
    char           *arg;
    opt_t          *opt;
{
    switch (*arg) {
    case 's':
	opt->start = atoi(arg + 1);	/* Fissa linea di partenza */
	break;
    case 'p':
	opt->print = atoi(arg + 1);	/* Fissa numero linee da stampare */
	break;
    default:
	for (; *arg; arg++) {
	    switch (*arg) {
	    case 'b':
		opt->outmode = BSLASH;	/* Sostituisce con backslash */
		break;
	    case 'c':
		opt->outmode = CONTROL;	/* Usa carattere di controllo */
		break;
	    case 'a':
		opt->outmode = ALLREPS;	/* Usa backslash */
		break;
	    case 'n':
		opt->outmode = NUMERIC;	/* Usa numerico */
		break;
	    case 'o':
		opt->base = OCT;/* Base numerica ottale */
		break;
	    case 'h':
		opt->base = HEX;/* Base numerica esa. */
		break;
	    case 'd':
		opt->base = DEC;/* Base numerica decimale */
		break;
	    default:
		printf("Unknown option: %c\n", *arg);
		return (FALSE);
	    }
	}
    }
    return (TRUE);
}

/* Trova la prima linea da stampare, eliminando tutte le altre */

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);
}

/* Restituisce VERO se la linea appena stampata era l'ultima */

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

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

/*
 * Stampa un carattere stampabile. Se e' un \ oppure l'accento circonflesso
 * ed i codici di controllo sono abilitati, usa \. ******************
 */

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);
}


/*
 * Rappresenta un carattere non stampabile usando una qualunque
 * rappresentazione appropriata. Le funzioni rep_xxx chiamate tentano di
 * rappresentare il carattere in un formato particolare, ritornando FALSE se
 * il carattere non è trattabile. ******************************************
 */

void 
represent_char(c, outmode, base)
    short           c, outmode, base;
{
    BOOL            rep_bslash();

    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);
}

/*
 * Rappresenta un carattere non stamapbile usando il costrutto con backslash
 * tipico del C, oppure restituisce FALSE se impossibile. Non è previsto \0.
 */

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);
}

void 
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');
    }
}
