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