Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / awk / io.c
1 /*
2  * io.c --- routines for dealing with input and output and records
3  */
4
5 /* 
6  * Copyright (C) 1976, 1988, 1989, 1991-2000 the Free Software Foundation, Inc.
7  * 
8  * This file is part of GAWK, the GNU implementation of the
9  * AWK Programming Language.
10  * 
11  * GAWK is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * GAWK is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
24  *
25  * $FreeBSD: src/contrib/awk/io.c,v 1.4.2.1 2001/01/23 22:08:31 asmodai Exp $
26  */
27
28 #include "awk.h"
29 #undef HAVE_MMAP        /* for now, probably forever */
30
31 #ifdef HAVE_SYS_PARAM_H
32 #undef RE_DUP_MAX       /* avoid spurious conflict w/regex.h */
33 #include <sys/param.h>
34 #endif /* HAVE_SYS_PARAM_H */
35
36 #ifdef HAVE_SYS_WAIT_H
37 #include <sys/wait.h>
38 #endif /* HAVE_SYS_WAIT_H */
39
40 #ifdef HAVE_MMAP
41 #include <sys/mman.h>
42 #ifndef MAP_FAILED
43 #define MAP_FAILED      ((caddr_t) -1)
44 #endif /* ! defined (MAP_FAILED) */
45 #endif /* HAVE_MMAP */
46
47 #ifndef O_RDONLY
48 #include <fcntl.h>
49 #endif
50 #ifndef O_ACCMODE
51 #define O_ACCMODE       (O_RDONLY|O_WRONLY|O_RDWR)
52 #endif
53
54 #if ! defined(S_ISREG) && defined(S_IFREG)
55 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
56 #endif
57
58 #if ! defined(S_ISDIR) && defined(S_IFDIR)
59 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
60 #endif
61
62 #ifndef ENFILE
63 #define ENFILE EMFILE
64 #endif
65
66 #ifdef atarist
67 #include <stddef.h>
68 #endif
69
70 #if defined(MSDOS) || defined(OS2) || defined(WIN32)
71 #define PIPES_SIMULATED
72 #endif
73
74 static IOBUF *nextfile P((int skipping));
75 static int inrec P((IOBUF *iop));
76 static int iop_close P((IOBUF *iop));
77 struct redirect *redirect P((NODE *tree, int *errflg));
78 static void close_one P((void));
79 static int close_redir P((struct redirect *rp, int exitwarn));
80 #ifndef PIPES_SIMULATED
81 static int wait_any P((int interesting));
82 #endif
83 static IOBUF *gawk_popen P((char *cmd, struct redirect *rp));
84 static IOBUF *iop_open P((const char *file, const char *how, IOBUF *buf));
85 static IOBUF *iop_alloc P((int fd, const char *name, IOBUF *buf));
86 static int gawk_pclose P((struct redirect *rp));
87 static int do_pathopen P((const char *file));
88 static int get_a_record P((char **out, IOBUF *iop, int rs, Regexp *RSre, int *errcode));
89 #ifdef HAVE_MMAP
90 static int mmap_get_record P((char **out, IOBUF *iop, int rs, Regexp *RSre, int *errcode));
91 #endif /* HAVE_MMAP */
92 static int str2mode P((const char *mode));
93 static void spec_setup P((IOBUF *iop, int len, int allocate));
94 static int specfdopen P((IOBUF *iop, const char *name, const char *mode));
95 static int pidopen P((IOBUF *iop, const char *name, const char *mode));
96 static int useropen P((IOBUF *iop, const char *name, const char *mode));
97
98 #if defined (HAVE_POPEN_H)
99 #include "popen.h"
100 #endif
101
102 static struct redirect *red_head = NULL;
103 static NODE *RS;
104 static Regexp *RS_regexp;
105
106 int RS_is_null;
107
108 extern int output_is_tty;
109 extern NODE *ARGC_node;
110 extern NODE *ARGV_node;
111 extern NODE *ARGIND_node;
112 extern NODE *ERRNO_node;
113 extern NODE **fields_arr;
114
115 static jmp_buf filebuf;         /* for do_nextfile() */
116
117 #ifdef VMS
118 /* File pointers have an extra level of indirection, and there are cases where
119    `stdin' can be null.  That can crash gawk if fileno() is used as-is.  */
120 static int vmsrtl_fileno P((FILE *));
121 static int vmsrtl_fileno(fp) FILE *fp; { return fileno(fp); }
122 #undef fileno
123 #define fileno(FP) (((FP) && *(FP)) ? vmsrtl_fileno(FP) : -1)
124 #endif  /* VMS */
125
126 /* do_nextfile --- implement gawk "nextfile" extension */
127
128 void
129 do_nextfile()
130 {
131         (void) nextfile(TRUE);
132         longjmp(filebuf, 1);
133 }
134
135 /* nextfile --- move to the next input data file */
136
137 static IOBUF *
138 nextfile(skipping)
139 int skipping;
140 {
141         static long i = 1;
142         static int files = 0;
143         NODE *arg;
144         static IOBUF *curfile = NULL;
145         static IOBUF mybuf;
146         const char *fname;
147
148         if (skipping) {
149                 if (curfile != NULL)
150                         iop_close(curfile);
151                 curfile = NULL;
152                 return NULL;
153         }
154         if (curfile != NULL) {
155                 if (curfile->cnt == EOF) {
156                         (void) iop_close(curfile);
157                         curfile = NULL;
158                 } else
159                         return curfile;
160         }
161         for (; i < (long) (ARGC_node->lnode->numbr); i++) {
162                 arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i));
163                 if (arg->stlen == 0)
164                         continue;
165                 arg->stptr[arg->stlen] = '\0';
166                 if (! do_traditional) {
167                         unref(ARGIND_node->var_value);
168                         ARGIND_node->var_value = make_number((AWKNUM) i);
169                 }
170                 if (! arg_assign(arg->stptr)) {
171                         files++;
172                         fname = arg->stptr;
173                         curfile = iop_open(fname, "r", &mybuf);
174                         if (curfile == NULL)
175                                 goto give_up;
176                         curfile->flag |= IOP_NOFREE_OBJ;
177                         /* This is a kludge.  */
178                         unref(FILENAME_node->var_value);
179                         FILENAME_node->var_value = dupnode(arg);
180                         FNR = 0;
181                         i++;
182                         break;
183                 }
184         }
185         if (files == 0) {
186                 files++;
187                 /* no args. -- use stdin */
188                 /* FNR is init'ed to 0 */
189                 FILENAME_node->var_value = make_string("-", 1);
190                 fname = "-";
191                 curfile = iop_open(fname, "r", &mybuf);
192                 if (curfile == NULL)
193                         goto give_up;
194                 curfile->flag |= IOP_NOFREE_OBJ;
195         }
196         return curfile;
197
198  give_up:
199         fatal("cannot open file `%s' for reading (%s)",
200                 fname, strerror(errno));
201         /* NOTREACHED */
202         return 0;
203 }
204
205 /* set_FNR --- update internal FNR from awk variable */
206
207 void
208 set_FNR()
209 {
210         FNR = (long) FNR_node->var_value->numbr;
211 }
212
213 /* set_NR --- update internal NR from awk variable */
214
215 void
216 set_NR()
217 {
218         NR = (long) NR_node->var_value->numbr;
219 }
220
221 /* inrec --- This reads in a record from the input file */
222
223 static int
224 inrec(iop)
225 IOBUF *iop;
226 {
227         char *begin;
228         register int cnt;
229         int retval = 0;
230
231         if ((cnt = iop->cnt) != EOF)
232                 cnt = (*(iop->getrec))
233                                 (&begin, iop, RS->stptr[0], RS_regexp, NULL);
234         if (cnt == EOF) {
235                 cnt = 0;
236                 retval = 1;
237         } else {
238                 NR += 1;
239                 FNR += 1;
240                 set_record(begin, cnt, TRUE);
241         }
242
243         return retval;
244 }
245
246 /* iop_close --- close an open IOP */
247
248 static int
249 iop_close(iop)
250 IOBUF *iop;
251 {
252         int ret;
253
254         if (iop == NULL)
255                 return 0;
256         errno = 0;
257
258 #ifdef _CRAY
259         /* Work around bug in UNICOS popen */
260         if (iop->fd < 3)
261                 ret = 0;
262         else
263 #endif
264         /* save these for re-use; don't free the storage */
265         if ((iop->flag & IOP_IS_INTERNAL) != 0) {
266                 iop->off = iop->buf;
267                 iop->end = iop->buf + strlen(iop->buf);
268                 iop->cnt = 0;
269                 iop->secsiz = 0;
270                 return 0;
271         }
272
273         /* Don't close standard files or else crufty code elsewhere will lose */
274         if (iop->fd == fileno(stdin)
275             || iop->fd == fileno(stdout)
276             || iop->fd == fileno(stderr)
277             || (iop->flag & IOP_MMAPPED) != 0)
278                 ret = 0;
279         else
280                 ret = close(iop->fd);
281
282         if (ret == -1)
283                 warning("close of fd %d (`%s') failed (%s)", iop->fd,
284                                 iop->name, strerror(errno));
285         if ((iop->flag & IOP_NO_FREE) == 0) {
286                 /*
287                  * Be careful -- $0 may still reference the buffer even though
288                  * an explicit close is being done; in the future, maybe we
289                  * can do this a bit better.
290                  */
291                 if (iop->buf) {
292                         if ((fields_arr[0]->stptr >= iop->buf)
293                             && (fields_arr[0]->stptr < (iop->buf + iop->secsiz + iop->size))) {
294                                 NODE *t;
295         
296                                 t = make_string(fields_arr[0]->stptr,
297                                                 fields_arr[0]->stlen);
298                                 unref(fields_arr[0]);
299                                 fields_arr[0] = t;
300                                 reset_record();
301                         }
302                         if ((iop->flag & IOP_MMAPPED) == 0)
303                                 free(iop->buf);
304 #ifdef HAVE_MMAP
305                         else
306                                 (void) munmap(iop->buf, iop->size);
307 #endif
308                 }
309                 if ((iop->flag & IOP_NOFREE_OBJ) == 0)
310                         free((char *) iop);
311         }
312         return ret == -1 ? 1 : 0;
313 }
314
315 /* do_input --- the main input processing loop */
316
317 void
318 do_input()
319 {
320         IOBUF *iop;
321         extern int exiting;
322
323         (void) setjmp(filebuf); /* for `nextfile' */
324
325         while ((iop = nextfile(FALSE)) != NULL) {
326                 if (inrec(iop) == 0)
327                         while (interpret(expression_value) && inrec(iop) == 0)
328                                 continue;
329 #ifdef C_ALLOCA
330                 /* recover any space from C based alloca */
331                 (void) alloca(0);
332 #endif
333                 if (exiting)
334                         break;
335         }
336 }
337
338 /* redirect --- Redirection for printf and print commands */
339
340 struct redirect *
341 redirect(tree, errflg)
342 NODE *tree;
343 int *errflg;
344 {
345         register NODE *tmp;
346         register struct redirect *rp;
347         register char *str;
348         int tflag = 0;
349         int outflag = 0;
350         const char *direction = "to";
351         const char *mode;
352         int fd;
353         const char *what = NULL;
354
355         switch (tree->type) {
356         case Node_redirect_append:
357                 tflag = RED_APPEND;
358                 /* FALL THROUGH */
359         case Node_redirect_output:
360                 outflag = (RED_FILE|RED_WRITE);
361                 tflag |= outflag;
362                 if (tree->type == Node_redirect_output)
363                         what = ">";
364                 else
365                         what = ">>";
366                 break;
367         case Node_redirect_pipe:
368                 tflag = (RED_PIPE|RED_WRITE);
369                 what = "|";
370                 break;
371         case Node_redirect_pipein:
372                 tflag = (RED_PIPE|RED_READ);
373                 what = "|";
374                 break;
375         case Node_redirect_input:
376                 tflag = (RED_FILE|RED_READ);
377                 what = "<";
378                 break;
379         default:
380                 fatal("invalid tree type %d in redirect()", tree->type);
381                 break;
382         }
383         tmp = tree_eval(tree->subnode);
384         if (do_lint && (tmp->flags & STR) == 0)
385                 warning("expression in `%s' redirection only has numeric value",
386                         what);
387         tmp = force_string(tmp);
388         str = tmp->stptr;
389
390         if (str == NULL || *str == '\0')
391                 fatal("expression for `%s' redirection has null string value",
392                         what);
393
394         if (do_lint
395             && (STREQN(str, "0", tmp->stlen) || STREQN(str, "1", tmp->stlen)))
396                 warning("filename `%s' for `%s' redirection may be result of logical expression", str, what);
397         for (rp = red_head; rp != NULL; rp = rp->next)
398                 if (strlen(rp->value) == tmp->stlen
399                     && STREQN(rp->value, str, tmp->stlen)
400                     && ((rp->flag & ~(RED_NOBUF|RED_EOF)) == tflag
401                         || (outflag != 0
402                             && (rp->flag & (RED_FILE|RED_WRITE)) == outflag)))
403                         break;
404         if (rp == NULL) {
405                 emalloc(rp, struct redirect *, sizeof(struct redirect),
406                         "redirect");
407                 emalloc(str, char *, tmp->stlen+1, "redirect");
408                 memcpy(str, tmp->stptr, tmp->stlen);
409                 str[tmp->stlen] = '\0';
410                 rp->value = str;
411                 rp->flag = tflag;
412                 rp->fp = NULL;
413                 rp->iop = NULL;
414                 rp->pid = 0;    /* unlikely that we're worried about init */
415                 rp->status = 0;
416                 /* maintain list in most-recently-used first order */
417                 if (red_head != NULL)
418                         red_head->prev = rp;
419                 rp->prev = NULL;
420                 rp->next = red_head;
421                 red_head = rp;
422         } else
423                 str = rp->value;        /* get \0 terminated string */
424         while (rp->fp == NULL && rp->iop == NULL) {
425                 if (rp->flag & RED_EOF)
426                         /*
427                          * encountered EOF on file or pipe -- must be cleared
428                          * by explicit close() before reading more
429                          */
430                         return rp;
431                 mode = NULL;
432                 errno = 0;
433                 switch (tree->type) {
434                 case Node_redirect_output:
435                         mode = "w";
436                         if ((rp->flag & RED_USED) != 0)
437                                 mode = "a";
438                         break;
439                 case Node_redirect_append:
440                         mode = "a";
441                         break;
442                 case Node_redirect_pipe:
443                         /* synchronize output before new pipe */
444                         (void) flush_io();
445
446                         if ((rp->fp = popen(str, "w")) == NULL)
447                                 fatal("can't open pipe (\"%s\") for output (%s)",
448                                         str, strerror(errno));
449                         rp->flag |= RED_NOBUF;
450                         break;
451                 case Node_redirect_pipein:
452                         direction = "from";
453                         if (gawk_popen(str, rp) == NULL)
454                                 fatal("can't open pipe (\"%s\") for input (%s)",
455                                         str, strerror(errno));
456                         break;
457                 case Node_redirect_input:
458                         direction = "from";
459                         rp->iop = iop_open(str, "r", NULL);
460                         break;
461                 default:
462                         cant_happen();
463                 }
464                 if (mode != NULL) {
465                         errno = 0;
466                         fd = devopen(str, mode);
467                         if (fd > INVALID_HANDLE) {
468                                 if (fd == fileno(stdin))
469                                         rp->fp = stdin;
470                                 else if (fd == fileno(stdout))
471                                         rp->fp = stdout;
472                                 else if (fd == fileno(stderr))
473                                         rp->fp = stderr;
474                                 else {
475                                         rp->fp = fdopen(fd, (char *) mode);
476                                         /* don't leak file descriptors */
477                                         if (rp->fp == NULL)
478                                                 close(fd);
479                                 }
480                                 if (rp->fp != NULL && isatty(fd))
481                                         rp->flag |= RED_NOBUF;
482                                 /* Move rp to the head of the list. */
483                                 if (red_head != rp) {
484                                         if ((rp->prev->next = rp->next) != NULL)
485                                                 rp->next->prev = rp->prev;
486                                         red_head->prev = rp;
487                                         rp->prev = NULL;
488                                         rp->next = red_head;
489                                         red_head = rp;
490                                 }
491                         }
492                 }
493                 if (rp->fp == NULL && rp->iop == NULL) {
494                         /* too many files open -- close one and try again */
495                         if (errno == EMFILE || errno == ENFILE)
496                                 close_one();
497 #if defined __MINGW32__ || defined HAVE_MMAP
498                         /* this works for solaris 2.5, not sunos */
499                         /* it is also needed for MINGW32 */
500                         else if (errno == 0)    /* HACK! */
501                                 close_one();
502 #endif
503 #ifdef VMS
504                         /* Alpha/VMS V7.1's C RTL is returning this instead
505                            of EMFILE (haven't tried other post-V6.2 systems) */
506 #define SS$_EXQUOTA 0x001C
507                         else if (errno == EIO && vaxc$errno == SS$_EXQUOTA)
508                                 close_one();
509 #endif
510                         else {
511                                 /*
512                                  * Some other reason for failure.
513                                  *
514                                  * On redirection of input from a file,
515                                  * just return an error, so e.g. getline
516                                  * can return -1.  For output to file,
517                                  * complain. The shell will complain on
518                                  * a bad command to a pipe.
519                                  */
520                                 if (errflg != NULL)
521                                         *errflg = errno;
522                                 if (tree->type == Node_redirect_output
523                                     || tree->type == Node_redirect_append)
524                                         fatal("can't redirect %s `%s' (%s)",
525                                             direction, str, strerror(errno));
526                                 else {
527                                         free_temp(tmp);
528                                         return NULL;
529                                 }
530                         }
531                 }
532         }
533         free_temp(tmp);
534         return rp;
535 }
536
537 /* getredirect --- find the struct redirect for this file or pipe */
538
539 struct redirect *
540 getredirect(str, len)
541 char *str;
542 int len;
543 {
544         struct redirect *rp;
545
546         for (rp = red_head; rp != NULL; rp = rp->next)
547                 if (strlen(rp->value) == len && STREQN(rp->value, str, len))
548                         return rp;
549
550         return NULL;
551 }
552
553 /* close_one --- temporarily close an open file to re-use the fd */
554
555 static void
556 close_one()
557 {
558         register struct redirect *rp;
559         register struct redirect *rplast = NULL;
560
561         /* go to end of list first, to pick up least recently used entry */
562         for (rp = red_head; rp != NULL; rp = rp->next)
563                 rplast = rp;
564         /* now work back up through the list */
565         for (rp = rplast; rp != NULL; rp = rp->prev)
566                 if (rp->fp != NULL && (rp->flag & RED_FILE) != 0) {
567                         rp->flag |= RED_USED;
568                         errno = 0;
569                         if (/* do_lint && */ fclose(rp->fp) != 0)
570                                 warning("close of \"%s\" failed (%s).",
571                                         rp->value, strerror(errno));
572                         rp->fp = NULL;
573                         break;
574                 }
575         if (rp == NULL)
576                 /* surely this is the only reason ??? */
577                 fatal("too many pipes or input files open"); 
578 }
579
580 /* do_close --- completely close an open file or pipe */
581
582 NODE *
583 do_close(tree)
584 NODE *tree;
585 {
586         NODE *tmp;
587         register struct redirect *rp;
588
589         tmp = force_string(tree_eval(tree->subnode));
590
591         for (rp = red_head; rp != NULL; rp = rp->next) {
592                 if (strlen(rp->value) == tmp->stlen
593                     && STREQN(rp->value, tmp->stptr, tmp->stlen))
594                         break;
595         }
596
597         if (rp == NULL) {       /* no match */
598                 /* icky special case: close(FILENAME) called. */
599                 if (tree->subnode == FILENAME_node
600                     || (tmp->stlen == FILENAME_node->var_value->stlen
601                         && STREQN(tmp->stptr, FILENAME_node->var_value->stptr, tmp->stlen))) {
602                         (void) nextfile(TRUE);
603                 } else if (do_lint)
604                         warning("close: `%.*s' is not an open file or pipe",
605                                 tmp->stlen, tmp->stptr);
606
607                 free_temp(tmp);
608                 return tmp_number((AWKNUM) 0.0);
609         }
610         free_temp(tmp);
611         fflush(stdout); /* synchronize regular output */
612         tmp = tmp_number((AWKNUM) close_redir(rp, FALSE));
613         rp = NULL;
614         return tmp;
615 }
616
617 /* close_redir --- close an open file or pipe */
618
619 static int
620 close_redir(rp, exitwarn)
621 register struct redirect *rp;
622 int exitwarn;
623 {
624         int status = 0;
625         char *what;
626
627         if (rp == NULL)
628                 return 0;
629         if (rp->fp == stdout || rp->fp == stderr)
630                 return 0;
631         errno = 0;
632         if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE))
633                 status = pclose(rp->fp);
634         else if (rp->fp != NULL)
635                 status = fclose(rp->fp);
636         else if (rp->iop != NULL) {
637                 if ((rp->flag & RED_PIPE) != 0)
638                         status = gawk_pclose(rp);
639                 else {
640                         status = iop_close(rp->iop);
641                         rp->iop = NULL;
642                 }
643         }
644
645         what = ((rp->flag & RED_PIPE) != 0) ? "pipe" : "file";
646
647         /* SVR4 awk checks and warns about status of close */
648         if (status != 0) {
649                 char *s = strerror(errno);
650
651                 /*
652                  * Too many people have complained about this.
653                  * As of 2.15.6, it is now under lint control.
654                  */
655                 if (do_lint)
656                         warning("failure status (%d) on %s close of \"%s\" (%s)",
657                                 status, what, rp->value, s);
658
659                 if (! do_traditional) {
660                         /* set ERRNO too so that program can get at it */
661                         unref(ERRNO_node->var_value);
662                         ERRNO_node->var_value = make_string(s, strlen(s));
663                 }
664         }
665
666         if (exitwarn) 
667                 warning("no explicit close of %s `%s' provided",
668                         what, rp->value);
669
670         if (rp->next != NULL)
671                 rp->next->prev = rp->prev;
672         if (rp->prev != NULL)
673                 rp->prev->next = rp->next;
674         else
675                 red_head = rp->next;
676         free(rp->value);
677         free((char *) rp);
678         return status;
679 }
680
681 /* flush_io --- flush all open output files */
682
683 int
684 flush_io()
685 {
686         register struct redirect *rp;
687         int status = 0;
688
689         errno = 0;
690         if (fflush(stdout)) {
691                 warning("error writing standard output (%s)", strerror(errno));
692                 status++;
693         }
694         if (fflush(stderr)) {
695                 warning("error writing standard error (%s)", strerror(errno));
696                 status++;
697         }
698         for (rp = red_head; rp != NULL; rp = rp->next)
699                 /* flush both files and pipes, what the heck */
700                 if ((rp->flag & RED_WRITE) && rp->fp != NULL) {
701                         if (fflush(rp->fp)) {
702                                 warning("%s flush of \"%s\" failed (%s).",
703                                     (rp->flag  & RED_PIPE) ? "pipe" :
704                                     "file", rp->value, strerror(errno));
705                                 status++;
706                         }
707                 }
708         return status;
709 }
710
711 /* close_io --- close all open files, called when exiting */
712
713 int
714 close_io()
715 {
716         register struct redirect *rp;
717         register struct redirect *next;
718         int status = 0;
719
720         errno = 0;
721         for (rp = red_head; rp != NULL; rp = next) {
722                 next = rp->next;
723                 /*
724                  * close_redir() will print a message if needed
725                  * if do_lint, warn about lack of explicit close
726                  */
727                 if (close_redir(rp, do_lint))
728                         status++;
729                 rp = NULL;
730         }
731         /*
732          * Some of the non-Unix os's have problems doing an fclose
733          * on stdout and stderr.  Since we don't really need to close
734          * them, we just flush them, and do that across the board.
735          */
736         if (fflush(stdout)) {
737                 warning("error writing standard output (%s)", strerror(errno));
738                 status++;
739         }
740         if (fflush(stderr)) {
741                 warning("error writing standard error (%s)", strerror(errno));
742                 status++;
743         }
744         return status;
745 }
746
747 /* str2mode --- convert a string mode to an integer mode */
748
749 static int
750 str2mode(mode)
751 const char *mode;
752 {
753         int ret;
754
755         switch(mode[0]) {
756         case 'r':
757                 ret = O_RDONLY;
758                 break;
759
760         case 'w':
761                 ret = O_WRONLY|O_CREAT|O_TRUNC;
762                 break;
763
764         case 'a':
765                 ret = O_WRONLY|O_APPEND|O_CREAT;
766                 break;
767
768         default:
769                 ret = 0;                /* lint */
770                 cant_happen();
771         }
772         return ret;
773 }
774
775 /* devopen --- handle /dev/std{in,out,err}, /dev/fd/N, regular files */
776
777 /*
778  * This separate version is still needed for output, since file and pipe
779  * output is done with stdio. iop_open() handles input with IOBUFs of
780  * more "special" files.  Those files are not handled here since it makes
781  * no sense to use them for output.
782  */
783
784 int
785 devopen(name, mode)
786 const char *name, *mode;
787 {
788         int openfd;
789         const char *cp;
790         char *ptr;
791         int flag = 0;
792         struct stat buf;
793         extern double strtod();
794
795         flag = str2mode(mode);
796
797         if (STREQ(name, "-"))
798                 openfd = fileno(stdin);
799         else
800                 openfd = INVALID_HANDLE;
801
802         if (do_traditional)
803                 goto strictopen;
804
805         if ((openfd = os_devopen(name, flag)) >= 0)
806                 return openfd;
807
808         if (STREQN(name, "/dev/", 5) && stat((char *) name, &buf) == -1) {
809                 cp = name + 5;
810                 
811                 if (STREQ(cp, "stdin") && (flag & O_ACCMODE) == O_RDONLY)
812                         openfd = fileno(stdin);
813                 else if (STREQ(cp, "stdout") && (flag & O_ACCMODE) == O_WRONLY)
814                         openfd = fileno(stdout);
815                 else if (STREQ(cp, "stderr") && (flag & O_ACCMODE) == O_WRONLY)
816                         openfd = fileno(stderr);
817                 else if (STREQN(cp, "fd/", 3)) {
818                         cp += 3;
819                         openfd = (int) strtod(cp, &ptr);
820                         if (openfd <= INVALID_HANDLE || ptr == cp)
821                                 openfd = INVALID_HANDLE;
822                 }
823         }
824
825 strictopen:
826         if (openfd == INVALID_HANDLE)
827                 openfd = open(name, flag, 0666);
828         if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0) 
829                 if (S_ISDIR(buf.st_mode))
830                         fatal("file `%s' is a directory", name);
831         return openfd;
832 }
833
834
835 /* spec_setup --- setup an IOBUF for a special internal file */
836
837 static void
838 spec_setup(iop, len, allocate)
839 IOBUF *iop;
840 int len;
841 int allocate;
842 {
843         char *cp;
844
845         if (allocate) {
846                 emalloc(cp, char *, len+2, "spec_setup");
847                 iop->buf = cp;
848         } else {
849                 len = strlen(iop->buf);
850                 iop->buf[len++] = '\n'; /* get_a_record clobbered it */
851                 iop->buf[len] = '\0';   /* just in case */
852         }
853         iop->off = iop->buf;
854         iop->cnt = 0;
855         iop->secsiz = 0;
856         iop->size = len;
857         iop->end = iop->buf + len;
858         iop->fd = -1;
859         iop->flag = IOP_IS_INTERNAL;
860         iop->getrec = get_a_record;
861 }
862
863 /* specfdopen --- open an fd special file */
864
865 static int
866 specfdopen(iop, name, mode)
867 IOBUF *iop;
868 const char *name, *mode;
869 {
870         int fd;
871         IOBUF *tp;
872
873         fd = devopen(name, mode);
874         if (fd == INVALID_HANDLE)
875                 return INVALID_HANDLE;
876         tp = iop_alloc(fd, name, NULL);
877         if (tp == NULL) {
878                 /* don't leak fd's */
879                 close(fd);
880                 return INVALID_HANDLE;
881         }
882         *iop = *tp;
883         iop->flag |= IOP_NO_FREE;
884         free(tp);
885         return 0;
886 }
887
888 #ifdef GETPGRP_VOID
889 #define getpgrp_arg() /* nothing */
890 #else
891 #define getpgrp_arg() getpid()
892 #endif
893
894 /* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */
895
896 static int
897 pidopen(iop, name, mode)
898 IOBUF *iop;
899 const char *name, *mode;
900 {
901         char tbuf[BUFSIZ];
902         int i;
903
904         if (name[6] == 'g')
905                 sprintf(tbuf, "%d\n", (int) getpgrp(getpgrp_arg()));
906         else if (name[6] == 'i')
907                 sprintf(tbuf, "%d\n", (int) getpid());
908         else
909                 sprintf(tbuf, "%d\n", (int) getppid());
910         i = strlen(tbuf);
911         spec_setup(iop, i, TRUE);
912         strcpy(iop->buf, tbuf);
913         return 0;
914 }
915
916 /* useropen --- "open" /dev/user */
917
918 /*
919  * /dev/user creates a record as follows:
920  *      $1 = getuid()
921  *      $2 = geteuid()
922  *      $3 = getgid()
923  *      $4 = getegid()
924  * If multiple groups are supported, then $5 through $NF are the
925  * supplementary group set.
926  */
927
928 static int
929 useropen(iop, name, mode)
930 IOBUF *iop;
931 const char *name, *mode;
932 {
933         char tbuf[BUFSIZ], *cp;
934         int i;
935 #if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
936         GETGROUPS_T groupset[NGROUPS_MAX];
937         int ngroups;
938 #endif
939
940         sprintf(tbuf, "%d %d %d %d", (int) getuid(), (int) geteuid(), (int) getgid(), (int) getegid());
941
942         cp = tbuf + strlen(tbuf);
943 #if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
944         ngroups = getgroups(NGROUPS_MAX, groupset);
945         if (ngroups == -1)
946                 fatal("could not find groups: %s", strerror(errno));
947
948         for (i = 0; i < ngroups; i++) {
949                 *cp++ = ' ';
950                 sprintf(cp, "%d", (int) groupset[i]);
951                 cp += strlen(cp);
952         }
953 #endif
954         *cp++ = '\n';
955         *cp++ = '\0';
956
957         i = strlen(tbuf);
958         spec_setup(iop, i, TRUE);
959         strcpy(iop->buf, tbuf);
960         return 0;
961 }
962
963 /* iop_open --- handle special and regular files for input */
964
965 static IOBUF *
966 iop_open(name, mode, iop)
967 const char *name, *mode;
968 IOBUF *iop;
969 {
970         int openfd = INVALID_HANDLE;
971         int flag = 0;
972         struct stat buf;
973         static struct internal {
974                 const char *name;
975                 int compare;
976                 int (*fp) P((IOBUF *, const char *, const char *));
977                 IOBUF iob;
978         } table[] = {
979                 { "/dev/fd/",           8,      specfdopen },
980                 { "/dev/stdin",         10,     specfdopen },
981                 { "/dev/stdout",        11,     specfdopen },
982                 { "/dev/stderr",        11,     specfdopen },
983                 { "/dev/pid",           8,      pidopen },
984                 { "/dev/ppid",          9,      pidopen },
985                 { "/dev/pgrpid",        11,     pidopen },
986                 { "/dev/user",          9,      useropen },
987         };
988         int devcount = sizeof(table) / sizeof(table[0]);
989
990         flag = str2mode(mode);
991
992         /*
993          * FIXME: remove the stat call, and always process these files
994          * internally.
995          */
996         if (STREQ(name, "-"))
997                 openfd = fileno(stdin);
998         else if (do_traditional)
999                 goto strictopen;
1000         else if (STREQN(name, "/dev/", 5) && stat((char *) name, &buf) == -1) {
1001                 int i;
1002
1003                 for (i = 0; i < devcount; i++) {
1004                         if (STREQN(name, table[i].name, table[i].compare)) {
1005                                 iop = & table[i].iob;
1006
1007                                 if (iop->buf != NULL) {
1008                                         spec_setup(iop, 0, FALSE);
1009                                         return iop;
1010                                 } else if ((*table[i].fp)(iop, name, mode) == 0)
1011                                         return iop;
1012                                 else {
1013                                         warning("could not open %s, mode `%s'",
1014                                                 name, mode);
1015                                         return NULL;
1016                                 }
1017                         }
1018                 }
1019         }
1020
1021 strictopen:
1022         if (openfd == INVALID_HANDLE)
1023                 openfd = open(name, flag, 0666);
1024         if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0) 
1025                 if (S_ISDIR(buf.st_mode))
1026                         fatal("file `%s' is a directory", name);
1027         return iop_alloc(openfd, name, iop);
1028 }
1029
1030 #ifndef PIPES_SIMULATED         /* real pipes */
1031
1032 /* wait_any --- wait for a child process, close associated pipe */
1033
1034 static int
1035 wait_any(interesting)
1036 int interesting;        /* pid of interest, if any */
1037 {
1038         RETSIGTYPE (*hstat)(), (*istat)(), (*qstat)();
1039         int pid;
1040         int status = 0;
1041         struct redirect *redp;
1042         extern int errno;
1043
1044         hstat = signal(SIGHUP, SIG_IGN);
1045         istat = signal(SIGINT, SIG_IGN);
1046         qstat = signal(SIGQUIT, SIG_IGN);
1047         for (;;) {
1048 #ifdef HAVE_SYS_WAIT_H  /* Posix compatible sys/wait.h */
1049                 pid = wait(&status);
1050 #else
1051                 pid = wait((union wait *)&status);
1052 #endif /* NeXT */
1053                 if (interesting && pid == interesting) {
1054                         break;
1055                 } else if (pid != -1) {
1056                         for (redp = red_head; redp != NULL; redp = redp->next)
1057                                 if (pid == redp->pid) {
1058                                         redp->pid = -1;
1059                                         redp->status = status;
1060                                         break;
1061                                 }
1062                 }
1063                 if (pid == -1 && errno == ECHILD)
1064                         break;
1065         }
1066         signal(SIGHUP, hstat);
1067         signal(SIGINT, istat);
1068         signal(SIGQUIT, qstat);
1069         return(status);
1070 }
1071
1072 /* gawk_popen --- open an IOBUF on a child process */
1073
1074 static IOBUF *
1075 gawk_popen(cmd, rp)
1076 char *cmd;
1077 struct redirect *rp;
1078 {
1079         int p[2];
1080         register int pid;
1081
1082         /*
1083          * used to wait for any children to synchronize input and output,
1084          * but this could cause gawk to hang when it is started in a pipeline
1085          * and thus has a child process feeding it input (shell dependant)
1086          */
1087         /*(void) wait_any(0);*/ /* wait for outstanding processes */
1088
1089         if (pipe(p) < 0)
1090                 fatal("cannot open pipe \"%s\" (%s)", cmd, strerror(errno));
1091         if ((pid = fork()) == 0) {
1092                 if (close(1) == -1)
1093                         fatal("close of stdout in child failed (%s)",
1094                                 strerror(errno));
1095                 if (dup(p[1]) != 1)
1096                         fatal("dup of pipe failed (%s)", strerror(errno));
1097                 if (close(p[0]) == -1 || close(p[1]) == -1)
1098                         fatal("close of pipe failed (%s)", strerror(errno));
1099                 execl("/bin/sh", "sh", "-c", cmd, NULL);
1100                 _exit(127);
1101         }
1102         if (pid == -1)
1103                 fatal("cannot fork for \"%s\" (%s)", cmd, strerror(errno));
1104         rp->pid = pid;
1105         if (close(p[1]) == -1)
1106                 fatal("close of pipe failed (%s)", strerror(errno));
1107         rp->iop = iop_alloc(p[0], cmd, NULL);
1108         if (rp->iop == NULL)
1109                 (void) close(p[0]);
1110         return (rp->iop);
1111 }
1112
1113 /* gawk_pclose --- close an open child pipe */
1114
1115 static int
1116 gawk_pclose(rp)
1117 struct redirect *rp;
1118 {
1119         (void) iop_close(rp->iop);
1120         rp->iop = NULL;
1121
1122         /* process previously found, return stored status */
1123         if (rp->pid == -1)
1124                 return (rp->status >> 8) & 0xFF;
1125         rp->status = wait_any(rp->pid);
1126         rp->pid = -1;
1127         return (rp->status >> 8) & 0xFF;
1128 }
1129
1130 #else   /* PIPES_SIMULATED */
1131
1132 /*
1133  * use temporary file rather than pipe
1134  * except if popen() provides real pipes too
1135  */
1136
1137 #if defined(VMS) || defined(OS2) || defined (MSDOS) || defined(WIN32)
1138
1139 /* gawk_popen --- open an IOBUF on a child process */
1140
1141 static IOBUF *
1142 gawk_popen(cmd, rp)
1143 char *cmd;
1144 struct redirect *rp;
1145 {
1146         FILE *current;
1147
1148         if ((current = popen(cmd, "r")) == NULL)
1149                 return NULL;
1150         rp->iop = iop_alloc(fileno(current), cmd, NULL);
1151         if (rp->iop == NULL) {
1152                 (void) pclose(current);
1153                 current = NULL;
1154         }
1155         rp->ifp = current;
1156         return (rp->iop);
1157 }
1158
1159 /* gawk_pclose --- close an open child pipe */
1160
1161 static int
1162 gawk_pclose(rp)
1163 struct redirect *rp;
1164 {
1165         int rval, aval, fd = rp->iop->fd;
1166
1167         rp->iop->fd = dup(fd);    /* kludge to allow close() + pclose() */
1168         rval = iop_close(rp->iop);
1169         rp->iop = NULL;
1170         aval = pclose(rp->ifp);
1171         rp->ifp = NULL;
1172         return (rval < 0 ? rval : aval);
1173 }
1174 #else   /* not (VMS || OS2 || MSDOS) */
1175
1176 static struct pipeinfo {
1177         char *command;
1178         char *name;
1179 } pipes[_NFILE];
1180
1181 /* gawk_popen --- open an IOBUF on a child process */
1182
1183 static IOBUF *
1184 gawk_popen(cmd, rp)
1185 char *cmd;
1186 struct redirect *rp;
1187 {
1188         extern char *strdup P((const char *));
1189         int current;
1190         char *name;
1191         static char cmdbuf[256];
1192
1193         /* get a name to use */
1194         if ((name = tempnam(".", "pip")) == NULL)
1195                 return NULL;
1196         sprintf(cmdbuf, "%s > %s", cmd, name);
1197         system(cmdbuf);
1198         if ((current = open(name, O_RDONLY)) == INVALID_HANDLE)
1199                 return NULL;
1200         pipes[current].name = name;
1201         pipes[current].command = strdup(cmd);
1202         rp->iop = iop_alloc(current, name, NULL);
1203         if (rp->iop == NULL)
1204                 (void) close(current);
1205         return (rp->iop);
1206 }
1207
1208 /* gawk_pclose --- close an open child pipe */
1209
1210 static int
1211 gawk_pclose(rp)
1212 struct redirect *rp;
1213 {
1214         int cur = rp->iop->fd;
1215         int rval;
1216
1217         rval = iop_close(rp->iop);
1218         rp->iop = NULL;
1219
1220         /* check for an open file  */
1221         if (pipes[cur].name == NULL)
1222                 return -1;
1223         unlink(pipes[cur].name);
1224         free(pipes[cur].name);
1225         pipes[cur].name = NULL;
1226         free(pipes[cur].command);
1227         return rval;
1228 }
1229 #endif  /* not (VMS || OS2 || MSDOS) */
1230
1231 #endif  /* PIPES_SIMULATED */
1232
1233 /* do_getline --- read in a line, into var and with redirection, as needed */
1234
1235 NODE *
1236 do_getline(tree)
1237 NODE *tree;
1238 {
1239         struct redirect *rp = NULL;
1240         IOBUF *iop;
1241         int cnt = EOF;
1242         char *s = NULL;
1243         int errcode;
1244
1245         while (cnt == EOF) {
1246                 if (tree->rnode == NULL) {       /* no redirection */
1247                         iop = nextfile(FALSE);
1248                         if (iop == NULL)                /* end of input */
1249                                 return tmp_number((AWKNUM) 0.0);
1250                 } else {
1251                         int redir_error = 0;
1252
1253                         rp = redirect(tree->rnode, &redir_error);
1254                         if (rp == NULL && redir_error) { /* failed redirect */
1255                                 if (! do_traditional) {
1256                                         s = strerror(redir_error);
1257
1258                                         unref(ERRNO_node->var_value);
1259                                         ERRNO_node->var_value =
1260                                                 make_string(s, strlen(s));
1261                                 }
1262                                 return tmp_number((AWKNUM) -1.0);
1263                         }
1264                         iop = rp->iop;
1265                         if (iop == NULL)                /* end of input */
1266                                 return tmp_number((AWKNUM) 0.0);
1267                 }
1268                 errcode = 0;
1269                 cnt = (*(iop->getrec))(&s, iop, RS->stptr[0], RS_regexp, &errcode);
1270                 if (errcode != 0) {
1271                         if (! do_traditional) {
1272                                 s = strerror(errcode);
1273
1274                                 unref(ERRNO_node->var_value);
1275                                 ERRNO_node->var_value = make_string(s, strlen(s));
1276                         }
1277                         return tmp_number((AWKNUM) -1.0);
1278                 }
1279                 if (cnt == EOF) {
1280                         if (rp != NULL) {
1281                                 /*
1282                                  * Don't do iop_close() here if we are
1283                                  * reading from a pipe; otherwise
1284                                  * gawk_pclose will not be called.
1285                                  */
1286                                 if ((rp->flag & RED_PIPE) == 0) {
1287                                         (void) iop_close(iop);
1288                                         rp->iop = NULL;
1289                                 }
1290                                 rp->flag |= RED_EOF;    /* sticky EOF */
1291                                 return tmp_number((AWKNUM) 0.0);
1292                         } else
1293                                 continue;       /* try another file */
1294                 }
1295                 if (rp == NULL) {
1296                         NR++;
1297                         FNR++;
1298                 }
1299                 if (tree->lnode == NULL)        /* no optional var. */
1300                         set_record(s, cnt, TRUE);
1301                 else {                  /* assignment to variable */
1302                         Func_ptr after_assign = NULL;
1303                         NODE **lhs;
1304
1305                         lhs = get_lhs(tree->lnode, &after_assign);
1306                         unref(*lhs);
1307                         *lhs = make_string(s, cnt);
1308                         (*lhs)->flags |= MAYBE_NUM;
1309                         /* we may have to regenerate $0 here! */
1310                         if (after_assign != NULL)
1311                                 (*after_assign)();
1312                 }
1313         }
1314         return tmp_number((AWKNUM) 1.0);
1315 }
1316
1317 /* pathopen --- pathopen with default file extension handling */
1318
1319 int
1320 pathopen(file)
1321 const char *file;
1322 {
1323         int fd = do_pathopen(file);
1324
1325 #ifdef DEFAULT_FILETYPE
1326         if (! do_traditional && fd <= INVALID_HANDLE) {
1327                 char *file_awk;
1328                 int save = errno;
1329 #ifdef VMS
1330                 int vms_save = vaxc$errno;
1331 #endif
1332
1333                 /* append ".awk" and try again */
1334                 emalloc(file_awk, char *, strlen(file) +
1335                         sizeof(DEFAULT_FILETYPE) + 1, "pathopen");
1336                 sprintf(file_awk, "%s%s", file, DEFAULT_FILETYPE);
1337                 fd = do_pathopen(file_awk);
1338                 free(file_awk);
1339                 if (fd <= INVALID_HANDLE) {
1340                         errno = save;
1341 #ifdef VMS
1342                         vaxc$errno = vms_save;
1343 #endif
1344                 }
1345         }
1346 #endif  /*DEFAULT_FILETYPE*/
1347
1348         return fd;
1349 }
1350
1351 /* do_pathopen --- search $AWKPATH for source file */
1352
1353 static int
1354 do_pathopen(file)
1355 const char *file;
1356 {
1357         static const char *savepath = NULL;
1358         static int first = TRUE;
1359         const char *awkpath;
1360         char *cp, trypath[BUFSIZ];
1361         int fd;
1362
1363         if (STREQ(file, "-"))
1364                 return (0);
1365
1366         if (do_traditional)
1367                 return (devopen(file, "r"));
1368
1369         if (first) {
1370                 first = FALSE;
1371                 if ((awkpath = getenv("AWKPATH")) != NULL && *awkpath)
1372                         savepath = awkpath;     /* used for restarting */
1373                 else
1374                         savepath = defpath;
1375         }
1376         awkpath = savepath;
1377
1378         /* some kind of path name, no search */
1379         if (ispath(file))
1380                 return (devopen(file, "r"));
1381
1382         do {
1383                 trypath[0] = '\0';
1384                 /* this should take into account limits on size of trypath */
1385                 for (cp = trypath; *awkpath && *awkpath != envsep; )
1386                         *cp++ = *awkpath++;
1387
1388                 if (cp != trypath) {    /* nun-null element in path */
1389                         /* add directory punctuation only if needed */
1390                         if (! isdirpunct(*(cp-1)))
1391                                 *cp++ = '/';
1392                         /* append filename */
1393                         strcpy(cp, file);
1394                 } else
1395                         strcpy(trypath, file);
1396                 if ((fd = devopen(trypath, "r")) > INVALID_HANDLE)
1397                         return (fd);
1398
1399                 /* no luck, keep going */
1400                 if(*awkpath == envsep && awkpath[1] != '\0')
1401                         awkpath++;      /* skip colon */
1402         } while (*awkpath != '\0');
1403         /*
1404          * You might have one of the awk paths defined, WITHOUT the current
1405          * working directory in it. Therefore try to open the file in the
1406          * current directory.
1407          */
1408         return (devopen(file, "r"));
1409 }
1410
1411 #ifdef TEST
1412 int bufsize = 8192;
1413
1414 void
1415 fatal(s)
1416 char *s;
1417 {
1418         printf("%s\n", s);
1419         exit(1);
1420 }
1421 #endif
1422
1423 /* iop_alloc --- allocate an IOBUF structure for an open fd */
1424
1425 static IOBUF *
1426 iop_alloc(fd, name, iop)
1427 int fd;
1428 const char *name;
1429 IOBUF *iop;
1430 {
1431         struct stat sbuf;
1432
1433         if (fd == INVALID_HANDLE)
1434                 return NULL;
1435         if (iop == NULL)
1436                 emalloc(iop, IOBUF *, sizeof(IOBUF), "iop_alloc");
1437         iop->flag = 0;
1438         if (isatty(fd))
1439                 iop->flag |= IOP_IS_TTY;
1440         iop->size = optimal_bufsize(fd, & sbuf);
1441         if (do_lint && S_ISREG(sbuf.st_mode) && sbuf.st_size == 0)
1442                 warning("data file `%s' is empty", name);
1443         iop->secsiz = -2;
1444         errno = 0;
1445         iop->fd = fd;
1446         iop->off = iop->buf = NULL;
1447         iop->cnt = 0;
1448         iop->name = name;
1449         iop->getrec = get_a_record;
1450 #ifdef HAVE_MMAP
1451         /* Use mmap only for regular files with positive sizes.
1452            The size must fit into size_t, so that mmap works correctly.
1453            Also, it must fit into int, so that iop->cnt won't overflow.  */
1454         if (S_ISREG(sbuf.st_mode) && sbuf.st_size > 0
1455             && sbuf.st_size == (size_t) sbuf.st_size
1456             && sbuf.st_size == (int) sbuf.st_size) {
1457                 register char *cp;
1458
1459                 iop->buf = iop->off = mmap((caddr_t) 0, sbuf.st_size,
1460                                         PROT_READ|PROT_WRITE, MAP_PRIVATE,
1461                                         fd,  0L);
1462                 /* cast is for buggy compilers (e.g. DEC OSF/1) */
1463                 if (iop->buf == (caddr_t)MAP_FAILED) {
1464                         iop->buf = iop->off = NULL;
1465                         goto out;
1466                 }
1467
1468                 iop->flag |= IOP_MMAPPED;
1469                 iop->size = sbuf.st_size;
1470                 iop->secsiz = 0;
1471                 iop->end = iop->buf + iop->size;
1472                 iop->cnt = sbuf.st_size;
1473                 iop->getrec = mmap_get_record;
1474                 (void) close(fd);
1475                 iop->fd = INVALID_HANDLE;
1476
1477 #if defined(HAVE_MADVISE) && defined(MADV_SEQUENTIAL)
1478                 madvise(iop->buf, iop->size, MADV_SEQUENTIAL);
1479 #endif
1480                 /*
1481                  * The following is a really gross hack.
1482                  * We want to ensure that we have a copy of the input
1483                  * data that won't go away, on the off chance that someone
1484                  * will truncate the data file we've just mmap'ed.
1485                  * So, we go through and touch each page, forcing the
1486                  * system to give us a private copy. A page size of 512
1487                  * guarantees this will work, even on the least common
1488                  * denominator system (like, oh say, a VAX).
1489                  */
1490                 for (cp = iop->buf; cp < iop->end; cp += 512)
1491                         *cp = *cp;
1492         }
1493 out:
1494 #endif /* HAVE_MMAP */
1495         return iop;
1496 }
1497
1498 /* These macros used by both record reading routines */
1499 #define set_RT_to_null() \
1500         (void)(! do_traditional && (unref(RT_node->var_value), \
1501                            RT_node->var_value = Nnull_string))
1502
1503 #define set_RT(str, len) \
1504         (void)(! do_traditional && (unref(RT_node->var_value), \
1505                            RT_node->var_value = make_string(str, len)))
1506
1507 /*
1508  * get_a_record:
1509  * Get the next record.  Uses a "split buffer" where the latter part is
1510  * the normal read buffer and the head part is an "overflow" area that is used
1511  * when a record spans the end of the normal buffer, in which case the first
1512  * part of the record is copied into the overflow area just before the
1513  * normal buffer.  Thus, the eventual full record can be returned as a
1514  * contiguous area of memory with a minimum of copying.  The overflow area
1515  * is expanded as needed, so that records are unlimited in length.
1516  * We also mark both the end of the buffer and the end of the read() with
1517  * a sentinel character (the current record separator) so that the inside
1518  * loop can run as a single test.
1519  *
1520  * Note that since we know or can compute the end of the read and the end
1521  * of the buffer, the sentinel character does not get in the way of regexp
1522  * based searching, since we simply search up to that character, but not
1523  * including it.
1524  */
1525
1526 static int
1527 get_a_record(out, iop, grRS, RSre, errcode)
1528 char **out;             /* pointer to pointer to data */
1529 IOBUF *iop;             /* input IOP */
1530 register int grRS;      /* first char in RS->stptr */
1531 Regexp *RSre;           /* regexp for RS */
1532 int *errcode;           /* pointer to error variable */
1533 {
1534         register char *bp = iop->off;
1535         char *bufend;
1536         char *start = iop->off;                 /* beginning of record */
1537         int rs;
1538         static Regexp *RS_null_re = NULL;
1539         Regexp *rsre = NULL;
1540         int continuing = FALSE, continued = FALSE;      /* used for re matching */
1541         int onecase;
1542
1543         /* first time through */
1544         if (RS_null_re == NULL) {
1545                 RS_null_re = make_regexp("\n\n+", 3, TRUE, TRUE);
1546                 if (RS_null_re == NULL)
1547                         fatal("internal error: file `%s', line %d\n",
1548                                 __FILE__, __LINE__);
1549         }
1550
1551         if (iop->cnt == EOF) {  /* previous read hit EOF */
1552                 *out = NULL;
1553                 set_RT_to_null();
1554                 return EOF;
1555         }
1556
1557         if (RS_is_null) /* special case:  RS == "" */
1558                 rs = '\n';
1559         else
1560                 rs = (char) grRS;
1561
1562         onecase = (IGNORECASE && isalpha(rs));
1563         if (onecase)
1564                 rs = casetable[rs];
1565
1566         /* set up sentinel */
1567         if (iop->buf) {
1568                 bufend = iop->buf + iop->size + iop->secsiz;
1569                 *bufend = rs;           /* add sentinel to buffer */
1570         } else
1571                 bufend = NULL;
1572
1573         for (;;) {      /* break on end of record, read error or EOF */
1574 /* buffer mgmt, chunk #1 */
1575                 /*
1576                  * Following code is entered on the first call of this routine
1577                  * for a new iop, or when we scan to the end of the buffer.
1578                  * In the latter case, we copy the current partial record to
1579                  * the space preceding the normal read buffer.  If necessary,
1580                  * we expand this space.  This is done so that we can return
1581                  * the record as a contiguous area of memory.
1582                  */
1583                 if ((iop->flag & IOP_IS_INTERNAL) == 0 && bp >= bufend) {
1584                         char *oldbuf = NULL;
1585                         char *oldsplit = iop->buf + iop->secsiz;
1586                         long len;       /* record length so far */
1587
1588                         len = bp - start;
1589                         if (len > iop->secsiz) {
1590                                 /* expand secondary buffer */
1591                                 if (iop->secsiz == -2)
1592                                         iop->secsiz = 256;
1593                                 while (len > iop->secsiz)
1594                                         iop->secsiz *= 2;
1595                                 oldbuf = iop->buf;
1596                                 emalloc(iop->buf, char *,
1597                                     iop->size+iop->secsiz+2, "get_a_record");
1598                                 bufend = iop->buf + iop->size + iop->secsiz;
1599                                 *bufend = rs;
1600                         }
1601                         if (len > 0) {
1602                                 char *newsplit = iop->buf + iop->secsiz;
1603
1604                                 if (start < oldsplit) {
1605                                         memcpy(newsplit - len, start,
1606                                                         oldsplit - start);
1607                                         memcpy(newsplit - (bp - oldsplit),
1608                                                         oldsplit, bp - oldsplit);
1609                                 } else
1610                                         memcpy(newsplit - len, start, len);
1611                         }
1612                         bp = iop->end = iop->off = iop->buf + iop->secsiz;
1613                         start = bp - len;
1614                         if (oldbuf != NULL) {
1615                                 free(oldbuf);
1616                                 oldbuf = NULL;
1617                         }
1618                 }
1619 /* buffer mgmt, chunk #2 */
1620                 /*
1621                  * Following code is entered whenever we have no more data to
1622                  * scan.  In most cases this will read into the beginning of
1623                  * the main buffer, but in some cases (terminal, pipe etc.)
1624                  * we may be doing smallish reads into more advanced positions.
1625                  */
1626                 if (bp >= iop->end) {
1627                         if ((iop->flag & IOP_IS_INTERNAL) != 0) {
1628                                 iop->cnt = EOF;
1629                                 break;
1630                         }
1631                         iop->cnt = read(iop->fd, iop->end, bufend - iop->end);
1632                         if (iop->cnt == -1) {
1633                                 if (! do_traditional && errcode != NULL) {
1634                                         *errcode = errno;
1635                                         iop->cnt = EOF;
1636                                         break;
1637                                 } else
1638                                         fatal("error reading input file `%s': %s",
1639                                                 iop->name, strerror(errno));
1640                         } else if (iop->cnt == 0) {
1641                                 /*
1642                                  * hit EOF before matching RS, so end
1643                                  * the record and set RT to ""
1644                                  */
1645                                 iop->cnt = EOF;
1646                                 /* see comments below about this test */
1647                                 if (! continuing) {
1648                                         set_RT_to_null();
1649                                         break;
1650                                 }
1651                         }
1652                         if (iop->cnt != EOF) {
1653                                 iop->end += iop->cnt;
1654                                 *iop->end = rs;         /* reset the sentinel */
1655                         }
1656                 }
1657 /* buffers are now setup and filled with data */
1658 /* search for RS, #1, regexp based, or RS = "" */
1659                 /*
1660                  * Attempt to simplify the code a bit. The case where
1661                  * RS = "" can also be described by a regexp, RS = "\n\n+".
1662                  * The buffer managment and searching code can thus now
1663                  * use a common case (the one for regexps) both when RS is
1664                  * a regexp, and when RS = "". This particularly benefits
1665                  * us for keeping track of how many newlines were matched
1666                  * in order to set RT.
1667                  */
1668                 if (! do_traditional && RSre != NULL)   /* regexp */
1669                         rsre = RSre;
1670                 else if (RS_is_null)            /* RS = "" */
1671                         rsre = RS_null_re;
1672                 else
1673                         rsre = NULL;
1674
1675                 /*
1676                  * Look for regexp match of RS.  Non-match conditions are:
1677                  *      1. No match at all
1678                  *      2. Match of a null string
1679                  *      3. Match ends at exact end of buffer
1680                  * Number 3 is subtle; we have to add more to the buffer
1681                  * in case the match would have extended further into the
1682                  * file, since regexp match by definition always matches the
1683                  * longest possible match.
1684                  *
1685                  * It is even more subtle than you might think. Suppose
1686                  * the re matches at exactly the end of file. We don't know
1687                  * that until we try to add more to the buffer. Thus, we
1688                  * set a flag to indicate, that if eof really does happen,
1689                  * don't break early.
1690                  */
1691                 continuing = FALSE;
1692                 if (rsre != NULL) {
1693                 again:
1694                         /* cases 1 and 2 are simple, just keep going */
1695                         if (research(rsre, start, 0, iop->end - start, TRUE) == -1
1696                             || RESTART(rsre, start) == REEND(rsre, start)) {
1697                                 /*
1698                                  * Leading newlines at the beginning of the file
1699                                  * should be ignored. Whew!
1700                                  */
1701                                 if (RS_is_null && *start == '\n') {
1702                                         /*
1703                                          * have to catch the case of a
1704                                          * single newline at the front of
1705                                          * the record, which the regex
1706                                          * doesn't. gurr.
1707                                          */
1708                                         while (*start == '\n' && start < iop->end)
1709                                                 start++;
1710                                         goto again;
1711                                 }
1712                                 bp = iop->end;
1713                                 continue;
1714                         }
1715                         /* case 3, regex match at exact end */
1716                         if (start + REEND(rsre, start) >= iop->end) {
1717                                 if (iop->cnt != EOF) {
1718                                         bp = iop->end;
1719                                         continuing = continued = TRUE;
1720                                         continue;
1721                                 }
1722                         }
1723                         /* got a match! */
1724                         /*
1725                          * Leading newlines at the beginning of the file
1726                          * should be ignored. Whew!
1727                          *
1728                          * Is this code ever executed?
1729                          */
1730                         if (RS_is_null && RESTART(rsre, start) == 0) {
1731                                 start += REEND(rsre, start);
1732                                 goto again;
1733                         }
1734                         bp = start + RESTART(rsre, start);
1735                         set_RT(bp, REEND(rsre, start) - RESTART(rsre, start));
1736                         *bp = '\0';
1737                         iop->off = start + REEND(rsre, start);
1738                         break;
1739                 }
1740 /* search for RS, #2, RS = <single char> */
1741                 if (onecase) {
1742                         while (casetable[(int) *bp++] != rs)
1743                                 continue;
1744                 } else {
1745                         while (*bp++ != rs)
1746                                 continue;
1747                 }
1748                 set_RT(bp - 1, 1);
1749
1750                 if (bp <= iop->end)
1751                         break;
1752                 else
1753                         bp--;
1754
1755                 if ((iop->flag & IOP_IS_INTERNAL) != 0)
1756                         iop->cnt = bp - start;
1757         }
1758         if (iop->cnt == EOF
1759             && (((iop->flag & IOP_IS_INTERNAL) != 0)
1760                   || (start == bp && ! continued))) {
1761                 *out = NULL;
1762                 set_RT_to_null();
1763                 return EOF;
1764         }
1765
1766         if (do_traditional || rsre == NULL) {
1767                 char *bstart;
1768
1769                 bstart = iop->off = bp;
1770                 bp--;
1771                 if (onecase ? casetable[(int) *bp] != rs : *bp != rs) {
1772                         bp++;
1773                         bstart = bp;
1774                 }
1775                 *bp = '\0';
1776         } else if (RS_is_null && iop->cnt == EOF) {
1777                 /*
1778                  * special case, delete trailing newlines,
1779                  * should never be more than one.
1780                  */
1781                 while (bp[-1] == '\n')
1782                         bp--;
1783                 *bp = '\0';
1784         }
1785
1786         *out = start;
1787         return bp - start;
1788 }
1789
1790 #ifdef TEST
1791 int
1792 main(argc, argv)
1793 int argc;
1794 char *argv[];
1795 {
1796         IOBUF *iop;
1797         char *out;
1798         int cnt;
1799         char rs[2];
1800
1801         rs[0] = '\0';
1802         if (argc > 1)
1803                 bufsize = atoi(argv[1]);
1804         if (argc > 2)
1805                 rs[0] = *argv[2];
1806         iop = iop_alloc(0, "stdin", NULL);
1807         while ((cnt = get_a_record(&out, iop, rs[0], NULL, NULL)) > 0) {
1808                 fwrite(out, 1, cnt, stdout);
1809                 fwrite(rs, 1, 1, stdout);
1810         }
1811         return 0;
1812 }
1813 #endif
1814
1815 #ifdef HAVE_MMAP
1816 /* mmap_get_record --- pull a record out of a memory-mapped file */
1817
1818 static int
1819 mmap_get_record(out, iop, grRS, RSre, errcode)
1820 char **out;             /* pointer to pointer to data */
1821 IOBUF *iop;             /* input IOP */
1822 register int grRS;      /* first char in RS->stptr */
1823 Regexp *RSre;           /* regexp for RS */
1824 int *errcode;           /* pointer to error variable */
1825 {
1826         register char *bp = iop->off;
1827         char *start = iop->off;                 /* beginning of record */
1828         int rs;
1829         static Regexp *RS_null_re = NULL;
1830         Regexp *rsre = NULL;
1831         int onecase;
1832         register char *end = iop->end;
1833         int cnt;
1834
1835         /* first time through */
1836         if (RS_null_re == NULL) {
1837                 RS_null_re = make_regexp("\n\n+", 3, TRUE, TRUE);
1838                 if (RS_null_re == NULL)
1839                         fatal("internal error: file `%s', line %d\n",
1840                                 __FILE__, __LINE__);
1841         }
1842
1843         if (iop->off >= iop->end) {     /* previous record was last */
1844                 *out = NULL;
1845                 set_RT_to_null();
1846                 iop->cnt = EOF;         /* tested by higher level code */
1847                 return EOF;
1848         }
1849
1850         if (RS_is_null) /* special case:  RS == "" */
1851                 rs = '\n';
1852         else
1853                 rs = (char) grRS;
1854
1855         onecase = (IGNORECASE && isalpha(rs));
1856         if (onecase)
1857                 rs = casetable[rs];
1858
1859         /* if RS = "", skip leading newlines at the front of the file */
1860         if (RS_is_null && iop->off == iop->buf) {
1861                 for (bp = iop->off; *bp == '\n'; bp++)
1862                         continue;
1863
1864                 if (bp != iop->off)
1865                         iop->off = start = bp;
1866         }
1867
1868         /*
1869          * Regexp based searching. Either RS = "" or RS = <regex>
1870          * See comments in get_a_record.
1871          */
1872         if (! do_traditional && RSre != NULL)   /* regexp */
1873                 rsre = RSre;
1874         else if (RS_is_null)            /* RS = "" */
1875                 rsre = RS_null_re;
1876         else
1877                 rsre = NULL;
1878
1879         /*
1880          * Look for regexp match of RS.  Non-match conditions are:
1881          *      1. No match at all
1882          *      2. Match of a null string
1883          *      3. Match ends at exact end of buffer
1884          *
1885          * #1 means that the record ends the file
1886          * and there is no text that actually matched RS.
1887          *
1888          * #2: is probably like #1.
1889          *
1890          * #3 is simple; since we have the whole file mapped, it's
1891          * the last record in the file.
1892          */
1893         if (rsre != NULL) {
1894                 if (research(rsre, start, 0, iop->end - start, TRUE) == -1
1895                     || RESTART(rsre, start) == REEND(rsre, start)) {
1896                         /* no matching text, we have the record */
1897                         *out = start;
1898                         iop->off = iop->end;    /* all done with the record */
1899                         set_RT_to_null();
1900                         /* special case, don't allow trailing newlines */
1901                         if (RS_is_null && *(iop->end - 1) == '\n')
1902                                 return iop->end - start - 1;
1903                         else
1904                                 return iop->end - start;
1905
1906                 }
1907                 /* have a match */
1908                 *out = start;
1909                 bp = start + RESTART(rsre, start);
1910                 set_RT(bp, REEND(rsre, start) - RESTART(rsre, start));
1911                 *bp = '\0';
1912                 iop->off = start + REEND(rsre, start);
1913                 return bp - start;
1914         }
1915
1916         /*
1917          * RS = "?", i.e., one character based searching.
1918          *
1919          * Alas, we can't just plug the sentinel character in at
1920          * the end of the mmapp'ed file ( *(iop->end) = rs; ). This
1921          * works if we're lucky enough to have a file that does not
1922          * take up all of its last disk block. But if we end up with
1923          * file whose size is an even multiple of the disk block size,
1924          * assigning past the end of it delivers a SIGBUS. So, we have to
1925          * add the extra test in the while loop at the front that looks
1926          * for going past the end of the mapped object. Sigh.
1927          */
1928         /* search for RS, #2, RS = <single char> */
1929         if (onecase) {
1930                 while (bp < end && casetable[*bp++] != rs)
1931                         continue;
1932         } else {
1933                 while (bp < end && *bp++ != rs)
1934                         continue;
1935         }
1936         cnt = (bp - start) - 1;
1937         if (bp >= iop->end) {
1938                 /* at end, may have actually seen rs, or may not */
1939                 if (*(bp-1) == rs)
1940                         set_RT(bp - 1, 1);      /* real RS seen */
1941                 else {
1942                         cnt++;
1943                         set_RT_to_null();
1944                 }
1945         } else
1946                 set_RT(bp - 1, 1);
1947
1948         iop->off = bp;
1949         *out = start;
1950         return cnt;
1951 }
1952 #endif /* HAVE_MMAP */
1953
1954 /* set_RS --- update things as appropriate when RS is set */
1955
1956 void
1957 set_RS()
1958 {
1959         static NODE *save_rs = NULL;
1960
1961         if (save_rs && cmp_nodes(RS_node->var_value, save_rs) == 0)
1962                 return;
1963         unref(save_rs);
1964         save_rs = dupnode(RS_node->var_value);
1965         RS_is_null = FALSE;
1966         RS = force_string(RS_node->var_value);
1967         if (RS_regexp != NULL) {
1968                 refree(RS_regexp);
1969                 RS_regexp = NULL;
1970         }
1971         if (RS->stlen == 0)
1972                 RS_is_null = TRUE;
1973         else if (RS->stlen > 1) {
1974                 static int warned = FALSE;
1975
1976                 RS_regexp = make_regexp(RS->stptr, RS->stlen, IGNORECASE, TRUE);
1977
1978                 if (do_lint && ! warned) {
1979                         warning("multicharacter value of `RS' is not portable");
1980                         warned = TRUE;
1981                 }
1982         }
1983
1984         set_FS_if_not_FIELDWIDTHS();
1985 }