#include "stdio.h"

#define OBSIZE 2048  /* output buffer size */
char *outbuf;

/* C command
/* written by John F. Reiser
/* July/August 1978
*/

char *alloc();
char *pbeg,*pbuf,*pend;
char *outp,*inp;
char *newp;
char cinit;

/* some code depends on whether characters are sign or zero extended */
/*  #if '\377' < 0	not used here, old cpp doesn't understand */


char macbit[256 /* alphabet size */+11];
char toktyp[256 /* alphabet size */];


/* a superimposed code is used to reduce the number of calls to the
/* symbol table lookup routine.  (if the kth character of an identifier
/* is 'a' and there are no macro names whose kth character is 'a'
/* then the identifier cannot be a macro name, hence there is no need
/* to look in the symbol table.)  'scw1' enables the test based on
/* single characters and their position in the identifier.  'scw2'
/* enables the test based on adjacent pairs of characters and their
/* position in the identifier.	scw1 typically costs 1 indexed fetch,
/* an AND, and a jump per character of identifier, until the identifier
/* is known as a non-macro name or until the end of the identifier.
/* scw1 is inexpensive.  scw2 typically costs 4 indexed fetches,
/* an add, an AND, and a jump per character of identifier, but it is also
/* slightly more effective at reducing symbol table searches.
/* scw2 usually costs too much because the symbol table search is
/* usually short; but if symbol table search should become expensive,
/* the code is here.
/* using both scw1 and scw2 is of dubious value.
*/


char fastab[256 /* alphabet size */];
char slotab[256 /* alphabet size */];
char *ptrtab;

char buffer[8+512+512+8];

char	*sbf;
char	*savch;


static char warnc = 0xFE    /* special character not legal ASCII or EBCDIC */;

int mactop,fretop;
char *instack[14  /* max buffers of macro pushback */],*bufstack[14],*endbuf[14];
int plvl;	/* parenthesis level during scan for macro actuals */
int maclin; /* line number of macro call requiring actuals */
char *macfil;	/* file name of macro call requiring actuals */
char *macnam;	/* name of macro requiring actuals */
int maclvl; /* # calls since last decrease in nesting level */
char *macforw;	/* pointer which must be exceeded to decrease nesting level */
int macdam; /* offset to macforw due to buffer shifting */


int inctop[10];
char	*fnames[10];
char	*dirnams[10];	/* actual directory of #include files */
FILE	*fins[10];
int lineno[10];

char	*dirs[10];  /* -I and <> directories */
char *strdex(), *copy(), *subst(), *trmdir();
struct symtab *stsym();
FILE *	fin ;
FILE	*fout	;
int nd	= 1;
int pflag;  /* don't put out lines "# 12 foo.c" */
int inasm = FALSE;  /* TRUE if we are in #asm stuff (Don't expand macros) */
int passcom;	/* don't delete comments */
int rflag;  /* allow macro recursion */
char *infile;	/* name of .o file to build dependencies from */
FILE *mout; /* file to place dependencies on */


int ifno;

char *prespc[20];
char **predef = prespc;
char *punspc[20];
char **prund = punspc;
int exfail;
struct symtab {
    char    *name;
    char    *value;
} 
*lastsym, *lookup(), *slookup();


struct symtab *stab;

struct symtab *defloc;
struct symtab *udfloc;
struct symtab *incloc;
struct symtab *ifloc;
struct symtab *elsloc;
struct symtab *eifloc;
struct symtab *ifdloc;
struct symtab *ifnloc;
struct symtab *lneloc;
struct symtab *asmloc;
struct symtab *easmloc;
struct symtab *ulnloc;
struct symtab *uflloc;
int trulvl;
int flslvl;

sayline(where)
int where;
{
    if (pflag==0) fprintf(fout,"#line %d \"%s\"\n", lineno[ifno], fnames[ifno]);
}

/* data structure guide
/*
/* most of the scanning takes place in the buffer:
/*
/*  (low address)					      (high address)
/*  pbeg			   pbuf 				pend
/*  |	   <-- BUFSIZ chars -->      |	       <-- BUFSIZ chars -->	   |
/*  _______________________________________________________________________
/* |_______________________________________________________________________|
/*	    |		    |		    |
/*	    |<-- waiting -->|		    |<-- waiting -->
/*	    |	 to be	    |<-- current -->|	 to be
/*	    |	 written    |	 token	    |	 scanned
/*	    |		    |		    |
/*	    outp	    inp 	    p
/*
/*  *outp   first char not yet written to output file
/*  *inp    first char of current token
/*  *p	    first char not yet scanned
/*
/* macro expansion: write from *outp to *inp (chars waiting to be written),
/* ignore from *inp to *p (chars of the macro call), place generated
/* characters in front of *p (in reverse order), update pointers,
/* resume scanning.
/*
/* symbol table pointers point to just beyond the end of macro definitions;
/* the first preceding character is the number of formal parameters.
/* the appearance of a formal in the body of a definition is marked by
/* 2 chars: the char WARN, and a char containing the parameter number.
/* the first char of a definition is preceded by a zero character.
/*
/* when macro expansion attempts to back up over the beginning of the
/* buffer, some characters preceding *pend are saved in a side bu*//*ffer,
/* the address of the side buffer is put on 'instack', and the rest
/* of the main buffer is moved to the right.  the end of the saved buffer
/* is kept in 'endbuf' since there may be nulls in the saved buffer.
/*
/* similar action is taken when an 'include' statement is processed,
/* except that the main buffer must be completely emptied.  the array
/* element 'inctop[ifno]' records the last side buffer saved when
/* file 'ifno' was included.  these buffers remain dormant while
/* the file is being read, and are reactivated at end-of-file.
/*
/* instack[0 : mactop] holds the addresses of all pending side buffers.
/* instack[inctop[ifno]+1 : mactop-1] holds the addresses of the side
/* buffers which are "live"; the side buffers instack[0 : inctop[ifno]]
/* are dormant, waiting for end-of-file on the current file.
/*
/* space for side buffers is obtained from 'savch' and is never returned.
/* bufstack[0:fretop-1] holds addresses of side buffers which
/* are available for use.
*/

