HAPError HAPStringWithFormatAndArguments()

in PAL/HAPBase+String.c [21:182]


HAPError HAPStringWithFormatAndArguments(char* bytes, size_t maxBytes, const char* format, va_list arguments) {
    HAPPrecondition(bytes);
    HAPPrecondition(format);

    char c = format[0];
    size_t i = 1;
    size_t n = 0;
    while (c) {
        if (c == '%') {
            // insert value
            c = format[i++];     // format defaults:
            char prefix = ' ';   // fill with space
            char sign = 0;       // do not us a positive sign
            uint32_t length = 0; // no long type
            uint32_t width = 0;  // minimum width
            // flags field
            while (c == '0' || c == '+' || c == ' ') {
                if (c == '0') {
                    prefix = '0';
                } else {
                    sign = c;
                }
                c = format[i++];
            }
            // width field
            while (c >= '0' && c <= '9') {
                width = width * 10 + (uint32_t)(c - '0');
                c = format[i++];
            }
            // length field
            if (c == 'l') {
                length = 1;
                c = format[i++];
                if (c == 'l') {
                    length = 2;
                    c = format[i++];
                }
            } else if (c == 'z') {
                length = 3;
                c = format[i++];
            }
            // type field
            char buffer[32]; // temporary string buffer
            char* string = NULL;
            size_t strLen = 0;
            int64_t sValue = 0;
            uint64_t uValue = 0;
            switch (c) {
                case '%': {
                    string = "%";
                } break;
                case 'd':
                case 'i': {
                    if (length == 0) {
                        sValue = (int64_t) va_arg(arguments, int);
                    } else if (length == 1) { // 'l'
                        sValue = (int64_t) va_arg(arguments, long);
                    } else if (length == 2) { // 'll'
                        sValue = (int64_t) va_arg(arguments, long long);
                    } else { // 'z'
                        sValue = (int64_t) va_arg(arguments, size_t);
                    }
                } break;
                case 'x':
                case 'X':
                case 'u': {
                    if (length == 0) {
                        uValue = (uint64_t) va_arg(arguments, unsigned int);
                    } else if (length == 1) { // 'l'
                        uValue = (uint64_t) va_arg(arguments, unsigned long);
                    } else if (length == 2) { // 'll'
                        uValue = (uint64_t) va_arg(arguments, long long);
                    } else { // 'z'
                        uValue = (uint64_t) va_arg(arguments, size_t);
                    }
                } break;
                case 'p': {
                    uValue = (uint64_t)(uintptr_t) va_arg(arguments, void*);
                } break;
                case 's': {
                    string = va_arg(arguments, char*);
                    if (string == NULL) {
                        string = "(null)";
                    }
                } break;
                case 'c': {
                    buffer[0] = (char) va_arg(arguments, int);
                    buffer[1] = 0;
                    string = buffer;
                    strLen = 1;
                } break;
                case 'g': {
                    double d = (double) va_arg(arguments, double);
                    HAPError res = HAPFloatGetDescription(buffer, sizeof buffer, (float) d);
                    if (res != kHAPError_None) {
                        return res;
                    }
                    string = buffer;
                } break;
                default: {
                    HAPLogError(&kHAPLog_Default, "Unsupported format string type specifier: %%%c", c);
                    HAPPreconditionFailure();
                }
            }
            if (!string) { // convert integer to string
                HAPError res;
                if (c == 'i' || c == 'd') { // signed
                    if (sValue < 0) {
                        sValue = -sValue;
                        sign = '-';
                    }
                    uValue = (uint64_t) sValue;
                }
                if (c == 'x') {
                    res = HAPUInt64GetHexDescription(uValue, buffer, sizeof buffer, kHAPLetterCase_Lowercase);
                } else if (c == 'X') {
                    res = HAPUInt64GetHexDescription(uValue, buffer, sizeof buffer, kHAPLetterCase_Uppercase);
                } else if (c == 'p') {
                    buffer[0] = '0';
                    buffer[1] = 'x';
                    res = HAPUInt64GetHexDescription(uValue, buffer + 2, sizeof buffer - 2, kHAPLetterCase_Lowercase);
                } else {
                    res = HAPUInt64GetDescription(uValue, buffer, sizeof buffer);
                }
                if (res != kHAPError_None) {
                    return res;
                }
                string = buffer;
            }
            if (strLen == 0) {
                strLen = HAPStringGetNumBytes(string);
            }
            size_t totLen = strLen;
            if (sign)
                totLen++;
            if (n + totLen >= maxBytes || n + width >= maxBytes) {
                return kHAPError_OutOfResources;
            }
            if (sign && prefix == '0') {
                bytes[n++] = sign; // write sign before zeros
            }
            while (totLen < width) {
                bytes[n++] = prefix;
                totLen++;
            }
            if (sign && prefix == ' ') {
                bytes[n++] = sign; // write sign after spaces
            }
            HAPRawBufferCopyBytes(&bytes[n], string, strLen);
            n += strLen;
        } else {
            // copy char to output
            if (n + 1 >= maxBytes) {
                return kHAPError_OutOfResources;
            }
            bytes[n++] = c;
        }
        c = format[i++];
    }
    bytes[n] = 0;
    return kHAPError_None;
}