<commit_before>// Copyright 2015 The rust-pcre authors.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate bzip2;
extern crate libc;
extern crate pkg_config;
extern crate tar;

use bzip2::reader::{BzDecompressor};
use std::collections::{BTreeSet};
use std::env;
use std::ffi::{OsString};
use std::fs;
use std::fs::{OpenOptions};
use std::io;
use std::io::{ErrorKind};
use std::iter::{FromIterator};
use std::path::{Path, PathBuf};
use std::process::{Command};
use std::vec::{Vec};
use tar::{Archive};

const BUNDLED_PCRE_VERSION: &'static str = "8.37";

fn main() {
    match pkg_config::Config::new().atleast_version("8.20").find("libpcre") {
        Ok(pkg_config_lib) => {
            for link_pathbuf in pkg_config_lib.link_paths {
                println!("cargo:rustc-link-search=native={}", link_pathbuf.as_path().display());
            }
        },
        Err(_) => {
            let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
            let out_dir = env::var("OUT_DIR").unwrap();

            let ext_pathbuf = Path::new(&cargo_manifest_dir).join("ext");

            let pcre_tbz2_pathbuf = ext_pathbuf.join(format!("pcre-{}.tar.bz2", BUNDLED_PCRE_VERSION));
            let pcre_tbz2_f = OpenOptions::new().read(true).open(pcre_tbz2_pathbuf).unwrap();
            let decompressor = BzDecompressor::new(pcre_tbz2_f);

            let mut archive = Archive::new(decompressor);
            // Keep track of which directory paths have already been created.
            let mut created_paths: BTreeSet<OsString> = BTreeSet::new();
            for file in archive.files_mut().unwrap() {
                let mut file = file.unwrap();
                let filename = String::from(file.filename().unwrap());

                let mut path_parts: Vec<&str> = filename.split('/').collect();
                let filename = path_parts.pop().unwrap();
                let parent_pathbuf = Path::new(&out_dir).join(PathBuf::from_iter(path_parts.iter()));
                if !path_parts.is_empty() {
                    if created_paths.insert(parent_pathbuf.as_os_str().to_os_string()) {
                        if let Err(e) = fs::create_dir_all(&parent_pathbuf) {
                            if e.kind() != ErrorKind::AlreadyExists {
                                panic!("failed to create the {} directory and parents: {}", parent_pathbuf.as_path().display(), e);
                            }
                        }
                    }
                }

                let out_pathbuf = parent_pathbuf.join(&filename);
                if filename.is_empty() {
                    if created_paths.insert(out_pathbuf.as_os_str().to_os_string()) {
                        if let Err(e) = fs::create_dir(&out_pathbuf) {
                            if e.kind() != ErrorKind::AlreadyExists {
                                panic!("failed to create the {} directory: {}", out_pathbuf.as_path().display(), e);
                            }
                        }
                    }
                } else {
                    let mut f = OpenOptions::new().write(true).create(true).open(&out_pathbuf).unwrap();
                    if let Err(e) = io::copy(&mut file, &mut f) {
                        panic!("failed to extract {} to {}: {}", filename, out_pathbuf.as_path().display(), e);
                    }
                }
                if cfg!(unix) {
                    // TODO Use `std::fs::Permissions` when `std::os::unix::fs::PermissionsExt` is stable.
                    use std::ffi::{CString};
                    use std::os::unix::ffi::{OsStringExt};
                    let out_pathbuf_cstring = CString::new(out_pathbuf.as_os_str().to_os_string().into_vec()).unwrap();
                    let mode = file.mode().unwrap();
                    unsafe {
                        libc::chmod(out_pathbuf_cstring.as_ptr(), mode as libc::mode_t);
                    }
                }
            }

            let pcre_pathbuf = Path::new(&out_dir).join(format!("pcre-{}", BUNDLED_PCRE_VERSION));

            if cfg!(unix) {
                let mut cmd = Command::new("autoreconf");
                cmd.current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(ref e) if e.kind() == ErrorKind::NotFound => {
                        panic!("failed to execute `autoreconf`: {}. Are the Autotools installed?", e);
                    },
                    Err(e) => {
                        panic!("failed to execute `autoreconf`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`autoreconf` did not run successfully.");
                }

                let mut cmd = Command::new("./configure");
                cmd.arg("--disable-shared");
                cmd.arg("--disable-cpp");
                cmd.arg("--enable-jit");
                cmd.arg("--enable-utf");
                cmd.arg("--enable-unicode-properties");
                cmd.arg(format!("--prefix={}", Path::new(&out_dir).display()));
                cmd.current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(e) => {
                        panic!("failed to execute `./configure`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`./configure --disable-shared ...` did not run successfully.");
                }

                let mut cmd = Command::new("make");
                cmd.arg("install");
                cmd.current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(ref e) if e.kind() == ErrorKind::NotFound => {
                        panic!("failed to execute `make`: {}. Is GNU Make installed?", e);
                    },
                    Err(e) => {
                        panic!("failed to execute `make`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`make install` did not run successfully.");
                }

                println!("cargo:rustc-link-search=native={}", Path::new(&out_dir).join("lib").as_path().display());
            } else {
                let mut cmd = Command::new("cmake");
                cmd.arg(".");
                cmd.arg("-DBUILD_SHARED_LIBS=OFF");
                cmd.arg("-DPCRE_BUILD_PCRECPP=OFF");
                cmd.arg("-DPCRE_BUILD_PCREGREP=OFF");
                cmd.arg("-DPCRE_BUILD_TESTS=OFF");
                cmd.arg("-DPCRE_BUILD_PCRE8=ON");
                cmd.arg("-DPCRE_SUPPORT_JIT=ON");
                cmd.arg("-DPCRE_SUPPORT_UTF=ON");
                cmd.arg("-DPCRE_SUPPORT_UNICODE_PROPERTIES=ON");
                cmd.current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(ref e) if e.kind() == ErrorKind::NotFound => {
                        panic!("failed to execute `cmake`: {}. Is CMake installed?", e);
                    },
                    Err(e) => {
                        panic!("failed to execute `cmake`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`cmake . -DBUILD_SHARED_LIBS=OFF ...` did not run successfully.");
                }

                let mut cmd = Command::new("cmake");
                cmd.arg("--build").arg(".").current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(ref e) if e.kind() == ErrorKind::NotFound => {
                        panic!("failed to execute `cmake`: {}. Is CMake installed?", e);
                    },
                    Err(e) => {
                        panic!("failed to execute `cmake`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`cmake --build .` did not run successfully.");
                }

                println!("cargo:rustc-link-search=native={}", pcre_pathbuf.as_path().display());
            }
        }
    };
}
<commit_msg>Configure with `--with-pic`<commit_after>// Copyright 2015 The rust-pcre authors.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate bzip2;
extern crate libc;
extern crate pkg_config;
extern crate tar;

use bzip2::reader::{BzDecompressor};
use std::collections::{BTreeSet};
use std::env;
use std::ffi::{OsString};
use std::fs;
use std::fs::{OpenOptions};
use std::io;
use std::io::{ErrorKind};
use std::iter::{FromIterator};
use std::path::{Path, PathBuf};
use std::process::{Command};
use std::vec::{Vec};
use tar::{Archive};

const BUNDLED_PCRE_VERSION: &'static str = "8.37";