dump() {
    /* write part of buffer which lies between	outp  and  inp .
    /* this should be a direct call to 'write', but the system slows to a crawl
    /* if it has to do an unaligned copy.  thus we buffer.  this silly loop
    /* is 15% of the total time, thus even the 'putc' macro is too slow.
    */
    register char *p1,*p2; 
    register FILE *f;
    if ((p1=outp)==inp || flslvl!=0) return;

    f=fout;

    while (p1<inp) putc(*p1++,f);
    outp=p1;
}

char *
refill(p) register char *p; 
{
    /* dump buffer.  save chars from inp to p.	read into buffer at pbuf,
    /* contiguous with p.  update pointers, return new p.
    */
    register char *np,*op; 
    register int ninbuf;
    int n; 
    int tlin; 
    char *tfil; /* Former block-locals */
    dump(); 
    np=pbuf-(p-inp); 
    op=inp;
    if ( (pbeg>=(np+1))) {
	pperror("token too long",0,0); 
	np=pbeg; 
	p=inp+512;
    }
    macdam += np-inp; 
    outp=inp=np;
    while (op<p) *np++= *op++;
    p=np;
    for (;;) {
	if (mactop>inctop[ifno]) {/* retrieve hunk of pushed-back macro text */
	    op=instack[--mactop]; 
	    np=pbuf;
	    do {
		while (*np++= *op++);
	    } 
	    while (op<endbuf[mactop]); 
	    pend=np-1;
	    /* make buffer space avail for 'include' processing */
	    if (fretop<14   /* max buffers of macro pushback */) bufstack[fretop++]=instack[mactop];
	    return(p);
	} 
	else {/* get more text from file(s) */
	    maclvl=0;
	    if (0<(ninbuf=fread(pbuf,1,512,fin))) {
		pend=pbuf+ninbuf; 
		*pend='\0';
		return(p);
	    }
	    /* end of #include file */
	    if (ifno==0) {/* end of input */
		if (plvl!=0) {
		    n=plvl;
		    tlin=lineno[ifno]; 
		    tfil=fnames[ifno];
		    lineno[ifno]=maclin; 
		    fnames[ifno]=macfil;
		    pperror("%s: unterminated macro call",macnam,0);
		    lineno[ifno]=tlin; 
		    fnames[ifno]=tfil;
		    np=p; 
		    *np++='\n'; /* shut off unterminated quoted string */
		    while (--n>=0) *np++=')';	/* supply missing parens */
		    pend=np; 
		    *np='\0'; 
		    if (plvl<0) plvl=0;
		    return(p);
		}
		if (trulvl || flslvl)
		    pperror("missing endif",0,0);
		inp=p; 
		dump(); 
		exit(exfail);
	    }
	    fclose(fin); 
	    fin=fins[--ifno]; 
	    dirs[0]=dirnams[ifno]; 
	    sayline( 3);
	}
    }
}


