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

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

Need to carefully pass va_list around by reference to be portable.

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