Google
 

Trailing-Edge - PDP-10 Archives - SRI_NIC_PERM_FS_1_19910112 - kcc-6/kcc/ccdecl.c
There are 8 other files named ccdecl.c in the archive. Click here to see a list.
/*	CCDECL.C - Declaration Parser
**
**	(c) Copyright Ken Harrenstien 1989
**		All changes after v.371, 28-May-1988
**	(c) Copyright Ken Harrenstien, SRI International 1985, 1986
**		All changes after v.154, 8-Aug-1985
**
**	Original version (C) 1981  K. Chen
*/

#include "cc.h"

/* Imported functions */
extern SYMBOL *symfidstr(), *creatsym(), *uniqsym();	/* CCSYM */
extern SYMBOL *findgsym();	/* CCSYM */
extern SYMBOL *lsymhead;	/* CCSYM - var indicating loc sym blk */
extern SYMBOL *newlabel();	/* CCSYM */
extern SYMBOL *symfxext();	/* CCSYM */
extern SYMBOL *symftag();	/* CCSYM */
extern SYMBOL *symfmember();	/* CCSYM */
extern SYMBOL *symqcreat();	/* CCSYM */
extern int isdupsym();		/* CCSYM */
extern SYMBOL *beglsym();	/* CCSYM */
extern void funlsym();		/* CCSYM */
extern TYPE *findctype(), *findtype(),	/* CCSYM */
	*findsztype(), *findftype(),	/* CCSYM */
	*findptype();			/* CCSYM */
extern TYPE *tcomposite();		/* CCSYM */
extern int cmptype(), cmputype();	/* CCSYM */
extern int sizetype();			/* CCSYM */
extern void copysym(), ridlsym();	/* CCSYM */
extern int mapintsym(), mapextsym();	/* CCSYM */
extern void freesym(), idpfcpy();	/* CCSYM */
extern NODE *evalexpr();		/* CCEVAL */
extern NODE *funstmt(), *asgnexpr();	/* CCSTMT */
extern int pconst();			/* CCSTMT */
extern NODE *convasgn();		/* CCTYPE */
extern TYPE *convfparam();		/* CCTYPE */
extern TYPE *tcomposite();		/* CCTYPE */
extern int nextoken();			/* CCLEX */

/* Exported functions */
void initpar();		/* Called by CC mainline */
NODE *extdef();		/* Called by CC mainline */
NODE *tntdef();		/* Called by CC mainline */
NODE *ldecllist();	/* CCSTMT for inner block declarations */
TYPE *typename();	/* CCSTMT for cast and sizeof exprs */
SYMBOL *defauto();	/* CCSTMT for invisible struct return obj */
SYMBOL *funchk();	/* CCSTMT for calls to undeclared functions */

/* Internal functions */
static NODE *funcdef(), *datadef();
static void pdecllist(), sdeclenum(), decllist();
static int sdeclstruct(), fldsize(),
	isdecl(), pbase();
static TYPE *fundecl(), *paramlist(), *mkproto();
static void pidentlist();
static void plcmpare(), plcmpold(), plcheck();

static NODE *dodecl();
static SYMBOL *declarator();
static TYPE *addpp(), *pushsztype(), *tagspec();
static SYMBOL *sdeclaration();

static NODE *pizer();

static void errtwotyp(), errdupsym();

/* Internal data */

static int paramok;	/* True if OK to parse parameter list for a function
			** definition.  Only true when at top
			** level and parsing the first declarator.
			*/
static int inproto;	/* True when parsing a function prototype list.
			** This is bumped to indicate level.
			*/

static NODE *statdecls,	/* Pointer to list of static decls within current fn */
	*stattail;	/* Pointer to tail of statdecls list */
static int nsdefs;	/* # of enum/struct/union side effect definitions seen.
			** The exact number doesn't matter; this is only used
			** to tell when a type-specifier has had the side
			** effect of defining a tag or enum constant.
			*/
static int itags;	/* # of internal tags defined.  This is used only
			** to create unique names for internal tags. 
			*/
static int tntcnt;	/* # of tentative defs output.  Only used to initialize
			** tntdef(), by setting to -1.
			*/
/* Flags returned from pbase(). */
/* These aren't really used yet, maybe should be flushed. */
#define BASEF_SC 01	/* Storage class parsed */
#define BASEF_TS 02	/* Type-specifier parsed */
#define BASEF_TAG 04	/* enum/struct/union tag was defined as side effect */

/* Also not really used yet */
typedef struct {
	int Dflags;
	SYMBOL *Dpnext, *Dpsblk;
	SYMBOL s;
} declsym;
#define DSname s.Sname
#define DSvalue s.Svalue
#define DSclass s.Sclass
#define DStype s.Stype
/* INITPAR - Initialize parser
**	Called once for each file compiled.
*/
void
initpar()
{
    nodeinit();			/* No parse-tree nodes used */
    curfn = NULL;		/* Not in any function yet */
    itags = 0;			/* Reset internal tag count for gensyms */
    nsdefs = 0;			/* Reset side-effect def count for neatness */
    tntcnt = -1;		/* Reset tntdef() */
}

#if 0	/* No longer supported */
/* ENTDEFS - Process "entry" statements at start of file
**	This is a hack routine in several respects, and should vanish
** someday after all library routines have been fixed to remove their
** existing "entry" statements.  The hacks, among other things, are that:
** (1) the char flag table is munged to allow otherwise invalid ident chars.
** (2) this does both parsing and generation and does not return a NODE *.
** (3) output to the assembler file is performed here (should not happen
**	until the code generation phase).  This is necessary to ensure that
**	the assembler ENTRY statements precede all other code.
*/

#include "ccchar.h"		/* To access setcsym and clrcsym */
void
entdefs()
{
    SYMBOL *s;

    /* Verify it's ok to do this stuff at all */
    if (!clevkcc) return;	/* Return if not using KCC extensions. */

    /* Check out first token to see if it's an entry statement. */
    if (token != Q_IDENT || strcmp(csymbol->Sname, "entry"))
	return;			/* 1st token not "entry", can just return. */

    /* Process possible ENTRY statement */
    setcsym('$');		/* Allow $ and % as identifier chars */
    setcsym('%');		/* (note '_' becomes '.', so needs no hack) */
    s = csymbol;		/* Save symbol */
    if(nextoken() != Q_IDENT) {	/* must be followed by ident */
	tokpush (Q_IDENT, s);	/* Nope, put "entry" back on stack */
	clrcsym('$');		/* No longer allow $ or % as ident chars */
	clrcsym('%');
	return;			/* And return */
    }
    freesym(s);		/* OK, can flush "entry" identifier sym */

    /* Process an ENTRY statement with at least one valid identifier arg */
    while (1) {
	outstr("\tENTRY\t");		/* make ENTRY statement */
	outid(csymbol->Sname);		/* before we call expect() */
	outnl();			/* so #asm in middle won't screw us */
	expect(Q_IDENT);		/* it better have been a sym */
	if (token != T_COMMA) break;	/* repeat while we have more */
	nextoken();			/* skip COMMA */
    }
    clrcsym('$');		/* No longer allow $ or % as ident chars */
    clrcsym('%');
    expect(T_SCOLON);	/* Must end with ';', flush til get one */
			/* Note error recovery will not gobble improper
			** identifiers due to above clrcsym calls.
			*/
    /* Now invoke self again, until no more entry stmts.
    ** Tail recursion makes this efficient, though it hardly matters.
    */
    entdefs();
}
#endif /* commented out */
/* EXTDEF() - Parse top-level declaration or definition
**	[dpANS 3.7, 3.5]
**
**	<external-decl> ::= <function-definition> | <decl>
**
**	<function-definition> ::=
**			{decl-specs} <declor> {decl-list} <compound-stmt>
**
**	<decl> ::=	<decl-specs> {init-decl-list} ';'
**
**	<init-decl-list> ::= <init-declor> {',' init-decl-list}
**	<init-declor>	::= <declor> {'=' izer}
**
** This is the main entry to input parser.  Current token is the
** first one of the <external-decl>.  Since both possibilities (function
** def or a decl) can start with <decl-specs>, we parse for that first.
** If there was nothing but <decl-specs>, we can just return.  Otherwise
** the initially parsed "base" needs to be passed on for further parsing.
*/

NODE *
extdef()
{
    SYMBOL *s, tempsym, base;

    /* Do top level initializations */
    paramok = 1;		/* OK to parse a function parameter list */
    inproto = 0;		/* Not in prototype scope */
    curfnnew = fline;		/* Remember line in file where funct started */

    pbase(&base);		/* Parse base (storage class & type) */
    if (token == T_SCOLON) {	/* Only got a type (struct)? */
	if ((base.Sflags&SF_SIDEFF) == 0)	/* If no side effs, */
	    warn("Null declaration");		/* warn user */
	switch (base.Sclass) {
	case SC_UNDEF:	break;			/* None given */
	case SC_AUTO:
	case SC_RAUTO:	error("Illegal storage class");	break;
	default:	note("Useless storage class");	break;
	}
	nextoken();		/* Skip over the semicolon */
	return NULL;		/* and stop doing this def */
    }

    /* Parsed <decl-specs>, now look for <declor> to determine whether
    ** we're doing a function def or not.
    */
    copysym(&tempsym, &base);		/* Copy base storage class & type */
    if (tempsym.Sclass == SC_UNDEF)	/* Set up for defaults */
	tempsym.Sclass = SC_EXLINK;	/* Default stg class: extern */
    if (tempsym.Stype == NULL)
	tempsym.Stype = deftype;	/* Default type: int */
    while (!(s = declarator(&tempsym))) {	/* Until we find declarator */
	/* No declarator found, may want to loop. */
	warn("Null declarator");
	if (tempsym.Spmnext)		/* Flush param syms if any (in case) */
	    ridlsym((SYMBOL *)NULL);
	if (token == T_COMMA) {		/* Only continue if hit comma */
	    nextoken();			/* in which case skip over it */
	    continue;			/* and try again */
	}
	if (token != T_SCOLON)		/* Else give up on it */
	    error("Bad syntax for top-level declaration");
	errflush();			/* Flush to probable end of stmt */
	return NULL;
    }

    paramok = 0;		/* No longer OK to parse function param list */

    /* If function type, need to examine more closely to see whether this
    ** is a definition or just a forward-reference declaration.
    ** It is a definition only if the current token is one of:
    **		Left brace (start of function body)
    **		A type-specifier keyword (a function parameter declaration).
    **		The "register" keyword (only permissible storage class for
    **			function parameter declarations).
    ** We permit any storage class here, for better error diagnostics later.
    */
    if (tempsym.Stype->Tspec == TS_FUNCT) {
	if (token == T_LBRACE || isdecl())
	    return funcdef(&base, &tempsym, s);  /* Parse funct def */
    }

    /* Not a function definition, so is either a function reference, or a
    ** data definition/reference.
    */
    return datadef(&base, &tempsym, s);	/* data def/ref or function ref */
}

/* TNTDEF - Return next remaining tentative definition, if any.
**	This is only invoked by the CC mainline after all input parsing
**	is completed for a file.  It scans for all tentative definitions
**	with internal or external linkage, and for each one returns a
**	node that defines it with a zero initializer.
*/
NODE *
tntdef()
{
    static SYMBOL *s;

    if (++tntcnt == 0)		/* If first time, */
	s = symbol;		/* start scanning global sym list */
    while (s = s->Snext) {
	switch (s->Sclass) {
	default: continue;
	case SC_INTREF:		/* Should only exist for functions */
	    if (s->Srefs)
		error("Static function %S not defined", s);
	    else note("Static function %S not defined or used", s);
	    continue;
	case SC_INTDEF:		/* May be function or object */
	    if (s->Srefs == 0)
		note("Static %s %S never used",
			(s->Stype->Tspec != TS_FUNCT ? "object" : "function"),
			s);
	    continue;
	case SC_INLINK:
	    s->Sclass = SC_INTDEF;
	    if (s->Srefs)	/* Ensure internal object was used */
		break;		/* Yup, go define it */
	    note("Static object %S never used, not emitted", s);
	    continue;
	case SC_EXLINK:
	    s->Sclass = SC_EXTDEF;	/* Always do it if external linkage */
	    break;
	}
	/* Set up a Q_IDENT and a null izer definition for it */
	return ndefl(N_DATA, ndefl(N_IZ, ndefident(s)));
    }
    return NULL;
}
/* FUNCDEF() - Parse function definition
**	[dpANS 3.7.1]
**	<function-definition> ::=
**			{decl-specs} <declor> {decl-list} <compound-stmt>
**
** Only called from extdef() when a function declaration turns out
** to be the start of a definition.  At this point, the current token
** is the first one past the <declor>.
**
** The first two pointer args always point to temporary symbol
** structures not in the symbol table.  However, the parameter list
** symbols ARE in the table, chained as local symbols.
*/
static int nsetjmps();

