harakit/src/getopt.rs

119 lines
3.6 KiB
Rust
Raw Normal View History

2024-04-02 02:37:35 +00:00
/*
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
use std::ffi::{ c_int, c_char, CString, CStr };
2024-04-02 02:37:35 +00:00
pub struct Opt {
pub arg: Option<String>,
pub ind: i32,
pub opt: String,
2024-04-02 02:37:35 +00:00
}
pub enum OptError {
MissingArg(String),
UnknownOpt(String),
2024-04-02 02:37:35 +00:00
}
2024-04-12 05:51:52 +00:00
/* function signature */
2024-04-02 02:37:35 +00:00
pub trait GetOpt {
fn getopt(&self, optstring: &str) -> Option<Result<Opt, OptError>>;
2024-04-02 02:37:35 +00:00
}
impl GetOpt for Vec<String> {
fn getopt(&self, optstring: &str) -> Option<Result<Opt, OptError>> {
let c_strings: Vec<_> = self
.iter()
.cloned()
.map(CString::new)
.map(Result::unwrap)
.collect();
2024-04-02 02:37:35 +00:00
2024-04-12 05:51:52 +00:00
/* these operations must be separated out into separate operations so
* the CStrings can live long enough */
let argv: Vec<_> = c_strings.iter().map(|x| x.as_ptr()).collect();
let argv_ptr = argv.as_ptr() as *const *mut c_char;
2024-04-12 05:51:52 +00:00
let opts = CString::new(optstring).unwrap().into_raw();
let len = self.len() as c_int;
2024-04-14 05:42:11 +00:00
unsafe { // TODO: enable optind modification
match getopt(len, argv_ptr, opts) {
2024-04-02 02:37:35 +00:00
/* From getopt(3p):
*
* The getopt() function shall return the next option character
* specified on the command line.
*
* A <colon> (':') shall be returned if getopt() detects a
* missing argument and the first character of optstring was a
* <colon> (':').
*
* A <question-mark> ('?') shall be returned if getopt()
* encounters an option character not in optstring or detects a
* missing argument and the first character of optstring was not
* a <colon> (':').
*
* Otherwise, getopt() shall return -1 when all command line
* options are parsed. */
2024-04-12 05:51:52 +00:00
58 => { /* numerical ASCII value for ':' */
Some(Err(OptError::MissingArg(optopt.to_string())))
2024-04-02 02:37:35 +00:00
},
2024-04-12 05:51:52 +00:00
63 => { /* numerical ASCII value for '?' */
Some(Err(OptError::UnknownOpt(optopt.to_string())))
2024-04-02 02:37:35 +00:00
},
/* From getopt(3p):
*
* If, when getopt() is called:
*
* argv[optind] is a null pointer
* *argv[optind] is not the character -
* argv[optind] points to the string "-"
*
* getopt() shall return -1 without changing optind. If:
*
* argv[optind] points to the string "--"
*
* getopt() shall return -1 after incrementing optind. */
-1 => return None,
opt => {
let arg = CStr::from_ptr(optarg)
.to_string_lossy()
.into_owned();
2024-04-02 02:37:35 +00:00
Some(Ok(Opt {
2024-04-14 05:42:11 +00:00
arg: Some(arg), /* opt argument */
ind: optind, /* opt index */
opt: opt.to_string(), /* option itself */
}))
},
}
}
}
2024-04-02 02:37:35 +00:00
}
/* binding to getopt(3p) */
extern "C" {
static mut optarg: *mut c_char;
2024-04-02 02:37:35 +00:00
static mut _opterr: c_int;
static mut optind: c_int;
static mut optopt: c_int;
fn getopt(
___argc: c_int,
___argv: *const *mut c_char,
__shortopts: *const c_char,
) -> c_int;
}