xref: /freebsd/contrib/one-true-awk/main.c (revision b7f7cc25c01aeacaafb86ebcffdeb258b7933b08)
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 const char	*version = "version 20231030";
26 
27 #define DEBUG
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <locale.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <signal.h>
34 #include "awk.h"
35 
36 extern	char	**environ;
37 extern	int	nfields;
38 
39 int	dbg	= 0;
40 Awkfloat	srand_seed = 1;
41 char	*cmdname;	/* gets argv[0] for error messages */
42 extern	FILE	*yyin;	/* lex input file */
43 char	*lexprog;	/* points to program argument if it exists */
44 extern	int errorflag;	/* non-zero if any syntax errors; set by yyerror */
45 enum compile_states	compile_time = ERROR_PRINTING;
46 
47 static char	**pfile;	/* program filenames from -f's */
48 static size_t	maxpfile;	/* max program filename */
49 static size_t	npfile;		/* number of filenames */
50 static size_t	curpfile;	/* current filename */
51 
52 bool	CSV = false;	/* true for csv input */
53 
54 bool	safe = false;	/* true => "safe" mode */
55 
56 size_t	awk_mb_cur_max = 1;
57 
58 static noreturn void fpecatch(int n
59 #ifdef SA_SIGINFO
60 	, siginfo_t *si, void *uc
61 #endif
62 )
63 {
64 #ifdef SA_SIGINFO
65 	static const char *emsg[] = {
66 		[0] = "Unknown error",
67 		[FPE_INTDIV] = "Integer divide by zero",
68 		[FPE_INTOVF] = "Integer overflow",
69 		[FPE_FLTDIV] = "Floating point divide by zero",
70 		[FPE_FLTOVF] = "Floating point overflow",
71 		[FPE_FLTUND] = "Floating point underflow",
72 		[FPE_FLTRES] = "Floating point inexact result",
73 		[FPE_FLTINV] = "Invalid Floating point operation",
74 		[FPE_FLTSUB] = "Subscript out of range",
75 	};
76 #endif
77 	FATAL("floating point exception"
78 #ifdef SA_SIGINFO
79 		": %s", (size_t)si->si_code < sizeof(emsg) / sizeof(emsg[0]) &&
80 		emsg[si->si_code] ? emsg[si->si_code] : emsg[0]
81 #endif
82 	    );
83 }
84 
85 /* Can this work with recursive calls?  I don't think so.
86 void segvcatch(int n)
87 {
88 	FATAL("segfault.  Do you have an unbounded recursive call?", n);
89 }
90 */
91 
92 static const char *
93 setfs(char *p)
94 {
95 	/* wart: t=>\t */
96 	if (p[0] == 't' && p[1] == '\0') {
97 		WARNING("-Ft to imply tab separator is deprecated behavior.");
98 		return "\t";
99 	}
100 	return p;
101 }
102 
103 static char *
104 getarg(int *argc, char ***argv, const char *msg)
105 {
106 	if ((*argv)[1][2] != '\0') {	/* arg is -fsomething */
107 		return &(*argv)[1][2];
108 	} else {			/* arg is -f something */
109 		(*argc)--; (*argv)++;
110 		if (*argc <= 1)
111 			FATAL("%s", msg);
112 		return (*argv)[1];
113 	}
114 }
115 
116 int main(int argc, char *argv[])
117 {
118 	const char *fs = NULL;
119 	char *fn, *vn;
120 
121 	setlocale(LC_CTYPE, "");
122 	setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */
123 	awk_mb_cur_max = MB_CUR_MAX;
124 	cmdname = argv[0];
125 	if (argc == 1) {
126 		fprintf(stderr,
127 		  "usage: %s [-F fs | --csv] [-v var=value] [-f progfile | 'prog'] [file ...]\n",
128 		  cmdname);
129 		exit(1);
130 	}
131 #ifdef SA_SIGINFO
132 	{
133 		struct sigaction sa;
134 		sa.sa_sigaction = fpecatch;
135 		sa.sa_flags = SA_SIGINFO;
136 		sigemptyset(&sa.sa_mask);
137 		(void)sigaction(SIGFPE, &sa, NULL);
138 	}
139 #else
140 	(void)signal(SIGFPE, fpecatch);
141 #endif
142 	/*signal(SIGSEGV, segvcatch); experiment */
143 
144 	/* Set and keep track of the random seed */
145 	srand_seed = 1;
146 	srandom((unsigned long) srand_seed);
147 
148 	yyin = NULL;
149 	symtab = makesymtab(NSYMTAB/NSYMTAB);
150 	while (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') {
151 		if (strcmp(argv[1], "-version") == 0 || strcmp(argv[1], "--version") == 0) {
152 			printf("awk %s\n", version);
153 			return 0;
154 		}
155 		if (strcmp(argv[1], "--") == 0) {	/* explicit end of args */
156 			argc--;
157 			argv++;
158 			break;
159 		}
160 		if (strcmp(argv[1], "--csv") == 0) {	/* turn on csv input processing */
161 			CSV = true;
162 			argc--;
163 			argv++;
164 			continue;
165 		}
166 		switch (argv[1][1]) {
167 		case 's':
168 			if (strcmp(argv[1], "-safe") == 0)
169 				safe = true;
170 			break;
171 		case 'f':	/* next argument is program filename */
172 			fn = getarg(&argc, &argv, "no program filename");
173 			if (npfile >= maxpfile) {
174 				maxpfile += 20;
175 				pfile = (char **) realloc(pfile, maxpfile * sizeof(*pfile));
176 				if (pfile == NULL)
177 					FATAL("error allocating space for -f options");
178  			}
179 			pfile[npfile++] = fn;
180  			break;
181 		case 'F':	/* set field separator */
182 			fs = setfs(getarg(&argc, &argv, "no field separator"));
183 			break;
184 		case 'v':	/* -v a=1 to be done NOW.  one -v for each */
185 			vn = getarg(&argc, &argv, "no variable name");
186 			if (isclvar(vn))
187 				setclvar(vn);
188 			else
189 				FATAL("invalid -v option argument: %s", vn);
190 			break;
191 		case 'd':
192 			dbg = atoi(&argv[1][2]);
193 			if (dbg == 0)
194 				dbg = 1;
195 			printf("awk %s\n", version);
196 			break;
197 		default:
198 			WARNING("unknown option %s ignored", argv[1]);
199 			break;
200 		}
201 		argc--;
202 		argv++;
203 	}
204 	/* argv[1] is now the first argument */
205 	if (npfile == 0) {	/* no -f; first argument is program */
206 		if (argc <= 1) {
207 			if (dbg)
208 				exit(0);
209 			FATAL("no program given");
210 		}
211 		DPRINTF("program = |%s|\n", argv[1]);
212 		lexprog = argv[1];
213 		argc--;
214 		argv++;
215 	}
216 	recinit(recsize);
217 	syminit();
218 	compile_time = COMPILING;
219 	argv[0] = cmdname;	/* put prog name at front of arglist */
220 	DPRINTF("argc=%d, argv[0]=%s\n", argc, argv[0]);
221 	arginit(argc, argv);
222 	if (!safe)
223 		envinit(environ);
224 	yyparse();
225 #if 0
226 	// Doing this would comply with POSIX, but is not compatible with
227 	// other awks and with what most users expect. So comment it out.
228 	setlocale(LC_NUMERIC, ""); /* back to whatever it is locally */
229 #endif
230 	if (fs)
231 		*FS = qstring(fs, '\0');
232 	DPRINTF("errorflag=%d\n", errorflag);
233 	if (errorflag == 0) {
234 		compile_time = RUNNING;
235 		run(winner);
236 	} else
237 		bracecheck();
238 	return(errorflag);
239 }
240 
241 int pgetc(void)		/* get 1 character from awk program */
242 {
243 	int c;
244 
245 	for (;;) {
246 		if (yyin == NULL) {
247 			if (curpfile >= npfile)
248 				return EOF;
249 			if (strcmp(pfile[curpfile], "-") == 0)
250 				yyin = stdin;
251 			else if ((yyin = fopen(pfile[curpfile], "r")) == NULL)
252 				FATAL("can't open file %s", pfile[curpfile]);
253 			lineno = 1;
254 		}
255 		if ((c = getc(yyin)) != EOF)
256 			return c;
257 		if (yyin != stdin)
258 			fclose(yyin);
259 		yyin = NULL;
260 		curpfile++;
261 	}
262 }
263 
264 char *cursource(void)	/* current source file name */
265 {
266 	if (npfile > 0)
267 		return pfile[curpfile < npfile ? curpfile : curpfile - 1];
268 	else
269 		return NULL;
270 }
271