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];
}
Relative path Relative paths make use of two special symbols, a dot (.) and a double-dot (..), which translate into the current directory and the parent directory. Double dots are used for moving up in the hierarchy. A single dot represents the current directory itself.
The absolutePath function works by beginning at the starting folder and moving up one level for each "../" in the relative path. Then it concatenates the changed starting folder with the relative path to produce the equivalent absolute path.
Relative links show the path to the file or refer to the file itself. A relative URL is useful within a site to transfer a user from point to point within the same domain. Absolute links are good when you want to send the user to a page that is outside of your server.
In simple words, an absolute path refers to the same location in a file system relative to the root directory, whereas a relative path points to a specific location in a file system relative to the current directory you are working on.
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.
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];
}
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.
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