.
[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_W32_SYSTEM
120 # define S_IRGRP S_IRUSR
121 # define S_IROTH S_IRUSR
122 # define S_IWGRP S_IWUSR
123 # define S_IWOTH S_IWUSR
124 #endif
125
126
127 #ifdef HAVE_W32CE_SYSTEM
128 # define _set_errno(a)  gpg_err_set_errno ((a))
129 /* Setmode is missing in cegcc but available since CE 5.0.  */
130 int _setmode (int handle, int mode);
131 # define setmode(a,b)   _setmode ((a),(b))
132 #else
133 # define _set_errno(a)  do { errno = (a); } while (0)
134 #endif
135
136 #ifdef HAVE_W32_SYSTEM
137 # define IS_INVALID_FD(a)    ((void*)(a) == (void*)(-1)) /* ?? FIXME.  */
138 #else
139 # define IS_INVALID_FD(a)    ((a) == -1)
140 #endif
141
142
143 /* Generally used types.  */
144
145 typedef void *(*func_realloc_t) (void *mem, size_t size);
146 typedef void (*func_free_t) (void *mem);
147
148
149 \f
150
151 /* Buffer management layer.  */
152
153 #define BUFFER_BLOCK_SIZE  BUFSIZ
154 #define BUFFER_UNREAD_SIZE 16
155
156 \f
157
158 /* Locking.  */
159
160 #ifdef HAVE_PTH
161
162 typedef pth_mutex_t estream_mutex_t;
163 # define ESTREAM_MUTEX_INITIALIZER PTH_MUTEX_INIT
164 # define ESTREAM_MUTEX_LOCK(mutex)        \
165   pth_mutex_acquire (&(mutex), 0, NULL)
166 # define ESTREAM_MUTEX_UNLOCK(mutex)      \
167   pth_mutex_release (&(mutex))
168 # define ESTREAM_MUTEX_TRYLOCK(mutex)     \
169   ((pth_mutex_acquire (&(mutex), 1, NULL) == TRUE) ? 0 : -1)
170 # define ESTREAM_MUTEX_INITIALIZE(mutex)  \
171   pth_mutex_init    (&(mutex))
172 #else
173
174 typedef void *estream_mutex_t;
175
176 static inline void
177 dummy_mutex_call_void (estream_mutex_t mutex)
178 {
179   (void)mutex;
180 }
181
182 static inline int
183 dummy_mutex_call_int (estream_mutex_t mutex)
184 {
185   (void)mutex;
186   return 0;
187 }
188
189 # define ESTREAM_MUTEX_INITIALIZER NULL
190 # define ESTREAM_MUTEX_LOCK(mutex) dummy_mutex_call_void ((mutex))
191 # define ESTREAM_MUTEX_UNLOCK(mutex) dummy_mutex_call_void ((mutex))
192 # define ESTREAM_MUTEX_TRYLOCK(mutex) dummy_mutex_call_int ((mutex))
193 # define ESTREAM_MUTEX_INITIALIZE(mutex) dummy_mutex_call_void ((mutex))
194 #endif
195
196 /* Primitive system I/O.  */
197
198 #ifdef HAVE_PTH
199 # define ESTREAM_SYS_READ  do_pth_read
200 # define ESTREAM_SYS_WRITE do_pth_write
201 # define ESTREAM_SYS_YIELD() pth_yield (NULL)
202 #else
203 # define ESTREAM_SYS_READ  read
204 # define ESTREAM_SYS_WRITE write
205 # define ESTREAM_SYS_YIELD() do { } while (0)
206 #endif
207
208
209 /* A linked list to hold notification functions. */
210 struct notify_list_s
211 {
212   struct notify_list_s *next;
213   void (*fnc) (estream_t, void*); /* The notification function.  */
214   void *fnc_value;                /* The value to be passed to FNC.  */
215 };
216 typedef struct notify_list_s *notify_list_t;
217
218 /* An internal stream object.  */
219 struct estream_internal
220 {
221   unsigned char buffer[BUFFER_BLOCK_SIZE];
222   unsigned char unread_buffer[BUFFER_UNREAD_SIZE];
223   estream_mutex_t lock;          /* Lock. */
224   void *cookie;                  /* Cookie.                */
225   void *opaque;                  /* Opaque data.           */
226   unsigned int modeflags;        /* Flags for the backend. */
227   char *printable_fname;         /* Malloced filename for es_fname_get.  */
228   off_t offset;
229   es_cookie_read_function_t func_read;
230   es_cookie_write_function_t func_write;
231   es_cookie_seek_function_t func_seek;
232   es_cookie_close_function_t func_close;
233   int strategy;
234   es_syshd_t syshd;              /* A copy of the sytem handle.  */
235   struct
236   {
237     unsigned int err: 1;
238     unsigned int eof: 1;
239   } indicators;
240   unsigned int deallocate_buffer: 1;
241   unsigned int is_stdstream:1;   /* This is a standard stream.  */
242   unsigned int stdstream_fd:2;   /* 0, 1 or 2 for a standard stream.  */
243   unsigned int printable_fname_inuse: 1;  /* es_fname_get has been used.  */
244   size_t print_ntotal;           /* Bytes written from in print_writer. */
245   notify_list_t onclose;         /* On close notify function list.  */
246 };
247
248
249 typedef struct estream_internal *estream_internal_t;
250
251 #define ESTREAM_LOCK(stream) ESTREAM_MUTEX_LOCK (stream->intern->lock)
252 #define ESTREAM_UNLOCK(stream) ESTREAM_MUTEX_UNLOCK (stream->intern->lock)
253 #define ESTREAM_TRYLOCK(stream) ESTREAM_MUTEX_TRYLOCK (stream->intern->lock)
254
255 /* Stream list.  */
256
257 typedef struct estream_list *estream_list_t;
258
259 struct estream_list
260 {
261   estream_t car;
262   estream_list_t cdr;
263   estream_list_t *prev_cdr;
264 };
265
266 static estream_list_t estream_list;
267 static estream_mutex_t estream_list_lock;
268
269 #define ESTREAM_LIST_LOCK   ESTREAM_MUTEX_LOCK   (estream_list_lock)
270 #define ESTREAM_LIST_UNLOCK ESTREAM_MUTEX_UNLOCK (estream_list_lock)
271
272 /* File descriptors registered to be used as the standard file handles. */
273 static int custom_std_fds[3];
274 static unsigned char custom_std_fds_valid[3];
275
276
277 #ifndef EOPNOTSUPP
278 # define EOPNOTSUPP ENOSYS
279 #endif
280
281
282 /* Local prototypes.  */
283 static void fname_set_internal (estream_t stream, const char *fname, int quote);
284
285
286
287 \f
288 /* Macros.  */
289
290 /* Calculate array dimension.  */
291 #ifndef DIM
292 #define DIM(array) (sizeof (array) / sizeof (*array))
293 #endif
294
295 #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
296
297
298 /* Evaluate EXPRESSION, setting VARIABLE to the return code, if
299    VARIABLE is zero.  */
300 #define SET_UNLESS_NONZERO(variable, tmp_variable, expression) \
301   do                                                           \
302     {                                                          \
303       tmp_variable = expression;                               \
304       if ((! variable) && tmp_variable)                        \
305         variable = tmp_variable;                               \
306     }                                                          \
307   while (0)
308
309
310 /* Malloc wrappers to overcome problems on some older OSes.  */
311 static void *
312 mem_alloc (size_t n)
313 {
314   if (!n)
315     n++;
316   return malloc (n);
317 }
318
319 static void *
320 mem_realloc (void *p, size_t n)
321 {
322   if (!p)
323     return mem_alloc (n);
324   return realloc (p, n);
325 }
326
327 static void
328 mem_free (void *p)
329 {
330   if (p)
331     free (p);
332 }
333
334 #ifdef HAVE_W32_SYSTEM
335 static int
336 map_w32_to_errno (DWORD w32_err)
337 {
338   switch (w32_err)
339     {
340     case 0:
341       return 0;
342
343     case ERROR_FILE_NOT_FOUND:
344       return ENOENT;
345
346     case ERROR_PATH_NOT_FOUND:
347       return ENOENT;
348
349     case ERROR_ACCESS_DENIED:
350       return EPERM;
351
352     case ERROR_INVALID_HANDLE:
353     case ERROR_INVALID_BLOCK:
354       return EINVAL;
355
356     case ERROR_NOT_ENOUGH_MEMORY:
357       return ENOMEM;
358       
359     case ERROR_NO_DATA:
360       return EPIPE;
361
362     default:
363       return EIO;
364     }
365 }
366 #endif /*HAVE_W32_SYSTEM*/
367
368 /*
369  * List manipulation.
370  */
371
372 /* Add STREAM to the list of registered stream objects.  If
373    WITH_LOCKED_LIST is true we assumed that the list of streams is
374    already locked.  */
375 static int
376 do_list_add (estream_t stream, int with_locked_list)
377 {
378   estream_list_t list_obj;
379   int ret;
380
381   list_obj = mem_alloc (sizeof (*list_obj));
382   if (! list_obj)
383     ret = -1;
384   else
385     {
386       if (!with_locked_list)
387         ESTREAM_LIST_LOCK;
388       list_obj->car = stream;
389       list_obj->cdr = estream_list;
390       list_obj->prev_cdr = &estream_list;
391       if (estream_list)
392         estream_list->prev_cdr = &list_obj->cdr;
393       estream_list = list_obj;
394       if (!with_locked_list)
395         ESTREAM_LIST_UNLOCK;
396       ret = 0;
397     }
398
399   return ret;
400 }
401
402 /* Remove STREAM from the list of registered stream objects.  */
403 static void
404 do_list_remove (estream_t stream, int with_locked_list)
405 {
406   estream_list_t list_obj;
407   
408   if (!with_locked_list)
409     ESTREAM_LIST_LOCK;
410   for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
411     if (list_obj->car == stream)
412       {
413         *list_obj->prev_cdr = list_obj->cdr;
414         if (list_obj->cdr)
415           list_obj->cdr->prev_cdr = list_obj->prev_cdr;
416         mem_free (list_obj);
417         break;
418       }
419   if (!with_locked_list)
420     ESTREAM_LIST_UNLOCK;
421 }
422
423 /* Type of an stream-iterator-function.  */
424 typedef int (*estream_iterator_t) (estream_t stream);
425
426 /* Iterate over list of registered streams, calling ITERATOR for each
427    of them.  */
428 static int
429 do_list_iterate (estream_iterator_t iterator)
430 {
431   estream_list_t list_obj;
432   int ret = 0;
433
434   ESTREAM_LIST_LOCK;
435   for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
436     ret |= (*iterator) (list_obj->car);
437   ESTREAM_LIST_UNLOCK;
438
439   return ret;
440 }
441
442
443 \f
444 /*
445  * I/O Helper
446  *
447  * Unfortunately our Pth emulation for Windows expects system handles
448  * for pth_read and pth_write.  We use a simple approach to fix this:
449  * If the function returns an error we fall back to a vanilla read or
450  * write, assuming that we do I/O on a plain file where the operation
451  * can't block.
452  */
453 #ifdef HAVE_PTH
454 static int
455 do_pth_read (int fd, void *buffer, size_t size)
456 {
457 # ifdef HAVE_W32_SYSTEM
458   int rc = pth_read (fd, buffer, size);
459   if (rc == -1 && errno == EINVAL)
460     rc = read (fd, buffer, size);
461   return rc;
462 # else /*!HAVE_W32_SYSTEM*/
463   return pth_read (fd, buffer, size);
464 # endif /* !HAVE_W32_SYSTEM*/
465 }
466
467 static int
468 do_pth_write (int fd, const void *buffer, size_t size)
469 {
470 # ifdef HAVE_W32_SYSTEM
471   int rc = pth_write (fd, buffer, size);
472   if (rc == -1 && errno == EINVAL)
473     rc = write (fd, buffer, size);
474   return rc;
475 # else /*!HAVE_W32_SYSTEM*/
476   return pth_write (fd, buffer, size);
477 # endif /* !HAVE_W32_SYSTEM*/
478 }
479 #endif /*HAVE_PTH*/
480
481 \f
482
483 static void
484 do_deinit (void)
485 {
486   /* Flush all streams. */
487   es_fflush (NULL);
488 }
489
490
491 /*
492  * Initialization.
493  */
494
495 static int
496 do_init (void)
497 {
498   static int initialized;
499
500   if (!initialized)
501     {
502 #ifdef HAVE_PTH
503       if (!pth_init () && errno != EPERM )
504         return -1;
505       if (pth_mutex_init (&estream_list_lock))
506         initialized = 1;
507 #else
508       initialized = 1;
509 #endif
510       atexit (do_deinit);  
511     }
512   return 0;
513 }
514
515 \f
516
517 /*
518  * I/O methods.
519  */
520
521 /* Implementation of Memory I/O.  */
522
523 /* Cookie for memory objects.  */
524 typedef struct estream_cookie_mem
525 {
526   unsigned int modeflags;       /* Open flags.  */
527   unsigned char *memory;        /* Allocated data buffer.  */
528   size_t memory_size;           /* Allocated size of MEMORY.  */
529   size_t memory_limit;          /* Caller supplied maximum allowed
530                                    allocation size or 0 for no limit.  */
531   size_t offset;                /* Current offset in MEMORY.  */
532   size_t data_len;              /* Used length of data in MEMORY.  */
533   size_t block_size;            /* Block size.  */
534   struct {
535     unsigned int grow: 1;       /* MEMORY is allowed to grow.  */
536   } flags;
537   func_realloc_t func_realloc;
538   func_free_t func_free;
539 } *estream_cookie_mem_t;
540
541
542 /* Create function for memory objects.  DATA is either NULL or a user
543    supplied buffer with the initial conetnt of the memory buffer.  If
544    DATA is NULL, DATA_N and DATA_LEN need to be 0 as well.  If DATA is
545    not NULL, DATA_N gives the allocated size of DATA and DATA_LEN the
546    used length in DATA.  */
547 static int
548 func_mem_create (void *ES__RESTRICT *ES__RESTRICT cookie,
549                  unsigned char *ES__RESTRICT data, size_t data_n,
550                  size_t data_len,
551                  size_t block_size, unsigned int grow,
552                  func_realloc_t func_realloc, func_free_t func_free,
553                  unsigned int modeflags,
554                  size_t memory_limit)
555 {
556   estream_cookie_mem_t mem_cookie;
557   int err;
558
559   if (!data && (data_n || data_len))
560     {
561       _set_errno (EINVAL);
562       return -1;
563     }
564
565   mem_cookie = mem_alloc (sizeof (*mem_cookie));
566   if (!mem_cookie)
567     err = -1;
568   else
569     {
570       mem_cookie->modeflags = modeflags;
571       mem_cookie->memory = data;
572       mem_cookie->memory_size = data_n;
573       mem_cookie->memory_limit = memory_limit;
574       mem_cookie->offset = 0;
575       mem_cookie->data_len = data_len;
576       mem_cookie->block_size = block_size;
577       mem_cookie->flags.grow = !!grow;
578       mem_cookie->func_realloc = func_realloc ? func_realloc : mem_realloc;
579       mem_cookie->func_free = func_free ? func_free : mem_free;
580       *cookie = mem_cookie;
581       err = 0;
582     }
583
584   return err;
585 }
586
587
588 /* Read function for memory objects.  */
589 static ssize_t
590 es_func_mem_read (void *cookie, void *buffer, size_t size)
591 {
592   estream_cookie_mem_t mem_cookie = cookie;
593   ssize_t ret;
594
595   if (size > mem_cookie->data_len - mem_cookie->offset)
596     size = mem_cookie->data_len - mem_cookie->offset;
597
598   if (size)
599     {
600       memcpy (buffer, mem_cookie->memory + mem_cookie->offset, size);
601       mem_cookie->offset += size;
602     }
603   
604   ret = size;
605   return ret;
606 }
607
608
609 /* Write function for memory objects.  */
610 static ssize_t
611 es_func_mem_write (void *cookie, const void *buffer, size_t size)
612 {
613   estream_cookie_mem_t mem_cookie = cookie;
614   ssize_t ret;
615   size_t nleft;
616
617   if (!size)
618     return 0;  /* A flush is a NOP for memory objects.  */
619
620   if (mem_cookie->modeflags & O_APPEND)
621     {
622       /* Append to data.  */
623       mem_cookie->offset = mem_cookie->data_len;
624     }
625
626   assert (mem_cookie->memory_size >= mem_cookie->offset);
627   nleft = mem_cookie->memory_size - mem_cookie->offset;
628   
629   /* If we are not allowed to grow limit the size to the left space.  */
630   if (!mem_cookie->flags.grow && size > nleft)
631     size = nleft;
632
633   /* Enlarge the memory buffer if needed.  */
634   if (size > nleft)
635     {
636       unsigned char *newbuf;
637       size_t newsize;
638
639       if (!mem_cookie->memory_size)
640         newsize = size;  /* Not yet allocated.  */
641       else
642         newsize = mem_cookie->memory_size + (nleft - size);
643       if (newsize < mem_cookie->offset)
644         {
645           _set_errno (EINVAL);
646           return -1;
647         }
648
649       /* Round up to the next block length.  BLOCK_SIZE should always
650          be set; we check anyway.  */
651       if (mem_cookie->block_size)
652         {
653           newsize += mem_cookie->block_size - 1;
654           if (newsize < mem_cookie->offset)
655             {
656               _set_errno (EINVAL);
657               return -1;
658             }
659           newsize /= mem_cookie->block_size;
660           newsize *= mem_cookie->block_size;
661         }
662
663       /* Check for a total limit.  */
664       if (mem_cookie->memory_limit && newsize > mem_cookie->memory_limit)
665         {
666           _set_errno (ENOSPC);
667           return -1;
668         }
669       
670       newbuf = mem_cookie->func_realloc (mem_cookie->memory, newsize);
671       if (!newbuf)
672         return -1;
673       
674       mem_cookie->memory = newbuf;
675       mem_cookie->memory_size = newsize;
676
677       assert (mem_cookie->memory_size >= mem_cookie->offset);
678       nleft = mem_cookie->memory_size - mem_cookie->offset;
679       
680       assert (size <= nleft);
681     }
682       
683   memcpy (mem_cookie->memory + mem_cookie->offset, buffer, size);
684   if (mem_cookie->offset + size > mem_cookie->data_len)
685     mem_cookie->data_len = mem_cookie->offset + size;
686   mem_cookie->offset += size;
687
688   ret = size;
689   return ret;
690 }
691
692
693 /* Seek function for memory objects.  */
694 static int
695 es_func_mem_seek (void *cookie, off_t *offset, int whence)
696 {
697   estream_cookie_mem_t mem_cookie = cookie;
698   off_t pos_new;
699
700   switch (whence)
701     {
702     case SEEK_SET:
703       pos_new = *offset;
704       break;
705
706     case SEEK_CUR:
707       pos_new = mem_cookie->offset += *offset;
708       break;
709
710     case SEEK_END:
711       pos_new = mem_cookie->data_len += *offset;
712       break;
713
714     default:
715       _set_errno (EINVAL);
716       return -1;
717     }
718
719   if (pos_new > mem_cookie->memory_size)
720     {
721       size_t newsize;
722       void *newbuf;
723
724       if (!mem_cookie->flags.grow)
725         {
726           _set_errno (ENOSPC);
727           return -1;
728         }
729
730       newsize = pos_new + mem_cookie->block_size - 1;
731       if (newsize < pos_new)
732         {
733           _set_errno (EINVAL);
734           return -1;
735         }
736       newsize /= mem_cookie->block_size;
737       newsize *= mem_cookie->block_size;
738
739       if (mem_cookie->memory_limit && newsize > mem_cookie->memory_limit)
740         {
741           _set_errno (ENOSPC);
742           return -1;
743         }
744       
745       newbuf = mem_cookie->func_realloc (mem_cookie->memory, newsize);
746       if (!newbuf)
747         return -1;
748
749       mem_cookie->memory = newbuf;
750       mem_cookie->memory_size = newsize;
751     }
752
753   if (pos_new > mem_cookie->data_len)
754     {
755       /* Fill spare space with zeroes.  */
756       memset (mem_cookie->memory + mem_cookie->data_len,
757               0, pos_new - mem_cookie->data_len);
758       mem_cookie->data_len = pos_new;
759     }
760
761   mem_cookie->offset = pos_new;
762   *offset = pos_new;
763
764   return 0;
765 }
766
767
768 /* Destroy function for memory objects.  */
769 static int
770 es_func_mem_destroy (void *cookie)
771 {
772   estream_cookie_mem_t mem_cookie = cookie;
773
774   if (cookie)
775     {
776       mem_cookie->func_free (mem_cookie->memory);
777       mem_free (mem_cookie);
778     }
779   return 0;
780 }
781
782
783 static es_cookie_io_functions_t estream_functions_mem =
784   {
785     es_func_mem_read,
786     es_func_mem_write,
787     es_func_mem_seek,
788     es_func_mem_destroy
789   };
790
791
792 \f
793 /* Implementation of file descriptor based I/O.  */
794
795 /* Cookie for fd objects.  */
796 typedef struct estream_cookie_fd
797 {
798   int fd;        /* The file descriptor we are using for actual output.  */
799   int no_close;  /* If set we won't close the file descriptor.  */
800 } *estream_cookie_fd_t;
801
802 /* Create function for objects indentified by a libc file descriptor.  */
803 static int
804 func_fd_create (void **cookie, int fd, unsigned int modeflags, int no_close)
805 {
806   estream_cookie_fd_t fd_cookie;
807   int err;
808
809   fd_cookie = mem_alloc (sizeof (*fd_cookie));
810   if (! fd_cookie)
811     err = -1;
812   else
813     {
814 #ifdef HAVE_DOSISH_SYSTEM
815       /* Make sure it is in binary mode if requested.  */
816       if ( (modeflags & O_BINARY) )
817         setmode (fd, O_BINARY);
818 #else
819       (void)modeflags;
820 #endif
821       fd_cookie->fd = fd;
822       fd_cookie->no_close = no_close;
823       *cookie = fd_cookie;
824       err = 0;
825     }
826   
827   return err;
828 }
829
830 /* Read function for fd objects.  */
831 static ssize_t
832 es_func_fd_read (void *cookie, void *buffer, size_t size)
833
834 {
835   estream_cookie_fd_t file_cookie = cookie;
836   ssize_t bytes_read;
837   
838   if (IS_INVALID_FD (file_cookie->fd))
839     {
840       ESTREAM_SYS_YIELD ();
841       bytes_read = 0;
842     }
843   else
844     {
845       do 
846         bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size);
847       while (bytes_read == -1 && errno == EINTR);
848     }
849
850   return bytes_read;
851 }
852
853 /* Write function for fd objects.  */
854 static ssize_t
855 es_func_fd_write (void *cookie, const void *buffer, size_t size)
856 {
857   estream_cookie_fd_t file_cookie = cookie;
858   ssize_t bytes_written;
859
860   if (IS_INVALID_FD (file_cookie->fd))
861     {
862       ESTREAM_SYS_YIELD ();
863       bytes_written = size; /* Yeah:  Success writing to the bit bucket.  */
864     }
865   else
866     {
867       do
868         bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size);
869       while (bytes_written == -1 && errno == EINTR);
870     }
871
872   return bytes_written;
873 }
874
875 /* Seek function for fd objects.  */
876 static int
877 es_func_fd_seek (void *cookie, off_t *offset, int whence)
878 {
879   estream_cookie_fd_t file_cookie = cookie;
880   off_t offset_new;
881   int err;
882
883   if (IS_INVALID_FD (file_cookie->fd))
884     {
885       _set_errno (ESPIPE);
886       err = -1;
887     }
888   else
889     {
890       offset_new = lseek (file_cookie->fd, *offset, whence);
891       if (offset_new == -1)
892         err = -1;
893       else
894         {
895           *offset = offset_new;
896           err = 0;
897         }
898     }
899
900   return err;
901 }
902
903 /* Destroy function for fd objects.  */
904 static int
905 es_func_fd_destroy (void *cookie)
906 {
907   estream_cookie_fd_t fd_cookie = cookie;
908   int err;
909
910   if (fd_cookie)
911     {
912       if (IS_INVALID_FD (fd_cookie->fd))
913         err = 0;
914       else
915         err = fd_cookie->no_close? 0 : close (fd_cookie->fd);
916       mem_free (fd_cookie);
917     }
918   else
919     err = 0;
920
921   return err;
922 }
923
924
925 static es_cookie_io_functions_t estream_functions_fd =
926   {
927     es_func_fd_read,
928     es_func_fd_write,
929     es_func_fd_seek,
930     es_func_fd_destroy
931   };
932
933
934
935 \f
936 #ifdef HAVE_W32_SYSTEM
937 /* Implementation of W32 handle based I/O.  */
938
939 /* Cookie for fd objects.  */
940 typedef struct estream_cookie_w32
941 {
942   HANDLE hd;     /* The handle we are using for actual output.  */
943   int no_close;  /* If set we won't close the handle.  */
944 } *estream_cookie_w32_t;
945
946
947 /* Create function for w32 handle objects.  */
948 static int
949 es_func_w32_create (void **cookie, HANDLE hd,
950                     unsigned int modeflags, int no_close)
951 {
952   estream_cookie_w32_t w32_cookie;
953   int err;
954
955   w32_cookie = mem_alloc (sizeof (*w32_cookie));
956   if (!w32_cookie)
957     err = -1;
958   else
959     {
960       /* CR/LF translations are not supported when using the bare W32
961          API.  If that is really required we need to implemented that
962          in the upper layer.  */
963       (void)modeflags;
964
965       w32_cookie->hd = hd;
966       w32_cookie->no_close = no_close;
967       *cookie = w32_cookie;
968       err = 0;
969     }
970   
971   return err;
972 }
973
974 /* Read function for W32 handle objects.  */
975 static ssize_t
976 es_func_w32_read (void *cookie, void *buffer, size_t size)
977 {
978   estream_cookie_w32_t w32_cookie = cookie;
979   ssize_t bytes_read;
980   
981   if (w32_cookie->hd == INVALID_HANDLE_VALUE)
982     {
983       ESTREAM_SYS_YIELD ();
984       bytes_read = 0;
985     }
986   else
987     {
988       do
989         {
990 #ifdef HAVE_PTH
991           /* Note: Our pth_read actually uses HANDLE! */
992           bytes_read = pth_read ((int)w32_cookie->hd, buffer, size);
993 #else
994           DWORD nread, ec;
995           
996           if (!ReadFile (w32_cookie->hd, buffer, size, &nread, NULL))
997             {
998               ec = GetLastError ();
999               if (ec == ERROR_BROKEN_PIPE)
1000                 bytes_read = 0; /* Like our pth_read we handle this as EOF.  */
1001               else
1002                 {
1003                   _set_errno (map_w32_to_errno (ec));
1004                   log_debug ("estream: ReadFile returned %d\n",
1005                              (int)GetLastError ());
1006                   bytes_read = -1;
1007                 }
1008             }
1009           else
1010             bytes_read = (int)nread;
1011 #endif
1012         }
1013       while (bytes_read == -1 && errno == EINTR);
1014     }
1015
1016   return bytes_read;
1017 }
1018
1019 /* Write function for W32 handle objects.  */
1020 static ssize_t
1021 es_func_w32_write (void *cookie, const void *buffer, size_t size)
1022 {
1023   estream_cookie_w32_t w32_cookie = cookie;
1024   ssize_t bytes_written;
1025
1026   if (w32_cookie->hd == INVALID_HANDLE_VALUE)
1027     {
1028       ESTREAM_SYS_YIELD ();
1029       bytes_written = size; /* Yeah:  Success writing to the bit bucket.  */
1030     }
1031   else
1032     {
1033       do
1034         {
1035 #ifdef HAVE_PTH
1036           /* Note: Our pth_write actually uses HANDLE! */
1037           bytes_written = pth_write ((int)w32_cookie->hd, buffer, size);
1038 #else
1039           DWORD nwritten;
1040
1041           if (!WriteFile (w32_cookie->hd, buffer, size, &nwritten, NULL))
1042             {
1043               _set_errno (map_w32_to_errno (GetLastError ()));
1044               bytes_written = -1;
1045             }
1046           else
1047             bytes_written = (int)nwritten;
1048 #endif
1049         }
1050       while (bytes_written == -1 && errno == EINTR);
1051     }
1052
1053   return bytes_written;
1054 }
1055
1056 /* Seek function for W32 handle objects.  */
1057 static int
1058 es_func_w32_seek (void *cookie, off_t *offset, int whence)
1059 {
1060   estream_cookie_w32_t w32_cookie = cookie;
1061   DWORD method;
1062   LARGE_INTEGER distance, newoff;
1063
1064   if (w32_cookie->hd == INVALID_HANDLE_VALUE)
1065     {
1066       _set_errno (ESPIPE);
1067       return -1;
1068     }
1069
1070   if (whence == SEEK_SET)
1071     {
1072       method = FILE_BEGIN;
1073       distance.QuadPart = (unsigned long long)(*offset);
1074     }
1075   else if (whence == SEEK_CUR)
1076     {
1077       method = FILE_CURRENT;
1078       distance.QuadPart = (long long)(*offset);
1079     }
1080   else if (whence == SEEK_END)
1081     {
1082       method = FILE_END;
1083       distance.QuadPart = (long long)(*offset);
1084     }
1085   else
1086     {
1087       _set_errno (EINVAL);
1088       return -1;
1089     }
1090 #ifdef HAVE_W32CE_SYSTEM
1091 # warning need to use SetFilePointer
1092 #else
1093   if (!SetFilePointerEx (w32_cookie->hd, distance, &newoff, method))
1094     {
1095       _set_errno (map_w32_to_errno (GetLastError ()));
1096       return -1;
1097     }
1098 #endif
1099   *offset = (unsigned long long)newoff.QuadPart;
1100   return 0;
1101 }
1102
1103 /* Destroy function for W32 handle objects.  */
1104 static int
1105 es_func_w32_destroy (void *cookie)
1106 {
1107   estream_cookie_w32_t w32_cookie = cookie;
1108   int err;
1109
1110   if (w32_cookie)
1111     {
1112       if (w32_cookie->hd == INVALID_HANDLE_VALUE)
1113         err = 0;
1114       else if (w32_cookie->no_close)
1115         err = 0;
1116       else
1117         { 
1118           if (!CloseHandle (w32_cookie->hd))
1119             {
1120               _set_errno (map_w32_to_errno (GetLastError ()));
1121               err = -1;
1122             }
1123           else
1124             err = 0;
1125         }
1126       mem_free (w32_cookie);
1127     }
1128   else
1129     err = 0;
1130
1131   return err;
1132 }
1133
1134
1135 static es_cookie_io_functions_t estream_functions_w32 =
1136   {
1137     es_func_w32_read,
1138     es_func_w32_write,
1139     es_func_w32_seek,
1140     es_func_w32_destroy
1141   };
1142 #endif /*HAVE_W32_SYSTEM*/
1143
1144
1145
1146 \f
1147 /* Implementation of FILE* I/O.  */
1148
1149 /* Cookie for fp objects.  */
1150 typedef struct estream_cookie_fp
1151 {
1152   FILE *fp;      /* The file pointer we are using for actual output.  */
1153   int no_close;  /* If set we won't close the file pointer.  */
1154 } *estream_cookie_fp_t;
1155
1156
1157 /* Create function for FILE objects.  */
1158 static int
1159 func_fp_create (void **cookie, FILE *fp, 
1160                 unsigned int modeflags, int no_close)
1161 {
1162   estream_cookie_fp_t fp_cookie;
1163   int err;
1164
1165   fp_cookie = mem_alloc (sizeof *fp_cookie);
1166   if (!fp_cookie)
1167     err = -1;
1168   else
1169     {
1170 #ifdef HAVE_DOSISH_SYSTEM
1171       /* Make sure it is in binary mode if requested.  */
1172       if ( (modeflags & O_BINARY) )
1173         setmode (fileno (fp), O_BINARY);
1174 #else
1175       (void)modeflags;
1176 #endif
1177       fp_cookie->fp = fp;
1178       fp_cookie->no_close = no_close;
1179       *cookie = fp_cookie;
1180       err = 0;
1181     }
1182
1183   return err;
1184 }
1185
1186 /* Read function for FILE* objects.  */
1187 static ssize_t
1188 es_func_fp_read (void *cookie, void *buffer, size_t size)
1189
1190 {
1191   estream_cookie_fp_t file_cookie = cookie;
1192   ssize_t bytes_read;
1193
1194   if (file_cookie->fp)
1195     bytes_read = fread (buffer, 1, size, file_cookie->fp);
1196   else
1197     bytes_read = 0;
1198   if (!bytes_read && ferror (file_cookie->fp))
1199     return -1;
1200   return bytes_read;
1201 }
1202
1203 /* Write function for FILE* objects.  */
1204 static ssize_t
1205 es_func_fp_write (void *cookie, const void *buffer, size_t size)
1206 {
1207   estream_cookie_fp_t file_cookie = cookie;
1208   size_t bytes_written;
1209
1210   if (file_cookie->fp)
1211     {
1212 #ifdef HAVE_W32_SYSTEM
1213       /* Using an fwrite to stdout connected to the console fails with
1214          the error "Not enough space" for an fwrite size of >= 52KB
1215          (tested on Windows XP SP2).  To solve this we always chunk
1216          the writes up into smaller blocks.  */
1217       bytes_written = 0;
1218       while (bytes_written < size)
1219         {
1220           size_t cnt = size - bytes_written;
1221
1222           if (cnt > 32*1024)
1223             cnt = 32*1024;
1224           if (fwrite ((const char*)buffer + bytes_written,
1225                       cnt, 1, file_cookie->fp) != 1)
1226             break; /* Write error.  */
1227           bytes_written += cnt;
1228         }
1229 #else
1230       bytes_written = fwrite (buffer, 1, size, file_cookie->fp);
1231 #endif
1232     }
1233   else
1234     bytes_written = size; /* Successfully written to the bit bucket.  */
1235   if (bytes_written != size)
1236     return -1;
1237   return bytes_written;
1238 }
1239
1240 /* Seek function for FILE* objects.  */
1241 static int
1242 es_func_fp_seek (void *cookie, off_t *offset, int whence)
1243 {
1244   estream_cookie_fp_t file_cookie = cookie;
1245   long int offset_new;
1246
1247   if (!file_cookie->fp)
1248     {
1249       _set_errno (ESPIPE);
1250       return -1; 
1251     }
1252
1253   if ( fseek (file_cookie->fp, (long int)*offset, whence) )
1254     {
1255       /* fprintf (stderr, "\nfseek failed: errno=%d (%s)\n", */
1256       /*          errno,strerror (errno)); */
1257       return -1;
1258     }
1259
1260   offset_new = ftell (file_cookie->fp);
1261   if (offset_new == -1)
1262     {
1263       /* fprintf (stderr, "\nftell failed: errno=%d (%s)\n",  */
1264       /*          errno,strerror (errno)); */
1265       return -1;
1266     }
1267   *offset = offset_new;
1268   return 0;
1269 }
1270
1271 /* Destroy function for FILE* objects.  */
1272 static int
1273 es_func_fp_destroy (void *cookie)
1274 {
1275   estream_cookie_fp_t fp_cookie = cookie;
1276   int err;
1277
1278   if (fp_cookie)
1279     {
1280       if (fp_cookie->fp)
1281         {
1282           fflush (fp_cookie->fp);
1283           err = fp_cookie->no_close? 0 : fclose (fp_cookie->fp);
1284         }
1285       else
1286         err = 0;
1287       mem_free (fp_cookie);
1288     }
1289   else
1290     err = 0;
1291
1292   return err;
1293 }
1294
1295
1296 static es_cookie_io_functions_t estream_functions_fp =
1297   {
1298     es_func_fp_read,
1299     es_func_fp_write,
1300     es_func_fp_seek,
1301     es_func_fp_destroy
1302   };
1303
1304
1305
1306 \f
1307 /* Implementation of file I/O.  */
1308
1309 /* Create function for objects identified by a file name.  */
1310 static int
1311 func_file_create (void **cookie, int *filedes,
1312                   const char *path, unsigned int modeflags, unsigned int cmode)
1313 {
1314   estream_cookie_fd_t file_cookie;
1315   int err;
1316   int fd;
1317
1318   err = 0;
1319   fd = -1;
1320
1321   file_cookie = mem_alloc (sizeof (*file_cookie));
1322   if (! file_cookie)
1323     {
1324       err = -1;
1325       goto out;
1326     }
1327
1328   fd = open (path, modeflags, cmode);
1329   if (fd == -1)
1330     {
1331       err = -1;
1332       goto out;
1333     }
1334 #ifdef HAVE_DOSISH_SYSTEM
1335   /* Make sure it is in binary mode if requested.  */
1336   if ( (modeflags & O_BINARY) )
1337     setmode (fd, O_BINARY);
1338 #endif
1339
1340   file_cookie->fd = fd;
1341   file_cookie->no_close = 0;
1342   *cookie = file_cookie;
1343   *filedes = fd;
1344
1345  out:
1346
1347   if (err)
1348     mem_free (file_cookie);
1349
1350   return err;
1351 }
1352
1353
1354 \f
1355 /* Parse the mode flags of fopen et al.  In addition to the POSIX
1356    defined mode flags keyword parameters are supported.  These are
1357    key/value pairs delimited by comma and optional white spaces.
1358    Keywords and values may not contain a comma or white space; unknown
1359    keyword are skipped.  The only supported keyword is mode; for
1360    example:
1361
1362      "wb,mode=-rw-r--"
1363
1364    Creates a file and gives the new file read and write permissions
1365    for the user and read permission for the group.  The format of the
1366    string is the same as shown by the -l option of the ls(1) command.
1367    However the first letter must be a dash and it is allowed to leave
1368    out trailing dashes.  If this keyword parameter is not given the
1369    default mode for creating files is "-rw-rw-r--" (664).  Note that
1370    the system still applies the current umask to the mode when crating
1371    a file.
1372
1373    Note: R_CMODE is optional because is only required by functions
1374    which are able to creat a file.  */
1375 static int
1376 parse_mode (const char *modestr,
1377             unsigned int *modeflags, unsigned int *r_cmode)
1378 {
1379   unsigned int omode, oflags, cmode;
1380   int got_cmode = 0;
1381
1382   switch (*modestr)
1383     {
1384     case 'r':
1385       omode = O_RDONLY;
1386       oflags = 0;
1387       break;
1388     case 'w':
1389       omode = O_WRONLY;
1390       oflags = O_TRUNC | O_CREAT;
1391       break;
1392     case 'a':
1393       omode = O_WRONLY;
1394       oflags = O_APPEND | O_CREAT;
1395       break;
1396     default:
1397       _set_errno (EINVAL);
1398       return -1;
1399     }
1400   for (modestr++; *modestr; modestr++)
1401     {
1402       switch (*modestr)
1403         {
1404         case '+':
1405           omode = O_RDWR;
1406           break;
1407         case 'b':
1408           oflags |= O_BINARY;
1409           break;
1410         case 'x':
1411           oflags |= O_EXCL;
1412           break;
1413         case ',':
1414           goto keyvalue;
1415         default: /* Ignore unknown flags.  */
1416           break; 
1417         }
1418     }
1419
1420  keyvalue:
1421   /* Parse key/value pairs (similar to fopen on mainframes).  */
1422   for (cmode=0; *modestr == ','; modestr += strcspn (modestr, ","))
1423     {
1424       modestr++;
1425       modestr += strspn (modestr, " \t");
1426       if (!strncmp (modestr, "mode=", 5))
1427         {
1428           static struct {
1429             char letter;
1430             unsigned int value;
1431           } table[] = { { '-', 0 },
1432                         { 'r', S_IRUSR }, { 'w', S_IWUSR }, { 'x', S_IXUSR },
1433                         { 'r', S_IRGRP }, { 'w', S_IWGRP }, { 'x', S_IXGRP },
1434                         { 'r', S_IROTH }, { 'w', S_IWOTH }, { 'x', S_IXOTH }};
1435           int idx;
1436
1437           got_cmode = 1;
1438           modestr += 5;
1439           /* For now we only support a string as used by ls(1) and no
1440              octal numbers.  The first character must be a dash.  */
1441           for (idx=0; idx < 10 && *modestr; idx++, modestr++)
1442             {
1443               if (*modestr == table[idx].letter)
1444                 cmode |= table[idx].value;
1445               else if (*modestr != '-')
1446                 break;
1447             }
1448           if (*modestr && !strchr (" \t,", *modestr))
1449             {
1450               _set_errno (EINVAL);
1451               return -1;
1452             }
1453         }
1454     }
1455   if (!got_cmode)
1456     cmode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
1457
1458   *modeflags = (omode | oflags);
1459   if (r_cmode)
1460     *r_cmode = cmode;
1461   return 0;
1462 }
1463
1464 \f
1465
1466 /*
1467  * Low level stream functionality.
1468  */
1469
1470 static int
1471 es_fill (estream_t stream)
1472 {
1473   size_t bytes_read = 0;
1474   int err;
1475
1476   if (!stream->intern->func_read)
1477     {
1478       _set_errno (EOPNOTSUPP);
1479       err = -1;
1480     }
1481   else
1482     {
1483       es_cookie_read_function_t func_read = stream->intern->func_read;
1484       ssize_t ret;
1485
1486       ret = (*func_read) (stream->intern->cookie,
1487                           stream->buffer, stream->buffer_size);
1488       if (ret == -1)
1489         {
1490           bytes_read = 0;
1491           err = -1;
1492         }
1493       else
1494         {
1495           bytes_read = ret;
1496           err = 0;
1497         }
1498     }
1499
1500   if (err)
1501     stream->intern->indicators.err = 1;
1502   else if (!bytes_read)
1503     stream->intern->indicators.eof = 1;
1504
1505   stream->intern->offset += stream->data_len;
1506   stream->data_len = bytes_read;
1507   stream->data_offset = 0;
1508
1509   return err;
1510 }
1511
1512 static int
1513 es_flush (estream_t stream)
1514 {
1515   es_cookie_write_function_t func_write = stream->intern->func_write;
1516   int err;
1517
1518   assert (stream->flags.writing);
1519
1520   if (stream->data_offset)
1521     {
1522       size_t bytes_written;
1523       size_t data_flushed;
1524       ssize_t ret;
1525
1526       if (! func_write)
1527         {
1528           err = EOPNOTSUPP;
1529           goto out;
1530         }
1531
1532       /* Note: to prevent an endless loop caused by user-provided
1533          write-functions that pretend to have written more bytes than
1534          they were asked to write, we have to check for
1535          "(stream->data_offset - data_flushed) > 0" instead of
1536          "stream->data_offset - data_flushed".  */
1537       
1538       data_flushed = 0;
1539       err = 0;
1540       
1541       while ((((ssize_t) (stream->data_offset - data_flushed)) > 0) && (! err))
1542         {
1543           ret = (*func_write) (stream->intern->cookie,
1544                                stream->buffer + data_flushed,
1545                                stream->data_offset - data_flushed);
1546           if (ret == -1)
1547             {
1548               bytes_written = 0;
1549               err = -1;
1550             }
1551           else
1552             bytes_written = ret;
1553
1554           data_flushed += bytes_written;
1555           if (err)
1556             break;
1557         }
1558
1559       stream->data_flushed += data_flushed;
1560       if (stream->data_offset == data_flushed)
1561         {
1562           stream->intern->offset += stream->data_offset;
1563           stream->data_offset = 0;
1564           stream->data_flushed = 0;
1565
1566           /* Propagate flush event.  */
1567           (*func_write) (stream->intern->cookie, NULL, 0);
1568         }
1569     }
1570   else
1571     err = 0;
1572
1573  out:
1574     
1575   if (err)
1576     stream->intern->indicators.err = 1;
1577
1578   return err;
1579 }
1580
1581 /* Discard buffered data for STREAM.  */
1582 static void
1583 es_empty (estream_t stream)
1584 {
1585   assert (!stream->flags.writing);
1586   stream->data_len = 0;
1587   stream->data_offset = 0;
1588   stream->unread_data_len = 0;
1589 }
1590
1591 /* Initialize STREAM.  */
1592 static void
1593 es_initialize (estream_t stream,
1594                void *cookie, es_syshd_t *syshd,
1595                es_cookie_io_functions_t functions,
1596                unsigned int modeflags)
1597 {
1598   stream->intern->cookie = cookie;
1599   stream->intern->opaque = NULL;
1600   stream->intern->offset = 0;
1601   stream->intern->func_read = functions.func_read;
1602   stream->intern->func_write = functions.func_write;
1603   stream->intern->func_seek = functions.func_seek;
1604   stream->intern->func_close = functions.func_close;
1605   stream->intern->strategy = _IOFBF;
1606   stream->intern->syshd = *syshd;
1607   stream->intern->print_ntotal = 0;
1608   stream->intern->indicators.err = 0;
1609   stream->intern->indicators.eof = 0;
1610   stream->intern->is_stdstream = 0;
1611   stream->intern->stdstream_fd = 0;
1612   stream->intern->deallocate_buffer = 0;
1613   stream->intern->printable_fname = NULL;
1614   stream->intern->printable_fname_inuse = 0;
1615   stream->intern->onclose = NULL;
1616
1617   stream->data_len = 0;
1618   stream->data_offset = 0;
1619   stream->data_flushed = 0;
1620   stream->unread_data_len = 0;
1621   /* Depending on the modeflags we set whether we start in writing or
1622      reading mode.  This is required in case we are working on a
1623      stream which is not seeekable (like stdout).  Without this
1624      pre-initialization we would do a seek at the first write call and
1625      as this will fail no utput will be delivered. */
1626   if ((modeflags & O_WRONLY) || (modeflags & O_RDWR) )
1627     stream->flags.writing = 1;
1628   else
1629     stream->flags.writing = 0;
1630 }
1631
1632 /* Deinitialize STREAM.  */
1633 static int
1634 es_deinitialize (estream_t stream)
1635 {
1636   es_cookie_close_function_t func_close;
1637   int err, tmp_err;
1638
1639   func_close = stream->intern->func_close;
1640
1641   err = 0;
1642   if (stream->flags.writing)
1643     SET_UNLESS_NONZERO (err, tmp_err, es_flush (stream));
1644   if (func_close)
1645     SET_UNLESS_NONZERO (err, tmp_err, (*func_close) (stream->intern->cookie));
1646
1647   mem_free (stream->intern->printable_fname);
1648   stream->intern->printable_fname = NULL;
1649   stream->intern->printable_fname_inuse = 0;
1650   while (stream->intern->onclose)
1651     {
1652       notify_list_t tmp = stream->intern->onclose->next;
1653       mem_free (stream->intern->onclose);
1654       stream->intern->onclose = tmp;
1655     }
1656
1657   return err;
1658 }
1659
1660 /* Create a new stream object, initialize it.  */
1661 static int
1662 es_create (estream_t *stream, void *cookie, es_syshd_t *syshd,
1663            es_cookie_io_functions_t functions, unsigned int modeflags,
1664            int with_locked_list)
1665 {
1666   estream_internal_t stream_internal_new;
1667   estream_t stream_new;
1668   int err;
1669
1670   stream_new = NULL;
1671   stream_internal_new = NULL;
1672
1673   stream_new = mem_alloc (sizeof (*stream_new));
1674   if (! stream_new)
1675     {
1676       err = -1;
1677       goto out;
1678     }
1679
1680   stream_internal_new = mem_alloc (sizeof (*stream_internal_new));
1681   if (! stream_internal_new)
1682     {
1683       err = -1;
1684       goto out;
1685     }
1686
1687   stream_new->buffer = stream_internal_new->buffer;
1688   stream_new->buffer_size = sizeof (stream_internal_new->buffer);
1689   stream_new->unread_buffer = stream_internal_new->unread_buffer;
1690   stream_new->unread_buffer_size = sizeof (stream_internal_new->unread_buffer);
1691   stream_new->intern = stream_internal_new;
1692
1693   ESTREAM_MUTEX_INITIALIZE (stream_new->intern->lock);
1694   es_initialize (stream_new, cookie, syshd, functions, modeflags);
1695
1696   err = do_list_add (stream_new, with_locked_list);
1697   if (err)
1698     goto out;
1699
1700   *stream = stream_new;
1701
1702  out:
1703
1704   if (err)
1705     {
1706       if (stream_new)
1707         {
1708           es_deinitialize (stream_new);
1709           mem_free (stream_new);
1710         }
1711     }
1712
1713   return err;
1714 }
1715
1716 /* Deinitialize a stream object and destroy it.  */
1717 static int
1718 do_close (estream_t stream, int with_locked_list)
1719 {
1720   int err;
1721
1722   if (stream)
1723     {
1724       do_list_remove (stream, with_locked_list);
1725       while (stream->intern->onclose)
1726         {
1727           notify_list_t tmp = stream->intern->onclose->next;
1728
1729           if (stream->intern->onclose->fnc)
1730             stream->intern->onclose->fnc (stream,
1731                                           stream->intern->onclose->fnc_value);
1732           mem_free (stream->intern->onclose);
1733           stream->intern->onclose = tmp;
1734         }
1735       err = es_deinitialize (stream);
1736       mem_free (stream->intern);
1737       mem_free (stream);
1738     }
1739   else
1740     err = 0;
1741
1742   return err;
1743 }
1744
1745
1746 /* This worker function is called with a locked stream.  */
1747 static int
1748 do_onclose (estream_t stream, int mode,
1749             void (*fnc) (estream_t, void*), void *fnc_value)
1750 {
1751   notify_list_t item;
1752
1753   if (!mode)
1754     {
1755       for (item = stream->intern->onclose; item; item = item->next)
1756         if (item->fnc && item->fnc == fnc && item->fnc_value == fnc_value)
1757           item->fnc = NULL; /* Disable this notification.  */
1758     }
1759   else
1760     {
1761       item = mem_alloc (sizeof *item);
1762       if (!item)
1763         return -1;
1764       item->fnc = fnc;
1765       item->fnc_value = fnc_value;
1766       item->next = stream->intern->onclose;
1767       stream->intern->onclose = item;
1768     }
1769   return 0;
1770 }
1771
1772
1773 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1774    unbuffered-mode, storing the amount of bytes read in
1775    *BYTES_READ.  */
1776 static int
1777 es_read_nbf (estream_t ES__RESTRICT stream,
1778              unsigned char *ES__RESTRICT buffer,
1779              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1780 {
1781   es_cookie_read_function_t func_read = stream->intern->func_read;
1782   size_t data_read;
1783   ssize_t ret;
1784   int err;
1785
1786   data_read = 0;
1787   err = 0;
1788
1789   while (bytes_to_read - data_read)
1790     {
1791       ret = (*func_read) (stream->intern->cookie,
1792                           buffer + data_read, bytes_to_read - data_read);
1793       if (ret == -1)
1794         {
1795           err = -1;
1796           break;
1797         }
1798       else if (ret)
1799         data_read += ret;
1800       else
1801         break;
1802     }
1803
1804   stream->intern->offset += data_read;
1805   *bytes_read = data_read;
1806
1807   return err;
1808 }
1809
1810 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1811    fully-buffered-mode, storing the amount of bytes read in
1812    *BYTES_READ.  */
1813 static int
1814 es_read_fbf (estream_t ES__RESTRICT stream,
1815              unsigned char *ES__RESTRICT buffer,
1816              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1817 {
1818   size_t data_available;
1819   size_t data_to_read;
1820   size_t data_read;
1821   int err;
1822
1823   data_read = 0;
1824   err = 0;
1825
1826   while ((bytes_to_read - data_read) && (! err))
1827     {
1828       if (stream->data_offset == stream->data_len)
1829         {
1830           /* Nothing more to read in current container, try to
1831              fill container with new data.  */
1832           err = es_fill (stream);
1833           if (! err)
1834             if (! stream->data_len)
1835               /* Filling did not result in any data read.  */
1836               break;
1837         }
1838
1839       if (! err)
1840         {
1841           /* Filling resulted in some new data.  */
1842
1843           data_to_read = bytes_to_read - data_read;
1844           data_available = stream->data_len - stream->data_offset;
1845           if (data_to_read > data_available)
1846             data_to_read = data_available;
1847
1848           memcpy (buffer + data_read,
1849                   stream->buffer + stream->data_offset, data_to_read);
1850           stream->data_offset += data_to_read;
1851           data_read += data_to_read;
1852         }
1853     }
1854
1855   *bytes_read = data_read;
1856
1857   return err;
1858 }
1859
1860 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1861    line-buffered-mode, storing the amount of bytes read in
1862    *BYTES_READ.  */
1863 static int
1864 es_read_lbf (estream_t ES__RESTRICT stream,
1865              unsigned char *ES__RESTRICT buffer,
1866              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1867 {
1868   int err;
1869
1870   err = es_read_fbf (stream, buffer, bytes_to_read, bytes_read);
1871
1872   return err;
1873 }
1874
1875 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER, storing
1876    *the amount of bytes read in BYTES_READ.  */
1877 static int
1878 es_readn (estream_t ES__RESTRICT stream,
1879           void *ES__RESTRICT buffer_arg,
1880           size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1881 {
1882   unsigned char *buffer = (unsigned char *)buffer_arg;
1883   size_t data_read_unread, data_read;
1884   int err;
1885
1886   data_read_unread = 0;
1887   data_read = 0;
1888   err = 0;
1889
1890   if (stream->flags.writing)
1891     {
1892       /* Switching to reading mode -> flush output.  */
1893       err = es_flush (stream);
1894       if (err)
1895         goto out;
1896       stream->flags.writing = 0;
1897     }  
1898
1899   /* Read unread data first.  */
1900   while ((bytes_to_read - data_read_unread) && stream->unread_data_len)
1901     {
1902       buffer[data_read_unread]
1903         = stream->unread_buffer[stream->unread_data_len - 1];
1904       stream->unread_data_len--;
1905       data_read_unread++;
1906     }
1907
1908   switch (stream->intern->strategy)
1909     {
1910     case _IONBF:
1911       err = es_read_nbf (stream,
1912                          buffer + data_read_unread,
1913                          bytes_to_read - data_read_unread, &data_read);
1914       break;
1915     case _IOLBF:
1916       err = es_read_lbf (stream,
1917                          buffer + data_read_unread,
1918                          bytes_to_read - data_read_unread, &data_read);
1919       break;
1920     case _IOFBF:
1921       err = es_read_fbf (stream,
1922                          buffer + data_read_unread,
1923                          bytes_to_read - data_read_unread, &data_read);
1924       break;
1925     }
1926
1927  out:
1928
1929   if (bytes_read)
1930     *bytes_read = data_read_unread + data_read;
1931
1932   return err;
1933 }
1934
1935 /* Try to unread DATA_N bytes from DATA into STREAM, storing the
1936    amount of bytes successfully unread in *BYTES_UNREAD.  */
1937 static void
1938 es_unreadn (estream_t ES__RESTRICT stream,
1939             const unsigned char *ES__RESTRICT data, size_t data_n,
1940             size_t *ES__RESTRICT bytes_unread)
1941 {
1942   size_t space_left;
1943
1944   space_left = stream->unread_buffer_size - stream->unread_data_len;
1945
1946   if (data_n > space_left)
1947     data_n = space_left;
1948
1949   if (! data_n)
1950     goto out;
1951
1952   memcpy (stream->unread_buffer + stream->unread_data_len, data, data_n);
1953   stream->unread_data_len += data_n;
1954   stream->intern->indicators.eof = 0;
1955
1956  out:
1957
1958   if (bytes_unread)
1959     *bytes_unread = data_n;
1960 }
1961
1962 /* Seek in STREAM.  */
1963 static int
1964 es_seek (estream_t ES__RESTRICT stream, off_t offset, int whence,
1965          off_t *ES__RESTRICT offset_new)
1966 {
1967   es_cookie_seek_function_t func_seek = stream->intern->func_seek;
1968   int err, ret;
1969   off_t off;
1970
1971   if (! func_seek)
1972     {
1973       _set_errno (EOPNOTSUPP);
1974       err = -1;
1975       goto out;
1976     }
1977
1978   if (stream->flags.writing)
1979     {
1980       /* Flush data first in order to prevent flushing it to the wrong
1981          offset.  */
1982       err = es_flush (stream);
1983       if (err)
1984         goto out;
1985       stream->flags.writing = 0;
1986     }
1987
1988   off = offset;
1989   if (whence == SEEK_CUR)
1990     {
1991       off = off - stream->data_len + stream->data_offset;
1992       off -= stream->unread_data_len;
1993     }
1994   
1995   ret = (*func_seek) (stream->intern->cookie, &off, whence);
1996   if (ret == -1)
1997     {
1998       err = -1;
1999       goto out;
2000     }
2001
2002   err = 0;
2003   es_empty (stream);
2004
2005   if (offset_new)
2006     *offset_new = off;
2007
2008   stream->intern->indicators.eof = 0;
2009   stream->intern->offset = off;
2010
2011  out:
2012   
2013   if (err)
2014     stream->intern->indicators.err = 1;
2015
2016   return err;
2017 }
2018
2019 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
2020    unbuffered-mode, storing the amount of bytes written in
2021    *BYTES_WRITTEN.  */
2022 static int
2023 es_write_nbf (estream_t ES__RESTRICT stream,
2024               const unsigned char *ES__RESTRICT buffer,
2025               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
2026 {
2027   es_cookie_write_function_t func_write = stream->intern->func_write;
2028   size_t data_written;
2029   ssize_t ret;
2030   int err;
2031
2032   if (bytes_to_write && (! func_write))
2033     {
2034       err = EOPNOTSUPP;
2035       goto out;
2036     }  
2037
2038   data_written = 0;
2039   err = 0;
2040   
2041   while (bytes_to_write - data_written)
2042     {
2043       ret = (*func_write) (stream->intern->cookie,
2044                            buffer + data_written,
2045                            bytes_to_write - data_written);
2046       if (ret == -1)
2047         {
2048           err = -1;
2049           break;
2050         }
2051       else
2052         data_written += ret;
2053     }
2054
2055   stream->intern->offset += data_written;
2056   *bytes_written = data_written;
2057
2058  out:
2059
2060   return err;
2061 }
2062
2063 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
2064    fully-buffered-mode, storing the amount of bytes written in
2065    *BYTES_WRITTEN.  */
2066 static int
2067 es_write_fbf (estream_t ES__RESTRICT stream,
2068               const unsigned char *ES__RESTRICT buffer,
2069               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
2070 {
2071   size_t space_available;
2072   size_t data_to_write;
2073   size_t data_written;
2074   int err;
2075
2076   data_written = 0;
2077   err = 0;
2078
2079   while ((bytes_to_write - data_written) && (! err))
2080     {
2081       if (stream->data_offset == stream->buffer_size)
2082         /* Container full, flush buffer.  */
2083         err = es_flush (stream);
2084
2085       if (! err)
2086         {
2087           /* Flushing resulted in empty container.  */
2088           
2089           data_to_write = bytes_to_write - data_written;
2090           space_available = stream->buffer_size - stream->data_offset;
2091           if (data_to_write > space_available)
2092             data_to_write = space_available;
2093               
2094           memcpy (stream->buffer + stream->data_offset,
2095                   buffer + data_written, data_to_write);
2096           stream->data_offset += data_to_write;
2097           data_written += data_to_write;
2098         }
2099     }
2100
2101   *bytes_written = data_written;
2102
2103   return err;
2104 }
2105
2106
2107 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
2108    line-buffered-mode, storing the amount of bytes written in
2109    *BYTES_WRITTEN.  */
2110 static int
2111 es_write_lbf (estream_t ES__RESTRICT stream,
2112               const unsigned char *ES__RESTRICT buffer,
2113               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
2114 {
2115   size_t data_flushed = 0;
2116   size_t data_buffered = 0;
2117   unsigned char *nlp;
2118   int err = 0;
2119
2120   nlp = memrchr (buffer, '\n', bytes_to_write);
2121   if (nlp)
2122     {
2123       /* Found a newline, directly write up to (including) this
2124          character.  */
2125       err = es_flush (stream);
2126       if (!err)
2127         err = es_write_nbf (stream, buffer, nlp - buffer + 1, &data_flushed);
2128     }
2129
2130   if (!err)
2131     {
2132       /* Write remaining data fully buffered.  */
2133       err = es_write_fbf (stream, buffer + data_flushed,
2134                           bytes_to_write - data_flushed, &data_buffered);
2135     }
2136
2137   *bytes_written = data_flushed + data_buffered;
2138   return err;
2139 }
2140
2141
2142 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in, storing the
2143    amount of bytes written in BYTES_WRITTEN.  */
2144 static int
2145 es_writen (estream_t ES__RESTRICT stream,
2146            const void *ES__RESTRICT buffer,
2147            size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
2148 {
2149   size_t data_written;
2150   int err;
2151
2152   data_written = 0;
2153   err = 0;
2154   
2155   if (!stream->flags.writing)
2156     {
2157       /* Switching to writing mode -> discard input data and seek to
2158          position at which reading has stopped.  We can do this only
2159          if a seek function has been registered. */
2160       if (stream->intern->func_seek)
2161         {
2162           err = es_seek (stream, 0, SEEK_CUR, NULL);
2163           if (err)
2164             {
2165               if (errno == ESPIPE)
2166                 err = 0;
2167               else
2168                 goto out;
2169             }
2170         }
2171     }
2172
2173   switch (stream->intern->strategy)
2174     {
2175     case _IONBF:
2176       err = es_write_nbf (stream, buffer, bytes_to_write, &data_written);
2177       break;
2178
2179     case _IOLBF:
2180       err = es_write_lbf (stream, buffer, bytes_to_write, &data_written);
2181       break;
2182
2183     case _IOFBF:
2184       err = es_write_fbf (stream, buffer, bytes_to_write, &data_written);
2185       break;
2186     }
2187
2188  out:
2189     
2190   if (bytes_written)
2191     *bytes_written = data_written;
2192   if (data_written)
2193     if (!stream->flags.writing)
2194       stream->flags.writing = 1;
2195
2196   return err;
2197 }
2198
2199
2200 static int
2201 es_peek (estream_t ES__RESTRICT stream, unsigned char **ES__RESTRICT data,
2202          size_t *ES__RESTRICT data_len)
2203 {
2204   int err;
2205
2206   if (stream->flags.writing)
2207     {
2208       /* Switching to reading mode -> flush output.  */
2209       err = es_flush (stream);
2210       if (err)
2211         goto out;
2212       stream->flags.writing = 0;
2213     }  
2214
2215   if (stream->data_offset == stream->data_len)
2216     {
2217       /* Refill container.  */
2218       err = es_fill (stream);
2219       if (err)
2220         goto out;
2221     }
2222   
2223   if (data)
2224     *data = stream->buffer + stream->data_offset;
2225   if (data_len)
2226     *data_len = stream->data_len - stream->data_offset;
2227   err = 0;
2228
2229  out:
2230
2231   return err;
2232 }
2233
2234
2235 /* Skip SIZE bytes of input data contained in buffer.  */
2236 static int
2237 es_skip (estream_t stream, size_t size)
2238 {
2239   int err;
2240
2241   if (stream->data_offset + size > stream->data_len)
2242     {
2243       _set_errno (EINVAL);
2244       err = -1;
2245     }
2246   else
2247     {
2248       stream->data_offset += size;
2249       err = 0;
2250     }
2251
2252   return err;
2253 }
2254
2255
2256 static int
2257 doreadline (estream_t ES__RESTRICT stream, size_t max_length,
2258             char *ES__RESTRICT *ES__RESTRICT line,
2259             size_t *ES__RESTRICT line_length)
2260 {
2261   size_t space_left;
2262   size_t line_size;
2263   estream_t line_stream;
2264   char *line_new;
2265   void *line_stream_cookie;
2266   char *newline;
2267   unsigned char *data;
2268   size_t data_len;
2269   int err;
2270   es_syshd_t syshd;
2271
2272   line_new = NULL;
2273   line_stream = NULL;
2274   line_stream_cookie = NULL;
2275
2276   err = func_mem_create (&line_stream_cookie, NULL, 0, 0,
2277                          BUFFER_BLOCK_SIZE, 1,
2278                          mem_realloc, mem_free, 
2279                          O_RDWR,
2280                          0);
2281   if (err)
2282     goto out;
2283
2284   memset (&syshd, 0, sizeof syshd);
2285   err = es_create (&line_stream, line_stream_cookie, &syshd,
2286                    estream_functions_mem, O_RDWR, 0);
2287   if (err)
2288     goto out;
2289
2290   space_left = max_length;
2291   line_size = 0;
2292   while (1)
2293     {
2294       if (max_length && (space_left == 1))
2295         break;
2296
2297       err = es_peek (stream, &data, &data_len);
2298       if (err || (! data_len))
2299         break;
2300
2301       if (data_len > (space_left - 1))
2302         data_len = space_left - 1;
2303
2304       newline = memchr (data, '\n', data_len);
2305       if (newline)
2306         {
2307           data_len = (newline - (char *) data) + 1;
2308           err = es_write (line_stream, data, data_len, NULL);
2309           if (! err)
2310             {
2311               space_left -= data_len;
2312               line_size += data_len;
2313               es_skip (stream, data_len);
2314               break;
2315             }
2316         }
2317       else
2318         {
2319           err = es_write (line_stream, data, data_len, NULL);
2320           if (! err)
2321             {
2322               space_left -= data_len;
2323               line_size += data_len;
2324               es_skip (stream, data_len);
2325             }
2326         }
2327       if (err)
2328         break;
2329     }
2330   if (err)
2331     goto out;
2332
2333   /* Complete line has been written to line_stream.  */
2334   
2335   if ((max_length > 1) && (! line_size))
2336     {
2337       stream->intern->indicators.eof = 1;
2338       goto out;
2339     }
2340
2341   err = es_seek (line_stream, 0, SEEK_SET, NULL);
2342   if (err)
2343     goto out;
2344
2345   if (! *line)
2346     {
2347       line_new = mem_alloc (line_size + 1);
2348       if (! line_new)
2349         {
2350           err = -1;
2351           goto out;
2352         }
2353     }
2354   else
2355     line_new = *line;
2356
2357   err = es_read (line_stream, line_new, line_size, NULL);
2358   if (err)
2359     goto out;
2360
2361   line_new[line_size] = '\0';
2362
2363   if (! *line)
2364     *line = line_new;
2365   if (line_length)
2366     *line_length = line_size;
2367
2368  out:
2369
2370   if (line_stream)
2371     do_close (line_stream, 0);
2372   else if (line_stream_cookie)
2373     es_func_mem_destroy (line_stream_cookie);
2374
2375   if (err)
2376     {
2377       if (! *line)
2378         mem_free (line_new);
2379       stream->intern->indicators.err = 1;
2380     }
2381
2382   return err;
2383 }
2384
2385
2386 /* Output fucntion used for estream_format.  */
2387 static int
2388 print_writer (void *outfncarg, const char *buf, size_t buflen)
2389 {
2390   estream_t stream = outfncarg;
2391   size_t nwritten;
2392   int rc;
2393
2394   nwritten = 0;
2395   rc = es_writen (stream, buf, buflen, &nwritten);
2396   stream->intern->print_ntotal += nwritten;
2397   return rc;
2398 }
2399
2400
2401 /* The core of our printf function.  This is called in locked state. */
2402 static int
2403 es_print (estream_t ES__RESTRICT stream,
2404           const char *ES__RESTRICT format, va_list ap)
2405 {
2406   int rc;
2407
2408   stream->intern->print_ntotal = 0;
2409   rc = estream_format (print_writer, stream, format, ap);
2410   if (rc)
2411     return -1;
2412   return (int)stream->intern->print_ntotal;
2413 }
2414
2415
2416 static void
2417 es_set_indicators (estream_t stream, int ind_err, int ind_eof)
2418 {
2419   if (ind_err != -1)
2420     stream->intern->indicators.err = ind_err ? 1 : 0;
2421   if (ind_eof != -1)
2422     stream->intern->indicators.eof = ind_eof ? 1 : 0;
2423 }
2424
2425
2426 static int
2427 es_get_indicator (estream_t stream, int ind_err, int ind_eof)
2428 {
2429   int ret = 0;
2430   
2431   if (ind_err)
2432     ret = stream->intern->indicators.err;
2433   else if (ind_eof)
2434     ret = stream->intern->indicators.eof;
2435
2436   return ret;
2437 }
2438
2439
2440 static int
2441 es_set_buffering (estream_t ES__RESTRICT stream,
2442                   char *ES__RESTRICT buffer, int mode, size_t size)
2443 {
2444   int err;
2445
2446   /* Flush or empty buffer depending on mode.  */
2447   if (stream->flags.writing)
2448     {
2449       err = es_flush (stream);
2450       if (err)
2451         goto out;
2452     }
2453   else
2454     es_empty (stream);
2455
2456   es_set_indicators (stream, -1, 0);
2457   
2458   /* Free old buffer in case that was allocated by this function.  */
2459   if (stream->intern->deallocate_buffer)
2460     {
2461       stream->intern->deallocate_buffer = 0;
2462       mem_free (stream->buffer);
2463       stream->buffer = NULL;
2464     }
2465
2466   if (mode == _IONBF)
2467     stream->buffer_size = 0;
2468   else
2469     {
2470       void *buffer_new;
2471       
2472       if (buffer)
2473         buffer_new = buffer;
2474       else
2475         {
2476           if (!size)
2477             size = BUFSIZ;
2478           buffer_new = mem_alloc (size);
2479           if (! buffer_new)
2480             {
2481               err = -1;
2482               goto out;
2483             }
2484         }
2485
2486       stream->buffer = buffer_new;
2487       stream->buffer_size = size;
2488       if (! buffer)
2489         stream->intern->deallocate_buffer = 1;
2490     }
2491   stream->intern->strategy = mode;
2492   err = 0;
2493
2494  out:
2495
2496   return err;
2497 }
2498
2499
2500 static off_t
2501 es_offset_calculate (estream_t stream)
2502 {
2503   off_t offset;
2504
2505   offset = stream->intern->offset + stream->data_offset;
2506   if (offset < stream->unread_data_len)
2507     /* Offset undefined.  */
2508     offset = 0;
2509   else
2510     offset -= stream->unread_data_len;
2511
2512   return offset;
2513 }
2514
2515
2516 static void
2517 es_opaque_ctrl (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque_new,
2518                 void **ES__RESTRICT opaque_old)
2519 {
2520   if (opaque_old)
2521     *opaque_old = stream->intern->opaque;
2522   if (opaque_new)
2523     stream->intern->opaque = opaque_new;
2524 }
2525
2526
2527 \f
2528
2529 /* API.  */
2530
2531 int
2532 es_init (void)
2533 {
2534   int err;
2535
2536   err = do_init ();
2537
2538   return err;
2539 }
2540
2541
2542 \f
2543 estream_t
2544 es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode)
2545 {
2546   unsigned int modeflags, cmode;
2547   int create_called;
2548   estream_t stream;
2549   void *cookie;
2550   int err;
2551   int fd;
2552   es_syshd_t syshd;
2553
2554   stream = NULL;
2555   cookie = NULL;
2556   create_called = 0;
2557
2558   err = parse_mode (mode, &modeflags, &cmode);
2559   if (err)
2560     goto out;
2561   
2562   err = func_file_create (&cookie, &fd, path, modeflags, cmode);
2563   if (err)
2564     goto out;
2565   
2566   syshd.type = ES_SYSHD_FD;
2567   syshd.u.fd = fd;
2568
2569   create_called = 1;
2570   err = es_create (&stream, cookie, &syshd, estream_functions_fd, modeflags, 0);
2571   if (err)
2572     goto out;
2573
2574   if (stream && path)
2575     fname_set_internal (stream, path, 1);
2576
2577  out:
2578   
2579   if (err && create_called)
2580     (*estream_functions_fd.func_close) (cookie);
2581
2582   return stream;
2583 }
2584
2585
2586 \f
2587 estream_t
2588 es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len,
2589           unsigned int grow,
2590           func_realloc_t func_realloc, func_free_t func_free,
2591           const char *ES__RESTRICT mode)
2592 {
2593   unsigned int modeflags;
2594   int create_called;
2595   estream_t stream;
2596   void *cookie;
2597   int err;
2598   es_syshd_t syshd;
2599
2600   cookie = 0;
2601   stream = NULL;
2602   create_called = 0;
2603   
2604   err = parse_mode (mode, &modeflags, NULL);
2605   if (err)
2606     goto out;
2607
2608   err = func_mem_create (&cookie, data, data_n, data_len,
2609                          BUFFER_BLOCK_SIZE, grow, 
2610                          func_realloc, func_free, modeflags, 0);
2611   if (err)
2612     goto out;
2613   
2614   memset (&syshd, 0, sizeof syshd);
2615   create_called = 1;
2616   err = es_create (&stream, cookie, &syshd,
2617                    estream_functions_mem, modeflags, 0);
2618
2619  out:
2620
2621   if (err && create_called)
2622     (*estream_functions_mem.func_close) (cookie);
2623
2624   return stream;
2625 }
2626
2627
2628 \f
2629 estream_t
2630 es_fopenmem (size_t memlimit, const char *ES__RESTRICT mode)
2631 {
2632   unsigned int modeflags;
2633   estream_t stream = NULL;
2634   void *cookie = NULL;
2635   es_syshd_t syshd;
2636
2637   /* Memory streams are always read/write.  We use MODE only to get
2638      the append flag.  */
2639   if (parse_mode (mode, &modeflags, NULL))
2640     return NULL;
2641   modeflags |= O_RDWR;
2642
2643   
2644   if (func_mem_create (&cookie, NULL, 0, 0,
2645                        BUFFER_BLOCK_SIZE, 1,
2646                        mem_realloc, mem_free, modeflags,
2647                        memlimit))
2648     return NULL;
2649   
2650   memset (&syshd, 0, sizeof syshd);
2651   if (es_create (&stream, cookie, &syshd, estream_functions_mem, modeflags, 0))
2652     (*estream_functions_mem.func_close) (cookie);
2653
2654   return stream;
2655 }
2656
2657
2658 \f
2659 estream_t
2660 es_fopencookie (void *ES__RESTRICT cookie,
2661                 const char *ES__RESTRICT mode,
2662                 es_cookie_io_functions_t functions)
2663 {
2664   unsigned int modeflags;
2665   estream_t stream;
2666   int err;
2667   es_syshd_t syshd;
2668
2669   stream = NULL;
2670   modeflags = 0;
2671   
2672   err = parse_mode (mode, &modeflags, NULL);
2673   if (err)
2674     goto out;
2675
2676   memset (&syshd, 0, sizeof syshd);
2677   err = es_create (&stream, cookie, &syshd, functions, modeflags, 0);
2678   if (err)
2679     goto out;
2680
2681  out:
2682   return stream;
2683 }
2684
2685
2686 \f
2687 estream_t
2688 do_fdopen (int filedes, const char *mode, int no_close, int with_locked_list)
2689 {
2690   unsigned int modeflags;
2691   int create_called;
2692   estream_t stream;
2693   void *cookie;
2694   int err;
2695   es_syshd_t syshd;
2696
2697   stream = NULL;
2698   cookie = NULL;
2699   create_called = 0;
2700
2701   err = parse_mode (mode, &modeflags, NULL);
2702   if (err)
2703     goto out;
2704
2705   err = func_fd_create (&cookie, filedes, modeflags, no_close);
2706   if (err)
2707     goto out;
2708
2709   syshd.type = ES_SYSHD_FD;
2710   syshd.u.fd = filedes;
2711   create_called = 1;
2712   err = es_create (&stream, cookie, &syshd, estream_functions_fd,
2713                    modeflags, with_locked_list);
2714
2715  out:
2716   if (err && create_called)
2717     (*estream_functions_fd.func_close) (cookie);
2718
2719   return stream;
2720 }
2721
2722 estream_t
2723 es_fdopen (int filedes, const char *mode)
2724 {
2725   return do_fdopen (filedes, mode, 0, 0);
2726 }
2727
2728 /* A variant of es_fdopen which does not close FILEDES at the end.  */
2729 estream_t
2730 es_fdopen_nc (int filedes, const char *mode)
2731 {
2732   return do_fdopen (filedes, mode, 1, 0);
2733 }
2734
2735
2736 \f
2737 estream_t
2738 do_fpopen (FILE *fp, const char *mode, int no_close, int with_locked_list)
2739 {
2740   unsigned int modeflags, cmode;
2741   int create_called;
2742   estream_t stream;
2743   void *cookie;
2744   int err;
2745   es_syshd_t syshd;
2746
2747   stream = NULL;
2748   cookie = NULL;
2749   create_called = 0;
2750
2751   err = parse_mode (mode, &modeflags, &cmode);
2752   if (err)
2753     goto out;
2754
2755   if (fp)
2756     fflush (fp);
2757   err = func_fp_create (&cookie, fp, modeflags, no_close);
2758   if (err)
2759     goto out;
2760
2761   syshd.type = ES_SYSHD_FD;
2762   syshd.u.fd = fp? fileno (fp): -1;
2763   create_called = 1;
2764   err = es_create (&stream, cookie, &syshd, estream_functions_fp,
2765                    modeflags, with_locked_list);
2766
2767  out:
2768
2769   if (err && create_called)
2770     (*estream_functions_fp.func_close) (cookie);
2771
2772   return stream;
2773 }
2774
2775   
2776 /* Create an estream from the stdio stream FP.  This mechanism is
2777    useful in case the stdio streams have special properties and may
2778    not be mixed with fd based functions.  This is for example the case
2779    under Windows where the 3 standard streams are associated with the
2780    console whereas a duped and fd-opened stream of one of this stream
2781    won't be associated with the console.  As this messes things up it
2782    is easier to keep on using the standard I/O stream as a backend for
2783    estream. */
2784 estream_t
2785 es_fpopen (FILE *fp, const char *mode)
2786 {
2787   return do_fpopen (fp, mode, 0, 0);
2788 }
2789
2790
2791 /* Same as es_fpopen but does not close  FP at the end.  */
2792 estream_t
2793 es_fpopen_nc (FILE *fp, const char *mode)
2794 {
2795   return do_fpopen (fp, mode, 1, 0);
2796 }
2797
2798
2799 \f
2800 #ifdef HAVE_W32_SYSTEM
2801 estream_t
2802 do_w32open (HANDLE hd, const char *mode,
2803             int no_close, int with_locked_list)
2804 {
2805   unsigned int modeflags, cmode;
2806   int create_called = 0;
2807   estream_t stream = NULL;
2808   void *cookie = NULL;
2809   int err;
2810   es_syshd_t syshd;
2811
2812   err = parse_mode (mode, &modeflags, &cmode);
2813   if (err)
2814     goto leave;
2815
2816   err = es_func_w32_create (&cookie, hd, modeflags, no_close);
2817   if (err)
2818     goto leave;
2819
2820   syshd.type = ES_SYSHD_HANDLE;
2821   syshd.u.handle = hd;
2822   create_called = 1;
2823   err = es_create (&stream, cookie, &syshd, estream_functions_w32,
2824                    modeflags, with_locked_list);
2825
2826  leave:
2827   if (err && create_called)
2828     (*estream_functions_w32.func_close) (cookie);
2829
2830   return stream;
2831 }
2832 #endif /*HAVE_W32_SYSTEM*/
2833
2834 static estream_t
2835 do_sysopen (es_syshd_t *syshd, const char *mode, int no_close)
2836 {
2837   estream_t stream;
2838
2839   switch (syshd->type)
2840     {
2841     case ES_SYSHD_FD:
2842     case ES_SYSHD_SOCK:
2843       stream = do_fdopen (syshd->u.fd, mode, no_close, 0);
2844       break;
2845       
2846 #ifdef HAVE_W32_SYSTEM
2847     case ES_SYSHD_HANDLE:
2848       stream = do_w32open (syshd->u.handle, mode, no_close, 0);
2849       break;
2850 #endif
2851
2852     /* FIXME: Support RVIDs under Wince?  */
2853
2854     default:
2855       _set_errno (EINVAL);
2856       stream = NULL;
2857     }
2858   return stream;
2859 }
2860
2861 /* On POSIX systems this function is an alias for es_fdopen.  Under
2862    Windows it uses the bare W32 API and thus a HANDLE instead of a
2863    file descriptor.  */
2864 estream_t
2865 es_sysopen (es_syshd_t *syshd, const char *mode)
2866 {
2867   return do_sysopen (syshd, mode, 0);
2868 }
2869
2870 /* Same as es_sysopen but the handle/fd will not be closed by
2871    es_fclose.  */
2872 estream_t
2873 es_sysopen_nc (es_syshd_t *syshd, const char *mode)
2874 {
2875   return do_sysopen (syshd, mode, 1);
2876 }
2877
2878
2879
2880 /* Set custom standard descriptors to be used for stdin, stdout and
2881    stderr.  This function needs to be called before any of the
2882    standard streams are accessed.  */
2883 void
2884 _es_set_std_fd (int no, int fd)
2885 {
2886   fprintf (stderr, "es_set_std_fd(%d, %d)\n", no, fd);
2887   ESTREAM_LIST_LOCK;
2888   if (no >= 0 && no < 3 && !custom_std_fds_valid[no])
2889     {
2890       custom_std_fds[no] = fd;
2891       custom_std_fds_valid[no] = 1;
2892     }
2893   ESTREAM_LIST_UNLOCK;
2894 }
2895
2896
2897 /* Return the stream used for stdin, stdout or stderr.  */
2898 estream_t
2899 _es_get_std_stream (int fd)
2900 {
2901   estream_list_t list_obj;
2902   estream_t stream = NULL;
2903
2904   fd %= 3; /* We only allow 0, 1 or 2 but we don't want to return an error. */
2905   ESTREAM_LIST_LOCK;
2906   for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
2907     if (list_obj->car->intern->is_stdstream
2908         && list_obj->car->intern->stdstream_fd == fd)
2909       {
2910         stream = list_obj->car;
2911         break;
2912       }
2913   if (!stream)
2914     {
2915       /* Standard stream not yet created.  We first try to create them
2916          from registered file descriptors.  */
2917       if (!fd && custom_std_fds_valid[0])
2918         stream = do_fdopen (custom_std_fds[0], "r", 1, 1);
2919       else if (fd == 1 && custom_std_fds_valid[1])
2920         stream = do_fdopen (custom_std_fds[1], "a", 1, 1);
2921       else if (custom_std_fds_valid[2])
2922         stream = do_fdopen (custom_std_fds[2], "a", 1, 1);
2923       
2924       if (!stream)
2925         {
2926           /* Second try is to use the standard C streams.  */
2927           if (!fd)
2928             stream = do_fpopen (stdin, "r", 1, 1);
2929           else if (fd == 1)
2930             stream = do_fpopen (stdout, "a", 1, 1);
2931           else
2932             stream = do_fpopen (stderr, "a", 1, 1);
2933         }
2934       
2935       if (!stream) 
2936         {
2937           /* Last try: Create a bit bucket.  */
2938           stream = do_fpopen (NULL, fd? "a":"r", 0, 1);
2939           if (!stream)
2940             {
2941               fprintf (stderr, "fatal: error creating a dummy estream"
2942                        " for %d: %s\n", fd, strerror (errno));
2943               abort();
2944             }
2945         }
2946
2947       stream->intern->is_stdstream = 1;
2948       stream->intern->stdstream_fd = fd;
2949       if (fd == 2)
2950         es_set_buffering (stream, NULL, _IOLBF, 0);
2951       fname_set_internal (stream, 
2952                           fd == 0? "[stdin]" :
2953                           fd == 1? "[stdout]" : "[stderr]", 0);
2954     }
2955   ESTREAM_LIST_UNLOCK;
2956   return stream;
2957 }
2958
2959
2960 estream_t
2961 es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode,
2962             estream_t ES__RESTRICT stream)
2963 {
2964   int err;
2965
2966   if (path)
2967     {
2968       unsigned int modeflags, cmode;
2969       int create_called;
2970       void *cookie;
2971       int fd;
2972       es_syshd_t syshd;
2973
2974       cookie = NULL;
2975       create_called = 0;
2976       
2977       ESTREAM_LOCK (stream);
2978
2979       es_deinitialize (stream);
2980
2981       err = parse_mode (mode, &modeflags, &cmode);
2982       if (err)
2983         goto leave;
2984       
2985       err = func_file_create (&cookie, &fd, path, modeflags, cmode);
2986       if (err)
2987         goto leave;
2988
2989       syshd.type = ES_SYSHD_FD;
2990       syshd.u.fd = fd;
2991       create_called = 1;
2992       es_initialize (stream, cookie, &syshd, estream_functions_fd, modeflags);
2993
2994     leave:
2995
2996       if (err)
2997         {
2998           if (create_called)
2999             es_func_fd_destroy (cookie);
3000       
3001           do_close (stream, 0);
3002           stream = NULL;
3003         }
3004       else
3005         {
3006           if (stream && path)
3007             fname_set_internal (stream, path, 1);
3008           ESTREAM_UNLOCK (stream);
3009         }
3010     }
3011   else
3012     {
3013       /* FIXME?  We don't support re-opening at the moment.  */
3014       _set_errno (EINVAL);
3015       es_deinitialize (stream);
3016       do_close (stream, 0);
3017       stream = NULL;
3018     }
3019
3020   return stream;
3021 }
3022
3023
3024 int
3025 es_fclose (estream_t stream)
3026 {
3027   int err;
3028
3029   err = do_close (stream, 0);
3030
3031   return err;
3032 }
3033
3034
3035 /* Register or unregister a close notification function for STREAM.
3036    FNC is the function to call and FNC_VALUE the value passed as
3037    second argument.  To register the notification the value for MODE
3038    must be 1.  If mode is 0 the function tries to remove or disable an
3039    already registered notification; for this to work the value of FNC
3040    and FNC_VALUE must be the same as with the registration and
3041    FNC_VALUE must be a unique value.  No error will be returned if
3042    MODE is 0.  Unregistered should only be used in the error case
3043    because it may not remove memory internall allocated for the
3044    onclose handler.
3045
3046    The notification will be called right before the stream is closed.
3047    It may not call any estream function for STREAM, neither direct nor
3048    indirectly. */
3049 int
3050 es_onclose (estream_t stream, int mode,
3051             void (*fnc) (estream_t, void*), void *fnc_value)
3052 {
3053   int err;
3054
3055   ESTREAM_LOCK (stream);
3056   err = do_onclose (stream, mode, fnc, fnc_value);
3057   ESTREAM_UNLOCK (stream);
3058   
3059   return err;
3060 }
3061
3062
3063 int
3064 es_fileno_unlocked (estream_t stream)
3065 {
3066   es_syshd_t syshd;
3067
3068   if (es_syshd (stream, &syshd))
3069     return -1;
3070   switch (syshd.type)
3071     {
3072     case ES_SYSHD_FD:   return syshd.u.fd;
3073     case ES_SYSHD_SOCK: return syshd.u.sock;
3074     default: 
3075       _set_errno (EINVAL);
3076       return -1;
3077     }
3078 }
3079
3080
3081 /* Return the handle of a stream which has been opened by es_sysopen.
3082    The caller needs to pass a structure which will be filled with the
3083    sys handle.  Return 0 on success or true on error and sets errno.
3084    This is the unlocked version.  */
3085 int
3086 es_syshd_unlocked (estream_t stream, es_syshd_t *syshd)
3087 {
3088   if (!stream || !syshd || stream->intern->syshd.type == ES_SYSHD_NONE)
3089     {
3090       if (syshd)
3091         syshd->type = ES_SYSHD_NONE;
3092       _set_errno (EINVAL);
3093       return -1;
3094     }
3095   
3096   *syshd = stream->intern->syshd;
3097   return 0;
3098 }
3099
3100
3101 void
3102 es_flockfile (estream_t stream)
3103 {
3104   ESTREAM_LOCK (stream);
3105 }
3106
3107
3108 int
3109 es_ftrylockfile (estream_t stream)
3110 {
3111   return ESTREAM_TRYLOCK (stream);
3112 }
3113
3114
3115 void
3116 es_funlockfile (estream_t stream)
3117 {
3118   ESTREAM_UNLOCK (stream);
3119 }
3120
3121
3122 int
3123 es_fileno (estream_t stream)
3124 {
3125   int ret;
3126
3127   ESTREAM_LOCK (stream);
3128   ret = es_fileno_unlocked (stream);
3129   ESTREAM_UNLOCK (stream);
3130
3131   return ret;
3132 }
3133
3134
3135 /* Return the handle of a stream which has been opened by es_sysopen.
3136    The caller needs to pass a structure which will be filled with the
3137    sys handle.  Return 0 on success or true on error and sets errno.
3138    This is the unlocked version.  */
3139 int
3140 es_syshd (estream_t stream, es_syshd_t *syshd)
3141 {
3142   int ret;
3143
3144   ESTREAM_LOCK (stream);
3145   ret = es_syshd_unlocked (stream, syshd);
3146   ESTREAM_UNLOCK (stream);
3147
3148   return ret;
3149 }
3150
3151
3152 int
3153 es_feof_unlocked (estream_t stream)
3154 {
3155   return es_get_indicator (stream, 0, 1);
3156 }
3157
3158
3159 int
3160 es_feof (estream_t stream)
3161 {
3162   int ret;
3163
3164   ESTREAM_LOCK (stream);
3165   ret = es_feof_unlocked (stream);
3166   ESTREAM_UNLOCK (stream);
3167
3168   return ret;
3169 }
3170
3171
3172 int
3173 es_ferror_unlocked (estream_t stream)
3174 {
3175   return es_get_indicator (stream, 1, 0);
3176 }
3177
3178
3179 int
3180 es_ferror (estream_t stream)
3181 {
3182   int ret;
3183
3184   ESTREAM_LOCK (stream);
3185   ret = es_ferror_unlocked (stream);
3186   ESTREAM_UNLOCK (stream);
3187
3188   return ret;
3189 }
3190
3191
3192 void
3193 es_clearerr_unlocked (estream_t stream)
3194 {
3195   es_set_indicators (stream, 0, 0);
3196 }
3197
3198
3199 void
3200 es_clearerr (estream_t stream)
3201 {
3202   ESTREAM_LOCK (stream);
3203   es_clearerr_unlocked (stream);
3204   ESTREAM_UNLOCK (stream);
3205 }
3206
3207
3208 static int
3209 do_fflush (estream_t stream)
3210 {
3211   int err;
3212   
3213   if (stream->flags.writing)
3214     err = es_flush (stream);
3215   else
3216     {
3217       es_empty (stream);
3218       err = 0;
3219     }
3220
3221   return err;
3222 }
3223
3224
3225 int
3226 es_fflush (estream_t stream)
3227 {
3228   int err;
3229   
3230   if (stream)
3231     {
3232       ESTREAM_LOCK (stream);
3233       err = do_fflush (stream);
3234       ESTREAM_UNLOCK (stream);
3235     }
3236   else
3237     err = do_list_iterate (do_fflush);
3238
3239   return err ? EOF : 0;
3240 }
3241
3242
3243 int
3244 es_fseek (estream_t stream, long int offset, int whence)
3245 {
3246   int err;
3247
3248   ESTREAM_LOCK (stream);
3249   err = es_seek (stream, offset, whence, NULL);
3250   ESTREAM_UNLOCK (stream);
3251
3252   return err;
3253 }
3254
3255
3256 int
3257 es_fseeko (estream_t stream, off_t offset, int whence)
3258 {
3259   int err;
3260   
3261   ESTREAM_LOCK (stream);
3262   err = es_seek (stream, offset, whence, NULL);
3263   ESTREAM_UNLOCK (stream);
3264
3265   return err;
3266 }
3267
3268
3269 long int
3270 es_ftell (estream_t stream)
3271 {
3272   long int ret;
3273   
3274   ESTREAM_LOCK (stream);
3275   ret = es_offset_calculate (stream);
3276   ESTREAM_UNLOCK (stream);
3277
3278   return ret;
3279 }
3280
3281
3282 off_t
3283 es_ftello (estream_t stream)
3284 {
3285   off_t ret = -1;
3286
3287   ESTREAM_LOCK (stream);
3288   ret = es_offset_calculate (stream);
3289   ESTREAM_UNLOCK (stream);
3290
3291   return ret;
3292 }
3293
3294
3295 void
3296 es_rewind (estream_t stream)
3297 {
3298   ESTREAM_LOCK (stream);
3299   es_seek (stream, 0L, SEEK_SET, NULL);
3300   es_set_indicators (stream, 0, -1);
3301   ESTREAM_UNLOCK (stream);
3302 }
3303
3304
3305 int
3306 _es_getc_underflow (estream_t stream)
3307 {
3308   int err;
3309   unsigned char c;
3310   size_t bytes_read;
3311
3312   err = es_readn (stream, &c, 1, &bytes_read);
3313
3314   return (err || (! bytes_read)) ? EOF : c;
3315 }
3316
3317
3318 int
3319 _es_putc_overflow (int c, estream_t stream)
3320 {
3321   unsigned char d = c;
3322   int err;
3323
3324   err = es_writen (stream, &d, 1, NULL);
3325
3326   return err ? EOF : c;
3327 }
3328
3329
3330 int
3331 es_fgetc (estream_t stream)
3332 {
3333   int ret;
3334   
3335   ESTREAM_LOCK (stream);
3336   ret = es_getc_unlocked (stream);
3337   ESTREAM_UNLOCK (stream);
3338
3339   return ret;
3340 }
3341
3342
3343 int
3344 es_fputc (int c, estream_t stream)
3345 {
3346   int ret;
3347   
3348   ESTREAM_LOCK (stream);
3349   ret = es_putc_unlocked (c, stream);
3350   ESTREAM_UNLOCK (stream);
3351
3352   return ret;
3353 }
3354
3355
3356 int
3357 es_ungetc (int c, estream_t stream)
3358 {
3359   unsigned char data = (unsigned char) c;
3360   size_t data_unread;
3361
3362   ESTREAM_LOCK (stream);
3363   es_unreadn (stream, &data, 1, &data_unread);
3364   ESTREAM_UNLOCK (stream);
3365
3366   return data_unread ? c : EOF;
3367 }
3368
3369
3370 int
3371 es_read (estream_t ES__RESTRICT stream,
3372          void *ES__RESTRICT buffer, size_t bytes_to_read,
3373          size_t *ES__RESTRICT bytes_read)
3374 {
3375   int err;
3376
3377   if (bytes_to_read)
3378     {
3379       ESTREAM_LOCK (stream);
3380       err = es_readn (stream, buffer, bytes_to_read, bytes_read);
3381       ESTREAM_UNLOCK (stream);
3382     }
3383   else
3384     err = 0;
3385
3386   return err;
3387 }
3388
3389
3390 int
3391 es_write (estream_t ES__RESTRICT stream,
3392           const void *ES__RESTRICT buffer, size_t bytes_to_write,
3393           size_t *ES__RESTRICT bytes_written)
3394 {
3395   int err;
3396
3397   if (bytes_to_write)
3398     {
3399       ESTREAM_LOCK (stream);
3400       err = es_writen (stream, buffer, bytes_to_write, bytes_written);
3401       ESTREAM_UNLOCK (stream);
3402     }
3403   else
3404     err = 0;
3405
3406   return err;
3407 }
3408
3409
3410 size_t
3411 es_fread (void *ES__RESTRICT ptr, size_t size, size_t nitems,
3412           estream_t ES__RESTRICT stream)
3413 {
3414   size_t ret, bytes;
3415   int err;
3416
3417   if (size * nitems)
3418     {
3419       ESTREAM_LOCK (stream);
3420       err = es_readn (stream, ptr, size * nitems, &bytes);
3421       ESTREAM_UNLOCK (stream);
3422
3423       ret = bytes / size;
3424     }
3425   else
3426     ret = 0;
3427
3428   return ret;
3429 }
3430
3431
3432 size_t
3433 es_fwrite (const void *ES__RESTRICT ptr, size_t size, size_t nitems,
3434            estream_t ES__RESTRICT stream)
3435 {
3436   size_t ret, bytes;
3437   int err;
3438
3439   if (size * nitems)
3440     {
3441       ESTREAM_LOCK (stream);
3442       err = es_writen (stream, ptr, size * nitems, &bytes);
3443       ESTREAM_UNLOCK (stream);
3444
3445       ret = bytes / size;
3446     }
3447   else
3448     ret = 0;
3449
3450   return ret;
3451 }
3452
3453
3454 char *
3455 es_fgets (char *ES__RESTRICT buffer, int length, estream_t ES__RESTRICT stream)
3456 {
3457   unsigned char *s = (unsigned char*)buffer;
3458   int c;
3459    
3460   if (!length)
3461     return NULL;
3462      
3463   c = EOF;
3464   ESTREAM_LOCK (stream);
3465   while (length > 1 && (c = es_getc_unlocked (stream)) != EOF && c != '\n')
3466     {
3467       *s++ = c;
3468       length--;
3469     }
3470   ESTREAM_UNLOCK (stream);
3471
3472   if (c == EOF && s == (unsigned char*)buffer)
3473     return NULL; /* Nothing read.  */
3474
3475   if (c != EOF && length > 1)
3476     *s++ = c;
3477
3478   *s = 0;
3479   return buffer;
3480 }
3481
3482
3483 int
3484 es_fputs_unlocked (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream)
3485 {
3486   size_t length;
3487   int err;
3488
3489   length = strlen (s);
3490   err = es_writen (stream, s, length, NULL);
3491   return err ? EOF : 0;
3492 }
3493
3494 int
3495 es_fputs (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream)
3496 {
3497   size_t length;
3498   int err;
3499
3500   length = strlen (s);
3501   ESTREAM_LOCK (stream);
3502   err = es_writen (stream, s, length, NULL);
3503   ESTREAM_UNLOCK (stream);
3504
3505   return err ? EOF : 0;
3506 }
3507
3508
3509 ssize_t
3510 es_getline (char *ES__RESTRICT *ES__RESTRICT lineptr, size_t *ES__RESTRICT n,
3511             estream_t ES__RESTRICT stream)
3512 {
3513   char *line = NULL;
3514   size_t line_n = 0;
3515   int err;
3516
3517   ESTREAM_LOCK (stream);
3518   err = doreadline (stream, 0, &line, &line_n);
3519   ESTREAM_UNLOCK (stream);
3520   if (err)
3521     goto out;
3522
3523   if (*n)
3524     {
3525       /* Caller wants us to use his buffer.  */
3526       
3527       if (*n < (line_n + 1))
3528         {
3529           /* Provided buffer is too small -> resize.  */
3530
3531           void *p;
3532
3533           p = mem_realloc (*lineptr, line_n + 1);
3534           if (! p)
3535             err = -1;
3536           else
3537             {
3538               if (*lineptr != p)
3539                 *lineptr = p;
3540             }
3541         }
3542
3543       if (! err)
3544         {
3545           memcpy (*lineptr, line, line_n + 1);
3546           if (*n != line_n)
3547             *n = line_n;
3548         }
3549       mem_free (line);
3550     }
3551   else
3552     {
3553       /* Caller wants new buffers.  */
3554       *lineptr = line;
3555       *n = line_n;
3556     }
3557
3558  out:
3559
3560   return err ? err : (ssize_t)line_n;
3561 }
3562
3563
3564
3565 /* Same as fgets() but if the provided buffer is too short a larger
3566    one will be allocated.  This is similar to getline. A line is
3567    considered a byte stream ending in a LF.
3568
3569    If MAX_LENGTH is not NULL, it shall point to a value with the
3570    maximum allowed allocation.  
3571
3572    Returns the length of the line. EOF is indicated by a line of
3573    length zero. A truncated line is indicated my setting the value at
3574    MAX_LENGTH to 0.  If the returned value is less then 0 not enough
3575    memory was enable or another error occurred; ERRNO is then set
3576    accordingly.
3577
3578    If a line has been truncated, the file pointer is moved forward to
3579    the end of the line so that the next read starts with the next
3580    line.  Note that MAX_LENGTH must be re-initialzied in this case.
3581
3582    The caller initially needs to provide the address of a variable,
3583    initialized to NULL, at ADDR_OF_BUFFER and don't change this value
3584    anymore with the following invocations.  LENGTH_OF_BUFFER should be
3585    the address of a variable, initialized to 0, which is also
3586    maintained by this function.  Thus, both paramaters should be
3587    considered the state of this function.
3588
3589    Note: The returned buffer is allocated with enough extra space to
3590    allow the caller to append a CR,LF,Nul.  The buffer should be
3591    released using es_free.
3592  */
3593 ssize_t
3594 es_read_line (estream_t stream, 
3595               char **addr_of_buffer, size_t *length_of_buffer,
3596               size_t *max_length)
3597 {
3598   int c;
3599   char  *buffer = *addr_of_buffer;
3600   size_t length = *length_of_buffer;
3601   size_t nbytes = 0;
3602   size_t maxlen = max_length? *max_length : 0;
3603   char *p;
3604
3605   if (!buffer)
3606     { 
3607       /* No buffer given - allocate a new one. */
3608       length = 256;
3609       buffer = mem_alloc (length);
3610       *addr_of_buffer = buffer;
3611       if (!buffer)
3612         {
3613           *length_of_buffer = 0;
3614           if (max_length)
3615             *max_length = 0;
3616           return -1;
3617         }
3618       *length_of_buffer = length;
3619     }
3620
3621   if (length < 4)
3622     {
3623       /* This should never happen. If it does, the function has been
3624          called with wrong arguments. */
3625       _set_errno (EINVAL);
3626       return -1;
3627     }
3628   length -= 3; /* Reserve 3 bytes for CR,LF,EOL. */
3629
3630   ESTREAM_LOCK (stream);
3631   p = buffer;
3632   while  ((c = es_getc_unlocked (stream)) != EOF)
3633     {
3634       if (nbytes == length)
3635         { 
3636           /* Enlarge the buffer. */
3637           if (maxlen && length > maxlen) 
3638             {
3639               /* We are beyond our limit: Skip the rest of the line. */
3640               while (c != '\n' && (c=es_getc_unlocked (stream)) != EOF)
3641                 ;
3642               *p++ = '\n'; /* Always append a LF (we reserved some space). */
3643               nbytes++;
3644               if (max_length)
3645                 *max_length = 0; /* Indicate truncation. */
3646               break; /* the while loop. */
3647             }
3648           length += 3; /* Adjust for the reserved bytes. */
3649           length += length < 1024? 256 : 1024;
3650           *addr_of_buffer = mem_realloc (buffer, length);
3651           if (!*addr_of_buffer)
3652             {
3653               int save_errno = errno;
3654               mem_free (buffer); 
3655               *length_of_buffer = 0;
3656               if (max_length)
3657                 *max_length = 0;
3658               ESTREAM_UNLOCK (stream);
3659               _set_errno (save_errno);
3660               return -1;
3661             }
3662           buffer = *addr_of_buffer;
3663           *length_of_buffer = length;
3664           length -= 3; 
3665           p = buffer + nbytes;
3666         }
3667       *p++ = c;
3668       nbytes++;
3669       if (c == '\n')
3670         break;
3671     }
3672   *p = 0; /* Make sure the line is a string. */
3673   ESTREAM_UNLOCK (stream);
3674
3675   return nbytes;
3676 }
3677
3678 /* Wrapper around free() to match the memory allocation system used
3679    by estream.  Should be used for all buffers returned to the caller
3680    by libestream. */
3681 void
3682 es_free (void *a)
3683 {
3684   mem_free (a);
3685 }
3686
3687
3688 int
3689 es_vfprintf_unlocked (estream_t ES__RESTRICT stream,
3690                       const char *ES__RESTRICT format,
3691                       va_list ap)
3692 {
3693   return es_print (stream, format, ap);
3694 }
3695
3696
3697 int
3698 es_vfprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format,
3699              va_list ap)
3700 {
3701   int ret;
3702   
3703   ESTREAM_LOCK (stream);
3704   ret = es_print (stream, format, ap);
3705   ESTREAM_UNLOCK (stream);
3706
3707   return ret;
3708 }
3709
3710
3711 int
3712 es_fprintf_unlocked (estream_t ES__RESTRICT stream,
3713                      const char *ES__RESTRICT format, ...)
3714 {
3715   int ret;
3716   
3717   va_list ap;
3718   va_start (ap, format);
3719   ret = es_print (stream, format, ap);
3720   va_end (ap);
3721
3722   return ret;
3723 }
3724
3725
3726 int
3727 es_fprintf (estream_t ES__RESTRICT stream,
3728             const char *ES__RESTRICT format, ...)
3729 {
3730   int ret;
3731   
3732   va_list ap;
3733   va_start (ap, format);
3734   ESTREAM_LOCK (stream);
3735   ret = es_print (stream, format, ap);
3736   ESTREAM_UNLOCK (stream);
3737   va_end (ap);
3738
3739   return ret;
3740 }
3741
3742
3743 int
3744 es_printf_unlocked (const char *ES__RESTRICT format, ...)
3745 {
3746   int ret;
3747   
3748   va_list ap;
3749   va_start (ap, format);
3750   ret = es_print (es_stdout, format, ap);
3751   va_end (ap);
3752
3753   return ret;
3754 }
3755
3756
3757 int
3758 es_printf (const char *ES__RESTRICT format, ...)
3759 {
3760   int ret;
3761   estream_t stream = es_stdout;
3762   
3763   va_list ap;
3764   va_start (ap, format);
3765   ESTREAM_LOCK (stream);
3766   ret = es_print (stream, format, ap);
3767   ESTREAM_UNLOCK (stream);
3768   va_end (ap);
3769
3770   return ret;
3771 }
3772
3773
3774 /* A variant of asprintf.  The function returns the allocated buffer
3775    or NULL on error; ERRNO is set in the error case.  The caller
3776    should use es_free to release the buffer.  This function actually
3777    belongs into estream-printf but we put it here as a convenience
3778    and because es_free is required anyway.  */
3779 char *
3780 es_asprintf (const char *ES__RESTRICT format, ...)
3781 {
3782   int rc;
3783   va_list ap;
3784   char *buf;
3785
3786   va_start (ap, format);
3787   rc = estream_vasprintf (&buf, format, ap);
3788   va_end (ap);
3789   if (rc < 0)
3790     return NULL;
3791   return buf;
3792 }
3793
3794
3795 /* A variant of vasprintf.  The function returns the allocated buffer
3796    or NULL on error; ERRNO is set in the error case.  The caller
3797    should use es_free to release the buffer.  This function actually
3798    belongs into estream-printf but we put it here as a convenience
3799    and because es_free is required anyway.  */
3800 char * 
3801 es_vasprintf (const char *ES__RESTRICT format, va_list ap)
3802 {
3803   int rc;
3804   char *buf;
3805
3806   rc = estream_vasprintf (&buf, format, ap);
3807   if (rc < 0)
3808     return NULL;
3809   return buf;
3810 }
3811
3812
3813 static int
3814 tmpfd (void)
3815 {
3816 #ifdef HAVE_W32_SYSTEM
3817   int attempts, n;
3818 #ifdef HAVE_W32CE_SYSTEM
3819   wchar_t buffer[MAX_PATH+9+12+1];
3820 # define mystrlen(a) wcslen (a)
3821   wchar_t *name, *p;
3822 #else
3823   char buffer[MAX_PATH+9+12+1];
3824 # define mystrlen(a) strlen (a)
3825   char *name, *p;
3826 #endif
3827   HANDLE file;
3828   int pid = GetCurrentProcessId ();
3829   unsigned int value;
3830   int i;
3831   
3832   n = GetTempPath (MAX_PATH+1, buffer);
3833   if (!n || n > MAX_PATH || mystrlen (buffer) > MAX_PATH)
3834     {
3835       _set_errno (ENOENT);
3836       return -1;
3837     }
3838   p = buffer + mystrlen (buffer);
3839 #ifdef HAVE_W32CE_SYSTEM
3840   wcscpy (p, L"_estream");
3841 #else
3842   strcpy (p, "_estream");
3843 #endif
3844   p += 8;
3845   /* We try to create the directory but don't care about an error as
3846      it may already exist and the CreateFile would throw an error
3847      anyway.  */
3848   CreateDirectory (buffer, NULL);
3849   *p++ = '\\';
3850   name = p;
3851   for (attempts=0; attempts < 10; attempts++)
3852     {
3853       p = name;
3854       value = (GetTickCount () ^ ((pid<<16) & 0xffff0000));
3855       for (i=0; i < 8; i++)
3856         {
3857           *p++ = tohex (((value >> 28) & 0x0f));
3858           value <<= 4;
3859         }
3860 #ifdef HAVE_W32CE_SYSTEM
3861       wcscpy (p, L".tmp");
3862 #else
3863       strcpy (p, ".tmp");
3864 #endif
3865       file = CreateFile (buffer,
3866                          GENERIC_READ | GENERIC_WRITE,
3867                          0,
3868                          NULL,
3869                          CREATE_NEW,
3870                          FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
3871                          NULL);
3872       if (file != INVALID_HANDLE_VALUE)
3873         {
3874 #ifdef HAVE_W32CE_SYSTEM
3875           int fd = (int)file;
3876 #else
3877           int fd = _open_osfhandle ((long)file, 0);
3878           if (fd == -1)
3879             {
3880               CloseHandle (file);
3881               return -1;
3882             }
3883 #endif
3884           return fd;
3885         }
3886       Sleep (1); /* One ms as this is the granularity of GetTickCount.  */
3887     }
3888   _set_errno (ENOENT);
3889   return -1;
3890 #else /*!HAVE_W32_SYSTEM*/
3891   FILE *fp;
3892   int fp_fd;
3893   int fd;
3894
3895   fp = NULL;
3896   fd = -1;
3897   
3898   fp = tmpfile ();
3899   if (! fp)
3900     goto out;
3901
3902   fp_fd = fileno (fp);
3903   fd = dup (fp_fd);
3904
3905  out:
3906
3907   if (fp)
3908     fclose (fp);
3909
3910   return fd;
3911 #endif /*!HAVE_W32_SYSTEM*/
3912 }
3913
3914 estream_t
3915 es_tmpfile (void)
3916 {
3917   unsigned int modeflags;
3918   int create_called;
3919   estream_t stream;
3920   void *cookie;
3921   int err;
3922   int fd;
3923   es_syshd_t syshd;
3924
3925   create_called = 0;
3926   stream = NULL;
3927   modeflags = O_RDWR | O_TRUNC | O_CREAT;
3928   cookie = NULL;
3929   
3930   fd = tmpfd ();
3931   if (fd == -1)
3932     {
3933       err = -1;
3934       goto out;
3935     }
3936
3937   err = func_fd_create (&cookie, fd, modeflags, 0);
3938   if (err)
3939     goto out;
3940
3941   syshd.type = ES_SYSHD_FD;
3942   syshd.u.fd = fd;
3943   create_called = 1;
3944   err = es_create (&stream, cookie, &syshd, estream_functions_fd, modeflags, 0);
3945
3946  out:
3947   if (err)
3948     {
3949       if (create_called)
3950         es_func_fd_destroy (cookie);
3951       else if (fd != -1)
3952         close (fd);
3953       stream = NULL;
3954     }
3955   
3956   return stream;
3957 }
3958
3959
3960 int
3961 es_setvbuf (estream_t ES__RESTRICT stream,
3962             char *ES__RESTRICT buf, int type, size_t size)
3963 {
3964   int err;
3965   
3966   if ((type == _IOFBF || type == _IOLBF || type == _IONBF)
3967       && (!buf || size || type == _IONBF))
3968     {
3969       ESTREAM_LOCK (stream);
3970       err = es_set_buffering (stream, buf, type, size);
3971       ESTREAM_UNLOCK (stream);
3972     }
3973   else
3974     {
3975       _set_errno (EINVAL);
3976       err = -1;
3977     }
3978
3979   return err;
3980 }
3981
3982
3983 void
3984 es_setbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf)
3985 {
3986   ESTREAM_LOCK (stream);
3987   es_set_buffering (stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
3988   ESTREAM_UNLOCK (stream);
3989 }
3990
3991
3992 /* Put a stream into binary mode.  This is only needed for the
3993    standard streams if they are to be used in a binary way.  On Unix
3994    systems it is never needed but MSDOS based systems require such a
3995    call.  It needs to be called before any I/O is done on STREAM.  */
3996 void
3997 es_set_binary (estream_t stream)
3998 {
3999   ESTREAM_LOCK (stream);
4000   if (!(stream->intern->modeflags & O_BINARY))
4001     {
4002       stream->intern->modeflags |= O_BINARY;
4003 #ifdef HAVE_DOSISH_SYSTEM
4004       if (stream->intern->func_read == es_func_fd_read)
4005         {
4006           estream_cookie_fd_t fd_cookie = stream->intern->cookie;
4007
4008           if (!IS_INVALID_FD (fd_cookie->fd))
4009             setmode (fd_cookie->fd, O_BINARY);
4010         }
4011       else if (stream->intern->func_read == es_func_fp_read)
4012         {
4013           estream_cookie_fp_t fp_cookie = stream->intern->cookie;
4014
4015           if (fp_cookie->fp)
4016             setmode (fileno (fp_cookie->fp), O_BINARY);
4017         }
4018 #endif
4019     }
4020   ESTREAM_UNLOCK (stream);
4021 }
4022
4023
4024 void
4025 es_opaque_set (estream_t stream, void *opaque)
4026 {
4027   ESTREAM_LOCK (stream);
4028   es_opaque_ctrl (stream, opaque, NULL);
4029   ESTREAM_UNLOCK (stream);
4030 }
4031
4032
4033 void *
4034 es_opaque_get (estream_t stream)
4035 {
4036   void *opaque;
4037   
4038   ESTREAM_LOCK (stream);
4039   es_opaque_ctrl (stream, NULL, &opaque);
4040   ESTREAM_UNLOCK (stream);
4041
4042   return opaque;
4043 }
4044
4045
4046 static void
4047 fname_set_internal (estream_t stream, const char *fname, int quote)
4048 {
4049   if (stream->intern->printable_fname
4050       && !stream->intern->printable_fname_inuse)
4051     {
4052       mem_free (stream->intern->printable_fname);
4053       stream->intern->printable_fname = NULL;
4054     }
4055   if (stream->intern->printable_fname)
4056     return; /* Can't change because it is in use.  */
4057
4058   if (*fname != '[')
4059     quote = 0;
4060   else
4061     quote = !!quote;
4062
4063   stream->intern->printable_fname = mem_alloc (strlen (fname) + quote + 1);
4064   if (fname)
4065     {
4066       if (quote)
4067         stream->intern->printable_fname[0] = '\\';
4068       strcpy (stream->intern->printable_fname+quote, fname);
4069     }
4070 }
4071
4072
4073 /* Set the filename attribute of STREAM.  There is no error return.
4074    as long as STREAM is valid.  This function is called internally by
4075    functions which open a filename.  */
4076 void
4077 es_fname_set (estream_t stream, const char *fname)
4078 {
4079   if (fname)
4080     {
4081       ESTREAM_LOCK (stream);
4082       fname_set_internal (stream, fname, 1);
4083       ESTREAM_UNLOCK (stream);
4084     }
4085 }
4086
4087
4088 /* Return the filename attribute of STREAM.  In case no filename has
4089    been set, "[?]" will be returned.  The returned file name is valid
4090    as long as STREAM is valid.  */
4091 const char *
4092 es_fname_get (estream_t stream)
4093 {
4094   const char *fname;
4095
4096   ESTREAM_LOCK (stream);
4097   fname = stream->intern->printable_fname;
4098   if (fname)
4099     stream->intern->printable_fname_inuse = 1;
4100   ESTREAM_UNLOCK (stream);
4101   if (!fname)
4102     fname = "[?]";
4103   return fname;
4104 }
4105
4106
4107 /* Print a BUFFER to STREAM while replacing all control characters and
4108    the characters in DELIMITERS by standard C escape sequences.
4109    Returns 0 on success or -1 on error.  If BYTES_WRITTEN is not NULL
4110    the number of bytes actually written are stored at this
4111    address.  */
4112 int 
4113 es_write_sanitized (estream_t ES__RESTRICT stream,
4114                     const void * ES__RESTRICT buffer, size_t length,
4115                     const char * delimiters, 
4116                     size_t * ES__RESTRICT bytes_written)
4117 {
4118   const unsigned char *p = buffer;
4119   size_t count = 0;
4120   int ret;
4121
4122   ESTREAM_LOCK (stream);
4123   for (; length; length--, p++, count++)
4124     {
4125       if (*p < 0x20 
4126           || *p == 0x7f
4127           || (delimiters 
4128               && (strchr (delimiters, *p) || *p == '\\')))
4129         {
4130           es_putc_unlocked ('\\', stream);
4131           count++;
4132           if (*p == '\n')
4133             {
4134               es_putc_unlocked ('n', stream);
4135               count++;
4136             }
4137           else if (*p == '\r')
4138             {
4139               es_putc_unlocked ('r', stream);
4140               count++;
4141             }
4142           else if (*p == '\f')
4143             {
4144               es_putc_unlocked ('f', stream);
4145               count++;
4146             }
4147           else if (*p == '\v')
4148             {
4149               es_putc_unlocked ('v', stream);
4150               count++;
4151             }
4152           else if (*p == '\b')
4153             {
4154               es_putc_unlocked ('b', stream);
4155               count++;
4156             }
4157           else if (!*p)
4158             {
4159               es_putc_unlocked('0', stream);
4160               count++;
4161             }
4162           else
4163             {
4164               es_fprintf_unlocked (stream, "x%02x", *p);
4165               count += 3;
4166             }
4167         }
4168       else
4169         {
4170           es_putc_unlocked (*p, stream);
4171           count++;
4172         }
4173     }
4174
4175   if (bytes_written)
4176     *bytes_written = count;
4177   ret =  es_ferror_unlocked (stream)? -1 : 0;
4178   ESTREAM_UNLOCK (stream);
4179
4180   return ret;
4181 }
4182
4183
4184 /* Write LENGTH bytes of BUFFER to STREAM as a hex encoded string.
4185    RESERVED must be 0.  Returns 0 on success or -1 on error.  If
4186    BYTES_WRITTEN is not NULL the number of bytes actually written are
4187    stored at this address.  */
4188 int
4189 es_write_hexstring (estream_t ES__RESTRICT stream,
4190                     const void *ES__RESTRICT buffer, size_t length,
4191                     int reserved, size_t *ES__RESTRICT bytes_written )
4192 {
4193   int ret;
4194   const unsigned char *s;
4195   size_t count = 0;
4196
4197   (void)reserved;
4198
4199 #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
4200
4201   if (!length)
4202     return 0;
4203
4204   ESTREAM_LOCK (stream);
4205
4206   for (s = buffer; length; s++, length--)
4207     {
4208       es_putc_unlocked ( tohex ((*s>>4)&15), stream);
4209       es_putc_unlocked ( tohex (*s&15), stream);
4210       count += 2;
4211     }
4212
4213   if (bytes_written)
4214     *bytes_written = count;
4215   ret = es_ferror_unlocked (stream)? -1 : 0;
4216
4217   ESTREAM_UNLOCK (stream);
4218
4219   return ret;
4220
4221 #undef tohex
4222 }
4223
4224
4225
4226 #ifdef GNUPG_MAJOR_VERSION
4227 /* Special estream function to print an UTF8 string in the native
4228    encoding.  The interface is the same as es_write_sanitized, however
4229    only one delimiter may be supported. 
4230
4231    THIS IS NOT A STANDARD ESTREAM FUNCTION AND ONLY USED BY GNUPG!. */
4232 int
4233 es_write_sanitized_utf8_buffer (estream_t stream,
4234                                 const void *buffer, size_t length, 
4235                                 const char *delimiters, size_t *bytes_written)
4236 {
4237   const char *p = buffer;
4238   size_t i;
4239
4240   /* We can handle plain ascii simpler, so check for it first. */
4241   for (i=0; i < length; i++ ) 
4242     {
4243       if ( (p[i] & 0x80) )
4244         break;
4245     }
4246   if (i < length)
4247     {
4248       int delim = delimiters? *delimiters : 0;
4249       char *buf;
4250       int ret;
4251
4252       /*(utf8 conversion already does the control character quoting). */
4253       buf = utf8_to_native (p, length, delim);
4254       if (bytes_written)
4255         *bytes_written = strlen (buf);
4256       ret = es_fputs (buf, stream);
4257       xfree (buf);
4258       return ret == EOF? ret : (int)i;
4259     }
4260   else
4261     return es_write_sanitized (stream, p, length, delimiters, bytes_written);
4262 }
4263 #endif /*GNUPG_MAJOR_VERSION*/