move the help and some arguments checking in cli.cc

This commit is contained in:
Frédéric Mangano-Tarumi 2018-11-11 10:54:54 -05:00
parent bf386899ae
commit b5dc595855
3 changed files with 86 additions and 67 deletions

View File

@ -1,33 +1,66 @@
#include <config.h>
#include <opustags.h>
#include <getopt.h>
#include <memory>
static const char* version = PROJECT_NAME " version " PROJECT_VERSION "\n";
static const char* usage = 1 + R"raw(
Usage: opustags --help
opustags [OPTIONS] FILE
opustags OPTIONS FILE -o FILE
)raw";
static const char* help = 1 + R"raw(
Options:
-h, --help print this help
-o, --output write the modified tags to a file
-i, --in-place [SUFFIX] use a temporary file then replace the original file
-y, --overwrite overwrite the output file if it already exists
-d, --delete FIELD delete all the fields of a specified type
-a, --add FIELD=VALUE add a field
-s, --set FIELD=VALUE delete then add a field
-D, --delete-all delete all the fields!
-S, --set-all read the fields from stdin
)raw";
static struct option getopt_options[] = {
{"help", no_argument, 0, 'h'},
{"output", required_argument, 0, 'o'},
{"in-place", optional_argument, 0, 'i'},
{"overwrite", no_argument, 0, 'y'},
{"delete", required_argument, 0, 'd'},
{"add", required_argument, 0, 'a'},
{"set", required_argument, 0, 's'},
{"delete-all", no_argument, 0, 'D'},
{"set-all", no_argument, 0, 'S'},
{NULL, 0, 0, 0}
{"help", no_argument, 0, 'h'},
{"output", required_argument, 0, 'o'},
{"in-place", optional_argument, 0, 'i'},
{"overwrite", no_argument, 0, 'y'},
{"delete", required_argument, 0, 'd'},
{"add", required_argument, 0, 'a'},
{"set", required_argument, 0, 's'},
{"delete-all", no_argument, 0, 'D'},
{"set-all", no_argument, 0, 'S'},
{NULL, 0, 0, 0}
};
/**
* Parse the command-line arguments.
*
* Return EXIT_SUCCESS on success, meaning the parsing succeeded and the program execution may
* continue. On error, a relevant message is printed on stderr a non-zero exit code is returned.
* Process the command-line arguments.
*
* This function does not perform I/O related validations, but checks the consistency of its
* arguments.
*
* It returns one of :
* - #ot::status::ok, meaning the process may continue normally.
* - #ot::status::exit_now, meaning there is nothing to do and process should exit successfully.
* This happens when all the user wants is see the help or usage.
* - #ot::status::bad_arguments, meaning the arguments were invalid and the process should exit with
* an error.
*
* Help messages are written on standard output, and error messages on standard error.
*/
int ot::parse_options(int argc, char** argv, ot::options& opt)
ot::status ot::process_options(int argc, char** argv, ot::options& opt)
{
if (argc == 1) {
fputs(version, stdout);
fputs(usage, stdout);
return status::exit_now;
}
int c;
while ((c = getopt_long(argc, argv, "ho:i::yd:a:s:DS", getopt_options, NULL)) != -1) {
switch (c) {
@ -38,14 +71,14 @@ int ot::parse_options(int argc, char** argv, ot::options& opt)
opt.path_out = optarg;
if (opt.path_out.empty()) {
fputs("output's file path cannot be empty\n", stderr);
return EXIT_FAILURE;
return status::bad_arguments;
}
break;
case 'i':
opt.inplace = optarg == nullptr ? ".otmp" : optarg;
if (strcmp(opt.inplace, "") == 0) {
fputs("the in-place suffix cannot be empty\n", stderr);
return EXIT_FAILURE;
return status::bad_arguments;
}
break;
case 'y':
@ -54,7 +87,7 @@ int ot::parse_options(int argc, char** argv, ot::options& opt)
case 'd':
if (strchr(optarg, '=') != nullptr) {
fprintf(stderr, "invalid field name: '%s'\n", optarg);
return EXIT_FAILURE;
return status::bad_arguments;
}
opt.to_delete.emplace_back(optarg);
break;
@ -62,7 +95,7 @@ int ot::parse_options(int argc, char** argv, ot::options& opt)
case 's':
if (strchr(optarg, '=') == NULL) {
fprintf(stderr, "invalid comment: '%s'\n", optarg);
return EXIT_FAILURE;
return status::bad_arguments;
}
opt.to_add.emplace_back(optarg);
if (c == 's')
@ -76,10 +109,26 @@ int ot::parse_options(int argc, char** argv, ot::options& opt)
break;
default:
/* getopt printed a message */
return EXIT_FAILURE;
return status::bad_arguments;
}
}
return EXIT_SUCCESS;
if (opt.print_help) {
puts(version);
puts(usage);
puts(help);
puts("See the man page for extensive documentation.");
return status::exit_now;
}
if (optind != argc - 1) {
fputs("invalid arguments\n", stderr);
return status::bad_arguments;
}
opt.path_in = argv[optind];
if (opt.path_in.empty()) {
fputs("input's file path cannot be empty\n", stderr);
return status::bad_arguments;
}
return status::ok;
}
/**

View File

@ -10,25 +10,6 @@
#include <unistd.h>
#include <ogg/ogg.h>
const char *version = PROJECT_NAME " version " PROJECT_VERSION "\n";
const char *usage =
"Usage: opustags --help\n"
" opustags [OPTIONS] FILE\n"
" opustags OPTIONS FILE -o FILE\n";
const char *help =
"Options:\n"
" -h, --help print this help\n"
" -o, --output write the modified tags to a file\n"
" -i, --in-place [SUFFIX] use a temporary file then replace the original file\n"
" -y, --overwrite overwrite the output file if it already exists\n"
" -d, --delete FIELD delete all the fields of a specified type\n"
" -a, --add FIELD=VALUE add a field\n"
" -s, --set FIELD=VALUE delete then add a field\n"
" -D, --delete-all delete all the fields!\n"
" -S, --set-all read the fields from stdin\n";
/**
* Display the tags on stdout.
*
@ -46,36 +27,12 @@ static void print_tags(ot::opus_tags &tags)
}
}
int main(int argc, char **argv){
if(argc == 1){
fputs(version, stdout);
fputs(usage, stdout);
return EXIT_SUCCESS;
}
ot::options opt;
int exit_code = parse_options(argc, argv, opt);
if (exit_code != EXIT_SUCCESS)
return exit_code;
if (opt.print_help) {
puts(version);
puts(usage);
puts(help);
puts("See the man page for extensive documentation.");
return EXIT_SUCCESS;
}
if(optind != argc - 1){
fputs("invalid arguments\n", stderr);
return EXIT_FAILURE;
}
static int run(ot::options& opt)
{
if (opt.inplace != nullptr && !opt.path_out.empty()) {
fputs("cannot combine --in-place and --output\n", stderr);
return EXIT_FAILURE;
}
opt.path_in = argv[optind];
if (opt.path_in.empty()) {
fputs("input's file path cannot be empty\n", stderr);
return EXIT_FAILURE;
}
if (!opt.path_out.empty() && opt.path_in != "-" && opt.path_out != "-") {
char canon_in[PATH_MAX+1], canon_out[PATH_MAX+1];
if (realpath(opt.path_in.c_str(), canon_in) && realpath(opt.path_out.c_str(), canon_out)) {
@ -247,3 +204,14 @@ int main(int argc, char **argv){
}
return EXIT_SUCCESS;
}
int main(int argc, char** argv) {
ot::status rc;
ot::options opt;
rc = process_options(argc, argv, opt);
if (rc == ot::status::exit_now)
return EXIT_SUCCESS;
else if (rc != ot::status::ok)
return EXIT_FAILURE;
return run(opt);
}

View File

@ -24,6 +24,8 @@ namespace ot {
*/
enum class status {
ok,
exit_now,
bad_arguments,
int_overflow,
/** On standard error, errno will give more details. */
standard_error,
@ -204,7 +206,7 @@ struct options {
bool print_help = false;
};
int parse_options(int argc, char** argv, options& opt);
status process_options(int argc, char** argv, options& opt);
std::list<std::string> read_tags(FILE* file);
/** \} */