Cocoa/Objective-C get a HFS path (path:to:desktop) from a posix path (path/to/desktop)

Viewed 621

I am on OSX, Objective-C.

I have a path/NSURL like

/Users/xxx/Desktop/image2.png

But i pass it to a third party application that excpects finder pathes like

Harddisk:Users:Desktop:image2.png

Is there any method (i can't find) to convert pathes like that or get them out of an NSURL (if possible without string modifying)?

In AppleScript it is

return POSIX file "/Users/xxx/Desktop/image2.png" -->  Harddisk:Users:xxx:Desktop:image2.png

EDIT: This is pretty much the same: Cocoa path string conversion Unfortunately, the method is deprecated...

3 Answers

In my own testing (on 10.13.6, 10.14.6 and 10.15b7), @vadian's solution doesn't work with paths where a folder name component contains a "/" (when viewed in Finder), which then appears as a ":" in a POSIX path and as a "/" in a HFS path.

Demonstration of the bug

Here's a quick test program that you can build by creating a new "command line" project in Xcode:

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSString *posixPath = @"/EndsInSlash:";
        NSURL *url = [NSURL fileURLWithPath:posixPath];
        if (url == nil) {
            NSLog(@"Oops, this went wrong");
        } else {
            CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)url, 1);
            NSString *res = (NSString *)CFBridgingRelease (hfsPath);
            NSLog(@"HFS path: <%@>", res);
        }
    }
    return 0;
}

When you run it, you'll probably see a correct result printed, i.e. a path that ends in "/". However, that only works if the folder does not exist. So, create a folder named "EndsInSlash/" (not a file!) in your root folder and run the app again - now the resulting path does not end in "/" any more as it should.

Work-around

Below is a "smart" function that uses the faster CFURLCopyFileSystemPath() function whenever possible, i.e. unless a ":" appears in the POSIX path - in which case it performs the conversion on its own, by splitting up the POSIX path into its components, converting them individually (replacing ":" into "/"), prepending the volume name and then merging the components again. This appears to work fine even on macOS 10.15 (Catalina), despite the deprecation warnings.

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"

static NSString* stringWithHFSUniStr255(const HFSUniStr255* hfsString)
{
    CFStringRef stringRef = FSCreateStringFromHFSUniStr(nil, hfsString);
    NSString* result = CFBridgingRelease(stringRef);
    return result;
}

NSString* hfsPathFromPOSIXPath (NSString *posixPath)
{
    if (posixPath == nil) return @"";
    if ([posixPath containsString:@":"]) {
        // slow version, but can handle ":" appearing in path components
        NSString *result = nil;
        FSRef ref;
        Boolean isDir;
        if (FSPathMakeRef ((const UInt8*)posixPath.UTF8String, &ref, &isDir) == noErr) {
            HFSUniStr255 elemName;
            FSCatalogInfo catInfo;
            NSMutableArray<NSString*> *elems = [NSMutableArray arrayWithCapacity:16];
            while (FSGetCatalogInfo (&ref, kFSCatInfoNodeID, &catInfo, &elemName, nil, &ref) == noErr) {
                [elems insertObject: stringWithHFSUniStr255(&elemName) atIndex:0];
                if (catInfo.nodeID == 2) break;
            }
            result = [elems componentsJoinedByString:@":"];
        }
        return result;
    } else {
        // see https://stackoverflow.com/a/45085776/43615
        NSURL *url = [NSURL fileURLWithPath:posixPath];
        if (url == nil) {
            // could not convert because the path doesn't exist
            return nil;
        }
        CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)url, kCFURLHFSPathStyle);
        return (NSString *)CFBridgingRelease (hfsPath);
    }
}

#pragma clang diagnostic pop

See also

Discussion of a related bug with AppleScript, with a work-around: https://forum.latenightsw.com/t/xxx/2097

Bug report filed with Apple: http://www.openradar.me/radar?id=4994410022436864

Related