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

本秂侑毒 提交于 2019-12-10 21:14:02

问题


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


回答1:


There is no (easy) alternative at the moment.

The function CFURLCopyFileSystemPath is not deprecated, only the enum case kCFURLHFSPathStyle is deprecated but the raw value 1 is still working and avoids the warning.

I'm using this category of NSString

@implementation NSString (POSIX_HFS)

- (NSString *)hfsPathFromPOSIXPath
{
    CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)[NSURL fileURLWithPath:self], 1);
    return (NSString *)CFBridgingRelease(hfsPath);
}
@end

The function works also in Swift. The Swift version is a bit more sophisticated and adds the trailing semicolon representing a dictionary implicitly, here as an extension of URL:

extension URL {

  func hfsPath() -> String?
  {
    if let cfpathHFS = CFURLCopyFileSystemPath(self as CFURL, CFURLPathStyle(rawValue: 1)!) { // CFURLPathStyle.CFURLHFSPathStyle)
      let pathHFS = cfpathHFS as String
      do {
        let info = try self.resourceValues(forKeys: [.isDirectoryKey, .isPackageKey])
        let isDirectory = info.isDirectory!
        let isPackage =  info.isPackage!

        if isDirectory && !isPackage {
          return pathHFS + ":" // directory, not package
        }
      } catch _ {}
      return pathHFS
    }
    return nil
  }
}



回答2:


Vadians answer is better than this one - but if vadians method is deprecated, this will be an alternative. Idea is to use applescripts methods to get HFS path called easily with an osascript from an NSString category.

NSString category (credits: https://stackoverflow.com/a/19014463/4591992)

@implementation NSString (ShellExecution)

- (NSString*)runAsCommand {
    NSPipe* pipe = [NSPipe pipe];

    NSTask* task = [[NSTask alloc] init];
    [task setLaunchPath: @"/bin/sh"];
    [task setArguments:@[@"-c", [NSString stringWithFormat:@"%@", self]]];
    [task setStandardOutput:pipe];

    NSFileHandle* file = [pipe fileHandleForReading];
    [task launch];

    NSString* result = [[NSString alloc] initWithData:[file readDataToEndOfFile] encoding:NSUTF8StringEncoding];
    return result;
}

@end

Usage for this case:

NSString* posixToHFS = [NSString stringWithFormat:@"osascript -e 'POSIX file \"%@\" as text'",filePath];
filePath = [posixToHFS runAsCommand];



回答3:


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



来源:https://stackoverflow.com/questions/45081556/cocoa-objective-c-get-a-hfs-path-pathtodesktop-from-a-posix-path-path-to-de

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!