static NODE *
funcdef(b, d, syment)
SYMBOL	*b,		/* Base, contains parsed <decl-specs> */
	*d,		/* Defaulted <decl-specs> plus <declor> type */
	*syment;	/* Identifier's symtab entry */
{
    int n, siz, nsjmps;
    NODE *nnode, *header;
    SYMBOL *s1;
    SYMBOL *args = d->Spmnext;	/* List of parameter syms */
    int npartypes = d->Svalue;	/* # of params if new-style proto */
    int nparidents = 0;

    /* "syment" points to the function name's symtab entry.
    ** Lexer will have created the symtab entry with
    ** class SC_UNDEF if it didn't already exist.
    */
    if (!args)			/* If no arglist, need to open local sym blk */
	(void) beglsym();	/* (needn't remember start) */

    curfnloc = curfnnew;
    curfn = syment;		/* Remember context for error messages */
    curfn = syment = funchk(1, b->Sclass, d, syment);	/* Do funct decl */
				/* which may update symbol and may also */
				/* do old-style <decl-list> parse! */

    /* Now that types are set, add up sizes and determine offsets for
    ** each parameter.  Also, for new-style function prototype defs, make
    ** sure that every parameter had an identifier associated with it.
    ** We do this by comparing the # of identifiers on the paramlist with
    ** the # of parameters that paramlist() actually parsed while building
    ** the prototype.
    */
    n = 0;				/* set up for first arg */
    siz = sizetype(syment->Stype->Tsubt);	/* get size of return val */
    if (siz > 2)			/* If returning too-large object, */
	n = 1;				/* just use struct-return pointer */
    while (args != NULL) {
	nparidents++;			/* Count # of param idents */
	s1 = args;			/* get arg symbol */
	args = args->Spmnext;		/* Move on before zapping it! */
	n += sizetype(s1->Stype);	/* count off by size */
	s1->Svalue = n;			/* set offset */
    }
    if (npartypes && (npartypes-1 != nparidents))
	error("Missing identifier for parameter in function def");

    maxauto = 0;			/* no local variables yet */
    stackrefs = 0;			/* and therefore no refs to them */
    nsjmps = nsetjmps();		/* Remember # of setjmp refs */
    statdecls = stattail = NULL;	/* No static declarations yet */

    /* The big call.  Parse function statement, having already set up a
    ** local symbol block with the parameters in it.  On return, this
    ** block will have been ended, and the current token will be the
    ** T_RBRACE ('}') terminating the function body.  See CCSTMT's
    ** compound() for further discussion.
    */
    nnode = funstmt();			/* Parse function statement */

    nextoken();		/* Now safe to flush the right brace
			** and set up new current token.
			** See CCSTMT's compound() for discussion of this.
			*/
    stkgoto = (nsjmps != nsetjmps());	/* Say whether any setjmps in funct */
    header = ndefop(N_NODE);		/* Put together the function header */
    header->Nright = statdecls;		/* Point to any static decls found */
    header->Nleft = ndefident(syment);	/* Point to Q_IDENT function name */

    /* Return completed parse tree */
    return ndeflr(N_FUNCTION, header, nnode);
}

/* NSETJMPS - Auxiliary to find current # of references to the "setjmp"
**	function.  Any functions which contain calls to setjmp have to
**	avoid using tail recursion.
*/
static int
nsetjmps()
{
    SYMBOL *s;
    if (s = symfidstr("setjmp")) {
	--(s->Srefs);			/* Not a real reference */
	switch (s->Sclass) {
	case SC_EXTREF: case SC_XEXTREF: case SC_EXTDEF:
	    if (s->Stype->Tspec == TS_FUNCT)
		return s->Srefs;
	}
    }
    return 0;
}
/* FUNCHK - Check out a function definition or reference for
**	proper use of storage class and type specifier.
** Called from funcdef() for a definition and dodecl() for a reference.
** Also called from CCSTMT's primary() to handle the pretend-declaration
** done for a call to an undeclared function.
**
** The parsed declarator symbol struct will have the following special
** members set (by fundecl()) if called from funcdef():
**	Svalue - 0 if an old-style function identlist, N+1 if new-style proto,
**		where N is the # of real parameters in the prototype.
**	Spmnext - if non-NULL, points to a list of parameter identifiers
**		and there is still a local symbol block active for the
**		prototype scope.  This block needs to be closed and the
**		symbols flushed if this was only a reference.
**	Spmhead - what beglsym() returned for the prototype scope block.
** These syms should be NULL if called from dodecl(), which flushes the
** prototype scope block itself.
**
** May parse the function's <decl-list> if doing an old-style definition.
** Returns with the symbol table entry completely set up, and returns
** pointer to the symbol since it may have changed.
*/
SYMBOL *
funchk(def, baseclass, d, s)
int def;	/* True if a definition */
int baseclass;	/* Parsed base storage class */
SYMBOL	*d,	/* Parsed declarator (Sclass and Stype) */
	*s;	/* Symtab entry for parsed identifier */
{
    
    /* Check out storage class.
    ** Parsing should have resulted in only one of the following three
    ** things: SC_UNDEF (none), SC_EXTREF (extern), SC_INTREF (static).
    ** For existing function def/refs, there are 5
    ** possible symbol classes:
    ** SC_EXTREF, SC_EXTDEF, SC_INTREF, SC_INTDEF, plus special case of
    ** normally invisible SC_XEXTREF.
    */
    s = symfxext(s);		/* Make SC_XEXTREF visible if any */

    switch (baseclass) {	/* Not all storage classes are allowed */
    default:
	error("Illegal storage class for function, assuming \"extern\"");
	return funchk(def, SC_EXTREF, d, s);	/* Just call again */

    case SC_INTREF:		/* Explicit "static" */
	/* dpANS: identifier always has internal linkage.
	** Note this must not appear within block scope!
	*/
	if (!lsymhead || def) {
	    /* At file-scope level, so "s" will be global */
	    d->Sclass = (def ? SC_INTDEF : SC_INTREF);
	    if (s->Sclass == SC_UNDEF		/* No old sym */
	      || s->Sclass == SC_INTREF		/* Or not defined */
	      || s->Sclass == SC_INTDEF)	/* Or defd, reffing */
		break;				/* Linkage matches OK */
	    if (s->Sclass != SC_EXTREF && s->Sclass != SC_EXTDEF
	      && s->Sclass != SC_XEXTREF) {
		error("Storage class conflict for \"%s\"", s->Sname);

		break;			/* Keep internal linkage */
	    }
	    warn("Linkage conflict for %S, is external", s);
	    /* Else fall thru as if SC_EXTREF */
	} else if (!def)
	    warn("Storage class for function decl in block must be \"extern\"");
	/* Drop thru to assume extern and carry on */

    case SC_UNDEF:		/* dpANS: default same as explicit "extern" */
    case SC_EXTREF:		/* Explicit "extern" */
	/* dpANS: linkage is same as any visible decl of this identifier
	** with file scope (ie global).  If none, linkage is external.
	** If there is already an expired external reference (SC_XEXTREF)
	** then we use that in order to emit advisory warnings if the type
	** isn't the same.
	*/
	/* If doing a reference in block scope and function ident already
	** belongs to a block-scope symbol, we:
	** (1) see if a file-scope symbol exists (if so, use that one)
	** (2) see if existing sym is external ref (if so, use that)
	**	(this includes SC_XEXTREF)
	** (3) generate new file-scope symbol with SC_EXTREF.
	*/
	if (!def && (s->Sflags&SF_LOCAL)) {
	    SYMBOL *ns;
	    if (ns = findgsym(s))	/* If file-scope def/ref exists, */
		s = ns;			/* use that! */
	    else if (s->Sclass == SC_EXTREF)
			;		/* OK if a block-scope extern ref */
	    else {
		if (isdupsym(s))	/* If was defined in this block, */
		    errdupsym(s);	/* barf! */
		s = uniqsym(s);		/* Create/re-use sym, now SC_UNDEF */
	    }
	}
	/* "s" now points to the sym linked to.  If it has any linkage, we
	** copy that, otherwise we use external linkage.
	*/
	switch (s->Sclass) {
	case SC_INTDEF:
	case SC_INTREF:
	    d->Sclass = (def ? SC_INTDEF : SC_INTREF);
	    break;

	default:
	    errdupsym(s);
	    s->Sclass = SC_UNDEF;		/* Sigh!  Smash it. */
	    /* Fall thru to handle as if extern */

	case SC_XEXTREF:
	case SC_EXTDEF:
	case SC_EXTREF:
	case SC_UNDEF:
	    d->Sclass = (def ? SC_EXTDEF : SC_EXTREF);
	    break;
	}
	break;
    }

    /* Check for duplicate function def */
    if (def && (s->Sclass == SC_EXTDEF || s->Sclass == SC_INTDEF)) {
	error("Duplicate function definition");
	s->Sclass = SC_UNDEF;	/* Wipe out previous definition! */
    }

    /* Handle simple case where sym hasn't yet been defined. */
    if (s->Sclass == SC_UNDEF) {	/* Symbol not defined yet? */
	if (!def) s = uniqsym(s);	/* No, ensure local if needed */
	s->Sclass = d->Sclass;		/* Copy the parsed class */
	s->Stype = d->Stype;		/* and the type specification */
	s->Srefs = 0;			/* and reset usage cnt in case a ref */
	if (s->Sclass == SC_INTDEF || s->Sclass == SC_INTREF)
	    mapintsym(s);		/* Set Smaplab (internal unique) */
	else if (!mapextsym(s))		/* Else check external map */
	    error("External link name duplicated: %S", s);
	if (def && !s->Stype->Tproto) {	/* If an old-style definition, */
	    pdecllist();		/* parse parameter declarations! */
	    s->Shproto = mkproto(d->Spmnext);	/* Build invisible prototype */
	} else s->Shproto = NULL;		/* Say no hidden prototype */ 
	return s;			/* All done! */
    }

    /* There is an existing symbol for this identifier, with a matching
    ** storage class.  Must now do lots of hairy type checking.
    ** First we see if old sym is a function returning a
    ** compatible type, then maybe check parameter list compatibility.
    **
    ** If both are old-style, no param checking needed.
    ** If both are new-style, check their param lists.
    ** If one sym is old-style ref and other is new-style ref/def,
    **   just do simple check of the proto list.
    ** If one sym is old-style DEF, then:
    **		If this is more recent sym, parse decl-list into prototype
    **		and then compare param lists as if both were new-style.
    **		Else is previous sym -- compare with "hidden" prototype
    **		that was generated during the old-style function def.
    */

    /* First make sure that basic type is also "function returning ..."
    ** and that return types are compatible.
    */
    if (s->Stype->Tspec != TS_FUNCT
      || !cmptype(d->Stype->Tsubt, s->Stype->Tsubt)) {
	/* Earlier ref/def conflicts with current one, smash to current. */
	errtwotyp(d, s);		/* Complain */
	s->Stype = d->Stype;		/* Skip prototype testing */
    }

    /* If doing an old-style definition, OK to parse decl-list now.
    ** The main reason for not doing this earlier is so error messages
    ** from the previous checks will show the most helpful context,
    ** i.e. errors are announced as soon as it is possible to detect them.
    */
    if (def && !d->Stype->Tproto) {		/* If no proto was declared, */
	pdecllist();				/* parse old-style decl-list */
	s->Shproto = mkproto(d->Spmnext);	/* Build invisible prototype */
    }

    /* Now see if the parameter lists need to be checked.
    ** Test for both old-style, or both with identical prototypes.
    */
    if (d->Stype->Tproto != s->Stype->Tproto) {
	TYPE *tc;
	if (d->Stype->Tproto && s->Stype->Tproto) {	/* Compare 2 protos? */
	    tc = tcomposite(d->Stype, s->Stype);	/* Get composite */
	    if (tc) d->Stype = tc;			/* If won, use it! */
	    else plcmpare(d->Stype->Tproto, s->Stype->Tproto, 0); /* Give err */
	} else if (d->Stype->Tproto) {
	    /* Compare existing non-proto ref/def with new prototype */
	    if (s->Sclass == SC_EXTDEF || s->Sclass == SC_INTDEF) {
		/* Old-style DEF followed by new-style REF, use hidden proto */
		if (!tcomposite(d->Stype, s->Stype))	/* If not compatible */
		    plcmpare(d->Stype->Tproto, s->Shproto, 1);	/* Give err */
		/* Don't use composite type, just the new prototype, so
		** leave d->Stype alone.
		*/
	    } else /* Old-style REF followed by new-style proto */
		plcheck(d->Stype->Tproto);		/* Check new proto */
	} else {
	    /* Compare existing proto with new non-proto ref/def */
	    if (def) {
		/* New-style REF followed by old-style DEF, compare params */
		plcmpold(d->Spmnext, s->Shproto, s->Stype->Tproto);
	    } else /* New-style ref/def followed by old-style REF */
		plcheck(s->Stype->Tproto);	/* Check previous proto */
	    d->Stype = s->Stype;		/* Ensure proto retained */
	}
    }

    /* Force the symbol table entry to match current declaration.
    ** Note that Srefs was incremented by the symbol lookup, hence needs
    ** to be set back if this was only a ref-type declaration.
    ** Also, if the existing symbol was a definition, don't change its
    ** class.
    */
    s->Stype = d->Stype;	/* Force the type specification */
    if (!def)			/* If this decl was just a ref, */
	--(s->Srefs);		/* then usage count to undo lookup bump */
    if (s->Sclass != SC_EXTDEF && s->Sclass != SC_INTDEF)
	s->Sclass = d->Sclass;	/* Force the storage class */
    return s;
}
/* PLCMPARE - Compare two prototype parameter lists.
*/
static void
plcmpare(t1, t2, flag)
TYPE *t1, *t2;
int flag;			/* 0 if both protos, 1 if t2 is fake proto */
{
    int n;
    if (t1 == t2) return;

    /* If pointers don't match, prototypes aren't compatible.  Examine
    ** more closely so can give helpful error message (or barf at ourselves
    ** if necessary)
    */
    if (t1 && t1->Tspec != TS_PARVOID && t2 && t2->Tspec != TS_PARVOID)
	for (n = 1; t1 && t2; t1 = t1->Tproto, t2 = t2->Tproto, ++n)
	    if (t1->Tspec != t2->Tspec
	      || (t1->Tspec == TS_PARAM && !cmputype(t1->Tsubt, t2->Tsubt))) {
		if (flag) warn("Type of param #%d conflicts with prior def", n);
		else error("Type of parameter #%d conflicts with prior prototype", n);
		return;
	    }
    if (t1 != t2) {
	if (flag) warn("Number of params conflicts with prior def");
	else error("Number of params conflicts with prior prototype");
    } else int_warn("plcmpare: proto mismatch not found");
}

/* PLCMPOLD - Compare old-style parameter sym types against a
**	prototype parameter list.
*/
static void
plcmpold(s, nt, ot)
SYMBOL *s;
TYPE *nt, *ot;
{
    if (nt == ot) return;

    /* Prototypes don't match, try to give clever error msg */
    for (; s; s = s->Spmnext, nt = nt->Tproto, ot = ot->Tproto)
	if (!nt || !ot || nt->Tspec != ot->Tspec || nt->Tsubt != ot->Tsubt) {
	    warn("Type of parameter \"%s\" conflicts with prior prototype", s->Sname);
	    return;
	}
    if (nt || ot)
	warn("Number of params conflicts with prior prototype");
    else int_warn("plcmpold: proto mismatch not found");
}

