<commit_before>use arch::segmentation::{self, SegmentSelector};
use super::bit_field::BitField;
use super::{HandlerFunc, InterruptVector};

/// Interrupt descriptor table.
pub struct Idt([Entry; 256]);

/// An entry of the interrupt descriptor table.
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct Entry {
    pointer_low: u16,
    gdt_selector: SegmentSelector,
    options: BitField<u16>,
    pointer_middle: u16,
    pointer_high: u32,
    reserved: u32,
}

/// Options in an entry of IDT.
pub struct EntryOptions<'a>(&'a mut Entry);

impl<'a> EntryOptions<'a> {
    /// Minimal settings of the entry.
    fn minimal(entry: &'a mut Entry) -> Self {
        let mut options = BitField::new(0);
        options.set_range(9..12, 0b111); // 'must-be-one' bits
        entry.options = options;
        EntryOptions(entry)
    }

    /// Create a new entry with default settings.
    fn new(entry: &'a mut Entry) -> Self {
        Self::minimal(entry)
            .set_present(true)
            .disable_interrupts(true)
            .set_stack_index(0x1)
    }

    /// Set the entry to be present.
    pub fn set_present(self, present: bool) -> Self {
        let mut options = self.0.options;
        options.set_bit(15, present);
        self.0.options = options;
        self
    }

    /// Disable interrupts when using this entry.
    pub fn disable_interrupts(self, disable: bool) -> Self {
        let mut options = self.0.options;
        options.set_bit(8, !disable);
        self.0.options = options;
        self
    }

    /// Set previlege level of this entry.
    pub fn set_privilege_level(self, dpl: u16) -> Self {
        let mut options = self.0.options;
        self.0.options.set_range(13..15, dpl);
        self.0.options = options;
        self
    }

    /// Set stack index to use in TSS for this interrupt entry.
    pub fn set_stack_index(self, index: u16) -> Self {
        let mut options = self.0.options;
        self.0.options.set_range(0..3, index);
        self.0.options = options;
        self
    }
}

impl Idt {
    /// Create a new IDT.
    pub fn new() -> Idt {
        Idt([Entry::missing(); 256])
    }

    /// Set an interrupt vector using a handler.
    pub fn set_handler(&mut self, entry: InterruptVector, handler: HandlerFunc)
        -> EntryOptions
    {
        self.0[entry as usize] = Entry::new(segmentation::cs(), handler);
        EntryOptions(&mut self.0[entry as usize])
    }

    /// Load this IDT.
    pub fn load(&self) {
        use super::dtables::{DescriptorTablePointer, lidt};
        use core::mem::size_of;

        let ptr = DescriptorTablePointer {
            base: self as *const _ as u64,
            limit: (size_of::<Self>() - 1) as u16,
        };

        unsafe { lidt(&ptr) };
    }
}

impl Entry {
    /// Create a new entry using the handler and GDT selector.
    fn new(gdt_selector: SegmentSelector, handler: HandlerFunc) -> Self {
        let pointer = handler as u64;
        let mut entry = Entry {
            gdt_selector: gdt_selector,
            pointer_low: pointer as u16,
            pointer_middle: (pointer >> 16) as u16,
            pointer_high: (pointer >> 32) as u32,
            options: BitField::new(0),
            reserved: 0,
        };
        EntryOptions::new(&mut entry);
        entry
    }

    /// Create a missing entry.
    fn missing() -> Self {
        let mut entry = Entry {
            gdt_selector: SegmentSelector::new(0),
            pointer_low: 0,
            pointer_middle: 0,
            pointer_high: 0,
            options: BitField::new(0),
            reserved: 0,
        };
        EntryOptions::minimal(&mut entry);
        entry
    }
}
<commit_msg>typo: should use local entry option copy<commit_after>use arch::segmentation::{self, SegmentSelector};
use super::bit_field::BitField;
use super::{HandlerFunc, InterruptVector};

/// Interrupt descriptor table.
pub struct Idt([Entry; 256]);

/// An entry of the interrupt descriptor table.
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct Entry {
    pointer_low: u16,
    gdt_selector: SegmentSelector,
    options: BitField<u16>,
    pointer_middle: u16,
    pointer_high: u32,
    reserved: u32,
}

/// Options in an entry of IDT.
pub struct EntryOptions<'a>(&'a mut Entry);

impl<'a> EntryOptions<'a> {
    /// Minimal settings of the entry.
    fn minimal(entry: &'a mut Entry) -> Self {
        let mut options = BitField::new(0);
        options.set_range(9..12, 0b111); // 'must-be-one' bits
        entry.options = options;
        EntryOptions(entry)
    }

    /// Create a new entry with default settings.
    fn new(entry: &'a mut Entry) -> Self {
        Self::minimal(entry)
            .set_present(true)
            .disable_interrupts(true)
            .set_stack_index(0x1)
    }

    /// Set the entry to be present.
    pub fn set_present(self, present: bool) -> Self {
        let mut options = self.0.options;
        options.set_bit(15, present);
        self.0.options = options;
        self
    }

    /// Disable interrupts when using this entry.
    pub fn disable_interrupts(self, disable: bool) -> Self {
        let mut options = self.0.options;
        options.set_bit(8, !disable);
        self.0.options = options;
        self
    }

    /// Set previlege level of this entry.
    pub fn set_privilege_level(self, dpl: u16) -> Self {
        let mut options = self.0.options;
        options.set_range(13..15, dpl);
        self.0.options = options;
        self
    }

    /// Set stack index to use in TSS for this interrupt entry.
    pub fn set_stack_index(self, index: u16) -> Self {
        let mut options = self.0.options;
        options.set_range(0..3, index);
        self.0.options = options;
        self
    }
}

impl Idt {
    /// Create a new IDT.
    pub fn new() -> Idt {
        Idt([Entry::missing(); 256])
    }

    /// Set an interrupt vector using a handler.
    pub fn set_handler(&mut self, entry: InterruptVector, handler: HandlerFunc)
        -> EntryOptions
    {
        self.0[entry as usize] = Entry::new(segmentation::cs(), handler);
        EntryOptions(&mut self.0[entry as usize])
    }

    /// Load this IDT.
    pub fn load(&self) {
        use super::dtables::{DescriptorTablePointer, lidt};
        use core::mem::size_of;

        let ptr = DescriptorTablePointer {
            base: self as *const _ as u64,
            limit: (size_of::<Self>() - 1) as u16,
        };

        unsafe { lidt(&ptr) };
    }
}

impl Entry {
    /// Create a new entry using the handler and GDT selector.
    fn new(gdt_selector: SegmentSelector, handler: HandlerFunc) -> Self {
        let pointer = handler as u64;
        let mut entry = Entry {
            gdt_selector: gdt_selector,
            pointer_low: pointer as u16,
            pointer_middle: (pointer >> 16) as u16,
            pointer_high: (pointer >> 32) as u32,
            options: BitField::new(0),
            reserved: 0,
        };
        EntryOptions::new(&mut entry);
        entry
    }

    /// Create a missing entry.
    fn missing() -> Self {
        let mut entry = Entry {
            gdt_selector: SegmentSelector::new(0),
            pointer_low: 0,
            pointer_middle: 0,
            pointer_high: 0,
            options: BitField::new(0),
            reserved: 0,
        };
        EntryOptions::minimal(&mut entry);
        entry
    }
}
<|endoftext|>


<commit_before>//! This crate provides Serde's two derive macros.
//!
//! ```edition2018
//! # use serde_derive::{Serialize, Deserialize};
//! #
//! #[derive(Serialize, Deserialize)]
//! # struct S;
//! #
//! # fn main() {}
//! ```
//!
//! Please refer to [https://serde.rs/derive.html] for how to set this up.
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html

#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.114")]
#![allow(unknown_lints, bare_trait_objects)]
#![deny(clippy::all, clippy::pedantic)]
// Ignored clippy lints
#![allow(
    clippy::cognitive_complexity,
    clippy::enum_variant_names,
    clippy::match_like_matches_macro,
    clippy::needless_pass_by_value,
    clippy::too_many_arguments,
    clippy::trivially_copy_pass_by_ref,
    clippy::used_underscore_binding,
    clippy::wildcard_in_or_patterns,
    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
    clippy::unnested_or_patterns,
)]
// Ignored clippy_pedantic lints
#![allow(
    clippy::cast_possible_truncation,
    clippy::checked_conversions,
    clippy::doc_markdown,
    clippy::enum_glob_use,
    clippy::filter_map,
    clippy::indexing_slicing,
    clippy::items_after_statements,
    clippy::match_same_arms,
    clippy::module_name_repetitions,
    clippy::must_use_candidate,
    clippy::option_if_let_else,
    clippy::similar_names,
    clippy::single_match_else,
    clippy::struct_excessive_bools,
    clippy::too_many_lines,
    clippy::unseparated_literal_suffix,
    clippy::use_self,
    clippy::wildcard_imports
)]
// The `quote!` macro requires deep recursion.
#![recursion_limit = "512"]

#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;

extern crate proc_macro;
extern crate proc_macro2;

mod internals;

use proc_macro::TokenStream;
use syn::DeriveInput;

#[macro_use]
mod bound;
#[macro_use]
mod fragment;

mod de;
mod dummy;
mod pretend;
mod ser;
mod try;

#[proc_macro_derive(Serialize, attributes(serde))]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    ser::expand_derive_serialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

