From 320a70bc56c3e6527bad325e0756ba010ffe17fa Mon Sep 17 00:00:00 2001 From: emma Date: Mon, 1 Apr 2024 20:37:35 -0600 Subject: [PATCH] getopt.rs(3): initial commit --- COPYING.LESSER | 165 ++++++++++++++++ Makefile | 4 +- src/getopt-rs/error.rs | 95 --------- src/getopt-rs/errorkind.rs | 61 ------ src/getopt-rs/lib.rs | 72 ------- src/getopt-rs/opt.rs | 89 --------- src/getopt-rs/parser.rs | 382 ------------------------------------- src/getopt-rs/result.rs | 59 ------ src/getopt-rs/tests.rs | 228 ---------------------- src/getopt.rs | 115 +++++++++++ 10 files changed, 282 insertions(+), 988 deletions(-) create mode 100644 COPYING.LESSER delete mode 100644 src/getopt-rs/error.rs delete mode 100644 src/getopt-rs/errorkind.rs delete mode 100644 src/getopt-rs/lib.rs delete mode 100644 src/getopt-rs/opt.rs delete mode 100644 src/getopt-rs/parser.rs delete mode 100644 src/getopt-rs/result.rs delete mode 100644 src/getopt-rs/tests.rs create mode 100644 src/getopt.rs diff --git a/COPYING.LESSER b/COPYING.LESSER new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/COPYING.LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/Makefile b/Makefile index 68efd20..7f5c36f 100644 --- a/Makefile +++ b/Makefile @@ -57,9 +57,9 @@ test: 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/getopt.rs $(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \ - -o $@ src/getopt-rs/lib.rs + -o $@ src/getopt.rs build/o/libstrerror.rlib: build src/strerror.rs $(RUSTC) $(RUSTFLAGS) --crate-type=lib -o $@ \ diff --git a/src/getopt-rs/error.rs b/src/getopt-rs/error.rs deleted file mode 100644 index 322af02..0000000 --- a/src/getopt-rs/error.rs +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2023 Emma Tebibyte - * 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 - } -} diff --git a/src/getopt-rs/errorkind.rs b/src/getopt-rs/errorkind.rs deleted file mode 100644 index 5475d8e..0000000 --- a/src/getopt-rs/errorkind.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2023 Emma Tebibyte - * 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, -} diff --git a/src/getopt-rs/lib.rs b/src/getopt-rs/lib.rs deleted file mode 100644 index 62f0e0d..0000000 --- a/src/getopt-rs/lib.rs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2023 Emma Tebibyte - * 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; diff --git a/src/getopt-rs/opt.rs b/src/getopt-rs/opt.rs deleted file mode 100644 index 05b51e6..0000000 --- a/src/getopt-rs/opt.rs +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2023 Emma Tebibyte - * 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> { -/// use getopt::Opt; -/// -/// // args = ["program", "-abc", "foo"]; -/// # let args: Vec = 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); - -impl fmt::Display for Opt { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Opt({:?}, {:?})", self.0, self.1) - } -} diff --git a/src/getopt-rs/parser.rs b/src/getopt-rs/parser.rs deleted file mode 100644 index 6f06cc3..0000000 --- a/src/getopt-rs/parser.rs +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (c) 2023 Emma Tebibyte - * 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> { -/// use getopt::Opt; -/// -/// // args = ["program", "-abc", "foo"]; -/// # let args: Vec = 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> { -/// use getopt::Opt; -/// -/// // args = ["program", "-abc", "-d", "foo", "-e", "bar"]; -/// # let mut args: Vec = 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, - args: Vec>, - 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 = 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; - - /// 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 = 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 = 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 = 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 = 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> { - 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)))) - } - } - } -} diff --git a/src/getopt-rs/result.rs b/src/getopt-rs/result.rs deleted file mode 100644 index 015a402..0000000 --- a/src/getopt-rs/result.rs +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2023 Emma Tebibyte - * 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 = result::Result; diff --git a/src/getopt-rs/tests.rs b/src/getopt-rs/tests.rs deleted file mode 100644 index c53d517..0000000 --- a/src/getopt-rs/tests.rs +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2023 Emma Tebibyte - * 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 = $expect; - let args: Vec = vec![$($arg),+] - .into_iter() - .map(String::from) - .collect(); - let next: Option = $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 = 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 = 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 = $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 = 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 - } -} diff --git a/src/getopt.rs b/src/getopt.rs new file mode 100644 index 0000000..f7668c6 --- /dev/null +++ b/src/getopt.rs @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2024 Emma Tebibyte + * 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 }; + +pub struct Opt { + pub arg: Option, + pub ind: i32, + pub opt: i32, +} + +pub enum OptError { + MissingArg, + UnknownOpt, +} + +// Function signature +pub trait GetOpt { + fn getopt( + argv: Vec, + optstring: &str + ) -> Option>; +} + +impl GetOpt for Vec { + fn getopt( + argv: Vec, + optstring: &str + ) -> Option> { + unsafe { + let args = argv + .iter() + .cloned() + .map(CString::new) + .map(Result::unwrap) + .map(|x| x.as_ptr() as c_char) + .collect::>() + .as_ptr() as *const *mut c_char; + + let len = argv.len() as c_int; + let opts = CString::new(optstring).unwrap().into_raw(); + + let a = match getopt(len, args, opts) { + /* From getopt(3p): + * + * The getopt() function shall return the next option character + * specified on the command line. + * + * A (':') shall be returned if getopt() detects a + * missing argument and the first character of optstring was a + * (':'). + * + * A ('?') 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 (':'). + * + * Otherwise, getopt() shall return -1 when all command line + * options are parsed. */ + 58 => { // ASCII value for ':' + return Some(Err(OptError::MissingArg)); + }, + 63 => { // ASCII value for '?' + return Some(Err(OptError::UnknownOpt)); + }, + /* 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, + a => a.to_string(), + }; + + Some(Ok(Opt { arg: Some(a), ind: optind, opt: optopt })) + } + } +} + +/* 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; +}