source: trunk/src/kash/input.c@ 1210

Last change on this file since 1210 was 1210, checked in by bird, 18 years ago

var.c ++

  • Property svn:eol-style set to native
File size: 12.3 KB
Line 
1/* $NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc 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. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#ifdef HAVE_SYS_CDEFS_H
36#include <sys/cdefs.h>
37#endif
38#ifndef lint
39#if 0
40static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95";
41#else
42__RCSID("$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $");
43#endif
44#endif /* not lint */
45
46#include <stdio.h> /* defines BUFSIZ */
47#include <fcntl.h>
48#include <errno.h>
49#include <unistd.h>
50#include <stdlib.h>
51#include <string.h>
52#ifdef __sun__
53#include <iso/limits_iso.h>
54#endif
55
56/*
57 * This file implements the input routines used by the parser.
58 */
59
60#include "shell.h"
61#include "redir.h"
62#include "syntax.h"
63#include "input.h"
64#include "output.h"
65#include "options.h"
66#include "memalloc.h"
67#include "error.h"
68#include "alias.h"
69#include "parser.h"
70#include "myhistedit.h"
71#include "shinstance.h"
72
73#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
74
75//MKINIT
76//struct strpush {
77// struct strpush *prev; /* preceding string on stack */
78// char *prevstring;
79// int prevnleft;
80// int prevlleft;
81// struct alias *ap; /* if push was associated with an alias */
82//};
83//
84///*
85// * The parsefile structure pointed to by the global variable parsefile
86// * contains information about the current file being read.
87// */
88//
89//MKINIT
90//struct parsefile {
91// struct parsefile *prev; /* preceding file on stack */
92// int linno; /* current line */
93// int fd; /* file descriptor (or -1 if string) */
94// int nleft; /* number of chars left in this line */
95// int lleft; /* number of chars left in this buffer */
96// char *nextc; /* next char in buffer */
97// char *buf; /* input buffer */
98// struct strpush *strpush; /* for pushing strings at this level */
99// struct strpush basestrpush; /* so pushing one is fast */
100//};
101//
102//
103//int plinno = 1; /* input line number */
104//int parsenleft; /* copy of parsefile->nleft */
105//MKINIT int parselleft; /* copy of parsefile->lleft */
106//char *parsenextc; /* copy of parsefile->nextc */
107//MKINIT struct parsefile basepf; /* top level input file */
108//MKINIT char basebuf[BUFSIZ]; /* buffer for top level input file */
109//struct parsefile *parsefile = &basepf; /* current input file */
110//int init_editline = 0; /* editline library initialized? */
111//int whichprompt; /* 1 == PS1, 2 == PS2 */
112//
113//#ifndef SMALL
114//EditLine *el; /* cookie for editline package */
115//#endif
116
117STATIC void pushfile(shinstance *psh);
118static int preadfd(shinstance *psh);
119
120#ifdef mkinit
121INCLUDE <stdio.h>
122INCLUDE "input.h"
123INCLUDE "error.h"
124
125INIT {
126 psh->basepf.nextc = psh->basepf.buf = psh->basebuf;
127}
128
129RESET {
130 if (psh->exception != EXSHELLPROC)
131 psh->parselleft = psh->parsenleft = 0; /* clear input buffer */
132 popallfiles(psh);
133}
134
135SHELLPROC {
136 popallfiles(psh);
137}
138#endif
139
140
141/*
142 * Read a line from the script.
143 */
144
145char *
146pfgets(shinstance *psh, char *line, int len)
147{
148 char *p = line;
149 int nleft = len;
150 int c;
151
152 while (--nleft > 0) {
153 c = pgetc_macro(psh);
154 if (c == PEOF) {
155 if (p == line)
156 return NULL;
157 break;
158 }
159 *p++ = c;
160 if (c == '\n')
161 break;
162 }
163 *p = '\0';
164 return line;
165}
166
167
168
169/*
170 * Read a character from the script, returning PEOF on end of file.
171 * Nul characters in the input are silently discarded.
172 */
173
174int
175pgetc(shinstance *psh)
176{
177 return pgetc_macro(psh);
178}
179
180
181static int
182preadfd(shinstance *psh)
183{
184 int nr;
185 char *buf = psh->parsefile->buf;
186 psh->parsenextc = buf;
187
188retry:
189#ifndef SMALL
190 if (psh->parsefile->fd == 0 && psh->el) {
191 static const char *rl_cp;
192 static int el_len;
193
194 if (rl_cp == NULL)
195 rl_cp = el_gets(psh->el, &el_len);
196 if (rl_cp == NULL)
197 nr = 0;
198 else {
199 nr = el_len;
200 if (nr > BUFSIZ - 8)
201 nr = BUFSIZ - 8;
202 memcpy(buf, rl_cp, nr);
203 if (nr != el_len) {
204 el_len -= nr;
205 rl_cp += nr;
206 } else
207 rl_cp = 0;
208 }
209
210 } else
211#endif
212 nr = shfile_read(&psh->fdtab, psh->parsefile->fd, buf, BUFSIZ - 8);
213
214
215 if (nr <= 0) {
216 if (nr < 0) {
217 if (errno == EINTR)
218 goto retry;
219 if (psh->parsefile->fd == 0 && errno == EWOULDBLOCK) {
220 int flags = shfile_fcntl(&psh->fdtab, 0, F_GETFL, 0);
221 if (flags >= 0 && flags & O_NONBLOCK) {
222 flags &=~ O_NONBLOCK;
223 if (shfile_fcntl(&psh->fdtab, 0, F_SETFL, flags) >= 0) {
224 out2str(psh, "sh: turning off NDELAY mode\n");
225 goto retry;
226 }
227 }
228 }
229 }
230 nr = -1;
231 }
232 return nr;
233}
234
235/*
236 * Refill the input buffer and return the next input character:
237 *
238 * 1) If a string was pushed back on the input, pop it;
239 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
240 * from a string so we can't refill the buffer, return EOF.
241 * 3) If the is more stuff in this buffer, use it else call read to fill it.
242 * 4) Process input up to the next newline, deleting nul characters.
243 */
244
245int
246preadbuffer(shinstance *psh)
247{
248 char *p, *q;
249 int more;
250 int something;
251 char savec;
252
253 if (psh->parsefile->strpush) {
254 popstring(psh);
255 if (--psh->parsenleft >= 0)
256 return (*psh->parsenextc++);
257 }
258 if (psh->parsenleft == EOF_NLEFT || psh->parsefile->buf == NULL)
259 return PEOF;
260 flushout(&psh->output);
261 flushout(&psh->errout);
262
263again:
264 if (psh->parselleft <= 0) {
265 if ((psh->parselleft = preadfd(psh)) == -1) {
266 psh->parselleft = psh->parsenleft = EOF_NLEFT;
267 return PEOF;
268 }
269 }
270
271 q = p = psh->parsenextc;
272
273 /* delete nul characters */
274 something = 0;
275 for (more = 1; more;) {
276 switch (*p) {
277 case '\0':
278 p++; /* Skip nul */
279 goto check;
280
281 case '\t':
282 case ' ':
283 break;
284
285 case '\n':
286 psh->parsenleft = (int)(q - psh->parsenextc);
287 more = 0; /* Stop processing here */
288 break;
289
290 default:
291 something = 1;
292 break;
293 }
294
295 *q++ = *p++;
296check:
297 if (--psh->parselleft <= 0) {
298 psh->parsenleft = (int)(q - psh->parsenextc - 1);
299 if (psh->parsenleft < 0)
300 goto again;
301 *q = '\0';
302 more = 0;
303 }
304 }
305
306 savec = *q;
307 *q = '\0';
308
309#ifndef SMALL
310 if (psh->parsefile->fd == 0 && hist && something) {
311 HistEvent he;
312 INTOFF;
313 history(hist, &he, psh->whichprompt == 1? H_ENTER : H_APPEND,
314 psh->parsenextc);
315 INTON;
316 }
317#endif
318
319 if (vflag(psh)) {
320 out2str(psh, psh->parsenextc);
321 flushout(psh->out2);
322 }
323
324 *q = savec;
325
326 return *psh->parsenextc++;
327}
328
329/*
330 * Undo the last call to pgetc. Only one character may be pushed back.
331 * PEOF may be pushed back.
332 */
333
334void
335pungetc(shinstance *psh)
336{
337 psh->parsenleft++;
338 psh->parsenextc--;
339}
340
341/*
342 * Push a string back onto the input at this current parsefile level.
343 * We handle aliases this way.
344 */
345void
346pushstring(shinstance *psh, char *s, size_t len, void *ap)
347{
348 struct strpush *sp;
349
350 INTOFF;
351/*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
352 if (psh->parsefile->strpush) {
353 sp = ckmalloc(sizeof (struct strpush));
354 sp->prev = psh->parsefile->strpush;
355 psh->parsefile->strpush = sp;
356 } else
357 sp = psh->parsefile->strpush = &(psh->parsefile->basestrpush);
358 sp->prevstring = psh->parsenextc;
359 sp->prevnleft = psh->parsenleft;
360 sp->prevlleft = psh->parselleft;
361 sp->ap = (struct alias *)ap;
362 if (ap)
363 ((struct alias *)ap)->flag |= ALIASINUSE;
364 psh->parsenextc = s;
365 psh->parsenleft = (int)len;
366 INTON;
367}
368
369void
370popstring(shinstance *psh)
371{
372 struct strpush *sp = psh->parsefile->strpush;
373
374 INTOFF;
375 psh->parsenextc = sp->prevstring;
376 psh->parsenleft = sp->prevnleft;
377 psh->parselleft = sp->prevlleft;
378/*dprintf("*** calling popstring: restoring to '%s'\n", psh->parsenextc);*/
379 if (sp->ap)
380 sp->ap->flag &= ~ALIASINUSE;
381 psh->parsefile->strpush = sp->prev;
382 if (sp != &(psh->parsefile->basestrpush))
383 ckfree(sp);
384 INTON;
385}
386
387/*
388 * Set the input to take input from a file. If push is set, push the
389 * old input onto the stack first.
390 */
391
392void
393setinputfile(shinstance *psh, const char *fname, int push)
394{
395 int fd;
396 int fd2;
397
398 INTOFF;
399/** @todo shfile fixme */
400 if ((fd = shfile_open(&psh->fdtab, fname, O_RDONLY)) < 0)
401 error(psh, "Can't open %s", fname);
402 if (fd < 10) {
403 fd2 = copyfd(psh, fd, 10);
404 shfile_close(&psh->fdtab, fd);
405 if (fd2 < 0)
406 error(psh, "Out of file descriptors");
407 fd = fd2;
408 }
409 setinputfd(psh, fd, push);
410 INTON;
411}
412
413
414/*
415 * Like setinputfile, but takes an open file descriptor. Call this with
416 * interrupts off.
417 */
418
419void
420setinputfd(shinstance *psh, int fd, int push)
421{
422 (void) shfile_fcntl(&psh->fdtab, fd, F_SETFD, FD_CLOEXEC);
423 if (push) {
424 pushfile(psh);
425 psh->parsefile->buf = ckmalloc(BUFSIZ);
426 }
427 if (psh->parsefile->fd > 0)
428 shfile_close(&psh->fdtab, psh->parsefile->fd);
429 psh->parsefile->fd = fd;
430 if (psh->parsefile->buf == NULL)
431 psh->parsefile->buf = ckmalloc(BUFSIZ);
432 psh->parselleft = psh->parsenleft = 0;
433 psh->plinno = 1;
434}
435
436
437/*
438 * Like setinputfile, but takes input from a string.
439 */
440
441void
442setinputstring(shinstance *psh, char *string, int push)
443{
444 INTOFF;
445 if (push)
446 pushfile(psh);
447 psh->parsenextc = string;
448 psh->parselleft = psh->parsenleft = (int)strlen(string);
449 psh->parsefile->buf = NULL;
450 psh->plinno = 1;
451 INTON;
452}
453
454
455
456/*
457 * To handle the "." command, a stack of input files is used. Pushfile
458 * adds a new entry to the stack and popfile restores the previous level.
459 */
460
461STATIC void
462pushfile(shinstance *psh)
463{
464 struct parsefile *pf;
465
466 psh->parsefile->nleft = psh->parsenleft;
467 psh->parsefile->lleft = psh->parselleft;
468 psh->parsefile->nextc = psh->parsenextc;
469 psh->parsefile->linno = psh->plinno;
470 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
471 pf->prev = psh->parsefile;
472 pf->fd = -1;
473 pf->strpush = NULL;
474 pf->basestrpush.prev = NULL;
475 psh->parsefile = pf;
476}
477
478
479void
480popfile(shinstance *psh)
481{
482 struct parsefile *pf = psh->parsefile;
483
484 INTOFF;
485 if (pf->fd >= 0)
486 shfile_close(&psh->fdtab, pf->fd);
487 if (pf->buf)
488 ckfree(pf->buf);
489 while (pf->strpush)
490 popstring(psh);
491 psh->parsefile = pf->prev;
492 ckfree(pf);
493 psh->parsenleft = psh->parsefile->nleft;
494 psh->parselleft = psh->parsefile->lleft;
495 psh->parsenextc = psh->parsefile->nextc;
496 psh->plinno = psh->parsefile->linno;
497 INTON;
498}
499
500
501/*
502 * Return to top level.
503 */
504
505void
506popallfiles(shinstance *psh)
507{
508 while (psh->parsefile != &psh->basepf)
509 popfile(psh);
510}
511
512
513
514/*
515 * Close the file(s) that the shell is reading commands from. Called
516 * after a fork is done.
517 *
518 * Takes one arg, vfork, which tells it to not modify its global vars
519 * as it is still running in the parent.
520 *
521 * This code is (probably) unnecessary as the 'close on exec' flag is
522 * set and should be enough. In the vfork case it is definitely wrong
523 * to close the fds as another fork() may be done later to feed data
524 * from a 'here' document into a pipe and we don't want to close the
525 * pipe!
526 */
527
528void
529closescript(shinstance *psh, int vforked)
530{
531 if (vforked)
532 return;
533 popallfiles(psh);
534 if (psh->parsefile->fd > 0) {
535 shfile_close(&psh->fdtab, psh->parsefile->fd);
536 psh->parsefile->fd = 0;
537 }
538}
Note: See TracBrowser for help on using the repository browser.