]> git.ipfire.org Git - thirdparty/bash.git/blame - input.c
Imported from ../bash-2.01.1.tar.gz.
[thirdparty/bash.git] / input.c
CommitLineData
726f6388
JA
1/* input.c -- functions to perform buffered input with synchronization. */
2
3/* Copyright (C) 1992 Free Software Foundation, Inc.
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
7 Bash is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with Bash; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
ccc6cda3 21#include "config.h"
726f6388
JA
22
23#include "bashtypes.h"
24#include <sys/file.h>
25#include "filecntl.h"
26#include "posixstat.h"
27#include <stdio.h>
28#include <errno.h>
29
ccc6cda3
JA
30#if defined (HAVE_UNISTD_H)
31# include <unistd.h>
32#endif
33
726f6388 34#include "bashansi.h"
726f6388
JA
35#include "command.h"
36#include "general.h"
37#include "input.h"
ccc6cda3
JA
38#include "error.h"
39#include "externs.h"
726f6388
JA
40
41#if !defined (errno)
42extern int errno;
43#endif /* !errno */
44
ccc6cda3
JA
45/* Functions to handle reading input on systems that don't restart read(2)
46 if a signal is received. */
47
48#if !defined (HAVE_RESTARTABLE_SYSCALLS)
49static unsigned char localbuf[128];
50static int local_index, local_bufused;
51
52/* Posix and USG systems do not guarantee to restart read () if it is
53 interrupted by a signal. We do the read ourselves, and restart it
54 if it returns EINTR. */
55int
56getc_with_restart (stream)
57 FILE *stream;
58{
59 /* Try local buffering to reduce the number of read(2) calls. */
60 if (local_index == local_bufused || local_bufused == 0)
61 {
62 while (1)
63 {
64 local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
65 if (local_bufused > 0)
66 break;
67 else if (local_bufused == 0 || errno != EINTR)
68 {
69 local_index = 0;
70 return EOF;
71 }
72 }
73 local_index = 0;
74 }
75 return (localbuf[local_index++]);
76}
77
78int
79ungetc_with_restart (c, stream)
80 int c;
81 FILE *stream;
82{
83 if (local_index == 0 || c == EOF)
84 return EOF;
85 return (localbuf[--local_index] = c);
86}
87#endif /* !HAVE_RESTARTABLE_SYSCALLS */
88
89#if defined (BUFFERED_INPUT)
90
91/* A facility similar to stdio, but input-only. */
92
726f6388
JA
93#define MAX_INPUT_BUFFER_SIZE 8192
94
95#if !defined (SEEK_CUR)
96# define SEEK_CUR 1
97#endif /* !SEEK_CUR */
98
99void free_buffered_stream ();
100
ccc6cda3
JA
101extern int return_EOF ();
102
726f6388
JA
103extern int interactive_shell;
104
105int bash_input_fd_changed;
ccc6cda3 106
726f6388
JA
107/* This provides a way to map from a file descriptor to the buffer
108 associated with that file descriptor, rather than just the other
109 way around. This is needed so that buffers are managed properly
110 in constructs like 3<&4. buffers[x]->b_fd == x -- that is how the
111 correspondence is maintained. */
112BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL;
ccc6cda3 113static int nbuffers;
726f6388
JA
114
115#define max(a, b) (((a) > (b)) ? (a) : (b))
116
117#define ALLOCATE_BUFFERS(n) \
118 do { if ((n) >= nbuffers) allocate_buffers (n); } while (0)
119
120/* Make sure `buffers' has at least N elements. */
121static void
122allocate_buffers (n)
123 int n;
124{
125 register int i, orig_nbuffers;
126
127 orig_nbuffers = nbuffers;
128 nbuffers = n + 20;
129 buffers = (BUFFERED_STREAM **)xrealloc
130 (buffers, nbuffers * sizeof (BUFFERED_STREAM *));
131
132 /* Zero out the new buffers. */
133 for (i = orig_nbuffers; i < nbuffers; i++)
134 buffers[i] = (BUFFERED_STREAM *)NULL;
135}
136
137/* Construct and return a BUFFERED_STREAM corresponding to file descriptor
138 FD, using BUFFER. */
139static BUFFERED_STREAM *
140make_buffered_stream (fd, buffer, bufsize)
141 int fd;
142 char *buffer;
143 int bufsize;
144{
145 BUFFERED_STREAM *bp;
146
147 bp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
148 ALLOCATE_BUFFERS (fd);
149 buffers[fd] = bp;
150 bp->b_fd = fd;
151 bp->b_buffer = buffer;
152 bp->b_size = bufsize;
ccc6cda3 153 bp->b_used = bp->b_inputp = bp->b_flag = 0;
726f6388
JA
154 if (bufsize == 1)
155 bp->b_flag |= B_UNBUFF;
156 return (bp);
157}
158
159/* Allocate a new BUFFERED_STREAM, copy BP to it, and return the new copy. */
160static BUFFERED_STREAM *
161copy_buffered_stream (bp)
162 BUFFERED_STREAM *bp;
163{
164 BUFFERED_STREAM *nbp;
165
166 if (!bp)
167 return ((BUFFERED_STREAM *)NULL);
168
169 nbp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
170 xbcopy ((char *)bp, (char *)nbp, sizeof (BUFFERED_STREAM));
171 return (nbp);
172}
173
174/* Check that file descriptor FD is not the one that bash is currently
175 using to read input from a script. FD is about to be duplicated onto,
176 which means that the kernel will close it for us. If FD is the bash
177 input file descriptor, we need to seek backwards in the script (if
178 possible and necessary -- scripts read from stdin are still unbuffered),
179 allocate a new file descriptor to use for bash input, and re-initialize
180 the buffered stream. */
181int
182check_bash_input (fd)
183 int fd;
184{
185 int nfd;
186
187 if (fd > 0 && ((bash_input.type == st_bstream && bash_input.location.buffered_fd == fd) ||
188 (interactive_shell == 0 && default_buffered_input == fd)))
189 {
190 /* Sync the stream so we can re-read from the new file descriptor. We
191 might be able to avoid this by copying the buffered stream verbatim
192 to the new file descriptor. */
193 if (buffers[fd])
194 sync_buffered_stream (fd);
195
196 /* Now take care of duplicating the file descriptor that bash is
197 using for input, so we can reinitialize it later. */
198 nfd = fcntl (fd, F_DUPFD, 10);
199 if (nfd == -1)
200 {
201 if (fcntl (fd, F_GETFD, 0) == 0)
ccc6cda3 202 sys_error ("cannot allocate new file descriptor for bash input from fd %d", fd);
726f6388
JA
203 return -1;
204 }
205
206 if (buffers[nfd])
207 {
208 /* What's this? A stray buffer without an associated open file
209 descriptor? Free up the buffer and report the error. */
ccc6cda3 210 internal_error ("check_bash_input: buffer already exists for new fd %d", nfd);
726f6388
JA
211 free_buffered_stream (buffers[nfd]);
212 }
213
214 /* Reinitialize bash_input.location. */
215 if (bash_input.type == st_bstream)
216 {
217 bash_input.location.buffered_fd = nfd;
218 fd_to_buffered_stream (nfd);
219 close_buffered_fd (fd); /* XXX */
220 }
221 else
222 /* If the current input type is not a buffered stream, but the shell
223 is not interactive and therefore using a buffered stream to read
224 input (e.g. with an `eval exec 3>output' inside a script), note
225 that the input fd has been changed. pop_stream() looks at this
226 value and adjusts the input fd to the new value of
227 default_buffered_input accordingly. */
228 bash_input_fd_changed++;
229
230 if (default_buffered_input == fd)
231 default_buffered_input = nfd;
232 }
233 return 0;
234}
235
236/* This is the buffered stream analogue of dup2(fd1, fd2). The
237 BUFFERED_STREAM corresponding to fd2 is deallocated, if one exists.
238 BUFFERS[fd1] is copied to BUFFERS[fd2]. This is called by the
239 redirect code for constructs like 4<&0 and 3</etc/rc.local. */
ccc6cda3 240int
726f6388
JA
241duplicate_buffered_stream (fd1, fd2)
242 int fd1, fd2;
243{
244 int is_bash_input, m;
245
246 if (fd1 == fd2)
247 return 0;
248
249 m = max (fd1, fd2);
250 ALLOCATE_BUFFERS (m);
251
252 /* If FD2 is the file descriptor bash is currently using for shell input,
253 we need to do some extra work to make sure that the buffered stream
254 actually exists (it might not if fd1 was not active, and the copy
255 didn't actually do anything). */
256 is_bash_input = (bash_input.type == st_bstream) &&
257 (bash_input.location.buffered_fd == fd2);
258
259 if (buffers[fd2])
260 free_buffered_stream (buffers[fd2]);
261 buffers[fd2] = copy_buffered_stream (buffers[fd1]);
262 if (buffers[fd2])
263 buffers[fd2]->b_fd = fd2;
264
ccc6cda3
JA
265 if (is_bash_input && !buffers[fd2])
266 fd_to_buffered_stream (fd2);
267
726f6388
JA
268 return (fd2);
269}
270
271/* Return 1 if a seek on FD will succeed. */
272#define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0)
273
274/* Take FD, a file descriptor, and create and return a buffered stream
275 corresponding to it. If something is wrong and the file descriptor
276 is invalid, return a NULL stream. */
277BUFFERED_STREAM *
278fd_to_buffered_stream (fd)
279 int fd;
280{
281 char *buffer;
282 int size;
283 struct stat sb;
284
285 if (fstat (fd, &sb) < 0)
286 {
287 close (fd);
288 return ((BUFFERED_STREAM *)NULL);
289 }
290
291 if (fd_is_seekable (fd) == 0)
292 size = 1;
293 else
294 size = (sb.st_size > MAX_INPUT_BUFFER_SIZE) ? MAX_INPUT_BUFFER_SIZE
295 : sb.st_size;
296
297 buffer = (char *)xmalloc (size);
298
299 return (make_buffered_stream (fd, buffer, size));
300}
301
302/* Return a buffered stream corresponding to FILE, a file name. */
303BUFFERED_STREAM *
304open_buffered_stream (file)
305 char *file;
306{
307 int fd;
308
309 fd = open (file, O_RDONLY);
ccc6cda3 310 return ((fd >= 0) ? fd_to_buffered_stream (fd) : (BUFFERED_STREAM *)NULL);
726f6388
JA
311}
312
313/* Deallocate a buffered stream and free up its resources. Make sure we
314 zero out the slot in BUFFERS that points to BP. */
315void
316free_buffered_stream (bp)
317 BUFFERED_STREAM *bp;
318{
319 int n;
320
321 if (!bp)
322 return;
323
324 n = bp->b_fd;
325 if (bp->b_buffer)
326 free (bp->b_buffer);
327 free (bp);
328 buffers[n] = (BUFFERED_STREAM *)NULL;
329}
330
331/* Close the file descriptor associated with BP, a buffered stream, and free
332 up the stream. Return the status of closing BP's file descriptor. */
333int
334close_buffered_stream (bp)
335 BUFFERED_STREAM *bp;
336{
337 int fd;
338
339 if (!bp)
340 return (0);
341 fd = bp->b_fd;
342 free_buffered_stream (bp);
343 return (close (fd));
344}
345
346/* Deallocate the buffered stream associated with file descriptor FD, and
347 close FD. Return the status of the close on FD. */
348int
349close_buffered_fd (fd)
350 int fd;
351{
352 if (fd >= nbuffers || !buffers || !buffers[fd])
353 return (close (fd));
354 return (close_buffered_stream (buffers[fd]));
355}
356
357/* Read a buffer full of characters from BP, a buffered stream. */
358static int
359b_fill_buffer (bp)
360 BUFFERED_STREAM *bp;
361{
362 do
363 {
364 bp->b_used = read (bp->b_fd, bp->b_buffer, bp->b_size);
365 }
366 while (bp->b_used < 0 && errno == EINTR);
367 if (bp->b_used <= 0)
368 {
369 bp->b_buffer[0] = 0;
370 if (bp->b_used == 0)
371 bp->b_flag |= B_EOF;
372 else
373 bp->b_flag |= B_ERROR;
374 return (EOF);
375 }
376 bp->b_inputp = 0;
377 return (bp->b_buffer[bp->b_inputp++] & 0xFF);
378}
379
380/* Get a character from buffered stream BP. */
381#define bufstream_getc(bp) \
382 (bp->b_inputp == bp->b_used || !bp->b_used) \
383 ? b_fill_buffer (bp) \
384 : bp->b_buffer[bp->b_inputp++] & 0xFF
385
386/* Push C back onto buffered stream BP. */
387static int
388bufstream_ungetc(c, bp)
389 int c;
390 BUFFERED_STREAM *bp;
391{
392 if (c == EOF || bp->b_inputp == 0)
393 return (EOF);
394
395 bp->b_buffer[--bp->b_inputp] = c;
396 return (c);
397}
398
399/* Seek backwards on file BFD to synchronize what we've read so far
400 with the underlying file pointer. */
401int
402sync_buffered_stream (bfd)
403 int bfd;
404{
405 BUFFERED_STREAM *bp;
ccc6cda3 406 off_t chars_left;
726f6388
JA
407
408 bp = buffers[bfd];
409 if (!bp)
410 return (-1);
411 chars_left = bp->b_used - bp->b_inputp;
412 if (chars_left)
413 lseek (bp->b_fd, -chars_left, SEEK_CUR);
414 bp->b_used = bp->b_inputp = 0;
415 return (0);
416}
417
418int
419buffered_getchar ()
420{
421 return (bufstream_getc (buffers[bash_input.location.buffered_fd]));
422}
423
424int
425buffered_ungetchar (c)
426 int c;
427{
428 return (bufstream_ungetc (c, buffers[bash_input.location.buffered_fd]));
429}
430
431/* Make input come from file descriptor BFD through a buffered stream. */
432void
433with_input_from_buffered_stream (bfd, name)
434 int bfd;
435 char *name;
436{
437 INPUT_STREAM location;
ccc6cda3 438 BUFFERED_STREAM *bp;
726f6388
JA
439
440 location.buffered_fd = bfd;
441 /* Make sure the buffered stream exists. */
ccc6cda3
JA
442 bp = fd_to_buffered_stream (bfd);
443 init_yy_io (bp == 0 ? return_EOF : buffered_getchar,
444 buffered_ungetchar, st_bstream, name, location);
726f6388
JA
445}
446
447#if defined (TEST)
726f6388
JA
448char *
449xmalloc(s)
450int s;
451{
452 return ((char *)malloc (s));
453}
454
455char *
456xrealloc(s, size)
457char *s;
458int size;
459{
460 if (!s)
461 return((char *)malloc (size));
462 else
463 return((char *)realloc (s, size));
464}
465
466void
467init_yy_io ()
468{
469}
470
471process(bp)
472BUFFERED_STREAM *bp;
473{
474 int c;
475
476 while ((c = bufstream_getc(bp)) != EOF)
477 putchar(c);
478}
479
480BASH_INPUT bash_input;
481
482struct stat dsb; /* can be used from gdb */
483
484/* imitate /bin/cat */
485main(argc, argv)
486int argc;
487char **argv;
488{
489 register int i;
490 BUFFERED_STREAM *bp;
491
492 if (argc == 1) {
493 bp = fd_to_buffered_stream (0);
494 process(bp);
495 exit(0);
496 }
497 for (i = 1; i < argc; i++) {
498 if (argv[i][0] == '-' && argv[i][1] == '\0') {
499 bp = fd_to_buffered_stream (0);
500 if (!bp)
501 continue;
502 process(bp);
503 free_buffered_stream (bp);
504 } else {
505 bp = open_buffered_stream (argv[i]);
506 if (!bp)
507 continue;
508 process(bp);
509 close_buffered_stream (bp);
510 }
511 }
512 exit(0);
513}
ccc6cda3
JA
514#endif /* TEST */
515#endif /* BUFFERED_INPUT */