[3076] | 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 | }
|
---|