#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 LOG_WARN(f,...) _SysDebug("WARN: %s: "f, __func__ ,## __VA_ARGS__)\r
+#define LOG_NOTICE(f,...) _SysDebug("NOTE: %s: "f, __func__ ,## __VA_ARGS__)\r
+\r
// === CONSTANTS ===\r
#define _stdin 0\r
#define _stdout 1\r
\r
+#define FD_NOTOPEN -1\r
+#define FD_MEMFILE -2\r
+#define FD_MEMSTREAM -3\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 _iob[STDIO_MAX_STREAMS]; // IO Buffer\r
-struct sFILE *stdin; // Standard Input\r
-struct sFILE *stdout; // Standard Output\r
-struct sFILE *stderr; // Standard Error\r
+struct sFILE *stdin = &_iob[0]; // Standard Input\r
+struct sFILE *stdout = &_iob[1]; // Standard Output\r
+struct sFILE *stderr = &_iob[2]; // 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->FD = 0;\r
+ stdin->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_READ|FILE_FLAG_LINEBUFFERED|FILE_FLAG_OURBUFFER;\r
+ stdin->Buffer = malloc(STDIN_BUFSIZ);\r
+ stdin->BufferSpace = STDIN_BUFSIZ;\r
+\r
+ stdout->FD = 1;\r
+ stdout->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE|FILE_FLAG_LINEBUFFERED|FILE_FLAG_OURBUFFER;\r
+ stdout->Buffer = malloc(STDOUT_BUFSIZ);\r
+ stdout->BufferSpace = STDOUT_BUFSIZ;\r
+ \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
// Sanity Check Arguments\r
if(!fp || !file || !mode) return NULL;\r
\r
- if(fp->FD != -1) {\r
+ if(fp->FD != FD_NOTOPEN) {\r
fflush(fp);\r
}\r
\r
}\r
\r
//Open File\r
- if(fp->FD != -1)\r
+ if(fp->FD != FD_NOTOPEN)\r
fp->FD = _SysReopen(fp->FD, file, openFlags);\r
else\r
fp->FD = _SysOpen(file, openFlags);\r
return NULL;\r
}\r
\r
+ // Default to buffered\r
+ // - Disabled until fseek() is fixed\r
+ #if 0\r
+ fp->BufferOfs = 0;\r
+ fp->BufferPos = 0;\r
+ fp->BufferSpace = BUFSIZ;\r
+ fp->Buffer = malloc( fp->BufferSpace );\r
+ fp->Flags |= FILE_FLAG_OURBUFFER;\r
+ #endif\r
+ \r
if( (fp->Flags & FILE_FLAG_MODE_MASK) == FILE_FLAG_MODE_APPEND ) {\r
_SysSeek(fp->FD, 0, SEEK_END); //SEEK_END\r
}\r
\r
ret = get_file_struct();\r
\r
- ret->FD = -2;\r
+ ret->FD = FD_MEMFILE;\r
ret->Flags = _fopen_modetoflags(mode);\r
if(ret->Flags == -1) {\r
ret->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 FILE *open_memstream(char **bufferptr, size_t *lengthptr)\r
+{\r
+ FILE *ret = get_file_struct();\r
+ ret->FD = FD_MEMSTREAM;\r
+ ret->Flags = FILE_FLAG_MODE_WRITE;\r
+ \r
+ ret->Buffer = NULL;\r
+ ret->BufferPos = 0;\r
+ ret->BufferSpace = 0;\r
+ ret->BufPtr = bufferptr;\r
+ ret->LenPtr = lengthptr;\r
+ \r
+ return ret;\r
+}\r
+\r
+EXPORT FILE *fdopen(int fd, const char *mode)\r
+{\r
+ FILE *ret;\r
+ \r
+ if( fd < 0 || !mode ) return NULL;\r
+ \r
+ ret = get_file_struct();\r
+ \r
+ ret->FD = fd;\r
+ ret->Flags = _fopen_modetoflags(mode);\r
+ if(ret->Flags == -1) {\r
+ ret->Flags = 0;\r
+ return NULL;\r
+ }\r
+ \r
+ ret->Buffer = NULL;\r
+ ret->BufferPos = 0;\r
+ ret->BufferSpace = 0;\r
+ \r
+ return ret;\r
+}\r
+\r
+EXPORT FILE *tmpfile(void)\r
+{\r
+ return NULL;\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
+ if( fp->Buffer && (fp->Flags & FILE_FLAG_OURBUFFER) ) {\r
+ free(fp->Buffer);\r
+ }\r
+ fp->Buffer = NULL;\r
}\r
fp->Flags = 0;\r
- fp->FD = -1;\r
+ fp->FD = FD_NOTOPEN;\r
return 0;\r
}\r
\r
-EXPORT void fflush(FILE *fp)\r
+EXPORT int setvbuf(FILE *fp, char *buffer, int mode, size_t size)\r
{\r
- if( !fp || fp->FD == -1 )\r
- return ;\r
+ if( !fp ) {\r
+ errno = EINVAL;\r
+ return 1;\r
+ }\r
+\r
+ // Check for memory files\r
+ if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM ) {\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
+ fp->Flags |= FILE_FLAG_OURBUFFER;\r
+ buffer = malloc(size);\r
+ assert(buffer);\r
+ }\r
+ else {\r
+ fp->Flags &= ~FILE_FLAG_OURBUFFER;\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
- if( !(fp->Flags & FILE_FLAG_DIRTY) )\r
- return ;\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
+ if( len <= fp->BufferPos )\r
+ {\r
+ fp->BufferPos -= len;\r
+ }\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
+ if( len <= fp->BufferPos )\r
+ {\r
+ fp->BufferPos -= len;\r
+ }\r
+ //else {\r
+ // _SysDebug("Flush of %i failed, %s", fp->FD, strerror(errno));\r
+ //}\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ return ret;\r
+}\r
+\r
+EXPORT int fflush(FILE *fp)\r
+{\r
+ if( !fp || fp->FD == FD_NOTOPEN )\r
+ return EBADF;\r
\r
// Nothing to do for memory files\r
- if( fp->FD == -2 )\r
- return ;\r
+ if( fp->FD == FD_MEMFILE )\r
+ return 0;\r
+ // Memory streams, update pointers\r
+ if( fp->FD == FD_MEMSTREAM ) {\r
+ *fp->BufPtr = fp->Buffer;\r
+ *fp->LenPtr = fp->BufferPos;\r
+ return 0;\r
+ }\r
+ \r
+ _fflush_int(fp);\r
+ return 0;\r
}\r
\r
EXPORT void clearerr(FILE *fp)\r
{\r
- if( !fp || fp->FD == -1 )\r
+ if( !fp || fp->FD == FD_NOTOPEN )\r
return ;\r
\r
// TODO: Impliment clearerr()\r
\r
EXPORT int feof(FILE *fp)\r
{\r
- if( !fp || fp->FD == -1 )\r
+ if( !fp || fp->FD == FD_NOTOPEN )\r
return 0;\r
return !!(fp->Flags & FILE_FLAG_EOF);\r
}\r
\r
EXPORT int ferror(FILE *fp)\r
{\r
- if( !fp || fp->FD == -1 )\r
+ if( !fp || fp->FD == FD_NOTOPEN )\r
return 0;\r
return 0;\r
}\r
\r
EXPORT off_t ftell(FILE *fp)\r
{\r
- if(!fp || fp->FD == -1) return -1;\r
+ if(!fp || fp->FD == FD_NOTOPEN) {\r
+ errno = EBADF;\r
+ return -1;\r
+ }\r
\r
- if( fp->FD == -2 )\r
+ if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM )\r
return fp->Pos; \r
else\r
return _SysTell(fp->FD);\r
}\r
\r
+int _fseek_memfile(FILE *fp, long int amt, int whence)\r
+{\r
+ switch(whence)\r
+ {\r
+ case SEEK_CUR:\r
+ fp->Pos += amt;\r
+ break;\r
+ case SEEK_SET:\r
+ fp->Pos = amt;\r
+ break;\r
+ case SEEK_END:\r
+ if( fp->BufferSpace < (size_t)amt )\r
+ fp->Pos = 0;\r
+ else\r
+ fp->Pos = fp->BufferSpace - amt;\r
+ break;\r
+ }\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
+\r
+int _fseek_memstream(FILE *fp, long int amt, int whence)\r
+{\r
+ switch(whence)\r
+ {\r
+ case SEEK_CUR:\r
+ fp->Pos += amt;\r
+ break;\r
+ case SEEK_SET:\r
+ fp->Pos = amt;\r
+ break;\r
+ case SEEK_END:\r
+ if( fp->BufferSpace < (size_t)amt )\r
+ fp->Pos = 0;\r
+ else\r
+ fp->Pos = fp->BufferSpace - amt;\r
+ break;\r
+ }\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
+\r
+EXPORT int fseeko(FILE *fp, off_t amt, int whence)\r
+{\r
+ if(!fp || fp->FD == FD_NOTOPEN) {\r
+ errno = EBADF;\r
+ return -1;\r
+ }\r
+\r
+ if( fp->FD == FD_MEMFILE ) {\r
+ return _fseek_memfile(fp, amt, whence);\r
+ }\r
+ else if( fp->FD == FD_MEMSTREAM ) {\r
+ return _fseek_memstream(fp, amt, whence);\r
+ }\r
+ else {\r
+ fflush(fp);\r
+ _SysSeek(fp->FD, amt, whence);\r
+ fp->Pos = _SysTell(fp->FD);\r
+ return 0;\r
+ }\r
+}\r
+\r
EXPORT int fseek(FILE *fp, long int amt, int whence)\r
{\r
- if(!fp || fp->FD == -1) return -1;\r
+ return fseeko(fp, amt, whence);\r
+}\r
\r
- if( fp->FD == -2 ) {\r
- switch(whence)\r
- {\r
- case SEEK_CUR:\r
- fp->Pos += amt;\r
- break;\r
- case SEEK_SET:\r
- fp->Pos = amt;\r
- break;\r
- case SEEK_END:\r
- if( fp->BufferSize < (size_t)amt )\r
- fp->Pos = 0;\r
- else\r
- fp->Pos = fp->BufferSize - amt;\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_t)-1 ) {\r
+ // Oops.\r
+ // TODO: Set error flag\r
break;\r
}\r
- if(fp->Pos > (off_t)fp->BufferSize) {\r
- fp->Pos = fp->BufferSize;\r
- fp->Flags |= FILE_FLAG_EOF;\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
- return 0;\r
+ data = (char*)data + size;\r
}\r
- else\r
- return _SysSeek(fp->FD, amt, whence);\r
+ fp->Pos += ret * size;\r
+ return ret;\r
}\r
\r
-\r
-/**\r
- * \fn EXPORT int vfprintf(FILE *fp, const char *format, va_list args)\r
- * \brief Print to a file from a variable argument list\r
- */\r
-EXPORT int vfprintf(FILE *fp, const char *format, va_list args)\r
+size_t _fwrite_memfile(const void *ptr, size_t size, size_t num, FILE *fp)\r
{\r
- va_list tmpList;\r
- int size;\r
-\r
- if(!fp || !format) return -1;\r
-\r
- va_copy(tmpList, args);\r
- \r
- size = vsnprintf(NULL, 0, (char*)format, tmpList);\r
- char buf[size+1];\r
- vsnprintf(buf, size+1, (char*)format, args);\r
- \r
- // Write to stream\r
- fwrite(buf, size, 1, fp);\r
- \r
- // Return written byte count\r
- return size;\r
+ size_t avail = (fp->BufferSpace - fp->Pos) / size;\r
+ if( avail == 0 )\r
+ fp->Flags |= FILE_FLAG_EOF;\r
+ if( num > avail )\r
+ num = avail;\r
+ size_t bytes = num * size;\r
+ memcpy(fp->Buffer + fp->Pos, ptr, bytes);\r
+ fp->Pos += bytes;\r
+ return num;\r
}\r
\r
-/**\r
- * \fn int fprintf(FILE *fp, const char *format, ...)\r
- * \brief Print a formatted string to a stream\r
- */\r
-EXPORT int fprintf(FILE *fp, const char *format, ...)\r
+size_t _fwrite_memstream(const void *ptr, size_t size, size_t num, FILE *fp)\r
{\r
- va_list args;\r
- int ret;\r
- \r
- // Get Size\r
- va_start(args, format);\r
- ret = vfprintf(fp, (char*)format, args);\r
- va_end(args);\r
- \r
- return ret;\r
+ size_t bytes = size*num;\r
+ // #1. Check if we need to expand\r
+ if( fp->Pos + bytes > fp->BufferSpace )\r
+ {\r
+ void *newbuf = realloc(fp->Buffer, fp->BufferSpace + bytes);\r
+ if( !newbuf ) {\r
+ errno = ENOMEM;\r
+ return -1;\r
+ }\r
+ fp->Buffer = newbuf;\r
+ fp->BufferSpace = fp->Pos + bytes;\r
+ }\r
+ // #2. Write (use the memfile code for that)\r
+ return _fwrite_memfile(ptr, size, num, fp);\r
}\r
\r
/**\r
* \fn EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
* \brief Write to a stream\r
*/\r
-EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
+EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp)\r
{\r
size_t ret;\r
\r
if(!fp || fp->FD == -1)\r
return -1;\r
+ if( size == 0 || num == 0 )\r
+ return 0;\r
\r
- if( fp->FD == -2 ) {\r
- size_t avail = (fp->BufferSize - 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((char*)fp->Buffer + fp->Pos, ptr, bytes);\r
- fp->Pos += bytes;\r
- ret = num;\r
- }\r
- else { \r
- ret = _SysWrite(fp->FD, ptr, size*num);\r
- ret /= size;\r
+ switch( _GetFileMode(fp) )\r
+ {\r
+ case FILE_FLAG_MODE_READ:\r
+ case FILE_FLAG_MODE_EXEC:\r
+ errno = EBADF;\r
+ return 0;\r
+ case FILE_FLAG_MODE_APPEND:\r
+ fseek(fp, 0, SEEK_END);\r
+ case FILE_FLAG_MODE_WRITE:\r
+ // Handle memory files first\r
+ if( fp->FD == FD_MEMFILE ) {\r
+ return _fwrite_memfile(ptr, size, num, fp);\r
+ }\r
+ else if( fp->FD == FD_MEMSTREAM ) {\r
+ return _fwrite_memstream(ptr, size, num, fp);\r
+ }\r
+ else 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
}\r
\r
+size_t _fread_memfile(void *ptr, size_t size, size_t num, FILE *fp)\r
+{\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, fp->Buffer + fp->Pos, bytes);\r
+ fp->Pos += bytes;\r
+ return num;\r
+}\r
+\r
+#if 0\r
+size_t _fread_memstream(void *ptr, size_t size, size_t num, FILE *fp)\r
+{\r
+ errno = ENOTIMPL;\r
+ return -1;\r
+}\r
+#endif\r
+\r
+size_t _fread_buffered(void *ptr, size_t size, FILE *fp)\r
+{\r
+ //_SysDebug("%p: %i-%i <= %i", fp,\r
+ // (int)fp->Pos, (int)fp->BufferOfs, (int)fp->BufferPos);\r
+ if( fp->BufferPos > 0 ) {\r
+ assert( fp->Pos - fp->BufferOfs <= (int)fp->BufferPos );\r
+ }\r
+ if( fp->BufferPos == 0 || fp->Pos - fp->BufferOfs == (int)fp->BufferPos )\r
+ {\r
+ int rv = _SysRead(fp->FD, fp->Buffer, fp->BufferSpace);\r
+ if( rv <= 0 ) {\r
+ fp->Flags |= FILE_FLAG_EOF;\r
+ return 0;\r
+ }\r
+ \r
+ fp->BufferPos = rv;\r
+ fp->BufferOfs = fp->Pos;\r
+ //_SysDebug("%p: Buffered %i at %i", fp, rv, fp->Pos);\r
+ }\r
+ \r
+ size_t inner_ofs = fp->Pos - fp->BufferOfs;\r
+ if(size > fp->BufferPos - inner_ofs)\r
+ size = fp->BufferPos - inner_ofs;\r
+ \r
+ //_SysDebug("%p: Read %i from %i+%i", fp, size,\r
+ // (int)fp->BufferOfs, inner_ofs);\r
+ memcpy(ptr, fp->Buffer + inner_ofs, size);\r
+ fp->Pos += size;\r
+ return size;\r
+}\r
+\r
/**\r
* \fn EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
* \brief Read from a stream\r
{\r
size_t ret;\r
\r
- if(!fp || fp->FD == -1)\r
+ if(!fp || fp->FD == -1) {\r
+ LOG_WARN("bad fp %p", fp);\r
return -1;\r
+ }\r
+ if( size == 0 || num == 0 )\r
+ return 0;\r
+ \r
+ if( _GetFileMode(fp) != FILE_FLAG_MODE_READ ) {\r
+ errno = 0;\r
+ LOG_WARN("not open for read");\r
+ return -1;\r
+ }\r
\r
- if( fp->FD == -2 ) {\r
- size_t avail = (fp->BufferSize - 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
- fp->Pos += bytes;\r
- ret = num;\r
+ // Don't read if EOF is set\r
+ if( fp->Flags & FILE_FLAG_EOF ) {\r
+ LOG_NOTICE("EOF");\r
+ return 0;\r
}\r
- else {\r
- ret = _SysRead(fp->FD, ptr, size*num);\r
- ret /= size;\r
+\r
+ \r
+ if( fp->FD == FD_MEMFILE ) {\r
+ return _fread_memfile(ptr, size, num, fp);\r
}\r
+ else if( fp->FD == FD_MEMSTREAM ) {\r
+ //return _fread_memstream(ptr, size, num, fp);\r
+ LOG_WARN("Reading from a mem stream");\r
+ errno = EBADF;\r
+ return 0;\r
+ }\r
+\r
+ // Standard file\r
+ const size_t bytes = size*num;\r
+\r
+ // TODO: Buffered reads\r
+ if( fp->BufferSpace )\r
+ {\r
+ size_t ofs = 0;\r
+ size_t rv;\r
+ // While not done, and buffered read succeeds\r
+ while( ofs < bytes && (rv = _fread_buffered((char*)ptr + ofs, bytes - ofs, fp)) != 0 )\r
+ {\r
+ ofs += rv;\r
+ }\r
\r
- return ret;\r
+ ret = ofs;\r
+ }\r
+ else\r
+ {\r
+ ret = _SysRead(fp->FD, ptr, bytes);\r
+ if( ret == (size_t)-1)\r
+ return -1;\r
+ if( ret == 0 && bytes > 0 ) {\r
+ fp->Flags |= FILE_FLAG_EOF;\r
+ return 0;\r
+ }\r
+ }\r
+\r
+ // if read was cut short \r
+ if( ret != bytes )\r
+ {\r
+ size_t extra = ret - (ret / size) * size;\r
+ // And it didn't fall short on a member boundary\r
+ if( extra )\r
+ {\r
+ // Need to roll back the file pointer to the end of the last member\r
+ _SysDebug("fread: TODO Roll back %zi bytes due to incomplete object (sz=%zi,n=%zi)",\r
+ extra, size, num\r
+ );\r
+ }\r
+ LOG_NOTICE("Incomplete read %i/%i bytes (object size %i)",\r
+ ret, bytes, size);\r
+ }\r
+ \r
+ return ret / size;\r
+}\r
+\r
+/**\r
+ * \brief Write a string to a stream (without trailing \n)\r
+ */\r
+EXPORT int fputs(const char *s, FILE *fp)\r
+{\r
+ int len = strlen(s);\r
+ return fwrite(s, len, 1, fp);\r
+}\r
+\r
+/**\r
+ * \brief Read a line (and possible trailing \n into a buffer)\r
+ */\r
+EXPORT char *fgets(char *s, int size, FILE *fp)\r
+{\r
+ int ofs = 0;\r
+ char ch = '\0';\r
+ while( ofs < size && ch != '\n' )\r
+ {\r
+ if( fread(&ch, 1, 1, fp) != 1 )\r
+ break;\r
+ s[ofs ++] = ch;\r
+ }\r
+ if( ofs < size )\r
+ s[ofs] = '\0';\r
+ return s;\r
}\r
\r
/**\r
*/\r
EXPORT int fputc(int c, FILE *fp)\r
{\r
- return fwrite(&c, 1, 1, fp);\r
+ char ch = c;\r
+ return fwrite(&ch, 1, 1, fp);\r
}\r
\r
EXPORT int putchar(int c)\r
{\r
c &= 0xFF;\r
- return _SysWrite(_stdout, &c, 1);\r
+ return fputc(c, stdout);\r
}\r
\r
/**\r
EXPORT int fgetc(FILE *fp)\r
{\r
char ret = 0;\r
- if( fread(&ret, 1, 1, fp) == (size_t)-1 )\r
+ if( fread(&ret, 1, 1, fp) != 1 )\r
return -1;\r
return ret;\r
}\r
\r
EXPORT int getchar(void)\r
{\r
- char ret = 0;\r
- if(_SysRead(_stdin, &ret, 1) != 1) return -1;\r
- return ret;\r
+ fflush(stdout);\r
+ return fgetc(stdin);\r
+}\r
+\r
+EXPORT int puts(const char *str)\r
+{\r
+ \r
+ if(!str) return 0;\r
+ int len = strlen(str);\r
+ \r
+ fwrite(str, 1, len, stdout);\r
+ fwrite("\n", 1, 1, stdout);\r
+ return len;\r
}\r
\r
// --- INTERNAL ---\r
*/\r
FILE *get_file_struct()\r
{\r
- int i;\r
- for(i=0;i<STDIO_MAX_STREAMS;i++)\r
+ for(int i=0;i<STDIO_MAX_STREAMS;i++)\r
{\r
if(_iob[i].Flags & FILE_FLAG_ALLOC)\r
continue ;\r
return NULL;\r
}\r
\r
-EXPORT int puts(const char *str)\r
-{\r
- int len;\r
- \r
- if(!str) return 0;\r
- len = strlen(str);\r
- \r
- len = _SysWrite(_stdout, str, len);\r
- _SysWrite(_stdout, "\n", 1);\r
- return len;\r
-}\r
-\r
-EXPORT int vsprintf(char * __s, const char *__format, va_list __args)\r
-{\r
- return vsnprintf(__s, 0x7FFFFFFF, __format, __args);\r
-}\r
-\r
-//sprintfv\r
-/**\r
- * \fn EXPORT void vsnprintf(char *buf, const char *format, va_list args)\r
- * \brief Prints a formatted string to a buffer\r
- * \param buf Pointer - Destination Buffer\r
- * \param format String - Format String\r
- * \param args VarArgs List - Arguments\r
- */\r
-EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list args)\r
-{\r
- char tmp[65];\r
- int c, minSize, precision, len;\r
- size_t pos = 0;\r
- char *p;\r
- char pad;\r
- uint64_t arg;\r
- int bLongLong, bPadLeft;\r
-\r
- void _addchar(char ch)\r
- {\r
- if(buf && pos < __maxlen) buf[pos] = ch;\r
- pos ++;\r
- }\r
-\r
- tmp[32] = '\0';\r
- \r
- while((c = *format++) != 0)\r
- {\r
- // Non-control character\r
- if (c != '%') {\r
- _addchar(c);\r
- continue;\r
- }\r
- \r
- // Control Character\r
- c = *format++;\r
- if(c == '%') { // Literal %\r
- _addchar('%');\r
- continue;\r
- }\r
- \r
- bPadLeft = 0;\r
- bLongLong = 0;\r
- minSize = 0;\r
- precision = -1;\r
- pad = ' ';\r
- \r
- // Padding Character\r
- if(c == '0') {\r
- pad = '0';\r
- c = *format++;\r
- }\r
- // Padding length\r
- if( c == '*' ) {\r
- // Variable length\r
- minSize = va_arg(args, size_t);\r
- c = *format++;\r
- }\r
- else {\r
- if('1' <= c && c <= '9')\r
- {\r
- minSize = 0;\r
- while('0' <= c && c <= '9')\r
- {\r
- minSize *= 10;\r
- minSize += c - '0';\r
- c = *format++;\r
- }\r
- }\r
- }\r
-\r
- // Precision\r
- if(c == '.') {\r
- c = *format++;\r
- if(c == '*') {\r
- precision = va_arg(args, size_t);\r
- c = *format++;\r
- }\r
- else if('1' <= c && c <= '9')\r
- {\r
- precision = 0;\r
- while('0' <= c && c <= '9')\r
- {\r
- precision *= 10;\r
- precision += c - '0';\r
- c = *format++;\r
- }\r
- }\r
- }\r
- \r
- // Check for long long\r
- bLongLong = 0;\r
- if(c == 'l')\r
- {\r
- c = *format++;\r
- if(c == 'l') {\r
- bLongLong = 1;\r
- }\r
- }\r
- \r
- // Just help things along later\r
- p = tmp;\r
- \r
- // Get Type\r
- switch( c )\r
- {\r
- // Signed Integer\r
- case 'd': case 'i':\r
- // Get Argument\r
- if(bLongLong) arg = va_arg(args, int64_t);\r
- else arg = va_arg(args, int32_t);\r
- itoa(tmp, arg, 10, minSize, pad, 1);\r
- precision = -1;\r
- goto sprintf_puts;\r
- \r
- // Unsigned Integer\r
- case 'u':\r
- // Get Argument\r
- if(bLongLong) arg = va_arg(args, uint64_t);\r
- else arg = va_arg(args, uint32_t);\r
- itoa(tmp, arg, 10, minSize, pad, 0);\r
- precision = -1;\r
- goto sprintf_puts;\r
- \r
- // Pointer\r
- case 'p':\r
- _addchar('*');\r
- _addchar('0');\r
- _addchar('x');\r
- arg = va_arg(args, intptr_t);\r
- itoa(tmp, arg, 16, minSize, pad, 0);\r
- precision = -1;\r
- goto sprintf_puts;\r
- // Unsigned Hexadecimal\r
- case 'x':\r
- if(bLongLong) arg = va_arg(args, uint64_t);\r
- else arg = va_arg(args, uint32_t);\r
- itoa(tmp, arg, 16, minSize, pad, 0);\r
- precision = -1;\r
- goto sprintf_puts;\r
- \r
- // Unsigned Octal\r
- case 'o':\r
- if(bLongLong) arg = va_arg(args, uint64_t);\r
- else arg = va_arg(args, uint32_t);\r
- itoa(tmp, arg, 8, minSize, pad, 0);\r
- precision = -1;\r
- goto sprintf_puts;\r
- \r
- // Unsigned binary\r
- case 'b':\r
- if(bLongLong) arg = va_arg(args, uint64_t);\r
- else arg = va_arg(args, uint32_t);\r
- itoa(tmp, arg, 2, minSize, pad, 0);\r
- precision = -1;\r
- goto sprintf_puts;\r
-\r
- // String\r
- case 's':\r
- p = va_arg(args, char*);\r
- sprintf_puts:\r
- if(!p) p = "(null)";\r
- //_SysDebug("vsnprintf: p = '%s'", p);\r
- if(precision >= 0)\r
- len = strnlen(p, precision);\r
- else\r
- len = strlen(p);\r
- if(bPadLeft) while(minSize > len++) _addchar(pad);\r
- while( *p ) {\r
- if(precision >= 0 && precision -- == 0)\r
- break;\r
- _addchar(*p++);\r
- }\r
- if(!bPadLeft) while(minSize > len++) _addchar(pad);\r
- break;\r
-\r
- // Unknown, just treat it as a character\r
- default:\r
- arg = va_arg(args, uint32_t);\r
- _addchar(arg);\r
- break;\r
- }\r
- }\r
- _addchar('\0');\r
- pos --;\r
- \r
- //_SysDebug("vsnprintf: buf = '%s'", buf);\r
- \r
- return pos;\r
-}\r
-\r
-const char cUCDIGITS[] = "0123456789ABCDEF";\r
-/**\r
- * \fn static void itoa(char *buf, uint64_t num, int base, int minLength, char pad, int bSigned)\r
- * \brief Convert an integer into a character string\r
- * \param buf Destination Buffer\r
- * \param num Number to convert\r
- * \param base Base-n number output\r
- * \param minLength Minimum length of output\r
- * \param pad Padding used to ensure minLength\r
- * \param bSigned Signed number output?\r
- */\r
-EXPORT void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned)\r
-{\r
- char tmpBuf[64];\r
- int pos=0, i;\r
-\r
- if(!buf) return;\r
- if(base > 16 || base < 2) {\r
- buf[0] = 0;\r
- return;\r
- }\r
- \r
- if(bSigned && (int64_t)num < 0)\r
- {\r
- num = -num;\r
- bSigned = 1;\r
- } else\r
- bSigned = 0;\r
- \r
- // Encode into reversed string\r
- while(num > base-1) {\r
- tmpBuf[pos++] = cUCDIGITS[ num % base ];\r
- num = (uint64_t) num / (uint64_t)base; // Shift {number} right 1 digit\r
- }\r
-\r
- tmpBuf[pos++] = cUCDIGITS[ num % base ]; // Last digit of {number}\r
- if(bSigned) tmpBuf[pos++] = '-'; // Append sign symbol if needed\r
- \r
- i = 0;\r
- minLength -= pos;\r
- while(minLength-- > 0) buf[i++] = pad;\r
- while(pos-- > 0) buf[i++] = tmpBuf[pos]; // Reverse the order of characters\r
- buf[i] = 0;\r
-}\r
-\r
-/**\r
- * \fn EXPORT int printf(const char *format, ...)\r
- * \brief Print a string to stdout\r
- */\r
-EXPORT int printf(const char *format, ...)\r
-{\r
- int size;\r
- va_list args;\r
- \r
- // Get final size\r
- va_start(args, format);\r
- size = vsnprintf(NULL, 0, (char*)format, args);\r
- va_end(args);\r
- char buf[size+1];\r
- // Fill Buffer\r
- va_start(args, format);\r
- vsnprintf(buf, size+1, (char*)format, args);\r
- va_end(args);\r
- \r
- // Send to stdout\r
- _SysWrite(_stdout, buf, size+1);\r
- \r
- // Free buffer\r
- free(buf);\r
- // Return\r
- return size;\r
-}\r
-\r
-/**\r
- * \fn EXPORT int sprintf(const char *buf, char *format, ...)\r
- * \brief Print a formatted string to a buffer\r
- */\r
-EXPORT int sprintf(char *buf, const char *format, ...)\r
-{\r
- int ret;\r
- va_list args;\r
- va_start(args, format);\r
- ret = vsprintf((char*)buf, (char*)format, args);\r
- va_end(args);\r
- return ret;\r
-}\r
-\r
-/**\r
- * \fn EXPORT int snprintf(const char *buf, size_t maxlen, char *format, ...)\r
- * \brief Print a formatted string to a buffer\r
- */\r
-EXPORT int snprintf(char *buf, size_t maxlen, const char *format, ...)\r
-{\r
- int ret;\r
- va_list args;\r
- va_start(args, format);\r
- ret = vsnprintf((char*)buf, maxlen, (char*)format, args);\r
- va_end(args);\r
- return ret;\r
-}\r