/*
           Strip.c * V2.0 * Novembre 1988 * By Luigi R. Callegari

     Elimina materiale inutile da files sorgenti ASCII in linguaggio C:

           LF consecutivi, tabulatori, spazi eccedenti, commenti

               Usabile da Workbench * Sintassi CLI: RUN strip

   Questo programma utilizza le routines di "pulizia" appartenenti al PD
                         scritte da Chris Metcalf.

****************************************************************************

                 Sviluppato con il seguente materiale (!!):

         Software: Lattice AmigaDOS C Compiler V4.0, Power Windows

    Hardware: 1 Amiga 500, 2 Mbytes, 2 Drives, 1 Epson EX800, 1 Cervello

                Editors: MicroEmacs, CygnusEd Professional,

   Documentazione: Intuition Reference Manual, ROM Kernel Manuals I & II

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

/* Inclusione dei files di header */

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

/* Le seguenti linee sono da tralasciare se non si usa Lattice C V4.0
Questi files permettono al compilatore di conoscere i tipi ed il numero
dei parametri richiesti dalle routines in ROM. Questa possibilita',
prevista dallo standard ANSI C in corso di approvazione, elimina un
grosso difetto intrinseco del linguaggio standard ed evita molti GURU.
Inoltre, nel caso specifico di Lattice AmigaDOS, l'inclusione di questi
files permette al programma compilato e linkato di saltare alle routines
in ROM con i parametri corretti gia' inseriti nei registri del 68000,
eliminando la necessita' di passare per le routines di supporto presenti
nella libreria Amiga.lib. Cio' elimina uno dei maggiori difetti dei
programmi scritti in C per Amiga e consente la creazione di codici
piu' compatti ed efficienti.
*/

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/dos.h>

#define WS 0x80		/* OR in caratteri nulli */

/* Qui iniziano le dichiarazioni di strutture usate dal programma.
Le prime due linee sono da inserire praticamente in tutti i
programmi che usano Intuition, per motivi troppo lunghi a spiegare.
*/

extern struct GfxBase *GfxBase;
extern struct IntuitionBase *IntuitionBase;

/* Le seguenti dichiarazioni sono specifiche per il programma. */

struct Window		*window, *OpenWindow();
struct IntuiMessage	*message;
struct GfxBase		*GfxBase;
struct IntuitionBase	*IntuitionBase;
struct Message		*GetMsg();

/* Servono matrici globali per i nomi dei files di input e di output.
Si noti il preferibile uso di UBYTE piuttosto che 'char'.
********************************************************/

UBYTE StringGadget2[80]="stripped";
UBYTE StringGadget1[80];

/* Qui iniziano le complesse difinizioni dei gadget di Intuition.
Per capirle, e' indispensabile possedere il testo ufficiale di
mamma Commodore: Intuition Reference Manual (Addison-Wesley)
*/

struct StringInfo Gadget2SInfo = {
	StringGadget2,	/* buffer del testo */
	NULL,		/* buffer di undo opzionale */
	0,		/* posizione primo carattere in buffer */
	80,		/* massimo numero di caratteri */
	0,		/* posizione primo carattere visualizzato in buffer */
	0,0,0,0,0,	/* riservate ad intuition */
	0,		/* Rastport dei gadget */
	0,		/* valore iniziale per gadgets */
	NULL		/* keymap alternativa(fill in if you set the flag) */
};

SHORT BorderVectors1[] = {0,0,	289,0,	289,11,	0,11,	0,0};

struct Border Border1 = {
	-2,-1,		/* origine XY relativa al container TopLeft */
	3,0,JAM1,	/* front pen, back pen and drawmode */
	5,		/* numero coppie XY */
	BorderVectors1,	/* puntatore coppie vettori XY */
	NULL		/* prossimo border nella lista*/
};

struct Gadget Gadget2 = {
	NULL,		/* prossimo gadget */
	75,35,		/* origine XY del box relativo a finestra TopLeft */
	286,10,		/* larghezza ed altezza box */
	NULL,		/* flags di gadget */
	RELVERIFY+STRINGCENTER,	/* flags di attivazione*/
	STRGADGET,	/* flags di tipo di gadget */
	(APTR)&Border1,	/* bordo gadget o immagine da rendere */
	NULL,		/* immagine alternata di selezione */
	NULL,		/* prima struttura IntuiText */
	NULL,		/* long word gadget a mutua esclusione*/
	(APTR)&Gadget2SInfo,	/* struttura SpecialInfo  */
	NULL,		/* dati definiti dall'utente*/
	NULL		/* puntatore a dati definiti dall'utente */
};

struct StringInfo mygadgetSInfo = {
	StringGadget1,	NULL, 0,80,0,0,0,0,0,0,0,0,NULL
};

SHORT BorderVectors2[] = {0,0,287,0,287,12,0,12,0,0};

struct Border Border2 = {
	-2,-1,3,0,JAM1,5,BorderVectors2,NULL
};

