From 94a8377b3e66d6d721510faf7c31cb81bdb296ff Mon Sep 17 00:00:00 2001 From: skal Date: Wed, 7 Feb 2018 23:09:14 -0800 Subject: [PATCH] extract the command-line parsing helpers to example_util + make img2webp tool use the text-file parsing option too. Change-Id: I1976e651bbe8b4701abceba89e054b4fb3c35696 --- examples/example_util.c | 69 ++++++++++++++++++++++++++ examples/example_util.h | 28 +++++++++++ examples/img2webp.c | 21 ++++---- examples/webpmux.c | 104 +++++++++++----------------------------- man/img2webp.1 | 7 ++- 5 files changed, 143 insertions(+), 86 deletions(-) diff --git a/examples/example_util.c b/examples/example_util.c index 8b408ce2..825a1234 100644 --- a/examples/example_util.c +++ b/examples/example_util.c @@ -12,10 +12,14 @@ #include "./example_util.h" +#include #include #include #include +#include "webp/mux_types.h" +#include "../imageio/imageio_util.h" + //------------------------------------------------------------------------------ // String parsing @@ -56,3 +60,68 @@ float ExUtilGetFloat(const char* const v, int* const error) { } return f; } + +//------------------------------------------------------------------------------ + +static void ResetCommandLineArguments(int argc, const char* argv[], + CommandLineArguments* const args) { + assert(args != NULL); + args->argc_ = argc; + args->argv_ = argv; + args->own_argv_ = 0; + WebPDataInit(&args->argv_data_); +} + +void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args) { + if (args != NULL) { + if (args->own_argv_) { + free((void*)args->argv_); + WebPDataClear(&args->argv_data_); + } + ResetCommandLineArguments(0, NULL, args); + } +} + +#define MAX_ARGC 16384 +int ExUtilInitCommandLineArguments(int argc, const char* argv[], + CommandLineArguments* const args) { + if (args == NULL || argv == NULL) return 0; + ResetCommandLineArguments(argc, argv, args); + if (argc == 1 && argv[0][0] != '-') { + char* cur; + const char sep[] = " \t\r\n\f\v"; + if (!ExUtilReadFileToWebPData(argv[0], &args->argv_data_)) { + return 0; + } + args->own_argv_ = 1; + args->argv_ = (const char**)malloc(MAX_ARGC * sizeof(*args->argv_)); + if (args->argv_ == NULL) return 0; + + argc = 0; + for (cur = strtok((char*)args->argv_data_.bytes, sep); + cur != NULL; + cur = strtok(NULL, sep)) { + if (argc == MAX_ARGC) { + fprintf(stderr, "ERROR: Arguments limit %d reached\n", MAX_ARGC); + return 0; + } + assert(strlen(cur) != 0); + args->argv_[argc++] = cur; + } + args->argc_ = argc; + } + return 1; +} + +//------------------------------------------------------------------------------ + +int ExUtilReadFileToWebPData(const char* const filename, + WebPData* const webp_data) { + const uint8_t* data; + size_t size; + if (webp_data == NULL) return 0; + if (!ImgIoUtilReadFile(filename, &data, &size)) return 0; + webp_data->bytes = data; + webp_data->size = size; + return 1; +} diff --git a/examples/example_util.h b/examples/example_util.h index 4bb42eb7..fe762a4d 100644 --- a/examples/example_util.h +++ b/examples/example_util.h @@ -14,6 +14,7 @@ #define WEBP_EXAMPLES_EXAMPLE_UTIL_H_ #include "webp/types.h" +#include "webp/mux_types.h" #ifdef __cplusplus extern "C" { @@ -35,6 +36,33 @@ float ExUtilGetFloat(const char* const v, int* const error); // actually parsed is returned, or -1 if an error occurred. int ExUtilGetInts(const char* v, int base, int max_output, int output[]); +// Reads a file named 'filename' into a WebPData structure. The content of +// webp_data is overwritten. Returns false in case of error. +int ExUtilReadFileToWebPData(const char* const filename, + WebPData* const webp_data); + +//------------------------------------------------------------------------------ +// Command-line arguments + +typedef struct { + int argc_; + const char** argv_; + WebPData argv_data_; + int own_argv_; +} CommandLineArguments; + +// Initializes the structure from the command-line parameters. If there is +// only one parameter and it does not start with a '-', then it is assumed to +// be a file name. This file will be read and tokenized into command-line +// arguments. The content of 'args' is overwritten. +// Returns false in case of error (memory allocation failure, non +// existing file, too many arguments, ...). +int ExUtilInitCommandLineArguments(int argc, const char* argv[], + CommandLineArguments* const args); + +// Deallocate all memory and reset 'args'. +void ExUtilDeleteCommandLineArguments(CommandLineArguments* const args); + #ifdef __cplusplus } // extern "C" #endif diff --git a/examples/img2webp.c b/examples/img2webp.c index d9012cc4..052aa87a 100644 --- a/examples/img2webp.c +++ b/examples/img2webp.c @@ -117,14 +117,13 @@ static int SetLoopCount(int loop_count, WebPData* const webp_data) { //------------------------------------------------------------------------------ -int main(int argc, char* argv[]) { +int main(int argc, const char* argv[]) { const char* output = NULL; WebPAnimEncoder* enc = NULL; int verbose = 0; int pic_num = 0; int duration = 100; int timestamp_ms = 0; - int ok = 1; int loop_count = 0; int width = 0, height = 0; WebPAnimEncoderOptions anim_config; @@ -133,17 +132,23 @@ int main(int argc, char* argv[]) { WebPData webp_data; int c; int have_input = 0; + CommandLineArguments cmd_args; + int ok = ExUtilInitCommandLineArguments(argc - 1, argv + 1, &cmd_args); + if (!ok) return 1; + argc = cmd_args.argc_; + argv = cmd_args.argv_; WebPDataInit(&webp_data); if (!WebPAnimEncoderOptionsInit(&anim_config) || !WebPConfigInit(&config) || !WebPPictureInit(&pic)) { fprintf(stderr, "Library version mismatch!\n"); - return 1; + ok = 0; + goto End; } // 1st pass of option parsing - for (c = 1; ok && c < argc; ++c) { + for (c = 0; ok && c < argc; ++c) { if (argv[c][0] == '-') { int parse_error = 0; if (!strcmp(argv[c], "-o") && c + 1 < argc) { @@ -171,7 +176,7 @@ int main(int argc, char* argv[]) { verbose = 1; } else if (!strcmp(argv[c], "-h") || !strcmp(argv[c], "-help")) { Help(); - return 0; + goto End; } else { continue; } @@ -184,13 +189,13 @@ int main(int argc, char* argv[]) { } if (!have_input) { fprintf(stderr, "No input file(s) for generating animation!\n"); - return 0; + goto End; } // image-reading pass pic_num = 0; config.lossless = 1; - for (c = 1; ok && c < argc; ++c) { + for (c = 0; ok && c < argc; ++c) { if (argv[c] == NULL) continue; if (argv[c][0] == '-') { // parse local options int parse_error = 0; @@ -294,7 +299,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, "[%d frames, %u bytes].\n", pic_num, (unsigned int)webp_data.size); } - WebPDataClear(&webp_data); + ExUtilDeleteCommandLineArguments(&cmd_args); return ok ? 0 : 1; } diff --git a/examples/webpmux.c b/examples/webpmux.c index aa6734fe..55e8d219 100644 --- a/examples/webpmux.c +++ b/examples/webpmux.c @@ -109,11 +109,7 @@ static const char* const kDescriptions[LAST_FEATURE] = { }; typedef struct { - // command line arguments - int argc_; - const char** argv_; - WebPData argv_data_; - int own_argv_; + CommandLineArguments cmd_args_; ActionType action_type_; const char* input_; @@ -126,12 +122,13 @@ typedef struct { //------------------------------------------------------------------------------ // Helper functions. -static int CountOccurrences(const Config* const config, const char* arg) { +static int CountOccurrences(const CommandLineArguments* const args, + const char* const arg) { int i; int num_occurences = 0; - for (i = 0; i < config->argc_; ++i) { - if (!strcmp(config->argv_[i], arg)) { + for (i = 0; i < args->argc_; ++i) { + if (!strcmp(args->argv_[i], arg)) { ++num_occurences; } } @@ -386,20 +383,10 @@ static void WarnAboutOddOffset(const WebPMuxFrameInfo* const info) { } } -static int ReadFileToWebPData(const char* const filename, - WebPData* const webp_data) { - const uint8_t* data; - size_t size; - if (!ImgIoUtilReadFile(filename, &data, &size)) return 0; - webp_data->bytes = data; - webp_data->size = size; - return 1; -} - static int CreateMux(const char* const filename, WebPMux** mux) { WebPData bitstream; assert(mux != NULL); - if (!ReadFileToWebPData(filename, &bitstream)) return 0; + if (!ExUtilReadFileToWebPData(filename, &bitstream)) return 0; *mux = WebPMuxCreate(&bitstream, 1); WebPDataClear(&bitstream); if (*mux != NULL) return 1; @@ -527,12 +514,8 @@ static int ParseBgcolorArgs(const char* args, uint32_t* const bgcolor) { static void DeleteConfig(Config* const config) { if (config != NULL) { free(config->args_); - if (config->own_argv_) { - free((void*)config->argv_); - WebPDataClear(&config->argv_data_); - } + ExUtilDeleteCommandLineArguments(&config->cmd_args_); memset(config, 0, sizeof(*config)); - WebPDataInit(&config->argv_data_); } } @@ -543,7 +526,7 @@ static void DeleteConfig(Config* const config) { // Returns 1 on valid, 0 otherwise. // Also fills up num_feature_args to be number of feature arguments given. // (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5). -static int ValidateCommandLine(const Config* const config, +static int ValidateCommandLine(const CommandLineArguments* const cmd_args, int* num_feature_args) { int num_frame_args; int num_loop_args; @@ -555,27 +538,27 @@ static int ValidateCommandLine(const Config* const config, *num_feature_args = 0; // Simple checks. - if (CountOccurrences(config, "-get") > 1) { + if (CountOccurrences(cmd_args, "-get") > 1) { ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate); } - if (CountOccurrences(config, "-set") > 1) { + if (CountOccurrences(cmd_args, "-set") > 1) { ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate); } - if (CountOccurrences(config, "-strip") > 1) { + if (CountOccurrences(cmd_args, "-strip") > 1) { ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate); } - if (CountOccurrences(config, "-info") > 1) { + if (CountOccurrences(cmd_args, "-info") > 1) { ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate); } - if (CountOccurrences(config, "-o") > 1) { + if (CountOccurrences(cmd_args, "-o") > 1) { ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate); } // Compound checks. - num_frame_args = CountOccurrences(config, "-frame"); - num_loop_args = CountOccurrences(config, "-loop"); - num_bgcolor_args = CountOccurrences(config, "-bgcolor"); - num_durations_args = CountOccurrences(config, "-duration"); + num_frame_args = CountOccurrences(cmd_args, "-frame"); + num_loop_args = CountOccurrences(cmd_args, "-loop"); + num_bgcolor_args = CountOccurrences(cmd_args, "-bgcolor"); + num_durations_args = CountOccurrences(cmd_args, "-duration"); if (num_loop_args > 1) { ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate); @@ -630,8 +613,8 @@ static int ParseCommandLine(Config* config) { int i = 0; int feature_arg_index = 0; int ok = 1; - int argc = config->argc_; - const char* const* argv = config->argv_; + int argc = config->cmd_args_.argc_; + const char* const* argv = config->cmd_args_.argv_; while (i < argc) { FeatureArg* const arg = &config->args_[feature_arg_index]; @@ -820,52 +803,19 @@ static int ValidateConfig(Config* const config) { return ok; } -#define MAX_ARGC 16384 -static int SetArguments(int argc, const char* argv[], - Config* const config) { - assert(config != NULL); - memset(config, 0, sizeof(*config)); - WebPDataInit(&config->argv_data_); - if (argc == 1 && argv[0][0] != '-') { - char* cur; - const char sep[] = " \t\r\n\f\v"; - if (!ReadFileToWebPData(argv[0], &config->argv_data_)) { - return 0; - } - config->own_argv_ = 1; - config->argv_ = (const char**)malloc(MAX_ARGC * sizeof(*config->argv_)); - if (config->argv_ == NULL) return 0; - - argc = 0; - for (cur = strtok((char*)config->argv_data_.bytes, sep); - cur != NULL; - cur = strtok(NULL, sep)) { - if (argc == MAX_ARGC) { - fprintf(stderr, "ERROR: Arguments limit %d reached\n", MAX_ARGC); - return 0; - } - assert(strlen(cur) != 0); - config->argv_[argc++] = cur; - } - config->argc_ = argc; - } else { - config->argc_ = argc; - config->argv_ = argv; - config->own_argv_ = 0; - } - return 1; -} - // Create config object from command-line arguments. static int InitializeConfig(int argc, const char* argv[], Config* const config) { int num_feature_args = 0; - int ok = SetArguments(argc, argv, config); + int ok; + memset(config, 0, sizeof(*config)); + + ok = ExUtilInitCommandLineArguments(argc, argv, &config->cmd_args_); if (!ok) return 0; // Validate command-line arguments. - if (!ValidateCommandLine(config, &num_feature_args)) { + if (!ValidateCommandLine(&config->cmd_args_, &num_feature_args)) { ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1); } @@ -1009,8 +959,8 @@ static int Process(const Config* config) { case SUBTYPE_ANMF: { WebPMuxFrameInfo frame; frame.id = WEBP_CHUNK_ANMF; - ok = ReadFileToWebPData(config->args_[i].filename_, - &frame.bitstream); + ok = ExUtilReadFileToWebPData(config->args_[i].filename_, + &frame.bitstream); if (!ok) goto Err2; ok = ParseFrameArgs(config->args_[i].params_, &frame); if (!ok) { @@ -1045,7 +995,7 @@ static int Process(const Config* config) { case FEATURE_XMP: { ok = CreateMux(config->input_, &mux); if (!ok) goto Err2; - ok = ReadFileToWebPData(config->args_[0].filename_, &chunk); + ok = ExUtilReadFileToWebPData(config->args_[0].filename_, &chunk); if (!ok) goto Err2; err = WebPMuxSetChunk(mux, kFourccList[config->type_], &chunk, 1); free((void*)chunk.bytes); diff --git a/man/img2webp.1 b/man/img2webp.1 index c12e2406..910c7942 100644 --- a/man/img2webp.1 +++ b/man/img2webp.1 @@ -1,11 +1,13 @@ .\" Hey, EMACS: -*- nroff -*- -.TH IMG2WEBP 1 "January 23, 2017" +.TH IMG2WEBP 1 "February 7, 2018" .SH NAME img2webp \- create animated WebP file from a sequence of input images. .SH SYNOPSIS .B img2webp [file_level_options] [files] [per_frame_options...] .br +.B img2webp argument_file_name +.br .SH DESCRIPTION This manual page documents the .B img2webp @@ -13,6 +15,9 @@ command. .PP \fBimg2webp\fP compresses a sequence of images using the animated WebP format. Input images can either be PNG, JPEG, TIFF or WebP. +If a single file name (not starting with the character '\-') is supplied as +the argument, the command line argument are actually tokenized from this file. +This allows for easy scripting or using large number of arguments. .SH FILE-LEVEL OPTIONS The file-level options are applied at the beginning of the compression process, before the input frames are read.