master ohmu/fastfetch-openbsd / src / common / apple / cf_helpers.c
ohmu  ·  2026-10-15
  1#include "cf_helpers.h"
  2#include "common/debug.h"
  3
  4const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result) {
  5    if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
  6        if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt64Type, result)) {
  7            FF_DEBUG("CFNumberGetValue(kCFNumberSInt64Type) failed, the number is of type 0x%08lx",
  8                (unsigned long) CFNumberGetType((CFNumberRef) cf));
  9            return "Number type is not SInt64";
 10        }
 11        return nullptr;
 12    } else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
 13        if (CFDataGetLength((CFDataRef) cf) != sizeof(int64_t)) {
 14            FF_DEBUG("CFData holds %ld bytes, int64_t needs %zu",
 15                (long) CFDataGetLength((CFDataRef) cf), sizeof(int64_t));
 16            return "Data length is not sizeof(int64_t)";
 17        }
 18        CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(int64_t)), (uint8_t*) result);
 19        return nullptr;
 20    }
 21
 22    FF_DEBUG("Expected a CFNumber or CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
 23    return "TypeID is neither 'CFNumber' nor 'CFData'";
 24}
 25
 26const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result) {
 27    if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
 28        if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberSInt32Type, result)) {
 29            FF_DEBUG("CFNumberGetValue(kCFNumberSInt32Type) failed, the number is of type 0x%08lx",
 30                (unsigned long) CFNumberGetType((CFNumberRef) cf));
 31            return "Number type is not SInt32";
 32        }
 33        return nullptr;
 34    } else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
 35        if (CFDataGetLength((CFDataRef) cf) != sizeof(*result)) {
 36            FF_DEBUG("CFData holds %ld bytes, int32_t needs %zu",
 37                (long) CFDataGetLength((CFDataRef) cf), sizeof(*result));
 38            return "Data length is not sizeof(int32_t)";
 39        }
 40        CFDataGetBytes((CFDataRef) cf, CFRangeMake(0, sizeof(*result)), (uint8_t*) result);
 41        return nullptr;
 42    }
 43
 44    FF_DEBUG("Expected a CFNumber or CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
 45    return "TypeID is neither 'CFNumber' nor 'CFData'";
 46}
 47
 48const char* ffCfNumGetDouble(CFTypeRef cf, double* result) {
 49    if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
 50        if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberDoubleType, result) &&
 51            !CFNumberGetValue((CFNumberRef) cf, kCFNumberFloatType, result)) {
 52            FF_DEBUG("CFNumber is of type 0x%08lx, neither Double nor Float",
 53                (unsigned long) CFNumberGetType((CFNumberRef) cf));
 54            return "Number type is not Double or Float";
 55        }
 56        return nullptr;
 57    }
 58
 59    FF_DEBUG("Expected a CFNumber, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
 60    return "TypeID is neither 'CFNumber'";
 61}
 62
 63const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result) {
 64    if (CFGetTypeID(cf) != CFDateGetTypeID()) {
 65        FF_DEBUG("Expected a CFDate, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
 66        return "TypeID is not 'CFDate'";
 67    }
 68
 69    CFAbsoluteTime absTime = CFDateGetAbsoluteTime((CFDateRef) cf);
 70    // Convert from seconds to milliseconds and add the difference between 1970 and 2001 in milliseconds
 71    *result = (uint64_t) ((absTime + 978307200 /*kCFAbsoluteTimeIntervalSince1970*/) * 1000);
 72    return nullptr;
 73}
 74
 75const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
 76    ffStrbufClear(result);
 77    if (!cf) {
 78        return nullptr;
 79    }
 80
 81    if (CFGetTypeID(cf) == CFStringGetTypeID()) {
 82        CFStringRef cfStr = (CFStringRef) cf;
 83
 84        const char* cstr = CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8);
 85        if (cstr) {
 86            ffStrbufSetS(result, cstr);
 87        } else {
 88            uint32_t length = (uint32_t) CFStringGetLength(cfStr);
 89            if (length == 0) {
 90                return nullptr;
 91            }
 92            ffStrbufEnsureFixedLengthFree(result, (uint32_t) CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8));
 93            if (!CFStringGetCString(cfStr, result->chars, result->allocated, kCFStringEncodingUTF8)) {
 94                FF_DEBUG("CFStringGetCString() failed for a %u character string", (unsigned) length);
 95                return "CFStringGetCString() failed";
 96            }
 97            // CFStringGetCString ensures the buffer is NUL terminated
 98            // https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring
 99            result->length = (uint32_t) strnlen(result->chars, (uint32_t) result->allocated);
100        }
101    } else if (CFGetTypeID(cf) == CFDataGetTypeID()) {
102        CFDataRef cfData = (CFDataRef) cf;
103        uint32_t length = (uint32_t) CFDataGetLength(cfData);
104        if (length == 0) {
105            return nullptr;
106        }
107        ffStrbufEnsureFixedLengthFree(result, length + 1);
108        CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
109        result->length = (uint32_t) strnlen(result->chars, length);
110        result->chars[result->length] = '\0';
111    } else {
112        FF_DEBUG("Expected a CFString or CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
113        return "TypeID is neither 'CFString' nor 'CFData'";
114    }
115
116    return nullptr;
117}
118
119const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result) {
120    ffStrbufClear(result);
121    if (!cf) {
122        return nullptr;
123    }
124
125    if (CFGetTypeID(cf) == CFDataGetTypeID()) {
126        CFDataRef cfData = (CFDataRef) cf;
127        uint32_t length = (uint32_t) CFDataGetLength(cfData);
128        if (length == 0) {
129            return nullptr;
130        }
131        ffStrbufEnsureFixedLengthFree(result, length + 1);
132        CFDataGetBytes(cfData, CFRangeMake(0, length), (uint8_t*) result->chars);
133        result->length = length;
134        result->chars[result->length] = '\0';
135    } else {
136        FF_DEBUG("Expected a CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
137        return "TypeID is not 'CFData'";
138    }
139
140    return nullptr;
141}
142
143const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
144    CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
145    if (cf == nullptr) {
146        FF_DEBUG("The dictionary has no value for the key");
147        return "CFDictionaryGetValue() failed";
148    }
149
150    return ffCfStrGetString(cf, result);
151}
152
153const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result) {
154    CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
155    if (cf == nullptr) {
156        FF_DEBUG("The dictionary has no value for the key");
157        return "CFDictionaryGetValue() failed";
158    }
159
160    return ffCfDataGetDataAsString(cf, result);
161}
162
163const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result) {
164    CFBooleanRef cf = (CFBooleanRef) CFDictionaryGetValue(dict, key);
165    if (cf == nullptr) {
166        FF_DEBUG("The dictionary has no value for the key");
167        return "CFDictionaryGetValue() failed";
168    }
169
170    if (CFGetTypeID(cf) != CFBooleanGetTypeID()) {
171        FF_DEBUG("Expected a CFBoolean, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
172        return "TypeID is not 'CFBoolean'";
173    }
174
175    *result = CFBooleanGetValue(cf);
176    return nullptr;
177}
178
179const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result) {
180    CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
181    if (cf == nullptr) {
182        FF_DEBUG("The dictionary has no value for the key");
183        return "CFDictionaryGetValue() failed";
184    }
185
186    return ffCfNumGetInt(cf, result);
187}
188
189const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result) {
190    CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
191    if (cf == nullptr) {
192        FF_DEBUG("The dictionary has no value for the key");
193        return "CFDictionaryGetValue() failed";
194    }
195
196    return ffCfNumGetInt64(cf, result);
197}
198
199const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result) {
200    CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
201    if (cf == nullptr) {
202        FF_DEBUG("The dictionary has no value for the key");
203        return "CFDictionaryGetValue() failed";
204    }
205
206    return ffCfNumGetDouble(cf, result);
207}
208
209const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length) {
210    CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
211    if (cf == nullptr) {
212        FF_DEBUG("The dictionary has no value for the key");
213        return "CFDictionaryGetValue() failed";
214    }
215
216    if (CFGetTypeID(cf) != CFDataGetTypeID()) {
217        FF_DEBUG("Expected a CFData, got TypeID 0x%08lx", (unsigned long) CFGetTypeID(cf));
218        return "TypeID is not 'CFData'";
219    }
220
221    CFIndex trueLength = CFDataGetLength((CFDataRef) cf);
222
223    if (trueLength < offset + size) {
224        FF_DEBUG("The CFData holds %ld bytes, the caller asked for [%u, %u)",
225            (long) trueLength, offset, offset + size);
226        return "Data length is less than offset + size";
227    }
228
229    if (length) {
230        *length = (uint32_t) trueLength;
231    }
232
233    CFDataGetBytes((CFDataRef) cf, CFRangeMake(offset, size), result);
234    return nullptr;
235}
236
237const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result) {
238    CFDictionaryRef cf = (CFDictionaryRef) CFDictionaryGetValue(dict, key);
239    if (cf == nullptr || CFGetTypeID(cf) != CFDictionaryGetTypeID()) {
240        FF_DEBUG("Expected a CFDictionary, got %s",
241            cf == nullptr ? "no value at all" : "a value of another type");
242        return "TypeID is not 'CFDictionary'";
243    }
244
245    *result = cf;
246    return nullptr;
247}
248
249const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result) {
250    CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
251    if (cf == nullptr) {
252        FF_DEBUG("The dictionary has no value for the key");
253        return "CFDictionaryGetValue() failed";
254    }
255
256    return ffCfDateGetEpoch(cf, result);
257}