#[proc_macro_derive(Deserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    de::expand_derive_deserialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
    let compile_errors = errors.iter().map(syn::Error::to_compile_error);
    quote!(#(#compile_errors)*)
}
<commit_msg>Quote no longer requires high recursion<commit_after>//! This crate provides Serde's two derive macros.
//!
//! ```edition2018
//! # use serde_derive::{Serialize, Deserialize};
//! #
//! #[derive(Serialize, Deserialize)]
//! # struct S;
//! #
//! # fn main() {}
//! ```
//!
//! Please refer to [https://serde.rs/derive.html] for how to set this up.
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html

#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.114")]
#![allow(unknown_lints, bare_trait_objects)]
#![deny(clippy::all, clippy::pedantic)]
// Ignored clippy lints
#![allow(
    clippy::cognitive_complexity,
    clippy::enum_variant_names,
    clippy::match_like_matches_macro,
    clippy::needless_pass_by_value,
    clippy::too_many_arguments,
    clippy::trivially_copy_pass_by_ref,
    clippy::used_underscore_binding,
    clippy::wildcard_in_or_patterns,
    // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704
    clippy::unnested_or_patterns,
)]
// Ignored clippy_pedantic lints
#![allow(
    clippy::cast_possible_truncation,
    clippy::checked_conversions,
    clippy::doc_markdown,
    clippy::enum_glob_use,
    clippy::filter_map,
    clippy::indexing_slicing,
    clippy::items_after_statements,
    clippy::match_same_arms,
    clippy::module_name_repetitions,
    clippy::must_use_candidate,
    clippy::option_if_let_else,
    clippy::similar_names,
    clippy::single_match_else,
    clippy::struct_excessive_bools,
    clippy::too_many_lines,
    clippy::unseparated_literal_suffix,
    clippy::use_self,
    clippy::wildcard_imports
)]

#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;

extern crate proc_macro;
extern crate proc_macro2;

mod internals;

use proc_macro::TokenStream;
use syn::DeriveInput;

#[macro_use]
mod bound;
#[macro_use]
mod fragment;

mod de;
mod dummy;
mod pretend;
mod ser;
mod try;

#[proc_macro_derive(Serialize, attributes(serde))]
pub fn derive_serialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    ser::expand_derive_serialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

#[proc_macro_derive(Deserialize, attributes(serde))]
pub fn derive_deserialize(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    de::expand_derive_deserialize(&input)
        .unwrap_or_else(to_compile_errors)
        .into()
}

fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
    let compile_errors = errors.iter().map(syn::Error::to_compile_error);
    quote!(#(#compile_errors)*)
}
<|endoftext|>


<commit_before>use std::ops::{Add};

fn main() {
    let search_term = "picture";
    let quote = "Every face, every shop, bedroom window, public-house, and
    dark square is a picture feverishly turned--in search of what?
    It is the same with books. What do we seek through millions of pages?";

    for line in quote.lines() {
        if line.contains(search_term) {
            println!("{}", line);
        }
    }
    println!("{}");

    let (a, b) = (1.2, 3.4);
    let (x, y) = (10, 20);

    let c = add(a,b);
    let z = add(x, y);

    println!("{} + {} = {}", a, b, c);
    println!("{} + {} = {}", x, y, z);
    println!("");

    let haystack = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862];

    for reference in haystack.iter() {
        let item = *reference;

        let result = match item {
            42 | 132 => "hit!",
            _ => "miss",
        };

        if result == "hit!" {
            println!("{}: {}", item, result);
        }
    }
    println!("");

    let three = 0b11;
    let thirty = 0o36;
    let three_hundred = 0x12C;

    println!("{} {} {}", three, thirty, three_hundred);
    println!("{:b} {:b} {:b}", three, thirty, three_hundred);
    println!("{:o} {:o} {:o}", three, thirty, three_hundred);
    println!("{:x} {:x} {:x}", three, thirty, three_hundred);
    println!("");

    let twenty = 20;
    let twenty_one: i32 = twenty + 1;
    let floats_okay = 21.0;
    let one_million = 1_000_000;

    println!("{}; {}; {}; {}", twenty, twenty_one, floats_okay, one_million);
    println!("");

    let a = 10;
    let b: i32 = 20;

    let c = add(a, b);
    println!("a + b = {}", c);
}

fn add<T: Add<Output = T>>(i: T, j: T) -> T {
    i + j
}
<commit_msg>mutable variable to track line number<commit_after>use std::ops::{Add};

fn main() {
    let search_term = "picture";
    let quote = "Every face, every shop, bedroom window, public-house, and
    dark square is a picture feverishly turned--in search of what?
    It is the same with books. What do we seek through millions of pages?";
    let mut line_num: usize = 1;

    for line in quote.lines() {
        if line.contains(search_term) {
            println!("{}: {}", line_num, line);
        }
        line_num += 1;
    }
    println!("");

    let (a, b) = (1.2, 3.4);
    let (x, y) = (10, 20);

    let c = add(a,b);
    let z = add(x, y);

    println!("{} + {} = {}", a, b, c);
    println!("{} + {} = {}", x, y, z);
    println!("");

    let haystack = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862];

    for reference in haystack.iter() {
        let item = *reference;

        let result = match item {
            42 | 132 => "hit!",
            _ => "miss",
        };

        if result == "hit!" {
            println!("{}: {}", item, result);
        }
    }
    println!("");

    let three = 0b11;
    let thirty = 0o36;
    let three_hundred = 0x12C;

    println!("{} {} {}", three, thirty, three_hundred);
    println!("{:b} {:b} {:b}", three, thirty, three_hundred);
    println!("{:o} {:o} {:o}", three, thirty, three_hundred);
    println!("{:x} {:x} {:x}", three, thirty, three_hundred);
    println!("");

    let twenty = 20;
    let twenty_one: i32 = twenty + 1;
    let floats_okay = 21.0;
    let one_million = 1_000_000;

    println!("{}; {}; {}; {}", twenty, twenty_one, floats_okay, one_million);
    println!("");

    let a = 10;
    let b: i32 = 20;

    let c = add(a, b);
    println!("a + b = {}", c);
}

fn add<T: Add<Output = T>>(i: T, j: T) -> T {
    i + j
}
<|endoftext|>


<commit_before>use super::{ImplTraitContext, LoweringContext, ParamMode};

use rustc_ast::ptr::P;
use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_span::symbol::Ident;
use rustc_span::{source_map::Spanned, Span};

