argument parsing cleanups, dynamic program file allocation, fpe error enhancement. (#72)

* - enhance fpe handler to print the error type
- cleanup argument parsing
- dynamically allocate program filename array

* bison uses enums now, not #define's, make it work with that.

* We need to use either the enums or the defines but not both. This
is because bison -y will create both enums and #defines, while bison
without -y produces only the enums, and byacc produces just #defines.

* fix indentation

* Set the tokentype when we have a match in the scan, and reset it later
when we decide that the match was bad. Fixes nbyacc.

* - don't use pattern rules for portability
- try to move both flavors of generated names for portability

* Amend tests for the new error messages
This commit is contained in:
zoulasc 2020-02-18 14:20:27 -05:00 committed by GitHub
parent e9c99065fd
commit 94e4c04561
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 122 additions and 69 deletions

5
lib.c
View File

@ -584,11 +584,6 @@ void SYNTAX(const char *fmt, ...)
eprint();
}
void fpecatch(int n)
{
FATAL("floating point exception %d", n);
}
extern int bracecnt, brackcnt, parencnt;
void bracecheck(void)

137
main.c
View File

@ -45,14 +45,40 @@ char *lexprog; /* points to program argument if it exists */
extern int errorflag; /* non-zero if any syntax errors; set by yyerror */
enum compile_states compile_time = ERROR_PRINTING;
#define MAX_PFILE 20 /* max number of -f's */
char *pfile[MAX_PFILE]; /* program filenames from -f's */
int npfile = 0; /* number of filenames */
int curpfile = 0; /* current filename */
static char **pfile; /* program filenames from -f's */
static size_t maxpfile; /* max program filename */
static size_t npfile; /* number of filenames */
static size_t curpfile; /* current filename */
bool safe = false; /* true => "safe" mode */
static __attribute__((__noreturn__)) void fpecatch(int n
#ifdef SA_SIGINFO
, siginfo_t *si, void *uc
#endif
)
{
#ifdef SA_SIGINFO
static const char *emsg[] = {
[0] = "Unknown error",
[FPE_INTDIV] = "Integer divide by zero",
[FPE_INTOVF] = "Integer overflow",
[FPE_FLTDIV] = "Floating point divide by zero",
[FPE_FLTOVF] = "Floating point overflow",
[FPE_FLTUND] = "Floating point underflow",
[FPE_FLTRES] = "Floating point inexact result",
[FPE_FLTINV] = "Invalid Floating point operation",
[FPE_FLTSUB] = "Subscript out of range",
};
#endif
FATAL("floating point exception"
#ifdef SA_SIGINFO
": %s\n", (size_t)si->si_code < sizeof(emsg) / sizeof(emsg[0]) &&
emsg[si->si_code] ? emsg[si->si_code] : emsg[0]
#endif
);
}
/* Can this work with recursive calls? I don't think so.
void segvcatch(int n)
{
@ -60,9 +86,34 @@ void segvcatch(int n)
}
*/
static const char *
setfs(char *p)
{
/* wart: t=>\t */
if (p[0] == 't' && p[1] == '\0')
return "\t";
else if (p[0] != '\0')
return p;
return NULL;
}
static char *
getarg(int *argc, char ***argv, const char *msg)
{
if ((*argv)[1][2] != '\0') { /* arg is -fsomething */
return &(*argv)[1][2];
} else { /* arg is -f something */
(*argc)--; (*argv)++;
if (*argc <= 1)
FATAL("%s", msg);
return (*argv)[1];
}
}
int main(int argc, char *argv[])
{
const char *fs = NULL;
char *fn, *vn;
setlocale(LC_CTYPE, "");
setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
@ -73,19 +124,30 @@ int main(int argc, char *argv[])
cmdname);
exit(1);
}
signal(SIGFPE, fpecatch);
#ifdef SA_SIGINFO
{
struct sigaction sa;
sa.sa_sigaction = fpecatch;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
(void)sigaction(SIGFPE, &sa, NULL);
}
#else
(void)signal(SIGFPE, fpecatch);
#endif
/*signal(SIGSEGV, segvcatch); experiment */
/* Set and keep track of the random seed */
srand_seed = 1;
srandom((unsigned long) srand_seed);
yyin = NULL;
symtab = makesymtab(NSYMTAB/NSYMTAB);
while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
if (strcmp(argv[1],"-version") == 0 || strcmp(argv[1],"--version") == 0) {
if (strcmp(argv[1], "-version") == 0 ||
strcmp(argv[1], "--version") == 0) {
printf("awk %s\n", version);
exit(0);
break;
return 0;
}
if (strcmp(argv[1], "--") == 0) { /* explicit end of args */
argc--;
@ -98,50 +160,27 @@ int main(int argc, char *argv[])
safe = true;
break;
case 'f': /* next argument is program filename */
if (argv[1][2] != 0) { /* arg is -fsomething */
if (npfile >= MAX_PFILE - 1)
FATAL("too many -f options");
pfile[npfile++] = &argv[1][2];
} else { /* arg is -f something */
argc--; argv++;
if (argc <= 1)
FATAL("no program filename");
if (npfile >= MAX_PFILE - 1)
FATAL("too many -f options");
pfile[npfile++] = argv[1];
}
break;
fn = getarg(&argc, &argv, "no program filename");
if (npfile >= maxpfile) {
maxpfile += 20;
pfile = realloc(pfile, maxpfile * sizeof(*pfile));
if (pfile == NULL)
FATAL("error allocating space for "
"-f options");
}
pfile[npfile++] = fn;
break;
case 'F': /* set field separator */
if (argv[1][2] != 0) { /* arg is -Fsomething */
if (argv[1][2] == 't' && argv[1][3] == 0) /* wart: t=>\t */
fs = "\t";
else if (argv[1][2] != 0)
fs = &argv[1][2];
} else { /* arg is -F something */
argc--; argv++;
if (argc > 1 && argv[1][0] == 't' && argv[1][1] == 0) /* wart: t=>\t */
fs = "\t";
else if (argc > 1 && argv[1][0] != 0)
fs = &argv[1][0];
}
if (fs == NULL || *fs == '\0')
fs = setfs(getarg(&argc, &argv, "no field separator"));
if (fs == NULL)
WARNING("field separator FS is empty");
break;
case 'v': /* -v a=1 to be done NOW. one -v for each */
if (argv[1][2] != 0) { /* arg is -vsomething */
if (isclvar(&argv[1][2]))
setclvar(&argv[1][2]);
else
FATAL("invalid -v option argument: %s", &argv[1][2]);
} else { /* arg is -v something */
argc--; argv++;
if (argc <= 1)
FATAL("no variable name");
if (isclvar(argv[1]))
setclvar(argv[1]);
else
FATAL("invalid -v option argument: %s", argv[1]);
}
vn = getarg(&argc, &argv, "no variable name");
if (isclvar(vn))
setclvar(vn);
else
FATAL("invalid -v option argument: %s", vn);
break;
case 'd':
dbg = atoi(&argv[1][2]);

View File

@ -35,7 +35,7 @@ CC = $(HOSTCC) # change this is cross-compiling.
# yacc options. pick one; this varies a lot by system.
#YFLAGS = -d -S
YACC = bison -d -y
YACC = bison -d
#YACC = yacc -d
# -S uses sprintf in yacc parser instead of sprint
@ -55,15 +55,15 @@ a.out: ytab.o $(OFILES)
$(OFILES): awk.h ytab.h proto.h
#Clear dependency for parallel build: (make -j)
#YACC generated y.tab.c and y.tab.h at the same time
#this needs to be a static pattern rules otherwise multiple target
#are mapped onto multiple executions of yacc, which overwrite
#each others outputs.
y%.c y%.h: awk.h proto.h awkgram.y
# Clear dependency for parallel build: (make -j)
# Depending if we used yacc or bison we can be generating different names
# ({awkgram,y}.tab.{c,h}) so try to move both. We could be using -p to
# specify the output prefix, but older yacc's don't support it.
ytab.c ytab.h: awk.h proto.h awkgram.y
$(YACC) $(YFLAGS) awkgram.y
mv y.$*.c y$*.c
mv y.$*.h y$*.h
-@for i in c h; do for j in awkgram y; do \
if [ -f "$$j.tab.$$i" ]; then mv $$j.tab.$$i ytab.$$i; fi; \
done; done
ytab.h: ytab.c

View File

@ -118,6 +118,7 @@ int main(int argc, char *argv[])
char c;
FILE *fp;
char buf[200], name[200], def[200];
enum { TOK_UNKNOWN, TOK_ENUM, TOK_DEFINE } tokentype = TOK_UNKNOWN;
printf("#include <stdio.h>\n");
printf("#include \"awk.h\"\n");
@ -135,12 +136,28 @@ int main(int argc, char *argv[])
i = 0;
while (fgets(buf, sizeof buf, fp) != NULL) {
// 199 is sizeof(def) - 1
n = sscanf(buf, "%1c %199s %199s %d", &c, def, name, &tok);
if (c != '#' || (n != 4 && strcmp(def,"define") != 0)) /* not a valid #define */
continue;
if (strcmp(name, "YYSTYPE_IS_DECLARED") == 0)
if (tokentype != TOK_ENUM) {
n = sscanf(buf, "%1c %199s %199s %d", &c, def, name,
&tok);
if (c == '#' && n == 4 && strcmp(def, "define") == 0) {
tokentype = TOK_DEFINE;
} else if (tokentype != TOK_UNKNOWN) {
continue;
}
}
if (tokentype != TOK_DEFINE) {
/* not a valid #define, bison uses enums now */
n = sscanf(buf, "%199s = %d,\n", name, &tok);
if (n != 2)
continue;
tokentype = TOK_ENUM;
}
if (strcmp(name, "YYSTYPE_IS_DECLARED") == 0) {
tokentype = TOK_UNKNOWN;
continue;
}
if (tok < FIRSTTOKEN || tok > LASTTOKEN) {
tokentype = TOK_UNKNOWN;
/* fprintf(stderr, "maketab funny token %d %s ignored\n", tok, buf); */
continue;
}

View File

@ -133,7 +133,6 @@ extern int refldbld(const char *, const char *);
extern void recbld(void);
extern Cell *fieldadr(int);
extern void yyerror(const char *);
extern void fpecatch(int);
extern void bracecheck(void);
extern void bcheck2(int, int, int);
extern void SYNTAX(const char *, ...)

View File

@ -18,4 +18,7 @@ $awk -zz 'BEGIN{}' >foo 2>&1
grep 'unknown option' foo >/dev/null || echo 'T.flags: bad unknown option'
$awk -F >foo 2>&1
grep 'field separator.*empty' foo >/dev/null || echo 'T.flags: bad null field separator'
grep 'no field separator' foo >/dev/null || echo 'T.flags: bad missing field separator'
$awk -F '' >foo 2>&1
grep 'field separator FS is empty' foo >/dev/null || echo 'T.flags: bad empty field separator'