1 /*
2 * Copyright (c) 2022 FuZhou Lockzhiner Electronic Co., Ltd. All rights reserved.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include <sys/stat.h>
16 #include <stdarg.h>
17 #include <stddef.h>
18 #include <stdlib.h>
19 #include <limits.h>
20 #include <stdio.h>
21 #include <ctype.h>
22
23 #define INDEX_0 0
24 #define INDEX_1 1
25 #define INDEX_2 2
26 #define BASE_8 9
27 #define BASE_2 2
28 #define BASE_0 0
29 #define BASE_10 10
30 #define BASE_16 16
31 #define is_digit(c) ((c) >= '0' && (c) <= '9')
32 #define likely(x) __builtin_expect((x), 1)
33 #define unlikely(x) __builtin_expect((x), 0)
34 #define KSTRTOX_OVERFLOW 0
35
36 /* Convert a character to lower case */
tolower_re(char char_c)37 inline static char tolower_re(char char_c)
38 {
39 char c = char_c;
40
41 if ((c >= 'A') && (c <= 'Z')) {
42 c = (c - 'A') + 'a';
43 }
44 return c;
45 }
46
47 // div_u64_rem
div_u64_rem(unsigned long long dividend,unsigned int divisor,unsigned int * remainder)48 static inline unsigned long long div_u64_rem(unsigned long long dividend,
49 unsigned int divisor,
50 unsigned int *remainder)
51 {
52 unsigned long long ret = 0;
53
54 if (divisor == 0) {
55 return ret;
56 }
57 *remainder = dividend % divisor;
58 return dividend / divisor;
59 }
60
div_u64(unsigned long long dividend,unsigned int divisor)61 static inline unsigned long long div_u64(unsigned long long dividend, unsigned int divisor)
62 {
63 unsigned int remainder;
64 return div_u64_rem(dividend, divisor, &remainder);
65 }
66
67 // lib/kstrtox.c, line 23
_parse_integer_fixup_radix(const char * str,unsigned int * base)68 const char *_parse_integer_fixup_radix(const char *str, unsigned int *base)
69 {
70 const char *s = str;
71
72 if (*base == BASE_0) {
73 if (s[BASE_0] == '0') {
74 if (tolower_re(s[INDEX_1]) == 'x' && isxdigit(s[INDEX_2])) {
75 *base = BASE_16;
76 } else {
77 *base = BASE_8;
78 }
79 } else {
80 *base = BASE_10;
81 }
82 }
83 if (*base == BASE_16 && s[INDEX_0] == '0' && tolower_re(s[INDEX_1]) == 'x') {
84 s += BASE_2;
85 }
86 return s;
87 }
88
_parse_integer(const char * str,unsigned int base,unsigned long long * p)89 unsigned int _parse_integer(const char *str, unsigned int base, unsigned long long *p)
90 {
91 unsigned long long res;
92 unsigned int rv;
93 int overflow;
94 const char *s = str;
95
96 res = 0;
97 rv = 0;
98 overflow = 0;
99 while (*s) {
100 unsigned int val;
101
102 if (*s >= '0' && *s <= '9') {
103 val = *s - '0';
104 } else if (tolower_re(*s) >= 'a' && tolower_re(*s) <= 'f') {
105 val = tolower_re(*s) - 'a' + BASE_10;
106 } else {
107 break;
108 }
109 if (val >= base) {
110 break;
111 }
112 /*
113 * Check for overflow only if we are within range of
114 * it in the max base we support (16)
115 */
116 if (unlikely(res & (~0ull << 60))) {
117 if (res > div_u64(ULLONG_MAX - val, base)) {
118 overflow = 1;
119 }
120 }
121 res = res * base + val;
122 rv++;
123 s++;
124 }
125 *p = res;
126 if (overflow) {
127 rv |= KSTRTOX_OVERFLOW;
128 }
129 return rv;
130 }
131
isspace_re(int x)132 int isspace_re(int x)
133 {
134 if (x == ' ' || x == '/t' || x == '/n' || x == '/f' || x == '/b' || x == '/r') {
135 return 1;
136 } else {
137 return 0;
138 }
139 }
140
skip_spaces(const char * str)141 char* skip_spaces(const char * str)
142 {
143 const char* str_temp = str;
144
145 while (isspace_re(*str_temp)) {
146 ++str_temp;
147 }
148 return (char *)str_temp;
149 }
150
151 // simple_strtoull - convert a string to an unsigned long long
simple_strtoull(const char * cp_temp,char ** endp,unsigned int base)152 unsigned long long simple_strtoull(const char *cp_temp, char **endp, unsigned int base)
153 {
154 unsigned long long result;
155 unsigned int rv;
156 const char *cp = cp_temp;
157
158 cp = _parse_integer_fixup_radix(cp, &base);
159 rv = _parse_integer(cp, base, &result);
160
161 cp += (rv & ~KSTRTOX_OVERFLOW);
162
163 if (endp) {
164 *endp = (char *)cp;
165 }
166 return result;
167 }
168
skip_atoi(const char ** s)169 static int skip_atoi(const char **s)
170 {
171 int i = 0;
172 while (is_digit(**s)) {
173 i = i * BASE_10 + *((*s)++) - '0';
174 }
175 return i;
176 }
177
simple_strtoul(const char * cp,char ** endp,unsigned int base)178 unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base)
179 {
180 return simple_strtoull(cp, endp, base);
181 }
182
183 /**
184 * simple_strtoll - convert a string to a signed long long
185 * @cp: The start of the string
186 * @endp: A pointer to the end of the parsed string will be placed here
187 * @base: The number base to use
188 */
simple_strtoll(const char * cp,char ** endp,unsigned int base)189 long long simple_strtoll(const char *cp, char **endp, unsigned int base)
190 {
191 if (*cp == '-') {
192 return -simple_strtoull(cp + 1, endp, base);
193 }
194 return simple_strtoull(cp, endp, base);
195 }
196
simple_strtol(const char * cp,char ** endp,unsigned int base)197 long simple_strtol(const char *cp, char **endp, unsigned int base)
198 {
199 if (*cp == '-') {
200 return -simple_strtoul(cp + 1, endp, base);
201 }
202 return simple_strtoul(cp, endp, base);
203 }
204
205 /**
206 * vsscanf - Unformat a buffer into a list of arguments
207 * @buf: input buffer
208 * @fmt: format of buffer
209 * @args: arguments
210 */
pre_vsscanf(const char * __restrict __s,const char * __restrict __format,va_list arg)211 int pre_vsscanf(const char *__restrict __s, const char *__restrict __format, va_list arg)
212 {
213 const char *str = __s;
214 char *next;
215 char digit;
216 int num = 0;
217 unsigned char qualifier;
218 unsigned char base;
219 short field_width;
220 char is_sign;
221 const char *fmt = __format;
222
223 while (*fmt && *str) {
224 /* skip any white space in format */
225 /* white space in format matchs any amount of
226 * white space, including none, in the input.
227 */
228 if (isspace_re(*fmt)) {
229 ++fmt;
230 fmt = skip_spaces(fmt);
231 str = skip_spaces(str);
232 }
233
234 /* anything that is not a conversion must match exactly */
235 if (*fmt != '%' && *fmt) {
236 if (*fmt++ != *str++) {
237 break;
238 }
239 continue;
240 }
241
242 if (!*fmt) {
243 break;
244 }
245 ++fmt;
246
247 /* skip this conversion.
248 * advance both strings to next white space
249 */
250 if (*fmt == '*') {
251 while (!isspace_re(*fmt) && *fmt != '%' && *fmt) {
252 fmt++;
253 }
254 while (!isspace_re(*str) && *str) {
255 str++;
256 }
257 continue;
258 }
259
260 /* get field width */
261 field_width = -1;
262 if (is_digit(*fmt)) {
263 field_width = skip_atoi(&fmt);
264 }
265
266 /* get conversion qualifier */
267 qualifier = -1;
268 if (*fmt == 'h' || tolower_re(*fmt) == 'l' ||
269 tolower_re(*fmt) == 'z') {
270 qualifier = *fmt++;
271 if (likely(qualifier == *fmt)) {
272 break;
273 }
274 if (qualifier == 'h') {
275 qualifier = 'H';
276 fmt++;
277 } else if (qualifier == 'l') {
278 qualifier = 'L';
279 fmt++;
280 }
281 }
282
283 if (!*fmt || !*str) {
284 break;
285 }
286
287 base = BASE_10;
288 is_sign = 0;
289
290 switch (*fmt++) {
291 case 'c':
292 {
293 char *s = (char *)va_arg(arg, char *);
294 if (field_width == -1) {
295 field_width = 1;
296 }
297 do {
298 *s++ = *str++;
299 } while (--field_width > 0 && *str);
300 num++;
301 }
302 continue;
303 case 's':
304 {
305 char *s = (char *)va_arg(arg, char *);
306 if (field_width == -1) {
307 field_width = SHRT_MAX;
308 }
309 /* first, skip leading white space in buffer */
310 str = skip_spaces(str);
311
312 /* now copy until next white space */
313 while (*str && !isspace_re(*str) && field_width--) {
314 *s++ = *str++;
315 }
316 *s = '\0';
317 num++;
318 }
319 continue;
320 case 'n':
321 /* return number of characters read so far */
322 {
323 int *i = (int *)va_arg(arg, int *);
324 *i = str - __s;
325 }
326 continue;
327 case 'o':
328 base = BASE_8;
329 break;
330 case 'x':
331 case 'X':
332 base = BASE_16;
333 break;
334 case 'i':
335 base = 0;
336 case 'd':
337 is_sign = 1;
338 case 'u':
339 break;
340 case '%':
341 /* looking for '%' in str */
342 if (*str++ != '%') {
343 return num;
344 }
345 continue;
346 default:
347 /* invalid format; stop here */
348 return num;
349 }
350
351 /* have some sort of integer conversion.
352 * first, skip white space in buffer.
353 */
354 str = skip_spaces(str);
355
356 digit = *str;
357 if (is_sign && digit == '-') {
358 digit = *(str + 1);
359 }
360 if (!digit
361 || (base == BASE_16 && !isxdigit(digit))
362 || (base == BASE_10 && !isdigit(digit))
363 || (base == BASE_8 && (!isdigit(digit) || digit > '7'))
364 || (base == BASE_0 && !isdigit(digit))) {
365 break;
366 }
367 switch (qualifier) {
368 case 'H': /* that's 'hh' in format */
369 if (is_sign) {
370 signed char *s = (signed char *)va_arg(arg, signed char *);
371 *s = (signed char)simple_strtol(str, &next, base);
372 } else {
373 unsigned char *s = (unsigned char *)va_arg(arg, unsigned char *);
374 *s = (unsigned char)simple_strtoul(str, &next, base);
375 }
376 break;
377 case 'h':
378 if (is_sign) {
379 short *s = (short *)va_arg(arg, short *);
380 *s = (short)simple_strtol(str, &next, base);
381 } else {
382 unsigned short *s = (unsigned short *)va_arg(arg, unsigned short *);
383 *s = (unsigned short)simple_strtoul(str, &next, base);
384 }
385 break;
386 case 'l':
387 if (is_sign) {
388 long *l = (long *)va_arg(arg, long *);
389 *l = simple_strtol(str, &next, base);
390 } else {
391 unsigned long *l = (unsigned long *)va_arg(arg, unsigned long *);
392 *l = simple_strtoul(str, &next, base);
393 }
394 break;
395 case 'L':
396 if (is_sign) {
397 long long *l = (long long *)va_arg(arg, long long *);
398 *l = simple_strtoll(str, &next, base);
399 } else {
400 unsigned long long *l = (unsigned long long *)va_arg(arg, unsigned long long *);
401 *l = simple_strtoull(str, &next, base);
402 }
403 break;
404 case 'Z':
405 case 'z':
406 {
407 size_t *s = (size_t *)va_arg(arg, size_t *);
408 *s = (size_t)simple_strtoul(str, &next, base);
409 }
410 break;
411 default:
412 if (is_sign) {
413 int *i = (int *)va_arg(arg, int *);
414 *i = (int)simple_strtol(str, &next, base);
415 } else {
416 unsigned int *i = (unsigned int *)va_arg(arg, unsigned int*);
417 *i = (unsigned int)simple_strtoul(str, &next, base);
418 }
419 break;
420 }
421 num++;
422
423 if (!next) {
424 break;
425 }
426 str = next;
427 }
428
429 /*
430 * Now we've come all the way through so either the input string or the
431 * format ended. In the former case, there can be a %n at the current
432 * position in the format that needs to be filled.
433 */
434 if (*fmt == '%' && *(fmt + 1) == 'n') {
435 int *p = (int *)va_arg(arg, int *);
436 *p = str - __s;
437 }
438
439 return num;
440 }
441 /**
442 * sscanf - Unformat a buffer into a list of arguments
443 * @__s: input buffer
444 * @__format: formatting of buffer
445 * @...: resulting arguments
446 */
sscanf(const char * __restrict __s,const char * __restrict __format,...)447 int sscanf(const char *__restrict __s, const char *__restrict __format, ...)
448 {
449 va_list args;
450 int i;
451
452 va_start(args, __format);
453 i = pre_vsscanf(__s, __format, args);
454 va_end(args);
455
456 return i;
457 }