port of netbsd's tr
[minix.git] / commands / awk / lib.c
blob017b37670d0798001a8a2c774655040fec9fa045
1 /****************************************************************
2 Copyright (C) Lucent Technologies 1997
3 All Rights Reserved
5 Permission to use, copy, modify, and distribute this software and
6 its documentation for any purpose and without fee is hereby
7 granted, provided that the above copyright notice appear in all
8 copies and that both that the copyright notice and this
9 permission notice and warranty disclaimer appear in supporting
10 documentation, and that the name Lucent Technologies or any of
11 its entities not be used in advertising or publicity pertaining
12 to distribution of the software without specific, written prior
13 permission.
15 LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
17 IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
18 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
20 IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
21 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
22 THIS SOFTWARE.
23 ****************************************************************/
25 #define DEBUG
26 #include <stdio.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include "awk.h"
33 #include "ytab.h"
35 FILE *infile = NULL;
36 char *file = "";
37 char *record;
38 int recsize = RECSIZE;
39 char *fields;
40 int fieldssize = RECSIZE;
42 Cell **fldtab; /* pointers to Cells */
43 char inputFS[100] = " ";
45 #define MAXFLD 2
46 int nfields = MAXFLD; /* last allocated slot for $i */
48 int donefld; /* 1 = implies rec broken into fields */
49 int donerec; /* 1 = record is valid (no flds have changed) */
51 int lastfld = 0; /* last used field */
52 int argno = 1; /* current input argument number */
53 extern Awkfloat *ARGC;
55 static Cell dollar0 = { OCELL, CFLD, NULL, "", 0.0, REC|STR|DONTFREE };
56 static Cell dollar1 = { OCELL, CFLD, NULL, "", 0.0, FLD|STR|DONTFREE };
58 void recinit(unsigned int n)
60 if ( (record = (char *) malloc(n)) == NULL
61 || (fields = (char *) malloc(n+1)) == NULL
62 || (fldtab = (Cell **) malloc((nfields+1) * sizeof(Cell *))) == NULL
63 || (fldtab[0] = (Cell *) malloc(sizeof(Cell))) == NULL )
64 FATAL("out of space for $0 and fields");
65 *fldtab[0] = dollar0;
66 fldtab[0]->sval = record;
67 fldtab[0]->nval = tostring("0");
68 makefields(1, nfields);
71 void makefields(int n1, int n2) /* create $n1..$n2 inclusive */
73 char temp[50];
74 int i;
76 for (i = n1; i <= n2; i++) {
77 fldtab[i] = (Cell *) malloc(sizeof (struct Cell));
78 if (fldtab[i] == NULL)
79 FATAL("out of space in makefields %d", i);
80 *fldtab[i] = dollar1;
81 sprintf(temp, "%d", i);
82 fldtab[i]->nval = tostring(temp);
86 void initgetrec(void)
88 int i;
89 char *p;
91 for (i = 1; i < *ARGC; i++) {
92 if (!isclvar(p = getargv(i))) { /* find 1st real filename */
93 setsval(lookup("FILENAME", symtab), getargv(i));
94 return;
96 setclvar(p); /* a commandline assignment before filename */
97 argno++;
99 infile = stdin; /* no filenames, so use stdin */
102 static int firsttime = 1;
104 int getrec(char **pbuf, int *pbufsize, int isrecord) /* get next input record */
105 { /* note: cares whether buf == record */
106 int c;
107 char *buf = *pbuf;
108 uschar saveb0;
109 int bufsize = *pbufsize, savebufsize = bufsize;
111 if (firsttime) {
112 firsttime = 0;
113 initgetrec();
115 dprintf( ("RS=<%s>, FS=<%s>, ARGC=%g, FILENAME=%s\n",
116 *RS, *FS, *ARGC, *FILENAME) );
117 if (isrecord) {
118 donefld = 0;
119 donerec = 1;
121 saveb0 = buf[0];
122 buf[0] = 0;
123 while (argno < *ARGC || infile == stdin) {
124 dprintf( ("argno=%d, file=|%s|\n", argno, file) );
125 if (infile == NULL) { /* have to open a new file */
126 file = getargv(argno);
127 if (*file == '\0') { /* it's been zapped */
128 argno++;
129 continue;
131 if (isclvar(file)) { /* a var=value arg */
132 setclvar(file);
133 argno++;
134 continue;
136 *FILENAME = file;
137 dprintf( ("opening file %s\n", file) );
138 if (*file == '-' && *(file+1) == '\0')
139 infile = stdin;
140 else if ((infile = fopen(file, "r")) == NULL)
141 FATAL("can't open file %s", file);
142 setfval(fnrloc, 0.0);
144 c = readrec(&buf, &bufsize, infile);
145 if (c != 0 || buf[0] != '\0') { /* normal record */
146 if (isrecord) {
147 if (freeable(fldtab[0]))
148 xfree(fldtab[0]->sval);
149 fldtab[0]->sval = buf; /* buf == record */
150 fldtab[0]->tval = REC | STR | DONTFREE;
151 if (is_number(fldtab[0]->sval)) {
152 fldtab[0]->fval = atof(fldtab[0]->sval);
153 fldtab[0]->tval |= NUM;
156 setfval(nrloc, nrloc->fval+1);
157 setfval(fnrloc, fnrloc->fval+1);
158 *pbuf = buf;
159 *pbufsize = bufsize;
160 return 1;
162 /* EOF arrived on this file; set up next */
163 if (infile != stdin)
164 fclose(infile);
165 infile = NULL;
166 argno++;
168 buf[0] = saveb0;
169 *pbuf = buf;
170 *pbufsize = savebufsize;
171 return 0; /* true end of file */
174 void nextfile(void)
176 if (infile != NULL && infile != stdin)
177 fclose(infile);
178 infile = NULL;
179 argno++;
182 int readrec(char **pbuf, int *pbufsize, FILE *inf) /* read one record into buf */
184 int sep, c;
185 char *rr, *buf = *pbuf;
186 int bufsize = *pbufsize;
188 if (strlen(*FS) >= sizeof(inputFS))
189 FATAL("field separator %.10s... is too long", *FS);
190 strcpy(inputFS, *FS); /* for subsequent field splitting */
191 if ((sep = **RS) == 0) {
192 sep = '\n';
193 while ((c=getc(inf)) == '\n' && c != EOF) /* skip leading \n's */
195 if (c != EOF)
196 ungetc(c, inf);
198 for (rr = buf; ; ) {
199 for (; (c=getc(inf)) != sep && c != EOF; ) {
200 if (rr-buf+1 > bufsize)
201 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 1"))
202 FATAL("input record `%.30s...' too long", buf);
203 *rr++ = c;
205 if (**RS == sep || c == EOF)
206 break;
207 if ((c = getc(inf)) == '\n' || c == EOF) /* 2 in a row */
208 break;
209 if (!adjbuf(&buf, &bufsize, 2+rr-buf, recsize, &rr, "readrec 2"))
210 FATAL("input record `%.30s...' too long", buf);
211 *rr++ = '\n';
212 *rr++ = c;
214 if (!adjbuf(&buf, &bufsize, 1+rr-buf, recsize, &rr, "readrec 3"))
215 FATAL("input record `%.30s...' too long", buf);
216 *rr = 0;
217 dprintf( ("readrec saw <%s>, returns %d\n", buf, c == EOF && rr == buf ? 0 : 1) );
218 *pbuf = buf;
219 *pbufsize = bufsize;
220 return c == EOF && rr == buf ? 0 : 1;
223 char *getargv(int n) /* get ARGV[n] */
225 Cell *x;
226 char *s, temp[50];
227 extern Array *ARGVtab;
229 sprintf(temp, "%d", n);
230 x = setsymtab(temp, "", 0.0, STR, ARGVtab);
231 s = getsval(x);
232 dprintf( ("getargv(%d) returns |%s|\n", n, s) );
233 return s;
236 void setclvar(char *s) /* set var=value from s */
238 char *p;
239 Cell *q;
241 for (p=s; *p != '='; p++)
243 *p++ = 0;
244 p = qstring(p, '\0');
245 q = setsymtab(s, p, 0.0, STR, symtab);
246 setsval(q, p);
247 if (is_number(q->sval)) {
248 q->fval = atof(q->sval);
249 q->tval |= NUM;
251 dprintf( ("command line set %s to |%s|\n", s, p) );
255 void fldbld(void) /* create fields from current record */
257 /* this relies on having fields[] the same length as $0 */
258 /* the fields are all stored in this one array with \0's */
259 char *r, *fr, sep;
260 Cell *p;
261 int i, j, n;
263 if (donefld)
264 return;
265 if (!isstr(fldtab[0]))
266 getsval(fldtab[0]);
267 r = fldtab[0]->sval;
268 n = strlen(r);
269 if (n > fieldssize) {
270 xfree(fields);
271 if ((fields = (char *) malloc(n+1)) == NULL)
272 FATAL("out of space for fields in fldbld %d", n);
273 fieldssize = n;
275 fr = fields;
276 i = 0; /* number of fields accumulated here */
277 strcpy(inputFS, *FS);
278 if (strlen(inputFS) > 1) { /* it's a regular expression */
279 i = refldbld(r, inputFS);
280 } else if ((sep = *inputFS) == ' ') { /* default whitespace */
281 for (i = 0; ; ) {
282 while (*r == ' ' || *r == '\t' || *r == '\n')
283 r++;
284 if (*r == 0)
285 break;
286 i++;
287 if (i > nfields)
288 growfldtab(i);
289 if (freeable(fldtab[i]))
290 xfree(fldtab[i]->sval);
291 fldtab[i]->sval = fr;
292 fldtab[i]->tval = FLD | STR | DONTFREE;
294 *fr++ = *r++;
295 while (*r != ' ' && *r != '\t' && *r != '\n' && *r != '\0');
296 *fr++ = 0;
298 *fr = 0;
299 } else if ((sep = *inputFS) == 0) { /* new: FS="" => 1 char/field */
300 for (i = 0; *r != 0; r++) {
301 char buf[2];
302 i++;
303 if (i > nfields)
304 growfldtab(i);
305 if (freeable(fldtab[i]))
306 xfree(fldtab[i]->sval);
307 buf[0] = *r;
308 buf[1] = 0;
309 fldtab[i]->sval = tostring(buf);
310 fldtab[i]->tval = FLD | STR;
312 *fr = 0;
313 } else if (*r != 0) { /* if 0, it's a null field */
314 /* subtlecase : if length(FS) == 1 && length(RS > 0)
315 * \n is NOT a field separator (cf awk book 61,84).
316 * this variable is tested in the inner while loop.
318 int rtest = '\n'; /* normal case */
319 if (strlen(*RS) > 0)
320 rtest = '\0';
321 for (;;) {
322 i++;
323 if (i > nfields)
324 growfldtab(i);
325 if (freeable(fldtab[i]))
326 xfree(fldtab[i]->sval);
327 fldtab[i]->sval = fr;
328 fldtab[i]->tval = FLD | STR | DONTFREE;
329 while (*r != sep && *r != rtest && *r != '\0') /* \n is always a separator */
330 *fr++ = *r++;
331 *fr++ = 0;
332 if (*r++ == 0)
333 break;
335 *fr = 0;
337 if (i > nfields)
338 FATAL("record `%.30s...' has too many fields; can't happen", r);
339 cleanfld(i+1, lastfld); /* clean out junk from previous record */
340 lastfld = i;
341 donefld = 1;
342 for (j = 1; j <= lastfld; j++) {
343 p = fldtab[j];
344 if(is_number(p->sval)) {
345 p->fval = atof(p->sval);
346 p->tval |= NUM;
349 setfval(nfloc, (Awkfloat) lastfld);
350 if (dbg) {
351 for (j = 0; j <= lastfld; j++) {
352 p = fldtab[j];
353 printf("field %d (%s): |%s|\n", j, p->nval, p->sval);
358 void cleanfld(int n1, int n2) /* clean out fields n1 .. n2 inclusive */
359 { /* nvals remain intact */
360 Cell *p;
361 int i;
363 for (i = n1; i <= n2; i++) {
364 p = fldtab[i];
365 if (freeable(p))
366 xfree(p->sval);
367 p->sval = "";
368 p->tval = FLD | STR | DONTFREE;
372 void newfld(int n) /* add field n after end of existing lastfld */
374 if (n > nfields)
375 growfldtab(n);
376 cleanfld(lastfld+1, n);
377 lastfld = n;
378 setfval(nfloc, (Awkfloat) n);
381 Cell *fieldadr(int n) /* get nth field */
383 if (n < 0)
384 FATAL("trying to access out of range field %d", n);
385 if (n > nfields) /* fields after NF are empty */
386 growfldtab(n); /* but does not increase NF */
387 return(fldtab[n]);
390 void growfldtab(int n) /* make new fields up to at least $n */
392 int nf = 2 * nfields;
393 size_t s;
395 if (n > nf)
396 nf = n;
397 s = (nf+1) * (sizeof (struct Cell *)); /* freebsd: how much do we need? */
398 if (s / sizeof(struct Cell *) - 1 == nf) /* didn't overflow */
399 fldtab = (Cell **) realloc(fldtab, s);
400 else /* overflow sizeof int */
401 xfree(fldtab); /* make it null */
402 if (fldtab == NULL)
403 FATAL("out of space creating %d fields", nf);
404 makefields(nfields+1, nf);
405 nfields = nf;
408 int refldbld(const char *rec, const char *fs) /* build fields from reg expr in FS */
410 /* this relies on having fields[] the same length as $0 */
411 /* the fields are all stored in this one array with \0's */
412 char *fr;
413 int i, tempstat, n;
414 fa *pfa;
416 n = strlen(rec);
417 if (n > fieldssize) {
418 xfree(fields);
419 if ((fields = (char *) malloc(n+1)) == NULL)
420 FATAL("out of space for fields in refldbld %d", n);
421 fieldssize = n;
423 fr = fields;
424 *fr = '\0';
425 if (*rec == '\0')
426 return 0;
427 pfa = makedfa(fs, 1);
428 dprintf( ("into refldbld, rec = <%s>, pat = <%s>\n", rec, fs) );
429 tempstat = pfa->initstat;
430 for (i = 1; ; i++) {
431 if (i > nfields)
432 growfldtab(i);
433 if (freeable(fldtab[i]))
434 xfree(fldtab[i]->sval);
435 fldtab[i]->tval = FLD | STR | DONTFREE;
436 fldtab[i]->sval = fr;
437 dprintf( ("refldbld: i=%d\n", i) );
438 if (nematch(pfa, rec)) {
439 pfa->initstat = 2; /* horrible coupling to b.c */
440 dprintf( ("match %s (%d chars)\n", patbeg, patlen) );
441 strncpy(fr, rec, patbeg-rec);
442 fr += patbeg - rec + 1;
443 *(fr-1) = '\0';
444 rec = patbeg + patlen;
445 } else {
446 dprintf( ("no match %s\n", rec) );
447 strcpy(fr, rec);
448 pfa->initstat = tempstat;
449 break;
452 return i;
455 void recbld(void) /* create $0 from $1..$NF if necessary */
457 int i;
458 char *r, *p;
460 if (donerec == 1)
461 return;
462 r = record;
463 for (i = 1; i <= *NF; i++) {
464 p = getsval(fldtab[i]);
465 if (!adjbuf(&record, &recsize, 1+strlen(p)+r-record, recsize, &r, "recbld 1"))
466 FATAL("created $0 `%.30s...' too long", record);
467 while ((*r = *p++) != 0)
468 r++;
469 if (i < *NF) {
470 if (!adjbuf(&record, &recsize, 2+strlen(*OFS)+r-record, recsize, &r, "recbld 2"))
471 FATAL("created $0 `%.30s...' too long", record);
472 for (p = *OFS; (*r = *p++) != 0; )
473 r++;
476 if (!adjbuf(&record, &recsize, 2+r-record, recsize, &r, "recbld 3"))
477 FATAL("built giant record `%.30s...'", record);
478 *r = '\0';
479 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
481 if (freeable(fldtab[0]))
482 xfree(fldtab[0]->sval);
483 fldtab[0]->tval = REC | STR | DONTFREE;
484 fldtab[0]->sval = record;
486 dprintf( ("in recbld inputFS=%s, fldtab[0]=%p\n", inputFS, fldtab[0]) );
487 dprintf( ("recbld = |%s|\n", record) );
488 donerec = 1;
491 int errorflag = 0;
493 void yyerror(const char *s)
495 SYNTAX("%s", s);
498 void SYNTAX(const char *fmt, ...)
500 extern char *cmdname, *curfname;
501 static int been_here = 0;
502 va_list varg;
504 if (been_here++ > 2)
505 return;
506 fprintf(stderr, "%s: ", cmdname);
507 va_start(varg, fmt);
508 vfprintf(stderr, fmt, varg);
509 va_end(varg);
510 fprintf(stderr, " at source line %d", lineno);
511 if (curfname != NULL)
512 fprintf(stderr, " in function %s", curfname);
513 if (compile_time == 1 && cursource() != NULL)
514 fprintf(stderr, " source file %s", cursource());
515 fprintf(stderr, "\n");
516 errorflag = 2;
517 eprint();
520 void fpecatch(int n)
522 FATAL("floating point exception %d", n);
525 extern int bracecnt, brackcnt, parencnt;
527 void bracecheck(void)
529 int c;
530 static int beenhere = 0;
532 if (beenhere++)
533 return;
534 while ((c = input()) != EOF && c != '\0')
535 bclass(c);
536 bcheck2(bracecnt, '{', '}');
537 bcheck2(brackcnt, '[', ']');
538 bcheck2(parencnt, '(', ')');
541 void bcheck2(int n, int c1, int c2)
543 if (n == 1)
544 fprintf(stderr, "\tmissing %c\n", c2);
545 else if (n > 1)
546 fprintf(stderr, "\t%d missing %c's\n", n, c2);
547 else if (n == -1)
548 fprintf(stderr, "\textra %c\n", c2);
549 else if (n < -1)
550 fprintf(stderr, "\t%d extra %c's\n", -n, c2);
553 void FATAL(const char *fmt, ...)
555 extern char *cmdname;
556 va_list varg;
558 fflush(stdout);
559 fprintf(stderr, "%s: ", cmdname);
560 va_start(varg, fmt);
561 vfprintf(stderr, fmt, varg);
562 va_end(varg);
563 error();
564 if (dbg > 1) /* core dump if serious debugging on */
565 abort();
566 exit(2);
569 void WARNING(const char *fmt, ...)
571 extern char *cmdname;
572 va_list varg;
574 fflush(stdout);
575 fprintf(stderr, "%s: ", cmdname);
576 va_start(varg, fmt);
577 vfprintf(stderr, fmt, varg);
578 va_end(varg);
579 error();
582 void error()
584 extern Node *curnode;
586 fprintf(stderr, "\n");
587 if (compile_time != 2 && NR && *NR > 0) {
588 fprintf(stderr, " input record number %d", (int) (*FNR));
589 if (strcmp(*FILENAME, "-") != 0)
590 fprintf(stderr, ", file %s", *FILENAME);
591 fprintf(stderr, "\n");
593 if (compile_time != 2 && curnode)
594 fprintf(stderr, " source line number %d", curnode->lineno);
595 else if (compile_time != 2 && lineno)
596 fprintf(stderr, " source line number %d", lineno);
597 if (compile_time == 1 && cursource() != NULL)
598 fprintf(stderr, " source file %s", cursource());
599 fprintf(stderr, "\n");
600 eprint();
603 void eprint(void) /* try to print context around error */
605 char *p, *q;
606 int c;
607 static int been_here = 0;
608 extern char ebuf[], *ep;
610 if (compile_time == 2 || compile_time == 0 || been_here++ > 0)
611 return;
612 p = ep - 1;
613 if (p > ebuf && *p == '\n')
614 p--;
615 for ( ; p > ebuf && *p != '\n' && *p != '\0'; p--)
617 while (*p == '\n')
618 p++;
619 fprintf(stderr, " context is\n\t");
620 for (q=ep-1; q>=p && *q!=' ' && *q!='\t' && *q!='\n'; q--)
622 for ( ; p < q; p++)
623 if (*p)
624 putc(*p, stderr);
625 fprintf(stderr, " >>> ");
626 for ( ; p < ep; p++)
627 if (*p)
628 putc(*p, stderr);
629 fprintf(stderr, " <<< ");
630 if (*ep)
631 while ((c = input()) != '\n' && c != '\0' && c != EOF) {
632 putc(c, stderr);
633 bclass(c);
635 putc('\n', stderr);
636 ep = ebuf;
639 void bclass(int c)
641 switch (c) {
642 case '{': bracecnt++; break;
643 case '}': bracecnt--; break;
644 case '[': brackcnt++; break;
645 case ']': brackcnt--; break;
646 case '(': parencnt++; break;
647 case ')': parencnt--; break;
651 double errcheck(double x, const char *s)
654 if (errno == EDOM) {
655 errno = 0;
656 WARNING("%s argument out of domain", s);
657 x = 1;
658 } else if (errno == ERANGE) {
659 errno = 0;
660 WARNING("%s result out of range", s);
661 x = 1;
663 return x;
666 int isclvar(const char *s) /* is s of form var=something ? */
668 const char *os = s;
670 if (!isalpha((uschar) *s) && *s != '_')
671 return 0;
672 for ( ; *s; s++)
673 if (!(isalnum((uschar) *s) || *s == '_'))
674 break;
675 return *s == '=' && s > os && *(s+1) != '=';
678 /* strtod is supposed to be a proper test of what's a valid number */
679 /* appears to be broken in gcc on linux: thinks 0x123 is a valid FP number */
680 /* wrong: violates 4.10.1.4 of ansi C standard */
682 #include <math.h>
683 int is_number(const char *s)
685 double r;
686 char *ep;
687 errno = 0;
688 r = strtod(s, &ep);
689 if (ep == s || r == HUGE_VAL || errno == ERANGE)
690 return 0;
691 while (*ep == ' ' || *ep == '\t' || *ep == '\n')
692 ep++;
693 if (*ep == '\0')
694 return 1;
695 else
696 return 0;