#include #include #include "cliparse.h" namespace blankie { namespace cliparse { void Parser::parse(int argc, char** argv) { for (Flag& flag : this->flags) { flag.used = false; flag.argument = nullptr; } this->arguments.clear(); int offset = 0; bool parse_flags = true; if (offset == 0 && argc) { this->program_name = argv[0]; offset++; } while (argc > offset) { if (argv[offset][0] != '-' || argv[offset][1] == '\0' || !parse_flags) { this->arguments.push_back(argv[offset]); } else if (argv[offset][1] == '-') { this->_handle_long_flag(argc, argv, offset, parse_flags); } else { this->_handle_short_flags(argc, argv, offset); } offset++; } auto unused_flag = std::find_if(this->flags.begin(), this->flags.end(), [](Flag& flag) { return flag.required && !flag.used; }); if (unused_flag != this->flags.end()) { throw missing_flag(unused_flag->long_flag); } if (this->arguments_min > this->arguments.size() || this->arguments_max < this->arguments.size()) { throw invalid_argument_count(this->arguments_min, this->arguments_max); } } void Parser::_handle_long_flag(int argc, char** argv, int& offset, bool& parse_flags) { if (argv[offset][2] == '\0') { parse_flags = false; return; } const char* long_flag = &argv[offset][2]; const char* equal_offset = strchr(long_flag, '='); size_t long_flag_length = equal_offset ? static_cast(equal_offset - long_flag) : strlen(long_flag); auto flag = std::find_if(this->flags.begin(), this->flags.end(), [=](Flag& flag) { return strncmp(flag.long_flag, long_flag, long_flag_length) == 0 && flag.long_flag[long_flag_length] == '\0'; }); if (flag == this->flags.end()) { throw unknown_flag(std::string(long_flag, long_flag_length)); } flag->used = true; if (flag->argument_required == ArgumentRequired::No) { if (equal_offset) { throw unnecessary_argument(flag->long_flag); } } else if (equal_offset) { flag->argument = &equal_offset[1]; } else if (flag->argument_required == ArgumentRequired::Yes) { if (offset < argc - 1) { flag->argument = argv[++offset]; } else { throw missing_argument(flag->long_flag); } } } void Parser::_handle_short_flags(int argc, char** argv, int& offset) { const char* options = &argv[offset][1]; while (options[0] != '\0') { Flag* flag = this->flag(options[0]); if (!flag) { throw unknown_flag(options[0]); } options++; flag->used = true; if (flag->argument_required == ArgumentRequired::No) { continue; } else if (options[0] != '\0') { flag->argument = options[0] == '=' ? &options[1] : options; break; } else if (flag->argument_required == ArgumentRequired::Yes) { if (offset < argc - 1) { flag->argument = argv[++offset]; } else { throw missing_argument(flag->long_flag); } break; } } } Flag* Parser::flag(const char* long_flag) { auto flag = std::find_if(this->flags.begin(), this->flags.end(), [=](Flag& flag) { return strcmp(flag.long_flag, long_flag) == 0; }); return flag != this->flags.end() ? &*flag : nullptr; } Flag* Parser::flag(char short_flag) { auto flag = std::find_if(this->flags.begin(), this->flags.end(), [=](Flag& flag) { return flag.short_flag == short_flag; }); return flag != this->flags.end() ? &*flag : nullptr; } } // namespace cliparse } // namespace blankie