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