michael@0: /* michael@0: ******************************************************************************* michael@0: * michael@0: * Copyright (C) 1998-2011, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ******************************************************************************* michael@0: * michael@0: * File uscnnf_p.c michael@0: * michael@0: * Modification History: michael@0: * michael@0: * Date Name Description michael@0: * 12/02/98 stephen Creation. michael@0: * 03/13/99 stephen Modified for new C API. michael@0: ******************************************************************************* michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: #include "unicode/uchar.h" michael@0: #include "unicode/ustring.h" michael@0: #include "unicode/unum.h" michael@0: #include "unicode/udat.h" michael@0: #include "unicode/uset.h" michael@0: #include "uscanf.h" michael@0: #include "ufmt_cmn.h" michael@0: #include "ufile.h" michael@0: #include "locbund.h" michael@0: michael@0: #include "cmemory.h" michael@0: #include "ustr_cnv.h" michael@0: michael@0: /* flag characters for u_scanf */ michael@0: #define FLAG_ASTERISK 0x002A michael@0: #define FLAG_PAREN 0x0028 michael@0: michael@0: #define ISFLAG(s) (s) == FLAG_ASTERISK || \ michael@0: (s) == FLAG_PAREN michael@0: michael@0: /* special characters for u_scanf */ michael@0: #define SPEC_DOLLARSIGN 0x0024 michael@0: michael@0: /* unicode digits */ michael@0: #define DIGIT_ZERO 0x0030 michael@0: #define DIGIT_ONE 0x0031 michael@0: #define DIGIT_TWO 0x0032 michael@0: #define DIGIT_THREE 0x0033 michael@0: #define DIGIT_FOUR 0x0034 michael@0: #define DIGIT_FIVE 0x0035 michael@0: #define DIGIT_SIX 0x0036 michael@0: #define DIGIT_SEVEN 0x0037 michael@0: #define DIGIT_EIGHT 0x0038 michael@0: #define DIGIT_NINE 0x0039 michael@0: michael@0: #define ISDIGIT(s) (s) == DIGIT_ZERO || \ michael@0: (s) == DIGIT_ONE || \ michael@0: (s) == DIGIT_TWO || \ michael@0: (s) == DIGIT_THREE || \ michael@0: (s) == DIGIT_FOUR || \ michael@0: (s) == DIGIT_FIVE || \ michael@0: (s) == DIGIT_SIX || \ michael@0: (s) == DIGIT_SEVEN || \ michael@0: (s) == DIGIT_EIGHT || \ michael@0: (s) == DIGIT_NINE michael@0: michael@0: /* u_scanf modifiers */ michael@0: #define MOD_H 0x0068 michael@0: #define MOD_LOWERL 0x006C michael@0: #define MOD_L 0x004C michael@0: michael@0: #define ISMOD(s) (s) == MOD_H || \ michael@0: (s) == MOD_LOWERL || \ michael@0: (s) == MOD_L michael@0: michael@0: /** michael@0: * Struct encapsulating a single uscanf format specification. michael@0: */ michael@0: typedef struct u_scanf_spec_info { michael@0: int32_t fWidth; /* Width */ michael@0: michael@0: UChar fSpec; /* Format specification */ michael@0: michael@0: UChar fPadChar; /* Padding character */ michael@0: michael@0: UBool fSkipArg; /* TRUE if arg should be skipped */ michael@0: UBool fIsLongDouble; /* L flag */ michael@0: UBool fIsShort; /* h flag */ michael@0: UBool fIsLong; /* l flag */ michael@0: UBool fIsLongLong; /* ll flag */ michael@0: UBool fIsString; /* TRUE if this is a NULL-terminated string. */ michael@0: } u_scanf_spec_info; michael@0: michael@0: michael@0: /** michael@0: * Struct encapsulating a single u_scanf format specification. michael@0: */ michael@0: typedef struct u_scanf_spec { michael@0: u_scanf_spec_info fInfo; /* Information on this spec */ michael@0: int32_t fArgPos; /* Position of data in arg list */ michael@0: } u_scanf_spec; michael@0: michael@0: /** michael@0: * Parse a single u_scanf format specifier in Unicode. michael@0: * @param fmt A pointer to a '%' character in a u_scanf format specification. michael@0: * @param spec A pointer to a u_scanf_spec to receive the parsed michael@0: * format specifier. michael@0: * @return The number of characters contained in this specifier. michael@0: */ michael@0: static int32_t michael@0: u_scanf_parse_spec (const UChar *fmt, michael@0: u_scanf_spec *spec) michael@0: { michael@0: const UChar *s = fmt; michael@0: const UChar *backup; michael@0: u_scanf_spec_info *info = &(spec->fInfo); michael@0: michael@0: /* initialize spec to default values */ michael@0: spec->fArgPos = -1; michael@0: michael@0: info->fWidth = -1; michael@0: info->fSpec = 0x0000; michael@0: info->fPadChar = 0x0020; michael@0: info->fSkipArg = FALSE; michael@0: info->fIsLongDouble = FALSE; michael@0: info->fIsShort = FALSE; michael@0: info->fIsLong = FALSE; michael@0: info->fIsLongLong = FALSE; michael@0: info->fIsString = TRUE; michael@0: michael@0: michael@0: /* skip over the initial '%' */ michael@0: s++; michael@0: michael@0: /* Check for positional argument */ michael@0: if(ISDIGIT(*s)) { michael@0: michael@0: /* Save the current position */ michael@0: backup = s; michael@0: michael@0: /* handle positional parameters */ michael@0: if(ISDIGIT(*s)) { michael@0: spec->fArgPos = (int) (*s++ - DIGIT_ZERO); michael@0: michael@0: while(ISDIGIT(*s)) { michael@0: spec->fArgPos *= 10; michael@0: spec->fArgPos += (int) (*s++ - DIGIT_ZERO); michael@0: } michael@0: } michael@0: michael@0: /* if there is no '$', don't read anything */ michael@0: if(*s != SPEC_DOLLARSIGN) { michael@0: spec->fArgPos = -1; michael@0: s = backup; michael@0: } michael@0: /* munge the '$' */ michael@0: else michael@0: s++; michael@0: } michael@0: michael@0: /* Get any format flags */ michael@0: while(ISFLAG(*s)) { michael@0: switch(*s++) { michael@0: michael@0: /* skip argument */ michael@0: case FLAG_ASTERISK: michael@0: info->fSkipArg = TRUE; michael@0: break; michael@0: michael@0: /* pad character specified */ michael@0: case FLAG_PAREN: michael@0: michael@0: /* first four characters are hex values for pad char */ michael@0: info->fPadChar = (UChar)ufmt_digitvalue(*s++); michael@0: info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*s++)); michael@0: info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*s++)); michael@0: info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*s++)); michael@0: michael@0: /* final character is ignored */ michael@0: s++; michael@0: michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /* Get the width */ michael@0: if(ISDIGIT(*s)){ michael@0: info->fWidth = (int) (*s++ - DIGIT_ZERO); michael@0: michael@0: while(ISDIGIT(*s)) { michael@0: info->fWidth *= 10; michael@0: info->fWidth += (int) (*s++ - DIGIT_ZERO); michael@0: } michael@0: } michael@0: michael@0: /* Get any modifiers */ michael@0: if(ISMOD(*s)) { michael@0: switch(*s++) { michael@0: michael@0: /* short */ michael@0: case MOD_H: michael@0: info->fIsShort = TRUE; michael@0: break; michael@0: michael@0: /* long or long long */ michael@0: case MOD_LOWERL: michael@0: if(*s == MOD_LOWERL) { michael@0: info->fIsLongLong = TRUE; michael@0: /* skip over the next 'l' */ michael@0: s++; michael@0: } michael@0: else michael@0: info->fIsLong = TRUE; michael@0: break; michael@0: michael@0: /* long double */ michael@0: case MOD_L: michael@0: info->fIsLongDouble = TRUE; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /* finally, get the specifier letter */ michael@0: info->fSpec = *s++; michael@0: michael@0: /* return # of characters in this specifier */ michael@0: return (int32_t)(s - fmt); michael@0: } michael@0: michael@0: #define UP_PERCENT 0x0025 michael@0: michael@0: michael@0: /* ANSI style formatting */ michael@0: /* Use US-ASCII characters only for formatting */ michael@0: michael@0: /* % */ michael@0: #define UFMT_SIMPLE_PERCENT {ufmt_simple_percent, u_scanf_simple_percent_handler} michael@0: /* s */ michael@0: #define UFMT_STRING {ufmt_string, u_scanf_string_handler} michael@0: /* c */ michael@0: #define UFMT_CHAR {ufmt_string, u_scanf_char_handler} michael@0: /* d, i */ michael@0: #define UFMT_INT {ufmt_int, u_scanf_integer_handler} michael@0: /* u */ michael@0: #define UFMT_UINT {ufmt_int, u_scanf_uinteger_handler} michael@0: /* o */ michael@0: #define UFMT_OCTAL {ufmt_int, u_scanf_octal_handler} michael@0: /* x, X */ michael@0: #define UFMT_HEX {ufmt_int, u_scanf_hex_handler} michael@0: /* f */ michael@0: #define UFMT_DOUBLE {ufmt_double, u_scanf_double_handler} michael@0: /* e, E */ michael@0: #define UFMT_SCIENTIFIC {ufmt_double, u_scanf_scientific_handler} michael@0: /* g, G */ michael@0: #define UFMT_SCIDBL {ufmt_double, u_scanf_scidbl_handler} michael@0: /* n */ michael@0: #define UFMT_COUNT {ufmt_count, u_scanf_count_handler} michael@0: /* [ */ michael@0: #define UFMT_SCANSET {ufmt_string, u_scanf_scanset_handler} michael@0: michael@0: /* non-ANSI extensions */ michael@0: /* Use US-ASCII characters only for formatting */ michael@0: michael@0: /* p */ michael@0: #define UFMT_POINTER {ufmt_pointer, u_scanf_pointer_handler} michael@0: /* V */ michael@0: #define UFMT_SPELLOUT {ufmt_double, u_scanf_spellout_handler} michael@0: /* P */ michael@0: #define UFMT_PERCENT {ufmt_double, u_scanf_percent_handler} michael@0: /* C K is old format */ michael@0: #define UFMT_UCHAR {ufmt_uchar, u_scanf_uchar_handler} michael@0: /* S U is old format */ michael@0: #define UFMT_USTRING {ufmt_ustring, u_scanf_ustring_handler} michael@0: michael@0: michael@0: #define UFMT_EMPTY {ufmt_empty, NULL} michael@0: michael@0: /** michael@0: * A u_scanf handler function. michael@0: * A u_scanf handler is responsible for handling a single u_scanf michael@0: * format specification, for example 'd' or 's'. michael@0: * @param stream The UFILE to which to write output. michael@0: * @param info A pointer to a u_scanf_spec_info struct containing michael@0: * information on the format specification. michael@0: * @param args A pointer to the argument data michael@0: * @param fmt A pointer to the first character in the format string michael@0: * following the spec. michael@0: * @param fmtConsumed On output, set to the number of characters consumed michael@0: * in fmt. Do nothing, if the argument isn't variable width. michael@0: * @param argConverted The number of arguments converted and assigned, or -1 if an michael@0: * error occurred. michael@0: * @return The number of code points consumed during reading. michael@0: */ michael@0: typedef int32_t (*u_scanf_handler) (UFILE *stream, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted); michael@0: michael@0: typedef struct u_scanf_info { michael@0: ufmt_type_info info; michael@0: u_scanf_handler handler; michael@0: } u_scanf_info; michael@0: michael@0: #define USCANF_NUM_FMT_HANDLERS 108 michael@0: #define USCANF_SYMBOL_BUFFER_SIZE 8 michael@0: michael@0: /* We do not use handlers for 0-0x1f */ michael@0: #define USCANF_BASE_FMT_HANDLERS 0x20 michael@0: michael@0: michael@0: static int32_t michael@0: u_scanf_skip_leading_ws(UFILE *input, michael@0: UChar pad) michael@0: { michael@0: UChar c; michael@0: int32_t count = 0; michael@0: UBool isNotEOF; michael@0: michael@0: /* skip all leading ws in the input */ michael@0: while( (isNotEOF = ufile_getch(input, &c)) && (c == pad || u_isWhitespace(c)) ) michael@0: { michael@0: count++; michael@0: } michael@0: michael@0: /* put the final character back on the input */ michael@0: if(isNotEOF) michael@0: u_fungetc(c, input); michael@0: michael@0: return count; michael@0: } michael@0: michael@0: /* TODO: Is always skipping the prefix symbol as a positive sign a good idea in all locales? */ michael@0: static int32_t michael@0: u_scanf_skip_leading_positive_sign(UFILE *input, michael@0: UNumberFormat *format, michael@0: UErrorCode *status) michael@0: { michael@0: UChar c; michael@0: int32_t count = 0; michael@0: UBool isNotEOF; michael@0: UChar plusSymbol[USCANF_SYMBOL_BUFFER_SIZE]; michael@0: int32_t symbolLen; michael@0: UErrorCode localStatus = U_ZERO_ERROR; michael@0: michael@0: if (U_SUCCESS(*status)) { michael@0: symbolLen = unum_getSymbol(format, michael@0: UNUM_PLUS_SIGN_SYMBOL, michael@0: plusSymbol, michael@0: sizeof(plusSymbol)/sizeof(*plusSymbol), michael@0: &localStatus); michael@0: michael@0: if (U_SUCCESS(localStatus)) { michael@0: /* skip all leading ws in the input */ michael@0: while( (isNotEOF = ufile_getch(input, &c)) && (count < symbolLen && c == plusSymbol[count]) ) michael@0: { michael@0: count++; michael@0: } michael@0: michael@0: /* put the final character back on the input */ michael@0: if(isNotEOF) { michael@0: u_fungetc(c, input); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return count; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_simple_percent_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: /* make sure the next character in the input is a percent */ michael@0: *argConverted = 0; michael@0: if(u_fgetc(input) != 0x0025) { michael@0: *argConverted = -1; michael@0: } michael@0: return 1; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_count_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: /* in the special case of count, the u_scanf_spec_info's width */ michael@0: /* will contain the # of items converted thus far */ michael@0: if (!info->fSkipArg) { michael@0: if (info->fIsShort) michael@0: *(int16_t*)(args[0].ptrValue) = (int16_t)(UINT16_MAX & info->fWidth); michael@0: else if (info->fIsLongLong) michael@0: *(int64_t*)(args[0].ptrValue) = info->fWidth; michael@0: else michael@0: *(int32_t*)(args[0].ptrValue) = (int32_t)(UINT32_MAX & info->fWidth); michael@0: } michael@0: *argConverted = 0; michael@0: michael@0: /* we converted 0 args */ michael@0: return 0; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_double_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: double num; michael@0: UNumberFormat *format; michael@0: int32_t parsePos = 0; michael@0: int32_t skipped; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: michael@0: michael@0: /* skip all ws in the input */ michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) michael@0: len = ufmt_min(len, info->fWidth); michael@0: michael@0: /* get the formatter */ michael@0: format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_DECIMAL); michael@0: michael@0: /* handle error */ michael@0: if(format == 0) michael@0: return 0; michael@0: michael@0: /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */ michael@0: skipped += u_scanf_skip_leading_positive_sign(input, format, &status); michael@0: michael@0: /* parse the number */ michael@0: num = unum_parseDouble(format, input->str.fPos, len, &parsePos, &status); michael@0: michael@0: if (!info->fSkipArg) { michael@0: if (info->fIsLong) michael@0: *(double*)(args[0].ptrValue) = num; michael@0: else if (info->fIsLongDouble) michael@0: *(long double*)(args[0].ptrValue) = num; michael@0: else michael@0: *(float*)(args[0].ptrValue) = (float)num; michael@0: } michael@0: michael@0: /* mask off any necessary bits */ michael@0: /* if(! info->fIsLong_double) michael@0: num &= DBL_MAX;*/ michael@0: michael@0: /* update the input's position to reflect consumed data */ michael@0: input->str.fPos += parsePos; michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return parsePos + skipped; michael@0: } michael@0: michael@0: #define UPRINTF_SYMBOL_BUFFER_SIZE 8 michael@0: michael@0: static int32_t michael@0: u_scanf_scientific_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: double num; michael@0: UNumberFormat *format; michael@0: int32_t parsePos = 0; michael@0: int32_t skipped; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: UChar srcExpBuf[UPRINTF_SYMBOL_BUFFER_SIZE]; michael@0: int32_t srcLen, expLen; michael@0: UChar expBuf[UPRINTF_SYMBOL_BUFFER_SIZE]; michael@0: michael@0: michael@0: /* skip all ws in the input */ michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) michael@0: len = ufmt_min(len, info->fWidth); michael@0: michael@0: /* get the formatter */ michael@0: format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_SCIENTIFIC); michael@0: michael@0: /* handle error */ michael@0: if(format == 0) michael@0: return 0; michael@0: michael@0: /* set the appropriate flags on the formatter */ michael@0: michael@0: srcLen = unum_getSymbol(format, michael@0: UNUM_EXPONENTIAL_SYMBOL, michael@0: srcExpBuf, michael@0: sizeof(srcExpBuf), michael@0: &status); michael@0: michael@0: /* Upper/lower case the e */ michael@0: if (info->fSpec == (UChar)0x65 /* e */) { michael@0: expLen = u_strToLower(expBuf, (int32_t)sizeof(expBuf), michael@0: srcExpBuf, srcLen, michael@0: input->str.fBundle.fLocale, michael@0: &status); michael@0: } michael@0: else { michael@0: expLen = u_strToUpper(expBuf, (int32_t)sizeof(expBuf), michael@0: srcExpBuf, srcLen, michael@0: input->str.fBundle.fLocale, michael@0: &status); michael@0: } michael@0: michael@0: unum_setSymbol(format, michael@0: UNUM_EXPONENTIAL_SYMBOL, michael@0: expBuf, michael@0: expLen, michael@0: &status); michael@0: michael@0: michael@0: michael@0: michael@0: /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */ michael@0: skipped += u_scanf_skip_leading_positive_sign(input, format, &status); michael@0: michael@0: /* parse the number */ michael@0: num = unum_parseDouble(format, input->str.fPos, len, &parsePos, &status); michael@0: michael@0: if (!info->fSkipArg) { michael@0: if (info->fIsLong) michael@0: *(double*)(args[0].ptrValue) = num; michael@0: else if (info->fIsLongDouble) michael@0: *(long double*)(args[0].ptrValue) = num; michael@0: else michael@0: *(float*)(args[0].ptrValue) = (float)num; michael@0: } michael@0: michael@0: /* mask off any necessary bits */ michael@0: /* if(! info->fIsLong_double) michael@0: num &= DBL_MAX;*/ michael@0: michael@0: /* update the input's position to reflect consumed data */ michael@0: input->str.fPos += parsePos; michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return parsePos + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_scidbl_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: double num; michael@0: UNumberFormat *scientificFormat, *genericFormat; michael@0: /*int32_t scientificResult, genericResult;*/ michael@0: double scientificResult, genericResult; michael@0: int32_t scientificParsePos = 0, genericParsePos = 0, parsePos = 0; michael@0: int32_t skipped; michael@0: UErrorCode scientificStatus = U_ZERO_ERROR; michael@0: UErrorCode genericStatus = U_ZERO_ERROR; michael@0: michael@0: michael@0: /* since we can't determine by scanning the characters whether */ michael@0: /* a number was formatted in the 'f' or 'g' styles, parse the */ michael@0: /* string with both formatters, and assume whichever one */ michael@0: /* parsed the most is the correct formatter to use */ michael@0: michael@0: michael@0: /* skip all ws in the input */ michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) michael@0: len = ufmt_min(len, info->fWidth); michael@0: michael@0: /* get the formatters */ michael@0: scientificFormat = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_SCIENTIFIC); michael@0: genericFormat = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_DECIMAL); michael@0: michael@0: /* handle error */ michael@0: if(scientificFormat == 0 || genericFormat == 0) michael@0: return 0; michael@0: michael@0: /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */ michael@0: skipped += u_scanf_skip_leading_positive_sign(input, genericFormat, &genericStatus); michael@0: michael@0: /* parse the number using each format*/ michael@0: michael@0: scientificResult = unum_parseDouble(scientificFormat, input->str.fPos, len, michael@0: &scientificParsePos, &scientificStatus); michael@0: michael@0: genericResult = unum_parseDouble(genericFormat, input->str.fPos, len, michael@0: &genericParsePos, &genericStatus); michael@0: michael@0: /* determine which parse made it farther */ michael@0: if(scientificParsePos > genericParsePos) { michael@0: /* stash the result in num */ michael@0: num = scientificResult; michael@0: /* update the input's position to reflect consumed data */ michael@0: parsePos += scientificParsePos; michael@0: } michael@0: else { michael@0: /* stash the result in num */ michael@0: num = genericResult; michael@0: /* update the input's position to reflect consumed data */ michael@0: parsePos += genericParsePos; michael@0: } michael@0: input->str.fPos += parsePos; michael@0: michael@0: if (!info->fSkipArg) { michael@0: if (info->fIsLong) michael@0: *(double*)(args[0].ptrValue) = num; michael@0: else if (info->fIsLongDouble) michael@0: *(long double*)(args[0].ptrValue) = num; michael@0: else michael@0: *(float*)(args[0].ptrValue) = (float)num; michael@0: } michael@0: michael@0: /* mask off any necessary bits */ michael@0: /* if(! info->fIsLong_double) michael@0: num &= DBL_MAX;*/ michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return parsePos + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_integer_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: void *num = (void*) (args[0].ptrValue); michael@0: UNumberFormat *format; michael@0: int32_t parsePos = 0; michael@0: int32_t skipped; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: int64_t result; michael@0: michael@0: michael@0: /* skip all ws in the input */ michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) michael@0: len = ufmt_min(len, info->fWidth); michael@0: michael@0: /* get the formatter */ michael@0: format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_DECIMAL); michael@0: michael@0: /* handle error */ michael@0: if(format == 0) michael@0: return 0; michael@0: michael@0: /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */ michael@0: skipped += u_scanf_skip_leading_positive_sign(input, format, &status); michael@0: michael@0: /* parse the number */ michael@0: result = unum_parseInt64(format, input->str.fPos, len, &parsePos, &status); michael@0: michael@0: /* mask off any necessary bits */ michael@0: if (!info->fSkipArg) { michael@0: if (info->fIsShort) michael@0: *(int16_t*)num = (int16_t)(UINT16_MAX & result); michael@0: else if (info->fIsLongLong) michael@0: *(int64_t*)num = result; michael@0: else michael@0: *(int32_t*)num = (int32_t)(UINT32_MAX & result); michael@0: } michael@0: michael@0: /* update the input's position to reflect consumed data */ michael@0: input->str.fPos += parsePos; michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return parsePos + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_uinteger_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: /* TODO Fix this when Numberformat handles uint64_t */ michael@0: return u_scanf_integer_handler(input, info, args, fmt, fmtConsumed, argConverted); michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_percent_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: double num; michael@0: UNumberFormat *format; michael@0: int32_t parsePos = 0; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: michael@0: michael@0: /* skip all ws in the input */ michael@0: u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) michael@0: len = ufmt_min(len, info->fWidth); michael@0: michael@0: /* get the formatter */ michael@0: format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_PERCENT); michael@0: michael@0: /* handle error */ michael@0: if(format == 0) michael@0: return 0; michael@0: michael@0: /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */ michael@0: u_scanf_skip_leading_positive_sign(input, format, &status); michael@0: michael@0: /* parse the number */ michael@0: num = unum_parseDouble(format, input->str.fPos, len, &parsePos, &status); michael@0: michael@0: if (!info->fSkipArg) { michael@0: *(double*)(args[0].ptrValue) = num; michael@0: } michael@0: michael@0: /* mask off any necessary bits */ michael@0: /* if(! info->fIsLong_double) michael@0: num &= DBL_MAX;*/ michael@0: michael@0: /* update the input's position to reflect consumed data */ michael@0: input->str.fPos += parsePos; michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return parsePos; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_string_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: const UChar *source; michael@0: UConverter *conv; michael@0: char *arg = (char*)(args[0].ptrValue); michael@0: char *alias = arg; michael@0: char *limit; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: int32_t count; michael@0: int32_t skipped = 0; michael@0: UChar c; michael@0: UBool isNotEOF = FALSE; michael@0: michael@0: /* skip all ws in the input */ michael@0: if (info->fIsString) { michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: } michael@0: michael@0: /* get the string one character at a time, truncating to the width */ michael@0: count = 0; michael@0: michael@0: /* open the default converter */ michael@0: conv = u_getDefaultConverter(&status); michael@0: michael@0: if(U_FAILURE(status)) michael@0: return -1; michael@0: michael@0: while( (info->fWidth == -1 || count < info->fWidth) michael@0: && (isNotEOF = ufile_getch(input, &c)) michael@0: && (!info->fIsString || (c != info->fPadChar && !u_isWhitespace(c)))) michael@0: { michael@0: michael@0: if (!info->fSkipArg) { michael@0: /* put the character from the input onto the target */ michael@0: source = &c; michael@0: /* Since we do this one character at a time, do it this way. */ michael@0: if (info->fWidth > 0) { michael@0: limit = alias + info->fWidth - count; michael@0: } michael@0: else { michael@0: limit = alias + ucnv_getMaxCharSize(conv); michael@0: } michael@0: michael@0: /* convert the character to the default codepage */ michael@0: ucnv_fromUnicode(conv, &alias, limit, &source, source + 1, michael@0: NULL, TRUE, &status); michael@0: michael@0: if(U_FAILURE(status)) { michael@0: /* clean up */ michael@0: u_releaseDefaultConverter(conv); michael@0: return -1; michael@0: } michael@0: } michael@0: michael@0: /* increment the count */ michael@0: ++count; michael@0: } michael@0: michael@0: /* put the final character we read back on the input */ michael@0: if (!info->fSkipArg) { michael@0: if ((info->fWidth == -1 || count < info->fWidth) && isNotEOF) michael@0: u_fungetc(c, input); michael@0: michael@0: /* add the terminator */ michael@0: if (info->fIsString) { michael@0: *alias = 0x00; michael@0: } michael@0: } michael@0: michael@0: /* clean up */ michael@0: u_releaseDefaultConverter(conv); michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return count + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_char_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: if (info->fWidth < 0) { michael@0: info->fWidth = 1; michael@0: } michael@0: info->fIsString = FALSE; michael@0: return u_scanf_string_handler(input, info, args, fmt, fmtConsumed, argConverted); michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_ustring_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: UChar *arg = (UChar*)(args[0].ptrValue); michael@0: UChar *alias = arg; michael@0: int32_t count; michael@0: int32_t skipped = 0; michael@0: UChar c; michael@0: UBool isNotEOF = FALSE; michael@0: michael@0: /* skip all ws in the input */ michael@0: if (info->fIsString) { michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: } michael@0: michael@0: /* get the string one character at a time, truncating to the width */ michael@0: count = 0; michael@0: michael@0: while( (info->fWidth == -1 || count < info->fWidth) michael@0: && (isNotEOF = ufile_getch(input, &c)) michael@0: && (!info->fIsString || (c != info->fPadChar && !u_isWhitespace(c)))) michael@0: { michael@0: michael@0: /* put the character from the input onto the target */ michael@0: if (!info->fSkipArg) { michael@0: *alias++ = c; michael@0: } michael@0: michael@0: /* increment the count */ michael@0: ++count; michael@0: } michael@0: michael@0: /* put the final character we read back on the input */ michael@0: if (!info->fSkipArg) { michael@0: if((info->fWidth == -1 || count < info->fWidth) && isNotEOF) { michael@0: u_fungetc(c, input); michael@0: } michael@0: michael@0: /* add the terminator */ michael@0: if (info->fIsString) { michael@0: *alias = 0x0000; michael@0: } michael@0: } michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return count + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_uchar_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: if (info->fWidth < 0) { michael@0: info->fWidth = 1; michael@0: } michael@0: info->fIsString = FALSE; michael@0: return u_scanf_ustring_handler(input, info, args, fmt, fmtConsumed, argConverted); michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_spellout_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: double num; michael@0: UNumberFormat *format; michael@0: int32_t parsePos = 0; michael@0: int32_t skipped; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: michael@0: michael@0: /* skip all ws in the input */ michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) michael@0: len = ufmt_min(len, info->fWidth); michael@0: michael@0: /* get the formatter */ michael@0: format = u_locbund_getNumberFormat(&input->str.fBundle, UNUM_SPELLOUT); michael@0: michael@0: /* handle error */ michael@0: if(format == 0) michael@0: return 0; michael@0: michael@0: /* Skip the positive prefix. ICU normally can't handle this due to strict parsing. */ michael@0: /* This is not applicable to RBNF. */ michael@0: /*skipped += u_scanf_skip_leading_positive_sign(input, format, &status);*/ michael@0: michael@0: /* parse the number */ michael@0: num = unum_parseDouble(format, input->str.fPos, len, &parsePos, &status); michael@0: michael@0: if (!info->fSkipArg) { michael@0: *(double*)(args[0].ptrValue) = num; michael@0: } michael@0: michael@0: /* mask off any necessary bits */ michael@0: /* if(! info->fIsLong_double) michael@0: num &= DBL_MAX;*/ michael@0: michael@0: /* update the input's position to reflect consumed data */ michael@0: input->str.fPos += parsePos; michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return parsePos + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_hex_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: int32_t skipped; michael@0: void *num = (void*) (args[0].ptrValue); michael@0: int64_t result; michael@0: michael@0: /* skip all ws in the input */ michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) michael@0: len = ufmt_min(len, info->fWidth); michael@0: michael@0: /* check for alternate form */ michael@0: if( *(input->str.fPos) == 0x0030 && michael@0: (*(input->str.fPos + 1) == 0x0078 || *(input->str.fPos + 1) == 0x0058) ) { michael@0: michael@0: /* skip the '0' and 'x' or 'X' if present */ michael@0: input->str.fPos += 2; michael@0: len -= 2; michael@0: } michael@0: michael@0: /* parse the number */ michael@0: result = ufmt_uto64(input->str.fPos, &len, 16); michael@0: michael@0: /* update the input's position to reflect consumed data */ michael@0: input->str.fPos += len; michael@0: michael@0: /* mask off any necessary bits */ michael@0: if (!info->fSkipArg) { michael@0: if (info->fIsShort) michael@0: *(int16_t*)num = (int16_t)(UINT16_MAX & result); michael@0: else if (info->fIsLongLong) michael@0: *(int64_t*)num = result; michael@0: else michael@0: *(int32_t*)num = (int32_t)(UINT32_MAX & result); michael@0: } michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return len + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_octal_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: int32_t skipped; michael@0: void *num = (void*) (args[0].ptrValue); michael@0: int64_t result; michael@0: michael@0: /* skip all ws in the input */ michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) michael@0: len = ufmt_min(len, info->fWidth); michael@0: michael@0: /* parse the number */ michael@0: result = ufmt_uto64(input->str.fPos, &len, 8); michael@0: michael@0: /* update the input's position to reflect consumed data */ michael@0: input->str.fPos += len; michael@0: michael@0: /* mask off any necessary bits */ michael@0: if (!info->fSkipArg) { michael@0: if (info->fIsShort) michael@0: *(int16_t*)num = (int16_t)(UINT16_MAX & result); michael@0: else if (info->fIsLongLong) michael@0: *(int64_t*)num = result; michael@0: else michael@0: *(int32_t*)num = (int32_t)(UINT32_MAX & result); michael@0: } michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return len + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_pointer_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: int32_t len; michael@0: int32_t skipped; michael@0: void *result; michael@0: void **p = (void**)(args[0].ptrValue); michael@0: michael@0: michael@0: /* skip all ws in the input */ michael@0: skipped = u_scanf_skip_leading_ws(input, info->fPadChar); michael@0: michael@0: /* fill the input's internal buffer */ michael@0: ufile_fill_uchar_buffer(input); michael@0: michael@0: /* determine the size of the input's buffer */ michael@0: len = (int32_t)(input->str.fLimit - input->str.fPos); michael@0: michael@0: /* truncate to the width, if specified */ michael@0: if(info->fWidth != -1) { michael@0: len = ufmt_min(len, info->fWidth); michael@0: } michael@0: michael@0: /* Make sure that we don't consume too much */ michael@0: if (len > (int32_t)(sizeof(void*)*2)) { michael@0: len = (int32_t)(sizeof(void*)*2); michael@0: } michael@0: michael@0: /* parse the pointer - assign to temporary value */ michael@0: result = ufmt_utop(input->str.fPos, &len); michael@0: michael@0: if (!info->fSkipArg) { michael@0: *p = result; michael@0: } michael@0: michael@0: /* update the input's position to reflect consumed data */ michael@0: input->str.fPos += len; michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return len + skipped; michael@0: } michael@0: michael@0: static int32_t michael@0: u_scanf_scanset_handler(UFILE *input, michael@0: u_scanf_spec_info *info, michael@0: ufmt_args *args, michael@0: const UChar *fmt, michael@0: int32_t *fmtConsumed, michael@0: int32_t *argConverted) michael@0: { michael@0: USet *scanset; michael@0: UErrorCode status = U_ZERO_ERROR; michael@0: int32_t chLeft = INT32_MAX; michael@0: UChar32 c; michael@0: UChar *alias = (UChar*) (args[0].ptrValue); michael@0: UBool isNotEOF = FALSE; michael@0: UBool readCharacter = FALSE; michael@0: michael@0: /* Create an empty set */ michael@0: scanset = uset_open(0, -1); michael@0: michael@0: /* Back up one to get the [ */ michael@0: fmt--; michael@0: michael@0: /* truncate to the width, if specified and alias the target */ michael@0: if(info->fWidth >= 0) { michael@0: chLeft = info->fWidth; michael@0: } michael@0: michael@0: /* parse the scanset from the fmt string */ michael@0: *fmtConsumed = uset_applyPattern(scanset, fmt, -1, 0, &status); michael@0: michael@0: /* verify that the parse was successful */ michael@0: if (U_SUCCESS(status)) { michael@0: c=0; michael@0: michael@0: /* grab characters one at a time and make sure they are in the scanset */ michael@0: while(chLeft > 0) { michael@0: if ((isNotEOF = ufile_getch32(input, &c)) && uset_contains(scanset, c)) { michael@0: readCharacter = TRUE; michael@0: if (!info->fSkipArg) { michael@0: int32_t idx = 0; michael@0: UBool isError = FALSE; michael@0: michael@0: U16_APPEND(alias, idx, chLeft, c, isError); michael@0: if (isError) { michael@0: break; michael@0: } michael@0: alias += idx; michael@0: } michael@0: chLeft -= (1 + U_IS_SUPPLEMENTARY(c)); michael@0: } michael@0: else { michael@0: /* if the character's not in the scanset, break out */ michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /* put the final character we read back on the input */ michael@0: if(isNotEOF && chLeft > 0) { michael@0: u_fungetc(c, input); michael@0: } michael@0: } michael@0: michael@0: uset_close(scanset); michael@0: michael@0: /* if we didn't match at least 1 character, fail */ michael@0: if(!readCharacter) michael@0: return -1; michael@0: /* otherwise, add the terminator */ michael@0: else if (!info->fSkipArg) { michael@0: *alias = 0x00; michael@0: } michael@0: michael@0: /* we converted 1 arg */ michael@0: *argConverted = !info->fSkipArg; michael@0: return (info->fWidth >= 0 ? info->fWidth : INT32_MAX) - chLeft; michael@0: } michael@0: michael@0: /* Use US-ASCII characters only for formatting. Most codepages have michael@0: characters 20-7F from Unicode. Using any other codepage specific michael@0: characters will make it very difficult to format the string on michael@0: non-Unicode machines */ michael@0: static const u_scanf_info g_u_scanf_infos[USCANF_NUM_FMT_HANDLERS] = { michael@0: /* 0x20 */ michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_EMPTY, UFMT_SIMPLE_PERCENT,UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: michael@0: /* 0x30 */ michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: michael@0: /* 0x40 */ michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_UCHAR, michael@0: UFMT_EMPTY, UFMT_SCIENTIFIC, UFMT_EMPTY, UFMT_SCIDBL, michael@0: #ifdef U_USE_OBSOLETE_IO_FORMATTING michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_UCHAR/*deprecated*/, michael@0: #else michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: #endif michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: michael@0: /* 0x50 */ michael@0: UFMT_PERCENT, UFMT_EMPTY, UFMT_EMPTY, UFMT_USTRING, michael@0: #ifdef U_USE_OBSOLETE_IO_FORMATTING michael@0: UFMT_EMPTY, UFMT_USTRING/*deprecated*/,UFMT_SPELLOUT, UFMT_EMPTY, michael@0: #else michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_SPELLOUT, UFMT_EMPTY, michael@0: #endif michael@0: UFMT_HEX, UFMT_EMPTY, UFMT_EMPTY, UFMT_SCANSET, michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: michael@0: /* 0x60 */ michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_CHAR, michael@0: UFMT_INT, UFMT_SCIENTIFIC, UFMT_DOUBLE, UFMT_SCIDBL, michael@0: UFMT_EMPTY, UFMT_INT, UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_COUNT, UFMT_OCTAL, michael@0: michael@0: /* 0x70 */ michael@0: UFMT_POINTER, UFMT_EMPTY, UFMT_EMPTY, UFMT_STRING, michael@0: UFMT_EMPTY, UFMT_UINT, UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_HEX, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, UFMT_EMPTY, michael@0: }; michael@0: michael@0: U_CFUNC int32_t michael@0: u_scanf_parse(UFILE *f, michael@0: const UChar *patternSpecification, michael@0: va_list ap) michael@0: { michael@0: const UChar *alias; michael@0: int32_t count, converted, argConsumed, cpConsumed; michael@0: uint16_t handlerNum; michael@0: michael@0: ufmt_args args; michael@0: u_scanf_spec spec; michael@0: ufmt_type_info info; michael@0: u_scanf_handler handler; michael@0: michael@0: /* alias the pattern */ michael@0: alias = patternSpecification; michael@0: michael@0: /* haven't converted anything yet */ michael@0: argConsumed = 0; michael@0: converted = 0; michael@0: cpConsumed = 0; michael@0: michael@0: /* iterate through the pattern */ michael@0: for(;;) { michael@0: michael@0: /* match any characters up to the next '%' */ michael@0: while(*alias != UP_PERCENT && *alias != 0x0000 && u_fgetc(f) == *alias) { michael@0: alias++; michael@0: } michael@0: michael@0: /* if we aren't at a '%', or if we're at end of string, break*/ michael@0: if(*alias != UP_PERCENT || *alias == 0x0000) michael@0: break; michael@0: michael@0: /* parse the specifier */ michael@0: count = u_scanf_parse_spec(alias, &spec); michael@0: michael@0: /* update the pointer in pattern */ michael@0: alias += count; michael@0: michael@0: handlerNum = (uint16_t)(spec.fInfo.fSpec - USCANF_BASE_FMT_HANDLERS); michael@0: if (handlerNum < USCANF_NUM_FMT_HANDLERS) { michael@0: /* skip the argument, if necessary */ michael@0: /* query the info function for argument information */ michael@0: info = g_u_scanf_infos[ handlerNum ].info; michael@0: if (info != ufmt_count && u_feof(f)) { michael@0: break; michael@0: } michael@0: else if(spec.fInfo.fSkipArg) { michael@0: args.ptrValue = NULL; michael@0: } michael@0: else { michael@0: switch(info) { michael@0: case ufmt_count: michael@0: /* set the spec's width to the # of items converted */ michael@0: spec.fInfo.fWidth = cpConsumed; michael@0: /* fall through to next case */ michael@0: case ufmt_char: michael@0: case ufmt_uchar: michael@0: case ufmt_int: michael@0: case ufmt_string: michael@0: case ufmt_ustring: michael@0: case ufmt_pointer: michael@0: case ufmt_float: michael@0: case ufmt_double: michael@0: args.ptrValue = va_arg(ap, void*); michael@0: break; michael@0: michael@0: default: michael@0: /* else args is ignored */ michael@0: args.ptrValue = NULL; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /* call the handler function */ michael@0: handler = g_u_scanf_infos[ handlerNum ].handler; michael@0: if(handler != 0) { michael@0: michael@0: /* reset count to 1 so that += for alias works. */ michael@0: count = 1; michael@0: michael@0: cpConsumed += (*handler)(f, &spec.fInfo, &args, alias, &count, &argConsumed); michael@0: michael@0: /* if the handler encountered an error condition, break */ michael@0: if(argConsumed < 0) { michael@0: converted = -1; michael@0: break; michael@0: } michael@0: michael@0: /* add to the # of items converted */ michael@0: converted += argConsumed; michael@0: michael@0: /* update the pointer in pattern */ michael@0: alias += count-1; michael@0: } michael@0: /* else do nothing */ michael@0: } michael@0: /* else do nothing */ michael@0: michael@0: /* just ignore unknown tags */ michael@0: } michael@0: michael@0: /* return # of items converted */ michael@0: return converted; michael@0: } michael@0: michael@0: #endif /* #if !UCONFIG_NO_FORMATTING */