diff options
| author | pepper <peppersclothescult@gmail.com> | 2015-01-10 21:32:32 -0800 |
|---|---|---|
| committer | pepper <peppersclothescult@gmail.com> | 2015-01-10 21:32:32 -0800 |
| commit | d53fa8a169832563c62262078b8d2ffe5cab8473 (patch) | |
| tree | b911d06d357d009c976709780f10e92ce915228a /source/base | |
first
Diffstat (limited to 'source/base')
| -rw-r--r-- | source/base/CharString.c | 298 | ||||
| -rw-r--r-- | source/base/CharString.h | 182 | ||||
| -rw-r--r-- | source/base/Endian.c | 93 | ||||
| -rw-r--r-- | source/base/Endian.h | 89 | ||||
| -rw-r--r-- | source/base/File.c | 922 | ||||
| -rw-r--r-- | source/base/File.h | 262 | ||||
| -rw-r--r-- | source/base/LinkedList.c | 157 | ||||
| -rw-r--r-- | source/base/LinkedList.h | 98 | ||||
| -rw-r--r-- | source/base/PlatformInfo.c | 272 | ||||
| -rw-r--r-- | source/base/PlatformInfo.h | 68 | ||||
| -rw-r--r-- | source/base/PlatformInfoMac.m | 41 | ||||
| -rw-r--r-- | source/base/Types.h | 89 |
12 files changed, 2571 insertions, 0 deletions
diff --git a/source/base/CharString.c b/source/base/CharString.c new file mode 100644 index 0000000..e44e4dd --- /dev/null +++ b/source/base/CharString.c @@ -0,0 +1,298 @@ +// +// CharString.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 "logging/EventLogger.h" + +CharString newCharString(void) +{ + return newCharStringWithCapacity(kCharStringLengthDefault); +} + +CharString newCharStringWithCapacity(size_t length) +{ + CharString charString = (CharString)malloc(sizeof(CharStringMembers)); + charString->capacity = length; + charString->data = (char *)malloc(sizeof(char) * length); + charStringClear(charString); + return charString; +} + +CharString newCharStringWithCString(const char *string) +{ + size_t length; + CharString result = NULL; + + length = string != NULL ? strlen(string) : 0; + + if (length > kCharStringLengthLong) { + logError("Can't create string with length %d", length); + } else if (length == 0) { + result = newCharString(); + } else { + // Add 1 to compensate for trailing null character + result = newCharStringWithCapacity(length + 1); + strncpy(result->data, string, length); + } + + return result; +} + +void charStringAppend(CharString self, const CharString string) +{ + charStringAppendCString(self, string->data); +} + +void charStringAppendCString(CharString self, const char *string) +{ + size_t stringLength = strlen(string); + size_t selfLength = strlen(self->data); + + if (stringLength + selfLength >= self->capacity) { + self->capacity = stringLength + selfLength + 1; // don't forget the null! + self->data = (char *)realloc(self->data, self->capacity); + strcat(self->data, string); + } else { + strcat(self->data, string); + } +} + +void charStringClear(CharString self) +{ + memset(self->data, 0, self->capacity); +} + +void charStringCopyCString(CharString self, const char *string) +{ + strncpy(self->data, string, self->capacity); +} + +void charStringCopy(CharString self, const CharString string) +{ + strncpy(self->data, string->data, self->capacity); +} + +boolByte charStringIsEmpty(const CharString self) +{ + return (boolByte)(self == NULL || self->data == NULL || self->data[0] == '\0'); +} + +boolByte charStringIsEqualTo(const CharString self, const CharString string, boolByte caseInsensitive) +{ + size_t comparisonSize; + + if (self == NULL || string == NULL) { + return false; + } + + // Only compare to the length of the smaller of the two strings + comparisonSize = self->capacity < string->capacity ? self->capacity : string->capacity; + + if (caseInsensitive) { + return (boolByte)(strncasecmp(self->data, string->data, comparisonSize) == 0); + } else { + return (boolByte)(strncmp(self->data, string->data, comparisonSize) == 0); + } +} + +boolByte charStringIsEqualToCString(const CharString self, const char *string, boolByte caseInsensitive) +{ + if (self == NULL || string == NULL) { + return false; + } else if (caseInsensitive) { + return (boolByte)(strncasecmp(self->data, string, self->capacity) == 0); + } else { + return (boolByte)(strncmp(self->data, string, self->capacity) == 0); + } +} + +boolByte charStringIsLetter(const CharString self, const size_t index) +{ + const char ch = self->data[index]; + return (boolByte)((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')); +} + +boolByte charStringIsNumber(const CharString self, const size_t index) +{ + const char ch = self->data[index]; + return (boolByte)(ch >= '0' && ch <= '9'); +} + +LinkedList charStringSplit(const CharString self, const char delimiter) +{ + LinkedList result = NULL; + char *delimiterPtr = NULL; + char *selfIndex = self->data; + CharString item = NULL; + size_t charsToCopy = 0; + boolByte done = false; + + if (delimiter == '\0') { + logError("Cannot split string with NULL delimiter"); + return NULL; + } + + result = newLinkedList(); + + while (!done) { + delimiterPtr = strchr(selfIndex, delimiter); + + if (delimiterPtr == NULL) { + done = true; + charsToCopy = self->data + strlen(self->data) - selfIndex; + } else { + charsToCopy = delimiterPtr - selfIndex; + } + + if (charsToCopy > 0) { + item = newCharStringWithCapacity(charsToCopy + 1); + strncpy(item->data, selfIndex, charsToCopy); + linkedListAppend(result, item); + } + + selfIndex = delimiterPtr + 1; + } + + return result; +} + +void _charStringWrap(const char *srcString, char *destString, size_t destStringSize, int indentSize, int lineLength); +void _charStringWrap(const char *srcString, char *destString, size_t destStringSize, int indentSize, int lineLength) +{ + char *lineBuffer = NULL; + unsigned long destStringIndex = 0; + unsigned long srcStringIndex = 0; + size_t lineIndex = 0; + int indentIndex = 0; + size_t bufferLength; + char *newlinePosition; + char *lastSpacePosition; + + // Sanity checks + if (srcString == NULL) { + return; + } else if (indentSize < 0 || indentSize > lineLength) { + return; + } else if (lineLength <= 0) { + return; + } + + lineBuffer = (char *)malloc(sizeof(char) * lineLength); + + while (srcStringIndex < strlen(srcString)) { + if (lineIndex == 0) { + for (indentIndex = 0; indentIndex < indentSize; indentIndex++) { + destString[destStringIndex++] = ' '; + lineIndex++; + } + } + + // Clear out the line buffer, and copy a full line into it + memset(lineBuffer, 0, lineLength); + bufferLength = lineLength - lineIndex - 1; // don't forget the null! + + if (bufferLength <= 0) { + break; + } + + strncpy(lineBuffer, srcString + srcStringIndex, bufferLength); + + // Check to see if we have copied the last line of the source string. If so, append that to + // the destination string and break. + if (bufferLength + srcStringIndex >= strlen(srcString)) { + strncpy(destString + destStringIndex, lineBuffer, destStringSize - destStringIndex - 1); + break; + } + + // Look for any newlines in the buffer, and stop there if we find any + newlinePosition = strchr(lineBuffer, '\n'); + + if (newlinePosition != NULL) { + bufferLength = newlinePosition - lineBuffer + 1; + strncpy(destString + destStringIndex, lineBuffer, destStringSize - destStringIndex - 1); + destStringIndex += bufferLength; + srcStringIndex += bufferLength; + lineIndex = 0; + continue; + } + + // If no newlines were found, then find the last space in this line and copy to that point + lastSpacePosition = strrchr(lineBuffer, ' '); + + if (lastSpacePosition == NULL) { + // If NULL is returned here, then there are no spaces in this line. In this case, insert + // a hyphen at the end of the line and start a new line. Also, we need to leave room + // for the newline, so subtract 2 from the total buffer length. + bufferLength = lineLength - lineIndex - 1; + strncpy(destString + destStringIndex, lineBuffer, bufferLength); + destString[lineLength - 1] = '-'; + // Move the destination string index ahead 1 to account for the hyphen, and the source + // string index back one to copy the last character from the previous line. + destStringIndex++; + srcStringIndex--; + } else { + bufferLength = lastSpacePosition - lineBuffer; + strncpy(destString + destStringIndex, lineBuffer, bufferLength); + } + + // Increase string indexes and continue looping + destStringIndex += bufferLength; + destString[destStringIndex++] = '\n'; + srcStringIndex += bufferLength + 1; + lineIndex = 0; + } + + free(lineBuffer); +} + +CharString charStringWrap(const CharString srcString, unsigned int indentSize) +{ + CharString destString; + + if (srcString == NULL) { + return NULL; + } + + // Allocate 2x as many characters as needed to avoid buffer overflows. + // Since this method is only used in "user-friendly" cases, it's ok to be + // a bit wasteful in the name of avoiding memory corruption. Therefore this + // function should *not* used for regular logging or text output. + destString = newCharStringWithCapacity(srcString->capacity * 2); + _charStringWrap(srcString->data, destString->data, destString->capacity, indentSize, TERMINAL_LINE_LENGTH); + return destString; +} + +void freeCharString(CharString self) +{ + if (self != NULL) { + free(self->data); + free(self); + } +} diff --git a/source/base/CharString.h b/source/base/CharString.h new file mode 100644 index 0000000..ff0e282 --- /dev/null +++ b/source/base/CharString.h @@ -0,0 +1,182 @@ +// +// CharString.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_CharString_h +#define MrsWatson_CharString_h + +#include <stdlib.h> + +#include "base/LinkedList.h" +#include "base/Types.h" + +static const size_t kCharStringLengthDefault = 256; +static const size_t kCharStringLengthShort = 32; +static const size_t kCharStringLengthLong = 8192; + +#define EMPTY_STRING "" +#define DEFAULT_INDENT_SIZE 2 +#ifndef TERMINAL_LINE_LENGTH +#define TERMINAL_LINE_LENGTH 80 +#endif + +#if WINDOWS +#define strncasecmp _strnicmp +#elif LINUX +#include <strings.h> +#endif + +typedef struct { + size_t capacity; + char *data; +} CharStringMembers; +typedef CharStringMembers *CharString; + +/** + * @return Create a new CharString instance + */ +CharString newCharString(void); + +/** + * @param length Number of characters + * @return Create a new CharString instance + */ +CharString newCharStringWithCapacity(size_t length); + +/** + * Create a new CharString from a C-String + * @param string C-String to use (copied to contents) + * @return New CharString instance + */ +CharString newCharStringWithCString(const char *string); + +/** + * Append another CharString to this instance, truncating if necessary. Appending + * a string to itself will result in undefined behavior. + * @param self + * @param string String to append + */ +void charStringAppend(CharString self, const CharString string); + +/** + * Append a C-String to this CharString. Appending a string to itself will result + * in undefined behavior. + * @param self + * @param string NULL-terminated string to append + */ +void charStringAppendCString(CharString self, const char *string); + +/** + * Copy the contents of another CharString to this one + * @param self + * @param string String to copy + */ +void charStringCopy(CharString self, const CharString string); + +/** + * Copy the contents of a C-String to this one + * @param self + * @param string NULL-terminated string to copy + */ +void charStringCopyCString(CharString self, const char *string); + +/** + * Clear a string's contents + * @param self + */ +void charStringClear(CharString self); + +/** + * @param self + * @return True if string is NULL or empty (ie, ""), false otherwise + */ +boolByte charStringIsEmpty(const CharString self); + +/** + * Test for string equality + * @param self + * @param string String to compare to + * @param caseInsensitive True for a case-insensitive comparison + * @return True if the strings are equal, false otherwise + */ +boolByte charStringIsEqualTo(const CharString self, const CharString string, boolByte caseInsensitive); + +/** + * Test for string equality + * @param self + * @param string NULL-terminated C-String to compare to + * @param caseInsensitive True for a case-insensitive comparison + * @return True if the strings are equal, false otherwise + */ +boolByte charStringIsEqualToCString(const CharString self, const char *string, boolByte caseInsensitive); + +/** + * Test if a given character in this string is a letter. This function does not + * do any bounds checking, and "letter" means an ASCII character. + * @param self + * @param index String index. If beyond the bounds of the string, undefined + * behavior will result. + * @return True if an ASCII character + */ +boolByte charStringIsLetter(const CharString self, const size_t index); + +/** + * Test if a given character in this string is a number. This function does not + * do any bounds checking. + * @param self + * @param index String index. If beyond the bounds of the string, undefined + * behavior will result. + * @return True is character is a number + */ +boolByte charStringIsNumber(const CharString self, const size_t index); + +/** + * Break a string up into a list of strings based on a delimeter character. The + * list of strings do not include the delimiter character itself. + * @param self + * @param delimiter Delimiter character (cannot be NULL) + * @return List of strings (may be an empty list if the delimiter was not found), + * or NULL if invalid input given. + */ +LinkedList charStringSplit(const CharString self, const char delimiter); + +/** + * Wrap a string to fix nicely within the width of a terminal window. This + * function is a bit inefficient, and should not be called in performance + * crucial code areas. + * @param self + * @param indentSize Initial indenting size to use + * @return The copy of the string line wrapped to fit in a terminal window + */ +CharString charStringWrap(const CharString self, unsigned int indentSize); + +/** + * Free a CharStar and its contents + * @param self + */ +void freeCharString(CharString self); + +#endif diff --git a/source/base/Endian.c b/source/base/Endian.c new file mode 100644 index 0000000..5eb3c81 --- /dev/null +++ b/source/base/Endian.c @@ -0,0 +1,93 @@ +// +// Endian.c - MrsWatson +// Created by Nik Reiman on 10 Dec 14. +// Copyright (c) 2014 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 "base/Endian.h" +#include "base/PlatformInfo.h" + +unsigned short flipShortEndian(const unsigned short value) +{ + return (value << 8) | (value >> 8); +} + +unsigned short convertBigEndianShortToPlatform(const unsigned short value) +{ + if (platformInfoIsLittleEndian()) { + return (value << 8) | (value >> 8); + } else { + return value; + } +} + +unsigned int convertBigEndianIntToPlatform(const unsigned int value) +{ + if (platformInfoIsLittleEndian()) { + return (value << 24) | ((value << 8) & 0x00ff0000) | ((value >> 8) & 0x0000ff00) | (value >> 24); + } else { + return value; + } +} + +unsigned int convertLittleEndianIntToPlatform(const unsigned int value) +{ + if (!platformInfoIsLittleEndian()) { + return (value << 24) | ((value << 8) & 0x00ff0000) | ((value >> 8) & 0x0000ff00) | (value >> 24); + } else { + return value; + } +} + +unsigned short convertByteArrayToUnsignedShort(const byte *value) +{ + if (platformInfoIsLittleEndian()) { + return ((value[1] << 8) & 0x0000ff00) | value[0]; + } else { + return ((value[0] << 8) & 0x0000ff00) | value[1]; + } +} + +unsigned int convertByteArrayToUnsignedInt(const byte *value) +{ + if (platformInfoIsLittleEndian()) { + return ((value[3] << 24) | ((value[2] << 16) & 0x00ff0000) | + ((value[1] << 8) & 0x0000ff00) | value[0]); + } else { + return ((value[0] << 24) | ((value[1] << 16) & 0x00ff0000) | + ((value[2] << 8) & 0x0000ff00) | value[0]); + } +} + +float convertBigEndianFloatToPlatform(const float value) +{ + float result = 0.0f; + byte *floatToConvert = (byte *)&value; + byte *floatResult = (byte *)&result; + floatResult[0] = floatToConvert[3]; + floatResult[1] = floatToConvert[2]; + floatResult[2] = floatToConvert[1]; + floatResult[3] = floatToConvert[0]; + return result; +} diff --git a/source/base/Endian.h b/source/base/Endian.h new file mode 100644 index 0000000..27be1c7 --- /dev/null +++ b/source/base/Endian.h @@ -0,0 +1,89 @@ +// +// Endian.h - MrsWatson +// Created by Nik Reiman on 10 Dec 14. +// Copyright (c) 2014 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_Endian_h +#define MrsWatson_Endian_h + +#include "base/Types.h" + +/** + * Flip bytes for a short value. This does not take into account the host's + * endian-ness. + * @param value Short integer + * @return Flipped version of short integer + */ +unsigned short flipShortEndian(const unsigned short value); + +/** + * Convert a big endian short integer to the platform's native endian-ness. + * @param value Short integer + * @return Flipped version of short integer. If the host is big endian, the same + * value will be returned. + */ +unsigned short convertBigEndianShortToPlatform(const unsigned short value); + +/** + * Convert a big endian integer to the platform's native endian-ness. + * @param value Integer + * @return Flipped version of integer. If the host is big endian, the same value + * will be returned. + */ +unsigned int convertBigEndianIntToPlatform(const unsigned int value); + +/** + * Convert a little endian short integer to the platform's native endian-ness. + * @param value Short integer + * @return Flipped version of short integer. If the host is little endian, the + * same value will be returned. + */ +unsigned int convertLittleEndianIntToPlatform(const unsigned int value); + +/** + * Convert a big endian floating-point value to the platform's native endian-ness. + * @param value Floating-point number + * @return Flipped version of float. If the host is big endian, the same value + * will be returned. + */ +float convertBigEndianFloatToPlatform(const float value); + +/** + * Convert raw bytes to an unsigned short value, taking into account the host's + * endian-ness. + * @param value A buffer which holds at least two bytes + * @return Unsigned short integer + */ +unsigned short convertByteArrayToUnsignedShort(const byte *value); + +/** + * Convert raw bytes to an unsigned int value, taking into account the host's + * endian-ness. + * @param value A buffer which holds at least four bytes + * @return Unsigned short integer + */ +unsigned int convertByteArrayToUnsignedInt(const byte *value); + +#endif diff --git a/source/base/File.c b/source/base/File.c new file mode 100644 index 0000000..f420eb6 --- /dev/null +++ b/source/base/File.c @@ -0,0 +1,922 @@ +// +// File.c - MrsWatson +// Created by Nik Reiman on 09 Dec 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. +// + +// Must be declared before stdlib, shouldn't have any effect on Windows builds +#define _XOPEN_SOURCE 700 +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#include "base/File.h" +#include "logging/EventLogger.h" + +#if WINDOWS +#include <Windows.h> +#include <Shellapi.h> +#elif UNIX +#if LINUX +#include <errno.h> +#elif MACOSX +#include <mach-o/dyld.h> +#endif + +#include <dirent.h> +#include <ftw.h> +#include <unistd.h> +#endif + +static const int kFileNameStringLength = 1024; +static const int kFileMaxRecursionDepth = 32; +static const char *kFileNameInvalidCharacters = "*:?<>|"; + +static boolByte _isAbsolutePath(const CharString path) +{ +#if WINDOWS + + if (path->capacity > 3) { + // Check for strings which start with a drive letter, ie C:\file + if (path->data[1] == ':' && path->data[2] == PATH_DELIMITER) { + return true; + } + // Check for network paths, ie \\SERVER\file + else if (path->data[0] == PATH_DELIMITER && path->data[1] == PATH_DELIMITER) { + return true; + } + } + +#else + + if (path->capacity > 1 && path->data[0] == PATH_DELIMITER) { + return true; + } + +#endif + return false; +} + +static CharString _convertRelativePathToAbsolute(const CharString path) +{ + CharString result = newCharStringWithCapacity(kFileNameStringLength); + CharString currentDirectory = fileGetCurrentDirectory(); + snprintf(result->data, result->capacity, "%s%c%s", currentDirectory->data, PATH_DELIMITER, path->data); + freeCharString(currentDirectory); + return result; +} + +static CharString _buildAbsolutePath(const CharString parentDir, const CharString filename) +{ + CharString result = NULL; + CharString parentDirAbsPath = NULL; + + if (parentDir == NULL || charStringIsEmpty(parentDir)) { + logWarn("Attempt to build absolute path with empty directory"); + return result; + } else if (filename == NULL || charStringIsEmpty(filename)) { + logWarn("Attempt to build absolute path with empty file"); + return result; + } else if (_isAbsolutePath(filename)) { + result = newCharStringWithCapacity(kFileNameStringLength); + charStringCopy(result, filename); + return result; + } + + if (_isAbsolutePath(parentDir)) { + parentDirAbsPath = newCharStringWithCapacity(kFileNameStringLength); + charStringCopy(parentDirAbsPath, parentDir); + } else { + parentDirAbsPath = _convertRelativePathToAbsolute(parentDir); + } + + result = newCharStringWithCapacity(kFileNameStringLength); + snprintf(result->data, result->capacity, "%s%c%s", parentDirAbsPath->data, PATH_DELIMITER, filename->data); + freeCharString(parentDirAbsPath); + return result; +} + +static boolByte _isDirectory(const CharString path) +{ + boolByte result = false; + +#if UNIX + struct stat buffer; + + if (stat(path->data, &buffer) == 0) { + result = (boolByte)S_ISDIR(buffer.st_mode); + } + +#elif WINDOWS + DWORD fileAttributes = GetFileAttributesA((LPCSTR)path->data); + + if (fileAttributes != INVALID_FILE_ATTRIBUTES) { + result = (boolByte)(fileAttributes & FILE_ATTRIBUTE_DIRECTORY); + } + +#endif + + return result; +} + +static boolByte _pathContainsInvalidChars(const CharString path) +{ + size_t i = 0; +#if WINDOWS + + // The colon is not allowed in pathnames (even on Windows), however it is + // present in absolute pathnames for the drive letter. Thus we must skip + // the first 3 characters of the path when dealing with absolute paths on + // this platform. + if (_isAbsolutePath(path)) { + i = 3; + } + +#endif + + if (path != NULL) { + for (i = 0; i < strlen(kFileNameInvalidCharacters); ++i) { + if (strchr(path->data, kFileNameInvalidCharacters[i]) != NULL) { + return true; + } + } + } + + return false; +} + +File newFile(void) +{ + File result = (File)malloc(sizeof(FileMembers)); + result->absolutePath = newCharStringWithCapacity(kFileNameStringLength); + result->fileType = kFileTypeInvalid; + result->_fileHandle = NULL; + result->_openMode = kFileOpenModeClosed; + return result; +} + +File newFileWithPath(const CharString path) +{ + File result = newFile(); + CharString currentDirectory = NULL; + CharString absolutePath = NULL; + + if (path != NULL && !charStringIsEmpty(path)) { + if (_isAbsolutePath(path)) { + charStringCopy(result->absolutePath, path); + } else if (_pathContainsInvalidChars(path)) { + logWarn("Could not create file/directory with name '%s'", path->data); + freeFile(result); + return NULL; + } else { + currentDirectory = fileGetCurrentDirectory(); + + if (currentDirectory == NULL) { + logWarn("Could not create file relative to current directory"); + freeFile(result); + return NULL; + } + + absolutePath = _buildAbsolutePath(currentDirectory, path); + charStringCopy(result->absolutePath, absolutePath); + freeCharString(currentDirectory); + freeCharString(absolutePath); + } + + if (fileExists(result)) { + result->fileType = _isDirectory(result->absolutePath) ? kFileTypeDirectory : kFileTypeFile; + } + } + + return result; +} + +File newFileWithPathCString(const char *path) +{ + File result = NULL; + + if (path != NULL) { + CharString pathString = newCharStringWithCString(path); + result = newFileWithPath(pathString); + freeCharString(pathString); + } + + return result; +} + +File newFileWithParent(const File parent, const CharString path) +{ + File result = NULL; + CharString absolutePath = NULL; + + if (parent == NULL) { + logWarn("Cannot create file/directory with null parent"); + return NULL; + } else if (!fileExists(parent)) { + logWarn("Cannot create file/directory under non-existent parent"); + return NULL; + } else if (!_isDirectory(parent->absolutePath)) { + logWarn("Cannot create file/directory under non-directory parent '%s'", parent->absolutePath->data); + return NULL; + } else if (path == NULL || charStringIsEmpty(path)) { + logWarn("Cannot create empty file/directory name with parent"); + return NULL; + } else if (_pathContainsInvalidChars(path)) { + logWarn("Could not create file/directory with name '%s'", path->data); + freeFile(result); + return NULL; + } else if (_isAbsolutePath(path)) { + logWarn("Cannot create file '%s' with absolute directory under a parent", path->data); + freeFile(result); + return NULL; + } + + result = newFile(); + absolutePath = _buildAbsolutePath(parent->absolutePath, path); + charStringCopy(result->absolutePath, absolutePath); + freeCharString(absolutePath); + + if (fileExists(result)) { + result->fileType = _isDirectory(result->absolutePath) ? kFileTypeDirectory : kFileTypeFile; + } + + return result; +} + +boolByte fileExists(File self) +{ +#if WINDOWS + // Visual Studio's compiler is not C99 compliant, so variable declarations + // need to be at the top. + unsigned long fileAttributes; +#endif + + // Normally, we don't do paranoid sanity checks for self != NULL, but in this + // case it's useful since the old file API had a different calling convention + // which supported calling fileExists() with NULL. + if (self == NULL || self->absolutePath == NULL) { + return false; + } + +#if UNIX + struct stat fileStat; + boolByte result = (boolByte)(stat(self->absolutePath->data, &fileStat) == 0); + return result; + +#elif WINDOWS + fileAttributes = GetFileAttributesA((LPCSTR)self->absolutePath->data); + + if (fileAttributes == INVALID_FILE_ATTRIBUTES) { + return false; + } + + return true; +#else + return false; +#endif +} + +boolByte fileCreate(File self, const FileType fileType) +{ + if (fileExists(self)) { + return false; + } else if (charStringIsEmpty(self->absolutePath)) { + logWarn("Attempt to create file/directory without a path"); + return false; + } + + switch (fileType) { + case kFileTypeFile: + self->_fileHandle = fopen(self->absolutePath->data, "wb"); + + if (self->_fileHandle != NULL) { + self->fileType = kFileTypeFile; + self->_openMode = kFileOpenModeWrite; + return true; + } + + break; + + case kFileTypeDirectory: +#if UNIX + if (mkdir(self->absolutePath->data, 0755) == 0) { +#elif WINDOWS + + if (CreateDirectoryA(self->absolutePath->data, NULL)) { +#endif + self->fileType = kFileTypeDirectory; + return true; + } + + break; + + default: + break; + } + + return false; +} + +static File _copyFileToDirectory(File self, const File destination) { + File result = NULL; + CharString basename = NULL; + void *selfContents = NULL; + size_t selfSize = 0; + + // Close and re-open file to make sure that we start reading at the beginning + fileClose(self); + self->_fileHandle = fopen(self->absolutePath->data, "rb"); + + if (self->_fileHandle == NULL) { + logError("Could not re-open file for reading during copy"); + return NULL; + } + + basename = fileGetBasename(self); + + if (basename == NULL) { + logError("Could not get basename during copy"); + } else { + result = newFileWithParent(destination, basename); + freeCharString(basename); + + if (result == NULL) { + logError("Could not create destination file object"); + return NULL; + } + + if (!fileCreate(result, kFileTypeFile)) { + logError("Could not create destination file"); + freeFile(result); + return NULL; + } + + selfSize = fileGetSize(self); + + if (selfSize == 0) { + // If the source file is empty, then creating the result file is good + // enough and we can return here. + logDebug("Source file during copy is 0 bytes"); + return result; + } + + selfContents = fileReadBytes(self, selfSize); + + if (selfContents == NULL) { + logError("Could not read source file during copy"); + freeFile(result); + return NULL; + } + + if (!fileWriteBytes(result, selfContents, selfSize)) { + logError("Could not copy source file"); + freeFile(result); + return NULL; + } + } + + return result; +} + +static File _copyDirectoryToDirectory(File self, const File destination) { + File result = NULL; + // Get the basename first, because if it fails then there's no point in doing + // the actual copy. + CharString basename = fileGetBasename(self); +#if WINDOWS + SHFILEOPSTRUCT fileOperation; +#endif + + if (basename == NULL) { + logError("Could not get basename from directory during copy"); + return NULL; + } + +#if UNIX + // Using nftw() is a real pain here, because the recursive callback will start + // at the top of the tree and work back up, meaning that for each file we'd + // need to recursively mkdir the basename, etc. + // So this is a bit lazy but actually more foolproof + CharString copyCommand = newCharString(); + snprintf(copyCommand->data, copyCommand->capacity, "/bin/cp -r \"%s\" \"%s\"", + self->absolutePath->data, destination->absolutePath->data); + int systemResult = system(copyCommand->data); + freeCharString(copyCommand); + + if (WEXITSTATUS(systemResult) == 0) { + result = newFileWithParent(destination, basename); + + if (result == NULL) { + logError("Copied '%s' to '%s', but could not create a File object for the result", + self->absolutePath->data, destination->absolutePath->data); + freeCharString(basename); + return NULL; + } + } else { + logError("Could not copy directory '%s' to '%s'", + self->absolutePath->data, destination->absolutePath->data); + } + +#elif WINDOWS + memset(&fileOperation, 0, sizeof(fileOperation)); + fileOperation.wFunc = FO_COPY; + fileOperation.pFrom = self->absolutePath->data; + fileOperation.pTo = destination->absolutePath->data; + fileOperation.fFlags = FOF_NO_UI; + + if (SHFileOperationA(&fileOperation) == 0) { + result = newFileWithParent(destination, basename); + } + +#endif + + freeCharString(basename); + return result; +} + +File fileCopyTo(File self, const File destination) { + File result = NULL; + + if (destination == NULL || !fileExists(destination)) { + logError("Attempt to copy file/directory to invalid destination"); + return NULL; + } else if (!_isDirectory(destination->absolutePath)) { + logError("Attempt to copy file/directory to non-directory destination"); + return NULL; + } else if (!fileExists(self)) { + logError("Attempt to copy non-existent file"); + return NULL; + } + + switch (self->fileType) { + case kFileTypeFile: + result = _copyFileToDirectory(self, destination); + break; + + case kFileTypeDirectory: + result = _copyDirectoryToDirectory(self, destination); + break; + + default: + logError("Attempt to copy invalid file type"); + break; + } + + return result; +} + +#if UNIX +static int _removeCallback(const char *path, const struct stat * fileState, int typeflag, struct FTW * ftwBuffer) { + int result = remove(path); + + if (result != 0) { + logWarn("Could not remove '%s'", path); + } + + return result; +} +#endif + +boolByte fileRemove(File self) { + boolByte result = false; +#if WINDOWS + SHFILEOPSTRUCTA fileOperation = {0}; +#endif + + if (fileExists(self)) { + switch (self->fileType) { + case kFileTypeFile: + // Yes, this seems a bit silly, but otherwise we threaten to leak resources + fileClose(self); + result = (boolByte)(remove(self->absolutePath->data) == 0); + break; + + case kFileTypeDirectory: +#if UNIX + result = (boolByte)(nftw(self->absolutePath->data, _removeCallback, kFileMaxRecursionDepth, FTW_DEPTH | FTW_PHYS) == 0); +#elif WINDOWS + memset(&fileOperation, 0, sizeof(fileOperation)); + fileOperation.wFunc = FO_DELETE; + fileOperation.pFrom = self->absolutePath->data; + fileOperation.pTo = NULL; + fileOperation.fFlags = FOF_NO_UI | FOF_NOCONFIRMATION | FOF_SILENT; + result = (SHFileOperationA(&fileOperation) == 0); +#endif + break; + + default: + break; + } + } + + if (result) { + self->fileType = kFileTypeInvalid; + } + + return result; +} + +LinkedList fileListDirectory(File self) { + LinkedList items = newLinkedList(); + CharString path; + File file; + +#if UNIX + DIR *directoryPtr = opendir(self->absolutePath->data); + + if (directoryPtr == NULL) { + freeLinkedList(items); + return 0; + } + + struct dirent *entry; + + while ((entry = readdir(directoryPtr)) != NULL) { + if (entry->d_name[0] != '.') { + path = newCharStringWithCString(entry->d_name); + file = newFileWithParent(self, path); + linkedListAppend(items, file); + freeCharString(path); + } + } + + closedir(directoryPtr); + +#elif WINDOWS + WIN32_FIND_DATAA findData; + HANDLE findHandle; + CharString searchString = newCharStringWithCapacity(kFileNameStringLength); + + snprintf(searchString->data, searchString->capacity, "%s\\*", self->absolutePath->data); + findHandle = FindFirstFileA((LPCSTR)(searchString->data), &findData); + freeCharString(searchString); + + if (findHandle == INVALID_HANDLE_VALUE) { + freeLinkedList(items); + return 0; + } + + do { + if (findData.cFileName[0] != '.') { + path = newCharStringWithCString(findData.cFileName); + file = newFileWithParent(self, path); + linkedListAppend(items, file); + freeCharString(path); + } + } while (FindNextFileA(findHandle, &findData) != 0); + + FindClose(findHandle); + +#else + logUnsupportedFeature("List directory contents"); +#endif + + return items; +} + +size_t fileGetSize(File self) { + size_t result = 0; + +#if UNIX + struct stat fileStat; + + if (self->absolutePath == NULL) { + return 0; + } + + if (stat(self->absolutePath->data, &fileStat) == 0) { + if (S_ISREG(fileStat.st_mode)) { + // Yes, this will result in a loss of precision, but both freah and fwrite + // take a size_t argument, which is what this function is mostly used for. + result = (size_t)fileStat.st_size; + } + } + +#elif WINDOWS + HANDLE handle = CreateFileA(self->absolutePath->data, GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (handle != INVALID_HANDLE_VALUE) { + result = GetFileSize(handle, NULL); + CloseHandle(handle); + } + +#else + logUnsupportedFeature("Get file size"); +#endif + + return result; +} + +CharString fileReadContents(File self) { + CharString result = NULL; + size_t fileSize = 0; + size_t itemsRead = 0; + + if (self->fileType != kFileTypeFile) { + logError("Attempt to read contents from non-file object '%s'", self->absolutePath->data); + return NULL; + } + + // Windows has problems opening files multiple times (which is done internally in + // fileGetSize() via CreateFile), so we must close the file first, calculate size, + // and then reopen it for reading. + fileClose(self); + fileSize = (size_t)fileGetSize(self); + fileClose(self); + + if (self->_fileHandle == NULL) { + self->_fileHandle = fopen(self->absolutePath->data, "rb"); + + if (self->_fileHandle == NULL) { + logError("Could not open '%s' for reading", self->absolutePath->data); + return NULL; + } else { + self->_openMode = kFileOpenModeRead; + } + } + + if (fileSize > 0) { + result = newCharStringWithCapacity(fileSize + 1); + itemsRead = fread(result->data, 1, fileSize, self->_fileHandle); + + if (itemsRead != fileSize) { + logError("Expected to read %d items, read %d items instead", fileSize, itemsRead); + } + } + + return result; +} + +LinkedList fileReadLines(File self) { + LinkedList result = NULL; + CharString line = NULL; + boolByte done = false; + char *newline = NULL; + + if (self->fileType != kFileTypeFile) { + logError("Attempt to read contents from non-file object '%s'", self->absolutePath->data); + return NULL; + } + + if (self->_openMode != kFileOpenModeRead && self->_fileHandle != NULL) { + fileClose(self); + } + + if (self->_fileHandle == NULL) { + self->_fileHandle = fopen(self->absolutePath->data, "rb"); + + if (self->_fileHandle == NULL) { + logError("Could not open '%s' for reading", self->absolutePath->data); + return NULL; + } else { + self->_openMode = kFileOpenModeRead; + } + } + + result = newLinkedList(); + + while (!done) { + line = newCharString(); + + if (fgets(line->data, (int)line->capacity, self->_fileHandle) == NULL) { + freeCharString(line); + done = true; + } else { + newline = strrchr(line->data, '\n'); + + if (newline != NULL) { + *newline = '\0'; + } + + // Also trim these characters, in case the file has Windows-style newlines + newline = strrchr(line->data, '\r'); + + if (newline != NULL) { + *newline = '\0'; + } + + linkedListAppend(result, line); + } + } + + return result; +} + +void *fileReadBytes(File self, size_t numBytes) { + void *result = NULL; + size_t itemsRead = 0; + + if (numBytes == 0) { + // Here we log an error (instead of an info message, as is done with the + // same type of error during writes) because the caller is probably + // expecting a non-NULL object back. + logError("Attempt to read 0 bytes from file"); + return NULL; + } + + if (self->fileType != kFileTypeFile) { + logError("Attempt to read bytes from non-file object '%s'", self->absolutePath->data); + return NULL; + } + + if (self->_openMode != kFileOpenModeRead && self->_fileHandle != NULL) { + fileClose(self); + } + + if (self->_fileHandle == NULL) { + self->_fileHandle = fopen(self->absolutePath->data, "rb"); + + if (self->_fileHandle == NULL) { + logError("Could not open '%s' for reading", self->absolutePath->data); + return NULL; + } else { + self->_openMode = kFileOpenModeRead; + } + } + + if (numBytes > 0) { + result = malloc(numBytes + 1); + memset(result, 0, numBytes + 1); + itemsRead = fread(result, 1, numBytes, self->_fileHandle); + + if (itemsRead != numBytes) { + logError("Expected to read %d items, read %d items instead", numBytes, itemsRead); + } + } + + return result; +} + +boolByte fileWrite(File self, const CharString data) { + return fileWriteBytes(self, data->data, strlen(data->data)); +} + +boolByte fileWriteBytes(File self, const void *data, size_t numBytes) { + size_t itemsWritten = 0; + + if (numBytes == 0) { + // Here we only log an info message, as this isn't such a critical error, + // but it is rather weird. + logInfo("Attempt to write 0 bytes to file"); + return false; + } + + if (!fileExists(self)) { + logError("Attempt to write to non-existent file"); + return false; + } + + if (self->_openMode != kFileOpenModeWrite && self->_fileHandle != NULL) { + fileClose(self); + } + + if (self->_fileHandle == NULL) { + self->_fileHandle = fopen(self->absolutePath->data, "wb"); + + if (self->_fileHandle == NULL) { + logError("Could not open '%s' for writing", self->absolutePath->data); + return false; + } else { + self->_openMode = kFileOpenModeWrite; + } + } + + itemsWritten = fwrite(data, 1, numBytes, self->_fileHandle); + return (boolByte)(itemsWritten == numBytes); +} + +CharString fileGetBasename(File self) { + CharString result = NULL; + char *lastDelimiter = NULL; + + if (self->absolutePath == NULL || charStringIsEmpty(self->absolutePath)) { + return NULL; + } + + lastDelimiter = strrchr(self->absolutePath->data, PATH_DELIMITER); + + if (lastDelimiter == NULL) { + result = newCharStringWithCString(self->absolutePath->data); + } else { + result = newCharStringWithCString(lastDelimiter + 1); + } + + return result; +} + +File fileGetParent(File self) { + File result = NULL; + CharString path = NULL; + char *lastDelimiter = NULL; + + if (self->absolutePath == NULL || charStringIsEmpty(self->absolutePath)) { + return NULL; + } + + lastDelimiter = strrchr(self->absolutePath->data, PATH_DELIMITER); + + if (lastDelimiter == NULL) { + result = newFileWithPath(self->absolutePath); + } else { + path = newCharStringWithCapacity(kFileNameStringLength); + strncpy(path->data, self->absolutePath->data, lastDelimiter - self->absolutePath->data); + result = newFileWithPath(path); + freeCharString(path); + } + + return result; +} + +CharString fileGetExtension(File self) { + CharString basename = fileGetBasename(self); + CharString result = NULL; + char *dot = NULL; + + if (basename == NULL || charStringIsEmpty(basename)) { + freeCharString(basename); + return NULL; + } + + dot = strrchr(basename->data, '.'); + + if (dot != NULL) { + result = newCharStringWithCString(dot + 1); + } + + freeCharString(basename); + return result; +} + +CharString fileGetExecutablePath(void) { + CharString executablePath = newCharString(); +#if LINUX + ssize_t result = readlink("/proc/self/exe", executablePath->data, executablePath->capacity); + + if (result < 0) { + logWarn("Could not find executable path, %s", stringForLastError(errno)); + return NULL; + } + +#elif MACOSX + _NSGetExecutablePath(executablePath->data, (uint32_t *)&executablePath->capacity); +#elif WINDOWS + GetModuleFileNameA(NULL, executablePath->data, (DWORD)executablePath->capacity); +#endif + return executablePath; +} + +CharString fileGetCurrentDirectory(void) { + CharString currentDirectory = newCharString(); +#if UNIX + + if (getcwd(currentDirectory->data, currentDirectory->capacity) == NULL) { + logError("Could not get current working directory"); + freeCharString(currentDirectory); + return NULL; + } + +#elif WINDOWS + GetCurrentDirectoryA((DWORD)currentDirectory->capacity, currentDirectory->data); +#endif + return currentDirectory; +} + +void fileClose(File self) { + if (self->_fileHandle != NULL && self->fileType == kFileTypeFile) { + fflush(self->_fileHandle); + fclose(self->_fileHandle); + self->_fileHandle = NULL; + self->_openMode = kFileOpenModeClosed; + } +} + +void freeFile(File self) { + if (self) { + fileClose(self); + freeCharString(self->absolutePath); + free(self); + } +} diff --git a/source/base/File.h b/source/base/File.h new file mode 100644 index 0000000..9c30924 --- /dev/null +++ b/source/base/File.h @@ -0,0 +1,262 @@ +// +// File.h - MrsWatson +// Created by Nik Reiman on 09 Dec 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_File_h +#define MrsWatson_File_h + +#include <stdio.h> +#include <stdlib.h> +#include "base/CharString.h" +#include "base/LinkedList.h" +#include "base/Types.h" + +#if UNIX +#define PATH_DELIMITER '/' +#define ROOT_DIRECTORY "/" +#elif WINDOWS +#define PATH_DELIMITER '\\' +#define ROOT_DIRECTORY "C:\\" +#endif + +typedef enum { + kFileTypeFile, + kFileTypeDirectory, + kFileTypeInvalid +} FileType; + +typedef enum { + kFileOpenModeClosed, + kFileOpenModeRead, + kFileOpenModeWrite, + kFileOpenModeInvalid +} FileOpenMode; + +typedef struct { + CharString absolutePath; + FileType fileType; + + /** Private */ + FILE *_fileHandle; + /** Private */ + FileOpenMode _openMode; +} FileMembers; +typedef FileMembers *File; + +/** + * @return New empty file object + */ +File newFile(void); + +/** + * Create a new file object which points to a given path. If something exists at + * the path, then this object will be initialized with the correct type. + * @param path Object path. If this is not an absolute path, it is assumed to + * be relative to the current directory. + * @return New file object + */ +File newFileWithPath(const CharString path); + +/** + * Create a new file object which points to a given path. If something exists at + * the path, then this object will be initialized with the correct type. + * @param path Object path. If this is not an absolute path, it is assumed to + * be relative to the current directory. + * @return New file object + */ +File newFileWithPathCString(const char *path); + +/** + * Create a new file object which points to a path under another directory. If + * something exists at the path, then this object will be initialized with the + * correct type. + * @param parent Directory which the object is located under. If parent does not + * exist or is not a directory, this will return NULL. + * @param path Object path, relative to the parent. This may not be an absolute + * path. + * @return New file object, or NULL if this object could not be created. + */ +File newFileWithParent(const File parent, const CharString path); + +/** + * Check to see if the path referenced by this object exists on disk. + * @param self + * @return True if the object exists on disk + */ +boolByte fileExists(File self); + +/** + * Create a file object on disk of the given type. This call will fail if an + * object already exists on the disk at the path pointed to by this file. + * @param self + * @param fileType Type of object to create + * @return True if the object was created + */ +boolByte fileCreate(File self, const FileType fileType); + +/** + * Copy a file object to a new location. If this file is a directory, it will + * be copied recursively. This call will fail if the destination does not exist. + * @param self + * @param destination Target destination to copy objects to. + * @return File object for copied object, or NULL if it could not be copied. The + * caller must free this object when finished with it. + */ +File fileCopyTo(File self, const File destination); + +/** + * Remove the file from disk. If this object is a directory, then it will be + * removed recursively. + * Note: On Windows, you must make sure that all files inside of this directory + * are closed, otherwise the operation will fail. + * @param self + * @return True if the object could be removed + */ +boolByte fileRemove(File self); + +/** + * List the contents of a directory, non-recursively. Special entries such as + * "." and ".." are not included in the listing, nor are hidden dotfiles. + * @param self + * @return A linked list of File objects, or NULL on error. The caller must + * release this memory using freeLinkedListAndItems. + */ +LinkedList fileListDirectory(File self); + +/** + * Return the size of a file in bytes. + * @param self + * @return Number of bytes in the file, or 0 if this object does not exist or + * is not a file. + */ +size_t fileGetSize(File self); + +/** + * Read the contents of an entire file into a string. If the file had previously + * been opened for writing, then it will be flushed, closed, and reopened for + * reading. + * @param self + * @return CharString containing file contents, or NULL if an error occurred. + */ +CharString fileReadContents(File self); + +/** + * Read the contents of an entire file line-by-line. The lines are returned in + * a linked list, which the caller must free when finished (and should use the + * freeLinkedListAndItems() method with freeCharString as the second argument). + * @param self + * @return LinkedList containing a CharString for each line, or NULL if an + * error occurred. Note that the newline character is removed from the lines. + */ +LinkedList fileReadLines(File self); + +/** + * Read part of a binary file into a raw byte array. If end of file is reached, + * then an array of numBytes is still delivered with the extra bytes initialized + * to 0. This is most useful when reading raw PCM data from disk. If the file + * had previously been opened for writing, then it will be flushed, closed, and + * reopened for reading. + * @param self + * @param numBytes Number of bytes to deliver, at most. If this is greater than + * the size of the actual file, then the entire file will be read. If this is + * zero, then NULL will be returned. + * @return An initialized array of numBytes bytes with the data, or NULL if an + * error occurred. + */ +void *fileReadBytes(File self, size_t numBytes); + +/** + * Write a string to file. The first time this function is called, the file will + * be opened for write mode, truncating any data present there if the file + * already exists. File buffers are not flushed until fileClose() is called. + * @param self + * @param data String to write + * @return True if the data was written + */ +boolByte fileWrite(File self, const CharString data); + +/** + * Write a binary byte array to disk. The first time this function is called, + * the file will be opened for write mode, truncating any data present there if + * the file already exists. File buffers are not flushed until fileClose() is + * called. + * @param self + * @param data Binary data to write + * @param numBytes Number of bytes to write + * @return True if the data could be written + */ +boolByte fileWriteBytes(File self, const void *data, size_t numBytes); + +/** + * Get the file basename, for example "/foo/bar" would return "bar" (regardless + * of whether "bar" is a file or directory). + * @param self + * @return CharString containing basename. The caller must free this memory when + * finished with it. + */ +CharString fileGetBasename(File self); + +/** + * Get a file's parent directory. + * @param self + * @return File representing the parent directory. The caller must free this + * object when finished with it. + */ +File fileGetParent(File self); + +/** + * Return a pointer to the file's extension. + * @param self + * @return Pointer to file extension, or NULL if the file has no extension or + * an error occurred. + */ +CharString fileGetExtension(File self); + +/** + * Return the path to the current running executable. + * @return Absolute path of the current executable + */ +CharString fileGetExecutablePath(void); + +/** + * Get the current working directory. + * @return Current working directory + */ +CharString fileGetCurrentDirectory(void); + +/** + * Close a file and flush its buffers to disk. + * @param self + */ +void fileClose(File self); + +/** + * Free a file object and any associated resources + * @param self + */ +void freeFile(File self); + +#endif diff --git a/source/base/LinkedList.c b/source/base/LinkedList.c new file mode 100644 index 0000000..4eec9c3 --- /dev/null +++ b/source/base/LinkedList.c @@ -0,0 +1,157 @@ +// +// LinkedList.c - MrsWatson +// Created by Nik Reiman on 1/3/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 "base/LinkedList.h" +#include "base/Types.h" + +LinkedList newLinkedList(void) +{ + LinkedList list = malloc(sizeof(LinkedListMembers)); + + list->item = NULL; + list->nextItem = NULL; + list->_numItems = 0; + + return list; +} + +void linkedListAppend(LinkedList self, void *item) +{ + LinkedListIterator iterator = self; + LinkedListIterator headNode; + LinkedList nextItem; + + if (self == NULL || item == NULL) { + return; + } + + // First item in the list + if (iterator->item == NULL) { + iterator->item = item; + iterator->_numItems = 1; + return; + } + + headNode = self; + + while (true) { + if (iterator->nextItem == NULL) { + nextItem = newLinkedList(); + nextItem->item = item; + iterator->nextItem = nextItem; + headNode->_numItems++; + break; + } else { + iterator = (LinkedListIterator)(iterator->nextItem); + } + } +} + +int linkedListLength(LinkedList self) +{ + return self != NULL ? self->_numItems : 0; +} + +void **linkedListToArray(LinkedList self) +{ + LinkedListIterator iterator = self; + void **array; + int i = 0; + + if (self == NULL || linkedListLength(self) == 0) { + return NULL; + } + + array = (void **)malloc(sizeof(void *) * (linkedListLength(self) + 1)); + + while (iterator != NULL) { + if (iterator->item != NULL) { + array[i++] = iterator->item; + } + + iterator = iterator->nextItem; + } + + array[i] = NULL; + return array; +} + +void linkedListForeach(LinkedList self, LinkedListForeachFunc foreachFunc, void *userData) +{ + LinkedListIterator iterator = self; + + while (iterator != NULL) { + if (iterator->item != NULL) { + foreachFunc(iterator->item, userData); + } + + iterator = iterator->nextItem; + } +} + +void freeLinkedList(LinkedList self) +{ + LinkedListIterator iterator = self; + + while (iterator != NULL) { + if (iterator->nextItem == NULL) { + free(iterator); + break; + } else { + LinkedList current = iterator; + iterator = iterator->nextItem; + free(current); + } + } +} + +void freeLinkedListAndItems(LinkedList self, LinkedListFreeItemFunc freeItem) +{ + LinkedListIterator iterator = self; + LinkedList current; + + if (iterator->item == NULL) { + free(iterator); + return; + } + + while (true) { + if (iterator->nextItem == NULL) { + freeItem(iterator->item); + free(iterator); + break; + } else { + freeItem(iterator->item); + current = iterator; + iterator = (LinkedListIterator)(iterator->nextItem); + free(current); + } + } +} diff --git a/source/base/LinkedList.h b/source/base/LinkedList.h new file mode 100644 index 0000000..1b5b851 --- /dev/null +++ b/source/base/LinkedList.h @@ -0,0 +1,98 @@ +// +// LinkedList.h - MrsWatson +// Created by Nik Reiman on 1/3/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_LinkedList_h +#define MrsWatson_LinkedList_h + +typedef struct { + void *item; + void *nextItem; + + // This field should be considered private, and is only valid for the head node + int _numItems; +} LinkedListMembers; + +typedef LinkedListMembers *LinkedList; +typedef LinkedListMembers *LinkedListIterator; + +typedef void (*LinkedListForeachFunc)(void *item, void *userData); +typedef void (*LinkedListFreeItemFunc)(void *item); + +/** + * Create a new linked list + * @return Linked list with no items + */ +LinkedList newLinkedList(void); + +/** + * Add an item to the end of a list + * @param self + * @param item Item to append. Items should (but do not necessarily have to be) + * of the same type. However, if items in the list are not of the same type, + * using functions such as foreachItemInList() will be much more difficult. + */ +void linkedListAppend(LinkedList self, void *item); + +/** + * Get the number of items in a list. Use this function instead of accessing + * the fields of the list, as the field names or use may change in the future. + * @param self + * @return Number of items in the list + */ +int linkedListLength(LinkedList self); + +/** + * Flatten a LinkedList to an array. The resulting array will be size N + 1, + * with a NULL object at the end of the array. + * @param self + * @return Array of void* objects with terminating NULL member + */ +void **linkedListToArray(LinkedList self); + +/** + * Iterate over each item in a linked list, calling the given function on each item. + * @param self + * @param foreachFunc Function to call + * @param userData User data to pass to the function + */ +void linkedListForeach(LinkedList self, LinkedListForeachFunc foreachFunc, void *userData); + +/** + * Free each item in a linked list. The contents of the items themselves are *not* + * freed. To do that, call freeLinkedListAndItems() instead. + * @param self + */ +void freeLinkedList(LinkedList self); + +/** + * Free a linked list and each of its items. + * @param self + * @param freeItem Free function to be called for each item + */ +void freeLinkedListAndItems(LinkedList self, LinkedListFreeItemFunc freeItem); + +#endif diff --git a/source/base/PlatformInfo.c b/source/base/PlatformInfo.c new file mode 100644 index 0000000..44c45cc --- /dev/null +++ b/source/base/PlatformInfo.c @@ -0,0 +1,272 @@ +// +// PlatformInfo.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 "base/PlatformInfo.h" +#include "logging/EventLogger.h" + +#if LINUX +#include <sys/utsname.h> +#include "base/File.h" + +#define LSB_FILE_PATH "/etc/lsb-release" +#define LSB_DISTRIBUTION "DISTRIB_DESCRIPTION" +#endif + +static PlatformType _getPlatformType() +{ +#if MACOSX + return PLATFORM_MACOSX; +#elif WINDOWS + return PLATFORM_WINDOWS; +#elif LINUX + return PLATFORM_LINUX; +#else + return PLATFORM_UNSUPPORTED; +#endif +} + +static const char *_getShortPlatformName(void) +{ +#if MACOSX + return "Mac OS X"; +#elif WINDOWS + + if (platformInfoIsRuntime64Bit()) { + return "Windows 64-bit"; + } else { + return "Windows 32-bit"; + } + +#elif LINUX + + if (platformInfoIsRuntime64Bit()) { + return "Linux-x86_64"; + } else { + return "Linux-i686"; + } + +#else + return "Unsupported"; +#endif +} + +#if LINUX +static void _findLsbDistribution(void *item, void *userData) +{ + CharString line = (CharString)item; + CharString distributionName = (CharString)userData; + LinkedList tokens = charStringSplit(line, '='); + + if (tokens != NULL && linkedListLength(tokens) == 2) { + CharString *tokensArray = (CharString *)linkedListToArray(tokens); + CharString key = tokensArray[0]; + CharString value = tokensArray[1]; + + if (!strcmp(key->data, LSB_DISTRIBUTION)) { + charStringCopy(distributionName, value); + } + + free(tokensArray); + } + + freeLinkedListAndItems(tokens, (LinkedListFreeItemFunc)freeCharString); +} +#endif + +#if MACOSX +extern void _getMacVersionString(CharString outString); +#endif + +static CharString _getPlatformName(void) +{ + CharString result = newCharString(); +#if MACOSX + charStringCopyCString(result, _getShortPlatformName()); + _getMacVersionString(result); +#elif LINUX + CharString distributionName = newCharString(); + struct utsname systemInfo; + File lsbRelease = NULL; + LinkedList lsbReleaseLines = NULL; + + if (uname(&systemInfo) != 0) { + logWarn("Could not get system information from uname"); + charStringCopyCString(result, "Linux (Unknown platform)"); + freeCharString(distributionName); + return result; + } + + charStringCopyCString(distributionName, "(Unknown distribution)"); + + lsbRelease = newFileWithPathCString(LSB_FILE_PATH); + + if (fileExists(lsbRelease)) { + lsbReleaseLines = fileReadLines(lsbRelease); + + if (lsbReleaseLines != NULL && linkedListLength(lsbReleaseLines) > 0) { + linkedListForeach(lsbReleaseLines, _findLsbDistribution, distributionName); + } + } + + if (charStringIsEmpty(result)) { + snprintf(result->data, result->capacity, "Linux %s, kernel %s %s", + distributionName->data, systemInfo.release, systemInfo.machine); + } + + freeCharString(distributionName); + freeLinkedListAndItems(lsbReleaseLines, (LinkedListFreeItemFunc)freeCharString); + freeFile(lsbRelease); +#elif WINDOWS + OSVERSIONINFOEX versionInformation; + memset(&versionInformation, 0, sizeof(OSVERSIONINFOEX)); + versionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + GetVersionEx((OSVERSIONINFO *)&versionInformation); + // Generic string which will also work with newer versions of windows + snprintf(result->data, result->capacity, "Windows %d.%d", + versionInformation.dwMajorVersion, versionInformation.dwMinorVersion); + + // This is a bit lame, but it seems that this is the standard way of getting + // the platform name on Windows. + switch (versionInformation.dwMajorVersion) { + case 6: + switch (versionInformation.dwMinorVersion) { + case 2: + charStringCopyCString(result, "Windows 8"); + break; + + case 1: + charStringCopyCString(result, "Windows 7"); + break; + + case 0: + charStringCopyCString(result, "Windows Vista"); + break; + } + + break; + + case 5: + switch (versionInformation.dwMinorVersion) { + case 2: + charStringCopyCString(result, "Windows Server 2003"); + break; + + case 1: + charStringCopyCString(result, "Windows XP"); + break; + + case 0: + charStringCopyCString(result, "Windows 2000"); + break; + } + + break; + } + +#else + charStringCopyCString(result, "Unsupported platform"); +#endif + + return result; +} + +boolByte platformInfoIsRuntime64Bit(void) +{ + return (boolByte)(sizeof(void *) == 8); +} + +boolByte platformInfoIsHost64Bit(void) +{ + boolByte result = false; + +#if LINUX + struct utsname systemInfo; + + if (uname(&systemInfo) != 0) { + logError("Could not get system bitness from uname"); + } else { + result = (boolByte)(strcmp(systemInfo.machine, "x86_64") == 0); + } + +#elif MACOSX +#elif WINDOWS + typedef BOOL (WINAPI * IsWow64ProcessFuncPtr)(HANDLE, PBOOL); + BOOL isProcessRunningInWow64 = false; + IsWow64ProcessFuncPtr isWow64ProcessFunc = NULL; + + // The IsWow64Process() function is not available on all versions of Windows, + // so it must be looked up first and called only if it exists. + isWow64ProcessFunc = (IsWow64ProcessFuncPtr)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); + + if (isWow64ProcessFunc != NULL) { + if (isWow64ProcessFunc(GetCurrentProcess(), &isProcessRunningInWow64)) { + // IsWow64Process will only return true if the current process is a 32-bit + // application running on 64-bit Windows. + if (isProcessRunningInWow64) { + result = true; + } else { + // If false, then we can assume that the host has the same bitness as + // the executable. + result = platformInfoIsRuntime64Bit(); + } + } + } + +#else + logUnsupportedFeature("Get host 64-bitness"); +#endif + + return result; +} + +boolByte platformInfoIsLittleEndian(void) +{ + int num = 1; + return (boolByte)(*(char *)&num == 1); +} + +PlatformInfo newPlatformInfo(void) +{ + PlatformInfo platformInfo = (PlatformInfo)malloc(sizeof(PlatformInfoMembers)); + platformInfo->type = _getPlatformType(); + platformInfo->name = _getPlatformName(); + platformInfo->shortName = newCharStringWithCString(_getShortPlatformName()); + platformInfo->is64Bit = platformInfoIsHost64Bit(); + return platformInfo; +} + +void freePlatformInfo(PlatformInfo self) +{ + if (self != NULL) { + freeCharString(self->name); + freeCharString(self->shortName); + free(self); + } +} diff --git a/source/base/PlatformInfo.h b/source/base/PlatformInfo.h new file mode 100644 index 0000000..cd706d6 --- /dev/null +++ b/source/base/PlatformInfo.h @@ -0,0 +1,68 @@ +// +// PlatformInfo.h - MrsWatson +// Created by Nik Reiman on 14/12/14. +// Copyright (c) 2014 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_PlatformInfo_h +#define MrsWatson_PlatformInfo_h + +#include "base/CharString.h" + +typedef enum { + PLATFORM_UNSUPPORTED, + PLATFORM_MACOSX, + PLATFORM_WINDOWS, + PLATFORM_LINUX, + NUM_PLATFORMS +} PlatformType; + +typedef struct { + PlatformType type; + CharString name; + CharString shortName; + boolByte is64Bit; +} PlatformInfoMembers; +typedef PlatformInfoMembers *PlatformInfo; + +PlatformInfo newPlatformInfo(void); + +/** + * @brief Static method which returns true if the host CPU is little endian + */ +boolByte platformInfoIsLittleEndian(void); + +/** + * @brief True if the platform is a native 64-bit OS + */ +boolByte platformInfoIsHost64Bit(void); + +/** + * @brief True if the executable is running as a 64-bit binary + */ +boolByte platformInfoIsRuntime64Bit(void); + +void freePlatformInfo(PlatformInfo self); + +#endif diff --git a/source/base/PlatformInfoMac.m b/source/base/PlatformInfoMac.m new file mode 100644 index 0000000..2bf0fd9 --- /dev/null +++ b/source/base/PlatformInfoMac.m @@ -0,0 +1,41 @@ +// +// PlatformInfoMac.mm - MrsWatson +// Created by Nik Reiman on 5/12/14. +// Copyright (c) 2014 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. +// + +#if MACOSX + +#include <Foundation/Foundation.h> + +#include "base/CharString.h" + +void _getMacVersionString(CharString outString); +void _getMacVersionString(CharString outString) { + NSString *osVersion = [[NSProcessInfo processInfo] operatingSystemVersionString]; + const char *osVersionCString = [osVersion UTF8String]; + charStringAppendCString(outString, osVersionCString); +} + +#endif diff --git a/source/base/Types.h b/source/base/Types.h new file mode 100644 index 0000000..c1772ef --- /dev/null +++ b/source/base/Types.h @@ -0,0 +1,89 @@ +// +// Types.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_Types_h +#define MrsWatson_Types_h + +// Custom types used across the application +typedef int PcmSample; // TODO: int32_t? +typedef float Sample; +typedef Sample *Samples; + +typedef double SampleRate; +typedef double Tempo; +typedef unsigned long SampleCount; +typedef unsigned short ChannelCount; + +// Using "bool" or "boolByte" (or their uppercase equivalents) is a bit dangerous +// since compilers on some platforms define this for us. This gets tricky when +// mixing C89/C99 syntax, so to be safe, we will use a new made-up type instead. +typedef unsigned char boolByte; + +#ifndef byte +typedef unsigned char byte; +#endif + +#ifndef false +#define false 0 +#endif + +#ifndef true +#define true 1 +#endif + +// Platform-specific hooks or compiler overrides +#if WINDOWS +#define WIN32_LEAN_AND_MEAN 1 +#include <Windows.h> + +// Even redefining most of the functions below doesn't stop the compiler +// from nagging about them. +#pragma warning(disable: 4996) + +// Substitutes for POSIX functions not found on Windows +#define strcasecmp _stricmp +#define strdup _strdup +#define unlink _unlink +#define snprintf _snprintf +#define isatty _isatty +#define chdir _chdir +#define unlink _unlink +#endif + +// LibraryHandle definition +#if MACOSX +#include <CoreFoundation/CFBundle.h> +typedef CFBundleRef LibraryHandle; +#elif LINUX +typedef void *LibraryHandle; +#elif WINDOWS +typedef HMODULE LibraryHandle; +#else +typedef void *LibraryHandle; +#endif + +#endif |
