basic implemtation general purpose clock and tinyprintf
[cortex-from-scratch] / lib / tinyprintf.c
1 /*
2 File: tinyprintf.c
3
4 Copyright (C) 2004  Kustaa Nyholm
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
20 */
21
22 #include <lib/tinyprintf.h>
23
24
25 /*
26  * Configuration
27  */
28
29 /* Enable long int support */
30 #define PRINTF_LONG_SUPPORT
31
32 /* Enable long long int support (implies long int support) */
33 //#define PRINTF_LONG_LONG_SUPPORT
34
35 /* Enable %z (size_t) support */
36 #define PRINTF_SIZE_T_SUPPORT
37
38 /*
39  * Configuration adjustments
40  */
41 #ifdef PRINTF_SIZE_T_SUPPORT
42 #include <sys/types.h>
43 #endif
44
45 #ifdef PRINTF_LONG_LONG_SUPPORT
46 # define PRINTF_LONG_SUPPORT
47 #endif
48
49 /* __SIZEOF_<type>__ defined at least by gcc */
50 #ifdef __SIZEOF_POINTER__
51 # define SIZEOF_POINTER __SIZEOF_POINTER__
52 #endif
53 #ifdef __SIZEOF_LONG_LONG__
54 # define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__
55 #endif
56 #ifdef __SIZEOF_LONG__
57 # define SIZEOF_LONG __SIZEOF_LONG__
58 #endif
59 #ifdef __SIZEOF_INT__
60 # define SIZEOF_INT __SIZEOF_INT__
61 #endif
62
63 #ifdef __GNUC__
64 # define _TFP_GCC_NO_INLINE_  __attribute__ ((noinline))
65 #else
66 # define _TFP_GCC_NO_INLINE_
67 #endif
68
69 /*
70  * Implementation
71  */
72 struct param {
73     char lz:1;          /**<  Leading zeros */
74     char alt:1;         /**<  alternate form */
75     char uc:1;          /**<  Upper case (for base16 only) */
76     char align_left:1;  /**<  0 == align right (default), 1 == align left */
77     unsigned int width; /**<  field width */
78     char sign;          /**<  The sign to display (if any) */
79     unsigned int base;  /**<  number base (e.g.: 8, 10, 16) */
80     char *bf;           /**<  Buffer to output */
81 };
82
83
84 #ifdef PRINTF_LONG_LONG_SUPPORT
85 static void _TFP_GCC_NO_INLINE_ ulli2a(
86     unsigned long long int num, struct param *p)
87 {
88     int n = 0;
89     unsigned long long int d = 1;
90     char *bf = p->bf;
91     while (num / d >= p->base)
92         d *= p->base;
93     while (d != 0) {
94         int dgt = num / d;
95         num %= d;
96         d /= p->base;
97         if (n || dgt > 0 || d == 0) {
98             *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
99             ++n;
100         }
101     }
102     *bf = 0;
103 }
104
105 static void lli2a(long long int num, struct param *p)
106 {
107     if (num < 0) {
108         num = -num;
109         p->sign = '-';
110     }
111     ulli2a(num, p);
112 }
113 #endif
114
115 #ifdef PRINTF_LONG_SUPPORT
116 static void uli2a(unsigned long int num, struct param *p)
117 {
118     int n = 0;
119     unsigned long int d = 1;
120     char *bf = p->bf;
121     while (num / d >= p->base)
122         d *= p->base;
123     while (d != 0) {
124         int dgt = num / d;
125         num %= d;
126         d /= p->base;
127         if (n || dgt > 0 || d == 0) {
128             *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
129             ++n;
130         }
131     }
132     *bf = 0;
133 }
134
135 static void li2a(long num, struct param *p)
136 {
137     if (num < 0) {
138         num = -num;
139         p->sign = '-';
140     }
141     uli2a(num, p);
142 }
143 #endif
144
145 static void ui2a(unsigned int num, struct param *p)
146 {
147     int n = 0;
148     unsigned int d = 1;
149     char *bf = p->bf;
150     while (num / d >= p->base)
151         d *= p->base;
152     while (d != 0) {
153         int dgt = num / d;
154         num %= d;
155         d /= p->base;
156         if (n || dgt > 0 || d == 0) {
157             *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
158             ++n;
159         }
160     }
161     *bf = 0;
162 }
163
164 static void i2a(int num, struct param *p)
165 {
166     if (num < 0) {
167         num = -num;
168         p->sign = '-';
169     }
170     ui2a(num, p);
171 }
172
173 static int a2d(char ch)
174 {
175     if (ch >= '0' && ch <= '9')
176         return ch - '0';
177     else if (ch >= 'a' && ch <= 'f')
178         return ch - 'a' + 10;
179     else if (ch >= 'A' && ch <= 'F')
180         return ch - 'A' + 10;
181     else
182         return -1;
183 }
184
185 static char a2u(char ch, const char **src, int base, unsigned int *nump)
186 {
187     const char *p = *src;
188     unsigned int num = 0;
189     int digit;
190     while ((digit = a2d(ch)) >= 0) {
191         if (digit > base)
192             break;
193         num = num * base + digit;
194         ch = *p++;
195     }
196     *src = p;
197     *nump = num;
198     return ch;
199 }
200
201 static void putchw(void *putp, putcf putf, struct param *p)
202 {
203     char ch;
204     int n = p->width;
205     char *bf = p->bf;
206
207     /* Number of filling characters */
208     while (*bf++ && n > 0)
209         n--;
210     if (p->sign)
211         n--;
212     if (p->alt && p->base == 16)
213         n -= 2;
214     else if (p->alt && p->base == 8)
215         n--;
216
217     /* Fill with space to align to the right, before alternate or sign */
218     if (!p->lz && !p->align_left) {
219         while (n-- > 0)
220             putf(putp, ' ');
221     }
222
223     /* print sign */
224     if (p->sign)
225         putf(putp, p->sign);
226
227     /* Alternate */
228     if (p->alt && p->base == 16) {
229         putf(putp, '0');
230         putf(putp, (p->uc ? 'X' : 'x'));
231     } else if (p->alt && p->base == 8) {
232         putf(putp, '0');
233     }
234
235     /* Fill with zeros, after alternate or sign */
236     if (p->lz) {
237         while (n-- > 0)
238             putf(putp, '0');
239     }
240
241     /* Put actual buffer */
242     bf = p->bf;
243     while ((ch = *bf++))
244         putf(putp, ch);
245
246     /* Fill with space to align to the left, after string */
247     if (!p->lz && p->align_left) {
248         while (n-- > 0)
249             putf(putp, ' ');
250     }
251 }
252
253 void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
254 {
255     struct param p;
256 #ifdef PRINTF_LONG_SUPPORT
257     char bf[23];  /* long = 64b on some architectures */
258 #else
259     char bf[12];  /* int = 32b on some architectures */
260 #endif
261     char ch;
262     p.bf = bf;
263
264     while ((ch = *(fmt++))) {
265         if (ch != '%') {
266             putf(putp, ch);
267         } else {
268 #ifdef PRINTF_LONG_SUPPORT
269             char lng = 0;  /* 1 for long, 2 for long long */
270 #endif
271             /* Init parameter struct */
272             p.lz = 0;
273             p.alt = 0;
274             p.width = 0;
275             p.align_left = 0;
276             p.sign = 0;
277
278             /* Flags */
279             while ((ch = *(fmt++))) {
280                 switch (ch) {
281                 case '-':
282                     p.align_left = 1;
283                     continue;
284                 case '0':
285                     p.lz = 1;
286                     continue;
287                 case '#':
288                     p.alt = 1;
289                     continue;
290                 default:
291                     break;
292                 }
293                 break;
294             }
295
296             /* Width */
297             if (ch >= '0' && ch <= '9') {
298                 ch = a2u(ch, &fmt, 10, &(p.width));
299             }
300
301             /* We accept 'x.y' format but don't support it completely:
302              * we ignore the 'y' digit => this ignores 0-fill
303              * size and makes it == width (ie. 'x') */
304             if (ch == '.') {
305               p.lz = 1;  /* zero-padding */
306               /* ignore actual 0-fill size: */
307               do {
308                 ch = *(fmt++);
309               } while ((ch >= '0') && (ch <= '9'));
310             }
311
312 #ifdef PRINTF_SIZE_T_SUPPORT
313 # ifdef PRINTF_LONG_SUPPORT
314             if (ch == 'z') {
315                 ch = *(fmt++);
316                 if (sizeof(size_t) == sizeof(unsigned long int))
317                     lng = 1;
318 #  ifdef PRINTF_LONG_LONG_SUPPORT
319                 else if (sizeof(size_t) == sizeof(unsigned long long int))
320                     lng = 2;
321 #  endif
322             } else
323 # endif
324 #endif
325
326 #ifdef PRINTF_LONG_SUPPORT
327             if (ch == 'l') {
328                 ch = *(fmt++);
329                 lng = 1;
330 #ifdef PRINTF_LONG_LONG_SUPPORT
331                 if (ch == 'l') {
332                   ch = *(fmt++);
333                   lng = 2;
334                 }
335 #endif
336             }
337 #endif
338             switch (ch) {
339             case 0:
340                 goto abort;
341             case 'u':
342                 p.base = 10;
343 #ifdef PRINTF_LONG_SUPPORT
344 #ifdef PRINTF_LONG_LONG_SUPPORT
345                 if (2 == lng)
346                     ulli2a(va_arg(va, unsigned long long int), &p);
347                 else
348 #endif
349                   if (1 == lng)
350                     uli2a(va_arg(va, unsigned long int), &p);
351                 else
352 #endif
353                     ui2a(va_arg(va, unsigned int), &p);
354                 putchw(putp, putf, &p);
355                 break;
356             case 'd':
357             case 'i':
358                 p.base = 10;
359 #ifdef PRINTF_LONG_SUPPORT
360 #ifdef PRINTF_LONG_LONG_SUPPORT
361                 if (2 == lng)
362                     lli2a(va_arg(va, long long int), &p);
363                 else
364 #endif
365                   if (1 == lng)
366                     li2a(va_arg(va, long int), &p);
367                 else
368 #endif
369                     i2a(va_arg(va, int), &p);
370                 putchw(putp, putf, &p);
371                 break;
372 #ifdef SIZEOF_POINTER
373             case 'p':
374                 p.alt = 1;
375 # if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT
376                 lng = 0;
377 # elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG
378                 lng = 1;
379 # elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG
380                 lng = 2;
381 # endif
382 #endif
383             case 'x':
384             case 'X':
385                 p.base = 16;
386                 p.uc = (ch == 'X')?1:0;
387 #ifdef PRINTF_LONG_SUPPORT
388 #ifdef PRINTF_LONG_LONG_SUPPORT
389                 if (2 == lng)
390                     ulli2a(va_arg(va, unsigned long long int), &p);
391                 else
392 #endif
393                   if (1 == lng)
394                     uli2a(va_arg(va, unsigned long int), &p);
395                 else
396 #endif
397                     ui2a(va_arg(va, unsigned int), &p);
398                 putchw(putp, putf, &p);
399                 break;
400             case 'o':
401                 p.base = 8;
402                 ui2a(va_arg(va, unsigned int), &p);
403                 putchw(putp, putf, &p);
404                 break;
405             case 'c':
406                 putf(putp, (char)(va_arg(va, int)));
407                 break;
408             case 's':
409                 p.bf = va_arg(va, char *);
410                 putchw(putp, putf, &p);
411                 p.bf = bf;
412                 break;
413             case '%':
414                 putf(putp, ch);
415             default:
416                 break;
417             }
418         }
419     }
420  abort:;
421 }
422
423 #if TINYPRINTF_DEFINE_TFP_PRINTF
424 static putcf stdout_putf;
425 static void *stdout_putp;
426
427 void init_printf(void *putp, putcf putf)
428 {
429     stdout_putf = putf;
430     stdout_putp = putp;
431 }
432
433 void tfp_printf(char *fmt, ...)
434 {
435     va_list va;
436     va_start(va, fmt);
437     tfp_format(stdout_putp, stdout_putf, fmt, va);
438     va_end(va);
439 }
440 #endif
441
442 #if TINYPRINTF_DEFINE_TFP_SPRINTF
443 struct _vsnprintf_putcf_data
444 {
445   size_t dest_capacity;
446   char *dest;
447   size_t num_chars;
448 };
449
450 static void _vsnprintf_putcf(void *p, char c)
451 {
452   struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p;
453   if (data->num_chars < data->dest_capacity)
454     data->dest[data->num_chars] = c;
455   data->num_chars ++;
456 }
457
458 int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap)
459 {
460   struct _vsnprintf_putcf_data data;
461
462   if (size < 1)
463     return 0;
464
465   data.dest = str;
466   data.dest_capacity = size-1;
467   data.num_chars = 0;
468   tfp_format(&data, _vsnprintf_putcf, format, ap);
469
470   if (data.num_chars < data.dest_capacity)
471     data.dest[data.num_chars] = '\0';
472   else
473     data.dest[data.dest_capacity] = '\0';
474
475   return data.num_chars;
476 }
477
478 int tfp_snprintf(char *str, size_t size, const char *format, ...)
479 {
480   va_list ap;
481   int retval;
482
483   va_start(ap, format);
484   retval = tfp_vsnprintf(str, size, format, ap);
485   va_end(ap);
486   return retval;
487 }
488
489 struct _vsprintf_putcf_data
490 {
491   char *dest;
492   size_t num_chars;
493 };
494
495 static void _vsprintf_putcf(void *p, char c)
496 {
497   struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p;
498   data->dest[data->num_chars++] = c;
499 }
500
501 int tfp_vsprintf(char *str, const char *format, va_list ap)
502 {
503   struct _vsprintf_putcf_data data;
504   data.dest = str;
505   data.num_chars = 0;
506   tfp_format(&data, _vsprintf_putcf, format, ap);
507   data.dest[data.num_chars] = '\0';
508   return data.num_chars;
509 }
510
511 int tfp_sprintf(char *str, const char *format, ...)
512 {
513   va_list ap;
514   int retval;
515
516   va_start(ap, format);
517   retval = tfp_vsprintf(str, format, ap);
518   va_end(ap);
519   return retval;
520 }
521 #endif