home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
unix
/
volume4
/
printfck
/
printfck.c
next >
Wrap
C/C++ Source or Header
|
1986-11-30
|
10KB
|
575 lines
/* 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);
*
*/
char *index();
char *rindex();
char *malloc();
#define MAXIRS 100
struct ir {
char *rname;
int pn; /* number of args preceding format string */
} irs[MAXIRS+1] = { /* should be read in - for now explicit */
"printf", 0,
"fprintf", 1,
"sprintf", 1,
};
int nirs;
char *progname = "printfck";
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);
break;
case 'n':
nirs = 0;
irs[nirs].rname = NULL;
break;
case 'f':
getirfile(optarg);
break;
}
}
printf("#include \"procent.h\"\n");
if (optind == argc)
treat(stdin, NULL);
else {
for (; optind < argc; ++optind) {
if (strcmp(argv[optind], "-") == 0)
treat(stdin, NULL);
else {
filename = argv[optind];
fp = fopen(filename, "r");
if (fp == NULL)
filerror(filename);
treat(fp, filename);
fclose(fp);
}
}
}
exit(0);
}
treat(fp, file)
FILE *fp;
char *file;
{
register int c;
filename = file;
linenr = 0;
inp = fp;
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;
}
domore(irp);
}
domore(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++ = 'd';
} else while(digit(c))
c = getcx();
if(c == '.')
c = getcx();
if(c == '*') {
c = getcx();
if(fp-fmt < sizeof(fmt)-1)
*fp++ = 'd';
} 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();
printf("procent_%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);
}
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;
}
}
}
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;
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 %s", name, &cnt, name+1);
if (n == 0 || name[0] == '\0')
continue; /* Skip empty line or comment */
if (n != 2 || cnt < 0)
error("bad format (must be %%s %%u)");
/* Should also check for valid name... */
addir(strsave(name), cnt);
}
fclose(fp);
filename = NULL;
linenr = 0;
}
addir(name, cnt)
char *name;
int cnt;
{
if (nirs >= MAXIRS) error("table overflow");
irs[nirs].rname = name;
irs[nirs].pn = cnt;
++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 */
}