<commit_before>extern crate ar;
extern crate clap;
extern crate libflate;
extern crate tar;
extern crate tempfile;
extern crate time as crates_time;
extern crate xz2;
extern crate zip;

use std::error;
use std::fmt;
use std::fs;
use std::io;
use std::time;

use std::rc::Rc;
use std::vec::Vec;

use clap::{Arg, App};

use libflate::gzip;
use tempfile::tempfile;

// magic:
use std::io::Seek;
use std::io::Write;

#[derive(Debug)]
struct Node<T> {
    next: Option<Rc<Node<T>>>,
    value: T,
}

impl<T: Clone> Node<T> {
    fn head(obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: None,
            value: obj,
        })
    }

    fn plus(what: &Rc<Node<T>>, obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: Some(what.clone()),
            value: obj
        })
    }

    fn to_vec(&self) -> Vec<T> {
        let mut ret = Vec::new();
        let mut val = self;
        loop {
            ret.push(val.value.clone());
            if let Some(ref next) = val.next {
                val = next;
            } else {
                break;
            }
        }
        ret.reverse();
        ret
    }
}

struct OutputTo {
    path: Rc<Node<String>>,
    size: u64,
    atime: u64,
    mtime: u64,
    ctime: u64,
    btime: u64,
}

impl OutputTo {
    fn warn(&self, msg: String) {
        writeln!(io::stderr(), "TODO: {}", msg).expect("stderr");
    }

    fn raw<F: io::Read + ?Sized>(&self, mut file: &mut F) -> io::Result<()> {
        let stdout = io::stdout();
        let mut stdout = stdout.lock();
        writeln!(stdout, "{:?} {} {} {} {} {}",
            self.path.to_vec(),
            self.size,
            self.atime, self.mtime, self.ctime, self.btime
        )?;

        io::copy(&mut file, &mut stdout).and_then(move |written|
            if written != self.size {
                Err(io::Error::new(
                    io::ErrorKind::UnexpectedEof,
                    format!("expecting to write {} but wrote {}", self.size, written)))
            } else {
                Ok(())
            })
    }

    fn from_file(path: &str, fd: &fs::File) -> io::Result<OutputTo> {
        let meta = fd.metadata()?;
        Ok(OutputTo {
            path: Node::head(path.to_string()),
            size: meta.len(),
            atime: meta.accessed().map(simple_time_sys)?,
            mtime: meta.modified().map(simple_time_sys)?,
            ctime: simple_time_ctime(&meta),
            btime: simple_time_btime(&meta)?,
        })
    }

    fn with_path(&self, path: &str) -> OutputTo {
        OutputTo {
            path: Node::plus(&self.path, path.to_string()),
            size: 0,
            atime: 0,
            mtime: 0,
            ctime: 0,
            btime: 0,
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum FileType {
    GZip,
    Zip,
    Tar,
    Ar,
    BZip2,
    Xz,
    Other,
}

fn read_octal(bytes: &[u8]) -> Option<u32> {
    let mut start = 0;
    while start < bytes.len() && b' ' == bytes[start] {
        start += 1;
    }

    let mut end = bytes.len() - 1;
    while end > start && (b' ' == bytes[end] || 0 == bytes[end]) {
        end -= 1;
    }

    if let Ok(string) = std::str::from_utf8(&bytes[start..(end+1)]) {
        if let Ok(val) = u32::from_str_radix(string, 8) {
            return Some(val);
        }
    }
    None
}

fn simple_time(dur: time::Duration) -> u64 {
    dur.as_secs().checked_mul(1_000_000_000)
        .map_or(0, |nanos| nanos + dur.subsec_nanos() as u64)
}

fn simple_time_sys(val: time::SystemTime) -> u64 {
    val.duration_since(time::UNIX_EPOCH).map(simple_time).unwrap_or(0)
}

fn simple_time_tm(val: crates_time::Tm) -> u64 {
    let timespec = val.to_timespec();
    simple_time(time::Duration::new(timespec.sec as u64, timespec.nsec as u32))
}

fn simple_time_btime(val: &fs::Metadata) -> io::Result<u64> {
    match val.created() {
        Ok(time) => Ok(simple_time_sys(time)),
        // "Other" is how "unsupported" is represented here; ew.
        Err(ref e) if e.kind() == io::ErrorKind::Other => Ok(0),
        Err(other) => Err(other),
    }
}

// TODO: I really feel this should be exposed by Rust, without cfg.
fn simple_time_ctime(val: &fs::Metadata) -> u64 {
    #[cfg(linux)] {
        use std::os::linux::fs::MetadataExt;
        let ctime: i64 = val.st_ctime();
        if ctime <= 0 {
            0
        } else {
            ctime as u64
        }
    }

    #[cfg(not(linux))] {
        0
    }
}

fn is_probably_tar(header: &[u8]) -> bool {
    if header.len() < 512 {
        return false;
    }

    if let Some(checksum) = read_octal(&header[148..156]) {
        let mut sum: u32 = (b' ' as u32) * 8;
        for i in 0..148 {
            sum += header[i] as u32;
        }
        for i in 156..512 {
            sum += header[i] as u32;
        }

        if checksum == sum {
            return true;
        }
    }

    return false;
}

fn identify<'a, T: io::BufRead + ?Sized>(fd: &mut T) -> io::Result<FileType> {
    let header = fd.fill_buf()?;
    if header.len() >= 20
        && 0x1f == header[0] && 0x8b == header[1] {
        Ok(FileType::GZip)
   } else if header.len() >= 152
        && b'P' == header[0] && b'K' == header[1]
        && 0x03 == header[2] && 0x04 == header[3] {
        Ok(FileType::Zip)
    } else if header.len() > 257 + 10
        && b'u' == header[257] && b's' == header[258]
        && b't' == header[259] && b'a' == header[260]
        && b'r' == header[261]
        && (
            (0 == header[262] && b'0' == header[263] && b'0' == header[264]) ||
            (b' ' == header[262] && b' ' == header[263] && 0 == header[264])
        ) {
        Ok(FileType::Tar)
    } else if header.len() > 8
        && b'!' == header[0] && b'<' == header[1]
        && b'a' == header[2] && b'r' == header[3]
        && b'c' == header[4] && b'h' == header[5]
        && b'>' == header[6] && b'\n' == header[7] {
        Ok(FileType::Ar)
    } else if header.len() > 40
        && b'B' == header[0] && b'Z' == header[1]
        && b'h' == header[2] && 0x31 == header[3]
        && 0x41 == header[4] && 0x59 == header[5]
        && 0x26 == header[6] {
        Ok(FileType::BZip2)
    } else if header.len() > 6
        && 0xfd == header[0] && b'7' == header[1]
        && b'z' == header[2] && b'X' == header[3]
        && b'Z' == header[4] && 0 == header[5] {
        Ok(FileType::Xz)
    } else if is_probably_tar(header) {
        Ok(FileType::Tar)
    } else {
        Ok(FileType::Other)
    }
}

trait Tee {
    fn normal(&mut self) -> &mut io::BufRead;
    fn abort(&mut self) -> io::Result<&mut io::BufRead>;
}

struct TempFileTee<'a> {
    fp: Box<io::BufRead + 'a>,
    tmp: fs::File,
}

impl<'a> TempFileTee<'a> {
    fn new<U: 'a + io::Read>(from: U) -> io::Result<TempFileTee<'a>> {
        Ok(TempFileTee {
            fp: Box::new(io::BufReader::new(from)),
            tmp: tempfile()?
        })
    }
}

impl<'a> Tee for TempFileTee<'a> {
    fn normal(&mut self) -> &mut io::BufRead {
        &mut self.fp
    }
    fn abort(&mut self) -> io::Result<&mut io::BufRead> {
        unimplemented!();
    }
}

struct BufReaderTee<T> {
    fp: Box<T>,
}

impl<U: io::Read> BufReaderTee<io::BufReader<U>> {
    fn new(from: U) -> Self {
        BufReaderTee {
            fp: Box::new(io::BufReader::new(from))
        }
    }
}

impl<T> Tee for BufReaderTee<T>
    where T: io::BufRead + io::Seek
{
    fn normal(&mut self) -> &mut io::BufRead {
        &mut self.fp
    }

    fn abort(&mut self) -> io::Result<&mut io::BufRead> {
        self.fp.seek(io::SeekFrom::Start(0))?;
        Ok(self.normal())
    }
}

fn count_bytes<'a, R: io::Read>(mut fd: &mut R) -> io::Result<u64> {
    let mut block = [0u8; 4096];
    let mut count = 0u64;
    loop {
        let found = fd.read(&mut block[..])?;
        if 0 == found {
            break;
        }
        count += found as u64;
    }
    Ok(count)
}

#[derive(Clone, Copy, Debug)]
struct Rewind {
}

impl error::Error for Rewind {
    fn description(&self) -> &str {
        "rewind"
    }
}

impl fmt::Display for Rewind {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "rewind")
    }
}

fn unpack_or_die<'a, T: ?Sized>(mut fd: &mut T, output: &OutputTo) -> io::Result<()>
where T: io::BufRead
{
    match identify(fd)? {
        FileType::GZip => {
            unpack(Box::new(TempFileTee::new(gzip::Decoder::new(fd)?)?), output)
        },
//        FileType::Xz => {
//            unpack_or_raw(fd, output,
//                |fd| Ok(Box::new(TempFileTee::new(xz2::bufread::XzDecoder::new(fd)))))
//        },
        FileType::Ar if output.path.value.ends_with(".deb") => {
            let mut decoder = ar::Archive::new(fd);
            while let Some(entry) = decoder.next_entry() {
                let entry = entry?;
                let new_output = output.with_path(entry.header().identifier());
                unpack(Box::new(TempFileTee::new(entry)?), &new_output)?;
            }
            Ok(())
        },
        FileType::Tar => {
            let mut decoder = tar::Archive::new(fd);
            for entry in decoder.entries()? {
                let entry = entry?;
                let new_output = output.with_path(entry.path()?.to_str().expect("valid utf-8"));
                unpack(Box::new(TempFileTee::new(entry)?), &new_output)?;
            }
            Ok(())
        },
        FileType::Zip => {
            let mut temp = tempfile()?;
            io::copy(fd, &mut temp)?;
            let mut zip = zip::ZipArchive::new(temp)?;

            for i in 0..zip.len() {

                // well, this has gone rather poorly
                let mut new_output = output.with_path(zip.by_index(i)?.name());

                let entry = zip.by_index(i)?;

                new_output.size = entry.size();
                new_output.mtime = simple_time_tm(entry.last_modified());
                let reader = Box::new(TempFileTee::new(entry)?);
                unpack(reader, &new_output)?;
            }
            Ok(())
        },

        // TODO: unimplemented!()
        _ => {
            Err(io::Error::new(io::ErrorKind::Other, Rewind {}))
        },
    }
}

fn is_format_error(e: &io::Error) -> bool {
    if io::ErrorKind::Other == e.kind() {
        if let Some(ref obj) = e.get_ref() {
            let option: Option<&Rewind> = obj.downcast_ref();
            if option.is_some() {
                return true;
            }
        }
    }

    panic!("don't know if {:?} / {:?} is a format error", e, e.get_ref())
}

fn unpack<'a>(mut fd: Box<Tee + 'a>, output: &OutputTo) -> io::Result<()> {
    match unpack_or_die(fd.normal(), output) {
        Ok(status) => Ok(status),
        Err(ref e) if is_format_error(e) => {
            output.warn(format!(
                "thought we could unpack '{:?}' but we couldn't: {}",
                output.path, e));
            output.raw(fd.abort()?)?;
            Ok(())
        },
        Err(e) => Err(e),
    }
}

fn process_real_path(path: &str) -> io::Result<()> {
    let file = fs::File::open(path)?;
    let output = OutputTo::from_file(
        path,
        &file
    )?;

    unpack(Box::new(BufReaderTee::new(&file)), &output)?;
    Ok(())
}

