From 407c12c7ace3421df4a143ca046ec59caa05c728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Mangano-Tarumi?= Date: Mon, 19 Nov 2018 19:22:18 -0500 Subject: [PATCH] review the overall code documentation --- src/cli.cc | 45 +++---------- src/ogg.cc | 5 ++ src/opustags.cc | 7 ++ src/opustags.h | 171 ++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 165 insertions(+), 63 deletions(-) diff --git a/src/cli.cc b/src/cli.cc index 9fd4e99..3258195 100644 --- a/src/cli.cc +++ b/src/cli.cc @@ -1,3 +1,11 @@ +/** + * \file src/cli.cc + * \ingroup cli + * + * Provide all the features of the opustags executable from a C++ API. The main point of separating + * this module from the main one is to allow easy testing. + */ + #include #include @@ -41,21 +49,6 @@ static struct option getopt_options[] = { {NULL, 0, 0, 0} }; -/** - * 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::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::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. - */ ot::status ot::process_options(int argc, char** argv, ot::options& opt) { if (argc == 1) { @@ -149,14 +142,6 @@ ot::status ot::process_options(int argc, char** argv, ot::options& opt) } /** - * Display the tags on stdout. - * - * Print all the comments, separated by line breaks. Since a comment may - * contain line breaks, this output is not completely reliable, but it fits - * most cases. - * - * The output generated is meant to be parseable by #ot::read_tags. - * * \todo Escape new lines. */ void ot::print_comments(const std::list& comments, FILE* output) @@ -168,9 +153,7 @@ void ot::print_comments(const std::list& comments, FILE* output) } /** - * Parse the comments outputted by #ot::print_comments. - * - * \todo Use an std::istream or getline. Lift the 16 KiB limitation and whatever's hardcoded here. + * \todo Use getline. Lift the 16 KiB limitation and whatever's hardcoded here. */ std::list ot::read_comments(FILE* input) { @@ -236,10 +219,6 @@ static ot::status process_tags(const ogg_packet& packet, const ot::options& opt, } } -/** - * Main loop of opustags. Read the packets from the reader, and forwards them to the writer. - * Transform the OpusTags packet on the fly. - */ ot::status ot::process(ogg_reader& reader, ogg_writer* writer, const ot::options &opt) { int packet_count = 0; @@ -307,12 +286,6 @@ static bool same_file(const std::string& path_in, const std::string& path_out) return false; } -/** - * Open the input and output streams, then call #ot::process. - * - * This is the main entry point to the opustags program, and pretty much the same as calling - * opustags from the command-line. - */ ot::status ot::run(ot::options& opt) { if (!opt.path_out.empty() && same_file(opt.path_in, opt.path_out)) diff --git a/src/ogg.cc b/src/ogg.cc index c8220c3..af2931b 100644 --- a/src/ogg.cc +++ b/src/ogg.cc @@ -1,6 +1,11 @@ /** * \file src/ogg.c * \ingroup ogg + * + * High-level interface for libogg. + * + * This module is not meant to be a complete libogg wrapper, but rather a convenient and highly + * specialized layer above libogg and stdio. */ #include "opustags.h" diff --git a/src/opustags.cc b/src/opustags.cc index d8a0239..3e0955d 100644 --- a/src/opustags.cc +++ b/src/opustags.cc @@ -1,3 +1,10 @@ +/** + * \file src/opustags.cc + * \brief Main function for opustags. + * + * See opustags.h for the program's documentation. + */ + #include /** diff --git a/src/opustags.h b/src/opustags.h index d9e0f36..6570a3c 100644 --- a/src/opustags.h +++ b/src/opustags.h @@ -1,6 +1,24 @@ /** * \file src/opustags.h - * \brief Interface of all the submodules of opustags. + * + * Welcome to opustags! + * + * Let's have a quick tour around. The project is split into the following modules: + * + * - The ogg module reads and writes Ogg files, letting you manipulate Ogg pages and packets. + * - The opus module parses the contents of Ogg packets according to the Opus specifications. + * - The cli module implements the main logic of the program. + * - The opustags module contains the main function, which is a simple wrapper around cli. + * + * Each module is implemented in its eponymous .cc file. Their interfaces are all defined and + * documented together in this header file. Look into the .cc files for implementation-specific + * details. + * + * To understand how this program works, you need to know what an Ogg files is made of, in + * particular the streams, pages, and packets. You hardly need any knowledge of the actual Opus + * audio codec, but need the RFC 7845 "Ogg Encapsulation for the Opus Audio Codec" that defines the + * format of the header packets that are essential to opustags. + * */ #include @@ -16,7 +34,12 @@ namespace ot { /** - * Possible return status. + * Possible return status code, ranging from errors to special statuses. They are usually + * accompanied with a message with the #status structure. + * + * Functions that return non-ok status codes to signal special conditions like #end_of_stream should + * have it explictly mentionned in their documentation. By default, a non-ok status should be + * handled like an error. * * The cut error family means that the end of packet was reached when attempting to read the * overflowing value. For example, cut_comment_count means that after reading the vendor string, @@ -47,10 +70,11 @@ enum class st { }; /** - * Wraps a status code with an optional message. It is implictly converted from and to a + * Wraps a status code with an optional message. It is implictly converted to and from a * #status_code. * - * All the error statuses should be accompanied with a relevant error message. + * All the statuses except #st::ok should be accompanied with a relevant error message, in case it + * propagates back to the main function and is shown to the user. */ struct status { status(st code = st::ok) : code(code) {} @@ -60,14 +84,8 @@ struct status { std::string message; }; -/** +/***********************************************************************************************//** * \defgroup ogg Ogg - * - * High-level interface for libogg. - * - * This module is not meant to be a complete libogg wrapper, but rather a convenient and highly - * specialized layer above libogg and stdio. - * * \{ */ @@ -251,28 +269,31 @@ private: /** \} */ -/** +/***********************************************************************************************//** * \defgroup opus Opus - * \brief Opus packet decoding and recoding. - * * \{ */ /** - * Represent all the data in an OpusTags packet. + * Faithfully represent *all* the data in an OpusTags packet, exactly as they will be written in the + * final stream, disregarding the current system locale or anything else. + * + * The vendor and comment strings are expected to contain valid UTF-8, but we should keep their + * values intact even if the string is not UTF-8 clean, or encoded in any other way. */ struct opus_tags { /** - * OpusTags packets begin with a vendor string, meant to identify the - * implementation of the encoder. It is an arbitrary UTF-8 string. + * OpusTags packets begin with a vendor string, meant to identify the implementation of the + * encoder. It should be an arbitrary UTF-8 string. */ std::string vendor; /** - * Comments. These are a list of string following the NAME=Value format. - * A comment may also be called a field, or a tag. + * Comments. These are a list of string following the NAME=Value format. A comment may also + * be called a field, or a tag. * - * The field name in vorbis comment is case-insensitive and ASCII, - * while the value can be any valid UTF-8 string. + * The field name in vorbis comment is case-insensitive and ASCII, while the value can be + * any valid UTF-8 string. The specification is not too clear for Opus, but let's assume + * it's the same. */ std::list comments; /** @@ -315,35 +336,131 @@ void delete_comments(opus_tags& tags, const char* field_name); /** \} */ -/** +/***********************************************************************************************//** * \defgroup cli Command-Line Interface * \{ */ +/** + * Structured representation of the arguments to opustags. + */ struct options { + /** + * Path to the input file. It cannot be empty. The special "-" string means stdin. + * + * This is the mandatory non-flagged parameter. + */ std::string path_in; + /** + * Path to the optional file. The special "-" string means stdout. When empty, opustags runs + * in read-only mode. + * + * Option: --output + */ std::string path_out; /** - * If null, in-place editing is disabled. - * Otherwise, it contains the suffix to add to the file name. + * If null, in-place editing is disabled. Otherwise, it points to the suffix to add to the + * file name. + * + * Option: --in-place + */ + const char* inplace = nullptr; + /** + * List of field names to delete. `{"ARTIST"}` will delete *all* the comments `ARTIST=*`. It + * is currently case-sensitive. When #delete_all is true, it becomes meaningless. + * + * #to_add takes precedence over #to_delete, so if the same comment appears in both lists, + * the one in #to_delete applies only to the previously existing tags. + * + * \todo Consider making it case-insensitive. + * \todo Allow values like `ARTIST=x` to delete only the ARTIST comment whose value is x. + * + * Option: --delete, --set */ - const char *inplace = nullptr; - std::vector to_add; std::vector to_delete; + /** + * List of comments to add, in the current system encoding. For exemple `TITLE=a b c`. They + * must be valid. + * + * Options: --add, --set, --set-all + */ + std::vector to_add; + /** + * Delete all the existing comments. + * + * Option: --delete-all + */ bool delete_all = false; + /** + * Replace the previous comments by the ones supplied by the user. + * + * Read a list of comments from stdin and populate #to_add. Implies #delete_all. Further + * comments may be added with the --add option. + * + * Option: --set-all + */ bool set_all = false; + /** + * By default, opustags won't overwrite the output file if it already exists. + * + * Option: --overwrite + */ bool overwrite = false; + /** + * When true, opustags prints a detailed help and exits. All the other options are ignored. + * + * Option: --help + */ bool print_help = false; }; +/** + * 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::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::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. + */ status process_options(int argc, char** argv, options& opt); +/** + * Print all the comments, separated by line breaks. Since a comment may + * contain line breaks, this output is not completely reliable, but it fits + * most cases. + * + * The output generated is meant to be parseable by #ot::read_tags. + */ void print_comments(const std::list& comments, FILE* output); + +/** + * Parse the comments outputted by #ot::print_comments. + */ std::list read_comments(FILE* input); -status run(options& opt); +/** + * Main loop of opustags. Read the packets from the reader, and forwards them to the writer. + * Transform the OpusTags packet on the fly. + * + * The writer is optional. When writer is nullptr, opustags runs in read-only mode. + */ status process(ogg_reader& reader, ogg_writer* writer, const options &opt); +/** + * Open the input and output streams, then call #ot::process. + * + * This is the main entry point to the opustags program, and pretty much the same as calling + * opustags from the command-line. + */ +status run(options& opt); + /** \} */ }