impl<'a, 'hir> LoweringContext<'a, 'hir> {
    crate fn lower_pat(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
        ensure_sufficient_stack(|| {
            let node = match p.kind {
                PatKind::Wild => hir::PatKind::Wild,
                PatKind::Ident(ref binding_mode, ident, ref sub) => {
                    let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
                    let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub);
                    node
                }
                PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)),
                PatKind::TupleStruct(ref path, ref pats) => {
                    let qpath = self.lower_qpath(
                        p.id,
                        &None,
                        path,
                        ParamMode::Optional,
                        ImplTraitContext::disallowed(),
                    );
                    let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
                    hir::PatKind::TupleStruct(qpath, pats, ddpos)
                }
                PatKind::Or(ref pats) => hir::PatKind::Or(
                    self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))),
                ),
                PatKind::Path(ref qself, ref path) => {
                    let qpath = self.lower_qpath(
                        p.id,
                        qself,
                        path,
                        ParamMode::Optional,
                        ImplTraitContext::disallowed(),
                    );
                    hir::PatKind::Path(qpath)
                }
                PatKind::Struct(ref path, ref fields, etc) => {
                    let qpath = self.lower_qpath(
                        p.id,
                        &None,
                        path,
                        ParamMode::Optional,
                        ImplTraitContext::disallowed(),
                    );

                    let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat {
                        hir_id: self.next_id(),
                        ident: f.ident,
                        pat: self.lower_pat(&f.pat),
                        is_shorthand: f.is_shorthand,
                        span: f.span,
                    }));
                    hir::PatKind::Struct(qpath, fs, etc)
                }
                PatKind::Tuple(ref pats) => {
                    let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
                    hir::PatKind::Tuple(pats, ddpos)
                }
                PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
                PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl),
                PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => {
                    hir::PatKind::Range(
                        e1.as_deref().map(|e| self.lower_expr(e)),
                        e2.as_deref().map(|e| self.lower_expr(e)),
                        self.lower_range_end(end, e2.is_some()),
                    )
                }
                PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
                PatKind::Rest => {
                    // If we reach here the `..` pattern is not semantically allowed.
                    self.ban_illegal_rest_pat(p.span)
                }
                // FIXME: consider not using recursion to lower this.
                PatKind::Paren(ref inner) => return self.lower_pat(inner),
                PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", p.span),
            };

            self.pat_with_node_id_of(p, node)
        })
    }

    fn lower_pat_tuple(
        &mut self,
        pats: &[P<Pat>],
        ctx: &str,
    ) -> (&'hir [&'hir hir::Pat<'hir>], Option<usize>) {
        let mut elems = Vec::with_capacity(pats.len());
        let mut rest = None;

        let mut iter = pats.iter().enumerate();
        for (idx, pat) in iter.by_ref() {
            // Interpret the first `..` pattern as a sub-tuple pattern.
            // Note that unlike for slice patterns,
            // where `xs @ ..` is a legal sub-slice pattern,
            // it is not a legal sub-tuple pattern.
            match pat.kind {
                // Found a sub-tuple rest pattern
                PatKind::Rest => {
                    rest = Some((idx, pat.span));
                    break;
                }
                // Found a sub-tuple pattern `$binding_mode $ident @ ..`.
                // This is not allowed as a sub-tuple pattern
                PatKind::Ident(ref _bm, ident, Some(ref sub)) if sub.is_rest() => {
                    let sp = pat.span;
                    self.diagnostic()
                        .struct_span_err(
                            sp,
                            &format!("`{} @` is not allowed in a {}", ident.name, ctx),
                        )
                        .span_label(sp, "this is only allowed in slice patterns")
                        .help("remove this and bind each tuple field independently")
                        .span_suggestion_verbose(
                            sp,
                            &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident),
                            "..".to_string(),
                            Applicability::MaybeIncorrect,
                        )
                        .emit();
                }
                _ => {}
            }

            // It was not a sub-tuple pattern so lower it normally.
            elems.push(self.lower_pat(pat));
        }

        for (_, pat) in iter {
            // There was a previous sub-tuple pattern; make sure we don't allow more...
            if pat.is_rest() {
                // ...but there was one again, so error.
                self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
            } else {
                elems.push(self.lower_pat(pat));
            }
        }

        (self.arena.alloc_from_iter(elems), rest.map(|(ddpos, _)| ddpos))
    }

    /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
    /// `hir::PatKind::Slice(before, slice, after)`.
    ///
    /// When encountering `($binding_mode $ident @)? ..` (`slice`),
    /// this is interpreted as a sub-slice pattern semantically.
    /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
    fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
        let mut before = Vec::new();
        let mut after = Vec::new();
        let mut slice = None;
        let mut prev_rest_span = None;

        // Lowers `$bm $ident @ ..` to `$bm $ident @ _`.
        let lower_rest_sub = |this: &mut Self, pat, bm, ident, sub| {
            let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
            let node = this.lower_pat_ident(pat, bm, ident, lower_sub);
            this.pat_with_node_id_of(pat, node)
        };

        let mut iter = pats.iter();
        // Lower all the patterns until the first occurrence of a sub-slice pattern.
        for pat in iter.by_ref() {
            match pat.kind {
                // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
                PatKind::Rest => {
                    prev_rest_span = Some(pat.span);
                    slice = Some(self.pat_wild_with_node_id_of(pat));
                    break;
                }
                // Found a sub-slice pattern `$binding_mode $ident @ ..`.
                // Record, lower it to `$binding_mode $ident @ _`, and stop here.
                PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
                    prev_rest_span = Some(sub.span);
                    slice = Some(lower_rest_sub(self, pat, bm, ident, sub));
                    break;
                }
                // It was not a subslice pattern so lower it normally.
                _ => before.push(self.lower_pat(pat)),
            }
        }

        // Lower all the patterns after the first sub-slice pattern.
        for pat in iter {
            // There was a previous subslice pattern; make sure we don't allow more.
            let rest_span = match pat.kind {
                PatKind::Rest => Some(pat.span),
                PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
                    // #69103: Lower into `binding @ _` as above to avoid ICEs.
                    after.push(lower_rest_sub(self, pat, bm, ident, sub));
                    Some(sub.span)
                }
                _ => None,
            };
            if let Some(rest_span) = rest_span {
                // We have e.g., `[a, .., b, ..]`. That's no good, error!
                self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
            } else {
                // Lower the pattern normally.
                after.push(self.lower_pat(pat));
            }
        }

        hir::PatKind::Slice(
            self.arena.alloc_from_iter(before),
            slice,
            self.arena.alloc_from_iter(after),
        )
    }

    fn lower_pat_ident(
        &mut self,
        p: &Pat,
        binding_mode: &BindingMode,
        ident: Ident,
        lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
    ) -> hir::PatKind<'hir> {
        match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
            // `None` can occur in body-less function signatures
            res @ (None | Some(Res::Local(_))) => {
                let canonical_id = match res {
                    Some(Res::Local(id)) => id,
                    _ => p.id,
                };

                hir::PatKind::Binding(
                    self.lower_binding_mode(binding_mode),
                    self.lower_node_id(canonical_id),
                    ident,
                    lower_sub(self),
                )
            }
            Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
                None,
                self.arena.alloc(hir::Path {
                    span: ident.span,
                    res: self.lower_res(res),
                    segments: arena_vec![self; hir::PathSegment::from_ident(ident)],
                }),
            )),
        }
    }

    fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
        match *b {
            BindingMode::ByValue(Mutability::Not) => hir::BindingAnnotation::Unannotated,
            BindingMode::ByRef(Mutability::Not) => hir::BindingAnnotation::Ref,
            BindingMode::ByValue(Mutability::Mut) => hir::BindingAnnotation::Mutable,
            BindingMode::ByRef(Mutability::Mut) => hir::BindingAnnotation::RefMut,
        }
    }

    fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
        self.pat_with_node_id_of(p, hir::PatKind::Wild)
    }

    /// Construct a `Pat` with the `HirId` of `p.id` lowered.
    fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
        self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
    }

    /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
    fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
        self.diagnostic()
            .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
            .span_label(sp, &format!("can only be used once per {} pattern", ctx))
            .span_label(prev_sp, "previously used here")
            .emit();
    }

    /// Used to ban the `..` pattern in places it shouldn't be semantically.
    fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
        self.diagnostic()
            .struct_span_err(sp, "`..` patterns are not allowed here")
            .note("only allowed in tuple, tuple struct, and slice patterns")
            .emit();

        // We're not in a list context so `..` can be reasonably treated
        // as `_` because it should always be valid and roughly matches the
        // intent of `..` (notice that the rest of a single slot is that slot).
        hir::PatKind::Wild
    }

    fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
        match *e {
            RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
            // No end; so `X..` behaves like `RangeFrom`.
            RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
        }
    }
}
<commit_msg>Loop instead of recursion<commit_after>use super::{ImplTraitContext, LoweringContext, ParamMode};

use rustc_ast::ptr::P;
use rustc_ast::*;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_span::symbol::Ident;
use rustc_span::{source_map::Spanned, Span};

