chdir: collapse /./ and /../ in path for conventional filesystems
[profile/ivi/syslinux.git] / memdump / printf.c
1 /*
2  * Oh, it's a waste of space, but oh-so-yummy for debugging.  It's just
3  * initialization code anyway, so it doesn't take up space when we're
4  * actually running.  This version of printf() does not include 64-bit
5  * support.  "Live with it."
6  *
7  * Most of this code was shamelessly snarfed from the Linux kernel, then
8  * modified.  It's therefore GPL.
9  *
10  * printf() isn't actually needed to build syslinux.com, but during
11  * debugging it's handy.
12  */
13
14 #include <stdarg.h>
15 #include <stdio.h>
16 #include "mystuff.h"
17
18 static int strnlen(const char *s, int maxlen)
19 {
20     const char *es = s;
21     while (*es && maxlen) {
22         es++;
23         maxlen--;
24     }
25
26     return (es - s);
27 }
28
29 #define ZEROPAD 1               /* pad with zero */
30 #define SIGN    2               /* unsigned/signed long */
31 #define PLUS    4               /* show plus */
32 #define SPACE   8               /* space if plus */
33 #define LEFT    16              /* left justified */
34 #define SPECIAL 32              /* 0x */
35 #define LARGE   64              /* use 'ABCDEF' instead of 'abcdef' */
36
37 #define do_div(n,base) ({ \
38 int __res; \
39 __res = ((unsigned long) n) % (unsigned) base; \
40 n = ((unsigned long) n) / (unsigned) base; \
41 __res; })
42
43 static char *number(char *str, long num, int base, int size, int precision,
44                     int type)
45 {
46     char c, sign, tmp[66];
47     const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
48     int i;
49
50     if (type & LARGE)
51         digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
52     if (type & LEFT)
53         type &= ~ZEROPAD;
54     if (base < 2 || base > 36)
55         return 0;
56     c = (type & ZEROPAD) ? '0' : ' ';
57     sign = 0;
58     if (type & SIGN) {
59         if (num < 0) {
60             sign = '-';
61             num = -num;
62             size--;
63         } else if (type & PLUS) {
64             sign = '+';
65             size--;
66         } else if (type & SPACE) {
67             sign = ' ';
68             size--;
69         }
70     }
71     if (type & SPECIAL) {
72         if (base == 16)
73             size -= 2;
74         else if (base == 8)
75             size--;
76     }
77     i = 0;
78     if (num == 0)
79         tmp[i++] = '0';
80     else
81         while (num != 0)
82             tmp[i++] = digits[do_div(num, base)];
83     if (i > precision)
84         precision = i;
85     size -= precision;
86     if (!(type & (ZEROPAD + LEFT)))
87         while (size-- > 0)
88             *str++ = ' ';
89     if (sign)
90         *str++ = sign;
91     if (type & SPECIAL) {
92         if (base == 8)
93             *str++ = '0';
94         else if (base == 16) {
95             *str++ = '0';
96             *str++ = digits[33];
97         }
98     }
99     if (!(type & LEFT))
100         while (size-- > 0)
101             *str++ = c;
102     while (i < precision--)
103         *str++ = '0';
104     while (i-- > 0)
105         *str++ = tmp[i];
106     while (size-- > 0)
107         *str++ = ' ';
108     return str;
109 }
110
111 /* Forward decl. needed for IP address printing stuff... */
112 int sprintf(char *buf, const char *fmt, ...);
113
114 int vsprintf(char *buf, const char *fmt, va_list args)
115 {
116     int len;
117     unsigned long num;
118     int i, base;
119     char *str;
120     const char *s;
121
122     int flags;                  /* flags to number() */
123
124     int field_width;            /* width of output field */
125     int precision;              /* min. # of digits for integers; max
126                                    number of chars for from string */
127     int qualifier;              /* 'h', 'l', or 'L' for integer fields */
128
129     for (str = buf; *fmt; ++fmt) {
130         if (*fmt != '%') {
131             *str++ = *fmt;
132             continue;
133         }
134
135         /* process flags */
136         flags = 0;
137 repeat:
138         ++fmt;                  /* this also skips first '%' */
139         switch (*fmt) {
140         case '-':
141             flags |= LEFT;
142             goto repeat;
143         case '+':
144             flags |= PLUS;
145             goto repeat;
146         case ' ':
147             flags |= SPACE;
148             goto repeat;
149         case '#':
150             flags |= SPECIAL;
151             goto repeat;
152         case '0':
153             flags |= ZEROPAD;
154             goto repeat;
155         }
156
157         /* get field width */
158         field_width = -1;
159         if (isdigit(*fmt))
160             field_width = skip_atou(&fmt);
161         else if (*fmt == '*') {
162             ++fmt;
163             /* it's the next argument */
164             field_width = va_arg(args, int);
165             if (field_width < 0) {
166                 field_width = -field_width;
167                 flags |= LEFT;
168             }
169         }
170
171         /* get the precision */
172         precision = -1;
173         if (*fmt == '.') {
174             ++fmt;
175             if (isdigit(*fmt))
176                 precision = skip_atou(&fmt);
177             else if (*fmt == '*') {
178                 ++fmt;
179                 /* it's the next argument */
180                 precision = va_arg(args, int);
181             }
182             if (precision < 0)
183                 precision = 0;
184         }
185
186         /* get the conversion qualifier */
187         qualifier = -1;
188         if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
189             qualifier = *fmt;
190             ++fmt;
191         }
192
193         /* default base */
194         base = 10;
195
196         switch (*fmt) {
197         case 'c':
198             if (!(flags & LEFT))
199                 while (--field_width > 0)
200                     *str++ = ' ';
201             *str++ = (unsigned char)va_arg(args, int);
202             while (--field_width > 0)
203                 *str++ = ' ';
204             continue;
205
206         case 's':
207             s = va_arg(args, char *);
208             len = strnlen(s, precision);
209
210             if (!(flags & LEFT))
211                 while (len < field_width--)
212                     *str++ = ' ';
213             for (i = 0; i < len; ++i)
214                 *str++ = *s++;
215             while (len < field_width--)
216                 *str++ = ' ';
217             continue;
218
219         case 'p':
220             if (field_width == -1) {
221                 field_width = 2 * sizeof(void *);
222                 flags |= ZEROPAD;
223             }
224             str = number(str,
225                          (unsigned long)va_arg(args, void *), 16,
226                          field_width, precision, flags);
227             continue;
228
229         case 'n':
230             if (qualifier == 'l') {
231                 long *ip = va_arg(args, long *);
232                 *ip = (str - buf);
233             } else {
234                 int *ip = va_arg(args, int *);
235                 *ip = (str - buf);
236             }
237             continue;
238
239         case '%':
240             *str++ = '%';
241             continue;
242
243             /* integer number formats - set up the flags and "break" */
244         case 'o':
245             base = 8;
246             break;
247
248         case 'X':
249             flags |= LARGE;
250         case 'x':
251             base = 16;
252             break;
253
254         case 'd':
255         case 'i':
256             flags |= SIGN;
257         case 'u':
258             break;
259
260         default:
261             *str++ = '%';
262             if (*fmt)
263                 *str++ = *fmt;
264             else
265                 --fmt;
266             continue;
267         }
268         if (qualifier == 'l')
269             num = va_arg(args, unsigned long);
270         else if (qualifier == 'h') {
271             num = (unsigned short)va_arg(args, int);
272             if (flags & SIGN)
273                 num = (short)num;
274         } else if (flags & SIGN)
275             num = va_arg(args, int);
276         else
277             num = va_arg(args, unsigned int);
278         str = number(str, num, base, field_width, precision, flags);
279     }
280     *str = '\0';
281     return str - buf;
282 }
283
284 int sprintf(char *buf, const char *fmt, ...)
285 {
286     va_list args;
287     int i;
288
289     va_start(args, fmt);
290     i = vsprintf(buf, fmt, args);
291     va_end(args);
292     return i;
293 }
294
295 int printf(const char *fmt, ...)
296 {
297     char printf_buf[1024];
298     va_list args;
299     int printed;
300
301     va_start(args, fmt);
302     printed = vsprintf(printf_buf, fmt, args);
303     va_end(args);
304
305     puts(printf_buf);
306
307     return printed;
308 }