home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / zip / gnu / gawk213s.lzh / GAWK213S / MAIN.C < prev    next >
C/C++ Source or Header  |  1993-07-29  |  16KB  |  673 lines

  1. /*
  2.  * main.c -- Expression tree constructors and main program for gawk. 
  3.  */
  4.  
  5. /* 
  6.  * Copyright (C) 1986, 1988, 1989, 1991 the Free Software Foundation, Inc.
  7.  * 
  8.  * This file is part of GAWK, the GNU implementation of the
  9.  * AWK Progamming Language.
  10.  * 
  11.  * GAWK is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 1, or (at your option)
  14.  * any later version.
  15.  * 
  16.  * GAWK is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  * 
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with GAWK; see the file COPYING.  If not, write to
  23.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  */
  25.  
  26. #include "awk.h"
  27. #include "patchlevel.h"
  28.  
  29. static void usage P((void));
  30. static void copyleft P((void));
  31. static void cmdline_fs P((char *str));
  32. static void init_args P((int argc0, int argc, char *argv0, char **argv));
  33. static void init_vars P((void));
  34. static void pre_assign P((char *v));
  35. SIGTYPE catchsig P((int sig, int code));
  36. static void gawk_option P((char *optstr));
  37. static void nostalgia P((void));
  38.  
  39. /* These nodes store all the special variables AWK uses */
  40. NODE *FS_node, *NF_node, *RS_node, *NR_node;
  41. NODE *FILENAME_node, *OFS_node, *ORS_node, *OFMT_node;
  42. NODE *CONVFMT_node;
  43. NODE *FNR_node, *RLENGTH_node, *RSTART_node, *SUBSEP_node;
  44. NODE *ENVIRON_node, *IGNORECASE_node;
  45. NODE *ARGC_node, *ARGV_node;
  46. NODE *FIELDWIDTHS_node;
  47.  
  48. int NF;
  49. int NR;
  50. int FNR;
  51. int IGNORECASE;
  52. char *FS;
  53. char *RS;
  54. char *OFS;
  55. char *ORS;
  56. char *OFMT;
  57. char *CONVFMT;
  58.  
  59. /*
  60.  * The parse tree and field nodes are stored here.  Parse_end is a dummy item
  61.  * used to free up unneeded fields without freeing the program being run 
  62.  */
  63. int errcount = 0;    /* error counter, used by yyerror() */
  64.  
  65. /* The global null string */
  66. NODE *Nnull_string;
  67.  
  68. /* The name the program was invoked under, for error messages */
  69. const char *myname;
  70.  
  71. /* A block of AWK code to be run before running the program */
  72. NODE *begin_block = 0;
  73.  
  74. /* A block of AWK code to be run after the last input file */
  75. NODE *end_block = 0;
  76.  
  77. int exiting = 0;        /* Was an "exit" statement executed? */
  78. int exit_val = 0;        /* optional exit value */
  79.  
  80. #if defined(YYDEBUG) || defined(DEBUG)
  81. extern int yydebug;
  82. #endif
  83.  
  84. char **srcfiles = NULL;        /* source file name(s) */
  85. int numfiles = -1;        /* how many source files */
  86. char *cmdline_src = NULL;    /* if prog is on command line */
  87.  
  88. int strict = 0;            /* turn off gnu extensions */
  89. int do_posix = 0;        /* turn off gnu extensions and \x */
  90. int do_lint = 0;        /* provide warnings about questionable stuff */
  91.  
  92. int output_is_tty = 0;        /* control flushing of output */
  93.  
  94. extern char *version_string;    /* current version, for printing */
  95.  
  96. NODE *expression_value;
  97.  
  98. /*
  99.  * for strict to work, legal options must be first
  100.  *
  101.  * Unfortunately, -a and -e are orthogonal to -c.
  102.  *
  103.  * Note that after 2.13, c,a,e,C,D, and V go away.
  104.  */
  105. #ifdef DEBUG
  106. char awk_opts[] = "F:f:v:W:caeCVD";
  107. #else
  108. char awk_opts[] = "F:f:v:W:caeCV";
  109. #endif
  110.  
  111. int
  112. main(argc, argv)
  113. int argc;
  114. char **argv;
  115. {
  116.     int c;
  117.     extern int optind;
  118.     extern char *optarg;
  119.     int i;
  120.     int do_nostalgia;
  121.     int regex_mode = RE_SYNTAX_AWK;
  122.  
  123.     (void) signal(SIGFPE,  (SIGTYPE (*) P((int))) catchsig);
  124.     (void) signal(SIGSEGV, (SIGTYPE (*) P((int))) catchsig);
  125. #ifdef VMS
  126.     (void) signal(SIGBUS,  (SIGTYPE (*) P((int))) catchsig);
  127. #endif
  128.  
  129. #ifndef VMS
  130.     myname = basename(argv[0]);
  131. #else    /* VMS */
  132.     myname = strdup(basename(argv[0]));
  133.     argv[0] = (char *) myname;   /* strip path [prior to getopt()] */
  134.     vms_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */
  135. #endif
  136.     if (argc < 2)
  137.         usage();
  138.  
  139.     /* remove sccs gunk */
  140.     if (strncmp(version_string, "@(#)", 4) == 0)
  141.         version_string += 4;
  142.  
  143.     /* initialize the null string */
  144.     Nnull_string = make_string("", 0);
  145.     Nnull_string->numbr = 0.0;
  146.     Nnull_string->type = Node_val;
  147.     Nnull_string->flags = (PERM|STR|STRING|NUM|NUMERIC|NUMBER);
  148.  
  149.     /* Set up the special variables */
  150.  
  151.     /*
  152.      * Note that this must be done BEFORE arg parsing else -F
  153.      * breaks horribly 
  154.      */
  155.     init_vars();
  156.  
  157.     /* worst case */
  158.     emalloc(srcfiles, char **, argc * sizeof(char *), "main");
  159.     srcfiles[0] = NULL;
  160.  
  161.     /* undocumented feature, inspired by nostalgia, and a T-shirt */
  162.     do_nostalgia = 0;
  163.     for (i = 1; i < argc && argv[i][0] == '-'; i++) {
  164.         if (argv[i][1] == '-')        /* -- */
  165.             break;
  166.         else if (argv[i][1] == 'c') {    /* compat not in next release */
  167.             do_nostalgia = 0;
  168.             break;
  169.         } else if (STREQ(&argv[i][1], "nostalgia"))
  170.             do_nostalgia = 1;
  171.             /* keep looping, in case -c after -nostalgia */
  172.     }
  173.     if (do_nostalgia) {
  174.         fprintf(stderr, "%s, %s\n",
  175.         "warning: option -nostalgia will go away in the next release",
  176.         "use -W nostalgia");
  177.         nostalgia();
  178.         /* NOTREACHED */
  179.     }
  180.  
  181.     while ((c = getopt (argc, argv, awk_opts)) != EOF) {
  182.         switch (c) {
  183. #ifdef DEBUG
  184.         case 'D':
  185.             fprintf(stderr,
  186. "warning: option -D will go away in the next release, use -W parsedebug\n");
  187.             gawk_option("parsedebug");
  188.             break;
  189. #endif
  190.  
  191.         case 'c':
  192.             fprintf(stderr,
  193.     "warning: option -c will go away in the next release, use -W compat\n");
  194.             gawk_option("compat");
  195.             break;
  196.  
  197.         case 'F':
  198.             cmdline_fs(optarg);
  199.             break;
  200.  
  201.         case 'f':
  202.             /*
  203.              * a la MKS awk, allow multiple -f options.
  204.              * this makes function libraries real easy.
  205.              * most of the magic is in the scanner.
  206.              */
  207.             srcfiles[++numfiles] = optarg;
  208.             break;
  209.  
  210.         case 'v':
  211.             pre_assign(optarg);
  212.             break;
  213.  
  214.         case 'V':
  215.             warning(
  216.         "option -V will go away in the next release, use -W version");
  217.             gawk_option("version");
  218.             break;
  219.  
  220.         case 'C':
  221.             warning(
  222.         "option -C will go away in the next release, use -W copyright");
  223.             gawk_option("copyright");
  224.             break;
  225.  
  226.         case 'a':    /* use old fashioned awk regexps */
  227.             warning("option -a will go away in the next release");
  228.             /*regex_mode = RE_SYNTAX_AWK;*/
  229.             break;
  230.  
  231.         case 'e':    /* use Posix style regexps */
  232.             warning("option -e will go away in the next release");
  233.             /*regex_mode = RE_SYNTAX_POSIX_AWK;*/
  234.             break;
  235.  
  236.         case 'W':       /* gawk specific options */
  237.             gawk_option(optarg);
  238.             break;
  239.  
  240.         case '?':
  241.         default:
  242.             /* getopt will print a message for us */
  243.             /* S5R4 awk ignores bad options and keeps going */
  244.             break;
  245.         }
  246.     }
  247.  
  248.     /* Tell the regex routines how they should work. . . */
  249.     (void) re_set_syntax(regex_mode);
  250.     regsyntax(regex_mode, 0);
  251.  
  252. #ifdef DEBUG
  253.     setbuf(stdout, (char *) NULL);    /* make debugging easier */
  254. #endif
  255.     if (isatty(fileno(stdout)))
  256.         output_is_tty = 1;
  257.     /* No -f option, use next arg */
  258.     /* write to temp file and save sourcefile name */
  259.     if (numfiles == -1) {
  260.         if (optind > argc - 1)    /* no args left */
  261.             usage();
  262.         cmdline_src = argv[optind];
  263.         optind++;
  264.     }
  265.     srcfiles[++numfiles] = NULL;
  266.     init_args(optind, argc, (char *) myname, argv);
  267.     (void) tokexpand();
  268.  
  269.     /* Read in the program */
  270.     if (yyparse() || errcount)
  271.         exit(1);
  272.  
  273.     /* Set up the field variables */
  274.     init_fields();
  275.  
  276.     if (begin_block)
  277.         (void) interpret(begin_block);
  278.     if (!exiting && (expression_value || end_block))
  279.         do_input();
  280.     if (end_block)
  281.         (void) interpret(end_block);
  282.     if (close_io() != 0 && exit_val == 0)
  283.         exit_val = 1;
  284.     exit(exit_val);        /* more portable */
  285.     return exit_val;    /* to suppress warnings */
  286. }
  287.  
  288. static void
  289. usage()
  290. {
  291.     char *opt1 = " -f progfile [--]";
  292.     char *opt2 = " [--] 'program'";
  293.     char *regops = " [-F fs] [-v var=val] [-W gawk-opts]";
  294.  
  295.     fprintf(stderr, "usage: %s%s%s file ...\n       %s%s%s file ...\n",
  296.         myname, regops, opt1, myname, regops, opt2);
  297.     exit(11);
  298. }
  299.  
  300. Regexp *
  301. mk_re_parse(s, ignorecase)
  302. char *s;
  303. int ignorecase;
  304. {
  305.     char *src;
  306.     register char *dest;
  307.     register int c;
  308.     int in_brack = 0;
  309.  
  310.     for (dest = src = s; *src != '\0';) {
  311.         if (*src == '\\') {
  312.             c = *++src;
  313.             switch (c) {
  314.             case '/':
  315.             case 'a':
  316.             case 'b':
  317.             case 'f':
  318.             case 'n':
  319.             case 'r':
  320.             case 't':
  321.             case 'v':
  322.             case 'x':
  323.             case '0':
  324.             case '1':
  325.             case '2':
  326.             case '3':
  327.             case '4':
  328.             case '5':
  329.             case '6':
  330.             case '7':
  331.                 c = parse_escape(&src);
  332.                 if (c < 0)
  333.                     cant_happen();
  334.                 *dest++ = (char)c;
  335.                 break;
  336.             default:
  337.                 *dest++ = '\\';
  338.                 *dest++ = (char)c;
  339.                 src++;
  340.                 break;
  341.             }
  342.         } else if (*src == '/' && ! in_brack)
  343.             break;
  344.         else {
  345.             if (*src == '[')
  346.                 in_brack = 1;
  347.             else if (*src == ']')
  348.                 in_brack = 0;
  349.  
  350.             *dest++ = *src++;
  351.         }
  352.     }
  353.     return make_regexp(tmp_string(s, dest-s), ignorecase, 1);
  354. }
  355.  
  356. static void
  357. copyleft ()
  358. {
  359.     static char blurb[] =
  360. "Copyright (C) 1989, Free Software Foundation.\n\
  361. GNU Awk comes with ABSOLUTELY NO WARRANTY.  This is free software, and\n\
  362. you are welcome to distribute it under the terms of the GNU General\n\
  363. Public License, which covers both the warranty information and the\n\
  364. terms for redistribution.\n\n\
  365. You should have received a copy of the GNU General Public License along\n\
  366. with this program; if not, write to the Free Software Foundation, Inc.,\n\
  367. 675 Mass Ave, Cambridge, MA 02139, USA.\n";
  368.  
  369.     fprintf(stderr, "%s, patchlevel %d\n", version_string, PATCHLEVEL);
  370.     fputs(blurb, stderr);
  371.     fflush(stderr);
  372. }
  373.  
  374. static void
  375. cmdline_fs(str)
  376. char *str;
  377. {
  378.     register NODE **tmp;
  379.     int len = strlen(str);
  380.  
  381.     tmp = get_lhs(FS_node, (Func_ptr *) 0);
  382.     unref(*tmp);
  383.     /*
  384.      * Only if in full compatibility mode check for the stupid special
  385.      * case so -F\t works as documented in awk even though the shell
  386.      * hands us -Ft.  Bleah!
  387.      */
  388.     if (strict && str[0] == 't' && str[1] == '\0')
  389.         str[0] = '\t';
  390.     *tmp = make_str_node(str, len, SCAN);    /* do process escapes */
  391.     set_FS();
  392. }
  393.  
  394. static void
  395. init_args(argc0, argc, argv0, argv)
  396. int argc0, argc;
  397. char *argv0;
  398. char **argv;
  399. {
  400.     int i, j;
  401.     NODE **aptr;
  402.  
  403.     ARGV_node = install("ARGV", node(Nnull_string, Node_var, (NODE *)NULL));
  404.     aptr = assoc_lookup(ARGV_node, tmp_number(0.0));
  405.     *aptr = make_string(argv0, strlen(argv0));
  406.     (*aptr)->flags |= MAYBE_NUM;
  407.     for (i = argc0, j = 1; i < argc; i++) {
  408.         aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j));
  409.         *aptr = make_string(argv[i], strlen(argv[i]));
  410.         (*aptr)->flags |= MAYBE_NUM;
  411.         j++;
  412.     }
  413.     ARGC_node = install("ARGC",
  414.             node(make_number((AWKNUM) j), Node_var, (NODE *) NULL));
  415. }
  416.  
  417. /*
  418.  * Set all the special variables to their initial values.
  419.  */
  420. struct varinit {
  421.     NODE **spec;
  422.     char *name;
  423.     NODETYPE type;
  424.     char *strval;
  425.     AWKNUM numval;
  426.     Func_ptr assign;
  427. };
  428. static struct varinit varinit[] = {
  429. {&NF_node,    "NF",        Node_NF,        0,    -1, set_NF },
  430. {&FIELDWIDTHS_node, "FIELDWIDTHS", Node_FIELDWIDTHS,    "",    0,  0 },
  431. {&NR_node,    "NR",        Node_NR,        0,    0,  set_NR },
  432. {&FNR_node,    "FNR",        Node_FNR,        0,    0,  set_FNR },
  433. {&FS_node,    "FS",        Node_FS,        " ",    0,  0 },
  434. {&RS_node,    "RS",        Node_RS,        "\n",    0,  set_RS },
  435. {&IGNORECASE_node, "IGNORECASE", Node_IGNORECASE,    0,    0,  set_IGNORECASE },
  436. {&FILENAME_node, "FILENAME",    Node_var,        "-",    0,  0 },
  437. {&OFS_node,    "OFS",        Node_OFS,        " ",    0,  set_OFS },
  438. {&ORS_node,    "ORS",        Node_ORS,        "\n",    0,  set_ORS },
  439. {&OFMT_node,    "OFMT",        Node_OFMT,        "%.6g",    0,  set_OFMT },
  440. {&CONVFMT_node,    "CONVFMT",    Node_CONVFMT,        "%.6g",    0,  set_CONVFMT },
  441. {&RLENGTH_node, "RLENGTH",    Node_var,        0,    0,  0 },
  442. {&RSTART_node,    "RSTART",    Node_var,        0,    0,  0 },
  443. {&SUBSEP_node,    "SUBSEP",    Node_var,        "\034",    0,  0 },
  444. {0,        0,        Node_illegal,        0,    0,  0 },
  445. };
  446.  
  447. static void
  448. init_vars()
  449. {
  450.     register struct varinit *vp;
  451.  
  452.     for (vp = varinit; vp->name; vp++) {
  453.         *(vp->spec) = install(vp->name,
  454.           node(vp->strval == 0 ? make_number(vp->numval)
  455.                 : make_string(vp->strval, strlen(vp->strval)),
  456.                vp->type, (NODE *) NULL));
  457.         if (vp->assign)
  458.             (*(vp->assign))();
  459.     }
  460. }
  461.  
  462. void
  463. load_environ()
  464. {
  465.     extern char **environ;
  466.     register char *var, *val;
  467.     NODE **aptr;
  468.     register int i;
  469.  
  470.     ENVIRON_node = install("ENVIRON", 
  471.             node(Nnull_string, Node_var, (NODE *) NULL));
  472.     for (i = 0; environ[i]; i++) {
  473.         static char nullstr[] = "";
  474.  
  475.         var = environ[i];
  476.         val = strchr(var, '=');
  477.         if (val)
  478.             *val++ = '\0';
  479.         else
  480.             val = nullstr;
  481.         aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen (var)));
  482.         *aptr = make_string(val, strlen (val));
  483.         (*aptr)->flags |= MAYBE_NUM;
  484.  
  485.         /* restore '=' so that system() gets a valid environment */
  486.         if (val != nullstr)
  487.             *--val = '=';
  488.     }
  489. }
  490.  
  491. /* Process a command-line assignment */
  492. char *
  493. arg_assign(arg)
  494. char *arg;
  495. {
  496.     char *cp;
  497.     Func_ptr after_assign = NULL;
  498.     NODE *var;
  499.     NODE *it;
  500.     NODE **lhs;
  501.  
  502.     cp = strchr(arg, '=');
  503.     if (cp != NULL) {
  504.         *cp++ = '\0';
  505.         /*
  506.          * Recent versions of nawk expand escapes inside assignments.
  507.          * This makes sense, so we do it too.
  508.          */
  509.         it = make_str_node(cp, strlen(cp), SCAN);
  510.         it->flags |= MAYBE_NUM;
  511.         var = variable(arg, 0);
  512.         lhs = get_lhs(var, &after_assign);
  513.         unref(*lhs);
  514.         *lhs = it;
  515.         if (after_assign)
  516.             (*after_assign)();
  517.         *--cp = '=';    /* restore original text of ARGV */
  518.     }
  519.     return cp;
  520. }
  521.  
  522. static void
  523. pre_assign(v)
  524. char *v;
  525. {
  526.     if (!arg_assign(v)) {
  527.         fprintf (stderr,
  528.             "%s: '%s' argument to -v not in 'var=value' form\n",
  529.                 myname, v);
  530.         usage();
  531.     }
  532. }
  533.  
  534. SIGTYPE
  535. catchsig(sig, code)
  536. int sig, code;
  537. {
  538. #ifdef lint
  539.     code = 0; sig = code; code = sig;
  540. #endif
  541.     if (sig == SIGFPE) {
  542.         fatal("floating point exception");
  543. #ifndef VMS
  544.     } else if (sig == SIGSEGV) {
  545.         msg("fatal error: segmentation fault");
  546. #else
  547.     } else if (sig == SIGSEGV || sig == SIGBUS) {
  548.         msg("fatal error: access violation");
  549. #endif
  550.         /* fatal won't abort() if not compiled for debugging */
  551.         abort();
  552.     } else
  553.         cant_happen();
  554.     /* NOTREACHED */
  555. }
  556.  
  557. /* gawk_option --- do gawk specific things */
  558.  
  559. static void
  560. gawk_option(optstr)
  561. char *optstr;
  562. {
  563.     char *cp;
  564.  
  565.     for (cp = optstr; *cp; cp++) {
  566.         switch (*cp) {
  567.         case ' ':
  568.         case '\t':
  569.         case ',':
  570.             break;
  571.         case 'v':
  572.         case 'V':
  573.             /* print version */
  574.             if (strncasecmp(cp, "version", 7) != 0)
  575.                 goto unknown;
  576.             else
  577.                 cp += 6;
  578.             fprintf(stderr, "%s, patchlevel %d\n",
  579.                     version_string, PATCHLEVEL);
  580.             break;
  581.         case 'c':
  582.         case 'C':
  583.             if (strncasecmp(cp, "copyright", 9) == 0) {
  584.                 cp += 8;
  585.                 copyleft();
  586.             } else if (strncasecmp(cp, "copyleft", 8) == 0) {
  587.                 cp += 7;
  588.                 copyleft();
  589.             } else if (strncasecmp(cp, "compat", 6) == 0) {
  590.                 cp += 5;
  591.                 strict = 1;
  592.             } else
  593.                 goto unknown;
  594.             break;
  595.         case 'n':
  596.         case 'N':
  597.             if (strncasecmp(cp, "nostalgia", 9) != 0)
  598.                 goto unknown;
  599.             nostalgia();
  600.             break;
  601.         case 'p':
  602.         case 'P':
  603. #ifdef DEBUG
  604.             if (strncasecmp(cp, "parsedebug", 10) == 0) {
  605.                 cp += 10;
  606.                 yydebug = 2;
  607.                 break;
  608.             }
  609. #endif
  610.             if (strncasecmp(cp, "posix", 5) != 0)
  611.                 goto unknown;
  612.             cp += 4;
  613.             do_posix = 1;
  614.             strict = 1;
  615.             break;
  616.         case 'l':
  617.         case 'L':
  618.             if (strncasecmp(cp, "lint", 4) != 0)
  619.                 goto unknown;
  620.             cp += 3;
  621.             do_lint = 1;
  622.             break;
  623.         default:
  624.         unknown:
  625.             fprintf(stderr, "'%c' -- unknown option, ignored\n",
  626.                 *cp);
  627.             break;
  628.         }
  629.     }
  630. }
  631.  
  632. /* nostalgia --- print the famous error message and die */
  633.  
  634. static void
  635. nostalgia()
  636. {
  637.     fprintf(stderr, "awk: bailing out near line 1\n");
  638.     abort();
  639. }
  640.  
  641. const char *
  642. basename(filespec)
  643. const char *filespec;
  644. {
  645. #ifndef VMS    /* "path/name" -> "name" */
  646.     char *p = strrchr(filespec, '/');
  647.  
  648. #if defined(MSDOS) || defined(atarist)
  649.     char *q = strrchr(filespec, '\\');
  650.  
  651.     if (p == NULL || q > p)
  652.         p = q;
  653. #endif
  654.  
  655.     return (p == NULL ? filespec : (const char *)(p + 1));
  656.  
  657. #else        /* "device:[root.][directory.subdir]GAWK.EXE;n" -> "GAWK" */
  658.     static char buf[255+1];
  659.     char *p = strrchr(filespec, ']');  /* directory punctuation */
  660.     char *q = strrchr(filespec, '>');  /* alternate <international> punct */
  661.  
  662.     if (p == NULL || q > p)
  663.         p = q;
  664.     (void) strcpy(buf, p == NULL ? filespec : (p + 1));
  665.     q = strrchr(buf, '.');
  666.     if (q != NULL)
  667.         *q = '\0';    /* strip .type;version */
  668.  
  669.     return (const char *) buf;
  670.  
  671. #endif /*VMS*/
  672. }
  673.