/* PLCHECK - Check a prototype parameter list to ensure that all types
**	are compatible with default argument promotions.  Note that use
**	of an ellipsis param (TS_PARINF) is illegal in this context.
*/
static void
plcheck(t)
TYPE *t;
{
    if (t->Tspec == TS_PARVOID) {
	if (t->Tproto)
	    int_error("plcheck: void param");
    } else for (; t; t = t->Tproto)
	if (t->Tspec != TS_PARAM || !cmputype(t->Tsubt,convfparam(t->Tsubt))) {
	    warn("Prototype param type not compatible with default type");
	    break;
	}
}
/* PDECLLIST - Parse the <decl-list> for an old-style function definition.
**	[dpANS 3.7.1, 3.6.2]
**
**	<decl-list> ::= *[<declaration>]
**
** The only storage-class spec allowed is "register".  No initializations
** are permitted.
*/
static void
pdecllist()
{
    TYPE *t;
    SYMBOL *s1, argbase, stemp;

    while (token != T_LBRACE && token != T_EOF) {	/* type-decl-list */
	pbase(&argbase);	/* Parse storage class and type specifier */
	copysym(&stemp, &argbase);
	switch (stemp.Sclass) {		/* Check storage class */
	    default:
		error("Illegal storage class for function parameter");
	    case SC_UNDEF:		/* Default becomes this. */
		stemp.Sclass = SC_ARG;
		break;
	    case SC_RAUTO:		/* If "register" seen, */
		stemp.Sclass = SC_RARG;	/* use right symbol class */
		break;
	}
	if ((t = stemp.Stype) == NULL) {	/* Check type-specifier */
	    if (argbase.Sclass == SC_UNDEF)
		error("No type-specifier for parameter decl, assuming int");
	    t = deftype;
	}

	/* Have <decl-specs> base, now parse each <declor> */
	while (1) {
	    stemp.Sname[0] = '\0';	/* no symbol given yet */
	    stemp.Stype = t;		/* Reset base type */
	    for(;;) {
		if (s1 = declarator(&stemp))	/* Get sym and rest of type */
		    break;
		if (token == T_COMMA) {
		    warn("Null parameter declarator");
		    nextoken();
		    continue;
		}
		if (token == T_SCOLON) {
		    nextoken();
		    break;
		}
		error("Bad parameter declaration token");
		errflush();	/* Flush to probable end of statement */
		break;		/* Will get two err msgs, but so what */
	    }

	    if (s1 == NULL)
		warn("Null parameter declaration");
	    else if (s1->Sclass != SC_ARG && s1->Sclass != SC_RARG) {
		error("Identifier \"%s\" not in function parameter list",
			s1->Sname);		/* not an arg to this fn */
		if (s1->Sclass == SC_UNDEF)
		    freesym(s1);		/* Clean up if boo-boo */
	    } else if (s1->Sflags & SF_PARAMDECL) {
		error("Duplicate parameter declaration: \"%s\"",s1->Sname);
	    } else {		/* Is arg, set type to what we parsed */
		s1->Sclass = stemp.Sclass;	/* Maybe indicate register */
		s1->Sflags |= SF_PARAMDECL;	/* Say decl seen for arg */
		s1->Stype = convfparam(stemp.Stype);	/* Get right type */
	    }
	    if (token != T_COMMA) break;	/* repeat through list */
	    nextoken();			/* skipping over commas */
	}
	expect(T_SCOLON);		/* decl line ends with a semicolon */
    }
}


/* MKPROTO - Make a prototype param type list out of a parameter symbol list.
**	Must be recursive for same reason that paramlist() is.
**	Note special handling for case of no symbols on list.
*/
static TYPE *mkprox();
static TYPE *
mkproto(s)
SYMBOL *s;
{
    return s ? mkprox(s) : findptype(TS_PARVOID, (TYPE *)NULL, (TYPE *)NULL);
}
static TYPE *
mkprox(s)
SYMBOL *s;
{
    return s ? findptype(TS_PARAM, mkprox(s->Spmnext), s->Stype) : NULL;
}
/* DATADEF - Parse top-level declaration that isn't a function definition.
**	[dpANS 3.5, 3.7.2]
**
**	<decl>		::= <decl-specs> {init-decl-list} ';'
**
**	<init-decl-list> ::= {init-decl-list ','} <init-declor>
**	<init-declor>	::= <declor> {'=' izer}
**
** Invoked by extdef() after determining that a declaration is not
** a function definition, to parse the rest of a top-level data declaration
** or function reference.
**	Current token is the first one after the first <declor>.
*/
static NODE *
datadef(base, s, syment)
SYMBOL	*base,		/* Parsed <decl-specs> */
	*s,		/* Default <decl-specs> and 1st <declor> */
	*syment;	/* Symtab entry of identifier for 1st <declor> */
{
    SYMBOL defbase;
    NODE *root = NULL, *tail = NULL;

    /* Check out the storage class and type
    ** specifications.  At top level, some storage class or type specifier
    ** must have been given.
    */
    defbase.Scontents = base->Scontents;	/* Copy contents of base */
    switch (defbase.Sclass) {
	case SC_UNDEF:
	    defbase.Sclass = s->Sclass;		/* Copy whatever default was */
	    if (defbase.Stype == NULL) {
		error("Declaration without storage class or type-spec");
	    }
	    break;		
	case SC_EXTREF:		/* "extern" */
	case SC_INTREF:		/* "static" */
	case SC_TYPEDEF:	/* "typedef" */
	    break;
	default:
	    error("Illegal top-level storage class");
	    s->Sclass = defbase.Sclass = SC_EXLINK;	/* Use default */
	    break;
    }
    if (defbase.Stype == NULL)
	defbase.Stype = s->Stype;	/* Copy whatever default was */

    decllist(base, &defbase, s, syment, &root, &tail);

    if (!expect(T_SCOLON) && token == T_RBRACE) nextoken();
    return (root);
}
/* LDECLLIST() - Parse local block declaration list
**	[dpANS 3.6.2, 3.5]
**
**	<decl-list> ::=	<decl> {decl-list}
**
** This is only called by CCSTMT's compound() to parse the
** optional declaration-list at the start of a compound statement.
** Makes entries in the symbol and type tables, and returns a node
** pointer to a list of initializations that must be done.
**	Current token is the first of a possible <decl>.  If it is not
** a valid first token for a declaration, nothing is done and NULL is returned.
*/

NODE *
ldecllist()
{
    SYMBOL base, defbase;
    NODE *autodecls, *autotail;	/* Pointers to list of inits for decls
				** within a block */

    if (!isdecl()) return NULL;		/* Most common case -- no decls */

    autodecls = autotail = NULL;
    do {
	/* If current token is start of a declaration, handle it. */
	pbase(&base);		/* Parse base storage-class and type */
				/* Note all classes are OK */
	copysym(&defbase, &base);
	if (defbase.Sclass == SC_UNDEF)
	    defbase.Sclass = SC_AUTO;	/* Default class is AUTO */
	if (defbase.Stype == NULL)
	    defbase.Stype = deftype;

	/* Handle the local declaration, adding defs to the right list. */
	if (defbase.Sclass == SC_INTREF)	/* "static" */
	     decllist(&base, &defbase, (SYMBOL *)NULL, (SYMBOL *)NULL,
			&statdecls, &stattail);
	else decllist(&base, &defbase, (SYMBOL *)NULL, (SYMBOL *)NULL,
			&autodecls, &autotail);

	expect(T_SCOLON);
    } while (isdecl());

    return(autodecls);
}
/* DECLLIST - Parse declarator list for a declaration, with possible izers.
**	[dpANS 3.5]
**
**	<init-decl-list> ::= <init-declor> {',' init-decl-list}
**	<init-declor>	::= <declor> {'=' izer}
**
** This is called by datadef() for top-level declarations and
** by ldecllist() for block-level declarations.
** Note the provision for having already parsed the first declarator.
*/
static void
decllist(base, defbase, d, s, root, tail)
SYMBOL *base,		/* Base storage class & type-spec furnished */
	*defbase,	/* Same but completely defaulted as necessary */
	*d,		/* First parsed declarator (NULL if none) */
	*s;		/* Symbol table entry for d's ident */
NODE **root,		/* Addr of Root of declaration parse tree, if any */
	**tail;		/* Addr of Tail of parse tree, if any */
{
    SYMBOL tempsym;
    NODE *n, *z;
  
    if (d == NULL) {		/* Already parsed first declarator? */
	d = &tempsym;		/* No, so do first one here. */
	copysym(d, defbase);
	s = declarator(d);
	if (s == NULL && token == T_SCOLON && (base->Sflags&SF_SIDEFF))
	    return;		/* No declarators but have side-effect */
    }
    while (1) {
	if (s == NULL)
	    warn("Null declarator");
	else if ((n = dodecl(base->Sclass, d, s)) != NULL) {
	    z = ndefl(N_DATA, n);
	    if (*tail == NULL)			/* Add parse result to tree */
		*root = *tail = z;		/* Either as 1st node */
	    else *tail = (*tail)->Nright = z;	/* or at end of current tree */
	}
	if (token != T_COMMA) break;
	nextoken();
	copysym(d, defbase);
	s = declarator(d);
    }
    return;
}
/* ISDECL() - returns TRUE if current token is the start of a declaration,
**	i.e. the start of {declaration-specifiers}, one of:
**		{storage-class-specifier}
**		{type-specifier}
**		{type-qualifier}
*/
static int
isdecl()
{
    return (csymbol != NULL && (
	(tok[token].tktype == TKTY_RWSC || tok[token].tktype == TKTY_RWTYPE)
	|| (csymbol->Sclass == SC_TYPEDEF)
	));
}

/* PBASE(&sym) - Parse base of declaration (stg class & type)
**	Handles either <decl-specs>, if a symbol pointer is given,
**   or <spec-qual-list>, if no pointer is given.
**
**	[dpANS 3.5, 3.5.1, 3.5.2]
**
**	<decl-specs> ::=  *[<stg-class-spec> | <type-spec> | <type-qual>]
**
**	<spec-qual-list> ::= *[<type-spec> | <type-qual>]
**
**	<type-spec> ::= "void" | "char" | etc...
**	<type-qual> ::= "const" | "volatile"
**	<stg-class-spec> ::=
**			"typedef" | "extern" | "static" | "auto" | "register"
*/

/* Flags to remember what we've already seen, for tokens that can combine
** with others in random order.
*/
#define PF_SIGNED	01
#define PF_UNSIGNED	02
#define PF_CHAR		04
#define PF_SHORT	010
#define PF_INT		020
#define PF_LONG		040
#define PF_DOUBLE	0100