fn real_main() -> u8 {

    let matches = App::new("contentin")
                    .arg(Arg::with_name("to-tar")
                         .long("to-tar")
                         .help("emit a tar file")
                         .required(true))
                    .arg(Arg::with_name("INPUT")
                         .required(true)
                         .multiple(true))
                    .get_matches();

    for path in matches.values_of("INPUT").unwrap() {
        if let Err(e) = process_real_path(path) {
            if let Err(_) = writeln!(io::stderr(), "fatal: processing '{}': {}", path, e) {
                return 6;
            }

            return 4;
        }
    }
    return 0;
}

fn main() {
    std::process::exit(real_main() as i32)
}
<commit_msg>Oh yeah, that cast syntax will work there.<commit_after>extern crate ar;
extern crate clap;
extern crate libflate;
extern crate tar;
extern crate tempfile;
extern crate time as crates_time;
extern crate xz2;
extern crate zip;

use std::error;
use std::fmt;
use std::fs;
use std::io;
use std::time;

use std::rc::Rc;
use std::vec::Vec;

use clap::{Arg, App};

use libflate::gzip;
use tempfile::tempfile;

// magic:
use std::io::Seek;
use std::io::Write;

#[derive(Debug)]
struct Node<T> {
    next: Option<Rc<Node<T>>>,
    value: T,
}

impl<T: Clone> Node<T> {
    fn head(obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: None,
            value: obj,
        })
    }

    fn plus(what: &Rc<Node<T>>, obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: Some(what.clone()),
            value: obj
        })
    }

    fn to_vec(&self) -> Vec<T> {
        let mut ret = Vec::new();
        let mut val = self;
        loop {
            ret.push(val.value.clone());
            if let Some(ref next) = val.next {
                val = next;
            } else {
                break;
            }
        }
        ret.reverse();
        ret
    }
}

struct OutputTo {
    path: Rc<Node<String>>,
    size: u64,
    atime: u64,
    mtime: u64,
    ctime: u64,
    btime: u64,
}

impl OutputTo {
    fn warn(&self, msg: String) {
        writeln!(io::stderr(), "TODO: {}", msg).expect("stderr");
    }

    fn raw<F: io::Read + ?Sized>(&self, mut file: &mut F) -> io::Result<()> {
        let stdout = io::stdout();
        let mut stdout = stdout.lock();
        writeln!(stdout, "{:?} {} {} {} {} {}",
            self.path.to_vec(),
            self.size,
            self.atime, self.mtime, self.ctime, self.btime
        )?;

        io::copy(&mut file, &mut stdout).and_then(move |written|
            if written != self.size {
                Err(io::Error::new(
                    io::ErrorKind::UnexpectedEof,
                    format!("expecting to write {} but wrote {}", self.size, written)))
            } else {
                Ok(())
            })
    }

    fn from_file(path: &str, fd: &fs::File) -> io::Result<OutputTo> {
        let meta = fd.metadata()?;
        Ok(OutputTo {
            path: Node::head(path.to_string()),
            size: meta.len(),
            atime: meta.accessed().map(simple_time_sys)?,
            mtime: meta.modified().map(simple_time_sys)?,
            ctime: simple_time_ctime(&meta),
            btime: simple_time_btime(&meta)?,
        })
    }

    fn with_path(&self, path: &str) -> OutputTo {
        OutputTo {
            path: Node::plus(&self.path, path.to_string()),
            size: 0,
            atime: 0,
            mtime: 0,
            ctime: 0,
            btime: 0,
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum FileType {
    GZip,
    Zip,
    Tar,
    Ar,
    BZip2,
    Xz,
    Other,
}

fn read_octal(bytes: &[u8]) -> Option<u32> {
    let mut start = 0;
    while start < bytes.len() && b' ' == bytes[start] {
        start += 1;
    }

    let mut end = bytes.len() - 1;
    while end > start && (b' ' == bytes[end] || 0 == bytes[end]) {
        end -= 1;
    }

    if let Ok(string) = std::str::from_utf8(&bytes[start..(end+1)]) {
        if let Ok(val) = u32::from_str_radix(string, 8) {
            return Some(val);
        }
    }
    None
}

fn simple_time(dur: time::Duration) -> u64 {
    dur.as_secs().checked_mul(1_000_000_000)
        .map_or(0, |nanos| nanos + dur.subsec_nanos() as u64)
}

fn simple_time_sys(val: time::SystemTime) -> u64 {
    val.duration_since(time::UNIX_EPOCH).map(simple_time).unwrap_or(0)
}

fn simple_time_tm(val: crates_time::Tm) -> u64 {
    let timespec = val.to_timespec();
    simple_time(time::Duration::new(timespec.sec as u64, timespec.nsec as u32))
}

fn simple_time_btime(val: &fs::Metadata) -> io::Result<u64> {
    match val.created() {
        Ok(time) => Ok(simple_time_sys(time)),
        // "Other" is how "unsupported" is represented here; ew.
        Err(ref e) if e.kind() == io::ErrorKind::Other => Ok(0),
        Err(other) => Err(other),
    }
}

// TODO: I really feel this should be exposed by Rust, without cfg.
fn simple_time_ctime(val: &fs::Metadata) -> u64 {
    #[cfg(linux)] {
        use std::os::linux::fs::MetadataExt;
        let ctime: i64 = val.st_ctime();
        if ctime <= 0 {
            0
        } else {
            ctime as u64
        }
    }

    #[cfg(not(linux))] {
        0
    }
}

fn is_probably_tar(header: &[u8]) -> bool {
    if header.len() < 512 {
        return false;
    }

    if let Some(checksum) = read_octal(&header[148..156]) {
        let mut sum: u32 = (b' ' as u32) * 8;
        for i in 0..148 {
            sum += header[i] as u32;
        }
        for i in 156..512 {
            sum += header[i] as u32;
        }

        if checksum == sum {
            return true;
        }
    }

    return false;
}

fn identify<'a, T: io::BufRead + ?Sized>(fd: &mut T) -> io::Result<FileType> {
    let header = fd.fill_buf()?;
    if header.len() >= 20
        && 0x1f == header[0] && 0x8b == header[1] {
        Ok(FileType::GZip)
   } else if header.len() >= 152
        && b'P' == header[0] && b'K' == header[1]
        && 0x03 == header[2] && 0x04 == header[3] {
        Ok(FileType::Zip)
    } else if header.len() > 257 + 10
        && b'u' == header[257] && b's' == header[258]
        && b't' == header[259] && b'a' == header[260]
        && b'r' == header[261]
        && (
            (0 == header[262] && b'0' == header[263] && b'0' == header[264]) ||
            (b' ' == header[262] && b' ' == header[263] && 0 == header[264])
        ) {
        Ok(FileType::Tar)
    } else if header.len() > 8
        && b'!' == header[0] && b'<' == header[1]
        && b'a' == header[2] && b'r' == header[3]
        && b'c' == header[4] && b'h' == header[5]
        && b'>' == header[6] && b'\n' == header[7] {
        Ok(FileType::Ar)
    } else if header.len() > 40
        && b'B' == header[0] && b'Z' == header[1]
        && b'h' == header[2] && 0x31 == header[3]
        && 0x41 == header[4] && 0x59 == header[5]
        && 0x26 == header[6] {
        Ok(FileType::BZip2)
    } else if header.len() > 6
        && 0xfd == header[0] && b'7' == header[1]
        && b'z' == header[2] && b'X' == header[3]
        && b'Z' == header[4] && 0 == header[5] {
        Ok(FileType::Xz)
    } else if is_probably_tar(header) {
        Ok(FileType::Tar)
    } else {
        Ok(FileType::Other)
    }
}

trait Tee {
    fn normal(&mut self) -> &mut io::BufRead;
    fn abort(&mut self) -> io::Result<&mut io::BufRead>;
}

struct TempFileTee<'a> {
    fp: Box<io::BufRead + 'a>,
    tmp: fs::File,
}

impl<'a> TempFileTee<'a> {
    fn new<U: 'a + io::Read>(from: U) -> io::Result<TempFileTee<'a>> {
        Ok(TempFileTee {
            fp: Box::new(io::BufReader::new(from)),
            tmp: tempfile()?
        })
    }
}

impl<'a> Tee for TempFileTee<'a> {
    fn normal(&mut self) -> &mut io::BufRead {
        &mut self.fp
    }
    fn abort(&mut self) -> io::Result<&mut io::BufRead> {
        unimplemented!();
    }
}

struct BufReaderTee<T> {
    fp: Box<T>,
}

impl<U: io::Read> BufReaderTee<io::BufReader<U>> {
    fn new(from: U) -> Self {
        BufReaderTee {
            fp: Box::new(io::BufReader::new(from))
        }
    }
}

impl<T> Tee for BufReaderTee<T>
    where T: io::BufRead + io::Seek
{
    fn normal(&mut self) -> &mut io::BufRead {
        &mut self.fp
    }

    fn abort(&mut self) -> io::Result<&mut io::BufRead> {
        self.fp.seek(io::SeekFrom::Start(0))?;
        Ok(self.normal())
    }
}

fn count_bytes<'a, R: io::Read>(mut fd: &mut R) -> io::Result<u64> {
    let mut block = [0u8; 4096];
    let mut count = 0u64;
    loop {
        let found = fd.read(&mut block[..])?;
        if 0 == found {
            break;
        }
        count += found as u64;
    }
    Ok(count)
}

#[derive(Clone, Copy, Debug)]
struct Rewind {
}

impl error::Error for Rewind {
    fn description(&self) -> &str {
        "rewind"
    }
}

impl fmt::Display for Rewind {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "rewind")
    }
}

fn unpack_or_die<'a, T: ?Sized>(mut fd: &mut T, output: &OutputTo) -> io::Result<()>
where T: io::BufRead
{
    match identify(fd)? {
        FileType::GZip => {
            unpack(Box::new(TempFileTee::new(gzip::Decoder::new(fd)?)?), output)
        },
//        FileType::Xz => {
//            unpack_or_raw(fd, output,
//                |fd| Ok(Box::new(TempFileTee::new(xz2::bufread::XzDecoder::new(fd)))))
//        },
        FileType::Ar if output.path.value.ends_with(".deb") => {
            let mut decoder = ar::Archive::new(fd);
            while let Some(entry) = decoder.next_entry() {
                let entry = entry?;
                let new_output = output.with_path(entry.header().identifier());
                unpack(Box::new(TempFileTee::new(entry)?), &new_output)?;
            }
            Ok(())
        },
        FileType::Tar => {
            let mut decoder = tar::Archive::new(fd);
            for entry in decoder.entries()? {
                let entry = entry?;
                let new_output = output.with_path(entry.path()?.to_str().expect("valid utf-8"));
                unpack(Box::new(TempFileTee::new(entry)?), &new_output)?;
            }
            Ok(())
        },
        FileType::Zip => {
            let mut temp = tempfile()?;
            io::copy(fd, &mut temp)?;
            let mut zip = zip::ZipArchive::new(temp)?;

            for i in 0..zip.len() {

                // well, this has gone rather poorly
                let mut new_output = output.with_path(zip.by_index(i)?.name());

                let entry = zip.by_index(i)?;

                new_output.size = entry.size();
                new_output.mtime = simple_time_tm(entry.last_modified());
                let reader = Box::new(TempFileTee::new(entry)?);
                unpack(reader, &new_output)?;
            }
            Ok(())
        },

        // TODO: unimplemented!()
        _ => {
            Err(io::Error::new(io::ErrorKind::Other, Rewind {}))
        },
    }
}

fn is_format_error(e: &io::Error) -> bool {
    if io::ErrorKind::Other == e.kind() {
        if let Some(ref obj) = e.get_ref() {
            if (obj.downcast_ref() as Option<&Rewind>).is_some() {
                return true;
            }
        }
    }

    panic!("don't know if {:?} / {:?} is a format error", e, e.get_ref())
}

fn unpack<'a>(mut fd: Box<Tee + 'a>, output: &OutputTo) -> io::Result<()> {
    match unpack_or_die(fd.normal(), output) {
        Ok(status) => Ok(status),
        Err(ref e) if is_format_error(e) => {
            output.warn(format!(
                "thought we could unpack '{:?}' but we couldn't: {}",
                output.path, e));
            output.raw(fd.abort()?)?;
            Ok(())
        },
        Err(e) => Err(e),
    }
}

