xref: /dragonfly/contrib/awk/lib.c (revision 0ca59c34)
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 	*fldtab[0] = dollar0;
66 	fldtab[0]->sval = record;
67 	fldtab[0]->nval = tostring("0");
68 	makefields(1, nfields);
69 }
70 
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 
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 
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 
179 void nextfile(void)
180 {
181 	if (infile != NULL && infile != stdin)
182 		fclose(infile);
183 	infile = NULL;
184 	argno++;
185 }
186 
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 
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 
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 
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 
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 
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 
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 
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 
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 	int i, tempstat, n;
423 	fa *pfa;
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 	pfa = makedfa(fs, 1);
437 	   dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
438 	tempstat = pfa->initstat;
439 	for (i = 1; ; i++) {
440 		if (i > nfields)
441 			growfldtab(i);
442 		if (freeable(fldtab[i]))
443 			xfree(fldtab[i]->sval);
444 		fldtab[i]->tval = FLD | STR | DONTFREE;
445 		fldtab[i]->sval = fr;
446 		   dprintf( ("refldbld: i=%d\n", i) );
447 		if (nematch(pfa, rec)) {
448 			pfa->initstat = 2;	/* horrible coupling to b.c */
449 			   dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
450 			strncpy(fr, rec, patbeg-rec);
451 			fr += patbeg - rec + 1;
452 			*(fr-1) = '\0';
453 			rec = patbeg + patlen;
454 		} else {
455 			   dprintf( ("no match %s\n", rec) );
456 			strcpy(fr, rec);
457 			pfa->initstat = tempstat;
458 			break;
459 		}
460 	}
461 	return i;
462 }
463 
464 void recbld(void)	/* create $0 from $1..$NF if necessary */
465 {
466 	int i;
467 	char *r, *p;
468 
469 	if (donerec == 1)
470 		return;
471 	r = record;
472 	for (i = 1; i <= *NF; i++) {
473 		p = getsval(fldtab[i]);
474 		if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
475 			FATAL("created $0 `%.30s...' too long", record);
476 		while ((*r = *p++) != 0)
477 			r++;
478 		if (i < *NF) {
479 			if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
480 				FATAL("created $0 `%.30s...' too long", record);
481 			for (p = *OFS; (*r = *p++) != 0; )
482 				r++;
483 		}
484 	}
485 	if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
486 		FATAL("built giant record `%.30s...'", record);
487 	*r = '\0';
488 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
489 
490 	if (freeable(fldtab[0]))
491 		xfree(fldtab[0]->sval);
492 	fldtab[0]->tval = REC | STR | DONTFREE;
493 	fldtab[0]->sval = record;
494 
495 	   dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
496 	   dprintf( ("recbld = |%s|\n", record) );
497 	donerec = 1;
498 }
499 
500 int	errorflag	= 0;
501 
502 void yyerror(const char *s)
503 {
504 	SYNTAX("%s", s);
505 }
506 
507 void SYNTAX(const char *fmt, ...)
508 {
509 	extern char *cmdname, *curfname;
510 	static int been_here = 0;
511 	va_list varg;
512 
513 	if (been_here++ > 2)
514 		return;
515 	fprintf(stderr, "%s: ", cmdname);
516 	va_start(varg, fmt);
517 	vfprintf(stderr, fmt, varg);
518 	va_end(varg);
519 	fprintf(stderr, " at source line %d", lineno);
520 	if (curfname != NULL)
521 		fprintf(stderr, " in function %s", curfname);
522 	if (compile_time == 1 && cursource() != NULL)
523 		fprintf(stderr, " source file %s", cursource());
524 	fprintf(stderr, "\n");
525 	errorflag = 2;
526 	eprint();
527 }
528 
529 void fpecatch(int n)
530 {
531 	FATAL("floating point exception %d", n);
532 }
533 
534 extern int bracecnt, brackcnt, parencnt;
535 
536 void bracecheck(void)
537 {
538 	int c;
539 	static int beenhere = 0;
540 
541 	if (beenhere++)
542 		return;
543 	while ((c = input()) != EOF && c != '\0')
544 		bclass(c);
545 	bcheck2(bracecnt, '{', '}');
546 	bcheck2(brackcnt, '[', ']');
547 	bcheck2(parencnt, '(', ')');
548 }
549 
550 void bcheck2(int n, int c1, int c2)
551 {
552 	if (n == 1)
553 		fprintf(stderr, "\tmissing %c\n", c2);
554 	else if (n > 1)
555 		fprintf(stderr, "\t%d missing %c's\n", n, c2);
556 	else if (n == -1)
557 		fprintf(stderr, "\textra %c\n", c2);
558 	else if (n < -1)
559 		fprintf(stderr, "\t%d extra %c's\n", -n, c2);
560 }
561 
562 void FATAL(const char *fmt, ...)
563 {
564 	extern char *cmdname;
565 	va_list varg;
566 
567 	fflush(stdout);
568 	fprintf(stderr, "%s: ", cmdname);
569 	va_start(varg, fmt);
570 	vfprintf(stderr, fmt, varg);
571 	va_end(varg);
572 	error();
573 	if (dbg > 1)		/* core dump if serious debugging on */
574 		abort();
575 	exit(2);
576 }
577 
578 void WARNING(const char *fmt, ...)
579 {
580 	extern char *cmdname;
581 	va_list varg;
582 
583 	fflush(stdout);
584 	fprintf(stderr, "%s: ", cmdname);
585 	va_start(varg, fmt);
586 	vfprintf(stderr, fmt, varg);
587 	va_end(varg);
588 	error();
589 }
590 
591 void error()
592 {
593 	extern Node *curnode;
594 
595 	fprintf(stderr, "\n");
596 	if (compile_time != 2 && NR && *NR > 0) {
597 		fprintf(stderr, " input record number %d", (int) (*FNR));
598 		if (strcmp(*FILENAME, "-") != 0)
599 			fprintf(stderr, ", file %s", *FILENAME);
600 		fprintf(stderr, "\n");
601 	}
602 	if (compile_time != 2 && curnode)
603 		fprintf(stderr, " source line number %d", curnode->lineno);
604 	else if (compile_time != 2 && lineno)
605 		fprintf(stderr, " source line number %d", lineno);
606 	if (compile_time == 1 && cursource() != NULL)
607 		fprintf(stderr, " source file %s", cursource());
608 	fprintf(stderr, "\n");
609 	eprint();
610 }
611 
612 void eprint(void)	/* try to print context around error */
613 {
614 	char *p, *q;
615 	int c;
616 	static int been_here = 0;
617 	extern char ebuf[], *ep;
618 
619 	if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
620 		return;
621 	p = ep - 1;
622 	if (p > ebuf && *p == '\n')
623 		p--;
624 	for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
625 		;
626 	while (*p == '\n')
627 		p++;
628 	fprintf(stderr, " context is\n\t");
629 	for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
630 		;
631 	for ( ; p < q; p++)
632 		if (*p)
633 			putc(*p, stderr);
634 	fprintf(stderr, " >>> ");
635 	for ( ; p < ep; p++)
636 		if (*p)
637 			putc(*p, stderr);
638 	fprintf(stderr, " <<< ");
639 	if (*ep)
640 		while ((c = input()) != '\n' && c != '\0' && c != EOF) {
641 			putc(c, stderr);
642 			bclass(c);
643 		}
644 	putc('\n', stderr);
645 	ep = ebuf;
646 }
647 
648 void bclass(int c)
649 {
650 	switch (c) {
651 	case '{': bracecnt++; break;
652 	case '}': bracecnt--; break;
653 	case '[': brackcnt++; break;
654 	case ']': brackcnt--; break;
655 	case '(': parencnt++; break;
656 	case ')': parencnt--; break;
657 	}
658 }
659 
660 double errcheck(double x, const char *s)
661 {
662 
663 	if (errno == EDOM) {
664 		errno = 0;
665 		WARNING("%s argument out of domain", s);
666 		x = 1;
667 	} else if (errno == ERANGE) {
668 		errno = 0;
669 		WARNING("%s result out of range", s);
670 		x = 1;
671 	}
672 	return x;
673 }
674 
675 int isclvar(const char *s)	/* is s of form var=something ? */
676 {
677 	const char *os = s;
678 
679 	if (!isalpha((uschar) *s) && *s != '_')
680 		return 0;
681 	for ( ; *s; s++)
682 		if (!(isalnum((uschar) *s) || *s == '_'))
683 			break;
684 	return *s == '=' && s > os && *(s+1) != '=';
685 }
686 
687 /* strtod is supposed to be a proper test of what's a valid number */
688 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
689 /* wrong: violates 4.10.1.4 of ansi C standard */
690 
691 #include <math.h>
692 int is_number(const char *s)
693 {
694 	double r;
695 	char *ep;
696 	errno = 0;
697 	r = strtod(s, &ep);
698 	if (ep == s || r == HUGE_VAL || errno == ERANGE)
699 		return 0;
700 	while (*ep == ' ' || *ep == '\t' || *ep == '\n')
701 		ep++;
702 	if (*ep == '\0')
703 		return 1;
704 	else
705 		return 0;
706 }
707