<commit_before>// A Regex Operation
//
pub enum OpCode {
  OpConcatenation,
  OpAlternation,
  OpKleine,
  OpZeroOrOne,
  OpOneOrMore,
  OpLineStart,
  OpLineEnd,
  OpLeftParen,
  OpRightParen
}

// Regexp Literal
// a literal character in a regex string (i.e 'abcd')
struct RegexpLiteral {
  value: ~str
}

impl RegexpLiteral {
  pub fn new(s: &str) -> RegexpLiteral {
    RegexpLiteral { value: s.clone().to_owned() }
  }
}

// Regexp
// represents a variable number of states with an
// operator applied to them.
struct Regexp {
  op: OpCode,
  state0: Option<~RegexpStack>,
  state1: Option<~RegexpStack>
}

impl Regexp {
  fn new(op: OpCode, state0: Option<~RegexpStack>, state1:
         Option<~RegexpStack>) -> Regexp {
    Regexp { op: op, state0: state0, state1: state1 }
  }
}

// RegexpCharClass
// represents a character class (i.e '[a-z123]')
struct RegexpCharClass;

// different representations of expressions on
// the stack
enum RegexpStack {
  ReOp(OpCode),
  ReLiteral(RegexpLiteral),
  ReExpression(Regexp)
}

//
pub struct RegexpState {
  priv stack: ~[RegexpStack],
  priv nparen: uint
}

impl RegexpState {
  pub fn new() -> RegexpState {
    RegexpState { stack: ~[], nparen: 0 }
  }
}

impl RegexpState {
  pub fn pushLiteral(&mut self, s: &str) -> () {
    // special cases to consider that we can optimize for:
    //   * if we have multiple literals in a row, we can condense
    //     that expression into one expression (i.e 'abc' can just
    //     be ReLiteral(abc))
    match self.stack.pop_opt() {
      Some(ReLiteral(l)) => {
        self.stack.push(ReLiteral(RegexpLiteral::new(l.value + s)));
      }
      Some(r) => {
        self.stack.push(r);
        self.stack.push(ReLiteral(RegexpLiteral::new(s)));
      }
      None => {
        self.stack.push(ReLiteral(RegexpLiteral::new(s)));
      }
    }
  }
  pub fn pushOperation(&mut self, op: OpCode) -> () {
    self.stack.push(ReOp(op));
  }
  pub fn pushExpression(&mut self, r: Regexp) -> () {
    self.stack.push(ReExpression(r));
  }
}

impl RegexpState {
  pub fn pushAlternation(&mut self) -> () {
    self.pushOperation(OpAlternation);
  }
  pub fn pushLeftParen(&mut self) -> () {
    self.pushOperation(OpLeftParen);
  }
}

impl RegexpState {
  pub fn doAlternation(&mut self) -> Result<bool, &'static str> {
    // try to pop off two items from the stack.
    // these should be branches that you can take.
    //     -> state0
    // s |
    //     -> state1
    //
    // with alternation occuring sequentially, we can
    // make an equivalent model by having state0 or state1
    // also be a regexp with a alternation operand applied to it.
    //     -> state0
    // s |              -> 'state0
    //     -> state1  |
    //                  -> 'state1
    //
    // the operand should be pushed onto the stack before we start parsing
    // the right hand side of the alternation, so the stack should look something
    // like this before doAlternation() is called:
    //
    // state0
    // OpCode(OpAlternation)
    // state1
    let branch1 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to alternate")
    };
    match self.stack.pop_opt() {
      Some(ReOp(OpAlternation)) => { },
      _ => return Err("No alternation operand on the stack, but expected one")
    };
    let branch2 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to alternate")
    };
    let r = Regexp::new(OpAlternation, Some(~branch1), Some(~branch2));

    self.pushExpression(r);

    Ok(true)
  }
  pub fn doConcatenation(&mut self) -> Result<bool, &'static str> {
    // try to take two items off the stack to
    // concatenate.
    //
    // state0 -> state1
    let branch1 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to concatenate")
    };
    let branch2 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to concatenate")
    };
    let r = Regexp::new(OpConcatenation, Some(~branch1), Some(~branch2));

    self.pushExpression(r);

    Ok(true)
  }
  pub fn tryConcatenation(&mut self) {
    // tries to perform a concatenation. this simply
    // means performing a concatenation operation
    // if two or more items are on the stack.
    if (self.stack.len() > 1) {
      self.doConcatenation();
    }
  }
  pub fn doLeftParen(&mut self) {

  }
  pub fn doRightParen(&mut self) {

  }
  pub fn doKleine(&mut self) -> Result<bool, &'static str> {
    match self.stack.pop_opt() {
      Some(r) => {
        let mut r = r;
        // check to see if the expr on the top
        // of the stack has some repition
        // op applied to it.
        let opcode = match &r {
          &ReExpression(ref e) => {
            match e.op {
              OpKleine      => Some(OpKleine),
              OpOneOrMore   => Some(OpOneOrMore),
              OpZeroOrMore  => Some(OpZeroOrMore)
            }
          },
          _ => {
            None
          }
        };
        // if we found that the expr already had
        // a repition op, try to condense (by collapsing
        // overlapping cases...i.e, *?, *+, **, can really
        // just be *).
        //
        // note: in the match block below,
        // the (_) case will cover cases
        // Some(OpOneOrMore), Some(OpZeroOrMore)
        //
        // otherwise, we can make a new expression.
        match opcode {
          Some(OpKleine) => {
            self.stack.push(r);
          }
          None => {
            let expr = Regexp::new(OpKleine, Some(~r), None);
            self.pushExpression(expr);
          }
          _ => {
            match r {
              ReExpression(ref mut e) => {
                e.op = OpKleine;
              }
              _ => { }
            }
            self.stack.push(r);
          }
        }
      }
      _ => {
        return Err("'*' not applied to any state.")
      }
    }
    Ok(true)
  }
}

impl RegexpState {
  pub fn trace(&mut self) {
    println("--STACK--");
    for e in self.stack.iter() {
      println(fmt!("%?", e));
    }
  }
}
<commit_msg>fixed error with concat, concatenating a operation and a literal<commit_after>// A Regex Operation
//
pub enum OpCode {
  OpConcatenation,
  OpAlternation,
  OpKleine,
  OpZeroOrOne,
  OpOneOrMore,
  OpLineStart,
  OpLineEnd,
  OpLeftParen,
  OpRightParen
}

// Regexp Literal
// a literal character in a regex string (i.e 'abcd')
struct RegexpLiteral {
  value: ~str
}

impl RegexpLiteral {
  pub fn new(s: &str) -> RegexpLiteral {
    RegexpLiteral { value: s.clone().to_owned() }
  }
}

// Regexp
// represents a variable number of states with an
// operator applied to them.
struct Regexp {
  op: OpCode,
  state0: Option<~RegexpStack>,
  state1: Option<~RegexpStack>
}

impl Regexp {
  fn new(op: OpCode, state0: Option<~RegexpStack>, state1:
         Option<~RegexpStack>) -> Regexp {
    Regexp { op: op, state0: state0, state1: state1 }
  }
}

// RegexpCharClass
// represents a character class (i.e '[a-z123]')
struct RegexpCharClass;

// different representations of expressions on
// the stack
enum RegexpStack {
  ReOp(OpCode),
  ReLiteral(RegexpLiteral),
  ReExpression(Regexp)
}

//
pub struct RegexpState {
  priv stack: ~[RegexpStack],
  priv nparen: uint
}

impl RegexpState {
  pub fn new() -> RegexpState {
    RegexpState { stack: ~[], nparen: 0 }
  }
}

impl RegexpState {
  pub fn pushLiteral(&mut self, s: &str) -> () {
    // special cases to consider that we can optimize for:
    //   * if we have multiple literals in a row, we can condense
    //     that expression into one expression (i.e 'abc' can just
    //     be ReLiteral(abc))
    match self.stack.pop_opt() {
      Some(ReLiteral(l)) => {
        self.stack.push(ReLiteral(RegexpLiteral::new(l.value + s)));
      }
      Some(r) => {
        self.stack.push(r);
        self.stack.push(ReLiteral(RegexpLiteral::new(s)));
      }
      None => {
        self.stack.push(ReLiteral(RegexpLiteral::new(s)));
      }
    }
  }
  pub fn pushOperation(&mut self, op: OpCode) -> () {
    self.stack.push(ReOp(op));
  }
  pub fn pushExpression(&mut self, r: Regexp) -> () {
    self.stack.push(ReExpression(r));
  }
}

impl RegexpState {
  pub fn pushAlternation(&mut self) -> () {
    self.pushOperation(OpAlternation);
  }
  pub fn pushLeftParen(&mut self) -> () {
    self.pushOperation(OpLeftParen);
  }
}