impl<'a, 'hir> LoweringContext<'a, 'hir> {
    crate fn lower_pat(&mut self, mut pattern: &Pat) -> &'hir hir::Pat<'hir> {
        ensure_sufficient_stack(|| {
            // loop here to avoid recursion
            let node = loop {
                match pattern.kind {
                    PatKind::Wild => break hir::PatKind::Wild,
                    PatKind::Ident(ref binding_mode, ident, ref sub) => {
                        let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
                        break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub);
                    }
                    PatKind::Lit(ref e) => break hir::PatKind::Lit(self.lower_expr(e)),
                    PatKind::TupleStruct(ref path, ref pats) => {
                        let qpath = self.lower_qpath(
                            pattern.id,
                            &None,
                            path,
                            ParamMode::Optional,
                            ImplTraitContext::disallowed(),
                        );
                        let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
                        break hir::PatKind::TupleStruct(qpath, pats, ddpos);
                    }
                    PatKind::Or(ref pats) => {
                        break hir::PatKind::Or(
                            self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))),
                        );
                    }
                    PatKind::Path(ref qself, ref path) => {
                        let qpath = self.lower_qpath(
                            pattern.id,
                            qself,
                            path,
                            ParamMode::Optional,
                            ImplTraitContext::disallowed(),
                        );
                        break hir::PatKind::Path(qpath);
                    }
                    PatKind::Struct(ref path, ref fields, etc) => {
                        let qpath = self.lower_qpath(
                            pattern.id,
                            &None,
                            path,
                            ParamMode::Optional,
                            ImplTraitContext::disallowed(),
                        );

                        let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat {
                            hir_id: self.next_id(),
                            ident: f.ident,
                            pat: self.lower_pat(&f.pat),
                            is_shorthand: f.is_shorthand,
                            span: f.span,
                        }));
                        break hir::PatKind::Struct(qpath, fs, etc);
                    }
                    PatKind::Tuple(ref pats) => {
                        let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
                        break hir::PatKind::Tuple(pats, ddpos);
                    }
                    PatKind::Box(ref inner) => {
                        break hir::PatKind::Box(self.lower_pat(inner));
                    }
                    PatKind::Ref(ref inner, mutbl) => {
                        break hir::PatKind::Ref(self.lower_pat(inner), mutbl);
                    }
                    PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => {
                        break hir::PatKind::Range(
                            e1.as_deref().map(|e| self.lower_expr(e)),
                            e2.as_deref().map(|e| self.lower_expr(e)),
                            self.lower_range_end(end, e2.is_some()),
                        );
                    }
                    PatKind::Slice(ref pats) => break self.lower_pat_slice(pats),
                    PatKind::Rest => {
                        // If we reach here the `..` pattern is not semantically allowed.
                        break self.ban_illegal_rest_pat(pattern.span);
                    }
                    // return inner to be processed in next loop
                    PatKind::Paren(ref inner) => pattern = inner,
                    PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
                }
            };

            self.pat_with_node_id_of(pattern, node)
        })
    }

    fn lower_pat_tuple(
        &mut self,
        pats: &[P<Pat>],
        ctx: &str,
    ) -> (&'hir [&'hir hir::Pat<'hir>], Option<usize>) {
        let mut elems = Vec::with_capacity(pats.len());
        let mut rest = None;

        let mut iter = pats.iter().enumerate();
        for (idx, pat) in iter.by_ref() {
            // Interpret the first `..` pattern as a sub-tuple pattern.
            // Note that unlike for slice patterns,
            // where `xs @ ..` is a legal sub-slice pattern,
            // it is not a legal sub-tuple pattern.
            match pat.kind {
                // Found a sub-tuple rest pattern
                PatKind::Rest => {
                    rest = Some((idx, pat.span));
                    break;
                }
                // Found a sub-tuple pattern `$binding_mode $ident @ ..`.
                // This is not allowed as a sub-tuple pattern
                PatKind::Ident(ref _bm, ident, Some(ref sub)) if sub.is_rest() => {
                    let sp = pat.span;
                    self.diagnostic()
                        .struct_span_err(
                            sp,
                            &format!("`{} @` is not allowed in a {}", ident.name, ctx),
                        )
                        .span_label(sp, "this is only allowed in slice patterns")
                        .help("remove this and bind each tuple field independently")
                        .span_suggestion_verbose(
                            sp,
                            &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident),
                            "..".to_string(),
                            Applicability::MaybeIncorrect,
                        )
                        .emit();
                }
                _ => {}
            }

            // It was not a sub-tuple pattern so lower it normally.
            elems.push(self.lower_pat(pat));
        }

        for (_, pat) in iter {
            // There was a previous sub-tuple pattern; make sure we don't allow more...
            if pat.is_rest() {
                // ...but there was one again, so error.
                self.ban_extra_rest_pat(pat.span, rest.unwrap().1, ctx);
            } else {
                elems.push(self.lower_pat(pat));
            }
        }

        (self.arena.alloc_from_iter(elems), rest.map(|(ddpos, _)| ddpos))
    }

    /// Lower a slice pattern of form `[pat_0, ..., pat_n]` into
    /// `hir::PatKind::Slice(before, slice, after)`.
    ///
    /// When encountering `($binding_mode $ident @)? ..` (`slice`),
    /// this is interpreted as a sub-slice pattern semantically.
    /// Patterns that follow, which are not like `slice` -- or an error occurs, are in `after`.
    fn lower_pat_slice(&mut self, pats: &[P<Pat>]) -> hir::PatKind<'hir> {
        let mut before = Vec::new();
        let mut after = Vec::new();
        let mut slice = None;
        let mut prev_rest_span = None;

        // Lowers `$bm $ident @ ..` to `$bm $ident @ _`.
        let lower_rest_sub = |this: &mut Self, pat, bm, ident, sub| {
            let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
            let node = this.lower_pat_ident(pat, bm, ident, lower_sub);
            this.pat_with_node_id_of(pat, node)
        };

        let mut iter = pats.iter();
        // Lower all the patterns until the first occurrence of a sub-slice pattern.
        for pat in iter.by_ref() {
            match pat.kind {
                // Found a sub-slice pattern `..`. Record, lower it to `_`, and stop here.
                PatKind::Rest => {
                    prev_rest_span = Some(pat.span);
                    slice = Some(self.pat_wild_with_node_id_of(pat));
                    break;
                }
                // Found a sub-slice pattern `$binding_mode $ident @ ..`.
                // Record, lower it to `$binding_mode $ident @ _`, and stop here.
                PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
                    prev_rest_span = Some(sub.span);
                    slice = Some(lower_rest_sub(self, pat, bm, ident, sub));
                    break;
                }
                // It was not a subslice pattern so lower it normally.
                _ => before.push(self.lower_pat(pat)),
            }
        }

        // Lower all the patterns after the first sub-slice pattern.
        for pat in iter {
            // There was a previous subslice pattern; make sure we don't allow more.
            let rest_span = match pat.kind {
                PatKind::Rest => Some(pat.span),
                PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
                    // #69103: Lower into `binding @ _` as above to avoid ICEs.
                    after.push(lower_rest_sub(self, pat, bm, ident, sub));
                    Some(sub.span)
                }
                _ => None,
            };
            if let Some(rest_span) = rest_span {
                // We have e.g., `[a, .., b, ..]`. That's no good, error!
                self.ban_extra_rest_pat(rest_span, prev_rest_span.unwrap(), "slice");
            } else {
                // Lower the pattern normally.
                after.push(self.lower_pat(pat));
            }
        }

        hir::PatKind::Slice(
            self.arena.alloc_from_iter(before),
            slice,
            self.arena.alloc_from_iter(after),
        )
    }

    fn lower_pat_ident(
        &mut self,
        p: &Pat,
        binding_mode: &BindingMode,
        ident: Ident,
        lower_sub: impl FnOnce(&mut Self) -> Option<&'hir hir::Pat<'hir>>,
    ) -> hir::PatKind<'hir> {
        match self.resolver.get_partial_res(p.id).map(|d| d.base_res()) {
            // `None` can occur in body-less function signatures
            res @ (None | Some(Res::Local(_))) => {
                let canonical_id = match res {
                    Some(Res::Local(id)) => id,
                    _ => p.id,
                };

                hir::PatKind::Binding(
                    self.lower_binding_mode(binding_mode),
                    self.lower_node_id(canonical_id),
                    ident,
                    lower_sub(self),
                )
            }
            Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
                None,
                self.arena.alloc(hir::Path {
                    span: ident.span,
                    res: self.lower_res(res),
                    segments: arena_vec![self; hir::PathSegment::from_ident(ident)],
                }),
            )),
        }
    }

    fn lower_binding_mode(&mut self, b: &BindingMode) -> hir::BindingAnnotation {
        match *b {
            BindingMode::ByValue(Mutability::Not) => hir::BindingAnnotation::Unannotated,
            BindingMode::ByRef(Mutability::Not) => hir::BindingAnnotation::Ref,
            BindingMode::ByValue(Mutability::Mut) => hir::BindingAnnotation::Mutable,
            BindingMode::ByRef(Mutability::Mut) => hir::BindingAnnotation::RefMut,
        }
    }

    fn pat_wild_with_node_id_of(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
        self.pat_with_node_id_of(p, hir::PatKind::Wild)
    }

    /// Construct a `Pat` with the `HirId` of `p.id` lowered.
    fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
        self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
    }

    /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
    fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
        self.diagnostic()
            .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
            .span_label(sp, &format!("can only be used once per {} pattern", ctx))
            .span_label(prev_sp, "previously used here")
            .emit();
    }

    /// Used to ban the `..` pattern in places it shouldn't be semantically.
    fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
        self.diagnostic()
            .struct_span_err(sp, "`..` patterns are not allowed here")
            .note("only allowed in tuple, tuple struct, and slice patterns")
            .emit();

        // We're not in a list context so `..` can be reasonably treated
        // as `_` because it should always be valid and roughly matches the
        // intent of `..` (notice that the rest of a single slot is that slot).
        hir::PatKind::Wild
    }

    fn lower_range_end(&mut self, e: &RangeEnd, has_end: bool) -> hir::RangeEnd {
        match *e {
            RangeEnd::Excluded if has_end => hir::RangeEnd::Excluded,
            // No end; so `X..` behaves like `RangeFrom`.
            RangeEnd::Excluded | RangeEnd::Included(_) => hir::RangeEnd::Included,
        }
    }
}
<|endoftext|>


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

use std::collections::HashMap;
use std::error;
use std::path::{Path, PathBuf};
use std::result;
use std::str::{self, FromStr};

use libarchive::writer;
use libarchive::reader::{self, Reader};
use libarchive::archive::{Entry, ReadFilter, ReadFormat};
use regex::Regex;

use error::{Error, Result};
use crypto::{artifact, hash};
use package::{Identifiable, PackageIdent, MetaFile};

lazy_static! {
    static ref METAFILE_REGXS: HashMap<MetaFile, Regex> = {
        let mut map = HashMap::new();
        map.insert(MetaFile::CFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::CFlags)).unwrap());
        map.insert(MetaFile::Config, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Config)).unwrap());
        map.insert(MetaFile::Deps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Deps)).unwrap());
        map.insert(MetaFile::TDeps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::TDeps)).unwrap());
        map.insert(MetaFile::Exposes, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Exposes)).unwrap());
        map.insert(MetaFile::Ident, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Ident)).unwrap());
        map.insert(MetaFile::LdRunPath, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdRunPath)).unwrap());
        map.insert(MetaFile::LdFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdFlags)).unwrap());
        map.insert(MetaFile::Manifest, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Manifest)).unwrap());
        map.insert(MetaFile::Path, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Path)).unwrap());
        map
    };
}

type Metadata = HashMap<MetaFile, String>;

#[derive(Debug)]
pub struct PackageArchive {
    pub path: PathBuf,
    metadata: Option<Metadata>,
}

