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