input.c revision 3044
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 *	$Id$
37 */
38
39#ifndef lint
40static char sccsid[] = "@(#)input.c	8.1 (Berkeley) 5/31/93";
41#endif /* not lint */
42
43/*
44 * This file implements the input routines used by the parser.
45 */
46
47#include <stdio.h>	/* defines BUFSIZ */
48#include "shell.h"
49#include <fcntl.h>
50#include <errno.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#include "myhistedit.h"
60
61#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
62
63MKINIT
64struct strpush {
65	struct strpush *prev;	/* preceding string on stack */
66	char *prevstring;
67	int prevnleft;
68	struct alias *ap;	/* if push was associated with an alias */
69};
70
71/*
72 * The parsefile structure pointed to by the global variable parsefile
73 * contains information about the current file being read.
74 */
75
76MKINIT
77struct parsefile {
78	struct parsefile *prev;	/* preceding file on stack */
79	int linno;		/* current line */
80	int fd;			/* file descriptor (or -1 if string) */
81	int nleft;		/* number of chars left in buffer */
82	char *nextc;		/* next char in buffer */
83	char *buf;		/* input buffer */
84	struct strpush *strpush; /* for pushing strings at this level */
85	struct strpush basestrpush; /* so pushing one is fast */
86};
87
88
89int plinno = 1;			/* input line number */
90MKINIT int parsenleft;		/* copy of parsefile->nleft */
91char *parsenextc;		/* copy of parsefile->nextc */
92MKINIT struct parsefile basepf;	/* top level input file */
93char basebuf[BUFSIZ];		/* buffer for top level input file */
94struct parsefile *parsefile = &basepf;	/* current input file */
95char *pushedstring;		/* copy of parsenextc when text pushed back */
96int pushednleft;		/* copy of parsenleft when text pushed back */
97int init_editline = 0;		/* editline library initialized? */
98int whichprompt;		/* 1 == PS1, 2 == PS2 */
99
100EditLine *el;			/* cookie for editline package */
101
102#ifdef __STDC__
103STATIC void pushfile(void);
104#else
105STATIC void pushfile();
106#endif
107
108
109
110#ifdef mkinit
111INCLUDE "input.h"
112INCLUDE "error.h"
113
114INIT {
115	extern char basebuf[];
116
117	basepf.nextc = basepf.buf = basebuf;
118}
119
120RESET {
121	if (exception != EXSHELLPROC)
122		parsenleft = 0;            /* clear input buffer */
123	popallfiles();
124}
125
126SHELLPROC {
127	popallfiles();
128}
129#endif
130
131
132/*
133 * Read a line from the script.
134 */
135
136char *
137pfgets(line, len)
138	char *line;
139	{
140	register char *p = line;
141	int nleft = len;
142	int c;
143
144	while (--nleft > 0) {
145		c = pgetc_macro();
146		if (c == PEOF) {
147			if (p == line)
148				return NULL;
149			break;
150		}
151		*p++ = c;
152		if (c == '\n')
153			break;
154	}
155	*p = '\0';
156	return line;
157}
158
159
160
161/*
162 * Read a character from the script, returning PEOF on end of file.
163 * Nul characters in the input are silently discarded.
164 */
165
166int
167pgetc() {
168	return pgetc_macro();
169}
170
171
172/*
173 * Refill the input buffer and return the next input character:
174 *
175 * 1) If a string was pushed back on the input, pop it;
176 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
177 *    from a string so we can't refill the buffer, return EOF.
178 * 3) Call read to read in the characters.
179 * 4) Delete all nul characters from the buffer.
180 */
181
182int
183preadbuffer() {
184	register char *p, *q;
185	register int i;
186	register int something;
187	extern EditLine *el;
188
189	if (parsefile->strpush) {
190		popstring();
191		if (--parsenleft >= 0)
192			return (*parsenextc++);
193	}
194	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
195		return PEOF;
196	flushout(&output);
197	flushout(&errout);
198retry:
199	p = parsenextc = parsefile->buf;
200	if (parsefile->fd == 0 && el) {
201		const char *rl_cp;
202		int len;
203
204		rl_cp = el_gets(el, &len);
205		if (rl_cp == NULL) {
206			i = 0;
207			goto eof;
208		}
209		strcpy(p, rl_cp);  /* XXX - BUFSIZE should redesign so not necessary */
210		i = len;
211
212	} else {
213regular_read:
214		i = read(parsefile->fd, p, BUFSIZ - 1);
215	}
216eof:
217	if (i <= 0) {
218                if (i < 0) {
219                        if (errno == EINTR)
220                                goto retry;
221                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
222                                int flags = fcntl(0, F_GETFL, 0);
223                                if (flags >= 0 && flags & O_NONBLOCK) {
224                                        flags &=~ O_NONBLOCK;
225                                        if (fcntl(0, F_SETFL, flags) >= 0) {
226						out2str("sh: turning off NDELAY mode\n");
227                                                goto retry;
228                                        }
229                                }
230                        }
231                }
232                parsenleft = EOF_NLEFT;
233                return PEOF;
234	}
235	parsenleft = i - 1;	/* we're returning one char in this call */
236
237	/* delete nul characters */
238	something = 0;
239	for (;;) {
240		if (*p == '\0')
241			break;
242		if (*p != ' ' && *p != '\t' && *p != '\n')
243			something = 1;
244		p++;
245		if (--i <= 0) {
246			*p = '\0';
247			goto done;		/* no nul characters */
248		}
249	}
250	/*
251	 * remove nuls
252	 */
253	q = p++;
254	while (--i > 0) {
255		if (*p != '\0')
256			*q++ = *p;
257		p++;
258	}
259	*q = '\0';
260	if (q == parsefile->buf)
261		goto retry;			/* buffer contained nothing but nuls */
262	parsenleft = q - parsefile->buf - 1;
263
264done:
265	if (parsefile->fd == 0 && hist && something) {
266		INTOFF;
267		history(hist, whichprompt == 1 ? H_ENTER : H_ADD,
268			   parsefile->buf);
269		INTON;
270	}
271	if (vflag) {
272		/*
273		 * This isn't right.  Most shells coordinate it with
274		 * reading a line at a time.  I honestly don't know if its
275		 * worth it.
276		 */
277		i = parsenleft + 1;
278		p = parsefile->buf;
279		for (; i--; p++)
280			out2c(*p)
281		flushout(out2);
282	}
283	return *parsenextc++;
284}
285
286/*
287 * Undo the last call to pgetc.  Only one character may be pushed back.
288 * PEOF may be pushed back.
289 */
290
291void
292pungetc() {
293	parsenleft++;
294	parsenextc--;
295}
296
297/*
298 * Push a string back onto the input at this current parsefile level.
299 * We handle aliases this way.
300 */
301void
302pushstring(s, len, ap)
303	char *s;
304	int len;
305	void *ap;
306	{
307	struct strpush *sp;
308
309	INTOFF;
310/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
311	if (parsefile->strpush) {
312		sp = ckmalloc(sizeof (struct strpush));
313		sp->prev = parsefile->strpush;
314		parsefile->strpush = sp;
315	} else
316		sp = parsefile->strpush = &(parsefile->basestrpush);
317	sp->prevstring = parsenextc;
318	sp->prevnleft = parsenleft;
319	sp->ap = (struct alias *)ap;
320	if (ap)
321		((struct alias *)ap)->flag |= ALIASINUSE;
322	parsenextc = s;
323	parsenleft = len;
324	INTON;
325}
326
327popstring()
328{
329	struct strpush *sp = parsefile->strpush;
330
331	INTOFF;
332	parsenextc = sp->prevstring;
333	parsenleft = sp->prevnleft;
334/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
335	if (sp->ap)
336		sp->ap->flag &= ~ALIASINUSE;
337	parsefile->strpush = sp->prev;
338	if (sp != &(parsefile->basestrpush))
339		ckfree(sp);
340	INTON;
341}
342
343/*
344 * Set the input to take input from a file.  If push is set, push the
345 * old input onto the stack first.
346 */
347
348void
349setinputfile(fname, push)
350	char *fname;
351	{
352	int fd;
353	int fd2;
354
355	INTOFF;
356	if ((fd = open(fname, O_RDONLY)) < 0)
357		error("Can't open %s", fname);
358	if (fd < 10) {
359		fd2 = copyfd(fd, 10);
360		close(fd);
361		if (fd2 < 0)
362			error("Out of file descriptors");
363		fd = fd2;
364	}
365	setinputfd(fd, push);
366	INTON;
367}
368
369
370/*
371 * Like setinputfile, but takes an open file descriptor.  Call this with
372 * interrupts off.
373 */
374
375void
376setinputfd(fd, push) {
377	if (push) {
378		pushfile();
379		parsefile->buf = ckmalloc(BUFSIZ);
380	}
381	if (parsefile->fd > 0)
382		close(parsefile->fd);
383	parsefile->fd = fd;
384	if (parsefile->buf == NULL)
385		parsefile->buf = ckmalloc(BUFSIZ);
386	parsenleft = 0;
387	plinno = 1;
388}
389
390
391/*
392 * Like setinputfile, but takes input from a string.
393 */
394
395void
396setinputstring(string, push)
397	char *string;
398	{
399	INTOFF;
400	if (push)
401		pushfile();
402	parsenextc = string;
403	parsenleft = strlen(string);
404	parsefile->buf = NULL;
405	plinno = 1;
406	INTON;
407}
408
409
410
411/*
412 * To handle the "." command, a stack of input files is used.  Pushfile
413 * adds a new entry to the stack and popfile restores the previous level.
414 */
415
416STATIC void
417pushfile() {
418	struct parsefile *pf;
419
420	parsefile->nleft = parsenleft;
421	parsefile->nextc = parsenextc;
422	parsefile->linno = plinno;
423	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
424	pf->prev = parsefile;
425	pf->fd = -1;
426	pf->strpush = NULL;
427	pf->basestrpush.prev = NULL;
428	parsefile = pf;
429}
430
431
432void
433popfile() {
434	struct parsefile *pf = parsefile;
435
436	INTOFF;
437	if (pf->fd >= 0)
438		close(pf->fd);
439	if (pf->buf)
440		ckfree(pf->buf);
441	while (pf->strpush)
442		popstring();
443	parsefile = pf->prev;
444	ckfree(pf);
445	parsenleft = parsefile->nleft;
446	parsenextc = parsefile->nextc;
447	plinno = parsefile->linno;
448	INTON;
449}
450
451
452/*
453 * Return to top level.
454 */
455
456void
457popallfiles() {
458	while (parsefile != &basepf)
459		popfile();
460}
461
462
463
464/*
465 * Close the file(s) that the shell is reading commands from.  Called
466 * after a fork is done.
467 */
468
469void
470closescript() {
471	popallfiles();
472	if (parsefile->fd > 0) {
473		close(parsefile->fd);
474		parsefile->fd = 0;
475	}
476}
477