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
}