9base

revived minimalist port of Plan 9 userland to Unix
git clone git://git.suckless.org/9base
Log | Files | Refs | README | LICENSE

lib.c (16713B)


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