source: mainline/uspace/lib/c/generic/stdio/scanf.c@ f4aa1c8

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since f4aa1c8 was 39f84ce4, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Add range support to scanf's set conversion.

  • Property mode set to 100644
File size: 24.3 KB
Line 
1/*
2 * Copyright (c) 2018 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup libc
30 * @{
31 */
32/**
33 * @file
34 * @brief Formatted input (scanf family)
35 */
36
37#include <assert.h>
38#include <_bits/ssize_t.h>
39#include <ctype.h>
40#include <errno.h>
41#include <limits.h>
42#include <stdarg.h>
43#include <stdbool.h>
44#include <stddef.h>
45#include <stdint.h>
46#include <stdio.h>
47#include <stdlib.h>
48
49typedef enum {
50 /** No length modifier */
51 lm_none,
52 /** 'hh' */
53 lm_hh,
54 /** 'h' */
55 lm_h,
56 /** 'l' */
57 lm_l,
58 /** 'll' */
59 lm_ll,
60 /** 'j' */
61 lm_j,
62 /** 'z' */
63 lm_z,
64 /** 't' */
65 lm_t,
66 /** 'L' */
67 lm_L
68} lenmod_t;
69
70typedef enum {
71 /** Unknown */
72 cs_unknown,
73 /** 'd' */
74 cs_decimal,
75 /** 'i' */
76 cs_int,
77 /** 'o' */
78 cs_octal,
79 /** 'u' */
80 cs_udecimal,
81 /** 'x', 'X' */
82 cs_hex,
83 /** 'a', 'e', 'f', 'g', 'A', 'E', 'F', 'G' */
84 cs_float,
85 /** 'c' */
86 cs_char,
87 /** 's' */
88 cs_str,
89 /* [...] */
90 cs_set,
91 /* 'p' */
92 cs_ptr,
93 /* 'n' */
94 cs_numchar,
95 /* '%' */
96 cs_percent
97} cvtspcr_t;
98
99/** Conversion specification */
100typedef struct {
101 /** Suppress assignment */
102 bool noassign;
103 /** Allocate memory for string (GNU extension) */
104 bool memalloc;
105 /** @c true if width field is valid */
106 bool have_width;
107 /** Width */
108 size_t width;
109 /** Length modifier */
110 lenmod_t lenmod;
111 /** Conversion specifier */
112 cvtspcr_t spcr;
113 /** Scan set (if spc == cs_set) */
114 const char *scanset;
115} cvtspec_t;
116
117/** Buffer for writing strings */
118typedef struct {
119 /** Buffer */
120 char *buf;
121 /** Place where to store pointer to the buffer */
122 char **pptr;
123 /** Allocating memory for caller */
124 bool memalloc;
125 /** Current size of allocated buffer */
126 size_t size;
127} strbuf_t;
128
129static int digit_value(char digit)
130{
131 switch (digit) {
132 case '0':
133 return 0;
134 case '1':
135 return 1;
136 case '2':
137 return 2;
138 case '3':
139 return 3;
140 case '4':
141 return 4;
142 case '5':
143 return 5;
144 case '6':
145 return 6;
146 case '7':
147 return 7;
148 case '8':
149 return 8;
150 case '9':
151 return 9;
152 case 'a':
153 case 'A':
154 return 10;
155 case 'b':
156 case 'B':
157 return 11;
158 case 'c':
159 case 'C':
160 return 12;
161 case 'd':
162 case 'D':
163 return 13;
164 case 'e':
165 case 'E':
166 return 14;
167 case 'f':
168 case 'F':
169 return 15;
170 default:
171 assert(false);
172 }
173}
174
175static void cvtspec_parse(const char **fmt, cvtspec_t *spec)
176{
177 spec->scanset = NULL;
178 spec->width = 0;
179
180 /* Assignment suppresion */
181
182 if (**fmt == '*') {
183 spec->noassign = true;
184 ++(*fmt);
185 } else {
186 spec->noassign = false;
187 }
188
189 /* Memory allocation */
190
191 if (**fmt == 'm') {
192 spec->memalloc = true;
193 ++(*fmt);
194 } else {
195 spec->memalloc = false;
196 }
197
198 /* Width specifier */
199
200 if (isdigit(**fmt)) {
201 spec->have_width = true;
202 assert(**fmt != '0');
203 spec->width = 0;
204 while (isdigit(**fmt)) {
205 spec->width *= 10;
206 spec->width += digit_value(**fmt);
207 ++(*fmt);
208 }
209 } else {
210 spec->have_width = false;
211 }
212
213 /* Length modifier */
214
215 switch (**fmt) {
216 case 'h':
217 ++(*fmt);
218 if (**fmt == 'h') {
219 spec->lenmod = lm_hh;
220 ++(*fmt);
221 } else {
222 spec->lenmod = lm_h;
223 }
224 break;
225 case 'l':
226 ++(*fmt);
227 if (**fmt == 'l') {
228 spec->lenmod = lm_ll;
229 ++(*fmt);
230 } else {
231 spec->lenmod = lm_l;
232 }
233 break;
234 case 'j':
235 ++(*fmt);
236 spec->lenmod = lm_j;
237 break;
238 case 'z':
239 ++(*fmt);
240 spec->lenmod = lm_z;
241 break;
242 case 't':
243 ++(*fmt);
244 spec->lenmod = lm_t;
245 break;
246 case 'L':
247 ++(*fmt);
248 spec->lenmod = lm_L;
249 break;
250 default:
251 spec->lenmod = lm_none;
252 break;
253 }
254
255 /* Conversion specifier */
256
257 switch (**fmt) {
258 case 'd':
259 ++(*fmt);
260 spec->spcr = cs_decimal;
261 break;
262 case 'i':
263 ++(*fmt);
264 spec->spcr = cs_int;
265 break;
266 case 'o':
267 ++(*fmt);
268 spec->spcr = cs_octal;
269 break;
270 case 'u':
271 ++(*fmt);
272 spec->spcr = cs_udecimal;
273 break;
274 case 'x':
275 case 'X':
276 ++(*fmt);
277 spec->spcr = cs_hex;
278 break;
279 case 'a':
280 case 'e':
281 case 'f':
282 case 'g':
283 case 'A':
284 case 'E':
285 case 'F':
286 case 'G':
287 ++(*fmt);
288 spec->spcr = cs_float;
289 break;
290 case 'c':
291 ++(*fmt);
292 spec->spcr = cs_char;
293 break;
294 case 's':
295 ++(*fmt);
296 spec->spcr = cs_str;
297 break;
298 case '[':
299 ++(*fmt);
300 spec->spcr = cs_set;
301 spec->scanset = *fmt;
302 while (**fmt != ']' && **fmt != '\0')
303 ++(*fmt);
304 if (**fmt == ']')
305 ++(*fmt);
306 break;
307 case 'p':
308 ++(*fmt);
309 spec->spcr = cs_ptr;
310 break;
311 case 'n':
312 ++(*fmt);
313 spec->spcr = cs_numchar;
314 break;
315 case '%':
316 ++(*fmt);
317 spec->spcr = cs_percent;
318 break;
319 default:
320 assert(false);
321 spec->spcr = cs_unknown;
322 break;
323 }
324}
325
326/** Initialize string buffer.
327 *
328 * String buffer is used to write characters from a string conversion.
329 * The buffer can be provided by caller or dynamically allocated
330 * (and grown).
331 *
332 * Initialize the string buffer @a strbuf. If @a spec->noassign,is true,
333 * set the buffer pointer to NULL. Otherwise, if @a spec->memalloc is true,
334 * allocate a buffer and read an argument of type char ** designating
335 * a place where the pointer should be stored. If @a spec->memalloc is false,
336 * read an argument of type char * and use it as a destination to write
337 * the characters to.
338 *
339 * @param strbuf String buffer to initialize
340 * @param cvtspec Conversion specification (noassign, memalloc)
341 * @param ap Argument list to read pointer from
342 *
343 * @return EOK on success, ENOMEM if out of memory
344 */
345static int strbuf_init(strbuf_t *strbuf, cvtspec_t *spec, va_list ap)
346{
347 if (spec->noassign) {
348 strbuf->memalloc = false;
349 strbuf->buf = NULL;
350 strbuf->pptr = NULL;
351 strbuf->size = 0;
352 return EOK;
353 }
354
355 if (spec->memalloc) {
356 /* Allocate memory for caller */
357 strbuf->memalloc = true;
358 strbuf->size = 1;
359 strbuf->buf = malloc(strbuf->size);
360 if (strbuf->buf == NULL)
361 return ENOMEM;
362
363 /*
364 * Save pointer to allocated buffer to caller-provided
365 * location
366 */
367 strbuf->pptr = va_arg(ap, char **);
368 *strbuf->pptr = strbuf->buf;
369 } else {
370 /* Caller-provided buffer */
371 strbuf->memalloc = false;
372 strbuf->size = 0;
373 strbuf->buf = va_arg(ap, char *);
374 strbuf->pptr = NULL;
375 }
376
377 return EOK;
378}
379
380/** Write character at the specified position in a string buffer.
381 *
382 * The buffer is enlarged if necessary.
383 *
384 * @param strbuf String buffer
385 * @param idx Character position (starting from 0)
386 * @param c Character to write
387 *
388 * @return EOK on sucess, ENOMEM if out of memory
389 */
390static int strbuf_write(strbuf_t *strbuf, size_t idx, char c)
391{
392 if (strbuf->memalloc && idx >= strbuf->size) {
393 /* Enlarge buffer */
394 strbuf->size = strbuf->size * 2;
395 strbuf->buf = realloc(strbuf->buf, strbuf->size);
396 *strbuf->pptr = strbuf->buf;
397 if (strbuf->buf == NULL)
398 return ENOMEM;
399 }
400
401 if (strbuf->buf != NULL)
402 strbuf->buf[idx] = c;
403
404 return EOK;
405}
406
407/** Get character from stream, keeping count of number of characters read.
408 *
409 * @param f Stream
410 * @param numchar Pointer to counter of characters read
411 * @return Character on success, EOF on error
412 */
413static int __fgetc(FILE *f, int *numchar)
414{
415 int c;
416
417 c = fgetc(f);
418 if (c == EOF)
419 return EOF;
420
421 ++(*numchar);
422 return c;
423}
424
425/** Unget character to stream, keeping count of number of characters read.
426 *
427 * @param c Character
428 * @param f Stream
429 * @param numchar Pointer to counter of characters read
430 *
431 * @return @a c on success, EOF on failure
432 */
433static int __ungetc(int c, FILE *f, int *numchar)
434{
435 int rc;
436
437 rc = ungetc(c, f);
438 if (rc == EOF)
439 return EOF;
440
441 --(*numchar);
442 return rc;
443}
444
445/* Skip whitespace in input stream */
446static int vfscanf_skip_ws(FILE *f, int *numchar)
447{
448 int c;
449
450 c = __fgetc(f, numchar);
451 if (c == EOF)
452 return EIO;
453
454 while (isspace(c)) {
455 c = __fgetc(f, numchar);
456 if (c == EOF)
457 return EIO;
458 }
459
460 if (c == EOF)
461 return EIO;
462
463 __ungetc(c, f, numchar);
464 return EOK;
465}
466
467/* Match whitespace. */
468static int vfscanf_match_ws(FILE *f, int *numchar, const char **fmt)
469{
470 int rc;
471
472 rc = vfscanf_skip_ws(f, numchar);
473 if (rc == EOF)
474 return EIO;
475
476 ++(*fmt);
477 return EOK;
478}
479
480/** Read intmax_t integer from file.
481 *
482 * @param f Input file
483 * @param numchar Pointer to counter of characters read
484 * @param base Numeric base (0 means detect using prefix)
485 * @param width Maximum field with in characters
486 * @param dest Place to store result
487 * @return EOK on success, EIO on I/O error, EINVAL if input is not valid
488 */
489static int __fstrtoimax(FILE *f, int *numchar, int base, size_t width,
490 intmax_t *dest)
491{
492 int c;
493 intmax_t v;
494 int digit;
495 int sign;
496
497 c = __fgetc(f, numchar);
498 if (c == EOF)
499 return EIO;
500
501 if (c == '+' || c == '-') {
502 /* Sign */
503 sign = (c == '-') ? -1 : +1;
504 c = __fgetc(f, numchar);
505 if (c == EOF)
506 return EIO;
507 --width;
508 } else {
509 sign = 1;
510 }
511
512 if (!isdigit(c) || width < 1) {
513 __ungetc(c, f, numchar);
514 return EINVAL;
515 }
516
517 if (base == 0) {
518 /* Base prefix */
519 if (c == '0') {
520 c = __fgetc(f, numchar);
521 if (c == EOF)
522 return EIO;
523 --width;
524
525 if (width > 0 && (c == 'x' || c == 'X')) {
526 --width;
527 c = __fgetc(f, numchar);
528 if (c == EOF)
529 return EIO;
530 if (width > 0 && isxdigit(c)) {
531 base = 16;
532 } else {
533 *dest = 0;
534 return EOK;
535 }
536 } else {
537 base = 8;
538 if (width == 0) {
539 *dest = 0;
540 return EOK;
541 }
542 }
543 } else {
544 base = 10;
545 }
546 }
547
548 /* Value */
549 v = 0;
550 do {
551 digit = digit_value(c);
552 if (digit >= base)
553 break;
554
555 v = v * base + digit;
556 c = __fgetc(f, numchar);
557 --width;
558 } while (width > 0 && isxdigit(c));
559
560 if (c != EOF)
561 __ungetc(c, f, numchar);
562
563 *dest = sign * v;
564 return EOK;
565}
566
567/** Read uintmax_t unsigned integer from file.
568 *
569 * @param f Input file
570 * @param numchar Pointer to counter of characters read
571 * @param base Numeric base (0 means detect using prefix)
572 * @param width Maximum field with in characters
573 * @param dest Place to store result
574 * @return EOK on success, EIO on I/O error, EINVAL if input is not valid
575 */
576static int __fstrtoumax(FILE *f, int *numchar, int base, size_t width,
577 uintmax_t *dest)
578{
579 int c;
580 uintmax_t v;
581 int digit;
582
583 c = __fgetc(f, numchar);
584 if (c == EOF)
585 return EIO;
586
587 if (!isdigit(c) || width < 1) {
588 __ungetc(c, f, numchar);
589 return EINVAL;
590 }
591
592 if (base == 0) {
593 /* Base prefix */
594 if (c == '0') {
595 c = __fgetc(f, numchar);
596 if (c == EOF)
597 return EIO;
598 --width;
599
600 if (width > 0 && (c == 'x' || c == 'X')) {
601 --width;
602 c = __fgetc(f, numchar);
603 if (c == EOF)
604 return EIO;
605 if (width > 0 && isxdigit(c)) {
606 base = 16;
607 } else {
608 *dest = 0;
609 return EOK;
610 }
611 } else {
612 base = 8;
613 if (width == 0) {
614 *dest = 0;
615 return EOK;
616 }
617 }
618 } else {
619 base = 10;
620 }
621 }
622
623 /* Value */
624 v = 0;
625 do {
626 digit = digit_value(c);
627 if (digit >= base)
628 break;
629
630 v = v * base + digit;
631 c = __fgetc(f, numchar);
632 --width;
633 } while (width > 0 && isxdigit(c));
634
635 if (c != EOF)
636 __ungetc(c, f, numchar);
637
638 *dest = v;
639 return EOK;
640}
641
642/** Read long double from file.
643 *
644 * @param f Input file
645 * @param numchar Pointer to counter of characters read
646 * @param width Maximum field with in characters
647 * @param dest Place to store result
648 * @return EOK on success, EIO on I/O error, EINVAL if input is not valid
649 */
650static int __fstrtold(FILE *f, int *numchar, size_t width, long double *dest)
651{
652 int c;
653 long double v;
654 int digit;
655 int sign;
656 int base;
657 int efactor;
658 int eadd;
659 int eadj;
660 int exp;
661 int expsign;
662
663 c = __fgetc(f, numchar);
664 if (c == EOF)
665 return EIO;
666
667 if (c == '+' || c == '-') {
668 /* Sign */
669 sign = (c == '-') ? -1 : +1;
670 c = __fgetc(f, numchar);
671 if (c == EOF)
672 return EIO;
673 --width;
674 } else {
675 sign = 1;
676 }
677
678 if (!isdigit(c) || width < 1) {
679 __ungetc(c, f, numchar);
680 return EINVAL;
681 }
682
683 /*
684 * Default is base 10
685 */
686
687 /* Significand is in base 10 */
688 base = 10;
689 /* e+1 multiplies number by ten */
690 efactor = 10;
691 /* Adjust exp. by one for each fractional digit */
692 eadd = 1;
693
694 /* Base prefix */
695 if (c == '0') {
696 c = __fgetc(f, numchar);
697 if (c == EOF)
698 return EIO;
699 --width;
700
701 if (width > 0 && (c == 'x' || c == 'X')) {
702 --width;
703 c = __fgetc(f, numchar);
704
705 if (width > 0 && isxdigit(c)) {
706 /* Significand is in base 16 */
707 base = 16;
708 /* p+1 multiplies number by two */
709 efactor = 2;
710 /*
711 * Adjust exponent by 4 for each
712 * fractional digit
713 */
714 eadd = 4;
715 } else {
716 *dest = 0;
717 return EOK;
718 }
719 }
720 }
721
722 /* Value */
723 v = 0;
724 do {
725 digit = digit_value(c);
726 if (digit >= base)
727 break;
728
729 v = v * base + digit;
730 c = __fgetc(f, numchar);
731 --width;
732 } while (width > 0 && isxdigit(c));
733
734 /* Decimal-point */
735 eadj = 0;
736
737 if (c == '.' && width > 1) {
738 c = __fgetc(f, numchar);
739 if (c == EOF)
740 return EIO;
741
742 --width;
743
744 /* Fractional part */
745 while (width > 0 && isxdigit(c)) {
746 digit = digit_value(c);
747 if (digit >= base)
748 break;
749
750 v = v * base + digit;
751 c = __fgetc(f, numchar);
752 --width;
753 eadj -= eadd;
754 }
755 }
756
757 exp = 0;
758
759 /* Exponent */
760 if ((width > 1 && base == 10 && (c == 'e' || c == 'E')) ||
761 (width > 1 && base == 16 && (c == 'p' || c == 'P'))) {
762 c = __fgetc(f, numchar);
763 if (c == EOF)
764 return EIO;
765
766 --width;
767
768 if (width > 1 && (c == '+' || c == '-')) {
769 /* Exponent sign */
770 if (c == '+') {
771 expsign = 1;
772 } else {
773 expsign = -1;
774 }
775
776 c = __fgetc(f, numchar);
777 if (c == EOF)
778 return EIO;
779
780 --width;
781 } else {
782 expsign = 1;
783 }
784
785 while (width > 0 && isdigit(c)) {
786 digit = digit_value(c);
787 if (digit >= 10)
788 break;
789
790 exp = exp * 10 + digit;
791 c = __fgetc(f, numchar);
792 --width;
793 }
794
795 exp = exp * expsign;
796 }
797
798 exp += eadj;
799
800 /* Adjust v for value of exponent */
801
802 while (exp > 0) {
803 v = v * efactor;
804 --exp;
805 }
806
807 while (exp < 0) {
808 v = v / efactor;
809 ++exp;
810 }
811
812 if (c != EOF)
813 __ungetc(c, f, numchar);
814
815 *dest = sign * v;
816 return EOK;
817}
818
819/* Read characters from stream */
820static int __fgetchars(FILE *f, int *numchar, size_t width, strbuf_t *strbuf,
821 size_t *nread)
822{
823 size_t cnt;
824 int c;
825 int rc;
826
827 for (cnt = 0; cnt < width; cnt++) {
828 c = __fgetc(f, numchar);
829 if (c == EOF) {
830 *nread = cnt;
831 return EIO;
832 }
833
834 rc = strbuf_write(strbuf, cnt, c);
835 if (rc != EOK) {
836 *nread = cnt;
837 return rc;
838 }
839 }
840
841 *nread = cnt;
842 return EOK;
843}
844
845/* Read non-whitespace string from stream */
846static int __fgetstr(FILE *f, int *numchar, size_t width, strbuf_t *strbuf,
847 size_t *nread)
848{
849 size_t cnt;
850 int c;
851 int rc;
852 int rc2;
853
854 rc = EOK;
855
856 for (cnt = 0; cnt < width; cnt++) {
857 c = __fgetc(f, numchar);
858 if (c == EOF) {
859 rc = EIO;
860 break;
861 }
862
863 if (isspace(c)) {
864 __ungetc(c, f, numchar);
865 break;
866 }
867
868 rc = strbuf_write(strbuf, cnt, c);
869 if (rc != EOK) {
870 *nread = cnt;
871 return rc;
872 }
873 }
874
875 /* Null-terminate */
876 rc2 = strbuf_write(strbuf, cnt, '\0');
877 if (rc2 != EOK) {
878 *nread = cnt;
879 return rc2;
880 }
881
882 *nread = cnt;
883 return rc;
884}
885
886/** Determine if character is in scanset.
887 *
888 * Note that we support ranges, although that is a GNU extension.
889 *
890 * @param c Character
891 * @param scanset Pointer to scanset
892 * @return @c true iff @a c is in scanset @a scanset.
893 */
894static bool is_in_scanset(char c, const char *scanset)
895{
896 const char *p = scanset;
897 bool inverted = false;
898 char startc;
899 char endc;
900
901 /* Inverted scanset */
902 if (*p == '^') {
903 inverted = true;
904 ++p;
905 }
906
907 /*
908 * Either ']' or '-' at beginning or after '^' loses special meaning.
909 * However, '-' after ']' (or vice versa) does not.
910 */
911 if (*p == ']') {
912 /* ']' character in scanset */
913 if (c == ']')
914 return !inverted;
915 ++p;
916 } else if (*p == '-') {
917 /* '-' character in scanset */
918 if (c == '-')
919 return !inverted;
920 ++p;
921 }
922
923 /* Remaining characters */
924 while (*p != '\0' && *p != ']') {
925 /* '-' is a range unless it's the last character in scanset */
926 if (*p == '-' && p[1] != ']' && p[1] != '\0') {
927 startc = p[-1];
928 endc = p[1];
929
930 if (c >= startc && c <= endc)
931 return !inverted;
932
933 p += 2;
934 continue;
935 }
936
937 if (*p == c)
938 return !inverted;
939 ++p;
940 }
941
942 return inverted;
943}
944
945/* Read string of characters from scanset from stream */
946static int __fgetscanstr(FILE *f, int *numchar, size_t width,
947 const char *scanset, strbuf_t *strbuf, size_t *nread)
948{
949 size_t cnt;
950 int c;
951 int rc;
952 int rc2;
953
954 rc = EOK;
955
956 for (cnt = 0; cnt < width; cnt++) {
957 c = __fgetc(f, numchar);
958 if (c == EOF) {
959 rc = EIO;
960 break;
961 }
962
963 if (!is_in_scanset(c, scanset)) {
964 __ungetc(c, f, numchar);
965 break;
966 }
967
968 rc = strbuf_write(strbuf, cnt, c);
969 if (rc != EOK) {
970 *nread = cnt;
971 break;
972 }
973 }
974
975 /* Null-terminate */
976 rc2 = strbuf_write(strbuf, cnt, '\0');
977 if (rc2 != EOK) {
978 *nread = cnt;
979 return rc2;
980 }
981
982 *nread = cnt;
983 return rc;
984}
985
986/** Perform a single conversion. */
987static int vfscanf_cvt(FILE *f, const char **fmt, va_list ap, int *numchar,
988 unsigned *ncvt)
989{
990 int rc;
991 int c;
992 intmax_t ival;
993 uintmax_t uval;
994 long double fval = 0.0;
995 int *iptr;
996 short *sptr;
997 signed char *scptr;
998 long *lptr;
999 long long *llptr;
1000 ssize_t *ssptr;
1001 ptrdiff_t *pdptr;
1002 intmax_t *imptr;
1003 unsigned *uptr;
1004 unsigned short *usptr;
1005 unsigned char *ucptr;
1006 unsigned long *ulptr;
1007 unsigned long long *ullptr;
1008 float *fptr;
1009 double *dptr;
1010 long double *ldptr;
1011 size_t *szptr;
1012 ptrdiff_t *updptr;
1013 uintmax_t *umptr;
1014 void **pptr;
1015 size_t width;
1016 cvtspec_t cvtspec;
1017 strbuf_t strbuf;
1018 size_t nread;
1019
1020 ++(*fmt);
1021
1022 cvtspec_parse(fmt, &cvtspec);
1023
1024 if (cvtspec.spcr != cs_set && cvtspec.spcr != cs_char &&
1025 cvtspec.spcr != cs_numchar) {
1026 /* Skip whitespace */
1027 rc = vfscanf_skip_ws(f, numchar);
1028 if (rc == EIO)
1029 return EIO;
1030
1031 assert(rc == EOK);
1032 }
1033
1034 width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
1035
1036 switch (cvtspec.spcr) {
1037 case cs_percent:
1038 /* Match % character */
1039 c = __fgetc(f, numchar);
1040 if (c == EOF)
1041 return EIO;
1042 if (c != '%') {
1043 __ungetc(c, f, numchar);
1044 return EINVAL;
1045 }
1046 break;
1047 case cs_decimal:
1048 /* Decimal integer */
1049 rc = __fstrtoimax(f, numchar, 10, width, &ival);
1050 if (rc != EOK)
1051 return rc;
1052 break;
1053 case cs_udecimal:
1054 /* Decimal unsigned integer */
1055 rc = __fstrtoumax(f, numchar, 10, width, &uval);
1056 if (rc != EOK)
1057 return rc;
1058 break;
1059 case cs_octal:
1060 /* Octal unsigned integer */
1061 rc = __fstrtoumax(f, numchar, 8, width, &uval);
1062 if (rc != EOK)
1063 return rc;
1064 break;
1065 case cs_hex:
1066 /* Hexadecimal unsigned integer */
1067 rc = __fstrtoumax(f, numchar, 16, width, &uval);
1068 if (rc != EOK)
1069 return rc;
1070 break;
1071 case cs_float:
1072 /* Floating-point value */
1073 rc = __fstrtold(f, numchar, width, &fval);
1074 if (rc != EOK)
1075 return rc;
1076 break;
1077 case cs_int:
1078 /* Signed integer with base detection */
1079 rc = __fstrtoimax(f, numchar, 0, width, &ival);
1080 if (rc != EOK)
1081 return rc;
1082 break;
1083 case cs_ptr:
1084 /* Unsigned integer with base detection (need 0xXXXXX) */
1085 rc = __fstrtoumax(f, numchar, 0, width, &uval);
1086 if (rc != EOK)
1087 return rc;
1088 break;
1089 case cs_char:
1090 /* Characters */
1091 rc = strbuf_init(&strbuf, &cvtspec, ap);
1092 if (rc != EOK)
1093 return rc;
1094
1095 width = cvtspec.have_width ? cvtspec.width : 1;
1096 rc = __fgetchars(f, numchar, width, &strbuf, &nread);
1097 if (rc != EOK) {
1098 if (rc == ENOMEM)
1099 return EIO;
1100
1101 assert(rc == EIO);
1102 /* If we have at least one char, we succeeded */
1103 if (nread > 0)
1104 ++(*ncvt);
1105 return EIO;
1106 }
1107 break;
1108 case cs_str:
1109 /* Non-whitespace string */
1110 rc = strbuf_init(&strbuf, &cvtspec, ap);
1111 if (rc != EOK)
1112 return rc;
1113
1114 width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
1115 rc = __fgetstr(f, numchar, width, &strbuf, &nread);
1116 if (rc != EOK) {
1117 if (rc == ENOMEM)
1118 return EIO;
1119
1120 assert(rc == EIO);
1121 /* If we have at least one char, we succeeded */
1122 if (nread > 0)
1123 ++(*ncvt);
1124 return EIO;
1125 }
1126 break;
1127 case cs_set:
1128 /* String of characters from scanset */
1129 rc = strbuf_init(&strbuf, &cvtspec, ap);
1130 if (rc != EOK)
1131 return rc;
1132
1133 width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
1134 rc = __fgetscanstr(f, numchar, width, cvtspec.scanset,
1135 &strbuf, &nread);
1136 if (rc != EOK) {
1137 if (rc == ENOMEM)
1138 return EIO;
1139
1140 assert(rc == EIO);
1141 /* If we have at least one char, we succeeded */
1142 if (nread > 0)
1143 ++(*ncvt);
1144 return EIO;
1145 }
1146 break;
1147 case cs_numchar:
1148 break;
1149 default:
1150 return EINVAL;
1151 }
1152
1153 /* Assignment */
1154
1155 if (cvtspec.noassign)
1156 goto skip_assign;
1157
1158 switch (cvtspec.spcr) {
1159 case cs_percent:
1160 break;
1161 case cs_decimal:
1162 case cs_int:
1163 switch (cvtspec.lenmod) {
1164 case lm_none:
1165 iptr = va_arg(ap, int *);
1166 *iptr = ival;
1167 break;
1168 case lm_hh:
1169 scptr = va_arg(ap, signed char *);
1170 *scptr = ival;
1171 break;
1172 case lm_h:
1173 sptr = va_arg(ap, short *);
1174 *sptr = ival;
1175 break;
1176 case lm_l:
1177 lptr = va_arg(ap, long *);
1178 *lptr = ival;
1179 break;
1180 case lm_ll:
1181 llptr = va_arg(ap, long long *);
1182 *llptr = ival;
1183 break;
1184 case lm_j:
1185 imptr = va_arg(ap, intmax_t *);
1186 *imptr = ival;
1187 break;
1188 case lm_z:
1189 ssptr = va_arg(ap, ssize_t *);
1190 *ssptr = ival;
1191 break;
1192 case lm_t:
1193 pdptr = va_arg(ap, ptrdiff_t *);
1194 *pdptr = ival;
1195 break;
1196 default:
1197 assert(false);
1198 }
1199
1200 ++(*ncvt);
1201 break;
1202 case cs_udecimal:
1203 case cs_octal:
1204 case cs_hex:
1205 switch (cvtspec.lenmod) {
1206 case lm_none:
1207 uptr = va_arg(ap, unsigned *);
1208 *uptr = uval;
1209 break;
1210 case lm_hh:
1211 ucptr = va_arg(ap, unsigned char *);
1212 *ucptr = uval;
1213 break;
1214 case lm_h:
1215 usptr = va_arg(ap, unsigned short *);
1216 *usptr = uval;
1217 break;
1218 case lm_l:
1219 ulptr = va_arg(ap, unsigned long *);
1220 *ulptr = uval;
1221 break;
1222 case lm_ll:
1223 ullptr = va_arg(ap, unsigned long long *);
1224 *ullptr = uval;
1225 break;
1226 case lm_j:
1227 umptr = va_arg(ap, uintmax_t *);
1228 *umptr = uval;
1229 break;
1230 case lm_z:
1231 szptr = va_arg(ap, size_t *);
1232 *szptr = uval;
1233 break;
1234 case lm_t:
1235 updptr = va_arg(ap, ptrdiff_t *);
1236 *updptr = uval;
1237 break;
1238 default:
1239 assert(false);
1240 }
1241
1242 ++(*ncvt);
1243 break;
1244 case cs_float:
1245 switch (cvtspec.lenmod) {
1246 case lm_none:
1247 fptr = va_arg(ap, float *);
1248 *fptr = fval;
1249 break;
1250 case lm_l:
1251 dptr = va_arg(ap, double *);
1252 *dptr = fval;
1253 break;
1254 case lm_L:
1255 ldptr = va_arg(ap, long double *);
1256 *ldptr = fval;
1257 break;
1258 default:
1259 assert(false);
1260 }
1261
1262 ++(*ncvt);
1263 break;
1264 case cs_ptr:
1265 pptr = va_arg(ap, void *);
1266 *pptr = (void *)(uintptr_t)uval;
1267 ++(*ncvt);
1268 break;
1269 case cs_char:
1270 ++(*ncvt);
1271 break;
1272 case cs_str:
1273 ++(*ncvt);
1274 break;
1275 case cs_set:
1276 ++(*ncvt);
1277 break;
1278 case cs_numchar:
1279 /* Store number of characters read so far. */
1280 iptr = va_arg(ap, int *);
1281 *iptr = *numchar;
1282 /* No incrementing of ncvt */
1283 break;
1284 default:
1285 return EINVAL;
1286 }
1287
1288skip_assign:
1289
1290 return EOK;
1291}
1292
1293int vfscanf(FILE *f, const char *fmt, va_list ap)
1294{
1295 const char *cp;
1296 int c;
1297 unsigned ncvt;
1298 int numchar;
1299 bool input_error = false;
1300 int rc;
1301
1302 ncvt = 0;
1303 numchar = 0;
1304 cp = fmt;
1305 while (*cp != '\0') {
1306 if (isspace(*cp)) {
1307 /* Whitespace */
1308 rc = vfscanf_match_ws(f, &numchar, &cp);
1309 if (rc == EIO) {
1310 input_error = true;
1311 break;
1312 }
1313
1314 assert(rc == EOK);
1315 } else if (*cp == '%') {
1316 /* Conversion specification */
1317 rc = vfscanf_cvt(f, &cp, ap, &numchar, &ncvt);
1318 if (rc == EIO) {
1319 /* Input error */
1320 input_error = true;
1321 break;
1322 }
1323
1324 /* Other error */
1325 if (rc != EOK)
1326 break;
1327 } else {
1328 /* Match specific character */
1329 c = __fgetc(f, &numchar);
1330 if (c == EOF) {
1331 input_error = true;
1332 break;
1333 }
1334
1335 if (c != *cp) {
1336 __ungetc(c, f, &numchar);
1337 break;
1338 }
1339
1340 ++cp;
1341 }
1342 }
1343
1344 if (input_error && ncvt == 0)
1345 return EOF;
1346
1347 return ncvt;
1348}
1349
1350int fscanf(FILE *f, const char *fmt, ...)
1351{
1352 va_list args;
1353 int rc;
1354
1355 va_start(args, fmt);
1356 rc = vfscanf(f, fmt, args);
1357 va_end(args);
1358
1359 return rc;
1360}
1361
1362int vscanf(const char *fmt, va_list ap)
1363{
1364 return vfscanf(stdin, fmt, ap);
1365}
1366
1367int scanf(const char *fmt, ...)
1368{
1369 va_list args;
1370 int rc;
1371
1372 va_start(args, fmt);
1373 rc = vscanf(fmt, args);
1374 va_end(args);
1375
1376 return rc;
1377}
1378
1379/** @}
1380 */
Note: See TracBrowser for help on using the repository browser.