input.c revision 1.10
1/*-
2 * Copyright (c) 1991, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38/*static char sccsid[] = "from: @(#)input.c	8.1 (Berkeley) 5/31/93";*/
39static char *rcsid = "$Id: input.c,v 1.10 1994/06/11 16:11:59 mycroft Exp $";
40#endif /* not lint */
41
42/*
43 * This file implements the input routines used by the parser.
44 */
45
46#include <stdio.h>	/* defines BUFSIZ */
47#include <fcntl.h>
48#include <errno.h>
49#include <unistd.h>
50#include "shell.h"
51#include "syntax.h"
52#include "input.h"
53#include "output.h"
54#include "options.h"
55#include "memalloc.h"
56#include "error.h"
57#include "alias.h"
58#include "parser.h"
59#ifndef NO_HISTORY
60#include "myhistedit.h"
61#endif
62
63#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
64
65MKINIT
66struct strpush {
67	struct strpush *prev;	/* preceding string on stack */
68	char *prevstring;
69	int prevnleft;
70	struct alias *ap;	/* if push was associated with an alias */
71};
72
73/*
74 * The parsefile structure pointed to by the global variable parsefile
75 * contains information about the current file being read.
76 */
77
78MKINIT
79struct parsefile {
80	struct parsefile *prev;	/* preceding file on stack */
81	int linno;		/* current line */
82	int fd;			/* file descriptor (or -1 if string) */
83	int nleft;		/* number of chars left in buffer */
84	char *nextc;		/* next char in buffer */
85	char *buf;		/* input buffer */
86	struct strpush *strpush; /* for pushing strings at this level */
87	struct strpush basestrpush; /* so pushing one is fast */
88};
89
90
91int plinno = 1;			/* input line number */
92MKINIT int parsenleft;		/* copy of parsefile->nleft */
93char *parsenextc;		/* copy of parsefile->nextc */
94MKINIT struct parsefile basepf;	/* top level input file */
95char basebuf[BUFSIZ];		/* buffer for top level input file */
96struct parsefile *parsefile = &basepf;	/* current input file */
97char *pushedstring;		/* copy of parsenextc when text pushed back */
98int pushednleft;		/* copy of parsenleft when text pushed back */
99int init_editline = 0;		/* editline library initialized? */
100int whichprompt;		/* 1 == PS1, 2 == PS2 */
101
102#ifndef NO_HISTORY
103EditLine *el;			/* cookie for editline package */
104#endif
105
106#ifdef __STDC__
107STATIC void pushfile(void);
108#else
109STATIC void pushfile();
110#endif
111
112
113
114#ifdef mkinit
115INCLUDE "input.h"
116INCLUDE "error.h"
117
118INIT {
119	extern char basebuf[];
120
121	basepf.nextc = basepf.buf = basebuf;
122}
123
124RESET {
125	if (exception != EXSHELLPROC)
126		parsenleft = 0;            /* clear input buffer */
127	popallfiles();
128}
129
130SHELLPROC {
131	popallfiles();
132}
133#endif
134
135
136/*
137 * Read a line from the script.
138 */
139
140char *
141pfgets(line, len)
142	char *line;
143	{
144	register char *p = line;
145	int nleft = len;
146	int c;
147
148	while (--nleft > 0) {
149		c = pgetc_macro();
150		if (c == PEOF) {
151			if (p == line)
152				return NULL;
153			break;
154		}
155		*p++ = c;
156		if (c == '\n')
157			break;
158	}
159	*p = '\0';
160	return line;
161}
162
163
164
165/*
166 * Read a character from the script, returning PEOF on end of file.
167 * Nul characters in the input are silently discarded.
168 */
169
170int
171pgetc() {
172	return pgetc_macro();
173}
174
175
176/*
177 * Refill the input buffer and return the next input character:
178 *
179 * 1) If a string was pushed back on the input, pop it;
180 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
181 *    from a string so we can't refill the buffer, return EOF.
182 * 3) Call read to read in the characters.
183 * 4) Delete all nul characters from the buffer.
184 */
185
186int
187preadbuffer() {
188	register char *p, *q;
189	register int i;
190	register int something;
191#ifndef NO_HISTORY
192	extern EditLine *el;
193#endif
194
195	if (parsefile->strpush) {
196		popstring();
197		if (--parsenleft >= 0)
198			return (*parsenextc++);
199	}
200	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
201		return PEOF;
202	flushout(&output);
203	flushout(&errout);
204retry:
205	p = parsenextc = parsefile->buf;
206#ifndef NO_HISTORY
207	if (parsefile->fd == 0 && el) {
208		const char *rl_cp;
209		int len;
210
211		rl_cp = el_gets(el, &len);
212		if (rl_cp == NULL) {
213			i = 0;
214			goto eof;
215		}
216		strcpy(p, rl_cp);  /* XXX - BUFSIZE should redesign so not necessary */
217		i = len;
218
219	} else {
220#endif
221regular_read:
222		i = read(parsefile->fd, p, BUFSIZ - 1);
223#ifndef NO_HISTORY
224	}
225#endif
226eof:
227	if (i <= 0) {
228                if (i < 0) {
229                        if (errno == EINTR)
230                                goto retry;
231                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
232                                int flags = fcntl(0, F_GETFL, 0);
233                                if (flags >= 0 && flags & O_NONBLOCK) {
234                                        flags &=~ O_NONBLOCK;
235                                        if (fcntl(0, F_SETFL, flags) >= 0) {
236						out2str("sh: turning off NDELAY mode\n");
237                                                goto retry;
238                                        }
239                                }
240                        }
241                }
242                parsenleft = EOF_NLEFT;
243                return PEOF;
244	}
245	parsenleft = i - 1;	/* we're returning one char in this call */
246
247	/* delete nul characters */
248	something = 0;
249	for (;;) {
250		if (*p == '\0')
251			break;
252		if (*p != ' ' && *p != '\t' && *p != '\n')
253			something = 1;
254		p++;
255		if (--i <= 0) {
256			*p = '\0';
257			goto done;		/* no nul characters */
258		}
259	}
260	/*
261	 * remove nuls
262	 */
263	q = p++;
264	while (--i > 0) {
265		if (*p != '\0')
266			*q++ = *p;
267		p++;
268	}
269	*q = '\0';
270	if (q == parsefile->buf)
271		goto retry;			/* buffer contained nothing but nuls */
272	parsenleft = q - parsefile->buf - 1;
273
274done:
275#ifndef NO_HISTORY
276	if (parsefile->fd == 0 && hist && something) {
277		INTOFF;
278		history(hist, whichprompt == 1 ? H_ENTER : H_ADD,
279			   parsefile->buf);
280		INTON;
281	}
282#endif
283	if (vflag) {
284		/*
285		 * This isn't right.  Most shells coordinate it with
286		 * reading a line at a time.  I honestly don't know if its
287		 * worth it.
288		 */
289		i = parsenleft + 1;
290		p = parsefile->buf;
291		for (; i--; p++)
292			out2c(*p)
293		flushout(out2);
294	}
295	return *parsenextc++;
296}
297
298/*
299 * Undo the last call to pgetc.  Only one character may be pushed back.
300 * PEOF may be pushed back.
301 */
302
303void
304pungetc() {
305	parsenleft++;
306	parsenextc--;
307}
308
309/*
310 * Push a string back onto the input at this current parsefile level.
311 * We handle aliases this way.
312 */
313void
314pushstring(s, len, ap)
315	char *s;
316	int len;
317	void *ap;
318	{
319	struct strpush *sp;
320
321	INTOFF;
322/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
323	if (parsefile->strpush) {
324		sp = ckmalloc(sizeof (struct strpush));
325		sp->prev = parsefile->strpush;
326		parsefile->strpush = sp;
327	} else
328		sp = parsefile->strpush = &(parsefile->basestrpush);
329	sp->prevstring = parsenextc;
330	sp->prevnleft = parsenleft;
331	sp->ap = (struct alias *)ap;
332	if (ap)
333		((struct alias *)ap)->flag |= ALIASINUSE;
334	parsenextc = s;
335	parsenleft = len;
336	INTON;
337}
338
339popstring()
340{
341	struct strpush *sp = parsefile->strpush;
342
343	INTOFF;
344	parsenextc = sp->prevstring;
345	parsenleft = sp->prevnleft;
346/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
347	if (sp->ap)
348		sp->ap->flag &= ~ALIASINUSE;
349	parsefile->strpush = sp->prev;
350	if (sp != &(parsefile->basestrpush))
351		ckfree(sp);
352	INTON;
353}
354
355/*
356 * Set the input to take input from a file.  If push is set, push the
357 * old input onto the stack first.
358 */
359
360void
361setinputfile(fname, push)
362	char *fname;
363	{
364	int fd;
365	int fd2;
366
367	INTOFF;
368	if ((fd = open(fname, O_RDONLY)) < 0)
369		error("Can't open %s", fname);
370	if (fd < 10) {
371		fd2 = copyfd(fd, 10);
372		close(fd);
373		if (fd2 < 0)
374			error("Out of file descriptors");
375		fd = fd2;
376	}
377	setinputfd(fd, push);
378	INTON;
379}
380
381
382/*
383 * Like setinputfile, but takes an open file descriptor.  Call this with
384 * interrupts off.
385 */
386
387void
388setinputfd(fd, push) {
389	if (push) {
390		pushfile();
391		parsefile->buf = ckmalloc(BUFSIZ);
392	}
393	if (parsefile->fd > 0)
394		close(parsefile->fd);
395	parsefile->fd = fd;
396	if (parsefile->buf == NULL)
397		parsefile->buf = ckmalloc(BUFSIZ);
398	parsenleft = 0;
399	plinno = 1;
400}
401
402
403/*
404 * Like setinputfile, but takes input from a string.
405 */
406
407void
408setinputstring(string, push)
409	char *string;
410	{
411	INTOFF;
412	if (push)
413		pushfile();
414	parsenextc = string;
415	parsenleft = strlen(string);
416	parsefile->buf = NULL;
417	plinno = 1;
418	INTON;
419}
420
421
422
423/*
424 * To handle the "." command, a stack of input files is used.  Pushfile
425 * adds a new entry to the stack and popfile restores the previous level.
426 */
427
428STATIC void
429pushfile() {
430	struct parsefile *pf;
431
432	parsefile->nleft = parsenleft;
433	parsefile->nextc = parsenextc;
434	parsefile->linno = plinno;
435	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
436	pf->prev = parsefile;
437	pf->fd = -1;
438	pf->strpush = NULL;
439	pf->basestrpush.prev = NULL;
440	parsefile = pf;
441}
442
443
444void
445popfile() {
446	struct parsefile *pf = parsefile;
447
448	INTOFF;
449	if (pf->fd >= 0)
450		close(pf->fd);
451	if (pf->buf)
452		ckfree(pf->buf);
453	while (pf->strpush)
454		popstring();
455	parsefile = pf->prev;
456	ckfree(pf);
457	parsenleft = parsefile->nleft;
458	parsenextc = parsefile->nextc;
459	plinno = parsefile->linno;
460	INTON;
461}
462
463
464/*
465 * Return to top level.
466 */
467
468void
469popallfiles() {
470	while (parsefile != &basepf)
471		popfile();
472}
473
474
475
476/*
477 * Close the file(s) that the shell is reading commands from.  Called
478 * after a fork is done.
479 */
480
481void
482closescript() {
483	popallfiles();
484	if (parsefile->fd > 0) {
485		close(parsefile->fd);
486		parsefile->fd = 0;
487	}
488}
489