1 // Copyright (C) 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 *******************************************************************************
5 *
6 *   Copyright (C) 1998-2016, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 *******************************************************************************
10 *
11 * File uscnnf_p.c
12 *
13 * Modification History:
14 *
15 *   Date        Name        Description
16 *   12/02/98    stephen        Creation.
17 *   03/13/99    stephen     Modified for new C API.
18 *******************************************************************************
19 */
20 
21 #include "unicode/utypes.h"
22 
23 #if !UCONFIG_NO_FORMATTING && !UCONFIG_NO_CONVERSION
24 
25 #include "unicode/uchar.h"
26 #include "unicode/ustring.h"
27 #include "unicode/unum.h"
28 #include "unicode/udat.h"
29 #include "unicode/uset.h"
30 #include "uscanf.h"
31 #include "ufmt_cmn.h"
32 #include "ufile.h"
33 #include "locbund.h"
34 
35 #include "cmemory.h"
36 #include "ustr_cnv.h"
37 
38 /* flag characters for u_scanf */
39 #define FLAG_ASTERISK 0x002A
40 #define FLAG_PAREN 0x0028
41 
42 #define ISFLAG(s)    (s) == FLAG_ASTERISK || \
43             (s) == FLAG_PAREN
44 
45 /* special characters for u_scanf */
46 #define SPEC_DOLLARSIGN 0x0024
47 
48 /* unicode digits */
49 #define DIGIT_ZERO 0x0030
50 #define DIGIT_ONE 0x0031
51 #define DIGIT_TWO 0x0032
52 #define DIGIT_THREE 0x0033
53 #define DIGIT_FOUR 0x0034
54 #define DIGIT_FIVE 0x0035
55 #define DIGIT_SIX 0x0036
56 #define DIGIT_SEVEN 0x0037
57 #define DIGIT_EIGHT 0x0038
58 #define DIGIT_NINE 0x0039
59 
60 #define ISDIGIT(s)    (s) == DIGIT_ZERO || \
61             (s) == DIGIT_ONE || \
62             (s) == DIGIT_TWO || \
63             (s) == DIGIT_THREE || \
64             (s) == DIGIT_FOUR || \
65             (s) == DIGIT_FIVE || \
66             (s) == DIGIT_SIX || \
67             (s) == DIGIT_SEVEN || \
68             (s) == DIGIT_EIGHT || \
69             (s) == DIGIT_NINE
70 
71 /* u_scanf modifiers */
72 #define MOD_H 0x0068
73 #define MOD_LOWERL 0x006C
74 #define MOD_L 0x004C
75 
76 #define ISMOD(s)    (s) == MOD_H || \
77             (s) == MOD_LOWERL || \
78             (s) == MOD_L
79 
80 /**
81  * Struct encapsulating a single uscanf format specification.
82  */
83 typedef struct u_scanf_spec_info {
84     int32_t fWidth;         /* Width  */
85 
86     UChar   fSpec;          /* Format specification  */
87 
88     UChar   fPadChar;       /* Padding character  */
89 
90     UBool   fSkipArg;       /* TRUE if arg should be skipped */
91     UBool   fIsLongDouble;  /* L flag  */
92     UBool   fIsShort;       /* h flag  */
93     UBool   fIsLong;        /* l flag  */
94     UBool   fIsLongLong;    /* ll flag  */
95     UBool   fIsString;      /* TRUE if this is a NULL-terminated string. */
96 } u_scanf_spec_info;
97 
98 
99 /**
100  * Struct encapsulating a single u_scanf format specification.
101  */
102 typedef struct u_scanf_spec {
103     u_scanf_spec_info    fInfo;        /* Information on this spec */
104     int32_t        fArgPos;    /* Position of data in arg list */
105 } u_scanf_spec;
106 
107 /**
108  * Parse a single u_scanf format specifier in Unicode.
109  * @param fmt A pointer to a '%' character in a u_scanf format specification.
110  * @param spec A pointer to a <TT>u_scanf_spec</TT> to receive the parsed
111  * format specifier.
112  * @return The number of characters contained in this specifier.
113  */
114 static int32_t
u_scanf_parse_spec(const UChar * fmt,u_scanf_spec * spec)115 u_scanf_parse_spec (const UChar     *fmt,
116             u_scanf_spec    *spec)
117 {
118     const UChar *s = fmt;
119     const UChar *backup;
120     u_scanf_spec_info *info = &(spec->fInfo);
121 
122     /* initialize spec to default values */
123     spec->fArgPos             = -1;
124 
125     info->fWidth        = -1;
126     info->fSpec         = 0x0000;
127     info->fPadChar      = 0x0020;
128     info->fSkipArg      = FALSE;
129     info->fIsLongDouble = FALSE;
130     info->fIsShort      = FALSE;
131     info->fIsLong       = FALSE;
132     info->fIsLongLong   = FALSE;
133     info->fIsString     = TRUE;
134 
135 
136     /* skip over the initial '%' */
137     s++;
138 
139     /* Check for positional argument */
140     if(ISDIGIT(*s)) {
141 
142         /* Save the current position */
143         backup = s;
144 
145         /* handle positional parameters */
146         if(ISDIGIT(*s)) {
147             spec->fArgPos = (int) (*s++ - DIGIT_ZERO);
148 
149             while(ISDIGIT(*s)) {
150                 spec->fArgPos *= 10;
151                 spec->fArgPos += (int) (*s++ - DIGIT_ZERO);
152             }
153         }
154 
155         /* if there is no '$', don't read anything */
156         if(*s != SPEC_DOLLARSIGN) {
157             spec->fArgPos = -1;
158             s = backup;
159         }
160         /* munge the '$' */
161         else
162             s++;
163     }
164 
165     /* Get any format flags */
166     while(ISFLAG(*s)) {
167         switch(*s++) {
168 
169             /* skip argument */
170         case FLAG_ASTERISK:
171             info->fSkipArg = TRUE;
172             break;
173 
174             /* pad character specified */
175         case FLAG_PAREN:
176 
177             /* first four characters are hex values for pad char */
178             info->fPadChar = (UChar)ufmt_digitvalue(*s++);
179             info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*s++));
180             info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*s++));
181             info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*s++));
182 
183             /* final character is ignored */
184             s++;
185 
186             break;
187         }
188     }
189 
190     /* Get the width */
191     if(ISDIGIT(*s)){
192         info->fWidth = (int) (*s++ - DIGIT_ZERO);
193 
194         while(ISDIGIT(*s)) {
195             info->fWidth *= 10;
196             info->fWidth += (int) (*s++ - DIGIT_ZERO);
197         }
198     }
199 
200     /* Get any modifiers */
201     if(ISMOD(*s)) {
202         switch(*s++) {
203 
204             /* short */
205         case MOD_H:
206             info->fIsShort = TRUE;
207             break;
208 
209             /* long or long long */
210         case MOD_LOWERL:
211             if(*s == MOD_LOWERL) {
212                 info->fIsLongLong = TRUE;
213                 /* skip over the next 'l' */
214                 s++;
215             }
216             else
217                 info->fIsLong = TRUE;
218             break;
219 
220             /* long double */
221         case MOD_L:
222             info->fIsLongDouble = TRUE;
223             break;
224         }
225     }
226 
227     /* finally, get the specifier letter */
228     info->fSpec = *s++;
229 
230     /* return # of characters in this specifier */
231     return (int32_t)(s - fmt);
232 }
233 
234 #define UP_PERCENT 0x0025
235 
236 
237 /* ANSI style formatting */
238 /* Use US-ASCII characters only for formatting */
239 
240 /* % */
241 #define UFMT_SIMPLE_PERCENT {ufmt_simple_percent, u_scanf_simple_percent_handler}
242 /* s */
243 #define UFMT_STRING         {ufmt_string, u_scanf_string_handler}
244 /* c */
245 #define UFMT_CHAR           {ufmt_string, u_scanf_char_handler}
246 /* d, i */
247 #define UFMT_INT            {ufmt_int, u_scanf_integer_handler}
248 /* u */
249 #define UFMT_UINT           {ufmt_int, u_scanf_uinteger_handler}
250 /* o */
251 #define UFMT_OCTAL          {ufmt_int, u_scanf_octal_handler}
252 /* x, X */
253 #define UFMT_HEX            {ufmt_int, u_scanf_hex_handler}
254 /* f */
255 #define UFMT_DOUBLE         {ufmt_double, u_scanf_double_handler}
256 /* e, E */
257 #define UFMT_SCIENTIFIC     {ufmt_double, u_scanf_scientific_handler}
258 /* g, G */
259 #define UFMT_SCIDBL         {ufmt_double, u_scanf_scidbl_handler}
260 /* n */
261 #define UFMT_COUNT          {ufmt_count, u_scanf_count_handler}
262 /* [ */
263 #define UFMT_SCANSET        {ufmt_string, u_scanf_scanset_handler}
264 
265 /* non-ANSI extensions */
266 /* Use US-ASCII characters only for formatting */
267 
268 /* p */
269 #define UFMT_POINTER        {ufmt_pointer, u_scanf_pointer_handler}
270 /* V */
271 #define UFMT_SPELLOUT       {ufmt_double, u_scanf_spellout_handler}
272 /* P */
273 #define UFMT_PERCENT        {ufmt_double, u_scanf_percent_handler}
274 /* C  K is old format */
275 #define UFMT_UCHAR          {ufmt_uchar, u_scanf_uchar_handler}
276 /* S  U is old format */
277 #define UFMT_USTRING        {ufmt_ustring, u_scanf_ustring_handler}
278 
279 
280 #define UFMT_EMPTY {ufmt_empty, NULL}
281 
282 /**
283  * A u_scanf handler function.
284  * A u_scanf handler is responsible for handling a single u_scanf
285  * format specification, for example 'd' or 's'.
286  * @param stream The UFILE to which to write output.
287  * @param info A pointer to a <TT>u_scanf_spec_info</TT> struct containing
288  * information on the format specification.
289  * @param args A pointer to the argument data
290  * @param fmt A pointer to the first character in the format string
291  * following the spec.
292  * @param fmtConsumed On output, set to the number of characters consumed
293  * in <TT>fmt</TT>. Do nothing, if the argument isn't variable width.
294  * @param argConverted The number of arguments converted and assigned, or -1 if an
295  * error occurred.
296  * @return The number of code points consumed during reading.
297  */
298 typedef int32_t (*u_scanf_handler) (UFILE   *stream,
299                    u_scanf_spec_info  *info,
300                    ufmt_args                *args,
301                    const UChar              *fmt,
302                    int32_t                  *fmtConsumed,
303                    int32_t                  *argConverted);
304 
305 typedef struct u_scanf_info {
306     ufmt_type_info info;
307     u_scanf_handler handler;
308 } u_scanf_info;
309 
310 #define USCANF_NUM_FMT_HANDLERS 108
311 #define USCANF_SYMBOL_BUFFER_SIZE 8
312 
313 /* We do not use handlers for 0-0x1f */
314 #define USCANF_BASE_FMT_HANDLERS 0x20
315 
316 
317 static int32_t
u_scanf_skip_leading_ws(UFILE * input,UChar pad)318 u_scanf_skip_leading_ws(UFILE   *input,
319                         UChar   pad)
320 {
321     UChar   c;
322     int32_t count = 0;
323     UBool isNotEOF;
324 
325     /* skip all leading ws in the input */
326     while( (isNotEOF = ufile_getch(input, &c)) && (c == pad || u_isWhitespace(c)) )
327     {
328         count++;
329     }
330 
331     /* put the final character back on the input */
332     if(isNotEOF)
333         u_fungetc(c, input);
334 
335     return count;
336 }
337 
338 /* TODO: Is always skipping the prefix symbol as a positive sign a good idea in all locales? */
339 static int32_t
u_scanf_skip_leading_positive_sign(UFILE * input,UNumberFormat * format,UErrorCode * status)340 u_scanf_skip_leading_positive_sign(UFILE   *input,
341                                    UNumberFormat *format,
342                                    UErrorCode *status)
343 {
344     UChar   c;
345     int32_t count = 0;
346     UBool isNotEOF;
347     UChar plusSymbol[USCANF_SYMBOL_BUFFER_SIZE];
348     int32_t symbolLen;
349     UErrorCode localStatus = U_ZERO_ERROR;
350 
351     if (U_SUCCESS(*status)) {
352         symbolLen = unum_getSymbol(format,
353             UNUM_PLUS_SIGN_SYMBOL,
354             plusSymbol,
355             UPRV_LENGTHOF(plusSymbol),
356             &localStatus);
357 
358         if (U_SUCCESS(localStatus)) {
359             /* skip all leading ws in the input */
360             while( (isNotEOF = ufile_getch(input, &c)) && (count < symbolLen && c == plusSymbol[count]) )
361             {
362                 count++;
363             }
364 
365             /* put the final character back on the input */
366             if(isNotEOF) {
367                 u_fungetc(c, input);
368             }
369         }
370     }
371 
372     return count;
373 }
374 
375 static int32_t
u_scanf_simple_percent_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)376 u_scanf_simple_percent_handler(UFILE        *input,
377                                u_scanf_spec_info *info,
378                                ufmt_args    *args,
379                                const UChar  *fmt,
380                                int32_t      *fmtConsumed,
381                                int32_t      *argConverted)
382 {
383     /* make sure the next character in the input is a percent */
384     *argConverted = 0;
385     if(u_fgetc(input) != 0x0025) {
386         *argConverted = -1;
387     }
388     return 1;
389 }
390 
391 static int32_t
u_scanf_count_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)392 u_scanf_count_handler(UFILE         *input,
393                       u_scanf_spec_info *info,
394                       ufmt_args     *args,
395                       const UChar   *fmt,
396                       int32_t       *fmtConsumed,
397                       int32_t       *argConverted)
398 {
399     /* in the special case of count, the u_scanf_spec_info's width */
400     /* will contain the # of items converted thus far */
401     if (!info->fSkipArg) {
402         if (info->fIsShort)
403             *(int16_t*)(args[0].ptrValue) = (int16_t)(UINT16_MAX & info->fWidth);
404         else if (info->fIsLongLong)
405             *(int64_t*)(args[0].ptrValue) = info->fWidth;
406         else
407             *(int32_t*)(args[0].ptrValue) = (int32_t)(UINT32_MAX & info->fWidth);
408     }
409     *argConverted = 0;
410 
411     /* we converted 0 args */
412     return 0;
413 }
414 
415 static int32_t
u_scanf_double_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)416 u_scanf_double_handler(UFILE        *input,
417                        u_scanf_spec_info *info,
418                        ufmt_args    *args,
419                        const UChar  *fmt,
420                        int32_t      *fmtConsumed,
421                        int32_t      *argConverted)
422 {
423     int32_t         len;
424     double          num;
425     UNumberFormat   *format;
426     int32_t         parsePos    = 0;
427     int32_t         skipped;
428     UErrorCode      status      = U_ZERO_ERROR;
429 
430 
431     /* skip all ws in the input */
432     skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
433 
434     /* fill the input's internal buffer */
435     ufile_fill_uchar_buffer(input);
436 
437     /* determine the size of the input's buffer */
438     len = (int32_t)(input->str.fLimit - input->str.fPos);
439 
440     /* truncate to the width, if specified */
441     if(info->fWidth != -1)
442         len = ufmt_min(len, info->fWidth);
443 
444     /* get the formatter */
445     format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_DECIMAL);
446 
447     /* handle error */
448     if(format == 0)
449         return 0;
450 
451     /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */
452     skipped += u_scanf_skip_leading_positive_sign(input, format, &status);
453 
454     /* parse the number */
455     num = unum_parseDouble(format, input->str.fPos, len, &parsePos, &status);
456 
457     if (!info->fSkipArg) {
458         if (info->fIsLong)
459             *(double*)(args[0].ptrValue) = num;
460         else if (info->fIsLongDouble)
461             *(long double*)(args[0].ptrValue) = num;
462         else
463             *(float*)(args[0].ptrValue) = (float)num;
464     }
465 
466     /* mask off any necessary bits */
467     /*  if(! info->fIsLong_double)
468     num &= DBL_MAX;*/
469 
470     /* update the input's position to reflect consumed data */
471     input->str.fPos += parsePos;
472 
473     /* we converted 1 arg */
474     *argConverted = !info->fSkipArg;
475     return parsePos + skipped;
476 }
477 
478 #define UPRINTF_SYMBOL_BUFFER_SIZE 8
479 
480 static int32_t
u_scanf_scientific_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)481 u_scanf_scientific_handler(UFILE        *input,
482                            u_scanf_spec_info *info,
483                            ufmt_args    *args,
484                            const UChar  *fmt,
485                            int32_t      *fmtConsumed,
486                            int32_t      *argConverted)
487 {
488     int32_t         len;
489     double          num;
490     UNumberFormat   *format;
491     int32_t         parsePos    = 0;
492     int32_t         skipped;
493     UErrorCode      status      = U_ZERO_ERROR;
494     UChar srcExpBuf[UPRINTF_SYMBOL_BUFFER_SIZE];
495     int32_t srcLen, expLen;
496     UChar expBuf[UPRINTF_SYMBOL_BUFFER_SIZE];
497 
498 
499     /* skip all ws in the input */
500     skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
501 
502     /* fill the input's internal buffer */
503     ufile_fill_uchar_buffer(input);
504 
505     /* determine the size of the input's buffer */
506     len = (int32_t)(input->str.fLimit - input->str.fPos);
507 
508     /* truncate to the width, if specified */
509     if(info->fWidth != -1)
510         len = ufmt_min(len, info->fWidth);
511 
512     /* get the formatter */
513     format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_SCIENTIFIC);
514 
515     /* handle error */
516     if(format == 0)
517         return 0;
518 
519     /* set the appropriate flags on the formatter */
520 
521     srcLen = unum_getSymbol(format,
522         UNUM_EXPONENTIAL_SYMBOL,
523         srcExpBuf,
524         sizeof(srcExpBuf),
525         &status);
526 
527     /* Upper/lower case the e */
528     if (info->fSpec == (UChar)0x65 /* e */) {
529         expLen = u_strToLower(expBuf, (int32_t)sizeof(expBuf),
530             srcExpBuf, srcLen,
531             input->str.fBundle.fLocale,
532             &status);
533     }
534     else {
535         expLen = u_strToUpper(expBuf, (int32_t)sizeof(expBuf),
536             srcExpBuf, srcLen,
537             input->str.fBundle.fLocale,
538             &status);
539     }
540 
541     unum_setSymbol(format,
542         UNUM_EXPONENTIAL_SYMBOL,
543         expBuf,
544         expLen,
545         &status);
546 
547 
548 
549 
550     /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */
551     skipped += u_scanf_skip_leading_positive_sign(input, format, &status);
552 
553     /* parse the number */
554     num = unum_parseDouble(format, input->str.fPos, len, &parsePos, &status);
555 
556     if (!info->fSkipArg) {
557         if (info->fIsLong)
558             *(double*)(args[0].ptrValue) = num;
559         else if (info->fIsLongDouble)
560             *(long double*)(args[0].ptrValue) = num;
561         else
562             *(float*)(args[0].ptrValue) = (float)num;
563     }
564 
565     /* mask off any necessary bits */
566     /*  if(! info->fIsLong_double)
567     num &= DBL_MAX;*/
568 
569     /* update the input's position to reflect consumed data */
570     input->str.fPos += parsePos;
571 
572     /* we converted 1 arg */
573     *argConverted = !info->fSkipArg;
574     return parsePos + skipped;
575 }
576 
577 static int32_t
u_scanf_scidbl_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)578 u_scanf_scidbl_handler(UFILE        *input,
579                        u_scanf_spec_info *info,
580                        ufmt_args    *args,
581                        const UChar  *fmt,
582                        int32_t      *fmtConsumed,
583                        int32_t      *argConverted)
584 {
585     int32_t       len;
586     double        num;
587     UNumberFormat *scientificFormat, *genericFormat;
588     /*int32_t       scientificResult, genericResult;*/
589     double        scientificResult, genericResult;
590     int32_t       scientificParsePos = 0, genericParsePos = 0, parsePos = 0;
591     int32_t       skipped;
592     UErrorCode    scientificStatus = U_ZERO_ERROR;
593     UErrorCode    genericStatus = U_ZERO_ERROR;
594 
595 
596     /* since we can't determine by scanning the characters whether */
597     /* a number was formatted in the 'f' or 'g' styles, parse the */
598     /* string with both formatters, and assume whichever one */
599     /* parsed the most is the correct formatter to use */
600 
601 
602     /* skip all ws in the input */
603     skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
604 
605     /* fill the input's internal buffer */
606     ufile_fill_uchar_buffer(input);
607 
608     /* determine the size of the input's buffer */
609     len = (int32_t)(input->str.fLimit - input->str.fPos);
610 
611     /* truncate to the width, if specified */
612     if(info->fWidth != -1)
613         len = ufmt_min(len, info->fWidth);
614 
615     /* get the formatters */
616     scientificFormat = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_SCIENTIFIC);
617     genericFormat = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_DECIMAL);
618 
619     /* handle error */
620     if(scientificFormat == 0 || genericFormat == 0)
621         return 0;
622 
623     /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */
624     skipped += u_scanf_skip_leading_positive_sign(input, genericFormat, &genericStatus);
625 
626     /* parse the number using each format*/
627 
628     scientificResult = unum_parseDouble(scientificFormat, input->str.fPos, len,
629         &scientificParsePos, &scientificStatus);
630 
631     genericResult = unum_parseDouble(genericFormat, input->str.fPos, len,
632         &genericParsePos, &genericStatus);
633 
634     /* determine which parse made it farther */
635     if(scientificParsePos > genericParsePos) {
636         /* stash the result in num */
637         num = scientificResult;
638         /* update the input's position to reflect consumed data */
639         parsePos += scientificParsePos;
640     }
641     else {
642         /* stash the result in num */
643         num = genericResult;
644         /* update the input's position to reflect consumed data */
645         parsePos += genericParsePos;
646     }
647     input->str.fPos += parsePos;
648 
649     if (!info->fSkipArg) {
650         if (info->fIsLong)
651             *(double*)(args[0].ptrValue) = num;
652         else if (info->fIsLongDouble)
653             *(long double*)(args[0].ptrValue) = num;
654         else
655             *(float*)(args[0].ptrValue) = (float)num;
656     }
657 
658     /* mask off any necessary bits */
659     /*  if(! info->fIsLong_double)
660     num &= DBL_MAX;*/
661 
662     /* we converted 1 arg */
663     *argConverted = !info->fSkipArg;
664     return parsePos + skipped;
665 }
666 
667 static int32_t
u_scanf_integer_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)668 u_scanf_integer_handler(UFILE       *input,
669                         u_scanf_spec_info *info,
670                         ufmt_args   *args,
671                         const UChar *fmt,
672                         int32_t     *fmtConsumed,
673                         int32_t     *argConverted)
674 {
675     int32_t         len;
676     void            *num        = (void*) (args[0].ptrValue);
677     UNumberFormat   *format;
678     int32_t         parsePos    = 0;
679     int32_t         skipped;
680     UErrorCode      status      = U_ZERO_ERROR;
681     int64_t         result;
682 
683 
684     /* skip all ws in the input */
685     skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
686 
687     /* fill the input's internal buffer */
688     ufile_fill_uchar_buffer(input);
689 
690     /* determine the size of the input's buffer */
691     len = (int32_t)(input->str.fLimit - input->str.fPos);
692 
693     /* truncate to the width, if specified */
694     if(info->fWidth != -1)
695         len = ufmt_min(len, info->fWidth);
696 
697     /* get the formatter */
698     format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_DECIMAL);
699 
700     /* handle error */
701     if(format == 0)
702         return 0;
703 
704     /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */
705     skipped += u_scanf_skip_leading_positive_sign(input, format, &status);
706 
707     /* parse the number */
708     result = unum_parseInt64(format, input->str.fPos, len, &parsePos, &status);
709 
710     /* mask off any necessary bits */
711     if (!info->fSkipArg) {
712         if (info->fIsShort)
713             *(int16_t*)num = (int16_t)(UINT16_MAX & result);
714         else if (info->fIsLongLong)
715             *(int64_t*)num = result;
716         else
717             *(int32_t*)num = (int32_t)(UINT32_MAX & result);
718     }
719 
720     /* update the input's position to reflect consumed data */
721     input->str.fPos += parsePos;
722 
723     /* we converted 1 arg */
724     *argConverted = !info->fSkipArg;
725     return parsePos + skipped;
726 }
727 
728 static int32_t
u_scanf_uinteger_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)729 u_scanf_uinteger_handler(UFILE          *input,
730                          u_scanf_spec_info *info,
731                          ufmt_args      *args,
732                          const UChar    *fmt,
733                          int32_t        *fmtConsumed,
734                          int32_t        *argConverted)
735 {
736     /* TODO Fix this when Numberformat handles uint64_t */
737     return u_scanf_integer_handler(input, info, args, fmt, fmtConsumed, argConverted);
738 }
739 
740 static int32_t
u_scanf_percent_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)741 u_scanf_percent_handler(UFILE       *input,
742                         u_scanf_spec_info *info,
743                         ufmt_args   *args,
744                         const UChar *fmt,
745                         int32_t     *fmtConsumed,
746                         int32_t     *argConverted)
747 {
748     int32_t         len;
749     double          num;
750     UNumberFormat   *format;
751     int32_t         parsePos    = 0;
752     UErrorCode      status      = U_ZERO_ERROR;
753 
754 
755     /* skip all ws in the input */
756     u_scanf_skip_leading_ws(input, info->fPadChar);
757 
758     /* fill the input's internal buffer */
759     ufile_fill_uchar_buffer(input);
760 
761     /* determine the size of the input's buffer */
762     len = (int32_t)(input->str.fLimit - input->str.fPos);
763 
764     /* truncate to the width, if specified */
765     if(info->fWidth != -1)
766         len = ufmt_min(len, info->fWidth);
767 
768     /* get the formatter */
769     format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_PERCENT);
770 
771     /* handle error */
772     if(format == 0)
773         return 0;
774 
775     /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */
776     u_scanf_skip_leading_positive_sign(input, format, &status);
777 
778     /* parse the number */
779     num = unum_parseDouble(format, input->str.fPos, len, &parsePos, &status);
780 
781     if (!info->fSkipArg) {
782         *(double*)(args[0].ptrValue) = num;
783     }
784 
785     /* mask off any necessary bits */
786     /*  if(! info->fIsLong_double)
787     num &= DBL_MAX;*/
788 
789     /* update the input's position to reflect consumed data */
790     input->str.fPos += parsePos;
791 
792     /* we converted 1 arg */
793     *argConverted = !info->fSkipArg;
794     return parsePos;
795 }
796 
797 static int32_t
u_scanf_string_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)798 u_scanf_string_handler(UFILE        *input,
799                        u_scanf_spec_info *info,
800                        ufmt_args    *args,
801                        const UChar  *fmt,
802                        int32_t      *fmtConsumed,
803                        int32_t      *argConverted)
804 {
805     const UChar *source;
806     UConverter  *conv;
807     char        *arg    = (char*)(args[0].ptrValue);
808     char        *alias  = arg;
809     char        *limit;
810     UErrorCode  status  = U_ZERO_ERROR;
811     int32_t     count;
812     int32_t     skipped = 0;
813     UChar       c;
814     UBool       isNotEOF = FALSE;
815 
816     /* skip all ws in the input */
817     if (info->fIsString) {
818         skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
819     }
820 
821     /* get the string one character at a time, truncating to the width */
822     count = 0;
823 
824     /* open the default converter */
825     conv = u_getDefaultConverter(&status);
826 
827     if(U_FAILURE(status))
828         return -1;
829 
830     while( (info->fWidth == -1 || count < info->fWidth)
831         && (isNotEOF = ufile_getch(input, &c))
832         && (!info->fIsString || (c != info->fPadChar && !u_isWhitespace(c))))
833     {
834 
835         if (!info->fSkipArg) {
836             /* put the character from the input onto the target */
837             source = &c;
838             /* Since we do this one character at a time, do it this way. */
839             if (info->fWidth > 0) {
840                 limit = alias + info->fWidth - count;
841             }
842             else {
843                 limit = alias + ucnv_getMaxCharSize(conv);
844             }
845 
846             /* convert the character to the default codepage */
847             ucnv_fromUnicode(conv, &alias, limit, &source, source + 1,
848                 NULL, TRUE, &status);
849 
850             if(U_FAILURE(status)) {
851                 /* clean up */
852                 u_releaseDefaultConverter(conv);
853                 return -1;
854             }
855         }
856 
857         /* increment the count */
858         ++count;
859     }
860 
861     /* put the final character we read back on the input */
862     if (!info->fSkipArg) {
863         if ((info->fWidth == -1 || count < info->fWidth) && isNotEOF)
864             u_fungetc(c, input);
865 
866         /* add the terminator */
867         if (info->fIsString) {
868             *alias = 0x00;
869         }
870     }
871 
872     /* clean up */
873     u_releaseDefaultConverter(conv);
874 
875     /* we converted 1 arg */
876     *argConverted = !info->fSkipArg;
877     return count + skipped;
878 }
879 
880 static int32_t
u_scanf_char_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)881 u_scanf_char_handler(UFILE          *input,
882                      u_scanf_spec_info *info,
883                      ufmt_args      *args,
884                      const UChar    *fmt,
885                      int32_t        *fmtConsumed,
886                      int32_t        *argConverted)
887 {
888     if (info->fWidth < 0) {
889         info->fWidth = 1;
890     }
891     info->fIsString = FALSE;
892     return u_scanf_string_handler(input, info, args, fmt, fmtConsumed, argConverted);
893 }
894 
895 static int32_t
u_scanf_ustring_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)896 u_scanf_ustring_handler(UFILE       *input,
897                         u_scanf_spec_info *info,
898                         ufmt_args   *args,
899                         const UChar *fmt,
900                         int32_t     *fmtConsumed,
901                         int32_t     *argConverted)
902 {
903     UChar   *arg     = (UChar*)(args[0].ptrValue);
904     UChar   *alias     = arg;
905     int32_t count;
906     int32_t skipped = 0;
907     UChar   c;
908     UBool   isNotEOF = FALSE;
909 
910     /* skip all ws in the input */
911     if (info->fIsString) {
912         skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
913     }
914 
915     /* get the string one character at a time, truncating to the width */
916     count = 0;
917 
918     while( (info->fWidth == -1 || count < info->fWidth)
919         && (isNotEOF = ufile_getch(input, &c))
920         && (!info->fIsString || (c != info->fPadChar && !u_isWhitespace(c))))
921     {
922 
923         /* put the character from the input onto the target */
924         if (!info->fSkipArg) {
925             *alias++ = c;
926         }
927 
928         /* increment the count */
929         ++count;
930     }
931 
932     /* put the final character we read back on the input */
933     if (!info->fSkipArg) {
934         if((info->fWidth == -1 || count < info->fWidth) && isNotEOF) {
935             u_fungetc(c, input);
936         }
937 
938         /* add the terminator */
939         if (info->fIsString) {
940             *alias = 0x0000;
941         }
942     }
943 
944     /* we converted 1 arg */
945     *argConverted = !info->fSkipArg;
946     return count + skipped;
947 }
948 
949 static int32_t
u_scanf_uchar_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)950 u_scanf_uchar_handler(UFILE         *input,
951                       u_scanf_spec_info *info,
952                       ufmt_args     *args,
953                       const UChar   *fmt,
954                       int32_t       *fmtConsumed,
955                       int32_t       *argConverted)
956 {
957     if (info->fWidth < 0) {
958         info->fWidth = 1;
959     }
960     info->fIsString = FALSE;
961     return u_scanf_ustring_handler(input, info, args, fmt, fmtConsumed, argConverted);
962 }
963 
964 static int32_t
u_scanf_spellout_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)965 u_scanf_spellout_handler(UFILE          *input,
966                          u_scanf_spec_info *info,
967                          ufmt_args      *args,
968                          const UChar    *fmt,
969                          int32_t        *fmtConsumed,
970                          int32_t        *argConverted)
971 {
972     int32_t         len;
973     double          num;
974     UNumberFormat   *format;
975     int32_t         parsePos    = 0;
976     int32_t         skipped;
977     UErrorCode      status      = U_ZERO_ERROR;
978 
979 
980     /* skip all ws in the input */
981     skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
982 
983     /* fill the input's internal buffer */
984     ufile_fill_uchar_buffer(input);
985 
986     /* determine the size of the input's buffer */
987     len = (int32_t)(input->str.fLimit - input->str.fPos);
988 
989     /* truncate to the width, if specified */
990     if(info->fWidth != -1)
991         len = ufmt_min(len, info->fWidth);
992 
993     /* get the formatter */
994     format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_SPELLOUT);
995 
996     /* handle error */
997     if(format == 0)
998         return 0;
999 
1000     /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */
1001     /* This is not applicable to RBNF. */
1002     /*skipped += u_scanf_skip_leading_positive_sign(input, format, &status);*/
1003 
1004     /* parse the number */
1005     num = unum_parseDouble(format, input->str.fPos, len, &parsePos, &status);
1006 
1007     if (!info->fSkipArg) {
1008         *(double*)(args[0].ptrValue) = num;
1009     }
1010 
1011     /* mask off any necessary bits */
1012     /*  if(! info->fIsLong_double)
1013     num &= DBL_MAX;*/
1014 
1015     /* update the input's position to reflect consumed data */
1016     input->str.fPos += parsePos;
1017 
1018     /* we converted 1 arg */
1019     *argConverted = !info->fSkipArg;
1020     return parsePos + skipped;
1021 }
1022 
1023 static int32_t
u_scanf_hex_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)1024 u_scanf_hex_handler(UFILE       *input,
1025                     u_scanf_spec_info *info,
1026                     ufmt_args   *args,
1027                     const UChar *fmt,
1028                     int32_t     *fmtConsumed,
1029                     int32_t     *argConverted)
1030 {
1031     int32_t     len;
1032     int32_t     skipped;
1033     void        *num    = (void*) (args[0].ptrValue);
1034     int64_t     result;
1035 
1036     /* skip all ws in the input */
1037     skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
1038 
1039     /* fill the input's internal buffer */
1040     ufile_fill_uchar_buffer(input);
1041 
1042     /* determine the size of the input's buffer */
1043     len = (int32_t)(input->str.fLimit - input->str.fPos);
1044 
1045     /* truncate to the width, if specified */
1046     if(info->fWidth != -1)
1047         len = ufmt_min(len, info->fWidth);
1048 
1049     /* check for alternate form */
1050     if( *(input->str.fPos) == 0x0030 &&
1051         (*(input->str.fPos + 1) == 0x0078 || *(input->str.fPos + 1) == 0x0058) ) {
1052 
1053         /* skip the '0' and 'x' or 'X' if present */
1054         input->str.fPos += 2;
1055         len -= 2;
1056     }
1057 
1058     /* parse the number */
1059     result = ufmt_uto64(input->str.fPos, &len, 16);
1060 
1061     /* update the input's position to reflect consumed data */
1062     input->str.fPos += len;
1063 
1064     /* mask off any necessary bits */
1065     if (!info->fSkipArg) {
1066         if (info->fIsShort)
1067             *(int16_t*)num = (int16_t)(UINT16_MAX & result);
1068         else if (info->fIsLongLong)
1069             *(int64_t*)num = result;
1070         else
1071             *(int32_t*)num = (int32_t)(UINT32_MAX & result);
1072     }
1073 
1074     /* we converted 1 arg */
1075     *argConverted = !info->fSkipArg;
1076     return len + skipped;
1077 }
1078 
1079 static int32_t
u_scanf_octal_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)1080 u_scanf_octal_handler(UFILE         *input,
1081                       u_scanf_spec_info *info,
1082                       ufmt_args     *args,
1083                       const UChar   *fmt,
1084                       int32_t       *fmtConsumed,
1085                       int32_t       *argConverted)
1086 {
1087     int32_t     len;
1088     int32_t     skipped;
1089     void        *num         = (void*) (args[0].ptrValue);
1090     int64_t     result;
1091 
1092     /* skip all ws in the input */
1093     skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
1094 
1095     /* fill the input's internal buffer */
1096     ufile_fill_uchar_buffer(input);
1097 
1098     /* determine the size of the input's buffer */
1099     len = (int32_t)(input->str.fLimit - input->str.fPos);
1100 
1101     /* truncate to the width, if specified */
1102     if(info->fWidth != -1)
1103         len = ufmt_min(len, info->fWidth);
1104 
1105     /* parse the number */
1106     result = ufmt_uto64(input->str.fPos, &len, 8);
1107 
1108     /* update the input's position to reflect consumed data */
1109     input->str.fPos += len;
1110 
1111     /* mask off any necessary bits */
1112     if (!info->fSkipArg) {
1113         if (info->fIsShort)
1114             *(int16_t*)num = (int16_t)(UINT16_MAX & result);
1115         else if (info->fIsLongLong)
1116             *(int64_t*)num = result;
1117         else
1118             *(int32_t*)num = (int32_t)(UINT32_MAX & result);
1119     }
1120 
1121     /* we converted 1 arg */
1122     *argConverted = !info->fSkipArg;
1123     return len + skipped;
1124 }
1125 
1126 static int32_t
u_scanf_pointer_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)1127 u_scanf_pointer_handler(UFILE       *input,
1128                         u_scanf_spec_info *info,
1129                         ufmt_args   *args,
1130                         const UChar *fmt,
1131                         int32_t     *fmtConsumed,
1132                         int32_t     *argConverted)
1133 {
1134     int32_t len;
1135     int32_t skipped;
1136     void    *result;
1137     void    **p     = (void**)(args[0].ptrValue);
1138 
1139 
1140     /* skip all ws in the input */
1141     skipped = u_scanf_skip_leading_ws(input, info->fPadChar);
1142 
1143     /* fill the input's internal buffer */
1144     ufile_fill_uchar_buffer(input);
1145 
1146     /* determine the size of the input's buffer */
1147     len = (int32_t)(input->str.fLimit - input->str.fPos);
1148 
1149     /* truncate to the width, if specified */
1150     if(info->fWidth != -1) {
1151         len = ufmt_min(len, info->fWidth);
1152     }
1153 
1154     /* Make sure that we don't consume too much */
1155     if (len > (int32_t)(sizeof(void*)*2)) {
1156         len = (int32_t)(sizeof(void*)*2);
1157     }
1158 
1159     /* parse the pointer - assign to temporary value */
1160     result = ufmt_utop(input->str.fPos, &len);
1161 
1162     if (!info->fSkipArg) {
1163         *p = result;
1164     }
1165 
1166     /* update the input's position to reflect consumed data */
1167     input->str.fPos += len;
1168 
1169     /* we converted 1 arg */
1170     *argConverted = !info->fSkipArg;
1171     return len + skipped;
1172 }
1173 
1174 static int32_t
u_scanf_scanset_handler(UFILE * input,u_scanf_spec_info * info,ufmt_args * args,const UChar * fmt,int32_t * fmtConsumed,int32_t * argConverted)1175 u_scanf_scanset_handler(UFILE       *input,
1176                         u_scanf_spec_info *info,
1177                         ufmt_args   *args,
1178                         const UChar *fmt,
1179                         int32_t     *fmtConsumed,
1180                         int32_t     *argConverted)
1181 {
1182     USet        *scanset;
1183     UErrorCode  status = U_ZERO_ERROR;
1184     int32_t     chLeft = INT32_MAX;
1185     UChar32     c;
1186     UChar       *alias = (UChar*) (args[0].ptrValue);
1187     UBool       isNotEOF = FALSE;
1188     UBool       readCharacter = FALSE;
1189 
1190     /* Create an empty set */
1191     scanset = uset_open(0, -1);
1192 
1193     /* Back up one to get the [ */
1194     fmt--;
1195 
1196     /* truncate to the width, if specified and alias the target */
1197     if(info->fWidth >= 0) {
1198         chLeft = info->fWidth;
1199     }
1200 
1201     /* parse the scanset from the fmt string */
1202     *fmtConsumed = uset_applyPattern(scanset, fmt, -1, 0, &status);
1203 
1204     /* verify that the parse was successful */
1205     if (U_SUCCESS(status)) {
1206         c=0;
1207 
1208         /* grab characters one at a time and make sure they are in the scanset */
1209         while(chLeft > 0) {
1210             if ((isNotEOF = ufile_getch32(input, &c)) && uset_contains(scanset, c)) {
1211                 readCharacter = TRUE;
1212                 if (!info->fSkipArg) {
1213                     int32_t idx = 0;
1214                     UBool isError = FALSE;
1215 
1216                     U16_APPEND(alias, idx, chLeft, c, isError);
1217                     if (isError) {
1218                         break;
1219                     }
1220                     alias += idx;
1221                 }
1222                 chLeft -= (1 + U_IS_SUPPLEMENTARY(c));
1223             }
1224             else {
1225                 /* if the character's not in the scanset, break out */
1226                 break;
1227             }
1228         }
1229 
1230         /* put the final character we read back on the input */
1231         if(isNotEOF && chLeft > 0) {
1232             u_fungetc(c, input);
1233         }
1234     }
1235 
1236     uset_close(scanset);
1237 
1238     /* if we didn't match at least 1 character, fail */
1239     if(!readCharacter)
1240         return -1;
1241     /* otherwise, add the terminator */
1242     else if (!info->fSkipArg) {
1243         *alias = 0x00;
1244     }
1245 
1246     /* we converted 1 arg */
1247     *argConverted = !info->fSkipArg;
1248     return (info->fWidth >= 0 ? info->fWidth : INT32_MAX) - chLeft;
1249 }
1250 
1251 /* Use US-ASCII characters only for formatting. Most codepages have
1252  characters 20-7F from Unicode. Using any other codepage specific
1253  characters will make it very difficult to format the string on
1254  non-Unicode machines */
1255 static const u_scanf_info g_u_scanf_infos[USCANF_NUM_FMT_HANDLERS] = {
1256 /* 0x20 */
1257     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1258     UFMT_EMPTY,         UFMT_SIMPLE_PERCENT,UFMT_EMPTY,         UFMT_EMPTY,
1259     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1260     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1261 
1262 /* 0x30 */
1263     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1264     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1265     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1266     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1267 
1268 /* 0x40 */
1269     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_UCHAR,
1270     UFMT_EMPTY,         UFMT_SCIENTIFIC,    UFMT_EMPTY,         UFMT_SCIDBL,
1271 #ifdef U_USE_OBSOLETE_IO_FORMATTING
1272     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_UCHAR/*deprecated*/,
1273 #else
1274     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1275 #endif
1276     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1277 
1278 /* 0x50 */
1279     UFMT_PERCENT,       UFMT_EMPTY,         UFMT_EMPTY,         UFMT_USTRING,
1280 #ifdef U_USE_OBSOLETE_IO_FORMATTING
1281     UFMT_EMPTY,         UFMT_USTRING/*deprecated*/,UFMT_SPELLOUT,      UFMT_EMPTY,
1282 #else
1283     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_SPELLOUT,      UFMT_EMPTY,
1284 #endif
1285     UFMT_HEX,           UFMT_EMPTY,         UFMT_EMPTY,         UFMT_SCANSET,
1286     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1287 
1288 /* 0x60 */
1289     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_CHAR,
1290     UFMT_INT,           UFMT_SCIENTIFIC,    UFMT_DOUBLE,        UFMT_SCIDBL,
1291     UFMT_EMPTY,         UFMT_INT,           UFMT_EMPTY,         UFMT_EMPTY,
1292     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_COUNT,         UFMT_OCTAL,
1293 
1294 /* 0x70 */
1295     UFMT_POINTER,       UFMT_EMPTY,         UFMT_EMPTY,         UFMT_STRING,
1296     UFMT_EMPTY,         UFMT_UINT,          UFMT_EMPTY,         UFMT_EMPTY,
1297     UFMT_HEX,           UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1298     UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,         UFMT_EMPTY,
1299 };
1300 
1301 U_CFUNC int32_t
u_scanf_parse(UFILE * f,const UChar * patternSpecification,va_list ap)1302 u_scanf_parse(UFILE     *f,
1303             const UChar *patternSpecification,
1304             va_list     ap)
1305 {
1306     const UChar     *alias;
1307     int32_t         count, converted, argConsumed, cpConsumed;
1308     uint16_t        handlerNum;
1309 
1310     ufmt_args       args;
1311     u_scanf_spec    spec;
1312     ufmt_type_info  info;
1313     u_scanf_handler handler;
1314 
1315     /* alias the pattern */
1316     alias = patternSpecification;
1317 
1318     /* haven't converted anything yet */
1319     argConsumed = 0;
1320     converted = 0;
1321     cpConsumed = 0;
1322 
1323     /* iterate through the pattern */
1324     for(;;) {
1325 
1326         /* match any characters up to the next '%' */
1327         while(*alias != UP_PERCENT && *alias != 0x0000 && u_fgetc(f) == *alias) {
1328             alias++;
1329         }
1330 
1331         /* if we aren't at a '%', or if we're at end of string, break*/
1332         if(*alias != UP_PERCENT || *alias == 0x0000)
1333             break;
1334 
1335         /* parse the specifier */
1336         count = u_scanf_parse_spec(alias, &spec);
1337 
1338         /* update the pointer in pattern */
1339         alias += count;
1340 
1341         handlerNum = (uint16_t)(spec.fInfo.fSpec - USCANF_BASE_FMT_HANDLERS);
1342         if (handlerNum < USCANF_NUM_FMT_HANDLERS) {
1343             /* skip the argument, if necessary */
1344             /* query the info function for argument information */
1345             info = g_u_scanf_infos[ handlerNum ].info;
1346             if (info != ufmt_count && u_feof(f)) {
1347                 break;
1348             }
1349             else if(spec.fInfo.fSkipArg) {
1350                 args.ptrValue = NULL;
1351             }
1352             else {
1353                 switch(info) {
1354                 case ufmt_count:
1355                     /* set the spec's width to the # of items converted */
1356                     spec.fInfo.fWidth = cpConsumed;
1357                     U_FALLTHROUGH;
1358                 case ufmt_char:
1359                 case ufmt_uchar:
1360                 case ufmt_int:
1361                 case ufmt_string:
1362                 case ufmt_ustring:
1363                 case ufmt_pointer:
1364                 case ufmt_float:
1365                 case ufmt_double:
1366                     args.ptrValue = va_arg(ap, void*);
1367                     break;
1368 
1369                 default:
1370                     /* else args is ignored */
1371                     args.ptrValue = NULL;
1372                     break;
1373                 }
1374             }
1375 
1376             /* call the handler function */
1377             handler = g_u_scanf_infos[ handlerNum ].handler;
1378             if(handler != 0) {
1379 
1380                 /* reset count to 1 so that += for alias works. */
1381                 count = 1;
1382 
1383                 cpConsumed += (*handler)(f, &spec.fInfo, &args, alias, &count, &argConsumed);
1384 
1385                 /* if the handler encountered an error condition, break */
1386                 if(argConsumed < 0) {
1387                     converted = -1;
1388                     break;
1389                 }
1390 
1391                 /* add to the # of items converted */
1392                 converted += argConsumed;
1393 
1394                 /* update the pointer in pattern */
1395                 alias += count-1;
1396             }
1397             /* else do nothing */
1398         }
1399         /* else do nothing */
1400 
1401         /* just ignore unknown tags */
1402     }
1403 
1404     /* return # of items converted */
1405     return converted;
1406 }
1407 
1408 #endif /* #if !UCONFIG_NO_FORMATTING */
1409