]> rtime.felk.cvut.cz Git - arc.git/blob - common/printf.c
Fixes to make the os kernel test framework pass.
[arc.git] / common / printf.c
1 /*\r
2  * Copyright ArcCore AB\r
3  *\r
4  * A simple implementation of all formatted xxprintf functionallity.\r
5  *\r
6  * DESIGN CRITERIA:\r
7  *  - Reentrant\r
8  *  - Use little stack\r
9  *\r
10  *  What you normally would do is to print to a buffer of some kind and then\r
11  *  call write(). However little stack indicates that we can't place buffers\r
12  *  on the stack and reentrant tells us that we can't have a static buffer.\r
13  *  That leaves us with printing characters as it is ready. From a speed\r
14  *  point of view that is less than optimal, but that the way it's got to be.\r
15  *  (Could make a 16 long char buffer??)\r
16  *\r
17  *  This is just intended to be used as a replacement for newlibs implementation.\r
18  *  Newlib porting interface have the following API:\r
19  *    int write(  int fd, char *buf, int nbytes)\r
20  *\r
21  *\r
22  *  Note that puts(), putc() are still the newlib variants....\r
23  *\r
24  *    printf()       -> vfprintf(stdout,) -> vsnprintf(buf,)\r
25  *                                           write()\r
26  *    vprintf()      -> vfprintf(stdout,) -> vsnprintf(buf,)\r
27  *                                           write()\r
28  *    sprintf(buf,)  ->                      vsnprintf(buf,)\r
29  *    snprintf(buf,) ->                      vsnprintf(buf,)\r
30  *\r
31  * IMPLEMENTATION NOTE:\r
32  *  - If printing more than the limit, e.g. using vsnprintf() then\r
33  *    the emit function will only stop printing, but not interrupted\r
34  *    (The code will get more complicated that way)\r
35  *  - ANSI-C and POSIX, streams and POSIX filenumbers.\r
36  *    POSIX file-numbers exist in unistd.h and are only to be used by the porting\r
37  *    newlib interface i.e. newlib_port.c.\r
38  *    The filenumber is actually just a cast of the steampointer and save in the member\r
39  *    _cookie in FILE.\r
40  *\r
41  */\r
42 \r
43 #include <unistd.h>\r
44 #include <stdio.h>\r
45 #include <stdarg.h>\r
46 #include <assert.h>\r
47 #include <string.h>\r
48 \r
49 //#define HOST_TEST     1\r
50 \r
51 int arc_putchar(_PTR fd, int c);\r
52 int print(FILE *file, char **buffer, size_t n, const char *format, va_list ap);\r
53 \r
54 int printf(const char *format, ...) {\r
55         va_list ap;\r
56         int rv;\r
57 \r
58         va_start(ap, format);\r
59         rv = vfprintf(stdout, format, ap);\r
60         va_end(ap);\r
61         return rv;\r
62 }\r
63 \r
64 int fprintf(FILE *file, const char *format, ...) {\r
65         va_list ap;\r
66         int rv;\r
67 \r
68         va_start(ap, format);\r
69         rv = vfprintf(file, format, ap);\r
70         va_end(ap);\r
71         return rv;\r
72 }\r
73 \r
74 int sprintf(char *buffer, const char *format, ...) {\r
75         va_list ap;\r
76         int rv;\r
77 \r
78         va_start(ap, format);\r
79         rv = vsnprintf(buffer, ~(size_t)0, format, ap);\r
80         va_end(ap);\r
81 \r
82         return rv;\r
83 }\r
84 \r
85 int snprintf(char *buffer, size_t n, const char *format, ...) {\r
86         va_list ap;\r
87         int rv;\r
88 \r
89         va_start(ap, format);\r
90         rv = vsnprintf(buffer, n, format, ap);\r
91         va_end(ap);\r
92         return rv;\r
93 }\r
94 \r
95 int vprintf(const char *format, va_list ap) {\r
96         return vfprintf(stdout, format, ap);\r
97 }\r
98 \r
99 int vsprintf(char *buffer, const char *format, va_list ap) {\r
100         return vsnprintf(buffer, ~(size_t)0, format, ap);\r
101 }\r
102 \r
103 int vfprintf(FILE *file, const char *format, va_list ap) {\r
104         int rv;\r
105         /* Just print to stdout */\r
106         rv = print(file,NULL,~(size_t)0, format,ap);\r
107         return rv;\r
108 }\r
109 \r
110 \r
111 int vsnprintf(char *buffer, size_t n, const char *format, va_list ap) {\r
112         int rv;\r
113 \r
114         rv = print(NULL, &buffer, n, format,ap);\r
115         return rv;\r
116 }\r
117 \r
118 \r
119 /*\r
120  * The integer only counterpart\r
121  */\r
122 int iprintf(const char *format, ...) __attribute__ ((alias("printf")));\r
123 int fiprintf(FILE *file, const char *format, ...) __attribute__ ((alias("fprintf")));\r
124 int siprintf(char *buffer, const char *format, ...) __attribute__ ((alias("sprintf")));\r
125 int sniprintf(char *buffer, size_t n, const char *format, ...) __attribute__ ((alias("snprintf")));\r
126 int viprintf(const char *format, va_list ap) __attribute__ ((alias("vprintf")));\r
127 int vsiprintf(char *buffer, const char *format, va_list ap) __attribute__ ((alias("vsprintf")));\r
128 int vfiprintf(FILE *file, const char *format, va_list ap) __attribute__ ((alias("vfprintf")));\r
129 \r
130 /**\r
131  *\r
132  * @param file  The file to print to\r
133  * @param buf   The buffer to print to\r
134  * @param c             The char to print\r
135  * @return\r
136  */\r
137 static inline int emitChar( FILE *file, char **buf, char c, int *left ) {\r
138         if( (*left) == 1 ) {\r
139                 return 1;\r
140         }\r
141         --(*left);\r
142         if( buf == NULL ) {\r
143 #if HOST_TEST\r
144                 putc(c, stdout);\r
145                 fflush(stdout);\r
146 #else\r
147                 arc_putchar(file->_cookie, c);\r
148 #endif\r
149         } else {\r
150                 **buf = c;\r
151                 (*buf)++;\r
152         }\r
153         return 1;\r
154 }\r
155 \r
156 \r
157 #if defined(HOST_TEST)\r
158 /**\r
159  * Convert a number to a string\r
160  *\r
161  * @param val           The value to convert\r
162  * @param str           Pointer to a space where to put the string\r
163  * @param base          The base\r
164  * @param negative      If negative or not.\r
165  */\r
166 void xtoa( unsigned long val, char* str, int base, int negative) {\r
167         int i;\r
168         char *oStr = str;\r
169         char c;\r
170 \r
171         if (negative) {\r
172                 val = -val;\r
173         }\r
174 \r
175         if( base < 10 && base > 16 ) {\r
176                 *str = '0';\r
177                 return;\r
178         }\r
179     i = 0;\r
180 \r
181     do {\r
182       str[i++] = "0123456789abcdef"[ val % base ];\r
183         } while ((val /= base) > 0);\r
184 \r
185 \r
186     if (negative) {\r
187         str[i++] = '-';\r
188     }\r
189 \r
190     str[i] = '\0';\r
191     str = &str[i]-1;\r
192     while(str > oStr) {\r
193         c = *str;\r
194         *str-- = *oStr;\r
195         *oStr++=c;\r
196     }\r
197 }\r
198 #else\r
199 extern void xtoa( unsigned long val, char* str, int base, int negative);\r
200 #endif\r
201 \r
202 \r
203 #define FL_NONE                                 (0<<0)\r
204 #define FL_ZERO                                 (1<<1)\r
205 #define FL_HASH                                 (1<<2)\r
206 #define FL_SPACE                                (1<<3)\r
207 #define FL_ALIGN_LEFT                   (1<<4)\r
208 #define FL_TYPE_SIGNED_INT              (1<<5)\r
209 #define FL_TYPE_UNSIGNED_INT    (1<<6)\r
210 #define FL_TYPE_POINTER                 (1<<7)\r
211 \r
212 static void emitString( FILE *file, char **buffer, char *string, int width, int flags, int *left) {\r
213         char pad;\r
214         char *str = string;\r
215         int i;\r
216         int strLen;\r
217         /* padding: FL_ZERO or normal ' '\r
218          * align: FL_ALIGN_LEFT (left) or normal (right)\r
219          */\r
220         pad = (flags & FL_ZERO) ? '0' : ' ';\r
221 \r
222 \r
223         if( flags & FL_ALIGN_LEFT ) {\r
224                 for(i=0;str[i]; i++) {\r
225                         emitChar(file,buffer,str[i],left);\r
226                 }\r
227 \r
228                 /* Pad the rest */\r
229                 for(;i<width;i++) {\r
230                         emitChar(file,buffer,pad,left);\r
231                 }\r
232         } else {\r
233 \r
234                 strLen = strlen(string);\r
235 \r
236                 /* Pad first  */\r
237                 if( width > strLen ) {\r
238                         for(i=0;i<(width-strLen);i++) {\r
239                                 emitChar(file,buffer,pad,left);\r
240                         }\r
241                 }\r
242 \r
243                 for(i=0;i<strLen; i++) {\r
244                         emitChar(file,buffer,string[i],left);\r
245                 }\r
246         }\r
247 }\r
248 \r
249 void emitInt( FILE *file, char **buffer, int val, int base, int width, int flags, int *left )\r
250 {\r
251         char lBuf[12];  // longest is base 10, 2^32\r
252         char *str = lBuf;\r
253 \r
254         if( flags & FL_TYPE_SIGNED_INT ) {\r
255                 xtoa(val,str,base ,(val < 0));\r
256         } else {\r
257                 xtoa((unsigned)val,str,base ,0);\r
258         }\r
259 \r
260         emitString(file,buffer,str,width,flags,left);\r
261 }\r
262 \r
263 #define PRINT_CHAR(_c)  *buffer++= (_c);\r
264 \r
265 \r
266 /**\r
267  * Write formatted output to an array with a maximum number of characters.\r
268  *\r
269  * This is the mother of the formatted print family. The main goal here\r
270  * is to be very small and memory efficient.\r
271  *\r
272  * Support:\r
273  *   Parameter: None\r
274  *   Flags    : '-' and '0'\r
275  *   Width    : Normal padding is supported, '*' is not.\r
276  *   Precision: None\r
277  *   Length   : None\r
278  *   C99      : None\r
279  *   Type     : d,u,x,s,and c\r
280  *\r
281  * @param file    The file descriptor\r
282  * @param buffer  A pointer to the place to store the output\r
283  *                If NULL the output is instead\r
284  * @param n       The maximum number of characters to write\r
285  * @param format  The format string\r
286  * @param ap      The va list\r
287  * @return\r
288  */\r
289 int print(FILE *file, char **buffer, size_t n, const char *format, va_list ap)\r
290 {\r
291         char ch;\r
292         int flags;\r
293         char *str;\r
294         int width;\r
295         int left = n;\r
296 \r
297         while ( (ch = *format++) ) {\r
298 \r
299                 if (ch == '%') {\r
300                         ch = *format++;\r
301 \r
302                         if( ch == '%') {\r
303                                 emitChar(file,buffer,ch,&left);\r
304                                 continue;\r
305                         }\r
306 \r
307                         /* Find flags */\r
308                         if (ch == '0')\r
309                         {\r
310                                 flags = FL_ZERO;\r
311                         }\r
312                         else if (ch == ' ')\r
313                         {\r
314                                 flags = FL_SPACE;\r
315                         }\r
316                         else if (ch == '-')\r
317                         {\r
318                                 flags = FL_ALIGN_LEFT;\r
319                         }\r
320                         else\r
321                         {\r
322                                 /* Not supported or no flag */\r
323                                 flags = FL_NONE;\r
324                                 format--;\r
325                         }\r
326 \r
327                         ch = *format++;\r
328 \r
329                         /* Width */\r
330                         if( (ch >= '0')  && (ch <= '9') ) {\r
331                                 width = ch -'0';\r
332                                 ch = *format++;\r
333                         } else {\r
334                                 width = 0;\r
335                         }\r
336 \r
337                         /* Find type */\r
338                         if (ch =='c')\r
339                         {\r
340                                 emitChar(file,buffer,(char )va_arg( ap, int ),&left);\r
341                         }\r
342                         else if (ch == 'd')\r
343                         {\r
344                                 flags |= FL_TYPE_SIGNED_INT;\r
345                                 emitInt(file,buffer,va_arg( ap, int ),10,width,flags,&left);\r
346                         }\r
347                         else if (ch == 'u')\r
348                         {\r
349                                 flags |= FL_TYPE_UNSIGNED_INT;\r
350                                 emitInt(file,buffer,va_arg( ap, int ),10,width,flags,&left);\r
351                         }\r
352                         else if (ch == 'x')\r
353                         {\r
354                                 flags |= FL_TYPE_UNSIGNED_INT;\r
355                                 emitInt(file,buffer,va_arg( ap, int ),16,width,flags,&left);\r
356                         }\r
357                         else if (ch == 'p')\r
358                         {\r
359                                 flags |= FL_TYPE_POINTER;\r
360                                 emitInt(file,buffer,va_arg( ap, int ),16,width,flags,&left);\r
361                         }\r
362                         else if (ch == 's')\r
363                         {\r
364                                 str = (char *)va_arg( ap, int );\r
365 \r
366                                 if( str == NULL ) {\r
367                                         str = "(null)";\r
368                                 }\r
369 \r
370                                 emitString(file,buffer,str,width,flags,&left);\r
371                         }\r
372                         else\r
373                         {\r
374                                 assert(0); // oops\r
375                         }\r
376                 } else {\r
377                         flags = FL_NONE;\r
378                         emitChar(file,buffer,ch,&left);\r
379                 }\r
380         }\r
381 //      va_end(ap);             // Removed, TODO: Check the va_start/va_end handling (used in calling functions also).\r
382         if(buffer!=0) {\r
383                 left = 0;\r
384                 emitChar(file,buffer,'\0',&left);\r
385         }\r
386         return 0; // Wrong.. but for now.\r
387 }\r
388 \r
389 #if 0\r
390 int main(void) {\r
391         char *ptr = NULL;\r
392         char buff[30];\r
393 \r
394         printf("char: %c %c = a B\n", 'a', 66);\r
395 \r
396         printf("string: %s = (null)\n", (char *)ptr);\r
397 \r
398         printf("string: %s = foobar \n", "foobar");\r
399 \r
400         printf("string: %2s = foobar \n", "foobar");\r
401         printf("string: \"%8s\" = \"  foobar\" \n", "foobar");\r
402         /* Left justify */\r
403         printf("string: \"%-8s\" = \"foobar  \" \n", "foobar");\r
404 \r
405         printf("decimal:  23 = %d \n", 23);\r
406         printf("hex:     c23 = %x \n", 0xc23);\r
407         printf("hex:    0c23 = %04x \n", 0xc23);\r
408         printf("decimal with blanks:     23 = %6d  \n", 23);\r
409         printf("decimal with zero:   000023 = %06d \n", 23);\r
410 \r
411         /* negative and large numbers */\r
412         printf("decimal:  -23 = %d \n", -23);\r
413         printf("decimal:  4294967273 = %u \n", -23);\r
414         printf("decimal:  c0000000   = %x \n", 0xc0000000);\r
415 \r
416         printf("decimal:  00c000   = %06x \n", 0xc000);\r
417 \r
418         fprintf(stdout, "string: %s = foobar \n", "foobar");\r
419         sprintf(buff, "string: %s = foobar \n", "foobar");\r
420         printf("%s",buff);\r
421 \r
422         snprintf(buff,10, "%s\n", "12345678901234567");\r
423         printf("\"123456789\" = \"%s\"\n",buff);\r
424 \r
425         snprintf(buff,12, "%s\n", "abcdefghijklmn");\r
426         printf("\"abcdefghijkl\" = \"%s\"\n",buff);\r
427 \r
428         return 0;\r
429 }\r
430 #endif\r
431 \r
432 \r