char *
cotoken(p) register char *p; 
{
    register int c,i; 
    char quoc;
    static int state = 0;

    if (state!=0) goto prevlf;
    for (;;) {
again:
	while (! (ptrtab[*p++]&2));
	switch (*(inp=p-1)) {
	case 0: 
	    {
		if ( ((--p)>=pend)) {
		    p=refill(p); 
		    goto again;
		}
		else ++p; /* ignore null byte */
	    } 
	    break;
	case '|': 
	case '&': 
	    for (;;) {/* sloscan only */
		if (*p++== *inp) break;
		if ( ((--p)>=pend)) p=refill(p);
		else break;
	    } 
	    break;
	case '=': 
	case '!': 
	    for (;;) {/* sloscan only */
		if (*p++=='=') break;
		if ( ((--p)>=pend)) p=refill(p);
		else break;
	    } 
	    break;
	case '<': 
	case '>': 
	    for (;;) {/* sloscan only */
		if (*p++=='=' || p[-2]==p[-1]) break;
		if ( ((--p)>=pend)) p=refill(p);
		else break;
	    } 
	    break;
	case '\\': 
	    for (;;) {
		if (*p++=='\n') {
		    ++lineno[ifno]; 
		    break;
		}
		if ( ((--p)>=pend)) p=refill(p);
		else {
		    ++p; 
		    break;
		}
	    } 
	    break;
	case '/': 
	    for (;;) {
		if (*p++=='*') {/* comment */
		    if (!passcom) {
			inp=p-2; 
			dump(); 
			++flslvl;
		    }
		    for (;;) {
			while (! ((fastab+128)[*p++]&8));
			if (p[-1]=='*') for (;;) {
			    if (*p++=='/') goto endcom;
			    if ( ((--p)>=pend)) {
				if (!passcom) {
				    inp=p; 
				    p=refill(p);
				}
				else if ((p-inp)>=512) {/* split long comment */
				    inp=p; 
				    p=refill(p);    /* last char written is '*' */
				    if(!flslvl) putc('/',fout); /* terminate first part */
				    /* and fake start of 2nd */
				    outp=inp=p-=3; 
				    *p++='/'; 
				    *p++='*'; 
				    *p++='*';
				} 
				else p=refill(p);
			    } 
			    else break;
			} 
			else if (p[-1]=='\n') {
			    ++lineno[ifno]; 
			    if (!passcom) putc('\n',fout);
			} 
			else if ( ((--p)>=pend)) {
			    if (!passcom) {
				inp=p; 
				p=refill(p);
			    }
			    else if ((p-inp)>=512) {/* split long comment */
				inp=p; 
				p=refill(p);
				if(!flslvl) putc('*',fout);	
				if(!flslvl) putc('/',fout);
				outp=inp=p-=2; 
				*p++='/'; 
				*p++='*';
			    } 
			    else p=refill(p);
			} 
			else ++p; /* ignore null byte */
		    }
endcom:
		    if (!passcom) {
			outp=inp=p; 
			--flslvl; 
			goto again;
		    }
		    break;
		}
		if ( ((--p)>=pend)) p=refill(p);
		else break;
	    } 
	    break;

	case '"': 
	case '\'': 
	    {
		quoc=p[-1];
		for (;;) {
		    while (! ((fastab+128)[*p++]&16));
		    if (p[-1]==quoc) break;
		    if (p[-1]=='\n') {
			--p; 
			break;
		    } /* bare \n terminates quotation */
		    if (p[-1]=='\\') for (;;) {
			if (*p++=='\n') {
			    ++lineno[ifno]; 
			    break;
			} /* escaped \n ignored */
			if ( ((--p)>=pend)) p=refill(p);
			else {
			    ++p; 
			    break;
			}
		    } 
		    else if ( ((--p)>=pend)) p=refill(p);
		    else ++p;	/* it was a different quote character */
		}
	    } 
	    break;
	case '\n': 
	    {
		++lineno[ifno]; 
/*		if (inasm) {
		    inp = p;
		    dump();
		    *--inp = '#';
		    *--inp = '#';
		    p++;
		    if ( ((inp= --p)>=pend)) p=refill(p);
		}		    
*/

		if ((ptrtab==(slotab+128))) {
		    state=1; 
		    return(p);
		}
prevlf:
		state=0;
		for (;;) {
		    if (*p++=='#') return(p);
		    if ( ((inp= --p)>=pend)) p=refill(p);
		    else goto again;
		}
	    } 
	    break;
	case '0': 
	case '1': 
	case '2': 
	case '3': 
	case '4':
	case '5': 
	case '6': 
	case '7': 
	case '8': 
	case '9':
	    for (;;) {
		while ( ((fastab+128)[*p++]&4));
		if ( ((--p)>=pend)) p=refill(p);
		else break;
	    } 
	    break;
	case 'A': 
	case 'B': 
	case 'C': 
	case 'D': 
	case 'E':
	case 'F': 
	case 'G': 
	case 'H': 
	case 'I': 
	case 'J':
	case 'K': 
	case 'L': 
	case 'M': 
	case 'N': 
	case 'O':
	case 'P': 
	case 'Q': 
	case 'R': 
	case 'S': 
	case 'T':
	case 'U': 
	case 'V': 
	case 'W': 
	case 'X': 
	case 'Y':
	case 'Z': 
	case '_':
	case 'a': 
	case 'b': 
	case 'c': 
	case 'd': 
	case 'e':
	case 'f': 
	case 'g': 
	case 'h': 
	case 'i': 
	case 'j':
	case 'k': 
	case 'l': 
	case 'm': 
	case 'n': 
	case 'o':
	case 'p': 
	case 'q': 
	case 'r': 
	case 's': 
	case 't':
	case 'u': 
	case 'v': 
	case 'w': 
	case 'x': 
	case 'y':
	case 'z':


	    if (flslvl) goto nomac;
  	    if (inasm) goto nomac; 
	    for (;;) {
		c= p[-1];			    
		if (! ((macbit+128)[c] & (1))) goto nomac;
		i= *p++; 
		if (!  ((fastab+128)[i]&1)) goto endid;  
		if (! ((macbit+128)[i] & (2))) goto nomac; 
		;
		c= *p++; 
		if (!  ((fastab+128)[c]&1)) goto endid;  
		if (! ((macbit+128)[c] & (4))) goto nomac; 
		;
		i= *p++; 
		if (!  ((fastab+128)[i]&1)) goto endid;  
		if (! ((macbit+128)[i] & (8))) goto nomac; 
		;
		c= *p++; 
		if (!  ((fastab+128)[c]&1)) goto endid;  
		if (! ((macbit+128)[c] & (16))) goto nomac; 
		;
		i= *p++; 
		if (!  ((fastab+128)[i]&1)) goto endid;  
		if (! ((macbit+128)[i] & (32))) goto nomac; 
		;
		c= *p++; 
		if (!  ((fastab+128)[c]&1)) goto endid;  
		if (! ((macbit+128)[c] & (64))) goto nomac; 
		;
		i= *p++; 
		if (!  ((fastab+128)[i]&1)) goto endid;  
		if (! ((macbit+128)[i] & (128))) goto nomac; 
		;
		;
		while (  ((fastab+128)[*p++]&1));
		if ( ((--p)>=pend)) {
		    refill(p); 
		    p=inp+1; 
		    continue;
		}
		goto lokid;
endid:
		if ( ((--p)>=pend)) {
		    refill(p); 
		    p=inp+1; 
		    continue;
		}
		;
lokid:
		slookup(inp,p,0); 
		if (newp) {
		    p=newp; 
		    goto again;
		}
		else break;
nomac:
		while (  ((fastab+128)[*p++]&1));
		if ( ((--p)>=pend)) {
		    p=refill(p); 
		    goto nomac;
		}
		else break;
	    } 
	    break;
	} /* end of switch */

	if ((ptrtab==(slotab+128))) return(p);
    } /* end of infinite loop */
}

