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