Usermode/libc - Flush stdout on getchar
[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 int fclose(FILE *fp)\r
227 {\r
228         if( !(fp->Flags & FILE_FLAG_ALLOC) )\r
229                 return 0;\r
230         fflush(fp);\r
231         if( fp->FD >= 0 ) {\r
232                 _SysClose(fp->FD);\r
233                 if( fp->Buffer && (fp->Flags & FILE_FLAG_OURBUFFER) ) {\r
234                         free(fp->Buffer);\r
235                 }\r
236                 fp->Buffer = NULL;\r
237         }\r
238         fp->Flags = 0;\r
239         fp->FD = FD_NOTOPEN;\r
240         return 0;\r
241 }\r
242 \r
243 EXPORT int setvbuf(FILE *fp, char *buffer, int mode, size_t size)\r
244 {\r
245         if( !fp ) {\r
246                 errno = EINVAL;\r
247                 return 1;\r
248         }\r
249 \r
250         // Check for memory files\r
251         if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM ) {\r
252                 errno = EINVAL;\r
253                 return 2;\r
254         }       \r
255 \r
256         // Not strictly needed, as this should only be called before any IO\r
257         fflush(fp);\r
258 \r
259         // Eliminate any pre-existing buffer\r
260         if( fp->Buffer ) {\r
261                 free( fp->Buffer );\r
262                 fp->Buffer = NULL;\r
263                 fp->BufferSpace = 0;\r
264                 fp->BufferPos = 0;\r
265         }\r
266 \r
267         if( mode == _IONBF ) {\r
268                 // Do nothing, buffering was disabled above\r
269         }\r
270         else\r
271         {\r
272                 // Sanity check buffering mode before allocating\r
273                 if( mode != _IOLBF && mode != _IOFBF ) {\r
274                         errno = EINVAL;\r
275                         return 1;\r
276                 }\r
277                 // Allocate a buffer if one was not provided\r
278                 if( !buffer ) {\r
279                         fp->Flags |= FILE_FLAG_OURBUFFER;\r
280                         buffer = malloc(size);\r
281                         assert(buffer);\r
282                 }\r
283                 else {\r
284                         fp->Flags &= ~FILE_FLAG_OURBUFFER;\r
285                 }\r
286                 \r
287                 // Set buffer pointer and size\r
288                 fp->Buffer = buffer;\r
289                 fp->BufferSpace = size;\r
290                 \r
291                 // Set mode flag\r
292                 if( mode == _IOLBF )\r
293                         fp->Flags |= FILE_FLAG_LINEBUFFERED;\r
294                 else\r
295                         fp->Flags &= ~FILE_FLAG_LINEBUFFERED;\r
296         }\r
297         \r
298         return 0;\r
299 }\r
300 \r
301 int _fflush_int(FILE *fp)\r
302 {\r
303          int    ret = 0;\r
304         size_t  len;\r
305         \r
306         // Check the buffer contains data\r
307         if( fp->BufferPos == 0 )\r
308                 return 0;\r
309         \r
310         switch(fp->Flags & FILE_FLAG_MODE_MASK)\r
311         {\r
312         // Read - Flush input buffer\r
313         case FILE_FLAG_MODE_READ:\r
314                 fp->BufferPos = 0;\r
315                 break;\r
316         \r
317         // Append - Seek to end and write\r
318         case FILE_FLAG_MODE_APPEND:\r
319                 _SysSeek(fp->FD, fp->BufferOfs, SEEK_SET);\r
320                 len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);\r
321                 if( len != fp->BufferPos )\r
322                         ret = 1;\r
323                 if( len <= fp->BufferPos )\r
324                 {\r
325                         fp->BufferPos -= len;\r
326                 }\r
327                 fp->BufferOfs = _SysTell(fp->FD);\r
328                 break;\r
329                 \r
330         // Write - Write buffer\r
331         case FILE_FLAG_MODE_WRITE:\r
332                 //_SysDebug("Flushing to %i '%.*s'", fp->FD, fp->BufferPos, fp->Buffer);\r
333                 len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);\r
334                 if( len != fp->BufferPos )\r
335                         ret = 1;\r
336                 if( len <= fp->BufferPos )\r
337                 {\r
338                         fp->BufferPos -= len;\r
339                 }\r
340                 //else {\r
341                 //      _SysDebug("Flush of %i failed, %s", fp->FD, strerror(errno));\r
342                 //}\r
343                 break;\r
344         default:\r
345                 break;\r
346         }\r
347         return ret;\r
348 }\r
349 \r
350 EXPORT void fflush(FILE *fp)\r
351 {\r
352         if( !fp || fp->FD == FD_NOTOPEN )\r
353                 return ;\r
354         \r
355         // Nothing to do for memory files\r
356         if( fp->FD == FD_MEMFILE )\r
357                 return ;\r
358         // Memory streams, update pointers\r
359         if( fp->FD == FD_MEMSTREAM ) {\r
360                 *fp->BufPtr = fp->Buffer;\r
361                 *fp->LenPtr = fp->BufferPos;\r
362                 return ;\r
363         }\r
364         \r
365         _fflush_int(fp);\r
366 }\r
367 \r
368 EXPORT void clearerr(FILE *fp)\r
369 {\r
370         if( !fp || fp->FD == FD_NOTOPEN )\r
371                 return ;\r
372         \r
373         // TODO: Impliment clearerr()\r
374 }\r
375 \r
376 EXPORT int feof(FILE *fp)\r
377 {\r
378         if( !fp || fp->FD == FD_NOTOPEN )\r
379                 return 0;\r
380         return !!(fp->Flags & FILE_FLAG_EOF);\r
381 }\r
382 \r
383 EXPORT int ferror(FILE *fp)\r
384 {\r
385         if( !fp || fp->FD == FD_NOTOPEN )\r
386                 return 0;\r
387         return 0;\r
388 }\r
389 EXPORT int fileno(FILE *stream)\r
390 {\r
391         return stream->FD;\r
392 }\r
393 \r
394 EXPORT off_t ftell(FILE *fp)\r
395 {\r
396         if(!fp || fp->FD == FD_NOTOPEN) {\r
397                 errno = EBADF;\r
398                 return -1;\r
399         }\r
400 \r
401         if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM )\r
402                 return fp->Pos; \r
403         else\r
404                 return _SysTell(fp->FD);\r
405 }\r
406 \r
407 int _fseek_memfile(FILE *fp, long int amt, int whence)\r
408 {\r
409         switch(whence)\r
410         {\r
411         case SEEK_CUR:\r
412                 fp->Pos += amt;\r
413                 break;\r
414         case SEEK_SET:\r
415                 fp->Pos = amt;\r
416                 break;\r
417         case SEEK_END:\r
418                 if( fp->BufferSpace < (size_t)amt )\r
419                         fp->Pos = 0;\r
420                 else\r
421                         fp->Pos = fp->BufferSpace - amt;\r
422                 break;\r
423         }\r
424         if(fp->Pos > (off_t)fp->BufferSpace) {\r
425                 fp->Pos = fp->BufferSpace;\r
426                 fp->Flags |= FILE_FLAG_EOF;\r
427         }\r
428         return 0;\r
429 }\r
430 \r
431 int _fseek_memstream(FILE *fp, long int amt, int whence)\r
432 {\r
433         switch(whence)\r
434         {\r
435         case SEEK_CUR:\r
436                 fp->Pos += amt;\r
437                 break;\r
438         case SEEK_SET:\r
439                 fp->Pos = amt;\r
440                 break;\r
441         case SEEK_END:\r
442                 if( fp->BufferSpace < (size_t)amt )\r
443                         fp->Pos = 0;\r
444                 else\r
445                         fp->Pos = fp->BufferSpace - amt;\r
446                 break;\r
447         }\r
448         if(fp->Pos > (off_t)fp->BufferSpace) {\r
449                 fp->Pos = fp->BufferSpace;\r
450                 fp->Flags |= FILE_FLAG_EOF;\r
451         }\r
452         return 0;\r
453 }\r
454 \r
455 EXPORT int fseek(FILE *fp, long int amt, int whence)\r
456 {\r
457         if(!fp || fp->FD == FD_NOTOPEN) {\r
458                 errno = EBADF;\r
459                 return -1;\r
460         }\r
461 \r
462         if( fp->FD == FD_MEMFILE ) {\r
463                 return _fseek_memfile(fp, amt, whence);\r
464         }\r
465         else if( fp->FD == FD_MEMSTREAM ) {\r
466                 return _fseek_memstream(fp, amt, whence);\r
467         }\r
468         else {\r
469                 fflush(fp);\r
470                 _SysSeek(fp->FD, amt, whence);\r
471                 fp->Pos = _SysTell(fp->FD);\r
472                 return 0;\r
473         }\r
474 }\r
475 \r
476 size_t _fwrite_unbuffered(FILE *fp, size_t size, size_t num, const void *data)\r
477 {\r
478         size_t  ret = 0, bytes;\r
479         while( num -- )\r
480         {\r
481                 bytes = _SysWrite(fp->FD, data, size);\r
482                 if( bytes == (size_t)-1 ) {\r
483                         // Oops.\r
484                         // TODO: Set error flag\r
485                         break;\r
486                 }\r
487                 if( bytes != size ) {\r
488                         _SysDebug("_fwrite_unbuffered: Oops, rollback %i/%i bytes!", bytes, size);\r
489                         _SysSeek(fp->FD, -bytes, SEEK_CUR);\r
490                         break;\r
491                 }\r
492                 data = (char*)data + size;\r
493         }\r
494         fp->Pos += ret * size;\r
495         return ret;\r
496 }\r
497 \r
498 size_t _fwrite_memfile(const void *ptr, size_t size, size_t num, FILE *fp)\r
499 {\r
500         size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
501         if( avail == 0 )\r
502                 fp->Flags |= FILE_FLAG_EOF;\r
503         if( num > avail )\r
504                 num = avail;\r
505         size_t  bytes = num * size;\r
506         memcpy(fp->Buffer + fp->Pos, ptr, bytes);\r
507         fp->Pos += bytes;\r
508         return num;\r
509 }\r
510 \r
511 size_t _fwrite_memstream(const void *ptr, size_t size, size_t num, FILE *fp)\r
512 {\r
513         size_t  bytes = size*num;\r
514         // #1. Check if we need to expand\r
515         if( fp->Pos + bytes > fp->BufferSpace )\r
516         {\r
517                 void *newbuf = realloc(fp->Buffer, fp->BufferSpace + bytes);\r
518                 if( !newbuf ) {\r
519                         errno = ENOMEM;\r
520                         return -1;\r
521                 }\r
522                 fp->Buffer = newbuf;\r
523                 fp->BufferSpace = fp->Pos + bytes;\r
524         }\r
525         // #2. Write (use the memfile code for that)\r
526         return _fwrite_memfile(ptr, size, num, fp);\r
527 }\r
528 \r
529 /**\r
530  * \fn EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
531  * \brief Write to a stream\r
532  */\r
533 EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp)\r
534 {\r
535         size_t  ret;\r
536         \r
537         if(!fp || fp->FD == -1)\r
538                 return -1;\r
539         if( size == 0 || num == 0 )\r
540                 return 0;\r
541 \r
542         switch( _GetFileMode(fp) )\r
543         {\r
544         case FILE_FLAG_MODE_READ:\r
545         case FILE_FLAG_MODE_EXEC:\r
546                 errno = EBADF;\r
547                 return 0;\r
548         case FILE_FLAG_MODE_APPEND:\r
549                 fseek(fp, 0, SEEK_END);\r
550         case FILE_FLAG_MODE_WRITE:\r
551                 // Handle memory files first\r
552                 if( fp->FD == FD_MEMFILE ) {\r
553                         return _fwrite_memfile(ptr, size, num, fp);\r
554                 }\r
555                 else if( fp->FD == FD_MEMSTREAM ) {\r
556                         return _fwrite_memstream(ptr, size, num, fp);\r
557                 }\r
558                 else if( fp->BufferSpace )\r
559                 {\r
560                         // Buffering enabled\r
561                         if( fp->BufferSpace - fp->BufferPos < size*num )\r
562                         {\r
563                                 // If there's not enough space, flush and write-through\r
564                                 _fflush_int(fp);        // TODO: check ret\r
565                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
566                         }\r
567                         else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) )\r
568                         {\r
569                                 // Newline present? Flush though\r
570                                 _fflush_int(fp);        // TODO: check ret\r
571                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
572                         }\r
573                         else\r
574                         {\r
575                                 // Copy to buffer\r
576                                 memcpy( fp->Buffer + fp->BufferPos, ptr, size*num );\r
577                                 fp->BufferPos += size*num;\r
578                                 ret = num;\r
579                         }\r
580                 }\r
581                 else\r
582                 {\r
583                         // Bufering disabled, write-though\r
584                         ret = _fwrite_unbuffered(fp, size, num, ptr);\r
585                 }\r
586                 // errno should be set earlier\r
587                 break;\r
588         }\r
589         \r
590         return ret;\r
591 }\r
592 \r
593 size_t _fread_memfile(void *ptr, size_t size, size_t num, FILE *fp)\r
594 {\r
595         size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
596         if( avail == 0 )\r
597                 fp->Flags |= FILE_FLAG_EOF;\r
598         if( num > avail )       num = avail;\r
599         size_t  bytes = num * size;\r
600         memcpy(ptr, fp->Buffer + fp->Pos, bytes);\r
601         fp->Pos += bytes;\r
602         return num;\r
603 }\r
604 \r
605 #if 0\r
606 size_t _fread_memstream(void *ptr, size_t size, size_t num, FILE *fp)\r
607 {\r
608         errno = ENOTIMPL;\r
609         return -1;\r
610 }\r
611 #endif\r
612 \r
613 size_t _fread_buffered(void *ptr, size_t size, FILE *fp)\r
614 {\r
615         //_SysDebug("%p: %i-%i <= %i", fp,\r
616         //      (int)fp->Pos, (int)fp->BufferOfs, (int)fp->BufferPos);\r
617         if( fp->BufferPos > 0 ) {\r
618                 assert( fp->Pos - fp->BufferOfs <= (int)fp->BufferPos );\r
619         }\r
620         if( fp->BufferPos == 0 || fp->Pos - fp->BufferOfs == (int)fp->BufferPos )\r
621         {\r
622                 int rv = _SysRead(fp->FD, fp->Buffer, fp->BufferSpace);\r
623                 if( rv <= 0 ) {\r
624                         fp->Flags |= FILE_FLAG_EOF;\r
625                         return 0;\r
626                 }\r
627                 \r
628                 fp->BufferPos = rv;\r
629                 fp->BufferOfs = fp->Pos;\r
630                 //_SysDebug("%p: Buffered %i at %i", fp, rv, fp->Pos);\r
631         }\r
632         \r
633         size_t  inner_ofs = fp->Pos - fp->BufferOfs;\r
634         if(size > fp->BufferPos - inner_ofs)\r
635                 size = fp->BufferPos - inner_ofs;\r
636         \r
637         //_SysDebug("%p: Read %i from %i+%i", fp, size,\r
638         //      (int)fp->BufferOfs, inner_ofs);\r
639         memcpy(ptr, fp->Buffer + inner_ofs, size);\r
640         fp->Pos += size;\r
641         return size;\r
642 }\r
643 \r
644 /**\r
645  * \fn EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
646  * \brief Read from a stream\r
647  */\r
648 EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
649 {\r
650         size_t  ret;\r
651         \r
652         if(!fp || fp->FD == -1)\r
653                 return -1;\r
654         if( size == 0 || num == 0 )\r
655                 return 0;\r
656         \r
657         if( _GetFileMode(fp) != FILE_FLAG_MODE_READ ) {\r
658                 errno = 0;\r
659                 return -1;\r
660         }\r
661 \r
662         // Don't read if EOF is set\r
663         if( fp->Flags & FILE_FLAG_EOF )\r
664                 return 0;\r
665 \r
666         \r
667         if( fp->FD == FD_MEMFILE ) {\r
668                 return _fread_memfile(ptr, size, num, fp);\r
669         }\r
670         else if( fp->FD == FD_MEMSTREAM ) {\r
671                 //return _fread_memstream(ptr, size, num, fp);\r
672                 errno = EBADF;\r
673                 return 0;\r
674         }\r
675 \r
676         // Standard file\r
677         const size_t    bytes = size*num;\r
678 \r
679         // TODO: Buffered reads\r
680         if( fp->BufferSpace )\r
681         {\r
682                 size_t  ofs = 0;\r
683                 size_t  rv;\r
684                 // While not done, and buffered read succeeds\r
685                 while( ofs < bytes && (rv = _fread_buffered((char*)ptr + ofs, bytes - ofs, fp)) != 0 )\r
686                 {\r
687                         ofs += rv;\r
688                 }\r
689                 \r
690                 ret = ofs;\r
691         }\r
692         else\r
693         {\r
694                 ret = _SysRead(fp->FD, ptr, bytes);\r
695                 if( ret == (size_t)-1)\r
696                         return -1;\r
697                 if( ret == 0 && bytes > 0 ) {\r
698                         fp->Flags |= FILE_FLAG_EOF;\r
699                         return 0;\r
700                 }\r
701         }\r
702 \r
703         // if read was cut short        \r
704         if( ret != bytes )\r
705         {\r
706                 size_t  extra = ret - (ret / size) * size;\r
707                 // And it didn't fall short on a member boundary\r
708                 if( extra )\r
709                 {\r
710                         // Need to roll back the file pointer to the end of the last member\r
711                         _SysDebug("fread: TODO Roll back %zi bytes due to incomplete object (sz=%zi,n=%zi)",\r
712                                 extra, size, num\r
713                                 );\r
714                 }\r
715         }\r
716         \r
717         return ret / size;\r
718 }\r
719 \r
720 /**\r
721  * \brief Write a string to a stream (without trailing \n)\r
722  */\r
723 EXPORT int fputs(const char *s, FILE *fp)\r
724 {\r
725         int len = strlen(s);\r
726         return fwrite(s, 1, len, fp);\r
727 }\r
728 \r
729 /**\r
730  * \brief Read a line (and possible trailing \n into a buffer)\r
731  */\r
732 EXPORT char *fgets(char *s, int size, FILE *fp)\r
733 {\r
734         int ofs = 0;\r
735         char    ch = '\0';\r
736         while( ofs < size && ch != '\n' )\r
737         {\r
738                 if( fread(&ch, 1, 1, fp) != 1 )\r
739                         break;\r
740                 s[ofs ++] = ch;\r
741         }\r
742         if( ofs < size )\r
743                 s[ofs] = '\0';\r
744         return s;\r
745 }\r
746 \r
747 /**\r
748  * \fn EXPORT int fputc(int c, FILE *fp)\r
749  * \brief Write a single character to the stream\r
750  */\r
751 EXPORT int fputc(int c, FILE *fp)\r
752 {\r
753         char    ch = c;\r
754         return fwrite(&ch, 1, 1, fp);\r
755 }\r
756 \r
757 EXPORT int putchar(int c)\r
758 {\r
759         c &= 0xFF;\r
760         return fputc(c, stdout);\r
761 }\r
762 \r
763 /**\r
764  * \fn EXPORT int fgetc(FILE *fp)\r
765  * \brief Read a character from the stream\r
766  */\r
767 EXPORT int fgetc(FILE *fp)\r
768 {\r
769         char    ret = 0;\r
770         if( fread(&ret, 1, 1, fp) != 1 )\r
771                 return -1;\r
772         return ret;\r
773 }\r
774 \r
775 EXPORT int getchar(void)\r
776 {\r
777         fflush(stdout);\r
778         return fgetc(stdin);\r
779 }\r
780 \r
781 EXPORT int puts(const char *str)\r
782 {\r
783         \r
784         if(!str)        return 0;\r
785          int    len = strlen(str);\r
786         \r
787         fwrite(str, 1, len, stdout);\r
788         fwrite("\n", 1, 1, stdout);\r
789         return len;\r
790 }\r
791 \r
792 // --- INTERNAL ---\r
793 /**\r
794  * \fn FILE *get_file_struct()\r
795  * \brief Returns a file descriptor structure\r
796  */\r
797 FILE *get_file_struct()\r
798 {\r
799         for(int i=0;i<STDIO_MAX_STREAMS;i++)\r
800         {\r
801                 if(_iob[i].Flags & FILE_FLAG_ALLOC)\r
802                         continue ;\r
803                 _iob[i].Flags |= FILE_FLAG_ALLOC;\r
804                 _iob[i].FD = -1;\r
805                 _iob[i].Pos = 0;\r
806                 return &_iob[i];\r
807         }\r
808         return NULL;\r
809 }\r
810 \r

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