1 use std::path::PathBuf;
2 use std::{env, ffi, fs, io, process};
3
4 extern crate weezl;
5 use weezl::{decode as delzw, encode as enlzw, BitOrder};
6
main()7 fn main() {
8 let args = env::args_os().skip(1);
9 let flags = Flags::from_args(args).unwrap_or_else(|ParamError| explain());
10
11 let out = io::stdout();
12 let out = out.lock();
13
14 let mut files = flags.files;
15 let input = files.pop().unwrap_or_else(explain);
16 if !files.is_empty() {
17 return explain();
18 }
19 let operation = flags.operation.unwrap_or_else(explain);
20 let min_code = if flags.min_code < 2 || flags.min_code > 12 {
21 return explain();
22 } else {
23 flags.min_code
24 };
25 let bit_order = flags.bit_order;
26
27 let result = match (input, operation) {
28 (Input::File(file), Operation::Encode) => (|| {
29 let data = fs::File::open(file)?;
30 let file = io::BufReader::with_capacity(1 << 26, data);
31
32 let mut encoder = enlzw::Encoder::new(bit_order, min_code);
33 encoder.into_stream(out).encode_all(file).status
34 })(),
35 (Input::Stdin, Operation::Encode) => {
36 let input = io::BufReader::with_capacity(1 << 26, io::stdin());
37 let mut encoder = enlzw::Encoder::new(bit_order, min_code);
38 encoder.into_stream(out).encode_all(input).status
39 }
40 (Input::File(file), Operation::Decode) => (|| {
41 let data = fs::File::open(file)?;
42 let file = io::BufReader::with_capacity(1 << 26, data);
43
44 let mut decoder = delzw::Decoder::new(bit_order, min_code);
45 decoder.into_stream(out).decode_all(file).status
46 })(),
47 (Input::Stdin, Operation::Decode) => {
48 let input = io::BufReader::with_capacity(1 << 26, io::stdin());
49 let mut decoder = delzw::Decoder::new(bit_order, min_code);
50 decoder.into_stream(out).decode_all(input).status
51 }
52 };
53
54 result.expect("Operation Failed: ");
55 }
56
57 struct Flags {
58 files: Vec<Input>,
59 operation: Option<Operation>,
60 min_code: u8,
61 bit_order: BitOrder,
62 }
63
64 struct ParamError;
65
66 enum Input {
67 File(PathBuf),
68 Stdin,
69 }
70
71 enum Operation {
72 Encode,
73 Decode,
74 }
75
explain<T>() -> T76 fn explain<T>() -> T {
77 println!(
78 "Usage: lzw [-e|-d] <file>\n\
79 Arguments:\n\
80 -e\t operation encode (default)\n\
81 -d\t operation decode\n\
82 <file>\tfilepath or '-' for stdin"
83 );
84 process::exit(1);
85 }
86
87 impl Default for Flags {
default() -> Flags88 fn default() -> Flags {
89 Flags {
90 files: vec![],
91 operation: None,
92 min_code: 8,
93 bit_order: BitOrder::Msb,
94 }
95 }
96 }
97
98 impl Flags {
from_args(mut args: impl Iterator<Item = ffi::OsString>) -> Result<Self, ParamError>99 fn from_args(mut args: impl Iterator<Item = ffi::OsString>) -> Result<Self, ParamError> {
100 let mut flags = Flags::default();
101 let mut operation = None;
102 loop {
103 match args.next().as_ref().and_then(|s| s.to_str()) {
104 Some("-d") | Some("--decode") => {
105 if operation.is_some() {
106 return Err(ParamError);
107 }
108 operation = Some(Operation::Decode);
109 }
110 Some("-e") | Some("--encode") => {
111 if operation.is_some() {
112 return Err(ParamError);
113 }
114 operation = Some(Operation::Encode);
115 }
116 Some("-w") | Some("--word-bits") => match args.next() {
117 None => return Err(ParamError),
118 Some(bits) => {
119 let st = bits.to_str().ok_or(ParamError)?;
120 flags.min_code = st.parse().ok().ok_or(ParamError)?;
121 }
122 },
123 Some("-le") | Some("--little-endian") => {
124 flags.bit_order = BitOrder::Lsb;
125 }
126 Some("-be") | Some("--big-endian") | Some("-ne") | Some("--network-endian") => {
127 flags.bit_order = BitOrder::Msb;
128 }
129 Some("-") => {
130 flags.files.push(Input::Stdin);
131 }
132 Some(other) if other.starts_with('-') => {
133 // Reserved for future use.
134 // -a: self-describing archive format, similar to actual compress
135 // -b: maximum bits
136 // -v: verbosity
137 // some compress compatibility mode? Probably through arg(0) though.
138 return Err(ParamError);
139 }
140 Some(file) => {
141 flags.files.push(Input::File(file.into()));
142 }
143 None => break,
144 };
145 }
146
147 flags.files.extend(args.map(|file| {
148 if let Some("-") = file.to_str() {
149 Input::Stdin
150 } else {
151 Input::File(file.into())
152 }
153 }));
154
155 flags.operation = operation;
156 Ok(flags)
157 }
158 }
159