Show manuals with command line arguments

Add --manual, --conf-manual command line arguments that display manpages
through a pager. If no pager is found, this currently fails. It should
print the manuals to stdout instead.

The manuals are read from src/manuals and are generated with mandoc
whenever changes to the manpage sources meli.1 and meli.conf.5 are made.
embed
Manos Pitsidianakis 2019-10-23 10:45:13 +03:00
parent 3a86a7ca16
commit 6a8f869e5b
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
5 changed files with 102 additions and 0 deletions

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ target/
**/*.rs.bk **/*.rs.bk
.gdb_history .gdb_history
*.log *.log
src/manuals

View File

@ -3,6 +3,7 @@ name = "meli"
version = "0.3.2" version = "0.3.2"
authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"] authors = ["Manos Pitsidianakis <el13635@mail.ntua.gr>"]
edition = "2018" edition = "2018"
build = "build.rs"
[[bin]] [[bin]]
name = "meli" name = "meli"

52
build.rs 100644
View File

@ -0,0 +1,52 @@
/*
* meli - bin.rs
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use std::fs::File;
use std::io::prelude::*;
use std::io::BufWriter;
use std::path::PathBuf;
use std::process::Command;
fn main() -> Result<(), std::io::Error> {
if let Err(e) = std::fs::create_dir("src/manuals") {
if e.kind() != std::io::ErrorKind::AlreadyExists {
Err(e)?;
}
}
let meli_1_metadata = std::fs::metadata("meli.1")?;
if let Ok(metadata) = std::fs::metadata("src/manuals/meli.txt") {
if metadata.modified()? < meli_1_metadata.modified()? {
let output = Command::new("mandoc").args(&["meli.1"]).output()?;
let man_path = PathBuf::from("src/manuals/meli.txt");
let file = File::create(&man_path)?;
BufWriter::new(file).write_all(&output.stdout)?;
}
}
let meli_conf_5_metadata = std::fs::metadata("meli.conf.5")?;
if let Ok(metadata) = std::fs::metadata("src/manuals/meli_conf.txt") {
if metadata.modified()? < meli_conf_5_metadata.modified()? {
let output = Command::new("mandoc").args(&["meli.conf.5"]).output()?;
let man_path = PathBuf::from("src/manuals/meli_conf.txt");
let file = File::create(&man_path)?;
BufWriter::new(file).write_all(&output.stdout)?;
}
}
Ok(())
}

6
meli.1
View File

@ -43,6 +43,10 @@ if given, or at
.Pa $XDG_CONFIG_HOME/meli/config .Pa $XDG_CONFIG_HOME/meli/config
.It Fl -config Ar path .It Fl -config Ar path
Start meli with given configuration file. Start meli with given configuration file.
.It Fl -manual
Show (this) manual for meli.
.It Fl -conf-manual
Show manual for meli configuration file.
.El .El
.Sh STARTING WITH meli .Sh STARTING WITH meli
When launched for the first time, meli will search for its configuration directory, When launched for the first time, meli will search for its configuration directory,
@ -280,6 +284,8 @@ catchall for general errors
Specifies the editor to use Specifies the editor to use
.It Ev MELI_CONFIG .It Ev MELI_CONFIG
Override the configuration file Override the configuration file
.It Ev PAGER
Pager to use for command line help output.
.El .El
.Sh FILES .Sh FILES
meli uses the following parts of the XDG standard: meli uses the following parts of the XDG standard:

View File

@ -81,6 +81,8 @@ struct CommandLineArguments {
config: Option<String>, config: Option<String>,
help: bool, help: bool,
version: bool, version: bool,
manual: bool,
conf_manual: bool,
} }
fn main() -> std::result::Result<(), std::io::Error> { fn main() -> std::result::Result<(), std::io::Error> {
@ -95,6 +97,8 @@ fn main() -> std::result::Result<(), std::io::Error> {
config: None, config: None,
help: false, help: false,
version: false, version: false,
manual: false,
conf_manual: false,
}; };
for i in std::env::args().skip(1) { for i in std::env::args().skip(1) {
@ -119,6 +123,12 @@ fn main() -> std::result::Result<(), std::io::Error> {
"--version" | "-v" => { "--version" | "-v" => {
args.version = true; args.version = true;
} }
"--manual" => {
args.manual = true;
}
"--conf-manual" => {
args.conf_manual = true;
}
e => match prev { e => match prev {
None => error_and_exit!("error: value without command {}", e), None => error_and_exit!("error: value without command {}", e),
Some(CreateConfig) if args.create_config.is_none() => { Some(CreateConfig) if args.create_config.is_none() => {
@ -144,6 +154,8 @@ fn main() -> std::result::Result<(), std::io::Error> {
println!("\t--version, -v\t\tprint version and exit"); println!("\t--version, -v\t\tprint version and exit");
println!("\t--create-config[ PATH]\tCreate a sample configuration file with available configuration options. If PATH is not specified, meli will try to create it in $XDG_CONFIG_HOME/meli/config"); println!("\t--create-config[ PATH]\tCreate a sample configuration file with available configuration options. If PATH is not specified, meli will try to create it in $XDG_CONFIG_HOME/meli/config");
println!("\t--config PATH, -c PATH\tUse specified configuration file"); println!("\t--config PATH, -c PATH\tUse specified configuration file");
println!("\t--manual\t\tshow manual for meli");
println!("\t--conf-manual\t\tshow manual for meli configuration file");
std::process::exit(0); std::process::exit(0);
} }
@ -152,6 +164,36 @@ fn main() -> std::result::Result<(), std::io::Error> {
std::process::exit(0); std::process::exit(0);
} }
if args.manual {
let man_contents = include_str!("manuals/meli.txt");
let pager = option_env!("PAGER").unwrap_or("less");
let (read_end, write_end) = nix::unistd::pipe()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
use std::os::unix::io::FromRawFd;
unsafe { std::fs::File::from_raw_fd(write_end) }.write_all(man_contents.as_bytes())?;
let mut handle = std::process::Command::new(pager)
.stdin(unsafe { std::process::Stdio::from_raw_fd(read_end) })
.stdout(std::process::Stdio::inherit())
.spawn()?;
handle.wait()?;
std::process::exit(0);
}
if args.conf_manual {
let man_contents = include_str!("manuals/meli_conf.txt");
let pager = option_env!("PAGER").unwrap_or("less");
let (read_end, write_end) = nix::unistd::pipe()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
use std::os::unix::io::FromRawFd;
unsafe { std::fs::File::from_raw_fd(write_end) }.write_all(man_contents.as_bytes())?;
let mut handle = std::process::Command::new(pager)
.stdin(unsafe { std::process::Stdio::from_raw_fd(read_end) })
.stdout(std::process::Stdio::inherit())
.spawn()?;
handle.wait()?;
std::process::exit(0);
}
match prev { match prev {
None => {} None => {}
Some(CreateConfig) if args.create_config.is_none() => args.create_config = Some("".into()), Some(CreateConfig) if args.create_config.is_none() => args.create_config = Some("".into()),