0

In swift 2 I had an NSURL extension for working with extended attributes and it included this:

func setAttribute(name: String, value: String) {
    let data = value.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
    setxattr(self.path!, name, data.bytes, data.length, 0, 0)
}

func getAttribute(name: String) -> String? {
    let length = getxattr(self.path!, name, nil, 0, 0, 0)
    if length == -1 {
        return nil
    }
    let bytes = malloc(length)
    if getxattr(self.path!, name, bytes, length, 0, 0) == -1 {
        return nil
    }
    return (String(data: NSData(bytes: bytes, length: length),
        encoding: NSUTF8StringEncoding))
}

func attributes() -> [String : String]? {
    let length = listxattr(self.path!, nil, 0, 0)
    if length == -1 {
        return nil
    }
    let bytes = UnsafeMutablePointer<Int8>(malloc(length))
    if listxattr(self.path!, bytes, length, 0) == -1 {
        return nil
    }
    if var names = NSString(bytes: bytes, length: length,
        encoding: NSUTF8StringEncoding)?.componentsSeparatedByString("\0") {
            names.removeLast()
            var attributes: [String : String] = [:]
            for name in names {
                attributes[name] = getAttribute(name)!
            }
            return attributes
    }
    return nil
}

Now I'm trying to convert it to Swift 3, but Xcode keeps complaining.

I changed the first two functions into this, but I'm not sure I'm doing it right:

func setAttribute(_ name: String, value: String) {
    let data = value.data(using: String.Encoding.utf8, allowLossyConversion: false)!
    setxattr(self.path, name, (data as NSData).bytes, data.count, 0, 0)
}

func getAttribute(_ name: String) -> String? {
    let length = getxattr(self.path, name, nil, 0, 0, 0)
    if length < 0 {
        return nil
    }
    guard let bytes = malloc(length),
        getxattr(self.path, name, bytes, length, 0, 0) >= 0 else {
            return nil
    }
    if getxattr(self.path, name, bytes, length, 0, 0) < 0 {
        return nil
    }
    return (String(data: Data(bytes: bytes, count: length),
        encoding: String.Encoding.utf8))
}

Do I have to cast Data to NSData there at the first function? Is there a better way to go about all this?

And that last function was converted by Xcode into this:

func attributes() -> [String : String]? {
    let length = listxattr(self.path, nil, 0, 0)
    if length == -1 {
        return nil
    }
    let bytes = UnsafeMutablePointer<Int8>(malloc(length))
    if listxattr(self.path, bytes, length, 0) == -1 {
        return nil
    }
    if var names = NSString(bytes: bytes, length: length,
        encoding: String.Encoding.utf8)?.components(separatedBy: "\0") {
            names.removeLast()
            var attributes: [String : String] = [:]
            for name in names {
                attributes[name] = getAttribute(name)!
            }
            return attributes
    }
    return nil
}

But I get

Cannot invoke initializer for type 'UnsafeMutablePointer' with an argument list of type '(UnsafeMutableRawPointer!)'

And I don't know what to do. Also Xcode's conversion sticks to NSString. Couldn't I use String there?

If it's not obvious already, I have no idea what I'm doing, haha. I based my original code on this, but I couldn't understand how all that malloc, length and bytes stuff work. I tried reading the documentation for Data and UnsafeMutablePointer and so on, but it didn't help much.

If somebody could help me convert this code I'd be really grateful, but even just pointing me in the right direction to understand all this would already be a huge help. I don't know where else to look and all that stuff seems really cryptic to me. The few recent sources I found assume I know a lot of stuff I have no idea about, and I don't even know what to look for.

Thanks!

dbmrq
  • 1,451
  • 13
  • 32
  • I had made a similar extenstion at http://stackoverflow.com/questions/38343186/write-extend-file-attributes-swift-example, which is updated for Swift 3 now. – Martin R Oct 12 '16 at 06:48
  • @MartinR Awesome, thanks! Why do you return `[UInt8]`, though? And how do I get a string from that? Also it would be nice to be able to get all attributes with `listxattr` like in my last function. That was a huge help already, though. Thanks. :) – dbmrq Oct 12 '16 at 06:54
  • The value of extended attributes can be arbitrary data, not only UTF-8 strings. – I am working on a list method. – Martin R Oct 12 '16 at 06:56
  • @MartinR But assuming it's a string, how do I get it? If it were `Data` I could use `String(data: …)`, but `[UInt8]`? Sorry if it's a silly question. – dbmrq Oct 12 '16 at 06:59
  • See updated version ... – Martin R Oct 12 '16 at 07:11
  • 1
    @MartinR You're my hero, thanks! <3 – dbmrq Oct 12 '16 at 07:15

0 Answers0