xref: /openbsd-src/usr.bin/awk/main.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: main.c,v 1.48 2021/07/27 18:28:19 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 20201218";
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 	return p;
115 }
116 
117 static char *
118 getarg(int *argc, char ***argv, const char *msg)
119 {
120 	if ((*argv)[1][2] != '\0') {	/* arg is -fsomething */
121 		return &(*argv)[1][2];
122 	} else {			/* arg is -f something */
123 		(*argc)--; (*argv)++;
124 		if (*argc <= 1)
125 			FATAL("%s", msg);
126 		return (*argv)[1];
127 	}
128 }
129 
130 int main(int argc, char *argv[])
131 {
132 	const char *fs = NULL;
133 	char *fn, *vn;
134 
135 	setlocale(LC_CTYPE, "");
136 	setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
137 
138 	cmdname = __progname;
139 	if (pledge("stdio rpath wpath cpath proc exec", NULL) == -1) {
140 		fprintf(stderr, "%s: pledge: incorrect arguments\n",
141 		    cmdname);
142 		exit(1);
143 	}
144 
145 	if (argc == 1) {
146 		fprintf(stderr, "usage: %s [-safe] [-V] [-d[n]] [-F fs] "
147 		    "[-v var=value] [prog | -f progfile]\n\tfile ...\n",
148 		    cmdname);
149 		exit(1);
150 	}
151 #ifdef SA_SIGINFO
152 	{
153 		struct sigaction sa;
154 		sa.sa_sigaction = fpecatch;
155 		sa.sa_flags = SA_SIGINFO;
156 		sigemptyset(&sa.sa_mask);
157 		(void)sigaction(SIGFPE, &sa, NULL);
158 	}
159 #else
160 	(void)signal(SIGFPE, fpecatch);
161 #endif
162 
163 	do_posix = (getenv("POSIXLY_CORRECT") != NULL);
164 
165 	yyin = NULL;
166 	symtab = makesymtab(NSYMTAB);
167 	while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
168 		if (strcmp(argv[1], "--") == 0) {	/* explicit end of args */
169 			argc--;
170 			argv++;
171 			break;
172 		}
173 		switch (argv[1][1]) {
174 		case 's':
175 			if (strcmp(argv[1], "-safe") == 0)
176 				safe = true;
177 			break;
178 		case 'f':	/* next argument is program filename */
179 			fn = getarg(&argc, &argv, "no program filename");
180 			if (npfile >= maxpfile) {
181 				maxpfile += 20;
182 				pfile = (char **) realloc(pfile, maxpfile * sizeof(*pfile));
183 				if (pfile == NULL)
184 					FATAL("error allocating space for -f options");
185  			}
186 			pfile[npfile++] = fn;
187  			break;
188 		case 'F':	/* set field separator */
189 			fs = setfs(getarg(&argc, &argv, "no field separator"));
190 			break;
191 		case 'v':	/* -v a=1 to be done NOW.  one -v for each */
192 			vn = getarg(&argc, &argv, "no variable name");
193 			if (isclvar(vn))
194 				setclvar(vn);
195 			else
196 				FATAL("invalid -v option argument: %s", vn);
197 			break;
198 		case 'd':
199 			dbg = atoi(&argv[1][2]);
200 			if (dbg == 0)
201 				dbg = 1;
202 			printf("awk %s\n", version);
203 			break;
204 		case 'V':	/* added for exptools "standard" */
205 			printf("awk %s\n", version);
206 			exit(0);
207 			break;
208 		default:
209 			WARNING("unknown option %s ignored", argv[1]);
210 			break;
211 		}
212 		argc--;
213 		argv++;
214 	}
215 
216 	if (safe) {
217 		if (pledge("stdio rpath", NULL) == -1) {
218 			fprintf(stderr, "%s: pledge: incorrect arguments\n",
219 			    cmdname);
220 			exit(1);
221 		}
222 	}
223 
224 	/* argv[1] is now the first argument */
225 	if (npfile == 0) {	/* no -f; first argument is program */
226 		if (argc <= 1) {
227 			if (dbg)
228 				exit(0);
229 			FATAL("no program given");
230 		}
231 		DPRINTF("program = |%s|\n", argv[1]);
232 		lexprog = argv[1];
233 		argc--;
234 		argv++;
235 	}
236 	recinit(recsize);
237 	syminit();
238 	compile_time = COMPILING;
239 	argv[0] = cmdname;	/* put prog name at front of arglist */
240 	DPRINTF("argc=%d, argv[0]=%s\n", argc, argv[0]);
241 	arginit(argc, argv);
242 	if (!safe)
243 		envinit(environ);
244 	yyparse();
245 #if 0
246 	// Doing this would comply with POSIX, but is not compatible with
247 	// other awks and with what most users expect. So comment it out.
248 	setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */
249 #endif
250 	if (fs)
251 		*FS = qstring(fs, '\0');
252 	DPRINTF("errorflag=%d\n", errorflag);
253 	if (errorflag == 0) {
254 		compile_time = RUNNING;
255 		run(winner);
256 	} else
257 		bracecheck();
258 	return(errorflag);
259 }
260 
261 int pgetc(void)		/* get 1 character from awk program */
262 {
263 	int c;
264 
265 	for (;;) {
266 		if (yyin == NULL) {
267 			if (curpfile >= npfile)
268 				return EOF;
269 			if (strcmp(pfile[curpfile], "-") == 0)
270 				yyin = stdin;
271 			else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
272 				FATAL("can't open file %s", pfile[curpfile]);
273 			lineno = 1;
274 		}
275 		if ((c = getc(yyin)) != EOF)
276 			return c;
277 		if (yyin != stdin)
278 			fclose(yyin);
279 		yyin = NULL;
280 		curpfile++;
281 	}
282 }
283 
284 char *cursource(void)	/* current source file name */
285 {
286 	if (npfile > 0)
287 		return pfile[curpfile < npfile ? curpfile : curpfile - 1];
288 	else
289 		return NULL;
290 }
291