diff --git a/build.xml b/build.xml
index 4d7d6ccb..76a0ecce 100644
--- a/build.xml
+++ b/build.xml
@@ -157,7 +157,7 @@
-
+
@@ -181,10 +181,6 @@
-
-
-
-
@@ -224,42 +220,42 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
diff --git a/lib/native/mac-x86_64/libxattrj.dylib b/lib/native/mac-x86_64/libxattrj.dylib
deleted file mode 100644
index 51687e40..00000000
Binary files a/lib/native/mac-x86_64/libxattrj.dylib and /dev/null differ
diff --git a/lib/xattrj.jar b/lib/xattrj.jar
deleted file mode 100644
index 12ed6d4a..00000000
Binary files a/lib/xattrj.jar and /dev/null differ
diff --git a/source/net/filebot/MetaAttributeView.java b/source/net/filebot/MetaAttributeView.java
index 9d69f64d..26c0a741 100644
--- a/source/net/filebot/MetaAttributeView.java
+++ b/source/net/filebot/MetaAttributeView.java
@@ -1,6 +1,7 @@
package net.filebot;
-import static java.nio.file.Files.*;
+import static java.nio.file.Files.isSymbolicLink;
+import static java.nio.file.Files.readSymbolicLink;
import java.io.File;
import java.io.IOException;
@@ -12,31 +13,20 @@ import java.nio.file.attribute.UserDefinedFileAttributeView;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.AbstractMap;
-import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
-import org.securityvision.xattrj.Xattrj;
+import net.filebot.mac.xattr.XattrUtil;
import com.sun.jna.Platform;
public class MetaAttributeView extends AbstractMap {
- // UserDefinedFileAttributeView (for Windows and Linux) OR 3rd party Xattrj (for Mac) because UserDefinedFileAttributeView is not supported (Oracle Java 7/8)
+ // UserDefinedFileAttributeView (for Windows and Linux) OR our own xattr.h JNA wrapper via MacXattrView (for Mac) because UserDefinedFileAttributeView is not supported (Oracle Java 7/8)
private Object xattr;
- private File file;
private Charset encoding;
- private static Xattrj MacXattrSupport;
-
- private static synchronized Xattrj getMacXattrSupport() throws Throwable {
- if (MacXattrSupport == null) {
- MacXattrSupport = new Xattrj();
- }
- return MacXattrSupport;
- }
-
public MetaAttributeView(File file) throws IOException {
Path path = file.getCanonicalFile().toPath();
while (isSymbolicLink(path)) {
@@ -49,25 +39,14 @@ public class MetaAttributeView extends AbstractMap {
xattr = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
- // try 3rd party Xattrj library
if (xattr == null) {
if (Platform.isMac()) {
- // Xattrj for Mac
- try {
- xattr = getMacXattrSupport();
- } catch (Throwable e) {
- throw new IOException("Unable to load library: xattrj", e);
- }
-
- // MacOS filesystem may require NFD unicode decomposition
- this.file = new File(Normalizer.normalize(path.toFile().getAbsolutePath(), Form.NFD));
- this.encoding = Charset.forName("UTF-8");
+ xattr = new MacXattrView(path);
} else {
throw new IOException("UserDefinedFileAttributeView is not supported");
}
} else {
// UserDefinedFileAttributeView
- this.file = path.toFile();
this.encoding = Charset.forName("UTF-8");
}
}
@@ -84,9 +63,9 @@ public class MetaAttributeView extends AbstractMap {
return encoding.decode(buffer).toString();
}
- if (xattr instanceof Xattrj) {
- Xattrj macXattr = (Xattrj) xattr;
- return macXattr.readAttribute(file, key.toString());
+ if (xattr instanceof MacXattrView) {
+ MacXattrView macXattr = (MacXattrView) xattr;
+ return macXattr.read(key.toString());
}
} catch (Exception e) {
// ignore
@@ -107,12 +86,12 @@ public class MetaAttributeView extends AbstractMap {
}
}
- if (xattr instanceof Xattrj) {
- Xattrj macXattr = (Xattrj) xattr;
+ if (xattr instanceof MacXattrView) {
+ MacXattrView macXattr = (MacXattrView) xattr;
if (value == null || value.isEmpty()) {
- macXattr.removeAttribute(file, key);
+ macXattr.delete(key);
} else {
- macXattr.writeAttribute(file, key, value);
+ macXattr.write(key, value);
}
}
} catch (Exception e) {
@@ -128,9 +107,9 @@ public class MetaAttributeView extends AbstractMap {
return attributeView.list();
}
- if (xattr instanceof Xattrj) {
- Xattrj macXattr = (Xattrj) xattr;
- return Arrays.asList(macXattr.listAttributes(file));
+ if (xattr instanceof MacXattrView) {
+ MacXattrView macXattr = (MacXattrView) xattr;
+ return macXattr.list();
}
return null;
@@ -189,4 +168,30 @@ public class MetaAttributeView extends AbstractMap {
}
}
+ private static class MacXattrView {
+
+ private final String path;
+
+ public MacXattrView(Path path) {
+ // MacOS filesystem may require NFD unicode decomposition
+ this.path = Normalizer.normalize(path.toFile().getAbsolutePath(), Form.NFD);
+ }
+
+ public List list() {
+ return XattrUtil.listXAttr(path);
+ }
+
+ public String read(String key) {
+ return XattrUtil.getXAttr(path, key);
+ }
+
+ public void write(String key, String value) {
+ XattrUtil.setXAttr(path, key, value);
+ }
+
+ public void delete(String key) {
+ XattrUtil.removeXAttr(path, key);
+ }
+ }
+
}
diff --git a/source/net/filebot/mac/xattr/XAttr.java b/source/net/filebot/mac/xattr/XAttr.java
new file mode 100644
index 00000000..73dead39
--- /dev/null
+++ b/source/net/filebot/mac/xattr/XAttr.java
@@ -0,0 +1,51 @@
+/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+package net.filebot.mac.xattr;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Pointer;
+
+/**
+ * JNA wrapper for
+ *
+ */
+interface XAttr extends Library {
+
+ // load from current image
+ XAttr INSTANCE = (XAttr) Native.loadLibrary(null, XAttr.class);
+
+ // see /usr/include/sys/xattr.h
+ int XATTR_NOFOLLOW = 0x0001;
+ int XATTR_CREATE = 0x0002;
+ int XATTR_REPLACE = 0x0004;
+ int XATTR_NOSECURITY = 0x0008;
+ int XATTR_NODEFAULT = 0x0010;
+ int XATTR_SHOWCOMPRESSION = 0x0020;
+ int XATTR_MAXNAMELEN = 127;
+ String XATTR_FINDERINFO_NAME = "com.apple.FinderInfo";
+ String XATTR_RESOURCEFORK_NAME = "com.apple.ResourceFork";
+
+ // see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/getxattr.2.html
+ long getxattr(String path, String name, Pointer value, long size, int position, int options);
+
+ // see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/setxattr.2.html
+ int setxattr(String path, String name, Pointer value, long size, int position, int options);
+
+ // see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/removexattr.2.html
+ int removexattr(String path, String name, int options);
+
+ // see https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/listxattr.2.html
+ long listxattr(String path, Pointer namebuff, long size, int options);
+
+}
diff --git a/source/net/filebot/mac/xattr/XattrUtil.java b/source/net/filebot/mac/xattr/XattrUtil.java
new file mode 100644
index 00000000..cbfa8f00
--- /dev/null
+++ b/source/net/filebot/mac/xattr/XattrUtil.java
@@ -0,0 +1,100 @@
+/* Copyright (c) 2014 Reinhard Pointner, All Rights Reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+package net.filebot.mac.xattr;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.sun.jna.Memory;
+import com.sun.jna.Pointer;
+
+public class XattrUtil {
+
+ public static List listXAttr(String path) {
+ // get required buffer size
+ long bufferLength = XAttr.INSTANCE.listxattr(path, Pointer.NULL, 0, 0);
+
+ if (bufferLength < 0)
+ return null;
+
+ if (bufferLength == 0)
+ return new ArrayList(0);
+
+ Memory valueBuffer = new Memory(bufferLength);
+ long valueLength = XAttr.INSTANCE.listxattr(path, valueBuffer, bufferLength, 0);
+
+ if (valueLength < 0)
+ return null;
+
+ return decodeStringSequence(valueBuffer.getByteBuffer(0, valueLength));
+ }
+
+ public static String getXAttr(String path, String name) {
+ // get required buffer size
+ long bufferLength = XAttr.INSTANCE.getxattr(path, name, Pointer.NULL, 0, 0, 0);
+
+ if (bufferLength < 0)
+ return null;
+
+ Memory valueBuffer = new Memory(bufferLength);
+ long valueLength = XAttr.INSTANCE.getxattr(path, name, valueBuffer, bufferLength, 0, 0);
+
+ if (valueLength < 0)
+ return null;
+
+ return decodeString(valueBuffer.getByteBuffer(0, valueLength - 1));
+ }
+
+ public static int setXAttr(String path, String name, String value) {
+ Memory valueBuffer = encodeString(value);
+ return XAttr.INSTANCE.setxattr(path, name, valueBuffer, valueBuffer.size(), 0, 0);
+ }
+
+ public static int removeXAttr(String path, String name) {
+ return XAttr.INSTANCE.removexattr(path, name, 0);
+ }
+
+ protected static Memory encodeString(String s) {
+ // create NULL-terminated UTF-8 String
+ byte[] bb = s.getBytes(Charset.forName("UTF-8"));
+ Memory valueBuffer = new Memory(bb.length + 1);
+ valueBuffer.write(0, bb, 0, bb.length);
+ valueBuffer.setByte(valueBuffer.size() - 1, (byte) 0);
+ return valueBuffer;
+ }
+
+ protected static String decodeString(ByteBuffer bb) {
+ return Charset.forName("UTF-8").decode(bb).toString();
+ }
+
+ protected static List decodeStringSequence(ByteBuffer bb) {
+ List names = new ArrayList();
+
+ bb.mark(); // first key starts from here
+ while (bb.hasRemaining()) {
+ if (bb.get() == 0) {
+ ByteBuffer nameBuffer = (ByteBuffer) bb.duplicate().limit(bb.position() - 1).reset();
+ if (nameBuffer.hasRemaining()) {
+ names.add(decodeString(nameBuffer));
+ }
+ bb.mark(); // next key starts from here
+ }
+ }
+
+ return names;
+ }
+
+}