
/*	@(#)gram.y	2.1		*/
%{#include "defs"
%}

%term NAME SHELLINE START COLON DOUBLECOLON EQUAL A_STRING VERSION

%union
	{
	SHBLOCK yshblock;
	DEPBLOCK ydepblock;
	NAMEBLOCK ynameblock;
	CHARSTAR ycharstring;
	}

%type	<yshblock>	SHELLINE, shlist, shellist
%type	<ynameblock>	NAME, namelist, name
%type	<ydepblock>	deplist, dlist
%type	<ycharstring>	A_STRING


%%

%{
DEPBLOCK pp;
FSTATIC SHBLOCK prevshp;

FSTATIC NAMEBLOCK lefts[NLEFTS];
NAMEBLOCK leftp;
FSTATIC int nlefts;

LINEBLOCK lp, lpp;
FSTATIC DEPBLOCK prevdep;
FSTATIC int sepc;
%}


file:
	| file comline
	;

comline:  START
	| START macrodef
	| START namelist deplist shellist = {
	    if(mainname == NULL && IS_OFF(INTRULE))
		if(lefts[0]->namep[0] != DOT || any(lefts[0]->namep, SLASH) )
			mainname = lefts[0];
	    while( --nlefts >= 0)
	    {
		leftp = lefts[nlefts];
		if(leftp->septype == 0)
			leftp->septype = sepc;
		else if(leftp->septype != sepc)
			fprintf(stderr, "Inconsistent rules lines for `%s'\n",
				leftp->namep);
		else if(sepc==ALLDEPS && *(leftp->namep)!=DOT && $4!=0)
		{
			for(lp=leftp->linep; lp->nextline!=0; lp=lp->nextline)
			    if(lp->shp)
				fprintf(stderr, "Multiple rules lines for `%s'\n",
				    leftp->namep);
		}

		lp = ALLOC(lineblock);
		lp->nextline = 0;
		lp->depp = $3;
		lp->shp = $4;

		if(equal(leftp->namep, ".SUFFIXES") && $3==0)
			leftp->linep = 0;
		else if(leftp->linep == 0)
			leftp->linep = lp;
		else
		{
			for(lpp = leftp->linep; lpp->nextline!=0;
				lpp = lpp->nextline) ;
				if(sepc==ALLDEPS && leftp->namep[0]==DOT)
					lpp->shp = 0;
			lpp->nextline = lp;
		}
	    }
	}
	| error
	;

macrodef:	NAME EQUAL A_STRING =
	{
		setvar($1, $3);
	}
	;

namelist: name	= { lefts[0] = $1; nlefts = 1; }
	| namelist name	= { lefts[nlefts++] = $2;
	    	if(nlefts>NLEFTS) fatal("Too many lefts"); }
	;

name:	NAME =
	{
	if(($$ = srchname($1)) == 0)
		$$ = makename($1);
	}
	| NAME VERSION = 
	{
	if(($$ = srchname($1)) == 0)
		$$ = makename($1);
	}
	;

deplist:	= { fatal1("Must be a separator on rules line %d", yylineno); }
	| dlist
	;

dlist:  sepchar	= { prevdep = 0;  $$ = 0; }
	| dlist name	= {
			  pp = ALLOC(depblock);
			  pp->nextdep = 0;
			  pp->depname = $2;
			  if(prevdep == 0) $$ = pp;
			  else  prevdep->nextdep = pp;
			  prevdep = pp;
			  }
	;

sepchar:  COLON 	= { sepc = ALLDEPS; }
	| DOUBLECOLON	= { sepc = SOMEDEPS; }
	;

shellist:	= {$$ = 0; }
	| shlist = { $$ = $1; }
	;

shlist:	SHELLINE   = { $$ = $1;  prevshp = $1; }
	| shlist SHELLINE = { $$ = $1;
			prevshp->nextsh = $2;
			prevshp = $2;
			}
	;


%%

/*	@(#)lex.c	3.1	*/

#include "ctype.h"
CHARSTAR zznextc;	/* zero if need another line; otherwise points to next char */
int yylineno;
static char inmacro = NO;

