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