fn process_real_path(path: &str) -> io::Result<()> {
    let file = fs::File::open(path)?;
    let output = OutputTo::from_file(
        path,
        &file
    )?;

    unpack(Box::new(BufReaderTee::new(&file)), &output)?;
    Ok(())
}

fn real_main() -> u8 {

    let matches = App::new("contentin")
                    .arg(Arg::with_name("to-tar")
                         .long("to-tar")
                         .help("emit a tar file")
                         .required(true))
                    .arg(Arg::with_name("INPUT")
                         .required(true)
                         .multiple(true))
                    .get_matches();

    for path in matches.values_of("INPUT").unwrap() {
        if let Err(e) = process_real_path(path) {
            if let Err(_) = writeln!(io::stderr(), "fatal: processing '{}': {}", path, e) {
                return 6;
            }

            return 4;
        }
    }
    return 0;
}

fn main() {
    std::process::exit(real_main() as i32)
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate conrod;
extern crate piston_window;
extern crate find_folder;
extern crate graphics;
extern crate lyon;
extern crate xml5ever;

use std::fs::File;
use std::io::Read;
use std::sync::mpsc;
use std::default::Default;
use std::iter;
use std::string::String;

use conrod::{Theme, Widget};
use piston_window::*;

use lyon::path_builder::*;

use xml5ever::tendril::SliceExt;
use xml5ever::parse;
use xml5ever::tree_builder::TreeSink;
use xml5ever::rcdom;
use xml5ever::rcdom::{Document, Element, RcDom, Handle};

/// Conrod is backend agnostic. Here, we define the `piston_window` backend to use for our `Ui`.
type Backend = (piston_window::G2dTexture<'static>, piston_window::Glyphs);
type Ui = conrod::Ui<Backend>;
type UiCell<'a> = conrod::UiCell<'a, Backend>;

struct InkApp {
    dom: RcDom,
    elem_sender: mpsc::Sender<(usize, usize, bool)>,
    elem_receiver: mpsc::Receiver<(usize, usize, bool)>,
}

impl InkApp {
    fn new() -> Self {
        let (elem_sender, elem_receiver) = mpsc::channel();
        InkApp {
            dom: RcDom::default(),
            elem_sender: elem_sender,
            elem_receiver: elem_receiver,
        }
    }

    fn set_dom(&mut self, dom: RcDom) {
        self.dom = dom;
    }

    fn get_doc_handle(&mut self) -> Handle {
        self.dom.get_document()
    }
}

fn main() {
    let mut app = InkApp::new();

    let mut file = File::open("tests/documents/testrect.svg").unwrap();

    let mut file_string = String::new();
    if let Err(err) = file.read_to_string(&mut file_string) {
        println!("Reading failed: {}", err);
        std::process::exit(1);
    };

    let input = file_string.to_tendril();

    let dom: RcDom = parse(iter::once(input), Default::default());
    app.set_dom(dom);

    // Construct the window.
    let mut window: PistonWindow = WindowSettings::new("Inkrust", [720, 360])
        .opengl(piston_window::OpenGL::V3_2)
        .samples(4)
        .exit_on_esc(true)
        .build()
        .unwrap();

    // Construct our `Ui`.
    let mut ui = {
        let assets = find_folder::Search::KidsThenParents(3, 5)
            .for_folder("assets")
            .unwrap();
        let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
        let theme = Theme::default();
        let glyph_cache = piston_window::Glyphs::new(&font_path, window.factory.clone()).unwrap();
        Ui::new(glyph_cache, theme)
    };

    window.set_ups(60);

    // Poll events from the window.
    while let Some(event) = window.next() {
        ui.handle_event(event.clone());
        event.update(|_| ui.set_widgets(|mut ui| set_widgets(&mut ui, &mut app)));
        window.draw_2d(&event, |c, g| ui.draw_if_changed(c, g));
    }
}

// Declare the `WidgetId`s and instantiate the widgets.
fn set_widgets(ui: &mut UiCell, app: &mut InkApp) {
    use conrod::{Canvas, Circle, Line, Oval, PointPath, Polygon, Positionable, Rectangle};
    use std::iter::once;

    let doc = app.get_doc_handle();
    walk("", ui, doc);

    // Generate a unique const `WidgetId` for each widget.
    widget_ids!{
	        CANVAS,
	        LINE,
	        POINT_PATH,
	        TRAPEZOID,
	        OVAL_FILL,
	        OVAL_OUTLINE,
	        CIRCLE,
	    };

    // The background canvas upon which we'll place our widgets.
    Canvas::new().pad(80.0).set(CANVAS, ui);

    Line::centred([-40.0, -40.0], [40.0, 40.0]).top_left_of(CANVAS).set(LINE, ui);

    let left = [-40.0, -40.0];
    let top = [0.0, 40.0];
    let right = [40.0, -40.0];
    let points = once(left).chain(once(top)).chain(once(right));
    PointPath::centred(points).down(80.0).set(POINT_PATH, ui);

    let bl = [-40.0, -40.0];
    let tl = [-20.0, 40.0];
    let tr = [20.0, 40.0];
    let br = [40.0, -40.0];
    let points = once(bl).chain(once(tl)).chain(once(tr)).chain(once(br));
    Polygon::centred_fill(points).right_from(LINE, 80.0).set(TRAPEZOID, ui);

    Oval::fill([40.0, 80.0]).down(80.0).align_middle_x().set(OVAL_FILL, ui);

    Oval::outline([80.0, 40.0]).down(100.0).align_middle_x().set(OVAL_OUTLINE, ui);

    Circle::fill(40.0).down(100.0).align_middle_x().set(CIRCLE, ui);
}

pub fn escape_default(s: &str) -> String {
    s.chars().flat_map(|c| c.escape_default()).collect()
}

fn walk(prefix: &str, ui: &mut UiCell, handle: Handle) {
    use conrod::{Canvas, Circle, Line, Oval, PointPath, Polygon, Positionable, Rectangle};
    let node = handle.borrow();

    print!("{}", prefix);
    match node.node {
        Document => println!("#document"),

        rcdom::Text(ref text) => println!("#text {}", escape_default(text)),

        Element(ref name, ref attr) => {
            println!("{}", name.local);

            {
                let map = attr.iter().map(|a| {
                    (a.name, a.value)
                });
                for pair in map {
                    println!("{:?}", pair);
                }
                widget_ids!{
                    RECTANGLE_FILL,
                    RECTANGLE_OUTLINE,
                };
                Rectangle::fill([80.0, 80.0]).down(80.0).set(RECTANGLE_FILL, ui);

                Rectangle::outline([80.0, 80.0]).down(80.0).set(RECTANGLE_OUTLINE, ui);
            }
        }

        _ => {}

    }

    let new_indent = {
        let mut temp = String::new();
        temp.push_str(prefix);
        temp.push_str("    ");
        temp
    };

    for child in node.children
        .iter()
        .filter(|child| match child.borrow().node {
            rcdom::Text(_) | Element(_, _) => true,
            _ => false,
        }) {
        walk(&new_indent, ui, child.clone());
    }
}
<commit_msg>Working on accessing attributes and researching ways to render.<commit_after>#[macro_use]
extern crate conrod;
extern crate piston_window;
extern crate find_folder;
extern crate graphics;
extern crate lyon;
extern crate xml5ever;

use std::fs::File;
use std::io::Read;
use std::sync::mpsc;
use std::default::Default;
use std::iter;
use std::string::String;

use conrod::{Theme, Widget};
use piston_window::*;

use lyon::path_builder::*;

use xml5ever::tendril::SliceExt;
use xml5ever::parse;
use xml5ever::tree_builder::TreeSink;
use xml5ever::rcdom;
use xml5ever::rcdom::{Document, Element, RcDom, Handle};

/// Conrod is backend agnostic. Here, we define the `piston_window` backend to use for our `Ui`.
type Backend = (piston_window::G2dTexture<'static>, piston_window::Glyphs);
type Ui = conrod::Ui<Backend>;
type UiCell<'a> = conrod::UiCell<'a, Backend>;

struct InkApp {
    dom: RcDom,
    elem_sender: mpsc::Sender<(usize, usize, bool)>,
    elem_receiver: mpsc::Receiver<(usize, usize, bool)>,
}

impl InkApp {
    fn new() -> Self {
        let (elem_sender, elem_receiver) = mpsc::channel();
        InkApp {
            dom: RcDom::default(),
            elem_sender: elem_sender,
            elem_receiver: elem_receiver,
        }
    }

    fn set_dom(&mut self, dom: RcDom) {
        self.dom = dom;
    }

    fn get_doc_handle(&mut self) -> Handle {
        self.dom.get_document()
    }
}

fn main() {
    let mut app = InkApp::new();

    let mut file = File::open("tests/documents/testrect.svg").unwrap();

    let mut file_string = String::new();
    if let Err(err) = file.read_to_string(&mut file_string) {
        println!("Reading failed: {}", err);
        std::process::exit(1);
    };

    let input = file_string.to_tendril();

    let dom: RcDom = parse(iter::once(input), Default::default());
    app.set_dom(dom);

    // Construct the window.
    let mut window: PistonWindow = WindowSettings::new("Inkrust", [720, 360])
        .opengl(piston_window::OpenGL::V3_2)
        .samples(4)
        .exit_on_esc(true)
        .build()
        .unwrap();

    // Construct our `Ui`.
    let mut ui = {
        let assets = find_folder::Search::KidsThenParents(3, 5)
            .for_folder("assets")
            .unwrap();
        let font_path = assets.join("fonts/NotoSans/NotoSans-Regular.ttf");
        let theme = Theme::default();
        let glyph_cache = piston_window::Glyphs::new(&font_path, window.factory.clone()).unwrap();
        Ui::new(glyph_cache, theme)
    };

    window.set_ups(60);

    // Poll events from the window.
    while let Some(event) = window.next() {
        ui.handle_event(event.clone());
        event.update(|_| ui.set_widgets(|mut ui| set_widgets(&mut ui, &mut app)));
        window.draw_2d(&event, |c, g| ui.draw_if_changed(c, g));
    }
}

// Declare the `WidgetId`s and instantiate the widgets.
fn set_widgets(ui: &mut UiCell, app: &mut InkApp) {
    use conrod::{Canvas, Circle, Line, Oval, PointPath, Polygon, Positionable, Rectangle, XYPad};
    use std::iter::once;

    let doc = app.get_doc_handle();
    walk("", ui, doc);

    // Generate a unique const `WidgetId` for each widget.
    widget_ids!{
        CANVAS,
        LINE,
        POINT_PATH,
        RECTANGLE_FILL,
        RECTANGLE_OUTLINE,
        TRAPEZOID,
        OVAL_FILL,
        OVAL_OUTLINE,
        CIRCLE,
        XYPAD,
    };

    // The background canvas upon which we'll place our widgets.
    Canvas::new().pad(80.0).set(CANVAS, ui);

    Line::centred([-40.0, -40.0], [40.0, 40.0]).top_left_of(CANVAS).set(LINE, ui);

    let left = [-40.0, -40.0];
    let top = [0.0, 40.0];
    let right = [40.0, -40.0];
    let points = once(left).chain(once(top)).chain(once(right));
    PointPath::centred(points).down(80.0).set(POINT_PATH, ui);

    Rectangle::fill([80.0, 80.0]).down(80.0).set(RECTANGLE_FILL, ui);

    Rectangle::outline([80.0, 80.0]).down(80.0).set(RECTANGLE_OUTLINE, ui);

    let bl = [-40.0, -40.0];
    let tl = [-20.0, 40.0];
    let tr = [20.0, 40.0];
    let br = [40.0, -40.0];
    let points = once(bl).chain(once(tl)).chain(once(tr)).chain(once(br));
    Polygon::centred_fill(points).right_from(LINE, 80.0).set(TRAPEZOID, ui);

    Oval::fill([40.0, 80.0]).down(80.0).align_middle_x().set(OVAL_FILL, ui);

    Oval::outline([80.0, 40.0]).down(100.0).align_middle_x().set(OVAL_OUTLINE, ui);

    Circle::fill(40.0).down(100.0).align_middle_x().set(CIRCLE, ui);

//    XYPad::new(0.0, 0.0, 200.0, 0.0, 0.0, 200.0).w_h(150.0, 150.0).set(XYPAD, ui);
}

pub fn escape_default(s: &str) -> String {
    s.chars().flat_map(|c| c.escape_default()).collect()
}

fn walk(prefix: &str, ui: &mut UiCell, handle: Handle) {
    let node = handle.borrow();

    print!("{}", prefix);
    match node.node {
        Document => println!("#document"),

        rcdom::Text(ref text) => println!("#text {}", escape_default(text)),

        Element(ref name, ref attrs) => {
        	let lname = name.local.as_ref();
            println!("{:?}", lname);
            match lname {
            	"rect" => {
            		let mut attr_it = attrs.into_iter();
//            		let x = attr_it.find(|&a| a.name.local.as_ref() == "x");
//            		let y = attr_it.find(|&a| a.name.local.as_ref() == "y");
            		let width = attr_it.inspect(|&x| println!("{:?}", x)).find(|&a| a.name.local.as_ref() == "width");
//            		let height = attr_it.find(|&a| a.name.local.as_ref() == "height");
//            		let x = attr_it.find(|&a| a.name.local.as_ref() == "rx");
//            		let y = attr_it.find(|&a| a.name.local.as_ref() == "ry");
//                	println!("{:?}", x);
//                	println!("{:?}", y);
                	println!("{:?}", width);
//                	println!("{:?}", height);
            	}
            	_ => {}
            }
        }
        _ => {}
    }

    let new_indent = {
        let mut temp = String::new();
        temp.push_str(prefix);
        temp.push_str("    ");
        temp
    };

    for child in node.children
        .iter()
        .filter(|child| match child.borrow().node {
            rcdom::Text(_) | Element(_, _) => true,
            _ => false,
        }) {
        walk(&new_indent, ui, child.clone());
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate clap;
extern crate tempdir;
extern crate rand;
extern crate base32;

use std::fs;

use clap::{Arg, SubCommand};

use tempdir::TempDir;

struct CmdErr {
    message: String,
    secondary: Option<String>,
}

trait ToCmdErr<T> {
    fn check(self, message: &str) -> Result<T, CmdErr>;
}

impl<T, E: ::std::fmt::Display> ToCmdErr<T> for Result<T, E> {
    fn check(self, message: &str) -> Result<T, CmdErr> {
        self.map_err(|e| CmdErr {
            message: message.to_owned(),
            secondary: Some(format!("{}", e)),
        })
    }
}

impl<T> ToCmdErr<T> for Option<T> {
    fn check(self, message: &str) -> Result<T, CmdErr> {
        match self {
            Some(t) => Ok(t),
            None => Err(CmdErr {
                message: message.to_owned(),
                secondary: None,
            }),
        }
    }
}

impl ToCmdErr<()> for bool {
    fn check(self, message: &str) -> Result<(), CmdErr> {
        if self {
            Ok(())
        } else {
            Err(CmdErr {
                message: message.to_owned(),
                secondary: None,
            })
        }
    }
}

fn cmd_run(image: &str, rootless: bool, volumes: &[(&str, &str)]) -> Result<Option<i32>, CmdErr> {
    if volumes.len() > 0 {
        return Err(CmdErr { message: "Volume bind-mounts are not supported yet".to_owned(), secondary: None });
    }

    let tmp_dir = TempDir::new("shipc").check("Cannot create temporary directory")?;
    eprintln!("info: Temporary directory: {:?}", tmp_dir.path());
    eprint!("info: Running image {}", image);
    if rootless {
        eprint!(" in rootless mode");
    }
    eprintln!();
    for &(ref orig, ref dest) in volumes {
        eprintln!("info:   with volume {}:{}", orig, dest);
    }
    let image_path = ::std::path::Path::new(image);
    let image_metadata = fs::metadata(image).check("Invalid image path")?;
    let mut image_dir_path = image_path.to_path_buf();

    // Support for .tar.gz images: un-tar them in the temporary directory
    if image_metadata.file_type().is_file() {
        image_path.file_name().expect("Filename is expected for a file")
            .to_string_lossy().ends_with(".tar.gz").check("File is not a tarball")?;
        image_dir_path = tmp_dir.path().join("image");
        fs::create_dir(&image_dir_path).check("Internal error")?;
        eprintln!("info: Uncompressing image into {}", image_dir_path.to_string_lossy());
        let tar_output = ::std::process::Command::new("tar")
            .arg("--strip-components")
            .arg("1")
            .arg("-C")
            .arg(&image_dir_path)
            .arg("-xf")
            .arg(image_path)
            .output()
            .check("Cannot execute tar")?;
        if !tar_output.status.success() {
            return Err(CmdErr {
                message: "Failed to un-tar image".to_owned(),
                secondary: Some(String::from_utf8_lossy(&tar_output.stderr).into_owned()),
            });
        }
    }

    let bundle = "bundle";

    let bundle_path = tmp_dir.path().join(bundle);
    eprintln!("info: Unpacking image to {}", bundle_path.to_string_lossy());

    let mut umoci_command = ::std::process::Command::new("umoci");
    umoci_command
        .current_dir(tmp_dir.path())
        .arg("unpack")
        .arg("--image")
        .arg(&image_dir_path.file_name().expect("The image directory must have a last component"));
    if rootless {
        umoci_command.arg("--rootless");
    }
    umoci_command.arg(bundle);

    let umoci_output = umoci_command.output()
        .check("Cannot run umoci")?;

    if !umoci_output.status.success() {
        return Err(CmdErr {
            message: "Failed to unpack image with umoci".to_owned(),
            secondary: Some(String::from_utf8_lossy(&umoci_output.stderr).into_owned()),
        });
    }

    let container_name = {
        use rand::Rng;
        let bytes: [u8; 4] = unsafe { ::std::mem::transmute(rand::thread_rng().gen::<u32>().to_be()) };
        ::base32::encode(::base32::Alphabet::RFC4648 { padding: false }, &bytes[..])
    };

    let mut runc_command = ::std::process::Command::new("runc");
    runc_command
        .current_dir(bundle_path);
    if rootless {
        let root_dir_path = tmp_dir.path().join("root");
        fs::create_dir(&root_dir_path).check("Internal error")?;
        runc_command
            .arg("--root")
            .arg(&root_dir_path);
    }
    runc_command
        .arg("run")
        .arg(&container_name);

    eprintln!("info: Starting container with name {}", &container_name);
    eprintln!("info: If runc is successful, the next line will be inside the container");

    let runc_status = runc_command.status().check("Failed to start the container")?;

    eprintln!("info: Container exited with {}", runc_status);

    Ok(runc_status.code())
}

fn fail(message: &str) {
    eprintln!("error: {}", message);
    eprintln!("error: Run with --help for usage");
    ::std::process::exit(-1);
}

fn check(condition: bool, message: &str) {
    if !condition {
        fail(message);
    }
}

fn main() {
    let app = app_from_crate!()
        .about("Unpack and run oci images")
        .subcommand(SubCommand::with_name("run")
                    .about("Run an oci image")
                    .arg(Arg::with_name("image")
                         .required(true))
                    .arg(Arg::with_name("rootless")
                         .help("Run in rootless mode")
                         .long("rootless"))
                    .arg(Arg::with_name("volume")
                         .help("Rebind volumes")
                         .long("volume")
                         .short("v")
                         .takes_value(true)
                         .value_name("VOLUME")
                         .multiple(true)));
    let matches = app.clone().get_matches();

    let result = match matches.subcommand() {
        ("run", Some(sub_matches)) => {
            let rootless: bool = sub_matches.is_present("rootless");
            let image = sub_matches.value_of("image").unwrap();
            let volumes = sub_matches.values_of("volume").map(|vol_strs| {
                vol_strs.map(|s| {
                    let vol_arr: Vec<&str> = s.split(":").collect();
                    check(vol_arr.len() == 2, "Volume should be origin:destination");
                    let orig = vol_arr[0];
                    let dest = vol_arr[1];
                    (orig, dest)
                }).collect::<Vec<_>>()
            }).unwrap_or(Vec::new());
            cmd_run(image, rootless, &volumes[..])
        },
        _ => {
            let mut out = ::std::io::stdout();
            app.write_help(&mut out).ok().expect("error: Failed to print usage to stdout");
            println!();
            ::std::process::exit(-1);
        },
    };
    match result {
        Ok(exit_code) => ::std::process::exit(exit_code.unwrap_or(-1)),
        Err(err) => {
            eprintln!("error: {}", err.message);
            if let Some(secondary) = err.secondary {
                eprintln!("error: {}", secondary);
            }
            eprintln!("error: Run with --help for usage");
        },
    }
}
<commit_msg>Fix for filesystem bundle (do not assume image is in /tmp)<commit_after>#[macro_use] extern crate clap;
extern crate tempdir;
extern crate rand;
extern crate base32;

use std::fs;

use clap::{Arg, SubCommand};

use tempdir::TempDir;

struct CmdErr {
    message: String,
    secondary: Option<String>,
}

trait ToCmdErr<T> {
    fn check(self, message: &str) -> Result<T, CmdErr>;
}

impl<T, E: ::std::fmt::Display> ToCmdErr<T> for Result<T, E> {
    fn check(self, message: &str) -> Result<T, CmdErr> {
        self.map_err(|e| CmdErr {
            message: message.to_owned(),
            secondary: Some(format!("{}", e)),
        })
    }
}

impl<T> ToCmdErr<T> for Option<T> {
    fn check(self, message: &str) -> Result<T, CmdErr> {
        match self {
            Some(t) => Ok(t),
            None => Err(CmdErr {
                message: message.to_owned(),
                secondary: None,
            }),
        }
    }
}

impl ToCmdErr<()> for bool {
    fn check(self, message: &str) -> Result<(), CmdErr> {
        if self {
            Ok(())
        } else {
            Err(CmdErr {
                message: message.to_owned(),
                secondary: None,
            })
        }
    }
}

fn cmd_run(image: &str, rootless: bool, volumes: &[(&str, &str)]) -> Result<Option<i32>, CmdErr> {
    if volumes.len() > 0 {
        return Err(CmdErr { message: "Volume bind-mounts are not supported yet".to_owned(), secondary: None });
    }

    let tmp_dir = TempDir::new("shipc").check("Cannot create temporary directory")?;
    eprintln!("info: Temporary directory: {:?}", tmp_dir.path());
    eprint!("info: Running image {}", image);
    if rootless {
        eprint!(" in rootless mode");
    }
    eprintln!();
    for &(ref orig, ref dest) in volumes {
        eprintln!("info:   with volume {}:{}", orig, dest);
    }
    let image_path = fs::canonicalize(::std::path::Path::new(image)).check("Image path is invalid")?;
    let image_metadata = fs::metadata(image).check("Invalid image path")?;
    let mut image_dir_path = image_path.to_path_buf();

    // Support for .tar.gz images: un-tar them in the temporary directory
    if image_metadata.file_type().is_file() {
        image_path.file_name().expect("Filename is expected for a file")
            .to_string_lossy().ends_with(".tar.gz").check("File is not a tarball")?;
        image_dir_path = tmp_dir.path().join("image");
        fs::create_dir(&image_dir_path).check("Internal error")?;
        eprintln!("info: Uncompressing image into {}", image_dir_path.to_string_lossy());
        let tar_output = ::std::process::Command::new("tar")
            .arg("--strip-components")
            .arg("1")
            .arg("-C")
            .arg(&image_dir_path)
            .arg("-xf")
            .arg(image_path)
            .output()
            .check("Cannot execute tar")?;
        if !tar_output.status.success() {
            return Err(CmdErr {
                message: "Failed to un-tar image".to_owned(),
                secondary: Some(String::from_utf8_lossy(&tar_output.stderr).into_owned()),
            });
        }
    }

    let bundle = "bundle";

    let bundle_path = tmp_dir.path().join(bundle);
    eprintln!("info: Unpacking image {} to {}", image_dir_path.to_string_lossy(), bundle_path.to_string_lossy());

    let mut umoci_command = ::std::process::Command::new("umoci");
    umoci_command
        .current_dir(tmp_dir.path())
        .arg("unpack")
        .arg("--image")
        .arg(&image_dir_path);
    if rootless {
        umoci_command.arg("--rootless");
    }
    umoci_command.arg(bundle);

    let umoci_output = umoci_command.output()
        .check("Cannot run umoci")?;

    if !umoci_output.status.success() {
        return Err(CmdErr {
            message: "Failed to unpack image with umoci".to_owned(),
            secondary: Some(String::from_utf8_lossy(&umoci_output.stderr).into_owned()),
        });
    }

    let container_name = {
        use rand::Rng;
        let bytes: [u8; 4] = unsafe { ::std::mem::transmute(rand::thread_rng().gen::<u32>().to_be()) };
        ::base32::encode(::base32::Alphabet::RFC4648 { padding: false }, &bytes[..])
    };

    let mut runc_command = ::std::process::Command::new("runc");
    runc_command
        .current_dir(bundle_path);
    if rootless {
        let root_dir_path = tmp_dir.path().join("root");
        fs::create_dir(&root_dir_path).check("Internal error")?;
        runc_command
            .arg("--root")
            .arg(&root_dir_path);
    }
    runc_command
        .arg("run")
        .arg(&container_name);

    eprintln!("info: Starting container with name {}", &container_name);
    eprintln!("info: If runc is successful, the next line will be inside the container");

    let runc_status = runc_command.status().check("Failed to start the container")?;

    eprintln!("info: Container exited with {}", runc_status);

    Ok(runc_status.code())
}

fn fail(message: &str) {
    eprintln!("error: {}", message);
    eprintln!("error: Run with --help for usage");
    ::std::process::exit(-1);
}

fn check(condition: bool, message: &str) {
    if !condition {
        fail(message);
    }
}

fn main() {
    let app = app_from_crate!()
        .about("Unpack and run oci images")
        .subcommand(SubCommand::with_name("run")
                    .about("Run an oci image")
                    .arg(Arg::with_name("image")
                         .required(true))
                    .arg(Arg::with_name("rootless")
                         .help("Run in rootless mode")
                         .long("rootless"))
                    .arg(Arg::with_name("volume")
                         .help("Rebind volumes")
                         .long("volume")
                         .short("v")
                         .takes_value(true)
                         .value_name("VOLUME")
                         .multiple(true)));
    let matches = app.clone().get_matches();

    let result = match matches.subcommand() {
        ("run", Some(sub_matches)) => {
            let rootless: bool = sub_matches.is_present("rootless");
            let image = sub_matches.value_of("image").unwrap();
            let volumes = sub_matches.values_of("volume").map(|vol_strs| {
                vol_strs.map(|s| {
                    let vol_arr: Vec<&str> = s.split(":").collect();
                    check(vol_arr.len() == 2, "Volume should be origin:destination");
                    let orig = vol_arr[0];
                    let dest = vol_arr[1];
                    (orig, dest)
                }).collect::<Vec<_>>()
            }).unwrap_or(Vec::new());
            cmd_run(image, rootless, &volumes[..])
        },
        _ => {
            let mut out = ::std::io::stdout();
            app.write_help(&mut out).ok().expect("error: Failed to print usage to stdout");
            println!();
            ::std::process::exit(-1);
        },
    };
    match result {
        Ok(exit_code) => ::std::process::exit(exit_code.unwrap_or(-1)),
        Err(err) => {
            eprintln!("error: {}", err.message);
            if let Some(secondary) = err.secondary {
                eprintln!("error: {}", secondary);
            }
            eprintln!("error: Run with --help for usage");
        },
    }
}
<|endoftext|>


<commit_before>extern crate ansi_term;
extern crate docopt;
extern crate rustc_serialize;

use ansi_term::Colour;
use docopt::Docopt;

use std::collections;
use std::io;
use std::path;
use std::process;

use std::os::unix::fs::FileTypeExt;

const USAGE: &'static str = "
srch, a command-line file search utility written in Rust.

Usage:
    srch [options] [<path>] <pattern>
    srch --help
    srch --version

Options:
    -h, --help              Show this help screen
    -v, --version           Show this program's version
    -i, --invisible         Also search inside directories starting with a . character
    -f, --filesonly         Only search filenames and NOT directory names
";

#[derive(RustcDecodable)]
struct Args {
    arg_pattern: String,
    arg_path: String,

    flag_i: bool,
    flag_invisible: bool,

    flag_f: bool,
    flag_filesonly: bool,
}

fn main() {
    let args: Args = Docopt::new(USAGE).unwrap()
                            .help(true)
                            .version(Some(String::from("srch ") + option_env!("CARGO_PKG_VERSION").unwrap_or("unknown")))
                            .decode()
                            .unwrap_or_else(|e| e.exit());

    let dir = path::PathBuf::from(&args.arg_path);
    if args.arg_path.is_empty() {
        let curdir = path::PathBuf::from(".");
        handle(&curdir, &args);
    } else if !dir.exists() {
        println!("Invalid search path: {}", Colour::Red.bold().paint(args.arg_path.as_str()));
        process::exit(1);
    } else {
        handle(&dir, &args);
    }
}

fn ignore (path: &path::PathBuf, args: &Args) -> io::Result<bool> {
    if !path.is_dir() {
        return Ok(true)
    }

    let m = try!(path.symlink_metadata()).file_type();
    if m.is_symlink() || m.is_block_device() || m.is_char_device() || m.is_fifo() || m.is_socket() {
        return Ok(true)
    }

    if args.flag_i || args.flag_invisible {
        return Ok(false)
    }

    let fname = path.file_name();
    if fname == None {
        return Ok(false)
    }

    let fname = fname.unwrap().to_str().unwrap();
    if fname.chars().next().unwrap() == '.' {
        return Ok(true)
    }

    Ok(false)
}

trait Misc {
    fn display_colour(&self) -> io::Result<String>;
    fn matches(&self, pattern: &String) -> bool;
}

impl Misc for path::PathBuf {
    fn display_colour(&self) -> io::Result<String> {
        let s: &str = self.to_str().unwrap();

        if self.is_dir() {
            Ok(Colour::Purple.paint(s).to_string())
        } else {
            Ok(s.to_string())
        }
    }

    fn matches(&self, pattern: &String) -> bool {
        let fname = self.file_name();
        if fname == None {
            return false
        }

        let fname = fname.unwrap().to_str().unwrap().to_string();
        if fname == *pattern {
            return true
        }
        false
    }
}

struct SearchResults {
    directories: i32,
    files: i32,

    scanned: i32,
    pushed: i32,
}

impl SearchResults {
    fn add(&mut self, other: SearchResults) {
        self.directories += other.directories;
        self.files += other.files;

        self.scanned += other.scanned;
        self.pushed += other.pushed;
    }
}

fn handle(path: &path::PathBuf, args: &Args) -> () {
    let mut q: collections::VecDeque<path::PathBuf> = collections::VecDeque::new();
    let mut results = SearchResults{directories: 0, files: 0, scanned: 0, pushed: 1};

    let pattern = &args.arg_pattern;

    println!("Searching {} for {}", Colour::Yellow.bold().paint(path.to_str().unwrap()), Colour::Green.bold().paint(pattern.as_str()));
    q.push_back(path.clone());
    while q.len() > 0 {
        let p = q.pop_front().unwrap();
        let i = ignore(&p, args);
        match i {
            Ok(tf) => {
                if tf {
                    continue;
                }
            },
            Err(_) => continue,
        };

        let r = search(&mut q, &p, pattern, args.flag_f || args.flag_filesonly);
        match r {
            Ok(n) => {
                results.add(n);
            },
            Err(_) => (),
        };
    }

    print!("Explored {} directories and searched {} objects, ", Colour::Yellow.bold().paint(results.pushed.to_string()), Colour::Yellow.bold().paint(results.scanned.to_string()));
    println!("found {} directories and {} files", Colour::Green.bold().paint(results.directories.to_string()), Colour::Green.bold().paint(results.files.to_string()));
}

fn search(q: &mut collections::VecDeque<path::PathBuf>, path: &path::PathBuf, pattern: &String, ignore_dirs: bool) -> std::io::Result<SearchResults> {
    let mut results = SearchResults{directories: 0, files: 0, scanned: 0, pushed: 0};

    for item in try!(path.read_dir()) {
        let f = try!(item); // f is a DirEntry
        let dir: bool;
        if f.path().is_dir() {
            results.pushed += 1;
            q.push_back(f.path());
            if ignore_dirs {
                continue;
            }
            dir = true;
            results.scanned += 1;
        } else {
            dir = false;
            results.scanned += 1;
        }

        if f.path().matches(&pattern) {
            println!("{}", try!(f.path().display_colour()));
            if dir {
                results.directories += 1;
            } else {
                results.files += 1;
            }
        }
    }

    Ok(results)
}
<commit_msg>cargo fmt<commit_after>extern crate ansi_term;
extern crate docopt;
extern crate rustc_serialize;

use ansi_term::Colour;
use docopt::Docopt;

use std::collections;
use std::io;
use std::path;
use std::process;

use std::os::unix::fs::FileTypeExt;

const USAGE: &'static str = "
srch, a command-line file search utility written in Rust.

Usage:
    srch [options] [<path>] <pattern>
    srch --help
    srch --version

Options:
    -h, --help              Show this help screen
    -v, --version           Show this program's version
    -i, --invisible         Also search inside directories starting with a . character
    -f, --filesonly         Only search filenames and NOT directory names
";

#[derive(RustcDecodable)]
struct Args {
    arg_pattern: String,
    arg_path: String,

    flag_i: bool,
    flag_invisible: bool,

    flag_f: bool,
    flag_filesonly: bool,
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .unwrap()
        .help(true)
        .version(Some(String::from("srch ") +
                      option_env!("CARGO_PKG_VERSION").unwrap_or("unknown")))
        .decode()
        .unwrap_or_else(|e| e.exit());

    let dir = path::PathBuf::from(&args.arg_path);
    if args.arg_path.is_empty() {
        let curdir = path::PathBuf::from(".");
        handle(&curdir, &args);
    } else if !dir.exists() {
        println!("Invalid search path: {}",
                 Colour::Red.bold().paint(args.arg_path.as_str()));
        process::exit(1);
    } else {
        handle(&dir, &args);
    }
}

fn ignore(path: &path::PathBuf, args: &Args) -> io::Result<bool> {
    if !path.is_dir() {
        return Ok(true);
    }

    let m = try!(path.symlink_metadata()).file_type();
    if m.is_symlink() || m.is_block_device() || m.is_char_device() || m.is_fifo() || m.is_socket() {
        return Ok(true);
    }

    if args.flag_i || args.flag_invisible {
        return Ok(false);
    }

    let fname = path.file_name();
    if fname == None {
        return Ok(false);
    }

    let fname = fname.unwrap().to_str().unwrap();
    if fname.chars().next().unwrap() == '.' {
        return Ok(true);
    }

    Ok(false)
}

trait Misc {
    fn display_colour(&self) -> io::Result<String>;
    fn matches(&self, pattern: &String) -> bool;
}

impl Misc for path::PathBuf {
    fn display_colour(&self) -> io::Result<String> {
        let s: &str = self.to_str().unwrap();

        if self.is_dir() {
            Ok(Colour::Purple.paint(s).to_string())
        } else {
            Ok(s.to_string())
        }
    }

    fn matches(&self, pattern: &String) -> bool {
        let fname = self.file_name();
        if fname == None {
            return false;
        }

        let fname = fname.unwrap().to_str().unwrap().to_string();
        if fname == *pattern {
            return true;
        }
        false
    }
}

struct SearchResults {
    directories: i32,
    files: i32,

    scanned: i32,
    pushed: i32,
}

impl SearchResults {
    fn add(&mut self, other: SearchResults) {
        self.directories += other.directories;
        self.files += other.files;

        self.scanned += other.scanned;
        self.pushed += other.pushed;
    }
}

fn handle(path: &path::PathBuf, args: &Args) -> () {
    let mut q: collections::VecDeque<path::PathBuf> = collections::VecDeque::new();
    let mut results = SearchResults {
        directories: 0,
        files: 0,
        scanned: 0,
        pushed: 1,
    };

    let pattern = &args.arg_pattern;

    println!("Searching {} for {}",
             Colour::Yellow.bold().paint(path.to_str().unwrap()),
             Colour::Green.bold().paint(pattern.as_str()));
    q.push_back(path.clone());
    while q.len() > 0 {
        let p = q.pop_front().unwrap();
        let i = ignore(&p, args);
        match i {
            Ok(tf) => {
                if tf {
                    continue;
                }
            }
            Err(_) => continue,
        };

        let r = search(&mut q, &p, pattern, args.flag_f || args.flag_filesonly);
        match r {
            Ok(n) => {
                results.add(n);
            }
            Err(_) => (),
        };
    }

    print!("Explored {} directories and searched {} objects, ",
           Colour::Yellow.bold().paint(results.pushed.to_string()),
           Colour::Yellow.bold().paint(results.scanned.to_string()));
    println!("found {} directories and {} files",
             Colour::Green.bold().paint(results.directories.to_string()),
             Colour::Green.bold().paint(results.files.to_string()));
}

fn search(q: &mut collections::VecDeque<path::PathBuf>,
          path: &path::PathBuf,
          pattern: &String,
          ignore_dirs: bool)
          -> std::io::Result<SearchResults> {
    let mut results = SearchResults {
        directories: 0,
        files: 0,
        scanned: 0,
        pushed: 0,
    };

    for item in try!(path.read_dir()) {
        let f = try!(item); // f is a DirEntry
        let dir: bool;
        if f.path().is_dir() {
            results.pushed += 1;
            q.push_back(f.path());
            if ignore_dirs {
                continue;
            }
            dir = true;
            results.scanned += 1;
        } else {
            dir = false;
            results.scanned += 1;
        }

        if f.path().matches(&pattern) {
            println!("{}", try!(f.path().display_colour()));
            if dir {
                results.directories += 1;
            } else {
                results.files += 1;
            }
        }
    }

    Ok(results)
}
<|endoftext|>


<commit_before>extern crate rand;

use std::io::Read;
use std::fs::File;
use rand::Rng;

const DICT_FILE: &'static str = "/usr/share/dict/words";

fn main() {
    let mut dict = File::open(DICT_FILE).unwrap();
    // Not ideal, but whatever.
    let mut buf = String::new();
    dict.read_to_string(&mut buf);

    let mut lines: Vec<_> = buf
        .lines()
        .filter(|x| x.len() > 3 && x.len() < 7)
        .collect();
    rand::thread_rng().shuffle(&mut lines);
    for i in (0..3) {
        print!("{} ", lines[i]);
    }
    println!("{}", lines[4]);
}
<commit_msg>Refactor, gear up for options<commit_after>extern crate rand;

use std::io::{self, Read};
use std::fs::File;
use rand::Rng;

const DEFAULT_DICT_FILE: &'static str = "/usr/share/dict/words";

struct Config {
    min: usize,
    max: usize,
    number: usize,
    count: usize,
    filename: String,
}

impl Config {
    fn new() -> Config {
        Config {
            min: 4,
            max: 6,
            number: 4,
            count: 1,
            filename: DEFAULT_DICT_FILE.to_string(),
        }
    }
}

fn read_words(filename: &String) -> io::Result<String> {
    let mut dict = File::open(filename).unwrap();
    let mut buf = String::new();
    try!(dict.read_to_string(&mut buf));
    Ok(buf)
}

fn shuffled_words<'a>(source: &'a String, cfg: &Config) -> Vec<&'a str> {
    // Not ideal, but whatever.

    let mut lines: Vec<_> = source
        .lines()
        .filter(|x| x.len() >= cfg.min && x.len() <= cfg.max)
        .collect();
    rand::thread_rng().shuffle(&mut lines);
    lines
}

fn main() {
    let cfg = Config::new();
    let words = read_words(&cfg.filename).unwrap();

    for _ in (0..cfg.count) {
        let shuffled = shuffled_words(&words, &cfg);

        for i in (0..(cfg.number-1)) {
            print!("{} ", shuffled[i]);
        }
        println!("{}", shuffled[cfg.number]);
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate env_logger;
extern crate rustylnk;
extern crate clap;
extern crate seek_bufread;
extern crate rwinstructs;
extern crate jmespath;
extern crate serde_json;
extern crate serde;
use rwinstructs::reference;
use rwinstructs::serialize;
use clap::{App, Arg, ArgMatches};
use jmespath::{Expression};
use seek_bufread::BufReader;
use rustylnk::lnkpkg::lnk::{Lnk};
use std::fs;

fn process_directory(directory: &str, options: &ArgMatches) {
    for dir_reader in fs::read_dir(directory) {
        for entry_result in dir_reader {
            match entry_result {
                Ok(entry) => {
                    let path = entry.path();
                    if path.is_file() {
                        let path_string = path.into_os_string().into_string().unwrap();
                        if path_string.to_lowercase().ends_with(".lnk"){
                            process_file(&path_string, &options);
                        }
                    } else if path.is_dir(){
                        let path_string = path.into_os_string().into_string().unwrap();
                        process_directory(&path_string, &options);
                    }
                },
                Err(error) => {
                    error!("Error reading {} [{:?}]",directory,error);
                }
            }
        }
    }
}

fn process_file(filename: &str, options: &ArgMatches) -> bool {
    // JMES Expression if needed
    let mut expr: Option<Expression> = None;
    if options.is_present("query") {
        expr = Some(jmespath::compile(
            options.value_of("query").unwrap()
        ).unwrap());
    }

    // Expression bool flag
    let mut expr_as_bool = false;
    if options.is_present("bool_expr"){
        expr_as_bool = true;
    }

    let file = match fs::File::open(filename) {
        Ok(file) => file,
        Err(error) => {
            error!("Could not open file: {} [error: {}]", filename, error);
            return false;
        }
    };

    let reader = BufReader::new(file);

    info!("Parsing file: {}",filename);
    let lnk = match Lnk::new(reader) {
        Ok(lnk) => lnk,
        Err(error) => {
            error!("Could not parse file: {} [{}]", filename, error);
            return false;
        }
    };

    let json_str = serde_json::to_string(&lnk).unwrap();
    match expr {
        Some(ref j_expr) => {
            let data = jmespath::Variable::from_json(&json_str).unwrap();
            let result = j_expr.search(data).unwrap();
            if expr_as_bool {
                match result.as_boolean() {
                    Some(bool_value) => {
                        match bool_value {
                            true => println!("{}",json_str),
                            false => {}
                        }
                    },
                    None => {
                        error!("Query expression is not a bool expression! [{}]",options.value_of("query").unwrap());
                    }
                }
            } else {
                println!("{}",result)
            }
        },
        None => {
            println!("{}",json_str);
        }
    }

    return true;
}

fn is_directory(source: &str)->bool{
    let metadata = fs::metadata(source).unwrap();

    let file_type = metadata.file_type();

    file_type.is_dir()
}

fn main() {
    env_logger::init().unwrap();

    let source_arg = Arg::with_name("source")
        .short("s")
        .long("source")
        .value_name("PATH")
        .help("A file or folder to to parse lnk files (looks for .lnk extention and is recursive)")
        .required(true)
        .takes_value(true);

    let jmes_arg = Arg::with_name("query")
        .short("q")
        .long("query")
        .value_name("QUERY")
        .help("JMES Query")
        .takes_value(true);

    let bool_arg = Arg::with_name("bool_expr")
        .short("b")
        .long("bool_expr")
        .help("JMES Query as bool only. (Prints whole record if true.)");

    let options = App::new("RusyLnk")
        .version("0.1.0")
        .author("Matthew Seyer <https://github.com/forensicmatt/RustyLnk>")
        .about("LNK Parser written in Rust.")
        .arg(source_arg)
        .arg(jmes_arg)
        .arg(bool_arg)
        .get_matches();

    // Set Reference Display Options
    unsafe{reference::NESTED_REFERENCE = true;}
    unsafe{serialize::U64_SERIALIZATION = serialize::U64Serialization::AsString;}

    let source = options.value_of("source").unwrap();

    if is_directory(source) {
        process_directory(source,&options);
    } else {
        process_file(source,&options);
    }
}
<commit_msg>upped version for cli output<commit_after>#[macro_use] extern crate log;
extern crate env_logger;
extern crate rustylnk;
extern crate clap;
extern crate seek_bufread;
extern crate rwinstructs;
extern crate jmespath;
extern crate serde_json;
extern crate serde;
use rwinstructs::reference;
use rwinstructs::serialize;
use clap::{App, Arg, ArgMatches};
use jmespath::{Expression};
use seek_bufread::BufReader;
use rustylnk::lnkpkg::lnk::{Lnk};
use std::fs;

fn process_directory(directory: &str, options: &ArgMatches) {
    for dir_reader in fs::read_dir(directory) {
        for entry_result in dir_reader {
            match entry_result {
                Ok(entry) => {
                    let path = entry.path();
                    if path.is_file() {
                        let path_string = path.into_os_string().into_string().unwrap();
                        if path_string.to_lowercase().ends_with(".lnk"){
                            process_file(&path_string, &options);
                        }
                    } else if path.is_dir(){
                        let path_string = path.into_os_string().into_string().unwrap();
                        process_directory(&path_string, &options);
                    }
                },
                Err(error) => {
                    error!("Error reading {} [{:?}]",directory,error);
                }
            }
        }
    }
}

fn process_file(filename: &str, options: &ArgMatches) -> bool {
    // JMES Expression if needed
    let mut expr: Option<Expression> = None;
    if options.is_present("query") {
        expr = Some(jmespath::compile(
            options.value_of("query").unwrap()
        ).unwrap());
    }

    // Expression bool flag
    let mut expr_as_bool = false;
    if options.is_present("bool_expr"){
        expr_as_bool = true;
    }

    let file = match fs::File::open(filename) {
        Ok(file) => file,
        Err(error) => {
            error!("Could not open file: {} [error: {}]", filename, error);
            return false;
        }
    };

    let reader = BufReader::new(file);

    info!("Parsing file: {}",filename);
    let lnk = match Lnk::new(reader) {
        Ok(lnk) => lnk,
        Err(error) => {
            error!("Could not parse file: {} [{}]", filename, error);
            return false;
        }
    };

    let json_str = serde_json::to_string(&lnk).unwrap();
    match expr {
        Some(ref j_expr) => {
            let data = jmespath::Variable::from_json(&json_str).unwrap();
            let result = j_expr.search(data).unwrap();
            if expr_as_bool {
                match result.as_boolean() {
                    Some(bool_value) => {
                        match bool_value {
                            true => println!("{}",json_str),
                            false => {}
                        }
                    },
                    None => {
                        error!("Query expression is not a bool expression! [{}]",options.value_of("query").unwrap());
                    }
                }
            } else {
                println!("{}",result)
            }
        },
        None => {
            println!("{}",json_str);
        }
    }

    return true;
}

fn is_directory(source: &str)->bool{
    let metadata = fs::metadata(source).unwrap();

    let file_type = metadata.file_type();

    file_type.is_dir()
}

fn main() {
    env_logger::init().unwrap();

    let source_arg = Arg::with_name("source")
        .short("s")
        .long("source")
        .value_name("PATH")
        .help("A file or folder to to parse lnk files (looks for .lnk extention and is recursive)")
        .required(true)
        .takes_value(true);

    let jmes_arg = Arg::with_name("query")
        .short("q")
        .long("query")
        .value_name("QUERY")
        .help("JMES Query")
        .takes_value(true);

    let bool_arg = Arg::with_name("bool_expr")
        .short("b")
        .long("bool_expr")
        .help("JMES Query as bool only. (Prints whole record if true.)");

    let options = App::new("RusyLnk")
        .version("0.1.1")
        .author("Matthew Seyer <https://github.com/forensicmatt/RustyLnk>")
        .about("LNK Parser written in Rust.")
        .arg(source_arg)
        .arg(jmes_arg)
        .arg(bool_arg)
        .get_matches();

    // Set Reference Display Options
    unsafe{reference::NESTED_REFERENCE = true;}
    unsafe{serialize::U64_SERIALIZATION = serialize::U64Serialization::AsString;}

    let source = options.value_of("source").unwrap();

    if is_directory(source) {
        process_directory(source,&options);
    } else {
        process_file(source,&options);
    }
}
<|endoftext|>


<commit_before>#![feature(custom_test_frameworks)]
#![no_main]
#![no_std]
#![reexport_test_harness_main = "test_main"]
#![test_runner(crate::test_runner)]

mod serial;
mod vga;

#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
    serial_println!("Running {} tests", tests.len());
    for test in tests {
        test();
    }
    exit_qemu(QemuExitCode::Success);
}

#[cfg(not(test))] // new attribute
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    println!("{}", info);
    loop {}
}

#[cfg(test)]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    serial_println!("[failed]\n");
    serial_println!("Error: {}\n", info);
    exit_qemu(QemuExitCode::Failed);
    loop {}
}

#[no_mangle]
pub extern "C" fn _start() -> ! {
    println!("Hello World{}", "!");

    #[cfg(test)]
    test_main();

    loop {}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
    Success = 0x10,
    Failed = 0x11,
}

pub fn exit_qemu(exit_code: QemuExitCode) {
    use x86_64::instructions::port::Port;

    unsafe {
        let mut port = Port::new(0xf4);
        port.write(exit_code as u32);
    }
}

#[test_case]
fn trivial_assertion() {
    serial_print!("trivial assertion... ");
    assert_eq!(1, 1);
    serial_println!("[ok]");
}
<commit_msg>main: nuke trivial assertion<commit_after>#![feature(custom_test_frameworks)]
#![no_main]
#![no_std]
#![reexport_test_harness_main = "test_main"]
#![test_runner(crate::test_runner)]

mod serial;
mod vga;

#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
    serial_println!("Running {} tests", tests.len());
    for test in tests {
        test();
    }
    exit_qemu(QemuExitCode::Success);
}

#[cfg(not(test))] // new attribute
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    println!("{}", info);
    loop {}
}

