1 /* $OpenBSD: lib.c,v 1.51 2023/09/17 14:49:44 millert Exp $ */ 2 /**************************************************************** 3 Copyright (C) Lucent Technologies 1997 4 All Rights Reserved 5 6 Permission to use, copy, modify, and distribute this software and 7 its documentation for any purpose and without fee is hereby 8 granted, provided that the above copyright notice appear in all 9 copies and that both that the copyright notice and this 10 permission notice and warranty disclaimer appear in supporting 11 documentation, and that the name Lucent Technologies or any of 12 its entities not be used in advertising or publicity pertaining 13 to distribution of the software without specific, written prior 14 permission. 15 16 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 17 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 18 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY 19 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 20 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 21 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 22 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 23 THIS SOFTWARE. 24 ****************************************************************/ 25 26 #define DEBUG 27 #include <stdio.h> 28 #include <string.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 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 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 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 */ 125 void savefs(void) 126 { 127 size_t len = strlen(getsval(fsloc)); 128 if (len >= len_inputFS) { 129 len_inputFS = len + 1; 130 inputFS = (char *) realloc(inputFS, len_inputFS); 131 if (inputFS == NULL) 132 FATAL("field separator %.10s... is too long", *FS); 133 } 134 if (strlcpy(inputFS, *FS, len_inputFS) >= len_inputFS) 135 FATAL("field separator %.10s... is too long", *FS); 136 } 137 138 static bool firsttime = true; 139 140 int getrec(char **pbuf, int *pbufsize, bool isrecord) /* get next input record */ 141 { /* note: cares whether buf == record */ 142 int c; 143 char *buf = *pbuf; 144 uschar saveb0; 145 int bufsize = *pbufsize, savebufsize = bufsize; 146 147 if (firsttime) { 148 firsttime = false; 149 initgetrec(); 150 } 151 DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", 152 *RS, *FS, *ARGC, *FILENAME); 153 saveb0 = buf[0]; 154 buf[0] = 0; 155 while (argno < *ARGC || infile == stdin) { 156 DPRINTF("argno=%d, file=|%s|\n", argno, file); 157 if (infile == NULL) { /* have to open a new file */ 158 file = getargv(argno); 159 if (file == NULL || *file == '\0') { /* deleted or zapped */ 160 argno++; 161 continue; 162 } 163 if (isclvar(file)) { /* a var=value arg */ 164 setclvar(file); 165 argno++; 166 continue; 167 } 168 *FILENAME = file; 169 DPRINTF("opening file %s\n", file); 170 if (*file == '-' && *(file+1) == '\0') 171 infile = stdin; 172 else if ((infile = fopen(file, "r")) == NULL) 173 FATAL("can't open file %s", file); 174 innew = true; 175 setfval(fnrloc, 0.0); 176 } 177 c = readrec(&buf, &bufsize, infile, innew); 178 if (innew) 179 innew = false; 180 if (c != 0 || buf[0] != '\0') { /* normal record */ 181 if (isrecord) { 182 double result; 183 184 if (freeable(fldtab[0])) 185 xfree(fldtab[0]->sval); 186 fldtab[0]->sval = buf; /* buf == record */ 187 fldtab[0]->tval = REC | STR | DONTFREE; 188 if (is_number(fldtab[0]->sval, & result)) { 189 fldtab[0]->fval = result; 190 fldtab[0]->tval |= NUM; 191 } 192 donefld = false; 193 donerec = true; 194 savefs(); 195 } 196 setfval(nrloc, nrloc->fval+1); 197 setfval(fnrloc, fnrloc->fval+1); 198 *pbuf = buf; 199 *pbufsize = bufsize; 200 return 1; 201 } 202 /* EOF arrived on this file; set up next */ 203 if (infile != stdin) 204 fclose(infile); 205 infile = NULL; 206 argno++; 207 } 208 buf[0] = saveb0; 209 *pbuf = buf; 210 *pbufsize = savebufsize; 211 return 0; /* true end of file */ 212 } 213 214 void nextfile(void) 215 { 216 if (infile != NULL && infile != stdin) 217 fclose(infile); 218 infile = NULL; 219 argno++; 220 } 221 222 extern int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag); 223 224 int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* read one record into buf */ 225 { 226 int sep, c, isrec; // POTENTIAL BUG? isrec is a macro in awk.h 227 char *rr = *pbuf, *buf = *pbuf; 228 int bufsize = *pbufsize; 229 char *rs = getsval(rsloc); 230 231 if (CSV) { 232 c = readcsvrec(pbuf, pbufsize, inf, newflag); 233 isrec = (c == EOF && rr == buf) ? false : true; 234 } else if (*rs && rs[1]) { 235 bool found; 236 237 fa *pfa = makedfa(rs, 1); 238 if (newflag) 239 found = fnematch(pfa, inf, &buf, &bufsize, recsize); 240 else { 241 int tempstat = pfa->initstat; 242 pfa->initstat = 2; 243 found = fnematch(pfa, inf, &buf, &bufsize, recsize); 244 pfa->initstat = tempstat; 245 } 246 if (found) 247 setptr(patbeg, '\0'); 248 isrec = (found == 0 && *buf == '\0') ? 0 : 1; 249 } else { 250 if ((sep = *rs) == 0) { 251 sep = '\n'; 252 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ 253 ; 254 if (c != EOF) 255 ungetc(c, inf); 256 } 257 for (rr = buf; ; ) { 258 for (; (c=getc(inf)) != sep && c != EOF; ) { 259 if (rr-buf+1 > bufsize) 260 if (!adjbuf(&buf, &bufsize, 1+rr-buf, 261 recsize, &rr, "readrec 1")) 262 FATAL("input record `%.30s...' too long", buf); 263 *rr++ = c; 264 } 265 if (*rs == sep || c == EOF) 266 break; 267 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ 268 break; 269 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, 270 "readrec 2")) 271 FATAL("input record `%.30s...' too long", buf); 272 *rr++ = '\n'; 273 *rr++ = c; 274 } 275 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) 276 FATAL("input record `%.30s...' too long", buf); 277 *rr = 0; 278 isrec = (c == EOF && rr == buf) ? 0 : 1; 279 } 280 *pbuf = buf; 281 *pbufsize = bufsize; 282 DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec); 283 return isrec; 284 } 285 286 287 /******************* 288 * loose ends here: 289 * \r\n should become \n 290 * what about bare \r? Excel uses that for embedded newlines 291 * can't have "" in unquoted fields, according to RFC 4180 292 */ 293 294 int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* csv can have \n's */ 295 { /* so read a complete record that might be multiple lines */ 296 int sep, c; 297 char *rr = *pbuf, *buf = *pbuf; 298 int bufsize = *pbufsize; 299 bool in_quote = false; 300 301 sep = '\n'; /* the only separator; have to skip over \n embedded in "..." */ 302 rr = buf; 303 while ((c = getc(inf)) != EOF) { 304 if (c == sep) { 305 if (! in_quote) 306 break; 307 if (rr > buf && rr[-1] == '\r') // remove \r if was \r\n 308 rr--; 309 } 310 311 if (rr-buf+1 > bufsize) 312 if (!adjbuf(&buf, &bufsize, 1+rr-buf, 313 recsize, &rr, "readcsvrec 1")) 314 FATAL("input record `%.30s...' too long", buf); 315 *rr++ = c; 316 if (c == '"') 317 in_quote = ! in_quote; 318 } 319 if (c == '\n' && rr > buf && rr[-1] == '\r') // remove \r if was \r\n 320 rr--; 321 322 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readcsvrec 4")) 323 FATAL("input record `%.30s...' too long", buf); 324 *rr = 0; 325 *pbuf = buf; 326 *pbufsize = bufsize; 327 DPRINTF("readcsvrec saw <%s>, returns %d\n", buf, c); 328 return c; 329 } 330 331 char *getargv(int n) /* get ARGV[n] */ 332 { 333 Cell *x; 334 char *s, temp[50]; 335 extern Array *ARGVtab; 336 337 snprintf(temp, sizeof(temp), "%d", n); 338 if (lookup(temp, ARGVtab) == NULL) 339 return NULL; 340 x = setsymtab(temp, "", 0.0, STR, ARGVtab); 341 s = getsval(x); 342 DPRINTF("getargv(%d) returns |%s|\n", n, s); 343 return s; 344 } 345 346 void setclvar(char *s) /* set var=value from s */ 347 { 348 char *e, *p; 349 Cell *q; 350 double result; 351 352 /* commit f3d9187d4e0f02294fb1b0e31152070506314e67 broke T.argv test */ 353 /* I don't understand why it was changed. */ 354 355 for (p=s; *p != '='; p++) 356 ; 357 e = p; 358 *p++ = 0; 359 p = qstring(p, '\0'); 360 q = setsymtab(s, p, 0.0, STR, symtab); 361 setsval(q, p); 362 if (is_number(q->sval, & result)) { 363 q->fval = result; 364 q->tval |= NUM; 365 } 366 DPRINTF("command line set %s to |%s|\n", s, p); 367 free(p); 368 *e = '='; 369 } 370 371 372 void fldbld(void) /* create fields from current record */ 373 { 374 /* this relies on having fields[] the same length as $0 */ 375 /* the fields are all stored in this one array with \0's */ 376 /* possibly with a final trailing \0 not associated with any field */ 377 char *r, *fr, sep; 378 Cell *p; 379 int i, j, n; 380 381 if (donefld) 382 return; 383 if (!isstr(fldtab[0])) 384 getsval(fldtab[0]); 385 r = fldtab[0]->sval; 386 n = strlen(r); 387 if (n > fieldssize) { 388 xfree(fields); 389 if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */ 390 FATAL("out of space for fields in fldbld %d", n); 391 fieldssize = n; 392 } 393 fr = fields; 394 i = 0; /* number of fields accumulated here */ 395 if (inputFS == NULL) /* make sure we have a copy of FS */ 396 savefs(); 397 if (strlen(inputFS) > 1) { /* it's a regular expression */ 398 i = refldbld(r, inputFS); 399 } else if (!CSV && (sep = *inputFS) == ' ') { /* default whitespace */ 400 for (i = 0; ; ) { 401 while (*r == ' ' || *r == '\t' || *r == '\n') 402 r++; 403 if (*r == 0) 404 break; 405 i++; 406 if (i > nfields) 407 growfldtab(i); 408 if (freeable(fldtab[i])) 409 xfree(fldtab[i]->sval); 410 fldtab[i]->sval = fr; 411 fldtab[i]->tval = FLD | STR | DONTFREE; 412 do 413 *fr++ = *r++; 414 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); 415 *fr++ = 0; 416 } 417 *fr = 0; 418 } else if (CSV) { /* CSV processing. no error handling */ 419 if (*r != 0) { 420 for (;;) { 421 i++; 422 if (i > nfields) 423 growfldtab(i); 424 if (freeable(fldtab[i])) 425 xfree(fldtab[i]->sval); 426 fldtab[i]->sval = fr; 427 fldtab[i]->tval = FLD | STR | DONTFREE; 428 if (*r == '"' ) { /* start of "..." */ 429 for (r++ ; *r != '\0'; ) { 430 if (*r == '"' && r[1] != '\0' && r[1] == '"') { 431 r += 2; /* doubled quote */ 432 *fr++ = '"'; 433 } else if (*r == '"' && (r[1] == '\0' || r[1] == ',')) { 434 r++; /* skip over closing quote */ 435 break; 436 } else { 437 *fr++ = *r++; 438 } 439 } 440 *fr++ = 0; 441 } else { /* unquoted field */ 442 while (*r != ',' && *r != '\0') 443 *fr++ = *r++; 444 *fr++ = 0; 445 } 446 if (*r++ == 0) 447 break; 448 449 } 450 } 451 *fr = 0; 452 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ 453 for (i = 0; *r != '\0'; ) { 454 char buf[10]; 455 i++; 456 if (i > nfields) 457 growfldtab(i); 458 if (freeable(fldtab[i])) 459 xfree(fldtab[i]->sval); 460 n = u8_nextlen(r); 461 for (j = 0; j < n; j++) 462 buf[j] = *r++; 463 buf[j] = '\0'; 464 fldtab[i]->sval = tostring(buf); 465 fldtab[i]->tval = FLD | STR; 466 } 467 *fr = 0; 468 } else if (*r != 0) { /* if 0, it's a null field */ 469 /* subtle case: if length(FS) == 1 && length(RS > 0) 470 * \n is NOT a field separator (cf awk book 61,84). 471 * this variable is tested in the inner while loop. 472 */ 473 int rtest = '\n'; /* normal case */ 474 if (strlen(*RS) > 0) 475 rtest = '\0'; 476 for (;;) { 477 i++; 478 if (i > nfields) 479 growfldtab(i); 480 if (freeable(fldtab[i])) 481 xfree(fldtab[i]->sval); 482 fldtab[i]->sval = fr; 483 fldtab[i]->tval = FLD | STR | DONTFREE; 484 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ 485 *fr++ = *r++; 486 *fr++ = 0; 487 if (*r++ == 0) 488 break; 489 } 490 *fr = 0; 491 } 492 if (i > nfields) 493 FATAL("record `%.30s...' has too many fields; can't happen", r); 494 cleanfld(i+1, lastfld); /* clean out junk from previous record */ 495 lastfld = i; 496 donefld = true; 497 for (j = 1; j <= lastfld; j++) { 498 double result; 499 500 p = fldtab[j]; 501 if(is_number(p->sval, & result)) { 502 p->fval = result; 503 p->tval |= NUM; 504 } 505 } 506 setfval(nfloc, (Awkfloat) lastfld); 507 donerec = true; /* restore */ 508 if (dbg) { 509 for (j = 0; j <= lastfld; j++) { 510 p = fldtab[j]; 511 printf("field %d (%s): |%s|\n", j, p->nval, p->sval); 512 } 513 } 514 } 515 516 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ 517 { /* nvals remain intact */ 518 Cell *p; 519 int i; 520 521 for (i = n1; i <= n2; i++) { 522 p = fldtab[i]; 523 if (freeable(p)) 524 xfree(p->sval); 525 p->sval = EMPTY, 526 p->tval = FLD | STR | DONTFREE; 527 } 528 } 529 530 void newfld(int n) /* add field n after end of existing lastfld */ 531 { 532 if (n > nfields) 533 growfldtab(n); 534 cleanfld(lastfld+1, n); 535 lastfld = n; 536 setfval(nfloc, (Awkfloat) n); 537 } 538 539 void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */ 540 { 541 if (n < 0) 542 FATAL("cannot set NF to a negative value"); 543 if (n > nfields) 544 growfldtab(n); 545 546 if (lastfld < n) 547 cleanfld(lastfld+1, n); 548 else 549 cleanfld(n+1, lastfld); 550 551 lastfld = n; 552 } 553 554 Cell *fieldadr(int n) /* get nth field */ 555 { 556 if (n < 0) 557 FATAL("trying to access out of range field %d", n); 558 if (n > nfields) /* fields after NF are empty */ 559 growfldtab(n); /* but does not increase NF */ 560 return(fldtab[n]); 561 } 562 563 void growfldtab(int n) /* make new fields up to at least $n */ 564 { 565 int nf = 2 * nfields; 566 size_t s; 567 568 if (n > nf) 569 nf = n; 570 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */ 571 if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */ 572 fldtab = (Cell **) realloc(fldtab, s); 573 else /* overflow sizeof int */ 574 xfree(fldtab); /* make it null */ 575 if (fldtab == NULL) 576 FATAL("out of space creating %d fields", nf); 577 makefields(nfields+1, nf); 578 nfields = nf; 579 } 580 581 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ 582 { 583 /* this relies on having fields[] the same length as $0 */ 584 /* the fields are all stored in this one array with \0's */ 585 char *fr; 586 int i, tempstat, n; 587 fa *pfa; 588 589 n = strlen(rec); 590 if (n > fieldssize) { 591 xfree(fields); 592 if ((fields = (char *) malloc(n+1)) == NULL) 593 FATAL("out of space for fields in refldbld %d", n); 594 fieldssize = n; 595 } 596 fr = fields; 597 *fr = '\0'; 598 if (*rec == '\0') 599 return 0; 600 pfa = makedfa(fs, 1); 601 DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs); 602 tempstat = pfa->initstat; 603 for (i = 1; ; i++) { 604 const size_t fss_rem = fields + fieldssize + 1 - fr; 605 if (i > nfields) 606 growfldtab(i); 607 if (freeable(fldtab[i])) 608 xfree(fldtab[i]->sval); 609 fldtab[i]->tval = FLD | STR | DONTFREE; 610 fldtab[i]->sval = fr; 611 DPRINTF("refldbld: i=%d\n", i); 612 if (nematch(pfa, rec)) { 613 const size_t reclen = patbeg - rec; 614 pfa->initstat = 2; /* horrible coupling to b.c */ 615 DPRINTF("match %s (%d chars)\n", patbeg, patlen); 616 if (reclen >= fss_rem) 617 FATAL("out of space for fields in refldbld"); 618 memcpy(fr, rec, reclen); 619 fr += reclen; 620 *fr++ = '\0'; 621 rec = patbeg + patlen; 622 } else { 623 DPRINTF("no match %s\n", rec); 624 if (strlcpy(fr, rec, fss_rem) >= fss_rem) 625 FATAL("out of space for fields in refldbld"); 626 pfa->initstat = tempstat; 627 break; 628 } 629 } 630 return i; 631 } 632 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 672 void yyerror(const char *s) 673 { 674 SYNTAX("%s", s); 675 } 676 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 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 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 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 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 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 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 p = ep - 1; 789 if (p > ebuf && *p == '\n') 790 p--; 791 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) 792 ; 793 while (*p == '\n') 794 p++; 795 fprintf(stderr, " context is\n\t"); 796 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) 797 ; 798 for ( ; p < q; p++) 799 if (*p) 800 putc(*p, stderr); 801 fprintf(stderr, " >>> "); 802 for ( ; p < ep; p++) 803 if (*p) 804 putc(*p, stderr); 805 fprintf(stderr, " <<< "); 806 if (*ep) 807 while ((c = input()) != '\n' && c != '\0' && c != EOF) { 808 putc(c, stderr); 809 bclass(c); 810 } 811 putc('\n', stderr); 812 ep = ebuf; 813 } 814 815 void bclass(int c) 816 { 817 switch (c) { 818 case '{': bracecnt++; break; 819 case '}': bracecnt--; break; 820 case '[': brackcnt++; break; 821 case ']': brackcnt--; break; 822 case '(': parencnt++; break; 823 case ')': parencnt--; break; 824 } 825 } 826 827 double errcheck(double x, const char *s) 828 { 829 830 if (errno == EDOM) { 831 errno = 0; 832 WARNING("%s argument out of domain", s); 833 x = 1; 834 } else if (errno == ERANGE) { 835 errno = 0; 836 WARNING("%s result out of range", s); 837 x = 1; 838 } 839 return x; 840 } 841 842 int isclvar(const char *s) /* is s of form var=something ? */ 843 { 844 const char *os = s; 845 846 if (!isalpha((uschar) *s) && *s != '_') 847 return 0; 848 for ( ; *s; s++) 849 if (!(isalnum((uschar) *s) || *s == '_')) 850 break; 851 return *s == '=' && s > os; 852 } 853 854 /* strtod is supposed to be a proper test of what's a valid number */ 855 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ 856 /* wrong: violates 4.10.1.4 of ansi C standard */ 857 858 /* well, not quite. As of C99, hex floating point is allowed. so this is 859 * a bit of a mess. We work around the mess by checking for a hexadecimal 860 * value and disallowing it. Similarly, we now follow gawk and allow only 861 * +nan, -nan, +inf, and -inf for NaN and infinity values. 862 */ 863 864 /* 865 * This routine now has a more complicated interface, the main point 866 * being to avoid the double conversion of a string to double, and 867 * also to convey out, if requested, the information that the numeric 868 * value was a leading string or is all of the string. The latter bit 869 * is used in getfval(). 870 */ 871 872 bool is_valid_number(const char *s, bool trailing_stuff_ok, 873 bool *no_trailing, double *result) 874 { 875 double r; 876 char *ep; 877 bool retval = false; 878 bool is_nan = false; 879 bool is_inf = false; 880 881 if (no_trailing) 882 *no_trailing = false; 883 884 while (isspace((uschar)*s)) 885 s++; 886 887 /* no hex floating point, sorry */ 888 if (s[0] == '0' && tolower((uschar)s[1]) == 'x') 889 return false; 890 891 /* allow +nan, -nan, +inf, -inf, any other letter, no */ 892 if (s[0] == '+' || s[0] == '-') { 893 is_nan = (strncasecmp(s+1, "nan", 3) == 0); 894 is_inf = (strncasecmp(s+1, "inf", 3) == 0); 895 if ((is_nan || is_inf) 896 && (isspace((uschar)s[4]) || s[4] == '\0')) 897 goto convert; 898 else if (! isdigit((uschar)s[1]) && s[1] != '.') 899 return false; 900 } 901 else if (! isdigit((uschar)s[0]) && s[0] != '.') 902 return false; 903 904 convert: 905 errno = 0; 906 r = strtod(s, &ep); 907 if (ep == s || errno == ERANGE) 908 return false; 909 910 if (isnan(r) && s[0] == '-' && signbit(r) == 0) 911 r = -r; 912 913 if (result != NULL) 914 *result = r; 915 916 /* 917 * check for trailing stuff 918 */ 919 while (isspace((uschar)*ep)) 920 ep++; 921 922 if (no_trailing != NULL) 923 *no_trailing = (*ep == '\0'); 924 925 /* return true if found the end, or trailing stuff is allowed */ 926 retval = *ep == '\0' || trailing_stuff_ok; 927 928 return retval; 929 } 930