Usermode/libc - Fixed fflush not handling errors
[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;\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;\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         if( (fp->Flags & FILE_FLAG_MODE_MASK) == FILE_FLAG_MODE_APPEND ) {\r
155                 _SysSeek(fp->FD, 0, SEEK_END);  //SEEK_END\r
156         }\r
157         \r
158         return fp;\r
159 }\r
160 /**\r
161  \fn FILE *fopen(const char *file, const char *mode)\r
162  \brief Opens a file and returns the pointer\r
163  \param file    String - Filename to open\r
164  \param mode    Mode to open in\r
165 */\r
166 EXPORT FILE *fopen(const char *file, const char *mode)\r
167 {\r
168         FILE    *retFile;\r
169         \r
170         // Sanity Check Arguments\r
171         if(!file || !mode)      return NULL;\r
172         \r
173         // Create Return Structure\r
174         retFile = get_file_struct();\r
175         \r
176         return freopen(file, mode, retFile);\r
177 }\r
178 \r
179 EXPORT FILE *fmemopen(void *buffer, size_t length, const char *mode)\r
180 {\r
181         FILE    *ret;\r
182         \r
183         if( !buffer || !mode )  return NULL;\r
184         \r
185         ret = get_file_struct();\r
186         \r
187         ret->FD = FD_MEMFILE;\r
188         ret->Flags = _fopen_modetoflags(mode);\r
189         if(ret->Flags == -1) {\r
190                 ret->Flags = 0;\r
191                 return NULL;\r
192         }\r
193         \r
194         ret->Buffer = buffer;\r
195         ret->BufferPos = 0;\r
196         ret->BufferSpace = length;\r
197         \r
198         return ret;\r
199 }\r
200 \r
201 EXPORT FILE *open_memstream(char **bufferptr, size_t *lengthptr)\r
202 {\r
203         FILE    *ret = get_file_struct();\r
204         ret->FD = FD_MEMSTREAM;\r
205         ret->Flags = FILE_FLAG_MODE_WRITE;\r
206         \r
207         ret->Buffer = NULL;\r
208         ret->BufferPos = 0;\r
209         ret->BufferSpace = 0;\r
210         ret->BufPtr = bufferptr;\r
211         ret->LenPtr = lengthptr;\r
212         \r
213         return ret;\r
214 }\r
215 \r
216 EXPORT int fclose(FILE *fp)\r
217 {\r
218         if( !(fp->Flags & FILE_FLAG_ALLOC) )\r
219                 return 0;\r
220         fflush(fp);\r
221         if( fp->FD >= 0 ) {\r
222                 _SysClose(fp->FD);\r
223         }\r
224         fp->Flags = 0;\r
225         fp->FD = FD_NOTOPEN;\r
226         return 0;\r
227 }\r
228 \r
229 EXPORT int setvbuf(FILE *fp, char *buffer, int mode, size_t size)\r
230 {\r
231         if( !fp ) {\r
232                 errno = EINVAL;\r
233                 return 1;\r
234         }\r
235 \r
236         // Check for memory files\r
237         if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM ) {\r
238                 errno = EINVAL;\r
239                 return 2;\r
240         }       \r
241 \r
242         // Not strictly needed, as this should only be called before any IO\r
243         fflush(fp);\r
244 \r
245         // Eliminate any pre-existing buffer\r
246         if( fp->Buffer ) {\r
247                 free( fp->Buffer );\r
248                 fp->Buffer = NULL;\r
249                 fp->BufferSpace = 0;\r
250                 fp->BufferPos = 0;\r
251         }\r
252 \r
253         if( mode == _IONBF ) {\r
254                 // Do nothing, buffering was disabled above\r
255         }\r
256         else\r
257         {\r
258                 // Sanity check buffering mode before allocating\r
259                 if( mode != _IOLBF && mode != _IOFBF ) {\r
260                         errno = EINVAL;\r
261                         return 1;\r
262                 }\r
263                 // Allocate a buffer if one was not provided\r
264                 if( !buffer ) {\r
265                         buffer = malloc(size);\r
266                         assert(buffer);\r
267                 }\r
268                 \r
269                 // Set buffer pointer and size\r
270                 fp->Buffer = buffer;\r
271                 fp->BufferSpace = size;\r
272                 \r
273                 // Set mode flag\r
274                 if( mode == _IOLBF )\r
275                         fp->Flags |= FILE_FLAG_LINEBUFFERED;\r
276                 else\r
277                         fp->Flags &= ~FILE_FLAG_LINEBUFFERED;\r
278         }\r
279         \r
280         return 0;\r
281 }\r
282 \r
283 int _fflush_int(FILE *fp)\r
284 {\r
285          int    ret = 0;\r
286         size_t  len;\r
287         \r
288         // Check the buffer contains data\r
289         if( fp->BufferPos == 0 )\r
290                 return 0;\r
291         \r
292         switch(fp->Flags & FILE_FLAG_MODE_MASK)\r
293         {\r
294         // Read - Flush input buffer\r
295         case FILE_FLAG_MODE_READ:\r
296                 fp->BufferPos = 0;\r
297                 break;\r
298         \r
299         // Append - Seek to end and write\r
300         case FILE_FLAG_MODE_APPEND:\r
301                 _SysSeek(fp->FD, fp->BufferOfs, SEEK_SET);\r
302                 len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);\r
303                 if( len != fp->BufferPos )\r
304                         ret = 1;\r
305                 if( len <= fp->BufferPos )\r
306                 {\r
307                         fp->BufferPos -= len;\r
308                 }\r
309                 fp->BufferOfs = _SysTell(fp->FD);\r
310                 break;\r
311                 \r
312         // Write - Write buffer\r
313         case FILE_FLAG_MODE_WRITE:\r
314                 //_SysDebug("Flushing to %i '%.*s'", fp->FD, fp->BufferPos, fp->Buffer);\r
315                 len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);\r
316                 if( len != fp->BufferPos )\r
317                         ret = 1;\r
318                 if( len <= fp->BufferPos )\r
319                 {\r
320                         fp->BufferPos -= len;\r
321                 }\r
322                 //else {\r
323                 //      _SysDebug("Flush of %i failed, %s", fp->FD, strerror(errno));\r
324                 //}\r
325                 break;\r
326         default:\r
327                 break;\r
328         }\r
329         return ret;\r
330 }\r
331 \r
332 EXPORT void fflush(FILE *fp)\r
333 {\r
334         if( !fp || fp->FD == FD_NOTOPEN )\r
335                 return ;\r
336         \r
337         // Nothing to do for memory files\r
338         if( fp->FD == FD_MEMFILE )\r
339                 return ;\r
340         // Memory streams, update pointers\r
341         if( fp->FD == FD_MEMSTREAM ) {\r
342                 *fp->BufPtr = fp->Buffer;\r
343                 *fp->LenPtr = fp->BufferPos;\r
344                 return ;\r
345         }\r
346         \r
347         _fflush_int(fp);\r
348 }\r
349 \r
350 EXPORT void clearerr(FILE *fp)\r
351 {\r
352         if( !fp || fp->FD == FD_NOTOPEN )\r
353                 return ;\r
354         \r
355         // TODO: Impliment clearerr()\r
356 }\r
357 \r
358 EXPORT int feof(FILE *fp)\r
359 {\r
360         if( !fp || fp->FD == FD_NOTOPEN )\r
361                 return 0;\r
362         return !!(fp->Flags & FILE_FLAG_EOF);\r
363 }\r
364 \r
365 EXPORT int ferror(FILE *fp)\r
366 {\r
367         if( !fp || fp->FD == FD_NOTOPEN )\r
368                 return 0;\r
369         return 0;\r
370 }\r
371 EXPORT int fileno(FILE *stream)\r
372 {\r
373         return stream->FD;\r
374 }\r
375 \r
376 EXPORT off_t ftell(FILE *fp)\r
377 {\r
378         if(!fp || fp->FD == FD_NOTOPEN) {\r
379                 errno = EBADF;\r
380                 return -1;\r
381         }\r
382 \r
383         if( fp->FD == FD_MEMFILE || fp->FD == FD_MEMSTREAM )\r
384                 return fp->Pos; \r
385         else\r
386                 return _SysTell(fp->FD);\r
387 }\r
388 \r
389 int _fseek_memfile(FILE *fp, long int amt, int whence)\r
390 {\r
391         switch(whence)\r
392         {\r
393         case SEEK_CUR:\r
394                 fp->Pos += amt;\r
395                 break;\r
396         case SEEK_SET:\r
397                 fp->Pos = amt;\r
398                 break;\r
399         case SEEK_END:\r
400                 if( fp->BufferSpace < (size_t)amt )\r
401                         fp->Pos = 0;\r
402                 else\r
403                         fp->Pos = fp->BufferSpace - amt;\r
404                 break;\r
405         }\r
406         if(fp->Pos > (off_t)fp->BufferSpace) {\r
407                 fp->Pos = fp->BufferSpace;\r
408                 fp->Flags |= FILE_FLAG_EOF;\r
409         }\r
410         return 0;\r
411 }\r
412 \r
413 int _fseek_memstream(FILE *fp, long int amt, int whence)\r
414 {\r
415         switch(whence)\r
416         {\r
417         case SEEK_CUR:\r
418                 fp->Pos += amt;\r
419                 break;\r
420         case SEEK_SET:\r
421                 fp->Pos = amt;\r
422                 break;\r
423         case SEEK_END:\r
424                 if( fp->BufferSpace < (size_t)amt )\r
425                         fp->Pos = 0;\r
426                 else\r
427                         fp->Pos = fp->BufferSpace - amt;\r
428                 break;\r
429         }\r
430         if(fp->Pos > (off_t)fp->BufferSpace) {\r
431                 fp->Pos = fp->BufferSpace;\r
432                 fp->Flags |= FILE_FLAG_EOF;\r
433         }\r
434         return 0;\r
435 }\r
436 \r
437 EXPORT int fseek(FILE *fp, long int amt, int whence)\r
438 {\r
439         if(!fp || fp->FD == FD_NOTOPEN) {\r
440                 errno = EBADF;\r
441                 return -1;\r
442         }\r
443 \r
444         if( fp->FD == FD_MEMFILE ) {\r
445                 return _fseek_memfile(fp, amt, whence);\r
446         }\r
447         else if( fp->FD == FD_MEMSTREAM ) {\r
448                 return _fseek_memstream(fp, amt, whence);\r
449         }\r
450         else {\r
451                 fflush(fp);\r
452                 _SysSeek(fp->FD, amt, whence);\r
453                 fp->Pos = _SysTell(fp->FD);\r
454                 return 0;\r
455         }\r
456 }\r
457 \r
458 size_t _fwrite_unbuffered(FILE *fp, size_t size, size_t num, const void *data)\r
459 {\r
460         size_t  ret = 0, bytes;\r
461         while( num -- )\r
462         {\r
463                 bytes = _SysWrite(fp->FD, data, size);\r
464                 if( bytes == (size_t)-1 ) {\r
465                         // Oops.\r
466                         // TODO: Set error flag\r
467                         break;\r
468                 }\r
469                 if( bytes != size ) {\r
470                         _SysDebug("_fwrite_unbuffered: Oops, rollback %i/%i bytes!", bytes, size);\r
471                         _SysSeek(fp->FD, -bytes, SEEK_CUR);\r
472                         break;\r
473                 }\r
474                 data = (char*)data + size;\r
475         }\r
476         fp->Pos += ret * size;\r
477         return ret;\r
478 }\r
479 \r
480 size_t _fwrite_memfile(const void *ptr, size_t size, size_t num, FILE *fp)\r
481 {\r
482         size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
483         if( avail == 0 )\r
484                 fp->Flags |= FILE_FLAG_EOF;\r
485         if( num > avail )\r
486                 num = avail;\r
487         size_t  bytes = num * size;\r
488         memcpy(fp->Buffer + fp->Pos, ptr, bytes);\r
489         fp->Pos += bytes;\r
490         return num;\r
491 }\r
492 \r
493 size_t _fwrite_memstream(const void *ptr, size_t size, size_t num, FILE *fp)\r
494 {\r
495         size_t  bytes = size*num;\r
496         // #1. Check if we need to expand\r
497         if( fp->Pos + bytes > fp->BufferSpace )\r
498         {\r
499                 void *newbuf = realloc(fp->Buffer, fp->BufferSpace + bytes);\r
500                 if( !newbuf ) {\r
501                         errno = ENOMEM;\r
502                         return -1;\r
503                 }\r
504                 fp->Buffer = newbuf;\r
505                 fp->BufferSpace = fp->Pos + bytes;\r
506         }\r
507         // #2. Write (use the memfile code for that)\r
508         return _fwrite_memfile(ptr, size, num, fp);\r
509 }\r
510 \r
511 /**\r
512  * \fn EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
513  * \brief Write to a stream\r
514  */\r
515 EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp)\r
516 {\r
517         size_t  ret;\r
518         \r
519         if(!fp || fp->FD == -1)\r
520                 return -1;\r
521         if( size == 0 || num == 0 )\r
522                 return 0;\r
523 \r
524         switch( _GetFileMode(fp) )\r
525         {\r
526         case FILE_FLAG_MODE_READ:\r
527         case FILE_FLAG_MODE_EXEC:\r
528                 errno = EBADF;\r
529                 return 0;\r
530         case FILE_FLAG_MODE_APPEND:\r
531                 fseek(fp, 0, SEEK_END);\r
532         case FILE_FLAG_MODE_WRITE:\r
533                 // Handle memory files first\r
534                 if( fp->FD == FD_MEMFILE ) {\r
535                         return _fwrite_memfile(ptr, size, num, fp);\r
536                 }\r
537                 else if( fp->FD == FD_MEMSTREAM ) {\r
538                         return _fwrite_memstream(ptr, size, num, fp);\r
539                 }\r
540                 else if( fp->BufferSpace )\r
541                 {\r
542                         // Buffering enabled\r
543                         if( fp->BufferSpace - fp->BufferPos < size*num )\r
544                         {\r
545                                 // If there's not enough space, flush and write-through\r
546                                 _fflush_int(fp);        // TODO: check ret\r
547                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
548                         }\r
549                         else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) )\r
550                         {\r
551                                 // Newline present? Flush though\r
552                                 _fflush_int(fp);        // TODO: check ret\r
553                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
554                         }\r
555                         else\r
556                         {\r
557                                 // Copy to buffer\r
558                                 memcpy( fp->Buffer + fp->BufferPos, ptr, size*num );\r
559                                 fp->BufferPos += size*num;\r
560                                 ret = num;\r
561                         }\r
562                 }\r
563                 else\r
564                 {\r
565                         // Bufering disabled, write-though\r
566                         ret = _fwrite_unbuffered(fp, size, num, ptr);\r
567                 }\r
568                 // errno should be set earlier\r
569                 break;\r
570         }\r
571         \r
572         return ret;\r
573 }\r
574 \r
575 size_t _fread_memfile(void *ptr, size_t size, size_t num, FILE *fp)\r
576 {\r
577         size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
578         if( avail == 0 )\r
579                 fp->Flags |= FILE_FLAG_EOF;\r
580         if( num > avail )       num = avail;\r
581         size_t  bytes = num * size;\r
582         memcpy(ptr, fp->Buffer + fp->Pos, bytes);\r
583         fp->Pos += bytes;\r
584         return num;\r
585 }\r
586 \r
587 #if 0\r
588 size_t _fread_memstream(void *ptr, size_t size, size_t num, FILE *fp)\r
589 {\r
590         errno = ENOTIMPL;\r
591         return -1;\r
592 }\r
593 #endif\r
594 \r
595 /**\r
596  * \fn EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
597  * \brief Read from a stream\r
598  */\r
599 EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
600 {\r
601         size_t  ret;\r
602         \r
603         if(!fp || fp->FD == -1)\r
604                 return -1;\r
605         if( size == 0 || num == 0 )\r
606                 return 0;\r
607         \r
608         if( _GetFileMode(fp) != FILE_FLAG_MODE_READ ) {\r
609                 errno = 0;\r
610                 return -1;\r
611         }\r
612 \r
613         if( fp->FD == FD_MEMFILE ) {\r
614                 return _fread_memfile(ptr, size, num, fp);\r
615         }\r
616         else if( fp->FD == FD_MEMSTREAM ) {\r
617                 //return _fread_memstream(ptr, size, num, fp);\r
618                 errno = EBADF;\r
619                 return 0;\r
620         }\r
621         \r
622         // Standard file\r
623         ret = _SysRead(fp->FD, ptr, size*num);\r
624         if( ret == (size_t)-1)\r
625                 return -1;\r
626         if( ret == 0 && size*num > 0 ) {\r
627                 fp->Flags |= FILE_FLAG_EOF;\r
628                 return 0;\r
629         }\r
630         ret /= size;\r
631         \r
632         return ret;\r
633 }\r
634 \r
635 /**\r
636  * \brief Write a string to a stream (without trailing \n)\r
637  */\r
638 EXPORT int fputs(const char *s, FILE *fp)\r
639 {\r
640         int len = strlen(s);\r
641         return fwrite(s, 1, len, fp);\r
642 }\r
643 \r
644 /**\r
645  * \brief Read a line (and possible trailing \n into a buffer)\r
646  */\r
647 EXPORT char *fgets(char *s, int size, FILE *fp)\r
648 {\r
649         int ofs = 0;\r
650         char    ch = '\0';\r
651         while( ofs < size && ch != '\n' )\r
652         {\r
653                 if( fread(&ch, 1, 1, fp) != 1 )\r
654                         break;\r
655                 s[ofs ++] = ch;\r
656         }\r
657         if( ofs < size )\r
658                 s[ofs] = '\0';\r
659         return s;\r
660 }\r
661 \r
662 /**\r
663  * \fn EXPORT int fputc(int c, FILE *fp)\r
664  * \brief Write a single character to the stream\r
665  */\r
666 EXPORT int fputc(int c, FILE *fp)\r
667 {\r
668         return fwrite(&c, 1, 1, fp);\r
669 }\r
670 \r
671 EXPORT int putchar(int c)\r
672 {\r
673         c &= 0xFF;\r
674         return _SysWrite(_stdout, &c, 1);\r
675 }\r
676 \r
677 /**\r
678  * \fn EXPORT int fgetc(FILE *fp)\r
679  * \brief Read a character from the stream\r
680  */\r
681 EXPORT int fgetc(FILE *fp)\r
682 {\r
683         char    ret = 0;\r
684         if( fread(&ret, 1, 1, fp) != 1 )\r
685                 return -1;\r
686         return ret;\r
687 }\r
688 \r
689 EXPORT int getchar(void)\r
690 {\r
691         char    ret = 0;\r
692         if(_SysRead(_stdin, &ret, 1) != 1)      return -1;\r
693         return ret;\r
694 }\r
695 \r
696 EXPORT int puts(const char *str)\r
697 {\r
698          int    len;\r
699         \r
700         if(!str)        return 0;\r
701         len = strlen(str);\r
702         \r
703         len = _SysWrite(_stdout, str, len);\r
704         _SysWrite(_stdout, "\n", 1);\r
705         return len;\r
706 }\r
707 \r
708 // --- INTERNAL ---\r
709 /**\r
710  * \fn FILE *get_file_struct()\r
711  * \brief Returns a file descriptor structure\r
712  */\r
713 FILE *get_file_struct()\r
714 {\r
715          int    i;\r
716         for(i=0;i<STDIO_MAX_STREAMS;i++)\r
717         {\r
718                 if(_iob[i].Flags & FILE_FLAG_ALLOC)\r
719                         continue ;\r
720                 _iob[i].Flags |= FILE_FLAG_ALLOC;\r
721                 _iob[i].FD = -1;\r
722                 _iob[i].Pos = 0;\r
723                 return &_iob[i];\r
724         }\r
725         return NULL;\r
726 }\r
727 \r

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