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