char *
skipbl(p) register char *p; 
{/* get next non-blank token */
    do {
	outp=inp=p; 
	p=cotoken(p);
    } 
    while ((toktyp+128)[*inp]==1);
    return(p);
}

char *
unfill(p) register char *p; 
{
    /* take <= BUFSIZ chars from right end of buffer and put them on instack .
    /* slide rest of buffer to the right, update pointers, return new p.
    */
    register char *np,*op; 
    register int d;
    if (mactop>=14  /* max buffers of macro pushback */) {
	pperror("%s: too much pushback",macnam,0);
	p=inp=pend; 
	dump(); /* begin flushing pushback */
	while (mactop>inctop[ifno]) {
	    p=refill(p); 
	    p=inp=pend; 
	    dump();
	}
    }
    if (fretop>0) np=bufstack[--fretop];
    else {
	np=savch; 
	savch+=512;
	if (savch>=sbf+12000	/* std = 12000, wnj aug 1979 */) {
	    pperror("no space",0,0); 
	    exit(exfail);
	}
	*savch++='\0';
    }
    instack[mactop]=np; 
    op=pend-512; 
    if (op<p) op=p;
    for (;;) {
	while (*np++= *op++); 
	if ( ((op)>=pend)) break;
    } /* out with old */
    endbuf[mactop++]=np;    /* mark end of saved text */
    np=pbuf+512; 
    op=pend-512; 
    pend=np; 
    if (op<p) op=p;
    while (outp<op) *--np= *--op; /* slide over new */
    if ( (pbeg>=(np))) pperror("token too long",0,0);
    d=np-outp; 
    outp+=d; 
    inp+=d; 
    macdam+=d; 
    return(p+d);
}

char *
doincl(p) register char *p; 
{
    int filok,inctype;
    register char *cp; 
    char **dirp,*nfil; 
    char filname[512];

    p=skipbl(p); 
    cp=filname;
    if (*inp++=='<') {/* special <> syntax */
	inctype=1;
	++flslvl;   /* prevent macro expansion */
	for (;;) {
	    outp=inp=p; 
	    p=cotoken(p);
	    if (*inp=='\n') {
		--p; 
		*cp='\0'; 
		break;
	    }
	    if (*inp=='>') {	  
		*cp='\0'; 
		break;
	    }

	    while (inp<p) *cp++= *inp++;
	}
	--flslvl;   /* reenable macro expansion */
    } 
    else if (inp[-1]=='"') {/* regular "" syntax */
	inctype=0;

	while (inp<p) *cp++= *inp++;

	if (*--cp=='"') *cp='\0';
    } 
    else {
	pperror("bad include syntax",0,0); 
	inctype=2;
    }
    /* flush current file to \n , then write \n */
    ++flslvl; 
    do {
	outp=inp=p; 
	p=cotoken(p);
    } 
    while (*inp!='\n'); 
    --flslvl;
    inp=p; 
    dump(); 
    if (inctype==2) return(p);
    /* look for included file */
    if (ifno+1 >=10) {
	pperror("Unreasonable include nesting",0,0); 
	return(p);
    }
    if((nfil=savch)>sbf+12000	    /* std = 12000, wnj aug 1979 */-512) {
	pperror("no space",0,0); 
	exit(exfail);
    }
    filok=0;
    for (dirp=dirs+inctype; *dirp; ++dirp) {
	if ( **dirp=='\0')
	    strcpy(nfil,filname);
	else {
	    strcpy(nfil,*dirp);

	    strcat(nfil,":");


	    strcat(nfil,filname);
	}
	if (NULL!=(fins[ifno+1]=fopen(nfil,"r"))) {
	    filok=1; 
	    fin=fins[++ifno]; 
	    break;
	}
    }
    if (filok==0) pperror("Can't find include file %s",filname,0);
    else {
	lineno[ifno]=1; 
	fnames[ifno]=cp=nfil; 
	while (*cp++); 
	savch=cp;
	dirnams[ifno]=dirs[0]=trmdir(copy(nfil));
	sayline(1);
	/* save current contents of buffer */
	while (! ((p)>=pend)) p=unfill(p);
	inctop[ifno]=mactop;
    }
    return(p);
}

equfrm(a,p1,p2) register char *a,*p1,*p2; 
{
    register char c; 
    int flag;
    c= *p2; 
    *p2='\0';
    flag=strcmp(a,p1); 
    *p2=c; 
    return(flag==0);
}