static int
pbase(symp)
SYMBOL *symp;
{
    static char *errmsg = "Illegal combination of type-specifiers";
    int savnsdefs = nsdefs;		/* Remember # side-eff defs so far */
    TYPE *t = NULL, *nt;
    int nflag, qflags = 0, tflags = 0;
    int chrsiz = 0;

    if (symp) {
	symp->Sname[0] = '\0';		/* init symbol */
	symp->Svalue = 0;		/* no val yet */
	symp->Sclass = SC_UNDEF;	/* no storage class */
	symp->Stype = NULL;		/* no type yet */
	symp->Sflags = 0;
    }

    /* First find all type-specs, type-quals, or stg-class-specs.
    ** Stop only when a token is seen that can't be one of these.
    */
    for (;;) {

	if (tok[token].tktype == TKTY_RWTYPE) {
	    /* Look for reserved word type-qualifiers or type-specifiers */
	    nt = NULL;
	    switch (token) {
#if SYS_CSI
	    case T_FORTRAN:
		if( symp->Sflags & (TF_FORTRAN | TF_BLISS) )
		    error("Duplicate foreign language qualifier!");
		symp->Sflags |= TF_FORTRAN;
		nextoken();
		continue;

	    case T_BLISS:
		if( symp->Sflags & (TF_FORTRAN | TF_BLISS) )
		    error("Duplicate foreign language qualifier!");
		symp->Sflags |= TF_BLISS;
		nextoken();
		continue;
#endif	/*  CSI  */
	    case T_CONST:		/* "const" type-qualifier */
		if (qflags & TF_CONST)
		    error("Duplicate \"const\"");
		qflags |= TF_CONST;
		nextoken();
		continue;

	    case T_VOLATILE:		/* "volatile" type-qualifier */
		if (qflags & TF_VOLATILE)
		    error("Duplicate \"volatile\"");
		qflags |= TF_VOLATILE;
		nextoken();
		continue;

	    case T_VOID:	nt = voidtype;	nflag = 0; break;
	    case T_FLOAT:	nt = flttype;	nflag = 0; break;
	    case T_STRUCT:	nt = tagspec(TS_STRUCT); nflag = 1; break;
	    case T_UNION:	nt = tagspec(TS_UNION);	 nflag = 1; break;
	    case T_ENUM:	(void) tagspec(TS_ENUM);
				nt = inttype;		 nflag = 1; break;
		/* Enums always type "int", but have tags and syms defined. */

	    case T_SIGNED:	nflag = PF_SIGNED;	break;
	    case T_UNSIGNED:	nflag = PF_UNSIGNED;	break;
	    case T_CHAR:	nflag = PF_CHAR;	break;
	    case T_SHORT:	nflag = PF_SHORT;	break;
	    case T_INT:		nflag = PF_INT;		break;
	    case T_LONG:	nflag = PF_LONG;	break;
	    case T_DOUBLE:	nflag = PF_DOUBLE;	break;
	    case T_CHAR6:	nflag = PF_CHAR; chrsiz = 6;	break;
	    case T_CHAR7:	nflag = PF_CHAR; chrsiz = 7;	break;
	    case T_CHAR8:	nflag = PF_CHAR; chrsiz = 8;	break;
	    case T_CHAR9:	nflag = PF_CHAR; chrsiz = 9;	break;
	    case T_CHAR18:	nflag = PF_CHAR; chrsiz = 18;	break;
	    default:
		int_error("ptypespec: unknown RWTYPE %Q", token);
		nextoken();		/* Skip over, get next */
		continue;
	    }
	    if (nt) {			/* Was type completely set? */
		if (t || tflags) error(errmsg);	/* Say bad typespec combo */
		t = nt;
		tflags = 0;
		/* Set flag if any side effects (tag or enum defined) */
		if (symp && savnsdefs != nsdefs)
		    symp->Sflags |= SF_SIDEFF;
		if (nflag)		/* If tagspec called, */
		    continue;		/* don't do another nextoken! */
	    } else {			/* Nope, nflag must be set */
		if (t || (tflags&nflag))
		    error(errmsg);	/* Say bad typespec combo */
		tflags |= nflag;
	    }
	    nextoken();			/* On to next token */
	    continue;

	} else if (tok[token].tktype == TKTY_RWSC) {
	    /* Check storage class */
	    if (!symp)
		error("Storage class specifier not allowed");
	    else if (symp->Sclass != SC_UNDEF)	/* Already have one? */
		error("Only one storage class specifier allowed");
	    else {
		if (t || tflags || qflags)
		    note("Storage class should come first in declaration");
		switch (token) {
		case T_AUTO:	symp->Sclass = SC_AUTO;   break;
		case T_STATIC:	symp->Sclass = SC_INTREF; break;
		case T_EXTERN:	symp->Sclass = SC_EXTREF; break;
		case T_REGISTER:	symp->Sclass = SC_RAUTO;  break;
		case T_TYPEDEF:	symp->Sclass = SC_TYPEDEF; break;
		default:
			int_error("ptypespec: unknown RWSC %Q", token);
		}
	    }
	    nextoken();
	    continue;		/* Skip over and get next */
	}

	/* Not a type-specifier keyword or storage-class, see if typedef,
	** but ONLY if we haven't already seen any other type specifiers.
	** Note that type qualifiers can be added in!  This requires a
	** special check for function and array types, which can't have
	** their types qualified except by typedefs.  But the qualifiers
	** may follow the typedef, so we have to check after all's done.
	*/
	if (!t && !tflags && csymbol && csymbol->Sclass == SC_TYPEDEF) {
	    t = csymbol->Stype;	/* Get the type */
	    nextoken();
	    continue;		/* Keep going, sigh */
	}
	break;			/* No matches, stop loop! */

    }	/* End of moby loop */

    /* All specifiers gobbled, now see whether a type resulted.  If none,
    ** but tflags has stuff, we try to decipher the flag combos.
    */
    if (!t) {
	switch (tflags) {
	case 0:	if (!qflags && (!symp || symp->Sclass == SC_UNDEF))
		    return 0;		/* Nothing read at all */
		t = deftype;		/* OK to default to "int" */
		break;

	/* Chars have special KCC extension case */
	case PF_CHAR:		  if (!chrsiz) { t = chartype;	break; }
	case PF_SIGNED|PF_CHAR:	  if (!chrsiz) { t = schartype;	break; }
	case PF_UNSIGNED|PF_CHAR: if (!chrsiz) { t = uchartype;	break; }
	    /* Special extension, drop thru to handle */
	    t = findctype((tflags&PF_UNSIGNED) ? TS_UCHAR : TS_CHAR,
				qflags | chrsiz, 1, (TYPE *)NULL);
	    qflags = 0;		/* Don't re-do these */
	    break;

	case PF_SHORT:
	case PF_SIGNED|PF_SHORT:
	case PF_SHORT|PF_INT:
	case PF_SIGNED|PF_SHORT|PF_INT:	t = shrttype;	break;
	case PF_UNSIGNED|PF_SHORT:
	case PF_UNSIGNED|PF_SHORT|PF_INT: t = ushrttype; break;
	case PF_INT:
	case PF_SIGNED:
	case PF_SIGNED|PF_INT:		t = inttype;	break;
	case PF_UNSIGNED:
	case PF_UNSIGNED|PF_INT:	t = uinttype;	break;
	case PF_LONG:
	case PF_SIGNED|PF_LONG:
	case PF_LONG|PF_INT:
	case PF_SIGNED|PF_LONG|PF_INT:	t = longtype;	break;
	case PF_UNSIGNED|PF_LONG:
	case PF_UNSIGNED|PF_LONG|PF_INT: t = ulongtype; break;
	case PF_DOUBLE:			t = dbltype;	break;
	case PF_LONG|PF_DOUBLE:		t = lngdbltype;	break;

	default:
	    error(errmsg);	/* Say bad typespec combo */
	    if (nt == flttype || (tflags&PF_DOUBLE))
		t = dbltype;
	    else t = inttype;
	}
    }
    if (qflags) {		/* Add any type qualifiers to the basic type */
	int oflag = 0;
	if (t->Tspec == TS_FUNCT)	/* See typedef comments */
	    advise("Ignoring type-qualifiers for function type");
	else if (t->Tspec == TS_ARRAY) {
	    /* If typedef name was array type, must add qualifier to the
	    ** base element of array, not to the array type itself.
	    */
	    TYPE *st;
	    for (nt = t; st = nt->Tsubt; nt = st)
	        if (st->Tspec != TS_ARRAY) {
		    nt->Tsubt = findctype(st->Tspec,
					qflags | (oflag = st->Tflag),
					st->Tsize, st->Tsubt);
		    break;
		}
	} else t = findctype(t->Tspec, qflags | (oflag = t->Tflag),
					t->Tsize, t->Tsubt);
	if (oflag &= qflags)
	    warn("Redundant type qualifier \"%s\"",
			(oflag&TF_CONST) ? "const" : "volatile");
    }
    if (symp)
	symp->Stype = t;
    return (int)t;
}
/* TAGSPEC - Handles tag for struct/union/enum
*/
static TYPE *
tagspec(typ)
int typ;		/* TS_STRUCT, TS_UNION, or TS_ENUM */
{
    SYMBOL s, *tagsym, *osym;

    switch (nextoken()) {
    case Q_IDENT:	/* Have tag ident */
	tagsym = symftag(osym = csymbol);	/* Get existing tag if any */
	if (nextoken() == T_LBRACE)	/* (Note this clobbers csymbol) */
	    nsdefs++;			/* Defining, say decl has side effs */
	break;

    case T_LBRACE:	/* No tag given, will have to make up internal one. */
	tagsym = osym = NULL;
	break;

    default:
	error("struct/union/enum not followed by tag or definition");
	while (1)  switch (nextoken()) {
	    case T_EOF: case T_RBRACE: case T_SCOLON:
		return NULL;
	}
    }

    /* If a tag symbol already exists, check it to see whether we can use
    ** that one or need to create a new one.  This also checks for
    ** duplicate definitions.
    ** Note that if this is a reference (not a definition) then we always
    ** just use whatever tagsym is.
    */
    if (tagsym && token == T_LBRACE) {	/* If this will be a definition */
	if (tagsym->Sclass == SC_TAG) {	/* Tag already defined? */
	    if (isdupsym(tagsym))	/* Dup def? */
		error("Duplicate tag definition: %S", tagsym);
	    tagsym = NULL;		/* Force a new definition */
	} else if (tagsym->Sclass == SC_UTAG) {	/* A ref already exists? */
	    if (!isdupsym(tagsym))	/* If not in same block, */
		tagsym = NULL;		/* don't satisfy the ref! */
	}
    }
    if (tagsym && tagsym->Stype->Tspec != typ) {
	error("Tag redefined with different type: %S", tagsym);
	tagsym = NULL;
    }

    /* If no tag (specified or internal) exists, make one and pretend it
    ** was seen as a forward reference (which it may well be).
    */
    if (tagsym == NULL) { 		/* Need tag symbol? */
	if (osym)			/* If already have an identifer, */
	    tagsym = symqcreat(osym);	/* use that for quick sym setup! */
	else {	/* No existing tag, invent one. */
		/* Note (safe) assumption that ident string is big enough */
	    sprintf(s.Sname, "%c%d", SPC_TAG, ++itags);
	    tagsym = creatsym(s.Sname);	/* Make symbol of right scope */
	}
	tagsym->Sflags |= SF_TAG;
	tagsym->Sclass = SC_UTAG;	/* but with no defined body */
	tagsym->Ssmnext = NULL;		/* No members yet */
	tagsym->Srefs++;		/* This creation is a reference too */
	if (inproto)			/* If created within fn prototype, */
	    tagsym->Sflags |= SF_PROTOTAG;	/* flag it for ridlsym */
					/* Create new type for it */
	tagsym->Stype = findtype(typ, (TYPE *)tagsym);
	tagsym->Stype->Tsmtag = tagsym;	/* Make sure type points to tag */
    }
    if (osym && osym->Sclass == SC_UNDEF)
	freesym(osym);			/* Can flush orig sym now if unused */

    if (token != T_LBRACE)		/* If no definition, just return */
	return tagsym->Stype;

    /* Define the structure. */
    nextoken();				/* Flush the left brace */
    if (typ == TS_ENUM) sdeclenum(tagsym);
    else tagsym->Stype->Tsize = sdeclstruct(tagsym, typ);
    if (eof) error("Unexpected EOF in declaration");
    else expect(T_RBRACE);		/* Now flush the right brace */

    tagsym->Sclass = SC_TAG;		/* Struct is now defined */
    return tagsym->Stype;
}
/* SDECLENUM - define enumeration type
*/
static void
sdeclenum(tag)
SYMBOL *tag;
{
    int val;
    SYMBOL *s, *last;

    s = NULL;
    val = 0;			/* start at zero */
    last = tag;
    for (;; nextoken()) {
	if (token == T_RBRACE) {
	    if (!s) warn("Empty enum definition list");
	    break;
	}
	if (token != Q_IDENT) {
	    error("Identifier expected as enum constant");
	    break;
	}
	nsdefs++;		/* Enum constant def as "side effect" */
	s = csymbol;		/* get identifier */
	if (isdupsym(s))
	    errdupsym(s);
	s = uniqsym(s);		/* Create or re-use symbol as needed */
	s->Sclass = SC_ENUM;	/* this is an enum constant */
	s->Stype = inttype;	/* acting like an int */
	s->Ssmtag = tag;	/* Remember tag defined within */
	s->Ssmnext = NULL;
	last->Ssmnext = s;	/* Link onto list of enum members */

	if (nextoken() == Q_ASGN) {
	    nextoken();		/* want specific value for this one */
	    val = pconst();	/* so set it to given constant */
	}
	s->Svalue = val++;		/* with this value */
	if (token != T_COMMA)
	    break;
    }
    /* Current token should now be T_RBRACE.  Caller will check. */
}


