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