Commit 529c1aac authored by Paul Asmuth's avatar Paul Asmuth
Browse files

switch to getopt for option parsing

parent 63c06482
Loading
Loading
Loading
Loading
+16 −21
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <iostream>
#include <iterator>
#include <string>
#include <getopt.h>

#include "config.h"
#include "context.h"
@@ -32,45 +33,40 @@ void printError(const ReturnCode& rc) {
  std::cerr << fmt::format("ERROR: {}", rc.message) << std::endl;
}

int main(int argc, const char** argv) {
  FlagParser flag_parser;
int main(int argc, char** argv) {
  FlagList flags;

  std::string flag_in;
  flag_parser.defineString("in", false, &flag_in);
  flags_add_string(&flags, 'i', "in", &flag_in);

  std::string flag_out;
  flag_parser.defineString("out", false, &flag_out);
  flags_add_string(&flags, 'e', "out", &flag_out);

  bool flag_stdin = false;
  flag_parser.defineSwitch("stdin", &flag_stdin);
  flags_add_switch(&flags, 0, "stdin", &flag_stdin);

  bool flag_stdout = false;
  flag_parser.defineSwitch("stdout", &flag_stdout);
  flags_add_switch(&flags, 0, "stdout", &flag_stdout);

  std::string flag_format;
  flag_parser.defineString("format", false, &flag_format);
  flags_add_string(&flags, 'f', "format", &flag_format);

  bool flag_help = false;
  flag_parser.defineSwitch("help", &flag_help);
  flags_add_switch(&flags, 'h', "help", &flag_help);

  bool flag_version = false;
  flag_parser.defineSwitch("version", &flag_version);
  flags_add_switch(&flags, 'v', "version", &flag_version);

  bool flag_debug = true;
  flag_parser.defineSwitch("debug", &flag_debug);

  bool flag_font_defaults = true;
  flag_parser.defineBool("font-defaults", &flag_font_defaults);
  flags_add_switch(&flags, 'd', "debug", &flag_debug);

  std::vector<std::string> flag_font_load;
  flag_parser.defineStringV("font-load", &flag_font_load);
  flags_add_stringv(&flags, 0, "font-load", &flag_font_load);

  {
    auto rc = flag_parser.parseArgv(argc - 1, argv + 1);
    if (!rc) {
  std::vector<std::string> args;
  if (auto rc = flags_parse(flags, argc, argv, &args); !rc) {
    std::cerr << "ERROR: " << rc.message << std::endl;
      return -1;
    }
    return EXIT_FAILURE;
  }

  if (flag_version) {
@@ -156,7 +152,6 @@ int main(int argc, const char** argv) {

  /* set up the context */
  Context ctx;
  ctx.font_defaults = flag_font_defaults;
  ctx.font_load = flag_font_load;

  if (auto rc = context_setup_defaults(&ctx); !rc) {
+102 −219
Original line number Diff line number Diff line
@@ -11,252 +11,135 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "flagparser.h"

#include <string>
#include <vector>
#include <assert.h>
#include "flagparser.h"
#include <getopt.h>

namespace clip {

FlagParser::FlagParser() {}

void FlagParser::defineString(
    const char* longopt,
    bool required,
void flags_add_string(
      FlagList* flags,
      char shortopt,
      const std::string& longopt,
      std::string* value) {
  FlagState flag_state;
  flag_state.type = T_STRING;
  flag_state.required = required;
  flag_state.longopt = longopt;
  flag_state.value = static_cast<void*>(value);
  flag_state.has_value = false;
  flags_.emplace_back(flag_state);
}
  FlagInfo flag;
  flag.opt_short = shortopt;
  flag.opt_long = longopt;
  flag.has_arg = true;

void FlagParser::defineStringV(
    const char* longopt,
    std::vector<std::string>* value) {
  FlagState flag_state;
  flag_state.type = T_STRING_V;
  flag_state.required = false;
  flag_state.longopt = longopt;
  flag_state.value = static_cast<void*>(value);
  flag_state.has_value = false;
  flags_.emplace_back(flag_state);
}
  flag.callback = [value] (auto v) {
    *value = v ? std::string(v) : std::string();
  };

void FlagParser::defineBool(
    const char* longopt,
    bool* value) {
  FlagState flag_state;
  flag_state.type = T_BOOL;
  flag_state.required = false;
  flag_state.longopt = longopt;
  flag_state.value = static_cast<void*>(value);
  flag_state.has_value = false;
  flags_.emplace_back(flag_state);
  flags->push_back(flag);
}

void FlagParser::defineSwitch(
    const char* longopt,
    bool* value) {
  FlagState flag_state;
  flag_state.type = T_SWITCH;
  flag_state.required = false;
  flag_state.longopt = longopt;
  flag_state.value = static_cast<void*>(value);
  flag_state.has_value = false;
  flags_.emplace_back(flag_state);
}
void flags_add_stringv(
      FlagList* flags,
      char shortopt,
      const std::string& longopt,
      std::vector<std::string>* values) {
  FlagInfo flag;
  flag.opt_short = shortopt;
  flag.opt_long = longopt;
  flag.has_arg = true;

void FlagParser::defineInt64(
    const char* longopt,
    bool required,
    int64_t* value) {
  FlagState flag_state;
  flag_state.type = T_INT64;
  flag_state.required = required;
  flag_state.longopt = longopt;
  flag_state.value = static_cast<void*>(value);
  flag_state.has_value = false;
  flags_.emplace_back(flag_state);
}
  flag.callback = [values] (auto v) {
    values->push_back(v ? std::string(v) : std::string());
  };

void FlagParser::defineUInt64(
    const char* longopt,
    bool required,
    uint64_t* value) {
  FlagState flag_state;
  flag_state.type = T_UINT64;
  flag_state.required = required;
  flag_state.longopt = longopt;
  flag_state.value = static_cast<void*>(value);
  flag_state.has_value = false;
  flags_.emplace_back(flag_state);
  flags->push_back(flag);
}

void FlagParser::defineFloat64(
    const char* longopt,
    bool required,
    double* value) {
  FlagState flag_state;
  flag_state.type = T_FLOAT64;
  flag_state.required = required;
  flag_state.longopt = longopt;
  flag_state.value = static_cast<void*>(value);
  flag_state.has_value = false;
  flags_.emplace_back(flag_state);
void flags_add_switch(
      FlagList* flags,
      char shortopt,
      const std::string& longopt,
      bool* value) {
  FlagInfo flag;
  flag.opt_short = shortopt;
  flag.opt_long = longopt;
  flag.has_arg = false;

  flag.callback = [value] (auto _) {
    *value = true;
  };

  flags->push_back(flag);
}

ReturnCode flags_parse(
    const FlagList& flags,
    int argc,
    char** argv,
    std::vector<std::string>* args_out) {
  // prepapre long opts
  std::vector<struct option> opts_long(flags.size() + 1);
  opts_long[flags.size()] = {0, 0, 0, 0};

  for (size_t i = 0; i < flags.size(); ++i) {
    opts_long[i] = {
      flags[i].opt_long.c_str(),
      flags[i].has_arg ? 1 : 0,
      0,
      0
    };
  }

  // prepare short opts
  std::string opts_short;
  for (const auto& f : flags) {
    if (f.opt_short == 0) {
      continue;
    }

ReturnCode parseValue(FlagState* flag, const std::string& value) {
  switch (flag->type) {
    case T_STRING:
      *static_cast<std::string*>(flag->value) = value;
      break;

    case T_STRING_V:
      static_cast<std::vector<std::string>*>(flag->value)->push_back(value);
      break;
    opts_short.push_back(f.opt_short);

    case T_SWITCH:
    case T_BOOL:
      if (value == "on") {
        *static_cast<bool*>(flag->value) = true;
      } else if (value == "off") {
        *static_cast<bool*>(flag->value) = false;
      } else {
        return errorf(
            ERROR,
            "flag --{}=... invalid value",
            flag->longopt);
    if (f.has_arg) {
      opts_short.push_back(':');
    }
      break;

    case T_INT64:
      try {
        *static_cast<int64_t*>(flag->value) = std::stoll(value);
      } catch (std::exception e) {
        return errorf(
            ERROR,
            "flag --{}=... invalid value",
            flag->longopt);
  }
      break;

    case T_UINT64:
      try {
        *static_cast<uint64_t*>(flag->value) = std::stoull(value);
      } catch (std::exception e) {
        return errorf(
            ERROR,
            "flag --{}=... invalid value",
            flag->longopt);
      }
      break;
  // call getopt
  for (;;) {
    int opt_long = 0;
    int opt_short = getopt_long(
        argc,
        argv,
        opts_short.c_str(),
        opts_long.data(),
        &opt_long);

    case T_FLOAT64:
      try {
        *static_cast<double*>(flag->value) = std::stod(value);
      } catch (std::exception e) {
        return errorf(
            ERROR,
            "flag --{}=... invalid value",
            flag->longopt);
      }
    if (opt_short == -1) {
      break;

    default:
      return errorf(
          ERROR,
          "flag --{}=... invalid type",
          flag->longopt);
    }

  flag->has_value = true;
  return OK;
}

ReturnCode FlagParser::parseArgv(int argc, const char** argv) {
  std::vector<std::string> args;
  for (int i = 0; i < argc; ++i) {
    args.emplace_back(argv[i]);
  }
  return parseArgv(args);
}

ReturnCode FlagParser::parseArgv(const std::vector<std::string>& argv) {
  for (int i = 0; i < argv.size(); i++) {
    int eq_len = -1;
    FlagState* flag_ptr = nullptr;
    auto& arg = argv[i];

    if (arg.size() == 0) {
      continue;
    }

    for (auto& flag : flags_) {
      auto longopt = std::string("--") + flag.longopt;
      auto longopt_eq = std::string("--") + flag.longopt + "=";

      if (arg.size() == longopt.size() &&
          arg.compare(0, longopt.size(), longopt) == 0) {
        flag_ptr = &flag;
      }

      else if (arg.size() == longopt_eq.size() &&
          arg.compare(0, longopt_eq.size(), longopt_eq) == 0) {
        flag_ptr = &flag;
        eq_len = longopt_eq.size();
      }

      if (flag_ptr != nullptr) {
    const FlagInfo* flag = nullptr;
    if (opt_short == 0) {
      flag = &flags[opt_long];
    } else {
      for (const auto& f : flags) {
        if (opt_short == f.opt_short) {
          flag = &f;
          break;
        }
      }

    if (flag_ptr == nullptr) {
      return errorf(
          ERROR,
          "invalid flag: {}",
          arg);
    } else if (eq_len > 0) {
      if (arg.size() == eq_len) {
        return errorf(
            ERROR,
            "flag --{}=... has no value",
            flag_ptr->longopt);
    }

      auto rc = parseValue(flag_ptr, arg.substr(eq_len));
      if (!rc) {
        return rc;
      }
    } else if (flag_ptr->type == T_SWITCH) {
      auto rc = parseValue(flag_ptr, "on");
      if (!rc) {
        return rc;
      }
    } else {
      if (i + 1 < argv.size()) {
        auto rc = parseValue(flag_ptr, argv[++i]);
        if (!rc) {
          return rc;
        }
      } else if (flag_ptr->required) {
        return errorf(
            ERROR,
            "flag --{} has no value",
            flag_ptr->longopt);
      }
    if (!flag) {
      return error(ERROR, "invalid option");
    }

    flag->callback(optarg);
  }

  for (const auto& flag : flags_) {
    if (flag.required == true && !flag.has_value) {
      return errorf(
          ERROR,
          "flag --{} is required",
          flag.longopt);
  // copy unparsed arguments
  if (args_out) {
    for (size_t i = optind; i < argc; ++i) {
      args_out->push_back(argv[i]);
    }
  }

+24 −82
Original line number Diff line number Diff line
@@ -18,95 +18,37 @@

namespace clip {

enum kFlagType {
  T_BOOL,
  T_SWITCH,
  T_STRING,
  T_STRING_V,
  T_INT64,
  T_UINT64,
  T_FLOAT64,
struct FlagInfo {
  char opt_short;
  std::string opt_long;
  bool has_arg;
  std::function<void (const char*)> callback;
};

struct FlagState {
  kFlagType type;
  bool required;
  const char* longopt;
  std::vector<std::string> values;
  void* value;
  bool has_value;
};

class FlagParser {
public:
using FlagList = std::vector<FlagInfo>;

  FlagParser();

  /**
   * Define a string flag
   */
  void defineString(
      const char* longopt,
      bool required,
void flags_add_string(
      FlagList* flags,
      char shortopt,
      const std::string& longopt,
      std::string* value);

  /**
   * Define a vec<string> flag
   */
  void defineStringV(
      const char* longopt,
      std::vector<std::string>* value);
void flags_add_stringv(
      FlagList* flags,
      char shortopt,
      const std::string& longopt,
      std::vector<std::string>* values);

  /**
   * Define a boolean flag
   */
  void defineBool(
      const char* longopt,
void flags_add_switch(
      FlagList* flags,
      char shortopt,
      const std::string& longopt,
      bool* value);

  /**
   * Define a boolean flag
   */
  void defineSwitch(
      const char* longopt,
      bool* value);

  /**
   * Define a int64 flag
   */
  void defineInt64(
      const char* longopt,
      bool required,
      int64_t* value);

  /**
   * Define a uint64 flag
   */
  void defineUInt64(
      const char* longopt,
      bool required,
      uint64_t* value);

  /**
   * Define a float64 flag
   */
  void defineFloat64(
      const char* longopt,
      bool required,
      double* value);

  /**
   * Parse an argv array. This may throw an exception.
   */
  ReturnCode parseArgv(int argc, const char** argv);

  /**
   * Parse an argv array. This may throw an exception.
   */
  ReturnCode parseArgv(const std::vector<std::string>& argv);

protected:
  std::vector<FlagState> flags_;
};
ReturnCode flags_parse(
    const FlagList& flags,
    int argc,
    char** argv,
    std::vector<std::string>* args_out);

}