summaryrefslogtreecommitdiffstats
path: root/sq
diff options
context:
space:
mode:
Diffstat (limited to 'sq')
-rw-r--r--sq/Cargo.toml5
-rw-r--r--sq/build.rs13
-rw-r--r--sq/src/commands/autocrypt.rs4
-rw-r--r--sq/src/commands/key.rs10
-rw-r--r--sq/src/commands/keyring.rs10
-rw-r--r--sq/src/commands/net.rs10
-rw-r--r--sq/src/commands/revoke.rs6
-rw-r--r--sq/src/sq-usage.rs1154
-rw-r--r--sq/src/sq.rs38
-rw-r--r--sq/src/sq_cli.rs678
10 files changed, 936 insertions, 992 deletions
diff --git a/sq/Cargo.toml b/sq/Cargo.toml
index 2007616d..47f121ea 100644
--- a/sq/Cargo.toml
+++ b/sq/Cargo.toml
@@ -32,7 +32,7 @@ sequoia-autocrypt = { path = "../autocrypt", version = "0.24", default-features
sequoia-net = { path = "../net", version = "0.24", default-features = false }
anyhow = "1.0.18"
chrono = "0.4.10"
-clap = { version = "2.33", features = ["wrap_help"] }
+clap = { version = "3", features = ["derive", "wrap_help"] }
itertools = "0.10"
tempfile = "3.1"
term_size = "0.3"
@@ -40,7 +40,8 @@ tokio = { version = "1.13.1" }
rpassword = "5.0"
[build-dependencies]
-clap = "2.33"
+clap = "3"
+clap_complete = "3"
sequoia-openpgp = { path = "../openpgp", version = "1.0.0", default-features = false }
subplot-build = "0.4.0"
diff --git a/sq/build.rs b/sq/build.rs
index 71399d46..efc13f8d 100644
--- a/sq/build.rs
+++ b/sq/build.rs
@@ -2,7 +2,7 @@ use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::Path;
-use clap::Shell;
+use clap_complete::Shell;
mod sq_cli {
include!("src/sq_cli.rs");
@@ -18,7 +18,7 @@ fn main() {
.expect("failed to generate code with Subplot");
let mut sq = sq_cli::configure(
- clap::App::new("sq").set_term_width(80),
+ clap::Command::new("sq").term_width(80),
cfg!(feature = "autocrypt"),
);
let mut main = fs::File::create("src/sq-usage.rs").unwrap();
@@ -38,14 +38,15 @@ fn main() {
};
fs::create_dir_all(&outdir).unwrap();
let mut sq = sq_cli::build();
+
for shell in &[Shell::Bash, Shell::Fish, Shell::Zsh, Shell::PowerShell,
Shell::Elvish] {
- sq.gen_completions("sq", *shell, &outdir);
- }
+ clap_complete::generate_to(*shell, &mut sq, "sq", &outdir).unwrap();
+ };
}
fn dump_help(sink: &mut dyn io::Write,
- sq: &mut clap::App,
+ sq: &mut clap::Command,
cmd: Vec<String>,
heading: &str)
-> io::Result<()>
@@ -67,7 +68,7 @@ fn dump_help(sink: &mut dyn io::Write,
.chain(std::iter::once("--help"))
.collect::<Vec<_>>();
- let help = sq.get_matches_from_safe_borrow(&args)
+ let help = sq.try_get_matches_from_mut(&args)
.unwrap_err().to_string();
writeln!(sink, "//! ```text")?;
diff --git a/sq/src/commands/autocrypt.rs b/sq/src/commands/autocrypt.rs
index ac852958..8ca018bf 100644
--- a/sq/src/commands/autocrypt.rs
+++ b/sq/src/commands/autocrypt.rs
@@ -15,7 +15,7 @@ use crate::{
pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
match m.subcommand() {
- ("decode", Some(m)) => {
+ Some(("decode", m)) => {
let input = open_or_stdin(m.value_of("input"))?;
let mut output =
config.create_or_stdout_pgp(m.value_of("output"),
@@ -29,7 +29,7 @@ pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
}
output.finalize()?;
},
- ("encode-sender", Some(m)) => {
+ Some(("encode-sender", m)) => {
let input = open_or_stdin(m.value_of("input"))?;
let mut output =
config.create_or_stdout_safe(m.value_of("output"))?;
diff --git a/sq/src/commands/key.rs b/sq/src/commands/key.rs
index aa03f2e1..99e849d9 100644
--- a/sq/src/commands/key.rs
+++ b/sq/src/commands/key.rs
@@ -26,11 +26,11 @@ use crate::decrypt_key;
pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
match m.subcommand() {
- ("generate", Some(m)) => generate(config, m)?,
- ("password", Some(m)) => password(config, m)?,
- ("extract-cert", Some(m)) => extract_cert(config, m)?,
- ("adopt", Some(m)) => adopt(config, m)?,
- ("attest-certifications", Some(m)) =>
+ Some(("generate", m)) => generate(config, m)?,
+ Some(("password", m)) => password(config, m)?,
+ Some(("extract-cert", m)) => extract_cert(config, m)?,
+ Some(("adopt", m)) => adopt(config, m)?,
+ Some(("attest-certifications", m)) =>
attest_certifications(config, m)?,
_ => unreachable!(),
}
diff --git a/sq/src/commands/keyring.rs b/sq/src/commands/keyring.rs
index 0fc2f51a..994c5fab 100644
--- a/sq/src/commands/keyring.rs
+++ b/sq/src/commands/keyring.rs
@@ -33,7 +33,7 @@ use crate::{
pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
match m.subcommand() {
- ("filter", Some(m)) => {
+ Some(("filter", m)) => {
let any_uid_predicates =
m.is_present("userid")
|| m.is_present("name")
@@ -151,7 +151,7 @@ pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
to_certificate)?;
output.finalize()
},
- ("join", Some(m)) => {
+ Some(("join", m)) => {
// XXX: Armor type selection is a bit problematic. If any
// of the certificates contain a secret key, it would be
// better to use Kind::SecretKey here. However, this
@@ -164,7 +164,7 @@ pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
filter(m.values_of("input"), &mut output, Some, false)?;
output.finalize()
},
- ("merge", Some(m)) => {
+ Some(("merge", m)) => {
let mut output =
config.create_or_stdout_pgp(m.value_of("output"),
m.is_present("binary"),
@@ -172,11 +172,11 @@ pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
merge(m.values_of("input"), &mut output)?;
output.finalize()
},
- ("list", Some(m)) => {
+ Some(("list", m)) => {
let mut input = open_or_stdin(m.value_of("input"))?;
list(config, &mut input, m.is_present("all-userids"))
},
- ("split", Some(m)) => {
+ Some(("split", m)) => {
let mut input = open_or_stdin(m.value_of("input"))?;
let prefix =
// The prefix is either specified explicitly...
diff --git a/sq/src/commands/net.rs b/sq/src/commands/net.rs
index 44046ee8..dbd33227 100644
--- a/sq/src/commands/net.rs
+++ b/sq/src/commands/net.rs
@@ -53,7 +53,7 @@ pub fn dispatch_keyserver(config: Config, m: &clap::ArgMatches) -> Result<()> {
.build()?;
match m.subcommand() {
- ("get", Some(m)) => {
+ Some(("get", m)) => {
let query = m.value_of("query").unwrap();
let handle = query.parse::<KeyHandle>();
@@ -83,7 +83,7 @@ pub fn dispatch_keyserver(config: Config, m: &clap::ArgMatches) -> Result<()> {
or an email address: {:?}", query));
}
},
- ("send", Some(m)) => {
+ Some(("send", m)) => {
let mut input = open_or_stdin(m.value_of("input"))?;
let cert = Cert::from_reader(&mut input).
context("Malformed key")?;
@@ -106,7 +106,7 @@ pub fn dispatch_wkd(config: Config, m: &clap::ArgMatches) -> Result<()> {
.build()?;
match m.subcommand() {
- ("url", Some(m)) => {
+ Some(("url", m)) => {
let email_address = m.value_of("input").unwrap();
let wkd_url = wkd::Url::from(email_address)?;
// XXX: Add other subcomand to specify whether it should be
@@ -114,7 +114,7 @@ pub fn dispatch_wkd(config: Config, m: &clap::ArgMatches) -> Result<()> {
let url = wkd_url.to_url(None)?;
println!("{}", url);
},
- ("get", Some(m)) => {
+ Some(("get", m)) => {
// Check that the policy allows https.
network_policy.assert(net::Policy::Encrypted)?;
@@ -137,7 +137,7 @@ pub fn dispatch_wkd(config: Config, m: &clap::ArgMatches) -> Result<()> {
serialize_keyring(&mut output, &certs,
m.is_present("binary"))?;
},
- ("generate", Some(m)) => {
+ Some(("generate", m)) => {
let domain = m.value_of("domain").unwrap();
let skip = m.is_present("skip");
let f = open_or_stdin(m.value_of("input"))?;
diff --git a/sq/src/commands/revoke.rs b/sq/src/commands/revoke.rs
index b863f245..3e020ea0 100644
--- a/sq/src/commands/revoke.rs
+++ b/sq/src/commands/revoke.rs
@@ -47,8 +47,8 @@ impl Subcommand {
pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
let (subcommand, m) = match m.subcommand() {
- ("certificate", Some(m)) => (Subcommand::Certificate, m),
- ("subkey", Some(m)) => {
+ Some(("certificate", m)) => (Subcommand::Certificate, m),
+ Some(("subkey", m)) => {
let subkey = m.value_of("subkey").expect("required");
let kh: KeyHandle = subkey
.parse()
@@ -58,7 +58,7 @@ pub fn dispatch(config: Config, m: &clap::ArgMatches) -> Result<()> {
(Subcommand::Subkey(kh), m)
}
- ("userid", Some(m)) => {
+ Some(("userid", m)) => {
let userid = m.value_of("userid").expect("required");
(Subcommand::UserID(userid.into()), m)
diff --git a/sq/src/sq-usage.rs b/sq/src/sq-usage.rs
index c588c213..66ba9d38 100644
--- a/sq/src/sq-usage.rs
+++ b/sq/src/sq-usage.rs
@@ -18,42 +18,56 @@
//! to refer to OpenPGP keys that do contain secrets.
//!
//! USAGE:
-//! sq [FLAGS] [OPTIONS] <SUBCOMMAND>
+//! sq [OPTIONS] <SUBCOMMAND>
//!
-//! FLAGS:
+//! OPTIONS:
//! -f, --force
//! Overwrites existing files
//!
//! -h, --help
-//! Prints help information
-//!
-//! -V, --version
-//! Prints version information
+//! Print help information
//!
-//!
-//! OPTIONS:
-//! --known-notation <NOTATION>...
+//! --known-notation <NOTATION>
//! Adds NOTATION to the list of known notations. This is used when
//! validating signatures. Signatures that have unknown notations with
//! the critical bit set are considered invalid.
//!
+//! -V, --version
+//! Print version information
+//!
//! SUBCOMMANDS:
-//! encrypt Encrypts a message
-//! decrypt Decrypts a message
-//! sign Signs messages or data files
-//! verify Verifies signed messages or detached signatures
-//! key Manages keys
-//! keyring Manages collections of keys or certs
-//! certify Certifies a User ID for a Certificate
-//! autocrypt Communicates certificates using Autocrypt
-//! keyserver Interacts with keyservers
-//! wkd Interacts with Web Key Directories
-//! armor Converts binary to ASCII
-//! dearmor Converts ASCII to binary
-//! inspect Inspects data, like file(1)
-//! packet Low-level packet manipulation
-//! revoke Generates revocation certificates
-//! help Prints this message or the help of the given subcommand(s)
+//! encrypt
+//! Encrypts a message
+//! decrypt
+//! Decrypts a message
+//! sign
+//! Signs messages or data files
+//! verify
+//! Verifies signed messages or detached signatures
+//! key
+//! Manages keys
+//! keyring
+//! Manages collections of keys or certs
+//! certify
+//! Certifies a User ID for a Certificate
+//! autocrypt
+//! Communicates certificates using Autocrypt
+//! keyserver
+//! Interacts with keyservers
+//! wkd
+//! Interacts with Web Key Directories
+//! armor
+//! Converts binary to ASCII
+//! dearmor
+//! Converts ASCII to binary
+//! inspect
+//! Inspects data, like file(1)
+//! packet
+//! Low-level packet manipulation
+//! revoke
+//! Generates revocation certificates
+//! help
+//! Print this message or the help of the given subcommand(s)
//! ```
//!
//! ## Subcommand encrypt
@@ -67,52 +81,57 @@
//! The converse operation is "sq decrypt".
//!
//! USAGE:
-//! sq encrypt [FLAGS] [OPTIONS] [--] [FILE]
+//! sq encrypt [OPTIONS] [FILE]
+//!
+//! ARGS:
+//! <FILE>
+//! Reads from FILE or stdin if omitted
//!
-//! FLAGS:
+//! OPTIONS:
//! -B, --binary
//! Emits binary data
//!
-//! -h, --help
-//! Prints help information
+//! --compression <KIND>
+//! Selects compression scheme to use
+//!
+//! [default: pad]
+//! [possible values: none, pad, zip, zlib, bzip2]
//!
-//! -s, --symmetric
-//! Adds a password to encrypt with. The message can be decrypted with
-//! either one of the recipient's keys, or any password.
-//! --use-expired-subkey
-//! If a certificate has only expired encryption-capable subkeys, falls
-//! back to using the one that expired last
+//! -h, --help
+//! Print help information
//!
-//! OPTIONS:
-//! --compression <KIND>
-//! Selects compression scheme to use [default: pad] [possible values:
-//! none, pad, zip, zlib, bzip2]
//! --mode <MODE>
//! Selects what kind of keys are considered for encryption. Transport
//! select subkeys marked as suitable for transport encryption, rest
//! selects those for encrypting data at rest, and all selects all
-//! encryption-capable subkeys. [default: all] [possible values:
-//! transport, rest, all]
+//! encryption-capable subkeys.
+//!
+//! [default: all]
+//! [possible values: transport, rest, all]
+//!
//! -o, --output <FILE>
//! Writes to FILE or stdout if omitted
//!
//! --private-key-store <KEY_STORE>
//! Provides parameters for private key store
//!
-//! --recipient-cert <CERT-RING>...
+//! --recipient-cert <CERT-RING>
//! Encrypts for all recipients in CERT-RING
//!
-//! --signer-key <KEY>...
+//! -s, --symmetric
+//! Adds a password to encrypt with. The message can be decrypted with
+//! either one of the recipient's keys, or any password.
+//!
+//! --signer-key <KEY>
//! Signs the message with KEY
//!
//! -t, --time <TIME>
//! Chooses keys valid at the specified time and sets the signature's
//! creation time
//!
-//! ARGS:
-//! <FILE>
-//! Reads from FILE or stdin if omitted
-//!
+//! --use-expired-subkey
+//! If a certificate has only expired encryption-capable subkeys, falls
+//! back to using the one that expired last
//!
//! EXAMPLES:
//!
@@ -150,9 +169,13 @@
//! The converse operation is "sq encrypt".
//!
//! USAGE:
-//! sq decrypt [FLAGS] [OPTIONS] [--] [FILE]
+//! sq decrypt [OPTIONS] [FILE]
+//!
+//! ARGS:
+//! <FILE>
+//! Reads from FILE or stdin if omitted
//!
-//! FLAGS:
+//! OPTIONS:
//! --dump
//! Prints a packet dump to stderr
//!
@@ -160,34 +183,27 @@
//! Prints the session key to stderr
//!
//! -h, --help
-//! Prints help information
-//!
-//! -x, --hex
-//! Prints a hexdump (implies --dump)
+//! Print help information
//!
+//! -n, --signatures <N>
+//! Sets the threshold of valid signatures to N. The message will only
+//! be considered verified if this threshold is reached. [default: 1 if
+//! at least one signer cert file is given, 0 otherwise]
//!
-//! OPTIONS:
//! -o, --output <FILE>
//! Writes to FILE or stdout if omitted
//!
//! --private-key-store <KEY_STORE>
//! Provides parameters for private key store
//!
-//! --recipient-key <KEY>...
+//! --recipient-key <KEY>
//! Decrypts with KEY
//!
-//! --signer-cert <CERT>...
+//! --signer-cert <CERT>
//! Verifies signatures with CERT
//!
-//! -n, --signatures <N>
-//! Sets the threshold of valid signatures to N. The message will only
-//! be considered verified if this threshold is reached. [default: 1 if
-//! at least one signer cert file is given, 0 otherwise]
-//!
-//! ARGS:
-//! <FILE>
-//! Reads from FILE or stdin if omitted
-//!
+//! -x, --hex
+//! Prints a hexdump (implies --dump)
//!
//! EXAMPLES:
//!
@@ -212,9 +228,13 @@
//! The converse operation is "sq verify".
//!
//! USAGE:
-//! sq sign [FLAGS] [OPTIONS] [--] [FILE]
+//! sq sign [OPTIONS] [--] [FILE]
//!
-//! FLAGS:
+//! ARGS:
+//! <FILE>
+//! Reads from FILE or stdin if omitted
+//!
+//! OPTIONS:
//! -a, --append
//! Appends a signature to existing signature
//!
@@ -228,16 +248,14 @@
//! Creates a detached signature
//!
//! -h, --help
-//! Prints help information
-//!
-//! -n, --notarize
-//! Signs a message and all existing signatures
+//! Print help information
//!
-//!
-//! OPTIONS:
//! --merge <SIGNED-MESSAGE>
//! Merges signatures from the input and SIGNED-MESSAGE
//!
+//! -n, --notarize
+//! Signs a message and all existing signatures
+//!
//! --notation <NAME> <VALUE>
//! Adds a notation to the certification. A user-defined notation's
//! name must be of the form "name@a.domain.you.control.org". If the
@@ -245,24 +263,20 @@
//! being critical. If a consumer of a signature doesn't understand a
//! critical notation, then it will ignore the signature. The notation
//! is marked as being human readable.
+//!
//! -o, --output <FILE>
//! Writes to FILE or stdout if omitted
//!
//! --private-key-store <KEY_STORE>
//! Provides parameters for private key store
//!
-//! --signer-key <KEY>...
+//! --signer-key <KEY>
//! Signs using KEY
//!
//! -t, --time <TIME>
//! Chooses keys valid at the specified time and sets the signature's
//! creation time
//!
-//! ARGS:
-//! <FILE>
-//! Reads from FILE or stdin if omitted
-//!
-//!
//! EXAMPLES:
//!
//! # Create a signed message
@@ -294,32 +308,30 @@
//! The converse operation is "sq sign".
//!
//! USAGE:
-//! sq verify [OPTIONS] [--] [FILE]
-//!
-//! FLAGS:
-//! -h, --help
-//! Prints help information
+//! sq verify [OPTIONS] [FILE]
//!
+//! ARGS:
+//! <FILE>
+//! Reads from FILE or stdin if omitted
//!
//! OPTIONS:
//! --detached <SIG>
//! Verifies a detached signature
//!
-//! -o, --output <FILE>
-//! Writes to FILE or stdout if omitted
-//!
-//! --signer-cert <CERT>...
-//! Verifies signatures with CERT
+//! -h, --help
+//! Print help information
//!
//! -n, --signatures <N>
//! Sets the threshold of valid signatures to N. If this threshold is
-//! not reached, the message will not be considered verified. [default:
-//! 1]
+//! not reached, the message will not be considered verified.
+//!
+//! [default: 1]
//!
-//! ARGS:
-//! <FILE>
-//! Reads from FILE or stdin if omitted
+//! -o, --output <FILE>
+//! Writes to FILE or stdout if omitted
//!
+//! --signer-cert <CERT>
+//! Verifies signatures with CERT
//!
//! EXAMPLES:
//!
@@ -351,19 +363,23 @@
//! USAGE:
//! sq key <SUBCOMMAND>
//!
-//! FLAGS:
+//! OPTIONS:
//! -h, --help
-//! Prints help information
-//!
+//! Print help information
//!
//! SUBCOMMANDS:
-//! generate Generates a new key
-//! password Changes password protecting secrets
-//! extract-cert Converts a key to a cert
-//! attest-certifications Attests to third-party certifications
-//! adopt Binds keys from one certificate to another
+//! generate
+//! Generates a new key
+//! password
+//! Changes password protecting secrets
+//! extract-cert
+//! Converts a key to a cert
+//! attest-certifications
+//! Attests to third-party certifications
+//! adopt
+//! Binds keys from one certificate to another
//! help
-//! Prints this message or the help of the given subcommand(s)
+//! Print this message or the help of the given subcommand(s)
//! ```
//!
//! ### Subcommand key generate
@@ -385,12 +401,25 @@
//! uploading it to a keyserver.
//!
//! USAGE:
-//! sq key generate [FLAGS] [OPTIONS] --export <OUTFILE>
+//! sq key generate [OPTIONS] --export <OUTFILE>
+//!
+//! OPTIONS:
+//! -c, --cipher-suite <CIPHER-SUITE>
+//! Selects the cryptographic algorithms for the key
+//!
+//! [default: cv25519]
+//! [possible values: rsa3k, rsa4k, cv25519]
//!
-//! FLAGS:
//! --can-authenticate
//! Adds an authentication-capable subkey (default)
//!
+//! --can-encrypt <PURPOSE>
+//! Adds an encryption-capable subkey. Encryption-capable subkeys can be
+//! marked as suitable for transport encryption, storage encryption, or
+//! both. [default: universal]
+//!
+//! [possible values: transport, storage, universal]
+//!
//! --can-sign
//! Adds a signing-capable subkey (default)
//!
@@ -403,25 +432,6 @@
//! --cannot-sign
//! Adds no signing-capable subkey
//!
-//! -h, --help
-//! Prints help information
-//!
-//! -V, --version
-//! Prints version information
-//!
-//! --with-password
-//! Protects the key with a password
-//!
-//!
-//! OPTIONS:
-//! --can-encrypt <PURPOSE>
-//! Adds an encryption-capable subkey. Encryption-capable subkeys can be
-//! marked as suitable for transport encryption, storage encryption, or
-//! both. [default: universal] [possible values: transport, storage,
-//! universal]
-//! -c, --cipher-suite <CIPHER-SUITE>
-//! Selects the cryptographic algorithms for the key [default: cv25519]
-//! [possible values: rsa3k, rsa4k, cv25519]
//! --creation-time <CREATION_TIME>
//! Sets the key's creation time to TIME. TIME is interpreted as an ISO
//! 8601
@@ -437,21 +447,30 @@
//!
//! $ sq key generate --creation-time 20110609T1938+0200 --export
//! noam.pgp
+//!
+//! -e, --export <OUTFILE>
+//! Writes the key to OUTFILE
+//!
//! --expires <TIME>
//! Makes the key expire at TIME (as ISO 8601). Use "never" to create
//! keys that do not expire.
+//!
//! --expires-in <DURATION>
//! Makes the key expire after DURATION. Either "N[ymwds]", for N years,
//! months, weeks, days, seconds, or "never".
-//! -e, --export <OUTFILE>
-//! Writes the key to OUTFILE
+//!
+//! -h, --help
+//! Print help information
//!
//! --rev-cert <FILE or ->
//! Writes the revocation certificate to FILE. mandatory if OUTFILE is
//! "-". [default: <OUTFILE>.rev]
-//! -u, --userid <EMAIL>...
+//!
+//! -u, --userid <EMAIL>
//! Adds a userid to the key
//!
+//! --with-password
+//! Protects the key with a password
//!
//! EXAMPLES:
//!
@@ -480,9 +499,13 @@
//! supply a zero-length password when prompted for the new password.
//!
//! USAGE:
-//! sq key password [FLAGS] [OPTIONS] [FILE]
+//! sq key password [OPTIONS] [FILE]
+//!
+//! ARGS:
+//! <FILE>
+//! Reads from FILE or stdin if omitted
//!
-//! FLAGS:
+//! OPTIONS:
//! -B, --binary
//! Emits binary data
//!
@@ -490,22 +513,11 @@
//! Emit a key with unencrypted secrets
//!
//! -h, --help
-//! Prints help information
-//!
-//! -V, --version
-//! Prints version information
-//!
+//! Print help information
//!
-//! OPTIONS:
//! -o, --output <FILE>
//! Writes to FILE or stdout if omitted
//!
-//!
-//! ARGS:
-//! <FILE>
-//! Reads from FILE or stdin if omitted
-//!
-//!
//! EXAMPLES: