Sign up ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free.

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];
}
share|improve this question
    
Can't you just use NSBundle? – Ramy Al Zuhouri Jun 30 '13 at 22:56
    
This is for a command-line program which works on files on the Mac filesystem. I would not touch NSBundle in this scenario. – Brennan Jun 30 '13 at 23:03
    
After seeing some of the initial responses I should note that in this Xcode project I have set up arguments for running the command-line program as well as setting the working directory to the location of the project. That sets the Current Working Directory where I have files managed with source control so I can reliably run this program with a test scenario. For now I seem to have what I need, though I don't understand the resolved value from the realpath function. – Brennan Jun 30 '13 at 23:28
    
When you are running your code inside a real shell instead of with Xcode's debugger, you will always be given absolute paths in argv, even if the user typed a relative path. I suggest you update your debug arguments to be full paths (/Users/brennan/file.txt, etc). – Abhi Beckert Jun 30 '13 at 23:32

3 Answers 3

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.

share|improve this answer
    
See my comment above. I am using realpath function from stdlib and the CWD is set from the scheme in Xcode which runs the program for testing purposes. I am able to get the expected path this way, but not with any methods I found on the Objective-C level with NSURL or NSString. Maybe NSFileManager has something useful. – Brennan Jun 30 '13 at 23:29
up vote 3 down vote accepted

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];
}
share|improve this answer
    
The call to stringByExpandingTildeInPath is unnecessary. That's the first thing that stringByStandardizingPath does. – Bob Whiteman Mar 25 '14 at 0:18

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 those 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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.