blob: f9cb6ca2a7a902dbd8d8394d5112c000c1cdc9a9 [file] [log] [blame]
Brian Kernighan87b94932012-12-22 10:35:39 -05001/****************************************************************
2Copyright (C) Lucent Technologies 1997
3All Rights Reserved
4
5Permission to use, copy, modify, and distribute this software and
6its documentation for any purpose and without fee is hereby
7granted, provided that the above copyright notice appear in all
8copies and that both that the copyright notice and this
9permission notice and warranty disclaimer appear in supporting
10documentation, and that the name Lucent Technologies or any of
11its entities not be used in advertising or publicity pertaining
12to distribution of the software without specific, written prior
13permission.
14
15LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22THIS 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
35FILE *infile = NULL;
36char *file = "";
37char *record;
38int recsize = RECSIZE;
39char *fields;
40int fieldssize = RECSIZE;
41
42Cell **fldtab; /* pointers to Cells */
43char inputFS[100] = " ";
44
45#define MAXFLD 2
46int nfields = MAXFLD; /* last allocated slot for $i */
47
48int donefld; /* 1 = implies rec broken into fields */
49int donerec; /* 1 = record is valid (no flds have changed) */
50
51int lastfld = 0; /* last used field */
52int argno = 1; /* current input argument number */
53extern Awkfloat *ARGC;
54
55static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
56static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
57
58void recinit(unsigned int n)
59{
60 if ( (record = (char *) malloc(n)) == NULL
61 || (fields = (char *) malloc(n+1)) == NULL
Cody Peter Mello75802352018-11-12 10:34:19 -080062 || (fldtab = (Cell **) malloc((nfields+2) * sizeof(Cell *))) == NULL
Brian Kernighan87b94932012-12-22 10:35:39 -050063 || (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
71void 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
86void 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
107static int firsttime = 1;
108
109int 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
179void nextfile(void)
180{
181 if (infile != NULL && infile != stdin)
182 fclose(infile);
183 infile = NULL;
184 argno++;
185}
186
187int 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;
Cody Peter Mello52566c02018-09-18 15:45:55 -0700192 char *rs = getsval(rsloc);
Brian Kernighan87b94932012-12-22 10:35:39 -0500193
Cody Peter Mello52566c02018-09-18 15:45:55 -0700194 if (strlen(getsval(fsloc)) >= sizeof (inputFS))
Brian Kernighan87b94932012-12-22 10:35:39 -0500195 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 */
Cody Peter Mello52566c02018-09-18 15:45:55 -0700198 if ((sep = *rs) == 0) {
Brian Kernighan87b94932012-12-22 10:35:39 -0500199 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 }
Cody Peter Mello52566c02018-09-18 15:45:55 -0700212 if (*rs == sep || c == EOF)
Brian Kernighan87b94932012-12-22 10:35:39 -0500213 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
230char *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
245void 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
264void 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 */
Cody Peter Mello52566c02018-09-18 15:45:55 -0700287 if (strlen(getsval(fsloc)) >= sizeof (inputFS))
288 FATAL("field separator %.10s... is too long", *FS);
Brian Kernighan87b94932012-12-22 10:35:39 -0500289 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);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300362 donerec = 1; /* restore */
Brian Kernighan87b94932012-12-22 10:35:39 -0500363 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
371void 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
385void 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
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300394void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
395{
Cody Peter Mello179536a2018-09-25 21:19:49 -0700396 if (n < 0)
397 FATAL("cannot set NF to a negative value");
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300398 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
Brian Kernighan87b94932012-12-22 10:35:39 -0500409Cell *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
418void 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
436int 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
483void recbld(void) /* create $0 from $1..$NF if necessary */
484{
485 int i;
486 char *r, *p;
Cody Peter Mello52566c02018-09-18 15:45:55 -0700487 char *sep = getsval(ofsloc);
Brian Kernighan87b94932012-12-22 10:35:39 -0500488
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) {
Cody Peter Mello52566c02018-09-18 15:45:55 -0700499 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
Brian Kernighan87b94932012-12-22 10:35:39 -0500500 FATAL("created $0 `%.30s...' too long", record);
Cody Peter Mello52566c02018-09-18 15:45:55 -0700501 for (p = sep; (*r = *p++) != 0; )
Brian Kernighan87b94932012-12-22 10:35:39 -0500502 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
520int errorflag = 0;
521
522void yyerror(const char *s)
523{
524 SYNTAX("%s", s);
525}
526
527void 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
549void fpecatch(int n)
550{
551 FATAL("floating point exception %d", n);
552}
553
554extern int bracecnt, brackcnt, parencnt;
555
556void 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
570void 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
582void 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
598void 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
611void 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
632void 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;
Brian Kernighan3ed9e242018-08-15 10:45:03 -0400641 if (ebuf == ep)
642 return;
Brian Kernighan87b94932012-12-22 10:35:39 -0500643 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
670void 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
682double 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
697int 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>
714int 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}