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