blob: 0485e5af8c8d0541529b66b18a55d40bfea30ee7 [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>
Michael Forney69325712020-01-30 22:23:34 -080032#include <limits.h>
Brian Kernighan87b94932012-12-22 10:35:39 -050033#include "awk.h"
34#include "ytab.h"
35
zoulasc6a877092020-01-24 04:11:59 -050036char EMPTY[] = { '\0' };
Brian Kernighan87b94932012-12-22 10:35:39 -050037FILE *infile = NULL;
zoulasc6a877092020-01-24 04:11:59 -050038char *file = EMPTY;
Brian Kernighan87b94932012-12-22 10:35:39 -050039char *record;
40int recsize = RECSIZE;
41char *fields;
42int fieldssize = RECSIZE;
43
44Cell **fldtab; /* pointers to Cells */
zoulasc6a877092020-01-24 04:11:59 -050045static size_t len_inputFS = 0;
46static char *inputFS = NULL; /* FS at time of input, for field splitting */
Brian Kernighan87b94932012-12-22 10:35:39 -050047
48#define MAXFLD 2
49int nfields = MAXFLD; /* last allocated slot for $i */
50
Arnold D. Robbins108224b2019-11-10 21:19:18 +020051bool donefld; /* true = implies rec broken into fields */
52bool donerec; /* true = record is valid (no flds have changed) */
Brian Kernighan87b94932012-12-22 10:35:39 -050053
54int lastfld = 0; /* last used field */
55int argno = 1; /* current input argument number */
56extern Awkfloat *ARGC;
57
zoulasc6a877092020-01-24 04:11:59 -050058static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE };
59static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE };
Brian Kernighan87b94932012-12-22 10:35:39 -050060
61void recinit(unsigned int n)
62{
zoulasc65892082019-10-24 09:40:15 -040063 if ( (record = malloc(n)) == NULL
64 || (fields = malloc(n+1)) == NULL
65 || (fldtab = calloc(nfields+2, sizeof(*fldtab))) == NULL
66 || (fldtab[0] = malloc(sizeof(**fldtab))) == NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -050067 FATAL("out of space for $0 and fields");
M. Warner Losh0939e332019-06-02 15:17:29 -060068 *record = '\0';
Brian Kernighan87b94932012-12-22 10:35:39 -050069 *fldtab[0] = dollar0;
70 fldtab[0]->sval = record;
71 fldtab[0]->nval = tostring("0");
72 makefields(1, nfields);
73}
74
75void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
76{
77 char temp[50];
78 int i;
79
80 for (i = n1; i <= n2; i++) {
zoulasc65892082019-10-24 09:40:15 -040081 fldtab[i] = malloc(sizeof(**fldtab));
Brian Kernighan87b94932012-12-22 10:35:39 -050082 if (fldtab[i] == NULL)
83 FATAL("out of space in makefields %d", i);
84 *fldtab[i] = dollar1;
zoulasc0d8778b2019-10-25 10:59:10 -040085 snprintf(temp, sizeof(temp), "%d", i);
Brian Kernighan87b94932012-12-22 10:35:39 -050086 fldtab[i]->nval = tostring(temp);
87 }
88}
89
90void initgetrec(void)
91{
92 int i;
93 char *p;
94
95 for (i = 1; i < *ARGC; i++) {
96 p = getargv(i); /* find 1st real filename */
97 if (p == NULL || *p == '\0') { /* deleted or zapped */
98 argno++;
99 continue;
100 }
101 if (!isclvar(p)) {
102 setsval(lookup("FILENAME", symtab), p);
103 return;
104 }
105 setclvar(p); /* a commandline assignment before filename */
106 argno++;
107 }
108 infile = stdin; /* no filenames, so use stdin */
109}
110
Cody Peter Mellob4636802018-10-19 15:07:53 -0700111/*
112 * POSIX specifies that fields are supposed to be evaluated as if they were
113 * split using the value of FS at the time that the record's value ($0) was
114 * read.
115 *
116 * Since field-splitting is done lazily, we save the current value of FS
117 * whenever a new record is read in (implicitly or via getline), or when
118 * a new value is assigned to $0.
119 */
120void savefs(void)
121{
zoulasc6a877092020-01-24 04:11:59 -0500122 size_t len;
123 if ((len = strlen(getsval(fsloc))) < len_inputFS) {
124 strcpy(inputFS, *FS); /* for subsequent field splitting */
125 return;
126 }
127
128 len_inputFS = len + 1;
129 inputFS = realloc(inputFS, len_inputFS);
130 if (inputFS == NULL)
Cody Peter Mellob4636802018-10-19 15:07:53 -0700131 FATAL("field separator %.10s... is too long", *FS);
zoulasc6a877092020-01-24 04:11:59 -0500132 memcpy(inputFS, *FS, len_inputFS);
Cody Peter Mellob4636802018-10-19 15:07:53 -0700133}
134
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200135static bool firsttime = true;
Brian Kernighan87b94932012-12-22 10:35:39 -0500136
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200137int getrec(char **pbuf, int *pbufsize, bool isrecord) /* get next input record */
Brian Kernighan87b94932012-12-22 10:35:39 -0500138{ /* note: cares whether buf == record */
139 int c;
140 char *buf = *pbuf;
141 uschar saveb0;
142 int bufsize = *pbufsize, savebufsize = bufsize;
143
144 if (firsttime) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200145 firsttime = false;
Brian Kernighan87b94932012-12-22 10:35:39 -0500146 initgetrec();
147 }
148 dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
149 *RS, *FS, *ARGC, *FILENAME) );
150 if (isrecord) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200151 donefld = false;
152 donerec = true;
Cody Peter Mellob4636802018-10-19 15:07:53 -0700153 savefs();
Brian Kernighan87b94932012-12-22 10:35:39 -0500154 }
155 saveb0 = buf[0];
156 buf[0] = 0;
157 while (argno < *ARGC || infile == stdin) {
158 dprintf( ("argno=%d, file=|%s|\n", argno, file) );
159 if (infile == NULL) { /* have to open a new file */
160 file = getargv(argno);
161 if (file == NULL || *file == '\0') { /* deleted or zapped */
162 argno++;
163 continue;
164 }
165 if (isclvar(file)) { /* a var=value arg */
166 setclvar(file);
167 argno++;
168 continue;
169 }
170 *FILENAME = file;
171 dprintf( ("opening file %s\n", file) );
172 if (*file == '-' && *(file+1) == '\0')
173 infile = stdin;
174 else if ((infile = fopen(file, "r")) == NULL)
175 FATAL("can't open file %s", file);
176 setfval(fnrloc, 0.0);
177 }
178 c = readrec(&buf, &bufsize, infile);
179 if (c != 0 || buf[0] != '\0') { /* normal record */
180 if (isrecord) {
181 if (freeable(fldtab[0]))
182 xfree(fldtab[0]->sval);
183 fldtab[0]->sval = buf; /* buf == record */
184 fldtab[0]->tval = REC | STR | DONTFREE;
185 if (is_number(fldtab[0]->sval)) {
186 fldtab[0]->fval = atof(fldtab[0]->sval);
187 fldtab[0]->tval |= NUM;
188 }
189 }
190 setfval(nrloc, nrloc->fval+1);
191 setfval(fnrloc, fnrloc->fval+1);
192 *pbuf = buf;
193 *pbufsize = bufsize;
194 return 1;
195 }
196 /* EOF arrived on this file; set up next */
197 if (infile != stdin)
198 fclose(infile);
199 infile = NULL;
200 argno++;
201 }
202 buf[0] = saveb0;
203 *pbuf = buf;
204 *pbufsize = savebufsize;
205 return 0; /* true end of file */
206}
207
208void nextfile(void)
209{
210 if (infile != NULL && infile != stdin)
211 fclose(infile);
212 infile = NULL;
213 argno++;
214}
215
216int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */
217{
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300218 int sep, c, isrec;
Brian Kernighan87b94932012-12-22 10:35:39 -0500219 char *rr, *buf = *pbuf;
220 int bufsize = *pbufsize;
Cody Peter Mello52566c02018-09-18 15:45:55 -0700221 char *rs = getsval(rsloc);
Brian Kernighan87b94932012-12-22 10:35:39 -0500222
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300223 if (*rs && rs[1]) {
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200224 bool found;
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300225
226 fa *pfa = makedfa(rs, 1);
227 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
228 if (found)
zoulasc65892082019-10-24 09:40:15 -0400229 setptr(patbeg, '\0');
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300230 } else {
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300231 if ((sep = *rs) == 0) {
232 sep = '\n';
233 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
234 ;
235 if (c != EOF)
236 ungetc(c, inf);
237 }
238 for (rr = buf; ; ) {
239 for (; (c=getc(inf)) != sep && c != EOF; ) {
240 if (rr-buf+1 > bufsize)
241 if (!adjbuf(&buf, &bufsize, 1+rr-buf,
242 recsize, &rr, "readrec 1"))
243 FATAL("input record `%.30s...' too long", buf);
244 *rr++ = c;
245 }
246 if (*rs == sep || c == EOF)
247 break;
248 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
249 break;
250 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
251 "readrec 2"))
252 FATAL("input record `%.30s...' too long", buf);
253 *rr++ = '\n';
Brian Kernighan87b94932012-12-22 10:35:39 -0500254 *rr++ = c;
255 }
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300256 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
Brian Kernighan87b94932012-12-22 10:35:39 -0500257 FATAL("input record `%.30s...' too long", buf);
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300258 *rr = 0;
Brian Kernighan87b94932012-12-22 10:35:39 -0500259 }
Brian Kernighan87b94932012-12-22 10:35:39 -0500260 *pbuf = buf;
261 *pbufsize = bufsize;
Arnold D. Robbins643a5a32019-07-28 20:12:05 +0300262 isrec = *buf || !feof(inf);
263 dprintf( ("readrec saw <%s>, returns %d\n", buf, isrec) );
264 return isrec;
Brian Kernighan87b94932012-12-22 10:35:39 -0500265}
266
267char *getargv(int n) /* get ARGV[n] */
268{
269 Cell *x;
270 char *s, temp[50];
271 extern Array *ARGVtab;
272
zoulasc0d8778b2019-10-25 10:59:10 -0400273 snprintf(temp, sizeof(temp), "%d", n);
Brian Kernighan87b94932012-12-22 10:35:39 -0500274 if (lookup(temp, ARGVtab) == NULL)
275 return NULL;
276 x = setsymtab(temp, "", 0.0, STR, ARGVtab);
277 s = getsval(x);
278 dprintf( ("getargv(%d) returns |%s|\n", n, s) );
279 return s;
280}
281
282void setclvar(char *s) /* set var=value from s */
283{
284 char *p;
285 Cell *q;
286
287 for (p=s; *p != '='; p++)
288 ;
289 *p++ = 0;
290 p = qstring(p, '\0');
291 q = setsymtab(s, p, 0.0, STR, symtab);
292 setsval(q, p);
293 if (is_number(q->sval)) {
294 q->fval = atof(q->sval);
295 q->tval |= NUM;
296 }
297 dprintf( ("command line set %s to |%s|\n", s, p) );
298}
299
300
301void fldbld(void) /* create fields from current record */
302{
303 /* this relies on having fields[] the same length as $0 */
304 /* the fields are all stored in this one array with \0's */
305 /* possibly with a final trailing \0 not associated with any field */
306 char *r, *fr, sep;
307 Cell *p;
308 int i, j, n;
309
310 if (donefld)
311 return;
312 if (!isstr(fldtab[0]))
313 getsval(fldtab[0]);
314 r = fldtab[0]->sval;
315 n = strlen(r);
316 if (n > fieldssize) {
317 xfree(fields);
zoulasc65892082019-10-24 09:40:15 -0400318 if ((fields = malloc(n+2)) == NULL) /* possibly 2 final \0s */
Brian Kernighan87b94932012-12-22 10:35:39 -0500319 FATAL("out of space for fields in fldbld %d", n);
320 fieldssize = n;
321 }
322 fr = fields;
323 i = 0; /* number of fields accumulated here */
Brian Kernighan87b94932012-12-22 10:35:39 -0500324 if (strlen(inputFS) > 1) { /* it's a regular expression */
325 i = refldbld(r, inputFS);
326 } else if ((sep = *inputFS) == ' ') { /* default whitespace */
327 for (i = 0; ; ) {
328 while (*r == ' ' || *r == '\t' || *r == '\n')
329 r++;
330 if (*r == 0)
331 break;
332 i++;
333 if (i > nfields)
334 growfldtab(i);
335 if (freeable(fldtab[i]))
336 xfree(fldtab[i]->sval);
337 fldtab[i]->sval = fr;
338 fldtab[i]->tval = FLD | STR | DONTFREE;
339 do
340 *fr++ = *r++;
341 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
342 *fr++ = 0;
343 }
344 *fr = 0;
345 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
Arnold D. Robbinsc879fbf2019-11-08 14:40:18 +0200346 for (i = 0; *r != '\0'; r += n) {
Michael Forney69325712020-01-30 22:23:34 -0800347 char buf[MB_LEN_MAX + 1];
Arnold D. Robbinsc879fbf2019-11-08 14:40:18 +0200348
Brian Kernighan87b94932012-12-22 10:35:39 -0500349 i++;
350 if (i > nfields)
351 growfldtab(i);
352 if (freeable(fldtab[i]))
353 xfree(fldtab[i]->sval);
Michael Forney69325712020-01-30 22:23:34 -0800354 n = mblen(r, MB_LEN_MAX);
Arnold D. Robbinsc879fbf2019-11-08 14:40:18 +0200355 if (n < 0)
356 n = 1;
357 memcpy(buf, r, n);
358 buf[n] = '\0';
Brian Kernighan87b94932012-12-22 10:35:39 -0500359 fldtab[i]->sval = tostring(buf);
360 fldtab[i]->tval = FLD | STR;
361 }
362 *fr = 0;
363 } else if (*r != 0) { /* if 0, it's a null field */
364 /* subtlecase : if length(FS) == 1 && length(RS > 0)
365 * \n is NOT a field separator (cf awk book 61,84).
366 * this variable is tested in the inner while loop.
367 */
368 int rtest = '\n'; /* normal case */
369 if (strlen(*RS) > 0)
370 rtest = '\0';
371 for (;;) {
372 i++;
373 if (i > nfields)
374 growfldtab(i);
375 if (freeable(fldtab[i]))
376 xfree(fldtab[i]->sval);
377 fldtab[i]->sval = fr;
378 fldtab[i]->tval = FLD | STR | DONTFREE;
379 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
380 *fr++ = *r++;
381 *fr++ = 0;
382 if (*r++ == 0)
383 break;
384 }
385 *fr = 0;
386 }
387 if (i > nfields)
388 FATAL("record `%.30s...' has too many fields; can't happen", r);
389 cleanfld(i+1, lastfld); /* clean out junk from previous record */
390 lastfld = i;
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200391 donefld = true;
Brian Kernighan87b94932012-12-22 10:35:39 -0500392 for (j = 1; j <= lastfld; j++) {
393 p = fldtab[j];
394 if(is_number(p->sval)) {
395 p->fval = atof(p->sval);
396 p->tval |= NUM;
397 }
398 }
399 setfval(nfloc, (Awkfloat) lastfld);
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200400 donerec = true; /* restore */
Brian Kernighan87b94932012-12-22 10:35:39 -0500401 if (dbg) {
402 for (j = 0; j <= lastfld; j++) {
403 p = fldtab[j];
404 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
405 }
406 }
407}
408
409void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
410{ /* nvals remain intact */
411 Cell *p;
412 int i;
413
414 for (i = n1; i <= n2; i++) {
415 p = fldtab[i];
416 if (freeable(p))
417 xfree(p->sval);
zoulasc6a877092020-01-24 04:11:59 -0500418 p->sval = EMPTY,
Brian Kernighan87b94932012-12-22 10:35:39 -0500419 p->tval = FLD | STR | DONTFREE;
420 }
421}
422
423void newfld(int n) /* add field n after end of existing lastfld */
424{
425 if (n > nfields)
426 growfldtab(n);
427 cleanfld(lastfld+1, n);
428 lastfld = n;
429 setfval(nfloc, (Awkfloat) n);
430}
431
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300432void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
433{
Cody Peter Mello179536a2018-09-25 21:19:49 -0700434 if (n < 0)
435 FATAL("cannot set NF to a negative value");
Arnold D. Robbins32093f52018-08-22 20:40:26 +0300436 if (n > nfields)
437 growfldtab(n);
438
439 if (lastfld < n)
440 cleanfld(lastfld+1, n);
441 else
442 cleanfld(n+1, lastfld);
443
444 lastfld = n;
445}
446
Brian Kernighan87b94932012-12-22 10:35:39 -0500447Cell *fieldadr(int n) /* get nth field */
448{
449 if (n < 0)
450 FATAL("trying to access out of range field %d", n);
451 if (n > nfields) /* fields after NF are empty */
452 growfldtab(n); /* but does not increase NF */
453 return(fldtab[n]);
454}
455
456void growfldtab(int n) /* make new fields up to at least $n */
457{
458 int nf = 2 * nfields;
459 size_t s;
460
461 if (n > nf)
462 nf = n;
463 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
464 if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
zoulasc65892082019-10-24 09:40:15 -0400465 fldtab = realloc(fldtab, s);
Brian Kernighan87b94932012-12-22 10:35:39 -0500466 else /* overflow sizeof int */
467 xfree(fldtab); /* make it null */
468 if (fldtab == NULL)
469 FATAL("out of space creating %d fields", nf);
470 makefields(nfields+1, nf);
471 nfields = nf;
472}
473
474int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
475{
476 /* this relies on having fields[] the same length as $0 */
477 /* the fields are all stored in this one array with \0's */
478 char *fr;
479 int i, tempstat, n;
480 fa *pfa;
481
482 n = strlen(rec);
483 if (n > fieldssize) {
484 xfree(fields);
zoulasc65892082019-10-24 09:40:15 -0400485 if ((fields = malloc(n+1)) == NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -0500486 FATAL("out of space for fields in refldbld %d", n);
487 fieldssize = n;
488 }
489 fr = fields;
490 *fr = '\0';
491 if (*rec == '\0')
492 return 0;
493 pfa = makedfa(fs, 1);
494 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
495 tempstat = pfa->initstat;
496 for (i = 1; ; i++) {
497 if (i > nfields)
498 growfldtab(i);
499 if (freeable(fldtab[i]))
500 xfree(fldtab[i]->sval);
501 fldtab[i]->tval = FLD | STR | DONTFREE;
502 fldtab[i]->sval = fr;
503 dprintf( ("refldbld: i=%d\n", i) );
504 if (nematch(pfa, rec)) {
505 pfa->initstat = 2; /* horrible coupling to b.c */
506 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
507 strncpy(fr, rec, patbeg-rec);
508 fr += patbeg - rec + 1;
509 *(fr-1) = '\0';
510 rec = patbeg + patlen;
511 } else {
512 dprintf( ("no match %s\n", rec) );
513 strcpy(fr, rec);
514 pfa->initstat = tempstat;
515 break;
516 }
517 }
Arnold D. Robbins795a06b2019-07-28 05:51:52 -0600518 return i;
Brian Kernighan87b94932012-12-22 10:35:39 -0500519}
520
521void recbld(void) /* create $0 from $1..$NF if necessary */
522{
523 int i;
524 char *r, *p;
Cody Peter Mello52566c02018-09-18 15:45:55 -0700525 char *sep = getsval(ofsloc);
Brian Kernighan87b94932012-12-22 10:35:39 -0500526
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200527 if (donerec)
Brian Kernighan87b94932012-12-22 10:35:39 -0500528 return;
529 r = record;
530 for (i = 1; i <= *NF; i++) {
531 p = getsval(fldtab[i]);
532 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
533 FATAL("created $0 `%.30s...' too long", record);
534 while ((*r = *p++) != 0)
535 r++;
536 if (i < *NF) {
Cody Peter Mello52566c02018-09-18 15:45:55 -0700537 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
Brian Kernighan87b94932012-12-22 10:35:39 -0500538 FATAL("created $0 `%.30s...' too long", record);
Cody Peter Mello52566c02018-09-18 15:45:55 -0700539 for (p = sep; (*r = *p++) != 0; )
Brian Kernighan87b94932012-12-22 10:35:39 -0500540 r++;
541 }
542 }
543 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
544 FATAL("built giant record `%.30s...'", record);
545 *r = '\0';
546 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
547
548 if (freeable(fldtab[0]))
549 xfree(fldtab[0]->sval);
550 fldtab[0]->tval = REC | STR | DONTFREE;
551 fldtab[0]->sval = record;
552
553 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]) );
554 dprintf( ("recbld = |%s|\n", record) );
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200555 donerec = true;
Brian Kernighan87b94932012-12-22 10:35:39 -0500556}
557
558int errorflag = 0;
559
560void yyerror(const char *s)
561{
562 SYNTAX("%s", s);
563}
564
565void SYNTAX(const char *fmt, ...)
566{
567 extern char *cmdname, *curfname;
568 static int been_here = 0;
569 va_list varg;
570
571 if (been_here++ > 2)
572 return;
573 fprintf(stderr, "%s: ", cmdname);
574 va_start(varg, fmt);
575 vfprintf(stderr, fmt, varg);
576 va_end(varg);
577 fprintf(stderr, " at source line %d", lineno);
578 if (curfname != NULL)
579 fprintf(stderr, " in function %s", curfname);
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200580 if (compile_time == COMPILING && cursource() != NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -0500581 fprintf(stderr, " source file %s", cursource());
582 fprintf(stderr, "\n");
583 errorflag = 2;
584 eprint();
585}
586
587void fpecatch(int n)
588{
589 FATAL("floating point exception %d", n);
590}
591
592extern int bracecnt, brackcnt, parencnt;
593
594void bracecheck(void)
595{
596 int c;
597 static int beenhere = 0;
598
599 if (beenhere++)
600 return;
601 while ((c = input()) != EOF && c != '\0')
602 bclass(c);
603 bcheck2(bracecnt, '{', '}');
604 bcheck2(brackcnt, '[', ']');
605 bcheck2(parencnt, '(', ')');
606}
607
608void bcheck2(int n, int c1, int c2)
609{
610 if (n == 1)
611 fprintf(stderr, "\tmissing %c\n", c2);
612 else if (n > 1)
613 fprintf(stderr, "\t%d missing %c's\n", n, c2);
614 else if (n == -1)
615 fprintf(stderr, "\textra %c\n", c2);
616 else if (n < -1)
617 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
618}
619
620void FATAL(const char *fmt, ...)
621{
622 extern char *cmdname;
623 va_list varg;
624
625 fflush(stdout);
626 fprintf(stderr, "%s: ", cmdname);
627 va_start(varg, fmt);
628 vfprintf(stderr, fmt, varg);
629 va_end(varg);
630 error();
631 if (dbg > 1) /* core dump if serious debugging on */
632 abort();
633 exit(2);
634}
635
636void WARNING(const char *fmt, ...)
637{
638 extern char *cmdname;
639 va_list varg;
640
641 fflush(stdout);
642 fprintf(stderr, "%s: ", cmdname);
643 va_start(varg, fmt);
644 vfprintf(stderr, fmt, varg);
645 va_end(varg);
646 error();
647}
648
649void error()
650{
651 extern Node *curnode;
652
653 fprintf(stderr, "\n");
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200654 if (compile_time != ERROR_PRINTING) {
655 if (NR && *NR > 0) {
656 fprintf(stderr, " input record number %d", (int) (*FNR));
657 if (strcmp(*FILENAME, "-") != 0)
658 fprintf(stderr, ", file %s", *FILENAME);
659 fprintf(stderr, "\n");
660 }
661 if (curnode)
662 fprintf(stderr, " source line number %d", curnode->lineno);
663 else if (lineno)
664 fprintf(stderr, " source line number %d", lineno);
Brian Kernighan87b94932012-12-22 10:35:39 -0500665 }
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200666
667 if (compile_time == COMPILING && cursource() != NULL)
Brian Kernighan87b94932012-12-22 10:35:39 -0500668 fprintf(stderr, " source file %s", cursource());
669 fprintf(stderr, "\n");
670 eprint();
671}
672
673void eprint(void) /* try to print context around error */
674{
675 char *p, *q;
676 int c;
677 static int been_here = 0;
678 extern char ebuf[], *ep;
679
Arnold D. Robbins108224b2019-11-10 21:19:18 +0200680 if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
Brian Kernighan87b94932012-12-22 10:35:39 -0500681 return;
Brian Kernighan3ed9e242018-08-15 10:45:03 -0400682 if (ebuf == ep)
683 return;
Brian Kernighan87b94932012-12-22 10:35:39 -0500684 p = ep - 1;
685 if (p > ebuf && *p == '\n')
686 p--;
687 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
688 ;
689 while (*p == '\n')
690 p++;
691 fprintf(stderr, " context is\n\t");
692 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
693 ;
694 for ( ; p < q; p++)
695 if (*p)
696 putc(*p, stderr);
697 fprintf(stderr, " >>> ");
698 for ( ; p < ep; p++)
699 if (*p)
700 putc(*p, stderr);
701 fprintf(stderr, " <<< ");
702 if (*ep)
703 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
704 putc(c, stderr);
705 bclass(c);
706 }
707 putc('\n', stderr);
708 ep = ebuf;
709}
710
711void bclass(int c)
712{
713 switch (c) {
714 case '{': bracecnt++; break;
715 case '}': bracecnt--; break;
716 case '[': brackcnt++; break;
717 case ']': brackcnt--; break;
718 case '(': parencnt++; break;
719 case ')': parencnt--; break;
720 }
721}
722
723double errcheck(double x, const char *s)
724{
725
726 if (errno == EDOM) {
727 errno = 0;
728 WARNING("%s argument out of domain", s);
729 x = 1;
730 } else if (errno == ERANGE) {
731 errno = 0;
732 WARNING("%s result out of range", s);
733 x = 1;
734 }
735 return x;
736}
737
738int isclvar(const char *s) /* is s of form var=something ? */
739{
740 const char *os = s;
741
742 if (!isalpha((uschar) *s) && *s != '_')
743 return 0;
744 for ( ; *s; s++)
745 if (!(isalnum((uschar) *s) || *s == '_'))
746 break;
Arnold D. Robbins4189ef52019-05-29 21:04:18 +0300747 return *s == '=' && s > os;
Brian Kernighan87b94932012-12-22 10:35:39 -0500748}
749
750/* strtod is supposed to be a proper test of what's a valid number */
751/* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
752/* wrong: violates 4.10.1.4 of ansi C standard */
753
754#include <math.h>
755int is_number(const char *s)
756{
757 double r;
758 char *ep;
759 errno = 0;
760 r = strtod(s, &ep);
761 if (ep == s || r == HUGE_VAL || errno == ERANGE)
762 return 0;
763 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
764 ep++;
765 if (*ep == '\0')
766 return 1;
767 else
768 return 0;
769}