Mercurial > clickerconvert
changeset 3:8b4c49c92451
Add initial implementation that handles choices
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Tue, 22 Mar 2016 10:39:19 +0100 |
parents | 4926d626fe15 |
children | 5c256892042c |
files | src/cconvert_options.h src/constants.h src/converter.cpp src/converter.h src/main.cpp src/strhelp.c src/strhelp.h |
diffstat | 7 files changed, 999 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cconvert_options.h Tue Mar 22 10:39:19 2016 +0100 @@ -0,0 +1,43 @@ +#ifndef CCONVERT_OPTIONS_H +#define CCONVERT_OPTIONS_H +/* Copyright (C) 2016 by ETH Zürich + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ + +/** @file Commandline options for clickerconvert */ + +#include <QCommandLineParser> +#include <QObject> + +static void cconvert_options(QCommandLineParser &parser) +{ + QList<QCommandLineOption> options; + + options << QCommandLineOption(QStringList() << QStringLiteral("pdf") + << QStringLiteral("p"), + QObject::tr("Output as pdf (default xlsx)")) + << QCommandLineOption(QStringList() << QStringLiteral("output") + << QStringLiteral("o"), + QObject::tr("write output to file (default stdout)"), + QObject::tr("[file]")) + << QCommandLineOption(QStringList() << QStringLiteral("title") + << QStringLiteral("t"), + QObject::tr("Set the title of the document."), + QObject::tr("\"The Title\"")) + << QCommandLineOption(QStringList() << QStringLiteral("debug"), + QObject::tr("Print debug output.")); + + Q_FOREACH (const QCommandLineOption &opt, options) + parser.addOption(opt); + parser.addVersionOption(); + parser.addHelpOption(); + + parser.addPositionalArgument(QStringLiteral("file"), + QObject::tr("File to process (default stdin)"), + QObject::tr("[file]")); +} +#endif // CCONVERT_OPTIONS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/constants.h Tue Mar 22 10:39:19 2016 +0100 @@ -0,0 +1,78 @@ +#ifndef CONSTANTS_H +#define CONSTANTS_H +/* Copyright (C) 2016 by ETH Zürich + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ + +/** @file constants.h + * @brief Common definitions for this project. + * + * @details This file can be included to pull in common + * definitions in the application. */ + +/** + * @brief The version of the software package. + * + * Usually defined as Build parameter. + */ +#ifndef VERSION +#define VERSION "0.0.1" +#endif + +/** +* @brief The user visible application name. + * + * Usually defined as Build parameter. + **/ +#ifndef APPNAME +#define APPNAME "unknown" +#endif + +/** +* @brief Short command line description. */ +#define DESCRIPTION "Convert output of clicker questionaires to different formats" + +/** +* @brief Short copyright notice to show users. */ +#define COPYRIGHT "Copyright (C) 2016 ETH Zürich \n\n" \ + "This file is Free Software under the GNU GPL (v>=2)\n" \ + "and comes with ABSOLUTELY NO WARRANTY!\n" + +/** + * @brief The number of columns the document uses. */ +#define COLUMN_CNT 3 +/** + * @brief The width of the columns in characters. */ +#define COLUMN_WIDTHS { 45, 30, 30 } + +/** + * @brief Regular expression to define a question. + * + * A new question is the first unquoted string after the position + * that is followed after a newline by the word "Answer" + */ +#define QUESTION_PATTERN "([^\"]+)^(Answer.*$)", QRegularExpression::MultilineOption + +/** + * @brief Identifiying line that shows a question is a choice question. */ +#define CHOICE_IDENTIFIER "Answer,Votes,Percent" + +/** + * @brief The pattern used to match a multiple choice answer. */ +#define CHOICE_PATTERN "\"(.*)\",(\\d+),(\\d+\\.\\d+)" + +/** + * @brief The pattern used to match a free text answer. */ +#define FREETXT_PATTERN "\"([^\"]*)\"", QRegularExpression::MultilineOption + +#define TITLE_ROW_HEIGHT 30 + +#define CHOICE_ROW_HEIGHT 30 + +#define TEXT_IDENTIFIER "Answer" + +#endif // CONSTANTS_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/converter.cpp Tue Mar 22 10:39:19 2016 +0100 @@ -0,0 +1,183 @@ +/* Copyright (C) 2016 by ETH Zürich + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ + +#include "converter.h" +#include <QDebug> +#include <QRegularExpression> +#include <QRegularExpressionMatch> + +#include "xlsxdocument.h" +#include "xlsxconditionalformatting.h" + +#include "constants.h" + +QTXLSX_USE_NAMESPACE + +Converter::Converter(const QString &input, const QString &output, + ConvertFormat fmt, const QString &title): + QThread(Q_NULLPTR), + mInput(input), + mOutput(output), + mFmt(fmt), + mTitle(title) +{ + mTitleFmt.setFontUnderline(Format::FontUnderlineSingle); + mTitleFmt.setFontSize(18); + mTitleFmt.setFontName("Calibri"); + mTitleFmt.setFontBold(true); + mTitleFmt.setVerticalAlignment(Format::AlignTop); + + mQuestionFmt.setFontSize(11); + mQuestionFmt.setFontName("Calibri"); + mQuestionFmt.setFontBold(true); + mQuestionFmt.setTopBorderStyle(Format::BorderThin); + mQuestionFmt.setTextWarp(true); + + mAnswerChoiceFmt.setFontSize(11); + mAnswerChoiceFmt.setFontName("Calibri"); + mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignRight); + + mChoiceTextFmt = mAnswerChoiceFmt; + mChoiceTextFmt.setVerticalAlignment(Format::AlignVCenter); + + mChoiceBarFmt = mChoiceTextFmt; + mChoiceBarFmt.setFontName("Webdings"); + mChoiceBarFmt.setFontSize(9); + mChoiceBarFmt.setFontColor(QColor(0xFF, 0x99, 0x33)); + + mChoiceBarInactiveFmt = mChoiceBarFmt; + mChoiceBarInactiveFmt.setFontColor(QColor(0xD9, 0xD9, 0xD9)); +} + +void Converter::run() +{ + QFile infile; + + if (mInput.isEmpty()) { + if (!infile.open(stdin, QIODevice::ReadOnly)) { + mErrors << tr("Failed to open standard input and no input file provided."); + return; + } + } else { + infile.setFileName(mInput); + if (!infile.open(QIODevice::ReadOnly)) { + mErrors << tr("Failed to open %1 for reading.").arg(mInput); + return; + } + } + QTextStream instream(&infile); + + QFile outfile; + if (mOutput.isEmpty()) { + if (!outfile.open(stdout, QIODevice::WriteOnly)) { + mErrors << tr("Failed to open standard output and no output file provided."); + return; + } + } else { + outfile.setFileName(mOutput); + if (!outfile.open(QIODevice::WriteOnly)) { + mErrors << tr("Failed to open %1 for writing.").arg(mOutput); + return; + } + } + convertToXSLX(instream, outfile); +} + +void Converter::convertToXSLX(QTextStream& instream, QFile &output) +{ + Document xlsx; + + ConditionalFormatting bars; + + bars.addDataBarRule(QColor(0xFF, 0x99, 0x33), ConditionalFormatting::VOT_Num, "0", ConditionalFormatting::VOT_Num, "100", false); + + const double colWidth[] = COLUMN_WIDTHS; + for (int i = 1; i <= COLUMN_CNT; i++) { + xlsx.setColumnWidth(i, colWidth[i-1]); + } + + int row = 1; + if (!mTitle.isEmpty()) { + // Set the title of the Questionaire + xlsx.write(row++, 1, mTitle, mTitleFmt); + xlsx.mergeCells("A1:C1"); + xlsx.setRowHeight(1, TITLE_ROW_HEIGHT); + } + + const QString input = instream.readAll(); + + QRegularExpression questionEx(QUESTION_PATTERN); + QRegularExpression choiceEx(CHOICE_PATTERN); + QRegularExpression freetxtEx (FREETXT_PATTERN); + + QRegularExpressionMatch match = questionEx.match(input); + bool foundSomething = false; + int cursor = match.capturedEnd(); + while (match.hasMatch() && cursor != -1) { + /* We've matched a question pattern. With the answer + line */ + if (!match.lastCapturedIndex() == 2) { + /* Should not happen without misconfiguration. */ + mErrors << "Internal parser error."; + return; + } + foundSomething = true; + const QString question = match.captured(1).trimmed(); + const QString answerLine = match.captured(2).trimmed(); + xlsx.write(row++, 1, question, mQuestionFmt); + + if (answerLine == QStringLiteral(CHOICE_IDENTIFIER)) { + QRegularExpressionMatch choiceMatch = choiceEx.match(input, cursor); + xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); + xlsx.write(row++, 1, tr("Answer"), mAnswerChoiceFmt); + int firstChoiceRow = row; + int lastChoiceRow = row; + while (choiceMatch.hasMatch() && choiceMatch.capturedStart() == cursor + 1) { + /* We use the cursor here to keep track of the state. Only if an answer + follows immediately behind the last answer we treat it as valid as + otherwise we can't figure out when the next question begins. */ + cursor = choiceMatch.capturedEnd(); + + /* Write the values */ + xlsx.write(row, 1, choiceMatch.captured(1), mChoiceTextFmt); + bool ok; + double percent = choiceMatch.captured(3).toDouble(&ok); + if (!ok) { + mErrors << "Unparsable number in string: " + choiceMatch.captured(); + } + xlsx.write(row, 2, percent); + xlsx.write(row, 3, tr("%1% | %2 Number of votes"). + arg(choiceMatch.captured(3)).arg(choiceMatch.captured(2)), + mChoiceVotesFmt); + xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); + /* As long as we can match a choice which is either before the next question + or before the end of the document */ + choiceMatch = choiceEx.match(input, cursor); + row++; + lastChoiceRow++; + } + bars.addRange(QString("B%1:B%2").arg(firstChoiceRow).arg(lastChoiceRow)); + } else if (answerLine == QStringLiteral(TEXT_IDENTIFIER)) { + + } + /* Insert Empty row. */ + xlsx.setRowHeight(row++, CHOICE_ROW_HEIGHT); + match = questionEx.match(input, cursor); + cursor = match.capturedEnd(); + } + xlsx.addConditionalFormatting(bars); + + if (!foundSomething) { + mErrors << tr("Failed to parse input document."); + } + + if (!xlsx.saveAs(&output)) { + mErrors << tr("Saving the XLSX document failed."); + return; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/converter.h Tue Mar 22 10:39:19 2016 +0100 @@ -0,0 +1,81 @@ +#ifndef CONVERTER_H +#define CONVERTER_H +/* Copyright (C) 2016 by ETH Zürich + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ + +#include <QThread> +#include <QString> +#include <QStringList> +#include <QTextStream> +#include <QFile> + +#include "xlsxformat.h" + +/** @file Declaration of the Converter class. + */ + +/** + * @enum ConvertFormat + * @brief Possible output format values. + */ +enum ConvertFormat { + /*! XLSX (default). */ + Format_XLSX, + /*! PDF */ + Format_PDF +}; + +/** @brief Base class of Convert operations. + * + * Set up an instance of this using the ctor and according setters and + * start it. + */ +class Converter : public QThread +{ + Q_OBJECT + +public: + /** Construct a new Converter object. + * + * If input is empty stdin is used. If output + * is empty stdout is used. + * + * @param input input filename. + * @param output output filename. + * @param format the format of this. + */ + Converter(const QString &input, const QString &output, + ConvertFormat fmt = Format_XLSX, + const QString &title = QString()); + + /** Check for errors + * + * @returns Empty stringlist on success. Translated errors otherwise.*/ + const QStringList & errors() {return mErrors;} + +protected: + void convertToXSLX(QTextStream &instream, QFile &output); + void run(); + + QString mInput, mOutput; + ConvertFormat mFmt; + QStringList mErrors; + QString mTitle; + + QXlsx::Format mTitleFmt, + mQuestionFmt, + mAnswerChoiceFmt, + mAnswerTextFmt, + mFreeTextFmt, + mChoiceTextFmt, + mChoiceBarFmt, + mChoiceBarInactiveFmt, + mChoiceVotesFmt; +}; + +#endif // CONVERTER_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main.cpp Tue Mar 22 10:39:19 2016 +0100 @@ -0,0 +1,164 @@ +/* Copyright (C) 2016 by ETH Zürich + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ + +/** @file Main entry point for the application. + * + * This file is the wrapper around the qt application. + * Does command line parsing and preparation of the QCoreApplication. + */ +#include "constants.h" + +#include "strhelp.h" +#include "converter.h" +#include "cconvert_options.h" + +#include <QCoreApplication> +#include <QCommandLineParser> +#include <QtPlugin> +#include <QDebug> +#include <QTranslator> +#include <QSettings> +#include <QTimer> + +#ifdef Q_OS_WIN + + #include <windows.h> + #include <shlobj.h> + + Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) +#elif defined(Q_OS_MAC) + Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin) +#else +/* this is only necessary if we build statically for GNU/Linux */ +// Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) +#endif + +#ifdef IS_TAG_BUILD +bool g_debug = false; +#else +bool g_debug = true; +#endif + +QtMessageHandler g_default_msg_handler = NULL; + +void filterDebugOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + if (!g_debug && type == QtDebugMsg) { + return; + } + + if (g_default_msg_handler) { + (*g_default_msg_handler)(type, context, msg); + } +} + +int realMain(int argc, char **argv); + +#if defined(WIN32) && defined(UNICODE) + +/** @brief Unicode entry point. + * + * Converts arguments to UTF-8 and executes the real + * entry point realMain. + */ +int wmain(int argc, wchar_t **argv, wchar_t **envp) +{ + char **utf8args = NULL; + + utf8args = (char**) xmalloc0 ((argc + 1) * sizeof(char*)); + + for (int i = 0; i < argc; i++) { + utf8args[i] = wchar_to_utf8(argv[i], wcslen(argv[i])); + if (utf8args[i] == NULL) { + printf ("Fatal: could not convert arguments to UTF-8.\n"); + exit(-1); + } + } + int ret = realMain(argc, utf8args); + strv_free(utf8args); + + return ret; +} +#else +int main(int argc, char **argv) +{ + return realMain(argc, argv); +} +#endif + +/** @brief The real entry point to the application. + * + * @param [in] argc the count of the arguments. + * @param [in] argv On GNU/Linux this function expects argv to be in the + * native system encoding. On Windows the arguments + * shall be UTF-8 + * + * @returns 0 on success an error code otherwise. */ +int realMain(int argc, char **argv) +{ + /* QCoreApplication setup */ + QCoreApplication app (argc, argv); + QCoreApplication::setOrganizationName(QStringLiteral(APPNAME)); + QCoreApplication::setApplicationName(QStringLiteral(APPNAME)); + QCoreApplication::setApplicationVersion(QStringLiteral(VERSION)); +// QSettings::setDefaultFormat(QSettings::IniFormat); + + /* Setup translations */ + QTranslator translator; + if (QLocale::system().name() == "C") { + /* Useful for testing / development as the primary target is german */ + translator.load(":/l10n/main_de_DE"); + } else { + translator.load(":/l10n/main_" + QLocale::system().name()); + } + app.installTranslator(&translator); + + /* Parse the command line */ + QCommandLineParser parser; + cconvert_options(parser); + + parser.process(app); + +#ifdef IS_TAG_BUILD + g_debug = parser.isSet(debugOpt); +#else + g_debug = true; +#endif + g_default_msg_handler = qInstallMessageHandler(filterDebugOutput); + + + const QStringList args = parser.positionalArguments(); + if (args.size() > 1) { + parser.showHelp(1); + } + + ConvertFormat fmt = Format_XLSX; + /* Initialize the converter. */ + if (parser.isSet(QStringLiteral("pdf"))) { + fmt = Format_PDF; + } + QString infile; + if (args.size()) { + infile = args.first(); + } + Converter conv(infile, parser.value("output"), fmt, + parser.value("title")); + + conv.start(); + conv.wait(); + + const QStringList errors = conv.errors(); + if (errors.isEmpty()) { + return 0; + } else { + Q_FOREACH (const QString err, errors) { + qCritical() << err; + } + } + return 1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/strhelp.c Tue Mar 22 10:39:19 2016 +0100 @@ -0,0 +1,277 @@ +/* Copyright (C) 2015 by ETH Zürich + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ +/* Needed to get asprintf */ +#define _GNU_SOURCE 1 + +/** @file See strhelp.h for documentation. */ + +#include <ctype.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#ifdef WIN32 +#include <windows.h> +#endif + +static void +out_of_core(void) +{ + fputs("\nfatal: out of memory\n", stderr); + exit(2); +} + +void * +xmalloc( size_t n ) +{ + void *p = malloc( n ); + if( !p ) + out_of_core(); + return p; +} + +void * +xmalloc0( size_t n ) +{ + void *p = malloc( n ); + if( !p ) + out_of_core(); + memset (p, 0, n); + return p; +} + +void * +xrealloc( void *a, size_t n ) +{ + void *p = realloc( a, n ); + if( !p ) + out_of_core(); + return p; +} + +char * +xstrndup( const char *string, const size_t len ) +{ + char *p = xmalloc( len + 1 ); + memcpy( p, string, len ); + p[len] = '\0'; + return p; +} + +unsigned int +strv_length (char **str_array) +{ + unsigned int i = 0; + + if (!str_array) + return 0; + + while (str_array[i]) + ++i; + + return i; +} + +void strv_append (char ***pArray, const char *string, const size_t len) +{ + unsigned int old_len = 0; + + if (!*pArray) + { + *pArray = xmalloc(2 * sizeof(char*)); + (*pArray)[0] = xstrndup(string, len); + (*pArray)[1] = NULL; + return; + } + old_len = strv_length(*pArray); + *pArray = xrealloc(*pArray, sizeof(char**) * (old_len + 2)); + + (*pArray)[old_len] = xstrndup(string, len); + (*pArray)[old_len + 1] = NULL; +} + +void +str_append_str(char **pDst, size_t *dst_len, const char *appendage, const size_t len) +{ + if (!appendage) + return; + + if (!(*pDst)) + { + *pDst = xstrndup(appendage, len); + *dst_len = len; + } + else + { + size_t new_size = (*dst_len) + len + 1; + char *p_old = *pDst; + *pDst = xmalloc(new_size); + strncpy(*pDst, p_old, *dst_len); + strncpy(*pDst + *dst_len, appendage, len); + *dst_len = new_size - 1; + (*pDst)[*dst_len] = '\0'; + free (p_old); + } +} + +void +strv_free (char **str_array) +{ + if (str_array) + { + int i; + + for (i = 0; str_array[i] != NULL; i++) + free (str_array[i]); + + free (str_array); + } +} + +bool +str_equal (char *s1, char *s2) +{ + size_t l1 = strlen(s1); + size_t l2 = strlen(s2); + if ((l1 == l2) && + (strcmp(s1, s2) == 0)) + return true; + else + return false; +} + +bool +str_starts_with (char *s1, char *s2) +{ + size_t l2 = strlen(s2); + if (strncmp(s1, s2, l2) == 0) + return true; + else + return false; +} + +void +str_trim (char **s) +{ + size_t i; + if (*s != NULL) + { + while (isspace(**s)) + (*s)++; + i = strlen(*s); + while (isspace((*s)[--i])) + (*s)[i] = '\0'; + } +} + +void +xfree (void *p) +{ + if (p) + free (p); +} + +int +xasprintf (char **strp, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vasprintf(strp, fmt, ap); + va_end(ap); + + if (ret == -1) + out_of_core(); + + return ret; +} + +#ifdef WIN32 +/* Adapted from GPGOL rev. e512053 */ +char * +wchar_to_utf8 (const wchar_t *string, size_t len) +{ + int n, ilen; + char *result; + + ilen = (int) len; + if (ilen < 0) + return NULL; + + /* Note, that CP_UTF8 is not defined in Windows versions earlier + than NT.*/ + n = WideCharToMultiByte (CP_UTF8, 0, string, ilen, NULL, 0, NULL, NULL); + if (n < 0) + return NULL; + + result = xmalloc ((size_t)n+1); + n = WideCharToMultiByte (CP_UTF8, 0, string, ilen, result, n, NULL, NULL); + if (n < 0) + { + xfree (result); + return NULL; + } + result[n] = 0; + return result; +} + +/* Adapted from GPGOL rev. e512053 */ +wchar_t * +utf8_to_wchar (const char *string, size_t len) +{ + int n, ilen; + wchar_t *result; + + ilen = (int) len; + if (ilen < 0) + return NULL; + + n = MultiByteToWideChar (CP_UTF8, 0, string, ilen, NULL, 0); + if (n < 0 || n + 1 < 0) + return NULL; + + result = xmalloc ((size_t)(n+1) * sizeof *result); + n = MultiByteToWideChar (CP_UTF8, 0, string, ilen, result, n); + if (n < 0) + { + xfree (result); + return NULL; + } + result[n] = 0; + return result; +} + +wchar_t +*acp_to_wchar (const char *string, size_t len) +{ + int n, ilen; + wchar_t *result; + + ilen = (int) len; + if (ilen < 0) + return NULL; + + n = MultiByteToWideChar (CP_ACP, 0, string, ilen, NULL, 0); + if (n < 0 || n + 1 < 0) + return NULL; + + result = xmalloc ((size_t)(n+1) * sizeof *result); + n = MultiByteToWideChar (CP_ACP, 0, string, ilen, result, n); + if (n < 0) + { + xfree (result); + return NULL; + } + result[n] = 0; + return result; +} +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/strhelp.h Tue Mar 22 10:39:19 2016 +0100 @@ -0,0 +1,173 @@ +/* Copyright (C) 2015 by ETH Zürich + * Software engineering by Intevation GmbH + * + * This file is Free Software under the GNU GPL (v>=2) + * and comes with ABSOLUTELY NO WARRANTY! + * See LICENSE.txt for details. + */ +#ifndef STRHELP_H +#define STRHELP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stddef.h> + +/** + * @file strhelp.h + * @brief Helper functions for c strings and memory management + * @details strhelp contains terminating memory allocation functions and + * some conveniance functions to work with c strings or arrays of c + * strings. + */ + +/** @def To avoid that a compiler optimizes certain memset calls away */ +#define wipememory2(_ptr,_set,_len) do { \ + volatile char *_vptr=(volatile char *)(_ptr); \ + size_t _vlen=(_len); \ + while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ + } while(0) +/** @def To avoid that a compiler optimizes certain memset calls away */ +#define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) + + +void *xmalloc( size_t n ); +/** @brief like malloc but initalizes the values with 0 */ +void *xmalloc0( size_t n ); +void *xrealloc( void *a, size_t n ); +void *xcalloc( size_t n, size_t m ); +char *xstrndup( const char *string, const size_t len ); +void xfree ( void *p ); + +/** + * @brief Terminating variant of asprintf + * + * This function behaves exactly like asprintf(3) but will terminate + * when an error occures (usally that means that memoy allocation + * failed). + */ +int xasprintf (char **strp, const char *fmt, ...); + +/** + * @brief Returns the length of the given %NULL-terminated + * string array str_array. + * @param[in] str_array a %NULL-terminated array of strings + * @returns length of str_array. + */ +unsigned int strv_length (char **str_array); + +/** + * @brief append a string to a NULL terminated array of strings. + * + * @param[in,out] pArray pointer to the NULL terminated list of string pointers. + * @param[in] string pointer to the string to append to the list. + * @param[in] len length of the string to append to the list + */ +void strv_append (char ***pArray, const char *string, const size_t len); + +/** + * @brief append a string to another string. + * + * @param[in,out] pDst pointer to the string to be extended. + * @param[in,out] dst_len length of the dst string. Will be modified. + * @param[in] appendage pointer to the string to append. + * @param[in] len length of the string to append. + */ +void str_append_str (char **pDst, size_t *dst_len, const char *appendage, + const size_t len); + +/** + * @brief Frees the given %NULL-terminated string array. + * @param[in,out] str_array a %NULL-terminated array of strings + */ +void strv_free (char **str_array); + +/** + * @brief Checks whether two strings exactly match + * @param[in] s1 the first string + * @param[in] s2 the second string + * @returns true if s1 and s2 are equal + */ +bool str_equal (char *s1, char *s2); + +/** + * @brief Checks whether s2 exactly matches the beginning of s1. + * @param[in] s1 the string who's beginning is searched + * @param[in] s2 the string which is searched for + * @returns true if s1 starts with s2, false otherwise + */ +bool str_starts_with (char *s1, char *s2); + +/** + * @brief Trims all white space from the start and end of string. + * @details the start of the string is trimmed by setting *s to the + * first non white space character. The end is trimmed by setting the + * first character after the last non white space character to \0. + * @param[in,out] s ponter to the string to strip + */ +bool str_trim (char **s); + +/** @brief decode base64 encoded data + * + * The memory allocated for dest needs to be free'd by the + * caller. + * + * _Input warning:_ + * If the input contains invalid base64 characters an error + * is returned. + * + * If the input is invalid base64 but consists of valid + * base64 characters _no error_ is returned and dst contains + * the valid input up to the error. + * + * @param [out] dst Pointer to the destination. Needs to be NULL + * @param [out] dst_size Size allocated for the destination. + * @param [in] src Pointer to the base64 encoded data. + * @param [in] src_size Size of the encoded data. + * + * @returns 0 on success a polarssl error or -1 otherwise + */ +int str_base64_decode(char **dst, size_t *dst_size, char *src, + size_t src_size); + +#ifdef WIN32 + +/** @brief convert a utf8 string to utf16 wchar + * + * @param[in] string utf8 string. Must be at least len characters long. + * @param[in] len number of characters to be converted. + * + * @returns pointer to a newly allocated wchar array. NULL on error. + * + **/ +wchar_t *utf8_to_wchar (const char *string, size_t len); + +/** @brief convert a local 8 bit (acp) string to utf16 wchar + * + * @param[in] string acp string. Must be at least len characters long. + * @param[in] len number of characters to be converted. + * + * @returns pointer to a newly allocated wchar array. NULL on error. + * + **/ +wchar_t *acp_to_wchar (const char *string, size_t len); + +/** @brief convert a utf16 string to utf8 + * + * @param[in] string utf16 string. Must be at least len characters long. + * @param[in] len number of characters to be converted. + * + * @returns pointer to a newly allocated char array. NULL on error. + * + **/ +char *wchar_to_utf8 (const wchar_t *string, size_t len); +#endif + +#ifdef __cplusplus +} +#endif + +#endif +