mirror of
https://github.com/fmang/opustags.git
synced 2025-01-15 20:53:16 +01:00
include the error message in ot::status
This commit is contained in:
parent
5445c5bc7c
commit
b9a0ece567
@ -20,7 +20,6 @@ add_library(
|
||||
libopustags
|
||||
OBJECT
|
||||
src/cli.cc
|
||||
src/error.cc
|
||||
src/ogg.cc
|
||||
src/opus.cc
|
||||
)
|
||||
|
94
src/cli.cc
94
src/cli.cc
@ -48,10 +48,10 @@ static struct option getopt_options[] = {
|
||||
* 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.
|
||||
* - #ot::st::ok, meaning the process may continue normally.
|
||||
* - #ot::st::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
|
||||
* - #ot::st::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.
|
||||
@ -61,7 +61,7 @@ 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;
|
||||
return st::exit_now;
|
||||
}
|
||||
int c;
|
||||
while ((c = getopt_long(argc, argv, "ho:i::yd:a:s:DS", getopt_options, NULL)) != -1) {
|
||||
@ -73,14 +73,14 @@ ot::status ot::process_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 status::bad_arguments;
|
||||
return st::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 status::bad_arguments;
|
||||
return st::bad_arguments;
|
||||
}
|
||||
break;
|
||||
case 'y':
|
||||
@ -89,7 +89,7 @@ ot::status ot::process_options(int argc, char** argv, ot::options& opt)
|
||||
case 'd':
|
||||
if (strchr(optarg, '=') != nullptr) {
|
||||
fprintf(stderr, "invalid field name: '%s'\n", optarg);
|
||||
return status::bad_arguments;
|
||||
return st::bad_arguments;
|
||||
}
|
||||
opt.to_delete.emplace_back(optarg);
|
||||
break;
|
||||
@ -97,7 +97,7 @@ ot::status ot::process_options(int argc, char** argv, ot::options& opt)
|
||||
case 's':
|
||||
if (strchr(optarg, '=') == NULL) {
|
||||
fprintf(stderr, "invalid comment: '%s'\n", optarg);
|
||||
return status::bad_arguments;
|
||||
return st::bad_arguments;
|
||||
}
|
||||
opt.to_add.emplace_back(optarg);
|
||||
if (c == 's')
|
||||
@ -111,7 +111,7 @@ ot::status ot::process_options(int argc, char** argv, ot::options& opt)
|
||||
break;
|
||||
default:
|
||||
/* getopt printed a message */
|
||||
return status::bad_arguments;
|
||||
return st::bad_arguments;
|
||||
}
|
||||
}
|
||||
if (opt.print_help) {
|
||||
@ -119,33 +119,33 @@ ot::status ot::process_options(int argc, char** argv, ot::options& opt)
|
||||
puts(usage);
|
||||
puts(help);
|
||||
puts("See the man page for extensive documentation.");
|
||||
return status::exit_now;
|
||||
return st::exit_now;
|
||||
}
|
||||
if (optind != argc - 1) {
|
||||
fputs("exactly one input file must be specified\n", stderr);
|
||||
return status::bad_arguments;
|
||||
return st::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 st::bad_arguments;
|
||||
}
|
||||
if (opt.inplace != nullptr) {
|
||||
if (!opt.path_out.empty()) {
|
||||
fputs("cannot combine --in-place and --output\n", stderr);
|
||||
return status::bad_arguments;
|
||||
return st::bad_arguments;
|
||||
}
|
||||
opt.path_out = opt.path_in + opt.inplace;
|
||||
}
|
||||
if (opt.path_in == "-" && opt.set_all) {
|
||||
fputs("can't open stdin for input when -S is specified\n", stderr);
|
||||
return status::bad_arguments;
|
||||
return st::bad_arguments;
|
||||
}
|
||||
if (opt.path_in == "-" && opt.inplace) {
|
||||
fputs("cannot modify stdin in-place\n", stderr);
|
||||
return status::bad_arguments;
|
||||
return st::bad_arguments;
|
||||
}
|
||||
return status::ok;
|
||||
return st::ok;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -211,8 +211,8 @@ std::list<std::string> ot::read_comments(FILE* input)
|
||||
static ot::status process_tags(const ogg_packet& packet, const ot::options& opt, ot::ogg_writer* writer)
|
||||
{
|
||||
ot::opus_tags tags;
|
||||
if(ot::parse_tags(packet, tags) != ot::status::ok)
|
||||
return ot::status::bad_comment_header;
|
||||
if(ot::parse_tags(packet, tags) != ot::st::ok)
|
||||
return ot::st::bad_comment_header;
|
||||
|
||||
if (opt.delete_all) {
|
||||
tags.comments.clear();
|
||||
@ -231,7 +231,7 @@ static ot::status process_tags(const ogg_packet& packet, const ot::options& opt,
|
||||
return writer->write_packet(packet);
|
||||
} else {
|
||||
ot::print_comments(tags.comments, stdout);
|
||||
return ot::status::ok;
|
||||
return ot::st::ok;
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,15 +241,15 @@ static ot::status process_tags(const ogg_packet& packet, const ot::options& opt,
|
||||
*/
|
||||
ot::status ot::process(ogg_reader& reader, ogg_writer* writer, const ot::options &opt)
|
||||
{
|
||||
const char *error = nullptr;
|
||||
std::string error;
|
||||
int packet_count = 0;
|
||||
while (error == nullptr) {
|
||||
while (error.empty()) {
|
||||
// Read the next page.
|
||||
ot::status rc = reader.read_page();
|
||||
if (rc == ot::status::end_of_stream) {
|
||||
if (rc == ot::st::end_of_stream) {
|
||||
break;
|
||||
} else if (rc != ot::status::ok) {
|
||||
if (rc == ot::status::standard_error)
|
||||
} else if (rc != ot::st::ok) {
|
||||
if (rc == ot::st::standard_error)
|
||||
error = strerror(errno);
|
||||
else
|
||||
error = "error reading the next ogg page";
|
||||
@ -257,29 +257,29 @@ ot::status ot::process(ogg_reader& reader, ogg_writer* writer, const ot::options
|
||||
}
|
||||
// Short-circuit when the relevant packets have been read.
|
||||
if (packet_count >= 2 && writer) {
|
||||
if (writer->write_page(reader.page) != ot::status::ok) {
|
||||
if (writer->write_page(reader.page) != ot::st::ok) {
|
||||
error = "error writing ogg page";
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (writer && writer->prepare_stream(ogg_page_serialno(&reader.page)) != ot::status::ok) {
|
||||
if (writer && writer->prepare_stream(ogg_page_serialno(&reader.page)) != ot::st::ok) {
|
||||
error = "ogg_stream_init: could not prepare the ogg stream";
|
||||
break;
|
||||
}
|
||||
// Read all the packets.
|
||||
while ((rc = reader.read_packet()) == ot::status::ok) {
|
||||
while ((rc = reader.read_packet()) == ot::st::ok) {
|
||||
packet_count++;
|
||||
if (packet_count == 1) { // Identification header
|
||||
rc = ot::validate_identification_header(reader.packet);
|
||||
if (rc != ot::status::ok) {
|
||||
error = ot::error_message(rc);
|
||||
if (rc != ot::st::ok) {
|
||||
error = "error reading the identification header: " + rc.message;
|
||||
break;
|
||||
}
|
||||
} else if (packet_count == 2) { // Comment header
|
||||
rc = process_tags(reader.packet, opt, writer);
|
||||
if (rc != ot::status::ok) {
|
||||
error = ot::error_message(rc);
|
||||
if (rc != ot::st::ok) {
|
||||
error = "error reading the comment header: " + rc.message;
|
||||
break;
|
||||
}
|
||||
if (!writer)
|
||||
@ -287,28 +287,28 @@ ot::status ot::process(ogg_reader& reader, ogg_writer* writer, const ot::options
|
||||
else
|
||||
continue; /* process_tags wrote the new packet */
|
||||
}
|
||||
if (writer && writer->write_packet(reader.packet) != ot::status::ok) {
|
||||
if (writer && writer->write_packet(reader.packet) != ot::st::ok) {
|
||||
error = "error feeding the packet to the ogg stream";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rc != ot::status::ok && rc != ot::status::end_of_page) {
|
||||
if (rc != ot::st::ok && rc != ot::st::end_of_page) {
|
||||
error = "error reading the ogg packets";
|
||||
break;
|
||||
}
|
||||
// Write the assembled page.
|
||||
if (writer && writer->flush_page() != ot::status::ok) {
|
||||
if (writer && writer->flush_page() != ot::st::ok) {
|
||||
error = "error flushing the ogg page";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!error && packet_count < 2)
|
||||
if (error.empty() && packet_count < 2)
|
||||
error = "opustags: invalid file";
|
||||
if (error != nullptr) {
|
||||
fprintf(stderr, "%s\n", error);
|
||||
return ot::status::exit_now;
|
||||
if (!error.empty()) {
|
||||
fprintf(stderr, "%s\n", error.c_str());
|
||||
return ot::st::exit_now;
|
||||
}
|
||||
return ot::status::ok;
|
||||
return ot::st::ok;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -336,7 +336,7 @@ ot::status ot::run(ot::options& opt)
|
||||
{
|
||||
if (!opt.path_out.empty() && same_file(opt.path_in, opt.path_out)) {
|
||||
fputs("error: the input and output files are the same\n", stderr);
|
||||
return ot::status::fatal_error;
|
||||
return ot::st::fatal_error;
|
||||
}
|
||||
|
||||
std::unique_ptr<FILE, decltype(&fclose)> input(nullptr, &fclose);
|
||||
@ -346,7 +346,7 @@ ot::status ot::run(ot::options& opt)
|
||||
FILE* input_file = fopen(opt.path_in.c_str(), "r");
|
||||
if (input_file == nullptr) {
|
||||
perror("fopen");
|
||||
return ot::status::fatal_error;
|
||||
return ot::st::fatal_error;
|
||||
}
|
||||
input.reset(input_file);
|
||||
}
|
||||
@ -357,12 +357,12 @@ ot::status ot::run(ot::options& opt)
|
||||
} else if (!opt.path_out.empty()) {
|
||||
if (!opt.overwrite && access(opt.path_out.c_str(), F_OK) == 0) {
|
||||
fprintf(stderr, "'%s' already exists (use -y to overwrite)\n", opt.path_out.c_str());
|
||||
return ot::status::fatal_error;
|
||||
return ot::st::fatal_error;
|
||||
}
|
||||
FILE* output_file = fopen(opt.path_out.c_str(), "w");
|
||||
if (output_file == nullptr) {
|
||||
perror("fopen");
|
||||
return ot::status::fatal_error;
|
||||
return ot::st::fatal_error;
|
||||
}
|
||||
output.reset(output_file);
|
||||
}
|
||||
@ -380,18 +380,18 @@ ot::status ot::run(ot::options& opt)
|
||||
input.reset();
|
||||
output.reset();
|
||||
|
||||
if (rc != ot::status::ok) {
|
||||
if (rc != ot::st::ok) {
|
||||
if (!opt.path_out.empty() && opt.path_out != "-")
|
||||
remove(opt.path_out.c_str());
|
||||
return ot::status::fatal_error;
|
||||
return ot::st::fatal_error;
|
||||
}
|
||||
|
||||
if (opt.inplace) {
|
||||
if (rename(opt.path_out.c_str(), opt.path_in.c_str()) == -1) {
|
||||
perror("rename");
|
||||
return ot::status::fatal_error;
|
||||
return ot::st::fatal_error;
|
||||
}
|
||||
}
|
||||
|
||||
return ot::status::ok;
|
||||
return ot::st::ok;
|
||||
}
|
||||
|
35
src/error.cc
35
src/error.cc
@ -1,35 +0,0 @@
|
||||
#include <opustags.h>
|
||||
|
||||
static const char* messages[] = {
|
||||
"OK",
|
||||
"Need to exit",
|
||||
"Fatal error",
|
||||
"Bad command-line arguments",
|
||||
"Integer overflow",
|
||||
nullptr, /* standard error: call stderror */
|
||||
"End of stream",
|
||||
"End of page",
|
||||
"Stream is not ready",
|
||||
"libogg error",
|
||||
"Invalid identification header, not an Opus stream",
|
||||
"Invalid comment header",
|
||||
"Bad magic number",
|
||||
"Overflowing magic number",
|
||||
"Overflowing vendor length",
|
||||
"Overflowing vendor data",
|
||||
"Overflowing comment count",
|
||||
"Overflowing comment length",
|
||||
"Overflowing comment data",
|
||||
};
|
||||
|
||||
static_assert(sizeof(messages) / sizeof(*messages) == static_cast<size_t>(ot::status::sentinel));
|
||||
|
||||
const char* ot::error_message(ot::status code)
|
||||
{
|
||||
if (code == ot::status::standard_error)
|
||||
return strerror(errno);
|
||||
if (code >= ot::status::sentinel)
|
||||
return nullptr;
|
||||
auto index = static_cast<size_t>(code);
|
||||
return messages[index];
|
||||
}
|
44
src/ogg.cc
44
src/ogg.cc
@ -24,44 +24,44 @@ ot::status ot::ogg_reader::read_page()
|
||||
{
|
||||
while (ogg_sync_pageout(&sync, &page) != 1) {
|
||||
if (feof(file))
|
||||
return status::end_of_stream;
|
||||
return st::end_of_stream;
|
||||
char* buf = ogg_sync_buffer(&sync, 65536);
|
||||
if (buf == nullptr)
|
||||
return status::libogg_error;
|
||||
return st::libogg_error;
|
||||
size_t len = fread(buf, 1, 65536, file);
|
||||
if (ferror(file))
|
||||
return status::standard_error;
|
||||
return st::standard_error;
|
||||
if (ogg_sync_wrote(&sync, len) != 0)
|
||||
return status::libogg_error;
|
||||
return st::libogg_error;
|
||||
if (ogg_sync_check(&sync) != 0)
|
||||
return status::libogg_error;
|
||||
return st::libogg_error;
|
||||
}
|
||||
/* at this point, we've got a good page */
|
||||
if (!stream_ready) {
|
||||
if (ogg_stream_init(&stream, ogg_page_serialno(&page)) != 0)
|
||||
return status::libogg_error;
|
||||
return st::libogg_error;
|
||||
stream_ready = true;
|
||||
}
|
||||
stream_in_sync = false;
|
||||
return status::ok;
|
||||
return st::ok;
|
||||
}
|
||||
|
||||
ot::status ot::ogg_reader::read_packet()
|
||||
{
|
||||
if (!stream_ready)
|
||||
return status::stream_not_ready;
|
||||
return st::stream_not_ready;
|
||||
if (!stream_in_sync) {
|
||||
if (ogg_stream_pagein(&stream, &page) != 0)
|
||||
return status::libogg_error;
|
||||
return st::libogg_error;
|
||||
stream_in_sync = true;
|
||||
}
|
||||
int rc = ogg_stream_packetout(&stream, &packet);
|
||||
if (rc == 1)
|
||||
return status::ok;
|
||||
return st::ok;
|
||||
else if (rc == 0 && ogg_stream_check(&stream) == 0)
|
||||
return status::end_of_page;
|
||||
return st::end_of_page;
|
||||
else
|
||||
return status::libogg_error;
|
||||
return st::libogg_error;
|
||||
}
|
||||
|
||||
ot::ogg_writer::ogg_writer(FILE* output)
|
||||
@ -79,31 +79,31 @@ ot::ogg_writer::~ogg_writer()
|
||||
ot::status ot::ogg_writer::write_page(const ogg_page& page)
|
||||
{
|
||||
if (page.header_len < 0 || page.body_len < 0)
|
||||
return status::int_overflow;
|
||||
return st::int_overflow;
|
||||
auto header_len = static_cast<size_t>(page.header_len);
|
||||
auto body_len = static_cast<size_t>(page.body_len);
|
||||
if (fwrite(page.header, 1, header_len, file) < header_len)
|
||||
return status::standard_error;
|
||||
return st::standard_error;
|
||||
if (fwrite(page.body, 1, body_len, file) < body_len)
|
||||
return status::standard_error;
|
||||
return status::ok;
|
||||
return st::standard_error;
|
||||
return st::ok;
|
||||
}
|
||||
|
||||
ot::status ot::ogg_writer::prepare_stream(long serialno)
|
||||
{
|
||||
if (stream.serialno != serialno) {
|
||||
if (ogg_stream_reset_serialno(&stream, serialno) != 0)
|
||||
return status::libogg_error;
|
||||
return st::libogg_error;
|
||||
}
|
||||
return status::ok;
|
||||
return st::ok;
|
||||
}
|
||||
|
||||
ot::status ot::ogg_writer::write_packet(const ogg_packet& packet)
|
||||
{
|
||||
if (ogg_stream_packetin(&stream, const_cast<ogg_packet*>(&packet)) != 0)
|
||||
return status::libogg_error;
|
||||
return st::libogg_error;
|
||||
else
|
||||
return status::ok;
|
||||
return st::ok;
|
||||
}
|
||||
|
||||
ot::status ot::ogg_writer::flush_page()
|
||||
@ -112,6 +112,6 @@ ot::status ot::ogg_writer::flush_page()
|
||||
if (ogg_stream_flush(&stream, &page) != 0)
|
||||
return write_page(page);
|
||||
if (ogg_stream_check(&stream) != 0)
|
||||
return status::libogg_error;
|
||||
return status::ok; /* nothing was done */
|
||||
return st::libogg_error;
|
||||
return st::ok; /* nothing was done */
|
||||
}
|
||||
|
24
src/opus.cc
24
src/opus.cc
@ -40,10 +40,10 @@
|
||||
ot::status ot::validate_identification_header(const ogg_packet& packet)
|
||||
{
|
||||
if (packet.bytes < 8)
|
||||
return ot::status::bad_identification_header;
|
||||
return ot::st::bad_identification_header;
|
||||
if (memcmp(packet.packet, "OpusHead", 8) != 0)
|
||||
return ot::status::bad_identification_header;
|
||||
return ot::status::ok;
|
||||
return ot::st::bad_identification_header;
|
||||
return ot::st::ok;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +52,7 @@ ot::status ot::validate_identification_header(const ogg_packet& packet)
|
||||
ot::status ot::parse_tags(const ogg_packet& packet, opus_tags& tags)
|
||||
{
|
||||
if (packet.bytes < 0)
|
||||
return status::int_overflow;
|
||||
return st::int_overflow;
|
||||
size_t size = static_cast<size_t>(packet.bytes);
|
||||
const char* data = reinterpret_cast<char*>(packet.packet);
|
||||
size_t pos = 0;
|
||||
@ -60,33 +60,33 @@ ot::status ot::parse_tags(const ogg_packet& packet, opus_tags& tags)
|
||||
|
||||
// Magic number
|
||||
if (8 > size)
|
||||
return status::overflowing_magic_number;
|
||||
return st::overflowing_magic_number;
|
||||
if (memcmp(data, "OpusTags", 8) != 0)
|
||||
return status::bad_magic_number;
|
||||
return st::bad_magic_number;
|
||||
|
||||
// Vendor
|
||||
pos = 8;
|
||||
if (pos + 4 > size)
|
||||
return status::overflowing_vendor_length;
|
||||
return st::overflowing_vendor_length;
|
||||
size_t vendor_length = le32toh(*((uint32_t*) (data + pos)));
|
||||
if (pos + 4 + vendor_length > size)
|
||||
return status::overflowing_vendor_data;
|
||||
return st::overflowing_vendor_data;
|
||||
my_tags.vendor = std::string(data + pos + 4, vendor_length);
|
||||
pos += 4 + my_tags.vendor.size();
|
||||
|
||||
// Comment count
|
||||
if (pos + 4 > size)
|
||||
return status::overflowing_comment_count;
|
||||
return st::overflowing_comment_count;
|
||||
uint32_t count = le32toh(*((uint32_t*) (data + pos)));
|
||||
pos += 4;
|
||||
|
||||
// Comments' data
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (pos + 4 > size)
|
||||
return status::overflowing_comment_length;
|
||||
return st::overflowing_comment_length;
|
||||
uint32_t comment_length = le32toh(*((uint32_t*) (data + pos)));
|
||||
if (pos + 4 + comment_length > size)
|
||||
return status::overflowing_comment_data;
|
||||
return st::overflowing_comment_data;
|
||||
const char *comment_value = data + pos + 4;
|
||||
my_tags.comments.emplace_back(comment_value, comment_length);
|
||||
pos += 4 + comment_length;
|
||||
@ -96,7 +96,7 @@ ot::status ot::parse_tags(const ogg_packet& packet, opus_tags& tags)
|
||||
my_tags.extra_data = std::string(data + pos, size - pos);
|
||||
|
||||
tags = std::move(my_tags);
|
||||
return status::ok;
|
||||
return st::ok;
|
||||
}
|
||||
|
||||
ot::dynamic_ogg_packet ot::render_tags(const opus_tags& tags)
|
||||
|
@ -9,10 +9,10 @@ int main(int argc, char** argv) {
|
||||
ot::status rc;
|
||||
ot::options opt;
|
||||
rc = process_options(argc, argv, opt);
|
||||
if (rc == ot::status::exit_now)
|
||||
if (rc == ot::st::exit_now)
|
||||
return EXIT_SUCCESS;
|
||||
else if (rc != ot::status::ok)
|
||||
else if (rc != ot::st::ok)
|
||||
return EXIT_FAILURE;
|
||||
rc = run(opt);
|
||||
return rc == ot::status::ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
return rc == ot::st::ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
@ -16,31 +16,25 @@
|
||||
namespace ot {
|
||||
|
||||
/**
|
||||
* Possible error status, ranging from #ok (0) to #sentinel.
|
||||
*
|
||||
* Use #error_message to get an error message from a status code.
|
||||
* Possible return status.
|
||||
*
|
||||
* The overflowing error family means that the end of packet was reached when attempting to read the
|
||||
* overflowing value. For example, overflowing_comment_count means that after reading the vendor
|
||||
* string, less than 4 bytes were left in the packet.
|
||||
*
|
||||
* When adding new status codes, make sure you update #error_message.
|
||||
*/
|
||||
enum class status {
|
||||
ok = 0,
|
||||
exit_now,
|
||||
fatal_error,
|
||||
bad_arguments,
|
||||
enum class st {
|
||||
/* Generic */
|
||||
ok,
|
||||
int_overflow,
|
||||
/** On standard error, errno will give more details. */
|
||||
standard_error,
|
||||
/* Ogg */
|
||||
end_of_stream,
|
||||
end_of_page,
|
||||
stream_not_ready,
|
||||
libogg_error,
|
||||
/* Opus */
|
||||
bad_identification_header,
|
||||
bad_comment_header,
|
||||
/* OpusTags parsing errors */
|
||||
bad_magic_number,
|
||||
overflowing_magic_number,
|
||||
overflowing_vendor_length,
|
||||
@ -48,22 +42,23 @@ enum class status {
|
||||
overflowing_comment_count,
|
||||
overflowing_comment_length,
|
||||
overflowing_comment_data,
|
||||
/**
|
||||
* Last error code, for integrity checking.
|
||||
* Do not use it, it does not represent any error.
|
||||
*/
|
||||
sentinel,
|
||||
/* CLI */
|
||||
bad_arguments,
|
||||
exit_now, /**< The program should terminate successfully. */
|
||||
fatal_error,
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an error message to show to user.
|
||||
*
|
||||
* For #ot::status::standard_error, the message is generated by strerror.
|
||||
*
|
||||
* The message is static, and must not be freed or modified. It is valid until the next call to
|
||||
* #error_message, because of stderror.
|
||||
* Wraps a status code with an optional message. It is implictly converted from and to a
|
||||
* #status_code.
|
||||
*/
|
||||
const char* error_message(status code);
|
||||
struct status {
|
||||
status(st code = st::ok) : code(code) {}
|
||||
template<class T> status(st code, T&& message) : code(code), message(message) {}
|
||||
operator st() { return code; }
|
||||
st code;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
/**
|
||||
* \defgroup ogg Ogg
|
||||
|
@ -4,13 +4,10 @@ target_link_libraries(opus.t libopustags)
|
||||
add_executable(ogg.t EXCLUDE_FROM_ALL ogg.cc)
|
||||
target_link_libraries(ogg.t libopustags)
|
||||
|
||||
add_executable(error.t EXCLUDE_FROM_ALL error.cc)
|
||||
target_link_libraries(error.t libopustags)
|
||||
|
||||
configure_file(gobble.opus . COPYONLY)
|
||||
|
||||
add_custom_target(
|
||||
check
|
||||
COMMAND prove "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
DEPENDS opustags gobble.opus opus.t ogg.t error.t
|
||||
DEPENDS opustags gobble.opus opus.t ogg.t
|
||||
)
|
||||
|
46
t/error.cc
46
t/error.cc
@ -1,46 +0,0 @@
|
||||
#include <opustags.h>
|
||||
#include "tap.h"
|
||||
|
||||
static void check_message()
|
||||
{
|
||||
if (strcmp(ot::error_message(ot::status::ok), "OK") != 0)
|
||||
throw failure("unexpected message for code ok");
|
||||
if (strcmp(ot::error_message(ot::status::overflowing_comment_data), "Overflowing comment data") != 0)
|
||||
throw failure("unexpected message for overflowing_comment_data");
|
||||
}
|
||||
|
||||
static void check_errno()
|
||||
{
|
||||
/* copy the messages in case strerror changes something */
|
||||
std::string got, expected;
|
||||
|
||||
errno = EINVAL;
|
||||
got = ot::error_message(ot::status::standard_error);
|
||||
expected = strerror(errno);
|
||||
if (got != expected)
|
||||
throw failure("unexpected message for errno EINVAL");
|
||||
|
||||
errno = 0;
|
||||
got = ot::error_message(ot::status::standard_error);
|
||||
expected = strerror(errno);
|
||||
if (got != expected)
|
||||
throw failure("unexpected message for errno 0");
|
||||
}
|
||||
|
||||
static void check_sentinel()
|
||||
{
|
||||
if (ot::error_message(ot::status::sentinel) != nullptr)
|
||||
throw failure("the sentinel should not have a message");
|
||||
auto too_far = static_cast<ot::status>(static_cast<int>(ot::status::sentinel) + 1);
|
||||
if (ot::error_message(too_far) != nullptr)
|
||||
throw failure("an invalid status should not have a message");
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << "1..3\n";
|
||||
run(check_message, "check a few error messages");
|
||||
run(check_errno, "check the message for standard errors");
|
||||
run(check_sentinel, "ensure the sentinel is respected");
|
||||
return 0;
|
||||
}
|
46
t/ogg.cc
46
t/ogg.cc
@ -11,36 +11,36 @@ static void check_ref_ogg()
|
||||
ot::ogg_reader reader(input.get());
|
||||
|
||||
ot::status rc = reader.read_page();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the first page");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the first packet");
|
||||
if (reader.packet.bytes != 19)
|
||||
throw failure("unexpected length for the first packet");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::end_of_page)
|
||||
if (rc != ot::st::end_of_page)
|
||||
throw failure("got an unexpected second packet on the first page");
|
||||
|
||||
rc = reader.read_page();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the second page");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the second packet");
|
||||
if (reader.packet.bytes != 62)
|
||||
throw failure("unexpected length for the first packet");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::end_of_page)
|
||||
if (rc != ot::st::end_of_page)
|
||||
throw failure("got an unexpected second packet on the second page");
|
||||
|
||||
while (!ogg_page_eos(&reader.page)) {
|
||||
rc = reader.read_page();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("failure reading a page");
|
||||
}
|
||||
rc = reader.read_page();
|
||||
if (rc != ot::status::end_of_stream)
|
||||
if (rc != ot::st::end_of_stream)
|
||||
throw failure("did not correctly detect the end of stream");
|
||||
}
|
||||
|
||||
@ -76,25 +76,25 @@ static void check_memory_ogg()
|
||||
{
|
||||
ot::ogg_writer writer(output.get());
|
||||
rc = writer.prepare_stream(1234);
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not prepare the stream for the first page");
|
||||
writer.write_packet(first_packet);
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not write the first packet");
|
||||
writer.flush_page();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not flush the first page");
|
||||
writer.prepare_stream(1234);
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not prepare the stream for the second page");
|
||||
writer.write_packet(second_packet);
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not write the second packet");
|
||||
writer.write_packet(third_packet);
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not write the third packet");
|
||||
writer.flush_page();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not flush the second page");
|
||||
my_ogg_size = ftell(output.get());
|
||||
if (my_ogg_size != 73)
|
||||
@ -109,34 +109,34 @@ static void check_memory_ogg()
|
||||
{
|
||||
ot::ogg_reader reader(input.get());
|
||||
rc = reader.read_page();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the first page");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the first packet");
|
||||
if (!same_packet(reader.packet, first_packet))
|
||||
throw failure("unexpected content in the first packet");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::end_of_page)
|
||||
if (rc != ot::st::end_of_page)
|
||||
throw failure("unexpected second packet in the first page");
|
||||
rc = reader.read_page();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the second page");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the second packet");
|
||||
if (!same_packet(reader.packet, second_packet))
|
||||
throw failure("unexpected content in the second packet");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("could not read the third packet");
|
||||
if (!same_packet(reader.packet, third_packet))
|
||||
throw failure("unexpected content in the third packet");
|
||||
rc = reader.read_packet();
|
||||
if (rc != ot::status::end_of_page)
|
||||
if (rc != ot::st::end_of_page)
|
||||
throw failure("unexpected third packet in the second page");
|
||||
rc = reader.read_page();
|
||||
if (rc != ot::status::end_of_stream)
|
||||
if (rc != ot::st::end_of_stream)
|
||||
throw failure("unexpected third page");
|
||||
}
|
||||
}
|
||||
|
26
t/opus.cc
26
t/opus.cc
@ -18,16 +18,16 @@ static void check_identification()
|
||||
ogg_packet packet {};
|
||||
packet.packet = (unsigned char*) "OpusHead..";
|
||||
packet.bytes = 10;
|
||||
if (ot::validate_identification_header(packet) != ot::status::ok)
|
||||
if (ot::validate_identification_header(packet) != ot::st::ok)
|
||||
throw failure("did not accept a good OpusHead");
|
||||
|
||||
packet.bytes = 7;
|
||||
if (ot::validate_identification_header(packet) != ot::status::bad_identification_header)
|
||||
if (ot::validate_identification_header(packet) != ot::st::bad_identification_header)
|
||||
throw failure("accepted an OpusHead that is too short");
|
||||
|
||||
packet.packet = (unsigned char*) "NotOpusHead";
|
||||
packet.bytes = 11;
|
||||
if (ot::validate_identification_header(packet) != ot::status::bad_identification_header)
|
||||
if (ot::validate_identification_header(packet) != ot::st::bad_identification_header)
|
||||
throw failure("did not report the right status for a bad OpusHead");
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ static void parse_standard()
|
||||
op.bytes = sizeof(standard_OpusTags) - 1;
|
||||
op.packet = (unsigned char*) standard_OpusTags;
|
||||
auto rc = ot::parse_tags(op, tags);
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("ot::parse_tags did not return ok");
|
||||
if (tags.vendor != "opustags test packet")
|
||||
throw failure("bad vendor string");
|
||||
@ -85,31 +85,31 @@ static void parse_corrupted()
|
||||
char* end = packet + size;
|
||||
|
||||
op.bytes = 7;
|
||||
if (ot::parse_tags(op, tags) != ot::status::overflowing_magic_number)
|
||||
if (ot::parse_tags(op, tags) != ot::st::overflowing_magic_number)
|
||||
throw failure("did not detect the overflowing magic number");
|
||||
op.bytes = 11;
|
||||
if (ot::parse_tags(op, tags) != ot::status::overflowing_vendor_length)
|
||||
if (ot::parse_tags(op, tags) != ot::st::overflowing_vendor_length)
|
||||
throw failure("did not detect the overflowing vendor string length");
|
||||
op.bytes = size;
|
||||
|
||||
header_data[0] = 'o';
|
||||
if (ot::parse_tags(op, tags) != ot::status::bad_magic_number)
|
||||
if (ot::parse_tags(op, tags) != ot::st::bad_magic_number)
|
||||
throw failure("did not detect the bad magic number");
|
||||
header_data[0] = 'O';
|
||||
|
||||
*vendor_length = end - vendor_string + 1;
|
||||
if (ot::parse_tags(op, tags) != ot::status::overflowing_vendor_data)
|
||||
if (ot::parse_tags(op, tags) != ot::st::overflowing_vendor_data)
|
||||
throw failure("did not detect the overflowing vendor string");
|
||||
*vendor_length = end - vendor_string - 3;
|
||||
if (ot::parse_tags(op, tags) != ot::status::overflowing_comment_count)
|
||||
if (ot::parse_tags(op, tags) != ot::st::overflowing_comment_count)
|
||||
throw failure("did not detect the overflowing comment count");
|
||||
*vendor_length = comment_count - vendor_string;
|
||||
|
||||
++*comment_count;
|
||||
if (ot::parse_tags(op, tags) != ot::status::overflowing_comment_length)
|
||||
if (ot::parse_tags(op, tags) != ot::st::overflowing_comment_length)
|
||||
throw failure("did not detect the overflowing comment length");
|
||||
*first_comment_length = end - first_comment_data + 1;
|
||||
if (ot::parse_tags(op, tags) != ot::status::overflowing_comment_data)
|
||||
if (ot::parse_tags(op, tags) != ot::st::overflowing_comment_data)
|
||||
throw failure("did not detect the overflowing comment data");
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ static void recode_standard()
|
||||
op.bytes = sizeof(standard_OpusTags) - 1;
|
||||
op.packet = (unsigned char*) standard_OpusTags;
|
||||
auto rc = ot::parse_tags(op, tags);
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("ot::parse_tags did not return ok");
|
||||
auto packet = ot::render_tags(tags);
|
||||
if (packet.b_o_s != 0)
|
||||
@ -148,7 +148,7 @@ static void recode_padding()
|
||||
op.packet = (unsigned char*) padded_OpusTags.data();
|
||||
|
||||
auto rc = ot::parse_tags(op, tags);
|
||||
if (rc != ot::status::ok)
|
||||
if (rc != ot::st::ok)
|
||||
throw failure("ot::parse_tags did not return ok");
|
||||
if (tags.extra_data != "\0hello"s)
|
||||
throw failure("corrupted extra data");
|
||||
|
Loading…
x
Reference in New Issue
Block a user