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