Import upstream 2.3.14
[packages/xinetd.git] / libs / src / sio / sio.c
1 /*
2  * (c) Copyright 1992, 1993 by Panagiotis Tsirigotis
3  * (c) Portions Copyright 1998-2001 by Rob Braun
4  * All rights reserved.  The file named COPYRIGHT specifies the terms 
5  * and conditions for redistribution.
6  */
7
8 #include "config.h"
9 #ifndef linux
10 #include <sys/types.h>
11 #endif
12
13 #ifdef _APPLE_
14 #undef HAVE_MMAP
15 #endif
16
17 #include <limits.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20
21 #include "sio.h"
22 #include "impl.h"
23
24 /*
25  * SIO WRITE FUNCTIONS: Swrite, Sputc
26  */
27
28 /*
29  * Stream write call: arguments same as those of write(2)
30  */
31 int Swrite( int fd, const char *addr, unsigned int nbytes )
32 {
33         __sio_descriptor_t *dp ;
34         __sio_od_t *odp ;
35         unsigned int b_transferred ;
36         unsigned int b_avail ;
37         int total_b_transferred ;
38         int b_written ;
39         int b_in_buffer ;
40
41         if ( nbytes > INT_MAX )
42                 return( SIO_ERR );
43         if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR )
44                 return( SIO_ERR );
45         odp = ODP( dp ) ;
46         ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ;
47
48         b_avail = odp->buf_end - odp->nextb ;
49         b_transferred = MIN( nbytes, b_avail ) ;
50         sio_memcopy( addr, odp->nextb, b_transferred ) ;
51         odp->nextb += b_transferred ;
52
53         /*
54          * check if we are done
55          */
56         if ( b_transferred == nbytes )
57                 return( b_transferred ) ;
58
59         /*
60          * at this point we know that the buffer is full
61          */
62         b_in_buffer = odp->buf_end - odp->start ;
63         b_written = __sio_writef( odp, fd ) ;
64         if ( b_written != b_in_buffer )
65                 return( (b_written >= (int)nbytes) ? (int)nbytes : b_written ) ;
66         
67         total_b_transferred = b_transferred ;
68         addr += b_transferred ;
69         nbytes -= b_transferred ;
70
71         for ( ;; )
72         {
73                 b_transferred = MIN( nbytes, odp->buffer_size ) ;
74                 sio_memcopy( addr, odp->nextb, b_transferred ) ;
75                 odp->nextb += b_transferred ;
76                 nbytes -= b_transferred ;
77                 if ( nbytes == 0 )
78                 {
79                         total_b_transferred += b_transferred ;
80                         break ;
81                 }
82                 /*
83                  * the buffer is full
84                  */
85                 b_written = __sio_writef( odp, fd ) ;
86                 if ( b_written != (int)odp->buffer_size )
87                 {
88                         if ( b_written != SIO_ERR )
89                         {
90                                 total_b_transferred += b_written ;
91                                 odp->nextb += b_written ;
92                         }
93                         break ;
94                 }
95                 /*
96                  * everything is ok
97                  */
98                 total_b_transferred += b_transferred ;
99                 addr += b_transferred ;
100         }
101         return( total_b_transferred ) ;
102 }
103
104
105 /*
106  * Add a character to a file
107  */
108 static int Sputc( int fd, char c )
109 {
110         __sio_descriptor_t *dp ;
111         __sio_od_t *odp ;
112
113         if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM) == SIO_ERR )
114                 return( SIO_ERR );
115         odp = ODP( dp ) ;
116         ASSERT( odp->start <= odp->nextb && odp->nextb <= odp->buf_end ) ;
117
118         /*
119          * The following is a weak check since we should really be
120          * checking that nextb == buf_end (it would be an error for
121          * nextb to exceed buf_end; btw, the assertion above, when
122          * enabled makes sure this does not occur).
123          *
124          * NOTE: __sio_writef NEVER uses data beyond the end of buffer.
125          */
126         if ( odp->nextb >= odp->buf_end )
127         {
128                 int b_in_buffer = odp->buf_end - odp->start ;
129
130                 /*
131                  * There is nothing we can do if __sio_writef does not manage
132                  * to write the whole buffer
133                  */
134                 if ( __sio_writef( odp, fd ) != b_in_buffer )
135                         return( SIO_ERR ) ;
136         }
137         *odp->nextb++ = c ;
138         if ( __SIO_MUST_FLUSH( *odp, c ) && __sio_writef( odp, fd ) == SIO_ERR )
139                 return( SIO_ERR ) ;
140         return ( c ) ;
141 }
142
143
144
145 /*
146  * SIO READ FUNCTIONS
147  */
148
149
150 /*
151  * Read a line from a file
152  * Returns a pointer to the beginning of the line or NULL
153  */
154 char *Srdline( int fd )
155 {
156         __sio_descriptor_t *dp ;
157         __sio_id_t *idp ;
158         char *cp ;
159         char *line_start ;
160         int b_left ;
161         int extension ;
162
163         if( sio_setup( fd, &dp, __SIO_INPUT_STREAM ) == SIO_ERR )
164                 return( NULL );
165         idp = IDP( dp ) ;
166         ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
167
168 #ifdef HAVE_MMAP
169         if ( idp->memory_mapped && __sio_switch( idp, fd ) == FAILURE )
170                 return( NULL ) ;
171 #endif
172
173         b_left = idp->end - idp->nextb ;
174         /*
175          * Look for a '\n'. If the search fails, extend the buffer
176          * and search again (the extension is performed by copying the
177          * bytes that were searched to the auxiliary buffer and reading 
178          * new input in the main buffer).
179          * If the new input still does not contain a '\n' and there is
180          * more space in the main buffer (this can happen with network
181          * connections), read more input until either the buffer is full
182          * or a '\n' is found.
183          * Finally, set cp to point to the '\n', and line_start to
184          * the beginning of the line
185          */
186         if ( b_left && ( cp = sio_memscan( idp->nextb, b_left, '\n' ) ) != NULL )
187         {
188                 line_start = idp->nextb ;
189                 idp->nextb = cp + 1 ;
190         }
191         else
192         {
193                 extension = __sio_extend_buffer( idp, fd, b_left ) ;
194                 if ( extension > 0 )
195                 {
196                         ASSERT( idp->start <= idp->nextb && idp->nextb <= idp->end ) ;
197
198                         line_start = idp->start ;
199                         cp = sio_memscan( idp->nextb, extension, '\n' ) ;
200                         if ( cp != NULL )
201                                 idp->nextb = cp + 1 ;
202                         else
203                                 for ( ;; )
204                                 {
205                                         idp->nextb = idp->end ;
206                                         extension = __sio_more( idp, fd ) ;
207                                         if ( extension > 0 )
208                                         {
209                                                 cp = sio_memscan( idp->nextb, extension, '\n' ) ;
210                                                 if ( cp == NULL )
211                                                         continue ;
212                                                 idp->nextb = cp + 1 ;
213                                                 break ;
214                                         }
215                                         else
216                                         {
217                                                 /*
218                                                  * If there is spare room in the buffer avoid trashing
219                                                  * the last character
220                                                  */
221                                                 if ( idp->end < &idp->buf[ idp->buffer_size ] )
222                                                         cp = idp->end ;
223                                                 else
224                                                         cp = &idp->buf[ idp->buffer_size - 1 ] ;
225                                                 break ;
226                                         }
227                                 }
228                 }
229                 else                                    /* buffer could not be extended */
230                         if ( b_left == 0 )
231                         {
232                                 /*
233                                  * Set errno to 0 if EOF has been reached
234                                  */
235                                 if ( extension == 0 )
236                                         errno = 0 ;
237                                 return( NULL ) ;
238                         }
239                         else
240                         {
241                                 line_start = idp->start ;
242                                 cp = idp->end ;
243                                 /*
244                                  * By setting idp->nextb to be equal to idp->end,
245                                  * subsequent calls to Srdline will return NULL because
246                                  * __sio_extend_buffer will be invoked and it will return 0.
247                                  */
248                                 idp->nextb = idp->end ;
249                         }
250         }
251         *cp = NUL ;
252         idp->line_length = cp - line_start ;
253         return( line_start ) ;
254 }
255
256
257
258 /*
259  * SIO CONTROL FUNCTIONS
260  */
261
262
263 /*
264  * Flush the buffer associated with the given file descriptor
265  * The special value, SIO_FLUSH_ALL flushes all buffers
266  *
267  * Return value:
268  *                      0 :  if fd is SIO_FLUSH_ALL or if the flush is successful
269  *              SIO_ERR: if fd is not SIO_FLUSH_ALL and
270  *                                                              the flush is unsuccessful
271  *                                                      or the descriptor is not initialized or it is not 
272  *                                                              an output descriptor
273  */
274 int Sflush( int fd )
275 {
276    __sio_descriptor_t *dp ;
277    int b_in_buffer ;
278
279    if ( fd == SIO_FLUSH_ALL )
280    {
281       for ( fd = 0, dp = __sio_descriptors ;
282                                 fd < __sio_n_descriptors ;
283                                 dp++, fd++ )
284          if ( DESCRIPTOR_INITIALIZED( dp ) &&
285                                                         dp->stream_type == __SIO_OUTPUT_STREAM )
286             (void) __sio_writef( ODP( dp ), fd ) ;
287       return( 0 ) ;
288    }
289    else
290    {
291       if ( fd >= __sio_n_descriptors ) {
292          errno = EBADF ;
293          return( SIO_ERR ) ;
294       }
295
296       dp = &__sio_descriptors[ fd ] ;
297
298       if( (! DESCRIPTOR_INITIALIZED( dp )) || 
299             (dp->stream_type != __SIO_OUTPUT_STREAM) )
300          return( SIO_ERR );
301
302       b_in_buffer = ODP( dp )->nextb - ODP( dp )->start ;
303       if ( __sio_writef( ODP( dp ), fd ) != b_in_buffer )
304          return( SIO_ERR ) ;
305       else
306          return( 0 ) ;
307    }
308 }
309
310
311 /*
312  * Close the file descriptor. This call is provided because
313  * a file descriptor may be closed and then reopened. There is
314  * no easy way for SIO to identify such a situation, so Sclose
315  * must be used.
316  *
317  * Sclose invokes Sdone which finalizes the buffer.
318  * There is no SIO_CLOSE_ALL value for fd because such a thing
319  * would imply that the program will exit very soon, therefore
320  * the closing of all file descriptors will be done in the kernel
321  * (and the finalization will be done by the finalization function
322  * NOTE: not true if the OS does not support a finalization function)
323  *
324  * There is no need to invoke SETUP; Sdone will do it.
325  */
326 int Sclose( int fd )
327 {
328         if ( __SIO_FD_INITIALIZED( fd ) ) {
329                 if ( Sdone( fd ) == SIO_ERR ) {
330                         close( fd ); 
331                         return( SIO_ERR ) ;
332                 }
333         }
334         return( close( fd ) ) ;
335 }
336
337
338 /*
339  * Changes the type of buffering on the specified descriptor.
340  * As a side-effect, it initializes the descriptor as an output stream.
341  */
342 int Sbuftype( int fd, int type )
343 {
344         __sio_descriptor_t *dp ;
345
346         /*
347          * Check for a valid type
348          */
349         if ( type != SIO_LINEBUF && type != SIO_FULLBUF && type != SIO_NOBUF )
350         {
351                 errno = EINVAL ;
352                 return( SIO_ERR ) ;
353         }
354
355         if( sio_setup( fd, &dp, __SIO_OUTPUT_STREAM ) == SIO_ERR )
356                 return( SIO_ERR );
357         ODP( dp )->buftype = type ;
358         return( 0 ) ;
359 }
360
361
362 #ifndef sio_memscan
363
364 static char *sio_memscan( const char *from, int how_many, char ch )
365 {
366    char *p ;
367    char *last = from + how_many ;
368
369    for ( p = from ; p < last ; p++ )
370       if ( *p == ch )
371          return( p ) ;
372       return( 0 ) ;
373 }
374
375 #endif  /* sio_memscan */
376
377
378 #ifdef NEED_MEMCOPY
379
380 void __sio_memcopy( const char *from, char *to, int nbytes )
381 {
382    while ( nbytes-- )
383       *to++ = *from++ ;
384 }
385
386 #endif /* NEED_MEMCOPY */
387
388 int sio_setup(int fd, __sio_descriptor_t **dp, unsigned int type)
389 {
390    if ( fd >= __sio_n_descriptors ) {
391       if( Smorefds(fd) != 0 ) {
392          errno = EBADF;
393          return(SIO_ERR);
394       }
395    }
396
397    *dp = &__sio_descriptors[ fd ];
398
399    if( DESCRIPTOR_INITIALIZED( *dp ) ) {
400       if( (*dp)->stream_type != type ) {
401          errno = EBADF;
402          return( SIO_ERR );
403       }
404    } else if( __sio_init(*dp, fd, type) == SIO_ERR )
405       return( SIO_ERR );
406
407    return( 0 );
408 }
409
410 /*
411  * The Sputchar function depends on the fact that the fields
412  *                              nextb, buf_end, end
413  * are 0 if a stream descriptor is not being used or has not yet been
414  * initialized.
415  * This is true initially because of the static allocation of the
416  * descriptor array, and Sdone must make sure that it is true
417  * after I/O on a descriptor is over.
418  */
419 int Sputchar( int fd, char c )
420 {
421    int ret = 0;
422
423    if( __SIO_OD( fd ).nextb < __SIO_OD( fd ).buf_end ) {
424       if( __SIO_OD(fd).buftype == SIO_FULLBUF ) {
425          ret = *(__SIO_OD(fd).nextb)++ = (unsigned char) (c);
426       } else if ( __SIO_OD(fd).buftype == SIO_LINEBUF ) {
427          if( (*(__SIO_OD(fd).nextb) = (unsigned char)(c)) != '\n' ) {
428             ret = *(__SIO_OD(fd).nextb)++;
429          } else {
430             ret = Sputc( fd, *(__SIO_OD(fd).nextb) );
431          }
432       } else {
433          ret = Sputc( fd, c );
434       }
435    } else {
436       ret = Sputc( fd, c );
437    }
438
439    return ret;
440 }
441
442