char *
dodef(p) char *p; 
{/* process '#define' */
    register char *pin,*psav,*cf;
    char **pf,**qf; 
    int b,c,params; 
    struct symtab *np;
    char *oldval,*oldsavch;
    char *formal[31 /* max number of formals/actuals to a macro */]; /* formal[n] is name of nth formal */
    char formtxt[512]; /* space for formal names */
    char quoc;	 /* Former block-local */

    if (savch>sbf+12000	    /* std = 12000, wnj aug 1979 */-512) {
	pperror("too much defining",0,0); 
	return(p);
    }
    oldsavch=savch; /* to reclaim space if redefinition */
    ++flslvl; /* prevent macro expansion during 'define' */
    p=skipbl(p); 
    pin=inp;
    if ((toktyp+128)[*pin]!=2) {
	ppwarn("illegal macro name"); 
	while (*inp!='\n') p=skipbl(p); 
	return(p);
    }
    np=slookup(pin,p,1);
    if (oldval=np->value) savch=oldsavch;   /* was previously defined */
    b=1; 
    cf=pin;
    while (cf<p) {/* update macbit */
	c= *cf++;  
	((macbit+128)[c] |= (b)); 
	b=(b+b)&0xFF;
	if (cf!=p) ;
	else ;
    }
    params=0; 
    outp=inp=p; 
    p=cotoken(p); 
    pin=inp;
    if (*pin=='(') {/* with parameters; identify the formals */
	cf=formtxt; 
	pf=formal;
	for (;;) {
	    p=skipbl(p); 
	    pin=inp;
	    if (*pin=='\n') {
		--lineno[ifno]; 
		--p; 
		pperror("%s: missing )",np->name,0); 
		break;
	    }
	    if (*pin==')') break;
	    if (*pin==',') continue;
	    if ((toktyp+128)[*pin]!=2) {
		c= *p; 
		*p='\0'; 
		pperror("bad formal: %s",pin,0); 
		*p=c;
	    } 
	    else if (pf>= &formal[31	/* max number of formals/actuals to a macro */]) {
		c= *p; 
		*p='\0'; 
		pperror("too many formals: %s",pin,0); 
		*p=c;
	    } 
	    else {
		*pf++=cf; 
		while (pin<p) *cf++= *pin++; 
		*cf++='\0'; 
		++params;
	    }
	}
	if (params==0) --params; /* #define foo() ... */
    } 
    else if (*pin=='\n') {
	--lineno[ifno]; 
	--p;
    }
    /* remember beginning of macro body, so that we can
	/* warn if a redefinition is different from old value.
	*/
    oldsavch=psav=savch;
    for (;;) {/* accumulate definition until linefeed */
	outp=inp=p; 
	p=cotoken(p); 
	pin=inp;
	if (*pin=='\\' && pin[1]=='\n') {
	    putc('\n',fout); 
	    continue;
	}   /* ignore escaped lf */
	if (*pin=='\n') break;
	if (params) {/* mark the appearance of formals in the definiton */
	    if ((toktyp+128)[*pin]==2) {
		for (qf=pf; --qf>=formal; ) {
		    if (equfrm(*qf,pin,p)) {
			*psav++=qf-formal+1; 
			*psav++=0xFE	/* special character not legal ASCII or EBCDIC */; 
			pin=p; 
			break;
		    }
		}
	    } 
	    else if (*pin=='"' || *pin=='\''

	    ) {/* inside quotation marks, too */
		quoc= *pin;
		for (*psav++= *pin++; pin<p && *pin!=quoc; ) {
		    while (pin<p && !  ((fastab+128)[*pin]&1)) *psav++= *pin++;
		    cf=pin; 
		    while (cf<p &&   ((fastab+128)[*cf]&1)) ++cf;
		    for (qf=pf; --qf>=formal; ) {
			if (equfrm(*qf,pin,cf)) {
			    *psav++=qf-formal+1; 
			    *psav++=0xFE    /* special character not legal ASCII or EBCDIC */; 
			    pin=cf; 
			    break;
			}
		    }
		    while (pin<cf) *psav++= *pin++;
		}
	    }
	}
	while (pin<p) *psav++= *pin++;
    }
    *psav++=params; 
    *psav++='\0';
    if ((cf=oldval)!=NULL) {/* redefinition */
	--cf;	/* skip no. of params, which may be zero */
	while (*--cf);	/* go back to the beginning */
	if (0!=strcmp(++cf,oldsavch)) {/* redefinition different from old */
	    --lineno[ifno]; 
	    ppwarn("%s redefined",np->name); 
	    ++lineno[ifno];
	    np->value=psav-1;
	} 
	else psav=oldsavch; /* identical redef.; reclaim space */
    } 
    else np->value=psav-1;
    --flslvl; 
    inp=pin; 
    savch=psav; 
    return(p);
}


