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