impl RegexpState {
  pub fn doAlternation(&mut self) -> Result<bool, &'static str> {
    // try to pop off two items from the stack.
    // these should be branches that you can take.
    //     -> state0
    // s |
    //     -> state1
    //
    // with alternation occuring sequentially, we can
    // make an equivalent model by having state0 or state1
    // also be a regexp with a alternation operand applied to it.
    //     -> state0
    // s |              -> 'state0
    //     -> state1  |
    //                  -> 'state1
    //
    // the operand should be pushed onto the stack before we start parsing
    // the right hand side of the alternation, so the stack should look something
    // like this before doAlternation() is called:
    //
    // state0
    // OpCode(OpAlternation)
    // state1
    let branch1 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to alternate")
    };
    match self.stack.pop_opt() {
      Some(ReOp(OpAlternation)) => { },
      _ => return Err("No alternation operand on the stack, but expected one")
    };
    let branch2 = match self.stack.pop_opt() {
      Some(s) => s,
      None => return Err("Nothing to alternate")
    };
    let r = Regexp::new(OpAlternation, Some(~branch1), Some(~branch2));

    self.pushExpression(r);

    Ok(true)
  }
  pub fn doConcatenation(&mut self) -> Result<bool, &'static str> {
    // try to take two items off the stack to
    // concatenate.
    // if either of them are opcodes, just
    // push them back no the stack, and return
    // (this means there is a singular item on the stack,
    // and can't be concatenated with anything)
    //
    // state0 -> state1
    let branch1 = match self.stack.pop_opt() {
      Some(ReOp(op)) => {
        self.pushOperation(op);
        return Ok(true);
      },
      Some(s) => s,
      None => return Err("Nothing to concatenate")
    };
    let branch2 = match self.stack.pop_opt() {
      Some(ReOp(op)) => {
        self.pushOperation(op);
        self.stack.push(branch1);
        return Ok(true);
      },
      Some(s) => s,
      None => return Err("Nothing to concatenate")
    };
    let r = Regexp::new(OpConcatenation, Some(~branch1), Some(~branch2));

    self.pushExpression(r);

    Ok(true)
  }
  pub fn tryConcatenation(&mut self) {
    // tries to perform a concatenation. this simply
    // means performing a concatenation operation
    // if two or more items are on the stack.
    if (self.stack.len() > 1) {
      self.doConcatenation();
    }
  }
  pub fn doLeftParen(&mut self) {

  }
  pub fn doRightParen(&mut self) {

  }
  pub fn doKleine(&mut self) -> Result<bool, &'static str> {
    match self.stack.pop_opt() {
      Some(r) => {
        let mut r = r;
        // check to see if the expr on the top
        // of the stack has some repition
        // op applied to it.
        let opcode = match &r {
          &ReExpression(ref e) => {
            match e.op {
              OpKleine      => Some(OpKleine),
              OpOneOrMore   => Some(OpOneOrMore),
              OpZeroOrMore  => Some(OpZeroOrMore)
            }
          },
          _ => {
            None
          }
        };
        // if we found that the expr already had
        // a repition op, try to condense (by collapsing
        // overlapping cases...i.e, *?, *+, **, can really
        // just be *).
        //
        // note: in the match block below,
        // the (_) case will cover cases
        // Some(OpOneOrMore), Some(OpZeroOrMore)
        //
        // otherwise, we can make a new expression.
        match opcode {
          Some(OpKleine) => {
            self.stack.push(r);
          }
          None => {
            let expr = Regexp::new(OpKleine, Some(~r), None);
            self.pushExpression(expr);
          }
          _ => {
            match r {
              ReExpression(ref mut e) => {
                e.op = OpKleine;
              }
              _ => { }
            }
            self.stack.push(r);
          }
        }
      }
      _ => {
        return Err("'*' not applied to any state.")
      }
    }
    Ok(true)
  }
}

impl RegexpState {
  pub fn trace(&mut self) {
    println("--STACK--");
    for e in self.stack.iter() {
      println(fmt!("%?", e));
    }
  }
}
<|endoftext|>


<commit_before>/// Parser

use std::char;

#[deriving(ToStr)]
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, uint, Option<uint>, Greedy)
}

#[deriving(ToStr)]
pub enum Greedy {
    NonGreedy,
    Greedy
}

/// Parse a regular expression into an AST.
pub fn parse(input: &str) -> Expr {
    let mut s = State::new(input);
    let e = p_alternate(&mut s);
    if s.has_input() {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

/// The parser state.
struct State<'a> {
    input: &'a str,
    prev: Option<&'a str>
}

impl<'a> State<'a> {
    fn new<'a>(input: &'a str) -> State<'a> {
        State {
            input: input,
            prev: None
        }
    }

    /// Consume and return the next character in the input, returning
    /// `None` if empty.
    fn advance(&mut self) -> Option<char> {
        self.prev = Some(self.input);
        if self.has_input() {
            let (c, input_) = self.input.slice_shift_char();
            self.input = input_;
            Some(c)
        } else {
            None
        }
    }

    /// Push the previously read character back onto the input.  This
    /// can only be called immediately after `shift`.
    fn retreat(&mut self) {
        self.input = self.prev.expect("nowhere to retreat");
        self.prev = None;
    }

    /// Return `true` if there is input remaining.
    fn has_input(&self) -> bool {
        self.input.len() > 0
    }
}

fn p_alternate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        items.push(p_concatenate(s));
        match s.advance() {
            Some(c) => {
                match c {
                    ')' => { s.retreat(); break },
                    '|' => continue,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Alternate(items)
    }
}

fn p_concatenate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        match s.advance() {
            Some(c) => match c {
                '|' | ')' => { s.retreat(); break },
                '(' => {
                    // Parse inside the parens
                    let e = p_alternate(s);
                    // Match the closing paren
                    match s.advance() {
                        Some(')') => push_ignore_empty(&mut items, e),
                        _ => fail!("mismatched parenthesis")
                    }
                },
                '.' => items.push(Range('\0', char::MAX)),
                '?' => {
                    let e = items.pop_opt().expect("nothing to repeat");
                    items.push(match e {
                        Repeat(_, _, _, NonGreedy) => fail!("multiple repeat"),
                        Repeat(inner, min, max, Greedy) =>
                            Repeat(inner, min, max, NonGreedy),
                        _ => Repeat(~e, 0, Some(1), Greedy)
                    })
                },
                '+' => {
                    let e = items.pop_opt().expect("nothing to repeat");
                    items.push(match e {
                        Repeat(..) => fail!("multiple repeat"),
                        _ => Repeat(~e, 1, None, Greedy)
                    })
                },
                '*' => {
                    let e = items.pop_opt().expect("nothing to repeat");
                    items.push(match e {
                        Repeat(..) => fail!("multiple repeat"),
                        _ => Repeat(~e, 0, None, Greedy)
                    })
                }
                _ => items.push(Range(c, c))
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Concatenate(items)
    }
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => {},
        _ => items.push(e)
    }
}
<commit_msg>Factor out repetition handling<commit_after>/// Parser

use std::char;

#[deriving(ToStr)]
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, uint, Option<uint>, Greedy)
}

#[deriving(ToStr)]
pub enum Greedy {
    NonGreedy,
    Greedy
}

/// Parse a regular expression into an AST.
pub fn parse(input: &str) -> Expr {
    let mut s = State::new(input);
    let e = p_alternate(&mut s);
    if s.has_input() {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

/// The parser state.
struct State<'a> {
    input: &'a str,
    prev: Option<&'a str>
}

impl<'a> State<'a> {
    fn new<'a>(input: &'a str) -> State<'a> {
        State {
            input: input,
            prev: None
        }
    }

    /// Consume and return the next character in the input, returning
    /// `None` if empty.
    fn advance(&mut self) -> Option<char> {
        self.prev = Some(self.input);
        if self.has_input() {
            let (c, input_) = self.input.slice_shift_char();
            self.input = input_;
            Some(c)
        } else {
            None
        }
    }

    /// Push the previously read character back onto the input.  This
    /// can only be called immediately after `shift`.
    fn retreat(&mut self) {
        self.input = self.prev.expect("nowhere to retreat");
        self.prev = None;
    }

    /// Return `true` if there is input remaining.
    fn has_input(&self) -> bool {
        self.input.len() > 0
    }
}

fn p_alternate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        items.push(p_concatenate(s));
        match s.advance() {
            Some(c) => {
                match c {
                    ')' => { s.retreat(); break },
                    '|' => continue,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Alternate(items)
    }
}

fn p_concatenate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        match s.advance() {
            Some(c) => match c {
                '|' | ')' => { s.retreat(); break },
                '(' => {
                    // Parse inside the parens
                    let e = p_alternate(s);
                    // Match the closing paren
                    match s.advance() {
                        Some(')') => push_ignore_empty(&mut items, e),
                        _ => fail!("mismatched parenthesis")
                    }
                },
                '.' => items.push(Range('\0', char::MAX)),
                '?' => {
                    let e = pop_expr(&mut items);
                    items.push(match e {
                        Repeat(_, _, _, NonGreedy) => fail!("multiple repeat"),
                        Repeat(inner, min, max, Greedy) =>
                            Repeat(inner, min, max, NonGreedy),
                        _ => Repeat(~e, 0, Some(1), Greedy)
                    })
                },
                '+' => add_repeat(&mut items, 1, None),
                '*' => add_repeat(&mut items, 0, None),
                _ => items.push(Range(c, c))
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Concatenate(items)
    }
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => {},
        _ => items.push(e)
    }
}

#[inline]
fn pop_expr(items: &mut ~[Expr]) -> Expr {
    items.pop_opt().expect("nothing to repeat")
}

