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