#[cfg(test)]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
    serial_println!("[failed]\n");
    serial_println!("Error: {}\n", info);
    exit_qemu(QemuExitCode::Failed);
    loop {}
}

#[no_mangle]
pub extern "C" fn _start() -> ! {
    println!("Hello World{}", "!");

    #[cfg(test)]
    test_main();

    loop {}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
    Success = 0x10,
    Failed = 0x11,
}

pub fn exit_qemu(exit_code: QemuExitCode) {
    use x86_64::instructions::port::Port;

    unsafe {
        let mut port = Port::new(0xf4);
        port.write(exit_code as u32);
    }
}
<|endoftext|>


<commit_before>extern crate env_logger;
extern crate octobot;
extern crate time;
extern crate thread_id;
extern crate log;

use octobot::config;
use octobot::server;

use env_logger::LogBuilder;
use log::{LogLevelFilter, LogRecord};

fn main() {
    if std::env::args().len() < 2 {
        panic!("Usage: octobot <config-file>")
    }

    setup_logging();

    let config_file = std::env::args().nth(1).unwrap();
    let config = config::parse(&config_file).expect("Error parsing config");

    server::start(config).expect("Failed to start server");
}

fn setup_logging() {
    let formatter = |record: &LogRecord| {
        let t = time::now();
        format!(
            "[{},{:03}][{}:{}] - {} - {}",
            time::strftime("%Y-%m-%d %H:%M:%S", &t).unwrap(),
            t.tm_nsec / 1000_000,
            thread_id::get(),
            std::thread::current().name().unwrap_or(""),
            record.level(),
            record.args()
        )
    };

    let mut builder = LogBuilder::new();
    builder.format(formatter).filter(None, LogLevelFilter::Info);
    if std::env::var("RUST_LOG").is_ok() {
        builder.parse(&std::env::var("RUST_LOG").unwrap());
    }

    builder.init().unwrap();
}
<commit_msg>Filter annoying rustls log spam (#132)<commit_after>extern crate env_logger;
extern crate octobot;
extern crate time;
extern crate thread_id;
extern crate log;

use octobot::config;
use octobot::server;

use env_logger::LogBuilder;
use log::{LogLevelFilter, LogRecord};

fn main() {
    if std::env::args().len() < 2 {
        panic!("Usage: octobot <config-file>")
    }

    setup_logging();

    let config_file = std::env::args().nth(1).unwrap();
    let config = config::parse(&config_file).expect("Error parsing config");

    server::start(config).expect("Failed to start server");
}

fn setup_logging() {
    let formatter = |record: &LogRecord| {
        let t = time::now();
        format!(
            "[{},{:03}][{}:{}] - {} - {}",
            time::strftime("%Y-%m-%d %H:%M:%S", &t).unwrap(),
            t.tm_nsec / 1000_000,
            thread_id::get(),
            std::thread::current().name().unwrap_or(""),
            record.level(),
            record.args()
        )
    };

    let mut builder = LogBuilder::new();
    builder.format(formatter).filter(None, LogLevelFilter::Info);

    let is_info;
    if let Ok(ref env_log) = std::env::var("RUST_LOG") {
        builder.parse(env_log);
        is_info = env_log.is_empty() || env_log.to_lowercase() == "info";
    } else {
        is_info = true;
    }

    if is_info {
        builder.filter(Some("rustls"), LogLevelFilter::Warn);
    }

    builder.init().unwrap();
}
<|endoftext|>


<commit_before>extern crate clap;
use clap::{Arg, App, AppSettings, SubCommand};
use slog::{LevelFilter, Level, DrainExt, Logger};
use yaml_rust::YamlLoader;
use std::io::prelude::*;
use std::fs::File;
use yaml_rust::{Yaml, YamlEmitter};
use std::error::Error;
use std::path::Path;
use std::process::exit;
use errors::DotcopterError;

#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_stdlog;
extern crate yaml_rust;
extern crate crypto;
extern crate regex;

#[cfg(test)]
extern crate spectral;

mod model;
mod config;
mod checksum;
mod files;
mod mutate;
mod import;
mod errors;

fn main() {
  let matches: clap::ArgMatches = create_app().get_matches();
  let stream = slog_term::streamer().full().build();
  let verbose: bool = matches.is_present("verbose");
  let force: bool = matches.is_present("force");
  let log = if verbose {
    slog::Logger::root(stream.fuse(), o!())
  } else {
    slog::Logger::root(LevelFilter::new(stream, Level::Info).fuse(), o!())
  };

  let config_file = matches.value_of("config_file").unwrap();
  info!(log, "Starting engine"; "config_file" => config_file);

  let config = match load_config_file(&log, config_file) {
    Ok(content) => content,
    Err(e) => {
      error!(log, "Failed to load config file."; "error" => e.description());
      exit(1)
    }
  };

  let mut yaml_documents = match YamlLoader::load_from_str(&config) {
    Ok(yaml) => yaml,
    Err(e) => {
      error!(log, "Failed to parse config file."; "error" => e.description());
      exit(2)
    }
  };
  if yaml_documents.is_empty() {
    let s = "files:";
    yaml_documents = YamlLoader::load_from_str(s).unwrap();
  }

  let maybe_ln_matches = matches.subcommand_matches("ln");
  let maybe_cp_matches = matches.subcommand_matches("cp");
  let maybe_apply_matches = matches.subcommand_matches("apply");
  let maybe_import_matches = matches.subcommand_matches("import");
  if maybe_apply_matches.is_some() {
    let yaml_config = &yaml_documents[0];
    let dot_files: &Yaml = &yaml_config["files"];
    info!(log, "Liftoff! Applying configuration.");
    files::process_dot_files(&log, dot_files, force);
  } else if let Some(ln_matches) = maybe_ln_matches {
    let yaml_config = &yaml_documents[0];
    let link_target = ln_matches.value_of("link_target").unwrap();
    let link_name = ln_matches.value_of("link_name").unwrap();
    let log = log.new(o!("link_target" => link_target.to_string(), "link_name" => link_name.to_string()));
    info!(log, "Liftoff! Adding new link to configuration");
    let new_config = mutate::add_dotfiles_to_config(&log,
                                                    yaml_config,
                                                    &[model::DotFile {
                                                        target: link_name.to_string(),
                                                        source: link_target.to_string(),
                                                        dot_file_type: model::DotFileType::LINK,
                                                      }]);
    write_new_yaml(&log, &new_config, config_file);
  } else if let Some(cp_matches) = maybe_cp_matches {
    let yaml_config = &yaml_documents[0];
    let target = cp_matches.value_of("target").unwrap();
    let source = cp_matches.value_of("source").unwrap();
    let log = log.new(o!("target" => target.to_string(), "source" => source.to_string()));
    info!(log, "Liftoff! Adding new copy to configuration");
    let new_config = mutate::add_dotfiles_to_config(&log,
                                                    yaml_config,
                                                    &[model::DotFile {
                                                        target: target.to_string(),
                                                        source: source.to_string(),
                                                        dot_file_type: model::DotFileType::COPY,
                                                      }]);
    write_new_yaml(&log, &new_config, config_file);
  } else if let Some(import_matches) = maybe_import_matches {
    let dir = import_matches.value_of("dir").unwrap();
    let log = log.new(o!("import_directory" => dir.to_string()));
    let yaml_config = &yaml_documents[0];
    info!(log, "Liftoff! Importing to configuration");
    let dot_files = import::scan_dir(&log, dir);
    if !dot_files.is_empty() {
      let new_config = mutate::add_dotfiles_to_config(&log, yaml_config, &dot_files);
      write_new_yaml(&log, &new_config, config_file);
    }
  }
}

fn write_new_yaml(log: &Logger, document: &Yaml, config_file: &str) {
  let mut out_str = String::new();
  {
    let mut emitter = YamlEmitter::new(&mut out_str);
    match emitter.dump(document) {
      Ok(_) => {}
      Err(e) => {
        error!(log, "Failed to write config file."; "error" => format!("{:?}", e));
        exit(3);
      }
    }
  }
  out_str.push_str("\n");
  match write_config_file(config_file, &out_str) {
    Ok(_) => info!(log, "Successfully written configuration"),
    Err(e) => {
      error!(log, "Failed to write config file."; "error" => e.description());
      exit(4);
    }
  };
}

fn write_config_file(file: &str, content: &str) -> Result<(), DotcopterError> {
  let mut file = try!(File::create(file));
  try!(file.write_all(content.as_bytes()));
  Ok(())
}

fn load_config_file(log: &Logger, file: &str) -> Result<String, DotcopterError> {
  let path = Path::new(file);
  if path.exists() {
    let mut file = try!(File::open(file));
    let mut content = String::new();
    try!(file.read_to_string(&mut content));
    Ok(content)
  } else {
    warn!(log, "Configuration doesn't exit"; "file" => file);
    Ok("".to_string())
  }
}

fn create_app<'a>() -> App<'a, 'a> {
  App::new("dotcopter")
    .version("0.1")
    .setting(AppSettings::ColoredHelp)
    .author("Patrick Haun <bomgar85@googlemail.com>")
    .about("manages dotfiles installation")
    .arg(Arg::with_name("force")
           .short("f")
           .long("force")
           .takes_value(false))
    .arg(Arg::with_name("verbose")
           .long("verbose")
           .short("v")
           .help("debug output")
           .required(false)
           .takes_value(false))
    .arg(Arg::with_name("config_file").required(true))
    .subcommand(SubCommand::with_name("apply").about("applies a dotfile configuration"))
    .subcommand(SubCommand::with_name("ln")
                  .about("adds new link to configuration")
                  .arg(Arg::with_name("link_target").required(true))
                  .arg(Arg::with_name("link_name").required(true)))
    .subcommand(SubCommand::with_name("cp")
                  .about("adds new copy to configuration")
                  .arg(Arg::with_name("source").required(true))
                  .arg(Arg::with_name("target").required(true)))
    .subcommand(SubCommand::with_name("import")
                  .about("imports dotfiles from a folder into the configuration")
                  .arg(Arg::with_name("dir").required(true)))
}
<commit_msg>auto version<commit_after>use clap::{Arg, App, AppSettings, SubCommand};
use slog::{LevelFilter, Level, DrainExt, Logger};
use yaml_rust::YamlLoader;
use std::io::prelude::*;
use std::fs::File;
use yaml_rust::{Yaml, YamlEmitter};
use std::error::Error;
use std::path::Path;
use std::process::exit;
use errors::DotcopterError;

