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