124 lines
3.7 KiB
C++
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
|