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