1 /* A small buffered I/O library.
2 * - by Richard W.M. Jones <rich@annexia.org>.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the Free
16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 * $Id: pthr_iolib.c,v 1.9 2003/02/02 18:05:31 rich Exp $
35 #ifdef HAVE_SYS_TYPE_H
36 #include <sys/types.h>
39 #ifdef HAVE_SYS_WAIT_H
53 #include "pthr_iolib.h"
54 #include "pthr_pseudothread.h"
58 /* Pool for memory allocation, etc. */
61 /* The underlying socket. */
64 /* The incoming buffer. */
65 char *inbuf; /* The actual buffer. */
66 char *inbufpos; /* Current position within buffer. */
67 int inbuflen; /* Number of bytes left to read. */
69 int inbufcount; /* Total number of bytes read. */
71 /* The outgoing buffer. */
72 char *outbuf; /* The actual buffer. */
73 char *outbufpos; /* Current position within buffer. */
74 int outbuffree; /* Number of bytes free left in buffer. */
76 int outbufcount; /* Total number of bytes written. */
78 int outbufmode; /* Output buffer mode. */
81 static void _flush (io_handle io, int ignore_errors);
82 static void _err (io_handle io, const char *msg) __attribute__((noreturn));
83 static void _do_close (io_handle io);
89 pool pool = new_subpool (pth_get_pool (current_pth));
91 io = pmalloc (pool, sizeof *io);
93 io->inbuf = pmalloc (pool, IOLIB_INPUT_BUFFER_SIZE * sizeof (char));
95 io->outbuf = pmalloc (pool, IOLIB_OUTPUT_BUFFER_SIZE * sizeof (char));
100 io->inbufpos = &io->inbuf[IOLIB_INPUT_BUFFER_SIZE]; /* Let ungetc work. */
103 io->outbufpos = io->outbuf;
104 io->outbuffree = IOLIB_OUTPUT_BUFFER_SIZE;
106 io->outbufmode = IO_MODE_LINE_BUFFERED;
108 /* Register to automagically close this I/O handle when we
111 pool_register_cleanup_fn (pool, (void (*)(void *)) _do_close, io);
117 io_fflush (io_handle io)
119 if (io->outbufpos > io->outbuf)
126 _do_close (io_handle io)
128 /* Flush the buffer, but don't worry too much about errors. */
129 if (io->outbufpos > io->outbuf)
132 /* Close underlying socket. */
133 if (close (io->sock) < 0) _err (io, "close");
137 io_fclose (io_handle io)
139 /* Deleting the subpool containing io causes the _do_close callback
140 * handler to run which actually closes the socket. The memory
141 * containing io is also freed.
143 delete_pool (io->pool);
147 io_fgetc (io_handle io)
154 /* Satify this from the input buffer? */
155 if (io->inbuflen > 0)
157 int c = *io->inbufpos;
163 /* Refill the input buffer from the socket. */
164 r = pth_read (io->sock, io->inbuf, IOLIB_INPUT_BUFFER_SIZE * sizeof (char));
165 if (r < 0) _err (io, "read");
167 io->inbufpos = io->inbuf;
172 if (r == 0) return -1;
174 /* Return the next character. */
179 io_fgets (char *s, int max_size, io_handle io, int store_eol)
185 while (n < max_size - 1)
187 int c = io_fgetc (io);
193 return n > 0 ? s : 0;
203 /* Remove the trailing CR LF? */
206 while (n > 0 && (s[n-1] == '\n' || s[n-1] == '\r'))
210 /* Terminate the line. */
216 io_ungetc (int c, io_handle io)
218 if (io->inbufpos > io->inbuf)
226 return -1; /* No room left in input buffer. */
230 io_fread (void *ptr, size_t size, size_t nmemb, io_handle io)
232 size_t n = size * nmemb, c = 0;
234 char *cptr = (char *) ptr;
238 /* Satisfy as much as possible from the input buffer. */
239 i = n > io->inbuflen ? io->inbuflen : n;
240 memcpy (cptr, io->inbufpos, i * sizeof (char));
247 /* Read the rest directly from the socket until we have either
248 * satisfied the request or there is an EOF.
252 int r = pth_read (io->sock, cptr, n * sizeof (char));
253 if (r < 0) _err (io, "read");
255 if (r == 0) /* End of file. */
268 io_fputc (int c, io_handle io)
271 if (io->outbuffree > 0)
277 /* Flush the buffer after each character or line. */
278 if (io->outbufmode == IO_MODE_UNBUFFERED ||
279 (io->outbufmode == IO_MODE_LINE_BUFFERED && c == '\n'))
285 /* We need to flush the output buffer and try again. */
291 io_fputs (const char *s, io_handle io)
299 /* According to the manual page, fputs returns a non-negative number
300 * on success or EOF on error.
306 io_fprintf (io_handle io, const char *fs, ...)
310 char *s = alloca (n);
312 if (s == 0) abort ();
315 r = vsnprintf (s, n, fs, args);
318 /* Did the string get truncated? If so, we can allocate the
319 * correct sized buffer directly from the pool.
321 * Note: according to the manual page, a return value of -1 indicates
322 * that the string was truncated. We have found that this is not
323 * actually true however. In fact, the library seems to return the
324 * number of characters which would have been written into the string
330 s = pmalloc (io->pool, n);
333 r = vsnprintf (s, n, fs, args);
343 io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle io)
345 size_t n = size * nmemb, c = 0;
346 char *cptr = (char *) ptr;
348 /* Flush out any existing data. */
351 /* Write the data directly to the socket. */
354 int r = pth_write (io->sock, cptr, n * sizeof (char));
355 if (r < 0) _err (io, "write");
360 io->outbufcount += r;
367 io_popen (const char *command, const char *type)
371 int mode = 0; /* 0 for read, 1 for write. */
374 if (strcmp (type, "r") == 0)
376 else if (strcmp (type, "w") == 0)
381 /* Create a pipe between parent and child. */
382 if (pipe (fd) < 0) { perror ("pipe"); return 0; }
384 /* Fork and connect up the pipe appropriately. */
386 if (pid < 0) { close (fd[0]); close (fd[1]); perror ("fork"); return 0; }
387 else if (pid > 0) /* Parent process. */
400 /* Set the file descriptor to non-blocking. */
401 if (fcntl (s, F_SETFL, O_NONBLOCK) < 0) abort ();
412 else /* Child process. */
433 /* Run the child command. */
434 _exit (system (command));
442 io_pclose (io_handle io)
444 /* Close connection. */
447 /* Wait for child to exit. */
452 io_copy (io_handle from_io, io_handle to_io, int len)
456 /* Synchronize the input handle. */
463 /* Read bytes directly from the input buffer. */
464 if (from_io->inbuflen == 0)
468 /* Need to refill the input buffer. We do this by reading a single
469 * character and the "ungetting" it back into the buffer. The
470 * io_ungetc call is guaranteed to work in this case.
472 c = io_fgetc (from_io);
473 if (c == -1) return written_len;
474 io_ungetc (c, from_io);
477 /* Decide how many bytes to read. */
479 n = from_io->inbuflen;
482 if (len >= from_io->inbuflen)
483 n = from_io->inbuflen;
489 io_fwrite (from_io->inbufpos, 1, n, to_io);
491 if (len > 0) len -= n;
492 from_io->inbufpos += n;
493 from_io->inbuflen -= n;
500 io_setbufmode (io_handle io, int mode)
502 io->outbufmode = mode;
506 _flush (io_handle io, int ignore_errors)
508 size_t n = io->outbufpos - io->outbuf;
509 char *cptr = io->outbuf;
511 /* Write the data to the socket. */
514 int r = pth_write (io->sock, cptr, n * sizeof (char));
525 io->outbufcount += r;
528 /* Reset the output buffer. */
529 io->outbufpos = io->outbuf;
530 io->outbuffree = IOLIB_OUTPUT_BUFFER_SIZE;
534 io_fileno (io_handle io)
540 io_get_inbufcount (io_handle io)
542 return io->inbufcount;
546 io_get_outbufcount (io_handle io)
548 return io->outbufcount;
552 _err (io_handle io, const char *msg)
554 /* I commented out the following line because, strangely, it seems to
555 * cause some sort of memory corruption bug. If there is a bug, it's
556 * probably related to either stack overflow or the stack swapping /
557 * context switching stuff. Whatever. Removed it for safety.
558 * -- RWMJ 2000/12/05.
564 /* Should errors be catchable? ie. Should we call pth_die here? And
565 * if so, what are the consequences of doing it?
566 * -- RWMJ 2001/05/30.