What is the modern, standard, canonical method for accessing standard directories on OS X from C++, such as ~/Library/Application Support
or ~/Library/Preferences
?
I've seen mention of the use CoreServices, but it's also mentioned that it's deprecated and I'm having trouble finding the documentation that will allow me to do more than just paste the code in.
I've found mention of using Objective C++, but most information about that option revolves around calling C++ code from Objective C, and again, Apple's documentation on it seems rather sparse, or at least I've not had success finding it.
On Mac and iOS the C Standard Library implementation is part of libSystem, a core library located in /usr/lib/libSystem. dylib . LibSystem includes other components such as the math library, the thread library and other low-level utilities.
If you type cd .. (that's two periods), you'll go to the directory above the one you're currently in. So if you're in your home folder, and type cd .. , you'll go to your Mac's /Users folder. And if you type cd - (hyphen) you'll go back to the directory you were in before the last time you issued the cd command.
PATH is an essential environment variable that decides how programs and commands work on macOS. Setting the PATH variable for a program or script allows you to run it from anywhere on the file system without specifying its absolute path.
Well, there is the little-known NSSystemDirectories.h
(at /usr/include/NSSystemDirectories.h
) header that has existed on every version of OS X I checked all the way back to OS X 10.3. It's part of libc: http://opensource.apple.com//source/Libc/Libc-825.40.1/include/NSSystemDirectories.h
That provides a C API, though it's not too difficult to create an Objective-C++ wrapper: create a pure C++ interface and in the implementation, use Objective-C.
FolderManager.h:
#include <stdio.h>
namespace fm {
enum {
NSApplicationDirectory = 1,
NSDemoApplicationDirectory,
NSDeveloperApplicationDirectory,
NSAdminApplicationDirectory,
NSLibraryDirectory,
NSDeveloperDirectory,
NSUserDirectory,
NSDocumentationDirectory,
NSDocumentDirectory,
NSCoreServiceDirectory,
NSAutosavedInformationDirectory = 11,
NSDesktopDirectory = 12,
NSCachesDirectory = 13,
NSApplicationSupportDirectory = 14,
NSDownloadsDirectory = 15,
NSInputMethodsDirectory = 16,
NSMoviesDirectory = 17,
NSMusicDirectory = 18,
NSPicturesDirectory = 19,
NSPrinterDescriptionDirectory = 20,
NSSharedPublicDirectory = 21,
NSPreferencePanesDirectory = 22,
NSApplicationScriptsDirectory = 23,
NSItemReplacementDirectory = 99,
NSAllApplicationsDirectory = 100,
NSAllLibrariesDirectory = 101,
NSTrashDirectory = 102
};
typedef unsigned long SearchPathDirectory;
enum {
NSUserDomainMask = 1, // user's home directory --- place to install user's personal items (~)
NSLocalDomainMask = 2, // local to the current machine --- place to install items available to everyone on this machine (/Library)
NSNetworkDomainMask = 4, // publically available location in the local area network --- place to install items available on the network (/Network)
NSSystemDomainMask = 8, // provided by Apple, unmodifiable (/System)
NSAllDomainsMask = 0x0ffff // all domains: all of the above and future items
};
typedef unsigned long SearchPathDomainMask;
class FolderManager {
public:
FolderManager();
~FolderManager();
const char *pathForDirectory(SearchPathDirectory directory, SearchPathDomainMask domainMask);
const char *pathForDirectoryAppropriateForItemAtPath(SearchPathDirectory directory, SearchPathDomainMask domainMask, const char *itemPath, bool create = false);
private:
void *m_autoreleasePool;
};
};
FolderManager.mm (note the filename extension)
#include "FolderManager.h"
#import <Foundation/Foundation.h>
using namespace fm;
FolderManager::FolderManager() {
m_autoreleasePool = [[NSAutoreleasePool alloc] init];
}
FolderManager::~FolderManager() {
[(NSAutoreleasePool *)m_autoreleasePool release];
}
const char * FolderManager::pathForDirectory(SearchPathDirectory directory, SearchPathDomainMask domainMask) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *URLs = [fileManager URLsForDirectory:(NSSearchPathDirectory)directory inDomains:domainMask];
if (URLs.count == 0) return NULL;
NSURL *URL = [URLs objectAtIndex:0];
NSString *path = URL.path;
// `fileSystemRepresentation` on an `NSString` gives a path suitable for POSIX APIs
return path.fileSystemRepresentation;
}
const char * FolderManager::pathForDirectoryAppropriateForItemAtPath(SearchPathDirectory directory,
SearchPathDomainMask domainMask, const char *itemPath, bool create) {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *nsPath = [fileManager stringWithFileSystemRepresentation:itemPath length:strlen(itemPath)];
NSURL *itemURL = (nsPath ? [NSURL fileURLWithPath:nsPath] : nil);
NSURL *URL = [fileManager URLForDirectory:(NSSearchPathDirectory)directory
inDomain:domainMask
appropriateForURL:itemURL
create:create error:NULL];
return URL.path.fileSystemRepresentation;
}
Note that this should be compiled without Automatic Reference Counting enabled (or at least, that's how I was using it; it's possible that it will also work with ARC, but I haven't tested that). It's important to create and release an NSAutoreleasePool
in the constructor and destructor, respectively, otherwise, you may get memory leaks and warnings in the Console to that effect (unless an NSAutoreleasePool
has been created elsewhere for you)**.
To use it, something like this:
int main(int argc, const char * argv[]) {
char *folderPath = NULL;
FolderManager folderManager;
folderPath = (char *)folderManager.pathForDirectory(NSApplicationSupportDirectory, NSUserDomainMask);
printf("folderPath == %s\n", folderPath);
folderPath = (char *)folderManager.pathForDirectory(NSApplicationSupportDirectory, NSLocalDomainMask);
printf("folderPath == %s\n", folderPath);
folderPath = (char *)folderManager.pathForDirectory(NSTrashDirectory, NSAllDomainsMask);
printf("folderPath == %s\n", folderPath);
if (argc > 1) {
folderPath = (char *)folderManager.pathForDirectoryAppropriateForItemAtPath(NSTrashDirectory, NSAllDomainsMask, argv[1]);
printf("folderPath == %s\n", folderPath);
folderPath = (char *)folderManager.pathForDirectoryAppropriateForItemAtPath(NSItemReplacementDirectory, NSUserDomainMask, argv[1], true);
printf("folderPath == %s\n", folderPath);
}
return 0;
}
When I run this on my machine, passing it an argument of /Volumes/Untitled3/folder/testFile.chm
, it will output the following:
folderPath == /Users/mdouma46/Library/Application Support
folderPath == /Library/Application Support
folderPath == /Users/mdouma46/.Trash
folderPath == /Volumes/Untitled 3/.Trashes/501
folderPath == /Volumes/Untitled 3/.TemporaryItems/folders.501/TemporaryItems/(A Document Being Saved By findFolder)
**Regarding the memory management comment about the importance of an NSAutoreleasePool
: I tested it here in OS X Yosemite, Xcode 7.2.1, by commenting out the creation of the NSAutoreleasePool
, and for the life of me, I cannot get any errors logged to console, so I'm not sure what's going on.
What I was expecting was the result I got when I compiled it in Xcode 3.2.6 in OS X 10.6:
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x10010c6d0 of class NSCFArray autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x10010c820 of class NSCFString autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111540 of class NSCFString autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111710 of class NSPathStore2 autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111770 of class NSPathStore2 autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111a20 of class NSCFArray autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111a60 of class NSCFArray autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111d10 of class NSCFString autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111ac0 of class NSURL autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111c20 of class NSCFArray autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111de0 of class NSCFString autoreleased with no pool in place - just leaking
findFolder[300] *** __NSAutoreleaseNoPool(): Object 0x100111cb0 of class NSConcreteData autoreleased with no pool in place - just leaking
folderPath == /Users/mdouma46/Library/Application Support
This happens because many of the Objective-C calls return autoreleased objects: NSFileManager
's URLsForDirectory:inDomain:
returns an autoreleased array of NSURL
s. Calling NSURL
's path
method returns (creates) autoreleased NSString
s (actually, the NSPathStore2
private subclass), etc...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With