xref: /csrg-svn/contrib/gawk-2.15.2/io.c (revision 60888)
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