/*	@(#)scan.c	2.1		*/
static	char	SCCSID[] = "@(#) scan.c:	2.2 83/07/08";

# include "mfile1.h"
# include <ctype.h>
/* temporarily */

/* character-set translation */
# ifndef CCTRANS
# define CCTRANS(x) (x)
# endif

int flflag = 0;
# ifndef FLOATCVT
double atof();
# define FLOATCVT(x) atof(x)
# endif

# ifndef ASMBUF
# define ASMBUF 50
# endif
char asmbuf[ASMBUF];
char *asmp;
int asm_esc = 0; /* asm escaped used in file */
/* lexical actions */

# define A_ERR 0		/* illegal character */
# define A_LET 1		/* saw a letter */
# define A_DIG 2		/* saw a digit */
# define A_1C 3			/* return a single character */
# define A_STR 4		/* string */
# define A_CC 5			/* character constant */
# define A_BCD 6		/* GCOS BCD constant */
# define A_SL 7			/* saw a / */
# define A_DOT 8		/* saw a . */
# define A_PL 9		/* + */
# define A_MI 10		/* - */
# define A_EQ 11		/* = */
# define A_NOT 12		/* ! */
# define A_LT 13		/* < */
# define A_GT 14		/* > */
# define A_AND 16		/* & */
# define A_OR 17		/* | */
# define A_WS 18		/* whitespace (not \n) */
# define A_NL 19		/* \n */

/* character classes */

# define LEXLET 01
# define LEXDIG 02
# define LEXOCT 04
# define LEXHEX 010
# define LEXWS 020
# define LEXDOT 040

/* reserved word actions */

# define AR_TY 0		/* type word */
# define AR_RW 1		/* simple reserved word */
# define AR_CL 2		/* storage class word */
# define AR_S 3		/* struct */
# define AR_U 4		/* union */
# define AR_E 5		/* enum */
# define AR_A 6		/* asm */

/* text buffer */
# define LXTSZ BUFSIZ		/* lots of room for FLEXNAMES */
char yytext[LXTSZ];
char * lxgcp;
/* If FLEXNAMES defined, let -T determine whether symbols are truncated.
** Otherwise, forcibly truncate all names.
*/

static int truncate_flag =
#ifdef	FLEXNAMES
0;		/* truncate based on -T (non-zero to truncate */
#else
1;		/* force all names <= NCHNAM chars */
#endif

#ifdef STKCK
#	include <string.h>
#	define TRUE	1
	extern char *getenv();
	int stkckflg;	/* global - referenced again in local.c - bfcode() */
#endif

#ifdef	PROFILE
	int proflg;	/* global - referenced again in local.c - bfcode() */
#endif	PROFILE