#[inline]
fn add_repeat(items: &mut ~[Expr], min: uint, max: Option<uint>) {
    let e = pop_expr(items);
    items.push(match e {
        Repeat(..) => fail!("multiple repeat"),
        _ => Repeat(~e, min, max, Greedy)
    })
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Intterupt handling on x86 machines.
//!
//! This module provides support for interrupt handling on both `x86` and
//! `x86_64` as a black box. Code that depends on this can use the same API
//! regardless of system word size.
use core::fmt;
use core::fmt::Write;

use arch::cpu::{control_regs, Registers};
use arch::cpu::dtable::DTable;

use io::term::CONSOLE;
use io::keyboard;

use vga::Color;

use spin::Mutex;

pub mod idt;
pub mod pics;

use self::idt::Idt;

/// State stored when handling an interrupt.
#[repr(C, packed)]
pub struct InterruptContext { /// callee-saved registers
                              pub registers: Registers
                            , /// interrupt ID number
                              pub int_id: usize
                            //, _pad_1: u32
                            //, /// error number
                            //  pub err_no:  u32
                            //, _pad_2: u32
                            }

//impl InterruptContext {
//    /// Fetches the corresponding CPU exception for this interrupt, if this
//    /// interrupt is a CPU exception.
//    #[inline]
//
//}

#[derive(Debug)]
pub struct ExceptionInfo { pub name: &'static str
                         , pub mnemonic: &'static str
                         , pub irq_type: &'static str
                         , pub source: &'static str
                         }

/// x86 exceptions.
///
/// Taken from the list at
/// [http://wiki.osdev.org/Exceptions](http://wiki.osdev.org/Exceptions)
pub static EXCEPTIONS: [ExceptionInfo; 20]
    = [ ExceptionInfo { name: "Divide-By-Zero Error"
                      , mnemonic: "#DE", irq_type: "Fault"
                      , source: "DIV or IDIV instruction" }
      , ExceptionInfo { name: "RESERVED"
                      , mnemonic: "#DB", irq_type: "Fault/trap"
                      , source: "Reserved for Intel use" }
      , ExceptionInfo { name: "Non-Maskable Interrupt"
                      , mnemonic: "NMI", irq_type: "Interrupt"
                      , source: "Non-maskable external interrupt" }
      , ExceptionInfo { name: "Breakpoint"
                      , mnemonic: "#BP", irq_type: "Trap"
                      , source: "INT 3 instruction" }
      , ExceptionInfo { name: "Overflow"
                      , mnemonic: "#OF", irq_type: "Trap"
                      , source: "INTO instruction" }
      , ExceptionInfo { name: "BOUND Range Exceeded"
                      , mnemonic: "#BR", irq_type: "Fault"
                      , source: "BOUND instruction" }
      , ExceptionInfo { name: "Undefined Opcode"
                     , mnemonic: "#UD", irq_type: "Fault"
                     , source: "UD2 instruction or reserved opcode" }
      , ExceptionInfo { name: "Device Not Available"
                      , mnemonic: "#NM", irq_type: "Fault"
                      , source: "Floating-point or WAIT/FWAIT instruction\
                                 (no math coprocessor)" }
      , ExceptionInfo { name: "Double Fault"
                      , mnemonic: "#DF", irq_type: "Abort"
                      , source: "Any instruction that can generate an\
                                 exception, a NMI, or an INTR" }
      , ExceptionInfo { name: "Coprocessor Segment Overrun"
                      , mnemonic: "", irq_type: "Fault"
                      , source: "Any floating-point instruction" }
      , ExceptionInfo { name: "Invalid TSS"
                      , mnemonic: "#TS", irq_type: "Fault"
                      , source: "Task switch or TSS access" }
      , ExceptionInfo { name: "Segment Not Present"
                      , mnemonic: "#NP", irq_type: "Fault"
                      , source: "Loading segment registers or accessing\
                                 system segments" }
      , ExceptionInfo { name: "Stack-Segment Fault"
                      , mnemonic: "#SS", irq_type: "Fault"
                      , source: "Stack operations and SS register loads" }
      , ExceptionInfo { name: "General Protection"
                      , mnemonic: "#GP", irq_type: "Fault"
                      , source: "Any memory reference or other protection\
                                 checks" }
      , ExceptionInfo { name: "Page Fault"
                      , mnemonic: "#PF", irq_type: "Fault"
                      , source: "Any memory reference" }
      , ExceptionInfo { name: "RESERVED"
                      , mnemonic: "", irq_type: ""
                      , source: "RESERVED FOR INTEL USE \n This should never \
                                 happen. Something is very wrong." }
      , ExceptionInfo { name: "x87 FPU Floating-Point Error (Math Fault)"
                      , mnemonic: "#MF", irq_type: "Fault"
                      , source: "x87 FPU floating-point or WAIT/FWAIT\
                                 instruction" }
      , ExceptionInfo { name: "Alignment Check"
                      , mnemonic: "#AC", irq_type: "Fault"
                      , source: "Any data reference in memory" }
      , ExceptionInfo { name: "Machine Check"
                      , mnemonic: "#MC", irq_type: "Abort"
                      , source: "Model-dependent" }
      , ExceptionInfo { name: "SIMD Floating-Point Exception"
                      , mnemonic: "#XM", irq_type: "Fault"
                      , source: "SSE/SSE2/SSE3 floating-point instructions" }
       ];

//==--------------------------------------------------------------------------==
// Top-level interrupt handling
/// Global Interrupt Descriptor Table instance
/// Our global IDT.
static IDT: Mutex<Idt> = Mutex::new(Idt::new());

/// Kernel interrupt-handling function.
///
/// Assembly interrupt handlers call into this, and it dispatches interrupts to
/// the appropriate consumers.
#[no_mangle] #[inline(never)]
pub extern "C" fn handle_interrupt(state: &InterruptContext) {
    let id = state.int_id;
   match id {
       // System timer
       0x20 => { /* TODO: make this work */ }
       // Keyboard
     , 0x21 => {
         // TODO: dispatch keypress event to subscribers (NYI)
           if let Some(input) = keyboard::read_char() {
               if input == '\r' {
                   println!("");
               } else {
                   print!("{}", input);
               }
           }
       }
       // Loonix syscall vector
     , 0x80 => { // TODO: currently, we do nothing here, do we want
                 // our syscalls on this vector as well?
       }
     , _ => panic!("Unknown interrupt: #{} Sorry!", id)
   }
   // send the PICs the end interrupt signal
   unsafe { pics::end_pic_interrupt(id as u8) };
}

/// Handle a CPU exception with a given interrupt context.
#[no_mangle] #[inline(never)]
pub extern "C" fn handle_cpu_exception( state: &InterruptContext
                                      , err_code: usize) -> ! {
    let id = state.int_id;
    let ex_info = &EXCEPTIONS[id];
    let cr_state = control_regs::dump();
    let _ = write!( CONSOLE.lock()
                          .set_colors(Color::White, Color::Blue)
                        //   .clear()
                  , "CPU EXCEPTION {}: {}\n\
                     {} on vector {} with error code {:#x}\n\
                     Source: {}.\nThis is fine.\n\n"
                  , ex_info.mnemonic, ex_info.name, ex_info.irq_type
                  , id, err_code
                  , ex_info.source );

    // TODO: parse error codes
    let _ = match id {
        14 => unimplemented!() //TODO: special handling for page faults
       , _ => write!( CONSOLE.lock()
                             .set_colors(Color::White, Color::Blue)
                    , "Registers:\n{:?}\n    {}\n"
                    , state.registers
                    , cr_state
                    )
    };

    loop { }
}

/// Initialize interrupt handling.
///
/// This function initializes the PICs, populates the IDT with interrupt
/// handlers, loads the IDT pointer, and enables interrupts.
///
/// This is called from the kernel during the init process.
pub unsafe fn initialize() {
   // println!(" . Enabling interrupts:" );
   // println!( " . . Initialising PICs {:>40}"
   //         , pics::initialize().unwrap_or("[ FAIL ]") );
   pics::initialize();
   IDT.lock()
      .add_handlers()
      .load();                 // Load the IDT pointer
   // print!("Testing interrupt handling...");
   // asm!("int $0" :: "N" (0x80));
   // println!("   [DONE]");
   Idt::enable_interrupts(); // enable interrupts

}

bitflags! {
    flags PageFaultErrorCode: u32 {
        /// If 1, the error was caused by a page that was present.
        /// Otherwise, the page was non-present.
        const PRESENT = 1 << 0
      , /// If 1, the error was caused by a read. If 0, the cause was a write.
        const READ_WRITE = 1 << 1
      , /// If 1, the error was caused during user-mode execution.
        /// If 0, the processor was in kernel mode.
        const USER_MODE = 1 << 2
      , /// If 1, the fault was caused by reserved bits set to 1 during a fetch.
        const RESERVED = 1 << 3
      , /// If 1, the fault was caused during an instruction fetch.
        const INST_FETCH = 1 << 4
      , /// If 1, there was a protection key violation.
        const PROTECTION = 1 << 5
    }
}

impl fmt::Display for PageFaultErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!( f, "Caused by {}{}{} during a {}{} executing in {} mode."
              , if self.contains(PRESENT) { "a present page" }
                else { "a non-present page" }
              , if self.contains(PROTECTION) { " protection-key violation" }
                else { "" }
              , if self.contains(RESERVED) { " reserved bits set to one "}
                else { "" }
              , if self.contains(READ_WRITE) { "read" } else { "write" }
              , if self.contains(INST_FETCH) { " in an instruction fetch"}
                else { "" }
              , if self.contains(USER_MODE) { "user" } else { "kernel" }            )
    }
}
<commit_msg>[x86_all] quick pass on page fault exception handler<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Intterupt handling on x86 machines.
//!
//! This module provides support for interrupt handling on both `x86` and
//! `x86_64` as a black box. Code that depends on this can use the same API
//! regardless of system word size.
use core::fmt;
use core::fmt::Write;

use arch::cpu::{control_regs, Registers};
use arch::cpu::dtable::DTable;

use io::term::CONSOLE;
use io::keyboard;

use vga::Color;

use spin::Mutex;

pub mod idt;
pub mod pics;

use self::idt::Idt;

/// State stored when handling an interrupt.
#[repr(C, packed)]
pub struct InterruptContext { /// callee-saved registers
                              pub registers: Registers
                            , /// interrupt ID number
                              pub int_id: usize
                            //, _pad_1: u32
                            //, /// error number
                            //  pub err_no:  u32
                            //, _pad_2: u32
                            }

//impl InterruptContext {
//    /// Fetches the corresponding CPU exception for this interrupt, if this
//    /// interrupt is a CPU exception.
//    #[inline]
//
//}

#[derive(Debug)]
pub struct ExceptionInfo { pub name: &'static str
                         , pub mnemonic: &'static str
                         , pub irq_type: &'static str
                         , pub source: &'static str
                         }

