First steps towards supporting W32.
[gnupg.git] / common / estream.c
1 /* estream.c - Extended Stream I/O Library
2  * Copyright (C) 2004, 2005, 2006, 2007 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, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #ifdef USE_ESTREAM_SUPPORT_H
23 # include <estream-support.h>
24 #endif
25
26 #ifdef HAVE_CONFIG_H
27 # include <config.h>
28 #endif
29
30 #include <sys/types.h>
31 #include <sys/file.h>
32 #include <sys/stat.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <stdarg.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <stddef.h>
41 #include <assert.h>
42
43 #ifdef WITHOUT_GNU_PTH /* Give the Makefile a chance to build without Pth.  */
44 #undef HAVE_PTH
45 #undef USE_GNU_PTH
46 #endif
47
48 #ifdef HAVE_PTH
49 # include <pth.h>
50 #endif
51
52 /* This is for the special hack to use estream.c in GnuPG.  */
53 #ifdef GNUPG_MAJOR_VERSION
54 #include "../common/util.h"
55 #endif
56
57 #ifndef HAVE_MKSTEMP
58 int mkstemp (char *template);
59 #endif
60
61 #ifndef HAVE_MEMRCHR
62 void *memrchr (const void *block, int c, size_t size);
63 #endif
64
65 #include <estream.h>
66 #include <estream-printf.h>
67
68 \f
69
70 /* Generally used types.  */
71
72 typedef void *(*func_realloc_t) (void *mem, size_t size);
73 typedef void (*func_free_t) (void *mem);
74
75 #ifdef HAVE_FOPENCOOKIE
76 typedef ssize_t my_funopen_hook_ret_t;
77 #else
78 typedef int     my_funopen_hook_ret_t;
79 #endif
80
81
82 \f
83
84 /* Buffer management layer.  */
85
86 #define BUFFER_BLOCK_SIZE  BUFSIZ
87 #define BUFFER_UNREAD_SIZE 16
88
89 \f
90
91 /* Macros.  */
92
93 #define BUFFER_ROUND_TO_BLOCK(size, block_size) \
94   (((size) + (block_size - 1)) / block_size)
95
96 \f
97
98 /* Locking.  */
99
100 #ifdef HAVE_PTH
101
102 typedef pth_mutex_t estream_mutex_t;
103 # define ESTREAM_MUTEX_INITIALIZER PTH_MUTEX_INIT
104 # define ESTREAM_MUTEX_LOCK(mutex)        \
105   pth_mutex_acquire (&(mutex), 0, NULL)
106 # define ESTREAM_MUTEX_UNLOCK(mutex)      \
107   pth_mutex_release (&(mutex))
108 # define ESTREAM_MUTEX_TRYLOCK(mutex)     \
109   ((pth_mutex_acquire (&(mutex), 1, NULL) == TRUE) ? 0 : -1)
110 # define ESTREAM_MUTEX_INITIALIZE(mutex)  \
111   pth_mutex_init    (&(mutex))
112 # define ESTREAM_THREADING_INIT() ((pth_init () == TRUE) ? 0 : -1)
113
114 #else
115
116 typedef void *estream_mutex_t;
117 # define ESTREAM_MUTEX_INITIALIZER NULL
118 # define ESTREAM_MUTEX_LOCK(mutex) (void) 0
119 # define ESTREAM_MUTEX_UNLOCK(mutex) (void) 0
120 # define ESTREAM_MUTEX_TRYLOCK(mutex) 0
121 # define ESTREAM_MUTEX_INITIALIZE(mutex) (void) 0
122 # define ESTREAM_THREADING_INIT() 0
123
124 #endif
125
126 /* Memory allocator functions.  */
127
128 #define ES_MEM_ALLOC   malloc
129 #define ES_MEM_REALLOC realloc
130 #define ES_MEM_FREE    free
131
132 /* Primitive system I/O.  */
133
134 #ifdef HAVE_PTH
135 # define ESTREAM_SYS_READ  pth_read
136 # define ESTREAM_SYS_WRITE pth_write
137 #else
138 # define ESTREAM_SYS_READ  read
139 # define ESTREAM_SYS_WRITE write
140 #endif
141
142 /* Misc definitions.  */
143
144 #define ES_DEFAULT_OPEN_MODE (S_IRUSR | S_IWUSR)
145
146 #define ES_FLAG_WRITING ES__FLAG_WRITING
147
148 /* An internal stream object.  */
149
150 struct estream_internal
151 {
152   unsigned char buffer[BUFFER_BLOCK_SIZE];
153   unsigned char unread_buffer[BUFFER_UNREAD_SIZE];
154   estream_mutex_t lock;          /* Lock. */
155   void *cookie;                  /* Cookie.               */
156   void *opaque;                  /* Opaque data.          */
157   unsigned int flags;            /* Flags.                */
158   off_t offset;
159   es_cookie_read_function_t func_read;
160   es_cookie_write_function_t func_write;
161   es_cookie_seek_function_t func_seek;
162   es_cookie_close_function_t func_close;
163   int strategy;
164   int fd;
165   struct
166   {
167     unsigned int err: 1;
168     unsigned int eof: 1;
169   } indicators;
170   unsigned int deallocate_buffer: 1;
171   unsigned int print_err: 1;     /* Error in print_fun_writer.  */
172   int print_errno;               /* Errno from print_fun_writer.  */
173   size_t print_ntotal;           /* Bytes written from in print_fun_writer. */
174   FILE *print_fp;                /* Stdio stream used by print_fun_writer.  */
175 };
176
177
178 typedef struct estream_internal *estream_internal_t;
179
180 #define ESTREAM_LOCK(stream) ESTREAM_MUTEX_LOCK (stream->intern->lock)
181 #define ESTREAM_UNLOCK(stream) ESTREAM_MUTEX_UNLOCK (stream->intern->lock)
182 #define ESTREAM_TRYLOCK(stream) ESTREAM_MUTEX_TRYLOCK (stream->intern->lock)
183
184 /* Stream list.  */
185
186 typedef struct estream_list *estream_list_t;
187
188 struct estream_list
189 {
190   estream_t car;
191   estream_list_t cdr;
192   estream_list_t *prev_cdr;
193 };
194
195 static estream_list_t estream_list;
196 #ifdef HAVE_PTH
197 /* Note that we can't use a static initialization with W32Pth; however
198    W32Pth does an implicit initialization anyway.  */
199 static estream_mutex_t estream_list_lock
200 # ifndef _W32_PTH_H
201          = ESTREAM_MUTEX_INITIALIZER
202 # endif
203          ;
204 #endif
205
206 #define ESTREAM_LIST_LOCK   ESTREAM_MUTEX_LOCK   (estream_list_lock)
207 #define ESTREAM_LIST_UNLOCK ESTREAM_MUTEX_UNLOCK (estream_list_lock)
208
209 #ifndef EOPNOTSUPP
210 # define EOPNOTSUPP ENOSYS
211 #endif
212
213
214 \f
215
216 /* Macros.  */
217
218 /* Calculate array dimension.  */
219 #ifndef DIM
220 #define DIM(array) (sizeof (array) / sizeof (*array))
221 #endif
222
223 /* Evaluate EXPRESSION, setting VARIABLE to the return code, if
224    VARIABLE is zero.  */
225 #define SET_UNLESS_NONZERO(variable, tmp_variable, expression) \
226   do                                                           \
227     {                                                          \
228       tmp_variable = expression;                               \
229       if ((! variable) && tmp_variable)                        \
230         variable = tmp_variable;                               \
231     }                                                          \
232   while (0)
233
234 /*
235  * List manipulation.
236  */
237
238 /* Add STREAM to the list of registered stream objects.  */
239 static int
240 es_list_add (estream_t stream)
241 {
242   estream_list_t list_obj;
243   int ret;
244
245   list_obj = ES_MEM_ALLOC (sizeof (*list_obj));
246   if (! list_obj)
247     ret = -1;
248   else
249     {
250       ESTREAM_LIST_LOCK;
251       list_obj->car = stream;
252       list_obj->cdr = estream_list;
253       list_obj->prev_cdr = &estream_list;
254       if (estream_list)
255         estream_list->prev_cdr = &list_obj->cdr;
256       estream_list = list_obj;
257       ESTREAM_LIST_UNLOCK;
258       ret = 0;
259     }
260
261   return ret;
262 }
263
264 /* Remove STREAM from the list of registered stream objects.  */
265 static void
266 es_list_remove (estream_t stream)
267 {
268   estream_list_t list_obj;
269   
270   ESTREAM_LIST_LOCK;
271   for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
272     if (list_obj->car == stream)
273       {
274         *list_obj->prev_cdr = list_obj->cdr;
275         if (list_obj->cdr)
276           list_obj->cdr->prev_cdr = list_obj->prev_cdr;
277         ES_MEM_FREE (list_obj);
278         break;
279       }
280   ESTREAM_LIST_UNLOCK;
281 }
282
283 /* Type of an stream-iterator-function.  */
284 typedef int (*estream_iterator_t) (estream_t stream);
285
286 /* Iterate over list of registered streams, calling ITERATOR for each
287    of them.  */
288 static int
289 es_list_iterate (estream_iterator_t iterator)
290 {
291   estream_list_t list_obj;
292   int ret = 0;
293
294   ESTREAM_LIST_LOCK;
295   for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
296     ret |= (*iterator) (list_obj->car);
297   ESTREAM_LIST_UNLOCK;
298
299   return ret;
300 }
301
302 \f
303
304 /*
305  * Initialization.
306  */
307
308 static int
309 es_init_do (void)
310 {
311   int err;
312
313   err = ESTREAM_THREADING_INIT ();
314
315   return err;
316 }
317
318 \f
319
320 /*
321  * I/O methods.
322  */
323
324 /* Implementation of Memory I/O.  */
325
326 /* Cookie for memory objects.  */
327 typedef struct estream_cookie_mem
328 {
329   unsigned int flags;           /* Open flags.  */
330   unsigned char *memory;        /* Data.  */
331   size_t memory_size;           /* Size of MEMORY.  */
332   size_t offset;                /* Current offset in MEMORY.  */
333   size_t data_len;              /* Length of data in MEMORY.  */
334   size_t block_size;            /* Block size.  */
335   unsigned int grow: 1;         /* MEMORY is allowed to grow.  */
336   unsigned int append_zero: 1;  /* Append zero after data.  */
337   unsigned int dont_free: 1;    /* Append zero after data.  */
338   char **ptr;
339   size_t *size;
340   func_realloc_t func_realloc;
341   func_free_t func_free;
342 } *estream_cookie_mem_t;
343
344 /* Create function for memory objects.  */
345 static int
346 es_func_mem_create (void *ES__RESTRICT *ES__RESTRICT cookie,
347                     unsigned char *ES__RESTRICT data, size_t data_n,
348                     size_t data_len,
349                     size_t block_size, unsigned int grow,
350                     unsigned int append_zero, unsigned int dont_free,
351                     char **ptr, size_t *size,
352                     func_realloc_t func_realloc, func_free_t func_free,
353                     unsigned int flags)
354 {
355   estream_cookie_mem_t mem_cookie;
356   int err;
357
358   mem_cookie = ES_MEM_ALLOC (sizeof (*mem_cookie));
359   if (! mem_cookie)
360     err = -1;
361   else
362     {
363       mem_cookie->flags = flags;
364       mem_cookie->memory = data;
365       mem_cookie->memory_size = data_n;
366       mem_cookie->offset = 0;
367       mem_cookie->data_len = data_len;
368       mem_cookie->block_size = block_size;
369       mem_cookie->grow = grow ? 1 : 0;
370       mem_cookie->append_zero = append_zero ? 1 : 0;
371       mem_cookie->dont_free = dont_free ? 1 : 0;
372       mem_cookie->ptr = ptr;
373       mem_cookie->size = size;
374       mem_cookie->func_realloc = func_realloc ? func_realloc : ES_MEM_REALLOC;
375       mem_cookie->func_free = func_free ? func_free : ES_MEM_FREE;
376       mem_cookie->offset = 0;
377       *cookie = mem_cookie;
378       err = 0;
379     }
380
381   return err;
382 }
383
384 /* Read function for memory objects.  */
385 static ssize_t
386 es_func_mem_read (void *cookie, void *buffer, size_t size)
387 {
388   estream_cookie_mem_t mem_cookie = cookie;
389   ssize_t ret;
390
391   if (size > mem_cookie->data_len - mem_cookie->offset)
392     size = mem_cookie->data_len - mem_cookie->offset;
393
394   if (size)
395     {
396       memcpy (buffer, mem_cookie->memory + mem_cookie->offset, size);
397       mem_cookie->offset += size;
398     }
399   
400   ret = size;
401
402   return ret;
403 }
404
405 /* Write function for memory objects.  */
406 static ssize_t
407 es_func_mem_write (void *cookie, const void *buffer, size_t size)
408 {
409   estream_cookie_mem_t mem_cookie = cookie;
410   func_realloc_t func_realloc = mem_cookie->func_realloc;
411   unsigned char *memory_new;
412   size_t newsize;
413   ssize_t ret;
414   int err;
415
416   if (size)
417     {
418       /* Regular write.  */
419
420       if (mem_cookie->flags & O_APPEND)
421         /* Append to data.  */
422         mem_cookie->offset = mem_cookie->data_len;
423           
424       if (! mem_cookie->grow)
425         if (size > mem_cookie->memory_size - mem_cookie->offset)
426           size = mem_cookie->memory_size - mem_cookie->offset;
427
428       err = 0;
429
430       while (size > (mem_cookie->memory_size - mem_cookie->offset))
431         {
432           memory_new = (*func_realloc) (mem_cookie->memory,
433                                         mem_cookie->memory_size
434                                         + mem_cookie->block_size);
435           if (! memory_new)
436             {
437               err = -1;
438               break;
439             }
440           else
441             {
442               if (mem_cookie->memory != memory_new)
443                 mem_cookie->memory = memory_new;
444               mem_cookie->memory_size += mem_cookie->block_size;
445             }
446         }
447       if (err)
448         goto out;
449
450       if (size)
451         {
452           memcpy (mem_cookie->memory + mem_cookie->offset, buffer, size);
453           if (mem_cookie->offset + size > mem_cookie->data_len)
454             mem_cookie->data_len = mem_cookie->offset + size;
455           mem_cookie->offset += size;
456         }
457     }
458   else
459     {
460       /* Flush.  */
461
462       err = 0;
463       if (mem_cookie->append_zero)
464         {
465           if (mem_cookie->data_len >= mem_cookie->memory_size)
466             {
467               newsize = BUFFER_ROUND_TO_BLOCK (mem_cookie->data_len + 1,
468                                                mem_cookie->block_size)
469                 * mem_cookie->block_size;
470           
471               memory_new = (*func_realloc) (mem_cookie->memory, newsize);
472               if (! memory_new)
473                 {
474                   err = -1;
475                   goto out;
476                 }
477
478               if (mem_cookie->memory != memory_new)
479                 mem_cookie->memory = memory_new;
480               mem_cookie->memory_size = newsize;
481             }
482
483           mem_cookie->memory[mem_cookie->data_len + 1] = 0;
484         }
485
486       /* Return information to user if necessary.  */
487       if (mem_cookie->ptr)
488         *mem_cookie->ptr = (char *) mem_cookie->memory;
489       if (mem_cookie->size)
490         *mem_cookie->size = mem_cookie->data_len;
491     }
492
493  out:
494
495   if (err)
496     ret = -1;
497   else
498     ret = size;
499
500   return ret;
501 }
502
503 /* Seek function for memory objects.  */
504 static int
505 es_func_mem_seek (void *cookie, off_t *offset, int whence)
506 {
507   estream_cookie_mem_t mem_cookie = cookie;
508   off_t pos_new;
509   int err = 0;
510
511   switch (whence)
512     {
513     case SEEK_SET:
514       pos_new = *offset;
515       break;
516
517     case SEEK_CUR:
518       pos_new = mem_cookie->offset += *offset;
519       break;
520
521     case SEEK_END:
522       pos_new = mem_cookie->data_len += *offset;
523       break;
524
525     default:
526       /* Never reached.  */
527       pos_new = 0;
528     }
529
530   if (pos_new > mem_cookie->memory_size)
531     {
532       /* Grow buffer if possible.  */
533
534       if (mem_cookie->grow)
535         {
536           func_realloc_t func_realloc = mem_cookie->func_realloc;
537           size_t newsize;
538           void *p;
539
540           newsize = BUFFER_ROUND_TO_BLOCK (pos_new, mem_cookie->block_size);
541           p = (*func_realloc) (mem_cookie->memory, newsize);
542           if (! p)
543             {
544               err = -1;
545               goto out;
546             }
547           else
548             {
549               if (mem_cookie->memory != p)
550                 mem_cookie->memory = p;
551               mem_cookie->memory_size = newsize;
552             }
553         }
554       else
555         {
556           errno = EINVAL;
557           err = -1;
558           goto out;
559         }
560     }
561
562   if (pos_new > mem_cookie->data_len)
563     /* Fill spare space with zeroes.  */
564     memset (mem_cookie->memory + mem_cookie->data_len,
565             0, pos_new - mem_cookie->data_len);
566
567   mem_cookie->offset = pos_new;
568   *offset = pos_new;
569
570  out:
571
572   return err;
573 }
574
575 /* Destroy function for memory objects.  */
576 static int
577 es_func_mem_destroy (void *cookie)
578 {
579   estream_cookie_mem_t mem_cookie = cookie;
580   func_free_t func_free = mem_cookie->func_free;
581
582   if (! mem_cookie->dont_free)
583     (*func_free) (mem_cookie->memory);
584   ES_MEM_FREE (mem_cookie);
585
586   return 0;
587 }
588
589 static es_cookie_io_functions_t estream_functions_mem =
590   {
591     es_func_mem_read,
592     es_func_mem_write,
593     es_func_mem_seek,
594     es_func_mem_destroy
595   };
596
597 /* Implementation of fd I/O.  */
598
599 /* Cookie for fd objects.  */
600 typedef struct estream_cookie_fd
601 {
602   int fd;
603 } *estream_cookie_fd_t;
604
605 /* Create function for fd objects.  */
606 static int
607 es_func_fd_create (void **cookie, int fd, unsigned int flags)
608 {
609   estream_cookie_fd_t fd_cookie;
610   int err;
611
612   fd_cookie = ES_MEM_ALLOC (sizeof (*fd_cookie));
613   if (! fd_cookie)
614     err = -1;
615   else
616     {
617       fd_cookie->fd = fd;
618       *cookie = fd_cookie;
619       err = 0;
620     }
621   
622   return err;
623 }
624
625 /* Read function for fd objects.  */
626 static ssize_t
627 es_func_fd_read (void *cookie, void *buffer, size_t size)
628
629 {
630   estream_cookie_fd_t file_cookie = cookie;
631   ssize_t bytes_read;
632
633   do 
634     bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size);
635   while (bytes_read == -1 && errno == EINTR);
636
637   return bytes_read;
638 }
639
640 /* Write function for fd objects.  */
641 static ssize_t
642 es_func_fd_write (void *cookie, const void *buffer, size_t size)
643                            
644 {
645   estream_cookie_fd_t file_cookie = cookie;
646   ssize_t bytes_written;
647
648   do
649     bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size);
650   while (bytes_written == -1 && errno == EINTR);
651
652   return bytes_written;
653 }
654
655 /* Seek function for fd objects.  */
656 static int
657 es_func_fd_seek (void *cookie, off_t *offset, int whence)
658 {
659   estream_cookie_fd_t file_cookie = cookie;
660   off_t offset_new;
661   int err;
662
663   offset_new = lseek (file_cookie->fd, *offset, whence);
664   if (offset_new == -1)
665     err = -1;
666   else
667     {
668       *offset = offset_new;
669       err = 0;
670     }
671
672   return err;
673 }
674
675 /* Destroy function for fd objects.  */
676 static int
677 es_func_fd_destroy (void *cookie)
678 {
679   estream_cookie_fd_t fd_cookie = cookie;
680   int err;
681
682   if (fd_cookie)
683     {
684       err = close (fd_cookie->fd);
685       ES_MEM_FREE (fd_cookie);
686     }
687   else
688     err = 0;
689
690   return err;
691 }
692
693 static es_cookie_io_functions_t estream_functions_fd =
694   {
695     es_func_fd_read,
696     es_func_fd_write,
697     es_func_fd_seek,
698     es_func_fd_destroy
699   };
700
701 /* Implementation of file I/O.  */
702
703 /* Create function for file objects.  */
704 static int
705 es_func_file_create (void **cookie, int *filedes,
706                      const char *path, unsigned int flags)
707 {
708   estream_cookie_fd_t file_cookie;
709   int err;
710   int fd;
711
712   err = 0;
713   fd = -1;
714
715   file_cookie = ES_MEM_ALLOC (sizeof (*file_cookie));
716   if (! file_cookie)
717     {
718       err = -1;
719       goto out;
720     }
721
722   fd = open (path, flags, ES_DEFAULT_OPEN_MODE);
723   if (fd == -1)
724     {
725       err = -1;
726       goto out;
727     }
728
729   file_cookie->fd = fd;
730   *cookie = file_cookie;
731   *filedes = fd;
732
733  out:
734
735   if (err)
736     ES_MEM_FREE (file_cookie);
737
738   return err;
739 }
740
741 static es_cookie_io_functions_t estream_functions_file =
742   {
743     es_func_fd_read,
744     es_func_fd_write,
745     es_func_fd_seek,
746     es_func_fd_destroy
747   };
748
749 \f
750
751 /* Stream primitives.  */
752
753 static int
754 es_convert_mode (const char *mode, unsigned int *flags)
755 {
756
757   /* FIXME: We need to allow all mode flags permutations and for
758      binary mode we need to do a
759
760      #ifdef HAVE_DOSISH_SYSTEM
761        setmode (fd, O_BINARY);
762      #endif
763   */
764   struct
765   {
766     const char *mode;
767     unsigned int flags;
768   } mode_flags[] = { { "r",
769                        O_RDONLY },
770                      { "rb",
771                        O_RDONLY },
772                      { "w",
773                        O_WRONLY | O_TRUNC | O_CREAT },
774                      { "wb",
775                        O_WRONLY | O_TRUNC | O_CREAT },
776                      { "a",
777                        O_WRONLY | O_APPEND | O_CREAT },
778                      { "ab",
779                        O_WRONLY | O_APPEND | O_CREAT },
780                      { "r+",
781                        O_RDWR },
782                      { "rb+",
783                        O_RDWR },
784                      { "r+b",
785                        O_RDONLY | O_WRONLY },
786                      { "w+",
787                        O_RDWR | O_TRUNC | O_CREAT },
788                      { "wb+",
789                        O_RDWR | O_TRUNC | O_CREAT },
790                      { "w+b",
791                        O_RDWR | O_TRUNC | O_CREAT },
792                      { "a+",
793                        O_RDWR | O_CREAT | O_APPEND },
794                      { "ab+",
795                        O_RDWR | O_CREAT | O_APPEND },
796                      { "a+b",
797                        O_RDWR | O_CREAT | O_APPEND } };
798   unsigned int i;
799   int err; 
800
801   for (i = 0; i < DIM (mode_flags); i++)
802     if (! strcmp (mode_flags[i].mode, mode))
803       break;
804   if (i == DIM (mode_flags))
805     {
806       errno = EINVAL;
807       err = -1;
808     }
809   else
810     {
811       err = 0;
812       *flags = mode_flags[i].flags;
813     }
814
815   return err;
816 }
817
818 \f
819
820 /*
821  * Low level stream functionality.
822  */
823
824 static int
825 es_fill (estream_t stream)
826 {
827   size_t bytes_read = 0;
828   int err;
829
830   if (!stream->intern->func_read)
831     {
832       errno = EOPNOTSUPP;
833       err = -1;
834     }
835   else
836     {
837       es_cookie_read_function_t func_read = stream->intern->func_read;
838       ssize_t ret;
839
840       ret = (*func_read) (stream->intern->cookie,
841                           stream->buffer, stream->buffer_size);
842       if (ret == -1)
843         {
844           bytes_read = 0;
845           err = -1;
846         }
847       else
848         {
849           bytes_read = ret;
850           err = 0;
851         }
852     }
853
854   if (err)
855     stream->intern->indicators.err = 1;
856   else if (!bytes_read)
857     stream->intern->indicators.eof = 1;
858
859   stream->intern->offset += stream->data_len;
860   stream->data_len = bytes_read;
861   stream->data_offset = 0;
862
863   return err;
864 }
865
866 static int
867 es_flush (estream_t stream)
868 {
869   es_cookie_write_function_t func_write = stream->intern->func_write;
870   int err;
871
872   assert (stream->flags & ES_FLAG_WRITING);
873
874   if (stream->data_offset)
875     {
876       size_t bytes_written;
877       size_t data_flushed;
878       ssize_t ret;
879
880       if (! func_write)
881         {
882           err = EOPNOTSUPP;
883           goto out;
884         }
885
886       /* Note: to prevent an endless loop caused by user-provided
887          write-functions that pretend to have written more bytes than
888          they were asked to write, we have to check for
889          "(stream->data_offset - data_flushed) > 0" instead of
890          "stream->data_offset - data_flushed".  */
891       
892       data_flushed = 0;
893       err = 0;
894       
895       while ((((ssize_t) (stream->data_offset - data_flushed)) > 0) && (! err))
896         {
897           ret = (*func_write) (stream->intern->cookie,
898                                stream->buffer + data_flushed,
899                                stream->data_offset - data_flushed);
900           if (ret == -1)
901             {
902               bytes_written = 0;
903               err = -1;
904             }
905           else
906             bytes_written = ret;
907
908           data_flushed += bytes_written;
909           if (err)
910             break;
911         }
912
913       stream->data_flushed += data_flushed;
914       if (stream->data_offset == data_flushed)
915         {
916           stream->intern->offset += stream->data_offset;
917           stream->data_offset = 0;
918           stream->data_flushed = 0;
919
920           /* Propagate flush event.  */
921           (*func_write) (stream->intern->cookie, NULL, 0);
922         }
923     }
924   else
925     err = 0;
926
927  out:
928     
929   if (err)
930     stream->intern->indicators.err = 1;
931
932   return err;
933 }
934
935 /* Discard buffered data for STREAM.  */
936 static void
937 es_empty (estream_t stream)
938 {
939   assert (! (stream->flags & ES_FLAG_WRITING));
940   stream->data_len = 0;
941   stream->data_offset = 0;
942   stream->unread_data_len = 0;
943 }
944
945 /* Initialize STREAM.  */
946 static void
947 es_initialize (estream_t stream,
948                void *cookie, int fd, es_cookie_io_functions_t functions)
949 {
950   stream->intern->cookie = cookie;
951   stream->intern->opaque = NULL;
952   stream->intern->offset = 0;
953   stream->intern->func_read = functions.func_read;
954   stream->intern->func_write = functions.func_write;
955   stream->intern->func_seek = functions.func_seek;
956   stream->intern->func_close = functions.func_close;
957   stream->intern->strategy = _IOFBF;
958   stream->intern->fd = fd;
959   stream->intern->print_err = 0;
960   stream->intern->print_errno = 0;
961   stream->intern->print_ntotal = 0;
962   stream->intern->print_fp = NULL;
963   stream->intern->indicators.err = 0;
964   stream->intern->indicators.eof = 0;
965   stream->intern->deallocate_buffer = 0;
966
967   stream->data_len = 0;
968   stream->data_offset = 0;
969   stream->data_flushed = 0;
970   stream->unread_data_len = 0;
971   stream->flags = 0;
972 }
973
974 /* Deinitialize STREAM.  */
975 static int
976 es_deinitialize (estream_t stream)
977 {
978   es_cookie_close_function_t func_close;
979   int err, tmp_err;
980
981   if (stream->intern->print_fp)
982     {
983       int save_errno = errno;
984       fclose (stream->intern->print_fp);
985       stream->intern->print_fp = NULL;
986       errno = save_errno;
987     }
988
989   func_close = stream->intern->func_close;
990
991   err = 0;
992   if (stream->flags & ES_FLAG_WRITING)
993     SET_UNLESS_NONZERO (err, tmp_err, es_flush (stream));
994   if (func_close)
995     SET_UNLESS_NONZERO (err, tmp_err, (*func_close) (stream->intern->cookie));
996
997   
998   return err;
999 }
1000
1001 /* Create a new stream object, initialize it.  */
1002 static int
1003 es_create (estream_t *stream, void *cookie, int fd,
1004            es_cookie_io_functions_t functions)
1005 {
1006   estream_internal_t stream_internal_new;
1007   estream_t stream_new;
1008   int err;
1009
1010   stream_new = NULL;
1011   stream_internal_new = NULL;
1012
1013   stream_new = ES_MEM_ALLOC (sizeof (*stream_new));
1014   if (! stream_new)
1015     {
1016       err = -1;
1017       goto out;
1018     }
1019
1020   stream_internal_new = ES_MEM_ALLOC (sizeof (*stream_internal_new));
1021   if (! stream_internal_new)
1022     {
1023       err = -1;
1024       goto out;
1025     }
1026
1027   stream_new->buffer = stream_internal_new->buffer;
1028   stream_new->buffer_size = sizeof (stream_internal_new->buffer);
1029   stream_new->unread_buffer = stream_internal_new->unread_buffer;
1030   stream_new->unread_buffer_size = sizeof (stream_internal_new->unread_buffer);
1031   stream_new->intern = stream_internal_new;
1032
1033   ESTREAM_MUTEX_INITIALIZE (stream_new->intern->lock);
1034   es_initialize (stream_new, cookie, fd, functions);
1035
1036   err = es_list_add (stream_new);
1037   if (err)
1038     goto out;
1039
1040   *stream = stream_new;
1041
1042  out:
1043
1044   if (err)
1045     {
1046       if (stream_new)
1047         {
1048           es_deinitialize (stream_new);
1049           ES_MEM_FREE (stream_new);
1050         }
1051     }
1052
1053   return err;
1054 }
1055
1056 /* Deinitialize a stream object and destroy it.  */
1057 static int
1058 es_destroy (estream_t stream)
1059 {
1060   int err = 0;
1061
1062   if (stream)
1063     {
1064       es_list_remove (stream);
1065       err = es_deinitialize (stream);
1066       ES_MEM_FREE (stream->intern);
1067       ES_MEM_FREE (stream);
1068     }
1069
1070   return err;
1071 }
1072
1073 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1074    unbuffered-mode, storing the amount of bytes read in
1075    *BYTES_READ.  */
1076 static int
1077 es_read_nbf (estream_t ES__RESTRICT stream,
1078              unsigned char *ES__RESTRICT buffer,
1079              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1080 {
1081   es_cookie_read_function_t func_read = stream->intern->func_read;
1082   size_t data_read;
1083   ssize_t ret;
1084   int err;
1085
1086   data_read = 0;
1087   err = 0;
1088
1089   while (bytes_to_read - data_read)
1090     {
1091       ret = (*func_read) (stream->intern->cookie,
1092                           buffer + data_read, bytes_to_read - data_read);
1093       if (ret == -1)
1094         {
1095           err = -1;
1096           break;
1097         }
1098       else if (ret)
1099         data_read += ret;
1100       else
1101         break;
1102     }
1103
1104   stream->intern->offset += data_read;
1105   *bytes_read = data_read;
1106
1107   return err;
1108 }
1109
1110 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1111    fully-buffered-mode, storing the amount of bytes read in
1112    *BYTES_READ.  */
1113 static int
1114 es_read_fbf (estream_t ES__RESTRICT stream,
1115              unsigned char *ES__RESTRICT buffer,
1116              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1117 {
1118   size_t data_available;
1119   size_t data_to_read;
1120   size_t data_read;
1121   int err;
1122
1123   data_read = 0;
1124   err = 0;
1125
1126   while ((bytes_to_read - data_read) && (! err))
1127     {
1128       if (stream->data_offset == stream->data_len)
1129         {
1130           /* Nothing more to read in current container, try to
1131              fill container with new data.  */
1132           err = es_fill (stream);
1133           if (! err)
1134             if (! stream->data_len)
1135               /* Filling did not result in any data read.  */
1136               break;
1137         }
1138
1139       if (! err)
1140         {
1141           /* Filling resulted in some new data.  */
1142
1143           data_to_read = bytes_to_read - data_read;
1144           data_available = stream->data_len - stream->data_offset;
1145           if (data_to_read > data_available)
1146             data_to_read = data_available;
1147
1148           memcpy (buffer + data_read,
1149                   stream->buffer + stream->data_offset, data_to_read);
1150           stream->data_offset += data_to_read;
1151           data_read += data_to_read;
1152         }
1153     }
1154
1155   *bytes_read = data_read;
1156
1157   return err;
1158 }
1159
1160 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
1161    line-buffered-mode, storing the amount of bytes read in
1162    *BYTES_READ.  */
1163 static int
1164 es_read_lbf (estream_t ES__RESTRICT stream,
1165              unsigned char *ES__RESTRICT buffer,
1166              size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1167 {
1168   int err;
1169
1170   err = es_read_fbf (stream, buffer, bytes_to_read, bytes_read);
1171
1172   return err;
1173 }
1174
1175 /* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER, storing
1176    *the amount of bytes read in BYTES_READ.  */
1177 static int
1178 es_readn (estream_t ES__RESTRICT stream,
1179           void *ES__RESTRICT buffer_arg,
1180           size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
1181 {
1182   unsigned char *buffer = (unsigned char *)buffer_arg;
1183   size_t data_read_unread, data_read;
1184   int err;
1185
1186   data_read_unread = 0;
1187   data_read = 0;
1188   err = 0;
1189
1190   if (stream->flags & ES_FLAG_WRITING)
1191     {
1192       /* Switching to reading mode -> flush output.  */
1193       err = es_flush (stream);
1194       if (err)
1195         goto out;
1196       stream->flags &= ~ES_FLAG_WRITING;
1197     }  
1198
1199   /* Read unread data first.  */
1200   while ((bytes_to_read - data_read_unread) && stream->unread_data_len)
1201     {
1202       buffer[data_read_unread]
1203         = stream->unread_buffer[stream->unread_data_len - 1];
1204       stream->unread_data_len--;
1205       data_read_unread++;
1206     }
1207
1208   switch (stream->intern->strategy)
1209     {
1210     case _IONBF:
1211       err = es_read_nbf (stream,
1212                          buffer + data_read_unread,
1213                          bytes_to_read - data_read_unread, &data_read);
1214       break;
1215     case _IOLBF:
1216       err = es_read_lbf (stream,
1217                          buffer + data_read_unread,
1218                          bytes_to_read - data_read_unread, &data_read);
1219       break;
1220     case _IOFBF:
1221       err = es_read_fbf (stream,
1222                          buffer + data_read_unread,
1223                          bytes_to_read - data_read_unread, &data_read);
1224       break;
1225     }
1226
1227  out:
1228
1229   if (bytes_read)
1230     *bytes_read = data_read_unread + data_read;
1231
1232   return err;
1233 }
1234
1235 /* Try to unread DATA_N bytes from DATA into STREAM, storing the
1236    amount of bytes succesfully unread in *BYTES_UNREAD.  */
1237 static void
1238 es_unreadn (estream_t ES__RESTRICT stream,
1239             const unsigned char *ES__RESTRICT data, size_t data_n,
1240             size_t *ES__RESTRICT bytes_unread)
1241 {
1242   size_t space_left;
1243
1244   space_left = stream->unread_buffer_size - stream->unread_data_len;
1245
1246   if (data_n > space_left)
1247     data_n = space_left;
1248
1249   if (! data_n)
1250     goto out;
1251
1252   memcpy (stream->unread_buffer + stream->unread_data_len, data, data_n);
1253   stream->unread_data_len += data_n;
1254   stream->intern->indicators.eof = 0;
1255
1256  out:
1257
1258   if (bytes_unread)
1259     *bytes_unread = data_n;
1260 }
1261
1262 /* Seek in STREAM.  */
1263 static int
1264 es_seek (estream_t ES__RESTRICT stream, off_t offset, int whence,
1265          off_t *ES__RESTRICT offset_new)
1266 {
1267   es_cookie_seek_function_t func_seek = stream->intern->func_seek;
1268   int err, ret;
1269   off_t off;
1270
1271   if (! func_seek)
1272     {
1273       errno = EOPNOTSUPP;
1274       err = -1;
1275       goto out;
1276     }
1277
1278   if (stream->flags & ES_FLAG_WRITING)
1279     {
1280       /* Flush data first in order to prevent flushing it to the wrong
1281          offset.  */
1282       err = es_flush (stream);
1283       if (err)
1284         goto out;
1285       stream->flags &= ~ES_FLAG_WRITING;
1286     }
1287
1288   off = offset;
1289   if (whence == SEEK_CUR)
1290     {
1291       off = off - stream->data_len + stream->data_offset;
1292       off -= stream->unread_data_len;
1293     }
1294   
1295   ret = (*func_seek) (stream->intern->cookie, &off, whence);
1296   if (ret == -1)
1297     {
1298       err = -1;
1299       goto out;
1300     }
1301
1302   err = 0;
1303   es_empty (stream);
1304
1305   if (offset_new)
1306     *offset_new = off;
1307
1308   stream->intern->indicators.eof = 0;
1309   stream->intern->offset = off;
1310
1311  out:
1312   
1313   if (err)
1314     stream->intern->indicators.err = 1;
1315
1316   return err;
1317 }
1318
1319 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
1320    unbuffered-mode, storing the amount of bytes written in
1321    *BYTES_WRITTEN.  */
1322 static int
1323 es_write_nbf (estream_t ES__RESTRICT stream,
1324               const unsigned char *ES__RESTRICT buffer,
1325               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
1326 {
1327   es_cookie_write_function_t func_write = stream->intern->func_write;
1328   size_t data_written;
1329   ssize_t ret;
1330   int err;
1331
1332   if (bytes_to_write && (! func_write))
1333     {
1334       err = EOPNOTSUPP;
1335       goto out;
1336     }  
1337
1338   data_written = 0;
1339   err = 0;
1340   
1341   while (bytes_to_write - data_written)
1342     {
1343       ret = (*func_write) (stream->intern->cookie,
1344                            buffer + data_written,
1345                            bytes_to_write - data_written);
1346       if (ret == -1)
1347         {
1348           err = -1;
1349           break;
1350         }
1351       else
1352         data_written += ret;
1353     }
1354
1355   stream->intern->offset += data_written;
1356   *bytes_written = data_written;
1357
1358  out:
1359
1360   return err;
1361 }
1362
1363 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
1364    fully-buffered-mode, storing the amount of bytes written in
1365    *BYTES_WRITTEN.  */
1366 static int
1367 es_write_fbf (estream_t ES__RESTRICT stream,
1368               const unsigned char *ES__RESTRICT buffer,
1369               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
1370 {
1371   size_t space_available;
1372   size_t data_to_write;
1373   size_t data_written;
1374   int err;
1375
1376   data_written = 0;
1377   err = 0;
1378
1379   while ((bytes_to_write - data_written) && (! err))
1380     {
1381       if (stream->data_offset == stream->buffer_size)
1382         /* Container full, flush buffer.  */
1383         err = es_flush (stream);
1384
1385       if (! err)
1386         {
1387           /* Flushing resulted in empty container.  */
1388           
1389           data_to_write = bytes_to_write - data_written;
1390           space_available = stream->buffer_size - stream->data_offset;
1391           if (data_to_write > space_available)
1392             data_to_write = space_available;
1393               
1394           memcpy (stream->buffer + stream->data_offset,
1395                   buffer + data_written, data_to_write);
1396           stream->data_offset += data_to_write;
1397           data_written += data_to_write;
1398         }
1399     }
1400
1401   *bytes_written = data_written;
1402
1403   return err;
1404 }
1405
1406
1407 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
1408    line-buffered-mode, storing the amount of bytes written in
1409    *BYTES_WRITTEN.  */
1410 static int
1411 es_write_lbf (estream_t ES__RESTRICT stream,
1412               const unsigned char *ES__RESTRICT buffer,
1413               size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
1414 {
1415   size_t data_flushed = 0;
1416   size_t data_buffered = 0;
1417   unsigned char *nlp;
1418   int err = 0;
1419
1420   nlp = memrchr (buffer, '\n', bytes_to_write);
1421   if (nlp)
1422     {
1423       /* Found a newline, directly write up to (including) this
1424          character.  */
1425       err = es_flush (stream);
1426       if (!err)
1427         err = es_write_nbf (stream, buffer, nlp - buffer + 1, &data_flushed);
1428     }
1429
1430   if (!err)
1431     {
1432       /* Write remaining data fully buffered.  */
1433       err = es_write_fbf (stream, buffer + data_flushed,
1434                           bytes_to_write - data_flushed, &data_buffered);
1435     }
1436
1437   *bytes_written = data_flushed + data_buffered;
1438   return err;
1439 }
1440
1441
1442 /* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in, storing the
1443    amount of bytes written in BYTES_WRITTEN.  */
1444 static int
1445 es_writen (estream_t ES__RESTRICT stream,
1446            const void *ES__RESTRICT buffer,
1447            size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
1448 {
1449   size_t data_written;
1450   int err;
1451
1452   data_written = 0;
1453   err = 0;
1454   
1455   if (! (stream->flags & ES_FLAG_WRITING))
1456     {
1457       /* Switching to writing mode -> discard input data and seek to
1458          position at which reading has stopped.  We can do this only
1459          if a seek function has been registered. */
1460       if (stream->intern->func_seek)
1461         {
1462           err = es_seek (stream, 0, SEEK_CUR, NULL);
1463           if (err)
1464             {
1465               if (errno == ESPIPE)
1466                 err = 0;
1467               else
1468                 goto out;
1469             }
1470         }
1471     }
1472
1473   switch (stream->intern->strategy)
1474     {
1475     case _IONBF:
1476       err = es_write_nbf (stream, buffer, bytes_to_write, &data_written);
1477       break;
1478
1479     case _IOLBF:
1480       err = es_write_lbf (stream, buffer, bytes_to_write, &data_written);
1481       break;
1482
1483     case _IOFBF:
1484       err = es_write_fbf (stream, buffer, bytes_to_write, &data_written);
1485       break;
1486     }
1487
1488  out:
1489     
1490   if (bytes_written)
1491     *bytes_written = data_written;
1492   if (data_written)
1493     if (! (stream->flags & ES_FLAG_WRITING))
1494       stream->flags |= ES_FLAG_WRITING;
1495
1496   return err;
1497 }
1498
1499
1500 static int
1501 es_peek (estream_t ES__RESTRICT stream, unsigned char **ES__RESTRICT data,
1502          size_t *ES__RESTRICT data_len)
1503 {
1504   int err;
1505
1506   if (stream->flags & ES_FLAG_WRITING)
1507     {
1508       /* Switching to reading mode -> flush output.  */
1509       err = es_flush (stream);
1510       if (err)
1511         goto out;
1512       stream->flags &= ~ES_FLAG_WRITING;
1513     }  
1514
1515   if (stream->data_offset == stream->data_len)
1516     {
1517       /* Refill container.  */
1518       err = es_fill (stream);
1519       if (err)
1520         goto out;
1521     }
1522   
1523   if (data)
1524     *data = stream->buffer + stream->data_offset;
1525   if (data_len)
1526     *data_len = stream->data_len - stream->data_offset;
1527   err = 0;
1528
1529  out:
1530
1531   return err;
1532 }
1533
1534
1535 /* Skip SIZE bytes of input data contained in buffer.  */
1536 static int
1537 es_skip (estream_t stream, size_t size)
1538 {
1539   int err;
1540
1541   if (stream->data_offset + size > stream->data_len)
1542     {
1543       errno = EINVAL;
1544       err = -1;
1545     }
1546   else
1547     {
1548       stream->data_offset += size;
1549       err = 0;
1550     }
1551
1552   return err;
1553 }
1554
1555
1556 static int
1557 doreadline (estream_t ES__RESTRICT stream, size_t max_length,
1558              char *ES__RESTRICT *ES__RESTRICT line,
1559              size_t *ES__RESTRICT line_length)
1560 {
1561   size_t space_left;
1562   size_t line_size;
1563   estream_t line_stream;
1564   char *line_new;
1565   void *line_stream_cookie;
1566   char *newline;
1567   unsigned char *data;
1568   size_t data_len;
1569   int err;
1570
1571   line_new = NULL;
1572   line_stream = NULL;
1573   line_stream_cookie = NULL;
1574
1575   err = es_func_mem_create (&line_stream_cookie, NULL, 0, 0, BUFFER_BLOCK_SIZE,
1576                             1, 0, 0, NULL, 0, ES_MEM_REALLOC, ES_MEM_FREE, O_RDWR);
1577   if (err)
1578     goto out;
1579
1580   err = es_create (&line_stream, line_stream_cookie, -1,
1581                    estream_functions_mem);
1582   if (err)
1583     goto out;
1584
1585   space_left = max_length;
1586   line_size = 0;
1587   while (1)
1588     {
1589       if (max_length && (space_left == 1))
1590         break;
1591
1592       err = es_peek (stream, &data, &data_len);
1593       if (err || (! data_len))
1594         break;
1595
1596       if (data_len > (space_left - 1))
1597         data_len = space_left - 1;
1598
1599       newline = memchr (data, '\n', data_len);
1600       if (newline)
1601         {
1602           data_len = (newline - (char *) data) + 1;
1603           err = es_write (line_stream, data, data_len, NULL);
1604           if (! err)
1605             {
1606               space_left -= data_len;
1607               line_size += data_len;
1608               es_skip (stream, data_len);
1609               break;
1610             }
1611         }
1612       else
1613         {
1614           err = es_write (line_stream, data, data_len, NULL);
1615           if (! err)
1616             {
1617               space_left -= data_len;
1618               line_size += data_len;
1619               es_skip (stream, data_len);
1620             }
1621         }
1622       if (err)
1623         break;
1624     }
1625   if (err)
1626     goto out;
1627
1628   /* Complete line has been written to line_stream.  */
1629   
1630   if ((max_length > 1) && (! line_size))
1631     {
1632       stream->intern->indicators.eof = 1;
1633       goto out;
1634     }
1635
1636   err = es_seek (line_stream, 0, SEEK_SET, NULL);
1637   if (err)
1638     goto out;
1639
1640   if (! *line)
1641     {
1642       line_new = ES_MEM_ALLOC (line_size + 1);
1643       if (! line_new)
1644         {
1645           err = -1;
1646           goto out;
1647         }
1648     }
1649   else
1650     line_new = *line;
1651
1652   err = es_read (line_stream, line_new, line_size, NULL);
1653   if (err)
1654     goto out;
1655
1656   line_new[line_size] = '\0';
1657
1658   if (! *line)
1659     *line = line_new;
1660   if (line_length)
1661     *line_length = line_size;
1662
1663  out:
1664
1665   if (line_stream)
1666     es_destroy (line_stream);
1667   else if (line_stream_cookie)
1668     es_func_mem_destroy (line_stream_cookie);
1669
1670   if (err)
1671     {
1672       if (! *line)
1673         ES_MEM_FREE (line_new);
1674       stream->intern->indicators.err = 1;
1675     }
1676
1677   return err;
1678 }
1679
1680
1681 /* Output fucntion used for estream_format.  */
1682 static int
1683 print_writer (void *outfncarg, const char *buf, size_t buflen)
1684 {
1685   estream_t stream = outfncarg;
1686   size_t nwritten;
1687   int rc;
1688
1689   nwritten = 0;
1690   rc = es_writen (stream, buf, buflen, &nwritten);
1691   stream->intern->print_ntotal += nwritten;
1692   return rc;
1693 }
1694
1695
1696 /* The core of our printf function.  This is called in locked state. */
1697 static int
1698 es_print (estream_t ES__RESTRICT stream,
1699           const char *ES__RESTRICT format, va_list ap)
1700 {
1701   int rc;
1702
1703   stream->intern->print_ntotal = 0;
1704   rc = estream_format (print_writer, stream, format, ap);
1705   if (rc)
1706     return -1;
1707   return (int)stream->intern->print_ntotal;
1708 }
1709
1710
1711 static void
1712 es_set_indicators (estream_t stream, int ind_err, int ind_eof)
1713 {
1714   if (ind_err != -1)
1715     stream->intern->indicators.err = ind_err ? 1 : 0;
1716   if (ind_eof != -1)
1717     stream->intern->indicators.eof = ind_eof ? 1 : 0;
1718 }
1719
1720
1721 static int
1722 es_get_indicator (estream_t stream, int ind_err, int ind_eof)
1723 {
1724   int ret = 0;
1725   
1726   if (ind_err)
1727     ret = stream->intern->indicators.err;
1728   else if (ind_eof)
1729     ret = stream->intern->indicators.eof;
1730
1731   return ret;
1732 }
1733
1734
1735 static int
1736 es_set_buffering (estream_t ES__RESTRICT stream,
1737                   char *ES__RESTRICT buffer, int mode, size_t size)
1738 {
1739   int err;
1740
1741   /* Flush or empty buffer depending on mode.  */
1742   if (stream->flags & ES_FLAG_WRITING)
1743     {
1744       err = es_flush (stream);
1745       if (err)
1746         goto out;
1747     }
1748   else
1749     es_empty (stream);
1750
1751   es_set_indicators (stream, -1, 0);
1752   
1753   /* Free old buffer in case that was allocated by this function.  */
1754   if (stream->intern->deallocate_buffer)
1755     {
1756       stream->intern->deallocate_buffer = 0;
1757       ES_MEM_FREE (stream->buffer);
1758       stream->buffer = NULL;
1759     }
1760
1761   if (mode == _IONBF)
1762     stream->buffer_size = 0;
1763   else
1764     {
1765       void *buffer_new;
1766       
1767       if (buffer)
1768         buffer_new = buffer;
1769       else
1770         {
1771           buffer_new = ES_MEM_ALLOC (size);
1772           if (! buffer_new)
1773             {
1774               err = -1;
1775               goto out;
1776             }
1777         }
1778
1779       stream->buffer = buffer_new;
1780       stream->buffer_size = size;
1781       if (! buffer)
1782         stream->intern->deallocate_buffer = 1;
1783     }
1784   stream->intern->strategy = mode;
1785   err = 0;
1786
1787  out:
1788
1789   return err;
1790 }
1791
1792
1793 static off_t
1794 es_offset_calculate (estream_t stream)
1795 {
1796   off_t offset;
1797
1798   offset = stream->intern->offset + stream->data_offset;
1799   if (offset < stream->unread_data_len)
1800     /* Offset undefined.  */
1801     offset = 0;
1802   else
1803     offset -= stream->unread_data_len;
1804
1805   return offset;
1806 }
1807
1808
1809 static void
1810 es_opaque_ctrl (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque_new,
1811                 void **ES__RESTRICT opaque_old)
1812 {
1813   if (opaque_old)
1814     *opaque_old = stream->intern->opaque;
1815   if (opaque_new)
1816     stream->intern->opaque = opaque_new;
1817 }
1818
1819
1820 static int
1821 es_get_fd (estream_t stream)
1822 {
1823   return stream->intern->fd;
1824 }
1825
1826 \f
1827
1828 /* API.  */
1829
1830 int
1831 es_init (void)
1832 {
1833   int err;
1834
1835   err = es_init_do ();
1836
1837   return err;
1838 }
1839
1840 estream_t
1841 es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode)
1842 {
1843   unsigned int flags;
1844   int create_called;
1845   estream_t stream;
1846   void *cookie;
1847   int err;
1848   int fd;
1849
1850   stream = NULL;
1851   cookie = NULL;
1852   create_called = 0;
1853
1854   err = es_convert_mode (mode, &flags);
1855   if (err)
1856     goto out;
1857   
1858   err = es_func_file_create (&cookie, &fd, path, flags);
1859   if (err)
1860     goto out;
1861
1862   create_called = 1;
1863   err = es_create (&stream, cookie, fd, estream_functions_file);
1864   if (err)
1865     goto out;
1866
1867  out:
1868   
1869   if (err && create_called)
1870     (*estream_functions_file.func_close) (cookie);
1871
1872   return stream;
1873 }
1874
1875
1876 estream_t
1877 es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len,
1878           unsigned int grow,
1879           func_realloc_t func_realloc, func_free_t func_free,
1880           const char *ES__RESTRICT mode)
1881 {
1882   unsigned int flags;
1883   int create_called;
1884   estream_t stream;
1885   void *cookie;
1886   int err;
1887
1888   cookie = 0;
1889   stream = NULL;
1890   create_called = 0;
1891   
1892   err = es_convert_mode (mode, &flags);
1893   if (err)
1894     goto out;
1895
1896   err = es_func_mem_create (&cookie, data, data_n, data_len,
1897                             BUFFER_BLOCK_SIZE, grow, 0, 0,
1898                             NULL, 0, func_realloc, func_free, flags);
1899   if (err)
1900     goto out;
1901   
1902   create_called = 1;
1903   err = es_create (&stream, cookie, -1, estream_functions_mem);
1904
1905  out:
1906
1907   if (err && create_called)
1908     (*estream_functions_mem.func_close) (cookie);
1909
1910   return stream;
1911 }
1912
1913
1914 estream_t
1915 es_open_memstream (char **ptr, size_t *size)
1916 {
1917   unsigned int flags;
1918   int create_called;
1919   estream_t stream;
1920   void *cookie;
1921   int err;
1922
1923   flags = O_RDWR;
1924   create_called = 0;
1925   stream = NULL;
1926   cookie = 0;
1927   
1928   err = es_func_mem_create (&cookie, NULL, 0, 0,
1929                             BUFFER_BLOCK_SIZE, 1, 1, 1,
1930                             ptr, size, ES_MEM_REALLOC, ES_MEM_FREE, flags);
1931   if (err)
1932     goto out;
1933   
1934   create_called = 1;
1935   err = es_create (&stream, cookie, -1, estream_functions_mem);
1936
1937  out:
1938
1939   if (err && create_called)
1940     (*estream_functions_mem.func_close) (cookie);
1941
1942   return stream;
1943 }
1944
1945
1946 estream_t
1947 es_fopencookie (void *ES__RESTRICT cookie,
1948                 const char *ES__RESTRICT mode,
1949                 es_cookie_io_functions_t functions)
1950 {
1951   unsigned int flags;
1952   estream_t stream;
1953   int err;
1954
1955   stream = NULL;
1956   flags = 0;
1957   
1958   err = es_convert_mode (mode, &flags);
1959   if (err)
1960     goto out;
1961
1962   err = es_create (&stream, cookie, -1, functions);
1963   if (err)
1964     goto out;
1965
1966  out:
1967
1968   return stream;
1969 }
1970
1971
1972 estream_t
1973 es_fdopen (int filedes, const char *mode)
1974 {
1975   unsigned int flags;
1976   int create_called;
1977   estream_t stream;
1978   void *cookie;
1979   int err;
1980
1981   stream = NULL;
1982   cookie = NULL;
1983   create_called = 0;
1984
1985   err = es_convert_mode (mode, &flags);
1986   if (err)
1987     goto out;
1988
1989   err = es_func_fd_create (&cookie, filedes, flags);
1990   if (err)
1991     goto out;
1992
1993   create_called = 1;
1994   err = es_create (&stream, cookie, filedes, estream_functions_fd);
1995
1996  out:
1997
1998   if (err && create_called)
1999     (*estream_functions_fd.func_close) (cookie);
2000
2001   return stream;
2002 }
2003   
2004
2005 estream_t
2006 es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode,
2007             estream_t ES__RESTRICT stream)
2008 {
2009   int err;
2010
2011   if (path)
2012     {
2013       unsigned int flags;
2014       int create_called;
2015       void *cookie;
2016       int fd;
2017
2018       cookie = NULL;
2019       create_called = 0;
2020       
2021       ESTREAM_LOCK (stream);
2022
2023       es_deinitialize (stream);
2024
2025       err = es_convert_mode (mode, &flags);
2026       if (err)
2027         goto leave;
2028       
2029       err = es_func_file_create (&cookie, &fd, path, flags);
2030       if (err)
2031         goto leave;
2032
2033       create_called = 1;
2034       es_initialize (stream, cookie, fd, estream_functions_file);
2035
2036     leave:
2037
2038       if (err)
2039         {
2040           if (create_called)
2041             es_func_fd_destroy (cookie);
2042       
2043           es_destroy (stream);
2044           stream = NULL;
2045         }
2046       else
2047         ESTREAM_UNLOCK (stream);
2048     }
2049   else
2050     {
2051       /* FIXME?  We don't support re-opening at the moment.  */
2052       errno = EINVAL;
2053       es_deinitialize (stream);
2054       es_destroy (stream);
2055       stream = NULL;
2056     }
2057
2058   return stream;
2059 }
2060
2061
2062 int
2063 es_fclose (estream_t stream)
2064 {
2065   int err;
2066
2067   err = es_destroy (stream);
2068
2069   return err;
2070 }
2071
2072 int
2073 es_fileno_unlocked (estream_t stream)
2074 {
2075   return es_get_fd (stream);
2076 }
2077
2078
2079 void
2080 es_flockfile (estream_t stream)
2081 {
2082   ESTREAM_LOCK (stream);
2083 }
2084
2085
2086 int
2087 es_ftrylockfile (estream_t stream)
2088 {
2089   return ESTREAM_TRYLOCK (stream);
2090 }
2091
2092
2093 void
2094 es_funlockfile (estream_t stream)
2095 {
2096   ESTREAM_UNLOCK (stream);
2097 }
2098
2099
2100 int
2101 es_fileno (estream_t stream)
2102 {
2103   int ret;
2104
2105   ESTREAM_LOCK (stream);
2106   ret = es_fileno_unlocked (stream);
2107   ESTREAM_UNLOCK (stream);
2108
2109   return ret;
2110 }
2111
2112
2113 int
2114 es_feof_unlocked (estream_t stream)
2115 {
2116   return es_get_indicator (stream, 0, 1);
2117 }
2118
2119
2120 int
2121 es_feof (estream_t stream)
2122 {
2123   int ret;
2124
2125   ESTREAM_LOCK (stream);
2126   ret = es_feof_unlocked (stream);
2127   ESTREAM_UNLOCK (stream);
2128
2129   return ret;
2130 }
2131
2132
2133 int
2134 es_ferror_unlocked (estream_t stream)
2135 {
2136   return es_get_indicator (stream, 1, 0);
2137 }
2138
2139
2140 int
2141 es_ferror (estream_t stream)
2142 {
2143   int ret;
2144
2145   ESTREAM_LOCK (stream);
2146   ret = es_ferror_unlocked (stream);
2147   ESTREAM_UNLOCK (stream);
2148
2149   return ret;
2150 }
2151
2152
2153 void
2154 es_clearerr_unlocked (estream_t stream)
2155 {
2156   es_set_indicators (stream, 0, 0);
2157 }
2158
2159
2160 void
2161 es_clearerr (estream_t stream)
2162 {
2163   ESTREAM_LOCK (stream);
2164   es_clearerr_unlocked (stream);
2165   ESTREAM_UNLOCK (stream);
2166 }
2167
2168
2169 int
2170 es_fflush (estream_t stream)
2171 {
2172   int err;
2173   
2174   if (stream)
2175     {
2176       ESTREAM_LOCK (stream);
2177       if (stream->flags & ES_FLAG_WRITING)
2178         err = es_flush (stream);
2179       else
2180         {
2181           es_empty (stream);
2182           err = 0;
2183         }
2184       ESTREAM_UNLOCK (stream);
2185     }
2186   else
2187     err = es_list_iterate (es_fflush);
2188
2189   return err ? EOF : 0;
2190 }
2191
2192
2193 int
2194 es_fseek (estream_t stream, long int offset, int whence)
2195 {
2196   int err;
2197
2198   ESTREAM_LOCK (stream);
2199   err = es_seek (stream, offset, whence, NULL);
2200   ESTREAM_UNLOCK (stream);
2201
2202   return err;
2203 }
2204
2205
2206 int
2207 es_fseeko (estream_t stream, off_t offset, int whence)
2208 {
2209   int err;
2210   
2211   ESTREAM_LOCK (stream);
2212   err = es_seek (stream, offset, whence, NULL);
2213   ESTREAM_UNLOCK (stream);
2214
2215   return err;
2216 }
2217
2218
2219 long int
2220 es_ftell (estream_t stream)
2221 {
2222   long int ret;
2223   
2224   ESTREAM_LOCK (stream);
2225   ret = es_offset_calculate (stream);
2226   ESTREAM_UNLOCK (stream);
2227
2228   return ret;
2229 }
2230
2231
2232 off_t
2233 es_ftello (estream_t stream)
2234 {
2235   off_t ret = -1;
2236
2237   ESTREAM_LOCK (stream);
2238   ret = es_offset_calculate (stream);
2239   ESTREAM_UNLOCK (stream);
2240
2241   return ret;
2242 }
2243
2244
2245 void
2246 es_rewind (estream_t stream)
2247 {
2248   ESTREAM_LOCK (stream);
2249   es_seek (stream, 0L, SEEK_SET, NULL);
2250   es_set_indicators (stream, 0, -1);
2251   ESTREAM_UNLOCK (stream);
2252 }
2253
2254
2255 int
2256 _es_getc_underflow (estream_t stream)
2257 {
2258   int err;
2259   unsigned char c;
2260   size_t bytes_read;
2261
2262   err = es_readn (stream, &c, 1, &bytes_read);
2263
2264   return (err || (! bytes_read)) ? EOF : c;
2265 }
2266
2267
2268 int
2269 _es_putc_overflow (int c, estream_t stream)
2270 {
2271   unsigned char d = c;
2272   int err;
2273
2274   err = es_writen (stream, &d, 1, NULL);
2275
2276   return err ? EOF : c;
2277 }
2278
2279
2280 int
2281 es_fgetc (estream_t stream)
2282 {
2283   int ret;
2284   
2285   ESTREAM_LOCK (stream);
2286   ret = es_getc_unlocked (stream);
2287   ESTREAM_UNLOCK (stream);
2288
2289   return ret;
2290 }
2291
2292
2293 int
2294 es_fputc (int c, estream_t stream)
2295 {
2296   int ret;
2297   
2298   ESTREAM_LOCK (stream);
2299   ret = es_putc_unlocked (c, stream);
2300   ESTREAM_UNLOCK (stream);
2301
2302   return ret;
2303 }
2304
2305
2306 int
2307 es_ungetc (int c, estream_t stream)
2308 {
2309   unsigned char data = (unsigned char) c;
2310   size_t data_unread;
2311
2312   ESTREAM_LOCK (stream);
2313   es_unreadn (stream, &data, 1, &data_unread);
2314   ESTREAM_UNLOCK (stream);
2315
2316   return data_unread ? c : EOF;
2317 }
2318
2319
2320 int
2321 es_read (estream_t ES__RESTRICT stream,
2322          void *ES__RESTRICT buffer, size_t bytes_to_read,
2323          size_t *ES__RESTRICT bytes_read)
2324 {
2325   int err;
2326
2327   if (bytes_to_read)
2328     {
2329       ESTREAM_LOCK (stream);
2330       err = es_readn (stream, buffer, bytes_to_read, bytes_read);
2331       ESTREAM_UNLOCK (stream);
2332     }
2333   else
2334     err = 0;
2335
2336   return err;
2337 }
2338
2339
2340 int
2341 es_write (estream_t ES__RESTRICT stream,
2342           const void *ES__RESTRICT buffer, size_t bytes_to_write,
2343           size_t *ES__RESTRICT bytes_written)
2344 {
2345   int err;
2346
2347   if (bytes_to_write)
2348     {
2349       ESTREAM_LOCK (stream);
2350       err = es_writen (stream, buffer, bytes_to_write, bytes_written);
2351       ESTREAM_UNLOCK (stream);
2352     }
2353   else
2354     err = 0;
2355
2356   return err;
2357 }
2358
2359
2360 size_t
2361 es_fread (void *ES__RESTRICT ptr, size_t size, size_t nitems,
2362           estream_t ES__RESTRICT stream)
2363 {
2364   size_t ret, bytes;
2365   int err;
2366
2367   if (size * nitems)
2368     {
2369       ESTREAM_LOCK (stream);
2370       err = es_readn (stream, ptr, size * nitems, &bytes);
2371       ESTREAM_UNLOCK (stream);
2372
2373       ret = bytes / size;
2374     }
2375   else
2376     ret = 0;
2377
2378   return ret;
2379 }
2380
2381
2382 size_t
2383 es_fwrite (const void *ES__RESTRICT ptr, size_t size, size_t nitems,
2384            estream_t ES__RESTRICT stream)
2385 {
2386   size_t ret, bytes;
2387   int err;
2388
2389   if (size * nitems)
2390     {
2391       ESTREAM_LOCK (stream);
2392       err = es_writen (stream, ptr, size * nitems, &bytes);
2393       ESTREAM_UNLOCK (stream);
2394
2395       ret = bytes / size;
2396     }
2397   else
2398     ret = 0;
2399
2400   return ret;
2401 }
2402
2403
2404 char *
2405 es_fgets (char *ES__RESTRICT s, int n, estream_t ES__RESTRICT stream)
2406 {
2407   char *ret = NULL;
2408   
2409   if (n)
2410     {
2411       int err;
2412       
2413       ESTREAM_LOCK (stream);
2414       err = doreadline (stream, n, &s, NULL);
2415       ESTREAM_UNLOCK (stream);
2416       if (! err)
2417         ret = s;
2418     }
2419   
2420   return ret;
2421 }
2422
2423
2424 int
2425 es_fputs (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream)
2426 {
2427   size_t length;
2428   int err;
2429
2430   length = strlen (s);
2431   ESTREAM_LOCK (stream);
2432   err = es_writen (stream, s, length, NULL);
2433   ESTREAM_UNLOCK (stream);
2434
2435   return err ? EOF : 0;
2436 }
2437
2438
2439 ssize_t
2440 es_getline (char *ES__RESTRICT *ES__RESTRICT lineptr, size_t *ES__RESTRICT n,
2441             estream_t ES__RESTRICT stream)
2442 {
2443   char *line = NULL;
2444   size_t line_n = 0;
2445   int err;
2446
2447   ESTREAM_LOCK (stream);
2448   err = doreadline (stream, 0, &line, &line_n);
2449   ESTREAM_UNLOCK (stream);
2450   if (err)
2451     goto out;
2452
2453   if (*n)
2454     {
2455       /* Caller wants us to use his buffer.  */
2456       
2457       if (*n < (line_n + 1))
2458         {
2459           /* Provided buffer is too small -> resize.  */
2460
2461           void *p;
2462
2463           p = ES_MEM_REALLOC (*lineptr, line_n + 1);
2464           if (! p)
2465             err = -1;
2466           else
2467             {
2468               if (*lineptr != p)
2469                 *lineptr = p;
2470             }
2471         }
2472
2473       if (! err)
2474         {
2475           memcpy (*lineptr, line, line_n + 1);
2476           if (*n != line_n)
2477             *n = line_n;
2478         }
2479       ES_MEM_FREE (line);
2480     }
2481   else
2482     {
2483       /* Caller wants new buffers.  */
2484       *lineptr = line;
2485       *n = line_n;
2486     }
2487
2488  out:
2489
2490   return err ? err : line_n;
2491 }
2492
2493
2494
2495 /* Same as fgets() but if the provided buffer is too short a larger
2496    one will be allocated.  This is similar to getline. A line is
2497    considered a byte stream ending in a LF.
2498
2499    If MAX_LENGTH is not NULL, it shall point to a value with the
2500    maximum allowed allocation.  
2501
2502    Returns the length of the line. EOF is indicated by a line of
2503    length zero. A truncated line is indicated my setting the value at
2504    MAX_LENGTH to 0.  If the returned value is less then 0 not enough
2505    memory was enable or another error occurred; ERRNO is then set
2506    accordingly.
2507
2508    If a line has been truncated, the file pointer is moved forward to
2509    the end of the line so that the next read starts with the next
2510    line.  Note that MAX_LENGTH must be re-initialzied in this case.
2511
2512    The caller initially needs to provide the address of a variable,
2513    initialized to NULL, at ADDR_OF_BUFFER and don't change this value
2514    anymore with the following invocations.  LENGTH_OF_BUFFER should be
2515    the address of a variable, initialized to 0, which is also
2516    maintained by this function.  Thus, both paramaters should be
2517    considered the state of this function.
2518
2519    Note: The returned buffer is allocated with enough extra space to
2520    allow the caller to append a CR,LF,Nul.  The buffer should be
2521    released using es_free.
2522  */
2523 ssize_t
2524 es_read_line (estream_t stream, 
2525               char **addr_of_buffer, size_t *length_of_buffer,
2526               size_t *max_length)
2527 {
2528   int c;
2529   char  *buffer = *addr_of_buffer;
2530   size_t length = *length_of_buffer;
2531   size_t nbytes = 0;
2532   size_t maxlen = max_length? *max_length : 0;
2533   char *p;
2534
2535   if (!buffer)
2536     { 
2537       /* No buffer given - allocate a new one. */
2538       length = 256;
2539       buffer = ES_MEM_ALLOC (length);
2540       *addr_of_buffer = buffer;
2541       if (!buffer)
2542         {
2543           *length_of_buffer = 0;
2544           if (max_length)
2545             *max_length = 0;
2546           return -1;
2547         }
2548       *length_of_buffer = length;
2549     }
2550
2551   if (length < 4)
2552     {
2553       /* This should never happen. If it does, the fucntion has been
2554          called with wrong arguments. */
2555       errno = EINVAL;
2556       return -1;
2557     }
2558   length -= 3; /* Reserve 3 bytes for CR,LF,EOL. */
2559
2560   ESTREAM_LOCK (stream);
2561   p = buffer;
2562   while  ((c = es_getc_unlocked (stream)) != EOF)
2563     {
2564       if (nbytes == length)
2565         { 
2566           /* Enlarge the buffer. */
2567           if (maxlen && length > maxlen) 
2568             {
2569               /* We are beyond our limit: Skip the rest of the line. */
2570               while (c != '\n' && (c=es_getc_unlocked (stream)) != EOF)
2571                 ;
2572               *p++ = '\n'; /* Always append a LF (we reserved some space). */
2573               nbytes++;
2574               if (max_length)
2575                 *max_length = 0; /* Indicate truncation. */
2576               break; /* the while loop. */
2577             }
2578           length += 3; /* Adjust for the reserved bytes. */
2579           length += length < 1024? 256 : 1024;
2580           *addr_of_buffer = ES_MEM_REALLOC (buffer, length);
2581           if (!*addr_of_buffer)
2582             {
2583               int save_errno = errno;
2584               ES_MEM_FREE (buffer); 
2585               *length_of_buffer = *max_length = 0;
2586               ESTREAM_UNLOCK (stream);
2587               errno = save_errno;
2588               return -1;
2589             }
2590           buffer = *addr_of_buffer;
2591           *length_of_buffer = length;
2592           length -= 3; 
2593           p = buffer + nbytes;
2594         }
2595       *p++ = c;
2596       nbytes++;
2597       if (c == '\n')
2598         break;
2599     }
2600   *p = 0; /* Make sure the line is a string. */
2601   ESTREAM_UNLOCK (stream);
2602
2603   return nbytes;
2604 }
2605
2606 /* Wrapper around free() to match the memory allocation system used
2607    by estream.  Should be used for all buffers returned to the caller
2608    by libestream. */
2609 void
2610 es_free (void *a)
2611 {
2612   if (a)
2613     ES_MEM_FREE (a);
2614 }
2615
2616
2617 int
2618 es_vfprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format,
2619              va_list ap)
2620 {
2621   int ret;
2622   
2623   ESTREAM_LOCK (stream);
2624   ret = es_print (stream, format, ap);
2625   ESTREAM_UNLOCK (stream);
2626
2627   return ret;
2628 }
2629
2630
2631 static int
2632 es_fprintf_unlocked (estream_t ES__RESTRICT stream,
2633            const char *ES__RESTRICT format, ...)
2634 {
2635   int ret;
2636   
2637   va_list ap;
2638   va_start (ap, format);
2639   ret = es_print (stream, format, ap);
2640   va_end (ap);
2641
2642   return ret;
2643 }
2644
2645
2646 int
2647 es_fprintf (estream_t ES__RESTRICT stream,
2648             const char *ES__RESTRICT format, ...)
2649 {
2650   int ret;
2651   
2652   va_list ap;
2653   va_start (ap, format);
2654   ESTREAM_LOCK (stream);
2655   ret = es_print (stream, format, ap);
2656   ESTREAM_UNLOCK (stream);
2657   va_end (ap);
2658
2659   return ret;
2660 }
2661
2662 static int
2663 tmpfd (void)
2664 {
2665   FILE *fp;
2666   int fp_fd;
2667   int fd;
2668
2669   fp = NULL;
2670   fd = -1;
2671   
2672   fp = tmpfile ();
2673   if (! fp)
2674     goto out;
2675
2676   fp_fd = fileno (fp);
2677   fd = dup (fp_fd);
2678
2679  out:
2680
2681   if (fp)
2682     fclose (fp);
2683
2684   return fd;
2685 }
2686
2687 estream_t
2688 es_tmpfile (void)
2689 {
2690   unsigned int flags;
2691   int create_called;
2692   estream_t stream;
2693   void *cookie;
2694   int err;
2695   int fd;
2696
2697   create_called = 0;
2698   stream = NULL;
2699   flags = O_RDWR | O_TRUNC | O_CREAT;
2700   cookie = NULL;
2701   
2702   fd = tmpfd ();
2703   if (fd == -1)
2704     {
2705       err = -1;
2706       goto out;
2707     }
2708
2709   err = es_func_fd_create (&cookie, fd, flags);
2710   if (err)
2711     goto out;
2712
2713   create_called = 1;
2714   err = es_create (&stream, cookie, fd, estream_functions_fd);
2715
2716  out:
2717
2718   if (err)
2719     {
2720       if (create_called)
2721         es_func_fd_destroy (cookie);
2722       else if (fd != -1)
2723         close (fd);
2724       stream = NULL;
2725     }
2726   
2727   return stream;
2728 }
2729
2730
2731 int
2732 es_setvbuf (estream_t ES__RESTRICT stream,
2733             char *ES__RESTRICT buf, int type, size_t size)
2734 {
2735   int err;
2736   
2737   if (((type == _IOFBF) || (type == _IOLBF) || (type == _IONBF))
2738       && (! ((! size) && (type != _IONBF))))
2739     {
2740       ESTREAM_LOCK (stream);
2741       err = es_set_buffering (stream, buf, type, size);
2742       ESTREAM_UNLOCK (stream);
2743     }
2744   else
2745     {
2746       errno = EINVAL;
2747       err = -1;
2748     }
2749
2750   return err;
2751 }
2752
2753
2754 void
2755 es_setbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf)
2756 {
2757   ESTREAM_LOCK (stream);
2758   es_set_buffering (stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
2759   ESTREAM_UNLOCK (stream);
2760 }
2761
2762 void
2763 es_opaque_set (estream_t stream, void *opaque)
2764 {
2765   ESTREAM_LOCK (stream);
2766   es_opaque_ctrl (stream, opaque, NULL);
2767   ESTREAM_UNLOCK (stream);
2768 }
2769
2770
2771 void *
2772 es_opaque_get (estream_t stream)
2773 {
2774   void *opaque;
2775   
2776   ESTREAM_LOCK (stream);
2777   es_opaque_ctrl (stream, NULL, &opaque);
2778   ESTREAM_UNLOCK (stream);
2779
2780   return opaque;
2781 }
2782
2783 /* Print a BUFFER to STREAM while replacing all control characters and
2784    the characters in DELIMITERS by standard C escape sequences.
2785    Returns 0 on success or -1 on error.  If BYTES_WRITTEN is not NULL
2786    the number of bytes actually written are stored at this
2787    address.  */
2788 int 
2789 es_write_sanitized (estream_t ES__RESTRICT stream,
2790                     const void * ES__RESTRICT buffer, size_t length,
2791                     const char * delimiters, 
2792                     size_t * ES__RESTRICT bytes_written)
2793 {
2794   const unsigned char *p = buffer;
2795   size_t count = 0;
2796   int ret;
2797
2798   ESTREAM_LOCK (stream);
2799   for (; length; length--, p++, count++)
2800     {
2801       if (*p < 0x20 
2802           || (*p >= 0x7f && *p < 0xa0)
2803           || (delimiters 
2804               && (strchr (delimiters, *p) || *p == '\\')))
2805         {
2806           es_putc_unlocked ('\\', stream);
2807           count++;
2808           if (*p == '\n')
2809             {
2810               es_putc_unlocked ('n', stream);
2811               count++;
2812             }
2813           else if (*p == '\r')
2814             {
2815               es_putc_unlocked ('r', stream);
2816               count++;
2817             }
2818           else if (*p == '\f')
2819             {
2820               es_putc_unlocked ('f', stream);
2821               count++;
2822             }
2823           else if (*p == '\v')
2824             {
2825               es_putc_unlocked ('v', stream);
2826               count++;
2827             }
2828           else if (*p == '\b')
2829             {
2830               es_putc_unlocked ('b', stream);
2831               count++;
2832             }
2833           else if (!*p)
2834             {
2835               es_putc_unlocked('0', stream);
2836               count++;
2837             }
2838           else
2839             {
2840               es_fprintf_unlocked (stream, "x%02x", *p);
2841               count += 3;
2842             }
2843         }
2844       else
2845         {
2846           es_putc_unlocked (*p, stream);
2847           count++;
2848         }
2849     }
2850
2851   if (bytes_written)
2852     *bytes_written = count;
2853   ret =  es_ferror_unlocked (stream)? -1 : 0;
2854   ESTREAM_UNLOCK (stream);
2855
2856   return ret;
2857 }
2858
2859
2860 /* Write LENGTH bytes of BUFFER to STREAM as a hex encoded string.
2861    RESERVED must be 0.  Returns 0 on success or -1 on error.  If
2862    BYTES_WRITTEN is not NULL the number of bytes actually written are
2863    stored at this address.  */
2864 int
2865 es_write_hexstring (estream_t ES__RESTRICT stream,
2866                     const void *ES__RESTRICT buffer, size_t length,
2867                     int reserved, size_t *ES__RESTRICT bytes_written )
2868 {
2869   int ret;
2870   const unsigned char *s;
2871   size_t count = 0;
2872
2873 #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
2874
2875   if (!length)
2876     return 0;
2877
2878   ESTREAM_LOCK (stream);
2879
2880   for (s = buffer; length; s++, length--)
2881     {
2882       es_putc_unlocked ( tohex ((*s>>4)&15), stream);
2883       es_putc_unlocked ( tohex (*s&15), stream);
2884       count += 2;
2885     }
2886
2887   if (bytes_written)
2888     *bytes_written = count;
2889   ret = es_ferror_unlocked (stream)? -1 : 0;
2890
2891   ESTREAM_UNLOCK (stream);
2892
2893   return ret;
2894
2895 #undef tohex
2896 }
2897
2898
2899
2900 #ifdef GNUPG_MAJOR_VERSION
2901 /* Special estream function to print an UTF8 string in the native
2902    encoding.  The interface is the same as es_write_sanitized, however
2903    only one delimiter may be supported. 
2904
2905    THIS IS NOT A STANDARD ESTREAM FUNCTION AND ONLY USED BY GNUPG!. */
2906 int
2907 es_write_sanitized_utf8_buffer (estream_t stream,
2908                                 const void *buffer, size_t length, 
2909                                 const char *delimiters, size_t *bytes_written)
2910 {
2911   const char *p = buffer;
2912   size_t i;
2913
2914   /* We can handle plain ascii simpler, so check for it first. */
2915   for (i=0; i < length; i++ ) 
2916     {
2917       if ( (p[i] & 0x80) )
2918         break;
2919     }
2920   if (i < length)
2921     {
2922       int delim = delimiters? *delimiters : 0;
2923       char *buf;
2924       int ret;
2925
2926       /*(utf8 conversion already does the control character quoting). */
2927       buf = utf8_to_native (p, length, delim);
2928       if (bytes_written)
2929         *bytes_written = strlen (buf);
2930       ret = es_fputs (buf, stream);
2931       xfree (buf);
2932       return i;
2933     }
2934   else
2935     return es_write_sanitized (stream, p, length, delimiters, bytes_written);
2936 }
2937 #endif /*GNUPG_MAJOR_VERSION*/