char *
control(p) register char *p; 
{/* find and handle preprocessor control lines */
    register struct symtab *np;
    char *cp, *cp2, *savestring();  /* Former block-locals */
    for (;;) {
	ptrtab=fastab+128; 
	p=cotoken(p); 
	if (*inp=='\n') ++inp; 
	dump();
	ptrtab=slotab+128; 
	p=skipbl(p);
	*--inp='#'; 
	outp=inp; 
	++flslvl; 
	np=slookup(inp,p,0); 
	--flslvl;
	if (np==defloc) {/* define */
	    if (flslvl==0) {
		p=dodef(p); 
		continue;
	    }
	} 
	else if (np==incloc) {/* include */
	    if (flslvl==0) {
		p=doincl(p); 
		continue;
	    }
	} 
	else if (np==ifnloc) {/* ifndef */
	    ++flslvl; 
	    p=skipbl(p); 
	    np=slookup(inp,p,0); 
	    --flslvl;
	    if (flslvl==0 && np->value==0) ++trulvl;
	    else ++flslvl;
	} 
	else if (np==ifdloc) {/* ifdef */
	    ++flslvl; 
	    p=skipbl(p); 
	    np=slookup(inp,p,0); 
	    --flslvl;
	    if (flslvl==0 && np->value!=0) ++trulvl;
	    else ++flslvl;
	} 
	else if (np==eifloc) {/* endif */
	    if (flslvl) {
		if (--flslvl==0) sayline( 2);
	    }
	    else if (trulvl) --trulvl;
	    else pperror("If-less endif",0,0);
	} 
	else if (np==elsloc) {/* else */
	    if (flslvl) {
		if (--flslvl!=0) ++flslvl;
		else {
		    ++trulvl; 
		    sayline( 2);
		}
	    }
	    else if (trulvl) {
		++flslvl; 
		--trulvl;
	    }
	    else pperror("If-less else",0,0);
	} 
	else if (np==udfloc) {/* undefine */
	    if (flslvl==0) {
		++flslvl; 
		p=skipbl(p); 
		slookup(inp,p,0xFE  /* special character not legal ASCII or EBCDIC */); 
		--flslvl;
	    }
	} 
	else if (np==ifloc) {/* if */

	    newp=p;
	    if (flslvl==0 && yyparse()) ++trulvl; 
	    else ++flslvl;
	    p=newp;

	} 

        else if (np==asmloc) {/* asm */
	    if (flslvl==0) {
		if (inasm)
		    pperror("Duplicate #asm",0,0);
		else {
 		    inasm = TRUE;
		    outp=inp=p; 
	 	    *--outp='m'; 
		    *--outp='s'; 
		    *--outp='a'; 
		    *--outp='#'; 
		    while (*inp!='\n') p=cotoken(p);
		}
	    }
    	}

        else if (np==easmloc) {/* endasm */
	    if (flslvl==0) {
		if ( !inasm)
		    pperror("Unexpected #endasm",0,0);
		else {
 		    inasm = FALSE;
  	 	    outp=inp=p; 
		    *--outp='m'; 
		    *--outp='s'; 
		    *--outp='a'; 
		    *--outp='d'; 
		    *--outp='n'; 
		    *--outp='e'; 
		    *--outp='#'; 
		    while (*inp!='\n') p=cotoken(p);
		}
	    }
    	}

	else if (np==lneloc) {/* line */
	    if (flslvl==0 && pflag==0) {
		outp=inp=p; 
		*--outp='e'; 
		*--outp='n'; 
		*--outp='i'; 
		*--outp='l'; 
		*--outp='#'; 
		while (*inp!='\n') p=cotoken(p);
		cp = outp + 5;
		while (isspace(*cp) && cp < inp)
		    cp++;
		while (isdigit(*cp) && cp < inp)
		    cp++;
		while (*cp != '"' && cp < inp)
		    cp++;
		if (cp < inp) {
		    cp++;
		    cp2 = cp;
		    while (*cp2 != '"' && cp2 < inp)
			cp2++;
		    fnames[ifno] = savestring(cp, cp2);
		}
		continue;
	    }
	} 
	else if (*++inp=='\n') outp=inp;    /* allows blank line after # */
	else pperror("undefined control",0,0);
	/* flush to lf */
	++flslvl; 
	while (*inp!='\n') {
	    outp=inp=p; 
	    p=cotoken(p);
	} 
	--flslvl;
    }
}

char *
savestring(start, finish)
register char *start, *finish;
{
    char *retbuf;
    register char *cp;

    retbuf = (char *) alloc(finish - start + 1, sizeof (char));
    cp = retbuf;
    while (start < finish)
	*cp++ = *start++;
    *cp = 0;
    return(retbuf);
}

struct symtab *
stsym(s) register char *s; 
{
    char buf[512]; 
    register char *p;

    /* make definition look exactly like end of #define line */
    /* copy to avoid running off end of world when param list is at end */
    p=buf; 
    while (*p++= *s++);
    p=buf; 
    while (  ((fastab+128)[*p++]&1)); /* skip first identifier */
    if (*--p=='=') {
	*p++=' '; 
	while (*p++);
    }
    else {
	s=" 1"; 
	while (*p++= *s++);
    }
    pend=p; 
    *--p='\n';
    ptrtab=slotab+128; 
    dodef(buf); 
    return(lastsym);
}

struct symtab *
ppsym(s) char *s; 
{/* kluge */
    register struct symtab *sp;
    cinit='#'; 
    *savch++='#'; 
    sp=stsym(s); 
    --sp->name; 
    cinit=0; 
    return(sp);
}

/* VARARGS1 */
pperror(s,x,y) char *s; 
{
    if (fnames[ifno][0]) fprintf(stderr,

    "%s: ",

    fnames[ifno]);
    fprintf(stderr, "%d: ",lineno[ifno]);
    fprintf(stderr, s, x, y);
    fprintf(stderr,"\n");
    ++exfail;
}

yyerror(s,a,b) char *s; 
{
    pperror(s,a,b);
}

ppwarn(s,x) char *s; 
{
    int fail;
    fail = exfail;
    exfail = -1;
    pperror(s,x,0);
    exfail = fail;
}