impl PackageArchive {
    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
        PackageArchive {
            path: path.into(),
            metadata: None,
        }
    }

    /// Calculate and return the checksum of the package archive in base64 format.
    ///
    /// # Failures
    ///
    /// * If the archive cannot be read
    pub fn checksum(&self) -> Result<String> {
        hash::hash_file(&self.path)
    }

    pub fn cflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::CFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn config(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Config) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// Returns a list of package identifiers representing the runtime package dependencies for
    /// this archive.
    ///
    /// # Failures
    ///
    /// * If a `DEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn deps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::Deps)
    }

    /// Returns a list of package identifiers representing the transitive runtime package
    /// dependencies for this archive.
    ///
    /// # Failures
    ///
    /// * If a `TDEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn tdeps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::TDeps)
    }

    pub fn exposes(&mut self) -> Result<Vec<u16>> {
        match self.read_metadata(MetaFile::Exposes) {
            Ok(Some(data)) => {
                let ports: Vec<u16> = data.split(" ")
                    .filter_map(|port| port.parse::<u16>().ok())
                    .collect();
                Ok(ports)
            }
            Ok(None) => Ok(vec![]),
            Err(e) => Err(e),
        }
    }

    pub fn ident(&mut self) -> Result<PackageIdent> {
        match self.read_metadata(MetaFile::Ident) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Ident)),
            Ok(Some(data)) => PackageIdent::from_str(&data),
            Err(e) => Err(e),
        }
    }

    pub fn ld_run_path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdRunPath) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn ldflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn manifest(&mut self) -> Result<String> {
        match self.read_metadata(MetaFile::Manifest) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Manifest)),
            Ok(Some(data)) => Ok(data.clone()),
            Err(e) => Err(e),
        }
    }

    pub fn path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Path) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// A plain string representation of the archive's file name.
    pub fn file_name(&self) -> String {
        self.path.file_name().unwrap().to_string_lossy().into_owned()
    }

    /// Given a package name and a path to a file as an `&str`, verify
    /// the files signature.
    ///
    /// # Failures
    ///
    /// * Fails if it cannot verify the signature for any reason
    pub fn verify<P: AsRef<Path>>(&self, cache_key_path: &P) -> Result<(String, String)> {
        artifact::verify(&self.path, cache_key_path)
    }

    /// Given a package name and a path to a file as an `&str`, unpack
    /// the package.
    ///
    /// # Failures
    ///
    /// * If the package cannot be unpacked
    pub fn unpack(&self, fs_root_path: Option<&Path>) -> Result<()> {
        let root = fs_root_path.unwrap_or(Path::new("/"));
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        let writer = writer::Disk::new();
        try!(writer.set_standard_lookup());
        try!(writer.write(&mut reader, Some(root.to_string_lossy().as_ref())));
        try!(writer.close());
        Ok(())
    }

    fn read_deps(&mut self, file: MetaFile) -> Result<Vec<PackageIdent>> {
        let mut deps: Vec<PackageIdent> = vec![];
        match self.read_metadata(file) {
            Ok(Some(body)) => {
                debug!("body = [{}]", &body);
                let ids: Vec<String> = body.split("\n").map(|d| d.to_string()).collect();
                for id in &ids {
                    debug!("id = [{}]", &id);
                    let package = try!(PackageIdent::from_str(id));
                    if !package.fully_qualified() {
                        // JW TODO: use a more appropriate erorr to describe the invalid
                        // user input here. (user because a package was generated by a user
                        // and read into program)
                        return Err(Error::InvalidPackageIdent(package.to_string()));
                    }
                    deps.push(package);
                }
                Ok(deps)
            }
            Ok(None) => Ok(vec![]),
            Err(Error::MetaFileNotFound(_)) => Ok(deps),
            Err(e) => Err(e),
        }
    }

    fn read_metadata(&mut self, file: MetaFile) -> Result<Option<&String>> {
        if let Some(ref files) = self.metadata {
            return Ok(files.get(&file));
        }
        let mut metadata = Metadata::new();
        let mut matched_count = 0u8;
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        loop {
            let mut matched_type: Option<MetaFile> = None;
            if let Some(entry) = reader.next_header() {
                for (matched, regx) in METAFILE_REGXS.iter() {
                    if regx.is_match(entry.pathname()) {
                        matched_type = Some((*matched).clone());
                        matched_count += 1;
                        break;
                    }
                }
            } else {
                break;
            }

            if matched_type.is_none() {
                continue;
            }

            let mut buf = String::new();
            loop {
                match reader.read_block() {
                    Ok(Some(bytes)) => {
                        match str::from_utf8(bytes) {
                            Ok(content) => {
                                buf.push_str(content.trim());
                            }
                            Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                        }
                    }
                    Ok(None) => {
                        debug!("content = {}", &buf);
                        metadata.insert(matched_type.unwrap(), buf);
                        break;
                    }
                    Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                }
            }//inner loop

            if matched_count == METAFILE_REGXS.len() as u8 {
                break;
            }
        }
        self.metadata = Some(metadata);
        Ok(self.metadata.as_ref().unwrap().get(&file))
    }
}

pub trait FromArchive: Sized {
    type Error: error::Error;

    fn from_archive(archive: &mut PackageArchive) -> result::Result<Self, Self::Error>;
}

#[cfg(test)]
mod test {
    use std::env;
    use std::path::PathBuf;
    use super::*;

    #[test]
    fn reading_artifact_metadata() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let ident = hart.ident().unwrap();
        assert_eq!(ident.origin, "happyhumans");
        assert_eq!(ident.name, "possums");
        assert_eq!(ident.version, Some("8.1.4".to_string()));
        assert_eq!(ident.release, Some("20160427165340".to_string()));
    }

    pub fn exe_path() -> PathBuf {
        env::current_exe().unwrap()
    }

    pub fn root() -> PathBuf {
        exe_path().parent().unwrap().parent().unwrap().parent().unwrap().join("tests")
    }

    pub fn fixtures() -> PathBuf {
        root().join("fixtures")
    }

    #[test]
    fn reading_artifact_deps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let _ = hart.deps().unwrap();
        let _ = hart.tdeps().unwrap();
    }

    #[test]
    fn reading_artifact_large_tdeps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("unhappyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let tdeps = hart.tdeps().unwrap();
        assert_eq!(1024, tdeps.len());
    }

}
<commit_msg>[core] Set mtime on files during unpacking archive<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;
use std::error;
use std::path::{Path, PathBuf};
use std::result;
use std::str::{self, FromStr};

use libarchive::writer;
use libarchive::reader::{self, Reader};
use libarchive::archive::{Entry, ReadFilter, ReadFormat, ExtractOption, ExtractOptions};
use regex::Regex;

use error::{Error, Result};
use crypto::{artifact, hash};
use package::{Identifiable, PackageIdent, MetaFile};

lazy_static! {
    static ref METAFILE_REGXS: HashMap<MetaFile, Regex> = {
        let mut map = HashMap::new();
        map.insert(MetaFile::CFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::CFlags)).unwrap());
        map.insert(MetaFile::Config, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Config)).unwrap());
        map.insert(MetaFile::Deps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Deps)).unwrap());
        map.insert(MetaFile::TDeps, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::TDeps)).unwrap());
        map.insert(MetaFile::Exposes, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Exposes)).unwrap());
        map.insert(MetaFile::Ident, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Ident)).unwrap());
        map.insert(MetaFile::LdRunPath, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdRunPath)).unwrap());
        map.insert(MetaFile::LdFlags, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::LdFlags)).unwrap());
        map.insert(MetaFile::Manifest, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Manifest)).unwrap());
        map.insert(MetaFile::Path, Regex::new(&format!(r"^hab/pkgs/([^/]+)/([^/]+)/([^/]+)/([^/]+)/{}$", MetaFile::Path)).unwrap());
        map
    };
}

type Metadata = HashMap<MetaFile, String>;

#[derive(Debug)]
pub struct PackageArchive {
    pub path: PathBuf,
    metadata: Option<Metadata>,
}

