forked from bonsai/harakit
		
	Merge branch 'main' into dj
This commit is contained in:
		
						commit
						69510d76af
					
				
							
								
								
									
										30
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								Makefile
									
									
									
									
									
								
							@ -50,9 +50,12 @@ install: dist
 | 
				
			|||||||
	cp -r $(DESTDIR)/* /
 | 
						cp -r $(DESTDIR)/* /
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: test
 | 
					.PHONY: test
 | 
				
			||||||
test: build
 | 
					test: build /tmp/getopt
 | 
				
			||||||
 | 
						/tmp/getopt
 | 
				
			||||||
	tests/posix-compat.sh
 | 
						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
 | 
					.PHONY: docs
 | 
				
			||||||
docs: docs/ build
 | 
					docs: docs/ build
 | 
				
			||||||
@ -65,21 +68,22 @@ docs: docs/ build
 | 
				
			|||||||
rustlibs: build/o/libsysexits.rlib build/o/libgetopt.rlib \
 | 
					rustlibs: build/o/libsysexits.rlib build/o/libgetopt.rlib \
 | 
				
			||||||
	build/o/libstrerror.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 \
 | 
						$(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 $@ \
 | 
						$(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/include/sysexits.h
 | 
				
			||||||
build/o/libsysexits.rlib: build $(SYSEXITS)sysexits.h
 | 
					 | 
				
			||||||
	printf '\043define EXIT_FAILURE 1\n' | cat - $(SYSEXITS)sysexits.h \
 | 
					 | 
				
			||||||
		> build/include/sysexits.h 
 | 
					 | 
				
			||||||
	bindgen --default-macro-constant-type signed --use-core --formatter=none \
 | 
						bindgen --default-macro-constant-type signed --use-core --formatter=none \
 | 
				
			||||||
		build/include/sysexits.h | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o $@ -
 | 
							build/include/sysexits.h | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o $@ -
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# bandage solution until bindgen(1) gets stdin support
 | 
				
			||||||
 | 
					build/include/sysexits.h: build $(SYSEXITS)sysexits.h
 | 
				
			||||||
 | 
						printf '\043define EXIT_FAILURE 1\n' | cat - $(SYSEXITS)sysexits.h > $@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: dj
 | 
					.PHONY: dj
 | 
				
			||||||
dj: build/bin/dj
 | 
					dj: build/bin/dj
 | 
				
			||||||
build/bin/dj: src/dj.c build
 | 
					build/bin/dj: src/dj.c build
 | 
				
			||||||
@ -137,10 +141,8 @@ build/bin/strcmp: src/strcmp.c build
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.PHONY: swab
 | 
					.PHONY: swab
 | 
				
			||||||
swab: build/bin/swab
 | 
					swab: build/bin/swab
 | 
				
			||||||
build/bin/swab: src/swab.rs build build/o/libsysexits.rlib
 | 
					build/bin/swab: src/swab.rs build rustlibs
 | 
				
			||||||
	$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
 | 
						$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/swab.rs
 | 
				
			||||||
		--extern sysexits=build/o/libsysexits.rlib \
 | 
					 | 
				
			||||||
		-o $@ src/swab.rs
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.PHONY: true
 | 
					.PHONY: true
 | 
				
			||||||
true: build/bin/true
 | 
					true: build/bin/true
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										32
									
								
								docs/dj.1
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								docs/dj.1
									
									
									
									
									
								
							@ -11,25 +11,17 @@ dj \(en disk jockey
 | 
				
			|||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dj
 | 
					dj
 | 
				
			||||||
.RB ( -Hn )
 | 
					.RB [ -Hn ]
 | 
				
			||||||
.RB ( -a
 | 
					.RB [ -a\ byte ]
 | 
				
			||||||
.RB [ byte ])
 | 
					.RB [ -c\ count ]
 | 
				
			||||||
.RB ( -c
 | 
					 | 
				
			||||||
.RB [ count ])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.RB ( -i
 | 
					.RB [ -i\ file ]
 | 
				
			||||||
[\fBinput file\fP])
 | 
					.RB [ -b\ block_size ]
 | 
				
			||||||
.RB ( -b
 | 
					.RB [ -s\ offset ]
 | 
				
			||||||
[\fBinput block size\fP])
 | 
					 | 
				
			||||||
.RB ( -s
 | 
					 | 
				
			||||||
[\fBinput offset\fP])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
.RB ( -o
 | 
					.RB [ -o\ file ]
 | 
				
			||||||
[\fBoutput file\fP])
 | 
					.RB [ -B\ block_size ]
 | 
				
			||||||
.RB ( -B
 | 
					.RB [ -S\ offset ]
 | 
				
			||||||
[\fBoutput block size\fP])
 | 
					 | 
				
			||||||
.RB ( -S
 | 
					 | 
				
			||||||
[\fBoutput offset\fP])
 | 
					 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -47,9 +39,9 @@ immediately subsequent to the specified byte.
 | 
				
			|||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH OPTIONS
 | 
					.SH OPTIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.IP \fB-i\fP
 | 
					.IP \fB-i\fP\ \fIfile\fP
 | 
				
			||||||
Takes a file path as an argument and opens it for use as an input.
 | 
					Takes a file path as an argument and opens it for use as an input.
 | 
				
			||||||
.IP \fB-b\fP
 | 
					.IP \fB-b\fP\ \fIblock_size\fP
 | 
				
			||||||
Takes a numeric argument as the size in bytes of the input buffer, the default
 | 
					Takes a numeric argument as the size in bytes of the input buffer, the default
 | 
				
			||||||
being 1024.
 | 
					being 1024.
 | 
				
			||||||
.IP \fB-s\fP
 | 
					.IP \fB-s\fP
 | 
				
			||||||
@ -58,7 +50,7 @@ commence; \(lqskips\(rq that number of bytes. If the standard input is used,
 | 
				
			|||||||
bytes read to this point are discarded.
 | 
					bytes read to this point are discarded.
 | 
				
			||||||
.IP \fB-o\fP
 | 
					.IP \fB-o\fP
 | 
				
			||||||
Takes a file path as an argument and opens it for use as an output.
 | 
					Takes a file path as an argument and opens it for use as an output.
 | 
				
			||||||
.IP \fB-B\fP
 | 
					.IP \fB-B\fP\ \fIblock_size\fP
 | 
				
			||||||
Does the same as
 | 
					Does the same as
 | 
				
			||||||
.B -b
 | 
					.B -b
 | 
				
			||||||
but for the output buffer.
 | 
					but for the output buffer.
 | 
				
			||||||
 | 
				
			|||||||
@ -22,7 +22,7 @@ Performs operations on specified fields in data read from the standard input.
 | 
				
			|||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH OPTIONS
 | 
					.SH OPTIONS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.IP \fB-d\fP
 | 
					.IP \fB-d\fP\ \fIdelimiter\fP
 | 
				
			||||||
Sets a delimiter by which the input data will be split into fields. The default
 | 
					Sets a delimiter by which the input data will be split into fields. The default
 | 
				
			||||||
is an ASCII record separator.
 | 
					is an ASCII record separator.
 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
 | 
				
			|||||||
@ -11,9 +11,7 @@ intcmp \(en compare integers
 | 
				
			|||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
intcmp
 | 
					intcmp
 | 
				
			||||||
.RB ( -egl )
 | 
					.RB [ -egl ]\ integer\ integer...
 | 
				
			||||||
.RB [ integer ]
 | 
					 | 
				
			||||||
.RB [ integer... ]
 | 
					 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
Compare integers to each other.
 | 
					Compare integers to each other.
 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								docs/mm.1
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								docs/mm.1
									
									
									
									
									
								
							@ -10,11 +10,9 @@ mm \(en middleman
 | 
				
			|||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mm
 | 
					mm
 | 
				
			||||||
.RB ( -aenu )
 | 
					.RB [ -aenu ]
 | 
				
			||||||
.RB ( -i
 | 
					.RB [ -i\ input ]
 | 
				
			||||||
.RB [ input ])
 | 
					.RB [ -o\ output ]
 | 
				
			||||||
.RB ( -o
 | 
					 | 
				
			||||||
.RB [ output ])
 | 
					 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -26,10 +24,10 @@ Catenate input files and write them to the start of each output file or stream.
 | 
				
			|||||||
Opens subsequent outputs for appending rather than updating.
 | 
					Opens subsequent outputs for appending rather than updating.
 | 
				
			||||||
.IP \fB-e\fP
 | 
					.IP \fB-e\fP
 | 
				
			||||||
Use the standard error as an output.
 | 
					Use the standard error as an output.
 | 
				
			||||||
.IP \fB-i\fP
 | 
					.IP \fB-i\fP\ \fIinput\fP
 | 
				
			||||||
Opens a path as an input. If one or more of the input files is \(lq-\(rq or if
 | 
					Opens a path as an input. If one or more of the input files is \(lq-\(rq or if
 | 
				
			||||||
no inputs are specified, the standard input shall be used.
 | 
					no inputs are specified, the standard input shall be used.
 | 
				
			||||||
.IP \fB-o\fP
 | 
					.IP \fB-o\fP\ \fIoutput\fP
 | 
				
			||||||
Opens a path as an output. If one or more of the output files is \(lq-\(rq or if
 | 
					Opens a path as an output. If one or more of the output files is \(lq-\(rq or if
 | 
				
			||||||
no outputs are specified, the standard output shall be used.
 | 
					no outputs are specified, the standard output shall be used.
 | 
				
			||||||
.IP \fB-u\fP
 | 
					.IP \fB-u\fP
 | 
				
			||||||
 | 
				
			|||||||
@ -11,7 +11,7 @@ npc \(en show non-printing characters
 | 
				
			|||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
npc
 | 
					npc
 | 
				
			||||||
.RB ( -et )
 | 
					.RB [ -et ]
 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -10,8 +10,8 @@ scrut \(en scrutinize file properties
 | 
				
			|||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
scrut
 | 
					scrut
 | 
				
			||||||
.RB ( -LSbcdefgkprsuwx )
 | 
					.RB [ -LSbcdefgkprsuwx ]
 | 
				
			||||||
.RB [ file... ]
 | 
					.B file...
 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,8 +11,7 @@ str \(en test string arguments
 | 
				
			|||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
str
 | 
					str
 | 
				
			||||||
.RB [ type ]
 | 
					.B type string...
 | 
				
			||||||
.RB [ string... ]
 | 
					 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -11,8 +11,7 @@ strcmp \(en compare strings
 | 
				
			|||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
strcmp
 | 
					strcmp
 | 
				
			||||||
.RM [ string ]
 | 
					.B string string...
 | 
				
			||||||
.RB [ strings... ]
 | 
					 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										16
									
								
								docs/swab.1
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								docs/swab.1
									
									
									
									
									
								
							@ -11,11 +11,8 @@ swab \(en swap bytes
 | 
				
			|||||||
.SH SYNOPSIS
 | 
					.SH SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
swab
 | 
					swab
 | 
				
			||||||
.RB ( -f )
 | 
					.RB [ -f ]
 | 
				
			||||||
.RB ( -w
 | 
					.RB [ -w\ word_size ]
 | 
				
			||||||
.R [
 | 
					 | 
				
			||||||
.B word size
 | 
					 | 
				
			||||||
.R ])
 | 
					 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH DESCRIPTION
 | 
					.SH DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -25,11 +22,10 @@ Swap the latter and former halves of a block of bytes.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
.IP \fB-f\fP
 | 
					.IP \fB-f\fP
 | 
				
			||||||
Ignore SIGINT signal.
 | 
					Ignore SIGINT signal.
 | 
				
			||||||
.IP \fB-w\fP
 | 
					.IP \fB-w\fP\ \fIword_size\fP
 | 
				
			||||||
Configures the word size; that is, the size in bytes of the block size
 | 
					Configures the word size; that is, the size in bytes of the block size on which
 | 
				
			||||||
on which to operate. The default word size is 2. The word size must be
 | 
					to operate. The default word size is 2. The word size must be cleanly divisible
 | 
				
			||||||
cleanly divisible by 2, otherwise the block of bytes being processed can\(cqt be
 | 
					by 2, otherwise the block of bytes being processed can\(cqt be halved.
 | 
				
			||||||
halved.
 | 
					 | 
				
			||||||
.\"
 | 
					.\"
 | 
				
			||||||
.SH EXAMPLES
 | 
					.SH EXAMPLES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										8
									
								
								src/dj.c
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								src/dj.c
									
									
									
									
									
								
							@ -249,10 +249,10 @@ parse(char *s){
 | 
				
			|||||||
static int
 | 
					static int
 | 
				
			||||||
usage(char *s){
 | 
					usage(char *s){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fprintf(stderr, "Usage: %s (-Hn) (-a [byte]) (-c [count])\n"
 | 
						fprintf(stderr, "Usage: %s [-Hn] [-a byte] [-c count]\n"
 | 
				
			||||||
		"\t(-i [input file]) (-b [input block size]) (-s [input offset])\n"
 | 
							"\t[-i file] [-b block_size] [-s offset]\n"
 | 
				
			||||||
		"\t(-o [output file]) (-B [output block size]) (-S [output offset])\n",
 | 
							"\t[-o file] [-B block_size] [-S offset]\n",
 | 
				
			||||||
		s);
 | 
							program_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return EX_USAGE;
 | 
						return EX_USAGE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										30
									
								
								src/fop.rs
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								src/fop.rs
									
									
									
									
									
								
							@ -26,27 +26,39 @@ extern crate getopt;
 | 
				
			|||||||
extern crate strerror;
 | 
					extern crate strerror;
 | 
				
			||||||
extern crate sysexits;
 | 
					extern crate sysexits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use getopt::{ Opt, Parser };
 | 
					use getopt::GetOpt;
 | 
				
			||||||
use strerror::StrError;
 | 
					use strerror::StrError;
 | 
				
			||||||
use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
 | 
					use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn main() {
 | 
					fn main() {
 | 
				
			||||||
	let argv = args().collect::<Vec<String>>();
 | 
						let argv = args().collect::<Vec<String>>();
 | 
				
			||||||
	let mut d = '\u{1E}'.to_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() {
 | 
						let usage = format!(
 | 
				
			||||||
		match opt {
 | 
							"Usage: {} [-d delimiter] index command [args...]",
 | 
				
			||||||
			Ok(Opt('d', Some(arg))) => d = arg,
 | 
							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 = index_arg as usize + 1;
 | 
				
			||||||
	let command_arg = arg_parser.index() + 1;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	argv.get(command_arg).unwrap_or_else(|| {
 | 
						argv.get(command_arg).unwrap_or_else(|| {
 | 
				
			||||||
		eprintln!("Usage: {} [-d delimiter] index command [args...]", argv[0]);
 | 
							eprintln!("{}", usage);
 | 
				
			||||||
		exit(EX_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
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -52,7 +52,7 @@ int main(int argc, char *argv[]){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if(optind + 2 /* ref cmp */ > argc){
 | 
						if(optind + 2 /* ref cmp */ > argc){
 | 
				
			||||||
usage:		fprintf(stderr,
 | 
					usage:		fprintf(stderr,
 | 
				
			||||||
			"Usage: %s (-eghl) [integer] [integer...]\n",
 | 
								"Usage: %s [-egl] integer integer...\n",
 | 
				
			||||||
			argv[0] == NULL ? program_name : argv[0]);
 | 
								argv[0] == NULL ? program_name : argv[0]);
 | 
				
			||||||
		return EX_USAGE;
 | 
							return EX_USAGE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										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);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										2
									
								
								src/mm.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								src/mm.c
									
									
									
									
									
								
							@ -110,7 +110,7 @@ oserr(char *s, char *r){
 | 
				
			|||||||
 * returns an exit status appropriate for a usage error. */
 | 
					 * returns an exit status appropriate for a usage error. */
 | 
				
			||||||
int usage(char *s){
 | 
					int usage(char *s){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fprintf(stderr, "Usage: %s (-aenu) (-i [input])... (-o [output])...\n", s);
 | 
						fprintf(stderr, "Usage: %s [-aenu] [-i input]... [-o output]...\n", s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return EX_USAGE;
 | 
						return EX_USAGE;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -39,7 +39,7 @@ int main(int argc, char *argv[]){
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(argc > optind){
 | 
						if(argc > optind){
 | 
				
			||||||
usage:		fprintf(stderr, "Usage: %s (-eht)\n", argv[0]);
 | 
					usage:		fprintf(stderr, "Usage: %s [-et]\n", argv[0]);
 | 
				
			||||||
		return EX_USAGE;
 | 
							return EX_USAGE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -66,7 +66,7 @@ int main(int argc, char *argv[]){
 | 
				
			|||||||
			if(ops[i] == 'e')
 | 
								if(ops[i] == 'e')
 | 
				
			||||||
				continue;
 | 
									continue;
 | 
				
			||||||
			else if(ops[i] == 'h'){
 | 
								else if(ops[i] == 'h'){
 | 
				
			||||||
usage:				fprintf(stderr, "Usage: %s (-%s) [file...]\n",
 | 
					usage:				fprintf(stderr, "Usage: %s [-%s] file...\n",
 | 
				
			||||||
					argv[0] == NULL
 | 
										argv[0] == NULL
 | 
				
			||||||
						? program_name
 | 
											? program_name
 | 
				
			||||||
						: argv[0],
 | 
											: argv[0],
 | 
				
			||||||
 | 
				
			|||||||
@ -56,7 +56,7 @@ int main(int argc, char *argv[]){
 | 
				
			|||||||
				goto pass;
 | 
									goto pass;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fprintf(stderr, "Usage: %s [type] [string...]\n",
 | 
						fprintf(stderr, "Usage: %s type string...\n",
 | 
				
			||||||
		argv[0] == NULL ? program_name : argv[0]);
 | 
							argv[0] == NULL ? program_name : argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return EX_USAGE;
 | 
						return EX_USAGE;
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@ int main(int argc, char *argv[]){
 | 
				
			|||||||
	int i;
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if(argc < 3){
 | 
						if(argc < 3){
 | 
				
			||||||
		fprintf(stderr, "Usage: %s [string] [string...]\n",
 | 
							fprintf(stderr, "Usage: %s string string...\n",
 | 
				
			||||||
			argv[0] == NULL ? program_name : argv[0]);
 | 
								argv[0] == NULL ? program_name : argv[0]);
 | 
				
			||||||
		return EX_USAGE;
 | 
							return EX_USAGE;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										29
									
								
								src/swab.rs
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/swab.rs
									
									
									
									
									
								
							@ -24,7 +24,7 @@ use std::{
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern crate getopt;
 | 
					extern crate getopt;
 | 
				
			||||||
use getopt::{ Opt, Parser };
 | 
					use getopt::GetOpt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern crate sysexits;
 | 
					extern crate sysexits;
 | 
				
			||||||
use sysexits::{ EX_OK, EX_OSERR, EX_USAGE };
 | 
					use sysexits::{ EX_OK, EX_OSERR, EX_USAGE };
 | 
				
			||||||
@ -35,7 +35,7 @@ fn oserr(s: &str, e: Error) -> ExitCode {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn usage(s: &str) -> ExitCode {
 | 
					fn usage(s: &str) -> ExitCode {
 | 
				
			||||||
	eprintln!("Usage: {} (-f) (-w [wordsize])", s);
 | 
						eprintln!("Usage: {} [-f] [-w word_size]", s);
 | 
				
			||||||
	ExitCode::from(EX_USAGE as u8)
 | 
						ExitCode::from(EX_USAGE as u8)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,24 +45,21 @@ fn main() -> ExitCode {
 | 
				
			|||||||
	let mut input = stdin();
 | 
						let mut input = stdin();
 | 
				
			||||||
	let mut output = stdout().lock();
 | 
						let mut output = stdout().lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	let mut opts = Parser::new(&argv, "fw:");
 | 
					 | 
				
			||||||
	let mut force = false;
 | 
						let mut force = false;
 | 
				
			||||||
	let mut wordsize: usize = 2;
 | 
						let mut wordsize: usize = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	loop {
 | 
						while let Some(opt) = argv.getopt("fw:") {
 | 
				
			||||||
		match opts.next() {
 | 
							match opt.opt() {
 | 
				
			||||||
			None => break,
 | 
								Ok("f") => force = true,
 | 
				
			||||||
			Some(opt) =>
 | 
								Ok("w") => {
 | 
				
			||||||
				match opt {
 | 
									if let Some(arg) = opt.arg() {
 | 
				
			||||||
					Ok(Opt('f', None)) => force = true,
 | 
										match arg.parse::<usize>() {
 | 
				
			||||||
					Ok(Opt('w', Some(arg))) => {
 | 
											Ok(w) if w % 2 == 0 => { wordsize = w; () },
 | 
				
			||||||
						match arg.parse::<usize>() {
 | 
											_ => { return usage(&argv[0]); },
 | 
				
			||||||
							Ok(w) if w % 2 == 0 => { wordsize = w; () },
 | 
										}
 | 
				
			||||||
							_ => { return usage(&argv[0]); },
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					_ => { return usage(&argv[0]); }
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								_ => { return usage(&argv[0]); }
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user