]> rtime.felk.cvut.cz Git - arc.git/blob - common/printf.c
Converted all line endings to Windows (CR LF)
[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 int vsnprintf(char *buffer, size_t n, const char *format, va_list ap) {\r
116         int rv;\r
117 \r
118         rv = print(NULL, &buffer, n, format,ap);\r
119         return rv;\r
120 }\r
121 \r
122 \r
123 /**\r
124  *\r
125  * @param file  The file to print to\r
126  * @param buf   The buffer to print to\r
127  * @param c             The char to print\r
128  * @return\r
129  */\r
130 static inline int emitChar( FILE *file, char **buf, char c, int *left ) {\r
131         if( (*left) == 1 ) {\r
132                 return 1;\r
133         }\r
134         --(*left);\r
135         if( buf == NULL ) {\r
136 #if HOST_TEST\r
137                 putc(c, _STDOUT);\r
138                 fflush(_STDOUT);\r
139 #else\r
140                 arc_putchar((int)file, c);\r
141 #endif\r
142         } else {\r
143                 **buf = c;\r
144                 (*buf)++;\r
145         }\r
146         return 1;\r
147 }\r
148 \r
149 \r
150 #if defined(HOST_TEST)\r
151 /**\r
152  * Convert a number to a string\r
153  *\r
154  * @param val           The value to convert\r
155  * @param str           Pointer to a space where to put the string\r
156  * @param base          The base\r
157  * @param negative      If negative or not.\r
158  */\r
159 void xtoa( unsigned long val, char* str, int base, int negative) {\r
160         int i;\r
161         char *oStr = str;\r
162         char c;\r
163 \r
164         if (negative) {\r
165                 val = -val;\r
166         }\r
167 \r
168         if( base < 10 && base > 16 ) {\r
169                 *str = '0';\r
170                 return;\r
171         }\r
172     i = 0;\r
173 \r
174     do {\r
175       str[i++] = "0123456789abcdef"[ val % base ];\r
176         } while ((val /= base) > 0);\r
177 \r
178 \r
179     if (negative) {\r
180         str[i++] = '-';\r
181     }\r
182 \r
183     str[i] = '\0';\r
184     str = &str[i]-1;\r
185     while(str > oStr) {\r
186         c = *str;\r
187         *str-- = *oStr;\r
188         *oStr++=c;\r
189     }\r
190 }\r
191 #else\r
192 extern void xtoa( unsigned long val, char* str, int base, int negative);\r
193 #endif\r
194 \r
195 \r
196 #define FL_NONE                                 (0<<0)\r
197 #define FL_ZERO                                 (1<<1)\r
198 #define FL_HASH                                 (1<<2)\r
199 #define FL_SPACE                                (1<<3)\r
200 #define FL_ALIGN_LEFT                   (1<<4)\r
201 #define FL_TYPE_SIGNED_INT              (1<<5)\r
202 #define FL_TYPE_UNSIGNED_INT    (1<<6)\r
203 \r
204 \r
205 static void emitString( FILE *file, char **buffer, char *string, int width, int flags, int *left) {\r
206         char pad;\r
207         char *str = string;\r
208         int i;\r
209         int strLen;\r
210         /* padding: FL_ZERO or normal ' '\r
211          * align: FL_ALIGN_LEFT (left) or normal (right)\r
212          */\r
213         pad = (flags & FL_ZERO) ? '0' : ' ';\r
214 \r
215 \r
216         if( flags & FL_ALIGN_LEFT ) {\r
217                 for(i=0;str[i]; i++) {\r
218                         emitChar(file,buffer,str[i],left);\r
219                 }\r
220 \r
221                 /* Pad the rest */\r
222                 for(;i<width;i++) {\r
223                         emitChar(file,buffer,pad,left);\r
224                 }\r
225         } else {\r
226 \r
227                 strLen = strlen(string);\r
228 \r
229                 /* Pad first  */\r
230                 if( width > strLen ) {\r
231                         for(i=0;i<(width-strLen);i++) {\r
232                                 emitChar(file,buffer,pad,left);\r
233                         }\r
234                 }\r
235 \r
236                 for(i=0;i<strLen; i++) {\r
237                         emitChar(file,buffer,string[i],left);\r
238                 }\r
239         }\r
240 }\r
241 \r
242 void emitInt( FILE *file, char **buffer, int base, int width, int flags, va_list ap, int *left )\r
243 {\r
244         char lBuf[12];  // longest is base 10, 2^32\r
245         char *str = lBuf;\r
246         int val;\r
247 \r
248         if( flags & FL_TYPE_SIGNED_INT ) {\r
249                 val = (int )va_arg( ap, int );\r
250                 xtoa(val,str,base ,(val < 0));\r
251         } else {\r
252                 xtoa((unsigned)va_arg( ap, int ),str,base ,0);\r
253         }\r
254 \r
255         emitString(file,buffer,str,width,flags,left);\r
256 }\r
257 \r
258 \r
259 \r
260 #define PRINT_CHAR(_c)  *buffer++= (_c);\r
261 \r
262 \r
263 /**\r
264  * Write formatted output to an array with a maximum number of characters.\r
265  *\r
266  * This is the mother of the formatted print family. The main goal here\r
267  * is to be very small and memory efficient.\r
268  *\r
269  * Support:\r
270  *   Parameter: None\r
271  *   Flags    : '-' and '0'\r
272  *   Width    : Normal padding is supported, '*' is not.\r
273  *   Precision: None\r
274  *   Length   : None\r
275  *   C99      : None\r
276  *   Type     : d,u,x,s,and c\r
277  *\r
278  * @param file    The file descriptor\r
279  * @param buffer  A pointer to the place to store the output\r
280  *                If NULL the output is instead\r
281  * @param n       The maximum number of characters to write\r
282  * @param format  The format string\r
283  * @param ap      The va list\r
284  * @return\r
285  */\r
286 int print(FILE *file, char **buffer, size_t n, const char *format, va_list ap)\r
287 {\r
288         char ch;\r
289         int flags;\r
290         char *str;\r
291         int width;\r
292         int left = n;\r
293 \r
294         while ( (ch = *format++) ) {\r
295 \r
296                 if (ch == '%') {\r
297                         ch = *format++;\r
298 \r
299                         if( ch == '%') {\r
300                                 emitChar(file,buffer,ch,&left);\r
301                                 continue;\r
302                         }\r
303 \r
304                         /* Find flags */\r
305                         switch (ch) {\r
306                         case '0':\r
307                                 flags = FL_ZERO;\r
308                                 break;\r
309                         case ' ':\r
310                                 flags = FL_SPACE;\r
311                                 break;\r
312                         case '-':\r
313                                 flags = FL_ALIGN_LEFT;\r
314                                 break;\r
315                         default:\r
316                                 /* Not supported or no flag */\r
317                                 flags = FL_NONE;\r
318                                 format--;\r
319                                 break;\r
320                         }\r
321 \r
322                         ch = *format++;\r
323 \r
324                         /* Width */\r
325                         if( (ch >= '0')  && (ch <= '9') ) {\r
326                                 width = ch -'0';\r
327                                 ch = *format++;\r
328                         } else {\r
329                                 width = 0;\r
330                         }\r
331 \r
332                         /* Find type */\r
333                         switch (ch) {\r
334                         case 'c':\r
335                                 emitChar(file,buffer,(char )va_arg( ap, int ),&left);\r
336                                 break;\r
337                         case 'd':\r
338                                 flags |= FL_TYPE_SIGNED_INT;\r
339                                 emitInt(file,buffer,10,width,flags,ap,&left);\r
340                                 break;\r
341                         case 'u':\r
342                                 flags |= FL_TYPE_UNSIGNED_INT;\r
343                                 emitInt(file,buffer,10,width,flags,ap,&left);\r
344                                 break;\r
345                         case 'x':\r
346                                 flags |= FL_TYPE_UNSIGNED_INT;\r
347                                 emitInt(file,buffer,16,width,flags,ap,&left);\r
348                                 break;\r
349                         case 's':\r
350                                 str = (char *)va_arg( ap, int );\r
351 \r
352                                 if( str == NULL ) {\r
353                                         str = "(null)";\r
354                                 }\r
355 \r
356                                 emitString(file,buffer,str,width,flags,&left);\r
357                                 break;\r
358                         default:\r
359                                 assert(0); // oops\r
360                                 break;\r
361                         }\r
362 \r
363                 } else {\r
364                         flags = FL_NONE;\r
365                         emitChar(file,buffer,ch,&left);\r
366                 }\r
367         }\r
368         va_end(ap);\r
369         if(buffer!=0) {\r
370                 left = 0;\r
371                 emitChar(file,buffer,'\0',&left);\r
372         }\r
373         return 0; // Wrong.. but for now.\r
374 }\r
375 \r
376 #if defined(HOST_TEST)\r
377 int main(void) {\r
378         char *ptr = NULL;\r
379         char buff[30];\r
380 \r
381         printf("char: %c %c = a B\n", 'a', 66);\r
382 \r
383         printf("string: %s = (null)\n", (char *)ptr);\r
384 \r
385         printf("string: %s = foobar \n", "foobar");\r
386 \r
387         printf("string: %2s = foobar \n", "foobar");\r
388         printf("string: \"%8s\" = \"  foobar\" \n", "foobar");\r
389         /* Left justify */\r
390         printf("string: \"%-8s\" = \"foobar  \" \n", "foobar");\r
391 \r
392         printf("decimal:  23 = %d \n", 23);\r
393         printf("hex:     c23 = %x \n", 0xc23);\r
394         printf("hex:    0c23 = %04x \n", 0xc23);\r
395         printf("decimal with blanks:     23 = %6d  \n", 23);\r
396         printf("decimal with zero:   000023 = %06d \n", 23);\r
397 \r
398         /* negative and large numbers */\r
399         printf("decimal:  -23 = %d \n", -23);\r
400         printf("decimal:  4294967273 = %u \n", -23);\r
401         printf("decimal:  c0000000   = %x \n", 0xc0000000);\r
402 \r
403         printf("decimal:  00c000   = %06x \n", 0xc000);\r
404 \r
405         fprintf(_STDOUT, "string: %s = foobar \n", "foobar");\r
406         sprintf(buff, "string: %s = foobar \n", "foobar");\r
407         printf("%s",buff);\r
408 \r
409         snprintf(buff,10, "%s\n", "12345678901234567");\r
410         printf("\"123456789\" = \"%s\"\n",buff);\r
411 \r
412         snprintf(buff,12, "%s\n", "abcdefghijklmn");\r
413         printf("\"abcdefghijkl\" = \"%s\"\n",buff);\r
414 \r
415         return 0;\r
416 }\r
417 #endif\r
418 \r
419 \r