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