]> rtime.felk.cvut.cz Git - arc.git/blob - common/printf.c
Merge with mahi-1
[arc.git] / common / printf.c
1 /*
2  * Copyright ArcCore AB
3  *
4  * A simple implementation of all formatted xxprintf functionallity.
5  *
6  * DESIGN CRITERIA:
7  *  - Reentrant
8  *  - Use little stack
9  *
10  *  What you normally would do is to print to a buffer of some kind and then
11  *  call write(). However little stack indicates that we can't place buffers
12  *  on the stack and reentrant tells us that we can't have a static buffer.
13  *  That leaves us with printing characters as it is ready. From a speed
14  *  point of view that is less than optimal, but that the way it's got to be.
15  *  (Could make a 16 long char buffer??)
16  *
17  *  This is just intended to be used as a replacement for newlibs implementation.
18  *  Newlib porting interface have the following API:
19  *    int write(  int fd, char *buf, int nbytes)
20  *
21  *
22  *  Note that puts(), putc() are still the newlib variants....
23  *
24  *    printf()       -> vfprintf(stdout,) -> vsnprintf(buf,)
25  *                                           write()
26  *    vprintf()      -> vfprintf(stdout,) -> vsnprintf(buf,)
27  *                                           write()
28  *    sprintf(buf,)  ->                      vsnprintf(buf,)
29  *    snprintf(buf,) ->                      vsnprintf(buf,)
30  *
31  * IMPLEMENTATION NOTE:
32  *  If printing more than the limit, e.g. using vsnprintf() then
33  *  the emit function will only stop printing, but not interrupted
34  *  (The code will get more complicated that way)
35  */
36
37 #include <unistd.h>
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <assert.h>
41
42 //#define HOST_TEST     1
43
44 #ifdef HOST_TEST
45 #define _STDOUT         stdout
46 #define _STDIN  stdin
47 #define _STDERR stderr
48 #else
49 #define _STDOUT         (FILE *)STDOUT_FILENO
50 #define _STDINT         STDIN_FILENO
51 #define _STDERR (FILE *)STDERR_FILENO
52 #endif
53
54
55
56 int print(FILE *file, char **buffer, size_t n, const char *format, va_list ap);
57
58 int printf(const char *format, ...) {
59         va_list ap;
60         int rv;
61
62         va_start(ap, format);
63         rv = vfprintf(_STDOUT, format, ap);
64         va_end(ap);
65         return rv;
66 }
67
68 int fprintf(FILE *file, const char *format, ...) {
69         va_list ap;
70         int rv;
71
72         va_start(ap, format);
73         rv = vfprintf(file, format, ap);
74         va_end(ap);
75         return rv;
76 }
77
78 int sprintf(char *buffer, const char *format, ...) {
79         va_list ap;
80         int rv;
81
82         va_start(ap, format);
83         rv = vsnprintf(buffer, ~(size_t)0, format, ap);
84         va_end(ap);
85
86         return rv;
87 }
88
89 int snprintf(char *buffer, size_t n, const char *format, ...) {
90         va_list ap;
91         int rv;
92
93         va_start(ap, format);
94         rv = vsnprintf(buffer, n, format, ap);
95         va_end(ap);
96         return rv;
97 }
98
99 int vprintf(const char *format, va_list ap) {
100         return vfprintf(_STDOUT, format, ap);
101 }
102
103 int vsprintf(char *buffer, const char *format, va_list ap) {
104         return vsnprintf(buffer, ~(size_t)0, format, ap);
105 }
106
107 int vfprintf(FILE *file, const char *format, va_list ap) {
108         int rv;
109         /* Just print to _STDOUT */
110         rv = print(file,NULL,~(size_t)0, format,ap);
111         return rv;
112 }
113
114 int vsnprintf(char *buffer, size_t n, const char *format, va_list ap) {
115         int rv;
116
117         rv = print(NULL, &buffer, n, format,ap);
118         return rv;
119 }
120
121
122 /**
123  *
124  * @param file  The file to print to
125  * @param buf   The buffer to print to
126  * @param c             The char to print
127  * @return
128  */
129 static inline int emitChar( FILE *file, char **buf, char c, int *left ) {
130         if( (*left) == 1 ) {
131                 return 1;
132         }
133         --(*left);
134         if( buf == NULL ) {
135 #if HOST_TEST
136                 putc(c, _STDOUT);
137                 fflush(_STDOUT);
138 #else
139                 arc_putchar(file,c);
140 #endif
141         } else {
142                 **buf = c;
143                 (*buf)++;
144         }
145         return 1;
146 }
147
148
149 #if defined(HOST_TEST)
150 /**
151  * Convert a number to a string
152  *
153  * @param val           The value to convert
154  * @param str           Pointer to a space where to put the string
155  * @param base          The base
156  * @param negative      If negative or not.
157  */
158 void xtoa( unsigned long val, char* str, int base, int negative) {
159         int i;
160         char *oStr = str;
161         char c;
162
163         if (negative) {
164                 val = -val;
165         }
166
167         if( base < 10 && base > 16 ) {
168                 *str = '0';
169                 return;
170         }
171     i = 0;
172
173     do {
174       str[i++] = "0123456789abcdef"[ val % base ];
175         } while ((val /= base) > 0);
176
177
178     if (negative) {
179         str[i++] = '-';
180     }
181
182     str[i] = '\0';
183     str = &str[i]-1;
184     while(str > oStr) {
185         c = *str;
186         *str-- = *oStr;
187         *oStr++=c;
188     }
189 }
190 #else
191 extern void xtoa( unsigned long val, char* str, int base, int negative);
192 #endif
193
194
195 #define FL_NONE                                 (0<<0)
196 #define FL_ZERO                                 (1<<1)
197 #define FL_HASH                                 (1<<2)
198 #define FL_SPACE                                (1<<3)
199 #define FL_ALIGN_LEFT                   (1<<4)
200 #define FL_TYPE_SIGNED_INT              (1<<5)
201 #define FL_TYPE_UNSIGNED_INT    (1<<6)
202
203
204 static void emitString( FILE *file, char **buffer, char *string, int width, int flags, int *left) {
205         char pad;
206         char *str = string;
207         int i;
208         int strLen;
209         /* padding: FL_ZERO or normal ' '
210          * align: FL_ALIGN_LEFT (left) or normal (right)
211          */
212         pad = (flags & FL_ZERO) ? '0' : ' ';
213
214
215         if( flags & FL_ALIGN_LEFT ) {
216                 for(i=0;str[i]; i++) {
217                         emitChar(file,buffer,str[i],left);
218                 }
219
220                 /* Pad the rest */
221                 for(;i<width;i++) {
222                         emitChar(file,buffer,pad,left);
223                 }
224         } else {
225
226                 strLen = strlen(string);
227
228                 /* Pad first  */
229                 if( width > strLen ) {
230                         for(i=0;i<(width-strLen);i++) {
231                                 emitChar(file,buffer,pad,left);
232                         }
233                 }
234
235                 for(i=0;i<strLen; i++) {
236                         emitChar(file,buffer,string[i],left);
237                 }
238         }
239 }
240
241 void emitInt( FILE *file, char **buffer, int base, int width, int flags, va_list ap, int *left )
242 {
243         char lBuf[12];  // longest is base 10, 2^32
244         char *str = lBuf;
245         int val;
246
247         if( flags & FL_TYPE_SIGNED_INT ) {
248                 val = (int )va_arg( ap, int );
249                 xtoa(val,str,base ,(val < 0));
250         } else {
251                 xtoa((unsigned)va_arg( ap, int ),str,base ,0);
252         }
253
254         emitString(file,buffer,str,width,flags,left);
255 }
256
257
258
259 #define PRINT_CHAR(_c)  *buffer++= (_c);
260
261
262 /**
263  * Write formatted output to an array with a maximum number of characters.
264  *
265  * This is the mother of the formatted print family. The main goal here
266  * is to be very small and memory efficient.
267  *
268  * Support:
269  *   Parameter: None
270  *   Flags    : '-' and '0'
271  *   Width    : Normal padding is supported, '*' is not.
272  *   Precision: None
273  *   Length   : None
274  *   C99      : None
275  *   Type     : d,u,x,s,and c
276  *
277  * @param file    The file descriptor
278  * @param buffer  A pointer to the place to store the output
279  *                If NULL the output is instead
280  * @param n       The maximum number of characters to write
281  * @param format  The format string
282  * @param ap      The va list
283  * @return
284  */
285 int print(FILE *file, char **buffer, size_t n, const char *format, va_list ap)
286 {
287         char ch;
288         int flags;
289         char *str;
290         int width;
291         int left = n;
292
293         while ( (ch = *format++) ) {
294
295                 if (ch == '%') {
296                         ch = *format++;
297
298                         if( ch == '%') {
299                                 emitChar(file,buffer,ch,&left);
300                                 continue;
301                         }
302
303                         /* Find flags */
304                         switch (ch) {
305                         case '0':
306                                 flags = FL_ZERO;
307                                 break;
308                         case ' ':
309                                 flags = FL_SPACE;
310                                 break;
311                         case '-':
312                                 flags = FL_ALIGN_LEFT;
313                                 break;
314                         default:
315                                 /* Not supported or no flag */
316                                 flags = FL_NONE;
317                                 format--;
318                                 break;
319                         }
320
321                         ch = *format++;
322
323                         /* Width */
324                         if( (ch >= '0')  && (ch <= '9') ) {
325                                 width = ch -'0';
326                                 ch = *format++;
327                         } else {
328                                 width = 0;
329                         }
330
331                         /* Find type */
332                         switch (ch) {
333                         case 'c':
334                                 emitChar(file,buffer,(char )va_arg( ap, int ),&left);
335                                 break;
336                         case 'd':
337                                 flags |= FL_TYPE_SIGNED_INT;
338                                 emitInt(file,buffer,10,width,flags,ap,&left);
339                                 break;
340                         case 'u':
341                                 flags |= FL_TYPE_UNSIGNED_INT;
342                                 emitInt(file,buffer,10,width,flags,ap,&left);
343                                 break;
344                         case 'x':
345                                 flags |= FL_TYPE_UNSIGNED_INT;
346                                 emitInt(file,buffer,16,width,flags,ap,&left);
347                                 break;
348                         case 's':
349                                 str = (char *)va_arg( ap, int );
350
351                                 if( str == NULL ) {
352                                         str = "(null)";
353                                 }
354
355                                 emitString(file,buffer,str,width,flags,&left);
356                                 break;
357                         default:
358                                 assert(0); // oops
359                                 break;
360                         }
361
362                 } else {
363                         flags = FL_NONE;
364                         emitChar(file,buffer,ch,&left);
365                 }
366         }
367         va_end(ap);
368         if(buffer!=0) {
369                 left = 0;
370                 emitChar(file,buffer,'\0',&left);
371         }
372         return 0; // Wrong.. but for now.
373 }
374
375 #if defined(HOST_TEST)
376 int main(void) {
377         char *ptr = NULL;
378         char buff[30];
379
380         printf("char: %c %c = a B\n", 'a', 66);
381
382         printf("string: %s = (null)\n", (char *)ptr);
383
384         printf("string: %s = foobar \n", "foobar");
385
386         printf("string: %2s = foobar \n", "foobar");
387         printf("string: \"%8s\" = \"  foobar\" \n", "foobar");
388         /* Left justify */
389         printf("string: \"%-8s\" = \"foobar  \" \n", "foobar");
390
391         printf("decimal:  23 = %d \n", 23);
392         printf("hex:     c23 = %x \n", 0xc23);
393         printf("hex:    0c23 = %04x \n", 0xc23);
394         printf("decimal with blanks:     23 = %6d  \n", 23);
395         printf("decimal with zero:   000023 = %06d \n", 23);
396
397         /* negative and large numbers */
398         printf("decimal:  -23 = %d \n", -23);
399         printf("decimal:  4294967273 = %u \n", -23);
400         printf("decimal:  c0000000   = %x \n", 0xc0000000);
401
402         printf("decimal:  00c000   = %06x \n", 0xc000);
403
404         fprintf(_STDOUT, "string: %s = foobar \n", "foobar");
405         sprintf(buff, "string: %s = foobar \n", "foobar");
406         printf("%s",buff);
407
408         snprintf(buff,10, "%s\n", "12345678901234567");
409         printf("\"123456789\" = \"%s\"\n",buff);
410
411         snprintf(buff,12, "%s\n", "abcdefghijklmn");
412         printf("\"abcdefghijkl\" = \"%s\"\n",buff);
413
414         return 0;
415 }
416 #endif
417
418