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