Changes pde_t* -> pgdir_t
[akaros.git] / kern / src / 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 /*
13  * Fri Jul 13 2001 Crutcher Dunnavant <crutcher+kernel@datastacks.com>
14  * - changed to provide snprintf and vsnprintf functions
15  * So Feb  1 16:51:32 CET 2004 Juergen Quade <quade@hsnr.de>
16  * - scnprintf and vscnprintf
17  */
18
19 /* (trimmed to just scanf for Akaros).  grep AKAROS_PORT for dirty hacks */
20
21 #include <ros/common.h>
22 #include <error.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdarg.h>
26 #include <ns.h>
27 #include <ctype.h>
28
29 #define unlikely(x) (x)
30
31 /**
32  * skip_spaces - Removes leading whitespace from @str.
33  * @str: The string to be stripped.
34  *
35  * Returns a pointer to the first non-whitespace character in @str.
36  */
37 static char *skip_spaces(const char *str)
38 {
39         while (isspace(*str))
40                 ++str;
41         return (char *)str;
42 }
43
44 static int skip_atoi(const char **s)
45 {
46         int i = 0;
47
48         while (isdigit(**s))
49                 i = i*10 + *((*s)++) - '0';
50
51         return i;
52 }
53
54 const char *_parse_integer_fixup_radix(const char *s, unsigned int *base)
55 {
56         if (*base == 0) {
57                 if (s[0] == '0') {
58                         if (_tolower(s[1]) == 'x' && isxdigit(s[2]))
59                                 *base = 16;
60                         else
61                                 *base = 8;
62                 } else
63                         *base = 10;
64         }
65         if (*base == 16 && s[0] == '0' && _tolower(s[1]) == 'x')
66                 s += 2;
67         return s;
68 }
69
70 /**
71  * vsscanf - Unformat a buffer into a list of arguments
72  * @buf:        input buffer
73  * @fmt:        format of buffer
74  * @args:       arguments
75  */
76 int vsscanf(const char *buf, const char *fmt, va_list args)
77 {
78         const char *str = buf;
79         char *next;
80         char digit;
81         int num = 0;
82         uint8_t qualifier;
83         unsigned int base;
84         union {
85                 long long s;
86                 unsigned long long u;
87         } val;
88         int16_t field_width;
89         bool is_sign;
90
91         while (*fmt) {
92                 /* skip any white space in format */
93                 /* white space in format matchs any amount of
94                  * white space, including none, in the input.
95                  */
96                 if (isspace(*fmt)) {
97                         fmt = skip_spaces(++fmt);
98                         str = skip_spaces(str);
99                 }
100
101                 /* anything that is not a conversion must match exactly */
102                 if (*fmt != '%' && *fmt) {
103                         if (*fmt++ != *str++)
104                                 break;
105                         continue;
106                 }
107
108                 if (!*fmt)
109                         break;
110                 ++fmt;
111
112                 /* skip this conversion.
113                  * advance both strings to next white space
114                  */
115                 if (*fmt == '*') {
116                         if (!*str)
117                                 break;
118                         while (!isspace(*fmt) && *fmt != '%' && *fmt)
119                                 fmt++;
120                         while (!isspace(*str) && *str)
121                                 str++;
122                         continue;
123                 }
124
125                 /* get field width */
126                 field_width = -1;
127                 if (isdigit(*fmt)) {
128                         field_width = skip_atoi(&fmt);
129                         if (field_width <= 0)
130                                 break;
131                 }
132
133                 /* get conversion qualifier */
134                 qualifier = -1;
135                 if (*fmt == 'h' || _tolower(*fmt) == 'l' ||
136                     _tolower(*fmt) == 'z') {
137                         qualifier = *fmt++;
138                         if (unlikely(qualifier == *fmt)) {
139                                 if (qualifier == 'h') {
140                                         qualifier = 'H';
141                                         fmt++;
142                                 } else if (qualifier == 'l') {
143                                         qualifier = 'L';
144                                         fmt++;
145                                 }
146                         }
147                 }
148
149                 if (!*fmt)
150                         break;
151
152                 if (*fmt == 'n') {
153                         /* return number of characters read so far */
154                         *va_arg(args, int *) = str - buf;
155                         ++fmt;
156                         continue;
157                 }
158
159                 if (!*str)
160                         break;
161
162                 base = 10;
163                 is_sign = false;
164
165                 switch (*fmt++) {
166                 case 'c':
167                 {
168                         char *s = (char *)va_arg(args, char*);
169                         if (field_width == -1)
170                                 field_width = 1;
171                         do {
172                                 *s++ = *str++;
173                         } while (--field_width > 0 && *str);
174                         num++;
175                 }
176                 continue;
177                 case 's':
178                 {
179                         char *s = (char *)va_arg(args, char *);
180                         if (field_width == -1)
181                                 field_width = INT16_MAX;
182                         /* first, skip leading white space in buffer */
183                         str = skip_spaces(str);
184
185                         /* now copy until next white space */
186                         while (*str && !isspace(*str) && field_width--)
187                                 *s++ = *str++;
188                         *s = '\0';
189                         num++;
190                 }
191                 continue;
192                 case 'o':
193                         base = 8;
194                         break;
195                 case 'x':
196                 case 'X':
197                         base = 16;
198                         break;
199                 case 'i':
200                         base = 0;
201                 case 'd':
202                         is_sign = true;
203                 case 'u':
204                         break;
205                 case '%':
206                         /* looking for '%' in str */
207                         if (*str++ != '%')
208                                 return num;
209                         continue;
210                 default:
211                         /* invalid format; stop here */
212                         return num;
213                 }
214
215                 /* have some sort of integer conversion.
216                  * first, skip white space in buffer.
217                  */
218                 str = skip_spaces(str);
219
220                 digit = *str;
221                 if (is_sign && digit == '-')
222                         digit = *(str + 1);
223
224                 if (!digit
225                     || (base == 16 && !isxdigit(digit))
226                     || (base == 10 && !isdigit(digit))
227                     || (base == 8 && (!isdigit(digit) || digit > '7'))
228                     || (base == 0 && !isdigit(digit)))
229                         break;
230
231                 if (is_sign)
232 #if 0 // AKAROS_PORT
233                         val.s = qualifier != 'L' ?
234                                 strtol(str, &next, base) :
235                                 strtoll(str, &next, base);
236 #else
237                         val.s = strtol(str, &next, base);
238 #endif
239                 else
240 #if 0 // AKAROS_PORT
241                         val.u = qualifier != 'L' ?
242                                 strtoul(str, &next, base) :
243                                 strtoull(str, &next, base);
244 #else
245                         val.s = strtoul(str, &next, base);
246 #endif
247
248                 if (field_width > 0 && next - str > field_width) {
249                         if (base == 0)
250                                 _parse_integer_fixup_radix(str, &base);
251                         while (next - str > field_width) {
252                                 if (is_sign)    // AKAROS_PORT
253                                         val.s = (int64_t)(val.s / base);
254                                 else
255                                         val.u = (uint64_t)(val.u / base);
256                                 --next;
257                         }
258                 }
259
260                 switch (qualifier) {
261                 case 'H':       /* that's 'hh' in format */
262                         if (is_sign)
263                                 *va_arg(args, signed char *) = val.s;
264                         else
265                                 *va_arg(args, unsigned char *) = val.u;
266                         break;
267                 case 'h':
268                         if (is_sign)
269                                 *va_arg(args, short *) = val.s;
270                         else
271                                 *va_arg(args, unsigned short *) = val.u;
272                         break;
273                 case 'l':
274                         if (is_sign)
275                                 *va_arg(args, long *) = val.s;
276                         else
277                                 *va_arg(args, unsigned long *) = val.u;
278                         break;
279                 case 'L':
280                         if (is_sign)
281                                 *va_arg(args, long long *) = val.s;
282                         else
283                                 *va_arg(args, unsigned long long *) = val.u;
284                         break;
285                 case 'Z':
286                 case 'z':
287                         *va_arg(args, size_t *) = val.u;
288                         break;
289                 default:
290                         if (is_sign)
291                                 *va_arg(args, int *) = val.s;
292                         else
293                                 *va_arg(args, unsigned int *) = val.u;
294                         break;
295                 }
296                 num++;
297
298                 if (!next)
299                         break;
300                 str = next;
301         }
302
303         return num;
304 }
305
306 /**
307  * sscanf - Unformat a buffer into a list of arguments
308  * @buf:        input buffer
309  * @fmt:        formatting of buffer
310  * @...:        resulting arguments
311  */
312 int sscanf(const char *buf, const char *fmt, ...)
313 {
314         va_list args;
315         int i;
316
317         va_start(args, fmt);
318         i = vsscanf(buf, fmt, args);
319         va_end(args);
320
321         return i;
322 }