git.cappig.dev / apheleiaOS.git master

1
#include "ctype.h"
2
#include "stdarg.h"
3
#include "stdbool.h"
4
#include "stddef.h"
5
#include "stdint.h"
6
#include "stdlib.h"
7
8
static int _get_width(const char *format, size_t *index) {
9
    int width = -1;
10
11
    if (isdigit(format[*index])) {
12
        char *end;
13
        width = strtol(&format[*index], &end, 10);
14
15
        *index += (size_t)(end - &format[*index]);
16
    }
17
18
    return width;
19
}
20
21
enum scan_size {
22
    SCAN_CHAR,
23
    SCAN_SHORT,
24
    SCAN_INT,
25
    SCAN_LONG,
26
    SCAN_LLONG,
27
    SCAN_SIZE,
28
    SCAN_PTRDIFF,
29
    SCAN_INTMAX,
30
    SCAN_PTR
31
};
32
33
static enum scan_size _read_size(const char *format, size_t *index) {
34
    switch (format[*index]) {
35
    case 'h':
36
        (*index)++;
37
        if (format[*index] == 'h') {
38
            (*index)++;
39
            return SCAN_CHAR;
40
        }
41
        return SCAN_SHORT;
42
43
    case 'l':
44
        (*index)++;
45
        if (format[*index] == 'l') {
46
            (*index)++;
47
            return SCAN_LLONG;
48
        }
49
        return SCAN_LONG;
50
51
    case 'z':
52
        (*index)++;
53
        return SCAN_SIZE;
54
55
    case 'j':
56
        (*index)++;
57
        return SCAN_INTMAX;
58
59
    case 't':
60
        (*index)++;
61
        return SCAN_PTRDIFF;
62
63
    default:
64
        return SCAN_INT;
65
    }
66
}
67
68
static bool _is_signed_number(char type) {
69
    return type == 'd' || type == 'i';
70
}
71
72
static void _store_signed(void *ptr, enum scan_size size, long long value) {
73
    switch (size) {
74
    case SCAN_CHAR:
75
        *(signed char *)ptr = (signed char)value;
76
        break;
77
78
    case SCAN_SHORT:
79
        *(short *)ptr = (short)value;
80
        break;
81
82
    case SCAN_LONG:
83
        *(long *)ptr = (long)value;
84
        break;
85
86
    case SCAN_LLONG:
87
        *(long long *)ptr = value;
88
        break;
89
90
    case SCAN_SIZE:
91
        *(size_t *)ptr = (size_t)value;
92
        break;
93
94
    case SCAN_PTRDIFF:
95
        *(ptrdiff_t *)ptr = (ptrdiff_t)value;
96
        break;
97
98
    case SCAN_INTMAX:
99
        *(intmax_t *)ptr = (intmax_t)value;
100
        break;
101
102
    case SCAN_INT:
103
    default:
104
        *(int *)ptr = (int)value;
105
        break;
106
    }
107
}
108
109
static void _store_unsigned(void *ptr, enum scan_size size, unsigned long long value) {
110
    switch (size) {
111
    case SCAN_CHAR:
112
        *(unsigned char *)ptr = (unsigned char)value;
113
        break;
114
115
    case SCAN_SHORT:
116
        *(unsigned short *)ptr = (unsigned short)value;
117
        break;
118
119
    case SCAN_LONG:
120
        *(unsigned long *)ptr = (unsigned long)value;
121
        break;
122
123
    case SCAN_LLONG:
124
        *(unsigned long long *)ptr = value;
125
        break;
126
127
    case SCAN_SIZE:
128
        *(size_t *)ptr = (size_t)value;
129
        break;
130
131
    case SCAN_PTRDIFF:
132
        *(ptrdiff_t *)ptr = (ptrdiff_t)value;
133
        break;
134
135
    case SCAN_INTMAX:
136
        *(uintmax_t *)ptr = (uintmax_t)value;
137
        break;
138
139
    case SCAN_PTR:
140
        *(void **)ptr = (void *)(uintptr_t)value;
141
        break;
142
143
    case SCAN_INT:
144
    default:
145
        *(unsigned int *)ptr = (unsigned int)value;
146
        break;
147
    }
148
}
149
150
#define BASE_STRING  -1
151
#define BASE_CHAR    -2
152
#define BASE_SET     -3
153
#define BASE_UNKNOWN -4
154
155
static int _get_base(char type) {
156
    switch (type) {
157
    case 'p':
158
    case 'x':
159
    case 'X':
160
        return 16;
161
162
    case 'i':
163
        return 0;
164
165
    case 'd':
166
    case 'u':
167
        return 10;
168
169
    case 'o':
170
        return 8;
171
172
    case 'b':
173
        return 2;
174
175
    case 'c':
176
        return BASE_CHAR;
177
178
    case 's':
179
        return BASE_STRING;
180
181
    case '[':
182
        return BASE_SET;
183
184
    default:
185
        return BASE_UNKNOWN;
186
    }
187
}
188
189
static void _adjust_width(int base, const char *restrict str, int *width) {
190
    if (base == BASE_STRING) {
191
        int len = 0;
192
        while (!isspace(str[len]) && str[len]) {
193
            len++;
194
        }
195
196
        if (*width == -1) {
197
            *width = len;
198
        } else {
199
            *width = min(*width, len);
200
        }
201
202
    } else if (base == BASE_CHAR) {
203
        if (*width == -1) {
204
            *width = 1;
205
        }
206
    }
207
}
208
209
int vsnscanf(const char *restrict str, size_t max, const char *restrict format, va_list vlist) {
210
    size_t filled = 0;
211
    size_t j = 0;
212
213
    for (size_t i = 0; format[i] && filled < max; i++) {
214
        if (format[i] == '%') {
215
            i++;
216
217
            bool ignore = false;
218
            if (format[i] == '*') {
219
                ignore = true;
220
                i++;
221
            }
222
223
            int width = _get_width(format, &i);
224
225
            enum scan_size size = _read_size(format, &i);
226
227
            int base = _get_base(format[i]);
228
            if (format[i] == 'p') {
229
                size = SCAN_PTR;
230
            }
231
232
            // number
233
            if (base >= 0) {
234
                char *end;
235
                const char *start = &str[j];
236
237
                if (_is_signed_number(format[i])) {
238
                    long long number = strtoll(start, &end, base);
239
                    if (end == start) {
240
                        break;
241
                    }
242
243
                    j += (size_t)(end - start);
244
245
                    if (ignore) {
246
                        continue;
247
                    }
248
249
                    _store_signed(va_arg(vlist, void *), size, number);
250
                    filled++;
251
                    continue;
252
                }
253
254
                unsigned long long number = strtoull(start, &end, base);
255
                if (end == start) {
256
                    break;
257
                }
258
259
                j += (size_t)(end - start);
260
261
                if (ignore) {
262
                    continue;
263
                }
264
265
                _store_unsigned(va_arg(vlist, void *), size, number);
266
                filled++;
267
            }
268
            // string
269
            else if (base == BASE_CHAR || base == BASE_STRING) {
270
                _adjust_width(base, &str[j], &width);
271
272
                if (ignore) {
273
                    j += width;
274
                    continue;
275
                }
276
277
                char *ptr = va_arg(vlist, char *);
278
                filled++;
279
280
                for (int c = 0; c < width; c++) {
281
                    ptr[c] = str[j++];
282
                }
283
284
                if (base == BASE_STRING) {
285
                    ptr[width] = '\0';
286
                }
287
            }
288
        }
289
        // handle spaces
290
        else if (format[i] == ' ') {
291
            while (isspace(str[j]) && str[j]) {
292
                j++;
293
            }
294
        }
295
        // check str against format
296
        else {
297
            if (format[i] != str[j]) {
298
                break;
299
            }
300
301
            j++;
302
        }
303
    }
304
305
    return filled;
306
}