yaboot-1.3.17
[yaboot.git] / lib / vsprintf.c
1 /*
2  *  linux/lib/vsprintf.c
3  *
4  *  Copyright (C) 1991, 1992  Linus Torvalds
5  */
6
7 /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
8 /*
9  * Wirzenius wrote this portably, Torvalds fucked it up :-)
10  */
11
12 #include <stdarg.h>
13 #include <types.h>
14 #include <string.h>
15 #include <ctype.h>
16
17
18 /**
19  * simple_strtoul - convert a string to an unsigned long
20  * @cp: The start of the string
21  * @endp: A pointer to the end of the parsed string will be placed here
22  * @base: The number base to use
23  */
24 unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
25 {
26         unsigned long result = 0,value;
27
28         if (!base) {
29                 base = 10;
30                 if (*cp == '0') {
31                         base = 8;
32                         cp++;
33                         if ((*cp == 'x') && isxdigit(cp[1])) {
34                                 cp++;
35                                 base = 16;
36                         }
37                 }
38         }
39         while (isxdigit(*cp) &&
40                (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
41                 result = result*base + value;
42                 cp++;
43         }
44         if (endp)
45                 *endp = (char *)cp;
46         return result;
47 }
48
49 /**
50  * simple_strtol - convert a string to a signed long
51  * @cp: The start of the string
52  * @endp: A pointer to the end of the parsed string will be placed here
53  * @base: The number base to use
54  */
55 long simple_strtol(const char *cp,char **endp,unsigned int base)
56 {
57         if(*cp=='-')
58                 return -simple_strtoul(cp+1,endp,base);
59         return simple_strtoul(cp,endp,base);
60 }
61
62 /**
63  * simple_strtoull - convert a string to an unsigned long long
64  * @cp: The start of the string
65  * @endp: A pointer to the end of the parsed string will be placed here
66  * @base: The number base to use
67  */
68 unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
69 {
70         unsigned long long result = 0,value;
71
72         if (!base) {
73                 base = 10;
74                 if (*cp == '0') {
75                         base = 8;
76                         cp++;
77                         if ((*cp == 'x') && isxdigit(cp[1])) {
78                                 cp++;
79                                 base = 16;
80                         }
81                 }
82         }
83         while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
84             ? toupper(*cp) : *cp)-'A'+10) < base) {
85                 result = result*base + value;
86                 cp++;
87         }
88         if (endp)
89                 *endp = (char *)cp;
90         return result;
91 }
92
93 /**
94  * simple_strtoll - convert a string to a signed long long
95  * @cp: The start of the string
96  * @endp: A pointer to the end of the parsed string will be placed here
97  * @base: The number base to use
98  */
99 long long simple_strtoll(const char *cp,char **endp,unsigned int base)
100 {
101         if(*cp=='-')
102                 return -simple_strtoull(cp+1,endp,base);
103         return simple_strtoull(cp,endp,base);
104 }
105
106 static int skip_atoi(const char **s)
107 {
108         int i=0;
109
110         while (isdigit(**s))
111                 i = i*10 + *((*s)++) - '0';
112         return i;
113 }
114
115 #define ZEROPAD 1               /* pad with zero */
116 #define SIGN    2               /* unsigned/signed long */
117 #define PLUS    4               /* show plus */
118 #define SPACE   8               /* space if plus */
119 #define LEFT    16              /* left justified */
120 #define SPECIAL 32              /* 0x */
121 #define LARGE   64              /* use 'ABCDEF' instead of 'abcdef' */
122
123 static char * number(char * str, long long num, int base, int size, int precision, int type)
124 {
125         char c,sign,tmp[66];
126         const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
127         int i;
128
129         if (type & LARGE)
130                 digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
131         if (type & LEFT)
132                 type &= ~ZEROPAD;
133         if (base < 2 || base > 36)
134                 return 0;
135         c = (type & ZEROPAD) ? '0' : ' ';
136         sign = 0;
137         if (type & SIGN) {
138                 if (num < 0) {
139                         sign = '-';
140                         num = -num;
141                         size--;
142                 } else if (type & PLUS) {
143                         sign = '+';
144                         size--;
145                 } else if (type & SPACE) {
146                         sign = ' ';
147                         size--;
148                 }
149         }
150         if (type & SPECIAL) {
151                 if (base == 16)
152                         size -= 2;
153                 else if (base == 8)
154                         size--;
155         }
156         i = 0;
157         if (num == 0)
158                 tmp[i++]='0';
159         else while (num != 0)
160         {
161                 int t = ((long long) num) % (unsigned int) base;
162                 tmp[i++] = digits[t];
163                 num = ((long long) num) / (unsigned int) base;
164         }
165         if (i > precision)
166                 precision = i;
167         size -= precision;
168         if (!(type&(ZEROPAD+LEFT)))
169                 while(size-->0)
170                         *str++ = ' ';
171         if (sign)
172                 *str++ = sign;
173         if (type & SPECIAL) {
174                 if (base==8)
175                         *str++ = '0';
176                 else if (base==16) {
177                         *str++ = '0';
178                         *str++ = digits[33];
179                 }
180         }
181         if (!(type & LEFT))
182                 while (size-- > 0)
183                         *str++ = c;
184         while (i < precision--)
185                 *str++ = '0';
186         while (i-- > 0)
187                 *str++ = tmp[i];
188         while (size-- > 0)
189                 *str++ = ' ';
190         return str;
191 }
192
193 /**
194  * vsprintf - Format a string and place it in a buffer
195  * @buf: The buffer to place the result into
196  * @fmt: The format string to use
197  * @args: Arguments for the format string
198  *
199  * Call this function if you are already dealing with a va_list.
200  * You probably want sprintf instead.
201  */
202 int vsprintf(char *buf, const char *fmt, va_list args)
203 {
204         int len;
205         unsigned long long num;
206         int i, base;
207         char * str;
208         const char *s;
209
210         int flags;              /* flags to number() */
211
212         int field_width;        /* width of output field */
213         int precision;          /* min. # of digits for integers; max
214                                    number of chars for from string */
215         int qualifier;          /* 'h', 'l', or 'L' for integer fields */
216                                 /* 'z' support added 23/7/1999 S.H.    */
217                                 /* 'z' changed to 'Z' --davidm 1/25/99 */
218
219
220         for (str=buf ; *fmt ; ++fmt) {
221                 if (*fmt != '%') {
222                         *str++ = *fmt;
223                         continue;
224                 }
225
226                 /* process flags */
227                 flags = 0;
228                 repeat:
229                         ++fmt;          /* this also skips first '%' */
230                         switch (*fmt) {
231                                 case '-': flags |= LEFT; goto repeat;
232                                 case '+': flags |= PLUS; goto repeat;
233                                 case ' ': flags |= SPACE; goto repeat;
234                                 case '#': flags |= SPECIAL; goto repeat;
235                                 case '0': flags |= ZEROPAD; goto repeat;
236                                 }
237
238                 /* get field width */
239                 field_width = -1;
240                 if (isdigit(*fmt))
241                         field_width = skip_atoi(&fmt);
242                 else if (*fmt == '*') {
243                         ++fmt;
244                         /* it's the next argument */
245                         field_width = va_arg(args, int);
246                         if (field_width < 0) {
247                                 field_width = -field_width;
248                                 flags |= LEFT;
249                         }
250                 }
251
252                 /* get the precision */
253                 precision = -1;
254                 if (*fmt == '.') {
255                         ++fmt;
256                         if (isdigit(*fmt))
257                                 precision = skip_atoi(&fmt);
258                         else if (*fmt == '*') {
259                                 ++fmt;
260                                 /* it's the next argument */
261                                 precision = va_arg(args, int);
262                         }
263                         if (precision < 0)
264                                 precision = 0;
265                 }
266
267                 /* get the conversion qualifier */
268                 qualifier = -1;
269                 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt =='Z') {
270                         qualifier = *fmt;
271                         ++fmt;
272                 }
273
274                 /* default base */
275                 base = 10;
276
277                 switch (*fmt) {
278                 case 'c':
279                         if (!(flags & LEFT))
280                                 while (--field_width > 0)
281                                         *str++ = ' ';
282                         *str++ = (unsigned char) va_arg(args, int);
283                         while (--field_width > 0)
284                                 *str++ = ' ';
285                         continue;
286
287                 case 's':
288                         s = va_arg(args, char *);
289                         if (!s)
290                                 s = "<NULL>";
291
292                         len = strnlen(s, precision);
293
294                         if (!(flags & LEFT))
295                                 while (len < field_width--)
296                                         *str++ = ' ';
297                         for (i = 0; i < len; ++i)
298                                 *str++ = *s++;
299                         while (len < field_width--)
300                                 *str++ = ' ';
301                         continue;
302
303                 case 'p':
304                         if (field_width == -1) {
305                                 field_width = 2*sizeof(void *);
306                                 flags |= ZEROPAD;
307                         }
308                         str = number(str,
309                                 (unsigned long) va_arg(args, void *), 16,
310                                 field_width, precision, flags);
311                         continue;
312
313
314                 case 'n':
315                         if (qualifier == 'l') {
316                                 long * ip = va_arg(args, long *);
317                                 *ip = (str - buf);
318                         } else if (qualifier == 'Z') {
319                                 size_t * ip = va_arg(args, size_t *);
320                                 *ip = (str - buf);
321                         } else {
322                                 int * ip = va_arg(args, int *);
323                                 *ip = (str - buf);
324                         }
325                         continue;
326
327                 case '%':
328                         *str++ = '%';
329                         continue;
330
331                 /* integer number formats - set up the flags and "break" */
332                 case 'o':
333                         base = 8;
334                         break;
335
336                 case 'X':
337                         flags |= LARGE;
338                 case 'x':
339                         base = 16;
340                         break;
341
342                 case 'd':
343                 case 'i':
344                         flags |= SIGN;
345                 case 'u':
346                         break;
347
348                 default:
349                         *str++ = '%';
350                         if (*fmt)
351                                 *str++ = *fmt;
352                         else
353                                 --fmt;
354                         continue;
355                 }
356                 if (qualifier == 'L') {
357                         num = va_arg(args, unsigned long long);
358                         if( flags & SIGN)
359                                 num = (signed long long) num;
360                 } else if (qualifier == 'l') {
361                         num = va_arg(args, unsigned long);
362                         if (flags & SIGN)
363                                 num = (signed long) num;
364                 } else if (qualifier == 'Z') {
365                         num = va_arg(args, size_t);
366                 } else if (qualifier == 'h') {
367                         num = (unsigned short) va_arg(args, int);
368                         if (flags & SIGN)
369                                 num = (signed short) num;
370                 } else {
371                         num = va_arg(args, unsigned int);
372                         if (flags & SIGN)
373                                 num = (signed int) num;
374                 }
375                 str = number(str, num, base, field_width, precision, flags);
376         }
377         *str = '\0';
378         return str-buf;
379 }
380
381 /**
382  * sprintf - Format a string and place it in a buffer
383  * @buf: The buffer to place the result into
384  * @fmt: The format string to use
385  * @...: Arguments for the format string
386  */
387 int sprintf(char * buf, const char *fmt, ...)
388 {
389         va_list args;
390         int i;
391
392         va_start(args, fmt);
393         i=vsprintf(buf,fmt,args);
394         va_end(args);
395         return i;
396 }