Subversion Repositories HelenOS

Rev

Rev 2714 | Details | Compare with Previous | Last modification | View Log | RSS feed

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