fn main() {
    match pkg_config::Config::new().atleast_version("8.20").find("libpcre") {
        Ok(pkg_config_lib) => {
            for link_pathbuf in pkg_config_lib.link_paths {
                println!("cargo:rustc-link-search=native={}", link_pathbuf.as_path().display());
            }
        },
        Err(_) => {
            let cargo_manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
            let out_dir = env::var("OUT_DIR").unwrap();

            let ext_pathbuf = Path::new(&cargo_manifest_dir).join("ext");

            let pcre_tbz2_pathbuf = ext_pathbuf.join(format!("pcre-{}.tar.bz2", BUNDLED_PCRE_VERSION));
            let pcre_tbz2_f = OpenOptions::new().read(true).open(pcre_tbz2_pathbuf).unwrap();
            let decompressor = BzDecompressor::new(pcre_tbz2_f);

            let mut archive = Archive::new(decompressor);
            // Keep track of which directory paths have already been created.
            let mut created_paths: BTreeSet<OsString> = BTreeSet::new();
            for file in archive.files_mut().unwrap() {
                let mut file = file.unwrap();
                let filename = String::from(file.filename().unwrap());

                let mut path_parts: Vec<&str> = filename.split('/').collect();
                let filename = path_parts.pop().unwrap();
                let parent_pathbuf = Path::new(&out_dir).join(PathBuf::from_iter(path_parts.iter()));
                if !path_parts.is_empty() {
                    if created_paths.insert(parent_pathbuf.as_os_str().to_os_string()) {
                        if let Err(e) = fs::create_dir_all(&parent_pathbuf) {
                            if e.kind() != ErrorKind::AlreadyExists {
                                panic!("failed to create the {} directory and parents: {}", parent_pathbuf.as_path().display(), e);
                            }
                        }
                    }
                }

                let out_pathbuf = parent_pathbuf.join(&filename);
                if filename.is_empty() {
                    if created_paths.insert(out_pathbuf.as_os_str().to_os_string()) {
                        if let Err(e) = fs::create_dir(&out_pathbuf) {
                            if e.kind() != ErrorKind::AlreadyExists {
                                panic!("failed to create the {} directory: {}", out_pathbuf.as_path().display(), e);
                            }
                        }
                    }
                } else {
                    let mut f = OpenOptions::new().write(true).create(true).open(&out_pathbuf).unwrap();
                    if let Err(e) = io::copy(&mut file, &mut f) {
                        panic!("failed to extract {} to {}: {}", filename, out_pathbuf.as_path().display(), e);
                    }
                }
                if cfg!(unix) {
                    // TODO Use `std::fs::Permissions` when `std::os::unix::fs::PermissionsExt` is stable.
                    use std::ffi::{CString};
                    use std::os::unix::ffi::{OsStringExt};
                    let out_pathbuf_cstring = CString::new(out_pathbuf.as_os_str().to_os_string().into_vec()).unwrap();
                    let mode = file.mode().unwrap();
                    unsafe {
                        libc::chmod(out_pathbuf_cstring.as_ptr(), mode as libc::mode_t);
                    }
                }
            }

            let pcre_pathbuf = Path::new(&out_dir).join(format!("pcre-{}", BUNDLED_PCRE_VERSION));

            if cfg!(unix) {
                let mut cmd = Command::new("autoreconf");
                cmd.current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(ref e) if e.kind() == ErrorKind::NotFound => {
                        panic!("failed to execute `autoreconf`: {}. Are the Autotools installed?", e);
                    },
                    Err(e) => {
                        panic!("failed to execute `autoreconf`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`autoreconf` did not run successfully.");
                }

                let mut cmd = Command::new("./configure");
                cmd.arg("--with-pic");
                cmd.arg("--disable-shared");
                cmd.arg("--disable-cpp");
                cmd.arg("--enable-jit");
                cmd.arg("--enable-utf");
                cmd.arg("--enable-unicode-properties");
                cmd.arg(format!("--prefix={}", Path::new(&out_dir).display()));
                cmd.current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(e) => {
                        panic!("failed to execute `./configure`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`./configure --with-pic ...` did not run successfully.");
                }

                let mut cmd = Command::new("make");
                cmd.arg("install");
                cmd.current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(ref e) if e.kind() == ErrorKind::NotFound => {
                        panic!("failed to execute `make`: {}. Is GNU Make installed?", e);
                    },
                    Err(e) => {
                        panic!("failed to execute `make`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`make install` did not run successfully.");
                }

                println!("cargo:rustc-link-search=native={}", Path::new(&out_dir).join("lib").as_path().display());
            } else {
                let mut cmd = Command::new("cmake");
                cmd.arg(".");
                cmd.arg("-DBUILD_SHARED_LIBS=OFF");
                cmd.arg("-DPCRE_BUILD_PCRECPP=OFF");
                cmd.arg("-DPCRE_BUILD_PCREGREP=OFF");
                cmd.arg("-DPCRE_BUILD_TESTS=OFF");
                cmd.arg("-DPCRE_BUILD_PCRE8=ON");
                cmd.arg("-DPCRE_SUPPORT_JIT=ON");
                cmd.arg("-DPCRE_SUPPORT_UTF=ON");
                cmd.arg("-DPCRE_SUPPORT_UNICODE_PROPERTIES=ON");
                cmd.current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(ref e) if e.kind() == ErrorKind::NotFound => {
                        panic!("failed to execute `cmake`: {}. Is CMake installed?", e);
                    },
                    Err(e) => {
                        panic!("failed to execute `cmake`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`cmake . -DBUILD_SHARED_LIBS=OFF ...` did not run successfully.");
                }

                let mut cmd = Command::new("cmake");
                cmd.arg("--build").arg(".").current_dir(&pcre_pathbuf);
                let status = match cmd.status() {
                    Err(ref e) if e.kind() == ErrorKind::NotFound => {
                        panic!("failed to execute `cmake`: {}. Is CMake installed?", e);
                    },
                    Err(e) => {
                        panic!("failed to execute `cmake`: {}", e);
                    },
                    Ok(status) => status
                };
                if !status.success() {
                    panic!("`cmake --build .` did not run successfully.");
                }

                println!("cargo:rustc-link-search=native={}", pcre_pathbuf.as_path().display());
            }
        }
    };
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An implementation of `RocksDB` database.
use profiler;

use rocksdb::TransactionDB as _RocksDB;
use rocksdb::DBRawIterator;
use rocksdb::Snapshot as _Snapshot;
use rocksdb::Error as _Error;

use std::mem;
use std::sync::Arc;
use std::path::Path;
use std::fmt;
use std::error;

pub use rocksdb::Options as RocksDBOptions;
pub use rocksdb::BlockBasedOptions as RocksBlockOptions;
pub use rocksdb::{TransactionDBOptions, TransactionOptions, WriteOptions};

use super::{Database, Iterator, Iter, Snapshot, Error, Patch, Change, Result};

impl From<_Error> for Error {
    fn from(err: _Error) -> Self {
        Error::new(error::Error::description(&err))
    }
}

/// Database implementation on the top of `RocksDB` backend.
#[derive(Clone)]
pub struct RocksDB {
    db: Arc<_RocksDB>,
}

/// A snapshot of a `RocksDB`.
pub struct RocksDBSnapshot {
    _db: Arc<_RocksDB>,
    snapshot: _Snapshot<'static>,
}

/// An iterator over the entries of a `RocksDB`.
struct RocksDBIterator {
    iter: DBRawIterator,
    key: Option<Vec<u8>>,
    value: Option<Vec<u8>>,
}

impl RocksDB {
    /// Open a database stored in the specified path with the specified options.
    pub fn open(path: &Path, options: RocksDBOptions) -> Result<RocksDB> {
        let txn_db_options = TransactionDBOptions::default();
        let database = _RocksDB::open(&options, &txn_db_options, path)?;
        Ok(RocksDB { db: Arc::new(database) })
    }
}

impl Database for RocksDB {
    fn clone(&self) -> Box<Database> {
        Box::new(Clone::clone(self))
    }

    fn snapshot(&self) -> Box<Snapshot> {
        let _p = profiler::ProfilerSpan::new("RocksDB::snapshot");
        Box::new(RocksDBSnapshot {
            _db: self.db.clone(),
            snapshot: unsafe { mem::transmute(self.db.snapshot()) },
        })
    }

    fn merge(&mut self, patch: Patch) -> Result<()> {
        let _p = profiler::ProfilerSpan::new("RocksDB::merge");
        let w_opts = WriteOptions::default();
        let txn_opts = TransactionOptions::default();
        let txn = self.db.transaction_begin(&w_opts, &txn_opts);
        for (key, change) in patch {
            match change {
                Change::Put(ref value) => txn.put(&key, value)?,
                Change::Delete => txn.delete(&key)?,
            }
        }
        txn.commit().map_err(Into::into)
    }
}

impl Snapshot for RocksDBSnapshot {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        let _p = profiler::ProfilerSpan::new("RocksDBSnapshot::get");
        match self.snapshot.get(key) {
            Ok(value) => value.map(|v| v.to_vec()),
            Err(e) => panic!(e),
        }
    }

    fn iter<'a>(&'a self, from: &[u8]) -> Iter<'a> {
        let _p = profiler::ProfilerSpan::new("RocksDBSnapshot::iter");
        let mut iter = self.snapshot.raw_iterator();
        iter.seek(from);
        Box::new(RocksDBIterator {
            iter: iter,
            key: None,
            value: None,
        })
    }
}

impl<'a> Iterator for RocksDBIterator {
    fn next(&mut self) -> Option<(&[u8], &[u8])> {
        let _p = profiler::ProfilerSpan::new("RocksDBIterator::next");
        let result = if self.iter.valid() {
            self.key = Some(unsafe { self.iter.key_inner().unwrap().to_vec() });
            self.value = Some(unsafe { self.iter.value_inner().unwrap().to_vec() });
            Some((
                self.key.as_ref().unwrap().as_ref(),
                self.value.as_ref().unwrap().as_ref(),
            ))
        } else {
            None
        };

        if result.is_some() {
            self.iter.next();
        }

        result
    }

    fn peek(&mut self) -> Option<(&[u8], &[u8])> {
        let _p = profiler::ProfilerSpan::new("RocksDBIterator::peek");
        if self.iter.valid() {
            self.key = Some(unsafe { self.iter.key_inner().unwrap().to_vec() });
            self.value = Some(unsafe { self.iter.value_inner().unwrap().to_vec() });
            Some((
                self.key.as_ref().unwrap().as_ref(),
                self.value.as_ref().unwrap().as_ref(),
            ))
        } else {
            None
        }
    }
}

impl fmt::Debug for RocksDB {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RocksDB(..)")
    }
}

impl fmt::Debug for RocksDBSnapshot {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RocksDBSnapshot(..)")
    }
}
<commit_msg>Changed fields order to stabilize drop order in rocksdb module<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An implementation of `RocksDB` database.
use profiler;

use rocksdb::TransactionDB as _RocksDB;
use rocksdb::DBRawIterator;
use rocksdb::Snapshot as _Snapshot;
use rocksdb::Error as _Error;

use std::mem;
use std::sync::Arc;
use std::path::Path;
use std::fmt;
use std::error;

pub use rocksdb::Options as RocksDBOptions;
pub use rocksdb::BlockBasedOptions as RocksBlockOptions;
pub use rocksdb::{TransactionDBOptions, TransactionOptions, WriteOptions};

use super::{Database, Iterator, Iter, Snapshot, Error, Patch, Change, Result};

impl From<_Error> for Error {
    fn from(err: _Error) -> Self {
        Error::new(error::Error::description(&err))
    }
}

/// Database implementation on the top of `RocksDB` backend.
#[derive(Clone)]
pub struct RocksDB {
    db: Arc<_RocksDB>,
}

/// A snapshot of a `RocksDB`.
pub struct RocksDBSnapshot {
    snapshot: _Snapshot<'static>,
    _db: Arc<_RocksDB>,
}

/// An iterator over the entries of a `RocksDB`.
struct RocksDBIterator {
    iter: DBRawIterator,
    key: Option<Vec<u8>>,
    value: Option<Vec<u8>>,
}

impl RocksDB {
    /// Open a database stored in the specified path with the specified options.
    pub fn open(path: &Path, options: RocksDBOptions) -> Result<RocksDB> {
        let txn_db_options = TransactionDBOptions::default();
        let database = _RocksDB::open(&options, &txn_db_options, path)?;
        Ok(RocksDB { db: Arc::new(database) })
    }
}

impl Database for RocksDB {
    fn clone(&self) -> Box<Database> {
        Box::new(Clone::clone(self))
    }

    fn snapshot(&self) -> Box<Snapshot> {
        let _p = profiler::ProfilerSpan::new("RocksDB::snapshot");
        Box::new(RocksDBSnapshot {
            snapshot: unsafe { mem::transmute(self.db.snapshot()) },
            _db: self.db.clone(),
        })
    }

