Imported from ../bash-1.14.7.tar.gz.
[platform/upstream/bash.git] / input.c
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