Q: How do I expand a tilde-based path to its full path value?A: Some situations may arise where you would like to expand a tilde-based relevant path into a resolved, absolute path. For example "~/" might resolve to "/Users/johndoe". Here are examples on how to accomplish this task under each development environment (Objective-C, Core Foundation, and POSIX). Note: Only use this code for resolving paths that have been generated at runtime or specified by user input. You should never store paths. For persistent storage of file/directory locations you should use aliases. Listing 1: Objective-C (result is autoreleased)
NSString *result = [@"~/Desktop" stringByExpandingTildeInPath];
Listing 2: Core Foundation (caller responsible for releasing result)
static CFURLRef CreateURLByExpandingTildePath(
CFStringRef path,
Boolean isDir
)
{
CFURLRef result = NULL, baseURL = NULL;
CFStringRef urlPath = NULL;
Boolean relative;
assert(path != NULL);
if ( ! CFStringHasPrefix(path, CFSTR("~")) )
{
// No leading "~", so use path as the entire path.
urlPath = CFStringCreateCopy(kCFAllocatorDefault, path);
relative = false;
} else {
CFIndex pathLen;
Boolean foundSlash;
CFRange foundRange = {kCFNotFound,0};
CFIndex userNameLengthUTF16;
relative = true;
pathLen = CFStringGetLength(path);
foundSlash = CFStringFindWithOptions(
path,
CFSTR("/"),
CFRangeMake(1, pathLen-1),
0,
&foundRange
);
if (foundSlash) {
userNameLengthUTF16 = foundRange.location - 1;
urlPath = CFStringCreateWithSubstring(
NULL,
path,
CFRangeMake(
foundRange.location + 1, // +1 to exclude the slash
pathLen - (foundRange.location + 1)
)
);
} else {
userNameLengthUTF16 = pathLen - 1;
urlPath = CFStringCreateWithCharacters(NULL, NULL, 0);
}
if (urlPath != NULL) {
char userNameUTF8[
CFStringGetMaximumSizeForEncoding(
userNameLengthUTF16,
kCFStringEncodingUTF8) + 1 // +1 for terminating null
];
CFIndex convertedCountUTF16;
CFIndex userNameLengthUTF8;
struct passwd * pw;
convertedCountUTF16 = CFStringGetBytes(
path,
CFRangeMake(1, userNameLengthUTF16),
kCFStringEncodingUTF8,
0,
false,
(UInt8 *) userNameUTF8,
sizeof(userNameUTF8),
&userNameLengthUTF8
);
assert(convertedCountUTF16 == userNameLengthUTF16);
assert(userNameLengthUTF8 < sizeof(userNameUTF8));
userNameUTF8[userNameLengthUTF8] = 0;
if (userNameLengthUTF8 == 0) {
pw = getpwuid( getuid() );
} else {
pw = getpwnam(userNameUTF8);
}
if (pw != NULL) {
baseURL = CFURLCreateFromFileSystemRepresentation(
NULL,
(const UInt8 *) pw->pw_dir,
strlen(pw->pw_dir),
true
);
}
}
}
if (urlPath != NULL) {
if ( ! relative ) {
result = CFURLCreateWithFileSystemPath(
NULL,
urlPath,
kCFURLPOSIXPathStyle,
isDir
);
} else if ( baseURL != NULL ) {
if ( CFStringGetLength(urlPath) == 0 ) {
result = baseURL;
CFRetain(result);
} else {
result = CFURLCreateWithFileSystemPathRelativeToBase(
NULL,
urlPath,
kCFURLPOSIXPathStyle,
isDir,
baseURL
);
}
}
}
if (urlPath != NULL) {
CFRelease(urlPath);
}
if (baseURL != NULL) {
CFRelease(baseURL);
}
return result;
}
Listing 3: POSIX (caller responsible for freeing result)
char *CreatePathByExpandingTildePath(char *path)
{
Boolean success;
char *result = NULL;
char finalPath[PATH_MAX], workingPath[PATH_MAX];
assert(path != NULL);
strncpy(workingPath, path, strlen(path));
if ( workingPath[0] != '~' )
success = (realpath(workingPath, finalPath) != NULL);
else
{
char *fullPath, *firstSlash, *suffix, *homeDirStr;
struct passwd *pw;
// initialize variables
fullPath = firstSlash = suffix = homeDirStr = NULL;
firstSlash = strchr(workingPath, '/');
if (firstSlash == NULL)
suffix = "";
else
{
*firstSlash = 0; // so userName is null terminated
suffix = firstSlash + 1;
}
if (workingPath[1] == 0)
pw = getpwuid( getuid() );
else
pw = getpwnam( &workingPath[1] );
if (pw != NULL)
homeDirStr = pw->pw_dir;
if (homeDirStr != NULL)
asprintf(&fullPath, "%s/%s", homeDirStr, suffix);
if (fullPath != NULL)
success = (realpath(fullPath, finalPath) != NULL);
else
success = false;
free(fullPath);
}
if (success)
{
result = (char*)calloc(1, strlen(finalPath));
if(result)
{
strncpy(result, finalPath, strlen(finalPath));
}
}
return result;
}
Document Revision History| Date | Notes |
|---|
| 2007-09-24 | First Version |
Posted: 2007-09-24
|