Included SimpleCLI library

This commit is contained in:
Spacehuhn 2021-01-11 21:20:09 +01:00
parent ffc01d9a2a
commit 1b95cda089
25 changed files with 2579 additions and 1 deletions

2
.gitignore vendored
View File

@ -5,3 +5,5 @@ build/*
*.map
.DS_Store
utils/web_converter/css_html_js_minify/__pycache__/

View File

@ -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"

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 */