Add to git.
[pthrlib.git] / src / pthr_iolib.c
1 /* A small buffered I/O library.
2  * - by Richard W.M. Jones <rich@annexia.org>.
3  *
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.
8  *
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.
13  *
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.
17  *
18  * $Id: pthr_iolib.c,v 1.9 2003/02/02 18:05:31 rich Exp $
19  */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #ifdef HAVE_STRING_H
32 #include <string.h>
33 #endif
34
35 #ifdef HAVE_SYS_TYPE_H
36 #include <sys/types.h>
37 #endif
38
39 #ifdef HAVE_SYS_WAIT_H
40 #include <sys/wait.h>
41 #endif
42
43 #ifdef HAVE_FCNTL_H
44 #include <fcntl.h>
45 #endif
46
47 #ifdef HAVE_ALLOCA_H
48 #include <alloca.h>
49 #endif
50
51 #include <pool.h>
52
53 #include "pthr_iolib.h"
54 #include "pthr_pseudothread.h"
55
56 struct io_handle
57 {
58   /* Pool for memory allocation, etc. */
59   pool pool;
60
61   /* The underlying socket. */
62   int sock;
63
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. */
68
69   int inbufcount;               /* Total number of bytes read. */
70
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. */
75
76   int outbufcount;              /* Total number of bytes written. */
77
78   int outbufmode;               /* Output buffer mode. */
79 };
80
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);
84
85 io_handle
86 io_fdopen (int sock)
87 {
88   io_handle io;
89   pool pool = new_subpool (pth_get_pool (current_pth));
90
91   io = pmalloc (pool, sizeof *io);
92
93   io->inbuf = pmalloc (pool, IOLIB_INPUT_BUFFER_SIZE * sizeof (char));
94
95   io->outbuf = pmalloc (pool, IOLIB_OUTPUT_BUFFER_SIZE * sizeof (char));
96
97   io->pool = pool;
98   io->sock = sock;
99
100   io->inbufpos = &io->inbuf[IOLIB_INPUT_BUFFER_SIZE]; /* Let ungetc work. */
101   io->inbuflen = 0;
102   io->inbufcount = 0;
103   io->outbufpos = io->outbuf;
104   io->outbuffree = IOLIB_OUTPUT_BUFFER_SIZE;
105   io->outbufcount = 0;
106   io->outbufmode = IO_MODE_LINE_BUFFERED;
107
108   /* Register to automagically close this I/O handle when we
109    * exit this thread.
110    */
111   pool_register_cleanup_fn (pool, (void (*)(void *)) _do_close, io);
112
113   return io;
114 }
115
116 inline int
117 io_fflush (io_handle io)
118 {
119   if (io->outbufpos > io->outbuf)
120     _flush (io, 0);
121
122   return 0;
123 }
124
125 void
126 _do_close (io_handle io)
127 {
128   /* Flush the buffer, but don't worry too much about errors. */
129   if (io->outbufpos > io->outbuf)
130     _flush (io, 1);
131
132   /* Close underlying socket. */
133   if (close (io->sock) < 0) _err (io, "close");
134 }
135
136 void
137 io_fclose (io_handle io)
138 {
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.
142    */
143   delete_pool (io->pool);
144 }
145
146 int
147 io_fgetc (io_handle io)
148 {
149   int r;
150
151   io_fflush (io);
152
153  next:
154   /* Satify this from the input buffer? */
155   if (io->inbuflen > 0)
156     {
157       int c = *io->inbufpos;
158       io->inbufpos++;
159       io->inbuflen--;
160       return c;
161     }
162
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");
166
167   io->inbufpos = io->inbuf;
168   io->inbuflen = r;
169   io->inbufcount += r;
170
171   /* End of buffer? */
172   if (r == 0) return -1;
173
174   /* Return the next character. */
175   goto next;
176 }
177
178 char *
179 io_fgets (char *s, int max_size, io_handle io, int store_eol)
180 {
181   int n = 0;
182
183   io_fflush (io);
184
185   while (n < max_size - 1)
186     {
187       int c = io_fgetc (io);
188
189       /* End of file? */
190       if (c == -1)
191         {
192           s[n] = '\0';
193           return n > 0 ? s : 0;
194         }
195
196       s[n++] = c;
197
198       /* End of line? */
199       if (c == '\n')
200         break;
201     }
202
203   /* Remove the trailing CR LF? */
204   if (!store_eol)
205     {
206       while (n > 0 && (s[n-1] == '\n' || s[n-1] == '\r'))
207         n--;
208     }
209
210   /* Terminate the line. */
211   s[n] = '\0';
212   return s;
213 }
214
215 int
216 io_ungetc (int c, io_handle io)
217 {
218   if (io->inbufpos > io->inbuf)
219     {
220       io->inbufpos--;
221       *io->inbufpos = c;
222       io->inbuflen++;
223       return c;
224     }
225
226   return -1;                    /* No room left in input buffer. */
227 }
228
229 size_t
230 io_fread (void *ptr, size_t size, size_t nmemb, io_handle io)
231 {
232   size_t n = size * nmemb, c = 0;
233   int i;
234   char *cptr = (char *) ptr;
235
236   io_fflush (io);
237
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));
241   n -= i;
242   c += i;
243   cptr += i;
244   io->inbuflen -= i;
245   io->inbufpos += i;
246
247   /* Read the rest directly from the socket until we have either
248    * satisfied the request or there is an EOF.
249    */
250   while (n > 0)
251     {
252       int r = pth_read (io->sock, cptr, n * sizeof (char));
253       if (r < 0) _err (io, "read");
254
255       if (r == 0)               /* End of file. */
256         return c;
257
258       n -= r;
259       c += r;
260       cptr += r;
261       io->inbufcount += r;
262     }
263
264   return c;
265 }
266
267 inline int
268 io_fputc (int c, io_handle io)
269 {
270  again:
271   if (io->outbuffree > 0)
272     {
273       *io->outbufpos = c;
274       io->outbufpos++;
275       io->outbuffree--;
276
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'))
280         _flush (io, 0);
281
282       return c;
283     }
284
285   /* We need to flush the output buffer and try again. */
286   _flush (io, 0);
287   goto again;
288 }
289
290 int
291 io_fputs (const char *s, io_handle io)
292 {
293   while (*s)
294     {
295       io_fputc (*s, io);
296       s++;
297     }
298
299   /* According to the manual page, fputs returns a non-negative number
300    * on success or EOF on error.
301    */
302   return 1;
303 }
304
305 int
306 io_fprintf (io_handle io, const char *fs, ...)
307 {
308   va_list args;
309   int r, n = 4096;
310   char *s = alloca (n);
311
312   if (s == 0) abort ();
313
314   va_start (args, fs);
315   r = vsnprintf (s, n, fs, args);
316   va_end (args);
317
318   /* Did the string get truncated? If so, we can allocate the
319    * correct sized buffer directly from the pool.
320    *
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
325    * (ie. r > n).
326    */
327   if (r > n)
328     {
329       n = r + 1;
330       s = pmalloc (io->pool, n);
331
332       va_start (args, fs);
333       r = vsnprintf (s, n, fs, args);
334       va_end (args);
335     }
336
337   io_fputs (s, io);
338
339   return r;
340 }
341
342 size_t
343 io_fwrite (const void *ptr, size_t size, size_t nmemb, io_handle io)
344 {
345   size_t n = size * nmemb, c = 0;
346   char *cptr = (char *) ptr;
347
348   /* Flush out any existing data. */
349   io_fflush (io);
350
351   /* Write the data directly to the socket. */
352   while (n > 0)
353     {
354       int r = pth_write (io->sock, cptr, n * sizeof (char));
355       if (r < 0) _err (io, "write");
356
357       n -= r;
358       cptr += r;
359       c += r;
360       io->outbufcount += r;
361     }
362
363   return c;
364 }
365
366 io_handle
367 io_popen (const char *command, const char *type)
368 {
369   int fd[2], s;
370   int pid;
371   int mode = 0;                 /* 0 for read, 1 for write. */
372   io_handle io;
373
374   if (strcmp (type, "r") == 0)
375     mode = 0;
376   else if (strcmp (type, "w") == 0)
377     mode = 1;
378   else
379     abort ();
380
381   /* Create a pipe between parent and child. */
382   if (pipe (fd) < 0) { perror ("pipe"); return 0; }
383
384   /* Fork and connect up the pipe appropriately. */
385   pid = fork ();
386   if (pid < 0) { close (fd[0]); close (fd[1]); perror ("fork"); return 0; }
387   else if (pid > 0)             /* Parent process. */
388     {
389       if (mode == 0)
390         {
391           close (fd[1]);
392           s = fd[0];
393         }
394       else
395         {
396           close (fd[0]);
397           s = fd[1];
398         }
399
400       /* Set the file descriptor to non-blocking. */
401       if (fcntl (s, F_SETFL, O_NONBLOCK) < 0) abort ();
402
403       io = io_fdopen (s);
404       if (io == 0)
405         {
406           close (s);
407           return 0;
408         }
409
410       return io;
411     }
412   else                          /* Child process. */
413     {
414       if (mode == 0)
415         {
416           close (fd[0]);
417           if (fd[1] != 1)
418             {
419               dup2 (fd[1], 1);
420               close (fd[1]);
421             }
422         }
423       else
424         {
425           close (fd[1]);
426           if (fd[0] != 0)
427             {
428               dup2 (fd[0], 0);
429               close (fd[0]);
430             }
431         }
432
433       /* Run the child command. */
434       _exit (system (command));
435
436       /*NOTREACHED*/
437       return 0;
438     }
439 }
440
441 void
442 io_pclose (io_handle io)
443 {
444   /* Close connection. */
445   io_fclose (io);
446
447   /* Wait for child to exit. */
448   wait (NULL);
449 }
450
451 int
452 io_copy (io_handle from_io, io_handle to_io, int len)
453 {
454   int written_len = 0;
455
456   /* Synchronize the input handle. */
457   io_fflush (from_io);
458
459   while (len != 0)
460     {
461       int n;
462
463       /* Read bytes directly from the input buffer. */
464       if (from_io->inbuflen == 0)
465         {
466           int c;
467
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.
471            */
472           c = io_fgetc (from_io);
473           if (c == -1) return written_len;
474           io_ungetc (c, from_io);
475         }
476
477       /* Decide how many bytes to read. */
478       if (len == -1)
479         n = from_io->inbuflen;
480       else
481         {
482           if (len >= from_io->inbuflen)
483             n = from_io->inbuflen;
484           else
485             n = len;
486         }
487
488       /* Write it. */
489       io_fwrite (from_io->inbufpos, 1, n, to_io);
490       written_len += n;
491       if (len > 0) len -= n;
492       from_io->inbufpos += n;
493       from_io->inbuflen -= n;
494     }
495
496   return written_len;
497 }
498
499 void
500 io_setbufmode (io_handle io, int mode)
501 {
502   io->outbufmode = mode;
503 }
504
505 static void
506 _flush (io_handle io, int ignore_errors)
507 {
508   size_t n = io->outbufpos - io->outbuf;
509   char *cptr = io->outbuf;
510
511   /* Write the data to the socket. */
512   while (n > 0)
513     {
514       int r = pth_write (io->sock, cptr, n * sizeof (char));
515       if (r < 0)
516         {
517           if (!ignore_errors)
518             _err (io, "write");
519           else
520             break;
521         }
522
523       n -= r;
524       cptr += r;
525       io->outbufcount += r;
526     }
527
528   /* Reset the output buffer. */
529   io->outbufpos = io->outbuf;
530   io->outbuffree = IOLIB_OUTPUT_BUFFER_SIZE;
531 }
532
533 int
534 io_fileno (io_handle io)
535 {
536   return io->sock;
537 }
538
539 int
540 io_get_inbufcount (io_handle io)
541 {
542   return io->inbufcount;
543 }
544
545 int
546 io_get_outbufcount (io_handle io)
547 {
548   return io->outbufcount;
549 }
550
551 static void
552 _err (io_handle io, const char *msg)
553 {
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.
559    */
560 #if 0
561   perror (msg);
562 #endif
563
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.
567    */
568   pth_exit ();
569 }