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