• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }