in CoreFoundation/String.subproj/CFString.c [7155:7878]
static Boolean __CFStringAppendFormatCore(CFMutableStringRef outputString, CFStringRef (*copyDescFunc)(void *, const void *), CFStringRef (*contextDescFunc)(void *, const void *, const void *, bool, bool *), CFDictionaryRef formatOptions, CFDictionaryRef stringsDictConfig, CFStringRef validFormatSpecifiers, CFStringRef formatString, CFIndex initialArgPosition, const void *origValues, CFIndex originalValuesSize, va_list args, CFArrayRef *outReplacementMetadata, CFErrorRef *errorPtr) {
int32_t numSpecs, sizeSpecs, sizeArgNum, formatIdx, curSpec, argNum;
CFIndex formatLen;
const uint8_t *cformat = NULL;
const UniChar *uformat = NULL;
UniChar *formatChars = NULL;
UniChar localFormatBuffer[FORMAT_BUFFER_LEN];
CFFormatSpec localSpecsBuffer[VPRINTF_BUFFER_LEN];
CFFormatSpec *specs;
CFPrintValue localValuesBuffer[VPRINTF_BUFFER_LEN];
CFPrintValue *values;
const CFPrintValue *originalValues = (const CFPrintValue *)origValues;
CFDictionaryRef localConfigs[VPRINTF_BUFFER_LEN];
CFDictionaryRef *configs;
CFMutableDictionaryRef formattingConfig = NULL;
CFIndex numConfigs;
CFAllocatorRef tmpAlloc = NULL;
bool localizedFormatting = formatOptions && (CFGetTypeID(formatOptions) == CFLocaleGetTypeID());
CFMutableArrayRef metadataStorage = NULL;
CFMutableArrayRef *metadata = outReplacementMetadata ? &metadataStorage : NULL;
intmax_t dummyLocation; // A place for %n to do its thing in; should be the widest possible int value
numSpecs = 0;
sizeSpecs = 0;
sizeArgNum = 0;
numConfigs = 0;
specs = NULL;
values = NULL;
configs = NULL;
if (validFormatSpecifiers) {
const uint8_t *cExpectedFormat = NULL;
const UniChar *uExpectedFormat = NULL;
UniChar *expectedFormatChars = NULL;
UniChar expectedLocalFormatBuffer[FORMAT_BUFFER_LEN];
CFFormatSpec *expectedSpecs = NULL;
CFFormatSpec localExpectedSpecsBuffer[VPRINTF_BUFFER_LEN];
CFStringRef *expectedFormatSpecs = NULL;
CFStringRef expectedFormatSpecsBuffer[VPRINTF_BUFFER_LEN];
CFIndex expectedFormatLen = CFStringGetLength(validFormatSpecifiers);
__CFStringSetUpFormatAndSpecBuffers(validFormatSpecifiers, expectedFormatLen, &cExpectedFormat, &uExpectedFormat, &expectedFormatChars, expectedLocalFormatBuffer, &expectedSpecs, localExpectedSpecsBuffer, &expectedFormatSpecs, expectedFormatSpecsBuffer);
SInt32 numExpectedSpecs = __CFStringFindFormatSpecifiersInString(cExpectedFormat, uExpectedFormat, expectedFormatLen, expectedSpecs, NULL, NULL);
if (expectedFormatChars != expectedLocalFormatBuffer) CFAllocatorDeallocate(tmpAlloc, expectedFormatChars);
if (expectedSpecs != localExpectedSpecsBuffer) CFAllocatorDeallocate(tmpAlloc, expectedSpecs);
if (expectedFormatSpecs != expectedFormatSpecsBuffer) CFAllocatorDeallocate(tmpAlloc, expectedFormatSpecs);
if (expectedFormatLen == 0 || numExpectedSpecs == 0) {
if (errorPtr) {
CFStringRef debugMsg = CFStringCreateWithFormat(tmpAlloc, NULL, CFSTR("Expected format '%@' is invalid"), validFormatSpecifiers);
CFMutableDictionaryRef userInfo = CFDictionaryCreateMutable(tmpAlloc, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(userInfo, kCFErrorDebugDescriptionKey, debugMsg);
*errorPtr = CFErrorCreate(tmpAlloc, kCFErrorDomainCocoa, NSFormattingError, userInfo);
CFRelease(debugMsg);
CFRelease(userInfo);
}
return false;
}
/* Validate expected format specifiers against untrusted format string */
if (__CFStringValidateFormat(validFormatSpecifiers, formatString, 0, errorPtr) < 0) {
return false;
}
}
Boolean success = true;
formatLen = CFStringGetLength(formatString);
/* Avoid overflow of formatIdx in loops below that compare to formatIdx */
if (formatLen > INT32_MAX) {
if (errorPtr) *errorPtr = __CFCreateOverflowError();
success = false;
goto cleanup;
}
if (!CF_IS_OBJC(_kCFRuntimeIDCFString, formatString) && !CF_IS_SWIFT(CFStringGetTypeID(), formatString)) {
__CFAssertIsString(formatString);
if (!__CFStrIsUnicode(formatString)) {
cformat = (const uint8_t *)__CFStrContents(formatString);
if (cformat) cformat += __CFStrSkipAnyLengthByte(formatString);
} else {
uformat = (const UniChar *)__CFStrContents(formatString);
}
}
if (!cformat && !uformat) {
formatChars = (formatLen > FORMAT_BUFFER_LEN) ? (UniChar *)CFAllocatorAllocate(tmpAlloc = __CFGetDefaultAllocator(), formatLen * sizeof(UniChar), 0) : localFormatBuffer;
if (formatChars != localFormatBuffer && __CFOASafe) __CFSetLastAllocationEventName(formatChars, "CFString (temp)");
CFStringGetCharacters(formatString, CFRangeMake(0, formatLen), formatChars);
uformat = formatChars;
}
/* Compute an upper bound for the number of format specifications */
if (cformat) {
for (formatIdx = 0; formatIdx < formatLen; formatIdx++) {
if ('%' == cformat[formatIdx]) {
if (__builtin_sadd_overflow(sizeSpecs, 1, &sizeSpecs)) {
if (errorPtr) *errorPtr = __CFCreateOverflowError();
success = false;
goto cleanup;
}
}
}
} else {
for (formatIdx = 0; formatIdx < formatLen; formatIdx++) {
if ('%' == uformat[formatIdx]) {
if (__builtin_sadd_overflow(sizeSpecs, 1, &sizeSpecs)) {
if (errorPtr) *errorPtr = __CFCreateOverflowError();
success = false;
goto cleanup;
}
}
}
}
/* The code following this point makes multiple integer calcuations based off sizeSpecs, which is an upper-bound of the number of tokens in the string based on the number of '%' characters found. In order to avoid integer overflow at multiple points throughout this function, we will cap sizeSpec to an amount that won't overflow, but is still plenty large enough to support any reasonable format strings */
#define MAX_SIZE_SPECS (0xfffff) /* Won't overflow a 32-bit integer up to and including a multiplicative factor of 256 */
if (sizeSpecs > MAX_SIZE_SPECS) {
if (errorPtr) *errorPtr = __CFCreateOverflowError();
success = false;
goto cleanup;
}
tmpAlloc = __CFGetDefaultAllocator();
specs = ((2 * sizeSpecs + 1) > VPRINTF_BUFFER_LEN) ? (CFFormatSpec *)CFAllocatorAllocate(tmpAlloc, (2 * sizeSpecs + 1) * sizeof(CFFormatSpec), 0) : localSpecsBuffer;
if (specs != localSpecsBuffer && __CFOASafe) __CFSetLastAllocationEventName(specs, "CFString (temp)");
configs = ((sizeSpecs < VPRINTF_BUFFER_LEN) ? localConfigs : (CFDictionaryRef *)CFAllocatorAllocate(tmpAlloc, sizeof(CFStringRef) * sizeSpecs, 0));
/* Collect format specification information from the format string */
for (curSpec = 0, formatIdx = 0; formatIdx < formatLen; curSpec++) {
SInt32 newFmtIdx;
specs[curSpec].loc = formatIdx;
specs[curSpec].len = 0;
specs[curSpec].size = 0;
specs[curSpec].type = 0;
specs[curSpec].flags = 0;
specs[curSpec].widthArg = -1;
specs[curSpec].precArg = -1;
specs[curSpec].mainArgNum = -1;
specs[curSpec].precArgNum = -1;
specs[curSpec].widthArgNum = -1;
specs[curSpec].configDictIndex = -1;
if (cformat) {
for (newFmtIdx = formatIdx; newFmtIdx < formatLen && '%' != cformat[newFmtIdx]; newFmtIdx++);
} else {
for (newFmtIdx = formatIdx; newFmtIdx < formatLen && '%' != uformat[newFmtIdx]; newFmtIdx++);
}
if (newFmtIdx != formatIdx) { /* Literal chunk */
specs[curSpec].type = CFFormatLiteralType;
specs[curSpec].len = newFmtIdx - formatIdx;
} else {
CFStringRef configKey = NULL;
newFmtIdx++; /* Skip % */
if (!__CFParseFormatSpec(uformat, cformat, &newFmtIdx, formatLen, &(specs[curSpec]), &configKey, errorPtr)) {
success = false;
goto cleanup;
}
if (CFFormatLiteralType == specs[curSpec].type) {
specs[curSpec].loc = formatIdx + 1;
specs[curSpec].len = 1;
specs[curSpec].flags |= kCFStringFormatPercentReplacementFlag;
} else {
specs[curSpec].len = newFmtIdx - formatIdx;
}
}
formatIdx = newFmtIdx;
// fprintf(stderr, "specs[%d] = {\n size = %d,\n type = %d,\n loc = %d,\n len = %d,\n mainArgNum = %d,\n precArgNum = %d,\n widthArgNum = %d\n}\n", curSpec, specs[curSpec].size, specs[curSpec].type, specs[curSpec].loc, specs[curSpec].len, specs[curSpec].mainArgNum, specs[curSpec].precArgNum, specs[curSpec].widthArgNum);
}
numSpecs = curSpec;
if (originalValues == NULL) {
// Max of three args per spec, reasoning thus: 1 width, 1 prec, 1 value
sizeArgNum = 3 * sizeSpecs + 1;
} else {
#define MAX_SIZE_ORIGINAL_VALUES (0x2ffffe) /* Max size of original values is (3 * MAX_SIZE_SPECS + 1) */
if (originalValuesSize > MAX_SIZE_ORIGINAL_VALUES) {
if (errorPtr) *errorPtr = __CFCreateOverflowError();
success = false;
goto cleanup;
}
sizeArgNum = originalValuesSize;
}
values = (sizeArgNum > VPRINTF_BUFFER_LEN) ? (CFPrintValue *)CFAllocatorAllocate(tmpAlloc, sizeArgNum * sizeof(CFPrintValue), 0) : localValuesBuffer;
if (values != localValuesBuffer && __CFOASafe) __CFSetLastAllocationEventName(values, "CFString (temp)");
memset(values, 0, sizeArgNum * sizeof(CFPrintValue));
va_list copiedArgs;
if (numConfigs > 0) va_copy(copiedArgs, args); // we need to preserve the original state for passing down
/* Compute values array */
argNum = initialArgPosition;
CFIndex validatedDictSpecs = 0;
for (curSpec = 0; curSpec < numSpecs; curSpec++) {
SInt32 newMaxArgNum;
if (0 == specs[curSpec].type) continue;
if (CFFormatLiteralType == specs[curSpec].type) continue;
if (CFFormatIncompleteSpecifierType == specs[curSpec].type) continue;
newMaxArgNum = sizeArgNum;
if (newMaxArgNum < specs[curSpec].mainArgNum) {
newMaxArgNum = specs[curSpec].mainArgNum;
}
if (newMaxArgNum < specs[curSpec].precArgNum) {
newMaxArgNum = specs[curSpec].precArgNum;
}
if (newMaxArgNum < specs[curSpec].widthArgNum) {
newMaxArgNum = specs[curSpec].widthArgNum;
}
if (sizeArgNum < newMaxArgNum) {
if (specs != localSpecsBuffer) CFAllocatorDeallocate(tmpAlloc, specs);
if (values != localValuesBuffer) CFAllocatorDeallocate(tmpAlloc, values);
if (formatChars && (formatChars != localFormatBuffer)) CFAllocatorDeallocate(tmpAlloc, formatChars);
// More arguments than expected - not an error case though.
return true;
}
/* It is actually incorrect to reorder some specs and not all; we just do some random garbage here */
if (-2 == specs[curSpec].widthArgNum) {
specs[curSpec].widthArgNum = argNum++;
}
if (-2 == specs[curSpec].precArgNum) {
specs[curSpec].precArgNum = argNum++;
}
if (-1 == specs[curSpec].mainArgNum) {
specs[curSpec].mainArgNum = argNum++;
}
values[specs[curSpec].mainArgNum].size = specs[curSpec].size;
values[specs[curSpec].mainArgNum].type = specs[curSpec].type;
if (-1 != specs[curSpec].widthArgNum) {
values[specs[curSpec].widthArgNum].size = 0;
values[specs[curSpec].widthArgNum].type = CFFormatLongType;
}
if (-1 != specs[curSpec].precArgNum) {
values[specs[curSpec].precArgNum].size = 0;
values[specs[curSpec].precArgNum].type = CFFormatLongType;
}
}
CFIndex validatedInnerSpecs = 0;
/* Collect the arguments in correct type from vararg list */
for (argNum = 0; argNum < sizeArgNum; argNum++) {
if ((NULL != originalValues) && (0 == values[argNum].type)) values[argNum] = originalValues[argNum];
switch (values[argNum].type) {
case 0:
case CFFormatIncompleteSpecifierType:
case CFFormatLiteralType:
break;
case CFFormatLongType:
case CFFormatSingleUnicharType:
if (CFFormatSize1 == values[argNum].size) {
values[argNum].value.int64Value = (int64_t)(int8_t)va_arg(args, int);
} else if (CFFormatSize2 == values[argNum].size) {
values[argNum].value.int64Value = (int64_t)(int16_t)va_arg(args, int);
} else if (CFFormatSize4 == values[argNum].size) {
values[argNum].value.int64Value = (int64_t)va_arg(args, int32_t);
} else if (CFFormatSize8 == values[argNum].size) {
values[argNum].value.int64Value = (int64_t)va_arg(args, int64_t);
} else {
values[argNum].value.int64Value = (int64_t)va_arg(args, int);
}
break;
case CFFormatDoubleType:
#if LONG_DOUBLE_SUPPORT
if (CFFormatSize16 == values[argNum].size) {
values[argNum].value.longDoubleValue = va_arg(args, long double);
} else
#endif
{
values[argNum].value.doubleValue = va_arg(args, double);
}
break;
case CFFormatPointerType:
case CFFormatCFType:
case CFFormatUnicharsType:
case CFFormatCharsType:
case CFFormatPascalCharsType:
values[argNum].value.pointerValue = va_arg(args, void *);
break;
case CFFormatDummyPointerType:
(void)va_arg(args, void *); // Skip the provided argument
values[argNum].value.pointerValue = &dummyLocation;
break;
}
}
va_end(args);
/* Format the pieces together */
if (NULL == originalValues) {
originalValues = values;
originalValuesSize = sizeArgNum;
}
SInt32 numSpecsContext = 0;
CFFormatSpec *specsContext = NULL;
if (numSpecs > 0) {
specsContext = (CFFormatSpec *)calloc(numSpecs, sizeof(CFFormatSpec));
}
const CFStringRef replacement = CFSTR("%@NSCONTEXT");
for (curSpec = 0; curSpec < numSpecs; curSpec++) {
SInt32 width = 0, precision = 0;
UniChar *up, ch;
Boolean hasWidth = false, hasPrecision = false;
// widthArgNum and widthArg are never set at the same time; same for precArg*
if (-1 != specs[curSpec].widthArgNum) {
width = (SInt32)values[specs[curSpec].widthArgNum].value.int64Value;
hasWidth = true;
}
if (-1 != specs[curSpec].precArgNum) {
precision = (SInt32)values[specs[curSpec].precArgNum].value.int64Value;
hasPrecision = true;
}
if (-1 != specs[curSpec].widthArg) {
width = specs[curSpec].widthArg;
hasWidth = true;
}
if (-1 != specs[curSpec].precArg) {
precision = specs[curSpec].precArg;
hasPrecision = true;
}
#define __CFStringFormatOutputLengthIfNeeded() (metadata ? CFStringGetLength(outputString) : 0)
CFIndex oldLength = 0;
switch (specs[curSpec].type) {
case CFFormatLongType:
case CFFormatDoubleType:
#if TARGET_OS_MAC || TARGET_OS_WIN32 || TARGET_OS_LINUX
if (localizedFormatting && (specs[curSpec].flags & kCFStringFormatLocalizable)) { // We have a locale, so we do localized formatting
oldLength = __CFStringFormatOutputLengthIfNeeded();
if (__CFStringFormatLocalizedNumber(outputString, (CFLocaleRef)formatOptions, values, &specs[curSpec], width, precision, hasPrecision)) {
_CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded());
break;
}
}
/* Otherwise fall-thru to the next case! */
#endif
case CFFormatPointerType: {
char stackFormatBuffer[128];
char *dynamicFormatBuffer = NULL;
char *formatBuffer = stackFormatBuffer;
if (specs[curSpec].len + 1 > sizeof(stackFormatBuffer)) {
// Leave space for the null terminator (+1)
dynamicFormatBuffer = (char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, specs[curSpec].len + 1, 0);
formatBuffer = dynamicFormatBuffer;
}
#define EXTRA_BUFFER_LEN_FOR_WIDTH_PRECISION (16)
char stackBuffer[BUFFER_LEN + EXTRA_BUFFER_LEN_FOR_WIDTH_PRECISION];
char *dynamicBuffer = NULL;
char *buffer = stackBuffer;
size_t bufferSize = sizeof(stackBuffer);
if (width + precision > EXTRA_BUFFER_LEN_FOR_WIDTH_PRECISION) {
bufferSize = BUFFER_LEN + width + precision;
dynamicBuffer = (char *)CFAllocatorAllocate(kCFAllocatorSystemDefault, bufferSize, 0);
buffer = dynamicBuffer;
}
SInt32 cidx, idx, loc;
Boolean appended = false;
loc = specs[curSpec].loc;
// In preparation to call snprintf(), copy the format string out
if (cformat) {
for (idx = 0, cidx = 0; cidx < specs[curSpec].len; idx++, cidx++) {
if ('$' == cformat[loc + cidx]) {
if (idx > -1) {
for (idx--; '0' <= formatBuffer[idx] && formatBuffer[idx] <= '9'; idx--);
}
} else {
formatBuffer[idx] = cformat[loc + cidx];
}
}
} else {
for (idx = 0, cidx = 0; cidx < specs[curSpec].len; idx++, cidx++) {
if ('$' == uformat[loc + cidx]) {
if (idx > -1) {
for (idx--; '0' <= formatBuffer[idx] && formatBuffer[idx] <= '9'; idx--);
}
} else {
formatBuffer[idx] = (int8_t)uformat[loc + cidx];
}
}
}
formatBuffer[idx] = '\0';
// Should modify format buffer here if necessary; for example, to translate %qd to
// the equivalent, on architectures which do not have %q.
buffer[bufferSize - 1] = '\0';
oldLength = __CFStringFormatOutputLengthIfNeeded();
switch (specs[curSpec].type) {
case CFFormatLongType:
if (CFFormatSize8 == specs[curSpec].size) {
SNPRINTF(int64_t, values[specs[curSpec].mainArgNum].value.int64Value)
} else {
SNPRINTF(SInt32, values[specs[curSpec].mainArgNum].value.int64Value)
}
break;
case CFFormatPointerType:
case CFFormatDummyPointerType:
SNPRINTF(void *, values[specs[curSpec].mainArgNum].value.pointerValue)
break;
case CFFormatDoubleType:
#if LONG_DOUBLE_SUPPORT
if (CFFormatSize16 == specs[curSpec].size) {
SNPRINTF(long double, values[specs[curSpec].mainArgNum].value.longDoubleValue)
} else
#endif
{
SNPRINTF(double, values[specs[curSpec].mainArgNum].value.doubleValue)
}
// See if we need to localize the decimal point
if (formatOptions) { // We have localization info
#if TARGET_OS_MAC || TARGET_OS_WIN32 || TARGET_OS_LINUX || TARGET_OS_BSD
CFStringRef decimalSeparator = (CFGetTypeID(formatOptions) == CFLocaleGetTypeID()) ? (CFStringRef)CFLocaleGetValue((CFLocaleRef)formatOptions, kCFLocaleDecimalSeparatorKey) : (CFStringRef)CFDictionaryGetValue(formatOptions, CFSTR("NSDecimalSeparator"));
#else
CFStringRef decimalSeparator = CFSTR(".");
#endif
if (decimalSeparator != NULL) { // We have a decimal separator in there
CFIndex decimalPointLoc = 0;
while (buffer[decimalPointLoc] != 0 && buffer[decimalPointLoc] != '.') decimalPointLoc++;
if (buffer[decimalPointLoc] == '.') { // And we have a decimal point in the formatted string
buffer[decimalPointLoc] = 0;
CFStringAppendCString(outputString, (const char *)buffer, __CFStringGetEightBitStringEncoding());
CFStringAppend(outputString, decimalSeparator);
CFStringAppendCString(outputString, (const char *)(buffer + decimalPointLoc + 1), __CFStringGetEightBitStringEncoding());
appended = true;
}
}
}
break;
}
if (!appended) CFStringAppendCString(outputString, (const char *)buffer, __CFStringGetEightBitStringEncoding());
_CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded());
if (dynamicBuffer) {
CFAllocatorDeallocate(kCFAllocatorSystemDefault, dynamicBuffer);
}
if (dynamicFormatBuffer) {
CFAllocatorDeallocate(kCFAllocatorSystemDefault, dynamicFormatBuffer);
}
}
break;
case CFFormatLiteralType:
oldLength = __CFStringFormatOutputLengthIfNeeded();
if (cformat) {
__CFStringAppendBytes(outputString, (const char *)(cformat+specs[curSpec].loc), specs[curSpec].len, __CFStringGetEightBitStringEncoding());
} else {
CFStringAppendCharacters(outputString, uformat+specs[curSpec].loc, specs[curSpec].len);
}
_CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded());
break;
case CFFormatIncompleteSpecifierType:
oldLength = __CFStringFormatOutputLengthIfNeeded();
_CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, oldLength);
break;
case CFFormatPascalCharsType:
case CFFormatCharsType:
oldLength = __CFStringFormatOutputLengthIfNeeded();
if (values[specs[curSpec].mainArgNum].value.pointerValue == NULL) {
CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII);
} else {
int len;
const char *str = (const char *)values[specs[curSpec].mainArgNum].value.pointerValue;
if (specs[curSpec].type == CFFormatPascalCharsType) { // Pascal string case
len = ((unsigned char *)str)[0];
str++;
if (hasPrecision && precision < len) len = precision;
} else { // C-string case
if (!hasPrecision) { // No precision, so rely on the terminating null character
len = strlen(str);
} else { // Don't blindly call strlen() if there is a precision; the string might not have a terminating null (3131988)
const char *terminatingNull = (const char *)memchr(str, 0, precision); // Basically strlen() on only the first precision characters of str
if (terminatingNull) { // There was a null in the first precision characters
len = terminatingNull - str;
} else {
len = precision;
}
}
}
// Since the spec says the behavior of the ' ', '0', '#', and '+' flags is undefined for
// '%s', and since we have ignored them in the past, the behavior is hereby cast in stone
// to ignore those flags (and, say, never pad with '0' instead of space).
if (specs[curSpec].flags & kCFStringFormatMinusFlag) {
__CFStringAppendBytes(outputString, str, len, __CFStringGetSystemEncoding());
if (hasWidth && width > len) {
int w = width - len; // We need this many spaces; do it ten at a time
do {__CFStringAppendBytes(outputString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0);
}
} else {
if (hasWidth && width > len) {
int w = width - len; // We need this many spaces; do it ten at a time
do {__CFStringAppendBytes(outputString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0);
}
__CFStringAppendBytes(outputString, str, len, __CFStringGetSystemEncoding());
}
}
_CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded());
break;
case CFFormatSingleUnicharType:
oldLength = __CFStringFormatOutputLengthIfNeeded();
ch = (UniChar)values[specs[curSpec].mainArgNum].value.int64Value;
CFStringAppendCharacters(outputString, &ch, 1);
_CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded());
break;
case CFFormatUnicharsType:
oldLength = __CFStringFormatOutputLengthIfNeeded();
up = (UniChar *)values[specs[curSpec].mainArgNum].value.pointerValue;
if (NULL == up) {
CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII);
} else {
int len;
if (hasPrecision) {
// if we have precision, still pay attention to earlier null termination, as we do with %s (19784466)
for (len = 0; (len < precision) && (0 != up[len]); len++);
} else {
// if no precision, then simply find the length
for (len = 0; 0 != up[len]; len++);
}
// Since the spec says the behavior of the ' ', '0', '#', and '+' flags is undefined for
// '%s', and since we have ignored them in the past, the behavior is hereby cast in stone
// to ignore those flags (and, say, never pad with '0' instead of space).
if (specs[curSpec].flags & kCFStringFormatMinusFlag) {
CFStringAppendCharacters(outputString, up, len);
if (hasWidth && width > len) {
int w = width - len; // We need this many spaces; do it ten at a time
do {__CFStringAppendBytes(outputString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0);
}
} else {
if (hasWidth && width > len) {
int w = width - len; // We need this many spaces; do it ten at a time
do {__CFStringAppendBytes(outputString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0);
}
CFStringAppendCharacters(outputString, up, len);
}
}
_CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded());
break;
case CFFormatCFType:
oldLength = __CFStringFormatOutputLengthIfNeeded();
if (specs[curSpec].configDictIndex != -1) { // config dict
CFTypeRef object = NULL;
switch (values[specs[curSpec].mainArgNum].type) {
case CFFormatLongType:
object = CFNumberCreate(tmpAlloc, kCFNumberSInt64Type, &(values[specs[curSpec].mainArgNum].value.int64Value));
break;
case CFFormatDoubleType:
#if LONG_DOUBLE_SUPPORT
if (CFFormatSize16 == values[specs[curSpec].mainArgNum].size) {
double aValue = values[specs[curSpec].mainArgNum].value.longDoubleValue; // losing precision
object = CFNumberCreate(tmpAlloc, kCFNumberDoubleType, &aValue);
} else
#endif
{
object = CFNumberCreate(tmpAlloc, kCFNumberDoubleType, &(values[specs[curSpec].mainArgNum].value.doubleValue));
}
break;
case CFFormatPointerType:
object = CFNumberCreate(tmpAlloc, kCFNumberCFIndexType, &(values[specs[curSpec].mainArgNum].value.pointerValue));
break;
case CFFormatPascalCharsType:
case CFFormatCharsType:
if (NULL != values[specs[curSpec].mainArgNum].value.pointerValue) {
CFMutableStringRef aString = CFStringCreateMutable(tmpAlloc, 0);
int len;
const char *str = (const char *)values[specs[curSpec].mainArgNum].value.pointerValue;
if (specs[curSpec].type == CFFormatPascalCharsType) { // Pascal string case
len = ((unsigned char *)str)[0];
str++;
if (hasPrecision && precision < len) len = precision;
} else { // C-string case
if (!hasPrecision) { // No precision, so rely on the terminating null character
len = strlen(str);
} else { // Don't blindly call strlen() if there is a precision; the string might not have a terminating null (3131988)
const char *terminatingNull = (const char *)memchr(str, 0, precision); // Basically strlen() on only the first precision characters of str
if (terminatingNull) { // There was a null in the first precision characters
len = terminatingNull - str;
} else {
len = precision;
}
}
}
// Since the spec says the behavior of the ' ', '0', '#', and '+' flags is undefined for
// '%s', and since we have ignored them in the past, the behavior is hereby cast in stone
// to ignore those flags (and, say, never pad with '0' instead of space).
if (specs[curSpec].flags & kCFStringFormatMinusFlag) {
__CFStringAppendBytes(aString, str, len, __CFStringGetSystemEncoding());
if (hasWidth && width > len) {
int w = width - len; // We need this many spaces; do it ten at a time
do {__CFStringAppendBytes(aString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0);
}
} else {
if (hasWidth && width > len) {
int w = width - len; // We need this many spaces; do it ten at a time
do {__CFStringAppendBytes(aString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0);
}
__CFStringAppendBytes(aString, str, len, __CFStringGetSystemEncoding());
}
object = aString;
}
break;
case CFFormatSingleUnicharType:
ch = (UniChar)values[specs[curSpec].mainArgNum].value.int64Value;
object = CFStringCreateWithCharactersNoCopy(tmpAlloc, &ch, 1, kCFAllocatorNull);
break;
case CFFormatUnicharsType:
//??? need to handle width, precision, and padding arguments
up = (UniChar *)values[specs[curSpec].mainArgNum].value.pointerValue;
if (NULL != up) {
CFMutableStringRef aString = CFStringCreateMutable(tmpAlloc, 0);
int len;
for (len = 0; 0 != up[len]; len++);
// Since the spec says the behavior of the ' ', '0', '#', and '+' flags is undefined for
// '%s', and since we have ignored them in the past, the behavior is hereby cast in stone
// to ignore those flags (and, say, never pad with '0' instead of space).
if (hasPrecision && precision < len) len = precision;
if (specs[curSpec].flags & kCFStringFormatMinusFlag) {
CFStringAppendCharacters(aString, up, len);
if (hasWidth && width > len) {
int w = width - len; // We need this many spaces; do it ten at a time
do {__CFStringAppendBytes(aString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0);
}
} else {
if (hasWidth && width > len) {
int w = width - len; // We need this many spaces; do it ten at a time
do {__CFStringAppendBytes(aString, " ", (w > 10 ? 10 : w), kCFStringEncodingASCII);} while ((w -= 10) > 0);
}
CFStringAppendCharacters(aString, up, len);
}
object = aString;
}
break;
case CFFormatCFType:
if (NULL != values[specs[curSpec].mainArgNum].value.pointerValue) object = CFRetain(values[specs[curSpec].mainArgNum].value.pointerValue);
break;
}
if (NULL != object) CFRelease(object);
} else if (NULL != values[specs[curSpec].mainArgNum].value.pointerValue) {
CFStringRef str = NULL;
if (contextDescFunc) {
bool found = NO;
str = contextDescFunc(values[specs[curSpec].mainArgNum].value.pointerValue, formatString, replacement, NO, &found);
if (found) {
str = CFRetain(replacement);
specsContext[numSpecsContext] = specs[curSpec];
numSpecsContext++;
}
}
if (!str) {
if (copyDescFunc) {
str = copyDescFunc(values[specs[curSpec].mainArgNum].value.pointerValue, formatOptions);
} else {
str = __CFCopyFormattingDescription(values[specs[curSpec].mainArgNum].value.pointerValue, formatOptions);
if (NULL == str) {
str = CFCopyDescription(values[specs[curSpec].mainArgNum].value.pointerValue);
}
}
}
if (str) {
CFStringAppend(outputString, str);
CFRelease(str);
} else {
CFStringAppendCString(outputString, "(null description)", kCFStringEncodingASCII);
}
} else {
CFStringAppendCString(outputString, "(null)", kCFStringEncodingASCII);
}
_CFStringFormatAppendMetadata(metadata, numSpecs, values, sizeArgNum, specs[curSpec], oldLength, __CFStringFormatOutputLengthIfNeeded());
break;
}
if (validFormatSpecifiers && specs[curSpec].type != CFFormatLiteralType && specs[curSpec].configDictIndex == -1) {
// We already took care of confirming special strings with config dictionaries;
// this makes other types of specs as validated as well
validatedInnerSpecs += 1;
}
}
for (SInt32 i = 0; i < numSpecsContext; i++) {
CFRange range = CFStringFind(outputString, replacement, 0);
CFStringRef str = contextDescFunc(values[specsContext[i].mainArgNum].value.pointerValue, outputString, replacement, true, NULL);
if (str) {
CFStringReplace(outputString, range, str);
CFRelease(str);
}
}
free(specsContext);
cleanup:
if (numConfigs > 0) va_end(copiedArgs);
if (specs != localSpecsBuffer) CFAllocatorDeallocate(tmpAlloc, specs);
if (values != localValuesBuffer) CFAllocatorDeallocate(tmpAlloc, values);
if (formatChars && (formatChars != localFormatBuffer)) CFAllocatorDeallocate(tmpAlloc, formatChars);
if (configs != localConfigs) CFAllocatorDeallocate(tmpAlloc, configs);
if (formattingConfig != NULL) CFRelease(formattingConfig);
if (metadataStorage && outReplacementMetadata) {
*outReplacementMetadata = CFArrayCreateCopy(kCFAllocatorSystemDefault, metadataStorage);
CFRelease(metadataStorage);
}
return success;
}