diff --git a/src/fart/Cli.cpp b/src/fart/Cli.cpp index 4db0051..d4fd149 100644 --- a/src/fart/Cli.cpp +++ b/src/fart/Cli.cpp @@ -1,5 +1,177 @@ #include +#include +#include +#include -Cli::Cli() +#define FLAGS \ + FLAG(height, "h", true) \ + FLAG(height_l, "height", true) \ + FLAG(help, "help", false) \ + FLAG(output, "o", true) \ + FLAG(output_l, "output", true) \ + FLAG(version, "version", false) \ + FLAG(width, "w", true) \ + FLAG(width_l, "width", true) \ + +#define FLAG(name, flag, arg) flag_##name, +typedef enum { + FLAGS + flag__count +} flag_id_t; +#undef FLAG + +#define FLAG(name, flag, arg) static const char flag_##name##_str[] = flag; +FLAGS +#undef FLAG + +typedef struct +{ + const char * flag; + size_t flag_length; + bool arg; +} flag_def_t; + +#define FLAG(name, flag, arg) { \ + flag_##name##_str, \ + sizeof(flag_##name##_str) - 1u, \ + arg}, +static const flag_def_t flag_defs[] = { + FLAGS +}; +#undef FLAG + +int Cli::run(int argc, char * argv[]) +{ + for (size_t i = 1; i < argc; i++) + { + size_t dashes = 0u; + if (strncmp(argv[i], "--", 2) == 0) + { + dashes = 2u; + } + else if (strncmp(argv[i], "-", 1) == 0) + { + dashes = 1u; + } + if (dashes > 0u) + { + const char * flag = &argv[i][dashes]; + size_t flag_idx; + for (flag_idx = 0u; flag_idx < flag__count; flag_idx++) + { + size_t flag_length = flag_defs[flag_idx].flag_length; + size_t flag_dashes = flag_length == 1u ? 1u : 2u; + if (flag_dashes != dashes) + { + continue; + } + if (strncmp(flag, flag_defs[flag_idx].flag, flag_length) != 0) + { + continue; + } + if (dashes == 2 && flag[flag_length] != '\0' && flag[flag_length] != '=') + { + continue; + } + bool arg = flag_defs[flag_idx].arg; + const char * arg_value = nullptr; + if (arg) + { + if (flag[flag_length] == '\0') + { + if (i >= (argc - 1)) + { + fprintf(stderr, "Error: option %s requires an argument\n", flag_defs[flag_idx].flag); + return 2; + } + i++; + arg_value = argv[i]; + } + else if (dashes == 1u) + { + arg_value = &flag[flag_length]; + } + else if (flag[flag_length + 1] != '\0') + { + arg_value = &flag[flag_length + 1]; + } + else + { + fprintf(stderr, "Error: option %s requires an argument\n", flag_defs[flag_idx].flag); + return 2; + } + } + else if (dashes == 2u && flag[flag_length] == '=') + { + fprintf(stderr, "Error: option %s does not take an argument\n", flag_defs[flag_idx].flag); + return 2; + } + else + { + /* TODO: support combining short flags, e.g. -cofoo */ + } + int r = handle_option(flag_idx, arg_value); + if (r != 0) + { + return r; + } + break; + } + if (flag_idx == flag__count) + { + fprintf(stderr, "Unknown flag: %s\n", flag); + return 2; + } + } + else + { + if (this->options.input_file != "") + { + fprintf(stderr, "Extraneous argument: %s\n", argv[i]); + return 2; + } + this->options.input_file = argv[i]; + } + } + return 0; +} + +int Cli::handle_option(size_t flag_idx, const char * arg_value) +{ + switch (flag_idx) + { + case flag_height: + case flag_height_l: + this->options.height = atoi(arg_value); + return 0; + + case flag_help: + printf("Usage: fart [options] input-file\n"); + printf("\n"); + printf(" Options:\n"); + printf("\n"); + printf(" -h, --height H Set render height to H\n"); + printf(" --help Show this help and exit\n"); + printf(" -o, --output F Write rendered image to file F\n"); + printf(" -w, --width W Set render width to W\n"); + printf(" --version Show fart version and exit\n"); + return 1; + + case flag_output: + case flag_output_l: + this->options.output_file = arg_value; + return 0; + + case flag_width: + case flag_width_l: + this->options.width = atoi(arg_value); + return 0; + + case flag_version: + printf("fart, version 0.0.0.0.0.1\n"); + return 1; + } + fprintf(stderr, "Internal error: unhandled flag_idx=%zu\n", flag_idx); + return 2; } diff --git a/src/fart/Cli.h b/src/fart/Cli.h index 57559ba..a38ebf4 100644 --- a/src/fart/Cli.h +++ b/src/fart/Cli.h @@ -1,7 +1,14 @@ #pragma once +#include + class Cli { public: - Cli(); + Options options; + + int run(int argc, char * argv[]); + +private: + int handle_option(size_t option_idx, const char * arg_value); }; diff --git a/src/fart/Options.h b/src/fart/Options.h new file mode 100644 index 0000000..7c3ae3f --- /dev/null +++ b/src/fart/Options.h @@ -0,0 +1,10 @@ +#include +#include + +struct Options +{ + std::string input_file; + std::string output_file; + size_t width = 0u; + size_t height = 0u; +}; diff --git a/src/fart/main.cpp b/src/fart/main.cpp index 4b64b28..13fb017 100644 --- a/src/fart/main.cpp +++ b/src/fart/main.cpp @@ -5,6 +5,11 @@ int main(int argc, char * argv[]) { Cli cli; + int rc = cli.run(argc, argv); + if (rc > 0) + { + return rc - 1; + } Engine engine; printf("Hello\n"); return 0;