]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/language/c/libc/stdio/v2_0/src/common/stream.cxx
42891a2827a92e8863672a4f3308055540895512
[karo-tx-redboot.git] / packages / language / c / libc / stdio / v2_0 / src / common / stream.cxx
1 //========================================================================
2 //
3 //      stream.cxx
4 //
5 //      Implementations of internal C library stdio stream functions
6 //
7 //========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 //
13 // eCos is free software; you can redistribute it and/or modify it under
14 // the terms of the GNU General Public License as published by the Free
15 // Software Foundation; either version 2 or (at your option) any later version.
16 //
17 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
18 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
20 // for more details.
21 //
22 // You should have received a copy of the GNU General Public License along
23 // with eCos; if not, write to the Free Software Foundation, Inc.,
24 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 //
26 // As a special exception, if other files instantiate templates or use macros
27 // or inline functions from this file, or you compile this file and link it
28 // with other works to produce a work based on this file, this file does not
29 // by itself cause the resulting work to be covered by the GNU General Public
30 // License. However the source code for this file must still be made available
31 // in accordance with section (3) of the GNU General Public License.
32 //
33 // This exception does not invalidate any other reasons why a work based on
34 // this file might be covered by the GNU General Public License.
35 //
36 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
37 // at http://sources.redhat.com/ecos/ecos-license/
38 // -------------------------------------------
39 //####ECOSGPLCOPYRIGHTEND####
40 //========================================================================
41 //#####DESCRIPTIONBEGIN####
42 //
43 // Author(s):     jlarmour
44 // Contributors:  
45 // Date:          2000-04-20
46 // Purpose:     
47 // Description: 
48 // Usage:       
49 //
50 //####DESCRIPTIONEND####
51 //
52 //========================================================================
53
54 // CONFIGURATION
55
56 #include <pkgconf/libc_stdio.h>   // Configuration header
57
58 // INCLUDES
59
60 #include <cyg/infra/cyg_type.h>    // Common project-wide type definitions
61 #include <cyg/infra/cyg_ass.h>     // Assertion infrastructure
62 #include <stddef.h>                // NULL and size_t from compiler
63 #include <errno.h>                 // Error codes
64 #include <string.h>                // memcpy() and memset()
65 #include <cyg/libc/stdio/stream.hxx>     // Header for this file
66 #include <cyg/libc/stdio/stdiosupp.hxx>  // Stdio support functions
67
68
69 #include <cyg/libc/stdio/io.inl>     // I/O system inlines
70
71
72 // FUNCTIONS
73
74 Cyg_StdioStream::Cyg_StdioStream(cyg_stdio_handle_t dev,
75                                  OpenMode open_mode,
76                                  cyg_bool append, cyg_bool binary,
77                                  int buffer_mode, cyg_ucount32 buffer_size,
78                                  cyg_uint8 *buffer_addr )
79 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
80     : io_buf( buffer_size, buffer_addr )
81 #endif
82 {
83     initialize( dev, open_mode, append, binary, buffer_mode,
84                 buffer_size, buffer_addr);
85 }
86
87 void Cyg_StdioStream::initialize(cyg_stdio_handle_t dev,
88                                  OpenMode open_mode,
89                                  cyg_bool append, cyg_bool binary,
90                                  int buffer_mode, cyg_ucount32 buffer_size,
91                                  cyg_uint8 *buffer_addr )
92 {
93
94 #ifdef CYGDBG_USE_ASSERTS
95     magic_validity_word = 0xbadbad;
96 #endif
97
98     my_device = dev;
99
100     // Clear all flags
101     memset( &flags, 0, sizeof(flags) );
102
103     switch (open_mode) {
104     case CYG_STREAM_READ:
105         flags.opened_for_read = true;
106         break;
107     case CYG_STREAM_WRITE:
108         flags.opened_for_write = true;
109         break;
110     case CYG_STREAM_READWRITE_NOCREATE:
111     case CYG_STREAM_READWRITE_CREATE:
112         flags.opened_for_read = true;
113         flags.opened_for_write = true;
114         break;
115     default:
116         error=EINVAL;
117         return;
118     } // switch
119         
120     
121     if (flags.opened_for_write) {
122 #if 0
123         // FIXME: need some replacement for this
124         if (!my_device->write_blocking) {
125             error = EDEVNOSUPP;
126             return;
127         } // if
128 #endif
129 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
130         flags.last_buffer_op_was_read = false;
131 #endif
132     } // if
133
134
135     if (flags.opened_for_read) {
136 #if 0
137         // FIXME: need some replacement for this
138         if (!my_device->read_blocking) {
139             error = EDEVNOSUPP;
140             return;
141         } // if
142 #endif
143
144         // NB also if opened for read AND write, then say last op was read
145 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
146         flags.last_buffer_op_was_read = true;
147 #endif
148     } // if
149
150     flags.binary = binary ? 1 : 0;
151
152     error = ENOERR;
153     
154     // in due course we would do an equivalent to fseek(...,0, SEEK_END);
155     // when appending. for now, there's nothing, except set eof
156     
157     flags.at_eof = append ? 1 : 0;
158
159     position = 0;
160
161 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
162
163     switch (buffer_mode) {
164     case _IONBF:
165         CYG_ASSERT( (buffer_size == 0) && (buffer_addr == NULL),
166                     "No buffering wanted but size/address specified!" );
167         flags.buffering = flags.line_buffering = false;
168         break;
169     case _IOLBF:
170         flags.buffering = true;
171         flags.line_buffering = true;
172         break;
173     case _IOFBF:
174         flags.buffering = true;
175         flags.line_buffering = false;
176         break;
177     default:
178         error = EINVAL;
179         return;
180     } // switch
181
182     // one way of checking the buffer was set up correctly
183     if (flags.buffering && io_buf.get_buffer_size()==-1) {
184         error = ENOMEM;
185         return;
186     }
187
188 #endif
189
190 #if 0 // FIXME - Need to set binary mode.
191     if (my_device->open) {
192         error = (*my_device->open)( my_device->cookie, 
193                                     binary ? CYG_DEVICE_OPEN_MODE_RAW
194                                            : CYG_DEVICE_OPEN_MODE_TEXT );
195         if (error != ENOERR)
196             return; // keep error code the same
197     } // if
198     
199 #endif
200
201 #ifdef CYGDBG_USE_ASSERTS
202     magic_validity_word = 0x7b4321ce;
203 #endif
204     
205 } // Cyg_StdioStream constructor
206
207
208 Cyg_StdioStream::Cyg_StdioStream( OpenMode open_mode,
209                                  cyg_ucount32 buffer_size,
210                                   cyg_uint8 *buffer_addr )
211 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
212     : io_buf( buffer_size, buffer_addr )
213 #endif
214 {
215     initialize( CYG_STDIO_HANDLE_NULL, open_mode, false, false, _IOFBF,
216                 buffer_size, buffer_addr );
217
218     if( error != ENOERR )
219         return;
220     
221     switch( open_mode )
222     {
223     case CYG_STREAM_READ:
224         // Fix up the stream so it looks like the buffer contents has just
225         // been read in.
226 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
227         io_buf.set_bytes_written( buffer_size );
228 #endif
229         break;
230         
231     case CYG_STREAM_WRITE:
232         // Fix up the stream so it looks like the buffer is ready to accept
233         // new data.
234         break;
235
236     default:
237         error = EINVAL;
238         return;
239     }
240 }
241
242
243 Cyg_ErrNo
244 Cyg_StdioStream::refill_read_buffer( void )
245 {
246     Cyg_ErrNo read_err;
247     cyg_uint8 *buffer;
248     cyg_uint32 len;
249
250     CYG_ASSERTCLASS( this, "Stream object is not a valid stream!" );
251     
252     if (!lock_me())
253         return EBADF;  // assume file is now invalid
254
255     // first just check that we _can_ read this device!
256     if (!flags.opened_for_read) {
257         unlock_me();
258         return EINVAL;
259     }
260     
261 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
262     // If there is pending output to write, then this will check and
263     // write it
264     if (flags.buffering) {
265         read_err = flush_output_unlocked();
266
267         // we're now reading
268         flags.last_buffer_op_was_read = true;
269
270         // flush ALL streams
271         if (read_err == ENOERR)
272             read_err = cyg_libc_stdio_flush_all_but(this);
273
274         if (read_err != ENOERR) {
275             unlock_me();
276             return read_err;
277         } // if
278
279         len = io_buf.get_buffer_addr_to_write( (cyg_uint8**)&buffer );
280         if (!len) { // no buffer space available
281             unlock_me();
282             return ENOERR;  // isn't an error, just needs user to read out data
283         } // if
284     }
285     else
286 #endif
287
288     if (!flags.readbuf_char_in_use) {
289         len = 1;
290         buffer = &readbuf_char;
291     }
292     else {
293         // no buffer space available
294         unlock_me();
295         return ENOERR;  // isn't an error, just needs user to read out data
296     } // else
297
298     read_err = cyg_stdio_read(my_device, buffer, &len);
299
300
301 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
302     if (flags.buffering)
303         io_buf.set_bytes_written( len );
304     else
305 #endif
306         flags.readbuf_char_in_use = len ? 1 : 0;
307
308     unlock_me();
309
310     if (read_err == ENOERR) {
311         if (len == 0) {
312             read_err = EAGAIN;
313             flags.at_eof = true;
314         }
315         else
316             flags.at_eof = false;
317     } // if
318     
319     return read_err;
320 } // refill_read_buffer()
321
322
323 Cyg_ErrNo
324 Cyg_StdioStream::read( cyg_uint8 *user_buffer, cyg_ucount32 buffer_length,
325                        cyg_ucount32 *bytes_read )
326 {
327     Cyg_ErrNo read_err=ENOERR;
328     CYG_ASSERTCLASS( this, "Stream object is not a valid stream!" );
329     
330     *bytes_read = 0;
331
332     if (!lock_me())
333         return EBADF;  // assume file is now invalid
334
335     if (!flags.opened_for_read) {
336         unlock_me();
337         return EINVAL;
338     }
339
340 #ifdef CYGFUN_LIBC_STDIO_ungetc
341     if (flags.unread_char_buf_in_use && buffer_length) {
342         *user_buffer++ = unread_char_buf;
343         ++*bytes_read;
344         flags.unread_char_buf_in_use = false;
345         --buffer_length;
346     } // if
347
348 #endif // ifdef CYGFUN_LIBC_STDIO_ungetc
349
350 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
351     if (flags.buffering) {
352
353         // need to flush output if we were writing before
354         if (!flags.last_buffer_op_was_read) {
355             Cyg_ErrNo err = flush_output_unlocked();
356
357             if (ENOERR != err) {
358                 unlock_me();
359                 return err;
360             }            
361         }
362             
363         cyg_uint8 *buff_to_read_from;
364         cyg_ucount32 bytes_available;
365     
366         bytes_available = io_buf.get_buffer_addr_to_read(
367               (cyg_uint8 **)&buff_to_read_from );
368         
369         cyg_ucount32 count =
370             (bytes_available < buffer_length) ? bytes_available : buffer_length;
371
372         if (count) {
373             memcpy( user_buffer, buff_to_read_from, count );
374             io_buf.set_bytes_read( count );
375             *bytes_read += count;
376         } // if
377
378     } // if
379     else
380         
381 #endif
382
383     if (flags.readbuf_char_in_use && buffer_length) {
384         *user_buffer = readbuf_char;
385         *bytes_read = 1;
386         flags.readbuf_char_in_use = false;
387     }
388
389     position += *bytes_read;
390     
391
392     // if we are unbuffered, we read as much as we can directly from the 
393     // file system at this point.
394     //
395     // unless we do this, we could end up reading byte-by-byte from the filing system
396     // due to the readbuf_char scheme.
397     if (
398 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
399         !flags.buffering &&
400 #endif
401         (*bytes_read<buffer_length)) {
402         cyg_uint32 len;
403         len=buffer_length-*bytes_read;
404         read_err = cyg_stdio_read(my_device, user_buffer + *bytes_read, &len);      
405         *bytes_read+=len;
406     }
407     
408     unlock_me();
409
410     return read_err;
411 } // read()
412
413
414 Cyg_ErrNo
415 Cyg_StdioStream::read_byte( cyg_uint8 *c )
416 {
417     Cyg_ErrNo err=ENOERR;
418
419     CYG_ASSERTCLASS( this, "Stream object is not a valid stream!" );
420     
421     if (!lock_me())
422         return EBADF;  // assume file is now invalid
423
424     if (!flags.opened_for_read) {
425         unlock_me();
426         return EINVAL;
427     }
428
429 # ifdef CYGFUN_LIBC_STDIO_ungetc
430     if (flags.unread_char_buf_in_use) {
431         *c = unread_char_buf;
432         flags.unread_char_buf_in_use = false;
433         position++;
434         unlock_me();
435         return ENOERR;
436     } // if
437 # endif // ifdef CYGFUN_LIBC_STDIO_ungetc
438
439 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
440     if (flags.buffering) {
441         // need to flush output if we were writing before
442         if (!flags.last_buffer_op_was_read)
443             err = flush_output_unlocked();
444
445         if (ENOERR != err) {
446             unlock_me();
447             return err;
448         }            
449             
450         cyg_uint8 *buff_to_read_from;
451         cyg_ucount32 bytes_available;
452     
453         bytes_available=io_buf.get_buffer_addr_to_read(&buff_to_read_from);
454
455         if (bytes_available) {
456             *c = *buff_to_read_from;
457             io_buf.set_bytes_read(1);
458             position++;
459         }
460         else
461             err = EAGAIN;
462     } // if
463     else
464     
465 #endif
466
467
468     if (flags.readbuf_char_in_use) {
469         *c = readbuf_char;
470         flags.readbuf_char_in_use = false;
471         position++;
472     }
473     else
474         err = EAGAIN;
475
476     unlock_me();
477
478     return err;
479 } // read_byte()
480
481
482 Cyg_ErrNo
483 Cyg_StdioStream::peek_byte( cyg_uint8 *c )
484 {
485     Cyg_ErrNo err=ENOERR;
486
487     CYG_ASSERTCLASS( this, "Stream object is not a valid stream!" );
488     
489     if (!lock_me())
490         return EBADF;  // assume file is now invalid
491
492     if (!flags.opened_for_read) {
493         unlock_me();
494         return EINVAL;
495     }
496
497     // this should really only be called after refill_read_buffer, but just
498     // in case
499 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
500     if (flags.buffering)
501         err = flush_output_unlocked();
502
503     if (err != ENOERR)
504         return err;
505
506     // we're now reading
507     flags.last_buffer_op_was_read = true;
508 #endif
509
510 # ifdef CYGFUN_LIBC_STDIO_ungetc
511     if (flags.unread_char_buf_in_use) {
512         *c = unread_char_buf;
513         unlock_me();
514         return ENOERR;
515     } // if
516 # endif // ifdef CYGFUN_LIBC_STDIO_ungetc
517
518 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
519     if (flags.buffering) {
520         cyg_uint8 *buff_to_read_from;
521         cyg_ucount32 bytes_available;
522     
523         bytes_available=io_buf.get_buffer_addr_to_read(&buff_to_read_from);
524
525         if (bytes_available) {
526             *c = *buff_to_read_from;
527         }
528         else
529             err = EAGAIN;
530     } // if
531     else
532     
533 #endif
534
535
536     if (flags.readbuf_char_in_use) {
537         *c = readbuf_char;
538     }
539     else
540         err = EAGAIN;
541
542     unlock_me();
543
544     return err;
545 } // peek_byte()
546
547
548 Cyg_ErrNo
549 Cyg_StdioStream::flush_output_unlocked( void )
550 {
551 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
552     Cyg_ErrNo write_err=ENOERR;
553     cyg_uint8 *buffer;
554     cyg_uint32 len;
555
556     CYG_ASSERTCLASS( this, "Stream object is not a valid stream!" );
557     
558     if ( flags.last_buffer_op_was_read )
559         return ENOERR;
560
561     // first just check that we _can_ write to the device!
562     if ( !flags.opened_for_write )
563         return EINVAL;
564
565     // shortcut if nothing to do
566     if (io_buf.get_buffer_space_used() == 0)
567         return ENOERR;
568         
569     len = io_buf.get_buffer_addr_to_read( (cyg_uint8 **)&buffer );
570     
571     CYG_ASSERT( len > 0, 
572                 "There should be data to read but there isn't!");
573
574     write_err = cyg_stdio_write(my_device, buffer, &len);
575
576     // since we're doing a concerted flush, we tell the I/O layer to
577     // flush too, otherwise output may just sit there forever
578     if (!write_err)
579         cyg_stdio_flush( my_device );
580     
581         // we've just read it all, so just wipe it out
582     io_buf.drain_buffer();
583
584     return write_err;
585
586 #else // ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
587
588     CYG_ASSERTCLASS( this, "Stream object is not a valid stream!" );
589     
590     return ENOERR;
591
592 #endif // ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
593 } // flush_output_unlocked()
594
595
596
597 Cyg_ErrNo
598 Cyg_StdioStream::write( const cyg_uint8 *buffer,
599                         cyg_ucount32 buffer_length,
600                         cyg_ucount32 *bytes_written )
601 {
602     Cyg_ErrNo write_err = ENOERR;
603
604     CYG_ASSERTCLASS( this, "Stream object is not a valid stream!" );
605     
606     *bytes_written = 0;
607
608     if (!lock_me())
609         return EBADF;  // assume file is now invalid
610
611     // first just check that we _can_ write to the device!
612     if ( !flags.opened_for_write ) {
613         unlock_me();
614         return EINVAL;
615     }
616
617 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
618     if (flags.last_buffer_op_was_read == true)
619         io_buf.drain_buffer();  // nuke input bytes to prevent confusion
620
621     flags.last_buffer_op_was_read = false;
622
623     if (!flags.buffering) {
624 #endif
625         cyg_uint32 len = buffer_length;
626
627         write_err = cyg_stdio_write(my_device, buffer, &len);
628
629         *bytes_written = len;
630
631 #ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
632     } // if
633     else {
634         cyg_ucount32 bytes_available;
635         cyg_ucount32 bytes_to_write;
636         cyg_ucount32 newline_pos;
637         cyg_uint8 *write_addr;
638         cyg_bool must_flush = false;
639         
640         while ( buffer_length > 0 ) {
641             bytes_available =
642                 io_buf.get_buffer_addr_to_write( &write_addr );
643             
644             // we need to flush if we've no room or the buffer has an up
645             // and coming newline
646             if ( !bytes_available || must_flush ) {
647                 write_err = flush_output_unlocked();
648                 
649                 // harmless even if there was an error
650                 bytes_available =
651                     io_buf.get_buffer_addr_to_write( &write_addr );
652
653                 CYG_ASSERT( bytes_available > 0,
654                             "Help! still no bytes available in "
655                             "write buffer" );
656             } // if
657             
658             if (write_err) {
659                 unlock_me();
660                 return write_err;
661             } // if
662             
663             // choose the lower of the buffer available and the length
664             // to write
665             bytes_to_write=(bytes_available < buffer_length) 
666                 ? bytes_available
667                 : buffer_length;
668         
669             // if we're line buffered, we may want want to flush if there's
670             // a newline character, so lets find out
671         
672             if (flags.line_buffering) {
673                 for (newline_pos=0;
674                      newline_pos<bytes_to_write;
675                      newline_pos++) {
676                     if (buffer[newline_pos] == '\n') {
677                         break;
678                     } // if
679                 } // for
680                 // if we didn't reach the end
681                 if (newline_pos != bytes_to_write) {
682                     // shrink bytes_to_write down to the bit we need to
683                     // flush including the newline itself
684                     bytes_to_write = newline_pos + 1;
685                     must_flush = true;
686                 } // if
687             } // if
688             
689             memcpy( write_addr, buffer, bytes_to_write );
690             
691             *bytes_written += bytes_to_write;
692             buffer += bytes_to_write;
693             buffer_length -= bytes_to_write;
694             io_buf.set_bytes_written( bytes_to_write );
695
696             position += bytes_to_write;
697             
698         } // while
699         
700         if ( must_flush ) {
701             write_err = flush_output_unlocked();
702         } // if
703     } // else
704 #endif // ifdef CYGSEM_LIBC_STDIO_WANT_BUFFERED_IO
705
706     unlock_me();
707
708     return write_err;
709 } // write()
710
711 // EOF stream.cxx