input.c revision 1.3
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * 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	5.4 (Berkeley) 7/1/91";
39static char rcsid[] = "$Header: /usr/local/opengrok/home/cvs-mirror/mirrors/netbsd/src/bin/sh/input.c,v 1.3 1993/03/23 00:28:07 cgd Exp $";
40#endif /* not lint */
41
42/*
43 * This file implements the input routines used by the parser.
44 */
45
46#include <stdio.h>	/* defines BUFSIZ */
47#include "shell.h"
48#include <fcntl.h>
49#include <errno.h>
50#include "syntax.h"
51#include "input.h"
52#include "output.h"
53#include "memalloc.h"
54#include "error.h"
55
56#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
57
58
59/*
60 * The parsefile structure pointed to by the global variable parsefile
61 * contains information about the current file being read.
62 */
63
64MKINIT
65struct parsefile {
66	int linno;		/* current line */
67	int fd;			/* file descriptor (or -1 if string) */
68	int nleft;		/* number of chars left in buffer */
69	char *nextc;		/* next char in buffer */
70	struct parsefile *prev;	/* preceding file on stack */
71	char *buf;		/* input buffer */
72};
73
74
75int plinno = 1;			/* input line number */
76MKINIT int parsenleft;		/* copy of parsefile->nleft */
77char *parsenextc;		/* copy of parsefile->nextc */
78MKINIT struct parsefile basepf;	/* top level input file */
79char basebuf[BUFSIZ];		/* buffer for top level input file */
80struct parsefile *parsefile = &basepf;	/* current input file */
81char *pushedstring;		/* copy of parsenextc when text pushed back */
82int pushednleft;		/* copy of parsenleft when text pushed back */
83
84#ifdef __STDC__
85STATIC void pushfile(void);
86#else
87STATIC void pushfile();
88#endif
89
90
91
92#ifdef mkinit
93INCLUDE "input.h"
94INCLUDE "error.h"
95
96INIT {
97	extern char basebuf[];
98
99	basepf.nextc = basepf.buf = basebuf;
100}
101
102RESET {
103	if (exception != EXSHELLPROC)
104		parsenleft = 0;            /* clear input buffer */
105	popallfiles();
106}
107
108SHELLPROC {
109	popallfiles();
110}
111#endif
112
113
114/*
115 * Read a line from the script.
116 */
117
118char *
119pfgets(line, len)
120	char *line;
121	{
122	register char *p = line;
123	int nleft = len;
124	int c;
125
126	while (--nleft > 0) {
127		c = pgetc_macro();
128		if (c == PEOF) {
129			if (p == line)
130				return NULL;
131			break;
132		}
133		*p++ = c;
134		if (c == '\n')
135			break;
136	}
137	*p = '\0';
138	return line;
139}
140
141
142
143/*
144 * Read a character from the script, returning PEOF on end of file.
145 * Nul characters in the input are silently discarded.
146 */
147
148int
149pgetc() {
150	return pgetc_macro();
151}
152
153
154/*
155 * Refill the input buffer and return the next input character:
156 *
157 * 1) If a string was pushed back on the input, switch back to the regular
158 *    buffer.
159 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
160 *    from a string so we can't refill the buffer, return EOF.
161 * 3) Call read to read in the characters.
162 * 4) Delete all nul characters from the buffer.
163 */
164
165int
166preadbuffer() {
167	register char *p, *q;
168	register int i;
169
170	if (pushedstring) {
171		parsenextc = pushedstring;
172		pushedstring = NULL;
173		parsenleft = pushednleft;
174		if (--parsenleft >= 0)
175			return *parsenextc++;
176	}
177	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
178		return PEOF;
179	flushout(&output);
180	flushout(&errout);
181retry:
182	p = parsenextc = parsefile->buf;
183	i = read(parsefile->fd, p, BUFSIZ);
184	if (i <= 0) {
185                if (i < 0) {
186                        if (errno == EINTR)
187                                goto retry;
188                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
189                                int flags = fcntl(0, F_GETFL, 0);
190                                if (flags >= 0 && flags & O_NONBLOCK) {
191                                        flags &=~ O_NONBLOCK;
192                                        if (fcntl(0, F_SETFL, flags) >= 0) {
193						out2str("sh: turning off NDELAY mode\n");
194                                                goto retry;
195                                        }
196                                }
197                        }
198                }
199                parsenleft = EOF_NLEFT;
200                return PEOF;
201	}
202	parsenleft = i - 1;
203
204	/* delete nul characters */
205	for (;;) {
206		if (*p++ == '\0')
207			break;
208		if (--i <= 0)
209			return *parsenextc++;		/* no nul characters */
210	}
211	q = p - 1;
212	while (--i > 0) {
213		if (*p != '\0')
214			*q++ = *p;
215		p++;
216	}
217	if (q == parsefile->buf)
218		goto retry;			/* buffer contained nothing but nuls */
219	parsenleft = q - parsefile->buf - 1;
220	return *parsenextc++;
221}
222
223
224/*
225 * Undo the last call to pgetc.  Only one character may be pushed back.
226 * PEOF may be pushed back.
227 */
228
229void
230pungetc() {
231	parsenleft++;
232	parsenextc--;
233}
234
235
236/*
237 * Push a string back onto the input.  This code doesn't work if the user
238 * tries to push back more than one string at once.
239 */
240
241void
242ppushback(string, length)
243	char *string;
244	{
245	pushedstring = parsenextc;
246	pushednleft = parsenleft;
247	parsenextc = string;
248	parsenleft = length;
249}
250
251
252
253/*
254 * Set the input to take input from a file.  If push is set, push the
255 * old input onto the stack first.
256 */
257
258void
259setinputfile(fname, push)
260	char *fname;
261	{
262	int fd;
263	int fd2;
264
265	INTOFF;
266	if ((fd = open(fname, O_RDONLY)) < 0)
267		error("Can't open %s", fname);
268	if (fd < 10) {
269		fd2 = copyfd(fd, 10);
270		close(fd);
271		if (fd2 < 0)
272			error("Out of file descriptors");
273		fd = fd2;
274	}
275	setinputfd(fd, push);
276	INTON;
277}
278
279
280/*
281 * Like setinputfile, but takes an open file descriptor.  Call this with
282 * interrupts off.
283 */
284
285void
286setinputfd(fd, push) {
287	if (push) {
288		pushfile();
289		parsefile->buf = ckmalloc(BUFSIZ);
290	}
291	if (parsefile->fd > 0)
292		close(parsefile->fd);
293	parsefile->fd = fd;
294	if (parsefile->buf == NULL)
295		parsefile->buf = ckmalloc(BUFSIZ);
296	parsenleft = 0;
297	plinno = 1;
298}
299
300
301/*
302 * Like setinputfile, but takes input from a string.
303 */
304
305void
306setinputstring(string, push)
307	char *string;
308	{
309	INTOFF;
310	if (push)
311		pushfile();
312	parsenextc = string;
313	parsenleft = strlen(string);
314	parsefile->buf = NULL;
315	plinno = 1;
316	INTON;
317}
318
319
320
321/*
322 * To handle the "." command, a stack of input files is used.  Pushfile
323 * adds a new entry to the stack and popfile restores the previous level.
324 */
325
326STATIC void
327pushfile() {
328	struct parsefile *pf;
329
330	parsefile->nleft = parsenleft;
331	parsefile->nextc = parsenextc;
332	parsefile->linno = plinno;
333	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
334	pf->prev = parsefile;
335	pf->fd = -1;
336	parsefile = pf;
337}
338
339
340void
341popfile() {
342	struct parsefile *pf = parsefile;
343
344	INTOFF;
345	if (pf->fd >= 0)
346		close(pf->fd);
347	if (pf->buf)
348		ckfree(pf->buf);
349	parsefile = pf->prev;
350	ckfree(pf);
351	parsenleft = parsefile->nleft;
352	parsenextc = parsefile->nextc;
353	plinno = parsefile->linno;
354	INTON;
355}
356
357
358/*
359 * Return to top level.
360 */
361
362void
363popallfiles() {
364	while (parsefile != &basepf)
365		popfile();
366}
367
368
369
370/*
371 * Close the file(s) that the shell is reading commands from.  Called
372 * after a fork is done.
373 */
374
375void
376closescript() {
377	popallfiles();
378	if (parsefile->fd > 0) {
379		close(parsefile->fd);
380		parsefile->fd = 0;
381	}
382}
383