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