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