| 1 | /* | 
|---|
| 2 | * main.c -- Expression tree constructors and main program for gawk. | 
|---|
| 3 | */ | 
|---|
| 4 |  | 
|---|
| 5 | /* | 
|---|
| 6 | * Copyright (C) 1986, 1988, 1989, 1991-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA | 
|---|
| 24 | */ | 
|---|
| 25 |  | 
|---|
| 26 | /* FIX THIS BEFORE EVERY RELEASE: */ | 
|---|
| 27 | #define UPDATE_YEAR     2005 | 
|---|
| 28 |  | 
|---|
| 29 | #include "awk.h" | 
|---|
| 30 | #include "getopt.h" | 
|---|
| 31 |  | 
|---|
| 32 | #ifndef O_BINARY | 
|---|
| 33 | #include <fcntl.h> | 
|---|
| 34 | #endif | 
|---|
| 35 |  | 
|---|
| 36 | #ifdef HAVE_MCHECK_H | 
|---|
| 37 | #include <mcheck.h> | 
|---|
| 38 | #endif | 
|---|
| 39 |  | 
|---|
| 40 | #define DEFAULT_PROFILE         "awkprof.out"   /* where to put profile */ | 
|---|
| 41 | #define DEFAULT_VARFILE         "awkvars.out"   /* where to put vars */ | 
|---|
| 42 |  | 
|---|
| 43 | static const char *varfile = DEFAULT_VARFILE; | 
|---|
| 44 |  | 
|---|
| 45 | static void usage P((int exitval, FILE *fp)) ATTRIBUTE_NORETURN; | 
|---|
| 46 | static void copyleft P((void)) ATTRIBUTE_NORETURN; | 
|---|
| 47 | static void cmdline_fs P((char *str)); | 
|---|
| 48 | static void init_args P((int argc0, int argc, char *argv0, char **argv)); | 
|---|
| 49 | static void init_vars P((void)); | 
|---|
| 50 | static NODE *load_environ P((void)); | 
|---|
| 51 | static NODE *load_procinfo P((void)); | 
|---|
| 52 | static void add_src P((struct src **data, long *num, long *alloc, enum srctype stype, char *val)); | 
|---|
| 53 | static RETSIGTYPE catchsig P((int sig)) ATTRIBUTE_NORETURN; | 
|---|
| 54 | static void nostalgia P((void)) ATTRIBUTE_NORETURN; | 
|---|
| 55 | static void version P((void)) ATTRIBUTE_NORETURN; | 
|---|
| 56 | static void init_fds P((void)); | 
|---|
| 57 | static void init_groupset P((void)); | 
|---|
| 58 |  | 
|---|
| 59 | /* These nodes store all the special variables AWK uses */ | 
|---|
| 60 | NODE *ARGC_node, *ARGIND_node, *ARGV_node, *BINMODE_node, *CONVFMT_node; | 
|---|
| 61 | NODE *ENVIRON_node, *ERRNO_node, *FIELDWIDTHS_node, *FILENAME_node, *FNR_node; | 
|---|
| 62 | NODE *FS_node, *IGNORECASE_node, *NF_node, *NR_node, *OFMT_node, *OFS_node; | 
|---|
| 63 | NODE *ORS_node, *PROCINFO_node, *RLENGTH_node, *RSTART_node, *RS_node; | 
|---|
| 64 | NODE *RT_node, *SUBSEP_node, *LINT_node, *TEXTDOMAIN_node; | 
|---|
| 65 |  | 
|---|
| 66 | long NF; | 
|---|
| 67 | long NR; | 
|---|
| 68 | long FNR; | 
|---|
| 69 | int BINMODE; | 
|---|
| 70 | int IGNORECASE; | 
|---|
| 71 | char *OFS; | 
|---|
| 72 | char *ORS; | 
|---|
| 73 | char *OFMT; | 
|---|
| 74 | char *TEXTDOMAIN; | 
|---|
| 75 | int MRL;        /* See -mr option for use of this variable */ | 
|---|
| 76 |  | 
|---|
| 77 | /* | 
|---|
| 78 | * CONVFMT is a convenience pointer for the current number to string format. | 
|---|
| 79 | * We must supply an initial value to avoid recursion problems of | 
|---|
| 80 | *      set_CONVFMT -> fmt_index -> r_force_string: gets NULL CONVFMT | 
|---|
| 81 | * Fun, fun, fun, fun. | 
|---|
| 82 | */ | 
|---|
| 83 | char *CONVFMT = "%.6g"; | 
|---|
| 84 |  | 
|---|
| 85 |  | 
|---|
| 86 | int errcount = 0;               /* error counter, used by yyerror() */ | 
|---|
| 87 |  | 
|---|
| 88 | NODE *Nnull_string;             /* The global null string */ | 
|---|
| 89 |  | 
|---|
| 90 | #if defined(HAVE_LOCALE_H) | 
|---|
| 91 | struct lconv loc;               /* current locale */ | 
|---|
| 92 | #endif /* defined(HAVE_LOCALE_H) */ | 
|---|
| 93 |  | 
|---|
| 94 | /* The name the program was invoked under, for error messages */ | 
|---|
| 95 | const char *myname; | 
|---|
| 96 |  | 
|---|
| 97 | /* A block of AWK code to be run before running the program */ | 
|---|
| 98 | NODE *begin_block = NULL; | 
|---|
| 99 |  | 
|---|
| 100 | /* A block of AWK code to be run after the last input file */ | 
|---|
| 101 | NODE *end_block = NULL; | 
|---|
| 102 |  | 
|---|
| 103 | int exiting = FALSE;            /* Was an "exit" statement executed? */ | 
|---|
| 104 | int exit_val = 0;               /* optional exit value */ | 
|---|
| 105 |  | 
|---|
| 106 | #if defined(YYDEBUG) || defined(GAWKDEBUG) | 
|---|
| 107 | extern int yydebug; | 
|---|
| 108 | #endif | 
|---|
| 109 |  | 
|---|
| 110 | struct src *srcfiles = NULL;    /* source file name(s) */ | 
|---|
| 111 | long numfiles = -1;             /* how many source files */ | 
|---|
| 112 | static long allocfiles;         /* for how many is *srcfiles allocated */ | 
|---|
| 113 |  | 
|---|
| 114 | #define srcfiles_add(stype, val) \ | 
|---|
| 115 | add_src(&srcfiles, &numfiles, &allocfiles, stype, val) | 
|---|
| 116 |  | 
|---|
| 117 | static struct src *preassigns = NULL;   /* requested via -v or -F */ | 
|---|
| 118 | static long numassigns = -1;            /* how many of them */ | 
|---|
| 119 | static long allocassigns;               /* for how many is allocated */ | 
|---|
| 120 |  | 
|---|
| 121 | static int disallow_var_assigns = FALSE;        /* true for --exec */ | 
|---|
| 122 |  | 
|---|
| 123 | #define preassigns_add(stype, val) \ | 
|---|
| 124 | add_src(&preassigns, &numassigns, &allocassigns, stype, val) | 
|---|
| 125 |  | 
|---|
| 126 | #undef do_lint | 
|---|
| 127 | #undef do_lint_old | 
|---|
| 128 |  | 
|---|
| 129 | int do_traditional = FALSE;     /* no gnu extensions, add traditional weirdnesses */ | 
|---|
| 130 | int do_posix = FALSE;           /* turn off gnu and unix extensions */ | 
|---|
| 131 | int do_lint = FALSE;            /* provide warnings about questionable stuff */ | 
|---|
| 132 | int do_lint_old = FALSE;        /* warn about stuff not in V7 awk */ | 
|---|
| 133 | int do_intl = FALSE;            /* dump locale-izable strings to stdout */ | 
|---|
| 134 | int do_non_decimal_data = FALSE;        /* allow octal/hex C style DATA. Use with caution! */ | 
|---|
| 135 | int do_nostalgia = FALSE;       /* provide a blast from the past */ | 
|---|
| 136 | int do_intervals = FALSE;       /* allow {...,...} in regexps */ | 
|---|
| 137 | int do_profiling = FALSE;       /* profile and pretty print the program */ | 
|---|
| 138 | int do_dump_vars = FALSE;       /* dump all global variables at end */ | 
|---|
| 139 | int do_tidy_mem = FALSE;        /* release vars when done */ | 
|---|
| 140 |  | 
|---|
| 141 | int in_begin_rule = FALSE;      /* we're in a BEGIN rule */ | 
|---|
| 142 | int in_end_rule = FALSE;        /* we're in a END rule */ | 
|---|
| 143 | int whiny_users = FALSE;        /* do things that whiny users want */ | 
|---|
| 144 | #ifdef MBS_SUPPORT | 
|---|
| 145 | int gawk_mb_cur_max;            /* MB_CUR_MAX value, see comment in main() */ | 
|---|
| 146 | #else | 
|---|
| 147 | const int gawk_mb_cur_max = 1; | 
|---|
| 148 | #endif | 
|---|
| 149 |  | 
|---|
| 150 | int output_is_tty = FALSE;      /* control flushing of output */ | 
|---|
| 151 |  | 
|---|
| 152 | extern const char *version_string; | 
|---|
| 153 |  | 
|---|
| 154 | #if defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0 | 
|---|
| 155 | GETGROUPS_T *groupset;          /* current group set */ | 
|---|
| 156 | int ngroups;                    /* size of said set */ | 
|---|
| 157 | #endif | 
|---|
| 158 |  | 
|---|
| 159 | /* The parse tree is stored here.  */ | 
|---|
| 160 | NODE *expression_value; | 
|---|
| 161 |  | 
|---|
| 162 | #if _MSC_VER == 510 | 
|---|
| 163 | void (*lintfunc) P((va_list va_alist, ...)) = warning; | 
|---|
| 164 | #else | 
|---|
| 165 | #if defined(HAVE_STDARG_H) && defined(__STDC__) && __STDC__ | 
|---|
| 166 | void (*lintfunc) P((const char *mesg, ...)) = warning; | 
|---|
| 167 | #else | 
|---|
| 168 | void (*lintfunc) () = warning; | 
|---|
| 169 | #endif | 
|---|
| 170 | #endif | 
|---|
| 171 |  | 
|---|
| 172 | static const struct option optab[] = { | 
|---|
| 173 | { "compat",             no_argument,            & do_traditional,       1 }, | 
|---|
| 174 | { "traditional",        no_argument,            & do_traditional,       1 }, | 
|---|
| 175 | { "lint",               optional_argument,      NULL,           'l' }, | 
|---|
| 176 | { "lint-old",           no_argument,            & do_lint_old,  1 }, | 
|---|
| 177 | { "posix",              no_argument,            & do_posix,     1 }, | 
|---|
| 178 | { "nostalgia",          no_argument,            & do_nostalgia, 1 }, | 
|---|
| 179 | { "gen-po",             no_argument,            & do_intl,      1 }, | 
|---|
| 180 | { "non-decimal-data",   no_argument,            & do_non_decimal_data, 1 }, | 
|---|
| 181 | { "profile",            optional_argument,      NULL,           'p' }, | 
|---|
| 182 | { "copyleft",           no_argument,            NULL,           'C' }, | 
|---|
| 183 | { "copyright",          no_argument,            NULL,           'C' }, | 
|---|
| 184 | { "field-separator",    required_argument,      NULL,           'F' }, | 
|---|
| 185 | { "file",               required_argument,      NULL,           'f' }, | 
|---|
| 186 | { "re-interval",        no_argument,            & do_intervals, 1 }, | 
|---|
| 187 | { "source",             required_argument,      NULL,           's' }, | 
|---|
| 188 | { "dump-variables",     optional_argument,      NULL,           'd' }, | 
|---|
| 189 | { "assign",             required_argument,      NULL,           'v' }, | 
|---|
| 190 | { "version",            no_argument,            NULL,           'V' }, | 
|---|
| 191 | { "usage",              no_argument,            NULL,           'u' }, | 
|---|
| 192 | { "help",               no_argument,            NULL,           'u' }, | 
|---|
| 193 | { "exec",               required_argument,      NULL,           'S' }, | 
|---|
| 194 | #ifdef GAWKDEBUG | 
|---|
| 195 | { "parsedebug",         no_argument,            NULL,           'D' }, | 
|---|
| 196 | #endif | 
|---|
| 197 | { NULL, 0, NULL, '\0' } | 
|---|
| 198 | }; | 
|---|
| 199 |  | 
|---|
| 200 | #ifdef NO_LINT | 
|---|
| 201 | #define do_lint 0 | 
|---|
| 202 | #define do_lint_old 0 | 
|---|
| 203 | #endif | 
|---|
| 204 |  | 
|---|
| 205 | /* main --- process args, parse program, run it, clean up */ | 
|---|
| 206 |  | 
|---|
| 207 | int | 
|---|
| 208 | main(int argc, char **argv) | 
|---|
| 209 | { | 
|---|
| 210 | int c; | 
|---|
| 211 | char *scan; | 
|---|
| 212 | /* the + on the front tells GNU getopt not to rearrange argv */ | 
|---|
| 213 | const char *optlist = "+F:f:v:W;m:D"; | 
|---|
| 214 | int stopped_early = FALSE; | 
|---|
| 215 | int old_optind; | 
|---|
| 216 | extern int optind; | 
|---|
| 217 | extern int opterr; | 
|---|
| 218 | extern char *optarg; | 
|---|
| 219 | int i; | 
|---|
| 220 | int stdio_problem = FALSE; | 
|---|
| 221 |  | 
|---|
| 222 | /* do these checks early */ | 
|---|
| 223 | if (getenv("TIDYMEM") != NULL) | 
|---|
| 224 | do_tidy_mem = TRUE; | 
|---|
| 225 |  | 
|---|
| 226 | if (getenv("WHINY_USERS") != NULL) | 
|---|
| 227 | whiny_users = TRUE; | 
|---|
| 228 |  | 
|---|
| 229 | #ifdef HAVE_MCHECK_H | 
|---|
| 230 | if (do_tidy_mem) | 
|---|
| 231 | mtrace(); | 
|---|
| 232 | #endif /* HAVE_MCHECK_H */ | 
|---|
| 233 |  | 
|---|
| 234 | #if defined(LC_CTYPE) | 
|---|
| 235 | setlocale(LC_CTYPE, ""); | 
|---|
| 236 | #endif | 
|---|
| 237 | #if defined(LC_COLLATE) | 
|---|
| 238 | setlocale(LC_COLLATE, ""); | 
|---|
| 239 | #endif | 
|---|
| 240 | #if defined(LC_MESSAGES) | 
|---|
| 241 | setlocale(LC_MESSAGES, ""); | 
|---|
| 242 | #endif | 
|---|
| 243 | #if defined(LC_NUMERIC) | 
|---|
| 244 | /* | 
|---|
| 245 | * Force the issue here.  According to POSIX 2001, decimal | 
|---|
| 246 | * point is used for parsing source code and for command-line | 
|---|
| 247 | * assignments and the locale value for processing input, | 
|---|
| 248 | * number to string conversion, and printing output. | 
|---|
| 249 | */ | 
|---|
| 250 | setlocale(LC_NUMERIC, "C"); | 
|---|
| 251 | #endif | 
|---|
| 252 | #if defined(LC_TIME) | 
|---|
| 253 | setlocale(LC_TIME, ""); | 
|---|
| 254 | #endif | 
|---|
| 255 |  | 
|---|
| 256 | #ifdef MBS_SUPPORT | 
|---|
| 257 | /* | 
|---|
| 258 | * In glibc, MB_CUR_MAX is actually a function.  This value is | 
|---|
| 259 | * tested *a lot* in many speed-critical places in gawk. Caching | 
|---|
| 260 | * this value once makes a speed difference. | 
|---|
| 261 | */ | 
|---|
| 262 | gawk_mb_cur_max = MB_CUR_MAX; | 
|---|
| 263 | /* Without MBS_SUPPORT, gawk_mb_cur_max is 1. */ | 
|---|
| 264 | #endif | 
|---|
| 265 |  | 
|---|
| 266 | (void) bindtextdomain(PACKAGE, LOCALEDIR); | 
|---|
| 267 | (void) textdomain(PACKAGE); | 
|---|
| 268 |  | 
|---|
| 269 | (void) signal(SIGFPE, catchsig); | 
|---|
| 270 | (void) signal(SIGSEGV, catchsig); | 
|---|
| 271 | #ifdef SIGBUS | 
|---|
| 272 | (void) signal(SIGBUS, catchsig); | 
|---|
| 273 | #endif | 
|---|
| 274 |  | 
|---|
| 275 | myname = gawk_name(argv[0]); | 
|---|
| 276 | argv[0] = (char *) myname; | 
|---|
| 277 | os_arg_fixup(&argc, &argv); /* emulate redirection, expand wildcards */ | 
|---|
| 278 |  | 
|---|
| 279 | /* remove sccs gunk */ | 
|---|
| 280 | if (strncmp(version_string, "@(#)", 4) == 0) | 
|---|
| 281 | version_string += 4; | 
|---|
| 282 |  | 
|---|
| 283 | if (argc < 2) | 
|---|
| 284 | usage(1, stderr); | 
|---|
| 285 |  | 
|---|
| 286 | /* Robustness: check that file descriptors 0, 1, 2 are open */ | 
|---|
| 287 | init_fds(); | 
|---|
| 288 |  | 
|---|
| 289 | /* init array handling. */ | 
|---|
| 290 | array_init(); | 
|---|
| 291 |  | 
|---|
| 292 | /* we do error messages ourselves on invalid options */ | 
|---|
| 293 | opterr = FALSE; | 
|---|
| 294 |  | 
|---|
| 295 | /* option processing. ready, set, go! */ | 
|---|
| 296 | for (optopt = 0, old_optind = 1; | 
|---|
| 297 | (c = getopt_long(argc, argv, optlist, optab, NULL)) != EOF; | 
|---|
| 298 | optopt = 0, old_optind = optind) { | 
|---|
| 299 | if (do_posix) | 
|---|
| 300 | opterr = TRUE; | 
|---|
| 301 |  | 
|---|
| 302 | switch (c) { | 
|---|
| 303 | case 'F': | 
|---|
| 304 | preassigns_add(PRE_ASSIGN_FS, optarg); | 
|---|
| 305 | break; | 
|---|
| 306 |  | 
|---|
| 307 | case 'S': | 
|---|
| 308 | disallow_var_assigns = TRUE; | 
|---|
| 309 | /* fall through */ | 
|---|
| 310 | case 'f': | 
|---|
| 311 | /* | 
|---|
| 312 | * a la MKS awk, allow multiple -f options. | 
|---|
| 313 | * this makes function libraries real easy. | 
|---|
| 314 | * most of the magic is in the scanner. | 
|---|
| 315 | * | 
|---|
| 316 | * The following is to allow for whitespace at the end | 
|---|
| 317 | * of a #! /bin/gawk line in an executable file | 
|---|
| 318 | */ | 
|---|
| 319 | scan = optarg; | 
|---|
| 320 | if (argv[optind-1] != optarg) | 
|---|
| 321 | while (ISSPACE(*scan)) | 
|---|
| 322 | scan++; | 
|---|
| 323 | srcfiles_add(SOURCEFILE, | 
|---|
| 324 | (*scan == '\0' ? argv[optind++] : optarg)); | 
|---|
| 325 | break; | 
|---|
| 326 |  | 
|---|
| 327 | case 'v': | 
|---|
| 328 | preassigns_add(PRE_ASSIGN, optarg); | 
|---|
| 329 | break; | 
|---|
| 330 |  | 
|---|
| 331 | case 'm': | 
|---|
| 332 | /* | 
|---|
| 333 | * Research awk extension. | 
|---|
| 334 | *      -mf nnn         set # fields, gawk ignores | 
|---|
| 335 | *      -mr nnn         set record length, ditto | 
|---|
| 336 | */ | 
|---|
| 337 | if (do_lint) | 
|---|
| 338 | lintwarn(_("`-m[fr]' option irrelevant in gawk")); | 
|---|
| 339 | if (optarg[0] != 'r' && optarg[0] != 'f') | 
|---|
| 340 | warning(_("-m option usage: `-m[fr] nnn'")); | 
|---|
| 341 | /* | 
|---|
| 342 | * Set fixed length records for Tandem, | 
|---|
| 343 | * ignored on other platforms (see io.c:get_a_record). | 
|---|
| 344 | */ | 
|---|
| 345 | if (optarg[0] == 'r') { | 
|---|
| 346 | if (ISDIGIT(optarg[1])) | 
|---|
| 347 | MRL = atoi(optarg+1); | 
|---|
| 348 | else { | 
|---|
| 349 | MRL = atoi(argv[optind]); | 
|---|
| 350 | optind++; | 
|---|
| 351 | } | 
|---|
| 352 | } else if (optarg[1] == '\0') | 
|---|
| 353 | optind++; | 
|---|
| 354 | break; | 
|---|
| 355 |  | 
|---|
| 356 | case 'W':       /* gawk specific options - now in getopt_long */ | 
|---|
| 357 | fprintf(stderr, _("%s: option `-W %s' unrecognized, ignored\n"), | 
|---|
| 358 | argv[0], optarg); | 
|---|
| 359 | break; | 
|---|
| 360 |  | 
|---|
| 361 | /* These can only come from long form options */ | 
|---|
| 362 | case 'C': | 
|---|
| 363 | copyleft(); | 
|---|
| 364 | break; | 
|---|
| 365 |  | 
|---|
| 366 | case 'd': | 
|---|
| 367 | do_dump_vars = TRUE; | 
|---|
| 368 | if (optarg != NULL && optarg[0] != '\0') | 
|---|
| 369 | varfile = optarg; | 
|---|
| 370 | break; | 
|---|
| 371 |  | 
|---|
| 372 | case 'l': | 
|---|
| 373 | #ifndef NO_LINT | 
|---|
| 374 | do_lint = LINT_ALL; | 
|---|
| 375 | if (optarg != NULL) { | 
|---|
| 376 | if (strcmp(optarg, "fatal") == 0) | 
|---|
| 377 | lintfunc = r_fatal; | 
|---|
| 378 | else if (strcmp(optarg, "invalid") == 0) | 
|---|
| 379 | do_lint = LINT_INVALID; | 
|---|
| 380 | } | 
|---|
| 381 | #endif | 
|---|
| 382 | break; | 
|---|
| 383 |  | 
|---|
| 384 | case 'p': | 
|---|
| 385 | do_profiling = TRUE; | 
|---|
| 386 | if (optarg != NULL) | 
|---|
| 387 | set_prof_file(optarg); | 
|---|
| 388 | else | 
|---|
| 389 | set_prof_file(DEFAULT_PROFILE); | 
|---|
| 390 | break; | 
|---|
| 391 |  | 
|---|
| 392 | case 's': | 
|---|
| 393 | if (optarg[0] == '\0') | 
|---|
| 394 | warning(_("empty argument to `--source' ignored")); | 
|---|
| 395 | else | 
|---|
| 396 | srcfiles_add(CMDLINE, optarg); | 
|---|
| 397 | break; | 
|---|
| 398 |  | 
|---|
| 399 | case 'u': | 
|---|
| 400 | usage(0, stdout);       /* per coding stds */ | 
|---|
| 401 | break; | 
|---|
| 402 |  | 
|---|
| 403 | case 'V': | 
|---|
| 404 | version(); | 
|---|
| 405 | break; | 
|---|
| 406 |  | 
|---|
| 407 | case 0: | 
|---|
| 408 | /* | 
|---|
| 409 | * getopt_long found an option that sets a variable | 
|---|
| 410 | * instead of returning a letter. Do nothing, just | 
|---|
| 411 | * cycle around for the next one. | 
|---|
| 412 | */ | 
|---|
| 413 | break; | 
|---|
| 414 |  | 
|---|
| 415 | case 'D': | 
|---|
| 416 | #ifdef GAWKDEBUG | 
|---|
| 417 | yydebug = 2; | 
|---|
| 418 | break; | 
|---|
| 419 | #endif | 
|---|
| 420 | /* if not debugging, fall through */ | 
|---|
| 421 |  | 
|---|
| 422 | case '?': | 
|---|
| 423 | default: | 
|---|
| 424 | /* | 
|---|
| 425 | * New behavior.  If not posix, an unrecognized | 
|---|
| 426 | * option stops argument processing so that it can | 
|---|
| 427 | * go into ARGV for the awk program to see. This | 
|---|
| 428 | * makes use of ``#! /bin/gawk -f'' easier. | 
|---|
| 429 | * | 
|---|
| 430 | * However, it's never simple. If optopt is set, | 
|---|
| 431 | * an option that requires an argument didn't get the | 
|---|
| 432 | * argument. We care because if opterr is 0, then | 
|---|
| 433 | * getopt_long won't print the error message for us. | 
|---|
| 434 | */ | 
|---|
| 435 | if (! do_posix | 
|---|
| 436 | && (optopt == '\0' || strchr(optlist, optopt) == NULL)) { | 
|---|
| 437 | /* | 
|---|
| 438 | * can't just do optind--. In case of an | 
|---|
| 439 | * option with >= 2 letters, getopt_long | 
|---|
| 440 | * won't have incremented optind. | 
|---|
| 441 | */ | 
|---|
| 442 | optind = old_optind; | 
|---|
| 443 | stopped_early = TRUE; | 
|---|
| 444 | goto out; | 
|---|
| 445 | } else if (optopt != '\0') | 
|---|
| 446 | /* Use 1003.2 required message format */ | 
|---|
| 447 | fprintf(stderr, | 
|---|
| 448 | _("%s: option requires an argument -- %c\n"), | 
|---|
| 449 | myname, optopt); | 
|---|
| 450 | /* else | 
|---|
| 451 | let getopt print error message for us */ | 
|---|
| 452 | break; | 
|---|
| 453 | } | 
|---|
| 454 | if (c == 'S')   /* --exec ends option processing */ | 
|---|
| 455 | break; | 
|---|
| 456 | } | 
|---|
| 457 | out: | 
|---|
| 458 |  | 
|---|
| 459 | if (do_nostalgia) | 
|---|
| 460 | nostalgia(); | 
|---|
| 461 |  | 
|---|
| 462 | /* check for POSIXLY_CORRECT environment variable */ | 
|---|
| 463 | if (! do_posix && getenv("POSIXLY_CORRECT") != NULL) { | 
|---|
| 464 | do_posix = TRUE; | 
|---|
| 465 | if (do_lint) | 
|---|
| 466 | lintwarn( | 
|---|
| 467 | _("environment variable `POSIXLY_CORRECT' set: turning on `--posix'")); | 
|---|
| 468 | } | 
|---|
| 469 |  | 
|---|
| 470 | if (do_posix) { | 
|---|
| 471 | if (do_traditional)     /* both on command line */ | 
|---|
| 472 | warning(_("`--posix' overrides `--traditional'")); | 
|---|
| 473 | else | 
|---|
| 474 | do_traditional = TRUE; | 
|---|
| 475 | /* | 
|---|
| 476 | * POSIX compliance also implies | 
|---|
| 477 | * no GNU extensions either. | 
|---|
| 478 | */ | 
|---|
| 479 | } | 
|---|
| 480 |  | 
|---|
| 481 | if (do_traditional && do_non_decimal_data) { | 
|---|
| 482 | do_non_decimal_data = FALSE; | 
|---|
| 483 | warning(_("`--posix'/`--traditional' overrides `--non-decimal-data'")); | 
|---|
| 484 | } | 
|---|
| 485 |  | 
|---|
| 486 | if (do_lint && os_is_setuid()) | 
|---|
| 487 | warning(_("running %s setuid root may be a security problem"), myname); | 
|---|
| 488 |  | 
|---|
| 489 | /* | 
|---|
| 490 | * Force profiling if this is pgawk. | 
|---|
| 491 | * Don't bother if the command line already set profiling up. | 
|---|
| 492 | */ | 
|---|
| 493 | if (! do_profiling) | 
|---|
| 494 | init_profiling(& do_profiling, DEFAULT_PROFILE); | 
|---|
| 495 |  | 
|---|
| 496 | /* load group set */ | 
|---|
| 497 | init_groupset(); | 
|---|
| 498 |  | 
|---|
| 499 | /* initialize the null string */ | 
|---|
| 500 | Nnull_string = make_string("", 0); | 
|---|
| 501 | Nnull_string->numbr = 0.0; | 
|---|
| 502 | Nnull_string->type = Node_val; | 
|---|
| 503 | Nnull_string->flags = (PERM|STRCUR|STRING|NUMCUR|NUMBER); | 
|---|
| 504 |  | 
|---|
| 505 | /* | 
|---|
| 506 | * Tell the regex routines how they should work. | 
|---|
| 507 | * Do this before initializing variables, since | 
|---|
| 508 | * they could want to do a regexp compile. | 
|---|
| 509 | */ | 
|---|
| 510 | resetup(); | 
|---|
| 511 |  | 
|---|
| 512 | /* Set up the special variables */ | 
|---|
| 513 | init_vars(); | 
|---|
| 514 |  | 
|---|
| 515 | /* Set up the field variables */ | 
|---|
| 516 | init_fields(); | 
|---|
| 517 |  | 
|---|
| 518 | /* Now process the pre-assignments */ | 
|---|
| 519 | for (i = 0; i <= numassigns; i++) | 
|---|
| 520 | if (preassigns[i].stype == PRE_ASSIGN) | 
|---|
| 521 | (void) arg_assign(preassigns[i].val, TRUE); | 
|---|
| 522 | else    /* PRE_ASSIGN_FS */ | 
|---|
| 523 | cmdline_fs(preassigns[i].val); | 
|---|
| 524 | free(preassigns); | 
|---|
| 525 |  | 
|---|
| 526 | if ((BINMODE & 1) != 0) | 
|---|
| 527 | if (os_setbinmode(fileno(stdin), O_BINARY) == -1) | 
|---|
| 528 | fatal(_("can't set binary mode on stdin (%s)"), strerror(errno)); | 
|---|
| 529 | if ((BINMODE & 2) != 0) { | 
|---|
| 530 | if (os_setbinmode(fileno(stdout), O_BINARY) == -1) | 
|---|
| 531 | fatal(_("can't set binary mode on stdout (%s)"), strerror(errno)); | 
|---|
| 532 | if (os_setbinmode(fileno(stderr), O_BINARY) == -1) | 
|---|
| 533 | fatal(_("can't set binary mode on stderr (%s)"), strerror(errno)); | 
|---|
| 534 | } | 
|---|
| 535 |  | 
|---|
| 536 | #ifdef GAWKDEBUG | 
|---|
| 537 | setbuf(stdout, (char *) NULL);  /* make debugging easier */ | 
|---|
| 538 | #endif | 
|---|
| 539 | if (isatty(fileno(stdout))) | 
|---|
| 540 | output_is_tty = TRUE; | 
|---|
| 541 | /* No -f or --source options, use next arg */ | 
|---|
| 542 | if (numfiles == -1) { | 
|---|
| 543 | if (optind > argc - 1 || stopped_early) /* no args left or no program */ | 
|---|
| 544 | usage(1, stderr); | 
|---|
| 545 | srcfiles_add(CMDLINE, argv[optind]); | 
|---|
| 546 | optind++; | 
|---|
| 547 | } | 
|---|
| 548 |  | 
|---|
| 549 | init_args(optind, argc, (char *) myname, argv); | 
|---|
| 550 | (void) tokexpand(); | 
|---|
| 551 |  | 
|---|
| 552 | #if defined(LC_NUMERIC) | 
|---|
| 553 | /* | 
|---|
| 554 | * FRAGILE!  CAREFUL! | 
|---|
| 555 | * Pre-initing the variables with arg_assign() can change the | 
|---|
| 556 | * locale.  Force it to C before parsing the program. | 
|---|
| 557 | */ | 
|---|
| 558 | setlocale(LC_NUMERIC, "C"); | 
|---|
| 559 | #endif | 
|---|
| 560 |  | 
|---|
| 561 | /* Read in the program */ | 
|---|
| 562 | if (yyparse() != 0 || errcount != 0) | 
|---|
| 563 | exit(1); | 
|---|
| 564 |  | 
|---|
| 565 | free(srcfiles); | 
|---|
| 566 |  | 
|---|
| 567 | if (do_intl) | 
|---|
| 568 | exit(0); | 
|---|
| 569 |  | 
|---|
| 570 | if (do_lint && begin_block == NULL && expression_value == NULL | 
|---|
| 571 | && end_block == NULL) | 
|---|
| 572 | lintwarn(_("no program text at all!")); | 
|---|
| 573 |  | 
|---|
| 574 | if (do_lint) | 
|---|
| 575 | shadow_funcs(); | 
|---|
| 576 |  | 
|---|
| 577 | init_profiling_signals(); | 
|---|
| 578 |  | 
|---|
| 579 | #if defined(LC_NUMERIC) | 
|---|
| 580 | /* See comment above. */ | 
|---|
| 581 | setlocale(LC_NUMERIC, ""); | 
|---|
| 582 | #endif | 
|---|
| 583 |  | 
|---|
| 584 | #if defined(HAVE_LOCALE_H) | 
|---|
| 585 | loc = *localeconv();    /* Make a local copy of locale numeric info */ | 
|---|
| 586 | #endif | 
|---|
| 587 |  | 
|---|
| 588 | /* Whew. Finally, run the program. */ | 
|---|
| 589 | if (begin_block != NULL) { | 
|---|
| 590 | in_begin_rule = TRUE; | 
|---|
| 591 | (void) interpret(begin_block); | 
|---|
| 592 | } | 
|---|
| 593 | in_begin_rule = FALSE; | 
|---|
| 594 | if (! exiting && (expression_value != NULL || end_block != NULL)) | 
|---|
| 595 | do_input(); | 
|---|
| 596 | if (end_block != NULL) { | 
|---|
| 597 | in_end_rule = TRUE; | 
|---|
| 598 | (void) interpret(end_block); | 
|---|
| 599 | } | 
|---|
| 600 | in_end_rule = FALSE; | 
|---|
| 601 | /* | 
|---|
| 602 | * This used to be: | 
|---|
| 603 | * | 
|---|
| 604 | * if (close_io() != 0 && ! exiting && exit_val == 0) | 
|---|
| 605 | *      exit_val = 1; | 
|---|
| 606 | * | 
|---|
| 607 | * Other awks don't care about problems closing open files | 
|---|
| 608 | * and pipes, in that it doesn't affect their exit status. | 
|---|
| 609 | * So we no longer do either. | 
|---|
| 610 | */ | 
|---|
| 611 | (void) close_io(& stdio_problem); | 
|---|
| 612 | /* | 
|---|
| 613 | * However, we do want to exit non-zero if there was a problem | 
|---|
| 614 | * with stdout/stderr, so we reinstate a slightly different | 
|---|
| 615 | * version of the above: | 
|---|
| 616 | */ | 
|---|
| 617 | if (stdio_problem && ! exiting && exit_val == 0) | 
|---|
| 618 | exit_val = 1; | 
|---|
| 619 |  | 
|---|
| 620 | if (do_profiling) { | 
|---|
| 621 | dump_prog(begin_block, expression_value, end_block); | 
|---|
| 622 | dump_funcs(); | 
|---|
| 623 | } | 
|---|
| 624 |  | 
|---|
| 625 | if (do_dump_vars) | 
|---|
| 626 | dump_vars(varfile); | 
|---|
| 627 |  | 
|---|
| 628 | if (do_tidy_mem) | 
|---|
| 629 | release_all_vars(); | 
|---|
| 630 |  | 
|---|
| 631 | exit(exit_val);         /* more portable */ | 
|---|
| 632 | return exit_val;        /* to suppress warnings */ | 
|---|
| 633 | } | 
|---|
| 634 |  | 
|---|
| 635 | /* add_src --- add one element to *srcfiles or *preassigns */ | 
|---|
| 636 |  | 
|---|
| 637 | static void | 
|---|
| 638 | add_src(struct src **data, long *num, long *alloc, enum srctype stype, char *val) | 
|---|
| 639 | { | 
|---|
| 640 | #define INIT_SRC 4 | 
|---|
| 641 |  | 
|---|
| 642 | ++*num; | 
|---|
| 643 |  | 
|---|
| 644 | if (*data == NULL) { | 
|---|
| 645 | emalloc(*data, struct src *, INIT_SRC * sizeof(struct src), "add_src"); | 
|---|
| 646 | *alloc = INIT_SRC; | 
|---|
| 647 | } else if (*num >= *alloc) { | 
|---|
| 648 | (*alloc) *= 2; | 
|---|
| 649 | erealloc(*data, struct src *, (*alloc) * sizeof(struct src), "add_src"); | 
|---|
| 650 | } | 
|---|
| 651 |  | 
|---|
| 652 | (*data)[*num].stype = stype; | 
|---|
| 653 | (*data)[*num].val = val; | 
|---|
| 654 |  | 
|---|
| 655 | #undef INIT_SRC | 
|---|
| 656 | } | 
|---|
| 657 |  | 
|---|
| 658 | /* usage --- print usage information and exit */ | 
|---|
| 659 |  | 
|---|
| 660 | static void | 
|---|
| 661 | usage(int exitval, FILE *fp) | 
|---|
| 662 | { | 
|---|
| 663 |  | 
|---|
| 664 | /* Not factoring out common stuff makes it easier to translate. */ | 
|---|
| 665 | fprintf(fp, _("Usage: %s [POSIX or GNU style options] -f progfile [--] file ...\n"), | 
|---|
| 666 | myname); | 
|---|
| 667 | fprintf(fp, _("Usage: %s [POSIX or GNU style options] [--] %cprogram%c file ...\n"), | 
|---|
| 668 | myname, quote, quote); | 
|---|
| 669 |  | 
|---|
| 670 | /* GNU long options info. This is too many options. */ | 
|---|
| 671 |  | 
|---|
| 672 | fputs(_("POSIX options:\t\tGNU long options:\n"), fp); | 
|---|
| 673 | fputs(_("\t-f progfile\t\t--file=progfile\n"), fp); | 
|---|
| 674 | fputs(_("\t-F fs\t\t\t--field-separator=fs\n"), fp); | 
|---|
| 675 | fputs(_("\t-v var=val\t\t--assign=var=val\n"), fp); | 
|---|
| 676 | fputs(_("\t-m[fr] val\n"), fp); | 
|---|
| 677 | fputs(_("\t-W compat\t\t--compat\n"), fp); | 
|---|
| 678 | fputs(_("\t-W copyleft\t\t--copyleft\n"), fp); | 
|---|
| 679 | fputs(_("\t-W copyright\t\t--copyright\n"), fp); | 
|---|
| 680 | fputs(_("\t-W dump-variables[=file]\t--dump-variables[=file]\n"), fp); | 
|---|
| 681 | fputs(_("\t-W exec=file\t\t--exec=file\n"), fp); | 
|---|
| 682 | fputs(_("\t-W gen-po\t\t--gen-po\n"), fp); | 
|---|
| 683 | fputs(_("\t-W help\t\t\t--help\n"), fp); | 
|---|
| 684 | fputs(_("\t-W lint[=fatal]\t\t--lint[=fatal]\n"), fp); | 
|---|
| 685 | fputs(_("\t-W lint-old\t\t--lint-old\n"), fp); | 
|---|
| 686 | fputs(_("\t-W non-decimal-data\t--non-decimal-data\n"), fp); | 
|---|
| 687 | #ifdef NOSTALGIA | 
|---|
| 688 | fputs(_("\t-W nostalgia\t\t--nostalgia\n"), fp); | 
|---|
| 689 | #endif | 
|---|
| 690 | #ifdef GAWKDEBUG | 
|---|
| 691 | fputs(_("\t-W parsedebug\t\t--parsedebug\n"), fp); | 
|---|
| 692 | #endif | 
|---|
| 693 | fputs(_("\t-W profile[=file]\t--profile[=file]\n"), fp); | 
|---|
| 694 | fputs(_("\t-W posix\t\t--posix\n"), fp); | 
|---|
| 695 | fputs(_("\t-W re-interval\t\t--re-interval\n"), fp); | 
|---|
| 696 | fputs(_("\t-W source=program-text\t--source=program-text\n"), fp); | 
|---|
| 697 | fputs(_("\t-W traditional\t\t--traditional\n"), fp); | 
|---|
| 698 | fputs(_("\t-W usage\t\t--usage\n"), fp); | 
|---|
| 699 | fputs(_("\t-W version\t\t--version\n"), fp); | 
|---|
| 700 |  | 
|---|
| 701 |  | 
|---|
| 702 | /* This is one string to make things easier on translators. */ | 
|---|
| 703 | fputs(_("\nTo report bugs, see node `Bugs' in `gawk.info', which is\n\ | 
|---|
| 704 | section `Reporting Problems and Bugs' in the printed version.\n\n"), fp); | 
|---|
| 705 |  | 
|---|
| 706 | /* ditto */ | 
|---|
| 707 | fputs(_("gawk is a pattern scanning and processing language.\n\ | 
|---|
| 708 | By default it reads standard input and writes standard output.\n\n"), fp); | 
|---|
| 709 |  | 
|---|
| 710 | /* ditto */ | 
|---|
| 711 | fputs(_("Examples:\n\tgawk '{ sum += $1 }; END { print sum }' file\n\ | 
|---|
| 712 | \tgawk -F: '{ print $1 }' /etc/passwd\n"), fp); | 
|---|
| 713 |  | 
|---|
| 714 | fflush(fp); | 
|---|
| 715 |  | 
|---|
| 716 | if (ferror(fp)) { | 
|---|
| 717 | if (fp == stdout) | 
|---|
| 718 | warning(_("error writing standard output (%s)"), strerror(errno)); | 
|---|
| 719 | exit(1); | 
|---|
| 720 | } | 
|---|
| 721 |  | 
|---|
| 722 | exit(exitval); | 
|---|
| 723 | } | 
|---|
| 724 |  | 
|---|
| 725 | /* copyleft --- print out the short GNU copyright information */ | 
|---|
| 726 |  | 
|---|
| 727 | static void | 
|---|
| 728 | copyleft() | 
|---|
| 729 | { | 
|---|
| 730 | static const char blurb_part1[] = | 
|---|
| 731 | N_("Copyright (C) 1989, 1991-%d Free Software Foundation.\n\ | 
|---|
| 732 | \n\ | 
|---|
| 733 | This program is free software; you can redistribute it and/or modify\n\ | 
|---|
| 734 | it under the terms of the GNU General Public License as published by\n\ | 
|---|
| 735 | the Free Software Foundation; either version 2 of the License, or\n\ | 
|---|
| 736 | (at your option) any later version.\n\ | 
|---|
| 737 | \n"); | 
|---|
| 738 | static const char blurb_part2[] = | 
|---|
| 739 | N_("This program is distributed in the hope that it will be useful,\n\ | 
|---|
| 740 | but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ | 
|---|
| 741 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\ | 
|---|
| 742 | GNU General Public License for more details.\n\ | 
|---|
| 743 | \n"); | 
|---|
| 744 | static const char blurb_part3[] = | 
|---|
| 745 | N_("You should have received a copy of the GNU General Public License\n\ | 
|---|
| 746 | along with this program; if not, write to the Free Software\n\ | 
|---|
| 747 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.\n"); | 
|---|
| 748 |  | 
|---|
| 749 | /* multiple blurbs are needed for some brain dead compilers. */ | 
|---|
| 750 | printf(_(blurb_part1), UPDATE_YEAR);    /* Last update year */ | 
|---|
| 751 | fputs(_(blurb_part2), stdout); | 
|---|
| 752 | fputs(_(blurb_part3), stdout); | 
|---|
| 753 | fflush(stdout); | 
|---|
| 754 |  | 
|---|
| 755 | if (ferror(stdout)) { | 
|---|
| 756 | warning(_("error writing standard output (%s)"), strerror(errno)); | 
|---|
| 757 | exit(1); | 
|---|
| 758 | } | 
|---|
| 759 |  | 
|---|
| 760 | exit(0); | 
|---|
| 761 | } | 
|---|
| 762 |  | 
|---|
| 763 | /* cmdline_fs --- set FS from the command line */ | 
|---|
| 764 |  | 
|---|
| 765 | static void | 
|---|
| 766 | cmdline_fs(char *str) | 
|---|
| 767 | { | 
|---|
| 768 | register NODE **tmp; | 
|---|
| 769 |  | 
|---|
| 770 | tmp = get_lhs(FS_node, (Func_ptr *) 0, FALSE); | 
|---|
| 771 | unref(*tmp); | 
|---|
| 772 | /* | 
|---|
| 773 | * Only if in full compatibility mode check for the stupid special | 
|---|
| 774 | * case so -F\t works as documented in awk book even though the shell | 
|---|
| 775 | * hands us -Ft.  Bleah! | 
|---|
| 776 | * | 
|---|
| 777 | * Thankfully, Posix didn't propagate this "feature". | 
|---|
| 778 | */ | 
|---|
| 779 | if (str[0] == 't' && str[1] == '\0') { | 
|---|
| 780 | if (do_lint) | 
|---|
| 781 | lintwarn(_("-Ft does not set FS to tab in POSIX awk")); | 
|---|
| 782 | if (do_traditional && ! do_posix) | 
|---|
| 783 | str[0] = '\t'; | 
|---|
| 784 | } | 
|---|
| 785 | *tmp = make_str_node(str, strlen(str), SCAN); /* do process escapes */ | 
|---|
| 786 | set_FS(); | 
|---|
| 787 | } | 
|---|
| 788 |  | 
|---|
| 789 | /* init_args --- set up ARGV from stuff on the command line */ | 
|---|
| 790 |  | 
|---|
| 791 | static void | 
|---|
| 792 | init_args(int argc0, int argc, char *argv0, char **argv) | 
|---|
| 793 | { | 
|---|
| 794 | int i, j; | 
|---|
| 795 | NODE **aptr; | 
|---|
| 796 |  | 
|---|
| 797 | ARGV_node = install("ARGV", node((NODE *) NULL, Node_var_array, (NODE *) NULL)); | 
|---|
| 798 | aptr = assoc_lookup(ARGV_node, tmp_number(0.0), FALSE); | 
|---|
| 799 | *aptr = make_string(argv0, strlen(argv0)); | 
|---|
| 800 | (*aptr)->flags |= MAYBE_NUM; | 
|---|
| 801 | for (i = argc0, j = 1; i < argc; i++) { | 
|---|
| 802 | aptr = assoc_lookup(ARGV_node, tmp_number((AWKNUM) j), FALSE); | 
|---|
| 803 | *aptr = make_string(argv[i], strlen(argv[i])); | 
|---|
| 804 | (*aptr)->flags |= MAYBE_NUM; | 
|---|
| 805 | j++; | 
|---|
| 806 | } | 
|---|
| 807 | ARGC_node = install("ARGC", | 
|---|
| 808 | node(make_number((AWKNUM) j), Node_var, (NODE *) NULL)); | 
|---|
| 809 | } | 
|---|
| 810 |  | 
|---|
| 811 | /* | 
|---|
| 812 | * Set all the special variables to their initial values. | 
|---|
| 813 | * Note that some of the variables that have set_FOO routines should | 
|---|
| 814 | * *N*O*T* have those routines called upon initialization, and thus | 
|---|
| 815 | * they have NULL entries in that field. This is notably true of FS | 
|---|
| 816 | * and IGNORECASE. | 
|---|
| 817 | */ | 
|---|
| 818 | struct varinit { | 
|---|
| 819 | NODE **spec; | 
|---|
| 820 | const char *name; | 
|---|
| 821 | NODETYPE type; | 
|---|
| 822 | const char *strval; | 
|---|
| 823 | AWKNUM numval; | 
|---|
| 824 | Func_ptr assign; | 
|---|
| 825 | }; | 
|---|
| 826 | static const struct varinit varinit[] = { | 
|---|
| 827 | {&CONVFMT_node, "CONVFMT",      Node_CONVFMT,           "%.6g", 0,  set_CONVFMT }, | 
|---|
| 828 | {&NF_node,      "NF",           Node_NF,                NULL,   -1, NULL }, | 
|---|
| 829 | {&FIELDWIDTHS_node, "FIELDWIDTHS", Node_FIELDWIDTHS,    "",     0,  NULL }, | 
|---|
| 830 | {&NR_node,      "NR",           Node_NR,                NULL,   0,  set_NR }, | 
|---|
| 831 | {&FNR_node,     "FNR",          Node_FNR,               NULL,   0,  set_FNR }, | 
|---|
| 832 | {&FS_node,      "FS",           Node_FS,                " ",    0,  NULL }, | 
|---|
| 833 | {&RS_node,      "RS",           Node_RS,                "\n",   0,  set_RS }, | 
|---|
| 834 | {&IGNORECASE_node, "IGNORECASE", Node_IGNORECASE,       NULL,   0,  NULL }, | 
|---|
| 835 | {&FILENAME_node, "FILENAME",    Node_var,               "",     0,  NULL }, | 
|---|
| 836 | {&OFS_node,     "OFS",          Node_OFS,               " ",    0,  set_OFS }, | 
|---|
| 837 | {&ORS_node,     "ORS",          Node_ORS,               "\n",   0,  set_ORS }, | 
|---|
| 838 | {&OFMT_node,    "OFMT",         Node_OFMT,              "%.6g", 0,  set_OFMT }, | 
|---|
| 839 | {&RLENGTH_node, "RLENGTH",      Node_var,               NULL,   0,  NULL }, | 
|---|
| 840 | {&RSTART_node,  "RSTART",       Node_var,               NULL,   0,  NULL }, | 
|---|
| 841 | {&SUBSEP_node,  "SUBSEP",       Node_SUBSEP,            "\034", 0,  NULL }, | 
|---|
| 842 | {&ARGIND_node,  "ARGIND",       Node_var,               NULL,   0,  NULL }, | 
|---|
| 843 | {&ERRNO_node,   "ERRNO",        Node_var,               NULL,   0,  NULL }, | 
|---|
| 844 | {&RT_node,      "RT",           Node_var,               "",     0,  NULL }, | 
|---|
| 845 | {&BINMODE_node, "BINMODE",      Node_BINMODE,           NULL,   0,  NULL }, | 
|---|
| 846 | {&LINT_node,    "LINT",         Node_LINT,              NULL,   0,  NULL }, | 
|---|
| 847 | {&TEXTDOMAIN_node,      "TEXTDOMAIN",           Node_TEXTDOMAIN,        "messages",     0,  set_TEXTDOMAIN }, | 
|---|
| 848 | {0,             NULL,           Node_illegal,           NULL,   0,  NULL }, | 
|---|
| 849 | }; | 
|---|
| 850 |  | 
|---|
| 851 | /* init_vars --- actually initialize everything in the symbol table */ | 
|---|
| 852 |  | 
|---|
| 853 | static void | 
|---|
| 854 | init_vars() | 
|---|
| 855 | { | 
|---|
| 856 | register const struct varinit *vp; | 
|---|
| 857 |  | 
|---|
| 858 | for (vp = varinit; vp->name; vp++) { | 
|---|
| 859 | *(vp->spec) = install((char *) vp->name, | 
|---|
| 860 | node(vp->strval == NULL ? make_number(vp->numval) | 
|---|
| 861 | : make_string((char *) vp->strval, | 
|---|
| 862 | strlen(vp->strval)), | 
|---|
| 863 | vp->type, (NODE *) NULL)); | 
|---|
| 864 | if (vp->assign) | 
|---|
| 865 | (*(vp->assign))(); | 
|---|
| 866 | } | 
|---|
| 867 |  | 
|---|
| 868 | /* Set up deferred variables (loaded only when accessed). */ | 
|---|
| 869 | if (! do_traditional) | 
|---|
| 870 | register_deferred_variable("PROCINFO", load_procinfo); | 
|---|
| 871 | register_deferred_variable("ENVIRON", load_environ); | 
|---|
| 872 | } | 
|---|
| 873 |  | 
|---|
| 874 | /* load_environ --- populate the ENVIRON array */ | 
|---|
| 875 |  | 
|---|
| 876 | static NODE * | 
|---|
| 877 | load_environ() | 
|---|
| 878 | { | 
|---|
| 879 | #if ! defined(TANDEM) | 
|---|
| 880 | #if ! (defined(MSDOS) && !defined(DJGPP)) && ! defined(OS2) && ! (defined(VMS) && defined(__DECC)) | 
|---|
| 881 | extern char **environ; | 
|---|
| 882 | #endif | 
|---|
| 883 | register char *var, *val; | 
|---|
| 884 | NODE **aptr; | 
|---|
| 885 | register int i; | 
|---|
| 886 | #endif /* TANDEM */ | 
|---|
| 887 |  | 
|---|
| 888 | ENVIRON_node = install("ENVIRON", | 
|---|
| 889 | node((NODE *) NULL, Node_var_array, (NODE *) NULL)); | 
|---|
| 890 | #if ! defined(TANDEM) | 
|---|
| 891 | for (i = 0; environ[i] != NULL; i++) { | 
|---|
| 892 | static char nullstr[] = ""; | 
|---|
| 893 |  | 
|---|
| 894 | var = environ[i]; | 
|---|
| 895 | val = strchr(var, '='); | 
|---|
| 896 | if (val != NULL) | 
|---|
| 897 | *val++ = '\0'; | 
|---|
| 898 | else | 
|---|
| 899 | val = nullstr; | 
|---|
| 900 | aptr = assoc_lookup(ENVIRON_node, tmp_string(var, strlen(var)), | 
|---|
| 901 | FALSE); | 
|---|
| 902 | *aptr = make_string(val, strlen(val)); | 
|---|
| 903 | (*aptr)->flags |= MAYBE_NUM; | 
|---|
| 904 |  | 
|---|
| 905 | /* restore '=' so that system() gets a valid environment */ | 
|---|
| 906 | if (val != nullstr) | 
|---|
| 907 | *--val = '='; | 
|---|
| 908 | } | 
|---|
| 909 | /* | 
|---|
| 910 | * Put AWKPATH into ENVIRON if it's not there. | 
|---|
| 911 | * This allows querying it from within awk programs. | 
|---|
| 912 | */ | 
|---|
| 913 | if (getenv("AWKPATH") == NULL) { | 
|---|
| 914 | aptr = assoc_lookup(ENVIRON_node, tmp_string("AWKPATH", 7), FALSE); | 
|---|
| 915 | *aptr = make_string(defpath, strlen(defpath)); | 
|---|
| 916 | } | 
|---|
| 917 | #endif /* TANDEM */ | 
|---|
| 918 | return ENVIRON_node; | 
|---|
| 919 | } | 
|---|
| 920 |  | 
|---|
| 921 | /* load_procinfo --- populate the PROCINFO array */ | 
|---|
| 922 |  | 
|---|
| 923 | static NODE * | 
|---|
| 924 | load_procinfo() | 
|---|
| 925 | { | 
|---|
| 926 | int i; | 
|---|
| 927 | NODE **aptr; | 
|---|
| 928 | char name[100]; | 
|---|
| 929 | AWKNUM value; | 
|---|
| 930 |  | 
|---|
| 931 | PROCINFO_node = install("PROCINFO", | 
|---|
| 932 | node((NODE *) NULL, Node_var_array, (NODE *) NULL)); | 
|---|
| 933 |  | 
|---|
| 934 | #ifdef GETPGRP_VOID | 
|---|
| 935 | #define getpgrp_arg() /* nothing */ | 
|---|
| 936 | #else | 
|---|
| 937 | #define getpgrp_arg() getpid() | 
|---|
| 938 | #endif | 
|---|
| 939 |  | 
|---|
| 940 | value = getpgrp(getpgrp_arg()); | 
|---|
| 941 | aptr = assoc_lookup(PROCINFO_node, tmp_string("pgrpid", 6), FALSE); | 
|---|
| 942 | *aptr = make_number(value); | 
|---|
| 943 |  | 
|---|
| 944 | /* | 
|---|
| 945 | * could put a lot of this into a table, but then there's | 
|---|
| 946 | * portability problems declaring all the functions. so just | 
|---|
| 947 | * do it the slow and stupid way. sigh. | 
|---|
| 948 | */ | 
|---|
| 949 |  | 
|---|
| 950 | aptr = assoc_lookup(PROCINFO_node, tmp_string("version", 7), FALSE); | 
|---|
| 951 | *aptr = make_string(VERSION, strlen(VERSION)); | 
|---|
| 952 |  | 
|---|
| 953 | value = getpid(); | 
|---|
| 954 | aptr = assoc_lookup(PROCINFO_node, tmp_string("pid", 3), FALSE); | 
|---|
| 955 | *aptr = make_number(value); | 
|---|
| 956 |  | 
|---|
| 957 | value = getppid(); | 
|---|
| 958 | aptr = assoc_lookup(PROCINFO_node, tmp_string("ppid", 4), FALSE); | 
|---|
| 959 | *aptr = make_number(value); | 
|---|
| 960 |  | 
|---|
| 961 | value = getuid(); | 
|---|
| 962 | aptr = assoc_lookup(PROCINFO_node, tmp_string("uid", 3), FALSE); | 
|---|
| 963 | *aptr = make_number(value); | 
|---|
| 964 |  | 
|---|
| 965 | value = geteuid(); | 
|---|
| 966 | aptr = assoc_lookup(PROCINFO_node, tmp_string("euid", 4), FALSE); | 
|---|
| 967 | *aptr = make_number(value); | 
|---|
| 968 |  | 
|---|
| 969 | value = getgid(); | 
|---|
| 970 | aptr = assoc_lookup(PROCINFO_node, tmp_string("gid", 3), FALSE); | 
|---|
| 971 | *aptr = make_number(value); | 
|---|
| 972 |  | 
|---|
| 973 | value = getegid(); | 
|---|
| 974 | aptr = assoc_lookup(PROCINFO_node, tmp_string("egid", 4), FALSE); | 
|---|
| 975 | *aptr = make_number(value); | 
|---|
| 976 |  | 
|---|
| 977 | aptr = assoc_lookup(PROCINFO_node, tmp_string("FS", 2), FALSE); | 
|---|
| 978 | *aptr = (using_fieldwidths() ? make_string("FIELDWIDTHS", 11) : | 
|---|
| 979 | make_string("FS", 2) ); | 
|---|
| 980 |  | 
|---|
| 981 | #if defined (HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0 | 
|---|
| 982 | for (i = 0; i < ngroups; i++) { | 
|---|
| 983 | sprintf(name, "group%d", i + 1); | 
|---|
| 984 | value = groupset[i]; | 
|---|
| 985 | aptr = assoc_lookup(PROCINFO_node, tmp_string(name, strlen(name)), FALSE); | 
|---|
| 986 | *aptr = make_number(value); | 
|---|
| 987 | } | 
|---|
| 988 | if (groupset) { | 
|---|
| 989 | free(groupset); | 
|---|
| 990 | groupset = NULL; | 
|---|
| 991 | } | 
|---|
| 992 | #endif | 
|---|
| 993 | return PROCINFO_node; | 
|---|
| 994 | } | 
|---|
| 995 |  | 
|---|
| 996 | /* arg_assign --- process a command-line assignment */ | 
|---|
| 997 |  | 
|---|
| 998 | int | 
|---|
| 999 | arg_assign(char *arg, int initing) | 
|---|
| 1000 | { | 
|---|
| 1001 | char *cp, *cp2; | 
|---|
| 1002 | int badvar; | 
|---|
| 1003 | Func_ptr after_assign = NULL; | 
|---|
| 1004 | NODE *var; | 
|---|
| 1005 | NODE *it; | 
|---|
| 1006 | NODE **lhs; | 
|---|
| 1007 |  | 
|---|
| 1008 | if (! initing && disallow_var_assigns) | 
|---|
| 1009 | return FALSE;   /* --exec */ | 
|---|
| 1010 |  | 
|---|
| 1011 | cp = strchr(arg, '='); | 
|---|
| 1012 |  | 
|---|
| 1013 | if (cp == NULL) { | 
|---|
| 1014 | if (! initing) | 
|---|
| 1015 | return FALSE;   /* This is file name, not assignment. */ | 
|---|
| 1016 |  | 
|---|
| 1017 | fprintf(stderr, | 
|---|
| 1018 | _("%s: `%s' argument to `-v' not in `var=value' form\n\n"), | 
|---|
| 1019 | myname, arg); | 
|---|
| 1020 | usage(1, stderr); | 
|---|
| 1021 | } | 
|---|
| 1022 |  | 
|---|
| 1023 | *cp++ = '\0'; | 
|---|
| 1024 |  | 
|---|
| 1025 | /* first check that the variable name has valid syntax */ | 
|---|
| 1026 | badvar = FALSE; | 
|---|
| 1027 | if (! ISALPHA(arg[0]) && arg[0] != '_') | 
|---|
| 1028 | badvar = TRUE; | 
|---|
| 1029 | else | 
|---|
| 1030 | for (cp2 = arg+1; *cp2; cp2++) | 
|---|
| 1031 | if (! ISALNUM(*cp2) && *cp2 != '_') { | 
|---|
| 1032 | badvar = TRUE; | 
|---|
| 1033 | break; | 
|---|
| 1034 | } | 
|---|
| 1035 |  | 
|---|
| 1036 | if (badvar) { | 
|---|
| 1037 | if (initing) | 
|---|
| 1038 | fatal(_("`%s' is not a legal variable name"), arg); | 
|---|
| 1039 |  | 
|---|
| 1040 | if (do_lint) | 
|---|
| 1041 | lintwarn(_("`%s' is not a variable name, looking for file `%s=%s'"), | 
|---|
| 1042 | arg, arg, cp); | 
|---|
| 1043 | } else { | 
|---|
| 1044 | /* | 
|---|
| 1045 | * Recent versions of nawk expand escapes inside assignments. | 
|---|
| 1046 | * This makes sense, so we do it too. | 
|---|
| 1047 | */ | 
|---|
| 1048 | it = make_str_node(cp, strlen(cp), SCAN); | 
|---|
| 1049 | it->flags |= MAYBE_NUM; | 
|---|
| 1050 | #ifdef LC_NUMERIC | 
|---|
| 1051 | setlocale(LC_NUMERIC, "C"); | 
|---|
| 1052 | (void) force_number(it); | 
|---|
| 1053 | setlocale(LC_NUMERIC, ""); | 
|---|
| 1054 | #endif /* LC_NUMERIC */ | 
|---|
| 1055 | var = variable(arg, FALSE, Node_var); | 
|---|
| 1056 | lhs = get_lhs(var, &after_assign, FALSE); | 
|---|
| 1057 | unref(*lhs); | 
|---|
| 1058 | *lhs = it; | 
|---|
| 1059 | if (after_assign != NULL) | 
|---|
| 1060 | (*after_assign)(); | 
|---|
| 1061 | } | 
|---|
| 1062 |  | 
|---|
| 1063 | *--cp = '=';    /* restore original text of ARGV */ | 
|---|
| 1064 |  | 
|---|
| 1065 | return ! badvar; | 
|---|
| 1066 | } | 
|---|
| 1067 |  | 
|---|
| 1068 | /* catchsig --- catch signals */ | 
|---|
| 1069 |  | 
|---|
| 1070 | static RETSIGTYPE | 
|---|
| 1071 | catchsig(int sig) | 
|---|
| 1072 | { | 
|---|
| 1073 | if (sig == SIGFPE) { | 
|---|
| 1074 | fatal(_("floating point exception")); | 
|---|
| 1075 | } else if (sig == SIGSEGV | 
|---|
| 1076 | #ifdef SIGBUS | 
|---|
| 1077 | || sig == SIGBUS | 
|---|
| 1078 | #endif | 
|---|
| 1079 | ) { | 
|---|
| 1080 | set_loc(__FILE__, __LINE__); | 
|---|
| 1081 | msg(_("fatal error: internal error")); | 
|---|
| 1082 | /* fatal won't abort() if not compiled for debugging */ | 
|---|
| 1083 | abort(); | 
|---|
| 1084 | } else | 
|---|
| 1085 | cant_happen(); | 
|---|
| 1086 | /* NOTREACHED */ | 
|---|
| 1087 | } | 
|---|
| 1088 |  | 
|---|
| 1089 | /* nostalgia --- print the famous error message and die */ | 
|---|
| 1090 |  | 
|---|
| 1091 | static void | 
|---|
| 1092 | nostalgia() | 
|---|
| 1093 | { | 
|---|
| 1094 | /* | 
|---|
| 1095 | * N.B.: This string is not gettextized, on purpose. | 
|---|
| 1096 | * So there. | 
|---|
| 1097 | */ | 
|---|
| 1098 | fprintf(stderr, "awk: bailing out near line 1\n"); | 
|---|
| 1099 | fflush(stderr); | 
|---|
| 1100 | abort(); | 
|---|
| 1101 | } | 
|---|
| 1102 |  | 
|---|
| 1103 | /* version --- print version message */ | 
|---|
| 1104 |  | 
|---|
| 1105 | static void | 
|---|
| 1106 | version() | 
|---|
| 1107 | { | 
|---|
| 1108 | printf("%s\n", version_string); | 
|---|
| 1109 | /* | 
|---|
| 1110 | * Per GNU coding standards, print copyright info, | 
|---|
| 1111 | * then exit successfully, do nothing else. | 
|---|
| 1112 | */ | 
|---|
| 1113 | copyleft(); | 
|---|
| 1114 | exit(0); | 
|---|
| 1115 | } | 
|---|
| 1116 |  | 
|---|
| 1117 | /* init_fds --- check for 0, 1, 2, open on /dev/null if possible */ | 
|---|
| 1118 |  | 
|---|
| 1119 | static void | 
|---|
| 1120 | init_fds() | 
|---|
| 1121 | { | 
|---|
| 1122 | struct stat sbuf; | 
|---|
| 1123 | int fd; | 
|---|
| 1124 | int newfd; | 
|---|
| 1125 | char const *const opposite_mode[] = {"w", "r", "r"}; | 
|---|
| 1126 |  | 
|---|
| 1127 | /* maybe no stderr, don't bother with error mesg */ | 
|---|
| 1128 | for (fd = 0; fd <= 2; fd++) { | 
|---|
| 1129 | if (fstat(fd, &sbuf) < 0) { | 
|---|
| 1130 | #if MAKE_A_HEROIC_EFFORT | 
|---|
| 1131 | if (do_lint) | 
|---|
| 1132 | lintwarn(_("no pre-opened fd %d"), fd); | 
|---|
| 1133 | #endif | 
|---|
| 1134 | newfd = devopen("/dev/null", opposite_mode[fd]); | 
|---|
| 1135 | /* turn off some compiler warnings "set but not used" */ | 
|---|
| 1136 | newfd += 0; | 
|---|
| 1137 | #ifdef MAKE_A_HEROIC_EFFORT | 
|---|
| 1138 | if (do_lint && newfd < 0) | 
|---|
| 1139 | lintwarn(_("could not pre-open /dev/null for fd %d"), fd); | 
|---|
| 1140 | #endif | 
|---|
| 1141 | } | 
|---|
| 1142 | } | 
|---|
| 1143 | } | 
|---|
| 1144 |  | 
|---|
| 1145 | /* init_groupset --- initialize groupset */ | 
|---|
| 1146 |  | 
|---|
| 1147 | static void | 
|---|
| 1148 | init_groupset() | 
|---|
| 1149 | { | 
|---|
| 1150 | #if defined(HAVE_GETGROUPS) && defined(NGROUPS_MAX) && NGROUPS_MAX > 0 | 
|---|
| 1151 | #ifdef GETGROUPS_NOT_STANDARD | 
|---|
| 1152 | /* For systems that aren't standards conformant, use old way. */ | 
|---|
| 1153 | ngroups = NGROUPS_MAX; | 
|---|
| 1154 | #else | 
|---|
| 1155 | /* | 
|---|
| 1156 | * If called with 0 for both args, return value is | 
|---|
| 1157 | * total number of groups. | 
|---|
| 1158 | */ | 
|---|
| 1159 | ngroups = getgroups(0, NULL); | 
|---|
| 1160 | #endif | 
|---|
| 1161 | if (ngroups == -1) | 
|---|
| 1162 | fatal(_("could not find groups: %s"), strerror(errno)); | 
|---|
| 1163 | else if (ngroups == 0) | 
|---|
| 1164 | return; | 
|---|
| 1165 |  | 
|---|
| 1166 | /* fill in groups */ | 
|---|
| 1167 | emalloc(groupset, GETGROUPS_T *, ngroups * sizeof(GETGROUPS_T), "init_groupset"); | 
|---|
| 1168 |  | 
|---|
| 1169 | ngroups = getgroups(ngroups, groupset); | 
|---|
| 1170 | if (ngroups == -1) | 
|---|
| 1171 | fatal(_("could not find groups: %s"), strerror(errno)); | 
|---|
| 1172 | #endif | 
|---|
| 1173 | } | 
|---|