plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

lib.c (16801B)


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