]>
Commit | Line | Data |
---|---|---|
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) | |
37 | extern 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 | ||
46 | void free_buffered_stream (); | |
47 | ||
48 | extern int interactive_shell; | |
49 | ||
50 | int 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. */ | |
56 | BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL; | |
57 | static 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. */ | |
65 | static void | |
66 | allocate_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. */ | |
83 | static BUFFERED_STREAM * | |
84 | make_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. */ | |
106 | static BUFFERED_STREAM * | |
107 | copy_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. */ | |
127 | int | |
128 | check_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. */ | |
188 | duplicate_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. */ | |
226 | BUFFERED_STREAM * | |
227 | fd_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. */ | |
252 | BUFFERED_STREAM * | |
253 | open_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. */ | |
266 | void | |
267 | free_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. */ | |
284 | int | |
285 | close_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. */ | |
299 | int | |
300 | close_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. */ | |
309 | static int | |
310 | b_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. */ | |
338 | static int | |
339 | bufstream_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. */ | |
352 | int | |
353 | sync_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 | ||
369 | int | |
370 | buffered_getchar () | |
371 | { | |
372 | return (bufstream_getc (buffers[bash_input.location.buffered_fd])); | |
373 | } | |
374 | ||
375 | int | |
376 | buffered_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. */ | |
383 | void | |
384 | with_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 | ||
398 | char * | |
399 | xmalloc(s) | |
400 | int s; | |
401 | { | |
402 | return ((char *)malloc (s)); | |
403 | } | |
404 | ||
405 | char * | |
406 | xrealloc(s, size) | |
407 | char *s; | |
408 | int size; | |
409 | { | |
410 | if (!s) | |
411 | return((char *)malloc (size)); | |
412 | else | |
413 | return((char *)realloc (s, size)); | |
414 | } | |
415 | ||
416 | void | |
417 | init_yy_io () | |
418 | { | |
419 | } | |
420 | ||
421 | process(bp) | |
422 | BUFFERED_STREAM *bp; | |
423 | { | |
424 | int c; | |
425 | ||
426 | while ((c = bufstream_getc(bp)) != EOF) | |
427 | putchar(c); | |
428 | } | |
429 | ||
430 | BASH_INPUT bash_input; | |
431 | ||
432 | struct stat dsb; /* can be used from gdb */ | |
433 | ||
434 | /* imitate /bin/cat */ | |
435 | main(argc, argv) | |
436 | int argc; | |
437 | char **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 |