in Frameworks/CoreFoundation/String.subproj/CFString.c [5815:6026]
CF_INLINE void __CFParseFormatSpec(const UniChar *uformat, const uint8_t *cformat, SInt32 *fmtIdx, SInt32 fmtLen, CFFormatSpec *spec, CFStringRef *configKeyPointer) {
Boolean seenDot = false;
Boolean seenSharp = false;
Boolean seenOpenBracket = false;
Boolean validBracketSequence = false;
CFIndex keyLength = 0;
CFIndex keyIndex = kCFNotFound;
for (;;) {
UniChar ch;
if (fmtLen <= *fmtIdx) return; /* no type */
if (cformat) ch = (UniChar)cformat[(*fmtIdx)++]; else ch = uformat[(*fmtIdx)++];
if (keyIndex >= 0) {
if ((ch < '0') || ((ch > '9') && (ch < 'A')) || ((ch > 'Z') && (ch < 'a') && (ch != '_')) || (ch > 'z')) {
if (ch == ']') {
if (seenOpenBracket) {
validBracketSequence = true;
keyLength = (*fmtIdx) - 1 - keyIndex;
}
} else if (ch == '@') {
if (validBracketSequence) {
spec->flags |= kCFStringFormatEntityMarkerFlag;
} else {
keyLength = (*fmtIdx) - 1 - keyIndex;
}
spec->flags |= kCFStringFormatExternalSpecFlag;
spec->type = CFFormatCFType;
spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64
if ((NULL != configKeyPointer) && (keyLength > 0)) {
if (cformat) {
*configKeyPointer = CFStringCreateWithBytes(NULL, cformat + keyIndex, keyLength, __CFStringGetEightBitStringEncoding(), FALSE);
} else {
*configKeyPointer = CFStringCreateWithCharactersNoCopy(NULL, uformat + keyIndex, keyLength, kCFAllocatorNull);
}
}
return;
} else {
keyIndex = kCFNotFound;
}
}
continue;
}
reswtch:switch (ch) {
case '#': // ignored for now
seenSharp = true;
break;
case '[':
if (!seenOpenBracket) { // We can only have one
seenOpenBracket = true;
keyIndex = *fmtIdx;
}
break;
case 0x20:
if (!(spec->flags & kCFStringFormatPlusFlag)) spec->flags |= kCFStringFormatSpaceFlag;
break;
case '-':
spec->flags |= kCFStringFormatMinusFlag;
spec->flags &= ~kCFStringFormatZeroFlag; // remove zero flag
break;
case '+':
spec->flags |= kCFStringFormatPlusFlag;
spec->flags &= ~kCFStringFormatSpaceFlag; // remove space flag
break;
case '0':
if (seenDot) { // after we see '.' and then we see '0', it is 0 precision. We should not see '.' after '0' if '0' is the zero padding flag
spec->precArg = 0;
break;
}
if (!(spec->flags & kCFStringFormatMinusFlag)) spec->flags |= kCFStringFormatZeroFlag;
break;
case 'h':
if (*fmtIdx < fmtLen) {
// fetch next character, don't increment fmtIdx
if (cformat) ch = (UniChar)cformat[(*fmtIdx)]; else ch = uformat[(*fmtIdx)];
if ('h' == ch) { // 'hh' for char, like 'c'
(*fmtIdx)++;
spec->size = CFFormatSize1;
break;
}
}
spec->size = CFFormatSize2;
break;
case 'l':
if (*fmtIdx < fmtLen) {
// fetch next character, don't increment fmtIdx
if (cformat) ch = (UniChar)cformat[(*fmtIdx)]; else ch = uformat[(*fmtIdx)];
if ('l' == ch) { // 'll' for long long, like 'q'
(*fmtIdx)++;
spec->size = CFFormatSize8;
break;
}
}
spec->size = CFFormatSizeLong; // 4 or 8 depending on LP64
break;
#if LONG_DOUBLE_SUPPORT
case 'L':
spec->size = CFFormatSize16;
break;
#endif
case 'q':
spec->size = CFFormatSize8;
break;
case 't': case 'z':
spec->size = CFFormatSizeLong; // 4 or 8 depending on LP64
break;
case 'j':
spec->size = CFFormatSize8;
break;
case 'c':
spec->type = CFFormatLongType;
spec->size = CFFormatSize1;
return;
case 'D': case 'd': case 'i': case 'U': case 'u':
// we can localize all but octal or hex
if (_CFExecutableLinkedOnOrAfter(CFSystemVersionMountainLion)) spec->flags |= kCFStringFormatLocalizable;
spec->numericFormatStyle = CFFormatStyleDecimal;
if (ch == 'u' || ch == 'U') spec->numericFormatStyle = CFFormatStyleUnsigned;
// fall thru
case 'O': case 'o': case 'x': case 'X':
spec->type = CFFormatLongType;
// Seems like if spec->size == 0, we should spec->size = CFFormatSize4. However, 0 is handled correctly.
return;
case 'f': case 'F': case 'g': case 'G': case 'e': case 'E': {
// we can localize all but hex float output
if (_CFExecutableLinkedOnOrAfter(CFSystemVersionMountainLion)) spec->flags |= kCFStringFormatLocalizable;
char lch = (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch;
spec->numericFormatStyle = ((lch == 'e' || lch == 'g') ? CFFormatStyleScientific : 0) | ((lch == 'f' || lch == 'g') ? CFFormatStyleDecimal : 0);
if (seenDot && spec->precArg == -1 && spec->precArgNum == -1) { // for the cases that we have '.' but no precision followed, not even '*'
spec->precArg = 0;
}
}
// fall thru
case 'a': case 'A':
spec->type = CFFormatDoubleType;
if (spec->size != CFFormatSize16) spec->size = CFFormatSize8;
return;
case 'n': /* %n is not handled correctly; for Leopard or newer apps, we disable it further */
spec->type = 1 ? CFFormatDummyPointerType : CFFormatPointerType;
spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64
return;
case 'p':
spec->type = CFFormatPointerType;
spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64
return;
case 's':
spec->type = CFFormatCharsType;
spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64
return;
case 'S':
spec->type = CFFormatUnicharsType;
spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64
return;
case 'C':
spec->type = CFFormatSingleUnicharType;
spec->size = CFFormatSize2;
return;
case 'P':
spec->type = CFFormatPascalCharsType;
spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64
return;
case '@':
if (seenSharp) {
seenSharp = false;
keyIndex = *fmtIdx;
break;
} else {
spec->type = CFFormatCFType;
spec->size = CFFormatSizePointer; // 4 or 8 depending on LP64
return;
}
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
int64_t number = 0;
do {
number = 10 * number + (ch - '0');
if (cformat) ch = (UniChar)cformat[(*fmtIdx)++]; else ch = uformat[(*fmtIdx)++];
} while ((UInt32)(ch - '0') <= 9);
if ('$' == ch) {
if (-2 == spec->precArgNum) {
spec->precArgNum = (int8_t)number - 1; // Arg numbers start from 1
} else if (-2 == spec->widthArgNum) {
spec->widthArgNum = (int8_t)number - 1; // Arg numbers start from 1
} else {
spec->mainArgNum = (int8_t)number - 1; // Arg numbers start from 1
}
break;
} else if (seenDot) { /* else it's either precision or width */
spec->precArg = (SInt32)number;
} else {
spec->widthArg = (SInt32)number;
}
goto reswtch;
}
case '*':
spec->widthArgNum = -2;
break;
case '.':
seenDot = true;
if (cformat) ch = (UniChar)cformat[(*fmtIdx)++]; else ch = uformat[(*fmtIdx)++];
if ('*' == ch) {
spec->precArgNum = -2;
break;
}
goto reswtch;
default:
spec->type = CFFormatLiteralType;
return;
}
}
}