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