input.c revision 1.7
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#ifndef NO_HISTORY
187	extern EditLine *el;
188#endif
189
190	if (parsefile->strpush) {
191		popstring();
192		if (--parsenleft >= 0)
193			return (*parsenextc++);
194	}
195	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
196		return PEOF;
197	flushout(&output);
198	flushout(&errout);
199retry:
200#ifndef NO_HISTORY
201	p = parsenextc = parsefile->buf;
202	if (parsefile->fd == 0 && el) {
203		const char *rl_cp;
204		int len;
205
206		rl_cp = el_gets(el, &len);
207		if (rl_cp == NULL) {
208			i = 0;
209			goto eof;
210		}
211		strcpy(p, rl_cp);  /* XXX - BUFSIZE should redesign so not necessary */
212		i = len;
213
214	} else {
215#endif
216regular_read:
217		i = read(parsefile->fd, p, BUFSIZ - 1);
218#ifndef NO_HISTORY
219	}
220#endif
221eof:
222	if (i <= 0) {
223                if (i < 0) {
224                        if (errno == EINTR)
225                                goto retry;
226                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
227                                int flags = fcntl(0, F_GETFL, 0);
228                                if (flags >= 0 && flags & O_NONBLOCK) {
229                                        flags &=~ O_NONBLOCK;
230                                        if (fcntl(0, F_SETFL, flags) >= 0) {
231						out2str("sh: turning off NDELAY mode\n");
232                                                goto retry;
233                                        }
234                                }
235                        }
236                }
237                parsenleft = EOF_NLEFT;
238                return PEOF;
239	}
240	parsenleft = i - 1;	/* we're returning one char in this call */
241
242	/* delete nul characters */
243	something = 0;
244	for (;;) {
245		if (*p == '\0')
246			break;
247		if (*p != ' ' && *p != '\t' && *p != '\n')
248			something = 1;
249		p++;
250		if (--i <= 0) {
251			*p = '\0';
252			goto done;		/* no nul characters */
253		}
254	}
255	/*
256	 * remove nuls
257	 */
258	q = p++;
259	while (--i > 0) {
260		if (*p != '\0')
261			*q++ = *p;
262		p++;
263	}
264	*q = '\0';
265	if (q == parsefile->buf)
266		goto retry;			/* buffer contained nothing but nuls */
267	parsenleft = q - parsefile->buf - 1;
268
269done:
270#ifndef NO_HISTORY
271	if (parsefile->fd == 0 && hist && something) {
272		INTOFF;
273		history(hist, whichprompt == 1 ? H_ENTER : H_ADD,
274			   parsefile->buf);
275		INTON;
276	}
277#endif
278	if (vflag) {
279		/*
280		 * This isn't right.  Most shells coordinate it with
281		 * reading a line at a time.  I honestly don't know if its
282		 * worth it.
283		 */
284		i = parsenleft + 1;
285		p = parsefile->buf;
286		for (; i--; p++)
287			out2c(*p)
288		flushout(out2);
289	}
290	return *parsenextc++;
291}
292
293/*
294 * Undo the last call to pgetc.  Only one character may be pushed back.
295 * PEOF may be pushed back.
296 */
297
298void
299pungetc() {
300	parsenleft++;
301	parsenextc--;
302}
303
304/*
305 * Push a string back onto the input at this current parsefile level.
306 * We handle aliases this way.
307 */
308void
309pushstring(s, len, ap)
310	char *s;
311	int len;
312	void *ap;
313	{
314	struct strpush *sp;
315
316	INTOFF;
317/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
318	if (parsefile->strpush) {
319		sp = ckmalloc(sizeof (struct strpush));
320		sp->prev = parsefile->strpush;
321		parsefile->strpush = sp;
322	} else
323		sp = parsefile->strpush = &(parsefile->basestrpush);
324	sp->prevstring = parsenextc;
325	sp->prevnleft = parsenleft;
326	sp->ap = (struct alias *)ap;
327	if (ap)
328		((struct alias *)ap)->flag |= ALIASINUSE;
329	parsenextc = s;
330	parsenleft = len;
331	INTON;
332}
333
334popstring()
335{
336	struct strpush *sp = parsefile->strpush;
337
338	INTOFF;
339	parsenextc = sp->prevstring;
340	parsenleft = sp->prevnleft;
341/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
342	if (sp->ap)
343		sp->ap->flag &= ~ALIASINUSE;
344	parsefile->strpush = sp->prev;
345	if (sp != &(parsefile->basestrpush))
346		ckfree(sp);
347	INTON;
348}
349
350/*
351 * Set the input to take input from a file.  If push is set, push the
352 * old input onto the stack first.
353 */
354
355void
356setinputfile(fname, push)
357	char *fname;
358	{
359	int fd;
360	int fd2;
361
362	INTOFF;
363	if ((fd = open(fname, O_RDONLY)) < 0)
364		error("Can't open %s", fname);
365	if (fd < 10) {
366		fd2 = copyfd(fd, 10);
367		close(fd);
368		if (fd2 < 0)
369			error("Out of file descriptors");
370		fd = fd2;
371	}
372	setinputfd(fd, push);
373	INTON;
374}
375
376
377/*
378 * Like setinputfile, but takes an open file descriptor.  Call this with
379 * interrupts off.
380 */
381
382void
383setinputfd(fd, push) {
384	if (push) {
385		pushfile();
386		parsefile->buf = ckmalloc(BUFSIZ);
387	}
388	if (parsefile->fd > 0)
389		close(parsefile->fd);
390	parsefile->fd = fd;
391	if (parsefile->buf == NULL)
392		parsefile->buf = ckmalloc(BUFSIZ);
393	parsenleft = 0;
394	plinno = 1;
395}
396
397
398/*
399 * Like setinputfile, but takes input from a string.
400 */
401
402void
403setinputstring(string, push)
404	char *string;
405	{
406	INTOFF;
407	if (push)
408		pushfile();
409	parsenextc = string;
410	parsenleft = strlen(string);
411	parsefile->buf = NULL;
412	plinno = 1;
413	INTON;
414}
415
416
417
418/*
419 * To handle the "." command, a stack of input files is used.  Pushfile
420 * adds a new entry to the stack and popfile restores the previous level.
421 */
422
423STATIC void
424pushfile() {
425	struct parsefile *pf;
426
427	parsefile->nleft = parsenleft;
428	parsefile->nextc = parsenextc;
429	parsefile->linno = plinno;
430	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
431	pf->prev = parsefile;
432	pf->fd = -1;
433	pf->strpush = NULL;
434	pf->basestrpush.prev = NULL;
435	parsefile = pf;
436}
437
438
439void
440popfile() {
441	struct parsefile *pf = parsefile;
442
443	INTOFF;
444	if (pf->fd >= 0)
445		close(pf->fd);
446	if (pf->buf)
447		ckfree(pf->buf);
448	while (pf->strpush)
449		popstring();
450	parsefile = pf->prev;
451	ckfree(pf);
452	parsenleft = parsefile->nleft;
453	parsenextc = parsefile->nextc;
454	plinno = parsefile->linno;
455	INTON;
456}
457
458
459/*
460 * Return to top level.
461 */
462
463void
464popallfiles() {
465	while (parsefile != &basepf)
466		popfile();
467}
468
469
470
471/*
472 * Close the file(s) that the shell is reading commands from.  Called
473 * after a fork is done.
474 */
475
476void
477closescript() {
478	popallfiles();
479	if (parsefile->fd > 0) {
480		close(parsefile->fd);
481		parsefile->fd = 0;
482	}
483}
484