Usermode/libc - Time formatting, stub locale.h, rename/remove
[tpg/acess2.git] / Usermode / Libraries / libc.so_src / stdio.c
1 /*\r
2  * AcessOS Basic C Library\r
3  * stdio.c\r
4  */\r
5 #include "config.h"\r
6 #include <acess/sys.h>\r
7 #include <stdlib.h>\r
8 #include <stdio.h>\r
9 #include <string.h>\r
10 #include "lib.h"\r
11 #include "stdio_int.h"\r
12 #include <errno.h>\r
13 #include <assert.h>\r
14 \r
15 #define DEBUG_BUILD     0\r
16 \r
17 // === CONSTANTS ===\r
18 #define _stdin  0\r
19 #define _stdout 1\r
20 \r
21 #define FD_NOTOPEN      -1\r
22 #define FD_MEMFILE      -2\r
23 #define FD_MEMSTREAM    -3\r
24 \r
25 // === PROTOTYPES ===\r
26 struct sFILE    *get_file_struct();\r
27 \r
28 // === GLOBALS ===\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
36 \r
37 // === CODE ===\r
38 void _stdio_init(void)\r
39 {\r
40         // Init FileIO Pointers\r
41         stdin = &_iob[0];\r
42         stdin->FD = 0;\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
46 \r
47         stdout = &_iob[1];\r
48         stdout->FD = 1;\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
52         \r
53         stderr = &_iob[2];\r
54         stderr->FD = 2;\r
55         stderr->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE;\r
56 }\r
57 \r
58 void _stdio_cleanup(void)\r
59 {\r
60          int    i;\r
61         for( i = 0; i < STDIO_MAX_STREAMS; i ++ )\r
62         {\r
63                 fclose( &_iob[i] );\r
64         }\r
65 }\r
66 \r
67 int _fopen_modetoflags(const char *mode)\r
68 {\r
69         int flags = 0;\r
70         \r
71         // Get main mode\r
72         switch(*mode)\r
73         {\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
78         default:\r
79                 return -1;\r
80         }\r
81         mode ++;\r
82 \r
83         // Get Modifiers\r
84         for( ; *mode; mode ++ )\r
85         {\r
86                 switch(*mode)\r
87                 {\r
88                 case 'b':       flags |= FILE_FLAG_M_BINARY;    break;\r
89                 case '+':       flags |= FILE_FLAG_M_EXT;       break;\r
90                 default:\r
91                         return -1;\r
92                 }\r
93         }\r
94         \r
95         return flags;\r
96 }\r
97 \r
98 /**\r
99  * \fn FILE *freopen(char *file, char *mode, FILE *fp)\r
100  */\r
101 EXPORT FILE *freopen(const char *file, const char *mode, FILE *fp)\r
102 {\r
103          int    openFlags = 0;\r
104         \r
105         // Sanity Check Arguments\r
106         if(!fp || !file || !mode)       return NULL;\r
107         \r
108         if(fp->FD != FD_NOTOPEN) {\r
109                 fflush(fp);\r
110         }\r
111 \r
112         // Get stdio flags\r
113         fp->Flags = _fopen_modetoflags(mode);\r
114         if(fp->Flags == -1)\r
115                 return NULL;\r
116         \r
117         // Get Open Flags\r
118         switch(fp->Flags & FILE_FLAG_MODE_MASK)\r
119         {\r
120         // Read\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
125                 break;\r
126         // 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
131                 break;\r
132         // Execute\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
137                 break;\r
138         // Execute\r
139         case FILE_FLAG_MODE_EXEC:\r
140                 openFlags = OPENFLAG_EXEC;\r
141                 break;\r
142         }\r
143 \r
144         //Open File\r
145         if(fp->FD != FD_NOTOPEN)\r
146                 fp->FD = _SysReopen(fp->FD, file, openFlags);\r
147         else\r
148                 fp->FD = _SysOpen(file, openFlags);\r
149         if(fp->FD == -1) {\r
150                 fp->Flags = 0;\r
151                 return NULL;\r
152         }\r
153         \r
154         // Default to buffered\r
155         // - Disabled until fseek() is fixed\r
156         #if 0\r
157         fp->BufferOfs = 0;\r
158         fp->BufferPos = 0;\r
159         fp->BufferSpace = BUFSIZ;\r
160         fp->Buffer = malloc( fp->BufferSpace );\r
161         fp->Flags |= FILE_FLAG_OURBUFFER;\r
162         #endif\r
163         \r
164         if( (fp->Flags & FILE_FLAG_MODE_MASK) == FILE_FLAG_MODE_APPEND ) {\r
165                 _SysSeek(fp->FD, 0, SEEK_END);  //SEEK_END\r
166         }\r
167         \r
168         return fp;\r
169 }\r
170 /**\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
175 */\r
176 EXPORT FILE *fopen(const char *file, const char *mode)\r
177 {\r
178         FILE    *retFile;\r
179         \r
180         // Sanity Check Arguments\r
181         if(!file || !mode)      return NULL;\r
182         \r
183         // Create Return Structure\r
184         retFile = get_file_struct();\r
185         \r
186         return freopen(file, mode, retFile);\r
187 }\r
188 \r
189 EXPORT FILE *fmemopen(void *buffer, size_t length, const char *mode)\r
190 {\r
191         FILE    *ret;\r
192         \r
193         if( !buffer || !mode )  return NULL;\r
194         \r
195         ret = get_file_struct();\r
196         \r
197         ret->FD = FD_MEMFILE;\r
198         ret->Flags = _fopen_modetoflags(mode);\r
199         if(ret->Flags == -1) {\r
200                 ret->Flags = 0;\r
201                 return NULL;\r
202         }\r
203         \r
204         ret->Buffer = buffer;\r
205         ret->BufferPos = 0;\r
206         ret->BufferSpace = length;\r
207         \r
208         return ret;\r
209 }\r
210 \r
211 EXPORT FILE *open_memstream(char **bufferptr, size_t *lengthptr)\r
212 {\r
213         FILE    *ret = get_file_struct();\r
214         ret->FD = FD_MEMSTREAM;\r
215         ret->Flags = FILE_FLAG_MODE_WRITE;\r
216         \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
222         \r
223         return ret;\r
224 }\r
225 \r
226 EXPORT FILE *fdopen(int fd, const char *mode)\r
227 {\r
228         FILE    *ret;\r
229         \r
230         if( fd < 0 || !mode )   return NULL;\r
231         \r
232         ret = get_file_struct();\r
233         \r
234         ret->FD = fd;\r
235         ret->Flags = _fopen_modetoflags(mode);\r
236         if(ret->Flags == -1) {\r
237                 ret->Flags = 0;\r
238                 return NULL;\r
239         }\r
240         \r
241         ret->Buffer = NULL;\r
242         ret->BufferPos = 0;\r
243         ret->BufferSpace = 0;\r
244         \r
245         return ret;\r
246 }\r
247 \r
248 EXPORT FILE *tmpfile(void)\r
249 {\r
250         return NULL;\r
251 }\r
252 \r
253 EXPORT int fclose(FILE *fp)\r
254 {\r
255         if( !(fp->Flags & FILE_FLAG_ALLOC) )\r
256                 return 0;\r
257         fflush(fp);\r
258         if( fp->FD >= 0 ) {\r
259                 _SysClose(fp->FD);\r
260                 if( fp->Buffer && (fp->Flags & FILE_FLAG_OURBUFFER) ) {\r
261                         free(fp->Buffer);\r
262                 }\r
263                 fp->Buffer = NULL;\r
264         }\r
265         fp->Flags = 0;\r
266         fp->FD = FD_NOTOPEN;\r
267         return 0;\r
268 }\r
269 \r
270 EXPORT int setvbuf(FILE *fp, char *buffer, int mode, size_t size)\r
271 {\r
272         if( !fp ) {\r
273                 errno = EINVAL;\r
274                 return 1;\r
275         }\r
276 \r
277         // Check for memory files\r
278         if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM ) {\r
279                 errno = EINVAL;\r
280                 return 2;\r
281         }       \r
282 \r
283         // Not strictly needed, as this should only be called before any IO\r
284         fflush(fp);\r
285 \r
286         // Eliminate any pre-existing buffer\r
287         if( fp->Buffer ) {\r
288                 free( fp->Buffer );\r
289                 fp->Buffer = NULL;\r
290                 fp->BufferSpace = 0;\r
291                 fp->BufferPos = 0;\r
292         }\r
293 \r
294         if( mode == _IONBF ) {\r
295                 // Do nothing, buffering was disabled above\r
296         }\r
297         else\r
298         {\r
299                 // Sanity check buffering mode before allocating\r
300                 if( mode != _IOLBF && mode != _IOFBF ) {\r
301                         errno = EINVAL;\r
302                         return 1;\r
303                 }\r
304                 // Allocate a buffer if one was not provided\r
305                 if( !buffer ) {\r
306                         fp->Flags |= FILE_FLAG_OURBUFFER;\r
307                         buffer = malloc(size);\r
308                         assert(buffer);\r
309                 }\r
310                 else {\r
311                         fp->Flags &= ~FILE_FLAG_OURBUFFER;\r
312                 }\r
313                 \r
314                 // Set buffer pointer and size\r
315                 fp->Buffer = buffer;\r
316                 fp->BufferSpace = size;\r
317                 \r
318                 // Set mode flag\r
319                 if( mode == _IOLBF )\r
320                         fp->Flags |= FILE_FLAG_LINEBUFFERED;\r
321                 else\r
322                         fp->Flags &= ~FILE_FLAG_LINEBUFFERED;\r
323         }\r
324         \r
325         return 0;\r
326 }\r
327 \r
328 int _fflush_int(FILE *fp)\r
329 {\r
330          int    ret = 0;\r
331         size_t  len;\r
332         \r
333         // Check the buffer contains data\r
334         if( fp->BufferPos == 0 )\r
335                 return 0;\r
336         \r
337         switch(fp->Flags & FILE_FLAG_MODE_MASK)\r
338         {\r
339         // Read - Flush input buffer\r
340         case FILE_FLAG_MODE_READ:\r
341                 fp->BufferPos = 0;\r
342                 break;\r
343         \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
349                         ret = 1;\r
350                 if( len <= fp->BufferPos )\r
351                 {\r
352                         fp->BufferPos -= len;\r
353                 }\r
354                 fp->BufferOfs = _SysTell(fp->FD);\r
355                 break;\r
356                 \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
362                         ret = 1;\r
363                 if( len <= fp->BufferPos )\r
364                 {\r
365                         fp->BufferPos -= len;\r
366                 }\r
367                 //else {\r
368                 //      _SysDebug("Flush of %i failed, %s", fp->FD, strerror(errno));\r
369                 //}\r
370                 break;\r
371         default:\r
372                 break;\r
373         }\r
374         return ret;\r
375 }\r
376 \r
377 EXPORT void fflush(FILE *fp)\r
378 {\r
379         if( !fp || fp->FD == FD_NOTOPEN )\r
380                 return ;\r
381         \r
382         // Nothing to do for memory files\r
383         if( fp->FD == FD_MEMFILE )\r
384                 return ;\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
389                 return ;\r
390         }\r
391         \r
392         _fflush_int(fp);\r
393 }\r
394 \r
395 EXPORT void clearerr(FILE *fp)\r
396 {\r
397         if( !fp || fp->FD == FD_NOTOPEN )\r
398                 return ;\r
399         \r
400         // TODO: Impliment clearerr()\r
401 }\r
402 \r
403 EXPORT int feof(FILE *fp)\r
404 {\r
405         if( !fp || fp->FD == FD_NOTOPEN )\r
406                 return 0;\r
407         return !!(fp->Flags & FILE_FLAG_EOF);\r
408 }\r
409 \r
410 EXPORT int ferror(FILE *fp)\r
411 {\r
412         if( !fp || fp->FD == FD_NOTOPEN )\r
413                 return 0;\r
414         return 0;\r
415 }\r
416 EXPORT int fileno(FILE *stream)\r
417 {\r
418         return stream->FD;\r
419 }\r
420 \r
421 EXPORT off_t ftell(FILE *fp)\r
422 {\r
423         if(!fp || fp->FD == FD_NOTOPEN) {\r
424                 errno = EBADF;\r
425                 return -1;\r
426         }\r
427 \r
428         if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM )\r
429                 return fp->Pos; \r
430         else\r
431                 return _SysTell(fp->FD);\r
432 }\r
433 \r
434 int _fseek_memfile(FILE *fp, long int amt, int whence)\r
435 {\r
436         switch(whence)\r
437         {\r
438         case SEEK_CUR:\r
439                 fp->Pos += amt;\r
440                 break;\r
441         case SEEK_SET:\r
442                 fp->Pos = amt;\r
443                 break;\r
444         case SEEK_END:\r
445                 if( fp->BufferSpace < (size_t)amt )\r
446                         fp->Pos = 0;\r
447                 else\r
448                         fp->Pos = fp->BufferSpace - amt;\r
449                 break;\r
450         }\r
451         if(fp->Pos > (off_t)fp->BufferSpace) {\r
452                 fp->Pos = fp->BufferSpace;\r
453                 fp->Flags |= FILE_FLAG_EOF;\r
454         }\r
455         return 0;\r
456 }\r
457 \r
458 int _fseek_memstream(FILE *fp, long int amt, int whence)\r
459 {\r
460         switch(whence)\r
461         {\r
462         case SEEK_CUR:\r
463                 fp->Pos += amt;\r
464                 break;\r
465         case SEEK_SET:\r
466                 fp->Pos = amt;\r
467                 break;\r
468         case SEEK_END:\r
469                 if( fp->BufferSpace < (size_t)amt )\r
470                         fp->Pos = 0;\r
471                 else\r
472                         fp->Pos = fp->BufferSpace - amt;\r
473                 break;\r
474         }\r
475         if(fp->Pos > (off_t)fp->BufferSpace) {\r
476                 fp->Pos = fp->BufferSpace;\r
477                 fp->Flags |= FILE_FLAG_EOF;\r
478         }\r
479         return 0;\r
480 }\r
481 \r
482 EXPORT int fseeko(FILE *fp, off_t amt, int whence)\r
483 {\r
484         if(!fp || fp->FD == FD_NOTOPEN) {\r
485                 errno = EBADF;\r
486                 return -1;\r
487         }\r
488 \r
489         if( fp->FD == FD_MEMFILE ) {\r
490                 return _fseek_memfile(fp, amt, whence);\r
491         }\r
492         else if( fp->FD == FD_MEMSTREAM ) {\r
493                 return _fseek_memstream(fp, amt, whence);\r
494         }\r
495         else {\r
496                 fflush(fp);\r
497                 _SysSeek(fp->FD, amt, whence);\r
498                 fp->Pos = _SysTell(fp->FD);\r
499                 return 0;\r
500         }\r
501 }\r
502 \r
503 EXPORT int fseek(FILE *fp, long int amt, int whence)\r
504 {\r
505         return fseeko(fp, amt, whence);\r
506 }\r
507 \r
508 size_t _fwrite_unbuffered(FILE *fp, size_t size, size_t num, const void *data)\r
509 {\r
510         size_t  ret = 0, bytes;\r
511         while( num -- )\r
512         {\r
513                 bytes = _SysWrite(fp->FD, data, size);\r
514                 if( bytes == (size_t)-1 ) {\r
515                         // Oops.\r
516                         // TODO: Set error flag\r
517                         break;\r
518                 }\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
522                         break;\r
523                 }\r
524                 data = (char*)data + size;\r
525         }\r
526         fp->Pos += ret * size;\r
527         return ret;\r
528 }\r
529 \r
530 size_t _fwrite_memfile(const void *ptr, size_t size, size_t num, FILE *fp)\r
531 {\r
532         size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
533         if( avail == 0 )\r
534                 fp->Flags |= FILE_FLAG_EOF;\r
535         if( num > avail )\r
536                 num = avail;\r
537         size_t  bytes = num * size;\r
538         memcpy(fp->Buffer + fp->Pos, ptr, bytes);\r
539         fp->Pos += bytes;\r
540         return num;\r
541 }\r
542 \r
543 size_t _fwrite_memstream(const void *ptr, size_t size, size_t num, FILE *fp)\r
544 {\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
548         {\r
549                 void *newbuf = realloc(fp->Buffer, fp->BufferSpace + bytes);\r
550                 if( !newbuf ) {\r
551                         errno = ENOMEM;\r
552                         return -1;\r
553                 }\r
554                 fp->Buffer = newbuf;\r
555                 fp->BufferSpace = fp->Pos + bytes;\r
556         }\r
557         // #2. Write (use the memfile code for that)\r
558         return _fwrite_memfile(ptr, size, num, fp);\r
559 }\r
560 \r
561 /**\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
564  */\r
565 EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp)\r
566 {\r
567         size_t  ret;\r
568         \r
569         if(!fp || fp->FD == -1)\r
570                 return -1;\r
571         if( size == 0 || num == 0 )\r
572                 return 0;\r
573 \r
574         switch( _GetFileMode(fp) )\r
575         {\r
576         case FILE_FLAG_MODE_READ:\r
577         case FILE_FLAG_MODE_EXEC:\r
578                 errno = EBADF;\r
579                 return 0;\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
586                 }\r
587                 else if( fp->FD == FD_MEMSTREAM ) {\r
588                         return _fwrite_memstream(ptr, size, num, fp);\r
589                 }\r
590                 else if( fp->BufferSpace )\r
591                 {\r
592                         // Buffering enabled\r
593                         if( fp->BufferSpace - fp->BufferPos < size*num )\r
594                         {\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
598                         }\r
599                         else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) )\r
600                         {\r
601                                 // Newline present? Flush though\r
602                                 _fflush_int(fp);        // TODO: check ret\r
603                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
604                         }\r
605                         else\r
606                         {\r
607                                 // Copy to buffer\r
608                                 memcpy( fp->Buffer + fp->BufferPos, ptr, size*num );\r
609                                 fp->BufferPos += size*num;\r
610                                 ret = num;\r
611                         }\r
612                 }\r
613                 else\r
614                 {\r
615                         // Bufering disabled, write-though\r
616                         ret = _fwrite_unbuffered(fp, size, num, ptr);\r
617                 }\r
618                 // errno should be set earlier\r
619                 break;\r
620         }\r
621         \r
622         return ret;\r
623 }\r
624 \r
625 size_t _fread_memfile(void *ptr, size_t size, size_t num, FILE *fp)\r
626 {\r
627         size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
628         if( avail == 0 )\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
633         fp->Pos += bytes;\r
634         return num;\r
635 }\r
636 \r
637 #if 0\r
638 size_t _fread_memstream(void *ptr, size_t size, size_t num, FILE *fp)\r
639 {\r
640         errno = ENOTIMPL;\r
641         return -1;\r
642 }\r
643 #endif\r
644 \r
645 size_t _fread_buffered(void *ptr, size_t size, FILE *fp)\r
646 {\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
651         }\r
652         if( fp->BufferPos == 0 || fp->Pos - fp->BufferOfs == (int)fp->BufferPos )\r
653         {\r
654                 int rv = _SysRead(fp->FD, fp->Buffer, fp->BufferSpace);\r
655                 if( rv <= 0 ) {\r
656                         fp->Flags |= FILE_FLAG_EOF;\r
657                         return 0;\r
658                 }\r
659                 \r
660                 fp->BufferPos = rv;\r
661                 fp->BufferOfs = fp->Pos;\r
662                 //_SysDebug("%p: Buffered %i at %i", fp, rv, fp->Pos);\r
663         }\r
664         \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
668         \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
672         fp->Pos += size;\r
673         return size;\r
674 }\r
675 \r
676 /**\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
679  */\r
680 EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
681 {\r
682         size_t  ret;\r
683         \r
684         if(!fp || fp->FD == -1)\r
685                 return -1;\r
686         if( size == 0 || num == 0 )\r
687                 return 0;\r
688         \r
689         if( _GetFileMode(fp) != FILE_FLAG_MODE_READ ) {\r
690                 errno = 0;\r
691                 return -1;\r
692         }\r
693 \r
694         // Don't read if EOF is set\r
695         if( fp->Flags & FILE_FLAG_EOF )\r
696                 return 0;\r
697 \r
698         \r
699         if( fp->FD == FD_MEMFILE ) {\r
700                 return _fread_memfile(ptr, size, num, fp);\r
701         }\r
702         else if( fp->FD == FD_MEMSTREAM ) {\r
703                 //return _fread_memstream(ptr, size, num, fp);\r
704                 errno = EBADF;\r
705                 return 0;\r
706         }\r
707 \r
708         // Standard file\r
709         const size_t    bytes = size*num;\r
710 \r
711         // TODO: Buffered reads\r
712         if( fp->BufferSpace )\r
713         {\r
714                 size_t  ofs = 0;\r
715                 size_t  rv;\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
718                 {\r
719                         ofs += rv;\r
720                 }\r
721                 \r
722                 ret = ofs;\r
723         }\r
724         else\r
725         {\r
726                 ret = _SysRead(fp->FD, ptr, bytes);\r
727                 if( ret == (size_t)-1)\r
728                         return -1;\r
729                 if( ret == 0 && bytes > 0 ) {\r
730                         fp->Flags |= FILE_FLAG_EOF;\r
731                         return 0;\r
732                 }\r
733         }\r
734 \r
735         // if read was cut short        \r
736         if( ret != bytes )\r
737         {\r
738                 size_t  extra = ret - (ret / size) * size;\r
739                 // And it didn't fall short on a member boundary\r
740                 if( extra )\r
741                 {\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
744                                 extra, size, num\r
745                                 );\r
746                 }\r
747         }\r
748         \r
749         return ret / size;\r
750 }\r
751 \r
752 /**\r
753  * \brief Write a string to a stream (without trailing \n)\r
754  */\r
755 EXPORT int fputs(const char *s, FILE *fp)\r
756 {\r
757         int len = strlen(s);\r
758         return fwrite(s, len, 1, fp);\r
759 }\r
760 \r
761 /**\r
762  * \brief Read a line (and possible trailing \n into a buffer)\r
763  */\r
764 EXPORT char *fgets(char *s, int size, FILE *fp)\r
765 {\r
766         int ofs = 0;\r
767         char    ch = '\0';\r
768         while( ofs < size && ch != '\n' )\r
769         {\r
770                 if( fread(&ch, 1, 1, fp) != 1 )\r
771                         break;\r
772                 s[ofs ++] = ch;\r
773         }\r
774         if( ofs < size )\r
775                 s[ofs] = '\0';\r
776         return s;\r
777 }\r
778 \r
779 /**\r
780  * \fn EXPORT int fputc(int c, FILE *fp)\r
781  * \brief Write a single character to the stream\r
782  */\r
783 EXPORT int fputc(int c, FILE *fp)\r
784 {\r
785         char    ch = c;\r
786         return fwrite(&ch, 1, 1, fp);\r
787 }\r
788 \r
789 EXPORT int putchar(int c)\r
790 {\r
791         c &= 0xFF;\r
792         return fputc(c, stdout);\r
793 }\r
794 \r
795 /**\r
796  * \fn EXPORT int fgetc(FILE *fp)\r
797  * \brief Read a character from the stream\r
798  */\r
799 EXPORT int fgetc(FILE *fp)\r
800 {\r
801         char    ret = 0;\r
802         if( fread(&ret, 1, 1, fp) != 1 )\r
803                 return -1;\r
804         return ret;\r
805 }\r
806 \r
807 EXPORT int getchar(void)\r
808 {\r
809         fflush(stdout);\r
810         return fgetc(stdin);\r
811 }\r
812 \r
813 EXPORT int puts(const char *str)\r
814 {\r
815         \r
816         if(!str)        return 0;\r
817          int    len = strlen(str);\r
818         \r
819         fwrite(str, 1, len, stdout);\r
820         fwrite("\n", 1, 1, stdout);\r
821         return len;\r
822 }\r
823 \r
824 // --- INTERNAL ---\r
825 /**\r
826  * \fn FILE *get_file_struct()\r
827  * \brief Returns a file descriptor structure\r
828  */\r
829 FILE *get_file_struct()\r
830 {\r
831         for(int i=0;i<STDIO_MAX_STREAMS;i++)\r
832         {\r
833                 if(_iob[i].Flags & FILE_FLAG_ALLOC)\r
834                         continue ;\r
835                 _iob[i].Flags |= FILE_FLAG_ALLOC;\r
836                 _iob[i].FD = -1;\r
837                 _iob[i].Pos = 0;\r
838                 return &_iob[i];\r
839         }\r
840         return NULL;\r
841 }\r
842 \r

UCC git Repository :: git.ucc.asn.au