1*fb60ec6aSmillert /* $OpenBSD: lib.c,v 1.59 2024/08/03 21:12:16 millert Exp $ */ 26ab05f83Stholo /**************************************************************** 307edfa4aSkstailey Copyright (C) Lucent Technologies 1997 46ab05f83Stholo All Rights Reserved 56ab05f83Stholo 66ab05f83Stholo Permission to use, copy, modify, and distribute this software and 76ab05f83Stholo its documentation for any purpose and without fee is hereby 86ab05f83Stholo granted, provided that the above copyright notice appear in all 96ab05f83Stholo copies and that both that the copyright notice and this 106ab05f83Stholo permission notice and warranty disclaimer appear in supporting 1107edfa4aSkstailey documentation, and that the name Lucent Technologies or any of 1207edfa4aSkstailey its entities not be used in advertising or publicity pertaining 1307edfa4aSkstailey to distribution of the software without specific, written prior 1407edfa4aSkstailey permission. 156ab05f83Stholo 1607edfa4aSkstailey LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 1707edfa4aSkstailey INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 1807edfa4aSkstailey IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY 1907edfa4aSkstailey SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 2007edfa4aSkstailey WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 2107edfa4aSkstailey IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 2207edfa4aSkstailey ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 2307edfa4aSkstailey THIS SOFTWARE. 246ab05f83Stholo ****************************************************************/ 256ab05f83Stholo 266ab05f83Stholo #define DEBUG 276ab05f83Stholo #include <stdio.h> 286ab05f83Stholo #include <string.h> 29a11aea23Smillert #include <strings.h> 306ab05f83Stholo #include <ctype.h> 316ab05f83Stholo #include <errno.h> 326ab05f83Stholo #include <stdlib.h> 337b11b857Smillert #include <stdarg.h> 34fabd211eSmillert #include <limits.h> 356de80fb8Smillert #include <math.h> 366ab05f83Stholo #include "awk.h" 376ab05f83Stholo 38fabd211eSmillert char EMPTY[] = { '\0' }; 396ab05f83Stholo FILE *infile = NULL; 406685ce51Smillert bool innew; /* true = infile has not been read by readrec */ 41fabd211eSmillert char *file = EMPTY; 426ab05f83Stholo char *record; 4307edfa4aSkstailey int recsize = RECSIZE; 446ab05f83Stholo char *fields; 4507edfa4aSkstailey int fieldssize = RECSIZE; 4607edfa4aSkstailey 4707edfa4aSkstailey Cell **fldtab; /* pointers to Cells */ 48fabd211eSmillert static size_t len_inputFS = 0; 49fabd211eSmillert static char *inputFS = NULL; /* FS at time of input, for field splitting */ 506ab05f83Stholo 5180b86fb0Smillert #define MAXFLD 2 5207edfa4aSkstailey int nfields = MAXFLD; /* last allocated slot for $i */ 536ab05f83Stholo 54f81b289fSmillert bool donefld; /* true = implies rec broken into fields */ 55f81b289fSmillert bool donerec; /* true = record is valid (no flds have changed) */ 566ab05f83Stholo 5707edfa4aSkstailey int lastfld = 0; /* last used field */ 586ab05f83Stholo int argno = 1; /* current input argument number */ 596ab05f83Stholo extern Awkfloat *ARGC; 606ab05f83Stholo 616685ce51Smillert static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL }; 626685ce51Smillert static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL }; 6307edfa4aSkstailey 646ab05f83Stholo void recinit(unsigned int n) 656ab05f83Stholo { 66483fa115Smillert if ( (record = (char *) malloc(n)) == NULL 67483fa115Smillert || (fields = (char *) malloc(n+1)) == NULL 68483fa115Smillert || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL 69483fa115Smillert || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL) 707b11b857Smillert FATAL("out of space for $0 and fields"); 716b41c728Smillert *record = '\0'; 7207edfa4aSkstailey *fldtab[0] = dollar0; 7307edfa4aSkstailey fldtab[0]->sval = record; 7407edfa4aSkstailey fldtab[0]->nval = tostring("0"); 7507edfa4aSkstailey makefields(1, nfields); 7607edfa4aSkstailey } 7707edfa4aSkstailey 7807edfa4aSkstailey void makefields(int n1, int n2) /* create $n1..$n2 inclusive */ 7907edfa4aSkstailey { 8007edfa4aSkstailey char temp[50]; 816ab05f83Stholo int i; 826ab05f83Stholo 8307edfa4aSkstailey for (i = n1; i <= n2; i++) { 84483fa115Smillert fldtab[i] = (Cell *) malloc(sizeof(**fldtab)); 8507edfa4aSkstailey if (fldtab[i] == NULL) 867b11b857Smillert FATAL("out of space in makefields %d", i); 8707edfa4aSkstailey *fldtab[i] = dollar1; 88d7cce239Smillert snprintf(temp, sizeof(temp), "%d", i); 8907edfa4aSkstailey fldtab[i]->nval = tostring(temp); 9007edfa4aSkstailey } 916ab05f83Stholo } 926ab05f83Stholo 936ab05f83Stholo void initgetrec(void) 946ab05f83Stholo { 956ab05f83Stholo int i; 966ab05f83Stholo char *p; 976ab05f83Stholo 986ab05f83Stholo for (i = 1; i < *ARGC; i++) { 99000399a4Smillert p = getargv(i); /* find 1st real filename */ 100000399a4Smillert if (p == NULL || *p == '\0') { /* deleted or zapped */ 101000399a4Smillert argno++; 102000399a4Smillert continue; 103000399a4Smillert } 104000399a4Smillert if (!isclvar(p)) { 105000399a4Smillert setsval(lookup("FILENAME", symtab), p); 1066ab05f83Stholo return; 1076ab05f83Stholo } 1086ab05f83Stholo setclvar(p); /* a commandline assignment before filename */ 1096ab05f83Stholo argno++; 1106ab05f83Stholo } 1116ab05f83Stholo infile = stdin; /* no filenames, so use stdin */ 1126685ce51Smillert innew = true; 1136ab05f83Stholo } 1146ab05f83Stholo 115c0fa3611Smillert /* 116c0fa3611Smillert * POSIX specifies that fields are supposed to be evaluated as if they were 117c0fa3611Smillert * split using the value of FS at the time that the record's value ($0) was 118c0fa3611Smillert * read. 119c0fa3611Smillert * 120c0fa3611Smillert * Since field-splitting is done lazily, we save the current value of FS 121c0fa3611Smillert * whenever a new record is read in (implicitly or via getline), or when 122c0fa3611Smillert * a new value is assigned to $0. 123c0fa3611Smillert */ 124c0fa3611Smillert void savefs(void) 125c0fa3611Smillert { 126144915fcSmillert size_t len = strlen(getsval(fsloc)); 127144915fcSmillert if (len >= len_inputFS) { 128fabd211eSmillert len_inputFS = len + 1; 129483fa115Smillert inputFS = (char *) realloc(inputFS, len_inputFS); 130fabd211eSmillert if (inputFS == NULL) 131c0fa3611Smillert FATAL("field separator %.10s... is too long", *FS); 132144915fcSmillert } 133144915fcSmillert if (strlcpy(inputFS, *FS, len_inputFS) >= len_inputFS) 134144915fcSmillert FATAL("field separator %.10s... is too long", *FS); 135c0fa3611Smillert } 136c0fa3611Smillert 137f81b289fSmillert static bool firsttime = true; 13823cb51abSmillert 139f81b289fSmillert int getrec(char **pbuf, int *pbufsize, bool isrecord) /* get next input record */ 14007edfa4aSkstailey { /* note: cares whether buf == record */ 1416ab05f83Stholo int c; 14207edfa4aSkstailey char *buf = *pbuf; 14380b86fb0Smillert uschar saveb0; 14480b86fb0Smillert int bufsize = *pbufsize, savebufsize = bufsize; 1456ab05f83Stholo 1466ab05f83Stholo if (firsttime) { 147f81b289fSmillert firsttime = false; 1486ab05f83Stholo initgetrec(); 1496ab05f83Stholo } 150115bd590Smillert DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", 151115bd590Smillert *RS, *FS, *ARGC, *FILENAME); 15280b86fb0Smillert saveb0 = buf[0]; 1536ab05f83Stholo buf[0] = 0; 1546ab05f83Stholo while (argno < *ARGC || infile == stdin) { 155115bd590Smillert DPRINTF("argno=%d, file=|%s|\n", argno, file); 1566ab05f83Stholo if (infile == NULL) { /* have to open a new file */ 1576ab05f83Stholo file = getargv(argno); 158000399a4Smillert if (file == NULL || *file == '\0') { /* deleted or zapped */ 1596ab05f83Stholo argno++; 1606ab05f83Stholo continue; 1616ab05f83Stholo } 1626ab05f83Stholo if (isclvar(file)) { /* a var=value arg */ 1636ab05f83Stholo setclvar(file); 1646ab05f83Stholo argno++; 1656ab05f83Stholo continue; 1666ab05f83Stholo } 1676ab05f83Stholo *FILENAME = file; 168115bd590Smillert DPRINTF("opening file %s\n", file); 1696ab05f83Stholo if (*file == '-' && *(file+1) == '\0') 1706ab05f83Stholo infile = stdin; 17107edfa4aSkstailey else if ((infile = fopen(file, "r")) == NULL) 1727b11b857Smillert FATAL("can't open file %s", file); 1730a102a45Smillert innew = true; 1746ab05f83Stholo setfval(fnrloc, 0.0); 1756ab05f83Stholo } 1766685ce51Smillert c = readrec(&buf, &bufsize, infile, innew); 1776685ce51Smillert if (innew) 1786685ce51Smillert innew = false; 1796ab05f83Stholo if (c != 0 || buf[0] != '\0') { /* normal record */ 18007edfa4aSkstailey if (isrecord) { 181483fa115Smillert double result; 182483fa115Smillert 18307edfa4aSkstailey if (freeable(fldtab[0])) 18407edfa4aSkstailey xfree(fldtab[0]->sval); 18507edfa4aSkstailey fldtab[0]->sval = buf; /* buf == record */ 18607edfa4aSkstailey fldtab[0]->tval = REC | STR | DONTFREE; 187483fa115Smillert if (is_number(fldtab[0]->sval, & result)) { 188483fa115Smillert fldtab[0]->fval = result; 18907edfa4aSkstailey fldtab[0]->tval |= NUM; 1906ab05f83Stholo } 191c4ead62fSmillert donefld = false; 192c4ead62fSmillert donerec = true; 193c4ead62fSmillert savefs(); 1946ab05f83Stholo } 1956ab05f83Stholo setfval(nrloc, nrloc->fval+1); 1966ab05f83Stholo setfval(fnrloc, fnrloc->fval+1); 19707edfa4aSkstailey *pbuf = buf; 19807edfa4aSkstailey *pbufsize = bufsize; 1996ab05f83Stholo return 1; 2006ab05f83Stholo } 2016ab05f83Stholo /* EOF arrived on this file; set up next */ 2026ab05f83Stholo if (infile != stdin) 2036ab05f83Stholo fclose(infile); 2046ab05f83Stholo infile = NULL; 2056ab05f83Stholo argno++; 2066ab05f83Stholo } 20780b86fb0Smillert buf[0] = saveb0; 20807edfa4aSkstailey *pbuf = buf; 20980b86fb0Smillert *pbufsize = savebufsize; 2106ab05f83Stholo return 0; /* true end of file */ 2116ab05f83Stholo } 2126ab05f83Stholo 2136ab05f83Stholo void nextfile(void) 2146ab05f83Stholo { 21580b86fb0Smillert if (infile != NULL && infile != stdin) 2166ab05f83Stholo fclose(infile); 2176ab05f83Stholo infile = NULL; 2186ab05f83Stholo argno++; 2196ab05f83Stholo } 2206ab05f83Stholo 221a886e62eSmillert extern int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag); 222a886e62eSmillert 2236685ce51Smillert int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* read one record into buf */ 2246ab05f83Stholo { 225a886e62eSmillert int sep, c, isrec; // POTENTIAL BUG? isrec is a macro in awk.h 226a886e62eSmillert char *rr = *pbuf, *buf = *pbuf; 22707edfa4aSkstailey int bufsize = *pbufsize; 22802265e66Smillert char *rs = getsval(rsloc); 2296ab05f83Stholo 230a886e62eSmillert if (CSV) { 231*fb60ec6aSmillert c = readcsvrec(&buf, &bufsize, inf, newflag); 232a886e62eSmillert isrec = (c == EOF && rr == buf) ? false : true; 233a886e62eSmillert } else if (*rs && rs[1]) { 234f81b289fSmillert bool found; 235ec104564Smillert 23635a004d4Smillert memset(buf, 0, bufsize); 237ec104564Smillert fa *pfa = makedfa(rs, 1); 2386685ce51Smillert if (newflag) 239ec104564Smillert found = fnematch(pfa, inf, &buf, &bufsize, recsize); 2406685ce51Smillert else { 2416685ce51Smillert int tempstat = pfa->initstat; 2426685ce51Smillert pfa->initstat = 2; 2436685ce51Smillert found = fnematch(pfa, inf, &buf, &bufsize, recsize); 2446685ce51Smillert pfa->initstat = tempstat; 2456685ce51Smillert } 246ec104564Smillert if (found) 247d7cce239Smillert setptr(patbeg, '\0'); 2482a0b1829Smillert isrec = (found == 0 && *buf == '\0') ? false : true; 249ec104564Smillert } else { 25002265e66Smillert if ((sep = *rs) == 0) { 2516ab05f83Stholo sep = '\n'; 2526ab05f83Stholo while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ 2536ab05f83Stholo ; 2546ab05f83Stholo if (c != EOF) 2556ab05f83Stholo ungetc(c, inf); 2566ab05f83Stholo } 25707edfa4aSkstailey for (rr = buf; ; ) { 25807edfa4aSkstailey for (; (c=getc(inf)) != sep && c != EOF; ) { 25907edfa4aSkstailey if (rr-buf+1 > bufsize) 260ec104564Smillert if (!adjbuf(&buf, &bufsize, 1+rr-buf, 261ec104564Smillert recsize, &rr, "readrec 1")) 2627b11b857Smillert FATAL("input record `%.30s...' too long", buf); 26307edfa4aSkstailey *rr++ = c; 26407edfa4aSkstailey } 26502265e66Smillert if (*rs == sep || c == EOF) 2666ab05f83Stholo break; 2676ab05f83Stholo if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ 2686ab05f83Stholo break; 269ec104564Smillert if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, 270ec104564Smillert "readrec 2")) 2717b11b857Smillert FATAL("input record `%.30s...' too long", buf); 2726ab05f83Stholo *rr++ = '\n'; 2736ab05f83Stholo *rr++ = c; 2746ab05f83Stholo } 27507edfa4aSkstailey if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) 2767b11b857Smillert FATAL("input record `%.30s...' too long", buf); 2776ab05f83Stholo *rr = 0; 2782a0b1829Smillert isrec = (c == EOF && rr == buf) ? false : true; 279ec104564Smillert } 28007edfa4aSkstailey *pbuf = buf; 28107edfa4aSkstailey *pbufsize = bufsize; 282115bd590Smillert DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec); 283ec104564Smillert return isrec; 2846ab05f83Stholo } 2856ab05f83Stholo 286a886e62eSmillert 287a886e62eSmillert /******************* 288a886e62eSmillert * loose ends here: 289a886e62eSmillert * \r\n should become \n 290a886e62eSmillert * what about bare \r? Excel uses that for embedded newlines 291a886e62eSmillert * can't have "" in unquoted fields, according to RFC 4180 292a886e62eSmillert */ 293a886e62eSmillert 294a886e62eSmillert int readcsvrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* csv can have \n's */ 295a886e62eSmillert { /* so read a complete record that might be multiple lines */ 296a886e62eSmillert int sep, c; 297a886e62eSmillert char *rr = *pbuf, *buf = *pbuf; 298a886e62eSmillert int bufsize = *pbufsize; 299a886e62eSmillert bool in_quote = false; 300a886e62eSmillert 301a886e62eSmillert sep = '\n'; /* the only separator; have to skip over \n embedded in "..." */ 302a886e62eSmillert rr = buf; 303a886e62eSmillert while ((c = getc(inf)) != EOF) { 304a886e62eSmillert if (c == sep) { 305a886e62eSmillert if (! in_quote) 306a886e62eSmillert break; 307a886e62eSmillert if (rr > buf && rr[-1] == '\r') // remove \r if was \r\n 308a886e62eSmillert rr--; 309a886e62eSmillert } 310a886e62eSmillert 311a886e62eSmillert if (rr-buf+1 > bufsize) 312a886e62eSmillert if (!adjbuf(&buf, &bufsize, 1+rr-buf, 313a886e62eSmillert recsize, &rr, "readcsvrec 1")) 314a886e62eSmillert FATAL("input record `%.30s...' too long", buf); 315a886e62eSmillert *rr++ = c; 316a886e62eSmillert if (c == '"') 317a886e62eSmillert in_quote = ! in_quote; 318a886e62eSmillert } 319a886e62eSmillert if (c == '\n' && rr > buf && rr[-1] == '\r') // remove \r if was \r\n 320a886e62eSmillert rr--; 321a886e62eSmillert 322a886e62eSmillert if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readcsvrec 4")) 323a886e62eSmillert FATAL("input record `%.30s...' too long", buf); 324a886e62eSmillert *rr = 0; 325a886e62eSmillert *pbuf = buf; 326a886e62eSmillert *pbufsize = bufsize; 327a886e62eSmillert DPRINTF("readcsvrec saw <%s>, returns %d\n", buf, c); 328a886e62eSmillert return c; 329a886e62eSmillert } 330a886e62eSmillert 3316ab05f83Stholo char *getargv(int n) /* get ARGV[n] */ 3326ab05f83Stholo { 333925804ecSjsg Array *ap; 3346ab05f83Stholo Cell *x; 33507edfa4aSkstailey char *s, temp[50]; 336925804ecSjsg extern Cell *ARGVcell; 3376ab05f83Stholo 338925804ecSjsg ap = (Array *)ARGVcell->sval; 339d7cce239Smillert snprintf(temp, sizeof(temp), "%d", n); 340925804ecSjsg if (lookup(temp, ap) == NULL) 341000399a4Smillert return NULL; 342925804ecSjsg x = setsymtab(temp, "", 0.0, STR, ap); 3436ab05f83Stholo s = getsval(x); 344115bd590Smillert DPRINTF("getargv(%d) returns |%s|\n", n, s); 3456ab05f83Stholo return s; 3466ab05f83Stholo } 3476ab05f83Stholo 3486ab05f83Stholo void setclvar(char *s) /* set var=value from s */ 3496ab05f83Stholo { 3507ef192a2Smillert char *e, *p; 3516ab05f83Stholo Cell *q; 352483fa115Smillert double result; 3536ab05f83Stholo 354a886e62eSmillert /* commit f3d9187d4e0f02294fb1b0e31152070506314e67 broke T.argv test */ 355a886e62eSmillert /* I don't understand why it was changed. */ 356a886e62eSmillert 3576ab05f83Stholo for (p=s; *p != '='; p++) 3586ab05f83Stholo ; 3597ef192a2Smillert e = p; 3606ab05f83Stholo *p++ = 0; 3616ab05f83Stholo p = qstring(p, '\0'); 3626ab05f83Stholo q = setsymtab(s, p, 0.0, STR, symtab); 3636ab05f83Stholo setsval(q, p); 364483fa115Smillert if (is_number(q->sval, & result)) { 365483fa115Smillert q->fval = result; 3666ab05f83Stholo q->tval |= NUM; 3676ab05f83Stholo } 368115bd590Smillert DPRINTF("command line set %s to |%s|\n", s, p); 369dd20eed5Smillert free(p); 3707ef192a2Smillert *e = '='; 3716ab05f83Stholo } 3726ab05f83Stholo 3736ab05f83Stholo 3746ab05f83Stholo void fldbld(void) /* create fields from current record */ 3756ab05f83Stholo { 37607edfa4aSkstailey /* this relies on having fields[] the same length as $0 */ 37707edfa4aSkstailey /* the fields are all stored in this one array with \0's */ 378000399a4Smillert /* possibly with a final trailing \0 not associated with any field */ 3796ab05f83Stholo char *r, *fr, sep; 3806ab05f83Stholo Cell *p; 38107edfa4aSkstailey int i, j, n; 3826ab05f83Stholo 3836ab05f83Stholo if (donefld) 3846ab05f83Stholo return; 38507edfa4aSkstailey if (!isstr(fldtab[0])) 38607edfa4aSkstailey getsval(fldtab[0]); 38707edfa4aSkstailey r = fldtab[0]->sval; 38807edfa4aSkstailey n = strlen(r); 38907edfa4aSkstailey if (n > fieldssize) { 39007edfa4aSkstailey xfree(fields); 391483fa115Smillert if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */ 3927b11b857Smillert FATAL("out of space for fields in fldbld %d", n); 39307edfa4aSkstailey fieldssize = n; 39407edfa4aSkstailey } 3956ab05f83Stholo fr = fields; 3966ab05f83Stholo i = 0; /* number of fields accumulated here */ 3974edce374Smillert if (inputFS == NULL) /* make sure we have a copy of FS */ 3984edce374Smillert savefs(); 399d9ecc40dSmillert if (!CSV && strlen(inputFS) > 1) { /* it's a regular expression */ 400b2698ba9Smillert i = refldbld(r, inputFS); 401a886e62eSmillert } else if (!CSV && (sep = *inputFS) == ' ') { /* default whitespace */ 4026ab05f83Stholo for (i = 0; ; ) { 4036ab05f83Stholo while (*r == ' ' || *r == '\t' || *r == '\n') 4046ab05f83Stholo r++; 4056ab05f83Stholo if (*r == 0) 4066ab05f83Stholo break; 4076ab05f83Stholo i++; 40807edfa4aSkstailey if (i > nfields) 40907edfa4aSkstailey growfldtab(i); 41007edfa4aSkstailey if (freeable(fldtab[i])) 41107edfa4aSkstailey xfree(fldtab[i]->sval); 41207edfa4aSkstailey fldtab[i]->sval = fr; 41307edfa4aSkstailey fldtab[i]->tval = FLD | STR | DONTFREE; 4146ab05f83Stholo do 4156ab05f83Stholo *fr++ = *r++; 4166ab05f83Stholo while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); 4176ab05f83Stholo *fr++ = 0; 4186ab05f83Stholo } 4196ab05f83Stholo *fr = 0; 420a886e62eSmillert } else if (CSV) { /* CSV processing. no error handling */ 421a886e62eSmillert if (*r != 0) { 422a886e62eSmillert for (;;) { 4236ab05f83Stholo i++; 42407edfa4aSkstailey if (i > nfields) 42507edfa4aSkstailey growfldtab(i); 42607edfa4aSkstailey if (freeable(fldtab[i])) 42707edfa4aSkstailey xfree(fldtab[i]->sval); 428a886e62eSmillert fldtab[i]->sval = fr; 429a886e62eSmillert fldtab[i]->tval = FLD | STR | DONTFREE; 430a886e62eSmillert if (*r == '"' ) { /* start of "..." */ 431a886e62eSmillert for (r++ ; *r != '\0'; ) { 432a886e62eSmillert if (*r == '"' && r[1] != '\0' && r[1] == '"') { 433a886e62eSmillert r += 2; /* doubled quote */ 434a886e62eSmillert *fr++ = '"'; 435a886e62eSmillert } else if (*r == '"' && (r[1] == '\0' || r[1] == ',')) { 436a886e62eSmillert r++; /* skip over closing quote */ 437a886e62eSmillert break; 438a886e62eSmillert } else { 439a886e62eSmillert *fr++ = *r++; 440a886e62eSmillert } 441a886e62eSmillert } 442a886e62eSmillert *fr++ = 0; 443a886e62eSmillert } else { /* unquoted field */ 444a886e62eSmillert while (*r != ',' && *r != '\0') 445a886e62eSmillert *fr++ = *r++; 446a886e62eSmillert *fr++ = 0; 447a886e62eSmillert } 448a886e62eSmillert if (*r++ == 0) 449a886e62eSmillert break; 450a886e62eSmillert 451a886e62eSmillert } 452a886e62eSmillert } 453a886e62eSmillert *fr = 0; 454a886e62eSmillert } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ 455a886e62eSmillert for (i = 0; *r != '\0'; ) { 456a886e62eSmillert char buf[10]; 457a886e62eSmillert i++; 458a886e62eSmillert if (i > nfields) 459a886e62eSmillert growfldtab(i); 460a886e62eSmillert if (freeable(fldtab[i])) 461a886e62eSmillert xfree(fldtab[i]->sval); 462a886e62eSmillert n = u8_nextlen(r); 463a886e62eSmillert for (j = 0; j < n; j++) 464a886e62eSmillert buf[j] = *r++; 465a886e62eSmillert buf[j] = '\0'; 46607edfa4aSkstailey fldtab[i]->sval = tostring(buf); 46707edfa4aSkstailey fldtab[i]->tval = FLD | STR; 4686ab05f83Stholo } 4696ab05f83Stholo *fr = 0; 4706ab05f83Stholo } else if (*r != 0) { /* if 0, it's a null field */ 47123cb51abSmillert /* subtle case: if length(FS) == 1 && length(RS > 0) 47223cb51abSmillert * \n is NOT a field separator (cf awk book 61,84). 47323cb51abSmillert * this variable is tested in the inner while loop. 47423cb51abSmillert */ 47523cb51abSmillert int rtest = '\n'; /* normal case */ 47623cb51abSmillert if (strlen(*RS) > 0) 47723cb51abSmillert rtest = '\0'; 4786ab05f83Stholo for (;;) { 4796ab05f83Stholo i++; 48007edfa4aSkstailey if (i > nfields) 48107edfa4aSkstailey growfldtab(i); 48207edfa4aSkstailey if (freeable(fldtab[i])) 48307edfa4aSkstailey xfree(fldtab[i]->sval); 48407edfa4aSkstailey fldtab[i]->sval = fr; 48507edfa4aSkstailey fldtab[i]->tval = FLD | STR | DONTFREE; 48623cb51abSmillert while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ 4876ab05f83Stholo *fr++ = *r++; 4886ab05f83Stholo *fr++ = 0; 4896ab05f83Stholo if (*r++ == 0) 4906ab05f83Stholo break; 4916ab05f83Stholo } 4926ab05f83Stholo *fr = 0; 4936ab05f83Stholo } 49407edfa4aSkstailey if (i > nfields) 4957b11b857Smillert FATAL("record `%.30s...' has too many fields; can't happen", r); 49607edfa4aSkstailey cleanfld(i+1, lastfld); /* clean out junk from previous record */ 49707edfa4aSkstailey lastfld = i; 498f81b289fSmillert donefld = true; 49907edfa4aSkstailey for (j = 1; j <= lastfld; j++) { 500483fa115Smillert double result; 501483fa115Smillert 50207edfa4aSkstailey p = fldtab[j]; 503483fa115Smillert if(is_number(p->sval, & result)) { 504483fa115Smillert p->fval = result; 5056ab05f83Stholo p->tval |= NUM; 5066ab05f83Stholo } 5076ab05f83Stholo } 50807edfa4aSkstailey setfval(nfloc, (Awkfloat) lastfld); 509f81b289fSmillert donerec = true; /* restore */ 51007edfa4aSkstailey if (dbg) { 51107edfa4aSkstailey for (j = 0; j <= lastfld; j++) { 51207edfa4aSkstailey p = fldtab[j]; 51307edfa4aSkstailey printf("field %d (%s): |%s|\n", j, p->nval, p->sval); 51407edfa4aSkstailey } 51507edfa4aSkstailey } 5166ab05f83Stholo } 5176ab05f83Stholo 5186ab05f83Stholo void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ 51907edfa4aSkstailey { /* nvals remain intact */ 52007edfa4aSkstailey Cell *p; 52107edfa4aSkstailey int i; 5226ab05f83Stholo 52307edfa4aSkstailey for (i = n1; i <= n2; i++) { 52407edfa4aSkstailey p = fldtab[i]; 52507edfa4aSkstailey if (freeable(p)) 5266ab05f83Stholo xfree(p->sval); 527fabd211eSmillert p->sval = EMPTY, 5286ab05f83Stholo p->tval = FLD | STR | DONTFREE; 5296ab05f83Stholo } 5306ab05f83Stholo } 5316ab05f83Stholo 53207edfa4aSkstailey void newfld(int n) /* add field n after end of existing lastfld */ 5336ab05f83Stholo { 53407edfa4aSkstailey if (n > nfields) 53507edfa4aSkstailey growfldtab(n); 53607edfa4aSkstailey cleanfld(lastfld+1, n); 53707edfa4aSkstailey lastfld = n; 5386ab05f83Stholo setfval(nfloc, (Awkfloat) n); 5396ab05f83Stholo } 5406ab05f83Stholo 541c062391aSmillert void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */ 542c062391aSmillert { 54302265e66Smillert if (n < 0) 54402265e66Smillert FATAL("cannot set NF to a negative value"); 545c062391aSmillert if (n > nfields) 546c062391aSmillert growfldtab(n); 547c062391aSmillert 548c062391aSmillert if (lastfld < n) 549c062391aSmillert cleanfld(lastfld+1, n); 550c062391aSmillert else 551c062391aSmillert cleanfld(n+1, lastfld); 552c062391aSmillert 553c062391aSmillert lastfld = n; 554c062391aSmillert } 555c062391aSmillert 55607edfa4aSkstailey Cell *fieldadr(int n) /* get nth field */ 55707edfa4aSkstailey { 55807edfa4aSkstailey if (n < 0) 55923cb51abSmillert FATAL("trying to access out of range field %d", n); 56007edfa4aSkstailey if (n > nfields) /* fields after NF are empty */ 56107edfa4aSkstailey growfldtab(n); /* but does not increase NF */ 56207edfa4aSkstailey return(fldtab[n]); 56307edfa4aSkstailey } 56407edfa4aSkstailey 56507edfa4aSkstailey void growfldtab(int n) /* make new fields up to at least $n */ 56607edfa4aSkstailey { 56707edfa4aSkstailey int nf = 2 * nfields; 56823cb51abSmillert size_t s; 56907edfa4aSkstailey 57007edfa4aSkstailey if (n > nf) 57107edfa4aSkstailey nf = n; 57223cb51abSmillert s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */ 5736685ce51Smillert if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */ 574483fa115Smillert fldtab = (Cell **) realloc(fldtab, s); 57523cb51abSmillert else /* overflow sizeof int */ 57623cb51abSmillert xfree(fldtab); /* make it null */ 57707edfa4aSkstailey if (fldtab == NULL) 5787b11b857Smillert FATAL("out of space creating %d fields", nf); 57907edfa4aSkstailey makefields(nfields+1, nf); 58007edfa4aSkstailey nfields = nf; 58107edfa4aSkstailey } 58207edfa4aSkstailey 5839a69093aSmillert int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ 5846ab05f83Stholo { 58507edfa4aSkstailey /* this relies on having fields[] the same length as $0 */ 58607edfa4aSkstailey /* the fields are all stored in this one array with \0's */ 5876ab05f83Stholo char *fr; 58807edfa4aSkstailey int i, tempstat, n; 5896ab05f83Stholo fa *pfa; 5906ab05f83Stholo 59107edfa4aSkstailey n = strlen(rec); 59207edfa4aSkstailey if (n > fieldssize) { 59307edfa4aSkstailey xfree(fields); 594483fa115Smillert if ((fields = (char *) malloc(n+1)) == NULL) 5957b11b857Smillert FATAL("out of space for fields in refldbld %d", n); 59607edfa4aSkstailey fieldssize = n; 59707edfa4aSkstailey } 5986ab05f83Stholo fr = fields; 5996ab05f83Stholo *fr = '\0'; 6006ab05f83Stholo if (*rec == '\0') 6016ab05f83Stholo return 0; 6026ab05f83Stholo pfa = makedfa(fs, 1); 603115bd590Smillert DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs); 6046ab05f83Stholo tempstat = pfa->initstat; 60507edfa4aSkstailey for (i = 1; ; i++) { 606144915fcSmillert const size_t fss_rem = fields + fieldssize + 1 - fr; 60707edfa4aSkstailey if (i > nfields) 60807edfa4aSkstailey growfldtab(i); 60907edfa4aSkstailey if (freeable(fldtab[i])) 61007edfa4aSkstailey xfree(fldtab[i]->sval); 61107edfa4aSkstailey fldtab[i]->tval = FLD | STR | DONTFREE; 61207edfa4aSkstailey fldtab[i]->sval = fr; 613115bd590Smillert DPRINTF("refldbld: i=%d\n", i); 6146ab05f83Stholo if (nematch(pfa, rec)) { 615144915fcSmillert const size_t reclen = patbeg - rec; 61607edfa4aSkstailey pfa->initstat = 2; /* horrible coupling to b.c */ 617115bd590Smillert DPRINTF("match %s (%d chars)\n", patbeg, patlen); 618144915fcSmillert if (reclen >= fss_rem) 619144915fcSmillert FATAL("out of space for fields in refldbld"); 620144915fcSmillert memcpy(fr, rec, reclen); 621144915fcSmillert fr += reclen; 622144915fcSmillert *fr++ = '\0'; 6236ab05f83Stholo rec = patbeg + patlen; 6246ab05f83Stholo } else { 625115bd590Smillert DPRINTF("no match %s\n", rec); 626144915fcSmillert if (strlcpy(fr, rec, fss_rem) >= fss_rem) 627144915fcSmillert FATAL("out of space for fields in refldbld"); 6286ab05f83Stholo pfa->initstat = tempstat; 6296ab05f83Stholo break; 6306ab05f83Stholo } 6316ab05f83Stholo } 6326ab05f83Stholo return i; 6336ab05f83Stholo } 6346ab05f83Stholo 6356ab05f83Stholo void recbld(void) /* create $0 from $1..$NF if necessary */ 6366ab05f83Stholo { 6376ab05f83Stholo int i; 6386ab05f83Stholo char *r, *p; 63902265e66Smillert char *sep = getsval(ofsloc); 6406ab05f83Stholo 641f81b289fSmillert if (donerec) 6426ab05f83Stholo return; 643a4fa8700Smillert r = record; 6446ab05f83Stholo for (i = 1; i <= *NF; i++) { 64507edfa4aSkstailey p = getsval(fldtab[i]); 646a4fa8700Smillert if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) 6477b11b857Smillert FATAL("created $0 `%.30s...' too long", record); 64807edfa4aSkstailey while ((*r = *p++) != 0) 6496ab05f83Stholo r++; 65007edfa4aSkstailey if (i < *NF) { 65102265e66Smillert if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2")) 6527b11b857Smillert FATAL("created $0 `%.30s...' too long", record); 65302265e66Smillert for (p = sep; (*r = *p++) != 0; ) 6546ab05f83Stholo r++; 6556ab05f83Stholo } 65607edfa4aSkstailey } 657a4fa8700Smillert if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) 6587b11b857Smillert FATAL("built giant record `%.30s...'", record); 6596ab05f83Stholo *r = '\0'; 660115bd590Smillert DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]); 66107edfa4aSkstailey 66207edfa4aSkstailey if (freeable(fldtab[0])) 66307edfa4aSkstailey xfree(fldtab[0]->sval); 66407edfa4aSkstailey fldtab[0]->tval = REC | STR | DONTFREE; 66507edfa4aSkstailey fldtab[0]->sval = record; 66607edfa4aSkstailey 667115bd590Smillert DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]); 668115bd590Smillert DPRINTF("recbld = |%s|\n", record); 669f81b289fSmillert donerec = true; 6706ab05f83Stholo } 6716ab05f83Stholo 6726ab05f83Stholo int errorflag = 0; 6736ab05f83Stholo 6749a69093aSmillert void yyerror(const char *s) 6756ab05f83Stholo { 6767843b775Sgrange SYNTAX("%s", s); 6777b11b857Smillert } 6787b11b857Smillert 6799a69093aSmillert void SYNTAX(const char *fmt, ...) 6807b11b857Smillert { 6816ab05f83Stholo extern char *cmdname, *curfname; 6826ab05f83Stholo static int been_here = 0; 6837b11b857Smillert va_list varg; 6846ab05f83Stholo 6856ab05f83Stholo if (been_here++ > 2) 6866ab05f83Stholo return; 6877b11b857Smillert fprintf(stderr, "%s: ", cmdname); 6887b11b857Smillert va_start(varg, fmt); 6897b11b857Smillert vfprintf(stderr, fmt, varg); 6907b11b857Smillert va_end(varg); 6916ab05f83Stholo fprintf(stderr, " at source line %d", lineno); 6926ab05f83Stholo if (curfname != NULL) 6936ab05f83Stholo fprintf(stderr, " in function %s", curfname); 694f81b289fSmillert if (compile_time == COMPILING && cursource() != NULL) 695271018d0Smillert fprintf(stderr, " source file %s", cursource()); 6966ab05f83Stholo fprintf(stderr, "\n"); 6976ab05f83Stholo errorflag = 2; 6986ab05f83Stholo eprint(); 6996ab05f83Stholo } 7006ab05f83Stholo 7016ab05f83Stholo extern int bracecnt, brackcnt, parencnt; 7026ab05f83Stholo 7036ab05f83Stholo void bracecheck(void) 7046ab05f83Stholo { 7056ab05f83Stholo int c; 7066ab05f83Stholo static int beenhere = 0; 7076ab05f83Stholo 7086ab05f83Stholo if (beenhere++) 7096ab05f83Stholo return; 710d3dcc631Smillert while ((c = input()) != EOF && c != '\0') 7116ab05f83Stholo bclass(c); 7126ab05f83Stholo bcheck2(bracecnt, '{', '}'); 7136ab05f83Stholo bcheck2(brackcnt, '[', ']'); 7146ab05f83Stholo bcheck2(parencnt, '(', ')'); 7156ab05f83Stholo } 7166ab05f83Stholo 7176ab05f83Stholo void bcheck2(int n, int c1, int c2) 7186ab05f83Stholo { 7196ab05f83Stholo if (n == 1) 7206ab05f83Stholo fprintf(stderr, "\tmissing %c\n", c2); 7216ab05f83Stholo else if (n > 1) 7226ab05f83Stholo fprintf(stderr, "\t%d missing %c's\n", n, c2); 7236ab05f83Stholo else if (n == -1) 7246ab05f83Stholo fprintf(stderr, "\textra %c\n", c2); 7256ab05f83Stholo else if (n < -1) 7266ab05f83Stholo fprintf(stderr, "\t%d extra %c's\n", -n, c2); 7276ab05f83Stholo } 7286ab05f83Stholo 7294edce374Smillert void FATAL(const char *fmt, ...) 7306ab05f83Stholo { 7316ab05f83Stholo extern char *cmdname; 7327b11b857Smillert va_list varg; 7336ab05f83Stholo 7346ab05f83Stholo fflush(stdout); 7356ab05f83Stholo fprintf(stderr, "%s: ", cmdname); 7367b11b857Smillert va_start(varg, fmt); 7377b11b857Smillert vfprintf(stderr, fmt, varg); 7387b11b857Smillert va_end(varg); 7397b11b857Smillert error(); 7407b11b857Smillert if (dbg > 1) /* core dump if serious debugging on */ 7417b11b857Smillert abort(); 7427b11b857Smillert exit(2); 7437b11b857Smillert } 7447b11b857Smillert 7459a69093aSmillert void WARNING(const char *fmt, ...) 7467b11b857Smillert { 7477b11b857Smillert extern char *cmdname; 7487b11b857Smillert va_list varg; 7497b11b857Smillert 7507b11b857Smillert fflush(stdout); 7517b11b857Smillert fprintf(stderr, "%s: ", cmdname); 7527b11b857Smillert va_start(varg, fmt); 7537b11b857Smillert vfprintf(stderr, fmt, varg); 7547b11b857Smillert va_end(varg); 7557b11b857Smillert error(); 7567b11b857Smillert } 7577b11b857Smillert 75888689b4cSmillert void error(void) 7597b11b857Smillert { 7607b11b857Smillert extern Node *curnode; 7617b11b857Smillert 7626ab05f83Stholo fprintf(stderr, "\n"); 763f81b289fSmillert if (compile_time != ERROR_PRINTING) { 764f81b289fSmillert if (NR && *NR > 0) { 7656ab05f83Stholo fprintf(stderr, " input record number %d", (int) (*FNR)); 7666ab05f83Stholo if (strcmp(*FILENAME, "-") != 0) 7676ab05f83Stholo fprintf(stderr, ", file %s", *FILENAME); 7686ab05f83Stholo fprintf(stderr, "\n"); 7696ab05f83Stholo } 770f81b289fSmillert if (curnode) 771271018d0Smillert fprintf(stderr, " source line number %d", curnode->lineno); 772f81b289fSmillert else if (lineno) 773271018d0Smillert fprintf(stderr, " source line number %d", lineno); 774f81b289fSmillert if (compile_time == COMPILING && cursource() != NULL) 775271018d0Smillert fprintf(stderr, " source file %s", cursource()); 776271018d0Smillert fprintf(stderr, "\n"); 7776ab05f83Stholo eprint(); 7786ab05f83Stholo } 779149ad3e3Smillert } 7806ab05f83Stholo 7816ab05f83Stholo void eprint(void) /* try to print context around error */ 7826ab05f83Stholo { 7836ab05f83Stholo char *p, *q; 7846ab05f83Stholo int c; 7856ab05f83Stholo static int been_here = 0; 7866ab05f83Stholo extern char ebuf[], *ep; 7876ab05f83Stholo 788f81b289fSmillert if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep) 7896ab05f83Stholo return; 7906ab05f83Stholo p = ep - 1; 7916ab05f83Stholo if (p > ebuf && *p == '\n') 7926ab05f83Stholo p--; 7936ab05f83Stholo for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) 7946ab05f83Stholo ; 7956ab05f83Stholo while (*p == '\n') 7966ab05f83Stholo p++; 7976ab05f83Stholo fprintf(stderr, " context is\n\t"); 7986ab05f83Stholo for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) 7996ab05f83Stholo ; 8006ab05f83Stholo for ( ; p < q; p++) 8016ab05f83Stholo if (*p) 8026ab05f83Stholo putc(*p, stderr); 8036ab05f83Stholo fprintf(stderr, " >>> "); 8046ab05f83Stholo for ( ; p < ep; p++) 8056ab05f83Stholo if (*p) 8066ab05f83Stholo putc(*p, stderr); 8076ab05f83Stholo fprintf(stderr, " <<< "); 8086ab05f83Stholo if (*ep) 8096ab05f83Stholo while ((c = input()) != '\n' && c != '\0' && c != EOF) { 8106ab05f83Stholo putc(c, stderr); 8116ab05f83Stholo bclass(c); 8126ab05f83Stholo } 8136ab05f83Stholo putc('\n', stderr); 8146ab05f83Stholo ep = ebuf; 8156ab05f83Stholo } 8166ab05f83Stholo 8176ab05f83Stholo void bclass(int c) 8186ab05f83Stholo { 8196ab05f83Stholo switch (c) { 8206ab05f83Stholo case '{': bracecnt++; break; 8216ab05f83Stholo case '}': bracecnt--; break; 8226ab05f83Stholo case '[': brackcnt++; break; 8236ab05f83Stholo case ']': brackcnt--; break; 8246ab05f83Stholo case '(': parencnt++; break; 8256ab05f83Stholo case ')': parencnt--; break; 8266ab05f83Stholo } 8276ab05f83Stholo } 8286ab05f83Stholo 8299a69093aSmillert double errcheck(double x, const char *s) 8306ab05f83Stholo { 8316ab05f83Stholo 8326ab05f83Stholo if (errno == EDOM) { 8336ab05f83Stholo errno = 0; 8347b11b857Smillert WARNING("%s argument out of domain", s); 8356ab05f83Stholo x = 1; 8366ab05f83Stholo } else if (errno == ERANGE) { 8376ab05f83Stholo errno = 0; 8387b11b857Smillert WARNING("%s result out of range", s); 8396ab05f83Stholo x = 1; 8406ab05f83Stholo } 8416ab05f83Stholo return x; 8426ab05f83Stholo } 8436ab05f83Stholo 8449a69093aSmillert int isclvar(const char *s) /* is s of form var=something ? */ 8456ab05f83Stholo { 8469a69093aSmillert const char *os = s; 8476ab05f83Stholo 848a27f5228Smillert if (!isalpha((uschar)*s) && *s != '_') 8496ab05f83Stholo return 0; 8506ab05f83Stholo for ( ; *s; s++) 851a27f5228Smillert if (!(isalnum((uschar)*s) || *s == '_')) 8526ab05f83Stholo break; 853203f9af3Smillert return *s == '=' && s > os; 8546ab05f83Stholo } 8556ab05f83Stholo 85607edfa4aSkstailey /* strtod is supposed to be a proper test of what's a valid number */ 857a27f5228Smillert /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ 858a27f5228Smillert /* wrong: violates 4.10.1.4 of ansi C standard */ 859483fa115Smillert 8600b359418Smillert /* well, not quite. As of C99, hex floating point is allowed. so this is 861483fa115Smillert * a bit of a mess. We work around the mess by checking for a hexadecimal 862483fa115Smillert * value and disallowing it. Similarly, we now follow gawk and allow only 863483fa115Smillert * +nan, -nan, +inf, and -inf for NaN and infinity values. 8640b359418Smillert */ 8656ab05f83Stholo 866483fa115Smillert /* 867483fa115Smillert * This routine now has a more complicated interface, the main point 868483fa115Smillert * being to avoid the double conversion of a string to double, and 869483fa115Smillert * also to convey out, if requested, the information that the numeric 870483fa115Smillert * value was a leading string or is all of the string. The latter bit 871483fa115Smillert * is used in getfval(). 872483fa115Smillert */ 873483fa115Smillert 874483fa115Smillert bool is_valid_number(const char *s, bool trailing_stuff_ok, 875483fa115Smillert bool *no_trailing, double *result) 8766ab05f83Stholo { 8776ab05f83Stholo double r; 8786ab05f83Stholo char *ep; 879483fa115Smillert bool retval = false; 8806de80fb8Smillert bool is_nan = false; 8816de80fb8Smillert bool is_inf = false; 882483fa115Smillert 883483fa115Smillert if (no_trailing) 884483fa115Smillert *no_trailing = false; 885483fa115Smillert 88664161c95Smillert while (isspace((uschar)*s)) 887483fa115Smillert s++; 888483fa115Smillert 889a886e62eSmillert /* no hex floating point, sorry */ 8906de80fb8Smillert if (s[0] == '0' && tolower((uschar)s[1]) == 'x') 891483fa115Smillert return false; 892483fa115Smillert 893a886e62eSmillert /* allow +nan, -nan, +inf, -inf, any other letter, no */ 894483fa115Smillert if (s[0] == '+' || s[0] == '-') { 8956de80fb8Smillert is_nan = (strncasecmp(s+1, "nan", 3) == 0); 8966de80fb8Smillert is_inf = (strncasecmp(s+1, "inf", 3) == 0); 8976de80fb8Smillert if ((is_nan || is_inf) 8986de80fb8Smillert && (isspace((uschar)s[4]) || s[4] == '\0')) 8996de80fb8Smillert goto convert; 9006de80fb8Smillert else if (! isdigit((uschar)s[1]) && s[1] != '.') 9016de80fb8Smillert return false; 902483fa115Smillert } 9036de80fb8Smillert else if (! isdigit((uschar)s[0]) && s[0] != '.') 904483fa115Smillert return false; 905483fa115Smillert 9066de80fb8Smillert convert: 9076ab05f83Stholo errno = 0; 9086ab05f83Stholo r = strtod(s, &ep); 90964161c95Smillert if (ep == s || errno == ERANGE) 910483fa115Smillert return false; 911483fa115Smillert 9126de80fb8Smillert if (isnan(r) && s[0] == '-' && signbit(r) == 0) 9136de80fb8Smillert r = -r; 9146de80fb8Smillert 915483fa115Smillert if (result != NULL) 916483fa115Smillert *result = r; 917483fa115Smillert 918f9297e05Smillert /* 919f9297e05Smillert * check for trailing stuff 920f9297e05Smillert */ 921f9297e05Smillert while (isspace((uschar)*ep)) 922f9297e05Smillert ep++; 923483fa115Smillert 9246de80fb8Smillert if (no_trailing != NULL) 925483fa115Smillert *no_trailing = (*ep == '\0'); 926483fa115Smillert 927a886e62eSmillert /* return true if found the end, or trailing stuff is allowed */ 928f9297e05Smillert retval = *ep == '\0' || trailing_stuff_ok; 929f9297e05Smillert 930483fa115Smillert return retval; 9316ab05f83Stholo } 932