Changes to BSD lint to check printf format and arguments.
Ray Butterworth
rbutterworth at watmath.waterloo.edu
Wed Feb 24 00:51:58 AEST 1988
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 ============
More information about the Comp.lang.c
mailing list