/* SDECLSTRUCT - Parse struct/union member list.
**	Returns size in words of the struct/union.
*/
static int
sdeclstruct(tag, typ)
SYMBOL *tag;
int typ;
{
    SYMBOL *lastmem = tag;
    int maxsize = 0, offset = 0, boffset = 0, inbitf = 0;
    int savpok = paramok;

    paramok = 0;			/* Not OK to collect param syms */
    while (!eof && token != T_RBRACE) {
	lastmem = sdeclaration(tag, lastmem, &offset, &boffset, &inbitf);
	if (typ == TS_STRUCT) continue;

	/* For union, each member starts over at beginning */
	if (boffset > 0) offset++;	/* Round out to full word */
	if (offset > maxsize)		/* Remember size of largest element */
	    maxsize = offset;
	offset = boffset = inbitf = 0;	/* Start over with zero offset */
    }
    paramok = savpok;			/* Restore saved paramok */

    /* Return either total size (struct) or largest element size (union) */
    if (typ == TS_STRUCT) {
	if (boffset > 0) offset++;	/* Round offset up to full word */
	maxsize = offset;		/* Total size is current offset */
    }
    if (maxsize == 0)
	error("Empty %s declaration", typ == TS_STRUCT ? "struct" : "union");
    return maxsize;
}
/* SDECLARATION - declare members of a struct or union
*/
static SYMBOL *
sdeclaration(tag, prevsmem, offset, boffset, inbitf)
SYMBOL *tag, *prevsmem;
int *offset, *boffset, *inbitf;
{
    SYMBOL base, tempsym, *u, *s;
    int offcode, bsiz, ts;

    pbase(&base);		/* Parse base storage-class and type */
    if (base.Stype == NULL) {
	error("No type-specifier for struct member, assuming int");
	base.Stype = deftype;
    }
    if (base.Sclass != SC_UNDEF)
	error("Storage class illegal for struct member");
    base.Sclass = SC_MEMBER;

    while (1) {
	if (token == T_COLON) {

	    /*
	    ** Colon without declarator before it - specifies space
	    ** left for alignment.  Constant expression following
	    ** colon is how much space, or zero to align to a word.
	    */
	    if (tag->Stype->Tspec == TS_UNION) {
		if (clevel < CLEV_ANSI)
		    error("Bit-field not allowed in union");
		else advise("Unnamed bit-field meaningless in union");
	    }
	    nextoken();			/* skip over colon */
		/* If not on word boundary and previous member was not a
		** bitfield, force alignment.  There can be non-bitfield
		** objects smaller than a word (eg chars).
		*/
		if (*boffset && !(*inbitf)) {
		    *boffset = 0;	/* Force to word boundary */
		    (*offset)++;
		}
	    fldsize(pconst(), offset, boffset); /* parse & handle size */
	    (*inbitf)++;			/* Say in bitfield now */
	} else {

	    /*
	    ** Normal declarator.  Parse it, then check if there is
	    ** a colon expression after it making it a bit field, or
	    ** if it is a whole word expression.
	    **
	    ** For bitfields, the offset is encoded as follows:
	    **  offcode % 07777 - high 12 bits of byte pointer to the field
	    **  offcode >> 12   - word offset in struct
	    ** and then the whole thing is negated.
	    **
	    ** Note that we let the bit offset remain at 36 rather
	    ** than folding it to zero - the calculations are easier.
	    */

	    copysym(&tempsym, &base);
	    u = declarator(&tempsym);
	    if (u == NULL) {		/* Check for case of no identifier */
		warn("Null declarator");
		if (token == T_COLON) continue;	/* Pretend no declarator */
		if (token == T_COMMA) {
		    nextoken();
		    continue;
		}
		break;			/* Something bad, stop loop */
	    }

	    if (token == T_COLON) {

		/* Handle bitfield */
		if (tag->Stype->Tspec == TS_UNION
		  && clevel < CLEV_ANSI)
		    error("Bit-field not allowed in union");
		switch (tempsym.Stype->Tspec) {
		    case TS_INT:
			ts = TS_BITF;  break;
		    default:
			error("Bit-field must be int or unsigned int");
		    case TS_UINT:		/* Above error drops thru */
			ts = TS_UBITF; break;
		}
		/* If not on word boundary and previous member was not a
		** bitfield, force alignment.  There can be non-bitfield
		** objects smaller than a word (eg chars).
		*/
		if (*boffset && !(*inbitf)) {
		    *boffset = 0;	/* Force to word boundary */
		    (*offset)++;
		}
		(*inbitf)++;			/* Say now in bitfield */

		nextoken();			/* move over colon */
		bsiz = pconst();		/* Parse size */
		offcode = fldsize(bsiz, offset, boffset);	/* Handle it */
		tempsym.Stype = findctype(ts,		/* Make bitfld type */
			bsiz | (tempsym.Stype->Tflag&TF_QUALS),
			1, (TYPE *)NULL);
	    } else {			/* not bitfield */

		/* Handle normal non-bitfield member */

		/* Leave byte mode if necessary.
		** If new member is not a byte object, or if byte mode is
		** due to previous bitfield, always leave it, to force
		** word alignment.
		*/
		if (tempsym.Stype->Tspec == TS_VOID) {
		    error("Struct/union member cannot be void");
		    tempsym.Stype = deftype;
		}

		bsiz = tisscalar(tempsym.Stype) ?	/* Get object size */
			tbitsize(tempsym.Stype)		/* in bits if can */
			: TGSIZ_WORD;		/* else force wd mode */
		if (*boffset > 0		/* If in byte mode */
		  && ((bsiz >= TGSIZ_WORD)	/* and obj not byte */
		      || (*inbitf))) {		/* or prev obj was bitfield */
		    *boffset = 0;	/* Then force word alignment */
		    (*offset)++;	/* and move up */
		}
		*inbitf = 0;		/* Say no longer in bitfield */

		/* Now see if this type should be in byte mode or not.
		** Sizes smaller than a word will either enter or remain
		** in byte mode.  If the size is >= to a word, then the
		** above code will have already taken us out of byte mode.
		*/
		if (bsiz < TGSIZ_WORD) {
		    if (*boffset % bsiz)	/* Align to byte bndry */
			*boffset += bsiz - (*boffset % bsiz);
		    offcode = fldsize(bsiz, offset, boffset);

		} else {			/* One or more words */
		    offcode = *offset;		/* starts at offset */
		    *offset += sizetype(tempsym.Stype); /* remember size */
		}
	    }

	    /*
	    ** Now we have parsed the declarator, and the encoded
	    ** offset is in offcode.  Always make a new symbol for each
	    ** structure member.
	    */
	    if ((s = symfmember(u, tag)) != NULL) {	/* Check for dup */
		/* If a dup, just ignore current declaration. */
		error("Duplicate struct member declaration: %S", s);
		if (u->Sclass == SC_UNDEF) freesym(u);
	    } else {
		u = symqcreat(u);		/* Get new sym w/right scope */
		u->Sflags |= SF_MEMBER;
		u->Sclass = SC_MEMBER;		/* Say it is a struct mem */
		u->Ssmoff = offcode;		/* with the given offset */
		u->Stype = tempsym.Stype;	/* Store type of member */
		u->Ssmtag = tag;		/* Point to parent structure */
		u->Ssmnext = NULL;		/* This is last mem so far */
		prevsmem->Ssmnext = u;	/* Point prev smem to this one */
		prevsmem = u;		/* This is new current smem */
		/* Pass on knowledge of any type qualifiers in struct! */
		if (u->Stype->Tflag & (TF_QUALS|TF_SIQUALS)) {
		    if (u->Stype->Tflag & (TF_CONST|TF_SICONST))
			tag->Stype->Tflag |= TF_SICONST;
		    if (u->Stype->Tflag & (TF_VOLATILE|TF_SIVOLAT))
			tag->Stype->Tflag |= TF_SIVOLAT;
		}
	    }
	}
	if (token != T_COMMA) break;
	nextoken();
    }
    expect(T_SCOLON);
    return prevsmem;			/* return with latest pointer */
}
/* FLDSIZE - handle bitfield size specification
*/
static int
fldsize(bsiz, offset, boffset)
int bsiz, *offset, *boffset;
{
    if (bsiz > TGSIZ_WORD || bsiz < 0)	/* range check */
	error("Bit field longer than word (%d bits)", TGSIZ_WORD);
    if (bsiz == 0 && *boffset > 0)	/* Zero size means round to wd bdry */
	*boffset = TGSIZ_WORD+1;	/* Hack so roundup is forced */

    *boffset += bsiz;			/* advance by that many bits */
    if (*boffset > TGSIZ_WORD) {	/* If not enough room */
	*boffset = bsiz;		/* move bit offset to word bdy */
	(*offset)++;			/* in next word */
    }
    /* Return encoded offset */
    return -(((*offset * 64) + TGSIZ_WORD - *boffset) * 64 + bsiz);
}
/* DECLARATOR - Parse a declarator with or without identifier
**	[dpANS 3.5.4, 3.5.5]
**
**	<declor> ::= {pointer} <direct-declor>
**
**	<direct-declor> ::= <ident>
**				| '(' <declor> ')'
**				| <direct-declor> '[' {const-expr} ']'
**				| <direct-declor> '(' <param-type-list> ')'
**				| <direct-declor> '(' {ident-list} ')'
**
**	<abstract-declor> ::= <pointer>
**				| {pointer} <direct-abs-declor>
**	<direct-abs-declor> ::=
**				| '(' <abstract-declor> ')'
**				| {direct-abs-declor} '[' {const-expr} ']'
**				| {direct-abs-declor} '(' {param-type-list} ')'
**
**	<pointer> ::= '*' {type-qual-list} {pointer}
**	<type-qual-list> ::= *[<type-qual>]
**
** A DECLARATOR specifies the identifier being declared and may also
** supply additional type information.  The resulting type is one of:
**	* decl		- Pointer to
**	( decl )	- parens used to establish precedence
**	ident		- Simple declarator
**	decl [...]	- Array of
**	decl (...)	- Function (definition or reference)
**
** This function parses both <declor> and <abstract-declor>.  The main
** difficulty in distinguishing them is how to interpret a left-parenthesis;
** does it indicate a function, or merely group an inner declarator?
** The decision can be made based on the following token and on whether
** the identifier has already been seen or not:
**	Ident?	Next token	Declor	Interpretation	
**
**	Yes	'(' <ident>	Direct	Function, with <ident-list>.
**	Yes	'(' ')'		Direct	Function, no prototype info.
**	Yes	'(' <decl-spec>	Direct	Function, with <param-type-list>.
**	Yes	'(' other	Direct	Illegal
**	No	'(' <ident>	Direct	Start of grouping
**	No	'(' ')'		Abstract Function, no prototype info.
**	No	'(' <decl-spec>	Abstract Function, with <param-type-list>.
**	No	'(' other	Either	Start of grouping
**
**
** All declaration statements eventually call this routine.
**	normal = extdef (toplevel) & decllist (toplevel & local)
**	smems = sdeclaration (decl within structure definition)
**	params = pdecllist (old-style function param decls)
**		paramlist (new-style proto param decls)
**	abstract = typename (abstract declaration for casts & sizeof)
**
** The argument symbol pointer "d" must always point to a temporary symbol
** structure which is NOT in the symbol table.  The parsed type is
** returned in this symbol.  It may be the same as the original type.
**
** The return value is NULL if nothing was parsed or the declarator was
** an abstract declarator.  Otherwise, the return value is a pointer
** to the symbol table entry of the parsed identifier, and the declarator
** was a normal non-abstract declarator.
*/
#define DF_INABSTR 01	/* In abstract declarator */
#define DF_INPROTO 02	/* In function prototype */
#define DF_INSMEM 04	/* In struct/union declaration */
#define DF_TYPEDEF 010	/* In typedef */

static SYMBOL *
declarator(d)
SYMBOL *d;
{
    SYMBOL *idsym;	/* Symtab ptr to parsed identifier */
    TYPE *pp;		/* Holds "derivation part" of type */
    int saveidsc;	/* Temp while parsing function params */

    d->Spmnext = d->Spmhead = NULL;	/* Init paramlist in case function */
    d->Svalue = 0;			/* Ditto param style indicator */

    /* Parse <pointer> before ident part, and mung the base type directly. */
    while (token == Q_MPLY) {
	int tflag = 0;
	for (;;) {
	    switch (nextoken()) {
	    case T_CONST:
		if (tflag&TF_CONST) error("Duplicate \"const\"");
		tflag |= TF_CONST;
		continue;
	    case T_VOLATILE:
		if (tflag&TF_VOLATILE) error("Duplicate \"volatile\"");
		tflag |= TF_VOLATILE;
		continue;
	    }
	    break;
	}
	d->Stype = pushsztype(TS_PTR, tflag, typsiztab[TS_PTR], d->Stype);
    }

    /* Now parse main part, <direct-declor> or <direct-abs-declor>.
    ** This is normally the ident,
    ** but it can be missing for abstract declarators.
    */
    switch (token) {
    case T_LPAREN:			/* Left-paren is tricky case */
	if (nextoken() == T_RPAREN	/* Must examine next token */
	  || isdecl()) {
	    /* '(' ')' -- Abstract function without prototype info */
	    /* '(' <decl-spec> -- Abstract function with prototype info */
	    idsym = NULL;
	    pp = fundecl(d, DF_INABSTR);

	} else {			/* None of those, assume grouping. */
	    TYPE *savt = d->Stype;	/* Save type thus far */
	    d->Stype = NULL;		/* and pretend it's null */
	    idsym = declarator(d);	/* Now parse stuff in parens */
	    pp = d->Stype;		/* Make base be derived part! */
	    d->Stype = savt;		/* Then can restore saved type */
	    expect(T_RPAREN);
	    /* pp is now NULL if no derivation was added, or will be some
	    ** sequence of "pointer to", "array of", or "function returning"
	    ** with an ultimate NULL base which has to be filled in later.
	    */
	}
	break;

    case Q_IDENT:
	idsym = csymbol;		/* Remember sym ptr for this ident */
	nextoken();
	pp = NULL;			/* No derived part */
	break;

    default:
	idsym = NULL;			/* No identifier */
	pp = NULL;			/* No derived part */
	break;
    }

    /* Check for function or array specifiers */
    for (;;) {
	switch (token) {
	case T_LPAREN:		/* Function definition or reference */
	    /* Parse param list of function.  If idsym is a new symbol,
	    ** we set it temporarily so as to avoid bashing our global
	    ** function-name symbol if it turns out to have a parameter
	    ** name the same as the function name!
	    */
	    if (idsym && ((saveidsc = idsym->Sclass) == SC_UNDEF))
		idsym->Sclass = SC_EXLINK;	/* Fake out paramlist */
	    nextoken();				/* Start paramlist parse */
	    pp = addpp(pp, fundecl(d,
		(paramok && !inproto && d->Sclass != SC_TYPEDEF)
		? 0 : DF_INPROTO));
	    if (idsym)			/* Restore real class after fakeout */
	    {   idsym->Sclass = saveidsc;
#if SYS_CSI
		idsym->Sflags = d->Sflags;	/* copy base flags */
#endif
	    }	    
	    break;

	case T_LBRACK:		/* Array of something */
#if SYS_CSI
	    if( d->Sflags & TF_FORTRAN )
		warn("FORTRAN arrays not supported: will be C array");
#endif
	    nextoken();
	    pp = addpp(pp, pushsztype(TS_ARRAY, 0,
				    (token == T_RBRACK)? 0 : pconst(),
				    (TYPE *)NULL));
	    expect(T_RBRACK);
	    break;
	    
	default:		/* Not array or function, we're done */
	    /* Add base type to built-up derived type, return the result */
	    if (pp) d->Stype = addpp(pp, d->Stype);
	    return idsym;
	}
    }
}
/* FUNDECL - Parse function parameter list declarator
**		This may be either <param-type-list> or <ident-list>.
**	[dpANS 3.5.4]
**
**	<param-type-list> ::= <param-list> {',' "..."}
**	<param-list> ::= <param-decl> *{',' <param-decl>}
**	<param-decl> ::= <decl-specs> {<declor> | <abs-declor>}
**	<ident-list> ::= <ident> *{',' <ident>}
**
** Only called by declarator() to handle a function type declaration.
** Current token is the first one following the opening left-paren.
** On return, current token is first thing after the right paren.
** (if error, current token is whatever halted the parse).
** Returns a pointer to the resulting function type, with a NULL
**	return-value subtype which is filled in later by declarator().
**
** If any valid parameter identifiers were seen, the following values are also
** returned in the declarator symbol struct:
**	Svalue - 0 if old-style identlist, N+1 if new-style prototype,
**			where N is the # of parameters ("void" and "..."
**			are not counted).
**	Spmnext - pointer to 1st parameter in param list
**	Spmhead - start of local symbol block that was opened for the
**		prototype scope, as returned by beglsym().  This block will
**		still be active on return, and must be closed by the caller
**		eventually.
*/
struct protostate {		/* State block for prototype parse */
	int nparams;
	SYMBOL *shead, *stail;	/* Parameter symbol list */
	SYMBOL decl;
};

static TYPE *
fundecl(d, dflag)
SYMBOL *d;
{
    TYPE *proto = NULL;

    if (token == T_RPAREN)		/* Quick check for most common case */
	nextoken();			/* No param or ident list */
    else {
	struct protostate ps;
	SYMBOL *blkhead;

	blkhead = beglsym();		/* Begin a local symbol block */
	++inproto;			/* Now inside prototype scope! */
	ps.nparams = 0;			/* No params yet */
	ps.shead = ps.stail = NULL;	/* Empty param sym list */
	if (!isdecl()) {
	    if (dflag) error("Bad context for old-style function parameters");
	    pidentlist(&ps);		/* Handle old identlist form */
	} else {
	    proto = paramlist(&ps);	/* Handle new prototype form */
	    ps.nparams += 1;		/* Ensure new-style indicator is set */
	}
	--inproto;			/* Out of proto scope... */
	if (dflag)		/* If wasn't OK to collect params, then */
	    ridlsym(blkhead);	/* quietly flush all syms defd in prototype */
	else {			/* Top-level, so remember param idents! */
	    if (!ps.shead)	/* If no actual parameter idents, then */
		ridlsym(blkhead);	/* flush sym block anyway */
	    if (d->Spmnext) {
		int_error("fundecl: already parsed funct params");
		ridlsym(blkhead);
	    } else {		/* Keep local sym block active */
		d->Spmnext = ps.shead;
		d->Spmhead = blkhead;
		d->Svalue = ps.nparams;	/* Remember # of new-style params */
	    }
	}

	/* Done, must have ended in right-paren */
	if (token == T_RPAREN) nextoken();
	else {
	    error("Bad syntax in function parameter list");
	    for (;;) {
		switch (nextoken()) {
		default: continue;
		case T_EOF: case T_RBRACE: case T_SCOLON: case T_RPAREN:
		    break;
		}
		break;
	    }
	}
    }
    return findftype((TYPE *)NULL, proto);
}

