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

Last change on this file since 1be7bee was 9d8307a, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Reimplement strtold function in libc.

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