Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would I resolve a relative path in Objective-C?

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];
}
like image 464
Brennan Avatar asked Jun 30 '13 22:06

Brennan


People also ask

How do you do relative paths?

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.

How do you convert an absolute path to a relative path?

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.

Why is it better to use relative paths instead of absolute paths?

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.

When would you use a relative path vs an absolute path?

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.


3 Answers

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.

like image 131
rob mayoff Avatar answered Sep 22 '22 20:09

rob mayoff


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];
}
like image 36
Brennan Avatar answered Sep 23 '22 20:09

Brennan


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.

like image 44
CRD Avatar answered Sep 22 '22 20:09

CRD