impl PackageArchive {
    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
        PackageArchive {
            path: path.into(),
            metadata: None,
        }
    }

    /// Calculate and return the checksum of the package archive in base64 format.
    ///
    /// # Failures
    ///
    /// * If the archive cannot be read
    pub fn checksum(&self) -> Result<String> {
        hash::hash_file(&self.path)
    }

    pub fn cflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::CFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn config(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Config) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// Returns a list of package identifiers representing the runtime package dependencies for
    /// this archive.
    ///
    /// # Failures
    ///
    /// * If a `DEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn deps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::Deps)
    }

    /// Returns a list of package identifiers representing the transitive runtime package
    /// dependencies for this archive.
    ///
    /// # Failures
    ///
    /// * If a `TDEPS` metafile is not found in the archive
    /// * If the archive cannot be read
    /// * If the archive cannot be verified
    pub fn tdeps(&mut self) -> Result<Vec<PackageIdent>> {
        self.read_deps(MetaFile::TDeps)
    }

    pub fn exposes(&mut self) -> Result<Vec<u16>> {
        match self.read_metadata(MetaFile::Exposes) {
            Ok(Some(data)) => {
                let ports: Vec<u16> = data.split(" ")
                    .filter_map(|port| port.parse::<u16>().ok())
                    .collect();
                Ok(ports)
            }
            Ok(None) => Ok(vec![]),
            Err(e) => Err(e),
        }
    }

    pub fn ident(&mut self) -> Result<PackageIdent> {
        match self.read_metadata(MetaFile::Ident) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Ident)),
            Ok(Some(data)) => PackageIdent::from_str(&data),
            Err(e) => Err(e),
        }
    }

    pub fn ld_run_path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdRunPath) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn ldflags(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::LdFlags) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    pub fn manifest(&mut self) -> Result<String> {
        match self.read_metadata(MetaFile::Manifest) {
            Ok(None) => Err(Error::MetaFileNotFound(MetaFile::Manifest)),
            Ok(Some(data)) => Ok(data.clone()),
            Err(e) => Err(e),
        }
    }

    pub fn path(&mut self) -> Result<Option<String>> {
        match self.read_metadata(MetaFile::Path) {
            Ok(data) => Ok(data.cloned()),
            Err(e) => Err(e),
        }
    }

    /// A plain string representation of the archive's file name.
    pub fn file_name(&self) -> String {
        self.path.file_name().unwrap().to_string_lossy().into_owned()
    }

    /// Given a package name and a path to a file as an `&str`, verify
    /// the files signature.
    ///
    /// # Failures
    ///
    /// * Fails if it cannot verify the signature for any reason
    pub fn verify<P: AsRef<Path>>(&self, cache_key_path: &P) -> Result<(String, String)> {
        artifact::verify(&self.path, cache_key_path)
    }

    /// Given a package name and a path to a file as an `&str`, unpack
    /// the package.
    ///
    /// # Failures
    ///
    /// * If the package cannot be unpacked
    pub fn unpack(&self, fs_root_path: Option<&Path>) -> Result<()> {
        let root = fs_root_path.unwrap_or(Path::new("/"));
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        let writer = writer::Disk::new();
        let mut extract_options = ExtractOptions::new();
        extract_options.add(ExtractOption::Time);
        try!(writer.set_options(&extract_options));
        try!(writer.set_standard_lookup());
        try!(writer.write(&mut reader, Some(root.to_string_lossy().as_ref())));
        try!(writer.close());
        Ok(())
    }

    fn read_deps(&mut self, file: MetaFile) -> Result<Vec<PackageIdent>> {
        let mut deps: Vec<PackageIdent> = vec![];
        match self.read_metadata(file) {
            Ok(Some(body)) => {
                debug!("body = [{}]", &body);
                let ids: Vec<String> = body.split("\n").map(|d| d.to_string()).collect();
                for id in &ids {
                    debug!("id = [{}]", &id);
                    let package = try!(PackageIdent::from_str(id));
                    if !package.fully_qualified() {
                        // JW TODO: use a more appropriate erorr to describe the invalid
                        // user input here. (user because a package was generated by a user
                        // and read into program)
                        return Err(Error::InvalidPackageIdent(package.to_string()));
                    }
                    deps.push(package);
                }
                Ok(deps)
            }
            Ok(None) => Ok(vec![]),
            Err(Error::MetaFileNotFound(_)) => Ok(deps),
            Err(e) => Err(e),
        }
    }

    fn read_metadata(&mut self, file: MetaFile) -> Result<Option<&String>> {
        if let Some(ref files) = self.metadata {
            return Ok(files.get(&file));
        }
        let mut metadata = Metadata::new();
        let mut matched_count = 0u8;
        let tar_reader = try!(artifact::get_archive_reader(&self.path));
        let mut builder = reader::Builder::new();
        try!(builder.support_format(ReadFormat::Gnutar));
        try!(builder.support_filter(ReadFilter::Xz));
        let mut reader = try!(builder.open_stream(tar_reader));
        loop {
            let mut matched_type: Option<MetaFile> = None;
            if let Some(entry) = reader.next_header() {
                for (matched, regx) in METAFILE_REGXS.iter() {
                    if regx.is_match(entry.pathname()) {
                        matched_type = Some((*matched).clone());
                        matched_count += 1;
                        break;
                    }
                }
            } else {
                break;
            }

            if matched_type.is_none() {
                continue;
            }

            let mut buf = String::new();
            loop {
                match reader.read_block() {
                    Ok(Some(bytes)) => {
                        match str::from_utf8(bytes) {
                            Ok(content) => {
                                buf.push_str(content.trim());
                            }
                            Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                        }
                    }
                    Ok(None) => {
                        debug!("content = {}", &buf);
                        metadata.insert(matched_type.unwrap(), buf);
                        break;
                    }
                    Err(_) => return Err(Error::MetaFileMalformed(matched_type.unwrap())),
                }
            }//inner loop

            if matched_count == METAFILE_REGXS.len() as u8 {
                break;
            }
        }
        self.metadata = Some(metadata);
        Ok(self.metadata.as_ref().unwrap().get(&file))
    }
}

pub trait FromArchive: Sized {
    type Error: error::Error;

    fn from_archive(archive: &mut PackageArchive) -> result::Result<Self, Self::Error>;
}

#[cfg(test)]
mod test {
    use std::env;
    use std::path::PathBuf;
    use super::*;

    #[test]
    fn reading_artifact_metadata() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let ident = hart.ident().unwrap();
        assert_eq!(ident.origin, "happyhumans");
        assert_eq!(ident.name, "possums");
        assert_eq!(ident.version, Some("8.1.4".to_string()));
        assert_eq!(ident.release, Some("20160427165340".to_string()));
    }

    pub fn exe_path() -> PathBuf {
        env::current_exe().unwrap()
    }

    pub fn root() -> PathBuf {
        exe_path().parent().unwrap().parent().unwrap().parent().unwrap().join("tests")
    }

    pub fn fixtures() -> PathBuf {
        root().join("fixtures")
    }

    #[test]
    fn reading_artifact_deps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("happyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let _ = hart.deps().unwrap();
        let _ = hart.tdeps().unwrap();
    }

    #[test]
    fn reading_artifact_large_tdeps() {
        let mut hart = PackageArchive::new(fixtures()
            .join("unhappyhumans-possums-8.1.4-20160427165340-x86_64-linux.hart"));
        let tdeps = hart.tdeps().unwrap();
        assert_eq!(1024, tdeps.len());
    }

}
<|endoftext|>


<commit_before>use crate::{error::Result,
            hcore::{fs as hfs,
                    package::{list,
                              PackageIdent}}};

use std::{path::Path,
          str::FromStr};

use clap::ArgMatches;

/// There are three options for what we can list:
///   - All packages (no prefix supplied)
///   - All packages in an origin (string with no '/')
///   - A set of packages with a common package ident (e.g. /ORIGIN/NAME) with optionally
///     version/release
#[derive(Debug)]
pub enum ListingType {
    AllPackages,
    Origin(String),
    Ident(PackageIdent),
}

/// Convert a set of command line options into a ListingType
impl<'a> From<&'a ArgMatches<'a>> for ListingType {
    /// Convert clap options into a listing type.
    ///
    /// We assume that the arguments have been validated during CLI parsing i.e.
    /// ORIGIN and PKG_IDENT are a valid origin and package identifier
    fn from(m: &ArgMatches<'_>) -> Self {
        if m.is_present("ALL") {
            return ListingType::AllPackages;
        }

        if m.is_present("ORIGIN") {
            let origin = m.value_of("ORIGIN").unwrap(); // Required by clap
            return ListingType::Origin(origin.to_string());
        }

        let p = m.value_of("PKG_IDENT").unwrap(); // Required by clap
        match PackageIdent::from_str(&p) {
            Ok(ident) => ListingType::Ident(ident),
            Err(_) => unreachable!("We've already validated PackageIdent {}", &p),
        }
    }
}

pub fn start(listing: &ListingType, fs_root_path: &Path) -> Result<()> {
    let package_path = hfs::pkg_root_path(Some(&fs_root_path));

    let mut packages = match listing {
        ListingType::AllPackages => list::all_packages(&package_path)?,
        ListingType::Origin(origin) => list::package_list_for_origin(&package_path, &origin)?,
        ListingType::Ident(ident) => list::package_list_for_ident(&package_path, &ident)?,
    };

    packages.sort_unstable_by(|a, b| a.by_parts_cmp(b));
    for p in &packages {
        println!("{}", &p);
    }

    Ok(())
}
<commit_msg>Factor out package_list<commit_after>use crate::{error::Result,
            hcore::{fs as hfs,
                    package::{list,
                              PackageIdent}}};

use std::{path::Path,
          str::FromStr};

use clap::ArgMatches;

/// There are three options for what we can list:
///   - All packages (no prefix supplied)
///   - All packages in an origin (string with no '/')
///   - A set of packages with a common package ident (e.g. /ORIGIN/NAME) with optionally
///     version/release
#[derive(Debug)]
pub enum ListingType {
    AllPackages,
    Origin(String),
    Ident(PackageIdent),
}

/// Convert a set of command line options into a ListingType
impl<'a> From<&'a ArgMatches<'a>> for ListingType {
    /// Convert clap options into a listing type.
    ///
    /// We assume that the arguments have been validated during CLI parsing i.e.
    /// ORIGIN and PKG_IDENT are a valid origin and package identifier
    fn from(m: &ArgMatches<'_>) -> Self {
        if m.is_present("ALL") {
            return ListingType::AllPackages;
        }

        if m.is_present("ORIGIN") {
            let origin = m.value_of("ORIGIN").unwrap(); // Required by clap
            return ListingType::Origin(origin.to_string());
        }

        let p = m.value_of("PKG_IDENT").unwrap(); // Required by clap
        match PackageIdent::from_str(&p) {
            Ok(ident) => ListingType::Ident(ident),
            Err(_) => unreachable!("We've already validated PackageIdent {}", &p),
        }
    }
}

pub fn package_list(listing: &ListingType, fs_root_path: &Path) -> Result<Vec<PackageIdent>> {
    let package_path = hfs::pkg_root_path(Some(&fs_root_path));

    let mut packages = match listing {
        ListingType::AllPackages => list::all_packages(&package_path)?,
        ListingType::Origin(origin) => list::package_list_for_origin(&package_path, &origin)?,
        ListingType::Ident(ident) => list::package_list_for_ident(&package_path, &ident)?,
    };

    packages.sort_unstable_by(|a, b| a.by_parts_cmp(b));
    Ok(packages)
}

