various improvements

This commit is contained in:
dick 1988-07-08 22:24:06 +00:00
parent 0505a65a74
commit 9dd080ae4b
5 changed files with 470 additions and 298 deletions

View file

@ -1,3 +1,28 @@
8-Jul-88 Dick Grune (dick) at dick
Added a special meta-type (also in lpaas1) for non-negative
constant actual arguments, because these may match both the signed
and unsigned versions of the type.
4-Jul-88 Dick Grune (dick) at dick
Added a scope number, to avoid problems with static object defined
in a .h file and used in the main file. These should match but
they didn't, since scope was checked by comparing file names.
28-Jun-88 Dick Grune (dick) at dick
Do not print messages that originate from files whose names start
with a /.
24-Jun-88 Dick Grune (dick) at dick
Introduced a format %L to print location indications like
"aap.c", line 31. report() does a printf-like analysis now.
18-Jun-88 Dick Grune (dick) at dick
Rewrote messages (originals were copies of lint).
13-Jun-88 Dick Grune (dick) at dick
Moved the file name to the end of the intermediate records, to
avoid problems with files with : in their names.
11-May-88 Dick Grune (dick) at dick
Received sources from Frans Kunst.

View file

@ -19,18 +19,27 @@ CFLAGS = -I$(EMHOME)/modules/h -I$(EMHOME)/modules/pkg -I/usr/include
.str.h:
$(LPASS1)/make.allocd <$*.str >$*.h
lpass2: lpass2.o makefile next.o
$(CC) $(COPTIONS) $(LDFLAGS) lpass2.o next.o $(LLIBS) -o lpass2
SRC = lpass2.c read.c
OBJ = lpass2.o read.o
test: lpass2
make lint
lpass2: $(OBJ) Makefile next.o
$(CC) $(COPTIONS) $(LDFLAGS) $(OBJ) next.o $(LLIBS) -o lpass2
size lpass2
lint:
lint $(CFLAGS) lpass2.c next.c
../lint $(CFLAGS) $(SRC) next.c #???
next.c: inpdef.str
$(LPASS1)/make.next inpdef.str > next.c
tags: $(SRC)
ctags $(SRC)
clean:
rm -f a.out core next.c inpdef.h lpass2.o next.o
rm -f a.out core next.c inpdef.h $(OBJ) next.o
#----------------------------------------------------------------
lpass2.o: ../lpass1/errout.h ../lpass1/lint.h ../lpass1/manifest.h inpdef.h
lpass2.o: ../lpass1/errout.h ../lpass1/lint.h ../lpass1/l_class.h inpdef.h

View file

