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