struct Gadget mygadget = {
	&Gadget2,76,15,284,11,
	SELECTED,RELVERIFY+STRINGCENTER,STRGADGET,	
	(APTR)&Border2,
	NULL,NULL,NULL,
	(APTR)&mygadgetSInfo,
	NULL,NULL
};

struct IntuiText IText2 = {
	3,0,JAM2,11,35,NULL,"Output:",NULL
};

struct IntuiText IText1 = {
	3,0,JAM2,11,15,NULL,"Input:",&IText2
};

/* Segue la classica ed immancabile definizione di finestra Intuition.
Bisogna guardarla bene, perche' la si trova sempre!
**************************************************/

struct NewWindow wind = {
	131,87,	/* Origine XY della finestra relativa a spigolo sinistro */
	373,50,	/* larghezza ed altezza finestra */
	0,1,		/* detail pen e block pen */
	GADGETDOWN+GADGETUP+CLOSEWINDOW,	/* flags IDCMP */
	WINDOWDRAG+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH,	/* flags */
	&mygadget,	/* primo gadget  */
	NULL,	/* immagine CHECKMARK custom */
	"Strip",	/* titolo finestra */
	NULL,	/* puntatore schermo custom */
	NULL,	/* puntatore bitmap custom */
	5,5,		/* minima altezza e larghezza */
	640,200,	/* massima altezza e larghezza */
	WBENCHSCREEN	/* tipo di schermo di destinazione */
};

/* Finite le definizioni e le dichiarazioni globali, passiamo ora
alle funzioni che fanno lavorare veramente il programma.
*******************************************************/

/* Cleanup() esce educatamente dal programma chiudendo tutto. Bisogna
ricordarsi che il S.O. di Amiga e' in multitasking, quindi bisogna
restituire tutto cio' che non serve piu', perche' possa essere
riutilizzato.
************/

void Cleanup(c)
int c;		/* Valore ritornato al CLI */
{
	if (c && window)	DisplayBeep(NULL);
	if (window)		CloseWindow((struct Window *)window);
	if (GfxBase)		CloseLibrary((struct Library *)GfxBase);
	if (IntuitionBase)	CloseLibrary((struct Library *)IntuitionBase);
	exit(c);
}

/* WbenchRun() interfaccia il Workbech, apre le librerie apre la finestra,
attiva il gadget di richiesta ed attende la risposta dall'utente.
****************************************************************/

BOOL WbenchRun()
{
struct Gadget *iaddr;
ULONG class;

if (!(IntuitionBase = (struct IntuitionBase *)
			OpenLibrary("intuition.library",33L)))
	Cleanup(21);

if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",33L)))
	Cleanup(22);

if (!(window = OpenWindow(&wind)))
	Cleanup(23);

SetAPen(window->RPort,1);		/* Fissa colore scrittura */
PrintIText(window->RPort,&IText1,0,0);	/* Scrive stringhe */
ActivateGadget(&mygadget,window,NULL);	/* Attiva gadget */

/* Il ciclo seguente e' un classico di Amiga-Intuition. Viene ripetuto
sinche' dalla porta di comunicazione di messaggi di Intuition (IDCMP),
l'interfaccia finestra-gadget di stringa, non arriva un messaggio
appropriato, quindi ritorna al programma principale con il valore TRUE
e le variabili globali contenenti i valori immessi, senno' ritorna
un valore FALSE.
****************/

FOREVER {
	Wait(1<<window->UserPort->mp_SigBit);
	while (message=(struct IntuiMessage *)GetMsg(window->UserPort)) {
		class = message->Class;
		iaddr = (struct Gadget *)message->IAddress;
		ReplyMsg((struct Message *)message);
		switch(class) {
			case CLOSEWINDOW:
				return(FALSE);
			case GADGETDOWN:
			case GADGETUP:
				return(TRUE);
		}
	}
}
}

/* Legge i caratteri saltando gli spazi, grazie a nextchar() */

void strip_blanks(in, out)
FILE *in, *out;
{
register char c;		/* Carattere corrente */
register char lastws = 0;	/* Il carattere da bufferizzare */
register BOOL ws;		/* Flag corrente carattere = spazio*/
char nextchar(FILE *);		/* Dichiara funzione seguente nel file */

while ((c = nextchar(in)) != EOF) {
	ws = c & WS;
	c &= ~WS;
	if (lastws) {
		if (!ws) {
		putc(lastws, out);
		putc(c, out);
		lastws = 0;
		}
			else if (c == '\n' || c == '\r' || c == '\f')
				lastws = '\n';
			else if (c == '\t' && lastws != '\n')
				lastws = '\t';
			else if (c == ' ' && lastws != '\n' && lastws != '\t')
				 lastws = ' ';
	}
	else if (ws)
		lastws = (c=='\r'||c=='\f')?'\n':c;
	else
		putc(c, out);
}
if (lastws)
	putc(lastws, out);
}

