michael@0: /* michael@0: ******************************************************************************* michael@0: * michael@0: * Copyright (C) 1999-2012, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ******************************************************************************* michael@0: * file name: umsg.cpp michael@0: * encoding: US-ASCII michael@0: * tab size: 8 (not used) michael@0: * indentation:4 michael@0: * michael@0: * This is a C wrapper to MessageFormat C++ API. michael@0: * michael@0: * Change history: michael@0: * michael@0: * 08/5/2001 Ram Added C wrappers for C++ API. Changed implementation of old API's michael@0: * Removed pattern parser. michael@0: * michael@0: */ michael@0: michael@0: #include "unicode/utypes.h" michael@0: michael@0: #if !UCONFIG_NO_FORMATTING michael@0: michael@0: #include "unicode/umsg.h" michael@0: #include "unicode/ustring.h" michael@0: #include "unicode/fmtable.h" michael@0: #include "unicode/msgfmt.h" michael@0: #include "unicode/unistr.h" michael@0: #include "cpputils.h" michael@0: #include "uassert.h" michael@0: #include "ustr_imp.h" michael@0: michael@0: U_NAMESPACE_BEGIN michael@0: /** michael@0: * This class isolates our access to private internal methods of michael@0: * MessageFormat. It is never instantiated; it exists only for C++ michael@0: * access management. michael@0: */ michael@0: class MessageFormatAdapter { michael@0: public: michael@0: static const Formattable::Type* getArgTypeList(const MessageFormat& m, michael@0: int32_t& count); michael@0: static UBool hasArgTypeConflicts(const MessageFormat& m) { michael@0: return m.hasArgTypeConflicts; michael@0: } michael@0: }; michael@0: const Formattable::Type* michael@0: MessageFormatAdapter::getArgTypeList(const MessageFormat& m, michael@0: int32_t& count) { michael@0: return m.getArgTypeList(count); michael@0: } michael@0: U_NAMESPACE_END michael@0: michael@0: U_NAMESPACE_USE michael@0: michael@0: U_CAPI int32_t michael@0: u_formatMessage(const char *locale, michael@0: const UChar *pattern, michael@0: int32_t patternLength, michael@0: UChar *result, michael@0: int32_t resultLength, michael@0: UErrorCode *status, michael@0: ...) michael@0: { michael@0: va_list ap; michael@0: int32_t actLen; michael@0: //argument checking defered to subsequent method calls michael@0: // start vararg processing michael@0: va_start(ap, status); michael@0: michael@0: actLen = u_vformatMessage(locale,pattern,patternLength,result,resultLength,ap,status); michael@0: // end vararg processing michael@0: va_end(ap); michael@0: michael@0: return actLen; michael@0: } michael@0: michael@0: U_CAPI int32_t U_EXPORT2 michael@0: u_vformatMessage( const char *locale, michael@0: const UChar *pattern, michael@0: int32_t patternLength, michael@0: UChar *result, michael@0: int32_t resultLength, michael@0: va_list ap, michael@0: UErrorCode *status) michael@0: michael@0: { michael@0: //argument checking defered to subsequent method calls michael@0: UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,NULL,status); michael@0: int32_t retVal = umsg_vformat(fmt,result,resultLength,ap,status); michael@0: umsg_close(fmt); michael@0: return retVal; michael@0: } michael@0: michael@0: U_CAPI int32_t michael@0: u_formatMessageWithError(const char *locale, michael@0: const UChar *pattern, michael@0: int32_t patternLength, michael@0: UChar *result, michael@0: int32_t resultLength, michael@0: UParseError *parseError, michael@0: UErrorCode *status, michael@0: ...) michael@0: { michael@0: va_list ap; michael@0: int32_t actLen; michael@0: //argument checking defered to subsequent method calls michael@0: // start vararg processing michael@0: va_start(ap, status); michael@0: michael@0: actLen = u_vformatMessageWithError(locale,pattern,patternLength,result,resultLength,parseError,ap,status); michael@0: michael@0: // end vararg processing michael@0: va_end(ap); michael@0: return actLen; michael@0: } michael@0: michael@0: U_CAPI int32_t U_EXPORT2 michael@0: u_vformatMessageWithError( const char *locale, michael@0: const UChar *pattern, michael@0: int32_t patternLength, michael@0: UChar *result, michael@0: int32_t resultLength, michael@0: UParseError *parseError, michael@0: va_list ap, michael@0: UErrorCode *status) michael@0: michael@0: { michael@0: //argument checking defered to subsequent method calls michael@0: UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,parseError,status); michael@0: int32_t retVal = umsg_vformat(fmt,result,resultLength,ap,status); michael@0: umsg_close(fmt); michael@0: return retVal; michael@0: } michael@0: michael@0: michael@0: // For parse, do the reverse of format: michael@0: // 1. Call through to the C++ APIs michael@0: // 2. Just assume the user passed in enough arguments. michael@0: // 3. Iterate through each formattable returned, and assign to the arguments michael@0: U_CAPI void michael@0: u_parseMessage( const char *locale, michael@0: const UChar *pattern, michael@0: int32_t patternLength, michael@0: const UChar *source, michael@0: int32_t sourceLength, michael@0: UErrorCode *status, michael@0: ...) michael@0: { michael@0: va_list ap; michael@0: //argument checking defered to subsequent method calls michael@0: michael@0: // start vararg processing michael@0: va_start(ap, status); michael@0: michael@0: u_vparseMessage(locale,pattern,patternLength,source,sourceLength,ap,status); michael@0: // end vararg processing michael@0: va_end(ap); michael@0: } michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: u_vparseMessage(const char *locale, michael@0: const UChar *pattern, michael@0: int32_t patternLength, michael@0: const UChar *source, michael@0: int32_t sourceLength, michael@0: va_list ap, michael@0: UErrorCode *status) michael@0: { michael@0: //argument checking defered to subsequent method calls michael@0: UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,NULL,status); michael@0: int32_t count = 0; michael@0: umsg_vparse(fmt,source,sourceLength,&count,ap,status); michael@0: umsg_close(fmt); michael@0: } michael@0: michael@0: U_CAPI void michael@0: u_parseMessageWithError(const char *locale, michael@0: const UChar *pattern, michael@0: int32_t patternLength, michael@0: const UChar *source, michael@0: int32_t sourceLength, michael@0: UParseError *error, michael@0: UErrorCode *status, michael@0: ...) michael@0: { michael@0: va_list ap; michael@0: michael@0: //argument checking defered to subsequent method calls michael@0: michael@0: // start vararg processing michael@0: va_start(ap, status); michael@0: michael@0: u_vparseMessageWithError(locale,pattern,patternLength,source,sourceLength,ap,error,status); michael@0: // end vararg processing michael@0: va_end(ap); michael@0: } michael@0: U_CAPI void U_EXPORT2 michael@0: u_vparseMessageWithError(const char *locale, michael@0: const UChar *pattern, michael@0: int32_t patternLength, michael@0: const UChar *source, michael@0: int32_t sourceLength, michael@0: va_list ap, michael@0: UParseError *error, michael@0: UErrorCode* status) michael@0: { michael@0: //argument checking defered to subsequent method calls michael@0: UMessageFormat *fmt = umsg_open(pattern,patternLength,locale,error,status); michael@0: int32_t count = 0; michael@0: umsg_vparse(fmt,source,sourceLength,&count,ap,status); michael@0: umsg_close(fmt); michael@0: } michael@0: ////////////////////////////////////////////////////////////////////////////////// michael@0: // michael@0: // Message format C API michael@0: // michael@0: ///////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: michael@0: U_CAPI UMessageFormat* U_EXPORT2 michael@0: umsg_open( const UChar *pattern, michael@0: int32_t patternLength, michael@0: const char *locale, michael@0: UParseError *parseError, michael@0: UErrorCode *status) michael@0: { michael@0: //check arguments michael@0: if(status==NULL || U_FAILURE(*status)) michael@0: { michael@0: return 0; michael@0: } michael@0: if(pattern==NULL||patternLength<-1){ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: return 0; michael@0: } michael@0: michael@0: UParseError tErr; michael@0: if(parseError==NULL) michael@0: { michael@0: parseError = &tErr; michael@0: } michael@0: michael@0: int32_t len = (patternLength == -1 ? u_strlen(pattern) : patternLength); michael@0: UnicodeString patString(patternLength == -1, pattern, len); michael@0: michael@0: MessageFormat* retVal = new MessageFormat(patString,Locale(locale),*parseError,*status); michael@0: if(retVal == NULL) { michael@0: *status = U_MEMORY_ALLOCATION_ERROR; michael@0: return NULL; michael@0: } michael@0: if (U_SUCCESS(*status) && MessageFormatAdapter::hasArgTypeConflicts(*retVal)) { michael@0: *status = U_ARGUMENT_TYPE_MISMATCH; michael@0: } michael@0: return (UMessageFormat*)retVal; michael@0: } michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: umsg_close(UMessageFormat* format) michael@0: { michael@0: //check arguments michael@0: if(format==NULL){ michael@0: return; michael@0: } michael@0: delete (MessageFormat*) format; michael@0: } michael@0: michael@0: U_CAPI UMessageFormat U_EXPORT2 michael@0: umsg_clone(const UMessageFormat *fmt, michael@0: UErrorCode *status) michael@0: { michael@0: //check arguments michael@0: if(status==NULL || U_FAILURE(*status)){ michael@0: return NULL; michael@0: } michael@0: if(fmt==NULL){ michael@0: *status = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return NULL; michael@0: } michael@0: UMessageFormat retVal = (UMessageFormat)((MessageFormat*)fmt)->clone(); michael@0: if(retVal == 0) { michael@0: *status = U_MEMORY_ALLOCATION_ERROR; michael@0: return 0; michael@0: } michael@0: return retVal; michael@0: } michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: umsg_setLocale(UMessageFormat *fmt, const char* locale) michael@0: { michael@0: //check arguments michael@0: if(fmt==NULL){ michael@0: return; michael@0: } michael@0: ((MessageFormat*)fmt)->setLocale(Locale(locale)); michael@0: } michael@0: michael@0: U_CAPI const char* U_EXPORT2 michael@0: umsg_getLocale(const UMessageFormat *fmt) michael@0: { michael@0: //check arguments michael@0: if(fmt==NULL){ michael@0: return ""; michael@0: } michael@0: return ((const MessageFormat*)fmt)->getLocale().getName(); michael@0: } michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: umsg_applyPattern(UMessageFormat *fmt, michael@0: const UChar* pattern, michael@0: int32_t patternLength, michael@0: UParseError* parseError, michael@0: UErrorCode* status) michael@0: { michael@0: //check arguments michael@0: UParseError tErr; michael@0: if(status ==NULL||U_FAILURE(*status)){ michael@0: return ; michael@0: } michael@0: if(fmt==NULL||pattern==NULL||patternLength<-1){ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: return ; michael@0: } michael@0: michael@0: if(parseError==NULL){ michael@0: parseError = &tErr; michael@0: } michael@0: if(patternLength<-1){ michael@0: patternLength=u_strlen(pattern); michael@0: } michael@0: michael@0: ((MessageFormat*)fmt)->applyPattern(UnicodeString(pattern,patternLength),*parseError,*status); michael@0: } michael@0: michael@0: U_CAPI int32_t U_EXPORT2 michael@0: umsg_toPattern(const UMessageFormat *fmt, michael@0: UChar* result, michael@0: int32_t resultLength, michael@0: UErrorCode* status) michael@0: { michael@0: //check arguments michael@0: if(status ==NULL||U_FAILURE(*status)){ michael@0: return -1; michael@0: } michael@0: if(fmt==NULL||resultLength<0 || (resultLength>0 && result==0)){ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: return -1; michael@0: } michael@0: michael@0: michael@0: UnicodeString res; michael@0: if(!(result==NULL && resultLength==0)) { michael@0: // NULL destination for pure preflighting: empty dummy string michael@0: // otherwise, alias the destination buffer michael@0: res.setTo(result, 0, resultLength); michael@0: } michael@0: ((const MessageFormat*)fmt)->toPattern(res); michael@0: return res.extract(result, resultLength, *status); michael@0: } michael@0: michael@0: U_CAPI int32_t michael@0: umsg_format( const UMessageFormat *fmt, michael@0: UChar *result, michael@0: int32_t resultLength, michael@0: UErrorCode *status, michael@0: ...) michael@0: { michael@0: va_list ap; michael@0: int32_t actLen; michael@0: //argument checking defered to last method call umsg_vformat which michael@0: //saves time when arguments are valid and we dont care when arguments are not michael@0: //since we return an error anyway michael@0: michael@0: michael@0: // start vararg processing michael@0: va_start(ap, status); michael@0: michael@0: actLen = umsg_vformat(fmt,result,resultLength,ap,status); michael@0: michael@0: // end vararg processing michael@0: va_end(ap); michael@0: michael@0: return actLen; michael@0: } michael@0: michael@0: U_CAPI int32_t U_EXPORT2 michael@0: umsg_vformat( const UMessageFormat *fmt, michael@0: UChar *result, michael@0: int32_t resultLength, michael@0: va_list ap, michael@0: UErrorCode *status) michael@0: { michael@0: //check arguments michael@0: if(status==0 || U_FAILURE(*status)) michael@0: { michael@0: return -1; michael@0: } michael@0: if(fmt==NULL||resultLength<0 || (resultLength>0 && result==0)) { michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: return -1; michael@0: } michael@0: michael@0: int32_t count =0; michael@0: const Formattable::Type* argTypes = michael@0: MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, count); michael@0: // Allocate at least one element. Allocating an array of length michael@0: // zero causes problems on some platforms (e.g. Win32). michael@0: Formattable* args = new Formattable[count ? count : 1]; michael@0: michael@0: // iterate through the vararg list, and get the arguments out michael@0: for(int32_t i = 0; i < count; ++i) { michael@0: michael@0: UChar *stringVal; michael@0: double tDouble=0; michael@0: int32_t tInt =0; michael@0: int64_t tInt64 = 0; michael@0: UDate tempDate = 0; michael@0: switch(argTypes[i]) { michael@0: case Formattable::kDate: michael@0: tempDate = va_arg(ap, UDate); michael@0: args[i].setDate(tempDate); michael@0: break; michael@0: michael@0: case Formattable::kDouble: michael@0: tDouble =va_arg(ap, double); michael@0: args[i].setDouble(tDouble); michael@0: break; michael@0: michael@0: case Formattable::kLong: michael@0: tInt = va_arg(ap, int32_t); michael@0: args[i].setLong(tInt); michael@0: break; michael@0: michael@0: case Formattable::kInt64: michael@0: tInt64 = va_arg(ap, int64_t); michael@0: args[i].setInt64(tInt64); michael@0: break; michael@0: michael@0: case Formattable::kString: michael@0: // For some reason, a temporary is needed michael@0: stringVal = va_arg(ap, UChar*); michael@0: if(stringVal){ michael@0: args[i].setString(UnicodeString(stringVal)); michael@0: }else{ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: break; michael@0: michael@0: case Formattable::kArray: michael@0: // throw away this argument michael@0: // this is highly platform-dependent, and probably won't work michael@0: // so, if you try to skip arguments in the list (and not use them) michael@0: // you'll probably crash michael@0: va_arg(ap, int); michael@0: break; michael@0: michael@0: case Formattable::kObject: michael@0: // Unused argument number. Read and ignore a pointer argument. michael@0: va_arg(ap, void*); michael@0: break; michael@0: michael@0: default: michael@0: // Unknown/unsupported argument type. michael@0: U_ASSERT(FALSE); michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: break; michael@0: } michael@0: } michael@0: UnicodeString resultStr; michael@0: FieldPosition fieldPosition(0); michael@0: michael@0: /* format the message */ michael@0: ((const MessageFormat*)fmt)->format(args,count,resultStr,fieldPosition,*status); michael@0: michael@0: delete[] args; michael@0: michael@0: if(U_FAILURE(*status)){ michael@0: return -1; michael@0: } michael@0: michael@0: return resultStr.extract(result, resultLength, *status); michael@0: } michael@0: michael@0: U_CAPI void michael@0: umsg_parse( const UMessageFormat *fmt, michael@0: const UChar *source, michael@0: int32_t sourceLength, michael@0: int32_t *count, michael@0: UErrorCode *status, michael@0: ...) michael@0: { michael@0: va_list ap; michael@0: //argument checking defered to last method call umsg_vparse which michael@0: //saves time when arguments are valid and we dont care when arguments are not michael@0: //since we return an error anyway michael@0: michael@0: // start vararg processing michael@0: va_start(ap, status); michael@0: michael@0: umsg_vparse(fmt,source,sourceLength,count,ap,status); michael@0: michael@0: // end vararg processing michael@0: va_end(ap); michael@0: } michael@0: michael@0: U_CAPI void U_EXPORT2 michael@0: umsg_vparse(const UMessageFormat *fmt, michael@0: const UChar *source, michael@0: int32_t sourceLength, michael@0: int32_t *count, michael@0: va_list ap, michael@0: UErrorCode *status) michael@0: { michael@0: //check arguments michael@0: if(status==NULL||U_FAILURE(*status)) michael@0: { michael@0: return; michael@0: } michael@0: if(fmt==NULL||source==NULL || sourceLength<-1 || count==NULL){ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: return; michael@0: } michael@0: if(sourceLength==-1){ michael@0: sourceLength=u_strlen(source); michael@0: } michael@0: michael@0: UnicodeString srcString(source,sourceLength); michael@0: Formattable *args = ((const MessageFormat*)fmt)->parse(srcString,*count,*status); michael@0: UDate *aDate; michael@0: double *aDouble; michael@0: UChar *aString; michael@0: int32_t* aInt; michael@0: int64_t* aInt64; michael@0: UnicodeString temp; michael@0: int len =0; michael@0: // assign formattables to varargs michael@0: for(int32_t i = 0; i < *count; i++) { michael@0: switch(args[i].getType()) { michael@0: michael@0: case Formattable::kDate: michael@0: aDate = va_arg(ap, UDate*); michael@0: if(aDate){ michael@0: *aDate = args[i].getDate(); michael@0: }else{ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: break; michael@0: michael@0: case Formattable::kDouble: michael@0: aDouble = va_arg(ap, double*); michael@0: if(aDouble){ michael@0: *aDouble = args[i].getDouble(); michael@0: }else{ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: break; michael@0: michael@0: case Formattable::kLong: michael@0: aInt = va_arg(ap, int32_t*); michael@0: if(aInt){ michael@0: *aInt = (int32_t) args[i].getLong(); michael@0: }else{ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: break; michael@0: michael@0: case Formattable::kInt64: michael@0: aInt64 = va_arg(ap, int64_t*); michael@0: if(aInt64){ michael@0: *aInt64 = args[i].getInt64(); michael@0: }else{ michael@0: *status=U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: break; michael@0: michael@0: case Formattable::kString: michael@0: aString = va_arg(ap, UChar*); michael@0: if(aString){ michael@0: args[i].getString(temp); michael@0: len = temp.length(); michael@0: temp.extract(0,len,aString); michael@0: aString[len]=0; michael@0: }else{ michael@0: *status= U_ILLEGAL_ARGUMENT_ERROR; michael@0: } michael@0: break; michael@0: michael@0: case Formattable::kObject: michael@0: // This will never happen because MessageFormat doesn't michael@0: // support kObject. When MessageFormat is changed to michael@0: // understand MeasureFormats, modify this code to do the michael@0: // right thing. [alan] michael@0: U_ASSERT(FALSE); michael@0: break; michael@0: michael@0: // better not happen! michael@0: case Formattable::kArray: michael@0: U_ASSERT(FALSE); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // clean up michael@0: delete [] args; michael@0: } michael@0: michael@0: #define SINGLE_QUOTE ((UChar)0x0027) michael@0: #define CURLY_BRACE_LEFT ((UChar)0x007B) michael@0: #define CURLY_BRACE_RIGHT ((UChar)0x007D) michael@0: michael@0: #define STATE_INITIAL 0 michael@0: #define STATE_SINGLE_QUOTE 1 michael@0: #define STATE_IN_QUOTE 2 michael@0: #define STATE_MSG_ELEMENT 3 michael@0: michael@0: #define MAppend(c) if (len < destCapacity) dest[len++] = c; else len++ michael@0: michael@0: int32_t umsg_autoQuoteApostrophe(const UChar* pattern, michael@0: int32_t patternLength, michael@0: UChar* dest, michael@0: int32_t destCapacity, michael@0: UErrorCode* ec) michael@0: { michael@0: int32_t state = STATE_INITIAL; michael@0: int32_t braceCount = 0; michael@0: int32_t len = 0; michael@0: michael@0: if (ec == NULL || U_FAILURE(*ec)) { michael@0: return -1; michael@0: } michael@0: michael@0: if (pattern == NULL || patternLength < -1 || (dest == NULL && destCapacity > 0)) { michael@0: *ec = U_ILLEGAL_ARGUMENT_ERROR; michael@0: return -1; michael@0: } michael@0: U_ASSERT(destCapacity >= 0); michael@0: michael@0: if (patternLength == -1) { michael@0: patternLength = u_strlen(pattern); michael@0: } michael@0: michael@0: for (int i = 0; i < patternLength; ++i) { michael@0: UChar c = pattern[i]; michael@0: switch (state) { michael@0: case STATE_INITIAL: michael@0: switch (c) { michael@0: case SINGLE_QUOTE: michael@0: state = STATE_SINGLE_QUOTE; michael@0: break; michael@0: case CURLY_BRACE_LEFT: michael@0: state = STATE_MSG_ELEMENT; michael@0: ++braceCount; michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: case STATE_SINGLE_QUOTE: michael@0: switch (c) { michael@0: case SINGLE_QUOTE: michael@0: state = STATE_INITIAL; michael@0: break; michael@0: case CURLY_BRACE_LEFT: michael@0: case CURLY_BRACE_RIGHT: michael@0: state = STATE_IN_QUOTE; michael@0: break; michael@0: default: michael@0: MAppend(SINGLE_QUOTE); michael@0: state = STATE_INITIAL; michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: case STATE_IN_QUOTE: michael@0: switch (c) { michael@0: case SINGLE_QUOTE: michael@0: state = STATE_INITIAL; michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: case STATE_MSG_ELEMENT: michael@0: switch (c) { michael@0: case CURLY_BRACE_LEFT: michael@0: ++braceCount; michael@0: break; michael@0: case CURLY_BRACE_RIGHT: michael@0: if (--braceCount == 0) { michael@0: state = STATE_INITIAL; michael@0: } michael@0: break; michael@0: } michael@0: break; michael@0: michael@0: default: // Never happens. michael@0: break; michael@0: } michael@0: michael@0: U_ASSERT(len >= 0); michael@0: MAppend(c); michael@0: } michael@0: michael@0: // End of scan michael@0: if (state == STATE_SINGLE_QUOTE || state == STATE_IN_QUOTE) { michael@0: MAppend(SINGLE_QUOTE); michael@0: } michael@0: michael@0: return u_terminateUChars(dest, destCapacity, len, ec); michael@0: } michael@0: michael@0: #endif /* #if !UCONFIG_NO_FORMATTING */