    fn merge(&mut self, patch: Patch) -> Result<()> {
        let _p = profiler::ProfilerSpan::new("RocksDB::merge");
        let w_opts = WriteOptions::default();
        let txn_opts = TransactionOptions::default();
        let txn = self.db.transaction_begin(&w_opts, &txn_opts);
        for (key, change) in patch {
            match change {
                Change::Put(ref value) => txn.put(&key, value)?,
                Change::Delete => txn.delete(&key)?,
            }
        }
        txn.commit().map_err(Into::into)
    }
}

impl Snapshot for RocksDBSnapshot {
    fn get(&self, key: &[u8]) -> Option<Vec<u8>> {
        let _p = profiler::ProfilerSpan::new("RocksDBSnapshot::get");
        match self.snapshot.get(key) {
            Ok(value) => value.map(|v| v.to_vec()),
            Err(e) => panic!(e),
        }
    }

    fn iter<'a>(&'a self, from: &[u8]) -> Iter<'a> {
        let _p = profiler::ProfilerSpan::new("RocksDBSnapshot::iter");
        let mut iter = self.snapshot.raw_iterator();
        iter.seek(from);
        Box::new(RocksDBIterator {
            iter: iter,
            key: None,
            value: None,
        })
    }
}

impl<'a> Iterator for RocksDBIterator {
    fn next(&mut self) -> Option<(&[u8], &[u8])> {
        let _p = profiler::ProfilerSpan::new("RocksDBIterator::next");
        let result = if self.iter.valid() {
            self.key = Some(unsafe { self.iter.key_inner().unwrap().to_vec() });
            self.value = Some(unsafe { self.iter.value_inner().unwrap().to_vec() });
            Some((
                self.key.as_ref().unwrap().as_ref(),
                self.value.as_ref().unwrap().as_ref(),
            ))
        } else {
            None
        };

        if result.is_some() {
            self.iter.next();
        }

        result
    }

    fn peek(&mut self) -> Option<(&[u8], &[u8])> {
        let _p = profiler::ProfilerSpan::new("RocksDBIterator::peek");
        if self.iter.valid() {
            self.key = Some(unsafe { self.iter.key_inner().unwrap().to_vec() });
            self.value = Some(unsafe { self.iter.value_inner().unwrap().to_vec() });
            Some((
                self.key.as_ref().unwrap().as_ref(),
                self.value.as_ref().unwrap().as_ref(),
            ))
        } else {
            None
        }
    }
}

impl fmt::Debug for RocksDB {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RocksDB(..)")
    }
}

impl fmt::Debug for RocksDBSnapshot {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RocksDBSnapshot(..)")
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Strahinja Val Markovic
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fmt;
use std::str;
use std::fmt::{Result};
pub use self::NodeContents::{Data, Children};

static NO_NAME : &'static str = "<none>";

pub struct PreOrderNodes<'a, 'b:'a> {
  queue: Vec<&'a Node<'b>>
}

impl<'a, 'b:'a> Iterator for PreOrderNodes<'a, 'b> {
  type Item = &'a Node<'b>;

  fn next( &mut self ) -> Option<&'a Node<'b>> {
    match self.queue.pop() {
      Some( node ) => {
        match node.contents {
          Children( ref x ) => {
            for child in x.iter().rev() {
              self.queue.push( child )
            }
          }
          _ => ()
        };
        Some( node )
      }
      _ => None
    }
  }
}

#[derive(Debug, PartialEq)]
pub enum NodeContents<'a> {
  /// A `&[u8]` byte slice this node matched in the parse input. Only leaf nodes
  /// have `Data` contents.
  Data( &'a [u8] ),

  /// Children of the node, if any. Only non-leaf nodes have `Children`
  /// contents.
  Children( Vec<Node<'a>> )
}

#[derive(PartialEq)]
pub struct Node<'a> {
  /// The name of the node.
  pub name: &'static str,

  /// The (inclusive) start index of the range this node matches. It's the byte
  /// (NOT char) offset of the parse input.
  pub start: usize,

  /// The (exclusive) end index of the range this node matches. It's the byte
  /// (NOT char) offset of the parse input.
  pub end: usize,

  /// The contents of the node; this can be either children nodes or a matched
  /// `&[u8]` slice.
  pub contents: NodeContents<'a>
}

fn indent( formatter: &mut fmt::Formatter, indent_spaces: u32 )
    -> fmt::Result {
  for _ in 0 .. indent_spaces {
    try!( write!( formatter, " " ) )
  }
  Ok(())
}

impl<'a> Node<'a> {
  fn format( &self, formatter: &mut fmt::Formatter, indent_spaces: u32 )
      -> fmt::Result {
    try!( indent( formatter, indent_spaces ) );
    try!( write!( formatter,
                  "{0:?} [{1:?}, {2:?}>",
                  self.displayName(), self.start, self.end ) );

    match self.contents {
      Data( data ) => {
        match str::from_utf8( data ) {
          Ok( string ) => {
            try!( writeln!( formatter,
                            ": \"{0:?}\"",
                            string ) );
          }
          _ => {
            try!( writeln!( formatter,
                            ": \"{0:?}\"",
                            data ) );
          }
        }
      }
      Children( ref children ) => {
        try!( writeln!( formatter, "" ) );
        for child in children.iter() {
          try!( child.format( formatter, indent_spaces + 1) )
        }
      }
    };

    Ok(())
  }

  /// The node name if set, or "<none>" if unset.
  pub fn displayName( &self ) -> &'static str {
    if !self.name.is_empty() {
      self.name
    } else {
      NO_NAME
    }
  }

  /// Creates a `Node` with an empty name.
  pub fn withoutName( start: usize, end: usize, contents: NodeContents<'a> )
      -> Node<'a> {
    Node { name: "", start: start, end: end, contents: contents }
  }

  /// Creates a `Node` with the provided `name` and makes it a parent of the
  /// provided `children`.
  pub fn withChildren( name: &'static str, mut children: Vec<Node<'a>> )
      -> Node<'a> {
    // In case 'children' has only one node with an empty name, our new Node
    // will take the guts of the child with the new 'name'.
    if children.len() == 1 && children[ 0 ].name.is_empty() {
      match children.pop() {
        Some( mut child ) => {
          child.name = name;
          return child;
        }
        _ => ()
      }
    }

    let start = if children.len() != 0 {
      children[ 0 ].start
    } else {
      0
    };

    let end = match children.last() {
      Some( ref node ) => node.end,
      _ => 0
    };

    Node { name: name,
           start: start,
           end: end,
           contents: Children( children ) }
  }

  /// Traverses the tree rooted at the node with pre-order traversal. Includes
  /// the `self` node as the first node.
  #[allow(dead_code)]
  pub fn preOrder<'b>( &'b self ) -> PreOrderNodes<'b, 'a> {
    PreOrderNodes { queue: vec!( self ) }
  }

  /// Concatenates and returns all `&[u8]` data in the leaf nodes beneath
  /// the current node.
  #[allow(dead_code)]
  pub fn matchedData( &self ) -> Vec<u8> {
    match self.contents {
      Data( x ) => x.to_vec(),
      Children( ref children ) => {
        let mut out : Vec<u8> = vec!();
        for child in children.iter() {
          out.extend( child.matchedData() );
        }
        out
      }
    }
  }

  // TODO: methods in_order/pre_order/post_order that yield
  // iterators for walking the node tree structure
}

impl<'a> fmt::Debug for Node<'a> {
  fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result {
    self.format( formatter, 0 )
  }
}

#[cfg(test)]
mod tests {
  use super::{Node, Data};

  fn nameOnly( name: &'static str ) -> Node {
    Node { name: name, start: 0, end: 0, contents: Data( b"" ) }
  }

  fn contentsOnly( contents: &'static [u8] ) -> Node {
    Node { name: "", start: 0, end: 0, contents: Data( contents ) }
  }

  fn testTree() -> Node<'static> {
    // Tree looks like the following:
    //        a
    //   b    c    d
    //  e f   g
    Node::withChildren( "a", vec!(
        Node::withChildren( "b", vec!( nameOnly( "e" ), nameOnly( "f" ) ) ),
        Node::withChildren( "c", vec!( nameOnly( "g" ) ) ),
        nameOnly( "d" ) ) )
  }

  fn testTreeWithContents() -> Node<'static> {
    // Tree looks like the following (nodes with ' have contents):
    //          a
    //    b     c     'd
    // 'e  'f  'g
    Node::withChildren( "a", vec!(
        Node::withChildren(
          "b", vec!( contentsOnly( b"e" ), contentsOnly( b"f" ) ) ),
        Node::withChildren( "c", vec!( contentsOnly( b"g" ) ) ),
        contentsOnly( b"d" ) ) )
  }

  #[test]
  fn preOrder_FullIteration() {
    let root = testTree();
    let names =
      root.preOrder().map( |x| x.name ).collect::<Vec<_>>();
    assert_eq!( names, vec!( "a", "b", "e", "f", "c", "g", "d" ) )
  }

  #[test]
  fn matchedData_FullTree() {
    let root = testTreeWithContents();
    assert_eq!( b"efgd", &root.matchedData() )
  }
}
<commit_msg>Workaround for rust-lang/rust#22649<commit_after>// Copyright 2014 Strahinja Val Markovic
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fmt;
use std::str;
use std::fmt::{Result};
pub use self::NodeContents::{Data, Children};

static NO_NAME : &'static str = "<none>";

pub struct PreOrderNodes<'a, 'b:'a> {
  queue: Vec<&'a Node<'b>>
}

impl<'a, 'b:'a> Iterator for PreOrderNodes<'a, 'b> {
  type Item = &'a Node<'b>;

  fn next( &mut self ) -> Option<&'a Node<'b>> {
    match self.queue.pop() {
      Some( node ) => {
        match node.contents {
          Children( ref x ) => {
            for child in x.iter().rev() {
              self.queue.push( child )
            }
          }
          _ => ()
        };
        Some( node )
      }
      _ => None
    }
  }
}

