1 /* $OpenBSD: lib.c,v 1.18 2008/10/06 20:38:33 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 <unistd.h> 33 #include <stdarg.h> 34 #include "awk.h" 35 #include "ytab.h" 36 37 FILE *infile = NULL; 38 char *file = ""; 39 char *record; 40 int recsize = RECSIZE; 41 char *fields; 42 int fieldssize = RECSIZE; 43 44 Cell **fldtab; /* pointers to Cells */ 45 char inputFS[100] = " "; 46 47 #define MAXFLD 2 48 int nfields = MAXFLD; /* last allocated slot for $i */ 49 50 int donefld; /* 1 = implies rec broken into fields */ 51 int donerec; /* 1 = record is valid (no flds have changed) */ 52 53 int lastfld = 0; /* last used field */ 54 int argno = 1; /* current input argument number */ 55 extern Awkfloat *ARGC; 56 57 static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE }; 58 static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE }; 59 60 void recinit(unsigned int n) 61 { 62 if ( (record = (char *) malloc(n)) == NULL 63 || (fields = (char *) malloc(n+1)) == NULL 64 || (fldtab = (Cell **) calloc(nfields+1, sizeof(Cell *))) == NULL 65 || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL ) 66 FATAL("out of space for $0 and fields"); 67 *fldtab[0] = dollar0; 68 fldtab[0]->sval = record; 69 fldtab[0]->nval = tostring("0"); 70 makefields(1, nfields); 71 } 72 73 void makefields(int n1, int n2) /* create $n1..$n2 inclusive */ 74 { 75 char temp[50]; 76 int i; 77 78 for (i = n1; i <= n2; i++) { 79 fldtab[i] = (Cell *) malloc(sizeof (struct Cell)); 80 if (fldtab[i] == NULL) 81 FATAL("out of space in makefields %d", i); 82 *fldtab[i] = dollar1; 83 snprintf(temp, sizeof temp, "%d", i); 84 fldtab[i]->nval = tostring(temp); 85 } 86 } 87 88 void initgetrec(void) 89 { 90 int i; 91 char *p; 92 93 for (i = 1; i < *ARGC; i++) { 94 if (!isclvar(p = getargv(i))) { /* find 1st real filename */ 95 setsval(lookup("FILENAME", symtab), getargv(i)); 96 return; 97 } 98 setclvar(p); /* a commandline assignment before filename */ 99 argno++; 100 } 101 infile = stdin; /* no filenames, so use stdin */ 102 } 103 104 static int firsttime = 1; 105 106 int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */ 107 { /* note: cares whether buf == record */ 108 int c; 109 char *buf = *pbuf; 110 uschar saveb0; 111 int bufsize = *pbufsize, savebufsize = bufsize; 112 113 if (firsttime) { 114 firsttime = 0; 115 initgetrec(); 116 } 117 dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n", 118 *RS, *FS, *ARGC, *FILENAME) ); 119 if (isrecord) { 120 donefld = 0; 121 donerec = 1; 122 } 123 saveb0 = buf[0]; 124 buf[0] = 0; 125 while (argno < *ARGC || infile == stdin) { 126 dprintf( ("argno=%d, file=|%s|\n", argno, file) ); 127 if (infile == NULL) { /* have to open a new file */ 128 file = getargv(argno); 129 if (*file == '\0') { /* it's been zapped */ 130 argno++; 131 continue; 132 } 133 if (isclvar(file)) { /* a var=value arg */ 134 setclvar(file); 135 argno++; 136 continue; 137 } 138 *FILENAME = file; 139 dprintf( ("opening file %s\n", file) ); 140 if (*file == '-' && *(file+1) == '\0') 141 infile = stdin; 142 else if ((infile = fopen(file, "r")) == NULL) 143 FATAL("can't open file %s", file); 144 setfval(fnrloc, 0.0); 145 } 146 c = readrec(&buf, &bufsize, infile); 147 if (c != 0 || buf[0] != '\0') { /* normal record */ 148 if (isrecord) { 149 if (freeable(fldtab[0])) 150 xfree(fldtab[0]->sval); 151 fldtab[0]->sval = buf; /* buf == record */ 152 fldtab[0]->tval = REC | STR | DONTFREE; 153 if (is_number(fldtab[0]->sval)) { 154 fldtab[0]->fval = atof(fldtab[0]->sval); 155 fldtab[0]->tval |= NUM; 156 } 157 } 158 setfval(nrloc, nrloc->fval+1); 159 setfval(fnrloc, fnrloc->fval+1); 160 *pbuf = buf; 161 *pbufsize = bufsize; 162 return 1; 163 } 164 /* EOF arrived on this file; set up next */ 165 if (infile != stdin) 166 fclose(infile); 167 infile = NULL; 168 argno++; 169 } 170 buf[0] = saveb0; 171 *pbuf = buf; 172 *pbufsize = savebufsize; 173 return 0; /* true end of file */ 174 } 175 176 void nextfile(void) 177 { 178 if (infile != NULL && infile != stdin) 179 fclose(infile); 180 infile = NULL; 181 argno++; 182 } 183 184 int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */ 185 { 186 int sep, c; 187 char *rr, *buf = *pbuf; 188 int bufsize = *pbufsize; 189 190 if (strlen(*FS) >= sizeof(inputFS)) 191 FATAL("field separator %.10s... is too long", *FS); 192 strlcpy(inputFS, *FS, sizeof inputFS); /* for subsequent field splitting */ 193 if ((sep = **RS) == 0) { 194 sep = '\n'; 195 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */ 196 ; 197 if (c != EOF) 198 ungetc(c, inf); 199 } 200 for (rr = buf; ; ) { 201 for (; (c=getc(inf)) != sep && c != EOF; ) { 202 if (rr-buf+1 > bufsize) 203 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1")) 204 FATAL("input record `%.30s...' too long", buf); 205 *rr++ = c; 206 } 207 if (**RS == sep || c == EOF) 208 break; 209 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */ 210 break; 211 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2")) 212 FATAL("input record `%.30s...' too long", buf); 213 *rr++ = '\n'; 214 *rr++ = c; 215 } 216 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3")) 217 FATAL("input record `%.30s...' too long", buf); 218 *rr = 0; 219 dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) ); 220 *pbuf = buf; 221 *pbufsize = bufsize; 222 return c == EOF && rr == buf ? 0 : 1; 223 } 224 225 char *getargv(int n) /* get ARGV[n] */ 226 { 227 Cell *x; 228 char *s, temp[50]; 229 extern Array *ARGVtab; 230 231 snprintf(temp, sizeof temp, "%d", n); 232 x = setsymtab(temp, "", 0.0, STR, ARGVtab); 233 s = getsval(x); 234 dprintf( ("getargv(%d) returns |%s|\n", n, s) ); 235 return s; 236 } 237 238 void setclvar(char *s) /* set var=value from s */ 239 { 240 char *p; 241 Cell *q; 242 243 for (p=s; *p != '='; p++) 244 ; 245 *p++ = 0; 246 p = qstring(p, '\0'); 247 q = setsymtab(s, p, 0.0, STR, symtab); 248 setsval(q, p); 249 if (is_number(q->sval)) { 250 q->fval = atof(q->sval); 251 q->tval |= NUM; 252 } 253 dprintf( ("command line set %s to |%s|\n", s, p) ); 254 } 255 256 257 void fldbld(void) /* create fields from current record */ 258 { 259 /* this relies on having fields[] the same length as $0 */ 260 /* the fields are all stored in this one array with \0's */ 261 char *r, *fr, sep; 262 Cell *p; 263 int i, j, n; 264 265 if (donefld) 266 return; 267 if (!isstr(fldtab[0])) 268 getsval(fldtab[0]); 269 r = fldtab[0]->sval; 270 n = strlen(r); 271 if (n > fieldssize) { 272 xfree(fields); 273 if ((fields = (char *) malloc(n+1)) == NULL) 274 FATAL("out of space for fields in fldbld %d", n); 275 fieldssize = n; 276 } 277 fr = fields; 278 i = 0; /* number of fields accumulated here */ 279 if (strlen(inputFS) > 1) { /* it's a regular expression */ 280 i = refldbld(r, inputFS); 281 } else if ((sep = *inputFS) == ' ') { /* default whitespace */ 282 for (i = 0; ; ) { 283 while (*r == ' ' || *r == '\t' || *r == '\n') 284 r++; 285 if (*r == 0) 286 break; 287 i++; 288 if (i > nfields) 289 growfldtab(i); 290 if (freeable(fldtab[i])) 291 xfree(fldtab[i]->sval); 292 fldtab[i]->sval = fr; 293 fldtab[i]->tval = FLD | STR | DONTFREE; 294 do 295 *fr++ = *r++; 296 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0'); 297 *fr++ = 0; 298 } 299 *fr = 0; 300 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */ 301 for (i = 0; *r != 0; r++) { 302 char buf[2]; 303 i++; 304 if (i > nfields) 305 growfldtab(i); 306 if (freeable(fldtab[i])) 307 xfree(fldtab[i]->sval); 308 buf[0] = *r; 309 buf[1] = 0; 310 fldtab[i]->sval = tostring(buf); 311 fldtab[i]->tval = FLD | STR; 312 } 313 *fr = 0; 314 } else if (*r != 0) { /* if 0, it's a null field */ 315 /* subtlecase : if length(FS) == 1 && length(RS > 0) 316 * \n is NOT a field separator (cf awk book 61,84). 317 * this variable is tested in the inner while loop. 318 */ 319 int rtest = '\n'; /* normal case */ 320 if (strlen(*RS) > 0) 321 rtest = '\0'; 322 for (;;) { 323 i++; 324 if (i > nfields) 325 growfldtab(i); 326 if (freeable(fldtab[i])) 327 xfree(fldtab[i]->sval); 328 fldtab[i]->sval = fr; 329 fldtab[i]->tval = FLD | STR | DONTFREE; 330 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */ 331 *fr++ = *r++; 332 *fr++ = 0; 333 if (*r++ == 0) 334 break; 335 } 336 *fr = 0; 337 } 338 if (i > nfields) 339 FATAL("record `%.30s...' has too many fields; can't happen", r); 340 cleanfld(i+1, lastfld); /* clean out junk from previous record */ 341 lastfld = i; 342 donefld = 1; 343 for (j = 1; j <= lastfld; j++) { 344 p = fldtab[j]; 345 if(is_number(p->sval)) { 346 p->fval = atof(p->sval); 347 p->tval |= NUM; 348 } 349 } 350 setfval(nfloc, (Awkfloat) lastfld); 351 if (dbg) { 352 for (j = 0; j <= lastfld; j++) { 353 p = fldtab[j]; 354 printf("field %d (%s): |%s|\n", j, p->nval, p->sval); 355 } 356 } 357 } 358 359 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */ 360 { /* nvals remain intact */ 361 Cell *p; 362 int i; 363 364 for (i = n1; i <= n2; i++) { 365 p = fldtab[i]; 366 if (freeable(p)) 367 xfree(p->sval); 368 p->sval = ""; 369 p->tval = FLD | STR | DONTFREE; 370 } 371 } 372 373 void newfld(int n) /* add field n after end of existing lastfld */ 374 { 375 if (n > nfields) 376 growfldtab(n); 377 cleanfld(lastfld+1, n); 378 lastfld = n; 379 setfval(nfloc, (Awkfloat) n); 380 } 381 382 Cell *fieldadr(int n) /* get nth field */ 383 { 384 if (n < 0) 385 FATAL("trying to access out of range field %d", n); 386 if (n > nfields) /* fields after NF are empty */ 387 growfldtab(n); /* but does not increase NF */ 388 return(fldtab[n]); 389 } 390 391 void growfldtab(int n) /* make new fields up to at least $n */ 392 { 393 int nf = 2 * nfields; 394 size_t s; 395 396 if (n > nf) 397 nf = n; 398 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */ 399 if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */ 400 fldtab = (Cell **) realloc(fldtab, s); 401 else /* overflow sizeof int */ 402 xfree(fldtab); /* make it null */ 403 if (fldtab == NULL) 404 FATAL("out of space creating %d fields", nf); 405 makefields(nfields+1, nf); 406 nfields = nf; 407 } 408 409 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */ 410 { 411 /* this relies on having fields[] the same length as $0 */ 412 /* the fields are all stored in this one array with \0's */ 413 char *fr; 414 int i, tempstat, n; 415 fa *pfa; 416 417 n = strlen(rec); 418 if (n > fieldssize) { 419 xfree(fields); 420 if ((fields = (char *) malloc(n+1)) == NULL) 421 FATAL("out of space for fields in refldbld %d", n); 422 fieldssize = n; 423 } 424 fr = fields; 425 *fr = '\0'; 426 if (*rec == '\0') 427 return 0; 428 pfa = makedfa(fs, 1); 429 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) ); 430 tempstat = pfa->initstat; 431 for (i = 1; ; i++) { 432 if (i > nfields) 433 growfldtab(i); 434 if (freeable(fldtab[i])) 435 xfree(fldtab[i]->sval); 436 fldtab[i]->tval = FLD | STR | DONTFREE; 437 fldtab[i]->sval = fr; 438 dprintf( ("refldbld: i=%d\n", i) ); 439 if (nematch(pfa, rec)) { 440 pfa->initstat = 2; /* horrible coupling to b.c */ 441 dprintf( ("match %s (%d chars)\n", patbeg, patlen) ); 442 strncpy(fr, rec, patbeg-rec); 443 fr += patbeg - rec + 1; 444 *(fr-1) = '\0'; 445 rec = patbeg + patlen; 446 } else { 447 dprintf( ("no match %s\n", rec) ); 448 strlcpy(fr, rec, fields + fieldssize - fr); 449 pfa->initstat = tempstat; 450 break; 451 } 452 } 453 return i; 454 } 455 456 void recbld(void) /* create $0 from $1..$NF if necessary */ 457 { 458 int i; 459 char *r, *p; 460 461 if (donerec == 1) 462 return; 463 r = record; 464 for (i = 1; i <= *NF; i++) { 465 p = getsval(fldtab[i]); 466 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1")) 467 FATAL("created $0 `%.30s...' too long", record); 468 while ((*r = *p++) != 0) 469 r++; 470 if (i < *NF) { 471 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2")) 472 FATAL("created $0 `%.30s...' too long", record); 473 for (p = *OFS; (*r = *p++) != 0; ) 474 r++; 475 } 476 } 477 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3")) 478 FATAL("built giant record `%.30s...'", record); 479 *r = '\0'; 480 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); 481 482 if (freeable(fldtab[0])) 483 xfree(fldtab[0]->sval); 484 fldtab[0]->tval = REC | STR | DONTFREE; 485 fldtab[0]->sval = record; 486 487 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) ); 488 dprintf( ("recbld = |%s|\n", record) ); 489 donerec = 1; 490 } 491 492 int errorflag = 0; 493 494 void yyerror(const char *s) 495 { 496 SYNTAX("%s", s); 497 } 498 499 void SYNTAX(const char *fmt, ...) 500 { 501 extern char *cmdname, *curfname; 502 static int been_here = 0; 503 va_list varg; 504 505 if (been_here++ > 2) 506 return; 507 fprintf(stderr, "%s: ", cmdname); 508 va_start(varg, fmt); 509 vfprintf(stderr, fmt, varg); 510 va_end(varg); 511 fprintf(stderr, " at source line %d", lineno); 512 if (curfname != NULL) 513 fprintf(stderr, " in function %s", curfname); 514 if (compile_time == 1 && cursource() != NULL) 515 fprintf(stderr, " source file %s", cursource()); 516 fprintf(stderr, "\n"); 517 errorflag = 2; 518 eprint(); 519 } 520 521 void fpecatch(int sig) 522 { 523 extern Node *curnode; 524 char buf[1024]; 525 526 snprintf(buf, sizeof buf, "floating point exception\n"); 527 write(STDERR_FILENO, buf, strlen(buf)); 528 529 if (compile_time != 2 && NR && *NR > 0) { 530 snprintf(buf, sizeof buf, " input record number %d", (int) (*FNR)); 531 write(STDERR_FILENO, buf, strlen(buf)); 532 533 if (strcmp(*FILENAME, "-") != 0) { 534 snprintf(buf, sizeof buf, ", file %s", *FILENAME); 535 write(STDERR_FILENO, buf, strlen(buf)); 536 } 537 write(STDERR_FILENO, "\n", 1); 538 } 539 if (compile_time != 2 && curnode) { 540 snprintf(buf, sizeof buf, " source line number %d", curnode->lineno); 541 write(STDERR_FILENO, buf, strlen(buf)); 542 } else if (compile_time != 2 && lineno) { 543 snprintf(buf, sizeof buf, " source line number %d", lineno); 544 write(STDERR_FILENO, buf, strlen(buf)); 545 } 546 if (compile_time == 1 && cursource() != NULL) { 547 snprintf(buf, sizeof buf, " source file %s", cursource()); 548 write(STDERR_FILENO, buf, strlen(buf)); 549 } 550 write(STDERR_FILENO, "\n", 1); 551 if (dbg > 1) /* core dump if serious debugging on */ 552 abort(); 553 _exit(1); 554 } 555 556 extern int bracecnt, brackcnt, parencnt; 557 558 void bracecheck(void) 559 { 560 int c; 561 static int beenhere = 0; 562 563 if (beenhere++) 564 return; 565 while ((c = input()) != EOF && c != '\0') 566 bclass(c); 567 bcheck2(bracecnt, '{', '}'); 568 bcheck2(brackcnt, '[', ']'); 569 bcheck2(parencnt, '(', ')'); 570 } 571 572 void bcheck2(int n, int c1, int c2) 573 { 574 if (n == 1) 575 fprintf(stderr, "\tmissing %c\n", c2); 576 else if (n > 1) 577 fprintf(stderr, "\t%d missing %c's\n", n, c2); 578 else if (n == -1) 579 fprintf(stderr, "\textra %c\n", c2); 580 else if (n < -1) 581 fprintf(stderr, "\t%d extra %c's\n", -n, c2); 582 } 583 584 void FATAL(const char *fmt, ...) 585 { 586 extern char *cmdname; 587 va_list varg; 588 589 fflush(stdout); 590 fprintf(stderr, "%s: ", cmdname); 591 va_start(varg, fmt); 592 vfprintf(stderr, fmt, varg); 593 va_end(varg); 594 error(); 595 if (dbg > 1) /* core dump if serious debugging on */ 596 abort(); 597 exit(2); 598 } 599 600 void WARNING(const char *fmt, ...) 601 { 602 extern char *cmdname; 603 va_list varg; 604 605 fflush(stdout); 606 fprintf(stderr, "%s: ", cmdname); 607 va_start(varg, fmt); 608 vfprintf(stderr, fmt, varg); 609 va_end(varg); 610 error(); 611 } 612 613 void error() 614 { 615 extern Node *curnode; 616 617 fprintf(stderr, "\n"); 618 if (compile_time != 2 && NR && *NR > 0) { 619 fprintf(stderr, " input record number %d", (int) (*FNR)); 620 if (strcmp(*FILENAME, "-") != 0) 621 fprintf(stderr, ", file %s", *FILENAME); 622 fprintf(stderr, "\n"); 623 } 624 if (compile_time != 2 && curnode) 625 fprintf(stderr, " source line number %d", curnode->lineno); 626 else if (compile_time != 2 && lineno) 627 fprintf(stderr, " source line number %d", lineno); 628 if (compile_time == 1 && cursource() != NULL) 629 fprintf(stderr, " source file %s", cursource()); 630 fprintf(stderr, "\n"); 631 eprint(); 632 } 633 634 void eprint(void) /* try to print context around error */ 635 { 636 char *p, *q; 637 int c; 638 static int been_here = 0; 639 extern char ebuf[], *ep; 640 641 if (compile_time == 2 || compile_time == 0 || been_here++ > 0) 642 return; 643 p = ep - 1; 644 if (p > ebuf && *p == '\n') 645 p--; 646 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--) 647 ; 648 while (*p == '\n') 649 p++; 650 fprintf(stderr, " context is\n\t"); 651 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--) 652 ; 653 for ( ; p < q; p++) 654 if (*p) 655 putc(*p, stderr); 656 fprintf(stderr, " >>> "); 657 for ( ; p < ep; p++) 658 if (*p) 659 putc(*p, stderr); 660 fprintf(stderr, " <<< "); 661 if (*ep) 662 while ((c = input()) != '\n' && c != '\0' && c != EOF) { 663 putc(c, stderr); 664 bclass(c); 665 } 666 putc('\n', stderr); 667 ep = ebuf; 668 } 669 670 void bclass(int c) 671 { 672 switch (c) { 673 case '{': bracecnt++; break; 674 case '}': bracecnt--; break; 675 case '[': brackcnt++; break; 676 case ']': brackcnt--; break; 677 case '(': parencnt++; break; 678 case ')': parencnt--; break; 679 } 680 } 681 682 double errcheck(double x, const char *s) 683 { 684 685 if (errno == EDOM) { 686 errno = 0; 687 WARNING("%s argument out of domain", s); 688 x = 1; 689 } else if (errno == ERANGE) { 690 errno = 0; 691 WARNING("%s result out of range", s); 692 x = 1; 693 } 694 return x; 695 } 696 697 int isclvar(const char *s) /* is s of form var=something ? */ 698 { 699 const char *os = s; 700 701 if (!isalpha((uschar) *s) && *s != '_') 702 return 0; 703 for ( ; *s; s++) 704 if (!(isalnum((uschar) *s) || *s == '_')) 705 break; 706 return *s == '=' && s > os && *(s+1) != '='; 707 } 708 709 /* strtod is supposed to be a proper test of what's a valid number */ 710 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */ 711 /* wrong: violates 4.10.1.4 of ansi C standard */ 712 713 #include <math.h> 714 int is_number(const char *s) 715 { 716 double r; 717 char *ep; 718 errno = 0; 719 r = strtod(s, &ep); 720 if (ep == s || r == HUGE_VAL || errno == ERANGE) 721 return 0; 722 while (*ep == ' ' || *ep == '\t' || *ep == '\n') 723 ep++; 724 if (*ep == '\0') 725 return 1; 726 else 727 return 0; 728 } 729