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