From: hama Date: Sun, 29 Nov 2015 22:42:12 +0000 (+0100) Subject: dayly work X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=a5eaf32b0bfedd214b3322304085650deb1e8d88;p=reqt dayly work --- diff --git a/appl/reidos/maincmdline.cpp b/appl/reidos/maincmdline.cpp new file mode 100644 index 0000000..38b203c --- /dev/null +++ b/appl/reidos/maincmdline.cpp @@ -0,0 +1,20 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. +*/ + + +#include "reidoscmdline.hpp" +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QString startDir = argc > 1 ? argv[1] : ""; + QString homeDir = argc > 2 ? argv[2] : ""; + + return a.exec(); +} diff --git a/appl/reidos/reidoscmdline.hpp b/appl/reidos/reidoscmdline.hpp new file mode 100644 index 0000000..c725455 --- /dev/null +++ b/appl/reidos/reidoscmdline.hpp @@ -0,0 +1,17 @@ +/* + * Licence: + * You can use and modify this file without any restriction. + * There is no warranty. + * You also can use the licence from http://www.wtfpl.net/. + * The original sources can be found on https://github.com/republib. +*/ + + +#ifndef REIDOSCMDLINE_HPP +#define REIDOSCMDLINE_HPP +#include "base/rebase.hpp" +#include "os/reos.hpp" +#include "gui/regui.hpp" + +#endif // REIDOSCMDLINE_HPP + diff --git a/appl/reidos/reidoscmdline.pro b/appl/reidos/reidoscmdline.pro new file mode 100644 index 0000000..b134310 --- /dev/null +++ b/appl/reidos/reidoscmdline.pro @@ -0,0 +1,44 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2015-09-14T18:00:57 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = reidos +TEMPLATE = app + +INCLUDEPATH += . ../.. + +SOURCES += \ + ../../gui/ReEdit.cpp \ + ../../gui/ReStateStorage.cpp \ + ../../gui/ReSettings.cpp \ + ../../gui/ReFileTree.cpp \ + ../../gui/ReGuiValidator.cpp \ + ../../base/ReMatcher.cpp \ + ../../base/ReFile.cpp \ + ../../base/ReStringUtils.cpp \ + ../../base/ReRandomizer.cpp \ + ../../os/ReFileSystem.cpp \ + ../../os/ReCryptFileSystem.cpp \ + ../../base/ReLogger.cpp \ + ../../base/ReQStringUtils.cpp \ + ../../base/ReFileUtils.cpp \ + ../../base/ReException.cpp \ + ../../base/ReProgramArgs.cpp \ + maincmdline.cpp + +HEADERS += ../../base/rebase.hpp \ + ../../gui/regui.hpp \ + ../../gui/ReEdit.hpp \ + ../../gui/ReStateStorage.hpp \ + ../../gui/ReSettings.hpp \ + ../../base/ReStringUtils.hpp \ + ../../base/ReQStringUtils.hpp \ + ../../base/ReException.hpp \ + ../../os/reos.hpp + reidoscmdline.hpp diff --git a/base/ReProgramArgs.cpp b/base/ReProgramArgs.cpp new file mode 100644 index 0000000..5e90c2c --- /dev/null +++ b/base/ReProgramArgs.cpp @@ -0,0 +1,648 @@ +/* + * ReProgramArgs.cpp + * + * License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * You also can use this license: http://www.wtfpl.net + * The latest sources: https://github.com/republib + */ + +#include "base/rebase.hpp" + +const char* ReProgramArgs::PREFIX_LINE_OPTION = " "; + +/** @brief Constructor. + * + * @param caller the object which throw the exception. + * @param message the error message with placeholders like sprintf() + * @param ... + */ +ReOptionException::ReOptionException(ReProgramArgs* caller, const char* format, + ...) : + ReException() { + char buffer[64*1024]; + va_list ap; + va_start(ap, format); + qvsnprintf(buffer, sizeof buffer, format, ap); + va_end(ap); + m_message = buffer; + if (caller != NULL) + caller->setLastError(m_message); +} + +/** @brief Constructor. + * + * @param caller the object which throw the exception + * @param message the error message +*/ +ReOptionException::ReOptionException(ReProgramArgs* caller, const QString& message) : + ReException() +{ + m_message = message.toUtf8(); + if (caller != NULL) + caller->setLastError(m_message); +} + +/** @brief Constructor. + * + * @param usage a string array with the description of the usage. + * Every string will be issued in a separate line + * @param examples a string with one ore more calling examples. + * Every string will be issued in a separate line + */ +ReProgramArgs::ReProgramArgs(const char* usageList[], const char* examples[]) : + m_usage(), + m_examples(), + m_options(), + m_args(NULL), + m_argCount(0), + m_program(NULL), + m_lastError(ReStringUtils::m_empty) { + QByteArray line; + for (const char** argv = usageList; *argv != NULL; argv++) { + line = *argv; + ReStringUtils::chomp(line); + m_usage.append(line.constData()); + } + if (examples != NULL) { + for (const char** argv = examples; *argv != NULL; argv++) { + if (strncmp(*argv, "$0", 2) != 0) + m_examples.append(*argv); + else { + QByteArray line; + line.append(m_program, -1); + m_examples.append(line.constData() + 2); + } + } + } +} + +/** @brief Constructor. + * + * @param usage a string with the description of the usage + * It may contain '\\n' for separate lines + * @param examples a string with one ore more calling examples. + * It may contain '\\n' for separate lines + */ +ReProgramArgs::ReProgramArgs(const char* usageString, const char* examples) : + m_usage(), + m_examples(), + m_options(), + m_args(NULL), + m_argCount(0), + m_program("?"), + m_lastError() { + m_usage = QByteArray(usageString).split('\n'); + if (examples != NULL) { + if (strstr(examples, "$0") == NULL) + m_examples = QByteArray(examples).split('\n'); + else { + QByteArray line; + line = examples; + line.replace("$0", m_program); + m_examples = line.split('\n'); + } + + } +} +/** @brief Destructor. + */ +ReProgramArgs::~ReProgramArgs() { +} + +/** + * Sets the usage message. + * + * @param usage a vector of lines without '\n' + */ +void ReProgramArgs::setUsage(const char* usage[]) { + m_usage.clear(); + for (int ix = 0; usage[ix] != NULL; ix++) + m_usage.append(usage[ix]); +} +/** @brief Puts the property infos into the property string. + * + * The property string is a string stored in the hashlist. + * It contains all infos about the option but the current value. + * + * @param name the name of the option. Used in the methods getInt(), ... + * @param description a short description of the option. Used in the user messages + * @param shortOpt the one character option identifier. Used in the arguments. Must be preceded by '-' + * @param longOpt the multi character option identifier. Used in the arguments. Must be preceded by '--' + * @param dataType the data type of the option: DT_INT, DT_BOOL ... + * @param defaultValue the default value of the option + * @param lengthValue the length of defaultValue + */ +void ReProgramArgs::addProperties(const char*name, const char* description, + char shortOpt, const char* longOpt, DataType dataType, + const char* defaultValue, size_t lengthValue) { + ReProgOptionMap::const_iterator it; + for (it = m_options.cbegin(); it != m_options.cend(); ++it){ + if (name == it.key()){ + throw ReOptionException(this, QObject::tr("name defined twice: %1").arg(name)); + } else { + ReProgOption* opt = it.value(); + if (shortOpt == opt->m_shortName) + throw ReOptionException(this, QObject::tr("short option defined twice: %1").arg(shortOpt)); + else if (opt->m_longName == longOpt) + throw ReOptionException(this, QObject::tr("long option defined twice: %1").arg(longOpt)); + } + } + ReProgOption* opt = new ReProgOption; + opt->m_name = name; + opt->m_longName = longOpt; + opt->m_defaultValue = defaultValue; + opt->m_shortName = shortOpt; + opt->m_type = dataType; + // Mark current value as default: + opt->m_value = QByteArray("!") + defaultValue; +} + +/** @brief Adds an option with a boolean value. + * + * @param name the name of the option. Used in the methods getBool() + * @param description a short description of the option. Used in the user messages + * @param shortOpt the one character option identifier. Used in the arguments. Must be preceded by '-' + * @param longOpt the multi character option identifier. Used in the arguments. Must be preceded by '--' + * @param defaultValue the default value of the option. + * + * @see getBool() + */ +void ReProgramArgs::addBool(const char* name, const char* description, + char shortOpt, const char* longOpt, bool defaultVal) { + addProperties(name, description, shortOpt, longOpt, DT_BOOL, + defaultVal ? "t" : "f", 1); +} + +/** @brief Adds an option with an integer value. + * + * @param name the name of the option. Used in the methods getInt() + * @param description a short description of the option. Used in the user messages + * @param shortOpt the one character option identifier. Used in the arguments. Must be preceded by '-' + * @param longOpt the multi character option identifier. Used in the arguments. Must be preceded by '--' + * @param defaultValue the default value of the option + * + * * @see getInt() + */ +void ReProgramArgs::addInt(const char* name, const char* description, + char shortOpt, const char* longOpt, int defaultVal) { + QByteArray number = QByteArray::number(defaultVal); + addProperties(name, description, shortOpt, longOpt, DT_INT, number.constData(), + number.length()); +} + +/** @brief Adds an option with a string value. + * + * @param name the name of the option. Used in the methods getString() + * @param description A short description of the option. Used in the user messages + * @param shortOpt the one character option identifier. Used in the arguments. Must be preceded by '-' + * @param longOpt the multi character option identifier. Used in the arguments. Must be preceded by '--' + * @param defaultValue the default value of the option + * + * @see getString() + */ +void ReProgramArgs::addString(const char* name, const char* description, + char shortOpt, const char* longOpt, bool mayBeEmpty, + const char* defaultVal) { + addProperties(name, description, shortOpt, longOpt, + mayBeEmpty ? DT_STRING_EMPTY : DT_STRING, defaultVal, + defaultVal == NULL ? 0 : strlen(defaultVal)); +} + +/** @brief Analyses a long name option. + * + * The syntax of an long name option is --name or --name=value + * + * @param opt the option string without -- + * + */ +void ReProgramArgs::analyseLong(const char* opt) { + + QByteArray name; + const char* value = strchr(opt, '='); + if (value == NULL) + name = opt; + else { + name = QByteArray(opt).mid(0, value - opt); + value++; + } + ReProgOption* option = m_options[name]; + + switch (option->m_type) { + case DT_INT: + if (value == NULL) + throw ReOptionException(this, + QObject::tr("Option %1: parameter expected. Use --%1=number").arg(name.constData())); + else + option->m_value = value; + break; + case DT_STRING: + if (value == NULL) + throw ReOptionException(this, + QObject::tr("Option %1: parameter expected. Use --%1=string").arg(name.constData())); + option->m_value = value; + break; + case DT_STRING_EMPTY: + if (value == NULL) + value = ""; + option->m_value = value; + break; + case DT_BOOL: { + const char* boolValue = "f"; + if (value == NULL + || ReStringUtils::isInList(value, ";y;yes;t;true", true, + ReStringUtils::AUTO_SEPARATOR)) + boolValue = "t"; + else if (!ReStringUtils::isInList(value, ";n;no;f;false", + true, ReStringUtils::AUTO_SEPARATOR)) + throw ReOptionException(this, + QObject::tr("Option %1: Not a boolean value: %2. Use true or false") + .arg(name.constData()).arg(value)); + // Invert the default value: + if (option->m_defaultValue == "t") + boolValue = boolValue[0] == 't' ? "f" : "t"; + option->m_value = boolValue; + break; + } + default: + break; + } +} + +/** @brief Analyses one or more short name options. + * + * Multiple short name options can be written in one word: + *