yylex()
{
	register CHARSTAR p;
	register CHARSTAR q;
	static char word[128];
	CHARSTAR pword;

	pword = word;
	if(zznextc == 0)
		return( nextlin() );

	while( isspace(*zznextc) )
		++zznextc;

	if(inmacro == YES)
	{
		inmacro = NO;
		yylval.ycharstring = copys(zznextc);
		zznextc = 0;
		return(A_STRING);
	}

	if(*zznextc == CNULL)
		return( nextlin() );

	if(*zznextc == KOLON)
	{
		if(*++zznextc == KOLON)
		{
			++zznextc;
			return(DOUBLECOLON);
		}
		else
			return(COLON);
	}

	if(*zznextc == EQUALS)
	{
		inmacro = YES;
		++zznextc;
		return(EQUAL);
	}

	if(*zznextc == SKOLON)
		return( retsh(zznextc) );

	p = zznextc;
	q = word;

	while( ! ( funny[*p] & TERMINAL) )
		*q++ = *p++;

	if(p != zznextc)
	{
		*q = CNULL;
		yylval.ycharstring = copys(pword);
		if(*p == RCURLY)
		{
			zznextc = p+1;
			return(VERSION);
		}
		if(*p == LCURLY)
			p++;
		zznextc = p;
		return(NAME);
		}

	else
	{
		fprintf(stderr,"Bad character %c (octal %o), line %d",
			*zznextc,*zznextc,yylineno);
		fatal(Nullstr);
	}
	return(0);	/* never executed */
}


retsh(q)
register CHARSTAR q;
{
	register CHARSTAR p;
	register int c;
	extern CHARSTAR *linesptr;
	SHBLOCK sp;

	for(p=q+1 ; *p==BLANK||*p==TAB ; ++p)  ;

	sp = ALLOC(shblock);
	sp->nextsh = 0;
	sp->shbp = (fin == NULL ? p : copys(p) );
	yylval.yshblock = sp;
	zznextc = 0;
/*
 *	The following if-else "thing" eats up newlines within
 *	shell blocks.
 */
	if(fin == NULL)
	{
		if(linesptr[0])
			while(linesptr[1] && equal(linesptr[1], "\n"))
			{
				yylineno++;
				linesptr++;
			}
	}
	else
	{
		while((c = GETC()) == NEWLINE)
			yylineno++;
		if(c != EOF)
			ungetc(c, fin);
	}
	return(SHELLINE);
}

