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