1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright (C) Lucent Technologies 1997
  29  * All Rights Reserved
  30  *
  31  * Permission to use, copy, modify, and distribute this software and
  32  * its documentation for any purpose and without fee is hereby
  33  * granted, provided that the above copyright notice appear in all
  34  * copies and that both that the copyright notice and this
  35  * permission notice and warranty disclaimer appear in supporting
  36  * documentation, and that the name Lucent Technologies or any of
  37  * its entities not be used in advertising or publicity pertaining
  38  * to distribution of the software without specific, written prior
  39  * permission.
  40  *
  41  * LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  42  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
  43  * IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
  44  * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  45  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  46  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  47  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
  48  * THIS SOFTWARE.
  49  */
  50 
  51 #include <errno.h>
  52 #include <stdarg.h>
  53 #include <math.h>
  54 #include "awk.h"
  55 #include "y.tab.h"
  56 
  57 uchar   *record;
  58 size_t  record_size = RECSIZE;
  59 
  60 Cell    **fldtab;       /* pointers to Cells */
  61 char    inputFS[100] = " ";
  62 
  63 #define MAXFLD  2
  64 int     nfields = MAXFLD;       /* last allocated slot for $i */
  65 
  66 int     donefld;        /* 1 = implies rec broken into fields */
  67 int     donerec;        /* 1 = record is valid (no flds have changed) */
  68 
  69 int     lastfld = 0;    /* last used field */
  70 
  71 static FILE     *infile = NULL;
  72 static uchar    *file   = (uchar*) "";
  73 static uchar    *fields;
  74 static size_t   fields_size = RECSIZE;
  75 
  76 static int      argno   = 1;    /* current input argument number */
  77 
  78 static  uchar   *getargv(int);
  79 static  void    cleanfld(int, int);
  80 static  int     refldbld(uchar *, uchar *);
  81 static  void    bcheck2(int, int, int);
  82 static  void    eprint(void);
  83 static  void    bclass(int);
  84 static  void    makefields(int, int);
  85 
  86 
  87 static Cell dollar0 = { OCELL, CFLD, NULL, (uchar *)"", 0.0, REC|STR|DONTFREE };
  88 static Cell dollar1 = { OCELL, CFLD, NULL, (uchar *)"", 0.0, FLD|STR|DONTFREE };
  89 
  90 void
  91 recinit(unsigned int n)
  92 {
  93         if ((record = (uchar *)malloc(n)) == NULL ||
  94             (fields = (uchar *)malloc(n+1)) == NULL ||
  95             (fldtab = (Cell **)malloc((nfields + 1) *
  96             sizeof (Cell *))) == NULL ||
  97             (fldtab[0] = (Cell *)malloc(sizeof (Cell))) == NULL)
  98                 FATAL("out of space for $0 and fields");
  99         *fldtab[0] = dollar0;
 100         fldtab[0]->sval = record;
 101         fldtab[0]->nval = tostring((uchar *)"0");
 102         makefields(1, nfields);
 103 }
 104 
 105 static void
 106 makefields(int n1, int n2)      /* create $n1..$n2 inclusive */
 107 {
 108         char temp[50];
 109         int i;
 110 
 111         for (i = n1; i <= n2; i++) {
 112                 fldtab[i] = (Cell *)malloc(sizeof (struct Cell));
 113                 if (fldtab[i] == NULL)
 114                         FATAL("out of space in makefields %d", i);
 115                 *fldtab[i] = dollar1;
 116                 (void) sprintf(temp, "%d", i);
 117                 fldtab[i]->nval = tostring((uchar *)temp);
 118         }
 119 }
 120 
 121 static void
 122 initgetrec(void)
 123 {
 124         int i;
 125         uchar *p;
 126 
 127         for (i = 1; i < *ARGC; i++) {
 128                 p = getargv(i); /* find 1st real filename */
 129                 if (p == NULL || *p == '\0') {  /* deleted or zapped */
 130                         argno++;
 131                         continue;
 132                 }
 133                 if (!isclvar(p)) {
 134                         (void) setsval(lookup((uchar *)"FILENAME", symtab), p);
 135                         return;
 136                 }
 137                 setclvar(p);    /* a commandline assignment before filename */
 138                 argno++;
 139         }
 140         infile = stdin;         /* no filenames, so use stdin */
 141 }
 142 
 143 static int firsttime = 1;
 144 
 145 int
 146 getrec(uchar **pbuf, size_t *pbufsize, int isrecord)
 147 {                               /* get next input record */
 148                                 /* note: cares whether buf == record */
 149         int c;
 150         uchar_t *buf = *pbuf;
 151         uchar saveb0;
 152         size_t bufsize = *pbufsize, savebufsize = bufsize;
 153 
 154         if (firsttime) {
 155                 firsttime = 0;
 156                 initgetrec();
 157         }
 158         dprintf(("RS=<%s>, FS=<%s>, ARGC=%f, FILENAME=%s\n",
 159             *RS, *FS, *ARGC, *FILENAME));
 160         if (isrecord) {
 161                 donefld = 0;
 162                 donerec = 1;
 163         }
 164         saveb0 = buf[0];
 165         buf[0] = 0;
 166         while (argno < *ARGC || infile == stdin) {
 167                 dprintf(("argno=%d, file=|%s|\n", argno, file));
 168                 if (infile == NULL) {   /* have to open a new file */
 169                         file = getargv(argno);
 170                         /* deleted or zapped */
 171                         if (file == NULL || *file == '\0') {
 172                                 argno++;
 173                                 continue;
 174                         }
 175                         if (isclvar(file)) {    /* a var=value arg */
 176                                 setclvar(file);
 177                                 argno++;
 178                                 continue;
 179                         }
 180                         *FILENAME = file;
 181                         dprintf(("opening file %s\n", file));
 182                         if (*file == '-' && *(file+1) == '\0')
 183                                 infile = stdin;
 184                         else if ((infile = fopen((char *)file, "r")) == NULL)
 185                                 FATAL("can't open file %s", file);
 186                         (void) setfval(fnrloc, 0.0);
 187                 }
 188                 c = readrec(&buf, &bufsize, infile);
 189                 if (c != 0 || buf[0] != '\0') { /* normal record */
 190                         if (isrecord) {
 191                                 if (freeable(fldtab[0]))
 192                                         xfree(fldtab[0]->sval);
 193                                 fldtab[0]->sval = buf;       /* buf == record */
 194                                 fldtab[0]->tval = REC | STR | DONTFREE;
 195                                 if (is_number(fldtab[0]->sval)) {
 196                                         fldtab[0]->fval =
 197                                             atof((const char *)fldtab[0]->sval);
 198                                         fldtab[0]->tval |= NUM;
 199                                 }
 200                         }
 201                         (void) setfval(nrloc, nrloc->fval+1);
 202                         (void) setfval(fnrloc, fnrloc->fval+1);
 203                         *pbuf = buf;
 204                         *pbufsize = bufsize;
 205                         return (1);
 206                 }
 207                 /* EOF arrived on this file; set up next */
 208                 if (infile != stdin)
 209                         (void) fclose(infile);
 210                 infile = NULL;
 211                 argno++;
 212         }
 213         buf[0] = saveb0;
 214         *pbuf = buf;
 215         *pbufsize = savebufsize;
 216         return (0);     /* true end of file */
 217 }
 218 
 219 void
 220 nextfile(void)
 221 {
 222         if (infile != NULL && infile != stdin)
 223                 (void) fclose(infile);
 224         infile = NULL;
 225         argno++;
 226 }
 227 
 228 /*
 229  * read one record into buf
 230  */
 231 int
 232 readrec(uchar **pbuf, size_t *pbufsize, FILE *inf)
 233 {
 234         int sep, c;
 235         uchar *rr, *buf = *pbuf;
 236         size_t bufsize = *pbufsize;
 237 
 238         if (strlen((char *)*FS) >= sizeof (inputFS))
 239                 FATAL("field separator %.10s... is too long", *FS);
 240         /*
 241          * fflush(stdout); avoids some buffering problem
 242          * but makes it 25% slower
 243          */
 244 
 245         /* for subsequent field splitting */
 246         (void) strcpy(inputFS, (char *)*FS);
 247         if ((sep = **RS) == 0) {
 248                 sep = '\n';
 249                 /* skip leading \n's */
 250                 while ((c = getc(inf)) == '\n' && c != EOF)
 251                         ;
 252                 if (c != EOF)
 253                         (void) ungetc(c, inf);
 254         }
 255         for (rr = buf; ; ) {
 256                 for (; (c = getc(inf)) != sep && c != EOF; ) {
 257                         if (rr-buf+1 > bufsize)
 258                                 if (!adjbuf(&buf, &bufsize, 1+rr-buf,
 259                                     record_size, &rr, "readrec 1"))
 260                                         FATAL(
 261                                 "input record `%.30s...' too long", buf);
 262                         *rr++ = c;
 263                 }
 264                 if (**RS == sep || c == EOF)
 265                         break;
 266                 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
 267                         break;
 268                 if (!adjbuf(&buf, &bufsize, 2+rr-buf, record_size, &rr,
 269                     "readrec 2"))
 270                         FATAL("input record `%.30s...' too long", buf);
 271                 *rr++ = '\n';
 272                 *rr++ = c;
 273         }
 274         if (!adjbuf(&buf, &bufsize, 1+rr-buf, record_size, &rr, "readrec 3"))
 275                 FATAL("input record `%.30s...' too long", buf);
 276         *rr = 0;
 277         dprintf(("readrec saw <%s>, returns %d\n",
 278             buf, c == EOF && rr == buf ? 0 : 1));
 279         *pbuf = buf;
 280         *pbufsize = bufsize;
 281         return (c == EOF && rr == buf ? 0 : 1);
 282 }
 283 
 284 static uchar *
 285 getargv(int n)          /* get ARGV[n] */
 286 {
 287         Cell *x;
 288         uchar *s, temp[50];
 289         extern Array *ARGVtab;
 290 
 291         (void) sprintf((char *)temp, "%d", n);
 292         if (lookup(temp, ARGVtab) == NULL)
 293                 return (NULL);
 294         x = setsymtab(temp, (uchar *)"", 0.0, STR, ARGVtab);
 295         s = getsval(x);
 296         dprintf(("getargv(%d) returns |%s|\n", n, s));
 297         return (s);
 298 }
 299 
 300 void
 301 setclvar(uchar *s)      /* set var=value from s */
 302 {
 303         uchar *p;
 304         Cell *q;
 305 
 306         for (p = s; *p != '='; p++)
 307                 ;
 308         *p++ = 0;
 309         p = qstring(p, '\0');
 310         q = setsymtab(s, p, 0.0, STR, symtab);
 311         (void) setsval(q, p);
 312         if (is_number(q->sval)) {
 313                 q->fval = atof((const char *)q->sval);
 314                 q->tval |= NUM;
 315         }
 316         dprintf(("command line set %s to |%s|\n", s, p));
 317 }
 318 
 319 void
 320 fldbld(void)    /* create fields from current record */
 321 {
 322         /* this relies on having fields[] the same length as $0 */
 323         /* the fields are all stored in this one array with \0's */
 324         /* possibly with a final trailing \0 not associated with any field */
 325         uchar *r, *fr, sep;
 326         Cell *p;
 327         int i, j;
 328         size_t n;
 329 
 330         if (donefld)
 331                 return;
 332         if (!isstr(fldtab[0]))
 333                 (void) getsval(fldtab[0]);
 334         r = fldtab[0]->sval;
 335         n = strlen((char *)r);
 336         if (n > fields_size) {
 337                 xfree(fields);
 338                 /* possibly 2 final \0s */
 339                 if ((fields = (uchar *)malloc(n + 2)) == NULL)
 340                         FATAL("out of space for fields in fldbld %d", n);
 341                 fields_size = n;
 342         }
 343         fr = fields;
 344         i = 0;  /* number of fields accumulated here */
 345         (void) strcpy(inputFS, (char *)*FS);
 346         if (strlen(inputFS) > 1) {   /* it's a regular expression */
 347                 i = refldbld(r, (uchar *)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;
 361                         do
 362                                 *fr++ = *r++;
 363                         while (*r != ' ' && *r != '\t' && *r != '\n' &&
 364                             *r != '\0')
 365                                 ;
 366                         *fr++ = 0;
 367                 }
 368                 *fr = 0;
 369         } else if ((sep = *inputFS) == 0) {     /* new: FS="" => 1 char/field */
 370                 for (i = 0; *r != 0; r++) {
 371                         uchar buf[2];
 372                         i++;
 373                         if (i > nfields)
 374                                 growfldtab(i);
 375                         if (freeable(fldtab[i]))
 376                                 xfree(fldtab[i]->sval);
 377                         buf[0] = *r;
 378                         buf[1] = 0;
 379                         fldtab[i]->sval = tostring(buf);
 380                         fldtab[i]->tval = FLD | STR;
 381                 }
 382                 *fr = 0;
 383         } else if (*r != 0) {   /* if 0, it's a null field */
 384                 /*
 385                  * subtlecase : if length(FS) == 1 && length(RS > 0)
 386                  * \n is NOT a field separator (cf awk book 61,84).
 387                  * this variable is tested in the inner while loop.
 388                  */
 389                 int rtest = '\n';  /* normal case */
 390                 if (strlen((char *)*RS) > 0)
 391                         rtest = '\0';
 392                 for (;;) {
 393                         i++;
 394                         if (i > nfields)
 395                                 growfldtab(i);
 396                         if (freeable(fldtab[i]))
 397                                 xfree(fldtab[i]->sval);
 398                         fldtab[i]->sval = fr;
 399                         fldtab[i]->tval = FLD | STR | DONTFREE;
 400                         /* \n is always a separator */
 401                         while (*r != sep && *r != rtest && *r != '\0')
 402                                 *fr++ = *r++;
 403                         *fr++ = 0;
 404                         if (*r++ == 0)
 405                                 break;
 406                 }
 407                 *fr = 0;
 408         }
 409         if (i > nfields)
 410                 FATAL("record `%.30s...' has too many fields; can't happen", r);
 411         /* clean out junk from previous record */
 412         cleanfld(i + 1, lastfld);
 413         lastfld = i;
 414         donefld = 1;
 415         for (j = 1; j <= lastfld; j++) {
 416                 p = fldtab[j];
 417                 if (is_number(p->sval)) {
 418                         p->fval = atof((const char *)p->sval);
 419                         p->tval |= NUM;
 420                 }
 421         }
 422         (void) setfval(nfloc, (Awkfloat)lastfld);
 423         if (dbg) {
 424                 for (j = 0; j <= lastfld; j++) {
 425                         p = fldtab[j];
 426                         (void) printf("field %d (%s): |%s|\n", j, p->nval,
 427                             p->sval);
 428                 }
 429         }
 430 }
 431 
 432 static void
 433 cleanfld(int n1, int n2)        /* clean out fields n1..n2 inclusive */
 434 {                               /* nvals remain intact */
 435         static uchar *nullstat = (uchar *)"";
 436         Cell *p;
 437         int i;
 438 
 439         for (i = n1; i <= n2; i++) {
 440                 p = fldtab[i];
 441                 if (freeable(p))
 442                         xfree(p->sval);
 443                 p->sval = nullstat;
 444                 p->tval = FLD | STR | DONTFREE;
 445         }
 446 }
 447 
 448 void
 449 newfld(int n)   /* add field n after end of existing lastfld */
 450 {
 451         if (n > nfields)
 452                 growfldtab(n);
 453         cleanfld(lastfld + 1, n);
 454         lastfld = n;
 455         (void) setfval(nfloc, (Awkfloat)n);
 456 }
 457 
 458 Cell *
 459 fieldadr(int n)         /* get nth field */
 460 {
 461         if (n < 0)
 462                 FATAL("trying to access out of range field %d", n);
 463         if (n > nfields)     /* fields after NF are empty */
 464                 growfldtab(n);  /* but does not increase NF */
 465         return (fldtab[n]);
 466 }
 467 
 468 void
 469 growfldtab(int n)       /* make new fields up to at least $n */
 470 {
 471         int nf = 2 * nfields;
 472         size_t s;
 473 
 474         if (n > nf)
 475                 nf = n;
 476         /* freebsd: how much do we need? */
 477         s = (nf + 1) * (sizeof (struct Cell *));
 478         if (s / sizeof (struct Cell *) - 1 == nf)       /* didn't overflow */
 479                 fldtab = (Cell **) realloc(fldtab, s);
 480         else    /* overflow sizeof int */
 481                 xfree(fldtab);  /* make it null */
 482         if (fldtab == NULL)
 483                 FATAL("out of space creating %d fields", nf);
 484         makefields(nfields + 1, nf);
 485         nfields = nf;
 486 }
 487 
 488 static int
 489 refldbld(uchar *rec, uchar *fs) /* build fields from reg expr in FS */
 490 {
 491         /* this relies on having fields[] the same length as $0 */
 492         /* the fields are all stored in this one array with \0's */
 493         uchar *fr;
 494         int i, tempstat;
 495         fa *pfa;
 496         size_t n;
 497 
 498         n = strlen((char *)rec);
 499         if (n > fields_size) {
 500                 xfree(fields);
 501                 if ((fields = (uchar *)malloc(n + 1)) == NULL)
 502                         FATAL("out of space for fields in refldbld %d", n);
 503                 fields_size = n;
 504         }
 505         fr = fields;
 506         *fr = '\0';
 507         if (*rec == '\0')
 508                 return (0);
 509         pfa = makedfa(fs, 1);
 510         dprintf(("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs));
 511         tempstat = pfa->initstat;
 512         for (i = 1; ; i++) {
 513                 if (i > nfields)
 514                         growfldtab(i);
 515                 if (freeable(fldtab[i]))
 516                         xfree(fldtab[i]->sval);
 517                 fldtab[i]->tval = FLD | STR | DONTFREE;
 518                 fldtab[i]->sval = fr;
 519                 dprintf(("refldbld: i=%d\n", i));
 520                 if (nematch(pfa, rec)) {
 521                         pfa->initstat = 2;   /* horrible coupling to b.c */
 522                         dprintf(("match %s (%d chars)\n", patbeg, patlen));
 523                         (void) strncpy((char *)fr, (char *)rec, patbeg-rec);
 524                         fr += patbeg - rec + 1;
 525                         *(fr-1) = '\0';
 526                         rec = patbeg + patlen;
 527                 } else {
 528                         dprintf(("no match %s\n", rec));
 529                         (void) strcpy((char *)fr, (char *)rec);
 530                         pfa->initstat = tempstat;
 531                         break;
 532                 }
 533         }
 534         return (i);
 535 }
 536 
 537 void
 538 recbld(void)            /* create $0 from $1..$NF if necessary */
 539 {
 540         int i;
 541         uchar *r, *p;
 542 
 543         if (donerec == 1)
 544                 return;
 545         r = record;
 546         for (i = 1; i <= *NF; i++) {
 547                 p = getsval(fldtab[i]);
 548                 if (!adjbuf(&record, &record_size, 1 + strlen((char *)p) + r -
 549                     record, record_size, &r, "recbld 1"))
 550                         FATAL("created $0 `%.30s...' too long", record);
 551                 while ((*r = *p++) != 0)
 552                         r++;
 553                 if (i < *NF) {
 554                         if (!adjbuf(&record, &record_size, 2 +
 555                             strlen((char *)*OFS) + r - record, record_size,
 556                             &r, "recbld 2"))
 557                                 FATAL("created $0 `%.30s...' too long", record);
 558                         for (p = *OFS; (*r = *p++) != 0; )
 559                                 r++;
 560                 }
 561         }
 562         if (!adjbuf(&record, &record_size, 2 + r - record, record_size, &r,
 563             "recbld 3"))
 564                 FATAL("built giant record `%.30s...'", record);
 565         *r = '\0';
 566         dprintf(("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS,
 567             (void *)fldtab[0]));
 568 
 569         if (freeable(fldtab[0]))
 570                 xfree(fldtab[0]->sval);
 571         fldtab[0]->tval = REC | STR | DONTFREE;
 572         fldtab[0]->sval = record;
 573 
 574         dprintf(("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS,
 575             (void *)fldtab[0]));
 576         dprintf(("recbld = |%s|\n", record));
 577         donerec = 1;
 578 }
 579 
 580 int     errorflag       = 0;
 581 
 582 void
 583 yyerror(char *s)
 584 {
 585         SYNTAX("%s", s);
 586 }
 587 
 588 void
 589 SYNTAX(const char *fmt, ...)
 590 {
 591         extern uchar *cmdname, *curfname;
 592         static int been_here = 0;
 593         va_list varg;
 594 
 595         if (been_here++ > 2)
 596                 return;
 597         (void) fprintf(stderr, "%s: ", cmdname);
 598         va_start(varg, fmt);
 599         (void) vfprintf(stderr, fmt, varg);
 600         va_end(varg);
 601         (void) fprintf(stderr, gettext(" at source line %lld"), lineno);
 602         if (curfname != NULL)
 603                 (void) fprintf(stderr, gettext(" in function %s"), curfname);
 604         if (compile_time == 1 && cursource() != NULL)
 605                 (void) fprintf(stderr, gettext(" source file %s"), cursource());
 606         (void) fprintf(stderr, "\n");
 607         errorflag = 2;
 608         eprint();
 609 }
 610 
 611 void
 612 fpecatch(int n)
 613 {
 614         FATAL("floating point exception %d", n);
 615 }
 616 
 617 extern int bracecnt, brackcnt, parencnt;
 618 
 619 void
 620 bracecheck(void)
 621 {
 622         int c;
 623         static int beenhere = 0;
 624 
 625         if (beenhere++)
 626                 return;
 627         while ((c = input()) != EOF && c != '\0')
 628                 bclass(c);
 629         bcheck2(bracecnt, '{', '}');
 630         bcheck2(brackcnt, '[', ']');
 631         bcheck2(parencnt, '(', ')');
 632 }
 633 
 634 /*ARGSUSED*/
 635 static void
 636 bcheck2(int n, int c1, int c2)
 637 {
 638         if (n == 1)
 639                 (void) fprintf(stderr, gettext("\tmissing %c\n"), c2);
 640         else if (n > 1)
 641                 (void) fprintf(stderr, gettext("\t%d missing %c's\n"), n, c2);
 642         else if (n == -1)
 643                 (void) fprintf(stderr, gettext("\textra %c\n"), c2);
 644         else if (n < -1)
 645                 (void) fprintf(stderr, gettext("\t%d extra %c's\n"), -n, c2);
 646 }
 647 
 648 void
 649 FATAL(const char *fmt, ...)
 650 {
 651         extern uchar *cmdname;
 652         va_list varg;
 653 
 654         (void) fflush(stdout);
 655         (void) fprintf(stderr, "%s: ", cmdname);
 656         va_start(varg, fmt);
 657         (void) vfprintf(stderr, fmt, varg);
 658         va_end(varg);
 659         error();
 660         if (dbg > 1) /* core dump if serious debugging on */
 661                 abort();
 662         exit(2);
 663 }
 664 
 665 void
 666 WARNING(const char *fmt, ...)
 667 {
 668         extern uchar *cmdname;
 669         va_list varg;
 670 
 671         (void) fflush(stdout);
 672         (void) fprintf(stderr, "%s: ", cmdname);
 673         va_start(varg, fmt);
 674         (void) vfprintf(stderr, fmt, varg);
 675         va_end(varg);
 676         error();
 677 }
 678 
 679 void
 680 error(void)
 681 {
 682         extern Node *curnode;
 683 
 684         (void) fprintf(stderr, "\n");
 685         if (compile_time != 2 && NR && *NR > 0) {
 686                 (void) fprintf(stderr,
 687                     gettext(" input record number %g"), (int) (*FNR));
 688                 if (strcmp((char *)*FILENAME, "-") != 0)
 689                         (void) fprintf(stderr, gettext(", file %s"), *FILENAME);
 690                 (void) fprintf(stderr, "\n");
 691         }
 692         if (compile_time != 2 && curnode)
 693                 (void) fprintf(stderr, gettext(" source line number %d"),
 694                     curnode->lineno);
 695         else if (compile_time != 2 && lineno) {
 696                 (void) fprintf(stderr,
 697                     gettext(" source line number %d"), lineno);
 698         }
 699         if (compile_time == 1 && cursource() != NULL)
 700                 (void) fprintf(stderr,
 701                     gettext(" source file %s"), cursource());
 702         (void) fprintf(stderr, "\n");
 703         eprint();
 704 }
 705 
 706 static void
 707 eprint(void)    /* try to print context around error */
 708 {
 709         uchar *p, *q;
 710         int c;
 711         static int been_here = 0;
 712         extern uchar ebuf[], *ep;
 713 
 714         if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
 715                 return;
 716         p = ep - 1;
 717         if (p > ebuf && *p == '\n')
 718                 p--;
 719         for (; p > ebuf && *p != '\n' && *p != '\0'; p--)
 720                 ;
 721         while (*p == '\n')
 722                 p++;
 723         (void) fprintf(stderr, gettext(" context is\n\t"));
 724         for (q = ep-1; q >= p && *q != ' ' && *q != '\t' && *q != '\n'; q--)
 725                 ;
 726         for (; p < q; p++)
 727                 if (*p)
 728                         (void) putc(*p, stderr);
 729         (void) fprintf(stderr, " >>> ");
 730         for (; p < ep; p++)
 731                 if (*p)
 732                         (void) putc(*p, stderr);
 733         (void) fprintf(stderr, " <<< ");
 734         if (*ep)
 735                 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
 736                         (void) putc(c, stderr);
 737                         bclass(c);
 738                 }
 739         (void) putc('\n', stderr);
 740         ep = ebuf;
 741 }
 742 
 743 static void
 744 bclass(int c)
 745 {
 746         switch (c) {
 747         case '{': bracecnt++; break;
 748         case '}': bracecnt--; break;
 749         case '[': brackcnt++; break;
 750         case ']': brackcnt--; break;
 751         case '(': parencnt++; break;
 752         case ')': parencnt--; break;
 753         }
 754 }
 755 
 756 double
 757 errcheck(double x, const char *s)
 758 {
 759         if (errno == EDOM) {
 760                 errno = 0;
 761                 WARNING("%s argument out of domain", s);
 762                 x = 1;
 763         } else if (errno == ERANGE) {
 764                 errno = 0;
 765                 WARNING("%s result out of range", s);
 766                 x = 1;
 767         }
 768         return (x);
 769 }
 770 
 771 int
 772 isclvar(const uchar *s) /* is s of form var=something? */
 773 {
 774         const uchar *os = s;
 775 
 776         if (!isalpha(*s) && *s != '_')
 777                 return (0);
 778         for (; *s; s++) {
 779                 if (!(isalnum(*s) || *s == '_'))
 780                         break;
 781         }
 782 
 783         return (*s == '=' && s > os && *(s+1) != '=');
 784 }
 785 
 786 /* strtod is supposed to be a proper test of what's a valid number */
 787 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
 788 /* wrong: violates 4.10.1.4 of ansi C standard */
 789 
 790 int
 791 is_number(const uchar *s)
 792 {
 793         double r;
 794         char *ep;
 795         errno = 0;
 796         r = strtod((const char *)s, &ep);
 797         if (ep == (char *)s || r == HUGE_VAL || errno == ERANGE)
 798                 return (0);
 799         while (*ep == ' ' || *ep == '\t' || *ep == '\n')
 800                 ep++;
 801         if (*ep == '\0')
 802                 return (1);
 803         else
 804                 return (0);
 805 }