home *** CD-ROM | disk | FTP | other *** search
- /*
- * main.c -- Expression tree constructors and main program for gawk.
- */
-
- /*
- * Copyright (C) 1986, 1988, 1989 the Free Software Foundation, Inc.
- *
- * This file is part of GAWK, the GNU implementation of the
- * AWK Progamming Language.
- *
- * GAWK is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 1, or (at your option)
- * any later version.
- *
- * GAWK is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GAWK; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
- #include "awk.h"
- #include "patchlevel.h"
- #include <signal.h>
-
- extern int yyparse();
- extern void do_input();
- extern int close_io();
- extern void init_fields();
- extern int getopt();
- extern int re_set_syntax();
- extern NODE *node();
-
- static void usage();
- static void set_fs();
- static void init_vars();
- static void init_args();
- static NODE *spc_var();
- static void pre_assign();
- static void copyleft();
-
- /* These nodes store all the special variables AWK uses */
- NODE *FS_node, *NF_node, *RS_node, *NR_node;
- NODE *FILENAME_node, *OFS_node, *ORS_node, *OFMT_node;
- NODE *FNR_node, *RLENGTH_node, *RSTART_node, *SUBSEP_node;
- NODE *ENVIRON_node, *IGNORECASE_node;
- NODE *ARGC_node, *ARGV_node;
-
- /*
- * The parse tree and field nodes are stored here. Parse_end is a dummy item
- * used to free up unneeded fields without freeing the program being run
- */
- int errcount = 0; /* error counter, used by yyerror() */
-
- /* The global null string */
- NODE *Nnull_string;
-
- /* The name the program was invoked under, for error messages */
- char *myname;
-
- /* A block of AWK code to be run before running the program */
- NODE *begin_block = 0;
-
- /* A block of AWK code to be run after the last input file */
- NODE *end_block = 0;
-
- int exiting = 0; /* Was an "exit" statement executed? */
- int exit_val = 0; /* optional exit value */
-
- #ifdef DEBUG
- /* non-zero means in debugging is enabled. Probably not very useful */
- int debugging = 1;
- extern int yydebug;
- #endif
-
- int tempsource = 0; /* source is in a temp file */
- char **sourcefile = NULL; /* source file name(s) */
- int numfiles = -1; /* how many source files */
-
- int strict = 0; /* turn off gnu extensions */
-
- int output_is_tty = 0; /* control flushing of output */
-
- NODE *expression_value;
-
- /*
- * for strict to work, legal options must be first
- *
- * Unfortunately, -a and -e are orthogonal to -c.
- */
- #define EXTENSIONS 8 /* where to clear */
- #ifdef DEBUG
- char awk_opts[] = "F:f:v:caeCVdD";
- #else
- char awk_opts[] = "F:f:v:caeCV";
- #endif
-
- #ifdef THINK_C
- #include <console.h>
- char **environ;
- #endif
-
-
- int
- main(argc, argv)
- int argc;
- char **argv;
- {
-
-
- #ifdef DEBUG
- /* Print out the parse tree. For debugging */
- register int dotree = 0;
- #endif
- extern char *version_string;
- FILE *fp;
- int c;
- extern int opterr, optind;
- extern char *optarg;
- extern char *strrchr();
- extern char *tmpnam();
- extern SIGTYPE catchsig();
- int i;
- int nostalgia;
- #ifdef somtime_in_the_future
- int regex_mode = RE_SYNTAX_POSIX_EGREP;
- #else
- int regex_mode = RE_SYNTAX_AWK;
- #endif
-
- (void) signal(SIGFPE, catchsig);
- (void) signal(SIGSEGV, catchsig);
-
- #ifdef THINK_C
- /* use the ccommand interface */
-
- argc = ccommand(&argv);
-
- /* initialize environ to something safe */
-
- *environ++ = "";
- *environ = (char *) 0;
- #endif
-
- if (strncmp(version_string, "@(#)", 4) == 0)
- version_string += 4;
-
- myname = strrchr(argv[0], '/');
- if (myname == NULL)
- myname = argv[0];
- else
- myname++;
- if (argc < 2)
- usage();
-
- /* initialize the null string */
- Nnull_string = make_string("", 0);
- Nnull_string->numbr = 0.0;
- Nnull_string->type = Node_val;
- Nnull_string->flags = (PERM|STR|NUM|NUMERIC);
-
- /* Set up the special variables */
-
- /*
- * Note that this must be done BEFORE arg parsing else -F
- * breaks horribly
- */
- init_vars();
-
- /* worst case */
- emalloc(sourcefile, char **, argc * sizeof(char *), "main");
-
-
- #ifdef STRICT /* strict new awk compatibility */
- strict = 1;
- awk_opts[EXTENSIONS] = '\0';
- #endif
-
- #ifndef STRICT
- /* undocumented feature, inspired by nostalgia, and a T-shirt */
- nostalgia = 0;
- for (i = 1; i < argc && argv[i][0] == '-'; i++) {
- if (argv[i][1] == '-') /* -- */
- break;
- else if (argv[i][1] == 'c') { /* compatibility mode */
- nostalgia = 0;
- break;
- } else if (STREQ(&argv[i][1], "nostalgia"))
- nostalgia = 1;
- /* keep looping, in case -c after -nostalgia */
- }
- if (nostalgia) {
- fprintf (stderr, "awk: bailing out near line 1\n");
- abort();
- }
- #endif
-
- while ((c = getopt (argc, argv, awk_opts)) != EOF) {
- switch (c) {
- #ifdef DEBUG
- case 'd':
- debugging++;
- dotree++;
- break;
-
- case 'D':
- debugging++;
- yydebug = 2;
- break;
- #endif
-
- #ifndef STRICT
- case 'c':
- strict = 1;
- break;
- #endif
-
- case 'F':
- set_fs(optarg);
- break;
-
- case 'f':
- /*
- * a la MKS awk, allow multiple -f options.
- * this makes function libraries real easy.
- * most of the magic is in the scanner.
- */
- sourcefile[++numfiles] = optarg;
- break;
-
- case 'v':
- pre_assign(optarg);
- break;
-
- case 'V':
- fprintf(stderr, "%s, patchlevel %d\n",
- version_string, PATCHLEVEL);
- break;
-
- case 'C':
- copyleft();
- break;
-
- case 'a': /* use old fashioned awk regexps */
- regex_mode = RE_SYNTAX_AWK;
- break;
-
- case 'e': /* use egrep style regexps, per Posix */
- regex_mode = RE_SYNTAX_POSIX_EGREP;
- break;
-
- case '?':
- default:
- /* getopt will print a message for us */
- /* S5R4 awk ignores bad options and keeps going */
- break;
- }
- }
-
- /* Tell the regex routines how they should work. . . */
- (void) re_set_syntax(regex_mode);
-
- #ifdef DEBUG
- setbuf(stdout, (char *) NULL); /* make debugging easier */
- #endif
- if (isatty(fileno(stdout)))
- output_is_tty = 1;
- /* No -f option, use next arg */
- /* write to temp file and save sourcefile name */
- if (numfiles == -1) {
- int i;
-
- if (optind > argc - 1) /* no args left */
- usage();
- numfiles++;
- i = strlen (argv[optind]);
- if (i == 0) { /* sanity check */
- fprintf(stderr, "%s: empty program text\n", myname);
- usage();
- /* NOTREACHED */
- }
- sourcefile[0] = tmpnam((char *) NULL);
- if ((fp = fopen (sourcefile[0], "w")) == NULL)
- fatal("could not save source prog in temp file (%s)",
- strerror(errno));
- if (fwrite (argv[optind], 1, i, fp) == 0)
- fatal(
- "could not write source program to temp file (%s)",
- strerror(errno));
- if (argv[optind][i-1] != '\n')
- putc ('\n', fp);
- (void) fclose (fp);
- tempsource++;
- optind++;
- }
- init_args(optind, argc, myname, argv);
-
- /* Read in the program */
- if (yyparse() || errcount)
- exit(1);
-
- #ifdef DEBUG
- if (dotree)
- print_parse_tree(expression_value);
- #endif
- /* Set up the field variables */
- init_fields();
-
- if (begin_block)
- (void) interpret(begin_block);
- if (!exiting && (expression_value || end_block))
- do_input();
- if (end_block)
- (void) interpret(end_block);
- if (close_io() != 0 && exit_val == 0)
- exit_val = 1;
- exit(exit_val);
- /* NOTREACHED */
- return exit_val;
- }
-
- static void
- usage()
- {
- char *opt1 = " -f progfile [--]";
- char *opt2 = " [--] 'program'";
- #ifdef STRICT
- char *regops = " [-ae] [-F fs] [-v var=val]"
- #else
- char *regops = " [-aecCV] [-F fs] [-v var=val]";
- #endif
-
- fprintf(stderr, "usage: %s%s%s file ...\n %s%s%s file ...\n",
- myname, regops, opt1, myname, regops, opt2);
- exit(11);
- }
-
- /* Generate compiled regular expressions */
- struct re_pattern_buffer *
- make_regexp(s, ignorecase)
- NODE *s;
- int ignorecase;
- {
- struct re_pattern_buffer *rp;
- char *err;
-
- emalloc(rp, struct re_pattern_buffer *, sizeof(*rp), "make_regexp");
- memset((char *) rp, 0, sizeof(*rp));
- emalloc(rp->buffer, char *, 16, "make_regexp");
- rp->allocated = 16;
- emalloc(rp->fastmap, char *, 256, "make_regexp");
-
- if (! strict && ignorecase)
- rp->translate = casetable;
- else
- rp->translate = NULL;
- if ((err = re_compile_pattern(s->stptr, s->stlen, rp)) != NULL)
- fatal("%s: /%s/", err, s->stptr);
- free_temp(s);
- return rp;
- }
-
- struct re_pattern_buffer *
- mk_re_parse(s, ignorecase)
- char *s;
- int ignorecase;
- {
- char *src;
- register char *dest;
- register int c;
- int in_brack = 0;
-
- for (dest = src = s; *src != '\0';) {
- if (*src == '\\') {
- c = *++src;
- switch (c) {
- case '/':
- case 'a':
- case 'b':
- case 'f':
- case 'n':
- case 'r':
- case 't':
- case 'v':
- case 'x':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- c = parse_escape(&src);
- if (c < 0)
- cant_happen();
- *dest++ = (char)c;
- break;
- default:
- *dest++ = '\\';
- *dest++ = (char)c;
- src++;
- break;
- }
- } else if (*src == '/' && ! in_brack)
- break;
- else {
- if (*src == '[')
- in_brack = 1;
- else if (*src == ']')
- in_brack = 0;
-
- *dest++ = *src++;
- }
- }
- return make_regexp(tmp_string(s, dest-s), ignorecase);
- }
-
- static void
- copyleft ()
- {
- extern char *version_string;
- char *cp;
- static char blurb[] =
- "Copyright (C) 1989, Free Software Foundation.\n\
- GNU Awk comes with ABSOLUTELY NO WARRANTY. This is free software, and\n\
- you are welcome to distribute it under the terms of the GNU General\n\
- Public License, which covers both the warranty information and the\n\
- terms for redistribution.\n\n\
- You should have received a copy of the GNU General Public License along\n\
- with this program; if not, write to the Free Software Foundation, Inc.,\n\
- 675 Mass Ave, Cambridge, MA 02139, USA.\n";
-
- fprintf (stderr, "%s, patchlevel %d\n", version_string, PATCHLEVEL);
- fputs(blurb, stderr);
- fflush(stderr);
- }
-
- static void
- set_fs(str)
- char *str;
- {
- register NODE **tmp;
-
- tmp = get_lhs(FS_node, 0);
- /*
- * Only if in full compatibility mode check for the stupid special
- * case so -F\t works as documented in awk even though the shell
- * hands us -Ft. Bleah!
- */
- if (strict && str[0] == 't' && str[1] == '\0')
- str[0] = '\t';
- *tmp = make_string(str, 1);
- do_deref();
- }
-
- static void
- init_args(argc0, argc, argv0, argv)
- int argc0, argc;
- char *argv0;
- char **argv;
- {
- int i, j;
- NODE **aptr;
-
- ARGV_node = spc_var("ARGV", Nnull_string);
- aptr = assoc_lookup(ARGV_node, tmp_number(0.0));
- *aptr = make_string(argv0, strlen(argv0));
- for (i = argc0, j = 1; i < argc; i++) {
- aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j));
- *aptr = make_string(argv[i], strlen(argv[i]));
- j++;
- }
- ARGC_node = spc_var("ARGC", make_number((AWKNUM) j));
- }
-
- /*
- * Set all the special variables to their initial values.
- */
- static void
- init_vars()
- {
- extern char **environ;
- char *var, *val;
- NODE **aptr;
- int i;
-
- FS_node = spc_var("FS", make_string(" ", 1));
- NF_node = spc_var("NF", make_number(-1.0));
- RS_node = spc_var("RS", make_string("\n", 1));
- NR_node = spc_var("NR", make_number(0.0));
- FNR_node = spc_var("FNR", make_number(0.0));
- FILENAME_node = spc_var("FILENAME", make_string("-", 1));
- OFS_node = spc_var("OFS", make_string(" ", 1));
- ORS_node = spc_var("ORS", make_string("\n", 1));
- OFMT_node = spc_var("OFMT", make_string("%.6g", 4));
- RLENGTH_node = spc_var("RLENGTH", make_number(0.0));
- RSTART_node = spc_var("RSTART", make_number(0.0));
- SUBSEP_node = spc_var("SUBSEP", make_string("\034", 1));
- IGNORECASE_node = spc_var("IGNORECASE", make_number(0.0));
-
- ENVIRON_node = spc_var("ENVIRON", Nnull_string);
-
- for (i = 0; environ[i]; i++) {
- static char nullstr[] = "";
-
- var = environ[i];
- val = strchr(var, '=');
- if (val)
- *val++ = '\0';
- else
- val = nullstr;
- aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen (var)));
- *aptr = make_string(val, strlen (val));
-
- /* restore '=' so that system() gets a valid environment */
- if (val != nullstr)
- *--val = '=';
- }
-
- }
-
- /* Create a special variable */
- static NODE *
- spc_var(name, value)
- char *name;
- NODE *value;
- {
- register NODE *r;
-
- if ((r = lookup(variables, name)) == NULL)
- r = install(variables, name, node(value, Node_var, (NODE *) NULL));
- return r;
- }
-
- static void
- pre_assign(v)
- char *v;
- {
- char *cp;
-
- cp = strchr(v, '=');
- if (cp != NULL) {
- *cp++ = '\0';
- variable(v)->var_value = make_string(cp, strlen(cp));
- } else {
- fprintf (stderr,
- "%s: '%s' argument to -v not in 'var=value' form\n",
- myname, v);
- usage();
- }
- }
-
- SIGTYPE
- catchsig(sig, code)
- int sig, code;
- {
- #ifdef lint
- code = 0; sig = code; code = sig;
- #endif
- if (sig == SIGFPE) {
- fatal("floating point exception");
- } else if (sig == SIGSEGV) {
- msg("fatal error: segmentation fault");
- /* fatal won't abort() if not compiled for debugging */
- abort();
- } else
- cant_happen();
- /* NOTREACHED */
- }
-