/// x86 exceptions.
///
/// Taken from the list at
/// [http://wiki.osdev.org/Exceptions](http://wiki.osdev.org/Exceptions)
pub static EXCEPTIONS: [ExceptionInfo; 20]
    = [ ExceptionInfo { name: "Divide-By-Zero Error"
                      , mnemonic: "#DE", irq_type: "Fault"
                      , source: "DIV or IDIV instruction" }
      , ExceptionInfo { name: "RESERVED"
                      , mnemonic: "#DB", irq_type: "Fault/trap"
                      , source: "Reserved for Intel use" }
      , ExceptionInfo { name: "Non-Maskable Interrupt"
                      , mnemonic: "NMI", irq_type: "Interrupt"
                      , source: "Non-maskable external interrupt" }
      , ExceptionInfo { name: "Breakpoint"
                      , mnemonic: "#BP", irq_type: "Trap"
                      , source: "INT 3 instruction" }
      , ExceptionInfo { name: "Overflow"
                      , mnemonic: "#OF", irq_type: "Trap"
                      , source: "INTO instruction" }
      , ExceptionInfo { name: "BOUND Range Exceeded"
                      , mnemonic: "#BR", irq_type: "Fault"
                      , source: "BOUND instruction" }
      , ExceptionInfo { name: "Undefined Opcode"
                     , mnemonic: "#UD", irq_type: "Fault"
                     , source: "UD2 instruction or reserved opcode" }
      , ExceptionInfo { name: "Device Not Available"
                      , mnemonic: "#NM", irq_type: "Fault"
                      , source: "Floating-point or WAIT/FWAIT instruction\
                                 (no math coprocessor)" }
      , ExceptionInfo { name: "Double Fault"
                      , mnemonic: "#DF", irq_type: "Abort"
                      , source: "Any instruction that can generate an\
                                 exception, a NMI, or an INTR" }
      , ExceptionInfo { name: "Coprocessor Segment Overrun"
                      , mnemonic: "", irq_type: "Fault"
                      , source: "Any floating-point instruction" }
      , ExceptionInfo { name: "Invalid TSS"
                      , mnemonic: "#TS", irq_type: "Fault"
                      , source: "Task switch or TSS access" }
      , ExceptionInfo { name: "Segment Not Present"
                      , mnemonic: "#NP", irq_type: "Fault"
                      , source: "Loading segment registers or accessing\
                                 system segments" }
      , ExceptionInfo { name: "Stack-Segment Fault"
                      , mnemonic: "#SS", irq_type: "Fault"
                      , source: "Stack operations and SS register loads" }
      , ExceptionInfo { name: "General Protection"
                      , mnemonic: "#GP", irq_type: "Fault"
                      , source: "Any memory reference or other protection\
                                 checks" }
      , ExceptionInfo { name: "Page Fault"
                      , mnemonic: "#PF", irq_type: "Fault"
                      , source: "Any memory reference" }
      , ExceptionInfo { name: "RESERVED"
                      , mnemonic: "", irq_type: ""
                      , source: "RESERVED FOR INTEL USE \n This should never \
                                 happen. Something is very wrong." }
      , ExceptionInfo { name: "x87 FPU Floating-Point Error (Math Fault)"
                      , mnemonic: "#MF", irq_type: "Fault"
                      , source: "x87 FPU floating-point or WAIT/FWAIT\
                                 instruction" }
      , ExceptionInfo { name: "Alignment Check"
                      , mnemonic: "#AC", irq_type: "Fault"
                      , source: "Any data reference in memory" }
      , ExceptionInfo { name: "Machine Check"
                      , mnemonic: "#MC", irq_type: "Abort"
                      , source: "Model-dependent" }
      , ExceptionInfo { name: "SIMD Floating-Point Exception"
                      , mnemonic: "#XM", irq_type: "Fault"
                      , source: "SSE/SSE2/SSE3 floating-point instructions" }
       ];

//==--------------------------------------------------------------------------==
// Top-level interrupt handling
/// Global Interrupt Descriptor Table instance
/// Our global IDT.
static IDT: Mutex<Idt> = Mutex::new(Idt::new());

/// Kernel interrupt-handling function.
///
/// Assembly ISRs call into this, and it dispatches interrupts to
/// the appropriate consumers.
//  TODO: should this be #[cold]?
#[no_mangle] #[inline(never)]
pub extern "C" fn handle_interrupt(state: &InterruptContext) {
    let id = state.int_id;
    match id {
        // System timer
        0x20 => { /* TODO: make this work
                    TODO: should this IRQ get its own handler?
                  */ }
        // Keyboard
      , 0x21 => {
          // TODO: dispatch keypress event to subscribers (NYI)
            // TODO: should this interrupt get its own handler for perf?
            if let Some(input) = keyboard::read_char() {
                if input == '\r' {
                    println!("");
                } else {
                    print!("{}", input);
                }
            }
        }
        // Loonix syscall vector
      , 0x80 => { // TODO: currently, we do nothing here, do we want
                  // our syscalls on this vector as well?
        }
      , _ => panic!("Unknown interrupt: #{} Sorry!", id)
    }
    // send the PICs the end interrupt signal
    unsafe { pics::end_pic_interrupt(id as u8) };
}

/// Handle a CPU exception with a given interrupt context.
//  TODO: should this be #[cold]?
#[no_mangle] #[inline(never)]
pub extern "C" fn handle_cpu_exception( state: &InterruptContext
                                      , err_code: usize) -> ! {
    let id = state.int_id;
    let ex_info = &EXCEPTIONS[id];
    let cr_state = control_regs::dump();
    let _ = write!( CONSOLE.lock()
                          .set_colors(Color::White, Color::Blue)
                        //   .clear()
                  , "CPU EXCEPTION {}: {}\n\
                     {} on vector {} with error code {:#x}\n\
                     Source: {}.\nThis is fine.\n\n"
                  , ex_info.mnemonic, ex_info.name, ex_info.irq_type
                  , id, err_code
                  , ex_info.source );

    // TODO: parse error codes
    let _ = match id {
        // TODO: page fault needs its own handler
        14 => write!( CONSOLE.lock()
                             .set_colors(Color::White, Color::Blue)
                    , "{}"
                    , PageFaultErrorCode::from_bits_truncate(err_code as u32)
                    )
       , _ => write!( CONSOLE.lock()
                             .set_colors(Color::White, Color::Blue)
                    , "Registers:\n{:?}\n    {}\n"
                    , state.registers
                    , cr_state
                    )
    };
    // TODO: stack dumps please

    loop { }
}

/// Initialize interrupt handling.
///
/// This function initializes the PICs, populates the IDT with interrupt
/// handlers, loads the IDT pointer, and enables interrupts.
///
/// This is called from the kernel during the init process.
pub unsafe fn initialize() {
   // println!(" . Enabling interrupts:" );
   // println!( " . . Initialising PICs {:>40}"
   //         , pics::initialize().unwrap_or("[ FAIL ]") );
   pics::initialize();
   // TODO: consider loading double-fault handler before anything else in case
   //       a double fault occurs during init?
   IDT.lock()
      .add_handlers()
      .load();                 // Load the IDT pointer
   // print!("Testing interrupt handling...");
   // asm!("int $0" :: "N" (0x80));
   // println!("   [DONE]");
   Idt::enable_interrupts(); // enable interrupts

}

bitflags! {
    flags PageFaultErrorCode: u32 {
        /// If 1, the error was caused by a page that was present.
        /// Otherwise, the page was non-present.
        const PRESENT = 1 << 0
      , /// If 1, the error was caused by a read. If 0, the cause was a write.
        const READ_WRITE = 1 << 1
      , /// If 1, the error was caused during user-mode execution.
        /// If 0, the processor was in kernel mode.
        const USER_MODE = 1 << 2
      , /// If 1, the fault was caused by reserved bits set to 1 during a fetch.
        const RESERVED = 1 << 3
      , /// If 1, the fault was caused during an instruction fetch.
        const INST_FETCH = 1 << 4
      , /// If 1, there was a protection key violation.
        const PROTECTION = 1 << 5
    }
}

impl fmt::Display for PageFaultErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!( f, "Caused by {}{}{} during a {}{} executing in {} mode."
              , if self.contains(PRESENT) { "a present page" }
                else { "a non-present page" }
              , if self.contains(PROTECTION) { " protection-key violation" }
                else { "" }
              , if self.contains(RESERVED) { " reserved bits set to one "}
                else { "" }
              , if self.contains(READ_WRITE) { "read" } else { "write" }
              , if self.contains(INST_FETCH) { " in an instruction fetch"}
                else { "" }
              , if self.contains(USER_MODE) { "user" } else { "kernel" }            )
    }
}
<|endoftext|>


<commit_before>use flowgger::config::Config;
use flowgger::record::{Record, StructuredData, SDValue, SDValueType};
use chrono::DateTime;
use std::collections::HashMap;
use super::Decoder;

#[derive(Clone)]
struct Suffixes {
    s_bool: Option<String>,
    s_f64: Option<String>,
    s_i64: Option<String>,
    s_u64: Option<String>
}

#[derive(Clone)]
pub struct LTSVDecoder {
    schema: Option<HashMap<String, SDValueType>>,
    suffixes: Suffixes
}

impl LTSVDecoder {
    pub fn new(config: &Config) -> LTSVDecoder {
        let schema = match config.lookup("input.ltsv_schema") {
            None => None,
            Some(pairs) => {
                let mut schema = HashMap::new();
                for (name, sdtype) in pairs.as_table().expect("input.ltsv_schema must be a list of key/type pairs") {
                    let sdtype = match sdtype.as_str().expect("input.ltsv_schema types must be strings").to_lowercase().as_ref() {
                        "string" => SDValueType::String,
                        "bool" => SDValueType::Bool,
                        "f64" => SDValueType::F64,
                        "i64" => SDValueType::I64,
                        "u64" => SDValueType::U64,
                        _ => panic!(format!("Unsupported type in input.ltsv_schema for name [{}]", name))
                    };
                    schema.insert(name.to_owned(), sdtype);
                }
                Some(schema)
            }
        };
        let mut suffixes = Suffixes {
            s_bool: None,
            s_f64: None,
            s_i64: None,
            s_u64: None
        };
        match config.lookup("input.ltsv_suffixes") {
            None => { }
            Some(pairs) => {
                for (sdtype, suffix) in pairs.as_table().expect("input.ltsv_suffixes must be a list of type/suffixes pairs") {
                    let suffix = suffix.as_str().expect("input.ltsv_suffixes suffixes must be strings").to_owned();
                    match sdtype.to_lowercase().as_ref() {
                        "string" => panic!("Strings cannot be suffixed"),
                        "bool" => suffixes.s_bool = Some(suffix),
                        "f64" => suffixes.s_f64 = Some(suffix),
                        "i64" => suffixes.s_i64 = Some(suffix),
                        "u64" => suffixes.s_u64 = Some(suffix),
                        _ => panic!(format!("Unsupported type in input.ltsv_suffixes for type [{}]", sdtype))
                    }
                }
            }
        };
        LTSVDecoder {
            schema: schema,
            suffixes: suffixes
        }
    }
}

impl Decoder for LTSVDecoder {
    fn decode(&self, line: &str) -> Result<Record, &'static str> {
        let mut sd = StructuredData::new(None);
        let mut ts = None;
        let mut hostname = None;
        let mut msg = None;
        let mut severity = None;

