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