/* ARGSUSED */
mainp1( argc, argv )
int argc;
char *argv[];
{
	/* control multiple files */

	register i;
	register char *cp;
#ifdef STKCK
	register char *strval;
#endif
	extern int idebug, bdebug, tdebug, edebug, ddebug, xdebug, opdebug;
#ifndef TWOPASS
	extern int zflag;
#endif
	extern unsigned int maxoffset;
	int fdef = 0;

	unsigned int temp = 1;
	i = 0;
	do
	{
		temp <<= 1;
		++i;
	} 	while ( temp != 0 );
	maxoffset = 1 << (i-1);

#ifdef STKCK			/* flag indicates if stack checking is req'd */
	if( ((strval=getenv("M68000")) != NULL) 
		&& (!strcmp(strval,"STACKCHECK")) )
	{
		stkckflg = TRUE;
	}
#endif
	

# ifdef SDB
	/* check specially for -g as first argument */
	if( argc>1 && *(cp=argv[1])=='-' && cp[1]=='g' && !cp[2] )
	{
		extern int gdebug;
		gdebug = 1;
		++argv;
		--argc;
	}
# endif

	for( i=1; i<argc; ++i )
	{
		if( *(cp=argv[i]) == '-' && *++cp == 'X' )
		{
			while( *++cp )
			{
				switch( *cp )
				{

				case 'd':
					++ddebug;
					break;
				case 'i':
					++idebug;
					break;
				case 'b':
					++bdebug;
					break;
				case 't':
					++tdebug;
					break;
				case 'e':
					++edebug;
					break;
				case 'x':
					++xdebug;
					break;
				case 'o':
					++opdebug;
					break;
#ifdef ASSYCOMMENT
				case 's':
					{
						 extern int asdebug;
						++asdebug;
						break;
					}
#ifndef TWOPASS
				case 'z':
					++zflag; /* stin file debug info */
					break;
#endif
#endif ASSYCOMMENT
				case 'T':	/* truncate names */
					++truncate_flag;
					break;
#ifdef	PROFILE
				case 'P':	/* profile code */
					++proflg;
					break;
#endif	PROFILE
				default:
					cerror( "bad option: X%c", *cp );
				}
			}
		}
		else
		{
			if( *(argv[i]) != '-' ) switch( fdef++ )
			{
			case 0:
			case 1:
				if( freopen(argv[i], fdef==1 ? "r" : "w", fdef==1 ? stdin : stdout) == NULL)
				{
					fprintf(stderr, "ccom:can't open %s\n", argv[i]);
					exit(1);
				}
				break;

			default:
				;
			}
		}
	}

#ifdef ONEPASS
	p2init( argc, argv );
#endif

	for( i=0; i<SYMTSZ; ++i ) stab[i].stype = TNULL;

	lxinit();
	tinit();
	mkdope();

	lineno = 1;

	/* dimension table initialization */

	dimtab[NULL] = 0;
	dimtab[VOID] = 0;
	dimtab[CHAR] = SZCHAR;
	dimtab[INT] = SZINT;
	dimtab[FLOAT] = SZFLOAT;
	dimtab[DOUBLE] = SZDOUBLE;
	dimtab[LONG] = SZLONG;
	dimtab[SHORT] = SZSHORT;
	dimtab[UCHAR] = SZCHAR;
	dimtab[USHORT] = SZSHORT;
	dimtab[UNSIGNED] = SZINT;
	dimtab[ULONG] = SZLONG;
	/* starts past any of the above */
	curdim = 16;
	reached = 1;

	yyparse();
	yyaccpt();

# ifdef SDB
	ejsdb();
# endif
# ifdef ENDJOB
	ENDJOB(nerrors?1:0);
# endif
	return(nerrors?1:0);

}

# ifdef ibm

# define CSMASK 0377
# define CSSZ 256

# else

# define CSMASK 0177
# define CSSZ 128

# endif

#ifdef	ASSYCOMMENT
/*
*	We buffer the input on a line by line basis
*	so that we can output the ``C'' source as comments
*	in the assembly language
*/
int	asdebug = 0;
static	char	linebuf[BUFSIZ] =
{
	'\n','\n','\n'
};
static	char	*linecp = &linebuf[0];
#undef	ungetc
#undef	getchar

#define	getchar()	(*linecp ? *linecp++ : eatline() )
#define	ungetc(c, ioptr)	*--linecp=(c)

eatline()
{
	linecp = &linebuf[0];
	if (fgets(linebuf, BUFSIZ, stdin) == NULL)
	{
		*linecp = '\0';
		return( EOF );
	}
	if (asdebug)
		spitline(linebuf);

	return( *linecp++ );
}


/*
** add the original C line to the assembler output as comment,
** unless it is empty.
*/
#define	MARKERCNT	20	/* how often to spit out line markers */
spitline(l)
register char *l;
{
	extern int lineno;

	if( !(lineno%MARKERCNT) )
		printf( "%s -- line %d %s\n", COMMENTSTR, lineno, ftitle );
				/* line marker */
	while( isspace( *l ) )
		l++;
	if( *l )
		printf( "%s %s", COMMENTSTR, l );
}
#endif ASSYCOMMENT

short lxmask[CSSZ+1];

lxenter( s, m )
register char *s;
register short m;
{
	/* enter a mask into lxmask */
	register c;

	while( c= *s++ ) lxmask[c+1] |= m;

}


# define lxget(c,m) (lxgcp=yytext,lxmore(c,m))

lxmore( c, m )
register c, m;
{
	register char *cp;

	*(cp = lxgcp) = c;
	while( (c = getchar()), lxmask[c+1]&m )
	{
		if( cp < &yytext[LXTSZ-1] )
		{
			*++cp = c;
		}
	}
	ungetc(c,stdin);
	*(lxgcp = cp+1) = '\0';
}

