/** * 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); }
/* We parse the argument list in Unicode */ U_CFUNC int32_t u_printf_parse(const u_printf_stream_handler *streamHandler, const UChar *fmt, void *context, u_localized_print_string *locStringContext, ULocaleBundle *formatBundle, int32_t *written, va_list ap) { uint16_t handlerNum; ufmt_args args; ufmt_type_info argType; u_printf_handler *handler; u_printf_spec spec; u_printf_spec_info *info = &(spec.fInfo); const UChar *alias = fmt; const UChar *backup; const UChar *lastAlias; /* iterate through the pattern */ while(!locStringContext || locStringContext->available > 0) { /* find the next '%' */ lastAlias = alias; while(*alias != UP_PERCENT && *alias != 0x0000) { alias++; } /* write any characters before the '%' */ if(alias > lastAlias) { *written += (streamHandler->write)(context, lastAlias, (int32_t)(alias - lastAlias)); } /* break if at end of string */ if(*alias == 0x0000) { break; } /* initialize spec to default values */ spec.fWidthPos = -1; spec.fPrecisionPos = -1; spec.fArgPos = -1; uprv_memset(info, 0, sizeof(*info)); info->fPrecision = -1; info->fWidth = -1; info->fPadChar = 0x0020; /* skip over the initial '%' */ alias++; /* Check for positional argument */ if(ISDIGIT(*alias)) { /* Save the current position */ backup = alias; /* handle positional parameters */ if(ISDIGIT(*alias)) { spec.fArgPos = (int) (*alias++ - DIGIT_ZERO); while(ISDIGIT(*alias)) { spec.fArgPos *= 10; spec.fArgPos += (int) (*alias++ - DIGIT_ZERO); } } /* if there is no '$', don't read anything */ if(*alias != SPEC_DOLLARSIGN) { spec.fArgPos = -1; alias = backup; } /* munge the '$' */ else alias++; } /* Get any format flags */ while(ISFLAG(*alias)) { switch(*alias++) { /* left justify */ case FLAG_MINUS: info->fLeft = TRUE; break; /* always show sign */ case FLAG_PLUS: info->fShowSign = TRUE; break; /* use space if no sign present */ case FLAG_SPACE: info->fShowSign = TRUE; info->fSpace = TRUE; break; /* use alternate form */ case FLAG_POUND: info->fAlt = TRUE; break; /* pad with leading zeroes */ case FLAG_ZERO: info->fZero = TRUE; info->fPadChar = 0x0030; break; /* pad character specified */ case FLAG_PAREN: /* TODO test that all four are numbers */ /* first four characters are hex values for pad char */ info->fPadChar = (UChar)ufmt_digitvalue(*alias++); info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); info->fPadChar = (UChar)((info->fPadChar * 16) + ufmt_digitvalue(*alias++)); /* final character is ignored */ alias++; break; } } /* Get the width */ /* width is specified out of line */ if(*alias == SPEC_ASTERISK) { info->fWidth = -2; /* Skip the '*' */ alias++; /* Save the current position */ backup = alias; /* handle positional parameters */ if(ISDIGIT(*alias)) { spec.fWidthPos = (int) (*alias++ - DIGIT_ZERO); while(ISDIGIT(*alias)) { spec.fWidthPos *= 10; spec.fWidthPos += (int) (*alias++ - DIGIT_ZERO); } } /* if there is no '$', don't read anything */ if(*alias != SPEC_DOLLARSIGN) { spec.fWidthPos = -1; alias = backup; } /* munge the '$' */ else alias++; } /* read the width, if present */ else if(ISDIGIT(*alias)){ info->fWidth = (int) (*alias++ - DIGIT_ZERO); while(ISDIGIT(*alias)) { info->fWidth *= 10; info->fWidth += (int) (*alias++ - DIGIT_ZERO); } } /* Get the precision */ if(*alias == SPEC_PERIOD) { /* eat up the '.' */ alias++; /* precision is specified out of line */ if(*alias == SPEC_ASTERISK) { info->fPrecision = -2; /* Skip the '*' */ alias++; /* save the current position */ backup = alias; /* handle positional parameters */ if(ISDIGIT(*alias)) { spec.fPrecisionPos = (int) (*alias++ - DIGIT_ZERO); while(ISDIGIT(*alias)) { spec.fPrecisionPos *= 10; spec.fPrecisionPos += (int) (*alias++ - DIGIT_ZERO); } /* if there is no '$', don't read anything */ if(*alias != SPEC_DOLLARSIGN) { spec.fPrecisionPos = -1; alias = backup; } else { /* munge the '$' */ alias++; } } } /* read the precision */ else if(ISDIGIT(*alias)){ info->fPrecision = (int) (*alias++ - DIGIT_ZERO); while(ISDIGIT(*alias)) { info->fPrecision *= 10; info->fPrecision += (int) (*alias++ - DIGIT_ZERO); } } } /* Get any modifiers */ if(ISMOD(*alias)) { switch(*alias++) { /* short */ case MOD_H: info->fIsShort = TRUE; break; /* long or long long */ case MOD_LOWERL: if(*alias == MOD_LOWERL) { info->fIsLongLong = TRUE; /* skip over the next 'l' */ alias++; } else info->fIsLong = TRUE; break; /* long double */ case MOD_L: info->fIsLongDouble = TRUE; break; } } /* finally, get the specifier letter */ info->fSpec = *alias++; info->fOrigSpec = info->fSpec; /* fill in the precision and width, if specified out of line */ /* width specified out of line */ if(spec.fInfo.fWidth == -2) { if(spec.fWidthPos == -1) { /* read the width from the argument list */ info->fWidth = va_arg(ap, int32_t); } /* else handle positional parameter */ /* if it's negative, take the absolute value and set left alignment */ if(info->fWidth < 0) { info->fWidth *= -1; /* Make positive */ info->fLeft = TRUE; } } /* precision specified out of line */ if(info->fPrecision == -2) { if(spec.fPrecisionPos == -1) { /* read the precision from the argument list */ info->fPrecision = va_arg(ap, int32_t); } /* else handle positional parameter */ /* if it's negative, set it to zero */ if(info->fPrecision < 0) info->fPrecision = 0; } handlerNum = (uint16_t)(info->fSpec - UPRINTF_BASE_FMT_HANDLERS); if (handlerNum < UPRINTF_NUM_FMT_HANDLERS) { /* query the info function for argument information */ argType = g_u_printf_infos[ handlerNum ].info; switch(argType) { case ufmt_count: /* set the spec's width to the # of chars written */ info->fWidth = *written; /* fall through to set the pointer */ case ufmt_string: case ufmt_ustring: case ufmt_pointer: args.ptrValue = va_arg(ap, void*); break; case ufmt_char: case ufmt_uchar: case ufmt_int: if (info->fIsLongLong) { args.int64Value = va_arg(ap, int64_t); } else { args.int64Value = va_arg(ap, int32_t); } break; case ufmt_float: args.floatValue = (float) va_arg(ap, double); break; case ufmt_double: args.doubleValue = va_arg(ap, double); break; default: /* else args is ignored */ args.ptrValue = NULL; break; } /* call the handler function */ handler = g_u_printf_infos[ handlerNum ].handler; if(handler != 0) { *written += (*handler)(streamHandler, context, formatBundle, info, &args); } else { /* just echo unknown tags */ *written += (streamHandler->write)(context, fmt, (int32_t)(alias - lastAlias)); } } else {