ICU-3499 Fix flaws found while testing %n, %c and %C
X-SVN-Rev: 15596
This commit is contained in:
parent
b2697fc485
commit
326ab9c4b0
@ -85,11 +85,12 @@ typedef struct u_scanf_spec_info {
|
|||||||
|
|
||||||
UChar fPadChar; /* Padding character */
|
UChar fPadChar; /* Padding character */
|
||||||
|
|
||||||
|
UBool fSkipArg; /* TRUE if arg should be skipped */
|
||||||
UBool fIsLongDouble; /* L flag */
|
UBool fIsLongDouble; /* L flag */
|
||||||
UBool fIsShort; /* h flag */
|
UBool fIsShort; /* h flag */
|
||||||
UBool fIsLong; /* l flag */
|
UBool fIsLong; /* l flag */
|
||||||
UBool fIsLongLong; /* ll flag */
|
UBool fIsLongLong; /* ll flag */
|
||||||
UBool fSkipArg; /* TRUE if arg should be skipped */
|
UBool fIsString; /* TRUE if this is a NULL-terminated string. */
|
||||||
} u_scanf_spec_info;
|
} u_scanf_spec_info;
|
||||||
|
|
||||||
|
|
||||||
@ -119,14 +120,15 @@ u_scanf_parse_spec (const UChar *fmt,
|
|||||||
/* initialize spec to default values */
|
/* initialize spec to default values */
|
||||||
spec->fArgPos = -1;
|
spec->fArgPos = -1;
|
||||||
|
|
||||||
info->fSkipArg = FALSE;
|
|
||||||
info->fSpec = 0x0000;
|
|
||||||
info->fWidth = -1;
|
info->fWidth = -1;
|
||||||
|
info->fSpec = 0x0000;
|
||||||
info->fPadChar = 0x0020;
|
info->fPadChar = 0x0020;
|
||||||
|
info->fSkipArg = FALSE;
|
||||||
info->fIsLongDouble = FALSE;
|
info->fIsLongDouble = FALSE;
|
||||||
info->fIsShort = FALSE;
|
info->fIsShort = FALSE;
|
||||||
info->fIsLong = FALSE;
|
info->fIsLong = FALSE;
|
||||||
info->fIsLongLong = FALSE;
|
info->fIsLongLong = FALSE;
|
||||||
|
info->fIsString = TRUE;
|
||||||
|
|
||||||
|
|
||||||
/* skip over the initial '%' */
|
/* skip over the initial '%' */
|
||||||
@ -285,16 +287,18 @@ u_scanf_parse_spec (const UChar *fmt,
|
|||||||
* @param args A pointer to the argument data
|
* @param args A pointer to the argument data
|
||||||
* @param fmt A pointer to the first character in the format string
|
* @param fmt A pointer to the first character in the format string
|
||||||
* following the spec.
|
* following the spec.
|
||||||
* @param consumed On output, set to the number of characters consumed
|
* @param fmtConsumed On output, set to the number of characters consumed
|
||||||
* in <TT>fmt</TT>.
|
* in <TT>fmt</TT>. Do nothing, if the argument isn't variable width.
|
||||||
* @return The number of arguments converted and assigned, or -1 if an
|
* @param argConverted The number of arguments converted and assigned, or -1 if an
|
||||||
* error occurred.
|
* error occurred.
|
||||||
|
* @return The number of code points consumed during reading.
|
||||||
*/
|
*/
|
||||||
typedef int32_t (*u_scanf_handler) (UFILE *stream,
|
typedef int32_t (*u_scanf_handler) (UFILE *stream,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed);
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted);
|
||||||
|
|
||||||
typedef struct u_scanf_info {
|
typedef struct u_scanf_info {
|
||||||
ufmt_type_info info;
|
ufmt_type_info info;
|
||||||
@ -330,147 +334,37 @@ u_scanf_skip_leading_ws(UFILE *input,
|
|||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_simple_percent_handler(UFILE *input,
|
u_scanf_simple_percent_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
/* make sure the next character in the input is a percent */
|
/* make sure the next character in the input is a percent */
|
||||||
|
*argConverted = 0;
|
||||||
if(u_fgetc(input) != 0x0025) {
|
if(u_fgetc(input) != 0x0025) {
|
||||||
return -1;
|
*argConverted = -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t
|
|
||||||
u_scanf_string_handler(UFILE *input,
|
|
||||||
const u_scanf_spec_info *info,
|
|
||||||
ufmt_args *args,
|
|
||||||
const UChar *fmt,
|
|
||||||
int32_t *consumed)
|
|
||||||
{
|
|
||||||
UChar c;
|
|
||||||
UBool isNotEOF;
|
|
||||||
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 input */
|
|
||||||
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))
|
|
||||||
&& (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. */
|
|
||||||
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(isNotEOF && (info->fWidth == -1 || count < info->fWidth) )
|
|
||||||
u_fungetc(c, input);
|
|
||||||
|
|
||||||
/* add the terminator */
|
|
||||||
*alias = 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clean up */
|
|
||||||
u_releaseDefaultConverter(conv);
|
|
||||||
|
|
||||||
/* we converted 1 arg */
|
|
||||||
return !info->fSkipArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t
|
|
||||||
u_scanf_ustring_handler(UFILE *input,
|
|
||||||
const u_scanf_spec_info *info,
|
|
||||||
ufmt_args *args,
|
|
||||||
const UChar *fmt,
|
|
||||||
int32_t *consumed)
|
|
||||||
{
|
|
||||||
UChar c;
|
|
||||||
UBool isNotEOF;
|
|
||||||
int32_t count;
|
|
||||||
UChar *arg = (UChar*)(args[0].ptrValue);
|
|
||||||
UChar *alias = arg;
|
|
||||||
|
|
||||||
/* skip all ws in the input */
|
|
||||||
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))
|
|
||||||
&& (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(isNotEOF && (info->fWidth == -1 || count < info->fWidth)) {
|
|
||||||
u_fungetc(c, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add the terminator */
|
|
||||||
*alias = 0x0000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we converted 1 arg */
|
|
||||||
return !info->fSkipArg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_count_handler(UFILE *input,
|
u_scanf_count_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
/* in the special case of count, the u_scanf_spec_info's width */
|
/* in the special case of count, the u_scanf_spec_info's width */
|
||||||
/* will contain the # of items converted thus far */
|
/* will contain the # of items converted thus far */
|
||||||
if (!info->fSkipArg) {
|
if (!info->fSkipArg) {
|
||||||
*(int*)(args[0].ptrValue) = info->fWidth;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we converted 0 args */
|
/* we converted 0 args */
|
||||||
@ -479,10 +373,11 @@ u_scanf_count_handler(UFILE *input,
|
|||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_double_handler(UFILE *input,
|
u_scanf_double_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
double num;
|
double num;
|
||||||
@ -526,15 +421,17 @@ u_scanf_double_handler(UFILE *input,
|
|||||||
input->str.fPos += parsePos;
|
input->str.fPos += parsePos;
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return parsePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_scientific_handler(UFILE *input,
|
u_scanf_scientific_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
double num;
|
double num;
|
||||||
@ -578,22 +475,24 @@ u_scanf_scientific_handler(UFILE *input,
|
|||||||
input->str.fPos += parsePos;
|
input->str.fPos += parsePos;
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return parsePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_scidbl_handler(UFILE *input,
|
u_scanf_scidbl_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
double num;
|
double num;
|
||||||
UNumberFormat *scientificFormat, *genericFormat;
|
UNumberFormat *scientificFormat, *genericFormat;
|
||||||
/*int32_t scientificResult, genericResult;*/
|
/*int32_t scientificResult, genericResult;*/
|
||||||
double scientificResult, genericResult;
|
double scientificResult, genericResult;
|
||||||
int32_t scientificParsePos = 0, genericParsePos = 0;
|
int32_t scientificParsePos = 0, genericParsePos = 0, parsePos = 0;
|
||||||
UErrorCode scientificStatus = U_ZERO_ERROR;
|
UErrorCode scientificStatus = U_ZERO_ERROR;
|
||||||
UErrorCode genericStatus = U_ZERO_ERROR;
|
UErrorCode genericStatus = U_ZERO_ERROR;
|
||||||
|
|
||||||
@ -638,14 +537,15 @@ u_scanf_scidbl_handler(UFILE *input,
|
|||||||
/* stash the result in num */
|
/* stash the result in num */
|
||||||
num = scientificResult;
|
num = scientificResult;
|
||||||
/* update the input's position to reflect consumed data */
|
/* update the input's position to reflect consumed data */
|
||||||
input->str.fPos += scientificParsePos;
|
parsePos += scientificParsePos;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* stash the result in num */
|
/* stash the result in num */
|
||||||
num = genericResult;
|
num = genericResult;
|
||||||
/* update the input's position to reflect consumed data */
|
/* update the input's position to reflect consumed data */
|
||||||
input->str.fPos += genericParsePos;
|
parsePos += genericParsePos;
|
||||||
}
|
}
|
||||||
|
input->str.fPos += parsePos;
|
||||||
|
|
||||||
if (!info->fSkipArg) {
|
if (!info->fSkipArg) {
|
||||||
*(double*)(args[0].ptrValue) = num;
|
*(double*)(args[0].ptrValue) = num;
|
||||||
@ -656,15 +556,17 @@ u_scanf_scidbl_handler(UFILE *input,
|
|||||||
num &= DBL_MAX;*/
|
num &= DBL_MAX;*/
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return parsePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_integer_handler(UFILE *input,
|
u_scanf_integer_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
void *num = (void*) (args[0].ptrValue);
|
void *num = (void*) (args[0].ptrValue);
|
||||||
@ -711,36 +613,29 @@ u_scanf_integer_handler(UFILE *input,
|
|||||||
input->str.fPos += parsePos;
|
input->str.fPos += parsePos;
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return parsePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_uinteger_handler(UFILE *input,
|
u_scanf_uinteger_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
ufmt_args uint_args;
|
/* TODO Fix this when Numberformat handles uint64_t */
|
||||||
int32_t converted_args;
|
return u_scanf_integer_handler(input, info, args, fmt, fmtConsumed, argConverted);
|
||||||
double currDouble;
|
|
||||||
|
|
||||||
uint_args.ptrValue = &currDouble;
|
|
||||||
converted_args = u_scanf_double_handler(input, info, &uint_args, fmt, consumed);
|
|
||||||
|
|
||||||
if (!info->fSkipArg) {
|
|
||||||
*(uint32_t*)(args[0].ptrValue) = (uint32_t)currDouble;
|
|
||||||
}
|
|
||||||
|
|
||||||
return converted_args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_percent_handler(UFILE *input,
|
u_scanf_percent_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
double num;
|
double num;
|
||||||
@ -784,74 +679,177 @@ u_scanf_percent_handler(UFILE *input,
|
|||||||
input->str.fPos += parsePos;
|
input->str.fPos += parsePos;
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*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)
|
||||||
|
{
|
||||||
|
UChar c;
|
||||||
|
UBool isNotEOF;
|
||||||
|
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 input */
|
||||||
|
if (info->fIsString) {
|
||||||
|
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. */
|
||||||
|
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(isNotEOF && (info->fWidth == -1 || count < info->fWidth) )
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_char_handler(UFILE *input,
|
u_scanf_char_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
UChar uc = 0;
|
if (info->fWidth < 0) {
|
||||||
char *result;
|
info->fWidth = 1;
|
||||||
char *c = (char*)(args[0].ptrValue);
|
}
|
||||||
|
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 c;
|
||||||
|
UBool isNotEOF;
|
||||||
|
int32_t count;
|
||||||
|
UChar *arg = (UChar*)(args[0].ptrValue);
|
||||||
|
UChar *alias = arg;
|
||||||
|
|
||||||
/* skip all ws in the input */
|
/* skip all ws in the input */
|
||||||
u_scanf_skip_leading_ws(input, info->fPadChar);
|
if (info->fIsString) {
|
||||||
|
u_scanf_skip_leading_ws(input, info->fPadChar);
|
||||||
/* get the character from the input, truncating to the width */
|
|
||||||
if(info->fWidth == -1 || info->fWidth > 1)
|
|
||||||
if (!ufile_getch(input, &uc))
|
|
||||||
return -1; /* no character */
|
|
||||||
|
|
||||||
/* convert the character to the default codepage */
|
|
||||||
result = ufmt_unicodeToDefaultCP(&uc, 1);
|
|
||||||
|
|
||||||
if (!info->fSkipArg) {
|
|
||||||
*c = result[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* clean up */
|
/* get the string one character at a time, truncating to the width */
|
||||||
uprv_free(result);
|
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(isNotEOF && (info->fWidth == -1 || count < info->fWidth)) {
|
||||||
|
u_fungetc(c, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the terminator */
|
||||||
|
if (info->fIsString) {
|
||||||
|
*alias = 0x0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_uchar_handler(UFILE *input,
|
u_scanf_uchar_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
UChar *c = (UChar*)(args[0].ptrValue);
|
if (info->fWidth < 0) {
|
||||||
UChar skippedChar;
|
info->fWidth = 1;
|
||||||
|
|
||||||
if (info->fSkipArg) {
|
|
||||||
c = &skippedChar;
|
|
||||||
}
|
}
|
||||||
|
info->fIsString = FALSE;
|
||||||
/* skip all ws in the input */
|
return u_scanf_ustring_handler(input, info, args, fmt, fmtConsumed, argConverted);
|
||||||
u_scanf_skip_leading_ws(input, info->fPadChar);
|
|
||||||
|
|
||||||
/* get the character from the input, truncating to the width */
|
|
||||||
if(info->fWidth == -1 || info->fWidth > 1)
|
|
||||||
if (!ufile_getch(input, c))
|
|
||||||
return -1; /* no character */
|
|
||||||
|
|
||||||
/* we converted 1 arg */
|
|
||||||
return !info->fSkipArg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_spellout_handler(UFILE *input,
|
u_scanf_spellout_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
double num;
|
double num;
|
||||||
@ -895,15 +893,17 @@ u_scanf_spellout_handler(UFILE *input,
|
|||||||
input->str.fPos += parsePos;
|
input->str.fPos += parsePos;
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return parsePos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_hex_handler(UFILE *input,
|
u_scanf_hex_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
void *num = (void*) (args[0].ptrValue);
|
void *num = (void*) (args[0].ptrValue);
|
||||||
@ -948,15 +948,17 @@ u_scanf_hex_handler(UFILE *input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_octal_handler(UFILE *input,
|
u_scanf_octal_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
void *num = (void*) (args[0].ptrValue);
|
void *num = (void*) (args[0].ptrValue);
|
||||||
@ -992,15 +994,17 @@ u_scanf_octal_handler(UFILE *input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_pointer_handler(UFILE *input,
|
u_scanf_pointer_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
int32_t len;
|
int32_t len;
|
||||||
void *result;
|
void *result;
|
||||||
@ -1031,15 +1035,17 @@ u_scanf_pointer_handler(UFILE *input,
|
|||||||
input->str.fPos += len;
|
input->str.fPos += len;
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t
|
static int32_t
|
||||||
u_scanf_scanset_handler(UFILE *input,
|
u_scanf_scanset_handler(UFILE *input,
|
||||||
const u_scanf_spec_info *info,
|
u_scanf_spec_info *info,
|
||||||
ufmt_args *args,
|
ufmt_args *args,
|
||||||
const UChar *fmt,
|
const UChar *fmt,
|
||||||
int32_t *consumed)
|
int32_t *fmtConsumed,
|
||||||
|
int32_t *argConverted)
|
||||||
{
|
{
|
||||||
USet *scanset;
|
USet *scanset;
|
||||||
UErrorCode status = U_ZERO_ERROR;
|
UErrorCode status = U_ZERO_ERROR;
|
||||||
@ -1061,7 +1067,7 @@ u_scanf_scanset_handler(UFILE *input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* parse the scanset from the fmt string */
|
/* parse the scanset from the fmt string */
|
||||||
*consumed = uset_applyPattern(scanset, fmt, -1, 0, &status);
|
*fmtConsumed = uset_applyPattern(scanset, fmt, -1, 0, &status);
|
||||||
|
|
||||||
/* verify that the parse was successful */
|
/* verify that the parse was successful */
|
||||||
if (U_SUCCESS(status)) {
|
if (U_SUCCESS(status)) {
|
||||||
@ -1106,7 +1112,8 @@ u_scanf_scanset_handler(UFILE *input,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* we converted 1 arg */
|
/* we converted 1 arg */
|
||||||
return !info->fSkipArg;
|
*argConverted = !info->fSkipArg;
|
||||||
|
return (info->fWidth >= 0 ? info->fWidth : INT32_MAX) - chLeft;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use US-ASCII characters only for formatting. Most codepages have
|
/* Use US-ASCII characters only for formatting. Most codepages have
|
||||||
@ -1165,7 +1172,7 @@ u_scanf_parse(UFILE *f,
|
|||||||
va_list ap)
|
va_list ap)
|
||||||
{
|
{
|
||||||
const UChar *alias;
|
const UChar *alias;
|
||||||
int32_t count, converted, temp;
|
int32_t count, converted, argConsumed, cpConsumed;
|
||||||
uint16_t handlerNum;
|
uint16_t handlerNum;
|
||||||
|
|
||||||
ufmt_args args;
|
ufmt_args args;
|
||||||
@ -1177,7 +1184,9 @@ u_scanf_parse(UFILE *f,
|
|||||||
alias = patternSpecification;
|
alias = patternSpecification;
|
||||||
|
|
||||||
/* haven't converted anything yet */
|
/* haven't converted anything yet */
|
||||||
|
argConsumed = 0;
|
||||||
converted = 0;
|
converted = 0;
|
||||||
|
cpConsumed = 0;
|
||||||
|
|
||||||
/* iterate through the pattern */
|
/* iterate through the pattern */
|
||||||
for(;;) {
|
for(;;) {
|
||||||
@ -1209,6 +1218,10 @@ u_scanf_parse(UFILE *f,
|
|||||||
if(info > ufmt_simple_percent) {
|
if(info > ufmt_simple_percent) {
|
||||||
switch(info) {
|
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_char:
|
||||||
case ufmt_uchar:
|
case ufmt_uchar:
|
||||||
case ufmt_int:
|
case ufmt_int:
|
||||||
@ -1220,12 +1233,6 @@ u_scanf_parse(UFILE *f,
|
|||||||
args.ptrValue = va_arg(ap, void*);
|
args.ptrValue = va_arg(ap, void*);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ufmt_count:
|
|
||||||
args.int64Value = va_arg(ap, int);
|
|
||||||
/* set the spec's width to the # of items converted */
|
|
||||||
spec.fInfo.fWidth = converted;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break; /* Should never get here */
|
break; /* Should never get here */
|
||||||
}
|
}
|
||||||
@ -1238,14 +1245,14 @@ u_scanf_parse(UFILE *f,
|
|||||||
/* reset count to 1 so that += for alias works. */
|
/* reset count to 1 so that += for alias works. */
|
||||||
count = 1;
|
count = 1;
|
||||||
|
|
||||||
temp = (*handler)(f, &spec.fInfo, &args, alias, &count);
|
cpConsumed += (*handler)(f, &spec.fInfo, &args, alias, &count, &argConsumed);
|
||||||
|
|
||||||
/* if the handler encountered an error condition, break */
|
/* if the handler encountered an error condition, break */
|
||||||
if(temp == -1)
|
if(argConsumed < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* add to the # of items converted */
|
/* add to the # of items converted */
|
||||||
converted += temp;
|
converted += argConsumed;
|
||||||
|
|
||||||
/* update the pointer in pattern */
|
/* update the pointer in pattern */
|
||||||
alias += count-1;
|
alias += count-1;
|
||||||
|
@ -691,6 +691,32 @@ static void TestVargs(void) {
|
|||||||
Test_u_vfprintf("8 9 a B 8.9", "%d %u %x %X %.1f", 8, 9, 10, 11, 8.9);
|
Test_u_vfprintf("8 9 a B 8.9", "%d %u %x %X %.1f", 8, 9, 10, 11, 8.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void TestCount(void) {
|
||||||
|
UChar testStr[16];
|
||||||
|
int16_t i16 = -1;
|
||||||
|
int32_t i32 = -1;
|
||||||
|
int64_t i64 = -1;
|
||||||
|
u_uastrcpy(testStr, "1233456789");
|
||||||
|
if (u_sscanf(testStr, "%*3[123]%n%*[1-9]", &i32) != 0) {
|
||||||
|
log_err("test 1: scanf did not return 0\n");
|
||||||
|
}
|
||||||
|
if (i32 != 3) {
|
||||||
|
log_err("test 1: scanf returned %hd instead of 3\n", i32);
|
||||||
|
}
|
||||||
|
if (u_sscanf(testStr, "%*4[123]%hn%*[1-9]", &i16) != 0) {
|
||||||
|
log_err("test 2: scanf did not return 0\n");
|
||||||
|
}
|
||||||
|
if (i16 != 4) {
|
||||||
|
log_err("test 2: scanf returned %d instead of 4\n", i16);
|
||||||
|
}
|
||||||
|
if (u_sscanf(testStr, "%*[123]%*[1-9]%lln", &i64) != 0) {
|
||||||
|
log_err("test 3: scanf did not return 0\n");
|
||||||
|
}
|
||||||
|
if (i64 != 10) {
|
||||||
|
log_err("test 3: scanf did not return 10\n", i64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
U_CFUNC void
|
U_CFUNC void
|
||||||
addStringTest(TestNode** root) {
|
addStringTest(TestNode** root) {
|
||||||
addTest(root, &TestString, "string/TestString");
|
addTest(root, &TestString, "string/TestString");
|
||||||
@ -701,6 +727,7 @@ addStringTest(TestNode** root) {
|
|||||||
addTest(root, &TestStringCompatibility, "string/TestStringCompatibility");
|
addTest(root, &TestStringCompatibility, "string/TestStringCompatibility");
|
||||||
addTest(root, &TestBadScanfFormat, "string/TestBadScanfFormat");
|
addTest(root, &TestBadScanfFormat, "string/TestBadScanfFormat");
|
||||||
addTest(root, &TestVargs, "string/TestVargs");
|
addTest(root, &TestVargs, "string/TestVargs");
|
||||||
|
addTest(root, &TestCount, "string/TestCount");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user