#[derive(Debug, PartialEq)]
pub enum NodeContents<'a> {
  /// A `&[u8]` byte slice this node matched in the parse input. Only leaf nodes
  /// have `Data` contents.
  Data( &'a [u8] ),

  /// Children of the node, if any. Only non-leaf nodes have `Children`
  /// contents.
  Children( Vec<Node<'a>> )
}

#[derive(PartialEq)]
pub struct Node<'a> {
  /// The name of the node.
  pub name: &'static str,

  /// The (inclusive) start index of the range this node matches. It's the byte
  /// (NOT char) offset of the parse input.
  pub start: usize,

  /// The (exclusive) end index of the range this node matches. It's the byte
  /// (NOT char) offset of the parse input.
  pub end: usize,

  /// The contents of the node; this can be either children nodes or a matched
  /// `&[u8]` slice.
  pub contents: NodeContents<'a>
}

fn indent( formatter: &mut fmt::Formatter, indent_spaces: u32 )
    -> fmt::Result {
  for _ in 0 .. indent_spaces {
    try!( write!( formatter, " " ) )
  }
  Ok(())
}

impl<'a> Node<'a> {
  fn format( &self, formatter: &mut fmt::Formatter, indent_spaces: u32 )
      -> fmt::Result {
    try!( indent( formatter, indent_spaces ) );
    try!( write!( formatter,
                  "{0:?} [{1:?}, {2:?}>",
                  self.displayName(), self.start, self.end ) );

    match self.contents {
      Data( data ) => {
        match str::from_utf8( data ) {
          Ok( string ) => {
            try!( writeln!( formatter,
                            ": \"{0:?}\"",
                            string ) );
          }
          _ => {
            try!( writeln!( formatter,
                            ": \"{0:?}\"",
                            data ) );
          }
        }
      }
      Children( ref children ) => {
        try!( writeln!( formatter, "" ) );
        for child in children.iter() {
          try!( child.format( formatter, indent_spaces + 1) )
        }
      }
    };

    Ok(())
  }

  /// The node name if set, or "<none>" if unset.
  pub fn displayName( &self ) -> &'static str {
    if !self.name.is_empty() {
      self.name
    } else {
      NO_NAME
    }
  }

  /// Creates a `Node` with an empty name.
  pub fn withoutName( start: usize, end: usize, contents: NodeContents<'a> )
      -> Node<'a> {
    Node { name: "", start: start, end: end, contents: contents }
  }

  /// Creates a `Node` with the provided `name` and makes it a parent of the
  /// provided `children`.
  pub fn withChildren( name: &'static str, mut children: Vec<Node<'a>> )
      -> Node<'a> {
    // In case 'children' has only one node with an empty name, our new Node
    // will take the guts of the child with the new 'name'.
    if children.len() == 1 && children[ 0 ].name.is_empty() {
      match children.pop() {
        Some( mut child ) => {
          child.name = name;
          return child;
        }
        _ => ()
      }
    }

    let start = if children.len() != 0 {
      children[ 0 ].start
    } else {
      0
    };

    let end = match children.last() {
      Some( ref node ) => node.end,
      _ => 0
    };

    Node { name: name,
           start: start,
           end: end,
           contents: Children( children ) }
  }

  /// Traverses the tree rooted at the node with pre-order traversal. Includes
  /// the `self` node as the first node.
  #[allow(dead_code)]
  pub fn preOrder<'b>( &'b self ) -> PreOrderNodes<'b, 'a> {
    PreOrderNodes { queue: vec!( self ) }
  }

  /// Concatenates and returns all `&[u8]` data in the leaf nodes beneath
  /// the current node.
  #[allow(dead_code)]
  pub fn matchedData( &self ) -> Vec<u8> {
    match self.contents {
      Data( x ) => x.to_vec(),
      Children( ref children ) => {
        let mut out : Vec<u8> = vec!();
        for child in children.iter() {
          out.extend( child.matchedData() );
        }
        out
      }
    }
  }

  // TODO: methods in_order/pre_order/post_order that yield
  // iterators for walking the node tree structure
}

impl<'a> fmt::Debug for Node<'a> {
  fn fmt( &self, formatter: &mut fmt::Formatter ) -> fmt::Result {
    self.format( formatter, 0 )
  }
}

#[cfg(test)]
mod tests {
  use super::{Node, Data};

  fn nameOnly( name: &'static str ) -> Node {
    Node { name: name, start: 0, end: 0, contents: Data( b"" ) }
  }

  fn contentsOnly( contents: &'static [u8] ) -> Node {
    Node { name: "", start: 0, end: 0, contents: Data( contents ) }
  }

  fn testTree() -> Node<'static> {
    // Tree looks like the following:
    //        a
    //   b    c    d
    //  e f   g
    Node::withChildren( "a", vec!(
        Node::withChildren( "b", vec!( nameOnly( "e" ), nameOnly( "f" ) ) ),
        Node::withChildren( "c", vec!( nameOnly( "g" ) ) ),
        nameOnly( "d" ) ) )
  }

  fn testTreeWithContents() -> Node<'static> {
    // Tree looks like the following (nodes with ' have contents):
    //          a
    //    b     c     'd
    // 'e  'f  'g
    Node::withChildren( "a", vec!(
        Node::withChildren(
          "b", vec!( contentsOnly( b"e" ), contentsOnly( b"f" ) ) ),
        Node::withChildren( "c", vec!( contentsOnly( b"g" ) ) ),
        contentsOnly( b"d" ) ) )
  }

  #[test]
  fn preOrder_FullIteration() {
    let root = testTree();
    let names =
      root.preOrder().map( |x| x.name ).collect::<Vec<_>>();
    assert_eq!( names, vec!( "a", "b", "e", "f", "c", "g", "d" ) )
  }

  #[test]
  fn matchedData_FullTree() {
    let root = testTreeWithContents();
    // TODO: drop "[..]" when this bug is fixed:
    //   https://github.com/rust-lang/rust/issues/22649
    assert_eq!( b"efgd", &root.matchedData()[..] )
  }
}
<|endoftext|>


<commit_before>extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate getopts;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate varlink;

use failure::Fail;
use org_example_ping::*;
use std::collections::HashMap;
use std::env;
use std::io::{self, BufRead, BufReader, Error, Read, Write};
use std::process::exit;
use std::sync::{Arc, RwLock};
use std::thread;
use varlink::{Call, Connection, ConnectionHandler, Listener, ServerStream, VarlinkService};

// Dynamically build the varlink rust code.
mod org_example_ping;

#[cfg(test)]
mod test;

// Main

fn print_usage(program: &str, opts: &getopts::Options) {
    let brief = format!("Usage: {} [--varlink=<address>] [--client]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();

    let mut opts = getopts::Options::new();
    opts.optopt("", "varlink", "varlink address URL", "<address>");
    opts.optflag("", "client", "run in client mode");
    opts.optflag("m", "multiplex", "run in multiplex mode");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            eprintln!("{}", f.to_string());
            print_usage(&program, &opts);
            return;
        }
    };

    if matches.opt_present("h") {
        print_usage(&program, &opts);
        return;
    }

    let client_mode = matches.opt_present("client");

    let ret = if client_mode {
        let connection = match matches.opt_str("varlink") {
            None => Connection::with_activate(&format!("{} --varlink=$VARLINK_ADDRESS", program))
                .unwrap(),
            Some(address) => Connection::with_address(&address).unwrap(),
        };
        run_client(&connection)
    } else if let Some(address) = matches.opt_str("varlink") {
        run_server(&address, 0, matches.opt_present("m")).map_err(|e| e.into())
    } else {
        print_usage(&program, &opts);
        eprintln!("Need varlink address in server mode.");
        exit(1);
    };
    exit(match ret {
        Ok(_) => 0,
        Err(err) => {
            eprintln!("error: {:?}", err);
            1
        }
    });
}

// Client

fn run_client(connection: &Arc<RwLock<varlink::Connection>>) -> Result<()> {
    {
        let mut iface = VarlinkClient::new(connection.clone());
        let ping = String::from("Test");

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let _reply = iface.upgrade().upgrade()?;
        println!("Client: upgrade()");
    }
    {
        // serve upgraded connection
        let mut conn = connection.write().unwrap();
        let mut writer = conn.writer.take().unwrap();
        writer.write_all(b"test test\nEnd\n")?;
        conn.writer = Some(writer);
        let mut buf = Vec::new();
        let mut reader = conn.reader.take().unwrap();
        if reader.read_until(b'\n', &mut buf)? == 0 {
            // incomplete data, in real life, store all bytes for the next call
            // for now just read the rest
            reader.read_to_end(&mut buf)?;
        };
        eprintln!("Client: upgraded got: {}", String::from_utf8_lossy(&buf));
        conn.reader = Some(reader);
    }
    Ok(())
}

// Server

struct MyOrgExamplePing;

impl org_example_ping::VarlinkInterface for MyOrgExamplePing {
    fn ping(&self, call: &mut Call_Ping, ping: String) -> varlink::Result<()> {
        call.reply(ping)
    }

    fn upgrade(&self, call: &mut Call_Upgrade) -> varlink::Result<()> {
        eprintln!("Server: called upgrade");
        call.to_upgraded();
        call.reply()
    }

    // An upgraded connection has its own application specific protocol.
    // Normally, there is no way back to the varlink protocol with this connection.
    fn call_upgraded(&self, call: &mut Call, bufreader: &mut BufRead) -> varlink::Result<Vec<u8>> {
        loop {
            let mut buf = String::new();
            let len = bufreader.read_line(&mut buf)?;
            if len == 0 {
                eprintln!("Server: upgraded got: none");
                // incomplete data, in real life, store all bytes for the next call
                // return Ok(buf.as_bytes().to_vec());
                return Err(varlink::ErrorKind::ConnectionClosed.into());
            }
            eprintln!("Server: upgraded got: {}", buf);

            call.writer.write_all(b"server reply: ")?;
            call.writer.write_all(buf.as_bytes())?;
            if buf.eq("End\n") {
                break;
            }
        }
        eprintln!("Server: upgraded ending");
        Ok(Vec::new())
    }
}

struct FdTracker {
    stream: Option<ServerStream>,
    buffer: Option<Vec<u8>>,
}

