]> rtime.felk.cvut.cz Git - arc.git/blob - common/printf.c
Merge branch 'mikulka' of git@rtime.felk.cvut.cz:arc into mikulka
[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) && defined(__GNUC__)\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 0\r
175 #if defined(USE_NEWLIB) && defined(__GNUC__)\r
176                 /* We are trying to print with newlib file descriptor.\r
177                  * That's wrong since we are using the POSIX file numbers here instead\r
178                  * Check stdout */\r
179                 assert( file != _impure_ptr->_stdout );\r
180 #endif\r
181 #endif\r
182                 if( (unsigned )file > 10UL ) {\r
183                         arc_putchar((int)(file->_file), c);\r
184                 } else {\r
185                         arc_putchar((int)(file), c);\r
186                 }\r
187 \r
188 #endif /* HOST_TEST */\r
189         } else {\r
190                 **buf = c;\r
191                 (*buf)++;\r
192         }\r
193         return 1;\r
194 }\r
195 \r
196 \r
197 #if defined(HOST_TEST)\r
198 /**\r
199  * Convert a number to a string\r
200  *\r
201  * @param val           The value to convert\r
202  * @param str           Pointer to a space where to put the string\r
203  * @param base          The base\r
204  * @param negative      If negative or not.\r
205  */\r
206 void xtoa( unsigned long val, char* str, int base, int negative) {\r
207         int i;\r
208         char *oStr = str;\r
209         char c;\r
210 \r
211         if (negative) {\r
212                 val = -val;\r
213         }\r
214 \r
215         if( base < 10 && base > 16 ) {\r
216                 *str = '0';\r
217                 return;\r
218         }\r
219     i = 0;\r
220 \r
221     do {\r
222       str[i++] = "0123456789abcdef"[ val % base ];\r
223         } while ((val /= base) > 0);\r
224 \r
225 \r
226     if (negative) {\r
227         str[i++] = '-';\r
228     }\r
229 \r
230     str[i] = '\0';\r
231     str = &str[i]-1;\r
232     while(str > oStr) {\r
233         c = *str;\r
234         *str-- = *oStr;\r
235         *oStr++=c;\r
236     }\r
237 }\r
238 #else\r
239 extern void xtoa( unsigned long val, char* str, int base, int negative);\r
240 #endif\r
241 \r
242 \r
243 #define FL_NONE                                 (0<<0)\r
244 #define FL_ZERO                                 (1<<1)\r
245 #define FL_HASH                                 (1<<2)\r
246 #define FL_SPACE                                (1<<3)\r
247 #define FL_ALIGN_LEFT                   (1<<4)\r
248 #define FL_TYPE_SIGNED_INT              (1<<5)\r
249 #define FL_TYPE_UNSIGNED_INT    (1<<6)\r
250 #define FL_TYPE_POINTER                 (1<<7)\r
251 \r
252 static void emitString( FILE *file, char **buffer, char *string, int width, int flags, int *left) {\r
253         char pad;\r
254         char *str = string;\r
255         int i;\r
256         int strLen;\r
257         /* padding: FL_ZERO or normal ' '\r
258          * align: FL_ALIGN_LEFT (left) or normal (right)\r
259          */\r
260         pad = (flags & FL_ZERO) ? '0' : ' ';\r
261 \r
262 \r
263         if( flags & FL_ALIGN_LEFT ) {\r
264                 for(i=0;str[i]; i++) {\r
265                         emitChar(file,buffer,str[i],left);\r
266                 }\r
267 \r
268                 /* Pad the rest */\r
269                 for(;i<width;i++) {\r
270                         emitChar(file,buffer,pad,left);\r
271                 }\r
272         } else {\r
273 \r
274                 strLen = strlen(string);\r
275 \r
276                 /* Pad first  */\r
277                 if( width > strLen ) {\r
278                         for(i=0;i<(width-strLen);i++) {\r
279                                 emitChar(file,buffer,pad,left);\r
280                         }\r
281                 }\r
282 \r
283                 for(i=0;i<strLen; i++) {\r
284                         emitChar(file,buffer,string[i],left);\r
285                 }\r
286         }\r
287 }\r
288 \r
289 void emitInt( FILE *file, char **buffer, int val, int base, int width, int flags, int *left )\r
290 {\r
291         char lBuf[12];  // longest is base 10, 2^32\r
292         char *str = lBuf;\r
293 \r
294         if( flags & FL_TYPE_SIGNED_INT ) {\r
295                 xtoa(val,str,base ,(val < 0));\r
296         } else {\r
297                 xtoa((unsigned)val,str,base ,0);\r
298         }\r
299 \r
300         emitString(file,buffer,str,width,flags,left);\r
301 }\r
302 \r
303 #define PRINT_CHAR(_c)  *buffer++= (_c);\r
304 \r
305 \r
306 /**\r
307  * Write formatted output to an array with a maximum number of characters.\r
308  *\r
309  * This is the mother of the formatted print family. The main goal here\r
310  * is to be very small and memory efficient.\r
311  *\r
312  * Support:\r
313  *   Parameter: None\r
314  *   Flags    : '-' and '0'\r
315  *   Width    : Normal padding is supported, '*' is not.\r
316  *   Precision: None\r
317  *   Length   : None\r
318  *   C99      : None\r
319  *   Type     : d,u,x,s,and c\r
320  *\r
321  * @param file    The file descriptor\r
322  * @param buffer  A pointer to the place to store the output\r
323  *                If NULL the output is instead\r
324  * @param n       The maximum number of characters to write\r
325  * @param format  The format string\r
326  * @param ap      The va list\r
327  * @return\r
328  */\r
329 int print(FILE *file, char **buffer, size_t n, const char *format, va_list ap)\r
330 {\r
331         char ch;\r
332         int flags;\r
333         char *str;\r
334         int width;\r
335         int left = n;\r
336 \r
337         while ( (ch = *format++) ) {\r
338 \r
339                 if (ch == '%') {\r
340                         ch = *format++;\r
341 \r
342                         if( ch == '%') {\r
343                                 emitChar(file,buffer,ch,&left);\r
344                                 continue;\r
345                         }\r
346 \r
347                         /* Find flags */\r
348                         if (ch == '0')\r
349                         {\r
350                                 flags = FL_ZERO;\r
351                         }\r
352                         else if (ch == ' ')\r
353                         {\r
354                                 flags = FL_SPACE;\r
355                         }\r
356                         else if (ch == '-')\r
357                         {\r
358                                 flags = FL_ALIGN_LEFT;\r
359                         }\r
360                         else\r
361                         {\r
362                                 /* Not supported or no flag */\r
363                                 flags = FL_NONE;\r
364                                 format--;\r
365                         }\r
366 \r
367                         ch = *format++;\r
368 \r
369                         /* Width */\r
370                         if( (ch >= '0')  && (ch <= '9') ) {\r
371                                 width = ch -'0';\r
372                                 ch = *format++;\r
373                         } else {\r
374                                 width = 0;\r
375                         }\r
376 \r
377                         /* Find type */\r
378                         if (ch =='c')\r
379                         {\r
380                                 emitChar(file,buffer,(char )va_arg( ap, int ),&left);\r
381                         }\r
382                         else if (ch == 'd')\r
383                         {\r
384                                 flags |= FL_TYPE_SIGNED_INT;\r
385                                 emitInt(file,buffer,va_arg( ap, int ),10,width,flags,&left);\r
386                         }\r
387                         else if (ch == 'u')\r
388                         {\r
389                                 flags |= FL_TYPE_UNSIGNED_INT;\r
390                                 emitInt(file,buffer,va_arg( ap, int ),10,width,flags,&left);\r
391                         }\r
392                         else if (ch == 'x')\r
393                         {\r
394                                 flags |= FL_TYPE_UNSIGNED_INT;\r
395                                 emitInt(file,buffer,va_arg( ap, int ),16,width,flags,&left);\r
396                         }\r
397                         else if (ch == 'p')\r
398                         {\r
399                                 flags |= FL_TYPE_POINTER;\r
400                                 emitInt(file,buffer,va_arg( ap, int ),16,width,flags,&left);\r
401                         }\r
402                         else if (ch == 's')\r
403                         {\r
404                                 str = (char *)va_arg( ap, int );\r
405 \r
406                                 if( str == NULL ) {\r
407                                         str = "(null)";\r
408                                 }\r
409 \r
410                                 emitString(file,buffer,str,width,flags,&left);\r
411                         }\r
412                         else\r
413                         {\r
414                                 assert(0); // oops\r
415                         }\r
416                 } else {\r
417                         flags = FL_NONE;\r
418                         emitChar(file,buffer,ch,&left);\r
419                 }\r
420         }\r
421 //      va_end(ap);             // Removed, TODO: Check the va_start/va_end handling (used in calling functions also).\r
422         if(buffer!=0) {\r
423                 left = 0;\r
424                 emitChar(file,buffer,'\0',&left);\r
425         }\r
426         return 0; // Wrong.. but for now.\r
427 }\r
428 \r
429 #if 0\r
430 int main(void) {\r
431         char *ptr = NULL;\r
432         char buff[30];\r
433 \r
434         printf("char: %c %c = a B\n", 'a', 66);\r
435 \r
436         printf("string: %s = (null)\n", (char *)ptr);\r
437 \r
438         printf("string: %s = foobar \n", "foobar");\r
439 \r
440         printf("string: %2s = foobar \n", "foobar");\r
441         printf("string: \"%8s\" = \"  foobar\" \n", "foobar");\r
442         /* Left justify */\r
443         printf("string: \"%-8s\" = \"foobar  \" \n", "foobar");\r
444 \r
445         printf("decimal:  23 = %d \n", 23);\r
446         printf("hex:     c23 = %x \n", 0xc23);\r
447         printf("hex:    0c23 = %04x \n", 0xc23);\r
448         printf("decimal with blanks:     23 = %6d  \n", 23);\r
449         printf("decimal with zero:   000023 = %06d \n", 23);\r
450 \r
451         /* negative and large numbers */\r
452         printf("decimal:  -23 = %d \n", -23);\r
453         printf("decimal:  4294967273 = %u \n", -23);\r
454         printf("decimal:  c0000000   = %x \n", 0xc0000000);\r
455 \r
456         printf("decimal:  00c000   = %06x \n", 0xc000);\r
457 \r
458         fprintf(stdout, "string: %s = foobar \n", "foobar");\r
459         sprintf(buff, "string: %s = foobar \n", "foobar");\r
460         printf("%s",buff);\r
461 \r
462         snprintf(buff,10, "%s\n", "12345678901234567");\r
463         printf("\"123456789\" = \"%s\"\n",buff);\r
464 \r
465         snprintf(buff,12, "%s\n", "abcdefghijklmn");\r
466         printf("\"abcdefghijkl\" = \"%s\"\n",buff);\r
467 \r
468         return 0;\r
469 }\r
470 #endif\r
471 \r
472 \r