This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / bin / sh / input.c
CommitLineData
15637ed4
RG
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
78ed81a3 38/*static char sccsid[] = "from: @(#)input.c 5.4 (Berkeley) 7/1/91";*/
39static char rcsid[] = "input.c,v 1.4 1993/08/01 18:58:15 mycroft Exp";
15637ed4
RG
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}