usr.sbin/makefs/hammer2: Remove redundant hammer2_inode_modify()
[dragonfly.git] / contrib / awk / lib.c
blobebe296fa33febab7219b1588b5adf8f8b427b3e5
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
25 #define DEBUG
26 #include <stdio.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <limits.h>
34 #include <math.h>
35 #include "awk.h"
37 char EMPTY[] = { '\0' };
38 FILE *infile = NULL;
39 bool innew; /* true = infile has not been read by readrec */
40 char *file = EMPTY;
41 char *record;
42 int recsize = RECSIZE;
43 char *fields;
44 int fieldssize = RECSIZE;
46 Cell **fldtab; /* pointers to Cells */
47 static size_t len_inputFS = 0;
48 static char *inputFS = NULL; /* FS at time of input, for field splitting */
50 #define MAXFLD 2
51 int nfields = MAXFLD; /* last allocated slot for $i */
53 bool donefld; /* true = implies rec broken into fields */
54 bool donerec; /* true = record is valid (no flds have changed) */
56 int lastfld = 0; /* last used field */
57 int argno = 1; /* current input argument number */
58 extern Awkfloat *ARGC;
60 static Cell dollar0 = { OCELL, CFLD, NULL, EMPTY, 0.0, REC|STR|DONTFREE, NULL, NULL };
61 static Cell dollar1 = { OCELL, CFLD, NULL, EMPTY, 0.0, FLD|STR|DONTFREE, NULL, NULL };
63 void recinit(unsigned int n)
65 if ( (record = (char *) malloc(n)) == NULL
66 || (fields = (char *) malloc(n+1)) == NULL
67 || (fldtab = (Cell **) calloc(nfields+2, sizeof(*fldtab))) == NULL
68 || (fldtab[0] = (Cell *) malloc(sizeof(**fldtab))) == NULL)
69 FATAL("out of space for $0 and fields");
70 *record = '\0';
71 *fldtab[0] = dollar0;
72 fldtab[0]->sval = record;
73 fldtab[0]->nval = tostring("0");
74 makefields(1, nfields);
77 void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
79 char temp[50];
80 int i;
82 for (i = n1; i <= n2; i++) {
83 fldtab[i] = (Cell *) malloc(sizeof(**fldtab));
84 if (fldtab[i] == NULL)
85 FATAL("out of space in makefields %d", i);
86 *fldtab[i] = dollar1;
87 snprintf(temp, sizeof(temp), "%d", i);
88 fldtab[i]->nval = tostring(temp);
92 void initgetrec(void)
94 int i;
95 char *p;
97 for (i = 1; i < *ARGC; i++) {
98 p = getargv(i); /* find 1st real filename */
99 if (p == NULL || *p == '\0') { /* deleted or zapped */
100 argno++;
101 continue;
103 if (!isclvar(p)) {
104 setsval(lookup("FILENAME", symtab), p);
105 return;
107 setclvar(p); /* a commandline assignment before filename */
108 argno++;
110 infile = stdin; /* no filenames, so use stdin */
111 innew = true;
115 * POSIX specifies that fields are supposed to be evaluated as if they were
116 * split using the value of FS at the time that the record's value ($0) was
117 * read.
119 * Since field-splitting is done lazily, we save the current value of FS
120 * whenever a new record is read in (implicitly or via getline), or when
121 * a new value is assigned to $0.
123 void savefs(void)
125 size_t len;
126 if ((len = strlen(getsval(fsloc))) < len_inputFS) {
127 strcpy(inputFS, *FS); /* for subsequent field splitting */
128 return;
131 len_inputFS = len + 1;
132 inputFS = (char *) realloc(inputFS, len_inputFS);
133 if (inputFS == NULL)
134 FATAL("field separator %.10s... is too long", *FS);
135 memcpy(inputFS, *FS, len_inputFS);
138 static bool firsttime = true;
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;
147 if (firsttime) {
148 firsttime = false;
149 initgetrec();
151 DPRINTF("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
152 *RS, *FS, *ARGC, *FILENAME);
153 if (isrecord) {
154 donefld = false;
155 donerec = true;
156 savefs();
158 saveb0 = buf[0];
159 buf[0] = 0;
160 while (argno < *ARGC || infile == stdin) {
161 DPRINTF("argno=%d, file=|%s|\n", argno, file);
162 if (infile == NULL) { /* have to open a new file */
163 file = getargv(argno);
164 if (file == NULL || *file == '\0') { /* deleted or zapped */
165 argno++;
166 continue;
168 if (isclvar(file)) { /* a var=value arg */
169 setclvar(file);
170 argno++;
171 continue;
173 *FILENAME = file;
174 DPRINTF("opening file %s\n", file);
175 if (*file == '-' && *(file+1) == '\0')
176 infile = stdin;
177 else if ((infile = fopen(file, "r")) == NULL)
178 FATAL("can't open file %s", file);
179 innew = true;
180 setfval(fnrloc, 0.0);
182 c = readrec(&buf, &bufsize, infile, innew);
183 if (innew)
184 innew = false;
185 if (c != 0 || buf[0] != '\0') { /* normal record */
186 if (isrecord) {
187 double result;
189 if (freeable(fldtab[0]))
190 xfree(fldtab[0]->sval);
191 fldtab[0]->sval = buf; /* buf == record */
192 fldtab[0]->tval = REC | STR | DONTFREE;
193 if (is_number(fldtab[0]->sval, & result)) {
194 fldtab[0]->fval = result;
195 fldtab[0]->tval |= NUM;
198 setfval(nrloc, nrloc->fval+1);
199 setfval(fnrloc, fnrloc->fval+1);
200 *pbuf = buf;
201 *pbufsize = bufsize;
202 return 1;
204 /* EOF arrived on this file; set up next */
205 if (infile != stdin)
206 fclose(infile);
207 infile = NULL;
208 argno++;
210 buf[0] = saveb0;
211 *pbuf = buf;
212 *pbufsize = savebufsize;
213 return 0; /* true end of file */
216 void nextfile(void)
218 if (infile != NULL && infile != stdin)
219 fclose(infile);
220 infile = NULL;
221 argno++;
224 int readrec(char **pbuf, int *pbufsize, FILE *inf, bool newflag) /* read one record into buf */
226 int sep, c, isrec;
227 char *rr, *buf = *pbuf;
228 int bufsize = *pbufsize;
229 char *rs = getsval(rsloc);
231 if (*rs && rs[1]) {
232 bool found;
234 fa *pfa = makedfa(rs, 1);
235 if (newflag)
236 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
237 else {
238 int tempstat = pfa->initstat;
239 pfa->initstat = 2;
240 found = fnematch(pfa, inf, &buf, &bufsize, recsize);
241 pfa->initstat = tempstat;
243 if (found)
244 setptr(patbeg, '\0');
245 isrec = (found == 0 && *buf == '\0') ? false : true;
246 } else {
247 if ((sep = *rs) == 0) {
248 sep = '\n';
249 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
251 if (c != EOF)
252 ungetc(c, inf);
254 for (rr = buf; ; ) {
255 for (; (c=getc(inf)) != sep && c != EOF; ) {
256 if (rr-buf+1 > bufsize)
257 if (!adjbuf(&buf, &bufsize, 1+rr-buf,
258 recsize, &rr, "readrec 1"))
259 FATAL("input record `%.30s...' too long", buf);
260 *rr++ = c;
262 if (*rs == sep || c == EOF)
263 break;
264 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
265 break;
266 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr,
267 "readrec 2"))
268 FATAL("input record `%.30s...' too long", buf);
269 *rr++ = '\n';
270 *rr++ = c;
272 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
273 FATAL("input record `%.30s...' too long", buf);
274 *rr = 0;
275 isrec = (c == EOF && rr == buf) ? false : true;
277 *pbuf = buf;
278 *pbufsize = bufsize;
279 DPRINTF("readrec saw <%s>, returns %d\n", buf, isrec);
280 return isrec;
283 char *getargv(int n) /* get ARGV[n] */
285 Cell *x;
286 char *s, temp[50];
287 extern Array *ARGVtab;
289 snprintf(temp, sizeof(temp), "%d", n);
290 if (lookup(temp, ARGVtab) == NULL)
291 return NULL;
292 x = setsymtab(temp, "", 0.0, STR, ARGVtab);
293 s = getsval(x);
294 DPRINTF("getargv(%d) returns |%s|\n", n, s);
295 return s;
298 void setclvar(char *s) /* set var=value from s */
300 char *e, *p;
301 Cell *q;
302 double result;
304 for (p=s; *p != '='; p++)
306 e = p;
307 *p++ = 0;
308 p = qstring(p, '\0');
309 q = setsymtab(s, p, 0.0, STR, symtab);
310 setsval(q, p);
311 if (is_number(q->sval, & result)) {
312 q->fval = result;
313 q->tval |= NUM;
315 DPRINTF("command line set %s to |%s|\n", s, p);
316 free(p);
317 *e = '=';
321 void fldbld(void) /* create fields from current record */
323 /* this relies on having fields[] the same length as $0 */
324 /* the fields are all stored in this one array with \0's */
325 /* possibly with a final trailing \0 not associated with any field */
326 char *r, *fr, sep;
327 Cell *p;
328 int i, j, n;
330 if (donefld)
331 return;
332 if (!isstr(fldtab[0]))
333 getsval(fldtab[0]);
334 r = fldtab[0]->sval;
335 n = strlen(r);
336 if (n > fieldssize) {
337 xfree(fields);
338 if ((fields = (char *) malloc(n+2)) == NULL) /* possibly 2 final \0s */
339 FATAL("out of space for fields in fldbld %d", n);
340 fieldssize = n;
342 fr = fields;
343 i = 0; /* number of fields accumulated here */
344 if (inputFS == NULL) /* make sure we have a copy of FS */
345 savefs();
346 if (strlen(inputFS) > 1) { /* it's a regular expression */
347 i = refldbld(r, inputFS);
348 } else if ((sep = *inputFS) == ' ') { /* default whitespace */
349 for (i = 0; ; ) {
350 while (*r == ' ' || *r == '\t' || *r == '\n')
351 r++;
352 if (*r == 0)
353 break;
354 i++;
355 if (i > nfields)
356 growfldtab(i);
357 if (freeable(fldtab[i]))
358 xfree(fldtab[i]->sval);
359 fldtab[i]->sval = fr;
360 fldtab[i]->tval = FLD | STR | DONTFREE;
362 *fr++ = *r++;
363 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
364 *fr++ = 0;
366 *fr = 0;
367 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
368 for (i = 0; *r != '\0'; r += n) {
369 char buf[MB_LEN_MAX + 1];
371 i++;
372 if (i > nfields)
373 growfldtab(i);
374 if (freeable(fldtab[i]))
375 xfree(fldtab[i]->sval);
376 n = mblen(r, MB_LEN_MAX);
377 if (n < 0)
378 n = 1;
379 memcpy(buf, r, n);
380 buf[n] = '\0';
381 fldtab[i]->sval = tostring(buf);
382 fldtab[i]->tval = FLD | STR;
384 *fr = 0;
385 } else if (*r != 0) { /* if 0, it's a null field */
386 /* subtlecase : if length(FS) == 1 && length(RS > 0)
387 * \n is NOT a field separator (cf awk book 61,84).
388 * this variable is tested in the inner while loop.
390 int rtest = '\n'; /* normal case */
391 if (strlen(*RS) > 0)
392 rtest = '\0';
393 for (;;) {
394 i++;
395 if (i > nfields)
396 growfldtab(i);
397 if (freeable(fldtab[i]))
398 xfree(fldtab[i]->sval);
399 fldtab[i]->sval = fr;
400 fldtab[i]->tval = FLD | STR | DONTFREE;
401 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
402 *fr++ = *r++;
403 *fr++ = 0;
404 if (*r++ == 0)
405 break;
407 *fr = 0;
409 if (i > nfields)
410 FATAL("record `%.30s...' has too many fields; can't happen", r);
411 cleanfld(i+1, lastfld); /* clean out junk from previous record */
412 lastfld = i;
413 donefld = true;
414 for (j = 1; j <= lastfld; j++) {
415 double result;
417 p = fldtab[j];
418 if(is_number(p->sval, & result)) {
419 p->fval = result;
420 p->tval |= NUM;
423 setfval(nfloc, (Awkfloat) lastfld);
424 donerec = true; /* restore */
425 if (dbg) {
426 for (j = 0; j <= lastfld; j++) {
427 p = fldtab[j];
428 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
433 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
434 { /* nvals remain intact */
435 Cell *p;
436 int i;
438 for (i = n1; i <= n2; i++) {
439 p = fldtab[i];
440 if (freeable(p))
441 xfree(p->sval);
442 p->sval = EMPTY,
443 p->tval = FLD | STR | DONTFREE;
447 void newfld(int n) /* add field n after end of existing lastfld */
449 if (n > nfields)
450 growfldtab(n);
451 cleanfld(lastfld+1, n);
452 lastfld = n;
453 setfval(nfloc, (Awkfloat) n);
456 void setlastfld(int n) /* set lastfld cleaning fldtab cells if necessary */
458 if (n < 0)
459 FATAL("cannot set NF to a negative value");
460 if (n > nfields)
461 growfldtab(n);
463 if (lastfld < n)
464 cleanfld(lastfld+1, n);
465 else
466 cleanfld(n+1, lastfld);
468 lastfld = n;
471 Cell *fieldadr(int n) /* get nth field */
473 if (n < 0)
474 FATAL("trying to access out of range field %d", n);
475 if (n > nfields) /* fields after NF are empty */
476 growfldtab(n); /* but does not increase NF */
477 return(fldtab[n]);
480 void growfldtab(int n) /* make new fields up to at least $n */
482 int nf = 2 * nfields;
483 size_t s;
485 if (n > nf)
486 nf = n;
487 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
488 if (s / sizeof(struct Cell *) - 1 == (size_t)nf) /* didn't overflow */
489 fldtab = (Cell **) realloc(fldtab, s);
490 else /* overflow sizeof int */
491 xfree(fldtab); /* make it null */
492 if (fldtab == NULL)
493 FATAL("out of space creating %d fields", nf);
494 makefields(nfields+1, nf);
495 nfields = nf;
498 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
500 /* this relies on having fields[] the same length as $0 */
501 /* the fields are all stored in this one array with \0's */
502 char *fr;
503 int i, tempstat, n;
504 fa *pfa;
506 n = strlen(rec);
507 if (n > fieldssize) {
508 xfree(fields);
509 if ((fields = (char *) malloc(n+1)) == NULL)
510 FATAL("out of space for fields in refldbld %d", n);
511 fieldssize = n;
513 fr = fields;
514 *fr = '\0';
515 if (*rec == '\0')
516 return 0;
517 pfa = makedfa(fs, 1);
518 DPRINTF("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs);
519 tempstat = pfa->initstat;
520 for (i = 1; ; i++) {
521 if (i > nfields)
522 growfldtab(i);
523 if (freeable(fldtab[i]))
524 xfree(fldtab[i]->sval);
525 fldtab[i]->tval = FLD | STR | DONTFREE;
526 fldtab[i]->sval = fr;
527 DPRINTF("refldbld: i=%d\n", i);
528 if (nematch(pfa, rec)) {
529 pfa->initstat = 2; /* horrible coupling to b.c */
530 DPRINTF("match %s (%d chars)\n", patbeg, patlen);
531 strncpy(fr, rec, patbeg-rec);
532 fr += patbeg - rec + 1;
533 *(fr-1) = '\0';
534 rec = patbeg + patlen;
535 } else {
536 DPRINTF("no match %s\n", rec);
537 strcpy(fr, rec);
538 pfa->initstat = tempstat;
539 break;
542 return i;
545 void recbld(void) /* create $0 from $1..$NF if necessary */
547 int i;
548 char *r, *p;
549 char *sep = getsval(ofsloc);
551 if (donerec)
552 return;
553 r = record;
554 for (i = 1; i <= *NF; i++) {
555 p = getsval(fldtab[i]);
556 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
557 FATAL("created $0 `%.30s...' too long", record);
558 while ((*r = *p++) != 0)
559 r++;
560 if (i < *NF) {
561 if (!adjbuf(&record, &recsize, 2+strlen(sep)+r-record, recsize, &r, "recbld 2"))
562 FATAL("created $0 `%.30s...' too long", record);
563 for (p = sep; (*r = *p++) != 0; )
564 r++;
567 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
568 FATAL("built giant record `%.30s...'", record);
569 *r = '\0';
570 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
572 if (freeable(fldtab[0]))
573 xfree(fldtab[0]->sval);
574 fldtab[0]->tval = REC | STR | DONTFREE;
575 fldtab[0]->sval = record;
577 DPRINTF("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, (void*)fldtab[0]);
578 DPRINTF("recbld = |%s|\n", record);
579 donerec = true;
582 int errorflag = 0;
584 void yyerror(const char *s)
586 SYNTAX("%s", s);
589 void SYNTAX(const char *fmt, ...)
591 extern char *cmdname, *curfname;
592 static int been_here = 0;
593 va_list varg;
595 if (been_here++ > 2)
596 return;
597 fprintf(stderr, "%s: ", cmdname);
598 va_start(varg, fmt);
599 vfprintf(stderr, fmt, varg);
600 va_end(varg);
601 fprintf(stderr, " at source line %d", lineno);
602 if (curfname != NULL)
603 fprintf(stderr, " in function %s", curfname);
604 if (compile_time == COMPILING && cursource() != NULL)
605 fprintf(stderr, " source file %s", cursource());
606 fprintf(stderr, "\n");
607 errorflag = 2;
608 eprint();
611 extern int bracecnt, brackcnt, parencnt;
613 void bracecheck(void)
615 int c;
616 static int beenhere = 0;
618 if (beenhere++)
619 return;
620 while ((c = input()) != EOF && c != '\0')
621 bclass(c);
622 bcheck2(bracecnt, '{', '}');
623 bcheck2(brackcnt, '[', ']');
624 bcheck2(parencnt, '(', ')');
627 void bcheck2(int n, int c1, int c2)
629 if (n == 1)
630 fprintf(stderr, "\tmissing %c\n", c2);
631 else if (n > 1)
632 fprintf(stderr, "\t%d missing %c's\n", n, c2);
633 else if (n == -1)
634 fprintf(stderr, "\textra %c\n", c2);
635 else if (n < -1)
636 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
639 void FATAL(const char *fmt, ...)
641 extern char *cmdname;
642 va_list varg;
644 fflush(stdout);
645 fprintf(stderr, "%s: ", cmdname);
646 va_start(varg, fmt);
647 vfprintf(stderr, fmt, varg);
648 va_end(varg);
649 error();
650 if (dbg > 1) /* core dump if serious debugging on */
651 abort();
652 exit(2);
655 void WARNING(const char *fmt, ...)
657 extern char *cmdname;
658 va_list varg;
660 fflush(stdout);
661 fprintf(stderr, "%s: ", cmdname);
662 va_start(varg, fmt);
663 vfprintf(stderr, fmt, varg);
664 va_end(varg);
665 error();
668 void error()
670 extern Node *curnode;
672 fprintf(stderr, "\n");
673 if (compile_time != ERROR_PRINTING) {
674 if (NR && *NR > 0) {
675 fprintf(stderr, " input record number %d", (int) (*FNR));
676 if (strcmp(*FILENAME, "-") != 0)
677 fprintf(stderr, ", file %s", *FILENAME);
678 fprintf(stderr, "\n");
680 if (curnode)
681 fprintf(stderr, " source line number %d", curnode->lineno);
682 else if (lineno)
683 fprintf(stderr, " source line number %d", lineno);
684 if (compile_time == COMPILING && cursource() != NULL)
685 fprintf(stderr, " source file %s", cursource());
686 fprintf(stderr, "\n");
687 eprint();
691 void eprint(void) /* try to print context around error */
693 char *p, *q;
694 int c;
695 static int been_here = 0;
696 extern char ebuf[], *ep;
698 if (compile_time != COMPILING || been_here++ > 0 || ebuf == ep)
699 return;
700 if (ebuf == ep)
701 return;
702 p = ep - 1;
703 if (p > ebuf && *p == '\n')
704 p--;
705 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
707 while (*p == '\n')
708 p++;
709 fprintf(stderr, " context is\n\t");
710 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
712 for ( ; p < q; p++)
713 if (*p)
714 putc(*p, stderr);
715 fprintf(stderr, " >>> ");
716 for ( ; p < ep; p++)
717 if (*p)
718 putc(*p, stderr);
719 fprintf(stderr, " <<< ");
720 if (*ep)
721 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
722 putc(c, stderr);
723 bclass(c);
725 putc('\n', stderr);
726 ep = ebuf;
729 void bclass(int c)
731 switch (c) {
732 case '{': bracecnt++; break;
733 case '}': bracecnt--; break;
734 case '[': brackcnt++; break;
735 case ']': brackcnt--; break;
736 case '(': parencnt++; break;
737 case ')': parencnt--; break;
741 double errcheck(double x, const char *s)
744 if (errno == EDOM) {
745 errno = 0;
746 WARNING("%s argument out of domain", s);
747 x = 1;
748 } else if (errno == ERANGE) {
749 errno = 0;
750 WARNING("%s result out of range", s);
751 x = 1;
753 return x;
756 int isclvar(const char *s) /* is s of form var=something ? */
758 const char *os = s;
760 if (!isalpha((uschar) *s) && *s != '_')
761 return 0;
762 for ( ; *s; s++)
763 if (!(isalnum((uschar) *s) || *s == '_'))
764 break;
765 return *s == '=' && s > os;
768 /* strtod is supposed to be a proper test of what's a valid number */
769 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
770 /* wrong: violates 4.10.1.4 of ansi C standard */
772 /* well, not quite. As of C99, hex floating point is allowed. so this is
773 * a bit of a mess. We work around the mess by checking for a hexadecimal
774 * value and disallowing it. Similarly, we now follow gawk and allow only
775 * +nan, -nan, +inf, and -inf for NaN and infinity values.
779 * This routine now has a more complicated interface, the main point
780 * being to avoid the double conversion of a string to double, and
781 * also to convey out, if requested, the information that the numeric
782 * value was a leading string or is all of the string. The latter bit
783 * is used in getfval().
786 bool is_valid_number(const char *s, bool trailing_stuff_ok,
787 bool *no_trailing, double *result)
789 double r;
790 char *ep;
791 bool retval = false;
792 bool is_nan = false;
793 bool is_inf = false;
795 if (no_trailing)
796 *no_trailing = false;
798 while (isspace(*s))
799 s++;
801 // no hex floating point, sorry
802 if (s[0] == '0' && tolower(s[1]) == 'x')
803 return false;
805 // allow +nan, -nan, +inf, -inf, any other letter, no
806 if (s[0] == '+' || s[0] == '-') {
807 is_nan = (strncasecmp(s+1, "nan", 3) == 0);
808 is_inf = (strncasecmp(s+1, "inf", 3) == 0);
809 if ((is_nan || is_inf)
810 && (isspace(s[4]) || s[4] == '\0'))
811 goto convert;
812 else if (! isdigit(s[1]) && s[1] != '.')
813 return false;
815 else if (! isdigit(s[0]) && s[0] != '.')
816 return false;
818 convert:
819 errno = 0;
820 r = strtod(s, &ep);
821 if (ep == s || errno == ERANGE)
822 return false;
824 if (isnan(r) && s[0] == '-' && signbit(r) == 0)
825 r = -r;
827 if (result != NULL)
828 *result = r;
831 * check for trailing stuff
833 while (isspace(*ep))
834 ep++;
836 if (no_trailing != NULL)
837 *no_trailing = (*ep == '\0');
839 // return true if found the end, or trailing stuff is allowed
840 retval = *ep == '\0' || trailing_stuff_ok;
842 return retval;