        for part in line.split('\t') {
            let mut pair = part.splitn(2, ':');
            let name = try!(pair.next().ok_or("Missing name in an LTSV record"));
            let value = try!(pair.next().ok_or("Missing value in an LTSV record"));
            match name {
                "time" => {
                    let ts_s = if value.starts_with('[') && value.ends_with(']') {
                        &value[1..(value.len() -1)]
                    } else {
                        value
                    };
                    ts = Some(try!(parse_ts(ts_s)));
                },
                "host" =>
                    hostname = Some(value.to_owned()),
                "message" =>
                    msg = Some(value.to_owned()),
                "level" => {
                    let severity_given: u8 = try!(value.parse().or(Err("Invalid severity level")));
                    if severity_given > 7 {
                        return Err("Severity level should be <= 7")
                    }
                    severity = Some(severity_given);
                },
                name => {
                    let (final_name, value): (String, SDValue) = if let Some(ref schema) = self.schema {
                        match schema.get(name) {
                            None | Some(&SDValueType::String) =>
                                (format!("_{}", name), SDValue::String(value.to_owned())),
                            Some(&SDValueType::Bool) => {
                                let final_name = match self.suffixes.s_bool {
                                    Some(ref suffix) if !name.ends_with(suffix) => format!("_{}{}", name, suffix),
                                    _ => format!("_{}", name)
                                };
                                (final_name, SDValue::Bool(try!(value.parse::<bool>().or(Err("Type error; boolean was expected")))))
                            }
                            Some(&SDValueType::F64) => {
                                let final_name = match self.suffixes.s_f64 {
                                    Some(ref suffix) if !name.ends_with(suffix) => format!("_{}{}", name, suffix),
                                    _ => format!("_{}", name)
                                };
                                (final_name, SDValue::F64(try!(value.parse::<f64>().or(Err("Type error; f64 was expected")))))
                            }
                            Some(&SDValueType::I64) => {
                                let final_name = match self.suffixes.s_i64 {
                                    Some(ref suffix) if !name.ends_with(suffix) => format!("_{}{}", name, suffix),
                                    _ => format!("_{}", name)
                                };
                                (final_name, SDValue::I64(try!(value.parse::<i64>().or(Err("Type error; i64 was expected")))))
                            }
                            Some(&SDValueType::U64) => {
                                let final_name = match self.suffixes.s_u64 {
                                    Some(ref suffix) if !name.ends_with(suffix) => format!("_{}{}", name, suffix),
                                    _ => format!("_{}", name)
                                };
                                (final_name, SDValue::U64(try!(value.parse::<u64>().or(Err("Type error; u64 was expected")))))
                            }
                        }
                    } else {
                        (format!("_{}", name), SDValue::String(value.to_owned()))
                    };
                    sd.pairs.push((final_name, value));
                }
            };
        }
        let record = Record {
            ts: try!(ts.ok_or("Missing timestamp")),
            hostname: try!(hostname.ok_or("Missing hostname")),
            facility: None,
            severity: severity,
            appname: None,
            procid: None,
            msgid: None,
            sd: if sd.pairs.is_empty() { None } else { Some(sd) },
            msg: msg,
            full_msg: None
        };
        Ok(record)
    }
}

fn rfc3339_to_unix(rfc3339: &str) -> Result<i64, &'static str> {
    match DateTime::parse_from_rfc3339(rfc3339) {
        Ok(date) => Ok(date.timestamp()),
        Err(_) => Err("Unable to parse the date")
    }
}

fn english_time_to_unix(et: &str) -> Result<i64, &'static str> {
    match DateTime::parse_from_str(et, "%e/%b/%Y:%H:%M:%S %z") {
        Ok(date) => Ok(date.timestamp()),
        Err(_) => Err("Unable to parse the date")
    }
}

fn parse_ts(line: &str) -> Result<i64, &'static str> {
    rfc3339_to_unix(line).or(english_time_to_unix(line))
}

#[test]
fn test_ltsv_suffixes() {
    let config = Config::from_string("[input]\n[input.ltsv_schema]\ncounter = \"U64\"\nscore = \"I64\"\nmean = \"f64\"\ndone = \"bool\"\n[input.ltsv_suffixes]\nu64 = \"_u64\"\ni64 = \"_i64\"\nF64 = \"_f64\"\nBool = \"_bool\"\n");
    let ltsv_decoder = LTSVDecoder::new(&config.unwrap());
    let msg = "time:[10/Oct/2000:13:55:36 -0700]\tdone:true\tscore:-1\tmean:0.42\tcounter:42\tlevel:3\thost:testhostname\tname1:value1\tname 2: value 2\tn3:v3\tmessage:this is a test";
    let res = ltsv_decoder.decode(msg).unwrap();
    let sd = res.sd.unwrap();
    let pairs = sd.pairs;
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::U64(v) = v { k == "_counter_u64" && v == 42 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::I64(v) = v { k == "_score_i64" && v == -1 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::F64(v) = v { k == "_mean_f64" && f64::abs(v - 0.42) < 1e-5 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::Bool(v) = v { k == "_done_bool" && v == true } else { false }
    ));
}

#[test]
fn test_ltsv_suffixes_2() {
    let config = Config::from_string("[input]\n[input.ltsv_schema]\ncounter_u64 = \"U64\"\nscore_i64 = \"I64\"\nmean_f64 = \"f64\"\ndone_bool = \"bool\"\n[input.ltsv_suffixes]\nu64 = \"_u64\"\ni64 = \"_i64\"\nf64 = \"_f64\"\nbool = \"_bool\"\n");
    let ltsv_decoder = LTSVDecoder::new(&config.unwrap());
    let msg = "time:[10/Oct/2000:13:55:36 -0700]\tdone_bool:true\tscore_i64:-1\tmean_f64:0.42\tcounter_u64:42\tlevel:3\thost:testhostname\tname1:value1\tname 2: value 2\tn3:v3\tmessage:this is a test";
    let res = ltsv_decoder.decode(msg).unwrap();
    let sd = res.sd.unwrap();
    let pairs = sd.pairs;
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::U64(v) = v { k == "_counter_u64" && v == 42 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::I64(v) = v { k == "_score_i64" && v == -1 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::F64(v) = v { k == "_mean_f64" && f64::abs(v - 0.42) < 1e-5 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::Bool(v) = v { k == "_done_bool" && v == true } else { false }
    ));
}

#[test]
fn test_ltsv() {
    let config = Config::from_string("[input]\n[input.ltsv_schema]\ncounter = \"u64\"\nscore = \"i64\"\nmean = \"f64\"\ndone = \"bool\"\n");
    let ltsv_decoder = LTSVDecoder::new(&config.unwrap());
    let msg = "time:[2015-08-05T15:53:45.637824Z]\thost:testhostname\tname1:value1\tname 2: value 2\tn3:v3";
    let res = ltsv_decoder.decode(msg).unwrap();
    assert!(res.ts == 1438790025);
}

#[test]
fn test_ltsv_2() {
    let config = Config::from_string("[input]\n[input.ltsv_schema]\ncounter = \"u64\"\nscore = \"i64\"\nmean = \"f64\"\ndone = \"bool\"\n");
    let ltsv_decoder = LTSVDecoder::new(&config.unwrap());
    let msg = "time:[10/Oct/2000:13:55:36 -0700]\tdone:true\tscore:-1\tmean:0.42\tcounter:42\tlevel:3\thost:testhostname\tname1:value1\tname 2: value 2\tn3:v3\tmessage:this is a test";
    let res = ltsv_decoder.decode(msg).unwrap();
    assert!(res.ts == 971211336);
    assert!(res.severity.unwrap() == 3);

    assert!(res.hostname == "testhostname");
    assert!(res.msg.unwrap() == "this is a test");
    let sd = res.sd.unwrap();
    let pairs = sd.pairs;
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::String(v) = v { k == "_name1" && v == "value1" } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::String(v) = v { k == "_name 2" && v == " value 2" } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::String(v) = v { k == "_n3" && v == "v3" } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::U64(v) = v { k == "_counter" && v == 42 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::I64(v) = v { k == "_score" && v == -1 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::F64(v) = v { k == "_mean" && f64::abs(v - 0.42) < 1e-5 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::Bool(v) = v { k == "_done" && v == true } else { false }
    ));
}
<commit_msg>Accept raw Unix timestamps in LTSV<commit_after>use flowgger::config::Config;
use flowgger::record::{Record, StructuredData, SDValue, SDValueType};
use chrono::DateTime;
use std::collections::HashMap;
use super::Decoder;

#[derive(Clone)]
struct Suffixes {
    s_bool: Option<String>,
    s_f64: Option<String>,
    s_i64: Option<String>,
    s_u64: Option<String>
}

#[derive(Clone)]
pub struct LTSVDecoder {
    schema: Option<HashMap<String, SDValueType>>,
    suffixes: Suffixes
}

impl LTSVDecoder {
    pub fn new(config: &Config) -> LTSVDecoder {
        let schema = match config.lookup("input.ltsv_schema") {
            None => None,
            Some(pairs) => {
                let mut schema = HashMap::new();
                for (name, sdtype) in pairs.as_table().expect("input.ltsv_schema must be a list of key/type pairs") {
                    let sdtype = match sdtype.as_str().expect("input.ltsv_schema types must be strings").to_lowercase().as_ref() {
                        "string" => SDValueType::String,
                        "bool" => SDValueType::Bool,
                        "f64" => SDValueType::F64,
                        "i64" => SDValueType::I64,
                        "u64" => SDValueType::U64,
                        _ => panic!(format!("Unsupported type in input.ltsv_schema for name [{}]", name))
                    };
                    schema.insert(name.to_owned(), sdtype);
                }
                Some(schema)
            }
        };
        let mut suffixes = Suffixes {
            s_bool: None,
            s_f64: None,
            s_i64: None,
            s_u64: None
        };
        match config.lookup("input.ltsv_suffixes") {
            None => { }
            Some(pairs) => {
                for (sdtype, suffix) in pairs.as_table().expect("input.ltsv_suffixes must be a list of type/suffixes pairs") {
                    let suffix = suffix.as_str().expect("input.ltsv_suffixes suffixes must be strings").to_owned();
                    match sdtype.to_lowercase().as_ref() {
                        "string" => panic!("Strings cannot be suffixed"),
                        "bool" => suffixes.s_bool = Some(suffix),
                        "f64" => suffixes.s_f64 = Some(suffix),
                        "i64" => suffixes.s_i64 = Some(suffix),
                        "u64" => suffixes.s_u64 = Some(suffix),
                        _ => panic!(format!("Unsupported type in input.ltsv_suffixes for type [{}]", sdtype))
                    }
                }
            }
        };
        LTSVDecoder {
            schema: schema,
            suffixes: suffixes
        }
    }
}

impl Decoder for LTSVDecoder {
    fn decode(&self, line: &str) -> Result<Record, &'static str> {
        let mut sd = StructuredData::new(None);
        let mut ts = None;
        let mut hostname = None;
        let mut msg = None;
        let mut severity = None;

        for part in line.split('\t') {
            let mut pair = part.splitn(2, ':');
            let name = try!(pair.next().ok_or("Missing name in an LTSV record"));
            let value = try!(pair.next().ok_or("Missing value in an LTSV record"));
            match name {
                "time" => {
                    let ts_s = if value.starts_with('[') && value.ends_with(']') {
                        &value[1..(value.len() -1)]
                    } else {
                        value
                    };
                    ts = Some(try!(parse_ts(ts_s)));
                },
                "host" =>
                    hostname = Some(value.to_owned()),
                "message" =>
                    msg = Some(value.to_owned()),
                "level" => {
                    let severity_given: u8 = try!(value.parse().or(Err("Invalid severity level")));
                    if severity_given > 7 {
                        return Err("Severity level should be <= 7")
                    }
                    severity = Some(severity_given);
                },
                name => {
                    let (final_name, value): (String, SDValue) = if let Some(ref schema) = self.schema {
                        match schema.get(name) {
                            None | Some(&SDValueType::String) =>
                                (format!("_{}", name), SDValue::String(value.to_owned())),
                            Some(&SDValueType::Bool) => {
                                let final_name = match self.suffixes.s_bool {
                                    Some(ref suffix) if !name.ends_with(suffix) => format!("_{}{}", name, suffix),
                                    _ => format!("_{}", name)
                                };
                                (final_name, SDValue::Bool(try!(value.parse::<bool>().or(Err("Type error; boolean was expected")))))
                            }
                            Some(&SDValueType::F64) => {
                                let final_name = match self.suffixes.s_f64 {
                                    Some(ref suffix) if !name.ends_with(suffix) => format!("_{}{}", name, suffix),
                                    _ => format!("_{}", name)
                                };
                                (final_name, SDValue::F64(try!(value.parse::<f64>().or(Err("Type error; f64 was expected")))))
                            }
                            Some(&SDValueType::I64) => {
                                let final_name = match self.suffixes.s_i64 {
                                    Some(ref suffix) if !name.ends_with(suffix) => format!("_{}{}", name, suffix),
                                    _ => format!("_{}", name)
                                };
                                (final_name, SDValue::I64(try!(value.parse::<i64>().or(Err("Type error; i64 was expected")))))
                            }
                            Some(&SDValueType::U64) => {
                                let final_name = match self.suffixes.s_u64 {
                                    Some(ref suffix) if !name.ends_with(suffix) => format!("_{}{}", name, suffix),
                                    _ => format!("_{}", name)
                                };
                                (final_name, SDValue::U64(try!(value.parse::<u64>().or(Err("Type error; u64 was expected")))))
                            }
                        }
                    } else {
                        (format!("_{}", name), SDValue::String(value.to_owned()))
                    };
                    sd.pairs.push((final_name, value));
                }
            };
        }
        let record = Record {
            ts: try!(ts.ok_or("Missing timestamp")),
            hostname: try!(hostname.ok_or("Missing hostname")),
            facility: None,
            severity: severity,
            appname: None,
            procid: None,
            msgid: None,
            sd: if sd.pairs.is_empty() { None } else { Some(sd) },
            msg: msg,
            full_msg: None
        };
        Ok(record)
    }
}

fn rfc3339_to_unix(rfc3339: &str) -> Result<i64, &'static str> {
    match DateTime::parse_from_rfc3339(rfc3339) {
        Ok(date) => Ok(date.timestamp()),
        Err(_) => Err("Unable to parse the date")
    }
}

fn english_time_to_unix(et: &str) -> Result<i64, &'static str> {
    match DateTime::parse_from_str(et, "%e/%b/%Y:%H:%M:%S %z") {
        Ok(date) => Ok(date.timestamp()),
        Err(_) => Err("Unable to parse the date")
    }
}

