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