add comment that TOUR does not fully represent current shell
[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
12d9746c 12static char sccsid[] = "@(#)input.c 5.7 (Berkeley) %G%";
9f8917b7
KB
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"
dcd51d39 26#include "options.h"
9f8917b7
KB
27#include "memalloc.h"
28#include "error.h"
dcd51d39
MT
29#include "alias.h"
30#include "parser.h"
fa73a0ba 31#include "myhistedit.h"
9f8917b7
KB
32
33#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
34
dcd51d39
MT
35MKINIT
36struct strpush {
37 struct strpush *prev; /* preceding string on stack */
38 char *prevstring;
39 int prevnleft;
40 struct alias *ap; /* if push was associated with an alias */
41};
9f8917b7
KB
42
43/*
44 * The parsefile structure pointed to by the global variable parsefile
45 * contains information about the current file being read.
46 */
47
48MKINIT
49struct parsefile {
dcd51d39 50 struct parsefile *prev; /* preceding file on stack */
9f8917b7
KB
51 int linno; /* current line */
52 int fd; /* file descriptor (or -1 if string) */
53 int nleft; /* number of chars left in buffer */
54 char *nextc; /* next char in buffer */
9f8917b7 55 char *buf; /* input buffer */
dcd51d39
MT
56 struct strpush *strpush; /* for pushing strings at this level */
57 struct strpush basestrpush; /* so pushing one is fast */
9f8917b7
KB
58};
59
60
61int plinno = 1; /* input line number */
62MKINIT int parsenleft; /* copy of parsefile->nleft */
63char *parsenextc; /* copy of parsefile->nextc */
64MKINIT struct parsefile basepf; /* top level input file */
65char basebuf[BUFSIZ]; /* buffer for top level input file */
66struct parsefile *parsefile = &basepf; /* current input file */
67char *pushedstring; /* copy of parsenextc when text pushed back */
68int pushednleft; /* copy of parsenleft when text pushed back */
dcd51d39
MT
69int init_editline = 0; /* editline library initialized? */
70int whichprompt; /* 1 == PS1, 2 == PS2 */
71
72EditLine *el; /* cookie for editline package */
9f8917b7
KB
73
74#ifdef __STDC__
75STATIC void pushfile(void);
76#else
77STATIC void pushfile();
78#endif
79
80
81
82#ifdef mkinit
83INCLUDE "input.h"
84INCLUDE "error.h"
85
86INIT {
87 extern char basebuf[];
88
89 basepf.nextc = basepf.buf = basebuf;
90}
91
92RESET {
93 if (exception != EXSHELLPROC)
94 parsenleft = 0; /* clear input buffer */
95 popallfiles();
96}
97
98SHELLPROC {
99 popallfiles();
100}
101#endif
102
103
104/*
105 * Read a line from the script.
106 */
107
108char *
109pfgets(line, len)
110 char *line;
111 {
112 register char *p = line;
113 int nleft = len;
114 int c;
115
116 while (--nleft > 0) {
117 c = pgetc_macro();
118 if (c == PEOF) {
119 if (p == line)
120 return NULL;
121 break;
122 }
123 *p++ = c;
124 if (c == '\n')
125 break;
126 }
127 *p = '\0';
128 return line;
129}
130
131
132
133/*
134 * Read a character from the script, returning PEOF on end of file.
135 * Nul characters in the input are silently discarded.
136 */
137
138int
139pgetc() {
140 return pgetc_macro();
141}
142
143
144/*
145 * Refill the input buffer and return the next input character:
146 *
dcd51d39 147 * 1) If a string was pushed back on the input, pop it;
9f8917b7
KB
148 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
149 * from a string so we can't refill the buffer, return EOF.
150 * 3) Call read to read in the characters.
151 * 4) Delete all nul characters from the buffer.
152 */
153
154int
155preadbuffer() {
156 register char *p, *q;
157 register int i;
dcd51d39
MT
158 register int something;
159 extern EditLine *el;
9f8917b7 160
dcd51d39
MT
161 if (parsefile->strpush) {
162 popstring();
9f8917b7 163 if (--parsenleft >= 0)
dcd51d39 164 return (*parsenextc++);
9f8917b7
KB
165 }
166 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
167 return PEOF;
168 flushout(&output);
169 flushout(&errout);
170retry:
171 p = parsenextc = parsefile->buf;
dcd51d39
MT
172 if (parsefile->fd == 0 && el) {
173 char *rl_cp;
174 int len;
175
176 rl_cp = el_gets(el, &len);
177 if (rl_cp == NULL) {
178 i = 0;
179 goto eof;
180 }
181 strcpy(p, rl_cp); /* XXX - BUFSIZE should redesign so not necessary */
182 i = len;
183
184 } else {
185regular_read:
186 i = read(parsefile->fd, p, BUFSIZ - 1);
187 }
188eof:
9f8917b7 189 if (i <= 0) {
ddba57cd
MT
190 if (i < 0) {
191 if (errno == EINTR)
192 goto retry;
193 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
194 int flags = fcntl(0, F_GETFL, 0);
f0c40091
KB
195 if (flags >= 0 && flags & O_NONBLOCK) {
196 flags &=~ O_NONBLOCK;
ddba57cd 197 if (fcntl(0, F_SETFL, flags) >= 0) {
bf226a97 198 out2str("sh: turning off NDELAY mode\n");
ddba57cd
MT
199 goto retry;
200 }
201 }
202 }
203 }
204 parsenleft = EOF_NLEFT;
205 return PEOF;
9f8917b7 206 }
dcd51d39 207 parsenleft = i - 1; /* we're returning one char in this call */
9f8917b7
KB
208
209 /* delete nul characters */
dcd51d39 210 something = 0;
9f8917b7 211 for (;;) {
dcd51d39 212 if (*p == '\0')
9f8917b7 213 break;
dcd51d39
MT
214 if (*p != ' ' && *p != '\t' && *p != '\n')
215 something = 1;
216 p++;
217 if (--i <= 0) {
218 *p = '\0';
219 goto done; /* no nul characters */
220 }
9f8917b7 221 }
dcd51d39
MT
222 /*
223 * remove nuls
224 */
225 q = p++;
9f8917b7
KB
226 while (--i > 0) {
227 if (*p != '\0')
228 *q++ = *p;
229 p++;
230 }
dcd51d39 231 *q = '\0';
9f8917b7
KB
232 if (q == parsefile->buf)
233 goto retry; /* buffer contained nothing but nuls */
234 parsenleft = q - parsefile->buf - 1;
dcd51d39
MT
235
236done:
237 if (parsefile->fd == 0 && hist && something) {
238 INTOFF;
239 history(hist, whichprompt == 1 ? H_ENTER : H_ADD,
240 parsefile->buf);
241 INTON;
242 }
243 if (vflag) {
244 /*
245 * This isn't right. Most shells coordinate it with
246 * reading a line at a time. I honestly don't know if its
247 * worth it.
248 */
249 i = parsenleft + 1;
250 p = parsefile->buf;
251 for (; i--; p++)
252 out2c(*p)
253 flushout(out2);
254 }
9f8917b7
KB
255 return *parsenextc++;
256}
257
9f8917b7
KB
258/*
259 * Undo the last call to pgetc. Only one character may be pushed back.
260 * PEOF may be pushed back.
261 */
262
263void
264pungetc() {
265 parsenleft++;
266 parsenextc--;
267}
268
9f8917b7 269/*
dcd51d39
MT
270 * Push a string back onto the input at this current parsefile level.
271 * We handle aliases this way.
9f8917b7 272 */
9f8917b7 273void
dcd51d39
MT
274pushstring(s, len, ap)
275 char *s;
276 int len;
277 void *ap;
9f8917b7 278 {
dcd51d39
MT
279 struct strpush *sp;
280
281 INTOFF;
282/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
283 if (parsefile->strpush) {
284 sp = ckmalloc(sizeof (struct strpush));
285 sp->prev = parsefile->strpush;
286 parsefile->strpush = sp;
287 } else
288 sp = parsefile->strpush = &(parsefile->basestrpush);
289 sp->prevstring = parsenextc;
290 sp->prevnleft = parsenleft;
291 sp->ap = (struct alias *)ap;
292 if (ap)
293 ((struct alias *)ap)->flag |= ALIASINUSE;
294 parsenextc = s;
295 parsenleft = len;
296 INTON;
9f8917b7
KB
297}
298
dcd51d39
MT
299popstring()
300{
301 struct strpush *sp = parsefile->strpush;
9f8917b7 302
dcd51d39
MT
303 INTOFF;
304 parsenextc = sp->prevstring;
305 parsenleft = sp->prevnleft;
306/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
307 if (sp->ap)
308 sp->ap->flag &= ~ALIASINUSE;
309 parsefile->strpush = sp->prev;
310 if (sp != &(parsefile->basestrpush))
311 ckfree(sp);
312 INTON;
313}
9f8917b7
KB
314
315/*
316 * Set the input to take input from a file. If push is set, push the
317 * old input onto the stack first.
318 */
319
320void
321setinputfile(fname, push)
322 char *fname;
323 {
324 int fd;
325 int fd2;
326
327 INTOFF;
328 if ((fd = open(fname, O_RDONLY)) < 0)
329 error("Can't open %s", fname);
330 if (fd < 10) {
331 fd2 = copyfd(fd, 10);
332 close(fd);
333 if (fd2 < 0)
334 error("Out of file descriptors");
335 fd = fd2;
336 }
337 setinputfd(fd, push);
338 INTON;
339}
340
341
342/*
343 * Like setinputfile, but takes an open file descriptor. Call this with
344 * interrupts off.
345 */
346
347void
348setinputfd(fd, push) {
349 if (push) {
350 pushfile();
351 parsefile->buf = ckmalloc(BUFSIZ);
352 }
353 if (parsefile->fd > 0)
354 close(parsefile->fd);
355 parsefile->fd = fd;
356 if (parsefile->buf == NULL)
357 parsefile->buf = ckmalloc(BUFSIZ);
358 parsenleft = 0;
359 plinno = 1;
360}
361
362
363/*
364 * Like setinputfile, but takes input from a string.
365 */
366
367void
368setinputstring(string, push)
369 char *string;
370 {
371 INTOFF;
372 if (push)
373 pushfile();
374 parsenextc = string;
375 parsenleft = strlen(string);
376 parsefile->buf = NULL;
377 plinno = 1;
378 INTON;
379}
380
381
382
383/*
384 * To handle the "." command, a stack of input files is used. Pushfile
385 * adds a new entry to the stack and popfile restores the previous level.
386 */
387
388STATIC void
389pushfile() {
390 struct parsefile *pf;
391
392 parsefile->nleft = parsenleft;
393 parsefile->nextc = parsenextc;
394 parsefile->linno = plinno;
395 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
396 pf->prev = parsefile;
397 pf->fd = -1;
dcd51d39
MT
398 pf->strpush = NULL;
399 pf->basestrpush.prev = NULL;
9f8917b7
KB
400 parsefile = pf;
401}
402
403
404void
405popfile() {
406 struct parsefile *pf = parsefile;
407
408 INTOFF;
409 if (pf->fd >= 0)
410 close(pf->fd);
411 if (pf->buf)
412 ckfree(pf->buf);
dcd51d39
MT
413 while (pf->strpush)
414 popstring();
9f8917b7
KB
415 parsefile = pf->prev;
416 ckfree(pf);
417 parsenleft = parsefile->nleft;
418 parsenextc = parsefile->nextc;
419 plinno = parsefile->linno;
420 INTON;
421}
422
423
424/*
425 * Return to top level.
426 */
427
428void
429popallfiles() {
430 while (parsefile != &basepf)
431 popfile();
432}
433
434
435
436/*
437 * Close the file(s) that the shell is reading commands from. Called
438 * after a fork is done.
439 */
440
441void
442closescript() {
443 popallfiles();
444 if (parsefile->fd > 0) {
445 close(parsefile->fd);
446 parsefile->fd = 0;
447 }
448}