|
15 | 15 | #include <errno.h>
|
16 | 16 | #include <string.h>
|
17 | 17 | #include <stdio.h>
|
| 18 | +#include <assert.h> |
18 | 19 |
|
19 | 20 | #if DEPLOYMENT_TARGET_WINDOWS
|
20 | 21 | #include <io.h>
|
@@ -1166,3 +1167,162 @@ CF_PRIVATE void _CFIterateDirectory(CFStringRef directoryPath, Boolean appendSla
|
1166 | 1167 | #endif
|
1167 | 1168 | }
|
1168 | 1169 |
|
| 1170 | +#if DEPLOYMENT_RUNTIME_SWIFT |
| 1171 | + |
| 1172 | +// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html |
| 1173 | +// Version 0.8 |
| 1174 | + |
| 1175 | +// This may not be safe to assume |
| 1176 | +#define _kCFXDGStringEncoding kCFStringEncodingUTF8 |
| 1177 | + |
| 1178 | +// All paths set in these environment variables must be absolute. If an implementation encounters a relative path in any of these variables it should consider the path invalid and ignore it. |
| 1179 | +static CFStringRef _CFXDGCreateHome(void) { |
| 1180 | + const char *home = __CFgetenv("HOME"); |
| 1181 | + if (home && strnlen(home, CFMaxPathSize) > 0) { |
| 1182 | + return CFStringCreateWithCString(kCFAllocatorSystemDefault, home, _kCFXDGStringEncoding); |
| 1183 | + } else { |
| 1184 | + return CFRetain(CFSTR("")); |
| 1185 | + } |
| 1186 | +} |
| 1187 | + |
| 1188 | +/// a single base directory relative to which user-specific data files should be written. This directory is defined by the environment variable $XDG_DATA_HOME. |
| 1189 | +CF_SWIFT_EXPORT |
| 1190 | +CFStringRef _CFXDGCreateDataHomePath(void) { |
| 1191 | + // $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. |
| 1192 | + const char *dataHome = __CFgetenv("XDG_DATA_HOME"); |
| 1193 | + if (dataHome && strnlen(dataHome, CFMaxPathSize) > 1 && dataHome[0] == '/') { |
| 1194 | + return CFStringCreateWithCString(kCFAllocatorSystemDefault, dataHome, _kCFXDGStringEncoding); |
| 1195 | + } else { |
| 1196 | + CFStringRef home = _CFXDGCreateHome(); |
| 1197 | + CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/.local/share"), home); |
| 1198 | + CFRelease(home); |
| 1199 | + return result; |
| 1200 | + } |
| 1201 | +} |
| 1202 | + |
| 1203 | +/// a single base directory relative to which user-specific configuration files should be written. This directory is defined by the environment variable $XDG_CONFIG_HOME. |
| 1204 | +CF_SWIFT_EXPORT |
| 1205 | +CFStringRef _CFXDGCreateConfigHomePath(void) { |
| 1206 | + // $XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used. |
| 1207 | + const char *configHome = __CFgetenv("XDG_CONFIG_HOME"); |
| 1208 | + if (configHome && strnlen(configHome, CFMaxPathSize) > 1 && configHome[0] == '/') { |
| 1209 | + return CFStringCreateWithCString(kCFAllocatorSystemDefault, configHome, _kCFXDGStringEncoding); |
| 1210 | + } else { |
| 1211 | + CFStringRef home = _CFXDGCreateHome(); |
| 1212 | + CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/.config"), home); |
| 1213 | + CFRelease(home); |
| 1214 | + return result; |
| 1215 | + } |
| 1216 | +} |
| 1217 | + |
| 1218 | +/// a set of preference ordered base directories relative to which data files should be searched. This set of directories is defined by the environment variable $XDG_DATA_DIRS. |
| 1219 | +CF_SWIFT_EXPORT |
| 1220 | +CFArrayRef _CFXDGCreateDataDirectoriesPaths(void) { |
| 1221 | + // $XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory. The directories in $XDG_DATA_DIRS should be seperated with a colon ':'. |
| 1222 | + // If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used. |
| 1223 | + const char *dataDirectoriesPaths = __CFgetenv("XDG_DATA_DIRS"); |
| 1224 | + if ((dataDirectoriesPaths == NULL) || (dataDirectoriesPaths[0] == '\0')) { |
| 1225 | + // Environmental variable not set. Return default value. |
| 1226 | + CFStringRef defaultPath[2]; |
| 1227 | + defaultPath[0] = CFSTR("/usr/local/share/"); |
| 1228 | + defaultPath[1] = CFSTR("/usr/share/"); |
| 1229 | + return CFArrayCreate(kCFAllocatorSystemDefault, (const void **)defaultPath, 2, &kCFTypeArrayCallBacks); |
| 1230 | + } |
| 1231 | + return _CFCreateCFArrayByTokenizingString(dataDirectoriesPaths, ':'); |
| 1232 | +} |
| 1233 | + |
| 1234 | + |
| 1235 | +/// a set of preference ordered base directories relative to which configuration files should be searched. This set of directories is defined by the environment variable $XDG_CONFIG_DIRS. |
| 1236 | +CF_SWIFT_EXPORT |
| 1237 | +CFArrayRef _CFXDGCreateConfigDirectoriesPaths(void) { |
| 1238 | + // $XDG_CONFIG_DIRS defines the preference-ordered set of base directories to search for configuration files in addition to the $XDG_CONFIG_HOME base directory. The directories in $XDG_CONFIG_DIRS should be seperated with a colon ':'. |
| 1239 | + // If $XDG_CONFIG_DIRS is either not set or empty, a value equal to /etc/xdg should be used. |
| 1240 | + const char *configDirectoriesPaths = __CFgetenv("XDG_CONFIG_DIRS"); |
| 1241 | + if ((configDirectoriesPaths == NULL) || (configDirectoriesPaths[0] == '\0')) { |
| 1242 | + //Environmental variable not set. Return default value. |
| 1243 | + CFStringRef defaultPath[1]; |
| 1244 | + defaultPath[0] = CFSTR("/etc/xdg"); |
| 1245 | + return CFArrayCreate(kCFAllocatorSystemDefault, (const void **)defaultPath, 1, &kCFTypeArrayCallBacks); |
| 1246 | + } |
| 1247 | + return _CFCreateCFArrayByTokenizingString(configDirectoriesPaths, ':'); |
| 1248 | +} |
| 1249 | + |
| 1250 | +/// a single base directory relative to which user-specific non-essential (cached) data should be written. This directory is defined by the environment variable $XDG_CACHE_HOME. |
| 1251 | +CF_SWIFT_EXPORT |
| 1252 | +CFStringRef _CFXDGCreateCacheDirectoryPath(void) { |
| 1253 | + //$XDG_CACHE_HOME defines the base directory relative to which user specific non-essential data files should be stored. If $XDG_CACHE_HOME is either not set or empty, a default equal to $HOME/.cache should be used. |
| 1254 | + const char *cacheHome = __CFgetenv("XDG_CACHE_HOME"); |
| 1255 | + const char *path = __CFgetenv("PATH"); |
| 1256 | + if (cacheHome && strnlen(cacheHome, CFMaxPathSize) > 1 && cacheHome[0] == '/') { |
| 1257 | + return CFStringCreateWithCString(kCFAllocatorSystemDefault, cacheHome, _kCFXDGStringEncoding); |
| 1258 | + } else { |
| 1259 | + CFStringRef home = _CFXDGCreateHome(); |
| 1260 | + CFStringRef result = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("%@/.cache"), home); |
| 1261 | + CFRelease(home); |
| 1262 | + return result; |
| 1263 | + } |
| 1264 | +} |
| 1265 | + |
| 1266 | +/// a single base directory relative to which user-specific runtime files and other file objects should be placed. This directory is defined by the environment variable $XDG_RUNTIME_DIR. |
| 1267 | +CF_SWIFT_EXPORT |
| 1268 | +CFStringRef _CFXDGCreateRuntimeDirectoryPath(void) { |
| 1269 | + const char *runtimeDir = __CFgetenv("XDG_RUNTIME_DIR"); |
| 1270 | + if (runtimeDir && strnlen(runtimeDir, CFMaxPathSize) > 1 && runtimeDir[0] == '/') { |
| 1271 | + return CFStringCreateWithCString(kCFAllocatorSystemDefault, runtimeDir, _kCFXDGStringEncoding); |
| 1272 | + } else { |
| 1273 | + // If $XDG_RUNTIME_DIR is not set applications should fall back to a replacement directory with similar capabilities and print a warning message. |
| 1274 | + return CFStringCreateWithCString(kCFAllocatorSystemDefault, "", _kCFXDGStringEncoding); |
| 1275 | + } |
| 1276 | +} |
| 1277 | + |
| 1278 | + |
| 1279 | +CF_PRIVATE CFArrayRef _CFCreateCFArrayByTokenizingString(const char *values, char delimiter) { |
| 1280 | + size_t pathCount = 0; |
| 1281 | + char* tmpDirectoriesPaths = (char*)values; |
| 1282 | + char* last_colon = 0; |
| 1283 | + // Count how many paths will be extracted. |
| 1284 | + while (*tmpDirectoriesPaths) |
| 1285 | + { |
| 1286 | + if (*tmpDirectoriesPaths == delimiter) |
| 1287 | + { |
| 1288 | + pathCount++; |
| 1289 | + last_colon = tmpDirectoriesPaths; |
| 1290 | + } |
| 1291 | + tmpDirectoriesPaths++; |
| 1292 | + } |
| 1293 | + // Add count for trailing path unless ending with colon. |
| 1294 | + pathCount += last_colon < (values + strlen(values) - 1); |
| 1295 | + if (pathCount > 0) |
| 1296 | + { |
| 1297 | + size_t validPathCount = 0; |
| 1298 | + CFStringRef pathList[pathCount]; |
| 1299 | + char* copyDirPath = strdup(values); |
| 1300 | + char delimiterStr[2]; |
| 1301 | + delimiterStr[0] = delimiter; |
| 1302 | + delimiterStr[1] = '\0'; |
| 1303 | + char* path = strtok(copyDirPath, delimiterStr); |
| 1304 | + while (path) |
| 1305 | + { |
| 1306 | + assert(validPathCount < pathCount); |
| 1307 | + char* pathString = strdup(path); |
| 1308 | + CFStringRef dirPath = CFStringCreateWithCString(kCFAllocatorSystemDefault, pathString, _kCFXDGStringEncoding); |
| 1309 | + CFStringRef slash = CFSTR("/"); |
| 1310 | + CFStringRef tilde = CFSTR("~"); |
| 1311 | + // Check for absolutePath, if not ignore. |
| 1312 | + if (CFStringHasPrefix(dirPath, slash) || CFStringHasPrefix(dirPath, tilde) ) { |
| 1313 | + pathList[validPathCount++] = dirPath; |
| 1314 | + } |
| 1315 | + path = strtok(NULL, delimiterStr); |
| 1316 | + free(pathString); |
| 1317 | + } |
| 1318 | + free(copyDirPath); |
| 1319 | + CFArrayRef pathArray = CFArrayCreate(kCFAllocatorSystemDefault, (const void **)pathList , validPathCount, &kCFTypeArrayCallBacks); |
| 1320 | + for(int i = 0; i < pathCount; i ++) { |
| 1321 | + CFRelease(pathList[i]); |
| 1322 | + } |
| 1323 | + return pathArray; |
| 1324 | + } |
| 1325 | + return CFArrayCreate(kCFAllocatorSystemDefault, NULL, 0, &kCFTypeArrayCallBacks); |
| 1326 | +} |
| 1327 | + |
| 1328 | +#endif |
0 commit comments