Main Page | Class List | Directories | File List | Class Members | File Members

main.c

Go to the documentation of this file.
00001 /*
00002  * main.c -- Expression tree constructors and main program for gawk. 
00003  */
00004 
00005 /* 
00006  * Copyright (C) 1986, 1988, 1989, 1991-2003 the Free Software Foundation, Inc.
00007  * 
00008  * This file is part of GAWK, the GNU implementation of the
00009  * AWK Programming Language.
00010  * 
00011  * GAWK is free software; you can redistribute it and/or modify
00012  * it under the terms of the GNU General Public License as published by
00013  * the Free Software Foundation; either version 2 of the License, or
00014  * (at your option) any later version.
00015  * 
00016  * GAWK is distributed in the hope that it will be useful,
00017  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  * GNU General Public License for more details.
00020  * 
00021  * You should have received a copy of the GNU General Public License
00022  * along with this program; if not, write to the Free Software
00023  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
00024  */
00025 
00026 #include "awk.h"
00027 #include "getopt.h"
00028 #ifdef TANDEM
00029 #include "ptchlvl.h"    /* blech */
00030 #else
00031 #include "patchlev.h"
00032 #endif
00033 
00034 #ifndef O_BINARY
00035 #include <fcntl.h>
00036 #endif
00037 
00038 #ifdef HAVE_MCHECK_H
00039 #include <mcheck.h>
00040 #endif
00041 
00042 #define DEFAULT_PROFILE         "awkprof.out"   /* where to put profile */
00043 #define DEFAULT_VARFILE         "awkvars.out"   /* where to put vars */
00044 
00045 static const char *varfile = DEFAULT_VARFILE;
00046 
00047 static void usage P((int exitval, FILE *fp)) ATTRIBUTE_NORETURN;
00048 static void copyleft P((void)) ATTRIBUTE_NORETURN;
00049 static void cmdline_fs P((char *str));
00050 static void init_args P((int argc0, int argc, char *argv0, char **argv));
00051 static void init_vars P((void));
00052 static void add_src P((struct src **data, long *num, long *alloc, enum srctype stype, char *val));
00053 static RETSIGTYPE catchsig P((int sig)) ATTRIBUTE_NORETURN;
00054 static void nostalgia P((void)) ATTRIBUTE_NORETURN;
00055 static void version P((void)) ATTRIBUTE_NORETURN;
00056 static void init_fds P((void));
00057 static void init_groupset P((void));
00058 
00059 /* These nodes store all the special variables AWK uses */
00060 NODE *ARGC_node, *ARGIND_node, *ARGV_node, *BINMODE_node, *CONVFMT_node;
00061 NODE *ENVIRON_node, *ERRNO_node, *FIELDWIDTHS_node, *FILENAME_node, *FNR_node;
00062 NODE *FS_node, *IGNORECASE_node, *NF_node, *NR_node, *OFMT_node, *OFS_node;
00063 NODE *ORS_node, *PROCINFO_node, *RLENGTH_node, *RSTART_node, *RS_node;
00064 NODE *RT_node, *SUBSEP_node, *LINT_node, *TEXTDOMAIN_node;
00065 
00066 long NF;
00067 long NR;
00068 long FNR;
00069 int BINMODE;
00070 int IGNORECASE;
00071 char *OFS;
00072 char *ORS;
00073 char *OFMT;
00074 char *TEXTDOMAIN;
00075 int MRL;        /* See -mr option for use of this variable */
00076 
00077 /*
00078  * CONVFMT is a convenience pointer for the current number to string format.
00079  * We must supply an initial value to avoid recursion problems of
00080  *      set_CONVFMT -> fmt_index -> r_force_string: gets NULL CONVFMT
00081  * Fun, fun, fun, fun.
00082  */
00083 char *CONVFMT = "%.6g";
00084 
00085 
00086 int errcount = 0;               /* error counter, used by yyerror() */
00087 
00088 NODE *Nnull_string;             /* The global null string */
00089 
00090 /* The name the program was invoked under, for error messages */
00091 const char *myname;
00092 
00093 /* A block of AWK code to be run before running the program */
00094 NODE *begin_block = NULL;
00095 
00096 /* A block of AWK code to be run after the last input file */
00097 NODE *end_block = NULL;
00098 
00099 int exiting = FALSE;            /* Was an "exit" statement executed? */
00100 int exit_val = 0;               /* optional exit value */
00101 
00102 #if defined(YYDEBUG) || defined(GAWKDEBUG)
00103 extern int yydebug;
00104 #endif
00105 
00106 struct src *srcfiles = NULL;    /* source file name(s) */
00107 long numfiles = -1;             /* how many source files */
00108 static long allocfiles;         /* for how many is *srcfiles allocated */
00109 
00110 #define srcfiles_add(stype, val) \
00111         add_src(&srcfiles, &numfiles, &allocfiles, stype, val)
00112 
00113 static struct src *preassigns = NULL;   /* requested via -v or -F */
00114 static long numassigns = -1;            /* how many of them */
00115 static long allocassigns;               /* for how many is allocated */
00116 
00117 #define preassigns_add(stype, val) \
00118         add_src(&preassigns, &numassigns, &allocassigns, stype, val)
00119 
00120 #undef do_lint
00121 #undef do_lint_old
00122 
00123 int do_traditional = FALSE;     /* no gnu extensions, add traditional weirdnesses */
00124 int do_posix = FALSE;           /* turn off gnu and unix extensions */
00125 int do_lint = FALSE;            /* provide warnings about questionable stuff */
00126 int do_lint_old = FALSE;        /* warn about stuff not in V7 awk */
00127 int do_intl = FALSE;            /* dump locale-izable strings to stdout */
00128 int do_non_decimal_data = FALSE;        /* allow octal/hex C style DATA. Use with caution! */
00129 int do_nostalgia = FALSE;       /* provide a blast from the past */
00130 int do_intervals = FALSE;       /* allow {...,...} in regexps */
00131 int do_profiling = FALSE;       /* profile and pretty print the program */
00132 int do_dump_vars = FALSE;       /* dump all global variables at end */
00133 int do_tidy_mem = FALSE;        /* release vars when done */
00134 
00135 int in_begin_rule = FALSE;      /* we're in a BEGIN rule */
00136 int in_end_rule = FALSE;        /* we're in a END rule */
00137 int whiny_users = FALSE;        /* do things that whiny users want */
00138 #ifdef MBS_SUPPORT
00139 int gawk_mb_cur_max = 1;        /* MB_CUR_MAX value, see comment in main() */
00140 #endif
00141 
00142 int output_is_tty = FALSE;      /* control flushing of output */
00143 
00144 extern const char *version_string;      /* current version, for printing */
00145 
00146 #if defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0
00147 GETGROUPS_T *groupset;          /* current group set */
00148 int ngroups;                    /* size of said set */
00149 #endif
00150 
00151 /* The parse tree is stored here.  */
00152 NODE *expression_value;
00153 
00154 #if _MSC_VER == 510
00155 void (*lintfunc) P((va_list va_alist, ...)) = warning;
00156 #else
00157 #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__
00158 void (*lintfunc) P((const char *mesg, ...)) = warning;
00159 #else
00160 void (*lintfunc) () = warning;
00161 #endif
00162 #endif
00163 
00164 static const struct option optab[] = {
00165         { "compat",             no_argument,            & do_traditional,       1 },
00166         { "traditional",        no_argument,            & do_traditional,       1 },
00167         { "lint",               optional_argument,      NULL,           'l' },
00168         { "lint-old",           no_argument,            & do_lint_old,  1 },
00169         { "posix",              no_argument,            & do_posix,     1 },
00170         { "nostalgia",          no_argument,            & do_nostalgia, 1 },
00171         { "gen-po",             no_argument,            & do_intl,      1 },
00172         { "non-decimal-data",   no_argument,            & do_non_decimal_data, 1 },
00173         { "profile",            optional_argument,      NULL,           'p' },
00174         { "copyleft",           no_argument,            NULL,           'C' },
00175         { "copyright",          no_argument,            NULL,           'C' },
00176         { "field-separator",    required_argument,      NULL,           'F' },
00177         { "file",               required_argument,      NULL,           'f' },
00178         { "re-interval",        no_argument,            & do_intervals, 1 },
00179         { "source",             required_argument,      NULL,           's' },
00180         { "dump-variables",     optional_argument,      NULL,           'd' },
00181         { "assign",             required_argument,      NULL,           'v' },
00182         { "version",            no_argument,            NULL,           'V' },
00183         { "usage",              no_argument,            NULL,           'u' },
00184         { "help",               no_argument,            NULL,           'u' },
00185 #ifdef GAWKDEBUG
00186         { "parsedebug",         no_argument,            NULL,           'D' },
00187 #endif
00188         { NULL, 0, NULL, '\0' }
00189 };
00190 
00191 #ifdef NO_LINT
00192 #define do_lint 0
00193 #define do_lint_old 0
00194 #endif
00195 
00196 /* main --- process args, parse program, run it, clean up */
00197 
00198 int
00199 main(int argc, char **argv)
00200 {
00201         int c;
00202         char *scan;
00203         /* the + on the front tells GNU getopt not to rearrange argv */
00204         const char *optlist = "+F:f:v:W;m:D";
00205         int stopped_early = FALSE;
00206         int old_optind;
00207         extern int optind;
00208         extern int opterr;
00209         extern char *optarg;
00210         int i;
00211 
00212         /* do these checks early */
00213         if (getenv("TIDYMEM") != NULL)
00214                 do_tidy_mem = TRUE;
00215 
00216         if (getenv("WHINY_USERS") != NULL)
00217                 whiny_users = TRUE;
00218 
00219 #ifdef HAVE_MCHECK_H
00220         if (do_tidy_mem)
00221                 mtrace();
00222 #endif /* HAVE_MCHECK_H */
00223         
00224 #if defined(LC_CTYPE)
00225         setlocale(LC_CTYPE, "");
00226 #endif
00227 #if defined(LC_COLLATE)
00228         setlocale(LC_COLLATE, "");
00229 #endif
00230 #if HAVE_LC_MESSAGES && defined(LC_MESSAGES)
00231         setlocale(LC_MESSAGES, "");
00232 #endif
00233 #if defined(LC_NUMERIC)
00234         /*
00235          * Force the issue here.  According to POSIX 2001, decimal
00236          * point is used for parsing source code and for command-line
00237          * assignments and the locale value for processing input,
00238          * number to string conversion, and printing output.
00239          */
00240         setlocale(LC_NUMERIC, "C");
00241 #endif
00242 #if defined(LC_TIME)
00243         setlocale(LC_TIME, "");
00244 #endif
00245 
00246 #ifdef MBS_SUPPORT
00247         /*
00248          * In glibc, MB_CUR_MAX is actually a function.  This value is
00249          * tested *a lot* in many speed-critical places in gawk. Caching
00250          * this value once makes a speed difference.
00251          */
00252         gawk_mb_cur_max = MB_CUR_MAX;
00253 #endif
00254 
00255         bindtextdomain(PACKAGE, LOCALEDIR);
00256         textdomain(PACKAGE);
00257 
00258         (void) signal(SIGFPE, catchsig);
00259         (void) signal(SIGSEGV, catchsig);
00260 #ifdef SIGBUS
00261         (void) signal(SIGBUS, catchsig);
00262 #endif
00263 
00264         myname = gawk_name(argv[0]);
00265         argv[0] = (char *) myname;
00266         os_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */
00267 
00268         /* remove sccs gunk */
00269         if (strncmp(version_string, "@(#)", 4) == 0)
00270                 version_string += 4;
00271 
00272         if (argc < 2)
00273                 usage(1, stderr);
00274 
00275         /* Robustness: check that file descriptors 0, 1, 2 are open */
00276         init_fds();
00277 
00278         /* init array handling. */
00279         array_init();
00280 
00281         /* we do error messages ourselves on invalid options */
00282         opterr = FALSE;
00283 
00284         /* option processing. ready, set, go! */
00285         for (optopt = 0, old_optind = 1;
00286              (c = getopt_long(argc, argv, optlist, optab, NULL)) != EOF;
00287              optopt = 0, old_optind = optind) {
00288                 if (do_posix)
00289                         opterr = TRUE;
00290 
00291                 switch (c) {
00292                 case 'F':
00293                         preassigns_add(PRE_ASSIGN_FS, optarg);
00294                         break;
00295 
00296                 case 'f':
00297                         /*
00298                          * a la MKS awk, allow multiple -f options.
00299                          * this makes function libraries real easy.
00300                          * most of the magic is in the scanner.
00301                          *
00302                          * The following is to allow for whitespace at the end
00303                          * of a #! /bin/gawk line in an executable file
00304                          */
00305                         scan = optarg;
00306                         if (argv[optind-1] != optarg)
00307                                 while (ISSPACE(*scan))
00308                                         scan++;
00309                         srcfiles_add(SOURCEFILE,
00310                                 (*scan == '\0' ? argv[optind++] : optarg));
00311                         break;
00312 
00313                 case 'v':
00314                         preassigns_add(PRE_ASSIGN, optarg);
00315                         break;
00316 
00317                 case 'm':
00318                         /*
00319                          * Research awk extension.
00320                          *      -mf nnn         set # fields, gawk ignores
00321                          *      -mr nnn         set record length, ditto
00322                          */
00323                         if (do_lint)
00324                                 lintwarn(_("`-m[fr]' option irrelevant in gawk"));
00325                         if (optarg[0] != 'r' && optarg[0] != 'f')
00326                                 warning(_("-m option usage: `-m[fr] nnn'"));
00327                         /*
00328                          * Set fixed length records for Tandem,
00329                          * ignored on other platforms (see io.c:get_a_record).
00330                          */
00331                         if (optarg[0] == 'r') {
00332                                 if (ISDIGIT(optarg[1]))
00333                                         MRL = atoi(optarg+1);
00334                                 else {
00335                                         MRL = atoi(argv[optind]);
00336                                         optind++;
00337                                 }
00338                         } else if (optarg[1] == '\0')
00339                                 optind++;
00340                         break;
00341 
00342                 case 'W':       /* gawk specific options - now in getopt_long */
00343                         fprintf(stderr, _("%s: option `-W %s' unrecognized, ignored\n"),
00344                                 argv[0], optarg);
00345                         break;
00346 
00347                 /* These can only come from long form options */
00348                 case 'C':
00349                         copyleft();
00350                         break;
00351 
00352                 case 'd':
00353                         do_dump_vars = TRUE;
00354                         if (optarg != NULL && optarg[0] != '\0')
00355                                 varfile = optarg;
00356                         break;
00357 
00358                 case 'l':
00359 #ifndef NO_LINT
00360                         do_lint = LINT_ALL;
00361                         if (optarg != NULL) {
00362                                 if (strcmp(optarg, "fatal") == 0)
00363                                         lintfunc = r_fatal;
00364                                 else if (strcmp(optarg, "invalid") == 0)
00365                                         do_lint = LINT_INVALID;
00366                         }
00367 #endif
00368                         break;
00369 
00370                 case 'p':
00371                         do_profiling = TRUE;
00372                         if (optarg != NULL)
00373                                 set_prof_file(optarg);
00374                         else
00375                                 set_prof_file(DEFAULT_PROFILE);
00376                         break;
00377 
00378                 case 's':
00379                         if (optarg[0] == '\0')
00380                                 warning(_("empty argument to `--source' ignored"));
00381                         else
00382                                 srcfiles_add(CMDLINE, optarg);
00383                         break;
00384 
00385                 case 'u':
00386                         usage(0, stdout);       /* per coding stds */
00387                         break;
00388 
00389                 case 'V':
00390                         version();
00391                         break;
00392 
00393                 case 0:
00394                         /*
00395                          * getopt_long found an option that sets a variable
00396                          * instead of returning a letter. Do nothing, just
00397                          * cycle around for the next one.
00398                          */
00399                         break;
00400 
00401                 case 'D':
00402 #ifdef GAWKDEBUG
00403                         yydebug = 2;
00404                         break;
00405 #endif
00406                         /* if not debugging, fall through */
00407 
00408                 case '?':
00409                 default:
00410                         /*
00411                          * New behavior.  If not posix, an unrecognized
00412                          * option stops argument processing so that it can
00413                          * go into ARGV for the awk program to see. This
00414                          * makes use of ``#! /bin/gawk -f'' easier.
00415                          *
00416                          * However, it's never simple. If optopt is set,
00417                          * an option that requires an argument didn't get the
00418                          * argument. We care because if opterr is 0, then
00419                          * getopt_long won't print the error message for us.
00420                          */
00421                         if (! do_posix
00422                             && (optopt == '\0' || strchr(optlist, optopt) == NULL)) {
00423                                 /*
00424                                  * can't just do optind--. In case of an
00425                                  * option with >= 2 letters, getopt_long
00426                                  * won't have incremented optind.
00427                                  */
00428                                 optind = old_optind;
00429                                 stopped_early = TRUE;
00430                                 goto out;
00431                         } else if (optopt != '\0')
00432                                 /* Use 1003.2 required message format */
00433                                 fprintf(stderr,
00434                                         _("%s: option requires an argument -- %c\n"),
00435                                         myname, optopt);
00436                         /* else
00437                                 let getopt print error message for us */
00438                         break;
00439                 }
00440         }
00441 out:
00442 
00443         if (do_nostalgia)
00444                 nostalgia();
00445 
00446         /* check for POSIXLY_CORRECT environment variable */
00447         if (! do_posix && getenv("POSIXLY_CORRECT") != NULL) {
00448                 do_posix = TRUE;
00449                 if (do_lint)
00450                         lintwarn(
00451         _("environment variable `POSIXLY_CORRECT' set: turning on `--posix'"));
00452         }
00453 
00454         if (do_posix) {
00455                 if (do_traditional)     /* both on command line */
00456                         warning(_("`--posix' overrides `--traditional'"));
00457                 else
00458                         do_traditional = TRUE;
00459                         /*
00460                          * POSIX compliance also implies
00461                          * no GNU extensions either.
00462                          */
00463         }
00464 
00465         if (do_traditional && do_non_decimal_data) {
00466                 do_non_decimal_data = FALSE;
00467                 warning(_("`--posix'/`--traditional' overrides `--non-decimal-data'"));
00468         }
00469 
00470         if (do_lint && os_is_setuid())
00471                 warning(_("running %s setuid root may be a security problem"), myname);
00472 
00473         /*
00474          * Force profiling if this is pgawk.
00475          * Don't bother if the command line already set profiling up.
00476          */
00477         if (! do_profiling)
00478                 init_profiling(& do_profiling, DEFAULT_PROFILE);
00479 
00480         /* load group set */
00481         init_groupset();
00482 
00483         /* initialize the null string */
00484         Nnull_string = make_string("", 0);
00485         Nnull_string->numbr = 0.0;
00486         Nnull_string->type = Node_val;
00487         Nnull_string->flags = (PERM|STRCUR|STRING|NUMCUR|NUMBER);
00488 
00489         /*
00490          * Tell the regex routines how they should work.
00491          * Do this before initializing variables, since
00492          * they could want to do a regexp compile.
00493          */
00494         resetup();
00495 
00496         /* Set up the special variables */
00497         init_vars();
00498 
00499         /* Set up the field variables */
00500         init_fields();
00501 
00502         /* Now process the pre-assignments */
00503         for (i = 0; i <= numassigns; i++)
00504                 if (preassigns[i].stype == PRE_ASSIGN)
00505                         (void) arg_assign(preassigns[i].val, TRUE);
00506                 else    /* PRE_ASSIGN_FS */
00507                         cmdline_fs(preassigns[i].val);
00508         free(preassigns);
00509 
00510         if ((BINMODE & 1) != 0)
00511                 if (os_setbinmode(fileno(stdin), O_BINARY) == -1)
00512                         fatal(_("can't set binary mode on stdin (%s)"), strerror(errno));
00513         if ((BINMODE & 2) != 0) {
00514                 if (os_setbinmode(fileno(stdout), O_BINARY) == -1)
00515                         fatal(_("can't set binary mode on stdout (%s)"), strerror(errno));
00516                 if (os_setbinmode(fileno(stderr), O_BINARY) == -1)
00517                         fatal(_("can't set binary mode on stderr (%s)"), strerror(errno));
00518         }
00519 
00520 #ifdef GAWKDEBUG
00521         setbuf(stdout, (char *) NULL);  /* make debugging easier */
00522 #endif
00523         if (isatty(fileno(stdout)))
00524                 output_is_tty = TRUE;
00525         /* No -f or --source options, use next arg */
00526         if (numfiles == -1) {
00527                 if (optind > argc - 1 || stopped_early) /* no args left or no program */
00528                         usage(1, stderr);
00529                 srcfiles_add(CMDLINE, argv[optind]);
00530                 optind++;
00531         }
00532 
00533         init_args(optind, argc, (char *) myname, argv);
00534         (void) tokexpand();
00535 
00536         /* Read in the program */
00537         if (yyparse() != 0 || errcount != 0)
00538                 exit(1);
00539 
00540         free(srcfiles);
00541 
00542         if (do_intl)
00543                 exit(0);
00544 
00545         if (do_lint && begin_block == NULL && expression_value == NULL
00546              && end_block == NULL)
00547                 lintwarn(_("no program text at all!"));
00548 
00549         if (do_lint)
00550                 shadow_funcs();
00551 
00552         init_profiling_signals();
00553 
00554 #if defined(LC_NUMERIC)
00555         /* See comment above. */
00556         setlocale(LC_NUMERIC, "");
00557 #endif
00558 
00559         /* Whew. Finally, run the program. */
00560         if (begin_block != NULL) {
00561                 in_begin_rule = TRUE;
00562                 (void) interpret(begin_block);
00563         }
00564         in_begin_rule = FALSE;
00565         if (! exiting && (expression_value != NULL || end_block != NULL))
00566                 do_input();
00567         if (end_block != NULL) {
00568                 in_end_rule = TRUE;
00569                 (void) interpret(end_block);
00570         }
00571         in_end_rule = FALSE;
00572         if (close_io() != 0 && exit_val == 0)
00573                 exit_val = 1;
00574 
00575         if (do_profiling) {
00576                 dump_prog(begin_block, expression_value, end_block);
00577                 dump_funcs();
00578         }
00579 
00580         if (do_dump_vars)
00581                 dump_vars(varfile);
00582 
00583         if (do_tidy_mem)
00584                 release_all_vars();
00585 
00586         exit(exit_val);         /* more portable */
00587         return exit_val;        /* to suppress warnings */
00588 }
00589 
00590 /* add_src --- add one element to *srcfiles or *preassigns */
00591 
00592 static void
00593 add_src(struct src **data, long *num, long *alloc, enum srctype stype, char *val)
00594 {
00595 #define INIT_SRC 4
00596 
00597         ++*num;
00598 
00599         if (*data == NULL) {
00600                 emalloc(*data, struct src *, INIT_SRC * sizeof(struct src), "add_src");
00601                 *alloc = INIT_SRC;
00602         } else if (*num >= *alloc) {
00603                 (*alloc) *= 2;
00604                 erealloc(*data, struct src *, (*alloc) * sizeof(struct src), "add_src");
00605         }
00606 
00607         (*data)[*num].stype = stype;
00608         (*data)[*num].val = val;
00609 
00610 #undef INIT_SRC
00611 }
00612 
00613 /* usage --- print usage information and exit */
00614 
00615 static void
00616 usage(int exitval, FILE *fp)
00617 {
00618 
00619         /* Not factoring out common stuff makes it easier to translate. */
00620         fprintf(fp, _("Usage: %s [POSIX or GNU style options] -f progfile [--] file ...\n"),
00621                 myname);
00622         fprintf(fp, _("Usage: %s [POSIX or GNU style options] [--] %cprogram%c file ...\n"),
00623                 myname, quote, quote);
00624 
00625         /* GNU long options info. This is too many options. */
00626 
00627         fputs(_("POSIX options:\t\tGNU long options:\n"), fp);
00628         fputs(_("\t-f progfile\t\t--file=progfile\n"), fp);
00629         fputs(_("\t-F fs\t\t\t--field-separator=fs\n"), fp);
00630         fputs(_("\t-v var=val\t\t--assign=var=val\n"), fp);
00631         fputs(_("\t-m[fr] val\n"), fp);
00632         fputs(_("\t-W compat\t\t--compat\n"), fp);
00633         fputs(_("\t-W copyleft\t\t--copyleft\n"), fp);
00634         fputs(_("\t-W copyright\t\t--copyright\n"), fp);
00635         fputs(_("\t-W dump-variables[=file]\t--dump-variables[=file]\n"), fp);
00636         fputs(_("\t-W gen-po\t\t--gen-po\n"), fp);
00637         fputs(_("\t-W help\t\t\t--help\n"), fp);
00638         fputs(_("\t-W lint[=fatal]\t\t--lint[=fatal]\n"), fp);
00639         fputs(_("\t-W lint-old\t\t--lint-old\n"), fp);
00640         fputs(_("\t-W non-decimal-data\t--non-decimal-data\n"), fp);
00641 #ifdef NOSTALGIA
00642         fputs(_("\t-W nostalgia\t\t--nostalgia\n"), fp);
00643 #endif
00644 #ifdef GAWKDEBUG
00645         fputs(_("\t-W parsedebug\t\t--parsedebug\n"), fp);
00646 #endif
00647         fputs(_("\t-W profile[=file]\t--profile[=file]\n"), fp);
00648         fputs(_("\t-W posix\t\t--posix\n"), fp);
00649         fputs(_("\t-W re-interval\t\t--re-interval\n"), fp);
00650         fputs(_("\t-W source=program-text\t--source=program-text\n"), fp);
00651         fputs(_("\t-W traditional\t\t--traditional\n"), fp);
00652         fputs(_("\t-W usage\t\t--usage\n"), fp);
00653         fputs(_("\t-W version\t\t--version\n"), fp);
00654 
00655 
00656         /* This is one string to make things easier on translators. */
00657         fputs(_("\nTo report bugs, see node `Bugs' in `gawk.info', which is\n\
00658 section `Reporting Problems and Bugs' in the printed version.\n\n"), fp);
00659 
00660         /* ditto */
00661         fputs(_("gawk is a pattern scanning and processing language.\n\
00662 By default it reads standard input and writes standard output.\n\n"), fp);
00663 
00664         /* ditto */
00665         fputs(_("Examples:\n\tgawk '{ sum += $1 }; END { print sum }' file\n\
00666 \tgawk -F: '{ print $1 }' /etc/passwd\n"), fp);
00667 
00668         fflush(fp);
00669 
00670         if (ferror(fp))
00671                 exit(1);
00672 
00673         exit(exitval);
00674 }
00675 
00676 /* copyleft --- print out the short GNU copyright information */
00677 
00678 static void
00679 copyleft()
00680 {
00681         static const char blurb_part1[] =
00682           N_("Copyright (C) 1989, 1991-%d Free Software Foundation.\n\
00683 \n\
00684 This program is free software; you can redistribute it and/or modify\n\
00685 it under the terms of the GNU General Public License as published by\n\
00686 the Free Software Foundation; either version 2 of the License, or\n\
00687 (at your option) any later version.\n\
00688 \n");
00689         static const char blurb_part2[] =
00690           N_("This program is distributed in the hope that it will be useful,\n\
00691 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
00692 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
00693 GNU General Public License for more details.\n\
00694 \n");
00695         static const char blurb_part3[] =
00696           N_("You should have received a copy of the GNU General Public License\n\
00697 along with this program; if not, write to the Free Software\n\
00698 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n");
00699  
00700         /* multiple blurbs are needed for some brain dead compilers. */
00701         printf(_(blurb_part1), 2003);   /* Last update year */
00702         fputs(_(blurb_part2), stdout);
00703         fputs(_(blurb_part3), stdout);
00704         fflush(stdout);
00705 
00706         if (ferror(stdout))
00707                 exit(1);
00708 
00709         exit(0);
00710 }
00711 
00712 /* cmdline_fs --- set FS from the command line */
00713 
00714 static void
00715 cmdline_fs(char *str)
00716 {
00717         register NODE **tmp;
00718 
00719         tmp = get_lhs(FS_node, (Func_ptr *) 0, FALSE);
00720         unref(*tmp);
00721         /*
00722          * Only if in full compatibility mode check for the stupid special
00723          * case so -F\t works as documented in awk book even though the shell
00724          * hands us -Ft.  Bleah!
00725          *
00726          * Thankfully, Posix didn't propagate this "feature".
00727          */
00728         if (str[0] == 't' && str[1] == '\0') {
00729                 if (do_lint)
00730                         lintwarn(_("-Ft does not set FS to tab in POSIX awk"));
00731                 if (do_traditional && ! do_posix)
00732                         str[0] = '\t';
00733         }
00734         *tmp = make_str_node(str, strlen(str), SCAN); /* do process escapes */
00735         set_FS();
00736 }
00737 
00738 /* init_args --- set up ARGV from stuff on the command line */
00739 
00740 static void
00741 init_args(int argc0, int argc, char *argv0, char **argv)
00742 {
00743         int i, j;
00744         NODE **aptr;
00745 
00746         ARGV_node = install("ARGV", node((NODE *) NULL, Node_var_array, (NODE *) NULL));
00747         aptr = assoc_lookup(ARGV_node, tmp_number(0.0), FALSE);
00748         *aptr = make_string(argv0, strlen(argv0));
00749         (*aptr)->flags |= MAYBE_NUM;
00750         for (i = argc0, j = 1; i < argc; i++) {
00751                 aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j), FALSE);
00752                 *aptr = make_string(argv[i], strlen(argv[i]));
00753                 (*aptr)->flags |= MAYBE_NUM;
00754                 j++;
00755         }
00756         ARGC_node = install("ARGC",
00757                         node(make_number((AWKNUM) j), Node_var, (NODE *) NULL));
00758 }
00759 
00760 /*
00761  * Set all the special variables to their initial values.
00762  * Note that some of the variables that have set_FOO routines should
00763  * *N*O*T* have those routines called upon initialization, and thus
00764  * they have NULL entries in that field. This is notably true of FS
00765  * and IGNORECASE.
00766  */
00767 struct varinit {
00768         NODE **spec;
00769         const char *name;
00770         NODETYPE type;
00771         const char *strval;
00772         AWKNUM numval;
00773         Func_ptr assign;
00774 };
00775 static const struct varinit varinit[] = {
00776 {&CONVFMT_node, "CONVFMT",      Node_CONVFMT,           "%.6g", 0,  set_CONVFMT },
00777 {&NF_node,      "NF",           Node_NF,                NULL,   -1, NULL },
00778 {&FIELDWIDTHS_node, "FIELDWIDTHS", Node_FIELDWIDTHS,    "",     0,  NULL },
00779 {&NR_node,      "NR",           Node_NR,                NULL,   0,  set_NR },
00780 {&FNR_node,     "FNR",          Node_FNR,               NULL,   0,  set_FNR },
00781 {&FS_node,      "FS",           Node_FS,                " ",    0,  NULL },
00782 {&RS_node,      "RS",           Node_RS,                "\n",   0,  set_RS },
00783 {&IGNORECASE_node, "IGNORECASE", Node_IGNORECASE,       NULL,   0,  NULL },
00784 {&FILENAME_node, "FILENAME",    Node_var,               "",     0,  NULL },
00785 {&OFS_node,     "OFS",          Node_OFS,               " ",    0,  set_OFS },
00786 {&ORS_node,     "ORS",          Node_ORS,               "\n",   0,  set_ORS },
00787 {&OFMT_node,    "OFMT",         Node_OFMT,              "%.6g", 0,  set_OFMT },
00788 {&RLENGTH_node, "RLENGTH",      Node_var,               NULL,   0,  NULL },
00789 {&RSTART_node,  "RSTART",       Node_var,               NULL,   0,  NULL },
00790 {&SUBSEP_node,  "SUBSEP",       Node_var,               "\034", 0,  NULL },
00791 {&ARGIND_node,  "ARGIND",       Node_var,               NULL,   0,  NULL },
00792 {&ERRNO_node,   "ERRNO",        Node_var,               NULL,   0,  NULL },
00793 {&RT_node,      "RT",           Node_var,               "",     0,  NULL },
00794 {&BINMODE_node, "BINMODE",      Node_BINMODE,           NULL,   0,  NULL },
00795 {&LINT_node,    "LINT",         Node_LINT,              NULL,   0,  NULL },
00796 {&TEXTDOMAIN_node,      "TEXTDOMAIN",           Node_TEXTDOMAIN,        "messages",     0,  set_TEXTDOMAIN },
00797 {0,             NULL,           Node_illegal,           NULL,   0,  NULL },
00798 };
00799 
00800 /* init_vars --- actually initialize everything in the symbol table */
00801 
00802 static void
00803 init_vars()
00804 {
00805         register const struct varinit *vp;
00806 
00807         for (vp = varinit; vp->name; vp++) {
00808                 *(vp->spec) = install((char *) vp->name,
00809                   node(vp->strval == NULL ? make_number(vp->numval)
00810                                 : make_string((char *) vp->strval,
00811                                         strlen(vp->strval)),
00812                        vp->type, (NODE *) NULL));
00813                 if (vp->assign)
00814                         (*(vp->assign))();
00815         }
00816 }
00817 
00818 /* load_environ --- populate the ENVIRON array */
00819 
00820 NODE *
00821 load_environ()
00822 {
00823 #if ! defined(TANDEM)
00824 #if ! (defined(MSDOS) && !defined(DJGPP)) && ! defined(OS2) && ! (defined(VMS) && defined(__DECC))
00825         extern char **environ;
00826 #endif
00827         register char *var, *val;
00828         NODE **aptr;
00829         register int i;
00830 #endif /* TANDEM */
00831 
00832         ENVIRON_node = install("ENVIRON", 
00833                         node((NODE *) NULL, Node_var_array, (NODE *) NULL));
00834 #if ! defined(TANDEM)
00835         for (i = 0; environ[i] != NULL; i++) {
00836                 static char nullstr[] = "";
00837 
00838                 var = environ[i];
00839                 val = strchr(var, '=');
00840                 if (val != NULL)
00841                         *val++ = '\0';
00842                 else
00843                         val = nullstr;
00844                 aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen(var)),
00845                                     FALSE);
00846                 *aptr = make_string(val, strlen(val));
00847                 (*aptr)->flags |= MAYBE_NUM;
00848 
00849                 /* restore '=' so that system() gets a valid environment */
00850                 if (val != nullstr)
00851                         *--val = '=';
00852         }
00853         /*
00854          * Put AWKPATH into ENVIRON if it's not there.
00855          * This allows querying it from within awk programs.
00856          */
00857         if (getenv("AWKPATH") == NULL) {
00858                 aptr = assoc_lookup(ENVIRON_node, tmp_string("AWKPATH", 7), FALSE);
00859                 *aptr = make_string(defpath, strlen(defpath));
00860         }
00861 #endif /* TANDEM */
00862         return ENVIRON_node;
00863 }
00864 
00865 /* load_procinfo --- populate the PROCINFO array */
00866 
00867 NODE *
00868 load_procinfo()
00869 {
00870         int i;
00871         NODE **aptr;
00872         char name[100];
00873         AWKNUM value;
00874 
00875         PROCINFO_node = install("PROCINFO",
00876                         node((NODE *) NULL, Node_var_array, (NODE *) NULL));
00877 
00878 #ifdef GETPGRP_VOID
00879 #define getpgrp_arg() /* nothing */
00880 #else
00881 #define getpgrp_arg() getpid()
00882 #endif
00883 
00884         value = getpgrp(getpgrp_arg());
00885         aptr = assoc_lookup(PROCINFO_node, tmp_string("pgrpid", 6), FALSE);
00886         *aptr = make_number(value);
00887 
00888         /*
00889          * could put a lot of this into a table, but then there's
00890          * portability problems declaring all the functions. so just
00891          * do it the slow and stupid way. sigh.
00892          */
00893 
00894         value = getpid();
00895         aptr = assoc_lookup(PROCINFO_node, tmp_string("pid", 3), FALSE);
00896         *aptr = make_number(value);
00897 
00898         value = getppid();
00899         aptr = assoc_lookup(PROCINFO_node, tmp_string("ppid", 4), FALSE);
00900         *aptr = make_number(value);
00901 
00902         value = getuid();
00903         aptr = assoc_lookup(PROCINFO_node, tmp_string("uid", 3), FALSE);
00904         *aptr = make_number(value);
00905 
00906         value = geteuid();
00907         aptr = assoc_lookup(PROCINFO_node, tmp_string("euid", 4), FALSE);
00908         *aptr = make_number(value);
00909 
00910         value = getgid();
00911         aptr = assoc_lookup(PROCINFO_node, tmp_string("gid", 3), FALSE);
00912         *aptr = make_number(value);
00913 
00914         value = getegid();
00915         aptr = assoc_lookup(PROCINFO_node, tmp_string("egid", 4), FALSE);
00916         *aptr = make_number(value);
00917 
00918         aptr = assoc_lookup(PROCINFO_node, tmp_string("FS", 2), FALSE);
00919         *aptr = (using_fieldwidths() ? make_string("FIELDWIDTHS", 11) :
00920                                 make_string("FS", 2) );
00921 
00922 #if defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0
00923         for (i = 0; i < ngroups; i++) {
00924                 sprintf(name, "group%d", i + 1);
00925                 value = groupset[i];
00926                 aptr = assoc_lookup(PROCINFO_node, tmp_string(name, strlen(name)), FALSE);
00927                 *aptr = make_number(value);
00928         }
00929         if (groupset) {
00930                 free(groupset);
00931                 groupset = NULL;
00932         }
00933 #endif
00934         return PROCINFO_node;
00935 }
00936 
00937 /* arg_assign --- process a command-line assignment */
00938 
00939 int
00940 arg_assign(char *arg, int initing)
00941 {
00942         char *cp, *cp2;
00943         int badvar;
00944         Func_ptr after_assign = NULL;
00945         NODE *var;
00946         NODE *it;
00947         NODE **lhs;
00948 
00949         cp = strchr(arg, '=');
00950 
00951         if (cp == NULL) {
00952                 if (! initing)
00953                         return FALSE;   /* This is file name, not assignment. */
00954 
00955                 fprintf(stderr,
00956                         _("%s: `%s' argument to `-v' not in `var=value' form\n\n"),
00957                         myname, arg);
00958                 usage(1, stderr);
00959         }
00960 
00961         *cp++ = '\0';
00962 
00963         /* first check that the variable name has valid syntax */
00964         badvar = FALSE;
00965         if (! ISALPHA(arg[0]) && arg[0] != '_')
00966                 badvar = TRUE;
00967         else
00968                 for (cp2 = arg+1; *cp2; cp2++)
00969                         if (! ISALNUM(*cp2) && *cp2 != '_') {
00970                                 badvar = TRUE;
00971                                 break;
00972                         }
00973 
00974         if (badvar) {
00975                 if (initing)
00976                         fatal(_("`%s' is not a legal variable name"), arg);
00977 
00978                 if (do_lint)
00979                         lintwarn(_("`%s' is not a variable name, looking for file `%s=%s'"),
00980                                 arg, arg, cp);
00981         } else {
00982                 /*
00983                  * Recent versions of nawk expand escapes inside assignments.
00984                  * This makes sense, so we do it too.
00985                  */
00986                 it = make_str_node(cp, strlen(cp), SCAN);
00987                 it->flags |= MAYBE_NUM;
00988 #ifdef LC_NUMERIC
00989                 setlocale(LC_NUMERIC, "C");
00990                 (void) force_number(it);
00991                 setlocale(LC_NUMERIC, "");
00992 #endif /* LC_NUMERIC */
00993                 var = variable(arg, FALSE, Node_var);
00994                 lhs = get_lhs(var, &after_assign, FALSE);
00995                 unref(*lhs);
00996                 *lhs = it;
00997                 if (after_assign != NULL)
00998                         (*after_assign)();
00999         }
01000 
01001         *--cp = '=';    /* restore original text of ARGV */
01002 
01003         return ! badvar;
01004 }
01005 
01006 /* catchsig --- catch signals */
01007 
01008 static RETSIGTYPE
01009 catchsig(int sig)
01010 {
01011         if (sig == SIGFPE) {
01012                 fatal(_("floating point exception"));
01013         } else if (sig == SIGSEGV
01014 #ifdef SIGBUS
01015                 || sig == SIGBUS
01016 #endif
01017         ) {
01018                 set_loc(__FILE__, __LINE__);
01019                 msg(_("fatal error: internal error"));
01020                 /* fatal won't abort() if not compiled for debugging */
01021                 abort();
01022         } else
01023                 cant_happen();
01024         /* NOTREACHED */
01025 }
01026 
01027 /* nostalgia --- print the famous error message and die */
01028 
01029 static void
01030 nostalgia()
01031 {
01032         /*
01033          * N.B.: This string is not gettextized, on purpose.
01034          * So there.
01035          */
01036         fprintf(stderr, "awk: bailing out near line 1\n");
01037         fflush(stderr);
01038         abort();
01039 }
01040 
01041 /* version --- print version message */
01042 
01043 static void
01044 version()
01045 {
01046         printf("%s.%s\n", version_string, PATCHLEVEL);
01047         /*
01048          * Per GNU coding standards, print copyright info,
01049          * then exit successfully, do nothing else.
01050          */
01051         copyleft();
01052         exit(0);
01053 }
01054 
01055 /* init_fds --- check for 0, 1, 2, open on /dev/null if possible */
01056 
01057 static void
01058 init_fds()
01059 {
01060         struct stat sbuf;
01061         int fd;
01062         int newfd;
01063 
01064         /* maybe no stderr, don't bother with error mesg */
01065         for (fd = 0; fd <= 2; fd++) {
01066                 if (fstat(fd, &sbuf) < 0) {
01067 #if MAKE_A_HEROIC_EFFORT
01068                         if (do_lint)
01069                                 lintwarn(_("no pre-opened fd %d"), fd);
01070 #endif
01071                         newfd = devopen("/dev/null", "r+");
01072 #ifdef MAKE_A_HEROIC_EFFORT
01073                         if (do_lint && newfd < 0)
01074                                 lintwarn(_("could not pre-open /dev/null for fd %d"), fd);
01075 #endif
01076                 }
01077         }
01078 }
01079 
01080 /* init_groupset --- initialize groupset */
01081 
01082 static void
01083 init_groupset()
01084 {
01085 #if defined(HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0
01086 #ifdef GETGROUPS_NOT_STANDARD
01087         /* For systems that aren't standards conformant, use old way. */
01088         ngroups = NGROUPS_MAX;
01089 #else
01090         /*
01091          * If called with 0 for both args, return value is
01092          * total number of groups.
01093          */
01094         ngroups = getgroups(0, NULL);
01095 #endif
01096         if (ngroups == -1)
01097                 fatal(_("could not find groups: %s"), strerror(errno));
01098         else if (ngroups == 0)
01099                 return;
01100 
01101         /* fill in groups */