#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_stdlog;
extern crate yaml_rust;
extern crate crypto;
extern crate regex;
#[macro_use]
extern crate clap;

#[cfg(test)]
extern crate spectral;

mod model;
mod config;
mod checksum;
mod files;
mod mutate;
mod import;
mod errors;

fn main() {
  let matches: clap::ArgMatches = create_app().get_matches();
  let stream = slog_term::streamer().full().build();
  let verbose: bool = matches.is_present("verbose");
  let force: bool = matches.is_present("force");
  let log = if verbose {
    slog::Logger::root(stream.fuse(), o!())
  } else {
    slog::Logger::root(LevelFilter::new(stream, Level::Info).fuse(), o!())
  };

  let config_file = matches.value_of("config_file").unwrap();
  info!(log, "Starting engine"; "config_file" => config_file);

  let config = match load_config_file(&log, config_file) {
    Ok(content) => content,
    Err(e) => {
      error!(log, "Failed to load config file."; "error" => e.description());
      exit(1)
    }
  };

  let mut yaml_documents = match YamlLoader::load_from_str(&config) {
    Ok(yaml) => yaml,
    Err(e) => {
      error!(log, "Failed to parse config file."; "error" => e.description());
      exit(2)
    }
  };
  if yaml_documents.is_empty() {
    let s = "files:";
    yaml_documents = YamlLoader::load_from_str(s).unwrap();
  }

  let maybe_ln_matches = matches.subcommand_matches("ln");
  let maybe_cp_matches = matches.subcommand_matches("cp");
  let maybe_apply_matches = matches.subcommand_matches("apply");
  let maybe_import_matches = matches.subcommand_matches("import");
  if maybe_apply_matches.is_some() {
    let yaml_config = &yaml_documents[0];
    let dot_files: &Yaml = &yaml_config["files"];
    info!(log, "Liftoff! Applying configuration.");
    files::process_dot_files(&log, dot_files, force);
  } else if let Some(ln_matches) = maybe_ln_matches {
    let yaml_config = &yaml_documents[0];
    let link_target = ln_matches.value_of("link_target").unwrap();
    let link_name = ln_matches.value_of("link_name").unwrap();
    let log = log.new(o!("link_target" => link_target.to_string(), "link_name" => link_name.to_string()));
    info!(log, "Liftoff! Adding new link to configuration");
    let new_config = mutate::add_dotfiles_to_config(&log,
                                                    yaml_config,
                                                    &[model::DotFile {
                                                        target: link_name.to_string(),
                                                        source: link_target.to_string(),
                                                        dot_file_type: model::DotFileType::LINK,
                                                      }]);
    write_new_yaml(&log, &new_config, config_file);
  } else if let Some(cp_matches) = maybe_cp_matches {
    let yaml_config = &yaml_documents[0];
    let target = cp_matches.value_of("target").unwrap();
    let source = cp_matches.value_of("source").unwrap();
    let log = log.new(o!("target" => target.to_string(), "source" => source.to_string()));
    info!(log, "Liftoff! Adding new copy to configuration");
    let new_config = mutate::add_dotfiles_to_config(&log,
                                                    yaml_config,
                                                    &[model::DotFile {
                                                        target: target.to_string(),
                                                        source: source.to_string(),
                                                        dot_file_type: model::DotFileType::COPY,
                                                      }]);
    write_new_yaml(&log, &new_config, config_file);
  } else if let Some(import_matches) = maybe_import_matches {
    let dir = import_matches.value_of("dir").unwrap();
    let log = log.new(o!("import_directory" => dir.to_string()));
    let yaml_config = &yaml_documents[0];
    info!(log, "Liftoff! Importing to configuration");
    let dot_files = import::scan_dir(&log, dir);
    if !dot_files.is_empty() {
      let new_config = mutate::add_dotfiles_to_config(&log, yaml_config, &dot_files);
      write_new_yaml(&log, &new_config, config_file);
    }
  }
}

