1/*	$OpenBSD: main.c,v 1.71 2024/06/03 00:58:04 millert Exp $	*/
2/****************************************************************
3Copyright (C) Lucent Technologies 1997
4All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and
7its documentation for any purpose and without fee is hereby
8granted, provided that the above copyright notice appear in all
9copies and that both that the copyright notice and this
10permission notice and warranty disclaimer appear in supporting
11documentation, and that the name Lucent Technologies or any of
12its entities not be used in advertising or publicity pertaining
13to distribution of the software without specific, written prior
14permission.
15
16LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
18IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
19SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
21IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
23THIS SOFTWARE.
24****************************************************************/
25
26const char	*version = "version 20240527";
27
28#define DEBUG
29#include <stdio.h>
30#include <ctype.h>
31#include <locale.h>
32#include <stdlib.h>
33#include <string.h>
34#include <signal.h>
35#include <unistd.h>
36#include "awk.h"
37
38extern	char	*__progname;
39extern	char	**environ;
40extern	int	nfields;
41
42int	dbg	= 0;
43Awkfloat	srand_seed = 1;
44char	*cmdname;	/* gets argv[0] for error messages */
45extern	FILE	*yyin;	/* lex input file */
46char	*lexprog;	/* points to program argument if it exists */
47extern	int errorflag;	/* non-zero if any syntax errors; set by yyerror */
48enum compile_states	compile_time = ERROR_PRINTING;
49
50static char	**pfile;	/* program filenames from -f's */
51static size_t	maxpfile;	/* max program filename */
52static size_t	npfile;		/* number of filenames */
53static size_t	curpfile;	/* current filename */
54
55bool	CSV = false;		/* true for csv input */
56bool	safe = false;		/* true => "safe" mode */
57bool	do_posix = false;	/* true => POSIX mode */
58
59size_t	awk_mb_cur_max = 1;
60
61static noreturn void fpecatch(int n
62#ifdef SA_SIGINFO
63	, siginfo_t *si, void *uc
64#endif
65)
66{
67	extern Node *curnode;
68#ifdef SA_SIGINFO
69	const char *mesg = NULL;
70
71	switch (si->si_code) {
72	case FPE_INTDIV:
73		mesg = "Integer divide by zero";
74		break;
75	case FPE_INTOVF:
76		mesg = "Integer overflow";
77		break;
78	case FPE_FLTDIV:
79		mesg = "Floating point divide by zero";
80		break;
81	case FPE_FLTOVF:
82		mesg = "Floating point overflow";
83		break;
84	case FPE_FLTUND:
85		mesg = "Floating point underflow";
86		break;
87	case FPE_FLTRES:
88		mesg = "Floating point inexact result";
89		break;
90	case FPE_FLTINV:
91		mesg = "Invalid Floating point operation";
92		break;
93	case FPE_FLTSUB:
94		mesg = "Subscript out of range";
95		break;
96	case 0:
97	default:
98		mesg = "Unknown error";
99		break;
100	}
101#endif
102	dprintf(STDERR_FILENO, "floating point exception%s%s\n",
103#ifdef SA_SIGINFO
104		": ", mesg
105#else
106		"", ""
107#endif
108	    );
109
110	if (compile_time != 2 && NR && *NR > 0) {
111		dprintf(STDERR_FILENO, " input record number %d", (int) (*FNR));
112		if (strcmp(*FILENAME, "-") != 0) {
113			dprintf(STDERR_FILENO, ", file %s", *FILENAME);
114		}
115		dprintf(STDERR_FILENO, "\n");
116	}
117	if (compile_time != 2 && curnode) {
118		dprintf(STDERR_FILENO, " source line number %d", curnode->lineno);
119	} else if (compile_time != 2 && lineno) {
120		dprintf(STDERR_FILENO, " source line number %d", lineno);
121	}
122	if (compile_time == 1 && cursource() != NULL) {
123		dprintf(STDERR_FILENO, " source file %s", cursource());
124	}
125	dprintf(STDERR_FILENO, "\n");
126	if (dbg > 1)		/* core dump if serious debugging on */
127		abort();
128	_exit(2);
129}
130
131static const char *
132setfs(char *p)
133{
134	/* wart: t=>\t */
135	if (p[0] == 't' && p[1] == '\0')
136		return "\t";
137	return p;
138}
139
140static char *
141getarg(int *argc, char ***argv, const char *msg)
142{
143	if ((*argv)[1][2] != '\0') {	/* arg is -fsomething */
144		return &(*argv)[1][2];
145	} else {			/* arg is -f something */
146		(*argc)--; (*argv)++;
147		if (*argc <= 1)
148			FATAL("%s", msg);
149		return (*argv)[1];
150	}
151}
152
153int main(int argc, char *argv[])
154{
155	const char *fs = NULL;
156	char *fn, *vn;
157
158	setlocale(LC_CTYPE, "");
159	setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
160	awk_mb_cur_max = MB_CUR_MAX;
161	cmdname = __progname;
162
163	if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1) {
164		fprintf(stderr, "%s: pledge: incorrect arguments\n",
165		    cmdname);
166		exit(1);
167	}
168
169	if (argc == 1) {
170		fprintf(stderr, "usage: %s [-safe] [-V] [-d[n]] "
171		    "[-f fs | --csv] [-v var=value]\n"
172		    "\t   [prog | -f progfile] file ...\n", cmdname);
173		return 1;
174	}
175#ifdef SA_SIGINFO
176	{
177		struct sigaction sa;
178		sa.sa_sigaction = fpecatch;
179		sa.sa_flags = SA_SIGINFO;
180		sigemptyset(&sa.sa_mask);
181		(void)sigaction(SIGFPE, &sa, NULL);
182	}
183#else
184	(void)signal(SIGFPE, fpecatch);
185#endif
186
187	do_posix = (getenv("POSIXLY_CORRECT") != NULL);
188
189	yyin = NULL;
190	symtab = makesymtab(NSYMTAB);
191	while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
192		if (strcmp(argv[1], "--version") == 0) {
193			printf("awk %s\n", version);
194			return 0;
195		}
196		if (strcmp(argv[1], "--") == 0) {	/* explicit end of args */
197			argc--;
198			argv++;
199			break;
200		}
201		if (strcmp(argv[1], "--csv") == 0) {	/* turn on csv input processing */
202			CSV = true;
203			argc--;
204			argv++;
205			continue;
206		}
207		switch (argv[1][1]) {
208		case 's':
209			if (strcmp(argv[1], "-safe") == 0)
210				safe = true;
211			break;
212		case 'f':	/* next argument is program filename */
213			fn = getarg(&argc, &argv, "no program filename");
214			if (npfile >= maxpfile) {
215				maxpfile += 20;
216				pfile = (char **) reallocarray(pfile, maxpfile, sizeof(*pfile));
217				if (pfile == NULL)
218					FATAL("error allocating space for -f options");
219 			}
220			pfile[npfile++] = fn;
221 			break;
222		case 'F':	/* set field separator */
223			fs = setfs(getarg(&argc, &argv, "no field separator"));
224			break;
225		case 'v':	/* -v a=1 to be done NOW.  one -v for each */
226			vn = getarg(&argc, &argv, "no variable name");
227			if (isclvar(vn))
228				setclvar(vn);
229			else
230				FATAL("invalid -v option argument: %s", vn);
231			break;
232		case 'd':
233			dbg = atoi(&argv[1][2]);
234			if (dbg == 0)
235				dbg = 1;
236			printf("awk %s\n", version);
237			break;
238		case 'V':
239			printf("awk %s\n", version);
240			return 0;
241		default:
242			WARNING("unknown option %s ignored", argv[1]);
243			break;
244		}
245		argc--;
246		argv++;
247	}
248
249	if (safe) {
250		if (pledge("stdio rpath", NULL) == -1) {
251			fprintf(stderr, "%s: pledge: incorrect arguments\n",
252			    cmdname);
253			exit(1);
254		}
255	}
256
257	if (CSV && (fs != NULL || lookup("FS", symtab) != NULL))
258		WARNING("danger: don't set FS when --csv is in effect");
259
260	/* argv[1] is now the first argument */
261	if (npfile == 0) {	/* no -f; first argument is program */
262		if (argc <= 1) {
263			if (dbg)
264				exit(0);
265			FATAL("no program given");
266		}
267		DPRINTF("program = |%s|\n", argv[1]);
268		lexprog = argv[1];
269		argc--;
270		argv++;
271	}
272	recinit(recsize);
273	syminit();
274	compile_time = COMPILING;
275	argv[0] = cmdname;	/* put prog name at front of arglist */
276	DPRINTF("argc=%d, argv[0]=%s\n", argc, argv[0]);
277	arginit(argc, argv);
278	if (!safe)
279		envinit(environ);
280	yyparse();
281#if 0
282	// Doing this would comply with POSIX, but is not compatible with
283	// other awks and with what most users expect. So comment it out.
284	setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */
285#endif
286	if (fs)
287		*FS = qstring(fs, '\0');
288	DPRINTF("errorflag=%d\n", errorflag);
289	if (errorflag == 0) {
290		compile_time = RUNNING;
291		run(winner);
292	} else
293		bracecheck();
294	return(errorflag);
295}
296
297int pgetc(void)		/* get 1 character from awk program */
298{
299	int c;
300
301	for (;;) {
302		if (yyin == NULL) {
303			if (curpfile >= npfile)
304				return EOF;
305			if (strcmp(pfile[curpfile], "-") == 0)
306				yyin = stdin;
307			else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
308				FATAL("can't open file %s", pfile[curpfile]);
309			lineno = 1;
310		}
311		if ((c = getc(yyin)) != EOF)
312			return c;
313		if (yyin != stdin)
314			fclose(yyin);
315		yyin = NULL;
316		curpfile++;
317	}
318}
319
320char *cursource(void)	/* current source file name */
321{
322	if (npfile > 0)
323		return pfile[curpfile < npfile ? curpfile : curpfile - 1];
324	else
325		return NULL;
326}
327