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