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+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 
     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 
    193 	if (strlen(*FS) >= sizeof(inputFS))
    194 		FATAL("field separator %.10s... is too long", *FS);
    195 	/*fflush(stdout); avoids some buffering problem but makes it 25% slower*/
    196 	strcpy(inputFS, *FS);	/* for subsequent field splitting */
    197 	if ((sep = **RS) == 0) {
    198 		sep = '\n';
    199 		while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
    200 			;
    201 		if (c != EOF)
    202 			ungetc(c, inf);
    203 	}
    204 	for (rr = buf; ; ) {
    205 		for (; (c=getc(inf)) != sep && c != EOF; ) {
    206 			if (rr-buf+1 > bufsize)
    207 				if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
    208 					FATAL("input record `%.30s...' too long", buf);
    209 			*rr++ = c;
    210 		}
    211 		if (**RS == sep || c == EOF)
    212 			break;
    213 		if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
    214 			break;
    215 		if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
    216 			FATAL("input record `%.30s...' too long", buf);
    217 		*rr++ = '\n';
    218 		*rr++ = c;
    219 	}
    220 	if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
    221 		FATAL("input record `%.30s...' too long", buf);
    222 	*rr = 0;
    223 	   dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
    224 	*pbuf = buf;
    225 	*pbufsize = bufsize;
    226 	return c == EOF && rr == buf ? 0 : 1;
    227 }
    228 
    229 char *getargv(int n)	/* get ARGV[n] */
    230 {
    231 	Cell *x;
    232 	char *s, temp[50];
    233 	extern Array *ARGVtab;
    234 
    235 	sprintf(temp, "%d", n);
    236 	if (lookup(temp, ARGVtab) == NULL)
    237 		return NULL;
    238 	x = setsymtab(temp, "", 0.0, STR, ARGVtab);
    239 	s = getsval(x);
    240 	   dprintf( ("getargv(%d) returns |%s|\n", n, s) );
    241 	return s;
    242 }
    243 
    244 void setclvar(char *s)	/* set var=value from s */
    245 {
    246 	char *p;
    247 	Cell *q;
    248 
    249 	for (p=s; *p != '='; p++)
    250 		;
    251 	*p++ = 0;
    252 	p = qstring(p, '\0');
    253 	q = setsymtab(s, p, 0.0, STR, symtab);
    254 	setsval(q, p);
    255 	if (is_number(q->sval)) {
    256 		q->fval = atof(q->sval);
    257 		q->tval |= NUM;
    258 	}
    259 	   dprintf( ("command line set %s to |%s|\n", s, p) );
    260 }
    261 
    262 
    263 void fldbld(void)	/* create fields from current record */
    264 {
    265 	/* this relies on having fields[] the same length as $0 */
    266 	/* the fields are all stored in this one array with \0's */
    267 	/* possibly with a final trailing \0 not associated with any field */
    268 	char *r, *fr, sep;
    269 	Cell *p;
    270 	int i, j, n;
    271 
    272 	if (donefld)
    273 		return;
    274 	if (!isstr(fldtab[0]))
    275 		getsval(fldtab[0]);
    276 	r = fldtab[0]->sval;
    277 	n = strlen(r);
    278 	if (n > fieldssize) {
    279 		xfree(fields);
    280 		if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
    281 			FATAL("out of space for fields in fldbld %d", n);
    282 		fieldssize = n;
    283 	}
    284 	fr = fields;
    285 	i = 0;	/* number of fields accumulated here */
    286 	strcpy(inputFS, *FS);
    287 	if (strlen(inputFS) > 1) {	/* it's a regular expression */
    288 		i = refldbld(r, inputFS);
    289 	} else if ((sep = *inputFS) == ' ') {	/* default whitespace */
    290 		for (i = 0; ; ) {
    291 			while (*r == ' ' || *r == '\t' || *r == '\n')
    292 				r++;
    293 			if (*r == 0)
    294 				break;
    295 			i++;
    296 			if (i > nfields)
    297 				growfldtab(i);
    298 			if (freeable(fldtab[i]))
    299 				xfree(fldtab[i]->sval);
    300 			fldtab[i]->sval = fr;
    301 			fldtab[i]->tval = FLD | STR | DONTFREE;
    302 			do
    303 				*fr++ = *r++;
    304 			while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
    305 			*fr++ = 0;
    306 		}
    307 		*fr = 0;
    308 	} else if ((sep = *inputFS) == 0) {		/* new: FS="" => 1 char/field */
    309 		for (i = 0; *r != 0; r++) {
    310 			char buf[2];
    311 			i++;
    312 			if (i > nfields)
    313 				growfldtab(i);
    314 			if (freeable(fldtab[i]))
    315 				xfree(fldtab[i]->sval);
    316 			buf[0] = *r;
    317 			buf[1] = 0;
    318 			fldtab[i]->sval = tostring(buf);
    319 			fldtab[i]->tval = FLD | STR;
    320 		}
    321 		*fr = 0;
    322 	} else if (*r != 0) {	/* if 0, it's a null field */
    323 		/* subtlecase : if length(FS) == 1 && length(RS > 0)
    324 		 * \n is NOT a field separator (cf awk book 61,84).
    325 		 * this variable is tested in the inner while loop.
    326 		 */
    327 		int rtest = '\n';  /* normal case */
    328 		if (strlen(*RS) > 0)
    329 			rtest = '\0';
    330 		for (;;) {
    331 			i++;
    332 			if (i > nfields)
    333 				growfldtab(i);
    334 			if (freeable(fldtab[i]))
    335 				xfree(fldtab[i]->sval);
    336 			fldtab[i]->sval = fr;
    337 			fldtab[i]->tval = FLD | STR | DONTFREE;
    338 			while (*r != sep && *r != rtest && *r != '\0')	/* \n is always a separator */
    339 				*fr++ = *r++;
    340 			*fr++ = 0;
    341 			if (*r++ == 0)
    342 				break;
    343 		}
    344 		*fr = 0;
    345 	}
    346 	if (i > nfields)
    347 		FATAL("record `%.30s...' has too many fields; can't happen", r);
    348 	cleanfld(i+1, lastfld);	/* clean out junk from previous record */
    349 	lastfld = i;
    350 	donefld = 1;
    351 	for (j = 1; j <= lastfld; j++) {
    352 		p = fldtab[j];
    353 		if(is_number(p->sval)) {
    354 			p->fval = atof(p->sval);
    355 			p->tval |= NUM;
    356 		}
    357 	}
    358 	setfval(nfloc, (Awkfloat) lastfld);
    359 	if (dbg) {
    360 		for (j = 0; j <= lastfld; j++) {
    361 			p = fldtab[j];
    362 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
    363 		}
    364 	}
    365 }
    366 
    367 void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
    368 {				/* nvals remain intact */
    369 	Cell *p;
    370 	int i;
    371 
    372 	for (i = n1; i <= n2; i++) {
    373 		p = fldtab[i];
    374 		if (freeable(p))
    375 			xfree(p->sval);
    376 		p->sval = "";
    377 		p->tval = FLD | STR | DONTFREE;
    378 	}
    379 }
    380 
    381 void newfld(int n)	/* add field n after end of existing lastfld */
    382 {
    383 	if (n > nfields)
    384 		growfldtab(n);
    385 	cleanfld(lastfld+1, n);
    386 	lastfld = n;
    387 	setfval(nfloc, (Awkfloat) n);
    388 }
    389 
    390 Cell *fieldadr(int n)	/* get nth field */
    391 {
    392 	if (n < 0)
    393 		FATAL("trying to access out of range field %d", n);
    394 	if (n > nfields)	/* fields after NF are empty */
    395 		growfldtab(n);	/* but does not increase NF */
    396 	return(fldtab[n]);
    397 }
    398 
    399 void growfldtab(int n)	/* make new fields up to at least $n */
    400 {
    401 	int nf = 2 * nfields;
    402 	size_t s;
    403 
    404 	if (n > nf)
    405 		nf = n;
    406 	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
    407 	if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
    408 		fldtab = (Cell **) realloc(fldtab, s);
    409 	else					/* overflow sizeof int */
    410 		xfree(fldtab);	/* make it null */
    411 	if (fldtab == NULL)
    412 		FATAL("out of space creating %d fields", nf);
    413 	makefields(nfields+1, nf);
    414 	nfields = nf;
    415 }
    416 
    417 int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
    418 {
    419 	/* this relies on having fields[] the same length as $0 */
    420 	/* the fields are all stored in this one array with \0's */
    421 	char *fr;
    422 	int i, tempstat, n;
    423 	fa *pfa;
    424 
    425 	n = strlen(rec);
    426 	if (n > fieldssize) {
    427 		xfree(fields);
    428 		if ((fields = (char *) malloc(n+1)) == NULL)
    429 			FATAL("out of space for fields in refldbld %d", n);
    430 		fieldssize = n;
    431 	}
    432 	fr = fields;
    433 	*fr = '\0';
    434 	if (*rec == '\0')
    435 		return 0;
    436 	pfa = makedfa(fs, 1);
    437 	   dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
    438 	tempstat = pfa->initstat;
    439 	for (i = 1; ; i++) {
    440 		if (i > nfields)
    441 			growfldtab(i);
    442 		if (freeable(fldtab[i]))
    443 			xfree(fldtab[i]->sval);
    444 		fldtab[i]->tval = FLD | STR | DONTFREE;
    445 		fldtab[i]->sval = fr;
    446 		   dprintf( ("refldbld: i=%d\n", i) );
    447 		if (nematch(pfa, rec)) {
    448 			pfa->initstat = 2;	/* horrible coupling to b.c */
    449 			   dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
    450 			strncpy(fr, rec, patbeg-rec);
    451 			fr += patbeg - rec + 1;
    452 			*(fr-1) = '\0';
    453 			rec = patbeg + patlen;
    454 		} else {
    455 			   dprintf( ("no match %s\n", rec) );
    456 			strcpy(fr, rec);
    457 			pfa->initstat = tempstat;
    458 			break;
    459 		}
    460 	}
    461 	return i;
    462 }
    463 
    464 void recbld(void)	/* create $0 from $1..$NF if necessary */
    465 {
    466 	int i;
    467 	char *r, *p;
    468 
    469 	if (donerec == 1)
    470 		return;
    471 	r = record;
    472 	for (i = 1; i <= *NF; i++) {
    473 		p = getsval(fldtab[i]);
    474 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
    475 			FATAL("created $0 `%.30s...' too long", record);
    476 		while ((*r = *p++) != 0)
    477 			r++;
    478 		if (i < *NF) {
    479 			if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
    480 				FATAL("created $0 `%.30s...' too long", record);
    481 			for (p = *OFS; (*r = *p++) != 0; )
    482 				r++;
    483 		}
    484 	}
    485 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
    486 		FATAL("built giant record `%.30s...'", record);
    487 	*r = '\0';
    488 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
    489 
    490 	if (freeable(fldtab[0]))
    491 		xfree(fldtab[0]->sval);
    492 	fldtab[0]->tval = REC | STR | DONTFREE;
    493 	fldtab[0]->sval = record;
    494 
    495 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
    496 	   dprintf( ("recbld = |%s|\n", record) );
    497 	donerec = 1;
    498 }
    499 
    500 int	errorflag	= 0;
    501 
    502 void yyerror(const char *s)
    503 {
    504 	SYNTAX("%s", s);
    505 }
    506 
    507 void SYNTAX(const char *fmt, ...)
    508 {
    509 	extern char *cmdname, *curfname;
    510 	static int been_here = 0;
    511 	va_list varg;
    512 
    513 	if (been_here++ > 2)
    514 		return;
    515 	fprintf(stderr, "%s: ", cmdname);
    516 	va_start(varg, fmt);
    517 	vfprintf(stderr, fmt, varg);
    518 	va_end(varg);
    519 	fprintf(stderr, " at source line %d", lineno);
    520 	if (curfname != NULL)
    521 		fprintf(stderr, " in function %s", curfname);
    522 	if (compile_time == 1 && cursource() != NULL)
    523 		fprintf(stderr, " source file %s", cursource());
    524 	fprintf(stderr, "\n");
    525 	errorflag = 2;
    526 	eprint();
    527 }
    528 
    529 void fpecatch(int n)
    530 {
    531 	FATAL("floating point exception %d", n);
    532 }
    533 
    534 extern int bracecnt, brackcnt, parencnt;
    535 
    536 void bracecheck(void)
    537 {
    538 	int c;
    539 	static int beenhere = 0;
    540 
    541 	if (beenhere++)
    542 		return;
    543 	while ((c = input()) != EOF && c != '\0')
    544 		bclass(c);
    545 	bcheck2(bracecnt, '{', '}');
    546 	bcheck2(brackcnt, '[', ']');
    547 	bcheck2(parencnt, '(', ')');
    548 }
    549 
    550 void bcheck2(int n, int c1, int c2)
    551 {
    552 	if (n == 1)
    553 		fprintf(stderr, "\tmissing %c\n", c2);
    554 	else if (n > 1)
    555 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
    556 	else if (n == -1)
    557 		fprintf(stderr, "\textra %c\n", c2);
    558 	else if (n < -1)
    559 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
    560 }
    561 
    562 void FATAL(const char *fmt, ...)
    563 {
    564 	extern char *cmdname;
    565 	va_list varg;
    566 
    567 	fflush(stdout);
    568 	fprintf(stderr, "%s: ", cmdname);
    569 	va_start(varg, fmt);
    570 	vfprintf(stderr, fmt, varg);
    571 	va_end(varg);
    572 	error();
    573 	if (dbg > 1)		/* core dump if serious debugging on */
    574 		abort();
    575 	exit(2);
    576 }
    577 
    578 void WARNING(const char *fmt, ...)
    579 {
    580 	extern char *cmdname;
    581 	va_list varg;
    582 
    583 	fflush(stdout);
    584 	fprintf(stderr, "%s: ", cmdname);
    585 	va_start(varg, fmt);
    586 	vfprintf(stderr, fmt, varg);
    587 	va_end(varg);
    588 	error();
    589 }
    590 
    591 void error()
    592 {
    593 	extern Node *curnode;
    594 
    595 	fprintf(stderr, "\n");
    596 	if (compile_time != 2 && NR && *NR > 0) {
    597 		fprintf(stderr, " input record number %d", (int) (*FNR));
    598 		if (strcmp(*FILENAME, "-") != 0)
    599 			fprintf(stderr, ", file %s", *FILENAME);
    600 		fprintf(stderr, "\n");
    601 	}
    602 	if (compile_time != 2 && curnode)
    603 		fprintf(stderr, " source line number %d", curnode->lineno);
    604 	else if (compile_time != 2 && lineno)
    605 		fprintf(stderr, " source line number %d", lineno);
    606 	if (compile_time == 1 && cursource() != NULL)
    607 		fprintf(stderr, " source file %s", cursource());
    608 	fprintf(stderr, "\n");
    609 	eprint();
    610 }
    611 
    612 void eprint(void)	/* try to print context around error */
    613 {
    614 	char *p, *q;
    615 	int c;
    616 	static int been_here = 0;
    617 	extern char ebuf[], *ep;
    618 
    619 	if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
    620 		return;
    621 	p = ep - 1;
    622 	if (p > ebuf && *p == '\n')
    623 		p--;
    624 	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
    625 		;
    626 	while (*p == '\n')
    627 		p++;
    628 	fprintf(stderr, " context is\n\t");
    629 	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
    630 		;
    631 	for ( ; p < q; p++)
    632 		if (*p)
    633 			putc(*p, stderr);
    634 	fprintf(stderr, " >>> ");
    635 	for ( ; p < ep; p++)
    636 		if (*p)
    637 			putc(*p, stderr);
    638 	fprintf(stderr, " <<< ");
    639 	if (*ep)
    640 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
    641 			putc(c, stderr);
    642 			bclass(c);
    643 		}
    644 	putc('\n', stderr);
    645 	ep = ebuf;
    646 }
    647 
    648 void bclass(int c)
    649 {
    650 	switch (c) {
    651 	case '{': bracecnt++; break;
    652 	case '}': bracecnt--; break;
    653 	case '[': brackcnt++; break;
    654 	case ']': brackcnt--; break;
    655 	case '(': parencnt++; break;
    656 	case ')': parencnt--; break;
    657 	}
    658 }
    659 
    660 double errcheck(double x, const char *s)
    661 {
    662 
    663 	if (errno == EDOM) {
    664 		errno = 0;
    665 		WARNING("%s argument out of domain", s);
    666 		x = 1;
    667 	} else if (errno == ERANGE) {
    668 		errno = 0;
    669 		WARNING("%s result out of range", s);
    670 		x = 1;
    671 	}
    672 	return x;
    673 }
    674 
    675 int isclvar(const char *s)	/* is s of form var=something ? */
    676 {
    677 	const char *os = s;
    678 
    679 	if (!isalpha((uschar) *s) && *s != '_')
    680 		return 0;
    681 	for ( ; *s; s++)
    682 		if (!(isalnum((uschar) *s) || *s == '_'))
    683 			break;
    684 	return *s == '=' && s > os && *(s+1) != '=';
    685 }
    686 
    687 /* strtod is supposed to be a proper test of what's a valid number */
    688 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
    689 /* wrong: violates 4.10.1.4 of ansi C standard */
    690 
    691 #include <math.h>
    692 int is_number(const char *s)
    693 {
    694 	double r;
    695 	char *ep;
    696 	errno = 0;
    697 	r = strtod(s, &ep);
    698 	if (ep == s || r == HUGE_VAL || errno == ERANGE)
    699 		return 0;
    700 	while (*ep == ' ' || *ep == '\t' || *ep == '\n')
    701 		ep++;
    702 	if (*ep == '\0')
    703 		return 1;
    704 	else
    705 		return 0;
    706 }
    707