Example: -x -y -z can be noted as -xyz

+ *

On the other side an option with parameter can be written in two forms:

+ * + * + * @param opt an option string + * @param nextArg the next argument behind the current option string. + * May be NULL (no more arguments) + * + * @return true a second word has been used: It was a parameter of an string or integer option.
+ * false: The next argument has not been used + */ +bool ReProgramArgs::analyseShort(const char* opt, const char* nextArg) { + bool rc = false; + QByteArrayList properties; + bool again; + do { + again = false; + + ReProgOption* option = search(opt[0], NULL); + + // Forget the option short name: + opt++; + switch (option->m_type) { + case DT_INT: + case DT_STRING: + case DT_STRING_EMPTY: + if (opt[0] != '\0') { + setValue(opt->m_name, opt, option->m_type); + } else { + if (nextArg == NULL || nextArg[0] == '-') { + if (dataType[0] == DT_STRING_EMPTY) + setValue(nameStr, "", dataType); + else + throw ReOptionException(this, + i18n( + "Option $1 has type $2! There is no parameter."), + nameStr, dataType); + } else { + setValue(nameStr, nextArg, dataType); + rc = true; + } + } + break; + case DT_BOOL: { + // Get the current value: + const char* value = "t"; + if (opt[0] == '-') { + opt++; + value = "f"; + } else if (opt[0] == '+') + opt++; + // Invert the default value: + if (properties.strOf(IxDefault)[0] == 't') + value = value[0] == 't' ? "f" : "t"; + setValue(nameStr, value, dataType); + again = opt[0] != '\0'; + break; + } + default: + break; + } + } while (again); + return rc; +} + +/** @brief Returns a not option argument given by an index. + * + * @param index the index of the wanted program argument which is not an option. + * + * @return NULL: wrong index
+ * otherwise: the wanted argument. + */ +const char* ReProgramArgs::arg(size_t index) const { + const char* rc = NULL; + + if (index < (size_t) m_argCount) + rc = m_args[index]; + return rc; +} + +/** @brief Returns the count of arguments (without options). + * + * @return the count of arguments + */ +int ReProgramArgs::argCount() const { + return m_argCount; +} + +/** @brief Returns the value of a boolean option. + * + * @param name Name of the option. + * + * @return The value of the option set in the programs arguments or the default value. + * + * @throws ReOptionException Unknown name or wrong type. + */ +bool ReProgramArgs::getBool(const char* name) { + ReProgOption* option = m_options.value(name, NULL); + if (option == NULL) + throw ReOptionException(this, QObject::tr("%1 is not an option name").arg(name)); + if (option->m_type != DT_BOOL) + throw ReOptionException(this, + QObject::tr("%1 is not an boolean option. Type is %2").arg(name) + .arg(option->m_type)); + bool rc = option->m_value.at(1) == 't'; + return rc; +} + +/** @brief Returns the value of an integer option. + * + * @param name Name of the option. + * + * @return The value of the option set in the programs arguments or the default value. + * + * @throws ReOptionException Unknown name or wrong type. + */ +int ReProgramArgs::getInt(const char* name) { + ReProgOption* option = m_options.value(name, NULL); + if (option == NULL) + throw ReOptionException(this, QObject::tr("%1 is not an option name").arg(name)); + if (option->m_type != DT_BOOL) + throw ReOptionException(this, + QObject::tr("%1 is not an integer option. Type is %2").arg(name) + .arg(option->m_type)); + // Note: first char is a tag: '!': default + int rc = atoi(option->m_value.constData() + 1); + return rc; +} + +/** @brief Returns the value of a string option. + * + * @param name Name of the option. + * + * @return The value of the option set in the programs arguments or the default value. + * + * @throws ReOptionException Unknown name or wrong type. + */ +const char* ReProgramArgs::getString(const char* name, QByteArray& buffer) { + ReProgOption* option = m_options.value(name, NULL); + if (option == NULL) + throw ReOptionException(this, QObject::tr("%1 is not an option name").arg(name)); + if (option->m_type != DT_STRING && option->m_type != DT_STRING_EMPTY) + throw ReOptionException(this, + QObject::tr("%1 is not an string option. Type is %2").arg(name) + .arg(option->m_type)); + // Note: first char is a tag: '!': default + buffer = option->m_value.mid(1); + return buffer.constData(); +} + +/** + * Issues a help message. + * + * @param message message to show + * @param issueLastError true: the last OS error will be shown + * @param lines OUT: a stringlist for the help message + */ +void ReProgramArgs::help(const char* message, bool issueLastError, + ReStringList& lines) const { + lines.append(m_usage); + lines.append(""); + + ReArrayPosition position; + if (m_properties.next(position, NULL, NULL)) { + lines.append(i18n(":")); + } + QByteArray name; + QByteArray prop; + QByteArray line; + QByteArray param; + + while (m_properties.next(position, &name, &prop)) { + ReStringList properties(512, 1024, 2, 2); + properties.split(prop.constData(), '\1'); + line.setLength(0); + DataType dataType = DataType(properties.strOf(IxType)[0]); + const char* shortName = properties.strOf(IxShort); + param.setLength(0); + switch (dataType) { + case DT_INT: + param.append(i18n(""), -1); + break; + case DT_STRING: + param.append(i18n(""), -1); + break; + case DT_STRING_EMPTY: + param.append(i18n("[]"), -1); + break; + default: + break; + } + if (shortName[0] != HIDDEN_SHORT_NAME) { + line.append("-", 1).append(shortName, 1); + line.append(param.constData(), -1).appendChar(' ').append(i18n(" or "), + -1); + } + line.append(i18n("--"), -1).append(properties.strOf(IxLong), -1); + if (param.length() > 0) { + line.append("=", -1).append(param.constData(), -1); + if (dataType != DT_STRING + || properties.strLengthOf(IxDefault) > 0) { + line.append(i18n(" Default value: "), -1); + if (dataType == DT_STRING) + line.appendChar('\''); + line.append(properties.strOf(IxDefault), -1); + if (dataType == DT_STRING) + line.appendChar('\''); + } + } + lines.append(line.constData()); + line.set(PREFIX_LINE_OPTION, -1).append(properties.strOf(IxDescr), -1); + lines.append(line.constData()); + } + if (m_examples.count() > 0) { + lines.append(i18n("Example(s):")); + lines.append(m_examples); + } + if (issueLastError && m_lastError.length() > 0) { + line.set("+++ ", 4).append(m_lastError.constData(), -1); + lines.append(line.constData()); + } + + if (message != NULL && message[0] != '\0') { + line.set("+++ ", 4).append(message, -1); + lines.append(line.constData()); + } +} +/** + * Issues a help message. + * + * @param message message to show + * @param issueLastError true: the last OS error will be shown + * @param stream OUT: output stream, e.g. stderr + */ +void ReProgramArgs::help(const char* message, bool issueLastError, + FILE* stream) const { + ReStringList lines(512, 1024, 8, 2); + help(message, issueLastError, lines); + for (size_t ii = 0; ii < lines.count(); ii++) { + fputs(lines.strOf(ii), stream); + fputc('\n', stream); + } +} + +/** @brief Initializes the options from the program arguments. + * + * While arguments are preceded by an '-' they will be treated as options. + * The rest of arguments are stored for retrieving with getArg(). + * + * @param argc The count of program arguments (inclusive options). + * @param argv The argument vector. + * + * @throws ReException + */ +void ReProgramArgs::init(int argc, const char* argv[]) { + m_program = argv[0]; + argv++; + argc--; + + while (argc > 0 && argv[0][0] == '-') { + if (argv[0][1] == '-') + analyseLong(argv[0] + 2); + else { + if (analyseShort(argv[0] + 1, argc <= 1 ? NULL : argv[1])) + argc--, argv++; + } + argc--; + argv++; + } + m_argCount = argc; + m_args = (const char**) argv; +} + +/** @brief Returns the program name. + * + * @return The name of the application. + */ +const char* ReProgramArgs::programName() const { + return m_program; +} + +/** @brief Search the property string of an option. + * + * @param shortName The option`s short name. Not relevant if longName != NULL. + * @param LongName The option`s long name. Not relevant if longName == NULL. + * @param name Out: The name of the option. + * @param list Out: The properties are returned in this list. + * + * @throws ReOptionException Unknown option. + */ +void ReProgramArgs::search(char shortName, const char* longName, + QByteArray& name, ReStringList& list) { + ReArrayPosition position; + QByteArray properties; + bool found = false; + size_t lengthLongName = 0; + if (longName != NULL) { + const char* ptr; + if ((ptr = strchr(longName, '=')) != NULL) + lengthLongName = ptr - longName; + else + lengthLongName = strlen(longName); + } + while (!found && m_properties.next(position, &name, &properties)) { + list.split(properties.constData(), '\1'); + if (longName == NULL && list.count() > IxShort + && shortName == list.strOf(IxShort)[0]) + found = true; + else if (lengthLongName > 0 && list.count() > IxLong + && list.sizeOf(IxLong) == lengthLongName + 1 + && strncmp(longName, list.strOf(IxLong), lengthLongName) == 0) + found = true; + } + if (!found) { + if (longName == NULL) + name.set(&shortName, 1); + else + name.set(longName, lengthLongName); + throw ReOptionException(this, i18n("Unknown option: $1"), name.constData()); + } +} + +/** @brief Sets the last error message. + * + * @param message The error message. + */ +void ReProgramArgs::setLastError(const char* message) { + m_lastError = message; +} + +/** @brief Sets the last error message. + * + * @param message The error message. + */ +void ReProgramArgs::setLastError(const QByteArray& message) { + m_lastError = message; +} + +/** @brief Sets the option value. + * + * @param name The option's name. + * @param value The option's value. + * @param dataType Theo option's data type. + */ +void ReProgramArgs::setValue(const char* name, const char* value, + DataType dataType) { + switch (dataType) { + case DT_INT: + if (strspn(value, "01234567890") != strlen(value)) + throw ReOptionException(this, + QObject::tr("Option %1 expect an integer as parameter, not %2").arg(name) + .arg(value)); + break; + case DT_STRING: + if (value[0] == '\0') + throw ReOptionException(this, + QObject::tr("Option %1: Empty parameter is not allowed").arg(name)); + break; + case DT_STRING_EMPTY: + case DT_BOOL: + default: + break; + } + QByteArray buffer; + // First character says: defined. + buffer.appendChar(' ').append(value, -1); + m_values.put(name, buffer.constData()); +} + diff --git a/base/ReProgramArgs.hpp b/base/ReProgramArgs.hpp new file mode 100644 index 0000000..cdd6af8 --- /dev/null +++ b/base/ReProgramArgs.hpp @@ -0,0 +1,109 @@ +/* + * ReProgramArgs.hpp + * + * License: Public Domain + * You can use and modify this file without any restriction. + * Do what you want. + * No warranties and disclaimer of any damages. + * You also can use this license: http://www.wtfpl.net + * The latest sources: https://github.com/republib + */ + +#ifndef REPROGRAMARGS_H_ +#define REPROGRAMARGS_H_ + +class ReProgramArgs; +/** All errors will reported by this exception. + */ +class ReOptionException: public ReException { +public: + ReOptionException(ReProgramArgs* caller, const char* format, ...); + ReOptionException(ReProgramArgs* caller, const QString& message); +}; + +class ReProgOption; +typedef QMap ReProgOptionMap; + +/** + * This class analyses the program arguments and give an interface for retrieving them. + * + * Program arguments contains the program name, possibly options and "true" arguments. + *

Options are short name options or long name options.

+ *

A short name option is preceded by a single dash ('-'), a long name option starts with two dashes: (--).

+ *

There are three types of options: boolean, integer and string.

+ *

Every option must have an default value.

+ *

A boolean option has normally a default value false. If it appears in the arguments + * it will be have the value true.

+ *

An integer or string option can be followed by an integer or string value:
+ * Short name option: -x value or -xvalue
+ * Long name option: -xxx=value

+ *

The program must contain a definition of the options: addInt(), addBool() and/or addString(), + * the analyse (init()) and the retrieval (getInt(), getBool() and/or getString().

+ *

The connection between definition and retrieval are names.

+ */ +class ReProgramArgs { + static const char* PREFIX_LINE_OPTION; +public: + enum DataType { + DT_UNDEF = 0, + DT_INT = 'i', + DT_BOOL = 'b', + DT_STRING = 's', + DT_STRING_EMPTY = 'S' + }; + enum { + HIDDEN_SHORT_NAME = 2 + }; +public: + ReProgramArgs(const char* usageList[], const char* examples[] = NULL); + ReProgramArgs(const char* usageString, const char* examples = NULL); + virtual ~ReProgramArgs(); +public: + void addBool(const char* name, const char* description, char shortOpt, + const char* longOpt, bool defaultVal); + void addInt(const char* name, const char* description, char shortOpt, + const char* longOpt, int defaultVal); + void addString(const char* name, const char* description, char shortOpt, + const char* longOpt, bool mayBeEmpty, const char* defaultVal); + int argCount() const; + const char* arg(size_t index) const; + bool getBool(const char* name); + int getInt(const char* name); + const char* getString(const char* name, QByteArray& buffer); + void help(const char* message, bool issueLastError, + QByteArrayList& lines) const; + void help(const char* message, bool issueLastError, FILE* stream) const; + void init(int argc, const char* argv[]); + const char* programName() const; + void setLastError(const char* message); + void setLastError(const QByteArray& message); + void setUsage(const char* usage[]); +private: + void addProperties(const char*name, const char* description, char shortOpt, + const char* longOpt, DataType dataType, const char* defaultValue, + size_t lengthValue); + void analyseLong(const char* opt); + bool analyseShort(const char* opt, const char* nextArg); + ReProgOption* search(char shortName, const char* longName); + void setValue(const char* name, const char* value, DataType dataType); +protected: + QByteArrayList m_usage; + QByteArrayList m_examples; + ReProgOptionMap m_options; + const char** m_args; + int m_argCount; + const char* m_program; + QByteArray m_lastError; +}; + +class ReProgOption { +public: + ReProgramArgs::DataType m_type; + QByteArray m_name; + QByteArray m_longName; + char m_shortName; + QByteArray m_value; + QByteArray m_defaultValue; +}; + +#endif /* REPROGRAMARGS_H_ */ diff --git a/base/ReStringUtils.cpp b/base/ReStringUtils.cpp index d660074..3ec4fd9 100644 --- a/base/ReStringUtils.cpp +++ b/base/ReStringUtils.cpp @@ -24,8 +24,30 @@ * This is a class with static members only. */ +const char ReStringUtils::AUTO_SEPARATOR = '\0'; + const QByteArray ReStringUtils::m_empty; +/** + * Removes a given character from the end of the string if it is there. + * + * @param string string to change + * @param cc character to remove. Default: end of line ('\n')
+ * If cc is '\n' then also '\r' will be removed + * at the end of the string + * @return string: for chaining + */ +QByteArray&ReStringUtils::chomp(QByteArray& string, char cc) +{ + int length; + if (string.length() > 0 && string.at(length = string.length() - 1) == cc) + { + string.resize(length); + if (cc == '\n' && length > 0 && string.at(--length) == '\r') + string.resize(length); + } + return string; +} /** * @brief Counts the occurrences of a given char in a string. * @@ -33,6 +55,7 @@ const QByteArray ReStringUtils::m_empty; * @param cc the char to count * @return the number of cc in the text */ + int ReStringUtils::countChar(const char* line, char cc) { const char* ptr = line; int rc = 0; @@ -119,6 +142,45 @@ char ReStringUtils::fileSeparatorChar() { return s_fileSeparator; } +/** @brief Tests whether a phrase is in a phrase list. + * + * @param phrase the word to search + * @param list the list to search. All phrases of the list are + * separated by separator + * @param ignoreCase true: The search is case insensitive
+ * false: The search is case sensitive + * @param separator the separator in list. + * If AUTO_SEPARATOR the separator will + * be taken from the list itself (the first character) + */ +bool ReStringUtils::isInList(const char* phrase, const char* list, + bool ignoreCase, char separator) { + if (separator == AUTO_SEPARATOR) + separator = *list++; + const char* end = strchr(list, separator); + int phraseLength = strlen(phrase); + bool rc = false; + while (!rc && end != NULL) { + if (end - list == phraseLength) { + if (ignoreCase) + rc = strnicmp(list, phrase, phraseLength) == 0; + else + rc = strncmp(list, phrase, end - list) == 0; + if (rc) + break; + } + list = end + 1; + end = strchr(list, separator); + } + if (!rc) { + if (ignoreCase) + rc = strnicmp(list, phrase, end - list) == 0; + else + rc = strncmp(list, phrase, end - list) == 0; + } + return rc; +} + /** * Builds a hexadecimal dump. * @@ -653,3 +715,4 @@ bool ReCharSet::fillIndexOf(const char* charSet, char minChar, char maxChar, } return rc; } + diff --git a/base/ReStringUtils.hpp b/base/ReStringUtils.hpp index ba2306b..a3939c4 100644 --- a/base/ReStringUtils.hpp +++ b/base/ReStringUtils.hpp @@ -11,6 +11,11 @@ #ifndef RPLSTRING_HPP #define RPLSTRING_HPP +#ifdef __linux__ +#define strnicmp(trg, src, len) strncasecmp(trg, src, len) +#elif __WIN32__ +#define strnicmp(trg, src, len) _strnicmp(trg, src, len) +#endif class ReCharSet { public: ReCharSet(const char* indexToChar, int* charToIndex, @@ -39,6 +44,7 @@ protected: class ReStringUtils { public: + static QByteArray& chomp(QByteArray &string, char cc = '\n'); static int countChar(const char* line, char cc); static int count(const char* source, const char* item); static const QByteArray& cutString(const QByteArray& source, int maxLength, @@ -50,6 +56,8 @@ public: 16) { return hexDump((uint8_t*) data, length, bytesPerLine); } + static bool isInList(const char* phrase, const char* list, bool ignoreCase, + char separator = AUTO_SEPARATOR); static QByteArray read(const char* file, bool removeLastNewline = true); static QByteArray replaceNode(const char* source, const char* newNode); static bool write(const char* file, const char* content = NULL, @@ -75,6 +83,11 @@ public: public: static const QByteArray m_empty; +public: + /** If used in isInList() the first character of the list + * will be the separator. + */ + static const char AUTO_SEPARATOR; }; #endif // RPLSTRING_HPP diff --git a/base/rebase.hpp b/base/rebase.hpp index 1c5a26f..ae366b5 100644 --- a/base/rebase.hpp +++ b/base/rebase.hpp @@ -158,6 +158,7 @@ inline int roundInt(double value) { #include "base/ReWriter.hpp" #include "base/ReLogger.hpp" #include "base/ReException.hpp" +#include "base/ReProgramArgs.hpp" #include "base/ReContainer.hpp" #include "base/ReStringUtils.hpp" #include "base/ReQStringUtils.hpp" diff --git a/cunit/cuReStringUtils.cpp b/cunit/cuReStringUtils.cpp index bb987ab..ae67b4f 100644 --- a/cunit/cuReStringUtils.cpp +++ b/cunit/cuReStringUtils.cpp @@ -185,7 +185,55 @@ public: checkEqu(2.5, value); } + void testChomp(){ + QByteArray buffer; + buffer = "abc\n"; + checkEqu("abc", ReStringUtils::chomp(buffer).constData()); + buffer = "abc\r\n"; + checkEqu("abc", ReStringUtils::chomp(buffer).constData()); + buffer = "abc/"; + checkEqu("abc", ReStringUtils::chomp(buffer, '/').constData()); + buffer = "\n"; + checkEqu("", ReStringUtils::chomp(buffer).constData()); + buffer = ""; + checkEqu("", ReStringUtils::chomp(buffer).constData()); + buffer = ""; + checkEqu("", ReStringUtils::chomp(buffer, 'x').constData()); + } + + void testIsInList(){ + // case sensitive, auto separator: + checkT(ReStringUtils::isInList("yes", ";ja;yes", false)); + checkT(ReStringUtils::isInList("yes", ";ja;yes;si", false)); + checkT(ReStringUtils::isInList("yes", ";yes;si", false)); + // case sensitive, explicite separator: + checkT(ReStringUtils::isInList("yes", "ja;yes;si", false, ';')); + checkT(ReStringUtils::isInList("yes", "yes;si", false, ';')); + checkT(ReStringUtils::isInList("yes", "ja;yes", false, ';')); + + // case insensitive, auto separator: + checkT(ReStringUtils::isInList("yes", ";ja;Yes", true)); + checkT(ReStringUtils::isInList("YES", ";ja;yes;si", true)); + checkT(ReStringUtils::isInList("yEs", ";yeS;si", true)); + // case sensitive, explicite separator: + checkT(ReStringUtils::isInList("Yes", "ja;yes;si", true, ';')); + checkT(ReStringUtils::isInList("yes", "Yes;si", true, ';')); + checkT(ReStringUtils::isInList("YES", "ja;yes", true, ';')); + + // substring + checkF(ReStringUtils::isInList("y", "ja;yes;si", true, ';')); + // sensitive + checkF(ReStringUtils::isInList("yes", "ja;Yes;si", false, ';')); + + // 1 element list + checkT(ReStringUtils::isInList("yes", "yes", false, ';')); + checkT(ReStringUtils::isInList("yes", ";yes", false)); + + } + virtual void run() { + testIsInList(); + testChomp(); testLengthOfReal(); testLengthOfUInt64(); testCountChar(); diff --git a/cunit/cunit.pro b/cunit/cunit.pro index 824a079..a3195c8 100644 --- a/cunit/cunit.pro +++ b/cunit/cunit.pro @@ -58,7 +58,8 @@ SOURCES += main.cpp \ cuReMatcher.cpp \ allTests.cpp \ ../base/ReProcess.cpp \ - cuReProcess.cpp + cuReProcess.cpp \ + ../base/ReProgramArgs.cpp HEADERS += \ ../base/ReFile.hpp \