/* PIDENTLIST - Parse old ident-list form of function params.
**	Note ps->nparams is always left 0 as "old-style" flag.
*/
static void
pidentlist(ps)
struct protostate *ps;
{
    SYMBOL *s;

    /* Parse parameter list as a simple ident-list. */
    while (token == Q_IDENT && csymbol->Sclass != SC_TYPEDEF) {
	if (csymbol->Sclass == SC_ARG)	/* Already a parameter? */
	    error("Duplicate parameter: \"%s\"", csymbol->Sname);
	s = uniqsym(csymbol);	/* make local sym for param */
	if (ps->shead == NULL)	/* Save pointer to this param */
	    ps->shead = s;	/* either in head of list */
	else ps->stail->Spmnext = s;	/* or in last param so far */
	ps->stail = s;		/* Move on to end of chain */
	s->Spmnext = NULL;	/* This one is now the last */
	s->Sclass = SC_ARG;	/* Say it's a function parameter */
	s->Stype = deftype;	/* and (int) unless declared later */

        if (nextoken() != T_COMMA) break;
	nextoken();
    }
}

/* PARAMLIST() - Parse parameter-type-list (function prototype declaration)
**
** Recursion is needed in order to come up with a canonical
** prototype list without lots of wasteful duplication.  The problem is
** that calls to findptype() must already know what the Tproto value
** is before a pure type can be found/created.  So we have to parse the
** entire parameter list before the prototype can be constructed!
** Stack usage is minimized by using as few args and locals as possible.
*/
static TYPE *
paramlist(ps)
struct protostate *ps;
{
    SYMBOL *s;
    TYPE *t;

    if (token == T_RPAREN)
	return NULL;
    if (token == T_ELPSIS) {
	if (!ps->nparams)
	    error("Ellipsis must follow a parameter");
	return findptype(TS_PARINF,
		(nextoken() == T_COMMA ? (nextoken(), paramlist(ps)) : NULL),
		NULL);
    }

    pbase(&(ps->decl));			/* First get <decl-specs> */
    switch (ps->decl.Sclass) {
	default:
	    error("Only storage class allowed for param is \"register\"");
		/* Drop thru to pretend normal arg */
	case SC_UNDEF:	ps->decl.Sclass = SC_ARG;	break;
	case SC_RAUTO:	ps->decl.Sclass = SC_RARG;	break;
    }
    if (ps->decl.Stype == NULL) {
	if (token == T_COMMA || token == T_RPAREN)
	    error("Null parameter declaration");
	else error("No type-specifier for parameter, assuming int");
	ps->decl.Stype = deftype;
    }
    s = declarator(&(ps->decl));	/* Finish type, maybe get ident */
    t = ps->decl.Stype;

    /* Now check type for conversions, and add to type list */
    switch (t->Tspec) {
	case TS_VOID:			/* Check for "(void)" */
	    if (!ps->nparams && !s && token == T_RPAREN)
		return findptype(TS_PARVOID, (TYPE *)NULL, (TYPE *)NULL);
	    error("Function parameter cannot be void");
	    t = deftype;
	    break;
	case TS_FUNCT:			/* Function of T becomes */
	    t = findtype(TS_PTR, t);	/* pointer to function of T */
	    break;
	case TS_ARRAY:			/* Array of T becomes */
	    t = findtype(TS_PTR, t->Tsubt);	/* pointer to T */
	    break;
    }

    /* Add parameter symbol to list.  Always need to build list, even if
    ** discarding later, so we can diagnose duplicate param defs.
    */
    if (s) {
	if (s->Sclass == SC_ARG	/* Already a parameter? */
	    || s->Sclass == SC_RARG) {
	    if (isdupsym(s))		/* See if defined in current block */
		error("Duplicate parameter: \"%s\"", s->Sname);
	}
	s = uniqsym(s);			/* Make local sym for param */
	if (ps->shead == NULL)		/* Save pointer to this param */
	    ps->shead = s;		/* either in head of list */
	else ps->stail->Spmnext = s;	/* or in last param so far */
	ps->stail = s;			/* Move on to end of chain */
	s->Spmnext = NULL;		/* This one is now the last */
	s->Stype = t;			/* Set type, and */
	s->Sclass = ps->decl.Sclass;	/* say it's a function parameter */
    }

    ps->nparams++;
    return findptype(TS_PARAM,
	((token == T_COMMA) ? (nextoken(), paramlist(ps)) : NULL),
	t);
}
/* ADDPP - add type to inside of nesting
**	Only invoked by declarator().
*/
static TYPE *
addpp(pp, t)
TYPE *pp, *t;
{
    /*
    ** This takes a base-less type structure in pp, and returns the
    ** result of replacing the NULL where the base should be with t.
    ** Thus it is the inverse of pushsztype(), adding the new type
    ** at the base of the structure rather than at the top.
    **
    ** I realize the recursive definition below may look messy,
    ** but an iterative definition of this function would be worse...
    */

    return (pp == NULL) ? t
	: pushsztype(pp->Tspec, pp->Tflag, pp->Tsize, addpp(pp->Tsubt, t));
}

/* PUSHTYPE - Construct a derived declarator type, checking for validity.
**
** As per [H&S 4.5.5] checks for the following illegal type
** combinations:
**	(1) Any type with "void" except "... function returning void"
**		Note [dpANS] also permits "pointer to void".
**	(2) "Array of function ..."
**	(3) "Function returning array of ..."
**	(4) "Function returning function ..."
** There is also one other special case (5) that this list omits: as per
** [H&S 4.5.3 and 5.5.3], a N-dimensional array must always have
** all of the last N-1 sizes specified; only the first dimension's size
** can be omitted.
**
**	If the type combination is illegal, some plausible type is
** substituted, not so that something useful will be compiled but so
** that no bizarre "types" will unduly interfere with scanning the rest of the
** source file for further errors.
**	Note that "void" is OK all by itself as a type.  The checks here
** only look for illegal COMBINATIONS of types.
*/
static TYPE *
pushsztype(typ, flags, siz, ptr)
TYPE *ptr;
{
    if (ptr != NULL) switch (typ) {	/* If making a combination, check it */
	default:	/* Top type must be one of array, funct, or ptr! */
	    int_error("pushsztype: bad top type %d", typ);
	    ptr = NULL;
	    break;

	case TS_PTR:			/* Ptr to most anything is OK */
	    if (ptr->Tspec == TS_VOID	/* But (1) allow "ptr to void" */
	      && clevel < CLEV_ANSI)	/* only if level high enough */
		warn("Restricted type - pointer to void");
	    break;

	case TS_ARRAY:		/* Check for (2) and (5) */
	    switch (ptr->Tspec) {
		case TS_VOID:
		    error("Illegal type - array of void");
		    ptr = inttype;	/* Use int instead */
		    break;
		case TS_ARRAY:		/* Array of arrays OK if size given */
					/* array[][x] ok, array[x][] bad */
		    if (ptr->Tsize == 0) {
			error("Illegal type - array of unknown-sized array");
			ptr = inttype;	/* Use int instead */
		    }
		    break;
		case TS_FUNCT:		/* Array cannot have functions */
		    error("Illegal type - array of function");
		    ptr = inttype;	/* Lose, substitute "int" */
		}
		break;

	case TS_FUNCT:		/* Check for (3) and (4) */
	    switch (ptr->Tspec) {
		case TS_ARRAY:		/* Function cannot return array */
		case TS_FUNCT:		/* Function cannot return function */
		    error("Illegal type - function returning %s",
			ptr->Tspec == TS_ARRAY ? "array" : "function");
		    ptr = voidtype;	/* Lose, substitute void */
		    break;
	    }
	    break;
    }

    /* Now hash up the actual type and return the canonicalized version */
    return findctype(typ, flags | typbsiztab[typ], siz, ptr);
}
/* DODECL - Do processing for a parsed declarator
**	Called by decllist() to process the results of parsing a declarator.
** This IS called for:
**	toplevel declarations
**	local (at head of block) declarations
** This is NOT called for:
**	abstract declarators - handled by typename().
**	function parameter declarations - handled by funcdef().
**	structure/union declarations - handled by sdeclaration().
** This also checks for an initializer, and handles it if one
** exists.  Note the first two args refer to temporary symbol structures
** which are NOT in the symbol table.
*/

