Skip to content
Snippets Groups Projects
Commit 3c9d959e authored by Michael Aaron Murphy's avatar Michael Aaron Murphy
Browse files

Implement String Namespace Plugins Support

The current API is subject to change, however, as the current feature set is a bit limited.
However, you can test out the existing support by installing the new git plugin, which is
recorded at the bottom of the newly-updated README.
parent c5b1d29a
No related branches found
No related tags found
No related merge requests found
......@@ -10,6 +10,7 @@ dependencies = [
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)",
"libloading 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"liner 0.4.0 (git+https://github.com/MovingtoMars/liner/)",
"nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"permutate 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -81,6 +82,15 @@ name = "glob"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "0.2.8"
......@@ -91,6 +101,16 @@ name = "libc"
version = "0.2.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libloading"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "liner"
version = "0.4.0"
......@@ -283,8 +303,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
"checksum libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)" = "8a014d9226c2cc402676fbe9ea2e15dd5222cd1dd57f576b5b283178c944a264"
"checksum libloading 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "be99f814beb3e9503a786a592c909692bb6d4fc5a695f6ed7987223acfbd5194"
"checksum liner 0.4.0 (git+https://github.com/MovingtoMars/liner/)" = "<none>"
"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487"
......
......@@ -62,6 +62,8 @@ libc = "0.2"
nix = "0.8"
# Obtains user directories
users = "0.5.1"
# Enables loading plugins
libloading = "0.4"
[build-dependencies]
ansi_term = "0.9"
......
This diff is collapsed.
......@@ -19,6 +19,7 @@ extern crate regex;
#[cfg(all(unix, not(target_os = "redox")))] extern crate libc;
#[cfg(all(unix, not(target_os = "redox")))] extern crate nix;
#[cfg(all(unix, not(target_os = "redox")))] extern crate users as users_unix;
#[cfg(all(unix, not(target_os = "redox")))] extern crate libloading;
#[cfg(target_os = "redox")] extern crate syscall;
#[cfg(target_os = "redox")]
......
use libloading::Library;
use std::fs::ReadDir;
use types::Identifier;
/// Grabs `Library` entries found within a given directory
pub struct LibraryIterator {
directory: ReadDir,
}
impl LibraryIterator {
pub fn new(directory: ReadDir) -> LibraryIterator { LibraryIterator { directory } }
}
impl Iterator for LibraryIterator {
type Item = (Identifier, Library);
fn next(&mut self) -> Option<(Identifier, Library)> {
while let Some(entry) = self.directory.next() {
let entry = if let Ok(entry) = entry { entry } else { continue };
let path = entry.path();
if path.is_file() && path.extension().map_or(false, |ext| ext == "so") {
let identifier = match path.file_stem().unwrap().to_str() {
Some(filename) => Identifier::from(filename),
None => {
eprintln!("ion: namespace plugin has invalid filename");
continue;
}
};
match Library::new(path.as_os_str()) {
Ok(library) => return Some((identifier, library)),
Err(why) => {
eprintln!("ion: failed to load library: {:?}, {:?}", path, why);
continue;
}
}
} else {
continue;
}
}
None
}
}
pub mod namespaces;
#[cfg(all(unix, not(target_os = "redox")))]
mod library_iter;
#[cfg(all(unix, not(target_os = "redox")))]
pub use self::library_iter::*;
use app_dirs::{AppDataType, AppInfo, app_root};
use std::path::PathBuf;
pub fn config_dir() -> Option<PathBuf> {
match app_root(
AppDataType::UserConfig,
&AppInfo {
name: "ion",
author: "Redox OS Developers",
},
) {
Ok(mut path) => {
path.push("plugins");
Some(path)
}
Err(why) => {
eprintln!("ion: unable to get config directory: {:?}", why);
None
}
}
}
#[cfg(target_os = "redox")]
mod redox;
#[cfg(target_os = "redox")]
pub use self::redox::*;
#[cfg(all(unix, not(target_os = "redox")))]
mod unix;
#[cfg(all(unix, not(target_os = "redox")))]
pub use self::unix::*;
use std::ffi::CString;
use std::fmt::{self, Display, Formatter};
use std::io;
use types::Identifier;
#[derive(Debug)]
pub enum NamespaceError {
SymbolErr(io::Error),
UTF8Function,
UTF8Result,
FunctionMissing(Identifier),
}
impl Display for NamespaceError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
NamespaceError::SymbolErr(ref error) => write!(f, "symbol error: {}", error),
NamespaceError::UTF8Function => write!(f, "function has invalid UTF-8 name"),
NamespaceError::UTF8Result => write!(f, "result is not valid UTF-8"),
NamespaceError::FunctionMissing(func) => write!(f, "{} doesn't exist in namespace", func),
}
}
}
#[repr(C)]
#[derive(Debug)]
struct NamespaceResult {
exists: bool,
data: *mut i8,
}
impl NamespaceResult {
fn into_option(self) -> Option<CString> {
if self.exists { Some(unsafe { CString::from_raw(self.data) }) } else { None }
}
}
pub struct StringNamespace {}
impl StringNamespace {
pub fn new() -> Result<StringNamespace, NamespaceError> { Err(NamespaceError::FunctionMissing) }
pub fn execute(&self, function: Identifier) -> Result<Option<String>, NamespaceError> { Ok(None) }
}
pub fn collect() -> FnvHashMap<Identifier, StringNamespace> {
eprintln!("ion: Redox doesn't support plugins yet");
let mut hashmap = FnvHashMap::default();
hashmap
}
use super::super::{LibraryIterator, config_dir};
use fnv::FnvHashMap;
use libloading::{Library, Symbol};
use libloading::os::unix::Symbol as RawSymbol;
use std::ffi::CString;
use std::fmt::{self, Display, Formatter};
use std::fs::read_dir;
use std::io;
use std::slice;
use std::str;
use types::Identifier;
#[derive(Debug)]
pub enum NamespaceError {
SymbolErr(io::Error),
UTF8Function,
UTF8Result,
FunctionMissing(Identifier),
}
impl Display for NamespaceError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match *self {
NamespaceError::SymbolErr(ref error) => write!(f, "symbol error: {}", error),
NamespaceError::UTF8Function => write!(f, "function has invalid UTF-8 name"),
NamespaceError::UTF8Result => write!(f, "result is not valid UTF-8"),
NamespaceError::FunctionMissing(ref func) => write!(f, "{} doesn't exist in namespace", func),
}
}
}
#[repr(C)]
#[derive(Debug)]
struct NamespaceResult {
exists: bool,
data: *mut i8,
}
impl NamespaceResult {
fn into_option(self) -> Option<CString> {
if self.exists { Some(unsafe { CString::from_raw(self.data) }) } else { None }
}
}
pub struct StringNamespace {
/// Do not remove this field, as it ensures that the library remains loaded.
#[allow(dead_code)]
library: Library,
/// A hash map of symbols collected from the `Library` stored in the `library` field.
symbols: FnvHashMap<Identifier, RawSymbol<unsafe extern "C" fn() -> NamespaceResult>>,
}
impl StringNamespace {
pub fn new(library: Library) -> Result<StringNamespace, NamespaceError> {
unsafe {
let mut symbols = FnvHashMap::default();
{
let index: Symbol<unsafe extern "C" fn() -> *const u8> =
library.get(b"index\0").map_err(NamespaceError::SymbolErr)?;
let symbol_list = index();
let (mut start, mut counter) = (0, 0usize);
let symbol_list: &[u8] = {
let mut byte = *symbol_list.offset(0);
while byte != b'\0' {
counter += 1;
byte = *symbol_list.offset(counter as isize);
}
slice::from_raw_parts(symbol_list, counter)
};
counter = 0;
for &byte in symbol_list {
if byte == b' ' {
if start == counter {
start += 1;
} else {
let slice = &symbol_list[start..counter];
let identifier = str::from_utf8(slice).map(Identifier::from).map_err(|_| {
NamespaceError::UTF8Function
})?;
let mut symbol = Vec::new();
symbol.reserve_exact(slice.len() + 1);
symbol.extend_from_slice(slice);
symbol.push(b'\0');
let symbol: Symbol<unsafe extern "C" fn() -> NamespaceResult> =
library.get(symbol.as_slice()).map_err(
NamespaceError::SymbolErr,
)?;
symbols.insert(identifier, symbol.into_raw());
start = counter + 1;
}
}
counter += 1;
}
if counter != start {
let slice = &symbol_list[start..];
let identifier = str::from_utf8(slice).map(Identifier::from).map_err(|_| {
NamespaceError::UTF8Function
})?;
let mut symbol = Vec::new();
symbol.reserve_exact(slice.len() + 1);
symbol.extend_from_slice(slice);
symbol.push(b'\0');
let symbol: Symbol<unsafe extern "C" fn() -> NamespaceResult> =
library.get(symbol.as_slice()).map_err(
NamespaceError::SymbolErr,
)?;
symbols.insert(identifier, symbol.into_raw());
}
}
Ok(StringNamespace { library, symbols })
}
}
pub fn execute(&self, function: Identifier) -> Result<Option<String>, NamespaceError> {
let func = self.symbols.get(&function).ok_or(
NamespaceError::FunctionMissing(
function.clone(),
),
)?;
unsafe {
match (*func)().into_option() {
None => Ok(None),
Some(cstring) => {
match cstring.to_str() {
Ok(string) => Ok(Some(string.to_owned())),
Err(_) => Err(NamespaceError::UTF8Result),
}
}
}
}
}
}
pub fn collect() -> FnvHashMap<Identifier, StringNamespace> {
let mut hashmap = FnvHashMap::default();
if let Some(mut path) = config_dir() {
path.push("namespaces");
path.push("strings");
match read_dir(&path).map(LibraryIterator::new) {
Ok(iterator) => {
for (identifier, library) in iterator {
match StringNamespace::new(library) {
Ok(namespace) => {
hashmap.insert(identifier, namespace);
}
Err(why) => {
eprintln!("ion: string namespace error: {}", why);
continue;
}
}
}
}
Err(why) => {
eprintln!("ion: unable to read namespaces plugin directory: {}", why);
}
}
}
hashmap
}
use fnv::FnvHashMap;
use std::env;
use std::io::{self, BufRead};
use std::process;
use super::directory_stack::DirectoryStack;
use super::plugins::namespaces::{self, StringNamespace};
use super::status::{FAILURE, SUCCESS};
use app_dirs::{AppDataType, AppInfo, app_root};
use fnv::FnvHashMap;
use liner::Context;
use std::env;
use std::io::{self, BufRead};
use std::process;
use types::{Array, ArrayVariableContext, HashMap, HashMapVariableContext, Identifier, Key, Value, VariableContext};
#[cfg(target_os = "redox")]
......@@ -18,6 +19,10 @@ use sys::getpid;
use sys;
use sys::variables as self_sys;
lazy_static! {
static ref STRING_NAMESPACES: FnvHashMap<Identifier, StringNamespace> = namespaces::collect();
}
#[derive(Debug)]
pub struct Variables {
pub hashmaps: HashMapVariableContext,
......@@ -63,12 +68,9 @@ impl Default for Variables {
);
// Initialize the HOME variable
env::home_dir().map_or_else(
|| env::set_var("HOME", "?"),
|path| {
env::set_var("HOME", path.to_str().unwrap_or("?"))
},
);
env::home_dir().map_or_else(|| env::set_var("HOME", "?"), |path| {
env::set_var("HOME", path.to_str().unwrap_or("?"))
});
Variables {
hashmaps: FnvHashMap::with_capacity_and_hasher(64, Default::default()),
arrays: FnvHashMap::with_capacity_and_hasher(64, Default::default()),
......@@ -143,12 +145,22 @@ impl Variables {
pub fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) }
pub fn get_var(&self, name: &str) -> Option<Value> {
if let Some((namespace, variable)) = name.find("::").map(|pos| (&name[..pos], &name[pos+2..])) {
match namespace {
if let Some((name, variable)) = name.find("::").map(|pos| (&name[..pos], &name[pos + 2..])) {
match name {
"env" => env::var(variable).map(Into::into).ok(),
_ => {
eprintln!("ion: unsupported namespace: '{}'", namespace);
None
if let Some(namespace) = STRING_NAMESPACES.get(name.into()) {
match namespace.execute(variable.into()) {
Ok(value) => value.map(Into::into),
Err(why) => {
eprintln!("ion: string namespace erorr: {}: {}", name, why);
None
}
}
} else {
eprintln!("ion: unsupported namespace: '{}'", name);
None
}
}
}
} else {
......@@ -289,9 +301,7 @@ mod tests {
struct VariableExpander(pub Variables);
impl Expander for VariableExpander {
fn variable(&self, var: &str, _: bool) -> Option<Value> {
self.0.get_var(var)
}
fn variable(&self, var: &str, _: bool) -> Option<Value> { self.0.get_var(var) }
}
#[test]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment