]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/io/uprntf_p.c
ICU-400.37.tar.gz
[apple/icu.git] / icuSources / io / uprntf_p.c
index 0823676221088fd68d2fbc013eb9026192816894..f303c0c8d6c2d383f47369b5ec878cbce1844062 100644 (file)
@@ -1,7 +1,7 @@
 /*
 ******************************************************************************
 *
-*   Copyright (C) 1998-2006, International Business Machines
+*   Copyright (C) 1998-2008, International Business Machines
 *   Corporation and others.  All Rights Reserved.
 *
 ******************************************************************************
@@ -800,6 +800,8 @@ u_printf_scidbl_handler(const u_printf_stream_handler  *handler,
     u_printf_spec_info scidbl_info;
     double      num = args[0].doubleValue;
     int32_t     retVal;
+    UNumberFormat *format;
+    int32_t maxSigDecimalDigits, significantDigits;
 
     memcpy(&scidbl_info, info, sizeof(u_printf_spec_info));
 
@@ -824,9 +826,13 @@ u_printf_scidbl_handler(const u_printf_stream_handler  *handler,
         retVal = u_printf_scientific_handler(handler, context, formatBundle, &scidbl_info, args);
     }
     else {
-        UNumberFormat   *format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL);
-        int32_t maxSigDecimalDigits = unum_getAttribute(format, UNUM_MAX_SIGNIFICANT_DIGITS);
-        int32_t significantDigits = scidbl_info.fPrecision;
+        format = u_locbund_getNumberFormat(formatBundle, UNUM_DECIMAL);
+        /* Check for null pointer */
+        if (format == NULL) {
+            return 0;
+        }
+        maxSigDecimalDigits = unum_getAttribute(format, UNUM_MAX_SIGNIFICANT_DIGITS);
+        significantDigits = scidbl_info.fPrecision;
 
         /* use 'f' notation */
         scidbl_info.fSpec = 0x0066;