fn write_new_yaml(log: &Logger, document: &Yaml, config_file: &str) {
  let mut out_str = String::new();
  {
    let mut emitter = YamlEmitter::new(&mut out_str);
    match emitter.dump(document) {
      Ok(_) => {}
      Err(e) => {
        error!(log, "Failed to write config file."; "error" => format!("{:?}", e));
        exit(3);
      }
    }
  }
  out_str.push_str("\n");
  match write_config_file(config_file, &out_str) {
    Ok(_) => info!(log, "Successfully written configuration"),
    Err(e) => {
      error!(log, "Failed to write config file."; "error" => e.description());
      exit(4);
    }
  };
}

fn write_config_file(file: &str, content: &str) -> Result<(), DotcopterError> {
  let mut file = try!(File::create(file));
  try!(file.write_all(content.as_bytes()));
  Ok(())
}

fn load_config_file(log: &Logger, file: &str) -> Result<String, DotcopterError> {
  let path = Path::new(file);
  if path.exists() {
    let mut file = try!(File::open(file));
    let mut content = String::new();
    try!(file.read_to_string(&mut content));
    Ok(content)
  } else {
    warn!(log, "Configuration doesn't exit"; "file" => file);
    Ok("".to_string())
  }
}

fn create_app<'a>() -> App<'a, 'a> {
  App::new("dotcopter")
    .version(crate_version!())
    .setting(AppSettings::ColoredHelp)
    .author("Patrick Haun <bomgar85@googlemail.com>")
    .about("manages dotfiles installation")
    .arg(Arg::with_name("force")
           .short("f")
           .long("force")
           .takes_value(false))
    .arg(Arg::with_name("verbose")
           .long("verbose")
           .short("v")
           .help("debug output")
           .required(false)
           .takes_value(false))
    .arg(Arg::with_name("config_file").required(true))
    .subcommand(SubCommand::with_name("apply").about("applies a dotfile configuration"))
    .subcommand(SubCommand::with_name("ln")
                  .about("adds new link to configuration")
                  .arg(Arg::with_name("link_target").required(true))
                  .arg(Arg::with_name("link_name").required(true)))
    .subcommand(SubCommand::with_name("cp")
                  .about("adds new copy to configuration")
                  .arg(Arg::with_name("source").required(true))
                  .arg(Arg::with_name("target").required(true)))
    .subcommand(SubCommand::with_name("import")
                  .about("imports dotfiles from a folder into the configuration")
                  .arg(Arg::with_name("dir").required(true)))
}
<|endoftext|>


