mirror of
https://github.com/fmang/opustags.git
synced 2025-01-15 12:43:17 +01:00
Make the top-level functions deal with exceptions
This commit is contained in:
parent
ea00b8fd80
commit
51c7f29c1a
69
src/cli.cc
69
src/cli.cc
@ -58,8 +58,9 @@ static struct option getopt_options[] = {
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
ot::status ot::parse_options(int argc, char** argv, ot::options& opt, FILE* comments_input)
|
||||
ot::options ot::parse_options(int argc, char** argv, FILE* comments_input)
|
||||
{
|
||||
options opt;
|
||||
static ot::encoding_converter to_utf8("", "UTF-8");
|
||||
std::string utf8;
|
||||
const char* equal;
|
||||
@ -67,7 +68,7 @@ ot::status ot::parse_options(int argc, char** argv, ot::options& opt, FILE* comm
|
||||
bool set_all = false;
|
||||
opt = {};
|
||||
if (argc == 1)
|
||||
return {st::bad_arguments, "No arguments specified. Use -h for help."};
|
||||
throw status {st::bad_arguments, "No arguments specified. Use -h for help."};
|
||||
int c;
|
||||
optind = 0;
|
||||
while ((c = getopt_long(argc, argv, ":ho:iyd:a:s:DSe", getopt_options, NULL)) != -1) {
|
||||
@ -77,7 +78,7 @@ ot::status ot::parse_options(int argc, char** argv, ot::options& opt, FILE* comm
|
||||
break;
|
||||
case 'o':
|
||||
if (opt.path_out)
|
||||
return {st::bad_arguments, "Cannot specify --output more than once."};
|
||||
throw status {st::bad_arguments, "Cannot specify --output more than once."};
|
||||
opt.path_out = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
@ -94,7 +95,7 @@ ot::status ot::parse_options(int argc, char** argv, ot::options& opt, FILE* comm
|
||||
case 's':
|
||||
equal = strchr(optarg, '=');
|
||||
if (equal == nullptr)
|
||||
return {st::bad_arguments, "Comment does not contain an equal sign: "s + optarg + "."};
|
||||
throw status {st::bad_arguments, "Comment does not contain an equal sign: "s + optarg + "."};
|
||||
if (c == 's')
|
||||
opt.to_delete.emplace_back(optarg, equal - optarg);
|
||||
opt.to_add.emplace_back(optarg);
|
||||
@ -113,15 +114,14 @@ ot::status ot::parse_options(int argc, char** argv, ot::options& opt, FILE* comm
|
||||
opt.raw = true;
|
||||
break;
|
||||
case ':':
|
||||
return {st::bad_arguments,
|
||||
"Missing value for option '"s + argv[optind - 1] + "'."};
|
||||
throw status {st::bad_arguments, "Missing value for option '"s + argv[optind - 1] + "'."};
|
||||
default:
|
||||
return {st::bad_arguments, "Unrecognized option '" +
|
||||
(optopt ? "-"s + static_cast<char>(optopt) : argv[optind - 1]) + "'."};
|
||||
throw status {st::bad_arguments, "Unrecognized option '" +
|
||||
(optopt ? "-"s + static_cast<char>(optopt) : argv[optind - 1]) + "'."};
|
||||
}
|
||||
}
|
||||
if (opt.print_help)
|
||||
return st::ok;
|
||||
return opt;
|
||||
|
||||
// All non-option arguments are input files.
|
||||
bool stdin_as_input = false;
|
||||
@ -136,42 +136,42 @@ ot::status ot::parse_options(int argc, char** argv, ot::options& opt, FILE* comm
|
||||
for (std::string& arg : *args) {
|
||||
rc = to_utf8(arg, utf8);
|
||||
if (rc != ot::st::ok)
|
||||
return {st::bad_arguments, "Could not encode argument into UTF-8: " + rc.message};
|
||||
throw status {st::bad_arguments, "Could not encode argument into UTF-8: " + rc.message};
|
||||
arg = std::move(utf8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opt.in_place && opt.path_out)
|
||||
return {st::bad_arguments, "Cannot combine --in-place and --output."};
|
||||
throw status {st::bad_arguments, "Cannot combine --in-place and --output."};
|
||||
|
||||
if (opt.in_place && stdin_as_input)
|
||||
return {st::bad_arguments, "Cannot modify standard input in place."};
|
||||
throw status {st::bad_arguments, "Cannot modify standard input in place."};
|
||||
|
||||
if ((!opt.in_place || opt.edit_interactively) && opt.paths_in.size() != 1)
|
||||
return {st::bad_arguments, "Exactly one input file must be specified."};
|
||||
throw status {st::bad_arguments, "Exactly one input file must be specified."};
|
||||
|
||||
if (set_all && stdin_as_input)
|
||||
return {st::bad_arguments, "Cannot use standard input as input file when --set-all is specified."};
|
||||
throw status {st::bad_arguments, "Cannot use standard input as input file when --set-all is specified."};
|
||||
|
||||
if (opt.edit_interactively && (stdin_as_input || opt.path_out == "-"))
|
||||
return {st::bad_arguments, "Cannot edit interactively when standard input or standard output are already used."};
|
||||
throw status {st::bad_arguments, "Cannot edit interactively when standard input or standard output are already used."};
|
||||
|
||||
if (opt.edit_interactively && !opt.path_out.has_value() && !opt.in_place)
|
||||
return {st::bad_arguments, "Cannot edit interactively when no output is specified."};
|
||||
throw status {st::bad_arguments, "Cannot edit interactively when no output is specified."};
|
||||
|
||||
if (opt.edit_interactively && (opt.delete_all || !opt.to_add.empty() || !opt.to_delete.empty()))
|
||||
return {st::bad_arguments, "Cannot mix --edit with -adDsS."};
|
||||
throw status {st::bad_arguments, "Cannot mix --edit with -adDsS."};
|
||||
|
||||
if (set_all) {
|
||||
// Read comments from stdin and prepend them to opt.to_add.
|
||||
std::list<std::string> comments;
|
||||
auto rc = read_comments(comments_input, comments, opt.raw);
|
||||
if (rc != st::ok)
|
||||
return rc;
|
||||
throw rc;
|
||||
opt.to_add.splice(opt.to_add.begin(), std::move(comments));
|
||||
}
|
||||
return st::ok;
|
||||
return opt;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -423,19 +423,23 @@ static ot::status process(ot::ogg_reader& reader, ot::ogg_writer* writer, const
|
||||
return ot::st::ok;
|
||||
}
|
||||
|
||||
static ot::status run_single(const ot::options& opt, const std::string& path_in, const std::optional<std::string>& path_out)
|
||||
static void run_single(const ot::options& opt, const std::string& path_in, const std::optional<std::string>& path_out)
|
||||
{
|
||||
ot::file input;
|
||||
if (path_in == "-")
|
||||
input = stdin;
|
||||
else if ((input = fopen(path_in.c_str(), "re")) == nullptr)
|
||||
return {ot::st::standard_error,
|
||||
"Could not open '" + path_in + "' for reading: " + strerror(errno)};
|
||||
throw ot::status {ot::st::standard_error,
|
||||
"Could not open '" + path_in + "' for reading: " + strerror(errno)};
|
||||
ot::ogg_reader reader(input.get());
|
||||
|
||||
/* Read-only mode. */
|
||||
if (!path_out)
|
||||
return process(reader, nullptr, opt);
|
||||
if (!path_out) {
|
||||
ot::status rc = process(reader, nullptr, opt);
|
||||
if (rc != ot::st::ok)
|
||||
throw rc;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read-write mode.
|
||||
*
|
||||
@ -488,7 +492,7 @@ static ot::status run_single(const ot::options& opt, const std::string& path_in,
|
||||
"Could not identify '" + path_in + "': " + strerror(errno)};
|
||||
}
|
||||
if (rc != ot::st::ok)
|
||||
return rc;
|
||||
throw rc;
|
||||
|
||||
ot::ogg_writer writer(output);
|
||||
writer.path = path_out;
|
||||
@ -496,24 +500,27 @@ static ot::status run_single(const ot::options& opt, const std::string& path_in,
|
||||
if (rc == ot::st::ok)
|
||||
rc = temporary_output.commit();
|
||||
|
||||
return rc;
|
||||
if (rc != ot::st::ok)
|
||||
throw rc;
|
||||
}
|
||||
|
||||
ot::status ot::run(const ot::options& opt)
|
||||
void ot::run(const ot::options& opt)
|
||||
{
|
||||
if (opt.print_help) {
|
||||
fputs(help_message, stdout);
|
||||
return st::ok;
|
||||
return;
|
||||
}
|
||||
|
||||
ot::status global_rc = st::ok;
|
||||
for (const auto& path_in : opt.paths_in) {
|
||||
ot::status rc = run_single(opt, path_in, opt.in_place ? path_in : opt.path_out);
|
||||
if (rc != st::ok) {
|
||||
try {
|
||||
run_single(opt, path_in, opt.in_place ? path_in : opt.path_out);
|
||||
} catch (const ot::status& rc) {
|
||||
global_rc = st::error;
|
||||
if (!rc.message.empty())
|
||||
fprintf(stderr, "%s: error: %s\n", path_in.c_str(), rc.message.c_str());
|
||||
}
|
||||
}
|
||||
return global_rc;
|
||||
if (global_rc != st::ok)
|
||||
throw global_rc;
|
||||
}
|
||||
|
@ -15,13 +15,14 @@
|
||||
* Does practically nothing but call the cli module.
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
setlocale(LC_ALL, "");
|
||||
ot::options opt;
|
||||
ot::status rc = ot::parse_options(argc, argv, opt, stdin);
|
||||
if (rc == ot::st::ok)
|
||||
rc = ot::run(opt);
|
||||
else if (!rc.message.empty())
|
||||
fprintf(stderr, "error: %s\n", rc.message.c_str());
|
||||
|
||||
return rc == ot::st::ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
try {
|
||||
setlocale(LC_ALL, "");
|
||||
ot::options opt = ot::parse_options(argc, argv, stdin);
|
||||
ot::run(opt);
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const ot::status& rc) {
|
||||
if (!rc.message.empty())
|
||||
fprintf(stderr, "error: %s\n", rc.message.c_str());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
@ -475,10 +475,8 @@ struct options {
|
||||
/**
|
||||
* Parse the command-line arguments. Does not perform I/O related validations, but checks the
|
||||
* consistency of its arguments. Comments are read if necessary from the given stream.
|
||||
*
|
||||
* On error, the state of the options structure is unspecified.
|
||||
*/
|
||||
status parse_options(int argc, char** argv, options& opt, FILE* comments);
|
||||
options parse_options(int argc, char** argv, FILE* comments);
|
||||
|
||||
/**
|
||||
* Print all the comments, separated by line breaks. Since a comment may contain line breaks, this
|
||||
@ -509,7 +507,7 @@ void delete_comments(std::list<std::string>& comments, const std::string& select
|
||||
* Main entry point to the opustags program, and pretty much the same as calling opustags from the
|
||||
* command-line.
|
||||
*/
|
||||
status run(const options& opt);
|
||||
void run(const options& opt);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
7
t/cli.cc
7
t/cli.cc
@ -54,7 +54,12 @@ static ot::status parse_options(const std::vector<const char*>& args, ot::option
|
||||
char* argv[argc];
|
||||
for (int i = 0; i < argc; ++i)
|
||||
argv[i] = strdup(args[i]);
|
||||
ot::status rc = ot::parse_options(argc, argv, opt, comments);
|
||||
ot::status rc = ot::st::ok;
|
||||
try {
|
||||
opt = ot::parse_options(argc, argv, comments);
|
||||
} catch (const ot::status& e) {
|
||||
rc = e;
|
||||
}
|
||||
for (int i = 0; i < argc; ++i)
|
||||
free(argv[i]);
|
||||
return rc;
|
||||
|
Loading…
x
Reference in New Issue
Block a user