@@ -1036,6 +1042,165 @@ static const u_printf_info g_u_printf_infos[UPRINTF_NUM_FMT_HANDLERS] = {
 #define ISMOD(s)    (s) == MOD_H || \
             (s) == MOD_LOWERL || \
             (s) == MOD_L
+/* Returns an array of the parsed argument type given in the format string. */
+static ufmt_args* parseArguments(const UChar *alias, va_list ap, UErrorCode *status) {
+    ufmt_args *arglist = NULL;
+    ufmt_type_info *typelist = NULL;
+    UBool *islonglong = NULL;
+    int32_t size = 0;
+    int32_t pos = 0;
+    UChar type;
+    uint16_t handlerNum;
+    const UChar *aliasStart = alias;
+
+    /* get maximum number of arguments */
+    for(;;) {
+        /* find % */
+        while(*alias != UP_PERCENT && *alias != 0x0000) {
+            alias++;
+        }
+
+        if(*alias == 0x0000) {
+            break;
+        }
+
+        alias++;
+
+        /* handle the pos number */
+        if(ISDIGIT(*alias)) {
+
+            /* handle positional parameters */
+            if(ISDIGIT(*alias)) {
+                pos = (int) (*alias++ - DIGIT_ZERO);
+
+                while(ISDIGIT(*alias)) {
+                    pos *= 10;
+                    pos += (int) (*alias++ - DIGIT_ZERO);
+                }
+            }
+
+            /* if there is no '$', don't read anything */
+            if(*alias != SPEC_DOLLARSIGN) {
+                return NULL;
+            }
+        } else {
+            return NULL;
+        }
+
+        if (pos > size) {
+            size = pos;
+        }
+    }
+
+    /* create the parsed argument list */
+    typelist = (ufmt_type_info*)uprv_malloc(sizeof(ufmt_type_info) * size);
+    islonglong = (UBool*)uprv_malloc(sizeof(UBool) * size);
+    arglist = (ufmt_args*)uprv_malloc(sizeof(ufmt_args) * size);
+
+    /* If malloc failed, return NULL */
+    if (!typelist || !islonglong || !arglist) {
+        if (typelist) {
+            uprv_free(typelist);
+        }
+
+        if (islonglong) {
+            uprv_free(islonglong);
+        }
+
+        if (arglist) {
+            uprv_free(arglist);
+        }
+
+        *status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+    }
+
+    /* reset alias back to the beginning */
+    alias = aliasStart;
+
+    for(;;) {
+        /* find % */
+        while(*alias != UP_PERCENT && *alias != 0x0000) {
+            alias++;
+        }
+
+        if(*alias == 0x0000) {
+            break;
+        }
+
+        alias++;
+
+        /* handle positional parameters */
+        if(ISDIGIT(*alias)) {
+            pos = (int) (*alias++ - DIGIT_ZERO);
+
+            while(ISDIGIT(*alias)) {
+                pos *= 10;
+                pos += (int) (*alias++ - DIGIT_ZERO);
+            }
+        }
+        /* offset position by 1 */
+        pos--;
+
+        /* skip over everything except for the type */
+        while (ISMOD(*alias) || ISFLAG(*alias) || ISDIGIT(*alias) || 
+            *alias == SPEC_ASTERISK || *alias == SPEC_PERIOD || *alias == SPEC_DOLLARSIGN) {
+                islonglong[pos] = FALSE;
+                if (ISMOD(*alias)) {
+                    alias++;
+                    if (*alias == MOD_LOWERL) {
+                        islonglong[pos] = TRUE;
+                    } 
+                } 
+                alias++;
+        }
+        type = *alias;
+
+        /* store the argument type in the correct position of the parsed argument list */
+        handlerNum = (uint16_t)(type - UPRINTF_BASE_FMT_HANDLERS);
+        if (handlerNum < UPRINTF_NUM_FMT_HANDLERS) {
+            typelist[pos] = g_u_printf_infos[ handlerNum ].info;
+        } else {
+            typelist[pos] = ufmt_empty;
+        }
+    }
+
+    /* store argument in arglist */
+    for (pos = 0; pos < size; pos++) {
+        switch (typelist[pos]) {
+        case ufmt_string:
+        case ufmt_ustring:
+        case ufmt_pointer:
+            arglist[pos].ptrValue = va_arg(ap, void*);
+            break;
+        case ufmt_char:
+        case ufmt_uchar:
+        case ufmt_int:
+            if (islonglong[pos]) {
+                arglist[pos].int64Value = va_arg(ap, int64_t);
+            }
+            else {
+                arglist[pos].int64Value = va_arg(ap, int32_t);
+            }
+            break;
+        case ufmt_float:
+            arglist[pos].floatValue = (float) va_arg(ap, double);
+            break;
+        case ufmt_double:
+            arglist[pos].doubleValue = va_arg(ap, double);
+            break;
+        default:
+            /* else args is ignored */
+            arglist[pos].ptrValue = NULL;
+            break;
+        }
+    }
+
+    uprv_free(typelist);
+    uprv_free(islonglong);
+
+    return arglist;
+}
 
 /* We parse the argument list in Unicode */
 U_CFUNC int32_t
@@ -1057,9 +1222,22 @@ u_printf_parse(const u_printf_stream_handler *streamHandler,
     const UChar *alias = fmt;
     const UChar *backup;
     const UChar *lastAlias;
-
+    const UChar *orgAlias = fmt;
+    /* parsed argument list */
+    ufmt_args *arglist = NULL; /* initialized it to avoid compiler warnings */
+    UErrorCode status = U_ZERO_ERROR;
+    if (!locStringContext || locStringContext->available >= 0) {
+        /* get the parsed list of argument types */
+        arglist = parseArguments(orgAlias, ap, &status);
+
+        /* Return error if parsing failed. */
+        if (U_FAILURE(status)) {
+            return -1;
+        }
+    }
+    
     /* iterate through the pattern */
-    while(!locStringContext || locStringContext->available > 0) {
+    while(!locStringContext || locStringContext->available >= 0) {
 
         /* find the next '%' */
         lastAlias = alias;
@@ -1320,36 +1498,69 @@ u_printf_parse(const u_printf_stream_handler *streamHandler,
         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);
+
+            /* goto the correct argument on arg_list if position is specified */
+            if (spec.fArgPos > 0) {
+                /* offset position by 1 */
+                spec.fArgPos--;
+                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 = arglist[spec.fArgPos].ptrValue;
+                    break;
+                case ufmt_char:
+                case ufmt_uchar:
+                case ufmt_int:
+                    args.int64Value = arglist[spec.fArgPos].int64Value;
+                    break;
+                case ufmt_float:
+                    args.floatValue = arglist[spec.fArgPos].floatValue;
+                    break;
+                case ufmt_double:
+                    args.doubleValue = arglist[spec.fArgPos].doubleValue;
+                    break;
+                default:
+                    /* else args is ignored */
+                    args.ptrValue = NULL;
+                    break;
                 }
-                else {
-                    args.int64Value = va_arg(ap, int32_t);
+            } else { /* no positional argument specified */
+                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;
                 }
-                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 */
@@ -1367,6 +1578,10 @@ u_printf_parse(const u_printf_stream_handler *streamHandler,
             *written += (streamHandler->write)(context, fmt, (int32_t)(alias - lastAlias));
         }
     }
+    /* delete parsed argument list */
+    if (arglist != NULL) {
+        uprv_free(arglist);
+    }
     /* return # of characters in this format that have been parsed. */
     return (int32_t)(alias - fmt);
 }