pub fn start(listing: &ListingType, fs_root_path: &Path) -> Result<()> {
    let packages = package_list(listing, fs_root_path)?;
    for p in &packages {
        println!("{}", &p);
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! Traits and types for navigating order sequences of update tuples.
//!
//! The `Cursor` trait contains several methods for efficiently navigating ordered collections
//! of tuples of the form `(key, val, time, diff)`. The cursor is different from an iterator
//! both because it allows navigation on multiple levels (key and val), but also because it
//! supports efficient seeking (via the `seek_key` and `seek_val` methods).

// pub mod viewers;
pub mod cursor_list;
// pub mod cursor_pair;

/// A cursor for navigating ordered `(key, val, time, diff)` updates.
pub trait Cursor<K, V, T, R> {

	/// Indicates if the current key is valid.
	///
	/// A value of `false` indicates that the cursor has exhausted all keys.
	fn key_valid(&self) -> bool;
	/// Indicates if the current value is valid.
	///
	/// A value of `false` indicates that the cursor has exhausted all values for this key.
	fn val_valid(&self) -> bool;

	/// A reference to the current key. Asserts if invalid.
	fn key(&self) -> &K;
	/// A reference to the current value. Asserts if invalid.
	fn val(&self) -> &V;
	/// Applies `logic` to each pair of time and difference.
	fn map_times<L: FnMut(&T, R)>(&mut self, logic: L);

	/// Advances the cursor to the next key. Indicates if the key is valid.
	fn step_key(&mut self);
	/// Advances the cursor to the specified key. Indicates if the key is valid.
	fn seek_key(&mut self, key: &K);

	/// Advances the cursor to the next value. Indicates if the value is valid.
	fn step_val(&mut self);
	/// Advances the cursor to the specified value. Indicates if the value is valid.
	fn seek_val(&mut self, val: &V);

	/// Rewinds the cursor to the first key.
	fn rewind_keys(&mut self);
	/// Rewinds the cursor to the first value for current key.
	fn rewind_vals(&mut self);
}<commit_msg>Cursor.map_times doc clarification<commit_after>//! Traits and types for navigating order sequences of update tuples.
//!
//! The `Cursor` trait contains several methods for efficiently navigating ordered collections
//! of tuples of the form `(key, val, time, diff)`. The cursor is different from an iterator
//! both because it allows navigation on multiple levels (key and val), but also because it
//! supports efficient seeking (via the `seek_key` and `seek_val` methods).

// pub mod viewers;
pub mod cursor_list;
// pub mod cursor_pair;

/// A cursor for navigating ordered `(key, val, time, diff)` updates.
pub trait Cursor<K, V, T, R> {

	/// Indicates if the current key is valid.
	///
	/// A value of `false` indicates that the cursor has exhausted all keys.
	fn key_valid(&self) -> bool;
	/// Indicates if the current value is valid.
	///
	/// A value of `false` indicates that the cursor has exhausted all values for this key.
	fn val_valid(&self) -> bool;

	/// A reference to the current key. Asserts if invalid.
	fn key(&self) -> &K;
	/// A reference to the current value. Asserts if invalid.
	fn val(&self) -> &V;
	/// Applies `logic` to each pair of time and difference. Intended for mutation of the
	/// closure's scope.
	fn map_times<L: FnMut(&T, R)>(&mut self, logic: L);

	/// Advances the cursor to the next key. Indicates if the key is valid.
	fn step_key(&mut self);
	/// Advances the cursor to the specified key. Indicates if the key is valid.
	fn seek_key(&mut self, key: &K);

	/// Advances the cursor to the next value. Indicates if the value is valid.
	fn step_val(&mut self);
	/// Advances the cursor to the specified value. Indicates if the value is valid.
	fn seek_val(&mut self, val: &V);

	/// Rewinds the cursor to the first key.
	fn rewind_keys(&mut self);
	/// Rewinds the cursor to the first value for current key.
	fn rewind_vals(&mut self);
}
<|endoftext|>


<commit_before>// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

// spell-checker:ignore tcgetattr tcsetattr tcsanow

mod flags;

use clap::{crate_version, Arg, ArgMatches, Command};
use nix::sys::termios::{
    tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags, OutputFlags, Termios,
};
use std::io::{self, stdout};
use std::ops::ControlFlow;
use std::os::unix::io::{AsRawFd, RawFd};
use uucore::error::{UResult, USimpleError};
use uucore::{format_usage, InvalidEncodingHandling};

use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS};

const NAME: &str = "stty";
const USAGE: &str = "\
{} [-F DEVICE | --file=DEVICE] [SETTING]...
{} [-F DEVICE | --file=DEVICE] [-a|--all]
{} [-F DEVICE | --file=DEVICE] [-g|--save]";
const SUMMARY: &str = "Print or change terminal characteristics.";

#[derive(Clone, Copy, Debug)]
pub struct Flag<T> {
    name: &'static str,
    flag: T,
    show: bool,
    sane: bool,
    group: Option<T>,
}

impl<T> Flag<T>
where
    T: Copy,
{
    pub const fn new(name: &'static str, flag: T) -> Self {
        Self {
            name,
            flag,
            show: true,
            sane: false,
            group: None,
        }
    }

    pub const fn hidden(&self) -> Self {
        Self {
            show: false,
            ..*self
        }
    }

    pub const fn sane(&self) -> Self {
        Self {
            sane: true,
            ..*self
        }
    }

    pub const fn group(&self, group: T) -> Self {
        Self {
            group: Some(group),
            ..*self
        }
    }
}

trait TermiosFlag: Copy {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool;
    fn apply(&self, termios: &mut Termios, val: bool);
}

mod options {
    pub const ALL: &str = "all";
    pub const SAVE: &str = "save";
    pub const FILE: &str = "file";
    pub const SETTINGS: &str = "settings";
}

struct Options<'a> {
    all: bool,
    _save: bool,
    file: RawFd,
    settings: Option<Vec<&'a str>>,
}

impl<'a> Options<'a> {
    fn from(matches: &'a ArgMatches) -> io::Result<Self> {
        Ok(Self {
            all: matches.is_present(options::ALL),
            _save: matches.is_present(options::SAVE),
            file: match matches.value_of(options::FILE) {
                Some(_f) => todo!(),
                None => stdout().as_raw_fd(),
            },
            settings: matches.values_of(options::SETTINGS).map(|v| v.collect()),
        })
    }
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let opts = Options::from(&matches)?;

    stty(&opts)
}

fn stty(opts: &Options) -> UResult<()> {
    // TODO: Figure out the right error message
    let mut termios = tcgetattr(opts.file).expect("Could not get terminal attributes");
    if let Some(settings) = &opts.settings {
        for setting in settings {
            if let ControlFlow::Break(false) = apply_setting(&mut termios, setting) {
                return Err(USimpleError::new(
                    1,
                    format!("invalid argument '{}'", setting),
                ));
            }
        }

        tcsetattr(opts.file, nix::sys::termios::SetArg::TCSANOW, &termios)
            .expect("Could not write terminal attributes");
    } else {
        print_settings(&termios, opts);
    }
    Ok(())
}

fn print_settings(termios: &Termios, opts: &Options) {
    print_flags(termios, opts, &CONTROL_FLAGS);
    print_flags(termios, opts, &INPUT_FLAGS);
    print_flags(termios, opts, &OUTPUT_FLAGS);
    print_flags(termios, opts, &LOCAL_FLAGS);
}

fn print_flags<T: TermiosFlag>(termios: &Termios, opts: &Options, flags: &[Flag<T>]) {
    let mut printed = false;
    for &Flag {
        name,
        flag,
        show,
        sane,
        group,
    } in flags
    {
        if !show {
            continue;
        }
        let val = flag.is_in(termios, group);
        if group.is_some() {
            if val && (!sane || opts.all) {
                print!("{name} ");
                printed = true;
            }
        } else if opts.all || val != sane {
            if !val {
                print!("-");
            }
            print!("{name} ");
            printed = true;
        }
    }
    if printed {
        println!();
    }
}

/// Apply a single setting
///
/// The value inside the `Break` variant of the `ControlFlow` indicates whether
/// the setting has been applied.
fn apply_setting(termios: &mut Termios, s: &str) -> ControlFlow<bool> {
    let (remove, name) = match s.strip_prefix('-') {
        Some(s) => (true, s),
        None => (false, s),
    };
    apply_flag(termios, &CONTROL_FLAGS, name, remove)?;
    apply_flag(termios, &INPUT_FLAGS, name, remove)?;
    apply_flag(termios, &OUTPUT_FLAGS, name, remove)?;
    apply_flag(termios, &LOCAL_FLAGS, name, remove)?;
    ControlFlow::Break(false)
}

/// Apply a flag to a slice of flags
///
/// The value inside the `Break` variant of the `ControlFlow` indicates whether
/// the setting has been applied.
fn apply_flag<T: TermiosFlag>(
    termios: &mut Termios,
    flags: &[Flag<T>],
    input: &str,
    remove: bool,
) -> ControlFlow<bool> {
    for Flag {
        name, flag, group, ..
    } in flags
    {
        if input == *name {
            // Flags with groups cannot be removed
            // Since the name matches, we can short circuit and don't have to check the other flags.
            if remove || group.is_some() {
                return ControlFlow::Break(false);
            }
            // If there is a group, the bits for that group should be cleared before applying the flag
            if let Some(group) = group {
                group.apply(termios, false);
            }
            flag.apply(termios, !remove);
            return ControlFlow::Break(true);
        }
    }
    ControlFlow::Continue(())
}