impl FdTracker {
    fn shutdown(&mut self) -> varlink::Result<()> {
        self.stream.as_mut().unwrap().shutdown()
    }
    fn chain_buffer(&mut self, buf: &mut Vec<u8>) {
        self.buffer.as_mut().unwrap().append(buf);
    }
    fn fill_buffer(&mut self, buf: &Vec<u8>) {
        self.buffer.as_mut().unwrap().clone_from(buf);
    }
    fn buf_as_slice(&mut self) -> &[u8] {
        self.buffer.as_mut().unwrap().as_slice()
    }
    fn write(&mut self, out: &[u8]) -> io::Result<usize> {
        self.stream.as_mut().unwrap().write(out)
    }
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.stream.as_mut().unwrap().read(buf)
    }
}

// listen_multiplex is just an example, if you don't want to use varlink::listen()
// and how to build your own main-loop and use the low-level varlink::handle() instead
pub fn listen_multiplex<S: ?Sized + AsRef<str>, H: ::ConnectionHandler + Send + Sync + 'static>(
    handler: H,
    address: &S,
    idle_timeout: u64,
) -> varlink::Result<()> {
    let timeout: i32 = match idle_timeout {
        0 => -1,
        n => (n * 1000) as i32,
    };

    let handler = Arc::new(handler);
    let mut fdmap: HashMap<i32, FdTracker> = HashMap::new();
    let mut fds = Vec::new();
    let mut threads = Vec::new();
    let listener = Listener::new(address)?;
    let upgraded_in_use = Arc::new(RwLock::new(0));

    listener.set_nonblocking(true)?;

    fds.push(libc::pollfd {
        fd: listener.as_raw_fd(),
        revents: 0,
        events: libc::POLLIN,
    });

    loop {
        // Read activity on listening socket
        if fds[0].revents != 0 {
            let mut client = listener.accept(0)?;

            client.set_nonblocking(true)?;

            let fd = client.as_raw_fd();
            fds.push(libc::pollfd {
                fd,
                revents: 0,
                events: libc::POLLIN,
            });

            fdmap.insert(
                fd,
                FdTracker {
                    stream: Some(client),
                    buffer: Some(Vec::new()),
                },
            );
        }

        // Store which indices to remove
        let mut indices_to_remove = vec![];

        // Check client connections ...
        for (i, fds_item) in fds.iter().enumerate().skip(1) {
            if fds_item.revents != 0 {
                let mut upgraded_iface: Option<String> = None;
                let mut tracker = fdmap.get_mut(&fds_item.fd).unwrap();
                loop {
                    let mut readbuf: [u8; 8192] = [0; 8192];

                    match tracker.read(&mut readbuf) {
                        Ok(0) => {
                            let _ = tracker.shutdown();
                            indices_to_remove.push(i);
                            break;
                        }
                        Ok(len) => {
                            let mut out: Vec<u8> = Vec::new();
                            tracker.chain_buffer(&mut readbuf[0..len].to_vec());
                            eprintln!(
                                "Handling: {}",
                                String::from_utf8_lossy(&tracker.buf_as_slice())
                            );

                            match handler.handle(&mut tracker.buf_as_slice(), &mut out, None) {
                                // TODO: buffer output and write only on POLLOUT
                                Ok((unprocessed_bytes, last_iface)) => {
                                    upgraded_iface = last_iface;
                                    if !unprocessed_bytes.is_empty() {
                                        eprintln!(
                                            "Unprocessed bytes: {}",
                                            String::from_utf8_lossy(&unprocessed_bytes)
                                        );
                                    }
                                    tracker.fill_buffer(&unprocessed_bytes);

                                    if let Err(err) = tracker.write(out.as_ref()) {
                                        eprintln!("write error: {}", err);
                                        for cause in Fail::iter_causes(&err) {
                                            eprintln!("  caused by: {}", cause);
                                        }
                                        let _ = tracker.shutdown();
                                        indices_to_remove.push(i);
                                        break;
                                    }
                                }
                                Err(e) => match e.kind() {
                                    err => {
                                        eprintln!("handler error: {}", err);
                                        for cause in Fail::iter_causes(&err).skip(1) {
                                            eprintln!("  caused by: {}", cause);
                                        }
                                        let _ = tracker.shutdown();
                                        indices_to_remove.push(i);
                                        break;
                                    }
                                },
                            }
                        }
                        Err(e) => match e.kind() {
                            io::ErrorKind::WouldBlock => {
                                break;
                            }
                            _ => {
                                let _ = tracker.shutdown();
                                indices_to_remove.push(i);
                                eprintln!("IO error: {}", e);
                                break;
                            }
                        },
                    }
                }
                if upgraded_iface.is_some() {
                    eprintln!("Upgraded MODE");
                    // upgraded mode... thread away the server
                    // feed it directly with the client stream
                    // If you have a better idea, open an Issue or PR on github
                    indices_to_remove.push(i);

                    let j = thread::spawn({
                        eprintln!("upgraded thread");
                        let handler = handler.clone();
                        let mut stream = tracker.stream.take().unwrap();
                        let mut buffer = tracker.buffer.take().unwrap();
                        let mut upgraded_in_use = upgraded_in_use.clone();
                        move || {
                            let _r = stream.set_nonblocking(false);
                            let (reader, mut writer) = stream.split().unwrap();
                            let br = BufReader::new(reader);
                            let mut bufreader = Box::new(buffer.chain(br));
                            let mut upgraded_iface = upgraded_iface.take();

                            {
                                let mut ctr = upgraded_in_use.write().unwrap();
                                *ctr += 1;
                            }
                            loop {
                                match handler.handle(&mut bufreader, &mut writer, upgraded_iface) {
                                    Ok((unread, iface)) => {
                                        upgraded_iface = iface;
                                        match bufreader.fill_buf() {
                                            Err(_) => {
                                                eprintln!("Upgraded end");
                                                break;
                                            }
                                            Ok(buf) if buf.is_empty() && unread.is_empty() => {
                                                eprintln!("Upgraded end");
                                                break;
                                            }
                                            _ => {
                                                if !unread.is_empty() {
                                                    eprintln!(
                                                        "Unhandled bytes: {}",
                                                        String::from_utf8_lossy(&unread)
                                                    );
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                    Err(err) => match err.kind() {
                                        varlink::ErrorKind::ConnectionClosed => {
                                            eprintln!("Upgraded end");
                                            break;
                                        }
                                        _ => {
                                            eprintln!("Upgraded end: {}", err);
                                            for cause in Fail::iter_causes(&err).skip(1) {
                                                eprintln!("  caused by: {}", cause);
                                            }
                                            break;
                                        }
                                    },
                                }
                            }
                            {
                                let mut ctr = upgraded_in_use.write().unwrap();
                                *ctr -= 1;
                            }
                        }
                    });
                    threads.push(j);
                }
            }
        }

        // We can't modify the vector while we are traversing it, so update now.
        for i in indices_to_remove {
            fdmap.remove(&fds[i].fd);
            fds.remove(i);
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), (fds.len() as u32).into(), timeout) };

        if r < 0 {
            for t in threads {
                let _r = t.join();
            }
            return Err(Error::last_os_error().into());
        }

        if r == 0 && fds.len() == 1 && *upgraded_in_use.read().unwrap() == 0 {
            eprintln!("listen_multiplex: Waiting for threads to end.");
            for t in threads {
                let _r = t.join();
            }

            return Err(varlink::Error::from(varlink::ErrorKind::Timeout));
        }
    }
}

fn run_server(address: &str, timeout: u64, multiplex: bool) -> varlink::Result<()> {
    let myorgexampleping = MyOrgExamplePing;
    let myinterface = org_example_ping::new(Box::new(myorgexampleping));
    let service = VarlinkService::new(
        "org.varlink",
        "test ping service",
        "0.1",
        "http://varlink.org",
        vec![Box::new(myinterface)],
    );

    if multiplex {
        // Demonstrate a single process, single-threaded service
        listen_multiplex(service, &address, timeout)?;
    } else {
        varlink::listen(service, &address, 1, 10, timeout)?;
    }
    Ok(())
}
<commit_msg>examples/ping: more debug output for listen_multiplex() in upgraded path<commit_after>extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate getopts;
extern crate libc;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate varlink;

use failure::Fail;
use org_example_ping::*;
use std::collections::HashMap;
use std::env;
use std::io::{self, BufRead, BufReader, Error, Read, Write};
use std::process::exit;
use std::sync::{Arc, RwLock};
use std::thread;
use varlink::{Call, Connection, ConnectionHandler, Listener, ServerStream, VarlinkService};

// Dynamically build the varlink rust code.
mod org_example_ping;

#[cfg(test)]
mod test;

// Main

fn print_usage(program: &str, opts: &getopts::Options) {
    let brief = format!("Usage: {} [--varlink=<address>] [--client]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();

    let mut opts = getopts::Options::new();
    opts.optopt("", "varlink", "varlink address URL", "<address>");
    opts.optflag("", "client", "run in client mode");
    opts.optflag("m", "multiplex", "run in multiplex mode");
    opts.optflag("h", "help", "print this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            eprintln!("{}", f.to_string());
            print_usage(&program, &opts);
            return;
        }
    };

    if matches.opt_present("h") {
        print_usage(&program, &opts);
        return;
    }

    let client_mode = matches.opt_present("client");

    let ret = if client_mode {
        let connection = match matches.opt_str("varlink") {
            None => Connection::with_activate(&format!("{} --varlink=$VARLINK_ADDRESS", program))
                .unwrap(),
            Some(address) => Connection::with_address(&address).unwrap(),
        };
        run_client(&connection)
    } else if let Some(address) = matches.opt_str("varlink") {
        run_server(&address, 0, matches.opt_present("m")).map_err(|e| e.into())
    } else {
        print_usage(&program, &opts);
        eprintln!("Need varlink address in server mode.");
        exit(1);
    };
    exit(match ret {
        Ok(_) => 0,
        Err(err) => {
            eprintln!("error: {:?}", err);
            1
        }
    });
}

// Client

fn run_client(connection: &Arc<RwLock<varlink::Connection>>) -> Result<()> {
    {
        let mut iface = VarlinkClient::new(connection.clone());
        let ping = String::from("Test");

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let reply = iface.ping(ping.clone()).call()?;
        assert_eq!(ping, reply.pong);
        println!("Pong: '{}'", reply.pong);

        let _reply = iface.upgrade().upgrade()?;
        println!("Client: upgrade()");
    }
    {
        // serve upgraded connection
        let mut conn = connection.write().unwrap();
        let mut writer = conn.writer.take().unwrap();
        writer.write_all(b"test test\nEnd\n")?;
        conn.writer = Some(writer);
        let mut buf = Vec::new();
        let mut reader = conn.reader.take().unwrap();
        if reader.read_until(b'\n', &mut buf)? == 0 {
            // incomplete data, in real life, store all bytes for the next call
            // for now just read the rest
            reader.read_to_end(&mut buf)?;
        };
        eprintln!("Client: upgraded got: {}", String::from_utf8_lossy(&buf));
        conn.reader = Some(reader);
    }
    Ok(())
}

// Server

struct MyOrgExamplePing;

impl org_example_ping::VarlinkInterface for MyOrgExamplePing {
    fn ping(&self, call: &mut Call_Ping, ping: String) -> varlink::Result<()> {
        call.reply(ping)
    }

    fn upgrade(&self, call: &mut Call_Upgrade) -> varlink::Result<()> {
        eprintln!("Server: called upgrade");
        call.to_upgraded();
        call.reply()
    }

    // An upgraded connection has its own application specific protocol.
    // Normally, there is no way back to the varlink protocol with this connection.
    fn call_upgraded(&self, call: &mut Call, bufreader: &mut BufRead) -> varlink::Result<Vec<u8>> {
        loop {
            let mut buf = String::new();
            let len = bufreader.read_line(&mut buf)?;
            if len == 0 {
                eprintln!("Server: upgraded got: none");
                // incomplete data, in real life, store all bytes for the next call
                // return Ok(buf.as_bytes().to_vec());
                return Err(varlink::ErrorKind::ConnectionClosed.into());
            }
            eprintln!("Server: upgraded got: {}", buf);

            call.writer.write_all(b"server reply: ")?;
            call.writer.write_all(buf.as_bytes())?;
            if buf.eq("End\n") {
                break;
            }
        }
        eprintln!("Server: upgraded ending");
        Ok(Vec::new())
    }
}

struct FdTracker {
    stream: Option<ServerStream>,
    buffer: Option<Vec<u8>>,
}

impl FdTracker {
    fn shutdown(&mut self) -> varlink::Result<()> {
        self.stream.as_mut().unwrap().shutdown()
    }
    fn chain_buffer(&mut self, buf: &mut Vec<u8>) {
        self.buffer.as_mut().unwrap().append(buf);
    }
    fn fill_buffer(&mut self, buf: &Vec<u8>) {
        self.buffer.as_mut().unwrap().clone_from(buf);
    }
    fn buf_as_slice(&mut self) -> &[u8] {
        self.buffer.as_mut().unwrap().as_slice()
    }
    fn write(&mut self, out: &[u8]) -> io::Result<usize> {
        self.stream.as_mut().unwrap().write(out)
    }
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.stream.as_mut().unwrap().read(buf)
    }
}

// listen_multiplex is just an example, if you don't want to use varlink::listen()
// and how to build your own main-loop and use the low-level varlink::handle() instead
pub fn listen_multiplex<S: ?Sized + AsRef<str>, H: ::ConnectionHandler + Send + Sync + 'static>(
    handler: H,
    address: &S,
    idle_timeout: u64,
) -> varlink::Result<()> {
    let timeout: i32 = match idle_timeout {
        0 => -1,
        n => (n * 1000) as i32,
    };

    let handler = Arc::new(handler);
    let mut fdmap: HashMap<i32, FdTracker> = HashMap::new();
    let mut fds = Vec::new();
    let mut threads = Vec::new();
    let listener = Listener::new(address)?;
    let upgraded_in_use = Arc::new(RwLock::new(0));

    listener.set_nonblocking(true)?;

    fds.push(libc::pollfd {
        fd: listener.as_raw_fd(),
        revents: 0,
        events: libc::POLLIN,
    });

    loop {
        // Read activity on listening socket
        if fds[0].revents != 0 {
            let mut client = listener.accept(0)?;

            client.set_nonblocking(true)?;

            let fd = client.as_raw_fd();
            fds.push(libc::pollfd {
                fd,
                revents: 0,
                events: libc::POLLIN,
            });

            fdmap.insert(
                fd,
                FdTracker {
                    stream: Some(client),
                    buffer: Some(Vec::new()),
                },
            );
        }

        // Store which indices to remove
        let mut indices_to_remove = vec![];

        // Check client connections ...
        for (i, fds_item) in fds.iter().enumerate().skip(1) {
            if fds_item.revents != 0 {
                let mut upgraded_iface: Option<String> = None;
                let mut tracker = fdmap.get_mut(&fds_item.fd).unwrap();
                loop {
                    let mut readbuf: [u8; 8192] = [0; 8192];

                    match tracker.read(&mut readbuf) {
                        Ok(0) => {
                            let _ = tracker.shutdown();
                            indices_to_remove.push(i);
                            break;
                        }
                        Ok(len) => {
                            let mut out: Vec<u8> = Vec::new();
                            tracker.chain_buffer(&mut readbuf[0..len].to_vec());
                            eprintln!(
                                "Handling: {}",
                                String::from_utf8_lossy(&tracker.buf_as_slice())
                            );

                            match handler.handle(&mut tracker.buf_as_slice(), &mut out, None) {
                                // TODO: buffer output and write only on POLLOUT
                                Ok((unprocessed_bytes, last_iface)) => {
                                    upgraded_iface = last_iface;
                                    if !unprocessed_bytes.is_empty() {
                                        eprintln!(
                                            "Unprocessed bytes: {}",
                                            String::from_utf8_lossy(&unprocessed_bytes)
                                        );
                                    }
                                    tracker.fill_buffer(&unprocessed_bytes);

                                    if let Err(err) = tracker.write(out.as_ref()) {
                                        eprintln!("write error: {}", err);
                                        for cause in Fail::iter_causes(&err) {
                                            eprintln!("  caused by: {}", cause);
                                        }
                                        let _ = tracker.shutdown();
                                        indices_to_remove.push(i);
                                        break;
                                    }
                                }
                                Err(e) => match e.kind() {
                                    err => {
                                        eprintln!("handler error: {}", err);
                                        for cause in Fail::iter_causes(&err).skip(1) {
                                            eprintln!("  caused by: {}", cause);
                                        }
                                        let _ = tracker.shutdown();
                                        indices_to_remove.push(i);
                                        break;
                                    }
                                },
                            }
                        }
                        Err(e) => match e.kind() {
                            io::ErrorKind::WouldBlock => {
                                break;
                            }
                            _ => {
                                let _ = tracker.shutdown();
                                indices_to_remove.push(i);
                                eprintln!("IO error: {}", e);
                                break;
                            }
                        },
                    }
                }
                if upgraded_iface.is_some() {
                    eprintln!("Upgraded MODE");
                    // upgraded mode... thread away the server
                    // feed it directly with the client stream
                    // If you have a better idea, open an Issue or PR on github
                    indices_to_remove.push(i);

                    let j = thread::spawn({
                        eprintln!("upgraded thread");
                        let handler = handler.clone();
                        let mut stream = tracker.stream.take().unwrap();
                        let mut buffer = tracker.buffer.take().unwrap();
                        let mut upgraded_in_use = upgraded_in_use.clone();
                        move || {
                            let _r = stream.set_nonblocking(false);
                            let (reader, mut writer) = stream.split().unwrap();
                            let br = BufReader::new(reader);
                            let mut bufreader = Box::new(buffer.chain(br));
                            let mut upgraded_iface = upgraded_iface.take();

                            {
                                let mut ctr = upgraded_in_use.write().unwrap();
                                *ctr += 1;
                            }
                            loop {
                                match handler.handle(&mut bufreader, &mut writer, upgraded_iface) {
                                    Ok((unread, iface)) => {
                                        upgraded_iface = iface;
                                        match bufreader.fill_buf() {
                                            Err(_) => {
                                                eprintln!("Upgraded end");
                                                break;
                                            }
                                            Ok(buf) => {
                                                if buf.is_empty() && unread.is_empty() => {
                                                    eprintln!("Upgraded end");
                                                    break;
                                                }

                                                if !unread.is_empty() {
                                                    eprintln!(
                                                        "Not handled bytes: {}",
                                                        String::from_utf8_lossy(&unread)
                                                    );
                                                    break;
                                                }

                                                if !buf.is_empty() {
                                                    eprintln!(
                                                        "fill_buf(): {}",
                                                        String::from_utf8_lossy(&buf)
                                                    );
                                                }
                                            }
                                        }
                                    }
                                    Err(err) => match err.kind() {
                                        varlink::ErrorKind::ConnectionClosed => {
                                            eprintln!("Upgraded end");
                                            break;
                                        }
                                        _ => {
                                            eprintln!("Upgraded end: {}", err);
                                            for cause in Fail::iter_causes(&err).skip(1) {
                                                eprintln!("  caused by: {}", cause);
                                            }
                                            break;
                                        }
                                    },
                                }
                            }
                            {
                                let mut ctr = upgraded_in_use.write().unwrap();
                                *ctr -= 1;
                            }
                        }
                    });
                    threads.push(j);
                }
            }
        }

        // We can't modify the vector while we are traversing it, so update now.
        for i in indices_to_remove {
            fdmap.remove(&fds[i].fd);
            fds.remove(i);
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), (fds.len() as u32).into(), timeout) };

        if r < 0 {
            for t in threads {
                let _r = t.join();
            }
            return Err(Error::last_os_error().into());
        }

        if r == 0 && fds.len() == 1 && *upgraded_in_use.read().unwrap() == 0 {
            eprintln!("listen_multiplex: Waiting for threads to end.");
            for t in threads {
                let _r = t.join();
            }

            return Err(varlink::Error::from(varlink::ErrorKind::Timeout));
        }
    }
}

