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