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
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+2) * 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 char *rs = getsval(rsloc);
193
194 if (strlen(getsval(fsloc)) >= 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
getargv(int n)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
setclvar(char * s)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
fldbld(void)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 if (strlen(getsval(fsloc)) >= sizeof (inputFS))
288 FATAL("field separator %.10s... is too long", *FS);
289 strcpy(inputFS, *FS);
290 if (strlen(inputFS) > 1) { /* it's a regular expression */
291 i = refldbld(r, inputFS);
292 } else if ((sep = *inputFS) == ' ') { /* default whitespace */
293 for (i = 0; ; ) {
294 while (*r == ' ' || *r == '\t' || *r == '\n')
295 r++;
296 if (*r == 0)
297 break;
298 i++;
299 if (i > nfields)
300 growfldtab(i);
301 if (freeable(fldtab[i]))
302 xfree(fldtab[i]->sval);
303 fldtab[i]->sval = fr;
304 fldtab[i]->tval = FLD | STR | DONTFREE;
305 do
306 *fr++ = *r++;
307 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
308 *fr++ = 0;
309 }
310 *fr = 0;
311 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
312 for (i = 0; *r != 0; r++) {
313 char buf[2];
314 i++;
315 if (i > nfields)
316 growfldtab(i);
317 if (freeable(fldtab[i]))
318 xfree(fldtab[i]->sval);
319 buf[0] = *r;
320 buf[1] = 0;
321 fldtab[i]->sval = tostring(buf);
322 fldtab[i]->tval = FLD | STR;
323 }
324 *fr = 0;
325 } else if (*r != 0) { /* if 0, it's a null field */
326 /* subtlecase : if length(FS) == 1 && length(RS > 0)
327 * \n is NOT a field separator (cf awk book 61,84).
328 * this variable is tested in the inner while loop.
329 */
330 int rtest = '\n'; /* normal case */
331 if (strlen(*RS) > 0)
332 rtest = '\0';
333 for (;;) {
334 i++;
335 if (i > nfields)
336 growfldtab(i);
337 if (freeable(fldtab[i]))
338 xfree(fldtab[i]->sval);
339 fldtab[i]->sval = fr;
340 fldtab[i]->tval = FLD | STR | DONTFREE;
341 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
342 *fr++ = *r++;
343 *fr++ = 0;
344 if (*r++ == 0)
345 break;
346 }
347 *fr = 0;
348 }
349 if (i > nfields)
350 FATAL("record `%.30s...' has too many fields; can't happen", r);
351 cleanfld(i+1, lastfld); /* clean out junk from previous record */
352 lastfld = i;
353 donefld = 1;
354 for (j = 1; j <= lastfld; j++) {
355 p = fldtab[j];
356 if(is_number(p->sval)) {
357 p->fval = atof(p->sval);
358 p->tval |= NUM;
359 }
360 }
361 setfval(nfloc, (Awkfloat) lastfld);
362 donerec = 1; /* restore */
363 if (dbg) {
364 for (j = 0; j <= lastfld; j++) {
365 p = fldtab[j];
366 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
367 }
368 }
369 }
370
cleanfld(int n1,int n2)371 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
372 { /* nvals remain intact */
373 Cell *p;
374 int i;
375
376 for (i = n1; i <= n2; i++) {
377 p = fldtab[i];
378 if (freeable(p))
379 xfree(p->sval);
380 p->sval = "";
381 p->tval = FLD | STR | DONTFREE;
382 }
383 }
384
newfld(int n)385 void newfld(int n) /* add field n after end of existing lastfld */
386 {
387 if (n > nfields)
388 growfldtab(n);
389 cleanfld(lastfld+1, n);
390 lastfld = n;
391 setfval(nfloc, (Awkfloat) n);
392 }
393
setlastfld(int n)394 void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
395 {
396 if (n < 0)
397 FATAL("cannot set NF to a negative value");
398 if (n > nfields)
399 growfldtab(n);
400
401 if (lastfld < n)
402 cleanfld(lastfld+1, n);
403 else
404 cleanfld(n+1, lastfld);
405
406 lastfld = n;
407 }
408
fieldadr(int n)409 Cell *fieldadr(int n) /* get nth field */
410 {
411 if (n < 0)
412 FATAL("trying to access out of range field %d", n);
413 if (n > nfields) /* fields after NF are empty */
414 growfldtab(n); /* but does not increase NF */
415 return(fldtab[n]);
416 }
417
growfldtab(int n)418 void growfldtab(int n) /* make new fields up to at least $n */
419 {
420 int nf = 2 * nfields;
421 size_t s;
422
423 if (n > nf)
424 nf = n;
425 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
426 if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
427 fldtab = (Cell **) realloc(fldtab, s);
428 else /* overflow sizeof int */
429 xfree(fldtab); /* make it null */
430 if (fldtab == NULL)
431 FATAL("out of space creating %d fields", nf);
432 makefields(nfields+1, nf);
433 nfields = nf;
434 }
435
refldbld(const char * rec,const char * fs)436 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
437 {
438 /* this relies on having fields[] the same length as $0 */
439 /* the fields are all stored in this one array with \0's */
440 char *fr;
441 int i, tempstat, n;
442 fa *pfa;
443
444 n = strlen(rec);
445 if (n > fieldssize) {
446 xfree(fields);
447 if ((fields = (char *) malloc(n+1)) == NULL)
448 FATAL("out of space for fields in refldbld %d", n);
449 fieldssize = n;
450 }
451 fr = fields;
452 *fr = '\0';
453 if (*rec == '\0')
454 return 0;
455 pfa = makedfa(fs, 1);
456 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
457 tempstat = pfa->initstat;
458 for (i = 1; ; i++) {
459 if (i > nfields)
460 growfldtab(i);
461 if (freeable(fldtab[i]))
462 xfree(fldtab[i]->sval);
463 fldtab[i]->tval = FLD | STR | DONTFREE;
464 fldtab[i]->sval = fr;
465 dprintf( ("refldbld: i=%d\n", i) );
466 if (nematch(pfa, rec)) {
467 pfa->initstat = 2; /* horrible coupling to b.c */
468 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
469 strncpy(fr, rec, patbeg-rec);
470 fr += patbeg - rec + 1;
471 *(fr-1) = '\0';
472 rec = patbeg + patlen;
473 } else {
474 dprintf( ("no match %s\n", rec) );
475 strcpy(fr, rec);
476 pfa->initstat = tempstat;
477 break;
478 }
479 }
480 return i;
481 }
482
recbld(void)483 void recbld(void) /* create $0 from $1..$NF if necessary */
484 {
485 int i;
486 char *r, *p;
487 char *sep = getsval(ofsloc);
488
489 if (donerec == 1)
490 return;
491 r = record;
492 for (i = 1; i <= *NF; i++) {
493 p = getsval(fldtab[i]);
494 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
495 FATAL("created $0 `%.30s...' too long", record);
496 while ((*r = *p++) != 0)
497 r++;
498 if (i < *NF) {
499 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
500 FATAL("created $0 `%.30s...' too long", record);
501 for (p = sep; (*r = *p++) != 0; )
502 r++;
503 }
504 }
505 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
506 FATAL("built giant record `%.30s...'", record);
507 *r = '\0';
508 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
509
510 if (freeable(fldtab[0]))
511 xfree(fldtab[0]->sval);
512 fldtab[0]->tval = REC | STR | DONTFREE;
513 fldtab[0]->sval = record;
514
515 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
516 dprintf( ("recbld = |%s|\n", record) );
517 donerec = 1;
518 }
519
520 int errorflag = 0;
521
yyerror(const char * s)522 void yyerror(const char *s)
523 {
524 SYNTAX("%s", s);
525 }
526
SYNTAX(const char * fmt,...)527 void SYNTAX(const char *fmt, ...)
528 {
529 extern char *cmdname, *curfname;
530 static int been_here = 0;
531 va_list varg;
532
533 if (been_here++ > 2)
534 return;
535 fprintf(stderr, "%s: ", cmdname);
536 va_start(varg, fmt);
537 vfprintf(stderr, fmt, varg);
538 va_end(varg);
539 fprintf(stderr, " at source line %d", lineno);
540 if (curfname != NULL)
541 fprintf(stderr, " in function %s", curfname);
542 if (compile_time == 1 && cursource() != NULL)
543 fprintf(stderr, " source file %s", cursource());
544 fprintf(stderr, "\n");
545 errorflag = 2;
546 eprint();
547 }
548
fpecatch(int n)549 void fpecatch(int n)
550 {
551 FATAL("floating point exception %d", n);
552 }
553
554 extern int bracecnt, brackcnt, parencnt;
555
bracecheck(void)556 void bracecheck(void)
557 {
558 int c;
559 static int beenhere = 0;
560
561 if (beenhere++)
562 return;
563 while ((c = input()) != EOF && c != '\0')
564 bclass(c);
565 bcheck2(bracecnt, '{', '}');
566 bcheck2(brackcnt, '[', ']');
567 bcheck2(parencnt, '(', ')');
568 }
569
bcheck2(int n,int c1,int c2)570 void bcheck2(int n, int c1, int c2)
571 {
572 if (n == 1)
573 fprintf(stderr, "\tmissing %c\n", c2);
574 else if (n > 1)
575 fprintf(stderr, "\t%d missing %c's\n", n, c2);
576 else if (n == -1)
577 fprintf(stderr, "\textra %c\n", c2);
578 else if (n < -1)
579 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
580 }
581
FATAL(const char * fmt,...)582 void FATAL(const char *fmt, ...)
583 {
584 extern char *cmdname;
585 va_list varg;
586
587 fflush(stdout);
588 fprintf(stderr, "%s: ", cmdname);
589 va_start(varg, fmt);
590 vfprintf(stderr, fmt, varg);
591 va_end(varg);
592 error();
593 if (dbg > 1) /* core dump if serious debugging on */
594 abort();
595 exit(2);
596 }
597
WARNING(const char * fmt,...)598 void WARNING(const char *fmt, ...)
599 {
600 extern char *cmdname;
601 va_list varg;
602
603 fflush(stdout);
604 fprintf(stderr, "%s: ", cmdname);
605 va_start(varg, fmt);
606 vfprintf(stderr, fmt, varg);
607 va_end(varg);
608 error();
609 }
610
error()611 void error()
612 {
613 extern Node *curnode;
614
615 fprintf(stderr, "\n");
616 if (compile_time != 2 && NR && *NR > 0) {
617 fprintf(stderr, " input record number %d", (int) (*FNR));
618 if (strcmp(*FILENAME, "-") != 0)
619 fprintf(stderr, ", file %s", *FILENAME);
620 fprintf(stderr, "\n");
621 }
622 if (compile_time != 2 && curnode)
623 fprintf(stderr, " source line number %d", curnode->lineno);
624 else if (compile_time != 2 && lineno)
625 fprintf(stderr, " source line number %d", lineno);
626 if (compile_time == 1 && cursource() != NULL)
627 fprintf(stderr, " source file %s", cursource());
628 fprintf(stderr, "\n");
629 eprint();
630 }
631
eprint(void)632 void eprint(void) /* try to print context around error */
633 {
634 char *p, *q;
635 int c;
636 static int been_here = 0;
637 extern char ebuf[], *ep;
638
639 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
640 return;
641 if (ebuf == ep)
642 return;
643 p = ep - 1;
644 if (p > ebuf && *p == '\n')
645 p--;
646 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
647 ;
648 while (*p == '\n')
649 p++;
650 fprintf(stderr, " context is\n\t");
651 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
652 ;
653 for ( ; p < q; p++)
654 if (*p)
655 putc(*p, stderr);
656 fprintf(stderr, " >>> ");
657 for ( ; p < ep; p++)
658 if (*p)
659 putc(*p, stderr);
660 fprintf(stderr, " <<< ");
661 if (*ep)
662 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
663 putc(c, stderr);
664 bclass(c);
665 }
666 putc('\n', stderr);
667 ep = ebuf;
668 }
669
bclass(int c)670 void bclass(int c)
671 {
672 switch (c) {
673 case '{': bracecnt++; break;
674 case '}': bracecnt--; break;
675 case '[': brackcnt++; break;
676 case ']': brackcnt--; break;
677 case '(': parencnt++; break;
678 case ')': parencnt--; break;
679 }
680 }
681
errcheck(double x,const char * s)682 double errcheck(double x, const char *s)
683 {
684
685 if (errno == EDOM) {
686 errno = 0;
687 WARNING("%s argument out of domain", s);
688 x = 1;
689 } else if (errno == ERANGE) {
690 errno = 0;
691 WARNING("%s result out of range", s);
692 x = 1;
693 }
694 return x;
695 }
696
isclvar(const char * s)697 int isclvar(const char *s) /* is s of form var=something ? */
698 {
699 const char *os = s;
700
701 if (!isalpha((uschar) *s) && *s != '_')
702 return 0;
703 for ( ; *s; s++)
704 if (!(isalnum((uschar) *s) || *s == '_'))
705 break;
706 return *s == '=' && s > os && *(s+1) != '=';
707 }
708
709 /* strtod is supposed to be a proper test of what's a valid number */
710 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
711 /* wrong: violates 4.10.1.4 of ansi C standard */
712
713 #include <math.h>
is_number(const char * s)714 int is_number(const char *s)
715 {
716 double r;
717 char *ep;
718 errno = 0;
719 r = strtod(s, &ep);
720 if (ep == s || r == HUGE_VAL || errno == ERANGE)
721 return 0;
722 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
723 ep++;
724 if (*ep == '\0')
725 return 1;
726 else
727 return 0;
728 }
729