nextlin()
{
	register char c;
	register CHARSTAR p, t;
	static char yytext[INMAX];
	static CHARSTAR yytextl = yytext+INMAX;
	CHARSTAR text;
	char templin[INMAX];
	char lastch;
	CHARSTAR lastchp;
	extern CHARSTAR *linesptr;
	int incom;
	int kc;
	int nflg;
	int poundflg;

again:
	incom = 0;
	zznextc = 0;
	poundflg = 0;

	if(fin == NULL)
	{
		if( (text = *linesptr++) == 0)
			return(0);
		++yylineno;
		copstr(yytext, text);
	}

	else
	{
		yytext[0] = CNULL;
		for(p=yytext ; ; ++p)
		{
			if(p > yytextl)
				fatal("line too long");
			kc = GETC();
			if(kc == EOF)
			{
				*p = CNULL;
				return(0);
			}
			else if(kc == SKOLON)
				++incom;
			else if (kc == TAB && p == yytext)
				++incom;
			else if (kc==POUND && !incom && yytext[0] != TAB)
			{
				poundflg++;
				kc = CNULL;
			}
			else if (kc == NEWLINE)
			{
				++yylineno;
				if(p==yytext || p[-1]!=BACKSLASH)
					break;
				if(incom || yytext[0] == TAB)
					*p++ = NEWLINE;
				else
					p[-1] = BLANK;
				nflg = YES;
				while( kc = GETC())
				{
					if(kc != TAB && kc != BLANK && kc != NEWLINE)
						break;
					if(incom || yytext[0] == TAB)
					{
						if(nflg == YES && kc == TAB)
						{
							nflg = NO;
							continue;
						}
						if(kc == NEWLINE)
						{
							nflg = YES;
						}

						*p++ = kc;
					}
					if(kc == NEWLINE)
						++yylineno;
				}

				if(kc == EOF)
				{
					*p = CNULL;
					return(0);
				}
			}
			*p = kc;
		}
		*p = CNULL;
		text = yytext;
	}

	c = text[0];

	if(c == TAB)  
		return( retsh(text) );

/*
 *	DO include FILES HERE.
 */
	if(sindex(text, "include") == 0 && (text[7] == BLANK || text[7] == TAB))
	{
		CHARSTAR pfile;

		for(p = &text[8]; *p != CNULL; p++)
			if(*p != TAB ||
			   *p != BLANK)
				break;
		pfile = p;
		for(;	*p != CNULL	&&
			*p != NEWLINE	&&
			*p != TAB	&&
			*p != BLANK; p++);
		if(*p != CNULL)
			*p = CNULL;

/*
 *	Start using new file.
 */
		fstack(pfile, &fin, &yylineno);
		goto again;
	}
	if(isalpha(c) || isdigit(c) || c==BLANK || c==DOT)
		for(p=text+1; *p!=CNULL; p++)
			if(*p == KOLON || *p == EQUALS)
				break;

/* substtitute for macros on dependency line up to the semicolon if any */
	if(*p != EQUALS)
	{
		for(t = yytext ; *t!=CNULL && *t!=SKOLON ; ++t);

		lastchp = t;
		lastch = *t;
		*t = CNULL;

		subst(yytext, templin);	/* Substitute for macros on dep lines */

		if(lastch)
		{
			for(t = templin ; *t ; ++t);
			*t = lastch;
			while( *++t = *++lastchp ) ;
		}

		p = templin;
		t = yytext;
		while( *t++ = *p++ );
	}

	if(poundflg == 0 || yytext[0] != CNULL)
	{
		zznextc = text;
		return(START);
	}
	else
		goto again;
}

#include "stdio.h"

/*
 *	GETC automatically unravels stacked include files. That is,
 *	during include file processing, when a new file is encountered
 *	fstack will stack the FILE pointer argument. Subsequent
 *	calls to GETC with the new FILE pointer will get characters
 *	from the new file. When an EOF is encountered, GETC will
 *	check to see if the file pointer has been stacked. If so,
 *	a character from the previous file will be returned.
 *	The external references are "GETC()" and "fstack(fname,stream,lno)".
 *	"Fstack(stfname,ream,lno)" is used to stack an old file pointer before
 *	the new file is assigned to the same variable. Also stacked are the
 *	file name and the old current lineno, generally, yylineno.
 */



static int morefiles;
static struct sfiles
{
	char sfname[64];
	FILE *sfilep;
	int syylno;
} sfiles[20];

GETC()
{
	register int c;

	c = getc(fin);
	while(c == EOF && morefiles)
	{
		fclose(fin);
		yylineno = sfiles[--morefiles].syylno;
		fin = sfiles[morefiles].sfilep;
		c = getc(fin);
	}
	return(c);
}
fstack(newname, oldfp, oldlno)
register char *newname;
register FILE **oldfp;
register int *oldlno;
{
	if(access(newname, 4) != 0)
/*
 *	This get line can be removed if used elsewhere than make.
 */
	if(get(newname, CD, Nullstr) == NO)
		fatal1("Cannot read or get %s", newname);
	if(IS_ON(DBUG))
		printf("Include file: \"%s\"\n", newname);
/*
 *	Stack the new file name, the old file pointer and the
 *	old yylineno;
 */
	strcat(sfiles[morefiles].sfname, newname);
	sfiles[morefiles].sfilep = *oldfp;
	sfiles[morefiles++].syylno = *oldlno;
	yylineno = 0;
	if((*oldfp=fopen(newname, "r")) == NULL)
		fatal1("Cannot open %s", newname);
}