fn unix_strtime_to_unix(et: &str) -> Result<i64, &'static str> {
    match et.parse::<i64>() {
        Ok(ts) => Ok(ts),
        Err(_) => Err("Unable to parse the date")
    }
}

fn parse_ts(line: &str) -> Result<i64, &'static str> {
    rfc3339_to_unix(line).or(unix_strtime_to_unix(line)).or(english_time_to_unix(line))
}

#[test]
fn test_ltsv_suffixes() {
    let config = Config::from_string("[input]\n[input.ltsv_schema]\ncounter = \"U64\"\nscore = \"I64\"\nmean = \"f64\"\ndone = \"bool\"\n[input.ltsv_suffixes]\nu64 = \"_u64\"\ni64 = \"_i64\"\nF64 = \"_f64\"\nBool = \"_bool\"\n");
    let ltsv_decoder = LTSVDecoder::new(&config.unwrap());
    let msg = "time:[10/Oct/2000:13:55:36 -0700]\tdone:true\tscore:-1\tmean:0.42\tcounter:42\tlevel:3\thost:testhostname\tname1:value1\tname 2: value 2\tn3:v3\tmessage:this is a test";
    let res = ltsv_decoder.decode(msg).unwrap();
    let sd = res.sd.unwrap();
    let pairs = sd.pairs;
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::U64(v) = v { k == "_counter_u64" && v == 42 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::I64(v) = v { k == "_score_i64" && v == -1 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::F64(v) = v { k == "_mean_f64" && f64::abs(v - 0.42) < 1e-5 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::Bool(v) = v { k == "_done_bool" && v == true } else { false }
    ));
}

#[test]
fn test_ltsv_suffixes_2() {
    let config = Config::from_string("[input]\n[input.ltsv_schema]\ncounter_u64 = \"U64\"\nscore_i64 = \"I64\"\nmean_f64 = \"f64\"\ndone_bool = \"bool\"\n[input.ltsv_suffixes]\nu64 = \"_u64\"\ni64 = \"_i64\"\nf64 = \"_f64\"\nbool = \"_bool\"\n");
    let ltsv_decoder = LTSVDecoder::new(&config.unwrap());
    let msg = "time:[10/Oct/2000:13:55:36 -0700]\tdone_bool:true\tscore_i64:-1\tmean_f64:0.42\tcounter_u64:42\tlevel:3\thost:testhostname\tname1:value1\tname 2: value 2\tn3:v3\tmessage:this is a test";
    let res = ltsv_decoder.decode(msg).unwrap();
    let sd = res.sd.unwrap();
    let pairs = sd.pairs;
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::U64(v) = v { k == "_counter_u64" && v == 42 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::I64(v) = v { k == "_score_i64" && v == -1 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::F64(v) = v { k == "_mean_f64" && f64::abs(v - 0.42) < 1e-5 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::Bool(v) = v { k == "_done_bool" && v == true } else { false }
    ));
}

#[test]
fn test_ltsv() {
    let config = Config::from_string("[input]\n[input.ltsv_schema]\ncounter = \"u64\"\nscore = \"i64\"\nmean = \"f64\"\ndone = \"bool\"\n");
    let ltsv_decoder = LTSVDecoder::new(&config.unwrap());
    let msg = "time:[2015-08-05T15:53:45.637824Z]\thost:testhostname\tname1:value1\tname 2: value 2\tn3:v3";
    let res = ltsv_decoder.decode(msg).unwrap();
    assert!(res.ts == 1438790025);
}

#[test]
fn test_ltsv_2() {
    let config = Config::from_string("[input]\n[input.ltsv_schema]\ncounter = \"u64\"\nscore = \"i64\"\nmean = \"f64\"\ndone = \"bool\"\n");
    let ltsv_decoder = LTSVDecoder::new(&config.unwrap());
    let msg = "time:[10/Oct/2000:13:55:36 -0700]\tdone:true\tscore:-1\tmean:0.42\tcounter:42\tlevel:3\thost:testhostname\tname1:value1\tname 2: value 2\tn3:v3\tmessage:this is a test";
    let res = ltsv_decoder.decode(msg).unwrap();
    assert!(res.ts == 971211336);
    assert!(res.severity.unwrap() == 3);

    assert!(res.hostname == "testhostname");
    assert!(res.msg.unwrap() == "this is a test");
    let sd = res.sd.unwrap();
    let pairs = sd.pairs;
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::String(v) = v { k == "_name1" && v == "value1" } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::String(v) = v { k == "_name 2" && v == " value 2" } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::String(v) = v { k == "_n3" && v == "v3" } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::U64(v) = v { k == "_counter" && v == 42 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::I64(v) = v { k == "_score" && v == -1 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::F64(v) = v { k == "_mean" && f64::abs(v - 0.42) < 1e-5 } else { false }
    ));
    assert!(pairs.iter().cloned().any(|(k, v)|
        if let SDValue::Bool(v) = v { k == "_done" && v == true } else { false }
    ));
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/// Backtrace support built on libgcc with some extra OS-specific support
///
/// Some methods of getting a backtrace:
///
/// * The backtrace() functions on unix. It turns out this doesn't work very
///   well for green threads on OSX, and the address to symbol portion of it
///   suffers problems that are described below.
///
/// * Using libunwind. This is more difficult than it sounds because libunwind
///   isn't installed everywhere by default. It's also a bit of a hefty library,
///   so possibly not the best option. When testing, libunwind was excellent at
///   getting both accurate backtraces and accurate symbols across platforms.
///   This route was not chosen in favor of the next option, however.
///
/// * We're already using libgcc_s for exceptions in rust (triggering thread
///   unwinding and running destructors on the stack), and it turns out that it
///   conveniently comes with a function that also gives us a backtrace. All of
///   these functions look like _Unwind_*, but it's not quite the full
///   repertoire of the libunwind API. Due to it already being in use, this was
///   the chosen route of getting a backtrace.
///
/// After choosing libgcc_s for backtraces, the sad part is that it will only
/// give us a stack trace of instruction pointers. Thankfully these instruction
/// pointers are accurate (they work for green and native threads), but it's
/// then up to us again to figure out how to translate these addresses to
/// symbols. As with before, we have a few options. Before, that, a little bit
/// of an interlude about symbols. This is my very limited knowledge about
/// symbol tables, and this information is likely slightly wrong, but the
/// general idea should be correct.
///
/// When talking about symbols, it's helpful to know a few things about where
/// symbols are located. Some symbols are located in the dynamic symbol table
/// of the executable which in theory means that they're available for dynamic
/// linking and lookup. Other symbols end up only in the local symbol table of
/// the file. This loosely corresponds to pub and priv functions in Rust.
///
/// Armed with this knowledge, we know that our solution for address to symbol
/// translation will need to consult both the local and dynamic symbol tables.
/// With that in mind, here's our options of translating an address to
/// a symbol.
///
/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
///   behind the scenes to translate, and this is why backtrace() was not used.
///   Conveniently, this method works fantastically on OSX. It appears dladdr()
///   uses magic to consult the local symbol table, or we're putting everything
///   in the dynamic symbol table anyway. Regardless, for OSX, this is the
///   method used for translation. It's provided by the system and easy to do.o
///
///   Sadly, all other systems have a dladdr() implementation that does not
///   consult the local symbol table. This means that most functions are blank
///   because they don't have symbols. This means that we need another solution.
///
/// * Use unw_get_proc_name(). This is part of the libunwind api (not the
///   libgcc_s version of the libunwind api), but involves taking a dependency
///   to libunwind. We may pursue this route in the future if we bundle
///   libunwind, but libunwind was unwieldy enough that it was not chosen at
///   this time to provide this functionality.
///
/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
///   semi-reasonable solution. The stdlib already knows how to spawn processes,
///   so in theory it could invoke readelf, parse the output, and consult the
///   local/dynamic symbol tables from there. This ended up not getting chosen
///   due to the craziness of the idea plus the advent of the next option.
///
/// * Use `libbacktrace`. It turns out that this is a small library bundled in
///   the gcc repository which provides backtrace and symbol translation
///   functionality. All we really need from it is the backtrace functionality,
///   and we only really need this on everything that's not OSX, so this is the
///   chosen route for now.
///
/// In summary, the current situation uses libgcc_s to get a trace of stack
/// pointers, and we use dladdr() or libbacktrace to translate these addresses
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
/// all unix platforms we support right now, so it at least gets the job done.

pub use self::tracing::write;

use io;
use io::prelude::*;
use libc;
use str;

use sys_common::backtrace::{demangle, HEX_WIDTH};

// tracing impls:
mod tracing;
// symbol resolvers:
mod printing;

pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
              s: Option<&[u8]>) -> io::Result<()> {
    try!(write!(w, "  {:2}: {:2$?} - ", idx, addr, HEX_WIDTH));
    match s.and_then(|s| str::from_utf8(s).ok()) {
        Some(string) => try!(demangle(w, string)),
        None => try!(write!(w, "<unknown>")),
    }
    w.write_all(&['\n' as u8])
}

pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
                       more: bool) -> io::Result<()> {
    let file = str::from_utf8(file).unwrap_or("<unknown>");
    // prior line: "  ##: {:2$} - func"
    try!(write!(w, "      {:3$}at {}:{}", "", file, line, HEX_WIDTH));
    if more {
        try!(write!(w, " <... and possibly more>"));
    }
    w.write_all(&['\n' as u8])
}
<commit_msg>Fix the Mac build, again.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/// Backtrace support built on libgcc with some extra OS-specific support
///
/// Some methods of getting a backtrace:
///
/// * The backtrace() functions on unix. It turns out this doesn't work very
///   well for green threads on OSX, and the address to symbol portion of it
///   suffers problems that are described below.
///
/// * Using libunwind. This is more difficult than it sounds because libunwind
///   isn't installed everywhere by default. It's also a bit of a hefty library,
///   so possibly not the best option. When testing, libunwind was excellent at
///   getting both accurate backtraces and accurate symbols across platforms.
///   This route was not chosen in favor of the next option, however.
///
/// * We're already using libgcc_s for exceptions in rust (triggering thread
///   unwinding and running destructors on the stack), and it turns out that it
///   conveniently comes with a function that also gives us a backtrace. All of
///   these functions look like _Unwind_*, but it's not quite the full
///   repertoire of the libunwind API. Due to it already being in use, this was
///   the chosen route of getting a backtrace.
///
/// After choosing libgcc_s for backtraces, the sad part is that it will only
/// give us a stack trace of instruction pointers. Thankfully these instruction
/// pointers are accurate (they work for green and native threads), but it's
/// then up to us again to figure out how to translate these addresses to
/// symbols. As with before, we have a few options. Before, that, a little bit
/// of an interlude about symbols. This is my very limited knowledge about
/// symbol tables, and this information is likely slightly wrong, but the
/// general idea should be correct.
///
/// When talking about symbols, it's helpful to know a few things about where
/// symbols are located. Some symbols are located in the dynamic symbol table
/// of the executable which in theory means that they're available for dynamic
/// linking and lookup. Other symbols end up only in the local symbol table of
/// the file. This loosely corresponds to pub and priv functions in Rust.
///
/// Armed with this knowledge, we know that our solution for address to symbol
/// translation will need to consult both the local and dynamic symbol tables.
/// With that in mind, here's our options of translating an address to
/// a symbol.
///
/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr()
///   behind the scenes to translate, and this is why backtrace() was not used.
///   Conveniently, this method works fantastically on OSX. It appears dladdr()
///   uses magic to consult the local symbol table, or we're putting everything
///   in the dynamic symbol table anyway. Regardless, for OSX, this is the
///   method used for translation. It's provided by the system and easy to do.o
///
///   Sadly, all other systems have a dladdr() implementation that does not
///   consult the local symbol table. This means that most functions are blank
///   because they don't have symbols. This means that we need another solution.
///
/// * Use unw_get_proc_name(). This is part of the libunwind api (not the
///   libgcc_s version of the libunwind api), but involves taking a dependency
///   to libunwind. We may pursue this route in the future if we bundle
///   libunwind, but libunwind was unwieldy enough that it was not chosen at
///   this time to provide this functionality.
///
/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a
///   semi-reasonable solution. The stdlib already knows how to spawn processes,
///   so in theory it could invoke readelf, parse the output, and consult the
///   local/dynamic symbol tables from there. This ended up not getting chosen
///   due to the craziness of the idea plus the advent of the next option.
///
/// * Use `libbacktrace`. It turns out that this is a small library bundled in
///   the gcc repository which provides backtrace and symbol translation
///   functionality. All we really need from it is the backtrace functionality,
///   and we only really need this on everything that's not OSX, so this is the
///   chosen route for now.
///
/// In summary, the current situation uses libgcc_s to get a trace of stack
/// pointers, and we use dladdr() or libbacktrace to translate these addresses
/// to symbols. This is a bit of a hokey implementation as-is, but it works for
/// all unix platforms we support right now, so it at least gets the job done.

pub use self::tracing::write;

use io;
use io::prelude::*;
use libc;
use str;

use sys_common::backtrace::{demangle, HEX_WIDTH};

// tracing impls:
mod tracing;
// symbol resolvers:
mod printing;

pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
              s: Option<&[u8]>) -> io::Result<()> {
    try!(write!(w, "  {:2}: {:2$?} - ", idx, addr, HEX_WIDTH));
    match s.and_then(|s| str::from_utf8(s).ok()) {
        Some(string) => try!(demangle(w, string)),
        None => try!(write!(w, "<unknown>")),
    }
    w.write_all(&['\n' as u8])
}

