blob: 4b1527ea05a1910ba2fe23f9071da38812b71d7c [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
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
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{
396 if (n > nfields)
397 growfldtab(n);
398
399 if (lastfld < n)
400 cleanfld(lastfld+1, n);
401 else
402 cleanfld(n+1, lastfld);
403
404 lastfld = n;
405}
406
Brian Kernighan87b94932012-12-22 10:35:39 -0500407Cell *fieldadr(int n) /* get nth field */
408{
409 if (n < 0)
410 FATAL("trying to access out of range field %d", n);
411 if (n > nfields) /* fields after NF are empty */
412 growfldtab(n); /* but does not increase NF */
413 return(fldtab[n]);
414}
415
416void growfldtab(int n) /* make new fields up to at least $n */
417{
418 int nf = 2 * nfields;
419 size_t s;
420
421 if (n > nf)
422 nf = n;
423 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
424 if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
425 fldtab = (Cell **) realloc(fldtab, s);
426 else /* overflow sizeof int */
427 xfree(fldtab); /* make it null */
428 if (fldtab == NULL)
429 FATAL("out of space creating %d fields", nf);
430 makefields(nfields+1, nf);
431 nfields = nf;
432}
433
434int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
435{
436 /* this relies on having fields[] the same length as $0 */
437 /* the fields are all stored in this one array with \0's */
438 char *fr;
439 int i, tempstat, n;
440 fa *pfa;
441
442 n = strlen(rec);
443 if (n > fieldssize) {
444 xfree(fields);
445 if ((fields = (char *) malloc(n+1)) == NULL)
446 FATAL("out of space for fields in refldbld %d", n);
447 fieldssize = n;
448 }
449 fr = fields;
450 *fr = '\0';
451 if (*rec == '\0')
452 return 0;
453 pfa = makedfa(fs, 1);
454 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
455 tempstat = pfa->initstat;
456 for (i = 1; ; i++) {
457 if (i > nfields)
458 growfldtab(i);
459 if (freeable(fldtab[i]))
460 xfree(fldtab[i]->sval);
461 fldtab[i]->tval = FLD | STR | DONTFREE;
462 fldtab[i]->sval = fr;
463 dprintf( ("refldbld: i=%d\n", i) );
464 if (nematch(pfa, rec)) {
465 pfa->initstat = 2; /* horrible coupling to b.c */
466 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
467 strncpy(fr, rec, patbeg-rec);
468 fr += patbeg - rec + 1;
469 *(fr-1) = '\0';
470 rec = patbeg + patlen;
471 } else {
472 dprintf( ("no match %s\n", rec) );
473 strcpy(fr, rec);
474 pfa->initstat = tempstat;
475 break;
476 }
477 }
478 return i;
479}
480
481void recbld(void) /* create $0 from $1..$NF if necessary */
482{
483 int i;
484 char *r, *p;
Cody Peter Mello52566c02018-09-18 15:45:55 -0700485 char *sep = getsval(ofsloc);
Brian Kernighan87b94932012-12-22 10:35:39 -0500486
487 if (donerec == 1)
488 return;
489 r = record;
490 for (i = 1; i <= *NF; i++) {
491 p = getsval(fldtab[i]);
492 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
493 FATAL("created $0 `%.30s...' too long", record);
494 while ((*r = *p++) != 0)
495 r++;
496 if (i < *NF) {
Cody Peter Mello52566c02018-09-18 15:45:55 -0700497 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
Brian Kernighan87b94932012-12-22 10:35:39 -0500498 FATAL("created $0 `%.30s...' too long", record);
Cody Peter Mello52566c02018-09-18 15:45:55 -0700499 for (p = sep; (*r = *p++) != 0; )
Brian Kernighan87b94932012-12-22 10:35:39 -0500500 r++;
501 }
502 }
503 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
504 FATAL("built giant record `%.30s...'", record);
505 *r = '\0';
506 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
507
508 if (freeable(fldtab[0]))
509 xfree(fldtab[0]->sval);
510 fldtab[0]->tval = REC | STR | DONTFREE;
511 fldtab[0]->sval = record;
512
513 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
514 dprintf( ("recbld = |%s|\n", record) );
515 donerec = 1;
516}
517
518int errorflag = 0;
519
520void yyerror(const char *s)
521{
522 SYNTAX("%s", s);
523}
524
525void SYNTAX(const char *fmt, ...)
526{
527 extern char *cmdname, *curfname;
528 static int been_here = 0;
529 va_list varg;
530
531 if (been_here++ > 2)
532 return;
533 fprintf(stderr, "%s: ", cmdname);
534 va_start(varg, fmt);
535 vfprintf(stderr, fmt, varg);
536 va_end(varg);
537 fprintf(stderr, " at source line %d", lineno);
538 if (curfname != NULL)
539 fprintf(stderr, " in function %s", curfname);
540 if (compile_time == 1 && cursource() != NULL)
541 fprintf(stderr, " source file %s", cursource());
542 fprintf(stderr, "\n");
543 errorflag = 2;
544 eprint();
545}
546
547void fpecatch(int n)
548{
549 FATAL("floating point exception %d", n);
550}
551
552extern int bracecnt, brackcnt, parencnt;
553
554void bracecheck(void)
555{
556 int c;
557 static int beenhere = 0;
558
559 if (beenhere++)
560 return;
561 while ((c = input()) != EOF && c != '\0')
562 bclass(c);
563 bcheck2(bracecnt, '{', '}');
564 bcheck2(brackcnt, '[', ']');
565 bcheck2(parencnt, '(', ')');
566}
567
568void bcheck2(int n, int c1, int c2)
569{
570 if (n == 1)
571 fprintf(stderr, "\tmissing %c\n", c2);
572 else if (n > 1)
573 fprintf(stderr, "\t%d missing %c's\n", n, c2);
574 else if (n == -1)
575 fprintf(stderr, "\textra %c\n", c2);
576 else if (n < -1)
577 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
578}
579
580void FATAL(const char *fmt, ...)
581{
582 extern char *cmdname;
583 va_list varg;
584
585 fflush(stdout);
586 fprintf(stderr, "%s: ", cmdname);
587 va_start(varg, fmt);
588 vfprintf(stderr, fmt, varg);
589 va_end(varg);
590 error();
591 if (dbg > 1) /* core dump if serious debugging on */
592 abort();
593 exit(2);
594}
595
596void WARNING(const char *fmt, ...)
597{
598 extern char *cmdname;
599 va_list varg;
600
601 fflush(stdout);
602 fprintf(stderr, "%s: ", cmdname);
603 va_start(varg, fmt);
604 vfprintf(stderr, fmt, varg);
605 va_end(varg);
606 error();
607}
608
609void error()
610{
611 extern Node *curnode;
612
613 fprintf(stderr, "\n");
614 if (compile_time != 2 && NR && *NR > 0) {
615 fprintf(stderr, " input record number %d", (int) (*FNR));
616 if (strcmp(*FILENAME, "-") != 0)
617 fprintf(stderr, ", file %s", *FILENAME);
618 fprintf(stderr, "\n");
619 }
620 if (compile_time != 2 && curnode)
621 fprintf(stderr, " source line number %d", curnode->lineno);
622 else if (compile_time != 2 && lineno)
623 fprintf(stderr, " source line number %d", lineno);
624 if (compile_time == 1 && cursource() != NULL)
625 fprintf(stderr, " source file %s", cursource());
626 fprintf(stderr, "\n");
627 eprint();
628}
629
630void eprint(void) /* try to print context around error */
631{
632 char *p, *q;
633 int c;
634 static int been_here = 0;
635 extern char ebuf[], *ep;
636
637 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
638 return;
Brian Kernighan3ed9e242018-08-15 10:45:03 -0400639 if (ebuf == ep)
640 return;
Brian Kernighan87b94932012-12-22 10:35:39 -0500641 p = ep - 1;
642 if (p > ebuf && *p == '\n')
643 p--;
644 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
645 ;
646 while (*p == '\n')
647 p++;
648 fprintf(stderr, " context is\n\t");
649 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
650 ;
651 for ( ; p < q; p++)
652 if (*p)
653 putc(*p, stderr);
654 fprintf(stderr, " >>> ");
655 for ( ; p < ep; p++)
656 if (*p)
657 putc(*p, stderr);
658 fprintf(stderr, " <<< ");
659 if (*ep)
660 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
661 putc(c, stderr);
662 bclass(c);
663 }
664 putc('\n', stderr);
665 ep = ebuf;
666}
667
668void bclass(int c)
669{
670 switch (c) {
671 case '{': bracecnt++; break;
672 case '}': bracecnt--; break;
673 case '[': brackcnt++; break;
674 case ']': brackcnt--; break;
675 case '(': parencnt++; break;
676 case ')': parencnt--; break;
677 }
678}
679
680double errcheck(double x, const char *s)
681{
682
683 if (errno == EDOM) {
684 errno = 0;
685 WARNING("%s argument out of domain", s);
686 x = 1;
687 } else if (errno == ERANGE) {
688 errno = 0;
689 WARNING("%s result out of range", s);
690 x = 1;
691 }
692 return x;
693}
694
695int isclvar(const char *s) /* is s of form var=something ? */
696{
697 const char *os = s;
698
699 if (!isalpha((uschar) *s) && *s != '_')
700 return 0;
701 for ( ; *s; s++)
702 if (!(isalnum((uschar) *s) || *s == '_'))
703 break;
704 return *s == '=' && s > os && *(s+1) != '=';
705}
706
707/* strtod is supposed to be a proper test of what's a valid number */
708/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
709/* wrong: violates 4.10.1.4 of ansi C standard */
710
711#include <math.h>
712int is_number(const char *s)
713{
714 double r;
715 char *ep;
716 errno = 0;
717 r = strtod(s, &ep);
718 if (ep == s || r == HUGE_VAL || errno == ERANGE)
719 return 0;
720 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
721 ep++;
722 if (*ep == '\0')
723 return 1;
724 else
725 return 0;
726}