fn run_server(address: &str, timeout: u64, multiplex: bool) -> varlink::Result<()> {
    let myorgexampleping = MyOrgExamplePing;
    let myinterface = org_example_ping::new(Box::new(myorgexampleping));
    let service = VarlinkService::new(
        "org.varlink",
        "test ping service",
        "0.1",
        "http://varlink.org",
        vec![Box::new(myinterface)],
    );

    if multiplex {
        // Demonstrate a single process, single-threaded service
        listen_multiplex(service, &address, timeout)?;
    } else {
        varlink::listen(service, &address, 1, 10, timeout)?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{io, run, os, str};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::io::fs::File;
use std::io::stdin;
use std::io::signal::{Listener, Interrupt};
use extra::getopts;

static exit_code : i32 = -999;
static nop_code : i32 = -1;

struct Shell {
    cmd_prompt : ~str,
    cmd_history : ~[~str],
}

impl Shell {

    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            cmd_history: ~[],
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());

        let (port_func, chan_func) : (Port<i32>, Chan<i32>) = Chan::new();
        let (port_status, chan_status) : (Port<i32>, Chan<i32>) = Chan::new();
        chan_status.send(nop_code);
        spawn(proc() {
            let mut listener = Listener::new();
            listener.register(Interrupt);
            loop {
                match port_func.try_recv() {
                    Some(status) if status == exit_code => {
                        listener.unregister(Interrupt);
                        unsafe { std::libc::exit(1 as std::libc::c_int); }
                        // return; // hangs if child processes running, so we used exit.
                    }
                    Some(response) => { chan_status.send(response); }   // To complete process
                    None    => {}
                }
            }
        });

        loop {
            port_status.recv();     // Wait for last op to finish.
            print(self.cmd_prompt); // Prints "gash >"
            io::stdio::flush();

            let line : ~str = stdin.read_line().unwrap().to_owned();
            let mut cmd_line: ~str = line.trim().to_owned();
            let mut background: bool = false;

            if cmd_line.char_len() > 0 {
                background = cmd_line.char_at(cmd_line.char_len() - 1) == '&';
                if background {
                    cmd_line = cmd_line.slice(0, cmd_line.char_len() - 1).trim().to_owned();
                }
            }

            let params = cmd_line.clone().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""          =>  { chan_func.send(nop_code);
                                  continue; }
                "exit"      =>  { chan_func.send(exit_code); }  // Exit
                "cd"        =>  { Shell::run_check_mode(background, Shell::run_cd(params), &chan_func); }
                "history"   =>  { Shell::run_check_mode(background, Shell::run_history(params, self.cmd_history), &chan_func); }
                _           =>  { Shell::run_check_mode(background, Shell::run_cmdline(params), &chan_func); }
            }

            self.cmd_history.push(program.to_owned());
        }
    }

    fn run_check_mode(background: bool, f: proc(), chan_func : &Chan<i32>) {
        let (port_temp, chan_temp) : (Port<i32>, Chan<i32>) = Chan::new();
        if background {
            spawn(proc() {
                f();
            });
            chan_func.send(nop_code);  // Return immediately.
        } else {
            spawn(proc() {
                f();
                chan_temp.send(nop_code); // Wait until f returns.
            });
            chan_func.send(port_temp.recv());   // Pass on the message.
        }
    }

    fn run_cmdline_single(cmd_line: &str) {
        (Shell::run_cmdline(cmd_line))();
    }

    fn run_cmdline(cmd_line: &str) -> proc() {
        let params = cmd_line.to_owned();
        return proc() {
            if params.trim() != "" {
                let output = Shell::handle_pipes(params, false);
                // write output to file (if necessary and exists)
                match output {
                    Some(outString) => {
                        match Shell::get_output_file(params) {
                            Some(fileName) => {
                                let mut f = File::create(&Path::new(fileName));
                                f.write(outString.into_bytes());
                            }
                            None => { /* Shouldn't happen... */ }
                        }
                    }
                    None => {}
                }
            }
        };
    }

    fn handle_pipes(cmd_line: &str, out_pipe: bool) -> Option<~str> {
        let pipeSplit = Shell::split_on_last_pipe(cmd_line);
        let mut argv = Shell::get_args_no_redirects(pipeSplit[0]);
        let program = argv.remove(0);
        let hasOutRedirect = match Shell::get_output_file(cmd_line) {
            Some(_) => { true }
            None => {
                if out_pipe { true }
                else { false }
            }
        };

        if pipeSplit.len() == 2 {
            return Shell::run_cmd(program, argv, Shell::handle_pipes(pipeSplit[1], true), hasOutRedirect);
        } else {
            return Shell::run_cmd(program, argv, Shell::get_input_file_contents(cmd_line), hasOutRedirect);
        }
    }

    fn run_cmd(program: &str, argv: &[~str], inputStr: Option<~str>, hasOutRedirect: bool) -> Option<~str> {
        if Shell::cmd_exists(program) {
            let mut options = run::ProcessOptions::new();
            options.in_fd = match inputStr {
                Some(_) => { None }
                None => { Some(0) } // 0 is stdin.
            };

            options.out_fd = if hasOutRedirect {
                None
            } else {
                Some(1) // 1 is stdout.
            };

            let mut process = run::Process::new(program, argv, options).unwrap();

            match inputStr {
                Some(string) => {
                    let buf = string.into_bytes();
                    process.input().write(buf);
                }
                None => {}
            }

            process.close_input();

            if hasOutRedirect {
                let processOutput = process.finish_with_output();
                return Some(str::from_utf8(processOutput.output).to_owned());
            } else {
                process.finish();
                return None;
            };
        } else {
            println!("{:s}: command not found", program);
            return None;
        }
    }

    fn cmd_exists(cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }

    fn run_cd(cmd_line: &str) -> proc() {
        let params = cmd_line.to_owned();
        return proc() {
            let argv: ~[~str] = Shell::get_args(params);
            let pathOpt: Option<Path> = match argv.len() {
                1   =>  { os::homedir() }
                0   =>  { os::homedir() }
                _   =>  { Some(Path::new(argv[1])) }
            };

            match pathOpt {
                Some(path)   =>  {
                    if path.is_dir() {
                        os::change_dir(&path);
                    } else {
                        println!("Error: {:s} is not a directory", path.as_str().unwrap());
                    }
                }
                None        =>  {
                    println!("Error: Invalid path");
                }
            };
        };
    }

    fn run_history(cmd_line: &str, cmd_history: &[~str]) -> proc() {
        let history = cmd_history.to_owned();
        let params = cmd_line.to_owned();
        return proc() {
            let argv: ~[~str] = Shell::get_args(params);
            if (argv.len() > 1) {
                println!("Error: history does not take options (sadly).");
            }
            else {
                let mut i = 1;
                for entry in history.iter() {
                    println!("{:d} \t{:s}", i, entry.to_owned());
                    i = i + 1;
                }
            }
        };
    }

    fn split_on_last_pipe(cmd_line: &str) -> ~[~str] {
        return cmd_line.rsplitn('|', 1).filter_map(|x| Some(x.trim().to_owned())).to_owned_vec();
    }

    fn get_input_file_contents(cmd_line: &str) -> Option<~str> {
        return match cmd_line.splitn('<', 1).nth(1) {
            Some(inputFileCandidate) => {
                match inputFileCandidate.trim().splitn(' ', 1).nth(0) {
                    Some(inputFile) => {
                        match File::open(&Path::new(inputFile)) {
                            Some(mut file) => {
                                Some(file.read_to_str())
                            }
                            None => {
                                println!("File {:s} does not exist", inputFile);
                                None
                            }
                        }
                    }
                    None => { None }
                }
            }
            None => { None }
        };
    }

    fn get_output_file(cmd_line: &str) -> Option<~str> {
        return match cmd_line.splitn('>', 1).nth(1) {
            Some(inputFileCandidate) => {
                match inputFileCandidate.trim().splitn(' ', 1).nth(0) {
                    Some(inputFile) => { Some(inputFile.to_owned()) }
                    None => { None }
                }
            }
            None => { None }
        };
    }

    fn get_args_no_redirects(cmd_line: &str) -> ~[~str] {
        let mut activeRedirect: bool = false;
        let mut args: ~[~str] = ~[];
        let mut input: ~[~str] = Shell::get_args(cmd_line);
        for arg in input.mut_iter() {
            if *arg == ~">" || *arg == ~"<" {
                activeRedirect = true;
            } else if activeRedirect {
                activeRedirect = false;
            } else {
                let newArg = arg.to_owned();
                args.push(newArg);
            }
        }

        let result = args;
        return result;
    }

    fn get_args(cmd_line: &str) -> ~[~str] {
        let mut current: ~str = ~"";
        let mut escape: bool = false;
        let mut quoted: bool = false;
        let mut params: ~[~str] = ~[];

        for symbol in cmd_line.chars() {
            if escape {
                if symbol == 't' || symbol == 'n' || symbol == 'r' {
                    current = current + "\\";
                }
                current = current + str::from_char(symbol);
                escape = false;
            } else if symbol == ' ' || symbol == '\t' {
                if quoted {
                    current = current + str::from_char(symbol);
                } else if current.char_len() != 0 {
                    params.push(current.to_owned());
                    current = ~"";
                }
            } else if symbol == '\\' {
                escape = true;
            } else if symbol == '"' {
                quoted = !quoted;
            } else {
                current = current + str::from_char(symbol);
            }
        }

        if current.char_len() > 0 {
            params.push(current.to_owned());
        }

        let result = params;
        return result;
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                                                Some(cmd_str) => {cmd_str.to_owned()},
                                                None => {~""}
                                              };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::run_cmdline_single(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<commit_msg>changed to file descriptor for output redirect<commit_after>//
// gash.rs
//
// Starting code for PS2
// Running on Rust 0.9
//
// University of Virginia - cs4414 Spring 2014
// Weilin Xu, David Evans
// Version 0.4
//

extern mod extra;

use std::{io, run, os, str};
use std::io::buffered::BufferedReader;
use std::path::posix::Path;
use std::io::fs::File;
use std::io::stdin;
use std::io::signal::{Listener, Interrupt};
use extra::getopts;

static exit_code : i32 = -999;
static nop_code : i32 = -1;

struct Shell {
    cmd_prompt : ~str,
    cmd_history : ~[~str],
}

impl Shell {

    fn new(prompt_str: &str) -> Shell {
        Shell {
            cmd_prompt: prompt_str.to_owned(),
            cmd_history: ~[],
        }
    }

    fn run(&mut self) {
        let mut stdin = BufferedReader::new(stdin());

        let (port_func, chan_func) : (Port<i32>, Chan<i32>) = Chan::new();
        let (port_status, chan_status) : (Port<i32>, Chan<i32>) = Chan::new();
        chan_status.send(nop_code);
        spawn(proc() {
            let mut listener = Listener::new();
            listener.register(Interrupt);
            loop {
                match port_func.try_recv() {
                    Some(status) if status == exit_code => {
                        listener.unregister(Interrupt);
                        unsafe { std::libc::exit(1 as std::libc::c_int); }
                        // return; // hangs if child processes running, so we used exit.
                    }
                    Some(response) => { chan_status.send(response); }   // To complete process
                    None    => {}
                }
            }
        });

        loop {
            port_status.recv();     // Wait for last op to finish.
            print(self.cmd_prompt); // Prints "gash >"
            io::stdio::flush();

            let line : ~str = stdin.read_line().unwrap().to_owned();
            let mut cmd_line: ~str = line.trim().to_owned();
            let mut background: bool = false;

            if cmd_line.char_len() > 0 {
                background = cmd_line.char_at(cmd_line.char_len() - 1) == '&';
                if background {
                    cmd_line = cmd_line.slice(0, cmd_line.char_len() - 1).trim().to_owned();
                }
            }

            let params = cmd_line.clone().to_owned();
            let program = cmd_line.splitn(' ', 1).nth(0).expect("no program");

            match program {
                ""          =>  { chan_func.send(nop_code);
                                  continue; }
                "exit"      =>  { chan_func.send(exit_code); }  // Exit
                "cd"        =>  { Shell::run_check_mode(background, Shell::run_cd(params), &chan_func); }
                "history"   =>  { Shell::run_check_mode(background, Shell::run_history(params, self.cmd_history), &chan_func); }
                _           =>  { Shell::run_check_mode(background, Shell::run_cmdline(params), &chan_func); }
            }

            self.cmd_history.push(program.to_owned());
        }
    }

    fn run_check_mode(background: bool, f: proc(), chan_func : &Chan<i32>) {
        let (port_temp, chan_temp) : (Port<i32>, Chan<i32>) = Chan::new();
        if background {
            spawn(proc() {
                f();
            });
            chan_func.send(nop_code);  // Return immediately.
        } else {
            spawn(proc() {
                f();
                chan_temp.send(nop_code); // Wait until f returns.
            });
            chan_func.send(port_temp.recv());   // Pass on the message.
        }
    }

    fn run_cmdline_single(cmd_line: &str) {
        (Shell::run_cmdline(cmd_line))();
    }

    fn run_cmdline(cmd_line: &str) -> proc() {
        let params = cmd_line.to_owned();
        return proc() {
            if params.trim() != "" {
                Shell::handle_pipes(params, false);
            }
        };
    }

    fn handle_pipes(cmd_line: &str, out_pipe: bool) -> Option<~str> {
        let pipeSplit = Shell::split_on_last_pipe(cmd_line);
        let mut argv = Shell::get_args_no_redirects(pipeSplit[0]);
        let program = argv.remove(0);
        let hasOutRedirect = match Shell::get_output_file(cmd_line) {
            Some(_) => { true }
            None => {
                if out_pipe { true }
                else { false }
            }
        };

        if pipeSplit.len() == 2 {
            return Shell::run_cmd(program, argv, Shell::handle_pipes(pipeSplit[1], true), hasOutRedirect, None);
        } else {
            return Shell::run_cmd(program, argv, Shell::get_input_file_contents(cmd_line), hasOutRedirect, Shell::get_output_file(cmd_line));
        }
    }

    fn run_cmd(program: &str, argv: &[~str], inputStr: Option<~str>, hasOutRedirect: bool, outFilename: Option<~str>) -> Option<~str> {
        if Shell::cmd_exists(program) {
            let mut options = run::ProcessOptions::new();
            options.in_fd = match inputStr {
                Some(_) => { None }
                None => { Some(0) } // 0 is stdin.
            };

            unsafe {
                options.out_fd = if hasOutRedirect {
                    match outFilename {
                        Some(ref filename) => {
                            Some(std::libc::funcs::posix88::fcntl::open(filename.to_c_str().unwrap(), std::libc::consts::os::posix88::O_RDWR | std::libc::consts::os::posix88::O_TRUNC | std::libc::consts::os::posix88::O_CREAT, std::libc::consts::os::posix88::S_IRUSR | std::libc::consts::os::posix88::S_IWUSR))
                        }
                        None => {None}
                    }
                } else {
                    Some(1) // 1 is stdout.
                };
            }

            let mut process = run::Process::new(program, argv, options).unwrap();

            match inputStr {
                Some(string) => {
                    let buf = string.into_bytes();
                    process.input().write(buf);
                }
                None => {}
            }

            process.close_input();

            if hasOutRedirect {
                match outFilename {
                    Some(_) => {
                        process.finish();
                        return None;
                    }
                    None => {
                        let processOutput = process.finish_with_output();
                        return Some(str::from_utf8(processOutput.output).to_owned());
                    }
                }
            } else {
                process.finish();
                return None;
            };
        } else {
            println!("{:s}: command not found", program);
            return None;
        }
    }

    fn cmd_exists(cmd_path: &str) -> bool {
        let ret = run::process_output("which", [cmd_path.to_owned()]);
        return ret.expect("exit code error.").status.success();
    }

    fn run_cd(cmd_line: &str) -> proc() {
        let params = cmd_line.to_owned();
        return proc() {
            let argv: ~[~str] = Shell::get_args(params);
            let pathOpt: Option<Path> = match argv.len() {
                1   =>  { os::homedir() }
                0   =>  { os::homedir() }
                _   =>  { Some(Path::new(argv[1])) }
            };

            match pathOpt {
                Some(path)   =>  {
                    if path.is_dir() {
                        os::change_dir(&path);
                    } else {
                        println!("Error: {:s} is not a directory", path.as_str().unwrap());
                    }
                }
                None        =>  {
                    println!("Error: Invalid path");
                }
            };
        };
    }

    fn run_history(cmd_line: &str, cmd_history: &[~str]) -> proc() {
        let history = cmd_history.to_owned();
        let params = cmd_line.to_owned();
        return proc() {
            let argv: ~[~str] = Shell::get_args(params);
            if (argv.len() > 1) {
                println!("Error: history does not take options (sadly).");
            }
            else {
                let mut i = 1;
                for entry in history.iter() {
                    println!("{:d} \t{:s}", i, entry.to_owned());
                    i = i + 1;
                }
            }
        };
    }

    fn split_on_last_pipe(cmd_line: &str) -> ~[~str] {
        return cmd_line.rsplitn('|', 1).filter_map(|x| Some(x.trim().to_owned())).to_owned_vec();
    }

    fn get_input_file_contents(cmd_line: &str) -> Option<~str> {
        return match cmd_line.splitn('<', 1).nth(1) {
            Some(inputFileCandidate) => {
                match inputFileCandidate.trim().splitn(' ', 1).nth(0) {
                    Some(inputFile) => {
                        match File::open(&Path::new(inputFile)) {
                            Some(mut file) => {
                                Some(file.read_to_str())
                            }
                            None => {
                                println!("File {:s} does not exist", inputFile);
                                None
                            }
                        }
                    }
                    None => { None }
                }
            }
            None => { None }
        };
    }

    fn get_output_file(cmd_line: &str) -> Option<~str> {
        return match cmd_line.splitn('>', 1).nth(1) {
            Some(inputFileCandidate) => {
                match inputFileCandidate.trim().splitn(' ', 1).nth(0) {
                    Some(inputFile) => { Some(inputFile.to_owned()) }
                    None => { None }
                }
            }
            None => { None }
        };
    }

    fn get_args_no_redirects(cmd_line: &str) -> ~[~str] {
        let mut activeRedirect: bool = false;
        let mut args: ~[~str] = ~[];
        let mut input: ~[~str] = Shell::get_args(cmd_line);
        for arg in input.mut_iter() {
            if *arg == ~">" || *arg == ~"<" {
                activeRedirect = true;
            } else if activeRedirect {
                activeRedirect = false;
            } else {
                let newArg = arg.to_owned();
                args.push(newArg);
            }
        }

        let result = args;
        return result;
    }

    fn get_args(cmd_line: &str) -> ~[~str] {
        let mut current: ~str = ~"";
        let mut escape: bool = false;
        let mut quoted: bool = false;
        let mut params: ~[~str] = ~[];

        for symbol in cmd_line.chars() {
            if escape {
                if symbol == 't' || symbol == 'n' || symbol == 'r' {
                    current = current + "\\";
                }
                current = current + str::from_char(symbol);
                escape = false;
            } else if symbol == ' ' || symbol == '\t' {
                if quoted {
                    current = current + str::from_char(symbol);
                } else if current.char_len() != 0 {
                    params.push(current.to_owned());
                    current = ~"";
                }
            } else if symbol == '\\' {
                escape = true;
            } else if symbol == '"' {
                quoted = !quoted;
            } else {
                current = current + str::from_char(symbol);
            }
        }

        if current.char_len() > 0 {
            params.push(current.to_owned());
        }

        let result = params;
        return result;
    }
}

fn get_cmdline_from_args() -> Option<~str> {
    /* Begin processing program arguments and initiate the parameters. */
    let args = os::args();

    let opts = ~[
        getopts::optopt("c")
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_err_msg()) }
    };

    if matches.opt_present("c") {
        let cmd_str = match matches.opt_str("c") {
                                                Some(cmd_str) => {cmd_str.to_owned()},
                                                None => {~""}
                                              };
        return Some(cmd_str);
    } else {
        return None;
    }
}

fn main() {
    let opt_cmd_line = get_cmdline_from_args();

    match opt_cmd_line {
        Some(cmd_line) => Shell::run_cmdline_single(cmd_line),
        None           => Shell::new("gash > ").run()
    }
}
<|endoftext|>