diff options
Diffstat (limited to 'source/app')
| -rw-r--r-- | source/app/BuildInfo.c | 101 | ||||
| -rw-r--r-- | source/app/BuildInfo.h | 78 | ||||
| -rw-r--r-- | source/app/ProgramOption.c | 534 | ||||
| -rw-r--r-- | source/app/ProgramOption.h | 246 | ||||
| -rw-r--r-- | source/app/ReturnCodes.h | 55 |
5 files changed, 1014 insertions, 0 deletions
diff --git a/source/app/BuildInfo.c b/source/app/BuildInfo.c new file mode 100644 index 0000000..d79a31d --- /dev/null +++ b/source/app/BuildInfo.c @@ -0,0 +1,101 @@ +// +// BuildInfo.c - MrsWatson +// Created by Nik Reiman on 1/2/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "app/BuildInfo.h" +#include "logging/EventLogger.h" + +unsigned long buildInfoGetYear(void) +{ + unsigned long result = 0; + CharString buildDate = newCharStringWithCapacity(kCharStringLengthShort); + const char *compilerDate = __DATE__; + size_t startingIndex = strlen(compilerDate) - 4; + strncpy(buildDate->data, compilerDate + startingIndex, 4); + result = (unsigned long)strtol(buildDate->data, NULL, 10); + freeCharString(buildDate); + return result; +} + +static short _getMonthNumber(const char *abbreviatedMonthName) +{ + if (!strncmp(abbreviatedMonthName, "Jan", 3)) { + return 1; + } else if (!strncmp(abbreviatedMonthName, "Feb", 3)) { + return 2; + } else if (!strncmp(abbreviatedMonthName, "Mar", 3)) { + return 3; + } else if (!strncmp(abbreviatedMonthName, "Apr", 3)) { + return 4; + } else if (!strncmp(abbreviatedMonthName, "May", 3)) { + return 5; + } else if (!strncmp(abbreviatedMonthName, "Jun", 3)) { + return 6; + } else if (!strncmp(abbreviatedMonthName, "Jul", 3)) { + return 7; + } else if (!strncmp(abbreviatedMonthName, "Aug", 3)) { + return 8; + } else if (!strncmp(abbreviatedMonthName, "Sep", 3)) { + return 9; + } else if (!strncmp(abbreviatedMonthName, "Oct", 3)) { + return 10; + } else if (!strncmp(abbreviatedMonthName, "Nov", 3)) { + return 11; + } else if (!strncmp(abbreviatedMonthName, "Dec", 3)) { + return 12; + } else { + logInternalError("Invalid build month '%s'", abbreviatedMonthName); + return 0; + } +} + +unsigned long buildInfoGetDatestamp(void) +{ + unsigned long result = buildInfoGetYear() * 10000; + + CharString buffer = newCharStringWithCapacity(kCharStringLengthShort); + strncpy(buffer->data, __DATE__, 3); + result += _getMonthNumber(buffer->data) * 100; + + charStringClear(buffer); + strncpy(buffer->data, __DATE__ + 4, 2); + result += strtol(buffer->data, NULL, 10); + + freeCharString(buffer); + return result; +} + +CharString buildInfoGetVersionString(void) +{ + CharString result = newCharStringWithCapacity(kCharStringLengthShort); + snprintf(result->data, result->capacity, "%s version %d.%d.%d", + PROGRAM_NAME, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH); + return result; +} diff --git a/source/app/BuildInfo.h b/source/app/BuildInfo.h new file mode 100644 index 0000000..c2977ef --- /dev/null +++ b/source/app/BuildInfo.h @@ -0,0 +1,78 @@ +// +// BuildInfo.h - MrsWatson +// Created by Nik Reiman on 1/2/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MrsWatson_BuildInfo_h +#define MrsWatson_BuildInfo_h + +#include "base/CharString.h" + +#define PROGRAM_NAME "MrsWatson" +#define VENDOR_NAME "Teragon Audio" + +#define VERSION_MAJOR 0 +#define VERSION_MINOR 9 +#define VERSION_PATCH 7 + +#define PROJECT_WEBSITE "https://github.com/teragonaudio/mrswatson" +#define SUPPORT_WEBSITE "https://github.com/teragonaudio/mrswatson/issues" +#define SUPPORT_EMAIL "support@teragonaudio.com" + +#define LICENSE_STRING "Redistribution and use in source and binary forms, with or without " \ + "modification, are permitted provided that the following conditions are met:\n\n" \ + "* Redistributions of source code must retain the above copyright notice, this list of " \ + "conditions and the following disclaimer.\n" \ + "* Redistributions in binary form must reproduce the above copyright notice, this list of " \ + "conditions and the following disclaimer in the documentation and/or other materials " \ + "provided with the distribution.\n\n" \ + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND ANY " \ + "EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF " \ + "MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE " \ + "COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, " \ + "EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE " \ + "GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED " \ + "AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING " \ + "NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED " \ + "OF THE POSSIBILITY OF SUCH DAMAGE." + +/** + * @return Year the application was built in + */ +unsigned long buildInfoGetYear(void); + +/** + * @return Build timestamp in the form YYYYMMDD + */ +unsigned long buildInfoGetDatestamp(void); + +/** + * Get the full application name and version + * @return String with the application's name and version. The caller must free + * this memory when finished with it. + */ +CharString buildInfoGetVersionString(void); + +#endif diff --git a/source/app/ProgramOption.c b/source/app/ProgramOption.c new file mode 100644 index 0000000..075b033 --- /dev/null +++ b/source/app/ProgramOption.c @@ -0,0 +1,534 @@ +// +// ProgramOption.c - MrsWatson +// Created by Nik Reiman on 1/2/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "app/ProgramOption.h" +#include "base/File.h" +#include "logging/EventLogger.h" + +CharString _programOptionGetString(const ProgramOption self); +float _programOptionGetNumber(const ProgramOption self); + +ProgramOption newProgramOption(void) +{ + return newProgramOptionWithName(-1, EMPTY_STRING, EMPTY_STRING, false, + kProgramOptionTypeNumber, kProgramOptionArgumentTypeInvalid); +} + +ProgramOption newProgramOptionWithName(const int optionIndex, const char *name, + const char *help, boolByte hasShortForm, ProgramOptionType type, + ProgramOptionArgumentType argumentType) +{ + ProgramOption option = (ProgramOption)malloc(sizeof(ProgramOptionMembers)); + + option->index = (unsigned int)optionIndex; + option->name = newCharStringWithCString(name); + option->help = newCharStringWithCString(help); + option->hasShortForm = hasShortForm; + option->hideInHelp = false; + + option->type = type; + + switch (type) { + case kProgramOptionTypeEmpty: + // Nothing needed here + break; + + case kProgramOptionTypeString: + option->_data.string = newCharString(); + break; + + case kProgramOptionTypeNumber: + option->_data.number = 0.0f; + break; + + case kProgramOptionTypeList: + option->_data.list = newLinkedList(); + break; + + default: + logInternalError("ProgramOption with invalid type"); + break; + } + + option->argumentType = argumentType; + option->enabled = false; + + return option; +} + +void _programOptionPrintDefaultValue(const ProgramOption self) +{ + CharString stringValue; + + switch (self->type) { + case kProgramOptionTypeString: + stringValue = _programOptionGetString(self); + + if (stringValue != NULL && !charStringIsEmpty(stringValue)) { + printf(", default value '%s'", stringValue->data); + } + + break; + + case kProgramOptionTypeNumber: + printf(", default value: %.0f", _programOptionGetNumber(self)); + break; + + default: + break; + } +} + +void programOptionPrintHelp(const ProgramOption self, boolByte withFullHelp, int indentSize, int initialIndent) +{ + CharString wrappedHelpString; + int i; + + if (self == NULL) { + logError("Can't find help for that option. Try running with --help to see all options\n"); + return; + } + + // Initial argument indent + for (i = 0; i < initialIndent; i ++) { + printf(" "); + } + + // All arguments have a long form, so that will always be printed + printf("--%s", self->name->data); + + if (self->hasShortForm) { + printf(" (or -%c)", self->name->data[0]); + } + + switch (self->argumentType) { + case kProgramOptionArgumentTypeRequired: + printf(" <argument>"); + break; + + case kProgramOptionArgumentTypeOptional: + printf(" [argument]"); + break; + + case kProgramOptionArgumentTypeNone: + default: + break; + } + + _programOptionPrintDefaultValue(self); + + if (withFullHelp) { + // Newline and indentation before help + wrappedHelpString = charStringWrap(self->help, (unsigned int)(initialIndent + indentSize)); + printf("\n%s\n\n", wrappedHelpString->data); + freeCharString(wrappedHelpString); + } else { + printf("\n"); + } +} + +CharString _programOptionGetString(const ProgramOption self) +{ + return self->type == kProgramOptionTypeString ? self->_data.string : NULL; +} + +float _programOptionGetNumber(const ProgramOption self) +{ + return self->type == kProgramOptionTypeNumber ? self->_data.number : -1.0f; +} + +static LinkedList _programOptionGetList(const ProgramOption self) +{ + return self->type == kProgramOptionTypeList ? self->_data.list : NULL; +} + +static void _programOptionSetString(ProgramOption self, const CharString value) +{ + if (self->type == kProgramOptionTypeString) { + charStringCopy(self->_data.string, value); + } +} + +static void _programOptionSetCString(ProgramOption self, const char *value) +{ + CharString valueString = newCharStringWithCString(value); + _programOptionSetString(self, valueString); + freeCharString(valueString); +} + +static void _programOptionSetNumber(ProgramOption self, const float value) +{ + if (self->type == kProgramOptionTypeNumber) { + self->_data.number = value; + } +} + +static void _programOptionSetListItem(ProgramOption self, void *value) +{ + if (self->type == kProgramOptionTypeList) { + linkedListAppend(self->_data.list, value); + } +} + +static void _programOptionSetData(ProgramOption self, const char *data) +{ + if (data == NULL) { + return; + } + + switch (self->type) { + case kProgramOptionTypeString: + _programOptionSetCString(self, data); + break; + + case kProgramOptionTypeNumber: + // Windows doesn't do strtof :( + _programOptionSetNumber(self, (float)strtod(data, NULL)); + break; + + case kProgramOptionTypeList: + _programOptionSetListItem(self, (void *)data); + break; + + default: + logInternalError("Set ProgramOption with invalid type"); + break; + } +} + +void freeProgramOption(ProgramOption self) +{ + if (self != NULL) { + freeCharString(self->name); + freeCharString(self->help); + + switch (self->type) { + case kProgramOptionTypeString: + freeCharString(self->_data.string); + break; + + case kProgramOptionTypeList: + // Note: This will not actually free the associated strings for this + // option. This is ok if the list items are parsed from argv/argc, but + // otherwise memory could be leaked here. + freeLinkedList(self->_data.list); + break; + + default: + break; + } + + free(self); + } +} + + +ProgramOptions newProgramOptions(int numOptions) +{ + ProgramOptions options = (ProgramOptions)malloc(sizeof(ProgramOptionsMembers)); + options->numOptions = (unsigned int)numOptions; + options->options = (ProgramOption *)malloc(sizeof(ProgramOption) * numOptions); + memset(options->options, 0, sizeof(ProgramOption) * numOptions); + return options; +} + +boolByte programOptionsAdd(const ProgramOptions self, const ProgramOption option) +{ + if (option != NULL && option->index < self->numOptions) { + self->options[option->index] = option; + return true; + } + + return false; +} + +static boolByte _isStringShortOption(const char *testString) +{ + return (boolByte)(testString != NULL && strlen(testString) == 2 && testString[0] == '-'); +} + +static boolByte _isStringLongOption(const char *testString) +{ + return (boolByte)(testString != NULL && strlen(testString) > 2 && testString[0] == '-' && testString[1] == '-'); +} + +static ProgramOption _findProgramOption(ProgramOptions self, const char *name) +{ + ProgramOption potentialMatchOption, optionMatch; + CharString optionStringWithoutDashes; + unsigned int i; + + if (_isStringShortOption(name)) { + for (i = 0; i < self->numOptions; i++) { + potentialMatchOption = self->options[i]; + + if (potentialMatchOption->hasShortForm && potentialMatchOption->name->data[0] == name[1]) { + return potentialMatchOption; + } + } + } + + if (_isStringLongOption(name)) { + optionMatch = NULL; + optionStringWithoutDashes = newCharStringWithCapacity(kCharStringLengthShort); + strncpy(optionStringWithoutDashes->data, name + 2, strlen(name) - 2); + + for (i = 0; i < self->numOptions; i++) { + potentialMatchOption = self->options[i]; + + if (charStringIsEqualTo(potentialMatchOption->name, optionStringWithoutDashes, false)) { + optionMatch = potentialMatchOption; + break; + } + } + + freeCharString(optionStringWithoutDashes); + return optionMatch; + } + + // If no option was found, then return null + return NULL; +} + +static boolByte _fillOptionArgument(ProgramOption self, int *currentArgc, int argc, char **argv) +{ + if (self->argumentType == kProgramOptionArgumentTypeNone) { + return true; + } else if (self->argumentType == kProgramOptionArgumentTypeOptional) { + int potentialNextArgc = *currentArgc + 1; + + if (potentialNextArgc >= argc) { + return true; + } else { + char *potentialNextArg = argv[potentialNextArgc]; + + // If the next string in the sequence is NOT an argument, we assume it is the optional argument + if (!_isStringShortOption(potentialNextArg) && !_isStringLongOption(potentialNextArg)) { + _programOptionSetData(self, potentialNextArg); + (*currentArgc)++; + return true; + } else { + // Otherwise, it is another option, but that's ok + return true; + } + } + } else if (self->argumentType == kProgramOptionArgumentTypeRequired) { + int nextArgc = *currentArgc + 1; + + if (nextArgc >= argc) { + logCritical("Option '%s' requires an argument, but none was given", self->name->data); + return false; + } else { + char *nextArg = argv[nextArgc]; + + if (_isStringShortOption(nextArg) || _isStringLongOption(nextArg)) { + logCritical("Option '%s' requires an argument, but '%s' is not valid", self->name->data, nextArg); + return false; + } else { + _programOptionSetData(self, nextArg); + (*currentArgc)++; + return true; + } + } + } else { + logInternalError("Unknown argument type '%d'", self->argumentType); + return false; + } +} + +boolByte programOptionsParseArgs(ProgramOptions self, int argc, char **argv) +{ + int argumentIndex; + + for (argumentIndex = 1; argumentIndex < argc; argumentIndex++) { + const ProgramOption option = _findProgramOption(self, argv[argumentIndex]); + + if (option == NULL) { + logCritical("Invalid option '%s'", argv[argumentIndex]); + return false; + } else { + if (_fillOptionArgument(option, &argumentIndex, argc, argv)) { + option->enabled = true; + } else { + return false; + } + } + } + + // If we make it to here, return true + return true; +} + +boolByte programOptionsParseConfigFile(ProgramOptions self, const CharString filename) +{ + boolByte result = false; + File configFile = NULL; + LinkedList configFileLines = NULL; + CharString *argvCharStrings; + int argc; + char **argv; + int i; + + if (filename == NULL || charStringIsEmpty(filename)) { + logCritical("Cannot read options from empty filename"); + return false; + } + + configFile = newFileWithPath(filename); + + if (configFile == NULL || configFile->fileType != kFileTypeFile) { + logCritical("Cannot read options from non-existent file '%s'", filename->data); + freeFile(configFile); + return false; + } + + configFileLines = fileReadLines(configFile); + + if (configFileLines == NULL) { + logInternalError("Could not split config file lines"); + return false; + } else if (linkedListLength(configFileLines) == 0) { + logInfo("Config file '%s' is empty", filename->data); + freeLinkedList(configFileLines); + freeFile(configFile); + return true; + } else { + // Don't need the file anymore, it can be freed here + freeFile(configFile); + } + + argvCharStrings = (CharString *)linkedListToArray(configFileLines); + argc = linkedListLength(configFileLines); + argv = (char **)malloc(sizeof(char *) * (argc + 1)); + // Normally this would be the application name, don't care about it here + argv[0] = NULL; + + for (i = 0; i < argc; i++) { + argv[i + 1] = argvCharStrings[i]->data; + } + + argc++; + result = programOptionsParseArgs(self, argc, argv); + + freeLinkedListAndItems(configFileLines, (LinkedListFreeItemFunc)freeCharString); + free(argvCharStrings); + free(argv); + return result; +} + +void programOptionsPrintHelp(const ProgramOptions self, boolByte withFullHelp, int indentSize) +{ + unsigned int i; + + for (i = 0; i < self->numOptions; i++) { + if (!self->options[i]->hideInHelp) { + programOptionPrintHelp(self->options[i], withFullHelp, indentSize, indentSize); + } + } +} + +ProgramOption programOptionsFind(const ProgramOptions self, const CharString string) +{ + unsigned int i; + + for (i = 0; i < self->numOptions; i++) { + if (charStringIsEqualTo(string, self->options[i]->name, true)) { + return self->options[i]; + } + } + + return NULL; +} + +void programOptionsPrintHelpForOption(const ProgramOptions self, const CharString string, + boolByte withFullHelp, int indentSize) +{ + programOptionPrintHelp(programOptionsFind(self, string), withFullHelp, indentSize, 0); +} + +const CharString programOptionsGetString(const ProgramOptions self, const unsigned int index) +{ + return index < self->numOptions ? _programOptionGetString(self->options[index]) : NULL; +} + +float programOptionsGetNumber(const ProgramOptions self, const unsigned int index) +{ + return index < self->numOptions ? _programOptionGetNumber(self->options[index]) : -1.0f; +} + +const LinkedList programOptionsGetList(const ProgramOptions self, const unsigned int index) +{ + return index < self->numOptions ? _programOptionGetList(self->options[index]) : NULL; +} + +void programOptionsSetCString(ProgramOptions self, const unsigned int index, const char *value) +{ + if (index < self->numOptions) { + _programOptionSetCString(self->options[index], value); + } +} + +void programOptionsSetString(ProgramOptions self, const unsigned int index, const CharString value) +{ + if (index < self->numOptions) { + _programOptionSetString(self->options[index], value); + } +} + +void programOptionsSetNumber(ProgramOptions self, const unsigned int index, const float value) +{ + if (index < self->numOptions) { + _programOptionSetNumber(self->options[index], value); + } +} + +void programOptionsSetListItem(ProgramOptions self, const unsigned int index, void *value) +{ + if (index < self->numOptions) { + _programOptionSetListItem(self->options[index], value); + } +} + +void freeProgramOptions(ProgramOptions self) +{ + unsigned int i; + + if (self == NULL) { + return; + } + + for (i = 0; i < self->numOptions; i++) { + freeProgramOption(self->options[i]); + } + + free(self->options); + free(self); +} diff --git a/source/app/ProgramOption.h b/source/app/ProgramOption.h new file mode 100644 index 0000000..c331b53 --- /dev/null +++ b/source/app/ProgramOption.h @@ -0,0 +1,246 @@ +// +// ProgramOption.h - MrsWatson +// Created by Nik Reiman on 1/2/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MrsWatson_ProgramOption_h +#define MrsWatson_ProgramOption_h + +#include "base/CharString.h" +#include "base/LinkedList.h" +#include "base/Types.h" + +#define NO_SHORT_FORM false +#define HAS_SHORT_FORM true + +typedef enum { + kProgramOptionTypeEmpty, + kProgramOptionTypeString, + kProgramOptionTypeNumber, + kProgramOptionTypeList, + kProgramOptionTypeNumTypes +} ProgramOptionType; + +typedef enum { + kProgramOptionArgumentTypeNone, + kProgramOptionArgumentTypeOptional, + kProgramOptionArgumentTypeRequired, + kProgramOptionArgumentTypeInvalid +} ProgramOptionArgumentType; + +typedef union { + CharString string; + float number; + LinkedList list; +} ProgramOptionData; + +typedef struct { + unsigned int index; + CharString name; + CharString help; + boolByte hasShortForm; + // For "hidden" options which should not be printed out in the help output + boolByte hideInHelp; + + ProgramOptionType type; + ProgramOptionData _data; + ProgramOptionArgumentType argumentType; + boolByte enabled; +} ProgramOptionMembers; +typedef ProgramOptionMembers *ProgramOption; + +typedef struct { + ProgramOption *options; + unsigned int numOptions; +} ProgramOptionsMembers; +typedef ProgramOptionsMembers *ProgramOptions; + + +/** + * Create a new ProgramOption instance + * @return An initialized ProgramOption + */ +ProgramOption newProgramOption(void); + +/** + * Create a new ProgramOption instance with some default values + * @param opnionIndex Reference index for option (ie, from an enum) + * @param name Full option name, hyphenated in the case of multiple words + * @param help Full help string + * @param hasShortForm True if the option should also be matched with the first letter + * @param argumentType Expected argument type which can be passed to this option + * @return + */ +ProgramOption newProgramOptionWithName(const int optionIndex, const char *name, + const char *help, boolByte hasShortForm, ProgramOptionType type, + ProgramOptionArgumentType argumentType); + +/** + * Print out help for the option + * @param self + * @param withFullHelp Print the entire help or just the argument name and summary + * @param indentSize Number of spaces to indent output + * @param initialIndent Initial number of spaces to offset output + */ +void programOptionPrintHelp(const ProgramOption self, boolByte withFullHelp, + int indentSize, int initialIndent); + +/** + * Free memory used by a ProgramOption instance + * @param self + */ +void freeProgramOption(ProgramOption self); + + +/** + * Create a new ProgramOptions container + * @param numOptions Number of options to hold + * @return An initialized ProgramOptions + */ +ProgramOptions newProgramOptions(int numOptions); + +/** + * Add a ProgramOption instance to the collection + * @param self + * @param option Option to add to the collection. Note that this option must have + * its index set correctly, as the ProgramOptions options array is statically + * allocated to a set size when the object is initialized. + * @return True on success, false if option is null or has an invalid index + */ +boolByte programOptionsAdd(const ProgramOptions self, const ProgramOption option); + +/** + * Find a ProgramOption by name + * @param self + * @param name Name to search for (case insensitive) + * @return Matching ProgramOption, NULL otherwise + */ +ProgramOption programOptionsFind(const ProgramOptions self, const CharString name); + +/** + * Parse a command line argument array. + * @param self + * @param argc Number of arguments (ie, from main(int argc, char** argv) + * @param argv Argument array (ie, from main(int argc, char** argv) + * @return False if an error occurred during parsing, such as a missing or invalid argument + */ +boolByte programOptionsParseArgs(ProgramOptions self, int argc, char **argv); + +/** + * Parse a configuration file to options. File should be plain text with one + * argument per line + * @param self + * @param filename Filename to parse + * @return True if all options were correctly parsed, false if there was an error + * either opening the file or with the arguments themselves. + */ +boolByte programOptionsParseConfigFile(ProgramOptions self, const CharString filename); + +/** + * Print out help for all options + * @param self + * @param withFullHelp Include full help text, or just option summaries + * @param indentSize Indent size to use for output + */ +void programOptionsPrintHelp(const ProgramOptions self, boolByte withFullHelp, int indentSize); + +/** + * Find an option and print out its help + * @param self + * @param string Option name to find + * @param withFullHelp True if full help should be shown, otherwise just the + * summary string. + * @param indentSize Indent size to use for output + */ +void programOptionsPrintHelpForOption(const ProgramOptions self, const CharString string, + boolByte withFullHelp, int indentSize); + +/** + * Get string value for an option + * @param self + * @param index Option index + * @return Option value string, or NULL if this option is of a different type + */ +const CharString programOptionsGetString(const ProgramOptions self, const unsigned int index); + +/** + * Get numeric value for an option + * @param self + * @param index Option index + * @return Option value string, or -1 if this option is of a different type + */ +float programOptionsGetNumber(const ProgramOptions self, const unsigned int index); + +/** + * Get linked list values for an option + * @param self + * @param index Option index + * @return Option value string, or NULL if this option is of a different type + */ +const LinkedList programOptionsGetList(const ProgramOptions self, const unsigned int index); + +/** + * Set an option's string value. If setting the wrong type to the option, this + * call does nothing. + * @param self + * @param index Option index + * @param value Value to set + */ +void programOptionsSetCString(ProgramOptions self, const unsigned int index, const char *value); + +/** + * Set an option's string value. If setting the wrong type to the option, this + * call does nothing. + * @param self + * @param index Option index + * @param value Value to set + */ +void programOptionsSetString(ProgramOptions self, const unsigned int index, const CharString value); + +/** + * Set an option's numeric value. If setting the wrong type to the option, this + * call does nothing. + * @param self + * @param index Option index + * @param value Value to set + */ +void programOptionsSetNumber(ProgramOptions self, const unsigned int index, const float value); + +/** + * Add an item to an option's linked list. If this option has the wrong type, + * then this call does nothing. + * @param self + * @param index Option index + * @param value Value to add + */ +void programOptionsSetListItem(ProgramOptions self, const unsigned int index, void *value); + +/** + * Free memory used by a ProgramOptions array and all options in the collection + * @param self + */ +void freeProgramOptions(ProgramOptions self); + +#endif diff --git a/source/app/ReturnCodes.h b/source/app/ReturnCodes.h new file mode 100644 index 0000000..2ff8708 --- /dev/null +++ b/source/app/ReturnCodes.h @@ -0,0 +1,55 @@ +// +// ReturnCodes.h - MrsWatson +// Created by Nik Reiman on 10 May 13. +// Copyright (c) 2013 Teragon Audio. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef MrsWatson_ReturnCodes_h +#define MrsWatson_ReturnCodes_h + +// Exit result codes +typedef enum { + RETURN_CODE_SUCCESS, + RETURN_CODE_NOT_RUN, + RETURN_CODE_INVALID_ARGUMENT, + RETURN_CODE_MISSING_REQUIRED_OPTION, + RETURN_CODE_IO_ERROR, + RETURN_CODE_PLUGIN_ERROR, + RETURN_CODE_INVALID_PLUGIN_CHAIN, + RETURN_CODE_UNSUPPORTED_FEATURE, + RETURN_CODE_INTERNAL_ERROR, + // This return code should always be last in this enum list. It is not + // actually used, but instead we add the signal number to it and exit with + // that code instead. + RETURN_CODE_SIGNAL, + + // Failure codes when forking processes (at present only used by test suite) + RETURN_CODE_FORK_FAILED = -1, + RETURN_CODE_SHELL_FAILED = 127, + RETURN_CODE_LAUNCH_FAILED_OTHER = 255, + + NUM_RETURN_CODES +} ReturnCodes; + +#endif |
