Included SimpleCLI library
This commit is contained in:
parent
ffc01d9a2a
commit
1b95cda089
|
@ -5,3 +5,5 @@ build/*
|
|||
*.map
|
||||
|
||||
.DS_Store
|
||||
|
||||
utils/web_converter/css_html_js_minify/__pycache__/
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include "cli.h"
|
||||
|
||||
#include <SimpleCLI.h> // SimpleCLI library
|
||||
#include "src/SimpleCLI-1.1.1/SimpleCLI.h"
|
||||
|
||||
#if SIMPLECLI_VERSION_MAJOR == 1 && SIMPLECLI_VERSION_MINOR < 1
|
||||
#error "Please update SimpleCLI library"
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include "Argument.h"
|
||||
|
||||
extern "C" {
|
||||
#include "c/arg.h"
|
||||
}
|
||||
|
||||
Argument::Argument(arg* argPointer, bool persistent) : argPointer(argPointer), persistent(persistent) {
|
||||
if (!persistent) argPointer = arg_copy(argPointer);
|
||||
}
|
||||
|
||||
Argument::Argument(const Argument& a) {
|
||||
argPointer = a.argPointer;
|
||||
persistent = a.persistent;
|
||||
if (!persistent) argPointer = arg_copy(argPointer);
|
||||
}
|
||||
|
||||
Argument::Argument(Argument&& a) {
|
||||
argPointer = a.argPointer;
|
||||
persistent = a.persistent;
|
||||
a.argPointer = NULL;
|
||||
}
|
||||
|
||||
Argument::~Argument() {
|
||||
if (!persistent) arg_destroy(argPointer);
|
||||
}
|
||||
|
||||
Argument& Argument::operator=(const Argument& a) {
|
||||
argPointer = a.argPointer;
|
||||
persistent = a.persistent;
|
||||
if (!persistent) argPointer = arg_copy(argPointer);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument& Argument::operator=(Argument&& a) {
|
||||
argPointer = a.argPointer;
|
||||
persistent = a.persistent;
|
||||
a.argPointer = NULL;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Argument::operator==(const Argument& a) const {
|
||||
return equals(a);
|
||||
}
|
||||
|
||||
bool Argument::operator!=(const Argument& a) const {
|
||||
return !equals(a);
|
||||
}
|
||||
|
||||
Argument::operator bool() const {
|
||||
return argPointer;
|
||||
}
|
||||
|
||||
bool Argument::isSet() const {
|
||||
return argPointer && argPointer->set == ARG_SET;
|
||||
}
|
||||
|
||||
bool Argument::isRequired() const {
|
||||
return argPointer && argPointer->req == ARG_REQ;
|
||||
}
|
||||
|
||||
bool Argument::isOptional() const {
|
||||
return !isRequired();
|
||||
}
|
||||
|
||||
bool Argument::hasDefaultValue() const {
|
||||
return isOptional();
|
||||
}
|
||||
|
||||
bool Argument::isReq() const {
|
||||
return isRequired();
|
||||
}
|
||||
|
||||
bool Argument::isOpt() const {
|
||||
return isOptional();
|
||||
}
|
||||
|
||||
String Argument::getName() const {
|
||||
if (argPointer) return String(argPointer->name);
|
||||
return String();
|
||||
}
|
||||
|
||||
String Argument::getValue() const {
|
||||
if (argPointer) return String(arg_get_value(argPointer));
|
||||
return String();
|
||||
}
|
||||
|
||||
ArgumentType Argument::getType() const {
|
||||
if (argPointer) {
|
||||
if (argPointer->mode == ARG_DEFAULT) return ArgumentType::NORMAL;
|
||||
if (argPointer->mode == ARG_POS) return ArgumentType::POSITIONAL;
|
||||
if (argPointer->mode == ARG_FLAG) return ArgumentType::FLAG;
|
||||
}
|
||||
return ArgumentType::NORMAL;
|
||||
}
|
||||
|
||||
String Argument::toString() const {
|
||||
String s;
|
||||
|
||||
toString(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void Argument::toString(String& s) const {
|
||||
if (isOptional()) s += '[';
|
||||
|
||||
String n = getName();
|
||||
String v = getValue();
|
||||
|
||||
switch (getType()) {
|
||||
case ArgumentType::NORMAL:
|
||||
case ArgumentType::POSITIONAL:
|
||||
s += '-';
|
||||
s += n;
|
||||
s += ' ';
|
||||
s += '<';
|
||||
s += v.length() > 0 ? v : "value";
|
||||
s += '>';
|
||||
break;
|
||||
|
||||
case ArgumentType::FLAG:
|
||||
s += '-';
|
||||
s += n;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isOptional()) s += ']';
|
||||
}
|
||||
|
||||
bool Argument::equals(String name, bool caseSensetive) const {
|
||||
return equals(name.c_str(), caseSensetive);
|
||||
}
|
||||
|
||||
bool Argument::equals(const char* name, bool caseSensetive) const {
|
||||
return argPointer && name && arg_name_equals(argPointer, name, strlen(name), caseSensetive);
|
||||
}
|
||||
|
||||
bool Argument::equals(const Argument& a, bool caseSensetive) const {
|
||||
return argPointer && a.argPointer && arg_equals(argPointer, a.argPointer, caseSensetive);
|
||||
}
|
||||
|
||||
arg* Argument::getPtr() {
|
||||
return argPointer;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef Argument_h
|
||||
#define Argument_h
|
||||
|
||||
#include "StringCLI.h"
|
||||
|
||||
extern "C" {
|
||||
#include "c/arg_types.h" // arg
|
||||
}
|
||||
|
||||
#define ARGUMENT_TEMPORARY false
|
||||
#define ARGUMENT_PERSISTENT true
|
||||
|
||||
enum class ArgumentType { NORMAL, POSITIONAL, FLAG };
|
||||
|
||||
class Argument {
|
||||
friend class Command;
|
||||
friend class SimpleCLI;
|
||||
|
||||
private:
|
||||
arg* argPointer;
|
||||
bool persistent;
|
||||
|
||||
public:
|
||||
Argument(arg* argPointer = NULL, bool persistent = ARGUMENT_PERSISTENT);
|
||||
Argument(const Argument& a);
|
||||
Argument(Argument&& a);
|
||||
|
||||
~Argument();
|
||||
|
||||
Argument& operator=(const Argument& a);
|
||||
Argument& operator=(Argument&& a);
|
||||
|
||||
bool operator==(const Argument& a) const;
|
||||
bool operator!=(const Argument& a) const;
|
||||
|
||||
operator bool() const;
|
||||
|
||||
bool isSet() const;
|
||||
bool isRequired() const;
|
||||
bool isOptional() const;
|
||||
bool hasDefaultValue() const;
|
||||
|
||||
bool isReq() const;
|
||||
bool isOpt() const;
|
||||
|
||||
String getName() const;
|
||||
String getValue() const;
|
||||
|
||||
ArgumentType getType() const;
|
||||
|
||||
String toString() const;
|
||||
void toString(String& s) const;
|
||||
|
||||
bool equals(String name, bool caseSensetive = false) const;
|
||||
bool equals(const char* name, bool caseSensetive = false) const;
|
||||
bool equals(const Argument& a, bool caseSensetive = false) const;
|
||||
|
||||
arg* getPtr();
|
||||
};
|
||||
|
||||
#endif /* ifndef Argument_h */
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include "Command.h"
|
||||
|
||||
extern "C" {
|
||||
#include "c/cmd.h"
|
||||
#include "c/arg.h"
|
||||
}
|
||||
|
||||
Command::Command(cmd* cmdPointer, bool persistent) : cmdPointer(cmdPointer), persistent(persistent) {
|
||||
if (!persistent) this->cmdPointer = cmd_copy(cmdPointer);
|
||||
}
|
||||
|
||||
Command::Command(const Command& c) {
|
||||
persistent = c.persistent;
|
||||
cmdPointer = c.cmdPointer;
|
||||
if (!persistent) cmdPointer = cmd_copy(c.cmdPointer);
|
||||
}
|
||||
|
||||
Command::Command(Command&& c) {
|
||||
persistent = c.persistent;
|
||||
cmdPointer = c.cmdPointer;
|
||||
c.cmdPointer = NULL;
|
||||
}
|
||||
|
||||
Command::~Command() {
|
||||
if (!persistent) cmd_destroy(cmdPointer);
|
||||
}
|
||||
|
||||
Command& Command::operator=(const Command& c) {
|
||||
persistent = c.persistent;
|
||||
cmdPointer = c.cmdPointer;
|
||||
if (!persistent) cmdPointer = cmd_copy(c.cmdPointer);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Command& Command::operator=(Command&& c) {
|
||||
persistent = c.persistent;
|
||||
cmdPointer = c.cmdPointer;
|
||||
c.cmdPointer = NULL;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Command::operator==(const Command& c) const {
|
||||
return equals(c);
|
||||
}
|
||||
|
||||
bool Command::operator!=(const Command& c) const {
|
||||
return !equals(c);
|
||||
}
|
||||
|
||||
Command::operator bool() const {
|
||||
return cmdPointer;
|
||||
}
|
||||
|
||||
bool Command::setCaseSensetive(bool caseSensetive) {
|
||||
if (cmdPointer) {
|
||||
cmdPointer->case_sensetive = caseSensetive;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Command::setCallback(void (* callback)(cmd* c)) {
|
||||
if (cmdPointer && callback) {
|
||||
cmdPointer->callback = callback;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Command::setDescription(const char* description) {
|
||||
cmd_set_description(cmdPointer, description);
|
||||
}
|
||||
|
||||
Argument Command::addArg(const char* name, const char* defaultValue) {
|
||||
if (cmdPointer && (cmdPointer->mode == CMD_DEFAULT)) {
|
||||
arg* a = arg_create_opt(name, defaultValue);
|
||||
|
||||
cmd_add_arg(cmdPointer, a);
|
||||
return Argument(a);
|
||||
}
|
||||
return Argument();
|
||||
}
|
||||
|
||||
Argument Command::addArg(const char* name) {
|
||||
if (cmdPointer && (cmdPointer->mode == CMD_DEFAULT)) {
|
||||
arg* a = arg_create_req(name);
|
||||
|
||||
cmd_add_arg(cmdPointer, a);
|
||||
return Argument(a);
|
||||
}
|
||||
return Argument();
|
||||
}
|
||||
|
||||
Argument Command::addPosArg(const char* name, const char* defaultValue) {
|
||||
if (cmdPointer && (cmdPointer->mode == CMD_DEFAULT)) {
|
||||
arg* a = arg_create_opt_positional(name, defaultValue);
|
||||
|
||||
cmd_add_arg(cmdPointer, a);
|
||||
return Argument(a);
|
||||
}
|
||||
return Argument();
|
||||
}
|
||||
|
||||
Argument Command::addPosArg(const char* name) {
|
||||
if (cmdPointer && (cmdPointer->mode == CMD_DEFAULT)) {
|
||||
arg* a = arg_create_req_positional(name);
|
||||
|
||||
cmd_add_arg(cmdPointer, a);
|
||||
return Argument(a);
|
||||
}
|
||||
return Argument();
|
||||
}
|
||||
|
||||
Argument Command::addFlagArg(const char* name, const char* defaultValue) {
|
||||
if (cmdPointer && (cmdPointer->mode == CMD_DEFAULT)) {
|
||||
arg* a = arg_create_flag(name, defaultValue);
|
||||
|
||||
cmd_add_arg(cmdPointer, a);
|
||||
return Argument(a);
|
||||
}
|
||||
return Argument();
|
||||
}
|
||||
|
||||
Argument Command::addArgument(const char* name, const char* defaultValue) {
|
||||
return addArg(name, defaultValue);
|
||||
}
|
||||
|
||||
Argument Command::addArgument(const char* name) {
|
||||
return addArg(name);
|
||||
}
|
||||
|
||||
Argument Command::addPositionalArgument(const char* name, const char* defaultValue) {
|
||||
return addPosArg(name, defaultValue);
|
||||
}
|
||||
|
||||
Argument Command::addPositionalArgument(const char* name) {
|
||||
return addPosArg(name);
|
||||
}
|
||||
|
||||
Argument Command::addFlagArgument(const char* name, const char* defaultValue) {
|
||||
return addFlagArg(name, defaultValue);
|
||||
}
|
||||
|
||||
bool Command::equals(String name) const {
|
||||
return equals(name.c_str());
|
||||
}
|
||||
|
||||
bool Command::equals(const char* name) const {
|
||||
if (cmdPointer && name) return cmd_name_equals(cmdPointer, name, strlen(name), cmdPointer->case_sensetive) == CMD_NAME_EQUALS;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Command::equals(const Command& c) const {
|
||||
if (cmdPointer && c.cmdPointer) return cmd_equals(cmdPointer, c.cmdPointer, cmdPointer->case_sensetive) == CMD_NAME_EQUALS;
|
||||
return false;
|
||||
}
|
||||
|
||||
String Command::getName() const {
|
||||
if (cmdPointer) {
|
||||
return String(cmdPointer->name);
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
int Command::countArgs() const {
|
||||
int i = 0;
|
||||
|
||||
if (cmdPointer) {
|
||||
arg* h = cmdPointer->arg_list;
|
||||
|
||||
while (h) {
|
||||
++i;
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
Argument Command::getArgument(int i) const {
|
||||
if (!cmdPointer) return Argument();
|
||||
|
||||
arg* h = cmdPointer->arg_list;
|
||||
int j = 0;
|
||||
|
||||
while (j < i && h) {
|
||||
h = h->next;
|
||||
++j;
|
||||
}
|
||||
|
||||
return Argument(j == i ? h : NULL);
|
||||
}
|
||||
|
||||
Argument Command::getArgument(const char* name) const {
|
||||
if (!cmdPointer || !name) return Argument();
|
||||
|
||||
arg* h = cmdPointer->arg_list;
|
||||
int j = 0;
|
||||
|
||||
while (h) {
|
||||
if (arg_name_equals(h, name, strlen(name), cmdPointer->case_sensetive) == ARG_NAME_EQUALS) return h;
|
||||
h = h->next;
|
||||
++j;
|
||||
}
|
||||
|
||||
return Argument();
|
||||
}
|
||||
|
||||
Argument Command::getArgument(String name) const {
|
||||
return getArgument(name.c_str());
|
||||
}
|
||||
|
||||
Argument Command::getArgument(const Argument& a) const {
|
||||
return getArgument(a.argPointer ? a.argPointer->name : NULL);
|
||||
}
|
||||
|
||||
Argument Command::getArg(int i) const {
|
||||
return getArgument(i);
|
||||
}
|
||||
|
||||
Argument Command::getArg(const char* name) const {
|
||||
return getArgument(name);
|
||||
}
|
||||
|
||||
Argument Command::getArg(String name) const {
|
||||
return getArgument(name);
|
||||
}
|
||||
|
||||
Argument Command::getArg(const Argument& a) const {
|
||||
return getArgument(a);
|
||||
}
|
||||
|
||||
String Command::toString(bool description) const {
|
||||
String s;
|
||||
|
||||
toString(s, description);
|
||||
return s;
|
||||
}
|
||||
|
||||
CommandType Command::getType() const {
|
||||
if (cmdPointer) {
|
||||
switch (cmdPointer->mode) {
|
||||
case CMD_DEFAULT:
|
||||
return CommandType::NORMAL;
|
||||
case CMD_BOUNDLESS:
|
||||
return CommandType::BOUNDLESS;
|
||||
case CMD_SINGLE:
|
||||
return CommandType::SINGLE;
|
||||
}
|
||||
}
|
||||
return CommandType::NORMAL;
|
||||
}
|
||||
|
||||
bool Command::hasDescription() const {
|
||||
return cmdPointer && cmdPointer->description;
|
||||
}
|
||||
|
||||
String Command::getDescription() const {
|
||||
return String(cmd_get_description(cmdPointer));
|
||||
}
|
||||
|
||||
void Command::toString(String& s, bool description) const {
|
||||
if (cmdPointer) {
|
||||
s += String(cmdPointer->name);
|
||||
|
||||
if (cmdPointer->mode == CMD_BOUNDLESS) {
|
||||
s += ' ';
|
||||
s += "<value> <value> ...";
|
||||
} else if (cmdPointer->mode == CMD_SINGLE) {
|
||||
s += ' ';
|
||||
s += "<...>";
|
||||
} else {
|
||||
arg* h = cmdPointer->arg_list;
|
||||
|
||||
while (h) {
|
||||
s += ' ';
|
||||
Argument(h).toString(s);
|
||||
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (description && hasDescription()) {
|
||||
s += "\r\n" + getDescription();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Command::run() const {
|
||||
if (cmdPointer && cmdPointer->callback) cmdPointer->callback(cmdPointer);
|
||||
}
|
||||
|
||||
cmd* Command::getPtr() {
|
||||
return cmdPointer;
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef Command_h
|
||||
#define Command_h
|
||||
|
||||
#include "StringCLI.h"
|
||||
#include "Argument.h" // Argument
|
||||
|
||||
extern "C" {
|
||||
#include "c/cmd_types.h" // cmd
|
||||
}
|
||||
|
||||
#define COMMAND_TEMPORARY false
|
||||
#define COMMAND_PERSISTENT true
|
||||
|
||||
enum class CommandType { NORMAL, BOUNDLESS, SINGLE };
|
||||
|
||||
class Command {
|
||||
friend class SimpleCLI;
|
||||
|
||||
private:
|
||||
cmd* cmdPointer;
|
||||
bool persistent;
|
||||
|
||||
public:
|
||||
Command(cmd* cmdPointer = NULL, bool persistent = COMMAND_PERSISTENT);
|
||||
Command(const Command& c);
|
||||
Command(Command&& c);
|
||||
~Command();
|
||||
|
||||
Command& operator=(const Command& c);
|
||||
Command& operator=(Command&& c);
|
||||
|
||||
bool operator==(const Command& c) const;
|
||||
bool operator!=(const Command& c) const;
|
||||
|
||||
operator bool() const;
|
||||
|
||||
bool setCaseSensetive(bool caseSensetive = true);
|
||||
bool setCallback(void (* callback)(cmd* c));
|
||||
|
||||
void setDescription(const char* description);
|
||||
|
||||
Argument addArg(const char* name, const char* defaultValue);
|
||||
Argument addArg(const char* name);
|
||||
Argument addPosArg(const char* name, const char* defaultValue);
|
||||
Argument addPosArg(const char* name);
|
||||
Argument addFlagArg(const char* name, const char* defaultValue = "");
|
||||
|
||||
Argument addArgument(const char* name, const char* defaultValue);
|
||||
Argument addArgument(const char* name);
|
||||
Argument addPositionalArgument(const char* name, const char* defaultValue);
|
||||
Argument addPositionalArgument(const char* name);
|
||||
Argument addFlagArgument(const char* name, const char* defaultValue = "");
|
||||
|
||||
bool equals(String name) const;
|
||||
bool equals(const char* name) const;
|
||||
bool equals(const Command& c) const;
|
||||
|
||||
String getName() const;
|
||||
int countArgs() const;
|
||||
|
||||
Argument getArgument(int i = 0) const;
|
||||
Argument getArgument(const char* name) const;
|
||||
Argument getArgument(String name) const;
|
||||
Argument getArgument(const Argument& a) const;
|
||||
|
||||
Argument getArg(int i = 0) const;
|
||||
Argument getArg(const char* name) const;
|
||||
Argument getArg(String name) const;
|
||||
Argument getArg(const Argument& a) const;
|
||||
|
||||
CommandType getType() const;
|
||||
|
||||
bool hasDescription() const;
|
||||
String getDescription() const;
|
||||
|
||||
String toString(bool description = true) const;
|
||||
void toString(String& s, bool description = true) const;
|
||||
|
||||
void run() const;
|
||||
|
||||
cmd* getPtr();
|
||||
};
|
||||
|
||||
#endif /* ifndef Command_h */
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include "CommandError.h"
|
||||
|
||||
extern "C" {
|
||||
#include "c/cmd_error.h" // cmd_error_create, cmd_error_destroy
|
||||
}
|
||||
|
||||
CommandError::CommandError(cmd_error* errorPointer, bool persistent) : errorPointer(errorPointer), persistent(persistent) {
|
||||
if (!persistent) this->errorPointer = cmd_error_copy(errorPointer);
|
||||
}
|
||||
|
||||
CommandError::CommandError(const CommandError& e) {
|
||||
persistent = e.persistent;
|
||||
errorPointer = e.errorPointer;
|
||||
if (!persistent) errorPointer = cmd_error_copy(errorPointer);
|
||||
}
|
||||
|
||||
CommandError::CommandError(CommandError&& e) {
|
||||
persistent = e.persistent;
|
||||
errorPointer = e.errorPointer;
|
||||
e.errorPointer = NULL;
|
||||
}
|
||||
|
||||
CommandError::~CommandError() {
|
||||
if (!persistent) cmd_error_destroy(errorPointer);
|
||||
}
|
||||
|
||||
CommandError& CommandError::operator=(const CommandError& e) {
|
||||
persistent = e.persistent;
|
||||
errorPointer = e.errorPointer;
|
||||
if (!persistent) errorPointer = cmd_error_copy(errorPointer);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CommandError& CommandError::operator=(CommandError&& e) {
|
||||
persistent = e.persistent;
|
||||
errorPointer = e.errorPointer;
|
||||
e.errorPointer = NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CommandError::operator==(const CommandError& e) const {
|
||||
return errorPointer == e.errorPointer ||
|
||||
(errorPointer && e.errorPointer &&
|
||||
errorPointer->mode == e.errorPointer->mode &&
|
||||
errorPointer->command == e.errorPointer->command &&
|
||||
errorPointer->argument == e.errorPointer->argument &&
|
||||
errorPointer->data == e.errorPointer->data);
|
||||
}
|
||||
|
||||
bool CommandError::operator!=(const CommandError& e) const {
|
||||
return !(*this == e);
|
||||
}
|
||||
|
||||
bool CommandError::operator>(const CommandError& e) const {
|
||||
return errorPointer && e.errorPointer && errorPointer->mode > e.errorPointer->mode;
|
||||
}
|
||||
|
||||
bool CommandError::operator<(const CommandError& e) const {
|
||||
return errorPointer && e.errorPointer && errorPointer->mode < e.errorPointer->mode;
|
||||
}
|
||||
|
||||
bool CommandError::operator>=(const CommandError& e) const {
|
||||
return errorPointer && e.errorPointer && errorPointer->mode >= e.errorPointer->mode;
|
||||
}
|
||||
|
||||
bool CommandError::operator<=(const CommandError& e) const {
|
||||
return errorPointer && e.errorPointer && errorPointer->mode <= e.errorPointer->mode;
|
||||
}
|
||||
|
||||
CommandError::operator bool() const {
|
||||
return errorPointer && errorPointer->mode != CMD_PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
bool CommandError::hasCommand() const {
|
||||
return errorPointer && errorPointer->command;
|
||||
}
|
||||
|
||||
bool CommandError::hasArgument() const {
|
||||
return errorPointer && errorPointer->argument;
|
||||
}
|
||||
|
||||
bool CommandError::hasData() const {
|
||||
return errorPointer && errorPointer->data;
|
||||
}
|
||||
|
||||
CommandErrorType CommandError::getType() const {
|
||||
if (errorPointer) {
|
||||
switch (errorPointer->mode) {
|
||||
case CMD_NULL_PTR: return CommandErrorType::NULL_POINTER;
|
||||
case CMD_EMPTY_LINE: return CommandErrorType::EMPTY_LINE;
|
||||
case CMD_PARSE_SUCCESS: return CommandErrorType::PARSE_SUCCESSFUL;
|
||||
case CMD_NOT_FOUND: return CommandErrorType::COMMAND_NOT_FOUND;
|
||||
case CMD_UNKOWN_ARG: return CommandErrorType::UNKNOWN_ARGUMENT;
|
||||
case CMD_MISSING_ARG: return CommandErrorType::MISSING_ARGUMENT;
|
||||
case CMD_MISSING_ARG_VALUE: return CommandErrorType::MISSING_ARGUMENT_VALUE;
|
||||
case CMD_UNCLOSED_QUOTE: return CommandErrorType::UNCLOSED_QUOTE;
|
||||
}
|
||||
}
|
||||
return CommandErrorType::PARSE_SUCCESSFUL;
|
||||
}
|
||||
|
||||
bool CommandError::hasCmd() const {
|
||||
return hasCommand();
|
||||
}
|
||||
|
||||
bool CommandError::hasArg() const {
|
||||
return hasArgument();
|
||||
}
|
||||
|
||||
Command CommandError::getCommand() const {
|
||||
return Command(errorPointer ? errorPointer->command : NULL);
|
||||
}
|
||||
|
||||
Argument CommandError::getArgument() const {
|
||||
return Argument(errorPointer ? errorPointer->argument : NULL);
|
||||
}
|
||||
|
||||
String CommandError::getData() const {
|
||||
if (!errorPointer || !errorPointer->data) return String();
|
||||
return String(errorPointer->data);
|
||||
}
|
||||
|
||||
String CommandError::getMessage() const {
|
||||
if (errorPointer) {
|
||||
switch (errorPointer->mode) {
|
||||
case CMD_NULL_PTR: return String(F("NULL Pointer"));
|
||||
case CMD_EMPTY_LINE: return String(F("Empty input"));
|
||||
case CMD_PARSE_SUCCESS: return String(F("No error"));
|
||||
case CMD_NOT_FOUND: return String(F("Command not found"));
|
||||
case CMD_UNKOWN_ARG: return String(F("Unknown argument"));
|
||||
case CMD_MISSING_ARG: return String(F("Missing argument"));
|
||||
case CMD_MISSING_ARG_VALUE: return String(F("Missing argument value"));
|
||||
case CMD_UNCLOSED_QUOTE: return String(F("Unclosed quote"));
|
||||
}
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
Command CommandError::getCmd() const {
|
||||
return getCommand();
|
||||
}
|
||||
|
||||
Argument CommandError::getArg() const {
|
||||
return getArgument();
|
||||
}
|
||||
|
||||
String CommandError::getMsg() const {
|
||||
return getMessage();
|
||||
}
|
||||
|
||||
String CommandError::toString() const {
|
||||
String s;
|
||||
|
||||
toString(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void CommandError::toString(String& s) const {
|
||||
s += getMessage();
|
||||
if (hasCommand()) s += String(F(" at command '")) + getCommand().getName() + String(F("'"));
|
||||
if (hasArgument()) s += String(F(" at argument '")) + getArgument().toString() + String(F("'"));
|
||||
if (hasData()) s += String(F(" at '")) + getData() + String(F("'"));
|
||||
}
|
||||
|
||||
cmd_error* CommandError::getPtr() {
|
||||
return errorPointer;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef CommandError_h
|
||||
#define CommandError_h
|
||||
|
||||
#include "StringCLI.h"
|
||||
#include "Command.h" // Command
|
||||
|
||||
extern "C" {
|
||||
#include "c/cmd_error_types.h" // cmd_error
|
||||
}
|
||||
|
||||
#define COMMAND_ERROR_TEMPORARY false
|
||||
#define COMMAND_ERROR_PERSISTENT true
|
||||
|
||||
enum class CommandErrorType { NULL_POINTER, EMPTY_LINE, PARSE_SUCCESSFUL,
|
||||
COMMAND_NOT_FOUND, UNKNOWN_ARGUMENT, MISSING_ARGUMENT,
|
||||
MISSING_ARGUMENT_VALUE, UNCLOSED_QUOTE };
|
||||
|
||||
class CommandError {
|
||||
private:
|
||||
cmd_error* errorPointer;
|
||||
bool persistent;
|
||||
|
||||
public:
|
||||
CommandError(cmd_error* errorPointer = NULL, bool persistent = COMMAND_ERROR_PERSISTENT);
|
||||
CommandError(const CommandError& e);
|
||||
CommandError(CommandError&& e);
|
||||
~CommandError();
|
||||
|
||||
CommandError& operator=(const CommandError& e);
|
||||
CommandError& operator=(CommandError&& e);
|
||||
|
||||
bool operator==(const CommandError& e) const;
|
||||
bool operator!=(const CommandError& e) const;
|
||||
|
||||
bool operator>(const CommandError& e) const;
|
||||
bool operator<(const CommandError& e) const;
|
||||
|
||||
bool operator>=(const CommandError& e) const;
|
||||
bool operator<=(const CommandError& e) const;
|
||||
|
||||
operator bool() const;
|
||||
|
||||
bool hasCommand() const;
|
||||
bool hasArgument() const;
|
||||
bool hasData() const;
|
||||
|
||||
bool hasCmd() const;
|
||||
bool hasArg() const;
|
||||
|
||||
CommandErrorType getType() const;
|
||||
Command getCommand() const;
|
||||
Argument getArgument() const;
|
||||
String getData() const;
|
||||
String getMessage() const;
|
||||
|
||||
Command getCmd() const;
|
||||
Argument getArg() const;
|
||||
String getMsg() const;
|
||||
|
||||
String toString() const;
|
||||
void toString(String& s) const;
|
||||
|
||||
cmd_error* getPtr();
|
||||
};
|
||||
|
||||
#endif /* ifndef CommandError_h */
|
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include "SimpleCLI.h"
|
||||
|
||||
extern "C" {
|
||||
#include "c/cmd.h" // cmd
|
||||
#include "c/parser.h" // parse_lines
|
||||
#include "c/cmd_error.h" // cmd_error_destroy
|
||||
}
|
||||
|
||||
SimpleCLI::SimpleCLI(int commandQueueSize, int errorQueueSize) : commandQueueSize(commandQueueSize), errorQueueSize(errorQueueSize) {}
|
||||
|
||||
SimpleCLI::~SimpleCLI() {
|
||||
cmd_destroy_rec(cmdList);
|
||||
cmd_destroy_rec(cmdQueue);
|
||||
cmd_error_destroy_rec(errorQueue);
|
||||
}
|
||||
|
||||
void SimpleCLI::pause() {
|
||||
pauseParsing = true;
|
||||
}
|
||||
|
||||
void SimpleCLI::unpause() {
|
||||
pauseParsing = false;
|
||||
|
||||
// Go through queued errors
|
||||
while (onError && errored()) {
|
||||
onError(getError().getPtr());
|
||||
}
|
||||
|
||||
// Go through queued commands
|
||||
if (available()) {
|
||||
cmd* prev = NULL;
|
||||
cmd* current = cmdQueue;
|
||||
cmd* next = NULL;
|
||||
|
||||
while (current) {
|
||||
next = current->next;
|
||||
|
||||
// Has callback, then run it and remove from queue
|
||||
if (current->callback) {
|
||||
current->callback(current);
|
||||
if (prev) prev->next = next;
|
||||
cmd_destroy(current);
|
||||
} else {
|
||||
prev = current;
|
||||
}
|
||||
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleCLI::parse(const String& input) {
|
||||
parse(input.c_str(), input.length());
|
||||
}
|
||||
|
||||
void SimpleCLI::parse(const char* input) {
|
||||
if (input) parse(input, strlen(input));
|
||||
}
|
||||
|
||||
void SimpleCLI::parse(const char* str, size_t len) {
|
||||
// Split str into a list of lines
|
||||
line_list* l = parse_lines(str, len);
|
||||
|
||||
// Go through all lines and try to find a matching command
|
||||
line_node* n = l->first;
|
||||
|
||||
while (n) {
|
||||
cmd* h = cmdList;
|
||||
bool success = false;
|
||||
bool errored = false;
|
||||
|
||||
while (h && !success && !errored) {
|
||||
cmd_error* e = cmd_parse(h, n);
|
||||
|
||||
// When parsing was successful
|
||||
if (e->mode == CMD_PARSE_SUCCESS) {
|
||||
if (h->callback && !pauseParsing) h->callback(h);
|
||||
else cmdQueue = cmd_push(cmdQueue, cmd_copy(h), commandQueueSize);
|
||||
|
||||
success = true;
|
||||
}
|
||||
|
||||
// When command name matches but something else went wrong, exit with error
|
||||
else if (e->mode > CMD_NOT_FOUND) {
|
||||
if (onError && !pauseParsing) {
|
||||
onError(e);
|
||||
} else {
|
||||
errorQueue = cmd_error_push(errorQueue, cmd_error_copy(e), errorQueueSize);
|
||||
}
|
||||
|
||||
errored = true;
|
||||
}
|
||||
|
||||
// When command name does not match
|
||||
|
||||
/*else (e->mode <= CMD_NOT_FOUND) {
|
||||
|
||||
}*/
|
||||
|
||||
cmd_error_destroy(e);
|
||||
|
||||
cmd_reset(h);
|
||||
|
||||
h = h->next;
|
||||
}
|
||||
|
||||
// No error but no success either => Command could not be found
|
||||
if (!errored && !success) {
|
||||
cmd_error* e = cmd_error_create_not_found(NULL, n->words->first);
|
||||
|
||||
if (onError && !pauseParsing) {
|
||||
onError(e);
|
||||
} else {
|
||||
errorQueue = cmd_error_push(errorQueue, cmd_error_copy(e), errorQueueSize);
|
||||
}
|
||||
|
||||
cmd_error_destroy(e);
|
||||
|
||||
errored = true;
|
||||
}
|
||||
|
||||
n = n->next;
|
||||
}
|
||||
|
||||
line_list_destroy(l);
|
||||
}
|
||||
|
||||
bool SimpleCLI::available() const {
|
||||
return cmdQueue;
|
||||
}
|
||||
|
||||
bool SimpleCLI::errored() const {
|
||||
return errorQueue;
|
||||
}
|
||||
|
||||
bool SimpleCLI::paused() const {
|
||||
return pauseParsing;
|
||||
}
|
||||
|
||||
int SimpleCLI::countCmdQueue() const {
|
||||
cmd* h = cmdQueue;
|
||||
int i = 0;
|
||||
|
||||
while (h) {
|
||||
h = h->next;
|
||||
++i;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int SimpleCLI::countErrorQueue() const {
|
||||
cmd_error* h = errorQueue;
|
||||
int i = 0;
|
||||
|
||||
while (h) {
|
||||
h = h->next;
|
||||
++i;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
Command SimpleCLI::getCmd() {
|
||||
if (!cmdQueue) return Command();
|
||||
|
||||
// "Pop" cmd pointer from queue
|
||||
cmd* ptr = cmdQueue;
|
||||
|
||||
cmdQueue = cmdQueue->next;
|
||||
|
||||
// Create wrapper class and copy cmd
|
||||
Command c(ptr, COMMAND_TEMPORARY);
|
||||
|
||||
// Destroy old cmd from queue
|
||||
cmd_destroy(ptr);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Command SimpleCLI::getCmd(String name) {
|
||||
return getCmd(name.c_str());
|
||||
}
|
||||
|
||||
Command SimpleCLI::getCmd(const char* name) {
|
||||
if (name) {
|
||||
cmd* h = cmdList;
|
||||
|
||||
while (h) {
|
||||
if (cmd_name_equals(h, name, strlen(name), h->case_sensetive) == CMD_NAME_EQUALS) return Command(h);
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
return Command();
|
||||
}
|
||||
|
||||
Command SimpleCLI::getCommand() {
|
||||
return getCmd();
|
||||
}
|
||||
|
||||
Command SimpleCLI::getCommand(String name) {
|
||||
return getCmd(name);
|
||||
}
|
||||
|
||||
Command SimpleCLI::getCommand(const char* name) {
|
||||
return getCmd(name);
|
||||
}
|
||||
|
||||
CommandError SimpleCLI::getError() {
|
||||
if (!errorQueue) return CommandError();
|
||||
|
||||
// "Pop" cmd_error pointer from queue
|
||||
cmd_error* ptr = errorQueue;
|
||||
|
||||
errorQueue = errorQueue->next;
|
||||
|
||||
// Create wrapper class and copy cmd_error
|
||||
CommandError e(ptr, COMMAND_ERROR_TEMPORARY);
|
||||
|
||||
// Destroy old cmd_error from queue
|
||||
cmd_error_destroy(ptr);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void SimpleCLI::addCmd(Command& c) {
|
||||
if (!cmdList) {
|
||||
cmdList = c.cmdPointer;
|
||||
} else {
|
||||
cmd* h = cmdList;
|
||||
|
||||
while (h->next) h = h->next;
|
||||
h->next = c.cmdPointer;
|
||||
}
|
||||
|
||||
c.setCaseSensetive(caseSensetive);
|
||||
c.persistent = true;
|
||||
}
|
||||
|
||||
Command SimpleCLI::addCmd(const char* name, void (* callback)(cmd* c)) {
|
||||
Command c(cmd_create_default(name));
|
||||
|
||||
c.setCallback(callback);
|
||||
addCmd(c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Command SimpleCLI::addBoundlessCmd(const char* name, void (* callback)(cmd* c)) {
|
||||
Command c(cmd_create_boundless(name));
|
||||
|
||||
c.setCallback(callback);
|
||||
addCmd(c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Command SimpleCLI::addSingleArgCmd(const char* name, void (* callback)(cmd* c)) {
|
||||
Command c(cmd_create_single(name));
|
||||
|
||||
c.setCallback(callback);
|
||||
addCmd(c);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
Command SimpleCLI::addCommand(const char* name, void (* callback)(cmd* c)) {
|
||||
return addCmd(name, callback);
|
||||
}
|
||||
|
||||
Command SimpleCLI::addBoundlessCommand(const char* name, void (* callback)(cmd* c)) {
|
||||
return addBoundlessCmd(name, callback);
|
||||
}
|
||||
|
||||
Command SimpleCLI::addSingleArgumentCommand(const char* name, void (* callback)(cmd* c)) {
|
||||
return addSingleArgCmd(name, callback);
|
||||
}
|
||||
|
||||
String SimpleCLI::toString(bool descriptions) const {
|
||||
String s;
|
||||
|
||||
toString(s, descriptions);
|
||||
return s;
|
||||
}
|
||||
|
||||
void SimpleCLI::toString(String& s, bool descriptions) const {
|
||||
cmd* h = cmdList;
|
||||
|
||||
while (h) {
|
||||
Command(h).toString(s, descriptions);
|
||||
if (descriptions) s += "\r\n";
|
||||
s += "\r\n";
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleCLI::setCaseSensetive(bool caseSensetive) {
|
||||
this->caseSensetive = caseSensetive;
|
||||
|
||||
cmd* h = cmdList;
|
||||
|
||||
while (h) {
|
||||
h->case_sensetive = caseSensetive;
|
||||
h = h->next;
|
||||
}
|
||||
}
|
||||
|
||||
void SimpleCLI::setOnError(void (* onError)(cmd_error* e)) {
|
||||
this->onError = onError;
|
||||
}
|
||||
|
||||
void SimpleCLI::setErrorCallback(void (* onError)(cmd_error* e)) {
|
||||
setOnError(onError);
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef SimpleCLI_h
|
||||
#define SimpleCLI_h
|
||||
|
||||
#include "CommandError.h" // CommandError, Command, Argument
|
||||
|
||||
#define SIMPLECLI_VERSION "1.1.1"
|
||||
#define SIMPLECLI_VERSION_MAJOR 1
|
||||
#define SIMPLECLI_VERSION_MINOR 1
|
||||
#define SIMPLECLI_VERSION_REVISION 1
|
||||
|
||||
class SimpleCLI {
|
||||
private:
|
||||
bool caseSensetive { false };
|
||||
bool pauseParsing { false };
|
||||
|
||||
cmd* cmdList { NULL }; // List of accessible commands
|
||||
cmd* cmdQueue { NULL }; // Queue with parsed commands the user has typed in
|
||||
cmd_error* errorQueue { NULL }; // Queue with parser errors
|
||||
|
||||
int commandQueueSize;
|
||||
int errorQueueSize;
|
||||
|
||||
void (* onError)(cmd_error* e) = NULL;
|
||||
|
||||
cmd* getNextCmd(cmd* begin, const char* name, size_t name_len);
|
||||
void parseLine(const char* input, size_t input_len);
|
||||
|
||||
void addCmd(Command& c);
|
||||
|
||||
public:
|
||||
SimpleCLI(int commandQueueSize = 10, int errorQueueSize = 10);
|
||||
~SimpleCLI();
|
||||
|
||||
void pause();
|
||||
void unpause();
|
||||
|
||||
void parse(const String& input);
|
||||
void parse(const char* input);
|
||||
void parse(const char* input, size_t input_len);
|
||||
|
||||
bool available() const;
|
||||
bool errored() const;
|
||||
bool paused() const;
|
||||
|
||||
int countCmdQueue() const;
|
||||
int countErrorQueue() const;
|
||||
|
||||
Command getCmd();
|
||||
Command getCmd(String name);
|
||||
Command getCmd(const char* name);
|
||||
|
||||
Command getCommand();
|
||||
Command getCommand(String name);
|
||||
Command getCommand(const char* name);
|
||||
|
||||
CommandError getError();
|
||||
|
||||
Command addCmd(const char* name, void (* callback)(cmd* c) = NULL);
|
||||
Command addBoundlessCmd(const char* name, void (* callback)(cmd* c) = NULL);
|
||||
Command addSingleArgCmd(const char* name, void (* callback)(cmd* c) = NULL);
|
||||
|
||||
Command addCommand(const char* name, void (* callback)(cmd* c) = NULL);
|
||||
Command addBoundlessCommand(const char* name, void (* callback)(cmd* c) = NULL);
|
||||
Command addSingleArgumentCommand(const char* name, void (* callback)(cmd* c) = NULL);
|
||||
|
||||
String toString(bool descriptions = true) const;
|
||||
void toString(String& s, bool descriptions = true) const;
|
||||
|
||||
void setCaseSensetive(bool caseSensetive = true);
|
||||
void setOnError(void (* onError)(cmd_error* e));
|
||||
void setErrorCallback(void (* onError)(cmd_error* e));
|
||||
};
|
||||
|
||||
#endif // ifndef SimpleCLI_h
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef StringCLI_h
|
||||
#define StringCLI_h
|
||||
|
||||
#if defined(ARDUINO)
|
||||
#include "Arduino.h" // String
|
||||
#else
|
||||
#include <string> // std::string
|
||||
#include <cstring> // strlen()
|
||||
typedef std::string String;
|
||||
inline String F(String s) { return s; };
|
||||
#endif
|
||||
|
||||
#endif // ifndef StringCLI_h
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include "arg.h"
|
||||
|
||||
#include <stdlib.h> // malloc()
|
||||
#include <string.h> // memcpy()
|
||||
|
||||
#include "comparator.h" // compare
|
||||
|
||||
// ===== Arg ===== //
|
||||
|
||||
// Constructors
|
||||
arg* arg_create(const char* name, const char* default_val, unsigned int mode, unsigned int req) {
|
||||
arg* a = (arg*)malloc(sizeof(arg));
|
||||
|
||||
a->name = name;
|
||||
a->default_val = default_val;
|
||||
a->val = NULL;
|
||||
a->mode = mode % 3;
|
||||
a->req = req % 2;
|
||||
a->set = ARG_UNSET;
|
||||
a->next = NULL;
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
arg* arg_create_opt(const char* name, const char* default_val) {
|
||||
return arg_create(name, default_val, ARG_DEFAULT, ARG_OPT);
|
||||
}
|
||||
|
||||
arg* arg_create_req(const char* name) {
|
||||
return arg_create(name, NULL, ARG_DEFAULT, ARG_REQ);
|
||||
}
|
||||
|
||||
arg* arg_create_opt_positional(const char* name, const char* default_value) {
|
||||
return arg_create(name, default_value, ARG_POS, ARG_OPT);
|
||||
}
|
||||
|
||||
arg* arg_create_req_positional(const char* name) {
|
||||
return arg_create(name, NULL, ARG_POS, ARG_REQ);
|
||||
}
|
||||
|
||||
arg* arg_create_flag(const char* name, const char* default_value) {
|
||||
return arg_create(name, default_value, ARG_FLAG, ARG_OPT);
|
||||
}
|
||||
|
||||
// Copy & Move Constructors
|
||||
|
||||
arg* arg_copy(arg* a) {
|
||||
if (!a) return NULL;
|
||||
|
||||
arg* na = (arg*)malloc(sizeof(arg));
|
||||
|
||||
na->name = a->name;
|
||||
na->default_val = a->default_val;
|
||||
na->val = NULL;
|
||||
na->mode = a->mode;
|
||||
na->req = a->req;
|
||||
na->set = a->set;
|
||||
na->next = NULL;
|
||||
|
||||
if (a->val) {
|
||||
na->val = (char*)malloc(strlen(a->val) + 1);
|
||||
strcpy(na->val, a->val);
|
||||
na->set = ARG_SET;
|
||||
}
|
||||
|
||||
return na;
|
||||
}
|
||||
|
||||
arg* arg_copy_rec(arg* a) {
|
||||
if (!a) return NULL;
|
||||
|
||||
arg* na = arg_copy(a);
|
||||
|
||||
na->next = arg_copy_rec(a->next);
|
||||
|
||||
return na;
|
||||
}
|
||||
|
||||
arg* arg_move(arg* a) {
|
||||
if (!a) return NULL;
|
||||
|
||||
arg* na = (arg*)malloc(sizeof(arg));
|
||||
|
||||
na->name = a->name;
|
||||
na->default_val = a->default_val;
|
||||
na->val = a->val;
|
||||
na->mode = a->mode;
|
||||
na->req = a->req;
|
||||
na->set = a->set;
|
||||
na->next = NULL;
|
||||
|
||||
a->val = NULL;
|
||||
a->set = ARG_UNSET;
|
||||
|
||||
return na;
|
||||
}
|
||||
|
||||
arg* arg_move_rec(arg* a) {
|
||||
if (!a) return NULL;
|
||||
|
||||
arg* na = arg_move(a);
|
||||
|
||||
na->next = arg_move_rec(a->next);
|
||||
|
||||
return na;
|
||||
}
|
||||
|
||||
// Destructors
|
||||
arg* arg_destroy(arg* a) {
|
||||
if (a) {
|
||||
arg_reset(a);
|
||||
free(a);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
arg* arg_destroy_rec(arg* a) {
|
||||
if (a) {
|
||||
arg_destroy_rec(a->next);
|
||||
arg_destroy(a);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Reset
|
||||
void arg_reset(arg* a) {
|
||||
if (a) {
|
||||
if (a->val) {
|
||||
free(a->val);
|
||||
a->val = NULL;
|
||||
}
|
||||
a->set = ARG_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
void arg_reset_rec(arg* a) {
|
||||
if (a) {
|
||||
arg_reset(a);
|
||||
arg_reset_rec(a->next);
|
||||
}
|
||||
}
|
||||
|
||||
// Comparisons
|
||||
int arg_name_equals(arg* a, const char* name, size_t name_len, int case_sensetive) {
|
||||
if (!a) return ARG_NAME_UNEQUALS;
|
||||
return compare(name, name_len, a->name, case_sensetive) == COMPARE_EQUAL ? ARG_NAME_EQUALS : ARG_NAME_UNEQUALS;
|
||||
}
|
||||
|
||||
int arg_equals(arg* a, arg* b, int case_sensetive) {
|
||||
if (a == b) return ARG_NAME_EQUALS;
|
||||
if (!a || !b) return ARG_NAME_UNEQUALS;
|
||||
return arg_name_equals(a, b->name, strlen(b->name), case_sensetive);
|
||||
}
|
||||
|
||||
// Getter
|
||||
const char* arg_get_value(arg* a) {
|
||||
if (a) {
|
||||
if (a->val) return a->val;
|
||||
if (a->default_val) return a->default_val;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Setter
|
||||
int arg_set_value(arg* a, const char* val, size_t val_size) {
|
||||
if (a) {
|
||||
if (val && (val_size > 0)) {
|
||||
if (a->set) arg_reset(a);
|
||||
a->val = (char*)malloc(val_size + 1);
|
||||
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
|
||||
int escaped = 0;
|
||||
int in_quote = 0;
|
||||
|
||||
while (i < val_size) {
|
||||
if ((val[i] == '\\') && (escaped == 0)) {
|
||||
escaped = 1;
|
||||
} else if ((val[i] == '"') && (escaped == 0)) {
|
||||
in_quote = !in_quote;
|
||||
} else {
|
||||
a->val[j++] = val[i];
|
||||
escaped = 0;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (in_quote) {
|
||||
free(a->val);
|
||||
a->val = NULL;
|
||||
return ARG_VALUE_FAIL;
|
||||
}
|
||||
|
||||
while (j <= val_size) {
|
||||
a->val[j++] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
a->set = ARG_SET;
|
||||
return ARG_VALUE_SUCCESS;
|
||||
}
|
||||
|
||||
return ARG_VALUE_FAIL;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef arg_h
|
||||
#define arg_h
|
||||
|
||||
#include "arg_types.h" // arg
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
// ===== Arg ===== //
|
||||
|
||||
// Constructors
|
||||
arg* arg_create(const char* name, const char* default_val, unsigned int mode, unsigned int req);
|
||||
arg* arg_create_opt(const char* name, const char* default_val);
|
||||
arg* arg_create_req(const char* name);
|
||||
arg* arg_create_opt_positional(const char* name, const char* default_value);
|
||||
arg* arg_create_req_positional(const char* name);
|
||||
arg* arg_create_flag(const char* name, const char* default_value);
|
||||
|
||||
// Copy & Move Constructors
|
||||
arg* arg_copy(arg* a);
|
||||
arg* arg_copy_rec(arg* a);
|
||||
arg* arg_move(arg* a);
|
||||
arg* arg_move_rec(arg* a);
|
||||
|
||||
// Destructors
|
||||
arg* arg_destroy(arg* a);
|
||||
arg* arg_destroy_rec(arg* a);
|
||||
|
||||
// Reset
|
||||
void arg_reset(arg* a);
|
||||
void arg_reset_rec(arg* a);
|
||||
|
||||
// Comparisons
|
||||
int arg_name_equals(arg* a, const char* name, size_t name_len, int case_sensetive);
|
||||
int arg_equals(arg* a, arg* b, int case_sensetive);
|
||||
|
||||
// Getter
|
||||
const char* arg_get_value(arg* a);
|
||||
|
||||
// Setter
|
||||
int arg_set_value(arg* a, const char* val, size_t val_size);
|
||||
|
||||
#endif /* ifndef arg_h */
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef arg_types_h
|
||||
#define arg_types_h
|
||||
|
||||
#define ARG_DEFAULT 0
|
||||
#define ARG_POS 1
|
||||
#define ARG_FLAG 2
|
||||
|
||||
#define ARG_OPT 0
|
||||
#define ARG_REQ 1
|
||||
|
||||
#define ARG_UNSET 0
|
||||
#define ARG_SET 1
|
||||
|
||||
#define ARG_NAME_UNEQUALS 0
|
||||
#define ARG_NAME_EQUALS 1
|
||||
|
||||
#define ARG_VALUE_FAIL 0
|
||||
#define ARG_VALUE_SUCCESS 1
|
||||
|
||||
typedef struct arg {
|
||||
const char * name;
|
||||
const char * default_val;
|
||||
char * val;
|
||||
unsigned int mode : 2;
|
||||
unsigned int req : 1;
|
||||
unsigned int set : 1;
|
||||
struct arg * next;
|
||||
} arg;
|
||||
|
||||
#endif /* ifndef arg_types_h */
|
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Soruce: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include <stdlib.h> // malloc
|
||||
#include <string.h> // strlen
|
||||
|
||||
#include "comparator.h" // compare
|
||||
|
||||
#include "cmd.h"
|
||||
|
||||
#include "cmd_error.h"
|
||||
#include "arg.h"
|
||||
|
||||
// ===== CMD ===== //
|
||||
|
||||
// CMD Constructors
|
||||
cmd* cmd_create(const char* name, unsigned int mode) {
|
||||
if (!name) return NULL;
|
||||
|
||||
cmd* c = (cmd*)malloc(sizeof(cmd));
|
||||
|
||||
c->name = name;
|
||||
c->mode = mode % 3;
|
||||
c->arg_list = NULL;
|
||||
c->case_sensetive = COMPARE_CASE_INSENSETIVE;
|
||||
c->callback = NULL;
|
||||
c->description = NULL;
|
||||
c->next = NULL;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
cmd* cmd_create_default(const char* name) {
|
||||
return cmd_create(name, CMD_DEFAULT);
|
||||
}
|
||||
|
||||
cmd* cmd_create_boundless(const char* name) {
|
||||
return cmd_create(name, CMD_BOUNDLESS);
|
||||
}
|
||||
|
||||
cmd* cmd_create_single(const char* name) {
|
||||
cmd* c = cmd_create(name, CMD_SINGLE);
|
||||
|
||||
c->arg_list = arg_create_opt_positional(NULL, NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
// Copy & Move Constructors
|
||||
cmd* cmd_copy(cmd* c) {
|
||||
if (!c) return NULL;
|
||||
|
||||
cmd* nc = (cmd*)malloc(sizeof(cmd));
|
||||
|
||||
nc->name = c->name;
|
||||
nc->mode = c->mode;
|
||||
nc->arg_list = arg_copy_rec(c->arg_list);
|
||||
nc->case_sensetive = c->case_sensetive;
|
||||
nc->callback = c->callback;
|
||||
nc->description = c->description;
|
||||
nc->next = NULL;
|
||||
|
||||
return nc;
|
||||
}
|
||||
|
||||
cmd* cmd_copy_rec(cmd* c) {
|
||||
if (!c) return NULL;
|
||||
|
||||
cmd* nc = cmd_copy(c);
|
||||
|
||||
nc->next = cmd_copy_rec(c->next);
|
||||
|
||||
return nc;
|
||||
}
|
||||
|
||||
cmd* cmd_move(cmd* c) {
|
||||
if (!c) return NULL;
|
||||
|
||||
cmd* nc = (cmd*)malloc(sizeof(cmd));
|
||||
|
||||
nc->name = c->name;
|
||||
nc->mode = c->mode;
|
||||
nc->arg_list = arg_move_rec(c->arg_list);
|
||||
nc->case_sensetive = c->case_sensetive;
|
||||
nc->callback = c->callback;
|
||||
nc->description = c->description;
|
||||
nc->next = NULL;
|
||||
|
||||
return nc;
|
||||
}
|
||||
|
||||
cmd* cmd_move_rec(cmd* c) {
|
||||
if (!c) return NULL;
|
||||
|
||||
cmd* nc = cmd_move(c);
|
||||
|
||||
nc->next = cmd_move_rec(c->next);
|
||||
|
||||
return nc;
|
||||
}
|
||||
|
||||
// Destructors
|
||||
cmd* cmd_destroy(cmd* c) {
|
||||
if (c) {
|
||||
arg_destroy_rec(c->arg_list);
|
||||
free(c);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmd* cmd_destroy_rec(cmd* c) {
|
||||
if (c) {
|
||||
cmd_destroy_rec(c->next);
|
||||
cmd_destroy(c);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Push CMD and Push Arg
|
||||
cmd* cmd_push(cmd* l, cmd* c, int max_size) {
|
||||
if (max_size < 1) {
|
||||
cmd_destroy_rec(l);
|
||||
cmd_destroy(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!l) return c;
|
||||
|
||||
cmd* h = l;
|
||||
int i = 1;
|
||||
|
||||
while (h->next) {
|
||||
h = h->next;
|
||||
++i;
|
||||
}
|
||||
|
||||
h->next = c;
|
||||
|
||||
// Remove first element if list is too big
|
||||
if (i > max_size) {
|
||||
cmd* ptr = l;
|
||||
l = l->next;
|
||||
cmd_destroy(ptr);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
cmd* cmd_add_arg(cmd* c, arg* a) {
|
||||
if (c && a) {
|
||||
arg* h = c->arg_list;
|
||||
|
||||
if (!h) {
|
||||
c->arg_list = a;
|
||||
} else {
|
||||
while (h->next) h = h->next;
|
||||
h->next = a;
|
||||
}
|
||||
|
||||
a->next = NULL;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
// Reset CMD
|
||||
void cmd_reset(cmd* c) {
|
||||
if (c) {
|
||||
if (c->mode == CMD_BOUNDLESS) {
|
||||
arg_destroy_rec(c->arg_list);
|
||||
c->arg_list = NULL;
|
||||
} else {
|
||||
arg_reset_rec(c->arg_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_reset_rec(cmd* c) {
|
||||
if (c) {
|
||||
cmd_reset(c);
|
||||
cmd_reset_rec(c->next);
|
||||
}
|
||||
}
|
||||
|
||||
// Comparisons
|
||||
int cmd_name_equals(cmd* c, const char* name, size_t name_len, int case_sensetive) {
|
||||
if (!c || !name) return CMD_NAME_UNEQUALS;
|
||||
if (c->name == name) return CMD_NAME_EQUALS;
|
||||
return compare(name, name_len, c->name, case_sensetive) == COMPARE_EQUAL ? CMD_NAME_EQUALS : CMD_NAME_UNEQUALS;
|
||||
}
|
||||
|
||||
int cmd_equals(cmd* a, cmd* b, int case_sensetive) {
|
||||
if (!a || !b) return CMD_NAME_UNEQUALS;
|
||||
if (a == b) return CMD_NAME_EQUALS;
|
||||
return cmd_name_equals(a, b->name, strlen(b->name), case_sensetive);
|
||||
}
|
||||
|
||||
// Parser
|
||||
cmd_error* cmd_parse(cmd* c, line_node* n) {
|
||||
if (!c || !n) return cmd_error_create_null_ptr(c);
|
||||
if (!n->words || (n->words->size == 0) || !n->words->first) return cmd_error_create_empty_line(c);
|
||||
|
||||
word_list* wl = n->words;
|
||||
word_node* cmd_name = wl->first;
|
||||
word_node* first_arg = cmd_name->next;
|
||||
|
||||
// Check if name equals command name
|
||||
if (compare(cmd_name->str, cmd_name->len, c->name, c->case_sensetive) == COMPARE_UNEQUAL) return cmd_error_create_not_found(c, cmd_name);
|
||||
|
||||
// When command boundless, set all words as anonymous args
|
||||
if (c->mode == CMD_BOUNDLESS) {
|
||||
// Delete all old args
|
||||
arg_destroy_rec(c->arg_list);
|
||||
c->arg_list = NULL;
|
||||
|
||||
// Fill command with an anonymous arg for each word
|
||||
word_node* w = first_arg;
|
||||
|
||||
while (w) {
|
||||
arg* a = arg_create_req_positional(NULL);
|
||||
arg_set_value(a, w->str, w->len);
|
||||
cmd_add_arg(c, a);
|
||||
w = w->next;
|
||||
}
|
||||
|
||||
return cmd_error_create_parse_success(c);
|
||||
}
|
||||
|
||||
// When command single-arg, set full string as first arg
|
||||
if (c->mode == CMD_SINGLE) {
|
||||
if (!c->arg_list) c->arg_list = arg_create_opt_positional(NULL, NULL);
|
||||
if (wl->size > 1) arg_set_value(c->arg_list, first_arg->str, n->len - cmd_name->len - 1);
|
||||
return cmd_error_create_parse_success(c);
|
||||
}
|
||||
|
||||
// Go through all words and try to find a matching arg
|
||||
word_node* w = first_arg;
|
||||
word_node* nw = w ? w->next : NULL;
|
||||
|
||||
while (w) {
|
||||
// Look for arg which matches the word name
|
||||
arg* a = c->arg_list;
|
||||
char prefix = w->str[0];
|
||||
|
||||
while (a) {
|
||||
if (a->set == ARG_UNSET) {
|
||||
if ((prefix != '-') && (a->mode == ARG_POS)) break;
|
||||
if ((prefix == '-') && (compare(&w->str[1], w->len - 1, a->name, c->case_sensetive) == COMPARE_EQUAL)) break;
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
|
||||
// No mathing arg found
|
||||
if (!a) return cmd_error_create_unknown_arg(c, w);
|
||||
|
||||
switch (a->mode) {
|
||||
// Anonym, Template Arg -> value = value
|
||||
case ARG_POS:
|
||||
if (prefix != '-') {
|
||||
arg_set_value(a, w->str, w->len);
|
||||
break;
|
||||
}
|
||||
|
||||
// Default Arg -> value in next word
|
||||
case ARG_DEFAULT:
|
||||
if (!nw) return cmd_error_create_missing_arg(c, a);
|
||||
if (arg_set_value(a, nw->str, nw->len) == ARG_VALUE_FAIL) return cmd_error_create_unclosed_quote(c, a, nw);
|
||||
w = w->next;
|
||||
nw = w ? w->next : NULL;
|
||||
break;
|
||||
|
||||
// Empty Arg -> no value
|
||||
case ARG_FLAG:
|
||||
arg_set_value(a, NULL, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
// Next word
|
||||
if (w) {
|
||||
w = w->next;
|
||||
nw = w ? w->next : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all required args have been set
|
||||
arg* a = c->arg_list;
|
||||
|
||||
while (a) {
|
||||
if (a->req && !a->set) {
|
||||
return cmd_error_create_missing_arg(c, a);
|
||||
}
|
||||
a = a->next;
|
||||
}
|
||||
|
||||
return cmd_error_create_parse_success(c);
|
||||
}
|
||||
|
||||
// Getter
|
||||
const char* cmd_get_description(cmd* c) {
|
||||
if (!c) return NULL;
|
||||
return c->description;
|
||||
}
|
||||
|
||||
// Setter
|
||||
void cmd_set_description(cmd* c, const char* description) {
|
||||
if (c) {
|
||||
c->description = description;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Soruce: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef cmd_h
|
||||
#define cmd_h
|
||||
|
||||
#include "cmd_types.h" // cmd, cmd_parse_error
|
||||
#include "parser_types.h" // line_node, word_list, word_node, ...
|
||||
#include "cmd_error_types.h" // cmd_error
|
||||
|
||||
// ===== CMD ===== //
|
||||
|
||||
// Constructors
|
||||
cmd* cmd_create(const char* name, unsigned int mode);
|
||||
cmd* cmd_create_default(const char* name);
|
||||
cmd* cmd_create_boundless(const char* name);
|
||||
cmd* cmd_create_single(const char* name);
|
||||
|
||||
// Copy & Move Constructors
|
||||
cmd* cmd_copy(cmd* c);
|
||||
cmd* cmd_copy_rec(cmd* c);
|
||||
cmd* cmd_move(cmd* c);
|
||||
cmd* cmd_move_rec(cmd* c);
|
||||
|
||||
// Destructors
|
||||
cmd* cmd_destroy(cmd* c);
|
||||
cmd* cmd_destroy_rec(cmd* c);
|
||||
|
||||
// Push CMD and Push Arg
|
||||
cmd* cmd_push(cmd* l, cmd* c, int max_size);
|
||||
cmd* cmd_add_arg(cmd* c, arg* a);
|
||||
|
||||
// Reset CMD
|
||||
void cmd_reset(cmd* c);
|
||||
void cmd_reset_rec(cmd* c);
|
||||
|
||||
// Comparisons
|
||||
int cmd_name_equals(cmd* c, const char* name, size_t name_len, int case_sensetive);
|
||||
int cmd_equals(cmd* a, cmd* b, int case_sensetive);
|
||||
|
||||
// Parser
|
||||
cmd_error* cmd_parse(cmd* c, line_node* n);
|
||||
|
||||
// Getter
|
||||
const char* cmd_get_description(cmd* c);
|
||||
|
||||
// Setter
|
||||
void cmd_set_description(cmd* c, const char* description);
|
||||
|
||||
#endif /* ifndef cmd_h */
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include "cmd_error.h"
|
||||
#include <stdlib.h> // malloc, memcpy
|
||||
#include <string.h> // strlen, strcpy
|
||||
|
||||
// ===== CMD Parse Error ===== //
|
||||
|
||||
// Constructors
|
||||
cmd_error* cmd_error_create(int mode, cmd* command, arg* argument, word_node* data) {
|
||||
cmd_error* e = (cmd_error*)malloc(sizeof(cmd_error));
|
||||
|
||||
e->mode = mode;
|
||||
e->command = command;
|
||||
e->argument = argument;
|
||||
e->data = NULL;
|
||||
e->next = NULL;
|
||||
|
||||
if (data && (data->len > 0)) {
|
||||
e->data = (char*)malloc(data->len + 1);
|
||||
memcpy(e->data, data->str, data->len);
|
||||
e->data[data->len] = '\0';
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
cmd_error* cmd_error_create_null_ptr(cmd* c) {
|
||||
return cmd_error_create(CMD_NULL_PTR, c, NULL, NULL);
|
||||
};
|
||||
|
||||
cmd_error* cmd_error_create_empty_line(cmd* c) {
|
||||
return cmd_error_create(CMD_EMPTY_LINE, c, NULL, NULL);
|
||||
}
|
||||
|
||||
cmd_error* cmd_error_create_parse_success(cmd* c) {
|
||||
return cmd_error_create(CMD_PARSE_SUCCESS, c, NULL, NULL);
|
||||
}
|
||||
|
||||
cmd_error* cmd_error_create_not_found(cmd* c, word_node* cmd_name) {
|
||||
return cmd_error_create(CMD_NOT_FOUND, c, NULL, cmd_name);
|
||||
}
|
||||
|
||||
cmd_error* cmd_error_create_unknown_arg(cmd* c, word_node* arg_name) {
|
||||
return cmd_error_create(CMD_UNKOWN_ARG, c, NULL, arg_name);
|
||||
}
|
||||
|
||||
cmd_error* cmd_error_create_missing_arg(cmd* c, arg* a) {
|
||||
return cmd_error_create(CMD_MISSING_ARG, c, a, NULL);
|
||||
}
|
||||
|
||||
cmd_error* cmd_error_create_unclosed_quote(cmd* c, arg* a, word_node* arg_value) {
|
||||
return cmd_error_create(CMD_UNCLOSED_QUOTE, c, a, arg_value);
|
||||
}
|
||||
|
||||
// Copy Constructors
|
||||
cmd_error* cmd_error_copy(cmd_error* e) {
|
||||
if (!e) return NULL;
|
||||
|
||||
cmd_error* ne = (cmd_error*)malloc(sizeof(cmd_error));
|
||||
|
||||
ne->mode = e->mode;
|
||||
ne->command = e->command;
|
||||
ne->argument = e->argument;
|
||||
ne->data = e->data;
|
||||
ne->next = e->next;
|
||||
|
||||
if (ne->data) {
|
||||
ne->data = (char*)malloc(strlen(e->data) + 1);
|
||||
strcpy(ne->data, e->data);
|
||||
}
|
||||
|
||||
return ne;
|
||||
}
|
||||
|
||||
cmd_error* cmd_error_copy_rec(cmd_error* e) {
|
||||
if (!e) return NULL;
|
||||
|
||||
cmd_error* ne = cmd_error_copy(e);
|
||||
|
||||
ne->next = cmd_error_copy_rec(e->next);
|
||||
|
||||
return ne;
|
||||
}
|
||||
|
||||
// Destructors
|
||||
cmd_error* cmd_error_destroy(cmd_error* e) {
|
||||
if (e) {
|
||||
if (e->data) free(e->data);
|
||||
free(e);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmd_error* cmd_error_destroy_rec(cmd_error* e) {
|
||||
if (e) {
|
||||
cmd_error_destroy_rec(e->next);
|
||||
cmd_error_destroy(e);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Push
|
||||
cmd_error* cmd_error_push(cmd_error* l, cmd_error* e, int max_size) {
|
||||
if (max_size < 1) {
|
||||
cmd_error_destroy_rec(l);
|
||||
cmd_error_destroy(e);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!l) return e;
|
||||
|
||||
cmd_error* h = l;
|
||||
int i = 1;
|
||||
|
||||
while (h->next) {
|
||||
h = h->next;
|
||||
++i;
|
||||
}
|
||||
|
||||
h->next = e;
|
||||
|
||||
// Remove first element if list is too big
|
||||
if (i > max_size) {
|
||||
cmd_error* ptr = l;
|
||||
l = l->next;
|
||||
cmd_error_destroy(ptr);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef cmd_error_h
|
||||
#define cmd_error_h
|
||||
|
||||
#include "cmd_error_types.h" // cmd_error
|
||||
|
||||
// ===== CMD Parse Error ===== //
|
||||
|
||||
// Constructors
|
||||
cmd_error* cmd_error_create(int mode, cmd* command, arg* argument, word_node* data);
|
||||
cmd_error* cmd_error_create_null_ptr(cmd* c);
|
||||
cmd_error* cmd_error_create_empty_line(cmd* c);
|
||||
cmd_error* cmd_error_create_parse_success(cmd* c);
|
||||
cmd_error* cmd_error_create_not_found(cmd* c, word_node* cmd_name);
|
||||
cmd_error* cmd_error_create_unknown_arg(cmd* c, word_node* arg_name);
|
||||
cmd_error* cmd_error_create_missing_arg(cmd* c, arg* a);
|
||||
cmd_error* cmd_error_create_unclosed_quote(cmd* c, arg* a, word_node* arg_value);
|
||||
|
||||
// Copy Constructors
|
||||
cmd_error* cmd_error_copy(cmd_error* e);
|
||||
cmd_error* cmd_error_copy_rec(cmd_error* e);
|
||||
|
||||
// Destructors
|
||||
cmd_error* cmd_error_destroy(cmd_error* e);
|
||||
cmd_error* cmd_error_destroy_rec(cmd_error* e);
|
||||
|
||||
// Push
|
||||
cmd_error* cmd_error_push(cmd_error* l, cmd_error* e, int max_size);
|
||||
|
||||
#endif /* ifndef cmd_error_h */
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef cmd_error_types_h
|
||||
#define cmd_error_types_h
|
||||
|
||||
#include "cmd_types.h" // cmd, arg
|
||||
#include "parser_types.h" // word_node
|
||||
|
||||
#define CMD_NULL_PTR -2
|
||||
#define CMD_EMPTY_LINE -1
|
||||
#define CMD_PARSE_SUCCESS 0
|
||||
#define CMD_NOT_FOUND 1
|
||||
#define CMD_UNKOWN_ARG 2
|
||||
#define CMD_MISSING_ARG 3
|
||||
#define CMD_MISSING_ARG_VALUE 4
|
||||
#define CMD_UNCLOSED_QUOTE 5
|
||||
|
||||
typedef struct cmd_error {
|
||||
int mode;
|
||||
cmd * command;
|
||||
arg * argument;
|
||||
char * data;
|
||||
struct cmd_error* next;
|
||||
} cmd_error;
|
||||
|
||||
#endif /* ifndef cmd_error_types_h */
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef cmd_types_h
|
||||
#define cmd_types_h
|
||||
|
||||
#include "arg_types.h" // arg
|
||||
|
||||
#define CMD_DEFAULT 0
|
||||
#define CMD_BOUNDLESS 1
|
||||
#define CMD_SINGLE 2
|
||||
|
||||
#define CMD_NAME_UNEQUALS 0
|
||||
#define CMD_NAME_EQUALS 1
|
||||
|
||||
#define CMD_CASE_INSENSETIVE 0
|
||||
#define CMD_CASE_SENSETIVE 1
|
||||
|
||||
typedef struct cmd {
|
||||
const char * name;
|
||||
unsigned int mode : 2;
|
||||
struct arg * arg_list;
|
||||
unsigned int case_sensetive : 1;
|
||||
void (* callback)(struct cmd* c);
|
||||
const char* description;
|
||||
struct cmd* next;
|
||||
} cmd;
|
||||
|
||||
#endif /* ifndef cmd_types_h */
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include <string.h> // strlen
|
||||
|
||||
#include "comparator.h"
|
||||
|
||||
// My own implementation, because the default one in ctype.h make problems on older ESP8266 SDKs
|
||||
char to_lower(char c) {
|
||||
if ((c >= 65) && (c <= 90)) {
|
||||
return (char)(c + 32);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int compare(const char* user_str, size_t user_str_len, const char* templ_str, int case_sensetive) {
|
||||
if (user_str == templ_str) return COMPARE_EQUAL;
|
||||
|
||||
// null check string pointers
|
||||
if (!user_str || !templ_str) return COMPARE_UNEQUAL;
|
||||
|
||||
// string lengths
|
||||
size_t str_len = user_str_len; // strlen(user_str);
|
||||
size_t key_len = strlen(templ_str);
|
||||
|
||||
// when same length, it there is no need to check for slashes or commas
|
||||
if (str_len == key_len) {
|
||||
for (size_t i = 0; i < key_len; i++) {
|
||||
if (case_sensetive == COMPARE_CASE_SENSETIVE) {
|
||||
if (user_str[i] != templ_str[i]) return COMPARE_UNEQUAL;
|
||||
} else {
|
||||
if (to_lower(user_str[i]) != to_lower(templ_str[i])) return COMPARE_UNEQUAL;
|
||||
}
|
||||
}
|
||||
return COMPARE_EQUAL;
|
||||
}
|
||||
|
||||
// string can't be longer than templ_str (but can be smaller because of '/' and ',')
|
||||
if (str_len > key_len) return COMPARE_UNEQUAL;
|
||||
|
||||
unsigned int res_i = 0;
|
||||
unsigned int a = 0;
|
||||
unsigned int b = 0;
|
||||
unsigned int res = 1;
|
||||
|
||||
while (a < str_len && b < key_len) {
|
||||
if (templ_str[b] == '/') {
|
||||
// skip slash in templ_str
|
||||
++b;
|
||||
} else if (templ_str[b] == ',') {
|
||||
// on comma increment res_i and reset str-index
|
||||
++b;
|
||||
a = 0;
|
||||
++res_i;
|
||||
}
|
||||
|
||||
// compare character
|
||||
if (case_sensetive == COMPARE_CASE_SENSETIVE) {
|
||||
if (user_str[a] != templ_str[b]) res = 0;
|
||||
} else {
|
||||
if (to_lower(user_str[a]) != to_lower(templ_str[b])) res = 0;
|
||||
}
|
||||
|
||||
// comparison incorrect or string checked until the end and templ_str not checked until the end
|
||||
if (!res || ((a == str_len - 1) &&
|
||||
(templ_str[b + 1] != ',') &&
|
||||
(templ_str[b + 1] != '/') &&
|
||||
(templ_str[b + 1] != '\0'))) {
|
||||
// fast forward to next comma
|
||||
while (b < key_len && templ_str[b] != ',') b++;
|
||||
res = 1;
|
||||
} else {
|
||||
// otherwise icrement indices
|
||||
++a;
|
||||
++b;
|
||||
}
|
||||
}
|
||||
|
||||
// comparison correct AND string checked until the end AND templ_str checked until the end
|
||||
if (res && (a == str_len) &&
|
||||
((templ_str[b] == ',') ||
|
||||
(templ_str[b] == '/') ||
|
||||
(templ_str[b] == '\0'))) return COMPARE_EQUAL; // res_i
|
||||
|
||||
return COMPARE_UNEQUAL;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef comparator_h
|
||||
#define comparator_h
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
#define COMPARE_UNEQUAL 0
|
||||
#define COMPARE_EQUAL 1
|
||||
|
||||
#define COMPARE_CASE_INSENSETIVE 0
|
||||
#define COMPARE_CASE_SENSETIVE 1
|
||||
|
||||
int compare(const char* user_str, size_t user_str_len, const char* templ_str, int case_sensetive);
|
||||
|
||||
#endif /* ifndef comparator_h */
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
#include <stdlib.h> // malloc
|
||||
|
||||
// ===== Word Node ===== //
|
||||
word_node* word_node_create(const char* str, size_t len) {
|
||||
word_node* n = (word_node*)malloc(sizeof(word_node));
|
||||
|
||||
n->str = str;
|
||||
n->len = len;
|
||||
n->next = NULL;
|
||||
return n;
|
||||
}
|
||||
|
||||
word_node* word_node_destroy(word_node* n) {
|
||||
if (n) {
|
||||
free(n);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
word_node* word_node_destroy_rec(word_node* n) {
|
||||
if (n) {
|
||||
word_node_destroy_rec(n->next);
|
||||
word_node_destroy(n);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ===== Word List ===== //
|
||||
word_list* word_list_create() {
|
||||
word_list* l = (word_list*)malloc(sizeof(word_list));
|
||||
|
||||
l->first = NULL;
|
||||
l->last = NULL;
|
||||
l->size = 0;
|
||||
return l;
|
||||
}
|
||||
|
||||
word_list* word_list_destroy(word_list* l) {
|
||||
if (l) {
|
||||
word_node_destroy_rec(l->first);
|
||||
free(l);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void word_list_push(word_list* l, word_node* n) {
|
||||
if (l && n) {
|
||||
if (l->last) {
|
||||
l->last->next = n;
|
||||
} else {
|
||||
l->first = n;
|
||||
}
|
||||
|
||||
l->last = n;
|
||||
++(l->size);
|
||||
}
|
||||
}
|
||||
|
||||
word_node* word_list_get(word_list* l, size_t i) {
|
||||
if (!l) return NULL;
|
||||
|
||||
size_t j;
|
||||
word_node* h = l->first;
|
||||
|
||||
for (j = 0; j < i && h; ++j) {
|
||||
h = h->next;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
// ===== Line Node ==== //
|
||||
line_node* line_node_create(const char* str, size_t len) {
|
||||
line_node* n = (line_node*)malloc(sizeof(line_node));
|
||||
|
||||
n->str = str;
|
||||
n->len = len;
|
||||
n->words = NULL;
|
||||
n->next = NULL;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
word_node* line_node_destroy(line_node* n) {
|
||||
if (n) {
|
||||
word_list_destroy(n->words);
|
||||
free(n);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
word_node* line_node_destroy_rec(line_node* n) {
|
||||
if (n) {
|
||||
line_node_destroy_rec(n->next);
|
||||
line_node_destroy(n);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ===== Line List ===== //
|
||||
line_list* line_list_create() {
|
||||
line_list* l = (line_list*)malloc(sizeof(line_list));
|
||||
|
||||
l->first = NULL;
|
||||
l->last = NULL;
|
||||
l->size = 0;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
line_list* line_list_destroy(line_list* l) {
|
||||
if (l) {
|
||||
line_node_destroy_rec(l->first);
|
||||
free(l);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void line_list_push(line_list* l, line_node* n) {
|
||||
if (l && n) {
|
||||
if (l->last) {
|
||||
l->last->next = n;
|
||||
} else {
|
||||
l->first = n;
|
||||
}
|
||||
|
||||
l->last = n;
|
||||
|
||||
++(l->size);
|
||||
}
|
||||
}
|
||||
|
||||
line_node* line_list_get(line_list* l, size_t i) {
|
||||
if (!l) return NULL;
|
||||
|
||||
size_t j;
|
||||
line_node* h = l->first;
|
||||
|
||||
for (j = 0; j < i && h; ++j) {
|
||||
h = h->next;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
// ===== Parser ===== //
|
||||
word_list* parse_words(const char* str, size_t len) {
|
||||
word_list* l = word_list_create();
|
||||
|
||||
if (len == 0) return l;
|
||||
|
||||
// Go through string and look for space to split it into words
|
||||
word_node* n = NULL;
|
||||
|
||||
size_t i = 0; // current index
|
||||
size_t j = 0; // start index of word
|
||||
|
||||
int escaped = 0;
|
||||
int ignore_space = 0;
|
||||
|
||||
for (i = 0; i <= len; ++i) {
|
||||
if ((str[i] == '\\') && (escaped == 0)) {
|
||||
escaped = 1;
|
||||
} else if ((str[i] == '"') && (escaped == 0)) {
|
||||
ignore_space = !ignore_space;
|
||||
} else if ((i == len) || ((str[i] == ' ') && (ignore_space == 0) && (escaped == 0))) {
|
||||
size_t k = i - j; // length of word
|
||||
|
||||
// for every word, add to list
|
||||
if (k > 0) {
|
||||
n = word_node_create(&str[j], k);
|
||||
word_list_push(l, n);
|
||||
}
|
||||
|
||||
j = i + 1; // reset start index of word
|
||||
} else if (escaped == 1) {
|
||||
escaped = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
line_list* parse_lines(const char* str, size_t len) {
|
||||
line_list* l = line_list_create();
|
||||
|
||||
if (len == 0) return l;
|
||||
|
||||
// Go through string and look for /r and /n to split it into lines
|
||||
line_node* n = NULL;
|
||||
|
||||
size_t i = 0; // current index
|
||||
size_t j = 0; // start index of line
|
||||
|
||||
int ignore_delimiter = 0;
|
||||
int delimiter = 0;
|
||||
int linebreak = 0;
|
||||
int endofline = 0;
|
||||
|
||||
for (i = 0; i <= len; ++i) {
|
||||
if ((str[i] == '"') && ((str[i-1] != '\\') || (i==0))) ignore_delimiter = !ignore_delimiter;
|
||||
|
||||
delimiter = (str[i] == ';' && str[i+1] == ';' && !ignore_delimiter && (i == 0 || str[i-1] != '\\'));
|
||||
linebreak = ((str[i] == '\r') || (str[i] == '\n')) && !ignore_delimiter;
|
||||
endofline = (i == len);
|
||||
|
||||
if (linebreak || endofline || delimiter) {
|
||||
size_t k = i - j; // length of line
|
||||
|
||||
// for every line, parse_words and add to list
|
||||
if (k > 0) {
|
||||
n = line_node_create(&str[j], k);
|
||||
n->words = parse_words(&str[j], k);
|
||||
line_list_push(l, n);
|
||||
}
|
||||
|
||||
if (delimiter) ++i;
|
||||
|
||||
j = i+1; // reset start index of line
|
||||
}
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef parser_h
|
||||
#define parser_h
|
||||
|
||||
#include "parser_types.h"
|
||||
|
||||
// ===== Word Node ===== //
|
||||
word_node* word_node_create(const char* str, size_t len);
|
||||
word_node* word_node_destroy(word_node* n);
|
||||
word_node* word_node_destroy_rec(word_node* n);
|
||||
|
||||
// ===== Word List ===== //
|
||||
word_list* word_list_create();
|
||||
word_list* word_list_destroy(word_list* l);
|
||||
|
||||
void word_list_push(word_list* l, word_node* n);
|
||||
word_node* word_list_get(word_list* l, size_t i);
|
||||
|
||||
// ===== Line Node ==== //
|
||||
line_node* line_node_create(const char* str, size_t len);
|
||||
word_node* line_node_destroy(line_node* n);
|
||||
word_node* line_node_destroy_rec(line_node* n);
|
||||
|
||||
// ===== Line List ===== //
|
||||
line_list* line_list_create();
|
||||
line_list* line_list_destroy(line_list* l);
|
||||
|
||||
void line_list_push(line_list* l, line_node* n);
|
||||
line_node* line_list_get(line_list* l, size_t i);
|
||||
|
||||
// ===== Parser ===== //
|
||||
word_list* parse_words(const char* str, size_t len);
|
||||
line_list* parse_lines(const char* str, size_t len);
|
||||
|
||||
#endif /* ifndef parser_h */
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright (c) 2019 Stefan Kremser
|
||||
This software is licensed under the MIT License. See the license file for details.
|
||||
Source: github.com/spacehuhn/SimpleCLI
|
||||
*/
|
||||
|
||||
#ifndef parser_types_h
|
||||
#define parser_types_h
|
||||
|
||||
#include <stddef.h> // size_t
|
||||
|
||||
typedef struct word_node {
|
||||
const char * str;
|
||||
size_t len;
|
||||
struct word_node* next;
|
||||
} word_node;
|
||||
|
||||
typedef struct word_list {
|
||||
struct word_node* first;
|
||||
struct word_node* last;
|
||||
size_t size;
|
||||
} word_list;
|
||||
|
||||
typedef struct line_node {
|
||||
const char * str;
|
||||
size_t len;
|
||||
struct word_list* words;
|
||||
struct line_node* next;
|
||||
} line_node;
|
||||
|
||||
typedef struct line_list {
|
||||
struct line_node* first;
|
||||
struct line_node* last;
|
||||
size_t size;
|
||||
} line_list;
|
||||
|
||||
#endif /* ifndef parser_types_h */
|
Loading…
Reference in New Issue