@ -8,7 +8,7 @@ struct inpdef {
int id_class;
char id_name[NAMESIZE];
char id_file[FNAMESIZE];
unsigned int id_line;
int id_line;
int id_nrargs;
char id_argtps[ARGTPSSIZE];
int id_returns;
@ -19,4 +19,6 @@ struct inpdef {
int id_voided;
};
#define is_def(id) (is_def_class(id->id_class))
/* ALLOCDEF "inpdef" 10 */

View file

@ -1,31 +1,27 @@
#include "../lpass1/lint.h"
#include "../lpass1/manifest.h"
#include "inpdef.h"
#include <ctype.h> /* for efficient definition of isdigit */
#include <varargs.h>
#define INP_NPUSHBACK 2
#include "../lpass1/l_lint.h"
#include "../lpass1/l_class.h"
#include "inpdef.h"
#include <inp_pkg.spec>
#include <inp_pkg.body>
#include <alloc.h>
#include <system.h>
#define min(a, b) ((a) <= (b) ? (a) : (b))
#define same_name() !strcmp(cur_name, next_id->id_name)
#define MSGOUT STDERR /* filedes on which to write the messages */
#define ERROUT STDERR /* filedes on which to write the panics */
#define ReadUnsigned ReadInt /* for the time being ??? */
#define streq(s1,s2) (strcmp(s1, s2) == 0)
#define min(a,b) ((a) <= (b) ? (a) : (b))
#define type_equal(s1,s2) (streq(s1, s2))
char *cur_name;
struct inpdef *next_id,
struct inpdef *dot_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--
#define same_name() streq(cur_name, dot_id->id_name)
main(argc, argv)
char *argv[];
@ -33,10 +29,12 @@ main(argc, argv)
struct inpdef *id;
init(argc, argv);
while (next_id) {
cur_name = next_id->id_name;
get_dot_id();
while (dot_id) {
cur_name = dot_id->id_name;
read_defs();
while (next_id && same_name()) {
while (dot_id && same_name()) {
id = id_read();
check(id);
free_inpdef(id);
@ -53,46 +51,47 @@ init(argc, argv)
char *argv[];
{
/* Prepare standard input for reading using the input-package
* Read first inpdef into next_id
* Read first inpdef into dot_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++)
while (argc > 1 && *argv[1] == '-') {
switch (argv[1][1]) {
case 'u':
/* don't give warnings like "used but not defined"
/* don't report situations like "used but not defined"
* and "defined but never used"
*/
case 'X': /* ??? prints incoming inpdefs */
options[argv[1][1]] = 1;
break;
default:
/* ready to extend */
break;
}
argc--, argv++;
}
if (!InsertFile((char *)0, table, &result))
panic("InsertFile() fails");
}
read_defs()
{
struct inpdef *id;
while (next_id && same_name() && next_id->id_class <= LVDF) {
if (ext_def || static_def)
panic("read_defs: slate not clean");/*???*/
while (dot_id && same_name() && is_def(dot_id)) {
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);
report("%L: %s also defined at %L",
id, id->id_name, ext_def);
free_inpdef(id);
}
else {
@ -102,8 +101,8 @@ read_defs()
case SFDF:
case SVDF:
if (ext_def) {
warning("%s multiply defined", id->id_name);
places(id, ext_def);
report("%L: %s also defined at %L",
id, id->id_name, ext_def);
free_inpdef(id);
}
else {
@ -116,12 +115,9 @@ read_defs()
/* 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);
if (is_lib_class(ext_def->id_class)) {
report("%L: %s redefined in library %L",
id, id->id_name, ext_def);
}
free_inpdef(id);
}
@ -138,160 +134,29 @@ read_defs()
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.
/* Returns the value of dot_id if present, 0 otherwise.
* Reads a new inpdef ahead, to which dot_id will be pointing.
* Cur_name will be pointing to id_name of the returned inpdef.
*/
struct inpdef *old_id;
if (!next_id)
if (!dot_id)
return (0);
old_id = next_id;
old_id = dot_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;
}
get_dot_id();
return (old_id);
}
int
get_id(id)
struct inpdef *id;
get_dot_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;
/* Allocates a new inpdef, calls it dot_id and fills it */
dot_id = new_inpdef();
if (!get_id(dot_id)) {
free_inpdef(dot_id);
cur_name = "";
dot_id = 0;
}
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();
@ -308,73 +173,69 @@ check(id)
switch (id->id_class) {
case EFDC:
if (!idef) {
uwarning("%s declared but never defined", id->id_name);
unewline();
if (!options['u']) {
report("%L: %s declared but never defined",
id, id->id_name);
}
discard_defs();
break;
}
if (idef->id_class == EVDF || idef->id_class == LVDF) {
warning("%s value declared inconsistently",
id->id_name);
places(id, idef);
report("%L: function %s declared as variable at %L",
id, id->id_name, idef);
break;
}
if (strcmp(id->id_type, idef->id_type)) {
warning("%s value declared inconsistently",
id->id_name);
places(id, idef);
if (!type_equal(id->id_type, idef->id_type)) {
report("%L: value of function %s declared differently at %L",
id, id->id_name, idef);
}
break;
case EVDC:
if (!idef) {
uwarning("%s declared but never defined", id->id_name);
unewline();
if (!options['u']) {
report("%L: %s declared but never defined",
id, id->id_name);
}
discard_defs();
break;
}
if (idef->id_class == EFDF || idef->id_class == LFDF) {
warning("%s value declared inconsistently",
id->id_name);
places(id, idef);
report("%L: variable %s declared as function at %L",
id, id->id_name, idef);
break;
}
if (strcmp(id->id_type, idef->id_type)) {
warning("%s value declared inconsistently",
id->id_name);
places(id, idef);
if (!type_equal(id->id_type, idef->id_type)) {
report("%L: variable %s declared differently at %L",
id, id->id_name, 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);
report("%L: function %s declared as variable at %L",
id, id->id_name, idef);
break;
}
if (strcmp(id->id_type, idef->id_type)) {
warning("%s implicitly declared inconsistently",
id->id_name);
places(id, idef);
if (!type_equal(id->id_type, idef->id_type)) {
report("%L: function value of %s declared differently at %L",
id, id->id_name, idef);
}
break;
case FC:
case VU:
if (!idef) {
uwarning("%s used but not defined", id->id_name);
unewline();
if (!options['u']) {
report("%L: function %s used but not defined",
id, id->id_name);
}
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);
report("%L: value of %s is used, but none is returned at %L",
id, id->id_name, idef);
}
switch (id->id_returns) {
case USED:
@ -390,6 +251,25 @@ check(id)
panic("invalid id->id_returns in check");
}
break;
case VU:
if (!idef) {
if (!options['u']) {
report("%L: variable %s used but not defined",
id, id->id_name);
}
discard_defs();
break;
}
idef->id_called = 1;
break;
case EFDF:
case SFDF:
case EVDF:
case SVDF:
case LFDF:
case LVDF:
panic("check() called for a definition");
break;
default:
panic("invalid class (%c) in check", id->id_class);
break;
@ -402,7 +282,7 @@ discard_defs()
struct inpdef *id;
while (next_id && same_name()) {
while (dot_id && same_name()) {
id = id_read();
free_inpdef(id);
}
@ -411,46 +291,88 @@ discard_defs()
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;
register char *act_tp = id->id_argtps;
register char *def_tp = idef->id_argtps;
register int i;
register int nrargs; /* # of args to be type-checked */
register int varargs;
/* determine nrargs */
if (idef->id_nrargs < 0) {
varargs = 1;
n = -idef->id_nrargs - 1;
nrargs = -idef->id_nrargs - 1;
}
else {
n = idef->id_nrargs;
varargs = 0;
nrargs = idef->id_nrargs;
}
/* adjust nrargs, if necessary */
if (varargs) {
if (n > id->id_nrargs) {
warning("%s variable # of args", id->id_name);
places(id, idef);
n = id->id_nrargs;
if (nrargs > id->id_nrargs) {
report("%L: number of args to %s differs from %L",
id, id->id_name, idef);
nrargs = 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);
if (nrargs != id->id_nrargs) {
report("%L: number of args to %s differs from %L",
id, id->id_name, idef);
nrargs = min(nrargs, id->id_nrargs);
}
}
for (i = 1; i <= n; i++) {
while (*argtps1 == *argtps2 && *argtps1 != ':') {
argtps1++;
argtps2++;
for (i = 1; i <= nrargs; i++) {
register char *act = act_tp;
register char *def = def_tp;
/* isolate actual argument type */
while (*act_tp) {
if (*act_tp == ':') {
*act_tp = '\0';
break;
}
act_tp++;
}
if (*argtps1 != *argtps2) {
warning("%s, arg %d used inconsistently",
id->id_name, i);
places(id, idef);
/* isolate formal argument type */
while (*def_tp) {
if (*def_tp == ':') {
*def_tp = '\0';
break;
}
def_tp++;
}
while (*argtps1++ != ':') ;
while (*argtps2++ != ':') ;
if (!type_match(act, def)) {
report("%L: arg %d of %s differs from that at %L",
id, i, id->id_name, idef);
}
*act_tp++ = ':';
*def_tp++ = ':';
}
}
int
type_match(act, def)
char *act, *def;
{
if (type_equal(act, def))
return 1;
if (act[0] == '+') {
/* a non-negative constant */
/* might be signed or unsigned */
if (type_equal(&act[1], def))
return 1;
if ( strncmp(def, "unsigned ", 9)
&& type_equal(&act[1], &def[10])
) {
return 1;
}
}
return 0;
}
check_usage()
{
/* Checks if the defined function or variable is used.
@ -470,24 +392,27 @@ check_usage()
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_called) {
if (streq(id->id_name, "main")) {
/* silent */
}
else if (ext_def && is_lib_class(ext_def->id_class)) {
/* silent */
}
else {
if (!options['u']) {
report("%L: %s defined but never used",
id, id->id_name);
}
}
}
if ( id->id_class == EFDF
|| id->id_class == SFDF
|| id->id_class == LFDF
) {
if (is_fundef_class(id->id_class)) {
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();
report("%L: %s returns value which is %s ignored",
id, id->id_name,
(id->id_used || id->id_voided) ?
"sometimes" : "always");
}
}
}
@ -513,7 +438,7 @@ definition(id)
struct inpdef *sd = static_def;
while (sd) {
if (!strcmp(id->id_file, sd->id_file))
if (id->id_statnr == sd->id_statnr)
return (sd);
sd = sd->next;
}
@ -536,46 +461,74 @@ free_defs()
}
}
#include "../lpass1/errout.h" /* for definition of ERROUT */
places(id1, id2)
struct inpdef *id1, *id2;
/* VARARGS */
report(va_alist)
va_dcl
{
fprint(ERROUT, "\t%s(%u) :: %s(%u)\n",
id1->id_file, id1->id_line, id2->id_file, id2->id_line);
}
va_list ap;
/* VARARGS1 */
warning(fmt, args)
char *fmt;
{
doprnt(ERROUT, fmt, &args);
}
va_start(ap);
{
char *fmt = va_arg(ap, char*);
register char *f = fmt;
register char fc;
/* VARARGS1 */
uwarning(fmt, args)
char *fmt;
{
if (!options['u'])
doprnt(ERROUT, fmt, &args);
}
/* First see if the first arg is an inpdef with
a global file name; if so, skip this message.
*/
if (f[0] == '%' && f[1] == 'L') {
/* it is an inpdef */
register struct inpdef *id =
va_arg(ap, struct inpdef *);
newline()
{
fprint(ERROUT, "\n");
}
unewline()
{
if (!options['u'])
fprint(ERROUT, "\n");
f += 2;
/* is the file name global? */
if (id->id_file[0] == '/')
return;
/* if no, we have used up the argument,
so print it here
*/
fprint(MSGOUT, "\"%s\", line %d",
id->id_file, id->id_line);
}
while ((fc = *f++)) {
if (fc == '%') {
switch (*f++) {
register struct inpdef *id;
register char *s;
register int i;
case 'L': /* a location item */
id = va_arg(ap, struct inpdef *);
fprint(MSGOUT, "\"%s\", line %d",
id->id_file, id->id_line);
break;
case 's': /* a string item */
s = va_arg(ap, char *);
fprint(MSGOUT, "%s", s);
break;
case 'd': /* an int item */
i = va_arg(ap, int);
fprint(MSGOUT, "%d", i);
break;
default:
panic("bad format %s", fmt);
break;
}
}
else {
fprint(MSGOUT, "%c", fc);
}
}
fprint(MSGOUT, "\n");
}
va_end(ap);
}
/* VARARGS1 */
panic(fmt, args)
char *fmt;
{
fprint(ERROUT, "PANIC: ");
fprint(ERROUT, "PANIC, lint, pass2: ");
doprnt(ERROUT, fmt, &args);
fprint(ERROUT, "\n");
exit(1);
@ -585,9 +538,21 @@ panic(fmt, args)
print_id(id)
struct inpdef *id;
{
print("%c, %s, %s, %u, %d, %s, %d, %s\n",
id->id_class,
print("inpdef: %s, %s, %04d, \"%s\", %d, %d, %s, %d, %s\n",
id->id_class == EFDF ? "EFDF" :
id->id_class == SFDF ? "SFDF" :
id->id_class == EVDF ? "EVDF" :
id->id_class == SVDF ? "SVDF" :
id->id_class == LFDF ? "LFDF" :
id->id_class == LVDF ? "LVDF" :
id->id_class == EFDC ? "EFDC" :
id->id_class == EVDC ? "EVDC" :
id->id_class == IFDC ? "IFDC" :
id->id_class == FC ? "FC" :
id->id_class == VU ? "VU" :
id->id_class == ERRCL ? "ERRCL" : "<BADCLASS>",
id->id_name,
id->id_statnr,
id->id_file,
id->id_line,
id->id_nrargs,
@ -595,3 +560,4 @@ print_id(id)
id->id_returns,
id->id_type);
}

170
lang/cem/lint/lpass2/read.c Normal file
View file

@ -0,0 +1,170 @@
#include "../lpass1/l_class.h"
#include "inpdef.h"
#include <ctype.h>
#define INP_NPUSHBACK 2
#include <inp_pkg.spec>
#include <inp_pkg.body>
PRIVATE 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--
PRIVATE int ReadString();
PRIVATE int ReadInt();
PRIVATE SkipChar();
PRIVATE int ReadArgs();
int
get_id(id)
struct inpdef *id;
{
/* A low-level function which just reads a definition */
if (!ReadString(id->id_name, ':', NAMESIZE))
return 0;
if (!ReadInt(&id->id_statnr))
return 0;
SkipChar(':');
loadchar(id->id_class);
if (id->id_class == EOI)
return 0;
SkipChar(':');
switch (id->id_class) {
case EFDF:
case SFDF:
case LFDF:
case FC:
if (!ReadInt(&id->id_nrargs))
return 0;
SkipChar(':');
if (!ReadArgs(id->id_nrargs, id->id_argtps))
return 0;
if (!ReadInt(&id->id_returns))
return 0;
SkipChar(':');
break;
}
if (!ReadString(id->id_type, ':', TYPESIZE))
return 0;
if (!ReadInt(&id->id_line))
return 0;
SkipChar(':');
if (!ReadString(id->id_file, '\n', FNAMESIZE))
return 0;
{ extern char options[];
if (options['X'])
print_id(id);/*???*/
}
return (1);
}
PRIVATE int
ReadString(buf, delim, maxsize)
char *buf;
{
/* Reads a string until 'delim' is encountered.
* Delim is discarded.
* If 'maxsize-1' is exceeded, "string too long" is written by panic().
* A '\0' is appended to the string.
* At EOI 0 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 0;
if (ch == delim)
break;
buf[nread++] = (char)ch;
}
if (ch != delim) {
panic("line %d: string too long: %s", LineNr, buf);
/*NOTREACHED*/
}
buf[nread++] = '\0';
return (nread);
}
PRIVATE 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 0 is returned, else 1.
*/
int ch;
int negative = 0;
int res = 0;
do {
loadchar(ch);
} while (!isdigit(ch) && ch != '-');
if (ch == EOI)
return 0;
if (ch == '-')
negative = 1;
else
res = ch - '0';
loadchar(ch);
while (isdigit(ch)) {
res = 10*res + ch - '0';
loadchar(ch);
}
pushback(ch);
*ip = negative ? -res : res;
return 1;
}
PRIVATE SkipChar(ch)
{
int c;
loadchar(c);
if (c != ch) {
panic("line %d: bad format, '%c' expected; '%c' read",
LineNr, ch, c);
/*NOTREACHED*/
}
}
PRIVATE int
ReadArgs(nrargs, buf)
char *buf;
{
/* Reads a string into buf with format <type1>:<type2>: ... :<typeN>: */
int i;
int charcount = 1;
int n;
if (nrargs < 0) {
/* variable # of args */
nrargs = -nrargs - 1;
}
*buf = '\0';
for (i = 0; i < nrargs; i++) {
if (!ReadString(buf, ':', ARGTPSSIZE-charcount-1))
return 0;
n = strlen(buf) + 1;
charcount += n;
buf += n - 1;
*buf++ = ':';
}
*buf = '\0';
return 1;
}