Usermode/libc #6 Fix string.h functions, add some more unit tests
[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 #define LOG_WARN(f,...) _SysDebug("WARN: %s: "f, __func__ ,## __VA_ARGS__)\r
18 #define LOG_NOTICE(f,...)       _SysDebug("NOTE: %s: "f, __func__ ,## __VA_ARGS__)\r
19 \r
20 // === CONSTANTS ===\r
21 #define _stdin  0\r
22 #define _stdout 1\r
23 \r
24 #define FD_NOTOPEN      -1\r
25 #define FD_MEMFILE      -2\r
26 #define FD_MEMSTREAM    -3\r
27 \r
28 // === PROTOTYPES ===\r
29 struct sFILE    *get_file_struct();\r
30 \r
31 // === GLOBALS ===\r
32 struct sFILE    _iob[STDIO_MAX_STREAMS];        // IO Buffer\r
33 struct sFILE    *stdin = &_iob[0];      // Standard Input\r
34 struct sFILE    *stdout = &_iob[1];     // Standard Output\r
35 struct sFILE    *stderr = &_iob[2];     // Standard Error\r
36 ///\note Initialised in SoMain\r
37 static const int STDIN_BUFSIZ = 512;\r
38 static const int STDOUT_BUFSIZ = 512;\r
39 \r
40 // === CODE ===\r
41 void _stdio_init(void)\r
42 {\r
43         // Init FileIO Pointers\r
44         stdin->FD = 0;\r
45         stdin->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_READ|FILE_FLAG_LINEBUFFERED|FILE_FLAG_OURBUFFER;\r
46         stdin->Buffer = malloc(STDIN_BUFSIZ);\r
47         stdin->BufferSpace = STDIN_BUFSIZ;\r
48 \r
49         stdout->FD = 1;\r
50         stdout->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE|FILE_FLAG_LINEBUFFERED|FILE_FLAG_OURBUFFER;\r
51         stdout->Buffer = malloc(STDOUT_BUFSIZ);\r
52         stdout->BufferSpace = STDOUT_BUFSIZ;\r
53         \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 int fflush(FILE *fp)\r
378 {\r
379         if( !fp || fp->FD == FD_NOTOPEN )\r
380                 return EBADF;\r
381         \r
382         // Nothing to do for memory files\r
383         if( fp->FD == FD_MEMFILE )\r
384                 return 0;\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 0;\r
390         }\r
391         \r
392         _fflush_int(fp);\r
393         return 0;\r
394 }\r
395 \r
396 EXPORT void clearerr(FILE *fp)\r
397 {\r
398         if( !fp || fp->FD == FD_NOTOPEN )\r
399                 return ;\r
400         \r
401         // TODO: Impliment clearerr()\r
402 }\r
403 \r
404 EXPORT int feof(FILE *fp)\r
405 {\r
406         if( !fp || fp->FD == FD_NOTOPEN )\r
407                 return 0;\r
408         return !!(fp->Flags & FILE_FLAG_EOF);\r
409 }\r
410 \r
411 EXPORT int ferror(FILE *fp)\r
412 {\r
413         if( !fp || fp->FD == FD_NOTOPEN )\r
414                 return 0;\r
415         return 0;\r
416 }\r
417 EXPORT int fileno(FILE *stream)\r
418 {\r
419         return stream->FD;\r
420 }\r
421 \r
422 EXPORT off_t ftell(FILE *fp)\r
423 {\r
424         if(!fp || fp->FD == FD_NOTOPEN) {\r
425                 errno = EBADF;\r
426                 return -1;\r
427         }\r
428 \r
429         if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM )\r
430                 return fp->Pos; \r
431         else\r
432                 return _SysTell(fp->FD);\r
433 }\r
434 \r
435 int _fseek_memfile(FILE *fp, long int amt, int whence)\r
436 {\r
437         switch(whence)\r
438         {\r
439         case SEEK_CUR:\r
440                 fp->Pos += amt;\r
441                 break;\r
442         case SEEK_SET:\r
443                 fp->Pos = amt;\r
444                 break;\r
445         case SEEK_END:\r
446                 if( fp->BufferSpace < (size_t)amt )\r
447                         fp->Pos = 0;\r
448                 else\r
449                         fp->Pos = fp->BufferSpace - amt;\r
450                 break;\r
451         }\r
452         if(fp->Pos > (off_t)fp->BufferSpace) {\r
453                 fp->Pos = fp->BufferSpace;\r
454                 fp->Flags |= FILE_FLAG_EOF;\r
455         }\r
456         return 0;\r
457 }\r
458 \r
459 int _fseek_memstream(FILE *fp, long int amt, int whence)\r
460 {\r
461         switch(whence)\r
462         {\r
463         case SEEK_CUR:\r
464                 fp->Pos += amt;\r
465                 break;\r
466         case SEEK_SET:\r
467                 fp->Pos = amt;\r
468                 break;\r
469         case SEEK_END:\r
470                 if( fp->BufferSpace < (size_t)amt )\r
471                         fp->Pos = 0;\r
472                 else\r
473                         fp->Pos = fp->BufferSpace - amt;\r
474                 break;\r
475         }\r
476         if(fp->Pos > (off_t)fp->BufferSpace) {\r
477                 fp->Pos = fp->BufferSpace;\r
478                 fp->Flags |= FILE_FLAG_EOF;\r
479         }\r
480         return 0;\r
481 }\r
482 \r
483 EXPORT int fseeko(FILE *fp, off_t amt, int whence)\r
484 {\r
485         if(!fp || fp->FD == FD_NOTOPEN) {\r
486                 errno = EBADF;\r
487                 return -1;\r
488         }\r
489 \r
490         if( fp->FD == FD_MEMFILE ) {\r
491                 return _fseek_memfile(fp, amt, whence);\r
492         }\r
493         else if( fp->FD == FD_MEMSTREAM ) {\r
494                 return _fseek_memstream(fp, amt, whence);\r
495         }\r
496         else {\r
497                 fflush(fp);\r
498                 _SysSeek(fp->FD, amt, whence);\r
499                 fp->Pos = _SysTell(fp->FD);\r
500                 return 0;\r
501         }\r
502 }\r
503 \r
504 EXPORT int fseek(FILE *fp, long int amt, int whence)\r
505 {\r
506         return fseeko(fp, amt, whence);\r
507 }\r
508 \r
509 size_t _fwrite_unbuffered(FILE *fp, size_t size, size_t num, const void *data)\r
510 {\r
511         size_t  ret = 0, bytes;\r
512         while( num -- )\r
513         {\r
514                 bytes = _SysWrite(fp->FD, data, size);\r
515                 if( bytes == (size_t)-1 ) {\r
516                         // Oops.\r
517                         // TODO: Set error flag\r
518                         break;\r
519                 }\r
520                 if( bytes != size ) {\r
521                         _SysDebug("_fwrite_unbuffered: Oops, rollback %i/%i bytes!", bytes, size);\r
522                         _SysSeek(fp->FD, -bytes, SEEK_CUR);\r
523                         break;\r
524                 }\r
525                 data = (char*)data + size;\r
526         }\r
527         fp->Pos += ret * size;\r
528         return ret;\r
529 }\r
530 \r
531 size_t _fwrite_memfile(const void *ptr, size_t size, size_t num, FILE *fp)\r
532 {\r
533         size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
534         if( avail == 0 )\r
535                 fp->Flags |= FILE_FLAG_EOF;\r
536         if( num > avail )\r
537                 num = avail;\r
538         size_t  bytes = num * size;\r
539         memcpy(fp->Buffer + fp->Pos, ptr, bytes);\r
540         fp->Pos += bytes;\r
541         return num;\r
542 }\r
543 \r
544 size_t _fwrite_memstream(const void *ptr, size_t size, size_t num, FILE *fp)\r
545 {\r
546         size_t  bytes = size*num;\r
547         // #1. Check if we need to expand\r
548         if( fp->Pos + bytes > fp->BufferSpace )\r
549         {\r
550                 void *newbuf = realloc(fp->Buffer, fp->BufferSpace + bytes);\r
551                 if( !newbuf ) {\r
552                         errno = ENOMEM;\r
553                         return -1;\r
554                 }\r
555                 fp->Buffer = newbuf;\r
556                 fp->BufferSpace = fp->Pos + bytes;\r
557         }\r
558         // #2. Write (use the memfile code for that)\r
559         return _fwrite_memfile(ptr, size, num, fp);\r
560 }\r
561 \r
562 /**\r
563  * \fn EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
564  * \brief Write to a stream\r
565  */\r
566 EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp)\r
567 {\r
568         size_t  ret;\r
569         \r
570         if(!fp || fp->FD == -1)\r
571                 return -1;\r
572         if( size == 0 || num == 0 )\r
573                 return 0;\r
574 \r
575         switch( _GetFileMode(fp) )\r
576         {\r
577         case FILE_FLAG_MODE_READ:\r
578         case FILE_FLAG_MODE_EXEC:\r
579                 errno = EBADF;\r
580                 return 0;\r
581         case FILE_FLAG_MODE_APPEND:\r
582                 fseek(fp, 0, SEEK_END);\r
583         case FILE_FLAG_MODE_WRITE:\r
584                 // Handle memory files first\r
585                 if( fp->FD == FD_MEMFILE ) {\r
586                         return _fwrite_memfile(ptr, size, num, fp);\r
587                 }\r
588                 else if( fp->FD == FD_MEMSTREAM ) {\r
589                         return _fwrite_memstream(ptr, size, num, fp);\r
590                 }\r
591                 else if( fp->BufferSpace )\r
592                 {\r
593                         // Buffering enabled\r
594                         if( fp->BufferSpace - fp->BufferPos < size*num )\r
595                         {\r
596                                 // If there's not enough space, flush and write-through\r
597                                 _fflush_int(fp);        // TODO: check ret\r
598                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
599                         }\r
600                         else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) )\r
601                         {\r
602                                 // Newline present? Flush though\r
603                                 _fflush_int(fp);        // TODO: check ret\r
604                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
605                         }\r
606                         else\r
607                         {\r
608                                 // Copy to buffer\r
609                                 memcpy( fp->Buffer + fp->BufferPos, ptr, size*num );\r
610                                 fp->BufferPos += size*num;\r
611                                 ret = num;\r
612                         }\r
613                 }\r
614                 else\r
615                 {\r
616                         // Bufering disabled, write-though\r
617                         ret = _fwrite_unbuffered(fp, size, num, ptr);\r
618                 }\r
619                 // errno should be set earlier\r
620                 break;\r
621         }\r
622         \r
623         return ret;\r
624 }\r
625 \r
626 size_t _fread_memfile(void *ptr, size_t size, size_t num, FILE *fp)\r
627 {\r
628         size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
629         if( avail == 0 )\r
630                 fp->Flags |= FILE_FLAG_EOF;\r
631         if( num > avail )       num = avail;\r
632         size_t  bytes = num * size;\r
633         memcpy(ptr, fp->Buffer + fp->Pos, bytes);\r
634         fp->Pos += bytes;\r
635         return num;\r
636 }\r
637 \r
638 #if 0\r
639 size_t _fread_memstream(void *ptr, size_t size, size_t num, FILE *fp)\r
640 {\r
641         errno = ENOTIMPL;\r
642         return -1;\r
643 }\r
644 #endif\r
645 \r
646 size_t _fread_buffered(void *ptr, size_t size, FILE *fp)\r
647 {\r
648         //_SysDebug("%p: %i-%i <= %i", fp,\r
649         //      (int)fp->Pos, (int)fp->BufferOfs, (int)fp->BufferPos);\r
650         if( fp->BufferPos > 0 ) {\r
651                 assert( fp->Pos - fp->BufferOfs <= (int)fp->BufferPos );\r
652         }\r
653         if( fp->BufferPos == 0 || fp->Pos - fp->BufferOfs == (int)fp->BufferPos )\r
654         {\r
655                 int rv = _SysRead(fp->FD, fp->Buffer, fp->BufferSpace);\r
656                 if( rv <= 0 ) {\r
657                         fp->Flags |= FILE_FLAG_EOF;\r
658                         return 0;\r
659                 }\r
660                 \r
661                 fp->BufferPos = rv;\r
662                 fp->BufferOfs = fp->Pos;\r
663                 //_SysDebug("%p: Buffered %i at %i", fp, rv, fp->Pos);\r
664         }\r
665         \r
666         size_t  inner_ofs = fp->Pos - fp->BufferOfs;\r
667         if(size > fp->BufferPos - inner_ofs)\r
668                 size = fp->BufferPos - inner_ofs;\r
669         \r
670         //_SysDebug("%p: Read %i from %i+%i", fp, size,\r
671         //      (int)fp->BufferOfs, inner_ofs);\r
672         memcpy(ptr, fp->Buffer + inner_ofs, size);\r
673         fp->Pos += size;\r
674         return size;\r
675 }\r
676 \r
677 /**\r
678  * \fn EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
679  * \brief Read from a stream\r
680  */\r
681 EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
682 {\r
683         size_t  ret;\r
684         \r
685         if(!fp || fp->FD == -1) {\r
686                 LOG_WARN("bad fp %p", fp);\r
687                 return -1;\r
688         }\r
689         if( size == 0 || num == 0 )\r
690                 return 0;\r
691         \r
692         if( _GetFileMode(fp) != FILE_FLAG_MODE_READ ) {\r
693                 errno = 0;\r
694                 LOG_WARN("not open for read");\r
695                 if( fp == stdin ) {\r
696                         LOG_WARN("BUGCHECK FAIL: stdin was not open for read");\r
697                         exit(129);\r
698                 }\r
699                 return -1;\r
700         }\r
701 \r
702         // Don't read if EOF is set\r
703         if( fp->Flags & FILE_FLAG_EOF ) {\r
704                 LOG_NOTICE("EOF");\r
705                 return 0;\r
706         }\r
707 \r
708         \r
709         if( fp->FD == FD_MEMFILE ) {\r
710                 return _fread_memfile(ptr, size, num, fp);\r
711         }\r
712         else if( fp->FD == FD_MEMSTREAM ) {\r
713                 //return _fread_memstream(ptr, size, num, fp);\r
714                 LOG_WARN("Reading from a mem stream");\r
715                 errno = EBADF;\r
716                 return 0;\r
717         }\r
718 \r
719         // Standard file\r
720         const size_t    bytes = size*num;\r
721 \r
722         // TODO: Buffered reads\r
723         if( fp->BufferSpace )\r
724         {\r
725                 size_t  ofs = 0;\r
726                 size_t  rv;\r
727                 // While not done, and buffered read succeeds\r
728                 while( ofs < bytes && (rv = _fread_buffered((char*)ptr + ofs, bytes - ofs, fp)) != 0 )\r
729                 {\r
730                         ofs += rv;\r
731                 }\r
732                 \r
733                 ret = ofs;\r
734         }\r
735         else\r
736         {\r
737                 ret = _SysRead(fp->FD, ptr, bytes);\r
738                 if( ret == (size_t)-1)\r
739                         return -1;\r
740                 if( ret == 0 && bytes > 0 ) {\r
741                         fp->Flags |= FILE_FLAG_EOF;\r
742                         return 0;\r
743                 }\r
744         }\r
745 \r
746         // if read was cut short        \r
747         if( ret != bytes )\r
748         {\r
749                 size_t  extra = ret - (ret / size) * size;\r
750                 // And it didn't fall short on a member boundary\r
751                 if( extra )\r
752                 {\r
753                         // Need to roll back the file pointer to the end of the last member\r
754                         _SysDebug("fread: TODO Roll back %zi bytes due to incomplete object (sz=%zi,n=%zi)",\r
755                                 extra, size, num\r
756                                 );\r
757                 }\r
758                 LOG_NOTICE("Incomplete read %i/%i bytes (object size %i)",\r
759                         ret, bytes, size);\r
760         }\r
761         \r
762         return ret / size;\r
763 }\r
764 \r
765 /**\r
766  * \brief Write a string to a stream (without trailing \n)\r
767  */\r
768 EXPORT int fputs(const char *s, FILE *fp)\r
769 {\r
770         int len = strlen(s);\r
771         return fwrite(s, len, 1, fp);\r
772 }\r
773 \r
774 /**\r
775  * \brief Read a line (and possible trailing \n into a buffer)\r
776  */\r
777 EXPORT char *fgets(char *s, int size, FILE *fp)\r
778 {\r
779         int ofs = 0;\r
780         char    ch = '\0';\r
781         while( ofs < size && ch != '\n' )\r
782         {\r
783                 if( fread(&ch, 1, 1, fp) != 1 )\r
784                         break;\r
785                 s[ofs ++] = ch;\r
786         }\r
787         if( ofs < size )\r
788                 s[ofs] = '\0';\r
789         return s;\r
790 }\r
791 \r
792 /**\r
793  * \fn EXPORT int fputc(int c, FILE *fp)\r
794  * \brief Write a single character to the stream\r
795  */\r
796 EXPORT int fputc(int c, FILE *fp)\r
797 {\r
798         unsigned char   ch = c;\r
799         return fwrite(&ch, 1, 1, fp);\r
800 }\r
801 \r
802 EXPORT int putchar(int c)\r
803 {\r
804         return fputc(c, stdout);\r
805 }\r
806 \r
807 /**\r
808  * \fn EXPORT int fgetc(FILE *fp)\r
809  * \brief Read a character from the stream\r
810  */\r
811 EXPORT int fgetc(FILE *fp)\r
812 {\r
813         unsigned char   ret = 0;\r
814         if( fread(&ret, 1, 1, fp) != 1 )\r
815                 return -1;\r
816         return ret;\r
817 }\r
818 \r
819 EXPORT int getchar(void)\r
820 {\r
821         fflush(stdout);\r
822         return fgetc(stdin);\r
823 }\r
824 \r
825 EXPORT int puts(const char *str)\r
826 {\r
827         if(!str)        return 0;\r
828          int    len = strlen(str);\r
829         \r
830         fwrite(str, 1, len, stdout);\r
831         fwrite("\n", 1, 1, stdout);\r
832         return len;\r
833 }\r
834 \r
835 // --- INTERNAL ---\r
836 /**\r
837  * \fn FILE *get_file_struct()\r
838  * \brief Returns a file descriptor structure\r
839  */\r
840 FILE *get_file_struct()\r
841 {\r
842         for(int i=0;i<STDIO_MAX_STREAMS;i++)\r
843         {\r
844                 if(_iob[i].Flags & FILE_FLAG_ALLOC)\r
845                         continue ;\r
846                 _iob[i].Flags |= FILE_FLAG_ALLOC;\r
847                 _iob[i].FD = -1;\r
848                 _iob[i].Pos = 0;\r
849                 return &_iob[i];\r
850         }\r
851         return NULL;\r
852 }\r
853 \r

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