struct lxdope
{
	short lxch;	/* the character */
	short lxact;	/* the action to be performed */
	short lxtok;	/* the token number to be returned */
	short lxval;	/* the value to be returned */
} lxdope[] =
{
	'$',	A_ERR,	0,	0,	/* illegal characters go here... */
	'_',	A_LET,	0,	0,	/* letters point here */
	'0',	A_DIG,	0,	0,	/* digits point here */
	' ',	A_WS,	0,	0,	/* whitespace goes here */
	'\n',	A_NL,	0,	0,
	'"',	A_STR,	0,	0,	/* character string */
	'\'',	A_CC,	0,	0,	/* character constant */
	'`',	A_BCD,	0,	0,	/* GCOS BCD constant */
	'(',	A_1C,	LP,	0,
	')',	A_1C,	RP,	0,
	'{',	A_1C,	LC,	0,
	'}',	A_1C,	RC,	0,
	'[',	A_1C,	LB,	0,
	']',	A_1C,	RB,	0,
	'*',	A_1C,	MUL,	MUL,
	'?',	A_1C,	QUEST,	0,
	':',	A_1C,	COLON,	0,
	'+',	A_PL,	PLUS,	PLUS,
	'-',	A_MI,	MINUS,	MINUS,
	'/',	A_SL,	DIVOP,	DIV,
	'%',	A_1C,	DIVOP,	MOD,
	'&',	A_AND,	AND,	AND,
	'|',	A_OR,	OR,	OR,
	'^',	A_1C,	ER,	ER,
	'!',	A_NOT,	UNOP,	NOT,
	'~',	A_1C,	UNOP,	COMPL,
	',',	A_1C,	CM,	CM,
	';',	A_1C,	SM,	0,
	'.',	A_DOT,	STROP,	DOT,
	'<',	A_LT,	RELOP,	LT,
	'>',	A_GT,	RELOP,	GT,
	'=',	A_EQ,	ASSIGN,	ASSIGN,
	-1,	A_1C,	0,	0,
};

struct lxdope *lxcp[CSSZ+1];