<commit_before>extern crate ansi_term;
extern crate git2;
extern crate regex;

use std::env;
use ansi_term::Colour::{Cyan, Blue, Purple, Red};
use ansi_term::ANSIStrings;
use git2::{Repository, StatusOptions};
use regex::Regex;

fn first_char(s: &str) -> String {
    let chars: Vec<char> = s.chars().collect();
    return match chars.len() {
        0 => String::from(""),
        _ => chars[0].to_string()
    }
}

fn fmt_current_path(cwd: &str) -> String {
    let home: Regex = match env::home_dir() {
        Some(path) => Regex::new(path.to_str().unwrap()).unwrap(),
        None => Regex::new("").unwrap(),
    };
    let friendly_path = home.replace(cwd, "~").to_string();
    let mut friendly_path_split: Vec<&str> = friendly_path.split("/").collect();
    let current_dir = friendly_path_split.pop().unwrap().to_string();
    let mut short_path: Vec<String> = friendly_path_split.iter().map(|s| first_char(s)).collect();
    short_path.push(current_dir);
    short_path.join("/")
}

fn repo_status(r: Repository) -> String {
    let mut opts = StatusOptions::new();
    let head = r.head().unwrap();
    let shorthand = Cyan.paint(head.shorthand().unwrap().to_string());
    let statuses = r.statuses(Some(&mut opts)).unwrap();

    let mut is_dirty = false;

    for entry in statuses.iter().filter(|e| e.status() != git2::STATUS_CURRENT) {
        is_dirty = match entry.status() {
            s if s.contains(git2::STATUS_INDEX_NEW) => true,
            s if s.contains(git2::STATUS_INDEX_MODIFIED) => true,
            s if s.contains(git2::STATUS_INDEX_DELETED) => true,
            s if s.contains(git2::STATUS_INDEX_RENAMED) => true,
            s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => true,
            s if s.contains(git2::STATUS_WT_NEW) => true,
            s if s.contains(git2::STATUS_WT_MODIFIED) => true,
            s if s.contains(git2::STATUS_WT_DELETED) => true,
            s if s.contains(git2::STATUS_WT_RENAMED) => true,
            s if s.contains(git2::STATUS_WT_TYPECHANGE) => true,
            _ => false,
        };
        match is_dirty {
            true => break,
            false => continue,
        }
    }
    if is_dirty == true {
        return ANSIStrings(&[shorthand, Red.bold().paint("*")]).to_string();
    } else {
        return ANSIStrings(&[shorthand]).to_string();
    }
}

