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 <strings.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <limits.h>
34 #include <math.h>
35 #include "awk.h"
36
37 char EMPTY[] = { '\0' };
38 FILE *infile = NULL;
39 bool innew; /* true = infile has not been read by readrec */
40 char *file = EMPTY;
41 char *record;
42 int recsize = RECSIZE;
43 char *fields;
44 int fieldssize = RECSIZE;
45
46 Cell **fldtab; /* pointers to Cells */
47 static size_t len_inputFS = 0;
48 static char *inputFS = NULL; /* FS at time of input, for field splitting */
49
50 #define MAXFLD 2
51 int nfields = MAXFLD; /* last allocated slot for $i */
52
53 bool donefld; /* true = implies rec broken into fields */
54 bool donerec; /* true = record is valid (no flds have changed) */
55
56 int lastfld = 0; /* last used field */
57 int argno = 1; /* current input argument number */
58 extern Awkfloat *ARGC;
59
60 static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
61 static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
62
recinit(unsigned int n)63 void recinit(unsigned int n)
64 {
65 if ( (record = (char *) malloc(n)) == NULL
66 || (fields = (char *) malloc(n+1)) == NULL
67 || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL
68 || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL)
69 FATAL("out of space for $0 and fields");
70 *record = '\0';
71 *fldtab[0] = dollar0;
72 fldtab[0]->sval = record;
73 fldtab[0]->nval = tostring("0");
74 makefields(1, nfields);
75 }
76
makefields(int n1,int n2)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(**fldtab));
84 if (fldtab[i] == NULL)
85 FATAL("out of space in makefields %d", i);
86 *fldtab[i] = dollar1;
87 snprintf(temp, sizeof(temp), "%d", i);
88 fldtab[i]->nval = tostring(temp);
89 }
90 }
91
initgetrec(void)92 void initgetrec(void)
93 {
94 int i;
95 char *p;
96
97 for (i = 1; i < *ARGC; i++) {
98 p = getargv(i); /* find 1st real filename */
99 if (p == NULL || *p == '\0') { /* deleted or zapped */
100 argno++;
101 continue;
102 }
103 if (!isclvar(p)) {
104 setsval(lookup("FILENAME", symtab), p);
105 return;
106 }
107 setclvar(p); /* a commandline assignment before filename */
108 argno++;
109 }
110 infile = stdin; /* no filenames, so use stdin */
111 innew = true;
112 }
113
114 /*
115 * POSIX specifies that fields are supposed to be evaluated as if they were
116 * split using the value of FS at the time that the record's value ($0) was
117 * read.
118 *
119 * Since field-splitting is done lazily, we save the current value of FS
120 * whenever a new record is read in (implicitly or via getline), or when
121 * a new value is assigned to $0.
122 */
savefs(void)123 void savefs(void)
124 {
125 size_t len;
126 if ((len = strlen(getsval(fsloc))) < len_inputFS) {
127 strcpy(inputFS, *FS); /* for subsequent field splitting */
128 return;
129 }
130
131 len_inputFS = len + 1;
132 inputFS = (char *) realloc(inputFS, len_inputFS);
133 if (inputFS == NULL)
134 FATAL("field separator %.10s... is too long", *FS);
135 memcpy(inputFS, *FS, len_inputFS);
136 }
137
138 static bool firsttime = true;
139
getrec(char ** pbuf,int * pbufsize,bool isrecord)140 int getrec(char **pbuf, int *pbufsize, bool isrecord) /* get next input record */
141 { /* note: cares whether buf == record */
142 int c;
143 char *buf = *pbuf;
144 uschar saveb0;
145 int bufsize = *pbufsize, savebufsize = bufsize;
146
147 if (firsttime) {
148 firsttime = false;
149 initgetrec();
150 }
151 DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
152 *RS, *FS, *ARGC, *FILENAME);
153 if (isrecord) {
154 donefld = false;
155 donerec = true;
156 savefs();
157 }
158 saveb0 = buf[0];
159 buf[0] = 0;
160 while (argno < *ARGC || infile == stdin) {
161 DPRINTF("argno=%d, file=|%s|\n", argno, file);
162 if (infile == NULL) { /* have to open a new file */
163 file = getargv(argno);
164 if (file == NULL || *file == '\0') { /* deleted or zapped */
165 argno++;
166 continue;
167 }
168 if (isclvar(file)) { /* a var=value arg */
169 setclvar(file);
170 argno++;
171 continue;
172 }
173 *FILENAME = file;
174 DPRINTF("opening file %s\n", file);
175 if (*file == '-' && *(file+1) == '\0')
176 infile = stdin;
177 else if ((infile = fopen(file, "r")) == NULL)
178 FATAL("can't open file %s", file);
179 setfval(fnrloc, 0.0);
180 }
181 c = readrec(&buf, &bufsize, infile, innew);
182 if (innew)
183 innew = false;
184 if (c != 0 || buf[0] != '\0') { /* normal record */
185 if (isrecord) {
186 double result;
187
188 if (freeable(fldtab[0]))
189 xfree(fldtab[0]->sval);
190 fldtab[0]->sval = buf; /* buf == record */
191 fldtab[0]->tval = REC | STR | DONTFREE;
192 if (is_number(fldtab[0]->sval, & result)) {
193 fldtab[0]->fval = result;
194 fldtab[0]->tval |= NUM;
195 }
196 }
197 setfval(nrloc, nrloc->fval+1);
198 setfval(fnrloc, fnrloc->fval+1);
199 *pbuf = buf;
200 *pbufsize = bufsize;
201 return 1;
202 }
203 /* EOF arrived on this file; set up next */
204 if (infile != stdin)
205 fclose(infile);
206 infile = NULL;
207 argno++;
208 }
209 buf[0] = saveb0;
210 *pbuf = buf;
211 *pbufsize = savebufsize;
212 return 0; /* true end of file */
213 }
214
nextfile(void)215 void nextfile(void)
216 {
217 if (infile != NULL && infile != stdin)
218 fclose(infile);
219 infile = NULL;
220 argno++;
221 }
222
readrec(char ** pbuf,int * pbufsize,FILE * inf,bool newflag)223 int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* read one record into buf */
224 {
225 int sep, c, isrec;
226 char *rr, *buf = *pbuf;
227 int bufsize = *pbufsize;
228 char *rs = getsval(rsloc);
229
230 if (*rs && rs[1]) {
231 bool found;
232
233 fa *pfa = makedfa(rs, 1);
234 if (newflag)
235 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
236 else {
237 int tempstat = pfa->initstat;
238 pfa->initstat = 2;
239 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
240 pfa->initstat = tempstat;
241 }
242 if (found)
243 setptr(patbeg, '\0');
244 } else {
245 if ((sep = *rs) == 0) {
246 sep = '\n';
247 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
248 ;
249 if (c != EOF)
250 ungetc(c, inf);
251 }
252 for (rr = buf; ; ) {
253 for (; (c=getc(inf)) != sep && c != EOF; ) {
254 if (rr-buf+1 > bufsize)
255 if (!adjbuf(&buf, &bufsize, 1+rr-buf,
256 recsize, &rr, "readrec 1"))
257 FATAL("input record `%.30s...' too long", buf);
258 *rr++ = c;
259 }
260 if (*rs == sep || c == EOF)
261 break;
262 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
263 break;
264 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
265 "readrec 2"))
266 FATAL("input record `%.30s...' too long", buf);
267 *rr++ = '\n';
268 *rr++ = c;
269 }
270 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
271 FATAL("input record `%.30s...' too long", buf);
272 *rr = 0;
273 }
274 *pbuf = buf;
275 *pbufsize = bufsize;
276 isrec = *buf || !feof(inf);
277 DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
278 return isrec;
279 }
280
getargv(int n)281 char *getargv(int n) /* get ARGV[n] */
282 {
283 Cell *x;
284 char *s, temp[50];
285 extern Array *ARGVtab;
286
287 snprintf(temp, sizeof(temp), "%d", n);
288 if (lookup(temp, ARGVtab) == NULL)
289 return NULL;
290 x = setsymtab(temp, "", 0.0, STR, ARGVtab);
291 s = getsval(x);
292 DPRINTF("getargv(%d) returns |%s|\n", n, s);
293 return s;
294 }
295
setclvar(char * s)296 void setclvar(char *s) /* set var=value from s */
297 {
298 char *p;
299 Cell *q;
300 double result;
301
302 for (p=s; *p != '='; p++)
303 ;
304 *p++ = 0;
305 p = qstring(p, '\0');
306 q = setsymtab(s, p, 0.0, STR, symtab);
307 setsval(q, p);
308 if (is_number(q->sval, & result)) {
309 q->fval = result;
310 q->tval |= NUM;
311 }
312 DPRINTF("command line set %s to |%s|\n", s, p);
313 }
314
315
fldbld(void)316 void fldbld(void) /* create fields from current record */
317 {
318 /* this relies on having fields[] the same length as $0 */
319 /* the fields are all stored in this one array with \0's */
320 /* possibly with a final trailing \0 not associated with any field */
321 char *r, *fr, sep;
322 Cell *p;
323 int i, j, n;
324
325 if (donefld)
326 return;
327 if (!isstr(fldtab[0]))
328 getsval(fldtab[0]);
329 r = fldtab[0]->sval;
330 n = strlen(r);
331 if (n > fieldssize) {
332 xfree(fields);
333 if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
334 FATAL("out of space for fields in fldbld %d", n);
335 fieldssize = n;
336 }
337 fr = fields;
338 i = 0; /* number of fields accumulated here */
339 if (inputFS == NULL) /* make sure we have a copy of FS */
340 savefs();
341 if (strlen(inputFS) > 1) { /* it's a regular expression */
342 i = refldbld(r, inputFS);
343 } else if ((sep = *inputFS) == ' ') { /* default whitespace */
344 for (i = 0; ; ) {
345 while (*r == ' ' || *r == '\t' || *r == '\n')
346 r++;
347 if (*r == 0)
348 break;
349 i++;
350 if (i > nfields)
351 growfldtab(i);
352 if (freeable(fldtab[i]))
353 xfree(fldtab[i]->sval);
354 fldtab[i]->sval = fr;
355 fldtab[i]->tval = FLD | STR | DONTFREE;
356 do
357 *fr++ = *r++;
358 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
359 *fr++ = 0;
360 }
361 *fr = 0;
362 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
363 for (i = 0; *r != '\0'; r += n) {
364 char buf[MB_LEN_MAX + 1];
365
366 i++;
367 if (i > nfields)
368 growfldtab(i);
369 if (freeable(fldtab[i]))
370 xfree(fldtab[i]->sval);
371 n = mblen(r, MB_LEN_MAX);
372 if (n < 0)
373 n = 1;
374 memcpy(buf, r, n);
375 buf[n] = '\0';
376 fldtab[i]->sval = tostring(buf);
377 fldtab[i]->tval = FLD | STR;
378 }
379 *fr = 0;
380 } else if (*r != 0) { /* if 0, it's a null field */
381 /* subtlecase : if length(FS) == 1 && length(RS > 0)
382 * \n is NOT a field separator (cf awk book 61,84).
383 * this variable is tested in the inner while loop.
384 */
385 int rtest = '\n'; /* normal case */
386 if (strlen(*RS) > 0)
387 rtest = '\0';
388 for (;;) {
389 i++;
390 if (i > nfields)
391 growfldtab(i);
392 if (freeable(fldtab[i]))
393 xfree(fldtab[i]->sval);
394 fldtab[i]->sval = fr;
395 fldtab[i]->tval = FLD | STR | DONTFREE;
396 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
397 *fr++ = *r++;
398 *fr++ = 0;
399 if (*r++ == 0)
400 break;
401 }
402 *fr = 0;
403 }
404 if (i > nfields)
405 FATAL("record `%.30s...' has too many fields; can't happen", r);
406 cleanfld(i+1, lastfld); /* clean out junk from previous record */
407 lastfld = i;
408 donefld = true;
409 for (j = 1; j <= lastfld; j++) {
410 double result;
411
412 p = fldtab[j];
413 if(is_number(p->sval, & result)) {
414 p->fval = result;
415 p->tval |= NUM;
416 }
417 }
418 setfval(nfloc, (Awkfloat) lastfld);
419 donerec = true; /* restore */
420 if (dbg) {
421 for (j = 0; j <= lastfld; j++) {
422 p = fldtab[j];
423 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
424 }
425 }
426 }
427
cleanfld(int n1,int n2)428 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
429 { /* nvals remain intact */
430 Cell *p;
431 int i;
432
433 for (i = n1; i <= n2; i++) {
434 p = fldtab[i];
435 if (freeable(p))
436 xfree(p->sval);
437 p->sval = EMPTY,
438 p->tval = FLD | STR | DONTFREE;
439 }
440 }
441
newfld(int n)442 void newfld(int n) /* add field n after end of existing lastfld */
443 {
444 if (n > nfields)
445 growfldtab(n);
446 cleanfld(lastfld+1, n);
447 lastfld = n;
448 setfval(nfloc, (Awkfloat) n);
449 }
450
setlastfld(int n)451 void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
452 {
453 if (n < 0)
454 FATAL("cannot set NF to a negative value");
455 if (n > nfields)
456 growfldtab(n);
457
458 if (lastfld < n)
459 cleanfld(lastfld+1, n);
460 else
461 cleanfld(n+1, lastfld);
462
463 lastfld = n;
464 }
465
fieldadr(int n)466 Cell *fieldadr(int n) /* get nth field */
467 {
468 if (n < 0)
469 FATAL("trying to access out of range field %d", n);
470 if (n > nfields) /* fields after NF are empty */
471 growfldtab(n); /* but does not increase NF */
472 return(fldtab[n]);
473 }
474
growfldtab(int n)475 void growfldtab(int n) /* make new fields up to at least $n */
476 {
477 int nf = 2 * nfields;
478 size_t s;
479
480 if (n > nf)
481 nf = n;
482 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
483 if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
484 fldtab = (Cell **) realloc(fldtab, s);
485 else /* overflow sizeof int */
486 xfree(fldtab); /* make it null */
487 if (fldtab == NULL)
488 FATAL("out of space creating %d fields", nf);
489 makefields(nfields+1, nf);
490 nfields = nf;
491 }
492
refldbld(const char * rec,const char * fs)493 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
494 {
495 /* this relies on having fields[] the same length as $0 */
496 /* the fields are all stored in this one array with \0's */
497 char *fr;
498 int i, tempstat, n;
499 fa *pfa;
500
501 n = strlen(rec);
502 if (n > fieldssize) {
503 xfree(fields);
504 if ((fields = (char *) malloc(n+1)) == NULL)
505 FATAL("out of space for fields in refldbld %d", n);
506 fieldssize = n;
507 }
508 fr = fields;
509 *fr = '\0';
510 if (*rec == '\0')
511 return 0;
512 pfa = makedfa(fs, 1);
513 DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
514 tempstat = pfa->initstat;
515 for (i = 1; ; i++) {
516 if (i > nfields)
517 growfldtab(i);
518 if (freeable(fldtab[i]))
519 xfree(fldtab[i]->sval);
520 fldtab[i]->tval = FLD | STR | DONTFREE;
521 fldtab[i]->sval = fr;
522 DPRINTF("refldbld: i=%d\n", i);
523 if (nematch(pfa, rec)) {
524 pfa->initstat = 2; /* horrible coupling to b.c */
525 DPRINTF("match %s (%d chars)\n", patbeg, patlen);
526 strncpy(fr, rec, patbeg-rec);
527 fr += patbeg - rec + 1;
528 *(fr-1) = '\0';
529 rec = patbeg + patlen;
530 } else {
531 DPRINTF("no match %s\n", rec);
532 strcpy(fr, rec);
533 pfa->initstat = tempstat;
534 break;
535 }
536 }
537 return i;
538 }
539
recbld(void)540 void recbld(void) /* create $0 from $1..$NF if necessary */
541 {
542 int i;
543 char *r, *p;
544 char *sep = getsval(ofsloc);
545
546 if (donerec)
547 return;
548 r = record;
549 for (i = 1; i <= *NF; i++) {
550 p = getsval(fldtab[i]);
551 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
552 FATAL("created $0 `%.30s...' too long", record);
553 while ((*r = *p++) != 0)
554 r++;
555 if (i < *NF) {
556 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
557 FATAL("created $0 `%.30s...' too long", record);
558 for (p = sep; (*r = *p++) != 0; )
559 r++;
560 }
561 }
562 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
563 FATAL("built giant record `%.30s...'", record);
564 *r = '\0';
565 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
566
567 if (freeable(fldtab[0]))
568 xfree(fldtab[0]->sval);
569 fldtab[0]->tval = REC | STR | DONTFREE;
570 fldtab[0]->sval = record;
571
572 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
573 DPRINTF("recbld = |%s|\n", record);
574 donerec = true;
575 }
576
577 int errorflag = 0;
578
yyerror(const char * s)579 void yyerror(const char *s)
580 {
581 SYNTAX("%s", s);
582 }
583
SYNTAX(const char * fmt,...)584 void SYNTAX(const char *fmt, ...)
585 {
586 extern char *cmdname, *curfname;
587 static int been_here = 0;
588 va_list varg;
589
590 if (been_here++ > 2)
591 return;
592 fprintf(stderr, "%s: ", cmdname);
593 va_start(varg, fmt);
594 vfprintf(stderr, fmt, varg);
595 va_end(varg);
596 fprintf(stderr, " at source line %d", lineno);
597 if (curfname != NULL)
598 fprintf(stderr, " in function %s", curfname);
599 if (compile_time == COMPILING && cursource() != NULL)
600 fprintf(stderr, " source file %s", cursource());
601 fprintf(stderr, "\n");
602 errorflag = 2;
603 eprint();
604 }
605
606 extern int bracecnt, brackcnt, parencnt;
607
bracecheck(void)608 void bracecheck(void)
609 {
610 int c;
611 static int beenhere = 0;
612
613 if (beenhere++)
614 return;
615 while ((c = input()) != EOF && c != '\0')
616 bclass(c);
617 bcheck2(bracecnt, '{', '}');
618 bcheck2(brackcnt, '[', ']');
619 bcheck2(parencnt, '(', ')');
620 }
621
bcheck2(int n,int c1,int c2)622 void bcheck2(int n, int c1, int c2)
623 {
624 if (n == 1)
625 fprintf(stderr, "\tmissing %c\n", c2);
626 else if (n > 1)
627 fprintf(stderr, "\t%d missing %c's\n", n, c2);
628 else if (n == -1)
629 fprintf(stderr, "\textra %c\n", c2);
630 else if (n < -1)
631 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
632 }
633
FATAL(const char * fmt,...)634 void FATAL(const char *fmt, ...)
635 {
636 extern char *cmdname;
637 va_list varg;
638
639 fflush(stdout);
640 fprintf(stderr, "%s: ", cmdname);
641 va_start(varg, fmt);
642 vfprintf(stderr, fmt, varg);
643 va_end(varg);
644 error();
645 if (dbg > 1) /* core dump if serious debugging on */
646 abort();
647 exit(2);
648 }
649
WARNING(const char * fmt,...)650 void WARNING(const char *fmt, ...)
651 {
652 extern char *cmdname;
653 va_list varg;
654
655 fflush(stdout);
656 fprintf(stderr, "%s: ", cmdname);
657 va_start(varg, fmt);
658 vfprintf(stderr, fmt, varg);
659 va_end(varg);
660 error();
661 }
662
error()663 void error()
664 {
665 extern Node *curnode;
666
667 fprintf(stderr, "\n");
668 if (compile_time != ERROR_PRINTING) {
669 if (NR && *NR > 0) {
670 fprintf(stderr, " input record number %d", (int) (*FNR));
671 if (strcmp(*FILENAME, "-") != 0)
672 fprintf(stderr, ", file %s", *FILENAME);
673 fprintf(stderr, "\n");
674 }
675 if (curnode)
676 fprintf(stderr, " source line number %d", curnode->lineno);
677 else if (lineno)
678 fprintf(stderr, " source line number %d", lineno);
679 if (compile_time == COMPILING && cursource() != NULL)
680 fprintf(stderr, " source file %s", cursource());
681 fprintf(stderr, "\n");
682 eprint();
683 }
684 }
685
eprint(void)686 void eprint(void) /* try to print context around error */
687 {
688 char *p, *q;
689 int c;
690 static int been_here = 0;
691 extern char ebuf[], *ep;
692
693 if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
694 return;
695 if (ebuf == ep)
696 return;
697 p = ep - 1;
698 if (p > ebuf && *p == '\n')
699 p--;
700 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
701 ;
702 while (*p == '\n')
703 p++;
704 fprintf(stderr, " context is\n\t");
705 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
706 ;
707 for ( ; p < q; p++)
708 if (*p)
709 putc(*p, stderr);
710 fprintf(stderr, " >>> ");
711 for ( ; p < ep; p++)
712 if (*p)
713 putc(*p, stderr);
714 fprintf(stderr, " <<< ");
715 if (*ep)
716 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
717 putc(c, stderr);
718 bclass(c);
719 }
720 putc('\n', stderr);
721 ep = ebuf;
722 }
723
bclass(int c)724 void bclass(int c)
725 {
726 switch (c) {
727 case '{': bracecnt++; break;
728 case '}': bracecnt--; break;
729 case '[': brackcnt++; break;
730 case ']': brackcnt--; break;
731 case '(': parencnt++; break;
732 case ')': parencnt--; break;
733 }
734 }
735
errcheck(double x,const char * s)736 double errcheck(double x, const char *s)
737 {
738
739 if (errno == EDOM) {
740 errno = 0;
741 WARNING("%s argument out of domain", s);
742 x = 1;
743 } else if (errno == ERANGE) {
744 errno = 0;
745 WARNING("%s result out of range", s);
746 x = 1;
747 }
748 return x;
749 }
750
isclvar(const char * s)751 int isclvar(const char *s) /* is s of form var=something ? */
752 {
753 const char *os = s;
754
755 if (!isalpha((uschar) *s) && *s != '_')
756 return 0;
757 for ( ; *s; s++)
758 if (!(isalnum((uschar) *s) || *s == '_'))
759 break;
760 return *s == '=' && s > os;
761 }
762
763 /* strtod is supposed to be a proper test of what's a valid number */
764 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
765 /* wrong: violates 4.10.1.4 of ansi C standard */
766
767 /* well, not quite. As of C99, hex floating point is allowed. so this is
768 * a bit of a mess. We work around the mess by checking for a hexadecimal
769 * value and disallowing it. Similarly, we now follow gawk and allow only
770 * +nan, -nan, +inf, and -inf for NaN and infinity values.
771 */
772
773 /*
774 * This routine now has a more complicated interface, the main point
775 * being to avoid the double conversion of a string to double, and
776 * also to convey out, if requested, the information that the numeric
777 * value was a leading string or is all of the string. The latter bit
778 * is used in getfval().
779 */
780
is_valid_number(const char * s,bool trailing_stuff_ok,bool * no_trailing,double * result)781 bool is_valid_number(const char *s, bool trailing_stuff_ok,
782 bool *no_trailing, double *result)
783 {
784 double r;
785 char *ep;
786 bool retval = false;
787 bool is_nan = false;
788 bool is_inf = false;
789
790 if (no_trailing)
791 *no_trailing = false;
792
793 while (isspace(*s))
794 s++;
795
796 // no hex floating point, sorry
797 if (s[0] == '0' && tolower(s[1]) == 'x')
798 return false;
799
800 // allow +nan, -nan, +inf, -inf, any other letter, no
801 if (s[0] == '+' || s[0] == '-') {
802 is_nan = (strncasecmp(s+1, "nan", 3) == 0);
803 is_inf = (strncasecmp(s+1, "inf", 3) == 0);
804 if ((is_nan || is_inf)
805 && (isspace(s[4]) || s[4] == '\0'))
806 goto convert;
807 else if (! isdigit(s[1]) && s[1] != '.')
808 return false;
809 }
810 else if (! isdigit(s[0]) && s[0] != '.')
811 return false;
812
813 convert:
814 errno = 0;
815 r = strtod(s, &ep);
816 if (ep == s || errno == ERANGE)
817 return false;
818
819 if (isnan(r) && s[0] == '-' && signbit(r) == 0)
820 r = -r;
821
822 if (result != NULL)
823 *result = r;
824
825 /*
826 * check for trailing stuff
827 */
828 while (isspace(*ep))
829 ep++;
830
831 if (no_trailing != NULL)
832 *no_trailing = (*ep == '\0');
833
834 // return true if found the end, or trailing stuff is allowed
835 retval = *ep == '\0' || trailing_stuff_ok;
836
837 return retval;
838 }
839