static NODE *
dodecl(baseclass, d, s)
int baseclass;	/* Whatever was actually parsed as storage class */
SYMBOL *d,	/* Declarator identifier, with sc and type all set up */
	*s;	/* Symbol table entry for the identifier */
{
    SYMBOL *ns;
    TYPE *nt;
    NODE *z;

    /* Symbol table entry will always exist, because the
    ** lexer will have created it if necessary as a global symbol with
    ** symbol class SC_UNDEF.  If the new symbol actually should be a local one
    ** then it needs to be flushed and then created again in the right place.
    */

    /* Check for a function reference, which needs special handling */
    switch (d->Stype->Tspec) {
	case TS_VOID:			/* Declaring a void object? */
	    if (d->Sclass != SC_TYPEDEF) {
		error("Cannot declare %S (or any object) as void", s);
		d->Stype = deftype;
	    }
	    break;
	case TS_FUNCT:		/* Function reference or typedef */
	    /* Take care of open local sym block if necessary */
	    if (d->Spmnext) {		/* Param sym blk active? */
		if (d->Svalue == 0)	/* Old-style function identlist? */
		    error("Bad syntax - function parameters without body");
		ridlsym(d->Spmhead);	/* Yes, flush syms from paramlist */
		d->Spmnext = d->Spmhead = NULL;	/* Be neat just in case */
	    }
	    if (d->Sclass != SC_TYPEDEF) {	/* A function reference? */
		funchk(0, baseclass, d, s);	/* Yep, go handle */
		return NULL;		/* function decl doesn't use storage */
	    }
	    break;
    }

    /* Real variable or typedef, do things depending on class */
    switch (d->Sclass) {
    case SC_TYPEDEF:		/* Type definition */
	if (isdupsym(s))	/* This symbol already def'd in same block? */
	    errdupsym(s);
	s = uniqsym(s);		/* Ensure sym is fresh new one */
	s->Sclass = d->Sclass;	/* Fill in necessary parts of sym */
	s->Stype = d->Stype;
	return NULL;		/* no initialization or storage */

    case SC_AUTO:		/* local extent variable, in function */
    case SC_RAUTO:
	if (isdupsym(s))
	    error("Duplicate definition: \"%s\"", s->Sname);
	s = uniqsym(s);		/* Always make local cell */
	s->Sclass = d->Sclass;	/* Fill in necessary parts of sym */
	s->Stype = d->Stype;
	break;			/* Go check for izer */

    /* External linkage, use existing sym if any.
    ** No storage class given, treat basically as if "extern" with
    ** possibility of being a tentative definition.
    ** Not sure what to do about re-using Sflags.
    */
    case SC_EXLINK:		/* No storage class given, file-scope */
	if (lsymhead) {		/* Should always be at file scope */
	    int_error("dodecl: scope mismatch");
	    return NULL;
	}
	s = symfxext(s);	/* Ensure an SC_XEXTREF is made visible */
	if (s->Sclass != SC_UNDEF) {	/* Check out type if any */
	    --(s->Srefs);		/* Linkage isn't a real ref */
	    if (nt = tcomposite(d->Stype, s->Stype))
		d->Stype = nt;		/* Types compatible, use composite */
	    else errtwotyp(d, s);	/* Types not compatible, barf */
	} else if (!mapextsym(s))	/* New, so set Smaplab now */
	    error("External link name duplicated: \"%s\"", s->Sname);
	s->Stype = d->Stype;		/* Clobber sym type to new one */

	if (token != Q_ASGN) switch (s->Sclass) { /* If just a ref, simple */
	    case SC_INTDEF: case SC_INTREF: case SC_INLINK:
		error("Linkage conflict (was internal)");
		s->Sclass = (s->Sclass==SC_INTDEF ? SC_EXTDEF : SC_EXLINK);
		return NULL;

	    default:
		errdupsym(s);	/* Barf & fall thru */
	    case SC_UNDEF:
	    case SC_XEXTREF: case SC_EXTREF:
		s->Sclass = SC_EXLINK;
	    case SC_EXTDEF: case SC_EXLINK:
		return NULL;
	}
	/* Handle initializing definition */
	switch (s->Sclass) {
	case SC_INTDEF:	case SC_EXTDEF:
	    error("Multiple initialization of %s", s->Sname);
	    break;		/* Do it anyway... */
	case SC_INTREF:	case SC_INLINK:
	    error("Linkage conflict (was internal)");
	    /* Fall thru to handle as if extern */
	case SC_UNDEF:
	case SC_XEXTREF: case SC_EXTREF: case SC_EXLINK:
	    s->Sclass = SC_EXTDEF;
	    break;
	default:
	    errdupsym(s);
	    s->Sclass = SC_EXTDEF;	/* Sigh!  Smash it. */
	    break;
	}
	break;				/* Go parse izer now */

    /* "extern" - existing or external linkage, use existing sym if any.
    ** If in block scope and object ident already belongs to a
    ** block-scope symbol, we:
    ** (1) see if a file-scope symbol exists (if so, use that one)
    ** (2) see if existing sym is external ref (if so, use that)
    ** (3) generate new file-scope symbol with SC_EXTREF.
    ** Again, not sure how to re-use Sflags.
    */
    case SC_EXTREF:		/* "extern" given, file or block scope */
	s = symfxext(s);	/* Ensure an SC_XEXTREF is made visible */
	if (lsymhead && (s->Sflags&SF_LOCAL)) {
	    if (ns = findgsym(s))	/* If file-scope def/ref exists, */
		s = ns;			/* use that! */
	    else if (s->Sclass == SC_EXTREF)
			;		/* OK if a block-scope extern ref */
	    else {
		if (isdupsym(s))	/* If was defined in this block, */
		    errdupsym(s);	/* barf! */
		s = uniqsym(s);		/* Create/re-use sym, now SC_UNDEF */
	    }
	}
	/* "s" now points to the sym linked to.  If it has any linkage, we
	** copy that, otherwise we use external linkage.
	*/
	if (s->Sclass != SC_UNDEF) {	/* Check out type if any */
	    --(s->Srefs);		/* Linkage isn't a real ref */
	    if (nt = tcomposite(d->Stype, s->Stype))
		d->Stype = nt;		/* Types compatible, use composite */
	    else errtwotyp(d, s);	/* Types not compatible, barf */
	} else if (!mapextsym(s))	/* New, so set Smaplab now */
	    error("External link name duplicated: \"%s\"", s->Sname);
	s->Stype = d->Stype;		/* Clobber sym type to new one */

	if (token != Q_ASGN) switch (s->Sclass) { /* If just a ref, simple */
	    default:
		errdupsym(s);
	    case SC_UNDEF:
		s->Stype = d->Stype;
	    case SC_XEXTREF:
		s->Sclass = SC_EXTREF;
	    case SC_INTDEF: case SC_INTREF: case SC_INLINK:
	    case SC_EXTDEF: case SC_EXTREF: case SC_EXLINK:
		return NULL;
	}
	/* Handle initializing definition */
	if (lsymhead)
	    error("Illegal to initialize block-scope extern");
	switch (s->Sclass) {
	case SC_INTDEF:	case SC_EXTDEF:
	    error("Multiple initialization of %s", s->Sname);
	    break;			/* Do it anyway... */
	case SC_INTREF:	case SC_INLINK:
	    s->Sclass = SC_INTDEF;
	    break;
	default:
	    errdupsym(s);		/* Barf and fall thru */
					/* to handle as if undef */
	case SC_UNDEF:
	case SC_XEXTREF: case SC_EXTREF: case SC_EXLINK:
	    s->Sclass = SC_EXTDEF;
	    break;
	}
	break;				/* Go handle izer now */

    /* "static" - internal or no linkage 
    ** In block scope always defines a unique object with no linkage;
    **		this becomes SC_ISTATIC.
    ** In file scope, it declares an object with internal linkage:
    **		with izer, it becomes SC_INTDEF (a def w/internal linkage)
    **		otherwise, SC_INLINK (a tentative def, w/internal linkage)
    **		It is an error if a SC_INTREF (function ref) already exists.
    ** Again, not sure how to re-use Sflags.
    */
    case SC_INTREF:		/* "static" given - internal linkage */
	if (!lsymhead) {		/* Check for existing global ref */
	    d->Sclass = (token == Q_ASGN) ? SC_INTDEF : SC_INLINK;
	    if (s->Sclass == SC_XEXTREF || s->Sclass == SC_EXTDEF
	      || s->Sclass == SC_EXTREF || s->Sclass == SC_EXLINK) {
		error("Linkage conflict (was external)");
	    } else if (s->Sclass == SC_INTDEF || s->Sclass == SC_INLINK
		|| s->Sclass == SC_UNDEF) {

		if (s->Sclass != SC_UNDEF) { /* Check out type if any */
		    --(s->Srefs);	/* Not really a usage */
		    if (nt = tcomposite(d->Stype, s->Stype))
			d->Stype = nt;	/* Types compatible, use composite */
		    else errtwotyp(d, s); /* Types not compatible, barf */
		} else mapintsym(s);	/* New, set Smaplab to unique name */
		s->Stype = d->Stype;	/* Clobber sym type to new one */

		if (token != Q_ASGN) {	/* Just a reference? */
		    s->Sclass = d->Sclass;
		    return NULL;	/* Yep, checking all done! */
		}
		if (s->Sclass != SC_INTDEF) {
		    s->Sclass = SC_INTDEF;	/* Def of a prior tent def */
		    break;			/* Go parse izer */
		}
		error("Duplicate initialization of %s", s->Sname);
		/* Fall thru to create duplicate symbol and initialize it */
	    }
	} else
	    d->Sclass = SC_ISTATIC;	/* Internal static */

	/* Block scope, or handling duplicate top-level sym */
	if (isdupsym(s))		/* Check for duplicate def */
	    errdupsym(s);
	s = uniqsym(s);			/* Then always make unique cell */
	if (lsymhead)			/* If in a local block (SC_ISTATIC) */
	    s->Ssym = newlabel();	/* create internal handle on object */
	s->Sclass = d->Sclass;
	s->Stype = d->Stype;
	break;

    default:
	int_error("dodecl: illegal Sclass %d for %s", d->Sclass, s->Sname);
	return NULL;
    }

    /* Parse initializer.
    ** At this point the symbol is guaranteed to have one of these classes:
    **	SC_EXTDEF, SC_EXLINK, SC_EXTREF,
    **	SC_INTDEF, SC_INLINK, SC_ISTATIC, SC_AUTO, SC_RAUTO.
    */
    if (token == Q_ASGN) {
	nextoken();		/* skip equal sign */
	z = pizer(s);		/* New initializer parsing */
	if (z == NULL)
	    error("Null initializer for \"%s\"", s->Sname);
    } else {
	z = NULL;

	/* No initializer.  Check to make sure this is okay. */
	if (s->Stype->Tspec == TS_ARRAY		/* If type is array, check */
	  && s->Stype->Tsize == 0		/* to make sure size 0 okay */
	  && s->Sclass != SC_EXTREF		/* Only ext refs allowed */ 
	  && s->Sclass != SC_EXLINK)
	    error("Missing size for def of array \"%s\"", s->Sname);

	/* If doing a file-scope tentative definition, we defer
	** creation of a node (and thus defer emitting code to reserve
	** storage).  This will be done either at end of file or
	** when an initializing definition is seen.
	*/
	if (s->Sclass == SC_INLINK || s->Sclass == SC_EXLINK)
	    return NULL;
    }

    /* For auto variables, assign stack offset and update total stack size.
    ** This must be done after parsing initializer in case the size of an
    ** array was specified by the izer.
    */
    if (s->Sclass == SC_AUTO || s->Sclass == SC_RAUTO) {
	s->Svalue = maxauto;		/* Remember its stack offset */
	maxauto += sizetype(s->Stype);	/* and count it in to frame size */
    }

    /* Make a izer node combining a Q_IDENT for the sym with its izer */
    return ndeflr(N_IZ, ndefident(s), z);
}

/* DEFAUTO - Define an automatic variable of the given type.
**	This routine is for the benefit of CCSTMT's function call
**	parsing, which sometimes needs to create temporary internal
**	variables to hold return values.
**	The id is assumed to be unique.
*/
SYMBOL *
defauto(id, typ)
char *id;
TYPE *typ;
{
    SYMBOL *s;
    s = creatsym(id);		/* Make symbol in current scope */
    s->Sclass = SC_AUTO;
    s->Srefs++;			/* This is always a reference */
    s->Stype = typ;
    s->Svalue = maxauto;	/* Remember stack offset for it */
    maxauto += sizetype(typ);
    return s;
}
/* PIZER - Parse initializer.
**	[dpANS 3.5.7]
**
**	<izer>	::= <assignment-expr>
**			| '{' <izer-list> {','} '}'
**
**	<izer-list> ::= <izer> {',' <izer-list>}
**
** Argument is pointer to symbol identifier being initialized.
** Current token should be first thing after the '='.
**
** All expressions for a static extent object, or within an <izer-list> for
** an array, struct, or union type, must be constant expressions.  This
** can be checked for as (!izautof || lev) which will be TRUE if the
** expression must be a constant.
*/

static NODE *piztype(), *chkarith(), *pizstruct(), *pizarray(),
	*pexizer(), *pizlist();
static void pizflush();
static int isauto(), nisconst();
			/* These are set by pizer and refed by its subrs */
static int izautof;	/* True if symbol being initialized is automatic */
static SYMBOL *izsym;	/* Symbol for var being initialized */

static NODE *
pizer(s)
SYMBOL *s;
{
    izautof = isauto(izsym = s);	/* Set "globals" for subroutines */
    return piztype(s->Stype, 0);	/* Do outermost-level parse for type */
}
/* PIZTYPE - Parse initializer for a given type.
**	Recursive; makes use of izautof/izsym globals.
**	Returns what was parsed even if encountered error, mainly so
**	debugging can dump out the parse tree.
*/
static NODE *
piztype(t,lev)
TYPE *t;
int lev;			/* Level being parsed.  0 - outermost */
{
    NODE *e, *n;

    switch (t->Tspec) {
	case TS_BITF:	case TS_UBITF:
	case TS_CHAR:	case TS_UCHAR:
	case TS_SHORT:	case TS_USHORT:
	case TS_INT:	case TS_UINT:
	case TS_LONG:	case TS_ULONG:
	case TS_ENUM:			/* Enums treated like ints */
	    if (e = pexizer(lev))	/* Parse a single expression */
		return chkarith(t, e, lev, N_ICONST);
	    break;

	case TS_FLOAT:	case TS_DOUBLE: case TS_LNGDBL:
	    if (e = pexizer(lev))	/* Parse a single expression */
		return chkarith(t, e, lev, N_FCONST);
	    break;

	case TS_PTR:
	    /* Parse a single expression */
	    if ((e = pexizer(lev)) == NULL)
		break;
	    if ((n = convasgn(t, e)) != e)	/* Apply assignment convs */
		e = (optpar || !izautof || lev)	/* and optimize result if */
			? evalexpr(n) : n;	/* want or need to. */
	    if (!cmputype(t, e->Ntype)) {	/* Types must match */
		error("Pointer initializer has wrong type");
		break;
	    }

	    /* A constant expression for a pointer requires hairy checks. */
	    if ((!izautof || lev)	/* If must be a constant expr */
	      && !nisconst(e)) {	/* then check it out */
		error("Pointer initializer not constant");
		break;
	    }
	    break;		/* OK, return e */

	case TS_ARRAY:
	    return pizarray(t, lev);

	case TS_STRUCT:
	    return pizstruct(t, lev, 0);

	case TS_UNION:
	    if (clevel >= CLEV_ANSI)
		return pizstruct(t, lev, 1);
	    error("Cannot initialize union type");
	    return pizlist();	/* Parse it anyway for debugging */

	case TS_VOID:
	    error("Cannot initialize void type");
	    return pizlist();	/* Parse it anyway for debugging */

	case TS_FUNCT:
	    error("Cannot initialize function type");
	    return pizlist();	/* Parse it anyway for debugging */

	default:
	    int_error("piztype: unknown Tspec = %d", t->Tspec);
	    return NULL;
    }
    return e;
}

