CF_INLINE Boolean __CFParseFormatSpec()

in CoreFoundation/String.subproj/CFString.c [6532:6762]


CF_INLINE Boolean __CFParseFormatSpec(const UniChar *uformat, const uint8_t *cformat, SInt32 *fmtIdx, SInt32 fmtLen, CFFormatSpec *spec, CFStringRef *configKeyPointer, CFErrorRef *errorPtr) {
    Boolean seenDot = false;
    Boolean seenSharp = false;
    Boolean seenOpenBracket = false;
    Boolean validBracketSequence = false;
    CFIndex keyLength = 0;
    CFIndex keyIndex = kCFNotFound;

    for (;;) {
	UniChar ch;
        if (fmtLen <= *fmtIdx) {	/* no type */
            spec->type = CFFormatIncompleteSpecifierType;
            return true;
        }
        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 true;
                } 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 true;
        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 true;
        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 true;
	case 'n':		/* %n is not handled correctly; for Leopard or newer apps, we disable it further */
            spec->type = CFFormatDummyPointerType;
	    spec->size = CFFormatSizePointer;  // 4 or 8 depending on LP64
	    return true;
	case 'p':	
	    spec->type = CFFormatPointerType;
	    spec->size = CFFormatSizePointer;  // 4 or 8 depending on LP64
	    return true;
	case 's':
	    spec->type = CFFormatCharsType;
	    spec->size = CFFormatSizePointer;  // 4 or 8 depending on LP64
	    return true;
	case 'S':
	    spec->type = CFFormatUnicharsType;
	    spec->size = CFFormatSizePointer;  // 4 or 8 depending on LP64
	    return true;
        case 'C':
            spec->type = CFFormatSingleUnicharType;
            spec->size = CFFormatSize2;
            return true;
	case 'P':
	    spec->type = CFFormatPascalCharsType;
	    spec->size = CFFormatSizePointer;  // 4 or 8 depending on LP64
	    return true;
	case '@':
	    if (seenSharp) {
		seenSharp = false;
		keyIndex = *fmtIdx;
		break;
	    } else {
		spec->type = CFFormatCFType;
		spec->size = CFFormatSizePointer;  // 4 or 8 depending on LP64
		return true;
	    }
	case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
            long long number = 0;
	    do {
                if (__builtin_smulll_overflow(number, 10, &number) || __builtin_saddll_overflow(number, ch - '0', &number) || number > INT64_MAX) {
                    if (errorPtr) *errorPtr = __CFCreateOverflowError();
                    return false;
                }
                if (cformat) ch = (UniChar)cformat[(*fmtIdx)++]; else ch = uformat[(*fmtIdx)++];
	    } while ((UInt32)(ch - '0') <= 9);
	    if ('$' == ch) {
                if (number > INT8_MAX) {
                    if (errorPtr) *errorPtr = __CFCreateOverflowError();
                    return false;
                }
		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 */
                if (number > INT32_MAX) {
                    if (errorPtr) *errorPtr = __CFCreateOverflowError();
                    return false;
                }
                spec->precArg = (SInt32)number;
	    } else {
                if (number > INT32_MAX) {
                    if (errorPtr) *errorPtr = __CFCreateOverflowError();
                    return false;
                }
		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 true;
	}
    }
    return true;
}