blob: c4f335d58dbb77b712769d802d078c04da1f63a3 [file] [log] [blame]
Jari Aalto726f6381996-08-26 18:22:31 +00001/* 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
Jari Aaltobb706242000-03-17 21:46:59 +000019 Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
Jari Aalto726f6381996-08-26 18:22:31 +000020
Jari Aaltoccc6cda1996-12-23 17:02:34 +000021#include "config.h"
Jari Aalto726f6381996-08-26 18:22:31 +000022
23#include "bashtypes.h"
Jari Aaltob80f6442004-07-27 13:29:18 +000024#if !defined (_MINIX) && defined (HAVE_SYS_FILE_H)
Jari Aaltocce855b1998-04-17 19:52:44 +000025# include <sys/file.h>
26#endif
Jari Aalto726f6381996-08-26 18:22:31 +000027#include "filecntl.h"
28#include "posixstat.h"
29#include <stdio.h>
30#include <errno.h>
31
Jari Aaltoccc6cda1996-12-23 17:02:34 +000032#if defined (HAVE_UNISTD_H)
33# include <unistd.h>
34#endif
35
Jari Aalto726f6381996-08-26 18:22:31 +000036#include "bashansi.h"
Jari Aaltob80f6442004-07-27 13:29:18 +000037#include "bashintl.h"
38
Jari Aalto726f6381996-08-26 18:22:31 +000039#include "command.h"
40#include "general.h"
41#include "input.h"
Jari Aaltoccc6cda1996-12-23 17:02:34 +000042#include "error.h"
43#include "externs.h"
Jari Aalto726f6381996-08-26 18:22:31 +000044
45#if !defined (errno)
46extern int errno;
47#endif /* !errno */
48
Jari Aaltoccc6cda1996-12-23 17:02:34 +000049/* Functions to handle reading input on systems that don't restart read(2)
50 if a signal is received. */
51
Jari Aaltof73dda02001-11-13 17:56:06 +000052static char localbuf[128];
Jari Aaltoccc6cda1996-12-23 17:02:34 +000053static int local_index, local_bufused;
54
55/* Posix and USG systems do not guarantee to restart read () if it is
56 interrupted by a signal. We do the read ourselves, and restart it
57 if it returns EINTR. */
58int
59getc_with_restart (stream)
60 FILE *stream;
61{
Jari Aaltof73dda02001-11-13 17:56:06 +000062 unsigned char uc;
63
Jari Aaltoccc6cda1996-12-23 17:02:34 +000064 /* Try local buffering to reduce the number of read(2) calls. */
65 if (local_index == local_bufused || local_bufused == 0)
66 {
67 while (1)
68 {
69 local_bufused = read (fileno (stream), localbuf, sizeof(localbuf));
70 if (local_bufused > 0)
71 break;
72 else if (local_bufused == 0 || errno != EINTR)
73 {
74 local_index = 0;
75 return EOF;
76 }
77 }
78 local_index = 0;
79 }
Jari Aaltof73dda02001-11-13 17:56:06 +000080 uc = localbuf[local_index++];
81 return uc;
Jari Aaltoccc6cda1996-12-23 17:02:34 +000082}
83
84int
85ungetc_with_restart (c, stream)
86 int c;
87 FILE *stream;
88{
89 if (local_index == 0 || c == EOF)
90 return EOF;
Jari Aaltof73dda02001-11-13 17:56:06 +000091 localbuf[--local_index] = c;
92 return c;
Jari Aaltoccc6cda1996-12-23 17:02:34 +000093}
Jari Aaltoccc6cda1996-12-23 17:02:34 +000094
95#if defined (BUFFERED_INPUT)
96
97/* A facility similar to stdio, but input-only. */
98
Jari Aaltobb706242000-03-17 21:46:59 +000099#if defined (USING_BASH_MALLOC)
100# define MAX_INPUT_BUFFER_SIZE 8176
101#else
102# define MAX_INPUT_BUFFER_SIZE 8192
103#endif
Jari Aalto726f6381996-08-26 18:22:31 +0000104
105#if !defined (SEEK_CUR)
106# define SEEK_CUR 1
107#endif /* !SEEK_CUR */
108
Jari Aalto28ef6c32001-04-06 19:14:31 +0000109#ifdef max
110# undef max
111#endif
112#define max(a, b) (((a) > (b)) ? (a) : (b))
113#ifdef min
114# undef min
115#endif
116#define min(a, b) ((a) > (b) ? (b) : (a))
117
Jari Aalto726f6381996-08-26 18:22:31 +0000118extern int interactive_shell;
119
120int bash_input_fd_changed;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000121
Jari Aalto726f6381996-08-26 18:22:31 +0000122/* This provides a way to map from a file descriptor to the buffer
123 associated with that file descriptor, rather than just the other
124 way around. This is needed so that buffers are managed properly
125 in constructs like 3<&4. buffers[x]->b_fd == x -- that is how the
126 correspondence is maintained. */
Jari Aaltocce855b1998-04-17 19:52:44 +0000127static BUFFERED_STREAM **buffers = (BUFFERED_STREAM **)NULL;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000128static int nbuffers;
Jari Aalto726f6381996-08-26 18:22:31 +0000129
Jari Aalto726f6381996-08-26 18:22:31 +0000130#define ALLOCATE_BUFFERS(n) \
131 do { if ((n) >= nbuffers) allocate_buffers (n); } while (0)
132
133/* Make sure `buffers' has at least N elements. */
134static void
135allocate_buffers (n)
136 int n;
137{
138 register int i, orig_nbuffers;
139
140 orig_nbuffers = nbuffers;
141 nbuffers = n + 20;
142 buffers = (BUFFERED_STREAM **)xrealloc
143 (buffers, nbuffers * sizeof (BUFFERED_STREAM *));
144
145 /* Zero out the new buffers. */
146 for (i = orig_nbuffers; i < nbuffers; i++)
147 buffers[i] = (BUFFERED_STREAM *)NULL;
148}
149
150/* Construct and return a BUFFERED_STREAM corresponding to file descriptor
151 FD, using BUFFER. */
152static BUFFERED_STREAM *
153make_buffered_stream (fd, buffer, bufsize)
154 int fd;
155 char *buffer;
Jari Aaltocce855b1998-04-17 19:52:44 +0000156 size_t bufsize;
Jari Aalto726f6381996-08-26 18:22:31 +0000157{
158 BUFFERED_STREAM *bp;
159
160 bp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
161 ALLOCATE_BUFFERS (fd);
162 buffers[fd] = bp;
163 bp->b_fd = fd;
164 bp->b_buffer = buffer;
165 bp->b_size = bufsize;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000166 bp->b_used = bp->b_inputp = bp->b_flag = 0;
Jari Aalto726f6381996-08-26 18:22:31 +0000167 if (bufsize == 1)
168 bp->b_flag |= B_UNBUFF;
169 return (bp);
170}
171
172/* Allocate a new BUFFERED_STREAM, copy BP to it, and return the new copy. */
173static BUFFERED_STREAM *
174copy_buffered_stream (bp)
175 BUFFERED_STREAM *bp;
176{
177 BUFFERED_STREAM *nbp;
178
179 if (!bp)
180 return ((BUFFERED_STREAM *)NULL);
181
182 nbp = (BUFFERED_STREAM *)xmalloc (sizeof (BUFFERED_STREAM));
183 xbcopy ((char *)bp, (char *)nbp, sizeof (BUFFERED_STREAM));
184 return (nbp);
185}
186
Jari Aalto28ef6c32001-04-06 19:14:31 +0000187int
188set_bash_input_fd (fd)
189 int fd;
190{
191 if (bash_input.type == st_bstream)
192 bash_input.location.buffered_fd = fd;
193 else if (interactive_shell == 0)
194 default_buffered_input = fd;
195 return 0;
196}
197
198int
199fd_is_bash_input (fd)
200 int fd;
201{
202 if (bash_input.type == st_bstream && bash_input.location.buffered_fd == fd)
203 return 1;
204 else if (interactive_shell == 0 && default_buffered_input == fd)
205 return 1;
206 return 0;
207}
208
209/* Save the buffered stream corresponding to file descriptor FD (which bash
210 is using to read input) to a buffered stream associated with NEW_FD. If
211 NEW_FD is -1, a new file descriptor is allocated with fcntl. The new
212 file descriptor is returned on success, -1 on error. */
213int
214save_bash_input (fd, new_fd)
215 int fd, new_fd;
216{
217 int nfd;
218
219 /* Sync the stream so we can re-read from the new file descriptor. We
220 might be able to avoid this by copying the buffered stream verbatim
221 to the new file descriptor. */
222 if (buffers[fd])
223 sync_buffered_stream (fd);
224
225 /* Now take care of duplicating the file descriptor that bash is
226 using for input, so we can reinitialize it later. */
227 nfd = (new_fd == -1) ? fcntl (fd, F_DUPFD, 10) : new_fd;
228 if (nfd == -1)
229 {
230 if (fcntl (fd, F_GETFD, 0) == 0)
Jari Aaltob80f6442004-07-27 13:29:18 +0000231 sys_error (_("cannot allocate new file descriptor for bash input from fd %d"), fd);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000232 return -1;
233 }
234
235 if (buffers[nfd])
236 {
237 /* What's this? A stray buffer without an associated open file
238 descriptor? Free up the buffer and report the error. */
Jari Aaltob80f6442004-07-27 13:29:18 +0000239 internal_error (_("save_bash_input: buffer already exists for new fd %d"), nfd);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000240 free_buffered_stream (buffers[nfd]);
241 }
242
243 /* Reinitialize bash_input.location. */
244 if (bash_input.type == st_bstream)
245 {
246 bash_input.location.buffered_fd = nfd;
247 fd_to_buffered_stream (nfd);
248 close_buffered_fd (fd); /* XXX */
249 }
250 else
251 /* If the current input type is not a buffered stream, but the shell
252 is not interactive and therefore using a buffered stream to read
253 input (e.g. with an `eval exec 3>output' inside a script), note
254 that the input fd has been changed. pop_stream() looks at this
255 value and adjusts the input fd to the new value of
256 default_buffered_input accordingly. */
257 bash_input_fd_changed++;
258
259 if (default_buffered_input == fd)
260 default_buffered_input = nfd;
261
262 SET_CLOSE_ON_EXEC (nfd);
263 return nfd;
264}
265
Jari Aalto726f6381996-08-26 18:22:31 +0000266/* Check that file descriptor FD is not the one that bash is currently
267 using to read input from a script. FD is about to be duplicated onto,
268 which means that the kernel will close it for us. If FD is the bash
269 input file descriptor, we need to seek backwards in the script (if
270 possible and necessary -- scripts read from stdin are still unbuffered),
271 allocate a new file descriptor to use for bash input, and re-initialize
Jari Aalto28ef6c32001-04-06 19:14:31 +0000272 the buffered stream. Make sure the file descriptor used to save bash
273 input is set close-on-exec. Returns 0 on success, -1 on failure. This
274 works only if fd is > 0 -- if fd == 0 and bash is reading input from
275 fd 0, save_bash_input is used instead, to cooperate with input
276 redirection (look at redir.c:add_undo_redirect()). */
Jari Aalto726f6381996-08-26 18:22:31 +0000277int
278check_bash_input (fd)
279 int fd;
280{
Jari Aaltob80f6442004-07-27 13:29:18 +0000281 if (fd_is_bash_input (fd))
282 {
283 if (fd > 0)
284 return ((save_bash_input (fd, -1) == -1) ? -1 : 0);
285 else if (fd == 0)
286 return ((sync_buffered_stream (fd) == -1) ? -1 : 0);
287 }
Jari Aalto726f6381996-08-26 18:22:31 +0000288 return 0;
289}
290
291/* This is the buffered stream analogue of dup2(fd1, fd2). The
292 BUFFERED_STREAM corresponding to fd2 is deallocated, if one exists.
293 BUFFERS[fd1] is copied to BUFFERS[fd2]. This is called by the
294 redirect code for constructs like 4<&0 and 3</etc/rc.local. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000295int
Jari Aalto726f6381996-08-26 18:22:31 +0000296duplicate_buffered_stream (fd1, fd2)
297 int fd1, fd2;
298{
299 int is_bash_input, m;
300
301 if (fd1 == fd2)
302 return 0;
303
304 m = max (fd1, fd2);
305 ALLOCATE_BUFFERS (m);
306
307 /* If FD2 is the file descriptor bash is currently using for shell input,
308 we need to do some extra work to make sure that the buffered stream
309 actually exists (it might not if fd1 was not active, and the copy
310 didn't actually do anything). */
311 is_bash_input = (bash_input.type == st_bstream) &&
312 (bash_input.location.buffered_fd == fd2);
313
314 if (buffers[fd2])
315 free_buffered_stream (buffers[fd2]);
316 buffers[fd2] = copy_buffered_stream (buffers[fd1]);
317 if (buffers[fd2])
318 buffers[fd2]->b_fd = fd2;
319
Jari Aalto28ef6c32001-04-06 19:14:31 +0000320 if (is_bash_input)
321 {
322 if (!buffers[fd2])
323 fd_to_buffered_stream (fd2);
324 buffers[fd2]->b_flag |= B_WASBASHINPUT;
325 }
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000326
Jari Aalto726f6381996-08-26 18:22:31 +0000327 return (fd2);
328}
329
330/* Return 1 if a seek on FD will succeed. */
Jari Aalto28ef6c32001-04-06 19:14:31 +0000331#ifndef __CYGWIN__
Jari Aaltocce855b1998-04-17 19:52:44 +0000332# define fd_is_seekable(fd) (lseek ((fd), 0L, SEEK_CUR) >= 0)
333#else
334# define fd_is_seekable(fd) 0
Jari Aalto28ef6c32001-04-06 19:14:31 +0000335#endif /* __CYGWIN__ */
Jari Aalto726f6381996-08-26 18:22:31 +0000336
337/* Take FD, a file descriptor, and create and return a buffered stream
338 corresponding to it. If something is wrong and the file descriptor
339 is invalid, return a NULL stream. */
340BUFFERED_STREAM *
341fd_to_buffered_stream (fd)
342 int fd;
343{
344 char *buffer;
Jari Aaltocce855b1998-04-17 19:52:44 +0000345 size_t size;
Jari Aalto726f6381996-08-26 18:22:31 +0000346 struct stat sb;
347
348 if (fstat (fd, &sb) < 0)
349 {
350 close (fd);
351 return ((BUFFERED_STREAM *)NULL);
352 }
353
Jari Aalto28ef6c32001-04-06 19:14:31 +0000354 size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1;
355 if (size == 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000356 size = 1;
Jari Aalto726f6381996-08-26 18:22:31 +0000357 buffer = (char *)xmalloc (size);
358
359 return (make_buffered_stream (fd, buffer, size));
360}
361
362/* Return a buffered stream corresponding to FILE, a file name. */
363BUFFERED_STREAM *
364open_buffered_stream (file)
365 char *file;
366{
367 int fd;
368
369 fd = open (file, O_RDONLY);
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000370 return ((fd >= 0) ? fd_to_buffered_stream (fd) : (BUFFERED_STREAM *)NULL);
Jari Aalto726f6381996-08-26 18:22:31 +0000371}
372
373/* Deallocate a buffered stream and free up its resources. Make sure we
374 zero out the slot in BUFFERS that points to BP. */
375void
376free_buffered_stream (bp)
377 BUFFERED_STREAM *bp;
378{
379 int n;
380
381 if (!bp)
382 return;
383
384 n = bp->b_fd;
385 if (bp->b_buffer)
386 free (bp->b_buffer);
387 free (bp);
388 buffers[n] = (BUFFERED_STREAM *)NULL;
389}
390
391/* Close the file descriptor associated with BP, a buffered stream, and free
392 up the stream. Return the status of closing BP's file descriptor. */
393int
394close_buffered_stream (bp)
395 BUFFERED_STREAM *bp;
396{
397 int fd;
398
399 if (!bp)
400 return (0);
401 fd = bp->b_fd;
402 free_buffered_stream (bp);
403 return (close (fd));
404}
405
406/* Deallocate the buffered stream associated with file descriptor FD, and
407 close FD. Return the status of the close on FD. */
408int
409close_buffered_fd (fd)
410 int fd;
411{
Jari Aaltof73dda02001-11-13 17:56:06 +0000412 if (fd < 0)
413 {
414 errno = EBADF;
415 return -1;
416 }
Jari Aalto726f6381996-08-26 18:22:31 +0000417 if (fd >= nbuffers || !buffers || !buffers[fd])
418 return (close (fd));
419 return (close_buffered_stream (buffers[fd]));
420}
421
Jari Aaltocce855b1998-04-17 19:52:44 +0000422/* Make the BUFFERED_STREAM associcated with buffers[FD] be BP, and return
423 the old BUFFERED_STREAM. */
424BUFFERED_STREAM *
425set_buffered_stream (fd, bp)
426 int fd;
427 BUFFERED_STREAM *bp;
428{
429 BUFFERED_STREAM *ret;
430
431 ret = buffers[fd];
432 buffers[fd] = bp;
433 return ret;
434}
435
Jari Aalto726f6381996-08-26 18:22:31 +0000436/* Read a buffer full of characters from BP, a buffered stream. */
437static int
438b_fill_buffer (bp)
439 BUFFERED_STREAM *bp;
440{
Jari Aaltof73dda02001-11-13 17:56:06 +0000441 ssize_t nr;
442
443 nr = zread (bp->b_fd, bp->b_buffer, bp->b_size);
444 if (nr <= 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000445 {
Jari Aaltof73dda02001-11-13 17:56:06 +0000446 bp->b_used = 0;
Jari Aalto726f6381996-08-26 18:22:31 +0000447 bp->b_buffer[0] = 0;
Jari Aaltof73dda02001-11-13 17:56:06 +0000448 if (nr == 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000449 bp->b_flag |= B_EOF;
450 else
451 bp->b_flag |= B_ERROR;
452 return (EOF);
453 }
Jari Aaltof73dda02001-11-13 17:56:06 +0000454
455#if defined (__CYGWIN__)
456 /* If on cygwin, translate \r\n to \n. */
457 if (nr >= 2 && bp->b_buffer[nr - 2] == '\r' && bp->b_buffer[nr - 1] == '\n')
458 {
459 bp->b_buffer[nr - 2] = '\n';
460 nr--;
461 }
462#endif
463
464 bp->b_used = nr;
Jari Aalto726f6381996-08-26 18:22:31 +0000465 bp->b_inputp = 0;
466 return (bp->b_buffer[bp->b_inputp++] & 0xFF);
467}
468
469/* Get a character from buffered stream BP. */
470#define bufstream_getc(bp) \
471 (bp->b_inputp == bp->b_used || !bp->b_used) \
472 ? b_fill_buffer (bp) \
473 : bp->b_buffer[bp->b_inputp++] & 0xFF
474
475/* Push C back onto buffered stream BP. */
476static int
477bufstream_ungetc(c, bp)
478 int c;
479 BUFFERED_STREAM *bp;
480{
481 if (c == EOF || bp->b_inputp == 0)
482 return (EOF);
483
484 bp->b_buffer[--bp->b_inputp] = c;
485 return (c);
486}
487
488/* Seek backwards on file BFD to synchronize what we've read so far
489 with the underlying file pointer. */
490int
491sync_buffered_stream (bfd)
492 int bfd;
493{
494 BUFFERED_STREAM *bp;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000495 off_t chars_left;
Jari Aalto726f6381996-08-26 18:22:31 +0000496
Jari Aalto28ef6c32001-04-06 19:14:31 +0000497 if (buffers == 0 || (bp = buffers[bfd]) == 0)
Jari Aalto726f6381996-08-26 18:22:31 +0000498 return (-1);
Jari Aalto28ef6c32001-04-06 19:14:31 +0000499
Jari Aalto726f6381996-08-26 18:22:31 +0000500 chars_left = bp->b_used - bp->b_inputp;
501 if (chars_left)
502 lseek (bp->b_fd, -chars_left, SEEK_CUR);
503 bp->b_used = bp->b_inputp = 0;
504 return (0);
505}
506
507int
508buffered_getchar ()
509{
Jari Aalto28ef6c32001-04-06 19:14:31 +0000510#if !defined (DJGPP)
Jari Aalto726f6381996-08-26 18:22:31 +0000511 return (bufstream_getc (buffers[bash_input.location.buffered_fd]));
Jari Aalto28ef6c32001-04-06 19:14:31 +0000512#else
513 /* On DJGPP, ignore \r. */
514 int ch;
515 while ((ch = bufstream_getc (buffers[bash_input.location.buffered_fd])) == '\r')
516 ;
517 return ch;
518#endif
Jari Aalto726f6381996-08-26 18:22:31 +0000519}
520
521int
522buffered_ungetchar (c)
523 int c;
524{
525 return (bufstream_ungetc (c, buffers[bash_input.location.buffered_fd]));
526}
527
528/* Make input come from file descriptor BFD through a buffered stream. */
529void
530with_input_from_buffered_stream (bfd, name)
531 int bfd;
532 char *name;
533{
534 INPUT_STREAM location;
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000535 BUFFERED_STREAM *bp;
Jari Aalto726f6381996-08-26 18:22:31 +0000536
537 location.buffered_fd = bfd;
538 /* Make sure the buffered stream exists. */
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000539 bp = fd_to_buffered_stream (bfd);
540 init_yy_io (bp == 0 ? return_EOF : buffered_getchar,
541 buffered_ungetchar, st_bstream, name, location);
Jari Aalto726f6381996-08-26 18:22:31 +0000542}
543
544#if defined (TEST)
Jari Aaltof73dda02001-11-13 17:56:06 +0000545void *
Jari Aalto726f6381996-08-26 18:22:31 +0000546xmalloc(s)
547int s;
548{
Jari Aaltof73dda02001-11-13 17:56:06 +0000549 return (malloc (s));
Jari Aalto726f6381996-08-26 18:22:31 +0000550}
551
Jari Aaltof73dda02001-11-13 17:56:06 +0000552void *
Jari Aalto726f6381996-08-26 18:22:31 +0000553xrealloc(s, size)
554char *s;
555int size;
556{
557 if (!s)
Jari Aaltof73dda02001-11-13 17:56:06 +0000558 return(malloc (size));
Jari Aalto726f6381996-08-26 18:22:31 +0000559 else
Jari Aaltof73dda02001-11-13 17:56:06 +0000560 return(realloc (s, size));
Jari Aalto726f6381996-08-26 18:22:31 +0000561}
562
563void
564init_yy_io ()
565{
566}
567
568process(bp)
569BUFFERED_STREAM *bp;
570{
571 int c;
572
573 while ((c = bufstream_getc(bp)) != EOF)
574 putchar(c);
575}
576
577BASH_INPUT bash_input;
578
579struct stat dsb; /* can be used from gdb */
580
581/* imitate /bin/cat */
582main(argc, argv)
583int argc;
584char **argv;
585{
586 register int i;
587 BUFFERED_STREAM *bp;
588
589 if (argc == 1) {
590 bp = fd_to_buffered_stream (0);
591 process(bp);
592 exit(0);
593 }
594 for (i = 1; i < argc; i++) {
595 if (argv[i][0] == '-' && argv[i][1] == '\0') {
596 bp = fd_to_buffered_stream (0);
597 if (!bp)
598 continue;
599 process(bp);
600 free_buffered_stream (bp);
601 } else {
602 bp = open_buffered_stream (argv[i]);
603 if (!bp)
604 continue;
605 process(bp);
606 close_buffered_stream (bp);
607 }
608 }
609 exit(0);
610}
Jari Aaltoccc6cda1996-12-23 17:02:34 +0000611#endif /* TEST */
612#endif /* BUFFERED_INPUT */