/* CHKARITH - Check initializer for arithmetic type.
**	This is a subroutine for code sharing purposes.
*/
static NODE *
chkarith(t, e, lev, noptyp)
TYPE *t;
NODE *e;
int lev, noptyp;
{
    NODE *n;

    if (!tisarith(e->Ntype)) {
	error("Initializer must be of arithmetic type");
	return e;
    }
    if ((n = convasgn(t, e)) != e)	/* Apply assignment convs */
	e = (optpar || !izautof || lev)	/* and optimize result if */
		? evalexpr(n) : n;	/* want or need to. */
    if ((!izautof || lev) && e->Nop != noptyp)
	error("%s constant required as initializer",
		(noptyp == N_ICONST ? "Integer" : "Floating-point"));
    return e;
}
/* PIZSTRUCT - Parse initializer for a structure or union.
**	If struct/union is static (not auto), izer must be brace-enclosed;
**		an auto izer may or may not have braces.
**	If izer is brace-enclosed or within a list, all members must be
**		constants; for unions, only one constant is allowed, and
**		this constant initializes the first member.
**	Otherwise (for auto izer w/o braces), the expression need not be
**		constant, but must have the proper struct/union type.
**
**	Returns parsed stuff even if error, for debugging.
*/
static NODE *
pizstruct(t, lev, isunion)
TYPE *t;
int lev;
int isunion;			/* TRUE if parsing union izer, else struct */
{
    register SYMBOL *smem;
    NODE *e, *n, *root;
    int braces;

    if (clevel < CLEV_ANSI && izautof)			/* Complain now */
	error("Initialization of auto struct/union not allowed");
    if ((smem = t->Tsmtag->Ssmnext) == NULL) {
	error("Attempting to initialize an undefined struct/union");
	return pizlist();		/* Flush entire izer */
    }
    if (smem->Sclass != SC_MEMBER)		/* Paranoia check */
	int_error("bad smem class");

    braces = (token == T_LBRACE);	/* Remember if have braces */
    if (!braces && lev == 0) {		/* Izing with struct/union expr? */
	if (!izautof) {			/* Can only do if auto */
	    error("Static struct/union initializer must be enclosed in braces");
	    return pizlist();		/* Flush entire izer */
	}
	e = pexizer(lev);		/* Get struct/union expr */
	if (e && !cmputype(t, e->Ntype)) {
	    error("Struct/union initializer has wrong type");
	    pizlist();
	}
	return e;
    }

    /* Now loop through structure members, reading an initializer for each.
    ** Note that first thing in list has its type set either to
    ** the overall type being initialized, if a struct, or the first
    ** member of the union, if a union.  This is for genadata() in CCGEN1.
    */
    if (braces) nextoken();			/* Skip over left-brace */
    root = n = ndeftl(N_IZLIST,			/* Start list, */
		(isunion ? smem->Stype : t),	/* giving its type. */
		piztype(smem->Stype, 1));	/* Parse 1st member */

    if (!braces) {
	while (token == T_COMMA) {
	    if (isunion
	      || (smem = smem->Ssmnext)==NULL)	/* Stop if no more members */
		return root;
	    if (smem->Sclass != SC_MEMBER)	/* Paranoia check */
		int_error("bad smem class");
	    if (nextoken() == T_RBRACE)		/* Skip comma, allow ",}" */
		return root;
	    n = n->Nright = ndefl(N_IZLIST,	/* Parse element and add in */
				piztype(smem->Stype, 1));
	}
	if (token != T_RBRACE)
	    error("Bad initializer list syntax");
	return root;

    } else {	/* Have braces, life is more complicated */
	while (token == T_COMMA) {
	    if (nextoken() == T_RBRACE)	/* Skip comma, allow ",}" */
		break;			/* Stop when done */
	    if (isunion || (smem = smem->Ssmnext) == NULL) {
		error("Too many members in initializer list");
		pizflush(1);			/* Flush rest of list */
		if (token == T_RBRACE) nextoken();
		return root;
	    }
	    if (smem->Sclass != SC_MEMBER)	/* Paranoia check */
		int_error("bad smem class");
	    n = n->Nright = ndefl(N_IZLIST,	/* Parse element and add in */
				piztype(smem->Stype, 1));
	}
	/* Check for proper ending */
	if (token != T_RBRACE) {
	    error("Bad initializer list syntax");
	    pizflush(1);		/* Flush inside of list */
	    if (token == T_RBRACE)
		nextoken();		/* Flush end close-brace if one */
	    return root;
	}
	nextoken();			/* Skip close-brace */
    }

    return root;
}
/* PIZARRAY - Parse initializer for an array.
*/
static NODE *
pizarray(t, lev)
TYPE *t;
int lev;
{
    register TYPE *subt;
    NODE *n, *root;
    int cnt, braces, size, gotstr = 0;

    if (clevel < CLEV_ANSI && izautof)			/* Complain now */
	error("Initialization of auto arrays is not allowed");

    if (((subt = t->Tsubt) == NULL)	/* Paranoia - must have subtype */
      || (t->Tsize == 0 && lev)) {	/* Unknown size only OK for top lev */
	int_error("pizarray: %s",
			(!subt) ? "null subt" : "no inner array size");
	return pizlist();		/* Flush entire izer */
    }

    braces = (token == T_LBRACE);	/* Remember whether have braces */
    if (braces) nextoken();		/* Skip over left-brace */

    /* Now handle first array element.  Requires some special hackery
    ** for initializing array of char with a string literal.
    */
    if (tischar(subt) && token == T_SCONST) {	/* Char array, and str lit? */
	if (!(n = pexizer(1)) || n->Nop != N_SCONST) {
	    int_error("pizarray: bad sconst");	/* Paranoia check */
	    return NULL;
	}
	++gotstr;			/* Got string, set flag! */
	cnt = n->Nsclen;		/* Set # of elements acquired */
	if (braces && token == T_COMMA)	/* Permit {"str",} */
	    nextoken();
    } else {
	if (!braces && lev == 0) {
	    error("Outer array initializer must be enclosed in braces");
	    pizflush(1);
	    return NULL;
	}
	n = piztype(subt, 1);		/* Parse 1st element of subtype */
    }

    /* Now loop through list, reading array elements (unless had a string).
    ** If brace-enclosed, read infinite elements up to closing brace, else
    ** stop when read just enough elements.
    ** Note that first node on list has its type set to the overall type
    ** of the array; this is for genadata() in CCGEN1.
    */
    root = n = ndeftl(N_IZLIST, t, n);		/* Put 1st into list */

    if (!gotstr) {
	size = (braces ? size = ((unsigned)(~0)>>1)	/* Inf if braces */
			: t->Tsize);
	for (cnt = 1; token == T_COMMA && cnt < size; ++cnt) {
	    if (nextoken() == T_RBRACE)		/* Skip comma, allow ",}" */
		break;
	    n = n->Nright = ndefl(N_IZLIST,	/* Parse element and add in */
					piztype(subt, 1));
	}
    }

    /* Now either fix up size of array, or check it against # elems we found */
    if (t->Tsize == 0)			/* If setting size, cnt always OK */
	root->Ntype = izsym->Stype = findctype(TS_ARRAY,
		t->Tflag,		/* Keep same flags */
		cnt, subt);
    else if (cnt > t->Tsize) {		/* Complain if too many */
	if (gotstr) {
	    if (cnt-1 > t->Tsize)	/* For string, permit NUL omission */
		error("String exceeds char array bounds");
	} else error("Too many elements in array initializer list");
    }

    /* Type checked out, do final checking for valid terminator and return */
    if (braces) {				/* If brace-enclosed, */
	if (token == T_RBRACE) nextoken();	/* must end in right brace */
	else {
	    error("Bad initializer list syntax");
	    pizflush(1);			/* Flush inner list */
	    if (token == T_RBRACE)
		nextoken();
	}
    } else if ((!gotstr || lev) && token != T_COMMA && token != T_RBRACE)
	error("Bad initializer list syntax");

    return root;			/* Done, return N_IZLIST! */
}
/* PEXIZER - Parse single initializer expression.
**	Should be only one expression; outermost braces are allowed.
**	Never returns a list; complains and fixes up as necessary.
*/
static NODE *
pexizer(lev)
int lev;			/* 0 if outermost level */
{
    NODE *n;
    if (token != T_LBRACE)
	return (optpar || !izautof || lev)	/* If want or need constant */
		?  evalexpr(asgnexpr())		/* evaluate parse result */
		: asgnexpr();

    /* We have a list of some sort.  This is only legal if at outermost
    ** level AND there is only one thing in the list.
    */
    if (lev)
	error("Inner initializer for this object cannot be a list");
    n = pizlist();		/* Parse the list, always returns N_IZLIST */
    if (n->Nright && lev == 0)	/* More than one thing in list? */
				/* Complain unless already complained above */
	error("Initializer cannot be a list of more than one element");
    if ((n = n->Nleft) == NULL) {
	error("Null initializer");
	return NULL;
    }
    if (n->Nop == N_IZLIST) {	/* Sublist? */
	error("Initializer cannot have sublist");
	while (n->Nop == N_IZLIST && (n = n->Nleft));
    }	
    return (optpar || !izautof || lev)	/* If want or need constant */
		?  evalexpr(n)		/* evaluate parse result */
		: n;
}

/* PIZLIST - Parse initializer list
**	Mainly for error recovery, when contents aren't analyzed closely.
*/
static NODE *
pizlist()
{
    NODE *n, *root;

    if (token != T_LBRACE)	/* If not a list, return simple izer */
	return asgnexpr();	/* Don't need to waste time optimizing */

    nextoken();				/* Skip open brace */
    root = n = ndefl(N_IZLIST, pizlist());
    while (token == T_COMMA) {
	nextoken();			/* Skip comma */
	if (token == T_RBRACE) break;	/* allow comma at end of initializer */
	n = n->Nright = ndefl(N_IZLIST, pizlist());
    }
    expect(T_RBRACE);			/* finish with close brace */
    return root;			/* return whole thing */
}

/* PIZFLUSH - Flush initializer tokens
**	Used during error recovery.  Type says what situation we're in.
**	0 - at start of izer, flush entire izer.
**		(stop if top-level comma, gobble end close-brace if one seen)
**		This also works to flush only a single list, if at its start.
**	1 - inside a list, flush to higher-level close-brace and don't
**		gobble it, so caller can handle close-brace.
*/
static void
pizflush(typ)
int typ;
{
    int lev = 0;
    if (typ) lev = 1;		/* Pretend already in a list */
    for(;; nextoken()) switch(token) {
	case T_COMMA:
	    if (typ == 0 && lev <= 0) return;
	    break;
	case T_LBRACE:		/* Start of list */
	    lev++;
	    break;
	case T_RBRACE:		/* End of list */
	    if (--lev <= 0) {
		if (typ == 0) nextoken();	/* Maybe gobble close-brace */
		return;
	    }
	    break;

	case T_EOF:		/* Input EOF */
	case T_SCOLON:		/* End of declaration statement */
	    return;
    }
}
/* NISCONST - Returns true if expression is an allowable initializer constant.
**	Return value indicates something about the type of constant:
**	0 - not a constant
**	1 - definitely a constant (arithmetic, or a cast pointer)
**	2 - address of some kind
**	3 - function address (cannot add or subtract from this)
*/
static int
nisconst(e)
NODE *e;
{
    switch(e->Nop) {
	case N_CAST:		/* Assume generator will be able to handle */
	    return nisconst(e->Nleft);

	case N_ICONST:
	case N_FCONST:
	case N_PCONST:
	    return 1;		/* Simple constant */
	case N_SCONST:
	    return 2;		/* Address */

	case Q_IDENT:
		/* Identifier.  See documentation for Q_IDENT in INTERN.DOC
		** for explanation of this method of testing.
		*/
	    switch (e->Nid->Stype->Tspec) {
		case TS_FUNCT:	return 3;	/* Function address */
		case TS_ARRAY:	return 2;	/* Array address */
	    }
	    break;

	case N_ADDR:
	    switch (e->Nleft->Nop) {
		case N_PTR:
		    return nisconst(e->Nleft->Nleft);

#if 0
		/* Allow for conversion of arrays generated by subscripting */
		case Q_PLUS:
		    if (e->Nleft->Ntype->Tspec == TS_ARRAY)
			return nisconst(e->Nleft);	/* OK, continue */
		    break;				/* Not array, fail */
#endif
		/* Structure hair.
		** For MEMBER (->) the Nleft must be a constant address.
		**	Can just apply nisconst to this.
		** For DOT (.) the Nleft can be anything that evaluates into
		**	a static structure.  We assume this is only possible
		**	with either Q_IDENT, or N_PTR of a struct addr.
		*/
		case Q_DOT:
		    if (tisbitf(e->Nleft->Ntype))	/* No bitfield ptrs */
			return 0;
		    switch (e->Nleft->Nleft->Nop) {
			case Q_IDENT:
			    switch (e->Nleft->Nleft->Nid->Sclass) {
				case SC_XEXTREF: case SC_EXLINK:
				case SC_EXTDEF: case SC_EXTREF:
				case SC_INTDEF: case SC_INTREF:
				case SC_INLINK: case SC_ISTATIC:
				    return 2;	/* Good address of object */
			    }
			    break;
			case N_PTR:
			    if (nisconst(e->Nleft->Nleft->Nleft)==2)
				return 2;
			    break;
		    }
		    break;			/* Otherwise fail. */

		case Q_MEMBER:
		    if (!tisbitf(e->Nleft->Ntype)	/* No bitfield ptrs */
		      && nisconst(e->Nleft->Nleft)==2)	/* If struct addr is */
			return 2;		/*  OK, then we're OK */
		    break;			/* Otherwise fail. */

		case Q_IDENT:	/* Addr OK if of external or static */
			/* Needn't test type since parser checks it while
			** parsing "&" to verify not function or array.
			*/
		    switch (e->Nleft->Nid->Sclass) {
			case SC_XEXTREF: case SC_EXLINK:
			case SC_EXTDEF: case SC_EXTREF:
			case SC_INTDEF: case SC_INTREF:
			case SC_INLINK: case SC_ISTATIC:
			    return 2;		/* Good address of object */
		    }
		    break;
	    }
	    break;

	/* Non-atomic expression checks, for plus and minus. */
	case Q_PLUS:
	    if (e->Nleft->Nop == N_ICONST	/* Integ constant */
		&& nisconst(e->Nright) == 2)	/* plus address */
		    return 2;
	    /* Fall through into Q_MINUS code */
	case Q_MINUS:
	    if (nisconst(e->Nleft) == 2		/* Address */
	      && e->Nright->Nop == N_ICONST)	/* plus/minus integ constant */
		return 2;
	    break;
    }
    return 0;
}

#if 0
/* CHKPTRCON - utility routine to check out a pointer initializer.
**	Allowed initializers (per CARM 4.6.3) must be an integer or
**	a static/external address plus/minus an integer.
*/
static NODE *
chkptrcon(e)
NODE *e;
{
    NODE *n, *addr, *con;

    n = (e->Nop == N_CAST) ? e->Nleft : e;	/* Allow casts */
    if (n->Nop == N_ICONST) return e;	/* Integer const OK (case 1,6) */
    if (n->Nop == Q_IDENT) return e;	/* Funct or array name OK (case 2,3) */
					/* (type cked by convasgn) */

    if (n->Nop == Q_PLUS || n->Nop == Q_MINUS) {
	addr = n->Nleft;
	con = n->Nright;
	if (n->Nop == Q_PLUS && con->Nop != N_ICONST)
	    addr = n->Nright, con = n->Nleft;
	if (con->Nop != N_ICONST) {
	    error("Pointer initializer constant has bad expression");
	    return NULL;
	}
    } else addr = n;

    /* Check out address */
    n = (addr->Nop == N_CAST) ? addr->Nleft : addr;	/* Allow casts */
    if (n->Nop == N_ICONST) return e;	/* Integer const OK (case 6) */
    if (n->Nop == N_SCONST) return e;	/* String const OK (case 7) */
    if (n->Nop == N_ADDR) {		/* Addr of something (case 4) */
	addr = n->Nleft;
	if (addr->Nop != Q_IDENT
	  || isauto(addr->Nid)) {
	    error("Constant pointer initializer has non-constant addr");
	    return NULL;
	}

    }
    return e;
}
#endif /* commented-out code */

/* ISAUTO - Returns true if symbol has automatic extent
*/
static int
isauto(s)
SYMBOL *s;
{
    switch(s->Sclass) {
	case SC_ARG:  case SC_RARG:
	case SC_AUTO: case SC_RAUTO:
		return 1;
	default:
		return 0;
    }
}
/* TYPENAME - Parse type name for cast and sizeof expressions.
**	[dpANS 3.5.5]
**
**	<type-name> ::= <spec-qual-list> {abstract-declor}
**	
** This parses a "type name" built from an abstract declarator.
** Storage-class specifiers and identifiers are illegal.
*/

TYPE *
typename()
{
    SYMBOL s, *t;

    pbase(&s);			/* Parse base */
    if (s.Sclass != SC_UNDEF) {
	error("Storage class not allowed for type-name");
	s.Sclass = SC_UNDEF;
    }
    if (s.Stype == NULL) {
	error("No type-specifier for type-name, assuming int");
	s.Stype = deftype;
    }
    if ((t = declarator(&s)) != NULL) {
	error("Identifier not allowed in type-name");
	if (t->Sclass == SC_UNDEF)
	    freesym(t);			/* Clean up */
    }
    return s.Stype;
}
/* Miscellaneous auxiliaries */

static void
errtwotyp(d, s)
SYMBOL *d, *s;		/* New decl sym and existing sym */
{
    error("Symbol %S previously declared with different type", s);
}

static void
errdupsym(s)
SYMBOL *s;
{
    error("Symbol %S previously defined", s);
}