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