struct symtab *
lookup(namep, enterf)
char *namep;
{
    register char *np, *snp;
    register int c, i; 
    int around;
    register struct symtab *sp;

    /* namep had better not be too long (currently, <=NCPS chars) */
    np=namep; 
    around=0; 
    i=cinit;
    while (c= *np++) i += i+c; 
    c=i;    /* c=i for register usage on pdp11 */
    c %= 500	    /* std = 500, wnj aug 1979 */; 
    if (c<0) c += 500	    /* std = 500, wnj aug 1979 */;
    sp = &stab[c];
    while (snp=sp->name) {
	np = namep;
	while (*snp++ == *np) if (*np++ == '\0') {
	    if (enterf==0xFE	/* special character not legal ASCII or EBCDIC */) {
		sp->name[0]= 0xFE   /* special character not legal ASCII or EBCDIC */; 
		sp->value=0;
	    }
	    return(lastsym=sp);
	}
	if (--sp < &stab[0])
	    if (around) {
		pperror("too many defines", 0,0); 
		exit(exfail);
	    }
	    else {
		++around; 
		sp = &stab[500     /* std = 500, wnj aug 1979 */-1];
	    }
    }
    if (enterf==1) sp->name=namep;
    return(lastsym=sp);
}

struct symtab *
slookup(p1,p2,enterf) register char *p1,*p2; 
int enterf;
{
    register char *p3; 
    char c2,c3; 
    struct symtab *np;
    c2= *p2; 
    *p2='\0';	/* mark end of token */
    if ((p2-p1)>8) p3=p1+8; 
    else p3=p2;
    c3= *p3; 
    *p3='\0';	/* truncate to NCPS chars or less */
    if (enterf==1) p1=copy(p1);
    np=lookup(p1,enterf); 
    *p3=c3; 
    *p2=c2;
    if (np->value!=0 && flslvl==0) newp=subst(p2,np);
    else newp=0;
    return(np);
}

char *
subst(p,sp) register char *p; 
struct symtab *sp; 
{
    static char match[]="%s: argument mismatch";
    register char *ca,*vp; 
    int params;
    register char **pa;  /* Former block-local */
    char *actual[31 /* max number of formals/actuals to a macro */]; /* actual[n] is text of nth actual   */
    char actused[31 /* max number of formals/actuals to a macro */]; /* for newline processing in actuals */
    char acttxt[512];  /* space for actuals */
    int  nlines;
    nlines = 0;

    if (0==(vp=sp->value)) return(p);
    if ((p-macforw)<=macdam) {
	if (++maclvl>500	/* std = 500, wnj aug 1979 */ && !rflag) {
	    pperror("%s: macro recursion",sp->name,0); 
	    return(p);
	}
    } 
    else maclvl=0;  /* level decreased */
    macforw=p; 
    macdam=0;	/* new target for decrease in level */
    macnam=sp->name;
    dump();
    if (sp==ulnloc) {
	vp=acttxt; 
	*vp++='\0';
	sprintf(vp,"%d",lineno[ifno]); 
	while (*vp++);
    } 
    else if (sp==uflloc) {
	vp=acttxt; 
	*vp++='\0';
	sprintf(vp,"\"%s\"",fnames[ifno]); 
	while (*vp++);
    }
    if (0!=(params= *--vp&0xFF)) {/* definition calls for params */
	ca=acttxt; 
	pa=actual;
	if (params==0xFF) params=1; /* #define foo() ... */
	ptrtab=slotab+128; 
	++flslvl; /* no expansion during search for actuals */
	plvl= -1;
	do p=skipbl(p); 
	while (*inp=='\n'); /* skip \n too */
	if (*inp=='(') {
	    maclin=lineno[ifno]; 
	    macfil=fnames[ifno];
	    for (plvl=1; plvl!=0; ) {
		*ca++='\0';
		for (;;) {
		    outp=inp=p; 
		    p=cotoken(p);
		    if (*inp=='(') ++plvl;
		    if (*inp==')' && --plvl==0) {
			--params; 
			break;
		    }
		    if (plvl==1 && *inp==',') {
			--params; 
			break;
		    }
		    while (inp<p) *ca++= *inp++;
		    if (ca> &acttxt[512])
			pperror("%s: actuals too long",sp->name,0);
		}
		if (pa>= &actual[31 /* max number of formals/actuals to a macro */]) ppwarn(match,sp->name);
		else { 
		    actused[pa-actual]=0; 
		    *pa++=ca; 
		}
	    }
	    nlines = lineno[ifno] - maclin;
	    lineno[ifno] = maclin; /* don't count newlines here */
	}
	if (params!=0) ppwarn(match,sp->name);
	while (--params>=0) *pa++=""+1; /* null string for missing actuals */
	--flslvl;  
	ptrtab=fastab+128;
    }
    for (;;) {/* push definition onto front of input stack */
	while (! ((fastab+128)[*--vp]&32)) {
	    if ( (pbeg>=(p))) {
		outp=inp=p; 
		p=unfill(p);
	    }
	    *--p= *vp;
	}
	if (*vp==warnc) {/* insert actual param */
	    ca=actual[*--vp-1];
	    while (*--ca) {
		if ( (pbeg>=(p))) {
		    outp=inp=p; 
		    p=unfill(p);
		}
		/* Actuals with newlines confuse line numbering */
		if (*ca == '\n' && actused[*vp-1])
		    if (*(ca-1) == '\\') ca--;
		    else *--p = ' ';
		else { 
		    *--p= *ca; 
		    if (*ca == '\n') nlines--; 
		}
	    }
	    actused[*vp-1] = 1;
	} 
	else {
	    if (nlines > 0 )
		while (nlines-- > 0)
		    *--p = '\n';
	    break;
	}
    }
    outp=inp=p;
    return(p);
}


char *
trmdir(s) register char *s; 
{
    register char *p;
    p = s;
    while (*p++); 
    --p; 
    while (p>s && *--p!='/');

    if (p==s) *p++='.';

    *p='\0';
    return(s);
}

