forked from bonsai/harakit
		
	Merge branch 'getopt-bindings'
This commit is contained in:
		
						commit
						cb88ed9809
					
				
							
								
								
									
										21
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								Makefile
									
									
									
									
									
								
							@ -50,9 +50,12 @@ install: dist
 | 
			
		||||
	cp -r $(DESTDIR)/* /
 | 
			
		||||
 | 
			
		||||
.PHONY: test
 | 
			
		||||
test: build
 | 
			
		||||
test: build /tmp/getopt
 | 
			
		||||
	/tmp/getopt
 | 
			
		||||
	tests/posix-compat.sh
 | 
			
		||||
	$(RUSTC) --test src/getopt-rs/lib.rs -o build/test/getopt
 | 
			
		||||
 | 
			
		||||
/tmp/getopt: src/libgetopt.rs
 | 
			
		||||
	$(RUSTC) --test -o /tmp/getopt src/libgetopt.rs
 | 
			
		||||
 | 
			
		||||
.PHONY: docs
 | 
			
		||||
docs: docs/ build
 | 
			
		||||
@ -65,13 +68,13 @@ docs: docs/ build
 | 
			
		||||
rustlibs: build/o/libsysexits.rlib build/o/libgetopt.rlib \
 | 
			
		||||
	build/o/libstrerror.rlib
 | 
			
		||||
 | 
			
		||||
build/o/libgetopt.rlib: build src/getopt-rs/lib.rs
 | 
			
		||||
build/o/libgetopt.rlib: build src/libgetopt.rs
 | 
			
		||||
	$(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \
 | 
			
		||||
		-o $@ src/getopt-rs/lib.rs
 | 
			
		||||
		-o $@ src/libgetopt.rs
 | 
			
		||||
 | 
			
		||||
build/o/libstrerror.rlib: build src/strerror.rs
 | 
			
		||||
build/o/libstrerror.rlib: build src/libstrerror.rs
 | 
			
		||||
	$(RUSTC) $(RUSTFLAGS) --crate-type=lib -o $@ \
 | 
			
		||||
		src/strerror.rs
 | 
			
		||||
		src/libstrerror.rs
 | 
			
		||||
 | 
			
		||||
# bandage solution until bindgen(1) gets stdin support
 | 
			
		||||
build/o/libsysexits.rlib: build $(SYSEXITS)sysexits.h
 | 
			
		||||
@ -137,10 +140,8 @@ build/bin/strcmp: src/strcmp.c build
 | 
			
		||||
 | 
			
		||||
.PHONY: swab
 | 
			
		||||
swab: build/bin/swab
 | 
			
		||||
build/bin/swab: src/swab.rs build build/o/libsysexits.rlib
 | 
			
		||||
	$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
 | 
			
		||||
		--extern sysexits=build/o/libsysexits.rlib \
 | 
			
		||||
		-o $@ src/swab.rs
 | 
			
		||||
build/bin/swab: src/swab.rs build rustlibs
 | 
			
		||||
	$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/swab.rs
 | 
			
		||||
 | 
			
		||||
.PHONY: true
 | 
			
		||||
true: build/bin/true
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								src/fop.rs
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								src/fop.rs
									
									
									
									
									
								
							@ -26,27 +26,39 @@ extern crate getopt;
 | 
			
		||||
extern crate strerror;
 | 
			
		||||
extern crate sysexits;
 | 
			
		||||
 | 
			
		||||
use getopt::{ Opt, Parser };
 | 
			
		||||
use getopt::GetOpt;
 | 
			
		||||
use strerror::StrError;
 | 
			
		||||
use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
	let argv = args().collect::<Vec<String>>();
 | 
			
		||||
	let mut d = '\u{1E}'.to_string();
 | 
			
		||||
	let mut arg_parser = Parser::new(&argv, "d:");
 | 
			
		||||
	let mut index_arg = 0;
 | 
			
		||||
 | 
			
		||||
	while let Some(opt) = arg_parser.next() {
 | 
			
		||||
		match opt {
 | 
			
		||||
			Ok(Opt('d', Some(arg))) => d = arg,
 | 
			
		||||
			_ => {},
 | 
			
		||||
	let usage = format!(
 | 
			
		||||
		"Usage: {} [-d delimiter] index command [args...]",
 | 
			
		||||
		argv[0],
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	while let Some(opt) = argv.getopt("d:") {
 | 
			
		||||
		match opt.opt() {
 | 
			
		||||
			Ok(_) => {
 | 
			
		||||
				/* unwrap because Err(OptError::MissingArg) will be returned if
 | 
			
		||||
				 * opt.arg() is None */
 | 
			
		||||
				d = opt.arg().unwrap();
 | 
			
		||||
				index_arg = opt.ind();
 | 
			
		||||
			},
 | 
			
		||||
			Err(_) => {
 | 
			
		||||
				eprintln!("{}", usage);
 | 
			
		||||
				exit(EX_USAGE);
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	let index_arg = arg_parser.index();
 | 
			
		||||
	let command_arg = arg_parser.index() + 1;
 | 
			
		||||
	let command_arg = index_arg as usize + 1;
 | 
			
		||||
 | 
			
		||||
	argv.get(command_arg).unwrap_or_else(|| {
 | 
			
		||||
		eprintln!("Usage: {} [-d delimiter] index command [args...]", argv[0]);
 | 
			
		||||
		eprintln!("{}", usage);
 | 
			
		||||
		exit(EX_USAGE);
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,95 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU Affero 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 Affero General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
			
		||||
 *
 | 
			
		||||
 * This file incorporates work covered by the following copyright and permission
 | 
			
		||||
 * notice:
 | 
			
		||||
 *     The Clear BSD License
 | 
			
		||||
 *
 | 
			
		||||
 *     Copyright © 2017-2023 David Wildasin
 | 
			
		||||
 *     All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *     Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 *     modification, are permitted (subject to the limitations in the disclaimer
 | 
			
		||||
 *     below) provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer in the
 | 
			
		||||
 *          documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Neither the name of the copyright holder nor the names of its
 | 
			
		||||
 *          contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *          this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 *     NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED
 | 
			
		||||
 *     BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | 
			
		||||
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 | 
			
		||||
 *     BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
			
		||||
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 *     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 *     TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 *     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 *     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 *     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 *     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::{ error, fmt };
 | 
			
		||||
 | 
			
		||||
use crate::ErrorKind::{ self, * };
 | 
			
		||||
 | 
			
		||||
/// A basic error type for [`Parser`](struct.Parser.html)
 | 
			
		||||
#[derive(Debug, Eq, PartialEq)]
 | 
			
		||||
pub struct Error {
 | 
			
		||||
    culprit: char,
 | 
			
		||||
    kind: ErrorKind,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Error {
 | 
			
		||||
    /// Creates a new error using a known kind and the character that caused the
 | 
			
		||||
	/// issue.
 | 
			
		||||
    pub fn new(kind: ErrorKind, culprit: char) -> Self {
 | 
			
		||||
        Self { culprit, kind }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns the [`ErrorKind`](enum.ErrorKind.html) for this error.
 | 
			
		||||
    pub fn kind(self) -> ErrorKind {
 | 
			
		||||
        self.kind
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Error {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        match self.kind {
 | 
			
		||||
            MissingArgument => write!(
 | 
			
		||||
				f, 
 | 
			
		||||
				"option requires an argument -- {:?}",
 | 
			
		||||
				self.culprit,
 | 
			
		||||
			),
 | 
			
		||||
            UnknownOption => write!(f, "unknown option -- {:?}", self.culprit),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl error::Error for Error {
 | 
			
		||||
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,61 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU Affero 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 Affero General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
			
		||||
 *
 | 
			
		||||
 * This file incorporates work covered by the following copyright and permission
 | 
			
		||||
 * notice:
 | 
			
		||||
 *     The Clear BSD License
 | 
			
		||||
 *
 | 
			
		||||
 *     Copyright © 2017-2023 David Wildasin
 | 
			
		||||
 *     All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *     Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 *     modification, are permitted (subject to the limitations in the disclaimer
 | 
			
		||||
 *     below) provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer in the
 | 
			
		||||
 *          documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Neither the name of the copyright holder nor the names of its
 | 
			
		||||
 *          contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *          this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 *     NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED
 | 
			
		||||
 *     BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | 
			
		||||
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 | 
			
		||||
 *     BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
			
		||||
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 *     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 *     TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 *     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 *     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 *     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 *     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/// What kinds of errors [`Parser`](struct.Parser.html) can return.
 | 
			
		||||
#[derive(Debug, Eq, PartialEq)]
 | 
			
		||||
pub enum ErrorKind {
 | 
			
		||||
    /// An argument was not found for an option that was expecting one.
 | 
			
		||||
    MissingArgument,
 | 
			
		||||
    /// An unknown option character was encountered.
 | 
			
		||||
    UnknownOption,
 | 
			
		||||
}
 | 
			
		||||
@ -1,72 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU Affero 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 Affero General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
			
		||||
 *
 | 
			
		||||
 * This file incorporates work covered by the following copyright and permission
 | 
			
		||||
 * notice:
 | 
			
		||||
 *     The Clear BSD License
 | 
			
		||||
 *
 | 
			
		||||
 *     Copyright © 2017-2023 David Wildasin
 | 
			
		||||
 *     All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *     Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 *     modification, are permitted (subject to the limitations in the disclaimer
 | 
			
		||||
 *     below) provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer in the
 | 
			
		||||
 *          documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Neither the name of the copyright holder nor the names of its
 | 
			
		||||
 *          contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *          this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 *     NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED
 | 
			
		||||
 *     BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | 
			
		||||
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 | 
			
		||||
 *     BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
			
		||||
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 *     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 *     TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 *     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 *     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 *     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 *     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
//! # getopt
 | 
			
		||||
//!
 | 
			
		||||
//! `getopt` provides a minimal, (essentially) POSIX-compliant option parser.
 | 
			
		||||
 | 
			
		||||
pub use crate::{
 | 
			
		||||
	error::Error,
 | 
			
		||||
	errorkind::ErrorKind,
 | 
			
		||||
	opt::Opt,
 | 
			
		||||
	parser::Parser,
 | 
			
		||||
	result::Result
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod error;
 | 
			
		||||
mod errorkind;
 | 
			
		||||
mod opt;
 | 
			
		||||
mod parser;
 | 
			
		||||
mod result;
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests;
 | 
			
		||||
@ -1,89 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU Affero 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 Affero General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
			
		||||
 *
 | 
			
		||||
 * This file incorporates work covered by the following copyright and permission
 | 
			
		||||
 * notice:
 | 
			
		||||
 *     The Clear BSD License
 | 
			
		||||
 *
 | 
			
		||||
 *     Copyright © 2017-2023 David Wildasin
 | 
			
		||||
 *     All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *     Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 *     modification, are permitted (subject to the limitations in the disclaimer
 | 
			
		||||
 *     below) provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer in the
 | 
			
		||||
 *          documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Neither the name of the copyright holder nor the names of its
 | 
			
		||||
 *          contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *          this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 *     NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED
 | 
			
		||||
 *     BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | 
			
		||||
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 | 
			
		||||
 *     BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
			
		||||
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 *     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 *     TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 *     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 *     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 *     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 *     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::fmt;
 | 
			
		||||
 | 
			
		||||
/// A single option.
 | 
			
		||||
///
 | 
			
		||||
/// For `Opt(x, y)`:
 | 
			
		||||
///   - `x` is the character representing the option.
 | 
			
		||||
///   - `y` is `Some` string, or `None` if no argument was expected.
 | 
			
		||||
///
 | 
			
		||||
/// # Example
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
/// use getopt::Opt;
 | 
			
		||||
///
 | 
			
		||||
/// // args = ["program", "-abc", "foo"];
 | 
			
		||||
/// # let args: Vec<String> = vec!["program", "-abc", "foo"]
 | 
			
		||||
/// #     .into_iter()
 | 
			
		||||
/// #     .map(String::from)
 | 
			
		||||
/// #     .collect();
 | 
			
		||||
/// let optstring = "ab:c";
 | 
			
		||||
/// let mut opts = getopt::Parser::new(&args, optstring);
 | 
			
		||||
///
 | 
			
		||||
/// assert_eq!(Opt('a', None), opts.next().transpose()?.unwrap());
 | 
			
		||||
/// assert_eq!(Opt('b', Some("c".to_string())), opts.next().transpose()?.unwrap());
 | 
			
		||||
/// assert_eq!(None, opts.next().transpose()?);
 | 
			
		||||
/// # Ok(())
 | 
			
		||||
/// # }
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
 | 
			
		||||
pub struct Opt(pub char, pub Option<String>);
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Opt {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 | 
			
		||||
        write!(f, "Opt({:?}, {:?})", self.0, self.1)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,382 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU Affero 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 Affero General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
			
		||||
 *
 | 
			
		||||
 * This file incorporates work covered by the following copyright and permission
 | 
			
		||||
 * notice:
 | 
			
		||||
 *     The Clear BSD License
 | 
			
		||||
 *
 | 
			
		||||
 *     Copyright © 2017-2023 David Wildasin
 | 
			
		||||
 *     All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *     Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 *     modification, are permitted (subject to the limitations in the disclaimer
 | 
			
		||||
 *     below) provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer in the
 | 
			
		||||
 *          documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Neither the name of the copyright holder nor the names of its
 | 
			
		||||
 *          contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *          this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 *     NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED
 | 
			
		||||
 *     BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | 
			
		||||
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 | 
			
		||||
 *     BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
			
		||||
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 *     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 *     TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 *     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 *     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 *     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 *     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use crate::{ error::Error, errorkind::ErrorKind, opt::Opt, result::Result };
 | 
			
		||||
 | 
			
		||||
/// The core of the `getopt` crate.
 | 
			
		||||
///
 | 
			
		||||
/// `Parser` is implemented as an iterator over the options present in the given
 | 
			
		||||
/// argument vector.
 | 
			
		||||
///
 | 
			
		||||
/// The method [`next`](#method.next) does the heavy lifting.
 | 
			
		||||
///
 | 
			
		||||
/// # Examples
 | 
			
		||||
///
 | 
			
		||||
/// ## Simplified usage:
 | 
			
		||||
/// ```
 | 
			
		||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
/// use getopt::Opt;
 | 
			
		||||
///
 | 
			
		||||
/// // args = ["program", "-abc", "foo"];
 | 
			
		||||
/// # let args: Vec<String> = vec!["program", "-abc", "foo"]
 | 
			
		||||
/// #     .into_iter()
 | 
			
		||||
/// #     .map(String::from)
 | 
			
		||||
/// #     .collect();
 | 
			
		||||
/// let mut opts = getopt::Parser::new(&args, "ab:c");
 | 
			
		||||
///
 | 
			
		||||
/// assert_eq!(Some(Opt('a', None)), opts.next().transpose()?);
 | 
			
		||||
/// assert_eq!(1, opts.index());
 | 
			
		||||
/// assert_eq!(Some(Opt('b', Some("c".to_string()))), opts.next().transpose()?);
 | 
			
		||||
/// assert_eq!(2, opts.index());
 | 
			
		||||
/// assert_eq!(None, opts.next());
 | 
			
		||||
/// assert_eq!(2, opts.index());
 | 
			
		||||
/// assert_eq!("foo", args[opts.index()]);
 | 
			
		||||
/// # Ok(())
 | 
			
		||||
/// # }
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ## A more idiomatic example:
 | 
			
		||||
/// ```
 | 
			
		||||
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
/// use getopt::Opt;
 | 
			
		||||
///
 | 
			
		||||
/// // args = ["program", "-abc", "-d", "foo", "-e", "bar"];
 | 
			
		||||
/// # let mut args: Vec<String> = vec!["program", "-abc", "-d", "foo", "-e", "bar"]
 | 
			
		||||
/// #     .into_iter()
 | 
			
		||||
/// #     .map(String::from)
 | 
			
		||||
/// #     .collect();
 | 
			
		||||
/// let mut opts = getopt::Parser::new(&args, "ab:cd:e");
 | 
			
		||||
///
 | 
			
		||||
/// let mut a_flag = false;
 | 
			
		||||
/// let mut b_flag = String::new();
 | 
			
		||||
/// let mut c_flag = false;
 | 
			
		||||
/// let mut d_flag = String::new();
 | 
			
		||||
/// let mut e_flag = false;
 | 
			
		||||
///
 | 
			
		||||
/// loop {
 | 
			
		||||
///     match opts.next().transpose()? {
 | 
			
		||||
///         None => break,
 | 
			
		||||
///         Some(opt) => match opt {
 | 
			
		||||
///             Opt('a', None) => a_flag = true,
 | 
			
		||||
///             Opt('b', Some(arg)) => b_flag = arg.clone(),
 | 
			
		||||
///             Opt('c', None) => c_flag = true,
 | 
			
		||||
///             Opt('d', Some(arg)) => d_flag = arg.clone(),
 | 
			
		||||
///             Opt('e', None) => e_flag = true,
 | 
			
		||||
///             _ => unreachable!(),
 | 
			
		||||
///         },
 | 
			
		||||
///     }
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// let new_args = args.split_off(opts.index());
 | 
			
		||||
///
 | 
			
		||||
/// assert_eq!(true, a_flag);
 | 
			
		||||
/// assert_eq!("c", b_flag);
 | 
			
		||||
/// assert_eq!(false, c_flag);
 | 
			
		||||
/// assert_eq!("foo", d_flag);
 | 
			
		||||
/// assert_eq!(true, e_flag);
 | 
			
		||||
///
 | 
			
		||||
/// assert_eq!(1, new_args.len());
 | 
			
		||||
/// assert_eq!("bar", new_args.first().unwrap());
 | 
			
		||||
/// # Ok(())
 | 
			
		||||
/// # }
 | 
			
		||||
/// ```
 | 
			
		||||
#[derive(Debug, Eq, PartialEq)]
 | 
			
		||||
pub struct Parser {
 | 
			
		||||
    opts: HashMap<char, bool>,
 | 
			
		||||
    args: Vec<Vec<char>>,
 | 
			
		||||
    index: usize,
 | 
			
		||||
    point: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Parser {
 | 
			
		||||
    /// Create a new `Parser`, which will process the arguments in `args`
 | 
			
		||||
	/// according to the options specified in `optstring`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// For compatibility with
 | 
			
		||||
	/// [`std::env::args`](https://doc.rust-lang.org/std/env/fn.args.html),
 | 
			
		||||
    /// valid options are expected to begin at the second element of `args`, and
 | 
			
		||||
	/// `index` is
 | 
			
		||||
    /// initialised to `1`.
 | 
			
		||||
    /// If `args` is structured differently, be sure to call
 | 
			
		||||
	/// [`set_index`](#method.set_index) before the first invocation of
 | 
			
		||||
	/// [`next`](#method.next).
 | 
			
		||||
    ///
 | 
			
		||||
    /// `optstring` is a string of recognised option characters; if a character
 | 
			
		||||
	/// is followed by a colon (`:`), that option takes an argument.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Note:
 | 
			
		||||
    /// Transforming the OS-specific argument strings into a vector of `String`s
 | 
			
		||||
	/// is the sole responsibility of the calling program, as it involves some
 | 
			
		||||
	/// level of potential information loss (which this crate does not presume
 | 
			
		||||
	/// to handle unilaterally) and error handling (which would complicate the
 | 
			
		||||
	/// interface).
 | 
			
		||||
    pub fn new(args: &[String], optstring: &str) -> Self {
 | 
			
		||||
        let optstring: Vec<char> = optstring.chars().collect();
 | 
			
		||||
        let mut opts = HashMap::new();
 | 
			
		||||
        let mut i = 0;
 | 
			
		||||
        let len = optstring.len();
 | 
			
		||||
 | 
			
		||||
        while i < len {
 | 
			
		||||
            let j = i + 1;
 | 
			
		||||
 | 
			
		||||
            if j < len && optstring[j] == ':' {
 | 
			
		||||
                opts.insert(optstring[i], true);
 | 
			
		||||
                i += 1;
 | 
			
		||||
            } else {
 | 
			
		||||
                opts.insert(optstring[i], false);
 | 
			
		||||
            }
 | 
			
		||||
            i += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            opts,
 | 
			
		||||
            // "explode" the args into a vector of character vectors, to allow
 | 
			
		||||
			// indexing
 | 
			
		||||
            args: args.iter().map(|e| e.chars().collect()).collect(),
 | 
			
		||||
            index: 1,
 | 
			
		||||
            point: 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the current `index` of the parser.
 | 
			
		||||
    ///
 | 
			
		||||
    /// `args[index]` will always point to the the next element of `args`; when
 | 
			
		||||
	/// the parser is
 | 
			
		||||
    /// finished with an element, it will increment `index`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// After the last option has been parsed (and [`next`](#method.next) is
 | 
			
		||||
	/// returning `None`),
 | 
			
		||||
    /// `index` will point to the first non-option argument.
 | 
			
		||||
    pub fn index(&self) -> usize {
 | 
			
		||||
        self.index
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // `point` must be reset to 0 whenever `index` is changed
 | 
			
		||||
 | 
			
		||||
    /// Modify the current `index` of the parser.
 | 
			
		||||
    pub fn set_index(&mut self, value: usize) {
 | 
			
		||||
        self.index = value;
 | 
			
		||||
        self.point = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Increment the current `index` of the parser.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This use case is common enough to warrant its own optimised method.
 | 
			
		||||
    pub fn incr_index(&mut self) {
 | 
			
		||||
        self.index += 1;
 | 
			
		||||
        self.point = 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Iterator for Parser {
 | 
			
		||||
    type Item = Result<Opt>;
 | 
			
		||||
 | 
			
		||||
    /// Returns the next option, if any.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Returns an [`Error`](struct.Error.html) if an unexpected option is
 | 
			
		||||
	/// encountered or if an
 | 
			
		||||
    /// expected argument is not found.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Parsing stops at the first non-hyphenated argument; or at the first
 | 
			
		||||
	/// argument matching "-";
 | 
			
		||||
    /// or after the first argument matching "--".
 | 
			
		||||
    ///
 | 
			
		||||
    /// When no more options are available, `next` returns `None`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ## "-"
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use getopt::Parser;
 | 
			
		||||
    ///
 | 
			
		||||
    /// // args = ["program", "-", "-a"];
 | 
			
		||||
    /// # let args: Vec<String> = vec!["program", "-", "-a"]
 | 
			
		||||
    /// #     .into_iter()
 | 
			
		||||
    /// #     .map(String::from)
 | 
			
		||||
    /// #     .collect();
 | 
			
		||||
    /// let mut opts = Parser::new(&args, "a");
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(None, opts.next());
 | 
			
		||||
    /// assert_eq!("-", args[opts.index()]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// ## "--"
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use getopt::Parser;
 | 
			
		||||
    ///
 | 
			
		||||
    /// // args = ["program", "--", "-a"];
 | 
			
		||||
    /// # let args: Vec<String> = vec!["program", "--", "-a"]
 | 
			
		||||
    /// #     .into_iter()
 | 
			
		||||
    /// #     .map(String::from)
 | 
			
		||||
    /// #     .collect();
 | 
			
		||||
    /// let mut opts = Parser::new(&args, "a");
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(None, opts.next());
 | 
			
		||||
    /// assert_eq!("-a", args[opts.index()]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// ## Unexpected option:
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use getopt::Parser;
 | 
			
		||||
    ///
 | 
			
		||||
    /// // args = ["program", "-b"];
 | 
			
		||||
    /// # let args: Vec<String> = vec!["program", "-b"]
 | 
			
		||||
    /// #     .into_iter()
 | 
			
		||||
    /// #     .map(String::from)
 | 
			
		||||
    /// #     .collect();
 | 
			
		||||
    /// let mut opts = Parser::new(&args, "a");
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     "unknown option -- 'b'".to_string(),
 | 
			
		||||
    ///     opts.next().unwrap().unwrap_err().to_string()
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// ## Missing argument:
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use getopt::Parser;
 | 
			
		||||
    ///
 | 
			
		||||
    /// // args = ["program", "-a"];
 | 
			
		||||
    /// # let args: Vec<String> = vec!["program", "-a"]
 | 
			
		||||
    /// #     .into_iter()
 | 
			
		||||
    /// #     .map(String::from)
 | 
			
		||||
    /// #     .collect();
 | 
			
		||||
    /// let mut opts = Parser::new(&args, "a:");
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(
 | 
			
		||||
    ///     "option requires an argument -- 'a'".to_string(),
 | 
			
		||||
    ///     opts.next().unwrap().unwrap_err().to_string()
 | 
			
		||||
    /// );
 | 
			
		||||
    /// ```
 | 
			
		||||
    fn next(&mut self) -> Option<Result<Opt>> {
 | 
			
		||||
        if self.point == 0 {
 | 
			
		||||
            /*
 | 
			
		||||
             * Rationale excerpts below taken verbatim from "The Open Group Base
 | 
			
		||||
			 * Specifications Issue 7, 2018 edition", IEEE Std 1003.1-2017
 | 
			
		||||
			 * (Revision of IEEE Std 1003.1-2008).
 | 
			
		||||
             * Copyright © 2001-2018 IEEE and The Open Group.
 | 
			
		||||
             */
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * 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 self.index >= self.args.len()
 | 
			
		||||
                || self.args[self.index].is_empty()
 | 
			
		||||
                || self.args[self.index][0] != '-'
 | 
			
		||||
                || self.args[self.index].len() == 1
 | 
			
		||||
            {
 | 
			
		||||
                return None;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            /*
 | 
			
		||||
             * If:
 | 
			
		||||
             *      argv[optind]    points to the string "--"
 | 
			
		||||
             * getopt() shall return -1 after incrementing index.
 | 
			
		||||
             */
 | 
			
		||||
            if self.args[self.index][1] == '-' && self.args[self.index].len() == 2 {
 | 
			
		||||
                self.incr_index();
 | 
			
		||||
                return None;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // move past the starting '-'
 | 
			
		||||
            self.point += 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let opt = self.args[self.index][self.point];
 | 
			
		||||
        self.point += 1;
 | 
			
		||||
 | 
			
		||||
        match self.opts.get(&opt) {
 | 
			
		||||
            None => {
 | 
			
		||||
                if self.point >= self.args[self.index].len() {
 | 
			
		||||
                    self.incr_index();
 | 
			
		||||
                }
 | 
			
		||||
                Some(Err(Error::new(ErrorKind::UnknownOption, opt)))
 | 
			
		||||
            }
 | 
			
		||||
            Some(false) => {
 | 
			
		||||
                if self.point >= self.args[self.index].len() {
 | 
			
		||||
                    self.incr_index();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Some(Ok(Opt(opt, None)))
 | 
			
		||||
            }
 | 
			
		||||
            Some(true) => {
 | 
			
		||||
                let arg: String = if self.point >= self.args[self.index].len() {
 | 
			
		||||
                    self.incr_index();
 | 
			
		||||
                    if self.index >= self.args.len() {
 | 
			
		||||
                        return Some(Err(Error::new(
 | 
			
		||||
									ErrorKind::MissingArgument,
 | 
			
		||||
									opt,
 | 
			
		||||
								)));
 | 
			
		||||
                    }
 | 
			
		||||
                    self.args[self.index].iter().collect()
 | 
			
		||||
                } else {
 | 
			
		||||
                    self.args[self.index]
 | 
			
		||||
                        .clone()
 | 
			
		||||
                        .split_off(self.point)
 | 
			
		||||
                        .iter()
 | 
			
		||||
                        .collect()
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                self.incr_index();
 | 
			
		||||
 | 
			
		||||
                Some(Ok(Opt(opt, Some(arg))))
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,59 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU Affero 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 Affero General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
			
		||||
 *
 | 
			
		||||
 * This file incorporates work covered by the following copyright and permission
 | 
			
		||||
 * notice:
 | 
			
		||||
 *     The Clear BSD License
 | 
			
		||||
 *
 | 
			
		||||
 *     Copyright © 2017-2023 David Wildasin
 | 
			
		||||
 *     All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *     Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 *     modification, are permitted (subject to the limitations in the disclaimer
 | 
			
		||||
 *     below) provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer in the
 | 
			
		||||
 *          documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Neither the name of the copyright holder nor the names of its
 | 
			
		||||
 *          contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *          this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 *     NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED
 | 
			
		||||
 *     BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | 
			
		||||
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 | 
			
		||||
 *     BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
			
		||||
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 *     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 *     TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 *     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 *     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 *     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 *     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::result;
 | 
			
		||||
 | 
			
		||||
use crate::error::Error;
 | 
			
		||||
 | 
			
		||||
/// A specialized `Result` type for use with [`Parser`](struct.Parser.html)
 | 
			
		||||
pub type Result<T> = result::Result<T, Error>;
 | 
			
		||||
@ -1,228 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU Affero 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 Affero General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
			
		||||
 *
 | 
			
		||||
 * This file incorporates work covered by the following copyright and permission
 | 
			
		||||
 * notice:
 | 
			
		||||
 *     The Clear BSD License
 | 
			
		||||
 *
 | 
			
		||||
 *     Copyright © 2017-2023 David Wildasin
 | 
			
		||||
 *     All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 *     Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 *     modification, are permitted (subject to the limitations in the disclaimer
 | 
			
		||||
 *     below) provided that the following conditions are met:
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions of source code must retain the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *          notice, this list of conditions, and the following disclaimer in the
 | 
			
		||||
 *          documentation and/or other materials provided with the distribution.
 | 
			
		||||
 *
 | 
			
		||||
 *          * Neither the name of the copyright holder nor the names of its
 | 
			
		||||
 *          contributors may be used to endorse or promote products derived from
 | 
			
		||||
 *          this software without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 *     NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED
 | 
			
		||||
 *     BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 | 
			
		||||
 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 | 
			
		||||
 *     BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 | 
			
		||||
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | 
			
		||||
 *     HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | 
			
		||||
 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 | 
			
		||||
 *     TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 | 
			
		||||
 *     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 | 
			
		||||
 *     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 | 
			
		||||
 *     NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 | 
			
		||||
 *     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use crate::{Opt, Parser};
 | 
			
		||||
 | 
			
		||||
macro_rules! basic_test {
 | 
			
		||||
    ($name:ident, $expect:expr, $next:expr, [$($arg:expr),+], $optstr:expr) => (
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn $name() -> Result<(), String> {
 | 
			
		||||
            let expect: Option<Opt> = $expect;
 | 
			
		||||
            let args: Vec<String> = vec![$($arg),+]
 | 
			
		||||
				.into_iter()
 | 
			
		||||
				.map(String::from)
 | 
			
		||||
				.collect();
 | 
			
		||||
            let next: Option<String> = $next;
 | 
			
		||||
            let mut opts = Parser::new(&args, $optstr);
 | 
			
		||||
 | 
			
		||||
            match opts.next().transpose() {
 | 
			
		||||
                Err(error) => {
 | 
			
		||||
                    return Err(format!("next() returned {:?}", error))
 | 
			
		||||
                },
 | 
			
		||||
                Ok(actual) => if actual != expect {
 | 
			
		||||
                    return Err(
 | 
			
		||||
						format!("expected {:?}; got {:?}", expect, actual)
 | 
			
		||||
					)
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            match next {
 | 
			
		||||
                None => if opts.index() < args.len() {
 | 
			
		||||
                    return Err(format!(
 | 
			
		||||
							"expected end of args; got {:?}", args[opts.index()]
 | 
			
		||||
					))
 | 
			
		||||
                },
 | 
			
		||||
                Some(n) => if args[opts.index()] != n {
 | 
			
		||||
                    return Err(format!(
 | 
			
		||||
							"next arg: expected {:?}; got {:?}",
 | 
			
		||||
							n,
 | 
			
		||||
							args[opts.index()]
 | 
			
		||||
					))
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[rustfmt::skip] basic_test!(
 | 
			
		||||
	blank_arg, None, Some(String::new()), ["x", ""], "a"
 | 
			
		||||
);
 | 
			
		||||
#[rustfmt::skip] basic_test!(
 | 
			
		||||
	double_dash, None, Some("-a".to_string()), ["x", "--", "-a", "foo"], "a"
 | 
			
		||||
);
 | 
			
		||||
#[rustfmt::skip] basic_test!(no_opts_1, None, None, ["x"], "a");
 | 
			
		||||
#[rustfmt::skip] basic_test!(
 | 
			
		||||
	no_opts_2, None, Some("foo".to_string()), ["x", "foo"], "a"
 | 
			
		||||
);
 | 
			
		||||
#[rustfmt::skip] basic_test!(
 | 
			
		||||
	no_opts_3, None, Some("foo".to_string()), ["x", "foo", "-a"], "a"
 | 
			
		||||
);
 | 
			
		||||
#[rustfmt::skip] basic_test!(
 | 
			
		||||
	single_dash, None, Some("-".to_string()), ["x", "-", "-a", "foo"], "a"
 | 
			
		||||
);
 | 
			
		||||
#[rustfmt::skip] basic_test!(
 | 
			
		||||
	single_opt,
 | 
			
		||||
	Some(Opt('a', None)),
 | 
			
		||||
	Some("foo".to_string()),
 | 
			
		||||
	["x", "-a", "foo"],
 | 
			
		||||
	"a"
 | 
			
		||||
);
 | 
			
		||||
#[rustfmt::skip] basic_test!(
 | 
			
		||||
	single_optarg,
 | 
			
		||||
	Some(Opt('a', Some("foo".to_string()))),
 | 
			
		||||
	None,
 | 
			
		||||
	["x", "-a", "foo"],
 | 
			
		||||
	"a:"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
macro_rules! error_test {
 | 
			
		||||
    ($name:ident, $expect:expr, [$($arg:expr),+], $optstr:expr) => (
 | 
			
		||||
        #[test]
 | 
			
		||||
        fn $name() -> Result<(), String> {
 | 
			
		||||
            let expect: String = $expect.to_string();
 | 
			
		||||
            let args: Vec<String> = vec![$($arg),+]
 | 
			
		||||
				.into_iter()
 | 
			
		||||
				.map(String::from)
 | 
			
		||||
				.collect();
 | 
			
		||||
            let mut opts = Parser::new(&args, $optstr);
 | 
			
		||||
 | 
			
		||||
            match opts.next() {
 | 
			
		||||
                None => {
 | 
			
		||||
                    return Err(format!(
 | 
			
		||||
							"unexpected successful response: end of options"
 | 
			
		||||
					))
 | 
			
		||||
                },
 | 
			
		||||
                Some(Err(actual)) => {
 | 
			
		||||
                    let actual = actual.to_string();
 | 
			
		||||
 | 
			
		||||
                    if actual != expect {
 | 
			
		||||
                        return Err(
 | 
			
		||||
							format!("expected {:?}; got {:?}", expect, actual)
 | 
			
		||||
						);
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                Some(Ok(opt)) => {
 | 
			
		||||
                    return Err(
 | 
			
		||||
						format!("unexpected successful response: {:?}", opt)
 | 
			
		||||
					)
 | 
			
		||||
                },
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Ok(())
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[rustfmt::skip] error_test!(
 | 
			
		||||
	bad_opt,
 | 
			
		||||
	"unknown option -- 'b'",
 | 
			
		||||
	["x", "-b"],
 | 
			
		||||
	"a"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#[rustfmt::skip] error_test!(
 | 
			
		||||
	missing_optarg,
 | 
			
		||||
	"option requires an argument -- 'a'",
 | 
			
		||||
	["x", "-a"],
 | 
			
		||||
	"a:"
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn multiple() -> Result<(), String> {
 | 
			
		||||
    let args: Vec<String> = vec!["x", "-abc", "-d", "foo", "-e", "bar"]
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .map(String::from)
 | 
			
		||||
        .collect();
 | 
			
		||||
    let optstring = "ab:d:e".to_string();
 | 
			
		||||
    let mut opts = Parser::new(&args, &optstring);
 | 
			
		||||
 | 
			
		||||
    macro_rules! check_result {
 | 
			
		||||
        ($expect:expr) => {
 | 
			
		||||
            let expect: Option<Opt> = $expect;
 | 
			
		||||
            match opts.next().transpose() {
 | 
			
		||||
                Err(error) => {
 | 
			
		||||
					return Err(format!("next() returned {:?}", error));
 | 
			
		||||
				},
 | 
			
		||||
                Ok(actual) => {
 | 
			
		||||
                    if actual != expect {
 | 
			
		||||
                        return Err(
 | 
			
		||||
							format!("expected {:?}; got {:?}", expect, actual)
 | 
			
		||||
						);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    check_result!(Some(Opt('a', None)));
 | 
			
		||||
    check_result!(Some(Opt('b', Some("c".to_string()))));
 | 
			
		||||
    check_result!(Some(Opt('d', Some("foo".to_string()))));
 | 
			
		||||
    check_result!(Some(Opt('e', None)));
 | 
			
		||||
    check_result!(None);
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn continue_after_error() {
 | 
			
		||||
    let args: Vec<String> = vec!["x", "-z", "-abc"]
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .map(String::from)
 | 
			
		||||
        .collect();
 | 
			
		||||
    let optstring = "ab:d:e".to_string();
 | 
			
		||||
    for _opt in Parser::new(&args, &optstring) {
 | 
			
		||||
        // do nothing, should not panic
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										202
									
								
								src/libgetopt.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								src/libgetopt.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,202 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2023–2024 Emma Tebibyte <emma@tebibyte.media>
 | 
			
		||||
 * SPDX-License-Identifier: AGPL-3.0-or-later
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software: you can redistribute it and/or modify it under
 | 
			
		||||
 * the terms of the GNU Affero 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 Affero General Public License for more
 | 
			
		||||
 * details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program. If not, see https://www.gnu.org/licenses/.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
use std::ffi::{ c_int, c_char, CString, CStr };
 | 
			
		||||
 | 
			
		||||
/* binding to getopt(3p) */
 | 
			
		||||
extern "C" {
 | 
			
		||||
	static mut optarg: *mut c_char;
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub enum OptError {
 | 
			
		||||
	MissingArg(String),
 | 
			
		||||
	UnknownOpt(String),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Opt {
 | 
			
		||||
	arg: Option<String>, /* option argument */
 | 
			
		||||
	ind: *mut i32, /* option index */
 | 
			
		||||
	opt: Result<String, OptError>, /* option option */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Opt {
 | 
			
		||||
	pub fn arg(&self) -> Option<String> { self.arg.clone() }
 | 
			
		||||
 | 
			
		||||
	/* sets optarg if default is desired */
 | 
			
		||||
	pub fn arg_or(&self, default: impl std::fmt::Display) -> String {
 | 
			
		||||
		default.to_string()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* makes matching the output of this method more bearable */
 | 
			
		||||
	pub fn opt(&self) -> Result<&str, OptError> {
 | 
			
		||||
		self.opt.as_ref().map(|o| o.as_str()).map_err(OptError::clone)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* From getopt(3p):
 | 
			
		||||
	 *
 | 
			
		||||
	 * The variable optind is the index of the next element of the argv[]
 | 
			
		||||
	 * vector to be processed. It shall be initialized to 1 by the system, and
 | 
			
		||||
	 * getopt() shall update it when it finishes with each element of argv[].
 | 
			
		||||
	 * If the application sets optind to zero  before calling getopt(), the
 | 
			
		||||
	 * behavior is unspecified. When an element of argv[] contains multiple
 | 
			
		||||
	 * option characters, it is unspecified how getopt() determines which
 | 
			
		||||
	 * options have already been processed. */
 | 
			
		||||
	pub fn ind(&self) -> usize { unsafe { *self.ind as usize } }
 | 
			
		||||
 | 
			
		||||
	/* this is patently terrible and is only happening because I’m stubborn */
 | 
			
		||||
	pub fn set_ind(&self, ind: i32) { unsafe { *self.ind = ind; } }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* function signature */
 | 
			
		||||
pub trait GetOpt {
 | 
			
		||||
	fn getopt(&self, optstring: &str) -> Option<Opt>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl GetOpt for Vec<String> {
 | 
			
		||||
    fn getopt(&self, optstring: &str) -> Option<Opt> {
 | 
			
		||||
        let c_strings: Vec<_> = self
 | 
			
		||||
			.iter()
 | 
			
		||||
			.cloned()
 | 
			
		||||
			.map(|x| CString::new(x).unwrap().into_raw())
 | 
			
		||||
			.collect();
 | 
			
		||||
 | 
			
		||||
		/* god knows what this does */
 | 
			
		||||
		let boxed = Box::into_raw(c_strings.into_boxed_slice());
 | 
			
		||||
		let argv = boxed as *const *mut c_char;
 | 
			
		||||
 | 
			
		||||
		/* operations are separated out so that everything lives long enough */
 | 
			
		||||
        let opts = CString::new(optstring).unwrap().into_raw();
 | 
			
		||||
        let len = self.len() as c_int;
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
            let ret = match getopt(len, argv, opts) { 
 | 
			
		||||
				/* 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. */
 | 
			
		||||
				58 => { /* ASCII ':' */
 | 
			
		||||
					Some(Opt {
 | 
			
		||||
						arg: None,
 | 
			
		||||
						ind: std::ptr::addr_of_mut!(optind),
 | 
			
		||||
						/* error containing option */
 | 
			
		||||
						opt: Err(OptError::MissingArg(optopt.to_string())),
 | 
			
		||||
					})
 | 
			
		||||
				},
 | 
			
		||||
				63 => { /* ASCII '?' */
 | 
			
		||||
					Some(Opt {
 | 
			
		||||
						arg: None,
 | 
			
		||||
						ind: std::ptr::addr_of_mut!(optind),
 | 
			
		||||
						/* error containing option */
 | 
			
		||||
						opt: Err(OptError::UnknownOpt(optopt.to_string())),
 | 
			
		||||
					})
 | 
			
		||||
				},
 | 
			
		||||
				/* 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: Option<String>;
 | 
			
		||||
 | 
			
		||||
					if optarg.is_null() { arg = None; }
 | 
			
		||||
					else {
 | 
			
		||||
						arg = Some(CStr::from_ptr(optarg)
 | 
			
		||||
							.to_string_lossy()
 | 
			
		||||
							.into_owned());
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					Some(Opt {
 | 
			
		||||
						arg,
 | 
			
		||||
						ind: std::ptr::addr_of_mut!(optind),
 | 
			
		||||
						/* I didn’t need to cast this before; I rewrote the
 | 
			
		||||
						 * pointer logic and now I do
 | 
			
		||||
						 *
 | 
			
		||||
						 * I don’t know why this is */
 | 
			
		||||
						opt: Ok((opt as u8 as char).to_string()),
 | 
			
		||||
					})
 | 
			
		||||
				},
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
			/* delloc argv (something online said I should do this) */
 | 
			
		||||
			let _ = Box::from_raw(boxed);
 | 
			
		||||
			return ret;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* tests (good) */
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
	use GetOpt;
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn testing() {
 | 
			
		||||
		let argv: Vec<String> = ["test", "-b", "-f", "arg", "-o", "arg"]
 | 
			
		||||
			.iter()
 | 
			
		||||
			.map(|s| s.to_string())
 | 
			
		||||
			.collect();
 | 
			
		||||
 | 
			
		||||
		while let Some(opt) = argv.getopt(":abf:o:") {
 | 
			
		||||
			match opt.opt() {
 | 
			
		||||
				Ok("a") => assert_eq!(opt.ind(), 1),
 | 
			
		||||
				Ok("b") => assert_eq!(opt.ind(), 2),
 | 
			
		||||
				Ok("f") | Ok("o") => {
 | 
			
		||||
					assert_eq!(opt.arg(), Some("arg".into()));
 | 
			
		||||
				},
 | 
			
		||||
				_ => assert!(false),
 | 
			
		||||
			};
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if let Some(opt) = argv.getopt("abc:") {
 | 
			
		||||
			opt.clone().set_ind(1);
 | 
			
		||||
			assert_eq!(opt.ind(), 1);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								src/swab.rs
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/swab.rs
									
									
									
									
									
								
							@ -24,7 +24,7 @@ use std::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
extern crate getopt;
 | 
			
		||||
use getopt::{ Opt, Parser };
 | 
			
		||||
use getopt::GetOpt;
 | 
			
		||||
 | 
			
		||||
extern crate sysexits;
 | 
			
		||||
use sysexits::{ EX_OK, EX_OSERR, EX_USAGE };
 | 
			
		||||
@ -45,24 +45,21 @@ fn main() -> ExitCode {
 | 
			
		||||
	let mut input = stdin();
 | 
			
		||||
	let mut output = stdout().lock();
 | 
			
		||||
 | 
			
		||||
	let mut opts = Parser::new(&argv, "fw:");
 | 
			
		||||
	let mut force = false;
 | 
			
		||||
	let mut wordsize: usize = 2;
 | 
			
		||||
 | 
			
		||||
	loop {
 | 
			
		||||
		match opts.next() {
 | 
			
		||||
			None => break,
 | 
			
		||||
			Some(opt) =>
 | 
			
		||||
				match opt {
 | 
			
		||||
					Ok(Opt('f', None)) => force = true,
 | 
			
		||||
					Ok(Opt('w', Some(arg))) => {
 | 
			
		||||
						match arg.parse::<usize>() {
 | 
			
		||||
							Ok(w) if w % 2 == 0 => { wordsize = w; () },
 | 
			
		||||
							_ => { return usage(&argv[0]); },
 | 
			
		||||
						}
 | 
			
		||||
					},
 | 
			
		||||
					_ => { return usage(&argv[0]); }
 | 
			
		||||
	while let Some(opt) = argv.getopt("fw:") {
 | 
			
		||||
		match opt.opt() {
 | 
			
		||||
			Ok("f") => force = true,
 | 
			
		||||
			Ok("w") => {
 | 
			
		||||
				if let Some(arg) = opt.arg() {
 | 
			
		||||
					match arg.parse::<usize>() {
 | 
			
		||||
						Ok(w) if w % 2 == 0 => { wordsize = w; () },
 | 
			
		||||
						_ => { return usage(&argv[0]); },
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			_ => { return usage(&argv[0]); }
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user