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