summaryrefslogtreecommitdiffstats
path: root/lib/wordexp-rs/src/lib.rs
blob: 0a2e1bf0082fa44cef432826a62648c986bc2769 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
extern crate libc;

mod ll;

use std::ffi::{CStr, CString};

pub const WRDE_DOOFFS: i32 = (1 << 0);
pub const WRDE_APPEND: i32 = (1 << 1);
pub const WRDE_NOCMD: i32 = (1 << 2);
pub const WRDE_REUSE: i32 = (1 << 3);
pub const WRDE_SHOWERR: i32 = (1 << 4);
pub const WRDE_UNDEF: i32 = (1 << 5);

trait ToCStr {
    fn to_c_str(&self) -> CString;
}

impl<'a> ToCStr for &'a str {
    fn to_c_str(&self) -> CString {
        CString::new(*self).unwrap()
    }
}

#[allow(dead_code)]
pub struct Wordexp<'a> {
    we_wordv: Vec<&'a str>,
    counter: usize,
    wordexp_ref: ll::wordexp_t,
}

impl<'a> Wordexp<'a> {
    pub fn new(wordexp_ref: ll::wordexp_t) -> Self {
        let we_wordc: usize = wordexp_ref.we_wordc as usize;
        let mut we_wordv: Vec<&str> = Vec::with_capacity(we_wordc);
        unsafe {
            let ptr: *const *const libc::c_char = wordexp_ref.we_wordv;

            for i in 0..we_wordc {
                let cstr = CStr::from_ptr(*ptr.offset(i as isize));
                if let Ok(s) = cstr.to_str() {
                    we_wordv.push(s);
                }
            }
        }

        Wordexp {
            we_wordv,
            counter: 0,
            wordexp_ref,
        }
    }
}

impl<'a> std::ops::Drop for Wordexp<'a> {
    fn drop(&mut self) {
        drop(&self.wordexp_ref);
    }
}

impl<'a> std::iter::Iterator for Wordexp<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<&'a str> {
        if self.counter >= self.we_wordv.len() {
            self.counter = 0;
            None
        } else {
            let item = self.we_wordv[self.counter];
            self.counter = self.counter + 1;
            Some(item)
        }
    }
}

#[derive(Clone, Debug)]
pub struct WordexpError {
    pub error_type: i32,
}

impl WordexpError {
    pub fn new(error_type: i32) -> Self {
        WordexpError { error_type }
    }
}

impl std::fmt::Display for WordexpError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.error_type)
    }
}

impl std::error::Error for WordexpError {}

pub fn wordexp<'a>(s: &str, flags: i32) -> Result<Wordexp, WordexpError> {
    let mut wordexp = ll::wordexp_t {
        we_wordc: 0,
        we_wordv: std::ptr::null(),
        we_offs: 0,
    };

    let result: i32;
    unsafe {
        result = ll::wordexp(s.to_c_str().as_ptr(), &mut wordexp, flags);
        match result {
            0 => Ok(Wordexp::new(wordexp)),
            _ => Err(WordexpError::new(result)),
        }
    }
}