/* Legge il seguente carattere da un file, eliminando la roba inutile */

char nextchar(in)
FILE *in;
{
register char c;		/* Carattere correntemente in lettura */
register BOOL modes;	 	/* variabile di verifica dei modi */
static char next;  		/* carattere in attesa di lettura */
static BOOL init = TRUE,  	/* flag di segnalatore inizio loop */
comments = FALSE,  		/* flag se siamo in blocco di commento */
oldcomments = FALSE,		/* flag se 'eravamo' in blocco commento */
quotes = FALSE, 		/* flag se siamo tra virgolette */
single = FALSE, 		/* flag se siamo in signoli apici */
preproc = FALSE, 		/* flag se siamo in linea di pre-processor */
demagic = FALSE, 		/* flag se prima era un backslash */
newline = TRUE; 		/* flag se prima era un newline */

/* Fissa ricerca in avanti la prima volta che si chiama la routine */

if (init) {
	next = getc(in);
	init = FALSE;
}

/* Il loop principale cicla sinche' non viene trovato un vero
carattere. Lo stato iniziale del pre-processore, gli apici, i commenti
sono conservati in modo da potere eliminare transizioni da stato a 
stato durante lo switch, nell'output.
Lo Switch gestisce il lavoro in casi particolari. Se si e' trovato un
# ed il precedente carattere era un newline, e non siamo in modo 
commento, lo stato pre-processore e' TRUE. Esso viene riportato a FALSE
solo quando si incontra un newline.
Il modo commento e' attivato da una sequenza '/' e '*' se non sono
attivate le atre (preprocesso, apici). Il modo commento e' disattivato
da '*' e '/', sempre se non sono stati attivati altri modi. 
Il modo di apice singolo e' attivato e terminato da un hyphen.
Il modo di doppi apici e' similare al modo di apice singolo. Si noti
che potrebbero essere attivati erroneamente nel corso di una linea del
pre-processore, ma cio' e' senza importanza, poiche' il newline spenge
comunque il modo. 
*/

do {
	if ( (c = next) == EOF) {
	init = TRUE;
	return (EOF);
}

next = getc(in);
modes = preproc || quotes || single;
oldcomments = comments;
switch (c) {
	case '#':
		if (newline && !comments)
			preproc = TRUE;
		break;
	case '\n':
		if (!demagic)
			preproc = single = quotes = FALSE;
		break;
	case '/':
		if (!quotes && !single && next == '*')
			comments = TRUE;
		break;
	case '*':
		if (comments && next == '/')
			comments = FALSE;
		break;
	case '\'':
		if (!comments && !demagic && !quotes)
			single = !single; 
		break;
	case '"':
		if (!comments && !demagic && !single)
			quotes = !quotes;
		break;
	}
demagic = ( !demagic && (c == '\\') );
newline = ( !demagic && (c == '\n') );
if ( comments != oldcomments )
	next = getc(in);	/* Non vogliamo doverlo leggere ancora! */
} while (comments || oldcomments);
if ((!isprint(c) || c == ' ') && !quotes && !single) c |= WS;
	return(c);
}

/* Funzione principale di gestione - Lancia WbenchRun, apre i files di I/O
secondo quanto specificato dall'utente, chiama le funzioni di pulizia
e ritorna educatamente al CLI.
L'uso di _main() invece di main() e' una raffinatezza consentita solo ai
piu' esperti (e da ora, anche ai lettori di EnigmA AmigA Disk!).
Tale ridenominazione della funzione principale consente al compilatore
di non inserire una grossa quantita' di codice di startup, cioe' quello
responsabile della crezione di una interfaccia software tipo UNIX per lo
I/O. Pare che il C, essendo stato sviluppato sotto UNIX, non possa vivere
senza una interfaccia di quel tipo, anche perche' e' descritta nella
"bibbia" di Kernighan & Ritchie. In realta', come dice John Toebes (lo
autore di Lattice C V4.00), bisogna cercare sempre di scrivere programmi
per Amiga, NON per Unix! Si perde in portatilita' (ma tanto Intuition
e' disponibile solo su Amiga, quindi il programma e' comunque non
trasportabile), ma si guadagna in efficienza.
Si noti che tale ridenominazione, che elimina anche 4K di codice inutile,
e' consentita solo se non si usano 'argc' o 'argv', cioe' gli
argomenti sulla linea di comando, ne' lo I/O standard.
*/

void _main()
{
register FILE *in, *out, *fopen();

if ( WbenchRun() == FALSE)
	Cleanup(5);

if (!(out = fopen(&StringGadget2[0],"w")))
	Cleanup("11");

if (!(in  = fopen(&StringGadget1[0],"r")))
	Cleanup("12");

SetWindowTitles(window,"Working...",NULL);

strip_blanks(in,out);

fclose(in);
fclose(out);
Cleanup(0);
}

/*************************** End Of File *****************************/