pub fn uu_app<'a>() -> Command<'a> {
    Command::new(uucore::util_name())
        .name(NAME)
        .version(crate_version!())
        .override_usage(format_usage(USAGE))
        .about(SUMMARY)
        .infer_long_args(true)
        .arg(Arg::new(options::ALL).short('a').long(options::ALL))
        .arg(Arg::new(options::SAVE).short('g').long(options::SAVE))
        .arg(
            Arg::new(options::FILE)
                .short('F')
                .long(options::FILE)
                .takes_value(true)
                .value_hint(clap::ValueHint::FilePath),
        )
        .arg(
            Arg::new(options::SETTINGS)
                .takes_value(true)
                .multiple_values(true),
        )
}

impl TermiosFlag for ControlFlags {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
        termios.control_flags.contains(*self)
            && group.map_or(true, |g| !termios.control_flags.intersects(g - *self))
    }

    fn apply(&self, termios: &mut Termios, val: bool) {
        termios.control_flags.set(*self, val);
    }
}

impl TermiosFlag for InputFlags {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
        termios.input_flags.contains(*self)
            && group.map_or(true, |g| !termios.input_flags.intersects(g - *self))
    }

    fn apply(&self, termios: &mut Termios, val: bool) {
        termios.input_flags.set(*self, val);
    }
}

impl TermiosFlag for OutputFlags {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
        termios.output_flags.contains(*self)
            && group.map_or(true, |g| !termios.output_flags.intersects(g - *self))
    }

    fn apply(&self, termios: &mut Termios, val: bool) {
        termios.output_flags.set(*self, val);
    }
}

impl TermiosFlag for LocalFlags {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
        termios.local_flags.contains(*self)
            && group.map_or(true, |g| !termios.local_flags.intersects(g - *self))
    }

    fn apply(&self, termios: &mut Termios, val: bool) {
        termios.local_flags.set(*self, val);
    }
}
<commit_msg>stty: fix setting grouped flags<commit_after>// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

// spell-checker:ignore tcgetattr tcsetattr tcsanow

mod flags;

use clap::{crate_version, Arg, ArgMatches, Command};
use nix::sys::termios::{
    tcgetattr, tcsetattr, ControlFlags, InputFlags, LocalFlags, OutputFlags, Termios,
};
use std::io::{self, stdout};
use std::ops::ControlFlow;
use std::os::unix::io::{AsRawFd, RawFd};
use uucore::error::{UResult, USimpleError};
use uucore::{format_usage, InvalidEncodingHandling};

use flags::{CONTROL_FLAGS, INPUT_FLAGS, LOCAL_FLAGS, OUTPUT_FLAGS};

const NAME: &str = "stty";
const USAGE: &str = "\
{} [-F DEVICE | --file=DEVICE] [SETTING]...
{} [-F DEVICE | --file=DEVICE] [-a|--all]
{} [-F DEVICE | --file=DEVICE] [-g|--save]";
const SUMMARY: &str = "Print or change terminal characteristics.";

#[derive(Clone, Copy, Debug)]
pub struct Flag<T> {
    name: &'static str,
    flag: T,
    show: bool,
    sane: bool,
    group: Option<T>,
}

impl<T> Flag<T>
where
    T: Copy,
{
    pub const fn new(name: &'static str, flag: T) -> Self {
        Self {
            name,
            flag,
            show: true,
            sane: false,
            group: None,
        }
    }

    pub const fn hidden(&self) -> Self {
        Self {
            show: false,
            ..*self
        }
    }

    pub const fn sane(&self) -> Self {
        Self {
            sane: true,
            ..*self
        }
    }

    pub const fn group(&self, group: T) -> Self {
        Self {
            group: Some(group),
            ..*self
        }
    }
}

trait TermiosFlag: Copy {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool;
    fn apply(&self, termios: &mut Termios, val: bool);
}

mod options {
    pub const ALL: &str = "all";
    pub const SAVE: &str = "save";
    pub const FILE: &str = "file";
    pub const SETTINGS: &str = "settings";
}

struct Options<'a> {
    all: bool,
    _save: bool,
    file: RawFd,
    settings: Option<Vec<&'a str>>,
}

impl<'a> Options<'a> {
    fn from(matches: &'a ArgMatches) -> io::Result<Self> {
        Ok(Self {
            all: matches.is_present(options::ALL),
            _save: matches.is_present(options::SAVE),
            file: match matches.value_of(options::FILE) {
                Some(_f) => todo!(),
                None => stdout().as_raw_fd(),
            },
            settings: matches.values_of(options::SETTINGS).map(|v| v.collect()),
        })
    }
}

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
    let args = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();

    let matches = uu_app().get_matches_from(args);

    let opts = Options::from(&matches)?;

    stty(&opts)
}

fn stty(opts: &Options) -> UResult<()> {
    // TODO: Figure out the right error message
    let mut termios = tcgetattr(opts.file).expect("Could not get terminal attributes");
    if let Some(settings) = &opts.settings {
        for setting in settings {
            if let ControlFlow::Break(false) = apply_setting(&mut termios, setting) {
                return Err(USimpleError::new(
                    1,
                    format!("invalid argument '{}'", setting),
                ));
            }
        }

        tcsetattr(opts.file, nix::sys::termios::SetArg::TCSANOW, &termios)
            .expect("Could not write terminal attributes");
    } else {
        print_settings(&termios, opts);
    }
    Ok(())
}

fn print_settings(termios: &Termios, opts: &Options) {
    print_flags(termios, opts, &CONTROL_FLAGS);
    print_flags(termios, opts, &INPUT_FLAGS);
    print_flags(termios, opts, &OUTPUT_FLAGS);
    print_flags(termios, opts, &LOCAL_FLAGS);
}

fn print_flags<T: TermiosFlag>(termios: &Termios, opts: &Options, flags: &[Flag<T>]) {
    let mut printed = false;
    for &Flag {
        name,
        flag,
        show,
        sane,
        group,
    } in flags
    {
        if !show {
            continue;
        }
        let val = flag.is_in(termios, group);
        if group.is_some() {
            if val && (!sane || opts.all) {
                print!("{name} ");
                printed = true;
            }
        } else if opts.all || val != sane {
            if !val {
                print!("-");
            }
            print!("{name} ");
            printed = true;
        }
    }
    if printed {
        println!();
    }
}

/// Apply a single setting
///
/// The value inside the `Break` variant of the `ControlFlow` indicates whether
/// the setting has been applied.
fn apply_setting(termios: &mut Termios, s: &str) -> ControlFlow<bool> {
    let (remove, name) = match s.strip_prefix('-') {
        Some(s) => (true, s),
        None => (false, s),
    };
    apply_flag(termios, &CONTROL_FLAGS, name, remove)?;
    apply_flag(termios, &INPUT_FLAGS, name, remove)?;
    apply_flag(termios, &OUTPUT_FLAGS, name, remove)?;
    apply_flag(termios, &LOCAL_FLAGS, name, remove)?;
    ControlFlow::Break(false)
}

/// Apply a flag to a slice of flags
///
/// The value inside the `Break` variant of the `ControlFlow` indicates whether
/// the setting has been applied.
fn apply_flag<T: TermiosFlag>(
    termios: &mut Termios,
    flags: &[Flag<T>],
    input: &str,
    remove: bool,
) -> ControlFlow<bool> {
    for Flag {
        name, flag, group, ..
    } in flags
    {
        if input == *name {
            // Flags with groups cannot be removed
            // Since the name matches, we can short circuit and don't have to check the other flags.
            if remove && group.is_some() {
                return ControlFlow::Break(false);
            }
            // If there is a group, the bits for that group should be cleared before applying the flag
            if let Some(group) = group {
                group.apply(termios, false);
            }
            flag.apply(termios, !remove);
            return ControlFlow::Break(true);
        }
    }
    ControlFlow::Continue(())
}

pub fn uu_app<'a>() -> Command<'a> {
    Command::new(uucore::util_name())
        .name(NAME)
        .version(crate_version!())
        .override_usage(format_usage(USAGE))
        .about(SUMMARY)
        .infer_long_args(true)
        .arg(Arg::new(options::ALL).short('a').long(options::ALL))
        .arg(Arg::new(options::SAVE).short('g').long(options::SAVE))
        .arg(
            Arg::new(options::FILE)
                .short('F')
                .long(options::FILE)
                .takes_value(true)
                .value_hint(clap::ValueHint::FilePath),
        )
        .arg(
            Arg::new(options::SETTINGS)
                .takes_value(true)
                .multiple_values(true),
        )
}

impl TermiosFlag for ControlFlags {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
        termios.control_flags.contains(*self)
            && group.map_or(true, |g| !termios.control_flags.intersects(g - *self))
    }

    fn apply(&self, termios: &mut Termios, val: bool) {
        termios.control_flags.set(*self, val);
    }
}

impl TermiosFlag for InputFlags {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
        termios.input_flags.contains(*self)
            && group.map_or(true, |g| !termios.input_flags.intersects(g - *self))
    }

    fn apply(&self, termios: &mut Termios, val: bool) {
        termios.input_flags.set(*self, val);
    }
}

impl TermiosFlag for OutputFlags {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
        termios.output_flags.contains(*self)
            && group.map_or(true, |g| !termios.output_flags.intersects(g - *self))
    }

    fn apply(&self, termios: &mut Termios, val: bool) {
        termios.output_flags.set(*self, val);
    }
}

impl TermiosFlag for LocalFlags {
    fn is_in(&self, termios: &Termios, group: Option<Self>) -> bool {
        termios.local_flags.contains(*self)
            && group.map_or(true, |g| !termios.local_flags.intersects(g - *self))
    }

    fn apply(&self, termios: &mut Termios, val: bool) {
        termios.local_flags.set(*self, val);
    }
}
<|endoftext|>