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