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
25#define DEBUG
26#include <stdio.h>
27#include <string.h>
28#include <ctype.h>
29#include <errno.h>
30#include <stdlib.h>
31#include <stdarg.h>
32#include "awk.h"
33#include "ytab.h"
34
35FILE	*infile	= NULL;
36char	*file	= "";
37char	*record;
38int	recsize	= RECSIZE;
39char	*fields;
40int	fieldssize = RECSIZE;
41
42Cell	**fldtab;	/* pointers to Cells */
43char	inputFS[100] = " ";
44
45#define	MAXFLD	2
46int	nfields	= MAXFLD;	/* last allocated slot for $i */
47
48int	donefld;	/* 1 = implies rec broken into fields */
49int	donerec;	/* 1 = record is valid (no flds have changed) */
50
51int	lastfld	= 0;	/* last used field */
52int	argno	= 1;	/* current input argument number */
53extern	Awkfloat *ARGC;
54
55static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
56static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
57
58void recinit(unsigned int n)
59{
60	if ( (record = (char *) malloc(n)) == NULL
61	  || (fields = (char *) malloc(n+1)) == NULL
62	  || (fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *))) == NULL
63	  || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL )
64		FATAL("out of space for $0 and fields");
65	*fldtab[0] = dollar0;
66	fldtab[0]->sval = record;
67	fldtab[0]->nval = tostring("0");
68	makefields(1, nfields);
69}
70
71void makefields(int n1, int n2)		/* create $n1..$n2 inclusive */
72{
73	char temp[50];
74	int i;
75
76	for (i = n1; i <= n2; i++) {
77		fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
78		if (fldtab[i] == NULL)
79			FATAL("out of space in makefields %d", i);
80		*fldtab[i] = dollar1;
81		sprintf(temp, "%d", i);
82		fldtab[i]->nval = tostring(temp);
83	}
84}
85
86void initgetrec(void)
87{
88	int i;
89	char *p;
90
91	for (i = 1; i < *ARGC; i++) {
92		if (!isclvar(p = getargv(i))) {	/* find 1st real filename */
93			setsval(lookup("FILENAME", symtab), getargv(i));
94			return;
95		}
96		setclvar(p);	/* a commandline assignment before filename */
97		argno++;
98	}
99	infile = stdin;		/* no filenames, so use stdin */
100}
101
102static int firsttime = 1;
103
104int getrec(char **pbuf, int *pbufsize, int isrecord)	/* get next input record */
105{			/* note: cares whether buf == record */
106	int c;
107	char *buf = *pbuf;
108	uschar saveb0;
109	int bufsize = *pbufsize, savebufsize = bufsize;
110
111	if (firsttime) {
112		firsttime = 0;
113		initgetrec();
114	}
115	   dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
116		*RS, *FS, *ARGC, *FILENAME) );
117	if (isrecord) {
118		donefld = 0;
119		donerec = 1;
120	}
121	saveb0 = buf[0];
122	buf[0] = 0;
123	while (argno < *ARGC || infile == stdin) {
124		   dprintf( ("argno=%d, file=|%s|\n", argno, file) );
125		if (infile == NULL) {	/* have to open a new file */
126			file = getargv(argno);
127			if (*file == '\0') {	/* it's been zapped */
128				argno++;
129				continue;
130			}
131			if (isclvar(file)) {	/* a var=value arg */
132				setclvar(file);
133				argno++;
134				continue;
135			}
136			*FILENAME = file;
137			   dprintf( ("opening file %s\n", file) );
138			if (*file == '-' && *(file+1) == '\0')
139				infile = stdin;
140			else if ((infile = fopen(file, "r")) == NULL)
141				FATAL("can't open file %s", file);
142			setfval(fnrloc, 0.0);
143		}
144		c = readrec(&buf, &bufsize, infile);
145		if (c != 0 || buf[0] != '\0') {	/* normal record */
146			if (isrecord) {
147				if (freeable(fldtab[0]))
148					xfree(fldtab[0]->sval);
149				fldtab[0]->sval = buf;	/* buf == record */
150				fldtab[0]->tval = REC | STR | DONTFREE;
151				if (is_number(fldtab[0]->sval)) {
152					fldtab[0]->fval = atof(fldtab[0]->sval);
153					fldtab[0]->tval |= NUM;
154				}
155			}
156			setfval(nrloc, nrloc->fval+1);
157			setfval(fnrloc, fnrloc->fval+1);
158			if (donefld == 0)
159		                fldbld();
160			*pbuf = buf;
161			*pbufsize = bufsize;
162			return 1;
163		}
164		/* EOF arrived on this file; set up next */
165		if (infile != stdin)
166			fclose(infile);
167		infile = NULL;
168		argno++;
169	}
170	buf[0] = saveb0;
171	*pbuf = buf;
172	*pbufsize = savebufsize;
173	return 0;	/* true end of file */
174}
175
176void nextfile(void)
177{
178	if (infile != NULL && infile != stdin)
179		fclose(infile);
180	infile = NULL;
181	argno++;
182}
183
184int readrec(char **pbuf, int *pbufsize, FILE *inf)	/* read one record into buf */
185{
186	int sep, c;
187	char *rr, *buf = *pbuf;
188	int bufsize = *pbufsize;
189
190	if (strlen(*FS) >= sizeof(inputFS))
191		FATAL("field separator %.10s... is too long", *FS);
192	strcpy(inputFS, *FS);	/* for subsequent field splitting */
193	if ((sep = **RS) == 0) {
194		sep = '\n';
195		while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
196			;
197		if (c != EOF)
198			ungetc(c, inf);
199	}
200	for (rr = buf; ; ) {
201		for (; (c=getc(inf)) != sep && c != EOF; ) {
202			if (rr-buf+1 > bufsize)
203				if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
204					FATAL("input record `%.30s...' too long", buf);
205			*rr++ = c;
206		}
207		if (**RS == sep || c == EOF)
208			break;
209		if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
210			break;
211		if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
212			FATAL("input record `%.30s...' too long", buf);
213		*rr++ = '\n';
214		*rr++ = c;
215	}
216	if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
217		FATAL("input record `%.30s...' too long", buf);
218	*rr = 0;
219	   dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
220	*pbuf = buf;
221	*pbufsize = bufsize;
222	return c == EOF && rr == buf ? 0 : 1;
223}
224
225char *getargv(int n)	/* get ARGV[n] */
226{
227	Cell *x;
228	char *s, temp[50];
229	extern Array *ARGVtab;
230
231	sprintf(temp, "%d", n);
232	x = setsymtab(temp, "", 0.0, STR, ARGVtab);
233	s = getsval(x);
234	   dprintf( ("getargv(%d) returns |%s|\n", n, s) );
235	return s;
236}
237
238void setclvar(char *s)	/* set var=value from s */
239{
240	char *p;
241	Cell *q;
242
243	for (p=s; *p != '='; p++)
244		;
245	*p++ = 0;
246	p = qstring(p, '\0');
247	q = setsymtab(s, p, 0.0, STR, symtab);
248	setsval(q, p);
249	if (is_number(q->sval)) {
250		q->fval = atof(q->sval);
251		q->tval |= NUM;
252	}
253	   dprintf( ("command line set %s to |%s|\n", s, p) );
254}
255
256
257void fldbld(void)	/* create fields from current record */
258{
259	/* this relies on having fields[] the same length as $0 */
260	/* the fields are all stored in this one array with \0's */
261	char *r, *fr, sep;
262	Cell *p;
263	int i, j, n;
264
265	if (donefld)
266		return;
267	if (!isstr(fldtab[0]))
268		getsval(fldtab[0]);
269	r = fldtab[0]->sval;
270	n = strlen(r);
271	if (n > fieldssize) {
272		xfree(fields);
273		if ((fields = (char *) malloc(n+1)) == NULL)
274			FATAL("out of space for fields in fldbld %d", n);
275		fieldssize = n;
276	}
277	fr = fields;
278	i = 0;	/* number of fields accumulated here */
279	if (strlen(inputFS) > 1) {	/* it's a regular expression */
280		i = refldbld(r, inputFS);
281	} else if ((sep = *inputFS) == ' ') {	/* default whitespace */
282		for (i = 0; ; ) {
283			while (*r == ' ' || *r == '\t' || *r == '\n')
284				r++;
285			if (*r == 0)
286				break;
287			i++;
288			if (i > nfields)
289				growfldtab(i);
290			if (freeable(fldtab[i]))
291				xfree(fldtab[i]->sval);
292			fldtab[i]->sval = fr;
293			fldtab[i]->tval = FLD | STR | DONTFREE;
294			do
295				*fr++ = *r++;
296			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
297			*fr++ = 0;
298		}
299		*fr = 0;
300	} else if ((sep = *inputFS) == 0) {		/* new: FS="" => 1 char/field */
301		for (i = 0; *r != 0; r++) {
302			char buf[2];
303			i++;
304			if (i > nfields)
305				growfldtab(i);
306			if (freeable(fldtab[i]))
307				xfree(fldtab[i]->sval);
308			buf[0] = *r;
309			buf[1] = 0;
310			fldtab[i]->sval = tostring(buf);
311			fldtab[i]->tval = FLD | STR;
312		}
313		*fr = 0;
314	} else if (*r != 0) {	/* if 0, it's a null field */
315		/* subtlecase : if length(FS) == 1 && length(RS > 0)
316		 * \n is NOT a field separator (cf awk book 61,84).
317		 * this variable is tested in the inner while loop.
318		 */
319		int rtest = '\n';  /* normal case */
320		if (strlen(*RS) > 0)
321			rtest = '\0';
322		for (;;) {
323			i++;
324			if (i > nfields)
325				growfldtab(i);
326			if (freeable(fldtab[i]))
327				xfree(fldtab[i]->sval);
328			fldtab[i]->sval = fr;
329			fldtab[i]->tval = FLD | STR | DONTFREE;
330			while (*r != sep && *r != rtest && *r != '\0')	/* \n is always a separator */
331				*fr++ = *r++;
332			*fr++ = 0;
333			if (*r++ == 0)
334				break;
335		}
336		*fr = 0;
337	}
338	if (i > nfields)
339		FATAL("record `%.30s...' has too many fields; can't happen", r);
340	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
341	lastfld = i;
342	donefld = 1;
343	for (j = 1; j <= lastfld; j++) {
344		p = fldtab[j];
345		if(is_number(p->sval)) {
346			p->fval = atof(p->sval);
347			p->tval |= NUM;
348		}
349	}
350	setfval(nfloc, (Awkfloat) lastfld);
351	if (dbg) {
352		for (j = 0; j <= lastfld; j++) {
353			p = fldtab[j];
354			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
355		}
356	}
357}
358
359void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
360{				/* nvals remain intact */
361	Cell *p;
362	int i;
363
364	for (i = n1; i <= n2; i++) {
365		p = fldtab[i];
366		if (freeable(p))
367			xfree(p->sval);
368		p->sval = "";
369		p->tval = FLD | STR | DONTFREE;
370	}
371}
372
373void newfld(int n)	/* add field n after end of existing lastfld */
374{
375	if (n > nfields)
376		growfldtab(n);
377	cleanfld(lastfld+1, n);
378	lastfld = n;
379	setfval(nfloc, (Awkfloat) n);
380}
381
382Cell *fieldadr(int n)	/* get nth field */
383{
384	if (n < 0)
385		FATAL("trying to access out of range field %d", n);
386	if (n > nfields)	/* fields after NF are empty */
387		growfldtab(n);	/* but does not increase NF */
388	return(fldtab[n]);
389}
390
391void growfldtab(int n)	/* make new fields up to at least $n */
392{
393	int nf = 2 * nfields;
394	size_t s;
395
396	if (n > nf)
397		nf = n;
398	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
399	if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
400		fldtab = (Cell **) realloc(fldtab, s);
401	else					/* overflow sizeof int */
402		xfree(fldtab);	/* make it null */
403	if (fldtab == NULL)
404		FATAL("out of space creating %d fields", nf);
405	makefields(nfields+1, nf);
406	nfields = nf;
407}
408
409int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
410{
411	/* this relies on having fields[] the same length as $0 */
412	/* the fields are all stored in this one array with \0's */
413	char *fr;
414	int i, tempstat, n;
415	fa *pfa;
416
417	n = strlen(rec);
418	if (n > fieldssize) {
419		xfree(fields);
420		if ((fields = (char *) malloc(n+1)) == NULL)
421			FATAL("out of space for fields in refldbld %d", n);
422		fieldssize = n;
423	}
424	fr = fields;
425	*fr = '\0';
426	if (*rec == '\0')
427		return 0;
428	pfa = makedfa(fs, 1);
429	   dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
430	tempstat = pfa->initstat;
431	for (i = 1; ; i++) {
432		if (i > nfields)
433			growfldtab(i);
434		if (freeable(fldtab[i]))
435			xfree(fldtab[i]->sval);
436		fldtab[i]->tval = FLD | STR | DONTFREE;
437		fldtab[i]->sval = fr;
438		   dprintf( ("refldbld: i=%d\n", i) );
439		if (nematch(pfa, rec)) {
440			pfa->initstat = 2;	/* horrible coupling to b.c */
441			   dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
442			strncpy(fr, rec, patbeg-rec);
443			fr += patbeg - rec + 1;
444			*(fr-1) = '\0';
445			rec = patbeg + patlen;
446		} else {
447			   dprintf( ("no match %s\n", rec) );
448			strcpy(fr, rec);
449			pfa->initstat = tempstat;
450			break;
451		}
452	}
453	return i;
454}
455
456void recbld(void)	/* create $0 from $1..$NF if necessary */
457{
458	int i;
459	char *r, *p;
460
461	if (donerec == 1)
462		return;
463	r = record;
464	for (i = 1; i <= *NF; i++) {
465		p = getsval(fldtab[i]);
466		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
467			FATAL("created $0 `%.30s...' too long", record);
468		while ((*r = *p++) != 0)
469			r++;
470		if (i < *NF) {
471			if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
472				FATAL("created $0 `%.30s...' too long", record);
473			for (p = *OFS; (*r = *p++) != 0; )
474				r++;
475		}
476	}
477	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
478		FATAL("built giant record `%.30s...'", record);
479	*r = '\0';
480	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
481
482	if (freeable(fldtab[0]))
483		xfree(fldtab[0]->sval);
484	fldtab[0]->tval = REC | STR | DONTFREE;
485	fldtab[0]->sval = record;
486
487	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
488	   dprintf( ("recbld = |%s|\n", record) );
489	donerec = 1;
490}
491
492int	errorflag	= 0;
493
494void yyerror(const char *s)
495{
496	SYNTAX("%s", s);
497}
498
499void SYNTAX(const char *fmt, ...)
500{
501	extern char *cmdname, *curfname;
502	static int been_here = 0;
503	va_list varg;
504
505	if (been_here++ > 2)
506		return;
507	fprintf(stderr, "%s: ", cmdname);
508	va_start(varg, fmt);
509	vfprintf(stderr, fmt, varg);
510	va_end(varg);
511	fprintf(stderr, " at source line %d", lineno);
512	if (curfname != NULL)
513		fprintf(stderr, " in function %s", curfname);
514	if (compile_time == 1 && cursource() != NULL)
515		fprintf(stderr, " source file %s", cursource());
516	fprintf(stderr, "\n");
517	errorflag = 2;
518	eprint();
519}
520
521void fpecatch(int n)
522{
523	FATAL("floating point exception %d", n);
524}
525
526extern int bracecnt, brackcnt, parencnt;
527
528void bracecheck(void)
529{
530	int c;
531	static int beenhere = 0;
532
533	if (beenhere++)
534		return;
535	while ((c = input()) != EOF && c != '\0')
536		bclass(c);
537	bcheck2(bracecnt, '{', '}');
538	bcheck2(brackcnt, '[', ']');
539	bcheck2(parencnt, '(', ')');
540}
541
542void bcheck2(int n, int c1, int c2)
543{
544	if (n == 1)
545		fprintf(stderr, "\tmissing %c\n", c2);
546	else if (n > 1)
547		fprintf(stderr, "\t%d missing %c's\n", n, c2);
548	else if (n == -1)
549		fprintf(stderr, "\textra %c\n", c2);
550	else if (n < -1)
551		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
552}
553
554void FATAL(const char *fmt, ...)
555{
556	extern char *cmdname;
557	va_list varg;
558
559	fflush(stdout);
560	fprintf(stderr, "%s: ", cmdname);
561	va_start(varg, fmt);
562	vfprintf(stderr, fmt, varg);
563	va_end(varg);
564	error();
565	if (dbg > 1)		/* core dump if serious debugging on */
566		abort();
567	exit(2);
568}
569
570void WARNING(const char *fmt, ...)
571{
572	extern char *cmdname;
573	va_list varg;
574
575	fflush(stdout);
576	fprintf(stderr, "%s: ", cmdname);
577	va_start(varg, fmt);
578	vfprintf(stderr, fmt, varg);
579	va_end(varg);
580	error();
581}
582
583void error()
584{
585	extern Node *curnode;
586
587	fprintf(stderr, "\n");
588	if (compile_time != 2 && NR && *NR > 0) {
589		fprintf(stderr, " input record number %d", (int) (*FNR));
590		if (strcmp(*FILENAME, "-") != 0)
591			fprintf(stderr, ", file %s", *FILENAME);
592		fprintf(stderr, "\n");
593	}
594	if (compile_time != 2 && curnode)
595		fprintf(stderr, " source line number %d", curnode->lineno);
596	else if (compile_time != 2 && lineno)
597		fprintf(stderr, " source line number %d", lineno);
598	if (compile_time == 1 && cursource() != NULL)
599		fprintf(stderr, " source file %s", cursource());
600	fprintf(stderr, "\n");
601	eprint();
602}
603
604void eprint(void)	/* try to print context around error */
605{
606	char *p, *q;
607	int c;
608	static int been_here = 0;
609	extern char ebuf[], *ep;
610
611	if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
612		return;
613	p = ep - 1;
614	if (p > ebuf && *p == '\n')
615		p--;
616	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
617		;
618	while (*p == '\n')
619		p++;
620	fprintf(stderr, " context is\n\t");
621	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
622		;
623	for ( ; p < q; p++)
624		if (*p)
625			putc(*p, stderr);
626	fprintf(stderr, " >>> ");
627	for ( ; p < ep; p++)
628		if (*p)
629			putc(*p, stderr);
630	fprintf(stderr, " <<< ");
631	if (*ep)
632		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
633			putc(c, stderr);
634			bclass(c);
635		}
636	putc('\n', stderr);
637	ep = ebuf;
638}
639
640void bclass(int c)
641{
642	switch (c) {
643	case '{': bracecnt++; break;
644	case '}': bracecnt--; break;
645	case '[': brackcnt++; break;
646	case ']': brackcnt--; break;
647	case '(': parencnt++; break;
648	case ')': parencnt--; break;
649	}
650}
651
652double errcheck(double x, const char *s)
653{
654
655	if (errno == EDOM) {
656		errno = 0;
657		WARNING("%s argument out of domain", s);
658		x = 1;
659	} else if (errno == ERANGE) {
660		errno = 0;
661		WARNING("%s result out of range", s);
662		x = 1;
663	}
664	return x;
665}
666
667int isclvar(const char *s)	/* is s of form var=something ? */
668{
669	const char *os = s;
670
671	if (!isalpha((uschar) *s) && *s != '_')
672		return 0;
673	for ( ; *s; s++)
674		if (!(isalnum((uschar) *s) || *s == '_'))
675			break;
676	return *s == '=' && s > os && *(s+1) != '=';
677}
678
679/* strtod is supposed to be a proper test of what's a valid number */
680/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
681/* wrong: violates 4.10.1.4 of ansi C standard */
682
683#include <math.h>
684int is_number(const char *s)
685{
686	double r;
687	char *ep;
688	errno = 0;
689	r = strtod(s, &ep);
690	if (ep == s || r == HUGE_VAL || errno == ERANGE)
691		return 0;
692	while (*ep == ' ' || *ep == '\t' || *ep == '\n')
693		ep++;
694	if (*ep == '\0')
695		return 1;
696	else
697		return 0;
698}
699