lxinit()
{
	register struct lxdope *p;
	register i;
	register char *cp;
	/* set up character classes */

	lxenter( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_", LEXLET );
	lxenter( "0123456789", LEXDIG );
	lxenter( "0123456789abcdefABCDEF", LEXHEX );
	/* \013 should become \v someday; \013 is OK for ASCII and EBCDIC */
	lxenter( " \t\r\b\f\013", LEXWS );
	lxenter( "01234567", LEXOCT );
	lxmask['.'+1] |= LEXDOT;

	/* make lxcp point to appropriate lxdope entry for each character */

	/* initialize error entries */

	for( i= 0; i<=CSSZ; ++i ) lxcp[i] = lxdope;

	/* make unique entries */

	for( p=lxdope; ; ++p )
	{
		lxcp[p->lxch+1] = p;
		if( p->lxch < 0 ) break;
	}

	/* handle letters, digits, and whitespace */
	/* by convention, first, second, and third places */

	cp = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
	while( *cp ) lxcp[*cp++ + 1] = &lxdope[1];
	cp = "123456789";
	while( *cp ) lxcp[*cp++ + 1] = &lxdope[2];
	cp = "\t\b\r\f\013";
	while( *cp ) lxcp[*cp++ + 1] = &lxdope[3];

	/* first line might have title */
	lxtitle();

}

int lxmatch;  /* character to be matched in char or string constant */

lxstr(ct)
register ct;
{
	/* match a string or character constant, up to lxmatch */

	register c;
	register val;
	register i;

	i=0;
	while( (c = getchar()) != lxmatch )
	{
		switch( c )
		{

		case EOF:
			uerror( "lxstr() unexpected EOF" );
			break;

		case '\n':
			uerror( "newline in string or char constant" );
			++lineno;
			break;

		case '\\':
			switch( c = getchar() )
			{

			case '\n':
				++lineno;
				continue;

			default:
				val = c;
				goto mkcc;

			case 'n':
				val = '\n';
				goto mkcc;

			case 'r':
				val = '\r';
				goto mkcc;

			case 'b':
				val = '\b';
				goto mkcc;

			case 't':
				val = '\t';
				goto mkcc;

			case 'f':
				val = '\f';
				goto mkcc;

			case 'v':
				val = '\013';
				goto mkcc;

			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
				val = c-'0';
				c = getchar();  /* try for 2 */
				if( lxmask[c+1] & LEXOCT )
				{
					val = (val<<3) | (c-'0');
					c = getchar();  /* try for 3 */
					if( lxmask[c+1] & LEXOCT )
					{
						val = (val<<3) | (c-'0');
					}
					else ungetc( c ,stdin);
				}
				else ungetc( c ,stdin);

				goto mkcc1;

			}
		default:
			val =c;
mkcc:
			val = CCTRANS(val);
mkcc1:
			if( lxmatch == '\'' )
			{
				/* it is, after all, a "character" constant */
				val = ccast((CONSZ)val,CHAR);
				if( i==0 ) lastcon = val;
				else
				{
# ifdef MAKECC
					MAKECC( val, i );
# else
# ifdef RTOLBYTES
					lastcon <<= SZCHAR;
					lastcon |= (val&BITMASK(SZCHAR));
# else
					lastcon &= BITMASK(SZCHAR*i);
					lastcon |= val << (SZCHAR*i);
# endif
# endif
				}
			}
			else
			{
				 /* stash the byte into the string */
				if( strflg )
				{
					if( ct==0 || i<ct ) putbyte( val );
					else if( i == ct )
						werror(
						"non-null byte ignored in string initializer" );
				}
				else bycode( val, i );
			}
			++i;
			continue;
		}
		break;
	}
	/* end of string or char constant */

	if( lxmatch == '"' )
	{
		if( strflg )
		{
			 /* end the string */
			if( ct==0 || i<ct ) putbyte( 0 );  /* the null at the end */
		}
		else
		{
			  /* the initializer gets a null byte */
			bycode( 0, i++ );
			bycode( -1, i );
			dimtab[curdim] = i;  /* in case of later sizeof ... */
		}
	}
	else
	{
		 /* end the character constant */
		if( i == 0 ) uerror( "empty character constant" );
		if( i>(SZINT/SZCHAR) || ( (pflag||hflag)&&i>1) )
			uerror( "too many characters in character constant" );
	}
}

lxcom()
{
	register c;
	/* saw a /*: process a comment */

	for(;;)
	{

		switch( c = getchar() )
		{

		case EOF:
			uerror( "lxcom() unexpected EOF" );
			return;

		case '\n':
			++lineno;

		default:
			continue;

		case '*':
			if( (c = getchar()) == '/' ) return;
			else ungetc( c ,stdin);
			continue;

# ifdef LINT
		case 'V':
			lxget( c, LEXLET|LEXDIG );
			{
				extern int vaflag;
				register i;
				i = yytext[7]?yytext[7]-'0':0;
				yytext[7] = '\0';
				if( strcmp( yytext, "VARARGS" ) ) continue;
				vaflag = i;
				continue;
			}
		case 'L':
			lxget( c, LEXLET );
			if( strcmp( yytext, "LINTLIBRARY" ) ) continue;
			{
				extern int libflag;
				libflag = 1;
			}
			continue;

		case 'A':
			lxget( c, LEXLET );
			if( strcmp( yytext, "ARGSUSED" ) ) continue;
			{
				extern int argflag, vflag;
				argflag = 1;
				vflag = 0;
			}
			continue;

		case 'N':
			lxget( c, LEXLET );
			if( strcmp( yytext, "NOTREACHED" ) ) continue;
			reached = 0;
			continue;
# endif
		}
	}
}

yylex()
{
	for(;;)
	{

		register lxchar;
		register struct lxdope *p;
		register struct symtab *sp;
		register char *cp;
		register id;

		switch( (p = lxcp[(lxchar = getchar())+1])->lxact )
		{

onechar:
			ungetc( lxchar ,stdin);

		case A_1C:
			/* eat up a single character, and return an opcode */

			yylval.intval = p->lxval;
			return( p->lxtok );

		case A_ERR:
			uerror( "illegal character: %03o (octal)", lxchar );
			break;

		case A_LET:
			/* collect an identifier, and check for reserved word */
			lxget( lxchar, LEXLET|LEXDIG );

			/* 0 means some kind of low level error, >0 reserved,
			** <0 means id
			*/
			if( (lxchar=lxres()) > 0 ) return( lxchar );
			if( lxchar== 0 ) continue;

			if (truncate_flag )
				yytext[NCHNAM] = '\0';	/* truncate name */
			id = lookup( hash(yytext),
					/* tag name for struct/union/enum */
				(stwart&TAGNAME)? STAG:
					/* member name for struct/union */
				(stwart&(INSTRUCT|INUNION|FUNNYNAME))?SMOS:0 );
			sp = &stab[id];
			if( sp->sclass == TYPEDEF && !stwart )
			{
				stwart = instruct;
				yylval.nodep = mkty( sp->stype, sp->dimoff,
				sp->sizoff );
				return( TYPE );
			}
			stwart = (stwart&SEENAME) ? instruct : 0;
			yylval.intval = id;
			return( NAME );

		case A_DIG:
			/* collect a digit string, then look at last one... */
			lastcon = 0;
			lxget( lxchar, LEXDIG );
			switch( lxchar = getchar() )
			{

			case 'x':
			case 'X':
				if( yytext[0] != '0' && !yytext[1] )
					uerror( "illegal hex constant" );
				lxmore( lxchar, LEXHEX );
				/* convert the value */
				{
					for( cp = yytext+2; *cp; ++cp )
					{
						/* this code won't work for all character sets,
						** but seems ok for ascii and ebcdic
						*/
						lastcon <<= 4;
						if( isdigit( *cp ) )
							lastcon += *cp-'0';
						else if( isupper( *cp ) )
							lastcon += *cp - 'A'+ 10;
						else
							lastcon += *cp - 'a'+ 10;
					}
				}

hexlong:
				/* hex and octal constants are long if they
				** fit within 0177777
				*/
				if( lastcon & ~0177777L ) yylval.intval = 1;
				else yylval.intval = 0;

				goto islong;

			case '.':
				lxmore( lxchar, LEXDIG );

getfp:
				if( (lxchar = getchar()) == 'e' || lxchar == 'E' )
				{
					 /* exponent */
				case 'e':
				case 'E':
					if( (lxchar = getchar()) == '+' ||
					    lxchar == '-' )
					{
						*lxgcp++ = 'e';
					}
					else
					{
						ungetc(lxchar,stdin);
						lxchar = 'e';
					}
					lxmore( lxchar, LEXDIG );
					/* now have the whole thing... */
				}
				else
				{
					  /* no exponent */
					ungetc( lxchar ,stdin);
				}
# ifdef NOFLOAT
				if( !flflag )
				{
					flflag = 1;
					werror( "sorry, no floating point" );
				}
				for( cp = yytext; *cp; ++cp )
				{
					if( *cp == 'e' || *cp == 'E' ||
					    *cp == '.' )
					{
						*cp = '\0';
						break;
					}
				}
				goto conv10;
# else
				flflag = 1;
				dcon = FLOATCVT(yytext);
				return( FCON );
# endif

			default:
				ungetc( lxchar ,stdin);
				if( yytext[0] == '0' )
				{
					/* convert in octal */
					for( cp = yytext+1; *cp; ++cp )
					{
						lastcon <<= 3;
						lastcon += *cp - '0';
					}
					goto hexlong;
				}
				else
				{
					/* convert in decimal */
# ifdef NOFLOAT
conv10:
# endif
					for( cp = yytext; *cp; ++cp )
					{
						lastcon = lastcon*10 + *cp - '0';
					}
				}

				/* decide if it is long or not (decimal case) */

				/* if it is positive and fits in 15 bits,
				** or negative and and fits in 15 bits
				** with an extended sign, it is int;
				** otherwise long
				*/
				/* if there is an l or L following,
				** all bets are off...
				*/

				{
					register CONSZ v;
					v = lastcon & ~077777L;
					if( v == 0 || v == ~077777L )
						yylval.intval = 0;
					else yylval.intval = 1;
				}

islong:
				/* finally, look for trailing L or l */
				if( (lxchar=getchar()) == 'L' || lxchar == 'l' )
						yylval.intval = 1;
				else ungetc( lxchar ,stdin);
				return( ICON );
			}

		case A_DOT:
			/* a dot: if followed by a digit, floating point */
			lxchar = getchar();
			if( lxmask[lxchar+1] & LEXDIG )
			{
				ungetc(lxchar,stdin);
				lxget( '.', LEXDIG );
				goto getfp;
			}
			stwart = FUNNYNAME;
			goto onechar;

		case A_STR:
			/* string constant */
			lxmatch = '"';
			return( STRING );

		case A_CC:
			/* character constant */
			lxmatch = '\'';
			lastcon = 0;
			lxstr(0);
			yylval.intval = 0;
			return( ICON );

		case A_BCD:
			{
				register i;
				register j;
				for( i=0; i<LXTSZ; ++i )
				{
					if( ( j = getchar() ) == '`' ) break;
					if( j == '\n' )
					{
						uerror(
						"newline in BCD constant" );
						break;
					}
					yytext[i] = j;
				}
				yytext[i] = '\0';
				if( i>6 )
					uerror(
					"BCD constant exceeds 6 characters" );
# ifdef gcos
				else strtob( yytext, &lastcon, i );
				lastcon >>= 6*(6-i);
# else
				if( yytext[0] == 'u' )
				{
					yylval.intval = UOP0 + yytext[1]-'0';
					return( UNOP );
				}
				uerror( "gcos BCD constant illegal" );
# endif
				yylval.intval = 0;  /* not long */
				return( ICON );
			}

		case A_SL:
			/* / */
			if( (lxchar = getchar()) != '*' ) goto onechar;
			lxcom();
		case A_WS:
			continue;

		case A_NL:
			++lineno;
			lxtitle();
			continue;

		case A_NOT:
			/* ! */
			if( (lxchar = getchar()) != '=' ) goto onechar;
			yylval.intval = NE;
			return( EQUOP );

		case A_MI:
			/* - */
			if( (lxchar = getchar()) == '-' )
			{
				yylval.intval = DECR;
				return( INCOP );
			}
			if( lxchar != '>' ) goto onechar;
			stwart = FUNNYNAME;
			yylval.intval=STREF;
			return( STROP );

		case A_PL:
			/* + */
			if( (lxchar = getchar()) != '+' ) goto onechar;
			yylval.intval = INCR;
			return( INCOP );

		case A_AND:
			/* & */
			if( (lxchar = getchar()) != '&' ) goto onechar;
			return( yylval.intval = ANDAND );

		case A_OR:
			/* | */
			if( (lxchar = getchar()) != '|' ) goto onechar;
			return( yylval.intval = OROR );

		case A_LT:
			/* < */
			if( (lxchar = getchar()) == '<' )
			{
				yylval.intval = LS;
				return( SHIFTOP );
			}
			if( lxchar != '=' ) goto onechar;
			yylval.intval = LE;
			return( RELOP );

		case A_GT:
			/* > */
			if( (lxchar = getchar()) == '>' )
			{
				yylval.intval = RS;
				return(SHIFTOP );
			}
			if( lxchar != '=' ) goto onechar;
			yylval.intval = GE;
			return( RELOP );

		case A_EQ:
			/* = */
			switch( lxchar = getchar() )
			{

			case '=':
				yylval.intval = EQ;
				return( EQUOP );

			default:
				goto onechar;

			}


		default:
			cerror( "yylex error, character %03o (octal)", lxchar );

		}

		/* ordinarily, repeat here... */
		cerror( "out of switch in yylex" );

	}

}

struct lxrdope
{
	/* dope for reserved, in alphabetical order */

	char *lxrch;	/* name of reserved word */
	short lxract;	/* reserved word action */
	short lxrval;	/* value to be returned */
} lxrdope[] =
{
	"asm",		AR_A,	0,
	"auto",		AR_CL,	AUTO,
	"break",	AR_RW,	BREAK,
	"char",		AR_TY,	CHAR,
	"case",		AR_RW,	CASE,
	"continue",	AR_RW,	CONTINUE,
	"double",	AR_TY,	DOUBLE,
	"default",	AR_RW,	DEFAULT,
	"do",		AR_RW,	DO,
	"extern",	AR_CL,	EXTERN,
	"else",		AR_RW,	ELSE,
	"enum",		AR_E,	ENUM,
# ifdef ONEFLOAT
	"float",	AR_TY,	DOUBLE,
# else
	"float",	AR_TY,	FLOAT,
# endif
	"for",		AR_RW,	FOR,
	"fortran",	AR_CL,	FORTRAN,
	"goto",		AR_RW,	GOTO,
	"if",		AR_RW,	IF,
	"int",		AR_TY,	INT,
	"long",		AR_TY,	LONG,
	"return",	AR_RW,	RETURN,
	"register",	AR_CL,	REGISTER,
	"switch",	AR_RW,	SWITCH,
	"struct",	AR_S,	0,
	"sizeof",	AR_RW,	SIZEOF,
	"short",	AR_TY,	SHORT,
	"static",	AR_CL,	STATIC,
	"typedef",	AR_CL,	TYPEDEF,
	"unsigned",	AR_TY,	UNSIGNED,
	"union",	AR_U,	0,
	"void",		AR_TY,	VOID,
	"while",	AR_RW,	WHILE,
	"",		0,	0,	/* to stop the search */
};

lxres()
{
	/* check to see of yytext is reserved; if so,
	** do the appropriate action and return
	*/
	/* otherwise, return -1 */

	register c, ch;
	register struct lxrdope *p;

	ch = yytext[0];

	if( !islower(ch) ) return( -1 );

	switch( ch )
	{

	case 'a':
		c=0;
		break;
	case 'b':
		c=2;
		break;
	case 'c':
		c=3;
		break;
	case 'd':
		c=6;
		break;
	case 'e':
		c=9;
		break;
	case 'f':
		c=12;
		break;
	case 'g':
		c=15;
		break;
	case 'i':
		c=16;
		break;
	case 'l':
		c=18;
		break;
	case 'r':
		c=19;
		break;
	case 's':
		c=21;
		break;
	case 't':
		c=26;
		break;
	case 'u':
		c=27;
		break;
	case 'v':
		c=29;
		break;
	case 'w':
		c=30;
		break;

	default:
		return( -1 );
	}

	for( p= lxrdope+c; p->lxrch[0] == ch; ++p )
	{
		if( !strcmp( yytext, p->lxrch ) )
		{
			 /* match */
			switch( p->lxract )
			{

			case AR_TY:
				/* type word */
				stwart = instruct;
				yylval.nodep = mkty( (TWORD)p->lxrval, 0, p->lxrval );
				if( p->lxrval==FLOAT || p->lxrval==DOUBLE )
				{
# ifdef NOFLOAT
					if( !flflag )
					{
						flflag = 1;
						werror( "sorry, no floating point" );
					}
					yylval.nodep->tn.type = LONG;
# else
					flflag = 1;
# endif
				}
				return( TYPE );

			case AR_RW:
				/* ordinary reserved word */
				return( yylval.intval = p->lxrval );

			case AR_CL:
				/* class word */
				yylval.intval = p->lxrval;
				return( CLASS );

			case AR_S:
				/* struct */
				stwart = INSTRUCT|SEENAME|TAGNAME;
				yylval.intval = INSTRUCT;
				return( STRUCT );

			case AR_U:
				/* union */
				stwart = INUNION|SEENAME|TAGNAME;
				yylval.intval = INUNION;
				return( STRUCT );

			case AR_E:
				/* enums */
				stwart = SEENAME|TAGNAME;
				return( yylval.intval = ENUM );

			case AR_A:
				/* asm */
				asm_esc = 1; /* warn the world! */
				lxget( ' ', LEXWS );
				if( getchar() != '(' ) goto badasm;
				lxget( ' ', LEXWS );
				if( getchar() != '"' ) goto badasm;
				asmp = asmbuf;
				while( (c = getchar()) != '"' )
				{
					if( c=='\n' || c==EOF ) goto badasm;
					*asmp++ = c;
					if( asmp >= &asmbuf[ASMBUF-1] )
					{
						uerror( "asm > %d chars",
						ASMBUF);
					}
				}
				lxget( ' ', LEXWS );
				if( getchar() != ')' ) goto badasm;
				*asmp++ = '\0';
				return( ASM );

badasm:
				uerror( "bad asm construction" );
				return( 0 );

			default:
				cerror( "bad AR_?? action" );
			}
		}
	}
	return( -1 );
}

lxtitle()
{
	/* called after a newline; set linenumber and file name */

	register c, val;
	register char *cp;

	for(;;)
	{
		 /* might be several such lines in a row */
#ifdef ASSYCOMMENT
		if( (c = eatline()) != '#' )
#else
		if( (c = getchar()) != '#' )
#endif ASSYCOMMENT
		{
			if( c != EOF ) ungetc(c,stdin);
			return;
		}

		lxget( ' ', LEXWS );
		val = 0;
		for( c = getchar(); isdigit(c); c = getchar() )
		{
			val = val*10+ c - '0';
		}
		ungetc( c, stdin );
		lineno = val;
		lxget( ' ', LEXWS );
		if( (c = getchar()) != '\n' )
		{
			for( cp = ftitle; c != '\n'; c = getchar(),++cp )
			{
				*cp = c;
			}
			*cp = '\0';
		}
	}
}

# ifndef MYASMOUT
asmout()
{
	printf( "%s\n", asmbuf );
}
# endif


#define NSAVETAB	4096
char	*savetab;
unsigned int	saveleft;

char *
savestr( cp )			/* place string into permanent string storage */
	register char *cp;
{
	register int len = strlen( cp ) + 1;
	extern char * malloc();
	extern char * strncpy();

	if ( len > saveleft )
	{
		saveleft = NSAVETAB;
		if ( len > saveleft )
			saveleft = len;
		if ( ( savetab = (char *) malloc( saveleft ) ) == 0 )
			cerror( "out of memory [savestr()]" );
	}
	strncpy( savetab, cp, len );
	cp = savetab;
	savetab += len;
	saveleft -= len;
	return ( cp );
}

#ifdef LINT
#	define LNCHNAM	8	/* length of symbols to check for pflag */
#endif

/*
* The segmented hash tables.
*/
#define MAXHASH		20
#define HASHINC		1013
struct ht
{
	char	**ht_low;
	char	**ht_high;
	int	ht_used;
} htab[MAXHASH];


char *
hash( s )	/* look for s in seg. hash tables.  Not found, make new entry */
	char *s;
{
	register char **h;
	register int i;
	register char *cp;
	struct ht *htp;
	int sh;
	extern char * calloc();
#ifdef LINT
	char *found = 0;	/* set once LNCHNAM chars. matched for name */
#endif

	/*
	* Hash on the correct number of characters.  Lint needs to be able
	* to limit this so that it can note length of names for portablility
	* concerns.
	*/
	cp = s;
	i = 0;
#ifdef LINT
	while ( *cp && ( ( cp - s ) < LNCHNAM ) )
#else
	while ( *cp )
#endif
	{
		i = ( i << 1 ) + *cp++;
	}
	sh = ( i & 077777 ) % HASHINC;
	cp = s;
	/*
	* Look through each table for name.  If not found in the current
	* table, skip to the next one.
	*/
	for ( htp = htab; htp < &htab[MAXHASH]; htp++ )
	{
		if ( htp->ht_low == 0 )
		{
			register char **hp = (char **) calloc(
				sizeof (char **), HASHINC );

			if ( hp == 0 )
				cerror( "out of memory [hash()]" );
			htp->ht_low = hp;
			htp->ht_high = hp + HASHINC;
		}
		h = htp->ht_low + sh;
		/*
		* Use quadratic re-hash
		*/
		i = 1;
		do
		{
			if ( *h == 0 )
			{
				if ( htp->ht_used > ( HASHINC * 3 ) / 4 )
					break;
				htp->ht_used++;
				*h = savestr( cp );
#ifdef LINT
				if ( pflag && found )
				{
					/*
					* If pflag set, then warn of greater
					* than LNCHNAM character names which
					* differ past the LNCHNAM'th character.
					*/
					werror( "`%s' may be indistinguishable from `%s' due to internal name truncation",
					*h, found );
				}
#endif
				return ( *h );
			}
#ifdef LINT
			if ( pflag )
			{
				if ( **h == *cp &&
					strncmp( *h, cp, LNCHNAM ) == 0 )
				{
					/*
					* We have matched on LNCHNAM chars.
					* Now, look for the ``total'' name.
					*/
					found = *h;
					if ( strcmp( *h, cp ) == 0 )
					{
						/*
						* This entry really is
						* the name we want.
						*/
						return ( *h );
					}
				}
			}
			else	/* No pflag - use entire name length */
			{
				if ( **h == *cp && strcmp( *h, cp ) == 0 )
					return ( *h );
			}
#else
			if ( **h == *cp && strcmp( *h, cp ) == 0 )
				return ( *h );
#endif
			h += i;
			i += 2;
			if ( h >= htp->ht_high )
				h -= HASHINC;
		} while ( i < HASHINC );
	}
	cerror( "out of hash tables" );
/*NOTREACHED*/
}
