1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! This `main` module uses the `options` module to read its command-line-arguments.
//! It defines code for spawning the _merger-thread_ who
//! collects the results produced by the worker threads.
//! The processing of the input-data is initiated by the `input`-module that itself uses
//! the `scanner` module in which the worker-threads are spawned.

mod mission;

mod input;
use crate::input::process_input;

mod options;
use crate::options::ARGS;

mod scanner;
use crate::scanner::ScannerPool;

mod finding;

mod helper;

mod codec {
    pub mod ascii;
}

use crate::mission::MISSIONS;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::process;
use std::str;
use std::thread::JoinHandle;

use std::sync::mpsc;

use std::thread;

use encoding::all;
use itertools::kmerge;
use itertools::Itertools;

// Version is defined in ../Cargo.toml
const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
const AUTHOR: &str = "(c) Jens Getreu, 2016-2018";

/// This function spawns and defines the behaviour of the _merger-thread_ who
/// collects and prints the results produced by the worker threads.
fn main2() -> Result<(), Box<std::io::Error>> {
    if ARGS.flag_list_encodings {
        let list = all::encodings()
            .iter()
            .filter_map(|&e| e.whatwg_name())
            .sorted();
        // Available encodings
        for e in list {
            println!("{}", e);
        }
        return Ok(());
    }

    if ARGS.flag_version {
        println!("Version {}, {}", VERSION.unwrap_or("unknown"), AUTHOR);
        return Ok(());
    }

    let merger: JoinHandle<Result<(), Box<std::io::Error>>>;
    // Scope for threads
    {
        let n_threads = MISSIONS.len();
        let (tx, rx) = mpsc::sync_channel(n_threads);
        let mut sc = ScannerPool::new(&MISSIONS, &tx);

        // Receive `FindingCollection`s from scanner threads.
        merger = thread::spawn(move || {
            let mut output = match ARGS.flag_output {
                Some(ref fname) => {
                    let f = File::create(&Path::new(fname.as_str()))?;
                    Box::new(f) as Box<dyn Write>
                }
                None => Box::new(io::stdout()) as Box<dyn Write>,
            };
            output.write_all("\u{feff}".as_bytes())?;

            'outer: loop {
                // collect
                let mut results = Vec::with_capacity(n_threads);
                for _ in 0..n_threads {
                    results.push(match rx.recv() {
                        Ok(fc) => {
                            //fc.print(&mut output);
                            fc.v
                        }
                        Err(_) => break 'outer,
                    });
                }
                // merge
                for finding in kmerge(&results) {
                    finding.print(&mut output)?;
                }
            }
            //println!("Merger terminated.");
            Ok(())
        });

        // Default for <file> is stdin.
        if (ARGS.arg_FILE.is_empty()) || ((ARGS.arg_FILE.len() == 1) && ARGS.arg_FILE[0] == "-") {
            process_input(None, &mut sc)?;
        } else {
            for filename in ARGS.arg_FILE.iter() {
                if let Err(e) = process_input(Some(&filename), &mut sc) {
                    writeln!(
                        &mut std::io::stderr(),
                        "Warning: `{}` while scanning file `{}`.",
                        e,
                        filename
                    )
                    .unwrap();
                };
            }
        };
    } // `tx` drops here, which "break"s the merger-loop.
    merger.join().unwrap()

    //println!("All threads terminated.");
}

fn main() {
    if let Err(e) = main2() {
        writeln!(&mut std::io::stderr(), "Error: `{}`.", e).unwrap();
        process::exit(1);
    }
}