Usermode/libc - Fixed back-seek of -1 when a write error occurs in buffering
[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_t)-1 ) {\r
393                         // Oops.\r
394                         // TODO: Set error flag\r
395                         break;\r
396                 }\r
397                 if( bytes != size ) {\r
398                         _SysDebug("_fwrite_unbuffered: Oops, rollback %i/%i bytes!", bytes, size);\r
399                         _SysSeek(fp->FD, -bytes, SEEK_CUR);\r
400                         break;\r
401                 }\r
402                 data = (char*)data + size;\r
403         }\r
404         fp->Pos += ret * size;\r
405         return ret;\r
406 }\r
407 \r
408 /**\r
409  * \fn EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
410  * \brief Write to a stream\r
411  */\r
412 EXPORT size_t fwrite(const void *ptr, size_t size, size_t num, FILE *fp)\r
413 {\r
414         size_t  ret;\r
415         \r
416         if(!fp || fp->FD == -1)\r
417                 return -1;\r
418         if( size == 0 || num == 0 )\r
419                 return 0;\r
420 \r
421         // Handle memory files first\r
422         if( fp->FD == -2 ) {\r
423                 size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
424                 if( avail == 0 )\r
425                         fp->Flags |= FILE_FLAG_EOF;\r
426                 if( num > avail )\r
427                         num = avail;\r
428                 size_t  bytes = num * size;\r
429                 memcpy(fp->Buffer + fp->Pos, ptr, bytes);\r
430                 fp->Pos += bytes;\r
431                 return num;\r
432         }\r
433 \r
434         switch( _GetFileMode(fp) )\r
435         {\r
436         case FILE_FLAG_MODE_READ:\r
437         case FILE_FLAG_MODE_EXEC:\r
438                 return 0;\r
439         case FILE_FLAG_MODE_APPEND:\r
440                 fseek(fp, 0, SEEK_END);\r
441         case FILE_FLAG_MODE_WRITE:\r
442                 if( fp->BufferSpace )\r
443                 {\r
444                         // Buffering enabled\r
445                         if( fp->BufferSpace - fp->BufferPos < size*num )\r
446                         {\r
447                                 // If there's not enough space, flush and write-through\r
448                                 _fflush_int(fp);        // TODO: check ret\r
449                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
450                         }\r
451                         else if( (fp->Flags & FILE_FLAG_LINEBUFFERED) && memchr(ptr,'\n',size*num) )\r
452                         {\r
453                                 // Newline present? Flush though\r
454                                 _fflush_int(fp);        // TODO: check ret\r
455                                 ret = _fwrite_unbuffered(fp, size, num, ptr);\r
456                         }\r
457                         else\r
458                         {\r
459                                 // Copy to buffer\r
460                                 memcpy( fp->Buffer + fp->BufferPos, ptr, size*num );\r
461                                 fp->BufferPos += size*num;\r
462                                 ret = num;\r
463                         }\r
464                 }\r
465                 else\r
466                 {\r
467                         // Bufering disabled, write-though\r
468                         ret = _fwrite_unbuffered(fp, size, num, ptr);\r
469                 }\r
470                 // errno should be set earlier\r
471                 break;\r
472         }\r
473         \r
474         return ret;\r
475 }\r
476 \r
477 /**\r
478  * \fn EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
479  * \brief Read from a stream\r
480  */\r
481 EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
482 {\r
483         size_t  ret;\r
484         \r
485         if(!fp || fp->FD == -1)\r
486                 return -1;\r
487         if( size == 0 || num == 0 )\r
488                 return 0;\r
489 \r
490         if( fp->FD == -2 ) {\r
491                 size_t  avail = (fp->BufferSpace - fp->Pos) / size;\r
492                 if( avail == 0 )\r
493                         fp->Flags |= FILE_FLAG_EOF;\r
494                 if( num > avail )       num = avail;\r
495                 size_t  bytes = num * size;\r
496                 memcpy(ptr, fp->Buffer + fp->Pos, bytes);\r
497                 fp->Pos += bytes;\r
498                 return num;\r
499         }\r
500         \r
501         // Standard file\r
502         ret = _SysRead(fp->FD, ptr, size*num);\r
503         if( ret == (size_t)-1)\r
504                 return -1;\r
505         if( ret == 0 && size*num > 0 ) {\r
506                 fp->Flags |= FILE_FLAG_EOF;\r
507                 return 0;\r
508         }\r
509         ret /= size;\r
510         \r
511         return ret;\r
512 }\r
513 \r
514 /**\r
515  * \brief Write a string to a stream (without trailing \n)\r
516  */\r
517 EXPORT int fputs(const char *s, FILE *fp)\r
518 {\r
519         int len = strlen(s);\r
520         return fwrite(s, 1, len, fp);\r
521 }\r
522 \r
523 /**\r
524  * \brief Read a line (and possible trailing \n into a buffer)\r
525  */\r
526 EXPORT char *fgets(char *s, int size, FILE *fp)\r
527 {\r
528         int ofs = 0;\r
529         char    ch = '\0';\r
530         while( ofs < size && ch != '\n' )\r
531         {\r
532                 if( fread(&ch, 1, 1, fp) != 1 )\r
533                         break;\r
534                 s[ofs ++] = ch;\r
535         }\r
536         if( ofs < size )\r
537                 s[ofs] = '\0';\r
538         return s;\r
539 }\r
540 \r
541 /**\r
542  * \fn EXPORT int fputc(int c, FILE *fp)\r
543  * \brief Write a single character to the stream\r
544  */\r
545 EXPORT int fputc(int c, FILE *fp)\r
546 {\r
547         return fwrite(&c, 1, 1, fp);\r
548 }\r
549 \r
550 EXPORT int putchar(int c)\r
551 {\r
552         c &= 0xFF;\r
553         return _SysWrite(_stdout, &c, 1);\r
554 }\r
555 \r
556 /**\r
557  * \fn EXPORT int fgetc(FILE *fp)\r
558  * \brief Read a character from the stream\r
559  */\r
560 EXPORT int fgetc(FILE *fp)\r
561 {\r
562         char    ret = 0;\r
563         if( fread(&ret, 1, 1, fp) != 1 )\r
564                 return -1;\r
565         return ret;\r
566 }\r
567 \r
568 EXPORT int getchar(void)\r
569 {\r
570         char    ret = 0;\r
571         if(_SysRead(_stdin, &ret, 1) != 1)      return -1;\r
572         return ret;\r
573 }\r
574 \r
575 // --- INTERNAL ---\r
576 /**\r
577  * \fn FILE *get_file_struct()\r
578  * \brief Returns a file descriptor structure\r
579  */\r
580 FILE *get_file_struct()\r
581 {\r
582          int    i;\r
583         for(i=0;i<STDIO_MAX_STREAMS;i++)\r
584         {\r
585                 if(_iob[i].Flags & FILE_FLAG_ALLOC)\r
586                         continue ;\r
587                 _iob[i].Flags |= FILE_FLAG_ALLOC;\r
588                 _iob[i].FD = -1;\r
589                 _iob[i].Pos = 0;\r
590                 return &_iob[i];\r
591         }\r
592         return NULL;\r
593 }\r
594 \r
595 EXPORT int puts(const char *str)\r
596 {\r
597          int    len;\r
598         \r
599         if(!str)        return 0;\r
600         len = strlen(str);\r
601         \r
602         len = _SysWrite(_stdout, str, len);\r
603         _SysWrite(_stdout, "\n", 1);\r
604         return len;\r
605 }\r
606 \r

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