xref: /dragonfly/contrib/awk/lib.c (revision e2ee60a4f1757f9ded9e1041053222b631f387b6)
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
4 
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
14 
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
24 
25 #define DEBUG
26 #include <stdio.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <limits.h>
34 #include <math.h>
35 #include "awk.h"
36 
37 extern int u8_nextlen(const char *s);
38 
39 char      EMPTY[] = { '\0' };
40 FILE      *infile   = NULL;
41 bool      innew;              /* true = infile has not been read by readrec */
42 char      *file     = EMPTY;
43 char      *record;
44 int       recsize   = RECSIZE;
45 char      *fields;
46 int       fieldssize = RECSIZE;
47 
48 Cell      **fldtab; /* pointers to Cells */
49 static size_t       len_inputFS = 0;
50 static char         *inputFS = NULL; /* FS at time of input, for field splitting */
51 
52 #define   MAXFLD    2
53 int       nfields   = MAXFLD; /* last allocated slot for $i */
54 
55 bool      donefld;  /* true = implies rec broken into fields */
56 bool      donerec;  /* true = record is valid (no flds have changed) */
57 
58 int       lastfld   = 0;      /* last used field */
59 int       argno     = 1;      /* current input argument number */
60 extern    Awkfloat *ARGC;
61 
62 static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
63 static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
64 
recinit(unsigned int n)65 void recinit(unsigned int n)
66 {
67           if ( (record = (char *) malloc(n)) == NULL
68             || (fields = (char *) malloc(n+1)) == NULL
69             || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL
70             || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL)
71                     FATAL("out of space for $0 and fields");
72           *record = '\0';
73           *fldtab[0] = dollar0;
74           fldtab[0]->sval = record;
75           fldtab[0]->nval = tostring("0");
76           makefields(1, nfields);
77 }
78 
makefields(int n1,int n2)79 void makefields(int n1, int n2)                   /* create $n1..$n2 inclusive */
80 {
81           char temp[50];
82           int i;
83 
84           for (i = n1; i <= n2; i++) {
85                     fldtab[i] = (Cell *) malloc(sizeof(**fldtab));
86                     if (fldtab[i] == NULL)
87                               FATAL("out of space in makefields %d", i);
88                     *fldtab[i] = dollar1;
89                     snprintf(temp, sizeof(temp), "%d", i);
90                     fldtab[i]->nval = tostring(temp);
91           }
92 }
93 
initgetrec(void)94 void initgetrec(void)
95 {
96           int i;
97           char *p;
98 
99           for (i = 1; i < *ARGC; i++) {
100                     p = getargv(i); /* find 1st real filename */
101                     if (p == NULL || *p == '\0') {  /* deleted or zapped */
102                               argno++;
103                               continue;
104                     }
105                     if (!isclvar(p)) {
106                               setsval(lookup("FILENAME", symtab), p);
107                               return;
108                     }
109                     setclvar(p);        /* a commandline assignment before filename */
110                     argno++;
111           }
112           infile = stdin;               /* no filenames, so use stdin */
113           innew = true;
114 }
115 
116 /*
117  * POSIX specifies that fields are supposed to be evaluated as if they were
118  * split using the value of FS at the time that the record's value ($0) was
119  * read.
120  *
121  * Since field-splitting is done lazily, we save the current value of FS
122  * whenever a new record is read in (implicitly or via getline), or when
123  * a new value is assigned to $0.
124  */
savefs(void)125 void savefs(void)
126 {
127           size_t len;
128           if ((len = strlen(getsval(fsloc))) < len_inputFS) {
129                     strcpy(inputFS, *FS);         /* for subsequent field splitting */
130                     return;
131           }
132 
133           len_inputFS = len + 1;
134           inputFS = (char *) realloc(inputFS, len_inputFS);
135           if (inputFS == NULL)
136                     FATAL("field separator %.10s... is too long", *FS);
137           memcpy(inputFS, *FS, len_inputFS);
138 }
139 
140 static bool firsttime = true;
141 
getrec(char ** pbuf,int * pbufsize,bool isrecord)142 int getrec(char **pbuf, int *pbufsize, bool isrecord)       /* get next input record */
143 {                             /* note: cares whether buf == record */
144           int c;
145           char *buf = *pbuf;
146           uschar saveb0;
147           int bufsize = *pbufsize, savebufsize = bufsize;
148 
149           if (firsttime) {
150                     firsttime = false;
151                     initgetrec();
152           }
153           DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
154                     *RS, *FS, *ARGC, *FILENAME);
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                               innew = true;
177                               setfval(fnrloc, 0.0);
178                     }
179                     c = readrec(&buf, &bufsize, infile, innew);
180                     if (innew)
181                               innew = false;
182                     if (c != 0 || buf[0] != '\0') {         /* normal record */
183                               if (isrecord) {
184                                         double result;
185 
186                                         if (freeable(fldtab[0]))
187                                                   xfree(fldtab[0]->sval);
188                                         fldtab[0]->sval = buf;        /* buf == record */
189                                         fldtab[0]->tval = REC | STR | DONTFREE;
190                                         if (is_number(fldtab[0]->sval, & result)) {
191                                                   fldtab[0]->fval = result;
192                                                   fldtab[0]->tval |= NUM;
193                                         }
194                                         donefld = false;
195                                         donerec = true;
196                                         savefs();
197                               }
198                               setfval(nrloc, nrloc->fval+1);
199                               setfval(fnrloc, fnrloc->fval+1);
200                               *pbuf = buf;
201                               *pbufsize = bufsize;
202                               return 1;
203                     }
204                     /* EOF arrived on this file; set up next */
205                     if (infile != stdin)
206                               fclose(infile);
207                     infile = NULL;
208                     argno++;
209           }
210           buf[0] = saveb0;
211           *pbuf = buf;
212           *pbufsize = savebufsize;
213           return 0; /* true end of file */
214 }
215 
nextfile(void)216 void nextfile(void)
217 {
218           if (infile != NULL && infile != stdin)
219                     fclose(infile);
220           infile = NULL;
221           argno++;
222 }
223 
224 extern int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag);
225 
readrec(char ** pbuf,int * pbufsize,FILE * inf,bool newflag)226 int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag)      /* read one record into buf */
227 {
228           int sep, c, isrec; // POTENTIAL BUG? isrec is a macro in awk.h
229           char *rr = *pbuf, *buf = *pbuf;
230           int bufsize = *pbufsize;
231           char *rs = getsval(rsloc);
232 
233           if (CSV) {
234                     c = readcsvrec(pbuf, pbufsize, inf, newflag);
235                     isrec = (c == EOF && rr == buf) ? false : true;
236           } else if (*rs && rs[1]) {
237                     bool found;
238 
239                     memset(buf, 0, bufsize);
240                     fa *pfa = makedfa(rs, 1);
241                     if (newflag)
242                               found = fnematch(pfa, inf, &buf, &bufsize, recsize);
243                     else {
244                               int tempstat = pfa->initstat;
245                               pfa->initstat = 2;
246                               found = fnematch(pfa, inf, &buf, &bufsize, recsize);
247                               pfa->initstat = tempstat;
248                     }
249                     if (found)
250                               setptr(patbeg, '\0');
251                     isrec = (found == 0 && *buf == '\0') ? false : true;
252 
253           } else {
254                     if ((sep = *rs) == 0) {
255                               sep = '\n';
256                               while ((c=getc(inf)) == '\n' && c != EOF)         /* skip leading \n's */
257                                         ;
258                               if (c != EOF)
259                                         ungetc(c, inf);
260                     }
261                     for (rr = buf; ; ) {
262                               for (; (c=getc(inf)) != sep && c != EOF; ) {
263                                         if (rr-buf+1 > bufsize)
264                                                   if (!adjbuf(&buf, &bufsize, 1+rr-buf,
265                                                       recsize, &rr, "readrec 1"))
266                                                             FATAL("input record `%.30s...' too long", buf);
267                                         *rr++ = c;
268                               }
269                               if (*rs == sep || c == EOF)
270                                         break;
271                               if ((c = getc(inf)) == '\n' || c == EOF)          /* 2 in a row */
272                                         break;
273                               if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
274                                   "readrec 2"))
275                                         FATAL("input record `%.30s...' too long", buf);
276                               *rr++ = '\n';
277                               *rr++ = c;
278                     }
279                     if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
280                               FATAL("input record `%.30s...' too long", buf);
281                     *rr = 0;
282                     isrec = (c == EOF && rr == buf) ? false : true;
283           }
284           *pbuf = buf;
285           *pbufsize = bufsize;
286           DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
287           return isrec;
288 }
289 
290 
291 /*******************
292  * loose ends here:
293  *   \r\n should become \n
294  *   what about bare \r?  Excel uses that for embedded newlines
295  *   can't have "" in unquoted fields, according to RFC 4180
296 */
297 
298 
readcsvrec(char ** pbuf,int * pbufsize,FILE * inf,bool newflag)299 int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* csv can have \n's */
300 {                             /* so read a complete record that might be multiple lines */
301           int sep, c;
302           char *rr = *pbuf, *buf = *pbuf;
303           int bufsize = *pbufsize;
304           bool in_quote = false;
305 
306           sep = '\n'; /* the only separator; have to skip over \n embedded in "..." */
307           rr = buf;
308           while ((c = getc(inf)) != EOF) {
309                     if (c == sep) {
310                               if (! in_quote)
311                                         break;
312                               if (rr > buf && rr[-1] == '\r')         // remove \r if was \r\n
313                                         rr--;
314                     }
315 
316                     if (rr-buf+1 > bufsize)
317                               if (!adjbuf(&buf, &bufsize, 1+rr-buf,
318                                   recsize, &rr, "readcsvrec 1"))
319                                         FATAL("input record `%.30s...' too long", buf);
320                     *rr++ = c;
321                     if (c == '"')
322                               in_quote = ! in_quote;
323           }
324           if (c == '\n' && rr > buf && rr[-1] == '\r')      // remove \r if was \r\n
325                     rr--;
326 
327           if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readcsvrec 4"))
328                     FATAL("input record `%.30s...' too long", buf);
329           *rr = 0;
330           *pbuf = buf;
331           *pbufsize = bufsize;
332           DPRINTF("readcsvrec saw <%s>, returns %d\n", buf, c);
333           return c;
334 }
335 
getargv(int n)336 char *getargv(int n)          /* get ARGV[n] */
337 {
338           Cell *x;
339           char *s, temp[50];
340           extern Array *ARGVtab;
341 
342           snprintf(temp, sizeof(temp), "%d", n);
343           if (lookup(temp, ARGVtab) == NULL)
344                     return NULL;
345           x = setsymtab(temp, "", 0.0, STR, ARGVtab);
346           s = getsval(x);
347           DPRINTF("getargv(%d) returns |%s|\n", n, s);
348           return s;
349 }
350 
setclvar(char * s)351 void setclvar(char *s)        /* set var=value from s */
352 {
353           char *e, *p;
354           Cell *q;
355           double result;
356 
357 /* commit f3d9187d4e0f02294fb1b0e31152070506314e67 broke T.argv test */
358 /* I don't understand why it was changed. */
359 
360           for (p=s; *p != '='; p++)
361                     ;
362           e = p;
363           *p++ = 0;
364           p = qstring(p, '\0');
365           q = setsymtab(s, p, 0.0, STR, symtab);
366           setsval(q, p);
367           if (is_number(q->sval, & result)) {
368                     q->fval = result;
369                     q->tval |= NUM;
370           }
371           DPRINTF("command line set %s to |%s|\n", s, p);
372           free(p);
373           *e = '=';
374 }
375 
376 
fldbld(void)377 void fldbld(void)   /* create fields from current record */
378 {
379           /* this relies on having fields[] the same length as $0 */
380           /* the fields are all stored in this one array with \0's */
381           /* possibly with a final trailing \0 not associated with any field */
382           char *r, *fr, sep;
383           Cell *p;
384           int i, j, n;
385 
386           if (donefld)
387                     return;
388           if (!isstr(fldtab[0]))
389                     getsval(fldtab[0]);
390           r = fldtab[0]->sval;
391           n = strlen(r);
392           if (n > fieldssize) {
393                     xfree(fields);
394                     if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
395                               FATAL("out of space for fields in fldbld %d", n);
396                     fieldssize = n;
397           }
398           fr = fields;
399           i = 0;    /* number of fields accumulated here */
400           if (inputFS == NULL)          /* make sure we have a copy of FS */
401                     savefs();
402           if (!CSV && strlen(inputFS) > 1) {      /* it's a regular expression */
403                     i = refldbld(r, inputFS);
404           } else if (!CSV && (sep = *inputFS) == ' ') {     /* default whitespace */
405                     for (i = 0; ; ) {
406                               while (*r == ' ' || *r == '\t' || *r == '\n')
407                                         r++;
408                               if (*r == 0)
409                                         break;
410                               i++;
411                               if (i > nfields)
412                                         growfldtab(i);
413                               if (freeable(fldtab[i]))
414                                         xfree(fldtab[i]->sval);
415                               fldtab[i]->sval = fr;
416                               fldtab[i]->tval = FLD | STR | DONTFREE;
417                               do
418                                         *fr++ = *r++;
419                               while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
420                               *fr++ = 0;
421                     }
422                     *fr = 0;
423           } else if (CSV) {   /* CSV processing.  no error handling */
424                     if (*r != 0) {
425                               for (;;) {
426                                         i++;
427                                         if (i > nfields)
428                                                   growfldtab(i);
429                                         if (freeable(fldtab[i]))
430                                                   xfree(fldtab[i]->sval);
431                                         fldtab[i]->sval = fr;
432                                         fldtab[i]->tval = FLD | STR | DONTFREE;
433                                         if (*r == '"' ) { /* start of "..." */
434                                                   for (r++ ; *r != '\0'; ) {
435                                                             if (*r == '"' && r[1] != '\0' && r[1] == '"') {
436                                                                       r += 2; /* doubled quote */
437                                                                       *fr++ = '"';
438                                                             } else if (*r == '"' && (r[1] == '\0' || r[1] == ',')) {
439                                                                       r++; /* skip over closing quote */
440                                                                       break;
441                                                             } else {
442                                                                       *fr++ = *r++;
443                                                             }
444                                                   }
445                                                   *fr++ = 0;
446                                         } else {  /* unquoted field */
447                                                   while (*r != ',' && *r != '\0')
448                                                             *fr++ = *r++;
449                                                   *fr++ = 0;
450                                         }
451                                         if (*r++ == 0)
452                                                   break;
453 
454                               }
455                     }
456                     *fr = 0;
457           } else if ((sep = *inputFS) == 0) {     /* new: FS="" => 1 char/field */
458                     for (i = 0; *r != '\0'; ) {
459                               char buf[10];
460                               i++;
461                               if (i > nfields)
462                                         growfldtab(i);
463                               if (freeable(fldtab[i]))
464                                         xfree(fldtab[i]->sval);
465                               n = u8_nextlen(r);
466                               for (j = 0; j < n; j++)
467                                         buf[j] = *r++;
468                               buf[j] = '\0';
469                               fldtab[i]->sval = tostring(buf);
470                               fldtab[i]->tval = FLD | STR;
471                     }
472                     *fr = 0;
473           } else if (*r != 0) {         /* if 0, it's a null field */
474                     /* subtle case: if length(FS) == 1 && length(RS > 0)
475                      * \n is NOT a field separator (cf awk book 61,84).
476                      * this variable is tested in the inner while loop.
477                      */
478                     int rtest = '\n';  /* normal case */
479                     if (strlen(*RS) > 0)
480                               rtest = '\0';
481                     for (;;) {
482                               i++;
483                               if (i > nfields)
484                                         growfldtab(i);
485                               if (freeable(fldtab[i]))
486                                         xfree(fldtab[i]->sval);
487                               fldtab[i]->sval = fr;
488                               fldtab[i]->tval = FLD | STR | DONTFREE;
489                               while (*r != sep && *r != rtest && *r != '\0')    /* \n is always a separator */
490                                         *fr++ = *r++;
491                               *fr++ = 0;
492                               if (*r++ == 0)
493                                         break;
494                     }
495                     *fr = 0;
496           }
497           if (i > nfields)
498                     FATAL("record `%.30s...' has too many fields; can't happen", r);
499           cleanfld(i+1, lastfld);       /* clean out junk from previous record */
500           lastfld = i;
501           donefld = true;
502           for (j = 1; j <= lastfld; j++) {
503                     double result;
504 
505                     p = fldtab[j];
506                     if(is_number(p->sval, & result)) {
507                               p->fval = result;
508                               p->tval |= NUM;
509                     }
510           }
511           setfval(nfloc, (Awkfloat) lastfld);
512           donerec = true; /* restore */
513           if (dbg) {
514                     for (j = 0; j <= lastfld; j++) {
515                               p = fldtab[j];
516                               printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
517                     }
518           }
519 }
520 
cleanfld(int n1,int n2)521 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
522 {                                       /* nvals remain intact */
523           Cell *p;
524           int i;
525 
526           for (i = n1; i <= n2; i++) {
527                     p = fldtab[i];
528                     if (freeable(p))
529                               xfree(p->sval);
530                     p->sval = EMPTY,
531                     p->tval = FLD | STR | DONTFREE;
532           }
533 }
534 
newfld(int n)535 void newfld(int n)  /* add field n after end of existing lastfld */
536 {
537           if (n > nfields)
538                     growfldtab(n);
539           cleanfld(lastfld+1, n);
540           lastfld = n;
541           setfval(nfloc, (Awkfloat) n);
542 }
543 
setlastfld(int n)544 void setlastfld(int n)        /* set lastfld cleaning fldtab cells if necessary */
545 {
546           if (n < 0)
547                     FATAL("cannot set NF to a negative value");
548           if (n > nfields)
549                     growfldtab(n);
550 
551           if (lastfld < n)
552               cleanfld(lastfld+1, n);
553           else
554               cleanfld(n+1, lastfld);
555 
556           lastfld = n;
557 }
558 
fieldadr(int n)559 Cell *fieldadr(int n)         /* get nth field */
560 {
561           if (n < 0)
562                     FATAL("trying to access out of range field %d", n);
563           if (n > nfields)    /* fields after NF are empty */
564                     growfldtab(n);      /* but does not increase NF */
565           return(fldtab[n]);
566 }
567 
growfldtab(int n)568 void growfldtab(int n)        /* make new fields up to at least $n */
569 {
570           int nf = 2 * nfields;
571           size_t s;
572 
573           if (n > nf)
574                     nf = n;
575           s = (nf+1) * (sizeof (struct Cell *));  /* freebsd: how much do we need? */
576           if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
577                     fldtab = (Cell **) realloc(fldtab, s);
578           else                                              /* overflow sizeof int */
579                     xfree(fldtab);      /* make it null */
580           if (fldtab == NULL)
581                     FATAL("out of space creating %d fields", nf);
582           makefields(nfields+1, nf);
583           nfields = nf;
584 }
585 
refldbld(const char * rec,const char * fs)586 int refldbld(const char *rec, const char *fs)     /* build fields from reg expr in FS */
587 {
588           /* this relies on having fields[] the same length as $0 */
589           /* the fields are all stored in this one array with \0's */
590           char *fr;
591           int i, tempstat, n;
592           fa *pfa;
593 
594           n = strlen(rec);
595           if (n > fieldssize) {
596                     xfree(fields);
597                     if ((fields = (char *) malloc(n+1)) == NULL)
598                               FATAL("out of space for fields in refldbld %d", n);
599                     fieldssize = n;
600           }
601           fr = fields;
602           *fr = '\0';
603           if (*rec == '\0')
604                     return 0;
605           pfa = makedfa(fs, 1);
606           DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
607           tempstat = pfa->initstat;
608           for (i = 1; ; i++) {
609                     if (i > nfields)
610                               growfldtab(i);
611                     if (freeable(fldtab[i]))
612                               xfree(fldtab[i]->sval);
613                     fldtab[i]->tval = FLD | STR | DONTFREE;
614                     fldtab[i]->sval = fr;
615                     DPRINTF("refldbld: i=%d\n", i);
616                     if (nematch(pfa, rec)) {
617                               pfa->initstat = 2;  /* horrible coupling to b.c */
618                               DPRINTF("match %s (%d chars)\n", patbeg, patlen);
619                               strncpy(fr, rec, patbeg-rec);
620                               fr += patbeg - rec + 1;
621                               *(fr-1) = '\0';
622                               rec = patbeg + patlen;
623                     } else {
624                               DPRINTF("no match %s\n", rec);
625                               strcpy(fr, rec);
626                               pfa->initstat = tempstat;
627                               break;
628                     }
629           }
630           return i;
631 }
632 
recbld(void)633 void recbld(void)   /* create $0 from $1..$NF if necessary */
634 {
635           int i;
636           char *r, *p;
637           char *sep = getsval(ofsloc);
638 
639           if (donerec)
640                     return;
641           r = record;
642           for (i = 1; i <= *NF; i++) {
643                     p = getsval(fldtab[i]);
644                     if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
645                               FATAL("created $0 `%.30s...' too long", record);
646                     while ((*r = *p++) != 0)
647                               r++;
648                     if (i < *NF) {
649                               if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
650                                         FATAL("created $0 `%.30s...' too long", record);
651                               for (p = sep; (*r = *p++) != 0; )
652                                         r++;
653                     }
654           }
655           if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
656                     FATAL("built giant record `%.30s...'", record);
657           *r = '\0';
658           DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
659 
660           if (freeable(fldtab[0]))
661                     xfree(fldtab[0]->sval);
662           fldtab[0]->tval = REC | STR | DONTFREE;
663           fldtab[0]->sval = record;
664 
665           DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
666           DPRINTF("recbld = |%s|\n", record);
667           donerec = true;
668 }
669 
670 int       errorflag = 0;
671 
yyerror(const char * s)672 void yyerror(const char *s)
673 {
674           SYNTAX("%s", s);
675 }
676 
SYNTAX(const char * fmt,...)677 void SYNTAX(const char *fmt, ...)
678 {
679           extern char *cmdname, *curfname;
680           static int been_here = 0;
681           va_list varg;
682 
683           if (been_here++ > 2)
684                     return;
685           fprintf(stderr, "%s: ", cmdname);
686           va_start(varg, fmt);
687           vfprintf(stderr, fmt, varg);
688           va_end(varg);
689           fprintf(stderr, " at source line %d", lineno);
690           if (curfname != NULL)
691                     fprintf(stderr, " in function %s", curfname);
692           if (compile_time == COMPILING && cursource() != NULL)
693                     fprintf(stderr, " source file %s", cursource());
694           fprintf(stderr, "\n");
695           errorflag = 2;
696           eprint();
697 }
698 
699 extern int bracecnt, brackcnt, parencnt;
700 
bracecheck(void)701 void bracecheck(void)
702 {
703           int c;
704           static int beenhere = 0;
705 
706           if (beenhere++)
707                     return;
708           while ((c = input()) != EOF && c != '\0')
709                     bclass(c);
710           bcheck2(bracecnt, '{', '}');
711           bcheck2(brackcnt, '[', ']');
712           bcheck2(parencnt, '(', ')');
713 }
714 
bcheck2(int n,int c1,int c2)715 void bcheck2(int n, int c1, int c2)
716 {
717           if (n == 1)
718                     fprintf(stderr, "\tmissing %c\n", c2);
719           else if (n > 1)
720                     fprintf(stderr, "\t%d missing %c's\n", n, c2);
721           else if (n == -1)
722                     fprintf(stderr, "\textra %c\n", c2);
723           else if (n < -1)
724                     fprintf(stderr, "\t%d extra %c's\n", -n, c2);
725 }
726 
FATAL(const char * fmt,...)727 void FATAL(const char *fmt, ...)
728 {
729           extern char *cmdname;
730           va_list varg;
731 
732           fflush(stdout);
733           fprintf(stderr, "%s: ", cmdname);
734           va_start(varg, fmt);
735           vfprintf(stderr, fmt, varg);
736           va_end(varg);
737           error();
738           if (dbg > 1)                  /* core dump if serious debugging on */
739                     abort();
740           exit(2);
741 }
742 
WARNING(const char * fmt,...)743 void WARNING(const char *fmt, ...)
744 {
745           extern char *cmdname;
746           va_list varg;
747 
748           fflush(stdout);
749           fprintf(stderr, "%s: ", cmdname);
750           va_start(varg, fmt);
751           vfprintf(stderr, fmt, varg);
752           va_end(varg);
753           error();
754 }
755 
error()756 void error()
757 {
758           extern Node *curnode;
759 
760           fprintf(stderr, "\n");
761           if (compile_time != ERROR_PRINTING) {
762                     if (NR && *NR > 0) {
763                               fprintf(stderr, " input record number %d", (int) (*FNR));
764                               if (strcmp(*FILENAME, "-") != 0)
765                                         fprintf(stderr, ", file %s", *FILENAME);
766                               fprintf(stderr, "\n");
767                     }
768                     if (curnode)
769                               fprintf(stderr, " source line number %d", curnode->lineno);
770                     else if (lineno)
771                               fprintf(stderr, " source line number %d", lineno);
772                     if (compile_time == COMPILING && cursource() != NULL)
773                               fprintf(stderr, " source file %s", cursource());
774                     fprintf(stderr, "\n");
775                     eprint();
776           }
777 }
778 
eprint(void)779 void eprint(void)   /* try to print context around error */
780 {
781           char *p, *q;
782           int c;
783           static int been_here = 0;
784           extern char ebuf[], *ep;
785 
786           if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
787                     return;
788           if (ebuf == ep)
789                     return;
790           p = ep - 1;
791           if (p > ebuf && *p == '\n')
792                     p--;
793           for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
794                     ;
795           while (*p == '\n')
796                     p++;
797           fprintf(stderr, " context is\n\t");
798           for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
799                     ;
800           for ( ; p < q; p++)
801                     if (*p)
802                               putc(*p, stderr);
803           fprintf(stderr, " >>> ");
804           for ( ; p < ep; p++)
805                     if (*p)
806                               putc(*p, stderr);
807           fprintf(stderr, " <<< ");
808           if (*ep)
809                     while ((c = input()) != '\n' && c != '\0' && c != EOF) {
810                               putc(c, stderr);
811                               bclass(c);
812                     }
813           putc('\n', stderr);
814           ep = ebuf;
815 }
816 
bclass(int c)817 void bclass(int c)
818 {
819           switch (c) {
820           case '{': bracecnt++; break;
821           case '}': bracecnt--; break;
822           case '[': brackcnt++; break;
823           case ']': brackcnt--; break;
824           case '(': parencnt++; break;
825           case ')': parencnt--; break;
826           }
827 }
828 
errcheck(double x,const char * s)829 double errcheck(double x, const char *s)
830 {
831 
832           if (errno == EDOM) {
833                     errno = 0;
834                     WARNING("%s argument out of domain", s);
835                     x = 1;
836           } else if (errno == ERANGE) {
837                     errno = 0;
838                     WARNING("%s result out of range", s);
839                     x = 1;
840           }
841           return x;
842 }
843 
isclvar(const char * s)844 int isclvar(const char *s)    /* is s of form var=something ? */
845 {
846           const char *os = s;
847 
848           if (!isalpha((int) *s) && *s != '_')
849                     return 0;
850           for ( ; *s; s++)
851                     if (!(isalnum((int) *s) || *s == '_'))
852                               break;
853           return *s == '=' && s > os;
854 }
855 
856 /* strtod is supposed to be a proper test of what's a valid number */
857 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
858 /* wrong: violates 4.10.1.4 of ansi C standard */
859 
860 /* well, not quite. As of C99, hex floating point is allowed. so this is
861  * a bit of a mess. We work around the mess by checking for a hexadecimal
862  * value and disallowing it. Similarly, we now follow gawk and allow only
863  * +nan, -nan, +inf, and -inf for NaN and infinity values.
864  */
865 
866 /*
867  * This routine now has a more complicated interface, the main point
868  * being to avoid the double conversion of a string to double, and
869  * also to convey out, if requested, the information that the numeric
870  * value was a leading string or is all of the string. The latter bit
871  * is used in getfval().
872  */
873 
is_valid_number(const char * s,bool trailing_stuff_ok,bool * no_trailing,double * result)874 bool is_valid_number(const char *s, bool trailing_stuff_ok,
875                               bool *no_trailing, double *result)
876 {
877           double r;
878           char *ep;
879           bool retval = false;
880           bool is_nan = false;
881           bool is_inf = false;
882 
883           if (no_trailing)
884                     *no_trailing = false;
885 
886           while (isspace((int) *s))
887                     s++;
888 
889           /* no hex floating point, sorry */
890           if (s[0] == '0' && tolower(s[1]) == 'x')
891                     return false;
892 
893           /* allow +nan, -nan, +inf, -inf, any other letter, no */
894           if (s[0] == '+' || s[0] == '-') {
895                     is_nan = (strncasecmp(s+1, "nan", 3) == 0);
896                     is_inf = (strncasecmp(s+1, "inf", 3) == 0);
897                     if ((is_nan || is_inf)
898                         && (isspace((int) s[4]) || s[4] == '\0'))
899                               goto convert;
900                     else if (! isdigit(s[1]) && s[1] != '.')
901                               return false;
902           }
903           else if (! isdigit(s[0]) && s[0] != '.')
904                     return false;
905 
906 convert:
907           errno = 0;
908           r = strtod(s, &ep);
909           if (ep == s || errno == ERANGE)
910                     return false;
911 
912           if (isnan(r) && s[0] == '-' && signbit(r) == 0)
913                     r = -r;
914 
915           if (result != NULL)
916                     *result = r;
917 
918           /*
919            * check for trailing stuff
920            */
921           while (isspace((int) *ep))
922                     ep++;
923 
924           if (no_trailing != NULL)
925                     *no_trailing = (*ep == '\0');
926 
927         /* return true if found the end, or trailing stuff is allowed */
928           retval = *ep == '\0' || trailing_stuff_ok;
929 
930           return retval;
931 }
932