#include <string.h>\r
#include "lib.h"\r
#include "stdio_int.h"\r
-\r
-#define WRITE_STR(_fd, _str) write(_fd, _str, sizeof(_str))\r
+#include <errno.h>\r
+#include <assert.h>\r
\r
#define DEBUG_BUILD 0\r
\r
#define _stdout 1\r
\r
// === PROTOTYPES ===\r
-EXPORT void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned);\r
struct sFILE *get_file_struct();\r
\r
// === GLOBALS ===\r
struct sFILE *stdout; // Standard Output\r
struct sFILE *stderr; // Standard Error\r
///\note Initialised in SoMain\r
+static const int STDIN_BUFSIZ = 512;\r
+static const int STDOUT_BUFSIZ = 512;\r
\r
// === CODE ===\r
+void _stdio_init(void)\r
+{\r
+ // Init FileIO Pointers\r
+ stdin = &_iob[0];\r
+ stdin->FD = 0;\r
+ stdin->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_READ|FILE_FLAG_LINEBUFFERED;\r
+ stdin->Buffer = malloc(STDIN_BUFSIZ);\r
+ stdin->BufferSpace = STDIN_BUFSIZ;\r
+\r
+ stdout = &_iob[1];\r
+ stdout->FD = 1;\r
+ stdout->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE|FILE_FLAG_LINEBUFFERED;\r
+ stdout->Buffer = malloc(STDOUT_BUFSIZ);\r
+ stdout->BufferSpace = STDOUT_BUFSIZ;\r
+ \r
+ stderr = &_iob[2];\r
+ stderr->FD = 2;\r
+ stderr->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE;\r
+}\r
+\r
+void _stdio_cleanup(void)\r
+{\r
+ int i;\r
+ for( i = 0; i < STDIO_MAX_STREAMS; i ++ )\r
+ {\r
+ fclose( &_iob[i] );\r
+ }\r
+}\r
+\r
int _fopen_modetoflags(const char *mode)\r
{\r
int flags = 0;\r
}\r
\r
ret->Buffer = buffer;\r
- ret->BufferStart = 0;\r
- ret->BufferSize = length;\r
+ ret->BufferPos = 0;\r
+ ret->BufferSpace = length;\r
\r
return ret;\r
}\r
\r
EXPORT int fclose(FILE *fp)\r
{\r
+ if( !(fp->Flags & FILE_FLAG_ALLOC) )\r
+ return 0;\r
fflush(fp);\r
- if( fp->FD != -1 ) {\r
+ if( fp->FD >= 0 ) {\r
_SysClose(fp->FD);\r
}\r
fp->Flags = 0;\r
return 0;\r
}\r
\r
+EXPORT int setvbuf(FILE *fp, char *buffer, int mode, size_t size)\r
+{\r
+ if( !fp ) {\r
+ errno = EINVAL;\r
+ return 1;\r
+ }\r
+\r
+ // Check for memory files\r
+ if( fp->FD == -2 ) {\r
+ errno = EINVAL;\r
+ return 2;\r
+ } \r
+\r
+ // Not strictly needed, as this should only be called before any IO\r
+ fflush(fp);\r
+\r
+ // Eliminate any pre-existing buffer\r
+ if( fp->Buffer ) {\r
+ free( fp->Buffer );\r
+ fp->Buffer = NULL;\r
+ fp->BufferSpace = 0;\r
+ fp->BufferPos = 0;\r
+ }\r
+\r
+ if( mode == _IONBF ) {\r
+ // Do nothing, buffering was disabled above\r
+ }\r
+ else\r
+ {\r
+ // Sanity check buffering mode before allocating\r
+ if( mode != _IOLBF && mode != _IOFBF ) {\r
+ errno = EINVAL;\r
+ return 1;\r
+ }\r
+ // Allocate a buffer if one was not provided\r
+ if( !buffer ) {\r
+ buffer = malloc(size);\r
+ assert(buffer);\r
+ }\r
+ \r
+ // Set buffer pointer and size\r
+ fp->Buffer = buffer;\r
+ fp->BufferSpace = size;\r
+ \r
+ // Set mode flag\r
+ if( mode == _IOLBF )\r
+ fp->Flags |= FILE_FLAG_LINEBUFFERED;\r
+ else\r
+ fp->Flags &= ~FILE_FLAG_LINEBUFFERED;\r
+ }\r
+ \r
+ return 0;\r
+}\r
+\r
+int _fflush_int(FILE *fp)\r
+{\r
+ int ret = 0;\r
+ size_t len;\r
+ \r
+ // Check the buffer contains data\r
+ if( fp->BufferPos == 0 )\r
+ return 0;\r
+ \r
+ switch(fp->Flags & FILE_FLAG_MODE_MASK)\r
+ {\r
+ // Read - Flush input buffer\r
+ case FILE_FLAG_MODE_READ:\r
+ fp->BufferPos = 0;\r
+ break;\r
+ \r
+ // Append - Seek to end and write\r
+ case FILE_FLAG_MODE_APPEND:\r
+ _SysSeek(fp->FD, fp->BufferOfs, SEEK_SET);\r
+ len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);\r
+ if( len < fp->BufferPos )\r
+ ret = 1;\r
+ fp->BufferPos -= len;\r
+ fp->BufferOfs = _SysTell(fp->FD);\r
+ break;\r
+ \r
+ // Write - Write buffer\r
+ case FILE_FLAG_MODE_WRITE:\r
+ _SysDebug("Flushing to %i '%.*s'", fp->FD, fp->BufferPos, fp->Buffer);\r
+ len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);\r
+ if( len != fp->BufferPos )\r
+ ret = 1;\r
+ fp->BufferPos -= len;\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ return ret;\r
+}\r
+\r
EXPORT void fflush(FILE *fp)\r
{\r
if( !fp || fp->FD == -1 )\r
return ;\r
\r
- if( !(fp->Flags & FILE_FLAG_DIRTY) )\r
- return ;\r
- \r
// Nothing to do for memory files\r
if( fp->FD == -2 )\r
return ;\r
+ \r
+ _fflush_int(fp);\r
}\r
\r
EXPORT void clearerr(FILE *fp)\r
fp->Pos = amt;\r
break;\r
case SEEK_END:\r
- if( fp->BufferSize < (size_t)amt )\r
+ if( fp->BufferSpace < (size_t)amt )\r
fp->Pos = 0;\r
else\r
- fp->Pos = fp->BufferSize - amt;\r
+ fp->Pos = fp->BufferSpace - amt;\r
break;\r
}\r
- if(fp->Pos > (off_t)fp->BufferSize) {\r
- fp->Pos = fp->BufferSize;\r
+ if(fp->Pos > (off_t)fp->BufferSpace) {\r
+ fp->Pos = fp->BufferSpace;\r
fp->Flags |= FILE_FLAG_EOF;\r
}\r
return 0;\r
}\r
- else\r
- return _SysSeek(fp->FD, amt, whence);\r
+ else {\r
+ fflush(fp);\r
+ _SysSeek(fp->FD, amt, whence);\r
+ fp->Pos = _SysTell(fp->FD);\r
+ return 0;\r
+ }\r
+}\r
+\r
+size_t _fwrite_unbuffered(FILE *fp, size_t size, size_t num, const void *data)\r
+{\r
+ size_t ret = 0, bytes;\r
+ while( num -- )\r
+ {\r
+ bytes = _SysWrite(fp->FD, data, size);\r
+ if( bytes != size ) {\r
+ _SysDebug("_fwrite_unbuffered: Oops, rollback %i/%i bytes!", bytes, size);\r
+ _SysSeek(fp->FD, -bytes, SEEK_CUR);\r
+ break;\r
+ }\r
+ data = (char*)data + size;\r
+ }\r
+ fp->Pos += ret * size;\r
+ return ret;\r
}\r
\r
/**\r
if( size == 0 || num == 0 )\r
return 0;\r
\r
+ // Handle memory files first\r
if( fp->FD == -2 ) {\r
- size_t avail = (fp->BufferSize - fp->Pos) / size;\r
+ size_t avail = (fp->BufferSpace - fp->Pos) / size;\r
if( avail == 0 )\r
fp->Flags |= FILE_FLAG_EOF;\r
- if( num > avail ) num = avail;\r
+ if( num > avail )\r
+ num = avail;\r
size_t bytes = num * size;\r
- memcpy((char*)fp->Buffer + fp->Pos, ptr, bytes);\r
+ memcpy(fp->Buffer + fp->Pos, ptr, bytes);\r
fp->Pos += bytes;\r
- ret = num;\r
+ return num;\r
}\r
- else { \r
- ret = _SysWrite(fp->FD, ptr, size*num);\r
- ret /= size;\r
+\r
+ switch( _GetFileMode(fp) )\r
+ {\r
+ case FILE_FLAG_MODE_READ:\r
+ case FILE_FLAG_MODE_EXEC:\r
+ return 0;\r
+ case FILE_FLAG_MODE_APPEND:\r
+ fseek(fp, 0, SEEK_END);\r
+ case FILE_FLAG_MODE_WRITE:\r
+ if( fp->BufferSpace )\r
+ {\r
+ // Buffering enabled\r
+ if( fp->BufferSpace - fp->BufferPos < size*num )\r
+ {\r
+ // If there's not enough space, flush and write-through\r
+ _fflush_int(fp); // TODO: check ret\r
+ ret = _fwrite_unbuffered(fp, size, num, ptr);\r
+ }\r
+ else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) )\r
+ {\r
+ // Newline present? Flush though\r
+ _fflush_int(fp); // TODO: check ret\r
+ ret = _fwrite_unbuffered(fp, size, num, ptr);\r
+ }\r
+ else\r
+ {\r
+ // Copy to buffer\r
+ memcpy( fp->Buffer + fp->BufferPos, ptr, size*num );\r
+ fp->BufferPos += size*num;\r
+ ret = num;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // Bufering disabled, write-though\r
+ ret = _fwrite_unbuffered(fp, size, num, ptr);\r
+ }\r
+ // errno should be set earlier\r
+ break;\r
}\r
\r
return ret;\r
return 0;\r
\r
if( fp->FD == -2 ) {\r
- size_t avail = (fp->BufferSize - fp->Pos) / size;\r
+ size_t avail = (fp->BufferSpace - fp->Pos) / size;\r
if( avail == 0 )\r
fp->Flags |= FILE_FLAG_EOF;\r
if( num > avail ) num = avail;\r
size_t bytes = num * size;\r
- memcpy(ptr, (char*)fp->Buffer + fp->Pos, bytes);\r
+ memcpy(ptr, fp->Buffer + fp->Pos, bytes);\r
fp->Pos += bytes;\r
- ret = num;\r
+ return num;\r
}\r
- else {\r
- ret = _SysRead(fp->FD, ptr, size*num);\r
- if( ret == (size_t)-1)\r
- return -1;\r
- if( ret == 0 && size*num > 0 ) {\r
- fp->Flags |= FILE_FLAG_EOF;\r
- return 0;\r
- }\r
- ret /= size;\r
+ \r
+ // Standard file\r
+ ret = _SysRead(fp->FD, ptr, size*num);\r
+ if( ret == (size_t)-1)\r
+ return -1;\r
+ if( ret == 0 && size*num > 0 ) {\r
+ fp->Flags |= FILE_FLAG_EOF;\r
+ return 0;\r
}\r
- \r
+ ret /= size;\r
+ \r
return ret;\r
}\r
\r