4.4BSD/usr/src/old/pcc/lint/lpass1/lint.c

Compare this file to the similar file:
Show the results in this format:

#ifndef lint
static char sccsid[] = "@(#)lint.c	1.14	(Berkeley)	12/11/87";
#endif lint

# include "pass1.h"

# include "lmanifest.h"

# include <ctype.h>

# define VAL 0
# define EFF 1

/* these are appropriate for the -p flag */
int  SZCHAR = 8;
int  SZINT = 16;
int  SZFLOAT = 32;
int  SZDOUBLE = 64;
int  SZLONG = 32;
int  SZSHORT = 16;
int SZPOINT = 16;
int ALCHAR = 8;
int ALINT = 16;
int ALFLOAT = 32;
int ALDOUBLE = 64;
int ALLONG = 32;
int ALSHORT = 16;
int ALPOINT = 16;
int ALSTRUCT = 16;

int nflag = 0;		/* avoid gripes about printf et al. */
int vflag = 1;		/* tell about unused argments */
int xflag = 0;		/* tell about unused externals */
int argflag = 0;	/* used to turn off complaints about arguments */
int libflag = 0;	/* used to generate library descriptions */
int vaflag = -1;	/* signal functions with a variable number of args */
int aflag = 0;		/* used to check precision of assignments */
int zflag = 0;		/* no 'structure never defined' error */
int Cflag = 0;	/* filter out certain output, for generating libraries */
char *libname = 0;	/* name of the library we're generating */

			/* flags for the "outdef" function */
# define USUAL (-101)
# define DECTY (-102)
# define NOFILE (-103)
# define SVLINE (-104)

# define LNAMES 250

struct lnm {
	short lid, flgs;
	}  lnames[LNAMES], *lnp;

contx( p, down, pl, pr ) register NODE *p; register *pl, *pr; {

	*pl = *pr = VAL;
	switch( p->in.op ){

	case ANDAND:
	case OROR:
	case QUEST:
		*pr = down;
		break;

	case SCONV:
	case PCONV:
	case COLON:
		*pr = *pl = down;
		break;

	case COMOP:
		*pl = EFF;
		*pr = down;

	case FORCE:
	case INIT:
	case UNARY CALL:
	case STCALL:
	case UNARY STCALL:
	case CALL:
	case UNARY FORTCALL:
	case FORTCALL:
	case CBRANCH:
		break;

	default:
		if( asgop(p->in.op) ) break;
		if( p->in.op == UNARY MUL && ( p->in.type == STRTY || p->in.type == UNIONTY || p->in.type == UNDEF) ) {
		/* struct x f( );  main( ) {  (void) f( ); }
		 * the the cast call appears as U* UNDEF
		 */
			break;  /* the compiler does this... */
			}
		if( down == EFF && hflag ) werror( "null effect" );

		}
	}

ecode( p ) NODE *p; {
	/* compile code for p */

	fwalk( p, contx, EFF );
	lnp = lnames;
	lprt( p, EFF, 0 );
	strforget();
	}

