Index: src/OFINIFileSettings.m ================================================================== --- src/OFINIFileSettings.m +++ src/OFINIFileSettings.m @@ -14,14 +14,15 @@ */ #include "config.h" #import "OFINIFileSettings.h" -#import "OFString.h" #import "OFArray.h" #import "OFINIFile.h" +#import "OFString.h" #import "OFSystemInfo.h" +#import "OFURL.h" @implementation OFINIFileSettings - (instancetype)initWithApplicationName: (OFString *)applicationName { self = [super initWithApplicationName: applicationName]; @@ -29,11 +30,12 @@ @try { void *pool = objc_autoreleasePoolPush(); OFString *fileName; fileName = [applicationName stringByAppendingString: @".ini"]; - _filePath = [[[OFSystemInfo userConfigPath] + _filePath = [[[OFSystemInfo + userConfigURL].fileSystemRepresentation stringByAppendingPathComponent: fileName] copy]; _INIFile = [[OFINIFile alloc] initWithPath: _filePath]; objc_autoreleasePoolPop(pool); } @catch (id e) { Index: src/OFSystemInfo.h ================================================================== --- src/OFSystemInfo.h +++ src/OFSystemInfo.h @@ -16,10 +16,12 @@ #import "OFObject.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN +@class OFURL; + /** * @class OFSystemInfo OFSystemInfo.h ObjFW/OFSystemInfo.h * * @brief A class for querying information about the system. */ @@ -32,14 +34,13 @@ @property (class, readonly, nonatomic) unsigned short ObjFWVersionMajor; @property (class, readonly, nonatomic) unsigned short ObjFWVersionMinor; @property (class, readonly, nullable, nonatomic) OFString *operatingSystemName; @property (class, readonly, nullable, nonatomic) OFString *operatingSystemVersion; -# ifdef OF_HAVE_FILES -@property (class, readonly, nullable, nonatomic) OFString *userDataPath; -@property (class, readonly, nullable, nonatomic) OFString *userConfigPath; -# endif +@property (class, readonly, nullable, nonatomic) OFURL *userDataURL; +@property (class, readonly, nullable, nonatomic) OFURL *userConfigURL; +@property (class, readonly, nullable, nonatomic) OFURL *temporaryDirectoryURL; @property (class, readonly, nullable, nonatomic) OFString *CPUVendor; @property (class, readonly, nullable, nonatomic) OFString *CPUModel; # if defined(OF_X86_64) || defined(OF_X86) || defined(DOXYGEN) @property (class, readonly, nonatomic) bool supportsMMX; @property (class, readonly, nonatomic) bool supportsSSE; @@ -112,11 +113,10 @@ * * @return The version of the operating system the application is running on */ + (nullable OFString *)operatingSystemVersion; -#ifdef OF_HAVE_FILES /** * @brief Returns the path where user data for the application can be stored. * * On UNIX systems, this adheres to the XDG Base Directory specification.@n * On macOS and iOS, it uses the `NSApplicationSupportDirectory` directory.@n @@ -124,11 +124,11 @@ * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory.@n * On AmigaOS and MorphOS, it returns `PROGDIR:`. * * @return The path where user data for the application can be stored */ -+ (nullable OFString *)userDataPath; ++ (nullable OFURL *)userDataURL; /** * @brief Returns the path where user configuration for the application can be * stored. * @@ -139,11 +139,11 @@ * On Haiku, it uses the `B_USER_SETTINGS_DIRECTORY` directory. * On AmigaOS and MorphOS, it returns `PROGDIR:`. * * @return The path where user configuration for the application can be stored */ -+ (nullable OFString *)userConfigPath; ++ (nullable OFURL *)userConfigURL; /** * @brief Returns a path where temporary files for can be stored. * * If possible, returns a temporary directory for the user, otherwise returns a @@ -157,12 +157,11 @@ * On Haiku, it uses the `B_SYSTEM_TEMP_DIRECTORY` directory. * On AmigaOS and MorphOS, it returns `T:`. * * @return A path where temporary files can be stored */ -+ (nullable OFString *)temporaryDirectoryPath; -#endif ++ (nullable OFURL *)temporaryDirectoryURL; /** * @brief Returns the vendor of the CPU. * * If the vendor could not be determined, `nil` is returned instead. Index: src/OFSystemInfo.m ================================================================== --- src/OFSystemInfo.m +++ src/OFSystemInfo.m @@ -52,12 +52,11 @@ #import "OFArray.h" #import "OFDictionary.h" #import "OFLocale.h" #import "OFOnce.h" #import "OFString.h" - -#import "OFNotImplementedException.h" +#import "OFURL.h" #if defined(OF_MACOS) || defined(OF_IOS) # ifdef HAVE_SYSDIR_H # include # endif @@ -241,17 +240,18 @@ encoding: [OFLocale encoding]]; #endif } #ifdef OF_NINTENDO_SWITCH -static OFString *tmpFSPath = nil; +static OFURL *tmpFSURL = nil; static void mountTmpFS(void) { if (R_SUCCEEDED(fsdevMountTemporaryStorage("tmpfs"))) - tmpFSPath = @"tmpfs:/"; + tmpFSURL = [[OFURL alloc] initFileURLWithPath: @"tmpfs:/" + isDirectory: true]; } #endif #if defined(OF_X86_64) || defined(OF_X86) static OF_INLINE struct X86Regs OF_CONST_FUNC @@ -362,13 +362,13 @@ OFOnce(&onceControl, initOperatingSystemVersion); return operatingSystemVersion; } -#ifdef OF_HAVE_FILES -+ (OFString *)userDataPath ++ (OFURL *)userDataURL { +#ifdef OF_HAVE_FILES # if defined(OF_MACOS) || defined(OF_IOS) char pathC[PATH_MAX]; OFMutableString *path; # ifdef HAVE_SYSDIR_START_SEARCH_PATH_ENUMERATION @@ -377,23 +377,19 @@ state = sysdir_start_search_path_enumeration( SYSDIR_DIRECTORY_APPLICATION_SUPPORT, SYSDIR_DOMAIN_MASK_USER); if (sysdir_get_next_search_path_enumeration(state, pathC) == 0) - @throw [OFNotImplementedException - exceptionWithSelector: _cmd - object: self]; + return nil; } else { # endif NSSearchPathEnumerationState state; state = NSStartSearchPathEnumeration( NSApplicationSupportDirectory, NSUserDomainMask); if (NSGetNextSearchPathEnumeration(state, pathC) == 0) - @throw [OFNotImplementedException - exceptionWithSelector: _cmd - object: self]; + return nil; # ifdef HAVE_SYSDIR_START_SEARCH_PATH_ENUMERATION } # endif path = [OFMutableString stringWithUTF8String: pathC]; @@ -400,67 +396,69 @@ if ([path hasPrefix: @"~"]) { OFDictionary *env = [OFApplication environment]; OFString *home; if ((home = [env objectForKey: @"HOME"]) == nil) - @throw [OFNotImplementedException - exceptionWithSelector: _cmd - object: self]; + return nil; [path deleteCharactersInRange: OFRangeMake(0, 1)]; [path prependString: home]; } [path makeImmutable]; - return path; + return [OFURL fileURLWithPath: path isDirectory: true]; # elif defined(OF_WINDOWS) OFDictionary *env = [OFApplication environment]; OFString *appData; if ((appData = [env objectForKey: @"APPDATA"]) == nil) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; + return nil; - return appData; + return [OFURL fileURLWithPath: appData isDirectory: true]; # elif defined(OF_HAIKU) char pathC[PATH_MAX]; if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, false, pathC, PATH_MAX) != B_OK) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; + return nil; - return [OFString stringWithUTF8String: pathC]; + return [OFURL fileURLWithPath: [OFString stringWithUTF8String: pathC] + isDirectory: true]; # elif defined(OF_AMIGAOS) - return @"PROGDIR:"; + return [OFURL fileURLWithPath: @"PROGDIR:" isDirectory: true]; # else OFDictionary *env = [OFApplication environment]; OFString *var; + OFURL *URL; void *pool; if ((var = [env objectForKey: @"XDG_DATA_HOME"]) != nil && var.length > 0) - return var; + return [OFURL fileURLWithPath: var isDirectory: true]; if ((var = [env objectForKey: @"HOME"]) == nil) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; + return nil; pool = objc_autoreleasePoolPush(); - var = [[OFString pathWithComponents: [OFArray arrayWithObjects: - var, @".local", @"share", nil]] retain]; + var = [OFString pathWithComponents: [OFArray arrayWithObjects: + var, @".local", @"share", nil]]; + URL = [[OFURL alloc] initFileURLWithPath: var isDirectory: true]; objc_autoreleasePoolPop(pool); - return [var autorelease]; + return [URL autorelease]; # endif +#else + return nil; +#endif } -+ (OFString *)userConfigPath ++ (OFURL *)userConfigURL { +#ifdef OF_HAVE_FILES # if defined(OF_MACOS) || defined(OF_IOS) char pathC[PATH_MAX]; OFMutableString *path; # ifdef HAVE_SYSDIR_START_SEARCH_PATH_ENUMERATION @@ -468,23 +466,19 @@ sysdir_search_path_enumeration_state state; state = sysdir_start_search_path_enumeration( SYSDIR_DIRECTORY_LIBRARY, SYSDIR_DOMAIN_MASK_USER); if (sysdir_get_next_search_path_enumeration(state, pathC) == 0) - @throw [OFNotImplementedException - exceptionWithSelector: _cmd - object: self]; + return nil; } else { # endif NSSearchPathEnumerationState state; state = NSStartSearchPathEnumeration(NSLibraryDirectory, NSUserDomainMask); if (NSGetNextSearchPathEnumeration(state, pathC) == 0) - @throw [OFNotImplementedException - exceptionWithSelector: _cmd - object: self]; + return nil; # ifdef HAVE_SYSDIR_START_SEARCH_PATH_ENUMERATION } # endif path = [OFMutableString stringWithUTF8String: pathC]; @@ -491,118 +485,134 @@ if ([path hasPrefix: @"~"]) { OFDictionary *env = [OFApplication environment]; OFString *home; if ((home = [env objectForKey: @"HOME"]) == nil) - @throw [OFNotImplementedException - exceptionWithSelector: _cmd - object: self]; + return nil; [path deleteCharactersInRange: OFRangeMake(0, 1)]; [path prependString: home]; } [path appendString: @"/Preferences"]; [path makeImmutable]; - return path; + return [OFURL fileURLWithPath: path isDirectory: true]; # elif defined(OF_WINDOWS) OFDictionary *env = [OFApplication environment]; OFString *appData; if ((appData = [env objectForKey: @"APPDATA"]) == nil) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; + return nil; - return appData; + return [OFURL fileURLWithPath: appData isDirectory: true]; # elif defined(OF_HAIKU) char pathC[PATH_MAX]; if (find_directory(B_USER_SETTINGS_DIRECTORY, 0, false, pathC, PATH_MAX) != B_OK) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; + return nil; - return [OFString stringWithUTF8String: pathC]; + return [OFURL fileURLWithPath: [OFString stringWithUTF8String: pathC] + isDirectory: true]; # elif defined(OF_AMIGAOS) return @"PROGDIR:"; # else OFDictionary *env = [OFApplication environment]; OFString *var; if ((var = [env objectForKey: @"XDG_CONFIG_HOME"]) != nil && var.length > 0) - return var; + return [OFURL fileURLWithPath: var isDirectory: true]; if ((var = [env objectForKey: @"HOME"]) == nil) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; + return nil; + + var = [var stringByAppendingPathComponent: @".config"]; - return [var stringByAppendingPathComponent: @".config"]; + return [OFURL fileURLWithPath: var isDirectory: true]; # endif +#else + return nil; +#endif } -+ (OFString *)temporaryDirectoryPath ++ (OFURL *)temporaryDirectoryURL { +#ifdef OF_HAVE_FILES # if defined(OF_MACOS) || defined(OF_IOS) char buffer[PATH_MAX]; size_t length; + OFString *path; if ((length = confstr(_CS_DARWIN_USER_TEMP_DIR, buffer, PATH_MAX)) == 0) - return @"/tmp"; + return [OFURL fileURLWithPath: @"/tmp" isDirectory: true]; - return [OFString stringWithCString: buffer + path = [OFString stringWithCString: buffer encoding: [OFLocale encoding] length: length - 1]; + + return [OFURL fileURLWithPath: path isDirectory: true]; # elif defined(OF_WINDOWS) + OFString *path; + if ([self isWindowsNT]) { wchar_t buffer[PATH_MAX]; if (!GetTempPathW(PATH_MAX, buffer)) return nil; - return [OFString stringWithUTF16String: buffer]; + path = [OFString stringWithUTF16String: buffer]; } else { char buffer[PATH_MAX]; if (!GetTempPathA(PATH_MAX, buffer)) return nil; - return [OFString stringWithCString: buffer + path = [OFString stringWithCString: buffer encoding: [OFLocale encoding]]; } + + return [OFURL fileURLWithPath: path isDirectory: true]; # elif defined(OF_HAIKU) char pathC[PATH_MAX]; if (find_directory(B_SYSTEM_TEMP_DIRECTORY, 0, false, pathC, PATH_MAX) != B_OK) - @throw [OFNotImplementedException exceptionWithSelector: _cmd - object: self]; + return nil; - return [OFString stringWithUTF8String: pathC]; + return [OFURL fileURLWithPath: [OFString stringWithUTF8String: pathC] + isDirectory: true]; # elif defined(OF_AMIGAOS) - return @"T:"; + return [OFURL fileURLWithPath: @"T:" isDirectory: true]; # elif defined(OF_MSDOS) - return [[OFApplication environment] objectForKey: @"TEMP"]; + OFString *path = [[OFApplication environment] objectForKey: @"TEMP"]; + + if (path == nil) + return nil; + + return [OFURL fileURLWithPath: path isDirectory: true]; # elif defined(OF_MINT) - return @"u:\\tmp"; + return [OFURL fileURLWithPath: @"u:\\tmp" isDirectory: true]; # elif defined(OF_NINTENDO_SWITCH) static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, mountTmpFS); - return tmpFSPath; + return tmpFSURL; # else OFString *path = [[OFApplication environment] objectForKey: @"XDG_RUNTIME_DIR"]; if (path != nil) - return path; + return [OFURL fileURLWithPath: path]; - return @"/tmp"; + return [OFURL fileURLWithPath: @"/tmp"]; # endif -} +#else + return nil; #endif +} + (OFString *)CPUVendor { #if (defined(OF_X86_64) || defined(OF_X86)) && defined(__GNUC__) struct X86Regs regs = x86CPUID(0, 0); Index: tests/OFINIFileTests.m ================================================================== --- tests/OFINIFileTests.m +++ tests/OFINIFileTests.m @@ -112,12 +112,14 @@ module = @"OFINIFile"; /* FIXME: Find a way to write files on Nintendo DS */ #ifndef OF_NINTENDO_DS - writePath = [[OFSystemInfo temporaryDirectoryPath] - stringByAppendingPathComponent: @"objfw-tests.ini"]; + writePath = [[OFSystemInfo temporaryDirectoryURL] + URLByAppendingPathComponent: @"objfw-tests.ini" + isDirectory: false] + .fileSystemRepresentation; TEST(@"-[writeToFile:encoding:]", R([file writeToFile: writePath encoding: OFStringEncodingCodepage437]) && [[OFString stringWithContentsOfFile: writePath encoding: OFStringEncodingCodepage437] Index: tests/OFSystemInfoTests.m ================================================================== --- tests/OFSystemInfoTests.m +++ tests/OFSystemInfoTests.m @@ -19,13 +19,10 @@ @implementation TestsAppDelegate (OFSystemInfoTests) - (void)systemInfoTests { void *pool = objc_autoreleasePoolPush(); -#ifdef OF_HAVE_FILES - OFString *userConfigPath, *userDataPath; -#endif [OFStdOut setForegroundColor: [OFColor lime]]; [OFStdOut writeFormat: @"[OFSystemInfo] Page size: %zd\n", [OFSystemInfo pageSize]]; @@ -47,27 +44,18 @@ [OFStdOut writeFormat: @"[OFSystemInfo] Operating system version: %@\n", [OFSystemInfo operatingSystemVersion]]; -#ifdef OF_HAVE_FILES - @try { - userConfigPath = [OFSystemInfo userConfigPath]; - } @catch (OFNotImplementedException *e) { - userConfigPath = @"Not implemented"; - } - [OFStdOut writeFormat: @"[OFSystemInfo] User config path: %@\n", - userConfigPath]; - - @try { - userDataPath = [OFSystemInfo userDataPath]; - } @catch (OFNotImplementedException *e) { - userDataPath = @"Not implemented"; - } - [OFStdOut writeFormat: @"[OFSystemInfo] User data path: %@\n", - userDataPath]; -#endif + [OFStdOut writeFormat: @"[OFSystemInfo] User config URL: %@\n", + [OFSystemInfo userConfigURL].string]; + + [OFStdOut writeFormat: @"[OFSystemInfo] User data URL: %@\n", + [OFSystemInfo userDataURL].string]; + + [OFStdOut writeFormat: @"[OFSystemInfo] Temporary directory URL: %@\n", + [OFSystemInfo temporaryDirectoryURL].string]; [OFStdOut writeFormat: @"[OFSystemInfo] CPU vendor: %@\n", [OFSystemInfo CPUVendor]]; [OFStdOut writeFormat: @"[OFSystemInfo] CPU model: %@\n", Index: tests/OFUNIXDatagramSocketTests.m ================================================================== --- tests/OFUNIXDatagramSocketTests.m +++ tests/OFUNIXDatagramSocketTests.m @@ -29,18 +29,19 @@ OFUNIXDatagramSocket *sock; OFSocketAddress address1, address2; char buffer[5]; #if defined(OF_HAVE_FILES) && !defined(OF_IOS) - path = [[OFSystemInfo temporaryDirectoryPath] - stringByAppendingPathComponent: [[OFUUID UUID] UUIDString]]; + path = [[OFSystemInfo temporaryDirectoryURL] + URLByAppendingPathComponent: [[OFUUID UUID] UUIDString]] + .fileSystemRepresentation; #else /* * We can have sockets, including UNIX sockets, while file support is * disabled. * - * We also use this code path for iOS, as the temporaryDirectoryPath is + * We also use this code path for iOS, as the temporaryDirectoryURL is * too long on the iOS simulator. */ path = [OFString stringWithFormat: @"/tmp/%@", [[OFUUID UUID] UUIDString]]; #endif Index: tests/OFUNIXStreamSocketTests.m ================================================================== --- tests/OFUNIXStreamSocketTests.m +++ tests/OFUNIXStreamSocketTests.m @@ -28,18 +28,19 @@ OFString *path; OFUNIXStreamSocket *sockClient, *sockServer, *sockAccepted; char buffer[5]; #if defined(OF_HAVE_FILES) && !defined(OF_IOS) - path = [[OFSystemInfo temporaryDirectoryPath] - stringByAppendingPathComponent: [[OFUUID UUID] UUIDString]]; + path = [[OFSystemInfo temporaryDirectoryURL] + URLByAppendingPathComponent: [[OFUUID UUID] UUIDString]] + .fileSystemRepresentation; #else /* * We can have sockets, including UNIX sockets, while file support is * disabled. * - * We also use this code path for iOS, as the temporaryDirectoryPath is + * We also use this code path for iOS, as the temporaryDirectoryURL is * too long on the iOS simulator. */ path = [OFString stringWithFormat: @"/tmp/%@", [[OFUUID UUID] UUIDString]]; #endif