home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / gnuawk.zip / main.c < prev    next >
C/C++ Source or Header  |  1997-05-13  |  20KB  |  736 lines

  1. /*
  2.  * main.c -- Expression tree constructors and main program for gawk. 
  3.  */
  4.  
  5. /* 
  6.  * Copyright (C) 1986, 1988, 1989, 1991-1997 the Free Software Foundation, Inc.
  7.  * 
  8.  * This file is part of GAWK, the GNU implementation of the
  9.  * AWK Programming 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 2 of the License, or
  14.  * (at your option) 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 this program; if not, write to the Free Software
  23.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
  24.  */
  25.  
  26. #include "awk.h"
  27. #include "getopt.h"
  28. #include "patchlevel.h"
  29.  
  30. static void usage P((int exitval, FILE *fp));
  31. static void copyleft P((void));
  32. static void cmdline_fs P((char *str));
  33. static void init_args P((int argc0, int argc, char *argv0, char **argv));
  34. static void init_vars P((void));
  35. static void pre_assign P((char *v));
  36. RETSIGTYPE catchsig P((int sig, int code));
  37. static void nostalgia P((void));
  38. static void version P((void));
  39.  
  40. /* These nodes store all the special variables AWK uses */
  41. NODE *ARGC_node, *ARGIND_node, *ARGV_node, *CONVFMT_node, *ENVIRON_node;
  42. NODE *ERRNO_node, *FIELDWIDTHS_node, *FILENAME_node, *FNR_node, *FS_node;
  43. NODE *IGNORECASE_node, *NF_node, *NR_node, *OFMT_node, *OFS_node;
  44. NODE *ORS_node, *RLENGTH_node, *RSTART_node, *RS_node, *RT_node, *SUBSEP_node;
  45.  
  46. long NF;
  47. long NR;
  48. long FNR;
  49. int IGNORECASE;
  50. char *OFS;
  51. char *ORS;
  52. char *OFMT;
  53.  
  54. /*
  55.  * CONVFMT is a convenience pointer for the current number to string format.
  56.  * We must supply an initial value to avoid recursion problems of
  57.  *    set_CONVFMT -> fmt_index -> r_force_string: gets NULL CONVFMT
  58.  * Fun, fun, fun, fun.
  59.  */
  60. char *CONVFMT = "%.6g";
  61.  
  62. int errcount = 0;        /* error counter, used by yyerror() */
  63.  
  64. NODE *Nnull_string;        /* The global null string */
  65.  
  66. /* The name the program was invoked under, for error messages */
  67. const char *myname;
  68.  
  69. /* A block of AWK code to be run before running the program */
  70. NODE *begin_block = NULL;
  71.  
  72. /* A block of AWK code to be run after the last input file */
  73. NODE *end_block = NULL;
  74.  
  75. int exiting = FALSE;        /* Was an "exit" statement executed? */
  76. int exit_val = 0;        /* optional exit value */
  77.  
  78. #if defined(YYDEBUG) || defined(DEBUG)
  79. extern int yydebug;
  80. #endif
  81.  
  82. struct src *srcfiles = NULL;    /* source file name(s) */
  83. long numfiles = -1;        /* how many source files */
  84.  
  85. int do_traditional = FALSE;    /* no gnu extensions, add traditional weirdnesses */
  86. int do_posix = FALSE;        /* turn off gnu and unix extensions */
  87. int do_lint = FALSE;        /* provide warnings about questionable stuff */
  88. int do_lint_old = FALSE;    /* warn about stuff not in V7 awk */
  89. int do_nostalgia = FALSE;    /* provide a blast from the past */
  90. int do_intervals = FALSE;    /* allow {...,...} in regexps */
  91.  
  92. int in_begin_rule = FALSE;    /* we're in a BEGIN rule */
  93. int in_end_rule = FALSE;    /* we're in a END rule */
  94.  
  95. int output_is_tty = FALSE;    /* control flushing of output */
  96.  
  97. extern char *version_string;    /* current version, for printing */
  98.  
  99. /* The parse tree is stored here.  */
  100. NODE *expression_value;
  101.  
  102. static struct option optab[] = {
  103.     { "compat",        no_argument,        & do_traditional,    1 },
  104.     { "traditional",    no_argument,        & do_traditional,    1 },
  105.     { "lint",        no_argument,        & do_lint,    1 },
  106.     { "lint-old",        no_argument,        & do_lint_old,    1 },
  107.     { "posix",        no_argument,        & do_posix,    1 },
  108.     { "nostalgia",        no_argument,        & do_nostalgia,    1 },
  109.     { "copyleft",        no_argument,        NULL,        'C' },
  110.     { "copyright",        no_argument,        NULL,        'C' },
  111.     { "field-separator",    required_argument,    NULL,        'F' },
  112.     { "file",        required_argument,    NULL,        'f' },
  113.     { "re-interval",        no_argument,    & do_intervals,        1 },
  114.     { "source",        required_argument,    NULL,        's' },
  115.     { "assign",        required_argument,    NULL,        'v' },
  116.     { "version",        no_argument,        NULL,        'V' },
  117.     { "usage",        no_argument,        NULL,        'u' },
  118.     { "help",        no_argument,        NULL,        'u' },
  119. #ifdef DEBUG
  120.     { "parsedebug",        no_argument,        NULL,        'D' },
  121. #endif
  122.     { NULL, 0, NULL, '\0' }
  123. };
  124.  
  125. /* main --- process args, parse program, run it, clean up */
  126.  
  127. int
  128. main(argc, argv)
  129. int argc;
  130. char **argv;
  131. {
  132.     int c;
  133.     char *scan;
  134.     /* the + on the front tells GNU getopt not to rearrange argv */
  135.     const char *optlist = "+F:f:v:W;m:";
  136.     int stopped_early = FALSE;
  137.     int old_optind;
  138.     extern int optind;
  139.     extern int opterr;
  140.     extern char *optarg;
  141.  
  142.     setlocale(LC_CTYPE, "");
  143.     setlocale(LC_COLLATE, "");
  144.  
  145.     (void) signal(SIGFPE,  (RETSIGTYPE (*) P((int))) catchsig);
  146.     (void) signal(SIGSEGV, (RETSIGTYPE (*) P((int))) catchsig);
  147. #ifdef SIGBUS
  148.     (void) signal(SIGBUS,  (RETSIGTYPE (*) P((int))) catchsig);
  149. #endif
  150.  
  151.     myname = gawk_name(argv[0]);
  152.         argv[0] = (char *) myname;
  153.     os_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */
  154.  
  155.     /* remove sccs gunk */
  156.     if (strncmp(version_string, "@(#)", 4) == 0)
  157.         version_string += 4;
  158.  
  159.     if (argc < 2)
  160.         usage(1, stderr);
  161.  
  162.     /* initialize the null string */
  163.     Nnull_string = make_string("", 0);
  164.     Nnull_string->numbr = 0.0;
  165.     Nnull_string->type = Node_val;
  166.     Nnull_string->flags = (PERM|STR|STRING|NUM|NUMBER);
  167.  
  168.     /*
  169.      * Tell the regex routines how they should work.
  170.      * Do this before initializing variables, since
  171.      * they could want to do a regexp compile.
  172.      */
  173.     resetup();
  174.  
  175.     /* Set up the special variables */
  176.     /*
  177.      * Note that this must be done BEFORE arg parsing else -F
  178.      * breaks horribly 
  179.      */
  180.     init_vars();
  181.  
  182.     /* worst case */
  183.     emalloc(srcfiles, struct src *, argc * sizeof(struct src), "main");
  184.     memset(srcfiles, '\0', argc * sizeof(struct src));
  185.  
  186.     /* we do error messages ourselves on invalid options */
  187.     opterr = FALSE;
  188.  
  189.     /* option processing. ready, set, go! */
  190.     for (optopt = 0, old_optind = 1;
  191.          (c = getopt_long(argc, argv, optlist, optab, NULL)) != EOF;
  192.          optopt = 0, old_optind = optind) {
  193.         if (do_posix)
  194.             opterr = TRUE;
  195.  
  196.         switch (c) {
  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.              * The following is to allow for whitespace at the end
  208.              * of a #! /bin/gawk line in an executable file
  209.              */
  210.             scan = optarg;
  211.             while (ISSPACE(*scan))
  212.                 scan++;
  213.  
  214.             ++numfiles;
  215.             srcfiles[numfiles].stype = SOURCEFILE;
  216.             if (*scan == '\0')
  217.                 srcfiles[numfiles].val = argv[optind++];
  218.             else
  219.                 srcfiles[numfiles].val = optarg;
  220.             break;
  221.  
  222.         case 'v':
  223.             pre_assign(optarg);
  224.             break;
  225.  
  226.         case 'm':
  227.             /*
  228.              * Research awk extension.
  229.              *    -mf nnn        set # fields, gawk ignores
  230.              *    -mr nnn        set record length, ditto
  231.              */
  232.             if (do_lint)
  233.                 warning("-m[fr] option irrelevant in gawk");
  234.             if (optarg[0] != 'r' && optarg[0] != 'f')
  235.                 warning("-m option usage: `-m[fr] nnn'");
  236.             if (optarg[1] == '\0')
  237.                 optind++;
  238.             break;
  239.  
  240.         case 'W':       /* gawk specific options - now in getopt_long */
  241.             fprintf(stderr, "%s: option `-W %s' unrecognized, ignored\n",
  242.                 argv[0], optarg);
  243.             break;
  244.  
  245.         /* These can only come from long form options */
  246.         case 'C':
  247.             copyleft();
  248.             break;
  249.  
  250.         case 's':
  251.             if (optarg[0] == '\0')
  252.                 warning("empty argument to --source ignored");
  253.             else {
  254.                 srcfiles[++numfiles].stype = CMDLINE;
  255.                 srcfiles[numfiles].val = optarg;
  256.             }
  257.             break;
  258.  
  259.         case 'u':
  260.             usage(0, stdout);    /* per coding stds */
  261.             break;
  262.  
  263.         case 'V':
  264.             version();
  265.             break;
  266.  
  267. #ifdef DEBUG
  268.         case 'D':
  269.             yydebug = 2;
  270.             break;
  271. #endif
  272.  
  273.         case 0:
  274.             /*
  275.              * getopt_long found an option that sets a variable
  276.              * instead of returning a letter. Do nothing, just
  277.              * cycle around for the next one.
  278.              */
  279.             break;
  280.  
  281.         case '?':
  282.         default:
  283.             /*
  284.              * New behavior.  If not posix, an unrecognized
  285.              * option stops argument processing so that it can
  286.              * go into ARGV for the awk program to see. This
  287.              * makes use of ``#! /bin/gawk -f'' easier.
  288.              *
  289.              * However, it's never simple. If optopt is set,
  290.              * an option that requires an argument didn't get the
  291.              * argument. We care because if opterr is 0, then
  292.              * getopt_long won't print the error message for us.
  293.              */
  294.             if (! do_posix
  295.                 && (optopt == '\0' || strchr(optlist, optopt) == NULL)) {
  296.                 /*
  297.                  * can't just do optind--. In case of an
  298.                  * option with >= 2 letters, getopt_long
  299.                  * won't have incremented optind.
  300.                  */
  301.                 optind = old_optind;
  302.                 stopped_early = TRUE;
  303.                 goto out;
  304.             } else if (optopt != '\0')
  305.                 /* Use 1003.2 required message format */
  306.                 fprintf(stderr,
  307.                 "%s: option requires an argument -- %c\n",
  308.                     myname, optopt);
  309.             /* else
  310.                 let getopt print error message for us */
  311.             break;
  312.         }
  313.     }
  314. out:
  315.  
  316.     if (do_nostalgia)
  317.         nostalgia();
  318.  
  319.     /* check for POSIXLY_CORRECT environment variable */
  320.     if (! do_posix && getenv("POSIXLY_CORRECT") != NULL) {
  321.         do_posix = TRUE;
  322.         if (do_lint)
  323.             warning(
  324.     "environment variable `POSIXLY_CORRECT' set: turning on --posix");
  325.     }
  326.  
  327.     if (do_posix) {
  328.         if (do_traditional)    /* both on command line */
  329.             warning("--posix overrides --traditional");
  330.         else
  331.             do_traditional = TRUE;
  332.             /*
  333.              * POSIX compliance also implies
  334.              * no GNU extensions either.
  335.              */
  336.     }
  337.  
  338.     /*
  339.      * Tell the regex routines how they should work.
  340.      * Do this again, after argument processing, since do_posix
  341.      * and do_traditional are now paid attention to by resetup().
  342.      */
  343.     if (do_traditional || do_posix || do_intervals) {
  344.         resetup();
  345.  
  346.         /* now handle RS and FS. have to be careful with FS */
  347.         set_RS();
  348.         if (using_fieldwidths()) {
  349.             set_FS();
  350.             set_FIELDWIDTHS();
  351.         } else
  352.             set_FS();
  353.     }
  354.  
  355. #ifdef DEBUG
  356.     setbuf(stdout, (char *) NULL);    /* make debugging easier */
  357. #endif
  358.     if (isatty(fileno(stdout)))
  359.         output_is_tty = TRUE;
  360.     /* No -f or --source options, use next arg */
  361.     if (numfiles == -1) {
  362.         if (optind > argc - 1 || stopped_early) /* no args left or no program */
  363.             usage(1, stderr);
  364.         srcfiles[++numfiles].stype = CMDLINE;
  365.         srcfiles[numfiles].val = argv[optind];
  366.         optind++;
  367.     }
  368.  
  369.     init_args(optind, argc, (char *) myname, argv);
  370.     (void) tokexpand();
  371.  
  372.     /* Read in the program */
  373.     if (yyparse() != 0 || errcount != 0)
  374.         exit(1);
  375.     /* recover any space from C based alloca */
  376. #ifdef C_ALLOCA
  377.     (void) alloca(0);
  378. #endif
  379.  
  380.     /* Set up the field variables */
  381.     init_fields();
  382.  
  383.     if (do_lint && begin_block == NULL && expression_value == NULL
  384.          && end_block == NULL)
  385.         warning("no program");
  386.  
  387.     if (begin_block != NULL) {
  388.         in_begin_rule = TRUE;
  389.         (void) interpret(begin_block);
  390.     }
  391.     in_begin_rule = FALSE;
  392.     if (! exiting && (expression_value != NULL || end_block != NULL))
  393.         do_input();
  394.     if (end_block != NULL) {
  395.         in_end_rule = TRUE;
  396.         (void) interpret(end_block);
  397.     }
  398.     in_end_rule = FALSE;
  399.     if (close_io() != 0 && exit_val == 0)
  400.         exit_val = 1;
  401.     exit(exit_val);        /* more portable */
  402.     return exit_val;    /* to suppress warnings */
  403. }
  404.  
  405. /* usage --- print usage information and exit */
  406.  
  407. static void
  408. usage(exitval, fp)
  409. int exitval;
  410. FILE *fp;
  411. {
  412.     char *opt1 = " -f progfile [--]";
  413.     char *regops = " [POSIX or GNU style options]";
  414.  
  415.     fprintf(fp, "Usage: %s%s%s file ...\n\t%s%s [--] %cprogram%c file ...\n",
  416.         myname, regops, opt1, myname, regops, quote, quote);
  417.  
  418.     /* GNU long options info. Gack. */
  419.     fputs("POSIX options:\t\tGNU long options:\n", fp);
  420.     fputs("\t-f progfile\t\t--file=progfile\n", fp);
  421.     fputs("\t-F fs\t\t\t--field-separator=fs\n", fp);
  422.     fputs("\t-v var=val\t\t--assign=var=val\n", fp);
  423.     fputs("\t-m[fr] val\n", fp);
  424.     fputs("\t-W compat\t\t--compat\n", fp);
  425.     fputs("\t-W copyleft\t\t--copyleft\n", fp);
  426.     fputs("\t-W copyright\t\t--copyright\n", fp);
  427.     fputs("\t-W help\t\t\t--help\n", fp);
  428.     fputs("\t-W lint\t\t\t--lint\n", fp);
  429.     fputs("\t-W lint-old\t\t--lint-old\n", fp);
  430. #ifdef NOSTALGIA
  431.     fputs("\t-W nostalgia\t\t--nostalgia\n", fp);
  432. #endif
  433. #ifdef DEBUG
  434.     fputs("\t-W parsedebug\t\t--parsedebug\n", fp);
  435. #endif
  436.     fputs("\t-W posix\t\t--posix\n", fp);
  437.     fputs("\t-W re-interval\t\t--re-interval\n", fp);
  438.     fputs("\t-W source=program-text\t--source=program-text\n", fp);
  439.     fputs("\t-W traditional\t\t--traditional\n", fp);
  440.     fputs("\t-W usage\t\t--usage\n", fp);
  441.     fputs("\t-W version\t\t--version\n", fp);
  442.     fputs("\nReport bugs to bug-gnu-utils@prep.ai.mit.edu,\n", fp);
  443.     fputs("with a Cc: to arnold@gnu.ai.mit.edu\n", fp);
  444.     exit(exitval);
  445. }
  446.  
  447. /* copyleft --- print out the short GNU copyright information */
  448.  
  449. static void
  450. copyleft()
  451. {
  452.     static char blurb_part1[] =
  453. "Copyright (C) 1989, 1991-1997 Free Software Foundation.\n\
  454. \n\
  455. This program is free software; you can redistribute it and/or modify\n\
  456. it under the terms of the GNU General Public License as published by\n\
  457. the Free Software Foundation; either version 2 of the License, or\n\
  458. (at your option) any later version.\n\
  459. \n";
  460.     static char blurb_part2[] =
  461. "This program is distributed in the hope that it will be useful,\n\
  462. but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
  463. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
  464. GNU General Public License for more details.\n\
  465. \n";
  466.     static char blurb_part3[] =
  467. "You should have received a copy of the GNU General Public License\n\
  468. along with this program; if not, write to the Free Software\n\
  469. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n";
  470.  
  471.     /* multiple blurbs are needed for some brain dead compilers. */
  472.     fputs(blurb_part1, stdout);
  473.     fputs(blurb_part2, stdout);
  474.     fputs(blurb_part3, stdout);
  475.     fflush(stdout);
  476.     exit(0);
  477. }
  478.  
  479. /* cmdline_fs --- set FS from the command line */
  480.  
  481. static void
  482. cmdline_fs(str)
  483. char *str;
  484. {
  485.     register NODE **tmp;
  486.  
  487.     tmp = get_lhs(FS_node, (Func_ptr *) 0);
  488.     unref(*tmp);
  489.     /*
  490.      * Only if in full compatibility mode check for the stupid special
  491.      * case so -F\t works as documented in awk book even though the shell
  492.      * hands us -Ft.  Bleah!
  493.      *
  494.      * Thankfully, Posix didn't propogate this "feature".
  495.      */
  496.     if (str[0] == 't' && str[1] == '\0') {
  497.         if (do_lint)
  498.             warning("-Ft does not set FS to tab in POSIX awk");
  499.         if (do_traditional && ! do_posix)
  500.             str[0] = '\t';
  501.     }
  502.     *tmp = make_str_node(str, strlen(str), SCAN); /* do process escapes */
  503.     set_FS();
  504. }
  505.  
  506. /* init_args --- set up ARGV from stuff on the command line */
  507.  
  508. static void
  509. init_args(argc0, argc, argv0, argv)
  510. int argc0, argc;
  511. char *argv0;
  512. char **argv;
  513. {
  514.     int i, j;
  515.     NODE **aptr;
  516.  
  517.     ARGV_node = install("ARGV", node(Nnull_string, Node_var_array, (NODE *) NULL));
  518.     aptr = assoc_lookup(ARGV_node, tmp_number(0.0));
  519.     *aptr = make_string(argv0, strlen(argv0));
  520.     (*aptr)->flags |= MAYBE_NUM;
  521.     for (i = argc0, j = 1; i < argc; i++) {
  522.         aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j));
  523.         *aptr = make_string(argv[i], strlen(argv[i]));
  524.         (*aptr)->flags |= MAYBE_NUM;
  525.         j++;
  526.     }
  527.     ARGC_node = install("ARGC",
  528.             node(make_number((AWKNUM) j), Node_var, (NODE *) NULL));
  529. }
  530.  
  531. /*
  532.  * Set all the special variables to their initial values.
  533.  * Note that some of the variables that have set_FOO routines should
  534.  * *N*O*T* have those routines called upon initialization, and thus
  535.  * they have NULL entries in that field. This is notably true of FS
  536.  * and IGNORECASE.
  537.  */
  538. struct varinit {
  539.     NODE **spec;
  540.     const char *name;
  541.     NODETYPE type;
  542.     const char *strval;
  543.     AWKNUM numval;
  544.     Func_ptr assign;
  545. };
  546. static struct varinit varinit[] = {
  547. {&CONVFMT_node,    "CONVFMT",    Node_CONVFMT,        "%.6g",    0,  set_CONVFMT },
  548. {&NF_node,    "NF",        Node_NF,        NULL,    -1, set_NF },
  549. {&FIELDWIDTHS_node, "FIELDWIDTHS", Node_FIELDWIDTHS,    "",    0,  NULL },
  550. {&NR_node,    "NR",        Node_NR,        NULL,    0,  set_NR },
  551. {&FNR_node,    "FNR",        Node_FNR,        NULL,    0,  set_FNR },
  552. {&FS_node,    "FS",        Node_FS,        " ",    0,  NULL },
  553. {&RS_node,    "RS",        Node_RS,        "\n",    0,  set_RS },
  554. {&IGNORECASE_node, "IGNORECASE", Node_IGNORECASE,    NULL,    0,  NULL },
  555. {&FILENAME_node, "FILENAME",    Node_var,        "",    0,  NULL },
  556. {&OFS_node,    "OFS",        Node_OFS,        " ",    0,  set_OFS },
  557. {&ORS_node,    "ORS",        Node_ORS,        "\n",    0,  set_ORS },
  558. {&OFMT_node,    "OFMT",        Node_OFMT,        "%.6g",    0,  set_OFMT },
  559. {&RLENGTH_node, "RLENGTH",    Node_var,        NULL,    0,  NULL },
  560. {&RSTART_node,    "RSTART",    Node_var,        NULL,    0,  NULL },
  561. {&SUBSEP_node,    "SUBSEP",    Node_var,        "\034",    0,  NULL },
  562. {&ARGIND_node,    "ARGIND",    Node_var,        NULL,    0,  NULL },
  563. {&ERRNO_node,    "ERRNO",    Node_var,        NULL,    0,  NULL },
  564. {&RT_node,    "RT",        Node_var,        "",    0,  NULL },
  565. {0,        NULL,        Node_illegal,        NULL,    0,  NULL },
  566. };
  567.  
  568. /* init_vars --- actually initialize everything in the symbol table */
  569.  
  570. static void
  571. init_vars()
  572. {
  573.     register struct varinit *vp;
  574.  
  575.     for (vp = varinit; vp->name; vp++) {
  576.         *(vp->spec) = install((char *) vp->name,
  577.           node(vp->strval == NULL ? make_number(vp->numval)
  578.                 : make_string((char *) vp->strval,
  579.                     strlen(vp->strval)),
  580.                vp->type, (NODE *) NULL));
  581.         (*(vp->spec))->flags |= SCALAR;
  582.         if (vp->assign)
  583.             (*(vp->assign))();
  584.     }
  585. }
  586.  
  587. /* load_environ --- populate the ENVIRON array */
  588.  
  589. void
  590. load_environ()
  591. {
  592. #if ! (defined(MSDOS) && !defined(DJGPP)) && ! defined(OS2) && ! (defined(VMS) && defined(__DECC))
  593.     extern char **environ;
  594. #endif
  595.     register char *var, *val, *cp;
  596.     NODE **aptr;
  597.     register int i;
  598.  
  599.     ENVIRON_node = install("ENVIRON", 
  600.             node(Nnull_string, Node_var, (NODE *) NULL));
  601.     for (i = 0; environ[i] != NULL; i++) {
  602.         static char nullstr[] = "";
  603.  
  604.         var = environ[i];
  605.         val = strchr(var, '=');
  606.         if (val != NULL)
  607.             *val++ = '\0';
  608.         else
  609.             val = nullstr;
  610.         aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen(var)));
  611.         *aptr = make_string(val, strlen(val));
  612.         (*aptr)->flags |= (MAYBE_NUM|SCALAR);
  613.  
  614.         /* restore '=' so that system() gets a valid environment */
  615.         if (val != nullstr)
  616.             *--val = '=';
  617.     }
  618.     /*
  619.      * Put AWKPATH into ENVIRON if it's not there.
  620.      * This allows querying it from outside gawk.
  621.      */
  622.     if ((cp = getenv("AWKPATH")) == NULL) {
  623.         aptr = assoc_lookup(ENVIRON_node, tmp_string("AWKPATH", 7));
  624.         *aptr = make_string(defpath, strlen(defpath));
  625.         (*aptr)->flags |= SCALAR;
  626.     }
  627. }
  628.  
  629. /* arg_assign --- process a command-line assignment */
  630.  
  631. char *
  632. arg_assign(arg)
  633. char *arg;
  634. {
  635.     char *cp, *cp2;
  636.     int badvar;
  637.     Func_ptr after_assign = NULL;
  638.     NODE *var;
  639.     NODE *it;
  640.     NODE **lhs;
  641.  
  642.     cp = strchr(arg, '=');
  643.     if (cp != NULL) {
  644.         *cp++ = '\0';
  645.         /* first check that the variable name has valid syntax */
  646.         badvar = FALSE;
  647.         if (! isalpha(arg[0]) && arg[0] != '_')
  648.             badvar = TRUE;
  649.         else
  650.             for (cp2 = arg+1; *cp2; cp2++)
  651.                 if (! isalnum(*cp2) && *cp2 != '_') {
  652.                     badvar = TRUE;
  653.                     break;
  654.                 }
  655.         if (badvar)
  656.             fatal("illegal name `%s' in variable assignment", arg);
  657.  
  658.         /*
  659.          * Recent versions of nawk expand escapes inside assignments.
  660.          * This makes sense, so we do it too.
  661.          */
  662.         it = make_str_node(cp, strlen(cp), SCAN);
  663.         it->flags |= (MAYBE_NUM|SCALAR);
  664.         var = variable(arg, FALSE, Node_var);
  665.         lhs = get_lhs(var, &after_assign);
  666.         unref(*lhs);
  667.         *lhs = it;
  668.         if (after_assign != NULL)
  669.             (*after_assign)();
  670.         *--cp = '=';    /* restore original text of ARGV */
  671.     }
  672.     return cp;
  673. }
  674.  
  675. /* pre_assign --- handle -v, print a message and die if a problem */
  676.  
  677. static void
  678. pre_assign(v)
  679. char *v;
  680. {
  681.     if (arg_assign(v) == NULL) {
  682.         fprintf(stderr,
  683.             "%s: `%s' argument to `-v' not in `var=value' form\n",
  684.                 myname, v);
  685.         usage(1, stderr);
  686.     }
  687. }
  688.  
  689. /* catchsig --- catch signals */
  690.  
  691. RETSIGTYPE
  692. catchsig(sig, code)
  693. int sig, code;
  694. {
  695. #ifdef lint
  696.     code = 0; sig = code; code = sig;
  697. #endif
  698.     if (sig == SIGFPE) {
  699.         fatal("floating point exception");
  700.     } else if (sig == SIGSEGV
  701. #ifdef SIGBUS
  702.             || sig == SIGBUS
  703. #endif
  704.     ) {
  705.         set_loc(__FILE__, __LINE__);
  706.         msg("fatal error: internal error");
  707.         /* fatal won't abort() if not compiled for debugging */
  708.         abort();
  709.     } else
  710.         cant_happen();
  711.     /* NOTREACHED */
  712. }
  713.  
  714. /* nostalgia --- print the famous error message and die */
  715.  
  716. static void
  717. nostalgia()
  718. {
  719.     fprintf(stderr, "awk: bailing out near line 1\n");
  720.     abort();
  721. }
  722.  
  723. /* version --- print version message */
  724.  
  725. static void
  726. version()
  727. {
  728.     printf("%s.%d\n", version_string, PATCHLEVEL);
  729.     /*
  730.      * Per GNU coding standards, print copyright info,
  731.      * then exit successfully, do nothing else.
  732.      */
  733.     copyleft();
  734.     exit(0);
  735. }
  736.