1 /*
2     SPDX-FileCopyrightText: 2009 Harald Fernengel <harry@kdevelop.org>
3     SPDX-FileCopyrightText: 2017 René J.V. Bertin <rjvbertin@gmail.com>
4 
5     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7 
8 #include <qdatetime.h>
9 #include <qdebug.h>
10 #include <qstring.h>
11 #include <qstringlist.h>
12 #include <qvariant.h>
13 #include <qvarlengtharray.h>
14 
15 #include <CoreFoundation/CoreFoundation.h>
16 
17 #include <sys/sysctl.h>
18 
19 /* helper classes to convert from CF types to Qt */
20 
q_toString(const CFStringRef & str)21 static QString q_toString(const CFStringRef &str)
22 {
23     CFIndex length = CFStringGetLength(str);
24     QVarLengthArray<UniChar> buffer(length);
25 
26     CFRange range = {0, length};
27     CFStringGetCharacters(str, range, buffer.data());
28     return QString(reinterpret_cast<const QChar *>(buffer.data()), length);
29 }
30 
31 template<typename T>
convertCFNumber(const CFNumberRef & num,CFNumberType type)32 static inline T convertCFNumber(const CFNumberRef &num, CFNumberType type)
33 {
34     T n;
35     CFNumberGetValue(num, type, &n);
36     return n;
37 }
38 
q_toVariant(const CFTypeRef & obj)39 static QVariant q_toVariant(const CFTypeRef &obj)
40 {
41     const CFTypeID typeId = CFGetTypeID(obj);
42 
43     if (typeId == CFStringGetTypeID()) {
44         return QVariant(q_toString(static_cast<const CFStringRef>(obj)));
45     }
46 
47     if (typeId == CFNumberGetTypeID()) {
48         const CFNumberRef num = static_cast<const CFNumberRef>(obj);
49         const CFNumberType type = CFNumberGetType(num);
50         switch (type) {
51         case kCFNumberSInt8Type:
52             return QVariant::fromValue(convertCFNumber<char>(num, type));
53         case kCFNumberSInt16Type:
54             return QVariant::fromValue(convertCFNumber<qint16>(num, type));
55         case kCFNumberSInt32Type:
56             return QVariant::fromValue(convertCFNumber<qint32>(num, type));
57         case kCFNumberSInt64Type:
58             return QVariant::fromValue(convertCFNumber<qint64>(num, type));
59         case kCFNumberCharType:
60             return QVariant::fromValue(convertCFNumber<uchar>(num, type));
61         case kCFNumberShortType:
62             return QVariant::fromValue(convertCFNumber<short>(num, type));
63         case kCFNumberIntType:
64             return QVariant::fromValue(convertCFNumber<int>(num, type));
65         case kCFNumberLongType:
66             return QVariant::fromValue(convertCFNumber<long>(num, type));
67         case kCFNumberLongLongType:
68             return QVariant::fromValue(convertCFNumber<long long>(num, type));
69         case kCFNumberFloatType:
70             return QVariant::fromValue(convertCFNumber<float>(num, type));
71         case kCFNumberDoubleType:
72             return QVariant::fromValue(convertCFNumber<double>(num, type));
73         default:
74             if (CFNumberIsFloatType(num)) {
75                 return QVariant::fromValue(convertCFNumber<double>(num, kCFNumberDoubleType));
76             }
77             return QVariant::fromValue(convertCFNumber<quint64>(num, kCFNumberLongLongType));
78         }
79     }
80 
81     if (typeId == CFDateGetTypeID()) {
82         QDateTime dt;
83         dt.setSecsSinceEpoch(qint64(kCFAbsoluteTimeIntervalSince1970));
84         return dt.addSecs(int(CFDateGetAbsoluteTime(static_cast<const CFDateRef>(obj))));
85     }
86 
87     if (typeId == CFDataGetTypeID()) {
88         const CFDataRef cfdata = static_cast<const CFDataRef>(obj);
89         return QByteArray(reinterpret_cast<const char *>(CFDataGetBytePtr(cfdata)), CFDataGetLength(cfdata));
90     }
91 
92     if (typeId == CFBooleanGetTypeID()) {
93         return QVariant(bool(CFBooleanGetValue(static_cast<const CFBooleanRef>(obj))));
94     }
95 
96     if (typeId == CFArrayGetTypeID()) {
97         const CFArrayRef cfarray = static_cast<const CFArrayRef>(obj);
98         QList<QVariant> list;
99         CFIndex size = CFArrayGetCount(cfarray);
100         bool metNonString = false;
101         for (CFIndex i = 0; i < size; ++i) {
102             QVariant value = q_toVariant(CFArrayGetValueAtIndex(cfarray, i));
103             if (value.type() != QVariant::String) {
104                 metNonString = true;
105             }
106             list << value;
107         }
108         if (metNonString) {
109             return list;
110         } else {
111             return QVariant(list).toStringList();
112         }
113     }
114 
115     if (typeId == CFDictionaryGetTypeID()) {
116         const CFDictionaryRef cfdict = static_cast<const CFDictionaryRef>(obj);
117         const CFTypeID arrayTypeId = CFArrayGetTypeID();
118         int size = int(CFDictionaryGetCount(cfdict));
119         QVarLengthArray<CFPropertyListRef> keys(size);
120         QVarLengthArray<CFPropertyListRef> values(size);
121         CFDictionaryGetKeysAndValues(cfdict, keys.data(), values.data());
122 
123         QMultiMap<QString, QVariant> map;
124         for (int i = 0; i < size; ++i) {
125             QString key = q_toString(static_cast<const CFStringRef>(keys[i]));
126 
127             if (CFGetTypeID(values[i]) == arrayTypeId) {
128                 const CFArrayRef cfarray = static_cast<const CFArrayRef>(values[i]);
129                 CFIndex arraySize = CFArrayGetCount(cfarray);
130                 for (CFIndex j = arraySize - 1; j >= 0; --j) {
131                     map.insert(key, q_toVariant(CFArrayGetValueAtIndex(cfarray, j)));
132                 }
133             } else {
134                 map.insert(key, q_toVariant(values[i]));
135             }
136         }
137         return map;
138     }
139 
140     return QVariant();
141 }
142 
q_toVariantMap(const CFMutableDictionaryRef & dict)143 QMap<QString, QVariant> q_toVariantMap(const CFMutableDictionaryRef &dict)
144 {
145     Q_ASSERT(dict);
146 
147     QMap<QString, QVariant> result;
148 
149     const int count = CFDictionaryGetCount(dict);
150     QVarLengthArray<void *> keys(count);
151     QVarLengthArray<void *> values(count);
152 
153     CFDictionaryGetKeysAndValues(dict, const_cast<const void **>(keys.data()), const_cast<const void **>(values.data()));
154 
155     for (int i = 0; i < count; ++i) {
156         const QString key = q_toString((CFStringRef)keys[i]);
157         const QVariant value = q_toVariant((CFTypeRef)values[i]);
158         result[key] = value;
159     }
160 
161     return result;
162 }
163 
q_sysctlbyname(const char * name,QString & result)164 bool q_sysctlbyname(const char *name, QString &result)
165 {
166     char *property = nullptr;
167     size_t size = 0;
168     int error = 0;
169     if (name && sysctlbyname(name, nullptr, &size, nullptr, 0) == 0 && size > 0) {
170         property = new char[size];
171         error = sysctlbyname(name, property, &size, nullptr, 0);
172         if (!error) {
173             result = QLatin1String(property);
174         }
175         delete[] property;
176     }
177     return !error;
178 }
179