about summary refs log tree commit diff
path: root/pkgs/profpatsch/execline
diff options
context:
space:
mode:
authorProfpatsch <mail@profpatsch.de>2020-06-28 15:14:04 +0200
committerProfpatsch <mail@profpatsch.de>2020-06-28 15:14:04 +0200
commit3f79ddf06375c3b34569c8b9ce91a607bbd0f052 (patch)
treec4e7515cb141cd9bce86df7e113056000af8f60d /pkgs/profpatsch/execline
parent13ad936f8432d2fe46169716ddf5eb7d4f84f202 (diff)
pkgs/profpatsch/execline: add el_exec and el_substitute
el_exec: wraps the various execve wrappers in skalib that are useful
for writing execline-like utils. currently only `xpathexec0` is
supported, which execs into the argv you give it or errors with the
right error if file not found.

el_substitute: execline argv substitution! Wraps the execline
function, so it will behave exactly the same as the existing execline
utils, like `importas`.
Diffstat (limited to 'pkgs/profpatsch/execline')
-rw-r--r--pkgs/profpatsch/execline/default.nix34
-rw-r--r--pkgs/profpatsch/execline/el-semicolon.nix19
-rw-r--r--pkgs/profpatsch/execline/el_exec.rs32
-rw-r--r--pkgs/profpatsch/execline/el_substitute.rs190
4 files changed, 256 insertions, 19 deletions
diff --git a/pkgs/profpatsch/execline/default.nix b/pkgs/profpatsch/execline/default.nix
new file mode 100644
index 00000000..317ca97d
--- /dev/null
+++ b/pkgs/profpatsch/execline/default.nix
@@ -0,0 +1,34 @@
+{ pkgs, writeRustSimpleLib, rust-deps }:
+
+let
+  el-semicolon-common = tests: writeRustSimpleLib "el_semicolon" {
+    buildTests = tests;
+    release = false;
+    verbose = true;
+  } ./el_semicolon.rs;
+
+  el-semicolon-tests = el-semicolon-common true;
+  el-semicolon = el-semicolon-common false;
+
+  el-exec = writeRustSimpleLib "el_exec" {
+    dependencies = [ rust-deps.libc ];
+    buildInputs = [ pkgs.skalibs ];
+    release = false;
+    verbose = true;
+  } ./el_exec.rs;
+
+  el-substitute = writeRustSimpleLib "el_substitute" {
+    dependencies = [ rust-deps.libc rust-deps.errno ];
+    buildInputs = [ pkgs.skalibs ];
+    release = false;
+    verbose = true;
+  } ./el_substitute.rs;
+
+in {
+  inherit
+    el-semicolon
+    el-semicolon-tests
+    el-exec
+    el-substitute
+    ;
+}
diff --git a/pkgs/profpatsch/execline/el-semicolon.nix b/pkgs/profpatsch/execline/el-semicolon.nix
index 73694bdb..e69de29b 100644
--- a/pkgs/profpatsch/execline/el-semicolon.nix
+++ b/pkgs/profpatsch/execline/el-semicolon.nix
@@ -1,19 +0,0 @@
-{ writeRustSimpleLib }:
-
-let
-  el-semicolon-common = tests: writeRustSimpleLib "el_semicolon" {
-    buildTests = tests;
-    release = false;
-    verbose = true;
-  } ./el_semicolon.rs;
-
-  el-semicolon-tests = el-semicolon-common true;
-
-  el-semicolon = el-semicolon-common false;
-
-in {
-  inherit
-    el-semicolon
-    el-semicolon-tests
-    ;
-}
diff --git a/pkgs/profpatsch/execline/el_exec.rs b/pkgs/profpatsch/execline/el_exec.rs
new file mode 100644
index 00000000..25fad36f
--- /dev/null
+++ b/pkgs/profpatsch/execline/el_exec.rs
@@ -0,0 +1,32 @@
+use std::ffi::CStr;
+extern crate libc;
+
+fn to_c_argv<S: AsRef<CStr>>(s: &[S]) -> (&[S], Vec<*const libc::c_char>) {
+    let mut c_ptr_args = s.iter()
+        .map(|s| s.as_ref().as_ptr())
+        .collect::<Vec<*const libc::c_char>>();
+    c_ptr_args.push(std::ptr::null());
+    (s, c_ptr_args)
+}
+
+/// Exec into argv, or exit 0 if it’s empty.
+/// Will throw 127 if the executable is not found (ENOENT)
+/// and 126 for any other exec error.
+pub fn xpathexec0<'a, S: AsRef<CStr>>(argv: &'a [S]) {
+    let (c_strings, c_argv) = to_c_argv(argv);
+
+    unsafe {
+        C::xpathexec0(
+            c_argv.as_ptr() as *const *const libc::c_char
+        )
+    }
+}
+
+mod C {
+    #[link(name = "skarnet")]
+    extern "C" {
+        pub fn xpathexec0(
+            argv: *const *const libc::c_char
+        );
+    }
+}
diff --git a/pkgs/profpatsch/execline/el_substitute.rs b/pkgs/profpatsch/execline/el_substitute.rs
new file mode 100644
index 00000000..b7e98cde
--- /dev/null
+++ b/pkgs/profpatsch/execline/el_substitute.rs
@@ -0,0 +1,190 @@
+extern crate errno;
+extern crate libc;
+
+use std::ffi::{CStr, CString};
+
+#[repr(C)]
+pub struct Stralloc_C {
+    s: *mut libc::c_char,
+    len: libc::size_t,
+    a: libc::size_t
+}
+
+fn stralloc_zero() -> Stralloc_C {
+    Stralloc_C {
+        s: std::ptr::null_mut(),
+        len: 0,
+        a: 0
+    }
+}
+
+#[link(name = "skarnet")]
+extern "C" {
+    fn stralloc_free(sa: *mut Stralloc_C);
+    fn stralloc_ready_tuned(
+        sa: *mut Stralloc_C,
+        n: libc::size_t,
+        base: libc::size_t,
+        a: libc::size_t,
+        b: libc::size_t,
+    ) -> libc::c_int;
+
+    fn stralloc_copyb(
+        sa: *mut Stralloc_C,
+        s: *const libc::c_char,
+        len: libc::size_t
+    ) -> libc::c_int;
+}
+
+fn stralloc_ready(sa: *mut Stralloc_C, n: libc::size_t) {
+    unsafe {
+        if (stralloc_ready_tuned(sa, n, 8, 1, 8) == 0) {
+            panic!("{}", errno::errno());
+        }
+    }
+}
+
+struct Stralloc(Stralloc_C);
+
+impl Stralloc {
+    fn new() -> Self {
+        let mut sa = stralloc_zero();
+        unsafe {
+            stralloc_ready(&mut sa, 0);
+        }
+        Stralloc(sa)
+    }
+}
+
+impl AsMut<Stralloc_C> for Stralloc {
+    fn as_mut(&mut self) -> &mut Stralloc_C {
+        match self {
+            Stralloc(s) => s
+        }
+    }
+}
+
+impl AsRef<Stralloc_C> for Stralloc {
+    fn as_ref(&self) -> &Stralloc_C {
+        match self {
+            Stralloc(s) => s
+        }
+    }
+}
+
+impl<'a> From<&mut [u8]> for Stralloc {
+    fn from(s: &mut [u8]) -> Self {
+        let mut sa = stralloc_zero();
+        let ptr = s.as_mut_ptr() as *mut libc::c_char;
+        unsafe {
+            if stralloc_copyb(&mut sa, ptr, s.len()) == 0 {
+                panic!("{}", errno::errno());
+            }
+        }
+        Stralloc(sa)
+    }
+}
+
+// TODO not sure if stralloc will always be a contiguous block?
+// that’s the precondition for from_raw_parts
+impl AsRef<[u8]> for Stralloc_C {
+    fn as_ref(&self) -> &[u8] {
+        let ptr = self.s as *const u8;
+        unsafe {
+            std::slice::from_raw_parts(ptr, self.len)
+        }
+    }
+}
+
+impl Drop for Stralloc {
+    fn drop(&mut self) {
+        match self {
+            Stralloc(inner) => {
+                unsafe {
+                    stralloc_free(inner);
+                }
+            }
+        }
+    }
+}
+
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct Elsubst_C
+{
+    var: libc::size_t,
+    value: libc::size_t,
+    // values are \0-separated strings,
+    // and n is the amount of elements in one such string
+    n: libc::c_uint
+}
+
+#[link(name = "execline")]
+extern "C" {
+    fn el_substitute(
+        dst: *mut Stralloc_C,
+        src: *const libc::c_char,
+        len: libc::size_t,
+        // length is given by nsubst
+        vars: *const libc::c_char,
+        // length is given by nsubst
+        values: *const libc::c_char,
+
+        substs: *const Elsubst_C,
+        nsubst: libc::size_t
+    ) -> libc::c_int;
+}
+
+pub struct Subst<'a> {
+    pub var: &'a CStr,
+    pub value: &'a CStr,
+}
+
+fn simple_substitute<'a, 'b>(subst: &[Subst<'a>], src: &CStr) -> CString {
+    let len = src.to_bytes_with_nul().len() as libc::size_t;
+    let src = src.as_ptr() as *const libc::c_char;
+    let mut vars : Vec<u8> = Vec::new();
+    let mut values : Vec<u8> = Vec::new();
+    let mut substs : Vec<Elsubst_C> = Vec::new();
+    let mut var_i = 0;
+    let mut value_i = 0;
+    for s in subst {
+        let var = s.var.to_bytes_with_nul();
+        let value = s.value.to_bytes_with_nul();
+        vars.extend_from_slice(var);
+        values.extend_from_slice(value);
+        substs.push(Elsubst_C {
+            // these index into the vars/values arrays given to el_substitute
+            var: var_i,
+            value: value_i,
+            // we don’t deal with split values here
+            n: 1
+        });
+        var_i += var.len();
+        value_i += value.len();
+    }
+    let nsubst = subst.len();
+    let mut dst = stralloc_zero();
+    unsafe {
+        if el_substitute(
+            &mut dst,
+            src,
+            len,
+            vars.as_ptr() as *const libc::c_char,
+            values.as_ptr() as *const libc::c_char,
+            substs.as_ptr() as *const Elsubst_C,
+            nsubst
+        ) == -1 {
+            panic!("{}", errno::errno());
+        }
+        // el_substitute returns a \0-delim C string
+        CStr::from_bytes_with_nul_unchecked(dst.as_ref()).to_owned()
+    }
+}
+
+pub fn simple_substitute_argv<'a, 'b, S: AsRef<CStr>>(subst: &[Subst<'a>], argv: &'b [S]) -> Vec<CString> {
+    argv.into_iter()
+        .map(|arg| simple_substitute(subst, arg.as_ref()))
+        .collect::<Vec<_>>()
+}