blob: b4a0d89c5b0df488f130dba84697f5835d726457 [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{
zoulasc65892082019-10-24 09:40:15 -040060 if ( (record = malloc(n)) == NULL
61 || (fields = malloc(n+1)) == NULL
62 || (fldtab = calloc(nfields+2, sizeof(*fldtab))) == NULL
63 || (fldtab[0] = malloc(sizeof(**fldtab))) == NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -050064 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++) {
zoulasc65892082019-10-24 09:40:15 -040078 fldtab[i] = malloc(sizeof(**fldtab));
Brian Kernighan87b94932012-12-22 10:35:39 -050079 if (fldtab[i] == NULL)
80 FATAL("out of space in makefields %d", i);
81 *fldtab[i] = dollar1;
zoulasc0d8778b2019-10-25 10:59:10 -040082 snprintf(temp, sizeof(temp), "%d", i);
Brian Kernighan87b94932012-12-22 10:35:39 -050083 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{
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300207 int sep, c, isrec;
Brian Kernighan87b94932012-12-22 10:35:39 -0500208 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
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300212 if (*rs && rs[1]) {
213 int found;
214
215 fa *pfa = makedfa(rs, 1);
216 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
217 if (found)
zoulasc65892082019-10-24 09:40:15 -0400218 setptr(patbeg, '\0');
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300219 } else {
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300220 if ((sep = *rs) == 0) {
221 sep = '\n';
222 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
223 ;
224 if (c != EOF)
225 ungetc(c, inf);
226 }
227 for (rr = buf; ; ) {
228 for (; (c=getc(inf)) != sep && c != EOF; ) {
229 if (rr-buf+1 > bufsize)
230 if (!adjbuf(&buf, &bufsize, 1+rr-buf,
231 recsize, &rr, "readrec 1"))
232 FATAL("input record `%.30s...' too long", buf);
233 *rr++ = c;
234 }
235 if (*rs == sep || c == EOF)
236 break;
237 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
238 break;
239 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
240 "readrec 2"))
241 FATAL("input record `%.30s...' too long", buf);
242 *rr++ = '\n';
Brian Kernighan87b94932012-12-22 10:35:39 -0500243 *rr++ = c;
244 }
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300245 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
Brian Kernighan87b94932012-12-22 10:35:39 -0500246 FATAL("input record `%.30s...' too long", buf);
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300247 *rr = 0;
Brian Kernighan87b94932012-12-22 10:35:39 -0500248 }
Brian Kernighan87b94932012-12-22 10:35:39 -0500249 *pbuf = buf;
250 *pbufsize = bufsize;
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300251 isrec = *buf || !feof(inf);
252 dprintf( ("readrec saw <%s>, returns %d\n", buf, isrec) );
253 return isrec;
Brian Kernighan87b94932012-12-22 10:35:39 -0500254}
255
256char *getargv(int n) /* get ARGV[n] */
257{
258 Cell *x;
259 char *s, temp[50];
260 extern Array *ARGVtab;
261
zoulasc0d8778b2019-10-25 10:59:10 -0400262 snprintf(temp, sizeof(temp), "%d", n);
Brian Kernighan87b94932012-12-22 10:35:39 -0500263 if (lookup(temp, ARGVtab) == NULL)
264 return NULL;
265 x = setsymtab(temp, "", 0.0, STR, ARGVtab);
266 s = getsval(x);
267 dprintf( ("getargv(%d) returns |%s|\n", n, s) );
268 return s;
269}
270
271void setclvar(char *s) /* set var=value from s */
272{
273 char *p;
274 Cell *q;
275
276 for (p=s; *p != '='; p++)
277 ;
278 *p++ = 0;
279 p = qstring(p, '\0');
280 q = setsymtab(s, p, 0.0, STR, symtab);
281 setsval(q, p);
282 if (is_number(q->sval)) {
283 q->fval = atof(q->sval);
284 q->tval |= NUM;
285 }
286 dprintf( ("command line set %s to |%s|\n", s, p) );
287}
288
289
290void fldbld(void) /* create fields from current record */
291{
292 /* this relies on having fields[] the same length as $0 */
293 /* the fields are all stored in this one array with \0's */
294 /* possibly with a final trailing \0 not associated with any field */
295 char *r, *fr, sep;
296 Cell *p;
297 int i, j, n;
298
299 if (donefld)
300 return;
301 if (!isstr(fldtab[0]))
302 getsval(fldtab[0]);
303 r = fldtab[0]->sval;
304 n = strlen(r);
305 if (n > fieldssize) {
306 xfree(fields);
zoulasc65892082019-10-24 09:40:15 -0400307 if ((fields = malloc(n+2)) == NULL) /* possibly 2 final \0s */
Brian Kernighan87b94932012-12-22 10:35:39 -0500308 FATAL("out of space for fields in fldbld %d", n);
309 fieldssize = n;
310 }
311 fr = fields;
312 i = 0; /* number of fields accumulated here */
Brian Kernighan87b94932012-12-22 10:35:39 -0500313 if (strlen(inputFS) > 1) { /* it's a regular expression */
314 i = refldbld(r, inputFS);
315 } else if ((sep = *inputFS) == ' ') { /* default whitespace */
316 for (i = 0; ; ) {
317 while (*r == ' ' || *r == '\t' || *r == '\n')
318 r++;
319 if (*r == 0)
320 break;
321 i++;
322 if (i > nfields)
323 growfldtab(i);
324 if (freeable(fldtab[i]))
325 xfree(fldtab[i]->sval);
326 fldtab[i]->sval = fr;
327 fldtab[i]->tval = FLD | STR | DONTFREE;
328 do
329 *fr++ = *r++;
330 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
331 *fr++ = 0;
332 }
333 *fr = 0;
334 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
335 for (i = 0; *r != 0; r++) {
336 char buf[2];
337 i++;
338 if (i > nfields)
339 growfldtab(i);
340 if (freeable(fldtab[i]))
341 xfree(fldtab[i]->sval);
342 buf[0] = *r;
343 buf[1] = 0;
344 fldtab[i]->sval = tostring(buf);
345 fldtab[i]->tval = FLD | STR;
346 }
347 *fr = 0;
348 } else if (*r != 0) { /* if 0, it's a null field */
349 /* subtlecase : if length(FS) == 1 && length(RS > 0)
350 * \n is NOT a field separator (cf awk book 61,84).
351 * this variable is tested in the inner while loop.
352 */
353 int rtest = '\n'; /* normal case */
354 if (strlen(*RS) > 0)
355 rtest = '\0';
356 for (;;) {
357 i++;
358 if (i > nfields)
359 growfldtab(i);
360 if (freeable(fldtab[i]))
361 xfree(fldtab[i]->sval);
362 fldtab[i]->sval = fr;
363 fldtab[i]->tval = FLD | STR | DONTFREE;
364 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
365 *fr++ = *r++;
366 *fr++ = 0;
367 if (*r++ == 0)
368 break;
369 }
370 *fr = 0;
371 }
372 if (i > nfields)
373 FATAL("record `%.30s...' has too many fields; can't happen", r);
374 cleanfld(i+1, lastfld); /* clean out junk from previous record */
375 lastfld = i;
376 donefld = 1;
377 for (j = 1; j <= lastfld; j++) {
378 p = fldtab[j];
379 if(is_number(p->sval)) {
380 p->fval = atof(p->sval);
381 p->tval |= NUM;
382 }
383 }
384 setfval(nfloc, (Awkfloat) lastfld);
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300385 donerec = 1; /* restore */
Brian Kernighan87b94932012-12-22 10:35:39 -0500386 if (dbg) {
387 for (j = 0; j <= lastfld; j++) {
388 p = fldtab[j];
389 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
390 }
391 }
392}
393
394void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
395{ /* nvals remain intact */
396 Cell *p;
397 int i;
398
399 for (i = n1; i <= n2; i++) {
400 p = fldtab[i];
401 if (freeable(p))
402 xfree(p->sval);
403 p->sval = "";
404 p->tval = FLD | STR | DONTFREE;
405 }
406}
407
408void newfld(int n) /* add field n after end of existing lastfld */
409{
410 if (n > nfields)
411 growfldtab(n);
412 cleanfld(lastfld+1, n);
413 lastfld = n;
414 setfval(nfloc, (Awkfloat) n);
415}
416
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300417void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
418{
Cody Peter Mello179536a2018-09-25 21:19:49 -0700419 if (n < 0)
420 FATAL("cannot set NF to a negative value");
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300421 if (n > nfields)
422 growfldtab(n);
423
424 if (lastfld < n)
425 cleanfld(lastfld+1, n);
426 else
427 cleanfld(n+1, lastfld);
428
429 lastfld = n;
430}
431
Brian Kernighan87b94932012-12-22 10:35:39 -0500432Cell *fieldadr(int n) /* get nth field */
433{
434 if (n < 0)
435 FATAL("trying to access out of range field %d", n);
436 if (n > nfields) /* fields after NF are empty */
437 growfldtab(n); /* but does not increase NF */
438 return(fldtab[n]);
439}
440
441void growfldtab(int n) /* make new fields up to at least $n */
442{
443 int nf = 2 * nfields;
444 size_t s;
445
446 if (n > nf)
447 nf = n;
448 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
449 if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
zoulasc65892082019-10-24 09:40:15 -0400450 fldtab = realloc(fldtab, s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500451 else /* overflow sizeof int */
452 xfree(fldtab); /* make it null */
453 if (fldtab == NULL)
454 FATAL("out of space creating %d fields", nf);
455 makefields(nfields+1, nf);
456 nfields = nf;
457}
458
459int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
460{
461 /* this relies on having fields[] the same length as $0 */
462 /* the fields are all stored in this one array with \0's */
463 char *fr;
464 int i, tempstat, n;
465 fa *pfa;
466
467 n = strlen(rec);
468 if (n > fieldssize) {
469 xfree(fields);
zoulasc65892082019-10-24 09:40:15 -0400470 if ((fields = malloc(n+1)) == NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -0500471 FATAL("out of space for fields in refldbld %d", n);
472 fieldssize = n;
473 }
474 fr = fields;
475 *fr = '\0';
476 if (*rec == '\0')
477 return 0;
478 pfa = makedfa(fs, 1);
479 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
480 tempstat = pfa->initstat;
481 for (i = 1; ; i++) {
482 if (i > nfields)
483 growfldtab(i);
484 if (freeable(fldtab[i]))
485 xfree(fldtab[i]->sval);
486 fldtab[i]->tval = FLD | STR | DONTFREE;
487 fldtab[i]->sval = fr;
488 dprintf( ("refldbld: i=%d\n", i) );
489 if (nematch(pfa, rec)) {
490 pfa->initstat = 2; /* horrible coupling to b.c */
491 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
492 strncpy(fr, rec, patbeg-rec);
493 fr += patbeg - rec + 1;
494 *(fr-1) = '\0';
495 rec = patbeg + patlen;
496 } else {
497 dprintf( ("no match %s\n", rec) );
498 strcpy(fr, rec);
499 pfa->initstat = tempstat;
500 break;
501 }
502 }
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600503 return i;
Brian Kernighan87b94932012-12-22 10:35:39 -0500504}
505
506void recbld(void) /* create $0 from $1..$NF if necessary */
507{
508 int i;
509 char *r, *p;
Cody Peter Mello52566c02018-09-18 15:45:55 -0700510 char *sep = getsval(ofsloc);
Brian Kernighan87b94932012-12-22 10:35:39 -0500511
512 if (donerec == 1)
513 return;
514 r = record;
515 for (i = 1; i <= *NF; i++) {
516 p = getsval(fldtab[i]);
517 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
518 FATAL("created $0 `%.30s...' too long", record);
519 while ((*r = *p++) != 0)
520 r++;
521 if (i < *NF) {
Cody Peter Mello52566c02018-09-18 15:45:55 -0700522 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
Brian Kernighan87b94932012-12-22 10:35:39 -0500523 FATAL("created $0 `%.30s...' too long", record);
Cody Peter Mello52566c02018-09-18 15:45:55 -0700524 for (p = sep; (*r = *p++) != 0; )
Brian Kernighan87b94932012-12-22 10:35:39 -0500525 r++;
526 }
527 }
528 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
529 FATAL("built giant record `%.30s...'", record);
530 *r = '\0';
531 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
532
533 if (freeable(fldtab[0]))
534 xfree(fldtab[0]->sval);
535 fldtab[0]->tval = REC | STR | DONTFREE;
536 fldtab[0]->sval = record;
537
538 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
539 dprintf( ("recbld = |%s|\n", record) );
540 donerec = 1;
541}
542
543int errorflag = 0;
544
545void yyerror(const char *s)
546{
547 SYNTAX("%s", s);
548}
549
550void SYNTAX(const char *fmt, ...)
551{
552 extern char *cmdname, *curfname;
553 static int been_here = 0;
554 va_list varg;
555
556 if (been_here++ > 2)
557 return;
558 fprintf(stderr, "%s: ", cmdname);
559 va_start(varg, fmt);
560 vfprintf(stderr, fmt, varg);
561 va_end(varg);
562 fprintf(stderr, " at source line %d", lineno);
563 if (curfname != NULL)
564 fprintf(stderr, " in function %s", curfname);
565 if (compile_time == 1 && cursource() != NULL)
566 fprintf(stderr, " source file %s", cursource());
567 fprintf(stderr, "\n");
568 errorflag = 2;
569 eprint();
570}
571
572void fpecatch(int n)
573{
574 FATAL("floating point exception %d", n);
575}
576
577extern int bracecnt, brackcnt, parencnt;
578
579void bracecheck(void)
580{
581 int c;
582 static int beenhere = 0;
583
584 if (beenhere++)
585 return;
586 while ((c = input()) != EOF && c != '\0')
587 bclass(c);
588 bcheck2(bracecnt, '{', '}');
589 bcheck2(brackcnt, '[', ']');
590 bcheck2(parencnt, '(', ')');
591}
592
593void bcheck2(int n, int c1, int c2)
594{
595 if (n == 1)
596 fprintf(stderr, "\tmissing %c\n", c2);
597 else if (n > 1)
598 fprintf(stderr, "\t%d missing %c's\n", n, c2);
599 else if (n == -1)
600 fprintf(stderr, "\textra %c\n", c2);
601 else if (n < -1)
602 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
603}
604
605void FATAL(const char *fmt, ...)
606{
607 extern char *cmdname;
608 va_list varg;
609
610 fflush(stdout);
611 fprintf(stderr, "%s: ", cmdname);
612 va_start(varg, fmt);
613 vfprintf(stderr, fmt, varg);
614 va_end(varg);
615 error();
616 if (dbg > 1) /* core dump if serious debugging on */
617 abort();
618 exit(2);
619}
620
621void WARNING(const char *fmt, ...)
622{
623 extern char *cmdname;
624 va_list varg;
625
626 fflush(stdout);
627 fprintf(stderr, "%s: ", cmdname);
628 va_start(varg, fmt);
629 vfprintf(stderr, fmt, varg);
630 va_end(varg);
631 error();
632}
633
634void error()
635{
636 extern Node *curnode;
637
638 fprintf(stderr, "\n");
639 if (compile_time != 2 && NR && *NR > 0) {
640 fprintf(stderr, " input record number %d", (int) (*FNR));
641 if (strcmp(*FILENAME, "-") != 0)
642 fprintf(stderr, ", file %s", *FILENAME);
643 fprintf(stderr, "\n");
644 }
645 if (compile_time != 2 && curnode)
646 fprintf(stderr, " source line number %d", curnode->lineno);
647 else if (compile_time != 2 && lineno)
648 fprintf(stderr, " source line number %d", lineno);
649 if (compile_time == 1 && cursource() != NULL)
650 fprintf(stderr, " source file %s", cursource());
651 fprintf(stderr, "\n");
652 eprint();
653}
654
655void eprint(void) /* try to print context around error */
656{
657 char *p, *q;
658 int c;
659 static int been_here = 0;
660 extern char ebuf[], *ep;
661
M. Warner Losh9310d452019-06-02 15:23:06 -0600662 if (compile_time == 2 || compile_time == 0 || been_here++ > 0 || ebuf == ep)
Brian Kernighan87b94932012-12-22 10:35:39 -0500663 return;
Brian Kernighan3ed9e242018-08-15 10:45:03 -0400664 if (ebuf == ep)
665 return;
Brian Kernighan87b94932012-12-22 10:35:39 -0500666 p = ep - 1;
667 if (p > ebuf && *p == '\n')
668 p--;
669 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
670 ;
671 while (*p == '\n')
672 p++;
673 fprintf(stderr, " context is\n\t");
674 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
675 ;
676 for ( ; p < q; p++)
677 if (*p)
678 putc(*p, stderr);
679 fprintf(stderr, " >>> ");
680 for ( ; p < ep; p++)
681 if (*p)
682 putc(*p, stderr);
683 fprintf(stderr, " <<< ");
684 if (*ep)
685 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
686 putc(c, stderr);
687 bclass(c);
688 }
689 putc('\n', stderr);
690 ep = ebuf;
691}
692
693void bclass(int c)
694{
695 switch (c) {
696 case '{': bracecnt++; break;
697 case '}': bracecnt--; break;
698 case '[': brackcnt++; break;
699 case ']': brackcnt--; break;
700 case '(': parencnt++; break;
701 case ')': parencnt--; break;
702 }
703}
704
705double errcheck(double x, const char *s)
706{
707
708 if (errno == EDOM) {
709 errno = 0;
710 WARNING("%s argument out of domain", s);
711 x = 1;
712 } else if (errno == ERANGE) {
713 errno = 0;
714 WARNING("%s result out of range", s);
715 x = 1;
716 }
717 return x;
718}
719
720int isclvar(const char *s) /* is s of form var=something ? */
721{
722 const char *os = s;
723
724 if (!isalpha((uschar) *s) && *s != '_')
725 return 0;
726 for ( ; *s; s++)
727 if (!(isalnum((uschar) *s) || *s == '_'))
728 break;
Arnold D. Robbins4189ef52019-05-29 21:04:18 +0300729 return *s == '=' && s > os;
Brian Kernighan87b94932012-12-22 10:35:39 -0500730}
731
732/* strtod is supposed to be a proper test of what's a valid number */
733/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
734/* wrong: violates 4.10.1.4 of ansi C standard */
735
736#include <math.h>
737int is_number(const char *s)
738{
739 double r;
740 char *ep;
741 errno = 0;
742 r = strtod(s, &ep);
743 if (ep == s || r == HUGE_VAL || errno == ERANGE)
744 return 0;
745 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
746 ep++;
747 if (*ep == '\0')
748 return 1;
749 else
750 return 0;
751}