Reputation: 11686
I am trying the code below to see if I can get it to expand the absolute path for those locations so that I can use them for actions with NSFileManager which fail when I use the tilde and relative paths.
I am working on a command-line app in Xcode in Objective-C. I can run the program from the command-line and it expands the path for me, but from the target in Xcode I am passing in values for the command-line arguments using $PROJECT_DIR and $HOME to get me part of the way there. The trouble is I need to get to $PROJECT_DIR/.. which is not resolving with NSFilemanager.
It does not appear that URLByResolvingSymlinksInPath or URLByStandardizingPath work as I am expecting. Is there something else that I should be doing?
BOOL isDir = TRUE;
for (NSString *path in @[@"~/", @".", @".."]) {
NSURL *url = [[[NSURL URLWithString:path] URLByResolvingSymlinksInPath] URLByStandardizingPath];
DebugLog(@"path: %@", url.absoluteString);
DebugLog(@"Exists: %@", [[NSFileManager defaultManager] fileExistsAtPath:url.path isDirectory:&isDir] ? @"YES" : @"NO");
}
Update: I am using realpath from stdlib to resolve the path and created the following method though I do not understand this C function. Specifically I do not know waht the resolved value is or how I would use it. I do see to get the expected return value.
- (NSString *)resolvePath:(NSString *)path {
NSString *expandedPath = [[path stringByExpandingTildeInPath] stringByStandardizingPath];
const char *cpath = [expandedPath cStringUsingEncoding:NSUTF8StringEncoding];
char *resolved = NULL;
char *returnValue = realpath(cpath, resolved);
// DebugLog(@"resolved: %s", resolved);
// DebugLog(@"returnValue: %s", returnValue);
return [NSString stringWithCString:returnValue encoding:NSUTF8StringEncoding];
}
Upvotes: 5
Views: 10429
Reputation: 53000
Your example paths fall into two groups: tilde paths and "dot" paths.
For tilde paths you must expand them in your code, the tilde is not recognised by the file system but is a shorthand introduced by command line interpreters (aka "shells", CLI). To do the expansion you can use methods such as stringByExpandingTildeInPath
.
The "dot" paths are different, the "." and ".." directory entries exist as part of the file system and paths containing them work.
However, a path starting with "." or ".." is taken as relative to the current working directory (CWD). While the CWD is obvious for a CLI it is less obvious what it might be set to for a GUI application - which means though such paths work they probably don't reference what you expect they do after a GUI app is launched. However you can set the CWD, see changeCurrentDirectoryPath:
, after which paths starting with "." or ".." should reference what you expect.
Upvotes: 2
Reputation: 11686
Below is my solution which is working well which uses a lower level C function which I was trying to avoid. It is working for my purposes. The full project which uses is available on GitHub with the method I created for Objective-C below.
https://github.com/brennanMKE/Xcode4CodeSnippets/tree/master/SnippetImporter
- (NSString *)resolvePath:(NSString *)path {
NSString *expandedPath = [[path stringByExpandingTildeInPath] stringByStandardizingPath];
const char *cpath = [expandedPath cStringUsingEncoding:NSUTF8StringEncoding];
char *resolved = NULL;
char *returnValue = realpath(cpath, resolved);
if (returnValue == NULL && resolved != NULL) {
printf("Error with path: %s\n", resolved);
// if there is an error then resolved is set with the path which caused the issue
// returning nil will prevent further action on this path
return nil;
}
return [NSString stringWithCString:returnValue encoding:NSUTF8StringEncoding];
}
Upvotes: 2
Reputation: 385690
Of the paths in your example, only ~/
is an absolute path, so only ~/
could possibly be turned into an absolute URL.
But alas, NSURL
doesn't resolve the tilde (~
) at all. You have to use -[NSString stringByStandardizingPath]
or -[NSString stringByExpandingTildeInPath]
to expand the tilde.
It is impossible to convert .
to an absolute URL without specifying what URL it is relative to. NSURL
will not just assume you want to use the process's current directory. You have to be explicit.
It is impossible to resolve ..
for the same reason.
You didn't give this in your question, but in somename/..
, it is impossible to resolve the ..
because somename
might end up being a symbolic link, and ..
after following the symbolic link may take you to a different directory than the one containing somename
.
Unfortunately, the NSURL
documentation does not mention these limitations. The NSString documentation does.
You can get the current directory with -[NSFileManager currentDirectoryPath]
, pass that to +[NSURL fileURLWithPath:]
, and pass the result (an absolute file
URL for the current directory) to +[NSURL URLWithString:relativeToURL:]
to resolve .
, ..
, and symbolic links.
Upvotes: 11