#[allow(dead_code)]
pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
                       more: bool) -> io::Result<()> {
    let file = str::from_utf8(file).unwrap_or("<unknown>");
    // prior line: "  ##: {:2$} - func"
    try!(write!(w, "      {:3$}at {}:{}", "", file, line, HEX_WIDTH));
    if more {
        try!(write!(w, " <... and possibly more>"));
    }
    w.write_all(&['\n' as u8])
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Jason White
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::collections::HashSet;
use std::io;
use std::path::{Path, PathBuf};
use std::process;

use os_pipe::PipeReader;
use tempfile::TempPath;

use error::Error;
use util::PathExt;

/// Input and output detection strategy.
#[derive(
    Serialize,
    Deserialize,
    Copy,
    Clone,
    Ord,
    Eq,
    PartialOrd,
    PartialEq,
    Hash,
    Debug,
)]
pub enum Detect {
    /// Detect inputs and outputs for MSVC Cl.exe. This works by adding
    /// `/showIncludes` to the command line and parsing the output.
    Cl,

    /// Don't do any input/output detection. Just run the process as is. This
    /// assumes that all inputs and outputs have been explicitly specified up
    /// front.
    None,
}

impl Default for Detect {
    fn default() -> Self {
        Detect::None
    }
}

impl Detect {
    /// Based on the program name, figure out the best detection method. If not
    /// known, returns `None`.
    #[cfg(windows)]
    pub fn from_program(program: &Path) -> Detect {
        // Only use the file stem on Windows (no file extension).
        if let Some(stem) = program.file_stem() {
            match stem.to_str() {
                Some("cl") => Detect::Cl,
                // Some("gcc") => Detect::GCC,
                // Some("clang") => Detect::Clang,
                _ => Detect::default(),
            }
        } else {
            Detect::None
        }
    }

    /// Based on the program name, figure out the best detection method. If not
    /// known, returns `None`.
    #[cfg(unix)]
    pub fn from_program(program: &Path) -> Detect {
        // Use the whole file name on Unix platforms.
        if let Some(name) = program.file_name() {
            match name.to_str() {
                Some("cl") => Detect::Cl,
                // Some("gcc") => Detect::GCC,
                // Some("clang") => Detect::Clang,
                _ => Detect::None,
            }
        } else {
            Detect::None
        }
    }

    /// Run the given process, returning its inputs and outputs.
    pub fn run(
        &self,
        root: &Path,
        process: Process,
        log: &mut io::Write,
    ) -> Result<Detected, Error> {
        match self {
            Detect::Cl => cl::run(root, process, log),
            Detect::None => base::run(root, process, log),
        }
    }
}

