* replace xattrj with my own JNA wrapper for <sys/xattr.h>
This commit is contained in:
parent
12d04c1681
commit
977452268e
22
build.xml
22
build.xml
|
@ -157,7 +157,7 @@
|
||||||
<include name="META-INF/services/**" />
|
<include name="META-INF/services/**" />
|
||||||
<include name="META-INF/*.properties" />
|
<include name="META-INF/*.properties" />
|
||||||
<!-- filebot already includes it's own extension modules -->
|
<!-- filebot already includes it's own extension modules -->
|
||||||
<exclude name="META-INF/services/org.codehaus.groovy.runtime.ExtensionModule"/>
|
<exclude name="META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" />
|
||||||
</zipfileset>
|
</zipfileset>
|
||||||
|
|
||||||
<zipfileset src="${dir.lib}/icu4j.jar">
|
<zipfileset src="${dir.lib}/icu4j.jar">
|
||||||
|
@ -181,10 +181,6 @@
|
||||||
<zipfileset src="${dir.lib}/sevenzipjbinding.jar">
|
<zipfileset src="${dir.lib}/sevenzipjbinding.jar">
|
||||||
<include name="net/sf/sevenzipjbinding/**" />
|
<include name="net/sf/sevenzipjbinding/**" />
|
||||||
</zipfileset>
|
</zipfileset>
|
||||||
|
|
||||||
<zipfileset src="${dir.lib}/xattrj.jar">
|
|
||||||
<include name="org/securityvision/**" />
|
|
||||||
</zipfileset>
|
|
||||||
|
|
||||||
<!-- Ivy for @Grapes automatic dependency management -->
|
<!-- Ivy for @Grapes automatic dependency management -->
|
||||||
<zipfileset src="${dir.lib}/scripting/ivy.jar">
|
<zipfileset src="${dir.lib}/scripting/ivy.jar">
|
||||||
|
@ -224,42 +220,42 @@
|
||||||
|
|
||||||
<target name="appbundle" depends="fatjar" description="Build an OSX application bundle">
|
<target name="appbundle" depends="fatjar" description="Build an OSX application bundle">
|
||||||
<taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask" classpath="${dir.lib}/build/appbundler.jar" />
|
<taskdef name="bundleapp" classname="com.oracle.appbundler.AppBundlerTask" classpath="${dir.lib}/build/appbundler.jar" />
|
||||||
|
|
||||||
<bundleapp outputdirectory="${dir.dist}" name="${title}" displayname="${title}" shortversion="${version}" identifier="net.sourceforge.FileBot" mainclassname="net.filebot.Main" icon="${dir.installer}/appbundle/filebot.icns" copyright="2014 Reinhard Pointner" applicationcategory="public.app-category.productivity">
|
<bundleapp outputdirectory="${dir.dist}" name="${title}" displayname="${title}" shortversion="${version}" identifier="net.sourceforge.FileBot" mainclassname="net.filebot.Main" icon="${dir.installer}/appbundle/filebot.icns" copyright="2014 Reinhard Pointner" applicationcategory="public.app-category.productivity">
|
||||||
<classpath file="${path.fatjar}" />
|
<classpath file="${path.fatjar}" />
|
||||||
<librarypath dir="${dir.lib}/native/mac-x86_64" />
|
<librarypath dir="${dir.lib}/native/mac-x86_64" />
|
||||||
|
|
||||||
<option value="-Dapplication.deployment=app" />
|
<option value="-Dapplication.deployment=app" />
|
||||||
<option value="-Dunixfs=false" />
|
<option value="-Dunixfs=false" />
|
||||||
<option value="-DuseExtendedFileAttributes=true" />
|
<option value="-DuseExtendedFileAttributes=true" />
|
||||||
<option value="-DuseCreationDate=false" />
|
<option value="-DuseCreationDate=false" />
|
||||||
|
|
||||||
<option value="-Djava.net.useSystemProxies=true" />
|
<option value="-Djava.net.useSystemProxies=true" />
|
||||||
<option value="-Dsun.net.client.defaultConnectTimeout=10000" />
|
<option value="-Dsun.net.client.defaultConnectTimeout=10000" />
|
||||||
<option value="-Dsun.net.client.defaultReadTimeout=60000" />
|
<option value="-Dsun.net.client.defaultReadTimeout=60000" />
|
||||||
<option value="-Dfile.encoding=UTF-8" />
|
<option value="-Dfile.encoding=UTF-8" />
|
||||||
|
|
||||||
<option value="-Djna.nosys=true" />
|
<option value="-Djna.nosys=true" />
|
||||||
<option value="-Djna.library.path=$APP_ROOT/Contents/MacOS" />
|
<option value="-Djna.library.path=$APP_ROOT/Contents/MacOS" />
|
||||||
<option value="-Djava.library.path=$APP_ROOT/Contents/MacOS" />
|
<option value="-Djava.library.path=$APP_ROOT/Contents/MacOS" />
|
||||||
<option value="-Dnet.filebot.AcoustID.fpcalc=$APP_ROOT/Contents/MacOS/fpcalc" />
|
<option value="-Dnet.filebot.AcoustID.fpcalc=$APP_ROOT/Contents/MacOS/fpcalc" />
|
||||||
|
|
||||||
<option value="-Xdock:name=${title}" />
|
<option value="-Xdock:name=${title}" />
|
||||||
<option value="-Dcom.apple.macos.use-file-dialog-packages=true" />
|
<option value="-Dcom.apple.macos.use-file-dialog-packages=true" />
|
||||||
<option value="-Dcom.apple.macos.useScreenMenuBar=true" />
|
<option value="-Dcom.apple.macos.useScreenMenuBar=true" />
|
||||||
</bundleapp>
|
</bundleapp>
|
||||||
|
|
||||||
<!-- application bundle folder as .tar.gz -->
|
<!-- application bundle folder as .tar.gz -->
|
||||||
<tar destfile="${path.appbundle.tar.gz}" compression="gzip">
|
<tar destfile="${path.appbundle.tar.gz}" compression="gzip">
|
||||||
<tarfileset dir="${dir.dist}" includes="${title}.app/**" excludes="**/MacOS/**" />
|
<tarfileset dir="${dir.dist}" includes="${title}.app/**" excludes="**/MacOS/**" />
|
||||||
<tarfileset dir="${dir.dist}" includes="${title}.app/**/**.dylib" />
|
<tarfileset dir="${dir.dist}" includes="${title}.app/**/**.dylib" />
|
||||||
|
|
||||||
<!-- IMPORTANT application stub must be executable!! -->
|
<!-- IMPORTANT application stub must be executable!! -->
|
||||||
<tarfileset dir="${dir.dist}" includes="${title}.app/**/**Launcher" filemode="755" />
|
<tarfileset dir="${dir.dist}" includes="${title}.app/**/**Launcher" filemode="755" />
|
||||||
<tarfileset dir="${dir.dist}" includes="${title}.app/**/fpcalc" filemode="755" />
|
<tarfileset dir="${dir.dist}" includes="${title}.app/**/fpcalc" filemode="755" />
|
||||||
<tarfileset prefix="${title}.app/Contents/MacOS" dir="${dir.installer}/appbundle" includes="*.sh" filemode="755" />
|
<tarfileset prefix="${title}.app/Contents/MacOS" dir="${dir.installer}/appbundle" includes="*.sh" filemode="755" />
|
||||||
</tar>
|
</tar>
|
||||||
|
|
||||||
<delete dir="${dir.dist}/${title}.app" />
|
<delete dir="${dir.dist}/${title}.app" />
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|
Binary file not shown.
BIN
lib/xattrj.jar
BIN
lib/xattrj.jar
Binary file not shown.
|
@ -1,6 +1,7 @@
|
||||||
package net.filebot;
|
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.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -12,31 +13,20 @@ import java.nio.file.attribute.UserDefinedFileAttributeView;
|
||||||
import java.text.Normalizer;
|
import java.text.Normalizer;
|
||||||
import java.text.Normalizer.Form;
|
import java.text.Normalizer.Form;
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.securityvision.xattrj.Xattrj;
|
import net.filebot.mac.xattr.XattrUtil;
|
||||||
|
|
||||||
import com.sun.jna.Platform;
|
import com.sun.jna.Platform;
|
||||||
|
|
||||||
public class MetaAttributeView extends AbstractMap<String, String> {
|
public class MetaAttributeView extends AbstractMap<String, String> {
|
||||||
|
|
||||||
// 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 Object xattr;
|
||||||
private File file;
|
|
||||||
private Charset encoding;
|
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 {
|
public MetaAttributeView(File file) throws IOException {
|
||||||
Path path = file.getCanonicalFile().toPath();
|
Path path = file.getCanonicalFile().toPath();
|
||||||
while (isSymbolicLink(path)) {
|
while (isSymbolicLink(path)) {
|
||||||
|
@ -49,25 +39,14 @@ public class MetaAttributeView extends AbstractMap<String, String> {
|
||||||
|
|
||||||
xattr = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
xattr = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
|
||||||
|
|
||||||
// try 3rd party Xattrj library
|
|
||||||
if (xattr == null) {
|
if (xattr == null) {
|
||||||
if (Platform.isMac()) {
|
if (Platform.isMac()) {
|
||||||
// Xattrj for Mac
|
xattr = new MacXattrView(path);
|
||||||
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");
|
|
||||||
} else {
|
} else {
|
||||||
throw new IOException("UserDefinedFileAttributeView is not supported");
|
throw new IOException("UserDefinedFileAttributeView is not supported");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// UserDefinedFileAttributeView
|
// UserDefinedFileAttributeView
|
||||||
this.file = path.toFile();
|
|
||||||
this.encoding = Charset.forName("UTF-8");
|
this.encoding = Charset.forName("UTF-8");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,9 +63,9 @@ public class MetaAttributeView extends AbstractMap<String, String> {
|
||||||
return encoding.decode(buffer).toString();
|
return encoding.decode(buffer).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xattr instanceof Xattrj) {
|
if (xattr instanceof MacXattrView) {
|
||||||
Xattrj macXattr = (Xattrj) xattr;
|
MacXattrView macXattr = (MacXattrView) xattr;
|
||||||
return macXattr.readAttribute(file, key.toString());
|
return macXattr.read(key.toString());
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// ignore
|
// ignore
|
||||||
|
@ -107,12 +86,12 @@ public class MetaAttributeView extends AbstractMap<String, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xattr instanceof Xattrj) {
|
if (xattr instanceof MacXattrView) {
|
||||||
Xattrj macXattr = (Xattrj) xattr;
|
MacXattrView macXattr = (MacXattrView) xattr;
|
||||||
if (value == null || value.isEmpty()) {
|
if (value == null || value.isEmpty()) {
|
||||||
macXattr.removeAttribute(file, key);
|
macXattr.delete(key);
|
||||||
} else {
|
} else {
|
||||||
macXattr.writeAttribute(file, key, value);
|
macXattr.write(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -128,9 +107,9 @@ public class MetaAttributeView extends AbstractMap<String, String> {
|
||||||
return attributeView.list();
|
return attributeView.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xattr instanceof Xattrj) {
|
if (xattr instanceof MacXattrView) {
|
||||||
Xattrj macXattr = (Xattrj) xattr;
|
MacXattrView macXattr = (MacXattrView) xattr;
|
||||||
return Arrays.asList(macXattr.listAttributes(file));
|
return macXattr.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -189,4 +168,30 @@ public class MetaAttributeView extends AbstractMap<String, String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<String> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <sys/xattr.h>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
|
@ -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<String> 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<String>(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<String> decodeStringSequence(ByteBuffer bb) {
|
||||||
|
List<String> names = new ArrayList<String>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue