koreth@ssyx.ucsc.edu (Steven Grimm) (07/06/88)
Submitted-by: decwrl!pacbell!dssch1!dsuthers
Posting-number: Volume 1, Issue 61
Archive-name: varlint
The following is a utility for those of us who program in 'C', but
are lousey typists. The program checks for:
scanf sccanf fscanf printf sprintf fprintf
function calls, and checks their syntax. It is a SUPPLEMENT to lint
(or a good syntax checking compiler); It is not a replacement for lint.
[Executable is being posted to the binaries group. -sg]
#--------------------------Cut Here--------------------------
#! /bin/sh
# This is a shell archive. Remove anything before the "#! /bin/sh" line,
# then unpack it by saving it in a file and typing "sh file."
#
# Wrapped by Steven Grimm (koreth) at ssyx on Wed Jul 6 07:38:20 1988
#
# unpacks with default permissions
#
# Contents : VARLINT.DOC VARLINT.C
#
if `test ! -s VARLINT.DOC`
then
echo "x - VARLINT.DOC"
cat > VARLINT.DOC << '@\Rogue\Monster\'
VARLINT(Local) 'C' Utility VARLINT(Local)
SYNOPSIS
varlint <program name> ... <program name>
HISTORY
This program was developed under Lattice C on an Atari 520 ST. It
is compatable with (and tested on) DEC 11/70 and AT&T 3B5 processors,
running SYS V, and earlier versions too. There is no reason that it
won't compile on any system.
USAGE
This program is designed to assist when coding in C. It double checks
your syntax for those funtions that have a variable number of arguments,
and use the same syntax as printf / scanf.
WHEN TO USE IT
If your program passes lint, and it compiles ok, but crashes any-way,
or doesn't quite work right, try varlint.
Mistakes with scanf or printf are one of the most frequent causes of
intermittant crashes. "Lint" does not check the syntax, and neither
do most of the compilers.
CAPABILITIES:
This program will detect and print out the following conditions.
1) More arguments following the control string than there are
percent signs in the control string.
2) Too many arguments as compared to the control string.
3) Mis-matches between the argument type and the item in
the control string.
4) Null control string.
5) Unknown specifiers after a percent sign in a control string.
6) Missing ampersands (&) in front of numbers. (scan functions)
7) Will not complain about valid uses. (allows funtion calls as
arguments, strings as *chars, etc.
RESTRICTIONS:
1) It does not yet check to ensure that the variables you name
are indeed the type you claim.
2) The use of a character array is allowed by the scanf / printf
functions, but is not yet verified by the program. You
are notified of "un-recognized contstruct" in these cases.
LICENSE:
Copyright 1988 by Daniel B. Suthers, Concord, California, USA.
License is granted to distribute this program at will, as long as
the source is also provided free of charge to anyone who
receives the binaries, or any one who wants it,
AND all copyright notices are left intact.
@\Rogue\Monster\
else
echo "shar: Will not over write VARLINT.DOC"
fi
if `test ! -s VARLINT.C`
then
echo "x - VARLINT.C"
cat > VARLINT.C << '@\Rogue\Monster\'
/*###########################################################################
*
* This program is used to find those pesty errors in varags type
* functions such as printf. It only handles those functions defined
* in the structure"fu".
*
* By Daniel B. Suthers Mar 3, 1988
*
* Copyright 1988 by Daniel B. Suthers, Concord, California, USA.
* License is granted to distribute this program at will, as long as
* the source is also provided free of charge to anyone who recieves
* the binaries, or to anyone who wants it,
* AND all copyright notices are left intact.
* The
*
###move $This_File 755 uprog none /uprog/bin/src/varlint.c
##move $This_File 755 uprog none /usr/uprog/src/bin/varlint.c
*#########################################################################*/
#include <stdio.h>
#include <ctype.h>
#define VER "2.7"
#define MAX 512
#define NUMNAMES 6
#ifdef pdp11
char APPL[] ="\n##move $This_File 755 uprog none /usr/uprog/bin/varlint\n";
#else
char APPL[]="\n###move $This_File 755 uprog none /usr/lbin/varlint\n";
FILE *fopen();
void scandoos();
#endif
struct funcdat{
char nam[9]; /* stores function names*/
int len; /* number of chars in names*/
int pos; /* position of control string*/
int scan;
};
struct funcdat fu[ NUMNAMES] = {
{ "printf", 6, 0, 0},
{ "scanf", 5, 0, 1},
{ "sprintf", 7,1, 0},
{ "sscanf", 6, 1, 1},
{ "fprintf", 7,1, 0},
{ "fscanf", 6, 1, 1}
};
char *fgets(), *findname();
int in_comment = 0; /* flag = 1 when in a comment*/
main(argc, argv, env)
int argc;
char *argv[], *env[];
{
extern int optind;
extern char *optarg;
register int inta;
char filename[128];
FILE *infile ;
while( (inta = getopt( argc, argv, "vVhH")) != EOF)
{
switch (inta)
{
case 'V':
case 'v':
fprintf (stderr, "%s\n"
, VER);
return(0);
case 'h':
case 'H':
help();
return(0);
default:
printf("Bad argument\nTry -h for help.\n");
return(1);
}
}
while( optind < argc ) /* these would be files*/
{
strcpy(filename, argv[optind++]);
if( (infile = fopen(filename, "r") ) == NULL)
{
fprintf(stderr, "Unable to open source file %s\n", filename);
}
else
{
printf("Working on %s\n", filename);
scandoos(infile);
if( infile != (FILE *) NULL)
fclose(infile);
}
}
return(0);
} /*end of main*/
help()
{
printf(
"\n\tCopyright 1988 by Daniel B. Suthers, Concord, California, USA.\n");
printf(
"\tLicense is granted to distribute this program at will, as long as\n");
printf(
"\tthe source is also provided free of charge to anyone who recieves\n");
printf("\tthe binaries, or to anyone who wants it,\n");
printf("\tAND all copyright notices are left intact.\n");
printf("\n\tThis program checks for mismatched arguments in functions\n");
printf("\tthat accept variable arguments ala printf().\n");
printf("\tCurrently supports: printf, scanf and their cousins.\n");
printf("\t(Checks only for # of args and & for integers)\n");
printf("\n\tThe arguments accepted are:\n\n");
printf("\t-v\tPrint the version number and exit.\n");
printf("\t-help\tPrint the most current help.\n");
return(0);
}
#ifdef u3b5
void
#endif
scandoos(infile)
FILE *infile;
{
register int linenum, reterr, oldline;
char buff[MAX*3], *buffp; /* allow 10 lines */
char tempstr[MAX*3];
int funcindex;
linenum = oldline = 0;
reterr = 0;
while( fgets(buff, MAX, infile) != (char *)NULL )
{
if(reterr == 100 ) /* multi line statement */
{
if( oldline == 0)
oldline = linenum;
strcat(tempstr,buff);
strcpy(buff, tempstr);
reterr = 0;
}
linenum++;
if( (buffp = findname(buff,&funcindex)) == NULL )
continue;
if( in_comment )
continue;
/*
printf("%4d: %s",linenum, buffp);
*/
if( (reterr = parsedo(buffp,funcindex)) > 0)
{
if( reterr < 100 )
{
if( oldline != 0)
{
printf("***** Error at line %5d\n%s", oldline, buffp);
oldline = 0;
}
else
printf("***** Error at line %5d\n%s", linenum, buffp);
}
switch(reterr)
{
case 1:
printf("More \"%%\" in control string than there are variables.\n\n");
break;
case 2:
printf("A varible type did not match the \"%%\".\n\n");
break;
case 3:
printf("An undefined variable used.\n\n");
break;
case 4:
printf("An un-recognized construct, might be ok.\n\n");
break;
case 5:
printf("Fewer \"%%\" in control string than there are variables.\n\n");
break;
case 6:
printf("Integer type requires an ampersand (\&).\n\n");
break;
case 7:
printf("Print found a string, expecting an int.\n\n");
break;
case 8:
printf("Unknown %% specifier.\n\n");
break;
case 9:
printf("Re declaration of the function. You sure this is ok?\n\n");
break;
case 10:
printf("Null control string. You sure this is ok?\n\n");
break;
case 100:
strcpy( tempstr, buff);
break;
}
} /* end of if(parsedo) */
else
oldline = 0;
}
} /* end of scandoos() */
char *
findname(string, funcind)
char string[];
int *funcind;
{
char *ptr;
register int a;
ptr = string;
while( *ptr != NULL )
{
if( *ptr == '/') /* set flag if this */
if( *(ptr + 1) == '*') /* is start of comment*/
{
in_comment = 1;
ptr += 2;
continue;
}
if( *ptr == '*' ) /* un-set flag if end */
if( *(ptr+1) == '/') /* of comment */
{
in_comment = 0;
ptr +=2;
continue;
}
for( a = 0; a < NUMNAMES; a++)
{
if( strncmp( ptr, fu[a].nam, fu[a].len ) == 0 )
{
*funcind = a;
return(ptr);
}
}
ptr++;
}
return(NULL);
}
int
parsedo(buff, fi)
char buff[]; /* function string */
int fi; /* fu.[] index */
{
register int parens, pcntnum, c;
char *buffp;
int end=0, inquotes = 0;
int vartype[124]; /* for var type */
parens = 0;
buffp = buff;
buffp += fu[fi].len; /* get past function */
while( isspace(*buffp) ) /* skip white space */
buffp++;
if( *buffp == NULL)
return(100);
if( *buffp != '(' ) /* not a function */
return(0);
if( *(buffp +1) == ')' ) /* function definition */
return(9);
buffp++;
while( isspace(*buffp) ) /* skip white space */
buffp++;
if( *buffp == NULL)
return(100);
for(c = 0;c < fu[fi].pos; c++) /* skip to control string*/
{ /* (stop on comma) */
while( (*buffp != NULL) && (*buffp != ',') )
buffp++;
if( *buffp == NULL)
return(100);
buffp++;
}
if( *buffp == NULL)
return(100);
while( isspace( *(buffp)) ) /* for ' , "test"' */
buffp++;
if( *buffp == NULL)
return(100);
if( *buffp != '"') /* should be on first quote*/
return(4);
if( *(buffp+1) == '"') /* dont want "" as cntl str */
return(10);
/* count the percent signs */
/* starting on first quote */
pcntnum = 0;
end = 0;
while( *buffp++ != NULL )
{
switch(*buffp)
{
case '"':
if( *(buffp-1) != '\\') /* \" is not end */
end = 1;
else if( *(buffp-2) == '\\') /* \\" is end */
end = 1;
break;
case '%':
if( *(buffp-1) != '%')
pcntnum++;
else
{
pcntnum--; /* delete first % of pair */
continue;
}
if( *(buffp+1) == '*' )
{
if( fu[fi].scan == 1)
{
pcntnum--; /* no matching variable */
}
else
{
pcntnum++;
}
}
/*######################
# load array with var type
######################*/
/* throw away justification*/
if( *(buffp +1 ) == '-' || *(buffp +1) == '+')
buffp++;
/* remove blanks and sharps */
while( isspace( *(buffp + 1)) || *(buffp + 1) == '#' )
buffp++;
/* throw away size spec */
while( isdigit(*(buffp+1)) || *(buffp+1) == '.' )
buffp++;
switch (*(buffp + 1) ) /* look at next char for type */
{
case 's':
case '[':
case 'c':
vartype[pcntnum] = 's';
break;
case 'd':
case 'l':
case 'h':
case 'i':
case 'o':
case 'x':
case 'u':
case 'f':
case 'e':
case 'g':
vartype[pcntnum] = 'i';
break;
case '%': /* no action */
case '*':
break;
default:
printf("error at %s\n\n", buffp + 1);
return(8);
break;
}
break;
}
if( end )
break;
}
parens = 0;
inquotes = 1; /* start on last " */
c = 1;
while( *buffp != NULL ) /* look for variables */
{
switch( *buffp )
{
case '(':
parens--;
break;
case ')':
parens++;
break;
case '"': /* for strings */
if( parens == 0) /* at the base level */
{
if( *(buffp-1) != '\\' ) /* \" is not end of str */
{
inquotes = !inquotes;
}
else if( *(buffp-2) == '\\') /* \\" is end */
inquotes = !inquotes;
}
break;
case ',':
if( parens == 0 && inquotes != 1) /* commas outside of func*/
{
pcntnum --;
buffp++;
while( isspace(*buffp) )
buffp++;
if( *buffp == NULL)
return(100);
if( fu[fi].scan == 1 )
{
if( vartype[c] == 'i' && *buffp != '\&' )
return(6);
}
else if( vartype[c] == 'i' && *buffp == '"')
return(7);
c++;
continue; /* buffp already ++ */
}
break;
}
if( parens == 1) /* hit end of function */
{
if( pcntnum > 0 )
return(1);
if( pcntnum < 0)
return(5);
return(0);
}
buffp++;
}
if( parens < 1 )
return(100);
return(0);
} /* end of parsedo() */
@\Rogue\Monster\
else
echo "shar: Will not over write VARLINT.C"
fi
# to concatenate archives, remove anything after this line
exit 0