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

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

vfscanf with tests.

  • Property mode set to 100644
File size: 23.9 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); // XXX
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 * @param c Character
889 * @param scanset Pointer to scanset
890 * @return @c true iff @a c is in scanset @a scanset.
891 */
892static bool is_in_scanset(char c, const char *scanset)
893{
894 const char *p = scanset;
895 bool inverted = false;
896
897 /* Inverted scanset */
898 if (*p == '^') {
899 inverted = true;
900 ++p;
901 }
902
903 /* ']' character in scanset */
904 if (*p == ']') {
905 if (c == ']')
906 return !inverted;
907 ++p;
908 }
909
910 /* Remaining characters */
911 while (*p != '\0' && *p != ']') {
912 if (*p == c)
913 return !inverted;
914 ++p;
915 }
916
917 return inverted;
918}
919
920/* Read string of characters from scanset from stream */
921static int __fgetscanstr(FILE *f, int *numchar, size_t width,
922 const char *scanset, strbuf_t *strbuf, size_t *nread)
923{
924 size_t cnt;
925 int c;
926 int rc;
927 int rc2;
928
929 rc = EOK;
930
931 for (cnt = 0; cnt < width; cnt++) {
932 c = __fgetc(f, numchar);
933 if (c == EOF) {
934 rc = EIO;
935 break;
936 }
937
938 if (!is_in_scanset(c, scanset)) {
939 __ungetc(c, f, numchar);
940 break;
941 }
942
943 rc = strbuf_write(strbuf, cnt, c);
944 if (rc != EOK) {
945 *nread = cnt;
946 break;
947 }
948 }
949
950 /* Null-terminate */
951 rc2 = strbuf_write(strbuf, cnt, '\0');
952 if (rc2 != EOK) {
953 *nread = cnt;
954 return rc2;
955 }
956
957 *nread = cnt;
958 return rc;
959}
960
961/** Perform a single conversion. */
962static int vfscanf_cvt(FILE *f, const char **fmt, va_list ap, int *numchar,
963 unsigned *ncvt)
964{
965 int rc;
966 int c;
967 intmax_t ival;
968 uintmax_t uval;
969 long double fval = 0.0;
970 int *iptr;
971 short *sptr;
972 signed char *scptr;
973 long *lptr;
974 long long *llptr;
975 ssize_t *ssptr;
976 ptrdiff_t *pdptr;
977 intmax_t *imptr;
978 unsigned *uptr;
979 unsigned short *usptr;
980 unsigned char *ucptr;
981 unsigned long *ulptr;
982 unsigned long long *ullptr;
983 float *fptr;
984 double *dptr;
985 long double *ldptr;
986 size_t *szptr;
987 ptrdiff_t *updptr;
988 uintmax_t *umptr;
989 void **pptr;
990 size_t width;
991 cvtspec_t cvtspec;
992 strbuf_t strbuf;
993 size_t nread;
994
995 ++(*fmt);
996
997 cvtspec_parse(fmt, &cvtspec);
998
999 if (cvtspec.spcr != cs_set && cvtspec.spcr != cs_char &&
1000 cvtspec.spcr != cs_numchar) {
1001 /* Skip whitespace */
1002 rc = vfscanf_skip_ws(f, numchar);
1003 if (rc == EIO)
1004 return EIO;
1005
1006 assert(rc == EOK);
1007 }
1008
1009 width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
1010
1011 switch (cvtspec.spcr) {
1012 case cs_percent:
1013 /* Match % character */
1014 c = __fgetc(f, numchar);
1015 if (c == EOF)
1016 return EIO;
1017 if (c != '%') {
1018 __ungetc(c, f, numchar);
1019 return EINVAL;
1020 }
1021 break;
1022 case cs_decimal:
1023 /* Decimal integer */
1024 rc = __fstrtoimax(f, numchar, 10, width, &ival);
1025 if (rc != EOK)
1026 return rc;
1027 break;
1028 case cs_udecimal:
1029 /* Decimal unsigned integer */
1030 rc = __fstrtoumax(f, numchar, 10, width, &uval);
1031 if (rc != EOK)
1032 return rc;
1033 break;
1034 case cs_octal:
1035 /* Octal unsigned integer */
1036 rc = __fstrtoumax(f, numchar, 8, width, &uval);
1037 if (rc != EOK)
1038 return rc;
1039 break;
1040 case cs_hex:
1041 /* Hexadecimal unsigned integer */
1042 rc = __fstrtoumax(f, numchar, 16, width, &uval);
1043 if (rc != EOK)
1044 return rc;
1045 break;
1046 case cs_float:
1047 /* Floating-point value */
1048 rc = __fstrtold(f, numchar, width, &fval);
1049 if (rc != EOK)
1050 return rc;
1051 break;
1052 case cs_int:
1053 /* Signed integer with base detection */
1054 rc = __fstrtoimax(f, numchar, 0, width, &ival);
1055 if (rc != EOK)
1056 return rc;
1057 break;
1058 case cs_ptr:
1059 /* Unsigned integer with base detection (need 0xXXXXX) */
1060 rc = __fstrtoumax(f, numchar, 0, width, &uval);
1061 if (rc != EOK)
1062 return rc;
1063 break;
1064 case cs_char:
1065 /* Characters */
1066 rc = strbuf_init(&strbuf, &cvtspec, ap);
1067 if (rc != EOK)
1068 return rc;
1069
1070 width = cvtspec.have_width ? cvtspec.width : 1;
1071 rc = __fgetchars(f, numchar, width, &strbuf, &nread);
1072 if (rc != EOK) {
1073 if (rc == ENOMEM)
1074 return EIO;
1075
1076 assert(rc == EIO);
1077 /* If we have at least one char, we succeeded */
1078 if (nread > 0)
1079 ++(*ncvt);
1080 return EIO;
1081 }
1082 break;
1083 case cs_str:
1084 /* Non-whitespace string */
1085 rc = strbuf_init(&strbuf, &cvtspec, ap);
1086 if (rc != EOK)
1087 return rc;
1088
1089 width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
1090 rc = __fgetstr(f, numchar, width, &strbuf, &nread);
1091 if (rc != EOK) {
1092 if (rc == ENOMEM)
1093 return EIO;
1094
1095 assert(rc == EIO);
1096 /* If we have at least one char, we succeeded */
1097 if (nread > 0)
1098 ++(*ncvt);
1099 return EIO;
1100 }
1101 break;
1102 case cs_set:
1103 /* String of characters from scanset */
1104 rc = strbuf_init(&strbuf, &cvtspec, ap);
1105 if (rc != EOK)
1106 return rc;
1107
1108 width = cvtspec.have_width ? cvtspec.width : SIZE_MAX;
1109 rc = __fgetscanstr(f, numchar, width, cvtspec.scanset,
1110 &strbuf, &nread);
1111 if (rc != EOK) {
1112 if (rc == ENOMEM)
1113 return EIO;
1114
1115 assert(rc == EIO);
1116 /* If we have at least one char, we succeeded */
1117 if (nread > 0)
1118 ++(*ncvt);
1119 return EIO;
1120 }
1121 break;
1122 case cs_numchar:
1123 break;
1124 default:
1125 return EINVAL;
1126 }
1127
1128 /* Assignment */
1129
1130 if (cvtspec.noassign)
1131 goto skip_assign;
1132
1133 switch (cvtspec.spcr) {
1134 case cs_percent:
1135 break;
1136 case cs_decimal:
1137 case cs_int:
1138 switch (cvtspec.lenmod) {
1139 case lm_none:
1140 iptr = va_arg(ap, int *);
1141 *iptr = ival;
1142 break;
1143 case lm_hh:
1144 scptr = va_arg(ap, signed char *);
1145 *scptr = ival;
1146 break;
1147 case lm_h:
1148 sptr = va_arg(ap, short *);
1149 *sptr = ival;
1150 break;
1151 case lm_l:
1152 lptr = va_arg(ap, long *);
1153 *lptr = ival;
1154 break;
1155 case lm_ll:
1156 llptr = va_arg(ap, long long *);
1157 *llptr = ival;
1158 break;
1159 case lm_j:
1160 imptr = va_arg(ap, intmax_t *);
1161 *imptr = ival;
1162 break;
1163 case lm_z:
1164 ssptr = va_arg(ap, ssize_t *);
1165 *ssptr = ival;
1166 break;
1167 case lm_t:
1168 pdptr = va_arg(ap, ptrdiff_t *);
1169 *pdptr = ival;
1170 break;
1171 default:
1172 assert(false);
1173 }
1174
1175 ++(*ncvt);
1176 break;
1177 case cs_udecimal:
1178 case cs_octal:
1179 case cs_hex:
1180 switch (cvtspec.lenmod) {
1181 case lm_none:
1182 uptr = va_arg(ap, unsigned *);
1183 *uptr = uval;
1184 break;
1185 case lm_hh:
1186 ucptr = va_arg(ap, unsigned char *);
1187 *ucptr = uval;
1188 break;
1189 case lm_h:
1190 usptr = va_arg(ap, unsigned short *);
1191 *usptr = uval;
1192 break;
1193 case lm_l:
1194 ulptr = va_arg(ap, unsigned long *);
1195 *ulptr = uval;
1196 break;
1197 case lm_ll:
1198 ullptr = va_arg(ap, unsigned long long *);
1199 *ullptr = uval;
1200 break;
1201 case lm_j:
1202 umptr = va_arg(ap, uintmax_t *);
1203 *umptr = uval;
1204 break;
1205 case lm_z:
1206 szptr = va_arg(ap, size_t *);
1207 *szptr = uval;
1208 break;
1209 case lm_t:
1210 updptr = va_arg(ap, ptrdiff_t *);
1211 *updptr = uval;
1212 break;
1213 default:
1214 assert(false);
1215 }
1216
1217 ++(*ncvt);
1218 break;
1219 case cs_float:
1220 switch (cvtspec.lenmod) {
1221 case lm_none:
1222 fptr = va_arg(ap, float *);
1223 *fptr = fval;
1224 break;
1225 case lm_l:
1226 dptr = va_arg(ap, double *);
1227 *dptr = fval;
1228 break;
1229 case lm_L:
1230 ldptr = va_arg(ap, long double *);
1231 *ldptr = fval;
1232 break;
1233 default:
1234 assert(false);
1235 }
1236
1237 ++(*ncvt);
1238 break;
1239 case cs_ptr:
1240 pptr = va_arg(ap, void *);
1241 *pptr = (void *)(uintptr_t)uval;
1242 ++(*ncvt);
1243 break;
1244 case cs_char:
1245 ++(*ncvt);
1246 break;
1247 case cs_str:
1248 ++(*ncvt);
1249 break;
1250 case cs_set:
1251 ++(*ncvt);
1252 break;
1253 case cs_numchar:
1254 /* Store number of characters read so far. */
1255 iptr = va_arg(ap, int *);
1256 *iptr = *numchar;
1257 /* No incrementing of ncvt */
1258 break;
1259 default:
1260 return EINVAL;
1261 }
1262
1263skip_assign:
1264
1265 return EOK;
1266}
1267
1268static int xxvfscanf(FILE *f, const char *fmt, va_list ap)
1269{
1270 const char *cp;
1271 int c;
1272 unsigned ncvt;
1273 int numchar;
1274 bool input_error = false;
1275 int rc;
1276
1277 ncvt = 0;
1278 numchar = 0;
1279 cp = fmt;
1280 while (*cp != '\0') {
1281 if (isspace(*cp)) {
1282 /* Whitespace */
1283 rc = vfscanf_match_ws(f, &numchar, &cp);
1284 if (rc == EIO) {
1285 input_error = true;
1286 break;
1287 }
1288
1289 assert(rc == EOK);
1290 } else if (*cp == '%') {
1291 /* Conversion specification */
1292 rc = vfscanf_cvt(f, &cp, ap, &numchar, &ncvt);
1293 if (rc == EIO) {
1294 /* Input error */
1295 input_error = true;
1296 break;
1297 }
1298
1299 /* Other error */
1300 if (rc != EOK)
1301 break;
1302 } else {
1303 /* Match specific character */
1304 c = __fgetc(f, &numchar);
1305 if (c == EOF) {
1306 input_error = true;
1307 break;
1308 }
1309
1310 if (c != *cp) {
1311 __ungetc(c, f, &numchar);
1312 break;
1313 }
1314
1315 ++cp;
1316 }
1317 }
1318
1319 if (input_error && ncvt == 0)
1320 return EOF;
1321
1322 return ncvt;
1323}
1324
1325int xxfscanf(FILE *f, const char *fmt, ...)
1326{
1327 va_list args;
1328 int rc;
1329
1330 va_start(args, fmt);
1331 rc = xxvfscanf(f, fmt, args);
1332 va_end(args);
1333
1334 return rc;
1335}
1336
1337int xxsscanf(const char *s, const char *fmt, ...)
1338{
1339 va_list args;
1340 FILE *f;
1341 int rc;
1342
1343 f = fopen("/tmp/test.tmp", "wt");
1344 if (f == NULL)
1345 return EOF;
1346
1347 if (fputs(s, f) == EOF)
1348 return EOF;
1349
1350 if (fclose(f) == EOF)
1351 return EOF;
1352
1353 f = fopen("/tmp/test.tmp", "rt");
1354 if (f == NULL) {
1355 printf("failed to open for reading\n");
1356 return EOF;
1357 }
1358
1359 va_start(args, fmt);
1360 rc = xxvfscanf(f, fmt, args);
1361 va_end(args);
1362
1363 fclose(f);
1364
1365 return rc;
1366}
Note: See TracBrowser for help on using the repository browser.