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