input.c revision 1.24 1 /* $NetBSD: input.c,v 1.24 1997/03/13 21:57:33 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
41 static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95";
42 #else
43 static char rcsid[] = "$NetBSD: input.c,v 1.24 1997/03/13 21:57:33 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
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 #ifndef NO_HISTORY
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
232 int
233 preadbuffer()
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
251 again:
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++;
284 check:
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 NO_HISTORY
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
320 void
321 pungetc() {
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 */
330 void
331 pushstring(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
357 void
358 popstring()
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
380 void
381 setinputfile(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
408 void
409 setinputfd(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
431 void
432 setinputstring(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
453 STATIC void
454 pushfile() {
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
470 void
471 popfile() {
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
495 void
496 popallfiles() {
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
508 void
509 closescript() {
510 popallfiles();
511 if (parsefile->fd > 0) {
512 close(parsefile->fd);
513 parsefile->fd = 0;
514 }
515 }
516