Usermode/libc - Removed debug in stdio flush
[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 // === PROTOTYPES ===\r
22 struct sFILE    *get_file_struct();\r
23 \r
24 // === GLOBALS ===\r
25 struct sFILE    _iob[STDIO_MAX_STREAMS];        // IO Buffer\r
26 struct sFILE    *stdin; // Standard Input\r
27 struct sFILE    *stdout;        // Standard Output\r
28 struct sFILE    *stderr;        // Standard Error\r
29 ///\note Initialised in SoMain\r
30 static const int STDIN_BUFSIZ = 512;\r
31 static const int STDOUT_BUFSIZ = 512;\r
32 \r
33 // === CODE ===\r
34 void _stdio_init(void)\r
35 {\r
36         // Init FileIO Pointers\r
37         stdin = &_iob[0];\r
38         stdin->FD = 0;\r
39         stdin->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_READ|FILE_FLAG_LINEBUFFERED;\r
40         stdin->Buffer = malloc(STDIN_BUFSIZ);\r
41         stdin->BufferSpace = STDIN_BUFSIZ;\r
42 \r
43         stdout = &_iob[1];\r
44         stdout->FD = 1;\r
45         stdout->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE|FILE_FLAG_LINEBUFFERED;\r
46         stdout->Buffer = malloc(STDOUT_BUFSIZ);\r
47         stdout->BufferSpace = STDOUT_BUFSIZ;\r
48         \r
49         stderr = &_iob[2];\r
50         stderr->FD = 2;\r
51         stderr->Flags = FILE_FLAG_ALLOC|FILE_FLAG_MODE_WRITE;\r
52 }\r
53 \r
54 void _stdio_cleanup(void)\r
55 {\r
56          int    i;\r
57         for( i = 0; i < STDIO_MAX_STREAMS; i ++ )\r
58         {\r
59                 fclose( &_iob[i] );\r
60         }\r
61 }\r
62 \r
63 int _fopen_modetoflags(const char *mode)\r
64 {\r
65         int flags = 0;\r
66         \r
67         // Get main mode\r
68         switch(*mode)\r
69         {\r
70         case 'r':       flags = FILE_FLAG_MODE_READ;    break;\r
71         case 'w':       flags = FILE_FLAG_MODE_WRITE;   break;\r
72         case 'a':       flags = FILE_FLAG_MODE_APPEND;  break;\r
73         case 'x':       flags = FILE_FLAG_MODE_EXEC;    break;  // Acess addon\r
74         default:\r
75                 return -1;\r
76         }\r
77         mode ++;\r
78 \r
79         // Get Modifiers\r
80         for( ; *mode; mode ++ )\r
81         {\r
82                 switch(*mode)\r
83                 {\r
84                 case 'b':       flags |= FILE_FLAG_M_BINARY;    break;\r
85                 case '+':       flags |= FILE_FLAG_M_EXT;       break;\r
86                 default:\r
87                         return -1;\r
88                 }\r
89         }\r
90         \r
91         return flags;\r
92 }\r
93 \r
94 /**\r
95  * \fn FILE *freopen(char *file, char *mode, FILE *fp)\r
96  */\r
97 EXPORT FILE *freopen(const char *file, const char *mode, FILE *fp)\r
98 {\r
99          int    openFlags = 0;\r
100         \r
101         // Sanity Check Arguments\r
102         if(!fp || !file || !mode)       return NULL;\r
103         \r
104         if(fp->FD != -1) {\r
105                 fflush(fp);\r
106         }\r
107 \r
108         // Get stdio flags\r
109         fp->Flags = _fopen_modetoflags(mode);\r
110         if(fp->Flags == -1)\r
111                 return NULL;\r
112         \r
113         // Get Open Flags\r
114         switch(fp->Flags & FILE_FLAG_MODE_MASK)\r
115         {\r
116         // Read\r
117         case FILE_FLAG_MODE_READ:\r
118                 openFlags = OPENFLAG_READ;\r
119                 if(fp->Flags & FILE_FLAG_M_EXT)\r
120                         openFlags |= OPENFLAG_WRITE;\r
121                 break;\r
122         // Write\r
123         case FILE_FLAG_MODE_WRITE:\r
124                 openFlags = OPENFLAG_WRITE;\r
125                 if(fp->Flags & FILE_FLAG_M_EXT)\r
126                         openFlags |= OPENFLAG_READ;\r
127                 break;\r
128         // Execute\r
129         case FILE_FLAG_MODE_APPEND:\r
130                 openFlags = OPENFLAG_APPEND;\r
131                 if(fp->Flags & FILE_FLAG_M_EXT)\r
132                         openFlags |= OPENFLAG_READ;\r
133                 break;\r
134         // Execute\r
135         case FILE_FLAG_MODE_EXEC:\r
136                 openFlags = OPENFLAG_EXEC;\r
137                 break;\r
138         }\r
139 \r
140         //Open File\r
141         if(fp->FD != -1)\r
142                 fp->FD = _SysReopen(fp->FD, file, openFlags);\r
143         else\r
144                 fp->FD = _SysOpen(file, openFlags);\r
145         if(fp->FD == -1) {\r
146                 fp->Flags = 0;\r
147                 return NULL;\r
148         }\r
149         \r
150         if( (fp->Flags & FILE_FLAG_MODE_MASK) == FILE_FLAG_MODE_APPEND ) {\r
151                 _SysSeek(fp->FD, 0, SEEK_END);  //SEEK_END\r
152         }\r
153         \r
154         return fp;\r
155 }\r
156 /**\r
157  \fn FILE *fopen(const char *file, const char *mode)\r
158  \brief Opens a file and returns the pointer\r
159  \param file    String - Filename to open\r
160  \param mode    Mode to open in\r
161 */\r
162 EXPORT FILE *fopen(const char *file, const char *mode)\r
163 {\r
164         FILE    *retFile;\r
165         \r
166         // Sanity Check Arguments\r
167         if(!file || !mode)      return NULL;\r
168         \r
169         // Create Return Structure\r
170         retFile = get_file_struct();\r
171         \r
172         return freopen(file, mode, retFile);\r
173 }\r
174 \r
175 EXPORT FILE *fmemopen(void *buffer, size_t length, const char *mode)\r
176 {\r
177         FILE    *ret;\r
178         \r
179         if( !buffer || !mode )  return NULL;\r
180         \r
181         ret = get_file_struct();\r
182         \r
183         ret->FD = -2;\r
184         ret->Flags = _fopen_modetoflags(mode);\r
185         if(ret->Flags == -1) {\r
186                 ret->Flags = 0;\r
187                 return NULL;\r
188         }\r
189         \r
190         ret->Buffer = buffer;\r
191         ret->BufferPos = 0;\r
192         ret->BufferSpace = length;\r
193         \r
194         return ret;\r
195 }\r
196 \r
197 EXPORT int fclose(FILE *fp)\r
198 {\r
199         if( !(fp->Flags & FILE_FLAG_ALLOC) )\r
200                 return 0;\r
201         fflush(fp);\r
202         if( fp->FD >= 0 ) {\r
203                 _SysClose(fp->FD);\r
204         }\r
205         fp->Flags = 0;\r
206         fp->FD = -1;\r
207         return 0;\r
208 }\r
209 \r
210 EXPORT int setvbuf(FILE *fp, char *buffer, int mode, size_t size)\r
211 {\r
212         if( !fp ) {\r
213                 errno = EINVAL;\r
214                 return 1;\r
215         }\r
216 \r
217         // Check for memory files\r
218         if( fp->FD == -2 ) {\r
219                 errno = EINVAL;\r
220                 return 2;\r
221         }       \r
222 \r
223         // Not strictly needed, as this should only be called before any IO\r
224         fflush(fp);\r
225 \r
226         // Eliminate any pre-existing buffer\r
227         if( fp->Buffer ) {\r
228                 free( fp->Buffer );\r
229                 fp->Buffer = NULL;\r
230                 fp->BufferSpace = 0;\r
231                 fp->BufferPos = 0;\r
232         }\r
233 \r
234         if( mode == _IONBF ) {\r
235                 // Do nothing, buffering was disabled above\r
236         }\r
237         else\r
238         {\r
239                 // Sanity check buffering mode before allocating\r
240                 if( mode != _IOLBF && mode != _IOFBF ) {\r
241                         errno = EINVAL;\r
242                         return 1;\r
243                 }\r
244                 // Allocate a buffer if one was not provided\r
245                 if( !buffer ) {\r
246                         buffer = malloc(size);\r
247                         assert(buffer);\r
248                 }\r
249                 \r
250                 // Set buffer pointer and size\r
251                 fp->Buffer = buffer;\r
252                 fp->BufferSpace = size;\r
253                 \r
254                 // Set mode flag\r
255                 if( mode == _IOLBF )\r
256                         fp->Flags |= FILE_FLAG_LINEBUFFERED;\r
257                 else\r
258                         fp->Flags &= ~FILE_FLAG_LINEBUFFERED;\r
259         }\r
260         \r
261         return 0;\r
262 }\r
263 \r
264 int _fflush_int(FILE *fp)\r
265 {\r
266          int    ret = 0;\r
267         size_t  len;\r
268         \r
269         // Check the buffer contains data\r
270         if( fp->BufferPos == 0 )\r
271                 return 0;\r
272         \r
273         switch(fp->Flags & FILE_FLAG_MODE_MASK)\r
274         {\r
275         // Read - Flush input buffer\r
276         case FILE_FLAG_MODE_READ:\r
277                 fp->BufferPos = 0;\r
278                 break;\r
279         \r
280         // Append - Seek to end and write\r
281         case FILE_FLAG_MODE_APPEND:\r
282                 _SysSeek(fp->FD, fp->BufferOfs, SEEK_SET);\r
283                 len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);\r
284                 if( len < fp->BufferPos )\r
285                         ret = 1;\r
286                 fp->BufferPos -= len;\r
287                 fp->BufferOfs = _SysTell(fp->FD);\r
288                 break;\r
289                 \r
290         // Write - Write buffer\r
291         case FILE_FLAG_MODE_WRITE:\r
292                 //_SysDebug("Flushing to %i '%.*s'", fp->FD, fp->BufferPos, fp->Buffer);\r
293                 len = _SysWrite(fp->FD, fp->Buffer, fp->BufferPos);\r
294                 if( len != fp->BufferPos )\r
295                         ret = 1;\r
296                 fp->BufferPos -= len;\r
297                 break;\r
298         default:\r
299                 break;\r
300         }\r
301         return ret;\r
302 }\r
303 \r
304 EXPORT void fflush(FILE *fp)\r
305 {\r
306         if( !fp || fp->FD == -1 )\r
307                 return ;\r
308         \r
309         // Nothing to do for memory files\r
310         if( fp->FD == -2 )\r
311                 return ;\r
312         \r
313         _fflush_int(fp);\r
314 }\r
315 \r
316 EXPORT void clearerr(FILE *fp)\r
317 {\r
318         if( !fp || fp->FD == -1 )\r
319                 return ;\r
320         \r
321         // TODO: Impliment clearerr()\r
322 }\r
323 \r
324 EXPORT int feof(FILE *fp)\r
325 {\r
326         if( !fp || fp->FD == -1 )\r
327                 return 0;\r
328         return !!(fp->Flags & FILE_FLAG_EOF);\r
329 }\r
330 \r
331 EXPORT int ferror(FILE *fp)\r
332 {\r
333         if( !fp || fp->FD == -1 )\r
334                 return 0;\r
335         return 0;\r
336 }\r
337 EXPORT int fileno(FILE *stream)\r
338 {\r
339         return stream->FD;\r
340 }\r
341 \r
342 EXPORT off_t ftell(FILE *fp)\r
343 {\r
344         if(!fp || fp->FD == -1) return -1;\r
345 \r
346         if( fp->FD == -2 )\r
347                 return fp->Pos; \r
348         else\r
349                 return _SysTell(fp->FD);\r
350 }\r
351 \r
352 EXPORT int fseek(FILE *fp, long int amt, int whence)\r
353 {\r
354         if(!fp || fp->FD == -1) return -1;\r
355 \r
356         if( fp->FD == -2 ) {\r
357                 switch(whence)\r
358                 {\r
359                 case SEEK_CUR:\r
360                         fp->Pos += amt;\r
361                         break;\r
362                 case SEEK_SET:\r
363                         fp->Pos = amt;\r
364                         break;\r
365                 case SEEK_END:\r
366                         if( fp->BufferSpace < (size_t)amt )\r
367                                 fp->Pos = 0;\r
368                         else\r
369                                 fp->Pos = fp->BufferSpace - amt;\r
370                         break;\r
371                 }\r
372                 if(fp->Pos > (off_t)fp->BufferSpace) {\r
373                         fp->Pos = fp->BufferSpace;\r
374                         fp->Flags |= FILE_FLAG_EOF;\r
375                 }\r
376                 return 0;\r
377         }\r
378         else {\r
379                 fflush(fp);\r
380                 _SysSeek(fp->FD, amt, whence);\r
381                 fp->Pos = _SysTell(fp->FD);\r
382                 return 0;\r
383         }\r
384 }\r
385 \r
386 size_t _fwrite_unbuffered(FILE *fp, size_t size, size_t num, const void *data)\r
387 {\r
388         size_t  ret = 0, bytes;\r
389         while( num -- )\r
390         {\r
391                 bytes = _SysWrite(fp->FD, data, size);\r
392                 if( bytes != size ) {\r
393                         _SysDebug("_fwrite_unbuffered: Oops, rollback %i/%i bytes!", bytes, size);\r
394                         _SysSeek(fp->FD, -bytes, SEEK_CUR);\r
395                         break;\r
396                 }\r
397                 data = (char*)data + size;\r
398         }\r
399         fp->Pos += ret * size;\r
400         return ret;\r
401 }\r
402 \r
403 /**\r
404  * \fn EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
405  * \brief Write to a stream\r
406  */\r
407 EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp)\r
408 {\r
409         size_t  ret;\r
410         \r
411         if(!fp || fp->FD == -1)\r
412                 return -1;\r
413         if( size == 0 || num == 0 )\r
414                 return 0;\r
415 \r
416         // Handle memory files first\r
417         if( fp->FD == -2 ) {\r
418                 size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
419                 if( avail == 0 )\r
420                         fp->Flags |= FILE_FLAG_EOF;\r
421                 if( num > avail )\r
422                         num = avail;\r
423                 size_t  bytes = num * size;\r
424                 memcpy(fp->Buffer + fp->Pos, ptr, bytes);\r
425                 fp->Pos += bytes;\r
426                 return num;\r
427         }\r
428 \r
429         switch( _GetFileMode(fp) )\r
430         {\r
431         case FILE_FLAG_MODE_READ:\r
432         case FILE_FLAG_MODE_EXEC:\r
433                 return 0;\r
434         case FILE_FLAG_MODE_APPEND:\r
435                 fseek(fp, 0, SEEK_END);\r
436         case FILE_FLAG_MODE_WRITE:\r
437                 if( fp->BufferSpace )\r
438                 {\r
439                         // Buffering enabled\r
440                         if( fp->BufferSpace - fp->BufferPos < size*num )\r
441                         {\r
442                                 // If there's not enough space, flush and write-through\r
443                                 _fflush_int(fp);        // TODO: check ret\r
444                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
445                         }\r
446                         else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) )\r
447                         {\r
448                                 // Newline present? Flush though\r
449                                 _fflush_int(fp);        // TODO: check ret\r
450                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
451                         }\r
452                         else\r
453                         {\r
454                                 // Copy to buffer\r
455                                 memcpy( fp->Buffer + fp->BufferPos, ptr, size*num );\r
456                                 fp->BufferPos += size*num;\r
457                                 ret = num;\r
458                         }\r
459                 }\r
460                 else\r
461                 {\r
462                         // Bufering disabled, write-though\r
463                         ret = _fwrite_unbuffered(fp, size, num, ptr);\r
464                 }\r
465                 // errno should be set earlier\r
466                 break;\r
467         }\r
468         \r
469         return ret;\r
470 }\r
471 \r
472 /**\r
473  * \fn EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
474  * \brief Read from a stream\r
475  */\r
476 EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
477 {\r
478         size_t  ret;\r
479         \r
480         if(!fp || fp->FD == -1)\r
481                 return -1;\r
482         if( size == 0 || num == 0 )\r
483                 return 0;\r
484 \r
485         if( fp->FD == -2 ) {\r
486                 size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
487                 if( avail == 0 )\r
488                         fp->Flags |= FILE_FLAG_EOF;\r
489                 if( num > avail )       num = avail;\r
490                 size_t  bytes = num * size;\r
491                 memcpy(ptr, fp->Buffer + fp->Pos, bytes);\r
492                 fp->Pos += bytes;\r
493                 return num;\r
494         }\r
495         \r
496         // Standard file\r
497         ret = _SysRead(fp->FD, ptr, size*num);\r
498         if( ret == (size_t)-1)\r
499                 return -1;\r
500         if( ret == 0 && size*num > 0 ) {\r
501                 fp->Flags |= FILE_FLAG_EOF;\r
502                 return 0;\r
503         }\r
504         ret /= size;\r
505         \r
506         return ret;\r
507 }\r
508 \r
509 /**\r
510  * \brief Write a string to a stream (without trailing \n)\r
511  */\r
512 EXPORT int fputs(const char *s, FILE *fp)\r
513 {\r
514         int len = strlen(s);\r
515         return fwrite(s, 1, len, fp);\r
516 }\r
517 \r
518 /**\r
519  * \brief Read a line (and possible trailing \n into a buffer)\r
520  */\r
521 EXPORT char *fgets(char *s, int size, FILE *fp)\r
522 {\r
523         int ofs = 0;\r
524         char    ch = '\0';\r
525         while( ofs < size && ch != '\n' )\r
526         {\r
527                 if( fread(&ch, 1, 1, fp) != 1 )\r
528                         break;\r
529                 s[ofs ++] = ch;\r
530         }\r
531         if( ofs < size )\r
532                 s[ofs] = '\0';\r
533         return s;\r
534 }\r
535 \r
536 /**\r
537  * \fn EXPORT int fputc(int c, FILE *fp)\r
538  * \brief Write a single character to the stream\r
539  */\r
540 EXPORT int fputc(int c, FILE *fp)\r
541 {\r
542         return fwrite(&c, 1, 1, fp);\r
543 }\r
544 \r
545 EXPORT int putchar(int c)\r
546 {\r
547         c &= 0xFF;\r
548         return _SysWrite(_stdout, &c, 1);\r
549 }\r
550 \r
551 /**\r
552  * \fn EXPORT int fgetc(FILE *fp)\r
553  * \brief Read a character from the stream\r
554  */\r
555 EXPORT int fgetc(FILE *fp)\r
556 {\r
557         char    ret = 0;\r
558         if( fread(&ret, 1, 1, fp) != 1 )\r
559                 return -1;\r
560         return ret;\r
561 }\r
562 \r
563 EXPORT int getchar(void)\r
564 {\r
565         char    ret = 0;\r
566         if(_SysRead(_stdin, &ret, 1) != 1)      return -1;\r
567         return ret;\r
568 }\r
569 \r
570 // --- INTERNAL ---\r
571 /**\r
572  * \fn FILE *get_file_struct()\r
573  * \brief Returns a file descriptor structure\r
574  */\r
575 FILE *get_file_struct()\r
576 {\r
577          int    i;\r
578         for(i=0;i<STDIO_MAX_STREAMS;i++)\r
579         {\r
580                 if(_iob[i].Flags & FILE_FLAG_ALLOC)\r
581                         continue ;\r
582                 _iob[i].Flags |= FILE_FLAG_ALLOC;\r
583                 _iob[i].FD = -1;\r
584                 _iob[i].Pos = 0;\r
585                 return &_iob[i];\r
586         }\r
587         return NULL;\r
588 }\r
589 \r
590 EXPORT int puts(const char *str)\r
591 {\r
592          int    len;\r
593         \r
594         if(!str)        return 0;\r
595         len = strlen(str);\r
596         \r
597         len = _SysWrite(_stdout, str, len);\r
598         _SysWrite(_stdout, "\n", 1);\r
599         return len;\r
600 }\r
601 \r

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