Import upstream 2.3.14
[packages/xinetd.git] / libs / src / sio / siosup.c
1 /*
2  * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis
3  * All rights reserved.  The file named COPYRIGHT specifies the terms 
4  * and conditions for redistribution.
5  */
6
7
8 #include "config.h"
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <syslog.h>
15
16 #ifdef _APPLE_
17 #undef HAVE_MMAP
18 #endif
19
20 #include "impl.h"
21 #include "sio.h"
22
23 int __sio_n_descriptors = 0 ;
24 __sio_descriptor_t *__sio_descriptors = NULL ;
25
26 static sio_status_e setup_read_buffer( __sio_id_t *idp, unsigned buf_size );
27
28 #ifndef MAP_FAILED
29 #define MAP_FAILED ((void *)-1)
30 #endif
31
32 /*
33  * Code for finalization
34  */
35 #ifdef HAVE_FINALIZATION_FUNCTION
36 static int finalizer_installed ;
37
38 SIO_DEFINE_FIN( sio_cleanup )
39 {
40    (void) Sflush( SIO_FLUSH_ALL ) ;
41 }
42 #endif /* HAVE_FINALIZATION_FUNCTION */
43
44
45
46 #ifdef HAVE_MMAP
47
48 #define CHAR_NULL            ((char *)0)
49
50 /*
51  * PAGES_MAPPED gives the size of each map unit in pages
52  */
53 #define PAGES_MAPPED            2
54
55 static size_t map_unit_size = 0 ;         /* bytes */
56 static size_t page_size = 0 ;               /* bytes */
57
58 static mapd_s *mmap_descriptors = NULL ;
59
60 #define MDP( fd )            ( mmap_descriptors + (fd) )
61
62
63 /*
64  * NOTES ON MEMORY MAPPING:
65  *
66  *    1. Memory mapping works only for file descriptors opened for input
67  *   2. Mapping an object to a part of the address space where another
68  *   object is mapped will cause the old mapping to disappear (i.e. mmap
69  *   will not fail)
70  *
71  * Memory mapping interface:
72  *      SIO_MMAP : maps a file into a portion of the address space.
73  *      SIO_MUNMAP: unmap a portion of the address space
74  *      SIO_MNEED: indicate to the OS that we will need a portion of
75  *                   our address space.
76  *
77  * The map_unit_size variable defines how much of the file is mapped at
78  * a time. It is a multiple of the operating system page size. It is
79  * not less than SIO_BUFFER_SIZE unless SIO_BUFFER_SIZE is not a
80  * multiple of the page size (so the SIO_BUFFER_SIZE overrides
81  * PAGES_MAPPED).
82  *
83  * NOTE: All memory mapping code is in this file only
84  */
85
86
87 /*
88  * Macros used by the memory mapping code
89  */
90 #define FIRST_TIME( dp )      ( dp->buf == NULL )
91
92 /*
93  * Functions to support memory mapping:
94  *
95  *         try_memory_mapping
96  *         buffer_setup
97  *         __sio_switch
98  *         initial_map
99  *         map_unit
100  */
101
102 /*
103  * try_memory_mapping attempts to setup the specified descriptor
104  * for memory mapping. 
105  * It returns FAILURE if it fails and SUCCESS if it is successful.
106  * If HAVE_MMAP is not defined, the function is defined to be FAILURE.
107  *
108  * Sets fields:
109  *      memory_mapped:          TRUE or FALSE
110  *      
111  * Also sets the following fields if memory_mapped is TRUE:
112  *    file_offset, file_size, buffer_size
113  *
114  */
115 static sio_status_e try_memory_mapping( int fd, __sio_id_t *idp, const struct stat *stp )
116 {
117    int access_f ;
118
119    /*
120     * Do not try memory mapping if:
121     *      1) The file is not a regular file
122     *      2) The file is a regular file but has zero-length
123     *      3) The file pointer is not positioned at the beginning of the file
124     *      4) The fcntl to obtain the file descriptor flags fails
125     *      5) The access mode is not O_RDONLY or O_RDWR
126     *
127     * The operations are done in this order to avoid the system calls
128     * if possible.
129     */
130    if ( ( ( stp->st_mode & S_IFMT ) != S_IFREG ) ||
131         ( stp->st_size == 0 ) ||
132         ( lseek( fd, (long)0, 1 ) != 0 ) ||
133         ( ( access_f = fcntl( fd, F_GETFL, 0 ) ) == -1 ) ||
134         ( ( access_f &= 0x3 ) != O_RDONLY && access_f != O_RDWR ) )
135    {
136       idp->memory_mapped = FALSE ;
137       return( FAILURE ) ;
138    }
139
140    /*
141     * Determine page_size and map_unit_size.
142     * Note that the code works even if PAGES_MAPPED is 0.
143     */
144    if ( page_size == 0 )
145    {
146       page_size = getpagesize() ;
147       map_unit_size = page_size * PAGES_MAPPED ;
148       if ( map_unit_size < SIO_BUFFER_SIZE )
149       {
150          if ( map_unit_size != 0 && SIO_BUFFER_SIZE % map_unit_size == 0 )
151             map_unit_size = SIO_BUFFER_SIZE ;
152          else
153             map_unit_size = page_size ;
154       }
155    }
156    
157    MDP(fd)->file_offset = 0 ;
158    MDP(fd)->file_size = stp->st_size ;
159    idp->buffer_size = map_unit_size ;
160    idp->buf = CHAR_NULL ;
161    idp->memory_mapped = TRUE ;
162
163    return( SUCCESS ) ;
164 }
165
166
167 /*
168  * Copy the current_unit to the primary buffer
169  *
170  * Sets fields: start, end, nextb
171  * Also sets the file pointer
172  */
173 static void buffer_setup( __sio_id_t *idp, int fd, const struct map_unit *mu_cur, const struct map_unit *mu_next )
174 {
175    off_t new_offset ;
176
177    sio_memcopy( mu_cur->addr, idp->buf, mu_cur->valid_bytes ) ;
178    idp->start = idp->buf ;
179    idp->end = idp->buf + mu_cur->valid_bytes ;
180    idp->nextb = idp->buf + ( idp->nextb - mu_cur->addr ) ;
181
182    if ( mu_next->addr != CHAR_NULL )
183       new_offset = MDP(fd)->file_offset - mu_next->valid_bytes ;
184    else
185       new_offset = MDP(fd)->file_offset ;
186    (void) lseek( fd, new_offset, 0 ) ;
187 }
188
189 /*
190  * Switch from memory mapping to buffered I/O
191  * If any mapping has occurred, then the current unit is
192  * copied into the buffer that is allocated.
193  * Any data in the next unit is ignored.
194  * We rely on idp->buf to identify the current unit (so it
195  * better be equal to the address of one of the units).
196  *
197  * Sets fields:
198  *         start, end, nextb
199  */
200 sio_status_e __sio_switch( __sio_id_t *idp, int fd )
201 {
202    mapd_s *mdp = MDP( fd ) ;
203    struct map_unit *mu_cur, *mu_next ;
204    unsigned buffer_size = idp->buffer_size ;
205    char *buf_addr = idp->buf ;
206    int first_time = FIRST_TIME( idp ) ;
207
208    /*
209     * Initialize stream for buffering
210     */
211    if ( setup_read_buffer( idp, buffer_size ) == FAILURE )
212       return( FAILURE ) ;
213
214    if ( ! first_time )
215    {
216       /*
217        * Find current, next unit
218        */
219       if ( buf_addr == mdp->first_unit.addr )
220       {
221          mu_cur = &mdp->first_unit ;
222          mu_next = &mdp->second_unit ;
223       }
224       else
225       {
226          mu_cur = &mdp->second_unit ;
227          mu_next = &mdp->first_unit ;
228       }
229
230       buffer_setup( idp, fd, mu_cur, mu_next ) ;
231       /*
232        * Destroy all mappings
233        */
234       (void) SIO_MUNMAP( mu_cur->addr, mu_cur->mapped_bytes ) ;
235       if ( mu_next->addr != NULL )
236          (void) SIO_MUNMAP( mu_next->addr, mu_next->mapped_bytes ) ;
237    }
238    else
239       idp->start = idp->end = idp->nextb = idp->buf ;
240
241    idp->memory_mapped = FALSE ;
242    return( SUCCESS ) ;
243 }
244
245
246 /*
247  * initial_map does the first memory map on the file descriptor.
248  * It attempts to map both units.
249  * The mapping always starts at file offset 0.
250  *
251  * SETS FIELDS:
252  *         first_unit.*, second_unit.*
253  *         file_offset
254  *
255  * Returns: 
256  *         number of bytes mapped in first_unit
257  *    or
258  *         0 to indicate that mmap failed.
259  */
260 static int initial_map( mapd_s *mdp, int fd )
261 {
262    caddr_t addr ;
263    size_t requested_length = 2 * map_unit_size ;
264    size_t mapped_length = MIN( (size_t)mdp->file_size, requested_length ) ;
265    size_t bytes_left ;
266    size_t bytes_in_unit ;
267
268    addr = SIO_MMAP( CHAR_NULL, mapped_length, fd, 0 ) ;
269    if ( addr == MAP_FAILED )
270       return( 0 ) ;
271
272    SIO_MNEED( addr, mapped_length ) ;
273
274    /*
275     * Map as much as possible in the first unit
276     */
277    bytes_in_unit = MIN( mapped_length, map_unit_size ) ;
278    mdp->first_unit.addr          = addr ;
279    mdp->first_unit.mapped_bytes    = bytes_in_unit ;
280    mdp->first_unit.valid_bytes    = bytes_in_unit ;
281
282    /*
283     * If there is more, map it in the second unit.
284     */
285    bytes_left = mapped_length - bytes_in_unit ;
286    if ( bytes_left != 0 )
287    {
288       mdp->second_unit.addr          = addr + bytes_in_unit ;
289       mdp->second_unit.mapped_bytes = bytes_left ;
290       mdp->second_unit.valid_bytes    = bytes_left ;
291    }
292    else
293       mdp->second_unit.addr          = CHAR_NULL ;
294
295    mdp->file_offset = mapped_length ;
296
297    return( mdp->first_unit.valid_bytes ) ;
298 }
299
300
301 /*
302  * ALGORITHM:
303  *
304  *      if ( there are more bytes in the file )
305  *      {
306  *         map them at the given unit
307  *         update offset
308  *         issue SIO_MNEED()
309  *      }
310  *      else
311  *         unmap the unit
312  */
313 static sio_status_e map_unit( mapd_s *mdp, int fd, struct map_unit *mup )
314 {
315    size_t bytes_left = mdp->file_size - mdp->file_offset ;
316    size_t bytes_to_map = MIN( bytes_left, map_unit_size ) ;
317
318    if ( bytes_to_map != 0 )
319    {
320       if ( SIO_MMAP( mup->addr, bytes_to_map,
321                                              fd, mdp->file_offset ) == MAP_FAILED )
322          return( FAILURE ) ;         /* XXX: need to do more ? */
323       
324       mup->valid_bytes = bytes_to_map ;
325       ASSERT( mup->valid_bytes <= mup->mapped_bytes ) ;
326       mdp->file_offset += bytes_to_map ;
327       SIO_MNEED( mup->addr, mup->valid_bytes ) ;
328    }
329    else
330    {
331       (void) SIO_MUNMAP( mup->addr, mup->mapped_bytes ) ;
332       mup->addr = CHAR_NULL ;
333    }
334    return( SUCCESS ) ;
335 }
336
337 #else
338
339 #define try_memory_mapping( x, y, z )            FAILURE
340
341 #endif /* HAVE_MMAP */
342
343
344 static sio_status_e setup_read_buffer( __sio_id_t *idp, unsigned buf_size )
345 {
346    char *buf ;
347
348    /*
349     * First allocate space for 2 buffers: primary and auxiliary
350     */
351    buf = malloc( buf_size * 2 ) ;
352    if ( buf == NULL )
353       return( FAILURE ) ;
354
355    /*
356     * The descriptor buf field should point to the start of the main buffer
357     */
358    idp->buf = buf + buf_size ;
359    idp->buffer_size = buf_size ;
360    return( SUCCESS ) ;
361 }
362
363
364 static sio_status_e init_input_stream( __sio_id_t *idp, int fd, const struct stat *stp )
365 {
366    /*
367     * First initialize the fields relevant to buffering: buf, buffer_size
368     */
369    if ( try_memory_mapping( fd, idp, stp ) == FAILURE )
370    {
371       /*
372        * Try to use normal buffering
373        */
374       unsigned buf_size = (unsigned)
375                      ( stp->st_blksize ? stp->st_blksize : SIO_BUFFER_SIZE ) ;
376       
377       if ( setup_read_buffer( idp, buf_size ) == FAILURE )
378          return( FAILURE ) ;
379    }
380
381     /*
382     * Initialize remaining descriptor fields
383     */
384    idp->max_line_length = 2 * idp->buffer_size - 1 ;
385    idp->start = idp->end = idp->nextb = idp->buf ;
386    idp->tied_fd = SIO_NO_TIED_FD ;
387
388    return( SUCCESS ) ;
389 }
390
391
392 static sio_status_e init_output_stream( __sio_od_t *odp, int fd, 
393         const struct stat *stp )
394 {
395    unsigned buf_size ;
396    char *buf ;
397
398    buf_size = (unsigned)
399                   ( stp->st_blksize ? stp->st_blksize : SIO_BUFFER_SIZE ) ;
400    buf = malloc( buf_size ) ;
401    if ( buf == NULL )
402       return( FAILURE ) ;
403    
404    /*
405     * Initialize buffering fields
406     */
407    odp->buf = buf ;
408    odp->buffer_size = buf_size ;
409    odp->buf_end = odp->buf + buf_size ;
410
411    /*
412     * Initialize remaining fields
413     */
414    odp->start = odp->nextb = odp->buf ;
415    if ( isatty( fd ) )
416       odp->buftype = SIO_LINEBUF ;
417
418    if ( fd == 2 )
419       odp->buftype = SIO_NOBUF ;
420
421    return( SUCCESS ) ;
422 }
423
424
425 #ifndef HAVE_ISATTY
426
427 #ifdef HAVE_SYSVTTY
428
429 #include <termio.h>
430
431 static int isatty( int fd )
432 {
433    struct termio t ;
434
435    if ( ioctl( fd, TCGETA, &t ) == -1 && errno == ENOTTY )
436       return( FALSE ) ;
437    else
438       return( TRUE ) ;
439 }
440 #endif   /* HAVE_SYSVTTY */
441
442 #ifdef HAVE_BSDTTY
443
444 #include <sgtty.h>
445
446 static int isatty( int fd )
447 {
448    struct sgttyb s ;
449
450    if ( ioctl( fd, TIOCGETP, &s ) == -1 && errno == ENOTTY )
451       return( FALSE ) ;
452    else
453       return( TRUE ) ;
454 }
455 #endif   /* HAVE_BSDTTY */
456
457 #endif   /* ! HAVE_ISATTY */
458
459
460 /*
461  * Initialize stream I/O for a file descriptor.
462  *
463  * Arguments:
464  *      fd:            file descriptor
465  *      dp:            descriptor pointer
466  *      stream_type:    either __SIO_INPUT_STREAM or __SIO_OUTPUT_STREAM
467  *
468  * Returns
469  *      0          if successful
470  *     SIO_ERR   if the file descriptor is not valid (sets errno)
471  *   exits      if stream_type is not __SIO_INPUT_STREAM or __SIO_OUTPUT_STREAM
472  */
473 int __sio_init( __sio_descriptor_t *dp, int fd, enum __sio_stream stream_type )
474 {
475    struct stat st ;
476
477    memset(dp, 0, sizeof(__sio_descriptor_t));
478    if ( fd >= __sio_n_descriptors )
479    {
480       errno = EBADF ;
481       return( SIO_ERR ) ;
482    }
483
484    if ( fstat( fd, &st ) == -1 )
485       return( SIO_ERR ) ;
486    
487    switch ( stream_type )
488    {
489       case __SIO_INPUT_STREAM:
490          if ( init_input_stream( IDP( dp ), fd, &st ) == FAILURE )
491             return( SIO_ERR ) ;
492          break ;
493
494       case __SIO_OUTPUT_STREAM:
495          if ( init_output_stream( ODP( dp ), fd, &st ) == FAILURE )
496             return( SIO_ERR ) ;
497          break ;
498          
499       default:
500          terminate( "SIO __sio_init: bad stream type (internal error).\n" ) ;
501          /* NOTREACHED */
502    }
503    dp->stream_type = stream_type ;
504    dp->initialized = TRUE ;
505
506 #ifdef HAVE_FINALIZATION_FUNCTION
507    if ( ! finalizer_installed )
508    {
509       if ( ! SIO_FINALIZE( sio_cleanup ) )
510       {
511          char *s = "SIO __sio_init: finalizer installation failed\n" ;
512
513          (void) write( 2, s, strlen( s ) ) ;
514       }
515       else
516          finalizer_installed = TRUE ;
517    }
518 #endif /* HAVE_FINALIZATION_FUNCTION */
519
520    return( 0 ) ;
521 }
522
523
524 /*
525  * __sio_writef writes the data in the buffer to the file descriptor.
526  *
527  * It tries to write as much data as possible until either all data
528  * are written or an error occurs. EINTR is the only error that is
529  * ignored.
530  * In case an error occurs but some data were written, that number
531  * is returned instead of SIO_ERR.
532  *
533  * Fields modified:
534  *      When successful: start, nextb
535  *      When not successful: start
536  *
537  * Return value:
538  *      Number of bytes written
539  *      SIO_ERR, if write(2) fails and no data were written
540  */ 
541 int __sio_writef( __sio_od_t *odp, int fd )
542 {
543    int b_in_buffer ;
544    int cc_total = 0 ;
545
546    /*
547     * Make sure we don't exceed the buffer limits
548     *   Maybe we should log this ?         XXX
549     */
550    if ( odp->nextb > odp->buf_end )
551       odp->nextb = odp->buf_end ;
552
553    b_in_buffer = odp->nextb - odp->start ;
554
555    if ( b_in_buffer == 0 )
556       return( 0 ) ;
557    
558    for ( ;; )
559    {
560       int cc ;
561
562       cc = write( fd, odp->start, b_in_buffer ) ;
563       if ( cc == b_in_buffer )
564       {
565          odp->start = odp->nextb = odp->buf ;
566          cc_total += cc ;
567          break ;
568       }
569       else if ( cc == -1 )
570       {
571          if ( errno == EINTR )
572             continue ;
573          else
574             /*
575              * If some bytes were written, return that number, otherwise
576              * return SIO_ERR
577              */
578             return( ( cc_total != 0 ) ? cc_total : SIO_ERR ) ;
579       }
580       else         /* some bytes were written */
581       {
582          odp->start += cc ;         /* advance start of buffer */
583          b_in_buffer -= cc ;         /* decrease number bytes left in buffer */
584          cc_total += cc ;            /* count the bytes that were written */
585       }
586    }
587    return( cc_total ) ;
588 }
589
590
591 /*
592  * __sio_readf reads data from the file descriptor into the buffer.
593  * Unlike __sio_writef it does NOT try to read as much data as will fit
594  * in the buffer. It ignores EINTR.
595  *
596  * Returns: # of bytes read or SIO_ERR
597  *
598  * Fields set:
599  *       If it does not return SIO_ERR, it sets start, nextb, end
600  *         If it returns SIO_ERR, it does not change anything
601  */
602 static int __sio_readf( __sio_id_t *idp, int fd )
603 {
604    int cc ;
605
606    /*
607     * First check for a tied fd and flush the stream if necessary
608     *
609     *       XXX   the return value of __sio_writef is not checked.
610     *               Is that right ?
611     */
612    if ( idp->tied_fd != SIO_NO_TIED_FD )
613       (void) __sio_writef( &__SIO_OD( idp->tied_fd ), idp->tied_fd ) ;
614
615 #ifdef HAVE_MMAP
616    if ( idp->memory_mapped )
617    {
618       mapd_s *mdp = MDP( fd ) ;
619
620       /*
621        * The functions initial_map and map_unit may fail.
622        * In either case, we switch to buffered I/O.
623        * If initial_map fails, we have read no data, so we
624        * should perform a read(2).
625        * If map_unit fails (for the next unit), we still have
626        * the data in the current unit, so we can return.
627        */
628       if ( FIRST_TIME( idp ) )
629       {
630          cc = initial_map( mdp, fd ) ;
631          if ( cc > 0 )
632             idp->buf = mdp->first_unit.addr ;
633          else
634          {
635             if ( __sio_switch( idp, fd ) == FAILURE )
636                return( SIO_ERR ) ;
637             cc = -1 ;
638          }
639       }
640       else
641       {
642          struct map_unit *mu_cur, *mu_next ;
643
644          if ( idp->buf == mdp->first_unit.addr )
645          {
646             mu_cur = &mdp->first_unit ;
647             mu_next = &mdp->second_unit ;
648          }
649          else
650          {
651             mu_cur = &mdp->second_unit ;
652             mu_next = &mdp->first_unit ;
653          }
654
655          if ( mu_next->addr != NULL )
656          {
657             idp->buf = mu_next->addr ;
658             cc = mu_next->valid_bytes ;
659             /*
660              * XXX:  Here we may return SIO_ERR even though there
661              *         are data in the current unit because the switch
662              *         fails (possibly because malloc failed).
663              */
664             if ( map_unit( mdp, fd, mu_cur ) == FAILURE &&
665                               __sio_switch( idp, fd ) == FAILURE )
666                return( SIO_ERR ) ;
667          }
668          else
669             cc = 0 ;
670       }
671       if ( cc >= 0 )
672       {
673          idp->end = idp->buf + cc ;
674          idp->start = idp->nextb = idp->buf ;
675          return( cc ) ;
676       }
677    }
678 #endif /* HAVE_MMAP */
679
680    for ( ;; )
681    {
682       cc = read( fd, idp->buf, (int) idp->buffer_size ) ;
683       if ( cc == -1 )
684          if ( errno == EINTR )
685             continue ;
686          else
687             return( SIO_ERR ) ;
688       else
689          break ;
690    }
691
692    idp->end = idp->buf + cc ;
693    idp->start = idp->nextb = idp->buf ;
694    return( cc ) ;
695 }
696
697
698 /*
699  * __sio_extend_buffer is used by Srdline to extend the buffer
700  * If successful, it returns the number of bytes that have been read.
701  * If it fails (because of end-of-file or I/O error), it returns 0 or -1.
702  *
703  * Fields modified:
704  *    idp->start points to the start of the buffer area (which is in the
705  *    auxiliary buffer)
706  *      Also, if successful, idp->nextb is set to idp->buf, idp->end is modified.
707  */
708 int __sio_extend_buffer( __sio_id_t *idp, int fd, int b_left )
709 {
710    int b_read ;
711
712    /*
713     * copy to auxiliary buffer
714     */
715    if ( b_left )
716       sio_memcopy( idp->nextb, idp->buf - b_left, b_left ) ;
717    b_read = __sio_readf( idp, fd ) ;
718    idp->start = idp->buf - b_left ;
719    return( b_read ) ;
720 }
721
722
723 /*
724  * __sio_more tries to read more data from the given file descriptor iff
725  * there is free space in the buffer.
726  * __sio_more is used only by Srdline and only AFTER __sio_extend_buffer
727  * has been called. This implies that 
728  *      a) this is not a memory mapped file
729  *      b) __sio_readf has been called (so we don't need to check for tied fd's
730  *
731  * Fields modified (only if successful):
732  *         idp->end
733  *
734  * Return value: the number of bytes read.
735  */
736 int __sio_more( __sio_id_t *idp, int fd )
737 {
738    int b_left = &idp->buf[ idp->buffer_size ] - idp->end ;
739    int cc ;
740
741    if ( b_left <= 0 )
742       return( 0 ) ;
743    
744    for ( ;; )
745    {
746       cc = read( fd, idp->end, b_left ) ;
747       if ( cc >= 0 )
748       {
749          idp->end += cc ;
750          return( cc ) ;
751       }
752       else
753          if ( errno == EINTR )
754             continue ;
755          else
756             return( SIO_ERR ) ;
757    }
758 }
759
760
761 /*
762  * Finalize a buffer by unmapping the file or freeing the malloc'ed memory.
763  * This function is only called by Sclose. We always free memory even if
764  * SIO_ERR is returned as long as the descriptor was initialized.
765  */
766 int Sdone( int fd )
767 {
768    __sio_descriptor_t *dp ;
769    int ret_val = 0;
770
771    if ( fd < 0 || fd >= __sio_n_descriptors )
772    {
773       errno = EBADF ;
774       return( SIO_ERR ) ;
775    }
776
777    dp = &__sio_descriptors[ fd ] ;
778    if ( ! DESCRIPTOR_INITIALIZED( dp ) )
779    {
780       errno = EBADF ;
781       return( SIO_ERR ) ;
782    }
783
784    switch ( dp->stream_type )
785    {
786       case __SIO_INPUT_STREAM:
787          {
788             __sio_id_t *idp = IDP( dp ) ;
789
790 #ifdef HAVE_MMAP
791             if ( idp->memory_mapped )
792             {
793                mapd_s *mdp = MDP( fd ) ;
794
795                if ( mdp->first_unit.addr != CHAR_NULL )
796                   (void) SIO_MUNMAP( mdp->first_unit.addr,
797                                           mdp->first_unit.mapped_bytes ) ;
798                if ( mdp->second_unit.addr != CHAR_NULL )
799                   (void) SIO_MUNMAP( mdp->second_unit.addr,
800                                           mdp->second_unit.mapped_bytes ) ;
801                idp->memory_mapped = FALSE ;
802             }
803             else
804 #endif   /* HAVE_MMAP */
805                free( idp->buf - idp->buffer_size ) ;
806                idp->nextb = idp->end = NULL ;
807          }
808          break ;
809       
810       case __SIO_OUTPUT_STREAM:
811          {
812             __sio_od_t *odp = ODP( dp ) ;
813
814             if ( Sflush( fd ) == SIO_ERR )
815                ret_val = SIO_ERR;
816             free( odp->buf ) ;
817             odp->nextb = odp->buf_end = NULL ;
818          }
819          break ;
820       
821       default:
822          terminate( "SIO Sdone: bad stream type\n" ) ;
823    }
824
825    memset( dp, 0, sizeof(__sio_descriptor_t) );
826    dp->initialized = FALSE ;
827    return ret_val;
828 }
829
830
831 static char *sioexpand( char *area, unsigned old_size, unsigned new_size, int is_static )
832 {
833    char *new_area ;
834
835    if ( is_static )
836    {
837       if ( ( new_area = malloc( new_size ) ) == NULL )
838          return( NULL ) ;
839       sio_memcopy( area, new_area, old_size ) ;
840    }
841    else
842       if ( ( new_area = realloc( area, new_size ) ) == NULL )
843          return( NULL ) ;
844       
845    return( new_area ) ;
846 }
847
848
849 /*
850  * Expand the descriptor array (and if we use memory mapping the
851  * memory mapping descriptors). We first expand the memory mapping
852  * descriptors.
853  * There is no problem if the expansion of the SIO descriptors fails
854  * (i.e. there is no need to undo anything).
855  */
856 int Smorefds(int fd)
857 {
858    char *p ;
859    int is_static ;
860    unsigned new_size, old_size ;
861    int n_fds = 4; /* Let's bump 4 at a time for hysteresis */
862
863    /* If the fd is out of range of the proposed size, make n_fds big enough */
864    if (fd >= (__sio_n_descriptors+n_fds))
865       n_fds += fd - __sio_n_descriptors;
866
867 #ifdef HAVE_MMAP
868    old_size = __sio_n_descriptors * sizeof( mapd_s ) ;
869    new_size = n_fds * sizeof( mapd_s ) ;
870    new_size += old_size;
871    is_static = ( mmap_descriptors == NULL ) ;
872    p = sioexpand( (char *)mmap_descriptors, old_size, new_size, is_static ) ;
873    if ( p == NULL )
874       return( SIO_ERR ) ;
875    memset(p+old_size, 0, new_size-old_size);
876    mmap_descriptors = (mapd_s *) p ;
877 #endif   /* HAVE_MMAP */
878    
879    old_size = __sio_n_descriptors * sizeof( __sio_descriptor_t ) ;
880    new_size = n_fds * sizeof( __sio_descriptor_t ) ;
881    new_size += old_size;
882    is_static =  ( __sio_descriptors == NULL ) ;
883    p = sioexpand( (char *)__sio_descriptors, old_size, new_size, is_static ) ;
884    if ( p == NULL )
885       return( SIO_ERR ) ;
886    memset(p+old_size, 0, new_size-old_size);
887    __sio_descriptors = (__sio_descriptor_t *) p ;
888
889    __sio_n_descriptors += n_fds ;
890    return( 0 ) ;
891 }
892
893 void terminate(const char *msg)
894 {
895       syslog(LOG_CRIT, "%s", msg);
896       (void) abort() ;
897       _exit( 1 ) ;      /* NOT REACHED */
898 }
899