1use std::process::exit;
15use std::{fmt::Display, fmt::Formatter};
16
17#[derive(Debug)]
19pub enum Verbosity {
20 Quiet,
21 Errors,
22 Warnings,
23 Info,
24 Debug,
25 Trace,
26}
27#[derive(Debug)]
28
29pub 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)]
44pub 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#[allow(dead_code)]
57#[derive(Debug)]
58pub enum Status {
59 Init,
60 NoData,
61}
62
63#[derive(Debug)]
64pub struct BzOpts {
65 pub block_size: usize,
67 pub files: Vec<String>,
69 pub force_overwrite: bool,
71 pub keep_input_files: bool,
73 pub iterations: usize,
75 pub op_mode: Mode,
77 pub output: Output,
79 pub small: bool,
81 pub status: Status,
83 pub verbose: Verbosity,
85 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");
114pub fn bzopts_init() -> BzOpts {
116 let mut cli = BzOpts::new();
117 {
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 }
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 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 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
314fn 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
351fn 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