fn main() {
    let my_path = env::current_dir().unwrap();
    let display_path = Blue.paint(fmt_current_path(my_path.to_str().unwrap()));

    let branch = match Repository::open(my_path) {
        Ok(repo) => repo_status(repo),
        Err(e) => String::from(""),
    };
    let display_branch = Cyan.paint(branch);

    println!("{} {}", display_path, display_branch);
    print!(" {} ", Purple.paint("❯"));
}
<commit_msg>include  untracked files<commit_after>extern crate ansi_term;
extern crate git2;
extern crate regex;

use std::env;
use ansi_term::Colour::{Cyan, Blue, Purple, Red};
use ansi_term::ANSIStrings;
use git2::{Repository, StatusOptions};
use regex::Regex;

fn first_char(s: &str) -> String {
    let chars: Vec<char> = s.chars().collect();
    return match chars.len() {
        0 => String::from(""),
        _ => chars[0].to_string()
    }
}

fn fmt_current_path(cwd: &str) -> String {
    let home: Regex = match env::home_dir() {
        Some(path) => Regex::new(path.to_str().unwrap()).unwrap(),
        None => Regex::new("").unwrap(),
    };
    let friendly_path = home.replace(cwd, "~").to_string();
    let mut friendly_path_split: Vec<&str> = friendly_path.split("/").collect();
    let current_dir = friendly_path_split.pop().unwrap().to_string();
    let mut short_path: Vec<String> = friendly_path_split.iter().map(|s| first_char(s)).collect();
    short_path.push(current_dir);
    short_path.join("/")
}

fn repo_status(r: Repository) -> String {
    let mut opts = StatusOptions::new();
    opts.include_untracked(true);
    let head = r.head().unwrap();
    let shorthand = Cyan.paint(head.shorthand().unwrap().to_string());
    let statuses = r.statuses(Some(&mut opts)).unwrap();

    let mut is_dirty = false;

    for entry in statuses.iter().filter(|e| e.status() != git2::STATUS_CURRENT) {
        is_dirty = match entry.status() {
            s if s.contains(git2::STATUS_INDEX_NEW) => true,
            s if s.contains(git2::STATUS_INDEX_MODIFIED) => true,
            s if s.contains(git2::STATUS_INDEX_DELETED) => true,
            s if s.contains(git2::STATUS_INDEX_RENAMED) => true,
            s if s.contains(git2::STATUS_INDEX_TYPECHANGE) => true,
            s if s.contains(git2::STATUS_WT_NEW) => true,
            s if s.contains(git2::STATUS_WT_MODIFIED) => true,
            s if s.contains(git2::STATUS_WT_DELETED) => true,
            s if s.contains(git2::STATUS_WT_RENAMED) => true,
            s if s.contains(git2::STATUS_WT_TYPECHANGE) => true,
            _ => false,
        };
        match is_dirty {
            true => break,
            false => continue,
        }
    }
    if is_dirty == true {
        return ANSIStrings(&[shorthand, Red.bold().paint("*")]).to_string();
    } else {
        return ANSIStrings(&[shorthand]).to_string();
    }
}

fn main() {
    let my_path = env::current_dir().unwrap();
    let display_path = Blue.paint(fmt_current_path(my_path.to_str().unwrap()));

    let branch = match Repository::open(my_path) {
        Ok(repo) => repo_status(repo),
        Err(e) => String::from(""),
    };
    let display_branch = Cyan.paint(branch);

    println!("{} {}", display_path, display_branch);
    print!(" {} ", Purple.paint("❯"));
}
<|endoftext|>