blob: 7e834393bbd4ecd6cbb4caecfafed8a21f212b43 [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");
M. Warner Losh0939e332019-06-02 15:17:29 -060065 *record = '\0';
Brian Kernighan87b94932012-12-22 10:35:39 -050066 *fldtab[0] = dollar0;
67 fldtab[0]->sval = record;
68 fldtab[0]->nval = tostring("0");
69 makefields(1, nfields);
70}
71
72void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
73{
74 char temp[50];
75 int i;
76
77 for (i = n1; i <= n2; i++) {
78 fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
79 if (fldtab[i] == NULL)
80 FATAL("out of space in makefields %d", i);
81 *fldtab[i] = dollar1;
82 sprintf(temp, "%d", i);
83 fldtab[i]->nval = tostring(temp);
84 }
85}
86
87void initgetrec(void)
88{
89 int i;
90 char *p;
91
92 for (i = 1; i < *ARGC; i++) {
93 p = getargv(i); /* find 1st real filename */
94 if (p == NULL || *p == '\0') { /* deleted or zapped */
95 argno++;
96 continue;
97 }
98 if (!isclvar(p)) {
99 setsval(lookup("FILENAME", symtab), p);
100 return;
101 }
102 setclvar(p); /* a commandline assignment before filename */
103 argno++;
104 }
105 infile = stdin; /* no filenames, so use stdin */
106}
107
Cody Peter Mellob4636802018-10-19 15:07:53 -0700108/*
109 * POSIX specifies that fields are supposed to be evaluated as if they were
110 * split using the value of FS at the time that the record's value ($0) was
111 * read.
112 *
113 * Since field-splitting is done lazily, we save the current value of FS
114 * whenever a new record is read in (implicitly or via getline), or when
115 * a new value is assigned to $0.
116 */
117void savefs(void)
118{
119 if (strlen(getsval(fsloc)) >= sizeof (inputFS))
120 FATAL("field separator %.10s... is too long", *FS);
121 strcpy(inputFS, *FS);
122}
123
Brian Kernighan87b94932012-12-22 10:35:39 -0500124static int firsttime = 1;
125
126int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */
127{ /* note: cares whether buf == record */
128 int c;
129 char *buf = *pbuf;
130 uschar saveb0;
131 int bufsize = *pbufsize, savebufsize = bufsize;
132
133 if (firsttime) {
134 firsttime = 0;
135 initgetrec();
136 }
137 dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
138 *RS, *FS, *ARGC, *FILENAME) );
139 if (isrecord) {
140 donefld = 0;
141 donerec = 1;
Cody Peter Mellob4636802018-10-19 15:07:53 -0700142 savefs();
Brian Kernighan87b94932012-12-22 10:35:39 -0500143 }
144 saveb0 = buf[0];
145 buf[0] = 0;
146 while (argno < *ARGC || infile == stdin) {
147 dprintf( ("argno=%d, file=|%s|\n", argno, file) );
148 if (infile == NULL) { /* have to open a new file */
149 file = getargv(argno);
150 if (file == NULL || *file == '\0') { /* deleted or zapped */
151 argno++;
152 continue;
153 }
154 if (isclvar(file)) { /* a var=value arg */
155 setclvar(file);
156 argno++;
157 continue;
158 }
159 *FILENAME = file;
160 dprintf( ("opening file %s\n", file) );
161 if (*file == '-' && *(file+1) == '\0')
162 infile = stdin;
163 else if ((infile = fopen(file, "r")) == NULL)
164 FATAL("can't open file %s", file);
165 setfval(fnrloc, 0.0);
166 }
167 c = readrec(&buf, &bufsize, infile);
168 if (c != 0 || buf[0] != '\0') { /* normal record */
169 if (isrecord) {
170 if (freeable(fldtab[0]))
171 xfree(fldtab[0]->sval);
172 fldtab[0]->sval = buf; /* buf == record */
173 fldtab[0]->tval = REC | STR | DONTFREE;
174 if (is_number(fldtab[0]->sval)) {
175 fldtab[0]->fval = atof(fldtab[0]->sval);
176 fldtab[0]->tval |= NUM;
177 }
178 }
179 setfval(nrloc, nrloc->fval+1);
180 setfval(fnrloc, fnrloc->fval+1);
181 *pbuf = buf;
182 *pbufsize = bufsize;
183 return 1;
184 }
185 /* EOF arrived on this file; set up next */
186 if (infile != stdin)
187 fclose(infile);
188 infile = NULL;
189 argno++;
190 }
191 buf[0] = saveb0;
192 *pbuf = buf;
193 *pbufsize = savebufsize;
194 return 0; /* true end of file */
195}
196
197void nextfile(void)
198{
199 if (infile != NULL && infile != stdin)
200 fclose(infile);
201 infile = NULL;
202 argno++;
203}
204
205int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */
206{
207 int sep, c;
208 char *rr, *buf = *pbuf;
209 int bufsize = *pbufsize;
Cody Peter Mello52566c02018-09-18 15:45:55 -0700210 char *rs = getsval(rsloc);
Brian Kernighan87b94932012-12-22 10:35:39 -0500211
Cody Peter Mello52566c02018-09-18 15:45:55 -0700212 if ((sep = *rs) == 0) {
Brian Kernighan87b94932012-12-22 10:35:39 -0500213 sep = '\n';
214 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
215 ;
216 if (c != EOF)
217 ungetc(c, inf);
218 }
219 for (rr = buf; ; ) {
220 for (; (c=getc(inf)) != sep && c != EOF; ) {
221 if (rr-buf+1 > bufsize)
222 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
223 FATAL("input record `%.30s...' too long", buf);
224 *rr++ = c;
225 }
Cody Peter Mello52566c02018-09-18 15:45:55 -0700226 if (*rs == sep || c == EOF)
Brian Kernighan87b94932012-12-22 10:35:39 -0500227 break;
228 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
229 break;
230 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
231 FATAL("input record `%.30s...' too long", buf);
232 *rr++ = '\n';
233 *rr++ = c;
234 }
235 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
236 FATAL("input record `%.30s...' too long", buf);
237 *rr = 0;
238 dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
239 *pbuf = buf;
240 *pbufsize = bufsize;
241 return c == EOF && rr == buf ? 0 : 1;
242}
243
244char *getargv(int n) /* get ARGV[n] */
245{
246 Cell *x;
247 char *s, temp[50];
248 extern Array *ARGVtab;
249
250 sprintf(temp, "%d", n);
251 if (lookup(temp, ARGVtab) == NULL)
252 return NULL;
253 x = setsymtab(temp, "", 0.0, STR, ARGVtab);
254 s = getsval(x);
255 dprintf( ("getargv(%d) returns |%s|\n", n, s) );
256 return s;
257}
258
259void setclvar(char *s) /* set var=value from s */
260{
261 char *p;
262 Cell *q;
263
264 for (p=s; *p != '='; p++)
265 ;
266 *p++ = 0;
267 p = qstring(p, '\0');
268 q = setsymtab(s, p, 0.0, STR, symtab);
269 setsval(q, p);
270 if (is_number(q->sval)) {
271 q->fval = atof(q->sval);
272 q->tval |= NUM;
273 }
274 dprintf( ("command line set %s to |%s|\n", s, p) );
275}
276
277
278void fldbld(void) /* create fields from current record */
279{
280 /* this relies on having fields[] the same length as $0 */
281 /* the fields are all stored in this one array with \0's */
282 /* possibly with a final trailing \0 not associated with any field */
283 char *r, *fr, sep;
284 Cell *p;
285 int i, j, n;
286
287 if (donefld)
288 return;
289 if (!isstr(fldtab[0]))
290 getsval(fldtab[0]);
291 r = fldtab[0]->sval;
292 n = strlen(r);
293 if (n > fieldssize) {
294 xfree(fields);
295 if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
296 FATAL("out of space for fields in fldbld %d", n);
297 fieldssize = n;
298 }
299 fr = fields;
300 i = 0; /* number of fields accumulated here */
Brian Kernighan87b94932012-12-22 10:35:39 -0500301 if (strlen(inputFS) > 1) { /* it's a regular expression */
302 i = refldbld(r, inputFS);
303 } else if ((sep = *inputFS) == ' ') { /* default whitespace */
304 for (i = 0; ; ) {
305 while (*r == ' ' || *r == '\t' || *r == '\n')
306 r++;
307 if (*r == 0)
308 break;
309 i++;
310 if (i > nfields)
311 growfldtab(i);
312 if (freeable(fldtab[i]))
313 xfree(fldtab[i]->sval);
314 fldtab[i]->sval = fr;
315 fldtab[i]->tval = FLD | STR | DONTFREE;
316 do
317 *fr++ = *r++;
318 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
319 *fr++ = 0;
320 }
321 *fr = 0;
322 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
323 for (i = 0; *r != 0; r++) {
324 char buf[2];
325 i++;
326 if (i > nfields)
327 growfldtab(i);
328 if (freeable(fldtab[i]))
329 xfree(fldtab[i]->sval);
330 buf[0] = *r;
331 buf[1] = 0;
332 fldtab[i]->sval = tostring(buf);
333 fldtab[i]->tval = FLD | STR;
334 }
335 *fr = 0;
336 } else if (*r != 0) { /* if 0, it's a null field */
337 /* subtlecase : if length(FS) == 1 && length(RS > 0)
338 * \n is NOT a field separator (cf awk book 61,84).
339 * this variable is tested in the inner while loop.
340 */
341 int rtest = '\n'; /* normal case */
342 if (strlen(*RS) > 0)
343 rtest = '\0';
344 for (;;) {
345 i++;
346 if (i > nfields)
347 growfldtab(i);
348 if (freeable(fldtab[i]))
349 xfree(fldtab[i]->sval);
350 fldtab[i]->sval = fr;
351 fldtab[i]->tval = FLD | STR | DONTFREE;
352 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
353 *fr++ = *r++;
354 *fr++ = 0;
355 if (*r++ == 0)
356 break;
357 }
358 *fr = 0;
359 }
360 if (i > nfields)
361 FATAL("record `%.30s...' has too many fields; can't happen", r);
362 cleanfld(i+1, lastfld); /* clean out junk from previous record */
363 lastfld = i;
364 donefld = 1;
365 for (j = 1; j <= lastfld; j++) {
366 p = fldtab[j];
367 if(is_number(p->sval)) {
368 p->fval = atof(p->sval);
369 p->tval |= NUM;
370 }
371 }
372 setfval(nfloc, (Awkfloat) lastfld);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300373 donerec = 1; /* restore */
Brian Kernighan87b94932012-12-22 10:35:39 -0500374 if (dbg) {
375 for (j = 0; j <= lastfld; j++) {
376 p = fldtab[j];
377 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
378 }
379 }
380}
381
382void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
383{ /* nvals remain intact */
384 Cell *p;
385 int i;
386
387 for (i = n1; i <= n2; i++) {
388 p = fldtab[i];
389 if (freeable(p))
390 xfree(p->sval);
391 p->sval = "";
392 p->tval = FLD | STR | DONTFREE;
393 }
394}
395
396void newfld(int n) /* add field n after end of existing lastfld */
397{
398 if (n > nfields)
399 growfldtab(n);
400 cleanfld(lastfld+1, n);
401 lastfld = n;
402 setfval(nfloc, (Awkfloat) n);
403}
404
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300405void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
406{
Cody Peter Mello179536a2018-09-25 21:19:49 -0700407 if (n < 0)
408 FATAL("cannot set NF to a negative value");
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300409 if (n > nfields)
410 growfldtab(n);
411
412 if (lastfld < n)
413 cleanfld(lastfld+1, n);
414 else
415 cleanfld(n+1, lastfld);
416
417 lastfld = n;
418}
419
Brian Kernighan87b94932012-12-22 10:35:39 -0500420Cell *fieldadr(int n) /* get nth field */
421{
422 if (n < 0)
423 FATAL("trying to access out of range field %d", n);
424 if (n > nfields) /* fields after NF are empty */
425 growfldtab(n); /* but does not increase NF */
426 return(fldtab[n]);
427}
428
429void growfldtab(int n) /* make new fields up to at least $n */
430{
431 int nf = 2 * nfields;
432 size_t s;
433
434 if (n > nf)
435 nf = n;
436 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
437 if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
438 fldtab = (Cell **) realloc(fldtab, s);
439 else /* overflow sizeof int */
440 xfree(fldtab); /* make it null */
441 if (fldtab == NULL)
442 FATAL("out of space creating %d fields", nf);
443 makefields(nfields+1, nf);
444 nfields = nf;
445}
446
447int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
448{
449 /* this relies on having fields[] the same length as $0 */
450 /* the fields are all stored in this one array with \0's */
451 char *fr;
452 int i, tempstat, n;
453 fa *pfa;
454
455 n = strlen(rec);
456 if (n > fieldssize) {
457 xfree(fields);
458 if ((fields = (char *) malloc(n+1)) == NULL)
459 FATAL("out of space for fields in refldbld %d", n);
460 fieldssize = n;
461 }
462 fr = fields;
463 *fr = '\0';
464 if (*rec == '\0')
465 return 0;
466 pfa = makedfa(fs, 1);
467 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
468 tempstat = pfa->initstat;
469 for (i = 1; ; i++) {
470 if (i > nfields)
471 growfldtab(i);
472 if (freeable(fldtab[i]))
473 xfree(fldtab[i]->sval);
474 fldtab[i]->tval = FLD | STR | DONTFREE;
475 fldtab[i]->sval = fr;
476 dprintf( ("refldbld: i=%d\n", i) );
477 if (nematch(pfa, rec)) {
478 pfa->initstat = 2; /* horrible coupling to b.c */
479 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
480 strncpy(fr, rec, patbeg-rec);
481 fr += patbeg - rec + 1;
482 *(fr-1) = '\0';
483 rec = patbeg + patlen;
484 } else {
485 dprintf( ("no match %s\n", rec) );
486 strcpy(fr, rec);
487 pfa->initstat = tempstat;
488 break;
489 }
490 }
491 return i;
492}
493
494void recbld(void) /* create $0 from $1..$NF if necessary */
495{
496 int i;
497 char *r, *p;
Cody Peter Mello52566c02018-09-18 15:45:55 -0700498 char *sep = getsval(ofsloc);
Brian Kernighan87b94932012-12-22 10:35:39 -0500499
500 if (donerec == 1)
501 return;
502 r = record;
503 for (i = 1; i <= *NF; i++) {
504 p = getsval(fldtab[i]);
505 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
506 FATAL("created $0 `%.30s...' too long", record);
507 while ((*r = *p++) != 0)
508 r++;
509 if (i < *NF) {
Cody Peter Mello52566c02018-09-18 15:45:55 -0700510 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
Brian Kernighan87b94932012-12-22 10:35:39 -0500511 FATAL("created $0 `%.30s...' too long", record);
Cody Peter Mello52566c02018-09-18 15:45:55 -0700512 for (p = sep; (*r = *p++) != 0; )
Brian Kernighan87b94932012-12-22 10:35:39 -0500513 r++;
514 }
515 }
516 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
517 FATAL("built giant record `%.30s...'", record);
518 *r = '\0';
519 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
520
521 if (freeable(fldtab[0]))
522 xfree(fldtab[0]->sval);
523 fldtab[0]->tval = REC | STR | DONTFREE;
524 fldtab[0]->sval = record;
525
526 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
527 dprintf( ("recbld = |%s|\n", record) );
528 donerec = 1;
529}
530
531int errorflag = 0;
532
533void yyerror(const char *s)
534{
535 SYNTAX("%s", s);
536}
537
538void SYNTAX(const char *fmt, ...)
539{
540 extern char *cmdname, *curfname;
541 static int been_here = 0;
542 va_list varg;
543
544 if (been_here++ > 2)
545 return;
546 fprintf(stderr, "%s: ", cmdname);
547 va_start(varg, fmt);
548 vfprintf(stderr, fmt, varg);
549 va_end(varg);
550 fprintf(stderr, " at source line %d", lineno);
551 if (curfname != NULL)
552 fprintf(stderr, " in function %s", curfname);
553 if (compile_time == 1 && cursource() != NULL)
554 fprintf(stderr, " source file %s", cursource());
555 fprintf(stderr, "\n");
556 errorflag = 2;
557 eprint();
558}
559
560void fpecatch(int n)
561{
562 FATAL("floating point exception %d", n);
563}
564
565extern int bracecnt, brackcnt, parencnt;
566
567void bracecheck(void)
568{
569 int c;
570 static int beenhere = 0;
571
572 if (beenhere++)
573 return;
574 while ((c = input()) != EOF && c != '\0')
575 bclass(c);
576 bcheck2(bracecnt, '{', '}');
577 bcheck2(brackcnt, '[', ']');
578 bcheck2(parencnt, '(', ')');
579}
580
581void bcheck2(int n, int c1, int c2)
582{
583 if (n == 1)
584 fprintf(stderr, "\tmissing %c\n", c2);
585 else if (n > 1)
586 fprintf(stderr, "\t%d missing %c's\n", n, c2);
587 else if (n == -1)
588 fprintf(stderr, "\textra %c\n", c2);
589 else if (n < -1)
590 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
591}
592
593void FATAL(const char *fmt, ...)
594{
595 extern char *cmdname;
596 va_list varg;
597
598 fflush(stdout);
599 fprintf(stderr, "%s: ", cmdname);
600 va_start(varg, fmt);
601 vfprintf(stderr, fmt, varg);
602 va_end(varg);
603 error();
604 if (dbg > 1) /* core dump if serious debugging on */
605 abort();
606 exit(2);
607}
608
609void WARNING(const char *fmt, ...)
610{
611 extern char *cmdname;
612 va_list varg;
613
614 fflush(stdout);
615 fprintf(stderr, "%s: ", cmdname);
616 va_start(varg, fmt);
617 vfprintf(stderr, fmt, varg);
618 va_end(varg);
619 error();
620}
621
622void error()
623{
624 extern Node *curnode;
625
626 fprintf(stderr, "\n");
627 if (compile_time != 2 && NR && *NR > 0) {
628 fprintf(stderr, " input record number %d", (int) (*FNR));
629 if (strcmp(*FILENAME, "-") != 0)
630 fprintf(stderr, ", file %s", *FILENAME);
631 fprintf(stderr, "\n");
632 }
633 if (compile_time != 2 && curnode)
634 fprintf(stderr, " source line number %d", curnode->lineno);
635 else if (compile_time != 2 && lineno)
636 fprintf(stderr, " source line number %d", lineno);
637 if (compile_time == 1 && cursource() != NULL)
638 fprintf(stderr, " source file %s", cursource());
639 fprintf(stderr, "\n");
640 eprint();
641}
642
643void eprint(void) /* try to print context around error */
644{
645 char *p, *q;
646 int c;
647 static int been_here = 0;
648 extern char ebuf[], *ep;
649
650 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
651 return;
Brian Kernighan3ed9e242018-08-15 10:45:03 -0400652 if (ebuf == ep)
653 return;
Brian Kernighan87b94932012-12-22 10:35:39 -0500654 p = ep - 1;
655 if (p > ebuf && *p == '\n')
656 p--;
657 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
658 ;
659 while (*p == '\n')
660 p++;
661 fprintf(stderr, " context is\n\t");
662 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
663 ;
664 for ( ; p < q; p++)
665 if (*p)
666 putc(*p, stderr);
667 fprintf(stderr, " >>> ");
668 for ( ; p < ep; p++)
669 if (*p)
670 putc(*p, stderr);
671 fprintf(stderr, " <<< ");
672 if (*ep)
673 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
674 putc(c, stderr);
675 bclass(c);
676 }
677 putc('\n', stderr);
678 ep = ebuf;
679}
680
681void bclass(int c)
682{
683 switch (c) {
684 case '{': bracecnt++; break;
685 case '}': bracecnt--; break;
686 case '[': brackcnt++; break;
687 case ']': brackcnt--; break;
688 case '(': parencnt++; break;
689 case ')': parencnt--; break;
690 }
691}
692
693double errcheck(double x, const char *s)
694{
695
696 if (errno == EDOM) {
697 errno = 0;
698 WARNING("%s argument out of domain", s);
699 x = 1;
700 } else if (errno == ERANGE) {
701 errno = 0;
702 WARNING("%s result out of range", s);
703 x = 1;
704 }
705 return x;
706}
707
708int isclvar(const char *s) /* is s of form var=something ? */
709{
710 const char *os = s;
711
712 if (!isalpha((uschar) *s) && *s != '_')
713 return 0;
714 for ( ; *s; s++)
715 if (!(isalnum((uschar) *s) || *s == '_'))
716 break;
Arnold D. Robbins4189ef52019-05-29 21:04:18 +0300717 return *s == '=' && s > os;
Brian Kernighan87b94932012-12-22 10:35:39 -0500718}
719
720/* strtod is supposed to be a proper test of what's a valid number */
721/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
722/* wrong: violates 4.10.1.4 of ansi C standard */
723
724#include <math.h>
725int is_number(const char *s)
726{
727 double r;
728 char *ep;
729 errno = 0;
730 r = strtod(s, &ep);
731 if (ep == s || r == HUGE_VAL || errno == ERANGE)
732 return 0;
733 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
734 ep++;
735 if (*ep == '\0')
736 return 1;
737 else
738 return 0;
739}