| /* |
| ******************************************************************************* |
| * |
| * Copyright (C) 1998-1999, International Business Machines |
| * Corporation and others. All Rights Reserved. |
| * |
| ******************************************************************************* |
| * |
| * File uscanf.c |
| * |
| * Modification History: |
| * |
| * Date Name Description |
| * 12/02/98 stephen Creation. |
| * 03/13/99 stephen Modified for new C API. |
| ******************************************************************************* |
| */ |
| |
| #include "unicode/utypes.h" |
| #include "unicode/uchar.h" |
| |
| #include "uscanf.h" |
| #include "uscanf_p.h" |
| #include "uscanset.h" |
| #include "unicode/ustdio.h" |
| #include "ufile.h" |
| #include "unicode/ustring.h" |
| #include "locbund.h" |
| #include "umutex.h" |
| #include "unicode/unum.h" |
| #include "unicode/udat.h" |
| |
| #include <string.h> |
| #include <stdlib.h> |
| #include <float.h> |
| #include <limits.h> |
| |
| static u_scanf_handler g_u_scanf_handlers [256]; |
| static u_scanf_info g_u_scanf_infos [256]; |
| static UBool g_u_scanf_inited = FALSE; |
| |
| int32_t |
| u_fscanf( UFILE *f, |
| const char *patternSpecification, |
| ... ) |
| { |
| va_list ap; |
| int32_t converted; |
| |
| va_start(ap, patternSpecification); |
| converted = u_vfscanf(f, patternSpecification, ap); |
| va_end(ap); |
| |
| return converted; |
| } |
| |
| int32_t |
| u_fscanf_u( UFILE *f, |
| const UChar *patternSpecification, |
| ... ) |
| { |
| va_list ap; |
| int32_t converted; |
| |
| va_start(ap, patternSpecification); |
| converted = u_vfscanf_u(f, patternSpecification, ap); |
| va_end(ap); |
| |
| return converted; |
| } |
| |
| int32_t |
| u_vfscanf( UFILE *f, |
| const char *patternSpecification, |
| va_list ap) |
| { |
| int32_t converted; |
| UChar *pattern; |
| |
| /* convert from the default codepage to Unicode */ |
| pattern = ufmt_defaultCPToUnicode(patternSpecification, |
| strlen(patternSpecification)); |
| if(pattern == 0) { |
| return 0; |
| } |
| |
| /* do the work */ |
| converted = u_vfscanf_u(f, pattern, ap); |
| |
| /* clean up */ |
| free(pattern); |
| |
| return converted; |
| } |
| |
| int32_t |
| u_scanf_register_handler (UChar spec, |
| u_scanf_info info, |
| u_scanf_handler handler) |
| { |
| /* lock the cache */ |
| umtx_lock(0); |
| |
| /* add to our list of function pointers */ |
| g_u_scanf_infos[ (unsigned char) spec ] = info; |
| g_u_scanf_handlers[ (unsigned char) spec ] = handler; |
| |
| /* unlock the cache */ |
| umtx_unlock(0); |
| return 0; |
| } |
| |
| int32_t |
| u_scanf_skip_leading_ws(UFILE *stream, |
| UChar pad) |
| { |
| UChar c; |
| int32_t count = 0; |
| |
| /* skip all leading ws in the stream */ |
| while( ((c = u_fgetc(stream)) != 0xFFFF) && (c == pad || ufmt_isws(c)) ) |
| ++count; |
| |
| /* put the final character back on the stream */ |
| if(c != 0xFFFF) |
| u_fungetc(c, stream); |
| |
| return count; |
| } |
| |
| int32_t |
| u_scanf_simple_percent_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* we don't need any arguments */ |
| return 0; |
| } |
| |
| int32_t |
| u_scanf_simple_percent_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| /* make sure the next character in the stream is a percent */ |
| if(u_fgetc(stream) != 0x0025) |
| return -1; |
| else |
| return 0; |
| } |
| |
| int32_t |
| u_scanf_string_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type string */ |
| argtypes[0] = ufmt_string; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_string_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| UChar c; |
| int32_t count; |
| const UChar *source; |
| UConverter *conv; |
| UErrorCode status = U_ZERO_ERROR; |
| char *arg = (char*)(args[0].ptrValue); |
| char *alias = arg; |
| char *limit; |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* get the string one character at a time, truncating to the width */ |
| count = 0; |
| |
| /* open the default converter */ |
| conv = ucnv_open(ucnv_getDefaultName(), &status); |
| |
| if(U_FAILURE(status)) |
| return -1; |
| |
| /* since there is no real limit, just use a reasonable value */ |
| limit = alias + 2048; |
| |
| while( ((c = u_fgetc(stream)) != 0xFFFF) && |
| (c != info->fPadChar && ! ufmt_isws(c)) && |
| (info->fWidth == -1 || count < info->fWidth) ) { |
| |
| /* put the character from the stream onto the target */ |
| source = &c; |
| |
| /* convert the character to the default codepage */ |
| ucnv_fromUnicode(conv, &alias, limit, &source, source + 1, |
| NULL, TRUE, &status); |
| |
| if(U_FAILURE(status)) |
| return -1; |
| |
| /* increment the count */ |
| ++count; |
| } |
| |
| /* put the final character we read back on the stream */ |
| if(c != 0xFFFF) |
| u_fungetc(c, stream); |
| |
| /* add the terminator */ |
| *alias = 0x00; |
| |
| /* clean up */ |
| ucnv_close(conv); |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_ustring_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type ustring */ |
| argtypes[0] = ufmt_ustring; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_ustring_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| UChar c; |
| int32_t count; |
| UChar *arg = (UChar*)(args[0].ptrValue); |
| UChar *alias = arg; |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* get the string one character at a time, truncating to the width */ |
| count = 0; |
| |
| while( ((c = u_fgetc(stream)) != 0xFFFF) && |
| (c != info->fPadChar && ! ufmt_isws(c)) && |
| (info->fWidth == -1 || count < info->fWidth) ) { |
| |
| /* put the character from the stream onto the target */ |
| *alias++ = c; |
| |
| /* increment the count */ |
| ++count; |
| } |
| |
| /* put the final character we read back on the stream */ |
| if(c != 0xFFFF) |
| u_fungetc(c, stream); |
| |
| /* add the terminator */ |
| *alias = 0x0000; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| |
| int32_t |
| u_scanf_count_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type count */ |
| argtypes[0] = ufmt_count; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_count_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int *converted = (int*)(args[0].ptrValue); |
| |
| /* in the special case of count, the u_scanf_spec_info's width */ |
| /* will contain the # of items converted thus far */ |
| *converted = info->fWidth; |
| |
| /* we converted 0 args */ |
| return 0; |
| } |
| |
| int32_t |
| u_scanf_integer_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type int */ |
| argtypes[0] = ufmt_int; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_integer_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| long *num = (long*) (args[0].ptrValue); |
| UNumberFormat *format; |
| int32_t parsePos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatter */ |
| format = u_locbund_getNumberFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(format == 0) |
| return 0; |
| |
| /* parse the number */ |
| *num = unum_parse(format, stream->fUCPos, len, &parsePos, &status); |
| |
| /* mask off any necessary bits */ |
| if(info->fIsShort) |
| *num &= SHRT_MAX; |
| else if(! info->fIsLong || ! info->fIsLongLong) |
| *num &= INT_MAX; |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += parsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_double_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type double */ |
| argtypes[0] = ufmt_double; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_double_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| double *num = (double*) (args[0].ptrValue); |
| UNumberFormat *format; |
| int32_t parsePos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatter */ |
| format = u_locbund_getNumberFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(format == 0) |
| return 0; |
| |
| /* parse the number */ |
| *num = unum_parseDouble(format, stream->fUCPos, len, &parsePos, &status); |
| |
| /* mask off any necessary bits */ |
| /* if(! info->fIsLong_double) |
| num &= DBL_MAX;*/ |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += parsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_scientific_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type double */ |
| argtypes[0] = ufmt_double; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_scientific_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| double *num = (double*) (args[0].ptrValue); |
| UNumberFormat *format; |
| int32_t parsePos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatter */ |
| format = u_locbund_getScientificFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(format == 0) |
| return 0; |
| |
| /* parse the number */ |
| *num = unum_parseDouble(format, stream->fUCPos, len, &parsePos, &status); |
| |
| /* mask off any necessary bits */ |
| /* if(! info->fIsLong_double) |
| num &= DBL_MAX;*/ |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += parsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_scidbl_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type double */ |
| argtypes[0] = ufmt_double; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_scidbl_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| double *num = (double*) (args[0].ptrValue); |
| UNumberFormat *scientificFormat, *genericFormat; |
| /*int32_t scientificResult, genericResult;*/ |
| double scientificResult, genericResult; |
| int32_t scientificParsePos = 0, genericParsePos = 0; |
| UErrorCode scientificStatus = U_ZERO_ERROR; |
| UErrorCode genericStatus = U_ZERO_ERROR; |
| UBool useScientific; |
| |
| |
| /* 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 stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatters */ |
| scientificFormat = u_locbund_getScientificFormat(stream->fBundle); |
| genericFormat = u_locbund_getNumberFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(scientificFormat == 0 || genericFormat == 0) |
| return 0; |
| |
| /* parse the number using each format*/ |
| |
| scientificResult = unum_parseDouble(scientificFormat, stream->fUCPos, len, |
| &scientificParsePos, &scientificStatus); |
| |
| genericResult = unum_parseDouble(genericFormat, stream->fUCPos, len, |
| &genericParsePos, &genericStatus); |
| |
| /* determine which parse made it farther */ |
| useScientific = (UBool)(scientificParsePos > genericParsePos); |
| |
| /* stash the result in num */ |
| *num = useScientific ? scientificResult : genericResult; |
| |
| /* mask off any necessary bits */ |
| /* if(! info->fIsLong_double) |
| num &= DBL_MAX;*/ |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += useScientific ? scientificParsePos : genericParsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_currency_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type double */ |
| argtypes[0] = ufmt_double; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_currency_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| double *num = (double*) (args[0].ptrValue); |
| UNumberFormat *format; |
| int32_t parsePos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatter */ |
| format = u_locbund_getCurrencyFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(format == 0) |
| return 0; |
| |
| /* parse the number */ |
| *num = unum_parseDouble(format, stream->fUCPos, len, &parsePos, &status); |
| |
| /* mask off any necessary bits */ |
| /* if(! info->fIsLong_double) |
| num &= DBL_MAX;*/ |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += parsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_percent_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type double */ |
| argtypes[0] = ufmt_double; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_percent_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| double *num = (double*) (args[0].ptrValue); |
| UNumberFormat *format; |
| int32_t parsePos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatter */ |
| format = u_locbund_getPercentFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(format == 0) |
| return 0; |
| |
| /* parse the number */ |
| *num = unum_parseDouble(format, stream->fUCPos, len, &parsePos, &status); |
| |
| /* mask off any necessary bits */ |
| /* if(! info->fIsLong_double) |
| num &= DBL_MAX;*/ |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += parsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_date_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type Date */ |
| argtypes[0] = ufmt_date; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_date_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| UDate *date = (UDate*) (args[0].ptrValue); |
| UDateFormat *format; |
| int32_t parsePos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatter */ |
| format = u_locbund_getDateFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(format == 0) |
| return 0; |
| |
| /* parse the number */ |
| *date = udat_parse(format, stream->fUCPos, len, &parsePos, &status); |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += parsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_time_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type Date */ |
| argtypes[0] = ufmt_date; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_time_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| UDate *time = (UDate*) (args[0].ptrValue); |
| UDateFormat *format; |
| int32_t parsePos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatter */ |
| format = u_locbund_getTimeFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(format == 0) |
| return 0; |
| |
| /* parse the number */ |
| *time = udat_parse(format, stream->fUCPos, len, &parsePos, &status); |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += parsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_char_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type char */ |
| argtypes[0] = ufmt_char; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_char_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| UChar uc = 0; |
| char *result; |
| char *c = (char*)(args[0].ptrValue); |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* get the character from the stream, truncating to the width */ |
| if(info->fWidth == -1 || info->fWidth > 1) |
| uc = u_fgetc(stream); |
| |
| /* handle EOF */ |
| if(uc == 0xFFFF) |
| return -1; |
| |
| /* convert the character to the default codepage */ |
| result = ufmt_unicodeToDefaultCP(&uc, 1); |
| *c = result[0]; |
| |
| /* clean up */ |
| free(result); |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_uchar_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type uchar */ |
| argtypes[0] = ufmt_uchar; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_uchar_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| UChar *c = (UChar*)(args[0].ptrValue); |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* get the character from the stream, truncating to the width */ |
| if(info->fWidth == -1 || info->fWidth > 1) |
| *c = u_fgetc(stream); |
| |
| /* handle EOF */ |
| if(*c == 0xFFFF) |
| return -1; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_spellout_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type double */ |
| argtypes[0] = ufmt_double; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_spellout_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| double *num = (double*) (args[0].ptrValue); |
| UNumberFormat *format; |
| int32_t parsePos = 0; |
| UErrorCode status = U_ZERO_ERROR; |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* get the formatter */ |
| format = u_locbund_getSpelloutFormat(stream->fBundle); |
| |
| /* handle error */ |
| if(format == 0) |
| return 0; |
| |
| /* parse the number */ |
| *num = unum_parseDouble(format, stream->fUCPos, len, &parsePos, &status); |
| |
| /* mask off any necessary bits */ |
| /* if(! info->fIsLong_double) |
| num &= DBL_MAX;*/ |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += parsePos; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_hex_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type int */ |
| argtypes[0] = ufmt_int; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_hex_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| long *num = (long*) (args[0].ptrValue); |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* check for alternate form */ |
| if( *(stream->fUCPos) == 0x0030 && |
| (*(stream->fUCPos + 1) == 0x0078 || *(stream->fUCPos + 1) == 0x0058) ) { |
| |
| /* skip the '0' and 'x' or 'X' if present */ |
| stream->fUCPos += 2; |
| len -= 2; |
| } |
| |
| /* parse the number */ |
| *num = ufmt_utol(stream->fUCPos, &len, 16); |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += len; |
| |
| /* mask off any necessary bits */ |
| if(info->fIsShort) |
| *num &= SHRT_MAX; |
| else if(! info->fIsLong || ! info->fIsLongLong) |
| *num &= INT_MAX; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_octal_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type int */ |
| argtypes[0] = ufmt_int; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_octal_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| long *num = (long*) (args[0].ptrValue); |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* parse the number */ |
| *num = ufmt_utol(stream->fUCPos, &len, 8); |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += len; |
| |
| /* mask off any necessary bits */ |
| if(info->fIsShort) |
| *num &= SHRT_MAX; |
| else if(! info->fIsLong || ! info->fIsLongLong) |
| *num &= INT_MAX; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_pointer_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type void* */ |
| argtypes[0] = ufmt_pointer; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_pointer_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| int32_t len; |
| void *p = (void*)(args[0].ptrValue); |
| |
| |
| /* skip all ws in the stream */ |
| u_scanf_skip_leading_ws(stream, info->fPadChar); |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* parse the pointer - cast to void** to assign to *p */ |
| *(void**)p = (void*) ufmt_utol(stream->fUCPos, &len, 16); |
| |
| /* update the stream's position to reflect consumed data */ |
| stream->fUCPos += len; |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_scanset_info(const u_scanf_spec_info *info, |
| int32_t *argtypes, |
| int32_t n) |
| { |
| /* handle error */ |
| if(n < 1) |
| return 0; |
| |
| /* we need 1 argument of type char* */ |
| argtypes[0] = ufmt_string; |
| return 1; |
| } |
| |
| int32_t |
| u_scanf_scanset_handler(UFILE *stream, |
| const u_scanf_spec_info *info, |
| ufmt_args *args, |
| const UChar *fmt, |
| int32_t *consumed) |
| { |
| u_scanf_scanset scanset; |
| int32_t len; |
| UBool success; |
| UChar c; |
| const UChar *source; |
| UConverter *conv; |
| UErrorCode status = U_ZERO_ERROR; |
| char *s = (char*) (args[0].ptrValue); |
| char *alias, *limit; |
| |
| |
| /* fill the stream's internal buffer */ |
| ufile_fill_uchar_buffer(stream); |
| |
| /* determine the size of the stream's buffer */ |
| len = stream->fUCLimit - stream->fUCPos; |
| |
| /* truncate to the width, if specified */ |
| if(info->fWidth != -1) |
| len = ufmt_min(len, info->fWidth); |
| |
| /* alias the target */ |
| alias = s; |
| limit = alias + len; |
| |
| /* parse the scanset from the fmt string */ |
| *consumed = u_strlen(fmt); |
| success = u_scanf_scanset_init(&scanset, fmt, consumed); |
| |
| /* increment consumed by one to eat the final ']' */ |
| ++(*consumed); |
| |
| /* open the default converter */ |
| conv = ucnv_open(ucnv_getDefaultName(), &status); |
| |
| /* verify that the parse was successful and the converter opened */ |
| if(! success || U_FAILURE(status)) |
| return -1; |
| |
| /* grab characters one at a time and make sure they are in the scanset */ |
| while( (c = u_fgetc(stream)) != 0xFFFF && alias < limit) { |
| if(u_scanf_scanset_in(&scanset, c)) { |
| source = &c; |
| /* convert the character to the default codepage */ |
| ucnv_fromUnicode(conv, &alias, limit, &source, source + 1, |
| NULL, TRUE, &status); |
| |
| if(U_FAILURE(status)) |
| return -1; |
| } |
| /* if the character's not in the scanset, break out */ |
| else { |
| break; |
| } |
| } |
| |
| /* put the final character we read back on the stream */ |
| if(c != 0xFFFF) |
| u_fungetc(c, stream); |
| |
| /* if we didn't match at least 1 character, fail */ |
| if(alias == s) |
| return -1; |
| /* otherwise, add the terminator */ |
| else |
| *alias = 0x00; |
| |
| /* clean up */ |
| ucnv_close(conv); |
| |
| /* we converted 1 arg */ |
| return 1; |
| } |
| |
| void |
| u_scanf_init(void) |
| { |
| int32_t i; |
| /*Mutex *lock;*/ |
| |
| /* if we're already inited, do nothing */ |
| if(g_u_scanf_inited) |
| return; |
| |
| /* lock the cache */ |
| umtx_lock(0); |
| |
| /* if we're already inited, do nothing */ |
| if(g_u_scanf_inited) { |
| umtx_unlock(0); |
| return; |
| } |
| |
| /* initialize all handlers and infos to 0 */ |
| for(i = 0; i < 256; ++i) { |
| g_u_scanf_infos[i] = 0; |
| g_u_scanf_handlers[i] = 0; |
| } |
| |
| /* register the handlers for standard specifiers */ |
| /* don't use u_scanf_register_handler to avoid mutex creation */ |
| |
| /* handle '%' */ |
| g_u_scanf_infos[ 0x0025 ] = u_scanf_simple_percent_info; |
| g_u_scanf_handlers[ 0x0025 ] = u_scanf_simple_percent_handler; |
| |
| /* handle 's' */ |
| g_u_scanf_infos[ 0x0073 ] = u_scanf_string_info; |
| g_u_scanf_handlers[ 0x0073 ] = u_scanf_string_handler; |
| |
| /* handle 'U' */ |
| g_u_scanf_infos[ 0x0055 ] = u_scanf_ustring_info; |
| g_u_scanf_handlers[ 0x0055 ] = u_scanf_ustring_handler; |
| |
| /* handle 'n' */ |
| g_u_scanf_infos[ 0x006E ] = u_scanf_count_info; |
| g_u_scanf_handlers[ 0x006E ] = u_scanf_count_handler; |
| |
| /* handle 'd' */ |
| g_u_scanf_infos[ 0x0064 ] = u_scanf_integer_info; |
| g_u_scanf_handlers[ 0x0064 ] = u_scanf_integer_handler; |
| |
| /* handle 'i' */ |
| g_u_scanf_infos[ 0x0069 ] = u_scanf_integer_info; |
| g_u_scanf_handlers[ 0x0069 ] = u_scanf_integer_handler; |
| |
| /* handle 'u' */ |
| g_u_scanf_infos[ 0x0075 ] = u_scanf_integer_info; |
| g_u_scanf_handlers[ 0x0075 ] = u_scanf_integer_handler; |
| |
| /* handle 'f' */ |
| g_u_scanf_infos[ 0x0066 ] = u_scanf_double_info; |
| g_u_scanf_handlers[ 0x0066 ] = u_scanf_double_handler; |
| |
| /* handle 'e' */ |
| g_u_scanf_infos[ 0x0065 ] = u_scanf_scientific_info; |
| g_u_scanf_handlers[ 0x0065 ] = u_scanf_scientific_handler; |
| |
| /* handle 'E' */ |
| g_u_scanf_infos[ 0x0045 ] = u_scanf_scientific_info; |
| g_u_scanf_handlers[ 0x0045 ] = u_scanf_scientific_handler; |
| |
| /* handle 'g' */ |
| g_u_scanf_infos[ 0x0067 ] = u_scanf_scidbl_info; |
| g_u_scanf_handlers[ 0x0067 ] = u_scanf_scidbl_handler; |
| |
| /* handle 'G' */ |
| g_u_scanf_infos[ 0x0047 ] = u_scanf_scidbl_info; |
| g_u_scanf_handlers[ 0x0047 ] = u_scanf_scidbl_handler; |
| |
| /* handle 'M' */ |
| g_u_scanf_infos[ 0x004D ] = u_scanf_currency_info; |
| g_u_scanf_handlers[ 0x004D ] = u_scanf_currency_handler; |
| |
| /* handle 'P' */ |
| g_u_scanf_infos[ 0x0050 ] = u_scanf_percent_info; |
| g_u_scanf_handlers[ 0x0050 ] = u_scanf_percent_handler; |
| |
| /* handle 'D' */ |
| g_u_scanf_infos[ 0x0044 ] = u_scanf_date_info; |
| g_u_scanf_handlers[ 0x0044 ] = u_scanf_date_handler; |
| |
| /* handle 'T' */ |
| g_u_scanf_infos[ 0x0054 ] = u_scanf_time_info; |
| g_u_scanf_handlers[ 0x0054 ] = u_scanf_time_handler; |
| |
| /* handle 'c' */ |
| g_u_scanf_infos[ 0x0063 ] = u_scanf_char_info; |
| g_u_scanf_handlers[ 0x0063 ] = u_scanf_char_handler; |
| |
| /* handle 'K' */ |
| g_u_scanf_infos[ 0x004B ] = u_scanf_uchar_info; |
| g_u_scanf_handlers[ 0x004B ] = u_scanf_uchar_handler; |
| |
| /* handle 'V' */ |
| g_u_scanf_infos[ 0x0056 ] = u_scanf_spellout_info; |
| g_u_scanf_handlers[ 0x0056 ] = u_scanf_spellout_handler; |
| |
| /* handle 'x' */ |
| g_u_scanf_infos[ 0x0078 ] = u_scanf_hex_info; |
| g_u_scanf_handlers[ 0x0078 ] = u_scanf_hex_handler; |
| |
| /* handle 'X' */ |
| g_u_scanf_infos[ 0x0058 ] = u_scanf_hex_info; |
| g_u_scanf_handlers[ 0x0058 ] = u_scanf_hex_handler; |
| |
| /* handle 'o' */ |
| g_u_scanf_infos[ 0x006F ] = u_scanf_octal_info; |
| g_u_scanf_handlers[ 0x006F ] = u_scanf_octal_handler; |
| |
| /* handle 'p' */ |
| g_u_scanf_infos[ 0x0070 ] = u_scanf_pointer_info; |
| g_u_scanf_handlers[ 0x0070 ] = u_scanf_pointer_handler; |
| |
| /* handle '[' */ |
| g_u_scanf_infos[ 0x005B ] = u_scanf_scanset_info; |
| g_u_scanf_handlers[ 0x005B ] = u_scanf_scanset_handler; |
| |
| /* we're finished */ |
| g_u_scanf_inited = TRUE; |
| |
| /* unlock the cache */ |
| umtx_unlock(0); |
| } |
| |
| |
| #define U_SCANF_MAX_ARGS 32 |
| #define UP_PERCENT 0x0025 |
| |
| int32_t |
| u_vfscanf_u( UFILE *f, |
| const UChar *patternSpecification, |
| va_list ap) |
| { |
| u_scanf_spec spec; |
| const UChar *alias; |
| int32_t count, converted, temp; |
| |
| int32_t num_args_wanted; |
| int32_t ufmt_types [U_SCANF_MAX_ARGS]; |
| ufmt_args args [U_SCANF_MAX_ARGS]; |
| |
| u_scanf_info info; |
| u_scanf_handler handler; |
| |
| int32_t cur_arg; |
| |
| |
| /* init our function tables */ |
| if(! g_u_scanf_inited) |
| u_scanf_init(); |
| |
| /* alias the pattern */ |
| alias = patternSpecification; |
| |
| /* haven't converted anything yet */ |
| converted = 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; |
| |
| /* skip the argument, if necessary */ |
| if(spec.fSkipArg) |
| va_arg(ap, int); |
| |
| /* query the info function for argument information */ |
| info = g_u_scanf_infos[ (unsigned char) spec.fInfo.fSpec ]; |
| if(info != 0) { |
| num_args_wanted = (*info)(&spec.fInfo, |
| ufmt_types, |
| U_SCANF_MAX_ARGS); |
| } |
| else |
| num_args_wanted = 0; |
| |
| /* fill in the requested arguments */ |
| for(cur_arg = 0; |
| cur_arg < num_args_wanted && cur_arg < U_SCANF_MAX_ARGS; |
| ++cur_arg) { |
| |
| switch(ufmt_types[cur_arg]) { |
| |
| case ufmt_count: |
| args[cur_arg].intValue = va_arg(ap, int); |
| /* set the spec's width to the # of items converted */ |
| spec.fInfo.fWidth = converted; |
| break; |
| |
| case ufmt_int: |
| args[cur_arg].ptrValue = va_arg(ap, int*); |
| break; |
| |
| case ufmt_char: |
| args[cur_arg].ptrValue = va_arg(ap, int*); |
| break; |
| |
| case ufmt_wchar: |
| args[cur_arg].ptrValue = va_arg(ap, wchar_t*); |
| break; |
| |
| case ufmt_string: |
| args[cur_arg].ptrValue = va_arg(ap, char*); |
| break; |
| |
| case ufmt_wstring: |
| args[cur_arg].ptrValue = va_arg(ap, wchar_t*); |
| break; |
| |
| case ufmt_pointer: |
| args[cur_arg].ptrValue = va_arg(ap, void*); |
| break; |
| |
| case ufmt_float: |
| args[cur_arg].ptrValue = va_arg(ap, float*); |
| break; |
| |
| case ufmt_double: |
| args[cur_arg].ptrValue = va_arg(ap, double*); |
| break; |
| |
| case ufmt_date: |
| args[cur_arg].ptrValue = va_arg(ap, UDate*); |
| break; |
| |
| case ufmt_ustring: |
| args[cur_arg].ptrValue = va_arg(ap, UChar*); |
| break; |
| |
| case ufmt_uchar: |
| args[cur_arg].ptrValue = va_arg(ap, int*); |
| break; |
| } |
| } |
| |
| /* call the handler function */ |
| handler = g_u_scanf_handlers[ (unsigned char) spec.fInfo.fSpec ]; |
| if(handler != 0) { |
| |
| /* reset count */ |
| count = 0; |
| |
| temp = (*handler)(f, &spec.fInfo, args, alias, &count); |
| |
| /* if the handler encountered an error condition, break */ |
| if(temp == -1) |
| break; |
| |
| /* add to the # of items converted */ |
| converted += temp; |
| |
| /* update the pointer in pattern */ |
| alias += count; |
| } |
| |
| /* just ignore unknown tags */ |
| |
| } |
| |
| /* return # of items converted */ |
| return converted; |
| } |
| |