diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e47fd12
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,16 @@
+root = true
+[*]
+indent_style=tab
+indent_size=tab
+tab_width=4
+end_of_line=lf
+charset=utf-8
+trim_trailing_whitespace=true
+max_line_length=120
+insert_final_newline=true
+
+[*.{yml,sh}]
+indent_style=space
+indent_size=2
+tab_width=8
+end_of_line=lf
diff --git a/.gitmodules b/.gitmodules
index bdf8d3a..af90e2b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
[submodule "etc/hidapi"]
path = etc/hidapi
url = https://github.com/paritytech/hidapi.git
+[submodule "etc/eudev"]
+ path = etc/eudev
+ url = https://github.com/gentoo/eudev.git
diff --git a/Cargo.toml b/Cargo.toml
index 989fce2..d4a7fc1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,4 +17,5 @@ documentation = "https://beta.docs.rs/hidapi"
libc = "0.2.15"
[build-dependencies]
+autotools = "0.2"
cc = "1.0"
diff --git a/build.rs b/build.rs
index 3ab282d..162805b 100644
--- a/build.rs
+++ b/build.rs
@@ -17,32 +17,133 @@
// along with hidapi_rust. If not, see .
// *************************************************************************
+extern crate autotools;
extern crate cc;
+use std::process::Command;
use std::env;
+use std::path::PathBuf;
+
+#[cfg(windows)]
+const GIT: &str = "git.exe";
+#[cfg(not(windows))]
+const GIT: &str = "git";
fn main() {
- let target = env::var("TARGET").unwrap();
-
- if target.contains("windows") {
- cc::Build::new()
- .file("etc/hidapi/windows/hid.c")
- .include("etc/hidapi/hidapi")
- .compile("libhidapi.a");
- println!("cargo:rustc-link-lib=setupapi");
-
- } else if target.contains("darwin") {
- cc::Build::new()
- .file("etc/hidapi/mac/hid.c")
- .include("etc/hidapi/hidapi")
- .compile("libhidapi.a");
- println!("cargo:rustc-link-lib=framework=IOKit");
- println!("cargo:rustc-link-lib=framework=CoreFoundation");
-
- } else if target.contains("linux") {
- let mut config = cc::Build::new();
- config.file("etc/hidapi/linux/hid.c").include("etc/hidapi/hidapi");
- config.compile("libhidapi.a");
- println!("cargo:rustc-link-lib=udev");
- }
+ let target = env::var("TARGET").unwrap();
+
+ if target.contains("windows") {
+ cc::Build::new()
+ .file("etc/hidapi/windows/hid.c")
+ .include("etc/hidapi/hidapi")
+ .compile("libhidapi.a");
+ println!("cargo:rustc-link-lib=setupapi");
+
+ } else if target.contains("darwin") {
+ cc::Build::new()
+ .file("etc/hidapi/mac/hid.c")
+ .include("etc/hidapi/hidapi")
+ .compile("libhidapi.a");
+
+ println!("cargo:rustc-link-lib=framework=IOKit");
+ println!("cargo:rustc-link-lib=framework=CoreFoundation");
+
+ } else if target.contains("android") {
+ enable_android_hack();
+ env::set_var("CXX", "arm-linux-androideabi-clang++");
+ env::set_var("CC", "arm-linux-androideabi-gcc");
+ let libudev = autotools::Config::new("etc/eudev")
+ // -s: make symlinks
+ // -m: build if it applicable
+ // -i: install
+ // -v: verbose
+ // -f: consider all files obsolete
+ .reconf("-smivf")
+ .insource(true)
+ .host("arm-linux-androideabi")
+ .disable_shared()
+ .disable("introspection", None)
+ .disable("programs", None)
+ .disable("hwdb", None)
+ .cflag("-D LINE_MAX=2048")
+ .cflag("-D RLIMIT_NLIMITS=15")
+ .cflag("-D IPTOS_LOWCOST=2")
+ .cflag("-std=gnu99")
+ .build();
+
+ disable_android_hack();
+
+ let mut config = cc::Build::new();
+ config.file("etc/hidapi/linux/hid.c").include("etc/hidapi/hidapi");
+ config.compile("libhidapi.a");
+
+ println!("cargo:rustc-link-search=native={}/src/libudev/.libs", libudev.display());
+ println!("cargo:rustc-link-lib=static=udev");
+ } else if target.contains("linux") {
+
+ let libudev = autotools::Config::new("etc/eudev")
+ // -s: make symlinks
+ // -m: build if it applicable
+ // -i: install
+ // -v: verbose
+ // -f: consider all files obsolete
+ .reconf("-smivf")
+ .insource(true)
+ .disable_shared()
+ .build();
+
+ cc::Build::new()
+ .file("etc/hidapi/linux/hid.c")
+ .include("etc/hidapi/hidapi")
+ .compile("libhidapi.a");
+
+ println!("cargo:rustc-link-search=native={}/src/libudev/.libs", libudev.display());
+ println!("cargo:rustc-link-lib=static=udev");
+ }
+}
+
+fn enable_android_hack() {
+ let (start_dir, dst_dir) = get_dirs();
+ env::set_current_dir(dst_dir).expect("set current dir to \"etc/eudev\" failed");
+
+ let cmd = Command::new(GIT)
+ .args(&["checkout", "83d918449f22720d84a341a05e24b6d109e6d3ae"])
+ .status()
+ .expect("git checkout failed");
+ assert!(cmd.success(), format!("{}", cmd));
+
+ let cmd = Command::new(GIT)
+ .args(&["apply", "../libudev.patch"])
+ .status()
+ .expect("git apply etc/libudev.patch failed");
+ assert!(cmd.success(), format!("{}", cmd));
+
+ env::set_current_dir(start_dir).expect("set current dir to \"../..\" failed");
+}
+
+fn disable_android_hack() {
+ let (start_dir, dst_dir) = get_dirs();
+ env::set_current_dir(dst_dir).expect("set current dir to \"etc/eudev\" failed");
+
+ let cmd = Command::new(GIT)
+ .args(&["apply", "-R", "../libudev.patch"])
+ .status()
+ .expect("git revert patch failed");
+
+ assert!(cmd.success(), format!("{}", cmd));
+
+ let cmd = Command::new(GIT)
+ .args(&["checkout", "master"])
+ .status()
+ .expect("git checkout master failed");
+
+ assert!(cmd.success(), format!("{}", cmd));
+
+ env::set_current_dir(start_dir).expect("set current dir to \"../..\" failed");
+}
+
+fn get_dirs() -> (PathBuf, PathBuf) {
+ let start_dir = env::current_dir().expect("Current dir failed");
+ let dst_dir = start_dir.join("etc/eudev");
+ (start_dir.to_owned(), dst_dir.to_owned())
}
diff --git a/etc/eudev b/etc/eudev
new file mode 160000
index 0000000..34b2037
--- /dev/null
+++ b/etc/eudev
@@ -0,0 +1 @@
+Subproject commit 34b2037d379e33f1cf79a3449b89ac2f98d546d9
diff --git a/etc/libudev.patch b/etc/libudev.patch
new file mode 100644
index 0000000..ba7e849
--- /dev/null
+++ b/etc/libudev.patch
@@ -0,0 +1,216 @@
+diff --git a/src/collect/collect.c b/src/collect/collect.c
+index 2cf1f00..b24f26b 100644
+--- a/src/collect/collect.c
++++ b/src/collect/collect.c
+@@ -84,7 +84,7 @@ static void usage(void)
+ " invoked for each ID in ) collect returns 0, the\n"
+ " number of missing IDs otherwise.\n"
+ " On error a negative number is returned.\n\n"
+- , program_invocation_short_name);
++ , "parity");
+ }
+
+ /*
+diff --git a/src/scsi_id/scsi_id.c b/src/scsi_id/scsi_id.c
+index 8b76d87..7bf3948 100644
+--- a/src/scsi_id/scsi_id.c
++++ b/src/scsi_id/scsi_id.c
+@@ -321,7 +321,7 @@ static void help(void) {
+ " -u --replace-whitespace Replace all whitespace by underscores\n"
+ " -v --verbose Verbose logging\n"
+ " -x --export Print values as environment keys\n"
+- , program_invocation_short_name);
++ , "parity");
+
+ }
+
+diff --git a/src/shared/hashmap.h b/src/shared/hashmap.h
+index a03ee58..a7c2005 100644
+--- a/src/shared/hashmap.h
++++ b/src/shared/hashmap.h
+@@ -98,10 +98,7 @@ extern const struct hash_ops uint64_hash_ops;
+ #if SIZEOF_DEV_T != 8
+ unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) _pure_;
+ int devt_compare_func(const void *a, const void *b) _pure_;
+-extern const struct hash_ops devt_hash_ops = {
+- .hash = devt_hash_func,
+- .compare = devt_compare_func
+-};
++extern const struct hash_ops devt_hash_ops;
+ #else
+ #define devt_hash_func uint64_hash_func
+ #define devt_compare_func uint64_compare_func
+diff --git a/src/shared/log.c b/src/shared/log.c
+index 4a40996..1496984 100644
+--- a/src/shared/log.c
++++ b/src/shared/log.c
+@@ -335,7 +335,7 @@ static int write_to_syslog(
+
+ IOVEC_SET_STRING(iovec[0], header_priority);
+ IOVEC_SET_STRING(iovec[1], header_time);
+- IOVEC_SET_STRING(iovec[2], program_invocation_short_name);
++ IOVEC_SET_STRING(iovec[2], "parity");
+ IOVEC_SET_STRING(iovec[3], header_pid);
+ IOVEC_SET_STRING(iovec[4], buffer);
+
+@@ -383,7 +383,7 @@ static int write_to_kmsg(
+ char_array_0(header_pid);
+
+ IOVEC_SET_STRING(iovec[0], header_priority);
+- IOVEC_SET_STRING(iovec[1], program_invocation_short_name);
++ IOVEC_SET_STRING(iovec[1], "parity");
+ IOVEC_SET_STRING(iovec[2], header_pid);
+ IOVEC_SET_STRING(iovec[3], buffer);
+ IOVEC_SET_STRING(iovec[4], "\n");
+diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c
+index 6af7163..3271e56 100644
+--- a/src/udev/udevadm-control.c
++++ b/src/udev/udevadm-control.c
+@@ -41,7 +41,7 @@ static void print_help(void) {
+ " -p --property=KEY=VALUE Set a global property for all events\n"
+ " -m --children-max=N Maximum number of children\n"
+ " --timeout=SECONDS Maximum time to block for a reply\n"
+- , program_invocation_short_name);
++ , "parity");
+ }
+
+ static int adm_control(struct udev *udev, int argc, char *argv[]) {
+diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c
+index 0aec976..a31ac02 100644
+--- a/src/udev/udevadm-info.c
++++ b/src/udev/udevadm-info.c
+@@ -279,7 +279,7 @@ static void help(void) {
+ " -P --export-prefix Export the key name with a prefix\n"
+ " -e --export-db Export the content of the udev database\n"
+ " -c --cleanup-db Clean up the udev database\n"
+- , program_invocation_short_name);
++ , "parity");
+ }
+
+ static int uinfo(struct udev *udev, int argc, char *argv[]) {
+diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c
+index 15ded09..b58dd08 100644
+--- a/src/udev/udevadm-monitor.c
++++ b/src/udev/udevadm-monitor.c
+@@ -73,7 +73,7 @@ static void help(void) {
+ " -u --udev Print udev events\n"
+ " -s --subsystem-match=SUBSYSTEM[/DEVTYPE] Filter events by subsystem\n"
+ " -t --tag-match=TAG Filter events by tag\n"
+- , program_invocation_short_name);
++ , "parity");
+ }
+
+ static int adm_monitor(struct udev *udev, int argc, char *argv[]) {
+diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c
+index 33597bc..b36a504 100644
+--- a/src/udev/udevadm-settle.c
++++ b/src/udev/udevadm-settle.c
+@@ -43,7 +43,7 @@ static void help(void) {
+ " --version Show package version\n"
+ " -t --timeout=SECONDS Maximum time to wait for events\n"
+ " -E --exit-if-exists=FILE Stop waiting if file exists\n"
+- , program_invocation_short_name);
++ , "parity");
+ }
+
+ static int adm_settle(struct udev *udev, int argc, char *argv[]) {
+diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c
+index baaeca9..50ed812 100644
+--- a/src/udev/udevadm-test-builtin.c
++++ b/src/udev/udevadm-test-builtin.c
+@@ -39,7 +39,7 @@ static void help(struct udev *udev) {
+ " -h --help Print this message\n"
+ " --version Print version of the program\n\n"
+ "Commands:\n"
+- , program_invocation_short_name);
++ , "parity");
+
+ udev_builtin_list(udev);
+ }
+diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c
+index 47fd924..a855412 100644
+--- a/src/udev/udevadm-test.c
++++ b/src/udev/udevadm-test.c
+@@ -39,7 +39,7 @@ static void help(void) {
+ " --version Show package version\n"
+ " -a --action=ACTION Set action string\n"
+ " -N --resolve-names=early|late|never When to resolve names\n"
+- , program_invocation_short_name);
++ , "parity");
+ }
+
+ static int adm_test(struct udev *udev, int argc, char *argv[]) {
+diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c
+index 4dc756a..67787d3 100644
+--- a/src/udev/udevadm-trigger.c
++++ b/src/udev/udevadm-trigger.c
+@@ -92,7 +92,7 @@ static void help(void) {
+ " -y --sysname-match=NAME Trigger devices with this /sys path\n"
+ " --name-match=NAME Trigger devices with this /dev name\n"
+ " -b --parent-match=NAME Trigger devices with that parent device\n"
+- , program_invocation_short_name);
++ , "parity");
+ }
+
+ static int adm_trigger(struct udev *udev, int argc, char *argv[]) {
+diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c
+index 3e57cf6..b03dfaa 100644
+--- a/src/udev/udevadm.c
++++ b/src/udev/udevadm.c
+@@ -62,7 +62,7 @@ static int adm_help(struct udev *udev, int argc, char *argv[]) {
+ printf("%s [--help] [--version] [--debug] COMMAND [COMMAND OPTIONS]\n\n"
+ "Send control commands or test the device manager.\n\n"
+ "Commands:\n"
+- , program_invocation_short_name);
++ , "parity");
+
+ for (i = 0; i < ELEMENTSOF(udevadm_cmds); i++)
+ if (udevadm_cmds[i]->help != NULL)
+@@ -128,7 +128,7 @@ int main(int argc, char *argv[]) {
+ goto out;
+ }
+
+- fprintf(stderr, "%s: missing or unknown command\n", program_invocation_short_name);
++ fprintf(stderr, "%s: missing or unknown command\n", "parity");
+ rc = 2;
+ out:
+ mac_selinux_finish();
+diff --git a/src/udev/udevd.c b/src/udev/udevd.c
+index cf826c6..4eec0af 100644
+--- a/src/udev/udevd.c
++++ b/src/udev/udevd.c
+@@ -1041,7 +1041,7 @@ static void help(void) {
+ " -t --event-timeout=SECONDS Seconds to wait before terminating an event\n"
+ " -N --resolve-names=early|late|never\n"
+ " When to resolve users and groups\n"
+- , program_invocation_short_name);
++ , "parity");
+ }
+
+ static int parse_argv(int argc, char *argv[]) {
+diff --git a/src/v4l_id/v4l_id.c b/src/v4l_id/v4l_id.c
+index 1dce0d5..f65badf 100644
+--- a/src/v4l_id/v4l_id.c
++++ b/src/v4l_id/v4l_id.c
+@@ -49,7 +49,7 @@ int main(int argc, char *argv[]) {
+ printf("%s [-h,--help] \n\n"
+ "Video4Linux device identification.\n\n"
+ " -h Print this message\n"
+- , program_invocation_short_name);
++ , "parity");
+ return 0;
+ case '?':
+ return -EINVAL;
+diff --git a/src/shared/path-util.c b/src/shared/path-util.c
+index 0744563..7151356 100644
+--- a/src/shared/path-util.c
++++ b/src/shared/path-util.c
+@@ -109,7 +109,7 @@ char *path_make_absolute_cwd(const char *p) {
+ if (path_is_absolute(p))
+ return strdup(p);
+
+- cwd = get_current_dir_name();
++ cwd = getcwd(malloc(128), 128);
+ if (!cwd)
+ return NULL;
+