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:
parent
e9c99065fd
commit
94e4c04561
5
lib.c
5
lib.c
@ -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
137
main.c
@ -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]);
|
||||
|
18
makefile
18
makefile
@ -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
|
||||
|
||||
|
25
maketab.c
25
maketab.c
@ -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;
|
||||
}
|
||||
|
1
proto.h
1
proto.h
@ -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 *, ...)
|
||||
|
@ -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'
|
||||
|
Loading…
Reference in New Issue
Block a user