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