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