2 * AcessOS Basic C Library
\r
6 #include <acess/sys.h>
\r
11 #include "stdio_int.h"
\r
15 #define DEBUG_BUILD 0
\r
17 // === CONSTANTS ===
\r
21 #define FD_NOTOPEN -1
\r
22 #define FD_MEMFILE -2
\r
23 #define FD_MEMSTREAM -3
\r
25 // === PROTOTYPES ===
\r
26 struct sFILE *get_file_struct();
\r
29 struct sFILE _iob[STDIO_MAX_STREAMS]; // IO Buffer
\r
30 struct sFILE *stdin; // Standard Input
\r
31 struct sFILE *stdout; // Standard Output
\r
32 struct sFILE *stderr; // Standard Error
\r
33 ///\note Initialised in SoMain
\r
34 static const int STDIN_BUFSIZ = 512;
\r
35 static const int STDOUT_BUFSIZ = 512;
\r
38 void _stdio_init(void)
\r
40 // Init FileIO Pointers
\r
43 stdin->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_READ|FILE_FLAG_LINEBUFFERED|FILE_FLAG_OURBUFFER;
\r
44 stdin->Buffer = malloc(STDIN_BUFSIZ);
\r
45 stdin->BufferSpace = STDIN_BUFSIZ;
\r
49 stdout->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE|FILE_FLAG_LINEBUFFERED|FILE_FLAG_OURBUFFER;
\r
50 stdout->Buffer = malloc(STDOUT_BUFSIZ);
\r
51 stdout->BufferSpace = STDOUT_BUFSIZ;
\r
55 stderr->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE;
\r
58 void _stdio_cleanup(void)
\r
61 for( i = 0; i < STDIO_MAX_STREAMS; i ++ )
\r
67 int _fopen_modetoflags(const char *mode)
\r
74 case 'r': flags = FILE_FLAG_MODE_READ; break;
\r
75 case 'w': flags = FILE_FLAG_MODE_WRITE; break;
\r
76 case 'a': flags = FILE_FLAG_MODE_APPEND; break;
\r
77 case 'x': flags = FILE_FLAG_MODE_EXEC; break; // Acess addon
\r
84 for( ; *mode; mode ++ )
\r
88 case 'b': flags |= FILE_FLAG_M_BINARY; break;
\r
89 case '+': flags |= FILE_FLAG_M_EXT; break;
\r
99 * \fn FILE *freopen(char *file, char *mode, FILE *fp)
\r
101 EXPORT FILE *freopen(const char *file, const char *mode, FILE *fp)
\r
105 // Sanity Check Arguments
\r
106 if(!fp || !file || !mode) return NULL;
\r
108 if(fp->FD != FD_NOTOPEN) {
\r
113 fp->Flags = _fopen_modetoflags(mode);
\r
114 if(fp->Flags == -1)
\r
118 switch(fp->Flags & FILE_FLAG_MODE_MASK)
\r
121 case FILE_FLAG_MODE_READ:
\r
122 openFlags = OPENFLAG_READ;
\r
123 if(fp->Flags & FILE_FLAG_M_EXT)
\r
124 openFlags |= OPENFLAG_WRITE;
\r
127 case FILE_FLAG_MODE_WRITE:
\r
128 openFlags = OPENFLAG_WRITE;
\r
129 if(fp->Flags & FILE_FLAG_M_EXT)
\r
130 openFlags |= OPENFLAG_READ;
\r
133 case FILE_FLAG_MODE_APPEND:
\r
134 openFlags = OPENFLAG_APPEND;
\r
135 if(fp->Flags & FILE_FLAG_M_EXT)
\r
136 openFlags |= OPENFLAG_READ;
\r
139 case FILE_FLAG_MODE_EXEC:
\r
140 openFlags = OPENFLAG_EXEC;
\r
145 if(fp->FD != FD_NOTOPEN)
\r
146 fp->FD = _SysReopen(fp->FD, file, openFlags);
\r
148 fp->FD = _SysOpen(file, openFlags);
\r
154 // Default to buffered
\r
155 // - Disabled until fseek() is fixed
\r
159 fp->BufferSpace = BUFSIZ;
\r
160 fp->Buffer = malloc( fp->BufferSpace );
\r
161 fp->Flags |= FILE_FLAG_OURBUFFER;
\r
164 if( (fp->Flags & FILE_FLAG_MODE_MASK) == FILE_FLAG_MODE_APPEND ) {
\r
165 _SysSeek(fp->FD, 0, SEEK_END); //SEEK_END
\r
171 \fn FILE *fopen(const char *file, const char *mode)
\r
172 \brief Opens a file and returns the pointer
\r
173 \param file String - Filename to open
\r
174 \param mode Mode to open in
\r
176 EXPORT FILE *fopen(const char *file, const char *mode)
\r
180 // Sanity Check Arguments
\r
181 if(!file || !mode) return NULL;
\r
183 // Create Return Structure
\r
184 retFile = get_file_struct();
\r
186 return freopen(file, mode, retFile);
\r
189 EXPORT FILE *fmemopen(void *buffer, size_t length, const char *mode)
\r
193 if( !buffer || !mode ) return NULL;
\r
195 ret = get_file_struct();
\r
197 ret->FD = FD_MEMFILE;
\r
198 ret->Flags = _fopen_modetoflags(mode);
\r
199 if(ret->Flags == -1) {
\r
204 ret->Buffer = buffer;
\r
205 ret->BufferPos = 0;
\r
206 ret->BufferSpace = length;
\r
211 EXPORT FILE *open_memstream(char **bufferptr, size_t *lengthptr)
\r
213 FILE *ret = get_file_struct();
\r
214 ret->FD = FD_MEMSTREAM;
\r
215 ret->Flags = FILE_FLAG_MODE_WRITE;
\r
217 ret->Buffer = NULL;
\r
218 ret->BufferPos = 0;
\r
219 ret->BufferSpace = 0;
\r
220 ret->BufPtr = bufferptr;
\r
221 ret->LenPtr = lengthptr;
\r
226 EXPORT FILE *fdopen(int fd, const char *mode)
\r
230 if( fd < 0 || !mode ) return NULL;
\r
232 ret = get_file_struct();
\r
235 ret->Flags = _fopen_modetoflags(mode);
\r
236 if(ret->Flags == -1) {
\r
241 ret->Buffer = NULL;
\r
242 ret->BufferPos = 0;
\r
243 ret->BufferSpace = 0;
\r
248 EXPORT FILE *tmpfile(void)
\r
253 EXPORT int fclose(FILE *fp)
\r
255 if( !(fp->Flags & FILE_FLAG_ALLOC) )
\r
258 if( fp->FD >= 0 ) {
\r
260 if( fp->Buffer && (fp->Flags & FILE_FLAG_OURBUFFER) ) {
\r
266 fp->FD = FD_NOTOPEN;
\r
270 EXPORT int setvbuf(FILE *fp, char *buffer, int mode, size_t size)
\r
277 // Check for memory files
\r
278 if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM ) {
\r
283 // Not strictly needed, as this should only be called before any IO
\r
286 // Eliminate any pre-existing buffer
\r
288 free( fp->Buffer );
\r
290 fp->BufferSpace = 0;
\r
294 if( mode == _IONBF ) {
\r
295 // Do nothing, buffering was disabled above
\r
299 // Sanity check buffering mode before allocating
\r
300 if( mode != _IOLBF && mode != _IOFBF ) {
\r
304 // Allocate a buffer if one was not provided
\r
306 fp->Flags |= FILE_FLAG_OURBUFFER;
\r
307 buffer = malloc(size);
\r
311 fp->Flags &= ~FILE_FLAG_OURBUFFER;
\r
314 // Set buffer pointer and size
\r
315 fp->Buffer = buffer;
\r
316 fp->BufferSpace = size;
\r
319 if( mode == _IOLBF )
\r
320 fp->Flags |= FILE_FLAG_LINEBUFFERED;
\r
322 fp->Flags &= ~FILE_FLAG_LINEBUFFERED;
\r
328 int _fflush_int(FILE *fp)
\r
333 // Check the buffer contains data
\r
334 if( fp->BufferPos == 0 )
\r
337 switch(fp->Flags & FILE_FLAG_MODE_MASK)
\r
339 // Read - Flush input buffer
\r
340 case FILE_FLAG_MODE_READ:
\r
344 // Append - Seek to end and write
\r
345 case FILE_FLAG_MODE_APPEND:
\r
346 _SysSeek(fp->FD, fp->BufferOfs, SEEK_SET);
\r
347 len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);
\r
348 if( len != fp->BufferPos )
\r
350 if( len <= fp->BufferPos )
\r
352 fp->BufferPos -= len;
\r
354 fp->BufferOfs = _SysTell(fp->FD);
\r
357 // Write - Write buffer
\r
358 case FILE_FLAG_MODE_WRITE:
\r
359 //_SysDebug("Flushing to %i '%.*s'", fp->FD, fp->BufferPos, fp->Buffer);
\r
360 len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);
\r
361 if( len != fp->BufferPos )
\r
363 if( len <= fp->BufferPos )
\r
365 fp->BufferPos -= len;
\r
368 // _SysDebug("Flush of %i failed, %s", fp->FD, strerror(errno));
\r
377 EXPORT void fflush(FILE *fp)
\r
379 if( !fp || fp->FD == FD_NOTOPEN )
\r
382 // Nothing to do for memory files
\r
383 if( fp->FD == FD_MEMFILE )
\r
385 // Memory streams, update pointers
\r
386 if( fp->FD == FD_MEMSTREAM ) {
\r
387 *fp->BufPtr = fp->Buffer;
\r
388 *fp->LenPtr = fp->BufferPos;
\r
395 EXPORT void clearerr(FILE *fp)
\r
397 if( !fp || fp->FD == FD_NOTOPEN )
\r
400 // TODO: Impliment clearerr()
\r
403 EXPORT int feof(FILE *fp)
\r
405 if( !fp || fp->FD == FD_NOTOPEN )
\r
407 return !!(fp->Flags & FILE_FLAG_EOF);
\r
410 EXPORT int ferror(FILE *fp)
\r
412 if( !fp || fp->FD == FD_NOTOPEN )
\r
416 EXPORT int fileno(FILE *stream)
\r
421 EXPORT off_t ftell(FILE *fp)
\r
423 if(!fp || fp->FD == FD_NOTOPEN) {
\r
428 if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM )
\r
431 return _SysTell(fp->FD);
\r
434 int _fseek_memfile(FILE *fp, long int amt, int whence)
\r
445 if( fp->BufferSpace < (size_t)amt )
\r
448 fp->Pos = fp->BufferSpace - amt;
\r
451 if(fp->Pos > (off_t)fp->BufferSpace) {
\r
452 fp->Pos = fp->BufferSpace;
\r
453 fp->Flags |= FILE_FLAG_EOF;
\r
458 int _fseek_memstream(FILE *fp, long int amt, int whence)
\r
469 if( fp->BufferSpace < (size_t)amt )
\r
472 fp->Pos = fp->BufferSpace - amt;
\r
475 if(fp->Pos > (off_t)fp->BufferSpace) {
\r
476 fp->Pos = fp->BufferSpace;
\r
477 fp->Flags |= FILE_FLAG_EOF;
\r
482 EXPORT int fseeko(FILE *fp, off_t amt, int whence)
\r
484 if(!fp || fp->FD == FD_NOTOPEN) {
\r
489 if( fp->FD == FD_MEMFILE ) {
\r
490 return _fseek_memfile(fp, amt, whence);
\r
492 else if( fp->FD == FD_MEMSTREAM ) {
\r
493 return _fseek_memstream(fp, amt, whence);
\r
497 _SysSeek(fp->FD, amt, whence);
\r
498 fp->Pos = _SysTell(fp->FD);
\r
503 EXPORT int fseek(FILE *fp, long int amt, int whence)
\r
505 return fseeko(fp, amt, whence);
\r
508 size_t _fwrite_unbuffered(FILE *fp, size_t size, size_t num, const void *data)
\r
510 size_t ret = 0, bytes;
\r
513 bytes = _SysWrite(fp->FD, data, size);
\r
514 if( bytes == (size_t)-1 ) {
\r
516 // TODO: Set error flag
\r
519 if( bytes != size ) {
\r
520 _SysDebug("_fwrite_unbuffered: Oops, rollback %i/%i bytes!", bytes, size);
\r
521 _SysSeek(fp->FD, -bytes, SEEK_CUR);
\r
524 data = (char*)data + size;
\r
526 fp->Pos += ret * size;
\r
530 size_t _fwrite_memfile(const void *ptr, size_t size, size_t num, FILE *fp)
\r
532 size_t avail = (fp->BufferSpace - fp->Pos) / size;
\r
534 fp->Flags |= FILE_FLAG_EOF;
\r
537 size_t bytes = num * size;
\r
538 memcpy(fp->Buffer + fp->Pos, ptr, bytes);
\r
543 size_t _fwrite_memstream(const void *ptr, size_t size, size_t num, FILE *fp)
\r
545 size_t bytes = size*num;
\r
546 // #1. Check if we need to expand
\r
547 if( fp->Pos + bytes > fp->BufferSpace )
\r
549 void *newbuf = realloc(fp->Buffer, fp->BufferSpace + bytes);
\r
554 fp->Buffer = newbuf;
\r
555 fp->BufferSpace = fp->Pos + bytes;
\r
557 // #2. Write (use the memfile code for that)
\r
558 return _fwrite_memfile(ptr, size, num, fp);
\r
562 * \fn EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)
\r
563 * \brief Write to a stream
\r
565 EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp)
\r
569 if(!fp || fp->FD == -1)
\r
571 if( size == 0 || num == 0 )
\r
574 switch( _GetFileMode(fp) )
\r
576 case FILE_FLAG_MODE_READ:
\r
577 case FILE_FLAG_MODE_EXEC:
\r
580 case FILE_FLAG_MODE_APPEND:
\r
581 fseek(fp, 0, SEEK_END);
\r
582 case FILE_FLAG_MODE_WRITE:
\r
583 // Handle memory files first
\r
584 if( fp->FD == FD_MEMFILE ) {
\r
585 return _fwrite_memfile(ptr, size, num, fp);
\r
587 else if( fp->FD == FD_MEMSTREAM ) {
\r
588 return _fwrite_memstream(ptr, size, num, fp);
\r
590 else if( fp->BufferSpace )
\r
592 // Buffering enabled
\r
593 if( fp->BufferSpace - fp->BufferPos < size*num )
\r
595 // If there's not enough space, flush and write-through
\r
596 _fflush_int(fp); // TODO: check ret
\r
597 ret = _fwrite_unbuffered(fp, size, num, ptr);
\r
599 else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) )
\r
601 // Newline present? Flush though
\r
602 _fflush_int(fp); // TODO: check ret
\r
603 ret = _fwrite_unbuffered(fp, size, num, ptr);
\r
608 memcpy( fp->Buffer + fp->BufferPos, ptr, size*num );
\r
609 fp->BufferPos += size*num;
\r
615 // Bufering disabled, write-though
\r
616 ret = _fwrite_unbuffered(fp, size, num, ptr);
\r
618 // errno should be set earlier
\r
625 size_t _fread_memfile(void *ptr, size_t size, size_t num, FILE *fp)
\r
627 size_t avail = (fp->BufferSpace - fp->Pos) / size;
\r
629 fp->Flags |= FILE_FLAG_EOF;
\r
630 if( num > avail ) num = avail;
\r
631 size_t bytes = num * size;
\r
632 memcpy(ptr, fp->Buffer + fp->Pos, bytes);
\r
638 size_t _fread_memstream(void *ptr, size_t size, size_t num, FILE *fp)
\r
645 size_t _fread_buffered(void *ptr, size_t size, FILE *fp)
\r
647 //_SysDebug("%p: %i-%i <= %i", fp,
\r
648 // (int)fp->Pos, (int)fp->BufferOfs, (int)fp->BufferPos);
\r
649 if( fp->BufferPos > 0 ) {
\r
650 assert( fp->Pos - fp->BufferOfs <= (int)fp->BufferPos );
\r
652 if( fp->BufferPos == 0 || fp->Pos - fp->BufferOfs == (int)fp->BufferPos )
\r
654 int rv = _SysRead(fp->FD, fp->Buffer, fp->BufferSpace);
\r
656 fp->Flags |= FILE_FLAG_EOF;
\r
660 fp->BufferPos = rv;
\r
661 fp->BufferOfs = fp->Pos;
\r
662 //_SysDebug("%p: Buffered %i at %i", fp, rv, fp->Pos);
\r
665 size_t inner_ofs = fp->Pos - fp->BufferOfs;
\r
666 if(size > fp->BufferPos - inner_ofs)
\r
667 size = fp->BufferPos - inner_ofs;
\r
669 //_SysDebug("%p: Read %i from %i+%i", fp, size,
\r
670 // (int)fp->BufferOfs, inner_ofs);
\r
671 memcpy(ptr, fp->Buffer + inner_ofs, size);
\r
677 * \fn EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)
\r
678 * \brief Read from a stream
\r
680 EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)
\r
684 if(!fp || fp->FD == -1)
\r
686 if( size == 0 || num == 0 )
\r
689 if( _GetFileMode(fp) != FILE_FLAG_MODE_READ ) {
\r
694 // Don't read if EOF is set
\r
695 if( fp->Flags & FILE_FLAG_EOF )
\r
699 if( fp->FD == FD_MEMFILE ) {
\r
700 return _fread_memfile(ptr, size, num, fp);
\r
702 else if( fp->FD == FD_MEMSTREAM ) {
\r
703 //return _fread_memstream(ptr, size, num, fp);
\r
709 const size_t bytes = size*num;
\r
711 // TODO: Buffered reads
\r
712 if( fp->BufferSpace )
\r
716 // While not done, and buffered read succeeds
\r
717 while( ofs < bytes && (rv = _fread_buffered((char*)ptr + ofs, bytes - ofs, fp)) != 0 )
\r
726 ret = _SysRead(fp->FD, ptr, bytes);
\r
727 if( ret == (size_t)-1)
\r
729 if( ret == 0 && bytes > 0 ) {
\r
730 fp->Flags |= FILE_FLAG_EOF;
\r
735 // if read was cut short
\r
738 size_t extra = ret - (ret / size) * size;
\r
739 // And it didn't fall short on a member boundary
\r
742 // Need to roll back the file pointer to the end of the last member
\r
743 _SysDebug("fread: TODO Roll back %zi bytes due to incomplete object (sz=%zi,n=%zi)",
\r
753 * \brief Write a string to a stream (without trailing \n)
\r
755 EXPORT int fputs(const char *s, FILE *fp)
\r
757 int len = strlen(s);
\r
758 return fwrite(s, len, 1, fp);
\r
762 * \brief Read a line (and possible trailing \n into a buffer)
\r
764 EXPORT char *fgets(char *s, int size, FILE *fp)
\r
768 while( ofs < size && ch != '\n' )
\r
770 if( fread(&ch, 1, 1, fp) != 1 )
\r
780 * \fn EXPORT int fputc(int c, FILE *fp)
\r
781 * \brief Write a single character to the stream
\r
783 EXPORT int fputc(int c, FILE *fp)
\r
786 return fwrite(&ch, 1, 1, fp);
\r
789 EXPORT int putchar(int c)
\r
792 return fputc(c, stdout);
\r
796 * \fn EXPORT int fgetc(FILE *fp)
\r
797 * \brief Read a character from the stream
\r
799 EXPORT int fgetc(FILE *fp)
\r
802 if( fread(&ret, 1, 1, fp) != 1 )
\r
807 EXPORT int getchar(void)
\r
810 return fgetc(stdin);
\r
813 EXPORT int puts(const char *str)
\r
817 int len = strlen(str);
\r
819 fwrite(str, 1, len, stdout);
\r
820 fwrite("\n", 1, 1, stdout);
\r
824 // --- INTERNAL ---
\r
826 * \fn FILE *get_file_struct()
\r
827 * \brief Returns a file descriptor structure
\r
829 FILE *get_file_struct()
\r
831 for(int i=0;i<STDIO_MAX_STREAMS;i++)
\r
833 if(_iob[i].Flags & FILE_FLAG_ALLOC)
\r
835 _iob[i].Flags |= FILE_FLAG_ALLOC;
\r