]> rtime.felk.cvut.cz Git - arc.git/blob - common/printf.c
Changed the print() and emitInt() function to work with hcs12.
[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 vfiprintf(FILE *file, const char *format, va_list ap) {\r
116         return vfprintf(file,format,ap);\r
117 }\r
118 \r
119 int vsnprintf(char *buffer, size_t n, const char *format, va_list ap) {\r
120         int rv;\r
121 \r
122         rv = print(NULL, &buffer, n, format,ap);\r
123         return rv;\r
124 }\r
125 \r
126 \r
127 /**\r
128  *\r
129  * @param file  The file to print to\r
130  * @param buf   The buffer to print to\r
131  * @param c             The char to print\r
132  * @return\r
133  */\r
134 static inline int emitChar( FILE *file, char **buf, char c, int *left ) {\r
135         if( (*left) == 1 ) {\r
136                 return 1;\r
137         }\r
138         --(*left);\r
139         if( buf == NULL ) {\r
140 #if HOST_TEST\r
141                 putc(c, _STDOUT);\r
142                 fflush(_STDOUT);\r
143 #else\r
144                 arc_putchar((int)file, c);\r
145 #endif\r
146         } else {\r
147                 **buf = c;\r
148                 (*buf)++;\r
149         }\r
150         return 1;\r
151 }\r
152 \r
153 \r
154 #if defined(HOST_TEST)\r
155 /**\r
156  * Convert a number to a string\r
157  *\r
158  * @param val           The value to convert\r
159  * @param str           Pointer to a space where to put the string\r
160  * @param base          The base\r
161  * @param negative      If negative or not.\r
162  */\r
163 void xtoa( unsigned long val, char* str, int base, int negative) {\r
164         int i;\r
165         char *oStr = str;\r
166         char c;\r
167 \r
168         if (negative) {\r
169                 val = -val;\r
170         }\r
171 \r
172         if( base < 10 && base > 16 ) {\r
173                 *str = '0';\r
174                 return;\r
175         }\r
176     i = 0;\r
177 \r
178     do {\r
179       str[i++] = "0123456789abcdef"[ val % base ];\r
180         } while ((val /= base) > 0);\r
181 \r
182 \r
183     if (negative) {\r
184         str[i++] = '-';\r
185     }\r
186 \r
187     str[i] = '\0';\r
188     str = &str[i]-1;\r
189     while(str > oStr) {\r
190         c = *str;\r
191         *str-- = *oStr;\r
192         *oStr++=c;\r
193     }\r
194 }\r
195 #else\r
196 extern void xtoa( unsigned long val, char* str, int base, int negative);\r
197 #endif\r
198 \r
199 \r
200 #define FL_NONE                                 (0<<0)\r
201 #define FL_ZERO                                 (1<<1)\r
202 #define FL_HASH                                 (1<<2)\r
203 #define FL_SPACE                                (1<<3)\r
204 #define FL_ALIGN_LEFT                   (1<<4)\r
205 #define FL_TYPE_SIGNED_INT              (1<<5)\r
206 #define FL_TYPE_UNSIGNED_INT    (1<<6)\r
207 \r
208 \r
209 static void emitString( FILE *file, char **buffer, char *string, int width, int flags, int *left) {\r
210         char pad;\r
211         char *str = string;\r
212         int i;\r
213         int strLen;\r
214         /* padding: FL_ZERO or normal ' '\r
215          * align: FL_ALIGN_LEFT (left) or normal (right)\r
216          */\r
217         pad = (flags & FL_ZERO) ? '0' : ' ';\r
218 \r
219 \r
220         if( flags & FL_ALIGN_LEFT ) {\r
221                 for(i=0;str[i]; i++) {\r
222                         emitChar(file,buffer,str[i],left);\r
223                 }\r
224 \r
225                 /* Pad the rest */\r
226                 for(;i<width;i++) {\r
227                         emitChar(file,buffer,pad,left);\r
228                 }\r
229         } else {\r
230 \r
231                 strLen = strlen(string);\r
232 \r
233                 /* Pad first  */\r
234                 if( width > strLen ) {\r
235                         for(i=0;i<(width-strLen);i++) {\r
236                                 emitChar(file,buffer,pad,left);\r
237                         }\r
238                 }\r
239 \r
240                 for(i=0;i<strLen; i++) {\r
241                         emitChar(file,buffer,string[i],left);\r
242                 }\r
243         }\r
244 }\r
245 \r
246 void emitInt( FILE *file, char **buffer, int val, int base, int width, int flags, int *left )\r
247 {\r
248         char lBuf[12];  // longest is base 10, 2^32\r
249         char *str = lBuf;\r
250 \r
251         if( flags & FL_TYPE_SIGNED_INT ) {\r
252                 xtoa(val,str,base ,(val < 0));\r
253         } else {\r
254                 xtoa((unsigned)val,str,base ,0);\r
255         }\r
256 \r
257         emitString(file,buffer,str,width,flags,left);\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,va_arg( ap, int ),10,width,flags,&left);\r
340                                 break;\r
341                         case 'u':\r
342                                 flags |= FL_TYPE_UNSIGNED_INT;\r
343                                 emitInt(file,buffer,va_arg( ap, int ),10,width,flags,&left);\r
344                                 break;\r
345                         case 'x':\r
346                                 flags |= FL_TYPE_UNSIGNED_INT;\r
347                                 emitInt(file,buffer,va_arg( ap, int ),16,width,flags,&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);             // Removed, TODO: Check the va_start/va_end handling (used in calling functions also).\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