char *
copy(s) register char *s; 
{
    register char *old;

    old = savch; 
    while (*savch++ = *s++);
    return(old);
}

char *
strdex(s,c) char *s,c; 
{
    while (*s) if (*s++==c) return(--s);
    return(0);
}

yywrap(){ 
    return(1); 
}

main(argc,argv)
char *argv[];
{
    register int i,c;
    register char *p;
    char *tf,**cp2;

    fout = stdout;  
    fin = stdin;
    p="_$ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    i=0;


    sbf = alloc(12000);
    savch = sbf;

    stab = alloc(500 * sizeof(struct symtab));

    outbuf = alloc(OBSIZE);

    while (c= *p++) {
	(fastab+128)[c] |= 1|4|2; 
	(toktyp+128)[c]=2;

    }
    p="0123456789.";
    while (c= *p++) {
	(fastab+128)[c] |= 4|2; 
	(toktyp+128)[c]=3;
    }

    p="\n\"'/\\";

    while (c= *p++) (fastab+128)[c] |= 2;

    p="\n\"'\\";

    while (c= *p++) (fastab+128)[c] |= 16;
    p="*\n"; 
    while (c= *p++) (fastab+128)[c] |= 8;
    (fastab+128)[warnc] |= 32;
    (fastab+128)['\0'] |= 8|16|2|32;
    for (i=256	/* alphabet size */; --i>=0; ) slotab[i]=fastab[i]|2;
    p=" \t\013\f\r";	/* note no \n;	\v not legal for vertical tab? */
    while (c= *p++) (toktyp+128)[c]=1;


    fnames[ifno=0] = ""; 
    dirnams[0]=dirs[0]=".";


    for(i=1; i<argc; i++)
    {
	switch(argv[i][0])
	{
	case '-':

	    switch(argv[i][1]) {

	    case 'P': 
		pflag++;
	    case 'E': 
		continue;
	    case 'R': 
		++rflag; 
		continue;
	    case 'C': 
		passcom++; 
		continue;
	    case 'D':
		if (predef>prespc+20) {
		    pperror("too many -D options, ignoring %s",argv[i],0);
		    continue;
		}
		/* ignore plain "-D" (no argument) */
		if (*(argv[i]+2)) *predef++ = argv[i]+2;
		continue;
	    case 'U':
		if (prund>punspc+20) {
		    pperror("too many -U options, ignoring %s",argv[i],0);
		    continue;
		}
		*prund++ = argv[i]+2;
		continue;
	    case 'I':
		if (nd>8) pperror("excessive -I file (%s) ignored",argv[i],0);
		else dirs[nd++] = argv[i]+2;
		continue;
	    case '\0': 
		continue;
	    default: 
		pperror("unknown flag %s", argv[i],0);
		continue;
	    }
	default:
	    if (fin==stdin) {
		if (NULL==(fin=fopen(argv[i], "r"))) {
		    pperror("No source file %s",argv[i],0); 
		    exit(8);
		}
		fnames[ifno]=copy(argv[i]);
		infile=copy(argv[i]);
		dirs[0]=dirnams[ifno]=trmdir(argv[i]);

		/* too dangerous to have file name in same syntactic position
		   be input or output file depending on file redirections,
		   so force output to stdout, willy-nilly
		    [i don't see what the problem is.  jfr]
		*/
	    } 
	    else if (fout==stdout) {
		if (NULL==(fout=fopen(argv[i], "w"))) {
		    pperror("Can't create %s", argv[i],0); 
		    exit(8);
		} 
		else {
		    fclose(stdout); 
		    setbuf(fout,outbuf);
		    setbsize(fout,OBSIZE);
		}

	    } 
	    else pperror("extraneous name %s", argv[i],0);
	}
    }

    fins[ifno]=fin;
    exfail = 0;
    /* after user -I files here are the standard include libraries */

    dirs[nd++] = "";
    dirs[nd++] = "B";
    dirs[nd++] = "A";


    dirs[nd++] = 0;
    defloc=ppsym("define");
    udfloc=ppsym("undef");
    incloc=ppsym("include");
    elsloc=ppsym("else");
    eifloc=ppsym("endif");
    ifdloc=ppsym("ifdef");
    ifnloc=ppsym("ifndef");
    ifloc=ppsym("if");
    lneloc=ppsym("line");
    asmloc=ppsym("asm");
    easmloc=ppsym("endasm");

    for (i=sizeof(macbit)/sizeof(macbit[0]); --i>=0; ) macbit[i]=0;


    ulnloc=stsym ("__LINE__");
    uflloc=stsym ("__FILE__");

    tf=fnames[ifno]; 
    fnames[ifno]="command line"; 
    lineno[ifno]=1;
    cp2=prespc;
    while (cp2<predef) stsym(*cp2++);
    cp2=punspc;
    while (cp2<prund) {
	if (p=strdex(*cp2, '=')) *p++='\0';
	lookup(*cp2++, 0xFE /* special character not legal ASCII or EBCDIC */);
    }
    fnames[ifno]=tf;
    pbeg=buffer+8; 
    pbuf=pbeg+512; 
    pend=pbuf+512;

    trulvl = 0; 
    flslvl = 0;
    lineno[0] = 1; 
    sayline(1);
    outp=inp=pend;
    control(pend);
    return (exfail);
}



char *alloc(nbytes)
unsigned nbytes;
{
    char *p;

    p = calloc(nbytes,1);
    if ( p == NULL )
    {
	pperror("Allocation failure",0,0);
	exit(1);
    }
    return(p);
}