ejobcode( flag ){
	/* called after processing each job */
	/* flag is nonzero if errors were detected */
	register k;
	register struct symtab *p;

	for( p=stab; p< &stab[SYMTSZ]; ++p ){

		if( p->stype != TNULL ) {

			if( p->stype == STRTY || p->stype == UNIONTY ){
				if( !zflag && dimtab[p->sizoff+1] < 0 ){
					/* never defined */
#ifndef FLEXNAMES
					if( hflag ) werror( "struct/union %.8s never defined", p->sname );
#else
					if( hflag ) werror( "struct/union %s never defined", p->sname );
#endif
					}
				}

			switch( p->sclass ){
			
			case STATIC:
				if( p->suse > 0 ){
					k = lineno;
					lineno = p->suse;
#ifndef FLEXNAMES
					uerror( "static variable %.8s unused",
#else
					uerror( "static variable %s unused",
#endif
						p->sname );
					lineno = k;
					break;
					}
				/* no statics in libraries */
				if( Cflag ) break;

			case EXTERN:
			case USTATIC:
				/* with the xflag, worry about externs not used */
				/* the filename may be wrong here... */
				if( xflag && p->suse >= 0 && !libflag ){
					outdef( p, LDX, NOFILE );
					}
			
			case EXTDEF:
				if( p->suse < 0 ){  /* used */
					outdef( p, LUM, SVLINE );
					}
				break;
				}
			
			}

		}
	exit( 0 );
	}

astype( t, i ) ATYPE *t; {
	TWORD tt;
	int j, k=0, l=0;

	if( (tt=BTYPE(t->aty))==STRTY || tt==UNIONTY ){
		if( i<0 || i>= DIMTABSZ-3 ){
			werror( "lint's little mind is blown" );
			}
		else {
			j = dimtab[i+3];
			if( j<0 || j>SYMTSZ ){
				k = dimtab[i];
				l = X_NONAME | stab[j].suse;
				}
			else {
				if( stab[j].suse <= 0 ) {
#ifndef FLEXNAMES
					werror( "no line number for %.8s",
#else
					werror( "no line number for %s",
#endif
						stab[j].sname );
					}
				else {
					k = dimtab[i];
#ifdef FLEXNAMES
					l = hashstr(stab[j].sname);
#else
					l = hashstr(stab[j].sname, LCHNM);
#endif
					}
				}
			}
		
		t->extra = k;
		t->extra1 = l;
		return( 1 );
		}
	else return( 0 );
	}

bfcode( a, n ) int a[]; {
	/* code for the beginning of a function; a is an array of
		indices in stab for the arguments; n is the number */
	/* this must also set retlab */
	register i;
	register struct symtab *cfp;
	static ATYPE t;

	strforget();
	retlab = 1;

	cfp = &stab[curftn];

	/* if creating library, don't do static functions */
	if( Cflag && cfp->sclass == STATIC ) return;

	/* if variable number of arguments, only print the ones which will be checked */
	if( vaflag >= 0 ){
		if( n < vaflag ) werror( "declare the VARARGS arguments you want checked!" );
		else n = vaflag;
		}
	fsave( ftitle );
	if( cfp->sclass == STATIC ) outdef( cfp, LST, vaflag>=0?~n:n );
	else outdef( cfp, libflag?LIB:LDI, vaflag>=0?~n:n );
	vaflag = -1;

	/* output the arguments */
	if( n ){
		for( i=0; i<n; ++i ) {
			t.aty = stab[a[i]].stype;
			t.extra = 0;
			t.extra1 = 0;
			if( !astype( &t, stab[a[i]].sizoff ) ) {
				switch( t.aty ){

				case ULONG:
					break;

				case CHAR:
				case SHORT:
					t.aty = INT;
					break;

				case UCHAR:
				case USHORT:
				case UNSIGNED:
					t.aty = UNSIGNED;
					break;

					}
				}
			fwrite( (char *)&t, sizeof(ATYPE), 1, stdout );
			}
		}
	}

ctargs( p ) NODE *p; {
	/* count arguments; p points to at least one */
	/* the arguemnts are a tower of commas to the left */
	register c;
	c = 1; /* count the rhs */
	while( p->in.op == CM ){
		++c;
		p = p->in.left;
		}
	return( c );
	}

lpta( p ) NODE *p; {
	static ATYPE t;

	if( p->in.op == CM ){
		lpta( p->in.left );
		p = p->in.right;
		}

	t.aty = p->in.type;
	t.extra = (p->in.op==ICON);
	t.extra1 = 0;

	if( !astype( &t, p->fn.csiz ) ) {
		switch( t.aty ){

			case CHAR:
			case SHORT:
				t.aty = INT;
			case LONG:
			case ULONG:
			case INT:
			case UNSIGNED:
				break;

			case UCHAR:
			case USHORT:
				t.aty = UNSIGNED;
				break;

			case FLOAT:
				t.aty = DOUBLE;
				t.extra = 0;
				break;

			default:
				t.extra = 0;
				break;
			}
		}
	fwrite( (char *)&t, sizeof(ATYPE), 1, stdout );
	}

# define VALSET 1
# define VALUSED 2
# define VALASGOP 4
# define VALADDR 8

lprt( p, down, uses ) register NODE *p; {
	register struct symtab *q;
	register id;
	register acount;
	register down1, down2;
	register use1, use2;
	register struct lnm *np1, *np2;

	/* first, set variables which are set... */

	use1 = use2 = VALUSED;
	if( p->in.op == ASSIGN ) use1 = VALSET;
	else if( p->in.op == UNARY AND ) use1 = VALADDR;
	else if( asgop( p->in.op ) ){ /* =ops */
		use1 = VALUSED|VALSET;
		if( down == EFF ) use1 |= VALASGOP;
		}


	/* print the lines for lint */

	down2 = down1 = VAL;
	acount = 0;

	switch( p->in.op ){

	case EQ:
	case NE:
	case GT:
	case GE:
	case LT:
	case LE:
		if( p->in.left->in.type == CHAR && p->in.right->in.op==ICON && p->in.right->tn.lval < 0 ){
			werror( "nonportable character comparison" );
			}
		if( (p->in.op==EQ || p->in.op==NE ) && ISUNSIGNED(p->in.left->in.type) && p->in.right->in.op == ICON ){
			if( p->in.right->tn.lval < 0 && p->in.right->tn.rval == NONAME && !ISUNSIGNED(p->in.right->in.type) ){
				werror( "comparison of unsigned with negative constant" );
				}
			}
		break;

	case UGE:
	case ULT:
		if( p->in.right->in.op == ICON && p->in.right->tn.lval == 0 && p->in.right->tn.rval == NONAME ){
			werror( "unsigned comparison with 0?" );
			break;
			}
	case UGT:
	case ULE:
		if( p->in.right->in.op == ICON && p->in.right->tn.lval <= 0 && !ISUNSIGNED(p->in.right->in.type) && p->in.right->tn.rval == NONAME ){
			werror( "degenerate unsigned comparison" );
			}
		break;

	case COMOP:
		down1 = EFF;

	case ANDAND:
	case OROR:
	case QUEST:
		down2 = down;
		/* go recursively left, then right  */
		np1 = lnp;
		lprt( p->in.left, down1, use1 );
		np2 = lnp;
		lprt( p->in.right, down2, use2 );
		lmerge( np1, np2, 0 );
		return;

	case SCONV:
	case PCONV:
	case COLON:
		down1 = down2 = down;
		break;

	case CALL:
	case STCALL:
	case FORTCALL:
		acount = ctargs( p->in.right );
	case UNARY CALL:
	case UNARY STCALL:
	case UNARY FORTCALL:
		if( p->in.left->in.op == ICON && (id=p->in.left->tn.rval) != NONAME ){ /* used to be &name */
			struct symtab *sp = &stab[id];
			int lty;

			fsave( ftitle );
			if (!nflag)
				doform(p, sp, acount);
			/*
			 * if we're generating a library -C then
			 * we don't want to output references to functions
			 */
			if( Cflag ) break;
			/*  if a function used in an effects context is
			 *  cast to type  void  then consider its value
			 *  to have been disposed of properly
			 *  thus a call of type  undef  in an effects
			 *  context is construed to be used in a value
			 *  context
			 */
			if ((down == EFF) && (p->in.type != UNDEF)) {
				lty = LUE;
			} else if (down == EFF) {
				lty = LUV | LUE;
			} else {
				lty = LUV;
			}
			outdef( sp, lty, acount );
			if( acount ) {
				lpta( p->in.right );
				}
			}
		break;

	case ICON:
		/* look for &name case */
		if( (id = p->tn.rval) >= 0 && id != NONAME ){
			q = &stab[id];
			q->sflags |= (SREF|SSET);
			q->suse = -lineno;
			}
		return;

	case NAME:
		if( (id = p->tn.rval) >= 0 && id != NONAME ){
			q = &stab[id];
			if( (uses&VALUSED) && !(q->sflags&SSET) ){
				if( q->sclass == AUTO || q->sclass == REGISTER ){
					if( !ISARY(q->stype ) && !ISFTN(q->stype) && q->stype!=STRTY && q->stype!=UNIONTY ){
#ifndef FLEXNAMES
						werror( "%.8s may be used before set", q->sname );
#else
						werror( "%s may be used before set", q->sname );
#endif
						q->sflags |= SSET;
						}
					}
				}
			if( uses & VALASGOP ) break;  /* not a real use */
			if( uses & VALSET ) q->sflags |= SSET;
			if( uses & VALUSED ) q->sflags |= SREF;
			if( uses & VALADDR ) q->sflags |= (SREF|SSET);
			if( p->tn.lval == 0 ){
				lnp->lid = id;
				lnp->flgs = (uses&VALADDR)?0:((uses&VALSET)?VALSET:VALUSED);
				if( ++lnp >= &lnames[LNAMES] ) --lnp;
				}
			}
		return;

		}

	/* recurse, going down the right side first if we can */

	switch( optype(p->in.op) ){

	case BITYPE:
		np1 = lnp;
		lprt( p->in.right, down2, use2 );
	case UTYPE:
		np2 = lnp;
		lprt( p->in.left, down1, use1 );
		}

	if( optype(p->in.op) == BITYPE ){
		if( p->in.op == ASSIGN && p->in.left->in.op == NAME ){ /* special case for a =  .. a .. */
			lmerge( np1, np2, 0 );
			}
		else lmerge( np1, np2, p->in.op != COLON );
		/* look for assignments to fields, and complain */
		if( p->in.op == ASSIGN && p->in.left->in.op == FLD && p->in.right->in.op == ICON ) fldcon( p );
		}

	}

lmerge( np1, np2, flag ) struct lnm *np1, *np2; {
	/* np1 and np2 point to lists of lnm members, for the two sides
	 * of a binary operator
	 * flag is 1 if commutation is possible, 0 otherwise
	 * lmerge returns a merged list, starting at np1, resetting lnp
	 * it also complains, if appropriate, about side effects
	 */

	register struct lnm *npx, *npy;

	for( npx = np2; npx < lnp; ++npx ){

		/* is it already there? */
		for( npy = np1; npy < np2; ++npy ){
			if( npx->lid == npy->lid ){ /* yes */
				if( npx->flgs == 0 || npx->flgs == (VALSET|VALUSED) )
					;  /* do nothing */
				else if( (npx->flgs|npy->flgs)== (VALSET|VALUSED) ||
					(npx->flgs&npy->flgs&VALSET) ){
#ifndef FLEXNAMES
					if( flag ) werror( "%.8s evaluation order undefined", stab[npy->lid].sname );
#else
					if( flag ) werror( "%s evaluation order undefined", stab[npy->lid].sname );
#endif
					}
				if( npy->flgs == 0 ) npx->flgs = 0;
				else npy->flgs |= npx->flgs;
				goto foundit;
				}
			}

		/* not there: update entry */
		np2->lid = npx->lid;
		np2->flgs = npx->flgs;
		++np2;

		foundit: ;
		}

	/* all finished: merged list is at np1 */
	lnp = np2;
	}

efcode(){
	/* code for the end of a function */
	register struct symtab *cfp;

	cfp = &stab[curftn];
	if( retstat & RETVAL && !(Cflag && cfp->sclass==STATIC) )
		outdef( cfp, LRV, DECTY );
	if( !vflag ){
		vflag = argflag;
		argflag = 0;
		}
	if( retstat == RETVAL+NRETVAL )
#ifndef FLEXNAMES
		werror( "function %.8s has return(e); and return;", cfp->sname);
#else
		werror( "function %s has return(e); and return;", cfp->sname);
#endif
	}

aocode(p) struct symtab *p; {
	/* called when automatic p removed from stab */
	register struct symtab *cfs;
	cfs = &stab[curftn];
	if(p->suse>0 && !(p->sflags&(SMOS|STAG)) ){
		if( p->sclass == PARAM ){
#ifndef FLEXNAMES
			if( vflag ) werror( "argument %.8s unused in function %.8s",
#else
			if( vflag ) werror( "argument %s unused in function %s",
#endif
				p->sname,
				cfs->sname );
			}
		else {
#ifndef FLEXNAMES
			if( p->sclass != TYPEDEF ) werror( "%.8s unused in function %.8s",
#else
			if( p->sclass != TYPEDEF ) werror( "%s unused in function %s",
#endif
				p->sname, cfs->sname );
			}
		}

	if( p->suse < 0 && (p->sflags & (SSET|SREF|SMOS)) == SSET &&
		!ISARY(p->stype) && !ISFTN(p->stype) ){

#ifndef FLEXNAMES
		werror( "%.8s set but not used in function %.8s", p->sname, cfs->sname );
#else
		werror( "%s set but not used in function %s", p->sname, cfs->sname );
#endif
		}

	if( p->stype == STRTY || p->stype == UNIONTY || p->stype == ENUMTY ){
		if( !zflag && dimtab[p->sizoff+1] < 0 )
#ifndef FLEXNAMES
			werror( "structure %.8s never defined", p->sname );
#else
			werror( "structure %s never defined", p->sname );
#endif
		}

	}

defnam( p ) register struct symtab *p; {
	/* define the current location as the name p->sname */

	if( p->sclass == STATIC && (p->slevel>1 || Cflag) ) return;

	if( !ISFTN( p->stype ) )
		if( p->sclass == STATIC ) outdef( p, LST, USUAL );
		else outdef( p, libflag?LIB:LDI, USUAL );
	}

zecode( n ){
	/* n integer words of zeros */
	OFFSZ temp;
	temp = n;
	inoff += temp*SZINT;
	;
	}

andable( p ) NODE *p; {  /* p is a NAME node; can it accept & ? */
	register r;

	if( p->in.op != NAME ) cerror( "andable error" );

	if( (r = p->tn.rval) < 0 ) return(1);  /* labels are andable */

	if( stab[r].sclass == AUTO || stab[r].sclass == PARAM ) return(0); 
#ifndef FLEXNAMES
	if( stab[r].sclass == REGISTER ) uerror( "can't take & of %.8s", stab[r].sname );
#else
	if( stab[r].sclass == REGISTER ) uerror( "can't take & of %s", stab[r].sname );
#endif
	return(1);
	}

NODE *
clocal(p) NODE *p; {

	/* this is called to do local transformations on
	   an expression tree preparitory to its being
	   written out in intermediate code.
	*/

	/* the major essential job is rewriting the
	   automatic variables and arguments in terms of
	   REG and OREG nodes */
	/* conversion ops which are not necessary are also clobbered here */
	/* in addition, any special features (such as rewriting
	   exclusive or) are easily handled here as well */

	register o;
	register unsigned t, tl;
	int s;

	switch( o = p->in.op ){
	case NAME:
		{
			extern int	didstr, subscr;
			extern NODE *	strnodes[];

			if (didstr) {
				didstr = 0;
				strnodes[subscr] = p;
			}
		}
		break;

	case SCONV:
	case PCONV:
		if( p->in.left->in.type==ENUMTY ){
			p->in.left = pconvert( p->in.left );
			}
		/* assume conversion takes place; type is inherited */
		t = p->in.type;
		tl = p->in.left->in.type;
		if( aflag && (tl==LONG||tl==ULONG) && (t!=LONG&&t!=ULONG&&t!=UNDEF) ){
			werror( "long assignment may lose accuracy" );
			}
		if( aflag>=2 && (tl!=LONG&&tl!=ULONG) && (t==LONG||t==ULONG) && p->in.left->in.op != ICON ){
			werror( "assignment to long may sign-extend incorrectly" );
			}
		if( ISPTR(tl) && ISPTR(t) ){
			tl = DECREF(tl);
			t = DECREF(t);
			switch( ISFTN(t) + ISFTN(tl) ){

			case 0:  /* neither is a function pointer */
				if( talign(t,p->fn.csiz) > talign(tl,p->in.left->fn.csiz) ){
					if( hflag||pflag ) werror( "possible pointer alignment problem" );
					}
				break;

			case 1:
				werror( "questionable conversion of function pointer" );

			case 2:
				;
				}
			}
		p->in.left->in.type = p->in.type;
		p->in.left->fn.cdim = p->fn.cdim;
		p->in.left->fn.csiz = p->fn.csiz;
		p->in.op = FREE;
		return( p->in.left );

	case PVCONV:
	case PMCONV:
		if( p->in.right->in.op != ICON ) cerror( "bad conversion");
		p->in.op = FREE;
		return( buildtree( o==PMCONV?MUL:DIV, p->in.left, p->in.right ) );

	case RS:
	case LS:
	case ASG RS:
	case ASG LS:
		if( p->in.right->in.op != ICON )
			break;
		s = p->in.right->tn.lval;
		if( s < 0 )
			werror( "negative shift" );
		else
		if( s >= dimtab[ p->fn.csiz ] )
			werror( "shift greater than size of object" );
		break;

		}

	return(p);
	}

NODE *
offcon( off, t, d, s ) OFFSZ off; TWORD t;{  /* make a structure offset node */
	register NODE *p;
	p = bcon(0);
	p->tn.lval = off/SZCHAR;
	return(p);
	}

noinit(){
	/* storage class for such as "int a;" */
	return( pflag ? EXTDEF : EXTERN );
	}


cinit( p, sz ) NODE *p; { /* initialize p into size sz */
	register int id;

	inoff += sz;
	if( p->in.op == INIT ){
		if( p->in.left->in.op == ICON ) return;
		if( p->in.left->in.op == NAME && p->in.left->in.type == MOE ) return;
		}
	uerror( "illegal initialization" );
	}

char *
exname( p ) char *p; {
	/* make a name look like an external name in the local machine */
	static char aa[8];
	register int i;

	if( !pflag ) return(p);
	for( i=0; i<6; ++i ){
		if( isupper(*p ) ) aa[i] = tolower( *p );
		else aa[i] = *p;
		if( *p ) ++p;
		}
	aa[6] = '\0';
	return( aa );
	}

char *
strip(s) char *s; {
#ifndef FLEXNAMES
	static char x[LFNM+1];
#else
	static char x[BUFSIZ];
#endif
	register char *p;
	static	int	stripping = 0;

	if (stripping)
		return(s);
	stripping++;
	for( p=x; *s; ++s ){
		if( *s != '"' ){
#ifndef FLEXNAMES
/* PATCHED by ROBERT HENRY on 8Jul80 to fix 14 character file name bug */
			if( p >= &x[LFNM] )
#else
			if( p >= &x[BUFSIZ] )
#endif
				cerror( "filename too long" );
			*p++ = *s;
		}
	}
	stripping = 0;
	*p = '\0';
#ifndef FLEXNAMES
	return( x );
#else
	return( hash(x) );
#endif
	}

fsave( s ) char *s; {
	static union rec fsname;
	s = strip( s );
#ifndef FLEXNAMES
	if( strncmp( s, fsname.f.fn, LFNM ) )
#else
	if (fsname.f.fn == NULL || strcmp(s, fsname.f.fn))
#endif
		{
		/* new one */
#ifndef FLEXNAMES
		strncpy( fsname.f.fn, s, LFNM );
#else
		fsname.f.fn = s;
#endif
		fsname.f.decflag = LFN;
		fwrite( (char *)&fsname, sizeof(fsname), 1, stdout );
#ifdef FLEXNAMES
		/* if generating a library, prefix with the library name */
		/* only do this for flexnames */
		if( libname ){
			fwrite( libname, strlen(libname), 1, stdout );
			putchar( ':' );
			}
		fwrite( fsname.f.fn, strlen(fsname.f.fn)+1, 1, stdout );
#endif
		}
	}

where(f){ /* print true location of error */
	if( f == 'u' && nerrors > 1 )
		--nerrors; /* don't get "too many errors" */
	fprintf( stderr, "%s(%d): ", strip(ftitle), lineno);
	}

	/* a number of dummy routines, unneeded by lint */

branch(n){;}
defalign(n){;}
deflab(n){;}

extern char *	strchr();

#define SBUFSIZE	16
#define SCLICK		80

#ifndef size_t
#define size_t	unsigned
#endif /* !size_t */

static char *	strings[SBUFSIZE];
static NODE *	strnodes[SBUFSIZE];
static int	didstr;
static int	subscr;
static int	strapped;

bycode(t, i)
{
	extern char *	calloc();
	extern char *	realloc();

	if (nflag || strapped)
		return;
	if (i == 0)
		if (subscr < (SBUFSIZE - 1))
			++subscr;
	if (subscr >= SBUFSIZE)
		return;
	didstr = 1;
	if ((i % SCLICK) == 0) {
		strings[subscr] = (strings[subscr] == NULL) ?
			calloc((size_t) (SCLICK + 1), 1) :
			realloc(strings[subscr], (size_t) (i + SCLICK + 1));
		if (strings[subscr] == NULL) {
			strapped = 1;
			return;
		}
	}
	strings[subscr][i] = t;
}

strforget()
{
	didstr = subscr = 0;
}

static char *
typestr(t)
{
	switch (t) {
		case CHAR:		return "char";
		case UCHAR:		return "unsigned char";
		case SHORT:		return "short";
		case USHORT:		return "unsigned short";
		case INT:		return "int";
		case UNSIGNED:		return "unsigned";
		case ENUMTY:		return "enum";
		case LONG:		return "long";
		case ULONG:		return "unsigned long";
		case FLOAT:		return "float";
		case DOUBLE:		return "double";
		case STRTY:		return "struct";
		case UNIONTY:		return "union";
		case PTR|CHAR:		return "char *";
		case PTR|UCHAR:		return "unsigned char *";
		case PTR|SHORT:		return "short *";
		case PTR|USHORT:	return "unsigned short *";
		case PTR|INT:		return "int *";
		case PTR|UNSIGNED:	return "unsigned *";
		case PTR|ENUMTY:	return "enum *";
		case PTR|LONG:		return "long *";
		case PTR|ULONG:		return "unsigned long *";
		case PTR|FLOAT:		return "float *";
		case PTR|DOUBLE:	return "double *";
		case PTR|STRTY:		return "struct *";
		case PTR|UNIONTY:	return "union *";
		default:		return ISPTR(t) ?
						"pointer" : "non-scalar";
	}
}

NODE *
ntharg(p, n, acount)
NODE *		p;
register int	n;
register int	acount;
{
	if (n > acount)
		return NULL;
	p = p->in.right;
	while (n != acount) {
		p = p->in.left;
		--acount;
	}
	return (n == 1) ? p : p->in.right;
}

struct entry {
	/* If argument to print/scan is of type... */	int	argtype;
	/* ...and this length character is used... */	char	lchar;
	/* ...and one of these is control char... */	char *	cchars;
	/* ...then use this format with werror... */	char *	werror;
	/* ...(where NULL means it's hunky dory)... */
};

/*
** Portable printf.
** H&S says "%o" takes an unsigned argument;
** X3J11 says "%o" takes an int argument;
** we'll allow either here.
*/

static struct entry pprintf[] = {
	CHAR,		'\0',	"c",		NULL, /* this is deliberate */
	INT,		'\0',	"cdoxX",	NULL,
	UNSIGNED,	'\0',	"uoxX",		NULL,
	CHAR,		'\0',	"cdoxX",	NULL,
	UCHAR,		'\0',	"udoxX",	NULL, /* yes, d is okay */
	SHORT,		'\0',	"cdoxX",	NULL,
	USHORT,		'\0',	"uoxX",		NULL,
	ENUMTY,		'\0',	"duoxX",	NULL,
	LONG,		'l',	"doxX",		NULL,
	ULONG,		'l',	"uoxX",		NULL,
	FLOAT,		'\0',	"eEfgG",	NULL,
	DOUBLE,		'\0',	"eEfgG",	NULL,
	PTR|CHAR,	'\0',	"s",		NULL,
	UNDEF,		'\0',	"",		NULL
};

/*
** Berkeley printf.
** It allows %D, %O, and %U, which we deprecate.
** Since
**	sizeof (char *) == sizeof (int) &&
**	sizeof (int) == sizeof (long) &&
**	sizeof (char *) == sizeof (int *)
** you can be lax--and we tolerate *some* laxness. 
** g/lax/p to find lax table entries and code.
*/

static char	uppercase[] = "deprecated upper-case control character (%c)";
#define lax	NULL

static struct entry bprintf[] = {
	CHAR,		'\0',	"c",		NULL,	/* this is deliberate */
	INT,		'\0',	"cdoxX",	NULL,
	INT,		'\0',	"DO",		uppercase,
	UNSIGNED,	'\0',	"uoxX",		NULL,
	UNSIGNED,	'\0',	"UO",		uppercase,
	CHAR,		'\0',	"cdoxX",	NULL,
	CHAR,		'\0',	"DO",		uppercase,
	UCHAR,		'\0',	"duoxX",	NULL,	/* yes, d is okay */
	UCHAR,		'\0',	"DUO",		uppercase,
	SHORT,		'\0',	"cdoxX",	NULL,
	SHORT,		'\0',	"DO",		uppercase,
	USHORT,		'\0',	"duoxX",	NULL,	/* d okay on BSD */
	USHORT,		'\0',	"DUO",		uppercase,
	ENUMTY,		'\0',	"duoxX",	NULL,
	ENUMTY,		'\0',	"DUO",		uppercase,
	LONG,		'\0',	"doxX",		lax,
	LONG,		'\0',	"DO",		uppercase,
	LONG,		'l',	"doxX",		NULL,
	INT,		'l',	"doxX",		lax,
	ULONG,		'\0',	"uoxX",		lax,
	ULONG,		'\0',	"UO",		uppercase,
	ULONG,		'l',	"uoxX",		NULL,
	UNSIGNED,	'l',	"uoxX",		lax,
	FLOAT,		'\0',	"eEfgG",	NULL,
	DOUBLE,		'\0',	"eEfgG",	NULL,
	PTR|CHAR,	'\0',	"s",		NULL,
	UNDEF,		'\0',	NULL,		NULL,
};

/*
** Portable scanf.  'l' and 'h' are universally ignored preceding 'c' and 's',
** and 'h' is universally ignored preceding 'e' and 'f',
** but you won't find such cruft here.
*/

static struct entry pscanf[] = {
	INT,		'\0',	"dox",	NULL,
	UNSIGNED,	'\0',	"uox",	NULL,
	CHAR,		'\0',	"cs[",	NULL,
	SHORT,		'h',	"dox",	NULL,
	USHORT,		'h',	"uox",	NULL,
	LONG,		'l',	"dox",	NULL,
	ULONG,		'l',	"uox",	NULL,
	FLOAT,		'\0',	"ef",	NULL,	/* BSD doesn't handle g */
	DOUBLE,		'l',	"ef",	NULL,
	UNDEF,		'\0',	NULL,	NULL,
};

/*
** Berkeley scanf.  An upper case letter equals an l plus the lower case char,
** but this is deprecated.
** Even though sizeof (int) == sizeof (long), we'll be picky here.
*/

static struct entry bscanf[] = {
	INT,		'\0',	"dox",	NULL,
	UNSIGNED,	'\0',	"uox",	NULL,
	CHAR,		'\0',	"cs[",	NULL,
	SHORT,		'h',	"dox",	NULL,
	USHORT,		'h',	"uox",	NULL,
	LONG,		'\0',	"dox",	lax,
	LONG,		'\0',	"DOX",	uppercase,
	LONG,		'l',	"dox",	NULL,
	ULONG,		'\0',	"uox",	lax,
	ULONG,		'\0',	"UOX",	uppercase,
	ULONG,		'l',	"uox",	NULL,
	FLOAT,		'\0',	"ef",	NULL,
	DOUBLE,		'\0',	"EF",	uppercase,
	DOUBLE,		'l',	"ef",	NULL,
	UNDEF,		'\0',	NULL,	NULL,
};

static struct item {
	char *		name;		/* such as "printf" */
	int		isscan;		/* scanf/printf */
	int		fmtarg;		/* number of format argument */
	struct entry *	ptable;		/* portable checking table */
	struct entry *	btable;		/* berkeley checking table */
} items[] = {
	"printf",	0,	1,	pprintf,	bprintf,	
	"fprintf",	0,	2,	pprintf,	bprintf,
	"sprintf",	0,	2,	pprintf,	bprintf,
	"scanf",	1,	1,	pscanf,		bscanf,
	"fscanf",	1,	2,	pscanf,		bscanf,
	"sscanf",	1,	2,	pscanf,		bscanf,
	NULL,		-1,	-1,	NULL,		NULL
};

static char	pwf[]	= "possible wild format";
static char	pfacm[]	= "possible format/argument count mismatch";

static struct entry *
findlc(ep, lchar, cchar)
register struct entry *	ep;
register int		lchar;
register int		cchar;
{
	for ( ; ep->argtype != UNDEF; ++ep)
		if (ep->lchar == lchar && strchr(ep->cchars, cchar) != 0)
			return ep;
	return NULL;
}

static char *
subform(p, sp, acount)
register NODE *			p;
register struct symtab *	sp;
{
	register int		i, j, isscan;
	register NODE *		tp;
	register char *		cp;
	register struct entry *	basep;
	register struct entry *	ep;
	register struct item *	ip;
	register int		lchar;
	register int		cchar;
	register int		t;
	register int		suppressed;
	static char		errbuf[132];

	if (nflag || strapped)
		return NULL;
	cp = sp->sname;
	for (ip = items; ; ++ip)
		if (ip->name == NULL)
			return NULL;	/* not a print/scan function */
		else if (strcmp(ip->name, sp->sname) == 0)
			break;
	isscan = ip->isscan;
	i = ip->fmtarg;
	if (i > acount)
		return NULL;	/* handled in pass 2 */
	tp = ntharg(p, i, acount);
	if (tp->in.type != (PTR|CHAR))
		return NULL;	/* handled in pass 2 */
	if (tp->in.op != ICON || tp->tn.lval != 0)
		return NULL;	/* can't check it */
	for (j = 1; j <= subscr; ++j)
		if (tp == strnodes[j])
			break;
	if (j > subscr)
		return NULL;	/* oh well. . . */
	cp = strings[j];
	/*
	** cp now points to format string.
	*/
	/*
	** For now, ALWAYS use "portable" table, rather than doing this:
	**	basep = pflag ? ip->ptable : ip->btable;
	*/
	basep = ip->ptable;
	for ( ; ; ) {
		if (*cp == '\0')
			return (i == acount) ? NULL : pfacm;
		if (*cp++ != '%')
			continue;
		if (*cp == '\0')
			return "wild trailing %% in format";
		if (*cp == '%') {
			++cp;
			continue;
		}
		if (isscan) {
			suppressed = *cp == '*';
			if (suppressed)
				++cp;
			while (isdigit(*cp))
				++cp;
			if (!suppressed && ++i <= acount) {
				t = ntharg(p, i, acount)->in.type;
				if (!ISPTR(t)) {
(void) sprintf(errbuf,
	"%s argument is type (%s) rather than pointer (arg %d)",
	ip->name, typestr(t), i);
					return errbuf;
				}
				t = DECREF(t);
			}
		} else {
			int	nspace, ndash, nplus, nhash;

			suppressed = 0;
			nspace = ndash = nplus = nhash = 0;
			for ( ; ; ) {
				if (*cp == ' ')
					++nspace;
				else if (*cp == '+')
					++nplus;
				else if (*cp == '-')
					++ndash;
				else if (*cp == '#')
					++nhash;
				else	break;
				++cp;
			}
			if (nspace > 1 || ndash > 1 || nplus > 1 || nhash > 1)
				return "wild repeated flag character in format";
			if (*cp == '*') {
				++cp;
				if (++i > acount)
					break;
				t = ntharg(p, i, acount)->in.type;
				/*
				** Width other than INT or UNSIGNED is suspect.
				*/
				if (t != INT && t != UNSIGNED) {
(void) sprintf(errbuf,
	"field width argument is type (%s) rather than (int) (arg %d)",
	typestr(t), i);
					return errbuf;
				}
			} else while (isdigit(*cp))
				++cp;
			if (*cp == '.') {
				++cp;
				if (*cp == '*') {
					++cp;
					if (++i > acount)
						return pfacm;
					t = ntharg(p, i, acount)->in.type;
					if (t != INT && t != UNSIGNED) {
(void) sprintf(errbuf,
	"precision argument is type (%s) rather than (int) (arg %d)",
	typestr(t), i);
						return errbuf;
					}
				} else while (isdigit(*cp))
					++cp;
			}
			if (++i <= acount)
				t = ntharg(p, i, acount)->in.type;
		}
		if (*cp == 'h' || *cp == 'l')
			lchar = *cp++;
		else	lchar = '\0';
		if ((cchar = *cp++) == '\0')
			return pwf;
		if (i > acount)
			return (findlc(basep, lchar, cchar) == NULL) ?
				pwf : pfacm;
		if (!isscan && !pflag && ISPTR(t) &&
			strchr("douxX", cchar) != 0)
				continue;	/* lax--printf("%d", (int *)) */
		if (suppressed) {
			if (findlc(basep, lchar, cchar) == NULL)
				return pwf;
		} else for (ep = basep; ; ++ep) {
			if (ep->argtype == UNDEF) {	/* end of table */
				ep = findlc(basep, lchar, cchar);
				if (ep == NULL)
					return pwf;
(void) sprintf(errbuf, "%s: (%s) format, (%s) arg (arg %d)",
					ip->name,
					typestr(ep->argtype),
					typestr(isscan ? (t | PTR) : t), i);
				return errbuf;
			}
			if (ep->argtype == t && ep->lchar == lchar &&
				strchr(ep->cchars, cchar) != 0)
					if (ep->werror == 0)
						break;
					else {
						werror(ep->werror, cchar);
						return NULL;
					}
		}
		if (cchar != '[')
			continue;
		do {
			if (*cp == '\0')
				return "possible unmatched '[' in format";
		} while (*cp++ != ']');
	}
	/*NOTREACHED*/
}

doform(p, sp, acount)
NODE *		p;
struct symtab *	sp;
{
	char *	cp;

	if ((cp = subform(p, sp, acount)) != NULL)
		werror(cp);
}

cisreg(t) TWORD t; {return(1);}  /* everyting is a register variable! */

fldty(p) struct symtab *p; {
	; /* all types are OK here... */
	}

fldal(t) unsigned t; { /* field alignment... */
	if( t == ENUMTY ) return( ALCHAR );  /* this should be thought through better... */
	if( ISPTR(t) ){ /* really for the benefit of honeywell (and someday IBM) */
		if( pflag ) uerror( "nonportable field type" );
		}
	else uerror( "illegal field type" );
	return(ALINT);
	}

main(argc, argv)
	int	argc;
	char	**argv;
{
	extern char	*optarg;
	extern int	optind;
	int	ch;

	while ((ch = getopt(argc,argv,"C:D:I:U:LX:Pabchnpuvxz")) != EOF)
		switch((char)ch) {
			case 'C':
				Cflag = 1;
				libname = optarg;
				continue;
			case 'D':	/* #define */
			case 'I':	/* include path */
			case 'U':	/* #undef */
			case 'X':	/* debugging, done in first pass */
			case 'P':	/* debugging, done in second pass */
				break;
			case 'L':
				libflag = 1;
				/*FALLTHROUGH*/
			case 'v':	/* unused arguments in functions */
				vflag = 0;
				break;
			case 'a':	/* long to int assignment */
				++aflag;
				break;
			case 'b':	/* unreached break statements */
				brkflag = 1;
				break;
			case 'c':	/* questionable casts */
				cflag = 1;
				break;
			case 'h':	/* heuristics */
				hflag = 1;
				break;
			case 'n':	/* standard library check */
				nflag = 1;
				break;
			case 'p':	/* IBM & GCOS portability */
				pflag = 1;
				break;
			case 'u':	/* 2nd pass: undefined or unused */
				break;
			case 'x':	/* unused externs */
				xflag = 1;
				break;
			case 'z':	/* use of undefined structures */
				zflag = 1;
				break;
			case '?':
			default:
				fputs("usage: lint [-C lib] [-D def] [-I include] [-U undef] [-Labchnpuvx] file ...\n",stderr);
				exit(1);
		}

	if (!pflag) {		/* set sizes to sizes of target machine */
# ifdef gcos
		SZCHAR = ALCHAR = 9;
# else
		SZCHAR = ALCHAR = 8;
# endif
		SZINT = ALINT = sizeof(int)*SZCHAR;
		SZFLOAT = ALFLOAT = sizeof(float)*SZCHAR;
		SZDOUBLE = ALDOUBLE = sizeof(double)*SZCHAR;
		SZLONG = ALLONG = sizeof(long)*SZCHAR;
		SZSHORT = ALSHORT = sizeof(short)*SZCHAR;
		SZPOINT = ALPOINT = sizeof(int *)*SZCHAR;
		ALSTRUCT = ALINT;
		/* now, fix some things up for various machines (I wish we had "alignof") */

# ifdef pdp11
		ALLONG = ALDOUBLE = ALFLOAT = ALINT;
# endif
# ifdef ibm
		ALSTRUCT = ALCHAR;
# endif
	}
	return(mainp1(argc,argv));
}

ctype( type ) unsigned type; { /* are there any funny types? */
	return( type );
	}

commdec( i ){
	/* put out a common declaration */
	if( stab[i].sclass == STATIC ) outdef( &stab[i], LST, USUAL );
	else outdef( &stab[i], libflag?LIB:LDC, USUAL );
	}

isitfloat ( s ) char *s; {
	/* s is a character string;
	   if floating point is implemented, set dcon to the value of s */
	/* lint version
	*/
	dcon = atof( s );
	return( DCON );
	}

fldcon( p ) register NODE *p; {
	/* p is an assignment of a constant to a field */
	/* check to see if the assignment is going to overflow, or otherwise cause trouble */
	register s;
	CONSZ v;

	if( !hflag & !pflag ) return;

	s = UPKFSZ(p->in.left->tn.rval);
	v = p->in.right->tn.lval;

	switch( p->in.left->in.type ){

	case CHAR:
	case INT:
	case SHORT:
	case LONG:
	case ENUMTY:
		if( v>=0 && (v>>(s-1))==0 ) return;
		werror( "precision lost in assignment to (possibly sign-extended) field" );
	default:
		return;

	case UNSIGNED:
	case UCHAR:
	case USHORT:
	case ULONG:
		if( v<0 || (v>>s)!=0 ) werror( "precision lost in field assignment" );
		
		return;
		}

	}

outdef( p, lty, mode ) struct symtab *p; {
	/* output a definition for the second pass */
	/* if mode is > USUAL, it is the number of args */
	char *fname;
	TWORD t;
	int line;
	static union rec rc;

	if( mode == NOFILE ){
		fname = "???";
		line = p->suse;
		}
	else if( mode == SVLINE ){
		fname = ftitle;
		line = -p->suse;
		}
	else {
		fname = ftitle;
		line = lineno;
		}
	fsave( fname );
#ifndef FLEXNAMES
	strncpy( rc.l.name, exname(p->sname), LCHNM );
#endif
	rc.l.decflag = lty;
	t = p->stype;
	if( mode == DECTY ) t = DECREF(t);
	rc.l.type.aty = t;
	rc.l.type.extra = 0;
	rc.l.type.extra1 = 0;
	astype( &rc.l.type, p->sizoff );
	rc.l.nargs = (mode>USUAL) ? mode : 0;
	rc.l.fline = line;
	fwrite( (char *)&rc, sizeof(rc), 1, stdout );
#ifdef FLEXNAMES
	rc.l.name = exname(p->sname);
	fwrite( rc.l.name, strlen(rc.l.name)+1, 1, stdout );
#endif
	}
int proflg;
int gdebug;