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