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

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

Implement sscanf via virtualizing FILE and implementing string backend for FILE.

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