rbutterworth@watmath.waterloo.edu (Ray Butterworth) (02/23/88)
Here are the changes I recently made to our version of BSD 4.3 lint. Lines beginning with " +" are new and to be added, those with "- " are old and to be deleted, and others are for context. Note that it won't do much if your source doesn't bother to include stdio.h. ============ /usr/include/stdio.h ============ + extern int printf(/*FORMAT1*/); + extern int fprintf(/*FORMAT2*/); + extern int sprintf(/*FORMAT2*/); /* (char *) on BSD 4.3 */ ============ /usr/src/lib/mip/pass1.h ============ - char sflags; /* flags, see below */ + short sflags; /* flags, see below */ + #define SFMT1 0400 /* function uses printf format in arg 1 */ + #define SFMT2 01000 /* function uses printf format in arg 2 */ + /* SFMT1|SFMT2 function uses it in arg 3 */ ============ /usr/src/lib/mip/cgram.y ============ fdeclarator: ... | name_lp RP = { + #ifdef LINT + {extern int fmtflag;} + if (fmtflag){ + if (fmtflag&01) + stab[$1].sflags |= SFMT1; + if (fmtflag&02) + stab[$1].sflags |= SFMT2; + fmtflag = 0; + } + #endif /*LINT*/ $$ = bdty(UNARY CALL, ... ============ /usr/lib/mip/common.c ============ - static char *tnames[] = { + char *tnames[] = { ============ /usr/lib/mip/scan.c ============ lxcom() { ... switch (c=getchar()) { ... + case 'F': + # define FMTSIZE 6 /* strlen("FORMAT") */ + {extern int fmtflag;} + + lxget( c, LEXLET|LEXDIG ); + if(strncmp(yytext, "FORMAT", FMTSIZE)) + continue; + fmtflag = 1; + if (yytext[FMTSIZE]) { + fmtflag = yytext[FMTSIZE] - '0'; + if ((yytext[FMTSIZE+1] != '\0') + || (fmtflag < 1) + || (fmtflag > 3) ) { + werror("number in \"%s\" must be 1, 2, or 3", yytext); + fmtflag = 0; + } + } + continue; === /usr/src/usr.bin/lint/macdefs.h === - # define getlab() 10 === /usr/src/usr.bin/lint/lpass1.c === + #define SAVECYCLE 64 + static char *savestring[SAVECYCLE] = 0; + static int savecurrent = SAVECYCLE; + static int saveused = 1; + + extern char *malloc(); + extern char *realloc(); + + int fmtflag = 0; /* following function has a printf-type format */ lprt( p, down, uses ) register NODE *p; { ... switch( p->in.op ){ ... case UNARY CALL: ... outdef( sp, lty, acount ); if( acount ) { lpta( p->in.right ); + if (hflag + && (sp->sflags & (SFMT1|SFMT2))) { + register int fmtarg = 0; + if (sp->sflags & SFMT1) + fmtarg += 1; + if (sp->sflags & SFMT2) + fmtarg += 2; + check_format(p, fmtarg); + } ... } ... } ... } + static char * + maketype(t) + TWORD t; + { + extern char *tnames[]; + static char typename[256]; + + typename[0] = '\0'; + for (; ; t = DECREF(t)) { + if (ISPTR(t)) + strcat(typename, "pointer to "); + else if (ISFTN(t)) + strcat(typename, "function returning "); + else if (ISARY(t)) + strcat(typename, "array of "); + else { + strcat(typename, tnames[t]); + return typename; + } + } + } + + getlab() + { + if (saveused) { + saveused = 0; + if (--savecurrent == 0) /* Don't use 0. We have to negate it. */ + savecurrent = SAVECYCLE-1; + } + return savecurrent; + } + - /*ARGSUSED*/ - bycode(t,i){;} + bycode(t,i) + { + static int savesize[SAVECYCLE]; + + saveused = 1; + + if (i >= savesize[savecurrent]) { + while (i >= savesize[savecurrent]) + savesize[savecurrent] = (savesize[savecurrent]*2) + 4; + savestring[savecurrent] + = realloc(savestring[savecurrent], savesize[savecurrent]); + } + + savestring[savecurrent][i] = t; + } + + static char * + makeformat(number, start, end) + int number; + char *start; + char *end; + { + static char *format = 0; + static int avail = 0; + auto int size; + + size = end - start + 20; + if (size >= avail) { + if (format) + free(format); + while (avail < size) + avail = (avail*2) + 4; + format = malloc(avail); + } + sprintf(format, "#%d \"%.*s\"", number, end-start+1, start); + return format; + } + + static TWORD + promote(type) + TWORD type; + { + switch (type) { + case FLOAT: + case DOUBLE: + return DOUBLE; + + case LONG: + case ULONG: + return LONG; + + case CHAR: + case UCHAR: + case SHORT: + case USHORT: + case INT: + case UNSIGNED: + case ENUMTY: + case MOETY: + return INT; + } + + return type; + } + + #define MAXARGS 64 + + #define push(p) \ + if (top < MAXARGS) arg_stack[top++] = p; \ + else goto overflow; + + #define pop(p) \ + if (top > 0) (p) = arg_stack[--top]; \ + else goto underflow; + + static NODE *arg_stack[MAXARGS]; + + static int top; + + check_format(p, fmtarg) + register NODE *p; + int fmtarg; + { + register char *format; + auto char *start; + auto char *func_name = "printf-like function"; + auto int fmtnum; + + if (p->in.left->tn.op == ICON) + func_name = stab[p->in.left->tn.rval].sname; + + /* save all the arguments */ + top = 0; + p = p->in.right; + while (p->in.op == CM) { + push(p->in.right); + p = p->in.left; + } + push(p); + + /* get the format string */ + while (--fmtarg >= 0) + pop(p); + if ((p->in.type != (PTR|CHAR)) + || (p->in.op != ICON) + || (p->tn.rval >= 0)) { + if (Oflag) { + werror("function '%s': format and argument types not linted", + func_name); + } + return; + } + format = savestring[-p->tn.rval]; + + fmtnum = 0; + while (*format) { + if (*format == '%') { + start = format; + if (*++format != '%') { + auto TWORD scale = INT; + auto TWORD type; + + ++fmtnum; + if (*format == '#') ++format; + if ((*format == '+') + || (*format == '-')) ++format; + if (*format == '*') { + ++format; + pop(p); + type = promote(p->in.type); + if (type != INT) + werror("format %s *-width requires int, not %s", + makeformat(fmtnum, start, format+1), + maketype(type)); + } + else while(isdigit(*format)) ++format; + if (*format == '.') { + ++format; + if (*format == '*') { + ++format; + pop(p); + type = promote(p->in.type); + if (type != INT) + werror("format %s *-precision requires int, not %s", + makeformat(fmtnum, start, format+1), + maketype(type)); + } + else while(isdigit(*format)) ++format; + } + if (*format == 'h') { + ++format; + scale = SHORT; + } + else if (*format == 'l') { + ++format; + scale = LONG; + } + pop(p); + type = promote(p->in.type); + switch (*format) { + default: + werror("unknown %s format specifier", + makeformat(fmtnum, start, format)); + break; + case 's': + if (((PTR|CHAR) != type) + && ((PTR|UCHAR) != type)) + werror("format %s requires string, not %s", + makeformat(fmtnum, start, format), + maketype(type)); + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (DOUBLE != type) + werror("format %s requires double, not %s", + makeformat(fmtnum, start, format), + maketype(type)); + break; + case 'p': + /* BSD 4.3 has neither (void *) nor %p, + * but we can check for it anyway. + */ + if (type != PTR) + werror("format %s requires (void *), not %s", + makeformat(fmtnum, start, format), + maketype(type)); + break; + case 'd': + case 'c': + case 'u': + case 'o': + case 'x': + case 'X': + if (scale == LONG) { + if (LONG != type) + werror("format %s requires long, not %s", + makeformat(fmtnum, start, format), + maketype(type)); + } + else { + if ((INT != type) + && (!ISPTR(type))) /* all ptrs are int (ugh!) */ + werror("format %s requires int, not %s", + makeformat(fmtnum, start, format), + maketype(type)); + } + scale = INT; + break; + } + if (scale != INT) + werror("format %s scale 'h' or 'l' is inapproprate for %s", + makeformat(fmtnum, start, format), + maketype(type)); + } + } + ++format; + } + + if (top != 0) + werror("Too many arguments for format"); + + return; + + overflow: + werror("lint error: only first %d arguments checked", MAXARGS); + return; + + underflow: + werror("Not enough arguments for format"); + return; + } ============ That's all there is ============