/// The sets of detected inputs and outputs of a process.
pub struct Detected {
    inputs: HashSet<PathBuf>,
    outputs: HashSet<PathBuf>,
}

impl Detected {
    pub fn new() -> Detected {
        Detected {
            inputs: HashSet::new(),
            outputs: HashSet::new(),
        }
    }

    pub fn inputs(&self) -> impl Iterator<Item = &Path> {
        self.inputs.iter().map(|p| p.as_ref())
    }

    pub fn outputs(&self) -> impl Iterator<Item = &Path> {
        self.outputs.iter().map(|p| p.as_ref())
    }

    pub fn add_input(&mut self, root: &Path, path: &Path) {
        let path = match path.relative_from(root) {
            Some(x) => x.normalize(),
            None => path.normalize(),
        };

        self.inputs.insert(path);
    }

    pub fn add_output(&mut self, root: &Path, path: &Path) {
        let path = match path.relative_from(root) {
            Some(x) => x.normalize(),
            None => path.normalize(),
        };

        self.outputs.insert(path);
    }
}

pub struct Process {
    child: process::Command,
    reader: PipeReader,
    response_file: Option<TempPath>,
}

impl Process {
    pub fn new(
        child: process::Command,
        reader: PipeReader,
        response_file: Option<TempPath>,
    ) -> Process {
        Process {
            child,
            reader,
            response_file,
        }
    }
}

fn wait_child(mut child: process::Child) -> Result<(), io::Error> {
    let status = child.wait()?;
    match status.code() {
        Some(code) => {
            if code == 0 {
                Ok(())
            } else {
                Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!("Process exited with error code {}", code),
                ).into())
            }
        }
        None => {
            #[cfg(unix)]
            {
                use std::os::unix::process::ExitStatusExt;

                Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Process terminated by signal {}",
                        status.signal().unwrap()
                    ),
                ).into())
            }

            #[cfg(windows)]
            Ok(())
        }
    }
}

mod base {
    use std::io::{self, Read};
    use std::path::Path;

    use error::{Error, ResultExt};

    use super::{wait_child, Detected, Process};

    pub fn run(
        _root: &Path,
        process: Process,
        log: &mut io::Write,
    ) -> Result<Detected, Error> {
        let Process {
            mut child,
            mut reader,
            response_file,
        } = process;

        let handle = child.spawn().context("Failed to spawn process")?;
        drop(child);

        let detected = Detected::new();

        // Read the combined stdout/stderr.
        let mut buf = [0u8; 4096];
        loop {
            let n = reader.read(&mut buf)?;
            if n == 0 {
                break;
            }

            log.write_all(&buf[0..n])?;
        }

        wait_child(handle)?;

        // NB: The temporary response file needs to outlive the spawned process.
        drop(response_file);

        Ok(detected)
    }
}

mod cl {
    use std::io::{self, BufRead};
    use std::path::Path;

    use error::{Error, ResultExt};

    use super::{wait_child, Detected, Process};

    static INCLUDE_PREFIX: &str = "Note: including file: ";

    pub fn run(
        root: &Path,
        process: Process,
        log: &mut io::Write,
    ) -> Result<Detected, Error> {
        let Process {
            mut child,
            reader,
            response_file,
        } = process;

        let mut reader = io::BufReader::new(reader);

        child.arg("/showIncludes");

        let handle = child.spawn().context("Failed to spawn process")?;
        drop(child);

        let mut detected = Detected::new();

        let mut line = String::new();

        while reader.read_line(&mut line)? != 0 {
            if line.starts_with(INCLUDE_PREFIX) {
                let include = &line[INCLUDE_PREFIX.len()..].trim();
                detected.add_input(root, Path::new(include));
            } else {
                log.write_all(line.as_ref())?;
            }

            line.clear();
        }

        wait_child(handle)?;

        drop(response_file);

        Ok(detected)
    }
}
<commit_msg>Canonicalize detected paths<commit_after>// Copyright (c) 2018 Jason White
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::collections::HashSet;
use std::io;
use std::path::{Path, PathBuf};
use std::process;

use os_pipe::PipeReader;
use tempfile::TempPath;

use error::Error;

/// Input and output detection strategy.
#[derive(
    Serialize,
    Deserialize,
    Copy,
    Clone,
    Ord,
    Eq,
    PartialOrd,
    PartialEq,
    Hash,
    Debug,
)]
pub enum Detect {
    /// Detect inputs and outputs for MSVC Cl.exe. This works by adding
    /// `/showIncludes` to the command line and parsing the output.
    Cl,

    /// Don't do any input/output detection. Just run the process as is. This
    /// assumes that all inputs and outputs have been explicitly specified up
    /// front.
    None,
}

impl Default for Detect {
    fn default() -> Self {
        Detect::None
    }
}

impl Detect {
    /// Based on the program name, figure out the best detection method. If not
    /// known, returns `None`.
    #[cfg(windows)]
    pub fn from_program(program: &Path) -> Detect {
        // Only use the file stem on Windows (no file extension).
        if let Some(stem) = program.file_stem() {
            match stem.to_str() {
                Some("cl") => Detect::Cl,
                // Some("gcc") => Detect::GCC,
                // Some("clang") => Detect::Clang,
                _ => Detect::default(),
            }
        } else {
            Detect::None
        }
    }

    /// Based on the program name, figure out the best detection method. If not
    /// known, returns `None`.
    #[cfg(unix)]
    pub fn from_program(program: &Path) -> Detect {
        // Use the whole file name on Unix platforms.
        if let Some(name) = program.file_name() {
            match name.to_str() {
                Some("cl") => Detect::Cl,
                // Some("gcc") => Detect::GCC,
                // Some("clang") => Detect::Clang,
                _ => Detect::None,
            }
        } else {
            Detect::None
        }
    }

    /// Run the given process, returning its inputs and outputs.
    pub fn run(
        &self,
        root: &Path,
        process: Process,
        log: &mut io::Write,
    ) -> Result<Detected, Error> {
        match self {
            Detect::Cl => cl::run(root, process, log),
            Detect::None => base::run(root, process, log),
        }
    }
}

/// The sets of detected inputs and outputs of a process.
pub struct Detected {
    inputs: HashSet<PathBuf>,
    outputs: HashSet<PathBuf>,
}

impl Detected {
    pub fn new() -> Detected {
        Detected {
            inputs: HashSet::new(),
            outputs: HashSet::new(),
        }
    }

    pub fn inputs(&self) -> impl Iterator<Item = &Path> {
        self.inputs.iter().map(|p| p.as_ref())
    }

    pub fn outputs(&self) -> impl Iterator<Item = &Path> {
        self.outputs.iter().map(|p| p.as_ref())
    }

    pub fn add_input(&mut self, path: PathBuf) {
        self.inputs.insert(path);
    }

    #[allow(dead_code)]
    pub fn add_output(&mut self, path: PathBuf) {
        self.outputs.insert(path);
    }
}

pub struct Process {
    child: process::Command,
    reader: PipeReader,
    response_file: Option<TempPath>,
}

impl Process {
    pub fn new(
        child: process::Command,
        reader: PipeReader,
        response_file: Option<TempPath>,
    ) -> Process {
        Process {
            child,
            reader,
            response_file,
        }
    }
}

fn wait_child(mut child: process::Child) -> Result<(), io::Error> {
    let status = child.wait()?;
    match status.code() {
        Some(code) => {
            if code == 0 {
                Ok(())
            } else {
                Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!("Process exited with error code {}", code),
                ).into())
            }
        }
        None => {
            #[cfg(unix)]
            {
                use std::os::unix::process::ExitStatusExt;

                Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "Process terminated by signal {}",
                        status.signal().unwrap()
                    ),
                ).into())
            }

            #[cfg(windows)]
            Ok(())
        }
    }
}

mod base {
    use std::io::{self, Read};
    use std::path::Path;

    use error::{Error, ResultExt};

    use super::{wait_child, Detected, Process};

    pub fn run(
        _root: &Path,
        process: Process,
        log: &mut io::Write,
    ) -> Result<Detected, Error> {
        let Process {
            mut child,
            mut reader,
            response_file,
        } = process;

        let handle = child.spawn().context("Failed to spawn process")?;
        drop(child);

        let detected = Detected::new();

        // Read the combined stdout/stderr.
        let mut buf = [0u8; 4096];
        loop {
            let n = reader.read(&mut buf)?;
            if n == 0 {
                break;
            }

            log.write_all(&buf[0..n])?;
        }

        wait_child(handle)?;

        // NB: The temporary response file needs to outlive the spawned process.
        drop(response_file);

        Ok(detected)
    }
}

mod cl {
    use std::io::{self, BufRead};
    use std::path::Path;

    use error::{Error, ResultExt};

    use super::{wait_child, Detected, Process};

    static INCLUDE_PREFIX: &str = "Note: including file: ";

    pub fn run(
        root: &Path,
        process: Process,
        log: &mut io::Write,
    ) -> Result<Detected, Error> {
        let Process {
            mut child,
            reader,
            response_file,
        } = process;

        // Canonicalize the root path such that `strip_prefix` works below.
        let root = root.canonicalize()?;

        let mut reader = io::BufReader::new(reader);

        // Use `/showIncludes` to capture header files that are used by the
        // build.
        //
        // TODO: Use the `VS_UNICODE_OUTPUT` environment variable to get Unicode
        // output from cl.exe.
        child.arg("/showIncludes");

        let handle = child.spawn().context("Failed to spawn process")?;
        drop(child);

        let mut detected = Detected::new();

        let mut line = String::new();

        while reader.read_line(&mut line)? != 0 {
            if line.starts_with(INCLUDE_PREFIX) {
                let include = &line[INCLUDE_PREFIX.len()..].trim();

                // Canonicalize the path such that the root path and this path
                // agree on the case of the file path. Otherwise, `strip_prefix`
                // won't work.
                let path = Path::new(include).canonicalize()?;

                // Only include paths that are contained within the project
                // root. Everything else is treated as a system dependency.
                match path.strip_prefix(&root) {
                    Ok(path) => detected.add_input(path.to_path_buf()),
                    Err(_) => (),
                };
            } else {
                log.write_all(line.as_ref())?;
            }

            line.clear();
        }

        wait_child(handle)?;

        drop(response_file);

        Ok(detected)
    }
}
<|endoftext|>