Exemplo n.º 1
0
/**
 * 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 {