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