3a17910f7d53afe3bdbab3e6c25d1b94fb791a94
[gnupg.git] / common / estream.c
1 /* estream.c - Extended Stream I/O Library
2  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 g10 Code GmbH
3  *
4  * This file is part of Libestream.
5  *
6  * Libestream is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * Libestream is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Libestream; if not, see <http://www.gnu.org/licenses/>.
18  *
19  * ALTERNATIVELY, Libestream may be distributed under the terms of the
20  * following license, in which case the provisions of this license are
21  * required INSTEAD OF the GNU General Public License. If you wish to
22  * allow use of your version of this file only under the terms of the
23  * GNU General Public License, and not to allow others to use your
24  * version of this file under the terms of the following license,
25  * indicate your decision by deleting this paragraph and the license
26  * below.
27  *
28  * Redistribution and use in source and binary forms, with or without
29  * modification, are permitted provided that the following conditions
30  * are met:
31  * 1. Redistributions of source code must retain the above copyright
32  *    notice, and the entire permission notice in its entirety,
33  *    including the disclaimer of warranties.
34  * 2. Redistributions in binary form must reproduce the above copyright
35  *    notice, this list of conditions and the following disclaimer in the
36  *    documentation and/or other materials provided with the distribution.
37  * 3. The name of the author may not be used to endorse or promote
38  *    products derived from this software without specific prior
39  *    written permission.
40  *
41  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
42  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
45  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
46  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
47  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51  * OF THE POSSIBILITY OF SUCH DAMAGE.
52  */
53
54 #ifdef USE_ESTREAM_SUPPORT_H
55 # include <estream-support.h>
56 #endif
57
58 #ifdef HAVE_CONFIG_H
59 # include <config.h>
60 #endif
61
62 #if defined(_WIN32) && !defined(HAVE_W32_SYSTEM)
63 # define HAVE_W32_SYSTEM 1
64 # if defined(__MINGW32CE__) && !defined (HAVE_W32CE_SYSTEM)
65 #  define HAVE_W32CE_SYSTEM
66 # endif
67 #endif
68
69 #include <sys/types.h>
70 #include <sys/file.h>
71 #include <sys/stat.h>
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
75 #include <unistd.h>
76 #include <stdarg.h>
77 #include <fcntl.h>
78 #include <errno.h>
79 #include <stddef.h>
80 #include <assert.h>
81 #ifdef HAVE_W32_SYSTEM
82 # include <windows.h>
83 #endif
84 #ifdef HAVE_W32CE_SYSTEM
85 # include <gpg-error.h> /* ERRNO replacement.  */
86 #endif
87
88 #ifdef WITHOUT_GNU_PTH /* Give the Makefile a chance to build without Pth.  */
89 # undef HAVE_PTH
90 # undef USE_GNU_PTH
91 #endif
92
93 #ifdef HAVE_PTH
94 # include <pth.h>
95 #endif
96
97 /* This is for the special hack to use estream.c in GnuPG.  */
98 #ifdef GNUPG_MAJOR_VERSION
99 # include "../common/util.h"
100 #endif
101
102 #ifndef HAVE_MKSTEMP
103 int mkstemp (char *template);
104 #endif
105
106 #ifndef HAVE_MEMRCHR
107 void *memrchr (const void *block, int c, size_t size);
108 #endif
109
110 #include <estream.h>
111 #include <estream-printf.h>
112
113 \f
114
115 #ifndef O_BINARY
116 #define O_BINARY 0
117 #endif
118
119 #ifdef HAVE_W32CE_SYSTEM
120 # define _set_errno(a)  gpg_err_set_errno ((a))
121 /* Setmode is missing in cegcc but available since CE 5.0.  */
122 int _setmode (int handle, int mode);
123 # define setmode(a,b)   _setmode ((a),(b))
124 #else
125 # define _set_errno(a)  do { errno = (a); } while (0)
126 #endif
127
128 #ifdef HAVE_W32_SYSTEM
129 # define IS_INVALID_FD(a) ((void*)(a) == (void*)(-1))
130 #else
131 # define IS_INVALID_FD(a) ((a) == -1)
132 #endif
133
134
135 /* Generally used types.  */
136
137 typedef void *(*func_realloc_t) (void *mem, size_t size);
138 typedef void (*func_free_t) (void *mem);
139
140
141 \f
142
143 /* Buffer management layer.  */
144
145 #define BUFFER_BLOCK_SIZE  BUFSIZ
146 #define BUFFER_UNREAD_SIZE 16
147
148 \f
149
150 /* Locking.  */
151
152 #ifdef HAVE_PTH
153
154 typedef pth_mutex_t estream_mutex_t;
155 # define ESTREAM_MUTEX_INITIALIZER PTH_MUTEX_INIT
156 # define ESTREAM_MUTEX_LOCK(mutex)        \
157   pth_mutex_acquire (&(mutex), 0, NULL)
158 # define ESTREAM_MUTEX_UNLOCK(mutex)      \
159   pth_mutex_release (&(mutex))
160 # define ESTREAM_MUTEX_TRYLOCK(mutex)     \
161   ((pth_mutex_acquire (&(mutex), 1, NULL) == TRUE) ? 0 : -1)
162 # define ESTREAM_MUTEX_INITIALIZE(mutex)  \
163   pth_mutex_init    (&(mutex))
164 #else
165
166 typedef void *estream_mutex_t;
167
168 static inline void
169 dummy_mutex_call_void (estream_mutex_t mutex)
170 {
171   (void)mutex;
172 }
173
174 static inline int
175 dummy_mutex_call_int (estream_mutex_t mutex)
176 {
177   (void)mutex;
178   return 0;
179 }
180
181 # define ESTREAM_MUTEX_INITIALIZER NULL
182 # define ESTREAM_MUTEX_LOCK(mutex) dummy_mutex_call_void ((mutex))
183 # define ESTREAM_MUTEX_UNLOCK(mutex) dummy_mutex_call_void ((mutex))
184 # define ESTREAM_MUTEX_TRYLOCK(mutex) dummy_mutex_call_int ((mutex))
185 # define ESTREAM_MUTEX_INITIALIZE(mutex) dummy_mutex_call_void ((mutex))
186 #endif
187
188 /* Primitive system I/O.  */
189
190 #ifdef HAVE_PTH
191 # define ESTREAM_SYS_READ  es_pth_read
192 # define ESTREAM_SYS_WRITE es_pth_write
193 # define ESTREAM_SYS_YIELD() pth_yield (NULL)
194 #else
195 # define ESTREAM_SYS_READ  read
196 # define ESTREAM_SYS_WRITE write
197 # define ESTREAM_SYS_YIELD() do { } while (0)
198 #endif
199
200 /* Misc definitions.  */
201
202 #define ES_DEFAULT_OPEN_MODE (S_IRUSR | S_IWUSR)
203
204 /* An internal stream object.  */
205
206 struct estream_internal
207 {
208   unsigned char buffer[BUFFER_BLOCK_SIZE];
209   unsigned char unread_buffer[BUFFER_UNREAD_SIZE];
210   estream_mutex_t lock;          /* Lock. */
211   void *cookie;                  /* Cookie.                */
212   void *opaque;                  /* Opaque data.           */
213   unsigned int modeflags;        /* Flags for the backend. */
214   off_t offset;
215   es_cookie_read_function_t func_read;
216   es_cookie_write_function_t func_write;
217   es_cookie_seek_function_t func_seek;
218   es_cookie_close_function_t func_close;
219   int strategy;
220   int fd;
221   struct
222   {
223     unsigned int err: 1;
224     unsigned int eof: 1;
225   } indicators;
226   unsigned int deallocate_buffer: 1;
227   unsigned int is_stdstream:1;   /* This is a standard stream.  */
228   unsigned int stdstream_fd:2;   /* 0, 1 or 2 for a standard stream.  */
229   unsigned int print_err: 1;     /* Error in print_fun_writer.  */
230   int print_errno;               /* Errno from print_fun_writer.  */
231   size_t print_ntotal;           /* Bytes written from in print_fun_writer. */
232   FILE *print_fp;                /* Stdio stream used by print_fun_writer.  */
233 };
234
235
236 typedef struct estream_internal *estream_internal_t;
237
238 #define ESTREAM_LOCK(stream) ESTREAM_MUTEX_LOCK (stream->intern->lock)
239 #define ESTREAM_UNLOCK(stream) ESTREAM_MUTEX_UNLOCK (stream->intern->lock)
240 #define ESTREAM_TRYLOCK(stream) ESTREAM_MUTEX_TRYLOCK (stream->intern->lock)
241
242 /* Stream list.  */
243
244 typedef struct estream_list *estream_list_t;
245
246 struct estream_list
247 {
248   estream_t car;
249   estream_list_t cdr;
250   estream_list_t *prev_cdr;
251 };
252
253 static estream_list_t estream_list;
254 static estream_mutex_t estream_list_lock;
255
256 #define ESTREAM_LIST_LOCK   ESTREAM_MUTEX_LOCK   (estream_list_lock)
257 #define ESTREAM_LIST_UNLOCK ESTREAM_MUTEX_UNLOCK (estream_list_lock)
258
259 /* File descriptors registered to be used as the standard file handles. */
260 static int custom_std_fds[3];
261 static unsigned char custom_std_fds_valid[3];
262
263
264 #ifndef EOPNOTSUPP
265 # define EOPNOTSUPP ENOSYS
266 #endif
267
268
269 \f
270
271 /* Macros.  */
272
273 /* Calculate array dimension.  */
274 #ifndef DIM
275 #define DIM(array) (sizeof (array) / sizeof (*array))
276 #endif
277
278 #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
279
280
281 /* Evaluate EXPRESSION, setting VARIABLE to the return code, if
282    VARIABLE is zero.  */
283 #define SET_UNLESS_NONZERO(variable, tmp_variable, expression) \
284   do                                                           \
285     {                                                          \
286       tmp_variable = expression;                               \
287       if ((! variable) && tmp_variable)                        \
288         variable = tmp_variable;                               \
289     }                                                          \
290   while (0)
291
292
293 /* Malloc wrappers to overcome problems on some older OSes.  */
294 static void *
295 mem_alloc (size_t n)
296 {
297   if (!n)
298     n++;
299   return malloc (n);
300 }
301
302 static void *
303 mem_realloc (void *p, size_t n)
304 {
305   if (!p)
306     return mem_alloc (n);
307   return realloc (p, n);
308 }
309
310 static void
311 mem_free (void *p)
312 {
313   if (p)
314     free (p);
315 }
316
317
318
319 /*
320  * List manipulation.
321  */
322
323 /* Add STREAM to the list of registered stream objects.  If
324    WITH_LOCKED_LIST is true we assumed that the list of streams is
325    already locked.  */
326 static int
327 es_list_add (estream_t stream, int with_locked_list)
328 {
329   estream_list_t list_obj;
330   int ret;
331
332   list_obj = mem_alloc (sizeof (*list_obj));
333   if (! list_obj)
334     ret = -1;
335   else
336     {
337       if (!with_locked_list)
338         ESTREAM_LIST_LOCK;
339       list_obj->car = stream;
340       list_obj->cdr = estream_list;
341       list_obj->prev_cdr = &estream_list;
342       if (estream_list)
343         estream_list->prev_cdr = &list_obj->cdr;
344       estream_list = list_obj;
345       if (!with_locked_list)
346         ESTREAM_LIST_UNLOCK;
347       ret = 0;
348     }
349
350   return ret;
351 }
352
353 /* Remove STREAM from the list of registered stream objects.  */
354 static void
355 es_list_remove (estream_t stream, int with_locked_list)
356 {
357   estream_list_t list_obj;
358   
359   if (!with_locked_list)
360     ESTREAM_LIST_LOCK;
361   for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
362     if (list_obj->car == stream)
363       {
364         *list_obj->prev_cdr = list_obj->cdr;
365         if (list_obj->cdr)
366           list_obj->cdr->prev_cdr = list_obj->prev_cdr;
367         mem_free (list_obj);
368         break;
369       }
370   if (!with_locked_list)
371     ESTREAM_LIST_UNLOCK;
372 }
373
374 /* Type of an stream-iterator-function.  */
375 typedef int (*estream_iterator_t) (estream_t stream);
376
377 /* Iterate over list of registered streams, calling ITERATOR for each
378    of them.  */
379 static int
380 es_list_iterate (estream_iterator_t iterator)
381 {
382   estream_list_t list_obj;
383   int ret = 0;
384
385   ESTREAM_LIST_LOCK;
386   for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
387     ret |= (*iterator) (list_obj->car);
388   ESTREAM_LIST_UNLOCK;
389
390   return ret;
391 }
392
393
394 \f
395 /*
396  * I/O Helper
397  *
398  * Unfortunately our Pth emulation for Windows expects system handles
399  * for pth_read and pth_write.  We use a simple approach to fix this:
400  * If the function returns an error we fall back to a vanilla read or
401  * write, assuming that we do I/O on a plain file where the operation
402  * can't block.
403  */
404 #ifdef HAVE_PTH
405 static int
406 es_pth_read (int fd, void *buffer, size_t size)
407 {
408 # ifdef HAVE_W32_SYSTEM
409   int rc = pth_read (fd, buffer, size);
410   if (rc == -1 && errno == EINVAL)
411     rc = read (fd, buffer, size);
412   return rc;
413 # else /*!HAVE_W32_SYSTEM*/
414   return pth_read (fd, buffer, size);
415 # endif /* !HAVE_W32_SYSTEM*/
416 }
417
418 static int
419 es_pth_write (int fd, const void *buffer, size_t size)
420 {
421 # ifdef HAVE_W32_SYSTEM
422   int rc = pth_write (fd, buffer, size);
423   if (rc == -1 && errno == EINVAL)
424     rc = write (fd, buffer, size);
425   return rc;
426 # else /*!HAVE_W32_SYSTEM*/
427   return pth_write (fd, buffer, size);
428 # endif /* !HAVE_W32_SYSTEM*/
429 }
430 #endif /*HAVE_PTH*/
431
432 \f
433
434 static void
435 es_deinit (void)
436 {
437   /* Flush all streams. */
438   es_fflush (NULL);
439 }
440
441
442 /*
443  * Initialization.
444  */
445
446 static int
447 es_init_do (void)
448 {
449   static int initialized;
450
451   if (!initialized)
452     {
453 #ifdef HAVE_PTH
454       if (!pth_init () && errno != EPERM )
455         return -1;
456       if (pth_mutex_init (&estream_list_lock))
457         initialized = 1;
458 #else
459       initialized = 1;
460 #endif
461       atexit (es_deinit);  
462     }
463   return 0;
464 }
465
466 \f
467
468 /*
469  * I/O methods.
470  */
471
472 /* Implementation of Memory I/O.  */
473
474 /* Cookie for memory objects.  */
475 typedef struct estream_cookie_mem
476 {
477   unsigned int modeflags;       /* Open flags.  */
478   unsigned char *memory;        /* Allocated data buffer.  */
479   size_t memory_size;           /* Allocated size of MEMORY.  */
480   size_t memory_limit;          /* Caller supplied maximum allowed
481                                    allocation size or 0 for no limit.  */
482   size_t offset;                /* Current offset in MEMORY.  */
483   size_t data_len;              /* Used length of data in MEMORY.  */
484   size_t block_size;            /* Block size.  */
485   struct {
486     unsigned int grow: 1;       /* MEMORY is allowed to grow.  */
487   } flags;
488   func_realloc_t func_realloc;
489   func_free_t func_free;
490 } *estream_cookie_mem_t;
491
492
493 /* Create function for memory objects.  DATA is either NULL or a user
494    supplied buffer with the initial conetnt of the memory buffer.  If
495    DATA is NULL, DATA_N and DATA_LEN need to be 0 as well.  If DATA is
496    not NULL, DATA_N gives the allocated size of DATA and DATA_LEN the
497    used length in DATA.  */
498 static int
499 es_func_mem_create (void *ES__RESTRICT *ES__RESTRICT cookie,
500                     unsigned char *ES__RESTRICT data, size_t data_n,
501                     size_t data_len,
502                     size_t block_size, unsigned int grow,
503                     func_realloc_t func_realloc, func_free_t func_free,
504                     unsigned int modeflags,
505                     size_t memory_limit)
506 {
507   estream_cookie_mem_t mem_cookie;
508   int err;
509
510   if (!data && (data_n || data_len))
511     {
512       _set_errno (EINVAL);
513       return -1;
514     }
515
516   mem_cookie = mem_alloc (sizeof (*mem_cookie));
517   if (!mem_cookie)
518     err = -1;
519   else
520     {
521       mem_cookie->modeflags = modeflags;
522       mem_cookie->memory = data;
523       mem_cookie->memory_size = data_n;
524       mem_cookie->memory_limit = memory_limit;
525       mem_cookie->offset = 0;
526       mem_cookie->data_len = data_len;
527       mem_cookie->block_size = block_size;
528       mem_cookie->flags.grow = !!grow;
529       mem_cookie->func_realloc = func_realloc ? func_realloc : mem_realloc;
530       mem_cookie->func_free = func_free ? func_free : mem_free;
531       *cookie = mem_cookie;
532       err = 0;
533     }
534
535   return err;
536 }
537
538
539 /* Read function for memory objects.  */
540 static ssize_t
541 es_func_mem_read (void *cookie, void *buffer, size_t size)
542 {
543   estream_cookie_mem_t mem_cookie = cookie;
544   ssize_t ret;
545
546   if (size > mem_cookie->data_len - mem_cookie->offset)
547     size = mem_cookie->data_len - mem_cookie->offset;
548
549   if (size)
550     {
551       memcpy (buffer, mem_cookie->memory + mem_cookie->offset, size);
552       mem_cookie->offset += size;
553     }
554   
555   ret = size;
556   return ret;
557 }
558
559
560 /* Write function for memory objects.  */
561 static ssize_t
562 es_func_mem_write (void *cookie, const void *buffer, size_t size)
563 {
564   estream_cookie_mem_t mem_cookie = cookie;
565   ssize_t ret;
566   size_t nleft;
567
568   if (!size)
569     return 0;  /* A flush is a NOP for memory objects.  */
570
571   if (mem_cookie->modeflags & O_APPEND)
572     {
573       /* Append to data.  */
574       mem_cookie->offset = mem_cookie->data_len;
575     }
576
577   assert (mem_cookie->memory_size >= mem_cookie->offset);
578   nleft = mem_cookie->memory_size - mem_cookie->offset;
579   
580   /* If we are not allowed to grow limit the size to the left space.  */
581   if (!mem_cookie->flags.grow && size > nleft)
582     size = nleft;
583
584   /* Enlarge the memory buffer if needed.  */
585   if (size > nleft)
586     {
587       unsigned char *newbuf;
588       size_t newsize;
589
590       if (!mem_cookie->memory_size)
591         newsize = size;  /* Not yet allocated.  */
592       else
593         newsize = mem_cookie->memory_size + (nleft - size);
594       if (newsize < mem_cookie->offset)
595         {
596           _set_errno (EINVAL);
597           return -1;
598         }
599
600       /* Round up to the next block length.  BLOCK_SIZE should always
601          be set; we check anyway.  */
602       if (mem_cookie->block_size)
603         {
604           newsize += mem_cookie->block_size - 1;
605           if (newsize < mem_cookie->offset)
606             {
607               _set_errno (EINVAL);
608               return -1;
609             }
610           newsize /= mem_cookie->block_size;
611           newsize *= mem_cookie->block_size;
612         }
613
614       /* Check for a total limit.  */
615       if (mem_cookie->memory_limit && newsize > mem_cookie->memory_limit)
616         {
617           _set_errno (ENOSPC);
618           return -1;
619         }
620       
621       newbuf = mem_cookie->func_realloc (mem_cookie->memory, newsize);
622       if (!newbuf)
623         return -1;
624       
625       mem_cookie->memory = newbuf;
626       mem_cookie->memory_size = newsize;
627
628       assert (mem_cookie->memory_size >= mem_cookie->offset);
629       nleft = mem_cookie->memory_size - mem_cookie->offset;
630       
631       assert (size <= nleft);
632     }
633       
634   memcpy (mem_cookie->memory + mem_cookie->offset, buffer, size);
635   if (mem_cookie->offset + size > mem_cookie->data_len)
636     mem_cookie->data_len = mem_cookie->offset + size;
637   mem_cookie->offset += size;
638
639   ret = size;
640   return ret;
641 }
642
643
644 /* Seek function for memory objects.  */
645 static int
646 es_func_mem_seek (void *cookie, off_t *offset, int whence)
647 {
648   estream_cookie_mem_t mem_cookie = cookie;
649   off_t pos_new;
650
651   switch (whence)
652     {
653     case SEEK_SET:
654       pos_new = *offset;
655       break;
656
657     case SEEK_CUR:
658       pos_new = mem_cookie->offset += *offset;
659       break;
660
661     case SEEK_END:
662       pos_new = mem_cookie->data_len += *offset;
663       break;
664
665     default:
666       _set_errno (EINVAL);
667       return -1;
668     }
669
670   if (pos_new > mem_cookie->memory_size)
671     {
672       size_t newsize;
673       void *newbuf;
674
675       if (!mem_cookie->flags.grow)
676         {
677           _set_errno (ENOSPC);
678           return -1;
679         }
680
681       newsize = pos_new + mem_cookie->block_size - 1;
682       if (newsize < pos_new)
683         {
684           _set_errno (EINVAL);
685           return -1;
686         }
687       newsize /= mem_cookie->block_size;
688       newsize *= mem_cookie->block_size;
689
690       if (mem_cookie->memory_limit && newsize > mem_cookie->memory_limit)
691         {
692           _set_errno (ENOSPC);
693           return -1;
694         }
695       
696       newbuf = mem_cookie->func_realloc (mem_cookie->memory, newsize);
697       if (!newbuf)
698         return -1;
699
700       mem_cookie->memory = newbuf;
701       mem_cookie->memory_size = newsize;
702     }
703
704   if (pos_new > mem_cookie->data_len)
705     {
706       /* Fill spare space with zeroes.  */
707       memset (mem_cookie->memory + mem_cookie->data_len,
708               0, pos_new - mem_cookie->data_len);
709       mem_cookie->data_len = pos_new;
710     }
711
712   mem_cookie->offset = pos_new;
713   *offset = pos_new;
714
715   return 0;
716 }
717
718
719 /* Destroy function for memory objects.  */
720 static int
721 es_func_mem_destroy (void *cookie)
722 {
723   estream_cookie_mem_t mem_cookie = cookie;
724
725   if (cookie)
726     {
727       mem_cookie->func_free (mem_cookie->memory);
728       mem_free (mem_cookie);
729     }
730   return 0;
731 }
732
733
734 static es_cookie_io_functions_t estream_functions_mem =
735   {
736     es_func_mem_read,
737     es_func_mem_write,
738     es_func_mem_seek,
739     es_func_mem_destroy
740   };
741
742
743 \f
744 /* Implementation of fd I/O.  */
745
746 /* Cookie for fd objects.  */
747 typedef struct estream_cookie_fd
748 {
749   int fd;        /* The file descriptor we are using for actual output.  */
750   int no_close;  /* If set we won't close the file descriptor.  */
751 } *estream_cookie_fd_t;
752
753 /* Create function for fd objects.  */
754 static int
755 es_func_fd_create (void **cookie, int fd, unsigned int modeflags, int no_close)
756 {
757   estream_cookie_fd_t fd_cookie;
758   int err;
759
760   fd_cookie = mem_alloc (sizeof (*fd_cookie));
761   if (! fd_cookie)
762     err = -1;
763   else
764     {
765 #ifdef HAVE_DOSISH_SYSTEM
766       /* Make sure it is in binary mode if requested.  */
767       if ( (modeflags & O_BINARY) )
768         setmode (fd, O_BINARY);
769 #else
770       (void)modeflags;
771 #endif
772       fd_cookie->fd = fd;
773       fd_cookie->no_close = no_close;
774       *cookie = fd_cookie;
775       err = 0;
776     }
777   
778   return err;
779 }
780
781 /* Read function for fd objects.  */
782 static ssize_t
783 es_func_fd_read (void *cookie, void *buffer, size_t size)
784
785 {
786   estream_cookie_fd_t file_cookie = cookie;
787   ssize_t bytes_read;
788   
789   if (IS_INVALID_FD (file_cookie->fd))
790     {
791       ESTREAM_SYS_YIELD ();
792       bytes_read = 0;
793     }
794   else
795     {
796       do 
797         bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size);
798       while (bytes_read == -1 && errno == EINTR);
799     }
800
801   return bytes_read;
802 }
803
804 /* Write function for fd objects.  */
805 static ssize_t
806 es_func_fd_write (void *cookie, const void *buffer, size_t size)
807 {
808   estream_cookie_fd_t file_cookie = cookie;
809   ssize_t bytes_written;
810
811   if (IS_INVALID_FD (file_cookie->fd))
812     {
813       ESTREAM_SYS_YIELD ();
814       bytes_written = size; /* Yeah:  Success writing to the bit bucket.  */
815     }
816   else
817     {
818       do
819         bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size);
820       while (bytes_written == -1 && errno == EINTR);
821     }
822
823   return bytes_written;
824 }
825
826 /* Seek function for fd objects.  */
827 static int
828 es_func_fd_seek (void *cookie, off_t *offset, int whence)
829 {
830   estream_cookie_fd_t file_cookie = cookie;
831   off_t offset_new;
832   int err;
833
834   if (IS_INVALID_FD (file_cookie->fd))
835     {
836       _set_errno (ESPIPE);
837       err = -1;
838     }
839   else
840     {
841       offset_new = lseek (file_cookie->fd, *offset, whence);
842       if (offset_new == -1)
843         err = -1;
844       else
845         {
846           *offset = offset_new;
847           err = 0;
848         }
849     }
850
851   return err;
852 }
853
854 /* Destroy function for fd objects.  */
855 static int
856 es_func_fd_destroy (void *cookie)
857 {
858   estream_cookie_fd_t fd_cookie = cookie;
859   int err;
860
861   if (fd_cookie)
862     {
863       if (IS_INVALID_FD (fd_cookie->fd))
864         err = 0;
865       else
866         err = fd_cookie->no_close? 0 : close (fd_cookie->fd);
867       mem_free (fd_cookie);
868     }
869   else
870     err = 0;
871
872   return err;
873 }
874
875
876 static es_cookie_io_functions_t estream_functions_fd =
877   {
878     es_func_fd_read,
879     es_func_fd_write,
880     es_func_fd_seek,
881     es_func_fd_destroy
882   };
883
884
885
886 \f
887 /* Implementation of FILE* I/O.  */
888
889 /* Cookie for fp objects.  */
890 typedef struct estream_cookie_fp
891 {
892   FILE *fp;      /* The file pointer we are using for actual output.  */
893   int no_close;  /* If set we won't close the file pointer.  */
894 } *estream_cookie_fp_t;
895
896 /* Create function for fd objects.  */
897 static int
898 es_func_fp_create (void **cookie, FILE *fp, 
899                    unsigned int modeflags, int no_close)
900 {
901   estream_cookie_fp_t fp_cookie;
902   int err;
903
904   fp_cookie = mem_alloc (sizeof *fp_cookie);
905   if (!fp_cookie)
906     err = -1;
907   else
908     {
909 #ifdef HAVE_DOSISH_SYSTEM
910       /* Make sure it is in binary mode if requested.  */
911       if ( (modeflags & O_BINARY) )
912         setmode (fileno (fp), O_BINARY);
913 #else
914       (void)modeflags;
915 #endif
916       fp_cookie->fp = fp;
917       fp_cookie->no_close = no_close;
918       *cookie = fp_cookie;
919       err = 0;
920     }
921   
922   return err;
923 }
924
925 /* Read function for FILE* objects.  */
926 static ssize_t
927 es_func_fp_read (void *cookie, void *buffer, size_t size)
928
929 {
930   estream_cookie_fp_t file_cookie = cookie;
931   ssize_t bytes_read;
932
933   if (file_cookie->fp)
934     bytes_read = fread (buffer, 1, size, file_cookie->fp);
935   else
936     bytes_read = 0;
937   if (!bytes_read && ferror (file_cookie->fp))
938     return -1;
939   return bytes_read;
940 }
941
942 /* Write function for FILE* objects.  */
943 static ssize_t
944 es_func_fp_write (void *cookie, const void *buffer, size_t size)
945                            
946 {
947   estream_cookie_fp_t file_cookie = cookie;
948   size_t bytes_written;
949
950
951   if (file_cookie->fp)
952     bytes_written = fwrite (buffer, 1, size, file_cookie->fp);
953   else
954     bytes_written = size; /* Successfully written to the bit bucket.  */
955   if (bytes_written != size)
956     return -1;
957   return bytes_written;
958 }
959
960 /* Seek function for FILE* objects.  */
961 static int
962 es_func_fp_seek (void *cookie, off_t *offset, int whence)
963 {
964   estream_cookie_fp_t file_cookie = cookie;
965   long int offset_new;
966
967   if (!file_cookie->fp)
968     {
969       _set_errno (ESPIPE);
970       return -1; 
971     }
972
973   if ( fseek (file_cookie->fp, (long int)*offset, whence) )
974     {
975       /* fprintf (stderr, "\nfseek failed: errno=%d (%s)\n", */
976       /*          errno,strerror (errno)); */
977       return -1;
978     }
979
980   offset_new = ftell (file_cookie->fp);
981   if (offset_new == -1)
982     {
983       /* fprintf (stderr, "\nftell failed: errno=%d (%s)\n",  */
984       /*          errno,strerror (errno)); */
985       return -1;
986     }
987   *offset = offset_new;
988   return 0;
989 }
990
991 /* Destroy function for fd objects.  */
992 static int
993 es_func_fp_destroy (void *cookie)
994 {
995   estream_cookie_fp_t fp_cookie = cookie;
996   int err;
997
998   if (fp_cookie)
999     {
1000       if (fp_cookie->fp)
1001         {
1002           fflush (fp_cookie->fp);
1003           err = fp_cookie->no_close? 0 : fclose (fp_cookie->fp);
1004         }
1005       else
1006         err = 0;
1007       mem_free (fp_cookie);
1008     }
1009   else
1010     err = 0;
1011
1012   return err;
1013 }
1014
1015
1016 static es_cookie_io_functions_t estream_functions_fp =
1017   {
1018     es_func_fp_read,
1019     es_func_fp_write,
1020     es_func_fp_seek,
1021     es_func_fp_destroy
1022   };
1023
1024
1025
1026 \f
1027 /* Implementation of file I/O.  */
1028
1029 /* Create function for file objects.  */
1030 static int
1031 es_func_file_create (void **cookie, int *filedes,
1032                      const char *path, unsigned int modeflags)
1033 {
1034   estream_cookie_fd_t file_cookie;
1035   int err;
1036   int fd;
1037
1038   err = 0;
1039   fd = -1;
1040
1041   file_cookie = mem_alloc (sizeof (*file_cookie));
1042   if (! file_cookie)
1043     {
1044       err = -1;
1045       goto out;
1046     }
1047
1048   fd = open (path, modeflags, ES_DEFAULT_OPEN_MODE);
1049   if (fd == -1)
1050     {
1051       err = -1;
1052       goto out;
1053     }
1054 #ifdef HAVE_DOSISH_SYSTEM
1055   /* Make sure it is in binary mode if requested.  */
1056   if ( (modeflags & O_BINARY) )
1057     setmode (fd, O_BINARY);
1058 #endif
1059
1060   file_cookie->fd = fd;
1061   file_cookie->no_close = 0;
1062   *cookie = file_cookie;
1063   *filedes = fd;
1064
1065  out:
1066
1067   if (err)
1068     mem_free (file_cookie);
1069
1070   return err;
1071 }
1072
1073 static es_cookie_io_functions_t estream_functions_file =
1074   {
1075     es_func_fd_read,
1076     es_func_fd_write,
1077     es_func_fd_seek,
1078     es_func_fd_destroy
1079   };
1080
1081 \f
1082 static int
1083 es_convert_mode (const char *mode, unsigned int *modeflags)
1084 {
1085   unsigned int omode, oflags;
1086
1087   switch (*mode)
1088     {
1089     case 'r':
1090       omode = O_RDONLY;
1091       oflags = 0;
1092       break;
1093     case 'w':
1094       omode = O_WRONLY;
1095       oflags = O_TRUNC | O_CREAT;
1096       break;
1097     case 'a':
1098       omode = O_WRONLY;
1099       oflags = O_APPEND | O_CREAT;
1100       break;
1101     default:
1102       _set_errno (EINVAL);
1103       return -1;
1104     }
1105   for (mode++; *mode; mode++)
1106     {
1107       switch (*mode)
1108         {
1109         case '+':
1110           omode = O_RDWR;
1111           break;
1112         case 'b':
1113           oflags |= O_BINARY;
1114           break;
1115         case 'x':
1116           oflags |= O_EXCL;
1117           break;
1118         default: /* Ignore unknown flags.  */
1119           break; 
1120         }
1121     }
1122
1123   *modeflags = (omode | oflags);
1124   return 0;
1125 }
1126
1127 \f
1128
1129 /*
1130  * Low level stream functionality.
1131  */
1132
1133 static int
1134 es_fill (estream_t stream)
1135 {
1136   size_t bytes_read = 0;
1137   int err;
1138
1139   if (!stream->intern->func_read)
1140     {
1141       _set_errno (EOPNOTSUPP);
1142       err = -1;
1143     }
1144   else
1145     {
1146       es_cookie_read_function_t func_read = stream->intern->func_read;
1147       ssize_t ret;
1148
1149       ret = (*func_read) (stream->intern->cookie,
1150                           stream->buffer, stream->buffer_size);
1151       if (ret == -1)
1152         {
1153           bytes_read = 0;
1154           err = -1;
1155         }
1156       else
1157         {
1158           bytes_read = ret;
1159           err = 0;
1160         }
1161     }
1162
1163   if (err)
1164     stream->intern->indicators.err = 1;
1165   else if (!bytes_read)
1166     stream->intern->indicators.eof = 1;
1167
1168   stream->intern->offset += stream->data_len;
1169   stream->data_len = bytes_read;
1170   stream->data_offset = 0;
1171
1172   return err;
1173 }
1174
1175 static int
1176 es_flush (estream_t stream)
1177 {
1178   es_cookie_write_function_t func_write = stream->intern->func_write;
1179   int err;
1180
1181   assert (stream->flags.writing);
1182
1183   if (stream->data_offset)
1184     {
1185       size_t bytes_written;
1186       size_t data_flushed;
1187       ssize_t ret;
1188
1189       if (! func_write)
1190         {
1191           err = EOPNOTSUPP;
1192           goto out;
1193         }
1194
1195       /* Note: to prevent an endless loop caused by user-provided
1196          write-functions that pretend to have written more bytes than
1197          they were asked to write, we have to check for
1198          "(stream->data_offset - data_flushed) > 0" instead of
1199          "stream->data_offset - data_flushed".  */
1200       
1201       data_flushed = 0;
1202       err = 0;
1203       
1204       while ((((ssize_t) (stream->data_offset - data_flushed)) > 0) && (! err))
1205         {
1206           ret = (*func_write) (stream->intern->cookie,
1207                                stream->buffer + data_flushed,
1208                                stream->data_offset - data_flushed);
1209           if (ret == -1)
1210             {
1211               bytes_written = 0;
1212               err = -1;
1213             }
1214           else
1215             bytes_written = ret;
1216
1217           data_flushed += bytes_written;
1218           if (err)
1219             break;
1220         }
1221
1222       stream->data_flushed += data_flushed;
1223       if (stream->data_offset == data_flushed)
1224         {
1225           stream->intern->offset += stream->data_offset;
1226           stream->data_offset = 0;
1227           stream->data_flushed = 0;
1228
1229           /* Propagate flush event.  */
1230           (*func_write) (stream->intern->cookie, NULL, 0);
1231         }
1232     }
1233   else
1234     err = 0;
1235
1236  out:
1237     
1238   if (err)
1239     stream->intern->indicators.err = 1;
1240
1241   return err;
1242 }
1243
1244 /* Discard buffered data for STREAM.  */
1245 static void
1246 es_empty (estream_t stream)
1247 {
1248   assert (!stream->flags.writing);
1249   stream->data_len = 0;
1250   stream->data_offset = 0;
1251   stream->unread_data_len = 0;
1252 }
1253
1254 /* Initialize STREAM.  */
1255 static void
1256 es_initialize (estream_t stream,
1257                void *cookie, int fd, es_cookie_io_functions_t functions,
1258                unsigned int modeflags)
1259 {
1260   stream->intern->cookie = cookie;
1261   stream->intern->opaque = NULL;
1262   stream->intern->offset = 0;
1263   stream->intern->func_read = functions.func_read;
1264   stream->intern->func_write = functions.func_write;
1265   stream->intern->func_seek = functions.func_seek;
1266   stream->intern->func_close = functions.func_close;
1267   stream->intern->strategy = _IOFBF;
1268   stream->intern->fd = fd;
1269   stream->intern->print_err = 0;
1270   stream->intern->print_errno = 0;
1271   stream->intern->print_ntotal = 0;
1272   stream->intern->print_fp = NULL;
1273   stream->intern->indicators.err = 0;
1274   stream->intern->indicators.eof = 0;
1275   stream->intern->is_stdstream = 0;
1276   stream->intern->stdstream_fd = 0;
1277   stream->intern->deallocate_buffer = 0;
1278
1279   stream->data_len = 0;
1280   stream->data_offset = 0;
1281   stream->data_flushed = 0;
1282   stream->unread_data_len = 0;
1283   /* Depending on the modeflags we set whether we start in writing or
1284      reading mode.  This is required in case we are working on a
1285      stream which is not seeekable (like stdout).  Without this
1286      pre-initialization we would do a seek at the first write call and
1287      as this will fail no utput will be delivered. */
1288   if ((modeflags & O_WRONLY) || (modeflags & O_RDWR) )
1289     stream->flags.writing = 1;
1290   else
1291     stream->flags.writing = 0;
1292 }
1293
1294 /* Deinitialize STREAM.  */
1295 static int
1296 es_deinitialize (estream_t stream)
1297 {
1298   es_cookie_close_function_t func_close;
1299   int err, tmp_err;
1300
1301   if (stream->intern->print_fp)
1302     {
1303       int save_errno = errno;
1304       fclose (stream->intern->print_fp);
1305       stream->intern->print_fp = NULL;
1306       _set_errno (save_errno);
1307     }
1308
1309   func_close = stream->intern->func_close;
1310
1311   err = 0;
1312   if (stream->flags.writing)
1313     SET_UNLESS_NONZERO (err, tmp_err, es_flush (stream));
1314   if (func_close)
1315     SET_UNLESS_NONZERO (err, tmp_err, (*func_close) (stream->intern->cookie));
1316
1317   
1318   return err;
1319 }
1320
1321 /* Create a new stream object, initialize it.  */
1322 static int
1323 es_create (estream_t *stream, void *cookie, int fd,
1324            es_cookie_io_functions_t functions, unsigned int modeflags,
1325            int with_locked_list)
1326 {
1327   estream_internal_t stream_internal_new;
1328   estream_t stream_new;
1329   int err;
1330
1331   stream_new = NULL;
1332   stream_internal_new = NULL;
1333
1334   stream_new = mem_alloc (sizeof (*stream_new));
1335   if (! stream_new)
1336     {
1337       err = -1;
1338       goto out;
1339     }
1340
1341   stream_internal_new = mem_alloc (sizeof (*stream_internal_new));
1342   if (! stream_internal_new)
1343     {
1344       err = -1;
1345       goto out;
1346     }
1347
1348   stream_new->buffer = stream_internal_new->buffer;
1349   stream_new->buffer_size = sizeof (stream_internal_new->buffer);
1350   stream_new->unread_buffer = stream_internal_new->unread_buffer;
1351   stream_new->unread_buffer_size = sizeof (stream_internal_new->unread_buffer);
1352   stream_new->intern = stream_internal_new;
1353
1354   ESTREAM_MUTEX_INITIALIZE (stream_new->intern->lock);
1355   es_initialize (stream_new, cookie, fd, functions, modeflags);
1356
1357   err = es_list_add (stream_new, with_locked_list);
1358   if (err)
1359     goto out;
1360
1361   *stream = stream_new;
1362
1363  out:
1364
1365   if (err)
1366     {
1367       if (stream_new)
1368         {
1369           es_deinitialize (stream_new);
1370           mem_free (stream_new);
1371         }
1372     }
1373
1374   return err;
1375 }
1376
1377 /* Deinitialize a stream object and destroy it.  */
1378 static int
1379 es_destroy (estream_t stream, int with_locked_list)
1380 {
1381   int err = 0;
1382
1383   if (stream)
1384     {
1385       es_list_remove (stream, with_locked_list);
1386       err = es_deinitialize (stream);
1387       mem_free (stream->intern);
1388       mem_free (stream);
1389     }
1390
1391   return err;
1392 }
1393
1394 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1395    unbuffered-mode, storing the amount of bytes read in
1396    *BYTES_READ.  */
1397 static int
1398 es_read_nbf (estream_t ES__RESTRICT stream,
1399              unsigned char *ES__RESTRICT buffer,
1400              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1401 {
1402   es_cookie_read_function_t func_read = stream->intern->func_read;
1403   size_t data_read;
1404   ssize_t ret;
1405   int err;
1406
1407   data_read = 0;
1408   err = 0;
1409
1410   while (bytes_to_read - data_read)
1411     {
1412       ret = (*func_read) (stream->intern->cookie,
1413                           buffer + data_read, bytes_to_read - data_read);
1414       if (ret == -1)
1415         {
1416           err = -1;
1417           break;
1418         }
1419       else if (ret)
1420         data_read += ret;
1421       else
1422         break;
1423     }
1424
1425   stream->intern->offset += data_read;
1426   *bytes_read = data_read;
1427
1428   return err;
1429 }
1430
1431 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1432    fully-buffered-mode, storing the amount of bytes read in
1433    *BYTES_READ.  */
1434 static int
1435 es_read_fbf (estream_t ES__RESTRICT stream,
1436              unsigned char *ES__RESTRICT buffer,
1437              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1438 {
1439   size_t data_available;
1440   size_t data_to_read;
1441   size_t data_read;
1442   int err;
1443
1444   data_read = 0;
1445   err = 0;
1446
1447   while ((bytes_to_read - data_read) && (! err))
1448     {
1449       if (stream->data_offset == stream->data_len)
1450         {
1451           /* Nothing more to read in current container, try to
1452              fill container with new data.  */
1453           err = es_fill (stream);
1454           if (! err)
1455             if (! stream->data_len)
1456               /* Filling did not result in any data read.  */
1457               break;
1458         }
1459
1460       if (! err)
1461         {
1462           /* Filling resulted in some new data.  */
1463
1464           data_to_read = bytes_to_read - data_read;
1465           data_available = stream->data_len - stream->data_offset;
1466           if (data_to_read > data_available)
1467             data_to_read = data_available;
1468
1469           memcpy (buffer + data_read,
1470                   stream->buffer + stream->data_offset, data_to_read);
1471           stream->data_offset += data_to_read;
1472           data_read += data_to_read;
1473         }
1474     }
1475
1476   *bytes_read = data_read;
1477
1478   return err;
1479 }
1480
1481 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1482    line-buffered-mode, storing the amount of bytes read in
1483    *BYTES_READ.  */
1484 static int
1485 es_read_lbf (estream_t ES__RESTRICT stream,
1486              unsigned char *ES__RESTRICT buffer,
1487              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1488 {
1489   int err;
1490
1491   err = es_read_fbf (stream, buffer, bytes_to_read, bytes_read);
1492
1493   return err;
1494 }
1495
1496 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER, storing
1497    *the amount of bytes read in BYTES_READ.  */
1498 static int
1499 es_readn (estream_t ES__RESTRICT stream,
1500           void *ES__RESTRICT buffer_arg,
1501           size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1502 {
1503   unsigned char *buffer = (unsigned char *)buffer_arg;
1504   size_t data_read_unread, data_read;
1505   int err;
1506
1507   data_read_unread = 0;
1508   data_read = 0;
1509   err = 0;
1510
1511   if (stream->flags.writing)
1512     {
1513       /* Switching to reading mode -> flush output.  */
1514       err = es_flush (stream);
1515       if (err)
1516         goto out;
1517       stream->flags.writing = 0;
1518     }  
1519
1520   /* Read unread data first.  */
1521   while ((bytes_to_read - data_read_unread) && stream->unread_data_len)
1522     {
1523       buffer[data_read_unread]
1524         = stream->unread_buffer[stream->unread_data_len - 1];
1525       stream->unread_data_len--;
1526       data_read_unread++;
1527     }
1528
1529   switch (stream->intern->strategy)
1530     {
1531     case _IONBF:
1532       err = es_read_nbf (stream,
1533                          buffer + data_read_unread,
1534                          bytes_to_read - data_read_unread, &data_read);
1535       break;
1536     case _IOLBF:
1537       err = es_read_lbf (stream,
1538                          buffer + data_read_unread,
1539                          bytes_to_read - data_read_unread, &data_read);
1540       break;
1541     case _IOFBF:
1542       err = es_read_fbf (stream,
1543                          buffer + data_read_unread,
1544                          bytes_to_read - data_read_unread, &data_read);
1545       break;
1546     }
1547
1548  out:
1549
1550   if (bytes_read)
1551     *bytes_read = data_read_unread + data_read;
1552
1553   return err;
1554 }
1555
1556 /* Try to unread DATA_N bytes from DATA into STREAM, storing the
1557    amount of bytes successfully unread in *BYTES_UNREAD.  */
1558 static void
1559 es_unreadn (estream_t ES__RESTRICT stream,
1560             const unsigned char *ES__RESTRICT data, size_t data_n,
1561             size_t *ES__RESTRICT bytes_unread)
1562 {
1563   size_t space_left;
1564
1565   space_left = stream->unread_buffer_size - stream->unread_data_len;
1566
1567   if (data_n > space_left)
1568     data_n = space_left;
1569
1570   if (! data_n)
1571     goto out;
1572
1573   memcpy (stream->unread_buffer + stream->unread_data_len, data, data_n);
1574   stream->unread_data_len += data_n;
1575   stream->intern->indicators.eof = 0;
1576
1577  out:
1578
1579   if (bytes_unread)
1580     *bytes_unread = data_n;
1581 }
1582
1583 /* Seek in STREAM.  */
1584 static int
1585 es_seek (estream_t ES__RESTRICT stream, off_t offset, int whence,
1586          off_t *ES__RESTRICT offset_new)
1587 {
1588   es_cookie_seek_function_t func_seek = stream->intern->func_seek;
1589   int err, ret;
1590   off_t off;
1591
1592   if (! func_seek)
1593     {
1594       _set_errno (EOPNOTSUPP);
1595       err = -1;
1596       goto out;
1597     }
1598
1599   if (stream->flags.writing)
1600     {
1601       /* Flush data first in order to prevent flushing it to the wrong
1602          offset.  */
1603       err = es_flush (stream);
1604       if (err)
1605         goto out;
1606       stream->flags.writing = 0;
1607     }
1608
1609   off = offset;
1610   if (whence == SEEK_CUR)
1611     {
1612       off = off - stream->data_len + stream->data_offset;
1613       off -= stream->unread_data_len;
1614     }
1615   
1616   ret = (*func_seek) (stream->intern->cookie, &off, whence);
1617   if (ret == -1)
1618     {
1619       err = -1;
1620       goto out;
1621     }
1622
1623   err = 0;
1624   es_empty (stream);
1625
1626   if (offset_new)
1627     *offset_new = off;
1628
1629   stream->intern->indicators.eof = 0;
1630   stream->intern->offset = off;
1631
1632  out:
1633   
1634   if (err)
1635     stream->intern->indicators.err = 1;
1636
1637   return err;
1638 }
1639
1640 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
1641    unbuffered-mode, storing the amount of bytes written in
1642    *BYTES_WRITTEN.  */
1643 static int
1644 es_write_nbf (estream_t ES__RESTRICT stream,
1645               const unsigned char *ES__RESTRICT buffer,
1646               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
1647 {
1648   es_cookie_write_function_t func_write = stream->intern->func_write;
1649   size_t data_written;
1650   ssize_t ret;
1651   int err;
1652
1653   if (bytes_to_write && (! func_write))
1654     {
1655       err = EOPNOTSUPP;
1656       goto out;
1657     }  
1658
1659   data_written = 0;
1660   err = 0;
1661   
1662   while (bytes_to_write - data_written)
1663     {
1664       ret = (*func_write) (stream->intern->cookie,
1665                            buffer + data_written,
1666                            bytes_to_write - data_written);
1667       if (ret == -1)
1668         {
1669           err = -1;
1670           break;
1671         }
1672       else
1673         data_written += ret;
1674     }
1675
1676   stream->intern->offset += data_written;
1677   *bytes_written = data_written;
1678
1679  out:
1680
1681   return err;
1682 }
1683
1684 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
1685    fully-buffered-mode, storing the amount of bytes written in
1686    *BYTES_WRITTEN.  */
1687 static int
1688 es_write_fbf (estream_t ES__RESTRICT stream,
1689               const unsigned char *ES__RESTRICT buffer,
1690               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
1691 {
1692   size_t space_available;
1693   size_t data_to_write;
1694   size_t data_written;
1695   int err;
1696
1697   data_written = 0;
1698   err = 0;
1699
1700   while ((bytes_to_write - data_written) && (! err))
1701     {
1702       if (stream->data_offset == stream->buffer_size)
1703         /* Container full, flush buffer.  */
1704         err = es_flush (stream);
1705
1706       if (! err)
1707         {
1708           /* Flushing resulted in empty container.  */
1709           
1710           data_to_write = bytes_to_write - data_written;
1711           space_available = stream->buffer_size - stream->data_offset;
1712           if (data_to_write > space_available)
1713             data_to_write = space_available;
1714               
1715           memcpy (stream->buffer + stream->data_offset,
1716                   buffer + data_written, data_to_write);
1717           stream->data_offset += data_to_write;
1718           data_written += data_to_write;
1719         }
1720     }
1721
1722   *bytes_written = data_written;
1723
1724   return err;
1725 }
1726
1727
1728 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
1729    line-buffered-mode, storing the amount of bytes written in
1730    *BYTES_WRITTEN.  */
1731 static int
1732 es_write_lbf (estream_t ES__RESTRICT stream,
1733               const unsigned char *ES__RESTRICT buffer,
1734               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
1735 {
1736   size_t data_flushed = 0;
1737   size_t data_buffered = 0;
1738   unsigned char *nlp;
1739   int err = 0;
1740
1741   nlp = memrchr (buffer, '\n', bytes_to_write);
1742   if (nlp)
1743     {
1744       /* Found a newline, directly write up to (including) this
1745          character.  */
1746       err = es_flush (stream);
1747       if (!err)
1748         err = es_write_nbf (stream, buffer, nlp - buffer + 1, &data_flushed);
1749     }
1750
1751   if (!err)
1752     {
1753       /* Write remaining data fully buffered.  */
1754       err = es_write_fbf (stream, buffer + data_flushed,
1755                           bytes_to_write - data_flushed, &data_buffered);
1756     }
1757
1758   *bytes_written = data_flushed + data_buffered;
1759   return err;
1760 }
1761
1762
1763 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in, storing the
1764    amount of bytes written in BYTES_WRITTEN.  */
1765 static int
1766 es_writen (estream_t ES__RESTRICT stream,
1767            const void *ES__RESTRICT buffer,
1768            size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
1769 {
1770   size_t data_written;
1771   int err;
1772
1773   data_written = 0;
1774   err = 0;
1775   
1776   if (!stream->flags.writing)
1777     {
1778       /* Switching to writing mode -> discard input data and seek to
1779          position at which reading has stopped.  We can do this only
1780          if a seek function has been registered. */
1781       if (stream->intern->func_seek)
1782         {
1783           err = es_seek (stream, 0, SEEK_CUR, NULL);
1784           if (err)
1785             {
1786               if (errno == ESPIPE)
1787                 err = 0;
1788               else
1789                 goto out;
1790             }
1791         }
1792     }
1793
1794   switch (stream->intern->strategy)
1795     {
1796     case _IONBF:
1797       err = es_write_nbf (stream, buffer, bytes_to_write, &data_written);
1798       break;
1799
1800     case _IOLBF:
1801       err = es_write_lbf (stream, buffer, bytes_to_write, &data_written);
1802       break;
1803
1804     case _IOFBF:
1805       err = es_write_fbf (stream, buffer, bytes_to_write, &data_written);
1806       break;
1807     }
1808
1809  out:
1810     
1811   if (bytes_written)
1812     *bytes_written = data_written;
1813   if (data_written)
1814     if (!stream->flags.writing)
1815       stream->flags.writing = 1;
1816
1817   return err;
1818 }
1819
1820
1821 static int
1822 es_peek (estream_t ES__RESTRICT stream, unsigned char **ES__RESTRICT data,
1823          size_t *ES__RESTRICT data_len)
1824 {
1825   int err;
1826
1827   if (stream->flags.writing)
1828     {
1829       /* Switching to reading mode -> flush output.  */
1830       err = es_flush (stream);
1831       if (err)
1832         goto out;
1833       stream->flags.writing = 0;
1834     }  
1835
1836   if (stream->data_offset == stream->data_len)
1837     {
1838       /* Refill container.  */
1839       err = es_fill (stream);
1840       if (err)
1841         goto out;
1842     }
1843   
1844   if (data)
1845     *data = stream->buffer + stream->data_offset;
1846   if (data_len)
1847     *data_len = stream->data_len - stream->data_offset;
1848   err = 0;
1849
1850  out:
1851
1852   return err;
1853 }
1854
1855
1856 /* Skip SIZE bytes of input data contained in buffer.  */
1857 static int
1858 es_skip (estream_t stream, size_t size)
1859 {
1860   int err;
1861
1862   if (stream->data_offset + size > stream->data_len)
1863     {
1864       _set_errno (EINVAL);
1865       err = -1;
1866     }
1867   else
1868     {
1869       stream->data_offset += size;
1870       err = 0;
1871     }
1872
1873   return err;
1874 }
1875
1876
1877 static int
1878 doreadline (estream_t ES__RESTRICT stream, size_t max_length,
1879             char *ES__RESTRICT *ES__RESTRICT line,
1880             size_t *ES__RESTRICT line_length)
1881 {
1882   size_t space_left;
1883   size_t line_size;
1884   estream_t line_stream;
1885   char *line_new;
1886   void *line_stream_cookie;
1887   char *newline;
1888   unsigned char *data;
1889   size_t data_len;
1890   int err;
1891
1892   line_new = NULL;
1893   line_stream = NULL;
1894   line_stream_cookie = NULL;
1895
1896   err = es_func_mem_create (&line_stream_cookie, NULL, 0, 0,
1897                             BUFFER_BLOCK_SIZE, 1,
1898                             mem_realloc, mem_free, 
1899                             O_RDWR,
1900                             0);
1901   if (err)
1902     goto out;
1903
1904   err = es_create (&line_stream, line_stream_cookie, -1,
1905                    estream_functions_mem, O_RDWR, 0);
1906   if (err)
1907     goto out;
1908
1909   space_left = max_length;
1910   line_size = 0;
1911   while (1)
1912     {
1913       if (max_length && (space_left == 1))
1914         break;
1915
1916       err = es_peek (stream, &data, &data_len);
1917       if (err || (! data_len))
1918         break;
1919
1920       if (data_len > (space_left - 1))
1921         data_len = space_left - 1;
1922
1923       newline = memchr (data, '\n', data_len);
1924       if (newline)
1925         {
1926           data_len = (newline - (char *) data) + 1;
1927           err = es_write (line_stream, data, data_len, NULL);
1928           if (! err)
1929             {
1930               space_left -= data_len;
1931               line_size += data_len;
1932               es_skip (stream, data_len);
1933               break;
1934             }
1935         }
1936       else
1937         {
1938           err = es_write (line_stream, data, data_len, NULL);
1939           if (! err)
1940             {
1941               space_left -= data_len;
1942               line_size += data_len;
1943               es_skip (stream, data_len);
1944             }
1945         }
1946       if (err)
1947         break;
1948     }
1949   if (err)
1950     goto out;
1951
1952   /* Complete line has been written to line_stream.  */
1953   
1954   if ((max_length > 1) && (! line_size))
1955     {
1956       stream->intern->indicators.eof = 1;
1957       goto out;
1958     }
1959
1960   err = es_seek (line_stream, 0, SEEK_SET, NULL);
1961   if (err)
1962     goto out;
1963
1964   if (! *line)
1965     {
1966       line_new = mem_alloc (line_size + 1);
1967       if (! line_new)
1968         {
1969           err = -1;
1970           goto out;
1971         }
1972     }
1973   else
1974     line_new = *line;
1975
1976   err = es_read (line_stream, line_new, line_size, NULL);
1977   if (err)
1978     goto out;
1979
1980   line_new[line_size] = '\0';
1981
1982   if (! *line)
1983     *line = line_new;
1984   if (line_length)
1985     *line_length = line_size;
1986
1987  out:
1988
1989   if (line_stream)
1990     es_destroy (line_stream, 0);
1991   else if (line_stream_cookie)
1992     es_func_mem_destroy (line_stream_cookie);
1993
1994   if (err)
1995     {
1996       if (! *line)
1997         mem_free (line_new);
1998       stream->intern->indicators.err = 1;
1999     }
2000
2001   return err;
2002 }
2003
2004
2005 /* Output fucntion used for estream_format.  */
2006 static int
2007 print_writer (void *outfncarg, const char *buf, size_t buflen)
2008 {
2009   estream_t stream = outfncarg;
2010   size_t nwritten;
2011   int rc;
2012
2013   nwritten = 0;
2014   rc = es_writen (stream, buf, buflen, &nwritten);
2015   stream->intern->print_ntotal += nwritten;
2016   return rc;
2017 }
2018
2019
2020 /* The core of our printf function.  This is called in locked state. */
2021 static int
2022 es_print (estream_t ES__RESTRICT stream,
2023           const char *ES__RESTRICT format, va_list ap)
2024 {
2025   int rc;
2026
2027   stream->intern->print_ntotal = 0;
2028   rc = estream_format (print_writer, stream, format, ap);
2029   if (rc)
2030     return -1;
2031   return (int)stream->intern->print_ntotal;
2032 }
2033
2034
2035 static void
2036 es_set_indicators (estream_t stream, int ind_err, int ind_eof)
2037 {
2038   if (ind_err != -1)
2039     stream->intern->indicators.err = ind_err ? 1 : 0;
2040   if (ind_eof != -1)
2041     stream->intern->indicators.eof = ind_eof ? 1 : 0;
2042 }
2043
2044
2045 static int
2046 es_get_indicator (estream_t stream, int ind_err, int ind_eof)
2047 {
2048   int ret = 0;
2049   
2050   if (ind_err)
2051     ret = stream->intern->indicators.err;
2052   else if (ind_eof)
2053     ret = stream->intern->indicators.eof;
2054
2055   return ret;
2056 }
2057
2058
2059 static int
2060 es_set_buffering (estream_t ES__RESTRICT stream,
2061                   char *ES__RESTRICT buffer, int mode, size_t size)
2062 {
2063   int err;
2064
2065   /* Flush or empty buffer depending on mode.  */
2066   if (stream->flags.writing)
2067     {
2068       err = es_flush (stream);
2069       if (err)
2070         goto out;
2071     }
2072   else
2073     es_empty (stream);
2074
2075   es_set_indicators (stream, -1, 0);
2076   
2077   /* Free old buffer in case that was allocated by this function.  */
2078   if (stream->intern->deallocate_buffer)
2079     {
2080       stream->intern->deallocate_buffer = 0;
2081       mem_free (stream->buffer);
2082       stream->buffer = NULL;
2083     }
2084
2085   if (mode == _IONBF)
2086     stream->buffer_size = 0;
2087   else
2088     {
2089       void *buffer_new;
2090       
2091       if (buffer)
2092         buffer_new = buffer;
2093       else
2094         {
2095           if (!size)
2096             size = BUFSIZ;
2097           buffer_new = mem_alloc (size);
2098           if (! buffer_new)
2099             {
2100               err = -1;
2101               goto out;
2102             }
2103         }
2104
2105       stream->buffer = buffer_new;
2106       stream->buffer_size = size;
2107       if (! buffer)
2108         stream->intern->deallocate_buffer = 1;
2109     }
2110   stream->intern->strategy = mode;
2111   err = 0;
2112
2113  out:
2114
2115   return err;
2116 }
2117
2118
2119 static off_t
2120 es_offset_calculate (estream_t stream)
2121 {
2122   off_t offset;
2123
2124   offset = stream->intern->offset + stream->data_offset;
2125   if (offset < stream->unread_data_len)
2126     /* Offset undefined.  */
2127     offset = 0;
2128   else
2129     offset -= stream->unread_data_len;
2130
2131   return offset;
2132 }
2133
2134
2135 static void
2136 es_opaque_ctrl (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque_new,
2137                 void **ES__RESTRICT opaque_old)
2138 {
2139   if (opaque_old)
2140     *opaque_old = stream->intern->opaque;
2141   if (opaque_new)
2142     stream->intern->opaque = opaque_new;
2143 }
2144
2145
2146 static int
2147 es_get_fd (estream_t stream)
2148 {
2149   return stream->intern->fd;
2150 }
2151
2152 \f
2153
2154 /* API.  */
2155
2156 int
2157 es_init (void)
2158 {
2159   int err;
2160
2161   err = es_init_do ();
2162
2163   return err;
2164 }
2165
2166 estream_t
2167 es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode)
2168 {
2169   unsigned int modeflags;
2170   int create_called;
2171   estream_t stream;
2172   void *cookie;
2173   int err;
2174   int fd;
2175
2176   stream = NULL;
2177   cookie = NULL;
2178   create_called = 0;
2179
2180   err = es_convert_mode (mode, &modeflags);
2181   if (err)
2182     goto out;
2183   
2184   err = es_func_file_create (&cookie, &fd, path, modeflags);
2185   if (err)
2186     goto out;
2187
2188   create_called = 1;
2189   err = es_create (&stream, cookie, fd, estream_functions_file, modeflags, 0);
2190   if (err)
2191     goto out;
2192
2193  out:
2194   
2195   if (err && create_called)
2196     (*estream_functions_file.func_close) (cookie);
2197
2198   return stream;
2199 }
2200
2201
2202 estream_t
2203 es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len,
2204           unsigned int grow,
2205           func_realloc_t func_realloc, func_free_t func_free,
2206           const char *ES__RESTRICT mode)
2207 {
2208   unsigned int modeflags;
2209   int create_called;
2210   estream_t stream;
2211   void *cookie;
2212   int err;
2213
2214   cookie = 0;
2215   stream = NULL;
2216   create_called = 0;
2217   
2218   err = es_convert_mode (mode, &modeflags);
2219   if (err)
2220     goto out;
2221
2222   err = es_func_mem_create (&cookie, data, data_n, data_len,
2223                             BUFFER_BLOCK_SIZE, grow, 
2224                             func_realloc, func_free, modeflags, 0);
2225   if (err)
2226     goto out;
2227   
2228   create_called = 1;
2229   err = es_create (&stream, cookie, -1, estream_functions_mem, modeflags, 0);
2230
2231  out:
2232
2233   if (err && create_called)
2234     (*estream_functions_mem.func_close) (cookie);
2235
2236   return stream;
2237 }
2238
2239
2240 estream_t
2241 es_fopenmem (size_t memlimit, const char *ES__RESTRICT mode)
2242 {
2243   unsigned int modeflags;
2244   estream_t stream = NULL;
2245   void *cookie = NULL;
2246
2247   /* Memory streams are always read/write.  We use MODE only to get
2248      the append flag.  */
2249   if (es_convert_mode (mode, &modeflags))
2250     return NULL;
2251   modeflags |= O_RDWR;
2252
2253   
2254   if (es_func_mem_create (&cookie, NULL, 0, 0,
2255                           BUFFER_BLOCK_SIZE, 1,
2256                           mem_realloc, mem_free, modeflags,
2257                           memlimit))
2258     return NULL;
2259   
2260   if (es_create (&stream, cookie, -1, estream_functions_mem, modeflags, 0))
2261     (*estream_functions_mem.func_close) (cookie);
2262
2263   return stream;
2264 }
2265
2266
2267
2268 estream_t
2269 es_fopencookie (void *ES__RESTRICT cookie,
2270                 const char *ES__RESTRICT mode,
2271                 es_cookie_io_functions_t functions)
2272 {
2273   unsigned int modeflags;
2274   estream_t stream;
2275   int err;
2276
2277   stream = NULL;
2278   modeflags = 0;
2279   
2280   err = es_convert_mode (mode, &modeflags);
2281   if (err)
2282     goto out;
2283
2284   err = es_create (&stream, cookie, -1, functions, modeflags, 0);
2285   if (err)
2286     goto out;
2287
2288  out:
2289
2290   return stream;
2291 }
2292
2293
2294 estream_t
2295 do_fdopen (int filedes, const char *mode, int no_close, int with_locked_list)
2296 {
2297   unsigned int modeflags;
2298   int create_called;
2299   estream_t stream;
2300   void *cookie;
2301   int err;
2302
2303   stream = NULL;
2304   cookie = NULL;
2305   create_called = 0;
2306
2307   err = es_convert_mode (mode, &modeflags);
2308   if (err)
2309     goto out;
2310
2311   err = es_func_fd_create (&cookie, filedes, modeflags, no_close);
2312   if (err)
2313     goto out;
2314
2315   create_called = 1;
2316   err = es_create (&stream, cookie, filedes, estream_functions_fd,
2317                    modeflags, with_locked_list);
2318
2319  out:
2320
2321   if (err && create_called)
2322     (*estream_functions_fd.func_close) (cookie);
2323
2324   return stream;
2325 }
2326
2327 estream_t
2328 es_fdopen (int filedes, const char *mode)
2329 {
2330   return do_fdopen (filedes, mode, 0, 0);
2331 }
2332
2333 /* A variant of es_fdopen which does not close FILEDES at the end.  */
2334 estream_t
2335 es_fdopen_nc (int filedes, const char *mode)
2336 {
2337   return do_fdopen (filedes, mode, 1, 0);
2338 }
2339
2340
2341 estream_t
2342 do_fpopen (FILE *fp, const char *mode, int no_close, int with_locked_list)
2343 {
2344   unsigned int modeflags;
2345   int create_called;
2346   estream_t stream;
2347   void *cookie;
2348   int err;
2349
2350   stream = NULL;
2351   cookie = NULL;
2352   create_called = 0;
2353
2354   err = es_convert_mode (mode, &modeflags);
2355   if (err)
2356     goto out;
2357
2358   if (fp)
2359     fflush (fp);
2360   err = es_func_fp_create (&cookie, fp, modeflags, no_close);
2361   if (err)
2362     goto out;
2363   
2364   create_called = 1;
2365   err = es_create (&stream, cookie, fp? fileno (fp):-1, estream_functions_fp,
2366                    modeflags, with_locked_list);
2367
2368  out:
2369
2370   if (err && create_called)
2371     (*estream_functions_fp.func_close) (cookie);
2372
2373   return stream;
2374 }
2375
2376   
2377 /* Create an estream from the stdio stream FP.  This mechanism is
2378    useful in case the stdio streams have special properties and may
2379    not be mixed with fd based functions.  This is for example the case
2380    under Windows where the 3 standard streams are associated with the
2381    console whereas a duped and fd-opened stream of one of this stream
2382    won't be associated with the console.  As this messes things up it
2383    is easier to keep on using the standard I/O stream as a backend for
2384    estream. */
2385 estream_t
2386 es_fpopen (FILE *fp, const char *mode)
2387 {
2388   return do_fpopen (fp, mode, 0, 0);
2389 }
2390
2391
2392 /* Same as es_fpopen but does not close  FP at the end.  */
2393 estream_t
2394 es_fpopen_nc (FILE *fp, const char *mode)
2395 {
2396   return do_fpopen (fp, mode, 1, 0);
2397 }
2398
2399
2400 /* Set custom standard descriptors to be used for stdin, stdout and
2401    stderr.  This function needs to be called before any of the
2402    standard streams are accessed.  */
2403 void
2404 _es_set_std_fd (int no, int fd)
2405 {
2406   ESTREAM_LIST_LOCK;
2407   if (no >= 0 && no < 3 && !custom_std_fds_valid[no])
2408     {
2409       custom_std_fds[no] = fd;
2410       custom_std_fds_valid[no] = 1;
2411     }
2412   ESTREAM_LIST_UNLOCK;
2413 }
2414
2415
2416 /* Return the stream used for stdin, stdout or stderr.  */
2417 estream_t
2418 _es_get_std_stream (int fd)
2419 {
2420   estream_list_t list_obj;
2421   estream_t stream = NULL;
2422
2423   fd %= 3; /* We only allow 0, 1 or 2 but we don't want to return an error. */
2424   ESTREAM_LIST_LOCK;
2425   for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
2426     if (list_obj->car->intern->is_stdstream
2427         && list_obj->car->intern->stdstream_fd == fd)
2428       {
2429         stream = list_obj->car;
2430         break;
2431       }
2432   if (!stream)
2433     {
2434       /* Standard stream not yet created.  We first try to create them
2435          from registered file descriptors.  */
2436       if (!fd && custom_std_fds_valid[0])
2437         stream = do_fdopen (custom_std_fds[0], "r", 1, 1);
2438       else if (fd == 1 && custom_std_fds_valid[1])
2439         stream = do_fdopen (custom_std_fds[1], "a", 1, 1);
2440       else if (custom_std_fds_valid[2])
2441         stream = do_fdopen (custom_std_fds[1], "a", 1, 1);
2442       
2443       if (!stream)
2444         {
2445           /* Second try is to use the standard C streams.  */
2446           if (!fd)
2447             stream = do_fpopen (stdin, "r", 1, 1);
2448           else if (fd == 1)
2449             stream = do_fpopen (stdout, "a", 1, 1);
2450           else
2451             stream = do_fpopen (stderr, "a", 1, 1);
2452         }
2453       
2454       if (!stream) 
2455         {
2456           /* Last try: Create a bit bucket.  */
2457           stream = do_fpopen (NULL, fd? "a":"r", 0, 1);
2458           if (!stream)
2459             {
2460               fprintf (stderr, "fatal: error creating a dummy estream"
2461                        " for %d: %s\n", fd, strerror (errno));
2462               abort();
2463             }
2464         }
2465
2466       stream->intern->is_stdstream = 1;
2467       stream->intern->stdstream_fd = fd;
2468       if (fd == 2)
2469         es_set_buffering (stream, NULL, _IOLBF, 0);
2470     }
2471   ESTREAM_LIST_UNLOCK;
2472   return stream;
2473 }
2474
2475
2476 estream_t
2477 es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode,
2478             estream_t ES__RESTRICT stream)
2479 {
2480   int err;
2481
2482   if (path)
2483     {
2484       unsigned int modeflags;
2485       int create_called;
2486       void *cookie;
2487       int fd;
2488
2489       cookie = NULL;
2490       create_called = 0;
2491       
2492       ESTREAM_LOCK (stream);
2493
2494       es_deinitialize (stream);
2495
2496       err = es_convert_mode (mode, &modeflags);
2497       if (err)
2498         goto leave;
2499       
2500       err = es_func_file_create (&cookie, &fd, path, modeflags);
2501       if (err)
2502         goto leave;
2503
2504       create_called = 1;
2505       es_initialize (stream, cookie, fd, estream_functions_file, modeflags);
2506
2507     leave:
2508
2509       if (err)
2510         {
2511           if (create_called)
2512             es_func_fd_destroy (cookie);
2513       
2514           es_destroy (stream, 0);
2515           stream = NULL;
2516         }
2517       else
2518         ESTREAM_UNLOCK (stream);
2519     }
2520   else
2521     {
2522       /* FIXME?  We don't support re-opening at the moment.  */
2523       _set_errno (EINVAL);
2524       es_deinitialize (stream);
2525       es_destroy (stream, 0);
2526       stream = NULL;
2527     }
2528
2529   return stream;
2530 }
2531
2532
2533 int
2534 es_fclose (estream_t stream)
2535 {
2536   int err;
2537
2538   err = es_destroy (stream, 0);
2539
2540   return err;
2541 }
2542
2543 int
2544 es_fileno_unlocked (estream_t stream)
2545 {
2546   return es_get_fd (stream);
2547 }
2548
2549
2550 void
2551 es_flockfile (estream_t stream)
2552 {
2553   ESTREAM_LOCK (stream);
2554 }
2555
2556
2557 int
2558 es_ftrylockfile (estream_t stream)
2559 {
2560   return ESTREAM_TRYLOCK (stream);
2561 }
2562
2563
2564 void
2565 es_funlockfile (estream_t stream)
2566 {
2567   ESTREAM_UNLOCK (stream);
2568 }
2569
2570
2571 int
2572 es_fileno (estream_t stream)
2573 {
2574   int ret;
2575
2576   ESTREAM_LOCK (stream);
2577   ret = es_fileno_unlocked (stream);
2578   ESTREAM_UNLOCK (stream);
2579
2580   return ret;
2581 }
2582
2583
2584 int
2585 es_feof_unlocked (estream_t stream)
2586 {
2587   return es_get_indicator (stream, 0, 1);
2588 }
2589
2590
2591 int
2592 es_feof (estream_t stream)
2593 {
2594   int ret;
2595
2596   ESTREAM_LOCK (stream);
2597   ret = es_feof_unlocked (stream);
2598   ESTREAM_UNLOCK (stream);
2599
2600   return ret;
2601 }
2602
2603
2604 int
2605 es_ferror_unlocked (estream_t stream)
2606 {
2607   return es_get_indicator (stream, 1, 0);
2608 }
2609
2610
2611 int
2612 es_ferror (estream_t stream)
2613 {
2614   int ret;
2615
2616   ESTREAM_LOCK (stream);
2617   ret = es_ferror_unlocked (stream);
2618   ESTREAM_UNLOCK (stream);
2619
2620   return ret;
2621 }
2622
2623
2624 void
2625 es_clearerr_unlocked (estream_t stream)
2626 {
2627   es_set_indicators (stream, 0, 0);
2628 }
2629
2630
2631 void
2632 es_clearerr (estream_t stream)
2633 {
2634   ESTREAM_LOCK (stream);
2635   es_clearerr_unlocked (stream);
2636   ESTREAM_UNLOCK (stream);
2637 }
2638
2639
2640 static int
2641 do_fflush (estream_t stream)
2642 {
2643   int err;
2644   
2645   if (stream->flags.writing)
2646     err = es_flush (stream);
2647   else
2648     {
2649       es_empty (stream);
2650       err = 0;
2651     }
2652
2653   return err;
2654 }
2655
2656
2657 int
2658 es_fflush (estream_t stream)
2659 {
2660   int err;
2661   
2662   if (stream)
2663     {
2664       ESTREAM_LOCK (stream);
2665       err = do_fflush (stream);
2666       ESTREAM_UNLOCK (stream);
2667     }
2668   else
2669     err = es_list_iterate (do_fflush);
2670
2671   return err ? EOF : 0;
2672 }
2673
2674
2675 int
2676 es_fseek (estream_t stream, long int offset, int whence)
2677 {
2678   int err;
2679
2680   ESTREAM_LOCK (stream);
2681   err = es_seek (stream, offset, whence, NULL);
2682   ESTREAM_UNLOCK (stream);
2683
2684   return err;
2685 }
2686
2687
2688 int
2689 es_fseeko (estream_t stream, off_t offset, int whence)
2690 {
2691   int err;
2692   
2693   ESTREAM_LOCK (stream);
2694   err = es_seek (stream, offset, whence, NULL);
2695   ESTREAM_UNLOCK (stream);
2696
2697   return err;
2698 }
2699
2700
2701 long int
2702 es_ftell (estream_t stream)
2703 {
2704   long int ret;
2705   
2706   ESTREAM_LOCK (stream);
2707   ret = es_offset_calculate (stream);
2708   ESTREAM_UNLOCK (stream);
2709
2710   return ret;
2711 }
2712
2713
2714 off_t
2715 es_ftello (estream_t stream)
2716 {
2717   off_t ret = -1;
2718
2719   ESTREAM_LOCK (stream);
2720   ret = es_offset_calculate (stream);
2721   ESTREAM_UNLOCK (stream);
2722
2723   return ret;
2724 }
2725
2726
2727 void
2728 es_rewind (estream_t stream)
2729 {
2730   ESTREAM_LOCK (stream);
2731   es_seek (stream, 0L, SEEK_SET, NULL);
2732   es_set_indicators (stream, 0, -1);
2733   ESTREAM_UNLOCK (stream);
2734 }
2735
2736
2737 int
2738 _es_getc_underflow (estream_t stream)
2739 {
2740   int err;
2741   unsigned char c;
2742   size_t bytes_read;
2743
2744   err = es_readn (stream, &c, 1, &bytes_read);
2745
2746   return (err || (! bytes_read)) ? EOF : c;
2747 }
2748
2749
2750 int
2751 _es_putc_overflow (int c, estream_t stream)
2752 {
2753   unsigned char d = c;
2754   int err;
2755
2756   err = es_writen (stream, &d, 1, NULL);
2757
2758   return err ? EOF : c;
2759 }
2760
2761
2762 int
2763 es_fgetc (estream_t stream)
2764 {
2765   int ret;
2766   
2767   ESTREAM_LOCK (stream);
2768   ret = es_getc_unlocked (stream);
2769   ESTREAM_UNLOCK (stream);
2770
2771   return ret;
2772 }
2773
2774
2775 int
2776 es_fputc (int c, estream_t stream)
2777 {
2778   int ret;
2779   
2780   ESTREAM_LOCK (stream);
2781   ret = es_putc_unlocked (c, stream);
2782   ESTREAM_UNLOCK (stream);
2783
2784   return ret;
2785 }
2786
2787
2788 int
2789 es_ungetc (int c, estream_t stream)
2790 {
2791   unsigned char data = (unsigned char) c;
2792   size_t data_unread;
2793
2794   ESTREAM_LOCK (stream);
2795   es_unreadn (stream, &data, 1, &data_unread);
2796   ESTREAM_UNLOCK (stream);
2797
2798   return data_unread ? c : EOF;
2799 }
2800
2801
2802 int
2803 es_read (estream_t ES__RESTRICT stream,
2804          void *ES__RESTRICT buffer, size_t bytes_to_read,
2805          size_t *ES__RESTRICT bytes_read)
2806 {
2807   int err;
2808
2809   if (bytes_to_read)
2810     {
2811       ESTREAM_LOCK (stream);
2812       err = es_readn (stream, buffer, bytes_to_read, bytes_read);
2813       ESTREAM_UNLOCK (stream);
2814     }
2815   else
2816     err = 0;
2817
2818   return err;
2819 }
2820
2821
2822 int
2823 es_write (estream_t ES__RESTRICT stream,
2824           const void *ES__RESTRICT buffer, size_t bytes_to_write,
2825           size_t *ES__RESTRICT bytes_written)
2826 {
2827   int err;
2828
2829   if (bytes_to_write)
2830     {
2831       ESTREAM_LOCK (stream);
2832       err = es_writen (stream, buffer, bytes_to_write, bytes_written);
2833       ESTREAM_UNLOCK (stream);
2834     }
2835   else
2836     err = 0;
2837
2838   return err;
2839 }
2840
2841
2842 size_t
2843 es_fread (void *ES__RESTRICT ptr, size_t size, size_t nitems,
2844           estream_t ES__RESTRICT stream)
2845 {
2846   size_t ret, bytes;
2847   int err;
2848
2849   if (size * nitems)
2850     {
2851       ESTREAM_LOCK (stream);
2852       err = es_readn (stream, ptr, size * nitems, &bytes);
2853       ESTREAM_UNLOCK (stream);
2854
2855       ret = bytes / size;
2856     }
2857   else
2858     ret = 0;
2859
2860   return ret;
2861 }
2862
2863
2864 size_t
2865 es_fwrite (const void *ES__RESTRICT ptr, size_t size, size_t nitems,
2866            estream_t ES__RESTRICT stream)
2867 {
2868   size_t ret, bytes;
2869   int err;
2870
2871   if (size * nitems)
2872     {
2873       ESTREAM_LOCK (stream);
2874       err = es_writen (stream, ptr, size * nitems, &bytes);
2875       ESTREAM_UNLOCK (stream);
2876
2877       ret = bytes / size;
2878     }
2879   else
2880     ret = 0;
2881
2882   return ret;
2883 }
2884
2885
2886 char *
2887 es_fgets (char *ES__RESTRICT buffer, int length, estream_t ES__RESTRICT stream)
2888 {
2889   unsigned char *s = (unsigned char*)buffer;
2890   int c;
2891    
2892   if (!length)
2893     return NULL;
2894      
2895   c = EOF;
2896   ESTREAM_LOCK (stream);
2897   while (length > 1 && (c = es_getc_unlocked (stream)) != EOF && c != '\n')
2898     {
2899       *s++ = c;
2900       length--;
2901     }
2902   ESTREAM_UNLOCK (stream);
2903
2904   if (c == EOF && s == (unsigned char*)buffer)
2905     return NULL; /* Nothing read.  */
2906
2907   if (c != EOF && length > 1)
2908     *s++ = c;
2909
2910   *s = 0;
2911   return buffer;
2912 }
2913
2914
2915 int
2916 es_fputs_unlocked (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream)
2917 {
2918   size_t length;
2919   int err;
2920
2921   length = strlen (s);
2922   err = es_writen (stream, s, length, NULL);
2923   return err ? EOF : 0;
2924 }
2925
2926 int
2927 es_fputs (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream)
2928 {
2929   size_t length;
2930   int err;
2931
2932   length = strlen (s);
2933   ESTREAM_LOCK (stream);
2934   err = es_writen (stream, s, length, NULL);
2935   ESTREAM_UNLOCK (stream);
2936
2937   return err ? EOF : 0;
2938 }
2939
2940
2941 ssize_t
2942 es_getline (char *ES__RESTRICT *ES__RESTRICT lineptr, size_t *ES__RESTRICT n,
2943             estream_t ES__RESTRICT stream)
2944 {
2945   char *line = NULL;
2946   size_t line_n = 0;
2947   int err;
2948
2949   ESTREAM_LOCK (stream);
2950   err = doreadline (stream, 0, &line, &line_n);
2951   ESTREAM_UNLOCK (stream);
2952   if (err)
2953     goto out;
2954
2955   if (*n)
2956     {
2957       /* Caller wants us to use his buffer.  */
2958       
2959       if (*n < (line_n + 1))
2960         {
2961           /* Provided buffer is too small -> resize.  */
2962
2963           void *p;
2964
2965           p = mem_realloc (*lineptr, line_n + 1);
2966           if (! p)
2967             err = -1;
2968           else
2969             {
2970               if (*lineptr != p)
2971                 *lineptr = p;
2972             }
2973         }
2974
2975       if (! err)
2976         {
2977           memcpy (*lineptr, line, line_n + 1);
2978           if (*n != line_n)
2979             *n = line_n;
2980         }
2981       mem_free (line);
2982     }
2983   else
2984     {
2985       /* Caller wants new buffers.  */
2986       *lineptr = line;
2987       *n = line_n;
2988     }
2989
2990  out:
2991
2992   return err ? err : (ssize_t)line_n;
2993 }
2994
2995
2996
2997 /* Same as fgets() but if the provided buffer is too short a larger
2998    one will be allocated.  This is similar to getline. A line is
2999    considered a byte stream ending in a LF.
3000
3001    If MAX_LENGTH is not NULL, it shall point to a value with the
3002    maximum allowed allocation.  
3003
3004    Returns the length of the line. EOF is indicated by a line of
3005    length zero. A truncated line is indicated my setting the value at
3006    MAX_LENGTH to 0.  If the returned value is less then 0 not enough
3007    memory was enable or another error occurred; ERRNO is then set
3008    accordingly.
3009
3010    If a line has been truncated, the file pointer is moved forward to
3011    the end of the line so that the next read starts with the next
3012    line.  Note that MAX_LENGTH must be re-initialzied in this case.
3013
3014    The caller initially needs to provide the address of a variable,
3015    initialized to NULL, at ADDR_OF_BUFFER and don't change this value
3016    anymore with the following invocations.  LENGTH_OF_BUFFER should be
3017    the address of a variable, initialized to 0, which is also
3018    maintained by this function.  Thus, both paramaters should be
3019    considered the state of this function.
3020
3021    Note: The returned buffer is allocated with enough extra space to
3022    allow the caller to append a CR,LF,Nul.  The buffer should be
3023    released using es_free.
3024  */
3025 ssize_t
3026 es_read_line (estream_t stream, 
3027               char **addr_of_buffer, size_t *length_of_buffer,
3028               size_t *max_length)
3029 {
3030   int c;
3031   char  *buffer = *addr_of_buffer;
3032   size_t length = *length_of_buffer;
3033   size_t nbytes = 0;
3034   size_t maxlen = max_length? *max_length : 0;
3035   char *p;
3036
3037   if (!buffer)
3038     { 
3039       /* No buffer given - allocate a new one. */
3040       length = 256;
3041       buffer = mem_alloc (length);
3042       *addr_of_buffer = buffer;
3043       if (!buffer)
3044         {
3045           *length_of_buffer = 0;
3046           if (max_length)
3047             *max_length = 0;
3048           return -1;
3049         }
3050       *length_of_buffer = length;
3051     }
3052
3053   if (length < 4)
3054     {
3055       /* This should never happen. If it does, the function has been
3056          called with wrong arguments. */
3057       _set_errno (EINVAL);
3058       return -1;
3059     }
3060   length -= 3; /* Reserve 3 bytes for CR,LF,EOL. */
3061
3062   ESTREAM_LOCK (stream);
3063   p = buffer;
3064   while  ((c = es_getc_unlocked (stream)) != EOF)
3065     {
3066       if (nbytes == length)
3067         { 
3068           /* Enlarge the buffer. */
3069           if (maxlen && length > maxlen) 
3070             {
3071               /* We are beyond our limit: Skip the rest of the line. */
3072               while (c != '\n' && (c=es_getc_unlocked (stream)) != EOF)
3073                 ;
3074               *p++ = '\n'; /* Always append a LF (we reserved some space). */
3075               nbytes++;
3076               if (max_length)
3077                 *max_length = 0; /* Indicate truncation. */
3078               break; /* the while loop. */
3079             }
3080           length += 3; /* Adjust for the reserved bytes. */
3081           length += length < 1024? 256 : 1024;
3082           *addr_of_buffer = mem_realloc (buffer, length);
3083           if (!*addr_of_buffer)
3084             {
3085               int save_errno = errno;
3086               mem_free (buffer); 
3087               *length_of_buffer = 0;
3088               if (max_length)
3089                 *max_length = 0;
3090               ESTREAM_UNLOCK (stream);
3091               _set_errno (save_errno);
3092               return -1;
3093             }
3094           buffer = *addr_of_buffer;
3095           *length_of_buffer = length;
3096           length -= 3; 
3097           p = buffer + nbytes;
3098         }
3099       *p++ = c;
3100       nbytes++;
3101       if (c == '\n')
3102         break;
3103     }
3104   *p = 0; /* Make sure the line is a string. */
3105   ESTREAM_UNLOCK (stream);
3106
3107   return nbytes;
3108 }
3109
3110 /* Wrapper around free() to match the memory allocation system used
3111    by estream.  Should be used for all buffers returned to the caller
3112    by libestream. */
3113 void
3114 es_free (void *a)
3115 {
3116   mem_free (a);
3117 }
3118
3119
3120 int
3121 es_vfprintf_unlocked (estream_t ES__RESTRICT stream,
3122                       const char *ES__RESTRICT format,
3123                       va_list ap)
3124 {
3125   return es_print (stream, format, ap);
3126 }
3127
3128
3129 int
3130 es_vfprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format,
3131              va_list ap)
3132 {
3133   int ret;
3134   
3135   ESTREAM_LOCK (stream);
3136   ret = es_print (stream, format, ap);
3137   ESTREAM_UNLOCK (stream);
3138
3139   return ret;
3140 }
3141
3142
3143 int
3144 es_fprintf_unlocked (estream_t ES__RESTRICT stream,
3145                      const char *ES__RESTRICT format, ...)
3146 {
3147   int ret;
3148   
3149   va_list ap;
3150   va_start (ap, format);
3151   ret = es_print (stream, format, ap);
3152   va_end (ap);
3153
3154   return ret;
3155 }
3156
3157
3158 int
3159 es_fprintf (estream_t ES__RESTRICT stream,
3160             const char *ES__RESTRICT format, ...)
3161 {
3162   int ret;
3163   
3164   va_list ap;
3165   va_start (ap, format);
3166   ESTREAM_LOCK (stream);
3167   ret = es_print (stream, format, ap);
3168   ESTREAM_UNLOCK (stream);
3169   va_end (ap);
3170
3171   return ret;
3172 }
3173
3174 /* A variant of asprintf.  The function returns the allocated buffer
3175    or NULL on error; ERRNO is set in the error case.  The caller
3176    should use es_free to release the buffer.  This function actually
3177    belongs into estream-printf but we put it here as a convenience
3178    and because es_free is required anyway.  */
3179 char *
3180 es_asprintf (const char *ES__RESTRICT format, ...)
3181 {
3182   int rc;
3183   va_list ap;
3184   char *buf;
3185
3186   va_start (ap, format);
3187   rc = estream_vasprintf (&buf, format, ap);
3188   va_end (ap);
3189   if (rc < 0)
3190     return NULL;
3191   return buf;
3192 }
3193
3194
3195 /* A variant of vasprintf.  The function returns the allocated buffer
3196    or NULL on error; ERRNO is set in the error case.  The caller
3197    should use es_free to release the buffer.  This function actually
3198    belongs into estream-printf but we put it here as a convenience
3199    and because es_free is required anyway.  */
3200 char * 
3201 es_vasprintf (const char *ES__RESTRICT format, va_list ap)
3202 {
3203   int rc;
3204   char *buf;
3205
3206   rc = estream_vasprintf (&buf, format, ap);
3207   if (rc < 0)
3208     return NULL;
3209   return buf;
3210 }
3211
3212
3213 static int
3214 tmpfd (void)
3215 {
3216 #ifdef HAVE_W32_SYSTEM
3217   int attempts, n;
3218 #ifdef HAVE_W32CE_SYSTEM
3219   wchar_t buffer[MAX_PATH+9+12+1];
3220 # define mystrlen(a) wcslen (a)
3221   wchar_t *name, *p;
3222 #else
3223   char buffer[MAX_PATH+9+12+1];
3224 # define mystrlen(a) strlen (a)
3225   char *name, *p;
3226 #endif
3227   HANDLE file;
3228   int pid = GetCurrentProcessId ();
3229   unsigned int value;
3230   int i;
3231   
3232   n = GetTempPath (MAX_PATH+1, buffer);
3233   if (!n || n > MAX_PATH || mystrlen (buffer) > MAX_PATH)
3234     {
3235       _set_errno (ENOENT);
3236       return -1;
3237     }
3238   p = buffer + mystrlen (buffer);
3239 #ifdef HAVE_W32CE_SYSTEM
3240   wcscpy (p, L"_estream");
3241 #else
3242   strcpy (p, "_estream");
3243 #endif
3244   p += 8;
3245   /* We try to create the directory but don't care about an error as
3246      it may already exist and the CreateFile would throw an error
3247      anyway.  */
3248   CreateDirectory (buffer, NULL);
3249   *p++ = '\\';
3250   name = p;
3251   for (attempts=0; attempts < 10; attempts++)
3252     {
3253       p = name;
3254       value = (GetTickCount () ^ ((pid<<16) & 0xffff0000));
3255       for (i=0; i < 8; i++)
3256         {
3257           *p++ = tohex (((value >> 28) & 0x0f));
3258           value <<= 4;
3259         }
3260 #ifdef HAVE_W32CE_SYSTEM
3261       wcscpy (p, L".tmp");
3262 #else
3263       strcpy (p, ".tmp");
3264 #endif
3265       file = CreateFile (buffer,
3266                          GENERIC_READ | GENERIC_WRITE,
3267                          0,
3268                          NULL,
3269                          CREATE_NEW,
3270                          FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
3271                          NULL);
3272       if (file != INVALID_HANDLE_VALUE)
3273         {
3274 #ifdef HAVE_W32CE_SYSTEM
3275           int fd = (int)file;
3276 #else
3277           int fd = _open_osfhandle ((long)file, 0);
3278           if (fd == -1)
3279             {
3280               CloseHandle (file);
3281               return -1;
3282             }
3283 #endif
3284           return fd;
3285         }
3286       Sleep (1); /* One ms as this is the granularity of GetTickCount.  */
3287     }
3288   _set_errno (ENOENT);
3289   return -1;
3290 #else /*!HAVE_W32_SYSTEM*/
3291   FILE *fp;
3292   int fp_fd;
3293   int fd;
3294
3295   fp = NULL;
3296   fd = -1;
3297   
3298   fp = tmpfile ();
3299   if (! fp)
3300     goto out;
3301
3302   fp_fd = fileno (fp);
3303   fd = dup (fp_fd);
3304
3305  out:
3306
3307   if (fp)
3308     fclose (fp);
3309
3310   return fd;
3311 #endif /*!HAVE_W32_SYSTEM*/
3312 }
3313
3314 estream_t
3315 es_tmpfile (void)
3316 {
3317   unsigned int modeflags;
3318   int create_called;
3319   estream_t stream;
3320   void *cookie;
3321   int err;
3322   int fd;
3323
3324   create_called = 0;
3325   stream = NULL;
3326   modeflags = O_RDWR | O_TRUNC | O_CREAT;
3327   cookie = NULL;
3328   
3329   fd = tmpfd ();
3330   if (fd == -1)
3331     {
3332       err = -1;
3333       goto out;
3334     }
3335
3336   err = es_func_fd_create (&cookie, fd, modeflags, 0);
3337   if (err)
3338     goto out;
3339
3340   create_called = 1;
3341   err = es_create (&stream, cookie, fd, estream_functions_fd, modeflags, 0);
3342
3343  out:
3344
3345   if (err)
3346     {
3347       if (create_called)
3348         es_func_fd_destroy (cookie);
3349       else if (fd != -1)
3350         close (fd);
3351       stream = NULL;
3352     }
3353   
3354   return stream;
3355 }
3356
3357
3358 int
3359 es_setvbuf (estream_t ES__RESTRICT stream,
3360             char *ES__RESTRICT buf, int type, size_t size)
3361 {
3362   int err;
3363   
3364   if ((type == _IOFBF || type == _IOLBF || type == _IONBF)
3365       && (!buf || size || type == _IONBF))
3366     {
3367       ESTREAM_LOCK (stream);
3368       err = es_set_buffering (stream, buf, type, size);
3369       ESTREAM_UNLOCK (stream);
3370     }
3371   else
3372     {
3373       _set_errno (EINVAL);
3374       err = -1;
3375     }
3376
3377   return err;
3378 }
3379
3380
3381 void
3382 es_setbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf)
3383 {
3384   ESTREAM_LOCK (stream);
3385   es_set_buffering (stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
3386   ESTREAM_UNLOCK (stream);
3387 }
3388
3389 void
3390 es_opaque_set (estream_t stream, void *opaque)
3391 {
3392   ESTREAM_LOCK (stream);
3393   es_opaque_ctrl (stream, opaque, NULL);
3394   ESTREAM_UNLOCK (stream);
3395 }
3396
3397
3398 void *
3399 es_opaque_get (estream_t stream)
3400 {
3401   void *opaque;
3402   
3403   ESTREAM_LOCK (stream);
3404   es_opaque_ctrl (stream, NULL, &opaque);
3405   ESTREAM_UNLOCK (stream);
3406
3407   return opaque;
3408 }
3409
3410 /* Print a BUFFER to STREAM while replacing all control characters and
3411    the characters in DELIMITERS by standard C escape sequences.
3412    Returns 0 on success or -1 on error.  If BYTES_WRITTEN is not NULL
3413    the number of bytes actually written are stored at this
3414    address.  */
3415 int 
3416 es_write_sanitized (estream_t ES__RESTRICT stream,
3417                     const void * ES__RESTRICT buffer, size_t length,
3418                     const char * delimiters, 
3419                     size_t * ES__RESTRICT bytes_written)
3420 {
3421   const unsigned char *p = buffer;
3422   size_t count = 0;
3423   int ret;
3424
3425   ESTREAM_LOCK (stream);
3426   for (; length; length--, p++, count++)
3427     {
3428       if (*p < 0x20 
3429           || *p == 0x7f
3430           || (delimiters 
3431               && (strchr (delimiters, *p) || *p == '\\')))
3432         {
3433           es_putc_unlocked ('\\', stream);
3434           count++;
3435           if (*p == '\n')
3436             {
3437               es_putc_unlocked ('n', stream);
3438               count++;
3439             }
3440           else if (*p == '\r')
3441             {
3442               es_putc_unlocked ('r', stream);
3443               count++;
3444             }
3445           else if (*p == '\f')
3446             {
3447               es_putc_unlocked ('f', stream);
3448               count++;
3449             }
3450           else if (*p == '\v')
3451             {
3452               es_putc_unlocked ('v', stream);
3453               count++;
3454             }
3455           else if (*p == '\b')
3456             {
3457               es_putc_unlocked ('b', stream);
3458               count++;
3459             }
3460           else if (!*p)
3461             {
3462               es_putc_unlocked('0', stream);
3463               count++;
3464             }
3465           else
3466             {
3467               es_fprintf_unlocked (stream, "x%02x", *p);
3468               count += 3;
3469             }
3470         }
3471       else
3472         {
3473           es_putc_unlocked (*p, stream);
3474           count++;
3475         }
3476     }
3477
3478   if (bytes_written)
3479     *bytes_written = count;
3480   ret =  es_ferror_unlocked (stream)? -1 : 0;
3481   ESTREAM_UNLOCK (stream);
3482
3483   return ret;
3484 }
3485
3486
3487 /* Write LENGTH bytes of BUFFER to STREAM as a hex encoded string.
3488    RESERVED must be 0.  Returns 0 on success or -1 on error.  If
3489    BYTES_WRITTEN is not NULL the number of bytes actually written are
3490    stored at this address.  */
3491 int
3492 es_write_hexstring (estream_t ES__RESTRICT stream,
3493                     const void *ES__RESTRICT buffer, size_t length,
3494                     int reserved, size_t *ES__RESTRICT bytes_written )
3495 {
3496   int ret;
3497   const unsigned char *s;
3498   size_t count = 0;
3499
3500   (void)reserved;
3501
3502 #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
3503
3504   if (!length)
3505     return 0;
3506
3507   ESTREAM_LOCK (stream);
3508
3509   for (s = buffer; length; s++, length--)
3510     {
3511       es_putc_unlocked ( tohex ((*s>>4)&15), stream);
3512       es_putc_unlocked ( tohex (*s&15), stream);
3513       count += 2;
3514     }
3515
3516   if (bytes_written)
3517     *bytes_written = count;
3518   ret = es_ferror_unlocked (stream)? -1 : 0;
3519
3520   ESTREAM_UNLOCK (stream);
3521
3522   return ret;
3523
3524 #undef tohex
3525 }
3526
3527
3528
3529 #ifdef GNUPG_MAJOR_VERSION
3530 /* Special estream function to print an UTF8 string in the native
3531    encoding.  The interface is the same as es_write_sanitized, however
3532    only one delimiter may be supported. 
3533
3534    THIS IS NOT A STANDARD ESTREAM FUNCTION AND ONLY USED BY GNUPG!. */
3535 int
3536 es_write_sanitized_utf8_buffer (estream_t stream,
3537                                 const void *buffer, size_t length, 
3538                                 const char *delimiters, size_t *bytes_written)
3539 {
3540   const char *p = buffer;
3541   size_t i;
3542
3543   /* We can handle plain ascii simpler, so check for it first. */
3544   for (i=0; i < length; i++ ) 
3545     {
3546       if ( (p[i] & 0x80) )
3547         break;
3548     }
3549   if (i < length)
3550     {
3551       int delim = delimiters? *delimiters : 0;
3552       char *buf;
3553       int ret;
3554
3555       /*(utf8 conversion already does the control character quoting). */
3556       buf = utf8_to_native (p, length, delim);
3557       if (bytes_written)
3558         *bytes_written = strlen (buf);
3559       ret = es_fputs (buf, stream);
3560       xfree (buf);
3561       return ret == EOF? ret : (int)i;
3562     }
3563   else
3564     return es_write_sanitized (stream, p, length, delimiters, bytes_written);
3565 }
3566 #endif /*GNUPG_MAJOR_VERSION*/