ok@quintus.UUCP (Richard A. O'Keefe) (02/08/88)
There's a feature missing from every Lint I've tried, which would be very useful, and should be easy to add (if I had sources, I'd be doing it right now). Consider the stdio functions fscanf( FILE *f, const char *f, ...) sscanf( char *b, const char *f, ...) scanf( const char *f, ...) fprintf(FILE *f, const char *f, ...) sprintf(char *b, const char *f, ...) printf( const char *f, ...) In each of these functions, the format argument is almost always a literal, and it is very easy by inspecting this literal to determine how many arguments should follow and what their types should be. Why not check that the arguments are consistent with the format? (This would also help to catch unimplemented format items; I wouldn't be surprised to find that some of my old programs still contain %r.) What got me thinking about this was seeing a program which did unsigned short x; ... printf("...%ld...", ..., x, ...); and which Lint had no objection to at all. I've used a Fortran compiler which caught this kind of mistake at compile time, and format errors were no less common than some of the other things Lint catches.
guy@gorodish.Sun.COM (Guy Harris) (02/08/88)
> Why not check that the arguments are consistent with the format?
The System V "lint" libraries contain items of the form:
/*VARARGS1 PRINTFLIKE1*/
int printf(s) char *s; { return (0); }
Unfortunately, the System V "lint" doesn't implement "PRINTFLIKE". However, a
Ninth Edition manual I saw did document "PRINTFLIKE", so maybe it is
implemented there; then again, "NOSTRICT" was documented in some versions of
the "lint" manual page although it wasn't implemented.
Guy Harris
{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
guy@sun.com
rsalz@bbn.com (Rich Salz) (02/09/88)
> Why not check >that the [printf, etc] arguments are consistent with the format? Here's a program that does it as a pre-processor to lint... If anyone cleans it up and expands it to handle all the printf-like family, or makes it parse the currently-mythical "/* PRINTFLIKE */" directive, send it to me for posting... /r$ Subject: v06i021: new printfck and manpage (printfck2) Newsgroups: mod.sources Approved: rs@mirror.UUCP Reply-To: liam@cs.qmc.ac.uk (William Roberts) Mod.sources: Volume 6, Issue 21 Submitted by: talcott!seismo!mcvax!cs.qmc.ac.uk!liam Archive-name: printfck2 [ One of the major reasons why I'm sending this out is that is that there is now a manual page. From now on, I will probably not send out anything that is missing one. Probably exceptions are non-Unix source, like the recent Apollo pacman. --r$] The enclosed shar file is an enhanced version of printfck which generates check code for scanf() as well as printf(), has a manual page and comes with a makefile. It is a straightforward extension of the program from Guido van Rossum (mod.sources: Volume 4, Issue 114) which was recently distributed. William Roberts ARPA: liam@cs.qmc.ac.uk Queen Mary College UUCP: liam@qmc-cs.UUCP LONDON, UK --------------------CUT HERE-------------------- #! /bin/sh # shar: Shell Archiver # Run the following with /bin/sh to create: # README # printfck.1 # printfck.c # printfck.h # percent.c # Makefile # This archive created: Fri May 30 18:24:35 BST 1986 echo shar: extracting "README" '('3109 chars')' if test -f README then echo shar: will not overwrite existing file "README" else cat << \SHAR_EOF > README Mod.sources: Volume 4, Issue 114 Submitted by: Guido van Rossum <seismo!mcvax!guido> Here's something a colleague of mind wrote one or two years ago, and which recently prompted some interest on the net. Unfortunately it is not a very finished product; I post this so that others can benefit from it and change it to fit their needs. I tried to compile and run it (on a VAX running 4.2BSD) and it gave sensible output when fed with itself as input -- I can't say more about the quality. Foreseen use is something like: printfck file.c >temp.c lint temp.c procent.c Lint warnings about improper usage of any of the procent_* functions mean you're using an incorrect argument to a % escape. For variable strings used as formats it doesn't help you; see also the comments at the begin of the program. Look in the program to find the command line options (you can feed it your own list of printf-like functions). I'm sorry I can't spend more time on this (well I can but don't intend to since I have no need for it right now). If anybody comes up with a manual, an improved version or any other changes, I'd like to hear about it. Greetings, Guido van Rossum, CWI, Amsterdam <guido@mcvax.UUCP> /* printfck.c - check all uses of %d, %ld, %s, %u etc. - 850325 aeb@mcvax*/ /* small fixes, made more robust, process cmdline arg - 850402 guido@boring*/ /* Copyright 1985,1986 Stichting Mathematisch Centrum. Use at own risk. */ #include <stdio.h> /* Feed with a list of routine names and descriptions: * printf("",...) * sprintf(s,"",...) * fprintf(f,"",...) * and with a source file; produce output in which occurrences of e.g. * sprintf(buf, "%s%ld", s, l) * are replaced by * sprintf(buf, "%s%ld", procent_s(s), procent_L(l)) * Now let lint do the checking. * Bugs: * Cases where the format string is not explicitly given (e.g., is the * result of some other routine, or looks like bool ? "s1" : "s2") * are not handled. * Cases where the preprocessor produces quotes or comment delimiters * or concatenates partial identifiers are not handled. * We do not distinguish two sets of identifiers. * Only the parts lint sees get checked - not parts between (false) * #ifdef's. If the call to printf is outside #ifdef's, but some * args are inside, printfck may get confused. However, this is easy * to avoid: * * THIS FAILS THIS WORKS * ---------- ---------- * printf("%s%d", printf("%s%d", ( * #ifdef debug #ifdef debug * "foo" "foo" * #else #else * "bar" "bar" * #endif debug #endif debug * , num); ), num); * */ 30/5/86 W.T. Roberts <liam@cs.qmc.ac.uk> I have modified printfck to handle scanf() as well as printf(), produced a manual page and a Makefile. SHAR_EOF if test 3109 -ne `wc -c < README` then echo shar: error transmitting "README" '('should be 3109 chars')' else echo README fi fi echo shar: extracting "printfck.1" '('3752 chars')' if test -f printfck.1 then echo shar: will not overwrite existing file "printfck.1" else cat << \SHAR_EOF > printfck.1 .TH PRINTFCK 1 "18 January 1983" .SH NAME printfck \- modify C program to enable typechecking of printf() calls .SH SYNOPSIS .B printfck [ -n ] [ -e functionname ] ... [ -f functionfile ] ... [ c-program ] ... .SH DESCRIPTION .I Printfck reads the C-program source from the named files (or standard input if no arguments are given) and determines the types of the arguments to .IR printf (3S) and .IR scanf (3S) according to the given format string. It writes to standard output a modified version of the program, where the .I printf() or .I scanf() arguments are turned into calls to dummy routines. For example: .PP printf("Name %8s not known.", x); becomes printf("Name %8s not known.", percent_s(x)); .PP The routine percent_s() is defined within the modified program to be a function requiring a pointer to a character, so .IR lint (1) can now check that variable x does indeed have the appropriate type. Any lint warnings about improper usage of a percent_* function indicates that the corresponding printf argument does not match the command string! .PP The program recognises the routines .IR printf() , .IR sprintf() , .IR fprintf() , .IR scanf() , .I sscanf() and .IR fscanf() . .PP A typical way of using this would be: printfck part1.c part2.c part3.c > temp.c lint temp.c .SH "OPTIONS" The options allow modification of the list of functions checked by .IR printfck . This allows the correct checking of user-defined variants of printf() or scanf() and makes lint give more useful line numbers when it complains. .TP -n Erase the default list of functions. .TP -e The following argument is the name of a function to be checked in the same way as printf(): its first argument is a printf() format string from which subsequent argument types may be deduced. Note that fprintf() could not be specified in this way, because its first argument is not the format string. .TP -f The following argument is the name of a file containing a list of (function name, .IR n , .IR t ) triples, where .I n is the number of arguments which precede the format string and .I t indicates the type of formatting involved; 0 means printf()-style and 1 means scanf()-style. For example, the following file is equivalent to the default function names (# indicates a comment): .PP # default specification for printfck printf 0 # printf( "format", arg2, ... ); fprintf 1 # fprintf( fp, "format", arg3, ... ); sprintf 1 # sprintf( s, "format", arg3, ... ); scanf 0 # scanf( "format", arg2, ... ); fscanf 1 # fscanf( fp, "format", arg3, ... ); sscanf 1 # sscanf( s, "format", arg3, ... ); .SH "LIMITATIONS" If the format string is not an explicit constant, .I printfck cannot help you. It will however do its level best, and can cope with complicated #ifdefs, given suitable hints: .nr x \n(.lu/2u .in +0.5i THIS FAILS \h'|\nxu' THIS WORKS \h'|\nxu' printf("%s%d", \h'|\nxu' printf("%s%d", ( #ifdef debug \h'|\nxu' #ifdef debug "foo" \h'|\nxu' "foo" #else \h'|\nxu' #else "bar" \h'|\nxu' "bar" #endif debug \h'|\nxu' #endif debug , num); \h'|\nxu' ), num); .in .SH "BUGS" Doesn't check arguments to see if they contain invocations of the routines to be checked. .SH "SEE ALSO" printf(3S), scanf(3S), lint(1) .SH "AUTHOR" .nf Mod.sources: Volume 4, Issue 114 Submitted by: Guido van Rossum <seismo!mcvax!guido> SHAR_EOF if test 3752 -ne `wc -c < printfck.1` then echo shar: error transmitting "printfck.1" '('should be 3752 chars')' else echo printfck.1 fi fi echo shar: extracting "printfck.c" '('13824 chars')' if test -f printfck.c then echo shar: will not overwrite existing file "printfck.c" else cat << \SHAR_EOF > printfck.c /* printfck.c - check all uses of %d, %ld, %s, %u etc. - 850325 aeb@mcvax*/ /* small fixes, made more robust, process cmdline arg - 850402 guido@boring*/ /* Copyright 1985,1986 Stichting Mathematisch Centrum. Use at own risk. */ /* $Header: printfck.c,v 1.3 86/05/30 12:31:42 liam Exp $ * $Log: printfck.c,v $ * Revision 1.3 86/05/30 12:31:42 liam * Added facility to recognise scanf formats as well. * */ #include <stdio.h> #include <strings.h> #include "printfck.h" /* Feed with a list of routine names and descriptions: * printf("",...) * sprintf(s,"",...) * fprintf(f,"",...) * and with a source file; produce output in which occurrences of e.g. * sprintf(buf, "%s%ld", s, l) * are replaced by * sprintf(buf, "%s%ld", percent_s(s), percent_L(l)) * Now let lint do the checking. * Bugs: * Cases where the format string is not explicitly given (e.g., is the * result of some other routine, or looks like bool ? "s1" : "s2") * are not handled. * Cases where the preprocessor produces quotes or comment delimiters * or concatenates partial identifiers are not handled. * We do not distinguish two sets of identifiers. * Only the parts lint sees get checked - not parts between (false) * #ifdef's. If the call to printf is outside #ifdef's, but some * args are inside, printfck may get confused. However, this is easy * to avoid: * * THIS FAILS THIS WORKS * ---------- ---------- * printf("%s%d", printf("%s%d", ( * #ifdef debug #ifdef debug * "foo" "foo" * #else #else * "bar" "bar" * #endif debug #endif debug * , num); ), num); * */ char *index(); char *rindex(); char *malloc(); #define MAXIRS 100 struct ir { char *rname; int pn; /* number of args preceding format string */ int type; /* 0 = printf, 1 = scanf */ } irs[MAXIRS+1] = { /* should be read in - for now explicit */ "printf", 0, 0, "fprintf", 1, 0, "sprintf", 1, 0, "scanf", 0, 1, "fscanf", 1, 1, "sscanf", 1, 1, }; int nirs; char *progname; char *filename = NULL; FILE *inp; int eof; int peekc; int lastc; int linenr; initgetcx() { eof = 0; peekc = '\n'; /* recognize # on very first line */ lastc = 0; /* result of last getchar() */ linenr = 1; } getcx() { register int c; if(peekc) { c = peekc; peekc = 0; } else if(eof) { c = EOF; } else { if(lastc) { putchar(lastc); lastc = 0; } if((c = getc(inp)) == EOF) eof++; else { lastc = c; if(c == '\n') linenr++; } } return(c); } /* Note: we do not want to eliminate comments; perhaps they contain lint directives. */ getcy() /* as getcx(), but skip comments */ { register int c = getcx(); if(c == '/') { c = getcx(); if(c == '*') { while(1) { c = getcx(); if(c == EOF) error("unfinished comment"); while(c == '*') { c = getcx(); if(c == '/') return(getcy()); } } } else { peekc = c; c = '/'; } } return(c); } getcz() /* as getcy(), but skip preprocessor directives */ { register int c = getcy(); while(c == '\n') { c = getcx(); if(c == '#') { while(c != '\n') { c = getcx(); if(c == EOF) error("incomplete line"); while(c == '\\') { (void) getcx(); c = getcx(); } } } else { peekc = c; return('\n'); } } return(c); } getcq() /* as getcz() but skip strings */ { register int c = getcz(); register int delim; if(c == '\'' || c == '"') { delim = c; while(1) { c = getcx(); if(c == '\n' || c == EOF) error("Unfinished string (delim %c)", delim); if(c == '\\') { (void) getcx(); continue; } if(c == delim) return(getcq()); } } return(c); } usage() { fprintf(stderr, "Usage: %s [-n] [-e function] ... [-f datafile] ... [file.c] ...\n", progname); exit(2); } extern char *optarg; extern int optind; main(argc, argv) int argc; char **argv; { register int c; FILE *fp; if (argc > 0) { progname = rindex(argv[0], '/'); if (progname != NULL) ++progname; else progname = argv[0]; } for (; irs[nirs].rname != NULL; ) ++nirs; /* Count defaults */ while ((c = getopt(argc, argv, "e:nf:")) != EOF) { switch (c) { case '?': usage(); /* NOTREACHED */ case 'e': addir(optarg, 0, 0); break; case 'n': nirs = 0; irs[nirs].rname = NULL; break; case 'f': getirfile(optarg); break; } } /* tell lint the types of the percent_* routines */ printf("#include \"%s\"\n", PERCENT_HEADERS); /* now process them files.... */ if (optind == argc) treat(stdin, "stdin"); else { for (; optind < argc; ++optind) { if (strcmp(argv[optind], "-") == 0) treat(stdin, "stdin"); else { filename = argv[optind]; fp = fopen(filename, "r"); if (fp == NULL) filerror(filename); treat(fp, filename); fclose(fp); } } } /* now include the bodies of the routines */ printf("#include \"%s\"\n", PERCENT_ROUTINES); exit(0); } treat(fp, file) FILE *fp; char *file; { register int c; filename = file; linenr = 0; inp = fp; printf("# line 1 \"%s\"\n", file); initgetcx(); while((c = getcq()) != EOF) { /* check for (interesting) identifiers */ if(letter(c)) rd_id(c); } filename = NULL; linenr = 0; irs[nirs].rname = NULL; } rd_id(first) register int first; { char idf[256]; register char *ip = idf; register int c; *ip++ = first; while(letdig(c = getcx())) if(ip-idf < sizeof(idf)-1) *ip++ = c; peekc = c; *ip = 0; handle(idf); } /*VARARGS1*/ error(s, x) char *s; { printf("\n"); /* Finish incomplete output line */ fprintf(stderr, "%s: Error (", progname); if (filename != NULL) fprintf(stderr, "%s, ", filename); fprintf(stderr, "line %d): ", linenr); fprintf(stderr, s, x); fprintf(stderr, "\n"); exit(1); } /*VARARGS1*/ warning(s, x1, x2) char *s; { fprintf(stderr, "%s: Warning (", progname); if (filename != NULL) fprintf(stderr, "%s, ", filename); fprintf(stderr, "line %d): ", linenr); fprintf(stderr, s, x1, x2); fprintf(stderr, "\n"); } filerror(file) char *file; { fprintf(stderr, "%s: can't open ", progname); perror(file); exit(2); } letter(c) register int c; { return(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'); } digit(c) register int c; { return('0' <= c && c <= '9'); } letdig(c) register int c; { return(letter(c) || digit(c)); } handle(idf) register char *idf; { register struct ir *irp = irs; while(irp->rname) { if(!strcmp(idf, irp->rname)) { doit(irp); return; } irp++; } } skipspaces() { register int c; while(1) { c = getcz(); if(c == ' ' || c == '\t' || c == '\n') continue; peekc = c; return; } } doit(irp) register struct ir *irp; { register int c, cnt = irp->pn; skipspaces(); if((c = getcz()) != '(') { peekc = c; warning("%s not followed by '('", irp->rname); return; } while(cnt--) { c = skiparg(); if(c != ',') { peekc = c; warning("arg of %s not followed by comma", irp->rname); return; } } skipspaces(); /* now parse format string (if present) */ /* (here we also avoid defining occurrences) */ if((c = getcx()) != '"') { peekc = c; return; } if (irp->type == 0) do_printf(irp); else do_scanf(irp); } do_printf(irp) register struct ir *irp; { char fmt[256]; register char *fp = fmt; register int c; while(1) { c = getcx(); if(c == '\n' || c == EOF) error("Unfinished format string"); if(c == '"') break; if(c != '%') { if (c == '\\') (void) getcx(); continue; } c = getcx(); if(c == '%') continue; if(c == '-') c = getcx(); if(c == '*') { c = getcx(); if(fp-fmt < sizeof(fmt)-1) *fp++ = '*'; } else while(digit(c)) c = getcx(); if(c == '.') c = getcx(); if(c == '*') { c = getcx(); if(fp-fmt < sizeof(fmt)-1) *fp++ = '*'; } else while(digit(c)) c = getcx(); if(c == '#') c = getcx(); if(c == 'l') { c = getcx(); if('a' <= c && c <= 'z') c -= 'a'-'A'; else error("%%l not followed by lowercase"); } if(fp-fmt < sizeof(fmt)-1) *fp++ = c; else warning("ridiculously long format"); } *fp = 0; fp = fmt; skipspaces(); while((c = getcz()) == ',') { if(!*fp) error("%s has too many arguments", irp->rname); skipspaces(); if (*fp == '*') { printf("percent_star("); fp++; } else printf("percent_%c(", *fp++); c = skiparg(); printf(")"); peekc = c; } if(c != ')') error("%s has ill-formed argument list", irp->rname); if(*fp) error("%s has too few arguments", irp->rname); } do_scanf(irp) register struct ir *irp; { char fmt[256], shorten; register char *fp = fmt; register int c; int dummy = 0; while(1) { dummy = 0; c = getcx(); if(c == '\n' || c == EOF) error("Unfinished format string"); if(c == '"') break; if(c != '%') { if (c == '\\') (void) getcx(); continue; } c = getcx(); if(c == '%') continue; /* %% */ if(c == '*') { dummy = 1; /* no corresponding argument */ c = getcx(); } while (digit(c) || c == '.' || c == '-') { c = getcx(); } switch(c) { case '[': c = skipbracket(); /* scan to close bracket */ break; case 'l': c = getcx(); if('a' <= c && c <= 'z') c -= 'a'-'A'; else error("%%l not followed by lowercase"); break; case 'h': shorten = c; c = getcx(); if('a' <= c && c <= 'z') c -= 'a'-'A'; else error("%%h not followed by lowercase"); break; default: /* must be a format letter */ break; } if (!dummy) { if(fp-fmt < sizeof(fmt)-2) { if (shorten == 'h') *fp++ = 'h'; *fp++ = c; } else warning("ridiculously long format"); } } *fp = 0; fp = fmt; skipspaces(); while((c = getcz()) == ',') { if(!*fp) error("%s has too many arguments", irp->rname); skipspaces(); if ( (shorten = *fp) == 'h') fp++; switch (*fp) { case 's': printf("percent_s("); break; case ']': printf("percent_bkt("); break; default: if (shorten == 'h') printf("percent_h%c_ptr(", *fp); else printf("percent_%c_ptr(", *fp); } fp++; c = skiparg(); printf(")"); peekc = c; } if(c != ')') error("%s has ill-formed argument list", irp->rname); if(*fp) error("%s has too few arguments", irp->rname); } skiparg() { register int parenct = 0; register int c; parenct = 0; while(1) { c = getcq(); if(c == EOF) error("eof in arg list"); if(!parenct && (c == ',' || c == ')')) return(c); if(c == '(' || c == '[') { parenct++; continue; } if(c == ')' || c == ']') { parenct--; continue; } } } skipbracket() { register int c; while(1) { c = getcx(); if(c == EOF) error("eof during %%[ ]"); if(c == '\\') { c = getcx(); continue; /* avoid escaped char */ } if(c == ']') return c; } } char *strsave(s) char *s; { char *t = malloc(strlen(s) + 1); if (t == NULL) error("out of memory"); strcpy(t, s); return t; } getirfile(irfile) char *irfile; { FILE *fp = fopen(irfile, "r"); char line[256]; char name[256]; char *end; int n; int cnt, type; if (fp == NULL) filerror(irfile); filename = irfile; linenr = 0; while (fgets(line, sizeof line, fp)) { ++linenr; end = index(line, '#'); if (end) *end = '\0'; n= sscanf(line, " %s %d %d %s", name, &cnt, &type, name+1); if (n == 0 || name[0] == '\0') continue; /* Skip empty line or comment */ if (n != 3 || cnt < 0 || (type != 0 && type != 1) ) error("bad format (must be %%s %%u %%u)"); /* Should also check for valid name... */ addir(strsave(name), cnt, type); } fclose(fp); filename = NULL; linenr = 0; } addir(name, cnt, type) char *name; int cnt, type; { if (nirs >= MAXIRS) error("table overflow"); irs[nirs].rname = name; irs[nirs].pn = cnt; irs[nirs].type = type; ++nirs; } /* * get option letter from argument vector */ int opterr = 1, /* useless, never set or used */ optind = 1, /* index into parent argv vector */ optopt; /* character checked for validity */ char *optarg; /* argument associated with option */ #define BADCH (int)'?' #define EMSG "" #define tell(s) fputs(*nargv,stderr);fputs(s,stderr); \ fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); getopt(nargc,nargv,ostr) int nargc; char **nargv, *ostr; { static char *place = EMSG; /* option letter processing */ register char *oli; /* option letter list index */ char *index(); if(!*place) { /* update scanning pointer */ if(optind >= nargc || *(place = nargv[optind]) != '-' || !*++place) return(EOF); if (*place == '-') { /* found "--" */ ++optind; return(EOF); } } /* option letter okay? */ if ((optopt = (int)*place++) == (int)':' || !(oli = index(ostr,optopt))) { if(!*place) ++optind; tell(": illegal option -- "); } if (*++oli != ':') { /* don't need argument */ optarg = NULL; if (!*place) ++optind; } else { /* need an argument */ if (*place) optarg = place; /* no white space */ else if (nargc <= ++optind) { /* no arg */ place = EMSG; tell(": option requires an argument -- "); } else optarg = nargv[optind]; /* white space */ place = EMSG; ++optind; } return(optopt); /* dump back option letter */ } SHAR_EOF if test 13824 -ne `wc -c < printfck.c` then echo shar: error transmitting "printfck.c" '('should be 13824 chars')' else echo printfck.c fi fi echo shar: extracting "printfck.h" '('132 chars')' if test -f printfck.h then echo shar: will not overwrite existing file "printfck.h" else cat << \SHAR_EOF > printfck.h #define PERCENT_HEADERS "/usr/src/net/printfck/percent.h" #define PERCENT_ROUTINES "/usr/src/net/printfck/percent.c" SHAR_EOF if test 132 -ne `wc -c < printfck.h` then echo shar: error transmitting "printfck.h" '('should be 132 chars')' else echo printfck.h fi fi echo shar: extracting "percent.c" '('2094 chars')' if test -f percent.c then echo shar: will not overwrite existing file "percent.c" else cat << \SHAR_EOF > percent.c /*LINTLIBRARY*/ int percent_d(x) int x; { return x; } int percent_o(x) int x; { return x; } int percent_x(x) int x; { return x; } long percent_D(x) long x; { return x; } long percent_O(x) long x; { return x; } long percent_X(x) long x; { return x; } double percent_e(x) double x; { return x; } double percent_f(x) double x; { return x; } double percent_g(x) double x; { return x; } double percent_E(x) double x; { return x; } double percent_F(x) double x; { return x; } double percent_G(x) double x; { return x; } unsigned percent_u(x) unsigned x; { return x; } int percent_c(x) int x; { return x; } char * percent_s(x) char * x; { return x; } int percent_star(x) int x; { return x; } int * percent_d_ptr(x) int * x; { return x; } int * percent_o_ptr(x) int * x; { return x; } int * percent_x_ptr(x) int * x; { return x; } short * percent_hd_ptr(x) short * x; { return x; } short * percent_ho_ptr(x) short * x; { return x; } short * percent_hx_ptr(x) short * x; { return x; } long * percent_D_ptr(x) long * x; { return x; } long * percent_O_ptr(x) long * x; { return x; } long * percent_X_ptr(x) long * x; { return x; } float * percent_e_ptr(x) float * x; { return x; } float * percent_f_ptr(x) float * x; { return x; } float * percent_g_ptr(x) float * x; { return x; } double * percent_E_ptr(x) double * x; { return x; } double * percent_F_ptr(x) double * x; { return x; } double * percent_G_ptr(x) double * x; { return x; } unsigned * percent_u_ptr(x) unsigned * x; { return x; } int * percent_c_ptr(x) int * x; { return x; } char * percent_bkt(x) char * x; { return x; } /* NOTE: not all C compilers support unsigned long! - If your compiler rejects * the following lines, replace "unsigned long" with just "long" */ unsigned long percent_U(x) unsigned long x; { return x; } unsigned long * percent_U_ptr(x) unsigned long * x; { return x; } SHAR_EOF if test 2094 -ne `wc -c < percent.c` then echo shar: error transmitting "percent.c" '('should be 2094 chars')' else echo percent.c fi fi echo shar: extracting "Makefile" '('300 chars')' if test -f Makefile then echo shar: will not overwrite existing file "Makefile" else cat << \SHAR_EOF > Makefile # Makefile for printfck # # Change printfck.h so that PERCENT_HEADERS and PERCENT_ROUTINES # have the correct directory paths. all: printfck percent.h printfck: printfck.o printfck.h cc -o printfck printfck.o percent.h: percent.c sed -e "s/LINTLIBRARY//" -e "s/(x.*/();/" percent.c >percent.h SHAR_EOF if test 300 -ne `wc -c < Makefile` then echo shar: error transmitting "Makefile" '('should be 300 chars')' else echo Makefile fi fi # End of shar archive exit 0 -- For comp.sources.unix stuff, mail to sources@uunet.uu.net.
dmk@dmk3b1.UUCP (David Keaton) (02/09/88)
In article <631@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes: > unsigned short x; > ... > printf("...%ld...", ..., x, ...); >and which Lint had no objection to at all. Safe C, from Catalytix in Boston, checks for this. I don't have their address handy at the moment, but I have used the product and it is excellent. (I have no connection with them except as a customer.) -- David Keaton dmk%dmk3b1@uunet.uu.net uunet!dmk3b1!dmk
rbutterworth@watmath.waterloo.edu (Ray Butterworth) (02/09/88)
In article <631@cresswell.quintus.UUCP>, ok@quintus.UUCP (Richard A. O'Keefe) writes: > There's a feature missing from every Lint I've tried, which would be > very useful, and should be easy to add (if I had sources, I'd be doing > it right now). Consider the stdio functions > In each of these functions, the format argument is almost always a literal, > and it is very easy by inspecting this literal to determine how many > arguments should follow and what their types should be. Why not check > that the arguments are consistent with the format? (This would also help > to catch unimplemented format items; I wouldn't be surprised to find that > some of my old programs still contain %r.) > From: guy@gorodish.Sun.COM (Guy Harris) > The System V "lint" libraries contain items of the form: > /*VARARGS1 PRINTFLIKE1*/ > int printf(s) char *s; { return (0); } > Unfortunately, the System V "lint" doesn't implement "PRINTFLIKE". However, a > Ninth Edition manual I saw did document "PRINTFLIKE", so maybe it is > implemented there; then again, "NOSTRICT" was documented in some versions of > the "lint" manual page although it wasn't implemented. Good timing. Last week I added a format checker to our highly modified BSD lint. After I've tested it a bit more, I'll probably post the change in a few weeks. I couldn't figure out how to do a /*PRINTFLIKE#*/ directive with the function definition, since that is looked at in pass2, and by then the format string used by the calling function is long gone. Instead, I added a directive to the function declaration. e.g. extern int printf(/*FORMAT1*/); goes into <stdio.h>. I tried linting some of the BSD source, but it didn't do much good since Berkeley seldom bothers to include stdio.h unless the code won't compile without it, so not many formats were actually checked. Here's an example of what it does at the moment: % cat xx.c #include <stdlib.h> #include <stdio.h> main(argv, argc) { printf("argc = %ld\n", argc, argv); printf("argv = %*.*s\n", 12.3, 17L, argv); printf("%r %d %d\n", 17); } % lint xx.c xx.c: xx.c(6): warning: format #1 "%ld" requires long, not int xx.c(6): warning: Too many arguments for format xx.c(7): warning: format #1 "%*.*" *-width requires int, not double xx.c(7): warning: format #1 "%*.*s" *-precision requires int, not long xx.c(7): warning: format #1 "%*.*s" requires string, not int xx.c(8): warning: unknown #1 "%r" format specifier xx.c(8): warning: Not enough arguments for format "main", arg. 2 used inconsistently (int != pointer to pointer to char) xx.c(5) :: llib-lmain:crt0.c(3) "main" result is used, but none is returned. "printf" result is always ignored.