160886Selan /*
260886Selan * io.c --- routines for dealing with input and output and records
360886Selan */
460886Selan
560886Selan /*
660886Selan * Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
760886Selan *
860886Selan * This file is part of GAWK, the GNU implementation of the
960886Selan * AWK Progamming Language.
1060886Selan *
1160886Selan * GAWK is free software; you can redistribute it and/or modify
1260886Selan * it under the terms of the GNU General Public License as published by
1360886Selan * the Free Software Foundation; either version 2 of the License, or
1460886Selan * (at your option) any later version.
1560886Selan *
1660886Selan * GAWK is distributed in the hope that it will be useful,
1760886Selan * but WITHOUT ANY WARRANTY; without even the implied warranty of
1860886Selan * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1960886Selan * GNU General Public License for more details.
2060886Selan *
2160886Selan * You should have received a copy of the GNU General Public License
2260886Selan * along with GAWK; see the file COPYING. If not, write to
2360886Selan * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
2460886Selan */
2560886Selan
26*60888Selan #include <sys/param.h>
2760886Selan #include "awk.h"
2860886Selan
2960886Selan #ifndef O_RDONLY
3060886Selan #include <fcntl.h>
3160886Selan #endif
3260886Selan
3360886Selan #if !defined(S_ISDIR) && defined(S_IFDIR)
3460886Selan #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
3560886Selan #endif
3660886Selan
3760886Selan #ifndef atarist
3860886Selan #define INVALID_HANDLE (-1)
3960886Selan #else
4060886Selan #define INVALID_HANDLE (__SMALLEST_VALID_HANDLE - 1)
4160886Selan #endif
4260886Selan
4360886Selan #if defined(MSDOS) || defined(atarist)
4460886Selan #define PIPES_SIMULATED
4560886Selan #endif
4660886Selan
4760886Selan static IOBUF *nextfile P((int skipping));
4860886Selan static int inrec P((IOBUF *iop));
4960886Selan static int iop_close P((IOBUF *iop));
5060886Selan struct redirect *redirect P((NODE *tree, int *errflg));
5160886Selan static void close_one P((void));
5260886Selan static int close_redir P((struct redirect *rp));
5360886Selan #ifndef PIPES_SIMULATED
5460886Selan static int wait_any P((int interesting));
5560886Selan #endif
5660886Selan static IOBUF *gawk_popen P((char *cmd, struct redirect *rp));
5760886Selan static IOBUF *iop_open P((char *file, char *how));
5860886Selan static int gawk_pclose P((struct redirect *rp));
5960886Selan static int do_pathopen P((char *file));
6060886Selan
6160886Selan extern FILE *fdopen();
6260886Selan extern FILE *popen();
6360886Selan
6460886Selan static struct redirect *red_head = NULL;
6560886Selan
6660886Selan extern int output_is_tty;
6760886Selan extern NODE *ARGC_node;
6860886Selan extern NODE *ARGV_node;
6960886Selan extern NODE *ARGIND_node;
7060886Selan extern NODE *ERRNO_node;
7160886Selan extern NODE **fields_arr;
7260886Selan
7360886Selan static jmp_buf filebuf; /* for do_nextfile() */
7460886Selan
7560886Selan /* do_nextfile --- implement gawk "next file" extension */
7660886Selan
7760886Selan void
do_nextfile()7860886Selan do_nextfile()
7960886Selan {
8060886Selan (void) nextfile(1);
8160886Selan longjmp(filebuf, 1);
8260886Selan }
8360886Selan
8460886Selan static IOBUF *
nextfile(skipping)8560886Selan nextfile(skipping)
8660886Selan int skipping;
8760886Selan {
8860886Selan static int i = 1;
8960886Selan static int files = 0;
9060886Selan NODE *arg;
9160886Selan int fd = INVALID_HANDLE;
9260886Selan static IOBUF *curfile = NULL;
9360886Selan
9460886Selan if (skipping) {
9560886Selan if (curfile != NULL)
9660886Selan iop_close(curfile);
9760886Selan curfile = NULL;
9860886Selan return NULL;
9960886Selan }
10060886Selan if (curfile != NULL) {
10160886Selan if (curfile->cnt == EOF) {
10260886Selan (void) iop_close(curfile);
10360886Selan curfile = NULL;
10460886Selan } else
10560886Selan return curfile;
10660886Selan }
10760886Selan for (; i < (int) (ARGC_node->lnode->numbr); i++) {
10860886Selan arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i));
10960886Selan if (arg->stptr[0] == '\0')
11060886Selan continue;
11160886Selan arg->stptr[arg->stlen] = '\0';
11260886Selan if (! do_unix) {
11360886Selan ARGIND_node->var_value->numbr = i;
11460886Selan ARGIND_node->var_value->flags = NUM|NUMBER;
11560886Selan }
11660886Selan if (!arg_assign(arg->stptr)) {
11760886Selan files++;
11860886Selan curfile = iop_open(arg->stptr, "r");
11960886Selan if (curfile == NULL)
12060886Selan fatal("cannot open file `%s' for reading (%s)",
12160886Selan arg->stptr, strerror(errno));
12260886Selan /* NOTREACHED */
12360886Selan /* This is a kludge. */
12460886Selan unref(FILENAME_node->var_value);
12560886Selan FILENAME_node->var_value =
12660886Selan dupnode(arg);
12760886Selan FNR = 0;
12860886Selan i++;
12960886Selan break;
13060886Selan }
13160886Selan }
13260886Selan if (files == 0) {
13360886Selan files++;
13460886Selan /* no args. -- use stdin */
13560886Selan /* FILENAME is init'ed to "-" */
13660886Selan /* FNR is init'ed to 0 */
13760886Selan curfile = iop_alloc(fileno(stdin));
13860886Selan }
13960886Selan return curfile;
14060886Selan }
14160886Selan
14260886Selan void
set_FNR()14360886Selan set_FNR()
14460886Selan {
14560886Selan FNR = (int) FNR_node->var_value->numbr;
14660886Selan }
14760886Selan
14860886Selan void
set_NR()14960886Selan set_NR()
15060886Selan {
15160886Selan NR = (int) NR_node->var_value->numbr;
15260886Selan }
15360886Selan
15460886Selan /*
15560886Selan * This reads in a record from the input file
15660886Selan */
15760886Selan static int
inrec(iop)15860886Selan inrec(iop)
15960886Selan IOBUF *iop;
16060886Selan {
16160886Selan char *begin;
16260886Selan register int cnt;
16360886Selan int retval = 0;
16460886Selan
16560886Selan cnt = get_a_record(&begin, iop, *RS, NULL);
16660886Selan if (cnt == EOF) {
16760886Selan cnt = 0;
16860886Selan retval = 1;
16960886Selan } else {
17060886Selan NR += 1;
17160886Selan FNR += 1;
17260886Selan }
17360886Selan set_record(begin, cnt, 1);
17460886Selan
17560886Selan return retval;
17660886Selan }
17760886Selan
17860886Selan static int
iop_close(iop)17960886Selan iop_close(iop)
18060886Selan IOBUF *iop;
18160886Selan {
18260886Selan int ret;
18360886Selan
18460886Selan if (iop == NULL)
18560886Selan return 0;
18660886Selan errno = 0;
18760886Selan
18860886Selan #ifdef _CRAY
18960886Selan /* Work around bug in UNICOS popen */
19060886Selan if (iop->fd < 3)
19160886Selan ret = 0;
19260886Selan else
19360886Selan #endif
19460886Selan /* save these for re-use; don't free the storage */
19560886Selan if ((iop->flag & IOP_IS_INTERNAL) != 0) {
19660886Selan iop->off = iop->buf;
19760886Selan iop->end = iop->buf + strlen(iop->buf);
19860886Selan iop->cnt = 0;
19960886Selan iop->secsiz = 0;
20060886Selan return 0;
20160886Selan }
20260886Selan
20360886Selan /* Don't close standard files or else crufty code elsewhere will lose */
20460886Selan if (iop->fd == fileno(stdin) ||
20560886Selan iop->fd == fileno(stdout) ||
20660886Selan iop->fd == fileno(stderr))
20760886Selan ret = 0;
20860886Selan else
20960886Selan ret = close(iop->fd);
21060886Selan if (ret == -1)
21160886Selan warning("close of fd %d failed (%s)", iop->fd, strerror(errno));
21260886Selan if ((iop->flag & IOP_NO_FREE) == 0) {
21360886Selan /*
21460886Selan * be careful -- $0 may still reference the buffer even though
21560886Selan * an explicit close is being done; in the future, maybe we
21660886Selan * can do this a bit better
21760886Selan */
21860886Selan if (iop->buf) {
21960886Selan if ((fields_arr[0]->stptr >= iop->buf)
22060886Selan && (fields_arr[0]->stptr < iop->end)) {
22160886Selan NODE *t;
22260886Selan
22360886Selan t = make_string(fields_arr[0]->stptr,
22460886Selan fields_arr[0]->stlen);
22560886Selan unref(fields_arr[0]);
22660886Selan fields_arr [0] = t;
22760886Selan reset_record ();
22860886Selan }
22960886Selan free(iop->buf);
23060886Selan }
23160886Selan free((char *)iop);
23260886Selan }
23360886Selan return ret == -1 ? 1 : 0;
23460886Selan }
23560886Selan
23660886Selan void
do_input()23760886Selan do_input()
23860886Selan {
23960886Selan IOBUF *iop;
24060886Selan extern int exiting;
24160886Selan
24260886Selan if (setjmp(filebuf) != 0) {
24360886Selan }
24460886Selan while ((iop = nextfile(0)) != NULL) {
24560886Selan if (inrec(iop) == 0)
24660886Selan while (interpret(expression_value) && inrec(iop) == 0)
24760886Selan ;
24860886Selan if (exiting)
24960886Selan break;
25060886Selan }
25160886Selan }
25260886Selan
25360886Selan /* Redirection for printf and print commands */
25460886Selan struct redirect *
redirect(tree,errflg)25560886Selan redirect(tree, errflg)
25660886Selan NODE *tree;
25760886Selan int *errflg;
25860886Selan {
25960886Selan register NODE *tmp;
26060886Selan register struct redirect *rp;
26160886Selan register char *str;
26260886Selan int tflag = 0;
26360886Selan int outflag = 0;
26460886Selan char *direction = "to";
26560886Selan char *mode;
26660886Selan int fd;
26760886Selan char *what = NULL;
26860886Selan
26960886Selan switch (tree->type) {
27060886Selan case Node_redirect_append:
27160886Selan tflag = RED_APPEND;
27260886Selan /* FALL THROUGH */
27360886Selan case Node_redirect_output:
27460886Selan outflag = (RED_FILE|RED_WRITE);
27560886Selan tflag |= outflag;
27660886Selan if (tree->type == Node_redirect_output)
27760886Selan what = ">";
27860886Selan else
27960886Selan what = ">>";
28060886Selan break;
28160886Selan case Node_redirect_pipe:
28260886Selan tflag = (RED_PIPE|RED_WRITE);
28360886Selan what = "|";
28460886Selan break;
28560886Selan case Node_redirect_pipein:
28660886Selan tflag = (RED_PIPE|RED_READ);
28760886Selan what = "|";
28860886Selan break;
28960886Selan case Node_redirect_input:
29060886Selan tflag = (RED_FILE|RED_READ);
29160886Selan what = "<";
29260886Selan break;
29360886Selan default:
29460886Selan fatal ("invalid tree type %d in redirect()", tree->type);
29560886Selan break;
29660886Selan }
29760886Selan tmp = tree_eval(tree->subnode);
29860886Selan if (do_lint && ! (tmp->flags & STR))
29960886Selan warning("expression in `%s' redirection only has numeric value",
30060886Selan what);
30160886Selan tmp = force_string(tmp);
30260886Selan str = tmp->stptr;
30360886Selan if (str == NULL || *str == '\0')
30460886Selan fatal("expression for `%s' redirection has null string value",
30560886Selan what);
30660886Selan if (do_lint
30760886Selan && (STREQN(str, "0", tmp->stlen) || STREQN(str, "1", tmp->stlen)))
30860886Selan warning("filename `%s' for `%s' redirection may be result of logical expression", str, what);
30960886Selan for (rp = red_head; rp != NULL; rp = rp->next)
31060886Selan if (strlen(rp->value) == tmp->stlen
31160886Selan && STREQN(rp->value, str, tmp->stlen)
31260886Selan && ((rp->flag & ~(RED_NOBUF|RED_EOF)) == tflag
31360886Selan || (outflag
31460886Selan && (rp->flag & (RED_FILE|RED_WRITE)) == outflag)))
31560886Selan break;
31660886Selan if (rp == NULL) {
31760886Selan emalloc(rp, struct redirect *, sizeof(struct redirect),
31860886Selan "redirect");
31960886Selan emalloc(str, char *, tmp->stlen+1, "redirect");
32060886Selan memcpy(str, tmp->stptr, tmp->stlen);
32160886Selan str[tmp->stlen] = '\0';
32260886Selan rp->value = str;
32360886Selan rp->flag = tflag;
32460886Selan rp->fp = NULL;
32560886Selan rp->iop = NULL;
32660886Selan rp->pid = 0; /* unlikely that we're worried about init */
32760886Selan rp->status = 0;
32860886Selan /* maintain list in most-recently-used first order */
32960886Selan if (red_head)
33060886Selan red_head->prev = rp;
33160886Selan rp->prev = NULL;
33260886Selan rp->next = red_head;
33360886Selan red_head = rp;
33460886Selan }
33560886Selan while (rp->fp == NULL && rp->iop == NULL) {
33660886Selan if (rp->flag & RED_EOF)
33760886Selan /* encountered EOF on file or pipe -- must be cleared
33860886Selan * by explicit close() before reading more
33960886Selan */
34060886Selan return rp;
34160886Selan mode = NULL;
34260886Selan errno = 0;
34360886Selan switch (tree->type) {
34460886Selan case Node_redirect_output:
34560886Selan mode = "w";
34660886Selan if (rp->flag & RED_USED)
34760886Selan mode = "a";
34860886Selan break;
34960886Selan case Node_redirect_append:
35060886Selan mode = "a";
35160886Selan break;
35260886Selan case Node_redirect_pipe:
35360886Selan if ((rp->fp = popen(str, "w")) == NULL)
35460886Selan fatal("can't open pipe (\"%s\") for output (%s)",
35560886Selan str, strerror(errno));
35660886Selan rp->flag |= RED_NOBUF;
35760886Selan break;
35860886Selan case Node_redirect_pipein:
35960886Selan direction = "from";
36060886Selan if (gawk_popen(str, rp) == NULL)
36160886Selan fatal("can't open pipe (\"%s\") for input (%s)",
36260886Selan str, strerror(errno));
36360886Selan break;
36460886Selan case Node_redirect_input:
36560886Selan direction = "from";
36660886Selan rp->iop = iop_open(str, "r");
36760886Selan break;
36860886Selan default:
36960886Selan cant_happen();
37060886Selan }
37160886Selan if (mode != NULL) {
37260886Selan fd = devopen(str, mode);
37360886Selan if (fd > INVALID_HANDLE) {
37460886Selan if (fd == fileno(stdin))
37560886Selan rp->fp = stdin;
37660886Selan else if (fd == fileno(stdout))
37760886Selan rp->fp = stdout;
37860886Selan else if (fd == fileno(stderr))
37960886Selan rp->fp = stderr;
38060886Selan else
38160886Selan rp->fp = fdopen(fd, mode);
38260886Selan if (isatty(fd))
38360886Selan rp->flag |= RED_NOBUF;
38460886Selan }
38560886Selan }
38660886Selan if (rp->fp == NULL && rp->iop == NULL) {
38760886Selan /* too many files open -- close one and try again */
38860886Selan if (errno == EMFILE)
38960886Selan close_one();
39060886Selan else {
39160886Selan /*
39260886Selan * Some other reason for failure.
39360886Selan *
39460886Selan * On redirection of input from a file,
39560886Selan * just return an error, so e.g. getline
39660886Selan * can return -1. For output to file,
39760886Selan * complain. The shell will complain on
39860886Selan * a bad command to a pipe.
39960886Selan */
40060886Selan *errflg = errno;
40160886Selan if (tree->type == Node_redirect_output
40260886Selan || tree->type == Node_redirect_append)
40360886Selan fatal("can't redirect %s `%s' (%s)",
40460886Selan direction, str, strerror(errno));
40560886Selan else {
40660886Selan free_temp(tmp);
40760886Selan return NULL;
40860886Selan }
40960886Selan }
41060886Selan }
41160886Selan }
41260886Selan free_temp(tmp);
41360886Selan return rp;
41460886Selan }
41560886Selan
41660886Selan static void
close_one()41760886Selan close_one()
41860886Selan {
41960886Selan register struct redirect *rp;
42060886Selan register struct redirect *rplast = NULL;
42160886Selan
42260886Selan /* go to end of list first, to pick up least recently used entry */
42360886Selan for (rp = red_head; rp != NULL; rp = rp->next)
42460886Selan rplast = rp;
42560886Selan /* now work back up through the list */
42660886Selan for (rp = rplast; rp != NULL; rp = rp->prev)
42760886Selan if (rp->fp && (rp->flag & RED_FILE)) {
42860886Selan rp->flag |= RED_USED;
42960886Selan errno = 0;
43060886Selan if (fclose(rp->fp))
43160886Selan warning("close of \"%s\" failed (%s).",
43260886Selan rp->value, strerror(errno));
43360886Selan rp->fp = NULL;
43460886Selan break;
43560886Selan }
43660886Selan if (rp == NULL)
43760886Selan /* surely this is the only reason ??? */
43860886Selan fatal("too many pipes or input files open");
43960886Selan }
44060886Selan
44160886Selan NODE *
do_close(tree)44260886Selan do_close(tree)
44360886Selan NODE *tree;
44460886Selan {
44560886Selan NODE *tmp;
44660886Selan register struct redirect *rp;
44760886Selan
44860886Selan tmp = force_string(tree_eval(tree->subnode));
44960886Selan for (rp = red_head; rp != NULL; rp = rp->next) {
45060886Selan if (strlen(rp->value) == tmp->stlen
45160886Selan && STREQN(rp->value, tmp->stptr, tmp->stlen))
45260886Selan break;
45360886Selan }
45460886Selan free_temp(tmp);
45560886Selan if (rp == NULL) /* no match */
45660886Selan return tmp_number((AWKNUM) 0.0);
45760886Selan fflush(stdout); /* synchronize regular output */
45860886Selan tmp = tmp_number((AWKNUM)close_redir(rp));
45960886Selan rp = NULL;
46060886Selan return tmp;
46160886Selan }
46260886Selan
46360886Selan static int
close_redir(rp)46460886Selan close_redir(rp)
46560886Selan register struct redirect *rp;
46660886Selan {
46760886Selan int status = 0;
46860886Selan
46960886Selan if (rp == NULL)
47060886Selan return 0;
47160886Selan if (rp->fp == stdout || rp->fp == stderr)
47260886Selan return 0;
47360886Selan errno = 0;
47460886Selan if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE))
47560886Selan status = pclose(rp->fp);
47660886Selan else if (rp->fp)
47760886Selan status = fclose(rp->fp);
47860886Selan else if (rp->iop) {
47960886Selan if (rp->flag & RED_PIPE)
48060886Selan status = gawk_pclose(rp);
48160886Selan else {
48260886Selan status = iop_close(rp->iop);
48360886Selan rp->iop = NULL;
48460886Selan }
48560886Selan }
48660886Selan /* SVR4 awk checks and warns about status of close */
48760886Selan if (status) {
48860886Selan char *s = strerror(errno);
48960886Selan
49060886Selan warning("failure status (%d) on %s close of \"%s\" (%s).",
49160886Selan status,
49260886Selan (rp->flag & RED_PIPE) ? "pipe" :
49360886Selan "file", rp->value, s);
49460886Selan
49560886Selan if (! do_unix) {
49660886Selan /* set ERRNO too so that program can get at it */
49760886Selan unref(ERRNO_node->var_value);
49860886Selan ERRNO_node->var_value = make_string(s, strlen(s));
49960886Selan }
50060886Selan }
50160886Selan if (rp->next)
50260886Selan rp->next->prev = rp->prev;
50360886Selan if (rp->prev)
50460886Selan rp->prev->next = rp->next;
50560886Selan else
50660886Selan red_head = rp->next;
50760886Selan free(rp->value);
50860886Selan free((char *)rp);
50960886Selan return status;
51060886Selan }
51160886Selan
51260886Selan int
flush_io()51360886Selan flush_io ()
51460886Selan {
51560886Selan register struct redirect *rp;
51660886Selan int status = 0;
51760886Selan
51860886Selan errno = 0;
51960886Selan if (fflush(stdout)) {
52060886Selan warning("error writing standard output (%s).", strerror(errno));
52160886Selan status++;
52260886Selan }
52360886Selan if (fflush(stderr)) {
52460886Selan warning("error writing standard error (%s).", strerror(errno));
52560886Selan status++;
52660886Selan }
52760886Selan for (rp = red_head; rp != NULL; rp = rp->next)
52860886Selan /* flush both files and pipes, what the heck */
52960886Selan if ((rp->flag & RED_WRITE) && rp->fp != NULL) {
53060886Selan if (fflush(rp->fp)) {
53160886Selan warning("%s flush of \"%s\" failed (%s).",
53260886Selan (rp->flag & RED_PIPE) ? "pipe" :
53360886Selan "file", rp->value, strerror(errno));
53460886Selan status++;
53560886Selan }
53660886Selan }
53760886Selan return status;
53860886Selan }
53960886Selan
54060886Selan int
close_io()54160886Selan close_io ()
54260886Selan {
54360886Selan register struct redirect *rp;
54460886Selan register struct redirect *next;
54560886Selan int status = 0;
54660886Selan
54760886Selan errno = 0;
54860886Selan if (fclose(stdout)) {
54960886Selan warning("error writing standard output (%s).", strerror(errno));
55060886Selan status++;
55160886Selan }
55260886Selan if (fclose(stderr)) {
55360886Selan warning("error writing standard error (%s).", strerror(errno));
55460886Selan status++;
55560886Selan }
55660886Selan for (rp = red_head; rp != NULL; rp = next) {
55760886Selan next = rp->next;
55860886Selan if (close_redir(rp))
55960886Selan status++;
56060886Selan rp = NULL;
56160886Selan }
56260886Selan return status;
56360886Selan }
56460886Selan
56560886Selan /* str2mode --- convert a string mode to an integer mode */
56660886Selan
56760886Selan static int
str2mode(mode)56860886Selan str2mode(mode)
56960886Selan char *mode;
57060886Selan {
57160886Selan int ret;
57260886Selan
57360886Selan switch(mode[0]) {
57460886Selan case 'r':
57560886Selan ret = O_RDONLY;
57660886Selan break;
57760886Selan
57860886Selan case 'w':
57960886Selan ret = O_WRONLY|O_CREAT|O_TRUNC;
58060886Selan break;
58160886Selan
58260886Selan case 'a':
58360886Selan ret = O_WRONLY|O_APPEND|O_CREAT;
58460886Selan break;
58560886Selan default:
58660886Selan cant_happen();
58760886Selan }
58860886Selan return ret;
58960886Selan }
59060886Selan
59160886Selan /* devopen --- handle /dev/std{in,out,err}, /dev/fd/N, regular files */
59260886Selan
59360886Selan /*
59460886Selan * This separate version is still needed for output, since file and pipe
59560886Selan * output is done with stdio. iop_open() handles input with IOBUFs of
59660886Selan * more "special" files. Those files are not handled here since it makes
59760886Selan * no sense to use them for output.
59860886Selan */
59960886Selan
60060886Selan int
devopen(name,mode)60160886Selan devopen(name, mode)
60260886Selan char *name, *mode;
60360886Selan {
60460886Selan int openfd = INVALID_HANDLE;
60560886Selan char *cp, *ptr;
60660886Selan int flag = 0;
60760886Selan struct stat buf;
60860886Selan extern double strtod();
60960886Selan
61060886Selan flag = str2mode(mode);
61160886Selan
61260886Selan if (do_unix)
61360886Selan goto strictopen;
61460886Selan
61560886Selan #ifdef VMS
61660886Selan if ((openfd = vms_devopen(name, flag)) >= 0)
61760886Selan return openfd;
61860886Selan #endif /* VMS */
61960886Selan
62060886Selan if (STREQ(name, "-"))
62160886Selan openfd = fileno(stdin);
62260886Selan else if (STREQN(name, "/dev/", 5) && stat(name, &buf) == -1) {
62360886Selan cp = name + 5;
62460886Selan
62560886Selan if (STREQ(cp, "stdin") && (flag & O_RDONLY) == O_RDONLY)
62660886Selan openfd = fileno(stdin);
62760886Selan else if (STREQ(cp, "stdout") && (flag & O_WRONLY) == O_WRONLY)
62860886Selan openfd = fileno(stdout);
62960886Selan else if (STREQ(cp, "stderr") && (flag & O_WRONLY) == O_WRONLY)
63060886Selan openfd = fileno(stderr);
63160886Selan else if (STREQN(cp, "fd/", 3)) {
63260886Selan cp += 3;
63360886Selan openfd = (int)strtod(cp, &ptr);
63460886Selan if (openfd <= INVALID_HANDLE || ptr == cp)
63560886Selan openfd = INVALID_HANDLE;
63660886Selan }
63760886Selan }
63860886Selan
63960886Selan strictopen:
64060886Selan if (openfd == INVALID_HANDLE)
64160886Selan openfd = open(name, flag, 0666);
64260886Selan if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0)
64360886Selan if (S_ISDIR(buf.st_mode))
64460886Selan fatal("file `%s' is a directory", name);
64560886Selan return openfd;
64660886Selan }
64760886Selan
64860886Selan
64960886Selan /* spec_setup --- setup an IOBUF for a special internal file */
65060886Selan
65160886Selan void
spec_setup(iop,len,allocate)65260886Selan spec_setup(iop, len, allocate)
65360886Selan IOBUF *iop;
65460886Selan int len;
65560886Selan int allocate;
65660886Selan {
65760886Selan char *cp;
65860886Selan
65960886Selan if (allocate) {
66060886Selan emalloc(cp, char *, len+2, "spec_setup");
66160886Selan iop->buf = cp;
66260886Selan } else {
66360886Selan len = strlen(iop->buf);
66460886Selan iop->buf[len++] = '\n'; /* get_a_record clobbered it */
66560886Selan iop->buf[len] = '\0'; /* just in case */
66660886Selan }
66760886Selan iop->off = iop->buf;
66860886Selan iop->cnt = 0;
66960886Selan iop->secsiz = 0;
67060886Selan iop->size = len;
67160886Selan iop->end = iop->buf + len;
67260886Selan iop->fd = -1;
67360886Selan iop->flag = IOP_IS_INTERNAL;
67460886Selan }
67560886Selan
67660886Selan /* specfdopen --- open a fd special file */
67760886Selan
67860886Selan int
specfdopen(iop,name,mode)67960886Selan specfdopen(iop, name, mode)
68060886Selan IOBUF *iop;
68160886Selan char *name, *mode;
68260886Selan {
68360886Selan int fd;
68460886Selan IOBUF *tp;
68560886Selan
68660886Selan fd = devopen(name, mode);
68760886Selan if (fd == INVALID_HANDLE)
68860886Selan return INVALID_HANDLE;
68960886Selan tp = iop_alloc(fd);
69060886Selan if (tp == NULL)
69160886Selan return INVALID_HANDLE;
69260886Selan *iop = *tp;
69360886Selan iop->flag |= IOP_NO_FREE;
69460886Selan free(tp);
69560886Selan return 0;
69660886Selan }
69760886Selan
69860886Selan /* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */
69960886Selan
70060886Selan int
pidopen(iop,name,mode)70160886Selan pidopen(iop, name, mode)
70260886Selan IOBUF *iop;
70360886Selan char *name, *mode;
70460886Selan {
70560886Selan char tbuf[BUFSIZ];
70660886Selan int i;
70760886Selan
70860886Selan if (name[6] == 'g')
70960886Selan /* following #if will improve in 2.16 */
71060886Selan #if defined(__svr4__) || defined(i860) || defined(_AIX) || defined(BSD4_4)
71160886Selan sprintf(tbuf, "%d\n", getpgrp());
71260886Selan #else
71360886Selan sprintf(tbuf, "%d\n", getpgrp(getpid()));
71460886Selan #endif
71560886Selan else if (name[6] == 'i')
71660886Selan sprintf(tbuf, "%d\n", getpid());
71760886Selan else
71860886Selan sprintf(tbuf, "%d\n", getppid());
71960886Selan i = strlen(tbuf);
72060886Selan spec_setup(iop, i, 1);
72160886Selan strcpy(iop->buf, tbuf);
72260886Selan return 0;
72360886Selan }
72460886Selan
72560886Selan /* useropen --- "open" /dev/user */
72660886Selan
72760886Selan /*
72860886Selan * /dev/user creates a record as follows:
72960886Selan * $1 = getuid()
73060886Selan * $2 = geteuid()
73160886Selan * $3 = getgid()
73260886Selan * $4 = getegid()
73360886Selan * If multiple groups are supported, the $5 through $NF are the
73460886Selan * supplementary group set.
73560886Selan */
73660886Selan
73760886Selan int
useropen(iop,name,mode)73860886Selan useropen(iop, name, mode)
73960886Selan IOBUF *iop;
74060886Selan char *name, *mode;
74160886Selan {
74260886Selan char tbuf[BUFSIZ], *cp;
74360886Selan int i;
74460886Selan #if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
74560886Selan int groupset[NGROUPS_MAX];
74660886Selan int ngroups;
74760886Selan #endif
74860886Selan
74960886Selan sprintf(tbuf, "%d %d %d %d", getuid(), geteuid(), getgid(), getegid());
75060886Selan
75160886Selan cp = tbuf + strlen(tbuf);
75260886Selan #if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
75360886Selan ngroups = getgroups(NGROUPS_MAX, groupset);
75460886Selan if (ngroups == -1)
75560886Selan fatal("could not find groups: %s", strerror(errno));
75660886Selan
75760886Selan for (i = 0; i < ngroups; i++) {
75860886Selan *cp++ = ' ';
75960886Selan sprintf(cp, "%d", groupset[i]);
76060886Selan cp += strlen(cp);
76160886Selan }
76260886Selan #endif
76360886Selan *cp++ = '\n';
76460886Selan *cp++ = '\0';
76560886Selan
76660886Selan
76760886Selan i = strlen(tbuf);
76860886Selan spec_setup(iop, i, 1);
76960886Selan strcpy(iop->buf, tbuf);
77060886Selan return 0;
77160886Selan }
77260886Selan
77360886Selan /* iop_open --- handle special and regular files for input */
77460886Selan
77560886Selan static IOBUF *
iop_open(name,mode)77660886Selan iop_open(name, mode)
77760886Selan char *name, *mode;
77860886Selan {
77960886Selan int openfd = INVALID_HANDLE;
78060886Selan char *cp, *ptr;
78160886Selan int flag = 0;
78260886Selan int i;
78360886Selan struct stat buf;
78460886Selan IOBUF *iop;
78560886Selan static struct internal {
78660886Selan char *name;
78760886Selan int compare;
78860886Selan int (*fp)();
78960886Selan IOBUF iob;
79060886Selan } table[] = {
79160886Selan { "/dev/fd/", 8, specfdopen },
79260886Selan { "/dev/stdin", 10, specfdopen },
79360886Selan { "/dev/stdout", 11, specfdopen },
79460886Selan { "/dev/stderr", 11, specfdopen },
79560886Selan { "/dev/pid", 8, pidopen },
79660886Selan { "/dev/ppid", 9, pidopen },
79760886Selan { "/dev/pgrpid", 11, pidopen },
79860886Selan { "/dev/user", 9, useropen },
79960886Selan };
80060886Selan int devcount = sizeof(table) / sizeof(table[0]);
80160886Selan
80260886Selan flag = str2mode(mode);
80360886Selan
80460886Selan if (do_unix)
80560886Selan goto strictopen;
80660886Selan
80760886Selan if (STREQ(name, "-"))
80860886Selan openfd = fileno(stdin);
80960886Selan else if (STREQN(name, "/dev/", 5) && stat(name, &buf) == -1) {
81060886Selan int i;
81160886Selan
81260886Selan for (i = 0; i < devcount; i++) {
81360886Selan if (STREQN(name, table[i].name, table[i].compare)) {
81460886Selan IOBUF *iop = & table[i].iob;
81560886Selan
81660886Selan if (iop->buf != NULL) {
81760886Selan spec_setup(iop, 0, 0);
81860886Selan return iop;
81960886Selan } else if ((*table[i].fp)(iop, name, mode) == 0)
82060886Selan return iop;
82160886Selan else {
82260886Selan warning("could not open %s, mode `%s'",
82360886Selan name, mode);
82460886Selan return NULL;
82560886Selan }
82660886Selan }
82760886Selan }
82860886Selan }
82960886Selan
83060886Selan strictopen:
83160886Selan if (openfd == INVALID_HANDLE)
83260886Selan openfd = open(name, flag, 0666);
83360886Selan if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0)
83460886Selan if ((buf.st_mode & S_IFMT) == S_IFDIR)
83560886Selan fatal("file `%s' is a directory", name);
83660886Selan iop = iop_alloc(openfd);
83760886Selan return iop;
83860886Selan }
83960886Selan
84060886Selan #ifndef PIPES_SIMULATED
84160886Selan /* real pipes */
84260886Selan static int
wait_any(interesting)84360886Selan wait_any(interesting)
84460886Selan int interesting; /* pid of interest, if any */
84560886Selan {
84660886Selan SIGTYPE (*hstat)(), (*istat)(), (*qstat)();
84760886Selan int pid;
84860886Selan int status = 0;
84960886Selan struct redirect *redp;
85060886Selan extern int errno;
85160886Selan
85260886Selan hstat = signal(SIGHUP, SIG_IGN);
85360886Selan istat = signal(SIGINT, SIG_IGN);
85460886Selan qstat = signal(SIGQUIT, SIG_IGN);
85560886Selan for (;;) {
85660886Selan #ifdef NeXT
85760886Selan pid = wait((union wait *)&status);
85860886Selan #else
85960886Selan pid = wait(&status);
86060886Selan #endif /* NeXT */
86160886Selan if (interesting && pid == interesting) {
86260886Selan break;
86360886Selan } else if (pid != -1) {
86460886Selan for (redp = red_head; redp != NULL; redp = redp->next)
86560886Selan if (pid == redp->pid) {
86660886Selan redp->pid = -1;
86760886Selan redp->status = status;
86860886Selan if (redp->fp) {
86960886Selan pclose(redp->fp);
87060886Selan redp->fp = 0;
87160886Selan }
87260886Selan if (redp->iop) {
87360886Selan (void) iop_close(redp->iop);
87460886Selan redp->iop = 0;
87560886Selan }
87660886Selan break;
87760886Selan }
87860886Selan }
87960886Selan if (pid == -1 && errno == ECHILD)
88060886Selan break;
88160886Selan }
88260886Selan signal(SIGHUP, hstat);
88360886Selan signal(SIGINT, istat);
88460886Selan signal(SIGQUIT, qstat);
88560886Selan return(status);
88660886Selan }
88760886Selan
88860886Selan static IOBUF *
gawk_popen(cmd,rp)88960886Selan gawk_popen(cmd, rp)
89060886Selan char *cmd;
89160886Selan struct redirect *rp;
89260886Selan {
89360886Selan int p[2];
89460886Selan register int pid;
89560886Selan
89660886Selan /* used to wait for any children to synchronize input and output,
89760886Selan * but this could cause gawk to hang when it is started in a pipeline
89860886Selan * and thus has a child process feeding it input (shell dependant)
89960886Selan */
90060886Selan /*(void) wait_any(0);*/ /* wait for outstanding processes */
90160886Selan
90260886Selan if (pipe(p) < 0)
90360886Selan fatal("cannot open pipe \"%s\" (%s)", cmd, strerror(errno));
90460886Selan if ((pid = fork()) == 0) {
90560886Selan if (close(1) == -1)
90660886Selan fatal("close of stdout in child failed (%s)",
90760886Selan strerror(errno));
90860886Selan if (dup(p[1]) != 1)
90960886Selan fatal("dup of pipe failed (%s)", strerror(errno));
91060886Selan if (close(p[0]) == -1 || close(p[1]) == -1)
91160886Selan fatal("close of pipe failed (%s)", strerror(errno));
91260886Selan if (close(0) == -1)
91360886Selan fatal("close of stdin in child failed (%s)",
91460886Selan strerror(errno));
91560886Selan execl("/bin/sh", "sh", "-c", cmd, 0);
91660886Selan _exit(127);
91760886Selan }
91860886Selan if (pid == -1)
91960886Selan fatal("cannot fork for \"%s\" (%s)", cmd, strerror(errno));
92060886Selan rp->pid = pid;
92160886Selan if (close(p[1]) == -1)
92260886Selan fatal("close of pipe failed (%s)", strerror(errno));
92360886Selan return (rp->iop = iop_alloc(p[0]));
92460886Selan }
92560886Selan
92660886Selan static int
gawk_pclose(rp)92760886Selan gawk_pclose(rp)
92860886Selan struct redirect *rp;
92960886Selan {
93060886Selan (void) iop_close(rp->iop);
93160886Selan rp->iop = NULL;
93260886Selan
93360886Selan /* process previously found, return stored status */
93460886Selan if (rp->pid == -1)
93560886Selan return (rp->status >> 8) & 0xFF;
93660886Selan rp->status = wait_any(rp->pid);
93760886Selan rp->pid = -1;
93860886Selan return (rp->status >> 8) & 0xFF;
93960886Selan }
94060886Selan
94160886Selan #else /* PIPES_SIMULATED */
94260886Selan /* use temporary file rather than pipe */
94360886Selan
94460886Selan #ifdef VMS
94560886Selan static IOBUF *
gawk_popen(cmd,rp)94660886Selan gawk_popen(cmd, rp)
94760886Selan char *cmd;
94860886Selan struct redirect *rp;
94960886Selan {
95060886Selan FILE *current;
95160886Selan
95260886Selan if ((current = popen(cmd, "r")) == NULL)
95360886Selan return NULL;
95460886Selan return (rp->iop = iop_alloc(fileno(current)));
95560886Selan }
95660886Selan
95760886Selan static int
gawk_pclose(rp)95860886Selan gawk_pclose(rp)
95960886Selan struct redirect *rp;
96060886Selan {
96160886Selan int rval, aval, fd = rp->iop->fd;
96260886Selan FILE *kludge = fdopen(fd, "r"); /* pclose needs FILE* w/ right fileno */
96360886Selan
96460886Selan rp->iop->fd = dup(fd); /* kludge to allow close() + pclose() */
96560886Selan rval = iop_close(rp->iop);
96660886Selan rp->iop = NULL;
96760886Selan aval = pclose(kludge);
96860886Selan return (rval < 0 ? rval : aval);
96960886Selan }
97060886Selan #else /* VMS */
97160886Selan
97260886Selan static
97360886Selan struct {
97460886Selan char *command;
97560886Selan char *name;
97660886Selan } pipes[_NFILE];
97760886Selan
97860886Selan static IOBUF *
gawk_popen(cmd,rp)97960886Selan gawk_popen(cmd, rp)
98060886Selan char *cmd;
98160886Selan struct redirect *rp;
98260886Selan {
98360886Selan extern char *strdup(const char *);
98460886Selan int current;
98560886Selan char *name;
98660886Selan static char cmdbuf[256];
98760886Selan
98860886Selan /* get a name to use. */
98960886Selan if ((name = tempnam(".", "pip")) == NULL)
99060886Selan return NULL;
99160886Selan sprintf(cmdbuf,"%s > %s", cmd, name);
99260886Selan system(cmdbuf);
99360886Selan if ((current = open(name,O_RDONLY)) == INVALID_HANDLE)
99460886Selan return NULL;
99560886Selan pipes[current].name = name;
99660886Selan pipes[current].command = strdup(cmd);
99760886Selan rp->iop = iop_alloc(current);
99860886Selan return (rp->iop = iop_alloc(current));
99960886Selan }
100060886Selan
100160886Selan static int
gawk_pclose(rp)100260886Selan gawk_pclose(rp)
100360886Selan struct redirect *rp;
100460886Selan {
100560886Selan int cur = rp->iop->fd;
100660886Selan int rval;
100760886Selan
100860886Selan rval = iop_close(rp->iop);
100960886Selan rp->iop = NULL;
101060886Selan
101160886Selan /* check for an open file */
101260886Selan if (pipes[cur].name == NULL)
101360886Selan return -1;
101460886Selan unlink(pipes[cur].name);
101560886Selan free(pipes[cur].name);
101660886Selan pipes[cur].name = NULL;
101760886Selan free(pipes[cur].command);
101860886Selan return rval;
101960886Selan }
102060886Selan #endif /* VMS */
102160886Selan
102260886Selan #endif /* PIPES_SIMULATED */
102360886Selan
102460886Selan NODE *
do_getline(tree)102560886Selan do_getline(tree)
102660886Selan NODE *tree;
102760886Selan {
102860886Selan struct redirect *rp = NULL;
102960886Selan IOBUF *iop;
103060886Selan int cnt = EOF;
103160886Selan char *s = NULL;
103260886Selan int errcode;
103360886Selan
103460886Selan while (cnt == EOF) {
103560886Selan if (tree->rnode == NULL) { /* no redirection */
103660886Selan iop = nextfile(0);
103760886Selan if (iop == NULL) /* end of input */
103860886Selan return tmp_number((AWKNUM) 0.0);
103960886Selan } else {
104060886Selan int redir_error = 0;
104160886Selan
104260886Selan rp = redirect(tree->rnode, &redir_error);
104360886Selan if (rp == NULL && redir_error) { /* failed redirect */
104460886Selan if (! do_unix) {
104560886Selan char *s = strerror(redir_error);
104660886Selan
104760886Selan unref(ERRNO_node->var_value);
104860886Selan ERRNO_node->var_value =
104960886Selan make_string(s, strlen(s));
105060886Selan }
105160886Selan return tmp_number((AWKNUM) -1.0);
105260886Selan }
105360886Selan iop = rp->iop;
105460886Selan if (iop == NULL) /* end of input */
105560886Selan return tmp_number((AWKNUM) 0.0);
105660886Selan }
105760886Selan errcode = 0;
105860886Selan cnt = get_a_record(&s, iop, *RS, & errcode);
105960886Selan if (! do_unix && errcode != 0) {
106060886Selan char *s = strerror(errcode);
106160886Selan
106260886Selan unref(ERRNO_node->var_value);
106360886Selan ERRNO_node->var_value = make_string(s, strlen(s));
106460886Selan return tmp_number((AWKNUM) -1.0);
106560886Selan }
106660886Selan if (cnt == EOF) {
106760886Selan if (rp) {
106860886Selan /*
106960886Selan * Don't do iop_close() here if we are
107060886Selan * reading from a pipe; otherwise
107160886Selan * gawk_pclose will not be called.
107260886Selan */
107360886Selan if (!(rp->flag & RED_PIPE)) {
107460886Selan (void) iop_close(iop);
107560886Selan rp->iop = NULL;
107660886Selan }
107760886Selan rp->flag |= RED_EOF; /* sticky EOF */
107860886Selan return tmp_number((AWKNUM) 0.0);
107960886Selan } else
108060886Selan continue; /* try another file */
108160886Selan }
108260886Selan if (!rp) {
108360886Selan NR += 1;
108460886Selan FNR += 1;
108560886Selan }
108660886Selan if (tree->lnode == NULL) /* no optional var. */
108760886Selan set_record(s, cnt, 1);
108860886Selan else { /* assignment to variable */
108960886Selan Func_ptr after_assign = NULL;
109060886Selan NODE **lhs;
109160886Selan
109260886Selan lhs = get_lhs(tree->lnode, &after_assign);
109360886Selan unref(*lhs);
109460886Selan *lhs = make_string(s, strlen(s));
109560886Selan (*lhs)->flags |= MAYBE_NUM;
109660886Selan /* we may have to regenerate $0 here! */
109760886Selan if (after_assign)
109860886Selan (*after_assign)();
109960886Selan }
110060886Selan }
110160886Selan return tmp_number((AWKNUM) 1.0);
110260886Selan }
110360886Selan
110460886Selan int
pathopen(file)110560886Selan pathopen (file)
110660886Selan char *file;
110760886Selan {
110860886Selan int fd = do_pathopen(file);
110960886Selan
111060886Selan #ifdef DEFAULT_FILETYPE
111160886Selan if (! do_unix && fd <= INVALID_HANDLE) {
111260886Selan char *file_awk;
111360886Selan int save = errno;
111460886Selan #ifdef VMS
111560886Selan int vms_save = vaxc$errno;
111660886Selan #endif
111760886Selan
111860886Selan /* append ".awk" and try again */
111960886Selan emalloc(file_awk, char *, strlen(file) +
112060886Selan sizeof(DEFAULT_FILETYPE) + 1, "pathopen");
112160886Selan sprintf(file_awk, "%s%s", file, DEFAULT_FILETYPE);
112260886Selan fd = do_pathopen(file_awk);
112360886Selan free(file_awk);
112460886Selan if (fd <= INVALID_HANDLE) {
112560886Selan errno = save;
112660886Selan #ifdef VMS
112760886Selan vaxc$errno = vms_save;
112860886Selan #endif
112960886Selan }
113060886Selan }
113160886Selan #endif /*DEFAULT_FILETYPE*/
113260886Selan
113360886Selan return fd;
113460886Selan }
113560886Selan
113660886Selan static int
do_pathopen(file)113760886Selan do_pathopen (file)
113860886Selan char *file;
113960886Selan {
114060886Selan static char *savepath = DEFPATH; /* defined in config.h */
114160886Selan static int first = 1;
114260886Selan char *awkpath, *cp;
114360886Selan char trypath[BUFSIZ];
114460886Selan int fd;
114560886Selan
114660886Selan if (STREQ(file, "-"))
114760886Selan return (0);
114860886Selan
114960886Selan if (do_unix)
115060886Selan return (devopen(file, "r"));
115160886Selan
115260886Selan if (first) {
115360886Selan first = 0;
115460886Selan if ((awkpath = getenv ("AWKPATH")) != NULL && *awkpath)
115560886Selan savepath = awkpath; /* used for restarting */
115660886Selan }
115760886Selan awkpath = savepath;
115860886Selan
115960886Selan /* some kind of path name, no search */
116060886Selan #ifdef VMS /* (strchr not equal implies either or both not NULL) */
116160886Selan if (strchr(file, ':') != strchr(file, ']')
116260886Selan || strchr(file, '>') != strchr(file, '/'))
116360886Selan #else /*!VMS*/
116460886Selan #ifdef MSDOS
116560886Selan if (strchr(file, '/') != strchr(file, '\\')
116660886Selan || strchr(file, ':') != NULL)
116760886Selan #else
116860886Selan if (strchr(file, '/') != NULL)
116960886Selan #endif /*MSDOS*/
117060886Selan #endif /*VMS*/
117160886Selan return (devopen(file, "r"));
117260886Selan
117360886Selan do {
117460886Selan trypath[0] = '\0';
117560886Selan /* this should take into account limits on size of trypath */
117660886Selan for (cp = trypath; *awkpath && *awkpath != ENVSEP; )
117760886Selan *cp++ = *awkpath++;
117860886Selan
117960886Selan if (cp != trypath) { /* nun-null element in path */
118060886Selan /* add directory punctuation only if needed */
118160886Selan #ifdef VMS
118260886Selan if (strchr(":]>/", *(cp-1)) == NULL)
118360886Selan #else
118460886Selan #ifdef MSDOS
118560886Selan if (strchr(":\\/", *(cp-1)) == NULL)
118660886Selan #else
118760886Selan if (*(cp-1) != '/')
118860886Selan #endif
118960886Selan #endif
119060886Selan *cp++ = '/';
119160886Selan /* append filename */
119260886Selan strcpy (cp, file);
119360886Selan } else
119460886Selan strcpy (trypath, file);
119560886Selan if ((fd = devopen(trypath, "r")) >= 0)
119660886Selan return (fd);
119760886Selan
119860886Selan /* no luck, keep going */
119960886Selan if(*awkpath == ENVSEP && awkpath[1] != '\0')
120060886Selan awkpath++; /* skip colon */
120160886Selan } while (*awkpath);
120260886Selan /*
120360886Selan * You might have one of the awk
120460886Selan * paths defined, WITHOUT the current working directory in it.
120560886Selan * Therefore try to open the file in the current directory.
120660886Selan */
120760886Selan return (devopen(file, "r"));
120860886Selan }
1209