Usermode/libc - Fixed implementation of atexit and printf
[tpg/acess2.git] / Usermode / Libraries / libc.so_src / fileIO.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 \r
13 #define DEBUG_BUILD     0\r
14 \r
15 // === CONSTANTS ===\r
16 #define _stdin  0\r
17 #define _stdout 1\r
18 \r
19 // === PROTOTYPES ===\r
20 EXPORT void     itoa(char *buf, uint64_t num, uint base, int minLength, char pad, int bSigned);\r
21 struct sFILE    *get_file_struct();\r
22 \r
23 // === GLOBALS ===\r
24 struct sFILE    _iob[STDIO_MAX_STREAMS];        // IO Buffer\r
25 struct sFILE    *stdin; // Standard Input\r
26 struct sFILE    *stdout;        // Standard Output\r
27 struct sFILE    *stderr;        // Standard Error\r
28 ///\note Initialised in SoMain\r
29 \r
30 // === CODE ===\r
31 /**\r
32  * \fn FILE *freopen(char *file, char *mode, FILE *fp)\r
33  */\r
34 EXPORT FILE *freopen(const char *file, const char *mode, FILE *fp)\r
35 {\r
36          int    openFlags = 0;\r
37          int    i;\r
38         \r
39         // Sanity Check Arguments\r
40         if(!fp || !file || !mode)       return NULL;\r
41         \r
42         if(fp->Flags) {\r
43                 fflush(fp);\r
44         } else\r
45                 fp->FD = -1;\r
46         \r
47         // Get main mode\r
48         switch(mode[0])\r
49         {\r
50         case 'r':       fp->Flags = FILE_FLAG_MODE_READ;        break;\r
51         case 'w':       fp->Flags = FILE_FLAG_MODE_WRITE;       break;\r
52         case 'a':       fp->Flags = FILE_FLAG_MODE_APPEND;      break;\r
53         case 'x':       fp->Flags = FILE_FLAG_MODE_EXEC;        break;\r
54         default:\r
55                 return NULL;\r
56         }\r
57         // Get Modifiers\r
58         for(i=1;mode[i];i++)\r
59         {\r
60                 switch(mode[i])\r
61                 {\r
62                 case '+':       fp->Flags |= FILE_FLAG_M_EXT;\r
63                 }\r
64         }\r
65         \r
66         // Get Open Flags\r
67         switch(mode[0])\r
68         {\r
69         // Read\r
70         case 'r':       openFlags = OPENFLAG_READ;\r
71                 if(fp->Flags & FILE_FLAG_M_EXT)\r
72                         openFlags |= OPENFLAG_WRITE;\r
73                 break;\r
74         // Write\r
75         case 'w':       openFlags = OPENFLAG_WRITE;\r
76                 if(fp->Flags & FILE_FLAG_M_EXT)\r
77                         openFlags |= OPENFLAG_READ;\r
78                 break;\r
79         // Execute\r
80         case 'x':       openFlags = OPENFLAG_EXEC;\r
81                 break;\r
82         }\r
83         \r
84         //Open File\r
85         if(fp->FD != -1)\r
86                 fp->FD = reopen(fp->FD, file, openFlags);\r
87         else\r
88                 fp->FD = open(file, openFlags);\r
89         if(fp->FD == -1) {\r
90                 fp->Flags = 0;\r
91                 return NULL;\r
92         }\r
93         \r
94         if(mode[0] == 'a') {\r
95                 seek(fp->FD, 0, SEEK_END);      //SEEK_END\r
96         }\r
97         \r
98         return fp;\r
99 }\r
100 /**\r
101  \fn FILE *fopen(const char *file, const char *mode)\r
102  \brief Opens a file and returns the pointer\r
103  \param file    String - Filename to open\r
104  \param mode    Mode to open in\r
105 */\r
106 EXPORT FILE *fopen(const char *file, const char *mode)\r
107 {\r
108         FILE    *retFile;\r
109         \r
110         // Sanity Check Arguments\r
111         if(!file || !mode)      return NULL;\r
112         \r
113         // Create Return Structure\r
114         retFile = get_file_struct();\r
115         \r
116         return freopen(file, mode, retFile);\r
117 }\r
118 \r
119 EXPORT void fclose(FILE *fp)\r
120 {\r
121         close(fp->FD);\r
122         free(fp);\r
123 }\r
124 \r
125 EXPORT void fflush(FILE *fp)\r
126 {\r
127         ///\todo Implement\r
128 }\r
129 \r
130 EXPORT long int ftell(FILE *fp)\r
131 {\r
132         if(!fp || !fp->FD)      return -1;\r
133         \r
134         return tell(fp->FD);\r
135 }\r
136 \r
137 EXPORT int fseek(FILE *fp, long int amt, int whence)\r
138 {\r
139         if(!fp || !fp->FD)      return -1;\r
140         \r
141         return seek(fp->FD, amt, whence);\r
142 }\r
143 \r
144 \r
145 /**\r
146  * \fn EXPORT int vfprintf(FILE *fp, const char *format, va_list args)\r
147  * \brief Print to a file from a variable argument list\r
148  */\r
149 EXPORT int vfprintf(FILE *fp, const char *format, va_list args)\r
150 {\r
151         va_list tmpList;\r
152          int    size;\r
153         char    sbuf[1024];\r
154         char    *buf = sbuf;\r
155  \r
156 \r
157 \r
158         if(!fp || !format)      return -1;\r
159 \r
160         va_copy(tmpList, args);\r
161         \r
162         size = vsnprintf(sbuf, 1024, (char*)format, tmpList);\r
163         \r
164         if( size >= 1024 )\r
165         {\r
166                 buf = (char*)malloc(size+1);\r
167                 if(!buf) {\r
168                         write(_stdout, 31, "vfprintf ERROR: malloc() failed");\r
169                         return 0;\r
170                 }\r
171                 buf[size] = '\0';\r
172         \r
173                 // Print\r
174                 vsnprintf(buf, size+1, (char*)format, args);\r
175         }\r
176         \r
177         // Write to stream\r
178         write(fp->FD, size, buf);\r
179         \r
180         // Free buffer\r
181         free(buf);\r
182         \r
183         // Return written byte count\r
184         return size;\r
185 }\r
186 \r
187 /**\r
188  * \fn int fprintf(FILE *fp, const char *format, ...)\r
189  * \brief Print a formatted string to a stream\r
190  */\r
191 EXPORT int fprintf(FILE *fp, const char *format, ...)\r
192 {\r
193         va_list args;\r
194          int    ret;\r
195         \r
196         // Get Size\r
197         va_start(args, format);\r
198         ret = vfprintf(fp, (char*)format, args);\r
199         va_end(args);\r
200         \r
201         return ret;\r
202 }\r
203 \r
204 /**\r
205  * \fn EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
206  * \brief Write to a stream\r
207  */\r
208 EXPORT size_t fwrite(void *ptr, size_t size, size_t num, FILE *fp)\r
209 {\r
210          int    ret;\r
211         if(!fp || !fp->FD)      return -1;\r
212         \r
213         ret = write(fp->FD, size*num, ptr);\r
214         \r
215         return ret;\r
216 }\r
217 \r
218 /**\r
219  * \fn EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
220  * \brief Read from a stream\r
221  */\r
222 EXPORT size_t fread(void *ptr, size_t size, size_t num, FILE *fp)\r
223 {\r
224          int    ret;\r
225         if(!fp || !fp->FD)      return -1;\r
226         \r
227         ret = read(fp->FD, size*num, ptr);\r
228         \r
229         return ret;\r
230 }\r
231 \r
232 /**\r
233  * \fn EXPORT int fputc(int c, FILE *fp)\r
234  * \brief Write a single character to the stream\r
235  */\r
236 EXPORT int fputc(int c, FILE *fp)\r
237 {\r
238         if(!fp || !fp->FD)      return -1;\r
239         return write(fp->FD, 1, &c);\r
240 }\r
241 \r
242 /**\r
243  * \fn EXPORT int fgetc(FILE *fp)\r
244  * \brief Read a character from the stream\r
245  */\r
246 EXPORT int fgetc(FILE *fp)\r
247 {\r
248         char    ret = 0;\r
249         if(!fp) return -1;\r
250         if(read(fp->FD, 1, &ret) == -1) return -1;\r
251         return ret;\r
252 }\r
253 \r
254 // --- INTERNAL ---\r
255 /**\r
256  * \fn FILE *get_file_struct()\r
257  * \brief Returns a file descriptor structure\r
258  */\r
259 FILE *get_file_struct()\r
260 {\r
261          int    i;\r
262         for(i=0;i<STDIO_MAX_STREAMS;i++)\r
263         {\r
264                 if(_iob[i].Flags == 0)  return &_iob[i];\r
265         }\r
266         return NULL;\r
267 }\r
268 \r
269 EXPORT int putchar(int ch)\r
270 {\r
271         return write(_stdout, 1, (char*)&ch);\r
272 }\r
273 \r
274 EXPORT int      puts(const char *str)\r
275 {\r
276          int    len;\r
277         \r
278         if(!str)        return 0;\r
279         len = strlen(str);\r
280         \r
281         len = write(_stdout, len, (char*)str);\r
282         write(_stdout, 1, "\n");\r
283         return len;\r
284 }\r
285 \r
286 EXPORT int vsprintf(char * __s, const char *__format, va_list __args)\r
287 {\r
288         return vsnprintf(__s, 0x7FFFFFFF, __format, __args);\r
289 }\r
290 \r
291 //sprintfv\r
292 /**\r
293  * \fn EXPORT void vsnprintf(char *buf, const char *format, va_list args)\r
294  * \brief Prints a formatted string to a buffer\r
295  * \param buf   Pointer - Destination Buffer\r
296  * \param format        String - Format String\r
297  * \param args  VarArgs List - Arguments\r
298  */\r
299 EXPORT int vsnprintf(char *buf, size_t __maxlen, const char *format, va_list args)\r
300 {\r
301         char    tmp[65];\r
302          int    c, minSize, precision, len;\r
303          int    pos = 0;\r
304         char    *p;\r
305         char    pad;\r
306         uint64_t        arg;\r
307          int    bLongLong, bPadLeft;\r
308 \r
309         void _addchar(char ch)\r
310         {\r
311                 if(buf && pos < __maxlen)       buf[pos] = ch;\r
312                 pos ++;\r
313         }\r
314 \r
315         tmp[32] = '\0';\r
316         \r
317         while((c = *format++) != 0)\r
318         {\r
319                 // Non-control character\r
320                 if (c != '%') {\r
321                         _addchar(c);\r
322                         continue;\r
323                 }\r
324                 \r
325                 // Control Character\r
326                 c = *format++;\r
327                 if(c == '%') {  // Literal %\r
328                         _addchar('%');\r
329                         continue;\r
330                 }\r
331                 \r
332                 bPadLeft = 0;\r
333                 bLongLong = 0;\r
334                 minSize = 0;\r
335                 precision = -1;\r
336                 pad = ' ';\r
337                 \r
338                 // Padding Character\r
339                 if(c == '0') {\r
340                         pad = '0';\r
341                         c = *format++;\r
342                 }\r
343                 // Padding length\r
344                 if( c == '*' ) {\r
345                         // Variable length\r
346                         minSize = va_arg(args, size_t);\r
347                         c = *format++;\r
348                 }\r
349                 else {\r
350                         if('1' <= c && c <= '9')\r
351                         {\r
352                                 minSize = 0;\r
353                                 while('0' <= c && c <= '9')\r
354                                 {\r
355                                         minSize *= 10;\r
356                                         minSize += c - '0';\r
357                                         c = *format++;\r
358                                 }\r
359                         }\r
360                 }\r
361 \r
362                 // Precision\r
363                 if(c == '.') {\r
364                         c = *format++;\r
365                         if(c == '*') {\r
366                                 precision = va_arg(args, size_t);\r
367                                 c = *format++;\r
368                         }\r
369                         else if('1' <= c && c <= '9')\r
370                         {\r
371                                 precision = 0;\r
372                                 while('0' <= c && c <= '9')\r
373                                 {\r
374                                         precision *= 10;\r
375                                         precision += c - '0';\r
376                                         c = *format++;\r
377                                 }\r
378                         }\r
379                 }\r
380         \r
381                 // Check for long long\r
382                 bLongLong = 0;\r
383                 if(c == 'l')\r
384                 {\r
385                         c = *format++;\r
386                         if(c == 'l') {\r
387                                 bLongLong = 1;\r
388                         }\r
389                 }\r
390                 \r
391                 // Just help things along later\r
392                 p = tmp;\r
393                 \r
394                 // Get Type\r
395                 switch( c )\r
396                 {\r
397                 // Signed Integer\r
398                 case 'd':       case 'i':\r
399                         // Get Argument\r
400                         if(bLongLong)   arg = va_arg(args, int64_t);\r
401                         else                    arg = va_arg(args, int32_t);\r
402                         itoa(tmp, arg, 10, minSize, pad, 1);\r
403                         precision = -1;\r
404                         goto sprintf_puts;\r
405                 \r
406                 // Unsigned Integer\r
407                 case 'u':\r
408                         // Get Argument\r
409                         if(bLongLong)   arg = va_arg(args, uint64_t);\r
410                         else                    arg = va_arg(args, uint32_t);\r
411                         itoa(tmp, arg, 10, minSize, pad, 0);\r
412                         precision = -1;\r
413                         goto sprintf_puts;\r
414                 \r
415                 // Pointer\r
416                 case 'p':\r
417                         _addchar('*');\r
418                         _addchar('0');\r
419                         _addchar('x');\r
420                         arg = va_arg(args, uint32_t);\r
421                         itoa(tmp, arg, 16, minSize, pad, 0);\r
422                         precision = -1;\r
423                         goto sprintf_puts;\r
424                 // Unsigned Hexadecimal\r
425                 case 'x':\r
426                         if(bLongLong)   arg = va_arg(args, uint64_t);\r
427                         else                    arg = va_arg(args, uint32_t);\r
428                         itoa(tmp, arg, 16, minSize, pad, 0);\r
429                         precision = -1;\r
430                         goto sprintf_puts;\r
431                 \r
432                 // Unsigned Octal\r
433                 case 'o':\r
434                         if(bLongLong)   arg = va_arg(args, uint64_t);\r
435                         else                    arg = va_arg(args, uint32_t);\r
436                         itoa(tmp, arg, 8, minSize, pad, 0);\r
437                         precision = -1;\r
438                         goto sprintf_puts;\r
439                 \r
440                 // Unsigned binary\r
441                 case 'b':\r
442                         if(bLongLong)   arg = va_arg(args, uint64_t);\r
443                         else                    arg = va_arg(args, uint32_t);\r
444                         itoa(tmp, arg, 2, minSize, pad, 0);\r
445                         precision = -1;\r
446                         goto sprintf_puts;\r
447 \r
448                 // String\r
449                 case 's':\r
450                         arg = va_arg(args, uint32_t);\r
451                         p = (void*)(intptr_t)arg;\r
452                 sprintf_puts:\r
453                         if(!p)  p = "(null)";\r
454                         //_SysDebug("vsnprintf: p = '%s'", p);\r
455                         if(precision >= 0)\r
456                                 len = strnlen(p, precision);\r
457                         else\r
458                                 len = strlen(p);\r
459                         if(bPadLeft)    while(minSize > len++)  _addchar(pad);\r
460                         while( *p ) {\r
461                                 if(precision >= 0 && precision -- == 0)\r
462                                         break;\r
463                                 _addchar(*p++);\r
464                         }\r
465                         if(!bPadLeft)   while(minSize > len++)  _addchar(pad);\r
466                         break;\r
467 \r
468                 // Unknown, just treat it as a character\r
469                 default:\r
470                         arg = va_arg(args, uint32_t);\r
471                         _addchar(arg);\r
472                         break;\r
473                 }\r
474         }\r
475         _addchar('\0');\r
476         pos --;\r
477         \r
478         //_SysDebug("vsnprintf: buf = '%s'", buf);\r
479         \r
480         return pos;\r
481 }\r
482 \r
483 const char cUCDIGITS[] = "0123456789ABCDEF";\r
484 /**\r
485  * \fn static void itoa(char *buf, uint64_t num, int base, int minLength, char pad, int bSigned)\r
486  * \brief Convert an integer into a character string\r
487  * \param buf   Destination Buffer\r
488  * \param num   Number to convert\r
489  * \param base  Base-n number output\r
490  * \param minLength     Minimum length of output\r
491  * \param pad   Padding used to ensure minLength\r
492  * \param bSigned       Signed number output?\r
493  */\r
494 EXPORT void itoa(char *buf, uint64_t num, size_t base, int minLength, char pad, int bSigned)\r
495 {\r
496         char    tmpBuf[64];\r
497          int    pos=0, i;\r
498 \r
499         if(!buf)        return;\r
500         if(base > 16 || base < 2) {\r
501                 buf[0] = 0;\r
502                 return;\r
503         }\r
504         \r
505         if(bSigned && (int64_t)num < 0)\r
506         {\r
507                 num = -num;\r
508                 bSigned = 1;\r
509         } else\r
510                 bSigned = 0;\r
511         \r
512         // Encode into reversed string\r
513         while(num > base-1) {\r
514                 tmpBuf[pos++] = cUCDIGITS[ num % base ];\r
515                 num = (uint64_t) num / (uint64_t)base;          // Shift {number} right 1 digit\r
516         }\r
517 \r
518         tmpBuf[pos++] = cUCDIGITS[ num % base ];                // Last digit of {number}\r
519         if(bSigned)     tmpBuf[pos++] = '-';    // Append sign symbol if needed\r
520         \r
521         i = 0;\r
522         minLength -= pos;\r
523         while(minLength-- > 0)  buf[i++] = pad;\r
524         while(pos-- > 0)                buf[i++] = tmpBuf[pos]; // Reverse the order of characters\r
525         buf[i] = 0;\r
526 }\r
527 \r
528 /**\r
529  * \fn EXPORT int printf(const char *format, ...)\r
530  * \brief Print a string to stdout\r
531  */\r
532 EXPORT int printf(const char *format, ...)\r
533 {\r
534         #if 1\r
535          int    size;\r
536         char    sbuf[1024];\r
537         char    *buf = sbuf;\r
538         va_list args;\r
539         \r
540         // Get final size\r
541         va_start(args, format);\r
542         size = vsnprintf(sbuf, 1024, (char*)format, args);\r
543         va_end(args);\r
544         \r
545         if( size >= 1024 ) {\r
546                 // Allocate buffer\r
547                 buf = (char*)malloc(size+1);\r
548                 if(buf) {\r
549                         write(_stdout, 29, "PRINTF ERROR: malloc() failed");\r
550                         return 0;\r
551                 }\r
552                 buf[size] = '\0';\r
553         \r
554                 // Fill Buffer\r
555                 va_start(args, format);\r
556                 vsnprintf(buf, size+1, (char*)format, args);\r
557                 va_end(args);\r
558         }\r
559         \r
560         // Send to stdout\r
561         write(_stdout, size+1, buf);\r
562         \r
563         // Free buffer\r
564         free(buf);\r
565         // Return\r
566         return size;\r
567         \r
568         #else\r
569         \r
570          int    ret;\r
571         va_list args;\r
572         va_start(args, format);\r
573         ret = fprintfv(stdout, (char*)format, args);\r
574         va_end(args);\r
575         return ret;\r
576         #endif\r
577 }\r
578 \r
579 /**\r
580  * \fn EXPORT int sprintf(const char *buf, char *format, ...)\r
581  * \brief Print a formatted string to a buffer\r
582  */\r
583 EXPORT int sprintf(char *buf, const char *format, ...)\r
584 {\r
585          int    ret;\r
586         va_list args;\r
587         va_start(args, format);\r
588         ret = vsprintf((char*)buf, (char*)format, args);\r
589         va_end(args);\r
590         return ret;\r
591 }\r
592 \r
593 /**\r
594  * \fn EXPORT int snprintf(const char *buf, size_t maxlen, char *format, ...)\r
595  * \brief Print a formatted string to a buffer\r
596  */\r
597 EXPORT int snprintf(char *buf, size_t maxlen, const char *format, ...)\r
598 {\r
599          int    ret;\r
600         va_list args;\r
601         va_start(args, format);\r
602         ret = vsnprintf((char*)buf, maxlen, (char*)format, args);\r
603         va_end(args);\r
604         return ret;\r
605 }\r

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