xref: /dflybsd-src/contrib/awk/main.c (revision 48d201a5a8c1dab4aa7166b0812594c101fc43c3)
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 }
749