mf/blankie/cliparse.cpp

124 lines
3.7 KiB
C++

#include <algorithm>
#include <cstring>
#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<size_t>(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