Initial version
This commit is contained in:
parent
8ba1ce6f1a
commit
885d03a020
3
lang/cem/lint/lpass2/ChangeLog
Normal file
3
lang/cem/lint/lpass2/ChangeLog
Normal file
|
@ -0,0 +1,3 @@
|
|||
11-May-88 Dick Grune (dick) at dick
|
||||
Received sources from Frans Kunst.
|
||||
|
36
lang/cem/lint/lpass2/Makefile
Normal file
36
lang/cem/lint/lpass2/Makefile
Normal file
|
@ -0,0 +1,36 @@
|
|||
# M A K E F I L E F O R L P A S S 2
|
||||
|
||||
# Machine and environ dependent definitions
|
||||
EMHOME = /usr/em
|
||||
LPASS1 = ../lpass1
|
||||
|
||||
# Libraries and EM interface definitions
|
||||
SYSLIB = $(EMHOME)/modules/lib/libsystem.a
|
||||
STRLIB = $(EMHOME)/modules/lib/libstring.a
|
||||
PRTLIB = $(EMHOME)/modules/lib/libprint.a
|
||||
INPLIB = $(EMHOME)/modules/lib/libinput.a
|
||||
ALLOCLIB = $(EMHOME)/modules/lib/liballoc.a
|
||||
MALLOC = $(EMHOME)/modules/lib/malloc.o
|
||||
LLIBS = $(INPLIB) $(PRTLIB) $(STRLIB) $(ALLOCLIB) $(MALLOC) $(SYSLIB)
|
||||
|
||||
CFLAGS = -I$(EMHOME)/modules/h -I$(EMHOME)/modules/pkg -I/usr/include
|
||||
|
||||
.SUFFIXES: .str .h
|
||||
.str.h:
|
||||
$(LPASS1)/make.allocd <$*.str >$*.h
|
||||
|
||||
lpass2: lpass2.o makefile next.o
|
||||
$(CC) $(COPTIONS) $(LDFLAGS) lpass2.o next.o $(LLIBS) -o lpass2
|
||||
size lpass2
|
||||
|
||||
lint:
|
||||
lint $(CFLAGS) lpass2.c next.c
|
||||
|
||||
next.c: inpdef.str
|
||||
$(LPASS1)/make.next inpdef.str > next.c
|
||||
|
||||
clean:
|
||||
rm -f a.out core next.c inpdef.h lpass2.o next.o
|
||||
|
||||
#----------------------------------------------------------------
|
||||
lpass2.o: ../lpass1/errout.h ../lpass1/lint.h ../lpass1/manifest.h inpdef.h
|
22
lang/cem/lint/lpass2/inpdef.str
Normal file
22
lang/cem/lint/lpass2/inpdef.str
Normal file
|
@ -0,0 +1,22 @@
|
|||
#define NAMESIZE 100
|
||||
#define FNAMESIZE 100
|
||||
#define ARGTPSSIZE 2000
|
||||
#define TYPESIZE 1000
|
||||
|
||||
struct inpdef {
|
||||
struct inpdef *next;
|
||||
int id_class;
|
||||
char id_name[NAMESIZE];
|
||||
char id_file[FNAMESIZE];
|
||||
unsigned int id_line;
|
||||
int id_nrargs;
|
||||
char id_argtps[ARGTPSSIZE];
|
||||
int id_returns;
|
||||
char id_type[TYPESIZE];
|
||||
int id_called;
|
||||
int id_used;
|
||||
int id_ignored;
|
||||
int id_voided;
|
||||
};
|
||||
|
||||
/* ALLOCDEF "inpdef" 10 */
|
597
lang/cem/lint/lpass2/lpass2.c
Normal file
597
lang/cem/lint/lpass2/lpass2.c
Normal file
|
@ -0,0 +1,597 @@
|
|||
#include "../lpass1/lint.h"
|
||||
#include "../lpass1/manifest.h"
|
||||
#include "inpdef.h"
|
||||
#include <ctype.h> /* for efficient definition of isdigit */
|
||||
|
||||
#define INP_NPUSHBACK 2
|
||||
|
||||
#include <inp_pkg.spec>
|
||||
#include <inp_pkg.body>
|
||||
|
||||
#define min(a, b) ((a) <= (b) ? (a) : (b))
|
||||
#define same_name() !strcmp(cur_name, next_id->id_name)
|
||||
|
||||
#define ReadUnsigned ReadInt /* for the time being ??? */
|
||||
|
||||
char *cur_name;
|
||||
struct inpdef *next_id,
|
||||
*ext_def,
|
||||
*static_def;
|
||||
struct inpdef *id_read();
|
||||
static int LineNr = 1;
|
||||
|
||||
/* Two dangerous macro's. They replace a single statement by
|
||||
* two statements
|
||||
*/
|
||||
#define loadchar(ch) LoadChar(ch); if (ch=='\n') LineNr++
|
||||
#define pushback(ch) PushBack(); if (ch=='\n') LineNr--
|
||||
|
||||
|
||||
main(argc, argv)
|
||||
char *argv[];
|
||||
{
|
||||
struct inpdef *id;
|
||||
|
||||
init(argc, argv);
|
||||
while (next_id) {
|
||||
cur_name = next_id->id_name;
|
||||
read_defs();
|
||||
while (next_id && same_name()) {
|
||||
id = id_read();
|
||||
check(id);
|
||||
free_inpdef(id);
|
||||
}
|
||||
check_usage();
|
||||
free_defs();
|
||||
}
|
||||
}
|
||||
|
||||
char options[128];
|
||||
static char *table[] = {0};
|
||||
|
||||
init(argc, argv)
|
||||
char *argv[];
|
||||
{
|
||||
/* Prepare standard input for reading using the input-package
|
||||
* Read first inpdef into next_id
|
||||
* Parse options
|
||||
*/
|
||||
|
||||
char *result;
|
||||
|
||||
if (!InsertFile((char *)0, table, &result))
|
||||
panic("InsertFile() fails");
|
||||
next_id = new_inpdef();
|
||||
if (get_id(next_id) == EOI) {
|
||||
free_inpdef(next_id);
|
||||
next_id = 0;
|
||||
return;
|
||||
}
|
||||
for (;argc > 1 && *argv[1] == '-'; argc--, argv++)
|
||||
switch (argv[1][1]) {
|
||||
case 'u':
|
||||
/* don't give warnings like "used but not defined"
|
||||
* and "defined but never used"
|
||||
*/
|
||||
options[argv[1][1]] = 1;
|
||||
break;
|
||||
default:
|
||||
/* ready to extend */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
read_defs()
|
||||
{
|
||||
struct inpdef *id;
|
||||
|
||||
while (next_id && same_name() && next_id->id_class <= LVDF) {
|
||||
id = id_read();
|
||||
switch (id->id_class) {
|
||||
case EFDF:
|
||||
case EVDF:
|
||||
if (ext_def) {
|
||||
warning("%s multiply defined", id->id_name);
|
||||
places(id, ext_def);
|
||||
free_inpdef(id);
|
||||
}
|
||||
else {
|
||||
ext_def = id;
|
||||
}
|
||||
break;
|
||||
case SFDF:
|
||||
case SVDF:
|
||||
if (ext_def) {
|
||||
warning("%s multiply defined", id->id_name);
|
||||
places(id, ext_def);
|
||||
free_inpdef(id);
|
||||
}
|
||||
else {
|
||||
static_in_list(id);
|
||||
}
|
||||
break;
|
||||
case LFDF:
|
||||
case LVDF:
|
||||
if (ext_def) {
|
||||
/* Some libraries contain more than one
|
||||
* definition
|
||||
*/
|
||||
if ( ext_def->id_class != LFDF
|
||||
&& ext_def->id_class != LVDF
|
||||
) {
|
||||
warning("%s also defined in library",
|
||||
id->id_name);
|
||||
places(id, ext_def);
|
||||
}
|
||||
free_inpdef(id);
|
||||
}
|
||||
else {
|
||||
ext_def = id;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
panic("invalid class (%c) in read_defs", id->id_class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct inpdef *
|
||||
id_read()
|
||||
{
|
||||
/* Returns the value of next_id if present, 0 otherwise.
|
||||
* Reads a new inpdef ahead, to which next_id will be pointing.
|
||||
* Cur_name will be pointing to id_name of the returned inpdef.
|
||||
*/
|
||||
struct inpdef *old_id;
|
||||
|
||||
if (!next_id)
|
||||
return (0);
|
||||
old_id = next_id;
|
||||
cur_name = old_id->id_name;
|
||||
next_id = new_inpdef();
|
||||
if (get_id(next_id) == EOI) {
|
||||
free_inpdef(next_id);
|
||||
cur_name = "";
|
||||
next_id = 0;
|
||||
}
|
||||
return (old_id);
|
||||
}
|
||||
|
||||
int
|
||||
get_id(id)
|
||||
struct inpdef *id;
|
||||
{
|
||||
/* A low-level function which just reads a definition */
|
||||
|
||||
int eoi;
|
||||
|
||||
if (ReadString(id->id_name, ':', NAMESIZE) == EOI)
|
||||
return (EOI);
|
||||
loadchar(id->id_class);
|
||||
if (id->id_class == EOI)
|
||||
return (EOI);
|
||||
SkipChar(':');
|
||||
if (ReadString(id->id_file, ':', FNAMESIZE) == EOI)
|
||||
return (EOI);
|
||||
if (ReadUnsigned(&id->id_line) == EOI)
|
||||
return (EOI);
|
||||
SkipChar(':');
|
||||
switch (id->id_class) {
|
||||
case EFDF:
|
||||
case SFDF:
|
||||
case LFDF:
|
||||
case FC:
|
||||
if (ReadInt(&id->id_nrargs) == EOI)
|
||||
return (EOI);
|
||||
SkipChar(':');
|
||||
eoi = args_read(
|
||||
(id->id_nrargs < 0 ? -id->id_nrargs-1 : id->id_nrargs),
|
||||
id->id_argtps);
|
||||
if (eoi == EOI)
|
||||
return (EOI);
|
||||
if (ReadInt(&id->id_returns) == EOI)
|
||||
return (EOI);
|
||||
SkipChar(':');
|
||||
break;
|
||||
}
|
||||
return (ReadString(id->id_type, '\n', TYPESIZE));
|
||||
}
|
||||
|
||||
int
|
||||
ReadString(buf, delim, maxsize)
|
||||
char *buf;
|
||||
{
|
||||
/* Reads a string until 'delim' is encountered.
|
||||
* Delim is discarded.
|
||||
* If 'maxsize-1' is exeded, "string too long" is written by panic().
|
||||
* A '\0' is appended to the string.
|
||||
* At EOI EOI is returned, else the length of the string (including
|
||||
* the appended '\0') is returned.
|
||||
*/
|
||||
int ch;
|
||||
int nread = 0;
|
||||
|
||||
while (nread < maxsize - 1) {
|
||||
loadchar(ch);
|
||||
if (ch == EOI)
|
||||
return (EOI);
|
||||
if (ch == delim)
|
||||
break;
|
||||
buf[nread++] = (char)ch;
|
||||
}
|
||||
if (ch != delim) {
|
||||
buf[nread] = '\0';
|
||||
panic("line %d: string too long: %s", LineNr, buf);
|
||||
}
|
||||
buf[nread++] = '\0';
|
||||
return (nread);
|
||||
}
|
||||
|
||||
int
|
||||
ReadInt(ip)
|
||||
int *ip;
|
||||
{
|
||||
/* Reads a decimal integer until a character which is not
|
||||
* a digit is encountered.
|
||||
* Non-digits except minus-sign in front of the number are discarded.
|
||||
* Doesn't check on overflow.
|
||||
* Just a minus-sign is interpreted as 0. (To prevent a look-ahead.)
|
||||
* At EOI EOI is returned.
|
||||
*/
|
||||
int ch;
|
||||
int negative = 0;
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
loadchar(ch);
|
||||
} /* {} needed because of the loadchar-macro; yack */
|
||||
while (!isdigit(ch) && ch != '-');
|
||||
if (ch == EOI)
|
||||
return (EOI);
|
||||
if (ch == '-')
|
||||
negative = 1;
|
||||
else
|
||||
i = ch - '0';
|
||||
loadchar(ch);
|
||||
while (isdigit(ch)) {
|
||||
i = 10*i + ch - '0';
|
||||
loadchar(ch);
|
||||
}
|
||||
pushback(ch);
|
||||
*ip = negative ? -i : i;
|
||||
return (!EOI);
|
||||
}
|
||||
|
||||
SkipChar(ch)
|
||||
{
|
||||
int c;
|
||||
|
||||
loadchar(c);
|
||||
if (c != ch)
|
||||
panic("line %d: bad format, '%c' expected; '%c' read",
|
||||
LineNr, ch, c);
|
||||
}
|
||||
|
||||
int
|
||||
args_read(nrargs, buf)
|
||||
char *buf;
|
||||
{
|
||||
/* Reads a string into buf with format <type1>:<type2>: ... :<typen>: */
|
||||
|
||||
int i;
|
||||
int charcount = 1;
|
||||
int n;
|
||||
|
||||
*buf = '\0';
|
||||
for (i = 0; i < nrargs; i++) {
|
||||
if ((n = ReadString(buf, ':', ARGTPSSIZE-charcount-1)) == EOI)
|
||||
return (EOI);
|
||||
charcount += n;
|
||||
buf += n - 1;
|
||||
*buf++ = ':';
|
||||
}
|
||||
*buf = '\0';
|
||||
return (!EOI);
|
||||
}
|
||||
|
||||
struct inpdef *definition();
|
||||
|
||||
check(id)
|
||||
struct inpdef *id;
|
||||
{
|
||||
/* Checks a declaration, function call or variable usage, described by id,
|
||||
* against the definitions.
|
||||
*/
|
||||
struct inpdef *idef;
|
||||
|
||||
idef = definition(id);
|
||||
switch (id->id_class) {
|
||||
case EFDC:
|
||||
if (!idef) {
|
||||
uwarning("%s declared but never defined", id->id_name);
|
||||
unewline();
|
||||
discard_defs();
|
||||
break;
|
||||
}
|
||||
if (idef->id_class == EVDF || idef->id_class == LVDF) {
|
||||
warning("%s value declared inconsistently",
|
||||
id->id_name);
|
||||
places(id, idef);
|
||||
break;
|
||||
}
|
||||
if (strcmp(id->id_type, idef->id_type)) {
|
||||
warning("%s value declared inconsistently",
|
||||
id->id_name);
|
||||
places(id, idef);
|
||||
}
|
||||
break;
|
||||
case EVDC:
|
||||
if (!idef) {
|
||||
uwarning("%s declared but never defined", id->id_name);
|
||||
unewline();
|
||||
discard_defs();
|
||||
break;
|
||||
}
|
||||
if (idef->id_class == EFDF || idef->id_class == LFDF) {
|
||||
warning("%s value declared inconsistently",
|
||||
id->id_name);
|
||||
places(id, idef);
|
||||
break;
|
||||
}
|
||||
if (strcmp(id->id_type, idef->id_type)) {
|
||||
warning("%s value declared inconsistently",
|
||||
id->id_name);
|
||||
places(id, idef);
|
||||
}
|
||||
break;
|
||||
case IFDC:
|
||||
if (!idef)
|
||||
break; /* used but not defined */
|
||||
if (idef->id_class == EVDF || idef->id_class == LVDF) {
|
||||
warning("%s value declared inconsistently",
|
||||
id->id_name);
|
||||
places(id, idef);
|
||||
break;
|
||||
}
|
||||
if (strcmp(id->id_type, idef->id_type)) {
|
||||
warning("%s implicitly declared inconsistently",
|
||||
id->id_name);
|
||||
places(id, idef);
|
||||
}
|
||||
break;
|
||||
case FC:
|
||||
case VU:
|
||||
if (!idef) {
|
||||
uwarning("%s used but not defined", id->id_name);
|
||||
unewline();
|
||||
discard_defs();
|
||||
break;
|
||||
}
|
||||
idef->id_called = 1;
|
||||
if (id->id_class == VU)
|
||||
break;
|
||||
check_args(id, idef);
|
||||
if (id->id_returns == USED && !idef->id_returns) {
|
||||
warning("%s value is used, but none is returned",
|
||||
id->id_name);
|
||||
places(id, idef);
|
||||
}
|
||||
switch (id->id_returns) {
|
||||
case USED:
|
||||
idef->id_used = 1;
|
||||
break;
|
||||
case IGNORED:
|
||||
idef->id_ignored = 1;
|
||||
break;
|
||||
case VOIDED:
|
||||
idef->id_voided = 1;
|
||||
break;
|
||||
default:
|
||||
panic("invalid id->id_returns in check");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
panic("invalid class (%c) in check", id->id_class);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
discard_defs()
|
||||
{
|
||||
/* Read until a definition having another name */
|
||||
|
||||
struct inpdef *id;
|
||||
|
||||
while (next_id && same_name()) {
|
||||
id = id_read();
|
||||
free_inpdef(id);
|
||||
}
|
||||
}
|
||||
|
||||
check_args(id, idef)
|
||||
struct inpdef *id, *idef;
|
||||
{
|
||||
register char *argtps1 = id->id_argtps;
|
||||
register char *argtps2 = idef->id_argtps;
|
||||
int i, n, varargs = 0;
|
||||
|
||||
if (idef->id_nrargs < 0) {
|
||||
varargs = 1;
|
||||
n = -idef->id_nrargs - 1;
|
||||
}
|
||||
else {
|
||||
n = idef->id_nrargs;
|
||||
}
|
||||
if (varargs) {
|
||||
if (n > id->id_nrargs) {
|
||||
warning("%s variable # of args", id->id_name);
|
||||
places(id, idef);
|
||||
n = id->id_nrargs;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (n != id->id_nrargs) {
|
||||
warning("%s variable # of args", id->id_name);
|
||||
places(id, idef);
|
||||
n = min(n, id->id_nrargs);
|
||||
}
|
||||
}
|
||||
for (i = 1; i <= n; i++) {
|
||||
while (*argtps1 == *argtps2 && *argtps1 != ':') {
|
||||
argtps1++;
|
||||
argtps2++;
|
||||
}
|
||||
if (*argtps1 != *argtps2) {
|
||||
warning("%s, arg %d used inconsistently",
|
||||
id->id_name, i);
|
||||
places(id, idef);
|
||||
}
|
||||
while (*argtps1++ != ':') ;
|
||||
while (*argtps2++ != ':') ;
|
||||
}
|
||||
}
|
||||
|
||||
check_usage()
|
||||
{
|
||||
/* Checks if the defined function or variable is used.
|
||||
* There can be several static definitions.
|
||||
*/
|
||||
|
||||
struct inpdef *sd = static_def;
|
||||
|
||||
if (ext_def)
|
||||
check_def(ext_def);
|
||||
while (sd) {
|
||||
check_def(sd);
|
||||
sd = sd->next;
|
||||
}
|
||||
}
|
||||
|
||||
check_def(id)
|
||||
struct inpdef *id;
|
||||
{
|
||||
if ( !id->id_called && strcmp(id->id_name, "main")
|
||||
&& ext_def->id_class != LFDF
|
||||
&& ext_def->id_class != LVDF
|
||||
) {
|
||||
uwarning("%s defined (%s(%u)) but never used",
|
||||
id->id_name, id->id_file, id->id_line);
|
||||
unewline();
|
||||
}
|
||||
if ( id->id_class == EFDF
|
||||
|| id->id_class == SFDF
|
||||
|| id->id_class == LFDF
|
||||
) {
|
||||
if (id->id_returns && id->id_called && id->id_ignored) {
|
||||
warning("%s returns value which is %s ignored",
|
||||
id->id_name,
|
||||
id->id_used || id->id_voided ?
|
||||
"sometimes" : "always");
|
||||
newline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static_in_list(id)
|
||||
struct inpdef *id;
|
||||
{
|
||||
/* Put id in the list of static definitions.
|
||||
* static_def points to the first element.
|
||||
*/
|
||||
id->next = static_def;
|
||||
static_def = id;
|
||||
}
|
||||
|
||||
struct inpdef *
|
||||
definition(id)
|
||||
struct inpdef *id;
|
||||
{
|
||||
/* If there is a static definition that comes from the same file, a pointer
|
||||
* to this definition will be returned.
|
||||
* Otherwise a pointer to the ext_def is returned (which may be null).
|
||||
*/
|
||||
struct inpdef *sd = static_def;
|
||||
|
||||
while (sd) {
|
||||
if (!strcmp(id->id_file, sd->id_file))
|
||||
return (sd);
|
||||
sd = sd->next;
|
||||
}
|
||||
return (ext_def);
|
||||
}
|
||||
|
||||
free_defs()
|
||||
{
|
||||
/* Dispose the external definition and the static definitions. */
|
||||
struct inpdef *sd;
|
||||
|
||||
if (ext_def) {
|
||||
free_inpdef(ext_def);
|
||||
ext_def = 0;
|
||||
}
|
||||
while (static_def) {
|
||||
sd = static_def;
|
||||
static_def = static_def->next;
|
||||
free_inpdef(sd);
|
||||
}
|
||||
}
|
||||
|
||||
#include "../lpass1/errout.h" /* for definition of ERROUT */
|
||||
|
||||
places(id1, id2)
|
||||
struct inpdef *id1, *id2;
|
||||
{
|
||||
fprint(ERROUT, "\t%s(%u) :: %s(%u)\n",
|
||||
id1->id_file, id1->id_line, id2->id_file, id2->id_line);
|
||||
}
|
||||
|
||||
/* VARARGS1 */
|
||||
warning(fmt, args)
|
||||
char *fmt;
|
||||
{
|
||||
doprnt(ERROUT, fmt, &args);
|
||||
}
|
||||
|
||||
/* VARARGS1 */
|
||||
uwarning(fmt, args)
|
||||
char *fmt;
|
||||
{
|
||||
if (!options['u'])
|
||||
doprnt(ERROUT, fmt, &args);
|
||||
}
|
||||
|
||||
newline()
|
||||
{
|
||||
fprint(ERROUT, "\n");
|
||||
}
|
||||
|
||||
unewline()
|
||||
{
|
||||
if (!options['u'])
|
||||
fprint(ERROUT, "\n");
|
||||
}
|
||||
|
||||
/* VARARGS1 */
|
||||
panic(fmt, args)
|
||||
char *fmt;
|
||||
{
|
||||
fprint(ERROUT, "PANIC: ");
|
||||
doprnt(ERROUT, fmt, &args);
|
||||
fprint(ERROUT, "\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* for DEBUGGING */
|
||||
print_id(id)
|
||||
struct inpdef *id;
|
||||
{
|
||||
print("%c, %s, %s, %u, %d, %s, %d, %s\n",
|
||||
id->id_class,
|
||||
id->id_name,
|
||||
id->id_file,
|
||||
id->id_line,
|
||||
id->id_nrargs,
|
||||
((id->id_nrargs == 0) ? "" : id->id_argtps),
|
||||
id->id_returns,
|
||||
id->id_type);
|
||||
}
|
Loading…
Reference in a new issue