bzip2_os/tools/
cli.rs

1//! Parse the command line for Rust BZIP2.
2//!
3//! The options that can be set are defined in the `BzOpts` struct. An instance of the struct must be created and 
4//! initialized.
5//! 
6//! Usage example:
7//! ```
8//! let mut opts = BzOpts::new();
9//! opts.init();
10//! ```
11//! You can then access the options via the instance you created.
12//! 
13
14use std::process::exit;
15use std::{fmt::Display, fmt::Formatter};
16
17/// Verbosity of user information
18#[derive(Debug)]
19pub enum Verbosity {
20    Quiet,
21    Errors,
22    Warnings,
23    Info,
24    Debug,
25    Trace,
26}
27#[derive(Debug)]
28
29/// Zip, Unzip, Test, and testing out Build_Index and Unzip_Blocks
30pub enum Mode {
31    Zip,
32    Unzip,
33    Test,
34    BuildIndex,
35    UnzipBlocks
36}
37impl Display for Mode {
38    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39        write!(f, "{:?}", self)
40    }
41}
42
43#[derive(Debug)]
44/// Define the two output channels
45pub enum Output {
46    File,
47    Stdout,
48}
49impl Display for Output {
50    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
51        write!(f, "{:?}", self)
52    }
53}
54
55/// NOT YET IMPLEMENTED. Used during the library mode by the calling program.
56#[allow(dead_code)]
57#[derive(Debug)]
58pub enum Status {
59    Init,
60    NoData,
61}
62
63#[derive(Debug)]
64pub struct BzOpts {
65    /// Maximum input block size to process during each loop
66    pub block_size: usize,
67    /// Vec of names of files to read for input
68    pub files: Vec<String>,
69    /// Silently overwrite existing files with the same name
70    pub force_overwrite: bool,
71    /// Don't remove input files after processing
72    pub keep_input_files: bool,
73    /// Iterations used to test/optimize small block compression
74    pub iterations: usize,
75    /// Compress/Decompress/Test
76    pub op_mode: Mode,
77    /// Location where output is sent
78    pub output: Output,
79    /// Small memory footprint requested
80    pub small: bool,
81    /// Current status of progress - not yet used
82    pub status: Status,
83    /// Verbosity of user information
84    pub verbose: Verbosity,
85    /// Optional setting used for oddly constructed data - may be depricated
86    pub work_factor: usize,
87}
88
89impl BzOpts {
90    pub fn new() -> Self {
91        Self {
92            block_size: 9,
93            files: vec![],
94            force_overwrite: false,
95            keep_input_files: false,
96            iterations: 4,
97            op_mode: Mode::Zip,
98            output: Output::File,
99            small: false,
100            status: Status::Init,
101            verbose: Verbosity::Errors,
102            work_factor: 30,
103        }
104    }
105}
106
107impl Default for BzOpts {
108    fn default() -> Self {
109        Self::new()
110    }
111}
112
113const VERSION: &str = env!("CARGO_PKG_VERSION");
114/// Initialize the options struct by parsing data supplied via the command line. Takes no arguments and returns nothing.
115pub fn bzopts_init() -> BzOpts {
116    let mut cli = BzOpts::new();
117    // Print opening line
118    {
119        let descr = "bzip2, a block-sorting file compressor.";
120        let created = "14-Jan-2023";
121        println!("{}  Rust version {}, {}", descr, VERSION, created);
122    }
123
124    let args = std::env::args().skip(1);
125    for mut arg in args {
126        if arg.starts_with("--") {
127            match arg.as_str() {
128                "--help" => help(),
129                "--decompress" => {
130                    cli.op_mode = Mode::Unzip;
131                }
132                "--compress" => {
133                    cli.op_mode = Mode::Zip;
134                }
135                "--index" => {
136                    cli.op_mode = Mode::BuildIndex;
137                }
138                "--extract" => {
139                    cli.op_mode = Mode::UnzipBlocks;
140                    // NEED TO IMPLEMENT WAY TO SPECIFY BLOCKS TO EXTRACT
141                }
142                "--keep" => cli.keep_input_files = true,
143                "--force" => cli.force_overwrite = true,
144                "--test" => cli.op_mode = Mode::Test,
145                "--stdout" => cli.output = Output::Stdout,
146                "--quiet" => cli.verbose = Verbosity::Quiet,
147                "--verbose" => cli.verbose = Verbosity::Errors,
148                "--license" => license(),
149                "--version" => version(),
150                "--small" => cli.small = true,
151                "--fast" => cli.block_size = 1,
152                "--best" => cli.block_size = 9,
153
154                other => eprintln!("Unexpected command line argument: {}", other),
155            }
156        } else if arg.starts_with('-') {
157            arg.remove(0);
158            while !arg.is_empty() {
159                if arg.starts_with("vvvvv") {
160                    cli.verbose = Verbosity::Trace;
161                    arg.remove(0);
162                    arg.remove(0);
163                    arg.remove(0);
164                    arg.remove(0);
165                    arg.remove(0);
166                    // And remove any excess v's
167                    while arg.starts_with('v') {
168                        arg.remove(0);
169                    }
170                    continue;
171                }
172                if arg.starts_with("vvvv") {
173                    cli.verbose = Verbosity::Debug;
174                    arg.remove(0);
175                    arg.remove(0);
176                    arg.remove(0);
177                    arg.remove(0);
178                    continue;
179                } else if arg.starts_with("vvv") {
180                    cli.verbose = Verbosity::Info;
181                    arg.remove(0);
182                    arg.remove(0);
183                    arg.remove(0);
184                    continue;
185                } else if arg.starts_with("vv") {
186                    cli.verbose = Verbosity::Warnings;
187                    arg.remove(0);
188                    arg.remove(0);
189                    continue;
190                } else if arg.starts_with('v') {
191                    cli.verbose = Verbosity::Errors;
192                    arg.remove(0);
193                    continue;
194                }
195                if arg.starts_with('h') {
196                    help();
197                    arg.remove(0);
198                    continue;
199                }
200                if arg.starts_with('d') {
201                    cli.op_mode = Mode::Unzip;
202                    arg.remove(0);
203                    continue;
204                }
205                if arg.starts_with('z') {
206                    cli.op_mode = Mode::Zip;
207                    arg.remove(0);
208                    continue;
209                }
210                if arg.starts_with('k') {
211                    cli.keep_input_files = true;
212                    arg.remove(0);
213                    continue;
214                }
215                if arg.starts_with('f') {
216                    cli.force_overwrite = true;
217                    arg.remove(0);
218                    continue;
219                }
220                if arg.starts_with('t') {
221                    cli.op_mode = Mode::Test;
222                    arg.remove(0);
223                    continue;
224                }
225                if arg.starts_with('c') {
226                    cli.output = Output::Stdout;
227                    arg.remove(0);
228                    continue;
229                }
230                if arg.starts_with('q') {
231                    cli.verbose = Verbosity::Quiet;
232                    arg.remove(0);
233                    continue;
234                }
235                if arg.starts_with('L') {
236                    license();
237                    arg.remove(0);
238                    continue;
239                }
240                if arg.starts_with('V') {
241                    version();
242                    arg.remove(0);
243                    continue;
244                }
245                if arg.starts_with('s') {
246                    cli.small = true;
247                    arg.remove(0);
248                    continue;
249                }
250                if arg.starts_with('1') {
251                    cli.block_size = 1;
252                    arg.remove(0);
253                    continue;
254                }
255                if arg.starts_with('2') {
256                    cli.block_size = 2;
257                    arg.remove(0);
258                    continue;
259                }
260                if arg.starts_with('3') {
261                    cli.block_size = 3;
262                    arg.remove(0);
263                    continue;
264                }
265                if arg.starts_with('4') {
266                    cli.block_size = 4;
267                    arg.remove(0);
268                    continue;
269                }
270                if arg.starts_with('5') {
271                    cli.block_size = 5;
272                    arg.remove(0);
273                    continue;
274                }
275                if arg.starts_with('6') {
276                    cli.block_size = 6;
277                    arg.remove(0);
278                    continue;
279                }
280                if arg.starts_with('7') {
281                    cli.block_size = 7;
282                    arg.remove(0);
283                    continue;
284                }
285                if arg.starts_with('8') {
286                    cli.block_size = 8;
287                    arg.remove(0);
288                    continue;
289                }
290                if arg.starts_with('9') {
291                    cli.block_size = 9;
292                    arg.remove(0);
293                    continue;
294                }
295                eprintln!("Unexpected command line argument: {}", arg);
296                help()
297            }
298        } else {
299            cli.files.push(arg);
300        };
301    }
302    // Set the log level
303    match cli.verbose {
304        Verbosity::Quiet => log::set_max_level(log::LevelFilter::Off),
305        Verbosity::Errors => log::set_max_level(log::LevelFilter::Error),
306        Verbosity::Warnings => log::set_max_level(log::LevelFilter::Warn),
307        Verbosity::Info => log::set_max_level(log::LevelFilter::Info),
308        Verbosity::Debug => log::set_max_level(log::LevelFilter::Debug),
309        Verbosity::Trace => log::set_max_level(log::LevelFilter::Trace),
310    };
311    cli
312}
313
314/// Prints help information
315fn help() {
316    println!(
317        "
318   usage: bzip2 [flags and input files in any order]
319
320   -h --help           print this message
321   -d --decompress     force decompression
322   -z --compress       force compression
323   -k --keep           keep (don't delete) input files (ALWAYS KEEPS. NOT IMPLEMENTED)
324   -f --force          overwrite existing output files (NOT IMPLEMENTED)
325   -t --test           test compressed file integrity
326   -c --stdout         output to standard out
327   -q --quiet          suppress noncritical error messages
328   -v --verbose        be verbose (a 2nd -v gives more)
329   -L --license        display software version & license
330   -V --version        display software version & license
331   -s --small          use less memory (at most 2500k) (NOT IMPLEMENTED)
332   -1 .. -9            set block size to 100k .. 900k
333   --fast              alias for -1
334   --best              alias for -9
335   
336    If invoked as `bzip2', default action is to compress.
337              as `bunzip2',  default action is to decompress.
338              as `bzcat', default action is to decompress to stdout.
339
340   If no file names are given, bzip2 compresses or decompresses
341   from standard input to standard output.  You can combine
342   short flags, so `-v -4' means the same as -v4 or -4v, &c.
343
344   Temporarily, you can specify one of these alogrithms for the BWT
345     -vvvvv (trace level debugging information)
346   "
347    );
348    exit(0);
349}
350
351/// Official license statement for Bzip2
352fn license() {
353    println!(
354        "
355   bzip2, a block-sorting file compressor.
356   Copyright (C) 1996-2010 by Julian Seward; 2010-2023 by various.
357 
358   This program is free software; you can redistribute it and/or modify
359   it under the terms set out in the LICENSE file, which is included
360   in the bzip2 source distribution.
361
362   This program is distributed in the hope that it will be useful,
363   but WITHOUT ANY WARRANTY; without even the implied warranty of
364   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
365   LICENSE file for more details."
366    );
367    exit(0);
368}
369
370fn version() {
371    println!("Version: {}, written in Rust", VERSION);
372    exit(0);
373}
374
375/*
376/// Define all user settable options to control program behavior
377#[derive(Debug)]
378pub struct BzOptsOld {
379    /// Optional name of file to read for input
380    pub file: Option<String>,
381    /// Maximum input block size to process during each loop
382    pub block_size: u8,
383    /// User feedback level setting
384    pub op_mode: Mode,
385    /// Don't remove input files after processing
386    pub keep_input_files: bool,
387    /// Optional setting used for oddly constructed data - may be depricated
388    pub work_factor: WorkFactor,
389    /// Silently overwrite existing files with the same name
390    pub force_overwrite: bool,
391    /// Location where output is sent
392    pub output: Output,
393    /// Current status of progress - not yet used
394    pub status: Status,
395    /// Algorithm used
396    pub algorithm: Algorithms,
397    /// Iterations used to test/optimize small block compression
398    pub iterations: usize,
399}
400*/