home *** CD-ROM | disk | FTP | other *** search
- /*
- * awk main programme
- *
- * Copyright (C) 1988, 1989, 1990, 1991 by Rob Duff
- * All rights reserved
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <mem.h>
- #include <alloc.h>
- #include <string.h>
- #include <setjmp.h>
- #include <signal.h>
-
- #include "awk.h"
-
- IDENT *ident; /* list of all identifiers */
-
- ITEM *nextvar; /* pointer to next unused variable */
- ITEM vartab[MAXNAME]; /* table to hold variables */
-
- ITEM *stackptr, *stacktop; /* stack pointer and top limit */
- ITEM stackbot[MAXSTACK]; /* the stack */
- ITEM fieldtab[MAXFIELD]; /* table to hold fields 0..MAXFIELD-1 */
- RULE *rules, *rulep; /* awk code pointers */
- FYLE files[MAXFILE]; /* table to hold files */
- FYLE *nextfile; /* pointer to unused file */
-
- LINK *beginact; /* list of BEGIN actions */
- LINK *beginend; /* pointer to last BEGIN action */
- LINK *endact; /* list of END actions */
- LINK *endend; /* pointer to last END action */
-
- FILE *awkfile; /* AWK program input file */
-
- char *yyname; /* program file name */
- char *awkfre; /* RE for field separator */
- char *awkname; /* data file name */
- char *blankre; /* RE for " " field separator */
-
- char fmtstr[66]; /* format string */
- char ofmtstr[66] = "\377%.6g"; /* output format string */
- char namebuf[66]; /* internal form for file name */
- char code[MAXCODE]; /* code generation buffer */
- char buffer[MAXCODE]; /* string buffer */
- char linebuf[MAXLINE] = ""; /* program input buffer */
-
- NAME *varlist; /* list of var=text options */
- NAME *varlast; /* last var in list */
- NAME *awklist; /* list of program file names */
- NAME *awklast; /* last name in list */
- char *awkstdin = "<stdin>";
-
- FSTR awkfs; /* current field separator */
- FSTR lineptr; /* program line pointer */
- FSTR nullstr = "\377"; /* standard null string */
- FSTR blankfs = "\377[ \\t\\n]+"; /* " " field separator */
- FSTR fieldbuf; /* field string storage */
-
- short trace; /* trace execution */
- short awkline; /* include line information */
- short builtin; /* number of builtin variables */
- short status, awkeof; /* exit status and EOF status */
-
- ITEM *ac, *av, *rl, *rst; /* BUILTIN variables */
- ITEM *nr, *fnr, *fs, *rs; /* BUILTIN */
- ITEM *ofs, *ors, *ofmt; /* BUILTIN */
- ITEM *nf, *fn, *nul, *subsep; /* BUILTIN */
-
- jmp_buf nextjmp, exitjmp; /* next and exit long jump buffers */
-
- void awkfpe();
- extern ELEMENT *allell(void);
-
- static void usage(void); /* error message */
- static void awkinit(void); /* initialize AWK */
- static void execute(void); /* execute an ACTION */
- static void addname(char *); /* add -f program file */
- static void addvar(char *); /* add -v var=text */
- static ITEM *buildin(char*, int, int, FSTR);
-
- int main(int argc, char *argv[])
- {
- int found; /* found wildcard file */
- char *arg0; /* program name */
- char *argn; /* data file names */
- LINK *acts; /* pointer to next action */
-
- awkinit(); /* initialize program */
- arg0 = argv[0]; /* get program name */
- argc--; argv++; /* get next argument */
- while (argc > 0 && argv[0][0] == '-') { /* parse options */
- if (argv[0][1] == 'f') { /* input file option */
- if (argv[0][2] != '\0') /* is it a separate arg? */
- addname(argv[0]+2); /* no */
- else if (argc > 0) {
- argc--; argv++; /* yes */
- addname(argv[0]); /* get next argument */
- }
- else { /* oops no more arguments */
- error("no file specified for -f", NULL);
- }
- }
- else if (argv[0][1] == 'v') { /* var=text option */
- if (argv[0][2] != '\0') /* is it a separate arg? */
- addvar(argv[0]+2); /* no */
- else if (argc > 0) {
- argc--; argv++; /* yes */
- addvar(argv[0]); /* get next argument */
- }
- else { /* oops no more arguments */
- error("no var=text specified for -v", NULL);
- }
- }
- else if (argv[0][1] == 'F') {
- argv[0][1] = LSTR; /* field separator */
- fs->sstr = argv[0] + 1; /* convert to internal form */
- awkeof = 1;
- setfs(); /* calculate RE for FS */
- yyinit();
- }
- else if (argv[0][1] == 't') {
- trace++; /* trace execution */
- }
- else if (argv[0][1] == 't') {
- awkline++; /* include lines */
- }
- else if (argv[0][1] == '-') {
- argc--; argv++; /* "--" means end of options */
- break;
- }
- else if (argv[0][1] == '\0') {
- addname(awkstdin); /* "-" means stdin */
- argc--; argv++;
- break;
- }
- else
- usage(); /* messy option */
- argc--; argv++;
- }
- if (awklist == NULL) { /* don't have -f option */
- if (argc > 0) {
- if (strpbrk(*argv, " \",/[]|<>+=;") == NULL) {
- strcpy(buffer, *argv); /* move to larger space */
- awkfile = fopen(buffer ,"r");
- if (awkfile != NULL) {
- awkeof = 0;
- yyname = *argv;
- lineptr = linebuf;
- *lineptr = '\0';
- }
- else {
- strcat(buffer, ".awk"); /* try .AWK */
- awkfile = fopen(buffer, "r");
- if (awkfile != NULL) {
- awkeof = 0;
- yyname = malloc(strlen(buffer)+1);
- strcpy(yyname, buffer);
- lineptr = linebuf;
- *lineptr = '\0';
- }
- else {
- awkeof = 1; /* no input files */
- strcpy(linebuf, *argv);
- strcat(linebuf, "\n");
- lineptr = linebuf;
- }
- }
- }
- else { /* must be a program */
- awkeof = 1; /* no input files */
- strcpy(linebuf, *argv);
- strcat(linebuf, "\n");
- lineptr = linebuf;
- }
- argc--; argv++;
- }
- else {
- awkeof = 0;
- addname(awkstdin); /* no file name for stdin */
- }
- }
- else
- awkeof = 0;
-
- yyparse(); /* compile AWK program */
- awkeof = 1; /* end of awk program */
- if (awkfile != stdin) /* close if not stdin */
- fclose(awkfile);
- deparse(); /* clean up input record */
- setargv(argc, arg0, argv); /* set ARGV and ARGC */
- if (varlist != NULL)
- getargs(varlist);
- if (setjmp(exitjmp) == 0) { /* EXIT() point */
- if (beginact != NULL) /* if we have BEGIN actions */
- if (setjmp(nextjmp) == 0) { /* set NEXT() point */
- acts = beginact;
- while (acts != NULL) { /* execute BEGIN actions */
- awkexec(acts->ccode);
- acts = acts->cnext; /* point to next action */
- }
- }
- found = 0;
- argn = getargv();
- if (rulep != NULL || endact != NULL) do {
- if (argn != NULL) {
- if (*argn == '\0') {
- argn = getargv(); /* get the next input file */
- if (argn == NULL)
- break; /* stop when no more */
- }
- if (argn[0] == '-' && argn[1] == '\0') {
- awkfile = stdin; /* check for standard input */
- awkname = "";
- argn = "";
- }
- else {
- if (awkfind(buffer, argn, 0) == NULL) {
- if (found > 0) { /* wildcard file */
- found = 0; /* no more match */
- argn = "";
- continue;
- }
- error("can't find file %s", argn);
- }
- awkfile = fopen(buffer ,"r");
- if (awkfile == NULL) /* can't open file */
- error("can't open data file", buffer);
- awkname = buffer; /* set FILENAME */
- found++;
- }
- }
- else {
- awkname = ""; /* no FILENAME for stdin */
- awkfile = stdin; /* file not specified */
- }
- namebuf[0] = TSTR; /* mark FILENAME as temporary */
- strcpy(namebuf+1, awkname);
- setfile(namebuf); /* set FILENAME and FNR */
- files[1].ffyle = awkfile;
- execute(); /* execute program */
- if (awkfile != stdin)
- fclose(awkfile); /* close opened file */
- } while (argn != NULL); /* repeat for next file */
- }
- deparse(); /* free FIELD temporaries */
- setfile(nullstr); /* clear FILENAME and FNR */
- if (setjmp(exitjmp) == 0) { /* EXIT () point in END */
- if (setjmp(nextjmp) == 0) { /* some fool may try this */
- for (acts = endact; acts != NULL; acts = acts->cnext)
- awkexec(acts->ccode); /* execute end actions */
- }
- }
- awkclose(); /* close all the FILES */
- return status; /* return exit code */
- }
-
- static void execute()
- {
- int flag;
-
- while (getrecord() > 0) { /* read a record (line) */
- rulep = rules; /* start at the first pat/act */
- if (setjmp(nextjmp) == 0) { /* NEXT() point */
- while (rulep != NULL) {
- if (rulep->start == NULL)
- rulep->seen = 1; /* no rule means always */
- else if (rulep->seen == 0 && awkexec(rulep->start) != 0)
- rulep->seen = 1; /* success means start */
- flag = rulep->seen;
- if (rulep->stop == NULL)
- rulep->seen = 0; /* no rule means once */
- else if (rulep->seen != 0 && awkexec(rulep->stop) != 0)
- rulep->seen = 0; /* success means stop */
- if (flag != 0)
- awkexec(rulep->action); /* execute action on start */
- rulep = rulep->next; /* do next rule */
- }
- }
- deparse(); /* free FIELD assignments */
- }
- }
-
- /*
- * create builtin variable
- */
- static ITEM *buildin(char *name, int class, int type, FSTR ptr)
- {
- IDENT *vp;
-
- nextvar->sclass = class;
- nextvar->stype = type;
- nextvar->sstr = ptr;
- nextvar->svalue.dval = 0;
-
- vp = malloc(sizeof(IDENT));
- vp->vitem = nextvar;
- vp->vname = name;
- vp->vfunc = NULL;
- vp->vnext = ident;
- ident = vp;
-
- builtin++;
- return nextvar++;
- }
-
- /*
- * initialize all the structures used by AWK
- */
- static void awkinit()
- {
- int i;
- ELEMENT *ep;
-
- trace = 0;
- status = 0;
- ident = NULL;
- rules = NULL;
- rulep = NULL;
- endact = NULL;
- endend = NULL;
- beginact = NULL;
- beginend = NULL;
- stackptr = stacktop = stackbot + MAXSTACK;
-
- fieldbuf = farmalloc(MAXLINE+MAXFIELD+MAXFIELD);
- builtin = 0;
- nextvar = vartab;
- nul = buildin("\"\"", ACTUAL, S_NUMBER, nullstr);
- fs = buildin("FS", ACTUAL, S_STRING, "\377 ");
- ofs = buildin("OFS", ACTUAL, S_STRING, "\377 ");
- nf = buildin("NF", ACTUAL, S_SHORT, 0);
- ofmt = buildin("OFMT", ACTUAL, S_STRING, ofmtstr);
-
- rs = buildin("RS", ACTUAL, S_STRING, "\377\n");
- ors = buildin("ORS", ACTUAL, S_STRING, "\377\n");
- nr = buildin("NR", ACTUAL, S_SHORT, 0);
- fnr = buildin("FNR", ACTUAL, S_SHORT, 0);
- fn = buildin("FILENAME", ACTUAL, S_STRING, namebuf);
-
- av = buildin("ARGV", ACTUAL, S_STRING, nullstr);
- ac = buildin("ARGC", ACTUAL, S_STRING, nullstr);
-
- rl = buildin("RLENGTH", ACTUAL, S_STRING, nullstr);
- rst = buildin("RSTART", ACTUAL, S_STRING, nullstr);
- subsep = buildin("SUBSEP", ACTUAL, S_STRING, "\377\034");
-
- for (i = builtin; i < MAXNAME; i++) {
- ep = allell(); /* this stuff allows undefined */
- ep->aclass = ACTUAL; /* variables to be passed to */
- ep->atype = S_NUMBER; /* functions as arrays */
- ep->astr = nullstr;
- ep->avalue.dval = 0;
- ep->aindex = nullstr;
- ep->anext = NULL;
- vartab[i].sclass = ACTUAL;
- vartab[i].stype = S_ARRAY;
- vartab[i].sstr = nullstr;
- vartab[i].svalue.aptr = ep;
- }
- for (i = 0; i < MAXFIELD; i++) {
- fieldtab[i].sclass = ACTUAL;
- fieldtab[i].stype = S_STRING;
- fieldtab[i].sstr = nullstr;
- }
- nextfile = files + 2;
- files[0].fname = "\377<stdout>";
- files[0].fmode = "w";
- files[0].ffyle = stdout;
- files[1].fname = "\377<stdin>";
- files[1].fmode = "r";
- files[1].ffyle = stdin;
- for (i = 2; i < MAXFILE; i++) {
- files[i].fname = nullstr;
- files[i].fmode = "";
- files[i].ffyle = NULL;
- }
- awkeof = 1;
- yyinit();
- lineptr = blankfs+1;
- blankre = regexp(2);
- *lineptr = '\0';
- yyinit();
- awkfs = blankfs;
- awkfre = blankre;
- awkfile = stdin;
- awkname = NULL;
- awklist = NULL;
- awklast = NULL;
- varlist = NULL;
- varlast = NULL;
- lineptr = NULL;
- yyname = NULL;
- yyline = 0;
-
- signal(SIGFPE, awkfpe);
- }
-
- /*
- * display an error message and leave
- */
- void
- error(char *fmt, ...)
- {
- va_list ap;
-
- va_start(ap, fmt);
- fprintf(stderr, "awk ERROR");
- if (yyname)
- fprintf(stderr, " %s", yyname);
- if (yyline)
- fprintf(stderr, " line %d", yyline);
- fprintf(stderr, ": ");
- vfprintf(stderr, fmt, ap);
- fprintf(stderr, "\n");
- va_end(ap)
- exit(1);
- }
-
- static void addname(char *name)
- {
- NAME *list;
-
- list = malloc(sizeof(NAME));
- list->name = name;
- list->next = NULL;
- if (awklast == NULL)
- awklist = list;
- else
- awklast->next = list;
- awklast = list;
- }
-
- static void addvar(char *name)
- {
- NAME *list;
-
- list = malloc(sizeof(NAME));
- list->name = name;
- list->next = NULL;
- if (varlast == NULL)
- varlist = list;
- else
- varlast->next = list;
- varlast = list;
- }
-
- void awkfpe()
- {
- error("floating point exception", NULL);
- }
-
- /*
- * print a help message about how to use AWK and exit
- */
- static void usage()
- {
- fprintf(stderr, " AWK version 3.20 (22-May-91)\n\n");
- fprintf(stderr, " Copyright (C) 1990, 91 by Rob Duff\n");
- fprintf(stderr, " Vancouver BC Canada V5N 1Y9\n\n");
- fprintf(stderr, "usage: awk [-l] [-t] [-f name] [-v var=text] [-Ffs] [--] [prog] [data ... ]\n");
- fprintf(stderr, " -l include line number tracing for debugging\n");
- fprintf(stderr, " -t will trace execution of the program\n");
- fprintf(stderr, " -f specify program file\n");
- fprintf(stderr, " name is the name of a file containing an AWK program. You may\n");
- fprintf(stderr, " specify multiple program files with multiple -f options\n");
- fprintf(stderr, " A -f name and prog program definition may not be mixed\n");
- fprintf(stderr, " -v assign var before any BEGIN blocks\n");
- fprintf(stderr, " -F specify a pattern to match for field separation\n");
- fprintf(stderr, " fs is the pattern to match for field separation\n");
- fprintf(stderr, " -- ends the option list.\n\n");
- fprintf(stderr, " prog is a quoted program or a file name with optional [.AWK]\n");
- fprintf(stderr, " you may specify stdin with a dash (-). Not used with -f.\n");
- fprintf(stderr, " data is any file name including wildcards. If file is of the\n");
- fprintf(stderr, " form var=text then text is assigned to var in the program\n");
- fprintf(stderr, " instead of reading a file.\n");
- fprintf(stderr, " If no options are specified then the program and data are both\n");
- fprintf(stderr, " read from stdin. You end the program with a line containing\n");
- fprintf(stderr, " only a single period (.) in the first column.\n\n");
- exit(1);
- }
-
-