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