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