fatbase

portable OpenBSD tools
git clone git://git.2f30.org/fatbase
Log | Files | Refs

lib.c (18525B)


      1 /*	$OpenBSD: lib.c,v 1.20 2011/09/28 19:27:18 millert Exp $	*/
      2 /****************************************************************
      3 Copyright (C) Lucent Technologies 1997
      4 All Rights Reserved
      5 
      6 Permission to use, copy, modify, and distribute this software and
      7 its documentation for any purpose and without fee is hereby
      8 granted, provided that the above copyright notice appear in all
      9 copies and that both that the copyright notice and this
     10 permission notice and warranty disclaimer appear in supporting
     11 documentation, and that the name Lucent Technologies or any of
     12 its entities not be used in advertising or publicity pertaining
     13 to distribution of the software without specific, written prior
     14 permission.
     15 
     16 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     17 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
     18 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
     19 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     20 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     21 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
     22 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
     23 THIS SOFTWARE.
     24 ****************************************************************/
     25 
     26 #define DEBUG
     27 #include <stdio.h>
     28 #include <string.h>
     29 #include <ctype.h>
     30 #include <errno.h>
     31 #include <stdlib.h>
     32 #include <unistd.h>
     33 #include <stdarg.h>
     34 #include "awk.h"
     35 #include "util.h"
     36 #include "ytab.h"
     37 
     38 FILE	*infile	= NULL;
     39 char	*file	= "";
     40 char	*record;
     41 int	recsize	= RECSIZE;
     42 char	*fields;
     43 int	fieldssize = RECSIZE;
     44 
     45 Cell	**fldtab;	/* pointers to Cells */
     46 char	inputFS[100] = " ";
     47 
     48 #define	MAXFLD	2
     49 int	nfields	= MAXFLD;	/* last allocated slot for $i */
     50 
     51 int	donefld;	/* 1 = implies rec broken into fields */
     52 int	donerec;	/* 1 = record is valid (no flds have changed) */
     53 
     54 int	lastfld	= 0;	/* last used field */
     55 int	argno	= 1;	/* current input argument number */
     56 extern	Awkfloat *ARGC;
     57 
     58 static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
     59 static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
     60 
     61 void recinit(unsigned int n)
     62 {
     63 	if ( (record = (char *) malloc(n)) == NULL
     64 	  || (fields = (char *) malloc(n+1)) == NULL
     65 	  || (fldtab = (Cell **) calloc(nfields+1, sizeof(Cell *))) == NULL
     66 	  || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL )
     67 		FATAL("out of space for $0 and fields");
     68 	*fldtab[0] = dollar0;
     69 	fldtab[0]->sval = record;
     70 	fldtab[0]->nval = tostring("0");
     71 	makefields(1, nfields);
     72 }
     73 
     74 void makefields(int n1, int n2)		/* create $n1..$n2 inclusive */
     75 {
     76 	char temp[50];
     77 	int i;
     78 
     79 	for (i = n1; i <= n2; i++) {
     80 		fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
     81 		if (fldtab[i] == NULL)
     82 			FATAL("out of space in makefields %d", i);
     83 		*fldtab[i] = dollar1;
     84 		snprintf(temp, sizeof temp, "%d", i);
     85 		fldtab[i]->nval = tostring(temp);
     86 	}
     87 }
     88 
     89 void initgetrec(void)
     90 {
     91 	int i;
     92 	char *p;
     93 
     94 	for (i = 1; i < *ARGC; i++) {
     95 		p = getargv(i); /* find 1st real filename */
     96 		if (p == NULL || *p == '\0') {  /* deleted or zapped */
     97 			argno++;
     98 			continue;
     99 		}
    100 		if (!isclvar(p)) {
    101 			setsval(lookup("FILENAME", symtab), p);
    102 			return;
    103 		}
    104 		setclvar(p);	/* a commandline assignment before filename */
    105 		argno++;
    106 	}
    107 	infile = stdin;		/* no filenames, so use stdin */
    108 }
    109 
    110 static int firsttime = 1;
    111 
    112 int getrec(char **pbuf, int *pbufsize, int isrecord)	/* get next input record */
    113 {			/* note: cares whether buf == record */
    114 	int c;
    115 	char *buf = *pbuf;
    116 	uschar saveb0;
    117 	int bufsize = *pbufsize, savebufsize = bufsize;
    118 
    119 	if (firsttime) {
    120 		firsttime = 0;
    121 		initgetrec();
    122 	}
    123 	   dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
    124 		*RS, *FS, *ARGC, *FILENAME) );
    125 	if (isrecord) {
    126 		donefld = 0;
    127 		donerec = 1;
    128 	}
    129 	saveb0 = buf[0];
    130 	buf[0] = 0;
    131 	while (argno < *ARGC || infile == stdin) {
    132 		   dprintf( ("argno=%d, file=|%s|\n", argno, file) );
    133 		if (infile == NULL) {	/* have to open a new file */
    134 			file = getargv(argno);
    135 			if (file == NULL || *file == '\0') {	/* deleted or zapped */
    136 				argno++;
    137 				continue;
    138 			}
    139 			if (isclvar(file)) {	/* a var=value arg */
    140 				setclvar(file);
    141 				argno++;
    142 				continue;
    143 			}
    144 			*FILENAME = file;
    145 			   dprintf( ("opening file %s\n", file) );
    146 			if (*file == '-' && *(file+1) == '\0')
    147 				infile = stdin;
    148 			else if ((infile = fopen(file, "r")) == NULL)
    149 				FATAL("can't open file %s", file);
    150 			setfval(fnrloc, 0.0);
    151 		}
    152 		c = readrec(&buf, &bufsize, infile);
    153 		if (c != 0 || buf[0] != '\0') {	/* normal record */
    154 			if (isrecord) {
    155 				if (freeable(fldtab[0]))
    156 					xfree(fldtab[0]->sval);
    157 				fldtab[0]->sval = buf;	/* buf == record */
    158 				fldtab[0]->tval = REC | STR | DONTFREE;
    159 				if (is_number(fldtab[0]->sval)) {
    160 					fldtab[0]->fval = atof(fldtab[0]->sval);
    161 					fldtab[0]->tval |= NUM;
    162 				}
    163 			}
    164 			setfval(nrloc, nrloc->fval+1);
    165 			setfval(fnrloc, fnrloc->fval+1);
    166 			*pbuf = buf;
    167 			*pbufsize = bufsize;
    168 			return 1;
    169 		}
    170 		/* EOF arrived on this file; set up next */
    171 		if (infile != stdin)
    172 			fclose(infile);
    173 		infile = NULL;
    174 		argno++;
    175 	}
    176 	buf[0] = saveb0;
    177 	*pbuf = buf;
    178 	*pbufsize = savebufsize;
    179 	return 0;	/* true end of file */
    180 }
    181 
    182 void nextfile(void)
    183 {
    184 	if (infile != NULL && infile != stdin)
    185 		fclose(infile);
    186 	infile = NULL;
    187 	argno++;
    188 }
    189 
    190 int readrec(char **pbuf, int *pbufsize, FILE *inf)	/* read one record into buf */
    191 {
    192 	int sep, c;
    193 	char *rr, *buf = *pbuf;
    194 	int bufsize = *pbufsize;
    195 
    196 	if (strlen(*FS) >= sizeof(inputFS))
    197 		FATAL("field separator %.10s... is too long", *FS);
    198 	/*fflush(stdout); avoids some buffering problem but makes it 25% slower*/
    199 	strlcpy(inputFS, *FS, sizeof inputFS);	/* for subsequent field splitting */
    200 	if ((sep = **RS) == 0) {
    201 		sep = '\n';
    202 		while ((c=getc(inf)) == '\n' && c != EOF)	/* skip leading \n's */
    203 			;
    204 		if (c != EOF)
    205 			ungetc(c, inf);
    206 	}
    207 	for (rr = buf; ; ) {
    208 		for (; (c=getc(inf)) != sep && c != EOF; ) {
    209 			if (rr-buf+1 > bufsize)
    210 				if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
    211 					FATAL("input record `%.30s...' too long", buf);
    212 			*rr++ = c;
    213 		}
    214 		if (**RS == sep || c == EOF)
    215 			break;
    216 		if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
    217 			break;
    218 		if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
    219 			FATAL("input record `%.30s...' too long", buf);
    220 		*rr++ = '\n';
    221 		*rr++ = c;
    222 	}
    223 	if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
    224 		FATAL("input record `%.30s...' too long", buf);
    225 	*rr = 0;
    226 	   dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
    227 	*pbuf = buf;
    228 	*pbufsize = bufsize;
    229 	return c == EOF && rr == buf ? 0 : 1;
    230 }
    231 
    232 char *getargv(int n)	/* get ARGV[n] */
    233 {
    234 	Cell *x;
    235 	char *s, temp[50];
    236 	extern Array *ARGVtab;
    237 
    238 	snprintf(temp, sizeof temp, "%d", n);
    239 	if (lookup(temp, ARGVtab) == NULL)
    240 		return NULL;
    241 	x = setsymtab(temp, "", 0.0, STR, ARGVtab);
    242 	s = getsval(x);
    243 	   dprintf( ("getargv(%d) returns |%s|\n", n, s) );
    244 	return s;
    245 }
    246 
    247 void setclvar(char *s)	/* set var=value from s */
    248 {
    249 	char *p;
    250 	Cell *q;
    251 
    252 	for (p=s; *p != '='; p++)
    253 		;
    254 	*p++ = 0;
    255 	p = qstring(p, '\0');
    256 	q = setsymtab(s, p, 0.0, STR, symtab);
    257 	setsval(q, p);
    258 	if (is_number(q->sval)) {
    259 		q->fval = atof(q->sval);
    260 		q->tval |= NUM;
    261 	}
    262 	   dprintf( ("command line set %s to |%s|\n", s, p) );
    263 }
    264 
    265 
    266 void fldbld(void)	/* create fields from current record */
    267 {
    268 	/* this relies on having fields[] the same length as $0 */
    269 	/* the fields are all stored in this one array with \0's */
    270 	/* possibly with a final trailing \0 not associated with any field */
    271 	char *r, *fr, sep;
    272 	Cell *p;
    273 	int i, j, n;
    274 
    275 	if (donefld)
    276 		return;
    277 	if (!isstr(fldtab[0]))
    278 		getsval(fldtab[0]);
    279 	r = fldtab[0]->sval;
    280 	n = strlen(r);
    281 	if (n > fieldssize) {
    282 		xfree(fields);
    283 		if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
    284 			FATAL("out of space for fields in fldbld %d", n);
    285 		fieldssize = n;
    286 	}
    287 	fr = fields;
    288 	i = 0;	/* number of fields accumulated here */
    289 	strlcpy(inputFS, *FS, sizeof(inputFS));
    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 	if (dbg) {
    363 		for (j = 0; j <= lastfld; j++) {
    364 			p = fldtab[j];
    365 			printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
    366 		}
    367 	}
    368 }
    369 
    370 void cleanfld(int n1, int n2)	/* clean out fields n1 .. n2 inclusive */
    371 {				/* nvals remain intact */
    372 	Cell *p;
    373 	int i;
    374 
    375 	for (i = n1; i <= n2; i++) {
    376 		p = fldtab[i];
    377 		if (freeable(p))
    378 			xfree(p->sval);
    379 		p->sval = "";
    380 		p->tval = FLD | STR | DONTFREE;
    381 	}
    382 }
    383 
    384 void newfld(int n)	/* add field n after end of existing lastfld */
    385 {
    386 	if (n > nfields)
    387 		growfldtab(n);
    388 	cleanfld(lastfld+1, n);
    389 	lastfld = n;
    390 	setfval(nfloc, (Awkfloat) n);
    391 }
    392 
    393 Cell *fieldadr(int n)	/* get nth field */
    394 {
    395 	if (n < 0)
    396 		FATAL("trying to access out of range field %d", n);
    397 	if (n > nfields)	/* fields after NF are empty */
    398 		growfldtab(n);	/* but does not increase NF */
    399 	return(fldtab[n]);
    400 }
    401 
    402 void growfldtab(int n)	/* make new fields up to at least $n */
    403 {
    404 	int nf = 2 * nfields;
    405 	size_t s;
    406 
    407 	if (n > nf)
    408 		nf = n;
    409 	s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
    410 	if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
    411 		fldtab = (Cell **) realloc(fldtab, s);
    412 	else					/* overflow sizeof int */
    413 		xfree(fldtab);	/* make it null */
    414 	if (fldtab == NULL)
    415 		FATAL("out of space creating %d fields", nf);
    416 	makefields(nfields+1, nf);
    417 	nfields = nf;
    418 }
    419 
    420 int refldbld(const char *rec, const char *fs)	/* build fields from reg expr in FS */
    421 {
    422 	/* this relies on having fields[] the same length as $0 */
    423 	/* the fields are all stored in this one array with \0's */
    424 	char *fr;
    425 	int i, tempstat, n;
    426 	fa *pfa;
    427 
    428 	n = strlen(rec);
    429 	if (n > fieldssize) {
    430 		xfree(fields);
    431 		if ((fields = (char *) malloc(n+1)) == NULL)
    432 			FATAL("out of space for fields in refldbld %d", n);
    433 		fieldssize = n;
    434 	}
    435 	fr = fields;
    436 	*fr = '\0';
    437 	if (*rec == '\0')
    438 		return 0;
    439 	pfa = makedfa(fs, 1);
    440 	   dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
    441 	tempstat = pfa->initstat;
    442 	for (i = 1; ; i++) {
    443 		if (i > nfields)
    444 			growfldtab(i);
    445 		if (freeable(fldtab[i]))
    446 			xfree(fldtab[i]->sval);
    447 		fldtab[i]->tval = FLD | STR | DONTFREE;
    448 		fldtab[i]->sval = fr;
    449 		   dprintf( ("refldbld: i=%d\n", i) );
    450 		if (nematch(pfa, rec)) {
    451 			pfa->initstat = 2;	/* horrible coupling to b.c */
    452 			   dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
    453 			strncpy(fr, rec, patbeg-rec);
    454 			fr += patbeg - rec + 1;
    455 			*(fr-1) = '\0';
    456 			rec = patbeg + patlen;
    457 		} else {
    458 			   dprintf( ("no match %s\n", rec) );
    459 			strlcpy(fr, rec, fields + fieldssize - fr);
    460 			pfa->initstat = tempstat;
    461 			break;
    462 		}
    463 	}
    464 	return i;		
    465 }
    466 
    467 void recbld(void)	/* create $0 from $1..$NF if necessary */
    468 {
    469 	int i;
    470 	char *r, *p;
    471 
    472 	if (donerec == 1)
    473 		return;
    474 	r = record;
    475 	for (i = 1; i <= *NF; i++) {
    476 		p = getsval(fldtab[i]);
    477 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
    478 			FATAL("created $0 `%.30s...' too long", record);
    479 		while ((*r = *p++) != 0)
    480 			r++;
    481 		if (i < *NF) {
    482 			if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
    483 				FATAL("created $0 `%.30s...' too long", record);
    484 			for (p = *OFS; (*r = *p++) != 0; )
    485 				r++;
    486 		}
    487 	}
    488 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
    489 		FATAL("built giant record `%.30s...'", record);
    490 	*r = '\0';
    491 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
    492 
    493 	if (freeable(fldtab[0]))
    494 		xfree(fldtab[0]->sval);
    495 	fldtab[0]->tval = REC | STR | DONTFREE;
    496 	fldtab[0]->sval = record;
    497 
    498 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
    499 	   dprintf( ("recbld = |%s|\n", record) );
    500 	donerec = 1;
    501 }
    502 
    503 int	errorflag	= 0;
    504 
    505 void yyerror(const char *s)
    506 {
    507 	SYNTAX("%s", s);
    508 }
    509 
    510 void SYNTAX(const char *fmt, ...)
    511 {
    512 	extern char *cmdname, *curfname;
    513 	static int been_here = 0;
    514 	va_list varg;
    515 
    516 	if (been_here++ > 2)
    517 		return;
    518 	fprintf(stderr, "%s: ", cmdname);
    519 	va_start(varg, fmt);
    520 	vfprintf(stderr, fmt, varg);
    521 	va_end(varg);
    522 	fprintf(stderr, " at source line %d", lineno);
    523 	if (curfname != NULL)
    524 		fprintf(stderr, " in function %s", curfname);
    525 	if (compile_time == 1 && cursource() != NULL)
    526 		fprintf(stderr, " source file %s", cursource());
    527 	fprintf(stderr, "\n");
    528 	errorflag = 2;
    529 	eprint();
    530 }
    531 
    532 void fpecatch(int sig)
    533 {
    534 	extern Node *curnode;
    535 	char buf[1024];
    536 
    537 	snprintf(buf, sizeof buf, "floating point exception\n");
    538 	write(STDERR_FILENO, buf, strlen(buf));
    539 
    540 	if (compile_time != 2 && NR && *NR > 0) {
    541 		snprintf(buf, sizeof buf, " input record number %d", (int) (*FNR));
    542 		write(STDERR_FILENO, buf, strlen(buf));
    543 
    544 		if (strcmp(*FILENAME, "-") != 0) {
    545 			snprintf(buf, sizeof buf, ", file %s", *FILENAME);
    546 			write(STDERR_FILENO, buf, strlen(buf));
    547 		}
    548 		write(STDERR_FILENO, "\n", 1);
    549 	}
    550 	if (compile_time != 2 && curnode) {
    551 		snprintf(buf, sizeof buf, " source line number %d", curnode->lineno);
    552 		write(STDERR_FILENO, buf, strlen(buf));
    553 	} else if (compile_time != 2 && lineno) {
    554 		snprintf(buf, sizeof buf, " source line number %d", lineno);
    555 		write(STDERR_FILENO, buf, strlen(buf));
    556 	}
    557 	if (compile_time == 1 && cursource() != NULL) {
    558 		snprintf(buf, sizeof buf, " source file %s", cursource());
    559 		write(STDERR_FILENO, buf, strlen(buf));
    560 	}
    561 	write(STDERR_FILENO, "\n", 1);
    562 	if (dbg > 1)		/* core dump if serious debugging on */
    563 		abort();
    564 	_exit(1);
    565 }
    566 
    567 extern int bracecnt, brackcnt, parencnt;
    568 
    569 void bracecheck(void)
    570 {
    571 	int c;
    572 	static int beenhere = 0;
    573 
    574 	if (beenhere++)
    575 		return;
    576 	while ((c = input()) != EOF && c != '\0')
    577 		bclass(c);
    578 	bcheck2(bracecnt, '{', '}');
    579 	bcheck2(brackcnt, '[', ']');
    580 	bcheck2(parencnt, '(', ')');
    581 }
    582 
    583 void bcheck2(int n, int c1, int c2)
    584 {
    585 	if (n == 1)
    586 		fprintf(stderr, "\tmissing %c\n", c2);
    587 	else if (n > 1)
    588 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
    589 	else if (n == -1)
    590 		fprintf(stderr, "\textra %c\n", c2);
    591 	else if (n < -1)
    592 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
    593 }
    594 
    595 void FATAL(const char *fmt, ...)
    596 {
    597 	extern char *cmdname;
    598 	va_list varg;
    599 
    600 	fflush(stdout);
    601 	fprintf(stderr, "%s: ", cmdname);
    602 	va_start(varg, fmt);
    603 	vfprintf(stderr, fmt, varg);
    604 	va_end(varg);
    605 	error();
    606 	if (dbg > 1)		/* core dump if serious debugging on */
    607 		abort();
    608 	exit(2);
    609 }
    610 
    611 void WARNING(const char *fmt, ...)
    612 {
    613 	extern char *cmdname;
    614 	va_list varg;
    615 
    616 	fflush(stdout);
    617 	fprintf(stderr, "%s: ", cmdname);
    618 	va_start(varg, fmt);
    619 	vfprintf(stderr, fmt, varg);
    620 	va_end(varg);
    621 	error();
    622 }
    623 
    624 void error()
    625 {
    626 	extern Node *curnode;
    627 
    628 	fprintf(stderr, "\n");
    629 	if (compile_time != 2 && NR && *NR > 0) {
    630 		fprintf(stderr, " input record number %d", (int) (*FNR));
    631 		if (strcmp(*FILENAME, "-") != 0)
    632 			fprintf(stderr, ", file %s", *FILENAME);
    633 		fprintf(stderr, "\n");
    634 	}
    635 	if (compile_time != 2 && curnode)
    636 		fprintf(stderr, " source line number %d", curnode->lineno);
    637 	else if (compile_time != 2 && lineno)
    638 		fprintf(stderr, " source line number %d", lineno);
    639 	if (compile_time == 1 && cursource() != NULL)
    640 		fprintf(stderr, " source file %s", cursource());
    641 	fprintf(stderr, "\n");
    642 	eprint();
    643 }
    644 
    645 void eprint(void)	/* try to print context around error */
    646 {
    647 	char *p, *q;
    648 	int c;
    649 	static int been_here = 0;
    650 	extern char ebuf[], *ep;
    651 
    652 	if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
    653 		return;
    654 	p = ep - 1;
    655 	if (p > ebuf && *p == '\n')
    656 		p--;
    657 	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
    658 		;
    659 	while (*p == '\n')
    660 		p++;
    661 	fprintf(stderr, " context is\n\t");
    662 	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
    663 		;
    664 	for ( ; p < q; p++)
    665 		if (*p)
    666 			putc(*p, stderr);
    667 	fprintf(stderr, " >>> ");
    668 	for ( ; p < ep; p++)
    669 		if (*p)
    670 			putc(*p, stderr);
    671 	fprintf(stderr, " <<< ");
    672 	if (*ep)
    673 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
    674 			putc(c, stderr);
    675 			bclass(c);
    676 		}
    677 	putc('\n', stderr);
    678 	ep = ebuf;
    679 }
    680 
    681 void bclass(int c)
    682 {
    683 	switch (c) {
    684 	case '{': bracecnt++; break;
    685 	case '}': bracecnt--; break;
    686 	case '[': brackcnt++; break;
    687 	case ']': brackcnt--; break;
    688 	case '(': parencnt++; break;
    689 	case ')': parencnt--; break;
    690 	}
    691 }
    692 
    693 double errcheck(double x, const char *s)
    694 {
    695 
    696 	if (errno == EDOM) {
    697 		errno = 0;
    698 		WARNING("%s argument out of domain", s);
    699 		x = 1;
    700 	} else if (errno == ERANGE) {
    701 		errno = 0;
    702 		WARNING("%s result out of range", s);
    703 		x = 1;
    704 	}
    705 	return x;
    706 }
    707 
    708 int isclvar(const char *s)	/* is s of form var=something ? */
    709 {
    710 	const char *os = s;
    711 
    712 	if (!isalpha((uschar) *s) && *s != '_')
    713 		return 0;
    714 	for ( ; *s; s++)
    715 		if (!(isalnum((uschar) *s) || *s == '_'))
    716 			break;
    717 	return *s == '=' && s > os && *(s+1) != '=';
    718 }
    719 
    720 /* strtod is supposed to be a proper test of what's a valid number */
    721 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
    722 /* wrong: violates 4.10.1.4 of ansi C standard */
    723 
    724 #include <math.h>
    725 int is_number(const char *s)
    726 {
    727 	double r;
    728 	char *ep;
    729 	errno = 0;
    730 	r = strtod(s, &ep);
    731 	if (ep == s || r == HUGE_VAL || errno == ERANGE)
    732 		return 0;
    733 	while (*ep == ' ' || *ep == '\t' || *ep == '\n')
    734 		ep++;
    735 	if (*ep == '\0')
    736 		return 1;
    737 	else
    738 		return 0;
    739 }