input.c revision 36150
11556Srgrimes/*-
21556Srgrimes * Copyright (c) 1991, 1993
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * This code is derived from software contributed to Berkeley by
61556Srgrimes * Kenneth Almquist.
71556Srgrimes *
81556Srgrimes * Redistribution and use in source and binary forms, with or without
91556Srgrimes * modification, are permitted provided that the following conditions
101556Srgrimes * are met:
111556Srgrimes * 1. Redistributions of source code must retain the above copyright
121556Srgrimes *    notice, this list of conditions and the following disclaimer.
131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141556Srgrimes *    notice, this list of conditions and the following disclaimer in the
151556Srgrimes *    documentation and/or other materials provided with the distribution.
161556Srgrimes * 3. All advertising materials mentioning features or use of this software
171556Srgrimes *    must display the following acknowledgement:
181556Srgrimes *	This product includes software developed by the University of
191556Srgrimes *	California, Berkeley and its contributors.
201556Srgrimes * 4. Neither the name of the University nor the names of its contributors
211556Srgrimes *    may be used to endorse or promote products derived from this software
221556Srgrimes *    without specific prior written permission.
231556Srgrimes *
241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341556Srgrimes * SUCH DAMAGE.
351556Srgrimes */
361556Srgrimes
371556Srgrimes#ifndef lint
3836150Scharnier#if 0
3936150Scharnierstatic char sccsid[] = "@(#)input.c	8.3 (Berkeley) 6/9/95";
4036150Scharnier#endif
4136150Scharnierstatic const char rcsid[] =
4236150Scharnier	"$Id$";
431556Srgrimes#endif /* not lint */
441556Srgrimes
4517987Speter#include <stdio.h>	/* defines BUFSIZ */
4617987Speter#include <fcntl.h>
4717987Speter#include <errno.h>
4817987Speter#include <unistd.h>
4917987Speter#include <stdlib.h>
5017987Speter#include <string.h>
5117987Speter
521556Srgrimes/*
531556Srgrimes * This file implements the input routines used by the parser.
541556Srgrimes */
551556Srgrimes
561556Srgrimes#include "shell.h"
5717987Speter#include "redir.h"
581556Srgrimes#include "syntax.h"
591556Srgrimes#include "input.h"
601556Srgrimes#include "output.h"
611556Srgrimes#include "options.h"
621556Srgrimes#include "memalloc.h"
631556Srgrimes#include "error.h"
641556Srgrimes#include "alias.h"
651556Srgrimes#include "parser.h"
661556Srgrimes#include "myhistedit.h"
671556Srgrimes
681556Srgrimes#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
691556Srgrimes
701556SrgrimesMKINIT
711556Srgrimesstruct strpush {
721556Srgrimes	struct strpush *prev;	/* preceding string on stack */
731556Srgrimes	char *prevstring;
741556Srgrimes	int prevnleft;
7512043Speter	int prevlleft;
761556Srgrimes	struct alias *ap;	/* if push was associated with an alias */
771556Srgrimes};
781556Srgrimes
791556Srgrimes/*
801556Srgrimes * The parsefile structure pointed to by the global variable parsefile
811556Srgrimes * contains information about the current file being read.
821556Srgrimes */
831556Srgrimes
841556SrgrimesMKINIT
851556Srgrimesstruct parsefile {
861556Srgrimes	struct parsefile *prev;	/* preceding file on stack */
871556Srgrimes	int linno;		/* current line */
881556Srgrimes	int fd;			/* file descriptor (or -1 if string) */
8920425Ssteve	int nleft;		/* number of chars left in this line */
9020425Ssteve	int lleft;		/* number of lines left in this buffer */
911556Srgrimes	char *nextc;		/* next char in buffer */
921556Srgrimes	char *buf;		/* input buffer */
931556Srgrimes	struct strpush *strpush; /* for pushing strings at this level */
941556Srgrimes	struct strpush basestrpush; /* so pushing one is fast */
951556Srgrimes};
961556Srgrimes
971556Srgrimes
981556Srgrimesint plinno = 1;			/* input line number */
991556SrgrimesMKINIT int parsenleft;		/* copy of parsefile->nleft */
10012043SpeterMKINIT int parselleft;		/* copy of parsefile->lleft */
1011556Srgrimeschar *parsenextc;		/* copy of parsefile->nextc */
1021556SrgrimesMKINIT struct parsefile basepf;	/* top level input file */
1031556Srgrimeschar basebuf[BUFSIZ];		/* buffer for top level input file */
1041556Srgrimesstruct parsefile *parsefile = &basepf;	/* current input file */
1051556Srgrimesint init_editline = 0;		/* editline library initialized? */
1061556Srgrimesint whichprompt;		/* 1 == PS1, 2 == PS2 */
1071556Srgrimes
1081556SrgrimesEditLine *el;			/* cookie for editline package */
1091556Srgrimes
11017987SpeterSTATIC void pushfile __P((void));
11125225Sstevestatic int preadfd __P((void));
1121556Srgrimes
1131556Srgrimes#ifdef mkinit
1141556SrgrimesINCLUDE "input.h"
1151556SrgrimesINCLUDE "error.h"
1161556Srgrimes
1171556SrgrimesINIT {
1181556Srgrimes	extern char basebuf[];
1191556Srgrimes
1201556Srgrimes	basepf.nextc = basepf.buf = basebuf;
1211556Srgrimes}
1221556Srgrimes
1231556SrgrimesRESET {
1241556Srgrimes	if (exception != EXSHELLPROC)
12512043Speter		parselleft = parsenleft = 0;	/* clear input buffer */
1261556Srgrimes	popallfiles();
1271556Srgrimes}
1281556Srgrimes
1291556SrgrimesSHELLPROC {
1301556Srgrimes	popallfiles();
1311556Srgrimes}
1321556Srgrimes#endif
1331556Srgrimes
1341556Srgrimes
1351556Srgrimes/*
1361556Srgrimes * Read a line from the script.
1371556Srgrimes */
1381556Srgrimes
1391556Srgrimeschar *
1401556Srgrimespfgets(line, len)
1411556Srgrimes	char *line;
14217987Speter	int len;
14317987Speter{
14425225Ssteve	char *p = line;
1451556Srgrimes	int nleft = len;
1461556Srgrimes	int c;
1471556Srgrimes
1481556Srgrimes	while (--nleft > 0) {
1491556Srgrimes		c = pgetc_macro();
1501556Srgrimes		if (c == PEOF) {
1511556Srgrimes			if (p == line)
1521556Srgrimes				return NULL;
1531556Srgrimes			break;
1541556Srgrimes		}
1551556Srgrimes		*p++ = c;
1561556Srgrimes		if (c == '\n')
1571556Srgrimes			break;
1581556Srgrimes	}
1591556Srgrimes	*p = '\0';
1601556Srgrimes	return line;
1611556Srgrimes}
1621556Srgrimes
1631556Srgrimes
1641556Srgrimes
1651556Srgrimes/*
1661556Srgrimes * Read a character from the script, returning PEOF on end of file.
1671556Srgrimes * Nul characters in the input are silently discarded.
1681556Srgrimes */
1691556Srgrimes
1701556Srgrimesint
17120425Sstevepgetc()
17220425Ssteve{
1731556Srgrimes	return pgetc_macro();
1741556Srgrimes}
1751556Srgrimes
17620425Ssteve
17712043Speterstatic int
17825225Sstevepreadfd()
17912043Speter{
18012043Speter	int nr;
18120425Ssteve	parsenextc = parsefile->buf;
1821556Srgrimes
18312043Speterretry:
18425225Ssteve#ifndef NO_HISTORY
18512043Speter	if (parsefile->fd == 0 && el) {
18612043Speter		const char *rl_cp;
18712043Speter
18812043Speter		rl_cp = el_gets(el, &nr);
18912043Speter		if (rl_cp == NULL)
19012043Speter			nr = 0;
19112043Speter		else {
19212043Speter			/* XXX - BUFSIZE should redesign so not necessary */
19320425Ssteve			(void) strcpy(parsenextc, rl_cp);
19412043Speter		}
19525225Ssteve	} else
19625225Ssteve#endif
19712043Speter		nr = read(parsefile->fd, parsenextc, BUFSIZ - 1);
19812043Speter
19912043Speter	if (nr <= 0) {
20012043Speter                if (nr < 0) {
20112043Speter                        if (errno == EINTR)
20212043Speter                                goto retry;
20312043Speter                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
20412043Speter                                int flags = fcntl(0, F_GETFL, 0);
20512043Speter                                if (flags >= 0 && flags & O_NONBLOCK) {
20612043Speter                                        flags &=~ O_NONBLOCK;
20712043Speter                                        if (fcntl(0, F_SETFL, flags) >= 0) {
20812043Speter						out2str("sh: turning off NDELAY mode\n");
20912043Speter                                                goto retry;
21012043Speter                                        }
21112043Speter                                }
21212043Speter                        }
21312043Speter                }
21420425Ssteve                nr = -1;
21512043Speter	}
21612043Speter	return nr;
21712043Speter}
21812043Speter
2191556Srgrimes/*
2201556Srgrimes * Refill the input buffer and return the next input character:
2211556Srgrimes *
2221556Srgrimes * 1) If a string was pushed back on the input, pop it;
2231556Srgrimes * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
2241556Srgrimes *    from a string so we can't refill the buffer, return EOF.
22520425Ssteve * 3) If there is more in this buffer, use it else call read to fill it.
22620425Ssteve * 4) Process input up to the next newline, deleting nul characters.
2271556Srgrimes */
2281556Srgrimes
2291556Srgrimesint
23020425Sstevepreadbuffer()
23120425Ssteve{
23212043Speter	char *p, *q;
23312043Speter	int more;
23412043Speter	int something;
2351556Srgrimes	extern EditLine *el;
23612043Speter	char savec;
2371556Srgrimes
2381556Srgrimes	if (parsefile->strpush) {
2391556Srgrimes		popstring();
2401556Srgrimes		if (--parsenleft >= 0)
2411556Srgrimes			return (*parsenextc++);
2421556Srgrimes	}
2431556Srgrimes	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
2441556Srgrimes		return PEOF;
2451556Srgrimes	flushout(&output);
2461556Srgrimes	flushout(&errout);
2471556Srgrimes
24812043Speteragain:
24912043Speter	if (parselleft <= 0) {
25025225Ssteve		if ((parselleft = preadfd()) == -1) {
25112043Speter			parselleft = parsenleft = EOF_NLEFT;
25212043Speter			return PEOF;
2531556Srgrimes		}
2541556Srgrimes	}
2551556Srgrimes
25612043Speter	q = p = parsenextc;
25712043Speter
2581556Srgrimes	/* delete nul characters */
2591556Srgrimes	something = 0;
26012043Speter	for (more = 1; more;) {
26112043Speter		switch (*p) {
26212043Speter		case '\0':
26312043Speter			p++;	/* Skip nul */
26412043Speter			goto check;
26512043Speter
26612043Speter		case '\t':
26712043Speter		case ' ':
2681556Srgrimes			break;
26912043Speter
27012043Speter		case '\n':
27112043Speter			parsenleft = q - parsenextc;
27212043Speter			more = 0; /* Stop processing here */
27312043Speter			break;
27412043Speter
27512043Speter		default:
2761556Srgrimes			something = 1;
27712043Speter			break;
2781556Srgrimes		}
27912043Speter
28012043Speter		*q++ = *p++;
28112043Spetercheck:
28212043Speter		if (--parselleft <= 0) {
28312043Speter			parsenleft = q - parsenextc - 1;
28412043Speter			if (parsenleft < 0)
28512043Speter				goto again;
28612043Speter			*q = '\0';
28712043Speter			more = 0;
28812043Speter		}
2891556Srgrimes	}
29012043Speter
29112043Speter	savec = *q;
2921556Srgrimes	*q = '\0';
2931556Srgrimes
29418018Speter#ifndef NO_HISTORY
2951556Srgrimes	if (parsefile->fd == 0 && hist && something) {
2961556Srgrimes		INTOFF;
29712043Speter		history(hist, whichprompt == 1 ? H_ENTER : H_ADD, parsenextc);
2981556Srgrimes		INTON;
2991556Srgrimes	}
30018018Speter#endif
30112043Speter
3021556Srgrimes	if (vflag) {
30312043Speter		out2str(parsenextc);
3041556Srgrimes		flushout(out2);
3051556Srgrimes	}
30612043Speter
30712043Speter	*q = savec;
30812043Speter
3091556Srgrimes	return *parsenextc++;
3101556Srgrimes}
3111556Srgrimes
3121556Srgrimes/*
3131556Srgrimes * Undo the last call to pgetc.  Only one character may be pushed back.
3141556Srgrimes * PEOF may be pushed back.
3151556Srgrimes */
3161556Srgrimes
3171556Srgrimesvoid
3181556Srgrimespungetc() {
3191556Srgrimes	parsenleft++;
3201556Srgrimes	parsenextc--;
3211556Srgrimes}
3221556Srgrimes
3231556Srgrimes/*
3241556Srgrimes * Push a string back onto the input at this current parsefile level.
3251556Srgrimes * We handle aliases this way.
3261556Srgrimes */
3271556Srgrimesvoid
3281556Srgrimespushstring(s, len, ap)
3291556Srgrimes	char *s;
3301556Srgrimes	int len;
3311556Srgrimes	void *ap;
3321556Srgrimes	{
3331556Srgrimes	struct strpush *sp;
3341556Srgrimes
3351556Srgrimes	INTOFF;
3361556Srgrimes/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
3371556Srgrimes	if (parsefile->strpush) {
3381556Srgrimes		sp = ckmalloc(sizeof (struct strpush));
3391556Srgrimes		sp->prev = parsefile->strpush;
3401556Srgrimes		parsefile->strpush = sp;
3411556Srgrimes	} else
3421556Srgrimes		sp = parsefile->strpush = &(parsefile->basestrpush);
3431556Srgrimes	sp->prevstring = parsenextc;
3441556Srgrimes	sp->prevnleft = parsenleft;
34512043Speter	sp->prevlleft = parselleft;
3461556Srgrimes	sp->ap = (struct alias *)ap;
3471556Srgrimes	if (ap)
3481556Srgrimes		((struct alias *)ap)->flag |= ALIASINUSE;
3491556Srgrimes	parsenextc = s;
3501556Srgrimes	parsenleft = len;
3511556Srgrimes	INTON;
3521556Srgrimes}
3531556Srgrimes
35417987Spetervoid
3551556Srgrimespopstring()
3561556Srgrimes{
3571556Srgrimes	struct strpush *sp = parsefile->strpush;
3581556Srgrimes
3591556Srgrimes	INTOFF;
3601556Srgrimes	parsenextc = sp->prevstring;
3611556Srgrimes	parsenleft = sp->prevnleft;
36212043Speter	parselleft = sp->prevlleft;
3631556Srgrimes/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
3641556Srgrimes	if (sp->ap)
3651556Srgrimes		sp->ap->flag &= ~ALIASINUSE;
3661556Srgrimes	parsefile->strpush = sp->prev;
3671556Srgrimes	if (sp != &(parsefile->basestrpush))
3681556Srgrimes		ckfree(sp);
3691556Srgrimes	INTON;
3701556Srgrimes}
3711556Srgrimes
3721556Srgrimes/*
3731556Srgrimes * Set the input to take input from a file.  If push is set, push the
3741556Srgrimes * old input onto the stack first.
3751556Srgrimes */
3761556Srgrimes
3771556Srgrimesvoid
3781556Srgrimessetinputfile(fname, push)
3791556Srgrimes	char *fname;
38017987Speter	int push;
38117987Speter{
3821556Srgrimes	int fd;
3831556Srgrimes	int fd2;
3841556Srgrimes
3851556Srgrimes	INTOFF;
3861556Srgrimes	if ((fd = open(fname, O_RDONLY)) < 0)
3871556Srgrimes		error("Can't open %s", fname);
3881556Srgrimes	if (fd < 10) {
3891556Srgrimes		fd2 = copyfd(fd, 10);
3901556Srgrimes		close(fd);
3911556Srgrimes		if (fd2 < 0)
3921556Srgrimes			error("Out of file descriptors");
3931556Srgrimes		fd = fd2;
3941556Srgrimes	}
3951556Srgrimes	setinputfd(fd, push);
3961556Srgrimes	INTON;
3971556Srgrimes}
3981556Srgrimes
3991556Srgrimes
4001556Srgrimes/*
4011556Srgrimes * Like setinputfile, but takes an open file descriptor.  Call this with
4021556Srgrimes * interrupts off.
4031556Srgrimes */
4041556Srgrimes
4051556Srgrimesvoid
40617987Spetersetinputfd(fd, push)
40717987Speter	int fd, push;
40817987Speter{
40925225Ssteve	(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
4101556Srgrimes	if (push) {
4111556Srgrimes		pushfile();
4121556Srgrimes		parsefile->buf = ckmalloc(BUFSIZ);
4131556Srgrimes	}
4141556Srgrimes	if (parsefile->fd > 0)
4151556Srgrimes		close(parsefile->fd);
4161556Srgrimes	parsefile->fd = fd;
4171556Srgrimes	if (parsefile->buf == NULL)
4181556Srgrimes		parsefile->buf = ckmalloc(BUFSIZ);
41912043Speter	parselleft = parsenleft = 0;
4201556Srgrimes	plinno = 1;
4211556Srgrimes}
4221556Srgrimes
4231556Srgrimes
4241556Srgrimes/*
4251556Srgrimes * Like setinputfile, but takes input from a string.
4261556Srgrimes */
4271556Srgrimes
4281556Srgrimesvoid
4291556Srgrimessetinputstring(string, push)
4301556Srgrimes	char *string;
43117987Speter	int push;
4321556Srgrimes	{
4331556Srgrimes	INTOFF;
4341556Srgrimes	if (push)
4351556Srgrimes		pushfile();
4361556Srgrimes	parsenextc = string;
43712043Speter	parselleft = parsenleft = strlen(string);
4381556Srgrimes	parsefile->buf = NULL;
4391556Srgrimes	plinno = 1;
4401556Srgrimes	INTON;
4411556Srgrimes}
4421556Srgrimes
4431556Srgrimes
4441556Srgrimes
4451556Srgrimes/*
4461556Srgrimes * To handle the "." command, a stack of input files is used.  Pushfile
4471556Srgrimes * adds a new entry to the stack and popfile restores the previous level.
4481556Srgrimes */
4491556Srgrimes
4501556SrgrimesSTATIC void
4511556Srgrimespushfile() {
4521556Srgrimes	struct parsefile *pf;
4531556Srgrimes
4541556Srgrimes	parsefile->nleft = parsenleft;
45512043Speter	parsefile->lleft = parselleft;
4561556Srgrimes	parsefile->nextc = parsenextc;
4571556Srgrimes	parsefile->linno = plinno;
4581556Srgrimes	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
4591556Srgrimes	pf->prev = parsefile;
4601556Srgrimes	pf->fd = -1;
4611556Srgrimes	pf->strpush = NULL;
4621556Srgrimes	pf->basestrpush.prev = NULL;
4631556Srgrimes	parsefile = pf;
4641556Srgrimes}
4651556Srgrimes
4661556Srgrimes
4671556Srgrimesvoid
4681556Srgrimespopfile() {
4691556Srgrimes	struct parsefile *pf = parsefile;
4701556Srgrimes
4711556Srgrimes	INTOFF;
4721556Srgrimes	if (pf->fd >= 0)
4731556Srgrimes		close(pf->fd);
4741556Srgrimes	if (pf->buf)
4751556Srgrimes		ckfree(pf->buf);
4761556Srgrimes	while (pf->strpush)
4771556Srgrimes		popstring();
4781556Srgrimes	parsefile = pf->prev;
4791556Srgrimes	ckfree(pf);
4801556Srgrimes	parsenleft = parsefile->nleft;
48112043Speter	parselleft = parsefile->lleft;
4821556Srgrimes	parsenextc = parsefile->nextc;
4831556Srgrimes	plinno = parsefile->linno;
4841556Srgrimes	INTON;
4851556Srgrimes}
4861556Srgrimes
4871556Srgrimes
4881556Srgrimes/*
4891556Srgrimes * Return to top level.
4901556Srgrimes */
4911556Srgrimes
4921556Srgrimesvoid
4931556Srgrimespopallfiles() {
4941556Srgrimes	while (parsefile != &basepf)
4951556Srgrimes		popfile();
4961556Srgrimes}
4971556Srgrimes
4981556Srgrimes
4991556Srgrimes
5001556Srgrimes/*
5011556Srgrimes * Close the file(s) that the shell is reading commands from.  Called
5021556Srgrimes * after a fork is done.
5031556Srgrimes */
5041556Srgrimes
5051556Srgrimesvoid
5061556Srgrimesclosescript() {
5071556Srgrimes	popallfiles();
5081556Srgrimes	if (parsefile->fd > 0) {
5091556Srgrimes		close(parsefile->fd);
5101556Srgrimes		parsefile->fd = 0;
5111556Srgrimes	}
5121556Srgrimes}
513