PatchworkOS  19e446b
A non-POSIX operating system.
Loading...
Searching...
No Matches
scan.h
Go to the documentation of this file.
1#pragma once
2
3#include <assert.h>
4#include <ctype.h>
5#include <stdarg.h>
6#include <stddef.h>
7#include <stdint.h>
8#include <stdlib.h>
9#include <string.h>
10#include <sys/defs.h>
11
12#include "digits.h"
13
14#ifndef _SCAN_GET
15#error "_SCAN_GET not defined"
16#endif
17
18#ifndef _SCAN_UNGET
19#error "_SCAN_UNGET not defined"
20#endif
21
22/**
23 * @brief Internal Scan Implementation.
24 * @defgroup libstd_common_scan Scan
25 * @ingroup libstd_common
26 *
27 * Provides a common implementation for scanning formatted input, any function that needs to scan formatted input should
28 * define the `_SCAN_GET()` and `_SCAN_UNGET()` macros before including this file.
29 *
30 * The `_SCAN_GET(ctx)` macro should evaluate to an expression that returns the next character from the input source and
31 * takes a pointer to the current `scan_ctx_t` as an argument.
32 *
33 * The`_SCAN_UNGET(ctx, c)` macro should evaluate to an expression that pushes back the character `c` to the input
34 * source and takes a pointer to the current `scan_ctx_t` and the character to push back as argument.
35 *
36 * @todo Implement floating point scanning.
37 *
38 * @see https://cplusplus.com/reference/cstdio/scanf/ for details on the format specifiers.
39 *
40 * @{
41 */
42
43typedef struct
44{
46 const char* p;
48 void* data;
50 char prev;
52
53static inline int _scan_next(_scan_ctx_t* ctx)
54{
55 if (ctx->prev != EOF)
56 {
57 int c = ctx->prev;
58 ctx->prev = EOF;
59 ctx->count++;
60 return c;
61 }
62
63 int c = _SCAN_GET(ctx);
64 if (c != EOF)
65 {
66 ctx->count++;
67 }
68 return c;
69}
70
71static inline void _scan_undo(_scan_ctx_t* ctx, int c)
72{
73 if (c != EOF)
74 {
75 assert(ctx->count > 0);
76 assert(ctx->prev == EOF);
77 ctx->prev = (char)c;
78 ctx->count--;
79 }
80}
81
82static inline int _scan_whitespace(_scan_ctx_t* ctx)
83{
84 while (isspace(*ctx->p))
85 {
86 ctx->p++;
87 }
88
89 int c;
90 while (true)
91 {
92 c = _scan_next(ctx);
93 if (c == EOF)
94 {
95 return EOF;
96 }
97
98 if (!isspace(c))
99 {
100 _scan_undo(ctx, c);
101 break;
102 }
103 }
104
105 return 0;
106}
107
108typedef enum
109{
112
124
131
133{
134 switch (format->length)
135 {
136 case _SCAN_DEFAULT:
137 *va_arg(ctx->arg, int*) = (int)value;
138 break;
139 case _SCAN_HH:
140 *va_arg(ctx->arg, signed char*) = (signed char)value;
141 break;
142 case _SCAN_H:
143 *va_arg(ctx->arg, short int*) = (short int)value;
144 break;
145 case _SCAN_L:
146 *va_arg(ctx->arg, long int*) = (long int)value;
147 break;
148 case _SCAN_LL:
149 *va_arg(ctx->arg, long long int*) = (long long int)value;
150 break;
151 case _SCAN_J:
152 *va_arg(ctx->arg, intmax_t*) = (intmax_t)value;
153 break;
154 case _SCAN_Z:
155 *va_arg(ctx->arg, size_t*) = (size_t)value;
156 break;
157 case _SCAN_T:
158 *va_arg(ctx->arg, ptrdiff_t*) = (ptrdiff_t)value;
159 break;
160 default:
161 return EOF;
162 }
163
164 return 0;
165}
166
168{
169 switch (format->length)
170 {
171 case _SCAN_DEFAULT:
172 *va_arg(ctx->arg, unsigned int*) = (unsigned int)value;
173 break;
174 case _SCAN_HH:
175 *va_arg(ctx->arg, unsigned char*) = (unsigned char)value;
176 break;
177 case _SCAN_H:
178 *va_arg(ctx->arg, unsigned short int*) = (unsigned short int)value;
179 break;
180 case _SCAN_L:
181 *va_arg(ctx->arg, unsigned long int*) = (unsigned long int)value;
182 break;
183 case _SCAN_LL:
184 *va_arg(ctx->arg, unsigned long long int*) = (unsigned long long int)value;
185 break;
186 case _SCAN_J:
187 *va_arg(ctx->arg, uintmax_t*) = (uintmax_t)value;
188 break;
189 case _SCAN_Z:
190 *va_arg(ctx->arg, size_t*) = (size_t)value;
191 break;
192 case _SCAN_T:
193 *va_arg(ctx->arg, ptrdiff_t*) = (ptrdiff_t)value;
194 break;
195 default:
196 return EOF;
197 }
198
199 return 0;
200}
201
207
210{
211 int sign = 1;
212
213 int c = _scan_next(ctx);
214 if (c == EOF)
215 {
216 return EOF;
217 }
218
219 if (c == '-')
220 {
221 sign = -1;
222 c = _scan_next(ctx);
223 if (c == EOF)
224 {
225 return EOF;
226 }
227 }
228 else if (c == '+')
229 {
230 c = _scan_next(ctx);
231 if (c == EOF)
232 {
233 return EOF;
234 }
235 }
236
237 if (base == 0 || base == 16)
238 {
239 if (c == '0')
240 {
241 int next = _scan_next(ctx);
242 if ((next == 'x' || next == 'X') && (base == 0 || base == 16))
243 {
244 base = 16;
245 c = _scan_next(ctx);
246 }
247 else
248 {
249 if (base == 0)
250 {
251 base = 8;
252 }
253
254 if (next != EOF)
255 {
256 _scan_undo(ctx, next);
257 }
258 }
259 }
260 }
261
262 if (base == 0)
263 {
264 base = 10;
265 }
266
267 uint64_t value = 0;
268 uint64_t digits = 0;
269 while (c != EOF && digits < format->width)
270 {
271 uint8_t digit = _digit_to_int(c);
272 if (digit >= base)
273 {
274 _scan_undo(ctx, c);
275 break;
276 }
277
278 value = (value * base) + digit;
279 c = _scan_next(ctx);
280 digits++;
281 }
282
283 if (digits == 0)
284 {
285 return 0;
286 }
287
289 {
290 return 0;
291 }
292
294 {
295 int64_t signedValue = (int64_t)value * sign;
296 if (_scan_asign_signed_int(ctx, format, signedValue) == EOF)
297 {
298 return EOF;
299 }
300
301 ctx->parsedItems++;
302 return 0;
303 }
304
305 if (_scan_asign_unsigned_int(ctx, format, value) == EOF)
306 {
307 return EOF;
308 }
309
310 ctx->parsedItems++;
311 return 0;
312}
313
315{
316 /// @todo Implement floating point scanning
317 (void)ctx;
318 (void)format;
319 return EOF;
320}
321
323{
324 uint64_t width = format->width == UINT64_MAX ? 1 : format->width;
325
327 {
328 for (uint64_t i = 0; i < width; i++)
329 {
330 int c = _scan_next(ctx);
331 if (c == EOF)
332 {
333 return EOF;
334 }
335 }
336
337 return 0;
338 }
339
340 ctx->parsedItems++;
341
342 char* buffer = va_arg(ctx->arg, char*);
343 for (uint64_t i = 0; i < width; i++)
344 {
345 int c = _scan_next(ctx);
346 if (c == EOF)
347 {
348 return EOF;
349 }
350
351 buffer[i] = (char)c;
352 }
353
354 return 0;
355}
356
358{
359 char* buffer = NULL;
360 if (!(format->flags & _SCAN_SUPPRESS_ASSIGNMENT))
361 {
362 buffer = va_arg(ctx->arg, char*);
363 }
364
365 uint64_t count = 0;
366 while (count < format->width)
367 {
368 int c = _scan_next(ctx);
369 if (c == EOF || isspace(c))
370 {
371 if (c != EOF)
372 {
373 _scan_undo(ctx, c);
374 }
375 break;
376 }
377
378 if (buffer != NULL)
379 {
380 buffer[count] = (char)c;
381 }
382
383 count++;
384 }
385
386 if (count == 0)
387 {
388 return EOF;
389 }
390
391 if (buffer != NULL)
392 {
393 buffer[count] = '\0';
394 ctx->parsedItems++;
395 }
396
397 return 0;
398}
399
400typedef struct
401{
402 uint64_t table[UINT8_MAX / (sizeof(uint64_t) * 8) + 1];
403 bool invert;
404} _scanset_t;
405
406static inline void _scanset_set(_scanset_t* scanset, uint8_t c)
407{
408 scanset->table[c / (sizeof(uint64_t) * 8)] |= ((uint64_t)1 << (c % (sizeof(uint64_t) * 8)));
409}
410
411static inline bool _scanset_get(_scanset_t* scanset, uint8_t c)
412{
413 bool found = (scanset->table[c / (sizeof(uint64_t) * 8)] & ((uint64_t)1 << (c % (sizeof(uint64_t) * 8)))) != 0;
414 return found ^ scanset->invert;
415}
416
418{
419 _scanset_t scanset = {0};
420
421 if (*ctx->p == '^')
422 {
423 scanset.invert = true;
424 ctx->p++;
425 }
426
427 if (*ctx->p == ']')
428 {
429 _scanset_set(&scanset, *ctx->p);
430 ctx->p++;
431 }
432
433 while (*ctx->p != '\0' && *ctx->p != ']')
434 {
435 if (ctx->p[1] == '-' && ctx->p[2] != ']' && ctx->p[2] != '\0')
436 {
437 unsigned char start = (unsigned char)*ctx->p;
438 unsigned char end = (unsigned char)ctx->p[2];
439
440 if (start <= end)
441 {
442 for (int i = start; i <= end; i++)
443 {
444 _scanset_set(&scanset, (uint8_t)i);
445 }
446 }
447 else
448 {
449 _scanset_set(&scanset, start);
450 _scanset_set(&scanset, '-');
451 _scanset_set(&scanset, end);
452 }
453
454 ctx->p += 3;
455 }
456 else
457 {
458 _scanset_set(&scanset, *ctx->p);
459 ctx->p++;
460 }
461 }
462
463 if (*ctx->p == ']')
464 {
465 ctx->p++;
466 }
467
468 char* buffer = NULL;
469 if (!(format->flags & _SCAN_SUPPRESS_ASSIGNMENT))
470 {
471 /// @todo Wide chars
472 buffer = va_arg(ctx->arg, char*);
473 }
474
475 uint64_t count = 0;
476 while (count < format->width)
477 {
478 int c = _scan_next(ctx);
479 if (c == EOF)
480 {
481 return EOF;
482 }
483
484 if (_scanset_get(&scanset, (uint8_t)c))
485 {
486 if (buffer != NULL)
487 {
488 buffer[count] = (char)c;
489 }
490
491 count++;
492 }
493 else
494 {
495 _scan_undo(ctx, c);
496 break;
497 }
498 }
499
500 if (count == 0)
501 {
502 return EOF;
503 }
504
505 if (buffer != NULL)
506 {
507 buffer[count] = '\0';
508 ctx->parsedItems++;
509 }
510
511 return 0;
512}
513
515{
517 {
518 return 0;
519 }
520
521 return _scan_asign_signed_int(ctx, format, ctx->count);
522}
523
525{
526 (void)format;
527
528 int c = _scan_next(ctx);
529 if (c == EOF)
530 {
531 return EOF;
532 }
533
534 if (c != '%')
535 {
536 _scan_undo(ctx, c);
537 return EOF;
538 }
539
540 return 0;
541}
542
543static inline int _scan_format(_scan_ctx_t* ctx)
544{
545 // %[*][width][length]specifier
546
548 .flags = 0,
549 .width = 0,
550 .length = _SCAN_DEFAULT,
551 };
552
553 if (*ctx->p == '*')
554 {
556 ctx->p++;
557 }
558
559 while (isdigit(*ctx->p))
560 {
561 format.width = format.width * 10 + (*ctx->p - '0');
562 ctx->p++;
563 }
564
565 if (format.width == 0)
566 {
567 format.width = UINT64_MAX;
568 }
569
570 switch (*ctx->p)
571 {
572 case 'h':
573 ctx->p++;
574 if (*ctx->p == 'h')
575 {
576 format.length = _SCAN_HH;
577 ctx->p++;
578 }
579 else
580 {
581 format.length = _SCAN_H;
582 }
583 break;
584 case 'l':
585 ctx->p++;
586 if (*ctx->p == 'l')
587 {
588 format.length = _SCAN_LL;
589 ctx->p++;
590 }
591 else
592 {
593 format.length = _SCAN_L;
594 }
595 break;
596 case 'j':
597 format.length = _SCAN_J;
598 ctx->p++;
599 break;
600 case 'z':
601 format.length = _SCAN_Z;
602 ctx->p++;
603 break;
604 case 't':
605 format.length = _SCAN_T;
606 ctx->p++;
607 break;
608 default:
609 break;
610 }
611
612 char specifier = *ctx->p;
613 ctx->p++;
614
615 int ret = 0;
616 switch (specifier)
617 {
618 case 'i':
620 break;
621 case 'd':
623 break;
624 case 'u':
626 break;
627 case 'o':
629 break;
630 case 'x':
632 break;
633 case 'f':
634 case 'e':
635 case 'g':
636 case 'a':
637 ret = _scan_format_float(ctx, &format);
638 break;
639 case 'c':
640 ret = _scan_format_char(ctx, &format);
641 break;
642 case 's':
643 ret = _scan_format_string(ctx, &format);
644 break;
645 case 'p':
646 format.length = _SCAN_Z;
648 break;
649 case '[':
650 ret = _scan_format_scanset(ctx, &format);
651 break;
652 case 'n':
653 ret = _scan_format_count(ctx, &format);
654 break;
655 case '%':
656 ret = _scan_format_percent(ctx, &format);
657 break;
658 default:
659 ret = EOF;
660 break;
661 }
662
663 return ret;
664}
665
666static inline int _scan(const char* _RESTRICT format, va_list arg, void* data)
667{
668 assert(format != NULL);
669
670 _scan_ctx_t ctx = {
671 .parsedItems = 0,
672 .p = format,
673 .data = data,
674 .count = 0,
675 .prev = EOF,
676 };
677 va_copy(ctx.arg, arg);
678
679 while (*ctx.p != '\0')
680 {
681 if (isspace(*ctx.p))
682 {
683 ctx.p++;
684 if (_scan_whitespace(&ctx) == EOF)
685 {
686 break;
687 }
688
689 continue;
690 }
691
692 if (*ctx.p == '%')
693 {
694 ctx.p++;
695 if (_scan_format(&ctx) == EOF)
696 {
697 break;
698 }
699
700 continue;
701 }
702
703 int c = _scan_next(&ctx);
704 if (c == EOF)
705 {
706 break;
707 }
708
709 if (c != *ctx.p)
710 {
711 _scan_undo(&ctx, c);
712 break;
713 }
714
715 ctx.p++;
716 }
717
718 va_end(ctx.arg);
719
720 if (ctx.prev != EOF)
721 {
722 _SCAN_UNGET(&ctx, ctx.prev);
723 ctx.prev = EOF;
724 }
725
726 if (ctx.count == 0)
727 {
728 return EOF;
729 }
730
731 return ctx.parsedItems;
732}
733
734/** @} */
#define _RESTRICT
Definition config.h:17
#define assert(expression)
Definition assert.h:29
EFI_PHYSICAL_ADDRESS buffer
Definition main.c:237
static void start()
Definition main.c:542
static char format[MAX_NAME]
Definition screen.c:17
static uint64_t width
Definition screen.c:13
#define isspace(c)
Definition ctype.h:30
#define isdigit(c)
Definition ctype.h:20
static uint8_t _digit_to_int(char c)
Definition digits.h:14
static fd_t data
Definition dwm.c:21
static int _scan(const char *_RESTRICT format, va_list arg, void *data)
Definition scan.h:666
static int _scan_format_count(_scan_ctx_t *ctx, _scan_format_ctx_t *format)
Definition scan.h:514
static int _scan_whitespace(_scan_ctx_t *ctx)
Definition scan.h:82
static int _scan_format(_scan_ctx_t *ctx)
Definition scan.h:543
static int _scan_format_char(_scan_ctx_t *ctx, _scan_format_ctx_t *format)
Definition scan.h:322
_scan_format_flags_t
Definition scan.h:109
static void _scanset_set(_scanset_t *scanset, uint8_t c)
Definition scan.h:406
static bool _scanset_get(_scanset_t *scanset, uint8_t c)
Definition scan.h:411
static int _scan_format_integer(_scan_ctx_t *ctx, _scan_format_ctx_t *format, uint32_t base, _scan_format_integer_flags_t flags)
Definition scan.h:208
static void _scan_undo(_scan_ctx_t *ctx, int c)
Definition scan.h:71
static int _scan_asign_unsigned_int(_scan_ctx_t *ctx, _scan_format_ctx_t *format, uint64_t value)
Definition scan.h:167
_scan_format_integer_flags_t
Definition scan.h:203
static int _scan_format_string(_scan_ctx_t *ctx, _scan_format_ctx_t *format)
Definition scan.h:357
static int _scan_format_scanset(_scan_ctx_t *ctx, _scan_format_ctx_t *format)
Definition scan.h:417
static int _scan_format_float(_scan_ctx_t *ctx, _scan_format_ctx_t *format)
Definition scan.h:314
static int _scan_next(_scan_ctx_t *ctx)
Definition scan.h:53
static int _scan_format_percent(_scan_ctx_t *ctx, _scan_format_ctx_t *format)
Definition scan.h:524
static int _scan_asign_signed_int(_scan_ctx_t *ctx, _scan_format_ctx_t *format, int64_t value)
Definition scan.h:132
_scan_format_length_t
Definition scan.h:114
@ _SCAN_SUPPRESS_ASSIGNMENT
Definition scan.h:110
@ _SCAN_INTEGER_SIGNED
Definition scan.h:205
@ _SCAN_INTEGER_UNSIGNED
Definition scan.h:204
@ _SCAN_T
Definition scan.h:122
@ _SCAN_Z
Definition scan.h:121
@ _SCAN_LL
Definition scan.h:119
@ _SCAN_H
Definition scan.h:117
@ _SCAN_HH
Definition scan.h:116
@ _SCAN_J
Definition scan.h:120
@ _SCAN_DEFAULT
Definition scan.h:115
@ _SCAN_L
Definition scan.h:118
#define NULL
Pointer error value.
Definition NULL.h:25
__UINTMAX_TYPE__ uintmax_t
Definition inttypes.h:12
__INTMAX_TYPE__ intmax_t
Definition inttypes.h:11
static const path_flag_t flags[]
Definition path.c:47
static atomic_long next
Definition main.c:12
static atomic_long count
Definition main.c:11
__PTRDIFF_TYPE__ ptrdiff_t
Definition ptrdiff_t.h:4
__SIZE_TYPE__ size_t
Definition size_t.h:4
#define va_copy(dest, src)
Definition stdarg.h:14
#define va_arg(ap, type)
Definition stdarg.h:13
#define va_end(ap)
Definition stdarg.h:15
__builtin_va_list va_list
Definition stdarg.h:11
__UINT32_TYPE__ uint32_t
Definition stdint.h:15
__UINT64_TYPE__ uint64_t
Definition stdint.h:17
#define UINT64_MAX
Definition stdint.h:74
__UINT8_TYPE__ uint8_t
Definition stdint.h:11
__INT64_TYPE__ int64_t
Definition stdint.h:16
#define UINT8_MAX
Definition stdint.h:62
#define EOF
Definition stdio.h:25
const char * p
Definition scan.h:46
char prev
Definition scan.h:50
uint64_t count
Definition scan.h:49
void * data
Definition scan.h:48
int parsedItems
Definition scan.h:45
va_list arg
Definition scan.h:47
_scan_format_length_t length
Definition scan.h:129
uint64_t width
Definition scan.h:128
_scan_format_flags_t flags
Definition scan.h:127
uint64_t table[UINT8_MAX/(sizeof(uint64_t) *8)+1]
Definition scan.h:402
bool invert
Definition scan.h:403
#define _SCAN_GET(ctx)
Definition vsscanf.c:5
#define _SCAN_UNGET(ctx, c)
Definition vsscanf.c:16