xref: /original-bsd/contrib/gawk-2.15.2/io.c (revision 8af5b582)
1 /*
2  * io.c --- routines for dealing with input and output and records
3  */
4 
5 /*
6  * Copyright (C) 1986, 1988, 1989, 1991, 1992 the Free Software Foundation, Inc.
7  *
8  * This file is part of GAWK, the GNU implementation of the
9  * AWK Progamming 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 GAWK; see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25 
26 #include <sys/param.h>
27 #include "awk.h"
28 
29 #ifndef O_RDONLY
30 #include <fcntl.h>
31 #endif
32 
33 #if !defined(S_ISDIR) && defined(S_IFDIR)
34 #define	S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
35 #endif
36 
37 #ifndef atarist
38 #define INVALID_HANDLE (-1)
39 #else
40 #define INVALID_HANDLE  (__SMALLEST_VALID_HANDLE - 1)
41 #endif
42 
43 #if defined(MSDOS) || defined(atarist)
44 #define PIPES_SIMULATED
45 #endif
46 
47 static IOBUF *nextfile P((int skipping));
48 static int inrec P((IOBUF *iop));
49 static int iop_close P((IOBUF *iop));
50 struct redirect *redirect P((NODE *tree, int *errflg));
51 static void close_one P((void));
52 static int close_redir P((struct redirect *rp));
53 #ifndef PIPES_SIMULATED
54 static int wait_any P((int interesting));
55 #endif
56 static IOBUF *gawk_popen P((char *cmd, struct redirect *rp));
57 static IOBUF *iop_open P((char *file, char *how));
58 static int gawk_pclose P((struct redirect *rp));
59 static int do_pathopen P((char *file));
60 
61 extern FILE	*fdopen();
62 extern FILE	*popen();
63 
64 static struct redirect *red_head = NULL;
65 
66 extern int output_is_tty;
67 extern NODE *ARGC_node;
68 extern NODE *ARGV_node;
69 extern NODE *ARGIND_node;
70 extern NODE *ERRNO_node;
71 extern NODE **fields_arr;
72 
73 static jmp_buf filebuf;		/* for do_nextfile() */
74 
75 /* do_nextfile --- implement gawk "next file" extension */
76 
77 void
78 do_nextfile()
79 {
80 	(void) nextfile(1);
81 	longjmp(filebuf, 1);
82 }
83 
84 static IOBUF *
85 nextfile(skipping)
86 int skipping;
87 {
88 	static int i = 1;
89 	static int files = 0;
90 	NODE *arg;
91 	int fd = INVALID_HANDLE;
92 	static IOBUF *curfile = NULL;
93 
94 	if (skipping) {
95 		if (curfile != NULL)
96 			iop_close(curfile);
97 		curfile = NULL;
98 		return NULL;
99 	}
100 	if (curfile != NULL) {
101 		if (curfile->cnt == EOF) {
102 			(void) iop_close(curfile);
103 			curfile = NULL;
104 		} else
105 			return curfile;
106 	}
107 	for (; i < (int) (ARGC_node->lnode->numbr); i++) {
108 		arg = *assoc_lookup(ARGV_node, tmp_number((AWKNUM) i));
109 		if (arg->stptr[0] == '\0')
110 			continue;
111 		arg->stptr[arg->stlen] = '\0';
112 		if (! do_unix) {
113 			ARGIND_node->var_value->numbr = i;
114 			ARGIND_node->var_value->flags = NUM|NUMBER;
115 		}
116 		if (!arg_assign(arg->stptr)) {
117 			files++;
118 			curfile = iop_open(arg->stptr, "r");
119 			if (curfile == NULL)
120 				fatal("cannot open file `%s' for reading (%s)",
121 					arg->stptr, strerror(errno));
122 				/* NOTREACHED */
123 			/* This is a kludge.  */
124 			unref(FILENAME_node->var_value);
125 			FILENAME_node->var_value =
126 				dupnode(arg);
127 			FNR = 0;
128 			i++;
129 			break;
130 		}
131 	}
132 	if (files == 0) {
133 		files++;
134 		/* no args. -- use stdin */
135 		/* FILENAME is init'ed to "-" */
136 		/* FNR is init'ed to 0 */
137 		curfile = iop_alloc(fileno(stdin));
138 	}
139 	return curfile;
140 }
141 
142 void
143 set_FNR()
144 {
145 	FNR = (int) FNR_node->var_value->numbr;
146 }
147 
148 void
149 set_NR()
150 {
151 	NR = (int) NR_node->var_value->numbr;
152 }
153 
154 /*
155  * This reads in a record from the input file
156  */
157 static int
158 inrec(iop)
159 IOBUF *iop;
160 {
161 	char *begin;
162 	register int cnt;
163 	int retval = 0;
164 
165 	cnt = get_a_record(&begin, iop, *RS, NULL);
166 	if (cnt == EOF) {
167 		cnt = 0;
168 		retval = 1;
169 	} else {
170 		    NR += 1;
171 		    FNR += 1;
172 	}
173 	set_record(begin, cnt, 1);
174 
175 	return retval;
176 }
177 
178 static int
179 iop_close(iop)
180 IOBUF *iop;
181 {
182 	int ret;
183 
184 	if (iop == NULL)
185 		return 0;
186 	errno = 0;
187 
188 #ifdef _CRAY
189 	/* Work around bug in UNICOS popen */
190 	if (iop->fd < 3)
191 		ret = 0;
192 	else
193 #endif
194 	/* save these for re-use; don't free the storage */
195 	if ((iop->flag & IOP_IS_INTERNAL) != 0) {
196 		iop->off = iop->buf;
197 		iop->end = iop->buf + strlen(iop->buf);
198 		iop->cnt = 0;
199 		iop->secsiz = 0;
200 		return 0;
201 	}
202 
203 	/* Don't close standard files or else crufty code elsewhere will lose */
204 	if (iop->fd == fileno(stdin) ||
205 	    iop->fd == fileno(stdout) ||
206 	    iop->fd == fileno(stderr))
207 		ret = 0;
208 	else
209 		ret = close(iop->fd);
210 	if (ret == -1)
211 		warning("close of fd %d failed (%s)", iop->fd, strerror(errno));
212 	if ((iop->flag & IOP_NO_FREE) == 0) {
213 		/*
214 		 * be careful -- $0 may still reference the buffer even though
215 		 * an explicit close is being done; in the future, maybe we
216 		 * can do this a bit better
217 		 */
218 		if (iop->buf) {
219 			if ((fields_arr[0]->stptr >= iop->buf)
220 			    && (fields_arr[0]->stptr < iop->end)) {
221 				NODE *t;
222 
223 				t = make_string(fields_arr[0]->stptr,
224 						fields_arr[0]->stlen);
225 				unref(fields_arr[0]);
226 				fields_arr [0] = t;
227 				reset_record ();
228 			}
229   			free(iop->buf);
230 		}
231 		free((char *)iop);
232 	}
233 	return ret == -1 ? 1 : 0;
234 }
235 
236 void
237 do_input()
238 {
239 	IOBUF *iop;
240 	extern int exiting;
241 
242 	if (setjmp(filebuf) != 0) {
243 	}
244 	while ((iop = nextfile(0)) != NULL) {
245 		if (inrec(iop) == 0)
246 			while (interpret(expression_value) && inrec(iop) == 0)
247 				;
248 		if (exiting)
249 			break;
250 	}
251 }
252 
253 /* Redirection for printf and print commands */
254 struct redirect *
255 redirect(tree, errflg)
256 NODE *tree;
257 int *errflg;
258 {
259 	register NODE *tmp;
260 	register struct redirect *rp;
261 	register char *str;
262 	int tflag = 0;
263 	int outflag = 0;
264 	char *direction = "to";
265 	char *mode;
266 	int fd;
267 	char *what = NULL;
268 
269 	switch (tree->type) {
270 	case Node_redirect_append:
271 		tflag = RED_APPEND;
272 		/* FALL THROUGH */
273 	case Node_redirect_output:
274 		outflag = (RED_FILE|RED_WRITE);
275 		tflag |= outflag;
276 		if (tree->type == Node_redirect_output)
277 			what = ">";
278 		else
279 			what = ">>";
280 		break;
281 	case Node_redirect_pipe:
282 		tflag = (RED_PIPE|RED_WRITE);
283 		what = "|";
284 		break;
285 	case Node_redirect_pipein:
286 		tflag = (RED_PIPE|RED_READ);
287 		what = "|";
288 		break;
289 	case Node_redirect_input:
290 		tflag = (RED_FILE|RED_READ);
291 		what = "<";
292 		break;
293 	default:
294 		fatal ("invalid tree type %d in redirect()", tree->type);
295 		break;
296 	}
297 	tmp = tree_eval(tree->subnode);
298 	if (do_lint && ! (tmp->flags & STR))
299 		warning("expression in `%s' redirection only has numeric value",
300 			what);
301 	tmp = force_string(tmp);
302 	str = tmp->stptr;
303 	if (str == NULL || *str == '\0')
304 		fatal("expression for `%s' redirection has null string value",
305 			what);
306 	if (do_lint
307 	    && (STREQN(str, "0", tmp->stlen) || STREQN(str, "1", tmp->stlen)))
308 		warning("filename `%s' for `%s' redirection may be result of logical expression", str, what);
309 	for (rp = red_head; rp != NULL; rp = rp->next)
310 		if (strlen(rp->value) == tmp->stlen
311 		    && STREQN(rp->value, str, tmp->stlen)
312 		    && ((rp->flag & ~(RED_NOBUF|RED_EOF)) == tflag
313 			|| (outflag
314 			    && (rp->flag & (RED_FILE|RED_WRITE)) == outflag)))
315 			break;
316 	if (rp == NULL) {
317 		emalloc(rp, struct redirect *, sizeof(struct redirect),
318 			"redirect");
319 		emalloc(str, char *, tmp->stlen+1, "redirect");
320 		memcpy(str, tmp->stptr, tmp->stlen);
321 		str[tmp->stlen] = '\0';
322 		rp->value = str;
323 		rp->flag = tflag;
324 		rp->fp = NULL;
325 		rp->iop = NULL;
326 		rp->pid = 0;	/* unlikely that we're worried about init */
327 		rp->status = 0;
328 		/* maintain list in most-recently-used first order */
329 		if (red_head)
330 			red_head->prev = rp;
331 		rp->prev = NULL;
332 		rp->next = red_head;
333 		red_head = rp;
334 	}
335 	while (rp->fp == NULL && rp->iop == NULL) {
336 		if (rp->flag & RED_EOF)
337 			/* encountered EOF on file or pipe -- must be cleared
338 			 * by explicit close() before reading more
339 			 */
340 			return rp;
341 		mode = NULL;
342 		errno = 0;
343 		switch (tree->type) {
344 		case Node_redirect_output:
345 			mode = "w";
346 			if (rp->flag & RED_USED)
347 				mode = "a";
348 			break;
349 		case Node_redirect_append:
350 			mode = "a";
351 			break;
352 		case Node_redirect_pipe:
353 			if ((rp->fp = popen(str, "w")) == NULL)
354 				fatal("can't open pipe (\"%s\") for output (%s)",
355 					str, strerror(errno));
356 			rp->flag |= RED_NOBUF;
357 			break;
358 		case Node_redirect_pipein:
359 			direction = "from";
360 			if (gawk_popen(str, rp) == NULL)
361 				fatal("can't open pipe (\"%s\") for input (%s)",
362 					str, strerror(errno));
363 			break;
364 		case Node_redirect_input:
365 			direction = "from";
366 			rp->iop = iop_open(str, "r");
367 			break;
368 		default:
369 			cant_happen();
370 		}
371 		if (mode != NULL) {
372 			fd = devopen(str, mode);
373 			if (fd > INVALID_HANDLE) {
374 				if (fd == fileno(stdin))
375 					rp->fp = stdin;
376 				else if (fd == fileno(stdout))
377 					rp->fp = stdout;
378 				else if (fd == fileno(stderr))
379 					rp->fp = stderr;
380 				else
381 					rp->fp = fdopen(fd, mode);
382 				if (isatty(fd))
383 					rp->flag |= RED_NOBUF;
384 			}
385 		}
386 		if (rp->fp == NULL && rp->iop == NULL) {
387 			/* too many files open -- close one and try again */
388 			if (errno == EMFILE)
389 				close_one();
390 			else {
391 				/*
392 				 * Some other reason for failure.
393 				 *
394 				 * On redirection of input from a file,
395 				 * just return an error, so e.g. getline
396 				 * can return -1.  For output to file,
397 				 * complain. The shell will complain on
398 				 * a bad command to a pipe.
399 				 */
400 				*errflg = errno;
401 				if (tree->type == Node_redirect_output
402 				    || tree->type == Node_redirect_append)
403 					fatal("can't redirect %s `%s' (%s)",
404 					    direction, str, strerror(errno));
405 				else {
406 					free_temp(tmp);
407 					return NULL;
408 				}
409 			}
410 		}
411 	}
412 	free_temp(tmp);
413 	return rp;
414 }
415 
416 static void
417 close_one()
418 {
419 	register struct redirect *rp;
420 	register struct redirect *rplast = NULL;
421 
422 	/* go to end of list first, to pick up least recently used entry */
423 	for (rp = red_head; rp != NULL; rp = rp->next)
424 		rplast = rp;
425 	/* now work back up through the list */
426 	for (rp = rplast; rp != NULL; rp = rp->prev)
427 		if (rp->fp && (rp->flag & RED_FILE)) {
428 			rp->flag |= RED_USED;
429 			errno = 0;
430 			if (fclose(rp->fp))
431 				warning("close of \"%s\" failed (%s).",
432 					rp->value, strerror(errno));
433 			rp->fp = NULL;
434 			break;
435 		}
436 	if (rp == NULL)
437 		/* surely this is the only reason ??? */
438 		fatal("too many pipes or input files open");
439 }
440 
441 NODE *
442 do_close(tree)
443 NODE *tree;
444 {
445 	NODE *tmp;
446 	register struct redirect *rp;
447 
448 	tmp = force_string(tree_eval(tree->subnode));
449 	for (rp = red_head; rp != NULL; rp = rp->next) {
450 		if (strlen(rp->value) == tmp->stlen
451 		    && STREQN(rp->value, tmp->stptr, tmp->stlen))
452 			break;
453 	}
454 	free_temp(tmp);
455 	if (rp == NULL) /* no match */
456 		return tmp_number((AWKNUM) 0.0);
457 	fflush(stdout);	/* synchronize regular output */
458 	tmp = tmp_number((AWKNUM)close_redir(rp));
459 	rp = NULL;
460 	return tmp;
461 }
462 
463 static int
464 close_redir(rp)
465 register struct redirect *rp;
466 {
467 	int status = 0;
468 
469 	if (rp == NULL)
470 		return 0;
471 	if (rp->fp == stdout || rp->fp == stderr)
472 		return 0;
473 	errno = 0;
474 	if ((rp->flag & (RED_PIPE|RED_WRITE)) == (RED_PIPE|RED_WRITE))
475 		status = pclose(rp->fp);
476 	else if (rp->fp)
477 		status = fclose(rp->fp);
478 	else if (rp->iop) {
479 		if (rp->flag & RED_PIPE)
480 			status = gawk_pclose(rp);
481 		else {
482 			status = iop_close(rp->iop);
483 			rp->iop = NULL;
484 		}
485 	}
486 	/* SVR4 awk checks and warns about status of close */
487 	if (status) {
488 		char *s = strerror(errno);
489 
490 		warning("failure status (%d) on %s close of \"%s\" (%s).",
491 			status,
492 			(rp->flag & RED_PIPE) ? "pipe" :
493 			"file", rp->value, s);
494 
495 		if (! do_unix) {
496 			/* set ERRNO too so that program can get at it */
497 			unref(ERRNO_node->var_value);
498 			ERRNO_node->var_value = make_string(s, strlen(s));
499 		}
500 	}
501 	if (rp->next)
502 		rp->next->prev = rp->prev;
503 	if (rp->prev)
504 		rp->prev->next = rp->next;
505 	else
506 		red_head = rp->next;
507 	free(rp->value);
508 	free((char *)rp);
509 	return status;
510 }
511 
512 int
513 flush_io ()
514 {
515 	register struct redirect *rp;
516 	int status = 0;
517 
518 	errno = 0;
519 	if (fflush(stdout)) {
520 		warning("error writing standard output (%s).", strerror(errno));
521 		status++;
522 	}
523 	if (fflush(stderr)) {
524 		warning("error writing standard error (%s).", strerror(errno));
525 		status++;
526 	}
527 	for (rp = red_head; rp != NULL; rp = rp->next)
528 		/* flush both files and pipes, what the heck */
529 		if ((rp->flag & RED_WRITE) && rp->fp != NULL) {
530 			if (fflush(rp->fp)) {
531 				warning("%s flush of \"%s\" failed (%s).",
532 				    (rp->flag  & RED_PIPE) ? "pipe" :
533 				    "file", rp->value, strerror(errno));
534 				status++;
535 			}
536 		}
537 	return status;
538 }
539 
540 int
541 close_io ()
542 {
543 	register struct redirect *rp;
544 	register struct redirect *next;
545 	int status = 0;
546 
547 	errno = 0;
548 	if (fclose(stdout)) {
549 		warning("error writing standard output (%s).", strerror(errno));
550 		status++;
551 	}
552 	if (fclose(stderr)) {
553 		warning("error writing standard error (%s).", strerror(errno));
554 		status++;
555 	}
556 	for (rp = red_head; rp != NULL; rp = next) {
557 		next = rp->next;
558 		if (close_redir(rp))
559 			status++;
560 		rp = NULL;
561 	}
562 	return status;
563 }
564 
565 /* str2mode --- convert a string mode to an integer mode */
566 
567 static int
568 str2mode(mode)
569 char *mode;
570 {
571 	int ret;
572 
573 	switch(mode[0]) {
574 	case 'r':
575 		ret = O_RDONLY;
576 		break;
577 
578 	case 'w':
579 		ret = O_WRONLY|O_CREAT|O_TRUNC;
580 		break;
581 
582 	case 'a':
583 		ret = O_WRONLY|O_APPEND|O_CREAT;
584 		break;
585 	default:
586 		cant_happen();
587 	}
588 	return ret;
589 }
590 
591 /* devopen --- handle /dev/std{in,out,err}, /dev/fd/N, regular files */
592 
593 /*
594  * This separate version is still needed for output, since file and pipe
595  * output is done with stdio. iop_open() handles input with IOBUFs of
596  * more "special" files.  Those files are not handled here since it makes
597  * no sense to use them for output.
598  */
599 
600 int
601 devopen(name, mode)
602 char *name, *mode;
603 {
604 	int openfd = INVALID_HANDLE;
605 	char *cp, *ptr;
606 	int flag = 0;
607 	struct stat buf;
608 	extern double strtod();
609 
610 	flag = str2mode(mode);
611 
612 	if (do_unix)
613 		goto strictopen;
614 
615 #ifdef VMS
616 	if ((openfd = vms_devopen(name, flag)) >= 0)
617 		return openfd;
618 #endif /* VMS */
619 
620 	if (STREQ(name, "-"))
621 		openfd = fileno(stdin);
622 	else if (STREQN(name, "/dev/", 5) && stat(name, &buf) == -1) {
623 		cp = name + 5;
624 
625 		if (STREQ(cp, "stdin") && (flag & O_RDONLY) == O_RDONLY)
626 			openfd = fileno(stdin);
627 		else if (STREQ(cp, "stdout") && (flag & O_WRONLY) == O_WRONLY)
628 			openfd = fileno(stdout);
629 		else if (STREQ(cp, "stderr") && (flag & O_WRONLY) == O_WRONLY)
630 			openfd = fileno(stderr);
631 		else if (STREQN(cp, "fd/", 3)) {
632 			cp += 3;
633 			openfd = (int)strtod(cp, &ptr);
634 			if (openfd <= INVALID_HANDLE || ptr == cp)
635 				openfd = INVALID_HANDLE;
636 		}
637 	}
638 
639 strictopen:
640 	if (openfd == INVALID_HANDLE)
641 		openfd = open(name, flag, 0666);
642 	if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0)
643 		if (S_ISDIR(buf.st_mode))
644 			fatal("file `%s' is a directory", name);
645 	return openfd;
646 }
647 
648 
649 /* spec_setup --- setup an IOBUF for a special internal file */
650 
651 void
652 spec_setup(iop, len, allocate)
653 IOBUF *iop;
654 int len;
655 int allocate;
656 {
657 	char *cp;
658 
659 	if (allocate) {
660 		emalloc(cp, char *, len+2, "spec_setup");
661 		iop->buf = cp;
662 	} else {
663 		len = strlen(iop->buf);
664 		iop->buf[len++] = '\n';	/* get_a_record clobbered it */
665 		iop->buf[len] = '\0';	/* just in case */
666 	}
667 	iop->off = iop->buf;
668 	iop->cnt = 0;
669 	iop->secsiz = 0;
670 	iop->size = len;
671 	iop->end = iop->buf + len;
672 	iop->fd = -1;
673 	iop->flag = IOP_IS_INTERNAL;
674 }
675 
676 /* specfdopen --- open a fd special file */
677 
678 int
679 specfdopen(iop, name, mode)
680 IOBUF *iop;
681 char *name, *mode;
682 {
683 	int fd;
684 	IOBUF *tp;
685 
686 	fd = devopen(name, mode);
687 	if (fd == INVALID_HANDLE)
688 		return INVALID_HANDLE;
689 	tp = iop_alloc(fd);
690 	if (tp == NULL)
691 		return INVALID_HANDLE;
692 	*iop = *tp;
693 	iop->flag |= IOP_NO_FREE;
694 	free(tp);
695 	return 0;
696 }
697 
698 /* pidopen --- "open" /dev/pid, /dev/ppid, and /dev/pgrpid */
699 
700 int
701 pidopen(iop, name, mode)
702 IOBUF *iop;
703 char *name, *mode;
704 {
705 	char tbuf[BUFSIZ];
706 	int i;
707 
708 	if (name[6] == 'g')
709 /* following #if will improve in 2.16 */
710 #if defined(__svr4__) || defined(i860) || defined(_AIX) || defined(BSD4_4)
711 		sprintf(tbuf, "%d\n", getpgrp());
712 #else
713 		sprintf(tbuf, "%d\n", getpgrp(getpid()));
714 #endif
715 	else if (name[6] == 'i')
716 		sprintf(tbuf, "%d\n", getpid());
717 	else
718 		sprintf(tbuf, "%d\n", getppid());
719 	i = strlen(tbuf);
720 	spec_setup(iop, i, 1);
721 	strcpy(iop->buf, tbuf);
722 	return 0;
723 }
724 
725 /* useropen --- "open" /dev/user */
726 
727 /*
728  * /dev/user creates a record as follows:
729  *	$1 = getuid()
730  *	$2 = geteuid()
731  *	$3 = getgid()
732  *	$4 = getegid()
733  * If multiple groups are supported, the $5 through $NF are the
734  * supplementary group set.
735  */
736 
737 int
738 useropen(iop, name, mode)
739 IOBUF *iop;
740 char *name, *mode;
741 {
742 	char tbuf[BUFSIZ], *cp;
743 	int i;
744 #if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
745 	int groupset[NGROUPS_MAX];
746 	int ngroups;
747 #endif
748 
749 	sprintf(tbuf, "%d %d %d %d", getuid(), geteuid(), getgid(), getegid());
750 
751 	cp = tbuf + strlen(tbuf);
752 #if defined(NGROUPS_MAX) && NGROUPS_MAX > 0
753 	ngroups = getgroups(NGROUPS_MAX, groupset);
754 	if (ngroups == -1)
755 		fatal("could not find groups: %s", strerror(errno));
756 
757 	for (i = 0; i < ngroups; i++) {
758 		*cp++ = ' ';
759 		sprintf(cp, "%d", groupset[i]);
760 		cp += strlen(cp);
761 	}
762 #endif
763 	*cp++ = '\n';
764 	*cp++ = '\0';
765 
766 
767 	i = strlen(tbuf);
768 	spec_setup(iop, i, 1);
769 	strcpy(iop->buf, tbuf);
770 	return 0;
771 }
772 
773 /* iop_open --- handle special and regular files for input */
774 
775 static IOBUF *
776 iop_open(name, mode)
777 char *name, *mode;
778 {
779 	int openfd = INVALID_HANDLE;
780 	char *cp, *ptr;
781 	int flag = 0;
782 	int i;
783 	struct stat buf;
784 	IOBUF *iop;
785 	static struct internal {
786 		char *name;
787 		int compare;
788 		int (*fp)();
789 		IOBUF iob;
790 	} table[] = {
791 		{ "/dev/fd/",		8,	specfdopen },
792 		{ "/dev/stdin",		10,	specfdopen },
793 		{ "/dev/stdout",	11,	specfdopen },
794 		{ "/dev/stderr",	11,	specfdopen },
795 		{ "/dev/pid",		8,	pidopen },
796 		{ "/dev/ppid",		9,	pidopen },
797 		{ "/dev/pgrpid",	11,	pidopen },
798 		{ "/dev/user",		9,	useropen },
799 	};
800 	int devcount = sizeof(table) / sizeof(table[0]);
801 
802 	flag = str2mode(mode);
803 
804 	if (do_unix)
805 		goto strictopen;
806 
807 	if (STREQ(name, "-"))
808 		openfd = fileno(stdin);
809 	else if (STREQN(name, "/dev/", 5) && stat(name, &buf) == -1) {
810 		int i;
811 
812 		for (i = 0; i < devcount; i++) {
813 			if (STREQN(name, table[i].name, table[i].compare)) {
814 				IOBUF *iop = & table[i].iob;
815 
816 				if (iop->buf != NULL) {
817 					spec_setup(iop, 0, 0);
818 					return iop;
819 				} else if ((*table[i].fp)(iop, name, mode) == 0)
820 					return iop;
821 				else {
822 					warning("could not open %s, mode `%s'",
823 						name, mode);
824 					return NULL;
825 				}
826 			}
827 		}
828 	}
829 
830 strictopen:
831 	if (openfd == INVALID_HANDLE)
832 		openfd = open(name, flag, 0666);
833 	if (openfd != INVALID_HANDLE && fstat(openfd, &buf) > 0)
834 		if ((buf.st_mode & S_IFMT) == S_IFDIR)
835 			fatal("file `%s' is a directory", name);
836 	iop = iop_alloc(openfd);
837 	return iop;
838 }
839 
840 #ifndef PIPES_SIMULATED
841 	/* real pipes */
842 static int
843 wait_any(interesting)
844 int interesting;	/* pid of interest, if any */
845 {
846 	SIGTYPE (*hstat)(), (*istat)(), (*qstat)();
847 	int pid;
848 	int status = 0;
849 	struct redirect *redp;
850 	extern int errno;
851 
852 	hstat = signal(SIGHUP, SIG_IGN);
853 	istat = signal(SIGINT, SIG_IGN);
854 	qstat = signal(SIGQUIT, SIG_IGN);
855 	for (;;) {
856 #ifdef NeXT
857 		pid = wait((union wait *)&status);
858 #else
859 		pid = wait(&status);
860 #endif /* NeXT */
861 		if (interesting && pid == interesting) {
862 			break;
863 		} else if (pid != -1) {
864 			for (redp = red_head; redp != NULL; redp = redp->next)
865 				if (pid == redp->pid) {
866 					redp->pid = -1;
867 					redp->status = status;
868 					if (redp->fp) {
869 						pclose(redp->fp);
870 						redp->fp = 0;
871 					}
872 					if (redp->iop) {
873 						(void) iop_close(redp->iop);
874 						redp->iop = 0;
875 					}
876 					break;
877 				}
878 		}
879 		if (pid == -1 && errno == ECHILD)
880 			break;
881 	}
882 	signal(SIGHUP, hstat);
883 	signal(SIGINT, istat);
884 	signal(SIGQUIT, qstat);
885 	return(status);
886 }
887 
888 static IOBUF *
889 gawk_popen(cmd, rp)
890 char *cmd;
891 struct redirect *rp;
892 {
893 	int p[2];
894 	register int pid;
895 
896 	/* used to wait for any children to synchronize input and output,
897 	 * but this could cause gawk to hang when it is started in a pipeline
898 	 * and thus has a child process feeding it input (shell dependant)
899 	 */
900 	/*(void) wait_any(0);*/	/* wait for outstanding processes */
901 
902 	if (pipe(p) < 0)
903 		fatal("cannot open pipe \"%s\" (%s)", cmd, strerror(errno));
904 	if ((pid = fork()) == 0) {
905 		if (close(1) == -1)
906 			fatal("close of stdout in child failed (%s)",
907 				strerror(errno));
908 		if (dup(p[1]) != 1)
909 			fatal("dup of pipe failed (%s)", strerror(errno));
910 		if (close(p[0]) == -1 || close(p[1]) == -1)
911 			fatal("close of pipe failed (%s)", strerror(errno));
912 		if (close(0) == -1)
913 			fatal("close of stdin in child failed (%s)",
914 				strerror(errno));
915 		execl("/bin/sh", "sh", "-c", cmd, 0);
916 		_exit(127);
917 	}
918 	if (pid == -1)
919 		fatal("cannot fork for \"%s\" (%s)", cmd, strerror(errno));
920 	rp->pid = pid;
921 	if (close(p[1]) == -1)
922 		fatal("close of pipe failed (%s)", strerror(errno));
923 	return (rp->iop = iop_alloc(p[0]));
924 }
925 
926 static int
927 gawk_pclose(rp)
928 struct redirect *rp;
929 {
930 	(void) iop_close(rp->iop);
931 	rp->iop = NULL;
932 
933 	/* process previously found, return stored status */
934 	if (rp->pid == -1)
935 		return (rp->status >> 8) & 0xFF;
936 	rp->status = wait_any(rp->pid);
937 	rp->pid = -1;
938 	return (rp->status >> 8) & 0xFF;
939 }
940 
941 #else	/* PIPES_SIMULATED */
942 	/* use temporary file rather than pipe */
943 
944 #ifdef VMS
945 static IOBUF *
946 gawk_popen(cmd, rp)
947 char *cmd;
948 struct redirect *rp;
949 {
950 	FILE *current;
951 
952 	if ((current = popen(cmd, "r")) == NULL)
953 		return NULL;
954 	return (rp->iop = iop_alloc(fileno(current)));
955 }
956 
957 static int
958 gawk_pclose(rp)
959 struct redirect *rp;
960 {
961 	int rval, aval, fd = rp->iop->fd;
962 	FILE *kludge = fdopen(fd, "r"); /* pclose needs FILE* w/ right fileno */
963 
964 	rp->iop->fd = dup(fd);	  /* kludge to allow close() + pclose() */
965 	rval = iop_close(rp->iop);
966 	rp->iop = NULL;
967 	aval = pclose(kludge);
968 	return (rval < 0 ? rval : aval);
969 }
970 #else	/* VMS */
971 
972 static
973 struct {
974 	char *command;
975 	char *name;
976 } pipes[_NFILE];
977 
978 static IOBUF *
979 gawk_popen(cmd, rp)
980 char *cmd;
981 struct redirect *rp;
982 {
983 	extern char *strdup(const char *);
984 	int current;
985 	char *name;
986 	static char cmdbuf[256];
987 
988 	/* get a name to use.  */
989 	if ((name = tempnam(".", "pip")) == NULL)
990 		return NULL;
991 	sprintf(cmdbuf,"%s > %s", cmd, name);
992 	system(cmdbuf);
993 	if ((current = open(name,O_RDONLY)) == INVALID_HANDLE)
994 		return NULL;
995 	pipes[current].name = name;
996 	pipes[current].command = strdup(cmd);
997 	rp->iop = iop_alloc(current);
998 	return (rp->iop = iop_alloc(current));
999 }
1000 
1001 static int
1002 gawk_pclose(rp)
1003 struct redirect *rp;
1004 {
1005 	int cur = rp->iop->fd;
1006 	int rval;
1007 
1008 	rval = iop_close(rp->iop);
1009 	rp->iop = NULL;
1010 
1011 	/* check for an open file  */
1012 	if (pipes[cur].name == NULL)
1013 		return -1;
1014 	unlink(pipes[cur].name);
1015 	free(pipes[cur].name);
1016 	pipes[cur].name = NULL;
1017 	free(pipes[cur].command);
1018 	return rval;
1019 }
1020 #endif	/* VMS */
1021 
1022 #endif	/* PIPES_SIMULATED */
1023 
1024 NODE *
1025 do_getline(tree)
1026 NODE *tree;
1027 {
1028 	struct redirect *rp = NULL;
1029 	IOBUF *iop;
1030 	int cnt = EOF;
1031 	char *s = NULL;
1032 	int errcode;
1033 
1034 	while (cnt == EOF) {
1035 		if (tree->rnode == NULL) {	 /* no redirection */
1036 			iop = nextfile(0);
1037 			if (iop == NULL)		/* end of input */
1038 				return tmp_number((AWKNUM) 0.0);
1039 		} else {
1040 			int redir_error = 0;
1041 
1042 			rp = redirect(tree->rnode, &redir_error);
1043 			if (rp == NULL && redir_error) { /* failed redirect */
1044 				if (! do_unix) {
1045 					char *s = strerror(redir_error);
1046 
1047 					unref(ERRNO_node->var_value);
1048 					ERRNO_node->var_value =
1049 						make_string(s, strlen(s));
1050 				}
1051 				return tmp_number((AWKNUM) -1.0);
1052 			}
1053 			iop = rp->iop;
1054 			if (iop == NULL)		/* end of input */
1055 				return tmp_number((AWKNUM) 0.0);
1056 		}
1057 		errcode = 0;
1058 		cnt = get_a_record(&s, iop, *RS, & errcode);
1059 		if (! do_unix && errcode != 0) {
1060 			char *s = strerror(errcode);
1061 
1062 			unref(ERRNO_node->var_value);
1063 			ERRNO_node->var_value = make_string(s, strlen(s));
1064 			return tmp_number((AWKNUM) -1.0);
1065 		}
1066 		if (cnt == EOF) {
1067 			if (rp) {
1068 				/*
1069 				 * Don't do iop_close() here if we are
1070 				 * reading from a pipe; otherwise
1071 				 * gawk_pclose will not be called.
1072 				 */
1073 				if (!(rp->flag & RED_PIPE)) {
1074 					(void) iop_close(iop);
1075 					rp->iop = NULL;
1076 				}
1077 				rp->flag |= RED_EOF;	/* sticky EOF */
1078 				return tmp_number((AWKNUM) 0.0);
1079 			} else
1080 				continue;	/* try another file */
1081 		}
1082 		if (!rp) {
1083 			NR += 1;
1084 			FNR += 1;
1085 		}
1086 		if (tree->lnode == NULL)	/* no optional var. */
1087 			set_record(s, cnt, 1);
1088 		else {			/* assignment to variable */
1089 			Func_ptr after_assign = NULL;
1090 			NODE **lhs;
1091 
1092 			lhs = get_lhs(tree->lnode, &after_assign);
1093 			unref(*lhs);
1094 			*lhs = make_string(s, strlen(s));
1095 			(*lhs)->flags |= MAYBE_NUM;
1096 			/* we may have to regenerate $0 here! */
1097 			if (after_assign)
1098 				(*after_assign)();
1099 		}
1100 	}
1101 	return tmp_number((AWKNUM) 1.0);
1102 }
1103 
1104 int
1105 pathopen (file)
1106 char *file;
1107 {
1108 	int fd = do_pathopen(file);
1109 
1110 #ifdef DEFAULT_FILETYPE
1111 	if (! do_unix && fd <= INVALID_HANDLE) {
1112 		char *file_awk;
1113 		int save = errno;
1114 #ifdef VMS
1115 		int vms_save = vaxc$errno;
1116 #endif
1117 
1118 		/* append ".awk" and try again */
1119 		emalloc(file_awk, char *, strlen(file) +
1120 			sizeof(DEFAULT_FILETYPE) + 1, "pathopen");
1121 		sprintf(file_awk, "%s%s", file, DEFAULT_FILETYPE);
1122 		fd = do_pathopen(file_awk);
1123 		free(file_awk);
1124 		if (fd <= INVALID_HANDLE) {
1125 			errno = save;
1126 #ifdef VMS
1127 			vaxc$errno = vms_save;
1128 #endif
1129 		}
1130 	}
1131 #endif	/*DEFAULT_FILETYPE*/
1132 
1133 	return fd;
1134 }
1135 
1136 static int
1137 do_pathopen (file)
1138 char *file;
1139 {
1140 	static char *savepath = DEFPATH;	/* defined in config.h */
1141 	static int first = 1;
1142 	char *awkpath, *cp;
1143 	char trypath[BUFSIZ];
1144 	int fd;
1145 
1146 	if (STREQ(file, "-"))
1147 		return (0);
1148 
1149 	if (do_unix)
1150 		return (devopen(file, "r"));
1151 
1152 	if (first) {
1153 		first = 0;
1154 		if ((awkpath = getenv ("AWKPATH")) != NULL && *awkpath)
1155 			savepath = awkpath;	/* used for restarting */
1156 	}
1157 	awkpath = savepath;
1158 
1159 	/* some kind of path name, no search */
1160 #ifdef VMS	/* (strchr not equal implies either or both not NULL) */
1161 	if (strchr(file, ':') != strchr(file, ']')
1162 	 || strchr(file, '>') != strchr(file, '/'))
1163 #else /*!VMS*/
1164 #ifdef MSDOS
1165 	if (strchr(file, '/') != strchr(file, '\\')
1166 	 || strchr(file, ':') != NULL)
1167 #else
1168 	if (strchr(file, '/') != NULL)
1169 #endif	/*MSDOS*/
1170 #endif	/*VMS*/
1171 		return (devopen(file, "r"));
1172 
1173 	do {
1174 		trypath[0] = '\0';
1175 		/* this should take into account limits on size of trypath */
1176 		for (cp = trypath; *awkpath && *awkpath != ENVSEP; )
1177 			*cp++ = *awkpath++;
1178 
1179 		if (cp != trypath) {	/* nun-null element in path */
1180 			/* add directory punctuation only if needed */
1181 #ifdef VMS
1182 			if (strchr(":]>/", *(cp-1)) == NULL)
1183 #else
1184 #ifdef MSDOS
1185 			if (strchr(":\\/", *(cp-1)) == NULL)
1186 #else
1187 			if (*(cp-1) != '/')
1188 #endif
1189 #endif
1190 				*cp++ = '/';
1191 			/* append filename */
1192 			strcpy (cp, file);
1193 		} else
1194 			strcpy (trypath, file);
1195 		if ((fd = devopen(trypath, "r")) >= 0)
1196 			return (fd);
1197 
1198 		/* no luck, keep going */
1199 		if(*awkpath == ENVSEP && awkpath[1] != '\0')
1200 			awkpath++;	/* skip colon */
1201 	} while (*awkpath);
1202 	/*
1203 	 * You might have one of the awk
1204 	 * paths defined, WITHOUT the current working directory in it.
1205 	 * Therefore try to open the file in the current directory.
1206 	 */
1207 	return (devopen(file, "r"));
1208 }
1209