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