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