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