1use {
4 crate::my_glibc,
5 std::{
6 collections::HashMap,
7 env,
8 ffi::{CStr, CString, NulError},
9 os::raw::{c_char, c_int},
10 },
11};
12
13#[derive(Debug)]
16pub enum OptError {
17 InvalidOption(String),
18 MissingOptionArgument(String),
19 InvalidOptind(i32),
20}
21
22impl std::error::Error for OptError {}
23
24impl std::fmt::Display for OptError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 write!(
27 f,
28 "{}",
29 match self {
30 OptError::InvalidOption(s) => format!("invalid option: {}", s),
31 OptError::MissingOptionArgument(s) => format!("missing option argument: {}", s),
32 OptError::InvalidOptind(i) => format!("invalid optind: {}", i),
33 }
34 )
35 }
36}
37
38pub type OptResult<T> = Result<T, Box<dyn std::error::Error>>;
39
40#[repr(i32)]
43pub enum HasArg {
44 NoArgument = 0,
45 RequiredArgument,
46 OptionalArgument,
47}
48
49pub struct Opt {
50 long_name: Option<CString>,
51 short_name: Option<char>,
52 has_arg: HasArg,
53 desc: String,
54}
55impl Opt {
56 pub fn new(
57 long_name: Option<String>,
58 short_name: Option<char>,
59 has_arg: HasArg,
60 desc: &str,
61 ) -> Result<Self, NulError> {
62 Ok(Self {
63 long_name: long_name.map(|v| CString::new(v)).transpose()?,
64 short_name,
65 has_arg,
66 desc: desc.to_owned(),
67 })
68 }
69 fn optstring(&self) -> Option<String> {
70 self.short_name.map(|v| {
71 let mut s = String::with_capacity(3);
72 s.push(v);
73 match self.has_arg {
74 HasArg::NoArgument => {}
75 HasArg::OptionalArgument => {
76 s.push_str("::");
77 }
78 HasArg::RequiredArgument => s.push(':'),
79 }
80 s
81 })
82 }
83 fn long_option(&self) -> Option<my_glibc::LongOption> {
84 self.long_name.as_ref().map(|v| my_glibc::LongOption {
85 name: v.as_ptr(),
86 has_arg: match self.has_arg {
87 HasArg::NoArgument => my_glibc::LongOption::NO_ARGUMENT,
88 HasArg::RequiredArgument => my_glibc::LongOption::REQUIRED_ARGUMENT,
89 HasArg::OptionalArgument => my_glibc::LongOption::OPTIONAL_ARGUMENT,
90 },
91 flag: std::ptr::null_mut(),
92 val: 0,
93 })
94 }
95}
96impl std::fmt::Display for Opt {
97 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98 write!(
99 f,
100 "{:<2}{:2}{:<16}{:<10} {}",
101 self.short_name
102 .map(|v| format!("-{}", v))
103 .unwrap_or(String::new()),
104 if self.short_name.is_none() || self.long_name.is_none() {
105 ""
106 } else {
107 ", "
108 },
109 self.long_name
110 .as_ref()
111 .map(|v| format!("--{}", v.to_str().unwrap_or("")))
112 .unwrap_or(String::new()),
113 match self.has_arg {
114 HasArg::NoArgument => "",
115 HasArg::OptionalArgument => "[=Arg]",
116 HasArg::RequiredArgument => " =Arg ",
117 },
118 self.desc
119 )
120 }
121}
122
123#[derive(Debug)]
124pub struct Arguments {
125 pub args: HashMap<String, String>,
126 pub operands: Vec<String>,
127}
128impl std::fmt::Display for Arguments {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 for a in self.args.iter() {
131 writeln!(f, "{}: {}", a.0, a.1)?;
132 }
133 for o in self.operands.iter() {
134 writeln!(f, "operand: {}", o)?;
135 }
136 Ok(())
137 }
138}
139
140pub fn getopt_long(opts: &[Opt]) -> OptResult<Arguments> {
143 let mut optstring = ":".to_owned();
144 let mut longopts = Vec::new();
145 opts.iter().for_each(|v| {
146 if let Some(s) = v.optstring() {
147 optstring.push_str(&s);
148 }
149 if let Some(lo) = v.long_option() {
150 longopts.push(lo);
151 }
152 });
153 let optstring = CString::new(optstring)?;
154 longopts.push(my_glibc::LongOption {
155 name: std::ptr::null(),
156 has_arg: 0,
157 flag: std::ptr::null_mut(),
158 val: 0,
159 });
160 let mut longindex: c_int = 0;
161
162 let mut argv = Vec::new();
164 for v in env::args() {
165 argv.push(CString::new(v)?);
166 }
167 let argv = argv
169 .into_iter()
170 .map(|arg| arg.into_raw())
171 .collect::<Vec<*mut c_char>>();
172 let mut argv = recover_guard::RecoverGuard::new(argv, |a| {
173 a.into_iter().for_each(|v| unsafe {
175 CString::from_raw(v);
176 })
177 });
178 let argc = argv.len() as c_int;
179
180 let mut args = HashMap::new();
181 loop {
182 match unsafe {
183 my_glibc::getopt_long(
184 argc,
185 argv.as_ptr(),
186 optstring.as_ptr(),
187 longopts.as_ptr(),
188 &mut longindex,
189 )
190 } {
191 -1 => break,
192 0 => {
193 let longopt = &longopts[longindex as usize];
194 args.insert(
195 unsafe { CStr::from_ptr(longopt.name) }
196 .to_str()?
197 .to_string(),
198 if unsafe { my_glibc::optarg.is_null() } {
199 String::new()
200 } else {
201 unsafe { CStr::from_ptr(my_glibc::optarg) }
202 .to_str()?
203 .to_string()
204 },
205 );
206 }
207 i => {
208 let optopt = unsafe {
209 CStr::from_ptr(
210 *argv
211 .get(my_glibc::optind as usize - 1)
212 .ok_or(OptError::InvalidOptind(my_glibc::optind))?,
213 )
214 }
215 .to_str()?
216 .to_string();
217 if i == b'?' as c_int {
218 Err(OptError::InvalidOption(optopt.clone()))?;
219 }
220 if i == b':' as c_int {
221 Err(OptError::MissingOptionArgument(optopt.clone()))?;
222 }
223
224 args.insert(
225 CStr::from_bytes_with_nul(&[i as u8, 0])?
226 .to_str()?
227 .to_string(),
228 if unsafe { my_glibc::optarg.is_null() } {
229 String::new()
230 } else {
231 unsafe { CStr::from_ptr(my_glibc::optarg) }
232 .to_str()?
233 .to_string()
234 },
235 );
236 }
237 }
238 }
239
240 let mut operands = Vec::new();
241 for v in argv.split_off(unsafe { my_glibc::optind } as usize) {
242 operands.push(unsafe { CStr::from_ptr(v) }.to_str()?.to_string());
243 }
244
245 Ok(Arguments { args, operands })
246}
247
248pub fn usage(name: &str, desc: &str, version: &str, opts: &[Opt]) {
249 println!(
250 r#"Description:
251 {}
252Version:
253 {}
254Usage:
255 {} [options [args]] [operands]
256Options:"#,
257 desc, version, name
258 );
259 opts.iter().for_each(|v| println!(" {}", v));
260 println!();
261}
262
263mod recover_guard {
266 use std::{
267 boxed::Box,
268 ops::{Deref, DerefMut, Drop, FnMut},
269 };
270
271 pub(crate) struct RecoverGuard<T, F: FnMut(T)> {
272 data: Option<T>,
273 func: Box<F>,
274 }
275
276 impl<T: Sized, F: FnMut(T)> RecoverGuard<T, F> {
277 pub fn new(data: T, func: F) -> RecoverGuard<T, F> {
278 RecoverGuard {
279 data: Some(data),
280 func: Box::new(func),
281 }
282 }
283 }
284
285 impl<T, F: FnMut(T)> Deref for RecoverGuard<T, F> {
286 type Target = T;
287
288 fn deref(&self) -> &T {
289 &self.data.as_ref().expect("the data should be here")
290 }
291 }
292
293 impl<T, F: FnMut(T)> DerefMut for RecoverGuard<T, F> {
294 fn deref_mut(&mut self) -> &mut T {
295 self.data.as_mut().expect("the data should be here")
296 }
297 }
298
299 impl<T, F: FnMut(T)> Drop for RecoverGuard<T, F> {
300 fn drop(&mut self) {
301 let data = self.data.take();
304 let ref mut f = self.func;
305 f(data.expect("the data is here until the drop"));
306 }
307 }
308}