diff --git a/.gitignore b/.gitignore index 00bc067..5123f5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Ignore the build and doc directories. -build/ +build*/ doc/doxygen_doc/* # Ignore any object or archive file anywhere in the tree. diff --git a/CMakeLists.txt b/CMakeLists.txt index 2129543..390d400 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,13 +19,17 @@ set(O_OUTPUT_DIR ${T_OUTPUT_DIR}/obj CACHE PATH "Build object output directory") endif() # Option Defaults. +option (BUILD_BYTE_ORDER "Whether or not to build the Byte Ordering code. (Defaults to yes.)" ON) option (BUILD_DYNAMIC_LIBRARY_SUBSYSTEM "Whether or not to build the Dynamic Library Subsystem. (Defaults to yes.)" ON) +option (BUILD_COMMON_ERROR_HANDLER "Whether or not to build the Common Error Handler. (Defaults to yes.)" ON) +option (BUILD_FATAL_ERROR_NOTIFY_SUPPORT "Whether or not to build the Common Error Handler's fatal error notification support. (Defaults to yes.)" ON) option (BUILD_INTERNAL_MUTEX_SUPPORT "Whether or not to build the internal mutex support library. (Defaults to yes.)" ON) option (BUILD_COMMON_ERROR_HANDLER "Whether or not to build the common error handler. (Defaults to yes.)" ON) option (BUILD_INTERNAL_MUTEX_THREAD_SUBSYS_WRAPPER "Whether or not the Threading Subsystem library will have Internal Mutex Support built in. (Defaults to yes, but is disabled currently due to changes to Thread_Utils that have not been commited yet.)" OFF) option (BUILD_INTERNAL_MUTEX_THREAD_SUBSYS_WRAPPER_PLUGIN "Whether or not to build the Threading Subsystem plugin for Internal Mutex Support. (Defaults to yes, but is disabled currently due to changes to Thread_Utils that have not been commited yet.)" OFF) option (BUILD_FILE_MANAGEMENT_SUBSYSTEM "Whether or not to build the file management subsystem. FileUtills. (Defaults to yes.)" ON) option (BUILD_THREADING_SUBSYSTEM "Whether or not to build the threading subsystem. Thread_Utils. (Defaults to yes.)" ON) +option (BUILD_RENDERING_SUBSYSTEM "Whether or not to build the rendering subsystem. (Defaults to yes.)" ON) option (ENABLE_THREADING_SUBSYSTEM_PLUGIN_SUPPORT "Whether or not to enable the threading subsystem's plugin support. (Defaults to yes.)" ON) option (BUILD_THREAD_SUBSYS_PTHREADS_PLUGIN "Whether or not to build the threading subsystem's pthreads plugin. (Defaults to yes.)" ON) option (BUILD_UNIT_TESTS "Whether or not to build the unit_test program. (Defaults to yes.)" ON) diff --git a/src/Common/Src/Byte_Order/Byte_Order.c b/src/Common/Src/Byte_Order/Byte_Order.c new file mode 100644 index 0000000..dcad71e --- /dev/null +++ b/src/Common/Src/Byte_Order/Byte_Order.c @@ -0,0 +1,352 @@ +/*! + Multiverse Engine Project 03/6/2015 Common Byte_Order.c + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Byte_Order.h" + +/* We need to include limits.h for UCHAR_MAX. (MSVC includes it automaticly with the above headers.) */ +#ifdef __GNUC__ +#include +#endif /* __GNUC__ */ + +/* Check for C++ compiler. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int Byte_Order_Bit_Comparison(const char * byte, const char bitMask, const char bitValues) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + + /* 10000000 wanna check the first two bits, how? + + 11000000 <-- Bit mask of bits to actually check. + + 10 <-- What the two bits should be. + + Do two comparisons. (One for the set (1) bits and one for the unset (0) bits. + + bits should be: BINARY AND BITMASK. BITS THAT ARE ACTUALLY SET TO CHECK. + (1000000) & (11000000) = (1000000) + + BITWISE NOT bits should be: Inverted what bits should be. + ~ (1000000) = (01111111) + + Inverted what bits should be: BINARY AND BITMASK. BITS THAT ARE NOT SET TO CHECK. + (01111111) & (11000000) = (0100000) + + BITS TO CHECK. BINARY AND BITS THAT ARE SET TO CHECK. (Comparison one result) + (10000000) & (1000000) = SUCCESS. (1000000) + + Inverted BITS TO CHECK. BINARY AND BITS THAT ARE NOT SET TO CHECK. (Comparison two result) + (01111111) & (0100000) = SUCCESS. (0100000) + + (Comparison one result) BINARY INCLUSIVE OR (Comparison two result) (Result of both comparisons) + (1000000) | (0100000) = (11000000) + + BITMASK BINARY AND (Result of both comparisons) Final result + (11000000) & (11000000) = SUCCESS (11000000) + + */ + + /* Check for invalid arguments. */ + if (byte != NULL) + { + /* Check for all value comparison. */ + /*ret = (bitMask == UCHAR_MAX) ? + /* Simple equality check, we only need to check for an exact match. Otherwise continue comparison on the next line. / + (((*byte) == bitValues) ? COMMON_ERROR_COMPARISON_PASSED : COMMON_ERROR_COMPARISON_FAILED) : \ + /* HARDCODED CHECK: If both bitValues and byte are zero, + and bitMask is non-zero we need to return true. Otherwise continue on the next line. / + ((((*byte) == 0) && (bitValues == 0)) ? ((bitMask != 0) ? COMMON_ERROR_COMPARISON_PASSED : COMMON_ERROR_COMPARISON_FAILED) : \ + /* Because it's not a simple equality check, Do first comparison. (Set (1) bits.) / + ((((*byte) & (bitValues & bitMask)) && ((~(*byte)) & ((~bitValues) & bitMask))) ? + /* Do the second comparison. (Unset (0) bits.) / + COMMON_ERROR_COMPARISON_PASSED : COMMON_ERROR_COMPARISON_FAILED));*/ + if (bitMask == UCHAR_MAX) + { + ret = ((*byte) == bitValues) ? COMMON_ERROR_COMPARISON_PASSED : COMMON_ERROR_COMPARISON_FAILED; + } + else + { + if (((*byte) == 0) && (bitValues == 0)) + { + ret = (bitMask != 0) ? COMMON_ERROR_COMPARISON_PASSED : COMMON_ERROR_COMPARISON_FAILED; + } + else + { + ret = ((((*byte) & (bitValues & bitMask)) | ((~(*byte)) & ((~bitValues) & bitMask))) & bitMask) ? + COMMON_ERROR_COMPARISON_PASSED : COMMON_ERROR_COMPARISON_FAILED; + } + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int Common_Byte_Swap(char * data, const size_t dataLength) +{ + /* Init ret. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + size_t x = 0; /* Counter used in the swap loop. */ + char tempByte = '\0'; /* Temporary memory storage for a byte being swapped out. */ + + /* Check for invalid arguments. */ + if ((data != NULL) && ((dataLength % 2) == 0)) + { + /* Begin swap loop. */ + for (x = 0; (x < (dataLength / 2)); x++) + { + /* + * Each bit is swapped with it's partner on the other side of the array. + * I.e. Each byte from x distance from the start of the array is swapped + * with the byte x distance from the end of the array. + */ + tempByte = data[x]; + data[x] = data[((dataLength - 1) - x)]; + data[((dataLength - 1) - x)] = tempByte; + } + + /* Done! */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int Common_Print_Bytes_To_CString(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const unsigned int base, const size_t width, const char fillValue, const bool spaceBetweenBytes) +{ +/* Define the size of the output values array. */ +#define MSYSOUTPUTVALUESSIZE 17 + + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + size_t x = 0; /* Counter used in the print and spacing loops. */ + size_t byteValueCount = 0; /* Used to keep track of how many bytes we have outputted for the current byte. */ + unsigned char currentByte = '\0'; /* Temporary value used to store the current byte we are working on. */ + size_t outputValue = 0; /* The value that we need to write into the output buffer. (Calculated from currentValue.) */ + char * outputBuffer = NULL; /* Pointer to the c-string that will be given back to the caller. */ + char * previousOutputBuffer = NULL; /* Temporary pointer used to copy previously generated data into the current outputBuffer. */ + const char outputValues[MSYSOUTPUTVALUESSIZE] = "0123456789ABCDEF"; /* C-String used to map a generated value to it's corresponding character. */ + size_t outputBufferSize = 1; /* Current size of the outputBuffer. Set to one by default to allow the string to be NULL terminated. */ + + /* Check for invalid arguments. */ + if ((data != NULL) && (dataLength > 0) && (retStr != NULL) && (retStrSize != NULL) && (base >= 2) && (base <= 16)) + { + /* Begin data print loop. */ + for (x = 0; ((x < dataLength) && (ret == COMMON_ERROR_UNKNOWN_ERROR)); x++) + { + /* Copy current value. */ + currentByte = (data[((dataLength - 1) - x)]); + + /* Reset byte value count. */ + byteValueCount = 0; + + /* Begin value parsing loop. */ + do + { + /* Read the current byte's value from right to left. (Mod is a reduction operation.) */ + outputValue = (currentByte % base); + + /* Copy the current buffer's pointer because we are about to create a new one. */ + previousOutputBuffer = outputBuffer; + + /* Increment the size of the new buffer. */ + outputBufferSize++; + + /* Allocate the new buffer. */ + outputBuffer = (char*)malloc(outputBufferSize); + + /* Check for successful memory allocation. */ + if (outputBuffer != NULL) + { + /* Blank out the new buffer. */ + memset(outputBuffer, '\0', outputBufferSize); + + /* Set the first value as the previous data comes after it. */ + if ((outputValue >= 0) && (outputValue < MSYSOUTPUTVALUESSIZE)) + { + outputBuffer[0] = outputValues[outputValue]; + + /* If we have any previous data we need to copy it into the new buffer and deallocate the previous one. */ + if (previousOutputBuffer != NULL) + { + memcpy((outputBuffer + 1), previousOutputBuffer, (outputBufferSize - 1)); + free(previousOutputBuffer); + previousOutputBuffer = NULL; + } + + /* Increment byte value count. */ + byteValueCount++; + + /* Get the next value by chopping off the "ones place", aka divide by the current base. */ + currentByte /= base; + } + else + { + /* Invalid byte value. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* Could not allocate memory for output buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + }while ((currentByte) && (ret == COMMON_ERROR_UNKNOWN_ERROR)); + + /* Check and see if the generated values used up all of the requested width. */ + if ((ret == COMMON_ERROR_UNKNOWN_ERROR) && (outputBuffer != NULL) && (byteValueCount < width)) + { + /* Copy the current buffer's pointer because we are about to create a new one. */ + previousOutputBuffer = outputBuffer; + + /* Increment the output buffer size by the remaining filler we need to add. */ + outputBufferSize += (width - byteValueCount); + + /* Allocate the new buffer. */ + outputBuffer = (char*)malloc(outputBufferSize); + + /* Check for successful memory allocation. */ + if (outputBuffer != NULL) + { + /* Blank out the new buffer. */ + memset(outputBuffer, '\0', outputBufferSize); + + /* Put in our filler. */ + memset(outputBuffer, fillValue, (width - byteValueCount)); + + /* If we have any previous data we need to copy it into the new buffer and deallocate the previous one. */ + if (previousOutputBuffer != NULL) + { + memcpy((outputBuffer + (width - byteValueCount)), previousOutputBuffer, (outputBufferSize - (width - byteValueCount))); + free(previousOutputBuffer); + previousOutputBuffer = NULL; + } + } + else + { + /* Could not allocate memory for output buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + + /* Insert spacing if needed. */ + if ((spaceBetweenBytes) && (ret == COMMON_ERROR_UNKNOWN_ERROR) && (outputBuffer != NULL) && ((x + 1) < dataLength)) + { + /* Copy the current buffer's pointer because we are about to create a new one. */ + previousOutputBuffer = outputBuffer; + + /* Increment the output buffer size. */ + outputBufferSize++; + + /* Allocate the new buffer. */ + outputBuffer = (char*)malloc(outputBufferSize); + + /* Check for successful memory allocation. */ + if (outputBuffer != NULL) + { + /* Blank out the new buffer. */ + memset(outputBuffer, '\0', outputBufferSize); + + /* Insert our space. */ + outputBuffer[0] = ' '; + + /* Copy the original buffer. */ + memcpy((outputBuffer + 1), previousOutputBuffer, (outputBufferSize - 1)); + } + else + { + /* Could not allocate memory for output buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + } + + /* Check for NULL output buffer. */ + if ((ret == COMMON_ERROR_UNKNOWN_ERROR) && (outputBuffer != NULL) && (outputBufferSize > 0)) + { + /* Copy the outputBuffer pointer to retStr. */ + (*retStr) = outputBuffer; + + /* Copy outputBufferSize to retStrSize. */ + (*retStrSize) = outputBufferSize; + + /* Done! */ + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + +/* Undefine the size of the output values array. */ +#undef MSYSOUTPUTVALUESSIZE +} + +void Common_Deallocate_Print_Bytes_CString(char ** str) +{ + /* Check for NULL. */ + if (str != NULL) + { + free((*str)); + (*str) = NULL; + } + + /* Exit function. */ + return; +} + +int Common_Print_Bytes_In_Hex(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const bool spaceBetweenBytes) +{ + return Common_Print_Bytes_To_CString(data, dataLength, retStr, retStrSize, 16, 2, '0', spaceBetweenBytes); +} + +int Common_Print_Bytes_In_Binary(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const bool spaceBetweenBytes) +{ + return Common_Print_Bytes_To_CString(data, dataLength, retStr, retStrSize, 2, 8, '0', spaceBetweenBytes); +} + +int Common_Print_Bytes_In_Decimal(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const bool spaceBetweenBytes) +{ + return Common_Print_Bytes_To_CString(data, dataLength, retStr, retStrSize, 10, 8, '0', spaceBetweenBytes); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ diff --git a/src/Common/Src/Byte_Order/Byte_Order.h b/src/Common/Src/Byte_Order/Byte_Order.h new file mode 100644 index 0000000..10a5bc1 --- /dev/null +++ b/src/Common/Src/Byte_Order/Byte_Order.h @@ -0,0 +1,215 @@ +/*! + Multiverse Engine Project 03/6/2015 Common Byte_Order.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef MSYS_BYTE_ORDER_H +#define MSYS_BYTE_ORDER_H + +/* Check for C++ compiler. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Define Endianness Result Values. */ +#define MSYS_BIG_ENDIAN 0 +#define MSYS_LITTLE_ENDIAN 1 +#define MSYS_UNKNOWN_ENDIANNESS 2 + +/* Pull in DLL_PORT.h */ +#include "../../../DLL_PORT.h" /* Defines MSYS_DLL_EXPORT, and MSYS_DLL_IMPORT_TEMPLATE. */ + +/* Define bool. */ +#if _MSC_FULL_VER && _MSC_FULL_VER < 180031101 +#include "../../../stdbool.h" /* Older versions of Visual C don't support the C99 stdbool header. So we have to fake one. */ +#else +#include +#endif + +/* External headers. */ +#include /* Defines NULL. */ +#include /* Defines malloc(), free(). */ +#include /* Defines memset(), memcpy(). */ + +/* Internal headers. */ +#include "../Error_Handler/Common_Error_Handler_Error_Codes.h" /* Defines the error codes. */ +#include "Byte_Order_Integers.h" +#include "Byte_Order_Floating_Points.h" + +/*! + * int Byte_Order_Bit_Comparison(const char * byte, const char bitMask, const char bitValues) + * + * Compares the given byte's bits to the given bitValues using bitMask to define what bits to compare. + * + * I.e. This function compares bits to see if they are equal, using the bitMask to determine what + * bits to actually compare. + * + * Example: If bitMask is 11000000, then only the first two bits are checked. So to make a truth table: + * + * byte: bitMask: bitValues: Result: + * 11000000 11000000 11000000 TRUE (The checked values in byte and bitValues are identical.) + * 10000000 11000000 10000000 TRUE (The checked first and second bits are identical.) + * 01110010 00110010 10110010 TRUE (The checked 3rd, 4th, and 7th bits are identical.) + * 11000100 11000000 11000000 TRUE (Extra bit in byte is not checked.) + * 11000000 11000000 11000100 TRUE (Extra bit in bitValues is not checked.) + * 01000000 11000000 11000000 FALSE (byte does not match bitValues.) + * 11000000 11000000 10000000 FALSE (bitValues does not match byte.) + * 00000000 00000000 00000000 FALSE (No bits are defined to check.) + * 00000000 00110010 00000000 TRUE (Third, forth, and 7th bits are identical. + * (Hardcoded zero check, for consistancy with other results.)) + * 00000000 11111111 00000000 TRUE (Checking all bits, byte and bitValues are both zero. + * (Hardcoded zero check, for consistancy with other results.)) + * + * A note about checking for zero bits: + * For consistancy with the other results, in the event an all zero byte and bitValues is given + * to this function, a hard coded check is inserted to return COMMON_ERROR_COMPARISON_PASSED if + * the given bitMask is not equal to zero. Otherwise it returns COMMON_ERROR_COMPARISON_FAILED. + * This contradicts what would be expected from a binary AND operation and callers of this + * function should therefore be aware of it and check for this result. + * (Normally, a binary AND operation on an all zero set of operands would give a FALSE result. + * The best antidote I can give to deal with this function's inconsistancy is: + * It checks the given bits to see if they match, if given to a non-programmer, what would they + * think the result should be? + * Answer: (0 == 0) is TRUE, so the function should return TRUE.") + * + * Returns COMMON_ERROR_COMPARISON_PASSED if the checked bits match. + * Returns COMMON_ERROR_COMPARISON_FAILED if the checked bits do NOT match. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer to char is invalid. + * Otherwise returns the approperite error code. + */ +MSYS_DLL_EXPORT int Byte_Order_Bit_Comparison(const char * byte, const char bitMask, const char bitValues); + +/*! + * int Common_Byte_Swap(char * data, const size_t dataLength) + * + * Swaps the given bytes by swapping each byte + * with it's complement on the other end of the + * byte string. + * + * I.e. Each byte from x distance from the start of the string is swapped + * with the byte x distance from the end of the string. + * + * Note: This function expects that the amount of data given to it + * is a multiple of 2. Also this function will alter the given + * data in-place. (I.e. If an error occurs, the data WILL be altered.) + * + * Returns COMMON_ERROR_SUCCESS if swapping was successful. + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is + * NULL, or if the data length is not a multiple of 2. + * + * Otherwise returns the appropriate error code. + */ +MSYS_DLL_EXPORT int Common_Byte_Swap(char * data, const size_t dataLength); + +/*! + * int Common_Print_Bytes_To_CString(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const unsigned int base, + * const size_t width, const char fillValue, const bool spaceBetweenBytes) + * + * Converts a given byte string to a printable (NULL-terminated) and human-readable string in the given base. + * + * @pram data: the byte string to convert. + * @pram dataLength: the length of the byte string. + * @pram retStr: a double pointer that will point to the converted string. + * @pram retStrSize: the size of the generated string. + * @pram base: the numerical base to convert each byte to. + * + * @pram width: the minimal number of values to print for each byte. (Note the minimal keyword. The value will not be truncated if it + * requires more than width characters to represent it. This is solely to make smaller values take up more space for more uniform value + * representation.) + * + * @pram fillValue: the value printed in the converted string to take up the extra space, when a byte's value representation is smaller + * than the given width. (E.x. the byte value is 0, width is 3, and fillValue is '.': "..0" would be the result.) + * + * @pram spaceBetweenBytes: Whether or not to insert a space character after each of the converted byte values. (Readability.) + * + * WARNING: If retStr is non-NULL when this function is called, and this function returns COMMON_ERROR_SUCCESS, then the pointer will + * be overwritten without being deallocated. If you need that pointer to deallocate it later, you should copy it elsewhere before calling + * this function. + * + * Note: To deallocate the returned string from this function, pass the returned string to Common_Deallocate_Print_Bytes_CString(). + * + * Returns COMMON_ERROR_SUCCESS if the conversion is successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers are NULL, or if the given base is not a value between 2 and 16. + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation fails. + * + * In case of error, (the returned error code is not COMMON_ERROR_SUCCESS, then the given arguments will NOT be altered by this function. + */ +MSYS_DLL_EXPORT int Common_Print_Bytes_To_CString(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const unsigned int base, const size_t width, const char fillValue, const bool spaceBetweenBytes); + +/*! + * void Common_Deallocate_Print_Bytes_CString(char ** str) + * + * Deallocates the given c-string and sets the given pointer to char to NULL. + * + * Note: This function is a destructor function for strings made by Common_Print_Bytes_To_CString() and + * should ONLY be used for that purpose. The behaviour for using this function to deallocate other allocations is undefined. + * + * This function has no return. If a given pointer is NULL, this function will silently fail. + */ +MSYS_DLL_EXPORT void Common_Deallocate_Print_Bytes_CString(char ** str); + +/*! + * int Common_Print_Bytes_In_Hex(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const bool spaceBetweenBytes) + * + * Wrapper function around Common_Print_Bytes_To_CString() for converting a byte string to a human-readable hexadecimal string. + * Example output: ("001FAC" if spacing is disabled, "00 1F AC" if spacing is enabled.) + * + * Note: This function is to make calls shorter, if you want more control over the output, call Common_Print_Bytes_To_CString() directly. + * + * All arguments and return values are identical to their Common_Print_Bytes_To_CString() counterparts. See Common_Print_Bytes_To_CString() + * for their descriptions. + */ +MSYS_DLL_EXPORT int Common_Print_Bytes_In_Hex(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const bool spaceBetweenBytes); + +/*! + * int Common_Print_Bytes_In_Binary(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, + * const bool spaceBetweenBytes) + * + * Wrapper function around Common_Print_Bytes_To_CString() for converting a byte string to a human-readable binary string. + * Example output: ("00000101000001010000010100000101" if spacing is disabled, "00000101 00000101 00000101 00000101" if spacing is enabled.) + * + * Note: This function is to make calls shorter, if you want more control over the output, call Common_Print_Bytes_To_CString() directly. + * + * All arguments and return values are identical to their Common_Print_Bytes_To_CString() counterparts. See Common_Print_Bytes_To_CString() + * for their descriptions. + */ +MSYS_DLL_EXPORT int Common_Print_Bytes_In_Binary(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const bool spaceBetweenBytes); + +/*! +* int Common_Print_Bytes_In_Decimal(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, +* const bool spaceBetweenBytes) +* +* Wrapper function around Common_Print_Bytes_To_CString() for converting a byte string to a human-readable decimal string. +* Example output: ("10012" if spacing is disabled, "10 0 12" if spacing is enabled.) +* +* Note: This function is to make calls shorter, if you want more control over the output, call Common_Print_Bytes_To_CString() directly. +* +* All arguments and return values are identical to their Common_Print_Bytes_To_CString() counterparts. See Common_Print_Bytes_To_CString() +* for their descriptions. +*/ +MSYS_DLL_EXPORT int Common_Print_Bytes_In_Decimal(const char * data, const size_t dataLength, char ** retStr, size_t * retStrSize, const bool spaceBetweenBytes); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MSYS_BYTE_ORDER_H */ + +/* End of Byte_Order.h */ diff --git a/src/Common/Src/Byte_Order/Byte_Order_Floating_Points.c b/src/Common/Src/Byte_Order/Byte_Order_Floating_Points.c new file mode 100644 index 0000000..45d40f9 --- /dev/null +++ b/src/Common/Src/Byte_Order/Byte_Order_Floating_Points.c @@ -0,0 +1,281 @@ +/*! + Multiverse Engine Project 09/6/2015 Common Byte_Order_Floating_Points.c + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Byte_Order.h" + +/* Check for C++ compiler. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int Common_Host_To_Big_Endian_Float(float * f) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_FLOAT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (f != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)f), sizeof (float)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_Float(float * f) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_FLOAT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (f != NULL) + { + /* + * Call Common_Byte_Swap(). + * + * (We could call Common_Host_To_Big_Endian_Float() here, but + * that would just repeat the calls above. Plus we may not be able + * to do that on all systems.) + */ + ret = Common_Byte_Swap(((char*)f), sizeof (float)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_Double(double * d) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_DOUBLE_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (d != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)d), sizeof (double)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_Double(double * d) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_DOUBLE_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (d != NULL) + { + /* + * Call Common_Byte_Swap(). + * + * (We could call Common_Host_To_Big_Endian_Double() here, but + * that would just repeat the calls above. Plus we may not be able + * to do that on all systems.) + */ + ret = Common_Byte_Swap(((char*)d), sizeof (double)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_FLOAT_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + float t = 1.0; /* Variable to check. */ + + /* Cast t to a char string and see if the first 2 values are 0x3F80. */ + if ((((char*)&t)[0] == 0x3F) && ((((char*)&t)[1] == 0x80))) + { + /* The first 2 bytes are 0x3F80 so it's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + else + { + /* + * The first check did not pass, so check and see if the last 2 bytes are 0x803F. + * If they are, then the host is little endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if ((((char*)&t)[(sizeof (float) - 1)] == 0x80) && ((((char*)&t)[(sizeof (float) - 2)] == 0x3F))) + { + /* It's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_DOUBLE_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + double t = 1.0; /* Variable to check. */ + + /* Cast t to a char string and see if the first 2 values are 0x3FF0. */ + if ((((char*)&t)[0] == 0x3F) && ((((char*)&t)[1] == 0xF0))) + { + /* The first 2 bytes are 0x3F80 so it's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + else + { + /* + * The first check did not pass, so check and see if the last 2 bytes are 0xF03F. + * If they are, then the host is little endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if ((((char*)&t)[(sizeof (double) - 1)] == 0xF0) && ((((char*)&t)[(sizeof (double) - 2)] == 0x3F))) + { + /* It's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ diff --git a/src/Common/Src/Byte_Order/Byte_Order_Floating_Points.h b/src/Common/Src/Byte_Order/Byte_Order_Floating_Points.h new file mode 100644 index 0000000..8bcd0c5 --- /dev/null +++ b/src/Common/Src/Byte_Order/Byte_Order_Floating_Points.h @@ -0,0 +1,176 @@ +/*! + Multiverse Engine Project 09/6/2015 Common Byte_Order_Floating_Points.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef MSYS_BYTE_ORDER_FLAOTING_POINTS_H +#define MSYS_BYTE_ORDER_FLAOTING_POINTS_H + +/* Make sure we are not included directly. */ +#ifndef MSYS_BYTE_ORDER_H +#error "You should not include __FILE__ directly, it is included automatically by Byte_Order.h, remove this include. Aborting build." +#endif + +/* Check for C++ compiler. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*! + * int Common_Host_To_Big_Endian_Float(float * f) + * + * Converts a given FLOAT value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_Float(float * f); + +/*! + * int Common_Big_Endian_To_Host_Float(float * f) + * + * Converts a given FLOAT value from Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_Float(float * f); + +/*! + * int Common_Host_To_Big_Endian_DOUBLE(double * d) + * + * Converts a given DOUBLE value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_Double(double * d); + +/*! + * int Common_Big_Endian_To_Host_Double(double * d) + * + * Converts a given DOUBLE value from Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_Double(double * d); + +/*! + * General Definitions for the Common_*_Endianness_Check() functions: + * + * The functions below all contain the same returns, + * and purpose. + */ + +/*! + * int Common_FLOAT_Endianness_Check() + * + * Template function which checks the host's endianness for the + * FLOAT data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a signed FLOAT argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_FLOAT_Endianness_Check(); + +/*! + * int Common_DOUBLE_Endianness_Check() + * + * Template function which checks the host's endianness for the + * DOUBLE data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a signed DOUBLE argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_DOUBLE_Endianness_Check(); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MSYS_BYTE_ORDER_FLAOTING_POINTS_H */ + +/* End of Byte_Order_Floating_Points.h */ diff --git a/src/Common/Src/Byte_Order/Byte_Order_Integers.c b/src/Common/Src/Byte_Order/Byte_Order_Integers.c new file mode 100644 index 0000000..263e952 --- /dev/null +++ b/src/Common/Src/Byte_Order/Byte_Order_Integers.c @@ -0,0 +1,1340 @@ +/*! + Multiverse Engine Project 09/6/2015 Common Byte_Order_Integers.c + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Byte_Order.h" + +/* Check for C++ compiler. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int Common_Host_To_Big_Endian_UChar(unsigned char * uc) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_UCHAR_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (uc != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)uc), sizeof (unsigned char)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_UChar(unsigned char * uc) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_UCHAR_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (uc != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)uc), sizeof (unsigned char)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_Char(char * c) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_CHAR_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (c != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)c), sizeof (char)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_Char(char * c) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_CHAR_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (c != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)c), sizeof (char)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_UShort(unsigned short * us) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_USHORT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (us != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)us), sizeof (unsigned short)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_UShort(unsigned short * us) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_USHORT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (us != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)us), sizeof (unsigned short)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_Short(short * s) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_SHORT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (s != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)s), sizeof (short)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_Short(short * s) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_SHORT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (s != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)s), sizeof (short)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_UInt(unsigned int * ui) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_UINT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (ui != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)ui), sizeof (unsigned int)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_UInt(unsigned int * ui) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_UINT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (ui != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)ui), sizeof (unsigned int)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_Int(int * i) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_INT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (i != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)i), sizeof (int)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_Int(int * i) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_INT_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (i != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)i), sizeof (int)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_ULong(unsigned long * ul) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_ULONG_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (ul != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)ul), sizeof (unsigned long)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_ULong(unsigned long * ul) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_ULONG_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (ul != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)ul), sizeof (unsigned long)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_Long(long * l) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_LONG_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (l != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)l), sizeof (long)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_Long(long * l) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_LONG_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (l != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)l), sizeof (long)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_ULong_Long(unsigned long long * ull) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_ULONG_LONG_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (ull != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)ull), sizeof (unsigned long long)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_ULong_Long(unsigned long long * ull) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_ULONG_LONG_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (ull != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)ull), sizeof (unsigned long long)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_Long_Long(long long * ll) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_LONG_LONG_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (ll != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)ll), sizeof (long long)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_Long_Long(long long * ll) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_LONG_LONG_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (ll != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)ll), sizeof (long long)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Host_To_Big_Endian_Size_T(size_t * st) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_SIZE_T_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (st != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)st), sizeof (size_t)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_Big_Endian_To_Host_Size_T(size_t * st) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Check host's endianness. */ + ret = Common_SIZE_T_Endianness_Check(); + if (ret != MSYS_BIG_ENDIAN) + { + /* Check for valid endianness. */ + if (ret == MSYS_LITTLE_ENDIAN) + { + /* Check for invalid arguments. */ + if (st != NULL) + { + /* Call Common_Byte_Swap(). */ + ret = Common_Byte_Swap(((char*)st), sizeof (size_t)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Host endianness is unknown. Cannot convert unknown endianness. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + } + } + else + { + /* + * This value should already be in the correct byte order, + * as the host is big endian. + */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Exit function. */ + return ret; +} + +int Common_UCHAR_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + unsigned char t = '\1'; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_CHAR_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + char t = '\1'; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_USHORT_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + unsigned short t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_SHORT_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + short t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_UINT_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + unsigned int t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_INT_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + int t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_ULONG_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + unsigned long t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_LONG_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + long t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_ULONG_LONG_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + unsigned long long t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_LONG_LONG_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + long long t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +int Common_SIZE_T_Endianness_Check() +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + size_t t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ diff --git a/src/Common/Src/Byte_Order/Byte_Order_Integers.h b/src/Common/Src/Byte_Order/Byte_Order_Integers.h new file mode 100644 index 0000000..551f883 --- /dev/null +++ b/src/Common/Src/Byte_Order/Byte_Order_Integers.h @@ -0,0 +1,752 @@ +/*! + Multiverse Engine Project 09/6/2015 Common Byte_Order_Integers.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef MSYS_BYTE_ORDER_INTEGERS_H +#define MSYS_BYTE_ORDER_INTEGERS_H + +/* Make sure we are not included directly. */ +#ifndef MSYS_BYTE_ORDER_H +#error "You should not include __FILE__ directly, it is included automaticly by Byte_Order.h, remove this include. Aborting build." +#endif + +/* Check for C++ compiler. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*! + * int Common_Host_To_Big_Endian_UChar(unsigned char * uc) + * + * Converts a given unsigned CHAR value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_UChar(unsigned char * uc); + +/*! + * int Common_Big_Endian_To_Host_UChar(unsigned char * uc) + * + * Converts a given unsigned CHAR value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_UChar(unsigned char * uc); + +/*! + * int Common_Host_To_Big_Endian_Char(char * c) + * + * Converts a given CHAR value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_Char(char * c); + +/*! + * int Common_Big_Endian_To_Host_Char(char * c) + * + * Converts a given CHAR value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_Char(char * c); + +/*! + * int Common_Host_To_Big_Endian_UShort(unsigned short * us) + * + * Converts a given unsigned SHORT value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_UShort(unsigned short * us); + +/*! + * int Common_Big_Endian_To_Host_UShort(unsigned short * us) + * + * Converts a given unsigned SHORT value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_UShort(unsigned short * us); + +/*! + * int Common_Host_To_Big_Endian_Short(short * s) + * + * Converts a given SHORT value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_Short(short * s); + +/*! + * int Common_Big_Endian_To_Host_Short(short * s) + * + * Converts a given SHORT value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_Short(short * s); + +/*! + * int Common_Host_To_Big_Endian_UInt(unsigned int * ui) + * + * Converts a given unsigned INT value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_UInt(unsigned int * ui); + +/*! + * int Common_Big_Endian_To_Host_UInt(unsigned int * ui) + * + * Converts a given unsigned INT value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_UInt(unsigned int * ui); + +/*! + * int Common_Host_To_Big_Endian_Int(int * i) + * + * Converts a given INT value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_Int(int * i); + +/*! + * int Common_Big_Endian_To_Host_Int(int * i) + * + * Converts a given INT value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_Int(int * i); + +/*! + * int Common_Host_To_Big_Endian_ULong(unsigned long * ul) + * + * Converts a given unsigned LONG value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_ULong(unsigned long * ul); + +/*! + * int Common_Big_Endian_To_Host_ULong(unsigned long * ul) + * + * Converts a given unsigned LONG value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_ULong(unsigned long * ul); + +/*! + * int Common_Host_To_Big_Endian_Long(long * l) + * + * Converts a given LONG value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_Long(long * l); + +/*! + * int Common_Big_Endian_To_Host_Long(long * l) + * + * Converts a given LONG value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_Long(long * l); + +/*! + * int Common_Host_To_Big_Endian_ULong_Long(unsigned long long * ull) + * + * Converts a given unsigned LONG LONG value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_ULong_Long(unsigned long long * ull); + +/*! + * int Common_Big_Endian_To_Host_ULong_Long(unsigned long long * ull) + * + * Converts a given unsigned LONG LONG value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_ULong_Long(unsigned long long * ull); + +/*! + * int Common_Host_To_Big_Endian_Long_Long(long long * ull) + * + * Converts a given LONG LONG value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_Long_Long(long long * ll); + +/*! + * int Common_Big_Endian_To_Host_Long_Long(long long * ll) + * + * Converts a given LONG LONG value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_Long_Long(long long * ll); + +/*! + * int Common_Host_To_Big_Endian_Size_T(size_t * st) + * + * Converts a given SIZE_T value from the host's endianness + * to Big Endian format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Host_To_Big_Endian_Size_T(size_t * st); + +/*! + * int Common_Big_Endian_To_Host_Size_T(size_t * st) + * + * Converts a given SIZE_T value from the Big Endian format + * to the host's endianness format. + * + * Returns COMMON_ERROR_SUCCESS, if the conversion is successful, + * (data will be converted after call returns), + * or if the host is a Big Endian host. (In such case no data alteration + * is performed.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * + * Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given data type's + * byte ordering is unknown for the given host. + * + * In case of error, (returned error code is not COMMON_ERROR_SUCCESS), + * all arguments will be left unaltered. + */ +MSYS_DLL_EXPORT int Common_Big_Endian_To_Host_Size_T(size_t * st); + +/*! + * General Definitions for the Common_*_Endianness_Check() functions: + * + * The functions below all contain the same returns, + * and purpose. + */ + +/*! + * int Common_UCHAR_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a unsigned CHAR argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_UCHAR_Endianness_Check(); + +/*! + * int Common_CHAR_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a CHAR argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_CHAR_Endianness_Check(); + +/*! + * int Common_USHORT_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a unsigned SHORT argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_USHORT_Endianness_Check(); + +/*! + * int Common_SHORT_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a SHORT argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_SHORT_Endianness_Check(); + +/*! + * int Common_UINT_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a unsigned INT argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_UINT_Endianness_Check(); + +/*! + * int Common_INT_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a INT argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_INT_Endianness_Check(); + +/*! + * int Common_ULONG_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a unsigned LONG argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_ULONG_Endianness_Check(); + +/*! + * int Common_LONG_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a LONG argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_LONG_Endianness_Check(); + +/*! + * int Common_ULONG_LONG_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a unsigned LONG LONG argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_ULONG_LONG_Endianness_Check(); + +/*! + * int Common_LONG_LONG_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a LONG LONG argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_LONG_LONG_Endianness_Check(); + +/*! + * int Common_SIZE_T_Endianness_Check() + * + * Template function which checks the host's endianness for the + * UCHAR data type. + * + * Note: This function is the equivalent of calling + * DataProcess_Endianness_Check() with a SIZE_T argument. + * This function is here for the purpose of making it easier + * to use C code. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +MSYS_DLL_EXPORT int Common_SIZE_T_Endianness_Check(); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MSYS_BYTE_ORDER_INTEGERS_H */ + +/* End of Byte_Order_Integers.h */ diff --git a/src/Common/Src/Byte_Order/CMakeLists.txt b/src/Common/Src/Byte_Order/CMakeLists.txt new file mode 100644 index 0000000..d97141a --- /dev/null +++ b/src/Common/Src/Byte_Order/CMakeLists.txt @@ -0,0 +1,14 @@ +# Set output directories. +set(LIBRARY_OUTPUT_PATH ${L_OUTPUT_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${O_OUTPUT_DIR}) + +# Define includes. +set (BYTE_ORDER_INCLUDES Byte_Order.c +Byte_Order_Floating_Points.c +Byte_Order_Integers.c) + +# Define static library. +add_library (Common_Byte_Order_Multiverse_Engine_Static STATIC ${BYTE_ORDER_INCLUDES}) + +# Define shared library. +add_library (Common_Byte_Order_Multiverse_Engine SHARED ${BYTE_ORDER_INCLUDES}) diff --git a/src/Common/Src/CMakeLists.txt b/src/Common/Src/CMakeLists.txt index 15d9c0b..7bf0ccb 100644 --- a/src/Common/Src/CMakeLists.txt +++ b/src/Common/Src/CMakeLists.txt @@ -2,6 +2,12 @@ set(LIBRARY_OUTPUT_PATH ${L_OUTPUT_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${O_OUTPUT_DIR}) +# Create the Byte_Order library. +if (BUILD_BYTE_ORDER) + add_subdirectory(Byte_Order) + set (COMMON_BUILT_SHARED_LIBRARY_LIST ${COMMON_BUILT_SHARED_LIBRARY_LIST} Common_Byte_Order_Multiverse_Engine) + set (COMMON_BUILT_STATIC_LIBRARY_LIST ${COMMON_BUILT_STATIC_LIBRARY_LIST} Common_Byte_Order_Multiverse_Engine_Static) +endif (BUILD_BYTE_ORDER) # Create the mutexes library. if (BUILD_INTERNAL_MUTEX_SUPPORT) @@ -43,6 +49,14 @@ if (BUILD_THREADING_SUBSYSTEM) endif (BUILD_THREADING_SUBSYSTEM) +# Create the Rendering Subsystem. +if (BUILD_RENDERING_SUBSYSTEM) + add_subdirectory(Rendering_Subsystem) + set (COMMON_BUILT_SHARED_LIBRARY_LIST ${COMMON_BUILT_SHARED_LIBRARY_LIST} Rendering_Subsystem_Multiverse_Engine) + set (COMMON_BUILT_STATIC_LIBRARY_LIST ${COMMON_BUILT_STATIC_LIBRARY_LIST} Rendering_Subsystem_Multiverse_Engine_Static) +endif (BUILD_RENDERING_SUBSYSTEM) + + # Add static library. add_library(Common_Multiverse_Engine_Static STATIC Generic_Wrapper.cpp) target_link_libraries(Common_Multiverse_Engine_Static Core_Multiverse_Engine_Static @@ -50,5 +64,5 @@ ${COMMON_BUILT_STATIC_LIBRARY_LIST}) # Add shared library. add_library(Common_Multiverse_Engine SHARED Generic_Wrapper.cpp) -target_link_libraries(Common_Multiverse_Engine Core_Multiverse_Engine_Static +target_link_libraries(Common_Multiverse_Engine Core_Multiverse_Engine ${COMMON_BUILT_SHARED_LIBRARY_LIST}) diff --git a/src/Common/Src/Dynamic_Library_Subsystem/CMakeLists.txt b/src/Common/Src/Dynamic_Library_Subsystem/CMakeLists.txt index 56eb522..24919f1 100644 --- a/src/Common/Src/Dynamic_Library_Subsystem/CMakeLists.txt +++ b/src/Common/Src/Dynamic_Library_Subsystem/CMakeLists.txt @@ -7,12 +7,16 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${O_OUTPUT_DIR}) IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") # Building on windows. set(DYNAMIC_LIBRARY_SUBSYSTEM_INCLUDES Dynamic_Library_Subsystem_Data_Structures.c + Dynamic_Library_Subsystem_Data_Structures_Private_API.c + Dynamic_Library_Subsystem.c Dynamic_Library_Subsystem_Windows.c) unset(DYNAMIC_LIBRARY_SUBSYSTEM_LINK_LIBS) ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") # Building on Linux. set(DYNAMIC_LIBRARY_SUBSYSTEM_INCLUDES Dynamic_Library_Subsystem_Data_Structures.c + Dynamic_Library_Subsystem_Data_Structures_Private_API.c + Dynamic_Library_Subsystem.c Dynamic_Library_Subsystem_POSIX.c) set(DYNAMIC_LIBRARY_SUBSYSTEM_LINK_LIBS dl) @@ -25,8 +29,10 @@ ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") # Actually build it. add_library(Dynamic_Library_Subsystem_Multiverse_Engine_Static STATIC ${DYNAMIC_LIBRARY_SUBSYSTEM_INCLUDES}) target_link_libraries(Dynamic_Library_Subsystem_Multiverse_Engine_Static ${DYNAMIC_LIBRARY_SUBSYSTEM_LINK_LIBS} -Common_Error_Handler_Multiverse_Engine_Static) +Common_Error_Handler_Multiverse_Engine_Static +DataProcess_Memory_Management_Multiverse_Engine_Static) add_library(Dynamic_Library_Subsystem_Multiverse_Engine SHARED ${DYNAMIC_LIBRARY_SUBSYSTEM_INCLUDES}) target_link_libraries(Dynamic_Library_Subsystem_Multiverse_Engine ${DYNAMIC_LIBRARY_SUBSYSTEM_LINK_LIBS} -Common_Error_Handler_Multiverse_Engine) +Common_Error_Handler_Multiverse_Engine +DataProcess_Memory_Management_Multiverse_Engine) diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem.c b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem.c new file mode 100644 index 0000000..5d66bd5 --- /dev/null +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem.c @@ -0,0 +1,626 @@ +/*! + Multiverse Engine Project 23/1/2016 Dynamic_Library_Subsystem Dynamic_Library_Subsystem.c + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Dynamic_Library_Subsystem.h" +#include "Dynamic_Library_Subsystem_Syscall.h" +#include "../Error_Handler/Common_Error_Handler_Structures.h" +#include "../Error_Handler/Common_Error_Handler_Error_Codes.h" +#include "../Error_Handler/Common_Error_Handler_Log_Channel_Defs.h" +#include "../../../Core/Src/DataProcess.h" + +/* External includes. */ +#include +#include + +/* Define the MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID macro. */ +#define MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID MSYS_ERROR_LOG_CHANNEL_DYNLIB + +/* Check for C++ Compiler. */ +#ifdef __cplusplus +/* Define extern C. */ +extern "C" { +#endif /* __cplusplus */ + + int Common_Dynamic_Library_Subsystem_Load_Library(const char * pathToLibrary, const size_t pathToLibraryLength, const bool reloadLibrary, Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + const char * tempPath = NULL; /* Used to verifiy that the path was stored in the management structure. */ + size_t tempPathLength = 0; /* Used to store the length of the tempPath string in bytes. */ + char * copiedPathFromStruct = NULL; /* Used to copy the path from the management structure during a reload. */ + size_t copiedPathLengthFromStruct = 0; /* Used to store the length of the tempPath string in bytes. */ + void * callResult = NULL; /* The result of the call the engine's syscall function. */ + + /* Check to see if the pointer to the management structure is valid. */ + if (lib != NULL) + { + /* Check to see if pathToLibrary is NULL. */ + if ((pathToLibrary != NULL) && (pathToLibraryLength > 0)) + { + /* Check to see if the library is loaded. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library(lib); + if ((retFromCall == COMMON_ERROR_FALSE) || (reloadLibrary)) + { + /* Check and see if the library is loaded. */ + if (retFromCall == COMMON_ERROR_TRUE) + { + /* Get the pathToLibrary pointer from the management structure. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(lib, &tempPath, &tempPathLength); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Check and see if the given pathToLibrary is from the lib structure. */ + if (tempPath == pathToLibrary) + { + /* Copy the length data. */ + copiedPathLengthFromStruct = tempPathLength; + + /* We need to copy the pathToLibrary data. */ + retFromCall = DataProcess_Reallocate_C_String(&copiedPathFromStruct, 0, copiedPathLengthFromStruct); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (copiedPathFromStruct != NULL)) + { + /* Copy the path string. */ + memcpy(copiedPathFromStruct, tempPath, copiedPathLengthFromStruct); + } + else + { + /* Could not allocate memory for tempPath. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Load_Library(): Memory allocation function returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + + /* Reset tempPath and tempPathLength. */ + tempPath = NULL; + tempPathLength = 0; + } + + /* Call Unload_Library. */ + retFromCall = Common_Dynamic_Library_Subsystem_Unload_Library(lib); + } + else + { + /* Could not get the pathToLibrary pointer from the data structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Load_Library(): Unable to get the pathToLibrary pointer from the data structure."); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Engine call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Log additional error. */ + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, we could not set the error flag for the internal library structure. Engine Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Reset retFromCall. */ + retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + } + + /* Only continue if the library was unloaded, or if we did not need to unload the library. */ + if ((retFromCall == COMMON_ERROR_UNKNOWN_ERROR) || (retFromCall == COMMON_ERROR_SUCCESS)) + { + /* Blank the values in lib. */ + Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library(lib); + + /* Copy the path string into lib. */ + retFromCall = Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library(lib, ((copiedPathFromStruct == NULL) ? (pathToLibrary) : (copiedPathFromStruct)), ((copiedPathLengthFromStruct == 0) ? (pathToLibraryLength) : (copiedPathLengthFromStruct))); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Get the path back. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(lib, &tempPath, &tempPathLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPath != NULL) && (tempPathLength > 0)) + { + /* Call Syscall. */ + retFromCall = Common_Dynamic_Library_Subsystem_Load_Library_Syscall(tempPath, tempPathLength, &callResult); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Copy returned osSpecificPointerData to the management structure. */ + retFromCall = Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library(lib, callResult); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Set bIsLoaded. */ + retFromCall = Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library(lib, true); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Load_Library(): <"); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, tempPath); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "> loaded."); + } + else + { + /* Could not set is loaded flag. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* Could not set os specific pointer data. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* Could not get osSpecificPointerData. Syscall failed. Set bLastCallEncounteredAnError flag in management structure. */ + ret = retFromCall; + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Load_Library_Syscall(): Could not load <"); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, pathToLibrary); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "> Host function returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Log additional error. */ + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, we could not set the error flag for the internal library structure. Engine Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Could not retrive path from lib structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* Could not copy path data. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* Encountered an error during the unload. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Load_Library(): Unable to reload library."); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Log additional error. */ + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, we could not set the error flag for the internal library structure. Engine Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Check and see if the library was already loaded. */ + if (retFromCall == COMMON_ERROR_TRUE) + { + /* Library is already loaded. */ + ret = DYNLIB_ERROR_LIBRARY_ALREADY_LOADED; + } + else + { + /* An error occured. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Load_Library(): Could not get loaded library flag from management structure. Engine call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* pathToLibrary is NULL. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Load_Library(): No path to the library was given. Unable to load a library without the path to it."); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Log additional error. */ + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, we could not set the error flag for the internal library structure. Engine Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Management structure is invalid. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Load_Library(): The engine's library structure for the given library is invalid. Unable to load a library without a valid library structure."); + } + + /* Check for an allocated copiedPathFromStruct and deallocate it if needed. */ + if (copiedPathFromStruct != NULL) + { + DataProcess_Deallocate_CString(&copiedPathFromStruct); + copiedPathLengthFromStruct = 0; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Unload_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + void * osData = NULL; /* The osSpecificPointerData from the management structure. */ + char * pSym = NULL; /* Used to log the library path to the error log. */ + size_t pSymLength = 0; /* The length of pSym in bytes. */ + + /* Check to see if the pointer to the management structure is valid. */ + if (lib != NULL) + { + /* Check for a loaded library. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library(lib); + if (retFromCall == COMMON_ERROR_TRUE) + { + /* Reset bLastCallEncounteredAnError. */ + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, false); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Get the osSpecificPointerData from the management structure. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library(lib, &osData); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (osData != NULL)) + { + /* Call the syscall. */ + retFromCall = Common_Dynamic_Library_Subsystem_Unload_Library_Syscall(osData); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* The library was unloaded successfully. */ + retFromCall = Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library(lib, false); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + retFromCall = Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library(lib, NULL); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Unload_Library(): <"); + + /* Attempt to get the library path for the error log. */ + if ((Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(lib, &pSym, &pSymLength) == COMMON_ERROR_SUCCESS) && + (pSym != NULL)) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, pSym); + } + else + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "[ERROR: COULD NOT FETCH PATH TO LIBRARY FROM MANAGEMENT STRUCTURE.]"); + } + pSym = NULL; /* Clear abused pSym. */ + + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "> unloaded."); + } + else + { + /* Could not clear os specific pointer data in management structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Unload_Library(): Could not clear os specific pointer data in management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, could not set the error flag in the management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Could not clear is loaded flag in management structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Unload_Library(): Could not clear is loaded flag in management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, could not set the error flag in the management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Could not unload the library. */ + ret = retFromCall; + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Unload_Library(): Could not unload <"); + + /* Attempt to get the library path for the error log. */ + if ((Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(lib, &pSym, &pSymLength) == COMMON_ERROR_SUCCESS) && + (pSym != NULL)) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, pSym); + } + else + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "[ERROR: COULD NOT FETCH PATH TO LIBRARY FROM MANAGEMENT STRUCTURE.]"); + } + pSym = NULL; /* Clear abused pSym. */ + + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "> Host function returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, could not set the error flag in the management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Could not get osSpecificPointerData from management structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Unload_Library(): Could not get os specific data pointer from management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + else + { + /* Could not reset last call incountered an error flag. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Unload_Library(): Could not reset management structure's error flag. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + else + { + /* Library is not loaded. */ + ret = DYNLIB_ERROR_LIBRARY_NOT_LOADED; + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Unload_Library(): The given library <"); + + /* Attempt to get the library path for the error log. */ + if ((Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(lib, &pSym, &pSymLength) == COMMON_ERROR_SUCCESS) && + (pSym != NULL)) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, pSym); + } + else + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "[ERROR: COULD NOT FETCH PATH TO LIBRARY FROM MANAGEMENT STRUCTURE.]"); + } + pSym = NULL; /* Clear abused pSym. */ + + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "> is not loaded."); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, could not set the error flag in the management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Management structure is invalid. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Unload_Library(): The engine's library structure for the given library is invalid. Unable to unload a library without a valid library structure."); + } + + /* Return result. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Reload_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + char * tempPath = NULL; /* Used to verifiy that the path was stored in the management structure. */ + size_t tempPathLength = 0; /* Used to store the length of the tempPath string in bytes. */ + + /* Check for invalid arguments. */ + if (lib != NULL) + { + /* Get the pathToLibrary pointer from the management structure. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(lib, &tempPath, &tempPathLength); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Check for valid path data */ + if ((tempPath != NULL) && (tempPathLength > 0)) + { + /* Call Common_Dynamic_Library_Subsystem_Load_Library(). */ + ret = Common_Dynamic_Library_Subsystem_Load_Library(tempPath, tempPathLength, true, lib); + } + else + { + /* The given library structure does not define a path. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Reload_Library(): The given library structure does not define a path."); + } + } + else + { + /* Could not get the pathToLibrary pointer from the data structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Reload_Library(): Unable to get the pathToLibrary pointer from the data structure."); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Engine call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Log additional error. */ + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, we could not set the error flag for the internal library structure. Engine Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Library structure is invalid. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Reload_Library(): The engine's library structure for the given library is invalid."); + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_Symbol(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib, const char * symbolName, const size_t symbolNameLength, void ** retSym) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + void * pSym = NULL; /* The returned symbol pointer. */ + size_t pSymLength = 0; /* The length of pSym (When being abused as a path string) in bytes. */ + void * osData = NULL; /* The osSpecificPointerData from the management structure. */ + + /* Check to see if the pointer to the management structure is valid. */ + if (lib != NULL) + { + /* Check and see if retSym is valid. */ + if (retSym != NULL) + { + /* Check to see if symbolName is NULL, or symbolNameLength is less than or equal to zero. */ + if ((symbolName != NULL) && (symbolNameLength > 0)) + { + /* Check for a loaded library. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library(lib); + if (retFromCall == COMMON_ERROR_TRUE) + { + /* Reset bLastCallEncounteredAnError. */ + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, false); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Get the osSpecificPointerData from the management structure. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library(lib, &osData); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (osData != NULL)) + { + /* Call syscall. */ + retFromCall = Common_Dynamic_Library_Subsystem_Get_Symbol_Syscall(osData, symbolName, symbolNameLength, &pSym); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Copy pSym to retSym. */ + (*retSym) = pSym; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* An error occured fetching the symbol. */ + ret = retFromCall; + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_Symbol(): Could not fetch symbol in <"); + + /* Attempt to get the library path for the error log. */ + if ((Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(lib, (char**)&pSym, &pSymLength) == COMMON_ERROR_SUCCESS) && + (pSym != NULL)) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, (char*)pSym); + } + else + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "[ERROR: COULD NOT FETCH PATH TO LIBRARY FROM MANAGEMENT STRUCTURE.]"); + } + pSym = NULL; /* Clear abused pSym. */ + + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "> Host function returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, could not set the error flag in the management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Could not get osSpecificPointerData from management structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_Symbol(): Could not get os specific data pointer from management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + else + { + /* Could not reset last call incountered an error flag. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_Symbol(): Could not reset management structure's error flag. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + else + { + /* Library is not loaded. */ + ret = DYNLIB_ERROR_LIBRARY_NOT_LOADED; + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_Symbol(): The given library <"); + + /* Attempt to get the library path for the error log. */ + if ((Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(lib, (char**)&pSym, &pSymLength) == COMMON_ERROR_SUCCESS) && + (pSym != NULL)) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, (char*)pSym); + } + else + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "[ERROR: COULD NOT FETCH PATH TO LIBRARY FROM MANAGEMENT STRUCTURE.]"); + } + pSym = NULL; /* Clear abused pSym. */ + + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "> is not loaded."); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, could not set the error flag in the management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* symbolName is NULL. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_Symbol(): No symbol name was given, cannot load a symbol without a name to identifiy it."); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, could not set the error flag in the management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* retSym is NULL. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + retFromCall = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(lib, true); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_Symbol(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Additionally, could not set the error flag in the management structure. Call returned: "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Library structure is invalid. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_Symbol(): The engine's library structure for the given library is invalid. Unable to lookup function without a valid library structure."); + } + + /* Return result. */ + return ret; + } + +#ifdef __cplusplus +} /* End of extern C. */ +#endif /* __cplusplus */ diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem.h b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem.h index 7042a1e..e82500c 100644 --- a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem.h +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem.h @@ -18,24 +18,32 @@ https://github.com/codebase7/mengine */ -// Include guard. +/* Include guard. */ #ifndef MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_H #define MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_H -#include // bool. -#include // NULL. -#include // Malloc. +/* Internal includes. */ +#include "../../../DLL_PORT.h" /* Defines MSYS_DLL_EXPORT, and MSYS_DLL_IMPORT_TEMPLATE. */ -// Internal includes. -#include "Dynamic_Library_Subsystem_Data_Structures.h" // Contains data structures used internally. +/* Check for MSVC. */ +#ifdef _MSC_FULL_VER +#include "..\..\..\stdbool.h" /* bool. (MSVC is special.) */ +#else +#include /* bool. */ +#endif /* _MSC_FULL_VER */ +#include /* NULL. */ +#include /* Malloc. */ + +/* Internal includes. */ +#include "Dynamic_Library_Subsystem_Data_Structures.h" /* Contains data structures used internally. */ #ifdef __win32 -#include "..\Error_Handler\Common_Error_Handler_Internal.h" // Contains the function defintions for calling the common error handler. +#include "..\Error_Handler\Common_Error_Handler_Internal.h" /* Contains the function defintions for calling the common error handler. */ #else -#include "../Error_Handler/Common_Error_Handler_Internal.h" // Contains the function defintions for calling the common error handler. -#endif // __win32 +#include "../Error_Handler/Common_Error_Handler_Internal.h" /* Contains the function defintions for calling the common error handler. */ +#endif /* __win32 */ -// Defines for the default library extension. +/* Defines for the default library extension. */ #ifdef __win32 #define DL_EXTENSION ".dll" #elif __linux__ @@ -43,41 +51,36 @@ #endif #ifdef __cplusplus -// Define extern C. +/* Define extern C. */ extern "C" { #endif /*! - * int Common_Dynamic_Library_Subsystem_Load_Library(const char * pathToLibrary, const bool reloadLibrary, + * int Common_Dynamic_Library_Subsystem_Load_Library(const char * pathToLibrary, const size_t pathToLibraryLength, const bool reloadLibrary, * Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib) * * This function calls the system specific dynamic library handler and attempts to load the requested library. * - * Note: Due to the nature of the Dynamic_Library_Subsystem being a generic wrapper over the system specific calls, - * we cannot return more informative error messages. (Not without a generic error lookup table anyway....) - * * Pram: const char * pathToLibrary, a pointer to the c-string that contains the path to the library on disk. + * Pram: const size_t pathToLibraryLength, length of the pathToLibrary c-string in bytes. * Pram: const bool reloadLibrary, whether or not to reload the library if it is already loaded. * Pram: Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib, A pointer to a properly constructed * management data structure used internally. * - * Returns 0 if the library was successfully loaded. (The value of lib.bLastCallEncounteredAnError will be false in this case as well.) - * Returns -1 if the library could not be loaded. (The value of lib.bLastCallEncounteredAnError will be true in this case as well.) - * Returns -2 if the attempt to unload the library failed. (Only possible if reloadLibrary is true.) (The value of lib.bLastCallEncounteredAnError will be true in this case as well.) - * Returns -3 if the pathToLibrary pointer was NULL. (The value of lib.bLastCallEncounteredAnError will be true in this case as well.) - * Returns -4 if the given Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library pointer was NULL. - * Returns 1 if the library was already loaded. (Only possible if reloadLibrary is false.) (The value of lib.bLastCallEncounteredAnError will be false in this case as well.) + * Returns COMMON_ERROR_SUCCESS if the library was successfully loaded. (The value of lib.bLastCallEncounteredAnError will be false in this case as well.) + * Returns Translated host system error code if the library could not be loaded. (The value of lib.bLastCallEncounteredAnError will be true in this case as well.) + * Returns All errors returned by Common_Dynamic_Library_Subsystem_Unload_Library() if the attempt to unload the library failed. (Only possible if reloadLibrary is true.) (The value of lib.bLastCallEncounteredAnError will be true in this case as well.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the pathToLibrary pointer was NULL. (The value of lib.bLastCallEncounteredAnError will be true in this case as well.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library pointer was NULL. + * Returns DYNLIB_ERROR_LIBRARY_ALREADY_LOADED if the library was already loaded. (Only possible if reloadLibrary is false.) (The value of lib.bLastCallEncounteredAnError will be false in this case as well.) */ - int Common_Dynamic_Library_Subsystem_Load_Library(const char * pathToLibrary, const bool reloadLibrary, - Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib); + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Load_Library(const char * pathToLibrary, const size_t pathToLibraryLength, const bool reloadLibrary, + Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib); /*! * int Common_Dynamic_Library_Subsystem_Unload_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib) * * This function calls the system specific dynamic library handler and attempts to unload the requested library. * - * Note: Due to the nature of the Dynamic_Library_Subsystem being a generic wrapper over the system specific calls, - * we cannot return more informative error messages. (Not without a generic error lookup table anyway....) - * * Note: Due to the underlying system, even if this call is successful, the library may still be loaded in memory. * Internally, the Dynamic_Library_Subsystem blocks attempts to load the library multiple times via calls to Common_Dynamic_Library_Subsystem_Load_Library(). * (I.e. Load a library that is already loaded.) This is to try and prevent specific systems that use reference counts @@ -91,41 +94,58 @@ extern "C" { * Pram: Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib, A pointer to a properly constructed * management data structure used internally. * - * Returns 0 if the library unload call returned successful. - * Returns -1 if the library was not loaded according to the given management data structure. - * Returns -2 if the library unload call returned unsuccessful. - * Returns -4 if the given Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library pointer was NULL. + * Returns COMMON_ERROR_SUCCESS if the library unload call returned successful. + * Returns DYNLIB_ERROR_LIBRARY_NOT_LOADED if the library was not loaded according to the given management data structure. + * Returns Translated host system error code if the library unload call returned unsuccessful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library pointer was NULL. */ - int Common_Dynamic_Library_Subsystem_Unload_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib); + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Unload_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib); /*! - * void * Common_Dynamic_Library_Subsystem_Get_Symbol(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib, - * const char * symbolName) + * int Common_Dynamic_Library_Subsystem_Reload_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib) * - * This function calls the system specific dynamic library handler and attempts to fetch a pointer to the first byte - * of the given symbol. + * This function reloads the given library. + * + * (Effectively this function is a wrapper around Common_Dynamic_Library_Subsystem_Load_Library(), setting it's + * reloadLibrary argument to true, and using the given management structure's path.) + * + * Returns COMMON_ERROR_SUCCESS if the library is reloaded successfully. * - * Note: Due to the nature of the Dynamic_Library_Subsystem being a generic wrapper over the system specific calls, - * we cannot return more informative error messages. (Not without a generic error lookup table anyway....) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL, or if the given management structure + * does not define a path. (Such as when it's first created or if it was cleared by a call to + * Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library().) * - * Note: To check for an error result from this function, check the value of lib.bLastCallEncounteredAnError. - * If lib.bLastCallEncounteredAnError is false, no error occured during this function. Otherwise an error occured. + * Otherwise returns any error given by Common_Dynamic_Library_Subsystem_Load_Library(). + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Reload_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_Symbol(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib, + * const char * symbolName, const size_t symbolNameLength, void ** retSym) + * + * This function calls the system specific dynamic library handler and attempts to fetch a pointer to the first byte + * of the given symbol. * * Pram: Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, A pointer to a properly constructed * management data structure used internally. * Pram: const char * symbolName, A pointer to a c-string that contains the name of the requested symbol to * search for. + * Pram: const size_t symbolNameLength, the length of symbolName in bytes. + * Pram: void ** retSym, A double pointer that will hold the address to the first byte of the requested symbol if the function succeeds. + * (retSym will NOT be modified if this function returns an error.) * - * Returns a valid pointer if the resulting lookup returned a valid pointer. (The value of lib.bLastCallEncounteredAnError will be false in this case.) + * Returns COMMON_ERROR_SUCCESS if the resulting lookup returned a valid pointer. (The value of lib.bLastCallEncounteredAnError will be false in this case.) * Returns a NULL pointer if the resulting lookup returned a NULL pointer. (The value of lib.bLastCallEncounteredAnError will be false in this case as well.) - * Returns a NULL pointer if the lookup failed for some reason. (The value of lib.bLastCallEncounteredAnError will be true in this case.) - * Returns a NULL pointer if the given Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library pointer was NULL. + * (Note: Some systems may not support NULL symbols, in this case the result is the same as if the lookup failed, see below.) + * Returns Translated host system error code if the lookup failed for some reason. (The value of lib.bLastCallEncounteredAnError will be true in this case.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library, symbolName, or retSym pointer(s) was / were set to NULL. + * Returns DYNLIB_ERROR_LIBRARY_NOT_LOADED if the library was not loaded according to the given management data structure. */ - void * Common_Dynamic_Library_Subsystem_Get_Symbol(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib, const char * symbolName); + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Get_Symbol(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib, const char * symbolName, const size_t symbolNameLength, void ** retSym); #ifdef __cplusplus -} // End of extern C. -#endif +} /* End of extern C. */ +#endif /* __cplusplus */ -#endif +#endif /* MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_H */ -// End of Dynamic_Library_Subsystem.h +/* End of Dynamic_Library_Subsystem.h */ diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures.c b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures.c index 7b1dc8c..c73a2f5 100644 --- a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures.c +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures.c @@ -18,44 +18,363 @@ https://github.com/codebase7/mengine */ +/* Internal includes */ #include "Dynamic_Library_Subsystem.h" +#include "Dynamic_Library_Subsystem_Data_Structures_Private_API.h" +#include "../../../Core/Src/DataProcess.h" +#include "../Error_Handler/Common_Error_Handler_Error_Codes.h" +#include "../Error_Handler/Common_Error_Handler_Internal.h" +#include "../Error_Handler/Common_Error_Handler_Structures.h" +#include "../Error_Handler/Common_Error_Handler_Log_Channel_Defs.h" +/* External includes. */ +#include +#include + +/* Define the MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID macro. */ +#define MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID MSYS_ERROR_LOG_CHANNEL_DYNLIB + +/* Check for C++ Compiler. */ #ifdef __cplusplus -// Define extern C. +/* Define extern C. */ extern "C" { -#endif - Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library() +#endif /* __cplusplus */ + int Common_Dynamic_Library_Subsystem_Get_API_Major_Version_Number() { - // Create the referece. + /* Return the API Version number. */ + return MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_API_MAJOR_VER; + } + + int Common_Dynamic_Library_Subsystem_Get_API_Minor_Version_Number() + { + /* Return the API Version number. */ + return MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_API_MINOR_VER; + } + + int Common_Dynamic_Library_Subsystem_Get_API_Revision_Version_Number() + { + /* Return the API Version number. */ + return MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_API_REVISION_VER; + } + + int Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library ** lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * result = NULL; - result = ((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library*)(malloc(sizeof(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library)))); - // Check for a valid result. - if (result != NULL) + /* Check for valid arguments. */ + if (lib != NULL) { - // Initilize the vars. - result->bIsLoaded = false; - result->bLastCallEncounteredAnError = false; - result->osSpecificPointerData = NULL; - result->pathToLibrary = NULL; + /* Create the referece. */ + retFromCall = DataProcess_Reallocate_C_String(((char**)(&result)), 0, sizeof(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library)); + + /* Check for a valid result. */ + if ((retFromCall == COMMON_ERROR_SUCCESS) && (result != NULL)) + { + /* Set the pointer to NULL. */ + result->pointer = NULL; + + /* Call the real function. */ + ret = Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private **)(&(result->pointer)))); + } + else + { + /* Could not init structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library(): Memory allocation function returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not allocate memory for management structure."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); } - // Return result. - return result; + /* Return result. */ + return ret; } - void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib) + void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library ** lib) { - // Check for valid pointer. - if (lib != NULL) + /* Check for valid pointer. */ + if ((lib != NULL) && ((*lib) != NULL)) + { + /* Check for allocated pointer. */ + if ((*lib)->pointer != NULL) + { + /* Deallocate the pointer. */ + Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private **)(&((*lib)->pointer)))); + } + + /* Free the structure. */ + DataProcess_Deallocate_CString(((char **)(lib))); + } + + /* Exit function. */ + return; + } + + void Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib) + { + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL)) { - // Free the structure. - free(lib); + /* Call real function. */ + Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer))); } - // Exit function. + /* Exit function. */ return; } + + int Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL)) + { + /* Call real function. */ + ret = Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer))); + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + /* That's an error, we didn't set up the private data structure correctly or it's been corrupted. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Private API data structure pointer is invalid."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const bool value) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL)) + { + /* Call real function. */ + ret = Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer)), value); + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + /* That's an error, we didn't set up the private data structure correctly or it's been corrupted. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Private API data structure pointer is invalid."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL)) + { + /* Call real function. */ + ret = Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer))); + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + /* That's an error, we didn't set up the private data structure correctly or it's been corrupted. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Private API data structure pointer is invalid."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const bool value) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL)) + { + /* Call real function. */ + ret = Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer)), value); + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + /* That's an error, we didn't set up the private data structure correctly or it's been corrupted. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Private API data structure pointer is invalid."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, void ** retVar) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL) && (retVar != NULL)) + { + /* Call real function. */ + ret = Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer)), retVar); + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + /* That's an error, we didn't set up the private data structure correctly or it's been corrupted. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Private API data structure pointer is invalid."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, void * value) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL)) + { + /* Call real function. */ + ret = Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer)), value); + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + /* That's an error, we didn't set up the private data structure correctly or it's been corrupted. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Private API data structure pointer is invalid."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const char ** retVar, size_t * retVarLength) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL) && (retVar != NULL) && (retVarLength != NULL)) + { + /* Call real function. */ + ret = Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer)), retVar, retVarLength); + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + /* That's an error, we didn't set up the private data structure correctly or it's been corrupted. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Private API data structure pointer is invalid."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const char * value, const size_t valueLength) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + char * tempPath = NULL; + + /* Check for valid pointers. */ + if ((lib != NULL) && (lib->pointer != NULL) && (((value == NULL) && (valueLength == 0)) || ((value != NULL) && (valueLength > 0)))) + { + /* Call real function. */ + ret = Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library_Private(((Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private *)(lib->pointer)), value, valueLength); + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + /* That's an error, we didn't set up the private data structure correctly or it's been corrupted. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Private API data structure pointer is invalid."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; + } + #ifdef __cplusplus -} // End of extern C. -#endif +} /* End of extern C. */ +#endif /* __cplusplus */ diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures.h b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures.h index f5beb16..1b41735 100644 --- a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures.h +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures.h @@ -18,71 +18,239 @@ https://github.com/codebase7/mengine */ -// Include guard. +/* Include guard. */ #ifndef MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_H #error "You must include the Dynamic_Library_Subsystem.h header file. It will include all of the other needed headers." #else #ifndef MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_DATA_STRUCTURES_H #define MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_DATA_STRUCTURES_H -// Define the supported API level. -#define MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_API_LEVEL 0 +/* Define the supported API version numbers. */ +#define MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_API_MAJOR_VER 0 +#define MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_API_MINOR_VER 0 +#define MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_API_REVISION_VER 0 +/* Internal includes. */ +#include "../../../DLL_PORT.h" + +/* Check for C++ Compiler. */ #ifdef __cplusplus -// Define extern C. +/* Define extern C. */ extern "C" { -#endif - // Define Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library. +#endif /* __cplusplus */ + /* Define Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library. */ struct Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library { - bool bIsLoaded; // Whether or not this library is loaded. - bool bLastCallEncounteredAnError; /* - Whether or not this library has encountered an error in the last call. - (Should reset to false when a new call is made and no error occurs.) - */ - - void * osSpecificPointerData; /* - Pointer to an OS specific data structure. - (For example, Windows defines loaded libraries with an HMODULE pointer, - which is needed to unload the library or to search it.) - */ - const char * pathToLibrary; // Path to the dynamic library file on disk. + void * pointer; }; + /* Create user defined data type for Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library structure. */ typedef struct Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library; /*! - * Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library() + * int Common_Dynamic_Library_Subsystem_Get_API_Major_Version_Number() + * + * Returns the API major version number for the Dynamic Library Subsystem. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Get_API_Major_Version_Number(); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_API_Minor_Version_Number() + * + * Returns the API minor version number for the Dynamic Library Subsystem. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Get_API_Minor_Version_Number(); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_API_Revision_Version_Number() + * + * Returns the API revision version number for the Dynamic Library Subsystem. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Get_API_Revision_Version_Number(); + + /*! + * int Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library ** lib) * * Creates a Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library data structure in a neutral state. * * This function should be called when constructing a Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library object. * As it will correctly setup said object. * - * WARNING: The caller is responsible for calling free() or - * Destroy_Loaded_Dynamic_Library() on the successfully created structure. + * WARNING: The caller is responsible for calling + * Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library() on the successfully created structure. + * + * WARNING: This function will NOT deallocate a preexisting structure, and will overwrite the given pointer if it is + * non-NULL if this function succeeds. Therefore if you need to keep the pointer, copy it elsewhere before calling this + * function. + * + * Returns COMMON_ERROR_SUCCESS if successful. (The created structure will be pointed to by lib.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given double pointer is NULL. + * Returns COMMON_ERROR_MEMORY_ERROR if the data structure could not be allocated. + * Otherwise returns the appropriate error code. * - * Returns a pointer to a Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library data structure if successful. - * Otherwise returns NULL. + * No-alteration clause: + * - In case of error, this function will not modify it's arguments. */ - Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library(); + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library ** lib); /*! - * void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib) + * void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library ** lib) * - * Destroys (frees) a created Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library data structure. + * Destroys (frees) a created Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library data structure and set's it's pointer to NULL. * - * Pram: Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, a pointer to - * a successfully created Loaded_Dynamic_Library data structure. + * Pram: Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library ** lib, a double pointer to + * a successfully created Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library data structure. * * This function has no return. */ - void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib); + MSYS_DLL_EXPORT void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library ** lib); + + /*! + * void Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib) + * + * Blanks the given Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library data structure's members. (Deallocating memory for them if needed.) + * + * If given an invalid pointer this function will silently fail. + * + * This function has no return. + */ + MSYS_DLL_EXPORT void Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib) + * + * ACCESSOR FUNCTION. + * + * Returns the status of the bIsLoaded member variable. + * + * Returns COMMON_ERROR_TRUE if the library is loaded. + * Returns COMMON_ERROR_FALSE if the library is NOT loaded. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib); + + /*! + * int Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const bool value) + * + * ACCESSOR FUNCTION. + * + * Sets the status of the bIsLoaded member variable to the given value. + * + * Returns COMMON_ERROR_SUCCESS if the status was set to the given value successfully. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const bool value); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib) + * + * ACCESSOR FUNCTION. + * + * Returns the status of the bLastCallEncounteredAnError member variable. + * + * Returns COMMON_ERROR_TRUE if the last operation on this library encountered an error. + * Returns COMMON_ERROR_FALSE if the last operation on this library did NOT encounter an error. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib); + + /*! + * int Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const bool value) + * + * ACCESSOR FUNCTION. + * + * Sets the status of the bLastCallEncounteredAnError member variable to the given value. + * + * Returns COMMON_ERROR_SUCCESS if the status was set to the given value successfully. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const bool value); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, void ** retVar) + * + * ACCESSOR FUNCTION. + * + * Returns the pointer for the osSpecificPointerData member variable. + * + * WARNING: This function will NOT deallocate a preexisting structure, and will overwrite the given pointer if it is + * non-NULL if this function succeeds. Therefore if you need to keep the pointer, copy it elsewhere before calling this + * function. + * + * WARNING: The caller should NOT deallocate the returned pointer. + * + * Returns COMMON_ERROR_SUCCESS if the data was returned successfully. (retVar will point to the pointer value in this case.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, void ** retVar); + + /*! + * int Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, void * value) + * + * ACCESSOR FUNCTION. + * + * Sets the pointer for the osSpecificPointerData member variable to the given value. + * + * Returns COMMON_ERROR_SUCCESS if the status was set to the given value successfully. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, void * value); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const char ** retVar, size_t * retVarLength) + * + * ACCESSOR FUNCTION. + * + * Returns the pointer for the pathToLibrary member variable, and the value of the pathToLibraryLength member variable. + * + * WARNING: This function will NOT deallocate a preexisting structure, and will overwrite the given pointer if it is + * non-NULL if this function succeeds. Therefore if you need to keep the pointer, copy it elsewhere before calling this + * function. + * + * WARNING: The caller should NOT deallocate the returned pointer. + * + * Returns COMMON_ERROR_SUCCESS if the data was returned successfully. (retVar will point to the pointer value in this case.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const char ** retVar, size_t * retVarLength); + + /*! + * int Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const char * value, const size_t valueLength) + * + * ACCESSOR FUNCTION. + * + * Sets the pointer for the pathToLibrary member variable to the given value. + * + * Note this function serves two purposes, to allocate and copy the needed path string into the structure, + * and to deallocate a pre-existing path string in the structure. The mode that is chosen is dependant on the + * arguments given to the function. + * + * I.e. If value is non-NULL and valueLength is greater than zero, then the given path string will be copied. + * If value is NULL, and valueLength is equal to zero, then any pre-existing path string in the structure will be + * deallocated. Any other combination will generate a COMMON_ERROR_INVALID_ARGUMENT error code and the structure + * will not be modified. + * + * Returns COMMON_ERROR_SUCCESS if the status was set to the given value successfully. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers or length is NULL. + * Otherwise returns the appropriate error code. + * + * No-alteration clause: + * - In case of error, this function will not modify it's arguments. + */ + MSYS_DLL_EXPORT int Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library * lib, const char * value, const size_t valueLength); + #ifdef __cplusplus -} // End of extern C. -#endif +} /* End of extern C. */ +#endif /* __cplusplus */ -#endif -#endif +#endif /* MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_DATA_STRUCTURES_H */ +#endif /* MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_H */ -// End of Dynamic_Library_Subsystem_Data_Structures.h +/* End of Dynamic_Library_Subsystem_Data_Structures.h */ diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures_Private_API.c b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures_Private_API.c new file mode 100644 index 0000000..f5c86e9 --- /dev/null +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures_Private_API.c @@ -0,0 +1,358 @@ +/*! + Multiverse Engine Project 22/1/2016 Dynamic_Library_Subsystem Dynamic_Library_Subsystem_Data_Structures_Private_API.c + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes */ +#include "Dynamic_Library_Subsystem.h" +#include "Dynamic_Library_Subsystem_Data_Structures_Private_API.h" +#include "../../../Core/Src/DataProcess.h" +#include "../Error_Handler/Common_Error_Handler_Error_Codes.h" +#include "../Error_Handler/Common_Error_Handler_Internal.h" +#include "../Error_Handler/Common_Error_Handler_Structures.h" +#include "../Error_Handler/Common_Error_Handler_Log_Channel_Defs.h" + +/* External includes. */ +#include +#include + +/* Define the MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID macro. */ +#define MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID MSYS_ERROR_LOG_CHANNEL_DYNLIB + +/* Check for C++ Compiler. */ +#ifdef __cplusplus +/* Define extern C. */ +extern "C" { +#endif /* __cplusplus */ + int Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private ** lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * result = NULL; + + /* Check for valid arguments. */ + if (lib != NULL) + { + /* Create the referece. */ + retFromCall = DataProcess_Reallocate_C_String(((char**)(&result)), 0, sizeof(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private)); + + /* Check for a valid result. */ + if ((retFromCall == COMMON_ERROR_SUCCESS) && (result != NULL)) + { + /* Initilize the vars. */ + result->pathToLibrary = NULL; /* Avoid potential non-NULL causing a memory deallocation attempt for a pointer that is not allocated. */ + Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library_Private(result); + + /* Copy the pointer to lib. */ + (*lib) = result; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not init structure. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library_Private(): Memory allocation function returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not allocate memory for management structure."); + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Return result. */ + return ret; + } + + void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private ** lib) + { + /* Check for valid pointer. */ + if ((lib != NULL) && ((*lib) != NULL)) + { + /* Free the structure. */ + DataProcess_Deallocate_CString(((char **)(lib))); + } + + /* Exit function. */ + return; + } + + void Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib) + { + /* Check for valid pointer. */ + if (lib != NULL) + { + /* Clear the data. */ + lib->bIsLoaded = false; + lib->bLastCallEncounteredAnError = false; + lib->osSpecificPointerData = NULL; + lib->pathToLibraryLength = 0; + + /* Check for set pathToLibrary. */ + if (lib->pathToLibrary != NULL) + { + /* Deallocate memory for the pathToLibrary variable. */ + DataProcess_Deallocate_CString(&(lib->pathToLibrary)); + } + } + + /* Exit function. */ + return; + } + + int Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointer. */ + if (lib != NULL) + { + /* Check bIsLoaded. */ + ret = ((lib->bIsLoaded) ? (COMMON_ERROR_TRUE) : (COMMON_ERROR_FALSE)); + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const bool value) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointer. */ + if (lib != NULL) + { + /* Set bIsLoaded. */ + lib->bIsLoaded = value; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointer. */ + if (lib != NULL) + { + /* Check bLastCallEncounteredAnError. */ + ret = ((lib->bLastCallEncounteredAnError) ? (COMMON_ERROR_TRUE) : (COMMON_ERROR_FALSE)); + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const bool value) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointer. */ + if (lib != NULL) + { + /* Set bLastCallEncounteredAnError. */ + lib->bLastCallEncounteredAnError = value; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, void ** retVar) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointer. */ + if ((lib != NULL) && (retVar != NULL)) + { + /* Get the osSpecificPointerData and copy it to retVar. */ + (*retVar) = lib->osSpecificPointerData; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, void * value) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointer. */ + if (lib != NULL) + { + /* Copy the pointer value to osSpecificPointerData. */ + lib->osSpecificPointerData = value; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const char ** retVar, size_t * retVarLength) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointer. */ + if ((lib != NULL) && (retVar != NULL) && (retVarLength != NULL)) + { + /* Get the pathToLibrary pointer and copy it to retVar. */ + (*retVar) = lib->pathToLibrary; + + /* Get the length of pathToLibrary and copy it to retVarLength. */ + (*retVarLength) = lib->pathToLibraryLength; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const char * value, const size_t valueLength) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + char * tempPath = NULL; + + /* Check for valid pointer. */ + if ((lib != NULL) && (((value == NULL) && (valueLength == 0)) || ((value != NULL) && (valueLength > 0)))) + { + /* OK, allocate memory. If needed. */ + if ((value != NULL) && (valueLength > 0)) + { + /* Allocate memory. */ + retFromCall = DataProcess_Reallocate_C_String(&tempPath, 0, valueLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPath != NULL)) + { + /* Copy the path string. */ + memcpy(tempPath, value, valueLength); + } + else + { + /* Could not allocate memory for path string. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library_Private(): Memory allocation function returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not allocate memory for library path."); + } + } + + /* Check for unknown error. */ + if (ret == COMMON_ERROR_UNKNOWN_ERROR) + { + /* Check for allocated pathToLibrary. */ + if (lib->pathToLibrary != NULL) + { + /* Deallocate the path string. */ + DataProcess_Deallocate_CString(&(lib->pathToLibrary)); + + /* Reset the path string's length. */ + lib->pathToLibraryLength = 0; + } + + /* Check and see if we need to copy the tempPath pointer and length. */ + if (tempPath != NULL) + { + /* Copy the tempPath pointer to the structure. */ + lib->pathToLibrary = tempPath; + } + if (valueLength > 0) + { + lib->pathToLibraryLength = valueLength; + } + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* Invalid lib pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + +#ifdef __cplusplus +} /* End of extern C. */ +#endif /* __cplusplus */ diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures_Private_API.h b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures_Private_API.h new file mode 100644 index 0000000..21c5a7d --- /dev/null +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Data_Structures_Private_API.h @@ -0,0 +1,248 @@ +/*! + Multiverse Engine Project 17/1/2016 Dynamic_Library_Subsystem Dynamic_Library_Subsystem_Data_Structures_Private_API.h + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_H +#error "You must include the Dynamic_Library_Subsystem.h header file. It will include all of the other needed headers." +#else +#ifndef MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_DATA_STRUCTURES_PRIVATE_API_H +#define MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_DATA_STRUCTURES_PRIVATE_API_H + +/* Check for C++ Compiler. */ +#ifdef __cplusplus +/* Define extern C. */ +extern "C" { +#endif /* __cplusplus */ + /*! + struct Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private + + This structure is an internal engine structure used to manage dynamicly + loaded libraries that were loaded at runtime via the Dynamic Library Subsystem. + + This structure should NOT be accessed directly as it is subject to change without + warning. You should use the Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library + wrapper instead. + */ + struct Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private + { + bool bIsLoaded; /* Whether or not this library is loaded. */ + bool bLastCallEncounteredAnError; /* + Whether or not this library has encountered an error in the last call. + (Should reset to false when a new call is made and no error occurs.) + */ + + void * osSpecificPointerData; /* + Pointer to an OS specific data structure. + (For example, Windows defines loaded libraries with an HMODULE pointer, + which is needed to unload the library or to search it.) + */ + char * pathToLibrary; /* Path to the dynamic library file on disk. */ + size_t pathToLibraryLength; /* Length of the pathToLibrary c-string in bytes. */ + }; + + /* Create user defined data type for Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private structure. */ + typedef struct Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private; + + /*! + * int Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private ** lib) + * + * Creates a Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private data structure in a neutral state. + * + * This function should be called when constructing a Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private object. + * As it will correctly setup said object. + * + * WARNING: The caller is responsible for calling + * Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library_Private() on the successfully created structure. + * + * WARNING: This function will NOT deallocate a preexisting structure, and will overwrite the given pointer if it is + * non-NULL if this function succeeds. Therefore if you need to keep the pointer, copy it elsewhere before calling this + * function. + * + * Returns COMMON_ERROR_SUCCESS if successful. (The created structure will be pointed to by lib.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given double pointer is NULL. + * Returns COMMON_ERROR_MEMORY_ERROR if the data structure could not be allocated. + * Otherwise returns the appropriate error code. + * + * No-alteration clause: + * - In case of error, this function will not modify it's arguments. + */ + int Common_Dynamic_Library_Subsystem_Create_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private ** lib); + + /*! + * void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private ** lib) + * + * Destroys (frees) a created Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private data structure and set's it's pointer to NULL. + * + * Pram: Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private ** lib, a double pointer to + * a successfully created Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private data structure. + * + * This function has no return. + */ + void Common_Dynamic_Library_Subsystem_Destroy_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private ** lib); + + /*! + * void Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib) + * + * Blanks the given Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private data structure's members. (Deallocating memory for them if needed.) + * + * If given an invalid pointer this function will silently fail. + * + * This function has no return. + */ + void Common_Dynamic_Library_Subsystem_Blank_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib) + * + * ACCESSOR FUNCTION. + * + * Returns the status of the bIsLoaded member variable. + * + * Returns COMMON_ERROR_TRUE if the library is loaded. + * Returns COMMON_ERROR_FALSE if the library is NOT loaded. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + int Common_Dynamic_Library_Subsystem_Get_IsLoaded_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib); + + /*! + * int Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const bool value) + * + * ACCESSOR FUNCTION. + * + * Sets the status of the bIsLoaded member variable to the given value. + * + * Returns COMMON_ERROR_SUCCESS if the status was set to the given value successfully. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + int Common_Dynamic_Library_Subsystem_Set_IsLoaded_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const bool value); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib) + * + * ACCESSOR FUNCTION. + * + * Returns the status of the bLastCallEncounteredAnError member variable. + * + * Returns COMMON_ERROR_TRUE if the last operation on this library encountered an error. + * Returns COMMON_ERROR_FALSE if the last operation on this library did NOT encounter an error. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + int Common_Dynamic_Library_Subsystem_Get_LastCallEncounteredError_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib); + + /*! + * int Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const bool value) + * + * ACCESSOR FUNCTION. + * + * Sets the status of the bLastCallEncounteredAnError member variable to the given value. + * + * Returns COMMON_ERROR_SUCCESS if the status was set to the given value successfully. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + int Common_Dynamic_Library_Subsystem_Set_LastCallEncounteredError_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const bool value); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, void ** retVar) + * + * ACCESSOR FUNCTION. + * + * Returns the pointer for the osSpecificPointerData member variable. + * + * WARNING: This function will NOT deallocate a preexisting structure, and will overwrite the given pointer if it is + * non-NULL if this function succeeds. Therefore if you need to keep the pointer, copy it elsewhere before calling this + * function. + * + * WARNING: The caller should NOT deallocate the returned pointer. + * + * Returns COMMON_ERROR_SUCCESS if the data was returned successfully. (retVar will point to the pointer value in this case.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + int Common_Dynamic_Library_Subsystem_Get_OsSpecificPointerData_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, void ** retVar); + + /*! + * int Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, void * value) + * + * ACCESSOR FUNCTION. + * + * Sets the pointer for the osSpecificPointerData member variable to the given value. + * + * Returns COMMON_ERROR_SUCCESS if the status was set to the given value successfully. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + int Common_Dynamic_Library_Subsystem_Set_OsSpecificPointerData_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, void * value); + + /*! + * int Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const char ** retVar, size_t * retVarLength) + * + * ACCESSOR FUNCTION. + * + * Returns the pointer for the pathToLibrary member variable, and the value of the pathToLibraryLength member variable. + * + * WARNING: This function will NOT deallocate a preexisting structure, and will overwrite the given pointer if it is + * non-NULL if this function succeeds. Therefore if you need to keep the pointer, copy it elsewhere before calling this + * function. + * + * WARNING: The caller should NOT deallocate the returned pointer. + * + * Returns COMMON_ERROR_SUCCESS if the data was returned successfully. (retVar will point to the pointer value in this case.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + * Otherwise returns the appropriate error code. + */ + int Common_Dynamic_Library_Subsystem_Get_PathToLibrary_Loaded_Dynamic_Library_Private(const Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const char ** retVar, size_t * retVarLength); + + /*! + * int Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const char * value, const size_t valueLength) + * + * ACCESSOR FUNCTION. + * + * Sets the pointer for the pathToLibrary member variable to the given value. + * + * Note this function serves two purposes, to allocate and copy the needed path string into the structure, + * and to deallocate a pre-existing path string in the structure. The mode that is chosen is dependant on the + * arguments given to the function. + * + * I.e. If value is non-NULL and valueLength is greater than zero, then the given path string will be copied. + * If value is NULL, and valueLength is equal to zero, then any pre-existing path string in the structure will be + * deallocated. Any other combination will generate a COMMON_ERROR_INVALID_ARGUMENT error code and the structure + * will not be modified. + * + * Returns COMMON_ERROR_SUCCESS if the status was set to the given value successfully. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers or length is NULL. + * Otherwise returns the appropriate error code. + * + * No-alteration clause: + * - In case of error, this function will not modify it's arguments. + */ + int Common_Dynamic_Library_Subsystem_Set_PathToLibrary_Loaded_Dynamic_Library_Private(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library_Private * lib, const char * value, const size_t valueLength); + +#ifdef __cplusplus +} /* End of extern C. */ +#endif /* __cplusplus */ + +#endif /* MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_DATA_STRUCTURES_PRIVATE_API_H */ +#endif /* MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_H */ + +/* End of Dynamic_Library_Subsystem_Data_Structures.h */ diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_POSIX.c b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_POSIX.c index d5fe56f..cb6f4ff 100644 --- a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_POSIX.c +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_POSIX.c @@ -17,186 +17,172 @@ Official source repository and project information can be found at https://github.com/codebase7/mengine */ - + +/* Check for linux. */ #ifdef __linux__ -#include "Dynamic_Library_Subsystem.h" -#include // dlopen, dlclose, dlsym, dlerror. +/* Internal includes */ +#include "Dynamic_Library_Subsystem_Syscall.h" +#include "../Error_Handler/Common_Error_Handler_Structures.h" +#include "../Error_Handler/Common_Error_Handler_Error_Codes.h" + +/* External includes. */ +#include /* dlopen, dlclose, dlsym, dlerror. */ + +/* Check for C++ Compiler. */ #ifdef __cplusplus -// Define extern C. +/* Define extern C. */ extern "C" { -#endif - int Common_Dynamic_Library_Subsystem_Load_Library(const char * pathToLibrary, const bool reloadLibrary, Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib) +#endif /* __cplusplus */ + int Common_Dynamic_Library_Subsystem_Load_Library_Syscall(const char * pathToLibrary, const size_t pathToLibraryLength, void ** osSpecificPointerData) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + void * callResult = NULL; /* The result of the call to dlopen(). */ + char * hostErr = NULL; /* The result returned from dlerror(). */ + + /* Check for invalid arguments. */ + if ((pathToLibrary != NULL) && (pathToLibraryLength > 0) && (osSpecificPointerData != NULL)) { - // Init vars. - int result = 0; // The result of this function. - void * callResult = NULL; // The result of the call to dlopen(). - - // Check to see if the pointer to the management structure is valid. - if (lib != NULL) - { - // Check to see if pathToLibrary is NULL. - if (pathToLibrary != NULL) - { - // Check to see if the library is loaded. - if ((!lib->bIsLoaded) || (reloadLibrary)) - { - // Check and see if the library is loaded. - if (lib->bIsLoaded) - { - // Call Unload_Library. - result = Common_Dynamic_Library_Subsystem_Unload_Library(lib); - } - - // Only continue if the library was unloaded, or if we did not need to unload the library. - if (result == 0) - { - // Set the values in lib. - lib->bIsLoaded = false; - lib->bLastCallEncounteredAnError = false; - lib->osSpecificPointerData = NULL; - lib->pathToLibrary = pathToLibrary; - - // Call dlopen(). - callResult = dlopen(lib->pathToLibrary, RTLD_LAZY | RTLD_LOCAL); - - // Check the callResult. - if (callResult == NULL) - { - // Could not load the library. - result = -1; - lib->bLastCallEncounteredAnError = true; - } - else - { - // Cast the OS specific data structure pointer to void*. - lib->osSpecificPointerData = callResult; - - // Set bIsLoaded. - lib->bIsLoaded = true; - } - } - else - { - // Encountered an error during the unload. - result = -2; - lib->bLastCallEncounteredAnError = true; - } - } - else - { - // Library is already loaded. - result = 1; - } - } - else - { - // pathToLibrary is NULL. - result = -3; - lib->bLastCallEncounteredAnError = true; - } - } - else - { - // Management structure is invalid. - result = -4; - } - - // Return result. - return result; + /* Call dlopen(). */ + callResult = dlopen(pathToLibrary, RTLD_LAZY | RTLD_LOCAL); + + /* Check the callResult. */ + if (callResult == NULL) + { + /* An error occured. + There is no clean way to check the error given here, as dlerror() returns a human-readable string. + In addition, dlopen() does not have any defined error codes in the POSIX standard. + As such we have no way of returning the specific error encountered to the caller, + so we must return COMMON_ERROR_SYSTEM_SPECIFIC. + */ + ret = COMMON_ERROR_SYSTEM_SPECIFIC; + + /* Could not load the library. */ + hostErr = dlerror(); + COMMON_LOG_VERBOSE("Common_Dynamic_Library_Subsystem_Load_Library_Syscall(): Could not load <"); + COMMON_LOG_VERBOSE(pathToLibrary); + COMMON_LOG_VERBOSE("> Host function returned: "); + COMMON_LOG_VERBOSE(hostErr); + } + else + { + /* Cast the OS specific data structure pointer to void*. */ + (*osSpecificPointerData) = (void*)callResult; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Unload_Library_Syscall(void * osData) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + char * hostErr = NULL; /* The result returned from dlerror(). */ - int Common_Dynamic_Library_Subsystem_Unload_Library(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib) + /* Check for invalid arguments. */ + if (osData != NULL) { - // Init vars. - int result = 0; // The result of this function. - - // Check to see if the pointer to the management structure is valid. - if (lib != NULL) - { - // Reset bLastCallEncounteredAnError. - lib->bLastCallEncounteredAnError = false; - - // Check for a valid handle. - if ((lib->bIsLoaded) && (lib->osSpecificPointerData != NULL)) - { - // Call dlclose(). - result = dlclose(lib->osSpecificPointerData); - - // Check result. - if (result != 0) - { - // An error occured. - result = -2; - lib->bLastCallEncounteredAnError = true; - } - else - { - // Library unloaded successfully. - lib->osSpecificPointerData = NULL; - lib->bIsLoaded = false; - } - } - else - { - // Library is not loaded. - result = -1; - lib->bLastCallEncounteredAnError = true; - } - } - else - { - // Management structure is invalid. - result = -4; - } - - // Return result. - return result; + /* Call dlclose(). */ + retFromCall = dlclose(osData); + if (retFromCall == 0) + { + /* The library was unloaded successfully. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* An error occured. + There is no clean way to check the error given here, as dlerror() returns a human-readable string. + In addition, dlclose() does not have any defined error codes in the POSIX standard. + As such we have no way of returning the specific error encountered to the caller, + so we must return COMMON_ERROR_SYSTEM_SPECIFIC. + */ + ret = COMMON_ERROR_SYSTEM_SPECIFIC; + + /* Could not load the library. */ + hostErr = dlerror(); + COMMON_LOG_VERBOSE("Common_Dynamic_Library_Subsystem_Unload_Library_Syscall(): Could not unload given library."); + COMMON_LOG_VERBOSE(" Host function returned: "); + COMMON_LOG_VERBOSE(hostErr); + } } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Return result. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_Symbol_Syscall(void * osData, const char * symbolName, const size_t symbolNameLength, void ** retSym) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + void * pSym = NULL; /* The returned symbol pointer. */ + char * hostErr = NULL; /* The result returned from dlerror(). */ - void * Common_Dynamic_Library_Subsystem_Get_Symbol(Common_Dynamic_Library_Subsystem_Loaded_Dynamic_Library *const lib, const char * symbolName) + /* Check for invalid arguments. */ + if ((osData != NULL) && (symbolName != NULL) && (retSym != NULL)) { - // Init vars. - void * result = NULL; // The result of this function. - - // Check to see if the pointer to the management structure is valid. - if (lib != NULL) - { - // Reset bLastCallEncounteredAnError. - lib->bLastCallEncounteredAnError = false; - - // Check to see if symbolName is NULL. - if (symbolName != NULL) - { - // Check to see if we have a valid handle. - if ((lib->bIsLoaded) && (lib->osSpecificPointerData != NULL)) - { - // Call dlerror to clear the error state. - dlerror(); - - // Call dlsym. - result = dlsym(lib->osSpecificPointerData, symbolName); - - // Call dlerror again to check for an error. - if (dlerror() != NULL) - { - // An error occured. - result = NULL; - lib->bLastCallEncounteredAnError = true; - } - } - } - else - { - // symbolName is NULL. - lib->bLastCallEncounteredAnError = true; - } - } - - // Return result. - return result; + /* Call dlerror to clear the error state. */ + dlerror(); + + /* Call dlsym. */ + pSym = dlsym(osData, symbolName); + + /* Call dlerror again to check for an error. */ + hostErr = dlerror(); + if (hostErr != NULL) + { + /* An error occured. + There is no clean way to check the error given here, as dlerror() returns a human-readable string. + In addition, dlsym() does not have any defined error codes in the POSIX standard. + As such we have no way of returning the specific error encountered to the caller, + so we must return COMMON_ERROR_SYSTEM_SPECIFIC. + */ + ret = COMMON_ERROR_SYSTEM_SPECIFIC; + + /* An error occured fetching the symbol. */ + COMMON_LOG_VERBOSE("Common_Dynamic_Library_Subsystem_Get_Symbol_Syscall(): Could not fetch symbol."); + COMMON_LOG_VERBOSE(" Host function returned: "); + COMMON_LOG_VERBOSE(hostErr); + } + else + { + /* Copy pSym to retSym. */ + (*retSym) = pSym; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Return result. */ + return ret; + } + #ifdef __cplusplus -} // End of extern C. -#endif -#endif +} /* End of extern C. */ +#endif /* __cplusplus */ +#endif /* __linux__ */ diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Syscall.h b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Syscall.h new file mode 100644 index 0000000..0041ef7 --- /dev/null +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Syscall.h @@ -0,0 +1,124 @@ +/*! + Multiverse Engine Project 23/1/2016 Dynamic_Library_Subsystem Dynamic_Library_Subsystem_Syscall.h + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_SYSCALL_H +#define MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_SYSCALL_H + +/* External includes. */ +#include + +/*! + int Common_Dynamic_Library_Subsystem_Load_Library_Syscall( const char * pathToLibrary, + const size_t pathToLibraryLength, + void ** osSpecificPointerData) + + Issues the system call that performs the actual loading of a library. + + NOTE: This function should NOT be called directly by anything except + Common_Dynamic_Library_Subsystem_Load_Library(). Use that function + if you want to load a given library. + + Any reference / data structure returned by the host system call that is required to + reference the loaded library in the future will be pointed to by osSpecificPointerData. + (Memory allocation may be performed here, but any allocation must be deallocated by the + Unload Library syscall function if an allocation is performed.) + + This function also translates any error code given by the host system into a + Common Namespace Error Code before returning it. + + ---General result code list--- + Note: Due to translation of host error codes, ANY Common Namespace Error Code + is possible here: + Returns COMMON_ERROR_SUCCESS if the library was loaded successfully. + + Returns COMMON_ERROR_INVALID_ARGUMENT if one of the given pointers is NULL, + or the length argument is less than or equal to zero. + + Otherwise returns the appropriate error code. + + No-alteration clause: + - In case of error, this function will not modify it's arguments. + */ +int Common_Dynamic_Library_Subsystem_Load_Library_Syscall(const char * pathToLibrary, const size_t pathToLibraryLength, void ** osSpecificPointerData); + +/*! + int Common_Dynamic_Library_Subsystem_Unload_Library_Syscall(void * osData) + + Issues the system call that performs the actual unloading of a library. + + NOTE: This function should NOT be called directly by anything except + Common_Dynamic_Library_Subsystem_Unload_Library(). Use that function + if you want to unload a given library. + + Any allocation made by the Load Library syscall must be deallocated by this function, + if an allocation was performed. + + This function also translates any error code given by the host system into a + Common Namespace Error Code before returning it. + + ---General result code list--- + Note: Due to translation of host error codes, ANY Common Namespace Error Code + is possible here: + Returns COMMON_ERROR_SUCCESS if the library was unloaded successfully. + + Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + + Otherwise returns the appropriate error code. + + No-alteration clause: + - In case of error, this function will not modify it's arguments. + */ +int Common_Dynamic_Library_Subsystem_Unload_Library_Syscall(void * osData); + +/*! + int Common_Dynamic_Library_Subsystem_Get_Symbol_Syscall(void * osData, + const char * symbolName, + const size_t symbolNameLength, + void ** retSym) + + Issues the system call that performs the actual lookup of a given symbol in a given library. + + NOTE: This function should NOT be called directly by anything except + Common_Dynamic_Library_Subsystem_Get_Symbol(). Use that function + if you want to lookup a given symbol. + + This function also translates any error code given by the host system into a + Common Namespace Error Code before returning it. + + ---General result code list--- + Note: Due to translation of host error codes, ANY Common Namespace Error Code + is possible here: + Returns COMMON_ERROR_SUCCESS if the given symbol was found successfully. + (The given symbol will be pointed to by retSym.) + + Returns COMMON_ERROR_INVALID_ARGUMENT if one of the given pointers is NULL, + or the length argument is less than or equal to zero. + + Otherwise returns the appropriate error code. + + No-alteration clause: + - In case of error, this function will not modify it's arguments. + */ +int Common_Dynamic_Library_Subsystem_Get_Symbol_Syscall(void * osData, const char * symbolName, const size_t symbolNameLength, void ** retSym); + +#endif /* MSYS_DYNAMIC_LIBRARY_SUBSYSTEM_SYSCALL_H */ + +/* End of Dynamic_Library_Subsystem_Syscall.h. */ diff --git a/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Windows.c b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Windows.c new file mode 100644 index 0000000..a7ab9ce --- /dev/null +++ b/src/Common/Src/Dynamic_Library_Subsystem/Dynamic_Library_Subsystem_Windows.c @@ -0,0 +1,150 @@ +/*! + Multiverse Engine Project 28/3/2014 Dynamic_Library_Subsystem Dynamic_Library_Subsystem_Windows.c + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Dynamic_Library_Subsystem_Syscall.h" +#include "../Error_Handler/Common_Error_Handler_Structures.h" +#include "../Error_Handler/Common_Error_Handler_Error_Codes.h" +#include "../Error_Handler/Windows_Error_Translation_Table.h" + +/* External includes. */ +#include + +/* Check for C++ Compiler. */ +#ifdef __cplusplus +/* Define extern C. */ +extern "C" { +#endif /* __cplusplus */ + + int Common_Dynamic_Library_Subsystem_Load_Library_Syscall(const char * pathToLibrary, const size_t pathToLibraryLength, void ** osSpecificPointerData) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + HMODULE callResult = NULL; /* The result of the call to LoadLibraryEx(). */ + DWORD retLLEX = 0; /* Error code from LoadLibraryEx(). */ + + /* Check for invalid arguments. */ + if ((pathToLibrary != NULL) && (pathToLibraryLength > 0) && (osSpecificPointerData != NULL)) + { + /* Call LoadLibraryEx(). */ + callResult = LoadLibraryEx(pathToLibrary, NULL, 0); + + /* Check the callResult. */ + if (callResult == NULL) + { + /* Get the last error. */ + retLLEX = GetLastError(); + ret = Common_Translate_Windows_Error_Code_To_Common_Error_Code(retLLEX); + } + else + { + /* Cast the OS specific data structure pointer to void*. */ + (*osSpecificPointerData) = ((void*)callResult); + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Unload_Library_Syscall(void * osData) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + DWORD retFL = 0; /* Error code from FreeLibrary(). */ + + /* Check for invalid arguments. */ + if (osData != NULL) + { + /* Call FreeLibrary. */ + if (FreeLibrary((HMODULE)osData)) + { + /* The library was unloaded successfully. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Get the last error. */ + retFL = GetLastError(); + ret = Common_Translate_Windows_Error_Code_To_Common_Error_Code(retFL); + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + + int Common_Dynamic_Library_Subsystem_Get_Symbol_Syscall(void * osData, const char * symbolName, const size_t symbolNameLength, void ** retSym) + { + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + DWORD retGPA = 0; /* Error code from GetProcAddress(). */ + void * pSym = NULL; /* The returned symbol pointer. */ + + /* Check for invalid arguments. */ + if ((osData != NULL) && (symbolName != NULL) && (retSym != NULL)) + { + /* Get the address. */ + pSym = (void*)GetProcAddress((HMODULE)osData, symbolName); + if (pSym == NULL) + { + /* Get the last error. */ + retGPA = GetLastError(); + ret = Common_Translate_Windows_Error_Code_To_Common_Error_Code(retGPA); + } + else + { + /* Copy pSym to retSym. */ + (*retSym) = pSym; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; + } + +#ifdef __cplusplus +} /* End of extern C. */ +#endif /* __cplusplus */ + diff --git a/src/Common/Src/Error_Handler/CMakeLists.txt b/src/Common/Src/Error_Handler/CMakeLists.txt index d3d993d..81cedbc 100644 --- a/src/Common/Src/Error_Handler/CMakeLists.txt +++ b/src/Common/Src/Error_Handler/CMakeLists.txt @@ -2,16 +2,56 @@ set(LIBRARY_OUTPUT_PATH ${L_OUTPUT_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${O_OUTPUT_DIR}) -# Create the error handler. -set (COMMON_ERROR_HANDLER_INCLUDES_BASE Common_Error_Handler.cpp) +# Check for internal mutex support as the error handler uses it. +if (BUILD_FATAL_ERROR_NOTIFY_SUPPORT) + if (BUILD_INTERNAL_MUTEX_SUPPORT) + # Enable the needed flags. + set (COMMON_ERROR_HANDLER_DEFINES MSYS_BUILD_FATAL_ERROR_SUPPORT) + + # Define the link libraries. + set(COMMON_ERROR_HANDLER_LINK_LIBS Common_Mutexes_Multiverse_Engine) + set(COMMON_ERROR_HANDLER_STATIC_LINK_LIBS Common_Mutexes_Multiverse_Engine_Static) + else(BUILD_INTERNAL_MUTEX_SUPPORT) + # No internal mutex support, so skip building the error handler's fatal error notification support. + message(SEND_ERROR "ERROR: Internal mutex support is disabled, and is REQUIRED to build the Common Error Handler's fatal error notification support. Skipping.") + endif(BUILD_INTERNAL_MUTEX_SUPPORT) +endif (BUILD_FATAL_ERROR_NOTIFY_SUPPORT) + +# Create the error handler. (Nasty hack to avoid a warning from GCC about invalid -std:gnu99 flag below....) +set (COMMON_ERROR_HANDLER_C_INCLUDES_BASE Common_Error_Handler.c + Common_Error_Handler_Log_Channel_Defs.c + Common_Error_Handler_Structures.c) +set (COMMON_ERROR_HANDLER_CXX_INCLUDES_BASE Common_Error_Handler_Structures_CPP_Bindings.cpp + Common_Error_Handler_CPP_Bindings.cpp) # Build the posix error translation table if we are running under posix. -if (${CMAKE_SYSTEM_NAME} EQUAL "Linux") - set (COMMON_ERROR_HANDLER_INCLUDES ${COMMON_ERROR_HANDLER_INCLUDES_BASE} - Posix_Error_Translation_Table.cpp) -else (${CMAKE_SYSTEM_NAME} EQUAL "Linux") - set (COMMON_ERROR_HANDLER_INCLUDES ${COMMON_ERROR_HANDLER_INCLUDES_BASE}) -endif (${CMAKE_SYSTEM_NAME} EQUAL "Linux") +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + set (COMMON_ERROR_HANDLER_C_INCLUDES ${COMMON_ERROR_HANDLER_C_INCLUDES_BASE} + Posix_Error_Translation_Table.c) + set (COMMON_ERROR_HANDLER_CXX_INCLUDES ${COMMON_ERROR_HANDLER_CXX_INCLUDES_BASE} + Posix_Error_Translation_Table_CPP_Bindings.cpp) + elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + set (COMMON_ERROR_HANDLER_C_INCLUDES ${COMMON_ERROR_HANDLER_C_INCLUDES_BASE} + Windows_Error_Translation_Table.c) + set (COMMON_ERROR_HANDLER_CXX_INCLUDES ${COMMON_ERROR_HANDLER_CXX_INCLUDES_BASE} + Windows_Error_Translation_Table_CPP_Bindings.cpp) + else (${CMAKE_SYSTEM_NAME} NOT STREQUAL "Linux") + set (COMMON_ERROR_HANDLER_C_INCLUDES ${COMMON_ERROR_HANDLER_C_INCLUDES_BASE}) + set (COMMON_ERROR_HANDLER_CXX_INCLUDES ${COMMON_ERROR_HANDLER_CXX_INCLUDES_BASE}) +endif (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + +# Check for gcc and enable gnu99 extensions if needed. +if (${CMAKE_COMPILER_IS_GNUCC}) + message (STATUS "GCC compiler detected, enabling gnu99 extensions for Common Error Handler.") + set (COMMON_ERROR_HANDLER_C_FLAGS ${COMMON_ERROR_HANDLER_C_FLAGS} -std=gnu99) +endif(${CMAKE_COMPILER_IS_GNUCC}) + +# Create the static library. +add_library(Common_Error_Handler_Multiverse_Engine_Static STATIC ${COMMON_ERROR_HANDLER_C_INCLUDES} ${COMMON_ERROR_HANDLER_CXX_INCLUDES}) +set_property(TARGET Common_Error_Handler_Multiverse_Engine_Static APPEND PROPERTY COMPILE_DEFINITIONS ${COMMON_ERROR_HANDLER_DEFINES}) +target_link_libraries(Common_Error_Handler_Multiverse_Engine_Static ${COMMON_ERROR_HANDLER_STATIC_LINK_LIBS}) -add_library(Common_Error_Handler_Multiverse_Engine SHARED ${COMMON_ERROR_HANDLER_INCLUDES}) -add_library(Common_Error_Handler_Multiverse_Engine_Static STATIC ${COMMON_ERROR_HANDLER_INCLUDES}) +# Now the shared library. +add_library(Common_Error_Handler_Multiverse_Engine SHARED ${COMMON_ERROR_HANDLER_C_INCLUDES} ${COMMON_ERROR_HANDLER_CXX_INCLUDES}) +set_property(TARGET Common_Error_Handler_Multiverse_Engine_Static APPEND PROPERTY COMPILE_DEFINITIONS ${COMMON_ERROR_HANDLER_DEFINES}) +target_link_libraries(Common_Error_Handler_Multiverse_Engine ${COMMON_ERROR_HANDLER_LINK_LIBS}) diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler.c b/src/Common/Src/Error_Handler/Common_Error_Handler.c new file mode 100644 index 0000000..a71a35f --- /dev/null +++ b/src/Common/Src/Error_Handler/Common_Error_Handler.c @@ -0,0 +1,412 @@ +/*! + Multiverse Engine Project 23/6/2014 Common Common_Error_Handler.c + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Common_Error_Handler.h" /* Main header. */ +#include "Common_Error_Handler_Internal.h" /* Private internal header that defines the structure used for error logging. */ + +/* Only include the mutex header if needed by the fatal error handler. */ +#ifdef MSYS_BUILD_FATAL_ERROR_SUPPORT +#ifdef _WIN32 +#include "..\Mutexes\MSYS_Mutexes.h" /* Internal mutex support. */ +#else +#include "../Mutexes/MSYS_Mutexes.h" /* Internal mutex support. */ +#endif /* _WIN32 */ +#endif /* MSYS_BUILD_FATAL_ERROR_SUPPORT */ + +/* Enable C linkage if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * struct CommonErrorLogData + * + * Private data struct to contain the registered error + * log callback pointer and current log level for the + * Common namespace functions. + * + * Note: We declare this here as the structure + * and it's definition is supposed to be private + * to the engine. DO NOT USE THIS STRUCTURE DIRECTLY! + * This structure is NOT a part of the public API, and + * is subject to change at anytime. + */ +struct CommonErrorLogData { + unsigned int errorLogLevel; // Current log level. + void (*loggingFunct)(const int channelID, const unsigned int logLevel, const char * errorMsg); // Pointer to current callback function. +}; + +/* + * static struct CommonErrorLogData commonErrorLoggingData + * + * The actual data structure that contains the error + * logging data for the Common namespace functions. + * + * Once again, DO NOT USE THIS STRUCTURE DIRECTLY! + * This structure is NOT a part of the public API, and + * is subject to change at anytime. + */ +static struct CommonErrorLogData commonErrorLoggingData = {ERROR_DISABLE, NULL}; + +int Common_Error_Handler_Get_API_Major_Version_Number() +{ + /* Return the API Version number. */ + return MSYS_COMMON_ERROR_HANDLER_API_MAJOR_VER; +} + +int Common_Error_Handler_Get_API_Minor_Version_Number() +{ + /* Return the API Version number. */ + return MSYS_COMMON_ERROR_HANDLER_API_MINOR_VER; +} + +int Common_Error_Handler_Get_API_Revision_Version_Number() +{ + /* Return the API Version number. */ + return MSYS_COMMON_ERROR_HANDLER_API_REVISION_VER; +} + +void Common_Set_Error_Log_Level(const unsigned int logLevel) +{ + /* Set the log level. */ + commonErrorLoggingData.errorLogLevel = logLevel; + + /* Exit function. */ + return; +} + +unsigned int Common_Get_Error_Log_Level() +{ + /* Return the current log level. */ + return commonErrorLoggingData.errorLogLevel; +} + +void Common_Register_Error_Log_Callback(void (*loggingFunction)(const int channelID, const unsigned int logLevel, const char * errorMsg)) +{ + /* Check and see if the pointer is NULL. */ + if (loggingFunction == NULL) + { + /* Set the log level to ERROR_DISABLE. */ + commonErrorLoggingData.errorLogLevel = ERROR_DISABLE; + commonErrorLoggingData.loggingFunct = NULL; + } + else + { + /* Set the pointer. */ + commonErrorLoggingData.loggingFunct = loggingFunction; + } + + /* Exit function. */ + return; +} + +/* Build the Fatal Error Handler if needed. */ +#ifdef MSYS_BUILD_FATAL_ERROR_SUPPORT +/*! + * static size_t registeredFatalErrorCallbackFunctionsSize + * + * This size_t is used to remember the allocated size of the registeredFatalErrorCallbackFunctions array. + */ +static size_t registeredFatalErrorCallbackFunctionsSize = 0; + +/* + * static Common_pErrorCallBackFunction * registeredFatalErrorCallbackFunctions + * + * This pointer to an array contains the callback functions (registered with Common::Register_Fatal_Error_Callback()) + * used to notify the engine's subsystems and the application that a fatal error has occured and the engine's + * process is about to be terminated by the host system. + */ +static Common_pErrorCallBackFunction * registeredFatalErrorCallbackFunctions = NULL; + +/*! + * static MSYS_Mutex * fatalErrorHandlerMutex + * + * This pointer is to an MSYS_Mutex object that is used to control access to the registered fatal error handler + * function pointer list and it's size value. + */ +static MSYS_Mutex * fatalErrorHandlerMutex = NULL; + +bool Common_Register_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction) +{ + /* Init vars. */ + bool ret = false; /* The result of this function. */ + size_t previousErrorListSize = registeredFatalErrorCallbackFunctionsSize; /* The size of the previous error list. */ + size_t newErrorListSize = 0; /* The size of the new error list we are creating. */ + size_t x = 0; /* Counter used in for loops. */ + Common_pErrorCallBackFunction * previousErrorList = registeredFatalErrorCallbackFunctions; /* The previous error list. */ + Common_pErrorCallBackFunction * newErrorList = NULL; /* The new error list we are creating. */ + MSYS_Mutex * retFromLockMutex = NULL; /* The result from the call to MSYS_Lock_Mutex(). */ + + /* Check for a valid mutex. */ + if (fatalErrorHandlerMutex == NULL) + { + /* Allocate the mutex. */ + fatalErrorHandlerMutex = MSYS_Create_Mutex(); + } + + /* Lock the error handler mutex. */ + retFromLockMutex = MSYS_Lock_Mutex(fatalErrorHandlerMutex); + if ((retFromLockMutex != NULL) && (retFromLockMutex == fatalErrorHandlerMutex)) + { + /* Check for valid function pointer. */ + if (fatalErrorNotifyFunction != NULL) + { + /* Check for a error function list. */ + if ((previousErrorList != NULL) && (previousErrorListSize > 0)) + { + /* Re-allocate the error list. */ + newErrorList = (Common_pErrorCallBackFunction *)malloc((sizeof(Common_pErrorCallBackFunction) * (previousErrorListSize + 1))); + if (newErrorList != NULL) + { + /* Update the size info. */ + newErrorListSize = (previousErrorListSize + 1); + + /* Copy the data. */ + for (x = 0; x < previousErrorListSize; x++) + { + newErrorList[x] = previousErrorList[x]; + } + } + } + else + { + /* Allocate the error list. */ + newErrorList = (Common_pErrorCallBackFunction *)malloc(sizeof(Common_pErrorCallBackFunction)); + if (newErrorList != NULL) + { + /* Update the size info. */ + newErrorListSize = 1; + } + } + + /* Check for a valid list. */ + if ((previousErrorListSize + 1) == newErrorListSize) + { + /* Register the function. */ + newErrorList[(previousErrorListSize)] = fatalErrorNotifyFunction; + + /* Copy the new list pointer, and size info. */ + registeredFatalErrorCallbackFunctions = newErrorList; + registeredFatalErrorCallbackFunctionsSize = newErrorListSize; + + /* Check and see if we need to deallocate the previousErrorList. */ + if ((previousErrorList != NULL) && (previousErrorList != newErrorList) && (previousErrorListSize > 0)) + { + /* Null out the old pointer list. */ + for (x = 0; x < previousErrorListSize; x++) + { + previousErrorList[x] = NULL; + } + + /* Deallocate the old array. */ + free(previousErrorList); + previousErrorList = NULL; + previousErrorListSize = 0; + } + + /* We are done. */ + ret = true; + } + } + + /* Release the error handler mutex. */ + MSYS_Unlock_Mutex(fatalErrorHandlerMutex); + } + + /* Return the result. */ + return ret; +} + +bool Common_Unregister_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction) +{ + /* Init vars. */ + bool ret = false; /* The result of this function. */ + size_t previousErrorListSize = registeredFatalErrorCallbackFunctionsSize; /* The size of the previous error list. */ + size_t newErrorListSize = 0; /* The size of the new error list we are creating. */ + size_t x = 0; /* Counter used in top level for loops. */ + size_t y = 0; /* Counter used in sub level 1 for loops. */ + Common_pErrorCallBackFunction * previousErrorList = registeredFatalErrorCallbackFunctions; /* The previous error list. */ + Common_pErrorCallBackFunction * newErrorList = NULL; /* The new error list we are creating. */ + MSYS_Mutex * retFromLockMutex = NULL; /* The result from the call to MSYS_Lock_Mutex(). */ + + /* Lock the error handler mutex. */ + retFromLockMutex = MSYS_Lock_Mutex(fatalErrorHandlerMutex); + if ((retFromLockMutex != NULL) && (retFromLockMutex == fatalErrorHandlerMutex)) + { + /* Check for valid function pointer. */ + if (fatalErrorNotifyFunction != NULL) + { + /* Check for a error function list. */ + if ((previousErrorList != NULL) && (previousErrorListSize > 0)) + { + /* Check the existing list for that function. */ + for (x = 0; ((newErrorList == NULL) && (x < previousErrorListSize)); x++) + { + /* Check for the correct function, to see if the function was registered previously. */ + if (previousErrorList[x] == fatalErrorNotifyFunction) + { + /* Found the function, so re-allocate the error list so we can remove it. */ + newErrorList = (Common_pErrorCallBackFunction *)malloc((sizeof(Common_pErrorCallBackFunction) * (previousErrorListSize - 1))); + if (newErrorList != NULL) + { + /* Update the size info. */ + newErrorListSize = (previousErrorListSize - 1); + } + } + } + + /* Only continue if the list was reallocated due to us finding the function to remove. */ + if ((newErrorList != NULL) && (newErrorListSize == (previousErrorListSize - 1))) + { + /* Copy the data. */ + for (x = 0, y = 0; ((x < previousErrorListSize) && (y < newErrorListSize)); x++) + { + /* Make sure we don't copy the pointer we are removing. */ + if (previousErrorList[x] != fatalErrorNotifyFunction) + { + /* Copy the pointer, and increment y. (Yes, y can be different than x, because it will not contain every pointer.) */ + newErrorList[y] = previousErrorList[x]; + y++; + } + } + + /* Now that the data is copied, copy the pointers. */ + registeredFatalErrorCallbackFunctions = newErrorList; + registeredFatalErrorCallbackFunctionsSize = newErrorListSize; + + /* Deallocate the old list. */ + if ((previousErrorList != NULL) && (previousErrorList != newErrorList) && (previousErrorListSize > 0)) + { + /* Null out the old pointer list. */ + for (x = 0; x < previousErrorListSize; x++) + { + previousErrorList[x] = NULL; + } + + free(previousErrorList); + previousErrorList = NULL; + previousErrorListSize = 0; + } + + /* Done. */ + ret = true; + } + } + } + + /* Release the error handler mutex. */ + MSYS_Unlock_Mutex(fatalErrorHandlerMutex); + } + + /* Return the result. */ + return ret; +} + +void Common_Fatal_Error_Notify() +{ + /* Init vars. */ + MSYS_Mutex * retFromLockMutex = NULL; /* The result from the call to MSYS_Lock_Mutex(). */ + size_t x = 0; /* Counter used in loops. */ + + /* Lock the error handler mutex. */ + retFromLockMutex = MSYS_Lock_Mutex(fatalErrorHandlerMutex); + if ((retFromLockMutex != NULL) && (retFromLockMutex == fatalErrorHandlerMutex)) + { + /* Check for registered fatal error callbacks. */ + if (registeredFatalErrorCallbackFunctionsSize > 0) + { + /* Begin vector iteration loop. */ + for (x = 0; (x < registeredFatalErrorCallbackFunctionsSize); x++) + { + /* Trigger each function. */ + if (registeredFatalErrorCallbackFunctions[x] != NULL) + { + registeredFatalErrorCallbackFunctions[x](); + } + } + } + + /* Release the error handler mutex. */ + MSYS_Unlock_Mutex(fatalErrorHandlerMutex); + } + + /* Exit function. */ + return; +} + +#endif /* MSYS_BUILD_FATAL_ERROR_SUPPORT */ + +void COMMON_LOG_ERROR(const int channelID, const unsigned int loggingLevel, const char * errorMsg) +{ + /* + * Only do something if the log is enabled, + * the error is at or below our current log level, + * and the logging callback function is defined. + * + * Note: The lower the log level, the higher severity of the error. + */ + if ((commonErrorLoggingData.errorLogLevel != ERROR_DISABLE) && + (commonErrorLoggingData.loggingFunct != NULL) && + (loggingLevel <= commonErrorLoggingData.errorLogLevel) && + (Common_Error_Get_Logging_Channel_Status_By_ID_Number(channelID) == COMMON_ERROR_TRUE)) + { + /* Call the callback. (Hope it returns....) */ + commonErrorLoggingData.loggingFunct(channelID, loggingLevel, errorMsg); + } + + /* Exit function. */ + return; +} + +void COMMON_LOG_CRITICAL(const int channelID, const char * errorMsg) +{ + COMMON_LOG_ERROR(channelID, ERROR_CRITICAL, errorMsg); + return; +} + +void COMMON_LOG_WARNING(const int channelID, const char * errorMsg) +{ + COMMON_LOG_ERROR(channelID, ERROR_WARNING, errorMsg); + return; +} + +void COMMON_LOG_INFO(const int channelID, const char * errorMsg) +{ + COMMON_LOG_ERROR(channelID, ERROR_INFO, errorMsg); + return; +} + +void COMMON_LOG_DEBUG(const int channelID, const char * errorMsg) +{ + COMMON_LOG_ERROR(channelID, ERROR_DEBUG, errorMsg); + return; +} + +void COMMON_LOG_VERBOSE(const int channelID, const char * errorMsg) +{ + COMMON_LOG_ERROR(channelID, ERROR_VERBOSE, errorMsg); + return; +} + +/* End C Linkage if needed. */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler.cpp b/src/Common/Src/Error_Handler/Common_Error_Handler.cpp deleted file mode 100644 index 1ea3719..0000000 --- a/src/Common/Src/Error_Handler/Common_Error_Handler.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/*! - Multiverse Engine Project 23/6/2014 Common Common_Error_Handler.cpp - - Copyright (C) 2014 Multiverse Engine Project - - This program is free software; - you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with this program; - if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - Official source repository and project information can be found at - https://github.com/codebase7/mengine -*/ - -#include "Common_Error_Handler.h" // Main header. -#include "Common_Error_Handler_Internal.h" // Private internal header that defines the structure used for error logging. - -const unsigned int Common::Get_Error_Table_Size() -{ - return (sizeof(Common::commonErrorTable) / sizeof(Common_Error_Object)); -} - -const char * Common::Get_Error_Message(const int & errorCode) -{ - // Init vars. - const char * result = NULL; // Result of this function. - const size_t errorTableSize = Common::Get_Error_Table_Size(); // Size of the Common error lookup table. - - // Begin lookup loop. - for (size_t x = 0; ((x < errorTableSize) && (result == NULL)); x++) - { - // Check for the correct error code. - if (Common::commonErrorTable[x].errorCode == errorCode) - { - // Found the correct error code. - result = Common::commonErrorTable[x].error; - } - } - - // If we still can't find the error message to return, use COMMON_UNKNOWN_ERROR - if (result == NULL) - { - result = Common::UNKNOWN_ERROR_MSG; - } - - // Return the result. - return result; -} - -void Common::Set_Error_Log_Level(const unsigned int & logLevel) -{ - // Set the log level. - commonErrorLoggingData.errorLogLevel = logLevel; - - // Exit function. - return; -} - -unsigned int Common::Get_Error_Log_Level() -{ - // Return the current log level. - return commonErrorLoggingData.errorLogLevel; -} - -void Common::Register_Error_Log_Callback(void (*loggingFunction)(const unsigned int logLevel, const char * errorMsg)) -{ - // Check and see if the pointer is NULL. - if (loggingFunction == NULL) - { - // Set the log level to ERROR_DISABLE. - commonErrorLoggingData.errorLogLevel = ERROR_DISABLE; - } - - // Set the pointer. - commonErrorLoggingData.loggingFunct = loggingFunction; - - // Exit function. - return; -} - -void COMMON_LOG_ERROR(const unsigned int loggingLevel, const char * errorMsg) -{ - /* - * Only do something if the log is enabled, - * the error is at or below our current log level, - * and the logging callback function is defined. - * - * Note: The lower the log level, the higher severity of the error. - */ - if ((commonErrorLoggingData.errorLogLevel != ERROR_DISABLE) && - (commonErrorLoggingData.loggingFunct != NULL) && - (loggingLevel <= commonErrorLoggingData.errorLogLevel)) - { - // Call the callback. (Hope it returns....) - commonErrorLoggingData.loggingFunct(loggingLevel, errorMsg); - } - - // Exit function. - return; -} - -void COMMON_LOG_CRITICAL(const char * errorMsg) -{ - COMMON_LOG_ERROR(ERROR_CRITICAL, errorMsg); - return; -} - -void COMMON_LOG_WARNING(const char * errorMsg) -{ - COMMON_LOG_ERROR(ERROR_WARNING, errorMsg); - return; -} - -void COMMON_LOG_INFO(const char * errorMsg) -{ - COMMON_LOG_ERROR(ERROR_INFO, errorMsg); - return; -} - -void COMMON_LOG_DEBUG(const char * errorMsg) -{ - COMMON_LOG_ERROR(ERROR_DEBUG, errorMsg); - return; -} - -void COMMON_LOG_VERBOSE(const char * errorMsg) -{ - COMMON_LOG_ERROR(ERROR_VERBOSE, errorMsg); - return; -} diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler.h b/src/Common/Src/Error_Handler/Common_Error_Handler.h index 3b348b2..fd130e8 100644 --- a/src/Common/Src/Error_Handler/Common_Error_Handler.h +++ b/src/Common/Src/Error_Handler/Common_Error_Handler.h @@ -18,50 +18,201 @@ https://github.com/codebase7/mengine */ -// Include guard. +/* Include guard. */ #ifndef COMMON_ERROR_HANDLER_H #define COMMON_ERROR_HANDLER_H /* Pull in DLL_PORT.h */ #include "../../../DLL_PORT.h" /* Defines MSYS_DLL_EXPORT, and MSYS_DLL_IMPORT_TEMPLATE. */ -// External includes. -#include // Defines NULL. +/* External includes. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ -// Project includes. -#ifdef _WIN32 // Needed for different path seperator in Windows. -#include "..\..\..\Core\Src\Panic_Error_Levels.h" // Defines the log levels. + #include /* Defines NULL. */ + +#ifdef __cplusplus +} /* End of extern C. */ +#endif /* __cplusplus */ + +#ifndef __cplusplus +#if _MSC_FULL_VER && _MSC_FULL_VER < 180031101 /* Visual C versions less than 2013 are special. (They lack support for C99's bool type.) */ +#include "..\..\..\stdbool.h" /* Defines bool data type. (For C compilers.) */ #else -#include "../../../Core/Src/Panic_Error_Levels.h" // Defines the log levels. -#include "Posix_Error_Translation_Table.h" // Defines the POSIX errno to Common namespace error translation table and functions. -#endif // _WIN32 +#include +#endif /* _MSC_FULL_VER && _MSC_FULL_VER < 180031101 */ +#endif /* __cplusplus */ -#include "Common_Error_Handler_Structures.h" // Defines the error codes, error lookup table error lookup table version number, and Common::commonLastErrorCode. +/* Project includes. */ +#ifdef _WIN32 /* Needed for different path seperator in Windows. */ +#include "..\..\..\Core\Src\Panic_Error_Levels.h" /* Defines the log levels. */ +#else +#include "../../../Core/Src/Panic_Error_Levels.h" /* Defines the log levels. */ +#include "Posix_Error_Translation_Table.h" /* Defines the POSIX errno to Common namespace error translation table and functions. */ +#endif /* _WIN32 */ -// Define namespaces. -namespace Common -{ - /*! - * const unsigned int Common::Get_Error_Table_Size() - * - * Returns the size of the common error table. - */ - MSYS_DLL_EXPORT const unsigned int Get_Error_Table_Size(); +#include "Common_Error_Handler_Log_Channel_Defs.h" /* Defines the error log channels and their related functions. */ +#include "Common_Error_Handler_Error_Codes.h" /* Defines error codes. */ +#include "Common_Error_Handler_Structures.h" /* Defines the error codes, error lookup table error lookup table version number, and Common::commonLastErrorCode. */ - /*! - * const char * Common::Get_Error_Message(const int & errorCode) - * - * This function takes the given error code and returns a pointer to a human - * readable string describing the meaning of the given error code. - * - * Returns a valid pointer if the given error code is in the common error table. - * Returns the message for Common::COMMON_UNKNOWN_ERROR otherwise. - */ - MSYS_DLL_EXPORT const char * Get_Error_Message(const int & errorCode); +/* Define the supported API version numbers. */ +#define MSYS_COMMON_ERROR_HANDLER_API_MAJOR_VER 1 +#define MSYS_COMMON_ERROR_HANDLER_API_MINOR_VER 0 +#define MSYS_COMMON_ERROR_HANDLER_API_REVISION_VER 0 + +/* Enable C linkage if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Define the C bindings for the error handler. */ + +/*! + int Common_Error_Handler_Get_API_Major_Version_Number() + + Returns the API major version number for the Common Error Handler. + */ +MSYS_DLL_EXPORT int Common_Error_Handler_Get_API_Major_Version_Number(); + +/*! + int Common_Error_Handler_Get_API_Minor_Version_Number() + + Returns the API minor version number for the Common Error Handler. + */ +MSYS_DLL_EXPORT int Common_Error_Handler_Get_API_Minor_Version_Number(); + +/*! + int Common_Error_Handler_Get_API_Revision_Version_Number() + + Returns the API revision version number for the Common Error Handler. + */ +MSYS_DLL_EXPORT int Common_Error_Handler_Get_API_Revision_Version_Number(); + +/*! + * void Common_Set_Error_Log_Level(const unsigned int & logLevel) + * + * Sets the error logging level for the Common namespace functions. + * + * By default it sets the error logging level to ERROR_DISABLE. + * (Disables all logging. See Core/Src/Panic.h for a list of + * valid logging levels.) + */ +MSYS_DLL_EXPORT void Common_Set_Error_Log_Level(const unsigned int logLevel); + +/*! + * unsigned int Common_Get_Error_Log_Level() + * + * Returns the current error logging level for the Common namespace functions. + * + * See Core/Src/Panic.h for a list of valid logging levels. + */ +MSYS_DLL_EXPORT unsigned int Common_Get_Error_Log_Level(); +/*! + * void Common_Register_Error_Log_Callback(void (*loggingFunction)(const int channelID, const unsigned int logLevel, const char * errorMsg)) + * + * WARNING: The callback function MUST return control back to + * the caller, as the caller will be blocked until the callback + * function returns. + * + * Sets the logging function to call when an error is generated + * by a Common namespace function. + * + * For example, this function can be used to send the generated + * errors to the console in a multi-threaded enviroment. + * + * Passing a NULL pointer to this function (the default) will + * disable calling another function when an error is generated. + * In addition the logging level will be reset to ERROR_DISABLE. + */ +MSYS_DLL_EXPORT void Common_Register_Error_Log_Callback(void (*loggingFunction)(const int channelID, const unsigned int logLevel, const char * errorMsg)); +#ifdef MSYS_BUILD_FATAL_ERROR_SUPPORT + +/*! + * typedef void(*Common_pErrorCallBackFunction)(void) + * + * Defines the function pointer type for use as arguments by the Common fatal error handler functions. + */ +typedef void(*Common_pErrorCallBackFunction)(void); + +/*! + * bool Common_Register_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction) + * + * WARNING: The callback function MUST return control back to + * the caller, as the caller will be blocked until the callback + * function returns. This means any other registered callbacks + * will not be triggered, which may lead to data loss. The only exception + * to this is if the host system will kill the engine's process after a set + * amount of time. In this one specific instance, nothing can be done to + * prevent the host system from killing the engine. As such the function + * registered with this call should attempt to clean up and return as + * fast as possible. + * + * Registers a callback function for notification of an engine subsystem + * triggering the host system to kill the engine's process. + * + * Note: The registered callback is not guaranteed to be called at all prior to + * the engine process being terminated. (The host system reserves the right to + * kill the engine without warning it of the impending termination.) + * As such this should be considered an informal notification and not something + * to be relied on if data preservation / security or proper cleanup is required + * prior to engine shutdown. + * + * Returns true if the regisration completed successfully. + * Returns false otherwise. + */ +MSYS_DLL_EXPORT bool Common_Register_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction); + +/*! + * bool Common_Unregister_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction) + * + * Unregisters the given fatal error callback function from the list of fatal error + * callback functions to be triggered in the event of a fatal error being generated. + * + * (I.e. If unregistered, a given callback function will not be called if a fatal + * error occurs.) + * + * The callback function pointer given to this function must match a function pointer + * given to Common_Register_Fatal_Error_Callback() (Or Common::Register_Fatal_Error_Callback()) previously, + * otherwise this function will fail. + * + * Returns true if the unregisration completed successfully. + * Returns false otherwise. + */ +MSYS_DLL_EXPORT bool Common_Unregister_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction); + +/*! + * void Common_Fatal_Error_Notify() + * + * This function triggers the registered fatal error callback functions + * registered with Common_Register_Fatal_Error_Callback() (Or Common::Register_Fatal_Error_Callback()), + * in an attempt to notify all need to know sections of the engine, and application, that the engine is + * about to be terminated. + * + * When this function returns to it's caller, it is expected that the caller + * will terminate the engine's process if it does not happen automaticly. + * + * This function does not return any data to it's caller. + */ +MSYS_DLL_EXPORT void Common_Fatal_Error_Notify(); +#endif /* MSYS_BUILD_FATAL_ERROR_SUPPORT */ + +/* End C Linkage if needed. */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/* Define C++ Bindings. */ +#ifdef __cplusplus +/* Define namespaces. */ +namespace Common +{ /*! * void Common::Set_Error_Log_Level(const unsigned int & logLevel) - * + * + * (C++ Binding) + * * Sets the error logging level for the Common namespace functions. * * By default it sets the error logging level to ERROR_DISABLE. @@ -72,7 +223,9 @@ namespace Common /*! * unsigned int Common::Get_Error_Log_Level() - * + * + * (C++ Binding) + * * Returns the current error logging level for the Common namespace functions. * * See Core/Src/Panic.h for a list of valid logging levels. @@ -80,8 +233,10 @@ namespace Common MSYS_DLL_EXPORT unsigned int Get_Error_Log_Level(); /*! - * void Common::Register_Error_Log_Callback(void (*loggingFunction)(const unsigned int logLevel, const char * errorMsg)) - * + * void Common::Register_Error_Log_Callback(void (*loggingFunction)(const int channelID, const unsigned int logLevel, const char * errorMsg)) + * + * (C++ Binding) + * * WARNING: The callback function MUST return control back to * the caller, as the caller will be blocked until the callback * function returns. @@ -96,9 +251,79 @@ namespace Common * disable calling another function when an error is generated. * In addition the logging level will be reset to ERROR_DISABLE. */ - MSYS_DLL_EXPORT void Register_Error_Log_Callback(void (*loggingFunction)(const unsigned int logLevel, const char * errorMsg) = NULL); + MSYS_DLL_EXPORT void Register_Error_Log_Callback(void (*loggingFunction)(const int channelID, const unsigned int logLevel, const char * errorMsg) = NULL); + +#ifdef MSYS_BUILD_FATAL_ERROR_SUPPORT + /*! + * bool Common::Register_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction) + * + * (C++ Binding) + * + * WARNING: The callback function MUST return control back to + * the caller, as the caller will be blocked until the callback + * function returns. This means any other registered callbacks + * will not be triggered, which may lead to data loss. The only exception + * to this is if the host system will kill the engine's process after a set + * amount of time. In this one specific instance, nothing can be done to + * prevent the host system from killing the engine. As such the function + * registered with this call should attempt to clean up and return as + * fast as possible. + * + * Registers a callback function for notification of an engine subsystem + * triggering the host system to kill the engine's process. + * + * Note: The registered callback is not guaranteed to be called at all prior to + * the engine process being terminated. (The host system reserves the right to + * kill the engine without warning it of the impending termination.) + * As such this should be considered an informal notification and not something + * to be relied on if data preservation / security or proper cleanup is required + * prior to engine shutdown. + * + * Returns true if the regisration completed successfully. + * Returns false otherwise. + */ + MSYS_DLL_EXPORT bool Register_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction); + + /*! + * bool Common::Unregister_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction) + * + * (C++ Binding) + * + * Unregisters the given fatal error callback function from the list of fatal error + * callback functions to be triggered in the event of a fatal error being generated. + * + * (I.e. If unregistered, a given callback function will not be called if a fatal + * error occurs.) + * + * The callback function pointer given to this function must match a function pointer + * given to Common_Register_Fatal_Error_Callback() (Or Common::Register_Fatal_Error_Callback()) previously, + * otherwise this function will fail. + * + * Returns true if the unregisration completed successfully. + * Returns false otherwise. + */ + MSYS_DLL_EXPORT bool Unregister_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction); + + /*! + * void Common::Fatal_Error_Notify() + * + * (C++ Binding) + * + * This function triggers the registered fatal error callback functions + * registered with Or Common_Register_Fatal_Error_Callback() (Common::Register_Fatal_Error_Callback()), + * in an attempt to notify all need to know sections of the engine, and application, that the engine is + * about to be terminated. + * + * When this function returns to it's caller, it is expected that the caller + * will terminate the engine's process if it does not happen automaticly. + * + * This function does not return any data to it's caller. + */ + MSYS_DLL_EXPORT void Fatal_Error_Notify(); +#endif /* MSYS_BUILD_FATAL_ERROR_SUPPORT */ }; +#endif /* __cplusplus */ -#endif // COMMON_ERROR_HANDLER_H +#endif /* COMMON_ERROR_HANDLER_H */ -// End of Common_Error_Handler.h +/* End of Common_Error_Handler.h */ diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler_CPP_Bindings.cpp b/src/Common/Src/Error_Handler/Common_Error_Handler_CPP_Bindings.cpp new file mode 100644 index 0000000..58a6bea --- /dev/null +++ b/src/Common/Src/Error_Handler/Common_Error_Handler_CPP_Bindings.cpp @@ -0,0 +1,121 @@ +/*! + Multiverse Engine Project 13/5/2015 Common Common_Error_Handler_CPP_Bindings.cpp + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Define C++ Bindings. +#ifdef __cplusplus + +// Internal includes. +#include "Common_Error_Handler.h" // Main header. +#include "Common_Error_Handler_Internal.h" // Private internal header that defines the structure used for error logging. + +// External includes. +#include + +/* Define the MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID macro. */ +#define MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID MSYS_ERROR_LOG_CHANNEL_ERROR_HANDLER + +void Common::Set_Error_Log_Level(const unsigned int & logLevel) +{ + Common_Set_Error_Log_Level(logLevel); +} + +unsigned int Common::Get_Error_Log_Level() +{ + return Common_Get_Error_Log_Level(); +} + +void Common::Register_Error_Log_Callback(void (*loggingFunction)(const int channelID, const unsigned int logLevel, const char * errorMsg)) +{ + Common_Register_Error_Log_Callback(loggingFunction); +} + +// Build the Fatal Error Handler if needed. +#ifdef MSYS_BUILD_FATAL_ERROR_SUPPORT + +bool Common::Register_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction) +{ + // Init vars. + bool ret = false; // The result of this function. + + // Begin try block. + try { + // Call real function. + ret = Common_Register_Fatal_Error_Callback(fatalErrorNotifyFunction); + } + catch (...) + { + /* + * Great probably a memory allocation error, if so, we don't do anything as the original list is not modifyed + * unless the operations succeed. + * + * If not, well we just destroyed the list of functions we needed to call when a fatal error happened, + * so maybe we should call terminate() here? + */ + ret = false; + } + + // Return the result. + return ret; +} + +bool Common::Unregister_Fatal_Error_Callback(const Common_pErrorCallBackFunction fatalErrorNotifyFunction) +{ + // Init vars. + bool ret = false; // The result of this function. + + // Begin try block. + try { + // Call real function. + ret = Common_Unregister_Fatal_Error_Callback(fatalErrorNotifyFunction); + } + catch (...) + { + /* + * Great probably a memory allocation error, if so, we don't do anything as the original list is not modifyed + * unless the operations succeed. + * + * If not, well we just destroyed the list of functions we needed to call when a fatal error happened, + * so maybe we should call terminate() here? + */ + ret = false; + } + + // Return the result. + return ret; +} + +void Common::Fatal_Error_Notify() +{ + // Begin try block. + try { + // Call real function. + Common_Fatal_Error_Notify(); + } + catch (...) + { + // Well, not much to do here, we are terminating anyway. + COMMON_LOG_CRITICAL(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "Common::Fatal_Error_Notify(): Exception occured while notifying engine subsystems / application that a fatal error occured."); + } +} + +#endif // MSYS_BUILD_FATAL_ERROR_SUPPORT + +#endif // __cplusplus + diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler_Error_Codes.h b/src/Common/Src/Error_Handler/Common_Error_Handler_Error_Codes.h new file mode 100644 index 0000000..8ff80ac --- /dev/null +++ b/src/Common/Src/Error_Handler/Common_Error_Handler_Error_Codes.h @@ -0,0 +1,107 @@ +/*! + Multiverse Engine Project 05/7/2015 Common Common_Error_Handler_Error_Codes.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef COMMON_ERROR_HANDLER_ERROR_CODES_H +#define COMMON_ERROR_HANDLER_ERROR_CODES_H + +/* Common namespace error code definitions. (Human-readable error message translation table is located in Common_Error_Handler_Structures.c) */ + enum { + /* Generic error codes. */ + COMMON_ERROR_SYSTEM_SPECIFIC = 99, + COMMON_ERROR_SUCCESS = 0, + COMMON_ERROR_UNKNOWN_ERROR = -1, + COMMON_ERROR_INVALID_ARGUMENT = -2, + COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED = -3, + COMMON_ERROR_ACCESS_DENIED = -4, + COMMON_ERROR_EXCEPTION_THROWN = -5, + COMMON_ERROR_INTERNAL_ERROR = -6, + COMMON_ERROR_IO_ERROR = -7, + COMMON_ERROR_RANGE_ERROR = -8, + COMMON_ERROR_MEMORY_ERROR = -9, + COMMON_ERROR_INVALID_LIBRARY_ID = -10, /* Formerly THREAD_UTILS_INVALID_LIBRARY_ID. */ + COMMON_ERROR_PEBKAC_INVALID_OPERATION_ORDER = -11, + COMMON_ERROR_CANNOT_GET_SYSTEM_TIME = -12, + COMMON_ERROR_SUBSYSTEM_OBJECT_NOT_INITED = -13, + COMMON_ERROR_SUBSYSTEM_OBJECT_ALREADY_INITED = -14, + COMMON_ERROR_END_OF_DATA = -15, + COMMON_ERROR_COMPARISON_PASSED = -16, + COMMON_ERROR_COMPARISON_FAILED = -17, + COMMON_ERROR_RACE_CONDITION = -18, + COMMON_ERROR_HOST_NOT_SUPPORTED = -19, + COMMON_ERROR_TRUE = -20, + COMMON_ERROR_FALSE = -21, + COMMON_ERROR_ARGUMENT_CONVERSION_FAILURE = -22, + COMMON_ERROR_FALSE_SUCCESS = -23, + COMMON_ERROR_PEBKAC_OPERATION_ALREADY_COMPLETED = -24, + COMMON_ERROR_NO_DATA = -25, + COMMON_ERROR_DATA_CORRUPTION = -26, + COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED = -27, + COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL = -28, + + /* Threading Subsystem error codes. */ + THREAD_UTILS_ERROR_EXCEPTION_THROWN = -31, + THREAD_UTILS_ERROR_PLUGIN_LOAD_FAILURE = -32, + THREAD_UTILS_ERROR_THREAD_COULD_NOT_START = -33, + THREAD_UTILS_ERROR_THREAD_COULD_NOT_DETACH = -34, + THREAD_UTILS_ERROR_THREAD_COULD_NOT_JOIN = -35, + THREAD_UTILS_ERROR_MUTEX_ALREADY_LOCKED = -36, + THREAD_UTILS_ERROR_CONDITION_CANNOT_LOCK_MUTEX = -37, + THREAD_UTILS_ERROR_CONDITION_WAIT_TIMEOUT_REACHED = -38, + + /* FileUtills error codes. */ + FILEUTILLS_ERROR_EXISTANT = -60, + FILEUTILLS_ERROR_NON_EXISTANT = -61, + FILEUTILLS_ERROR_READ_ONLY = -62, + FILEUTILLS_ERROR_PATH_LENGTH_INVALID = -63, + FILEUTILLS_ERROR_PATH_FILE_AS_DIRECTORY = -64, + FILEUTILLS_ERROR_PATH_IS_A_FILE = -65, + FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY = -66, + FILEUTILLS_ERROR_PATH_IS_A_SYMLINK = -67, + FILEUTILLS_ERROR_PATH_IS_ABSOLUTE = -68, + FILEUTILLS_ERROR_PATH_IS_RELATIVE = -69, + FILEUTILLS_ERROR_FILESYSTEM_FULL = -70, + FILEUTILLS_ERROR_FILESYSTEM_QUOTA_REACHED = -71, + FILEUTILLS_ERROR_EMPTY_DIRECTORY = -72, + FILEUTILLS_ERROR_NON_EMPTY_DIRECTORY = -73, + FILEUTILLS_ERROR_SYMLINK_CHAIN_TOO_DEEP = -74, + + /* UI Subsystem. */ + UI_SUBSYSTEM_ERROR_EXCEPTION_THROWN = -90, + + /* Dynamic Library Subsystem. */ + DYNLIB_ERROR_INVALID_LIBRARY = -150, + DYNLIB_ERROR_LIBRARY_ALREADY_LOADED = -151, + DYNLIB_ERROR_LIBRARY_NOT_LOADED = -152, + DYNLIB_ERROR_HOST_VS_LIBRARY_MISMATCH = -153, + DYNLIB_ERROR_SYMBOL_NOT_FOUND = -154, + + /* Rendering Subsystem error codes. */ + RENDERER_ERROR_UNABLE_TO_ALLOC_OI_BUF = -175, /* Overlay image buffer. */ + RENDERER_ERROR_UNABLE_TO_ALLOC_TD_BUF = -176, /* Transparency data buffer. */ + RENDERER_ERROR_MEM_BUF_ALLOC_EXCEPTION = -177, + RENDERER_ERROR_DUPE_OVERLAY_EXCEPTION = -178, + RENDERER_ERROR_INVAL_OVERLAY_SELF_OVERWRITE = -179, + RENDERER_ERROR_TRANSPARENCY_DISABLED = -180, + }; + +#endif /* COMMON_ERROR_HANDLER_ERROR_CODES_H */ + +/* End of Common_Error_Handler_Error_Codes.h. */ diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler_Internal.h b/src/Common/Src/Error_Handler/Common_Error_Handler_Internal.h index e938340..9a9d4fd 100644 --- a/src/Common/Src/Error_Handler/Common_Error_Handler_Internal.h +++ b/src/Common/Src/Error_Handler/Common_Error_Handler_Internal.h @@ -18,54 +18,29 @@ https://github.com/codebase7/mengine */ -// Include guard. +/* Include guard. */ #ifndef COMMON_ERROR_HANDLER_INTERNAL_H #define COMMON_ERROR_HANDLER_INTERNAL_H -// Project includes. -#ifdef __win32 // Needed for different path seperator in Windows. -#include "..\..\..\Core\Src\Panic_Error_Levels.h" // Defines the log levels. +/* Project includes. */ +#ifdef _WIN32 /* Needed for different path seperator in Windows. */ +#include "..\..\..\Core\Src\Panic_Error_Levels.h" /* Defines the log levels. */ #else -#include "../../../Core/Src/Panic_Error_Levels.h" // Defines the log levels. -#endif // __win32 +#include "../../../Core/Src/Panic_Error_Levels.h" /* Defines the log levels. */ +#endif /* _WIN32 */ -// Check to see if we are being included directly. -//#ifndef COMMON_ERROR_HANDLER_H -//#error "This header is for internal engine use only. It should not be linked against, as it is NOT a part of the public API." -//#endif // COMMON_ERROR_HANDLER_H +/* Check to see if we are being included directly. */ +/*#ifndef COMMON_ERROR_HANDLER_H */ +/*#error "This header is for internal engine use only. It should not be linked against, as it is NOT a part of the public API." */ +/*#endif COMMON_ERROR_HANDLER_H */ -/* - * struct CommonErrorLogData - * - * Private data struct to contain the registered error - * log callback pointer and current log level for the - * Common namespace functions. - * - * Note: We declare this here as the structure - * and it's definition is supposed to be private - * to the engine. DO NOT USE THIS STRUCTURE DIRECTLY! - * This structure is NOT a part of the public API, and - * is subject to change at anytime. - */ -struct CommonErrorLogData { - unsigned int errorLogLevel; // Current log level. - void (*loggingFunct)(const unsigned int logLevel, const char * errorMsg); // Pointer to current callback function. -}; +/* Enable C linkage if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ /* - * static struct CommonErrorLogData commonErrorLoggingData - * - * The actual data structure that contains the error - * logging data for the Common namespace functions. - * - * Once again, DO NOT USE THIS STRUCTURE DIRECTLY! - * This structure is NOT a part of the public API, and - * is subject to change at anytime. - */ -static struct CommonErrorLogData commonErrorLoggingData = {ERROR_DISABLE, NULL}; - -/* - * void COMMON_LOG_ERROR(const unsigned int loggingLevel, const char * errorMsg) + * void COMMON_LOG_ERROR(const int channelID, const unsigned int loggingLevel, const char * errorMsg) * * This is the actual function that calls the current * error logging callback function for the Common @@ -75,10 +50,10 @@ static struct CommonErrorLogData commonErrorLoggingData = {ERROR_DISABLE, NULL}; * This function is NOT a part of the public API, and * is subject to change at anytime. */ -MSYS_DLL_EXPORT void COMMON_LOG_ERROR(const unsigned int loggingLevel, const char * errorMsg); +MSYS_DLL_EXPORT void COMMON_LOG_ERROR(const int channelID, const unsigned int loggingLevel, const char * errorMsg); /* - * COMMON_LOG_CRITICAL(const char * errorMsg) + * COMMON_LOG_CRITICAL(const int channelID, const char * errorMsg) * * Internal API for calling the current callback error * logging function with a log level of ERROR_CRITICAL. @@ -87,10 +62,10 @@ MSYS_DLL_EXPORT void COMMON_LOG_ERROR(const unsigned int loggingLevel, const cha * This function is NOT a part of the public API, and * is subject to change at anytime. */ -MSYS_DLL_EXPORT void COMMON_LOG_CRITICAL(const char * errorMsg); +MSYS_DLL_EXPORT void COMMON_LOG_CRITICAL(const int channelID, const char * errorMsg); /* - * COMMON_LOG_WARNING(const char * errorMsg) + * COMMON_LOG_WARNING(const int channelID, const char * errorMsg) * * Internal API for calling the current callback error * logging function with a log level of ERROR_WARNING. @@ -99,10 +74,10 @@ MSYS_DLL_EXPORT void COMMON_LOG_CRITICAL(const char * errorMsg); * This function is NOT a part of the public API, and * is subject to change at anytime. */ -MSYS_DLL_EXPORT void COMMON_LOG_WARNING(const char * errorMsg); +MSYS_DLL_EXPORT void COMMON_LOG_WARNING(const int channelID, const char * errorMsg); /* - * COMMON_LOG_INFO(const char * errorMsg) + * COMMON_LOG_INFO(const int channelID, const char * errorMsg) * * Internal API for calling the current callback error * logging function with a log level of ERROR_INFO. @@ -111,10 +86,10 @@ MSYS_DLL_EXPORT void COMMON_LOG_WARNING(const char * errorMsg); * This function is NOT a part of the public API, and * is subject to change at anytime. */ -MSYS_DLL_EXPORT void COMMON_LOG_INFO(const char * errorMsg); +MSYS_DLL_EXPORT void COMMON_LOG_INFO(const int channelID, const char * errorMsg); /* - * COMMON_LOG_DEBUG(const char * errorMsg) + * COMMON_LOG_DEBUG(const int channelID, const char * errorMsg) * * Internal API for calling the current callback error * logging function with a log level of ERROR_DEBUG. @@ -123,10 +98,10 @@ MSYS_DLL_EXPORT void COMMON_LOG_INFO(const char * errorMsg); * This function is NOT a part of the public API, and * is subject to change at anytime. */ -MSYS_DLL_EXPORT void COMMON_LOG_DEBUG(const char * errorMsg); +MSYS_DLL_EXPORT void COMMON_LOG_DEBUG(const int channelID, const char * errorMsg); /* - * COMMON_LOG_VERBOSE(const char * errorMsg) + * COMMON_LOG_VERBOSE(const int channelID, const char * errorMsg) * * Internal API for calling the current callback error * logging function with a log level of ERROR_VERBOSE. @@ -135,8 +110,13 @@ MSYS_DLL_EXPORT void COMMON_LOG_DEBUG(const char * errorMsg); * This function is NOT a part of the public API, and * is subject to change at anytime. */ -MSYS_DLL_EXPORT void COMMON_LOG_VERBOSE(const char * errorMsg); +MSYS_DLL_EXPORT void COMMON_LOG_VERBOSE(const int channelID, const char * errorMsg); + +/* End C Linkage if needed. */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ -#endif // COMMON_ERROR_HANDLER_INTERNAL_H +#endif /* COMMON_ERROR_HANDLER_INTERNAL_H */ -// End of Common_Error_Handler_Internal.h +/* End of Common_Error_Handler_Internal.h */ diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler_Log_Channel_Defs.c b/src/Common/Src/Error_Handler/Common_Error_Handler_Log_Channel_Defs.c new file mode 100644 index 0000000..8d57558 --- /dev/null +++ b/src/Common/Src/Error_Handler/Common_Error_Handler_Log_Channel_Defs.c @@ -0,0 +1,178 @@ +/*! + Multiverse Engine Project 12/2/2016 Common Common_Error_Handler_Log_Channel_Defs.c + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include headers. */ +#include "Common_Error_Handler_Log_Channel_Defs.h" +#include "Common_Error_Handler_Error_Codes.h" + +/* External includes. */ +#include +#include + +/* Define Common_Error_Log_Channel_Status structure. + + Used internally for managing the states of each logging channel. +*/ +typedef struct Common_Error_Log_Channel_Status { + short disabled; /* Whether or not this channel is disabled. (Messages for disabled channels are discarded.) */ + const int channelID; /* Internal ID number of the channel. */ + const size_t channelNameLength; /* Length of the human readable channel name. */ + const char * const channelName; /* Human readable channel name. */ +} Common_Error_Log_Channel_Status; + +/* Define the internal array for storing the channel statuses. + + Contains a definition for each supported logging channel. +*/ +static Common_Error_Log_Channel_Status commonErrorLoggingChannelStatuses[] = { + {0, MSYS_ERROR_LOG_CHANNEL_UNDEFINED, sizeof(MSYS_ERROR_LOG_CHANNEL_UNDEFINED_NAME), MSYS_ERROR_LOG_CHANNEL_UNDEFINED_NAME}, + {0, MSYS_ERROR_LOG_CHANNEL_USERDEFINED, sizeof(MSYS_ERROR_LOG_CHANNEL_USERDEFINED_NAME), MSYS_ERROR_LOG_CHANNEL_USERDEFINED_NAME}, + {0, MSYS_ERROR_LOG_CHANNEL_ERROR_HANDLER, sizeof(MSYS_ERROR_LOG_CHANNEL_ERROR_HANDLER_NAME), MSYS_ERROR_LOG_CHANNEL_ERROR_HANDLER_NAME}, + {0, MSYS_ERROR_LOG_CHANNEL_DYNLIB, sizeof(MSYS_ERROR_LOG_CHANNEL_DYNLIB_NAME), MSYS_ERROR_LOG_CHANNEL_DYNLIB_NAME}, + {0, MSYS_ERROR_LOG_CHANNEL_BYTEORDER, sizeof(MSYS_ERROR_LOG_CHANNEL_BYTEORDER_NAME), MSYS_ERROR_LOG_CHANNEL_BYTEORDER_NAME}, + {0, MSYS_ERROR_LOG_CHANNEL_FILEUTILLS, sizeof(MSYS_ERROR_LOG_CHANNEL_FILEUTILLS_NAME), MSYS_ERROR_LOG_CHANNEL_FILEUTILLS_NAME}, + {0, MSYS_ERROR_LOG_CHANNEL_THREADUTILS, sizeof(MSYS_ERROR_LOG_CHANNEL_THREADUTILS_NAME), MSYS_ERROR_LOG_CHANNEL_THREADUTILS_NAME}, + {0, MSYS_ERROR_LOG_CHANNEL_RENDERING_SUBSYS, sizeof(MSYS_ERROR_LOG_CHANNEL_RENDERING_SUBSYS_NAME), MSYS_ERROR_LOG_CHANNEL_RENDERING_SUBSYS_NAME}, +}; + +size_t Common_Error_Get_Length_Of_Internal_Status_Array() +{ + /* Internal function to return the length of the commonErrorLoggingChannelStatuses array. + + Should NOT be called outside of this file. + */ + return ((sizeof(Common_Error_Log_Channel_Status) / sizeof(commonErrorLoggingChannelStatuses))); +} + +int Common_Error_Get_Logging_Channel_ID_Number_By_Name(int * channelID, const char * name, const size_t nameLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_INVALID_ARGUMENT; /* The result of this function. */ + size_t x = 0; /* Counter used in the for loop. */ + + /* Check for valid arguments. */ + if ((channelID != NULL) && (name != NULL) && (nameLength > 0)) + { + /* Begin search loop. */ + for (x = 0; ((ret == COMMON_ERROR_INVALID_ARGUMENT) && (x < Common_Error_Get_Length_Of_Internal_Status_Array())); x++) + { + /* Check the current struct in the array for the given channel name length. */ + if (commonErrorLoggingChannelStatuses[x].channelNameLength == nameLength) + { + /* Check for a valid name pointer. */ + if (commonErrorLoggingChannelStatuses[x].channelName != NULL) + { + /* Check the current struct in the array for the given channel name. */ + if (memcmp(name, commonErrorLoggingChannelStatuses[x].channelName, nameLength) == 0) + { + /* Copy the channel ID value. */ + (*channelID) = commonErrorLoggingChannelStatuses[x].channelID; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + } + } + } + } + + /* Exit function. */ + return ret; +} + +int Common_Error_Get_Logging_Channel_Name_By_ID_Number(const int channelID, const char ** name, size_t * nameLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_INVALID_ARGUMENT; /* The result of this function. */ + size_t x = 0; /* Counter used in the for loop. */ + + /* Check for valid arguments. */ + if ((name != NULL) && (nameLength != NULL)) + { + /* Begin search loop. */ + for (x = 0; ((ret == COMMON_ERROR_INVALID_ARGUMENT) && (x < Common_Error_Get_Length_Of_Internal_Status_Array())); x++) + { + /* Check the current struct in the array for the given ID number. */ + if (commonErrorLoggingChannelStatuses[x].channelID == channelID) + { + /* Copy the pointer for the name and the value for the length. */ + (*name) = commonErrorLoggingChannelStatuses[x].channelName; + (*nameLength) = commonErrorLoggingChannelStatuses[x].channelNameLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + } + } + + /* Exit function. */ + return ret; +} + +int Common_Error_Get_Logging_Channel_Status_By_ID_Number(const int channelID) +{ + /* Init vars. */ + int ret = COMMON_ERROR_INVALID_ARGUMENT; /* The result of this function. */ + size_t x = 0; /* Counter used in the for loop. */ + + /* Begin search loop. */ + for (x = 0; ((ret == COMMON_ERROR_INVALID_ARGUMENT) && (x < Common_Error_Get_Length_Of_Internal_Status_Array())); x++) + { + /* Check the current struct in the array for the given ID number. */ + if (commonErrorLoggingChannelStatuses[x].channelID == channelID) + { + /* Check and see if the channel is enabled. + Returns COMMON_ERROR_TRUE if the channel is enabled, or + Returns COMMON_ERROR_FALSE if the channel is disabled. + */ + ret = ((commonErrorLoggingChannelStatuses[x].disabled) ? (COMMON_ERROR_FALSE) : (COMMON_ERROR_TRUE)); + } + } + + /* Exit function. */ + return ret; +} + +int Common_Error_Set_Logging_Channel_Status_By_ID_Number(const int channelID, const short enabled) +{ + /* Init vars. */ + int ret = COMMON_ERROR_INVALID_ARGUMENT; /* The result of this function. */ + size_t x = 0; /* Counter used in the for loop. */ + + /* Begin search loop. */ + for (x = 0; ((ret == COMMON_ERROR_INVALID_ARGUMENT) && (x < Common_Error_Get_Length_Of_Internal_Status_Array())); x++) + { + /* Check the current struct in the array for the given ID number. */ + if (commonErrorLoggingChannelStatuses[x].channelID == channelID) + { + /* Set the channel status. + Set commonErrorLoggingChannelStatuses[x].disabled to non-zero value if the channel should be disabled. + Set commonErrorLoggingChannelStatuses[x].disabled to zero if the channel should be enabled. + */ + commonErrorLoggingChannelStatuses[x].disabled = ((enabled) ? (0) : (1)); + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + } + + /* Exit function. */ + return ret; +} diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler_Log_Channel_Defs.h b/src/Common/Src/Error_Handler/Common_Error_Handler_Log_Channel_Defs.h new file mode 100644 index 0000000..72d02f3 --- /dev/null +++ b/src/Common/Src/Error_Handler/Common_Error_Handler_Log_Channel_Defs.h @@ -0,0 +1,128 @@ +/*! + Multiverse Engine Project 12/2/2016 Common Common_Error_Handler_Log_Channel_Defs.h + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef COMMON_ERROR_HANDLER_LOG_CHANNEL_DEFS_H +#define COMMON_ERROR_HANDLER_LOG_CHANNEL_DEFS_H + +/* Internal includes. */ +#include "../../../DLL_PORT.h" + +/* External includes. */ +#include + +/* Define error log channels. */ +#define MSYS_ERROR_LOG_CHANNEL_LIST_VER 0 /* Version ID of the channel list. If you change the channel IDs below change the version ID. */ +#define MSYS_ERROR_LOG_CHANNEL_UNDEFINED 0 +#define MSYS_ERROR_LOG_CHANNEL_USERDEFINED 1 +#define MSYS_ERROR_LOG_CHANNEL_ERROR_HANDLER 2 +#define MSYS_ERROR_LOG_CHANNEL_DYNLIB 3 +#define MSYS_ERROR_LOG_CHANNEL_BYTEORDER 4 +#define MSYS_ERROR_LOG_CHANNEL_FILEUTILLS 5 +#define MSYS_ERROR_LOG_CHANNEL_THREADUTILS 6 +#define MSYS_ERROR_LOG_CHANNEL_RENDERING_SUBSYS 7 + +/* Define the human readable error log channel name strings. */ +#define MSYS_ERROR_LOG_CHANNEL_UNDEFINED_NAME "Undefined" +#define MSYS_ERROR_LOG_CHANNEL_USERDEFINED_NAME "User Defined" +#define MSYS_ERROR_LOG_CHANNEL_ERROR_HANDLER_NAME "Error Handler" +#define MSYS_ERROR_LOG_CHANNEL_DYNLIB_NAME "Dynamic Library Subsystem" +#define MSYS_ERROR_LOG_CHANNEL_BYTEORDER_NAME "Byte Order" +#define MSYS_ERROR_LOG_CHANNEL_FILEUTILLS_NAME "File Management Subsystem" +#define MSYS_ERROR_LOG_CHANNEL_THREADUTILS_NAME "Threading Subsystem" +#define MSYS_ERROR_LOG_CHANNEL_RENDERING_SUBSYS_NAME "Rendering Subsystem" + +/* Check for a C++ Compiler. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*! + int Common_Error_Get_Logging_Channel_ID_Number_By_Name(int * channelID, const char * name, const size_t nameLength) + + Takes the given human readable error log channel name (in C-String format) and length, and returns it's associated channel ID number. + + Note: This function will overwrite whatever value is stored by the given channel ID argument. + If you need that value after this function returns, copy it elsewhere before calling this function. + + Returns COMMON_ERROR_SUCCESS if the channel ID was found. (The channel ID argument will be updated in this case.) + Returns COMMON_ERROR_INVALID_ARGUMENT if the given channel name is invalid / not recognized, the given channel name + length is less than or equal to zero, or a given pointer is NULL. + + No-alteration clause: + - In case of error, this function will not modify it's arguments. + */ +MSYS_DLL_EXPORT int Common_Error_Get_Logging_Channel_ID_Number_By_Name(int * channelID, const char * name, const size_t nameLength); + +/*! + int Common_Error_Get_Logging_Channel_Name_By_ID_Number(const int channelID, const char ** name, size_t * nameLength) + + Takes the given error log channel ID number, and returns a pointer to a human readable name in C-String format, along with it's length. + + WARNING: DO NOT ATTEMPT TO DEALLOCATE OR ALTER THIS STRING. IT IS FOR REFERENCE ONLY. + + Note: This function will overwrite whatever values are stored by the given name pointer and length arguments. + If you need those values after this function returns, copy them elsewhere before calling this function. + + Returns COMMON_ERROR_SUCCESS if the channel name was found. (The pointer and length arguments will be updated in this case.) + Returns COMMON_ERROR_INVALID_ARGUMENT if the given channel ID is invalid / not recognized, or a given pointer is NULL. + + No-alteration clause: + - In case of error, this function will not modify it's arguments. + */ +MSYS_DLL_EXPORT int Common_Error_Get_Logging_Channel_Name_By_ID_Number(const int channelID, const char ** name, size_t * nameLength); + +/*! + int Common_Error_Get_Logging_Channel_Status_By_ID_Number(const int channelID) + + Takes the given error log channel ID number, and returns whether or not the channel is enabled. + (If a channel is enabled, it's messages will be sent to the registered error handler callback function. + If a channel is disabled, it's messages will be discarded.) + + Returns COMMON_ERROR_TRUE if the channel is enabled. + Returns COMMON_ERROR_FALSE if the channel is disabled. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given channel ID is invalid / not recognized. + */ +MSYS_DLL_EXPORT int Common_Error_Get_Logging_Channel_Status_By_ID_Number(const int channelID); + +/*! + int Common_Error_Set_Logging_Channel_Status_By_ID_Number(const int channelID, const short enabled) + + Takes the given error log channel ID number, and sets whether or not the channel is enabled. + (If a channel is enabled, it's messages will be sent to the registered error handler callback function. + If a channel is disabled, it's messages will be discarded.) + + Note: This function will return success if the given channel is already set to the given status. + + Pram enabled: If enabled is set to zero, the given channel will be disabled. + If enabled is set to a non-zero value, the given channel will be enabled. + + Returns COMMON_ERROR_SUCCESS if setting the channel's status was successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given channel ID is invalid / not recognized. + */ +MSYS_DLL_EXPORT int Common_Error_Set_Logging_Channel_Status_By_ID_Number(const int channelID, const short enabled); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* COMMON_ERROR_HANDLER_LOG_CHANNEL_DEFS_H */ + +/* End of Common_Error_Handler_Log_Channel_Defs.h. */ diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler_Structures.c b/src/Common/Src/Error_Handler/Common_Error_Handler_Structures.c new file mode 100644 index 0000000..1a5d3f4 --- /dev/null +++ b/src/Common/Src/Error_Handler/Common_Error_Handler_Structures.c @@ -0,0 +1,173 @@ +/*! + Multiverse Engine Project 16/5/2015 Common Common_Error_Handler_Structures.c + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal Includes. */ +#include "Common_Error_Handler.h" + +/* Enable C linkage if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*! + * const char * COMMON_ERROR_UNKNOWN_ERROR_MSG + * + * This is the definition for the generic unknown error + * code. + * + * It is defined here to allow Common_Get_Error_Message() + * to reference it without needing to use another for loop + * to locate it's offset in the error message table. + */ +const char COMMON_ERROR_UNKNOWN_ERROR_MSG[] = COMMON_UNKNOWN_ERROR_MSG_DEF; + +/*! + * const Common_Error_Object Common_commonErrorTable[] + * + * This array contains all of the error codes that a Common namespace + * function may return, in addition to a human readable string describing + * the error code's meaning. (This is a work in progress, some error codes + * conflict with error codes in other functions, as such these are slowly + * being corrected.) + * + * The easiest way of fetching the human readable string from this table + * is to call Common::GetErrorMessage(). + * + * Do not reuse an existing error code. (If the error is generic then use a + * generic error code.) + * + * Do not change an existing error code. (This is to keep the API stable + * between revisions.) + */ +const Common_Error_Object Common_commonErrorTable[] = { + /* Define the initilizers for the lookup table. */ + {COMMON_ERROR_UNKNOWN_ERROR, COMMON_ERROR_UNKNOWN_ERROR_MSG}, + {COMMON_ERROR_SYSTEM_SPECIFIC, "System specific error code. Check log."}, + {COMMON_ERROR_SUCCESS, "Success."}, + {COMMON_ERROR_INVALID_ARGUMENT, "Invalid argument."}, + {COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED, "Function not implemented."}, + {COMMON_ERROR_ACCESS_DENIED, "Access Denied."}, + {COMMON_ERROR_EXCEPTION_THROWN, "Execption thrown."}, + {COMMON_ERROR_INTERNAL_ERROR, "Internal error."}, + {COMMON_ERROR_IO_ERROR, "Input / Output error. (Normally this is a hardware issue.)"}, + {COMMON_ERROR_RANGE_ERROR, "Given range is invalid."}, + {COMMON_ERROR_MEMORY_ERROR, "Memory error (Could not allocate memory / Control loop out of bounds.)"}, + {COMMON_ERROR_INVALID_LIBRARY_ID, "Given LibraryID does not match a supported library or a loaded plugin."}, + {COMMON_ERROR_PEBKAC_INVALID_OPERATION_ORDER, "Caller is attempting to do something that requires another call / process to be completed first. (Check the order in which you are doing things.)"}, + {COMMON_ERROR_CANNOT_GET_SYSTEM_TIME, "Could not get the system's current time."}, + {COMMON_ERROR_SUBSYSTEM_OBJECT_NOT_INITED, "Given object has not been inited yet."}, + {COMMON_ERROR_SUBSYSTEM_OBJECT_ALREADY_INITED, "Given object has already been inited."}, + {COMMON_ERROR_END_OF_DATA, "There is no remaining data to process."}, + {COMMON_ERROR_COMPARISON_PASSED, "A check passed it's requirements."}, + {COMMON_ERROR_COMPARISON_FAILED, "A check failed to pass it's requirements."}, + {COMMON_ERROR_RACE_CONDITION, "Another process or thread has altered the state of an object needed by the function while the function was using it. Because of the modification to the object, the function could not complete it's task successfully. Please make sure nothing is accessing the needed object before calling the function again."}, + {COMMON_ERROR_HOST_NOT_SUPPORTED, "The host system does not support the requested function / command / feature."}, + {COMMON_ERROR_TRUE, "The result of the previous operation returned true. (See the code documentation for what the result actually means. This is just a generic boolean response.)"}, + {COMMON_ERROR_FALSE, "The result of the previous operation returned false. (See the code documentation for what the result actually means. This is just a generic boolean response.)"}, + {COMMON_ERROR_ARGUMENT_CONVERSION_FAILURE, "A given argument to the previous engine call was not converted properly. (See the code documentation for what the result actually means.)"}, + {COMMON_ERROR_FALSE_SUCCESS, "A success code was returned without a valid result. (AKA. Some expected return value was not obtained, was invalid, corrupted, etc. This can happen when the engine interacts with poorly written / designed code. Including when interacting with itself.)"}, + {COMMON_ERROR_PEBKAC_OPERATION_ALREADY_COMPLETED, "Caller is attempting to redo an operation that only needs to be done once per program execution / object, unless it is undone. (Currently the operation has already been performed, so the call was not needed.)"}, + {COMMON_ERROR_NO_DATA, "There was no data to process."}, + {COMMON_ERROR_DATA_CORRUPTION, "The given data / structure was in an inconsistant state."}, + {COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED, "A limit defined by the system is being surpassed. (Normally this is something like a variable being incremented beyond it's MAX value. Note: This code means the error was caught before it happened.)"}, + {COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL, "The destionation memory buffer was too small."}, + /* Rendering Subsystem error codes. */ + {RENDERER_ERROR_UNABLE_TO_ALLOC_OI_BUF, "Could not allocate memory for overlay image buffer."}, + {RENDERER_ERROR_UNABLE_TO_ALLOC_TD_BUF, "Could not allocate memory for transparency data buffer."}, + {RENDERER_ERROR_MEM_BUF_ALLOC_EXCEPTION, "Exception thrown while attempting to allocate memory buffer(s)."}, + {RENDERER_ERROR_DUPE_OVERLAY_EXCEPTION, "Exception thrown while duplicating overlay, clearing dest overlay."}, + {RENDERER_ERROR_INVAL_OVERLAY_SELF_OVERWRITE, "Given overlays are the same. Cannot overwrite an overlay with itself."}, + {RENDERER_ERROR_TRANSPARENCY_DISABLED, "Transparency is disabled on given overlay."}, + /* Threading Subsystem (Thread_Utils) error codes. */ + {THREAD_UTILS_ERROR_EXCEPTION_THROWN, "Exception thrown in threading subsystem."}, + {THREAD_UTILS_ERROR_PLUGIN_LOAD_FAILURE, "Unable to load plugin(s). Internal error."}, + {THREAD_UTILS_ERROR_THREAD_COULD_NOT_START, "Could not start new thread."}, + {THREAD_UTILS_ERROR_THREAD_COULD_NOT_DETACH, "Could not detach thread."}, + {THREAD_UTILS_ERROR_THREAD_COULD_NOT_JOIN, "Could not join thread."}, + {THREAD_UTILS_ERROR_MUTEX_ALREADY_LOCKED, "The given mutex is already locked."}, + {THREAD_UTILS_ERROR_CONDITION_WAIT_TIMEOUT_REACHED, "Given timeout period was exceeded while waiting for the condition variable to signal."}, + {THREAD_UTILS_ERROR_CONDITION_CANNOT_LOCK_MUTEX, "Could not lock internal mutex in condition variable object."}, + /* FileUtills. */ + {FILEUTILLS_ERROR_EXISTANT, "The path exists."}, + {FILEUTILLS_ERROR_NON_EXISTANT, "The path (or a component of the path) does not exist."}, + {FILEUTILLS_ERROR_READ_ONLY, "The path is read only."}, + {FILEUTILLS_ERROR_PATH_LENGTH_INVALID, "The path's length is beyond the filesystem's maximum length."}, + {FILEUTILLS_ERROR_PATH_FILE_AS_DIRECTORY, "The path has a file in it that is being treated as a directory."}, + {FILEUTILLS_ERROR_PATH_IS_A_FILE, "Given path is a file."}, + {FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY, "Given path is a directory."}, + {FILEUTILLS_ERROR_PATH_IS_A_SYMLINK, "Given path is a symbolic link."}, + {FILEUTILLS_ERROR_PATH_IS_ABSOLUTE, "Given path is in absolute format (Fully resolved)."}, + {FILEUTILLS_ERROR_PATH_IS_RELATIVE, "Given path is in relative format (Needs resolving)."}, + {FILEUTILLS_ERROR_FILESYSTEM_FULL, "Given filesystem is full."}, + {FILEUTILLS_ERROR_FILESYSTEM_QUOTA_REACHED, "User's disk usage quota for the given filesystem has been reached."}, + {FILEUTILLS_ERROR_EMPTY_DIRECTORY, "The given path is an empty directory."}, + {FILEUTILLS_ERROR_NON_EMPTY_DIRECTORY, "The given path is a non-empty directory."}, + {FILEUTILLS_ERROR_SYMLINK_CHAIN_TOO_DEEP, "While parsing a host-defined symbolic link chain, the host system indicated the link chain was longer than what it supports."}, + /* UI Subsystem */ + {UI_SUBSYSTEM_ERROR_EXCEPTION_THROWN, "An exception was thrown in the UI Subsystem."}, + /* Dynamic Library Subsystem. */ + {DYNLIB_ERROR_INVALID_LIBRARY, "The given library is not recognized by the system. (Invalid container format / not a library / data corruption / etc.)"}, + {DYNLIB_ERROR_LIBRARY_ALREADY_LOADED, "The given library is already loaded."}, + {DYNLIB_ERROR_LIBRARY_NOT_LOADED, "The given library is not currently loaded."}, + {DYNLIB_ERROR_HOST_VS_LIBRARY_MISMATCH, "The given library was built for a different system. (Different OS / Arch / etc.)"}, + {DYNLIB_ERROR_SYMBOL_NOT_FOUND, "The requested symbol was not found in the given library."}, + /* TODO: Need to add the error codes from all common namespace functions. */ +}; + +const unsigned int Common_Get_Error_Table_API_Version() +{ + return COMMON_ERROR_TABLE_ABI_VER; +} + +const unsigned int Common_Get_Error_Table_Size() +{ + return (sizeof(Common_commonErrorTable) / sizeof(Common_Error_Object)); +} + +const char * Common_Get_Error_Message(const int errorCode) +{ + /* Init vars. */ + const char * result = COMMON_ERROR_UNKNOWN_ERROR_MSG; /* Result of this function. */ + const size_t errorTableSize = Common_Get_Error_Table_Size(); /* Size of the Common error lookup table. */ + size_t x = 0; /* Counter used in for loop. */ + + /* Check for COMMON_UNKNOWN_ERROR. */ + if (errorCode != COMMON_ERROR_UNKNOWN_ERROR) + { + /* Begin lookup loop. */ + for (x = 0; ((x < errorTableSize) && (result == COMMON_ERROR_UNKNOWN_ERROR_MSG)); x++) + { + /* Check for the correct error code. */ + if (Common_commonErrorTable[x].errorCode == errorCode) + { + /* Found the correct error code. */ + result = Common_commonErrorTable[x].error; + } + } + } + + /* Return the result. */ + return result; +} + +/* End C Linkage if needed. */ +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler_Structures.h b/src/Common/Src/Error_Handler/Common_Error_Handler_Structures.h index eb656b1..98d6bc8 100644 --- a/src/Common/Src/Error_Handler/Common_Error_Handler_Structures.h +++ b/src/Common/Src/Error_Handler/Common_Error_Handler_Structures.h @@ -23,132 +23,90 @@ #define COMMON_ERROR_HANDLER_STRUCTURES_H /* Internal Includes. */ -#include "Error_Struct.h" // Structure used to create error lookup table. +#include "Error_Struct.h" /* Structure used to create error lookup table. */ +#include "../../../DLL_PORT.h" /* Defines MSYS_DLL_EXPORT. */ -namespace Common { - // Common namespace error code definitions. - // Error code table ABI version. - const unsigned int COMMON_ERROR_ABI_VER = 1; // Increment this if you alter the table. +/* Define the Common unknown error message. (It has to be replicated in two places, once for the C code and once for the C++ bindings.) */ +#define COMMON_UNKNOWN_ERROR_MSG_DEF "Unknown error code." +/* Error code table ABI version. */ +#define COMMON_ERROR_TABLE_ABI_VER 5 /* Increment this if you alter the table. */ + +/* Enable C linkage if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* ERROR CODES MOVED TO Common_Error_Handler_Error_Codes.h */ + +/*! + * const unsigned int Common_Get_Error_Table_API_Version() + * + * Returns the API version number of the common error table. + */ +MSYS_DLL_EXPORT const unsigned int Common_Get_Error_Table_API_Version(); + +/*! + * const unsigned int Common_Get_Error_Table_Size() + * + * Returns the size of the common error table. + */ +MSYS_DLL_EXPORT const unsigned int Common_Get_Error_Table_Size(); + +/*! + * const char * Common_Get_Error_Message(const int & errorCode) + * + * This function takes the given error code and returns a pointer to a human + * readable string describing the meaning of the given error code. + * + * Returns a valid pointer if the given error code is in the common error table. + * Returns the message for Common_COMMON_UNKNOWN_ERROR otherwise. + */ +MSYS_DLL_EXPORT const char * Common_Get_Error_Message(const int errorCode); + +/* End C Linkage if needed. */ +#ifdef __cplusplus +} /* extern C */ +#endif /* __cplusplus */ + +/* Define C++ Bindings. */ +#ifdef __cplusplus +/* Define namespaces. */ +namespace Common +{ /*! - * const static char * Common::UNKNOWN_ERROR_MSG + * const unsigned int Common::Get_Error_Table_API_Version() * - * This is the definition for the generic unknown error - * code. + * (C++ Binding) * - * It is defined here to allow Common::Get_Error_Message() - * to reference it without needing to use another for loop - * to locate it's offset in the error message table. + * Returns the API version number of the common error table. */ - const static char * UNKNOWN_ERROR_MSG = "Unknown error code."; - - // Error code table. - enum { - // Generic error codes. - COMMON_SYSTEM_SPECIFIC = 99, - COMMON_SUCCESS = 0, - COMMON_UNKNOWN_ERROR = -1, - COMMON_INVALID_ARGUMENT = -2, - COMMON_FUNCTION_NOT_IMPLEMENTED = -3, - COMMON_ACCESS_DENIED = -4, - COMMON_EXCEPTION_THROWN = -5, - COMMON_INTERNAL_ERROR = -6, - COMMON_IO_ERROR = -7, - COMMON_RANGE_ERROR = -8, - COMMON_MEMORY_ERROR = -9, - // Rendering Subsystem error codes. - RENDERER_UNABLE_TO_ALLOC_OI_BUF = -12, // Overlay image buffer. - RENDERER_UNABLE_TO_ALLOC_TD_BUF = -13, // Transparency data buffer. - RENDERER_MEM_BUF_ALLOC_EXCEPTION = -14, - RENDERER_DUPE_OVERLAY_EXCEPTION = -15, - RENDERER_INVAL_OVERLAY_SELF_OVERWRITE = -16, - RENDERER_TRANSPARENCY_DISABLED = -17, - // Threading Subsystem error codes. - THREAD_UTILS_EXCEPTION_THROWN = -31, - THREAD_UTILS_INVALID_LIBRARY_ID = -32, - THREAD_UTILS_PLUGIN_LOAD_FAILURE = -33, - // FileUtills error codes. - FILEUTILLS_EXISTANT = -60, - FILEUTILLS_NON_EXISTANT = -61, - FILEUTILLS_READ_ONLY = -62, - FILEUTILLS_PATH_LENGTH_INVALID = -63, - FILEUTILLS_PATH_FILE_AS_DIRECTORY = -64, - FILEUTILLS_PATH_IS_A_FILE = -65, - FILEUTILLS_PATH_IS_A_DIRECTORY = -66, - FILEUTILLS_FILESYSTEM_FULL = -67, - FILEUTILLS_FILESYSTEM_QUOTA_REACHED = -68, - FILEUTILLS_EMPTY_DIRECTORY = -69, - FILEUTILLS_NON_EMPTY_DIRECTORY = -70, - }; + MSYS_DLL_EXPORT const unsigned int Get_Error_Table_API_Version(); /*! - * const Common_Error_Object Common::commonErrorTable[] - * - * This array contains all of the error codes that a Common namespace - * function may return, in addition to a human readable string describing - * the error code's meaning. (This is a work in progress, some error codes - * conflict with error codes in other functions, as such these are slowly - * being corrected.) - * - * The easiest way of fetching the human readable string from this table - * is to call Common::GetErrorMessage(). - * - * Do not reuse an existing error code. (If the error is generic then use a - * generic error code.) - * - * Do not change an existing error code. (This is to keep the API stable - * between revisions.) + * const unsigned int Common::Get_Error_Table_Size() + * + * (C++ Binding) + * + * Returns the size of the common error table. */ - const Common_Error_Object commonErrorTable[] = { - {COMMON_UNKNOWN_ERROR, UNKNOWN_ERROR_MSG}, - {COMMON_SYSTEM_SPECIFIC, "System specific error code. Check log."}, - {COMMON_SUCCESS, "Success."}, - {COMMON_INVALID_ARGUMENT, "Invalid argument."}, - {COMMON_FUNCTION_NOT_IMPLEMENTED, "Function not implemented."}, - {COMMON_ACCESS_DENIED, "Access Denied."}, - {COMMON_EXCEPTION_THROWN, "Execption thrown."}, - {COMMON_INTERNAL_ERROR, "Internal error."}, - {COMMON_IO_ERROR, "Input / Output error. (Normally this is a hardware issue.)"}, - {COMMON_RANGE_ERROR, "Given range is invalid."}, - {COMMON_MEMORY_ERROR, "Memory error (Could not allocate memory / Control loop out of bounds.)"}, - // Rendering Subsystem error codes. - {RENDERER_UNABLE_TO_ALLOC_OI_BUF, "Could not allocate memory for overlay image buffer."}, - {RENDERER_UNABLE_TO_ALLOC_TD_BUF, "Could not allocate memory for transparency data buffer."}, - {RENDERER_MEM_BUF_ALLOC_EXCEPTION, "Exception thrown while attempting to allocate memory buffer(s)."}, - {RENDERER_DUPE_OVERLAY_EXCEPTION, "Exception thrown while duplicating overlay, clearing dest overlay."}, - {RENDERER_INVAL_OVERLAY_SELF_OVERWRITE, "Given overlays are the same. Cannot overwrite an overlay with itself."}, - {RENDERER_TRANSPARENCY_DISABLED, "Transparency is disabled on given overlay."}, - // Threading Subsystem (Thread_Utils) error codes. - {THREAD_UTILS_EXCEPTION_THROWN, "Exception thrown in threading subsystem."}, - {THREAD_UTILS_INVALID_LIBRARY_ID, "Given LibraryID does not match a supported library or a loaded plugin."}, - {THREAD_UTILS_PLUGIN_LOAD_FAILURE, "Unable to load plugin(s). Internal error."}, - // FileUtills. - {FILEUTILLS_EXISTANT, "The path exists."}, - {FILEUTILLS_NON_EXISTANT, "The path (or a component of the path) does not exist."}, - {FILEUTILLS_READ_ONLY, "The path is read only."}, - {FILEUTILLS_PATH_LENGTH_INVALID, "The path's length is beyond the filesystem's maximum length."}, - {FILEUTILLS_PATH_FILE_AS_DIRECTORY, "The path has a file in it that is being treated as a directory."}, - {FILEUTILLS_PATH_IS_A_FILE, "Given path is a file."}, - {FILEUTILLS_PATH_IS_A_DIRECTORY, "Given path is a directory."}, - {FILEUTILLS_FILESYSTEM_FULL, "Given filesystem is full."}, - {FILEUTILLS_FILESYSTEM_QUOTA_REACHED, "User's disk usage quota for the given filesystem has been reached."}, - {FILEUTILLS_EMPTY_DIRECTORY, "The given path is an empty directory."}, - {FILEUTILLS_NON_EMPTY_DIRECTORY, "The given path is a non-empty directory."}, - // TODO: Need to add the error codes from all common namespace functions. - }; + MSYS_DLL_EXPORT const unsigned int Get_Error_Table_Size(); /*! - * static int Common::commonLastErrorCode + * const char * Common::Get_Error_Message(const int & errorCode) + * + * (C++ Binding) * - * Contains the last error code encountered by a Common namespace function. + * This function takes the given error code and returns a pointer to a human + * readable string describing the meaning of the given error code. * - * Note: Calling Common::GetErrorMessage() or Common::GetErrorTableSize() - * will NOT clear this variable. - * Calling any other Common namespace function WILL clear this variable. + * Returns a valid pointer if the given error code is in the common error table. + * Returns the message for Common_COMMON_UNKNOWN_ERROR otherwise. */ - static int commonLastErrorCode = COMMON_SUCCESS; + MSYS_DLL_EXPORT const char * Get_Error_Message(const int & errorCode); }; +#endif /* __cplusplus */ #endif /* COMMON_ERROR_HANDLER_STRUCTURES_H */ -// End of Common_Error_Handler_Structures.h +/* End of Common_Error_Handler_Structures.h */ diff --git a/src/Common/Src/Error_Handler/Common_Error_Handler_Structures_CPP_Bindings.cpp b/src/Common/Src/Error_Handler/Common_Error_Handler_Structures_CPP_Bindings.cpp new file mode 100644 index 0000000..0fd7237 --- /dev/null +++ b/src/Common/Src/Error_Handler/Common_Error_Handler_Structures_CPP_Bindings.cpp @@ -0,0 +1,41 @@ +/*! + Multiverse Engine Project 16/5/2015 Common Common_Error_Handler_Structures_CPP_Bindings.cpp + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Internal Includes. +#include "Common_Error_Handler.h" + +// Define C++ Bindings. +#ifdef __cplusplus + +const unsigned int Common::Get_Error_Table_API_Version() +{ + return Common_Get_Error_Table_API_Version(); +} + +const unsigned int Common::Get_Error_Table_Size() +{ + return Common_Get_Error_Table_Size(); +} + +const char * Common::Get_Error_Message(const int & errorCode) +{ + return Common_Get_Error_Message(errorCode); +} +#endif // __cplusplus diff --git a/src/Common/Src/Error_Handler/Error_Struct.h b/src/Common/Src/Error_Handler/Error_Struct.h index d551a0a..e08b1b0 100644 --- a/src/Common/Src/Error_Handler/Error_Struct.h +++ b/src/Common/Src/Error_Handler/Error_Struct.h @@ -18,14 +18,31 @@ https://github.com/codebase7/mengine */ +// Include guard. #ifndef ERROR_STRUCT_H #define ERROR_STRUCT_H -struct Common_Error_Object { +// Enable C linkage if needed. +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/*! + * typedef struct Common_Error_Object_T + * + * Defines the data structure used by the Common Error Handler functions + * to look up human-readable strings and their assoicated error codes. + */ +typedef struct Common_Error_Object_T { short errorCode; const char * error; -}; +} Common_Error_Object; + +// End C Linkage if needed. +#ifdef __cplusplus +} +#endif // __cplusplus -#endif +#endif // ERROR_STRUCT_H -// End of Error_Struct.h \ No newline at end of file +// End of Error_Struct.h diff --git a/src/Common/Src/Error_Handler/Posix_Error_Translation_Table.cpp b/src/Common/Src/Error_Handler/Posix_Error_Translation_Table.c similarity index 60% rename from src/Common/Src/Error_Handler/Posix_Error_Translation_Table.cpp rename to src/Common/Src/Error_Handler/Posix_Error_Translation_Table.c index edfe8c1..e33fe41 100644 --- a/src/Common/Src/Error_Handler/Posix_Error_Translation_Table.cpp +++ b/src/Common/Src/Error_Handler/Posix_Error_Translation_Table.c @@ -1,5 +1,5 @@ /*! - Multiverse Engine Project 05/7/2014 Common Posix_Error_Translation_Table.cpp + Multiverse Engine Project 05/7/2014 Common Posix_Error_Translation_Table.c Copyright (C) 2014 Multiverse Engine Project @@ -22,33 +22,48 @@ #include "Common_Error_Handler_Structures.h" #include "Posix_Error_Translation_Table.h" -unsigned int Common::Get_Posix_Error_Translation_Table_Size() +// Include Extern C if needed. +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +const unsigned int Common_Get_Posix_Error_Translation_Table_API_Version() +{ + return COMMON_POSIX_ERROR_TRANSLATION_TABLE_API_VERSION; +} + +const unsigned int Common_Get_Posix_Error_Translation_Table_Size() { - return (sizeof(Common::posixErrorTranslationTable) / sizeof(Common::posixErrnoTableEntry)); + return (sizeof(Common_posixErrorTranslationTable) / sizeof(Common_posixErrnoTableEntry)); } -int Common::Translate_Posix_Errno_To_Common_Error_Code(const int & err) +int Common_Translate_Posix_Errno_To_Common_Error_Code(const int err) { // Init result. - int ret = COMMON_SYSTEM_SPECIFIC; /* - * Default is COMMON_SYSTEM_SPECIFIC, as + int ret = COMMON_ERROR_SYSTEM_SPECIFIC; /* + * Default is COMMON_ERROR_SYSTEM_SPECIFIC, as * not all POSIX errno(s) are / can be * reperesented in the Common namespace * error code table. */ // Run loop. - for (unsigned int x = 0; ((x < Common::Get_Posix_Error_Translation_Table_Size()) && - (ret == COMMON_SYSTEM_SPECIFIC)); x++) + for (unsigned int x = 0; ((x < Common_Get_Posix_Error_Translation_Table_Size()) && + (ret == COMMON_ERROR_SYSTEM_SPECIFIC)); x++) { // Check for a match in the error code translation table. - if (Common::posixErrorTranslationTable[x].posixErrorNo == err) + if (Common_posixErrorTranslationTable[x].posixErrorNo == err) { // Match found, return the Common namespace error code. - ret = Common::posixErrorTranslationTable[x].commonErrorCode; + ret = Common_posixErrorTranslationTable[x].commonErrorCode; } } // Return the result. return ret; -} \ No newline at end of file +} + +// Terminate C linkage if needed. +#ifdef __cplusplus +} +#endif // __cplusplus diff --git a/src/Common/Src/Error_Handler/Posix_Error_Translation_Table.h b/src/Common/Src/Error_Handler/Posix_Error_Translation_Table.h index 25d3ab6..f5a66a1 100644 --- a/src/Common/Src/Error_Handler/Posix_Error_Translation_Table.h +++ b/src/Common/Src/Error_Handler/Posix_Error_Translation_Table.h @@ -25,66 +25,146 @@ // Include POSIX errno header. #include +// Enable C linkage if needed. +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/* Include common error code definitions. */ +#include "Common_Error_Handler_Error_Codes.h" + +/*! + * #define COMMON_POSIX_ERROR_TRANSLATION_TABLE_API_VERSION + * + * Defines the API version of the posix errno to + * common namespace error code translation table. + * + * If you change the table increment the version + * number. + */ +#define COMMON_POSIX_ERROR_TRANSLATION_TABLE_API_VERSION 1 + +/*! + * typedef struct Common_posixErrnoTableEntry_T Common_posixErrnoTableEntry + * + * (C Binding.) + * + * Used to construct the errno translation table. + * for POSIX compliant systems. + */ +typedef struct Common_posixErrnoTableEntry_T { + int posixErrorNo; + int commonErrorCode; +} Common_posixErrnoTableEntry; + +/*! + * const static Common_posixErrnoTableEntry Common_posixErrorTranslationTable[] + * + * A table that contains (some) POSIX errno codes and their + * Common namespace error code equilivants. + * + * Not all POSIX errno(s) are present in this table, as + * some POSIX errno(s) are specific to POSIX compliant + * systems. As such there is no Common namespace error + * code (beyond COMMON_SYSTEM_SPECIFIC) to represent them. + */ +const static Common_posixErrnoTableEntry Common_posixErrorTranslationTable[] = { + {EACCES, COMMON_ERROR_ACCESS_DENIED}, + {ENOENT, FILEUTILLS_ERROR_NON_EXISTANT}, + {EEXIST, FILEUTILLS_ERROR_EXISTANT}, + {EROFS, FILEUTILLS_ERROR_READ_ONLY}, + {EINVAL, COMMON_ERROR_INVALID_ARGUMENT}, + {ENAMETOOLONG, FILEUTILLS_ERROR_PATH_LENGTH_INVALID}, + {ENOTDIR, FILEUTILLS_ERROR_PATH_FILE_AS_DIRECTORY}, + {ENOMEM, COMMON_ERROR_MEMORY_ERROR}, + {EFAULT, COMMON_ERROR_INVALID_ARGUMENT}, + {ENOSPC, FILEUTILLS_ERROR_FILESYSTEM_FULL}, + {EDQUOT, FILEUTILLS_ERROR_FILESYSTEM_QUOTA_REACHED}, + {ENOTEMPTY, FILEUTILLS_ERROR_NON_EMPTY_DIRECTORY}, +}; + +/*! + * const unsigned int Common_Get_Posix_Error_Translation_Table_API_Version() + * + * Returns the API Version number of the Common_posixErrorTranslationTable + * array. + */ +const unsigned int Common_Get_Posix_Error_Translation_Table_API_Version(); + +/*! + * const unsigned int Common_Get_Posix_Error_Translation_Table_Size() + * + * Returns the size of the Common::posixErrorTranslationTable + * array. + */ +const unsigned int Common_Get_Posix_Error_Translation_Table_Size(); + +/*! + * int Common_Translate_Posix_Errno_To_Common_Error_Code(const int err) + * + * Translates the given POSIX errno to it's Common namespace + * error code equilivant. (If applicable.) + * + * WARNING: Because the POSIX errno variable is global, you + * should never call this function passing errno directly. + * + * Instead, copy the errno to a temporary variable and then + * pass the temporary variable to this function. + * + * (Otherwise just calling this function could change errno + * before it is checked.) + * + * @pram const int err, the POSIX error code to translate. + * + * Returns the translated Common namespace error code if applicable. + * + * Returns COMMON_SYSTEM_SPECIFIC if the POSIX errno does not have a + * Common namespace error code translation. + */ +int Common_Translate_Posix_Errno_To_Common_Error_Code(const int err); + +// End C Linkage if needed. +#ifdef __cplusplus +} +#endif // __cplusplus + +// Check for a C compiler. +#ifdef __cplusplus namespace Common { /*! - * struct Common::posixErrnoTableEntry - * + * typedef Common_posixErrnoTableEntry Common::posixErrnoTableEntry + * + * (C++ Binding.) + * * Used to construct the errno translation table. * for POSIX compliant systems. */ - struct posixErrnoTableEntry{ - int posixErrorNo; - int commonErrorCode; - }; + typedef Common_posixErrnoTableEntry posixErrnoTableEntry; /*! - * const int posixErrorTranslationTableAPIVersion - * - * Defines the API version of the posix errno to - * common namespace error code translation table. - * - * If you change the table increment the version - * number. - */ - const int posixErrorTranslationTableAPIVersion = 1; - - /*! - * const Common::posixErrnoTableEntry Common::posixErrorTranslationTable[] - * - * A table that contains (some) POSIX errno codes and their - * Common namespace error code equilivants. + * const unsigned int Common::Get_Posix_Error_Translation_Table_API_Version() * - * Not all POSIX errno(s) are present in this table, as - * some POSIX errno(s) are specific to POSIX compliant - * systems. As such there is no Common namespace error - * code (beyond COMMON_SYSTEM_SPECIFIC) to represent them. + * Returns the API Version number of the Common_posixErrorTranslationTable + * array. */ - const posixErrnoTableEntry posixErrorTranslationTable[] = { - {EACCES, Common::COMMON_ACCESS_DENIED}, - {ENOENT, Common::FILEUTILLS_NON_EXISTANT}, - {EEXIST, Common::FILEUTILLS_EXISTANT}, - {EROFS, Common::FILEUTILLS_READ_ONLY}, - {EINVAL, Common::COMMON_INVALID_ARGUMENT}, - {ENAMETOOLONG, Common::FILEUTILLS_PATH_LENGTH_INVALID}, - {ENOTDIR, Common::FILEUTILLS_PATH_FILE_AS_DIRECTORY}, - {ENOMEM, Common::COMMON_MEMORY_ERROR}, - {EFAULT, Common::COMMON_INVALID_ARGUMENT}, - {ENOSPC, Common::FILEUTILLS_FILESYSTEM_FULL}, - {EDQUOT, Common::FILEUTILLS_FILESYSTEM_QUOTA_REACHED}, - }; + const unsigned int Get_Posix_Error_Translation_Table_API_Version(); /*! - * unsigned int Common::Get_Posix_Error_Translation_Table_Size() - * + * const unsigned int Common::Get_Posix_Error_Translation_Table_Size() + * + * (C++ Binding) + * * Returns the size of the Common::posixErrorTranslationTable * array. */ - unsigned int Get_Posix_Error_Translation_Table_Size(); + const unsigned int Get_Posix_Error_Translation_Table_Size(); /*! * int Common::Translate_Posix_Errno_To_Common_Error_Code(const int & err) - * + * + * (C++ Binding) + * * Translates the given POSIX errno to it's Common namespace * error code equilivant. (If applicable.) * @@ -106,6 +186,7 @@ namespace Common */ int Translate_Posix_Errno_To_Common_Error_Code(const int & err); }; +#endif // __cplusplus #endif // POSIX_ERROR_TRANSLATION_TABLE_H diff --git a/src/Common/Src/Error_Handler/Posix_Error_Translation_Table_CPP_Bindings.cpp b/src/Common/Src/Error_Handler/Posix_Error_Translation_Table_CPP_Bindings.cpp new file mode 100644 index 0000000..96c7adb --- /dev/null +++ b/src/Common/Src/Error_Handler/Posix_Error_Translation_Table_CPP_Bindings.cpp @@ -0,0 +1,45 @@ +/*! + Multiverse Engine Project 15/5/2015 Common Posix_Error_Translation_Table_CPP_Bindings.cpp + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// C++ Bindings. +#ifdef __cplusplus + +// Internal includes. +#include "Common_Error_Handler_Structures.h" +#include "Posix_Error_Translation_Table.h" + +// External includes. +#include + +const unsigned int Common::Get_Posix_Error_Translation_Table_API_Version() +{ + return Common_Get_Posix_Error_Translation_Table_API_Version(); +} + +const unsigned int Common::Get_Posix_Error_Translation_Table_Size() +{ + return Common_Get_Posix_Error_Translation_Table_Size(); +} + +int Common::Translate_Posix_Errno_To_Common_Error_Code(const int & err) +{ + return Common_Translate_Posix_Errno_To_Common_Error_Code(err); +} +#endif // __cplusplus diff --git a/src/Common/Src/Error_Handler/Windows_Error_Translation_Table.c b/src/Common/Src/Error_Handler/Windows_Error_Translation_Table.c new file mode 100644 index 0000000..77758ec --- /dev/null +++ b/src/Common/Src/Error_Handler/Windows_Error_Translation_Table.c @@ -0,0 +1,70 @@ +/*! + Multiverse Engine Project 17/1/2016 Common Windows_Error_Translation_Table.c + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Common_Error_Handler_Structures.h" +#include "Windows_Error_Translation_Table.h" + +/* Include Extern C if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +const unsigned int Common_Get_Windows_Error_Translation_Table_API_Version() +{ + return COMMON_WINDOWS_ERROR_TRANSLATION_TABLE_API_VERSION; +} + +const unsigned int Common_Get_Windows_Error_Translation_Table_Size() +{ + return (sizeof(Common_windowsErrorTranslationTable) / sizeof(Common_windowsErrorCodeTableEntry)); +} + +int Common_Translate_Windows_Error_Code_To_Common_Error_Code(const DWORD err) +{ + /* Init result. */ + int ret = COMMON_ERROR_SYSTEM_SPECIFIC; /* + * Default is COMMON_ERROR_SYSTEM_SPECIFIC, as + * not all Windows error codes are / can be + * reperesented in the Common namespace + * error code table. + */ + unsigned int x = 0; /* Loop counter */ + + /* Run loop. */ + for (x = 0; ((x < Common_Get_Windows_Error_Translation_Table_Size()) && + (ret == COMMON_ERROR_SYSTEM_SPECIFIC)); x++) + { + /* Check for a match in the error code translation table. */ + if (Common_windowsErrorTranslationTable[x].windowsErrorCode == err) + { + /* Match found, return the Common namespace error code. */ + ret = Common_windowsErrorTranslationTable[x].commonErrorCode; + } + } + + /* Return the result. */ + return ret; +} + +/* Terminate C linkage if needed. */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ diff --git a/src/Common/Src/Error_Handler/Windows_Error_Translation_Table.h b/src/Common/Src/Error_Handler/Windows_Error_Translation_Table.h new file mode 100644 index 0000000..a28a552 --- /dev/null +++ b/src/Common/Src/Error_Handler/Windows_Error_Translation_Table.h @@ -0,0 +1,189 @@ +/*! + Multiverse Engine Project 17/1/2016 Common Windows_Error_Translation_Table.h + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef WINDOWS_ERROR_TRANSLATION_TABLE_H +#define WINDOWS_ERROR_TRANSLATION_TABLE_H + +/* Include common error code definitions. */ +#include "Common_Error_Handler_Error_Codes.h" + +/* Include Windows headers. */ +#include +#include + +/* Enable C linkage if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*! + * #define COMMON_WINDOWS_ERROR_TRANSLATION_TABLE_API_VERSION + * + * Defines the API version of the Windows Error code to + * common namespace error code translation table. + * + * If you change the table increment the version + * number. + */ +#define COMMON_WINDOWS_ERROR_TRANSLATION_TABLE_API_VERSION 1 + +/*! + * typedef struct Common_windowsErrorCodeTableEntry_T Common_windowsErrorCodeTableEntry_T + * + * (C Binding.) + * + * Used to construct the error code translation table + * for Windows systems. + */ +typedef struct Common_windowsErrorCodeTableEntry_T { + int windowsErrorCode; + int commonErrorCode; +} Common_windowsErrorCodeTableEntry; + +/*! + * const static Common_windowsErrorCodeTableEntry Common_windowsErrorTranslationTable[] + * + * A table that contains (some) Windows error codes and their + * Common namespace error code equilivants. + * + * Not all Windows error codes are present in this table. + */ +const static Common_windowsErrorCodeTableEntry Common_windowsErrorTranslationTable[] = { + {ERROR_SUCCESS, COMMON_ERROR_SUCCESS}, + {ERROR_ACCESS_DENIED, COMMON_ERROR_ACCESS_DENIED}, + {ERROR_PATH_NOT_FOUND, FILEUTILLS_ERROR_NON_EXISTANT}, + {ERROR_FILE_NOT_FOUND, FILEUTILLS_ERROR_NON_EXISTANT}, + {ERROR_INVALID_HANDLE, COMMON_ERROR_INVALID_ARGUMENT}, + {ERROR_TOO_MANY_OPEN_FILES, COMMON_ERROR_MEMORY_ERROR}, + {ERROR_ARENA_TRASHED, COMMON_ERROR_MEMORY_ERROR}, + {ERROR_NOT_ENOUGH_MEMORY, COMMON_ERROR_MEMORY_ERROR}, + {ERROR_INVALID_BLOCK, COMMON_ERROR_INVALID_ARGUMENT}, + {ERROR_INVALID_ACCESS, COMMON_ERROR_ACCESS_DENIED}, + {ERROR_OUTOFMEMORY, COMMON_ERROR_MEMORY_ERROR}, + {ERROR_INVALID_DRIVE, FILEUTILLS_ERROR_NON_EXISTANT}, + {ERROR_CURRENT_DIRECTORY, COMMON_ERROR_PEBKAC_INVALID_OPERATION_ORDER}, + {ERROR_NO_MORE_FILES, COMMON_ERROR_END_OF_DATA}, + {ERROR_WRITE_PROTECT, FILEUTILLS_ERROR_READ_ONLY}, + {ERROR_BAD_LENGTH, COMMON_ERROR_INVALID_ARGUMENT}, + {ERROR_BAD_COMMAND, COMMON_ERROR_INVALID_ARGUMENT}, + {ERROR_WRITE_FAULT, COMMON_ERROR_IO_ERROR}, + {ERROR_READ_FAULT, COMMON_ERROR_IO_ERROR}, + {ERROR_HANDLE_DISK_FULL, FILEUTILLS_ERROR_FILESYSTEM_FULL}, + {ERROR_NOT_SUPPORTED, COMMON_ERROR_HOST_NOT_SUPPORTED}, + {ERROR_FILE_EXISTS, FILEUTILLS_ERROR_EXISTANT}, + {ERROR_CANNOT_MAKE, COMMON_ERROR_IO_ERROR}, + {ERROR_BUFFER_OVERFLOW, COMMON_ERROR_INVALID_ARGUMENT}, + {ERROR_DISK_FULL, FILEUTILLS_ERROR_FILESYSTEM_FULL}, + {ERROR_BAD_DRIVER_LEVEL, COMMON_ERROR_HOST_NOT_SUPPORTED}, + {ERROR_CALL_NOT_IMPLEMENTED, COMMON_ERROR_HOST_NOT_SUPPORTED}, + {ERROR_INSUFFICIENT_BUFFER, COMMON_ERROR_INVALID_ARGUMENT}, +}; + +/*! + * const unsigned int Common_Get_Windows_Error_Translation_Table_API_Version() + * + * Returns the API Version number of the Common_windowsErrorTranslationTable + * array. + */ +MSYS_DLL_EXPORT const unsigned int Common_Get_Windows_Error_Translation_Table_API_Version(); + +/*! + * const unsigned int Common_Get_Windows_Error_Translation_Table_Size() + * + * Returns the size of the Common_windowsErrorTranslationTable + * array. + */ +MSYS_DLL_EXPORT const unsigned int Common_Get_Windows_Error_Translation_Table_Size(); + +/*! + * int Common_Translate_Windows_Error_Code_To_Common_Error_Code(const DWORD err) + * + * Translates the given Windows error code to it's Common namespace + * error code equilivant. (If applicable.) + * + * @pram const DWORD err, the Windows error code to translate. + * + * Returns the translated Common namespace error code if applicable. + * + * Returns COMMON_SYSTEM_SPECIFIC if the given Windows error code does not have a + * Common namespace error code translation. + */ +MSYS_DLL_EXPORT int Common_Translate_Windows_Error_Code_To_Common_Error_Code(const DWORD err); + +/* End C Linkage if needed. */ +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +/* Check for a C compiler. */ +#ifdef __cplusplus +namespace Common +{ + /*! + * typedef Common_windowsErrorCodeTableEntry Common::windowsErrorCodeTableEntry + * + * (C++ Binding.) + * + * Used to construct the windows error code translation table. + * for Windows systems. + */ + typedef Common_windowsErrorCodeTableEntry windowsErrorCodeTableEntry; + + /*! + * const unsigned int Common::Get_Windows_Error_Translation_Table_API_Version() + * + * Returns the API Version number of the Common_windowsErrorTranslationTable + * array. + */ + MSYS_DLL_EXPORT const unsigned int Get_Windows_Error_Translation_Table_API_Version(); + + /*! + * const unsigned int Common::Get_Windows_Error_Translation_Table_Size() + * + * (C++ Binding) + * + * Returns the size of the Common::windowsErrorTranslationTable + * array. + */ + MSYS_DLL_EXPORT const unsigned int Get_Windows_Error_Translation_Table_Size(); + + /*! + * int Common::Translate_Windows_Error_Code_To_Common_Error_Code(const DWORD & err) + * + * (C++ Binding) + * + * Translates the given Windows error code to it's Common namespace + * error code equilivant. (If applicable.) + * + * @pram const DWORD & err, the Windows error code to translate. + * + * Returns the translated Common namespace error code if applicable. + * + * Returns COMMON_SYSTEM_SPECIFIC if the given Windows error code does not have a + * Common namespace error code translation. + */ + MSYS_DLL_EXPORT int Translate_Windows_Error_Code_To_Common_Error_Code(const DWORD & err); +}; +#endif /* __cplusplus */ + +#endif /* WINDOWS_ERROR_TRANSLATION_TABLE_H */ + +/* End of Windows_Error_Translation_Table.h. */ diff --git a/src/Common/Src/Error_Handler/Windows_Error_Translation_Table_CPP_Bindings.cpp b/src/Common/Src/Error_Handler/Windows_Error_Translation_Table_CPP_Bindings.cpp new file mode 100644 index 0000000..7e6b99b --- /dev/null +++ b/src/Common/Src/Error_Handler/Windows_Error_Translation_Table_CPP_Bindings.cpp @@ -0,0 +1,45 @@ +/*! + Multiverse Engine Project 17/1/2016 Common Windows_Error_Translation_Table_CPP_Bindings.cpp + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* C++ Bindings. */ +#ifdef __cplusplus + +/* Internal includes. */ +#include "Common_Error_Handler_Structures.h" +#include "Windows_Error_Translation_Table.h" + +/* External includes. */ +#include + +const unsigned int Common::Get_Windows_Error_Translation_Table_API_Version() +{ + return Common_Get_Windows_Error_Translation_Table_API_Version(); +} + +const unsigned int Common::Get_Windows_Error_Translation_Table_Size() +{ + return Common_Get_Windows_Error_Translation_Table_Size(); +} + +int Common::Translate_Windows_Error_Code_To_Common_Error_Code(const DWORD & err) +{ + return Common_Translate_Windows_Error_Code_To_Common_Error_Code(err); +} +#endif /* __cplusplus */ diff --git a/src/Common/Src/File_Management_Subsystem/CMakeLists.txt b/src/Common/Src/File_Management_Subsystem/CMakeLists.txt index d8dd7dd..b0035dd 100644 --- a/src/Common/Src/File_Management_Subsystem/CMakeLists.txt +++ b/src/Common/Src/File_Management_Subsystem/CMakeLists.txt @@ -2,7 +2,24 @@ set(LIBRARY_OUTPUT_PATH ${L_OUTPUT_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${O_OUTPUT_DIR}) -set(FILEUTILLS_INCLUDES FileUtills.cpp) +# Set default includes. +set(FILEUTILLS_C_INCLUDES FileUtills.c FileUtills_Private_API.c) +set(FILEUTILLS_CPP_INCLUDES FileUtills.cpp FileUtills_Private_API.cpp) + +# Set system specific includes. +IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + # Building on windows. + set(FILEUTILLS_INCLUDES ${FILEUTILLS_C_INCLUDES} ${FILEUTILLS_CPP_INCLUDES} FileUtills_Private_API_Windows_Syscall.cpp) + + ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + # Building on Linux. + set(FILEUTILLS_INCLUDES ${FILEUTILLS_C_INCLUDES} ${FILEUTILLS_CPP_INCLUDES} FileUtills_Private_API_Posix_Syscall.cpp) + + ELSE(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + # We don't have a match against a known system. + message(FATAL_ERROR "Attempting to build the File Management Subsystem with an unrecognized OS / System / Arch. Aborting build.") + +ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") add_library(File_Management_Multiverse_Engine SHARED ${FILEUTILLS_INCLUDES}) target_link_libraries(File_Management_Multiverse_Engine Common_Error_Handler_Multiverse_Engine) diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills.c b/src/Common/Src/File_Management_Subsystem/FileUtills.c new file mode 100644 index 0000000..c5a0180 --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills.c @@ -0,0 +1,1673 @@ +/*! + Multiverse Engine Project 17/12/2015 FileUtills FileUtills.c + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "FileUtills.h" +#include "FileUtills_Private_API.h" + +/* Define the MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID macro. */ +#define MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID MSYS_ERROR_LOG_CHANNEL_FILEUTILLS + +/*! + * int FileUtills_RemoveTrailingSlash(char ** retStr, size_t * retStrSize) + * + * Removes a DIR_SEP (Either a '\\' or '/' character) on the end of the given NULL terminated path string. + * + * WARNING: This function will DEALLOCATE the original string if it finds a character to remove. As such, + * if this call returns COMMON_ERROR_SUCCESS, any pointer to the original string will be INVALID. The + * result of attempting to dereference such a pointer is undefined. + * + * Returns COMMON_ERROR_SUCCESS if the removal was successful, or if there was not a character to remove. + * (The character at the end of the string was NOT a '\\' or '/' character.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers are NULL, or if the given size is less + * than 3. + * + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + * + * Otherwise returns the appropriate error code. + * + * In case of error, (the return code is NOT COMMON_ERROR_SUCCESS), this function will NOT alter any given + * arguments. + */ +int FileUtills_RemoveTrailingSlash(char ** retStr, size_t * retStrSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + char * tempBuffer = NULL; /* Temporary buffer to alter the string with. */ + + /* Check for invalid arguments. */ + if ((retStr != NULL) && ((*retStr) != NULL) && (retStrSize != NULL) && ((*retStrSize) > 2)) + { + /* Check and see if the last character in the string is either '/' or '\\'. */ + /* String should be NULL terminated. */ + if (((*retStr)[((*retStrSize) - 2)] == '\\') || ((*retStr)[((*retStrSize) - 2)] == '/')) + { + /* OK, allocate a new string. */ + tempBuffer = (char*)malloc(((*retStrSize) - 1)); + if (tempBuffer != NULL) + { + /* NULL out the buffer. */ + memset(tempBuffer, '\0', ((*retStrSize) - 1)); + + /* Copy the data. */ + memcpy(tempBuffer, retStr, ((*retStrSize) - 2)); + + /* Deallocate the original string. */ + free((*retStr)); + + /* Copy the new pointer. */ + (*retStr) = tempBuffer; + + /* Copy the new size. */ + (*retStrSize) = ((*retStrSize) - 1); + + /* Done! */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for new string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* There is no DIR_SEP character ('\\' or '/') to remove. */ + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* We can't remove anything, if we do the return would be empty. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +/*! + * int FileUtills_CheckPathType(const char * path, const size_t pathSize, bool * bIsDosStylePath) + * + * Checks the given path to see if it matches the DOS / Windows path format. + * (I.e. :\ OR \). + * The assumption being that if a '/' is found then it's a POSIX style path. + * + * Returns COMMON_ERROR_SUCCESS if the check was performed. + * (Result will be stored in the given bIsDosStylePath argument.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers are NULL, or the + * given path size is less than 1. + * + * Otherwise returns the appropriate error code. + * + * In case of error, (the return code is NOT COMMON_ERROR_SUCCESS), this function will NOT + * alter any given arguments. + * + * Under NO circumstance is the given path or pathSize argument altered in any way. + */ +int FileUtills_CheckPathType(const char * path, const size_t pathSize, bool * bIsDosStylePath) +{ + /* Init vars. */ + short ret = COMMON_ERROR_INTERNAL_ERROR; /* Result of this function. */ + size_t x = 0; /* Counter used in check loop. */ + + /* Check for valid arguments. */ + if ((path != NULL) && (pathSize > 0) && (bIsDosStylePath != NULL)) + { + /* Check for a DOS style absolute path reference. */ + if ((pathSize > 3) && (path[1] == ':') && (path[2] == '\\')) + { + /* This is DOS style absolute path. */ + (*bIsDosStylePath) = true; + } + else + { + /* Set bIsDosStylePath. */ + (*bIsDosStylePath) = true; + + /* + * We are assuming that if a '/' character is found + * in the path, it is a POSIX style path. + * + * (Probably a dangerous assumption, but there is not + * much of a way to determine the path type short of + * determining the OS in use. (And even then it's a + * problem if the target is different than the host.)) + * + * Hopefully the target OS will complain if it gets an + * invalid path...... + */ + for (x = 0; ((x < pathSize) && ((*bIsDosStylePath) == true)); x++) + { + /* Check for a '/' character. */ + if (path[x] == '/') + { + /* OK this is a reserved character on windows, so assume the path is a POSIX style path. */ + (*bIsDosStylePath) = false; + } + } + } + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid path. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Return the result. */ + return ret; +} + +void FileUtills_Deallocate_CString(char ** str) +{ + /* Check for valid args. */ + if ((str != NULL) && ((*str) != NULL)) + { + /* Deallocate the string. */ + DataProcess_Deallocate_CString(str); + } + + /* Exit function. */ + return; +} + +int FileUtills_Create_MSYS_FILESIZE_Structure(MSYS_FILESIZE_T ** str) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + struct MSYS_FILESIZE_PRIV * realStr = NULL; /* Temporary pointer for creation of structure. */ + + /* Check for invalid argument. */ + if (str != NULL) + { + /* Allocate memory. */ + realStr = (struct MSYS_FILESIZE_PRIV *)malloc(sizeof(struct MSYS_FILESIZE_PRIV)); + if (realStr != NULL) + { + /* Blank out the structure. */ + memset(realStr, '\0', sizeof(struct MSYS_FILESIZE_PRIV)); + + /* Set the type. */ +#ifdef _MSC_VER + realStr->type = WINDOWS_FILESIZE_TYPE; +#else + realStr->type = POSIX_FILESIZE_TYPE; +#endif /* _MSC_VER */ + + /* Copy the pointer. */ + (*str) = (struct MSYS_FILESIZE *)realStr; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for structure. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +void FileUtills_Destroy_MSYS_FILESIZE_Structure(MSYS_FILESIZE_T ** str) +{ + /* Check for invalid argument. */ + if ((str != NULL) && ((*str) != NULL)) + { + /* Deallocate the structure. */ + free((*str)); + (*str) = NULL; + } + + /* Exit function. */ + return; +} + +int FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT(const MSYS_FILESIZE_T * str, long long int * retVal) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + const struct MSYS_FILESIZE_PRIV * realStr = NULL; /* Temporary pointer for dereferencing of structure. */ + + /* Check for invalid argument. */ + if ((str != NULL) && (retVal != NULL)) + { + /* Reinterpret the struct. */ + realStr = (const struct MSYS_FILESIZE_PRIV *)str; + + /* Set retVal. */ + (*retVal) = realStr->length; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int FileUtills_Set_Length_From_MSYS_FILESIZE_Structure_LLINT(MSYS_FILESIZE_T * str, const long long int * val) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + struct MSYS_FILESIZE_PRIV * realStr = NULL; /* Temporary pointer for dereferencing of structure. */ + + /* Check for invalid argument. */ + if ((str != NULL) && (val != NULL)) + { + /* Reinterpret the struct. */ + realStr = (struct MSYS_FILESIZE_PRIV *)str; + + /* Set length. */ + realStr->length = (*val); + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +void FileUtills_Destroy_FileUtills_dirlist_Structure(FileUtills_dirlist_T ** dirList) +{ + /* Check for valid args. */ + if ((dirList != NULL) && ((*dirList) != NULL)) + { + /* Check and see if the member pointer is NULL. */ + if ((*dirList)->pointer != NULL) + { + /* Call private object's destructor. */ + FileUtills_Destroy_dirList_PRIV_Object(((*dirList)->pointer)); + } + + /* Destroy our object. */ + free((*dirList)); + (*dirList) = NULL; + } + + /* Exit function. */ + return; +} + +int FileUtills_Get_File_Length_By_Filename(const char * filename, const size_t filenameSize, MSYS_FILESIZE_T * fileLength) +{ + /* Init vars. */ + int retFromC = 0; /* The result from C calls. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + long long int tempSize = 0; /* The size of the file returned from FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT(). */ + FILE * fp = NULL; /* Pointer to the file. */ + MSYS_FILESIZE_T * fileSize = NULL; /* Returned size from Get_File_Length(). */ + + /* Check for invalid arguments. */ + if ((filename != NULL) && (filenameSize > 0) && (fileLength != NULL)) + { + /* Allocate memory for the size structure. */ + ret = FileUtills_Create_MSYS_FILESIZE_Structure(&fileSize); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Open the file. */ + fp = fopen(filename, "rb"); + if (fp != NULL) + { + /* Call correct function. */ + ret = FileUtills_Get_File_Length(fp, fileSize); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Close the file. */ + retFromC = fclose(fp); + if (retFromC == 0) + { + /* Get the size. */ + retFromCall = FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT(fileSize, &tempSize); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Set the size. */ + retFromCall = FileUtills_Set_Length_From_MSYS_FILESIZE_Structure_LLINT(fileSize, &tempSize); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* SUCCESS. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not set size in structure. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_Get_File_Length_By_Filename(): Could not set size in structure."); + } + } + else + { + /* Could not get size from structure. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_Get_File_Length_By_Filename(): Could not get size from structure."); + } + } + else + { + /* Could not close the file. */ + ret = COMMON_ERROR_IO_ERROR; + } + } + else + { + /* Close the file. */ + retFromC = fclose(fp); + if (retFromC != 0) + { + /* Could not close the file. */ + ret = COMMON_ERROR_IO_ERROR; + } + } + } + else + { + /* Could not open file. */ + ret = COMMON_ERROR_IO_ERROR; + } + + /* Deallocate the fileSize structure. */ + FileUtills_Destroy_MSYS_FILESIZE_Structure(&fileSize); + } + else + { + /* Could not allocate memory for the fileSize structure. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int FileUtills_Get_File_Length(FILE * fp, MSYS_FILESIZE_T * fileLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromC = 0; /* The result of C calls. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of a call to an engine function. */ + fpos_t * previousLocation = NULL; /* The location to restore us to at the end of the function. */ + long long int retFromGetPos = 0; /* Result from the various calls to ftell*(). */ + + /* Check for invalid arguments. */ + if ((fp != NULL) && (fileLength != NULL)) + { + /* Check for errors. */ + retFromC = ferror(fp); + if (retFromC == 0) + { + /* Allocate memory for fpos_t. */ + previousLocation = (fpos_t *)malloc(sizeof(fpos_t)); + if (previousLocation != NULL) + { + /* NULL out the previousLocation buffer. */ + memset(previousLocation, '\0', sizeof(fpos_t)); + + /* Set previousLocation to a known value. */ + (*previousLocation) = 0; + + /* Get the current position. */ + retFromC = fgetpos(fp, previousLocation); + if (retFromC == 0) + { + /* Go back to the beginining of the file. */ + rewind(fp); + + /* Begin loop to find the end of the file. */ + while ((ferror(fp) == 0) && (feof(fp) == 0)) + { + /* Get next char. */ + retFromC = fgetc(fp); + } + + /* OK, now figure out if we hit the end of the file or if we hit an error. */ + retFromC = ferror(fp); + if (retFromC == 0) /* No error. */ + { + /* Check for eof. */ + retFromC = feof(fp); + if (retFromC != 0) /* Hit EOF. */ + { + /* Get the end of file position. */ +#ifdef _MSC_VER + retFromGetPos = _ftelli64(fp); /* Visual C is special. */ +#else + retFromGetPos = ftello(fp); +#endif /* _MSC_VER */ + if ((retFromGetPos == -1) || (retFromGetPos < 0)) + { +/* _ftelli64() in MSC does not return an EOVERFLOW errno status. + Actually, it has no way to indicate (as of 19/12/2015 anyway...) + that the file size is too big to store in the return variable. + + This will probably change one day, so if if / when it does, + just add the detection code here and return COMMON_ERROR_MEMORY_ERROR. +*/ +#ifndef _MSC_VER + /* Check and see if the error is EOVERFLOW. */ + if ((retFromGetPos == -1) && (errno == EOVERFLOW)) + { + /* This is a memory error, as we can't store the result. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + else + { +#endif /* _MSC_VER */ + /* OK, yet another IO_ERROR. */ + ret = COMMON_ERROR_IO_ERROR; +#ifndef _MSC_VER + } +#endif /* _MSC_VER */ + } + } + else + { + /* We hit a file stream error. */ + ret = COMMON_ERROR_IO_ERROR; + } + } + else + { + /* We hit a file stream error. */ + ret = COMMON_ERROR_IO_ERROR; + } + + /* Clear the error status, and reset the file position. */ + clearerr(fp); + retFromC = fsetpos(fp, previousLocation); + if ((retFromC == 0) && (retFromGetPos >= 0)) + { + /* Copy the length to the given MSYS_FILESIZE structure. */ + retFromCall = FileUtills_Set_Length_From_MSYS_FILESIZE_Structure_LLINT(fileLength, &retFromGetPos); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Set success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Internal engine error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_Get_File_Length(): Could not copy file length to management structure."); + } + } + else + { + /* File stream error. */ + ret = COMMON_ERROR_IO_ERROR; + } + } + else + { + /* Could not get current file position. */ + ret = COMMON_ERROR_IO_ERROR; + } + + /* Free previousLocation. */ + free(previousLocation); + previousLocation = NULL; + } + else + { + /* Could not allocate memory for fpos_t buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* File stream has errored out. */ + ret = COMMON_ERROR_IO_ERROR; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int FileUtills_Read_Bytes_From_File(FILE * IN, const size_t dataLength, char * dataBuf, const size_t dataBufLength, const size_t destStaringOffset, const bool blankDataBuf) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromC = 0; /* The result of C calls. */ + size_t x = 0; /* Counter in for loop. */ + + /* Check for invalid arguments. */ + if ((IN != NULL) && (!ferror(IN)) && (!feof(IN)) && (dataBuf != NULL) && (dataBufLength > 0) && (dataLength > 0) && ((destStaringOffset + dataLength) < dataBufLength)) + { + /* Blank out dataBuf with NULL bytes if needed. */ + if (blankDataBuf) + { + memset(dataBuf, '\0', dataBufLength); + } + + /* Begin data input loop. */ + for (x = 0; ((x < dataLength) && ((destStaringOffset + x) < dataBufLength) && (!ferror(IN)) && (!feof(IN))); x++) + { + /* Get the data. */ + retFromC = fgetc(IN); + + /* Check for EOF. */ + if (retFromC == EOF) + { + /* Check for actual EOF or error. */ + if ((!(feof(IN))) && (!(ferror(IN)))) + { + /* The input value is (0xFF) which just so happens to be EOF, + but it is not an error. So copy the value. + */ + dataBuf[x] = retFromC; + } + } + else + { + /* Copy data to dataBuf. */ + dataBuf[x] = retFromC; + } + } + + /* Check for success. */ + if ((!ferror(IN)) && (x == dataLength)) + { + /* Data read successfully. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + if (feof(IN)) + { + /* End of file. */ + ret = COMMON_ERROR_END_OF_DATA; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Read_Bytes_From_File(): Reached end of file before reading the given amount of data."); + } + else + { + /* Bad file stream. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Read_Bytes_From_File(): Input file handle has failed."); + } + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Read_Bytes_From_File(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Exit function. */ + return ret; +} + +int FileUtills_Write_Data_To_File_From_File(FILE * OUT, const char * filename, const size_t filenameLength, const MSYS_FILESIZE_T * fileStartingOffset, const size_t dataLength) +{ +/* Define IO_BUF_SIZE for this function if needed. */ +#ifndef IO_BUF_SIZE +#define IO_BUF_SIZE 512 +#define IO_BUF_SIZE_UNDEF 1 /* Used to undef this later if needed. */ +#endif /* IO_BUF_SIZE */ + + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + int retFromC = 0; /* The result of C calls. */ + MSYS_FILESIZE_T * inFileLength = NULL; /* The size of the input file. */ + FILE * IN = NULL; /* The input file. */ + char * inputBuf = NULL; /* Memory buffer used for reading in data from a file. NOT on the stack! Bad input can follow! */ + size_t remainingLength = 0; /* Used to calculate remaining bytes to write in output loop. */ + size_t x = 0; /* Counter used in I/O loop. */ + size_t y = 0; /* Counter used in Input Loop and Output Loop. */ + long long int realStartOffset = 0; /* The position to start writing data into the file at. */ + long long int realFileLengthlli = 0; /* The length of the input file as a lli. */ + + /* Check for invalid arguments. */ + if ((OUT != NULL) && (ferror(OUT) == 0) && (filename != NULL) && (filenameLength > 0) && (fileStartingOffset != NULL) && (dataLength > 0)) + { + /* Check for valid file starting offset. */ + retFromCall = FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT(fileStartingOffset, &realStartOffset); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (realStartOffset > 0)) + { + /* Allocate memory for the filesize structures. */ + ret = FileUtills_Create_MSYS_FILESIZE_Structure(&inFileLength); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Open the input file. */ + IN = fopen(filename, "rb"); + if (IN != NULL) + { + /* Get the length of the input file. */ + ret = FileUtills_Get_File_Length(IN, inFileLength); + + /* Make sure we got a valid file length and that the length of the file is big enough to store the data we want. */ + if ((ret == COMMON_ERROR_SUCCESS) && (inFileLength != NULL)) + { + /* Get the length of the file. */ + retFromCall = FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT(inFileLength, &realFileLengthlli); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (realFileLengthlli > 0) && + ((((unsigned)(realStartOffset)) + dataLength) <= ((unsigned)(realFileLengthlli)))) + { + /* Reset ret. */ + ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Skip to the starting offset. */ +#ifdef _MSC_VER /* VC is special */ + retFromC = _fseeki64(IN, realStartOffset, SEEK_SET); +#else + retFromC = fseeko64(IN, realStartOffset, SEEK_SET); +#endif /* _MSC_VER */ + if (retFromC == 0) + { + /* Allocate input buffer. */ + inputBuf = (char*)malloc(IO_BUF_SIZE); + if (inputBuf != NULL) + { + /* Begin I/O loop. */ + for (x = 0; ((x < dataLength) && (!ferror(IN)) && (!feof(IN)) && (!ferror(OUT)));) + { + /* Determine if the remaining data to read in is less than the buffer size. */ + remainingLength = (dataLength - x); + if (remainingLength >= IO_BUF_SIZE) + { + /* Set remaining length to the buffer size. */ + remainingLength = IO_BUF_SIZE; + } + + /* Begin input loop. */ + for (y = 0; ((y < remainingLength) && (!ferror(IN)) && (!feof(IN))); y++) + { + /* Read in the remaining data. */ + retFromC = fgetc(IN); + inputBuf[y] = retFromC; + } + + /* Check for successful read. */ + if ((!ferror(IN)) && (!feof(IN))) + { + /* OK, begin the output loop. */ + for (y = 0; ((y < remainingLength) && (!ferror(OUT))); y++) + { + /* Output the current buffer. */ + fputc(inputBuf[y], OUT); + } + + /* Check for successful output. */ + if (!ferror(OUT)) + { + /* OK, flush the buffer. */ + retFromC = fflush(OUT); + if (retFromC == 0) + { + /* OK, add the remaining amount to x. */ + x = (x + remainingLength); + } + else + { + /* Could not flush the buffer. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not flush output buffer data to output file.\n"); + } + } + else + { + /* Bad output file stream. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not write data to output file.\n"); + } + } + else + { + /* Bad input file stream. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not read data from input file.\n"); + } + } + + /* Deallocate the input memory buffer. */ + if (inputBuf != NULL) + { + free(inputBuf); + inputBuf = NULL; + } + + /* Check for success. */ + if ((ret == COMMON_ERROR_UNKNOWN_ERROR) && (!ferror(IN)) && (!feof(IN)) && (!ferror(OUT))) + { + /* Success! */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Check for and log IO errors. */ + if (ret == COMMON_ERROR_IO_ERROR) + { + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): File I/O Error.\n"); + } + } + } + else + { + /* Could not allocate memory for input buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not allocate memory for input buffer.\n"); + } + } + else + { + /* Bad file stream. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not seek to starting offset.\n"); + } + } + else + { + /* Invalid input file. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not get input file length or input file is empty.\n"); + } + + /* Close the input file. */ + retFromC = fclose(IN); + if ((retFromC != 0) && (ret != COMMON_ERROR_IO_ERROR)) + { + /* Could not close the input file. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not close input file ( "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, filename); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ).\n"); + } + } + else + { + /* Could not get length of the file. */ + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT() Call errored out. */ + ret = retFromCall; + } + else + { + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_Write_Data_To_File_From_File(): Could not get length of file. FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT() returned success without result."); + } + } + } + else + { + /* Could not open input file. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not open input file ( "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, filename); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ).\n"); + } + + /* Deallocate inFileLength. */ + FileUtills_Destroy_MSYS_FILESIZE_Structure(&inFileLength); + } + else + { + /* Could not allocate memory for the inFileLength structure. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Could not allocate memory for MSYS_FILESIZE structure.\n"); + } + } + else + { + /* Either we have an invalid file starting offset or we could not get it from the file. */ + ret = ((retFromCall != COMMON_ERROR_SUCCESS) ? (COMMON_ERROR_INTERNAL_ERROR) : (COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, ((retFromCall != COMMON_ERROR_SUCCESS) ? + ("FileUtills_Write_Data_To_File_From_File(): Could not get starting offset from management structure.") : + ("FileUtills_Write_Data_To_File_From_File(): Invalid file starting offset."))); + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_File(): Invalid argument.\n"); + } + + /* Exit function. */ + return ret; + +/* Undef IO_BUF_SIZE if we defined it. */ +#ifdef IO_BUF_SIZE_UNDEF +#undef IO_BUF_SIZE +#undef IO_BUF_SIZE_UNDEF +#endif /* IO_BUF_SIZE_UNDEF */ +} + +int FileUtills_Write_Data_To_File_From_Memory(FILE * OUT, const char * data, const size_t dataLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromC = 0; /* The result of C calls. */ + size_t x = 0; /* Counter used in output loop. */ + + /* Check for invalid arguments. */ + if ((OUT != NULL) && (!ferror(OUT)) && (data != NULL) && (dataLength > 0)) + { + /* Begin output loop. */ + for (x = 0; ((x < dataLength) && (!ferror(OUT))); x++) + { + /* Write out the data. */ + fputc(data[x], OUT); + } + + /* Check for good file stream. */ + if (!ferror(OUT)) + { + /* Flush the buffer. */ + retFromC = fflush(OUT); + if ((retFromC == 0) && (!ferror(OUT))) + { + /* Done! */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Bad file stream. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_Memory(): Could not flush remaining output file data to disk.\n"); + } + } + else + { + /* Bad file stream. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_Memory(): Could not write data to file.\n"); + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + + /* Log error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "DEBUG: FileUtills_Write_Data_To_File_From_Memory(): Invalid argument.\n"); + } + + /* Exit function. */ + return ret; +} + +int FileUtills_Get_Last_Path_Component(char ** retStr, size_t * retStrLength, const int getParent) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + char * cleanedPath = NULL; /* Path without a trailing PATH_SEP. */ + char * tempSubStr = NULL; /* Used to create the substr. */ + size_t tempSubStrLength = 0; /* Size of the tempSubString. */ + size_t cleanedPathLength = 0; /* Length of the cleanedPath string. */ + + /* Check for invalid arguments. */ + if ((retStr != NULL) && ((*retStr) != NULL) && (retStrLength != NULL) && ((*retStrLength) > 0)) + { + /* Allocate memory for the cleanedPath string. */ + cleanedPathLength = (*retStrLength); + cleanedPath = (char *)malloc(cleanedPathLength); + if (cleanedPath != NULL) + { + /* NULL out the cleaned path string. */ + memset(cleanedPath, '\0', cleanedPathLength); + + /* Copy the given path to cleanedPath. */ + memcpy(cleanedPath, (*retStr), cleanedPathLength); + + /* Call FileUtills_RemoveTrailingSlash(). (Path had to be copied as this function will reallocate the path if it needs to.) */ + ret = FileUtills_RemoveTrailingSlash(&cleanedPath, &cleanedPathLength); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Call DataProcess_Get_SubString_Using_Delimiter(). */ + ret = DataProcess_Get_SubString_Using_Delimiter(cleanedPath, cleanedPathLength, DIR_SEP_STR, + /* (Note: DIR_SEP_STR is NULL terminated, so we are subtracting one from the length of the delimiter.) */ + ((DIR_SEP_STR[(sizeof(DIR_SEP_STR) - 1)] == '\0') ? (sizeof(DIR_SEP_STR) - 1) : (sizeof(DIR_SEP_STR))), + /* If getParent is non-zero then we need to return the parent path, otherwise we need to only return the last path component. */ + &tempSubStr, &tempSubStrLength, true, (getParent ? true : false)); + if ((ret == COMMON_ERROR_SUCCESS) && (tempSubStr != NULL) && (tempSubStrLength > 0)) + { + /* Copy the pointer and length. */ + (*retStr) = tempSubStr; + (*retStrLength) = tempSubStrLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Change the error code to internal error if it's not range or end of data. */ + if ((ret != COMMON_ERROR_RANGE_ERROR) && (ret != COMMON_ERROR_END_OF_DATA)) + { + /* Internal error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + } + + /* Deallocate the tempSubStr if needed. */ + if (tempSubStr != NULL) + { + DataProcess_Deallocate_CString(&tempSubStr); + } + } + } + else + { + /* Call to FileUtills_RemoveTrailingSlash() failed. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + } + + /* Deallocate the cleanedPath string. */ + free(cleanedPath); + cleanedPath = NULL; + cleanedPathLength = 0; + } + else + { + /* Could not allocate memory for cleanedPath. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid argument(s). */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int FileUtills_Get_File_Name_Component(const char * path, const size_t pathLength, char ** retStr, size_t * retStrLength, const int getExtension) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + char * tempFilename = NULL; /* Used to create the filename. */ + char * tempSubStr = NULL; /* Used to create the substr. */ + size_t tempFilenameLength = 0; /* Size of the tempFilename string. */ + size_t tempSubStrLength = 0; /* Size of the tempSubString. */ + + /* Check for invalid arguments. */ + if ((path != NULL) && (pathLength > 0) && (retStr != NULL) && (retStrLength != NULL)) + { + /* Call DataProcess_Get_SubString_Using_Delimiter() to get the filename. */ + ret = DataProcess_Get_SubString_Using_Delimiter(path, pathLength, DIR_SEP_STR, + /* (Note: DIR_SEP_STR is NULL terminated, so we are subtracting one from the length of the delimiter.) */ + ((DIR_SEP_STR[(sizeof(DIR_SEP_STR) - 1)] == '\0') ? (sizeof(DIR_SEP_STR) - 1) : (sizeof(DIR_SEP_STR))), + &tempFilename, &tempFilenameLength, true, false); + if ((ret == COMMON_ERROR_SUCCESS) && (tempFilename != NULL) && (tempFilenameLength > 0)) + { + /* Call DataProcess_Get_SubString_Using_Delimiter() to get the file name (if getExtension is zero) / extension. (if getExtension is non-zero) */ + ret = DataProcess_Get_SubString_Using_Delimiter(tempFilename, tempFilenameLength, FILEEXT_SEP_STR, + /* (Note: FILEEXT_SEP_STR is NULL terminated, so we are subtracting one from the length of the delimiter.) */ + ((FILEEXT_SEP_STR[(sizeof(FILEEXT_SEP_STR) - 1)] == '\0') ? (sizeof(FILEEXT_SEP_STR) - 1) : (sizeof(FILEEXT_SEP_STR))), + &tempSubStr, &tempSubStrLength, true, (getExtension ? false : true)); + if ((ret == COMMON_ERROR_SUCCESS) && (tempSubStr != NULL) && (tempSubStrLength > 0)) + { + /* Copy the pointer and length. */ + (*retStr) = tempSubStr; + (*retStrLength) = tempSubStrLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Change the error code to internal error if it's not range or end of data. */ + if ((ret != COMMON_ERROR_RANGE_ERROR) && (ret != COMMON_ERROR_END_OF_DATA)) + { + /* Internal error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + } + + /* Deallocate the tempSubStr if needed. */ + if (tempSubStr != NULL) + { + DataProcess_Deallocate_CString(&tempSubStr); + } + } + + /* Deallocate the temporary filename string. */ + DataProcess_Deallocate_CString(&tempFilename); + } + else + { + /* Check for a path without a directory component. */ + if (ret == COMMON_ERROR_RANGE_ERROR) + { + /* There is not a directory component in this path, so just check for the file name (if getExtension is zero) / extension. (if getExtension is non-zero) */ + ret = DataProcess_Get_SubString_Using_Delimiter(path, pathLength, FILEEXT_SEP_STR, + /* (Note: FILEEXT_SEP_STR is NULL terminated, so we are subtracting one from the length of the delimiter.) */ + ((FILEEXT_SEP_STR[(sizeof(FILEEXT_SEP_STR) - 1)] == '\0') ? (sizeof(FILEEXT_SEP_STR) - 1) : (sizeof(FILEEXT_SEP_STR))), + &tempSubStr, &tempSubStrLength, true, (getExtension ? false : true)); + if ((ret == COMMON_ERROR_SUCCESS) && (tempSubStr != NULL) && (tempSubStrLength > 0)) + { + /* Copy the pointer and length. */ + (*retStr) = tempSubStr; + (*retStrLength) = tempSubStrLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Change the error code to internal error if it's not range or end of data. */ + if ((ret != COMMON_ERROR_RANGE_ERROR) && (ret != COMMON_ERROR_END_OF_DATA)) + { + /* Internal error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + } + + /* Deallocate the tempSubStr if needed. */ + if (tempSubStr != NULL) + { + DataProcess_Deallocate_CString(&tempSubStr); + } + } + } + else + { + /* Change the error code to internal error if it's not a range error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + } + + /* Deallocate the tempFilename if needed. */ + if (tempFilename != NULL) + { + DataProcess_Deallocate_CString(&tempFilename); + } + } + } + else + { + /* Invalid argument(s). */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int FileUtills_GetUserProfileDirectoryPath(char ** retStr, size_t * retStrSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of an engine call. */ + char * outputStr = NULL; /* Pointer used to reallocate the string with our own allocator. */ + char * tempStr = NULL; /* Temporary pointer used to get the path from the host. */ + size_t tempStrLength = 0; /* Length of the temp string. */ + + /* Call the syscall. */ + retFromCall = FileUtills_GetUserProfileDirectoryPath_Syscall(&tempStr, &tempStrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempStr != NULL) && (tempStrLength > 0)) + { + /* Allocate memory so we can reallocate the string with our own allocator. */ + retFromCall = DataProcess_Reallocate_C_String(&outputStr, 0, tempStrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Copy the data. */ + memcpy(outputStr, tempStr, tempStrLength); + + /* Copy the pointer and length. */ + (*retStr) = outputStr; + (*retStrSize) = tempStrLength; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for output string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetUserProfileDirectoryPath(): Could not allocate memory for output string."); + } + } + else + { + /* Set internal error. (Unless it was a permissions error.) */ + ret = ((retFromCall != COMMON_ERROR_ACCESS_DENIED) ? (COMMON_ERROR_INTERNAL_ERROR) : (COMMON_ERROR_ACCESS_DENIED)); + + /* Check for a INVALID_ARGUMENT. */ + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* Log this error. */ + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetUserProfileDirectoryPath(): Syscall returned an invalid argument error. Please report this bug.\n"); + } + else + { + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetUserProfileDirectoryPath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "\n"); + } + } + + /* Check for allocated strings. */ + if (tempStr != NULL) + { + /* Call Syscall deallocator. */ + FileUtills_Deallocate_CString_Syscall(&tempStr); + } + if ((ret != COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Call our deallocator. */ + DataProcess_Deallocate_CString(&outputStr); + } + + /* Return the result. */ + return ret; +} + +int FileUtills_GetCurrentWorkingDirectoryPath(char ** retStr, size_t * retStrSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of an engine call. */ + char * outputStr = NULL; /* Pointer used to reallocate the string with our own allocator. */ + char * tempStr = NULL; /* Temporary pointer used to get the path from the host. */ + size_t tempStrLength = 0; /* Length of the temp string. */ + + /* Call the syscall. */ + retFromCall = FileUtills_GetCurrentWorkingDirectoryPath_Syscall(&tempStr, &tempStrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempStr != NULL) && (tempStrLength > 0)) + { + /* Allocate memory so we can reallocate the string with our own allocator. */ + retFromCall = DataProcess_Reallocate_C_String(&outputStr, 0, tempStrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Copy the data. */ + memcpy(outputStr, tempStr, tempStrLength); + + /* Copy the pointer and length. */ + (*retStr) = outputStr; + (*retStrSize) = tempStrLength; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for output string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetCurrentWorkingDirectoryPath(): Could not allocate memory for output string."); + } + } + else + { + /* Set internal error. (Unless it was a permissions error.) */ + ret = ((retFromCall != COMMON_ERROR_ACCESS_DENIED) ? (COMMON_ERROR_INTERNAL_ERROR) : (COMMON_ERROR_ACCESS_DENIED)); + + /* Check for a INVALID_ARGUMENT. */ + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* Log this error. */ + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetCurrentWorkingDirectoryPath(): Syscall returned an invalid argument error. Please report this bug.\n"); + } + else + { + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetCurrentWorkingDirectoryPath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "\n"); + } + } + + /* Check for allocated strings. */ + if (tempStr != NULL) + { + /* Call Syscall deallocator. */ + FileUtills_Deallocate_CString_Syscall(&tempStr); + } + if ((ret != COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Call our deallocator. */ + DataProcess_Deallocate_CString(&outputStr); + } + + /* Return the result. */ + return ret; +} + +int FileUtills_GetExecDirectory(char ** retStr, size_t * retStrSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of an engine call. */ + char * outputStr = NULL; /* Pointer used to reallocate the string with our own allocator. */ + char * tempStr = NULL; /* Temporary pointer used to get the path from the host. */ + size_t tempStrLength = 0; /* Length of the temp string. */ + + /* Call the syscall. */ + retFromCall = FileUtills_GetExecDirectory_Syscall(&tempStr, &tempStrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempStr != NULL) && (tempStrLength > 0)) + { + /* Allocate memory so we can reallocate the string with our own allocator. */ + retFromCall = DataProcess_Reallocate_C_String(&outputStr, 0, tempStrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Copy the data. */ + memcpy(outputStr, tempStr, tempStrLength); + + /* Copy the pointer and length. */ + (*retStr) = outputStr; + (*retStrSize) = tempStrLength; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for output string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): Could not allocate memory for output string."); + } + } + else + { + /* Set internal error. (Unless it was a permissions error.) */ + ret = ((retFromCall != COMMON_ERROR_ACCESS_DENIED) ? (COMMON_ERROR_INTERNAL_ERROR) : (COMMON_ERROR_ACCESS_DENIED)); + + /* Check for a INVALID_ARGUMENT. */ + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* Log this error. */ + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): Syscall returned an invalid argument error. Please report this bug.\n"); + } + else + { + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "\n"); + } + } + + /* Check for allocated strings. */ + if (tempStr != NULL) + { + /* Call Syscall deallocator. */ + FileUtills_Deallocate_CString_Syscall(&tempStr); + } + if ((ret != COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Call our deallocator. */ + DataProcess_Deallocate_CString(&outputStr); + } + + /* Return the result. */ + return ret; +} + +int FileUtills_ResolvePath(const char * path, const size_t pathSize, char ** retStr, size_t * retStrSize, const bool disableSymLinkResolution) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + size_t numOfResolveAttempts = 0; /* The number of times we have attempted to resolve a symbolic link. */ + size_t outputStrSize = 0; /* The current size of the output string. */ + size_t tempLinkBufSize = 0; /* The current size of the tempLinkBuf. */ + size_t x = 0; /* Loop counter. */ + char * outputStr = NULL; /* The resolved path. */ + char * tempLinkBuf = NULL; /* Used to hold the result of FileUtills_ResolveSystemSymoblicLink_Syscall(). */ + + /* Check for valid path. */ + if ((path != NULL) && (pathSize > 0) && (retStr != NULL) && (retStrSize != NULL)) + { + /* Allocate buffer for outputStr. */ + retFromCall = DataProcess_Reallocate_C_String(&outputStr, 0, pathSize); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Copy path to outputStr. */ + for (outputStrSize = 0; ((ret == COMMON_ERROR_UNKNOWN_ERROR) && (outputStrSize < pathSize)); outputStrSize++) + { + /* Check for a NULL that's before the end of the buffer. */ + if ((path[outputStrSize] == '\0') && ((outputStrSize + 1) != pathSize)) + { + /* Invalid path string. (NULL should be at the end of the buffer.) */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " given path argument has a NULL character before the end of the buffer."); + } + else + { + /* Copy the data. */ + outputStr[outputStrSize] = path[outputStrSize]; + } + } + + /* Check for success. */ + if ((pathSize == outputStrSize) && (ret == COMMON_ERROR_UNKNOWN_ERROR)) + { + /* Begin resolution loop. */ + for (numOfResolveAttempts = 0; ((retFromCall == FILEUTILLS_ERROR_PATH_IS_A_SYMLINK) && (numOfResolveAttempts < (FileUtills_Get_Max_Symlink_Depth_Syscall()))); numOfResolveAttempts++) + { + /* Resolve the given path. */ + retFromCall = FileUtills_ResolvePath_Helper(&outputStr, &outputStrSize); + + /* Check for error. */ + if ((retFromCall == COMMON_ERROR_SUCCESS) && (outputStr != NULL) && (outputStrSize > 0)) + { + /* Check for valid result. */ + if ((outputStr != NULL) && (outputStrSize > 0)) + { + /* Check to see if the path is a symbolic link. */ + retFromCall = FileUtills_IsFileOrDirectory_Helper(outputStr, outputStrSize); + + /* Check the result. */ + switch (retFromCall) + { + case COMMON_ERROR_SUCCESS: /* System specific (unchecked) filesystem entry type. */ + case FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY: + case FILEUTILLS_ERROR_PATH_IS_A_FILE: + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + break; + case FILEUTILLS_ERROR_PATH_IS_A_SYMLINK: + /* Check and see if we are resolving symbolic links. */ + if (!disableSymLinkResolution) + { + /* Copy the pointers. */ + tempLinkBuf = outputStr; + tempLinkBufSize = outputStrSize; + + /* OK, Resolve the symbolic link. */ + retFromCall = FileUtills_ResolveSystemSymoblicLink_Syscall(&tempLinkBuf, &tempLinkBufSize); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempLinkBuf != NULL) && (tempLinkBufSize > 0)) + { + /* OK, we need to determine if the resolved symbolic link is a relative path or an absolute path. */ + retFromCall = FileUtills_IsAbsolutePathReference(tempLinkBuf, tempLinkBufSize); + + /* Check for relative path. (We don't need to do anything with an absolute path.) */ + if (retFromCall == FILEUTILLS_ERROR_PATH_IS_RELATIVE) + { + /* Remove the symlink from the outputStr. */ + retFromCall = FileUtills_RemoveLastPathSegment(&tempLinkBuf, &tempLinkBufSize); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Reallocate the buffer to store the relative path. */ + retFromCall = DataProcess_Reallocate_C_String(&outputStr, outputStrSize, (outputStrSize + tempLinkBufSize)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Reset retFromCall. */ + retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + + /* Append the relative path to the result. */ + for (x = outputStrSize; ((x < (outputStrSize + tempLinkBufSize)) && (ret == COMMON_ERROR_UNKNOWN_ERROR)); x++) + { + /* Check for NULL. */ + if ((tempLinkBuf[x] == '\0') && ((x + 1) != (outputStrSize + tempLinkBufSize))) + { + /* Alright, the tempLinkBuf should NOT have a NULL character in the middle of the buffer. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " invalid NULL character found in the middle of a resolved symbolic link's buffer."); + } + else + { + /* Copy the data. */ + outputStr[x] = tempLinkBuf[x]; + } + } + } + else + { + /* An error occured while reallocating the buffer. */ + if (retFromCall == COMMON_ERROR_MEMORY_ERROR) + { + /* Unable to allocate memory for buffer reallocation. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " out of usable memory. Cannot reallocate buffer for addition of relative path."); + } + else + { + /* All other errors. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " unable to reallocate output buffer for addition of relative path. Please report this bug."); + } + } + } + else + { + /* Unable to remove the last path segment. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Call to path segment removal function failed. Please report this bug."); + } + } + else + { + /* Check and see if the path is absolute. */ + if (ret == FILEUTILLS_ERROR_PATH_IS_ABSOLUTE) + { + /* Copy the absolute path to the output path. */ + retFromCall = DataProcess_Reallocate_C_String(&outputStr, 0, tempLinkBufSize); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (outputStr != NULL)) + { + /* Copy the string. */ + for (x = 0; (x < tempLinkBufSize); x++) + { + outputStr[x] = tempLinkBuf[x]; + } + } + else + { + /* Could not reallocate memory for outputStr. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Call to DataProcess_Reallocate_C_String() failed while attempting to copy resolved absolute path system symbolic link."); + } + } + else + { + /* This is any other error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Unable to resolve the given path ( "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, path); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) Unable to determine the type of the symbolic link."); + } + } + } + else + { + /* OK an error occured report and log it. */ + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* This is an internal engine error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Call to system symbolic link resolution function failed with the given path ( "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, outputStr); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) Please report this bug."); + } + else + { + /* This is any other error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Unable to resolve the given path ( "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, outputStr); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) Unable to resolve system defined symbolic link."); + } + } + + /* Deallocate the resolved symlink string if needed. */ + if (tempLinkBuf != NULL) + { + FileUtills_Deallocate_CString_Syscall(&tempLinkBuf); + } + tempLinkBufSize = 0; + } + break; + default: + /* OK an error occured report and log it. */ + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* This is an internal engine error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Call to FileUtills_IsFileOrDirectory() failed with the given path ( "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, path); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) Please report this bug.\n"); + } + else + { + /* This is any other error. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Unable to resolve the given path ( "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, path); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) Unable to determine final path type.\n"); + } + break; + }; + } + else + { + /* Success without result. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + + /* Log the error. */ + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Call to helper function indicated success but did not give a result. Please report this bug.\n"); + } + } + } + + /* Check and see if the loop exited because we hit the resolution attempt limit. */ + if (numOfResolveAttempts >= FileUtills_Get_Max_Symlink_Depth_Syscall()) + { + /* Resolve attempt limit reached. */ + ret = FILEUTILLS_ERROR_SYMLINK_CHAIN_TOO_DEEP; + + /* Log the error. */ + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Unable to resolve the given path ( "); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, path); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) "); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(FILEUTILLS_ERROR_SYMLINK_CHAIN_TOO_DEEP)); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "\n"); + } + else + { + /* Check and see if output str is valid and we have a success code. */ + if ((ret == COMMON_ERROR_SUCCESS) && (outputStr != NULL) && (outputStrSize > 0)) + { + /* Copy output str. */ + (*retStr) = outputStr; + (*retStrSize) = outputStrSize; + } + } + } + } + } + else + { + /* Given path is invalid. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given path is invalid.\n"); + } + + /* If we do not have success, we need to deallocate the outputStr buffer. */ + if (ret != COMMON_ERROR_SUCCESS) + { + if (outputStr != NULL) + { + DataProcess_Deallocate_CString(&outputStr); + } + } + + /* Return the result. */ + return ret; +} diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills.cpp b/src/Common/Src/File_Management_Subsystem/FileUtills.cpp index c7e28fa..f4b6a3c 100644 --- a/src/Common/Src/File_Management_Subsystem/FileUtills.cpp +++ b/src/Common/Src/File_Management_Subsystem/FileUtills.cpp @@ -1,7 +1,7 @@ /*! Multiverse Engine Project 04/12/2011 FileUtills FileUtills.cpp - Copyright (C) 2013 Multiverse Engine Project + Copyright (C) 2014 Multiverse Engine Project This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; @@ -18,654 +18,416 @@ https://github.com/codebase7/mengine */ - +// Internal includes. #include "FileUtills.h" +#include "FileUtills_Private_API.h" -/*! - std::string RemoveTrailingSlash(const std::string & path) - - Removes a DIR_SEP on the end of the given path string (if it exists) - Will not do anything and return the original string if there is only 1 charater in the given path string. - Will also return the original string if a memory error occurs. -*/ -std::string RemoveTrailingSlash(const std::string & path) +int FileUtills::GetUserProfileDirectoryPath(std::string & path) { - //init vars - size_t size = 0; - std::string buffer = ""; + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + std::string resultString = ""; /* The string we will return. to the caller. */ + char * tempString = NULL; /* Pointer used to store result from C function call. */ + size_t tempStringLength = 0; /* Length of the result string from the C function call. */ + + /* Call the C function. */ + ret = FileUtills_GetUserProfileDirectoryPath(&tempString, &tempStringLength); + if ((ret == COMMON_ERROR_SUCCESS) && (tempString != NULL) && (tempStringLength > 0)) + { + /* Run loop to copy the data into the resultString. */ + for (size_t x = 0; (x < tempStringLength); x++) + { + resultString += tempString[x]; + } - // Get size of the Directory string. - size = path.size(); + /* Now copy the string. */ + path = resultString; - // Check the length of the string see if it is only 1 charater - if (size == 1) - { - // We can't remove anything, if we do the return would be empty. - return path; + /* Success. */ + ret = COMMON_ERROR_SUCCESS; } - // Copy the directory string to buffer - buffer = path; - - // Check and see if a directory seperator is on the end of the string. - if ((path.find_last_of(DIR_SEP)) == (size-1)) + /* Deallocate the C-string if needed. */ + if (tempString != NULL) { - // Remove the trailing Directory seperator so we don't screw up the next check. - // Note substr CAN throw an exception so we must put it in a try catch block - try{ - buffer = path.substr(0, (size-1)); - } - catch(exception ex) - { - // We can't return an error so we will silently fail. - return path; - } + FileUtills_Deallocate_CString(&tempString); } - // Return new path. - return buffer; + /* Exit function. */ + return ret; } -int FileUtills::GetUserProfileDirectoryPath(std::string & path) +int FileUtills::GetCurrentWorkingDirectoryPath(std::string & path) { - // Reset Common::commonLastErrorCode. - Common::commonLastErrorCode = Common::COMMON_FUNCTION_NOT_IMPLEMENTED; -#ifdef __linux__ - // Init vars. - int errorg = 0; // Used to hold the original state of errno so it can be restored after we finish. - int errcpy = 0; // Used to store errno if needed. - char * pHD = NULL; // Used to fetch the path from the system. - - // Backup and clear errno. - errorg = errno; - errno = 0; - - // Blank out the path value. - path.clear(); - - // Get the path to the user's profile directory. - try { - pHD = getenv("HOME"); - if ((errno == 0) && (pHD != NULL)) - { - // Copy the variable to the path. - path = pHD; - - // Set success code. - Common::commonLastErrorCode = Common::COMMON_SUCCESS; - } - else + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + std::string resultString = ""; /* The string we will return. to the caller. */ + char * tempString = NULL; /* Pointer used to store result from C function call. */ + size_t tempStringLength = 0; /* Length of the result string from the C function call. */ + + /* Call the C function. */ + ret = FileUtills_GetCurrentWorkingDirectoryPath(&tempString, &tempStringLength); + if ((ret == COMMON_ERROR_SUCCESS) && (tempString != NULL) && (tempStringLength > 0)) + { + /* Run loop to copy the data into the resultString. */ + for (size_t x = 0; (x < tempStringLength); x++) { - // Could not get user profile directory path variable. - errcpy = errno; - Common::commonLastErrorCode = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); - COMMON_LOG_VERBOSE("FileUtills::GetUserProfileDirectoryPath(): "); - COMMON_LOG_VERBOSE(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_VERBOSE(" Could not get user profile directory path from enviorment.\n"); - - // Reset path. - path.clear(); + resultString += tempString[x]; } - } - catch (exception &ex) - { - // Exception thown. - Common::commonLastErrorCode = Common::COMMON_EXCEPTION_THROWN; - COMMON_LOG_VERBOSE("FileUtills::GetUserProfileDirectoryPath(): "); - COMMON_LOG_VERBOSE(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_VERBOSE(" "); - COMMON_LOG_VERBOSE(ex.what()); - COMMON_LOG_VERBOSE("\n"); - - // Reset path. - path.clear(); + + /* Now copy the string. */ + path = resultString; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; } - // Check for an allocated buffer. - if (pHD != NULL) + /* Deallocate the C-string if needed. */ + if (tempString != NULL) { - // Release the buffer. - free(pHD); - pHD = NULL; + FileUtills_Deallocate_CString(&tempString); } - // Restore errno. - errno = errorg; -#endif // __linux__ - - // Exit function. - return Common::commonLastErrorCode; + /* Exit function. */ + return ret; } -int FileUtills::GetCurrentWorkingDirectoryPath(std::string & path) +int FileUtills::GetExecDirectory(std::string & path) { - // Reset Common::commonLastErrorCode. - Common::commonLastErrorCode = Common::COMMON_FUNCTION_NOT_IMPLEMENTED; -#ifdef __linux__ - // Init vars. - int errorg = 0; // Used to hold the original state of errno so it can be restored after we finish. - int errcpy = 0; // Used to store errno if needed. - char * pCWD = NULL; // Used to fetch path from system. - - // Backup and clear errno. - errorg = errno; - errno = 0; - - // Blank out the path value. - path.clear(); - - // Get current directory. - try { - /* - * NOTE: The call below is linux (libc5, libc6, and glibc) specific. - * (The standard requires a preallocated buffer.) - * - * I dislike the idea of a preallocated buffer as we cannot reliably - * determine the needed length. (Plus it could change between calls.) - * - * This really should be checked for and an error thrown if the OS / - * libc in use does not support this usage. - */ - pCWD = getcwd(NULL, 0); - if ((errno == 0) && (pCWD != NULL)) - { - // Copy the path. - path = pCWD; - - // Set commonLastErrorCode. - Common::commonLastErrorCode = Common::COMMON_SUCCESS; - } - else + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + std::string resultString = ""; /* The string we will return. to the caller. */ + char * tempString = NULL; /* Pointer used to store result from C function call. */ + size_t tempStringLength = 0; /* Length of the result string from the C function call. */ + + /* Call the C function. */ + ret = FileUtills_GetExecDirectory(&tempString, &tempStringLength); + if ((ret == COMMON_ERROR_SUCCESS) && (tempString != NULL) && (tempStringLength > 0)) + { + /* Run loop to copy the data into the resultString. */ + for (size_t x = 0; (x < tempStringLength); x++) { - // Could not get current working directory path variable. - errcpy = errno; - Common::commonLastErrorCode = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); - COMMON_LOG_VERBOSE("FileUtills::GetCurrentWorkingDirectoryPath(): "); - COMMON_LOG_VERBOSE(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_VERBOSE(" Could not get current working directory path from enviorment.\n"); - - // Reset path. - path.clear(); + resultString += tempString[x]; } - } - catch(exception &ex) - { - // Exception thown. - Common::commonLastErrorCode = Common::COMMON_EXCEPTION_THROWN; - COMMON_LOG_VERBOSE("FileUtills::GetCurrentWorkingDirectoryPath(): "); - COMMON_LOG_VERBOSE(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_VERBOSE(" "); - COMMON_LOG_VERBOSE(ex.what()); - COMMON_LOG_VERBOSE("\n"); - - // Reset path. - path.clear(); + + /* Now copy the string. */ + path = resultString; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; } - // Check for an allocated buffer. - if (pCWD != NULL) + /* Deallocate the C-string if needed. */ + if (tempString != NULL) { - // Release the buffer. - free(pCWD); - pCWD = NULL; + FileUtills_Deallocate_CString(&tempString); } - // Restore errno. - errno = errorg; -#endif // __linux__ - - // Exit function. - return Common::commonLastErrorCode; + /* Exit function. */ + return ret; } -std::string FileUtills::GetExecDirectory() +/*! + * int FileUtills_Copy_CString(const char * src, const size_t srcSize, char ** dest) + * + * Allocates a new c-string and copies the given source string to it. + * The copied c-string is identical to the source, and is allocated + * with the same size. + * + * Returns COMMON_ERROR_SUCCESS if successful. (The dest pointer will point + * to the copied c-string in this case.) + * + * Otherwise returns the appropriate error. (The dest pointer will NOT be + * altered in this case.) + */ +int FileUtills_Copy_CString(const char * src, const size_t srcSize, char ** dest) { // Init vars. - std::string result = ""; // The string returned from this function. -#ifdef __linux__ - /* - * Short version: - * This is a cluster. - * - * Long version: - * The only reliable (guarrenteed to work) method for getting the executable - * path in linux, is by using readlink() on /proc/self/exe. - * - * However, this has several issues. - * - * First issue: readlink() expects a preallocated buffer to store the result. - * If the preallocated buffer is too small for the full path, then readlink() - * will silently truncate the remaining data and not tell us how much was - * left over. - * - * Second issue: The proc filesystem misbehaves and does not fill in the - * st_size field of the stat structure. So as a result we can't easilly - * tell how much memory to allocate for our buffer. - * - * Third issue: Because some filesystems allow paths of unlimited size, - * (i.e. the only restriction is having the space to store the path), - * we can't use PATH_MAX (as it's possibly wrong), and we can't set a - * limit on our memory allocation. - * - * Because of these issues, the glibc MANUAL actually gives an indefinite - * loop of memory allocation->readlink()->if(!got entire link)->Deallocate memory->Allocate memory. - * for reading a symlink from the filesystem. - * - * This loop is reimplimented here, but with two differences. - * - * - The loop will end once a certian amount of relocations (defined by GET_EXE_PATH_REALLOC) - * is reached. (This is to prevent some of the above issues.) - * - * - The loop will also check to make sure that the returned string has not been altered. - * (via memcmp). - */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + char * tempBuf = NULL; // Temporary variable to copy the src with. - // Init vars. - int errcpy = 0; // Used to copy errno if needed. - const size_t MAX_GET_EXE_PATH_REALLOC = 4; // Maximum number of times to run the memory reallocation loop. - const size_t BASE_SIZE = 1000; // Inital number of bytes to allocate. (This number is multiplied by the loop iteration value (x) after each loop iteration.) - size_t realBufferLength = 0; // Unsigned value of how big the buffer is. (Used for accessing and allocating the buffer.) - ssize_t firstBufferLength = 0; // Signed return value of the number of bytes written to the buffer by readlink(). (Just so they could return that -1 error code....) - ssize_t secondBufferLength = 0; // Signed return value of the number of bytes written to the checkBuffer by readlink(). (Just so they could return that -1 error code....) - char * buffer = NULL; // First buffer. - char * checkBuffer = NULL; // Second buffer. (Both buffers must match exactly (memcmp) for this function to return a valid path.) - struct stat st; // Used to make sure that PROC_PATH is actually a symlink. - const char * const PROC_PATH = "/proc/self/exe";// The actual path to the symlink that represents the executable in the /proc filesystem. - - // Set Common::commonLastErrorCode to COMMON_UNKNOWN_ERROR. - Common::commonLastErrorCode = Common::COMMON_UNKNOWN_ERROR; - - // OK this is dumb, but check it anyway.... - errcpy = lstat(PROC_PATH, &st); - if (errcpy == 0) + // Check for valid arguments. + if ((src != NULL) && (srcSize > 0) && (dest != NULL)) { - // Make sure that PROC_PATH is a symlink. - if ((st.st_mode & S_IFMT) == S_IFLNK) // S_IFMT is a bit mask for extracting the file type from st_mode. S_IFLNK is the file type for a symlink. + // Allocate tempBuf. + tempBuf = (char*)malloc(srcSize); + if (tempBuf != NULL) { - // Begin the devil's loop. - try { - for (size_t x = 0; ((x < MAX_GET_EXE_PATH_REALLOC) && - ((buffer == NULL) && (checkBuffer == NULL)) && - (Common::commonLastErrorCode == Common::COMMON_UNKNOWN_ERROR)); x++) - { - // Recalculate bufferLength. - realBufferLength = (BASE_SIZE * (x + 1)); - - // Allocate the memory. - buffer = (char*)malloc(realBufferLength); - checkBuffer = (char*)malloc(realBufferLength); - - // Make sure it was allocated. - if ((buffer != NULL) && (checkBuffer != NULL)) - { - // Blank out the allocated memory. - memset(buffer, '\0', realBufferLength); - memset(checkBuffer, '\0', realBufferLength); - - // Call readlink() for the first buffer. - firstBufferLength = readlink(PROC_PATH, buffer, realBufferLength); - - // Check bufferLength. - if (firstBufferLength >= 0) - { - // Check to see if we got the entire path. - if (firstBufferLength < realBufferLength) - { - // Call readlink() for the second buffer. - secondBufferLength = readlink(PROC_PATH, checkBuffer, realBufferLength); - - // Check secondBufferLength. - if (secondBufferLength >= 0) - { - // Check to see if we got the entire path. - if (secondBufferLength == firstBufferLength) - { - // Call memcmp(). - if (memcmp(buffer, checkBuffer, realBufferLength) == 0) - { - // Paths match, deallocate the second buffer. - if (checkBuffer != NULL) - { - free(checkBuffer); - checkBuffer = NULL; - } - - // Copy the first buffer to the result. - result = buffer; - - // Because the returned link is the actual executable, call GetParent(). - result = FileUtills::GetParent(result); - if (result.size() > 0) - { - // Clear Common::commonLastErrorCode. - Common::commonLastErrorCode = Common::COMMON_SUCCESS; - } - else - { - // GetParent() failed. - Common::commonLastErrorCode = Common::COMMON_INTERNAL_ERROR; - COMMON_LOG_DEBUG("FileUtills::GetExecDirectory(): "); - COMMON_LOG_DEBUG(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_DEBUG(" Call to GetParent() failed.\n"); - result = ""; - } - } - } - } - else - { - // Error. - errcpy = errno; - Common::commonLastErrorCode = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); - COMMON_LOG_DEBUG("FileUtills::GetExecDirectory(): readlink() system call returned: "); - COMMON_LOG_DEBUG(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_DEBUG("\n"); - result = ""; - } - } - } - else - { - // Error. - errcpy = errno; - Common::commonLastErrorCode = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); - COMMON_LOG_DEBUG("FileUtills::GetExecDirectory(): readlink() system call returned: "); - COMMON_LOG_DEBUG(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_DEBUG("\n"); - result = ""; - } - } - - // Deallocate any remaining buffers. - if (buffer != NULL) - { - free(buffer); - buffer = NULL; - } - if (checkBuffer != NULL) - { - free(checkBuffer); - checkBuffer = NULL; - } - } - - /* - * If we reach here, and Common::commonLastErrorCode == Common::COMMON_UNKNOWN_ERROR, - * then we have failed. (Most likely we ran out of reallocation attempts...) - */ - if (Common::commonLastErrorCode == Common::COMMON_UNKNOWN_ERROR) - { - /* - * Set COMMON_INTERNAL_ERROR. - * - * (The system most likely can fetch the link, - * but we need to limit the reallocation attempts - * to prevent issues. So it's not appropriate to use - * FILEUTILLS_PATH_LENGTH_INVALID.) - */ - Common::commonLastErrorCode = Common::COMMON_INTERNAL_ERROR; - COMMON_LOG_INFO("FileUtills::GetExecDirectory(): "); - COMMON_LOG_INFO(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_INFO(" executable path is too long to retrive due to engine limitation.\n"); - result = ""; - } - } - catch(...) + // Copy the string. + for (size_t x = 0; (x < srcSize); x++) { - // Exception thrown. - Common::commonLastErrorCode = Common::COMMON_EXCEPTION_THROWN; - COMMON_LOG_DEBUG("FileUtills::GetExecDirectory(): "); - COMMON_LOG_DEBUG(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_DEBUG("\n"); - result = ""; + tempBuf[x] = src[x]; } + + // Set the dest pointer. + (*dest) = tempBuf; + + // Done. + ret = COMMON_ERROR_SUCCESS; } else { - // Internal error. (PROC_PATH is NOT a symbolic link.) - Common::commonLastErrorCode = Common::COMMON_INTERNAL_ERROR; - COMMON_LOG_DEBUG("FileUtills::GetExecDirectory(): "); - COMMON_LOG_DEBUG(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_DEBUG(" PROC_PATH is NOT a symbolic link.\n"); + // Could not allocate memory. + ret = COMMON_ERROR_MEMORY_ERROR; } } else { - // lstat() Error. - errcpy = errno; - Common::commonLastErrorCode = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); - COMMON_LOG_DEBUG("FileUtills::GetExecDirectory(): lstat() system call returned: "); - COMMON_LOG_DEBUG(Common::Get_Error_Message(Common::commonLastErrorCode)); - COMMON_LOG_DEBUG("\n"); + // Invalid argument error. + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG("FileUtills_Copy_CString(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(" Invalid string, string size, or destionation pointer given."); } -#endif - // Return result. - return result; -} -std::string FileUtills::CheckPathType(const std::string & path) -{ - // Init vars. - std::string output = ""; - char charArray[1000]; // Used for the cwd function. - char * pCWD; // Used to store result of the getCWD function. - long int size = 0; // Size of path string. - - // Set size. - size = path.size(); - - // Check and see if a directory seperator is in the path. - if (path.find(DIR_SEP) == string::npos) - { - // Going with realtive path. - pCWD = getcwd(charArray, sizeof(charArray)); - output = pCWD; - output += DIR_SEP; - output += path; - return output; - } - - // Look for home '~'. - if (path.find_first_of('~') == 0) - { - // Get the home path. - pCWD = getenv("HOME"); - output = pCWD; - output += path.substr(1, size); - - // Remove the slash if it was added back. - output = RemoveTrailingSlash(output); - - // Return output. - return output; - } - - // Look for "./" (Relative) - if(path.find("./") == 0) - { - // Get current directory. - pCWD = getcwd(charArray, sizeof(charArray)); - output = pCWD; - - // Add a DIR_SEP to the string or we will mess up the directory name. - output += DIR_SEP; - - // Add data from the Directory string. - output += path.substr(2, size); - - // Remove the slash if it was added back. - output = RemoveTrailingSlash(output); - - // Return the output. - return output; - } - - // Look for absolute path. - if (path.find(DIR_SEP) == 0) - { - // Path is absolute. - return path; - } - else // If all else fails it must be relative. - { - // Assuming relative path. - pCWD = getcwd(charArray, sizeof(charArray)); - output = pCWD; - output += DIR_SEP; - output += path; - return output; - } - - // Default return - return ""; + // Return the result. + return ret; } -FileUtills::dirlist * FileUtills::getDirectory(const std::string & path, const bool & cleanList) +int FileUtills::ResolvePath(const std::string path, std::string & resolvedPath, const bool disableSymLinkResolution) { -#ifdef POSIX_COMMON_H - // Dumb check. - if (path.size() <= 0) + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + std::string resultString = ""; /* The string we will return. to the caller. */ + char * tempString = NULL; /* Pointer used to store result from C function call. */ + size_t tempStringLength = 0; /* Length of the result string from the C function call. */ + + /* Call the C function. */ + ret = FileUtills_ResolvePath(path.c_str(), path.length(), &tempString, &tempStringLength, disableSymLinkResolution); + if ((ret == COMMON_ERROR_SUCCESS) && (tempString != NULL) && (tempStringLength > 0)) { - // Invalid path. - return NULL; - } + /* Run loop to copy the data into the resultString. */ + for (size_t x = 0; (x < tempStringLength); x++) + { + resultString += tempString[x]; + } - // Init vars. - FileUtills::dirlist * buffer; - struct dirent * dir; // '' - DIR * dp; // Directory stream to hold directory name..... (Why can't it just take a c string?) - std::string tempname = ""; // Used to store the filename / subdirectory name for the addToArray function. - - // Allocate the dirlist. - try{ - buffer = new FileUtills::dirlist; + /* Now copy the string. */ + resolvedPath = resultString; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; } - catch(bad_alloc) + + /* Deallocate the C-string if needed. */ + if (tempString != NULL) { - // Could not allocate struct. - return NULL; + FileUtills_Deallocate_CString(&tempString); } - // Set the path. - buffer->path = path; + /* Exit function. */ + return ret; +} - // Dump the path as a c string into the Directory stream object......(Overly complicated POS......) +FileUtills::dirlist * FileUtills::getDirectory(const std::string & path, const bool & cleanList) +{ + // Init vars. + FileUtills::dirlist * ret = NULL; // Result of this function. + std::string absPath = ""; // Absolute version of the given path. - // Check and make sure we can open the directory first. - if ((dp = (opendir(path.c_str()))) == NULL) + // Resolve the given path. + absPath = FileUtills::ResolvePath(path); + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) { - // An error occured. - if (buffer != NULL) + // Call helper. + ret = FileUtills::getDirectory_Helper(absPath, cleanList); + if (Common_commonLastErrorCode != COMMON_ERROR_SUCCESS) { - delete buffer; - buffer = NULL; - } - - // Exit function. - return NULL; - } + // Check for a list. + if (ret != NULL) + { + delete ret; + ret = NULL; + } - // Start filesystem fetch loop. - while ((dir = readdir(dp))) // Call Host OS function. - { - // Check and see if the cleanList flag is set. - if (cleanList) + // Check for a INVALID_ARGUMENT error. + if (Common_commonLastErrorCode == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. (The caller does not need to see the invalid argument error.) + Common_commonLastErrorCode = COMMON_ERROR_INTERNAL_ERROR; + } + } + else { - // Check to see if d_name is a POSIX directory shortcut. - if ((strcmp(dir->d_name, ".") == 0) || (strcmp(dir->d_name, "..") == 0)) + // Check for a success without result. + if (ret == NULL) { - // d_name is a directory shortcut, do not add it to the list. - continue; + // Set this to internal error. (The caller does not need to see the success without result error.) + Common_commonLastErrorCode = COMMON_ERROR_INTERNAL_ERROR; } } - - // Cast d_name to a string. - tempname = dir->d_name; - - // Add the data to the array. - buffer->list.push_back(tempname); } - - // Close the directory stream and reset it. - closedir(dp); - - // If we are cleaning the list, call DataProcess::DecrementingSort(). - if (DataProcess::DecrementingSort(buffer->list) != 0) + else { - // An exception was thrown in the DecrementingSort() function, bail out. - if (buffer != NULL) + // Log the error if needed. + COMMON_LOG_VERBOSE("FileUtills::getDirectory(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(Common_commonLastErrorCode)); + COMMON_LOG_VERBOSE("Unable to resolve the given path, aborting.\n"); + + /* + * Just in case for some reason Common_commonLastErrorCode + * was COMMON_ERROR_SUCCESS, and we got here because absPath.size() + * was less than or equal to (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1), + * make sure we set the correct error code. + */ + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() <= 0)) { - delete buffer; - buffer = NULL; + // The error is an internal error. + Common_commonLastErrorCode = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::getDirectory(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); } - - // Exit function. - return NULL; } - // Set the number of entries. - buffer->numOfEntries = buffer->list.size(); - - // Return the buffer. - return buffer; -#endif - // Default return for unimplemted function. - return NULL; + // Return the result. + return ret; } int FileUtills::GetGigaFreespace(const std::string & path, size_t & result) { - // Init ret. - int ret = Common::COMMON_FUNCTION_NOT_IMPLEMENTED; // The result of this function. - - // Call GetByteFreespace(). - ret = FileUtills::GetByteFreespace(path, result); + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // Result of this function. + std::string absPath = ""; // Absolute version of the given path. - // Check for success. - if (ret == Common::COMMON_SUCCESS) + // Resolve the given path. + absPath = FileUtills::ResolvePath(path); + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) + { + // Call helper. + ret = FileUtills::GetGigaFreespace_Helper(absPath, result); + if (ret != COMMON_ERROR_SUCCESS) + { + // Check for a INVALID_ARGUMENT error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. (The caller does not need to see the invalid argument error.) + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else { - // Byte conversion. - result = (result / ((double)1000000000)); + // Log the error if needed. + COMMON_LOG_VERBOSE("FileUtills::GetGigaFreespace(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE("Unable to resolve the given path, aborting.\n"); + + /* + * Just in case for some reason result + * was COMMON_SUCCESS, and we got here because absPath.size() + * was less than or equal to (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1), + * make sure we set the correct error code. + */ + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() <= 0)) + { + // The error is an internal error. + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::GetGigaFreespace(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); + } } + // Copy ret to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; + // Return ret. return ret; } int FileUtills::GetFreespace(const std::string & path, size_t & result) { - // Init ret. - int ret = Common::COMMON_FUNCTION_NOT_IMPLEMENTED; // The result of this function. - - // Call GetByteFreespace(). - ret = FileUtills::GetByteFreespace(path, result); + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // Result of this function. + std::string absPath = ""; // Absolute version of the given path. - // Check for success. - if (ret == Common::COMMON_SUCCESS) + // Resolve the given path. + absPath = FileUtills::ResolvePath(path); + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) + { + // Call helper. + ret = FileUtills::GetFreespace_Helper(absPath, result); + if (ret != COMMON_ERROR_SUCCESS) + { + // Check for a INVALID_ARGUMENT error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. (The caller does not need to see the invalid argument error.) + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else { - // Byte conversion. - result = (result / ((double)1000000)); + // Log the error if needed. + COMMON_LOG_VERBOSE("FileUtills::GetFreespace(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE("Unable to resolve the given path, aborting.\n"); + + /* + * Just in case for some reason result + * was COMMON_SUCCESS, and we got here because absPath.size() + * was less than or equal to (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1), + * make sure we set the correct error code. + */ + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() <= 0)) + { + // The error is an internal error. + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::GetFreespace(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); + } } + // Copy ret to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; + // Return ret. return ret; } int FileUtills::GetKiloFreespace(const std::string & path, size_t & result) { - // Init ret. - int ret = Common::COMMON_FUNCTION_NOT_IMPLEMENTED; // The result of this function. - - // Call GetByteFreespace(). - ret = FileUtills::GetByteFreespace(path, result); + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // Result of this function. + std::string absPath = ""; // Absolute version of the given path. - // Check for success. - if (ret == Common::COMMON_SUCCESS) + // Resolve the given path. + absPath = FileUtills::ResolvePath(path); + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) + { + // Call helper. + ret = FileUtills::GetKiloFreespace_Helper(absPath, result); + if (ret != COMMON_ERROR_SUCCESS) + { + // Check for a INVALID_ARGUMENT error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. (The caller does not need to see the invalid argument error.) + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else { - // Byte conversion. - result = (result / ((double)1000)); + // Log the error if needed. + COMMON_LOG_VERBOSE("FileUtills::GetKiloFreespace(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE("Unable to resolve the given path, aborting.\n"); + + /* + * Just in case for some reason result + * was COMMON_SUCCESS, and we got here because absPath.size() + * was less than or equal to (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1), + * make sure we set the correct error code. + */ + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() <= 0)) + { + // The error is an internal error. + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::GetKiloFreespace(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); + } } + // Copy ret to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; + // Return ret. return ret; } @@ -673,1947 +435,1448 @@ int FileUtills::GetKiloFreespace(const std::string & path, size_t & result) int FileUtills::GetByteFreespace(const std::string & path, size_t & result) { // Init vars. - int ret = Common::COMMON_FUNCTION_NOT_IMPLEMENTED; // Result of this function. -#ifdef POSIX_COMMON_H - struct statvfs * buffer = NULL; // Buffer used to get filesystem info from the OS. - - // Reset result. - result = 0; - - // Begin try block. - try { - // Create buffer. - buffer = new struct statvfs; - if (buffer != NULL) - { - // Call host's function, and check for error. - if (statvfs(path.c_str(), buffer) == 0) - { - // Determine number of free bytes. (Number of avaiable blocks * block size) - result = (buffer->f_bavail * buffer->f_bsize); + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // Result of this function. + std::string absPath = ""; // Absolute version of the given path. - // Set success. - ret = Common::COMMON_SUCCESS; - } - else + // Resolve the given path. + absPath = FileUtills::ResolvePath(path); + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) + { + // Call helper. + ret = FileUtills::GetByteFreespace_Helper(absPath, result); + if (ret != COMMON_ERROR_SUCCESS) + { + // Check for a INVALID_ARGUMENT error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) { - // Figure out what the error we got back was. - ret = errno; - ret = Common::Translate_Posix_Errno_To_Common_Error_Code(ret); - - // Log the error. - COMMON_LOG_DEBUG("FileUtills::GetByteFreespace(): "); - COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); - COMMON_LOG_DEBUG("\n"); + // Set this to internal error. (The caller does not need to see the invalid argument error.) + ret = COMMON_ERROR_INTERNAL_ERROR; } - - // Delete the buffer - delete buffer; - buffer = NULL; - } - else - { - // Could not allocate memory. - ret = Common::COMMON_MEMORY_ERROR; - - // Log the error. - COMMON_LOG_DEBUG("FileUtills::GetByteFreespace(): "); - COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); - COMMON_LOG_DEBUG("\n"); } } - catch (exception &ex) + else { - // Exception thrown. - ret = Common::COMMON_EXCEPTION_THROWN; + // Log the error if needed. + COMMON_LOG_VERBOSE("FileUtills::GetByteFreespace(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE("Unable to resolve the given path, aborting.\n"); - // Check for allocated buffer. - if (buffer != NULL) + /* + * Just in case for some reason result + * was COMMON_SUCCESS, and we got here because absPath.size() + * was less than or equal to (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1), + * make sure we set the correct error code. + */ + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() <= 0)) { - delete buffer; - buffer = NULL; + // The error is an internal error. + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::GetByteFreespace(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); } - - // Log the error. - COMMON_LOG_VERBOSE("FileUtills::GetByteFreespace(): "); - COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); - COMMON_LOG_VERBOSE(" "); - COMMON_LOG_VERBOSE(ex.what()); - COMMON_LOG_VERBOSE("\n"); } -#endif - // Copy ret to Common::commonLastErrorCode. - Common::commonLastErrorCode = ret; + + // Copy ret to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; // Return ret. return ret; } -short FileUtills::CreateDirectory(const std::string & directory, Panic::ERROR & error, const bool & createRecursive) +int FileUtills::CreateDirectory(const std::string & directory, const bool & createRecursive) { -#ifdef POSIX_COMMON_H // Init vars. - std::string buffer = ""; // Used to store the current directory path. - std::string parentbuffer = ""; // Used to store the parent directory's path. - std::string realpath = ""; // Used to store the real directory path if a relative path or a user path is given instead of an absolute path. - int errorcode = 0; // Used to store return error codes. - size_t position = 0; // Used to store current search position in buffer. - size_t size = 0; // Used to store the size of the path string - int permission = 0; // Used to store return code from FileUtills::CheckPermissions(). - bool skiploop = false; // Used to control the recursive directory creation loop. - bool isrelative = false; // Used to tell if the path is relative or abosulute. - bool isHome = false; // Used to tell if the path is in a user's home directory. - char charArray[1000]; // Used for the cwd function. - char * pCWD; // Used to store result of the getCWD function. - - // Check to see if the full directory path already exists. - if (FileUtills::DoesExist(directory, error) == true) - { - // Directory already exists thus return true. - return -3; - } - - // Check for a directory seperator on the end of the path and remove it if it is found. - buffer = RemoveTrailingSlash(directory); - - // Get the size of the buffer string. - size = buffer.size(); + int result = COMMON_ERROR_UNKNOWN_ERROR; // Used to hold the result of this function. + std::string absPath = ""; // Used to store the current directory path. - // Determine if we have a relative path or a absolute one. - if (buffer.find_first_of(DIR_SEP) == 0) + // Check to see if the full directory path already exists. + if (FileUtills::DoesExist(directory) != true) { - isrelative = false; - } - - if (buffer.find("./") == 0) // BUG: this should not be called when string != "./". - { - // Ok relative path get the full path - isrelative = true; - pCWD = getcwd(charArray, sizeof(charArray)); - buffer = pCWD; - - buffer += directory.substr(1, size); + // Check for a directory seperator on the end of the path and remove it if it is found. + absPath = RemoveTrailingSlash(directory); - // Remove the slash if it was added back. - buffer = RemoveTrailingSlash(buffer); - - // Copy it to the realpath. - realpath = buffer; + // Get the absolute version of the path to create. + absPath = FileUtills::ResolvePath(absPath); + // Check the result. + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1))) + { + // Call helper. + result = FileUtills::CreateDirectory_Helper(absPath, createRecursive); + if (result != COMMON_ERROR_SUCCESS) + { + // Check for a INVALID_ARGUMENT error. + if (result == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. (The caller does not need to see the invalid argument error.) + result = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else + { + // Could not get absolute path to create. + COMMON_LOG_VERBOSE("FileUtills::CreateDirectory(): Could not get absolute path to create, aborting.\n"); + + /* + * Just in case for some reason result + * was COMMON_SUCCESS, and we got here because absPath.size() + * was less than or equal to (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1), + * make sure we set the correct error code. + */ + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() <= (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1))) + { + // The error is an internal error. + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::CreateDirectory(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); + } + } } - - // Also check if it's in a user's home subdirectory. - if (buffer.find_first_of('~') == 0) + else { - // Ok user path, get the full path. - isHome = true; - pCWD = getenv("HOME"); - buffer = pCWD; - buffer += directory.substr(1, size); - - // remove the slash if it was added back. - buffer = RemoveTrailingSlash(buffer); - - // Copy it to the real path. - realpath = buffer; + // Directory already exists thus return true. + result = FILEUTILLS_ERROR_EXISTANT; } - // Assume relative if we have something else here. - if (buffer.find_first_of(DIR_SEP) != 0 && isHome == false && isrelative == false) // BUG: This does not get called. - { - // Set isrelative to true and retrive the current working directory. - isrelative = true; - pCWD = getcwd(charArray, sizeof(charArray)); - buffer = pCWD; - - // Add a DIR_SEP to the string or we will mess up the directory name. - buffer += DIR_SEP; - - // Add all data from the Directory string because the string does not have a DIR_SEP at position zero. - buffer += directory.substr(0, size); - - // Remove the slash if it was added back. - buffer = RemoveTrailingSlash(buffer); + // Copy result to commonLastErrorCode. + Common_commonLastErrorCode = result; - // Copy it to the realpath. - realpath = buffer; - } - - // Get the parent directory out of the buffer and store it for later. - position = buffer.find_last_of(DIR_SEP); - parentbuffer = buffer.substr(0, position); - - // Check and see if the parent directory exists. - errorcode = FileUtills::CheckParent(directory); // Switch - - switch (errorcode) { - case -1: // Can't write to the parent directory exit the function. - error.PanicHandler("FileUtills::CreateDirectory : Parent Directory not writable. will not create directory."); - return -1; - permission = 0; - break; - case -2: // Parent Directory does not exist, - if (createRecursive == false) - { - // Don't create the directory structure. - error.PanicHandler("FileUtills::CreateDirectory : Parent Directory does not exist. createRecursive is false, will not create directory."); - return -2; - } - break; - - case 0: // Parent Directory does exist and is writeable. - // since we can write to the directory skip the loop. - skiploop = true; - break; - default: // Ether there was an unknown error or this Arch / OS is not supported. ether way return -4. - return -4; - errorcode = 0; - break; - } + // Return the result. + return result; +} - // Clear the vars for the recursive directory creation loop. - permission = 0; - position = 0; - errorcode = 0; +int FileUtills::CheckPermissions(const std::string & path, const bool & read, const bool & write, const bool & exec) +{ + // Init vars. + int ret = COMMON_ERROR_SUCCESS; // The result of this function. + std::string absPath = ""; // The absolute path of the given path. - // Set buffer so we can check the path correctly. - // If we have a relative or user path use the real path. - if (isHome == true || isrelative == true) + // Call ResolvePath(). + absPath = FileUtills::ResolvePath(path); + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) { - buffer = realpath.substr(0, (realpath.find_first_of( DIR_SEP , 1))); - - // Also set the size of the string correctly. - size = realpath.size(); + // Call helper function. + ret = FileUtills::CheckPermissions_Helper(absPath, read, write, exec); + if (ret != COMMON_ERROR_SUCCESS) + { + // Check for a INVALID_ARGUMENT error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. (The caller does not need to see the invalid argument error.) + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } } else { - // Otherwise use the directory string. - buffer = directory.substr(0, (directory.find_first_of( DIR_SEP, 1))); + // Call to ResolvePath() failed. + COMMON_LOG_VERBOSE("FileUtils::CheckPermissions(): Unable to resolve the given path, aborting.\n"); - // Also set the size of the string correctly. - size = directory.size(); - } - - // If the parent directory does not exist and createRecursive is true then begin main loop. - while (skiploop != true) - { - // If we are at position 0 and there is a DIR_SEP there incremnt position by one. - if (position == 0 && buffer.find_first_of(DIR_SEP) == 0) + // Make sure Common_commonLastErrorCode is set to something. + if (absPath.size() <= 0) { - // Incremnt the position by one. - position++; + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::CheckPermissions(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); } + } - // Start with root directory and run though the following until we reach the requested directory. + // Copy ret to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; - if (isHome == true || isrelative == true) // If true use realpath - { - if ((position = realpath.find_first_of(DIR_SEP, position)) == string::npos) - { - // This is the requested directory so exit the loop. - skiploop = true; - break; - } - } - else // Otherwise use directory. + // Return the result. + return ret; +} + +int FileUtills::DoesExist(const std::string & path) +{ + // Declare some vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // Holds common namepspace error code. + std::string absPath = ""; // Holds the abosolute value of the given path. + + // Call ResolvePath(). + absPath = FileUtills::ResolvePath(path); + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) + { + // Call helper function. + ret = FileUtills::DoesExist_Helper(absPath); + if (ret != COMMON_ERROR_SUCCESS) { - if ((position = directory.find_first_of(DIR_SEP, position)) == string::npos) + // Check for a INVALID_ARGUMENT error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) { - // This is the requested directory so exit the loop. - skiploop = true; - break; + // Set this to internal error. (The caller does not need to see the invalid argument error.) + ret = COMMON_ERROR_INTERNAL_ERROR; } } + } + else + { + // Call to ResolvePath() failed. + COMMON_LOG_VERBOSE("FileUtils::DoesExist(): Call to FileUtills::ResolvePath() failed, aborting.\n"); - // Do a sanity check for the size of the string. - if (position < 0 || position > size) - { - // Ok something went wrong. - return -4; - } - - // Dump the last buffer into the parentdirectory buffer. - parentbuffer = buffer; - - // Cut up the directory string so we can check for it. - - if (isHome == true || isrelative == true) + // Make sure Common_commonLastErrorCode is set to something. + if (absPath.size() <= 0) { - buffer = realpath.substr(0, position); + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::DoesExist(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); } - else - { - buffer = directory.substr(0, position); - } - - // Do Check. - errorcode = FileUtills::DoesExist(buffer, error); - - switch (errorcode){ - case -1: // Directory does not exist on system. - // Check and see if we can create it. - permission = FileUtills::CheckPermissions(parentbuffer, error); + } - switch (permission){ - case -1: - // Permission Check Failed. - return false; - break; + // Copy ret to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; - case 0: - // Ok try and create the directory. - if ((errorcode = (mkdir(buffer.c_str(), S_IRWXU))) != 0) - { - // An error ocurred find out what. + // Default return. + return ret; +} - switch (errno) { - case EACCES: // Permission error. - return -1; - break; - case EEXIST: // Path exists on system. - return -3; - break; - case ENOSPC: // Out of disk space. - return -2; - break; - default: // We really don't care about the other errors. - return -4; - break; - } - } +int FileUtills::IsFileOrDirectory(const std::string & path) +{ + // Init result. + int result = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // Result of this function. (Defaults to function not implemetated.) + std::string absPath = ""; // The absolute path of the given path argument. - // Update the position counter. - position++; + // Make sure that the path is not empty. + if (path.size() > 0) + { + // Get the absolute path of the given path argument. + absPath = FileUtills::ResolvePath(path); - // Check and see if that was the last directory to create. - if (isHome == true || isrelative == true) + // Check for success. + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) + { + // Call Helper function. + result = FileUtills::IsFileOrDirectory_Helper(absPath); + if (result != COMMON_ERROR_SUCCESS) { - // Check realpath. - if (buffer == realpath) + // Check for a INVALID_ARGUMENT error. + if (result == COMMON_ERROR_INVALID_ARGUMENT) { - skiploop = true; - return 0; + // Set this to internal error. (The caller does not need to see the invalid argument error.) + result = COMMON_ERROR_INTERNAL_ERROR; } } - // Otherwise check the directory buffer. - if (buffer == directory) + } + else + { + // Unable to generate absolute path. + COMMON_LOG_DEBUG("FileUtills::IsFileOrDirectory(): Unable to get absolute path, aborting.\n"); + + // Make sure Common_commonLastErrorCode is set to a valid error code. + if (absPath.size() <= 0) { - skiploop = true; - return 0; + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::IsFileOrDirectory(): ResolvePath() returned an invalid path string, but indicated success. Please report this bug.\n"); } - break; - - default: - // Bad errorcode exit function. - return -4; - break; - } - permission = 0; - errorcode = 0; - break; - - case 0: // Ok directory does exist. - position++; - break; - - default: - // Bad error code exit function. - return -4; } - - - - // If the next directory does exist then skip the remaining portion of the checks and restart with the next directory. - - // If the next directory does not exist the check the permissions of this one. - - // If we can write to this directory then create the directory. - - // If the directory creation was successful then check and see what to do next. - - // If we have created the requested directory exit the function - //return true. - - // If we have not created the requested directory contiune loop with the next - // directory. - - // If we encountered a fatal error exit the function return false. - - // If we can't write to this directory exit the function, return false. - - // If the permission check fails with a fatal error exit the function return false. - - // If the existance check fails exit the function, return false. - } - - - - //If we have a relative or user path use realpath. - if (isHome == true || isrelative == true) - { - buffer = realpath; } - else // Otherwise use the directory string. + else { + // Empty path. + result = COMMON_ERROR_INVALID_ARGUMENT; - buffer = directory; + // Log the error. + COMMON_LOG_DEBUG("FileUtills::IsFileOrDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" given path argument is invalid."); } - // Ok try and create the directory. - if ((errorcode = (mkdir(buffer.c_str(), S_IRWXU))) != 0) - { - // An error ocurred find out what. - - switch (errno) { - case EACCES: // Permission error. - return -1; - break; - case EEXIST: // Path exists on system. - return -5; - break; - case ENOSPC: // Out of disk space. - return -4; - break; - default: // We really don't care about the other errors. - return -6; - break; - } - } - if (errorcode == 0) - { - return 0; - } -#endif - // If an implemetation does not exist for this platform then exit the function, return false. - return -3; + // Copy result to commonLastErrorCode. + Common_commonLastErrorCode = result; + + // Exit function. + return result; } -short FileUtills::CreateDirectory(const std::string & directory, const bool & createRecursive) +int FileUtills::CheckParent(const std::string & path, const bool & read, const bool & write, const bool & exec) { -#ifdef POSIX_COMMON_H // Init vars. - std::string buffer = ""; // Used to store the current directory path. - std::string parentbuffer = ""; // Used to store the parent directory's path. - std::string realpath = ""; // Used to store the real directory path if a relative path or a user path is given instead of an absolute path. - int errorcode = 0; // Used to store return error codes. - size_t position = 0; // Used to store current search position in buffer. - size_t size = 0; // Used to store the size of the path string - int permission = 0; // Used to store return code from FileUtills::CheckPermissions(). - bool skiploop = false; // Used to control the recursive directory creation loop. - bool isrelative = false; // Used to tell if the path is relative or abosulute. - bool isHome = false; // Used to tell if the path is in a user's home directory. - char charArray[1000]; // Used for the cwd function. - char * pCWD; // Used to store result of the getCWD function. - - // Check to see if the full directory path already exists. - if (FileUtills::DoesExist(directory) == true) - { - // Directory already exists thus return true. - return -3; - } - - // Check for a directory seperator on the end of the path and remove it if it is found. - buffer = RemoveTrailingSlash(directory); - - // Get the size of the buffer string. - size = buffer.size(); - - // Determine if we have a relative path or a absolute one. - if (buffer.find_first_of(DIR_SEP) == 0) - { - isrelative = false; - } - - if (buffer.find("./") == 0) // BUG: this should not be called when string != "./". - { - // Ok relative path get the full path - isrelative = true; - pCWD = getcwd(charArray, sizeof(charArray)); - buffer = pCWD; - - buffer += directory.substr(1, size); - - // Remove the slash if it was added back. - buffer = RemoveTrailingSlash(buffer); - - // Copy it to the realpath. - realpath = buffer; + int result = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // Result of this function. + std::string absPath = path; // Absolute version of the given path. - } - - // Also check if it's in a user's home subdirectory. - if (buffer.find_first_of('~') == 0) + // Check and see if the length is zero. + if (absPath.size() > 0) { - // Ok user path, get the full path. - isHome = true; - pCWD = getenv("HOME"); - buffer = pCWD; - buffer += directory.substr(1, size); - - // remove the slash if it was added back. - buffer = RemoveTrailingSlash(buffer); - - // Copy it to the real path. - realpath = buffer; - } - - // Assume relative if we have something else here. - if (buffer.find_first_of(DIR_SEP) != 0 && isHome == false && isrelative == false) // BUG: This does not get called. - { - // Set isrelative to true and retrive the current working directory. - isrelative = true; - pCWD = getcwd(charArray, sizeof(charArray)); - buffer = pCWD; - - // Add a DIR_SEP to the string or we will mess up the directory name. - buffer += DIR_SEP; - - // Add all data from the Directory string because the string does not have a DIR_SEP at position zero. - buffer += directory.substr(0, size); - - // Remove the slash if it was added back. - buffer = RemoveTrailingSlash(buffer); - - // Copy it to the realpath. - realpath = buffer; - } - - // Get the parent directory out of the buffer and store it for later. - position = buffer.find_last_of(DIR_SEP); - parentbuffer = buffer.substr(0, position); - - // Check and see if the parent directory exists. - errorcode = FileUtills::CheckParent(directory); // Switch - - switch (errorcode) { - case -1: // Can't write to the parent directory exit the function. - return -1; - permission = 0; - break; - case -2: // Parent Directory does not exist, - if (createRecursive == false) - { - // Don't create the directory structure. - return -2; - } - break; - - case 0: // Parent Directory does exist and is writeable. - // since we can write to the directory skip the loop. - skiploop = true; - break; - default: // Ether there was an unknown error or this Arch / OS is not supported. ether way return -4. - return -4; - errorcode = 0; - break; - } - - // Clear the vars for the recursive directory creation loop. - permission = 0; - position = 0; - errorcode = 0; - - // Set buffer so we can check the path correctly. - // If we have a relative or user path use the real path. - if (isHome == true || isrelative == true) - { - buffer = realpath.substr(0, (realpath.find_first_of( DIR_SEP , 1))); - - // Also set the size of the string correctly. - size = realpath.size(); - } - else - { - // Otherwise use the directory string. - buffer = directory.substr(0, (directory.find_first_of( DIR_SEP, 1))); - - // Also set the size of the string correctly. - size = directory.size(); - } - - // If the parent directory does not exist and createRecursive is true then begin main loop. - while (skiploop != true) - { - // If we are at position 0 and there is a DIR_SEP there incremnt position by one. - if (position == 0 && buffer.find_first_of(DIR_SEP) == 0) - { - // Incremnt the position by one. - position++; - } - - // Start with root directory and run though the following until we reach the requested directory. - - if (isHome == true || isrelative == true) // If true use realpath + // Get the absolute path. + absPath = FileUtills::ResolvePath(absPath); + if (Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) { - if ((position = realpath.find_first_of(DIR_SEP, position)) == string::npos) + // Call helper. + result = FileUtills::CheckParent_Helper(absPath, read, write, exec); + if (result != COMMON_ERROR_SUCCESS) { - // This is the requested directory so exit the loop. - skiploop = true; - break; + // Check for a INVALID_ARGUMENT error. + if (result == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. (The caller does not need to see the invalid argument error.) + result = COMMON_ERROR_INTERNAL_ERROR; + } } } - else // Otherwise use directory. + else { - if ((position = directory.find_first_of(DIR_SEP, position)) == string::npos) - { - // This is the requested directory so exit the loop. - skiploop = true; - break; - } + // Call to ResolvePath() failed. + COMMON_LOG_VERBOSE("FileUtills::CheckParent(): Could not resolve the parent path. Unable to check parent's existance / permissions.\n"); } + } + else + { + // Invalid path. + result = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE("FileUtills::CheckParent(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(result)); + COMMON_LOG_VERBOSE(" Given path is invalid, aborting.\n"); + } - // Do a sanity check for the size of the string. - if (position < 0 || position > size) - { - // Ok something went wrong. - return -4; - } + // Copy result to commonLastErrorCode. + Common_commonLastErrorCode = result; - // Dump the last buffer into the parentdirectory buffer. - parentbuffer = buffer; + // Return the result. + return result; +} - // Cut up the directory string so we can check for it. +int FileUtills_GetParent(char ** retStr, size_t * retStrLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of a engine function call. */ + char * tempStr = NULL; /* Temporary pointer for the given c-string argument to work with. */ + size_t tempStrLength = 0; /* Temporary size_t for the given length argument to work with. */ + + /* Check for invalid arguments. */ + if ((retStr != NULL) && ((*retStr) != NULL) && (retStrLength != NULL)) + { + /* Copy the pointer and length. */ + tempStr = (*retStr); + tempStrLength = (*retStrLength); - if (isHome == true || isrelative == true) + /* Call FileUtills_ResolvePath(). */ + retFromCall = FileUtills_ResolvePath(&tempStr, &tempStrLength); + if (retFromCall == COMMON_ERROR_SUCCESS) { - buffer = realpath.substr(0, position); + /* Call FileUtills_Get_Last_Path_Component(). */ + ret = FileUtills_Get_Last_Path_Component(&tempStr, &tempStrLength, true); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Copy the pointer and length. */ + (*retStr) = tempStr; + (*retStrLength) = tempStrLength; + } } else { - buffer = directory.substr(0, position); + /* Could not resolve the given path. */ + ret = COMMON_ERROR_INTERNAL_ERROR; } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; - // Do Check. - errorcode = FileUtills::DoesExist(buffer); - - switch (errorcode){ - case -1: // Directory does not exist on system. - // Check and see if we can create it. - permission = FileUtills::CheckPermissions(parentbuffer); - - switch (permission){ - case -1: - // Permission Check Failed. - return false; - break; + /* Log error. */ + COMMON_LOG_DEBUG("DEBUG: FileUtills_GetParent(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } - case 0: - // Ok try and create the directory. - if ((errorcode = (mkdir(buffer.c_str(), S_IRWXU))) != 0) - { - // An error ocurred find out what. + /* Exit function. */ + return ret; +} - switch (errno) { - case EACCES: // Permission error. - return -1; - break; - case EEXIST: // Path exists on system. - return -3; - break; - case ENOSPC: // Out of disk space. - return -2; - break; - default: // We really don't care about the other errors. - return -4; - break; - } - } +int FileUtills::DeletePath(const std::string & path, const bool & recursive) +{ + // Init vars. + int result = COMMON_ERROR_UNKNOWN_ERROR; // Used to store results from calls to other functions. + std::string absPath = ""; // Used to contain the top level absolute path. - // Update the position counter. - position++; + // Avoid running ResolvePath() if the given path argument is empty. + if (path.size() > 0) + { + // Get the absolute path. + absPath = FileUtills::ResolvePath(path); - // Check and see if that was the last directory to create. - if (isHome == true || isrelative == true) + // Make sure we got the path. + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() > 0)) + { + // Call helper. + result = FileUtills::DeletePath_Helper(path, recursive); + if (result != COMMON_ERROR_SUCCESS) { - // Check realpath. - if (buffer == realpath) + // Check for a INVALID_ARGUMENT error. + if (result == COMMON_ERROR_INVALID_ARGUMENT) { - skiploop = true; - return 0; + // Set this to internal error. (The caller does not need to see the invalid argument error.) + result = COMMON_ERROR_INTERNAL_ERROR; } } - // Otherwise check the directory buffer. - if (buffer == directory) + } + else + { + // Log the error. + COMMON_LOG_INFO("FileUtills::DeletePath(): Unable to delete ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) Path resolution failed with error: "); + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPath.size() <= 0)) + { + // Internal error. + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_INFO(Common::Get_Error_Message(result)); + COMMON_LOG_INFO("\n"); + COMMON_LOG_WARNING("FileUtills::DeletePath(): ResolvePath() returned an empty / invalid absolute path while indicating success, Please report this bug."); + } + else { - skiploop = true; - return 0; + // Preserve the error code from ResolvePath(). + result = Common_commonLastErrorCode; + COMMON_LOG_INFO(Common::Get_Error_Message(result)); } - break; - - default: - // Bad errorcode exit function. - return -4; - break; - } - permission = 0; - errorcode = 0; - break; - - case 0: // Ok directory does exist. - position++; - break; - - default: - // Bad error code exit function. - return -4; + COMMON_LOG_INFO("\n"); } - - - - // If the next directory does exist then skip the remaining portion of the checks and restart with the next directory. - - // If the next directory does not exist the check the permissions of this one. - - // If we can write to this directory then create the directory. - - // If the directory creation was successful then check and see what to do next. - - // If we have created the requested directory exit the function - //return true. - - // If we have not created the requested directory contiune loop with the next - // directory. - - // If we encountered a fatal error exit the function return false. - - // If we can't write to this directory exit the function, return false. - - // If the permission check fails with a fatal error exit the function return false. - - // If the existance check fails exit the function, return false. } - - - - //If we have a relative or user path use realpath. - if (isHome == true || isrelative == true) + else { - buffer = realpath; + // Invalid path argument. + result = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG("FileUtills::DeletePath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" Given path argument is empty.\n"); } - else // Otherwise use the directory string. - { - buffer = directory; - } + // Copy result to Common_commonLastErrorCode. + Common_commonLastErrorCode = result; - // Ok try and create the directory. - if ((errorcode = (mkdir(buffer.c_str(), S_IRWXU))) != 0) - { - // An error ocurred find out what. - - switch (errno) { - case EACCES: // Permission error. - return -1; - break; - case EEXIST: // Path exists on system. - return -5; - break; - case ENOSPC: // Out of disk space. - return -4; - break; - default: // We really don't care about the other errors. - return -6; - break; - } - } - if (errorcode == 0) - { - return 0; - } -#endif - // If an implemetation does not exist for this platform then exit the function, return false. - return -3; + // Return the result. + return result; } -int FileUtills::CheckPermissions(const std::string & Filename, Panic::ERROR & error, bool read, bool write) +int FileUtills::CopyFile(const std::string & src, const std::string & dest, const bool & append, const streamsize & begOffset, const streamsize & endOffset) { - // Double check and make sure we have something to do. - if (read == false && write == false) + // Init vars. + int result = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + size_t amountOfBytesToCopy = 0; // Used to hold the amount of bytes to copy from the source file. + size_t x = 0; // Used to control the outer copy loop. + size_t y = 0; // Used to control the inner input copy loop. + size_t z = 0; // Used to control the inner output copy loop. + const size_t bufferSize = 512; // Size of the buffer to allocate. + char * ioBuffer = NULL; // Used to hold data in a buffer while it is being copied. + std::string absPathSrc = ""; // Used to fetch the absolute path of src. + std::string absPathDest = ""; // Used to fetch the absolute path of dest. + std::fstream input; // File stream for the input (source) file. + std::fstream output; // File stream for the output (destionation) file. + + // Check Arguments. + if (src.size() > 0) { - // Ok what? Call PanicHandler. - error.PanicHandler("FileUtills::CheckPermissions : CheckPermissions called but no permissions to check due to args. Returning -3"); + if (dest.size() > 0) + { + if (begOffset >= 0) + { + if (endOffset >= 0) + { + // Check and see if the offsets are valid. + if (((begOffset == 0) && (endOffset == 0)) || + ((begOffset == 0) && (endOffset > 0)) || + ((begOffset > 0) && (endOffset == 0)) || + ((begOffset > 0) && (endOffset > 0) && (endOffset > begOffset))) + { + // Set the amountOfBytesToCopy if we know the total amount. + if ((begOffset == 0) && (endOffset > 0)) + { + amountOfBytesToCopy = endOffset; + } + else + { + if ((begOffset > 0) && (endOffset > 0) && (endOffset > begOffset)) + { + amountOfBytesToCopy = (endOffset - begOffset); + } + } - // Return -3 - return -3; - } + // Get absolute path for src. + absPathSrc = FileUtills::ResolvePath(src); -#ifdef POSIX_COMMON_H - //init vars - int errorcode = 0; + // Check for error. + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPathSrc.size() > 0)) + { + // Get absolute path for dest. + absPathDest = FileUtills::ResolvePath(dest); - // Find out if file or directory exists - if ((errorcode = DoesExist(Filename, error)) == -1) - { - // File does not exist return -2. - return -2; - } + // Check for error. + if ((Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) && (absPathDest.size() > 0)) + { + // Check and see if src is a regular file. + if (FileUtills::IsFileOrDirectory(absPathSrc) == FILEUTILLS_ERROR_PATH_IS_A_FILE) + { + // Check and see if dest exists. + FileUtills::IsFileOrDirectory(absPathDest); + if ((Common_commonLastErrorCode == FILEUTILLS_ERROR_NON_EXISTANT) || + (Common_commonLastErrorCode == FILEUTILLS_ERROR_PATH_IS_A_FILE)) + { + // Check to see if the parent directory exists. + result = FileUtills::CheckParent(absPathDest); + if (result == COMMON_ERROR_SUCCESS) + { + // Open the dest file first. + if ((Common_commonLastErrorCode == FILEUTILLS_ERROR_PATH_IS_A_FILE) && + (append)) + { + // Open in append mode. + output.open(absPathDest.c_str(), std::ios::out | std::ios::binary | std::ios::app); + } + else + { + // Open in truncate mode. + output.open(absPathDest.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + } - // Also check and make sure no other error occured. - if (errorcode == -2) - { - // Something went wrong with DoesExist return -3. - return -3; - } + // Check and see if the dest file opened. + if (output.is_open()) + { + // If we are appending data jump to the end. + if (append) + { + output.seekp(0, std::ios::end); + } - // Reset errorcode - errorcode = 0; + // Open the input file. + input.open(absPathSrc.c_str(), std::ios::in | std::ios::binary); - // DoesExist returned ok, Begin check - if (read == true && write == false) - { - // Check for read only permission. - errorcode = access(Filename.c_str(), R_OK); - if (errorcode != 0) - { - // File or Directory is not readable return -1 - return -1; - } - if (errorcode == 0) - { - // File or directory is readable return 0. - return 0; - } + // Check and see if the input file opened. + if (input.is_open()) + { + // Calculate the size of the input file, and determine if we need to take into account an offset. (amountOfBytesToCopy will be greater than zero if we do.) + input.seekg(0, std::ios::end); + if (amountOfBytesToCopy > 0) + { + // Check and see if we have a file big enough to have that many bytes. + if (input.tellg() >= (begOffset + amountOfBytesToCopy)) + { + // Jump to the correct beginning offset in the input file. + input.seekg(begOffset, std::ios::beg); + } + else + { + // File is not big enough to copy the requested byte range. + result = COMMON_ERROR_RANGE_ERROR; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" The given source file ( "); + COMMON_LOG_DEBUG(absPathSrc.c_str()); + COMMON_LOG_DEBUG(" ) is too small. Either the beginning offset itself, or the beginning offset plus the range of bytes to copy, is beyond the end of the source file. Aborting.\n"); + + // Reset amountOfBytesToCopy. + amountOfBytesToCopy = 0; + } + } + else + { + // Copy the entire file. (Only if both offsets are equal to zero.) + amountOfBytesToCopy = input.tellg(); - // Unknown error call Panic Handler - error.PanicHandler("FileUtills::CheckPermissions : Unknown Error occured during read permission check. Returning -3"); - return -3; - } + // Seek back to the beginning og the source file. + input.seekg(0, std::ios::beg); + } - if (read == false && write == true) - { - // Check for write only permission. (odd but for rundundantcy.) - errorcode = access(Filename.c_str(), W_OK); + // Allocate our memory buffer. + try { + ioBuffer = (char*)malloc(bufferSize); + + // Check for an allocated buffer. + if (ioBuffer != NULL) + { + // Begin copy loop. + for (x = 0; ((x < amountOfBytesToCopy) && (result == COMMON_ERROR_SUCCESS));) + { + // Start internal buffer loop. + for (y = 0; ((y < bufferSize) && ((x + y) < amountOfBytesToCopy) && (result == COMMON_ERROR_SUCCESS)); y++) + { + // Copy the data. + ioBuffer[(x + y)] = input.get(); + + // Check the input stream. + if (!input.good()) + { + // I/O error. + result = COMMON_ERROR_IO_ERROR; + + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::CopyFile(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(result)); + COMMON_LOG_VERBOSE(" Input file stream failed."); + COMMON_LOG_VERBOSE("\n"); + } + } - if (errorcode != 0) - { - // File or Directory is not writeable return -1 - return -1; - } - if (errorcode == 0) - { - // File or directory is writeable return 0. - return 0; - } + // Output the data. + for (z = 0; ((z < y) && (result == COMMON_ERROR_SUCCESS)); z++) + { + output.put(ioBuffer[z]); + + // Check the output stream. + if (!output.good()) + { + // I/O error. + result = COMMON_ERROR_IO_ERROR; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_VERBOSE(" Output file stream failed."); + COMMON_LOG_DEBUG("\n"); + } + } - // Unknown error call Panic Handler - error.PanicHandler("FileUtills::CheckPermissions : Unknown Error occured during write permission check. Returning -3"); - return -3; - } + // Flush the output buffer. + if (output.good()) + { + output.flush(); + } + + // Increment x. + x += y; + } + } + else + { + // Could not allocate memory buffer. + result = COMMON_ERROR_MEMORY_ERROR; - if (read == true && write == true) - { - // Check for read and write permissions. - errorcode = access(Filename.c_str(), R_OK); - if (errorcode != 0) - { - // File or directory is not readable. it has failed one critera for check, return -1. - return -1; - } - if (errorcode == 0) - { - // File or directory is readable check for write permission. - errorcode = access(Filename.c_str(), W_OK); + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::CopyFile(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(result)); + COMMON_LOG_VERBOSE("\n"); + } + } + catch(exception &ex) + { + // Exception thrown. + result = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::CopyFile(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(result)); + COMMON_LOG_VERBOSE(" "); + COMMON_LOG_VERBOSE(ex.what()); + COMMON_LOG_VERBOSE("\n"); + } - if (errorcode != 0) - { - // File or Directory is not writeable return -1 - return -1; - } - if (errorcode == 0) - { - // File or directory is writeable return 0. - return 0; + // Free the buffer if needed. + if (ioBuffer != NULL) + { + free(ioBuffer); + ioBuffer = NULL; + } + + // Close the input and output files. + if (input.is_open()) + { + input.close(); + } + if (output.is_open()) + { + output.close(); + } + + // Log file creation. + if (result == COMMON_ERROR_SUCCESS) + { + COMMON_LOG_INFO("FileUtills::CopyFile(): Copied file ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) successfully.\n"); + } + else + { + COMMON_LOG_INFO("FileUtills::CopyFile(): File ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) created, but coping was unsuccessful.\n"); + } + } + else + { + // Could not open the source file for reading. + result = COMMON_ERROR_IO_ERROR; + + // Log error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" Unable to open ( "); + COMMON_LOG_DEBUG(absPathSrc.c_str()); + COMMON_LOG_DEBUG(" ) for reading.\n"); + + // Close the output file. + output.close(); + } + } + else + { + // Could not open the destionation file for writing. + result = COMMON_ERROR_IO_ERROR; + + // Log error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" Unable to open ( "); + COMMON_LOG_DEBUG(absPathDest.c_str()); + COMMON_LOG_DEBUG(" ) for writing.\n"); + } + } + else + { + // Check for non-existant parent directory. + if (result == FILEUTILLS_ERROR_NON_EXISTANT) + { + COMMON_LOG_INFO("FileUtills::CopyFile(): Destionation directory (or a path componet leading up to it) does not exist, CopyFile() does not create non-existant directories, call CopyPath() instead. Aborting.\n"); + } + else + { + COMMON_LOG_INFO("FileUtills::CopyFile(): Error while checking destionation parent directory, aborting.\n"); + } + } + } + else + { + // Check for a directory. + if (Common_commonLastErrorCode == FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) + { + // Cannot overwrite a directory with a file. + COMMON_LOG_INFO("FileUtills::CopyFile(): Cannot overwrite a directory with a file, aborting.\n"); + } + else + { + // Other error, abort. + COMMON_LOG_INFO("FileUtills::CopyFile(): Unable to determine the destionation path's type / existance, aborting.\n"); + } + } + } + else + { + // Check error. + if ((Common_commonLastErrorCode == FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) || + (Common_commonLastErrorCode == COMMON_ERROR_SUCCESS)) + { + // Unable to copy a directory or special file. (COMMON_ERROR_SUCCESS is used to indicate path existance when it is not a regular file or directory.) + COMMON_LOG_DEBUG("FileUtills::CopyFile(): Unable to copy non-regular files or directories, aborting.\n"); + } + else + { + // Got an error, abort. + COMMON_LOG_VERBOSE("FileUtills::CopyFile(): Unable to determine the source path's type / existance, aborting.\n"); + } + } + } + else + { + // Unable to get destionation absolute path. + COMMON_LOG_VERBOSE("Unable to get destionation absolute path, aborting.\n"); + + // Make sure Common_commonLastErrorCode is set to something. + if (absPathDest.size() <= 0) + { + result = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else + { + // Unable to get source absolute path. + COMMON_LOG_VERBOSE("Unable to get source absolute path, aborting.\n"); + + // Make sure Common_commonLastErrorCode is set to something. + if (absPathSrc.size() <= 0) + { + result = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else + { + // Offsets are switched. + result = COMMON_ERROR_INVALID_ARGUMENT; + + // Log error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" Your offsets are invalid. (Really the only way this is possible is if you either reverse the beginning and ending offsets, or if you have an offset that is less than zero.) Aborting.\n"); + } + } + else + { + // endOffset is invalid. + result = COMMON_ERROR_INVALID_ARGUMENT; + + // Log error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" ending offset argument is invalid.\n"); + } } else { - // Unknown error call panic handler. - error.PanicHandler("FileUtills::CheckPermissions : Unknown Error occured during write permission check. Returning -3"); - return -3; + // begOffset is invalid. + result = COMMON_ERROR_INVALID_ARGUMENT; + + // Log error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" beginning offset argument is invalid.\n"); } } else { - // Unknown error call panic handler. - error.PanicHandler("FileUtills::CheckPermissions : Unknown Error occured during read permission check. Returning -3"); - return -3; + // dest is invalid. + result = COMMON_ERROR_INVALID_ARGUMENT; + + // Log error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" destionation path argument is invalid.\n"); } } - -#endif - // Call Panic Handler there is no OS / arch impilmentation. - error.PanicHandler("FileUtills::CheckPermissions : OS / arch not supported. No Implementation found. Returning -3."); - - // Default Return - return -3; -} - - -int FileUtills::CheckPermissions(const std::string & path, bool read, bool write) -{ - // Double check and make sure we have something to do. - if (read == false && write == false) + else { - // Return -3 - return -3; - } - -#ifdef POSIX_COMMON_H - //init vars - int errorcode = 0; + // Src is invalid. + result = COMMON_ERROR_INVALID_ARGUMENT; - // Find out if file or directory exists - if ((errorcode = DoesExist(path)) == -1) - { - // File does not exist return -2. - return -2; + // Log error. + COMMON_LOG_DEBUG("FileUtills::CopyFile(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" source path argument is invalid.\n"); } - // Also check and make sure no other error occured. - if (errorcode == -2) - { - // Something went wrong with DoesExist return -3. - return -3; - } + // Copy result to Common_commonLastErrorCode. + Common_commonLastErrorCode = result; - // Reset errorcode - errorcode = 0; + // Return the result. + return result; +} - // DoesExist returned ok, Begin check - if (read == true && write == false) - { - // Check for read only permission. - errorcode = access(path.c_str(), R_OK); - if (errorcode != 0) - { - // File or Directory is not readable return -1 - return -1; - } - if (errorcode == 0) - { - // File or directory is readable return 0. - return 0; - } - // Unknown error - return -3; - } +int FileUtills::CopyPathData(const std::string & src, const std::string & dest, const bool & recursive, + const bool & rename, const bool & abort_on_failure, + const bool & append, const streamsize & begOffset, const streamsize & endOffset) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; - if (read == false && write == true) - { - // Check for write only permission. (odd but for rundundantcy.) - errorcode = access(path.c_str(), W_OK); + // LOG INCOMPLETE FUNCTION. + COMMON_LOG_WARNING("FileUtills::CopyPathData(): "); + COMMON_LOG_WARNING(Common::Get_Error_Message(ret)); + COMMON_LOG_WARNING("\n"); - if (errorcode != 0) - { - // File or Directory is not writeable return -1 - return -1; - } - if (errorcode == 0) - { - // File or directory is writeable return 0. - return 0; - } + // Copy result to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; - // Unknown error - return -3; - } + // Return the result. + return ret; +} + +int FileUtills::CopyPath(const std::string & src, const std::string & dest, const bool & recursive, + const bool & useSyscall, const bool & useRename) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + std::string absPathSrc = ""; // The absolute version of the given source path. + std::string absPathDest = ""; // The absolute version of the given destionation path. - if (read == true && write == true) + // Call FileUtills::ResolvePath() for the source path. + absPathSrc = FileUtills::ResolvePath(src); + if (Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) { - // Check for read and write permissions. - errorcode = access(path.c_str(), R_OK); - if (errorcode != 0) - { - // File or directory is not readable. it has failed one critera for check, return -1. - return -1; - } - if (errorcode == 0) + // Check for valid result. + if (absPathSrc.size() > 0) { - // File or directory is readable check for write permission. - errorcode = access(path.c_str(), W_OK); + // Resolve the destionation path. + absPathDest = FileUtills::ResolvePath(dest); + if (Common_commonLastErrorCode == COMMON_ERROR_SUCCESS) + { + // Check for valid result. + if (absPathDest.size() > 0) + { + // Call helper function. + ret = FileUtills::CopyPath_Helper(absPathSrc, absPathDest, recursive, useSyscall, useRename); + if (ret == COMMON_ERROR_SUCCESS) + { + // Log success. + COMMON_LOG_INFO("FileUtills::CopyPath(): Copied ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) successfully.\n"); + } + else + { + // Check for invalid argument. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. (The caller does not need to see the invalid argument error.) + ret = COMMON_ERROR_INTERNAL_ERROR; + } - if (errorcode != 0) - { - // File or Directory is not writeable return -1 - return -1; - } - if (errorcode == 0) - { - // File or directory is writeable return 0. - return 0; + // Log the error. + COMMON_LOG_INFO("FileUtills::CopyPath(): Unable to copy ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) successfully. Some files / directories may have been copied. Please double check manually.\n"); + } + } + else + { + // Could not resolve the destionation path. (Success without result.) + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING("FileUtills::CopyPath(): "); + COMMON_LOG_WARNING(Common::Get_Error_Message(ret)); + COMMON_LOG_WARNING(" Could not resolve the destionation path, FileUtills::ResolvePath() returned an invalid path but indicated success, Please report this bug.\n"); + } } else { - // Unknown error - return -3; + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Could not resolve the destionation path, aborting.\n"); } } else { - // Unknown error - return -3; + // Could not resolve the source path. (Success without result.) + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING("FileUtills::CopyPath(): "); + COMMON_LOG_WARNING(Common::Get_Error_Message(ret)); + COMMON_LOG_WARNING(" Could not resolve the source path, FileUtills::ResolvePath() returned an invalid path but indicated success, Please report this bug.\n"); } } + else + { + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Could not resolve the destionation path, aborting.\n"); + } -#endif - // there is no OS / arch impilmentation return default. - // Default Return - return -3; -} - - -int FileUtills::DoesExist(const std::string & Filename, Panic::ERROR & error) -{ -#ifdef POSIX_COMMON_H - - // Declare some vars. - int errorcode = 0; // Holds error number. - - // Check and see if the file exists. - errorcode = access(Filename.c_str(), F_OK); - - // Get error code. - if (errorcode == 0) - { - // File exists return ok - return 0; - } - if (errorcode == -1) - { - // File does not exist return false. - return -1; - } - else - { - // Ok What happened? Call PanicHandler - error.PanicHandler("FileUtills::DoesExist : Unknown Error, returning false on file exist check."); - } -#endif - // Call Panic Handler arch / OS not supported. - error.PanicHandler("FileUtils::DoesExist : OS / arch not supported. No Implementation found. Returning -2."); - - // Default return. - return -2; -} + // Copy ret to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; -int FileUtills::DoesExist(const std::string & Filename) + // Return the result. + return ret; +} + // Below is the Helper function for CopyPath(). + + rewrite directory parser loop with the one from DeletePath(). + +int FileUtills::CopyPath_Helper(const std::string & absPathSrc, const std::string & absPathDest, const bool & recursive, const bool & useSyscall, const bool & useRename) { -#ifdef POSIX_COMMON_H - - // Declare some vars. - int errorcode = 0; // Holds error number. - - // Check and see if the file exists. - errorcode = access(Filename.c_str(), F_OK); - - // Get error code. - if (errorcode == 0) - { - // File exists return ok - return 0; - } - if (errorcode == -1) - { - // File does not exist return false. - return -1; - } - else - { - // Unknown error return false. - return -1; - } -#endif - // arch / OS not supported. - - // Default return. - return -2; -} + // Init vars. + int ret = COMMON_ERROR_SUCCESS; // Used to hold the result of this function. + char * pCurrentPathSegment = NULL; // Used to allocate memory for a substring that contains the current path segment. + std::string absPathDestFixedFileName = ""; // Used to copy a source path with a different destionation name. -int FileUtills::IsFileOrDirectory(const std::string & path) -{ - // Make sure that the path is not empty. - if (path.size() <= 0) - { - // Empty path. - return -5; - } - -#ifdef POSIX_COMMON_H - // Init vars. - int result = 0; - int call_result = 0; - struct stat * buf = NULL; - - // Init stat buffer. - buf = new struct stat; - - // Ask the OS to stat() the path. - call_result = stat(path.c_str(), buf); - - // Check the result. - if (call_result == 0) - { - // Check the stat structure. - if (S_ISREG((buf->st_mode))) - { - // Path is a regular file. - result = 1; - } - else + // Check for valid absPathSrc. + if (absPathSrc.size() > 0) + { + // Check for valid absPathDest. + if (absPathDest.size() > 0) { - if (S_ISDIR((buf->st_mode))) + // Determine the type of source. (File or directory.) + ret = FileUtills::IsFileOrDirectory_Helper(absPathSrc); + if ((ret == FILEUTILLS_ERROR_PATH_IS_A_FILE) || (ret == COMMON_ERROR_SUCCESS)) + { + // Check and see if the source is a non-regular file, and if the copy syscall use is disabled. + if ((ret == COMMON_ERROR_SUCCESS) && (!useSyscall)) + { + // We cannot copy a non-regular file, without using a syscall. + COMMON_LOG_INFO("FileUtills::CopyPath(): Cannot copy source ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to destionation ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) Cannot copy a non-regular file without using a host's syscall. Caller has disabled use of a host's syscall. Aborting.\n"); + } + else + { + // Check and see if the destionation path exists. + ret = FileUtills::IsFileOrDirectory_Helper(absPathDest); + if (ret == FILEUTILLS_ERROR_NON_EXISTANT) + { + // Destionation path does not exist. Assume the last path segment in the destionation path is the file name of the copied file. + // OK, check and see if the parent exists. + ret = FileUtills::CheckParent_Helper(absPathDest, true, true, true); + if (ret == COMMON_ERROR_SUCCESS) + { + // OK Copy the source file to the destionation directory. + // Using the given last path segment in the destionation path as the copied file's name. + if (useSyscall && useRename) + { + // Use the rename syscall. + ret = FileUtills::RenameFile_Syscall(absPathSrc, absPathDest); + if (ret == COMMON_ERROR_SUCCESS) + { + // Log the copied file. + COMMON_LOG_INFO("FileUtills::CopyPath(): Copied ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to destionation ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) successfully.\n"); + } + else + { + // An error occured. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Error ( "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" ) was returned from the RenameFile syscall. Unable to copy ( "); + COMMON_LOG_DEBUG(absPathSrc.c_str()); + COMMON_LOG_DEBUG(" ) to destionation ( "); + COMMON_LOG_DEBUG(absPathDest.c_str()); + COMMON_LOG_DEBUG(" ).\n"); + + // If ret was invalid argument change it to internal error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else + { + // Check to see if we are using the syscall. + if (useSyscall) + { + // Copy using the host's copy syscall. + ret = FileUtills::CopyPath_Syscall(absPathSrc, absPathDest); + if (ret == COMMON_ERROR_SUCCESS) + { + // Log the copied file. + COMMON_LOG_INFO("FileUtills::CopyPath(): Copied ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to destionation ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) successfully.\n"); + } + else + { + // An error occured. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Error ( "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" ) was returned from the CopyFile syscall. Unable to copy ( "); + COMMON_LOG_DEBUG(absPathSrc.c_str()); + COMMON_LOG_DEBUG(" ) to destionation ( "); + COMMON_LOG_DEBUG(absPathDest.c_str()); + COMMON_LOG_DEBUG(" ).\n"); + + // If ret was invalid argument change it to internal error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else + { + // Copy using the CopyPathData() function. + } + } + } + else + { + // An error occured. + } + } + else + { + // Check and see if the destionation path is a directory. + if (ret == FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) + { + // The last path segment in the destionation path is a directory, + // assume the copied file's name is the same as the source file. + + // Copy absPathDest to absPathDestFixedFileName and add a DIR_SEP. + absPathDestFixedFileName = absPathDest; + absPathDestFixedFileName += DIR_SEP; + + // We need to copy the last path segment of the source to the destionation. + // (So the copied file will have the same name as the source file.) + ret = FileUtills::GetLastPathSegment(absPathSrc, absPathDestFixedFileName, false); + if (ret == COMMON_ERROR_SUCCESS) + { + // Check for success without result. + if (absPathDestFixedFileName.size() > (absPathDest.size() + sizeof(DIR_SEP))) + { + // Check and see if we are using the syscalls. + if (useSyscall && useRename) + { + // Use the rename syscall. + ret = FileUtills::RenameFile_Syscall(absPathSrc, absPathDestFixedFileName); + if (ret == COMMON_ERROR_SUCCESS) + { + // Log the copied file. + COMMON_LOG_INFO("FileUtills::CopyPath(): Copied ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to destionation ( "); + COMMON_LOG_INFO(absPathDestFixedFileName.c_str()); + COMMON_LOG_INFO(" ) successfully.\n"); + } + else + { + // An error occured. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Error ( "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" ) was returned from the CopyFile syscall. Unable to copy ( "); + COMMON_LOG_DEBUG(absPathSrc.c_str()); + COMMON_LOG_DEBUG(" ) to destionation ( "); + COMMON_LOG_DEBUG(absPathDestFixedFileName.c_str()); + COMMON_LOG_DEBUG(" ).\n"); + + // If ret was invalid argument change it to internal error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else + { + // Check and see if we are using the host's copy syscall. + if (useSyscall) + { + // OK, copy the source file to the destionation directory, using the syscall. + ret = FileUtills::CopyPath_Syscall(absPathSrc, absPathDestFixedFileName); + if (ret == COMMON_ERROR_SUCCESS) + { + // Log the copied file. + COMMON_LOG_INFO("FileUtills::CopyPath(): Copied ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to destionation ( "); + COMMON_LOG_INFO(absPathDestFixedFileName.c_str()); + COMMON_LOG_INFO(" ) successfully.\n"); + } + else + { + // An error occured. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Error ( "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" ) was returned from the CopyFile syscall. Unable to copy ( "); + COMMON_LOG_DEBUG(absPathSrc.c_str()); + COMMON_LOG_DEBUG(" ) to destionation ( "); + COMMON_LOG_DEBUG(absPathDestFixedFileName.c_str()); + COMMON_LOG_DEBUG(" ).\n"); + + // If ret was invalid argument change it to internal error. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else + { + // Copy using the CopyPathData() function. + } + } + else + { + // We got success without a result. + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::CopyPath(): "); + COMMON_LOG_WARNING(Common::Get_Error_Message(ret)); + COMMON_LOG_WARNING(" FileUtills::GetLastPathSegment() returned an invalid path while indicating success. Please report this bug.\n"); + } + } + else + { + // Log the error. + + + // Could not copy the file name. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + // Set this to internal error. + ret = COMMON_ERROR_INTERNAL_ERROR; + } + } + } + else + { + // An error occured. + } + } + } + } + else { - // Path is a directory. - result = 2; + // Check and see if the source is a directory. + if (ret == FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) + { + // Check and see if copy recursive is false. + if (recursive) + { + // Check and see if the destionation exists. + ret = FileUtills::IsFileOrDirectory_Helper(absPathDest); + if (ret == FILEUTILLS_ERROR_NON_EXISTANT) + { + // Destionation path does not exist, assume that the destionation path's + // last path segment is the name to use for the copied directory. + + // Check and see if the parent exists. + ret = FileUtills::CheckParent_Helper(absPathDest, true, true, true); + if (ret == COMMON_ERROR_SUCCESS) + { + // Ok, create the destionation path. + ret = FileUtills::CreateDirectory_Helper(absPathDest); + if (ret != COMMON_ERROR_SUCCESS) + { + // We cannot create the destionation directory. + COMMON_LOG_INFO("FileUtills::CopyPath(): Cannot copy source ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to destionation ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) unable to create destionation directory. Aborting.\n"); + } + } + else + { + // An error occured. + } + } + else + { + // Check and see if the destionation is a pre-existing directory. + // If the destionation is a pre-existing directory, assume we need + // to merge the source directory's contents with the destionation's + // contents. + if (ret != FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) + { + // Check and see if the destionation path is a file. + if ((ret == COMMON_ERROR_SUCCESS) || (ret == FILEUTILLS_ERROR_PATH_IS_A_FILE)) + { + // Indicate failure. + ret = FILEUTILLS_ERROR_PATH_IS_A_FILE; + + // Cannot overwrite a file with a directory. + COMMON_LOG_INFO("FileUtills::CopyPath(): Cannot copy source ( "); + COMMON_LOG_INFO(absPathSrc.c_str()); + COMMON_LOG_INFO(" ) to destionation ( "); + COMMON_LOG_INFO(absPathDest.c_str()); + COMMON_LOG_INFO(" ) Cannot overwrite a pre-existing file with a directory. Aborting.\n"); + } + else + { + // An error occured. + } + } + } + + // Check and see if we can continue. (If the destionation path was non-existant then ret would be COMMON_ERROR_SUCCESS if the directory was created.) + if ((ret == FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) || (ret == COMMON_ERROR_SUCCESS)) + { + // Begin try block. + try { + // Begin recursion loop. + } + catch (exception &ex) + { + // Exception thrown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" "); + COMMON_LOG_DEBUG(ex.what()); + COMMON_LOG_DEBUG("\n"); + } + } + } + else + { + // Cannot copy a directory if recursion is disabled by the caller. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Cannot copy source ( "); + COMMON_LOG_DEBUG(absPathSrc.c_str()); + COMMON_LOG_DEBUG(" ) to destionation ( "); + COMMON_LOG_DEBUG(absPathDest.c_str()); + COMMON_LOG_DEBUG(" ) because recursion is disabled by the caller. Aborting.\n"); + } + } + else + { + // An error occured. + } } - } - } - else - { - // Error, see if it was a permissions error. - switch (errno) - { - case EACCES: - // Permissions error. - result = -4; - break; - case ENOENT: - // A path componet does not exist. - result = -6; - break; - case ENOTDIR: - // Path has a file in it, and is not the final componet. - result = -7; - break; - default: - // Some other error. - result = -9; - break; - }; - } + + + // Run copy loop. + for (size_t x = (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1); ((x < absPath.size()) && (result == COMMON_ERROR_SUCCESS)); x++) + { + // Search for the next DIR_SEP, or the end of the given path string. + if ((absPath[x] == DIR_SEP) || ((x + 1) == absPath.size())) + { + // This is the end of the next path segment, create the needed substring and create the directory. + try { + // Allocate memory buffer. + if (absPath[x] != DIR_SEP) + { + // Allocate an extra character only if we are before a directory seperator. + pCurrentPathSegment = (char*)malloc((x + 1)); + } + else + { + // If the current char is a directory seperator, then we need to allocate only the current x value. + pCurrentPathSegment = (char*)malloc(x); + } - // Delete stat buffer. - delete buf; - buf = NULL; + // Check and make sure we got the memory allocated. + if (pCurrentPathSegment != NULL) + { + // Check and see if the current char is a DIR_SEP. (Controls how much of the source buffer we need to copy.) + if (absPath[x] != DIR_SEP) + { + // Copy the data. + for (size_t y = 0; (y < (x + 1)); y++) + { + pCurrentPathSegment[y] = absPath[y]; + } - // Exit function. - return result; -#endif + // Terminate the string. (Arrays start at zero, x is the last valid character in the string.) + pCurrentPathSegment[x] = '\0'; + } + else + { + // Copy the data. + for (size_t y = 0; (y < x); y++) + { + pCurrentPathSegment[y] = absPath[y]; + } - // System unsupported, why not add support for it? :) - return -3; -} + // Terminate the string. (Arrays start at zero, (x - 1) is the last valid character in the string.) + pCurrentPathSegment[(x - 1)] = '\0'; + } -int FileUtills::CheckParent(const std::string & path, bool read, bool write) -{ -#ifdef POSIX_COMMON_H - // Init vars - std::string parentbuffer = ""; - long int length = 0; - long int location = 0; - int result = 0; - - // Remove the trailing slash if present. - parentbuffer = RemoveTrailingSlash(path); - - // Check the Path for absolute path. - parentbuffer = FileUtills::CheckPathType(parentbuffer); - - // Check the length of the path. - length = parentbuffer.length(); - - // Check and see if the length is zero. - if (length == 0) - { - // Nothing to do so return that the path does not exist. (It clearly does not exist.) - return -2; - } - - // Locate the path seperator before the last directory / file name. - location = parentbuffer.find_last_of(DIR_SEP); - - // Check and make sure that the location of the dir sep is inside the string - if (location > length || length < 0 || location < 0) - { - // Memory error or someone is fucking with us. - // Either way kill the function - return -5; - } - - // Check and see if the parentbuffer is a root directory - if (length > 1) - { - - // Get the parent directory path. - // Note substr CAN throw an exception. so we need to put this in a try catch block. - try{ - parentbuffer = parentbuffer.substr(0, location); - } - catch(exception &ex) - { - // If we end up here we're in trouble. - // Kill the function to be safe. - return -5; - } - - // Now rerun the check for a dir sep on the end. - parentbuffer = RemoveTrailingSlash(parentbuffer); - } - - // Call the other functions - result = FileUtills::CheckPermissions(parentbuffer, read, write); - - // Check the result - switch (result) - { - // Successful - case 0: - return 0; - break; - // Permission error - case -1: - return -1; - break; - // Parent Does not exist - case -2: - return -2; - break; - // Unknown error (Probabliy a memory error) - default: - return -5; - break; - }; -#endif - // arch / OS not supported. - return -3; -} + // Now create the path. (Issue system call.) + if ((FileUtills::CreateDirectory_Syscall(pCurrentPathSegment) != COMMON_ERROR_SUCCESS) || (Common_commonLastErrorCode != COMMON_ERROR_SUCCESS)) + { + // Check and see if the error code is FILEUTILLS_ERROR_EXISTANT. (This is only an error if the final directory segment cannot be created.) + if ((Common_commonLastErrorCode != FILEUTILLS_ERROR_EXISTANT) || ((Common_commonLastErrorCode == FILEUTILLS_ERROR_EXISTANT) && ((x + 1) >= absPath.size()))) + { + // Copy the error. + result = Common_commonLastErrorCode; + + // Log the error if needed. + COMMON_LOG_INFO("FileUtills::CreateDirectory(): "); + COMMON_LOG_INFO(Common::Get_Error_Message(result)); + COMMON_LOG_INFO(" Unable to create directory ( "); + COMMON_LOG_INFO(pCurrentPathSegment); + COMMON_LOG_INFO(" ), aborting.\n"); + + // Force the loop to exit. + x = absPath.size(); + } + } + else + { + // Report success. + COMMON_LOG_INFO("FileUtills::CreateDirectory(): Created directory ( "); + COMMON_LOG_INFO(pCurrentPathSegment); + COMMON_LOG_INFO(" )\n"); + } -std::string FileUtills::GetParent(const std::string & path) -{ - // Init vars. - std::string parentbuffer = ""; - long int length = 0; - long int location = 0; - -#ifdef POSIX_COMMON_H - // Remove the trailing slash if present. - parentbuffer = RemoveTrailingSlash(path); - - // Check the Path for absolute path. - parentbuffer = FileUtills::CheckPathType(parentbuffer); - - // Check the length of the path. - length = parentbuffer.length(); - - // Check and see if the length is zero. - if (length == 0) - { - // Nothing to do so return that the path does not exist. (It clearly does not exist.) - parentbuffer = ""; - return parentbuffer; - } - - // Locate the path seperator before the last directory / file name. - location = parentbuffer.find_last_of(DIR_SEP); - - // Check and make sure that the location of the dir sep is inside the string - if (location > length || length < 0 || location < 0) - { - // Memory error or someone is fucking with us. - // Either way kill the function - parentbuffer = ""; - return parentbuffer; - } - - // Check and see if the parentbuffer is a root directory - if (length > 1) - { - // Get the parent directory path. - // Note substr CAN throw an exception. so we need to put this in a try catch block. - try{ - parentbuffer = parentbuffer.substr(0, location); - } - catch(exception &ex) - { - // If we end up here we're in trouble. - // Kill the function to be safe. - parentbuffer = ""; - return parentbuffer; - } - - // Now rerun the check for a dir sep on the end. - parentbuffer = RemoveTrailingSlash(parentbuffer); - - // Exit function. - return parentbuffer; - - } -#endif - // arch / OS not supported. - parentbuffer = ""; - return parentbuffer; + // Deallocate the buffer. + if (pCurrentPathSegment != NULL) + { + free(pCurrentPathSegment); + pCurrentPathSegment = NULL; + } + } + else + { + // Could not allocate memory. + result = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG("FileUtills::CreateDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG("\n"); + + // Force loop to exit. + x = absPath.size(); + } + } + catch(exception &ex) + { + // Exception thrown. + result = COMMON_ERROR_EXCEPTION_THROWN; + COMMON_LOG_DEBUG("FileUtills::CreateDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" "); + COMMON_LOG_DEBUG(ex.what()); + COMMON_LOG_DEBUG("\n"); -} + // Force loop to exit. + x = absPath.size(); + } + } + } + } + } + else + { + // Invalid destionation path. + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given destionation path is invalid.\n"); + } + } + else + { + // Invalid source path. + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given source path is invalid.\n"); + } -short FileUtills::DeletePath(const std::string & path, const bool & recursive) -{ -#ifdef POSIX_COMMON_H - // Init vars. - bool done = false; // Used to know when to exit the recursive iterator loop. - bool resetIterLoop = false; // Used to control resetting the recursive iterator loop. - bool unableToDeleteAll = false; // Used to tell if we could not delete something while deleting recursively. - int result = 0; // Used to store results from calls to other functions. - size_t x = 0; // Used by the recursion loop, for accessing the directory list. - std::string currentabspath = ""; // Used to contain the current absolute path. - std::string dummy = ""; // Used to construct temporary paths. - FileUtills::dirlist * plist = NULL; // Used to store paths for recursive deletions. - std::vector::iterator iter; // plist->list (Directory Listing). - std::vector directory_offsets; // Used to store the offsets in the directory listings. - - // Check and see if the file can be erased. - result = FileUtills::CheckPermissions(path); - - // Attempt to delete the path if the OS permissions allow it. - if (result == 0) - { - // Call removal function. - if (remove(path.c_str()) != 0) - { - // Check the error. - if (errno == EACCES) - { - // Permissions error. - return -1; - } - if (errno == EPERM) - { - // Permissions error. - return -1; - } - if (errno == ENOTEMPTY) // Path is a non empty directory. - { - // If we have recursive set to true we can delete the path, else return an error. - if (recursive) - { - // Copy the src path to the currentabspath var. - currentabspath = path; - - // Begin recursion loop. - do - { - // OK we need to get the paths of every file in the directory and any other directories. - plist = NULL; - plist = FileUtills::getDirectory(currentabspath, true); - if (plist == NULL) - { - // Could not get directory list. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - return -4; - } - - // Reset the iter just to be safe. - iter = plist->list.begin(); - - // Check and see if the directory offset list has anything in it. - if ((!resetIterLoop) && (directory_offsets.size() > 0)) - { - // We have an offset, so init x to that offset. - x = directory_offsets[(directory_offsets.size() - 1)]; // size() returns the number of valid elements in the vector. - - // Fix the iterator. - iter = iter + x; - - // Erase the offset from the vector. - directory_offsets.erase((directory_offsets.end() - 1)); // end() returns the position after the last valid element in the vector. - } - else - { - // We don't have any offsets, (or we just switched to a subdirectory) so init x to zero. - x = 0; - - // Unset resetIterLoop. - resetIterLoop = false; - } - - // Begin loop to check for more subdirectories. - for (; x < plist->list.size(); x++) - { - // Create temp path. - dummy.clear(); - dummy = currentabspath + DIR_SEP + (plist->list[x]); - dummy = RemoveTrailingSlash(dummy); // If we got a DIR_SEP at the end of the string get rid of it. - - // Check to see if the current entry in the list is a directory. - switch (FileUtills::IsFileOrDirectory(dummy)) - { - case 0: // Not a file or directory. (Try to remove anyway.) - case 1: // File. - // Attempt to delete file. - if (remove(dummy.c_str()) != 0) - { - // Set unableToDeleteAll. - unableToDeleteAll = true; - - /* - Note: We need someway of keeping track of what - files we've already attempted to delete, - Otherwise we'll end up in an indefinte loop. - - The easist way to to have the list generated the same - way each time and store the offsets. (I.e each call to - FileUtills::getDirectory() returns the same list. As - opposed to just the way the OS spits out the directory - listing.) - - Then when the subdirectory is removed, (or after the failed removal - attempt) find where that subdirectory was in the list and continue. - */ - } - break; - case 2: // Directory. - // Set currentabspath to this path. - currentabspath = dummy; - - // Set the current directory list offset (x) in to the vector. - directory_offsets.push_back(x); - - // Set reset flag. - resetIterLoop = true; - - // Get out of this loop. - break; - case -3: // OS not supported. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - return -3; - case -4: // Permission error. - case -5: // Arg error. - case -6: // Path componet does not exist. - case -7: // Part of path is a file. - case -9: // Memory error. - default: - // Set unableToDeleteAll. - unableToDeleteAll = true; - break; - }; - - // Check to see if reset flag is set. - if (resetIterLoop) - { - // Get out of this loop. - break; - } - } - - // Check to see how the previous loop exited. - if (!resetIterLoop) - { - // Remove the directory. - if (remove(currentabspath.c_str()) != 0) - { - // Check and see if unableToDeleteAll was set. If it was set, we ignore this error. - if (!unableToDeleteAll) - { - // Check the error. - if (errno == EACCES) - { - // Permissions error. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - return -1; - } - else - { - if (errno == EPERM) - { - // Permissions error. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - return -1; - } - else - { - // Some other error. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - return -5; - } - } - } - else - { - // Increment the offset in the vector. (We have dealt with this subdirectory.) - if (directory_offsets.size() > 0) - { - directory_offsets[(directory_offsets.size() - 1)] = (directory_offsets[(directory_offsets.size() - 1)] + 1); - } - } - } - - // Check to see if we removed the source dir. - if (currentabspath != path) - { - // Get the parent directory of currentabspath. - currentabspath = FileUtills::GetParent(currentabspath); - if (currentabspath.size() <= 0) - { - // Could not get parent directory. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - return -7; - } - - // Delete the old plist. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - } - else - { - // Delete the old plist. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - - // All directories have been deleted. - done = true; - } - } - else - { - // Delete the old plist. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - } - }while (!done); - - // Delete the plist. - if (plist != NULL) - { - delete plist; - plist = NULL; - } - - // Clear dummy and currentabspath. - dummy.clear(); - currentabspath.clear(); - - // Flush the directory_offsets vector. - directory_offsets.clear(); - - // Check and see if the unableToDeleteAll flag is set. - if (unableToDeleteAll) - { - // We can't delete some things so return an error. - return -8; - } - } - else - { - // Path is not empty and we are not deleting recursively. - return -6; - } - } - else - { - // Some other error. - return -5; - } - } - } - else - { - // Determine the error. - switch (result) - { - // Permission BAD - case -1: - return -1; - break; - // Path does not exist - case -2: - return -2; - break; - // Either an unknown error or function is not supported - case -3: - return -3; - break; - // Possible memory error - default: - return -4; - break; - }; - } - - // Exit function. - return 0; -#endif - // System not supported. - return -3; -} + // Copy ret to Common_commonLastErrorCode. + Common_commonLastErrorCode = ret; -short FileUtills::CopyFile(const std::string & src, const std::string & dest, const bool & append, const streamsize & begOffset, const streamsize & endOffset) -{ - // Init vars. - const short buffer_size = 512; // Constant used to determine the size of the file copy buffer. - streamsize result = 0; // Used to hold the returned result of this function to the caller. - streamsize count = 0; // Used while copying data to count the number of copied bytes. - streamsize total_bytes_copied = 0; // Holds the total amount of copied bytes. - streamsize input_size = 0; // Holds the size of the input file. - streamsize bytes_to_copy = 0; // Holds the total amount of bytes to copy from the input file. - char * pBuffer = NULL; // Used as a buffer for copying data. - fstream input; // File stream for input file. - fstream output; // File stream for output file. - - // Check for invalid offsets. - if ((begOffset < 0) || (endOffset < 0)) - { - // Error, An offset is negative. - result = -1; - } - else - { - // Check and make sure that the beginning offset is smaller than the ending offset. - if (begOffset > endOffset) - { - // Offsets are out of order. - result = -5; - } - else - { - // Check the source file name length. - if (src.size() <= 0) - { - // Invalid source file name. - result = -10; - } - else - { - // Check to see if src is a directory. - result = FileUtills::IsFileOrDirectory(src); - if (result == 2) - { - // Src is a directory, do not continue. - result = -17; - } - else - { - // Check for negative return code. - if (result < 0) - { - // IsFileOrDirectory() returned an error. - switch (result) - { - case -3: - // IsFileOrDirectory() is not supported on this system. - result = -33; - break; - case -4: - // A permissions error occured. - result = -34; - break; - case -5: - // The given path is empty. - result = -35; - break; - case -6: - // A path componet does not exist. - result = -36; - break; - case -7: - // The path has a file in it and is not at the end. (I.e you are treating a file as a directory.) - result = -37; - break; - default: - // All other errors. - result = -39; - break; - }; - } - else - { - // Attempt to open the file. - input.open(src.c_str(), ios::in | ios::binary); - if (!input.is_open()) - { - // Could not open file. - result = -11; - } - else - { - // Get file size. - input.seekg(0, ios::end); - input_size = input.tellg(); - input.seekg(0, ios::beg); - - // Determine if the input file has the needed amount of bytes to copy. - if (input_size < begOffset) - { - // Input file is smaller than the beginning offset. - result = -12; - } - else - { - if (input_size < endOffset) - { - // Input file is smaller than the ending offset. - result = -13; - } - else - { - // Check to see if we are copying the entire file. - if ((begOffset == 0) && (endOffset == 0)) // Shortcut so the caller does not need to know the size of the file. - { - // We are copying the entire file. - bytes_to_copy = input_size; - } - else - { - // Calculate the total amount of bytes to copy. - bytes_to_copy = endOffset - begOffset; - - // Seek to the beginning offset in the source file. - input.seekg(begOffset, ios::beg); - } - - // Check the length of the dest. - if (dest.size() <= 0) - { - // Invalid dest path. - result = -20; - } - else - { - // Check and see if dest is a directory. - result = FileUtills::IsFileOrDirectory(dest); - if (result == 2) - { - // Dest is a directory, abort. - result = -27; - } - else - { - // Check for negative return code. (That is NOT the non existant file error.) - if ((result != -6) && (result < 0)) - { - // IsFileOrDirectory() returned an error. - switch (result) - { - case -3: - // IsFileOrDirectory() is not supported on this system. - result = -33; - break; - case -4: - // A permissions error occured. - result = -34; - break; - case -5: - // The given path is empty. - result = -35; - break; - case -6: - // A path componet does not exist. - result = -36; - break; - case -7: - // The path has a file in it and is not at the end. (I.e you are treating a file as a directory.) - result = -37; - break; - default: - // All other errors. - result = -39; - break; - }; - } - else - { - // Check and see if we got the non existant file error from IsFileOrDirectory(). - if (result == -6) - { - // Reset result. - result = 0; - } - - // Open output file. - if (append) - { - output.open(dest.c_str(), ios::in | ios::out | ios::binary | ios::app); - output.seekg(0, ios::end); - output.seekp(0, ios::end); - } - else - { - output.open(dest.c_str(), ios::in | ios::out | ios::binary | ios::trunc); - } - - // Check to see if the file is open. - if (!output.is_open()) - { - // Could not open output file. - result = -21; - } - else - { - // Begin try block. - try{ - // Allocate memory buffer. - pBuffer = (char*)malloc(buffer_size); - if (pBuffer == NULL) - { - // Could not allocate memory buffer. - result = -9; - } - else - { - // Blank the buffer. - memset(pBuffer, '\0', buffer_size); - - // Start copy loop. - while (total_bytes_copied < bytes_to_copy) - { - // Check the state of the file streams. - if (input.fail()) - { - // Input file stream error. - if (input.bad()) - { - // Read IO error. - result = -14; - } - else - { - // Logical IO error. - result = -15; - } - - // Get out of the loop. - break; - } - if (input.eof()) - { - // ??? We should not have hit eof.... - result = -16; - - // Get out of the loop. - break; - } - if (output.fail()) - { - // Output file stream error. - if (output.bad()) - { - // Write IO error. - result = -24; - } - else - { - // Logical IO error. - result = -25; - } - - // Get out of the loop. - break; - } - - // Check and see if the data remaining to copy is less than the buffer size. - if ((bytes_to_copy - total_bytes_copied) < buffer_size) - { - // OK we only need to copy the remaining data. - input.read(pBuffer, (bytes_to_copy - total_bytes_copied)); - } - else - { - // Fill up the buffer. - input.read(pBuffer, buffer_size); - } - - // Get the number of read bytes. - count = input.gcount(); - - // Write out the bytes to the output file. - output.write(pBuffer, count); - - // Add the bytes copied to the total. - total_bytes_copied += count; - } - - // Deallocate memory buffer. - memset(pBuffer, '\0', buffer_size); - if (pBuffer != NULL) - { - free(pBuffer); - pBuffer = NULL; - } - } - } - catch(...) - { - // Exception thrown during copy loop. - result = -99; - } - - // Close output file. - if (output.is_open()) - { - output.close(); - } - } - } - } - } - } - } - - // Close input file. - if (input.is_open()) - { - input.close(); - } - } - } - } - } - } - } - - // Exit function. - return result; + // Return the result. + return ret; } - -short FileUtills::CopyPath(const std::string & src, const std::string & dest, const bool & recursive, - const bool & rename, const bool & abort_on_failure, - const bool & append, const streamsize & begOffset, const streamsize & endOffset) -{ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + // Init vars. bool done = false; // Used to know when to exit the recursive iterator loop. bool resetIterLoop = false; // Used to control resetting the recursive iterator loop. bool unableToCopyAll = false; // Used to know if a file copy failed while recursively copying files. - short result = 0; // Used to store results from calls to other functions. + int result = COMMON_ERROR_UNKNOWN_ERROR; // Used to store results from calls to other functions. std::string currentabspath_src = ""; // Used to contain the current absolute source path. std::string currentabspath_dest = ""; // Used to contain the current absolute destionation path. std::string src_path = ""; // Used to construct temporary source paths. @@ -2625,7 +1888,7 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co // Check and see if the src is a file or a directory. result = FileUtills::IsFileOrDirectory(src); - if (result == 2) // Directory. + if (result == FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) // Directory. { // Copy the src path to the currentabspath_src and the dest path to the currentabspath_dest var. currentabspath_src = src; @@ -2633,66 +1896,42 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co // OK, check and see if the dest directory exists. result = FileUtills::IsFileOrDirectory(currentabspath_dest); - if (result == -6) // Directory does not exist. + if (result == FILEUTILLS_ERROR_NON_EXISTANT) // Directory does not exist. { - // Create the dest directory. - result = FileUtills::CreateDirectory(currentabspath_dest); - if (result != 0) - { - // OK, we can't create the dest directory. - result = -1; - } + // Create the dest directory. + result = FileUtills::CreateDirectory(currentabspath_dest); + if (result == COMMON_ERROR_SUCCESS) + { + // Set result to FILEUTILLS_ERROR_NON_EXISTANT, so that we know we can continue. + result = FILEUTILLS_ERROR_NON_EXISTANT; + } + else + { + // OK, we can't create the dest directory. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Could not create destionation directory, aborting.\n"); + } } else { - if (result == 2) // Dest Directory exists. + if (result != FILEUTILLS_ERROR_EXISTANT) // Dest Directory exists. + { + if (result == COMMON_ERROR_SUCCESS) { - // Set result to zero. (0) - result = 0; + // This is some special file, so we can't overwrite it. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Destionation is a special file, will not overwrite it, aborting.\n"); } else { - if (result >= 0) - { - // OK, dest is some file or other filesystem entry, so we can't copy a directory to it. - result = -2; - } - else - { - // IsFileOrDirectory() returned an error. - switch (result) - { - case -3: - // IsFileOrDirectory() is not supported on this system. - result = -13; - break; - case -4: - // A permissions error occured. - result = -14; - break; - case -5: - // The given path is empty. - result = -15; - break; - case -6: - // A path componet does not exist. - result = -16; - break; - case -7: - // The path has a file in it and is not at the end. (I.e you are treating a file as a directory.) - result = -17; - break; - default: - // All other errors. - result = -19; - break; - }; - } + // Some other error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Unable to check destionation, returned error was: "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG("\n"); } + } } // Make sure that result == 0. (I.e. that we can continue.) - if (result == 0) + if ((result == FILEUTILLS_ERROR_NON_EXISTANT) || (result == FILEUTILLS_ERROR_EXISTANT)) { // Begin recursion loop. do @@ -2708,7 +1947,7 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co delete plist; plist = NULL; } - result = -4; + result = Common_commonLastErrorCode; // Exit loop. done = true; @@ -2753,13 +1992,13 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co // Check to see if the current entry in the list is a directory. switch (FileUtills::IsFileOrDirectory(src_path)) { - case 0: // Not a file or directory. (Try to copy anyway.) - case 1: // File. + case COMMON_ERROR_SUCCESS: // Not a file or directory. (Try to copy anyway.) + case FILEUTILLS_ERROR_PATH_IS_A_FILE: // File. // Attempt to copy file. result = FileUtills::CopyFile(src_path, dest_path, false, 0, 0); - if (result != 0) + if (result != COMMON_ERROR_SUCCESS) { - if ((result != -3) || (result == -33)) // If result does = -3 (CopyFile), or -33 (IsFileOrDirectory) it means the OS / Arch is unsupported by CopyFile(). + if (result == COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED) // If result does = -3 (CopyFile), or -33 (IsFileOrDirectory) it means the OS / Arch is unsupported by CopyFile(). { // Set unableToCopyAll. unableToCopyAll = true; @@ -2781,7 +2020,7 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co } } break; - case 2: // Directory. + case FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY: // Directory. // Check recursive flag. if (recursive) { @@ -2798,19 +2037,18 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co // Get out of this loop. } // Ignore otherwise. break; - case -3: // OS not supported. + case COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED: // OS not supported. if (plist != NULL) { delete plist; plist = NULL; } - result = -3; break; - case -4: // Permission error. - case -5: // Arg error. - case -6: // Path componet does not exist. - case -7: // Part of path is a file. - case -9: // Memory error. + case COMMON_ERROR_ACCESS_DENIED: // Permission error. + case COMMON_ERROR_INVALID_ARGUMENT: // Arg error. + case FILEUTILLS_ERROR_NON_EXISTANT: // Path componet does not exist. + case FILEUTILLS_ERROR_PATH_FILE_AS_DIRECTORY: // Part of path is a file. + case COMMON_ERROR_MEMORY_ERROR: // Memory error. default: // Set unableToCopyAll. unableToCopyAll = true; @@ -2818,7 +2056,7 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co }; // Check to see if reset flag is set, if we hit OS / Arch unsupported, or if we failed and the user requested abort on failure. - if ((resetIterLoop) || (result == -3) || ((unableToCopyAll) && (abort_on_failure))) + if ((resetIterLoop) || (result == COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED) || ((unableToCopyAll) && (abort_on_failure))) { // Get out of this loop. break; @@ -2826,7 +2064,7 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co } // Check to see how the previous loop exited. - if ((result == -3) || (result == -33) || ((abort_on_failure) && (unableToCopyAll))) + if ((result == COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED) || ((abort_on_failure) && (unableToCopyAll))) { /* OS / Arch not supported, or * we failed to copy something and the user requested abort on failure. @@ -2847,13 +2085,13 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co // Check and see if the dest directory exists. result = FileUtills::IsFileOrDirectory(currentabspath_dest); - if (result == -6) // Directory does NOT exist. + if (result == FILEUTILLS_ERROR_NON_EXISTANT) // Directory does NOT exist. { // Create subdirectory. result = FileUtills::CreateDirectory(currentabspath_dest); - if (result != 0) + if (result != COMMON_ERROR_SUCCESS) { - if (result == -3) // -3 means OS / Arch not supported. + if (result == COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED) // -3 means OS / Arch not supported. { // Exit loop. done = true; @@ -2871,14 +2109,14 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co else { // Check to see if currentabspath_dest is a some sort of filesystem entry. - if (result >= 0) + if (result != FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) { // OK, we can't create a directory where a file (or some other filesystem entry) exists. unableToCopyAll = true; } else { - if (result == -3) // OS / Arch not supported. + if (result == COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED) // OS / Arch not supported. { // Exit loop. done = true; @@ -2912,14 +2150,14 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co if (currentabspath_src.size() <= 0) { // Could not get parent directory. - result = -5; + result = COMMON_ERROR_INTERNAL_ERROR; done = true; } currentabspath_dest = FileUtills::GetParent(currentabspath_dest); if (currentabspath_dest.size() <= 0) { // Could not get parent directory. - result = -5; + result = COMMON_ERROR_INTERNAL_ERROR; done = true; } @@ -2968,136 +2206,120 @@ short FileUtills::CopyPath(const std::string & src, const std::string & dest, co // We can't copy some things so return an error. result = -6; } - - // Check and see if IsFileOrDirectory() returned -3. - if (result == -33) - { - // IsFileOrDirectory() is not supported. - result = -13; - } } } else // Treat as single file. { - if ((result == 0) || (result == 1)) - { - // Call CopyFile. - result = FileUtills::CopyFile(src, dest, append, begOffset, endOffset); - } - else - { - // IsFileOrDirectory() returned an error. - switch (result) - { - case -3: - // IsFileOrDirectory() is not supported on this system. - result = -13; - break; - case -4: - // A permissions error occured. - result = -14; - break; - case -5: - // The given path is empty. - result = -15; - break; - case -6: - // A path componet does not exist. - result = -16; - break; - case -7: - // The path has a file in it and is not at the end. (I.e you are treating a file as a directory.) - result = -17; - break; - default: - // All other errors. - result = -19; - break; - }; - } + if ((result == COMMON_ERROR_SUCCESS) || (result == FILEUTILLS_ERROR_PATH_IS_A_FILE)) + { + // Call CopyFile. + result = FileUtills::CopyFile(src, dest, append, begOffset, endOffset); + } + else + { + // IsFileOrDirectory() returned an error. + COMMON_LOG_INFO("FileUtills::CopyPath(): Unable to determine the type of the source path, aborting.\n"); + } } + // Copy result to Common_commonLastErrorCode. + Common_commonLastErrorCode = result; + // Return result. return result; } -short FileUtills::MovePath(const std::string & src, const std::string & dest, const bool & overwrite) +int FileUtills::MovePath(const std::string & src, const std::string & dest, const bool & overwrite) { - // Dumb check. - if ((src.size() <= 0) || (dest.size() <= 0)) - { - // Bad src or dest. - return -5; - } + // Init vars. + int result = 0; // Used for return codes. - // Init vars. - short result = 0; // Used for return codes. - - // Check and see if dest already exists. - result = FileUtills::DoesExist(dest); - switch (result) - { - case 0: // Path exists. - if (overwrite == true) - { - // Attempt to delete dest path. - result = FileUtills::DeletePath(dest, true); - switch (result) - { - case 0: // Delete OK. - break; - default: // Unable to delete path. - result = -21; - break; - }; - } - else - { - // Can't delete existing path bail out. - result = -20; - break; - } - break; - case -1: // Path does not exist. - break; - default: // Some error. - result = -22; - break; - }; - - // Check to see if we can continue. - if ((result == -1) || (result == 0)) - { - // Copy entire path to the dest. - result = FileUtills::CopyPath(src, dest); - switch (result) - { - case 0: // Copy ok. - break; - case -1: // Source Permission error. - result = -10; - case -2: // Source Does not exist. - result = -10; - default: // Some error. - result = -4; - break; - }; - - // Check to see if the copy was successful. - if (result == 0) - { - // Delete original path. - result = FileUtills::DeletePath(src, true); - switch (result) - { - case 0: // Delete ok. - break; - default: // Some error. - result = -4; - break; - }; - } - } + // Dumb check. + if (src.size() > 0) + { + if (dest.size() > 0) + { + // Check and see if dest already exists. + result = FileUtills::DoesExist(dest); + switch (result) + { + case FILEUTILLS_ERROR_EXISTANT: // Path exists. + if (overwrite == true) + { + // Attempt to delete dest path. + result = FileUtills::DeletePath(dest, true); + switch (result) + { + case COMMON_ERROR_SUCCESS: // Delete OK. + break; + default: // Unable to delete path. + COMMON_LOG_INFO("FileUtills::MovePath(): Unable to delete destionation path, aborting.\n"); + break; + }; + } + else + { + // Can't delete existing path bail out. + COMMON_LOG_INFO("FileUtills::MovePath(): Destionation path already exists. Caller has disabled overwriting pre-existing paths, aborting.\n"); + break; + } + break; + case FILEUTILLS_ERROR_NON_EXISTANT: // Path does not exist. + break; + default: // Some error. + COMMON_LOG_VERBOSE("FileUtills::MovePath(): Unable to determine if the destionation path already exists. Aborting.\n"); + break; + }; + + // Check to see if we can continue. + if ((result == FILEUTILLS_ERROR_NON_EXISTANT) || (result == COMMON_ERROR_SUCCESS)) + { + // Copy entire path to the dest. + result = FileUtills::CopyPath(src, dest); + switch (result) + { + case COMMON_ERROR_SUCCESS: // Copy ok. + break; + default: // Some error. + COMMON_LOG_INFO("FileUtills::MovePath(): Unable to copy data from source to destionation, aborting.\n"); + break; + }; + + // Check to see if the copy was successful. + if (result == COMMON_ERROR_SUCCESS) + { + // Delete original path. + result = FileUtills::DeletePath(src, true); + switch (result) + { + case COMMON_ERROR_SUCCESS: // Delete ok. + COMMON_LOG_INFO("FileUtills::MovePath(): Source path moved to destionation path successfully.\n"); + break; + default: // Some error. + COMMON_LOG_WARNING("FileUtills::MovePath(): Source path copied to destionation path successfully, but we were unable to delete the source path. Returning error: "); + COMMON_LOG_WARNING(Common::Get_Error_Message(result)); + COMMON_LOG_WARNING("\n"); + break; + }; + } + } + } + else + { + // Dest is invalid. + result = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_INFO("FileUtills::MovePath(): Invalid destionation path, aborting.\n"); + } + } + else + { + // Invalid source path. + result = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_INFO("FileUtills::MovePath(): Invalid source path, aborting.\n"); + } + + // Copy result to Common_commonLastErrorCode. + Common_commonLastErrorCode = result; // Default return. return result; diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills.h b/src/Common/Src/File_Management_Subsystem/FileUtills.h index c6ba89b..297f5af 100644 --- a/src/Common/Src/File_Management_Subsystem/FileUtills.h +++ b/src/Common/Src/File_Management_Subsystem/FileUtills.h @@ -19,423 +19,782 @@ https://github.com/codebase7/mengine */ -#ifndef FILEUTILLS -#define FILEUTILLS +/* Include guard. */ +#ifndef FILEUTILLS_H +#define FILEUTILLS_H -// Engine Includes -#ifdef __win32 -#include "..\BaseHeader.h" +/* Check for MSVC and disable the CRT_SECURE_WARNINGS so that the compiler will be quiet about finding portable code... */ +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS 1 +#endif /* _MSC_VER */ + +/* Define extern C if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus. */ + +/* External Includes. */ +#include /* Defines FILE and friends. */ + +/* Engine Includes */ +#ifdef _WIN32 #include "..\Error_Handler\Common_Error_Handler.h" #include "..\Error_Handler\Common_Error_Handler_Internal.h" +#include "..\..\..\Core\Src\DataProcess.h" #else -#include "../BaseHeader.h" #include "../Error_Handler/Common_Error_Handler.h" #include "../Error_Handler/Common_Error_Handler_Internal.h" +#include "../../../Core/Src/DataProcess.h" #endif -namespace FileUtills{ +/* Define the stringifier. */ +#ifndef MSYS_REAL_STRINGIFIY +#define MSYS_REAL_STRINGIFIY(s) #s +#endif /* MSYS_REAL_STRINGIFIY */ + +#ifndef MSYS_STRINGIFIY +#define MSYS_STRINGIFIY(s) MSYS_REAL_STRINGIFIY(s) +#endif /* MSYS_STRINGIFIY */ +/* Define the appropriate Directory seperators and Symbols. */ #ifdef _WIN32 -// Define the Windows Directory seperator -#define DIR_SEP = '\\' +/* Define the Windows Directory seperator */ +#define DIR_SEP '\\' +#define DIR_SEP_STR MSYS_STRINGIFIY(DIR_SEP) +#define HOME_DIR_SYMBOL '~' +#define FILEEXT_SEP '.' +#define FILEEXT_SEP_STR MSYS_STRINGIFIY(FILEEXT_SEP) +/* Define the minimal valid absolute directory path length. */ +/* + * MINIMAL_VALID_ABSOLUTE_PATH_LENGTH is supposed to include the needed DIR_SEP for the root directory. + * In addition it is supposed to be the minimal number of char(s) needed to represent a valid absolute path + * to a root directory. + * + * In windows ":" is minimal for an absolute path. (Root of given drive.) + */ +#define MINIMAL_VALID_ABSOLUTE_PATH_LENGTH 3 #else -// Define the Posix Directory Seperator. +/* Define the Posix Directory Seperator. */ #ifndef DIR_SEP #define DIR_SEP '/' +#define DIR_SEP_STR MSYS_STRINGIFIY(DIR_SEP) +#define HOME_DIR_SYMBOL '~' #define Relative_Symbol "./" -#endif -#endif +#define FILEEXT_SEP '.' +#define FILEEXT_SEP_STR MSYS_STRINGIFIY(FILEEXT_SEP) +/* + * MINIMAL_VALID_ABSOLUTE_PATH_LENGTH is supposed to include the needed DIR_SEP for the root directory. + * In addition it is supposed to be the minimal number of char(s) needed to represent a valid absolute path + * to a root directory. + * + * In linux "" is minimal for an absolute path. (Root of the entire filesystem / chroot.) + */ +#define MINIMAL_VALID_ABSOLUTE_PATH_LENGTH 1 +#endif /* DIR_SEP */ +#endif /* _WIN32 */ -// Define the directory list structure -struct dirlist{ - long int numOfEntries; // Used to store the number of entries in the list array. - std::vector list; // Used to store the directory's entry data. - std::string path; // Used to store the path of the directory that the entry list is about. - Panic::ERROR error; // Used to store an errorcode if one occurs. +/*! + * enum MSYS_FILESIZE_TYPES + * + * Defines the types that an MSYS_FILESIZE object may have. + */ +enum MSYS_FILESIZE_TYPES { + UNKNOWN_FILESIZE_TYPE = 0, + WINDOWS_FILESIZE_TYPE = 1, + POSIX_FILESIZE_TYPE = 2, }; +/* File size structure. Contains the length of a file's size. */ +typedef struct MSYS_FILESIZE { + enum MSYS_FILESIZE_TYPES type; /* What type of struct it is. (Windows or POSIX. )*/ +} MSYS_FILESIZE_T; + +/* Define the directory list structure. */ +typedef struct FileUtills_dirlist { + void * pointer; /* Pointer to object. */ +} FileUtills_dirlist_T; + /*! - * int FileUtills::GetUserProfileDirectoryPath(std::string & path) + * void FileUtills_Deallocate_CString(char ** str) * - * Fetches the user's profile directory from the enviorment, and stores - * it in the given path argument. (Note: The given path argument will be - * erased when this function is called. So take care not to leave something - * in it you need.) + * Deallocates C strings made by FileUtills, and sets the given pointer to NULL. + * Once the object is deallocated, it should not be dereferenced again. * - * If the function fails for any reason, path will be an empty string, - * and Common::commonLastErrorCode will be set to the approiate error. + * If given a string NOT created by FileUtills, the behaviour and result of this function is undefined. + * + * If given an invalid (NULL) pointer this function will silently fail. + * + * This function has no return. */ -int GetUserProfileDirectoryPath(std::string & path); +void FileUtills_Deallocate_CString(char ** str); /*! - * int FileUtills::GetCurrentWorkingDirectoryPath(std::string & path) + * int FileUtills_Create_MSYS_FILESIZE_Structure(MSYS_FILESIZE_T ** str) + * + * Factory function for MSYS_FILESIZE structures. + * + * WARNING: This function will NOT deallocate a preexisting object pointed to by the given + * pointer argument. The function WILL overwrite the original pointer if it is successful. + * If you need to keep that pointer for later deallocation, copy it elsewhere before calling + * this function. * - * Fetches the current working directory from the enviorment, and stores - * it in the given path argument. (Note: The given path argument will be - * erased when this function is called. So take care not to leave something - * in it you need.) + * Returns COMMON_ERROR_SUCCESS if creation of structure was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer to pointer is NULL. + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation fails. + * Otherwise returns the appropriate error code. * - * If the function fails for any reason, path will be an empty string, - * and Common::commonLastErrorCode will be set to the approiate error. + * In case of error, this function will NOT modify it's argument. */ -int GetCurrentWorkingDirectoryPath(std::string & path); +int FileUtills_Create_MSYS_FILESIZE_Structure(MSYS_FILESIZE_T ** str); /*! - * std::string FileUtills::GetExecDirectory() + * void FileUtills_Destroy_MSYS_FILESIZE_Structure(MSYS_FILESIZE_T ** str) * - * Returns a NULL terminated std::string the path to the directory where the - * executable is stored. + * Destructor for MSYS_FILESIZE structures. Takes the given pointer to pointer, derefs it and + * deallocates the pointer to object if it is allocated. + * + * WARNING: This function is a destructor for MSYS_FILESIZE objects ONLY! Giving a different object + * to this function will cause undefined behavior. + * + * If the given pointer is NULL, then this function will silently fail. * - * If the function fails for any reason, the returned std::string will be empty, - * and Common::commonLastErrorCode will be set to the error. + * This function has no return. */ -std::string GetExecDirectory(); +void FileUtills_Destroy_MSYS_FILESIZE_Structure(MSYS_FILESIZE_T ** str); /*! - FileUtills::dirlist * getDirectory(const std::string & path, const bool & cleanList) - - Lists the given directory's contents. + * int FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT(const MSYS_FILESIZE_T * str, long long int * retVal) + * + * Accessor function. + * + * Returns the length variable from the given MSYS_FILESIZE structure. + * + * Returns COMMON_ERROR_SUCCESS if successful. retVal will hold the result. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers are NULL. + * Otherwise returns the appropriate error code. + * + * In case of error, this function will NOT modify it's arguments. + */ +int FileUtills_Get_Length_From_MSYS_FILESIZE_Structure_LLINT(const MSYS_FILESIZE_T * str, long long int * retVal); - Pram: path, path of directory to check. +/*! + * int FileUtills_Set_Length_From_MSYS_FILESIZE_Structure_LLINT(MSYS_FILESIZE_T * str, const long long int * val) + * + * Accessor function. + * + * Sets the length variable for the given MSYS_FILESIZE structure. + * + * Returns COMMON_ERROR_SUCCESS if successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers are NULL. + * Otherwise returns the appropriate error code. + * + * In case of error, this function will NOT modify it's arguments. + */ +int FileUtills_Set_Length_From_MSYS_FILESIZE_Structure_LLINT(MSYS_FILESIZE_T * str, const long long int * val); - Pram: cleanList, If this is true then, the generated list will be sorted - (via decrementing sort), and the list will have the entries for the current - and parent directories removed. - (Default is to do nothing, and return the list as the OS generated it.) +/*! + void FileUtills_Destroy_FileUtills_dirlist_Structure(FileUtills_dirlist_T ** dirList) - Returns a pointer to a dirlist if successfull. - Returns NULL if an error occurs. -*/ -dirlist * getDirectory(const std::string & path, const bool & cleanList = false); + Destroys (frees) the given FileUtills_dirlist data structure, and sets the (*dirList) pointer to NULL. + Once destroyed, the given object should not be reused. -/*! - int FileUtills::DoesExist(const std::string & Filename, Panic::ERROR & error) - Prams: @std::string Filename + If given an object that is not a FileUtills_dirlist data structure, the result is undefined. - Checks to see if given file or directory exists. + Returns nothing. + */ +void FileUtills_Destroy_FileUtills_dirlist_Structure(FileUtills_dirlist_T ** dirList); - This function is called by FileUtills::CheckPermissions() before runing permission checks. +/* + * int FileUtills_Get_File_Length_By_Filename(const char * filename, const size_t filenameSize, MSYS_FILESIZE_T * fileLength) + * + * Returns the length of the given file. + * (This is just a wrapper around FileUtills_Get_File_Length().) + * + * Returns COMMON_ERROR_SUCCESS if the length is read in successfully. (fileLength will be set to the named file's length in this case.) + * Otherwise returns the appropriate error code. + * + * In case of error, (the returned error code is not COMMON_ERROR_SUCCESS), the fileLength argument will NOT be altered. + */ +int FileUtills_Get_File_Length_By_Filename(const char * filename, const size_t filenameSize, MSYS_FILESIZE_T * fileLength); - This function uses the Panic::ERROR class. +/*! + * int FileUtills_Get_File_Length(FILE * fp, MSYS_FILESIZE_T * fileLength) + * + * Gets the length of the given open file. + * + * This function expects the file stream to be in a good state, (I.e. It's error flag is not set), and that the file + * stream was opened in binary mode. (If the stream is not already open, use FileUtills_Get_File_Length_By_Filename() instead, + * it will ensure that these requirements are met.) + * + * This function calculates the size of the file by starting at the beginning and working it's way to the end by calling + * fgetc(), and then checking the state flags (error and eof) after each call to fgetc(). When the EOF flag is set, + * (and the error flag is not set), the function will call ftello() to get the offset and check it for error. Finally + * regardless if an error has occurred or not, the function will attempt to restore the previous position in the file + * that the file was at when the call to this function was made. + * + * Returns COMMON_ERROR_SUCCESS if the file length is determined. (fileLength will be set to the determined length in this case.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given file pointer or fileLength pointer is NULL. + * + * Returns COMMON_ERROR_IO_ERROR if one of the f*() functions fails. + * + * Returns COMMON_ERROR_MEMORY_ERROR if the file length was determined, but could not be retrieved from the file stream because the + * value could not be converted and stored. (Blame the C standard in this case.....) + * + * Otherwise returns the appropriate error code. + * + * In case of error, (the returned error code is not COMMON_ERROR_SUCCESS), the fileLength argument will NOT be altered. + */ +int FileUtills_Get_File_Length(FILE * fp, MSYS_FILESIZE_T * fileLength); - Returns 0 if file or directory exists on the system. - Returns -1 if file or directory does not exist on the system. - Returns -2 if an error occurs, call ReturnLastError() on the error instance to get details. - Note it will also return -2 if the function does not have an implementation for your OS / arch. -*/ -int DoesExist(const std::string & Filename, Panic::ERROR & error); +/*! + * int FileUtills_Read_Bytes_From_File(FILE * IN, const size_t dataLength, char * dataBuf, const size_t dataBufLength, const size_t destStaringOffset, const bool blankDataBuf) + * + * Reads the given amount of bytes from the given input file and copies them + * to the given memory buffer. + * + * WARNING: This function expects a pre-allocated memory buffer as big as the + * given data length. If the buffer is smaller than this a memory access violation + * WILL HAPPEN. + * + * NOTE: This function expects that the given input file is already open and not errored out when called. + * The input file will remain open after the function returns. (It will still be usable if the returned error code is SUCCESS.) + * + * The input position of the file will NOT be restored in ANY instance after this function returns. + * If you need the current position for something, copy it elsewhere before calling this function. + * + * @pram IN: C FILE structure that points to a pre-opened file that data will be read from. (Cannot be in an error state, data will be read from the file's current offset.) + * @pram dataLength: The amount of data to read from the file. + * @pram dataBuf: Preallocated C-String where the data read from the file will be stored. (Will not be reallocated if too small.) + * @pram dataBufLength: The total length of the pre-allocated dataBuf string. + * @pram destStaringOffset: Position in the dataBuf string where read data from the file will start at. + * @pram blankDataBuf: Whether or not to blank out the dataBuf with NULL bytes. + * + * Returns COMMON_ERROR_SUCCESS if the data is read into the memory buffer successfully. + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is NULL, + * the given input file has errored out prior to the function call, + * dataLength or dataBufLength is less than 1, or destStaringOffset plus dataLength + * is greater than or equal to dataBufLength. + * + * Returns COMMON_ERROR_IO_ERROR on all f*() errors, or if the end of the file is hit prior to reading + * the amount of requested data. + * + * Otherwise returns the appropriate error code. + */ +int FileUtills_Read_Bytes_From_File(FILE * IN, const size_t dataLength, char * dataBuf, const size_t dataBufLength, const size_t destStaringOffset, const bool blankDataBuf); /*! - int FileUtills::DoesExist(const std::string & Filename) - Prams: @std::string Filename + * int FileUtills_Write_Data_To_File_From_File(FILE * OUT, const char * filename, const size_t filenameLength, const MSYS_FILESIZE_T * fileStartingOffset, const size_t dataLength) + * + * Opens the given input file in binary mode and jumps to the given starting offset, + * then reads the given amount of data and writes it directly to the given output file, + * finally it closes the input file. + * + * NOTE: This function expects that the given output file is already open and not errored out when called. + * The output file will remain open after the function returns. (It will still be usable if the returned error code is SUCCESS.) + * + * The output position of the file will NOT be restored in ANY instance after this function returns. + * If you need the current position for something, copy it elsewhere before calling this function. + * + * Returns COMMON_ERROR_SUCCESS if the data is written to the output file successfully. + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given output file pointer is NULL, + * the given output file has errored out prior to the function call, + * filename is NULL, filenameLength is less than 1, fileStartingOffset is less than 0, + * or dataLength is less than 1. + * + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + * + * Returns COMMON_ERROR_IO_ERROR on all f*() errors, or if the end of the file is hit prior to reading + * the amount of requested data. + * + * Otherwise returns the appropriate error code. + */ +int FileUtills_Write_Data_To_File_From_File(FILE * OUT, const char * filename, const size_t filenameLength, const MSYS_FILESIZE_T * fileStartingOffset, const size_t dataLength); - Checks to see if given file or directory exists. +/*! + * int FileUtills_Write_Data_To_File_From_Memory(FILE * OUT, const char * data, const size_t dataLength) + * + * Writes out the given data in memory to the given output file. + * + * NOTE: This function expects that the given output file is already open and not errored out when called. + * The output file will remain open after the function returns. (It will still be usable if the returned error code is SUCCESS.) + * + * The output position of the file will NOT be restored in ANY instance after this function returns. + * If you need the current position for something, copy it elsewhere before calling this function. + * + * Returns COMMON_ERROR_SUCCESS if the data is written to the output file successfully. + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given output file pointer is NULL, + * the given output file has errored out prior to the function call, + * data is NULL, or dataLength is less than 1. + * + * Returns COMMON_ERROR_IO_ERROR on all f*() errors. + * + * Otherwise returns the appropriate error code. + */ +int FileUtills_Write_Data_To_File_From_Memory(FILE * OUT, const char * data, const size_t dataLength); - This function is called by FileUtills::CheckPermissions() before runing permission checks. +/*! + * int FileUtills_Get_Last_Path_Component(char ** retStr, size_t * retStrLength, const int getParent) + * + * Fetches the last part of the given path and creates a sub-string from it. + * + * Alternatively, this function can be used to return a sub-string with the parent path of the last path component, + * if getParent is set to a non-zero value. + * + * (This is a wrapper around DataProcess_Get_SubString_Using_Delimiter(), and as such has similar error codes.) + * + * @pram getParent: If set to a non-zero value, then the parent path of the last path component will be used for the sub-string. + * Otherwise, the last path component will be used for the sub-string. + * + * WARNING: This function will NOT deallocate the given c-string argument (*retStr). If this function returns COMMON_ERROR_SUCCESS, + * the given c-string pointer (*retStr) will be overwritten with a pointer to the newly created sub-string. If you need to deallocate + * the original c-string, copy the pointer elsewhere before calling this function. + * + * Returns COMMON_ERROR_SUCCESS if the last path component (or parent path) was found and a sub-string was created. + * Returns COMMON_ERROR_RANGE_ERROR if the path is a relative path without any directory components. + * Returns COMMON_ERROR_END_OF_DATA if the path is a directory with directory separator on the end. + * (This should be stripped off by this function if needed, but may still be returned.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given argument is invalid. + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + * Returns COMMON_ERROR_INTERNAL_ERROR if a call to an engine function fails. + * Otherwise returns the appropriate error code. + * + * No alteration clause: + * In case of error, this function will not alter any given argument. + * (For the purposes of this no-alteration clause, the error codes COMMON_ERROR_RANGE_ERROR and COMMON_ERROR_END_OF_DATA + * are considered errors.) + */ +int FileUtills_Get_Last_Path_Component(char ** retStr, size_t * retStrLength, const int getParent); - Returns 0 if file or directory exists on the system. - Returns -1 if file or directory does not exist on the system. - Returns -2 if an error occurs. - Note it will also return -2 if the function does not have an implementation for your OS / arch. -*/ -int DoesExist(const std::string & Filename); +/*! + * int FileUtills_Get_File_Name_Component(const char * path, const size_t pathLength, char ** retStr, size_t * retStrLength, + * const int getExtension) + * + * Fetches the file name or extension from the given path and creates a sub-string from it. + * + * (This is a wrapper around DataProcess_Get_SubString_Using_Delimiter(), and as such has similar error codes.) + * + * @pram getExtension whether or not to get the file extension. If set to zero the file name will be put into the sub-string, + * otherwise the file extension will be put into the sub-string. + * + * Returns COMMON_ERROR_SUCCESS if the file name / extension was found and a sub-string was created. + * Returns COMMON_ERROR_RANGE_ERROR if the path is a path without an name / extension. + * Returns COMMON_ERROR_END_OF_DATA if the path is a path with an extension separator ('.') (on the end / at the beginning) + * and nothing (before / after) it. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given argument is invalid. + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + * Returns COMMON_ERROR_INTERNAL_ERROR if a call to an engine function fails. + * Otherwise returns the appropriate error code. + * + * No alteration clause: + * In case of error, this function will not alter any given argument. + * (For the purposes of this no-alteration clause, the error codes COMMON_ERROR_RANGE_ERROR and COMMON_ERROR_END_OF_DATA + * are considered errors.) + */ +int FileUtills_Get_File_Name_Component(const char * path, const size_t pathLength, char ** retStr, size_t * retStrLength, const int getExtension); /*! - FileUtills::IsFileOrDirectory(const std::string & path) + * int FileUtills_GetUserProfileDirectoryPath(char ** retStr, size_t * retStrSize) + * + * Fetches the user's profile directory from the environment, and stores + * it in the given path argument. + * + * If the function fails for any reason, the arguments will NOT be altered, + * and the appropriate error will be returned to the caller. + */ +int FileUtills_GetUserProfileDirectoryPath(char ** retStr, size_t * retStrSize); - Checks to see if a given path is a file or directory. +/*! + * int FileUtills_GetCurrentWorkingDirectoryPath(char ** path, size_t * pathSize) + * + * Fetches the current working directory from the environment, and stores + * it in the given retStr argument. + * + * If the function fails for any reason, the arguments will NOT be altered, + * and the appropriate error will be returned to the caller. + */ +int FileUtills_GetCurrentWorkingDirectoryPath(char ** retStr, size_t * retStrSize); - Returns 0 if the path exists but it is not a file or directory. - Returns 1 if the path is a file. - Returns 2 if the path is a directory. - Returns -3 if the given system is unsupported. - Returns -4 if a permissions error occurs. - Returns -5 if the given path is empty. - Returns -6 if a path componet does not exist. - Returns -7 if the path has a file in it and is not at the end. (I.e you are treating a file as a directory.) - Returns -9 on all other errors. -*/ -int IsFileOrDirectory(const std::string & path); +/*! + * int FileUtills_GetExecDirectory(char ** path, size_t * pathSize) + * + * Replaces the contents of the given retStr argument with a NULL + * terminated C-String that contains the path to the directory where the + * executable is stored. + * + * If the function fails for any reason, the arguments will NOT be altered, + * and the appropriate error will be returned to the caller. + */ +int FileUtills_GetExecDirectory(char ** retStr, size_t * retStrSize); /*! - int FileUtills::CheckParent(const std::string & path, bool read, bool write) + int getDirectory(const char * path, const size_t pathSize, FileUtills_dirlist_T ** dirTree, const bool & cleanList) - Acts as a wrapper to DoesExist and CheckPermissions - Checks for the parent directory's existance in the path given. - Also Checks to see if it is accessible. By default it checks for Read and Write access. + Lists the given directory's contents. - Ex. This path is given "/home/user/Homework.txt" this function will check and see if the "/home/user" parent - directory exists, and if it is accessable. + Pram: path, path of directory to check. - Returns 0 if the directory exists and is accessible with the requested permissions. - Returns -1 if A permission error occurs - Returns -2 if the parent directory does not exist. - Returns -3 if the OS / Arch is not supported. - Returns -5 if an unknown error occurs. + Pram: FileUtills_dirlist ** dirTree, pointer to a pointer to a + FileUtills_dirlist structure that will contain the resulting + directory listing if successful. If the structure is already + allocated when passed to this function and this function succeeds, + the pre-existing structure will be deallocated. + + Pram: cleanList, If this is true then, the generated list will be sorted + (via decrementing sort), and the list will have the entries for the current + and parent directories removed. + Default is to do nothing, and return the list as the OS generated it. + + WARNING: The OS generated list has no guarantee that it will always show up + in the same order, as that depends on the underlying OS and filesystem. + + If you need a predictable list, (I.e. a list that always shows up in the same + order (excluding ANY modifications to the filesystem)) then you should always + set cleanList to true when calling this function. + + Returns COMMON_ERROR_SUCCESS if successfull, dirTree's pointer will point to a valid + FileUtills_dirlist structure with the given path's contents in this case. + (Any previous structure pointed to by the dirTree pointer will be deallocated.) + + Otherwise this function will return the appropriate error, and dirTree's pointer will + not be modified. */ -int CheckParent(const std::string & path, bool read = true, bool write = true); +int FileUtills_getDirectory(const char * path, const size_t pathSize, FileUtills_dirlist_T ** dirTree, const bool cleanList); /*! - std::string FileUtills::GetParent(const std::string & path) + int FileUtills_DoesExist(const char * path, const size_t pathSize) - Returns the parent directory for the given file. + Prams: @std::string path, path to check for existence on the filesystem. - Gets the path exactly like FileUtills::CheckParent(). + This function checks to see if given file or directory exists. - Returns std::string with parent directory path if successful. - Returns an empty string otherwise. + This function is called by FileUtills_CheckPermissions() before running permission checks. + + Returns FILEUTILLS_ERROR_EXISTANT if file or directory exists on the system. + Returns FILEUTILLS_ERROR_NON_EXISTANT if file or directory does not exist on the system. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given path is empty. + Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given system is unsupported. + Returns the appropriate error in all other instances. */ -std::string GetParent(const std::string & path); +int FileUtills_DoesExist(const char * path, const size_t pathSize); /*! - std::string FileUtills::CheckPathType(const std::string & path) - - Checks the path given, and converts it to a absolute path. + FileUtills_IsFileOrDirectory(const char * path, const size_t pathSize) + + Checks to see if a given path is a file or directory. + + Returns COMMON_ERROR_SUCCESS if the path exists but it is not a file or directory. + Returns FILEUTILLS_ERROR_PATH_IS_A_FILE if the path is a file. + Returns FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY if the path is a directory. + Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the given system is unsupported. + Returns COMMON_ERROR_ACCESS_DENIED if a permissions error occurs. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given path is empty. + Returns FILEUTILLS_ERROR_NON_EXISTANT if a path component does not exist. + Returns FILEUTILLS_ERROR_ if the path has a file in it and is not at the end. (I.e you are treating a file as a directory.) + Returns the appropriate error in all other instances. +*/ +int FileUtills_IsFileOrDirectory(const char * path, const size_t pathSize); - Returns absoulte path on success. - Returns a zero length string otherwise. +/*! + int FileUtills_CheckParent(const char * path, const size_t pathSize, const bool read, const bool write, const bool exec) + + Acts as a wrapper to DoesExist and CheckPermissions + Checks for the parent directory's existence in the path given. + Also Checks to see if it is accessible. By default it checks for Read and Write access. + (Optionally it can check for execute permissions, and any combination of the three. This + function will return a COMMON_ERROR_INVALID_ARGUMENT error however if all of the permissions to check + are false.) + + Ex. If this path is given: "/home/user/Homework.txt", this function will check and see if the "/home/user" parent + directory exists, and if it is accessible. + + The returned error code is always copied to Common::commonLastErrorCode. + + Returns COMMON_ERROR_SUCCESS if the directory exists and is accessible with the requested permissions. + Returns COMMON_ERROR_ACCESS_DENIED if A permission error occurs. + Returns FILEUTILLS_ERROR_NON_EXISTANT if the parent directory does not exist. + Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the OS / Arch is not supported. + Returns COMMON_ERROR_SYSTEM_SPECIFIC if a system specific / untranslated error occurs. + Returns COMMON_ERROR_INTERNAL_ERROR if an engine error occurs. + Returns COMMON_ERROR_UNKNOWN_ERROR if an unknown error occurs. + Returns COMMON_ERROR_EXCEPTION_THROWN if an exception is thrown. + Returns the appropriate error in all other cases. */ -std::string CheckPathType(const std::string & path); +int FileUtills_CheckParent(const char * path, const size_t pathSize, const bool read, const bool write, const bool exec); /*! - short FileUtills::CreateDirectory(const std::string & directory, Panic::ERROR & error, const bool & createRecursive) - @pram : directory, path to create. - @pram : error, error handler for a human readable error message. - @pram : createRecursive, if this is set to true, then this function will try to create the - parent directories of the given directory if they do not exist. + int FileUtills_GetParent(char ** retStr, size_t * retStrLength) + + Returns the parent directory for the given file. - Creates given directory on the filesystem. + This function is now a wrapper for FileUtills_Get_Last_Path_Component() with getParent set to true. - This function uses the Panic::ERROR class. + WARNING: This function will NOT deallocate the given c-string argument (*retStr). If this function returns COMMON_ERROR_SUCCESS, + the given c-string pointer (*retStr) will be overwritten with a pointer to the newly created sub-string. If you need to deallocate + the original c-string, copy the pointer elsewhere before calling this function. - Returns 0 if directory creation was successful. - Returns -1 if permission is denied. - Returbs -2 if the parent directory does not exist and createRecursive is false. - Returns -3 if this function is unsupported. - Returns -4 if the disk is full. - Returns -5 if the directory already exists. - Returns -6 if another error is encountered. + Returns COMMON_ERROR_SUCCESS with parent directory path set in retStr, and length of retStr set in retStrLength if successful. + (retStr's size will be set in retStrSize.) + + Returns the appropriate error code otherwise. (See error code description for FileUtills_Get_Last_Path_Component() for error codes.) + (retStr and retStrLength will NOT be altered in this case.) */ -short CreateDirectory(const std::string & directory, Panic::ERROR & error, const bool & createRecursive = false); +int FileUtills_GetParent(char ** retStr, size_t * retStrLength); /*! - short FileUtills::CreateDirectory(const std::string & directory, const bool & createRecursive) - @pram : directory, path to create. - @pram : createRecursive, if this is set to true, then this function will try to create the - parent directories of the given directory if they do not exist. + int FileUtills_ResolvePath(const char * path, const size_t pathSize, char ** retStr, size_t * retStrSize, const bool disableSymLinkResolution) - Creates given directory on the filesystem. + Checks the path given, and converts it to a absolute path. The result is allocated by this function, + and a pointer to it is stored in retStr. The size of the result is stored in retStrSize. - Returns 0 if directory creation was successful. - Returns -1 if permission is denied. - Returbs -2 if the parent directory does not exist and createRecursive is false. - Returns -3 if this function is unsupported. - Returns -4 if the disk is full. - Returns -5 if the directory already exists. - Returns -6 if another error is encountered. -*/ -short CreateDirectory(const std::string & directory, const bool & createRecursive = false); + WARNING: If this function returns success, the contents of retStr and retStrSize will be overwritten, + and no deallocation attempt will be made on them. (Even if they were created by this function.) + If the contents of retStr and retStrSize are needed for use after this function returns, then the + contents should be copied elsewhere before calling this function. -/*! - int FileUtills::CheckPermissions(const std::string & path, Panic::ERROR & error, bool read, bool write) + When the result of this function is no longer needed it should be deallocated with FileUtills_Deallocate_CString(). - Checks Permissions on the given file or directory. Also checks if the given file or directory - actully exists first before checking other permissions. By default it will check for both read and - write permissions. + Setting disableSymLinkResolution to true will disable resolving any symbolic link(s) if a + symbolic link is encountered while resolving the given path(s). Setting + disableSymLinkResolution to false will make this function resolve any + symbolic link(s) that are encountered while resolving the given path(s). - This function uses the Panic::ERROR class. + Returns COMMON_ERROR_SUCCESS if successful, retStr and retStrSize will be altered in this + case. - Pram: path to directory or file to check. - Pram: error handler instance. - Pram: check for read permission. default is true. - Pram: check for wrtie permission. default is true. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given path is empty, it's size is less than or equal to zero, + or a given pointer is NULL. - Return: 0 if all permission checks pass. - Return: -1 if permission check fails. - Return: -2 if the file does not exist. - Return: -3 if a diffrent error occurs. Call ReturnLastError for details. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + + Returns COMMON_ERROR_INTERNAL_ERROR if an unexpected error occurs while processing the path. + + No alteration clause: + In case of error, this function will not alter any given argument. */ -int CheckPermissions(const std::string & path, Panic::ERROR & error, bool read = true, bool write = true); +int FileUtills_ResolvePath(const char * path, const size_t pathSize, char ** retStr, size_t * retStrSize, const bool disableSymLinkResolution); /*! - int FileUtills::CheckPermissions(const std::string & path, bool read, bool write) - Checks Permissions on the given file or directory. Also checks if the given file or directory - actully exists first before checking other permissions. By default it will check for both read and - write permissions. - - Pram: path to directory or file to check. - Pram: check for read permission. default is true. - Pram: check for wrtie permission. default is true. + int FileUtills_CreateDirectory(const char * directory, const size_t directorySize, const bool createRecursive) + @pram : directory, path to create. + @pram : createRecursive, if this is set to true, then this function will try to create the + parent directories of the given directory if they do not exist. + + This function attempts to create the given directory on the filesystem. + + Returns COMMON_ERROR_SUCCESS if directory creation was successful. + Returns COMMON_ERROR_ACCESS_DENIED if permission is denied. + Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if this function is unsupported. + Returns FILEUTILLS_ERROR_FILESYSTEM_FULL if the disk is full. + Returns FILEUTILLS_ERROR_EXISTANT if the directory already exists. + Returns the appropriate error or COMMON_ERROR_UNKNOWN_ERROR if another error is encountered. +*/ +int FileUtills_CreateDirectory(const char * directory, const size_t directorySize, const bool createRecursive); - Return: 0 if all permission checks pass. - Return: -1 if permission check fails. - Return: -2 if the file does not exist. - Return: -3 if a diffrent error occurs. +/*! + int FileUtills_CheckPermissions(const char * path, const size_t pathSize, const bool read, const bool write, const bool exec) + + Checks Permissions on the given file or directory. Also checks if the given file or directory + actually exists first before checking other permissions. By default it will check for both read and + write permissions. (Optionally it can check for execute permissions, and any combination of the three. This + function will return a COMMON_ERROR_INVALID_ARGUMENT error however if all of the permissions to check + are false.) + + Pram: path to directory or file to check. + Pram: check for read permission. default is true. + Pram: check for write permission. default is true. + Pram: check for execute permission. default is false. + + Returns COMMON_ERROR_SUCCESS if the directory exists and is accessible with the requested permissions. + Returns COMMON_ERROR_ACCESS_DENIED if A permission error occurs. + Returns FILEUTILLS_ERROR_NON_EXISTANT if the parent directory does not exist. + Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the OS / Arch is not supported. + Returns COMMON_ERROR_SYSTEM_SPECIFIC if a system specific / untranslated error occurs. + Returns COMMON_ERROR_INTERNAL_ERROR if an engine error occurs. + Returns COMMON_ERROR_UNKNOWN_ERROR if an unknown error occurs. + Returns COMMON_ERROR_EXCEPTION_THROWN if an exception is thrown. + Returns the appropriate error in all other cases. */ -int CheckPermissions(const std::string & path, bool read = true, bool write = true); +int FileUtills_CheckPermissions(const char * path, const size_t pathSize, const bool read, const bool write, const bool exec); /*! - int FileUtills::GetGigaFreespace(const std::string & path, size_t & result) + int FileUtills_GetGigaFreespace(const char * path, const size_t pathSize, size_t * result) Returns the remaining disk space in Gigabytes (SI Unit) on the given disk. Note if an error occurs, result will be set to zero, and an error code will be returned. (Although result can also be set to zero if there really is no remaining disk space.) - @pram const std::string & path : volume / path to check. + @pram const char * path : string that contains the volume / path to check. + @pram const size_t pathSize: length of the path string. @pram size_t & result : The remaining space on the filesystem. - Returns Common::COMMON_SUCCESS if successfull, result will contain the remaining space. - Returns a Common namespace error if an error occurs. Size will be equal to zero in this + Returns COMMON_ERROR_SUCCESS if successful, result will contain the remaining space. + Returns a Common name-space error if an error occurs. result will not be altered in this case. To obtain more detailed info register an error hander before calling this function. */ -int GetGigaFreespace(const std::string & path, size_t & result); +int FileUtills_GetGigaFreespace(const char * path, const size_t pathSize, size_t * result); /*! - int FileUtills::GetFreespace(const std::string & path, size_t & result) + int FileUtills_GetFreespace(const char * path, const size_t pathSize, size_t * result) Returns the remaining disk space in Megabytes (SI Unit) on the given disk. Note if an error occurs, result will be set to zero, and an error code will be returned. (Although result can also be set to zero if there really is no remaining disk space.) - @pram const std::string & path : volume / path to check. + @pram const char * path : string that contains the volume / path to check. + @pram const size_t pathSize: length of the path string. @pram size_t & result : The remaining space on the filesystem. - Returns Common::COMMON_SUCCESS if successfull, result will contain the remaining space. - Returns a Common namespace error if an error occurs. Size will be equal to zero in this + Returns COMMON_ERROR_SUCCESS if successful, result will contain the remaining space. + Returns a Common name-space error if an error occurs. Size will be equal to zero in this case. To obtain more detailed info register an error hander before calling this function. */ -int GetFreespace(const std::string & path, size_t & result); +int FileUtills_GetFreespace(const char * path, const size_t pathSize, size_t * result); /*! - int FileUtills::GetKiloFreespace(const std::string & path, size_t & result) + int FileUtills_GetKiloFreespace(const char * path, const size_t pathSize, size_t * result) Returns the remaining disk space in Kilobytes (SI Unit) on the given disk. Note if an error occurs, result will be set to zero, and an error code will be returned. (Although result can also be set to zero if there really is no remaining disk space.) - @pram const std::string & path : volume / path to check. + @pram const char * path : string that contains the volume / path to check. + @pram const size_t pathSize: length of the path string. @pram size_t & result : The remaining space on the filesystem. - Returns Common::COMMON_SUCCESS if successfull, result will contain the remaining space. - Returns a Common namespace error if an error occurs. Size will be equal to zero in this + Returns COMMON_ERROR_SUCCESS if successful, result will contain the remaining space. + Returns a Common name-space error if an error occurs. Size will be equal to zero in this case. To obtain more detailed info register an error hander before calling this function. */ -int GetKiloFreespace(const std::string & path, size_t & result); +int FileUtills_GetKiloFreespace(const char * path, const size_t pathSize, size_t * result); /*! - int FileUtills::GetByteFreespace(const std::string & path, size_t & result) + int FileUtills_GetByteFreespace(const char * path, const size_t pathSize, size_t * result) Returns the remaining disk space in Bytes (SI Unit) on the given disk. Note if an error occurs, result will be set to zero, and an error code will be returned. (Although result can also be set to zero if there really is no remaining disk space.) - @pram const std::string & path : volume / path to check. + @pram const char * path : string that contains the volume / path to check. + @pram const size_t pathSize: length of the path string. @pram size_t & result : The remaining space on the filesystem. - Returns Common::COMMON_SUCCESS if successfull, result will contain the remaining space. - Returns a Common namespace error if an error occurs. Size will be equal to zero in this + Returns COMMON_ERROR_SUCCESS if successful, result will contain the remaining space. + Returns a Common name-space error if an error occurs. Size will be equal to zero in this case. To obtain more detailed info register an error hander before calling this function. */ -int GetByteFreespace(const std::string & path, size_t & result); +int FileUtills_GetByteFreespace(const char * path, const size_t pathSize, size_t * result); /*! - short FileUtills::DeletePath(const std::string & path, const bool & recursive) - - Deletes the file or directory given by path. - - By default this function will NOT delete recursively. - If the given path is a non-empty directory, by default this function will throw an error. - If the given path is an empty directory, however by default this function WILL delete it. - - Note: If while deleting rescursively, a file or subdirectory can't be deleted, this function will try to continue - deleting any other files and subdirectories that can be deleted, but it will throw an error apon exit. - - Returns 0 if successful. - -1 if the user lacks permission to delete the path. (Or if recursively deleting a directory, a file or subdirectory could not be deleted.) - -2 if the path does not exist. (Or if recursively deleting a directory, a file or subdirectory does not exist.) - -3 if the function is not supported. - -4 if a memory error occurs. - -5 if an unknown error occurs. (An error other than a permissions error occured while deleting something.) - -6 if the path is a non empty directory and recursive is set to false. - -7 if while deleting recursively, the parent directory of a deleted directory could not be obtained. (I.e could not "go up a level in the directory tree.") - -8 if while deleting recursively, there were files and or subdirectories that could not be deleted. (Some files may have been deleted however.) + int FileUtills_DeletePath(const char * path, const size_t pathSize, const bool recursive) + + This function attempts to delete the given file or directory from the filesystem. + + By default this function will NOT delete recursively. + If the given path is a non-empty directory, by default this function will throw an error. + If the given path is an empty directory, however by default this function WILL delete it. + + Note: If while deleting recursively, a file or subdirectory can't be deleted, this function will try to continue + deleting any other files and subdirectories that can be deleted, but it will throw an error upon exit. + + Returns COMMON_ERROR_SUCCESS if successful. + COMMON_ERROR_ACCESS_DENIED if the user lacks permission to delete the path. (Or if recursively deleting a directory, a file or subdirectory could not be deleted.) + FILEUTILLS_ERROR_NON_EXISTANT if the path does not exist. (Or if recursively deleting a directory, a file or subdirectory does not exist.) + COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the function is not supported. + COMMON_ERROR_MEMORY_ERROR if a memory error occurs. + COMMON_ERROR_UNKNOWN_ERROR if an unknown error occurs. (An error other than a permissions error occured while deleting something.) + FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY if the path is a non empty directory and recursive is set to false. + COMMON_ERROR_IO_ERROR if while deleting recursively, the parent directory of a deleted directory could not be obtained. (I.e could not "go up a level in the directory tree.") + COMMON_ERROR_IO_ERROR if while deleting recursively, there were files and or subdirectories that could not be deleted. (Some files may have been deleted however.) */ -short DeletePath(const std::string & path, const bool & recursive = false); +int FileUtills_DeletePath(const char * path, const size_t pathSize, const bool recursive); /*! - short FileUtills::CopyFile(const std::string & src, const std::string & dest, const bool & append, - const streamsize & begOffset, const streamsize & endOffset) + int FileUtills_CopyFile(const char * srcPath, const size_t srcPathSize, const char * destPath, const size_t destPathSize, + const bool append, const size_t begOffset, const size_t endOffset) Copies endOffset bytes starting at begOffset, from source file to dest file. Offset rules: - If offsets are used, they must be positive. (If one of the given offsets is negative an error is returned.) - If zeros are used for both offsets, then the entire file will be copied. - If endOffset is greater than begOffset an error will be returned. - - This function only works on FILES. Not directories. To copy a directory, call FileUtills::CopyPath(). - - @pram src, absolute path to the source file. - @pram dest, absolute path to the dest file. - @pram append, Whether or not to append data to the dest file. (Note only applies if the dest file exists. - If append is false, then the dest file will be overwritten.) + - If offsets are used, they must be positive. (If one or both of the given offsets is negative, + then COMMON_ERROR_INVALID_ARGUMENT will be returned.) + + - If zeros are used for both offsets, then the entire file will be copied. + + - If endOffset is greater than begOffset then the given range of bytes + from the file will be copied. + + - If begOffset is equal to zero and endOffset is greater than zero, then + the file will be copied from the start of the file to endOffset. + + - If begOffset is greater than zero and endOffset is equal to zero, then + the file will be copied from begOffset to the end of the file. + + - If any offset is not zero, then ONLY IF the file has enough bytes in it + to copy the given range of bytes from the given beginning offset, will the + copy take place. + (This allows for some insurance if the source file's size is known before + calling this function.) + + This function only works on FILES. Not directories. To copy a directory, call FileUtills_CopyPath(). + + @pram src, path to the source file. + @pram dest, path to the dest file. + + @pram append, Whether or not to append data to the dest file. + (Note only applies if the dest file exists. If append is false, then the dest file will be overwritten.) + @pram begOffset, Location in the source file to start copying data from. @pram endOffset, Location in the source file to stop copying data when it is reached. By default the entire file is copied and the dest file is overwritten. - Note: This function expects that both given paths are absolute. (I.e Complete paths to both files.) - (If the paths are not absolute, then the source and dest files must be relative to the current working directory.) - - Returns 0 on success. - Returns -1 if a given offset is negative. - Returns -5 if the given begOffset is bigger than the given endOffset. (I.e you reversed the offsets.) - Returns -9 if the memory buffer could not be allocated. - Returns -10 if the source file was not given. - Returns -11 if the source file could not be opened. - Returns -12 if the beginning offset given is larger than the source file. - Returns -13 if the ending offset given is larger than the source file. - Returns -14 if an I/O error occured while reading the source file. - Returns -15 if a logical error occured while reading the source file. - Returns -16 if end of file was encountered unexpectedly. (I.e It was expected that the source file had more data in it.) - Returns -17 if the given source file was a directory. - Returns -20 if the dest file was not given. - Returns -21 if dest file could not be opened. - Returns -24 if an I/O error occured while writing to the dest file. - Returns -25 if a logical error occured while writing to the dest file. - Returns -27 if the given dest file was a directory. - Returns -33 if FileUtills::IsFileOrDirectory() returns -3. (OS / Arch not supported.) - Returns -34 if FileUtills::IsFileOrDirectory() returns -4. (A permissions error occured.) - Returns -35 if FileUtills::IsFileOrDirectory() returns -5. (The given path is empty.) - Returns -36 if FileUtills::IsFileOrDirectory() returns -6. (A path componet does not exist.) - Returns -37 if FileUtills::IsFileOrDirectory() returns -7. (The path has a file in it and is not at the end. (I.e you are treating a file as a directory.)) - Returns -39 if FileUtills::IsFileOrDirectory() returns -9. (All other errors.) - Returns -99 if an exception is thrown while copying data. + Note: This function does NOT make any attempt to preserve the destination file if it already exists. + + Note: This function can return ANY error in the Common name-space error list, below is only an example + of what errors this function itself may generate. (I.e. This function calls other FileUtills functions + and as such can return the errors generated by them.) If you need a more detailed error message, you + should register a callback function for the Common error handler (via Common::Register_Error_Log_Callback()) + before calling this function. + + Returns COMMON_ERROR_SUCCESS on success. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given offset is negative. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given begOffset is bigger than the given endOffset. (I.e you reversed the offsets.) + Returns COMMON_ERROR_MEMORY_ERROR if the memory buffer could not be allocated. + Returns COMMON_ERROR_INVALID_ARGUMENT if the source file was not given. + Returns COMMON_ERROR_IO_ERROR if the source file could not be opened. + Returns COMMON_ERROR_IO_ERROR if an I/O error occurred while reading the source file. + Returns COMMON::FILEUTILLS_PATH_IS_A_DIRECTORY if the given source file was a directory. + Returns COMMON_ERROR_INVALID_ARGUMENT if the dest file was not given. + Returns COMMON_ERROR_IO_ERROR if dest file could not be opened. + Returns COMMON_ERROR_IO_ERROR if an I/O error occurred while writing to the dest file. + Returns FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY if the given dest file was a directory. + Returns COMMON_ERROR_EXCEPTION_THROWN if an exception is thrown while copying data. */ -short CopyFile(const std::string & src, const std::string & dest, const bool & append = false, - const streamsize & begOffset = 0, const streamsize & endOffset = 0); +int FileUtills_CopyFile(const char * srcPath, const size_t srcPathSize, const char * destPath, const size_t destPathSize, const bool append, + const size_t begOffset, const size_t endOffset); /*! - short FileUtills::CopyPath(const std::string & src, const std::string & dest, const bool & recursive, - const bool & rename, const bool & abort_on_failure, - const bool & append, const streamsize & begOffset, - const streamsize & endOffset) + int FileUtills_CopyPath(const char * srcPath, const size_t srcPathSize, const char * destPath, const size_t destPathSize, const bool recursive, + const bool rename, const bool abort_on_failure, const bool append, const size_t begOffset, const size_t endOffset) This function takes a given source path and copies it to the given dest path. This function supports files and directories. - (Do note that while copying files, you must give ABS (absolute) paths to the src and dest. - If you give a file as src and a directory as dest, the function WILL return an error.) + If you give a file as src and a directory as dest, the function WILL return an error. - If the given src is a file, then this function acts as a wrapper to FileUtills::CopyFile(), and returns all of it's + If the given src is a file, then this function acts as a wrapper to FileUtills_CopyFile(), and returns all of it's error codes. If the given src is a directory, then this function will copy the entire directory to the given dest, creating dest if - nessacarry. If recursive is set to true, then the entire directory AND it's subdirectories will be copied. + necessary. If recursive is set to true, then the entire directory AND it's subdirectories will be copied. (The subdirectories will be created as needed, if a subdirectory already exists, then the data from src will be merged.) @pram recursive, if this is true, then this function will recursively copy all subdirectories from src. Merging / creating @@ -444,7 +803,7 @@ short CopyFile(const std::string & src, const std::string & dest, const bool & a @pram rename, if this is set to true, then this function will try to rename the file rather than copy it. otherwise a copy will be performed (Default). - It should be noted that a rename is equivelent to calling CopyFile(), and then DeletePath() on the same source. + It should be noted that a rename is equivalent to calling CopyFile(), and then DeletePath() on the same source. (Minus the overhead and extra disk space use of actually copying the source first.) And that renaming a src will only work if the dest is on the same filesystem. In the event that rename fails, then a copy will be performed UNLESS abort_on_failure is set to true. In that case @@ -455,58 +814,63 @@ short CopyFile(const std::string & src, const std::string & dest, const bool & a otherwise this function will try to continue with the remaining list of files and subdirectories. (Default) @pram append, this is only used if src is a file. (As this function acts as a wrapper to CopyFile() in that case, - see FileUtills::CopyFile() for it's description. This function provides the same default value for this pram as CopyFile().) + see FileUtills_CopyFile() for it's description. This function provides the same default value for this pram as CopyFile().) If src is a directory, this pram has no effect. @pram begOffset, this is only used if src is a file. (As this function acts as a wrapper to CopyFile() in that case, - see FileUtills::CopyFile() for it's description. This function provides the same default value for this pram as CopyFile().) + see FileUtills_CopyFile() for it's description. This function provides the same default value for this pram as CopyFile().) If src is a directory, this pram has no effect. @pram endOffset, this is only used if src is a file. (As this function acts as a wrapper to CopyFile() in that case, - see FileUtills::CopyFile() for it's description. This function provides the same default value for this pram as CopyFile().) + see FileUtills_CopyFile() for it's description. This function provides the same default value for this pram as CopyFile().) If src is a directory, this pram has no effect. Below are return codes for when src is a directory, if src is a file, then the return codes for this function are identical to - FileUtills::CopyFile(). Please see FileUtills::CopyFile() for it's return codes. + FileUtills_CopyFile(). Please see FileUtills_CopyFile() for it's return codes. - Returns 0 on success. + Returns COMMON_ERROR_SUCCESS on success. Returns -1 if the function was unable to create top level dest path and top level dest path does not exist. Returns -2 if the top level dest path exists and is a file or some other filesystem entry. - Returns -3 if the host OS / Arch is unsupported. + Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED if the host OS / Arch is unsupported. Returns -4 if the function could not get a directory listing. Returns -5 if the function could not get parent directory string. Returns -6 if the function was unable to copy all files. (Some files may have been copied however.) - Returns -13 if FileUtills::IsFileOrDirectory() returns -3. (OS / Arch not supported.) - Returns -14 if FileUtills::IsFileOrDirectory() returns -4. (A permissions error occured.) - Returns -15 if FileUtills::IsFileOrDirectory() returns -5. (The given path is empty.) - Returns -16 if FileUtills::IsFileOrDirectory() returns -6. (A path componet does not exist.) - Returns -17 if FileUtills::IsFileOrDirectory() returns -7. (The path has a file in it and is not at the end. (I.e you are treating a file as a directory.)) - Returns -19 if FileUtills::IsFileOrDirectory() returns -9. (All other errors.) + Returns -13 if FileUtills_IsFileOrDirectory() returns -3. (OS / Arch not supported.) + Returns -14 if FileUtills_IsFileOrDirectory() returns -4. (A permissions error occurred.) + Returns -15 if FileUtills_IsFileOrDirectory() returns -5. (The given path is empty.) + Returns -16 if FileUtills_IsFileOrDirectory() returns -6. (A path component does not exist.) + Returns -17 if FileUtills_IsFileOrDirectory() returns -7. (The path has a file in it and is not at the end. (I.e you are treating a file as a directory.)) + Returns COMMON_ERROR_UNKNOWN_ERROR if FileUtills_IsFileOrDirectory() returns -9. (All other errors.) */ -short CopyPath(const std::string & src, const std::string & dest, const bool & recursive = false, - const bool & rename = false, const bool & abort_on_failure = false, - const bool & append = false, const streamsize & begOffset = 0, const streamsize & endOffset = 0); +int FileUtills_CopyPath(const char * srcPath, const size_t srcPathSize, const char * destPath, const size_t destPathSize, const bool recursive, + const bool rename, const bool abort_on_failure, const bool append, const size_t begOffset, const size_t endOffset); /*! - short FileUtills::MovePath(const std::string & src, const std::string & dest, const bool & overwrite) + int FileUtills_MovePath(const char * srcPath, const size_t srcPathSize, const char * destPath, const size_t destPathSize, const bool overwrite) Acts as a wrapper for a call to CopyPath and DeletePath. - The src file is copied to the dest file and then the src file is deleted. + The srcPath is copied to the destPath and then the srcPath is deleted. - Returns 0 on success. + Returns COMMON_ERROR_SUCCESS on success. Returns -10 If an error occurs while accessing the src file. Returns -20 If dest already exists and overwriting is disabled. Returns -21 If dest already exists and can't be deleted. (overwrite = true) Returns -22 If an error occurs while accessing the dest file. - Returns -3 If an Implementation does not exist for your system. - Returns -4 If an error occurs while moving data. - Returns -5 If an argument to the function is bad. + Returns COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED If an Implementation does not exist for your system. + Returns COMMON_ERROR_IO_ERROR If an error occurs while moving data. + Returns COMMON_ERROR_INVALID_ARGUMENT If an argument to the function is bad. */ -short MovePath(const std::string & src, const std::string & dest, const bool & overwrite = false); +int FileUtills_MovePath(const char * srcPath, const size_t srcPathSize, const char * destPath, const size_t destPathSize, const bool overwrite); -} +#ifdef __cplusplus +} /* End of extern "C" */ +#endif /* __cplusplus. */ +/* Include C++ header if needed. */ +#ifdef __cplusplus +#include "FileUtills_CPP.h" +#endif /* __cplusplus. */ -#endif +#endif /* FILEUTILLS_H */ -// End of FileUtills.h +/* End of FileUtills.h */ diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_CPP.h b/src/Common/Src/File_Management_Subsystem/FileUtills_CPP.h new file mode 100644 index 0000000..fe8aa9c --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_CPP.h @@ -0,0 +1,113 @@ +/*! + Multiverse Engine Project 17/12/2015 FileUtills FileUtills_CPP.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef FILEUTILLS_CPP_H +#define FILEUTILLS_CPP_H + +/* Check for defined FILEUTILLS_H. */ +#ifndef FILEUTILLS_H +#error "This header should not be included directly. It will be included automaticly by FileUtills.h if the compiler is a C++ compiler." +#endif /* FILEUTILLS_H */ + +/* Check for a C++ compiler. */ +#ifdef __cplusplus + +namespace FileUtills { + /*! + * int FileUtills::GetUserProfileDirectoryPath(std::string & path) + * + * This function is a wrapper around FileUtills_GetUserProfileDirectoryPath() that takes the + * string returned from FileUtills_GetUserProfileDirectoryPath() and turns it into an std::string object. + * + * Fetches the user's profile directory from the environment, and stores + * it in the given path argument. + * + * If the function fails for any reason, the arguments will NOT be altered, + * and the appropriate error will be returned to the caller. + * + * Returns COMMON_ERROR_SUCCESS if successful. + * Otherwise returns all error codes from FileUtills_GetUserProfileDirectoryPath(). See documentation + * for the error code descriptions. + */ + int GetUserProfileDirectoryPath(std::string & path); + + /*! + * int FileUtills::GetCurrentWorkingDirectoryPath(std::string & path) + * + * This function is a wrapper around FileUtills_GetCurrentWorkingDirectoryPath() that takes the + * string returned from FileUtills_GetCurrentWorkingDirectoryPath() and turns it into an std::string object. + * + * Fetches the current working directory from the environment, and stores + * it in the given path argument. + * + * If the function fails for any reason, the arguments will NOT be altered, + * and the appropriate error will be returned to the caller. + * + * Returns COMMON_ERROR_SUCCESS if successful. + * Otherwise returns all error codes from FileUtills_GetCurrentWorkingDirectoryPath(). See documentation + * for the error code descriptions. + */ + int GetCurrentWorkingDirectoryPath(std::string & path); + + /*! + * int FileUtills::GetExecDirectory(std::string & path) + * + * This function is a wrapper around FileUtills_GetExecDirectory() that takes the + * string returned from FileUtills_GetExecDirectory() and turns it into an std::string object. + * + * Fetches the engine's executable directory from the environment, and stores + * it in the given path argument. + * + * If the function fails for any reason, the arguments will NOT be altered, + * and the appropriate error will be returned to the caller. + * + * Returns COMMON_ERROR_SUCCESS if successful. + * Otherwise returns all error codes from FileUtills_GetExecDirectory(). See documentation + * for the error code descriptions. + */ + int GetExecDirectory(std::string & path); + + /*! + * int FileUtills_ResolvePath(const std::string path, std::string & resolvedPath, const bool disableSymLinkResolution) + * + * Checks the path given, and converts it to a absolute path. + * + * This function is a wrapper to FileUtills_ResolvePath(), and returns all of it's error codes. + * + * Setting disableSymLinkResolution to true will disable resolving any symbolic link(s) if a + * symbolic link is encountered while resolving the given path(s). Setting + * disableSymLinkResolution to false will make this function resolve any + * symbolic link(s) that are encountered while resolving the given path(s). + * + * Returns COMMON_ERROR_SUCCESS if successful, retStr and retStrSize will be altered in this + * case. + * + * Otherwise returns all error codes from FileUtills_ResolvePath(). See documentation + * for the error code descriptions. + */ + int ResolvePath(const std::string path, std::string & resolvedPath, const bool disableSymLinkResolution); +} /* End of FileUtills namespace. */ + +#endif /* __cplusplus */ + +#endif /* FILEUTILLS_CPP_H */ + +/* End of FileUtills_CPP.h. */ diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Emulated_Symbolic_Link_Functions.cpp b/src/Common/Src/File_Management_Subsystem/FileUtills_Emulated_Symbolic_Link_Functions.cpp new file mode 100644 index 0000000..3e26c7f --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Emulated_Symbolic_Link_Functions.cpp @@ -0,0 +1,320 @@ +/*! + Multiverse Engine Project 21/8/2014 FileUtills FileUtills_Emulated_Symbolic_Link_Functions.cpp + Yes we are reinventing the wheel here, go with it. + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Include header file. +#include "FileUtills_Emulated_Symbolic_Link_Functions.h" +#include "../Byte_Order.h" /* Defines the various Common_*_Endianness_Check() functions. */ + +int FileUtills::Create_MSYS_Emulated_Symbolic_Link(const char * linkDestionation, const size_t linkDestionationSize, + const char * pathToLinkFile, const size_t pathToLinkFileSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; /* The result of this function. */ + int retFromC = 0; /* The result of C calls. */ + FILE * outputFile = NULL; /* The FILE pointer for the emulated link file. */ + size_t tempSize = 0; /* Temporary size used to calculate the size of variables. */ + + /* Check for valid arguments. */ + if ((linkDestionation != NULL) && (pathToLinkFile != NULL) && (linkDestionationSize > 0) && (pathToLinkFileSize > 0)) + { + /* Check and see if the file already exists. */ + ret = FileUtills::DoesExist(pathToLinkFile, pathToLinkFileSize); + if (ret == FILEUTILLS_ERROR_NON_EXISTANT) + { + /* Open the new link file for writing. */ + outputFile = fopen(pathToLinkFile, "wb"); + if (outputFile != NULL) + { + /* Now output the magic string. */ + ret = FileUtills_Write_Data_To_File_From_Memory(outputFile, MSYS_EMU_SYMLINK_MAGIC, sizeof MSYS_EMU_SYMLINK_MAGIC); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Set tempSize to the length of a size_t. (length of the path length integer.) */ + tempSize = sizeof (size_t); + + /* Determine if we need to swap the byte order of the integers. (size of length and the length itself are supposed to be big-endian encoded.) */ + if (Common_UINT_Endianness_Check() == MSYS_BIG_ENDIAN) + { + /* Now we need to output the number of bytes for the linkDestionation string's length. (ie. sizeof(size_t)). (This is in big-endianess format.) */ + ret = FileUtills_Write_Data_To_File_From_Memory(outputFile, ((char*)(&tempSize)), MSYS_EMU_SYMLINK_BYTE_COUNT_FIELD_SIZE); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Now we need to output the length of the linkDestionation string. (This is in big-endianess format.) */ + tempSize = linkDestionationSize; + ret = FileUtills_Write_Data_To_File_From_Memory(outputFile, ((char*)(&tempSize)), sizeof(size_t)); + } + } + else + { + /* Convert the bits to big endian first. */ + ret = Common_Host_To_Big_Endian_UInt(&tempSize); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Now we need to output the number of bytes for the linkDestionation string's length. (ie. sizeof(size_t)). (This is in big-endianess format.) */ + ret = FileUtills_Write_Data_To_File_From_Memory(outputFile, ((char*)(&tempSize)), MSYS_EMU_SYMLINK_BYTE_COUNT_FIELD_SIZE); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Now convert the length of the string to big-endian format. */ + tempSize = linkDestionationSize; + ret = Common_Host_To_Big_Endian_UInt(&tempSize); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Now we need to output the length of the linkDestionation string. (This is in big-endianess format.) */ + ret = FileUtills_Write_Data_To_File_From_Memory(outputFile, ((char*)(&tempSize)), sizeof(size_t)); + } + } + } + } + + /* Only continue if we were successful in outputting the length. */ + if (ret == COMMON_ERROR_SUCCESS) + { + /* Output the linkDestionation string. */ + ret = FileUtills_Write_Data_To_File_From_Memory(outputFile, linkDestionation, linkDestionationSize); + } + + /* Close the file. */ + retFromC = fclose(outputFile); + if (retFromC != 0) + { + if (ret == COMMON_ERROR_SUCCESS) + { + /* OK, we could not close the file for some reason. */ + ret = COMMON_ERROR_IO_ERROR; + COMMON_LOG_DEBUG("FileUtills_Create_MSYS_Emulated_Symbolic_Link(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_IO_ERROR)); + COMMON_LOG_DEBUG(" Could not close link file."); + + } + else + { + /* Previous errors detected. */ + COMMON_LOG_DEBUG("FileUtills_Create_MSYS_Emulated_Symbolic_Link(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_IO_ERROR)); + COMMON_LOG_DEBUG(" Could not close link file, another error was detected: "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(ret)); + ret = COMMON_ERROR_IO_ERROR; + } + } + } + } + else + { + /* Could not open output file for writing. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log the error. */ + COMMON_LOG_DEBUG("FileUtills_Create_MSYS_Emulated_Symbolic_Link(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_IO_ERROR)); + COMMON_LOG_DEBUG(" could not open ( "); + COMMON_LOG_DEBUG(pathToLinkFile); + COMMON_LOG_DEBUG(" ) for writing."); + } + } + else + { + /* File is not empty, abort. */ + ret = FILEUTILLS_ERROR_EXISTANT; + + /* Log the error. */ + COMMON_LOG_INFO("FileUtills_Create_MSYS_Emulated_Symbolic_Link(): "); + COMMON_LOG_INFO(Common_Get_Error_Message(FILEUTILLS_ERROR_EXISTANT)); + COMMON_LOG_INFO(" The given path ( "); + COMMON_LOG_INFO(pathToLinkFile); + COMMON_LOG_INFO(" ) already exists."); + } + } + else + { + /* Invalid destionationLink. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + + /* Log the error. */ + COMMON_LOG_DEBUG("FileUtills_Create_MSYS_Emulated_Symbolic_Link(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(" Given destination link string is invalid."); + } + + /* Return the result. */ + return ret; +} + +int FileUtills::Read_MSYS_Emulated_Symbolic_Link(char ** retStr, size_t * retStrSize, const char * pathToLinkFile, + const size_t pathToLinkFileSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; /* The result of this function. */ + int retFromC = 0; /* The result of C calls. */ + unsigned int byteCount = 0; /* The number of bytes in a size_t variable, as reported from the file. */ + FILE * inputFile = NULL; /* The FILE pointer for the emulated link file. */ + char * tempRetStr = NULL; /* Used to construct the retStr before we copy the pointer at the end. */ + size_t tempSize = 0; /* Temporary size used to calculate the size of variables. */ + size_t fileSize = 0; /* Size of the given input file. */ + + + /* Check for valid arguments. */ + if ((retStr != NULL) && (pathToLinkFile != NULL) && (retStrSize != NULL) && (pathToLinkFileSize > 0)) + { + /* Open the file for reading. */ + inputFile = fopen(pathToLinkFile, "rb"); + if (inputFile != NULL) + { + /* Get the size of the file. */ + + + /* Allocate the needed tempRetStr buffer. */ + tempRetStr = (char*)malloc(MSYS_FILEUTILLS_IO_BUFFER_LENGTH); + if (tempRetStr != NULL) + { + /* OK, check and see if this is a link file. */ + ret = FileUtills_Read_Bytes_From_File(IN, (sizeof MSYS_EMU_SYMLINK_MAGIC), tempRetStr, MSYS_FILEUTILLS_IO_BUFFER_LENGTH, 0, true); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Check and see if the given bytes are the correct magic signature. */ + if (memcmp(tempRetStr, MSYS_EMU_SYMLINK_MAGIC, (sizeof MSYS_EMU_SYMLINK_MAGIC)) == 0) + { + /* Valid magic signature found, check and see if we can store the byte count in a size_t variable. */ + if (MSYS_EMU_SYMLINK_BYTE_COUNT_FIELD_SIZE < (sizeof(size_t))) + { + /* Load the length of size_t from the file. */ + ret = FileUtills_Read_Bytes_From_File(IN, MSYS_EMU_SYMLINK_BYTE_COUNT_FIELD_SIZE, tempRetStr, MSYS_FILEUTILLS_IO_BUFFER_LENGTH, ((sizeof (size_t)) - MSYS_EMU_SYMLINK_BYTE_COUNT_FIELD_SIZE), true); + if (ret == COMMON_ERROR_SUCCESS) + { + /* Memcpy the data into tempSize. */ + memcpy(((char*)&tempSize), tempRetStr, sizeof (tempSize)); + + /* Get the system's endianess. */ + ret = Common_SIZE_T_Endianness_Check(); + switch (ret) + { + MSYS_BIG_ENDIAN: + /* Nothing to do. */ + ret = COMMON_ERROR_SUCCESS; + break; + MSYS_LITTLE_ENDIAN: + /* We need to convert the bytes. */ + ret = Common_Big_Endian_To_Host_UInt(tempSize); + break; + MSYS_UNKNOWN_ENDIANNESS: + default: + /* ERROR, we cannot convert the bytes due to unknown host endianess. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + break; + }; + + /* Make sure we can store the size_t from the file. */ + if (tempSize <= sizeof(size_t)) + { + /* Get link length variable. */ + ret = FileUtills_Read_Bytes_From_File(IN, , tempRetStr , ); + if (ret == COMMON_ERROR_SUCCESS) + { + /* OK copy the bytes from the tempRetStr buffer to byteCount. */ + memcpy(((char*)&byteCount), tempRetStr, MSYS_EMU_SYMLINK_BYTE_COUNT_FIELD_SIZE); + + /* Check and see if we need to convert the bytes to the host's format. */ + ret = Common_SIZE_T_Endianness_Check(); + switch (ret) + { + MSYS_BIG_ENDIAN: + /* Nothing to do. */ + ret = COMMON_ERROR_SUCCESS; + break; + MSYS_LITTLE_ENDIAN: + /* We need to convert the bytes. */ + ret = Common_Big_Endian_To_Host_UInt(tempSize); + break; + MSYS_UNKNOWN_ENDIANNESS: + default: + /* ERROR, we cannot convert the bytes due to unknown host endianess. */ + ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; + break; + }; + + /* Check for success. */ + if (ret == COMMON_ERROR_SUCCESS) + { + /* OK, check and see if our size_t can store that much data. */ + if (byteCount <= (sizeof size_t)) + { + + } + else + { + /* Size of length variable is too big for us to process. */ + ret = ; + } + } + } + } + } + } + } + else + { + /* Could not allocate memory. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + + /* Close the input file. */ + retFromC = fclose(IN); + if (retFromC != 0) + { + /* Could not close the input file. */ + if (ret != COMMON_ERROR_IO_ERROR) + { + ret = COMMON_ERROR_IO_ERROR; + } + + /* Log the error. */ + COMMON_LOG_DEBUG("FileUtills_Read_MSYS_Emulated_Symbolic_Link(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_IO_ERROR)); + COMMON_LOG_DEBUG(" could not close input file."); + } + } + else + { + /* Could not open input file for reading. */ + ret = COMMON_ERROR_IO_ERROR; + + /* Log the error. */ + COMMON_LOG_DEBUG("FileUtills_Read_MSYS_Emulated_Symbolic_Link(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_IO_ERROR)); + COMMON_LOG_DEBUG(" could not open ( "); + COMMON_LOG_DEBUG(pathToLinkFile); + COMMON_LOG_DEBUG(" ) for reading."); + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + + /* Log the error. */ + COMMON_LOG_DEBUG("FileUtills_Read_MSYS_Emulated_Symbolic_Link(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Return the result. */ + return ret; +} + +/* End of FileUtills_Emulated_Symbolic_Link_Functions.cpp */ diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Emulated_Symbolic_Link_Functions.h b/src/Common/Src/File_Management_Subsystem/FileUtills_Emulated_Symbolic_Link_Functions.h new file mode 100644 index 0000000..ed99938 --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Emulated_Symbolic_Link_Functions.h @@ -0,0 +1,141 @@ +/*! + Multiverse Engine Project 21/8/2014 FileUtills FileUtills_Emulated_Symbolic_Link_Functions.h + Yes we are reinventing the wheel here, go with it. + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef FILEUTILLS_EMULATED_SYMBOLIC_LINK_FUNCTIONS_H +#define FILEUTILLS_EMULATED_SYMBOLIC_LINK_FUNCTIONS_H + +/* Internal includes. */ +#include "../Error_Handler/Common_Error_Handler.h" +#include "../Error_Handler/Common_Error_Handler_Internal.h" + +/* External includes. */ +#include +#include +#include +#include + +/* Define magic string for the emulated symbolic link file. */ +#define MSYS_EMU_SYMLINK_MAGIC "MSYS_EMU_SYMLINK" + +/* Define file extension for the emulated symbolic link file. */ +#define MSYS_EMU_SYMLINK_FILE_EXT "msl" + +/* Define size of the byte count field. */ +#define MSYS_EMU_SYMLINK_BYTE_COUNT_FIELD_SIZE 4 + +/*! + * Description of the emulated symbolic link file format: + * + * Offset 0: Magic string. (E.g. "MSYS_EMU_SYMLINK") + * + * Offset (0 + sizeof()): unsigned 32-bit integer (4 bytes, big endian) size of the length value. (I.e. sizeof(size_t)). + * + * Offset (0 + sizeof() + 4): length of the link. (Length is the same as the read size variable.) + * + * Offset (0 + sizeof() + 4 + (size of the length value)): Start of link. + * + * Offset (0 + sizeof() + 4 + (size of the length value) + ): End of the link, and end of file. + */ + + +// Define namespace. +namespace FileUtills +{ + // Define functions. + + /*! + * int FileUtills::Create_MSYS_Emulated_Symbolic_Link(const char * linkDestionation, const size_t linkDestionationSize, + * const char * pathToLinkFile, const size_t pathToLinkFileSize) + * + * Takes the given linkDestionation string and creates an emulated symbolic link file with the string's + * contents at the given path on the filesystem. + * + * NOTE: This function expects that the given path is invalid. (I.e. That no file / directory / some other + * filesystem entry exists at the given path.) In addition this function expects that the given path's + * parent directory exists at the time that this function is called. (I.e. That this function does not + * need to create the directory tree leading up to the file to create.) If either of these assumptions + * are false, this function will abort and return an error. + * + * If successful this function will return COMMON_ERROR_SUCCESS, and the emulated symbolic link file + * will be present at the given location on the filesystem. + * + * If an error occurs, the appropriate Common name-space error code will be returned. In addition, the + * emulated link file will be rendered invalid on the filesystem. (It will still be present on the + * filesystem however, as such it will need to be deleted manually.) + */ + int Create_MSYS_Emulated_Symbolic_Link(const char * linkDestionation, const size_t linkDestionationSize, + const char * pathToLinkFile, const size_t pathToLinkFileSize); + + /*! + * int FileUtills::Read_MSYS_Emulated_Symbolic_Link(char ** retStr, size_t * retStrSize, const char * pathToLinkFile, + * const size_t pathToLinkFileSize) + * + * Erases the given link argument string, then reads the file from the given path for a symbolic link + * string, and writes it to the given link argument string. + * + * NOTE: This function does NOT resolve the read symbolic link, and will return it as is on the filesystem. + * To resolve the link, either call your own resolver function, or call FileUtills::ResolvePath(). + * + * NOTE: This function is called automatically by FileUtills::ResolvePath() if symbolic link resolution is + * enabled by the caller (it is by default), and an emulated link file is found during path resolution. + * As such it's not necessary to call this function directly before FileUtills::ResolvePath(). + * + * (Sidenote: This function returns read links as is, if the read link is relative to the directory it is in, + * then the link string will need to have the path to the directory that the emulated link file is stored + * in prepended to the link string before calling FileUtills::ResolvePath(). If you fail to do this, + * FileUtills::ResolvePath() will assume that the given emulated link string is relative to the + * calling program's current working directory. (Along with all of the issues that come with that + * assumption.)) + * + * If successful, the given link argument string will have it's contents replaced with the read link + * string, and COMMON_ERROR_SUCCESS will be returned. + * + * If an error occurs then the given link argument string will be blank / empty, and the appropriate + * Common name-space error code will be returned. + */ + int Read_MSYS_Emulated_Symbolic_Link(char ** retStr, size_t * retStrSize, const char * pathToLinkFile, const size_t pathToLinkFileSize); + + /*! + * int FileUtills::Update_MSYS_Emulated_Symbolic_Link(const char * linkDestionation, const size_t linkDestionationSize, + * const char * pathToLinkFile, const size_t pathToLinkFileSize) + * + * This function updates a pre-existing emulated link file at the given path on the filesystem, with the contents + * of the given linkDestionation argument string. + * + * NOTE: This function expects that the emulated link file already exists on the filesystem at the given path. It also + * expects that the emulated link file will be valid. If either of these two conditions are not true, then this function + * will abort and return an error. + * + * If successful, the given emulated link file will contain the contents of the given linkDestionation argument string, and + * the returned error code will be COMMON_ERROR_SUCCESS. + * + * If an error occurs, the appropriate Common name-space error code will be returned. In addition, the + * emulated link file will be rendered invalid on the filesystem. (It will still be present on the + * filesystem however, as such it will need to be deleted manually.) + */ + int Update_MSYS_Emulated_Symbolic_Link(const char * linkDestionation, const size_t linkDestionationSize, + const char * pathToLinkFile, const size_t pathToLinkFileSize); +}; + +#endif /* FILEUTILLS_EMULATED_SYMBOLIC_LINK_FUNCTIONS_H */ + +/* End of FileUtills_Emulated_Symbolic_Link_Functions.h */ diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.c b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.c new file mode 100644 index 0000000..99d1c7a --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.c @@ -0,0 +1,1045 @@ +/*! + Multiverse Engine Project 08/8/2015 FileUtills FileUtills_Private_API.c + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "FileUtills.h" +#include "FileUtills_Private_API.h" +#include "../../../Core/Src/DataProcess.h" /* For DataProcess Allocators. (DataProcess_Deallocate_C_String(), DataProcess_Reallocate_CString().) */ + +/* Define the MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID macro. */ +#define MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID MSYS_ERROR_LOG_CHANNEL_FILEUTILLS + +int FileUtills_Create_dirList_PRIV_Object(MSYS_FileUtills_dirList_PRIV_T ** obj) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + MSYS_FileUtills_dirList_PRIV_T * tempObj = NULL; /* The object we are creating. */ + + /* Check for invalid pointer argument. */ + if (obj != NULL) + { + /* Allocate object. */ + tempObj = (MSYS_FileUtills_dirList_PRIV_T *)malloc((sizeof(MSYS_FileUtills_dirList_PRIV_T))); + if (tempObj != NULL) + { + /* NULL out object. */ + memset(obj, '\0', (sizeof(MSYS_FileUtills_dirList_PRIV_T))); + + /* Copy pointer. */ + (*obj) = tempObj; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for object. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +void FileUtills_Destroy_dirList_PRIV_Object(MSYS_FileUtills_dirList_PRIV_T ** obj) +{ + /* Check for valid pointer. */ + if ((obj != NULL) && ((*obj) != NULL)) + { + /* Deallocate the entry list. */ + MSYS_Linked_List_Deallocate_Entire_List((&((*obj)->list))); + + /* Release the memory. */ + free((*obj)); + (*obj) = NULL; + } + + /* Exit function. */ + return; +} + +int FileUtills_dirList_PRIV_Add_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, + const char * entry, const size_t entryLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of a call to another engine function. */ + int allocatedInitialListMemory = 0; /* Whether or not we allocated the first entry in the list. */ + MSYS_Linked_List_T * allocatedObject = NULL; /* Pointer to the allocated list entry. */ + + /* Check for invalid arguments. */ + if ((dirListPriv != NULL) && (entry != NULL) && (entryLength > 0)) + { + /* Allocate the linked list object. */ + retFromCall = MSYS_Linked_List_Allocate_And_Return_Linked_List_Object(&(dirListPriv->list), &allocatedObject); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (dirListPriv->list != NULL) && (allocatedObject != NULL)) + { + /* Increment the number of entries. */ + dirListPriv->numOfEntries++; + + /* Copy the data into the list. */ + retFromCall = MSYS_Linked_List_Set_Current_Object_Contents(allocatedObject, (void *)entry, entryLength, 1); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not create a copy of the entry data. */ + ret = ((retFromCall != COMMON_ERROR_MEMORY_ERROR) ? (COMMON_ERROR_INTERNAL_ERROR) : + (COMMON_ERROR_MEMORY_ERROR)); + + /* Decrement the number of entries. */ + dirListPriv->numOfEntries--; + + /* Deallocate the allocated list object. */ + MSYS_Linked_List_Deallocate_Linked_List_Object(&allocatedObject); + + /* Log error. */ + if (ret != COMMON_ERROR_MEMORY_ERROR) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_dirList_PRIV_Add_Entry(): Could not create a copy of the entry data."); + } + } + } + else + { + /* Could not allocate memory for the linked list object. */ + ret = ((retFromCall != COMMON_ERROR_MEMORY_ERROR) ? (COMMON_ERROR_INTERNAL_ERROR) : + (COMMON_ERROR_MEMORY_ERROR)); + + /* Log error. */ + if (ret != COMMON_ERROR_MEMORY_ERROR) + { + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_dirList_PRIV_Add_Entry(): Could not allocate memory for the linked list object."); + } + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int FileUtills_dirList_PRIV_Get_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, const size_t entryOffset, + char ** entry, size_t * entryLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of a call to another engine function. */ + MSYS_Linked_List_T * iterator = NULL; /* Iterator used to iterate through the linked list. */ + char * fetchedEntry = NULL; /* Pointer to data in linked list object. */ + size_t fetchedEntryLength = 0; /* Length of fetchedEntry. */ + char * returnedEntry = NULL; /* Pointer to copy of the entry data that is returned to the caller. */ + size_t x = 0; /* Loop counter. */ + + /* Check for invalid arguments. */ + if ((dirListPriv != NULL) && (dirListPriv->list != NULL) && (entry != NULL) && (entryLength != NULL)) + { + /* Get the first iterator pointer. */ + retFromCall = MSYS_Linked_List_Get_First_Object(dirListPriv->list, &iterator); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Begin iteration loop. (Start at index one as we already have the first entry.) */ + for (x = 1; ((x < entryOffset) && (retFromCall == COMMON_ERROR_SUCCESS) && (iterator != NULL)); x++) + { + /* Get the next object. */ + retFromCall = MSYS_Linked_List_Get_Next_Object(iterator, &iterator); + } + + /* Check for valid iterator. */ + if (iterator != NULL) + { + /* Get the contents of the correct entry. */ + retFromCall = MSYS_Linked_List_Get_Current_Object_Contents(iterator, ((void**)(&fetchedEntry)), &fetchedEntryLength, 1); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Check and see if there is entry data at all.... */ + if ((fetchedEntry != NULL) && (fetchedEntryLength > 0)) + { + /* Copy the new pointer to the caller. */ + (*entry) = fetchedEntry; + (*entryLength) = fetchedEntryLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* No entry data. */ + (*entry) = NULL; + (*entryLength) = 0; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* Unable to get object's data contents. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_dirList_PRIV_Get_Entry(): Unable to get object's data contents."); + } + } + else + { + /* Invalid index. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* Could not get initial iterator pointer. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_dirList_PRIV_Get_Entry(): Could not get initial iterator pointer."); + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int FileUtills_dirList_PRIV_Remove_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, const size_t entryOffset) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of a call to another engine function. */ + MSYS_Linked_List_T * iterator = NULL; /* Iterator used to iterate through the linked list. */ + char * entry = NULL; /* Pointer to the entry data. */ + size_t entryLength = 0; /* Length of the entry data. */ + size_t x = 0; /* Loop counter. */ + + /* Check for invalid arguments. */ + if ((dirListPriv != NULL) && (dirListPriv->list != NULL)) + { + /* Get the first iterator pointer. */ + retFromCall = MSYS_Linked_List_Get_First_Object(dirListPriv->list, &iterator); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Begin iteration loop. (Start at index one as we already have the first entry.) */ + for (x = 1; ((x < entryOffset) && (retFromCall == COMMON_ERROR_SUCCESS) && (iterator != NULL)); x++) + { + /* Get the next object. */ + retFromCall = MSYS_Linked_List_Get_Next_Object(iterator, &iterator); + } + + /* Check for valid iterator. */ + if (iterator != NULL) + { + /* Deallocate the entry's linked list object. */ + MSYS_Linked_List_Deallocate_Linked_List_Object(&iterator); + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Non-existant entry. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* Could not get initial iterator pointer. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_dirList_PRIV_Remove_Entry(): Could not get initial iterator pointer."); + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +void FileUtills_dirList_PRIV_Deallocate_Entry_Data_Copy(char ** data) +{ + /* Check for valid pointer. */ + if ((data != NULL) && ((*data) != NULL)) + { + /* Deallocate the data. */ + MSYS_Linked_List_Deallocate_Copied_Data(data); + } + + /* Exit function. */ + return; +} + +int FileUtills_IsAbsolutePathReference(const char * path, const size_t pathSize) +{ + /* Call FileUtills_IsAbsolutePathReference_absRef(). */ + return (FileUtills_IsAbsolutePathReference_absRef(path, pathSize, NULL, NULL)); +} + +int FileUtills_IsAbsolutePathReference_absRef(const char * path, const size_t pathSize, char ** absRef, size_t * absRefLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* Result of this function. */ + char * tempAbsRef = NULL; /* Temporary variable for construction of absRef. */ + + /* Check for a valid arguments. */ + if ((path != NULL) && (pathSize > 0)) + { + /* Check for a valid string. */ + if (path[0] == '/') /* Posix style path. */ + { + /* Valid Posix style path. */ + ret = FILEUTILLS_ERROR_PATH_IS_ABSOLUTE; + + /* OPTIONAL: If absRef and absRefLength are non-NULL attempt to copy the absolute path reference. */ + if ((absRef != NULL) && (absRefLength != NULL)) + { + /* Allocate memory for absRef. */ + tempAbsRef = (char*)malloc((sizeof(char) * 2)); + if (tempAbsRef != NULL) + { + /* Write the data. */ + tempAbsRef[0] = '/'; + tempAbsRef[1] = '\0'; + + /* Copy the pointer. */ + (*absRef) = tempAbsRef; + + /* Update the absRefLength. */ + (*absRefLength) = (sizeof(char) * 2); + } + else + { + /* Could not allocate memory. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Log error. */ + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsAbsolutePathReference(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Unable to allocate memory for absRef buffer."); + } + } + } + else + { + /* Check for a Windows / DOS style path. */ + if ((pathSize > 2) && (path[0] != '\0') && (path[1] == ':') && (path[2] == '\\')) + { + /* Valid Windows / DOS style path. */ + ret = FILEUTILLS_ERROR_PATH_IS_ABSOLUTE; + + /* OPTIONAL: If absRef and absRefLength are non-NULL attempt to copy the absolute path reference. */ + if ((absRef != NULL) && (absRefLength != NULL)) + { + /* Allocate memory for absRef. */ + tempAbsRef = (char*)malloc((sizeof(char) * 4)); + if (tempAbsRef != NULL) + { + /* Write the data. */ + tempAbsRef[0] = path[0]; + tempAbsRef[1] = path[1]; + tempAbsRef[2] = path[2]; + tempAbsRef[3] = '\0'; + + /* Copy the pointer. */ + (*absRef) = tempAbsRef; + + /* Update the absRefLength. */ + (*absRefLength) = (sizeof(char) * 4); + } + else + { + /* Could not allocate memory. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Log error. */ + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsAbsolutePathReference(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Unable to allocate memory for absRef buffer."); + } + } + } + else + { + /* Path is not absolute. */ + ret = FILEUTILLS_ERROR_PATH_IS_RELATIVE; + } + } + } + else + { + /* Invalid pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsAbsolutePathReference(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Return the result. */ + return ret; +} + +int FileUtills_ResolvePath_Helper(char ** retStr, size_t * retStrSize) +{ + /* Init vars. */ + bool eraseLoopDone = false; /* Used to tell when the loop for erasing the current directory segment from the output is done. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code from this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code from another engine function. */ + const char * pCurrentPos = NULL; /* Used to access the path string. */ + size_t x = 0; /* Counter used in parsing loop. */ + size_t initalOffset = 0; /* Used to start parsing at the correct offset if the given path starts with a resolveable identifier. */ + size_t currentOutputPos = 0; /* Used to store the current position in the output string. + * (Reasoning for this is that I can't determine if any of + * the insertion operators consider NULL characters to not + * be a valid part of the string's value. + * (E.x. for the given string: "some\0\0\0" is the current + * insertion point 4 or is it 7? Also does the C++ standard + * define this, or is the result implimentation defined, or + * is this possibly undefined behavour?)) + */ + char * output = NULL; /* Result of this function. */ + size_t outputLength = 0; /* Length of the output string. */ + char * homeDir = NULL; /* Used if we need to get the home (User profile) directory. */ + size_t homeDirLength = 0; /* Length of the homeDir string. */ + char * currentDir = NULL; /* Used if we need to get the current working directory. */ + size_t currentDirLength = 0; /* Length of the currentDir string. */ + + /* Check for invalid args. */ + if ((retStr != NULL) && ((*retStr) != NULL) && (retStrSize != NULL) && ((*retStrSize) > 0)) + { + /* Results: + * + * Does nothing but copy the path string to the output + * string if the path is already absolute. + * + * If the path is not absolute, then the path is made + * absolute before being returned. + * + * Default is to assume the path is Relative. + * + * This function does NOT filter invalid characters in + * the given path. (Valid characters are filesystem + * specific. The OS in use may not allow the user to + * access that information without special privileges. + * In addition it's not practical for us to maintain a + * list of valid characters for each filesystem type in + * existance. (Some OSes may not have an easy mechinism + * for determining what characters are valid vs. invalid + * short of attempting to actually use them.)) As such + * any given invalid character will be retained in the + * output string if this function succeeds. + * + * Notes: About linux behavior. + * /foo/bar/../fee translates to /foo/fee. (../ nulls out bar.) + * /foo/./bar translates to /foo/bar. (./ is ignored or translates to the current path without it.) + * /foo/./bar/../ translates to /foo. (./ is ignored like above, ../ nulls out bar.) + * /foo/./bar/../.././ translates to /. (the first ./ is ignored like above, the first ../ nulls out bar, the second ../ nulls out foo, and the last ./ is ignored like above.) + * /foo/./bar/../../~ results in a no such file or directory error. (Apperently ~ only has a special meaning if it is located at the beginning of the given path string. Also not all shells resolve it.) + * /foo/./bar/../../$HOME translates to $HOME. (./ is ignored like above, the first ../ nulls out bar, the second ../ nulls out foo, and $ is treated as the start of an enviorment variable. the enviorment variable's name is HOME (the space is the delimiter.)) + * ./foo translates to /foo. + * ../foo translates to /foo. + * foo translates to /foo. + */ + + /* Get a pointer to the path string. */ + pCurrentPos = (*retStr); + + /* Make sure we got the pointer. */ + if (pCurrentPos != NULL) + { + /* First allocate enough memory for the current path string. */ + retFromCall = DataProcess_Reallocate_C_String(&output, 0, (*retStrSize)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (output != NULL)) + { + /* Set the outputLength. */ + outputLength = (*retStrSize); + + /* + * Check to see if the path is a user profile directory path. + * + * This only has a special meaning if there is a DIR_SEP as the next character, + * or if it is the only character in the given path string. + * + * If this is not the case, then the HOME_DIR_SYMBOL looses it's special meaning, + * and is assumed to be a part of the current path segment. + */ + if ((pCurrentPos[0] == HOME_DIR_SYMBOL) && ((outputLength == 1) || ((outputLength > 1) && (pCurrentPos[1] == DIR_SEP)))) + { + /* Get the user profile directory path. */ + ret = FileUtills_GetUserProfileDirectoryPath(&homeDir, &homeDirLength); + if ((ret == COMMON_ERROR_SUCCESS) && (homeDir != NULL) && (homeDirLength > 0)) + { + /* Allocate memory for the output string to hold the home dir path. */ + retFromCall = DataProcess_Reallocate_C_String(&output, 0, (outputLength + (homeDirLength))); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (output != NULL)) + { + /* Set the length of the output string. */ + outputLength += homeDirLength; + + /* Set the user profile directory. */ + memcpy(output, homeDir, homeDirLength); + + /* Update currentOutputPos. */ + currentOutputPos = homeDirLength; + + /* Set the initial offset. */ + initalOffset = 1; + } + else + { + /* Unable to reallocate memory for the output string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Unable to get user profile directory. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Unable to get needed user profile directory path, aborting.\n"); + + /* Deallocate output. */ + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + } + else + { + /* Check for an absolute path reference. */ + /* We need to check for a abosolute reference here. (If it is an absolute reference we do nothing.) */ + retFromCall = FileUtills_IsAbsolutePathReference((*retStr), (*retStrSize)); + if (retFromCall == FILEUTILLS_ERROR_PATH_IS_RELATIVE) + { + /* The default is to assume that the path is relative to the current working directory. */ + /* Get the current working directory. */ + retFromCall = FileUtills_GetCurrentWorkingDirectoryPath(¤tDir, ¤tDirLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentDir != NULL) && (currentDirLength > 0)) + { + /* Allocate memory for the output string to hold the current dir path. */ + retFromCall = DataProcess_Reallocate_C_String(&output, 0, (outputLength + currentDirLength)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (output != NULL)) + { + /* Set the length of the output string. */ + outputLength += currentDirLength; + + /* Copy the current working directory. */ + memcpy(output, currentDir, currentDirLength); + + /* Update currentOutputPos. */ + currentOutputPos = currentDirLength; + + /* + * Continue parsing looking for another dot as we may not be done yet. + * + * If this is not the case, then we assume that the given path is a path + * segment that starts in the given working directory. + */ + if (pCurrentPos[0] == '.') + { + /* Check and see if there is something else to parse after the dot. */ + if ((*retStrSize) > 1) + { + /* Check for another dot. */ + if (pCurrentPos[1] == '.') /* ".." (Parent directory of the current working directory.) */ + { + /* + * Check for the end of the path or that there is another directory seperator present. + * + * If this is not the case then the first two dots loose their special meaning, and we + * assume that the first two dots are part of the current path segment. + * (That or the caller made a typo....) + */ + if (((*retStrSize) == 2) || (((*retStrSize) > 2) && (pCurrentPos[2] == DIR_SEP)) || (((*retStrSize) == 3) && (pCurrentPos[2] == '\0'))) + { + /* Erase the last directory segment from the output. */ + retFromCall = FileUtills_RemoveLastPathSegment(&output, &outputLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (outputLength > 0)) + { + /* Skip the first two dots. */ + initalOffset = 2; + } + else + { + /* FileUtills_RemoveLastPathSegment() failed. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Call to FileUtills_RemoveLastPathSegment() failed, unable to get needed parent directory. Aborting.\n"); + + /* Deallocate output. */ + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + } + } + } + else + { + /* + * The dot is by itself, assumed to be a reference to the + * current working directory. + * + * (Rather than a reference to a file in the current working + * directory whose filename is a dot.) + */ + initalOffset = 1; + } + } + else + { + /* This is the start of a directory entry, so add a DIR_SEP to output and increment currentOutputPos. */ + output[currentOutputPos] = DIR_SEP; + currentOutputPos++; + } + } + else + { + /* Could not allocate memory to hold the currentDir in the output string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Could not get current working directory. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Unable to get needed current working directory path, aborting.\n"); + + /* Deallocate output. */ + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + } + else + { + /* Check for FILEUTILLS_ERROR_PATH_IS_ABSOLUTE. */ + if (retFromCall != FILEUTILLS_ERROR_PATH_IS_ABSOLUTE) + { + /* Call to FileUtills_IsAbsolutePathReference() failed. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Could not determing if the given path was an absolute path reference, aborting.\n"); + + /* Deallocate output. */ + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + } + } + + /* Start processing loop. */ + for (x = initalOffset; ((x < (*retStrSize)) && (ret == COMMON_ERROR_UNKNOWN_ERROR)); x++) + { + /* Selection switch. */ + switch (pCurrentPos[x]) + { + case '.': /* Dot ('.') character. Normally used for indicating + the current or parent directory. + + For the dot character to have a special meaning, + one of the following conditions must be true: + + - There must be a DIR_SEP at either the + (x + 1) or (x + 2) position (but not both) in + the given path string. (In the case of the + latter, position (x + 1) must have another + dot character for (x + 2) to have a meaning.) + + - The dot characters (either "." or "..") must + be at the end of the given path string, and + have nothing after them. (The terminating NULL + byte for c-style strings is permitted however.) + + If both of these conditions are false, then the + dot character is assumed to be part of the + current path segment, and therefore loses it's + special meaning. + */ + + /* Check and see if we have at least one character left after the current position. */ + if ((x + 1) < (*retStrSize)) + { + /* Check for "." current working directory variant. */ + if (pCurrentPos[(x + 1)] == DIR_SEP) + { + /* Increment x to skip checking the directory seperator on the next loop iteration. */ + x++; + } + else + { + /* Check for ".." variant. (Parent directory.) */ + if (pCurrentPos[(x + 1)] == '.') + { + /* + * Determine if we can continue. + * + * /foo/bar/.. is a valid path. + * (Yes, ".." only has a special meaning if it's at the end of a path. + * Otherwise it's considered part of a filesystem entry. + * Ex. "/foo/bar/..my_filename_begins_with_two_dots" is a valid filename.) + */ + if (((x + 2) >= (*retStrSize)) || + (((x + 2) < (*retStrSize)) && ((pCurrentPos[(x + 2)] == DIR_SEP) || (((x + 3) >= (*retStrSize)) && (pCurrentPos[(x + 2)] == '\0'))))) + { + /* Referening the parent directory. Check to see if we are at the root directory. (No parent path reference can pass beyond the filesystem's root directory.) */ + if (currentOutputPos > MINIMAL_VALID_ABSOLUTE_PATH_LENGTH) + { + /* Reset eraseLoopDone. */ + eraseLoopDone = false; + + /* Remove last path segment from output. (Search from the end of the output string.) */ + ret = FileUtills_RemoveLastPathSegment(&output, &outputLength); + if ((ret == COMMON_ERROR_SUCCESS) && (output != NULL) && (outputLength > 0)) + { + /* Set eraseLoopDone. */ + eraseLoopDone = true; + } + else + { + /* FileUtills_RemoveLastPathSegment() failed. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Call to FileUtills_RemoveLastPathSegment() failed. Unable to remove current path segment. Aborting.\n"); + + /* Deallocate output. */ + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + } + + /* Check to see if there are at least two characters after the current position. */ + if (((x + 2) < (*retStrSize)) && (pCurrentPos[(x + 2)] == DIR_SEP)) + { + /* Increment x by 2 to skip the dots and the directory seperator. */ + x += 2; + } + else + { + /* Increment x to skip the dots. */ + x++; + } + } + else + { + /* Allocate memory for adding dot to output string. */ + retFromCall = DataProcess_Reallocate_C_String(&output, outputLength, (outputLength + (sizeof(char)))); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (output != NULL)) + { + /* Copy the dot. */ + memcpy((output + outputLength), (pCurrentPos + x), (sizeof(char))); + + /* Update outputLength. */ + outputLength += (sizeof(char)); + + /* Increment currentOutputPos. */ + currentOutputPos++; + } + else + { + /* Could not reallocate memory for output string addition. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Deallocate output. */ + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + } + } + else + { + /* Allocate memory for adding dot to output string. */ + retFromCall = DataProcess_Reallocate_C_String(&output, outputLength, (outputLength + (sizeof(char)))); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (output != NULL)) + { + /* Copy the dot. */ + memcpy((output + outputLength), (pCurrentPos + x), (sizeof(char))); + + /* Update outputLength. */ + outputLength += (sizeof(char)); + + /* Increment currentOutputPos. */ + currentOutputPos++; + } + else + { + /* Could not reallocate memory for output string addition. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Deallocate output. */ + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + } + } + } + break; + default: + /* Check and see if the current character is a DIR_SEP and it is the last character in the string. */ + if (((x + 1) < (*retStrSize)) || (((x + 1) == (*retStrSize)) && (pCurrentPos[x] != DIR_SEP))) + { + /* We don't do anything here, except copy the data to the output buffer. */ + retFromCall = DataProcess_Reallocate_C_String(&output, outputLength, (outputLength + (sizeof(char)))); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (output != NULL) && (outputLength > 0)) + { + /* Copy the data. */ + memcpy((output + outputLength), (pCurrentPos + x), (sizeof(char))); + + /* Update outputLength. */ + outputLength += (sizeof(char)); + + /* Increment currentOutputPos. */ + currentOutputPos++; + } + else + { + /* Could not reallocate memory for output string addition. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Deallocate output. */ + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + } + break; + }; + } + + /* Set ret. */ + ret = COMMON_ERROR_SUCCESS; + + /* Copy output to retStr. */ + (*retStr) = output; + (*retStrSize) = outputLength; + + /* Log result. */ + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): Path ( "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, (*retStr)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) resolved to ( "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, output); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, ").\n"); + } + else + { + /* Could not allocate memory for output. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Could not get pointer for given path. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Unable to get pointer to given path argument.\n"); + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolvePath(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " No path given.\n"); + } + + /* Check for allocated strings, and deallocate them if needed. */ + if (homeDir != NULL) + { + DataProcess_Deallocate_CString(&homeDir); + homeDirLength = 0; + } + if (currentDir != NULL) + { + DataProcess_Deallocate_CString(¤tDir); + currentDirLength = 0; + } + if ((ret != COMMON_ERROR_SUCCESS) && (output != NULL)) + { + DataProcess_Deallocate_CString(&output); + outputLength = 0; + } + + /* Return the result. */ + return ret; +} + +int FileUtills_RemoveLastPathSegmentAtPosition(char ** path, size_t * pathSize, size_t * currentPathPos) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* Result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* Result code of other engine functions. */ + size_t tempPathSize = 0; /* The temporary variable used to store the new path's size. */ + size_t y = 0; /* Loop counter. */ + char * tempPath = NULL; /* Temporary pointer to construct the result string. */ + + /* Check for valid path. */ + if ((pathSize != NULL) && ((*pathSize) > 0) && (path != NULL) && ((*path) != NULL)) + { + /* Check for a valid path position. */ + if ((currentPathPos != NULL) && ((*currentPathPos) > 0)) + { + /* Make sure the path position is within the path buffer. */ + if ((*currentPathPos) < (*pathSize)) + { + /* Remove last path segment from output. (Search from the end of the output string.) */ + for (y = 0; (((y < (*pathSize))) && (ret == COMMON_ERROR_UNKNOWN_ERROR)); y++) + { + /* Look for the DIR_SEP. */ + if ((*path)[((*currentPathPos) - y)] == DIR_SEP) + { + /* Check to see if we have hit the first directory seperator. */ + if (((*currentPathPos) - y) == MINIMAL_VALID_ABSOLUTE_PATH_LENGTH) + { + /* Decrement y, as we need this directory seperator. */ + y--; + } + + /* Calculate the new string's length. */ + tempPathSize = ((sizeof(char)) * ((*currentPathPos) - y)); + + /* Reallocate the new path string. */ + retFromCall = DataProcess_Reallocate_C_String(&tempPath, 0, tempPathSize); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPath != NULL)) + { + /* Memcopy the data to the new buffer. */ + memcpy(tempPath, path, tempPathSize); + + /* Reset the current path position. */ + (*currentPathPos) = ((*currentPathPos) - y); + + /* Reset the pathSize. */ + (*pathSize) = tempPathSize; + + /* Copy the temp pointer. */ + (*path) = tempPath; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Call to DataProcess_Reallocate_C_String() failed. */ + if (retFromCall == COMMON_ERROR_MEMORY_ERROR) + { + ret = COMMON_ERROR_MEMORY_ERROR; + } + else + { + ret = COMMON_ERROR_INTERNAL_ERROR; + } + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_RemoveLastPathSegmentAtPosition(): Call to DataProcess_Reallocate_C_String() failed with error code: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + + /* If we get here and result is still COMMON_UNKNOWN_ERROR, then the path did not have a directory seperator in it. */ + if (ret == COMMON_ERROR_UNKNOWN_ERROR) + { + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* currentPathPos is beyond the end of the path buffer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_RemoveLastPathSegmentAtPosition(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " current path position is beyond the end of the path buffer. (Nice try.)"); + } + } + else + { + /* Invalid currentPathPos. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_RemoveLastPathSegmentAtPosition(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " current path position is invalid."); + } + } + else + { + /* No path given. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_RemoveLastPathSegmentAtPosition(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " No valid path given."); + } + + /* Check for success. */ + if (ret != COMMON_ERROR_SUCCESS) + { + /* Deallocate the tempPath string if needed. */ + if (tempPath != NULL) + { + DataProcess_Deallocate_CString(&tempPath); + } + } + + /* Return the result. */ + return ret; +} + +int FileUtills_RemoveLastPathSegment(char ** path, size_t * pathSize) +{ + /* Init vars. */ + size_t fakeCurrentPathPos = 0; /* Fake variable to satisfy FileUtills_RemoveLastPathSegmentAtPosition()'s arugment list. */ + + /* Call FileUtills_RemoveLastPathSegmentAtPosition() and return it's result. */ + return (FileUtills_RemoveLastPathSegmentAtPosition(path, pathSize, &fakeCurrentPathPos)); +} + +int FileUtills_IsFileOrDirectory_Helper(const char * absPath, const size_t absPathSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Call syscall function. */ + ret = FileUtills_IsFileOrDirectory_Syscall(absPath, absPathSize); + + /* Check return. */ + switch (ret) + { + /* VALID ERROR CODES: */ + case FILEUTILLS_ERROR_PATH_IS_A_FILE: + case FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY: + case FILEUTILLS_ERROR_PATH_IS_A_SYMLINK: + case COMMON_ERROR_SUCCESS: + break; + default: /* Called function returned an invalid error code. */ + ret = COMMON_ERROR_UNKNOWN_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectoryHelper(): Called FileUtills_IsFileOrDirectory_Syscall() function returned an invalid error code, and needs to be rewritten to conform to the error code definitions. Please report this bug."); + break; + }; + + /* Return the result. */ + return ret; +} diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.cpp b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.cpp new file mode 100644 index 0000000..86e973d --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.cpp @@ -0,0 +1,1049 @@ +/*! + Multiverse Engine Project 04/8/2014 FileUtills FileUtills_Private_API.cpp + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +#include "FileUtills.h" +#include "FileUtills_Private_API.h" + +int FileUtills::IsAbsolutePathReference(const char * path, const size_t pathSize) +{ + /* Call C function. */ + return (FileUtills_IsAbsolutePathReference(path, pathSize)); +} + +int FileUtills::IsAbsolutePathReference(const char * path, const size_t pathSize, char ** absRef, size_t * absRefSize) +{ + /* Call C function. */ + return (FileUtills_IsAbsolutePathReference_absRef(path, pathSize, absRef, absRefSize)); +} + +int FileUtills::GetPathSegment(const std::string & path, const size_t & currentPathPos, std::string & pathSegment, const bool & blankPathSegment) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + + // Blank pathSegment if needed. + if (blankPathSegment) + { + pathSegment.clear(); + } + + // Check for valid path. + if (path.capacity > 0) + { + // Check for a valid path position. + if (currentPathPos > 0) + { + // Make sure the path position is within the path buffer. + if (currentPathPos < path.capacity()) + { + // Copy the path segment from output. (Search from the given currentPathPos offset.) + for (size_t y = 0; ((y < path.capacity()) && (result == COMMON_ERROR_UNKNOWN_ERROR)); y++) + { + // Look for the DIR_SEP. + if (path[(currentPathPos - y)] == DIR_SEP) + { + // Decrement y, as we don't want to copy the directory seperator. + y--; + + // This position is the start of the directory segment. + for (size_t z = 0; ((z < y) && (((currentPathPos - y) + z) < path.capacity())); z++) + { + // Blank the value. + pathSegment += path[((currentPathPos - y) + z)]; + } + + // Set result. + result = COMMON_ERROR_SUCCESS; + } + } + + // If we get here and result is still COMMON_UNKNOWN_ERROR, then the path did not have a directory seperator in it. + if (result == COMMON_ERROR_UNKNOWN_ERROR) + { + result = COMMON_ERROR_SUCCESS; + } + } + else + { + // currentPathPos is beyond the end of the path buffer. + result = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE("FileUtills::GetPathSegment(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(result)); + COMMON_LOG_VERBOSE(" current path position is beyond the end of the path buffer. (Nice try.)\n"); + } + } + else + { + // Invalid currentPathPos. + result = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE("FileUtills::GetPathSegment(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(result)); + COMMON_LOG_VERBOSE(" current path position is invalid.\n"); + } + } + else + { + // Invalid path. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::GetPathSegment(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::GetLastPathSegment(const std::string & path, std::string & pathSegment, const bool & blankPathSegment) +{ + // Init fake currentPathPos. + size_t currentPathPos = path.size(); + + // Check for valid path size. (No need to check for false here. If this is false, then the real function will return COMMON_ERROR_INVALID_ARGUMENT. (It does it's own check.)) + if (currentPathPos > 0) + { + // Make sure currentPathPos is less than it's size. + currentPathPos--; + } + + // Call the GetPathSegment() function. + return (FileUtills::GetPathSegment(path, currentPathPos, pathSegment)); +} + +int FileUtills::RemoveLastPathSegment(std::string & path, size_t * currentPathPos) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* Result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* Result code of other engine functions. */ + size_t pathSize = 0; /* The given path's size. */ + char * tempPath = NULL; /* Temporary pointer to construct the result string. */ + std::string resultStr = ""; /* The result string of this function. */ + + /* Get the path size. */ + pathSize = path.size(); + + /* Copy the string to a C-style string. */ + retFromCall = DataProcess::CopyStdStringToCString(path, &tempPath); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPath != NULL)) + { + /* Call C function. */ + ret = FileUtills_RemoveLastPathSegmentAtPosition(&tempPath, &pathSize, currentPathPos); + if ((ret == COMMON_ERROR_SUCCESS) && (tempPath != NULL) && (pathSize > 0)) + { + /* OK, copy the result path to the path var. */ + retFromCall = DataProcess::CopyCStringToStdString(tempPath, &pathSize, &resultStr); + if (retFromCall != NULL) + { + /* Deallocate the C style string. */ + DataProcess_Deallocate_CString(&tempPath); + + /* Copy the resultStr to the path variable. */ + path = resultStr; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not copy c string to std::string. */ + ret = retFromCall; + COMMON_LOG_DEBUG("FileUtills::RemoveLastPathSegment(): Could not copy result string to path argument."); + } + } + } + else + { + /* No path given. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_VERBOSE("FileUtills_RemoveLastPathSegment(): "); + COMMON_LOG_VERBOSE(Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_VERBOSE(" No valid path given."); + } + + /* Deallocate the tempPath string if needed. */ + if (tempPath != NULL) + { + DataProcess_Deallocate_CString(&tempPath); + } + + /* Return the result. */ + return ret; +} + +int FileUtills::RemoveLastPathSegment(std::string & path) +{ + /* Init vars. */ + size_t fakeCurrentPathPos = 0; /* Fake variable to satisfy FileUtills_RemoveLastPathSegmentAtPosition()'s arugment list. */ + + /* Call FileUtills::RemoveLastPathSegment() and return it's result. */ + return (FileUtills::RemoveLastPathSegment(path, &fakeCurrentPathPos)); +} + +FileUtills::dirlist * FileUtills::getDirectory_Helper(const std::string & absPath, const bool & cleanList) +{ + // Init vars. + int errcode_SortFunct = 0; // The returned error code from DataProcess::DecrementingSort(). + FileUtills::dirlist * ret = NULL; // The constructed directory list. + + // Dumb check. + if (absPath.size() > 0) + { + // Call syscall. + ret = FileUtills::getDirectory_Syscall(absPath, cleanList); + if ((Common::commonLastErrorCode == COMMON_ERROR_SUCCESS) && (ret != NULL)) + { + // If we are cleaning the list, call DataProcess::DecrementingSort(). + if (cleanList) + { + errcode_SortFunct = DataProcess::DecrementingSort(ret->list); + if (errcode_SortFunct != 0) + { + // An exception was thrown in the DecrementingSort() function, bail out. + if (ret != NULL) + { + delete ret; + ret = NULL; + } + + // Internal error. + Common::commonLastErrorCode = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::getDirectory(): The call to DataProcess::DecrementingSort() failed, Aborting.\n"); + } + } + } + else + { + // Check for SUCCESS but no list. + if ((Common::commonLastErrorCode == COMMON_ERROR_SUCCESS) && (ret == NULL)) + { + // Bug in the syscall. + Common::commonLastErrorCode = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::getDirectory(): "); + COMMON_LOG_WARNING(Common::Get_Error_Message(Common::commonLastErrorCode)); + COMMON_LOG_WARNING(" The syscall returned an invalid directory list, but indicated success. Please report this bug.\n"); + } + else + { + // An error occured in the syscall. + if (ret != NULL) + { + delete ret; + ret = NULL; + } + + // Check for invalid argument. + if (Common::commonLastErrorCode == COMMON_ERROR_INVALID_ARGUMENT) + { + // OK the syscall is bugged. + Common::commonLastErrorCode = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::getDirectory(): "); + COMMON_LOG_WARNING(Common::Get_Error_Message(Common::commonLastErrorCode)); + COMMON_LOG_WARNING(" Syscall returned invalid argument, please report this bug.\n"); + } + } + } + } + else + { + // Invalid path. + Common::commonLastErrorCode = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG("FileUtills::getDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(Common::commonLastErrorCode)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Return the result. + return ret; +} + +int FileUtills::GetGigaFreespace_Helper(const std::string & absPath, size_t & result) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // Result of this function. + + // Issue syscall. + ret = FileUtills::GetByteFreespace_Syscall(absPath, result); + + // Check for success. + if (ret == COMMON_ERROR_SUCCESS) + { + // Byte conversion. + result = (result / ((double)1000000000)); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::GetFreespace_Helper(const std::string & absPath, size_t & result) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // Result of this function. + + // Issue syscall. + ret = FileUtills::GetByteFreespace_Syscall(absPath, result); + + // Check for success. + if (ret == COMMON_ERROR_SUCCESS) + { + // Byte conversion. + result = (result / ((double)1000000)); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::GetKiloFreespace_Helper(const std::string & absPath, size_t & result) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // Result of this function. + + // Issue syscall. + ret = FileUtills::GetByteFreespace_Syscall(absPath, result); + + // Check for success. + if (ret == COMMON_ERROR_SUCCESS) + { + // Byte conversion. + result = (result / ((double)1000)); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::GetByteFreespace_Helper(const std::string & absPath, size_t & result) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // Result of this function. + + // Issue syscall. + ret = FileUtills::GetByteFreespace_Syscall(absPath, result); + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::CreateDirectory_Helper(const std::string & absPath, const bool & createRecursive) +{ + // Init vars. + int result = COMMON_ERROR_SUCCESS; // Used to hold the result of this function. + char * pCurrentPathSegment = NULL; // Used to allocate memory for a substring that contains the current path segment. + + // Check for valid absPath. + if (absPath.size() > 0) + { + // Check and see if create recursive is false. + if (!createRecursive) + { + // Check and see if the parent directory exists. + result = FileUtills::CheckParent(absPath); + if (result == COMMON_ERROR_SUCCESS) + { + // Attempt to create the directory path. (Issue syscall.) + result = FileUtills::CreateDirectory_Syscall(absPath.c_str()); + if ((result != COMMON_ERROR_SUCCESS) || (Common::commonLastErrorCode != COMMON_ERROR_SUCCESS)) + { + // Log the error if needed. + COMMON_LOG_INFO("FileUtills::CreateDirectory(): Error returned while attempting to create directory ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) "); + COMMON_LOG_INFO(Common::Get_Error_Message(result)); + COMMON_LOG_INFO(" Aborting.\n"); + } + else + { + COMMON_LOG_INFO("FileUtills::CreateDirectory(): Created directory ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" )\n"); + } + } + else + { + // Unable to create directory. + COMMON_LOG_INFO("FileUtills::CreateDirectory(): Unable to create directory ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) as the parent directory does not exist or is inaccessable, and recursive directory creation is disabled by the caller. Aborting.\n"); + } + } + else + { + // Run directory creation loop. + for (size_t x = (MINIMAL_VALID_ABSOLUTE_PATH_LENGTH + 1); ((x < absPath.size()) && (result == COMMON_ERROR_SUCCESS)); x++) + { + // Search for the next DIR_SEP, or the end of the given path string. + if ((absPath[x] == DIR_SEP) || ((x + 1) == absPath.size())) + { + // This is the end of the next path segment, create the needed substring and create the directory. + try { + // Allocate memory buffer. + if (absPath[x] != DIR_SEP) + { + // Allocate an extra character only if we are before a directory seperator. + pCurrentPathSegment = (char*)malloc((x + 1)); + } + else + { + // If the current char is a directory seperator, then we need to allocate only the current x value. + pCurrentPathSegment = (char*)malloc(x); + } + + // Check and make sure we got the memory allocated. + if (pCurrentPathSegment != NULL) + { + // Check and see if the current char is a DIR_SEP. (Controls how much of the source buffer we need to copy.) + if (absPath[x] != DIR_SEP) + { + // Copy the data. + for (size_t y = 0; (y < (x + 1)); y++) + { + pCurrentPathSegment[y] = absPath[y]; + } + + // Terminate the string. (Arrays start at zero, x is the last valid character in the string.) + pCurrentPathSegment[x] = '\0'; + } + else + { + // Copy the data. + for (size_t y = 0; (y < x); y++) + { + pCurrentPathSegment[y] = absPath[y]; + } + + // Terminate the string. (Arrays start at zero, (x - 1) is the last valid character in the string.) + pCurrentPathSegment[(x - 1)] = '\0'; + } + + // Now create the path. (Issue system call.) + if ((FileUtills::CreateDirectory_Syscall(pCurrentPathSegment) != COMMON_ERROR_SUCCESS) || (Common::commonLastErrorCode != COMMON_ERROR_SUCCESS)) + { + // Check and see if the error code is FILEUTILLS_ERROR_EXISTANT. (This is only an error if the final directory segment cannot be created.) + if ((Common::commonLastErrorCode != FILEUTILLS_ERROR_EXISTANT) || ((Common::commonLastErrorCode == FILEUTILLS_ERROR_EXISTANT) && ((x + 1) >= absPath.size()))) + { + // Copy the error. + result = Common::commonLastErrorCode; + + // Log the error if needed. + COMMON_LOG_INFO("FileUtills::CreateDirectory(): "); + COMMON_LOG_INFO(Common::Get_Error_Message(result)); + COMMON_LOG_INFO(" Unable to create directory ( "); + COMMON_LOG_INFO(pCurrentPathSegment); + COMMON_LOG_INFO(" ), aborting.\n"); + + // Force the loop to exit. + x = absPath.size(); + } + } + else + { + // Report success. + COMMON_LOG_INFO("FileUtills::CreateDirectory(): Created directory ( "); + COMMON_LOG_INFO(pCurrentPathSegment); + COMMON_LOG_INFO(" )\n"); + } + + // Deallocate the buffer. + if (pCurrentPathSegment != NULL) + { + free(pCurrentPathSegment); + pCurrentPathSegment = NULL; + } + } + else + { + // Could not allocate memory. + result = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG("FileUtills::CreateDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG("\n"); + + // Force loop to exit. + x = absPath.size(); + } + } + catch(exception &ex) + { + // Exception thrown. + result = COMMON_ERROR_EXCEPTION_THROWN; + COMMON_LOG_DEBUG("FileUtills::CreateDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" "); + COMMON_LOG_DEBUG(ex.what()); + COMMON_LOG_DEBUG("\n"); + + // Force loop to exit. + x = absPath.size(); + } + } + } + } + } + else + { + // Invalid path. + result = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG("FileUtills::CreateDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Copy result to Common::commonLastErrorCode. + Common::commonLastErrorCode = result; + + // Return the result. + return result; +} + +int FileUtills::CheckPermissions_Helper(const std::string & absPath, const bool & read, const bool & write, const bool & exec) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + + // Call syscall. + ret = FileUtills::CheckPermissions_Syscall(absPath, read, write, exec); + if (ret != COMMON_ERROR_SUCCESS) + { + // Check for INVALID_ARGUMENT. + if (ret == COMMON_ERROR_INVALID_ARGUMENT) + { + // This is an internal error. (The caller does not need to see the invalid argument error.) + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING("FileUtills::CheckPermissions(): "); + COMMON_LOG_WARNING(Common::Get_Error_Message(ret)); + COMMON_LOG_WARNING(" Misbehaving syscall, returned invalid argument. Please report this bug.\n"); + } + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::CheckParent_Helper(const std::string & path, const bool & read, const bool & write, const bool & exec) +{ + // Init vars. + int result = COMMON_ERROR_UNKNOWN_ERROR; // Result of this function. + std::string absPath = path; // Absolute version of the given path. + + // Check for valid path. + if (absPath.size() > 0) + { + // Get the parent path. + result = FileUtills::RemoveLastPathSegment(absPath); + if (result == COMMON_ERROR_SUCCESS) + { + // Call the other functions + result = FileUtills::CheckPermissions_Helper(absPath, read, write, exec); + + // Log the result. + COMMON_LOG_INFO("FileUtills::CheckParent(): Path ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) "); + + // Check the result + switch (result) + { + // Successful. + case COMMON_ERROR_SUCCESS: + COMMON_LOG_INFO("exists and is accessable with the requested permissions.\n"); + break; + // Permission error. + case COMMON_ERROR_ACCESS_DENIED: + COMMON_LOG_INFO("Status unknown. A permissions error was encountered while performing the needed checks.\n"); + break; + // Parent Does not exist. + case FILEUTILLS_ERROR_NON_EXISTANT: + COMMON_LOG_INFO("does not exist.\n"); + break; + // Unknown error. + default: + COMMON_LOG_INFO("Call to CheckPermissions() failed, unable to check parent's permissions or existance.\n"); + break; + }; + } + else + { + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::CheckParent(): Unable to get absolute path of parent directory, aborting.\n"); + + // Set result to COMMON_ERROR_INTERNAL_ERROR. (Caller should not see the invalid argument error.) + result = COMMON_ERROR_INTERNAL_ERROR; + } + } + else + { + // Invalid path. + result = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG("FileUtills::CheckParent(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Copy result to Common::commonLastErrorCode. + Common::commonLastErrorCode = result; + + // Return the result. + return result; +} + +int FileUtills::DoesExist_Helper(const std::string & absPath) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + + // Check for invalid absPath. + if (absPath.size() > 0) + { + // Call the syscall. + ret = FileUtills::DoesExist_Syscall(absPath); + + // Get error code. + switch (ret) { + case FILEUTILLS_ERROR_EXISTANT: // Path exists. + // Log existance if needed. + COMMON_LOG_INFO("FileUtills::DoesExist(): Path ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) exists.\n"); + break; + default: // Error occured. + // Check for non-existing path error. + if (ret == FILEUTILLS_ERROR_NON_EXISTANT) + { + // Path does not exist. + COMMON_LOG_INFO("FileUtills::DoesExist(): Path ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) does not exist.\n"); + } + else + { + // Log the error. + COMMON_LOG_DEBUG("FileUtills::DoesExist(): An error occured while checking existance of path: "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG("\n"); + } + break; + }; + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::DoesExist(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::DeletePath_Helper(const std::string & absPath, const bool & recursive) +{ + // Init vars. + bool unableToDeleteAll = false; // Used to tell if we could not delete something while deleting recursively. + int result = COMMON_ERROR_UNKNOWN_ERROR; // Used to store results from calls to other functions. + size_t currentEntry = 0; // Used by the recursion loop to indicate the current entry in the directory listing that it is working on. + std::string currentAbsPath = ""; // Used to contain the current absolute path. + std::string tempPath = ""; // Used to construct temporary paths. + FileUtills::dirlist * pList = NULL; // Used to store paths for recursive deletions. + + // Check path string. + if (absPath.size() > 0) + { + // Determine the type of path given. + result = FileUtills::IsFileOrDirectory_Helper(absPath); + switch (result) + { + case COMMON_ERROR_SUCCESS: + case FILEUTILLS_ERROR_PATH_IS_A_FILE: + // Attempt to delete the file. + result = FileUtills::DeletePath_Syscall(absPath); + if (result == COMMON_ERROR_SUCCESS) + { + // Log the success. + COMMON_LOG_INFO("FileUtills::DeletePath(): Deleted ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) from the filesystem.\n"); + } + else + { + // Log the failure. + COMMON_LOG_INFO("FileUtills::DeletePath(): Unable to delete ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) from the filesystem. The returned error was: "); + COMMON_LOG_INFO(Common::Get_Error_Message(result)); + COMMON_LOG_INFO("\n"); + } + break; + case FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY: + // If we have recursive set to true we can delete the path, else return an error. + if (recursive) + { + // Copy the top level path. + currentAbsPath = absPath; + + // Blank tempPath. + tempPath.clear(); + + // Begin recursive deletion loop. + do + { + // Delete the current directory list. + if (pList != NULL) + { + delete pList; + pList = NULL; + } + + // OK we need to get the paths of every file in the directory and any other directories. + pList = FileUtills::getDirectory(currentAbsPath, true); + if ((Common::commonLastErrorCode == COMMON_ERROR_SUCCESS) && (pList != NULL)) + { + // Set currentEntry to zero. + currentEntry = 0; + + // Check to see if tempPath is defined. + if (tempPath.size() > 0) + { + // Ok, we just swapped out of a subdirectory, so we need to skip to it in the list and delete it. + for (size_t x = 0; (x < pList->list.size()); x++) + { + // Check for the subdirectory we just finished parsing. + if (((currentAbsPath + DIR_SEP) + pList->list[x]) == tempPath) + { + // Subdirectory entry found, attempt to delete it. + result = FileUtills::DeletePath_Syscall(tempPath); + if (result != COMMON_ERROR_SUCCESS) + { + // Set unableToDeleteAll. + unableToDeleteAll = true; + } + + // Set currentEntry to the next entry in the list. (The reason this works is because the Directory listing is sorted in desending order. Anything in the list prior to this position, we were unable to delete.) + currentEntry = (x + 1); + + // Clear tempPath. + tempPath.clear(); + } + } + } + + // Begin inner path loop. (Should exit early if we hit a subdirectory, or if we hit a critical error.) + for (; ((pList != NULL) && (currentEntry < (pList->list.size())) && (result != FILEUTILLS_ERROR_NON_EMPTY_DIRECTORY)); currentEntry++) + { + // Construct the path to be deleted. + tempPath = ((currentAbsPath + DIR_SEP) + pList->list[currentEntry]); + + // Attempt to delete the entry. + result = FileUtills::DeletePath_Syscall(tempPath); + if (result != COMMON_ERROR_SUCCESS) + { + // Check and see if it's a directory. + if (result == FILEUTILLS_ERROR_NON_EMPTY_DIRECTORY) + { + // Update currentAbsPath to add the new subdirectory. + currentAbsPath = ((currentAbsPath + DIR_SEP) + pList->list[currentEntry]); + } + else + { + // Skip the entry and continue. + unableToDeleteAll = true; + } + } + } + + // Blank tempPath. + tempPath.clear(); + + // Check to see if the directory list we just finished parsing is for the given path from the caller. + if ((currentAbsPath != absPath) && (result != FILEUTILLS_ERROR_NON_EMPTY_DIRECTORY)) + { + // OK, copy currentAbsPath to tempPath so that the next loop will check the parent directory for currentAbsPath's entry in the list. + tempPath = currentAbsPath; + + // Now call FileUtills::RemoveLastPathSegment() on the currentAbsPath, so we can get the parent directory's entry list on the next loop. + if (FileUtills::RemoveLastPathSegment(currentAbsPath) != COMMON_ERROR_SUCCESS) + { + // Could not remove path segment. + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_INFO("FileUtills::DeletePath(): FileUtills::RemoveLastPathSegment() returned: "); + COMMON_LOG_INFO(Common::Get_Error_Message(Common::commonLastErrorCode)); + COMMON_LOG_INFO(" Please report this bug, Aborting.\n"); + } + } + } + else + { + // Unable to get directory listing, check to see if pList is NULL and Common::commonLastErrorCode is COMMON_ERROR_SUCCESS. + if ((pList == NULL) && (Common::commonLastErrorCode == COMMON_ERROR_SUCCESS)) + { + // Internal bug in getDirectory(). (This should never happen.) + result = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_WARNING("FileUtills::DeletePath(): "); + COMMON_LOG_WARNING(" FileUtills::getDirectory() returned an invalid directory listing while indicating success, please report this bug.\n"); + } + else + { + // Preserve the returned error code from getDirectory(). + result = Common::commonLastErrorCode; + } + } + } + while ((currentAbsPath != absPath) && ((result == COMMON_ERROR_SUCCESS) || (result == FILEUTILLS_ERROR_NON_EMPTY_DIRECTORY))); + + // Reattempt to delete the given top level directory if unableToDeleteAll is not set. + if (!unableToDeleteAll) + { + // Attempt to delete the given top level directory. + result = FileUtills::DeletePath_Syscall(absPath); + if (result == COMMON_ERROR_SUCCESS) + { + // Success. + COMMON_LOG_INFO("FileUtills::DeletePath(): Deleted ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) from the filesystem.\n"); + } + else + { + // Unsuccessfull. + COMMON_LOG_INFO("FileUtills::DeletePath(): Unable to completely delete ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) from the filesystem. The the directory should be empty however. The returned error was: "); + COMMON_LOG_INFO(Common::Get_Error_Message(result)); + COMMON_LOG_INFO("\n"); + } + } + else + { + // We were unable to delete the requested path. + COMMON_LOG_INFO("FileUtills::DeletePath(): Unable to completely delete ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) from the filesystem.\n"); + } + } + else + { + // Unable to delete a directory if recursive is set to false. + COMMON_LOG_INFO("FileUtills::DeletePath(): Unable to delete ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) as it's a directory, and recursive deletion was disabled by the caller.\n"); + } + break; + default: + // Log the error. + COMMON_LOG_INFO("FileUtills::DeletePath(): Unable to delete ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) from the filesystem.\n"); + break; + }; + } + else + { + // Invalid path argument. + result = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::DeletePath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(result)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Copy result to Common::commonLastErrorCode. + Common::commonLastErrorCode = result; + + // Return the result. + return result; +} + +int FileUtills::CopyPath_Helper(const std::string & absPathSrc, const std::string & absPathDest, const bool & recursive, + const bool & rename, const bool & abort_on_failure, + const bool & append, const streamsize & begOffset, const streamsize & endOffset) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + + // Check for valid absPathSrc. + if (absPathSrc.size() > 0) + { + // Check for valid absPathDest. + if (absPathDest.size() > 0) + { + // Ok, Check and see what the source path is. + ret = FileUtills::IsFileOrDirectory_Helper(absPathSrc); + if ((ret == FILEUTILLS_ERROR_PATH_IS_A_FILE) || (ret == COMMON_ERROR_SUCCESS)) + { + // Path is either a regular file or a special file. + COMMON_LOG_WARNING("FileUtills::CopyPath(): TODO: We can only copy a special file if the system's copy syscall is used. Impliment the copy syscall functions.\n"); + + // Check to see if the dest is a file or a directory. + ret = FileUtills::IsFileOrDirectory_Helper(absPathDest); + if (ret == FILEUTILLS_ERROR_PATH_IS_A_FILE) + { + // Overwrite pre-existing file? + } + else + { + // Check and see if dest is a directory. + if (ret == FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) + { + // Copy source file to pre-existing directory. + } + else + { + // Check for non-existant path. + if (ret == FILEUTILLS_ERROR_NON_EXISTANT) + { + // Check for the parent path's existance. + ret = FileUtills::CheckParent_Helper(absPathDest); + if (ret == COMMON_ERROR_SUCCESS) + { + // Copy the source file to the destionation path with the given filename instead of it's original filename. + + } + else + { + // Check for non-existant error. + if (ret == FILEUTILLS_ERROR_NON_EXISTANT) + { + // We cannot copy something to a non-existant destionation directory. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): Cannot copy to non-existant destionation directory ( "); + COMMON_LOG_DEBUG(absPathDest.c_str()); + COMMON_LOG_DEBUG(" ). Please create the destionation directory and try to copy the source again.\n"); + } + else + { + // An error has occured. + if () + } + } + } + else + { + // An error occured. + + } + } + } + } + else + { + // Check and see if the source path is a directory. + if (ret == FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY) + { + // Check to see if recursion is enabled. + if (recursive) + { + // OK, begin try block. + try { + // Begin directory recursion loop. + + + } + catch (execption &ex) + { + // Exception thrown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" "); + COMMON_LOG_DEBUG(ex.what()); + COMMON_LOG_DEBUG("\n"); + } + } + else + { + // Cannot copy a directory if recursion is disabled. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Unable to copy a directory because the caller has disabled recursion, aborting.\n"); + } + } + else + { + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" was returned while checking the source path's filesystem type, aborting.\n"); + } + } + } + else + { + // Invalid destionation path argument. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given destionation path is invalid.\n"); + } + } + else + { + // Invalid source path argument. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CopyPath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given source path is invalid.\n"); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.h b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.h new file mode 100644 index 0000000..c72a1c9 --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API.h @@ -0,0 +1,590 @@ +/*! + Multiverse Engine Project 04/12/2011 FileUtills FileUtills_Private_API.h + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef FILEUTILLS_PRIVATE_API_H +#define FILEUTILLS_PRIVATE_API_H + +/* External includes. */ + +/* Define extern C. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ +#include +#include +#include + +#ifdef __cplusplus +} /* End of extern C. */ +#endif /* __cplusplus */ + +/* End of external includes. */ + +/* Define extern C. If needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Include the Basic_Linked_List header. */ +#include "../../../Core/Src/Basic_Linked_List.h" + +/* Define the internal MSYS_FILESIZE_PRIV structure. */ +typedef struct MSYS_FILESIZE_PRIV { +enum MSYS_FILESIZE_TYPES type; /* What type of struct it is. (Windows or POSIX. )*/ +#ifdef _MSC_VER + __int64 length; /* Length of the file. */ +#else + off_t length; /* Length of the file. */ +#endif /* _MSC_VER */ +} MSYS_FILESIZE_PRIV_T; + +/* Define the internal FileUtills_dirList_private structure. */ +typedef struct MSYS_FileUtills_dirList_PRIV { + size_t numOfEntries; /* Used to store the number of entries in the list array. */ + char * path; /* Used to store the path of the directory that the entry list is about. */ + size_t pathLength; /* Length of the path string. */ + MSYS_Linked_List_T * list; /* The actual list of entries. */ +} MSYS_FileUtills_dirList_PRIV_T; + +/* Define C functions. */ + +/*! + * void FileUtills_Deallocate_CString_Syscall(char ** str) + * + * Deallocates C strings made by FileUtills Syscall functions, and sets the given pointer to NULL. + * Once the object is deallocated, it should not be dereferenced again. + * + * If given a string NOT created by a FileUtills Syscall function, the behaviour and result of this + * function is undefined. + * + * If given an invalid (NULL) pointer this function will silently fail. + * + * This function has no return. + */ +void FileUtills_Deallocate_CString_Syscall(char ** str); + +/*! + int FileUtills_Create_dirList_PRIV_Object(MSYS_FileUtills_dirList_PRIV_T ** obj) + + Creates a MSYS_FileUtills_dirList_PRIV_T data structure, and set's obj to point to it. + + WARNING: This function will overwrite the *obj pointer. If you need the previous pointer after + this function returns, you should copy it elsewhere before calling this function. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given obj pointer is NULL. + Returns COMMON_ERROR_MEMORY_ERROR if memory allocation fails. + + No alteration clause: + In the event of an error, this function will not modifiy the arguments given to it. + */ +int FileUtills_Create_dirList_PRIV_Object(MSYS_FileUtills_dirList_PRIV_T ** obj); + +/*! + void FileUtills_Destroy_dirList_PRIV_Object(MSYS_FileUtills_dirList_PRIV_T ** obj) + + Destroys (frees) the given MSYS_FileUtills_dirList_PRIV_T data structure, and + sets the (*obj) pointer to NULL. + Once destroyed, the given object should not be reused. + + If given an object that is not a MSYS_FileUtills_dirList_PRIV_T data structure, the result is undefined. + + Returns nothing. + */ +void FileUtills_Destroy_dirList_PRIV_Object(MSYS_FileUtills_dirList_PRIV_T ** obj); + +/*! +int FileUtills_dirList_PRIV_Add_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, + const char * entry, const size_t entryLength) + + Copies the given entry data and inserts it into the entry list. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers are NULL or the entry length is less than or equal to zero. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation fails. + Returns COMMON_ERROR_INTERNAL_ERROR if an unexpected error occurs. + Otherwise returns the appropriate error code. + + No alteration clause: + In the event of an error, this function will not modifiy the arguments given to it. +*/ +int FileUtills_dirList_PRIV_Add_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, + const char * entry, const size_t entryLength); + +/*! + int FileUtills_dirList_PRIV_Get_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, const size_t entryOffset, + char ** entry, size_t * entryLength); + + Returns a copy of the requested entry's data. (If any.) + This copy should be deallocated via FileUtills_dirList_PRIV_Deallocate_Entry_Data_Copy() + when it is no longer needed. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointers are NULL. + Returns COMMON_ERROR_RANGE_ERROR if the given entry offset is beyond the end of the entry list. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation fails. + Returns COMMON_ERROR_INTERNAL_ERROR if an unexpected error occurs. + Otherwise returns the appropriate error code. + + No alteration clause: + In the event of an error, this function will not modifiy the arguments given to it. +*/ +int FileUtills_dirList_PRIV_Get_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, const size_t entryOffset, + char ** entry, size_t * entryLength); + +/*! + int FileUtills_dirList_PRIV_Remove_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, const size_t entryOffset) + + Removes the given entry from the entry list. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is NULL. + Returns COMMON_ERROR_RANGE_ERROR if the given entry offset is beyond the end of the entry list. + Returns COMMON_ERROR_INTERNAL_ERROR if an unexpected error occurs. + Otherwise returns the appropriate error code. + + No alteration clause: + In the event of an error, this function will not modifiy the arguments given to it. +*/ +int FileUtills_dirList_PRIV_Remove_Entry(MSYS_FileUtills_dirList_PRIV_T * dirListPriv, const size_t entryOffset); + +/*! + void FileUtills_dirList_PRIV_Deallocate_Entry_Data_Copy(char ** data) + + Deallocates entry data returned by FileUtills_dirList_PRIV_Get_Entry() and + sets the given pointer to NULL. + + ALL entry data returned from FileUtills_dirList_PRIV_Get_Entry() should be + deallocated by this function. + + If the given pointer is invalid this function will silently fail. + This function has no return. +*/ +void FileUtills_dirList_PRIV_Deallocate_Entry_Data_Copy(char ** data); + +/*! + * int FileUtills_IsAbsolutePathReference(const char * path, const size_t pathSize) + * + * This function checks the given path to see if it's beginning is in + * absolute path reference form. + * + * Absolute path reference form is defined as: + * - On DOS / Windows systems: Having the drive letter the colon + * and directory seperator as the first three (3) characters in + * the given path. E.x. "C:\Windows" or "D:\Setup.exe" + * + * - On all other systems: Having the directory seperator as the + * first character in the given path. E.x. "/usr" or "/home" + * or "/foo" or "/bar" + * + * If the given path is in absolute path reference form as defined above, + * then the function will return FILEUTILLS_ERROR_PATH_IS_ABSOLUTE. + * + * If the given path is NOT in absolute path reference form as defined + * above, then this function will return FILEUTILLS_ERROR_PATH_IS_RELATIVE. + * + * If any error occurs then, this function will return the appropriate error code. + */ +int FileUtills_IsAbsolutePathReference(const char * path, const size_t pathSize); + +/*! + * int FileUtills_IsAbsolutePathReference_absRef(const char * path, const size_t pathSize, char ** absRef, size_t * absRefLength) + * + * This function checks the given path to see if it's beginning is in + * absolute path reference form. This function also copies the found + * absolute path reference to the given absRef argument if absRef and + * absRefLength are non-NULL. + * + * WARNING: This function expects absRefLength to point to a valid size_t + * variable when it is called. (This function will NOT allocate it.) + * absRef will be allocated by this function, but will NOT deallocate + * any pre-existing pointer. (As such you should deallocate it yourself + * or copy the pointer somewhere else if you need to save it for later use.) + * + * Absolute path reference form is defined as: + * - On DOS / Windows systems: Having the drive letter the colon + * and directory seperator as the first three (3) characters in + * the given path. E.x. "C:\Windows" or "D:\Setup.exe" + * + * - On all other systems: Having the directory seperator as the + * first character in the given path. E.x. "/usr" or "/home" + * or "/foo" or "/bar" + * + * If the given path is in absolute path reference form as defined above, + * then the function will return FILEUTILLS_ERROR_PATH_IS_ABSOLUTE, + * absRef will be set to the absolute path reference (Only the part that is + * checked is stored in absRef as per above. + * E.x. On DOS / Windows systems: ":\" on all + * other systems this will be set to "/".) + * absRefLength will be set to the length of the absRef string. + * + * If the given path is NOT in absolute path reference form as defined + * above, then this function will return FILEUTILLS_ERROR_PATH_IS_RELATIVE, + * the absRef string and absRefLength variables will NOT BE ALTERED. + * + * If any error occurs then, this function will return the appropriate error code, + * the absRef string and absRefLength variables will NOT BE ALTERED. + */ +int FileUtills_IsAbsolutePathReference_absRef(const char * path, const size_t pathSize, char ** absRef, size_t * absRefLength); + +/*! + * int FileUtills_ResolvePath_Helper(char ** retStr, size_t * retStrSize) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY OUTSIDE OF THE + * FILEUTILLS NAMESPACE. THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that resolves the given path and returns the result. + * + * WARNING: This function does NOT resolve any symbolic link(s) if a + * symbolic link is encountered. If you need to resolve symbolic links, + * use the public API version of this function: FileUtills_ResolvePath(). + * + * The path resolution method is described below: + * + * First the path is checked for a user profile directory reference. + * If one is found, then the profile directory path for the currently + * effective user is prepended to the result and parsing continues as + * if the remaining path segment(s) are relative to that profile + * directory path. (The check below for a relative or absolute path + * reference is skipped.) + * + * The given path is checked to determine whether it is an absolute path + * reference or a relative (to the current working directory) path reference + * by running it through FileUtills_IsAbsolutePathReference(). + * + * If the path is considered a relative reference, the current working + * directory is fetched from the host system and prepended to the result. + * Parsing then continues on the remaining path segments. + * + * If the path is considered an absolute path reference, then the absolute + * path reference is copied to the result, and parsing continues. + * + * The parsing loop starts which checks each given path segment to see if it + * contains references to the current path segment or the parent of the current + * path segment. + * + * If a reference to the current path segment is found, then the found reference + * is ignored, and parsing continues with the next path segment. + * + * If a reference to the parent of the current path segment is found, then + * the result is checked to see if removing the last path segment in the + * result would cause going past the boundry of a root directory. + * This check is defined below: + * If the target system uses the DOS / Windows style path scheme, + * then the check will indicate passing a root directory boundry + * if the removal of the path segment would remove the drive letter + * reference. (I.e. It would remove the + * ":" portion. + * (For Example: "C:\", "D:\", "Z:\", etc.)) + * + * Otherwise the check will indicate passing a root directory + * boundry if the removal of the path segment would remove the + * initial directory seperator. (I.e. The first directory + * seperator. A.K.A '/'.) + * + * If the check described above indicates that removal of the last path + * segment in the result would pass the root directory boundry this function + * will abort and return an error. + * + * If the check described above indicates that removal of the last path + * segment would NOT result in passing the root directory boundry, then + * the last segment will be removed from the result and parsing will + * continue. + * + * Once the last path segment in the given path is reached, the result + * will replace the contents of the retStr argument and COMMON_ERROR_SUCCESS + * will be returned. + * + * If an error is encountered at any point, the retStr argument will NOT be altered, + * and the appropriate error will be returned to the caller. + * (Depending on the set Common namespace logging level, a human readable + * error message will be logged to describe the error encountered more + * throughly.) + */ +int FileUtills_ResolvePath_Helper(char ** retStr, size_t * retStrSize); + +/*! + * int FileUtills_RemoveLastPathSegmentAtPosition(char ** path, size_t * pathSize, size_t * currentPathPos) + * + * This function removes a path segment from the given path, and updates the + * given currentPathPos variable to point to the location in the string + * where the removed path segment began. + * + * Long description: + * The given path argument is checked to see if is is big enough to have more + * than just the filesystem root directory reference. If this is not the case + * then this function will return the COMMON_ERROR_INVALID_ARGUMENT error + * code, and the given path and position arguments will NOT be modified. + * + * The path segment to remove is determined by the given currentPathPos + * variable. currentPathPos is checked to make sure it is within the capacity + * of the given string and if it is, iterates backwards in the string until a + * directory seperator (defined by DIR_SEP) is found. If the currentPathPos + * check fails, then this function will return the COMMON_ERROR_INVALID_ARGUMENT + * error code, and the given path and position arguments will NOT be modified. + * + * Once the path segment to remove is identfied, a check is performed to make sure + * the function is not removing the filesystem root directory reference. + * + * If the path segment to be removed is within the filesystem root directory, then the + * path segment is removed by replacing all of the bytes from the start of the path + * segment to be removed (while preserving the DIR_SEP as required for the filesystem + * root directory reference) to the end of the string with NULL character bytes. + * + * If removal of the path segment will not result in the removal of the filesystem root + * directory reference, then the path segment is removed by replacing all of the bytes + * from the start of the path segment to be removed (including the DIR_SEP) to the end + * of the string with NULL character bytes. + * + * If the path segment is removed, then currentPathPos will be updated to indicate where + * the next path segment's entry name (file or directory name) would start in the altered + * string. + * + * If a path segment to remove cannot be found, then the given path and position arguments will + * NOT be altered, and the function will return the COMMON_ERROR_SUCCESS error code. + * + * Error Codes: + * Returns COMMON_ERROR_SUCCESS if successful, the results will be stored in the given arguments. + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given path (pointer) is NULL, the given length of + * the path is less than or equal to zero, or if the currentPathPos is less than zero, or beyond + * the given path length. + * + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + * + * Returns COMMON_ERROR_INTERNAL_ERROR if the memory allocation call fails for some other reason. + * (Reason for failure will be sent to the debug log.) + * + * NOTE: This function will NOT deallocate a pre-existing pointer, but it WILL OVERWRITE IT. + * (So if you need to use or deallocate it later, make sure to copy the pointer before calling + * this function.) + * + * No alteration clause: + * In the event of an error, this function will not modifiy the arguments given to it. + */ +int FileUtills_RemoveLastPathSegmentAtPosition(char ** path, size_t * pathSize, size_t * currentPathPos); + +/*! + int FileUtills_RemoveLastPathSegment(char ** path, size_t * pathSize) + + This function is a shortcut wrapper around FileUtills_RemoveLastPathSegmentAtPosition() with the assumption that + the path segment to remove is at the end of the string. (End of the string is determined by the given pathSize.) + As such, this function is equivalent to calling FileUtills_RemoveLastPathSegmentAtPosition() with a currentPathPos + value of 0. + + See FileUtills_RemoveLastPathSegmentAtPosition()'s documentation for the usage and error code documentation. +*/ +int FileUtills_RemoveLastPathSegment(char ** path, size_t * pathSize); + +/*! + * int FileUtills_IsFileOrDirectory_Helper(const char * absPath, const size_t absPathSize) + * + * Helper function that calls FileUtills_IsFileOrDirectory_Syscall() for + * determining whether or not a given path is a file or + * directory on a filesystem. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills_ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills_ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + * + * Returns FILEUTILLS_ERROR_PATH_IS_A_FILE if the given absPath is a file + * in the filesystem. + * + * Returns FILEUTILLS_PATH_IS_A_DIRECTORY if the given absPath is a + * directory on the file system. + * + * Returns FILEUTILLS_ERROR_PATH_IS_A_SYMLINK if the given absPath is a + * symbolic link in the file system. + * + * Returns COMMON_ERROR_SUCCESS if the given absPath is a valid (but unrecognised) + * entry in the filesystem. + * + * Otherwise the appropriate error is returned to the caller. + */ +int FileUtills_IsFileOrDirectory_Helper(const char * absPath, const size_t absPathSize); + +/*! + * int FileUtills_IsFileOrDirectory_Syscall(const char * absPath, const size_t absPathSize) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY. + * THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * determining whether or not a given path is a file or + * directory on a filesystem. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * This function is permitted to perform any necessary allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int FileUtills_IsFileOrDirectory_Syscall(const char * absPath, const size_t absPathSize); + +/*! + * int FileUtills_GetUserProfileDirectoryPath_Syscall(char ** retStr, size_t * retStrSize) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY. + * THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * getting the user profile directory's location on + * the filesystem for the current user. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * This function is permitted to perform any necessary allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int FileUtills_GetUserProfileDirectoryPath_Syscall(char ** retStr, size_t * retStrSize); + +/*! + * int FileUtills_GetCurrentWorkingDirectoryPath_Syscall(char ** retStr, size_t * retStrSize) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY. + * THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * getting the current working directory's location on + * the filesystem. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * This function is permitted to perform any necessary allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int FileUtills_GetCurrentWorkingDirectoryPath_Syscall(char ** retStr, size_t * retStrSize); + +/*! + * int FileUtills_GetExecDirectory_Syscall(char ** retStr, size_t * retStrSize) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY. + * THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * getting the main executable's location on the filesystem. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * This function is permitted to perform any necessary allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int FileUtills_GetExecDirectory_Syscall(char ** retStr, size_t * retStrSize); + +/*! + * int FileUtills_ResolveSystemSymoblicLink_Syscall(char ** path, size_t * pathSize) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY. + * THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall(s) to resolve a given symbolic link. + * (Note: This only works if the given symbolic link is in the format that the host system + * expects / supports.) + * + * WARNING: This function verifies that the given path is a symbolic link using the host system's syscalls + * for doing so. Depending on your views / requirements of secure programming, this function may not be + * considered safe to call from certain contexts. + * + * All public FileUtills functions will wind up calling this function (directly or indirectly), as a side + * effect of FileUtills_ResolvePath() encountering a symbolic link during path resolution. (Unless the + * called public FileUtills function had it's disableSymLinkResolution boolean argument set to true + * explicitly.) + * + * Returns COMMON_ERROR_SUCCESS if successful. (path will have it's contents reset and the resolved + * path stored in it. pathSize will be reset and have the correct size in it for the resolved path. + * Note: No deallocation is performed on either pointer, so if you need to keep the existing pointer + * you should copy it elsewhere before calling this function.) + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given path argument is empty, or if the given path string is + * not a symbolic link as defined by the host system. + * + * Returns all other errors where appropriate. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * This function is permitted to perform any necessary allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + * + * No alteration clause: + * In the event of an error, this function will not modifiy the arguments given to it. + */ +int FileUtills_ResolveSystemSymoblicLink_Syscall(char ** path, size_t * pathSize); + +/*! + * size_t FileUtills_Get_Max_Symlink_Depth_Syscall() + * + * Returns the host's maximum supported symbolic link depth. + */ +size_t FileUtills_Get_Max_Symlink_Depth_Syscall(); + +#ifdef __cplusplus +} /* End of extern C. */ +#endif /* __cplusplus */ + +/* Check for C++ compiler and include the C++ specific header if needed. */ +#ifdef __cplusplus +/* Include the C++ specific header. */ +#include "FileUtills_Private_API_CPP.h" +#endif /* __cplusplus */ + +#endif /* FILEUTILLS_PRIVATE_API_H */ + +/* End of FileUtills_Private_API.h */ diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_CPP.h b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_CPP.h new file mode 100644 index 0000000..dafff34 --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_CPP.h @@ -0,0 +1,542 @@ +/*! + Multiverse Engine Project 17/12/2015 FileUtills FileUtills_Private_API_CPP.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef FILEUTILLS_PRIVATE_API_CPP_H +#define FILEUTILLS_PRIVATE_API_CPP_H + +/* Check for defined FILEUTILLS_PRIVATE_API_H. */ +#ifndef FILEUTILLS_PRIVATE_API_H +#error "This header should not be included directly. It will be included automaticly by FileUtills_Private_API.h if the compiler in use is a C++ compiler." +#endif /* FILEUTILLS_PRIVATE_API_H */ + +/* Check for C++ compiler. */ +#ifdef __cplusplus + +/* C++ specific external includes. */ +#include +#include + +/* Namespace definition. */ +namespace FileUtills{ + +/*! + * int FileUtills::IsAbsolutePathReference(const char * path, const size_t pathSize) + * + * This function is a wrapper around FileUtills_IsAbsolutePathReference(). + * See that function above for the documentation. + */ +int IsAbsolutePathReference(const char * path, const size_t pathSize); + +/*! + * int FileUtills::IsAbsolutePathReference(const char * path, const size_t pathSize, char ** absRef, size_t * absRefLength) + * + * This function is a wrapper around FileUtills_IsAbsolutePathReference_absRef(). + * See that function above for the documentation. + */ +int IsAbsolutePathReference(const char * path, const size_t pathSize, char ** absRef, size_t * absRefLength); + +/*! + * int FileUtills::GetPathSegment(const std::string & path, const size_t & currentPathPos, std::string & pathSegment, const bool & blankPathSegment) + * + * This function looks for the last path segment from the given path + * and copies it to the given pathSegment string argument. + * + * WARNING: If the given blankPathSegment boolean is true (default), + * pathSegment is cleared (by calling std::string.clear().) even if the + * function fails with an error. (So make sure pathSegment does not have + * something in it you need to keep when you call this function.) + * + * (This function is mostly a slightly altered version of FileUtills::RemoveLastPathSegment() + * that copies the last path segment rather than remove it from the given path string.) + * + * The path segment to copy is determined by the given currentPathPos + * variable. currentPathPos is checked to make sure it is within the capacity + * of the given string and if it is, iterates backwards in the string until a + * directory seperator (defined by DIR_SEP) is found. If the currentPathPos + * check fails, then this function will return the COMMON_ERROR_INVALID_ARGUMENT + * error code. + * + * The path segment is then copied from the directory seperator (but NOT including the directory + * seperator) to pathSegment until it reaches the given currentPathPos offset in the path string. + * (Or the end of the path string is reached which ever comes first.) + * + * If a path segment to copy cannot be found, then pathSegment will be cleared (if blankPathSegment is true (default)) + * or pathSegment will have it's original contents (if blankPathSegment is false), and + * the function will return the COMMON_ERROR_SUCCESS error code. + * + * If a path segment to copy is found, then pathSegment will have a copy of the + * path segment appended to it (if blankPathSegment is true (default) then only the copied path segment will + * be in pathSegment, otherwise pathSegment will have it's original contents appended with the copied path + * segment.), and the function will return the COMMON_ERROR_SUCCESS error code. + * + * In any case, the returned error code will also be written to Common::commonLastErrorCode. + */ +int GetPathSegment(const std::string & path, const size_t & currentPathPos, std::string & pathSegment, const bool & blankPathSegment = true); + +/*! + * int FileUtills::GetLastPathSegment(const std::string & path, std::string & pathSegment, const bool & blankPathSegment) + * + * This function acts as a wrapper to FileUtills::GetPathSegment(const std::string &, const size_t &, std::string &) + * with the assumption that the path segment you want to copy is at the end of the + * string. (end of the given string is determined by the string's size.) + * + * By default the given path segment argument will be cleared. To not clear the argument, set blankPathSegment to false. + */ +int GetLastPathSegment(const std::string & path, std::string & pathSegment, const bool & blankPathSegment = true); + +/*! + * int FileUtills::RemoveLastPathSegment(std::string & path, size_t * currentPathPos) + * + * This function removes a path segment from the given path, and updates the + * given currentPathPos variable to point to the location in the string + * where the removed path segment began. + * + * This function is a wrapper to FileUtills_RemoveLastPathSegmentAtPosition(), and performs + * conversions of the result to an std::string. As such this function will return all of + * FileUtills_RemoveLastPathSegmentAtPosition()'s error codes. + * + * This function will return the COMMON_ERROR_SUCCESS error code if it is successful, + * the results will be stored in the given path argument. + * + * See FileUtills_RemoveLastPathSegmentAtPosition() for the remaining error code descriptions. + * + * NOTE: This function will NOT deallocate a pre-existing string, but it WILL OVERWRITE IT. + * (So if you need to use or deallocate it later, make sure to copy the string before calling + * this function.) + * + * No alteration clause: + * In case of error, this function will not alter any given argument. + */ +int RemoveLastPathSegment(std::string & path, size_t * currentPathPos); + +/*! + * int FileUtills::RemoveLastPathSegment(std::string & path) + * + * This function acts as a wrapper to FileUtills::RemoveLastPathSegment(std::string &, size_t *) + * with the assumption that the path segment you want to remove is at the end of the + * string. (End of the given string is determined by the string's size.) + * As such, this function is equivalent to calling FileUtills::RemoveLastPathSegment(std::string &, size_t *) with a currentPathPos + * value of 0. + * + * See FileUtills::RemoveLastPathSegment(std::string &, size_t *)'s documentation for the usage and error code documentation. + */ +int RemoveLastPathSegment(std::string & path); + +/*! + * FileUtills::dirlist * FileUtills::getDirectory_Helper(const std::string & absPath, const bool & cleanList) + * + * Helper function that calls FileUtills::getDirectory_Syscall() for + * generating a list of the given path's directory contents. If cleanList + * is set to true, then the list will have the references to the + * given path's current directory and parent directory removed from the + * generated list, as well as having the list sorted by + * DataProcess::DecrementingSort(). + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + */ +FileUtills::dirlist * getDirectory_Helper(const std::string & absPath, const bool & cleanList); + +/*! + * FileUtills::dirlist * FileUtills::getDirectory_Syscall(const std::string & absPath, const bool & cleanList) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY OUTSIDE OF THE + * FILEUTILLS NAMESPACE. THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * getting a listing of the given path's directory contents + * from the filesystem. If cleanList is set to true, then the + * list will have the references to the given path's current + * directory and parent directory removed from the generated + * list. (The sorting is done by the Helper function.) + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * The result of this function is stored in Common::commonLastErrorCode, + * and a pointer to a FileUtills::dirlist is returned. (In case of error + * the returned pointer will be NULL.) + * + * This function is permitted to perform any nessacarry allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +FileUtills::dirlist * getDirectory_Syscall(const std::string & absPath, const bool & cleanList); + +/*! + * int FileUtills::GetGigaFreespace_Helper(const std::string & absPath, size_t & result) + * + * Helper function that calls FileUtills::GetByteFreespace_Syscall() for + * getting the number of remaining free bytes on the given filesystem, + * then performs the conversion of the remaining bytes to the SI unit + * gigabyte (GB) representation. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + */ +int GetGigaFreespace_Helper(const std::string & absPath, size_t & result); + +/*! + * int FileUtills::GetFreespace_Helper(const std::string & absPath, size_t & result) + * + * Helper function that calls FileUtills::GetByteFreespace_Syscall() for + * getting the number of remaining free bytes on the given filesystem, + * then performs the conversion of the remaining bytes to the SI unit + * megabyte (MB) representation. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + */ +int GetFreespace_Helper(const std::string & absPath, size_t & result); + +/*! + * int FileUtills::GetKiloFreespace_Helper(const std::string & absPath, size_t & result) + * + * Helper function that calls FileUtills::GetByteFreespace_Syscall() for + * getting the number of remaining free bytes on the given filesystem, + * then performs the conversion of the remaining bytes to the SI unit + * kilobyte (kB) representation. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + */ +int GetKiloFreespace_Helper(const std::string & absPath, size_t & result); + +/*! + * int FileUtills::GetByteFreespace_Helper(const std::string & absPath, size_t & result) + * + * Helper function that calls FileUtills::GetByteFreespace_Syscall() for + * getting the number of remaining free bytes on the given filesystem. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + */ +int GetByteFreespace_Helper(const std::string & absPath, size_t & result); + +/*! + * int FileUtills::GetByteFreespace_Syscall(const std::string & absPath, size_t & result) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY OUTSIDE OF THE + * FILEUTILLS NAMESPACE. THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * getting the number of remaining free bytes on the given filesystem. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * The result of this function is stored in Common::commonLastErrorCode + * in addition to being returned. + * + * This function is permitted to perform any nessacarry allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int GetByteFreespace_Syscall(const std::string & absPath, size_t & result); + +/*! + * int FileUtills::CreateDirectory_Helper(const std::string & absPath, const bool & createRecursive) + * + * Helper function that calls FileUtills::CreateDirectory_Syscall() for + * creating a directory on the filesystem. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + */ +int CreateDirectory_Helper(const std::string & absPath, const bool & createRecursive); + +/*! + * int FileUtills::CreateDirectory_Syscall(const char * absPath) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY OUTSIDE OF THE + * FILEUTILLS NAMESPACE. THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * creating a directory on the filesystem. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * The result of this function is stored in Common::commonLastErrorCode + * in addition to being returned. + * + * This function is permitted to perform any nessacarry allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int CreateDirectory_Syscall(const char * absPath); + +/*! + * int FileUtills::CheckPermissions_Helper(const std::string & absPath, const bool & read, const bool & write, const bool & exec) + * + * Helper function that calls FileUtills::CheckPermissions_Syscall() for + * checking the permissions of a given file or directory on the filesystem. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + * + * WARNING: This function expects that the path given to it + * is a filesystem entry within the directory to check. (I.e. + * The given path is a file or directory whose parent directory + * needs to be checked.) As such it will remove the last path + * component from the given path before performing the checks. + */ +int CheckPermissions_Helper(const std::string & absPath, const bool & read, const bool & write, const bool & exec); + +/*! + * int FileUtills::CheckPermissions_Syscall(const std::string & absPath, const bool & read, const bool & write, const bool & exec) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY OUTSIDE OF THE + * FILEUTILLS NAMESPACE. THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * checking the permissions of a given file or directory on the filesystem. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * The result of this function is stored in Common::commonLastErrorCode + * in addition to being returned. + * + * This function is permitted to perform any nessacarry allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int CheckPermissions_Syscall(const std::string & absPath, const bool & read, const bool & write, const bool & exec); + +/*! + * int FileUtills::CheckParent_Helper(const std::string & path, const bool & read, const bool & write, const bool & exec) + * + * Helper function that calls FileUtills::CheckPermissions_Helper() for + * determining whether or not a given path's parent directory + * exists and is accessable with the given permissions. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + * + * WARNING: This function expects that the path given to it + * is a filesystem entry within the directory to check. (I.e. + * The given path is a file or directory whose parent directory + * needs to be checked.) As such it will remove the last path + * component from the given path before performing the checks. + */ +int CheckParent_Helper(const std::string & path, const bool & read, const bool & write, const bool & exec); + +/*! + * int FileUtills::DoesExist_Helper(const std::string & absPath) + * + * Helper function that calls FileUtills::DoesExist_Syscall() for + * determining whether or not a given path exists on the given filesystem. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + */ +int DoesExist_Helper(const std::string & absPath); + +/*! + * int FileUtills::DoesExist_Syscall(const std::string & absPath) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY OUTSIDE OF THE + * FILEUTILLS NAMESPACE. THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * determining whether or not a given path exists on the given filesystem. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * The result of this function is stored in Common::commonLastErrorCode + * in addition to being returned. + * + * This function is permitted to perform any nessacarry allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int DoesExist_Syscall(const std::string & absPath); + +/*! + * int FileUtills::DeletePath_Helper(const std::string & absPath, const bool & recursive) + * + * Helper function that calls FileUtills::DeletePath_Syscall() for + * removing (unlinking / deleting) a given file or directory on a + * filesystem. + * + * This is used internally by other FileUtills functions to + * prevent calling FileUtills::ResolvePath() multiple times. + * + * WARNING: This function expects that the given path has + * already been resolved by FileUtills::ResolvePath(). + * If the path needs to be resolved (if you are unsure then + * it does) use the public API version of this function, + * which will resolve the path. + */ +int DeletePath_Helper(const std::string & absPath, const bool & recursive); + +/*! + * int FileUtills::DeletePath_Syscall(const std::string & absPath) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY OUTSIDE OF THE + * FILEUTILLS NAMESPACE. THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * removing (unlinking / deleting) a given file or + * directory on a filesystem. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * The result of this function is stored in Common::commonLastErrorCode + * in addition to being returned. + * + * This function is permitted to perform any nessacarry allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int DeletePath_Syscall(const std::string & absPath); + +/*! + * int FileUtills::RenamePath_Syscall(const std::string & absPathSrc, const std::string & absPathDest, const bool & dereferenceSymLinks) + * + * WARNING: NEVER CALL THIS FUNCTION DIRECTLY OUTSIDE OF THE + * FILEUTILLS NAMESPACE. THIS FUNCTION EXPECTS ANY AND ALL OTHER + * SAFETY CHECKS HAVE PASSED BEFORE IT IS CALLED. + * + * Helper function that calls the host's syscall for + * renaming a given file or directory on the same filesystem. + * + * This function is NOT permitted to issue a call to CopyFile_Syscall() + * (or actually use the host's copy syscall) under any circumstance. + * (Even if the filesystems are the same.) + * + * In addition, this function is NOT permitted to move / copy files across + * filesystem boundries under any circumstance. + * + * This function MUST resolve any symbolic links (symlinks) passed to it before + * performing it's task, if the host's syscall for that task does not resolve them + * automaticly. The ONLY exception is if the given dereferenceSymLinks argument is + * set to false. The default for dereferenceSymLinks is true. + * + * This function is required to translate any error that + * may be returned by the syscall into a Common namespace + * error code. + * + * The result of this function is stored in Common::commonLastErrorCode + * in addition to being returned. + * + * This function is permitted to perform any nessacarry allocations or + * modifications needed by the host's syscall to perform the task, however + * these modifications and or allocations must be undone prior to the + * function's return. In addition this function must catch any thrown + * exception created by itself or the host's syscall. (In that instance + * COMMON_ERROR_EXCEPTION_THROWN must be returned, regardless of result.) + */ +int RenamePath_Syscall(const std::string & absPathSrc, const std::string & absPathDest, const bool & dereferenceSymLinks = true); +}; + +#endif /* __cplusplus */ + +#endif /* FILEUTILLS_PRIVATE_API_CPP_H */ + +/* End of FileUtills_Private_API_CPP.h. */ diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Posix_Syscall.cpp b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Posix_Syscall.cpp new file mode 100644 index 0000000..a302e94 --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Posix_Syscall.cpp @@ -0,0 +1,1373 @@ +/*! + Multiverse Engine Project 04/8/2014 FileUtills FileUtills_Private_API_Posix_Syscall.cpp + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +#include "FileUtills.h" +#include "FileUtills_Private_API.h" +#include "FileUtills_Private_API_Posix_Syscall.h" + +int FileUtills::ResolveSystemSymoblicLink_Syscall(char ** path, size_t * pathSize) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + int errcpy = 0; // Used to copy errno for translation if needed. + char * readLinkBuf = NULL; // Pointer to a buffer for readlink(). + char * checkBuffer = NULL; // A second buffer for readlink() that is used to verifiy the contents of readLinkBuf during the "devil's loop". + ssize_t firstBufferLength = 0; // Signed return value of the number of bytes written to the buffer by readlink(). (Just so they could return that -1 error code....) + ssize_t secondBufferLength = 0; // Signed return value of the number of bytes written to the checkBuffer by readlink(). (Just so they could return that -1 error code....) + size_t realBufferLength = 0; // Unsigned value of how big the buffer is. (Used for accessing and allocating the buffer.) + const size_t MAX_GET_EXE_PATH_REALLOC = 4; // Maximum number of times to run the memory reallocation loop. + const size_t BASE_SIZE = 1000; // Inital number of bytes to allocate. (This number is multiplied by the loop iteration value (x) after each loop iteration.) + struct stat st; // POSIX Stat structure for retriving the status of the given path. (Whether or not it's a symlink and it's length.) + + // Make sure we got a valid path. + if ((path != NULL) && ((*path) != NULL) && (pathSize != NULL) && ((*pathSize) > 0)) + { + // Call lstat. + if (lstat((*path), &st) == 0) + { + // Check and see if the given path is a symbolic link. + if (S_ISLNK((st.st_mode))) + { + // Check the size of the given link. (Just in case we get a non-conforming system that actually fills in st_size for the proc fs. (See below rant.)) + if (st.st_size > 0) + { + // OK, allocate a buffer for the given length. (Plus one so we can add a NULL character to it as readlink() does not.) + readLinkBuf = malloc((st.st_size + 1)); + if (readLinkBuf != NULL) + { + // Call readlink. + firstBufferLength = readlink((*path), readLinkBuf, st.st_size); + + // Check and see if the result from readlink is not -1 and if it equals st.st_size. + if (firstBufferLength != -1) + { + if (firstBufferLength == st.st_size) + { + // Insert a NULL character after the end of the string. + readLinkBuf[st.st_size] = '\0'; + + // Copy the result to the path buffer. + (*path) = readLinkBuf; + + // Set success. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // OK, we got screwed with. The result should be the same size as the original lstat() call returned. + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_VERBOSE("FileUtills_ResolveSystemSymoblicLink(): Size of the read symbolic link is not the same as the size returned from the OS."); + } + } + else + { + // Copy the error code. + errcpy = errno; + + // Translate the error code. + ret = Common_Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // Log the error. + COMMON_LOG_DEBUG("FileUtills_ResolveSystemSymoblicLink(): An error was returned while reading the symbolic link: "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(ret)); + } + } + else + { + // Could not allocate buffer. + ret = COMMON_ERROR_MEMORY_ERROR; + + // Log the error. + COMMON_LOG_INFO("FileUtills_ResolveSystemSymoblicLink(): "); + COMMON_LOG_INFO(Common_Get_Error_Message(ret)); + } + } + else + { + /* + * OK, run the hard limited implemetation. + * (Copied from the original FileUtills::GetExecDirectory_Syscall() implemetation for POSIX.) + * + * We do this to avoid a runaway reallocation loop which could be considered a resource starvation + * issue. (See below for the original rant, just ignore the /proc file system issues for symlinks + * in general. (Only issues 1 and 3 apply to ALL symlinks.)) + * + * (Begin original rant.) + * + * Short version: + * This is a cluster. + * + * Long version: + * The only reliable (guarrenteed to work) method for getting the executable + * path in linux, is by using readlink() on /proc/self/exe. + * + * However, this has several issues. + * + * First issue: readlink() expects a preallocated buffer to store the result. + * If the preallocated buffer is too small for the full path, then readlink() + * will silently truncate the remaining data and not tell us how much was + * left over. + * + * Second issue: The proc filesystem misbehaves and does not fill in the + * st_size field of the stat structure. So as a result we can't easilly + * tell how much memory to allocate for our buffer. + * + * Third issue: Because some filesystems allow paths of unlimited size, + * (i.e. the only restriction is having the space to store the path), + * we can't use PATH_MAX (as it's possibly wrong), and we can't set a + * limit on our memory allocation. + * + * Because of these issues, the glibc MANUAL actually gives an indefinite + * loop of memory allocation->readlink()->if(!got entire link)->Deallocate memory->Allocate memory. + * for reading a symlink from the filesystem. + * + * This loop is reimplimented here, but with three differences. + * + * - The loop will end once a certian amount of reallocations (defined by MAX_GET_EXE_PATH_REALLOC) + * is reached. (This is to prevent some of the above issues.) + * + * - The loop will also check to make sure that the returned string has not been altered. + * (via memcmp). (Attempt to prevent a compromised system from mallissously changing the + * symlink's destionation as a result of our multiple accesses to the link itself.) + * + * - The loop will reallocate the buffer after the link is fully read so that ONLY the + * needed memory to store it is allocated for it. (Prevent unnessacarry memory usage.) + * + * (End of original rant.) + */ + // Begin the devil's loop. + for (size_t x = 0; ((x < MAX_GET_EXE_PATH_REALLOC) && + ((readLinkBuf == NULL) && (checkBuffer == NULL)) && + (ret == COMMON_ERROR_UNKNOWN_ERROR)); x++) + { + // Recalculate bufferLength. + realBufferLength = (BASE_SIZE * (x + 1)); + + // Allocate the memory. + readLinkBuf = (char*)malloc(realBufferLength); + checkBuffer = (char*)malloc(realBufferLength); + + // Make sure it was allocated. + if ((readLinkBuf != NULL) && (checkBuffer != NULL)) + { + // Blank out the allocated memory. + memset(readLinkBuf, '\0', realBufferLength); + memset(checkBuffer, '\0', realBufferLength); + + // Call readlink() for the first buffer. + firstBufferLength = readlink((*path), readLinkBuf, realBufferLength); + + // Check bufferLength. + if (firstBufferLength >= 0) + { + // Check to see if we got the entire path. + if (firstBufferLength < realBufferLength) + { + // Call readlink() for the second buffer. + secondBufferLength = readlink((*path), checkBuffer, realBufferLength); + + // Check secondBufferLength. + if (secondBufferLength >= 0) + { + // Check to see if we got the entire path. + if (secondBufferLength == firstBufferLength) + { + // Call memcmp(). + if (memcmp(readLinkBuf, checkBuffer, realBufferLength) == 0) + { + // Paths match, deallocate the second buffer. + if (checkBuffer != NULL) + { + memset(checkBuffer, '\0', realBufferLength); + free(checkBuffer); + checkBuffer = NULL; + } + + // Reallocate the buffer. (Free unneeded memory.) + ret = FileUtills_Reallocate_CString_Buffer(readLinkBuf, realBufferLength, firstBufferLength); + if (ret == COMMON_ERROR_SUCCESS) + { + // Copy the first buffer pointer to the path buffer pointer. + (*path) = readLinkBuf; + } + else + { + // Could not free the unneeded memory. + COMMON_LOG_DEBUG("FileUtills_ResolveSystemSymoblicLink(): Call to FileUtills_Reallocate_CString_Buffer() failed: "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(ret)); + } + } + else + { + // Something is screwing with us...abort. + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG("FileUtills_ResolveSystemSymoblicLink(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + // Something is screwing with us...abort. + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG("FileUtills_ResolveSystemSymoblicLink(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + // Error. + errcpy = errno; + ret = Common_Translate_Posix_Errno_To_Common_Error_Code(errcpy); + COMMON_LOG_DEBUG("FileUtills_ResolveSystemSymoblicLink(): readlink() system call returned: "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(ret)); + } + } + else + { + // We did not get the entire link, so we need to deallocate it for the next loop. + if (readLinkBuf != NULL) + { + memset(readLinkBuf, '\0', realBufferLength); + free(readLinkBuf); + readLinkBuf = NULL; + } + if (checkBuffer != NULL) + { + memset(checkBuffer, '\0', realBufferLength); + free(checkBuffer); + checkBuffer = NULL; + } + } + } + else + { + // Error. + errcpy = errno; + ret = Common_Translate_Posix_Errno_To_Common_Error_Code(errcpy); + COMMON_LOG_DEBUG("FileUtills_ResolveSystemSymoblicLink(): readlink() system call returned: "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(ret)); + } + } + } + + /* + * If we reach here, and ret == COMMON_ERROR_UNKNOWN_ERROR, + * then we have failed. (Most likely we ran out of reallocation attempts...) + */ + if (ret == COMMON_ERROR_UNKNOWN_ERROR) + { + /* + * Set COMMON_INTERNAL_ERROR. + * + * (The system most likely can fetch the link, + * but we need to limit the reallocation attempts + * to prevent issues. So it's not appropriate to use + * FILEUTILLS_PATH_LENGTH_INVALID.) + */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_INFO("FileUtills_ResolveSystemSymoblicLink(): "); + COMMON_LOG_INFO(Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_INFO(" Unable to resolve the symbolic link due to engine limitation. (Length of the resolved path is too long.)"); + } + + // If unsuccessful, make sure both buffers are deallocated. + if (ret != COMMON_ERROR_SUCCESS) + { + if (readLinkBuf != NULL) + { + memset(readLinkBuf, '\0', realBufferLength); + free(readLinkBuf); + readLinkBuf = NULL; + } + if (checkBuffer != NULL) + { + memset(checkBuffer, '\0', realBufferLength); + free(checkBuffer); + checkBuffer = NULL; + } + } + } + } + else + { + // This is not a symbolic link. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_INFO("FileUtills_ResolveSystemSymoblicLink(): The given path ( "); + COMMON_LOG_INFO(path); + COMMON_LOG_INFO(" ) is not a symbolic link created by the system."); + } + } + else + { + // Copy errno. + errcpy = errno; + + // Translate the error. + ret = Common_Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // Log the error. + COMMON_LOG_DEBUG("FileUtills_ResolveSystemSymoblicLink(): An error was returned while accessing the given path: "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(ret)); + } + } + else + { + // Invalid path. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills_ResolveSystemSymoblicLink(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(" Given path argument is invalid."); + } + + // Return the result. + return ret; +} + +int FileUtills::GetUserProfileDirectoryPath_Syscall(std::string & path) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. +#ifdef __linux__ + // Init vars. + int errorg = 0; // Used to hold the original state of errno so it can be restored after we finish. + int errcpy = 0; // Used to store errno if needed. + char * pHD = NULL; // Used to fetch the path from the system. + + // Backup and clear errno. + errorg = errno; + errno = 0; + + // Blank out the path value. + path.clear(); + + // Get the path to the user's profile directory. + try { + pHD = getenv("HOME"); + if ((errno == 0) && (pHD != NULL)) + { + // Copy the variable to the path. + path = pHD; + + // Set success code. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Could not get user profile directory path variable. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + COMMON_LOG_VERBOSE("FileUtills::GetUserProfileDirectoryPath(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(" Could not get user profile directory path from enviorment.\n"); + + // Reset path. + path.clear(); + } + } + catch (exception &ex) + { + // Exception thown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + COMMON_LOG_VERBOSE("FileUtills::GetUserProfileDirectoryPath(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(" "); + COMMON_LOG_VERBOSE(ex.what()); + COMMON_LOG_VERBOSE("\n"); + + // Reset path. + path.clear(); + } + + // Check for an allocated buffer. + if (pHD != NULL) + { + // Release the buffer. + free(pHD); + pHD = NULL; + } + + // Restore errno. + errno = errorg; +#endif // __linux__ + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::GetCurrentWorkingDirectoryPath_Syscall(std::string & path) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. +#ifdef __linux__ + int errorg = 0; // Used to hold the original state of errno so it can be restored after we finish. + int errcpy = 0; // Used to store errno if needed. + char * pCWD = NULL; // Used to fetch path from system. + + // Backup and clear errno. + errorg = errno; + errno = 0; + + // Blank out the path value. + path.clear(); + + // Get current directory. + try { + /* + * NOTE: The call below is linux (libc5, libc6, and glibc) specific. + * (The standard requires a preallocated buffer.) + * + * I dislike the idea of a preallocated buffer as we cannot reliably + * determine the needed length. (Plus it could change between calls.) + * + * This really should be checked for and an error thrown if the OS / + * libc in use does not support this usage. + */ + pCWD = getcwd(NULL, 0); + if ((errno == 0) && (pCWD != NULL)) + { + // Copy the path. + path = pCWD; + + // Set ret. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Could not get current working directory path variable. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + COMMON_LOG_VERBOSE("FileUtills::GetCurrentWorkingDirectoryPath(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(" Could not get current working directory path from enviorment.\n"); + + // Reset path. + path.clear(); + } + } + catch(exception &ex) + { + // Exception thown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + COMMON_LOG_VERBOSE("FileUtills::GetCurrentWorkingDirectoryPath(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(" "); + COMMON_LOG_VERBOSE(ex.what()); + COMMON_LOG_VERBOSE("\n"); + + // Reset path. + path.clear(); + } + + // Check for an allocated buffer. + if (pCWD != NULL) + { + // Release the buffer. + free(pCWD); + pCWD = NULL; + } + + // Restore errno. + errno = errorg; +#endif // __linux__ + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::GetExecDirectory_Syscall(char ** retStr, size_t * retStrSize) +{ + // Init vars. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result code returned from this function. + char * result = NULL; // The string returned from this function. + size_t resultSize = 0; // The size of the result string. + + // Check for valid arguments. + if ((retStr != NULL) && (retStrSize != NULL)) + { +#ifdef __linux__ + // Copy the pointer. + result = PROC_PATH; + resultSize = PROC_PATH_SIZE; + + // Call FileUtills::ResolveSystemSymoblicLink_Syscall(). (Make sure it's the real path not a symlink. (PROC_PATH is a symlink, and a weird one at that.)) + ret = FileUtills::ResolveSystemSymoblicLink_Syscall(&result, &resultSize); + if (ret == COMMON_ERROR_SUCCESS) + { + // The resulting path is actually the exe itself, so we need to call RemoveLastPathSegment(). + ret = FileUtills::RemoveLastPathSegment(&result, &resultSize); + if (ret == COMMON_ERROR_SUCCESS) + { + // Copy the result string to retStr. + (*retStr) = result; + (*retStrSize) = resultSize; + } + else + { + // Could not remove exe from path. + COMMON_LOG_DEBUG("FileUtills_GetExecDirectory_Syscall(): Could not remove exe from it's path, call to FileUtills_RemoveLastPathSegment() failed: "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(ret)); + } + } + else + { + // This is an internal engine error. + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Report it. + COMMON_LOG_WARNING("FileUtills_GetExecDirectory(): "); + COMMON_LOG_WARNING(Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_WARNING(" Getting the executable path failed. (Call to an internal engine function failed.) Please report this bug."); + } +#endif + } + else + { + // Invalid arguments. + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG("FileUtills_GetExecDirectory(): "); + COMMON_LOG_DEBUG(Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + // Return result. + return ret; +} + +FileUtills::dirlist * FileUtills::getDirectory_Syscall(const std::string & absPath, const bool & cleanList) +{ + // Init vars. + int errcpy = 0; // Used to fetch the error code. + FileUtills::dirlist * ret = NULL; // The FileUtills directory structure. + struct dirent * dir = NULL; // The OS (POSIX) directory structure. + DIR * dp = NULL; // Directory stream to hold directory name..... (Why can't it just take a c string?) + std::string tempname = ""; // Used to store the filename / subdirectory name for the addToArray function. + + // Dumb check. + if (absPath.size() > 0) + { +#ifdef POSIX_COMMON_H + // Allocate the dirlist. + try{ + ret = new FileUtills::dirlist; + if (ret != NULL) + { + // Set the path. + ret->path = absPath; + + // Dump the path as a c string into the Directory stream object......(Overly complicated POS......) + + // Check and make sure we can open the directory first. + if ((dp = (opendir(absPath.c_str()))) == NULL) + { + // Copy the error code, and translate it. + errcpy = errno; + errcpy = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // An error occured. + if (ret != NULL) + { + delete ret; + ret = NULL; + } + + // Log the error. + COMMON_LOG_INFO("FileUtills::getDirectory(): Unable to get directory contents for ( "); + COMMON_LOG_INFO(absPath.c_str()); + COMMON_LOG_INFO(" ) The system returned "); + COMMON_LOG_INFO(Common::Get_Error_Message(errcpy)); + COMMON_LOG_INFO("\n"); + + // Copy the translated error code to Common::commonLastErrorCode. + Common::commonLastErrorCode = errcpy; + } + + // Start filesystem fetch loop. + while ((dir = readdir(dp))) // Call Host OS function. + { + // Check and see if the cleanList flag is set. + if (cleanList) + { + // Check to see if d_name is a POSIX directory shortcut. + if ((strcmp(dir->d_name, ".") == 0) || (strcmp(dir->d_name, "..") == 0)) + { + // d_name is a directory shortcut, do not add it to the list. + continue; + } + } + + // Cast d_name to a string. + tempname = dir->d_name; + + // Add the data to the array. + ret->list.push_back(tempname); + } + + // Close the directory stream and reset it. + closedir(dp); + + // Set the number of entries. + ret->numOfEntries = ret->list.size(); + + // Set Common::commonLastErrorCode. + Common::commonLastErrorCode = COMMON_ERROR_SUCCESS; + } + else + { + // Could not allocate memory. + Common::commonLastErrorCode = COMMON_ERROR_MEMORY_ERROR; + } + } + catch(exception &ex) + { + // Free memory if needed. + if (ret != NULL) + { + delete ret; + ret = NULL; + } + + // Could not allocate struct. + Common::commonLastErrorCode = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::getDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(COMMON_ERROR_EXCEPTION_THROWN)); + COMMON_LOG_DEBUG(" "); + COMMON_LOG_DEBUG(ex.what()); + COMMON_LOG_DEBUG("\n"); + } +#endif + } + else + { + // Invalid path. + Common::commonLastErrorCode = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::getDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Return the result. + return ret; +} + +int FileUtills::GetByteFreespace_Syscall(const std::string & absPath, size_t & result) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + +#ifdef POSIX_COMMON_H + struct statvfs * buffer = NULL; // Buffer used to get filesystem info from the OS. + + // Reset result. + result = 0; + + // Check for invalid absPath. + if (absPath.size() > 0) + { + // Begin try block. + try { + // Create buffer. + buffer = new struct statvfs; + if (buffer != NULL) + { + // Call host's function, and check for error. + if (statvfs(absPath.c_str(), buffer) == 0) + { + // Determine number of free bytes. (Number of avaiable blocks * block size) + result = (buffer->f_bavail * buffer->f_bsize); + + // Set success. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Figure out what the error we got back was. + ret = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(ret); + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::GetByteFreespace(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG("\n"); + } + + // Delete the buffer + delete buffer; + buffer = NULL; + } + else + { + // Could not allocate memory. + ret = COMMON_ERROR_MEMORY_ERROR; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::GetByteFreespace(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG("\n"); + } + } + catch (exception &ex) + { + // Exception thrown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + + // Check for allocated buffer. + if (buffer != NULL) + { + delete buffer; + buffer = NULL; + } + + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::GetByteFreespace(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(" "); + COMMON_LOG_VERBOSE(ex.what()); + COMMON_LOG_VERBOSE("\n"); + } + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::GetByteFreespace(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } +#endif + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::CreateDirectory_Syscall(const char * absPath) +{ + // Init errcpy. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + int errcpy = 0; // Used to copy errno for translation if needed. + + // Check for NULL pointer. + if (absPath != NULL) + { + // Attempt to create the given directory. + if (mkdir(absPath, S_IRWXU) != 0) + { + // Copy errno, and convert it. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + } + else + { + // Directory created successfully. + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + // Invalid argument. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CreateDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::CheckPermissions_Syscall(const std::string & absPath, const bool & read, const bool & write, const bool & exec) +{ + // Init vars. + int errcpy = 0; // Used to hold the errno value to translate it if needed. + int mode = 0; // Used to construct the mode bitmask. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + + // Make sure that absPath is valid. + if (absPath.size() > 0) + { + // Double check and make sure we have something to do. + if ((read != false) || (write != false) || (exec != false)) + { + // Set the mode. + mode = F_OK; + + // Check for read permission check. + if (read == true) + { + mode = mode | R_OK; + } + + // Check for write permission check. + if (write == true) + { + mode = mode | W_OK; + } + + // Check for execute permission check. + if (exec == true) + { + mode = mode | X_OK; + } + + // Issue syscall. + ret = access(absPath.c_str(), mode); + if (ret == 0) + { + // File or directory is readable & writable set ret to: COMMON_ERROR_SUCCESS. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Translate the errorcode. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::CheckPermissions(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(Common::commonLastErrorCode)); + COMMON_LOG_VERBOSE("\n"); + } + } + else + { + // No check to make. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CheckPermissions(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" No check to perform, aborting.\n"); + } + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::CheckPermissions(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Default return. + return ret; +} + +int FileUtills::DoesExist_Syscall(const std::string & absPath) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + + // Check for valid arg. + if (absPath.size() > 0) + { +#ifdef POSIX_COMMON_H + int errcpy = 0; // Used to copy errno if needed. + + // Check and see if the file exists. + ret = access(absPath.c_str(), F_OK); + if (ret == 0) + { + // Path exists. + ret = FILEUTILLS_ERROR_EXISTANT; + } + else + { + // Copy the error code. + errcpy = errno; + + // Translate the error code. + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + } +#endif // POSIX_COMMON_H + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::DoesExist(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + + // Set Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::IsFileOrDirectory_Syscall(const char * absPath, const size_t absPathSize) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // Result of this function. (Defaults to function not implemetated.) + int errcpy = 0; // Used to copy errno if needed. + +#ifdef POSIX_COMMON_H + struct stat * buf = NULL; // Linux stat structure, used with syscall. + + // Check for invalid absPath. + if ((absPath != NULL) && (absPathSize() > 0)) + { + // Init stat buffer. + buf = new struct stat; + if (buf != NULL) + { + // Ask the OS to stat() the path. + ret = lstat(absPath, buf); + + // Check the result. + if (ret == 0) + { + // Log result. + COMMON_LOG_VERBOSE("FileUtills::IsFileOrDirectory(): The given path ( "); + COMMON_LOG_VERBOSE(absPath); + + // Check the stat structure. + if (S_ISREG((buf->st_mode))) + { + // Path is a regular file. + ret = FILEUTILLS_ERROR_PATH_IS_A_FILE; + + // Log result. + COMMON_LOG_VERBOSE(" ) is a regular file."); + } + else + { + if (S_ISDIR((buf->st_mode))) + { + // Path is a directory. + ret = FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY; + + // Log result. + COMMON_LOG_VERBOSE(" ) is a directory."); + } + else + { + // Check for a symlink. + if (S_ISLNK((buf->st_mode))) + { + // Symlink. + ret = FILEUTILLS_ERROR_PATH_IS_A_SYMLINK; + + // Log result. + COMMON_LOG_VERBOSE(" ) is a symbolic link."); + } + else + { + // Special file. + ret = COMMON_ERROR_SUCCESS; + + // Log result. + COMMON_LOG_VERBOSE(" ) is a special file."); + } + } + } + } + else + { + // Error, translate it and log it. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // Check for non-existance. + if (ret == FILEUTILLS_ERROR_NON_EXISTANT) + { + COMMON_LOG_VERBOSE("FileUtills::IsFileOrDirectory(): The given path ( "); + COMMON_LOG_VERBOSE(absPath); + COMMON_LOG_VERBOSE(" ) or a componet of it does not exist."); + } + else + { + COMMON_LOG_DEBUG("FileUtills::IsFileOrDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Was returned while checking path ( "); + COMMON_LOG_DEBUG(absPath); + COMMON_LOG_DEBUG(" )."); + } + } + + // Delete stat buffer. + delete buf; + buf = NULL; + } + else + { + // Coul not allocate stat structure. + ret = COMMON_ERROR_MEMORY_ERROR; + + // Log the error if needed. + COMMON_LOG_DEBUG("FileUtills::IsFileOrDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + } + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::IsFileOrDirectory(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given path is invalid."); + } +#endif // POSIX_COMMON_H + + // Return the result. + return ret; +} + +int FileUtills::DeletePath_Syscall(const std::string & absPath) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + int errcpy = 0; // Used to copy the error code if needed. + + // Begin try block. + try { + // Check for invalid absPath. + if (absPath.size() > 0) + { +#ifdef POSIX_COMMON_H + // Issue syscall. + if (remove(absPath.c_str()) == 0) + { + // Success. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Copy errno. + errcpy = errno; + + // Translate the error. + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + } +#endif // POSIX_COMMON_H + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::DeletePath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given path is invalid.\n"); + } + } + catch (exception &ex) + { + // Exception thrown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::DeletePath(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(" "); + COMMON_LOG_VERBOSE(ex.what()); + COMMON_LOG_VERBOSE("\n"); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} + +int FileUtills::RenamePath_Syscall(const std::string & absPathSrc, const std::string & absPathDest, const bool & dereferenceSymLinks) +{ + // Init vars. + bool srcIsASymLink = false; // Used to tell if the source is a symlink. + bool destIsASymLink = false; // Used to tell if the destionation is a symlink. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + int errcpy = 0; // Used to copy errno for translation if needed. + char * resultFromRealPath = NULL; // Used to check the result from realpath(). + char * srcLinkedPath = NULL; // Used to hold the dereferenced source path if needed. + char * destLinkedPath = NULL; // Used to hold the dereferenced destionation path if needed. + + // Begin try block. + try { + // Check for valid source path. + if (absPathSrc.capacity() > 0) + { + // Check for valid destionation path. + if (absPathDest.capacity() > 0) + { + // Disable the symlink check if dereferenceSymLinks is set to false. + if (!dereferenceSymLinks) + { + // Check and see if the source is a symlink. + ret = FileUtills::IsFileOrDirectory_Syscall(absPathSrc); + if (ret == FILEUTILLS_ERROR_PATH_IS_A_SYMLINK) + { + // Clear resultFromRealPath. + resultFromRealPath = NULL; + + // Source is a symlink, so we need to dereference it. + srcIsASymLink = true; + resultFromRealPath = realpath(absPathSrc.c_str(), srcLinkedPath); + if (resultFromRealPath == NULL) + { + // We've hit an error. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::RenamePath(): Unable to dereference symlink on the source path ( "); + COMMON_LOG_DEBUG(absPathSrc.c_str()); + COMMON_LOG_DEBUG(").\n"); + + // Deallocate the srcLinkedPath if needed. + if (srcLinkedPath != NULL) + { + free(srcLinkedPath); + srcLinkedPath = NULL; + } + } + } + + // Check and see if the destionation is a symlink. + ret = FileUtills::IsFileOrDirectory_Syscall(absPathDest); + if (ret == FILEUTILLS_ERROR_PATH_IS_A_SYMLINK) + { + // Clear resultFromRealPath. + resultFromRealPath = NULL; + + // Destionation is a symlink, so we need to dereference it. + destIsASymLink = true; + resultFromRealPath = realpath(absPathDest.c_str(), destLinkedPath); + if (resultFromRealPath == NULL) + { + // We've hit an error. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::RenamePath(): Unable to dereference symlink on the destionation path ( "); + COMMON_LOG_DEBUG(absPathDest.c_str()); + COMMON_LOG_DEBUG(").\n"); + + // Deallocate the destLinkedPath if needed. + if (destLinkedPath != NULL) + { + free(destLinkedPath); + destLinkedPath = NULL; + } + } + } + } + + // Check and see what variables must be passed to rename. + if (srcIsASymLink && destIsASymLink) + { + // Check for allocated srcLinkedPath. + if (srcLinkedPath != NULL) + { + // Check for allocated destLinkedPath. + if (destLinkedPath != NULL) + { + // Issue syscall. + errcpy = rename(srcLinkedPath, destLinkedPath); + } + else + { + // Success without result? + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING("FileUtills::RenamePath(): Resolved symlink buffer for the destionation path ( "); + COMMON_LOG_WARNING(absPathDest.c_str()); + COMMON_LOG_WARNING(" ) is invalid, but it should be valid. Please report this bug.\n"); + } + } + else + { + // Success without result? + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING("FileUtills::RenamePath(): Resolved symlink buffer for the source path ( "); + COMMON_LOG_WARNING(absPathSrc.c_str()); + COMMON_LOG_WARNING(" ) is invalid, but it should be valid. Please report this bug.\n"); + } + } + else + { + if (srcIsASymLink && !destIsASymLink) + { + // Check for allocated srcLinkedPath. + if (srcLinkedPath != NULL) + { + // Issue syscall. + errcpy = rename(srcLinkedPath, absPathDest.c_str()); + } + else + { + // Success without result? + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING("FileUtills::RenamePath(): Resolved symlink buffer for the source path ( "); + COMMON_LOG_WARNING(absPathSrc.c_str()); + COMMON_LOG_WARNING(" ) is invalid, but it should be valid. Please report this bug.\n"); + } + } + else + { + if (!srcIsASymLink && destIsASymLink) + { + // Check for allocated destLinkedPath. + if (destLinkedPath != NULL) + { + // Issue syscall. + errcpy = rename(absPathSrc.c_str(), destLinkedPath); + } + else + { + // Success without result? + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING("FileUtills::RenamePath(): Resolved symlink buffer for the destionation path ( "); + COMMON_LOG_WARNING(absPathDest.c_str()); + COMMON_LOG_WARNING(" ) is invalid, but it should be valid. Please report this bug.\n"); + } + } + else + { + // Issue syscall. + errcpy = rename(absPathSrc.c_str(), absPathDest.c_str()); + } + } + } + + // Check to see if we hit an internal error. + if (ret != COMMON_ERROR_INTERNAL_ERROR) + { + // Check the result of the syscall. + if (errcpy == 0) + { + // Success. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Copy the error code. + errcpy = errno; + + // Check for EXDEV. + if (errcpy = EXDEV) + { + // The result is that the source and destionation are not on the same filesystem. + ret = FILEUTILLS_ERROR_NOT_ON_SAME_FILESYSTEM; + } + else + { + // Translate the error code. + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + } + } + } + } + else + { + // Invalid absPathDest. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::RenamePath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given destionation path is invalid.\n"); + } + } + else + { + // Invalid absPathSrc. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG("FileUtills::RenamePath(): "); + COMMON_LOG_DEBUG(Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(" Given source path is invalid.\n"); + } + + // Deallocate srcLinkedPath and destLinkedPath if needed. + if (srcLinkedPath != NULL) + { + free(srcLinkedPath); + srcLinkedPath = NULL; + } + if (destLinkedPath != NULL) + { + free(destLinkedPath); + destLinkedPath = NULL; + } + } + catch (exception &ex) + { + // Exception thrown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_VERBOSE("FileUtills::RenamePath(): "); + COMMON_LOG_VERBOSE(Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(" "); + COMMON_LOG_VERBOSE(ex.what()); + COMMON_LOG_VERBOSE("\n"); + } + + // Copy ret to Common::commonLastErrorCode. + Common::commonLastErrorCode = ret; + + // Return the result. + return ret; +} diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Posix_Syscall.h b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Posix_Syscall.h new file mode 100644 index 0000000..78ed703 --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Posix_Syscall.h @@ -0,0 +1,30 @@ +/*! + Multiverse Engine Project 16/5/2015 FileUtills FileUtills_Private_API_Posix_Syscall.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Include guard. +#ifndef FILEUTILLS_PRIVATE_API_POSIX_SYSCALL_H +#define FILEUTILLS_PRIVATE_API_POSIX_SYSCALL_H + +// Local definitions for paths. +#define PROC_PATH "/proc/self/exe" // The actual path to the symlink that represents the executable in the /proc filesystem. + +#endif // FILEUTILLS_PRIVATE_API_POSIX_SYSCALL_H + +// End of FileUtills_Private_API_Posix_Syscall.h diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Windows_Syscall.cpp b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Windows_Syscall.cpp new file mode 100644 index 0000000..03b2693 --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Windows_Syscall.cpp @@ -0,0 +1,1368 @@ +/*! + Multiverse Engine Project 06/7/2015 FileUtills FileUtills_Private_API_Windows_Syscall.cpp (Based on FileUtills_Private_API_Posix_Syscall.cpp) + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "FileUtills.h" +#include "FileUtills_Private_API.h" +#include "FileUtills_Private_API_Windows_Syscall.h" + +/* Define the MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID macro. */ +#define MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID MSYS_ERROR_LOG_CHANNEL_FILEUTILLS + +size_t FileUtills_Get_Max_Symlink_Depth_Syscall() +{ + /* Windows supports a maximum of 31 reparse points (and therefore symbolic links). + See also: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365460%28v=vs.85%29.aspx + + For some reason there does not appear to be a system constant for it.... + */ + return (MSYS_MAX_SYMLINK_DEPTH); +} + +void FileUtills_Windows_Syscall_Deallocate_CString(char ** obj) +{ + /* Deallocate the pointer. */ + DataProcess_Deallocate_CString(obj); + + /* Exit function. */ + return; +} + +void FileUtils_Deallocate_CString_Syscall(char ** obj) +{ + /* Call the real syscall function. */ + FileUtills_Windows_Syscall_Deallocate_CString(obj); + + /* Exit function. */ + return; +} + +/*! + int FileUtills_Windows_Syscall_Copy_C_String(const char * source, const size_t sourceLength, char ** dest) + + Internal function for coping a const string for later modification. + (Copied string if the function succeeds will be pointed to by dest, copied string is always + the same length as it's source.) + + Note: This function will overwrite dest without deallocating it. If you need whatever + dest points to after this function returns, copy the pointer elsewhere before calling + this function. + + When this string is no longer needed it should be deallocated by + FileUtills_Windows_Syscall_Deallocate_CString(), BEFORE returning to the helper + or public engine function. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is NULL, or if sourceLength <= 0. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + + No alteration clause: + In the event of an error, this function will not modifiy the arguments given to it. +*/ +int FileUtills_Windows_Syscall_Copy_C_String(const char * source, const size_t sourceLength, char ** dest) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of other engine functions. */ + size_t x = 0; /* Loop counter. */ + char * tempDest = NULL; /* Temporary pointer used to copy the source string. */ + + /* Check for valid arguments. */ + if ((source != NULL) && (sourceLength > 0) && (dest != NULL)) + { + /* Allocate memory for the copy. */ + retFromCall = DataProcess_Reallocate_C_String(&tempDest, 0, sourceLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempDest != NULL)) + { + /* Copy the string. */ + for (x = 0; (x < sourceLength); x++) + { + tempDest[x] = source[x]; + } + + /* Copy the pointer. */ + (*dest) = tempDest; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for tempDest. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_Windows_Syscall_Copy_C_String(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_Windows_Syscall_Copy_C_String(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Check for no success. */ + if (ret != COMMON_ERROR_SUCCESS) + { + /* Deallocate tempDest if needed. */ + if (tempDest != NULL) + { + DataProcess_Deallocate_CString(&tempDest); + } + } + + /* Exit function. */ + return ret; +} + +/*! + int FileUtills_Windows_Syscall_Add_Extended_Length_Prefix(char ** path, size_t * pathSize) + + Modifies the given string to prepend MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX + to the given path. (This allows the windows syscalls to use paths beyond MAX_PATH in length.) + The resulting string pointer will overwrite the original path pointer, and pathSize will be updated + to reflect the length of the resulting string. + + The length of the resulting string should always be: + ((*pathSize) + sizeof(MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX)). + + Note: This function will overwrite path and pathSize without deallocating them. If you need whatever + they point to after this function returns, copy them elsewhere before calling + this function. + + When this string is no longer needed it should be deallocated by + FileUtills_Windows_Syscall_Deallocate_CString(), BEFORE returning to the helper + or public engine function. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is NULL, or if pathSize <= 0. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + + No alteration clause: + In the event of an error, this function will not modifiy the arguments given to it. +*/ +int FileUtills_Windows_Syscall_Add_Extended_Length_Prefix(char ** path, size_t * pathSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of other engine functions. */ + size_t x = 0; /* Loop counter. */ + char * tempPath = NULL; /* Temporary variable used to prepend MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX to the path. */ + + /* Check for valid data. */ + if ((path != NULL) && ((*path) != NULL) && (pathSize != NULL) && ((*pathSize) > 0)) + { + /* Allocate memory for a new string. */ + retFromCall = DataProcess_Reallocate_CString(&tempPath, 0, ((*pathSize) + sizeof(MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX))); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPath != NULL)) + { + /* Add the MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX. */ + for (x = 0; (x < sizeof(MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX)); x++) + { + tempPath[x] = MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX[x]; + } + + /* Copy the original string. */ + for (x = 0; (x < (*pathSize)); x++) + { + tempPath[x] = ((*path)[x]); + } + + /* Copy the new pointer and size. */ + (*path) = tempPath; + (*pathSize) = ((*pathSize) + sizeof(MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX)); + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for tempPath. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_Windows_Syscall_Add_Extended_Length_Prefix(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_Windows_Syscall_Add_Extended_Length_Prefix(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Check for no success. */ + if (ret != COMMON_ERROR_SUCCESS) + { + /* Deallocate tempPath if needed. */ + if (tempPath != NULL) + { + DataProcess_Deallocate_CString(&tempPath); + } + } + + /* Exit function. */ + return ret; +} + +int FileUtills_ResolveSystemSymoblicLink_Syscall(char ** path, size_t * pathSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + + /* Make sure we got a valid path. */ + if ((path != NULL) && ((*path) != NULL) && (pathSize != NULL) && ((*pathSize) > 0)) + { + + } + else + { + /* Invalid path. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_ResolveSystemSymoblicLink(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given path argument is invalid."); + } + + /* Return the result. */ + return ret; +} + +int FileUtills::GetUserProfileDirectoryPath_Syscall(char ** path, size_t * pathLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; /* The result of this function. */ + _TCHAR * tempPath = NULL; /* Temporary pointer for constructing the path. */ + size_t tempPathLength = 0; /* Temporary variable for storing the length of the path. */ + + /* Check for invalid arguments. */ + if ((path != NULL) && (pathLength != NULL)) + { + + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Return the result. */ + return ret; +} + +int FileUtills_GetCurrentWorkingDirectoryPath_Syscall(char ** path, size_t * pathLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of other engine functions. */ + size_t tempPathLength = 0; /* The length of the tempPath string. */ + DWORD retGCD = 0; /* The result of GetCurrentDirectory(). */ + char * tempPath = NULL; /* Temporary variable for fetching the path from the host. */ + + /* Check for invalid arguments. */ + if ((path != NULL) && (pathLength != NULL)) + { + /* Set tempPathLength. */ + tempPathLength = MSYS_MAX_GET_EXE_PATH_REALLOC_BASE_SIZE; + + /* Allocate memory for the path. */ + retFromCall = DataProcess_Reallocate_C_String(&tempPath, 0, tempPathLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPath != NULL)) + { + /* Get the path. */ + retGCD = GetCurrentDirectory(tempPathLength, tempPath); + if (retGCD > tempPathLength) + { + /* The buffer is not big enough retGCD contains the needed size, in characters. */ + tempPathLength = retGCD; + + /* The buffer is not big enough so reallocate it. */ + retFromCall = DataProcess_Reallocate_C_String(&tempPath, 0, tempPathLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPath != NULL)) + { + /* Get the path. */ + retGCD = GetCurrentDirectory(tempPathLength, tempPath); + if (retGCD > tempPathLength) + { + /* Host has changed the current directory since the last call, so abort. */ + ret = COMMON_ERROR_RACE_CONDITION; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetCurrentWorkingDirectoryPath_Syscall(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_RACE_CONDITION)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Host has changed the current working directory while attempting to get it's path."); + } + else + { + if (retGCD == 0) + { + /* Call to GetCurrentDirectory() failed, get the error code. */ + retGCD = GetLastError(); + + /* Translate the error code. */ + retFromCall = Common_Error_Handler_Translate_Windows_Error_Code(retGFA); + ret = retFromCall; + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectory_Syscall(): Host function returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + else + { + /* Copy the pointer and length. */ + (*path) = tempPath; + (*pathLength) = tempPathLength; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + } + } + else + { + /* Could not allocate memory for path string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetCurrentWorkingDirectoryPath_Syscall(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not allocate memory for path string."); + } + } + else + { + if (retGCD == 0) + { + /* Call to GetCurrentDirectory() failed, get the error code. */ + retGCD = GetLastError(); + + /* Translate the error code. */ + retFromCall = Common_Error_Handler_Translate_Windows_Error_Code(retGFA); + ret = retFromCall; + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectory_Syscall(): Host function returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + else + { + /* Reallocate the buffer to reduce memory consumption. */ + retFromCall = DataProcess_Reallocate_C_String_With_NULL_Terminator(&tempPath, tempPathLength, retGCD); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPath != NULL)) + { + /* Copy the pointer and length. */ + (*path) = tempPath; + (*pathLength) = tempPathLength; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for path string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetCurrentWorkingDirectoryPath_Syscall(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not allocate memory for path string."); + } + } + } + } + else + { + /* Could not allocate memory for path string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetCurrentWorkingDirectoryPath_Syscall(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not allocate memory for path string."); + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetCurrentWorkingDirectoryPath_Syscall(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* If unsuccessful, make sure the buffer is deallocated. */ + if (ret != COMMON_ERROR_SUCCESS) + { + if (tempPath != NULL) + { + DataProcess_Deallocate_CString(&tempPath); + } + } + + /* Return the result. */ + return ret; +} + +int FileUtills_GetExecDirectory_Syscall(char ** retStr, size_t * retStrSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code returned from this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code returned from other engine functions. */ + LPTSTR readLinkBuf = NULL; /* The buffer used to get the string from the host. */ + LPTSTR checkBuffer = NULL; /* The buffer used to check the result of calling GetModuleFileName() with it's previous result. */ + DWORD realBufferLength = 0; /* The current length used to allocate the buffers. */ + DWORD firstBufferLength = 0; /* The result length for readLinkBuf returned from GetModuleFileName(). */ + DWORD secondBufferLength = 0; /* The result length for checkBuffer returned from GetModuleFileName(). */ + + /* Check for valid arguments. */ + if ((retStr != NULL) && (retStrSize != NULL)) + { + /* + * OK, run the hard limited implemetation. + * (Copied from the original FileUtills::GetExecDirectory_Syscall() implemetation for POSIX.) + * + * We do this to avoid a runaway reallocation loop which could be considered a resource starvation + * issue. (See below for the original rant, just replace the symlink talk with "using GetModuleFileName()", + * "readlink()" with "GetModuleFileName()", "linux" with "windows", "/proc/self/exe" with "", + * "the glibc manual" with "MSDN", and delete the second issue. + * + * Apperently Brain Dead Coders is an epidemic....... + * + * (Begin original rant.) + * + * Short version: + * This is a cluster. + * + * Long version: + * The only reliable (guarrenteed to work) method for getting the executable + * path in linux, is by using readlink() on /proc/self/exe. + * + * However, this has several issues. + * + * First issue: readlink() expects a preallocated buffer to store the result. + * If the preallocated buffer is too small for the full path, then readlink() + * will silently truncate the remaining data and not tell us how much was + * left over. + * + * Second issue: The proc filesystem misbehaves and does not fill in the + * st_size field of the stat structure. So as a result we can't easilly + * tell how much memory to allocate for our buffer. + * + * Third issue: Because some filesystems allow paths of unlimited size, + * (i.e. the only restriction is having the space to store the path), + * we can't use PATH_MAX (as it's possibly wrong), and we can't set a + * limit on our memory allocation. + * + * Because of these issues, the glibc MANUAL actually gives an indefinite + * loop of memory allocation->readlink()->if(!got entire link)->Deallocate memory->Allocate memory. + * for reading a symlink from the filesystem. + * + * This loop is reimplimented here, but with three differences. + * + * - The loop will end once a certian amount of reallocations (defined by MAX_GET_EXE_PATH_REALLOC) + * is reached. (This is to prevent some of the above issues.) + * + * - The loop will also check to make sure that the returned string has not been altered. + * (via memcmp). (Attempt to prevent a compromised system from mallissously changing the + * symlink's destionation as a result of our multiple accesses to the link itself.) + * + * - The loop will reallocate the buffer after the link is fully read so that ONLY the + * needed memory to store it is allocated for it. (Prevent unnessacarry memory usage.) + * + * (End of original rant.) + */ + /* Begin the devil's loop. */ + for (size_t x = 0; ((x < MSYS_MAX_GET_EXE_PATH_REALLOC) && + ((readLinkBuf == NULL) && (checkBuffer == NULL)) && + (ret == COMMON_ERROR_UNKNOWN_ERROR)); x++) + { + /* Recalculate bufferLength. */ + realBufferLength = (MSYS_MAX_GET_EXE_PATH_REALLOC_BASE_SIZE * (x + 1)); + + /* Allocate the memory. */ + retFromCall = DataProcess_Reallocate_C_String(((char**)&readLinkBuf), 0, realBufferLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (readLinkBuf != NULL)) + { + retFromCall = DataProcess_Reallocate_C_String(((char**)&checkBuffer), 0, realBufferLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (checkBuffer != NULL)) + { + /* Make sure it was allocated. */ + if ((readLinkBuf != NULL) && (checkBuffer != NULL)) + { + /* Blank out the allocated memory. */ + memset(readLinkBuf, '\0', realBufferLength); + memset(checkBuffer, '\0', realBufferLength); + + /* Call GetModuleFileName() for the first buffer. */ + firstBufferLength = GetModuleFileName(NULL, readLinkBuf, realBufferLength); + + /* Check bufferLength. */ + if (firstBufferLength >= 0) + { + /* Check to see if we got the entire path. */ + if (firstBufferLength < realBufferLength) + { + /* Call GetModuleFileName() for the second buffer. */ + secondBufferLength = GetModuleFileName(NULL, checkBuffer, realBufferLength); + + /* Check secondBufferLength. */ + if (secondBufferLength >= 0) + { + /* Check to see if we got the entire path. */ + if (secondBufferLength == firstBufferLength) + { + /* Call memcmp(). */ + if (memcmp(readLinkBuf, checkBuffer, realBufferLength) == 0) + { + /* Paths match, deallocate the second buffer. */ + if (checkBuffer != NULL) + { + memset(checkBuffer, '\0', realBufferLength); + DataProcess_Deallocate_CString(&checkBuffer); + } + + /* Reallocate the buffer. (Free unneeded memory.) */ + retFromCall = FileUtills_Reallocate_CString_Buffer(readLinkBuf, realBufferLength, firstBufferLength); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Copy the first buffer pointer to the path buffer pointer. */ + (*retStr) = readLinkBuf; + + /* Copy the length of the buffer. */ + (*retStrSize) = firstBufferLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not free the unneeded memory. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): Call to FileUtills_Reallocate_CString_Buffer() failed: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + else + { + /* Something is screwing with us...abort. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* Something is screwing with us...abort. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* Error. */ + errcpy = GetLastError(); + ret = Common_Error_Handler_Translate_Windows_Error_Code(errcpy); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): GetModuleFileName() system call returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + } + } + else + { + /* We did not get the entire link, so we need to deallocate it for the next loop. */ + if (readLinkBuf != NULL) + { + memset(readLinkBuf, '\0', realBufferLength); + DataProcess_Deallocate_CString(&readLinkBuf); + } + if (checkBuffer != NULL) + { + memset(checkBuffer, '\0', realBufferLength); + DataProcess_Deallocate_CString(&checkBuffer); + } + } + } + else + { + /* Error. */ + errcpy = GetLastError(); + ret = Common_Error_Handler_Translate_Windows_Error_Code(errcpy); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): GetModuleFileName() system call returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(ret)); + } + } + } + else + { + /* Could not allocate memory for check buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not allocate memory for check buffer."); + } + } + else + { + /* Could not allocate memory for path buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_MEMORY_ERROR)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not allocate memory for path buffer."); + } + } + + /* + * If we reach here, and ret == COMMON_ERROR_UNKNOWN_ERROR, + * then we have failed. (Most likely we ran out of reallocation attempts...) + */ + if (ret == COMMON_ERROR_UNKNOWN_ERROR) + { + /* + * Set COMMON_INTERNAL_ERROR. + * + * (The system most likely can fetch the link, + * but we need to limit the reallocation attempts + * to prevent issues. So it's not appropriate to use + * FILEUTILLS_PATH_LENGTH_INVALID.) + */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): "); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INTERNAL_ERROR)); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Unable to resolve the symbolic link due to engine limitation. (Length of the resolved path is too long.)"); + } + + /* If unsuccessful, make sure both buffers are deallocated. */ + if (ret != COMMON_ERROR_SUCCESS) + { + if (readLinkBuf != NULL) + { + memset(readLinkBuf, '\0', realBufferLength); + DataProcess_Deallocate_CString(&readLinkBuf); + } + if (checkBuffer != NULL) + { + memset(checkBuffer, '\0', realBufferLength); + DataProcess_Deallocate_CString(&checkBuffer); + } + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_GetExecDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + } + + /* Return result. */ + return ret; +} + +FileUtills::dirlist * FileUtills::getDirectory_Syscall(const std::string & absPath, const bool & cleanList) +{ + /* Init vars. */ + int errcpy = 0; /* Used to fetch the error code. */ + FileUtills::dirlist * ret = NULL; /* The FileUtills directory structure. */ + std::string tempname = ""; /* Used to store the filename / subdirectory name for the addToArray function. */ + _TCHAR * tempPath = NULL; + + + _tfindfirst(); /* Find first file. */ + _tfindnext(); /* Find next file. */ + _tfindclose(); /* Release directory search handles and resources. */ + + /* Dumb check. */ + if (absPath.size() > 0) + { + + /* Allocate the dirlist. */ + try{ + ret = new FileUtills::dirlist; + if (ret != NULL) + { + /* Set the path.*/ + ret->path = absPath; + + /* Dump the path as a c string into the Directory stream object......(Overly complicated POS......) */ + + /* Check and make sure we can open the directory first. */ + if ((dp = (opendir(absPath.c_str()))) == NULL) + { + // Copy the error code, and translate it. + errcpy = errno; + errcpy = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // An error occured. + if (ret != NULL) + { + delete ret; + ret = NULL; + } + + // Log the error. + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::getDirectory(): Unable to get directory contents for ( "); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, absPath.c_str()); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) The system returned "); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(errcpy)); + COMMON_LOG_INFO(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "\n"); + + // Copy the translated error code to Common::commonLastErrorCode. + ret = errcpy; + } + + // Start filesystem fetch loop. + while ((dir = readdir(dp))) // Call Host OS function. + { + // Check and see if the cleanList flag is set. + if (cleanList) + { + // Check to see if d_name is a POSIX directory shortcut. + if ((strcmp(dir->d_name, ".") == 0) || (strcmp(dir->d_name, "..") == 0)) + { + // d_name is a directory shortcut, do not add it to the list. + continue; + } + } + + // Cast d_name to a string. + tempname = dir->d_name; + + // Add the data to the array. + ret->list.push_back(tempname); + } + + // Close the directory stream and reset it. + closedir(dp); + + // Set the number of entries. + ret->numOfEntries = ret->list.size(); + + // Set Common::commonLastErrorCode. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Could not allocate memory. + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + catch(std::exception &ex) + { + // Free memory if needed. + if (ret != NULL) + { + delete ret; + ret = NULL; + } + + // Could not allocate struct. + ret = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::getDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(COMMON_ERROR_EXCEPTION_THROWN)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, ex.what()); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "\n"); + } +#endif + } + else + { + // Invalid path. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::getDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given path is invalid.\n"); + } + + // Return the result. + return ret; +} + +int FileUtills::GetByteFreespace_Syscall(const std::string & absPath, size_t & result) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + + + + // Return the result. + return ret; +} + +int FileUtills::CreateDirectory_Syscall(const char * absPath) +{ + // Init errcpy. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + + // Check for NULL pointer. + if (absPath != NULL) + { + + } + else + { + // Invalid argument. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::CreateDirectory(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given path is invalid.\n"); + } + + // Return the result. + return ret; +} + +int FileUtills::CheckPermissions_Syscall(const std::string & absPath, const bool & read, const bool & write, const bool & exec) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + + // Make sure that absPath is valid. + if (absPath.size() > 0) + { + // Double check and make sure we have something to do. + if ((read != false) || (write != false) || (exec != false)) + { + + } + else + { + // No check to make. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::CheckPermissions(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " No check to perform, aborting.\n"); + } + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::CheckPermissions(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given path is invalid.\n"); + } + + // Default return. + return ret; +} + +int FileUtills::DoesExist_Syscall(const std::string & absPath) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + + // Check for valid arg. + if (absPath.size() > 0) + { + + int errcpy = 0; // Used to copy errno if needed. + + // Check and see if the file exists. + ret = access(absPath.c_str(), F_OK); + if (ret == 0) + { + // Path exists. + ret = FILEUTILLS_ERROR_EXISTANT; + } + else + { + // Copy the error code. + errcpy = errno; + + // Translate the error code. + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + } +#endif // POSIX_COMMON_H + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::DoesExist(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given path is invalid.\n"); + } + + // Return the result. + return ret; +} + +int FileUtills_IsFileOrDirectory_Syscall(const char * absPath, const size_t absPathSize) +{ + /* Init vars. */ + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; /* Result of this function. (Defaults to function not implemented.) */ + char * tempAbsPath = NULL; /* Temporary variable used to prepend the needed syntax for unicode use. */ + size_t tempAbsPathLength = 0; /* Length of the tempAbsPath string. */ + DWORD retGFA = 0; /* The result of the call to GetFileAttributes(). */ + LPWIN32_FIND_DATA fdLP = NULL; /* Pointer to a LPWIN32_FIND_DATA structure. Used when checking for symlinks. */ + HANDLE retFFF = NULL; /* The handle from FindFirstFile(). */ + + /* Check for invalid absPath. */ + if ((absPath != NULL) && (absPathSize() > 0)) + { + /* Copy the absPath pointer and length. */ + retFromCall = FileUtills_Windows_Syscall_Copy_C_String(absPath, absPathSize, tempAbsPath); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempAbsPath != NULL)) + { + /* Set tempAbsPathLength. */ + tempAbsPathLength = absPathSize; + + /* Convert string to have needed prepended tag. */ + retFromCall = FileUtills_Windows_Syscall_Add_Extended_Length_Prefix(&tempAbsPath, &tempAbsPathLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempAbsPath != NULL) && (tempAbsPathLength > 0)) + { + /* Call GetFileAttributes(). */ + retGFA = GetFileAttributes(tempAbsPath); + if (retGFA != INVALID_FILE_ATTRIBUTES) + { + /* Check for directory. */ + if (retGFA | FILE_ATTRIBUTE_DIRECTORY) + { + ret = FILEUTILLS_ERROR_PATH_IS_A_DIRECTORY; + } + else + { + /* Check for a file. + + Note: Windows does not provide a "this is a file" attribute, + so we have to check for all other entry types, to determine if + the given filesystem entry is a file. + */ + if (retGFA | FILE_ATTRIBUTE_DEVICE) + { + /* This is a device handle! + The engine does not support + accessing raw devices, so return + COMMON_ERROR_SUCCESS. + */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* It's a file. */ + ret = FILEUTILLS_ERROR_PATH_IS_A_FILE; + } + } + + /* Check for reparse point. */ + if (retGFA | FILE_ATTRIBUTE_REPARSE_POINT) + { + /* Get the entry's LPWIN32_FIND_DATA structure. */ + retFFF = FindFirstFile(tempAbsPath, fdLP); + if ((retFFF != INVALID_HANDLE_VALUE) && (retFFF != ERROR_FILE_NOT_FOUND) && (fdLP != NULL)) + { + /* Check for symlink. */ + if ((fdLP->dwReserved0) | IO_REPARSE_TAG_SYMLINK) + { + ret = FILEUTILLS_ERROR_PATH_IS_A_SYMLINK; + } + } + else + { + /* Call failed get error info. */ + retGFA = GetLastError(); + + /* Translate the error code. */ + retFromCall = Common_Error_Handler_Translate_Windows_Error_Code(retGFA); + ret = retFromCall; + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectory_Syscall(): Host function returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + } + else + { + /* Call failed get error info. */ + retGFA = GetLastError(); + + /* Translate the error code. */ + retFromCall = Common_Error_Handler_Translate_Windows_Error_Code(retGFA); + ret = retFromCall; + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectory_Syscall(): Host function returned: "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + } + } + else + { + /* Check for memory error. */ + if (retFromCall == COMMON_ERROR_MEMORY_ERROR) + { + ret = COMMON_ERROR_MEMORY_ERROR; + } + else + { + /* Could not prepend the extended length prefix to the given path. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectory_Syscall(): Could not prepend extended length prefix to path."); + } + } + } + else + { + /* Could not copy path string to temp buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectory_Syscall(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(retFromCall)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Could not copy path string to temp buffer."); + } + } + else + { + /* Invalid absPath. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + + /* Log the error. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectory_Syscall(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common_Get_Error_Message(COMMON_ERROR_INVALID_ARGUMENT)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given path is invalid."); + } + + /* Check for open handle and close it if needed. */ + if (fdLP != NULL) + { + if (FindClose(fdLP)) + { + fdLP = NULL; + } + else + { + /* Could not release LPWIN32_FIND_DATA structure. */ + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills_IsFileOrDirectory_Syscall(): Could not release LPWIN32_FIND_DATA structure."); + } + } + + /* Deallocate temporary string memory. */ + FileUtills_Windows_Syscall_Deallocate_CString(&tempAbsPath); + + /* Return the result. */ + return ret; +} + +int FileUtills::DeletePath_Syscall(const std::string & absPath) +{ + // Init vars. + int ret = COMMON_ERROR_FUNCTION_NOT_IMPLEMENTED; // The result of this function. + + // Begin try block. + try { + // Check for invalid absPath. + if (absPath.size() > 0) + { + + // Issue syscall. + if (remove(absPath.c_str()) == 0) + { + // Success. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Copy errno. + errcpy = errno; + + // Translate the error. + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + } +#endif // POSIX_COMMON_H + } + else + { + // Invalid absPath. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::DeletePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given path is invalid.\n"); + } + } + catch (std::exception &ex) + { + // Exception thrown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::DeletePath(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, ex.what()); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "\n"); + } + + // Return the result. + return ret; +} + +int FileUtills::RenamePath_Syscall(const std::string & absPathSrc, const std::string & absPathDest, const bool & dereferenceSymLinks) +{ + // Init vars. + bool srcIsASymLink = false; // Used to tell if the source is a symlink. + bool destIsASymLink = false; // Used to tell if the destionation is a symlink. + int ret = COMMON_ERROR_UNKNOWN_ERROR; // The result of this function. + int errcpy = 0; // Used to copy errno for translation if needed. + char * resultFromRealPath = NULL; // Used to check the result from realpath(). + char * srcLinkedPath = NULL; // Used to hold the dereferenced source path if needed. + char * destLinkedPath = NULL; // Used to hold the dereferenced destionation path if needed. + + // Begin try block. + try { + // Check for valid source path. + if (absPathSrc.capacity() > 0) + { + // Check for valid destionation path. + if (absPathDest.capacity() > 0) + { + // Disable the symlink check if dereferenceSymLinks is set to false. + if (!dereferenceSymLinks) + { + // Check and see if the source is a symlink. + ret = FileUtills::IsFileOrDirectory_Syscall(absPathSrc); + if (ret == FILEUTILLS_ERROR_PATH_IS_A_SYMLINK) + { + // Clear resultFromRealPath. + resultFromRealPath = NULL; + + // Source is a symlink, so we need to dereference it. + srcIsASymLink = true; + resultFromRealPath = realpath(absPathSrc.c_str(), srcLinkedPath); + if (resultFromRealPath == NULL) + { + // We've hit an error. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): Unable to dereference symlink on the source path ( "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, absPathSrc.c_str()); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, ").\n"); + + // Deallocate the srcLinkedPath if needed. + if (srcLinkedPath != NULL) + { + free(srcLinkedPath); + srcLinkedPath = NULL; + } + } + } + + // Check and see if the destionation is a symlink. + ret = FileUtills::IsFileOrDirectory_Syscall(absPathDest); + if (ret == FILEUTILLS_ERROR_PATH_IS_A_SYMLINK) + { + // Clear resultFromRealPath. + resultFromRealPath = NULL; + + // Destionation is a symlink, so we need to dereference it. + destIsASymLink = true; + resultFromRealPath = realpath(absPathDest.c_str(), destLinkedPath); + if (resultFromRealPath == NULL) + { + // We've hit an error. + errcpy = errno; + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): Unable to dereference symlink on the destionation path ( "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, absPathDest.c_str()); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, ").\n"); + + // Deallocate the destLinkedPath if needed. + if (destLinkedPath != NULL) + { + free(destLinkedPath); + destLinkedPath = NULL; + } + } + } + } + + // Check and see what variables must be passed to rename. + if (srcIsASymLink && destIsASymLink) + { + // Check for allocated srcLinkedPath. + if (srcLinkedPath != NULL) + { + // Check for allocated destLinkedPath. + if (destLinkedPath != NULL) + { + // Issue syscall. + errcpy = rename(srcLinkedPath, destLinkedPath); + } + else + { + // Success without result? + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): Resolved symlink buffer for the destionation path ( "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, absPathDest.c_str()); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) is invalid, but it should be valid. Please report this bug.\n"); + } + } + else + { + // Success without result? + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): Resolved symlink buffer for the source path ( "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, absPathSrc.c_str()); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) is invalid, but it should be valid. Please report this bug.\n"); + } + } + else + { + if (srcIsASymLink && !destIsASymLink) + { + // Check for allocated srcLinkedPath. + if (srcLinkedPath != NULL) + { + // Issue syscall. + errcpy = rename(srcLinkedPath, absPathDest.c_str()); + } + else + { + // Success without result? + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): Resolved symlink buffer for the source path ( "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, absPathSrc.c_str()); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) is invalid, but it should be valid. Please report this bug.\n"); + } + } + else + { + if (!srcIsASymLink && destIsASymLink) + { + // Check for allocated destLinkedPath. + if (destLinkedPath != NULL) + { + // Issue syscall. + errcpy = rename(absPathSrc.c_str(), destLinkedPath); + } + else + { + // Success without result? + ret = COMMON_ERROR_INTERNAL_ERROR; + + // Log the error. + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): Resolved symlink buffer for the destionation path ( "); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, absPathDest.c_str()); + COMMON_LOG_WARNING(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " ) is invalid, but it should be valid. Please report this bug.\n"); + } + } + else + { + // Issue syscall. + errcpy = rename(absPathSrc.c_str(), absPathDest.c_str()); + } + } + } + + // Check to see if we hit an internal error. + if (ret != COMMON_ERROR_INTERNAL_ERROR) + { + // Check the result of the syscall. + if (errcpy == 0) + { + // Success. + ret = COMMON_ERROR_SUCCESS; + } + else + { + // Copy the error code. + errcpy = errno; + + // Check for EXDEV. + if (errcpy = EXDEV) + { + // The result is that the source and destionation are not on the same filesystem. + ret = FILEUTILLS_ERROR_NOT_ON_SAME_FILESYSTEM; + } + else + { + // Translate the error code. + ret = Common::Translate_Posix_Errno_To_Common_Error_Code(errcpy); + } + } + } + } + else + { + // Invalid absPathDest. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given destionation path is invalid.\n"); + } + } + else + { + // Invalid absPathSrc. + ret = COMMON_ERROR_INVALID_ARGUMENT; + + // Log the error. + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): "); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_DEBUG(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " Given source path is invalid.\n"); + } + + // Deallocate srcLinkedPath and destLinkedPath if needed. + if (srcLinkedPath != NULL) + { + free(srcLinkedPath); + srcLinkedPath = NULL; + } + if (destLinkedPath != NULL) + { + free(destLinkedPath); + destLinkedPath = NULL; + } + } + catch (std::exception &ex) + { + // Exception thrown. + ret = COMMON_ERROR_EXCEPTION_THROWN; + + // Log the error. + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "FileUtills::RenamePath(): "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, Common::Get_Error_Message(ret)); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, " "); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, ex.what()); + COMMON_LOG_VERBOSE(MSYS_SUBSYS_DEFAULT_ERROR_CHANNEL_ID, "\n"); + } + + // Return the result. + return ret; +} diff --git a/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Windows_Syscall.h b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Windows_Syscall.h new file mode 100644 index 0000000..622c2c6 --- /dev/null +++ b/src/Common/Src/File_Management_Subsystem/FileUtills_Private_API_Windows_Syscall.h @@ -0,0 +1,45 @@ +/*! + Multiverse Engine Project 06/7/2015 FileUtills FileUtills_Private_API_Windows_Syscall.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef FILEUTILLS_PRIVATE_API_WINDOWS_SYSCALL_H +#define FILEUTILLS_PRIVATE_API_WINDOWS_SYSCALL_H + +/* Define CSIDL values. */ +#define FILEUTILLS_USER_PROFILE_ID CSIDL_PROFILE +#define FILEUTILLS_USER_DOCUMENTS_ID CSIDL_PERSONAL +#define FILEUTILLS_APPDATA_ID CSIDL_LOCAL_APPDATA + +/* Define MSYS_MAX_SYMLINK_DEPTH. + MSDN Says the max depth is 31. + https://msdn.microsoft.com/en-us/library/windows/desktop/aa365460%28v=vs.85%29.aspx +*/ +#define MSYS_MAX_SYMLINK_DEPTH 31 + +/* Define extended length prefix for filesystem calls to use Unicode mode. */ +#define MSYS_FILEUTILLS_WINDOWS_SYSCALL_EXTENDED_LENGTH_PREFIX "\\?\" + +/* Define the crap for the devil's loop in FileUtills_GetExecDirectory_Syscall(). */ +#define MSYS_MAX_GET_EXE_PATH_REALLOC 4 +#define MSYS_MAX_GET_EXE_PATH_REALLOC_BASE_SIZE 1000 + +#endif /* FILEUTILLS_PRIVATE_API_WINDOWS_SYSCALL_H */ + +/* End of FileUtills_Private_API_Windows_Syscall.h. */ diff --git a/src/Common/Src/Mutexes/Linux/MSYS_Mutexes_Linux.c b/src/Common/Src/Mutexes/Linux/MSYS_Mutexes_Linux.c index dd3c100..27899ad 100644 --- a/src/Common/Src/Mutexes/Linux/MSYS_Mutexes_Linux.c +++ b/src/Common/Src/Mutexes/Linux/MSYS_Mutexes_Linux.c @@ -19,21 +19,26 @@ */ /* Internal includes. */ -#include "MSYS_Mutexes.h" +#include "../MSYS_Mutexes.h" /* Define extern C */ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ +/* Because we need to issue syscall(), we need to include the needed headers. */ +#define _GNU_SOURCE +#include +#include + /* Define the needed function calls. */ long MSYS_Get_Thread_ID() { /* Init vars. */ long ret = 0; /* The result of this function. */ - +/* NOTE: This call really needs to return a generic value (Probably a struct....) that contains the correct value. (Along with an ID to identify the source library...) That's going to require changing the API though. */ /* Call the system call gettid(2). (NOT pthread_self(3), that cannot be converted to a long in ANY reasonable manner. Hence the linux host preprocessor check as gettid(2) is linux specific.) */ - ret = gettid(); + ret = syscall(SYS_gettid); /* Check for success. */ if (ret == 0) diff --git a/src/Common/Src/Mutexes/Linux/MSYS_Mutexes_Linux.h b/src/Common/Src/Mutexes/Linux/MSYS_Mutexes_Linux.h index 7b45212..2ea05d7 100644 --- a/src/Common/Src/Mutexes/Linux/MSYS_Mutexes_Linux.h +++ b/src/Common/Src/Mutexes/Linux/MSYS_Mutexes_Linux.h @@ -35,6 +35,24 @@ extern "C" { #endif /* __cplusplus */ +/* Check which C standard is supported by the compiler. */ +#if __STDC_VERSION__ < 199901L +/* We need to include our fake stdbool.h header file. */ +#include "../../../../stdbool.h" +#else +/* Use the compiler's version of stdbool.h. */ +#include + +/* We need to include aliases for TRUE and FALSE. */ +#ifndef TRUE +#define TRUE true +#endif /* TRUE */ + +#ifndef FALSE +#define FALSE false +#endif /* FALSE */ +#endif /* __STDC_VERSION__ < 199901L */ + /* Define the invalid thread ID to use. */ #define MSYS_INVALID_TID 0 diff --git a/src/Common/Src/Mutexes/MSYS_Mutexes.c b/src/Common/Src/Mutexes/MSYS_Mutexes.c index 33e2738..677de37 100644 --- a/src/Common/Src/Mutexes/MSYS_Mutexes.c +++ b/src/Common/Src/Mutexes/MSYS_Mutexes.c @@ -167,7 +167,7 @@ MSYS_Mutex * MSYS_Lock_Mutex(MSYS_Mutex * mu) if ((mu != NULL) && (mu->lock != NULL)) { /* Begin loop. */ - while (retFromLOCK != MSYS_MU_SUCCESS) + while ((retFromLOCK != MSYS_MU_SUCCESS) || (retFromLOCK != MSYS_MU_ALREADY_OWNED)) { /* Attempt to lock the mutex. */ retFromLOCK = MSYS_Try_Lock_Mutex(mu); @@ -243,15 +243,44 @@ short MSYS_Try_Lock_Mutex(MSYS_Mutex * mu) } else { - /* Lock is already active. */ - ret = MSYS_MU_ALREADY_LOCKED; + /* Check and see if we own the lock. */ + if (((MSYS_Mutex_Private *)mu->lock)->tidLock == tid) + { + /* Lock is already held by us. */ + ret = MSYS_MU_ALREADY_OWNED; + } + else + { + /* Lock is owned by someone else. */ + ret = MSYS_MU_ALREADY_LOCKED; + } } /* Release the lock. */ MSYS_Compare_And_Swap((&((MSYS_Mutex_Private *)mu->lock)->lock), TRUE, FALSE); #else /* Attempt to lock the mutex. */ - ret = MSYS_Compare_And_Swap_Long((&((MSYS_Mutex_Private *)mu->lock)->tidLock), MSYS_INVALID_TID, tid); + retFromCAS = MSYS_Compare_And_Swap_Long((&((MSYS_Mutex_Private *)mu->lock)->tidLock), MSYS_INVALID_TID, tid); + if (retFromCAS == TRUE) + { + /* Set ret. */ + ret = MSYS_MU_SUCCESS; + } + else + { + /* Check and see if we own the lock. */ + retFromCAS = MSYS_Compare_And_Swap_Long((&((MSYS_Mutex_Private *)mu->lock)->tidLock), tid, tid); + if (retFromCAS) + { + /* Lock is already held by us. */ + ret = MSYS_MU_ALREADY_OWNED; + } + else + { + /* Lock is owned by someone else. */ + ret = MSYS_MU_ALREADY_LOCKED; + } + } #endif /* MSYS_INSUFFIENCT_BITS_LONG_LOCK */ } @@ -326,8 +355,17 @@ short MSYS_Unlock_Mutex(MSYS_Mutex * mu) } else { - /* Lock is owned by another thread, or is unowned. */ - ret = MSYS_MU_ALREADY_LOCKED; + /* Check whether lock is owned by another thread, or is unowned. */ + if (((MSYS_Mutex_Private *)mu->lock)->tidLock == MSYS_INVALID_TID) + { + /* The mutex is not owned by anyone. */ + ret = MSYS_MU_ALREADY_UNLOCKED; + } + else + { + /* The mutex is locked by another thread. */ + ret = MSYS_MU_ALREADY_LOCKED; + } } /* Release the lock. */ @@ -341,8 +379,18 @@ short MSYS_Unlock_Mutex(MSYS_Mutex * mu) } else { - /* Lock is owned by another thread, or is unowned. */ - ret = MSYS_MU_ALREADY_LOCKED; + /* Check whether lock is owned by another thread, or is unowned. */ + retFromCAS = MSYS_Compare_And_Swap_Long((&((MSYS_Mutex_Private *)mu->lock)->tidLock), MSYS_INVALID_TID, MSYS_INVALID_TID); + if (retFromCAS == TRUE) + { + /* The mutex is not owned by anyone. */ + ret = MSYS_MU_ALREADY_UNLOCKED; + } + else + { + /* The mutex is locked by another thread. */ + ret = MSYS_MU_ALREADY_LOCKED; + } } #endif /* MSYS_INSUFFIENCT_BITS_LONG_LOCK */ } diff --git a/src/Common/Src/Mutexes/MSYS_Mutexes.h b/src/Common/Src/Mutexes/MSYS_Mutexes.h index d46ddc9..bdf9047 100644 --- a/src/Common/Src/Mutexes/MSYS_Mutexes.h +++ b/src/Common/Src/Mutexes/MSYS_Mutexes.h @@ -32,7 +32,9 @@ extern "C" { /* Define error codes. */ #define MSYS_MU_SUCCESS 0 -#define MSYS_MU_ALREADY_LOCKED 1 +#define MSYS_MU_ALREADY_UNLOCKED 3 /* Locked by no-one. */ +#define MSYS_MU_ALREADY_OWNED 2 /* Locked by the current thread. */ +#define MSYS_MU_ALREADY_LOCKED 1 /* Locked by another thread. */ #define MSYS_MU_INVALID -1 #define MSYS_MU_UNKNOWN_ERROR -2 @@ -107,13 +109,14 @@ MSYS_Mutex * MSYS_Lock_Mutex(MSYS_Mutex * mu); * Takes the given MSYS_Mutex struct pointer and blocks the * caller until a lock attempt is made. * - * Returns 0 if the lock is aquired and now owned by the + * Returns MSYS_MU_SUCCESS if the lock is aquired and now owned by the * calling thread. * - * Returns 1 if the lock is already owned by another thread, - * or owned by the calling thread. + * Returns MSYS_MU_ALREADY_LOCKED if the lock is already owned by another thread. * - * Returns -1 if the given mutex pointer is invalid. + * Returns MSYS_MU_ALREADY_OWNED if owned by the calling thread. + * + * Returns MSYS_MU_INVALID if the given mutex pointer is invalid. */ short MSYS_Try_Lock_Mutex(MSYS_Mutex * mu); @@ -123,12 +126,13 @@ short MSYS_Try_Lock_Mutex(MSYS_Mutex * mu); * Takes the given MSYS_Mutex struct pointer and attempts * to release the mutex lock. * - * Returns 0 if successful. + * Returns MSYS_MU_SUCCESS if successful. + * + * Returns MSYS_MU_ALREADY_UNLOCKED if the lock is free. * - * Returns 1 if the lock is unowned, or if - * the calling thread does not own the lock. + * Returns MSYS_MU_ALREADY_LOCKED the calling thread does not own the lock. * - * Returns -1 if the given mutex pointer is invalid. + * Returns MSYS_MU_INVALID if the given mutex pointer is invalid. */ short MSYS_Unlock_Mutex(MSYS_Mutex * mu); diff --git a/src/Common/Src/Mutexes/Windows/MSYS_Mutexes_MSVC.h b/src/Common/Src/Mutexes/Windows/MSYS_Mutexes_MSVC.h index 8082ecf..c5bdd00 100644 --- a/src/Common/Src/Mutexes/Windows/MSYS_Mutexes_MSVC.h +++ b/src/Common/Src/Mutexes/Windows/MSYS_Mutexes_MSVC.h @@ -35,7 +35,7 @@ extern "C" { #endif /* __cplusplus */ /* Define stdbool. */ -#include "..\stdbool.h" +#include "..\..\..\stdbool.h" /* Define the functions. */ diff --git a/src/Common/Src/Rendering_Subsystem/CMakeLists.txt b/src/Common/Src/Rendering_Subsystem/CMakeLists.txt new file mode 100644 index 0000000..e5bc51a --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/CMakeLists.txt @@ -0,0 +1,20 @@ +# Set output directories. +set(LIBRARY_OUTPUT_PATH ${L_OUTPUT_DIR}) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${O_OUTPUT_DIR}) + +# Set the includes. +set(RENDERING_SUBSYS_INCLUDES Renderer_Data_Structures.cpp +Renderer_Data_Structures_Overlay_Base_Functions.cpp +Renderer_Basic_Functions.cpp +Renderer_Text_Console.cpp) + +# Build Rendering_Subsystem library. +add_library(Rendering_Subsystem_Multiverse_Engine SHARED ${RENDERING_SUBSYS_INCLUDES}) +target_link_libraries(Rendering_Subsystem_Multiverse_Engine Common_Error_Handler_Multiverse_Engine +Core_Multiverse_Engine +Panic_Handler_Multiverse_Engine) + +add_library(Rendering_Subsystem_Multiverse_Engine_Static STATIC ${RENDERING_SUBSYS_INCLUDES}) +target_link_libraries(Rendering_Subsystem_Multiverse_Engine_Static Common_Error_Handler_Multiverse_Engine_Static +Core_Multiverse_Engine_Static +Panic_Handler_Multiverse_Engine_Static) diff --git a/src/Common/Src/Rendering_Subsystem/Renderer.h b/src/Common/Src/Rendering_Subsystem/Renderer.h new file mode 100644 index 0000000..4ddde25 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer.h @@ -0,0 +1,64 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer.h 20/12/2013 + + This is the base header for the rendering subsystem. + You should only include this header when using the rendering subsystem. + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef COMMON_RENDER_SUBSYSTEM_H +#define COMMON_RENDER_SUBSYSTEM_H + +/* Check for C++ compiler. */ +#ifdef __cplusplus + +/* C++ Standard library includes. */ +#include +#include +#include +#include + +/* Check for C compiler. */ +#else /* __cplusplus */ + +/* C includes. */ +#include +#include + +#endif /* __cplusplus */ + +/* Internal library header includes. */ +#include "../Error_Handler/Common_Error_Handler.h" /* Used for error handling. */ +#include "../../../Core/Src/DataProcess.h" /* Used for DataProcess::removeFromVector(). */ + +/* Internal includes. */ +#include "Renderer_Basic_Functions.h" /* Functions that are used in both the renderer and overlay classes. */ +#include "Renderer_Data_Structures.h" /* Renderer base class. */ +#include "Renderer_Data_Structures_Overlay_Base_Functions.h" /* Base functions for the overlay template class defined in Renderer_Data_Structures.h. */ +#include "Renderer_Management_Functions.h" /* Static renderer functions. (Create_Renderer(), Choose_Renderer(), etc.) */ +#include "Renderer_Text_Console.h" /* Text Console Renderer. */ + +/* Define API Version. */ +#define MSYS_RENDERING_SUBSYS_API_MAJOR_VER 0 +#define MSYS_RENDERING_SUBSYS_API_MINOR_VER 0 +#define MSYS_RENDERING_SUBSYS_API_REVISION_VER 1 + +#endif /* COMMON_RENDER_SUBSYSTEM_H */ + +/* End of Renderer.h */ diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Basic_Functions.cpp b/src/Common/Src/Rendering_Subsystem/Renderer_Basic_Functions.cpp new file mode 100644 index 0000000..c44da3e --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Basic_Functions.cpp @@ -0,0 +1,268 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Basic_Functions.cpp 21/12/2013 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +#include "Renderer.h" + +int Common::Renderer::Get_Rendering_Subsystem_API_Major_Version_Number() +{ + return MSYS_RENDERING_SUBSYS_API_MAJOR_VER; +} + +int Common::Renderer::Get_Rendering_Subsystem_API_Minor_Version_Number() +{ + return MSYS_RENDERING_SUBSYS_API_MINOR_VER; +} + +int Common::Renderer::Get_Rendering_Subsystem_API_Revision_Version_Number() +{ + return MSYS_RENDERING_SUBSYS_API_REVISION_VER; +} + +bool Common::Renderer::Calculate_Offsets_From_Coordnates(const long & destXCoordOffset, const long & destYCoordOffset, const size_t & destXAxisResolution, const size_t & destYAxisResolution, + const size_t & sourceXAxisResolution, const size_t & sourceYAxisResolution, size_t & currentSourceXCoordOffset, size_t & currentDestXCoordOffset, size_t & currentDestOffset, + size_t & currentSourceOffset, size_t & nextDestEndOfLineOffset, size_t & nextSourceEndOfLineOffset) +{ + // Init vars. + bool result = false; // The result of this function. + std::stringstream errorText; // Used to construct error messages. + + /* + * Varaiable definitions: + * + * destXCoordOffset: The difference between the destionation and source x axis origin point. + * destYCoordOffset: The difference between the destionation and source y axis origin point. + * destXAxisResolution: The horazontal resolution (width) of the destionation. + * destYAxisResolution: The vertical resolution (length) of the destionation. + * sourceXAxisResolution: The horazontal resolution (width) of the source. + * sourceYAxisResolution: The vertical resolution (length) of the source. + * currentSourceOffset: The offset within the source to start at. + * currentDestOffset: The offset within the destionation to start at. + * nextDestEndOfLineOffset: The offset within the destionation where the current line will end. + * nextSourceEndOfLineOffset: The offset within the source where the current line will end. + * currentSourceXCoordOffset: The amount the Overlay::Blit() function must jump forward when moving to a new line in the source. + * currentDestXCoordOffset: The amount the Overlay::Blit() function must jump forward when moving to a new line in the destionation. + */ + + // Debug output for the arguments. + if (Common::Renderer::DebugChannel.get_log_level() >= ERROR_VERBOSE) + { + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): Arguments:\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): destXCoordOffset: " << destXCoordOffset << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): destYCoordOffset: " << destYCoordOffset << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): sourceXAxisResolution: " << sourceXAxisResolution << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): sourceYAxisResolution: " << sourceYAxisResolution << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): destXAxisResolution: " << destXAxisResolution << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): destYAxisResolution: " << destYAxisResolution << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); // Send to the debug channel. + errorText.str(std::string()); // Clear errorText. + } + + // Set all of the non-const arguments to zero. + currentSourceXCoordOffset = 0; + currentDestXCoordOffset = 0; + currentDestOffset = 0; + currentSourceOffset = 0; + nextDestEndOfLineOffset = 0; + nextSourceEndOfLineOffset = 0; + + /* Check and see if at least part of the overlay is inside the final image area. + * + * Assumptions made: + * If the offset is greater than the destionation overlay's resolution, then the source overlay is to the right of the destionation overlay's area. + * + * If the source overlay's resolution minus the offset is less than one (1), then the source overlay is to the left of the destionation overlay's area. + * + * If the offset is negative, then it must be made positive before it can be checked or compared. + * + * If the offset is negative, then it can be made positive without overflowing the buffer. (Bad assumption.) + * + * source resolution minus the offset equals the remaining amount of data on each line after jumping to that offset, if this value is less than one we don't need to do anything. + * + * All overlays start at coordnates (0,0) and overlay coordnates cannot be negative. + * + * The total size of the overlay's allocated memory buffer is the overlay's xAxisResolution times the overlay's yAxisResolution minus one (1). + * E.g. ((xAxisResolution * yAxisResolution) - 1) = . + * + * + * Horazontal check. If the below is not true, then the overlay does not overlap the final + * image, nor is the overlay on the final image. + * + * The format for this is coordnate is destionation overlay starting offset is less than the destionation overlay's resolution, and + * + * eg. ((offset to start coping data to is within destionation memory buffer) && + * (offset to start coping data to minus the source overlay's resolution is greater than zero. I.e. We have data to copy starting at that offset.)) + * + * The (is offset negative) check at the beginning of each individual condition block is used to slightly alter the + * checks performed as the negative offset requires special handling to get valid results due to the integer comparision + * treating it as an unsigned value. + * + * (I.e. The overlay is "outside" / "seperate from" the final image.) + * + * So this overlay must be skipped. + */ + if (((destXCoordOffset > -1) && ((size_t)destXCoordOffset < destXAxisResolution) && ((sourceXAxisResolution - destXCoordOffset) > 0)) || + ((destXCoordOffset <= -1) && (((size_t)(destXCoordOffset * -1)) < destXAxisResolution) && ((((size_t)(destXCoordOffset * -1)) < sourceXAxisResolution) && (sourceXAxisResolution + destXCoordOffset) > 0))) + { + + /* Check and see if at least part of the overlay is inside the final image area. + * + * Vertical check. If the below is not true, then the overlay does not overlap the final + * image, nor is the overlay on the final image. + * + * (I.e. The overlay is "outside" / "seperate from" the final image.) + * + * So this overlay must be skipped. + */ + if (((destYCoordOffset > -1) && ((size_t)destYCoordOffset < destYAxisResolution) && ((sourceYAxisResolution - destYCoordOffset) > 0)) || + ((destYCoordOffset <= -1) && (((size_t)(destYCoordOffset * -1)) < destYAxisResolution) && ((((size_t)(destYCoordOffset * -1)) < sourceYAxisResolution) && (sourceYAxisResolution + destYCoordOffset) > 0))) + { + /* + * Negative offset check. + * + * If the offset is negative then we must make it positive. Then treat that positive offset + * as if it is part of the origin point on the overlay. + * + * (I.e. We can't render something off-screen. So we have to start rendering from the overlay + * where it overlaps the final image's origin point.) + * + * If both offsets are greater than or equal to zero, (0), then we need to set the currentScreenOffset + * variable using the offsets, instead of the currentOverlayOffset variable. + */ + if (destXCoordOffset < 0) + { + // Set currentDestOffset to zero. (Where we will start at on the destionation.) + currentDestOffset = 0; + + // Set nextDestEndOfLineOffset to destXAxisResolution minus one. (destXAxisResolution = destionation horazontal resolution, lin) + nextDestEndOfLineOffset = (destXAxisResolution - 1); + + /* + * We need to check and see if the y axis (vertical axis) offset is negative. (As we won't check it later.) + * So that we can correct our starting point in the overlay. + */ + if (destYCoordOffset < 0) + { + // Both offsets are negative. So both must be made positive and multiplied together, to get our starting point in the overlayData. + currentSourceOffset = ((size_t)(((sourceXAxisResolution - 1) * ((size_t)(destXCoordOffset * (-1)))) * ((sourceYAxisResolution - 1) * ((size_t)(destYCoordOffset * (-1)))))); + } + else + { + // Only the x axis (horazontal axis) offset is negative, so only the x offset needs to be made positive, to get our starting point in the overlayData. + currentSourceOffset = ((size_t)(((sourceXAxisResolution - 1) * ((size_t)(destXCoordOffset * (-1)))) * ((sourceYAxisResolution - 1) * destYCoordOffset))); + } + + // Prime the nextSourceEndOfLineOffset value. + nextSourceEndOfLineOffset = (sourceXAxisResolution - 1); + + // The nextSourceEndOfLineOffset is greater than the currentSourceOffset. + while ((nextSourceEndOfLineOffset < currentSourceOffset) && (nextSourceEndOfLineOffset < (sourceXAxisResolution * sourceYAxisResolution))) + { + nextSourceEndOfLineOffset += sourceXAxisResolution; + } + + /* + * currentDestXCoordOffset must be set to zero here as the source's x offset is negative, + * therefore the source must shift forward not the destionation overlay. + */ + currentDestXCoordOffset = 0; + + // If the source's x offset is negative, make it positive. + currentSourceXCoordOffset = ((size_t)(destXCoordOffset * (-1))); + + // Set result. + result = true; + } + else + { + // Set the currentSourceMasterXOffset. + currentDestXCoordOffset = ((size_t)(destXCoordOffset)); + + // Check and see if the y axis offset is negative. + if (destYCoordOffset < 0) + { + // Set currentDestOffset to zero. + currentDestOffset = 0; + + // Set nextDestEndOfLineOffset to destXAxisResolution minus one. + nextDestEndOfLineOffset = (destXAxisResolution - 1); + + /* + * Because we would have gotten caught on the x axis offset check earier, we know that the x axis offset is positive if we get here. + * So only the y axis offset needs to be made positive, to get our starting point in the overlayData. + */ + currentSourceOffset = ((size_t)((sourceXAxisResolution - 1) * destXCoordOffset) * ((sourceYAxisResolution - 1) * ((size_t)destYCoordOffset * (-1)))); + } + else + { + // Both offsets are positive, so use them to set the currentDestOffset variable. + currentDestOffset = ((size_t)(((sourceXAxisResolution - 1) * ((size_t)destXCoordOffset)) * ((sourceYAxisResolution - 1) * ((size_t)destYCoordOffset)))); + + // Prime the nextDestEndOfLineOffset value. + nextDestEndOfLineOffset = (destXAxisResolution - 1); + + // The nextDestEndOfLineOffset is greater than the currentDestOffset. + while (nextDestEndOfLineOffset < currentDestOffset) + { + nextDestEndOfLineOffset += destXAxisResolution; + } + } + + // Prime the nextSourceEndOfLineOffset value. + nextSourceEndOfLineOffset = (sourceXAxisResolution - 1); + + // The nextSourceEndOfLineOffset is greater than the currentSourceOffset. + while ((nextSourceEndOfLineOffset < currentSourceOffset) && (nextSourceEndOfLineOffset < (sourceXAxisResolution * sourceYAxisResolution))) + { + nextSourceEndOfLineOffset += sourceXAxisResolution; + } + + // Set result. + result = true; + } + } + } + + // Debug output the results. + if (Common::Renderer::DebugChannel.get_log_level() >= ERROR_VERBOSE) + { + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): Results:\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): currentSourceOffset: " << currentSourceOffset << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): currentDestOffset: " << currentDestOffset << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): currentSourceXCoordOffset: " << currentSourceXCoordOffset << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): currentDestXCoordOffset: " << currentDestXCoordOffset << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): nextSourceEndOfLineOffset: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): nextDestEndOfLineOffset: " << nextDestEndOfLineOffset << ".\n"; + errorText << "Common::Renderer::Calculate_Offsets_From_Coordnates(): result: "; + if (result) + { + errorText << "True"; + } + else + { + errorText << "False"; + } + errorText << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); // Send to the debug channel. + errorText.str(std::string()); // Clear errorText. + } + + // Exit function. + return result; +} diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Basic_Functions.h b/src/Common/Src/Rendering_Subsystem/Renderer_Basic_Functions.h new file mode 100644 index 0000000..576c105 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Basic_Functions.h @@ -0,0 +1,135 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Basic_Functions.h 20/12/2013 + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef COMMON_RENDER_SUBSYSTEM_H +#error "You must include the Renderer.h header file. It will include all of the other needed headers." +#else /* COMMON_RENDER_SUBSYSTEM_H */ + +#ifndef COMMON_RENDER_BASIC_FUNCTIONS_H +#define COMMON_RENDER_BASIC_FUNCTIONS_H + +/* Include DLL_PORT.h */ +#include "../../../DLL_PORT.h" + +/* Define C functions. */ +/*! + * int Common_Renderer_Get_Rendering_Subsystem_API_Major_Version_Number() + * + * Returns the major version number of the Rendering Subsystem. + */ +MSYS_DLL_EXPORT int Common_Renderer_Get_Rendering_Subsystem_API_Major_Version_Number(); + +/*! + * int Common_Renderer_Get_Rendering_Subsystem_API_Minor_Version_Number() + * + * Returns the minor version number of the Rendering Subsystem. + */ +MSYS_DLL_EXPORT int Common_Renderer_Get_Rendering_Subsystem_API_Minor_Version_Number(); + +/*! + * int Common_Renderer_Get_Rendering_Subsystem_API_Revision_Version_Number() + * + * Returns the revision version number of the Rendering Subsystem. + */ +MSYS_DLL_EXPORT int Common_Renderer_Get_Rendering_Subsystem_API_Revision_Version_Number(); + +/*! + * bool Common_Renderer_Calculate_Offsets_From_Coordnates(const long destXCoordOffset, const long destYCoordOffset, const size_t destXAxisResolution, const size_t destYAxisResolution, + * const size_t sourceXAxisResolution, const size_t sourceYAxisResolution, size_t * currentSourceXCoordOffset, size_t * currentDestXCoordOffset, size_t * currentDestOffset, + * size_t * currentSourceOffset, size_t * nextDestEndOfLineOffset, size_t * nextSourceEndOfLineOffset) + * + * Calculates offsets for use by the overlay system and renderer. + * + * If a given coordnate is negative it will be made positive prior to calculating the offset. (This will not change the given coordnate outside of the function.) + * + * Pram: const long & destXCoordOffset, the offset from the destionation's x axis origin point where the source's x axis origin point will be placed. + * Pram: const long & destYCoordOffset, the offset from the destionation's y axis origin point where the source's y axis origin point will be placed. + * Pram: const size_t & destXAxisResolution, the resolution of the destionation's x (horazontal) axis. + * Pram: const size_t & destYAxisResolution, the resolution of the destionation's y (vertical) axis. + * Pram: const size_t & sourceXAxisResolution, the resolution of the source's x (horazontal) axis. + * Pram: const size_t & sourceYAxisResolution, the resolution of the source's y (vertical) axis. + * Pram: size_t & currentSourceXCoordOffset, The amount the Overlay::Blit() function must jump forward when moving to a new line in the source. + * Pram: size_t & currentDestXCoordOffset, The amount the Overlay::Blit() function must jump forward when moving to a new line in the destionation. + * Pram: size_t & currentDestOffset, the offset in the destionation where we will start blitting data to. + * Pram: size_t & currentSourceOffset, the offset in the source where we will start blitting data from. + * Pram: size_t & nextDestEndOfLineOffset, the offset on the destionation where the first line we blit to will end. + * Pram: size_t & nextSourceEndOfLineOffset, the offset on the source where the first line we blit from will end. + * + * Returns true if the source will show up on destionation. (The needed offsets will be in their respective arguments.) + * Returns false otherwise. (All of the non-const offset arguments will be set to zero as well.) + */ +MSYS_DLL_EXPORT bool Common_Renderer_Calculate_Offsets_From_Coordnates(const long destXCoordOffset, const long destYCoordOffset, const size_t destXAxisResolution, const size_t destYAxisResolution, + const size_t sourceXAxisResolution, const size_t sourceYAxisResolution, size_t * currentSourceXCoordOffset, size_t * currentDestXCoordOffset, size_t * currentDestOffset, + size_t * currentSourceOffset, size_t * nextDestEndOfLineOffset, size_t * nextSourceEndOfLineOffset); + +/* Check for C++ compiler. */ +#ifdef __cplusplus + +// Declare namespaces. +namespace Common +{ + namespace Renderer + { + /*! + * int Common::Renderer::Get_Rendering_Subsystem_API_Major_Version_Number() + * + * Returns the major version number of the Rendering Subsystem. + * + * (Wrapper to Common_Renderer_Get_Rendering_Subsystem_API_Major_Version_Number().) + */ + MSYS_DLL_EXPORT int Get_Rendering_Subsystem_API_Major_Version_Number(); + + /*! + * int Common::Renderer::Get_Rendering_Subsystem_API_Minor_Version_Number() + * + * Returns the minor version number of the Rendering Subsystem. + * + * (Wrapper to Common_Renderer_Get_Rendering_Subsystem_API_Minor_Version_Number().) + */ + MSYS_DLL_EXPORT int Get_Rendering_Subsystem_API_Minor_Version_Number(); + + /*! + * int Common::Renderer::Get_Rendering_Subsystem_API_Revision_Version_Number() + * + * Returns the revision version number of the Rendering Subsystem. + * + * (Wrapper to Common_Renderer_Get_Rendering_Subsystem_API_Revision_Version_Number().) + */ + MSYS_DLL_EXPORT int Get_Rendering_Subsystem_API_Revision_Version_Number(); + + /*! + * bool Common::Renderer::Calculate_Offsets_From_Coordnates(const long & destXCoordOffset, const long & destYCoordOffset, const size_t & destXAxisResolution, const size_t & destYAxisResolution, + * const size_t & sourceXAxisResolution, const size_t & sourceYAxisResolution, size_t & currentSourceXCoordOffset, size_t & currentDestXCoordOffset, size_t & currentDestOffset, + * size_t & currentSourceOffset, size_t & nextDestEndOfLineOffset, size_t & nextSourceEndOfLineOffset) + * + * (Wrapper to Common_Renderer_Calculate_Offsets_From_Coordnates().) + */ + bool Calculate_Offsets_From_Coordnates(const long & destXCoordOffset, const long & destYCoordOffset, const size_t & destXAxisResolution, const size_t & destYAxisResolution, + const size_t & sourceXAxisResolution, const size_t & sourceYAxisResolution, size_t & currentSourceXCoordOffset, size_t & currentDestXCoordOffset, size_t & currentDestOffset, + size_t & currentSourceOffset, size_t & nextDestEndOfLineOffset, size_t & nextSourceEndOfLineOffset); + }; +}; +#endif /* __cplusplus */ + +#endif /* COMMON_RENDER_BASIC_FUNCTIONS_H */ +#endif /* COMMON_RENDER_SUBSYSTEM_H */ + +/* End of Renderer_Basic_Functions.h */ diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures.cpp b/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures.cpp new file mode 100644 index 0000000..864ec62 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures.cpp @@ -0,0 +1,26 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Data_Structures.cpp 02/2/2014 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +#include "Renderer.h" + +const char * Common::Renderer::Renderer2D::Get_Last_Error() +{ + return this->lastError; +} diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures.h b/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures.h new file mode 100644 index 0000000..3b22fc2 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures.h @@ -0,0 +1,121 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Data_Structures.h 20/12/2013 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Include guard. +#ifndef COMMON_RENDER_SUBSYSTEM_H +#error "You must include the Renderer.h header file. It will include all of the other needed headers." +#endif + +#ifndef COMMON_RENDER_DATA_STRUCT_H +#define COMMON_RENDER_DATA_STRUCT_H + +// Declare namespaces. +namespace Common +{ + namespace Renderer + { + /*! + * static Panic::ERROR Common::Renderer::DebugChannel + * + * A class used to allow error / debug logging by various portions of the Rendering Subsystem. + */ + static Panic::Panic_ERROR DebugChannel; + + // Declare 2D renderer base class. + class Renderer2D + { + private: + + protected: + bool bOverlaysEnabled; // Whether or not to use the overlay vector. + size_t xScreenResolution; // Holds the horazontal (X axis) size of the window / screen. + size_t yScreenResolution; // Holds the vertical (Y axis) size of the window / screen. + size_t colorDepth; // Holds the color depth of the window / screen. + const char * lastError; // Points to a c-string that contains the human readable version of the last error that the renderer encountered. + + public: + Renderer2D() + { + bOverlaysEnabled = false; + xScreenResolution = 0; + yScreenResolution = 0; + colorDepth = 0; + lastError = NULL; + } + + // Declare virtual destructor. + virtual ~Renderer2D() + { + + } + + /* + * Wondering if the renderer subsystem should include a function to import bitmaps and characters into + * a given Renderer class. + * + * Something like a static conversion function, that calls the needed class functions for bliting. + * + * Of cource the text console renderer is special as it lacks the needed support for graphical display. + * (Hence the name TEXT console renderer.) All other renderers should not be required to act on character + * data however. (They may not support it. Also the text console renderer, does not act on bitmap requests.) + * + * Of cource the real issue is that we need generic functions that default to doing nothing. + * Templates have no way to lock them to specific types. (Yes template specialisation exists, + * but we can't say ONLY the special templates may be used. As if a type can be converted to + * another supported type, the compiler will accept it, even if our code won't, and there will + * be no warning at compile time. (Unless that default template winds up calling the #error preprocessor macro.)) + * + * The alternitve is to create overloaded functions. (But this requires all of the data types to be + * accepted by the base class, which would be impossible to maintain.) + * + * I could create a second data structure to contain different data types and pass that to the renderer objects. + * The problem then becomes how do you figure out if a given renderer supports a given data type? + */ + + // Declare basic functions. + virtual bool Change_Resolution(const size_t & xScreenResolution, const size_t & yScreenResolution, const size_t & colorDepth) = 0; + + virtual const char * Get_Last_Error(); // Returns a human readable string of the last error that the renderering backend encountered. + + virtual bool Render() = 0; + + virtual bool Blit_Text(const char * textString, const size_t & string_length, const size_t & xCoordOffset = 0, const size_t & yCoordOffset = 0, const size_t & overlayNumber = 0) = 0; + + virtual bool Blit_Bitmap(const char * bitmap, const size_t & xCoordOffset = 0, const size_t & yCoordOffset = 0, const size_t & overlayNumber = 0) = 0; + + // Declare overlay functions. + virtual bool Create_Overlay() = 0; + + virtual bool Destroy_Overlay(const size_t & overlayNumber) = 0; + + virtual void Clear_Overlay(const size_t & overlayNumber) = 0; + + void Move_Entire_Overlay(const long & xCoordOffset, const long & yCoordOffset = 0); + + bool Swap_Overlays(const size_t & firstOverlayNumber, const size_t & secondOverlayNumber = 0); + + bool Duplicate_Overlay(const size_t & overlayNumber, const size_t & duplicateOverlayNumber = 0); + }; + }; +}; + +#endif + +// End of Renderer_Data_Structures.h \ No newline at end of file diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures_Overlay_Base_Functions.cpp b/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures_Overlay_Base_Functions.cpp new file mode 100644 index 0000000..27ce25f --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures_Overlay_Base_Functions.cpp @@ -0,0 +1,22 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Data_Structures_Overlay_Base_Functions.cpp 12/3/2014 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +#include "Renderer.h" + diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures_Overlay_Base_Functions.h b/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures_Overlay_Base_Functions.h new file mode 100644 index 0000000..95e4462 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Data_Structures_Overlay_Base_Functions.h @@ -0,0 +1,2036 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Data_Structures_Overlay_Base_Functions.h 21/12/2013 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Include guard. +#ifndef COMMON_RENDER_SUBSYSTEM_H +#error "You must include the Renderer.h header file. It will include all of the other needed headers." +#endif + +#ifndef COMMON_RENDER_DATA_STRUCT_OVERLAY_BASE_FUNCTIONS_H +#define COMMON_RENDER_DATA_STRUCT_OVERLAY_BASE_FUNCTIONS_H + +namespace Common +{ + namespace Renderer + { + /* + * class Common::Renderer::Overlay + * + * This class is designed to use / manage raw bitmap data directly. + * (It can use other formatted data, by overriding the Get_Data_Element() and Set_Data_Element() functions, + * but the functions must allow access to the raw bitmap data.) + * + * The generated output can be any data type, but the data type is used strictly as a storage variable, + * and not for processing data. + * (I.e. if it is a class for some rendering backend, none of it's functions except for accessing the raw + * bitmap storage are used.) + * + * The goal of this class is to provide an overlay system, completely independant of whatever rendering backend + * is used to actually display the final result. + * + * A crude data flow diagram: + * [bitmap data] [Rendering Backend] + * \ /\ + * \ / + * \ / + * _\/ / + * [overlay system] + */ + template + class Overlay + { + private: + bool created; // Whether or not the Create_Overlay() function has run at least once. + bool transparencyBufferEnabled; // Whether or not the transparencyData memory buffer is enabled for use. + short lastError; // The last error encountered by the overlay. + short debugLevel; // What debugging level to use for this Overlay. + long masterXAxisOffset; // The X axis offset on the final image where this overlay's top left corner will be placed. + long masterYAxisOffset; // The Y axis offset on the final image where this overlay's top left corner will be placed. + size_t overlayXAxisResolution; // The X resolution of the overlay. (Normally this applies to the amount of horazontal pixels, but in this case it applies to whatever your base unit of mesurement is. (Pixel, character, etc.)) + size_t overlayYAxisResolution; // The Y resolution of the overlay. (Normally this applies to the amount of vertical pixels, but in this case it applies to whatever your base unit of mesurement is. (Pixel, character, etc.)) + T * overlayData; // The actual image data in the overlay. + T * transparencyData; // Same size as overlayData, contains transparency information for each screen element in overlayData. + void (*calcTrans)(const T& srcImgVal, const T& srcTransVal, T& destImgVal, T& destTransVal); // Pointer to the transparency calculation function in the renderer. + T blankValue; // The value used to indicate no data is at that offset. + + + protected: + + public: + Overlay() + { + created = false; + transparencyBufferEnabled = true; + lastError = COMMON_ERROR_SUCCESS; + debugLevel = ERROR_CRITICAL; + masterXAxisOffset = 0; + masterYAxisOffset = 0; + overlayXAxisResolution = 0; + overlayYAxisResolution = 0; + overlayData = NULL; + transparencyData = NULL; + calcTrans = NULL; + } + ~Overlay() + { + if (overlayData != NULL) + { + delete[] overlayData; + overlayData = NULL; + } + if (transparencyData != NULL) + { + delete[] transparencyData; + transparencyData = NULL; + } + calcTrans = NULL; + } + + /*! + * void Common::Renderer::Overlay::Set_Transparency_Function(void(*funct)(const T& srcImgVal, const T& srcTransVal, T& destImgVal, T& destTransVal)) + * + * Sets the function callback used by the overlay to calculate transparencies. + * + * NOTE: The function callback is only used if the transparency buffer is enabled. + * + * Note: This is / SHOULD BE set by the renderer. + */ + void Set_Transparency_Function(void(*funct)(const T& srcImgVal, const T& srcTransVal, T& destImgVal, T& destTransVal)) + { + // Set the function. + this->calcTrans = funct; + + // Reset lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Exit function. + return; + } + + /*! + * void Common::Renderer::Overlay::Enable_Transparency_Buffer() + * + * Enables the transparencyData memory buffer and allocates it. + * + * If the transparencyData memory buffer is already enabled this function will silently fail. + */ + void Enable_Transparency_Buffer() + { + // Init result. + bool result = false; + + // Reset lastError. + this->lastError = COMMON_SUCCESS; + + // Check and see if Create_Overlay() has been called at least once. + if (this->created) + { + // Check and see if the transparencyBufferEnabled flag is false. + if (!this->transparencyBufferEnabled) + { + // Begin try block. + try{ + // Check to see if the transparencyData buffer is allocated. + if (this->transparencyData != NULL) + { + delete[] this->transparencyData; + this->transparencyData = NULL; + } + + // Run the allocation code. + this->transparencyData = new T[(this->overlayXAxisResolution * this->overlayYAxisResolution)]; + + // Check to make sure it was allocated. + if (this->transparencyData != NULL) + { + // Blank out the transparencyData buffer. + memset(this->transparencyData, this->blankValue, (this->overlayXAxisResolution * this->overlayYAxisResolution)); + + // Set the transparencyBufferEnabled flag. + this->transparencyBufferEnabled = true; + + // Set the result flag. + result = true; + } + else + { + // Could not allocate memory for transparencyData memory buffer. + this->lastError = RENDERER_UNABLE_TO_ALLOC_TD_BUF; + } + } + catch(...) + { + // Check to see if the transparencyData buffer is allocated. + if (this->transparencyData != NULL) + { + delete[] this->transparencyData; + this->transparencyData = NULL; + } + + // Set the transparencyBufferEnabled flag to false. + this->transparencyBufferEnabled = false; + + // Exception thrown. + this->lastError = RENDERER_MEM_BUF_ALLOC_EXCEPTION; + } + } + } + + // Return the result. + return result; + } + + /*! + * void Common::Renderer::Overlay::Disable_Transparency_Buffer() + * + * Disables the transparencyData memory buffer, and deallocates it, if it has been allocated. + * + * If the transparencyData memory buffer is already disabled this function will silently fail. + */ + void Disable_Transparency_Buffer() + { + // Reset lastError. + this->lastError = COMMON_SUCCESS; + + // Check and see if the transparencyData buffer is enabled and allocated. + if (this->transparencyBufferEnabled) + { + // Set the transparencyBufferEnabled flag to false. + this->transparencyBufferEnabled = false; + + // Deallocate the transparencyData buffer. + if (this->transparencyData != NULL) + { + delete[] this->transparencyData; + this->transparencyData = NULL; + } + } + + // Exit function. + return; + } + + /*! + * void Common::Renderer::Overlay::is_Transparency_Buffer_Enabled() const + * + * Returns true if the overlay's transparencyData memory buffer is enabled, and allocated. + * Returns false otherwise. + */ + bool is_Transparency_Buffer_Enabled() const + { + // Check and see if the transparencyData buffer is enabled and allocated. + return ((this->transparencyBufferEnabled) && (this->transparencyData != NULL)); + } + + /*! + * const char * Common::Renderer::Overlay::Get_Last_Error() const + * + * Returns a human readable string of the last error that the overlay encountered. + */ + const char * Get_Last_Error() const + { + // Init result. + const char * result = NULL; // Default is unknown error. + + // Guard against no error. + if (this->lastError != 0) + { + /* Call error lookup function call. */ + result = Common_Get_Error_Message(this->lastError); + } + else + { + // Result is no error. + result = Common_Get_Error_Message(COMMON_ERROR_SUCCESS); + } + + // Return the result. + return result; + } + + /*! + * short Common::Renderer::Overlay::Get_Last_Error_Code() const + * + * Returns the last error code encountered by the overlay. + */ + short Get_Last_Error_Code() const + { + // Return the last error we encountered. + return this->lastError; + } + + /*! + * void Common::Renderer::Overlay::Clear_Last_Error() + * + * Clears the last error encountered variable. + */ + void Clear_Last_Error() + { + // Reset lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Exit function. + return; + } + + /*! + * short Common::Renderer::Overlay::Get_Debug_Level() const + * + * Returns the error / debug logging level for an overlay. + */ + short Get_Debug_Level() const + { + // Return the current error / debug logging level. + return this->debugLevel; + } + + /*! + * void Common::Renderer::Overlay::Set_Debug_Level(const short & level) + * + * Sets the error / debug logging level for an overlay. + */ + void Set_Debug_Level(const short & level) + { + // Set the error / debug logging level. + this->debugLevel = level; + Common::Renderer::DebugChannel.change_log_level(this->debugLevel); + + // Exit function. + return; + } + + /*! + * bool Common::Renderer::Overlay::is_Overlay_Created() const + * + * Returns whether or not the Create_Overlay() function has been called at least once for this overlay. + */ + bool is_Overlay_Created() const + { + // Return the created flag. + return this->created; + } + + /*! + * bool Common::Renderer::Overlay::Create_Overlay(const long & xOffset, const long & yOffset, const size_t & xResolution, const size_t & yResolution) + * + * Sets up the overlay for the user. + * + * Returns true if the overlay is set up properly and it's buffers were allocated. + * Returns false otherwise. + */ + bool Create_Overlay(const T & newBlankValue, const long & xOffset, const long & yOffset, const size_t & xResolution, const size_t & yResolution) + { + // Init result. + bool result = false; // The result of this function. + + // Reset this->lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Check for invalid resolutions. + if ((xResolution > 0) && (yResolution > 0)) + { + // Check and see if the overlayData and transparencyData buffers are allocated. + if (this->overlayData != NULL) + { + // Delete the old buffer. + delete[] this->overlayData; + this->overlayData = NULL; + } + if (this->transparencyData != NULL) + { + // Delete the old buffer. + delete[] this->transparencyData; + this->transparencyData = NULL; + } + + // Reset the other data members. + this->masterXAxisOffset = 0; + this->masterYAxisOffset = 0; + this->overlayXAxisResolution = 0; + this->overlayYAxisResolution = 0; + + // Begin try block. + try{ + // Set the blank value. + this->blankValue = newBlankValue; + + // Allocate new memory. (Array length is the product of the xScreenResolution and yScreenResolution arguments.) + this->overlayData = new T[(xResolution * yResolution)]; + + // Check and see if the transparencyData buffer is enabled. + if (this->transparencyBufferEnabled) + { + // Allocate the transparencyData buffer. + this->transparencyData = new T[(xResolution * yResolution)]; + } + + // Make sure that array was allocated. + if (this->overlayData != NULL) + { + // Fill the array with the blank value. + for (size_t x = 0; x < (xResolution * yResolution); x++) + { + this->overlayData[x] = this->blankValue; + } + + // Set data values in the overlay. + this->masterXAxisOffset = xOffset; + this->masterYAxisOffset = yOffset; + this->overlayXAxisResolution = xResolution; + this->overlayYAxisResolution = yResolution; + + // Check and see if the transparencyData buffer is enabled. + if (this->transparencyBufferEnabled) + { + // Make sure the transparencyData buffer was allocated. + if (this->transparencyData != NULL) + { + // Fill the array with the blank value. + for (size_t x = 0; x < (xResolution * yResolution); x++) + { + this->transparencyData[x] = this->blankValue; + } + + // Set created. + this->created = true; + + // Set result. + result = true; + } + else + { + // Release memory for overlayData. + if (this->overlayData != NULL) + { + delete[] this->overlayData; + this->overlayData = NULL; + } + + // Could not allocate memory for overlay transparency data buffer. + this->lastError = RENDERER_ERROR_UNABLE_TO_ALLOC_TD_BUF; + } + } + else + { + // Set created. + this->created = true; + + // Set result. + result = true; + } + } + else + { + // Could not allocate memory for overlay image buffer. + this->lastError = RENDERER_ERROR_UNABLE_TO_ALLOC_OI_BUF; + } + } + catch(...) + { + // An exception was thrown while attempting to create the overlay. + this->lastError = RENDERER_ERROR_MEM_BUF_ALLOC_EXCEPTION; + + // Check to see the overlay data image buffer was allocated. + if (this->overlayData != NULL) + { + // Release the overlay data image buffer. + delete[] this->overlayData; + this->overlayData = NULL; + } + + // Check to see the transparency data buffer was allocated. + if (this->transparencyData != NULL) + { + // Release the transparency data buffer. + delete[] this->transparencyData; + this->transparencyData = NULL; + } + + // Blank the other variables. + this->overlayXAxisResolution = 0; + this->overlayYAxisResolution = 0; + this->masterXAxisOffset = 0; + this->masterYAxisOffset = 0; + } + } + + // Exit function. + return result; + } + + /*! + * template + * void Common::Renderer::Overlay::Blit(const T * dataArray, const long & startingXoffset, const long & startingYoffset, const size_t & dataXResolution, const size_t & dataYResolution, const bool & lineWrap) + * + * Copies data from an array of the same data type as the overlay's template to the destionation overlay's data buffer. + * + * Pram: const T * dataArray, The array to copy image data from. Must be the same data type as the Overlay's templated data type. + * Pram: const T * transparencyArray, The array to copy transparency data from. Must be the same data type as the Overlay's templated data type. + * Pram: const long & startingXoffset, the starting x axis offset in the overlay where data from the given array will be copied to. + * Pram: const long & startingYoffset, the starting y axis offset in the overlay where data from the given array will be copied to. + * Pram: const size_t & dataXResolution, the horazontal resolution of the dataArray. (Multiplied with dataYResolution to get the dataArray size.) + * Pram: const size_t & dataYResolution, the vertical resolution of the dataArray. (Multiplied with dataXResolution to get the dataArray size.) + * Pram: const bool & lineWrap, if true, any remaining data on the current dataArray line will be blited to the next destionation overlay line if the end of + * the current destionation overlay line is reached. Otherwise any remaining data on the current dataArray line not be blited onto the destionation overlay. Defaults to false. + */ + void Blit(const T * dataArray, const T * transparencyArray, const long & startingXoffset, const long & startingYoffset, const size_t & dataXAxisResolution, const size_t & dataYAxisResolution, const bool & lineWrap = false) + { + // Init vars. + size_t currentSourceOffset = 0; // The current offset in the data array. + size_t currentDestOffset = 0; // The current offset in the overlay. + size_t currentSourceXCoordOffset = 0; // The offset that must be added to the start of a new line in the source data array to align the biltted data. + size_t currentDestXCoordOffset = 0; // The offset that must be added to the start of a new line in the overlay to align the biltted data. + size_t nextSourceEndOfLineOffset = 0; // The offset where the end of the current line in the data array is located. + size_t nextDestEndOfLineOffset = 0; // The offset where the end of the current line in the overlay is located. + std::stringstream errorText; // Holds error messages. + +#ifndef _NDEBUG + // Debug the arguments. + if (this->debugLevel >= ERROR_INFO) + { + errorText << "VERBOSE Channel: Overlay::Blit(dataArray): Arguments:\n"; + errorText << "Overlay::Blit(dataArray): dataArray: " << &dataArray << ".\n"; + errorText << "Overlay::Blit(dataArray): transparencyArray: " << &transparencyArray << ".\n"; + errorText << "Overlay::Blit(dataArray): startingXoffset: " << startingXoffset << ".\n"; + errorText << "Overlay::Blit(dataArray): startingYoffset: " << startingYoffset << ".\n"; + errorText << "Overlay::Blit(dataArray): dataXAxisResolution: " << dataXAxisResolution << ".\n"; + errorText << "Overlay::Blit(dataArray): dataYAxisResolution: " << dataYAxisResolution << ".\n"; + errorText << "Overlay::Blit(dataArray): lineWrap: "; + if (lineWrap) + { + errorText << "True"; + } + else + { + errorText << "False"; + } + errorText << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_INFO); + errorText.str(std::string()); + } + + // Debug the object. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "VERBOSE Channel: Overlay::Blit(dataArray): Object data members:\n"; + errorText << "Overlay::Blit(dataArray): masterXAxisOffset: " << this->masterXAxisOffset << ".\n"; + errorText << "Overlay::Blit(dataArray): masterYAxisOffset: " << this->masterYAxisOffset << ".\n"; + errorText << "Overlay::Blit(dataArray): overlayXAxisResolution: " << this->overlayXAxisResolution << ".\n"; + errorText << "Overlay::Blit(dataArray): overlayYAxisResolution: " << this->overlayYAxisResolution << ".\n"; + errorText << "Overlay::Blit(dataArray): overlayData: " << std::hex << &overlayData << std::dec << ".\n"; + errorText << "Overlay::Blit(dataArray): transparencyData: " << std::hex << &transparencyData << std::dec << ".\n"; + errorText << "Overlay::Blit(dataArray): calcTrans: " << std::hex << calcTrans << std::dec << ".\n"; + errorText << "Overlay::Blit(dataArray): lastError: " << this->lastError << ".\n"; + errorText << "Overlay::Blit(dataArray): created: "; + if (this->created) + { + errorText << "True"; + } + else + { + errorText << "False"; + } + errorText << ".\n"; + errorText << "Overlay::Blit(dataArray): transparencyBufferEnabled: "; + if (this->transparencyBufferEnabled) + { + errorText << "True"; + } + else + { + errorText << "False"; + } + errorText << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + // Make sure we were set up properly. + if ((this->created) && ((this->overlayXAxisResolution > 0) && (this->overlayYAxisResolution > 0)) && (this->overlayData != NULL)) + { + // Check for valid pointer. + if (dataArray != NULL) + { + // Make sure the given resolution is not zero. + if ((dataXAxisResolution > 0) && (dataYAxisResolution > 0)) + { + // Calculate the starting offsets. + if (Common::Renderer::Calculate_Offsets_From_Coordnates(startingXoffset, startingYoffset, this->overlayXAxisResolution, this->overlayYAxisResolution, + dataXAxisResolution, dataYAxisResolution, currentSourceXCoordOffset, currentDestXCoordOffset, currentDestOffset, + currentSourceOffset, nextDestEndOfLineOffset, nextSourceEndOfLineOffset)) + { + // Begin blitting loop. + for (; (currentSourceOffset < (dataXAxisResolution * dataYAxisResolution)) && (currentDestOffset < (this->overlayXAxisResolution * this->overlayYAxisResolution)); currentSourceOffset++) + { + /* + * Note the expected behavior of this process is the following: + * + * This copies data from the current offset in the overlay data to the current offset in the finalImage buffer. + * the masterXAxisOffset and masterYAxisOffset values are used to determine where to start copying data to with the + * for loop's currentOverlayOffset value acting as a counter of the amount of data copied. + * + * The main loop allows iterating through each overlay starting at the last overlay and proceeding until we copy the first overlay. + * + * All x values in the main for loop are decremented by one because the last element in a vector is: n - 1, where n is equal to the current amount of elements in the vector. + * + * The reason for this is because the overlay system renders each overlay in reverse. + * (I.e. For any given overlay that overlay has a lower priority (with regards to visibility) than those that come before it in the overlay system's vector, + * and that overlay has a higher priority than those that come after it in the overlay system's vector.) + * + * Which means something on a lower priority overlay, at the same position on screen as something at a higher priority overlay, will never be shown on screen. + * (I.e. The higher priority overlay will "cover up" anything under it.) + * + * To achive this effect, the main for loop must start with the last overlay in the vector and finish with the first overlay in the vector. + * (Due to the way for loops work (when dealing with decrementing unsigned integers), (at least in gcc), the x value in the main for loop + * must start at one past the last the last element in the vector and finish when it reaches zero. (Otherwise it results in an indefinite + * loop because the x value will wrap around to the highest positive value it can hold.) Which nessatates decrementing x by one to access + * the correct element. (Unless you'd like that segfault to happen..... :P)) + */ + + /* + * Issue: In order for an image to "scroll" off-screen, we need to be able to depeict an overlay that is not fully on the screen. + * + * Which means we need to do two things: + * + * 1. When we reach the end of the current line on the screen, we need to output a newline character. + * (This prevents us from distoring the screen via a wrap-around effect, if the set resolution is not correct. + * (I.e. Otherwise this would require an image editor to (realign / cut-and-paste different lines on) the + * image so it could be viewed properly.)) + * + * 2. We need to detect where in an overlay a newline starts at, so that when we do reach the end of a + * line on the screen, we can skip the data that should not be shown. + * (This prevents us from displaying the image incorrectly making it un-recognizable.) + * + * 3. We need to continue at the correct horazontal offset on the next line. + * (This prevents us from splitting up the image on different sides of the screen.) + * + * How to do this: + * 1. Compute the current screen offset, and if we are at the end of the current line, jump to the next one, + * fix the horazontal offset in the screen image, and skip to the next line in the overlay data. + * (I.e. currentScreenOffset = ((< Compute offset for start of next screen line >) + overlay.MasterXOffset); + * currentOverlayOffset += (< Compute amount of data to skip to next line >); + * ) + * + * 2. Detect newlines in the for loop, if one is found fix the current offset in the screen image. (Jump to the next line + * and fix the horazontal offset. (I.e. currentScreenOffset = ((< Compute offset for start of next screen line >) + overlay.MasterXOffset);) + * + * 3. Fix the current offset in the screen image. (Jump to the next line + * and fix the horazontal offset. (I.e. currentScreenOffset = ((< Compute offset for start of next screen line >) + overlay.MasterXOffset);) + * + */ + + // Check and see if the transparencyData buffer is enabled. + if (this->transparencyBufferEnabled) + { + // Check and see if the transparency function is set and use it if it is. Otherwise do a raw copy, and expect the renderer does something with it. + if (this->calcTrans != NULL) + { + // Call the transparency function. + (*calcTrans)(dataArray[currentSourceOffset], transparencyArray[currentSourceOffset], this->overlayData[currentDestOffset], this->transparencyData[currentDestOffset]); + } + else + { + // Copy the image data and continue. + this->overlayData[currentDestOffset] = dataArray[(currentSourceOffset)]; + + // Get the transparency value. + this->transparencyData[currentDestOffset] = transparencyArray[(currentSourceOffset)]; + } +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(dataArray): overlayData[" << currentDestOffset << "] = " << this->overlayData[currentDestOffset] << "\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): transparencyData[" << currentDestOffset << "] = " << this->transparencyData[currentDestOffset] << "\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + } + else + { + // Check for a blank value and output the data if it is not one. + if (dataArray[(currentSourceOffset)] != this->blankValue) + { + // Not a blank value, so copy the data and continue. + this->overlayData[currentDestOffset] = dataArray[(currentSourceOffset)]; +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(dataArray): overlayData[" << currentDestOffset << "] = " << this->overlayData[currentDestOffset] << "\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): transparencyData[" << currentDestOffset << "] = " << this->transparencyData[currentDestOffset] << "\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + } + } + + // Check to see if we have reached the end of the current line in the overlay data. (current position % (overlay horazontal resolution - 1)) will equal 0 if we are at the end of that line. + if (currentSourceOffset == nextSourceEndOfLineOffset) + { +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(dataArray): currentSourceOffset before end of data line jump: " << currentSourceOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): currentDestOffset before end of data line jump: " << currentDestOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): nextSourceEndOfLineOffset before end of data line jump: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): nextDestEndOfLineOffset before end of data line jump: " << nextDestEndOfLineOffset << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + // Check and see if we have already moved to the next line in the final image. + if (currentDestOffset < nextDestEndOfLineOffset) + { + // We have hit a newline in the overlay data, so we need to jump to the next line and offset in the final image. (One is added at the end of the loop, no need to add it here.) + currentDestOffset += ((nextDestEndOfLineOffset - currentDestOffset) + currentDestXCoordOffset); + + // Calculate new nextDestEndOfLineOffset. + nextDestEndOfLineOffset += this->overlayXAxisResolution; + } + else + { + // Check if we are at the end of the current Screen line. + if (currentDestOffset == nextDestEndOfLineOffset) + { + // Jump to the correct offset. (One is added at the end of the loop, no need to add it here.) + currentDestOffset += currentDestXCoordOffset; + + // Calculate new nextDestEndOfLineOffset. + nextDestEndOfLineOffset += this->overlayXAxisResolution; + } + } + + // Calculate the nextSourceEndOfLineOffset. + nextSourceEndOfLineOffset += dataXAxisResolution; + + // Jump the current source offset forward by the offset amount. + currentSourceOffset += currentSourceXCoordOffset; +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(dataArray): currentSourceOffset after end of data line jump: " << currentSourceOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): currentDestOffset after end of data line jump: " << currentDestOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): nextSourceEndOfLineOffset after end of data line jump: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): nextDestEndOfLineOffset after end of data line jump: " << nextDestEndOfLineOffset << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + // DO NOT OUTPUT THE NEWLINE CHARACTER IN THE OVERLAY DATA! + } + else + { + // Check and see if we are at the end of the current line on the overlay. (current position % (screen horazontal resolution - 1)) will equal 0 if we are at the end of that line. + if (currentDestOffset == nextDestEndOfLineOffset) + { +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(dataArray): currentSourceOffset before end of overlay line jump: " << currentSourceOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): currentDestOffset before end of overlay line jump: " << currentDestOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): nextSourceEndOfLineOffset before end of overlay line jump: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): nextDestEndOfLineOffset before end of overlay line jump: " << nextDestEndOfLineOffset << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + // We've reached the end of the current line on the overlay, calculate new nextDestEndOfLineOffset. + nextDestEndOfLineOffset += this->overlayXAxisResolution; + + // Because we reached the end of the overlay line, we need to skip forward until we reach the next line in the data array. + // First, check and see if we have already reached the next line in the data array. + if (currentSourceOffset < nextSourceEndOfLineOffset) + { + // Only jump forward if lineWrap is false. + if (!lineWrap) + { + /* + * Determine how much more to add to the current data offset to reach the next line in the data array. + * + * This is done by subtracting the currentSourceOffset from the nextSourceEndOfLineOffset. + * + * The result of that is the amount of elements in the array we need to add to the current offset, to reach the next line + * in the data array. + */ + currentSourceOffset += (nextSourceEndOfLineOffset - currentSourceOffset); + + // Calculate new nextSourceEndOfLineOffset. + nextSourceEndOfLineOffset += dataXAxisResolution; + } + } + else + { + // Check to see if we are at the end of the current line in the data array. + if (currentSourceOffset == nextSourceEndOfLineOffset) + { + // Calculate new nextSourceEndOfLineOffset. + nextSourceEndOfLineOffset += dataXAxisResolution; + } + } + + // Jump the current source offset forward by the offset amount. + currentSourceOffset += currentSourceXCoordOffset; +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(dataArray): currentSourceOffset after end of overlay line jump: " << currentSourceOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): currentDestOffset after end of overlay line jump: " << currentDestOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): nextSourceEndOfLineOffset after end of overlay line jump: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(dataArray): nextDestEndOfLineOffset after end of overlay line jump: " << nextDestEndOfLineOffset << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + } + } + + // Increment the currentDestOffset. + currentDestOffset++; + } + } + } +#ifndef _NDEBUG + else + { + // Only output an error message if we are actually outputing error messages. + if (this->debugLevel >= ERROR_WARNING) + { + errorText << "DEBUG Channel: Overlay::Blit(dataArray): Invalid dataArray resolution: " << dataXAxisResolution << 'x' << dataYAxisResolution << ". Resolution must be greater than zero, aborting with no changes to overlay data.\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_WARNING); + errorText.str(std::string()); + } + } +#endif + } + } + +#ifndef _NDEBUG + else + { + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + // We were not setup properly, abort. + errorText << "DEBUG Channel: Overlay::Blit(dataArray): Destination overlay is invalid, memory buffer and / or dimentions not setup correctly."; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } + } +#endif + + // Reset this->lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Exit function. + return; + } + + /*! + * template + * void Common::Renderer::Overlay::Blit(const Overlay & data, const long & startingXoffset, const long & startingYoffset, const bool & lineWrap) + * + * Copies data from an overlay of the same data type as the overlay's template data type to the overlay's data buffer. + * + * Pram: const Overlay & data, The overlay to copy data from. + * Pram: const long & startingXoffset, the starting x axis offset in the overlay where data from the given overlay will be copied to. Defaults to the overlay origin point. + * Pram: const long & startingYoffset, the starting y axis offset in the overlay where data from the given overlay will be copied to. Defaults to the overlay origin point. + * Pram: const bool & lineWrap, if true, any remaining data on the current source overlay line will be blited to the next memory buffer line if the end of + * the current memory buffer line is reached. Otherwise any remaining data on the current source overlay line will not be blited into the destionation overlay's buffer. Defaults to false. + */ + void Blit(const Overlay & data, const long & startingXoffset = 0, const long & startingYoffset = 0, const bool & lineWrap = false) + { + // Init vars. + size_t currentSourceOffset = 0; // The current offset in the source overlay. + size_t currentDestOffset = 0; // The current offset in our memory buffer. + size_t currentSourceXCoordOffset = 0; // The offset that must be added to the start of a new line in the source overlay to align the biltted data. + size_t currentDestXCoordOffset = 0; // The offset that must be added to the start of a new line in the overlay to align the biltted data. + size_t nextSourceEndOfLineOffset = 0; // The offset where the end of the current line in the source overlay is located. + size_t nextDestEndOfLineOffset = 0; // The offset where the end of the current line in our memory buffer is located. + size_t dataXAxisResolution = 0; // The source overlay's x axis resolution. + size_t dataYAxisResolution = 0; // The source overlay's y axis resolution. + const T * dataArray = NULL; // Pointer to the source overlay's image data buffer. + const T * transparencyArray = NULL; // Pointer to the source overlay's transparency data buffer. + T sourceBlankValue = data.Get_Blank_Value(); // Holds a copy of the source overlay's blank value. + std::stringstream errorText; // Holds error messages. + + // Get the source overlay's resolution. + data.Get_Resolution(dataXAxisResolution, dataYAxisResolution); +#ifndef _NDEBUG + // Debug the arguments. + if (this->debugLevel >= ERROR_INFO) + { + errorText << "VERBOSE Channel: Overlay::Blit(Overlay): Arguments:\n"; + errorText << "Overlay::Blit(Overlay): data: " << &data << ".\n"; + errorText << "Overlay::Blit(Overlay): startingXoffset: " << startingXoffset << ".\n"; + errorText << "Overlay::Blit(Overlay): startingYoffset: " << startingYoffset << ".\n"; + errorText << "Overlay::Blit(Overlay): dataXAxisResolution: " << dataXAxisResolution << ".\n"; + errorText << "Overlay::Blit(Overlay): dataYAxisResolution: " << dataYAxisResolution << ".\n"; + errorText << "Overlay::Blit(Overlay): lineWrap: "; + if (lineWrap) + { + errorText << "True"; + } + else + { + errorText << "False"; + } + errorText << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_INFO); + errorText.str(std::string()); + } + + // Debug the object. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "VERBOSE Channel: Overlay::Blit(Overlay): Object data members:\n"; + errorText << "Overlay::Blit(Overlay): masterXAxisOffset: " << this->masterXAxisOffset << ".\n"; + errorText << "Overlay::Blit(Overlay): masterYAxisOffset: " << this->masterYAxisOffset << ".\n"; + errorText << "Overlay::Blit(Overlay): overlayXAxisResolution: " << this->overlayXAxisResolution << ".\n"; + errorText << "Overlay::Blit(Overlay): overlayYAxisResolution: " << this->overlayYAxisResolution << ".\n"; + errorText << "Overlay::Blit(Overlay): overlayData: " << std::hex << &overlayData << std::dec << ".\n"; + errorText << "Overlay::Blit(Overlay): transparencyData: " << std::hex << &transparencyData << std::dec << ".\n"; + errorText << "Overlay::Blit(Overlay): calcTrans: " << std::hex << calcTrans << std::dec << ".\n"; + errorText << "Overlay::Blit(Overlay): lastError: " << this->lastError << ".\n"; + errorText << "Overlay::Blit(Overlay): created: "; + if (this->created) + { + errorText << "True"; + } + else + { + errorText << "False"; + } + errorText << ".\n"; + errorText << "Overlay::Blit(Overlay): Source Overlay transparencyBufferEnabled: "; + if (data.is_Transparency_Buffer_Enabled()) + { + errorText << "True"; + } + else + { + errorText << "False"; + } + errorText << ".\n"; + errorText << "Overlay::Blit(Overlay): Destination Overlay transparencyBufferEnabled: "; + if (this->transparencyBufferEnabled) + { + errorText << "True"; + } + else + { + errorText << "False"; + } + errorText << ".\n"; + if (data.is_Transparency_Buffer_Enabled() != this->transparencyBufferEnabled) + { + errorText << "WARNING: Attempting to blit overlays when one overlay uses a transparency data buffer and the other does not.\n"; + errorText << "The result will probably NOT be what you were expecting.\n"; + } + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + // Make sure we were set up properly. + if ((this->created) && ((this->overlayXAxisResolution > 0) && (this->overlayYAxisResolution > 0)) && (this->overlayData != NULL)) + { + // Make sure that the source overlay's resolution is valid. + if ((dataXAxisResolution > 0) && (dataYAxisResolution > 0)) + { + // Get the source overlay's data pointers. + dataArray = data.Get_Overlay_Data(); + + // Get the transparencyArray pointer if transparencies are enabled. + if (data.is_Transparency_Buffer_Enabled()) + { + transparencyArray = data.Get_Transparency_Data(); + } + + // Make sure that the pointers are valid. + if ((dataArray != NULL) && (((data.is_Transparency_Buffer_Enabled()) && (transparencyArray != NULL)) || ((!data.is_Transparency_Buffer_Enabled()) && (transparencyArray == NULL)))) + { + // Calculate the starting offsets. + if (Common::Renderer::Calculate_Offsets_From_Coordnates(startingXoffset, startingYoffset, this->overlayXAxisResolution, this->overlayYAxisResolution, + dataXAxisResolution, dataYAxisResolution, currentSourceXCoordOffset, currentDestXCoordOffset, currentDestOffset, + currentSourceOffset, nextDestEndOfLineOffset, nextSourceEndOfLineOffset)) + { + // Begin blitting loop. + for (; (currentSourceOffset < (dataXAxisResolution * dataYAxisResolution)) && (currentDestOffset < (this->overlayXAxisResolution * this->overlayYAxisResolution)); currentSourceOffset++) + { + /* + * Note the expected behavior of this process is the following: + * + * This copies data from the current offset in the overlay data to the current offset in the finalImage buffer. + * the masterXAxisOffset and masterYAxisOffset values are used to determine where to start copying data to with the + * for loop's currentOverlayOffset value acting as a counter of the amount of data copied. + * + * The main loop allows iterating through each overlay starting at the last overlay and proceeding until we copy the first overlay. + * + * All x values in the main for loop are decremented by one because the last element in a vector is: n - 1, where n is equal to the current amount of elements in the vector. + * + * The reason for this is because the overlay system renders each overlay in reverse. + * (I.e. For any given overlay that overlay has a lower priority (with regards to visibility) than those that come before it in the overlay system's vector, + * and that overlay has a higher priority than those that come after it in the overlay system's vector.) + * + * Which means something on a lower priority overlay, at the same position on screen as something at a higher priority overlay, will never be shown on screen. + * (I.e. The higher priority overlay will "cover up" anything under it.) + * + * To achive this effect, the main for loop must start with the last overlay in the vector and finish with the first overlay in the vector. + * (Due to the way for loops work (when dealing with decrementing unsigned integers), (at least in gcc), the x value in the main for loop + * must start at one past the last the last element in the vector and finish when it reaches zero. (Otherwise it results in an indefinite + * loop because the x value will wrap around to the highest positive value it can hold.) Which nessatates decrementing x by one to access + * the correct element. (Unless you'd like that segfault to happen..... :P)) + */ + + /* + * Issue: In order for an image to "scroll" off-screen, we need to be able to depeict an overlay that is not fully on the screen. + * + * Which means we need to do two things: + * + * 1. When we reach the end of the current line on the screen, we need to output a newline character. + * (This prevents us from distoring the screen via a wrap-around effect, if the set resolution is not correct. + * (I.e. Otherwise this would require an image editor to (realign / cut-and-paste different lines on) the + * image so it could be viewed properly.)) + * + * 2. We need to detect where in an overlay a newline starts at, so that when we do reach the end of a + * line on the screen, we can skip the data that should not be shown. + * (This prevents us from displaying the image incorrectly making it un-recognizable.) + * + * 3. We need to continue at the correct horazontal offset on the next line. + * (This prevents us from splitting up the image on different sides of the screen.) + * + * How to do this: + * 1. Compute the current screen offset, and if we are at the end of the current line, jump to the next one, + * fix the horazontal offset in the screen image, and skip to the next line in the overlay data. + * (I.e. currentScreenOffset = ((< Compute offset for start of next screen line >) + overlay.MasterXOffset); + * currentOverlayOffset += (< Compute amount of data to skip to next line >); + * ) + * + * 2. Detect newlines in the for loop, if one is found fix the current offset in the screen image. (Jump to the next line + * and fix the horazontal offset. (I.e. currentScreenOffset = ((< Compute offset for start of next screen line >) + overlay.MasterXOffset);) + * + * 3. Fix the current offset in the screen image. (Jump to the next line + * and fix the horazontal offset. (I.e. currentScreenOffset = ((< Compute offset for start of next screen line >) + overlay.MasterXOffset);) + * + */ + + // Check and see if the transparencyData buffers are enabled. + if ((this->transparencyBufferEnabled) && (data.is_Transparency_Buffer_Enabled())) + { + // Check and see if the transparency function is set and use it if it is. Otherwise do a raw copy, and expect the renderer does something with it. + if (this->calcTrans != NULL) + { + // Call the transparency function. + (*calcTrans)(dataArray[currentSourceOffset], transparencyArray[currentSourceOffset], this->overlayData[currentDestOffset], this->transparencyData[currentDestOffset]); + } + else + { + // Copy the image data and continue. + this->overlayData[currentDestOffset] = dataArray[(currentSourceOffset)]; + + // Get the transparency value. + this->transparencyData[currentDestOffset] = transparencyArray[(currentSourceOffset)]; + } +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(Overlay): overlayData[" << currentDestOffset << "] = " << this->overlayData[currentDestOffset] << "\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): transparencyData[" << currentDestOffset << "] = " << this->transparencyData[currentDestOffset] << "\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + } + else + { + // Check for a blank value and output the data if it is not one. + if (dataArray[(currentSourceOffset)] != this->blankValue) + { + // Not a blank value, so copy the data and continue. + this->overlayData[currentDestOffset] = dataArray[(currentSourceOffset)]; +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(Overlay): overlayData[" << currentDestOffset << "] = " << this->overlayData[currentDestOffset] << "\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + } + } + + // Check to see if we have reached the end of the current line in the overlay data. (current position % (overlay horazontal resolution - 1)) will equal 0 if we are at the end of that line. + if (currentSourceOffset == nextSourceEndOfLineOffset) + { +#ifndef _NDEBUG + // Debugging output for the calculations. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(Overlay): currentSourceOffset before end of data line jump: " << currentSourceOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): currentDestOffset before end of data line jump: " << currentDestOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): nextSourceEndOfLineOffset before end of data line jump: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): nextDestEndOfLineOffset before end of data line jump: " << nextDestEndOfLineOffset << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + // Check and see if we have already moved to the next line in the final image. + if (currentDestOffset < nextDestEndOfLineOffset) + { + // We have hit a newline in the overlay data, so we need to jump to the next line and offset in the final image. (One is added at the end of the loop, no need to add it here.) + currentDestOffset += ((nextDestEndOfLineOffset - currentDestOffset) + currentDestXCoordOffset); + + // Calculate new nextDestEndOfLineOffset. + nextDestEndOfLineOffset += this->overlayXAxisResolution; + } + else + { + // Check if we are at the end of the current Screen line. + if (currentDestOffset == nextDestEndOfLineOffset) + { + // Jump to the correct offset. (One is added at the end of the loop, no need to add it here.) + currentDestOffset += currentDestXCoordOffset; + + // Calculate new nextDestEndOfLineOffset. + nextDestEndOfLineOffset += this->overlayXAxisResolution; + } + } + + // Calculate the nextSourceEndOfLineOffset. + nextSourceEndOfLineOffset += dataXAxisResolution; + + // Jump the current source offset forward by the offset amount. + currentSourceOffset += currentSourceXCoordOffset; + +#ifndef _NDEBUG + // Debugging output for the calculations. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(Overlay): currentSourceOffset after end of data line jump: " << currentSourceOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): currentDestOffset after end of data line jump: " << currentDestOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): nextSourceEndOfLineOffset after end of data line jump: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): nextDestEndOfLineOffset after end of data line jump: " << nextDestEndOfLineOffset << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + // DO NOT OUTPUT THE NEWLINE CHARACTER IN THE OVERLAY DATA! + } + else + { + // Check and see if we are at the end of the current line on the overlay. (current position % (screen horazontal resolution - 1)) will equal 0 if we are at the end of that line. + if (currentDestOffset == nextDestEndOfLineOffset) + { +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(Overlay): currentSourceOffset before end of overlay line jump: " << currentSourceOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): currentDestOffset before end of overlay line jump: " << currentDestOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): nextSourceEndOfLineOffset before end of overlay line jump: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): nextDestEndOfLineOffset before end of overlay line jump: " << nextDestEndOfLineOffset << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + // Jump currentDestOffset forward by the data's masterXAxisOffset. (One is added at the end of the loop, no need to add it here.) + currentDestOffset += currentDestXCoordOffset; + + // Calculate new nextDestEndOfLineOffset. + nextDestEndOfLineOffset += this->overlayXAxisResolution; + + // Because we reached the end of the overlay line, we need to skip forward until we reach the next line in the data array. + // First, check and see if we have already reached the next line in the data array. + if (currentSourceOffset < nextSourceEndOfLineOffset) + { + // Only jump forward if lineWrap is false. + if (!lineWrap) + { + /* + * Determine how much more to add to the current data offset to reach the next line in the data array. + * + * This is done by subtracting the currentSourceOffset from the nextSourceEndOfLineOffset. + * + * The result of that is the amount of elements in the array we need to add to the current offset, to reach the next line + * in the data array. + */ + currentSourceOffset += (nextSourceEndOfLineOffset - currentSourceOffset); + + // Calculate new nextSourceEndOfLineOffset. + nextSourceEndOfLineOffset += dataXAxisResolution; + } + } + else + { + // Check to see if we are at the end of the current line in the data array. + if (currentSourceOffset == nextSourceEndOfLineOffset) + { + // Calculate new nextSourceEndOfLineOffset. + nextSourceEndOfLineOffset += dataXAxisResolution; + } + } + + // Jump the current source offset forward by the offset amount. + currentSourceOffset += currentSourceXCoordOffset; +#ifndef _NDEBUG + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(Overlay): currentSourceOffset after end of overlay line jump: " << currentSourceOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): currentDestOffset after end of overlay line jump: " << currentDestOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): nextSourceEndOfLineOffset after end of overlay line jump: " << nextSourceEndOfLineOffset << ".\n"; + errorText << "DEBUG Channel: Overlay::Blit(Overlay): nextDestEndOfLineOffset after end of overlay line jump: " << nextDestEndOfLineOffset << ".\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } +#endif + + } + } + + // Increment the currentDestOffset. + currentDestOffset++; + } + } +#ifndef _NDEBUG + else + { + // Source is outside of this overlay's bounds, or a calculation error occured. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(Overlay): Source is outside of this overlay's bounds, or a calculation error occured."; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } + } +#endif + } +#ifndef _NDEBUG + else + { + if (this->debugLevel >= ERROR_WARNING) + { + // Could not get pointer to source overlay's data buffer. + Common::Renderer::DebugChannel.PanicHandler("DEBUG Channel: Overlay::Blit(Overlay): Could not get pointer to source overlay's data buffer, exiting function without altering destionation overlay.\n", COMMON_ID, ERROR_WARNING); + } + } +#endif + } +#ifndef _NDEBUG + else + { + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + errorText << "DEBUG Channel: Overlay::Blit(Overlay): Invalid source overlay resolution: " << dataXAxisResolution << 'x' << dataYAxisResolution << ". Resolution must be greater than zero, aborting with no changes to destionation overlay data.\n"; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } + } +#endif + } +#ifndef _NDEBUG + else + { + // Only output message if we are in verbose error reporting mode. + if (this->debugLevel >= ERROR_VERBOSE) + { + // We were not setup properly, abort. + errorText << "DEBUG Channel: Overlay::Blit(Overlay): Destination overlay is invalid, memory buffer and / or dimentions not setup correctly."; + Common::Renderer::DebugChannel.PanicHandler(errorText.str(), COMMON_ID, ERROR_VERBOSE); + errorText.str(std::string()); + } + } +#endif + // Reset this->lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Exit function. + return; + } + + /*! + * template + * void Common::Renderer::Overlay::Blit(const Overlay & data, const long & startingXoffset, const long & startingYoffset) + * + * Copies data from an overlay of a different data type than the overlay's template data type to the overlay's data buffer. See note below about the required function template specialisation. + * + * Pram: const Overlay & data, The overlay to copy data from. A template specialisation must exist for the Get_Overlay_Data() and Get_Blank_Value() functions to convert the data from the source overlay's data type. + * Pram: const size_t & startingXoffset, the starting x axis offset in the overlay where data from the given overlay will be copied to. Defaults to the overlay origin point. + * Pram: const size_t & startingYoffset, the starting y axis offset in the overlay where data from the given overlay will be copied to. Defaults to the overlay origin point. + */ + + /*! + * void Common::Renderer::Overlay::Clear_Overlay_Data() + * + * Fills the overlay's memory buffers with the overlay's current blank value. + * + * This function has no return. + */ + void Clear_Overlay_Data() + { + // Check and see if the Create_Overlay() function has been run at least once. + if (this->created) + { + // Make sure we have a buffer to clear. + if ((this->overlayData != NULL) && (this->overlayXAxisResolution > 0) && (this->overlayYAxisResolution > 0)) + { + // Fill the array with the blank character. + for (size_t x = 0; x < (this->overlayXAxisResolution * this->overlayYAxisResolution); x++) + { + this->overlayData[x] = this->blankValue; + } + } + if ((this->transparencyBufferEnabled) && (this->transparencyData != NULL) && (this->overlayXAxisResolution > 0) && (this->overlayYAxisResolution > 0)) + { + // Fill the array with the blank character. + for (size_t x = 0; x < (this->overlayXAxisResolution * this->overlayYAxisResolution); x++) + { + this->transparencyData[x] = this->blankValue; + } + } + } + + // Reset this->lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Exit function. + return; + } + + /*! + * void Common::Renderer::Overlay::Reset_Overlay() + * + * Deallocates any allocated memory buffers, and sets the overlay back to it's default state. + * WARNING: This DOES set the transparency calculation function pointer to NULL. + */ + void Reset_Overlay() + { + // Check and see if the overlay data buffer is allocated. + if (this->overlayData != NULL) + { + // Delete the old buffer. + delete[] this->overlayData; + this->overlayData = NULL; + } + + // Check and see if the transparency data buffer is allocated. + if (this->transparencyData != NULL) + { + // Delete the old buffer. + delete[] this->transparencyData; + this->transparencyData = NULL; + } + + // Reset the remaining variables. + this->transparencyBufferEnabled = false; + this->calcTrans = NULL; + this->lastError = COMMON_ERROR_SUCCESS; + this->debugLevel = 0; + Common::Renderer::DebugChannel.change_log_level(this->debugLevel); + this->masterXAxisOffset = 0; + this->masterYAxisOffset = 0; + this->overlayXAxisResolution = 0; + this->overlayYAxisResolution = 0; + + // Exit function. + return; + } + + /*! + * bool Common::Renderer::Overlay::Change_Resolution(const size_t & newXResolution, const size_t & newYResolution) + * + * Changes the resolution of the overlay to the given resolution. + * + * Returns true if the resolution change / buffer reallocation is successfull. + * Returns false otherwise. (Error code will be set in this case.) + */ + bool Change_Resolution(const size_t & newXResolution, const size_t & newYResolution) + { + // Init result. + bool result = false; // The result of this function. + + // Reset this->lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Check and see if Create_Overlay() has been run at least once. + if (this->created) + { + // Check to see if we need to release the previous buffers. + if (this->overlayData != NULL) + { + // Delete the old buffer. + delete[] this->overlayData; + this->overlayData = NULL; + } + if (this->transparencyData != NULL) + { + // Delete the old buffer. + delete[] this->transparencyData; + this->transparencyData = NULL; + } + + // Blank the overlay data members. + this->overlayXAxisResolution = 0; + this->overlayYAxisResolution = 0; + + // Begin try block. + try{ + // Allocate new memory. (Array length is the product of the xScreenResolution and yScreenResolution arguments.) + this->overlayData = new T[(newXResolution * newYResolution)]; + + // Check and see if the transparencyData buffer is enabled. + if (this->transparencyBufferEnabled) + { + // Allocate transparencyData buffer. + this->transparencyData = new T[(newXResolution * newYResolution)]; + } + + // Check that the buffer was allocated. + if (this->overlayData != NULL) + { + // Fill the array with the blank character. + for (size_t x = 0; x < (newXResolution * newYResolution); x++) + { + this->overlayData[x] = this->blankValue; + } + + // Set new resolution values in the overlay. + this->overlayXAxisResolution = newXResolution; + this->overlayYAxisResolution = newYResolution; + + // Check and see if the transparencyData buffer is enabled. + if (this->transparencyBufferEnabled) + { + // Make sure the transparencyData buffer was allocated. + if (this->transparencyData != NULL) + { + // Fill the array with the blank value. + for (size_t x = 0; x < (newXResolution * newYResolution); x++) + { + this->transparencyData[x] = this->blankValue; + } + + // Set result. + result = true; + } + else + { + // Release memory for overlayData. + if (this->overlayData != NULL) + { + delete[] this->overlayData; + this->overlayData = NULL; + } + + // Could not allocate memory for the transparencyData buffer. + this->lastError = RENDERER_ERROR_UNABLE_TO_ALLOC_TD_BUF; + } + } + else + { + // Set result. + result = true; + } + } + else + { + // Could not allocate memory for overlay image buffer. + this->lastError = RENDERER_ERROR_UNABLE_TO_ALLOC_OI_BUF; + } + } + catch(...) + { + // An exception was thrown while attempting to create the overlay. + this->lastError = RENDERER_ERROR_MEM_BUF_ALLOC_EXCEPTION; + + // Check to see the overlay data image buffer was allocated. + if (this->overlayData != NULL) + { + // Delete the old buffer. + delete[] this->overlayData; + this->overlayData = NULL; + } + + // Check to see the transparency data buffer was allocated. + if (this->transparencyData != NULL) + { + // Release the transparency data buffer. + delete[] this->transparencyData; + this->transparencyData = NULL; + } + + // Blank the resolution sizes. + this->overlayXAxisResolution = 0; + this->overlayYAxisResolution = 0; + } + } + + // Exit function. + return result; + } + + /*! + * bool Common::Renderer::Overlay::Swap_Overlays(Common::Renderer::Overlay & src) + * + * Swaps the data members of the source overlay with this one. + * + * Note: This does not reallocate the memory buffers, it only swaps the pointers. + * + * If the source overlay is the same as the destionation overlay, then this function will fail. + * (An error code will be set in the destionation overlay. + * The destionation overlay is the one whose Swap_Overlays() function was called.) + * + * Returns true if the swap was successfull. + * Returns false otherwise. + */ + bool Swap_Overlays(Common::Renderer::Overlay & src) + { + // Init vars. + bool result = false; // The result of this function. + bool temp_bool = false; // Temporary variable used to hold a bool value while the original one is overwritten. + size_t temp_size = 0; // Temporary variable used to hold a size value while the original one is overwritten. + void (*temp_fptr)(const T&, const T&, T&, T&) = NULL; // Temporary variable used to hold a function pointer while the original one is overwritten. + T * temp_data = NULL; // Temporary variable used to hold a pointer while the original one is overwritten. + T temp_blank = this->blankValue; // Temporary variable used to hold a blank value while the original one is overwritten. + + // Prevent self-assignment. + if (this != &src) + { + // Reset this->lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Swap the created flags. + temp_bool = this->created; + this->created = src.created; + src.created = temp_bool; + temp_bool = false; + + // Swap the transparencyBufferEnabled flags. + temp_bool = this->transparencyBufferEnabled; + this->transparencyBufferEnabled = src.transparencyBufferEnabled; + src.transparencyBufferEnabled = temp_bool; + temp_bool = false; + + // Swap the blank value. + this->blankValue = src.blankValue; + src.blankValue = temp_blank; + + // Swap the transparency calculation function pointer. + temp_fptr = this->calcTrans; + this->calcTrans = src.calcTrans; + src.calcTrans = temp_fptr; + temp_fptr = NULL; + + // Swap the masterXAxisOffset. + temp_size = this->masterXAxisOffset; + this->masterXAxisOffset = src.masterXAxisOffset; + src.masterXAxisOffset = temp_size; + temp_size = 0; + + // Swap the masterYAxisOffset. + temp_size = this->masterYAxisOffset; + this->masterYAxisOffset = src.masterYAxisOffset; + src.masterYAxisOffset = temp_size; + temp_size = 0; + + // Swap the overlayXAxisResolution. + temp_size = this->overlayXAxisResolution; + this->overlayXAxisResolution = src.overlayXAxisResolution; + src.overlayXAxisResolution = temp_size; + temp_size = 0; + + // Swap the overlayYAxisResolution. + temp_size = this->overlayYAxisResolution; + this->overlayYAxisResolution = src.overlayYAxisResolution; + src.overlayYAxisResolution = temp_size; + temp_size = 0; + + // Swap the overlay data pointers. + temp_data = this->overlayData; + this->overlayData = src.overlayData; + src.overlayData = temp_data; + temp_data = NULL; + + // Swap the transparency data pointers. + temp_data = this->transparencyData; + this->transparencyData = src.transparencyData; + src.transparencyData = temp_data; + temp_data = NULL; + + // Set result. + result = true; + } + else + { + // We are the given overlay, cannot do self assignment. + this->lastError = RENDERER_ERROR_INVAL_OVERLAY_SELF_OVERWRITE; + } + + // Return the result. + return result; + } + + /*! + * bool Common::Renderer::Overlay::Duplicate_Overlay(const Common::Renderer::Overlay & src) + * + * Overwrites this overlay's data with the data from the source. + * + * Note: This is a deep copy function. + * (I.e. This function can reallocate this overlay's memory buffers, + * if they are not the same size as the source overlay's memory buffers.) + * + * Note: If the source overlay is the same as the destionation overlay, then this function will fail. + * (An error code will be set in the destionation overlay. + * The destionation overlay is the one whose Duplicate_Overlay() function was called.) + * + * Returns true if the source overlay's data was successfully copied. + * Returns false otherwise. (Overlay's error code will be set in this case.) + */ + bool Duplicate_Overlay(const Common::Renderer::Overlay & src) + { + // Init result. + bool result = false; + + // Prevent duplicating ourselves. + if (this != &src) + { + // Reset this->lastError. + this->lastError = COMMON_ERROR_SUCCESS; + + // Copy the created flag of the source overlay. + this->created = src.created; + + // Copy the transparency calculation function pointer. + this->calcTrans = src.calcTrans; + + // Copy the overlay offsets. + this->masterXAxisOffset = src.masterXAxisOffset; + this->masterYAxisOffset = src.masterYAxisOffset; + + // Copy the blank value. + this->blankValue = src.blankValue; + + // Check to see if we need to reallocate our buffers. + if ((this->overlayXAxisResolution != src.overlayXAxisResolution) || (this->overlayYAxisResolution != src.overlayYAxisResolution)) + { + // We need to reallocate our buffers. + if (this->overlayData != NULL) + { + delete[] this->overlayData; + this->overlayData = NULL; + } + if (this->transparencyData != NULL) + { + delete[] this->transparencyData; + this->transparencyData = NULL; + } + + // Clear our resolution settings. + this->overlayXAxisResolution = 0; + this->overlayYAxisResolution = 0; + + // Check and see if the source overlay's resolution is greater than zero. + if ((src.overlayXAxisResolution > 0) && (src.overlayYAxisResolution > 0)) + { + // Begin try block. + try{ + // Reallocate the buffers. + this->overlayData = new T[(src.overlayXAxisResolution * src.overlayYAxisResolution)]; + + // Check and see if the source overlay's transparencyBufferEnabled flag is set. + if (src.transparencyBufferEnabled) + { + // Allocate our transparencyData buffer. + this->transparencyData = new T[(src.overlayXAxisResolution * src.overlayYAxisResolution)]; + } + else + { + // Set our transparencyBufferEnabled flag to false. + this->transparencyBufferEnabled = false; + } + } + catch(...) + { + // Exception thrown. + this->lastError = RENDERER_ERROR_MEM_BUF_ALLOC_EXCEPTION; + } + + // Make sure that the overlayData buffer was allocated. + if (this->overlayData != NULL) + { + // Blank out the overlayData buffer. + for (size_t x = 0; x < (this->overlayXAxisResolution * this->overlayYAxisResolution); x++) + { + this->overlayData[x] = this->blankValue; + } + + // Copy the resolution settings. + this->overlayXAxisResolution = src.overlayXAxisResolution; + this->overlayYAxisResolution = src.overlayYAxisResolution; + + // Check and see if the source overlay's transparencyData buffer is enabled. + if (src.transparencyBufferEnabled) + { + // Check the transparencyData buffer. + if (this->transparencyData != NULL) + { + // Blank out the transparencyData buffer. + for (size_t x = 0; x < (this->overlayXAxisResolution * this->overlayYAxisResolution); x++) + { + this->transparencyData[x] = this->blankValue; + } + + // Set our transparencyBufferEnabled flag. + this->transparencyBufferEnabled = true; + } + else + { + // Deallocate overlayData buffer. + if (this->overlayData != NULL) + { + delete[] this->overlayData; + this->overlayData = NULL; + } + + // Could not allocate memory for transparencyData buffer. + this->lastError = RENDERER_ERROR_UNABLE_TO_ALLOC_TD_BUF; + } + } + } + else + { + // Could not allocate memory for overlay image buffer. + this->lastError = RENDERER_ERROR_UNABLE_TO_ALLOC_OI_BUF; + } + } + } + + // Check and see if the buffers are allocated. + if ((this->overlayData != NULL) && (src.overlayData != NULL)) + { + // Copy data from the source. + for (size_t x = 0; ((x < (this->overlayXAxisResolution * this->overlayYAxisResolution)) && (x < (src.overlayXAxisResolution * src.overlayYAxisResolution))); x++) + { + // Copy the data. + this->overlayData[x] = src.overlayData[x]; + } + + // Check and see if the source overlay's transparencyBufferEnabled flag and our transparencyBufferEnabled flags are set and our transparencyData buffer is allocated. + if (this->transparencyBufferEnabled && src.transparencyBufferEnabled) + { + // Do the same for the transparencyData. + if ((this->transparencyData != NULL) && (src.transparencyData != NULL)) + { + // Copy data from the source. + for (size_t x = 0; ((x < (this->overlayXAxisResolution * this->overlayYAxisResolution)) && (x < (src.overlayXAxisResolution * src.overlayYAxisResolution))); x++) + { + // Copy the data. + this->transparencyData[x] = src.transparencyData[x]; + } + + // We are done. + result = true; + } + } + else + { + // We are done. + result = true; + } + } + } + else + { + // We cannot duplicate ourselves. + this->lastError = RENDERER_ERROR_INVAL_OVERLAY_SELF_OVERWRITE; + } + + // Exit function. + return result; + } + + /*! + * const T * Common::Renderer::Overlay::Get_Overlay_Data() const + * + * Returns a pointer to the overlay's current image buffer. + * + * WARNING: If another overlay function is called the returned pointer can become invalid, or + * the data can be altered. DO NOT USE THE RETURNED POINTER AFTER CALLING A DIFFERENT OVERLAY FUNCTION! + */ + const T * Get_Overlay_Data() const + { + // Return a const data pointer. + return this->overlayData; + } + + /*! + * const T * Common::Renderer::Overlay::Get_Transparency_Data() const + * + * Returns a pointer to the overlay's current transparency data buffer. + * + * WARNING: If another overlay function is called the returned pointer can become invalid, or + * the data can be altered. DO NOT USE THE RETURNED POINTER AFTER CALLING A DIFFERENT OVERLAY FUNCTION! + */ + const T * Get_Transparency_Data() const + { + // Return a const data pointer. + return this->transparencyData; + } + + /*! + * const T & Common::Renderer::Overlay::Get_Blank_Value() const + * + * Returns a reference to the overlay's current blank value. + */ + const T & Get_Blank_Value() const + { + // Return the blank value. + return this->blankValue; + } + + /*! + * void Common::Renderer::Overlay::Set_Blank_Value(const T & value) + * + * Sets the overlay's blank value to the given value. + */ + void Set_Blank_Value(const T & value) + { + // Set the blankValue. + this->blankValue = value; + + // Exit function. + return; + } + + /*! + * void Common::Renderer::Overlay::Get_Overlay_Offsets(long & xOffset, long & yOffset) const + * + * Sets the overlay's current masterXAxisOffset to xOffset, and the overlay's current masterYAxisOffset to yOffset. + * (I.e. It sets the given arguments to the overlay's current offsets.) + * + * This function has no return, result is stored in the given arguments. + */ + void Get_Overlay_Offsets(long & xOffset, long & yOffset) const + { + // Set the values. + xOffset = this->masterXAxisOffset; + yOffset = this->masterYAxisOffset; + + // Exit function. + return; + } + + /*! + * void Common::Renderer::Overlay::Set_Overlay_Offsets(const long & newXOffset, const long & newYOffset) + * + * Sets the overlay's offsets to the given offsets. + * + * This function has no return. + */ + void Set_Overlay_Offsets(const long & newXOffset, const long & newYOffset) + { + // Move overlay. + this->masterXAxisOffset = newXOffset; + this->masterYAxisOffset = newYOffset; + + // Exit function. + return; + } + + /*! + * void Common::Renderer::Overlay::Get_Resolution(size_t & xResolution, size_t & yResolution) const + * + * Sets the overlay's current overlayXAxisResolution to xResolution, and the overlay's current overlayYAxisResolution to yResolution. + * (I.e. It sets the given arguments to the overlay's current resolution.) + * + * This function has no return, result is stored in the given arguments. + */ + void Get_Resolution(size_t & xResolution, size_t & yResolution) const + { + // Set the values. + xResolution = this->overlayXAxisResolution; + yResolution = this->overlayYAxisResolution; + + // Exit function. + return; + } + + /* + * Specializations for the Get_Data_Element() and Set_Data_Element() functions go in the Renderer_Overlay_Element_Accessor_Functions.h file. + * + * NOT HERE! + */ + + /*! + * T Common::Renderer::Overlay::Get_Data_Element(const size_t & offset) const + * + * Returns the element at the given offset in the overlay's image data buffer. + * + * If an invalid offset is given then this function will return the overlay's current blank value. + */ + T Get_Data_Element(const size_t & offset) const + { + // Init result. + T result = this->blankValue; + + // Check and see if we have data stored. + if ((this->overlayData != NULL) && ((overlayXAxisResolution > 0) && (overlayYAxisResolution > 0))) + { + // Check for valid offset. + if (offset < (overlayXAxisResolution * overlayYAxisResolution)) + { + // Return the data at that offset. + result = this->overlayData[offset]; + } + } + + // Return the result. + return result; + } + + /*! + * T Common::Renderer::Overlay::Get_Transparency_Element(const size_t & offset) const + * + * Returns the element at the given offset in the overlay's transparency data buffer. + * + * If an invalid offset is given then this function will return the overlay's current blank value. + */ + T Get_Transparency_Element(const size_t & offset) const + { + // Init result. + T result = this->blankValue; + + // Check and see if we have data stored. + if ((this->transparencyBufferEnabled) && (this->transparencyData != NULL) && ((overlayXAxisResolution > 0) && (overlayYAxisResolution > 0))) + { + // Check for valid offset. + if (offset < (overlayXAxisResolution * overlayYAxisResolution)) + { + // Return the data at that offset. + result = this->transparencyData[offset]; + } + } + + // Return the result. + return result; + } + + template + T Get_Data_Element(const size_t & offset, const Overlay * source) const + { + /*! + * This is the generic function for Get_Data_Element(). + * + * All it does is return the current blankValue as we are required to return + * a valid object of type T. + * + * All Overlay functions call this function to access an element that is outside + * of the overlay object. This allows the Overlay object to cater to other data + * types as needed. Therefore, you must impliment the needed functionality as a + * template specialization in Renderer_Overlay_Element_Accessor_Functions.h. (NOT HERE!) + */ + + // We don't do anything here except return a copy of the current blankValue. + return this->blankValue; + } + + template + T Get_Transparency_Element(const size_t & offset, const Overlay * source) const + { + /*! + * This is the generic function for Get_Transparency_Element(). + * + * All it does is return the current blankValue as we are required to return + * a valid object of type T. + * + * All Overlay functions call this function to access an element that is outside + * of the overlay object. This allows the Overlay object to cater to other data + * types as needed. Therefore, you must impliment the needed functionality as a + * template specialization in Renderer_Overlay_Element_Accessor_Functions.h. (NOT HERE!) + */ + + // We don't do anything here except return a copy of the current blankValue. + return this->blankValue; + } + + template + void Set_Data_Element(const size_t & offset, const A & element) + { + /*! + * This is the generic function for Set_Data_Element(). + * + * All it does is return the second it is called. + * + * All Overlay functions call this function to convert and set the given element + * from it's original data type, to the Overlay's data type, and then set the + * converted element at the given offset in the overlay's data array. Therefore, + * you must impliment the needed functionality as a template specialization in + * Renderer_Overlay_Element_Accessor_Functions.h. (NOT HERE!) + */ + + // We don't do anything here except exit the function. + return; + } + + template + void Set_Transparency_Element(const size_t & offset, const A & element) + { + /*! + * This is the generic function for Set_Transparency_Element(). + * + * All it does is return the second it is called. + * + * All Overlay functions call this function to convert and set the given element + * from it's original data type, to the Overlay's data type, and then set the + * converted element at the given offset in the overlay's data array. Therefore, + * you must impliment the needed functionality as a template specialization in + * Renderer_Overlay_Element_Accessor_Functions.h. (NOT HERE!) + */ + + // We don't do anything here except exit the function. + return; + } + + // Include Renderer_Overlay_Element_Accessor_Functions.h. + }; + } +} + +#endif + +// End of Renderer_Data_Structures_Overlay_Base_Functions.h diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Management_Functions.h b/src/Common/Src/Rendering_Subsystem/Renderer_Management_Functions.h new file mode 100644 index 0000000..c638583 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Management_Functions.h @@ -0,0 +1,42 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Management_Functions.h 4/2/2014 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Include guard. +#ifndef COMMON_RENDER_SUBSYSTEM_H +#error "You must include the Renderer.h header file. It will include all of the other needed headers." +#endif + +#ifndef COMMON_RENDER_MANAGEMENT_FUNCTIONS_H +#define COMMON_RENDER_MANAGEMENT_FUNCTIONS_H + +// Declare namespaces. +namespace Common +{ + namespace Renderer + { + size_t Choose_Renderer(); + + Common::Renderer::Renderer2D * Create_2D_Renderer(const size_t & rendererID); + } +} + +#endif + +// End of Renderer_Management_Functions.h \ No newline at end of file diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Overlay_Element_Accessor_Functions.h b/src/Common/Src/Rendering_Subsystem/Renderer_Overlay_Element_Accessor_Functions.h new file mode 100644 index 0000000..96820e8 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Overlay_Element_Accessor_Functions.h @@ -0,0 +1,45 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Overlay_Element_Accessor_Functions.h 05/2/2014 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Include guard. +#ifndef COMMON_RENDER_SUBSYSTEM_H +#error "You must include the Renderer.h header file. It will include all of the other needed headers." +#endif + +#ifndef COMMON_RENDERER_OVERLAY_ELEMENT_ACCESSOR_FUNCTIONS_H +#define COMMON_RENDERER_OVERLAY_ELEMENT_ACCESSOR_FUNCTIONS_H + +/* + * NOTE: This file is included at the end of the definition for the Common::Renderer::Overlay class. + * + * As such it's not required to fully qualify the function names for the template specializations. + * + * THIS FILE IS FOR TEMPLATE SPECIALIZATIONS OF THE T Common::Renderer::Overlay::Get_Data_Element(const size_t &, const A *) AND THE + * void Common::Renderer::Overlay::Set_Data_Element(const size_t &, const A *) FUNCTIONS ONLY. ANY OTHER FUNCTION FOUND HERE WILL BE REMOVED + * WITHOUT WARNING. + * + */ + +// Specialization of char. + + +#endif + +// End of Renderer_Overlay_Element_Accessor_Functions.h \ No newline at end of file diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Text_Console.cpp b/src/Common/Src/Rendering_Subsystem/Renderer_Text_Console.cpp new file mode 100644 index 0000000..263ac91 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Text_Console.cpp @@ -0,0 +1,932 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Text_Console.cpp 22/12/2013 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +#include "Renderer.h" + +bool Common::Renderer::Renderer_Text_Console::Generate_Fake_Transparency_Data(const char * dataArray, const size_t & dataLength, char *& transparencyData) +{ + // Init result. + bool result = false; + + // Debug args. + //std::cout << "Generate_Fake_Transparency_Data() Arguments:\n"; + //std::cout << "dataArray: " << &dataArray << ".\n"; + //std::cout << "dataLength: " << dataLength << ".\n"; + //std::cout << "transparencyData: " << &transparencyData << ".\n"; + //std::cout.flush(); + + // Make sure dataArray is not NULL. + if (dataArray != NULL) + { + // Make sure dataLength is greater than zero. + if (dataLength > 0) + { + // Begin try block. + try{ + // Check and see if the transparencyData buffer is allocated. + if (transparencyData != NULL) + { + // Release the transparencyData buffer. + delete[] transparencyData; + transparencyData = NULL; + } + + // Allocate the transparencyData buffer. + transparencyData = new char[dataLength]; + + // Check and see if it was allocated. + if (transparencyData != NULL) + { + // Set the values to NULL. + memset(transparencyData, '\0', dataLength); + + // Start generation loop. + for (size_t x = 0; x < dataLength; x++) + { + /* + * For each value in dataArray that is not blankValue, + * make the corrosponding value in transparencyData, TEXT_CONSOLE_RENDERER_TRANSPARENCY_VALUE. + * + * (This performs the same effect as the original blank value. + * I.e. If the blankValue is not present at that location, + * the renderer considers it part of the image. + * + * If the blankValue is present at that location, then the + * renderer considers that location blank, and anything + * under that overlay on the final image will not be altered. + * + * Short version: Fake transparency, as the text console does + * not display actual images only characters.) + */ + if (dataArray[x] != this->blankValue) + { + transparencyData[x] = TEXT_CONSOLE_RENDERER_TRANSPARENCY_VALUE; + } + } + + // Set result. + result = true; + } + else + { + // Could not allocate memory. + this->lastError = -25; + } + } + catch(...) + { + // Clear the buffer if it was allocated. + if (transparencyData != NULL) + { + delete[] transparencyData; + transparencyData = NULL; + } + + // Exception thrown. + this->lastError = -13; + } + } + } + + // Return result. + return result; +} + +void Common::Renderer::Renderer_Text_Console::Calculate_Transparency(const char & srcImgVal, const char & srcTransVal, char & destImgVal, char & destTransVal) +{ + // Check and see if the srcTransVal is equal to TEXT_CONSOLE_RENDERER_TRANSPARENCY_VALUE. + if (srcTransVal == TEXT_CONSOLE_RENDERER_TRANSPARENCY_VALUE) + { + // Copy the data and continue. + destImgVal = srcImgVal; + destTransVal = srcTransVal; + } + + // Exit function. + return; +} + +size_t Common::Renderer::Get_TEXT_CONSOLE_ERROR_TABLE_Size() +{ + // Return the size of the table. (Size of the table divided by the size of a single element in the table.) + return (sizeof(Common::Renderer::TEXT_CONSOLE_ERROR_TABLE) / sizeof(Common_Error_Object)); +} + +bool Common::Renderer::Renderer_Text_Console::Change_Resolution(const size_t & xScreenResolution, const size_t & yScreenResolution, const size_t & colorDepth, const bool & fullscreen) +{ + // Init char pointer. + bool result = false; // Result of this function. + Common::Renderer::Overlay temp; // Used to create an overlay buffer if needed. + size_t finalResultXResolution = 0; + size_t finalResultYResolution = 0; + + // Reset last error. + this->lastError = 0; + + // Begin try block. + try{ + // Set the change flag. + this->bChangeSinceLastRender = true; + + // Allocate new memory. (c-string length is the product of the xScreenResolution and yScreenResolution arguments.) + this->finalResult.Create_Overlay(this->blankValue, 0, 0, xScreenResolution, yScreenResolution); + + // Check that the overlay was setup correctly. + this->finalResult.Get_Resolution(finalResultXResolution, finalResultYResolution); + if ((finalResultXResolution == xScreenResolution) && (finalResultYResolution == yScreenResolution)) + { + // Set the finalResult blankValue. + this->finalResult.Set_Blank_Value(this->blankValue); + + // Set the renderer transparency function. + this->finalResult.Set_Transparency_Function(Common::Renderer::Renderer_Text_Console::Calculate_Transparency); + + // Check to see if the overlay system is disabled. + if (!this->bOverlaysEnabled) + { + // Overlays are disabled, reset the overlay vector. + this->overlayStack.clear(); + + // Place the temp value in the vector. + this->overlayStack.push_back(temp); + + // Make sure we added the overlay to the overlayStack. + if (this->overlayStack.size() == 1) + { + // Now create a new overlay for a working buffer. + // Allocate new memory. (c-string length is the product of the xScreenResolution and yScreenResolution arguments.) + if (this->overlayStack[0].Create_Overlay(this->blankValue, 0, 0, xScreenResolution, yScreenResolution)) + { + // Set the remaining class pramaters. + this->xScreenResolution = xScreenResolution; + this->yScreenResolution = yScreenResolution; + this->colorDepth = colorDepth; + this->bFullscreen = fullscreen; + + // Set result. + result = true; + } + else + { + // Could not allocate memory for overlay image buffer. + this->lastError = -11; + this->overlayStack.clear(); + } + } + else + { + // Could not create a new overlay in the overlay stack. + this->lastError = -10; + } + } + else + { + // Set the remaining class pramaters. + this->xScreenResolution = xScreenResolution; + this->yScreenResolution = yScreenResolution; + this->colorDepth = colorDepth; + this->bFullscreen = fullscreen; + + // Set result. + result = true; + } + } + else + { + // Set lastError. + this->lastError = -12; + } + } + catch(...) + { + // If the memory was allocated, free it. + this->finalResult.Reset_Overlay(); + + // If needed reset the overlayStack. + if (!this->bOverlaysEnabled) + { + // Reset the overlay stack. + this->overlayStack.clear(); + } + + // Set lastError. + this->lastError = -13; + } + + // Return the result. + return result; +} + +bool Common::Renderer::Renderer_Text_Console::Set_Blank_Value(const char & value) +{ + // Init result. + bool result = false; + + // Guard against using newline as a blank value. + if (value != '\n') + { + // Set blank value. + this->blankValue = value; + + // Set it on all overlays if needed. + if (bOverlaysEnabled) + { + for (size_t x = 0; x < this->overlayStack.size(); x++) + { + this->overlayStack[x].Set_Blank_Value(this->blankValue); + } + } + + // Set the change flag. + this->bChangeSinceLastRender = true; + + // Set result. + result = true; + } + else + { + // You cannot use a newline ('\n') character as a blank value. + this->lastError = -14; + } + + // Return the result. + return result; +} + +bool Common::Renderer::Renderer_Text_Console::Set_Blank_Value(const size_t & overlayNumber, const char & value) +{ + // Init result. + bool result = false; + + // Guard against using newline as a blank value. + if (value != '\n') + { + // Check and see if overlays are enabled, and that the overlayNumber is valid. + if ((bOverlaysEnabled) && ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size()))) + { + // Set the overlay's blankValue. + this->overlayStack[overlayNumber].Set_Blank_Value(this->blankValue); + + // Set the change flag. + this->bChangeSinceLastRender = true; + + // Set result. + result = true; + } + else + { + // Invalid overlayNumber. + this->lastError = -17; + } + } + else + { + // You cannot use a newline ('\n') character as a blank value. + this->lastError = -14; + } + + // Return the result. + return result; +} + +char Common::Renderer::Renderer_Text_Console::Get_Blank_Value() const +{ + // Return blankValue. + return this->blankValue; +} + +char Common::Renderer::Renderer_Text_Console::Get_Blank_Value(const size_t & overlayNumber) const +{ + // Init result. + char result = this->blankValue; + + // Check and see if the given overlay exists. + if ((this->bOverlaysEnabled) && ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size()))) + { + // Get the overlay's blankValue. + result = this->overlayStack[overlayNumber].Get_Blank_Value(); + } + + // Return the result. + return result; +} + +const char * Common::Renderer::Renderer_Text_Console::Get_Last_Error() const +{ + // Init result. + const char * result = TEXT_CONSOLE_ERROR_TABLE[1].error; // Default is to return the unknown error code message. + + // Guard against no errors. + if (this->lastError != 0) + { + // Start lookup loop. + for (size_t x = 0; ((x < Common::Renderer::Get_TEXT_CONSOLE_ERROR_TABLE_Size()) && (result == TEXT_CONSOLE_ERROR_TABLE[1].error)); x++) + { + // Check for matching error code. + if (this->lastError == TEXT_CONSOLE_ERROR_TABLE[x].errorCode) + { + // Set the error message. + result = TEXT_CONSOLE_ERROR_TABLE[x].error; + } + } + } + else + { + // Return no error message. + result = TEXT_CONSOLE_ERROR_TABLE[0].error; + } + + // Return the result. + return result; +} + +short Common::Renderer::Renderer_Text_Console::Get_Last_Error_Code() const +{ + // Return lastError. + return this->lastError; +} + +bool Common::Renderer::Renderer_Text_Console::Render() +{ + // Init result. + bool result = false; // The result of this function. + long overlayXOffset = 0; // Holds the X axis offset for the current overlay. + long overlayYOffset = 0; // Holds the Y axis offset for the current overlay. + size_t finalResultXResolution = 0; // Holds the X axis resolution of the finalResult overlay. + size_t finalResultYResolution = 0; // Holds the Y axis resolution of the finalResult overlay. + + // Reset last error. + this->lastError = 0; + + // Make sure finalResult is allocated. + this->finalResult.Get_Resolution(finalResultXResolution, finalResultYResolution); + if ((finalResultXResolution > 0) && (finalResultYResolution > 0)) + { + // Check for a change since the last time we were called. + if (this->bChangeSinceLastRender) + { + // Reset the finalResult buffer. + this->finalResult.Clear_Overlay_Data(); + + // Begin overlayStack iteration loop. + for (size_t x = this->overlayStack.size(); x > 0; x--) + { + // Get the overlay offsets. + this->overlayStack[(x - 1)].Get_Overlay_Offsets(overlayXOffset, overlayYOffset); + + // Call Blit(). + this->finalResult.Blit(this->overlayStack[(x - 1)], overlayXOffset, overlayYOffset); + } + + // Clear the change flag. + this->bChangeSinceLastRender = false; + } + + // Output the data to std::cout. + for (size_t x = 0; x < (this->xScreenResolution * this->yScreenResolution); x++) + { + /* + Newlines must be inserted at the end of the row after the output. + + (XRes - 1) = last element on row + XRes = first element on next row + + XRes: 3 YRes: 3 + + [0] [1] [2] <'\n'> + [3] [4] [5] <'\n'> + [6] [7] [8] <'\n'> + */ + + // Check and see if a newline is needed. + if ((!this->bFullscreen) && ((x % (this->xScreenResolution) == 0) && (x > 0))) + { + // Output a newline character. + std::cout << '\n'; + + // Flush the output buffer. + std::cout.flush(); + } + + // Output data from the buffer. + std::cout << this->finalResult.Get_Data_Element(x); + std::cout.flush(); + } + + // Set result. + result = true; + } + else + { + // Internal Error, finalResult buffer is not allocated while trying to output to console. + this->lastError = -15; + } + + // Exit function. + return result; +} + +bool Common::Renderer::Renderer_Text_Console::Blit_Text(const char * textString, const size_t & overlayNumber, const bool & lineWrap, const size_t & textStringXResolution, const size_t & textStringYResolution, const size_t & xCoordOffset, const size_t & yCoordOffset) +{ + // Init result. + bool result = false; // The result of this function. + char * transparencyData = NULL; // Holds a pointer to the fake transparency data buffer. + + // Reset lastError. + this->lastError = 0; + + // Check for a valid string. + if ((textString != NULL) && (textStringXResolution > 0)) + { + // Check for a valid overlay. + if ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + // Generate fake transparency data. + if (Common::Renderer::Renderer_Text_Console::Generate_Fake_Transparency_Data(textString, (textStringXResolution * textStringYResolution), transparencyData)) + { + // Blit the data. + this->overlayStack[overlayNumber].Blit(textString, transparencyData, xCoordOffset, yCoordOffset, textStringXResolution, textStringYResolution, lineWrap); + + // Release the transparencyData buffer. + if (transparencyData != NULL) + { + delete[] transparencyData; + transparencyData = NULL; + } + + // Set result. + result = true; + } + else + { + // Set last error. (Could not generate transparency data.) + this->lastError = -26; + } + } + else + { + // Set last error. + this->lastError = -17; + } + } + else + { + // Set last error. + this->lastError = -18; + } + + // Exit function. + return result; +} + +short Common::Renderer::Renderer_Text_Console::Get_Overlay_Debugging_Level(const size_t & overlayNumber) +{ + // Init result. + short result = 0; // Holds the returned debugging level from the overlay. (Default is debugging level zero (0).) + + // Check to see if the given overlay number exists. + if ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + // Get the overlay's debugging level. + result = this->overlayStack[overlayNumber].Get_Debug_Level(); + } + else + { + // No overlay exists with the given offset. + this->lastError = -17; + } + + // Return the result. + return result; +} + +bool Common::Renderer::Renderer_Text_Console::Set_Overlay_Debugging_Level(const size_t & overlayNumber, const short & level) +{ + // Init result. + bool result = false; + + // Check to see if the given overlay number exists. + if ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + // Set the overlay's debugging level. + this->overlayStack[overlayNumber].Set_Debug_Level(level); + + // Set result. + result = true; + } + else + { + // No overlay exists with the given offset. + this->lastError = -17; + } + + // Return the result. + return result; +} + +bool Common::Renderer::Renderer_Text_Console::Create_Overlay(const size_t & xResolution, const size_t & yResolution, const long & xCoordOffset, const long & yCoordOffset) +{ + // Init result. + bool result = false; // The result of this function. + size_t overlayStackSize = this->overlayStack.size(); // Holds the overlay stack size prior to creating the new overlay. + size_t createdOverlayXAxisResolution = 0; // Used to check the created overlay's x axis resolution. + size_t createdOverlayYAxisResolution = 0; // Used to check the created overlay's y axis resolution. + Common::Renderer::Overlay temp; // Used to create an overlay buffer. + + std::cout << "xCoordOffset = " << xCoordOffset << ".\nyCoordOffset = " << yCoordOffset << ".\n"; + std::cout.flush(); + + // Check to see if overlays are enabled. + if (this->bOverlaysEnabled) + { + // Check for invalid resolutions. + if ((xResolution > 0) && (yResolution > 0)) + { + // Begin try block. + try{ + // Place the temp value in the vector. + this->overlayStack.push_back(temp); + + // Make sure we added the overlay to the overlayStack. + if ((overlayStackSize + 1) == this->overlayStack.size()) + { + // Set renderer transparency function. + this->finalResult.Set_Transparency_Function(Common::Renderer::Renderer_Text_Console::Calculate_Transparency); + + // Allocate new memory. (c-string length is the product of the xResolution and yResolution arguments.) + this->overlayStack[(this->overlayStack.size() - 1)].Create_Overlay(this->blankValue, xCoordOffset, yCoordOffset, xResolution, yResolution); + + // Check that the overlay was setup correctly. + this->overlayStack[(this->overlayStack.size() - 1)].Get_Resolution(createdOverlayXAxisResolution, createdOverlayYAxisResolution); + if ((createdOverlayXAxisResolution == xResolution) && (createdOverlayYAxisResolution == yResolution)) + { + // Set result. + result = true; + } + else + { + // Could not allocate memory for overlay image buffer. + this->lastError = -11; + } + } + else + { + // Could not create a new overlay in the overlay stack. + this->lastError = -10; + } + } + catch(...) + { + // An exception was thrown while attempting to create the overlay. + this->lastError = -19; + + // If the memory was allocated, free it. + if ((overlayStackSize + 1) == this->overlayStack.size()) + { + // Clear the overlay data. + this->overlayStack[overlayStackSize].Clear_Overlay_Data(); + + // Erase the created overlay. + DataProcess::removeFromVector(this->overlayStack, overlayStackSize); + } + } + } + } + + // Exit function. + return result; +} + +bool Common::Renderer::Renderer_Text_Console::Destroy_Overlay(const size_t & overlayNumber) +{ + // Init result. + bool result = false; // The result of this function. + + // Check to see if overlays are enabled. + if (this->bOverlaysEnabled) + { + // Check to see if the given overlay number exists. + if ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + // Clear the overlay's data. + this->overlayStack[overlayNumber].Clear_Overlay_Data(); + + // Destroy that overlay. + DataProcess::removeFromVector(this->overlayStack, overlayNumber); + + // Set the change flag. + this->bChangeSinceLastRender = true; + + // Set result. + result = true; + } + else + { + // No overlay exists with the given offset. + this->lastError = -17; + } + } + + // Exit function. + return result; +} + +void Common::Renderer::Renderer_Text_Console::Clear_Overlay(const size_t & overlayNumber) +{ + // Check to see if the given overlay number exists. + if ((this->bOverlaysEnabled) && (overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + // Clear that overlay. + this->overlayStack[overlayNumber].Clear_Overlay_Data(); + + // Set the change flag. + this->bChangeSinceLastRender = true; + } + else + { + // No overlay exists with the given offset. + this->lastError = -17; + } + + // Exit function. + return; +} + +void Common::Renderer::Renderer_Text_Console::Reset_Overlay(const size_t & overlayNumber) +{ + // Check to see if the given overlay number exists. + if ((this->bOverlaysEnabled) && (overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + // Reset the overlay. + this->overlayStack[overlayNumber].Reset_Overlay(); + + // Set the change flag. + this->bChangeSinceLastRender = true; + } + else + { + // No overlay exists with the given offset. + this->lastError = -17; + } + + // Exit function. + return; +} + +bool Common::Renderer::Renderer_Text_Console::Change_Overlay_Resolution(const size_t & overlayNumber, const size_t & xScreenResolution, const size_t & yScreenResolution, const size_t & colorDepth, const bool & fullscreen) +{ + // Init char pointer. + bool result = false; // Result of this function. + + // Check and see if overlays are enabled, and that the overlayNumber is valid. + if ((bOverlaysEnabled) && ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size()))) + { + // Change the overlay's resolution. + result = this->overlayStack[overlayNumber].Change_Resolution(xScreenResolution, yScreenResolution); + } + + // Return the result. + return result; +} + +void Common::Renderer::Renderer_Text_Console::Get_Overlay_Offsets(long & xCoordOffset, long & yCoordOffset, const size_t & overlayNumber) +{ + // Check to see if overlays are enabled. + if (this->bOverlaysEnabled) + { + // Check to see if the given overlay number exists. + if ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + // Get the offsets. + this->overlayStack[(overlayNumber)].Get_Overlay_Offsets(xCoordOffset, yCoordOffset); + } + else + { + // No overlay exists with the given offset. + this->lastError = -17; + } + } + else + { + // Make sure the working buffer is allocated. + if (this->overlayStack.size() == 1) + { + // Get the offsets for the working buffer. + this->overlayStack[0].Get_Overlay_Offsets(xCoordOffset, yCoordOffset); + } + else + { + // We don't have a working buffer. + this->lastError = -16; + } + } + + // Exit function. + return; +} + +void Common::Renderer::Renderer_Text_Console::Set_Overlay_Offsets(const long & xCoordOffset, const long & yCoordOffset, const size_t & overlayNumber) +{ + // Check to see if overlays are enabled. + if (this->bOverlaysEnabled) + { + // Check to see if the given overlay number exists. + if ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + // Move overlay. + this->overlayStack[overlayNumber].Set_Overlay_Offsets(xCoordOffset, yCoordOffset); + + // Set the change flag. + this->bChangeSinceLastRender = true; + } + else + { + // No overlay exists with the given offset. + this->lastError = -17; + } + } + else + { + // Check and see if the working buffer is allocated. + if (this->overlayStack.size() == 1) + { + // Move overlay. + this->overlayStack[0].Set_Overlay_Offsets(xCoordOffset, yCoordOffset); + + // Set the change flag. + this->bChangeSinceLastRender = true; + } + else + { + // We don't have a working buffer. + this->lastError = -16; + } + } + + // Exit function. + return; +} + +bool Common::Renderer::Renderer_Text_Console::Swap_Overlays(const size_t & firstOverlayNumber, const size_t & secondOverlayNumber) +{ + // Init vars. + bool result = false; // The result of this function. + + // Check to see if overlays are enabled. + if (this->bOverlaysEnabled) + { + // Check to see if the given overlay numbers exist. + if (((firstOverlayNumber >= 0) && (firstOverlayNumber < this->overlayStack.size())) && ((secondOverlayNumber >= 0) && (secondOverlayNumber < this->overlayStack.size()))) + { + // Call Overlay<>::Swap_Overlays(). + if (this->overlayStack[firstOverlayNumber].Swap_Overlays(this->overlayStack[secondOverlayNumber])) + { + // Set the change flag. + this->bChangeSinceLastRender = true; + } + } + else + { + // A given overlay does not exist. + this->lastError = -20; + } + } + else + { + // Overlays are disabled. + this->lastError = -21; + } + + // Exit function. + return result; +} + +bool Common::Renderer::Renderer_Text_Console::Duplicate_Overlay(const size_t & sourceOverlayNumber, const size_t & destOverlayNumber) +{ + // Init vars. + bool result = false; // The result of this function. + + // Check to see if overlays are enabled. + if (this->bOverlaysEnabled) + { + // Check to make sure that the given overlays are not the same overlay. + if (sourceOverlayNumber != destOverlayNumber) + { + // Begin try block. + try{ + // Check to see if the given overlay numbers exist. + if (((sourceOverlayNumber >= 0) && (sourceOverlayNumber < this->overlayStack.size())) && ((destOverlayNumber >= 0) && (destOverlayNumber < this->overlayStack.size()))) + { + // Call Overlay::Duplicate_Overlay(). + if (!this->overlayStack[destOverlayNumber].Duplicate_Overlay(this->overlayStack[sourceOverlayNumber])) + { + // Error copying overlay, get last error. + this->lastError = -27; + + // Reset the overlay. + this->overlayStack[destOverlayNumber].Clear_Overlay_Data(); + this->overlayStack[destOverlayNumber].Reset_Overlay(); + } + else + { + // Set result. + result = true; + } + } + } + catch(...) + { + // Exception thrown while duplicating overlay, clearing dest overlay. + this->lastError = -23; + this->overlayStack[destOverlayNumber].Clear_Overlay_Data(); + this->overlayStack[destOverlayNumber].Reset_Overlay(); + } + } + else + { + // Given overlay offsets are the same. Cannot overwrite an overlay with itself. + this->lastError = -24; + } + } + else + { + // Overlays are disabled. + this->lastError = -21; + } + + // Exit function. + return result; +} + +const char * Common::Renderer::Renderer_Text_Console::Get_Last_Error_From_Overlay(const size_t & overlayNumber) const +{ + // Init result. + const char * result = NULL; // Holds the pointer to the error message string. (Default is to return the invalid argument message. (If the overlay is valid it will set unknown error.)) + + // Check and see if the given overlay exists. + if ((this->bOverlaysEnabled) && ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size()))) + { + // Get the overlay's last error. + result = this->overlayStack[overlayNumber].Get_Last_Error(); + } + + // Return the result. + return result; +} + +short Common::Renderer::Renderer_Text_Console::Get_Last_Error_Code_From_Overlay(const size_t & overlayNumber) const +{ + // Init result. + short result = COMMON_ERROR_INVALID_ARGUMENT; // Holds the error code from the overlay. (Default is to return invalid argument.) + + // Check and see if the given overlay exists. + if ((this->bOverlaysEnabled) && ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size()))) + { + // Get the overlay's last error code. + result = this->overlayStack[overlayNumber].Get_Last_Error_Code(); + } + + // Return the result. + return result; +} + +#ifndef _NDEBUG +const char * Common::Renderer::Renderer_Text_Console::Return_Current_Final_Image_Buffer() +{ + // Return the pointer. + return this->finalResult.Get_Overlay_Data(); +} + +const char * Common::Renderer::Renderer_Text_Console::Return_Current_Overlay_Image_Buffer(const size_t & overlayNumber) +{ + // Init result. + const char * result = NULL; + + // Get the correct overlay. + if ((overlayNumber >= 0) && (overlayNumber < this->overlayStack.size())) + { + result = this->overlayStack[overlayNumber].Get_Overlay_Data(); + } + + // Return the result. + return result; +} + +#endif diff --git a/src/Common/Src/Rendering_Subsystem/Renderer_Text_Console.h b/src/Common/Src/Rendering_Subsystem/Renderer_Text_Console.h new file mode 100644 index 0000000..bbd6740 --- /dev/null +++ b/src/Common/Src/Rendering_Subsystem/Renderer_Text_Console.h @@ -0,0 +1,557 @@ +/* + Multiverse Engine Project Common Rendering_Subsystem Renderer_Text_Console.h 20/12/2013 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Include guard. +#ifndef COMMON_RENDER_SUBSYSTEM_H +#error "You must include the Renderer.h header file. It will include all of the other needed headers." +#endif + +//#ifndef COMMON_RENDER_DATA_STRUCT_H +//#define COMMON_RENDER_DATA_STRUCT_H + +#define TEXT_CONSOLE_RENDERER_TRANSPARENCY_VALUE '1' + +// Declare namespaces. +namespace Common +{ + namespace Renderer + { + // Declare the text console error table. + /*! + * const static Common_Error_Object TEXT_CONSOLE_ERROR_TABLE[] + * + * Contains a mapping of error messages and their respective error + * codes. + * + * Used to display human readable error messages when + * Renderer_Text_Console::Get_Last_Error() is called. + */ + const static Common_Error_Object TEXT_CONSOLE_ERROR_TABLE[] = { + {0, "No error."}, + {-1, "Unknown error code."}, + {-3, "Function not supported with this renderer."}, + {-10, "Could not create a new overlay in the overlay stack."}, + {-11, "Could not allocate memory for overlay image buffer."}, + {-12, "Unable to allocate memory for finalImage buffer."}, + {-13, "Exception thrown while attempting to allocate memory buffer(s)."}, + {-14, "You cannot use a newline (\\n) character as a blank value."}, + {-15, "Internal Error, finalResult buffer is not allocated while trying to output to console."}, + {-16, "Internal Error, working buffer is not allocated while attempting an operation on it."}, + {-17, "No overlay exists with the given offset."}, + {-18, "No text given to blit."}, + {-19, "An exception was thrown while attempting to create the overlay."}, + {-20, "A given overlay does not exist."}, + {-21, "Overlays are disabled."}, + {-22, "Source overlay is not set up correctly, it does not have an allocated memory buffer, destroying source overlay."}, + {-23, "Exception thrown while duplicating overlay, clearing dest overlay."}, + {-24, "Given overlay offsets are the same. Cannot overwrite an overlay with itself."}, + {-25, "Unable to allocate memory for fake transparency data generation."}, + {-26, "Unable to generate fake transparency data."}, + {-27, "Error duplicating overlay. Check the overlay for the real error."} + }; + + /*! + * Common::Renderer::Get_TEXT_CONSOLE_ERROR_TABLE_Size() + * + * Returns the size (number of elements) of the TEXT_CONSOLE_ERROR_TABLE. + */ + size_t Get_TEXT_CONSOLE_ERROR_TABLE_Size(); + + /*! + * class Common::Renderer::Renderer_Text_Console + * + * This class is a renderer that supports a text-based console only. + * + * Notes about this renderer: + * This renderer uses a 2D plane to render it's images. + * + * This renderer's plane starts at coordnates (0,0), and expands by "falling" to the right. + * (I.e. There are no negative offsets / coordnates, and the top left corner of the screen + * is the origin point while the bottom right corner is the (resolution - 1) point. + * (AKA the (xScreenResolution, yResolution) point.)) + * + * Unless otherwise stated, all functions of this class will destroy any overlay that + * is considered invalid. (I.e. Missing a valid pointer to it's data.) + * + * All overlays are stored in an overlay "stack" that starts at zero. The number of created + * overlays - 1, is the last overlay in the stack. + * + * To get the correct overlayNumber / offset, use this formula: + * offset_of_overlay_in_overlay_stack = overlay_you_want_to_use - 1. + * + * Overlays are rendered starting with the last overlay in the overlay stack, and ending + * with the first overlay in the overlay stack, with the data from the previous overlays being + * overwritten with the current overlay's data if the current overlay overlaps the + * previous overlays. (Data from the previous overlays is kept intact only if the current + * overlay has a blank value at the same position.) + * This means the first overlay is the most visible, and the last overlay is the least + * visible. + * An example of this using a 7 layer overlay stack: + * Overlay Number: Visiblity: + * 0 ALWAYS + * 1 | + * 2 | + * 3 | + * 4 \ / + * 5 . + * 6 LEAST + * + * The blank value is used as a transpareny value on the overlays, and a blank space + * on the final image. + * Because the text console does not support color output, there is no seperation + * between transpareny and blank values. There is also no way to create a real + * translucent image. (I.e. The output cannot "appear" to be transparent.) + * + * The blank value is also forbidden from being a character that causes the output + * to jump to the next line. (This is to prevent errors in output as std::cout is used + * for output.) + * + * Fullscreen means whether or not the current resolution takes up the entire window / screen. + * (If fullscreen is true, the renderer assumes that after outputting xScreenResolution characters to the backend, that the next character + * outputted will appear on the next line automaticly. + * If this value is false, the renderer assumes it needs to manually output a newline character, after outputting xScreenResolution + * characters, to get further output to appear on the next line.) + */ + class Renderer_Text_Console + { + private: + bool bChangeSinceLastRender; // Whether or not a change that would affect the final image has occured since the last call to Render(). + bool bOverlaysEnabled; // Whether or not to use the overlay vector. + bool bFullscreen; // Whether or not the given resolution is the size of the screen. (Determines whether or not to output a newline after outputting xScreenResolution characters.) + short lastError; // Holds the error code of the last error that the renderer encountered. + size_t xScreenResolution; // Holds the horazontal (X axis) size of the window / screen. + size_t yScreenResolution; // Holds the vertical (Y axis) size of the window / screen. + size_t colorDepth; // Holds the color depth of the window / screen. + Overlay finalResult; // What is actually printed out to std::cout. (Or returned.) + char blankValue; // What is used to fill unused portions of overlays and the final image. (Could also be the transpareny color.) + std::vector > overlayStack; // The stack of overlays. + + // Private functions. + /*! + * bool Common::Renderer::Renderer_Text_Console::Generate_Fake_Transparency_Data(const char * dataArray, const size_t & dataLength, char ** transparencyData) + * + * Generates fake transparency data for the renderer to send to the overlay blit function. + * (Transparency data is required by the overlay object, but transparency beyond seeing + * something behind a given overlay layer is not possible on a text console, as the text + * console has no concept of displaying bitmap images.) + * + * Double delete / free Warning: transparencyData will be deallocated (by calling delete) if transparencyData + * when this function is called is not equal to NULL. + * + * Below is copied from the function itself: + * + * For each value in dataArray that is not blankValue, + * make the corrosponding value in transparencyData '1'. + * + * (This performs the same effect as the original blank value. + * I.e. If the blankValue is not present at that location, + * the renderer considers it part of the image. + * + * If the blankValue is present at that location, then the + * renderer considers that location blank, and anything + * under that overlay on the final image will not be altered. + * + * Short version: Fake transparency, as the text console does + * not display actual images only characters.) + * + * Returns true if the generation is successfull. (transparencyData will be non-NULL and have the same length as defined by dataLength.) + * Returns false otherwise. (Note: If false is returned, then transparencyData will be deallocated and set to NULL.) + */ + bool Generate_Fake_Transparency_Data(const char * dataArray, const size_t & dataLength, char *& transparencyData); + + /*! + * static void Common::Renderer::Renderer_Text_Console::Calculate_Transparency(const char & srcImgVal, const char & srcTransVal, char & destImgVal, char & destTransVal) + * + * Internal transparency calculation function used by Common::Renderer::Overlay::Blit(). + * + * This function is called by an overlay that is created by this renderer to perform + * transparency calculations for the image data. + * + * The reason for this is that only the renderer knows how to calculate transparency, as only the + * renderer knows how to interpret the data stored in the overlay. + * + * The result is calculated based on the current values of the arguments when the function is called. + * The results are then stored in destImgVal and destTransVal. + * (I.e. destImgVal and destTransVal are used in the calculation and the results from the calculation + * overwrite destImgVal and destTransVal.) + * + * Pram: const char & srcImgVal, source overlay's image value. (I.e. the image screen element value.) + * Pram: const char & srcTransVal, source overlay's transparency value. (I.e. the transparency screen element value.) + * Pram: char & destImgVal, destionation overlay's image value. (I.e. the image screen element value.) + * Pram: char & destTransVal, destionation overlay's transparency value. (I.e. the transparency screen element value.) + */ + static void Calculate_Transparency(const char & srcImgVal, const char & srcTransVal, char & destImgVal, char & destTransVal); + protected: + + public: + Renderer_Text_Console() + { + bChangeSinceLastRender = false; + bOverlaysEnabled = false; + bFullscreen = false; + lastError = 0; + xScreenResolution = 0; + yScreenResolution = 0; + colorDepth = 0; + blankValue = ' '; + } + + ~Renderer_Text_Console() + { + // Free the overlayStack. + overlayStack.clear(); + } + + /*! + * bool Common::Renderer::Renderer_Text_Console::Change_Resolution(const size_t & xScreenResolution, const size_t & yScreenResolution, const size_t & colorDepth, const bool & fullscreen) + * + * This function changes the resolution of the final image. + * + * If overlays are disabled, then this function will destroy the working buffer, and destroy the final image buffer, then recreate them. + * If overlays are enabled, then only the final image buffer will be destroyed, and recreated. + * + * Pram: xScreenResolution, the new horazontal resolution of the final image. + * + * Pram: yScreenResolution, the new vertical resolution of the final image. + * + * Pram: colorDepth, the colorDepth of the final image. (Currently not used at all.) + * + * Pram: fullscreen, whether or not the given xScreenResolution and yScreenResolution is the resolution of the actual screen / window. + * (If this is true, the renderer assumes that after outputting xScreenResolution characters to the backend, that the next character + * outputted will appear on the next line automaticly. + * If this value is false, the renderer assumes it needs to manually output a newline character, after outputting xScreenResolution + * characters, to get further output to appear on the next line.) + * + * Returns true if resolution change was successful, and all of the buffers were reallocated successfully. + * Returns false otherwise. + */ + bool Change_Resolution(const size_t & xScreenResolution, const size_t & yScreenResolution, const size_t & colorDepth, const bool & fullscreen); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Set_Blank_Value(const char & value) + * + * This function sets the blank value that is used for transpareny (during rendering) and empty space on the final image and all overlays. (if enabled.) + * + * Note: You cannot use the newline character, ('\n'), as the blank value. + * + * Pram: value, character to use as the blank value. + */ + bool Set_Blank_Value(const char & value); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Set_Blank_Value(const size_t & overlayNumber, const char & value) + * + * This function sets the blank value that is used for transpareny (during rendering) and empty space on the given overlay. + * + * Note: You cannot use the newline character, ('\n'), as the blank value. + * + * Pram: value, character to use as the blank value. + */ + bool Set_Blank_Value(const size_t & overlayNumber, const char & value); + + /*! + * char Common::Renderer::Renderer_Text_Console::Get_Blank_Value() const + * + * Returns the current blank value used by the renderer. + */ + char Get_Blank_Value() const; + + /*! + * char Common::Renderer::Renderer_Text_Console::Get_Blank_Value(const size_t & overlayNumber) const + * + * Returns the current blank value used by the given overlay. + * + * Note: If the given overlay does not exist, then this function will silently fail, + * returning the renderer's blankValue. + */ + char Get_Blank_Value(const size_t & overlayNumber) const; + + /*! + * const char * Common::Renderer::Renderer_Text_Console::Get_Last_Error() const + * + * Returns a human readable string of the last error that the renderering backend encountered. + */ + const char * Get_Last_Error() const; + + /*! + * short Common::Renderer::Renderer_Text_Console::Get_Last_Error_Code() const + * + * Returns the last error code that the renderering backend encountered. + */ + short Get_Last_Error_Code() const; + + /*! + * bool Common::Renderer::Renderer_Text_Console::Render() + * + * "Renders" the overlays / working buffer to the final image, then sends the final + * image to std::cout. + * + * Note: This function blits from the top left corner to the bottom right corner of + * the final image. + * + * Returns true if image was successfully sent to std::cout. + * Returns false otherwise. + */ + bool Render(); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Blit_Text(const char * textString, const size_t & textStringXResolution, const size_t & textStringYResolution, const size_t & xCoordOffset, const size_t & yCoordOffset, const size_t & overlayNumber) + * + * Takes the given c-string and blits it to the given overlay, at the given offsets on that overlay. + * (Note: If overlays are disabled, it blits the given text to the working buffer, but only if the given overlay is not changed from the default.) + * + * This function will continue bliting on the next line if the newline character, ('\n'), is found in the given c-string. + * (This allows multiple lines to be blited at once.) + * + * If while bliting the given c-string, the function reaches the end of the given overlay / working buffer, + * then this function will not blit the remaining data, and return true. + * (All data blited prior to this will remain uneffected.) + * + * If while bliting the given c-string, the function reaches the end of the current line on the final image, + * any data left on that line in the given c-string will be skipped. + * + * Note: This function blits from the top left corner to the bottom right corner of the given overlay / working buffer. + * + * Pram: textString, a pointer that points to the actual text to copy to the given overlay. + * Pram: textStringXResolution, horazontal resolution of textString. Multiplied with textStringYResolution to get the total textString length. Must be defined by the caller. + * Pram: textStringYResolution, vertical resolution of textString. Multipled with textStringXResolution to get the total textString length. Defaults to one (1) so that single line strings only need textStringXResolution defined. + * Pram: xCoordOffset, horazontal offset where the given c-string will start being blited to on the overlay / working buffer. Defaults to the top left corner of the given overlay. (Coordnate pair: 0, 0) + * Pram: yCoordOffset, vertical offset where the given c-string will start being blited to on the overlay / working buffer. Defaults to the top left corner of the given overlay. (Coordnate pair: 0, 0) + * Pram: overlayNumber, overlay to blit the given c-string to. (Default is the top overlay, 0. (This is also the working buffer if overlays are disabled.)) + * Pram: const bool & lineWrap, if true, any remaining data on the current dataArray line will be blited to the next overlay line if the end of + * the current overlay line is reached. Otherwise any remaining data on the current dataArray line not be blited onto the overlay. Defaults to false. + */ + bool Blit_Text(const char * textString, const size_t & overlayNumber = 0, const bool & lineWrap = false, const size_t & textStringXResolution = 0, const size_t & textStringYResolution = 0, const size_t & xCoordOffset = 0, const size_t & yCoordOffset = 0); + + // Declare overlay functions. + /*! + * short Common::Renderer::Renderer_Text_Console::Get_Overlay_Debugging_Level(const size_t & overlayNumber) + * + * Returns the given overlay's current debugging level. + * + * If the given overlay does not exist, error level zero (0) is returned. + */ + short Get_Overlay_Debugging_Level(const size_t & overlayNumber); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Set_Overlay_Debugging_Level(const size_t & overlayNumber, const short & level) + * + * Sets the given overlay's debugging level. + * + * Returns true if successfull. + * Returns false otherwise. + */ + bool Set_Overlay_Debugging_Level(const size_t & overlayNumber, const short & level); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Create_Overlay(const size_t & xResolution, const size_t & yResolution, const long & xCoordOffset, const long & yCoordOffset) + * + * Creates a new overlay with the given resolution / final image offsets, and inserts it into + * the overlay stack at the bottom of the stack. + * (This means the newly created overlay is the least visible overlay.) + * + * Pram: xResolution, the horazontal resolution of the new overlay. + * Pram: yResolution, the vertical resolution of the new overlay. + * Pram: xCoordOffset, the horazontal offset on the final image where the new overlay's top left corner will be placed. + * Pram: yCoordOffset, the vertical offset on the final image where the new overlay's top left corner will be placed. + * + * Returns true if the overlay was created and inserted into the overlay stack successfully. + * Returns false otherwise. + */ + bool Create_Overlay(const size_t & xResolution, const size_t & yResolution, const long & xCoordOffset, const long & yCoordOffset); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Destroy_Overlay(const size_t & overlayNumber) + * + * Destroys the given overlay. + * + * Pram: overlayNumber, the offset in the overlay stack for the requested overlay. + * + * Returns true if the given overlay was successfully destroyed. + * Returns false otherwise. + */ + bool Destroy_Overlay(const size_t & overlayNumber); + + /*! + * void Common::Renderer::Renderer_Text_Console::Clear_Overlay(const size_t & overlayNumber) + * + * Overwrites the entirity of the given overlay with the current blank value. + * + * Pram: overlayNumber, the offset in the overlay stack for the requested overlay. + */ + void Clear_Overlay(const size_t & overlayNumber); + + /*! + * void Common::Renderer::Renderer_Text_Console::Reset_Overlay(const size_t & overlayNumber) + * + * Deallocates any of the given overlay's allocated memory buffers, and sets the given overlay back to it's default state. + * + * Pram: overlayNumber, the offset in the overlay stack for the requested overlay. + */ + void Reset_Overlay(const size_t & overlayNumber); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Change_Overlay_Resolution(const size_t & overlayNumber, const size_t & xScreenResolution, const size_t & yScreenResolution, const size_t & colorDepth, const bool & fullscreen) + * + * This function changes the resolution of the given overlay. + * + * Pram: overlayNumber, the offset in the overlay stack for the requested overlay. + * + * Pram: xScreenResolution, the new horazontal resolution for the given overlay. + * + * Pram: yScreenResolution, the new vertical resolution for the given overlay. + * + * Pram: colorDepth, the colorDepth for the given overlay. (Currently not used at all.) + * + * Pram: fullscreen, whether or not the given xScreenResolution and yScreenResolution is the resolution of the actual screen / window. + * + * Returns true if resolution change was successful, and all of the buffers were reallocated successfully. + * Returns false otherwise. + */ + bool Change_Overlay_Resolution(const size_t & overlayNumber, const size_t & xScreenResolution, const size_t & yScreenResolution, const size_t & colorDepth, const bool & fullscreen); + + /*! + * void Common::Renderer::Renderer_Text_Console::Get_Overlay_Offsets(long & xCoordOffset, long & yCoordOffset, const size_t & overlayNumber) + * + * Retrives the current offsets for the given overlay. + * + * Pram: xCoordOffset, will hold the current x axis offset for the given overlay if successful. Otherwise no change will be made. + * Pram: yCoordOffset, will hold the current y axis offset for the given overlay if successful. Otherwise no change will be made. + * Pram: overlayNumber, the offset in the overlay stack for the requested overlay. (Default is the top overlay in the overlay stack, or the working buffer if overlays are disabled.) + */ + void Get_Overlay_Offsets(long & xCoordOffset, long & yCoordOffset, const size_t & overlayNumber = 0); + + /*! + * void Common::Renderer::Renderer_Text_Console::Set_Overlay_Offsets(const long & xCoordOffset, const long & yCoordOffset, const size_t & overlayNumber) + * + * Moves the entire given overlay to the given position on the final image. (Transform overlay by changing it's starting offsets.) + * + * Pram: xCoordOffset, the new horazontal offset on the final image where the overlay's top left corner will be placed. + * Pram: yCoordOffset, the new vertical offset on the final image where the overlay's top left corner will be placed. + * Pram: overlayNumber, the offset in the overlay stack for the requested overlay. (Default is the top overlay in the overlay stack, or the working buffer if overlays are disabled.) + */ + void Set_Overlay_Offsets(const long & xCoordOffset, const long & yCoordOffset, const size_t & overlayNumber = 0); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Swap_Overlays(const size_t & firstOverlayNumber, const size_t & secondOverlayNumber) + * + * Swaps the positions of the two given overlays in the overlay stack. + * + * Pram: firstOverlayNumber, the offset of an overlay to swap. + * Pram: secondOverlayNumber, the offset of an overlay to swap. + * + * Returns true if the overlays were swapped successfully. + * Returns false otherwise. + */ + bool Swap_Overlays(const size_t & firstOverlayNumber, const size_t & secondOverlayNumber); + + /*! + * bool Common::Renderer::Renderer_Text_Console::Duplicate_Overlay(const size_t & sourceOverlayNumber, const size_t & destOverlayNumber) + * + * Overwrites the destionation overlay with the source overlay. + * (The result is two identical overlays in the overlay stack + * at the given positions within the overlay stack.) + * + * This function will NOT clear the source overlay. + * + * Note: This function will NOT create the destionation overlay if it does not exist + * when this function is called. If the destionation overlay does not exist, then this + * function will fail. + * + * Note: This function CAN overwrite an invalid destionation overlay. + * + * Pram: sourceOverlayNumber, the offset of the overlay to duplicate. + * Pram: destOverlayNumber, the offset of the overlay that will be overwritten. + * + * Returns true if the source overlay was copied to the destionation overlay successfully. + * Returns false otherwise. + */ + bool Duplicate_Overlay(const size_t & sourceOverlayNumber, const size_t & destOverlayNumber); + + /*! + * const char * Common::Renderer::Renderer_Text_Console::Get_Last_Error_From_Overlay() const + * + * Returns a human readable string of the last error that the given overlay encountered. + * + * Note: If the given overlay does not exist or overlay support is disabled, then the returned error message will be + * the default unknown error code message. + */ + const char * Get_Last_Error_From_Overlay(const size_t & overlayNumber) const; + + /*! + * short Common::Renderer::Renderer_Text_Console::Get_Last_Error_Code_From_Overlay() const + * + * Returns the last error code that the given overlay encountered. + * + * Note: If the given overlay does not exist or overlay support is disabled, then the returned error message will be + * the default unknown error code. + */ + short Get_Last_Error_Code_From_Overlay(const size_t & overlayNumber) const; +#ifndef _NDEBUG + /*! + * const char * Common::Renderer::Renderer_Text_Console::Return_Current_Final_Image_Buffer() + * + * A debug function that does exactly what it says, it returns a pointer to the current final + * image buffer. + * + * This pointer can become invalid if the internal final image buffer is reallocated. + * (So you should probably use the pointer to copy the data it points to the second you get it, + * or not do anything else with the renderer until you are done with that data. (Unless you + * want that segfault anyway.) As a sidenote: I realize that this seems to come off as rude, + * however I think that the point needs to be made: This is NOT a standard function! It only + * exists to debug the output from the renderer, so don't expect it to work in a release build, + * and don't use it for a purpose other than DEBUGGING!) + * + * Returns a pointer to the current final image buffer if it is allocated when this function is called. + * Returns NULL otherwise. + */ + const char * Return_Current_Final_Image_Buffer(); + + /*! + * const char * Common::Renderer::Renderer_Text_Console::Return_Current_Overlay_Image_Buffer(const size_t & overlayNumber) + * + * A debug function that does exactly what it says, it returns a pointer to the given overlay's + * image data buffer. + * + * This pointer can become invalid if the internal overlay image buffer is reallocated. + * (So you should probably use the pointer to copy the data it points to the second you get it, + * or not do anything else with the renderer until you are done with that data. (Unless you + * want that segfault anyway.) As a sidenote: I realize that this seems to come off as rude, + * however I think that the point needs to be made: This is NOT a standard function! It only + * exists to debug the output from the renderer, so don't expect it to work in a release build, + * and don't use it for a purpose other than DEBUGGING!) + * + * Pram: overlayNumber, the offset in the overlay stack for the requested overlay. + * Default is the working buffer if overlays are disabled (provided the overlayNumber + * is not changed), and the first (most visible) overlay in the overlay stack, if + * overlays are enabled. + * + * Returns a pointer to the current overlay image buffer if it is allocated when this function is called. + * Returns NULL otherwise. + */ + const char * Return_Current_Overlay_Image_Buffer(const size_t & overlayNumber = 0); +#endif + }; + }; +}; + +//#endif + +// End of Renderer_Text_Console.h diff --git a/src/Common/Src/Threading_Subsystem/CMakeLists.txt b/src/Common/Src/Threading_Subsystem/CMakeLists.txt index 0e95194..c20d1d8 100644 --- a/src/Common/Src/Threading_Subsystem/CMakeLists.txt +++ b/src/Common/Src/Threading_Subsystem/CMakeLists.txt @@ -43,7 +43,7 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") # Create the Pthreads Plugin. set(PTHREADS_PLUGIN_INCLUDES ../Generic_Wrapper.cpp Thread_Utils_Base.cpp - Thread_Utils_pthread.cpp + Posix_Threads/Thread_Utils_pthread.cpp Posix_Threads/Thread_Utils_pthread_Plugin.cpp) ELSEIF(${CMAKE_SYSTEM_NAME} MATCHES "Linux") @@ -57,7 +57,7 @@ IF(${CMAKE_SYSTEM_NAME} MATCHES "Windows") # Create the Pthreads Plugin. set(PTHREADS_PLUGIN_INCLUDES ../Generic_Wrapper.cpp Thread_Utils_Base.cpp - Thread_Utils_pthread.cpp + Posix_Threads/Thread_Utils_pthread.cpp Posix_Threads/Thread_Utils_pthread_Plugin.cpp) ELSE(${CMAKE_SYSTEM_NAME} MATCHES "Windows") @@ -90,4 +90,4 @@ if (BUILD_THREAD_SUBSYS_PTHREADS_PLUGIN) # Set output directories for plugins. set_target_properties(Pthreads_Plugin_Multiverse_Engine PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGIN_OUTPUT_DIR}) -endif (BUILD_THREAD_SUBSYS_PTHREADS_PLUGIN) \ No newline at end of file +endif (BUILD_THREAD_SUBSYS_PTHREADS_PLUGIN) diff --git a/src/Core/Src/BaseHeader.h b/src/Core/Src/BaseHeader.h index 0846d01..992a0eb 100644 --- a/src/Core/Src/BaseHeader.h +++ b/src/Core/Src/BaseHeader.h @@ -30,5 +30,3 @@ #include #include #include - -using namespace std; diff --git a/src/Core/Src/Basic_Linked_List.c b/src/Core/Src/Basic_Linked_List.c new file mode 100644 index 0000000..e841426 --- /dev/null +++ b/src/Core/Src/Basic_Linked_List.c @@ -0,0 +1,601 @@ +/*! + Multiverse Engine Project 05/7/2015 Basic_Linked_List.c + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Begin extern C if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Internal includes. */ +#include "Basic_Linked_List.h" +#include "DataProcess.h" + +/* External includes. */ +#include /* Define malloc() & free(). */ +#include /* Define memset(). */ + +void Blank_MSYS_Linked_List_Object(MSYS_Linked_List_T * list) +{ + /* Check for valid arguments. */ + if (list != NULL) + { + /* Check to see if we should have allocated memory. */ + if (list->allocated) + { + /* Check for allocated memory. */ + if (list->data != NULL) + { + /* Deallocate memory. */ + DataProcess_Deallocate_CString(((char**)(&(list->data)))); + } + } + + /* Blank out the list object. */ + list->allocated = 0; + list->data = NULL; + list->dataLength = 0; + list->nextObject = NULL; + list->prevObject = NULL; + } + + /* Exit function. */ + return; +} + +int MSYS_Linked_List_Allocate_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + MSYS_Linked_List_T * pList = NULL; /* Temporary pointer used to create the list object. */ + + /* Check for valid args. */ + if (ppAllocatedList != NULL) + { + /* Allocating new object. */ + pList = (MSYS_Linked_List_T *)malloc(sizeof(MSYS_Linked_List_T)); + if (pList != NULL) + { + /* Blank out the list. */ + Blank_MSYS_Linked_List_Object(pList); + + /* Determine if we are creating a new list or appending to an existing one. */ + if ((*ppAllocatedList) != NULL) + { + /* Appending to an existing list, determine if we are at the end of the list. */ + if ((*ppAllocatedList)->nextObject != NULL) + { + /* Only update the next object's prevObject pointer if it points to the current object. */ + if ((*ppAllocatedList)->nextObject->prevObject == (*ppAllocatedList)) + { + (*ppAllocatedList)->nextObject->prevObject = pList; + } + + /* Somewhere within the list, need to update the pointers. */ + pList->prevObject = (*ppAllocatedList); + pList->nextObject = (*ppAllocatedList)->nextObject; + } + + /* Set the next pointer. */ + (*ppAllocatedList)->nextObject = pList; + } + else + { + /* Creating a new list. */ + (*ppAllocatedList) = pList; + } + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +MSYS_DLL_EXPORT int MSYS_Linked_List_Allocate_And_Return_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList, MSYS_Linked_List_T ** ppAllocatedObject) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + MSYS_Linked_List_T * tempPtr = NULL; /* Temp pointer to keep track of the original state. */ + + /* Check for a defined originalPointer argument. */ + if (ppAllocatedObject != NULL) + { + /* Check and see if the original pointer is valid. */ + if ((ppAllocatedList != NULL) && ((*ppAllocatedList) != NULL)) + { + /* We need to copy the original pointer so we can return a pointer to the allocated object. */ + tempPtr = (*ppAllocatedList); + + /* Call the original allocator. */ + ret = MSYS_Linked_List_Allocate_Linked_List_Object(ppAllocatedList); + if ((ret == COMMON_ERROR_SUCCESS) && (ppAllocatedList != NULL)) + { + /* The pointer to the allocated object is determined by the state of the original pointer passed to the allocator. */ + (*ppAllocatedObject) = ((tempPtr == NULL) ? (*ppAllocatedList) : ((*ppAllocatedList)->nextObject)); + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* Just call the original allocator. */ + ret = MSYS_Linked_List_Allocate_Linked_List_Object(ppAllocatedList); + } + + /* Exit function. */ + return ret; +} + +void MSYS_Linked_List_Deallocate_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList) +{ + /* Init vars. */ + MSYS_Linked_List_T * pList = NULL; /* Temporary pointer used to deallocate the given object. + * (Just in case we get called with an object's nextObject or prevObject + * pointer as the current object.) + */ + + /* Check for valid args. */ + if ((ppAllocatedList != NULL) && ((*ppAllocatedList) != NULL)) + { + /* Copy the pointer. */ + pList = (*ppAllocatedList); + + /* Check for other objects to update. */ + if ((pList->prevObject != NULL) || (pList->nextObject != NULL)) + { + /* Check and see if both the previous object and the next object are defined. */ + if ((pList->prevObject != NULL) && (pList->nextObject != NULL)) + { + /* Simple stiching, but make sure that the next and previous objects actually point to the current one before altering them. */ + if (pList->prevObject->nextObject == pList) + { + pList->prevObject->nextObject = pList->nextObject; + } + if (pList->nextObject->prevObject == pList) + { + pList->nextObject->prevObject = pList->prevObject; + } + } + else + { + /* OK, figure out what object needs to be updated. */ + if (pList->prevObject != NULL) + { + /* Set the previous object's next object pointer to NULL, + * but make sure that the previous object actually points + * to the current one before altering it. + */ + if (pList->prevObject->nextObject == pList) + { + pList->prevObject->nextObject = NULL; + } + } + if (pList->nextObject != NULL) + { + /* Set the next object's previous object pointer to NULL, + * but make sure that the next object actually points + * to the current one before altering it. + */ + if (pList->nextObject->prevObject == pList) + { + pList->nextObject->prevObject = NULL; + } + } + } + } + + /* Blank and deallocate the current object. */ + Blank_MSYS_Linked_List_Object((*ppAllocatedList)); + free((*ppAllocatedList)); + (*ppAllocatedList) = NULL; + } + + /* Exit function. */ + return; +} + +void MSYS_Linked_List_Deallocate_Entire_List(MSYS_Linked_List_T ** ppAllocatedList) +{ + /* Init vars. */ + int retFromFunct = COMMON_ERROR_UNKNOWN_ERROR; /* The result code from MSYS_Linked_List_*() functions. */ + MSYS_Linked_List_T * nextObject = NULL; /* The next object in the list, after the current one. */ + MSYS_Linked_List_T * currentObject = NULL; /* The current object we are looking at in the list. */ + MSYS_Linked_List_T * lastObject = NULL; /* The last object in the list. */ + + /* Check for invalid arguments. */ + if ((ppAllocatedList != NULL) && ((*ppAllocatedList) != NULL)) + { + /* Get the first object in the list. */ + retFromFunct = MSYS_Linked_List_Get_First_Object((*ppAllocatedList), ¤tObject); + if (retFromFunct == COMMON_ERROR_SUCCESS) + { + /* Get the last object in the list. */ + retFromFunct = MSYS_Linked_List_Get_Last_Object((*ppAllocatedList), &lastObject); + if (retFromFunct == COMMON_ERROR_SUCCESS) + { + /* Begin deallocation loop. */ + while ((retFromFunct == COMMON_ERROR_SUCCESS) && (currentObject != lastObject)) + { + /* Get the next object. */ + retFromFunct = MSYS_Linked_List_Get_Next_Object(currentObject, &nextObject); + if (retFromFunct == COMMON_ERROR_SUCCESS) + { + /* Deallocate the current object. */ + MSYS_Linked_List_Deallocate_Linked_List_Object(¤tObject); + + /* Copy the pointer for the next object to the current one. */ + currentObject = nextObject; + } + } + } + } + } + + /* Exit function. */ + return; +} + +int MSYS_Linked_List_Get_First_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppFirstObject) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + const MSYS_Linked_List_T * currentObject = NULL; /* The current object we are looking at in the list. */ + + /* Check for valid args. */ + if ((pAllocatedList != NULL) && (ppFirstObject != NULL)) + { + /* Copy the pointer. */ + currentObject = pAllocatedList; + + /* Set ret to COMMON_ERROR_INTERNAL_ERROR. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + + /* Search for the first object. + * (The first object in the list has it's prevObject pointer set to NULL.) + * Also the extra check is to prevent jumping out of our chain. + */ + while ((ret == COMMON_ERROR_INTERNAL_ERROR) && + (currentObject->prevObject != NULL) && + (currentObject->prevObject->nextObject == currentObject) && + (currentObject->prevObject->prevObject != currentObject)) + { + /* Check and see if the next object is going to end the loop. */ + if (currentObject->prevObject->prevObject == NULL) + { + /* Copy the pointer for the first object. */ + (*ppFirstObject) = currentObject->prevObject; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Switch to previous object. */ + currentObject = currentObject->prevObject; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Linked_List_Get_Last_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppLastObject) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + const MSYS_Linked_List_T * currentObject = NULL; /* The current object we are looking at in the list. */ + + /* Check for valid args. */ + if ((pAllocatedList != NULL) && (ppLastObject != NULL)) + { + /* Copy the pointer. */ + currentObject = pAllocatedList; + + /* Set ret to COMMON_ERROR_INTERNAL_ERROR. */ + ret = COMMON_ERROR_INTERNAL_ERROR; + + /* Search for the last object. + * (The last object in the list has it's nextObject pointer set to NULL.) + * Also the extra check is to prevent jumping out of our chain. + */ + while ((ret == COMMON_ERROR_INTERNAL_ERROR) && + (currentObject->nextObject != NULL) && + (currentObject->nextObject->prevObject == currentObject) && + (currentObject->nextObject->nextObject != currentObject)) + { + /* Check and see if the next object is going to end the loop. */ + if (currentObject->nextObject->nextObject == NULL) + { + /* Copy the pointer for the last object. */ + (*ppLastObject) = currentObject->nextObject; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + + /* Switch to next object. */ + currentObject = currentObject->nextObject; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Linked_List_Get_Next_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppNextObject) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + + /* Check for valid args. */ + if ((pAllocatedList != NULL) && (ppNextObject != NULL)) + { + /* Copy the pointer to the next object. */ + (*ppNextObject) = pAllocatedList->nextObject; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Linked_List_Get_Previous_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppPrevObject) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + + /* Check for valid args. */ + if ((pAllocatedList != NULL) && (ppPrevObject != NULL)) + { + /* Copy the pointer to the previous object. */ + (*ppPrevObject) = pAllocatedList->prevObject; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Linked_List_Get_Current_Object_Contents(const MSYS_Linked_List_T * pAllocatedList, void ** ppData, size_t * dataLength, const int copyData) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code from a call to an engine function. */ + char * tempData = NULL; /* Temporary pointer used to copy the data. */ + + /* Check for valid args. */ + if ((pAllocatedList != NULL) && (ppData != NULL) && (dataLength != NULL)) + { + /* Check and see if we are copying the data. */ + if ((copyData) && (pAllocatedList->allocated) && (pAllocatedList->dataLength > 0)) + { + /* Allocate memory for the copy. */ + retFromCall = DataProcess_Reallocate_C_String(&tempData, 0, (pAllocatedList->dataLength)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempData != NULL)) + { + /* Copy data. */ + memcpy(tempData, (pAllocatedList->data), (pAllocatedList->dataLength)); + + /* Copy the pointer and length. */ + (*ppData) = tempData; + (*dataLength) = pAllocatedList->dataLength; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for data copy. */ + ret = ((retFromCall != COMMON_ERROR_MEMORY_ERROR) ? (COMMON_ERROR_INTERNAL_ERROR) : + (COMMON_ERROR_MEMORY_ERROR)); + } + } + else + { + /* Copy the pointer and length. */ + (*ppData) = pAllocatedList->data; + (*dataLength) = pAllocatedList->dataLength; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Linked_List_Get_Allocated(const MSYS_Linked_List_T * pAllocatedList, int * isAllocated) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + + /* Check for valid arguments. */ + if ((pAllocatedList != NULL) && (isAllocated != NULL)) + { + /* Copy the allocated flag. */ + (*isAllocated) = pAllocatedList->allocated; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Linked_List_Set_Current_Object_Contents(MSYS_Linked_List_T * pAllocatedList, void * pData, const size_t dataLength, const int copyData) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code from an engine function. */ + char * tempData = NULL; /* Temporary pointer used to allocate memory. */ + + /* Check for valid args. */ + if (pAllocatedList != NULL) + { + /* Blank the existing list. */ + Blank_MSYS_Linked_List_Object(pAllocatedList); + + /* Check and see if we need to copy the data instead of the pointer. */ + if (copyData) + { + /* Allocate memory. */ + retFromCall = DataProcess_Reallocate_C_String(&tempData, 0, dataLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempData != NULL)) + { + /* Copy the data. */ + memcpy(tempData, pData, dataLength); + + /* Copy the allocated pointer, and data length. */ + pAllocatedList->data = tempData; + pAllocatedList->dataLength = dataLength; + + /* Make sure allocated is true. */ + pAllocatedList->allocated = 1; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Copy the pointer and length. */ + pAllocatedList->data = pData; + pAllocatedList->dataLength = dataLength; + + /* Make sure allocated is false. */ + pAllocatedList->allocated = 0; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Linked_List_Swap_Objects(MSYS_Linked_List_T * pFirstObject, MSYS_Linked_List_T * pSecondObject) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + void * pData = NULL; /* Temporary pointer used to swap the data pointers. */ + size_t dataLength = 0; /* Temporary pointer used to swap the data lengths. */ + + /* Check for invalid arguments. */ + if ((pFirstObject != NULL) && (pSecondObject != NULL)) + { + /* Copy the pointers. */ + pData = pFirstObject->data; + pFirstObject->data = pSecondObject->data; + pSecondObject->data = pData; + pData = NULL; + + /* Copy the data lengths. */ + dataLength = pFirstObject->dataLength; + pFirstObject->dataLength = pSecondObject->dataLength; + pSecondObject->dataLength = dataLength; + dataLength = 0; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +MSYS_DLL_EXPORT void MSYS_Linked_List_Deallocate_Copied_Data(void ** ppData) +{ + /* Check for valid pointer. */ + if ((ppData != NULL) && ((*ppData) != NULL)) + { + /* Deallocate memory. */ + DataProcess_Deallocate_CString(((char**)ppData)); + } + + /* Exit function. */ + return; +} diff --git a/src/Core/Src/Basic_Linked_List.h b/src/Core/Src/Basic_Linked_List.h new file mode 100644 index 0000000..fa137ea --- /dev/null +++ b/src/Core/Src/Basic_Linked_List.h @@ -0,0 +1,251 @@ +/*! + Multiverse Engine Project 25/6/2015 Basic_Linked_List.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef MSYS_BASIC_LINKED_LIST_H +#define MSYS_BASIC_LINKED_LIST_H + +/* Begin extern C if needed. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Internal includes. */ +#include "../../DLL_PORT.h" /* Defines MSYS_DLL_EXPORT, and MSYS_DLL_IMPORT_TEMPLATE. */ +#include "../../Common/Src/Error_Handler/Common_Error_Handler_Error_Codes.h" /* Defines Common error codes used for return codes. */ + +/* External includes. */ +#include /* Define NULL, size_t. */ + +/* Define basic linked list object. */ +typedef struct MSYS_Linked_List { + int allocated; /* Whether or not we have used our allocator for the data we point to. */ + void * data; /* Data structure for this object in the chain. */ + size_t dataLength; /* Optional Length of the given data structure if data is non-NULL. */ + struct MSYS_Linked_List * nextObject; /* The next object in the chain, should be NULL if the current object is the last one in the chain. */ + struct MSYS_Linked_List * prevObject; /* The previous object in the chain, should be NULL if the current object is the first one in the chain. */ +} MSYS_Linked_List_T; + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Allocate_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList) + * + * Allocates a new MSYS_Linked_List object. + * + * If the given pointer to object is non-NULL, the newly allocated object will be pointed + * to by the given object's nextObject pointer. If given object's nextObject pointer is + * non-NULL, then the newly allocated object will be inserted between the given object and + * the object that came after it prior to the call to this function. + * + * Ex. + * ppAllocatedList nextObject pointer result: + * NULL N / A (*ppAllocatedList) points to allocated object. + * + * non-NULL NULL (*ppAllocatedList)->nextObject points to allocated object. + * + * non-NULL non-NULL (*ppAllocatedList)->nextObject points to allocated object, + * allocated object->nextObject points to nextObject. + * allocated object->prevObject points to (*ppAllocatedList). + * nextObject->prevObject points to allocated object. (ONLY TRUE IF nextObject->prevObject == (*ppAllocatedList)) + * + * Returns COMMON_ERROR_SUCCESS if allocation was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if given pointer to pointer was NULL. + * Returns COMMON_ERROR_MEMORY_ERROR if memory allocation for new object failed. + * Otherwise returns the appropriate error. + * + * If an error is returned, then the list will not be altered. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Allocate_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Allocate_And_Return_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList, MSYS_Linked_List_T ** ppAllocatedObject) + * + * This function is a wrapper around MSYS_Linked_List_Allocate_Linked_List_Object() + * that returns the allocated object's pointer via the function's ppAllocatedObject pointer. + * + * If ppAllocatedObject is NULL, then this function's behavior is identical to calling: + * MSYS_Linked_List_Allocate_Linked_List_Object(ppAllocatedList). + * + * See MSYS_Linked_List_Allocate_Linked_List_Object() for error code descriptions. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Allocate_And_Return_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList, MSYS_Linked_List_T ** ppAllocatedObject); + +/*! + * MSYS_DLL_EXPORT void MSYS_Linked_List_Deallocate_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList) + * + * Deallocates the given MSYS_Linked_List object, and sets it's pointer to NULL. + * + * If the given object points to any other MSYS_Linked_List object, then they will be modified to + * reflect the removal of the given object. + * I.e. The pointed to objects will have their nextObject and prevObject pointers updated. + * + * This function has no return, and will silently fail if the given pointer to pointer is NULL. + * Otherwise upon return, the given pointer to object will be set to NULL. + */ +MSYS_DLL_EXPORT void MSYS_Linked_List_Deallocate_Linked_List_Object(MSYS_Linked_List_T ** ppAllocatedList); + +/*! + * MSYS_DLL_EXPORT void MSYS_Linked_List_Deallocate_Entire_List(MSYS_Linked_List_T ** ppAllocatedList) + * + * Deallocates the given MSYS_Linked_List object and all other objects in it's list. Then sets the given + * argument to NULL. + * + * If the given object points to any other MSYS_Linked_List object, then they will deallocated as well. + * + * This function has no return, and will silently fail if the given pointer to pointer is NULL. + * Otherwise upon return, the given pointer to object will be set to NULL. + */ +MSYS_DLL_EXPORT void MSYS_Linked_List_Deallocate_Entire_List(MSYS_Linked_List_T ** ppAllocatedList); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Get_First_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppFirstObject) + * + * Copies the pointer to the first object in the list to the given ppFirstObject argument. + * + * Returns COMMON_ERROR_SUCCESS if copying the pointer to the first object was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer to pointer or pointer to object was NULL. + * Returns COMMON_ERROR_INTERNAL_ERROR if the given linked list is broken. (I.e. recursive.) + * Otherwise returns the appropriate error. + * + * If an error occurs, ppFirstObject will not be modified. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Get_First_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppFirstObject); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Last_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppLastObject) + * + * Copies the pointer to the last object in the list to the given ppLastObject argument. + * + * Returns COMMON_ERROR_SUCCESS if copying the pointer to the last object was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer to pointer or pointer to object was NULL. + * Returns COMMON_ERROR_INTERNAL_ERROR if the given linked list is broken. (I.e. recursive.) + * Otherwise returns the appropriate error. + * + * If an error occurs, ppLastObject will not be modified. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Last_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppLastObject); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Next_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppNextObject) + * + * Copies the pointer to the next object in the list to the given ppNextObject argument. + * + * Returns COMMON_ERROR_SUCCESS if copying the pointer to the next object was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer to pointer or pointer to object was NULL. + * Otherwise returns the appropriate error. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Next_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppNextObject); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Previous_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppPrevObject) + * + * Copies the pointer to the previous object in the list to the given ppPrevObject argument. + * + * Returns COMMON_ERROR_SUCCESS if copying the pointer to the previous object was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer to pointer or pointer to object was NULL. + * Otherwise returns the appropriate error. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Previous_Object(const MSYS_Linked_List_T * pAllocatedList, MSYS_Linked_List_T ** ppPrevObject); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Current_Object_Contents(const MSYS_Linked_List_T * pAllocatedList, void ** ppData, size_t * dataLength, const int copyData) + * + * Copies the contents of the given object in the list to the given pointers. + * + * @Pram: copyData, whether or not to copy the data the MSYS_LINKED_LIST_T object points to. + * (If applicable see below.) + * + * If copyData is non-zero, the given MSYS_LINKED_LIST_T object allocated (copied) the data it points to, + * and the given length of the data was greater than zero, then this function will copy the data from the + * given MSYS_LINKED_LIST_T object and return a pointer to the copy. + * This pointer should be deallocated by calling MSYS_Linked_List_Deallocate_Copied_Data(). + * + * If copyData is zero, the given MSYS_LINKED_LIST_T object did not allocate the data it points to, or + * the given length of the data was 0, then this function will only copy the MSYS_LINKED_LIST_T object's + * internal data pointer and dataLength. + * + * Returns COMMON_ERROR_SUCCESS if copying the contents was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer to pointer or pointer to data was NULL. + * Returns COMMON_ERROR_MEMORY_ERROR if memory allocation for coping data failed. + * Otherwise returns the appropriate error. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Current_Object_Contents(const MSYS_Linked_List_T * pAllocatedList, void ** ppData, size_t * dataLength, const int copyData); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Allocated(const MSYS_Linked_List_T * pAllocatedList, int * isAllocated) + * + * This function returns whether or not the given MSYS_LINKED_LIST_T object has allocated + * the data it points to or not via it's isAllocated argument. + * + * Returns COMMON_ERROR_SUCCESS if successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer to pointer or pointer to data was NULL. + * Otherwise returns the appropriate error code. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Get_Allocated(const MSYS_Linked_List_T * pAllocatedList, int * isAllocated); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Set_Current_Object_Contents(MSYS_Linked_List_T * pAllocatedList, void * pData, const size_t dataLength, const int copyData) + * + * Copies the given contents to the given object in the list. + * + * Pram: copyData, If non-zero the data pointed to by pData will be copied using the + * linked list library's internal allocator. + * If copyData is zero, then only the given pointer will be copied. + * + * Returns COMMON_ERROR_SUCCESS if copying the contents was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer to object was NULL. + * Returns COMMON_ERROR_MEMORY_ERROR if memory allocation for coping data failed. + * Otherwise returns the appropriate error. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Set_Current_Object_Contents(MSYS_Linked_List_T * pAllocatedList, void * pData, const size_t dataLength, const int copyData); + +/*! + * MSYS_DLL_EXPORT int MSYS_Linked_List_Swap_Objects(MSYS_Linked_List_T * pFirstObject, MSYS_Linked_List_T * pSecondObject) + * + * Swaps the given contents of the two given objects in the list. + * + * Returns COMMON_ERROR_SUCCESS if swapping the contents of the two objects was successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer to object was NULL. + * Otherwise returns the appropriate error. + */ +MSYS_DLL_EXPORT int MSYS_Linked_List_Swap_Objects(MSYS_Linked_List_T * pFirstObject, MSYS_Linked_List_T * pSecondObject); + +/*! + * MSYS_DLL_EXPORT void MSYS_Linked_List_Deallocate_Copied_Data(void ** ppData) + * + * This function deallocates data that was copied out of a MSYS_LINKED_LIST_T object + * by a call to MSYS_Linked_List_Get_Current_Object_Contents(). + * + * This function should ALWAYS be used to deallocate data that was copied out of a + * MSYS_LINKED_LIST_T object by a call to MSYS_Linked_List_Get_Current_Object_Contents(). + * + * If the given pointer is invalid this function will silently fail. + * + * This function has no return. + */ +MSYS_DLL_EXPORT void MSYS_Linked_List_Deallocate_Copied_Data(void ** ppData); + +/* End extern C if needed. */ +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* MSYS_BASIC_LINKED_LIST_H */ + +/* End of Basic_Linked_List.h. */ diff --git a/src/Core/Src/CMakeLists.txt b/src/Core/Src/CMakeLists.txt index 800ab92..17d45b2 100644 --- a/src/Core/Src/CMakeLists.txt +++ b/src/Core/Src/CMakeLists.txt @@ -3,10 +3,37 @@ set(LIBRARY_OUTPUT_PATH ${L_OUTPUT_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${O_OUTPUT_DIR}) # Add libraries. +add_library(DataProcess_Memory_Management_Multiverse_Engine SHARED DataProcess_Memory_Functions.c) +add_library(DataProcess_Memory_Management_Multiverse_Engine_Static STATIC DataProcess_Memory_Functions.c) + add_library(Panic_Handler_Multiverse_Engine SHARED Panic.cpp) add_library(Panic_Handler_Multiverse_Engine_Static STATIC Panic.cpp) -add_library(Core_Multiverse_Engine SHARED DataProcess.cpp FileStreams.cpp) -add_library(Core_Multiverse_Engine_Static STATIC DataProcess.cpp FileStreams.cpp) -target_link_libraries(Core_Multiverse_Engine Panic_Handler_Multiverse_Engine) -target_link_libraries(Core_Multiverse_Engine_Static Panic_Handler_Multiverse_Engine_Static) +add_library(Basic_Linked_List_Multiverse_Engine SHARED Basic_Linked_List.c) +add_library(Basic_Linked_List_Multiverse_Engine_Static STATIC Basic_Linked_List.c) + +target_link_libraries(Basic_Linked_List_Multiverse_Engine DataProcess_Memory_Management_Multiverse_Engine) +target_link_libraries(Basic_Linked_List_Multiverse_Engine_Static DataProcess_Memory_Management_Multiverse_Engine_Static) + +# Include the Data_Object code. +add_subdirectory(Data_Object) + +add_library(Core_Multiverse_Engine SHARED DataProcess.cpp +DataProcess.c +DataProcess_Endianness_Check.cpp +FileStreams.cpp) + +add_library(Core_Multiverse_Engine_Static STATIC DataProcess.cpp +DataProcess.c +DataProcess_Endianness_Check.cpp +FileStreams.cpp) + +target_link_libraries(Core_Multiverse_Engine Panic_Handler_Multiverse_Engine +Basic_Linked_List_Multiverse_Engine +DataProcess_Memory_Management_Multiverse_Engine +Data_Object_Multiverse_Engine) + +target_link_libraries(Core_Multiverse_Engine_Static Panic_Handler_Multiverse_Engine_Static +Basic_Linked_List_Multiverse_Engine_Static +DataProcess_Memory_Management_Multiverse_Engine_Static +Data_Object_Multiverse_Engine_Static) diff --git a/src/Core/Src/DataProcess.c b/src/Core/Src/DataProcess.c new file mode 100644 index 0000000..49d32e9 --- /dev/null +++ b/src/Core/Src/DataProcess.c @@ -0,0 +1,375 @@ +/*! + Multiverse Engine Project DataProcess DataProcess.c 07/8/2015 + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Set extern C. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Disable shitty M$ deprecations of the C standard. */ +#ifdef _MSC_FULL_VER +#define _CRT_SECURE_NO_WARNINGS +#endif /* _MSC_FULL_VER */ + +/* Internal includes. */ +#include "DataProcess.h" +#include "../../Common/Src/Error_Handler/Common_Error_Handler_Error_Codes.h" /* Defines Common Error Codes. */ + +/* External includes. */ +#include +#include +#include +#include +#include + +size_t DataProcess_Trivial_Random_Number_Generator(const size_t min_value, const size_t max_value, const bool reset_rand) +{ + /* Define MAX_MODIFIER */ +#define MAX_MODIFIER 100000 /* Used to prevent overflowing the static modifier variable below. */ + + /* NO, it's not 4..... (Although it could be. I won't lie.) */ + + /* Init vars. */ + static bool rand_set; /* Whether or not the random seed has been set or not. */ + static size_t modifier; /* Used to ensure that the random seed is unique if the function is called more than once within the timeframe of a second. */ + time_t tt; /* The current system time as a time_t. */ + struct tm * timeM = NULL; /* The current system time as a tm structure. */ + + /* Check if we need to set the RNG. */ + if ((!rand_set) || (reset_rand)) + { + /* Increment modifier. */ + modifier = ((modifier < MAX_MODIFIER) ? (modifier + 1) : (0)); + + /* Get the current system time. */ + time(&tt); + + /* Convert the current system time to a tm structure. */ + timeM = gmtime(&tt); + + /* Check for NULL pointer. */ + if (timeM != NULL) + { + /* Seed random number generator. */ + srand(((timeM->tm_sec) + (timeM->tm_min) + (timeM->tm_hour) + (timeM->tm_yday) + (timeM->tm_year) + (timeM->tm_mon) + (modifier))); + + /* Set rand_set. */ + rand_set = true; + } + else + { + /* Clear rand_set, so we can try again on the next call. */ + rand_set = false; + } + } + + /* Return the result. */ + return ((rand_set) ? (rand() % max_value + min_value) : (0)); + +/* Undef MAX_MODIFIER. */ +#undef MAX_MODIFIER +} + +int DataProcess_getCStringFromSizeT(const size_t number, char ** str, size_t * strLength) +{ + /* Init vars. */ + size_t currentNum = 0; /* Temporary value used to store the current number we are working on. */ + char outputValue = '\0'; /* The value that we need to write into the output buffer. (Calculated from currentNum.) */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of a call to an engine function. */ + char * result = NULL; /* The resulting string of this function. */ + char * previousResult = NULL; /* Temporary pointer used to copy previously generated data into the current result. */ + size_t resultLength = 1; /* The size of the result string. Set to one by default to allow the string to be NULL terminated. */ + const char outputValues[10] = "0123456789"; /* C-String used to map a generated value to it's corresponding character. */ + + /* Check for invalid arguments. */ + if ((str != NULL) && (strLength != NULL)) + { + /* Allocate memory for result. */ + retFromCall = DataProcess_Reallocate_C_String(&result, 0, resultLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (result != NULL)) + { + /* Set currentNum. */ + currentNum = number; + + /* Begin value parsing loop. */ + do + { + /* Read the current byte's value from right to left. (Mod is a reduction operation.) */ + outputValue = (currentNum % 10); + + /* Copy the current buffer's pointer because we are about to create a new one. */ + previousResult = result; + + /* Reset the current buffer's pointer. (Otherwise Reallocate_C_String() will deallocate it.) */ + result = NULL; + + /* Increment the size of the new buffer. */ + resultLength++; + + /* Allocate the new buffer. */ + retFromCall = DataProcess_Reallocate_C_String(&result, 0, resultLength); + + /* Check for successful memory allocation. */ + if ((retFromCall == COMMON_ERROR_SUCCESS) && (result != NULL)) + { + /* Set the first value as the previous data comes after it. */ + result[0] = outputValues[outputValue]; + + /* If we have any previous data we need to copy it into the new buffer and deallocate the previous one. */ + if (previousResult != NULL) + { + memcpy((result + 1), previousResult, (resultLength - 1)); + DataProcess_Deallocate_CString(&previousResult); + } + + /* Get the next value by chopping off the "ones place", aka divide by the current base. */ + currentNum /= 10; + } + else + { + /* Could not allocate memory for output buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } while ((currentNum) && (ret != COMMON_ERROR_MEMORY_ERROR)); + + /* Check for success. */ + if ((ret == COMMON_ERROR_UNKNOWN_ERROR) && (result != NULL) && (resultLength > 0)) + { + /* Copy result pointer, and length. */ + (*str) = result; + (*strLength) = resultLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Deallocate result if needed. */ + if (result != NULL) + { + DataProcess_Deallocate_CString(&result); + } + resultLength = 0; + } + } + else + { + /* Could not allocate memory for result string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid argument(s). */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int DataProcess_Get_SubString_Using_Delimiter(const char * src, const size_t srcLength, const char * delim, const size_t delimLength, + char ** subStr, size_t * subStrLength, const int searchFromEnd, const int getPriorData) +{ + /* Init vars. */ + int foundDelim = 0; /* Whether or not we have found the delim. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of a call to an engine function. */ + char * tempSubStr = NULL; /* Used to create the substr. */ + size_t x = 0; /* Counter used in search loop. */ + size_t y = 0; /* Counter used in search subloop. */ + size_t tempSubStrLength = 0; /* Size of the tempSubString. */ + + /* Check for invalid arguments. */ + if ((src != NULL) && (srcLength > 0) && (delim != NULL) && (delimLength > 0) && (subStr != NULL) && (subStrLength != NULL)) + { + /* Begin search loop. */ + for (x = 0; ((x < srcLength) && (!foundDelim) && (ret == COMMON_ERROR_UNKNOWN_ERROR));) + { + /* Reset foundDelim. */ + foundDelim = 1; + + /* Begin search subloop for the delim. */ + for (y = 0; ((y < delimLength) && (foundDelim)); y++) + { + /* Determine if we are searching from the end of the search string or not. */ + if (searchFromEnd) + { + /* Check and see if the current byte is a match to the delim. */ + if (src[((srcLength - 1) - (x + y))] != delim[((delimLength - 1) - y)]) + { + /* Mismatch, have not found delim. */ + foundDelim = 0; + } + } + else + { + /* Check and see if the current byte is a match to the delim. */ + if (src[(x + y)] != delim[y]) + { + /* Mismatch, have not found delim. */ + foundDelim = 0; + } + } + } + + /* Add the y value to the current x value. (Update the counters.) */ + x += y; + } /* End of search loop. */ + + /* Check and see if we have found the delim. */ + if ((foundDelim) && (y == delimLength)) + { + /* We found the delim, Check and see if we need the data after the delimiter or before it. */ + if ((getPriorData) ? ((srcLength - x) > 0) : (((x - delimLength) > 0) && (x < (srcLength - 1)))) + { + /* Set the size for the new substring. */ + tempSubStrLength = ((getPriorData) ? (srcLength - x) : (x - delimLength)); + + /* Allocate memory for the substring. */ + retFromCall = DataProcess_Reallocate_C_String(&tempSubStr, 0, tempSubStrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempSubStr != NULL)) + { + /* Copy the bytes, before the delimiter if getPriorData is true, otherwise + Copy the bytes after the delimiter. + */ + memcpy(tempSubStr, ((getPriorData) ? (src) : + (searchFromEnd ? (src + ((srcLength + 1) - x)) : (src + x))), + tempSubStrLength); + + /* Copy the pointer and size. */ + (*subStr) = tempSubStr; + (*subStrLength) = tempSubStrLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for substring. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* No bytes remaining to create substring. */ + ret = COMMON_ERROR_END_OF_DATA; + } + } + else + { + /* Delim not found in search string. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* Invalid argument(s). */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int DataProcess_Get_SubString_Using_Offset(const char * src, const size_t srcLength, const size_t offset, + char ** subStr, size_t * subStrLength, const int searchFromEnd, const int getPriorData) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of a call to an engine function. */ + char * tempSubStr = NULL; /* Used to create the substr. */ + size_t tempRealOffset = 0; /* Used to hold the real starting offset in the given string. */ + size_t tempSubStrLength = 0; /* Size of the tempSubString. */ + + /* Check for invalid arguments. */ + if ((src != NULL) && (srcLength > 0) && (offset >= 0) && (offset < srcLength) && (subStr != NULL) && (subStrLength != NULL)) + { + /* + * --------------------------------------------------------------------------------------------------------- + * | searchFromEnd: | getPriorData: | substring contents: (Starting and ending offset ranges.) | + * --------------------------------------------------------------------------------------------------------- + * | FALSE | FALSE | offset <--> end of string | + * | TRUE | FALSE | (end of string - offset) <--> end of string | + * | FALSE | TRUE | start of string <--> offset | + * | TRUE | TRUE | start of string <--> (end of string - offset) | + * --------------------------------------------------------------------------------------------------------- + * + * Note: searchFromEnd only controls the offset's point of origin. (I.e. which end of the given + * string the offset is based at.) searchFromEnd does NOT control what substring is selected + * based on the given offset; the selection is made based on getPriorData. + */ + + /* Determine the real starting offset and sub-string length. */ + tempRealOffset = ((searchFromEnd) ? ((getPriorData) ? (0) : ((srcLength - offset))) : ((getPriorData) ? (0) : (offset))); + tempSubStrLength = ((searchFromEnd) ? ((getPriorData) ? ((srcLength - offset)) : ((srcLength - 1))) : ((getPriorData) ? (offset) : ((srcLength - 1)))); + + /* Make sure the temp values are within the given string's buffer. */ + if ((tempRealOffset >= 0) && (tempRealOffset < srcLength) && (tempSubStrLength > 0) && ((tempRealOffset + tempSubStrLength) < srcLength)) + { + /* Allocate memory for the substring. Using DataProcess_Reallocate_C_String(). */ + retFromCall = DataProcess_Reallocate_C_String(&tempSubStr, 0, tempSubStrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempSubStr != NULL)) + { + /* Copy the data. */ + memcpy(tempSubStr, (src + tempRealOffset), tempSubStrLength); + + /* Copy the pointer and data length. */ + (*subStr) = tempSubStr; + (*subStrLength) = tempSubStrLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for substring. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid temp vars. */ + if (tempSubStrLength <= 0) + { + /* No data to create substring with. */ + ret = COMMON_ERROR_END_OF_DATA; + } + else + { + /* Internal calculation error. (Could not calculate a valid offset and / or substring length.) */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + } + else + { + /* Invalid argument(s). */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +#ifdef __cplusplus +} /* End of extern "C". */ +#endif /* __cplusplus */ diff --git a/src/Core/Src/DataProcess.cpp b/src/Core/Src/DataProcess.cpp index 2ba04c7..ab4699d 100644 --- a/src/Core/Src/DataProcess.cpp +++ b/src/Core/Src/DataProcess.cpp @@ -21,29 +21,107 @@ /* Internal includes. */ #include "DataProcess.h" #include "FileStreams.h" +#include "../../Common/Src/Error_Handler/Common_Error_Handler_Error_Codes.h" /* External includes. */ -#include +#include +#include +#include +#include -size_t DataProcess::Trivial_Random_Number_Generator(const size_t & min_value, const size_t & max_value, const bool & reset_rand) +int DataProcess::CopyCStringToStdString(const char * source, const size_t & sourceLength, std::string & dest) +{ + /* Init result. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + std::string tempStr = ""; /* Temporary string used to copy data. */ + + /* Check for valid data. */ + if ((source != NULL) && (sourceLength > 0)) + { + /* Begin loop to copy data. */ + for (size_t x = 0; (x < sourceLength); x++) + { + tempStr += source[x]; + } + + /* Copy the tempStr to dest. */ + dest = tempStr; + + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int DataProcess::CopyStdStringToCString(const std::string & source, char ** dest) { - // NO, it's not 4..... (Although it could be. I won't lie.) + /* Init result. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* Result code of other engine functions. */ + size_t sourceLength = 0; /* The length of the source string. */ + char * tempStr = NULL; /* Temporary string used to copy data. */ + const char * sourcePtr = NULL; /* Temporary pointer used to copy the source string's data. */ + + /* Get the source string's length, and pointer. */ + sourceLength = source.size(); + sourcePtr = source.c_str(); + + /* Check for valid data. */ + if ((dest != NULL) && (sourceLength > 0) && (sourcePtr != NULL)) + { + /* Allocate memory for dest string. */ + retFromCall = DataProcess_Reallocate_C_String(&tempStr, 0, sourceLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempStr != NULL)) + { + /* Begin loop to copy data. */ + for (size_t x = 0; (x < sourceLength); x++) + { + tempStr[x] = sourcePtr[x]; + } - // Set static. - static bool rand_set; + /* Copy the tempStr to dest. */ + (*dest) = tempStr; - // Check if we need to set the rng. - if ((!rand_set) || (reset_rand)) + /* Success. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for tempStr. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else { - // Seed random number generator. - srand(time(NULL)); + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } - // Set rand_set. - rand_set = true; + /* Check for success. */ + if (ret != COMMON_ERROR_SUCCESS) + { + /* Deallocate the tempStr if needed. */ + if (tempStr != NULL) + { + DataProcess_Deallocate_CString(&tempStr); + } } - // Return the result. - return (rand() % max_value + min_value); + /* Exit function. */ + return ret; +} + +size_t DataProcess::Trivial_Random_Number_Generator(const size_t & min_value, const size_t & max_value, const bool & reset_rand) +{ + /* Call C function. */ + return (DataProcess_Trivial_Random_Number_Generator(min_value, max_value, reset_rand)); } short DataProcess::IncrementingSort(std::vector & sort) @@ -162,7 +240,7 @@ short DataProcess::IncrementingSort(std::vector & sort) } catch (...) { - // Exception thrown. + // std::exception thrown. return -9; } } @@ -287,7 +365,7 @@ short DataProcess::DecrementingSort(std::vector & sort) } catch (...) { - // Exception thrown. + // std::exception thrown. return -9; } } @@ -296,7 +374,7 @@ short DataProcess::DecrementingSort(std::vector & sort) return 0; } -bool DataProcess::CheckForEOF(fstream & source) +bool DataProcess::CheckForEOF(std::fstream & source) { // Init vars. size_t current_pos = 0; // Used to store current pos. @@ -314,7 +392,7 @@ bool DataProcess::CheckForEOF(fstream & source) current_pos = source.tellg(); // Seek to the end of the file. - source.seekg(0, ios::end); + source.seekg(0, std::ios::end); // Compare positions. if (current_pos == (unsigned)source.tellg()) @@ -324,7 +402,7 @@ bool DataProcess::CheckForEOF(fstream & source) } // Seek back to the original position. - source.seekg(current_pos, ios::beg); + source.seekg(current_pos, std::ios::beg); } // Exit function and return the result. @@ -415,7 +493,7 @@ double DataProcess::getnumberFromString(std::string source, std::string varName, } // Look for the varName. - if (source.find(varName) == string::npos) + if (source.find(varName) == std::string::npos) { error.PanicHandler("DataProcess::getnumberFromString : Can't find varName in string. Returning default 0."); real_limiter = ""; @@ -441,56 +519,56 @@ double DataProcess::getnumberFromString(std::string source, std::string varName, if (temp2 == "1") { temp1 = temp1 += temp2; - cout <> output; + std::istringstream (temp1) >> output; } - catch(exception& ex){ - cout << "\n ERROR: DataProcess::getnumberFromString() Exception: " << ex.what() << "\n Clearing data buffer and returning zero.\n\n"; + catch(std::exception& ex){ + std::cout << "\n ERROR: DataProcess::getnumberFromString() std::exception: " << ex.what() << "\n Clearing data buffer and returning zero.\n\n"; // Guess what? we found bullshit. we can't clear it now so dump the buffer and exit. error.PanicHandler("DataProcess::getnumberFromString : Can't convert number string back to double."); @@ -530,7 +608,7 @@ double DataProcess::getnumberFromString(std::string source, std::string varName, real_location = 0; sourceSize = 0; } - cout <<" the result is: " <> number; - getline(cin, dummy); // cin appends a end line char, so we use this to get rid of it. other wise it will show up on the next cin op. - if (cin.fail() == true) // If this goes true user has inputed a non int. + std::cin >> number; + getline(std::cin, dummy); // std::cin appends a end line char, so we use this to get rid of it. other wise it will show up on the next std::cin op. + if (std::cin.fail() == true) // If this goes true user has inputed a non int. { - cout << " invalid data \n "; + std::cout << " invalid data \n "; // Fix the buffer. - cin.clear(); - getline(cin, dummy); + std::cin.clear(); + getline(std::cin, dummy); } return number; } @@ -558,7 +636,7 @@ bool DataProcess::yesNoConsolePrompt() // Prompt user. std::cout << "(YES or NO) (Must be in CAPS): "; - getline(cin, dummy); + getline(std::cin, dummy); // Check result, if user said YES then return true. if (dummy == "YES") @@ -581,7 +659,7 @@ bool DataProcess::getboolFromstring(std::string source, std::string varName, cha std::string searchString = ""; // Look for the var name - if (source.find(varName) == string::npos) + if (source.find(varName) == std::string::npos) { // If varname is not found in the string return default. error.PanicHandler("DataProcess::getboolFromstring : Varname not found."); @@ -598,11 +676,11 @@ bool DataProcess::getboolFromstring(std::string source, std::string varName, cha { delimiterReal = '\n'; } - if (source.find(delimiterReal, location1) == string::npos) + if (source.find(delimiterReal, location1) == std::string::npos) { location2 = -1; } - if (source.find('\n', location1) == string::npos) + if (source.find('\n', location1) == std::string::npos) { location2 = -1; } @@ -617,24 +695,24 @@ bool DataProcess::getboolFromstring(std::string source, std::string varName, cha searchString = source; } // Debug - cout << searchString < 31 ) && (data[x] < 127)) { - cout << data[x] << " "; + std::cout << data[x] << " "; } else { - cout << " 0x" << (int)data[x] << " "; + std::cout << " 0x" << (int)data[x] << " "; } if (memory_format == true) { if (x%15 == 0) { // Print current line. - cout << (x/15) << "\n"; + std::cout << (x/15) << "\n"; } } } @@ -1306,10 +1384,10 @@ void DataProcess::dumpDataToConsole(const char * data, size_t length, size_t off // Some error. // Flush console buffer. - cout.flush(); + std::cout.flush(); // Output a new line. - cout << "\n\n"; + std::cout << "\n\n"; // Exit function. return; @@ -1317,10 +1395,10 @@ void DataProcess::dumpDataToConsole(const char * data, size_t length, size_t off } // Flush console buffer. - cout.flush(); + std::cout.flush(); // Flush console buffer. - cout.flush(); + std::cout.flush(); // Default return. return; @@ -1370,617 +1448,537 @@ short DataProcess::getnumberFromString(char input) return 0; } -const char * DataProcess::Data_Object::get_Pointer() const -{ - return this->data; -} - -char * DataProcess::Data_Object::get_Copy() const +int DataProcess::Data_Object::get_C_Struct(MSYS_DataObject ** retPtr) { - // Dumb check. - if (this->data == NULL) - { - return NULL; - } - if (this->length <= 0) - { - return NULL; - } + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; - // Init vars. - char * result = NULL; + /* Check for valid pointers. */ + if (retPtr != NULL) + { + if (this->obj != NULL) + { + /* Copy pointer. */ + (*retPtr) = this->obj; - try{ - result = (char*)malloc(this->length); - memset(result, '\0', this->length); - for(size_t x = 0; x < this->length; x++) - { - result[x] = this->data[x]; - } - return result; - } - catch(...) - { - if (result != NULL) - { - free(result); - return NULL; - } - } + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid internal pointer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid retPtr. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } - // Default return. - return NULL; + /* Exit function. */ + return ret; } -DataProcess::Data_Object::Data_Object(const DataProcess::Data_Object & source) +const char * DataProcess::Data_Object::get_Pointer() const { - // Set variables to known safe values. - this->length = 0; - this->capacity = 0; - this->data = NULL; + /* Init vars. */ + const char * ptr = NULL; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Now deep copy data if it is allocated. - if ((source.capacity > 0) && (source.data != NULL)) - { - try { - this->data = (char*)malloc(source.capacity); - memset(this->data, '\0', source.capacity); - - // Init loop. - for (size_t x = 0; x < source.capacity; x++) - { - this->data[x] = source.data[x]; - } + /* Check for valid C library object. */ + if (this->obj != NULL) + { + /* Call C library. */ + retFromCall = MSYS_DataObject_Get_Pointer(this->obj, &ptr); + } - // Set final vars. - this->length = source.length; - this->capacity = source.capacity; - } - catch(...) - { - // Error. - this->clear(); - } - } + /* Exit function. */ + return ptr; } -DataProcess::Data_Object::Data_Object(const std::string & source) +char * DataProcess::Data_Object::get_Data_Copy() const { - // Set variables to known safe values. - this->length = 0; - this->capacity = 0; - this->data = NULL; - - // Now deep copy data if it is allocated. - if (source.size() > 0) - { - try{ - this->data = (char*)malloc(source.size()); - memset(this->data, '\0', (source.size())); + /* Init vars. */ + char * ptr = NULL; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - for(size_t x = 0; x < source.size(); x++) - { - this->data[x] = source[x]; - } + /* Check for valid C library object. */ + if (this->obj != NULL) + { + /* Begin try block. */ + try { + /* Call C library. */ + retFromCall = MSYS_DataObject_Get_Data_Copy(this->obj, &ptr); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (ptr != NULL)) + { + DataProcess_Deallocate_CString(&ptr); + } + } + catch (...) + { + if (ptr != NULL) + { + DataProcess_Deallocate_CString(&ptr); + } + } + } - // Put the variable update here after we have actually set everything up. - this->length = source.size(); - this->capacity = this->length; - } - catch(...) - { - // Some error. - this->clear(); - } - } + /* Exit function. */ + return ptr; } -DataProcess::Data_Object::Data_Object(const char * source, size_t str_length) +void DataProcess::Data_Object::destroy_Data_Copy(char ** obj) const { - // Set variables to known safe values. - this->length = 0; - this->capacity = 0; - this->data = NULL; + /* Call C Library. */ + MSYS_Destroy_DataObject_Data_Copy(obj); - // Now deep copy data if it is allocated. - if ((source != NULL) && (str_length > 0)) - { - try{ - this->data = (char*)malloc(str_length); - memset(this->data, '\0', str_length); + /* Exit function. */ + return; +} - for(size_t x = 0; x < str_length; x++) - { - this->data[x] = source[x]; - } +DataProcess::Data_Object::Data_Object() +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Put the variable update here after we have actually set everything up. - this->length = str_length; - this->capacity = str_length; - } - catch(...) - { - // Some error. - this->clear(); - } - } + // Set variables to known safe values. + this->obj = NULL; + + /* Attempt to allocate the C library object. */ + try { + /* Call C Library. */ + retFromCall = MSYS_Create_DataObject((&(this->obj))); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } + catch(...) + { + // Error. + this->clear(); + } } -DataProcess::Data_Object::Data_Object(const char & source) +DataProcess::Data_Object::Data_Object(const DataProcess::Data_Object & source) { - // Set variables to known safe values. - this->length = 0; - this->capacity = 0; - this->data = NULL; - - // Now deep copy data if it is allocated. - try{ - // Allocate memory. - this->data = (char*)malloc(1); - memset(this->data, '\0', 1); + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Set the source value. - this->data[0] = source; + // Set variables to known safe values. + this->obj = NULL; - // Put the variable update here after we have actually set everything up. - this->length = 1; - this->capacity = 1; + // Now deep copy data if it is allocated. + if (source.obj != NULL) + { + try { + /* Call C Library. */ + retFromCall = MSYS_DataObject_Create_From_Existing_DataObject(source.obj, (&(this->obj))); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } } catch(...) { - // Some error. - this->clear(); + // Error. + this->clear(); } + } } -DataProcess::Data_Object & DataProcess::Data_Object::operator= (const DataProcess::Data_Object & source) +DataProcess::Data_Object::Data_Object(const std::string & source) { - // Prevent self assignment. - if (this != &source) - { - // Set variables to known safe values. - this->length = 0; - this->capacity = 0; - this->data = NULL; - - // Now deep copy data if it is allocated. - if ((source.capacity > 0) && (source.data != NULL)) - { - try { - this->data = (char*)malloc(source.capacity); - memset(this->data, '\0', source.capacity); + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Init loop. - for (size_t x = 0; x < source.capacity; x++) - { - this->data[x] = source.data[x]; - } + // Set variables to known safe values. + this->obj = NULL; - // Set final vars. - this->length = source.length; - this->capacity = source.capacity; - } - catch(...) - { - // Error. - this->clear(); - } - } + // Now deep copy data if it is allocated. + if (source.size() > 0) + { + try{ + /* Create the object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + /* Copy the data. */ + retFromCall = MSYS_DataObject_Set_Data_From_CString(this->obj, source.c_str(), source.size()); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } + else + { + if (this->obj != NULL) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } } - - // Exit function. - return *this; + catch(...) + { + // Some error. + this->clear(); + } + } } -DataProcess::Data_Object & DataProcess::Data_Object::operator= (const std::string & source) +DataProcess::Data_Object::Data_Object(const char * source, size_t str_length) { - // Set varaiables to known safe values. - this->length = 0; - this->capacity = 0; - this->data = NULL; + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // If the source is empty then we've already finished (the clear command above is enough). - if (source.size() <= 0) - { - return *this; - } + // Set variables to known safe values. + this->obj = NULL; + // Now deep copy data if it is allocated. + if ((source != NULL) && (str_length > 0)) + { try{ - this->data = (char*)malloc(source.size()); - memset(this->data, '\0', (source.size())); - - for(size_t x = 0; x < source.size(); x++) - { - this->data[x] = source[x]; - } - - // Put the variable update here after we have actually set everything up. - this->length = source.size(); - this->capacity = this->length; + /* Create the object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + /* Copy the data. */ + retFromCall = MSYS_DataObject_Set_Data_From_CString(this->obj, source, str_length); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } + else + { + if (this->obj != NULL) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } } catch(...) { - // Some error. - this->clear(); + // Some error. + this->clear(); } - - // Exit function. - return *this; + } } -DataProcess::Data_Object & DataProcess::Data_Object::operator= (const char & source) +DataProcess::Data_Object::Data_Object(const char & source) { - // Set varaiables to known safe values. - this->length = 0; - this->capacity = 0; - this->data = NULL; + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - try{ - // Allocate memory. - this->data = (char*)malloc(1); - memset(this->data, '\0', 1); + // Set variables to known safe values. + this->obj = NULL; + + // Now deep copy data if it is allocated. + try{ + /* Create the object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + /* Copy the data. */ + retFromCall = MSYS_DataObject_Set_Data_From_Char(this->obj, source); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } + else + { + if (this->obj != NULL) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } + } + catch(...) + { + // Some error. + this->clear(); + } +} - // Set the source value. - this->data[0] = source; +DataProcess::Data_Object & DataProcess::Data_Object::operator= (const DataProcess::Data_Object & source) +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Put the variable update here after we have actually set everything up. - this->length = 1; - this->capacity = 1; - } - catch(...) + // Prevent self assignment. + if (this != &source) + { + /* Check for allocated buffer. */ + if (this->obj == NULL) + { + /* Allocate the data object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } + + // Now deep copy data if it is allocated. + if ((source.obj != NULL) && (this->obj != NULL)) { - // Some error. + try { + retFromCall = MSYS_Deep_Copy_DataObject(source.obj, this->obj); + } + catch(...) + { + // Error. this->clear(); + } } + } - // Exit function. - return *this; + // Exit function. + return *this; } -DataProcess::Data_Object & DataProcess::Data_Object::operator+= (const char & source) +DataProcess::Data_Object & DataProcess::Data_Object::operator= (const std::string & source) { - // Init vars. - char * result = NULL; + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Check and see if we need to reallocate memory. - try{ - if ((this->length + 1) <= this->capacity) - { - this->data[(this->length)] = source; - this->length++; - } - else - { - result = (char*)malloc((this->capacity + 1)); - memset(result, '\0', (this->capacity + 1)); - if (this->length > 0) - { - for(size_t x = 0; x < (this->length); x++) - { - result[x] = this->data[x]; - } - } - result[(this->length)] = source; - free(this->data); - this->data = result; - this->length++; - this->capacity++; - result = NULL; - } - } - catch(...) - { - if (result != NULL) - { - free(result); - result = NULL; - } - this->clear(); - } + /* Check for allocated buffer. */ + if (this->obj == NULL) + { + /* Allocate the data object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } - // Exit function. - return *this; + // Now copy data if it is allocated. + if (this->obj != NULL) + { + // If the source is empty then we've already finished (the clear command above is enough). + if (source.size() > 0) + { + try{ + retFromCall = MSYS_DataObject_Set_Data_From_CString(this->obj, source.c_str(), source.size()); + } + catch(...) + { + // Some error. + this->clear(); + } + } + } + + // Exit function. + return *this; } -DataProcess::Data_Object & DataProcess::Data_Object::operator+= (const std::string source) +DataProcess::Data_Object & DataProcess::Data_Object::operator= (const char & source) { - // Check for blank string. - if (source.size() <= 0) - { - return *this; - } + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Init var. - char * result = NULL; - size_t old_size = this->length; + /* Check for allocated buffer. */ + if (this->obj == NULL) + { + /* Allocate the data object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } - // Check and see if we need to reallocate memory. - try{ - if ((this->length + source.size()) <= this->capacity) - { - // Copy the data into the buffer. - for (size_t x = 0; x < source.size(); x++) - { - this->data[((old_size) + x)] = source[x]; - } - this->length = ((old_size) + source.size()); - } - else - { - // Allocate the new buffer. - result = (char*)malloc((this->length + source.size())); - memset(result, '\0', (this->length + source.size())); - - if (this->data != NULL) - { - // Copy data to new buffer then free the old one. - for(size_t x = 0; x < this->length; x++) - { - result[x] = this->data[x]; - } - this->clear(); - } - for (size_t x = 0; x < (source.size()); x++) - { - // Copy the new data into the new buffer. - result[((old_size) + x)] = source[x]; - } - - // Set the new buffer, and update the length and capacity. - this->data = result; - this->length = (old_size + source.size()); - this->capacity = this->length; - } - } - catch(...) - { - if (result != NULL) - { - free(result); - result = NULL; - } - this->clear(); - } + // Now copy data if the object is allocated. + if (this->obj != NULL) + { + try{ + retFromCall = MSYS_DataObject_Set_Data_From_Char(this->obj, source); + } + catch(...) + { + // Some error. + this->clear(); + } + } - // Exit function. - return *this; + // Exit function. + return *this; } -DataProcess::Data_Object & DataProcess::Data_Object::operator+= (const DataProcess::Data_Object & source) +DataProcess::Data_Object & DataProcess::Data_Object::operator+= (const char & source) { - // Check for empty source. - if (source.size() <= 0) - { - // Nothing to do. - return *this; - } + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Init vars. - char * result = NULL; - size_t old_size = this->length; - - // Check and see if we need to reallocate memory. - try{ - if ((this->length + source.size()) <= this->capacity) - { - // Copy the data into the buffer. - for (size_t x = 0; x < source.size(); x++) - { - this->data[((old_size) + x)] = source.data[x]; - } - this->length = ((old_size) + source.size()); - } - else - { - // Allocate new buffer. - result = (char*)malloc((this->length + source.size())); - memset(result, '\0', (this->length + source.size())); - - // Copy old data into the new buffer. - if (this->data != NULL) - { - for(size_t x = 0; x < this->length; x++) - { - result[x] = this->data[x]; - } - - // Clear the old data. - this->clear(); - } + /* Check for allocated buffer. */ + if (this->obj == NULL) + { + /* Allocate the data object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } - // Copy in new data. - for (size_t x = 0; x < (source.size()); x++) - { - result[((old_size)+x)] = source.data[x]; - } + // Now copy data if the object is allocated. + if (this->obj != NULL) + { + try{ + retFromCall = MSYS_DataObject_Append_Char(this->obj, source); + } + catch(...) + { - // Update the data pointer, and the length and capacity. - this->data = result; - this->length = (old_size + source.size()); - this->capacity = this->length; - } - } - catch (...) - { - if (result != NULL) - { - free(result); - result = NULL; - } - this->clear(); - } + } + } - // Exit function. - return *this; + // Exit function. + return *this; } -char * DataProcess::Data_Object::substr(size_t offset, size_t endpoint) const +DataProcess::Data_Object & DataProcess::Data_Object::operator+= (const std::string & source) { - // Dumb check. - if ((this->data == NULL) || (this->length <= 0)) - { - // Nothing to do. - return NULL; - } - if ((offset > this->length) || (offset < 0) || (offset >= endpoint)) - { - // Invalid offset. - return NULL; - } - if ((endpoint > this->length) || (endpoint < 0) || (endpoint <= offset)) - { - // Invalid endpoint. - return NULL; - } + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Init vars. - char * result = NULL; - size_t result_size = 0; + /* Check for allocated buffer. */ + if (this->obj == NULL) + { + /* Allocate the data object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } - // Compute diffrence between endpoint and offset. - result_size = (endpoint - offset); - if (result_size <= 0) - { - // Bad args. - return NULL; - } - try{ - result = (char*)malloc(result_size); - memset(result, '\0', result_size); - for (size_t x = 0; x < result_size; x++) - { - result[x] = this->data[(offset+x)]; - } - return result; - } - catch(...) - { - if (result != NULL) - { - free(result); - result = NULL; - } - return NULL; - } + // Now copy data if the object is allocated. + if ((this->obj != NULL) && (source.size() > 0)) + { + try{ + retFromCall = MSYS_DataObject_Append_CString(this->obj, source.c_str(), source.size()); + } + catch(...) + { - if (result != NULL) - { - free(result); - result = NULL; - } - // Default return. - return result; + } + } + + // Exit function. + return *this; } -int DataProcess::Data_Object::Buffer_Copy(const DataProcess::Data_Object & source, size_t copy_offset, size_t copy_length) +DataProcess::Data_Object & DataProcess::Data_Object::operator+= (const DataProcess::Data_Object & source) { - // Reset our buffers. - this->reset(); + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Dumb check. - if ((source.length <= 0) || (source.length < copy_offset)) - { - // Can not copy non existant data. - return -5; - } - if ((copy_offset == 0) && (copy_length == 0)) - { - // Copy all data from source. - copy_length = source.length; - } - if ((copy_offset != 0) && (copy_length == 0)) - { - // Bad range. - return -5; - } - if ((source.length < copy_length) || (source.length < (copy_offset + copy_length))) - { - // Bad range. - return -5; - } - try{ - // Copy data. - for (size_t x = 0; x < this->capacity; x++) - { - // Get data. - this->data[x] = source.data[(copy_offset + x)]; + /* Check for allocated buffer. */ + if (this->obj == NULL) + { + /* Allocate the data object. */ + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } - // Increment length. - this->length++; + // Now copy data if the object is allocated. + if ((this->obj != NULL) && (source.obj != NULL)) + { + try{ + retFromCall = MSYS_DataObject_Append_DataObject(this->obj, source.obj); + } + catch(...) + { - // Check for end of data. - if ((x + 1) == copy_length) - { - // Data copied. - return 0; - } + } + } - // Check for end of buffer. - if ((x + 1) == this->capacity) - { - // End of buffer, due to the fact that we ran out of space in the buffer, return -1. - return -1; - } + // Exit function. + return *this; +} - // Boundry check. - if ((x < 0) || (x >= this->capacity) || (x > copy_length)) - { - // Out of bounds. - this->reset(); - return -9; - } - } - } - catch(...) - { - // Some error. - this->reset(); - return -9; - } +char * DataProcess::Data_Object::substr(size_t offset, size_t endpoint) const +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + char * ret = NULL; - // Default return. - this->reset(); - return -3; + // Check vars. + if (this->obj != NULL) + { + try{ + retFromCall = MSYS_DataObject_Substr(this->obj, offset, endpoint, &ret); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (ret != NULL)) + { + DataProcess_Deallocate_CString(&ret); + } + } + catch(...) + { + if (ret != NULL) + { + DataProcess_Deallocate_CString(&ret); + } + } + } + + // Exit function. + return ret; } -bool DataProcess::Data_Object::Compare(const DataProcess::Data_Object &source) const +int DataProcess::Data_Object::Buffer_Copy(const DataProcess::Data_Object & source, size_t copy_offset, size_t copy_length) { - // Init var. - const char * test = NULL; - test = source.get_Pointer(); - - if (source.get_length() != this->length) - { - return false; - } + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; - if (source.get_Capacity() != this->capacity) - { - return false; - } + // Check vars. + if ((this->obj != NULL) && (source.obj != NULL)) + { + try{ + ret = MSYS_DataObject_Buffer_Copy(this->obj, source.obj, copy_offset, copy_length); + } + catch(...) + { + ret = COMMON_ERROR_EXCEPTION_THROWN; + } + } + else + { + /* Invalid object. */ + ret = COMMON_ERROR_SUBSYSTEM_OBJECT_NOT_INITED; + } - if ((this->data == NULL) && (test != NULL)) - { - return false; - } + // Exit function. + return ret; +} - if ((this->data != NULL) && (test == NULL)) - { - return false; - } +bool DataProcess::Data_Object::Compare(const DataProcess::Data_Object &source) const +{ + /* Init vars. */ + bool ret = false; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - if ((test != NULL) && (this->data != NULL)) - { - for (size_t x = 0; x < this->length; x++) - { - if (this->data[x] != test[x]) - { - return false; - } - } - } + /* Begin try block. */ + try{ + /* Call C library. (Note: It can handle invalid objects.) */ + retFromCall = MSYS_DataObject_Compare(this->obj, source.obj); + ret = ((retFromCall == COMMON_ERROR_COMPARISON_PASSED) ? (true) : (false)); + } + catch(...) + { + ret = false; + } - return true; + // Exit function. + return ret; } bool DataProcess::Data_Object::NCompare(const DataProcess::Data_Object &source) const @@ -1990,37 +1988,23 @@ bool DataProcess::Data_Object::NCompare(const DataProcess::Data_Object &source) bool DataProcess::Data_Object::Data_Compare(const DataProcess::Data_Object & source) const { - // Check length. - if (source.length != this->length) - { - return false; - } - - // Check pointers. - if ((this->data == NULL) && (source.data != NULL)) - { - return false; - } - - if ((this->data != NULL) && (source.data == NULL)) - { - return false; - } + /* Init vars. */ + bool ret = false; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Check data. - if ((source.data != NULL) && (this->data != NULL)) - { - for (size_t x = 0; x < this->length; x++) - { - if (this->data[x] != source.data[x]) - { - return false; - } - } - } + /* Begin try block. */ + try{ + /* Call C library. (Note: It can handle invalid objects.) */ + retFromCall = MSYS_DataObject_Data_Compare(this->obj, source.obj); + ret = ((retFromCall == COMMON_ERROR_COMPARISON_PASSED) ? (true) : (false)); + } + catch(...) + { + ret = false; + } - // Data is equal. - return true; + // Exit function. + return ret; } bool DataProcess::Data_Object::Data_NCompare(const DataProcess::Data_Object & source) const @@ -2030,487 +2014,334 @@ bool DataProcess::Data_Object::Data_NCompare(const DataProcess::Data_Object & so void DataProcess::Data_Object::clear() { - if (this->data != NULL) - { - free(this->data); - this->data = NULL; - } - this->length = 0; - this->capacity = 0; - return; + /* Check for valid object. */ + if (this->obj != NULL) + { + MSYS_Clear_DataObject(this->obj); + } + + /* Exit function. */ + return; } size_t DataProcess::Data_Object::get_length() const { - return this->length; + /* Init vars. */ + size_t ret = 0; + + /* Check for valid object. */ + if (this->obj != NULL) + { + try { + MSYS_DataObject_Get_Length(this->obj, &ret); + } + catch(...) + { + /* Bad result... (Need to fix this function.) */ + ret = 0; + } + } + + /* Exit function. */ + return ret; } size_t DataProcess::Data_Object::get_Capacity() const { - return this->capacity; + /* Init vars. */ + size_t ret = 0; + + /* Check for valid object. */ + if (this->obj != NULL) + { + try { + MSYS_DataObject_Get_Capacity(this->obj, &ret); + } + catch(...) + { + /* Bad result... (Need to fix this function.) */ + ret = 0; + } + } + + /* Exit function. */ + return ret; } size_t DataProcess::Data_Object::size() const { - return this->length; -} + /* Init vars. */ + size_t ret = 0; -void DataProcess::Data_Object::set(const char * source, const size_t & source_length) -{ - // Clear the old data. - this->clear(); + /* Check for valid object. */ + if (this->obj != NULL) + { + try { + MSYS_DataObject_Get_Length(this->obj, &ret); + } + catch(...) + { + /* Bad result... (Need to fix this function.) */ + ret = 0; + } + } - // Dumb check. - if (source == NULL) - { - // Nothing to do. - return; - } - if (source_length <= 0) - { - // Nothing to do. - return; - } + /* Exit function. */ + return ret; +} - // Try and set the vars. - try{ - // Allocate the new buffer. - this->data = (char*)malloc(source_length); - memset(this->data, '\0', source_length); +int DataProcess::Data_Object::Shallow_Copy(const DataProcess::Data_Object & source) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Copy the data into the new buffer. - for(size_t x = 0; x < source_length; x++) - { - this->data[x] = source[x]; - } + /* Check for valid object. */ + if ((this->obj != NULL) && (source.obj != NULL)) + { + /* Call C Library function. */ + retFromCall = MSYS_Shallow_Copy_DataObject(source.obj, this->obj); - // Update capacity and length. - this->length = source_length; - this->capacity = source_length; - } - catch(...) - { - this->clear(); - } + /* Set result. */ + ret = ((retFromCall != COMMON_ERROR_INVALID_ARGUMENT) ? (retFromCall) : (COMMON_ERROR_SUBSYSTEM_OBJECT_NOT_INITED)); + } + else + { + /* Invalid object. */ + ret = COMMON_ERROR_SUBSYSTEM_OBJECT_NOT_INITED; + } - // Exit function. - return; + /* Exit function. */ + return ret; } -void DataProcess::Data_Object::reset() +void DataProcess::Data_Object::set(const char * source, const size_t & source_length) { - // Dumb check. - if ((this->data == NULL) || (this->capacity <= 0)) - { - // Nothing to do. - return; - } + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Reset the data buffer. + // Check vars. + if ((source != NULL) && (source_length > 0)) + { try{ - memset(this->data, '\0', (this->capacity - 1)); - this->length = 0; - return; + /* Create the object. (If needed.) */ + if (this->obj == NULL) + { + retFromCall = MSYS_Create_DataObject(&(this->obj)); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + MSYS_Destroy_DataObject(&(this->obj)); + } + } + else + { + /* Indicate that when we checked the object it was initilized. */ + retFromCall = COMMON_ERROR_SUCCESS; + } + + /* Copy the data. */ + if ((this->obj != NULL) && (retFromCall == COMMON_ERROR_SUCCESS)) + { + retFromCall = MSYS_DataObject_Set_Data_From_CString(this->obj, source, source_length); + if ((retFromCall != COMMON_ERROR_SUCCESS) && (this->obj != NULL)) + { + /* The object won't be modified here, but we need to return the error code. */ + } + } + else + { + /* Need to return an error here... */ + } } catch(...) { - // Error. - return; + /* More error codes needed. */ } - - // Exit function. - return; + } } -void DataProcess::Data_Object::reserve(size_t new_length) +void DataProcess::Data_Object::reset() { - // Check and see if length is less than our current size. - if (new_length < this->length) - { - // Failsafe, we don't want to destroy data if we can help it. - return; - } - - // Init vars. - char * temp_buffer = NULL; - size_t old_length = 0; - - try{ - // Allocate new buffer. - temp_buffer = (char*)malloc(new_length); - if (temp_buffer == NULL) - { - // Cannot allocate memory. - return; - } - memset(temp_buffer, '\0', new_length); - } - catch(...) - { - // Could not realloc memory. - return; - } - - // Copy old data if nessacarry. - if (this->data != NULL) - { - try{ - // Set old length. - old_length = this->length; - - // Reset length. - this->length = 0; - - // Copy the contents of the original data. - for (size_t x = 0; x < old_length; x++) - { - temp_buffer[x] = this->data[x]; - this->length++; - } - } - catch(...) - { - - } - - // Free the original buffer. - if (this->data != NULL) - { - free(this->data); - this->data = NULL; - } - - // Set our new buffer. - this->data = temp_buffer; - - // Set our new capacity. - this->capacity = new_length; + /* Check for valid object. */ + if (this->obj != NULL) + { + try { + MSYS_Reset_DataObject(this->obj); + } + catch(...) + { + /* Ignore the error. */ + } + } - // Exit function. - return; - } - else - { - // Set our new length. - this->length = 0; + /* Exit function. */ + return; +} - // Set our new capacity. - this->capacity = new_length; +void DataProcess::Data_Object::reserve(size_t new_length) +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Set our new buffer. - this->data = temp_buffer; + /* Check for valid object. */ + if (this->obj != NULL) + { + try { + retFromCall = MSYS_DataObject_Reserve_Memory(this->obj, new_length); + } + catch(...) + { + /* Bad result... (Need to fix this function.) */ - // Exit function. - return; - } + } + } - // Default return. - return; + /* Exit function. */ + return; } size_t DataProcess::Data_Object::insert(size_t offset, const char source) { - // Check for bad offset. - if ((offset < 0) || (offset > this->length)) - { - // Bad offset. - return 0; - } - - // Init vars. - char current_char = '\0'; - char next_char = '\0'; - char * result = NULL; - - // Check and see if we need to reallocate memory. - try{ - if ((this->length + 1) <= this->capacity) - { - // Set the inital current char, to the source value. - current_char = source; - - // At the offset read in the next char. - for (size_t x = offset; x < (this->length); x++) - { - // Read in the next char. - next_char = this->data[x]; - - // Copy the current_char. - this->data[x] = current_char; - - // Current char is updated. - current_char = next_char; - } - - // Insert the last char. - this->data[(this->length)] = current_char; - - // Increment the length. - this->length++; - - // Exit function. - return 1; - } - else - { - result = (char*)malloc((this->capacity + 1)); - memset(result, '\0', (this->capacity + 1)); - if (this->length > 0) - { - // Copy data until we reach the offset. - for(size_t x = 0; x < ((this->length) + 1); x++) - { - // If we have not yet reached the offset, copy data directly. - if (x < offset) - { - result[x] = this->data[x]; - } - - // If we are at the offset, copy the source value. - if (x == offset) - { - result[x] = source; - } - - /* - If we are past the offset, copy the remaining data. - Note: This is correct as x - 1 equals the data without - the source value added. - */ - if (x > offset) - { - result[x] = this->data[x - 1]; - } - - } - } - else - { - // We have no data so just insert the source value. - this->data[0] = source; - } - - // Free the old data buffer. - if (this->data != NULL) - { - free(this->data); - this->data = NULL; - } - - // Set the new buffer. - this->data = result; - - // Update length and capacity. - this->length++; - this->capacity++; + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Exit function. - return 1; - } - } - catch(...) - { - if (result != NULL) - { - free(result); - result = NULL; - } - this->clear(); - } + /* Check for valid object. */ + if (this->obj != NULL) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Insert_CString(this->obj, offset, &source, sizeof(char)); + } - // Default return. - return 0; + /* Exit function. (Bad error code.) */ + return (sizeof(char)); } size_t DataProcess::Data_Object::insert(size_t offset, const std::string & source) { - // Check for empty source. - if (source.size() <= 0) - { - // Nothing to do. - return 0; - } + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Check for bad offset. - if ((offset < 0) || (offset > this->length)) - { - // Bad offset. - return 0; - } + /* Check for valid object. */ + if ((this->obj != NULL) && (source.size() > 0)) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Insert_CString(this->obj, offset, source.c_str(), source.size()); + } - // Init vars. - size_t old_size = this->length; - char * result = NULL; + /* Exit function. (Bad error code.) */ + return (source.size()); +} - try{ - if ((this->length + source.size()) <= this->capacity) - { - for (size_t x = 0; x < source.size(); x++) - { - if (this->insert((offset + x), source[x]) != 1) - { - // Error Could not copy all data. - return x; - } - } +size_t DataProcess::Data_Object::insert(size_t offset, const DataProcess::Data_Object & source) +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Exit the function. - return source.size(); - } - else - { - // Allocate new buffer. - result = (char*)malloc((this->length + source.size())); - memset(result, '\0', (this->length + source.size())); + /* Check for valid object. */ + if ((this->obj != NULL) && (source.obj != NULL)) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Insert_From_DataObject(this->obj, offset, source.obj); + } - // Copy old data into the new buffer until we reach the offset. - if (this->data != NULL) - { - for (size_t x = 0; x < offset; x++) - { - result[x] = this->data[x]; - } - } + /* Exit function. (Bad error code.) */ + return 1; +} - // Copy in new data. - for (size_t x = 0; x < (source.size()); x++) - { - result[(offset + x)] = source[x]; - } +int DataProcess::Data_Object::Replace(const size_t offset, const char source) +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Copy remaining data from the old buffer. - if (this->data != NULL) - { - for(size_t x = offset; x < old_size; x++) - { - result[(offset + (source.size()) + x)] = this->data[(offset + x)]; - } - } + /* Check for valid object. */ + if (this->obj != NULL) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Replace_With_CString(this->obj, offset, &source, sizeof(char)); + } - // Release the old memory. - if (this->data != NULL) - { - free(this->data); - this->data = NULL; - } + /* Exit function. */ + return retFromCall; +} - // Update the data pointer, and the length and capacity. - this->data = result; - this->length = (old_size + source.size()); - this->capacity = this->length; +int DataProcess::Data_Object::Replace(const size_t offset, const std::string & source) +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Exit function. - return source.size(); - } - } - catch (...) - { - if (result != NULL) - { - free(result); - result = NULL; - } - this->clear(); - } + /* Check for valid object. */ + if ((this->obj != NULL) && (source.size() > 0)) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Replace_With_CString(this->obj, offset, source.c_str(), source.size()); + } - // Default return. - return 0; + /* Exit function. */ + return retFromCall; } -size_t DataProcess::Data_Object::insert(size_t offset, const DataProcess::Data_Object & source) +int DataProcess::Data_Object::Replace(const size_t offset, const DataProcess::Data_Object & source) { - // Check for empty source. - if (source.size() <= 0) - { - // Nothing to do. - return 0; - } + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Check for bad offset. - if ((offset < 0) || (offset > this->length)) - { - // Bad offset. - return 0; - } + /* Check for valid object. */ + if ((this->obj != NULL) && (source.obj != NULL)) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Replace_With_DataObject(this->obj, offset, source.obj); + } - // Init vars. - size_t old_size = this->length; - char * result = NULL; + /* Exit function. */ + return retFromCall; +} - try{ - if ((this->length + source.size()) <= this->capacity) - { - // This is a single char insert, but short of creating a second buffer, This is the best way to do it. - for (size_t x = 0; x < source.size(); x++) - { - if (this->insert((offset + x), source.data[x]) != 1) - { - // Error Could not copy all data. - return x; - } - } +int DataProcess::Data_Object::Overwrite(const size_t offset, const char source) +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Exit function. - return source.size(); - } - else - { - // Allocate new buffer. - result = (char*)malloc((this->length + source.size())); - memset(result, '\0', (this->length + source.size())); + /* Check for valid object. */ + if (this->obj != NULL) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Overwrite_With_CString(this->obj, offset, &source, sizeof(char)); + } - // Copy old data into the new buffer until we reach the offset. - if (this->data != NULL) - { - for (size_t x = 0; x < offset; x++) - { - result[x] = this->data[x]; - } - } + /* Exit function. */ + return retFromCall; +} - // Copy in new data. - for (size_t x = 0; x < (source.size()); x++) - { - result[(offset + x)] = source.data[x]; - } +int DataProcess::Data_Object::Overwrite(const size_t offset, const std::string & source) +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Copy remaining data from the old buffer. - if (this->data != NULL) - { - for(size_t x = offset; x < old_size; x++) - { - result[(offset + (source.size()) + x)] = this->data[(offset + x)]; - } - } + /* Check for valid object. */ + if ((this->obj != NULL) && (source.size() > 0)) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Overwrite_With_CString(this->obj, offset, source.c_str(), source.size()); + } - // Release the old memory. - if (this->data != NULL) - { - free(this->data); - this->data = NULL; - } + /* Exit function. */ + return retFromCall; +} - // Update the data pointer, and the length and capacity. - this->data = result; - this->length = (old_size + source.size()); - this->capacity = this->length; +int DataProcess::Data_Object::Overwrite(const size_t offset, const DataProcess::Data_Object & source) +{ + /* Init vars. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; - // Exit function. - return source.size(); - } - } - catch (...) - { - if (result != NULL) - { - free(result); - result = NULL; - } - this->clear(); - } + /* Check for valid object. */ + if ((this->obj != NULL) && (source.obj != NULL)) + { + /* Call C library function. */ + retFromCall = MSYS_DataObject_Overwrite_With_DataObject(this->obj, offset, source.obj); + } - // Default return. - return 0; + /* Exit function. */ + return retFromCall; } short DataProcess::RegularExpressionParser(const std::string & expression, const std::string & input, Panic::Panic_ERROR * error) diff --git a/src/Core/Src/DataProcess.h b/src/Core/Src/DataProcess.h index a7d7fe5..0b1c09b 100644 --- a/src/Core/Src/DataProcess.h +++ b/src/Core/Src/DataProcess.h @@ -18,36 +18,310 @@ https://github.com/codebase7/mengine */ +/* Include guard. */ #ifndef DATAPROCESS_H #define DATAPROCESS_H -#include "BaseHeader.h" +/* Internal includes. */ #include "Panic.h" +#if _MSC_FULL_VER && _MSC_FULL_VER < 180031101 +/* Visual C++ 2013 introduced support for C99's _Bool. Anything lower than that has to use our fake bool header. */ +#include "../../stdbool.h" +#else +/* Use the built in bool data type. */ +#include +#endif /* _MSC_FULL_VER 180021005 */ + +/* Define extern "C". */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* External includes. */ +#include + +/* Include data object C header. */ +#include "Data_Object/Data_Object.h" + +/* Include DLL_PORT.h */ +#include "../../DLL_PORT.h" + +/* We need to include stdint.h for SIZE_MAX. (MSVC includes it automaticly with the above headers.) */ +#ifdef __GNUC__ +#include +#endif /* __GNUC__ */ + +/* Define C functions. */ +/*! + size_t DataProcess_Trivial_Random_Number_Generator(const size_t min_value, const size_t max_value, const bool reset_rand) + + This function generates psudo-random numbers based on the given max_value. + + @pram min_value, the minimum value that is acceptable for the function to return. + @pram max_value, the maximum value that is acceptable for the function to return. + + E.x. If you want a range of 1 to 100 set min_value to 1 and max_value to 100. + + @pram reset_rand, if this is set to true, the RNG will be re-seeded with the current time value returned by time(). + Otherwise the next psudo-random number from the current seed will be returned. + + Returns the generated psudo-random number if successful. + If the current system time cannot be used to set the random seed, then this function will consistantly return zero (0) + regardless of the range defined by min_value and max_value. +*/ +MSYS_DLL_EXPORT size_t DataProcess_Trivial_Random_Number_Generator(const size_t min_value, const size_t max_value, const bool reset_rand); + +/*! + * int DataProcess_Reallocate_C_String(char ** str, const size_t strLength, const size_t newLength) + * + * Reallocates the given string to be the new length. + * + * Optionally it may do the following: + * - If newLength is 0, it will only deallocate the given string. + * - If newLength is greater than zero, but str is NULL or strLength is zero then, + * this function will only allocate a string of newLength bytes. + * (The string will be NULL filled.) + * - If newLength and strLength is greater than zero, and str is non-NULL, + * then the data from str will be copied into the reallocated string as follows: + * - If the newLength is less than the original length, then the data from str + * will be copied, but any bytes after newLength will be truncated. + * - If the newLength is greater than or equal to the original length then, + * the entire original string will be copied. + * In any instance, the reallocated string may NOT be NULL terminated. This occurs if + * data is copied from the original string and either the original string is not NULL + * terminated, or if the data to be copied from the original string did not include the + * NULL terminator. In addition, the resulting string is always newLength in size if + * this function is successful. + * + * Returns COMMON_ERROR_SUCCESS if successful. + * Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer to pointer is NULL. + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + * Otherwise returns the appropriate error code. + * + * In case of error, the given arguments will NOT be altered. + */ +MSYS_DLL_EXPORT int DataProcess_Reallocate_C_String(char ** str, const size_t strLength, const size_t newLength); + +/*! + * int DataProcess_Reallocate_C_String_With_NULL_Terminator(char ** str, const size_t strLength, size_t * newLength) + * + * Reallocates the given string to be the new length and adds a NULL byte terminator if needed. + * The resulting string is guaranteed to ALWAYS be NULL byte terminated if this function returns COMMON_ERROR_SUCCESS. + * + * Note: This function WILL change the given string to make it NULL byte terminated if the NULL byte + * could not be added during reallocation (this happens if the additional byte would result in a length greater than SIZE_MAX), + * but only if the function returns COMMON_ERROR_SUCCESS. + * + * (This function is a wrapper around DataProcess_Reallocate_C_String(), see that function for more information.) + * + * In case of error, the given arguments will NOT be altered. + */ +MSYS_DLL_EXPORT int DataProcess_Reallocate_C_String_With_NULL_Terminator(char ** str, const size_t strLength, size_t * newLength); + +/*! + * void DataProcess_Deallocate_CString(char ** str) + * + * Destructor function for C-Strings allocated by DataProcess. + * + * WARNING: Giving an object or a C-String not allocated by DataProcess will cause + * undefined behaviour. + * + * If a given pointer is NULL, this function will silently fail. + * + * This function has no return. + */ +MSYS_DLL_EXPORT void DataProcess_Deallocate_CString(char ** str); + +/*! + int DataProcess_Copy_C_String(const char * source, const size_t sourceLength, char ** dest) + + Internal function for coping a const string for later modification. + (Copied string if the function succeeds will be pointed to by dest, copied string is always + the same length as it's source.) + + Note: This function will overwrite dest without deallocating it. If you need whatever + dest points to after this function returns, copy the pointer elsewhere before calling + this function. + + When this string is no longer needed it should be deallocated by + DataProcess_Deallocate_CString(). + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is NULL, or if sourceLength <= 0. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + + No alteration clause: + In the event of an error, this function will not modifiy the arguments given to it. +*/ +MSYS_DLL_EXPORT int DataProcess_Copy_C_String(const char * source, const size_t sourceLength, char ** dest); + +/*! + * int DataProcess_getCStringFromSizeT(const size_t number, char ** str, size_t * strLength) + * + * Takes given size_t and outputs the equivalent human-readable string as a C-String. + * + * The generated string should be freed using DataProcess_Deallocate_CString() when it is no longer needed. + * + * WARNING: This function will NOT deallocate the given string if it is already allocated. + * The pointer WILL be overwritten if this function returns success! + * If you need to keep the pointer for later deallocation, copy it elsewhere before calling this function. + * + * Returns COMMON_ERROR_SUCCESS if successful. str will point to a C-String with string equivalent to number in this case. + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given argument is invalid. + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + * Otherwise returns the appropriate error code. + * + * No alteration clause: + * In case of error, this function will not alter any given argument. + */ +MSYS_DLL_EXPORT int DataProcess_getCStringFromSizeT(const size_t number, char ** str, size_t * strLength); + +/*! + * int DataProcess_Get_SubString_Using_Delimiter(const char * src, const size_t srcLength, const char * delim, const size_t delimLength, + * char ** subStr, size_t * subStrLength, const int searchFromEnd, const int getPriorData) + * + * Basic delimiter based sub-string generation function. + * + * Takes the given source string and looks for the first occurrence of the given delimiter string within it. + * If the delimiter string is found, this function will produce a sub-string with either the remaining bytes in the + * source string that come after the delimiter, or the bytes from the source string that came before the delimiter. + * + * The generated sub-string should be freed using DataProcess_Deallocate_CString() when it is no longer needed. + * + * Optionally if searchFromEnd is non-zero, the search will start from the end of the source string instead of the beginning. + * + * Notes about resulting substring: + * If getPriorData is true, the length of the substring is sourceLength - , otherwise + * the length of the substring is - . + * + * If getPriorData is true the start of the substring is the start of the source string, otherwise it depends + * on the value of searchFromEnd. If searchFromEnd is true, then the start of the substring is + * ( - ), otherwise the start of the string is the . + * + * Truth Table: + * + * -------------------------------------------------------------------------------------------------------------------------------------------------- + * | getPriorData: | searchFromEnd: | Start of substring: | Length of substring: | + * -------------------------------------------------------------------------------------------------------------------------------------------------- + * | true | N / A | Start of source string | ( - ) | + * -------------------------------------------------------------------------------------------------------------------------------------------------- + * | false | true | ( - ) | ( - ) | + * -------------------------------------------------------------------------------------------------------------------------------------------------- + * | false | false | Delimiter offset | ( - ) | + * -------------------------------------------------------------------------------------------------------------------------------------------------- + * + * @pram getPriorData, Chooses the bytes that came before the delimiter or after the delimiter for generating the sub-string from. + * If getPriorData is non-zero then the sub-string will contain the bytes before the delimiter, otherwise the sub-string will + * contain the bytes after the delimiter. + * + * Returns COMMON_ERROR_SUCCESS if the delimiter was found and a sub-string was created. + * Returns COMMON_ERROR_RANGE_ERROR if the delimiter string was not found in the source string. + * Returns COMMON_ERROR_END_OF_DATA if the delimiter string was found at the end of source string + * with no data remaining to create the sub-string with. (subStr and subStrLength will NOT be altered in this case.) + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given argument is invalid. + * Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + * Otherwise returns the appropriate error code. + * + * No alteration clause: + * In case of error, this function will not alter any given argument. + * (For the purposes of this no-alteration clause, the error codes COMMON_ERROR_RANGE_ERROR and COMMON_ERROR_END_OF_DATA + * are considered errors.) + */ +MSYS_DLL_EXPORT int DataProcess_Get_SubString_Using_Delimiter(const char * src, const size_t srcLength, const char * delim, const size_t delimLength, + char ** subStr, size_t * subStrLength, const int searchFromEnd, const int getPriorData); + +/*! + * int DataProcess_Get_SubString_Using_Offset(const char * src, const size_t srcLength, const size_t offset, + * char ** subStr, size_t * subStrLength, const int searchFromEnd, const int getPriorData) + * + * Takes the given source data string and offset and generates a substring from it. The generated sub-string should be freed using + * DataProcess_Deallocate_CString() when it is no longer needed. + * + * Note: The generated substring is not NULL terminated. This is to allow the function to be used with unformatted data. + * + * The substring is generated based on the given offset, searchFromEnd, and getPriorData arguments using the following truth table: + * + * --------------------------------------------------------------------------------------------------------- + * | searchFromEnd: | getPriorData: | substring contents: (Starting and ending offset ranges.) | + * --------------------------------------------------------------------------------------------------------- + * | FALSE | FALSE | offset <--> end of string | + * | TRUE | FALSE | (end of string - offset) <--> end of string | + * | FALSE | TRUE | start of string <--> offset | + * | TRUE | TRUE | start of string <--> (end of string - offset) | + * --------------------------------------------------------------------------------------------------------- + * + * Note: searchFromEnd only controls the offset's point of origin. (I.e. which end of the given + * string the offset is based at.) searchFromEnd does NOT control what substring is selected + * based on the given offset; the selection is made based on getPriorData. + * + * @pram src (const char *), the source string that the substring is generated from. + * + * @pram srcLength (const size_t), the length of the source string. (Function may fail if given length is invalid / incorrect.) + * + * @pram offset (const size_t), the starting offset within the source string to start copying data from. + * + * @pram subStr (char **), a pointer that points to the pointer for the generated sub-string. WARNING: The pointer value will be overwritten + * if this call returns COMMON_ERROR_SUCCESS. This pointer should be released by calling DataProcess_Deallocate_CString(). + * + * @pram subStrLength (size_t *), a pointer to a size_t value that contains the length of the generated substring. + * + * @pram searchFromEnd (integer treated as boolean), Which end of the given source string the given offset is is based at. + * (Start of given string = 0, end of given string = any non-zero value.) + * + * @pram getPriorData (integer treated as boolean), Whether or not to use the segment of data that comes before the given offset as the + * generated substring. (False = zero, True = any non-zero value.) + * + * Returns COMMON_ERROR_SUCCESS if successfull. In this case the values of subStr and subStrLength will change to contain a pointer to the generated + * substring and the length of the generated substring respectively. + * + * Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is NULL, the length of the source string is 0, the given offset is less than zero or + * greater than or equal to the length of the string. + * + * Returns COMMON_ERROR_MEMORY_ERROR if memory allocation for the substring fails. + * + * Returns COMMON_ERROR_END_OF_DATA if the given arguments would generate a substring that contains no data. + * (I.e. The generated substring's length would be zero.) + * + * Returns COMMON_ERROR_RANGE_ERROR if an internal calculation fails. + * + * Otherwise returns the approperite error code. + * + * No alteration clause: + * In case of error, this function will not alter any given argument. + * (For the purposes of this no-alteration clause, the error codes COMMON_ERROR_RANGE_ERROR and COMMON_ERROR_END_OF_DATA + * are considered errors.) + */ +MSYS_DLL_EXPORT int DataProcess_Get_SubString_Using_Offset(const char * src, const size_t srcLength, const size_t offset, + char ** subStr, size_t * subStrLength, const int searchFromEnd, const int getPriorData); +#ifdef __cplusplus +} /* End of extern "C". */ +#endif /* __cplusplus */ + +/* Only define the below with a CPP compiler. */ +#ifdef __cplusplus + +/* Internal includes. */ +#include "DataProcess_Endianness_Check.h" +/* External includes. */ +#include + +/* Define DataProcess namespace. */ namespace DataProcess{ class Data_Object{ private: - char * data; // Pointer to data. - size_t length; // Length of data. - size_t capacity; // Length of data we can store. + MSYS_DataObject_T * obj; /* The actual C object. */ public: - MSYS_DLL_EXPORT Data_Object() - { - data = NULL; - length = 0; - capacity = 0; - } + MSYS_DLL_EXPORT Data_Object(); + MSYS_DLL_EXPORT ~Data_Object() { - if (data != NULL) - { - free(data); - data = NULL; - } - length = 0; - capacity = 0; + if (obj != NULL) + { + MSYS_Destroy_DataObject(&(this->obj)); + } } MSYS_DLL_EXPORT Data_Object(const Data_Object & source); MSYS_DLL_EXPORT Data_Object(const std::string & source); @@ -57,7 +331,7 @@ class Data_Object{ MSYS_DLL_EXPORT Data_Object & operator= (const std::string &source); MSYS_DLL_EXPORT Data_Object & operator= (const char &source); MSYS_DLL_EXPORT Data_Object & operator+= (const char &source); - MSYS_DLL_EXPORT Data_Object & operator+= (const std::string source); + MSYS_DLL_EXPORT Data_Object & operator+= (const std::string & source); MSYS_DLL_EXPORT Data_Object & operator+= (const Data_Object & source); MSYS_DLL_EXPORT char * substr(size_t offset, size_t endpoint) const; @@ -80,6 +354,25 @@ class Data_Object{ */ MSYS_DLL_EXPORT int Buffer_Copy(const DataProcess::Data_Object & source, size_t copy_offset = 0, size_t copy_length = 0); + /*! + int DataProcess::Data_Object::get_C_Struct(MSYS_DataObject ** retPtr) + + This function returns a pointer to the internal MSYS_DataObject C object, which can be used + by the MSYS_DataObject C functions. + + WARNING: The pointer returned by this function will become invalid if the DataProcess::Data_Object that it + originated from gets deallocated. DO NOT attempt to use the returned pointer if it's parent DataProcess::Data_Object + has been deallocated. Attempting to do so will result in undefined behavior. + + Note: This function will overwrite the contents of retPtr if it's successful, otherwise the contents of + retPtr will not be altered. + + Returns COMMON_ERROR_SUCCESS if successful, retPtr will contain pointer to MSYS_DataObject in this instance. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given retPtr is invalid. + Returns COMMON_ERROR_MEMORY_ERROR if a memory error occurs. + */ + MSYS_DLL_EXPORT int get_C_Struct(MSYS_DataObject ** retPtr); + /*! const char * DataProcess::Data_Object::get_Pointer() const @@ -90,16 +383,34 @@ class Data_Object{ MSYS_DLL_EXPORT const char * get_Pointer() const; /*! - char * DataProcess::Data_Object::get_Copy() const + char * DataProcess::Data_Object::get_Data_Copy() const This function creates a copy of the data in the Data_Object and then returns a non-const pointer to that copy. Note: The copy that is created will not be freed after the Data_Object goes out of scope. (I.e is destroyed.) - The caller must free the copy's memory on their own. + The caller must free the copy's memory using DataProcess::Data_Object::destroy_Data_Copy(). + + Note: The resulting copy (if the function succeeds) will have a memory buffer length identical to the value + returned by Data_Object::size() at the time this function was called. If this function fails to allocate memory, it will return NULL. */ - MSYS_DLL_EXPORT char * get_Copy() const; + MSYS_DLL_EXPORT char * get_Data_Copy() const; + + /*! + void DataProcess::Data_Object::destroy_Data_Copy(char ** obj) const + + Destroys (Deallocates) the copied object data, created by + DataProcess::Data_Object::get_Data_Copy(). + + WARNING: DO NOT ATTEMPT to deallocate DataProcess::Data_Object objects with this function. + Use ~DataProcess::Data_Object() instead. Failure to do so, will result in + UNDEFINED BEHAVIOR. + + This function has no return. + */ + MSYS_DLL_EXPORT void destroy_Data_Copy(char ** obj) const; + MSYS_DLL_EXPORT size_t get_length() const; /*! @@ -122,6 +433,21 @@ class Data_Object{ */ MSYS_DLL_EXPORT size_t size() const; + /*! + int DataProcess::Data_Object::Shallow_Copy(const DataProcess::Data_Object & source) + + Makes the object that this function is called on a shallow copy of the given DataProcess::Data_Object. + (Variable only copy.) + + Note: If this function succeeds, then ANY changes made to either the source object or the object + that this function is called on will desync the other object. Care should be taken to not + cause a desync between a shallow copied object and it's source object. Failure to prevent + a desync, will result in UNDEFINED BEHAVIOR. + + Returns COMMON_ERROR_SUCCESS if the shallow copy is successful. + */ + MSYS_DLL_EXPORT int Shallow_Copy(const DataProcess::Data_Object & source); + /*! void DataProcess::Data_Object::set(const char * source, const size_t & source_length) @@ -242,24 +568,139 @@ class Data_Object{ Returns zero (0) if an error occurs or if no data was copied. */ MSYS_DLL_EXPORT size_t insert(size_t offset, const Data_Object & source); + + /*! + int DataProcess::Data_Object::Replace(const size_t offset, const char source) + + This function replaces a single char in the Data_Object at the given offset. + + This function is a wrapper for MSYS_DataObject_Replace_With_CString(). See + MSYS_DataObject_Replace_With_CString() for expected behavior and error codes. + */ + MSYS_DLL_EXPORT int Replace(const size_t offset, const char source); + + /*! + int DataProcess::Data_Object::Replace(const size_t offset, const std::string & source) + + This function replaces the contents of the Data_Object at the given offset with the contents + the given string. + + Note: This function does not allocate memory if needed. If automatic reallocation of + memory is desired, call DataProcess::Data_Object::Overwrite() instead. + + This function is a wrapper for MSYS_DataObject_Replace_With_CString(). See + MSYS_DataObject_Replace_With_CString() for expected behavior and error codes. + */ + MSYS_DLL_EXPORT int Replace(const size_t offset, const std::string & source); + + /*! + int DataProcess::Data_Object::Replace(const size_t offset, const DataProcess::Data_Object & source) + + This function replaces the contents of the Data_Object at the given offset with the contents + the given source Data Object. + + Note: This function does not allocate memory if needed. If automatic reallocation of + memory is desired, call DataProcess::Data_Object::Overwrite() instead. + + This function is a wrapper for MSYS_DataObject_Replace_With_CString(). See + MSYS_DataObject_Replace_With_CString() for expected behavior and error codes. + */ + MSYS_DLL_EXPORT int Replace(const size_t offset, const DataProcess::Data_Object & source); + + /*! + int DataProcess::Data_Object::Overwrite(const size_t offset, const char source) + + This function overwrites a single char in the Data_Object at the given offset. + + This function is a wrapper for MSYS_DataObject_Overwrite_With_CString(). See + MSYS_DataObject_Overwrite_With_CString() for expected behavior and error codes. + */ + MSYS_DLL_EXPORT int Overwrite(const size_t offset, const char source); + + /*! + int DataProcess::Data_Object::Overwrite(const size_t offset, const std::string & source) + + This function overwrites the contents of the Data_Object at the given offset with the contents + the given string. + + Note: This function allocates memory if needed. If automatic reallocation of + memory is NOT desired, call DataProcess::Data_Object::Replace() instead. + + This function is a wrapper for MSYS_DataObject_Overwrite_With_CString(). See + MSYS_DataObject_Overwrite_With_CString() for expected behavior and error codes. + */ + MSYS_DLL_EXPORT int Overwrite(const size_t offset, const std::string & source); + + /*! + int DataProcess::Data_Object::Overwrite(const size_t offset, const DataProcess::Data_Object & source) + + This function overwrites the contents of the Data_Object at the given offset with the contents + the given source Data Object. + + Note: This function allocates memory if needed. If automatic reallocation of + memory is NOT desired, call DataProcess::Data_Object::Replace() instead. + + This function is a wrapper for MSYS_DataObject_Overwrite_With_CString(). See + MSYS_DataObject_Overwrite_With_CString() for expected behavior and error codes. + */ + MSYS_DLL_EXPORT int Overwrite(const size_t offset, const DataProcess::Data_Object & source); }; /*! - size_t DataProcess::Trivial_Random_Number_Generator(const size_t & min_value, const size_t & max_value, const bool & reset_rand) + int DataProcess::CopyCStringToStdString(const char * source, const size_t & sourceLength, std::string & dest) - This function generates psudo-random numbers based on the given max_value. + This function takes a given C style string and copies it to a std::string by copying the amount of bytes given by + sourceLength. + The resulting string if this function succeeds will be the same length as the given sourceLength. - @pram min_value, the minimum value that is acceptable for the function to return. - @pram max_value, the maximum value that is acceptable for the function to return. + NOTE: This function will NOT deallocate a pre-existing string, but it WILL OVERWRITE IT. + (So if you need to use or deallocate it later, make sure to copy the string before calling + this function.) - E.x. If you want a range of 1 to 100 set min_value to 1 and max_value to 100. + WARNING: This function will copy the given amount of bytes (as defined by sourceLength) from the given source pointer. + If the source string's buffer is NOT at least sourceLength bytes long, then the result of this function is UNDEFINED. + (Depending on the system, a memory access violation may occur, or the function may copy data from beyond the end of + the source string's buffer, but neither of these behaviours can be guaranteed by this function. It is up to the caller + to ensure that the given amount of bytes to copy is within the given source string's allocated memory buffer.) + + Returns COMMON_ERROR_SUCCESS if the function succeeds. (Result string will be stored in dest.) + Returns COMMON_ERROR_INVALID_ARGUMENT if the given source string pointer is NULL, or the given length is less than or equal to zero. + + No alteration clause: + In case of error, this function will not alter any given argument. +*/ +MSYS_DLL_EXPORT int CopyCStringToStdString(const char * source, const size_t & sourceLength, std::string & dest); + +/*! + int DataProcess::CopyStdStringToCString(const std::string & source, char ** dest) + + This function takes a given std::string and copies it to a C style string. + The resulting string if this function succeeds will be the same length as the source string. - By default this function returns a number between 0 and 255. + NOTE: This function will NOT deallocate a pre-existing pointer, but it WILL OVERWRITE IT. + (So if you need to use or deallocate it later, make sure to copy the pointer before calling + this function.) - @pram reset_rand, if this is set to true, the rng will be re-seeded with the current time value returned by time(NULL). - Otherwise the next psudo-random number from the current seed will be returned. (Default) + When the result string of this function is no longer needed, it should be deallocated by calling + DataProcess_Deallocate_CString(). - Returns the generated psudo-random number. + Returns COMMON_ERROR_SUCCESS if the function succeeds. (Result string will be pointed to by dest.) + Returns COMMON_ERROR_INVALID_ARGUMENT if the given source string is empty, or it's data could not be accessed. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + + No alteration clause: + In case of error, this function will not alter any given argument. +*/ +MSYS_DLL_EXPORT int CopyStdStringToCString(const std::string & source, char ** dest); + +/*! + size_t DataProcess::Trivial_Random_Number_Generator(const size_t & min_value, const size_t & max_value, const bool & reset_rand) + + A wrapper around the DataProcess_Trivial_Random_Number_Generator() C function. (For compatibility.) + + See DataProcess_Trivial_Random_Number_Generator()'s description for documentation. + + Note: Default values for arguments are: min_value 0, max_value 255, reset_rand false. */ MSYS_DLL_EXPORT size_t Trivial_Random_Number_Generator(const size_t & min_value = 0, const size_t & max_value = 255, const bool & reset_rand = false); @@ -292,7 +733,7 @@ MSYS_DLL_EXPORT short IncrementingSort(std::vector & sort); MSYS_DLL_EXPORT short DecrementingSort(std::vector & sort); /*! - bool DataProcess::CheckForEOF(fstream & source) + bool DataProcess::CheckForEOF(std::fstream & source) Checks the given file stream for eof (End of File) status and returns the result. @@ -302,7 +743,7 @@ MSYS_DLL_EXPORT short DecrementingSort(std::vector & sort); Returns true if the fstream is at EOF. (End of File) Returns false otherwise. (Note: This function will return false if the fstream has failed. (i.e source.fail() == true).) */ -MSYS_DLL_EXPORT bool CheckForEOF(fstream & source); +MSYS_DLL_EXPORT bool CheckForEOF(std::fstream & source); /*! GenerateUID(long int length) @@ -519,6 +960,8 @@ MSYS_DLL_EXPORT short RegularExpressionParser(const std::string & expression, co MSYS_DLL_EXPORT short RegularExpressionParser(const DataProcess::Data_Object & expression, const DataProcess::Data_Object & input, Panic::Panic_ERROR * error = NULL); } -#endif +#endif /* __cplusplus */ + +#endif /* DATAPROCESS_H */ -// End of DataProcess.h +/* End of DataProcess.h */ diff --git a/src/Core/Src/DataProcess_Endianness_Check.cpp b/src/Core/Src/DataProcess_Endianness_Check.cpp new file mode 100644 index 0000000..f30da8f --- /dev/null +++ b/src/Core/Src/DataProcess_Endianness_Check.cpp @@ -0,0 +1,126 @@ +/*! + Multiverse Engine Project 11/7/2015 DataProcess DataProcess_Endianness_Check.cpp + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include internal header. */ +#include "DataProcess_Endianness_Check.h" + +template +int DataProcess_Endianness_Check(const T & a) +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + T t = 1; /* Variable to check. */ + + /* Cast t to a char string and see if the result is 1. */ + if (((char*)&t)[0]) + { + /* The first byte is 1 so it's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + else + { + /* + * The first byte is 0 so, check and see if the last byte is non-zero. + * If it is, then the host is big endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if (((char*)&t)[((sizeof(t)) - 1)]) + { + /* It's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +template<> +int DataProcess_Endianness_Check(const float & a) +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + float t = 1.0; /* Variable to check. */ + + /* Cast t to a char string and see if the first 2 values are 0x3F80. */ + if ((((char*)&t)[0] == 0x3F) && ((((char*)&t)[1] == 0x80))) + { + /* The first 2 bytes are 0x3F80 so it's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + else + { + /* + * The first check did not pass, so check and see if the last 2 bytes are 0x803F. + * If they are, then the host is little endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if ((((char*)&t)[(sizeof (float) - 1)] == 0x80) && ((((char*)&t)[(sizeof (float) - 2)] == 0x3F))) + { + /* It's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +template<> +int DataProcess_Endianness_Check(const double & a) +{ + /* Init vars. */ + int ret = MSYS_UNKNOWN_ENDIANNESS; /* The result of this function. */ + double t = 1.0; /* Variable to check. */ + + /* Cast t to a char string and see if the first 2 values are 0x3FF0. */ + if ((((char*)&t)[0] == 0x3F) && ((((char*)&t)[1] == 0xF0))) + { + /* The first 2 bytes are 0x3F80 so it's big endian. */ + ret = MSYS_BIG_ENDIAN; + } + else + { + /* + * The first check did not pass, so check and see if the last 2 bytes are 0xF03F. + * If they are, then the host is little endian. + * + * Otherwise the host is using something like middle-endian to store + * the value, but we would need more checks to determine what exact + * kind of endianness the host is using. + */ + if ((((char*)&t)[(sizeof (double) - 1)] == 0xF0) && ((((char*)&t)[(sizeof (double) - 2)] == 0x3F))) + { + /* It's little endian. */ + ret = MSYS_LITTLE_ENDIAN; + } + } + + /* Return the result. */ + return ret; +} + +/* End of DataProcess_Endianness_Check.cpp */ diff --git a/src/Core/Src/DataProcess_Endianness_Check.h b/src/Core/Src/DataProcess_Endianness_Check.h new file mode 100644 index 0000000..148e5c5 --- /dev/null +++ b/src/Core/Src/DataProcess_Endianness_Check.h @@ -0,0 +1,68 @@ +/*! + Multiverse Engine Project 11/7/2015 DataProcess DataProcess_Endianness_Check.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef DATAPROCESS_ENDIANNESS_CHECK_H +#define DATAPROCESS_ENDIANNESS_CHECK_H + +/* Define Endianness Result Values. */ +#define MSYS_BIG_ENDIAN 0 +#define MSYS_LITTLE_ENDIAN 1 +#define MSYS_UNKNOWN_ENDIANNESS 2 + +/* Define namespace. */ +namespace DataProcess { + +/*! + * template + * int DataProcess_Endianness_Check(T & a) + * + * Template function which checks the host's endianness for the + * given argument's data type. + * + * Note: The given argument is not altered or used by the function. + * It's only used to exploit the C++ template generator. + * + * The specializations for float and double are required as floating + * point data types must be detected differently from integers. + * + * Returns MSYS_BIG_ENDIAN if the given data type is stored as big + * endian on the host machine. + * + * Returns MSYS_LITTLE_ENDIAN if the given data type stored as little + * endian on the host machine. + * + * Returns MSYS_UNKNOWN_ENDIANNESS if the given data type's byte ordering + * is unknown for the given host. + */ +template +int DataProcess_Endianness_Check(const T & a); + +template<> +int DataProcess_Endianness_Check(const float & a); + +template<> +int DataProcess_Endianness_Check(const double & a); + +} /* namespace DataProcess */ + +#endif /* DATAPROCESS_ENDIANNESS_CHECK_H */ + +/* End of DataProcess_Endianness_Check.h */ diff --git a/src/Core/Src/DataProcess_Memory_Functions.c b/src/Core/Src/DataProcess_Memory_Functions.c new file mode 100644 index 0000000..8ed5162 --- /dev/null +++ b/src/Core/Src/DataProcess_Memory_Functions.c @@ -0,0 +1,208 @@ +/*! + Multiverse Engine Project DataProcess DataProcess_Memory_Functions.c 21/12/2015 + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Set extern C. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Internal includes. */ +#include "DataProcess.h" +#include "../../Common/Src/Error_Handler/Common_Error_Handler_Error_Codes.h" /* Defines Common Error Codes. */ + +/* External includes. */ +#include +#include +#include +#include + +int DataProcess_Reallocate_C_String(char ** str, const size_t strLength, const size_t newLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code for this function. */ + char * tempStr = NULL; /* Temporary string pointer used during reallocation.*/ + + /* Check for invalid arguments. */ + if (str != NULL) + { + /* Allocate memory for new string. (If needed.) */ + if (newLength > 0) + { + tempStr = (char *)malloc(newLength); + if (tempStr != NULL) + { + /* NULL out the new string. */ + memset(tempStr, '\0', newLength); + + /* Check and see if we need to copy the old data. */ + if (((*str) != NULL) && (strLength > 0)) + { + /* Determine which length is longer. */ + if (newLength < strLength) + { + /* Copy the old data. */ + memcpy(tempStr, (*str), newLength); + } + else + { + /* Copy the old data. */ + memcpy(tempStr, (*str), strLength); + } + } + } + else + { + /* Could not allocate memory. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + + /* Deallocate the old string. (If needed.) */ + if ((*str) != NULL) + { + DataProcess_Deallocate_CString(str); + } + + /* Copy the new string pointer if needed. */ + if ((newLength > 0) && (ret == COMMON_ERROR_UNKNOWN_ERROR) && (tempStr != NULL)) + { + (*str) = tempStr; + } + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int DataProcess_Reallocate_C_String_With_NULL_Terminator(char ** str, const size_t strLength, size_t * newLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + size_t newSize = 0; /* The modified size if needed. */ + + /* Check for valid args. */ + if ((str != NULL) && (newLength != NULL) && (((*str) == NULL) || (strLength > 0))) + { + /* Check and see if new length is greater than zero (0). */ + /* Safety check on newSize.... */ + newSize = ((*newLength) > 0) ? (((*newLength) < (SIZE_MAX - (sizeof(char)))) ? ((*newLength) + (sizeof(char))) : (*newLength)) : (0); + + /* Call DataProcess_Reallocate_C_String(). (It will NULL out the allocated buffer before copying data.) */ + ret = DataProcess_Reallocate_C_String(str, strLength, newSize); + if ((ret == COMMON_ERROR_SUCCESS) && (str != NULL) && ((*str) != NULL)) + { + /* See if the string is NULL terminated. */ + if ((newSize > 0) && (((*str)[(newSize - 1)]) != '\0')) + { + /* Set the last byte to NULL, because it should be. + (The last byte is always NULL if this function returns COMMON_ERROR_SUCCESS.) + */ + ((*str)[(newSize - 1)]) = '\0'; + } + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +void DataProcess_Deallocate_CString(char ** str) +{ + /* Check for invalid arguments. */ + if ((str != NULL) && ((*str) != NULL)) + { + /* Deallocate (*str) and set it to NULL. */ + free((*str)); + (*str) = NULL; + } + + /* Exit function. */ + return; +} + +int DataProcess_Copy_C_String(const char * source, const size_t sourceLength, char ** dest) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of other engine functions. */ + size_t x = 0; /* Loop counter. */ + char * tempDest = NULL; /* Temporary pointer used to copy the source string. */ + + /* Check for valid arguments. */ + if ((source != NULL) && (sourceLength > 0) && (dest != NULL)) + { + /* Allocate memory for the copy. */ + retFromCall = DataProcess_Reallocate_C_String(&tempDest, 0, sourceLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempDest != NULL)) + { + /* Copy the string. */ + for (x = 0; (x < sourceLength); x++) + { + tempDest[x] = source[x]; + } + + /* Copy the pointer. */ + (*dest) = tempDest; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for tempDest. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Check for no success. */ + if (ret != COMMON_ERROR_SUCCESS) + { + /* Deallocate tempDest if needed. */ + if (tempDest != NULL) + { + DataProcess_Deallocate_CString(&tempDest); + } + } + + /* Exit function. */ + return ret; +} + +#ifdef __cplusplus +} /* End of extern "C". */ +#endif /* __cplusplus */ diff --git a/src/Core/Src/Data_Object/CMakeLists.txt b/src/Core/Src/Data_Object/CMakeLists.txt new file mode 100644 index 0000000..6ba58e0 --- /dev/null +++ b/src/Core/Src/Data_Object/CMakeLists.txt @@ -0,0 +1,11 @@ + +# Set source files. +set(Data_Object_Includes Data_Object.c) + +# Create libraries. +add_library(Data_Object_Multiverse_Engine SHARED ${Data_Object_Includes}) +add_library(Data_Object_Multiverse_Engine_Static STATIC ${Data_Object_Includes}) + +# Link libraries. +target_link_libraries(Data_Object_Multiverse_Engine DataProcess_Memory_Management_Multiverse_Engine) +target_link_libraries(Data_Object_Multiverse_Engine_Static DataProcess_Memory_Management_Multiverse_Engine_Static) diff --git a/src/Core/Src/Data_Object/Data_Object.c b/src/Core/Src/Data_Object/Data_Object.c new file mode 100644 index 0000000..6fa12c8 --- /dev/null +++ b/src/Core/Src/Data_Object/Data_Object.c @@ -0,0 +1,2168 @@ +/* + Multiverse Engine Project Core Data_Object.c 02/4/2016 + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "../DataProcess.h" +#include "../../../Common/Src/Error_Handler/Common_Error_Handler_Error_Codes.h" + +/* External includes. */ +#include +#include +#include +#include + +/* + * + * Private data structures and functions are defined below. + * (Should not be used outside this source file, subject to change without warning.) + */ +#include "Data_Object_Private_Structure.c" + +/*! + void MSYS_Blank_DataObject_Private(MSYS_DataObject_T_Private * buffer) + + Blanks out a newly allocated MSYS_DataObject_T_Private object. + + If the given pointer is invalid, this function will silently fail. + + This function has no return code. + */ +void MSYS_Blank_DataObject_Private(MSYS_DataObject_T_Private * buffer) +{ + /* Check for valid pointer. */ + if (buffer != NULL) + { + buffer->data = NULL; + buffer->length = 0; + buffer->capacity = 0; + buffer->refcount = 0; + } + + /* Exit function. */ + return; +} + +/*! + void MSYS_Clear_DataObject_Private(MSYS_DataObject_T_Private * obj) + + Resets the given MSYS_DataObject_T_Private object to it's default state. + (I.e. No allocated memory buffer, length and capacity are both set to zero.) + + If the given pointer is invalid this function will silently fail. + + This function has no return. + */ +void MSYS_Clear_DataObject_Private(MSYS_DataObject_T_Private * obj) +{ + /* Check for valid pointer. */ + if (obj != NULL) + { + /* Check and see if we need to deallocate the buffer. */ + if (obj->data != NULL) + { + DataProcess_Deallocate_CString((&(obj->data))); + } + + /* Reset the length and capacity. */ + obj->capacity = 0; + obj->length = 0; + } + + /* Exit function. */ + return; +} + +/*! + void MSYS_Reset_DataObject_Private(MSYS_DataObject_T_Private * obj) + + This function erases all data in the data object's buffer, (Sets all bytes to NULL) + and sets it's length to zero (0). The capacity is left unchanged. + + If the given pointer / object is invalid, or there is no allocated buffer, + this function will silently fail. + + This function has no return. + */ +void MSYS_Reset_DataObject_Private(MSYS_DataObject_T_Private * obj) +{ + /* Init vars. */ + size_t x = 0; /* Counter for erase loop. */ + + /* Check for valid pointer, and object. */ + if ((obj != NULL) && (obj->data != NULL) && (obj->capacity > 0)) + { + /* NULL out the buffer. (erase loop.) */ + for (x = 0; (x < (obj->capacity)); x++) + { + obj->data[x] = '\0'; + } + + /* Reset the length. */ + obj->length = 0; + } + + /* Exit function. */ + return; +} + +/*! + int MSYS_Check_DataObject_Consistency_Private(const MSYS_DataObject_T_Private * obj) + + Checks the given MSYS_DataObject_T_Private object for consistency. + + Returns COMMON_ERROR_COMPARISON_PASSED if the given object is consistant. + Returns COMMON_ERROR_COMPARISON_FAILED if the given object is NOT consistant. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is invalid. + */ +int MSYS_Check_DataObject_Consistency_Private(const MSYS_DataObject_T_Private * obj) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + + /* Check for valid pointer. */ + if (obj != NULL) + { + /* Check for allocated buffer. */ + if (obj->data != NULL) + { + /* Check for valid capacity. */ + if (obj->capacity >= 0) + { + /* Check for valid length. */ + if (obj->length <= obj->capacity) /* Make sure that obj->length is less than or equal to obj->capacity. */ + { + /* Valid object. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + else + { + /* Invalid object. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Invalid object. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* No allocated buffer, check length and capacity. */ + if ((obj->capacity == 0) && (obj->length == 0)) + { + /* Valid object. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + else + { + /* Invalid object. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + } + else + { + /* Invalid pointer. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +/*! + void MSYS_Destroy_DataObject_Private(MSYS_DataObject_T_Private ** buffer) + + Deallocates the given MSYS_DataObject_T_Private object. + + This function has no return. + */ +void MSYS_Destroy_DataObject_Private(MSYS_DataObject_T_Private ** buffer) +{ + /* Check for valid pointer. */ + if ((buffer != NULL) && (*(buffer) != NULL)) + { + /* Check the reference count to see if it's greater than zero. */ + if ((*buffer)->refcount > 0) + { + /* Decrease the reference count. */ + (*buffer)->refcount--; + } + else + { + /* The last reference has been deallocated. Clear and deallocate the private structure. */ + MSYS_Clear_DataObject_Private(*buffer); + DataProcess_Deallocate_CString((char**)(buffer)); + } + } + + /* Exit function. */ + return; +} + +/*! + int MSYS_DataObject_Insert_CString_No_Allocation_Private(char * buffer, size_t * bufferLength, const size_t bufferCapacity, + const char * data, const size_t dataLength, const size_t offset) + + Inserts the given data into the given buffer at the given offset. + + Note 1: This function, as the name implies, does NOT allocate memory. The given buffer must be big enough to + store the data given to it. If the buffer is not big enough to store the given data in addition to the + original data, then an error will be returned and the buffer will not be modified. + + Note 2: Any data that exists after the given offset will be "moved" to after the given data in the buffer. + (I.e. Data after the given offset will be moved to the following offset: (offset + dataLength).) + + Note 3: bufferLength will be updated with the current (content) length of the buffer if the function succeeds. + (Otherwise it will not be updated, as the buffer itself will not be modified.) + + Note 4: This function like all other Data_Object functions, is limited by the compiler's defined SIZE_MAX. If + the given size values would cause an overflow of SIZE_MAX, this function will return an error. + + Note 5: The given bufferLength must be less than or equal to the given bufferCapacity or an error will be returned. + + Note 6: This function, like all private functions, should not be called directly. Use the public functions instead, as + this function and it's prototype are subject to change without warning. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, dataLength is zero, or the given bufferLength is greater + than the given bufferCapacity. + Returns COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED if the given size values would cause an overflow of SIZE_MAX. + Returns COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL if the given memory buffer is not big enough to store the given data. + */ +int MSYS_DataObject_Insert_CString_No_Allocation_Private(char * buffer, size_t * bufferLength, const size_t bufferCapacity, + const char * data, const size_t dataLength, const size_t offset) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + size_t realOffset = 0; /* The offset used in the memcpy() calls. */ + + /* Check args. */ + if ((buffer != NULL) && (bufferLength != NULL) && ((*bufferLength) <= bufferCapacity) && (data != NULL) && (dataLength > 0)) + { + /* If the given offset is bigger than the content length of the buffer, use the end of the buffer for the offset. */ + realOffset = ((offset < (*bufferLength)) ? (offset) : (*bufferLength)); + + /* Check for UINT Overflow. + This function keeps the original data, and simply inserts the given data at the given offset. + + So the total amount of data we will wind up with is: ((*bufferLength) + dataLength). + */ + if (((SIZE_MAX - realOffset) >= dataLength) && ((SIZE_MAX - (*bufferLength)) >= dataLength)) + { + /* Determine if we need to reallocate memory. */ + if ((bufferCapacity - (*bufferLength)) >= dataLength) + { + /* Allocated buffer has enough avaiable memory, so copy the existing data after the offset passed the new data. (If needed.) */ + if (realOffset < (*bufferLength)) + { + memcpy((buffer + realOffset + dataLength), (buffer + realOffset), ((*bufferLength) - realOffset)); + } + + /* Copy the new data. */ + memcpy((buffer + realOffset), data, dataLength); + + /* Update length. */ + (*bufferLength) = ((*bufferLength) + dataLength); + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* The given data will not fit in the existing buffer. */ + ret = COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL; + } + } + else + { + /* Cannot add the given data, total data size would be bigger than SIZE_MAX. */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +/* + * + * Public functions are defined below. + * + */ + +int MSYS_Create_DataObject(MSYS_DataObject_T ** buffer) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of the call to another engine function. */ + MSYS_DataObject_T * tempPtr = NULL; /* Used to create the MSYS_DataObject_T object. */ + MSYS_DataObject_T_Private * realStrPtr = NULL; /* Used to create the MSYS_DataObject_T_Private object. */ + char ** newPtP = NULL; /* Used to allocate pointer to pointer for the public object. */ + + /* Check vars. */ + if (buffer != NULL) + { + /* Allocate memory for the public object. */ + retFromCall = DataProcess_Reallocate_C_String(((char**)(&tempPtr)), 0, sizeof(MSYS_DataObject_T)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempPtr != NULL)) + { + /* Set the internal pointer to NULL. */ + tempPtr->ppObject = NULL; + + /* Create new pointer to pointer for the public object's ppObject variable. */ + retFromCall = DataProcess_Reallocate_C_String(((char**)(&newPtP)), 0, sizeof(char*)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (newPtP != NULL)) + { + /* Allocate memory for the private object. */ + retFromCall = DataProcess_Reallocate_C_String(((char**)(&realStrPtr)), 0, sizeof(MSYS_DataObject_T_Private)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (realStrPtr != NULL)) + { + /* Blank the private structure. */ + MSYS_Blank_DataObject_Private(realStrPtr); + + /* Copy the pointer for the private structure into the pointer to pointer variable. + + Note: We are doing the memcpy here to copy the address of realStrPtr + into the value of newPtP. (I.e. (*newPtP) should equal the address of + realStrPtr. Not (newPtP). + */ + *newPtP = (char*)realStrPtr; + + /* Copy the pointer to pointer into the public structure. + + The newPtP pointer is a double pointer at this point. + [newPtP @ 0x: 0x]. + */ + tempPtr->ppObject = (void **)newPtP; + + /* Copy the pointer to the public structure into the buffer argument. */ + (*buffer) = tempPtr; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for the private object. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Deallocate memory for the pointer to pointer variable. */ + DataProcess_Deallocate_CString(((char**)(&newPtP))); + + /* Deallocate memory for the public structure. */ + DataProcess_Deallocate_CString(((char**)(&tempPtr))); + } + } + else + { + /* Could not allocate memory for the public object's ppObject variable. */ + ret = COMMON_ERROR_MEMORY_ERROR; + + /* Deallocate memory for the public structure. */ + DataProcess_Deallocate_CString(((char**)(&tempPtr))); + } + } + else + { + /* Could not allocate memory for the public object. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +void MSYS_Destroy_DataObject(MSYS_DataObject_T ** buffer) +{ + /* Check for valid pointer. */ + if ((buffer != NULL) && ((*buffer) != NULL) && (((*buffer)->ppObject) != NULL)) + { + /* Check for private structure. */ + if ((*((*buffer)->ppObject)) != NULL) + { + /* Deallocate the private structure. */ + MSYS_Destroy_DataObject_Private(((MSYS_DataObject_T_Private **)((*buffer)->ppObject))); + } + + /* Deallocate the pointer to pointer object. (ppObject itself.) */ + DataProcess_Deallocate_CString(((char**)(&((*buffer)->ppObject)))); + + /* Deallocate the public structure. */ + DataProcess_Deallocate_CString(((char**)(buffer))); + } + + /* Exit function. */ + return; +} + +int MSYS_Check_DataObject_Consistency(const MSYS_DataObject_T * obj) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + const MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((obj != NULL) && ((obj->ppObject) != NULL) && (*(obj->ppObject) != NULL)) + { + /* Get back the real pointer. */ + realPtr = (const MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Call the real function. */ + ret = (MSYS_Check_DataObject_Consistency_Private(realPtr)); + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Get_Capacity(const MSYS_DataObject_T * obj, size_t * retPtr) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + const MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((obj != NULL) && ((obj->ppObject) != NULL) && (*(obj->ppObject) != NULL) && (retPtr != NULL)) + { + /* Get back the real pointer. */ + realPtr = (const MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Copy the capacity. */ + (*retPtr) = realPtr->capacity; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Get_Length(const MSYS_DataObject_T * obj, size_t * retPtr) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + const MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((obj != NULL) && ((obj->ppObject) != NULL) && (*(obj->ppObject) != NULL) && (retPtr != NULL)) + { + /* Get back the real pointer. */ + realPtr = (const MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Copy the length. */ + (*retPtr) = realPtr->length; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Get_Pointer(const MSYS_DataObject_T * buffer, const char ** retPtr) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + const MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((buffer != NULL) && ((buffer->ppObject) != NULL) && (*(buffer->ppObject) != NULL) && (retPtr != NULL)) + { + /* Get back the real pointer. */ + realPtr = (const MSYS_DataObject_T_Private *)(*(buffer->ppObject)); + + /* Return the internal data pointer. */ + (*retPtr) = realPtr->data; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Get_Data_Copy(const MSYS_DataObject_T * buffer, char ** retPtr) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + const MSYS_DataObject_T_Private * realPtr = NULL; + char * tempCopy = NULL; + + /* Check args. */ + if ((buffer != NULL) && ((buffer->ppObject) != NULL) && (*(buffer->ppObject) != NULL) && (retPtr != NULL)) + { + /* Get back the real pointer. */ + realPtr = (const MSYS_DataObject_T_Private *)(*(buffer->ppObject)); + + /* Check for a valid pointer. */ + if ((realPtr->data != NULL) && (realPtr->length > 0) && (realPtr->capacity > 0)) + { + /* Copy the internal string. */ + retFromCall = DataProcess_Copy_C_String(realPtr->data, realPtr->length, &tempCopy); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (tempCopy != NULL)) + { + /* Copy the new pointer to retPtr. */ + (*retPtr) = tempCopy; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not copy the string. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* No buffer is allocated in the private structure. */ + ret = COMMON_ERROR_NO_DATA; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Get_Byte(const MSYS_DataObject_T * buffer, char * retPtr, const size_t offset) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + const MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((buffer != NULL) && ((buffer->ppObject) != NULL) && (*(buffer->ppObject) != NULL) && (retPtr != NULL)) + { + /* Get back the real pointer. */ + realPtr = (const MSYS_DataObject_T_Private *)(*(buffer->ppObject)); + + /* Check for data object consistancy. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(realPtr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check for data. */ + if ((realPtr->data != NULL) && (realPtr->capacity > 0)) + { + /* Check for valid offset. */ + if (offset < realPtr->capacity) + { + /* Return the byte. */ + (*retPtr) = realPtr->data[offset]; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid offset. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* No data to return. */ + ret = COMMON_ERROR_NO_DATA; + } + } + else + { + /* Given data object is inconsistant. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Set_Byte(MSYS_DataObject_T * buffer, const char byte, const size_t offset) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((buffer != NULL) && ((buffer->ppObject) != NULL) && (*(buffer->ppObject) != NULL)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(buffer->ppObject)); + + /* Check for data object consistancy. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(realPtr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check for data. */ + if ((realPtr->data != NULL) && (realPtr->capacity > 0)) + { + /* Check for valid offset. */ + if (offset < realPtr->capacity) + { + /* Set the byte. */ + realPtr->data[offset] = byte; + + /* Check to see if we need to update the length. */ + if (realPtr->length < offset) + { + /* Update the length to include the byte we just set. */ + realPtr->length = offset; + } + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid offset. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* No data to return. */ + ret = COMMON_ERROR_NO_DATA; + } + } + else + { + /* Given data object is inconsistant. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +void MSYS_Destroy_DataObject_Data_Copy(char ** obj) +{ + /* Check for valid pointer. */ + if (obj != NULL) + { + /* Deallocate the copy. */ + DataProcess_Deallocate_CString(obj); + } + + /* Exit function. */ + return; +} + +void MSYS_Clear_DataObject(MSYS_DataObject_T * obj) +{ + /* Init vars. */ + MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((obj != NULL) && ((obj->ppObject) != NULL) && (*(obj->ppObject) != NULL)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Call the private function. */ + MSYS_Clear_DataObject_Private(realPtr); + } + + /* Exit function. */ + return; +} + +void MSYS_Reset_DataObject(MSYS_DataObject_T * obj) +{ + /* Init vars. */ + MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((obj != NULL) && ((obj->ppObject) != NULL) && (*(obj->ppObject) != NULL)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Call the private function. */ + MSYS_Reset_DataObject_Private(realPtr); + } + + /* Exit function. */ + return; +} + +int MSYS_DataObject_Create_From_Existing_DataObject(const MSYS_DataObject_T * source, MSYS_DataObject_T ** retObj) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + MSYS_DataObject_T * newObj = NULL; /* The object we will create. */ + + /* Check vars. */ + /* Yes, we are explictly checking that retObj does not hold the same memory address as source, + or the source pointer. + */ + if ((source != NULL) && (retObj != NULL) && (((void *)retObj) != ((void *)(source))) && + (source->ppObject != NULL) && (*(source->ppObject) != NULL) && + (((void*)(retObj)) != *(source->ppObject))) + { + /* Create the new object. */ + retFromCall = MSYS_Create_DataObject(&newObj); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (newObj != NULL) && (newObj->ppObject != NULL) && (*(newObj->ppObject) != NULL)) + { + /* Deep copy the source structure. */ + retFromCall = MSYS_Deep_Copy_DataObject(source, newObj); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Copy newObj pointer to retObj. */ + (*retObj) = newObj; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Check error code. */ + ret = ((retFromCall == COMMON_ERROR_DATA_CORRUPTION) ? (COMMON_ERROR_DATA_CORRUPTION) : + (COMMON_ERROR_MEMORY_ERROR)); + + /* Deallocate the newObj. */ + MSYS_Destroy_DataObject(&newObj); + } + } + else + { + /* Could not create new DataObject. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Shallow_Copy_DataObject(MSYS_DataObject_T * source, MSYS_DataObject_T * dest) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + MSYS_DataObject_T_Private * realPtr = NULL; + + /* Check args. */ + if ((source != NULL) && (dest != NULL) && (source != dest) && (source->ppObject != NULL) && (*(source->ppObject) != NULL) && + (dest->ppObject != NULL) && (source->ppObject != dest->ppObject) && (*(source->ppObject) != *(dest->ppObject))) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(source->ppObject)); + + /* Check for max refcount. */ + if ((SIZE_MAX - realPtr->refcount) > 0) + { + /* Deallocate the dest private structure if needed. */ + if (*(dest->ppObject) != NULL) + { + MSYS_Destroy_DataObject_Private(((MSYS_DataObject_T_Private **)(dest->ppObject))); + } + + /* Copy the source pointer to the dest pointer. */ + *(dest->ppObject) = *(source->ppObject); + + /* Increment the refcount. */ + realPtr->refcount++; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* There are too many references to this object. Cannot create any more. */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_Deep_Copy_DataObject(const MSYS_DataObject_T * source, MSYS_DataObject_T * dest) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + char * copyBuffer = NULL; /* The copied data buffer. */ + const MSYS_DataObject_T_Private * srcPrivStr = NULL; /* The internal structure of the source data object. */ + MSYS_DataObject_T_Private * destPrivStr = NULL; /* The internal structure of the dest data object. */ + + /* Check args. */ + if ((source != NULL) && (dest != NULL) && (source != dest) && (source->ppObject != NULL) && (*(source->ppObject) != NULL) && + (dest->ppObject != NULL) && (*(dest->ppObject) != NULL) && (*(source->ppObject) != *(dest->ppObject))) + { + /* Get the internal pointers. */ + srcPrivStr = (const MSYS_DataObject_T_Private *)(*(source->ppObject)); + destPrivStr = (MSYS_DataObject_T_Private *)(*(dest->ppObject)); + + /* Check for valid data pointer in the source object. */ + if (srcPrivStr->data != NULL) + { + /* Check for valid capacity and length. */ + if ((srcPrivStr->length >= 0) && (srcPrivStr->capacity >= srcPrivStr->length)) + { + /* Clear the internal structure for the dest object. */ + MSYS_Clear_DataObject_Private(destPrivStr); + + /* Deep copy the values from the source object to the dest object. */ + retFromCall = DataProcess_Copy_C_String(srcPrivStr->data, srcPrivStr->capacity, ©Buffer); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (copyBuffer != NULL)) + { + /* Copy the new pointer into the dest object. */ + destPrivStr->data = copyBuffer; + + /* Copy remaining values. */ + destPrivStr->capacity = srcPrivStr->capacity; + destPrivStr->length = srcPrivStr->length; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not copy source buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Check and see if the source object's length and capacity are both equal to zero. */ + if ((srcPrivStr->capacity == 0) && (srcPrivStr->length == 0)) + { + /* Clear the internal structure for the dest object. */ + MSYS_Clear_DataObject_Private(destPrivStr); + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Set_Data_From_CString(MSYS_DataObject_T * obj, const char * data, const size_t dataLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + MSYS_DataObject_T_Private * realPtr = NULL; /* Pointer to private data structure. */ + char * copyStr = NULL; /* Pointer to the copied data. */ + + /* Check args. */ + if ((obj != NULL) && (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && + (data != NULL) && (dataLength > 0)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Copy the string data. */ + retFromCall = DataProcess_Copy_C_String(data, dataLength, ©Str); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (copyStr != NULL)) + { + /* Clear the internal structure. */ + MSYS_Clear_DataObject_Private(realPtr); + + /* Copy dataLength to the private structure's length and capacity vars. */ + realPtr->length = dataLength; + realPtr->capacity = dataLength; + + /* Copy copyStr's pointer to the private structure. */ + realPtr->data = copyStr; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not copy the data. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Set_Data_From_Char(MSYS_DataObject_T * obj, const char data) +{ + /* Call the real function. */ + return MSYS_DataObject_Set_Data_From_CString(obj, &data, sizeof(char)); +} + +int MSYS_DataObject_Append_CString(MSYS_DataObject_T * obj, const char * data, const size_t dataLength) +{ + /* Call insert function. */ + return (MSYS_DataObject_Insert_CString(obj, SIZE_MAX, data, dataLength)); +} + +int MSYS_DataObject_Append_Char(MSYS_DataObject_T * obj, const char data) +{ + /* Call insert function. */ + return (MSYS_DataObject_Insert_CString(obj, SIZE_MAX, &data, sizeof(char))); +} + +int MSYS_DataObject_Append_DataObject(MSYS_DataObject_T * obj, const MSYS_DataObject_T * src) +{ + /* Call insert function. */ + return (MSYS_DataObject_Insert_From_DataObject(obj, SIZE_MAX, src)); +} + +int MSYS_DataObject_Substr(const MSYS_DataObject_T * obj, const size_t offset, const size_t endpoint, char ** substr) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + const MSYS_DataObject_T_Private * srcPrivStr = NULL; /* The internal structure of the source data object. */ + char * newSubstr = NULL; /* The substring to be created. */ + size_t newSubstrLength = 0; /* Length of the new substring. */ + + /* Check args. */ + if ((obj != NULL) && (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && + (offset >= 0) && (offset < endpoint) && (substr != NULL)) + { + /* Get the internal pointers. */ + srcPrivStr = (const MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Check for valid data pointer in the source object. */ + if (srcPrivStr->data != NULL) + { + /* Check for valid length and capacity in the source object. */ + if ((srcPrivStr->length >= 0) && (srcPrivStr->capacity >= srcPrivStr->length)) + { + /* Check for valid offset and endpoint range. */ + if (srcPrivStr->length > endpoint) + { + /* Check for safe length. */ + if ((SIZE_MAX - endpoint) > offset) + { + /* Recalculate the substring length. */ + newSubstrLength = (endpoint - offset); + + /* Allocate memory for the new substring. */ + retFromCall = DataProcess_Reallocate_C_String(&newSubstr, 0, newSubstrLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (newSubstr != NULL)) + { + /* Copy the data. */ + memcpy(newSubstr, (srcPrivStr->data + offset), newSubstrLength); + + /* Copy the pointer to the substr arg. */ + (*substr) = newSubstr; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for the new substring. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Cannot represent that value. */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + else + { + /* Invalid substring range. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Check for valid object. */ + if ((srcPrivStr->length == 0) && (offset == 0)) + { + /* No data to create substring from. */ + ret = COMMON_ERROR_NO_DATA; + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Buffer_Copy(MSYS_DataObject_T * dest, const MSYS_DataObject_T * source, const size_t copy_offset, const size_t copy_length) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + const MSYS_DataObject_T_Private * srcPrivStr = NULL; /* The internal structure of the source data object. */ + MSYS_DataObject_T_Private * destPrivStr = NULL; /* The internal structure of the dest data object. */ + + /* Check args. */ + if ((source != NULL) && (dest != NULL) && (source != dest) && (source->ppObject != NULL) && (*(source->ppObject) != NULL) && + (dest->ppObject != NULL) && (*(dest->ppObject) != NULL) && (*(source->ppObject) != *(dest->ppObject)) && + (copy_offset >= 0) && (copy_length > 0)) + { + /* Get the internal pointers. */ + srcPrivStr = (const MSYS_DataObject_T_Private *)(*(source->ppObject)); + destPrivStr = (MSYS_DataObject_T_Private *)(*(dest->ppObject)); + + /* Check for valid data pointer in the source object. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(srcPrivStr); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Check for data in the source structure. */ + if ((srcPrivStr->data != NULL) && (srcPrivStr->length > 0)) + { + /* Check for valid dest object. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(destPrivStr); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Check and see if we can get the correct range. */ + if ((SIZE_MAX - (SIZE_MAX - copy_offset)) > copy_length) + { + /* Check and see if the given offset and length is within the source buffer. */ + if ((copy_offset < srcPrivStr->length) && ((copy_offset + copy_length) < srcPrivStr->length)) + { + /* Check and see if the requested length of data will fit in the given dest buffer. */ + if ((destPrivStr->data != NULL) && (copy_length < destPrivStr->capacity)) + { + /* Reset the internal buffer for dest. */ + MSYS_Reset_DataObject_Private(destPrivStr); + + /* Copy the data. */ + memcpy(destPrivStr->data, (srcPrivStr->data + copy_offset), copy_length); + + /* Set the length of the dest object. */ + destPrivStr->length = copy_length; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Not enough space in the dest buffer to copy the data into it. */ + ret = COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL; + } + } + else + { + /* Invalid source data range. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* OK, can't represent this value. */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + else + { + /* OK, the dest object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* No data in source object to create substring from. */ + ret = COMMON_ERROR_NO_DATA; + } + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Compare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + const MSYS_DataObject_T_Private * obj1PrivStr = NULL; /* The internal structure of the obj1 data object. */ + const MSYS_DataObject_T_Private * obj2PrivStr = NULL; /* The internal structure of the obj2 data object. */ + + /* Check args. */ + if (obj1 != obj2) + { + /* Check and see if we got a NULL.... */ + if ((obj1 != NULL) && (obj2 != NULL)) + { + /* Check internal pointer to pointers. */ + if (obj1->ppObject != obj2->ppObject) + { + /* Check for a NULL pointer to pointer object. */ + if ((obj1->ppObject != NULL) && (obj2->ppObject != NULL)) + { + /* Check the private object pointers. */ + if (*(obj1->ppObject) != *(obj2->ppObject)) + { + /* Check and see if a private object is NULL. */ + if ((*(obj1->ppObject) != NULL) && (*(obj2->ppObject) != NULL)) + { + /* Get the internal pointers. */ + obj1PrivStr = ((const MSYS_DataObject_T_Private *)(*(obj1->ppObject))); + obj2PrivStr = ((const MSYS_DataObject_T_Private *)(*(obj2->ppObject))); + + /* Check the capacity. */ + if (obj1PrivStr->capacity == obj2PrivStr->capacity) + { + /* Check the length. */ + if (obj1PrivStr->length == obj2PrivStr->length) + { + /* Check the buffer memory addresses. */ + if (obj1PrivStr->data == obj2PrivStr->data) + { + /* Objects are identical. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + else + { + /* Objects have different buffers. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects have different lengths. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects have different capacities. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Comparing a valid private object against a NULL object. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects have the same private object memory address. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + } + else + { + /* Comparing a valid pointer to pointer object against a NULL object. + + This is the result of user manipulation of the public structures, + the library will only free the public structure's pointer to pointer + variable when that public structure itself is deallocated. + */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects have the same pointer to pointer memory address. + + This is the result of user manipulation of the public structures, + the library will never create two different objects with the + same pointer to pointer variable. (As it would break + the Shallow Copy support and cause a double free.) + */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + } + else + { + /* Comparing a valid public object against a NULL object. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects have the same memory address. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_NCompare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2) +{ + /* Call the compare function and inverse the result. */ + return (!(MSYS_DataObject_Compare(obj1, obj2))); +} + +int MSYS_DataObject_Data_Compare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + const MSYS_DataObject_T_Private * obj1PrivStr = NULL; /* The internal structure of the obj1 data object. */ + const MSYS_DataObject_T_Private * obj2PrivStr = NULL; /* The internal structure of the obj2 data object. */ + + /* Check args. */ + if (obj1 != obj2) + { + /* Check and see if we got a NULL.... */ + if ((obj1 != NULL) && (obj2 != NULL)) + { + /* Check internal pointer to pointers. */ + if (obj1->ppObject != obj2->ppObject) + { + /* Check for a NULL pointer to pointer object. */ + if ((obj1->ppObject != NULL) && (obj2->ppObject != NULL)) + { + /* Check the private object pointers. */ + if (*(obj1->ppObject) != *(obj2->ppObject)) + { + /* Check and see if a private object is NULL. */ + if ((*(obj1->ppObject) != NULL) && (*(obj2->ppObject) != NULL)) + { + /* Get the internal pointers. */ + obj1PrivStr = ((const MSYS_DataObject_T_Private *)(*(obj1->ppObject))); + obj2PrivStr = ((const MSYS_DataObject_T_Private *)(*(obj2->ppObject))); + + /* Check the length. */ + if (obj1PrivStr->length == obj2PrivStr->length) + { + /* Check the buffer memory addresses. */ + if (obj1PrivStr->data != obj2PrivStr->data) + { + /* Check for NULL data buffers. */ + if ((obj1PrivStr->data != NULL) && (obj2PrivStr->data != NULL)) + { + /* Check the data. */ + if (memcmp(obj1PrivStr->data, obj2PrivStr->data, obj1PrivStr->length) == 0) + { + /* Objects have the same data. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + else + { + /* Objects have different data. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Comparing a valid private buffer against a NULL buffer. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects are identical. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + } + else + { + /* Objects have different lengths. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Comparing a valid private object against a NULL object. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects have the same private object memory address. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + } + else + { + /* Comparing a valid pointer to pointer object against a NULL object. + + This is the result of user manipulation of the public structures, + the library will only free the public structure's pointer to pointer + variable when that public structure itself is deallocated. + */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects have the same pointer to pointer memory address. + + This is the result of user manipulation of the public structures, + the library will never create two different objects with the + same pointer to pointer variable. (As it would break + the Shallow Copy support and cause a double free.) + */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + } + else + { + /* Comparing a valid public object against a NULL object. */ + ret = COMMON_ERROR_COMPARISON_FAILED; + } + } + else + { + /* Objects have the same memory address. */ + ret = COMMON_ERROR_COMPARISON_PASSED; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Data_NCompare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2) +{ + /* Call the data compare function and inverse the result. */ + return (!(MSYS_DataObject_Data_Compare(obj1, obj2))); +} + +int MSYS_DataObject_Reserve_Memory(MSYS_DataObject_T * obj, const size_t memoryLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + MSYS_DataObject_T_Private * realPtr = NULL; /* Pointer to private data structure. */ + char * expandedBuffer = NULL; /* Pointer to the expanded buffer. */ + + /* Check args. */ + if ((obj != NULL) && (obj->ppObject != NULL) && (*(obj->ppObject) != NULL)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Check the private data structure. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(realPtr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check the memoryLength, and see if it's less than the current capacity, or equal to zero. */ + if ((memoryLength > 0) && (memoryLength > realPtr->capacity)) + { + /* OK, we need to allocate a new buffer. */ + retFromCall = DataProcess_Reallocate_C_String(&expandedBuffer, 0, memoryLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (expandedBuffer != NULL)) + { + /* Check for old data. */ + if ((realPtr->data != NULL) && (realPtr->capacity > 0)) + { + /* Copy the original data. */ + memcpy(expandedBuffer, realPtr->data, realPtr->capacity); + + /* Deallocate the original buffer. */ + DataProcess_Deallocate_CString(&(realPtr->data)); + realPtr->capacity = 0; + } + + /* Copy the expandedBuffer pointer to the object. */ + realPtr->data = expandedBuffer; + realPtr->capacity = memoryLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not copy the buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* Invalid memoryLength. (This function reserves absolute amounts memory, we won't deallocate it.) */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + } + else + { + /* The data object is corrupt. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid argument. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Insert_CString_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + MSYS_DataObject_T_Private * realPtr = NULL; /* Pointer to private data structure. */ + size_t newLength = 0; /* The length of the new buffer. */ + size_t realOffset = 0; /* The offset used in the memcpy() calls. */ + + /* Check args. */ + if ((obj != NULL) && (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && (data != NULL) && (dataLength > 0)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Check for data object consistancy. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(realPtr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* If the given offset is bigger than the content length of the buffer, use the end of the buffer for the offset. */ + realOffset = ((offset < realPtr->length) ? (offset) : (realPtr->length)); + + /* Check for UINT Overflow. + This function keeps the original data, and simply inserts the given data at the given offset. + + So the total amount of data we will wind up with is: (realPtr->length + dataLength). + */ + if (((SIZE_MAX - realOffset) >= dataLength) && ((SIZE_MAX - realPtr->length) >= dataLength)) + { + /* Copy the length. */ + newLength = realPtr->length; + + /* Call MSYS_DataObject_Insert_CString_No_Allocation_Private(). (Will fail if the buffer is not big enough.) */ + retFromCall = MSYS_DataObject_Insert_CString_No_Allocation_Private(realPtr->data, + &newLength, /* This is altered by this function call. */ + realPtr->capacity, + data, + dataLength, + realOffset); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (newLength == (realPtr->length + dataLength))) + { + /* Update length. */ + realPtr->length = (realPtr->length + dataLength); + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Set error code. */ + ret = retFromCall; + } + } + else + { + /* Cannot add the given data, total data size would be bigger than SIZE_MAX. */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + else + { + /* Given data object is inconsistant. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Insert_Char_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const char data) +{ + /* Call real function. */ + return (MSYS_DataObject_Insert_CString_No_Allocaton(obj, offset, &data, sizeof(char))); +} + +int MSYS_DataObject_Insert_From_DataObject_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + const MSYS_DataObject_T_Private * srcPrivStr = NULL; /* The internal structure of the source data object. */ + + /* Check args. */ + if ((src != NULL) && (obj != NULL) && (src != obj) && (src->ppObject != NULL) && (*(src->ppObject) != NULL) && + (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && (*(src->ppObject) != *(obj->ppObject))) + { + /* Get the internal pointer for the source object. */ + srcPrivStr = (const MSYS_DataObject_T_Private *)(*(src->ppObject)); + + /* Check for valid source object. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(srcPrivStr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check for valid data buffer and length. */ + if ((srcPrivStr->data != NULL) && (srcPrivStr->length > 0)) + { + /* Call the insert C-String function. */ + retFromCall = MSYS_DataObject_Insert_CString_No_Allocaton(obj, offset, srcPrivStr->data, srcPrivStr->length); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Check error code. */ + ret = ((retFromCall != COMMON_ERROR_INVALID_ARGUMENT) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* OK, the source object is empty. */ + ret = COMMON_ERROR_NO_DATA; + } + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Insert_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + MSYS_DataObject_T_Private * realPtr = NULL; /* Pointer to private data structure. */ + char * expandedBuffer = NULL; /* Pointer to the expanded buffer. */ + size_t newLength = 0; /* The length of the new buffer. */ + size_t realOffset = 0; /* The offset used in the memcpy() calls. */ + + /* Check args. */ + if ((obj != NULL) && (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && (data != NULL) && (dataLength > 0)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Check for data object consistancy. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(realPtr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* If the given offset is bigger than the content length of the buffer, use the end of the buffer for the offset. */ + realOffset = ((offset < realPtr->length) ? (offset) : (realPtr->length)); + + /* Check for UINT Overflow. + This function keeps the original data, and simply inserts the given data at the given offset. + + So the total amount of data we will wind up with is: (realPtr->length + dataLength). + */ + if (((SIZE_MAX - realOffset) >= dataLength) && ((SIZE_MAX - realPtr->length) >= dataLength)) + { + /* Determine if we need to reallocate memory. + + Note: If MSYS_Check_DataObject_Consistency_Private() passed, the capacity value will be at least + as big as the length value. So this subtraction is safe. (As long as the realOffset check above runs.) + */ + if ((realPtr->capacity - realPtr->length) < dataLength) + { + /* Memory allocation required, Recalculate the needed capacity. + + New allocated memory length is (realPtr->length + dataLength). + */ + retFromCall = DataProcess_Reallocate_C_String(&expandedBuffer, 0, (realPtr->length + dataLength)); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (expandedBuffer != NULL)) + { + /* Copy the data. */ + memcpy(expandedBuffer, realPtr->data, realPtr->length); + } + else + { + /* Could not copy the buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + + /* Check if safe to continue. + (If the memory allocation succeeds or no memory allocation was required, ret will be set to COMMON_ERROR_UNKNOWN_ERROR.) + */ + if (ret == COMMON_ERROR_UNKNOWN_ERROR) + { + /* Copy the length. */ + newLength = realPtr->length; + + /* Call MSYS_DataObject_Insert_CString_No_Allocation_Private(). */ + retFromCall = MSYS_DataObject_Insert_CString_No_Allocation_Private(((expandedBuffer != NULL) ? (expandedBuffer) : (realPtr->data)), + &newLength, + (realPtr->length + dataLength), + data, + dataLength, + realOffset); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (newLength == (realPtr->length + dataLength))) + { + /* Check and see if we allocated a new buffer. */ + if (expandedBuffer != NULL) + { + /* Deallocate the original buffer. */ + DataProcess_Deallocate_CString((&(realPtr->data))); + + /* Copy in the new pointer. */ + realPtr->data = expandedBuffer; + + /* Update capacity. */ + realPtr->capacity = (realPtr->length + dataLength); + } + + /* Update length. */ + realPtr->length = (realPtr->length + dataLength); + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Check for buffer too small error code. (If that code gets sent back we are being messed with.) */ + ret = ((retFromCall != COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + + /* Deallocate the expandedBuffer. (If needed.) */ + if (expandedBuffer != NULL) + { + DataProcess_Deallocate_CString(&expandedBuffer); + } + } + }/* End of "Check if safe to continue." No else statement needed. */ + } + else + { + /* Cannot add the given data, total data size would be bigger than SIZE_MAX. */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + else + { + /* Given data object is inconsistant. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Insert_Char(MSYS_DataObject_T * obj, const size_t offset, const char data) +{ + /* Call real function. */ + return (MSYS_DataObject_Insert_CString(obj, offset, &data, sizeof(char))); +} + +int MSYS_DataObject_Insert_From_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + const MSYS_DataObject_T_Private * srcPrivStr = NULL; /* The internal structure of the source data object. */ + + /* Check args. */ + if ((src != NULL) && (obj != NULL) && (src != obj) && (src->ppObject != NULL) && (*(src->ppObject) != NULL) && + (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && (*(src->ppObject) != *(obj->ppObject))) + { + /* Get the internal pointer for the source object. */ + srcPrivStr = (const MSYS_DataObject_T_Private *)(*(src->ppObject)); + + /* Check for valid source object. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(srcPrivStr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check for valid data buffer and length. */ + if ((srcPrivStr->data != NULL) && (srcPrivStr->length > 0)) + { + /* Call the insert C-String function. */ + retFromCall = MSYS_DataObject_Insert_CString(obj, offset, srcPrivStr->data, srcPrivStr->length); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Check error code. */ + ret = ((retFromCall != COMMON_ERROR_INVALID_ARGUMENT) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* OK, the source object is empty. */ + ret = COMMON_ERROR_NO_DATA; + } + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Replace_With_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + MSYS_DataObject_T_Private * realPtr = NULL; /* Pointer to private data structure. */ + + /* Check args. */ + if ((obj != NULL) && (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && (data != NULL) && (dataLength > 0)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Check for data object consistancy. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(realPtr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check and see if the offset is within the existing buffer. */ + if ((offset == 0) || (offset < realPtr->length)) + { + /* UINT Overflow check. */ + if ((SIZE_MAX - offset) >= dataLength) + { + /* Check and see if the given data will fit in the remaining capacity of the buffer. + + If MSYS_Check_DataObject_Consistency_Private() passed, the capacity of the buffer will be + at least it's length, so subtracting offset from it is safe. (Assuming the check against length + passed above.) + + Replace "eats" the first byte of the dataLength. So we must subtract 1 from it here. + */ + if ((dataLength - 1) < (realPtr->capacity - offset)) + { + /* Copy the given data into the buffer at the given offset. */ + memcpy((realPtr->data + offset), data, dataLength); + + /* Update the private structure's length. (If needed.) */ + if ((offset + (dataLength - 1)) > realPtr->length) + { + /* Replace "eats" the first byte of the dataLength. So we must subtract 1 from it here. */ + realPtr->length = (offset + (dataLength - 1)); + } + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* The given data will not fit in the existing buffer. */ + ret = COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL; + } + } + else + { + /* OK, adding the given offset to the given dataLength would result in a value bigger than SIZE_MAX. + How did this happen if capacity is at least that size? + + In anycase, throw back the system limit exceeded error. + */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + else + { + /* The offset is outside of the buffer. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* Given data object is inconsistant. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Replace_With_Char(MSYS_DataObject_T * obj, const size_t offset, const char data) +{ + /* Call real function. */ + return (MSYS_DataObject_Replace_With_CString(obj, offset, &data, sizeof(char))); +} + +int MSYS_DataObject_Replace_With_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + const MSYS_DataObject_T_Private * srcPrivStr = NULL; /* The internal structure of the source data object. */ + + /* Check args. */ + if ((src != NULL) && (obj != NULL) && (src != obj) && (src->ppObject != NULL) && (*(src->ppObject) != NULL) && + (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && (*(src->ppObject) != *(obj->ppObject))) + { + /* Get the internal pointer for the source object. */ + srcPrivStr = (const MSYS_DataObject_T_Private *)(*(src->ppObject)); + + /* Check for valid source object. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(srcPrivStr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check for valid data buffer and length. */ + if ((srcPrivStr->data != NULL) && (srcPrivStr->length > 0)) + { + /* Call the replace with C-String function. */ + retFromCall = MSYS_DataObject_Replace_With_CString(obj, offset, srcPrivStr->data, srcPrivStr->length); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Check error code. */ + ret = ((retFromCall != COMMON_ERROR_INVALID_ARGUMENT) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* OK, the source object is empty. */ + ret = COMMON_ERROR_NO_DATA; + } + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Overwrite_With_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + MSYS_DataObject_T_Private * realPtr = NULL; /* Pointer to private data structure. */ + char * newData = NULL; /* Pointer to the reallocation buffer. */ + size_t newDataLength = 0; /* Length of the newData buffer. */ + + /* Check args. */ + if ((obj != NULL) && (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && (data != NULL) && (dataLength > 0)) + { + /* Get back the real pointer. */ + realPtr = (MSYS_DataObject_T_Private *)(*(obj->ppObject)); + + /* Check for data object consistancy. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(realPtr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check and see if the offset is within the existing buffer. */ + if ((offset == 0) || (offset < realPtr->length)) + { + /* UINT Overflow check. */ + if ((SIZE_MAX - offset) >= dataLength) + { + /* Check and see if the given data will fit in the remaining capacity of the buffer. + + If MSYS_Check_DataObject_Consistency_Private() passed, the capacity of the buffer will be + at least it's length, so subtracting offset from it is safe. (Assuming the check against length + passed above.) + + Overwrite "eats" the first byte of the dataLength. So we must subtract 1 from it here. + */ + if ((dataLength - 1) < (realPtr->capacity - offset)) + { + /* Copy the given data into the buffer at the given offset. */ + memcpy((realPtr->data + offset), data, dataLength); + + /* Update the private structure's length. (If needed.) */ + if ((offset + (dataLength - 1)) > realPtr->length) + { + /* Overwrite "eats" the first byte of the dataLength. So we must subtract 1 from it here. */ + realPtr->length = (offset + (dataLength - 1)); + } + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* SIZE_T overflow check. */ + if ((SIZE_MAX - (realPtr->capacity - offset)) >= dataLength) + { + /* The given data will not fit in the existing buffer, so we need to calculate the size of the needed buffer. + Overwrite "eats" the first byte of the dataLength. So we must subtract 1 from it here. + */ + newDataLength = ((realPtr->capacity - offset) + (dataLength - 1)); + + /* Allocate the new buffer. */ + retFromCall = DataProcess_Reallocate_C_String(&newData, 0, newDataLength); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Copy the original data up to the offset. */ + memcpy(newData, (realPtr->data), offset); + + /* Copy the given data into the buffer at the given offset. */ + memcpy((newData + offset), data, dataLength); + + /* Deallocate the old string. */ + DataProcess_Deallocate_CString(&(realPtr->data)); + + /* Copy the new pointer into the private structure. */ + realPtr->data = newData; + + /* Update the private structure's length and capacity. */ + realPtr->length = newDataLength; + realPtr->capacity = newDataLength; + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Could not allocate memory for new buffer. */ + ret = COMMON_ERROR_MEMORY_ERROR; + } + } + else + { + /* The length of the data to add with the given offset is too big for a size_t to represent. */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + } + else + { + /* OK, adding the given offset to the given dataLength would result in a value bigger than SIZE_MAX. + How did this happen if capacity is at least that size? + + In anycase, throw back the system limit exceeded error. + */ + ret = COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED; + } + } + else + { + /* The offset is outside of the buffer. */ + ret = COMMON_ERROR_RANGE_ERROR; + } + } + else + { + /* Given data object is inconsistant. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int MSYS_DataObject_Overwrite_With_Char(MSYS_DataObject_T * obj, const size_t offset, const char data) +{ + /* Call real function. */ + return (MSYS_DataObject_Overwrite_With_CString(obj, offset, &data, sizeof(char))); +} + +int MSYS_DataObject_Overwrite_With_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result of this function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of calls to other engine functions. */ + const MSYS_DataObject_T_Private * srcPrivStr = NULL; /* The internal structure of the source data object. */ + + /* Check args. */ + if ((src != NULL) && (obj != NULL) && (src != obj) && (src->ppObject != NULL) && (*(src->ppObject) != NULL) && + (obj->ppObject != NULL) && (*(obj->ppObject) != NULL) && (*(src->ppObject) != *(obj->ppObject))) + { + /* Get the internal pointer for the source object. */ + srcPrivStr = (const MSYS_DataObject_T_Private *)(*(src->ppObject)); + + /* Check for valid source object. */ + retFromCall = MSYS_Check_DataObject_Consistency_Private(srcPrivStr); + if (retFromCall == COMMON_ERROR_COMPARISON_PASSED) + { + /* Check for valid data buffer and length. */ + if ((srcPrivStr->data != NULL) && (srcPrivStr->length > 0)) + { + /* Call the overwrite with C-String function. */ + retFromCall = MSYS_DataObject_Overwrite_With_CString(obj, offset, srcPrivStr->data, srcPrivStr->length); + if (retFromCall == COMMON_ERROR_SUCCESS) + { + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Check error code. */ + ret = ((retFromCall != COMMON_ERROR_INVALID_ARGUMENT) ? (retFromCall) : (COMMON_ERROR_INTERNAL_ERROR)); + } + } + else + { + /* OK, the source object is empty. */ + ret = COMMON_ERROR_NO_DATA; + } + } + else + { + /* OK, the source object is invalid. */ + ret = COMMON_ERROR_DATA_CORRUPTION; + } + } + else + { + /* Invalid args. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} diff --git a/src/Core/Src/Data_Object/Data_Object.h b/src/Core/Src/Data_Object/Data_Object.h new file mode 100644 index 0000000..65f57b2 --- /dev/null +++ b/src/Core/Src/Data_Object/Data_Object.h @@ -0,0 +1,743 @@ +/* + Multiverse Engine Project Core Data_Object.h 02/4/2016 + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef MSYS_DATAOBJECT_H +#define MSYS_DATAOBJECT_H + +/* Check for C++ compiler. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Internal include for library symbol export. */ +#include "../../../DLL_PORT.h" + +/*! + typedef struct MSYS_DataObject {} MSYS_DataObject_T + + This structure is a basic RAW data storage structure. + + It supports preallocating memory in addition to reallocating + memory when needed to store data. It also supports Shallow Copies + by using reference counts, and Deep Copies where needed. + + It is NOT synchronized, and therefore NOT thread safe. If it is to be + used in a threaded environment, care should be taken to ensure that no + data race conditions occur, either by wraping it in another thread-safe + container (best option), or by ensuring it is NOT shared between threads. + */ +MSYS_DLL_EXPORT typedef struct MSYS_DataObject { + void ** ppObject; /* The object itself. */ +} MSYS_DataObject_T; + +/*! + int MSYS_Create_DataObject(MSYS_DataObject_T ** buffer) + + Creates a MSYS_DataObject_T object and sets the pointer pointed to by buffer to the + newly created object. + + The created data structure should be deallocated with MSYS_Destroy_DataObject(). + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer to pointer argument is invalid. + Returns COMMON_ERROR_MEMORY_ERROR if an error occurs while allocating memory for the structure. + */ +MSYS_DLL_EXPORT int MSYS_Create_DataObject(MSYS_DataObject_T ** buffer); + +/*! + void MSYS_Destroy_DataObject(MSYS_DataObject_T ** buffer) + + Destroys (Deallocates) the given MSYS_DataObject_T object, created by + MSYS_Create_DataObject() or MSYS_DataObject_Create_From_Existing_DataObject(). + + This function has no return. + */ +MSYS_DLL_EXPORT void MSYS_Destroy_DataObject(MSYS_DataObject_T ** buffer); + +/*! + int MSYS_Check_DataObject_Consistency(const MSYS_DataObject_T * obj) + + Checks the given MSYS_DataObject_T object for consistency. + + Returns COMMON_ERROR_COMPARISON_PASSED if the given object is consistant. + Returns COMMON_ERROR_COMPARISON_FAILED if the given object is NOT consistant. + Returns COMMON_ERROR_INVALID_ARGUMENT if the given pointer is invalid. + */ +MSYS_DLL_EXPORT int MSYS_Check_DataObject_Consistency(const MSYS_DataObject_T * obj); + +/*! + int MSYS_DataObject_Get_Capacity(const MSYS_DataObject_T * obj, size_t * retPtr) + + Returns the currently allocated buffer's total memory capacity, + by copying the value to retPtr. + + WARNING: The returned value is only valid so long as no other calls are made with + the given object that will change the allocated buffer. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Get_Capacity(const MSYS_DataObject_T * obj, size_t * retPtr); + +/*! + int MSYS_DataObject_Get_Length(const MSYS_DataObject_T * obj, size_t * retPtr) + + Returns the currently allocated buffer's content length, (i.e. the length of the actual data + in the given object), by copying the value to retPtr. + + WARNING: The returned value is only valid so long as no other calls are made with + the given object that will change the allocated buffer OR it's content. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Get_Length(const MSYS_DataObject_T * obj, size_t * retPtr); + +/*! + int MSYS_DataObject_Get_Pointer(const MSYS_DataObject_T * buffer, const char ** retPtr) + + Returns the internal pointer that points to the currently allocated data buffer, + by copying the internal pointer to retPtr. + + WARNING: The returned pointer should not be modified or deallocated. Also this pointer + should only be accessed when NO other thread / process will access the given + MSYS_Data_Object_T structure. FAILURE TO ABIDE BY THIS WARNING MAY CAUSE + UNDEFINED BEHAVIOR. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Get_Pointer(const MSYS_DataObject_T * buffer, const char ** retPtr); + +/*! + int MSYS_DataObject_Get_Data_Copy(const MSYS_DataObject_T * buffer, char ** retPtr) + + Copies the given object's data to a C-String, and copies the resulting pointer to retPtr. + + The returned pointer should be deallocated with MSYS_Destroy_DataObject_Data_Copy() when it is + no longer needed. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + Returns COMMON_ERROR_MEMORY_ERROR if memory could not be allocated for the copied data. + Returns COMMON_ERROR_NO_DATA if the given MSYS_DataObject_T has no data in it. (I.e. Length is zero.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Get_Data_Copy(const MSYS_DataObject_T * buffer, char ** retPtr); + +/*! + int MSYS_DataObject_Get_Byte(const MSYS_DataObject_T * buffer, char * retPtr, const size_t offset) + + Returns the value of the byte in the given MSYS_DataObject_T's allocated memory buffer at the given offset. + + Note: This function DOES NOT consider the current length (as returned by MSYS_DataObject_Get_Length()) + when checking the given offset. This is because it allows accessing any value in the allocated memory + buffer for the object. + + Returns COMMON_ERROR_SUCCESS if successful. The desired byte will be stored in retPtr. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + Returns COMMON_ERROR_NO_DATA if the given MSYS_DataObject_T lacks an allocated buffer. (I.e. Capacity is zero.) + Returns COMMON_ERROR_DATA_CORRUPTION if the source MSYS_DataObject_T object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + Returns COMMON_ERROR_RANGE_ERROR if the given offset argument is outside the given object's allocated memory buffer. (I.e. An offset that + would point to data from before or after the end of the given data object's allocated memory buffer.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Get_Byte(const MSYS_DataObject_T * buffer, char * retPtr, const size_t offset); + +/*! + int MSYS_DataObject_Set_Byte(MSYS_DataObject_T * buffer, const char byte, const size_t offset) + + Sets the value of the byte in the given MSYS_DataObject_T's allocated memory buffer at the given offset. + + Note 1: This function DOES NOT consider the current length (as returned by MSYS_DataObject_Get_Length()) + when checking the given offset. This is because it allows accessing any value in the allocated memory + buffer for the object. + + Note 2: This function DOES NOT allocate memory. If the given MSYS_DataObject_T lacks an allocated + memory buffer when this function is called, or if the given offset is outside of the allocated + memory buffer, an error will be returned. See below. + + Note 3: This function will set the length of the given MSYS_DataObject_T to include the set byte + if the given offset is greater than the MSYS_DataObject_T's length at the time of the call. + (I.e. length = ((length < offset) ? (offset) : (length))) + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + Returns COMMON_ERROR_NO_DATA if the given MSYS_DataObject_T lacks an allocated buffer. (I.e. Capacity is zero.) + Returns COMMON_ERROR_DATA_CORRUPTION if the source MSYS_DataObject_T object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + Returns COMMON_ERROR_RANGE_ERROR if the given offset argument is outside the given object's allocated memory buffer. (I.e. An offset that + would point to data from before or after the end of the given data object's allocated memory buffer.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Set_Byte(MSYS_DataObject_T * buffer, const char byte, const size_t offset); + +/*! + void MSYS_Destroy_DataObject_Data_Copy(char ** obj) + + Destroys (Deallocates) the copied object data, created by + MSYS_DataObject_Get_Data_Copy(). + + WARNING: DO NOT ATTEMPT to deallocate MSYS_DataObject_T objects with this function. + Use MSYS_Destroy_DataObject() instead. Failure to do so, will result in + UNDEFINED BEHAVIOR. + + This function has no return. + */ +MSYS_DLL_EXPORT void MSYS_Destroy_DataObject_Data_Copy(char ** obj); + +/*! + void MSYS_Clear_DataObject(MSYS_DataObject_T * obj) + + Clears the given MSYS_DataObject_T object to it's default state. + (I.e. No allocated memory buffer, length and capacity are both set to zero.) + + If the given pointer is invalid this function will silently fail. + + This function has no return. + */ +MSYS_DLL_EXPORT void MSYS_Clear_DataObject(MSYS_DataObject_T * obj); + +/*! + void MSYS_Reset_DataObject(MSYS_DataObject_T * obj) + + This function erases all data in the given MSYS_DataObject_T object's buffer, + (Sets all bytes to NULL) and sets it's length to zero (0). The capacity is left unchanged. + + If the given pointer / object is invalid, or there is no allocated buffer, + this function will silently fail. + + This function has no return. + */ +MSYS_DLL_EXPORT void MSYS_Reset_DataObject(MSYS_DataObject_T * obj); + +/*! + int MSYS_DataObject_Create_From_Existing_DataObject(const MSYS_DataObject_T * source, MSYS_DataObject_T ** retObj) + + Creates a new MSYS_DataObject_T structure from a pre-existing MSYS_DataObject_T structure, + by creating a new structure, deep copying the original data, and copying the new structure's pointer + to retObj. + + The created data structure should be deallocated with MSYS_Destroy_DataObject(). + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + Returns COMMON_ERROR_DATA_CORRUPTION if the source MSYS_DataObject_T object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Create_From_Existing_DataObject(const MSYS_DataObject_T * source, MSYS_DataObject_T ** retObj); + +/*! + int MSYS_Shallow_Copy_DataObject(MSYS_DataObject_T * source, MSYS_DataObject_T * dest) + + Copies the data values from the source MSYS_DataObject_T object to the dest MSYS_DataObject_T object, without + performing memory allocations. The source object has it's reference count incremented upon success. + + Note: Only the values are copied, the actual data is not. If either the source object or the dest object gets + modified, the data for the other object will be as well. If either the source object or the dest object + gets deallocated, the data will only be deallocated if the object being deallocated is the last reference + to it. + + The maximum number of references to an object are defined by the system's SIZE_MAX. An object cannot have + more references than that amount. Attempts to create more than the maximum allowed references, will result in + an error. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + Returns COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED if there are too many pre-existing references to the given object. + */ +MSYS_DLL_EXPORT int MSYS_Shallow_Copy_DataObject(MSYS_DataObject_T * source, MSYS_DataObject_T * dest); + +/*! + int MSYS_Deep_Copy_DataObject(const MSYS_DataObject_T * source, MSYS_DataObject_T * dest) + + Copies the data from the source MSYS_DataObject_T object to the dest MSYS_DataObject_T object. + + Note: This function copies both the values for the struct and the actual data. (A deep copy.) + Unlike MSYS_Shallow_Copy_DataObject(), the resulting dest object that this function produces + is completely independant of the source object, and may be deallocated / modified without + changing the source object at all. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + Returns COMMON_ERROR_DATA_CORRUPTION if the source MSYS_DataObject_T object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + */ +MSYS_DLL_EXPORT int MSYS_Deep_Copy_DataObject(const MSYS_DataObject_T * source, MSYS_DataObject_T * dest); + +/*! + int MSYS_DataObject_Set_Data_From_CString(MSYS_DataObject_T * obj, const char * data, const size_t dataLength) + + Copies the given data into the given data object, deallocating any previous data held by that data object. + (The result is the given data object only contains a copy of the given data.) + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, or the given dataLength is less than or equal to zero. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Set_Data_From_CString(MSYS_DataObject_T * obj, const char * data, const size_t dataLength); + +/*! + int MSYS_DataObject_Set_Data_From_Char(MSYS_DataObject_T * obj, const char data) + + Copies the given char into the given data object, deallocating any previous data held by that data object. + (The result is the given data object only contains a copy of the given char.) + + Note: This function is just a wrapper for MSYS_DataObject_Set_Data_From_CString(), + and is the equivalent to calling it like: MSYS_DataObject_Set_Data_From_CString(obj, &data, sizeof(char)). + (That's what this function does....) + + As such this function's return codes are identical to MSYS_DataObject_Set_Data_From_CString(). + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Set_Data_From_Char(MSYS_DataObject_T * obj, const char data); + +/*! + int MSYS_DataObject_Append_CString(MSYS_DataObject_T * obj, const char * data, const size_t dataLength) + + Appends the given C-Style string to the given data object's buffer. + + Note: This function is just a wrapper for MSYS_DataObject_Insert_CString(), with the given + offset set to SIZE_MAX. + + See MSYS_DataObject_Insert_CString() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Append_CString(MSYS_DataObject_T * obj, const char * data, const size_t dataLength); + +/*! + int MSYS_DataObject_Append_Char(MSYS_DataObject_T * obj, const char data) + + Appends the given char to the given data object's buffer. + + Note: This function is just a wrapper for MSYS_DataObject_Insert_CString(), with the given + offset set to SIZE_MAX, and dataLength set to sizeof(char). + + See MSYS_DataObject_Insert_CString() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Append_Char(MSYS_DataObject_T * obj, const char data); + +/*! + int MSYS_DataObject_Append_DataObject(MSYS_DataObject_T * obj, const MSYS_DataObject_T * src) + + Appends the given MSYS_DataObject_T object (src) to the given data object's (obj) buffer. + + Note: This function is just a wrapper for MSYS_DataObject_Insert_From_DataObject(), with the given + offset set to SIZE_MAX. + + See MSYS_DataObject_Insert_From_DataObject() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Append_DataObject(MSYS_DataObject_T * obj, const MSYS_DataObject_T * src); + +/*! + int MSYS_DataObject_Substr(const MSYS_DataObject_T * obj, const size_t offset, const size_t endpoint, char ** substr) + + Creates a new substring from the given data object's current contents, using the given offset and endpoint (in bytes). + The pointer for the new substring is then copied to substr. + + The resulting substring should be deallocated with DataProcess_Deallocate_CString() when no longer needed. + + Note: Both the offset and endpoint arguments are considered to be relative to the start of the data object's current buffer, + and must be within that buffer. The resulting substring is always (endpoint - offset) bytes long. Also only the actual content + of the buffer is considered, not the capacity of the buffer. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, the given offset is less than zero, + or the given endpoint is less than or equal to the given offset. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + Returns COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED if the resulting length / capacity of the substring would be + bigger than the SIZE_MAX that the system supports. + Returns COMMON_ERROR_DATA_CORRUPTION if the source MSYS_DataObject_T object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + Returns COMMON_ERROR_NO_DATA if the given MSYS_DataObject_T has no data in it. (I.e. Length is zero.) + Returns COMMON_ERROR_RANGE_ERROR if the given offset and / or endpoint arguments would result in a substring with contents outside of the + given data object's current contents. (I.e. A substring that would contain data from before or after the end of the data object's contents.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Substr(const MSYS_DataObject_T * obj, const size_t offset, const size_t endpoint, char ** substr); + +/*! + int MSYS_DataObject_Buffer_Copy(MSYS_DataObject_T * dest, const MSYS_DataObject_T * source, const size_t copy_offset, const size_t copy_length) + + This function is designed to copy data from one data object to another, without performing a memory re/allocation of any kind. + + If the requested amount of data to be copied from the source object is too big to fit in the dest object, + then an error will be returned. + + Note: This function will erase the contents of the dest buffer before copying the data from the source object if it succeeds. + (I.e. The result is only the requested data from the source object will be in the dest object's memory buffer (starting at offset 0) + if the function succeeds.) No changes to the dest object's capacity will be made, the dest object will have the same capacity as before + the call to this function. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, the given copy_offset is less than zero, + or the given copy_length is less than or equal to zero. + Returns COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED if the result of adding the given copy_offset and copy_length would be + bigger than the SIZE_MAX that the system supports. + Returns COMMON_ERROR_DATA_CORRUPTION if the source / dest MSYS_DataObject_T object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + Returns COMMON_ERROR_NO_DATA if the given source MSYS_DataObject_T has no data in it. (I.e. Length is zero.). + Returns COMMON_ERROR_RANGE_ERROR if the given copy_offset and / or copy_length arguments would result in a buffer with contents outside of the + given source data object's current contents. (I.e. A buffer that would contain data from before or after the end of the source data object's contents.) + Returns COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL if the given dest object's memory buffer is too small to contain the data to be copied from the source + object. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Buffer_Copy(MSYS_DataObject_T * dest, const MSYS_DataObject_T * source, const size_t copy_offset, const size_t copy_length); + +/*! + int MSYS_DataObject_Compare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2) + + This function checks the given MSYS_DataObject_T against another MSYS_DataObject_T object to + see if they equal each other. + + Note: This function is looking for EXACT matches. Which means either the structures must have identical memory + addresses or the buffer memory addresses, length, and capacities much match. (I.e. A shallow copy will match the object it was created + from, but a deep copy will not. (The deep copy has a different internal buffer memory address.)) + + Additional note: Passing a NULL object as a comparison will fail against a valid object. + Passing two NULL objects will pass. + + Returns COMMON_ERROR_COMPARISON_PASSED if both MSYS_DataObject_T objects are equal. (Or both are NULL objects.) + Returns COMMON_ERROR_COMPARISON_FAILED if MSYS_DataObject_T objects are NOT equal. (Or one object is NULL and the other is a VALID object.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Compare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2); + +/*! + int MSYS_DataObject_NCompare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2) + + This function checks the given MSYS_DataObject_T against another MSYS_DataObject_T object to + see if they equal each other, but inverses the result. (Via a NOT operation on the result.) + + Note: This function is a wrapper for MSYS_DataObject_Compare(), the only difference is that it inverses the result + from MSYS_DataObject_Compare(). As such it's the equivalent to calling !(MSYS_DataObject_Compare(obj1, obj2)). + + Returns COMMON_ERROR_COMPARISON_FAILED if both MSYS_DataObject_T objects are equal. (Or both are NULL objects.) + Returns COMMON_ERROR_COMPARISON_PASSED if MSYS_DataObject_T objects are NOT equal. (Or one object is NULL and the other is a VALID object.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_NCompare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2); + +/*! + MSYS_DLL_EXPORT int MSYS_DataObject_Data_Compare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2) + + This function checks the data and lengths of the given MSYS_DataObject_T against another MSYS_DataObject_T object to + see if they equal each other. + + Note: Unlike MSYS_DataObject_Compare(), this function does not check capacity, only the length and actual data is + checked. In other words a shallow copy, AND a deep copy, will match the object it was created from. BUT, the allocated + memory capacities MAY differ from each other. + + Additional note: Passing a NULL object as a comparison will fail against a valid object. + Passing two NULL objects will pass. + + Returns COMMON_ERROR_COMPARISON_PASSED if both MSYS_DataObject_T objects are equal. (Or both are NULL objects.) + Returns COMMON_ERROR_COMPARISON_FAILED if MSYS_DataObject_T objects are NOT equal. (Or one object is NULL and the other is a VALID object.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Data_Compare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2); + +/*! + int MSYS_DataObject_Data_NCompare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2) + + This function checks the data and lengths of the given MSYS_DataObject_T against another MSYS_DataObject_T object to + see if they equal each other, but inverses the result. (Via a NOT operation on the result.) + + Note: This function is a wrapper for MSYS_DataObject_Data_Compare(), the only difference is that it inverses the result + from MSYS_DataObject_Data_Compare(). As such it's the equivalent to calling !(MSYS_DataObject_Data_Compare(obj1, obj2)). + + Returns COMMON_ERROR_COMPARISON_FAILED if both MSYS_DataObject_T objects are equal. (Or both are NULL objects.) + Returns COMMON_ERROR_COMPARISON_PASSED if MSYS_DataObject_T objects are NOT equal. (Or one object is NULL and the other is a VALID object.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Data_NCompare(const MSYS_DataObject_T * obj1, const MSYS_DataObject_T * obj2); + +/*! + int MSYS_DataObject_Reserve_Memory(MSYS_DataObject_T * obj, const size_t memoryLength) + + This function reserves memory (preallocates) for a data object. If this function reallocates + memory, then any pre-existing data will be retained after the reallocation. + + Note: This function expects that the given length is the total amount + of memory to allocate for the data object, not the amount added. + + I.e if you have allocated 6 bytes, and then call this function with the amount + of 7 bytes you will get a total size of 7 bytes not 13 bytes. + + If given a length that is equal to zero or less than the given object's current + capacity, this function will return an error. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, the given memoryLength is equal to zero, + or the given memoryLength is less than or equal to the given object's current capacity. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + Returns COMMON_ERROR_DATA_CORRUPTION if the given data object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Reserve_Memory(MSYS_DataObject_T * obj, const size_t memoryLength); + +/*! + int MSYS_DataObject_Insert_CString_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength) + + Inserts the given C-Style string to the given data object's buffer at the given offset. (No memory allocation version.) + + Note: If the given data object's remaining capacity is not big enough to hold the pre-existing data and the given string, + then an error will be returned, and the data object will not be modified. (Use MSYS_DataObject_Insert_CString() if you want memory to be + reallocated automatically. This function is intended to be used when memory is preallocated for the data.) + + If this function is given an offset greater than the length of the given object's pre-existing content, (as returned by MSYS_DataObject_Get_Length()), + then this function will append the given data to the end of the given object's pre-existing content. (Pre-allocated capacity is ignored when considering + the position of the offset.) + + I.E. + Pre-existing content: "Cat" (Length: 3) + --> Actual buffer: "Cat " (Total Capacity: 8) + Given Data: " Stop" (Length: 5) + Given offset: 5. + Result: "Cat Stop" (Length: 8) + --> Actual buffer: "Cat Stop" (Total Capacity: 8) + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, or the given dataLength is less than or equal to zero. + Returns COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL if the given data object lacks a big enough buffer to store the given data and it's original data. + Returns COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED if the resulting calculations on offset / length / capacity would be bigger than the SIZE_MAX + that the system supports. + Returns COMMON_ERROR_DATA_CORRUPTION if the given data object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Insert_CString_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength); + +/*! + int MSYS_DataObject_Insert_Char_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const char data) + + Inserts the given char into the given data object, at the given offset. (No memory allocation version.) + + Note: This function is just a wrapper for MSYS_DataObject_Insert_CString_No_Allocaton(), and is the equivalent to calling + MSYS_DataObject_Insert_CString_No_Allocaton(obj, offset, data, sizeof(char)). + + See MSYS_DataObject_Insert_CString_No_Allocaton() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Insert_Char_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const char data); + +/*! + int MSYS_DataObject_Insert_From_DataObject_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src) + + Inserts the given source (src) data object's content, into the given dest data object (obj), at the given offset. (No memory allocation version.) + + Note: This function is just a wrapper for MSYS_DataObject_Insert_CString_No_Allocaton(). + + See MSYS_DataObject_Insert_CString_No_Allocaton() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Insert_From_DataObject_No_Allocaton(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src); + +/*! + int MSYS_DataObject_Insert_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength) + + Inserts the given C-Style string to the given data object's buffer at the given offset. + + Note: This function performs memory (re)allocations. If you DO NOT want the buffer to be reallocated automatically, + (due to memory constraints / design considerations / etc.) call MSYS_DataObject_Insert_CString_No_Allocaton() instead. + + Note 2: If the given data object's remaining capacity is not big enough to hold the pre-existing data and the given string, + then the object's buffer will be reallocated to be big enough to contain both. The resulting buffer will be + have a length and capacity that are equal to the length of the pre-existing data and the given string. + I.E. + newCapacity = (obj->capacity + (dataLength - (obj->capacity - obj->length))) + newLength = newCapacity + + If this function is given an offset greater than the length of the given object's pre-existing content, (as returned by MSYS_DataObject_Get_Length()), + then this function will append the given data to the end of the given object's pre-existing content. (Pre-allocated capacity is ignored when considering + the position of the offset.) + + Example without remaining data: + Pre-existing content: "Dog" (Length: 3) + --> Actual buffer: "Dog " (Total Capacity: 8) + Given Data: " Eats." (Length: 6) + Given offset: 5. + Result: "Dog Eats." (Length: 9) + --> Actual buffer: "Dog Eats." (Total Capacity: 9) + + Example with remaining data: + Pre-existing content: "Suzie walked to the park." (Length: 25) + --> Actual buffer: "Suzie walked to the park. " (Total Capacity: 26) + Given Data: "ran" (Length: 3) + Given offset: 6. + Result: "Suzie ranwalked to the park. " (Length: 29) + --> Actual buffer: "Suzie ranwalked to the park. " (Total Capacity: 29) + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, or the given dataLength is less than or equal to zero. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + Returns COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED if the resulting calculations on offset / length / capacity would be bigger than the SIZE_MAX + that the system supports. + Returns COMMON_ERROR_DATA_CORRUPTION if the given data object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Insert_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength); + +/*! + int MSYS_DataObject_Insert_Char(MSYS_DataObject_T * obj, const size_t offset, const char data) + + Inserts the given char into the given data object, at the given offset. + + Note: This function is just a wrapper for MSYS_DataObject_Insert_CString(), and is the equivalent to calling + MSYS_DataObject_Insert_CString(obj, offset, data, sizeof(char)). + + See MSYS_DataObject_Insert_CString() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Insert_Char(MSYS_DataObject_T * obj, const size_t offset, const char data); + +/*! + int MSYS_DataObject_Insert_From_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src) + + Inserts the given source (src) data object's content, into the given dest data object (obj), at the given offset. + + Note: This function is just a wrapper for MSYS_DataObject_Insert_CString(). + + See MSYS_DataObject_Insert_CString() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Insert_From_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src); + +/*! + int MSYS_DataObject_Replace_With_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength) + + Takes the given C-Style string and replaces the given data object's buffer at the given offset with it, and updates the data object's + length if needed. + + Note: If any data past the given offset is not replaced it will be retained. + + Example with remaining data: + Pre-existing content: "Suzie walked to the park." (Length: 25) + --> Actual buffer: "Suzie walked to the park. " (Total Capacity: 26) + Given Data: "ran" (Length: 3) + Given offset: 6. + Result: "Suzie ranked to the park. " (Length: 26) + --> Actual buffer: "Suzie ranked to the park. " (Total Capacity: 26) + + Note 2: This function does NOT perform memory (re)allocations. In cases where a memory (re)allocation is required, an error will be returned, + and the given data object will NOT be altered. If you want the buffer to be reallocated automatically, call + MSYS_DataObject_Overwrite_With_CString() instead. + + Note 3: This function is only for replacing existing data. The given offset must be within the given data object's length, (A special case + is permitted for when length is zero. See note 4.) and the given data must fit within the remaining space in memory buffer, or an error + will be returned. + + Note 4: In the event the given data object's length is zero, then the given offset must be zero. (Pre-allocated capacity is ignored when + considering the position of the offset.) The given data must still fit within the capacity of the existing memory buffer. + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, or the given dataLength is less than or equal to zero. + Returns COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED if the resulting calculations on offset / length / capacity would be bigger than the SIZE_MAX + that the system supports. + Returns COMMON_ERROR_DATA_CORRUPTION if the given data object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + Returns COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL if the given offset and / or dataLength would require a memory reallocation to fit within the + given data object's memory buffer. + Returns COMMON_ERROR_RANGE_ERROR if the given offset is beyond the current length of the buffer. (As returned by MSYS_DataObject_Get_Length().) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Replace_With_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength); + +/*! + int MSYS_DataObject_Replace_With_Char(MSYS_DataObject_T * obj, const size_t offset, const char data) + + Replaces the given data object's data, at the given offset, with the given char. + + Note: This function is just a wrapper for MSYS_DataObject_Replace_With_CString(), and is the equivalent to calling + MSYS_DataObject_Replace_With_CString(obj, offset, data, sizeof(char)). + + See MSYS_DataObject_Replace_With_CString() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Replace_With_Char(MSYS_DataObject_T * obj, const size_t offset, const char data); + +/*! + int MSYS_DataObject_Replace_With_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src) + + Replaces the given dest data object (obj)'s content, with the given source (src) data object's content, at the given offset. + + Note: This function is just a wrapper for MSYS_DataObject_Replace_With_CString(). + + See MSYS_DataObject_Replace_With_CString() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Replace_With_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src); + +/*! + int MSYS_DataObject_Overwrite_With_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength) + + Takes the given C-Style string and overwrites the given data object's buffer at the given offset with it, and updates the data object's + length if needed. + + If the buffer is not big enough to contain the entireity of the given data after the given offset, the buffer will be automaticly reallocated + to be able to contain the data. + I.e. + newCapacity = (offset + dataLength) + newLength = newCapacity + + Note: If any data past the given offset is not overwritten it will be retained. + + Example without remaining data: + Pre-existing content: "Dog bark" (Length: 8) + --> Actual buffer: "Dog bark " (Total Capacity: 9) + Given Data: "ug Eats." (Length: 8) + Given offset: 2. + Result: "Doug Eats." (Length: 10) + --> Actual buffer: "Doug Eats." (Total Capacity: 10) + + Example with remaining data: + Pre-existing content: "Suzie walked to the park." (Length: 25) + --> Actual buffer: "Suzie walked to the park. " (Total Capacity: 26) + Given Data: "ran" (Length: 3) + Given offset: 6. + Result: "Suzie ranked to the park. " (Length: 26) + --> Actual buffer: "Suzie ranked to the park. " (Total Capacity: 26) + + Note 2: This function performs memory (re)allocations. If you DO NOT want the buffer to be reallocated automatically, + (due to memory constraints / design considerations / etc.) call MSYS_DataObject_Replace_With_CString() instead. + + Note 3: This function is only for overwriting existing data. The given offset must be within the given data object's length, (A special case + is permitted for when length is zero. See note 4.) + + Note 4: In the event the given data object's length is zero, then the given offset must be zero. (Pre-allocated capacity is ignored when + considering the position of the offset.) + + Returns COMMON_ERROR_SUCCESS if successful. + Returns COMMON_ERROR_INVALID_ARGUMENT if a given pointer is invalid, or the given dataLength is less than or equal to zero. + Returns COMMON_ERROR_SYSTEM_LIMIT_EXCEEDED if the resulting calculations on offset / length / capacity would be bigger than the SIZE_MAX + that the system supports. + Returns COMMON_ERROR_MEMORY_ERROR if a memory allocation attempt fails. + Returns COMMON_ERROR_DATA_CORRUPTION if the given data object is inconsistant. (E.x. No allocated buffer, but size or capacity > 0.) + Returns COMMON_ERROR_RANGE_ERROR if the given offset is beyond the current length of the buffer. (As returned by MSYS_DataObject_Get_Length().) + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Overwrite_With_CString(MSYS_DataObject_T * obj, const size_t offset, const char * data, const size_t dataLength); + +/*! + int MSYS_DataObject_Overwrite_With_Char(MSYS_DataObject_T * obj, const size_t offset, const char data) + + Overwrites the given data object's data, at the given offset, with the given char. + + Note: This function is just a wrapper for MSYS_DataObject_Overwrite_With_CString(), and is the equivalent to calling + MSYS_DataObject_Overwrite_With_CString(obj, offset, data, sizeof(char)). + + See MSYS_DataObject_Overwrite_With_CString() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Overwrite_With_Char(MSYS_DataObject_T * obj, const size_t offset, const char data); + +/*! + int MSYS_DataObject_Overwrite_With_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src) + + Overwrites the given dest data object (obj)'s content, with the given source (src) data object's content, at the given offset. + + Note: This function is just a wrapper for MSYS_DataObject_Overwrite_With_CString(). + + See MSYS_DataObject_Overwrite_With_CString() for the list of possible return codes / expected behavior. + */ +MSYS_DLL_EXPORT int MSYS_DataObject_Overwrite_With_DataObject(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src); + +/* Check for C++ compiler. */ +#ifdef __cplusplus +} /* End of extern "C" */ +#endif /* __cplusplus */ + +#endif /* MSYS_DATAOBJECT_H */ + +/* End of Data_Object.h. */ diff --git a/src/Core/Src/Data_Object/Data_Object_Private_Structure.c b/src/Core/Src/Data_Object/Data_Object_Private_Structure.c new file mode 100644 index 0000000..6695a2e --- /dev/null +++ b/src/Core/Src/Data_Object/Data_Object_Private_Structure.c @@ -0,0 +1,44 @@ +/* + Multiverse Engine Project Core Data_Object_Private_Structure.c 28/7/2016 + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +#ifdef __cplusplus__ +extern "C" { +#endif /* __cplusplus__ */ + +/* + * + * Private data structure is defined below. + * (Subject to change without warning.) + * + * DO NOT USE MSYS_DataObject_T_Private DIRECTLY. (The exception this is made for is the Unit_Tests program.) + * USE THE MSYS_DataObject_T object and functions instead. + */ + +/* Define the real MSYS_DataObject_T object. */ +typedef struct MSYS_DataObject_Private { + char * data; /* Pointer to data. */ + size_t length; /* Length of data. */ + size_t capacity; /* Length of data we can store. */ + size_t refcount; /* Number of references (Shadow Copies) that point to the object. */ +} MSYS_DataObject_T_Private; + +#ifdef __cplusplus__ +} /* extern "C" */ +#endif /* __cplusplus__ */ diff --git a/src/Core/Src/FileStreams.cpp b/src/Core/Src/FileStreams.cpp index f8be1ef..66b6c38 100644 --- a/src/Core/Src/FileStreams.cpp +++ b/src/Core/Src/FileStreams.cpp @@ -31,7 +31,7 @@ Mode Values are std::string FileStreams::configCreate(std::string path, Panic::Panic_ERROR & error) { // init vars - ofstream Ini; + std::ofstream Ini; // open file on disk. Ini.open(path.c_str()); @@ -52,11 +52,11 @@ std::string FileStreams::configCreate(std::string path, Panic::Panic_ERROR & err return "OK"; } -std::string FileStreams::configParser(string path, string key, Panic::Panic_ERROR & Error) +std::string FileStreams::configParser(std::string path, std::string key, Panic::Panic_ERROR & Error) { // init vars - ifstream Ini; + std::ifstream Ini; bool dataFound = false; bool fileatEOF = false; bool fileFail = false; @@ -68,8 +68,7 @@ std::string FileStreams::configParser(string path, string key, Panic::Panic_ERRO long diff = 0; // Open file - - Ini.open(path.c_str(), ios::in); + Ini.open(path.c_str(), std::ios::in); // check if file open failed @@ -100,7 +99,7 @@ std::string FileStreams::configParser(string path, string key, Panic::Panic_ERRO // Comment Line ignore it. temp = ""; } - if (temp.find(key) != string::npos) + if (temp.find(key) != std::string::npos) { // data found. Condence variable to just data and return it. dStartLocation = temp.find("="); @@ -126,12 +125,12 @@ std::string FileStreams::configParser(string path, string key, Panic::Panic_ERRO output = temp; Ini.close(); } - if (temp.find(key) == string::npos && Ini.eof() != true) + if (temp.find(key) == std::string::npos && Ini.eof() != true) { // data not found try again. fileatEOF = false; } - if (temp.find(key) == string::npos && Ini.eof() == true) + if (temp.find(key) == std::string::npos && Ini.eof() == true) { // We didn't find what we were looking for so close the file, warn the user, clear the buffers, and exit the function. fileatEOF = true; @@ -145,10 +144,10 @@ std::string FileStreams::configParser(string path, string key, Panic::Panic_ERRO } -string FileStreams::configWriter(string path, string key, string value, Panic::Panic_ERROR & error , bool createkey) +std::string FileStreams::configWriter(std::string path, std::string key, std::string value, Panic::Panic_ERROR & error , bool createkey) { // Init vars. - fstream OUT; // Output File stream + std::fstream OUT; // Output File stream bool keyfound = false; // has key been found in file bool atEOF = false; // has end of file been hit. std::string buffer = ""; // Temp buffer for data from file. @@ -191,8 +190,8 @@ string FileStreams::configWriter(string path, string key, string value, Panic::P // Get line from file getline(OUT , buffer); - // Check buffer for string - if (buffer.find(key.c_str()) != string::npos) + // Check buffer for std::string + if (buffer.find(key.c_str()) != std::string::npos) { // Key found keyfound = true; @@ -217,7 +216,7 @@ string FileStreams::configWriter(string path, string key, string value, Panic::P // Open file in write mode. - OUT.open(path.c_str(), ios::out); + OUT.open(path.c_str(), std::ios::out); // If key is not present and createkey is true write key into file. @@ -225,7 +224,7 @@ string FileStreams::configWriter(string path, string key, string value, Panic::P if (keyfound == false && createkey == true) { // Seek to eof. - OUT.seekp(ios::end); + OUT.seekp(std::ios::end); // Write key to file before writing variable. OUT << key <<" = " ; @@ -235,7 +234,7 @@ string FileStreams::configWriter(string path, string key, string value, Panic::P if (keyfound == true) { // Move to correct line. - OUT.seekp(ios::beg+location); + OUT.seekp(std::ios::beg+location); getline(OUT, buffer); } @@ -256,7 +255,7 @@ std::string readFile(std::string file_name, int indexNumber) //char input[900]; std::string output; output = ""; - ifstream SYSIN; + std::ifstream SYSIN; bool indexnumberFound = false; bool fileatEOF = false; std::string temp = ""; @@ -271,62 +270,62 @@ std::string readFile(std::string file_name, int indexNumber) realIndexNumber = realIndexNumber += indexNumber; realIndexNumber = realIndexNumber += ']'; - cout << "the realIndexNumber is : " < 2) { - cout << "ERROR: FileStreams.cpp: Unknown Command. \n"; - cout << "the mode is : " << mode <<"\n" << "the filename is : " << file_name <<"\n" << "the data is: " << data << "\n"; + std::cout << "ERROR: FileStreams.cpp: Unknown Command. \n"; + std::cout << "the mode is : " << mode <<"\n" << "the filename is : " << file_name.c_str() <<"\n" << "the data is: " << data.c_str() << "\n"; errorCode = mode; } return errorCode; } -string FileStreams::readEntireFile(std::string filename, Panic::Panic_ERROR & error) +std::string FileStreams::readEntireFile(std::string filename, Panic::Panic_ERROR & error) { // init vars std::string output = ""; std::string temp = ""; - ifstream SYSIN; + std::ifstream SYSIN; // Open the file - SYSIN.open(filename.c_str(), ios::in); + SYSIN.open(filename.c_str(), std::ios::in); // Make sure file is open if (SYSIN.is_open() != true) { // File did not open call PanicHandler and exit the function. - cout << "ERROR: Opening File " < /* Needed to define exit(). (MSVC includes this header automaticly with the includes from Panic.h.) */ +#endif /* __GNUC__ */ + std::string Panic::Panic_ERROR::ReturnLastError() const { return this->LastError; @@ -30,7 +36,7 @@ std::string Panic::Panic_ERROR::ReturnLastError() const std::string Panic::Panic_ERROR::PanicHandler(const std::string & message, const int & moduleID, const unsigned int & log_level, const bool & killengine) { // Init vars. - fstream old_log_file; + std::fstream old_log_file; std::string temp_path = ""; size_t size = 0; @@ -41,7 +47,7 @@ std::string Panic::Panic_ERROR::PanicHandler(const std::string & message, const this->LastError = "PanicHandler : No error message specified"; // Print error to stdout - cout << "PanicHandler : No error message specified" <<"\n\n"; + std::cout << "PanicHandler : No error message specified" <<"\n\n"; // Check and see if we need to die. if (killengine == true) @@ -85,7 +91,7 @@ std::string Panic::Panic_ERROR::PanicHandler(const std::string & message, const this->LastError += message; // Print error to stdout. - cout << "\nPanicHandler: " << this->LastError <<"\n\n"; + std::cout << "\nPanicHandler: " << this->LastError.c_str() <<"\n\n"; } // Check and see if logging is enabled. @@ -105,21 +111,21 @@ std::string Panic::Panic_ERROR::PanicHandler(const std::string & message, const { // Reset the log line count and reset file position. this->currentLogLine = 0; - this->logfile.seekp(0, ios::beg); - this->logfile.seekg(0, ios::beg); + this->logfile.seekp(0, std::ios::beg); + this->logfile.seekg(0, std::ios::beg); // Create old file name. temp_path = this->pathToLogFile; temp_path += ".old"; // Try to open the file. - old_log_file.open(temp_path.c_str(), ios::in | ios::out | ios::trunc); + old_log_file.open(temp_path.c_str(), std::ios::in | std::ios::out | std::ios::trunc); if (old_log_file.is_open()) { // Compute the size of the old log file. - this->logfile.seekg(0, ios::end); + this->logfile.seekg(0, std::ios::end); size = this->logfile.tellg(); - this->logfile.seekg(0, ios::beg); + this->logfile.seekg(0, std::ios::beg); // Copy the old log file. for(size_t x = 0; x < size; x++) @@ -136,7 +142,7 @@ std::string Panic::Panic_ERROR::PanicHandler(const std::string & message, const // Close the log_file and attempt to reopen it. this->logfile.close(); - this->logfile.open(this->pathToLogFile.c_str(), ios::in | ios::out | ios::trunc); + this->logfile.open(this->pathToLogFile.c_str(), std::ios::in | std::ios::out | std::ios::trunc); if(!this->logfile.is_open()) { // Could not open log file, disable logging. @@ -155,7 +161,7 @@ std::string Panic::Panic_ERROR::PanicHandler(const std::string & message, const } // Write the log entry. - this->logfile << this->LastError << '\n'; + this->logfile << this->LastError.c_str() << '\n'; // Flush the output buffer. this->logfile.flush(); @@ -202,7 +208,7 @@ short Panic::Panic_ERROR::enable_logging(const std::string & path_to_logfile, co } // Attempt to open the file. - this->logfile.open(path_to_logfile.c_str(), ios::in | ios::out | ios::trunc); + this->logfile.open(path_to_logfile.c_str(), std::ios::in | std::ios::out | std::ios::trunc); if (this->logfile.is_open() == false) { // Could not open log file. @@ -287,7 +293,7 @@ unsigned int Panic::Panic_ERROR::get_current_log_line() const return this->currentLogLine; } -void Panic::FileStream_Status(Panic::Panic_ERROR & error, fstream & stream, const unsigned int & log_level) +void Panic::FileStream_Status(Panic::Panic_ERROR & error, std::fstream & stream, const unsigned int & log_level) { // Output status header. error.PanicHandler("FileStream_Status:", ERROR_ID, log_level); diff --git a/src/Core/Src/Panic.h b/src/Core/Src/Panic.h index 23f4df5..72ebebf 100644 --- a/src/Core/Src/Panic.h +++ b/src/Core/Src/Panic.h @@ -19,12 +19,11 @@ https://github.com/codebase7/mengine */ +/* Include guard. */ #ifndef PANIC_H #define PANIC_H -#include "BaseHeader.h" - -// Define version and compile date time. +/* Define version and compile date time. */ #ifndef PANIC_HANDLER_VERSION #define PANIC_HANDLER_VERSION "0.0.1 Alpha\0" #endif @@ -37,7 +36,7 @@ #define PANIC_HANDLER_COMPILETIME __TIME__ #endif -// Module ERROR IDs +/* Module ERROR IDs */ #define CORE_ID 1 #define COMMON_ID 2 #define GAME_ID 7 @@ -45,7 +44,19 @@ #define PORT_AUDIO_ID 9 #define ERROR_ID 10 -#include "Panic_Error_Levels.h" // Defines the error / log levels. +/* Include the error / log level definitions. */ +#include "Panic_Error_Levels.h" + +/* Check for C++ compiler. */ +#ifdef __cplusplus + +/* Internal includes. */ +#include "../../DLL_PORT.h" /* Defines MSYS_DLL_EXPORT. */ + +/* External includes. */ +#include +#include +#include namespace Panic{ class Panic_ERROR { @@ -57,7 +68,7 @@ class Panic_ERROR { unsigned int logLevel; // Determines when to write to the log file. unsigned int maxLogLines; // Determines when the log file will start being overwritten. unsigned int currentLogLine; // Contains the current Log line. - fstream logfile; // File handler for the log file. + std::fstream logfile; // File handler for the log file. bool logfile_enabled; // Used to tell if we are writing to a log file or not. /* @@ -210,11 +221,11 @@ class Panic_ERROR { MSYS_DLL_EXPORT unsigned int get_current_log_line() const; }; /*! - void Panic::FileStream_Status(Panic::ERROR & error, fstream & stream, const unsigned int & log_level) + void Panic::FileStream_Status(Panic::ERROR & error, std::fstream & stream, const unsigned int & log_level) Gets the status for a given file stream, and outputs that status to the given error handler with the given log_level. */ - MSYS_DLL_EXPORT void FileStream_Status(Panic::Panic_ERROR & error, fstream & stream, const unsigned int & log_level); + MSYS_DLL_EXPORT void FileStream_Status(Panic::Panic_ERROR & error, std::fstream & stream, const unsigned int & log_level); /*! const char * Panic::Get_Library_Version() @@ -238,6 +249,8 @@ class Panic_ERROR { MSYS_DLL_EXPORT const char * Get_Library_Compile_Time(); } -#endif +#endif /* __cplusplus */ + +#endif /* PANIC_H */ -// End of Panic.h +/* End of Panic.h */ diff --git a/src/Core/Src/Panic_Error_Levels.h b/src/Core/Src/Panic_Error_Levels.h index 2616c9a..18421a0 100644 --- a/src/Core/Src/Panic_Error_Levels.h +++ b/src/Core/Src/Panic_Error_Levels.h @@ -19,11 +19,11 @@ https://github.com/codebase7/mengine */ -// Include guard. +/* Include guard. */ #ifndef PANIC_ERROR_LEVELS_H #define PANIC_ERROR_LEVELS_H -// Define the error / log levels. +/* Define the error / log levels. */ #define ERROR_DISABLE 0 #define ERROR_CRITICAL 1 #define ERROR_WARNING 2 @@ -31,6 +31,6 @@ #define ERROR_DEBUG 4 #define ERROR_VERBOSE 5 -#endif // PANIC_ERROR_LEVELS_H +#endif /* PANIC_ERROR_LEVELS_H */ -// End of Panic_Error_Levels.h \ No newline at end of file +/* End of Panic_Error_Levels.h */ diff --git a/src/Tests/CMakeLists.txt b/src/Tests/CMakeLists.txt index d6ed68f..2bd05bc 100644 --- a/src/Tests/CMakeLists.txt +++ b/src/Tests/CMakeLists.txt @@ -5,11 +5,16 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${O_OUTPUT_DIR}) # Define Unit Tests base includes. set (UNIT_TESTS_INCLUDES_BASE Unit_Tests.cpp) -# We may make Core an optional subsystem in the future. +# We may make Core and Byte_Order an optional subsystem in the future. set (UNIT_TESTS_INCLUDES ${UNIT_TESTS_INCLUDES_BASE} Test_Base_Header.cpp -Unit_Test_Data_Object.cpp -Unit_Test_Data_Object_Insert_Char.cpp) +Unit_Test_DataProcess.c +Unit_Test_Data_Object.cpp +Unit_Test_Data_Object_Insert_Char.cpp +Unit_Test_Data_Object_Insert_Replace_Overwrite_Char.cpp +Unit_Test_Data_Object_Insert_Replace_Overwrite_Data_Object.cpp +Unit_Test_Data_Object_Insert_Replace_Overwrite_C_Str.cpp +Unit_Test_Byte_Order.cpp) # Only build the tests for the subsystems that are enabled. if (BUILD_COMMON_ERROR_HANDLER) @@ -27,6 +32,11 @@ if (BUILD_THREADING_SUBSYSTEM) set (UNIT_TESTS_DEFINES ${UNIT_TESTS_DEFINES} MSYS_HAVE_THREAD_UTILS) endif (BUILD_THREADING_SUBSYSTEM) +if (BUILD_RENDERING_SUBSYSTEM) + set (UNIT_TESTS_INCLUDES ${UNIT_TESTS_INCLUDES} Unit_Test_Rendering_Subsystem.cpp) + set (UNIT_TESTS_DEFINES ${UNIT_TESTS_DEFINES} MSYS_HAVE_RENDERING_SUBSYS) +endif (BUILD_RENDERING_SUBSYSTEM) + # Add shared library executable. add_executable(Unit_Tests ${UNIT_TESTS_INCLUDES}) target_link_libraries(Unit_Tests Common_Multiverse_Engine Core_Multiverse_Engine) diff --git a/src/Tests/Test_Base_Header.h b/src/Tests/Test_Base_Header.h index f435d66..3028489 100644 --- a/src/Tests/Test_Base_Header.h +++ b/src/Tests/Test_Base_Header.h @@ -22,7 +22,17 @@ #define TEST_BASE_HEADER_H // External Includes. + +/* C++ specific includes. */ +#ifdef __cplusplus #include // For std::cout / NULL macro. +#else +/* C specific includes. */ +#include +#include +#include +#endif /* __cplusplus */ + #include // For strcmp. #ifdef _WIN32 #include /* For SleepEx(). */ diff --git a/src/Tests/Unit_Test_Byte_Order.cpp b/src/Tests/Unit_Test_Byte_Order.cpp new file mode 100644 index 0000000..77f0f06 --- /dev/null +++ b/src/Tests/Unit_Test_Byte_Order.cpp @@ -0,0 +1,466 @@ +/*! + Multiverse Engine Project 11/7/2015 Unit Tests Unit_Test_Byte_Order.cpp + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Unit_Tests.h" + +/* Check for GCC. */ +#ifdef __GNUC__ +#include /* Defines CHAR_MIN and UCHAR_MAX. */ +#endif /* End of __GNUC__ */ + +void Print_Random_Bits(const char bits) +{ + /* Init vars. */ + int retFromFunct = COMMON_ERROR_UNKNOWN_ERROR; /* Result code from engine function. */ + char * bitMaskString = NULL; /* The bitmask represented as a human-readable string. */ + size_t bitMaskStringLength = 0; /* The length of the bit mask string. */ + + /* Print the bits. */ + retFromFunct = Common_Print_Bytes_In_Binary(&bits, 1, &bitMaskString, &bitMaskStringLength, 1); + if (retFromFunct == COMMON_ERROR_SUCCESS) + { + /* Print the string. */ + std::cout << bitMaskString << " "; + + /* Deallocate the string. */ + Common_Deallocate_Print_Bytes_CString(&bitMaskString); + } + else + { + std::cout << "< ERROR: Unable to print random bits, Common_Print_Bytes_In_Binary() failed. > "; + } + std::cout.flush(); + + /* Exit function. */ + return; +} + +void Print_Bits_and_BitMask(const char byteToCheck, const char bitMask, const char bitValues) +{ + /* Print the values. */ + std::cout << "DEBUG: BYTE:\t\t"; + Print_Random_Bits(byteToCheck); + std::cout << "\nDEBUG: BITMASK:\t\t"; + Print_Random_Bits(bitMask); + std::cout << "\nDEBUG: BIT_VALUES:\t"; + Print_Random_Bits(bitValues); + std::cout << '\n'; + std::cout.flush(); + + /* Exit function. */ + return; +} + +char Generate_Random_Non_Conforming_Bit_Mask(const char byte1, const char byte2) +{ + /* Init vars. */ + char ret = '\0'; /* The result of this function. */ + + /* Generate a random bit mask to check for. */ + std::cout << "Generating random bit mask. Please Wait.\n"; + std::cout.flush(); + + /* Begin generation loop. */ + do + { + ret = ((char)DataProcess::Trivial_Random_Number_Generator(0, 255)); + } while ((byte1 | byte2) ^ ret); + + /* Exit function. */ + return ret; +} + +char Generate_Random_Bit_Mask(const char bitMask) +{ + /* Init vars. */ + char ret = '\0'; /* The result of this function. */ + + /* Generate a random bit mask to check for. */ + std::cout << "Generating random bit mask. Please Wait.\n"; + std::cout.flush(); + do + { + /* Generate a bit mask. */ + ret = ((char)DataProcess::Trivial_Random_Number_Generator(1, 254)); + } while (ret == bitMask); + + /* Exit function. */ + return ret; +} + +char Generate_Random_Bits(const char notThisValue) +{ + /* Init vars. */ + char ret = '\0'; /* The result of this function. */ + + /* Generate some random bits to check for. */ + std::cout << "Generating random bits. Please Wait.\n"; + std::cout.flush(); + + /* Generate some bits, avoiding the given value if needed. */ + while (ret == notThisValue) + { + ret = ((char)DataProcess::Trivial_Random_Number_Generator(2, 254)); + } + + /* Exit function. */ + return ret; +} + +int Generate_Matching_Bitmask_Random_Data(char * byteToCheck, char * bitMask, char * bitValues) +{ + /* Init vars. */ + int ret = COMMON_ERROR_UNKNOWN_ERROR; /* The result code of this function. */ + + /* Check for invalid arguments. */ + if ((byteToCheck != NULL) && (bitMask != NULL) && (bitValues != NULL)) + { + /* Call bitmask generation function. */ + (*bitMask) = Generate_Random_Bit_Mask(0); + + /* Generate the byteToCheck and bitValues data. (If needed, check memory addresses.) */ + if (byteToCheck != bitMask) + { + (*byteToCheck) = Generate_Random_Bits((*bitMask)); + + /* Bitwise Or the bitMask to the byteToCheck data. */ + (*byteToCheck) |= (*bitMask); + } + if ((bitValues != bitMask) && (bitValues != byteToCheck)) + { + (*bitValues) = Generate_Random_Bits((*byteToCheck)); + + /* Bitwise Or the bitMask to the bitValues data. */ + (*bitValues) |= (*bitMask); + } + + /* Done. */ + ret = COMMON_ERROR_SUCCESS; + } + else + { + /* Invalid arguments. */ + ret = COMMON_ERROR_INVALID_ARGUMENT; + } + + /* Exit function. */ + return ret; +} + +int unit_test_byte_order_Common_Print_Bytes_In_Binary_check() +{ + /* Init vars. */ + int ret = 0; /* The result of this function. */ + + + + /* Exit function. */ + return ret; +} + +int unit_test_byte_order_comparison_check() +{ + /* Init vars. */ + int ret = 0; /* The result of this function. */ + char byteToCheck = '\0'; /* The byte to compare against. */ + char bitMask = '\0'; /* The bits we should compare. */ + char bitValues = '\0'; /* The bits we compare to. */ + char extraBits = '\0'; /* Used to generate extra random bits to test with. */ + + /* Run zero comparison checks. */ + std::cout << "Byte_Order_Bit_Comparison() Test 1: All zero, no bits to check test. (Should result in COMMON_ERROR_COMPARISON_FAILED.): "; + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_FAILED) + { + /* Test 1 successful. */ + std::cout << "PASS\n"; + std::cout << "Byte_Order_Bit_Comparison() Test 2: All zero, check all bits test. (Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + bitMask = CHAR_MIN; + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 2 successful. */ + std::cout << "PASS\n"; + + /* Generate random bit mask. */ + std::cout << "Byte_Order_Bit_Comparison() Test 3: All zero, random bit mask test.\n"; + bitMask = Generate_Random_Bit_Mask(0); + Print_Bits_and_BitMask(byteToCheck, bitMask, bitValues); + std::cout << "(Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + + /* Perform all zero test with random bit mask. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 3 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 4: Non-zero matching bits, no bits to check test. (Should result in COMMON_ERROR_COMPARISON_FAILED.): "; + /* Begin checks on non-zero bit values. */ + bitMask = 0; + bitValues = 1; + byteToCheck = 1; + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_FAILED) + { + /* Test 4 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 5: Non-zero matching bits, check bits test. (Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + bitMask = 1; + + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 5 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 6: Non-zero matching bits, extra bits in byte, check bits test.\n"; + Generate_Matching_Bitmask_Random_Data(&byteToCheck, &bitMask, &bitMask); + bitValues = bitMask; /* Only the bits in the bit mask should be set in bitValues. */ + Print_Bits_and_BitMask(byteToCheck, bitMask, bitValues); + std::cout << "(Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + + /* Perform Non-zero matching bits, extra bits in byte, check bits test. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 6 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 7: Non-zero matching bits, extra bits in bitValues, check bits test.\n"; + Generate_Matching_Bitmask_Random_Data(&bitMask, &bitMask, &bitValues); + byteToCheck = bitMask; /* Only the bits in the bit mask should be set in byteToCheck. */ + Print_Bits_and_BitMask(byteToCheck, bitMask, bitValues); + std::cout << "(Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + + /* Perform Non-zero matching bits, extra bits in bitValues, check bits test. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 7 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 8: Non-zero matching bits, extra bits in byte and bitValues, check bits test.\n"; + Generate_Matching_Bitmask_Random_Data(&byteToCheck, &bitMask, &bitValues); + Print_Bits_and_BitMask(byteToCheck, bitMask, bitValues); + std::cout << "(Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + + /* Perform Non-zero matching bits, extra bits in byte and bitValues, check bits test. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 8 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 9: Non-zero matching bits, extra bits in byte and bitValues, random bitmask check bits test.\n"; + extraBits = Generate_Random_Bits(0); + + /* Binary OR the bits together so that the bits we check for will be valid. */ + byteToCheck = 0; + byteToCheck |= extraBits; + extraBits = Generate_Random_Bits(0); + + /* Binary OR the bits together so that the bits we check for will be valid. */ + bitValues = 0; + bitValues |= extraBits; + + /* Generate random bit mask. */ + bitMask = Generate_Random_Bit_Mask(0); + + /* Binary OR the bits together so that the bits we check for will be valid. */ + byteToCheck |= bitMask; + bitValues |= bitMask; + + Print_Bits_and_BitMask(byteToCheck, bitMask, bitValues); + std::cout << "(Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + + /* Perform Non-zero matching bits, extra bits in byte and bitValues, random bitmask check bits test. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 9 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 10: Non-zero NON-matching bits, extra bits in byte and bitValues, random bitmask check bits test.\n"; + extraBits = Generate_Random_Bits(0); + + /* Binary OR the bits together so that the bits we check for will be valid. */ + byteToCheck = 0; + byteToCheck |= extraBits; + extraBits = Generate_Random_Bits(byteToCheck); + + /* Binary OR the bits together so that the bits we check for will be valid. */ + bitValues = 0; + bitValues |= extraBits; + + /* Generate random bit mask. */ + bitMask = Generate_Random_Non_Conforming_Bit_Mask(byteToCheck, bitValues); + + Print_Bits_and_BitMask(byteToCheck, bitMask, bitValues); + std::cout << "(Should result in COMMON_ERROR_COMPARISON_FAILED.): "; + + /* Perform Non-zero NON-matching bits, extra bits in byte and bitValues, random bitmask check bits test. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_FAILED) + { + /* Test 10 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 11: All bits set, check all bits test. (Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + bitMask = UCHAR_MAX; + byteToCheck = UCHAR_MAX; + bitValues = UCHAR_MAX; + + /* Perform All bits set, check all bits test. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 11 successful. */ + std::cout << "PASS\n"; + std::cout << "Byte_Order_Bit_Comparison() Test 12: All bits set, no bits to check test. (Should result in COMMON_ERROR_COMPARISON_FAILED.): "; + bitMask = 0; + + /* Perform All bits set, no bits to check test. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_FAILED) + { + /* Test 12 successful. */ + std::cout << "PASS\n"; + + std::cout << "Byte_Order_Bit_Comparison() Test 13: All bits set, random bit mask test.\n"; + bitMask = Generate_Random_Bit_Mask(0); + Print_Bits_and_BitMask(byteToCheck, bitMask, bitValues); + std::cout << "(Should result in COMMON_ERROR_COMPARISON_PASSED.): "; + + /* Perform All bits set, random bit mask test. */ + if (Byte_Order_Bit_Comparison(&byteToCheck, bitMask, bitValues) == COMMON_ERROR_COMPARISON_PASSED) + { + /* Test 13 successful. */ + std::cout << "PASS\n"; + + /* End of tests. */ + ret = 0; + } + else + { + /* All bits set, random bit mask test failed. */ + ret = -13; + std::cout << "FAIL\n"; + } + } + else + { + /* All bits set, no bits to check test failed. */ + ret = -12; + std::cout << "FAIL\n"; + } + } + else + { + /* All bits set, check all bits test failed. */ + ret = -11; + std::cout << "FAIL\n"; + } + } + else + { + /* Non-zero NON-matching bits, extra bits in byte and bitValues, random bitmask check bits test failed. */ + ret = -10; + std::cout << "FAIL\n"; + } + } + else + { + /* Non-zero matching bits, extra bits in byte and bitValues, random bitmask check bits test failed. */ + ret = -9; + std::cout << "FAIL\n"; + } + } + else + { + /* Non-zero matching bits, extra bits in byte and bitValues, check bits test failed. */ + ret = -8; + std::cout << "FAIL\n"; + } + } + else + { + /* Non-zero matching bits, extra bits in bitValues, check bits test failed. */ + ret = -7; + std::cout << "FAIL\n"; + } + } + else + { + /* Non-zero matching bits, extra bits in byte, check bits test failed. */ + ret = -6; + std::cout << "FAIL\n"; + } + } + else + { + /* Non-zero matching bits, check bits test failed. */ + ret = -5; + std::cout << "FAIL\n"; + } + } + else + { + /* Non-zero matching bits, no bits to check test failed. */ + ret = -4; + std::cout << "FAIL\n"; + } + } + else + { + /* All zero, random bit mask test failed. */ + ret = -3; + std::cout << "FAIL\n"; + } + } + else + { + /* All zero, check all bits test failed. */ + ret = -2; + std::cout << "FAIL\n"; + } + } + else + { + /* All zero no bits to check test failed. */ + ret = -1; + std::cout << "FAIL\n"; + } + + /* Flush output buffer. */ + std::cout.flush(); + + /* Exit function. */ + return ret; +} + +int Unit_Test_Byte_Order_Main() +{ + /* Init vars. */ + int ret = 0; /* The result of this function. */ + int result_comparision_check = 0; /* The result of the comparison checks. */ + + /* Output START OF TEST SECTION. */ + std::cout << START_TEST_SECTION; + + /* Run comparison checks. */ + result_comparision_check = unit_test_byte_order_comparison_check(); + + /* Output END OF TEST SECTION. */ + std::cout << END_TEST_SECTION; + + /* Exit function. */ + return ret; +} diff --git a/src/Tests/Unit_Test_Common.h b/src/Tests/Unit_Test_Common.h index d72fb95..f9bf5e1 100644 --- a/src/Tests/Unit_Test_Common.h +++ b/src/Tests/Unit_Test_Common.h @@ -23,6 +23,8 @@ #define COMMON_UNIT_TESTS_H /* Include headers from Common. (If needed.) */ +#include "Unit_Tests_Byte_Order.h" + #ifdef MSYS_HAVE_FILEUTILLS #include "Unit_Tests_FileUtills.h" #endif /* MSYS_HAVE_FILEUTILLS */ @@ -31,6 +33,10 @@ #include "Unit_Tests_Thread_Utils.h" #endif /* MSYS_HAVE_THREAD_UTILS */ +#ifdef MSYS_HAVE_RENDERING_SUBSYS +#include "Unit_Test_Rendering_Subsystem.h" +#endif /* MSYS_HAVE_RENDERING_SUBSYS */ + #ifdef MSYS_HAVE_COMMON_ERROR_HANDLER #include "Unit_Tests_Common_Error_Handler.h" #endif /* MSYS_HAVE_COMMON_ERROR_HANDLER */ diff --git a/src/Tests/Unit_Test_Core.h b/src/Tests/Unit_Test_Core.h index cae904d..9596795 100644 --- a/src/Tests/Unit_Test_Core.h +++ b/src/Tests/Unit_Test_Core.h @@ -24,8 +24,22 @@ // Include headers from Core. #include "../Core/Src/DataProcess.h" +/* The Unit_Tests_DataProcess_Main() function is a C function. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int Unit_Tests_DataProcess_Main(); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + int Unit_Test_Data_Object(); int Unit_Test_Data_Object_Insert_char(); +int Unit_Test_Data_Object_Insert_Replace_Overwrite_Char(); +int Unit_Test_Data_Object_Insert_Replace_Overwrite_Data_Object(); +int Unit_Test_Data_Object_Insert_Replace_Overwrite_C_Str(); #endif diff --git a/src/Tests/Unit_Test_DataProcess.c b/src/Tests/Unit_Test_DataProcess.c new file mode 100644 index 0000000..464fcdd --- /dev/null +++ b/src/Tests/Unit_Test_DataProcess.c @@ -0,0 +1,1076 @@ +/*! + Multiverse Engine Project 09/9/2015 Unit Tests Unit_Test_DataProcess.c + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Unit_Tests.h" + +/* Use C linkage. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*! + * Unit_Tests_DataProcess_TRNG() + * + * Tests the DataProcess_Trivial_Random_Number_Generator() function. + * Function should return at least one different value in both runs for the test to be considered successful. + * + * Note: The number of values per run can be controlled by the TEST_ARRAY_SIZE #define within the function. + * + * Note: The range of permissible values returned by the function can be controlled by the TEST_MIN_VAL and + * TEST_MAX_VAL #define(s) within the function, however the test code expects a result of zero (0) to be an error, + * so the range defined by TEST_MIN_VAL and TEST_MAX_VAL should NOT include zero (0) for it to work correctly. + * (The original range is 1 - 10000.) + * + * Note: The number of test runs performed by the function can be controlled by the TEST_NUM_OF_RUNS #define + * within the function, however the test code expects that at least two (2) runs will be made, so TEST_NUM_OF_RUNS + * must be greater than two (2) for the function to work correctly. + * + * Returns 0 if the test passes. + * Returns -1 if the initial function call does not return a valid value. + * Returns -2 if the TRNG function's seed could not be reset. + * Returns -3 if the output from the TRNG function for both runs was identical. + * Returns -9 if an unknown error occurs. + * Returns -10 if memory allocation fails. + */ +int Unit_Tests_DataProcess_TRNG() +{ +#define TEST_ARRAY_SIZE 10 /* Size of the test arrays, (also defines the number of calls per test run of the tested function.) */ +#define TEST_MIN_VAL 1 /* Minimal value that the test function may return. (Do NOT set this to zero the test code below will fail to work correctly if you do.) */ +#define TEST_MAX_VAL 10000 /* Maximum value that the test function may return. (This value should be bigger than TEST_MIN_VAL.) */ +#define TEST_NUM_OF_RUNS 10 /* Number of test runs to make. */ + + /* Init vars. */ + int ret = -9; /* Result of this test. */ + size_t retFromTRNG = 0; /* The result of the call to the TRNG. */ + size_t x = 0; /* Counter used in the outer for loops. */ + size_t y = 0; /* Counter used in the inner for loops. */ + size_t ** testArrays[TEST_NUM_OF_RUNS] = { NULL }; /* Holds the pointer to the test result arrays. */ + const char * ERR_MSG_INVALID_VAL = "TEST FAILURE: Unit_Tests_DataProcess_TRNG(): Call to DataProcess_Trivial_Random_Number_Generator() returned an invalid value.\n"; + const char * ERR_MSG_INVALID_VAL_RESET = "TEST FAILURE: Unit_Tests_DataProcess_TRNG(): Unable to reset TRNG, call to DataProcess_Trivial_Random_Number_Generator() returned an invalid value.\n"; + const char * STAT_MSG_RESET_TRNG = "TEST INFO: Unit_Tests_DataProcess_TRNG(): Resetting TRNG.\n"; + const char * STAT_MSG_BEGIN_RUN = "TEST INFO: Unit_Tests_DataProcess_TRNG(): Begining test run, please wait.\n"; + const char * STAT_MSG_CHECK_RETVALS = "TEST INFO: Unit_Tests_DataProcess_TRNG(): Checking results of the test runs to see if they do not match. Please wait.\n"; + const char * STAT_MSG_INITIAL_CALL = "TEST INFO: Unit_Tests_DataProcess_TRNG(): Makeing initial test call to DataProcess_Trivial_Random_Number_Generator(). Please Wait.\n"; + const char * STAT_MSG_TEST_PASS = "TEST INFO: Unit_Tests_DataProcess_TRNG(): TEST PASSED.\n"; + const char * STAT_MSG_TEST_FAIL = "TEST INFO: Unit_Tests_DataProcess_TRNG(): TEST FAILED, generated numbers were identical despite TRNG reset.\n"; + const char * ERR_MSG_MEM_ALLOC_FAIL = "TEST FAILURE: Unit_Tests_DataProcess_TRNG(): Unable to allocate memory for test.\n"; + const char * ERR_MSG_INVALID_PTR_TO_ARRAYS = "TEST FAILURE: Unit_Tests_DataProcess_TRNG(): Invalid pointer to result arrays.\n"; + const char * ERR_MSG_INVALID_ARRAY = "TEST FAILURE: Unit_Tests_DataProcess_TRNG(): Invalid result array pointer.\n"; + + /* Start test section. */ + printf("%s", START_TEST_SECTION); + + /* Run call. */ + printf("%s", STAT_MSG_INITIAL_CALL); + retFromTRNG = DataProcess_Trivial_Random_Number_Generator(TEST_MIN_VAL, TEST_MAX_VAL, false); + if ((retFromTRNG >= TEST_MIN_VAL) && (retFromTRNG <= TEST_MAX_VAL)) + { + /* Begin test run memory allocation outer loop. */ + printf("%s", STAT_MSG_BEGIN_RUN); + for (x = 0; ((x < TEST_NUM_OF_RUNS) && (ret == -9)); x++) + { + /* Allocate memory for test array pointer. */ + testArrays[x] = (size_t **)malloc(((sizeof(size_t *)) * TEST_ARRAY_SIZE)); + if (testArrays[x] != NULL) + { + /* Set the pointers to NULL, and allocate them. */ + memset(testArrays[x], 0, ((sizeof(size_t *)) * TEST_ARRAY_SIZE)); + + /* Allocate the test array values. */ + for (y = 0; ((y < TEST_ARRAY_SIZE) && (ret == -9)); y++) + { + (testArrays[x])[y] = (size_t *)malloc(sizeof(size_t)); + if ((testArrays[x])[y] != NULL) + { + /* Set the allocated value to zero. (0). */ + *((testArrays[x])[y]) = 0; + } + else + { + /* Memory allocation error. */ + ret = -10; + printf("%s", ERR_MSG_MEM_ALLOC_FAIL); + } + } + } + + /* Check for mem alloc success. */ + if (ret == -9) + { + /* Reset the TRNG. */ + printf("%s", STAT_MSG_RESET_TRNG); + retFromTRNG = DataProcess_Trivial_Random_Number_Generator(TEST_MIN_VAL, TEST_MAX_VAL, true); + if ((retFromTRNG >= TEST_MIN_VAL) && (retFromTRNG <= TEST_MAX_VAL)) + { + /* Begin test run inner loop. */ + for (y = 0; ((y < TEST_ARRAY_SIZE) && ((retFromTRNG >= TEST_MIN_VAL) && (retFromTRNG <= TEST_MAX_VAL))); y++) + { + retFromTRNG = DataProcess_Trivial_Random_Number_Generator(TEST_MIN_VAL, TEST_MAX_VAL, false); + if ((retFromTRNG >= TEST_MIN_VAL) && (retFromTRNG <= TEST_MAX_VAL)) + { + /* Copy the value to the array. */ + (*((testArrays[x])[y])) = retFromTRNG; + } + else + { + /* Test failure, invalid value. */ + ret = -1; + printf("%s", ERR_MSG_INVALID_VAL); + } + } + } + else + { + /* Could not reset the TRNG. */ + ret = -2; + printf("%s", ERR_MSG_INVALID_VAL_RESET); + } + } + } /* End of test run outer loop. */ + + /* Check for valid data. */ + if ((ret == -9) && (testArrays != NULL)) + { + /* Begin test result loop. */ + for (x = 0; (x < TEST_NUM_OF_RUNS); x++) + { + /* Check for NULL. */ + if (testArrays[x] == NULL) + { + /* Invalid test result arrays. */ + ret = -10; + printf("%s", ERR_MSG_INVALID_ARRAY); + } + } + + /* Check for success. */ + if (ret == -9) + { + /* Set failure code to bad RNG source. */ + ret = -3; + + /* A note about the verify loop structure: + * + * The outer loop runs through each value in the result array, + * and the inner loop loops through each result array. + * + * The reason why is that we are looking for a value to be different + * at some point in each array. I.e. We care about the order in which the + * values appear, not the values themselves. As long as at least one pair of + * values at the exact same position in the arrays do not match each other, + * the test is considered successful. + */ + /* Begin outer verify loop. (Loops through each value.) */ + for (y = 0; ((y < TEST_ARRAY_SIZE) && (ret == -3)); y++) + { + /* Begin inner verify loop. (Loops through each result array.) */ + for (x = 0; ((x < (TEST_NUM_OF_RUNS - 1)) && (ret == -3)); x++) + { + /* Check the results of each run to see if they match exactly, (they should not.) */ + if ((*((testArrays[x])[y])) != (*((testArrays[(x + 1)])[y]))) + { + /* Set ret to success. */ + ret = 0; + } + } + } + + /* Print out the arrays for verification. */ + printf("%s", "Random value table:\n|Run Number:|Random values:|\n"); + for (x = 0; (x < TEST_NUM_OF_RUNS); x++) + { + printf("|%u|", x); + for (y = 0; (y < TEST_ARRAY_SIZE); y++) + { + printf("%u ", (*((testArrays[x])[y]))); + } + printf("%s", "|\n"); + } + + /* Check for failure. */ + if (ret == 0) + { + printf("%s", STAT_MSG_TEST_PASS); + } + else + { + printf("%s", STAT_MSG_TEST_FAIL); + } + } + } + else + { + /* Invalid test result arrays. */ + ret = -10; + printf("%s", ERR_MSG_INVALID_PTR_TO_ARRAYS); + } + } + else + { + /* Test failure. */ + ret = -1; + printf("%s", ERR_MSG_INVALID_VAL); + } + + /* Deallocate memory. */ + for (x = 0; (x < TEST_NUM_OF_RUNS); x++) + { + /* Check for NULL. */ + if (testArrays[x] != NULL) + { + /* Deallocate the test array values. */ + for (y = 0; ((y < TEST_ARRAY_SIZE) && (ret == -9)); y++) + { + if ((testArrays[x])[y] != NULL) + { + free((testArrays[x])[y]); + (testArrays[x])[y] = NULL; + } + } + + /* Deallocate the pointer to the array. */ + free(testArrays[x]); + testArrays[x] = NULL; + } + } + + /* End test section. */ + printf("%s", END_TEST_SECTION); + + /* Return ret. */ + return ret; + +/* Run sanity checks on defines here, and abort build if they fail. */ +#if TEST_NUM_OF_RUNS < 2 +#error __FILE__ "Unit_Tests_DataProcess_TRNG(): TEST_NUM_OF_RUNS must be greater than one (1)." +#endif +#if TEST_MIN_VAL <= 0 +#error __FILE__ "Unit_Tests_DataProcess_TRNG(): TEST_MIN_VAL cannot be less than or equal to zero (0)." +#endif +#if (TEST_MAX_VAL <= TEST_MIN_VAL) +#error __FILE__ "Unit_Tests_DataProcess_TRNG(): TEST_MAX_VAL must be greater than TEST_MIN_VAL." +#endif + +#undef TEST_NUM_OF_RUNS +#undef TEST_MAX_VAL +#undef TEST_MIN_VAL +#undef TEST_ARRAY_SIZE +} + +/* Define TRNGUseMayFailMSG. */ +const static char * TRNGUseMayFailMSG = "The remainder of these tests rely on the DataProcess_Trivial_Random_Number_Generator() function to work correctly and may fail or give false results if that function does not work correctly. Therefore the results for the remainder of the test should only be considered valid if the TRNG function works correctly.\n\n"; + +/* Define some common error messages. */ +const static char * periodAndNewlineMSG = ".\n"; +const static char * errorCodeReturnedMSG = "The function returned error code: "; +const static char * errorSuccessNoResultMSG = "The function returned success without producing a result.\n"; +const static char * errorSamePtrMSG = "The function returned success, but the returned memory pointer is identical to the original one. (No actual allocation occured.)\n"; +const static char * InvalidArgStringPointerTestMSG = "Attempting to get COMMON_ERROR_INVALID_ARGUMENT error code by passing a NULL string pointer to "; +const static char * InvalidArgStringPointerFailMSG = "Unable to get COMMON_ERROR_INVALID_ARGUMENT error code by passing a NULL string pointer to "; +const static char * InvalidArgLengthPointerTestMSG = "Attempting to get COMMON_ERROR_INVALID_ARGUMENT error code by passing a NULL length pointer to "; +const static char * InvalidArgLengthPointerFailMSG = "Unable to get COMMON_ERROR_INVALID_ARGUMENT error code by passing a NULL length pointer to "; + +/*! + * int Unit_Tests_DataProcess_Random_String_Generator(char ** string, size_t * stringLength) + * + * Generates a c-string using printable ASCII characters for use by other testing functions. + * The generated string should be deallocated by DataProcess_Deallocate_CString() when it is + * no longer needed. + * + * Returns 0 if successful. + * Returns -1 if an argument pointer is NULL. + * Returns -2 if a random number could not be generated. + * Returns -3 if memory allocation fails. + * + * This function will not modifiy the given arguments if it's return code is not zero (0). + */ +int Unit_Tests_DataProcess_Random_String_Generator(char ** string, size_t * stringLength) +{ + /* Define the error messaging macros. */ +#define TEST_FAILURE_MSG_HEAD "TEST_FAILURE: Unit_Tests_DataProcess_Allocator_and_Deallocator(): " +#define TEST_ERROR_LOG_REAL(ERR_MSG) printf("%s", TEST_FAILURE_MSG_HEAD); printf("%s", ERR_MSG); +#define TEST_ERROR_LOG(ERR_MSG) TEST_ERROR_LOG_REAL(ERR_MSG) + + /* Define the range of ASCII values to use for the random string. */ +#define TEST_PRINTABLE_ASCII_START 33 +#define TEST_PRINTABLE_ASCII_END 126 + + /* Define the range used to select the length of the randomly generated string. */ +#define TEST_RANDOM_STRING_LENGTH_MINIMAL 3 +#define TEST_RANDOM_STRING_LENGTH_MAXIMUM 100 + + /* Init vars. */ + int ret = -999; /* Result of the tests. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* Result code from engine function. */ + size_t x = 0; /* Counter used in random string generation loop. */ + size_t randVal = 0; /* Result from the call to TRNG() function. */ + size_t randomLength = 0; /* Chosen length of the string to be generated. */ + char * randString = NULL; /* Temporary pointer used to create the random string. */ + + /* Check for invalid arguments. */ + if ((string != NULL) && (stringLength != NULL)) + { + /* Randomly generate a length for the string. */ + randomLength = DataProcess_Trivial_Random_Number_Generator(TEST_RANDOM_STRING_LENGTH_MINIMAL, TEST_RANDOM_STRING_LENGTH_MAXIMUM, true); + if (randomLength > 0) + { + /* Allocate memory for the TRNG String. */ + retFromCall = DataProcess_Reallocate_C_String(&randString, 0, randomLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (randString != NULL)) + { + /* Use the TRNG to generate the source string. (Note the last character in the string should be a NULL byte. + Which it is if the string was allocated by DataProcess_Reallocate_C_String().) + */ + for (x = 0; (x < (randomLength - 1)); x++) + { + randVal = DataProcess_Trivial_Random_Number_Generator(TEST_PRINTABLE_ASCII_START, TEST_PRINTABLE_ASCII_END, false); + randString[x] = (int)randVal; + } + + /* Copy the string pointer, and length value. */ + (*string) = randString; + (*stringLength) = (randomLength - 1); /* Omit NULL termination byte from string length. */ + + /* Done. */ + ret = 0; + } + else + { + /* Could not allocate memory? */ + ret = -3; + TEST_ERROR_LOG("Unable to allocate memory for random string.\n"); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, ".\n")) : + (printf("%s", errorSuccessNoResultMSG))); + } + } + else + { + /* Could not generate a random number. */ + ret = -2; + TEST_ERROR_LOG("Could not generate a random number.\n"); + } + } + else + { + /* Invalid arguments. */ + ret = -1; + TEST_ERROR_LOG("Invalid argument.\n"); + } + + /* Exit function. */ + return ret; + + /* Check for valid random string length. */ +#if TEST_RANDOM_STRING_LENGTH_MINIMAL < 3 +#error "Unit_Tests_DataProcess_Allocator_and_Deallocator(): TEST_RANDOM_STRING_LENGTH must be greater than two (2)." +#endif /* TEST_RANDOM_STRING_LENGTH < 3 */ + + /* Check for valid ASCII ranges. */ +#if TEST_PRINTABLE_ASCII_START >= TEST_PRINTABLE_ASCII_END +#error "Unit_Tests_DataProcess_Allocator_and_Deallocator(): TEST_PRINTABLE_ASCII_START must be less than TEST_PRINTABLE_ASCII_END." +#endif /* TEST_PRINTABLE_ASCII_START >= TEST_PRINTABLE_ASCII_END */ +#if TEST_PRINTABLE_ASCII_START < 1 +#error "Unit_Tests_DataProcess_Allocator_and_Deallocator(): TEST_PRINTABLE_ASCII_START must be a greater than or equal to one (1)." +#endif /* TEST_PRINTABLE_ASCII_START < 1 */ +#if TEST_PRINTABLE_ASCII_END < 2 +#error "Unit_Tests_DataProcess_Allocator_and_Deallocator(): TEST_PRINTABLE_ASCII_END must be a greater than or equal to two (2)." +#endif /* TEST_PRINTABLE_ASCII_END < 2 */ + + /* Undefine the macros. */ +#undef TEST_RANDOM_STRING_LENGTH_MAXIMUM +#undef TEST_RANDOM_STRING_LENGTH_MINIMAL +#undef TEST_PRINTABLE_ASCII_END +#undef TEST_PRINTABLE_ASCII_START +#undef TEST_ERROR_LOG +#undef TEST_ERROR_LOG_REAL +#undef TEST_FAILURE_MSG_HEAD +} + +/*! + * int Unit_Tests_DataProcess_Allocator_and_Deallocator() + * + * This function tests the DataProcess_Reallocate_C_String(), + * DataProcess_Reallocate_C_String_With_NULL_Terminator(), + * and DataProcess_Deallocate_CString() functions. + * + * Note: This test function depends on a working DataProcess_Trivial_Random_Number_Generator() + * function to work correctly, and may produce inaccurate results if + * DataProcess_Trivial_Random_Number_Generator() does not work correctly. + */ +int Unit_Tests_DataProcess_Allocator_and_Deallocator() +{ + /* Define the passed test message. */ +#define TEST_PASSED_MSG "Unit_Tests_DataProcess_Allocator_and_Deallocator(): TEST_PASSED" + + /* Define the error messaging macros. */ +#define TEST_FAILURE_MSG_HEAD "TEST_FAILURE: Unit_Tests_DataProcess_Allocator_and_Deallocator(): " +#define TEST_ERROR_LOG_REAL(ERR_MSG) printf("%s", TEST_FAILURE_MSG_HEAD); printf("%s", ERR_MSG); +#define TEST_ERROR_LOG(ERR_MSG) TEST_ERROR_LOG_REAL(ERR_MSG) + + /* Init vars. */ + int ret = -999; /* Result of the tests. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* Result code from engine function. */ + size_t x = 0; /* Counter used in random string generation loop. */ + size_t randVal = 0; /* Result from the call to TRNG() function. */ + size_t randStringLength = 0; /* Length of the random string. */ + char * currentString = NULL; /* The current string pointer. */ + char * previousString = NULL; /* The previous string pointer. */ + char * randString = NULL; /* A random string. */ + const char * byteAllocationTestMSG = "Attempting to allocate one (1) byte using DataProcess_Reallocate_C_String().\n"; + const char * byteAllocationFailMSG = "Unable to allocate one (1) byte. "; + const char * byteReallocationTestMSG = "Attempting to reallocate the byte using DataProcess_Reallocate_C_String().\n"; + const char * byteReallocationFailMSG = "Unable to reallocate the byte. "; + const char * byteDeallocationTestMSG = "Attempting to deallocate the byte using DataProcess_Deallocate_CString().\n"; + const char * byteDeallocationFailMSG = "Unable to deallocate the byte.\n"; + const char * useAsAllocatorTestMSG = "Attempting to use DataProcess_Reallocate_C_String() as a memory allocator.\n"; + const char * useAsAllocatorFailMSG = "Unable to use DataProcess_Reallocate_C_String() as a memory allocator.\n"; + const char * rangeTest1MSG = "Attempting to reallocate the following string < "; + const char * rangeTest2MSG = " > using only the first "; + const char * rangeTest3MSG = " bytes"; + const char * rangeFailMSG = "Unable to reallocate the following string < "; + const char * dataMismatch1FailMSG = "Copied string < "; + const char * dataMismatch2FailMSG = " > does not match the source string.\n"; + const char * reallocationWithNullTermTestMSG = "Attempting to reallocate the random string with a NULL termination byte using DataProcess_Reallocate_C_String_With_NULL_Terminator().\n"; + const char * reallocationWithNullTermFailMSG = "Unable to reallocate the random string with a NULL termination byte.\n"; + const char * reallocationWithNullTermFailInvalidSize1MSG = "Expected length of NULL terminated string was: "; + const char * reallocationWithNullTermFailInvalidSize2MSG = ". The returned length was: "; + const char * reallocationWithNullTermFailNoNullMSG = "The reallocated string is not null byte terminated."; + const char * reallocationWithPreExistingNullTermTestMSG = "Attempting to reallocate the random string using DataProcess_Reallocate_C_String_With_NULL_Terminator() with a NULL termination byte already present.\n"; + const char * reallocationWithPreExistingNullTermFailMSG = "Unable to reallocate the random string with a pre-existing NULL termination byte using DataProcess_Reallocate_C_String_With_NULL_Terminator().\n"; + const char * reallocateCStringFunctMSG = "DataProcess_Reallocate_C_String()"; + const char * reallocateCStringWithNullFunctMSG = "DataProcess_Reallocate_C_String_With_NULL_Terminator()"; + + /* Start test section. */ + printf("%s", START_TEST_SECTION); + + /* Attempt to allocate a small amount of memory. */ + printf("%s", byteAllocationTestMSG); + retFromCall = DataProcess_Reallocate_C_String(¤tString, 0, 1); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentString != NULL)) + { + /* Copy the pointer. */ + previousString = currentString; + + /* Now attempt to reallocate the string. */ + printf("%s", byteReallocationTestMSG); + retFromCall = retFromCall = DataProcess_Reallocate_C_String(¤tString, 1, 1); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentString != NULL) && (currentString != previousString)) + { + /* Set previousString to NULL. + (The string was deallocated by DataProcess_Reallocate_C_String() as it was a copy of currentString's pointer value.) + */ + previousString = NULL; + + /* Test the deallocation function. */ + printf("%s", byteDeallocationTestMSG); + DataProcess_Deallocate_CString(¤tString); + if (currentString == NULL) + { + /* Warn user about TRNG use. */ + printf("%s", TRNGUseMayFailMSG); + fflush(stdout); + + /* Create the TRNG String. */ + retFromCall = Unit_Tests_DataProcess_Random_String_Generator(&randString, &randStringLength); + if ((retFromCall == 0) && (randString != NULL) && (randStringLength > 0)) + { + /* Reset retFromCall. */ + retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + + /* Allocate memory to copy the random string into. */ + printf("%s", useAsAllocatorTestMSG); + retFromCall = DataProcess_Reallocate_C_String(¤tString, 0, randStringLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentString != NULL)) + { + /* Copy the random string, because reallocating it will loose data. */ + memcpy(currentString, randString, randStringLength); + + /* Copy the pointer. (To make sure a new allocation is made.) */ + previousString = currentString; + + /* Generate one final random number to determine how much of the random string should be copied. */ + randVal = DataProcess_Trivial_Random_Number_Generator(2, (randStringLength - 1), false); + + /* Check and see if giving a range of the source string outputs the correct sub-string. */ + printf("%s%s%s%i%s%s", rangeTest1MSG, randString, rangeTest2MSG, randVal, rangeTest3MSG, periodAndNewlineMSG); + retFromCall = DataProcess_Reallocate_C_String(¤tString, randStringLength, randVal); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentString != previousString) && (currentString != randString)) + { + /* Set previousString to NULL. + (The string was deallocated by DataProcess_Reallocate_C_String() as it was a copy of currentString's pointer value.) + */ + previousString = NULL; + + /* Begin verification loop. */ + for (x = 0; ((x < randVal) && (x < randStringLength)); x++) + { + /* Check for identical data in both strings. */ + if (randString[x] != currentString[x]) + { + /* Data mismatch. */ + break; + } + } + + /* Check result of verification loop. */ + if ((x == randVal) || (x == randStringLength)) + { + /* Test the DataProcess_Reallocate_C_String_With_NULL_Terminator() function by setting the + last byte of the currentString to a non-zero value. + */ + if (currentString[(randVal - 1)] == 0x0) + { + currentString[(randVal - 1)] = 0x1; + } + + /* Copy the string pointer. (To verifiy a new allocation was made.) */ + previousString = currentString; + + /* Abuse x to store the current random value. */ + x = randVal; + + /* Now reallocate the string. */ + printf("%s", reallocationWithNullTermTestMSG); + retFromCall = DataProcess_Reallocate_C_String_With_NULL_Terminator(¤tString, randVal, &x); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentString != NULL) && (currentString != previousString) && + (currentString[(randVal)] == '\0')) + { + /* Set previousString to NULL. + (The string was deallocated by DataProcess_Reallocate_C_String() as it was a copy of currentString's pointer value.) + */ + previousString = NULL; + + /* Begin verification loop. */ + for (x = 0; ((x < randVal) && (x < randStringLength)); x++) + { + /* Check for identical data in both strings. */ + if (randString[x] != currentString[x]) + { + /* Data mismatch. */ + break; + } + } + + /* Check result of verification loop. */ + if ((x == randVal) || (x == randStringLength)) + { + /* Copy the pointer. */ + previousString = currentString; + + /* Recall DataProcess_Reallocate_C_String_With_NULL_Terminator() with a NULL'd string. + The size of the string should not change in this case. + */ + printf("%s", reallocationWithPreExistingNullTermTestMSG); + retFromCall = DataProcess_Reallocate_C_String_With_NULL_Terminator(¤tString, randVal, &x); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentString != NULL) && (currentString != previousString) && + ((randVal < randStringLength) ? (currentString[(randVal)] == '\0') : (currentString[(randStringLength + 1)]))) + { + /* Set previousString to NULL. + (The string was deallocated by DataProcess_Reallocate_C_String() as it was a copy of currentString's pointer value.) + */ + previousString = NULL; + + /* Begin verification loop. */ + for (x = 0; ((x < randVal) && (x < randStringLength)); x++) + { + /* Check for identical data in both strings. */ + if (randString[x] != currentString[x]) + { + /* Data mismatch. */ + break; + } + } + + /* Check result of verification loop. */ + if ((x == randVal) || (x == randStringLength)) + { + /* Test for invalid argument caused by invalid pointer to pointer. */ + printf("%s%s%s", InvalidArgStringPointerTestMSG, reallocateCStringFunctMSG, periodAndNewlineMSG); + retFromCall = DataProcess_Reallocate_C_String(NULL, 0, x); + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test for invalid argument caused by invalid pointer to pointer. */ + printf("%s%s%s", InvalidArgStringPointerTestMSG, reallocateCStringWithNullFunctMSG, periodAndNewlineMSG); + retFromCall = DataProcess_Reallocate_C_String_With_NULL_Terminator(NULL, 0, &x); + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test for invalid argument caused by invalid pointer to pointer. */ + printf("%s%s%s", InvalidArgLengthPointerTestMSG, reallocateCStringWithNullFunctMSG, periodAndNewlineMSG); + retFromCall = DataProcess_Reallocate_C_String_With_NULL_Terminator(¤tString, 0, NULL); + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test successful. */ + printf("%s%s", TEST_PASSED_MSG, periodAndNewlineMSG); + ret = 0; + } + else + { + /* Did not get COMMON_ERROR_INVALID_ARGUMENT error code from () due to invalid newLength pointer. */ + ret = -14; + printf("%s%s%s", InvalidArgLengthPointerFailMSG, reallocateCStringWithNullFunctMSG, periodAndNewlineMSG); + } + } + else + { + /* Did not get COMMON_ERROR_INVALID_ARGUMENT error code from () due to invalid string pointer. */ + ret = -13; + printf("%s%s%s", InvalidArgStringPointerFailMSG, reallocateCStringWithNullFunctMSG, periodAndNewlineMSG); + } + } + else + { + /* Did not get COMMON_ERROR_INVALID_ARGUMENT error code from DataProcess_Reallocate_C_String(). */ + ret = -12; + printf("%s%s%s", InvalidArgStringPointerFailMSG, reallocateCStringFunctMSG, periodAndNewlineMSG); + } + } + else + { + /* Reallocation with NULL byte failed. Data Mismatch. */ + ret = -11; + TEST_ERROR_LOG(reallocationWithPreExistingNullTermFailMSG); + printf("%s%s%s", dataMismatch1FailMSG, currentString, dataMismatch2FailMSG); + } + } + else + { + /* Reallocation with NULL byte failed. */ + ret = -10; + TEST_ERROR_LOG(reallocationWithPreExistingNullTermFailMSG); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, ".\n")) : + (currentString == NULL) ? (printf("%s", errorSuccessNoResultMSG)) : + (currentString == previousString) ? (printf("%s", errorSamePtrMSG)) : + (printf(reallocationWithNullTermFailNoNullMSG))); + } + } + else + { + /* Reallocation with NULL byte failed. Data Mismatch. */ + ret = -9; + TEST_ERROR_LOG(reallocationWithNullTermFailMSG); + printf("%s%s%s", dataMismatch1FailMSG, currentString, dataMismatch2FailMSG); + } + } + else + { + /* Reallocation with NULL byte failed. */ + ret = -8; + TEST_ERROR_LOG(reallocationWithNullTermFailMSG); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, ".\n")) : + (currentString == NULL) ? (printf("%s", errorSuccessNoResultMSG)) : + (currentString == previousString) ? (printf("%s", errorSamePtrMSG)) : + (x != randVal) ? (printf("%s%i%s%i%s" , reallocationWithNullTermFailInvalidSize1MSG, randVal, + reallocationWithNullTermFailInvalidSize2MSG, x, periodAndNewlineMSG)) : (printf(reallocationWithNullTermFailNoNullMSG))); + } + } + else + { + /* Error data mismatch. */ + ret = -7; + TEST_ERROR_LOG(rangeFailMSG); + printf("%s%s%i%s%s%s%s%s", randString, rangeTest2MSG, randVal, rangeTest3MSG, periodAndNewlineMSG, dataMismatch1FailMSG, currentString, dataMismatch2FailMSG); + } + } + else + { + /* Could not complete range test. */ + ret = -6; + TEST_ERROR_LOG(rangeFailMSG); + printf("%s%s%i%s", randString, rangeTest2MSG, randVal, rangeTest3MSG); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, ".\n")) : + (currentString == NULL) ? (printf("%s", errorSuccessNoResultMSG)) : (printf("%s", errorSamePtrMSG))); + } + } + else + { + /* Could not allocate memory? */ + ret = -5; + TEST_ERROR_LOG(useAsAllocatorFailMSG); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, ".\n")) : + (printf("%s", errorSuccessNoResultMSG))); + } + } + else + { + /* Could not allocate memory? */ + ret = -4; + TEST_ERROR_LOG(useAsAllocatorFailMSG); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, ".\n")) : + (printf("%s", errorSuccessNoResultMSG))); + } + } + else + { + /* Could not deallocate the byte. */ + ret = -3; + TEST_ERROR_LOG(byteDeallocationFailMSG); + } + } + else + { + /* Could not reallocate the byte. */ + ret = -2; + TEST_ERROR_LOG(byteReallocationFailMSG); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, ".\n")) : + ((currentString != NULL) ? (printf("%s", errorSuccessNoResultMSG)) : (printf("%s", errorSamePtrMSG)))); + } + + /* Flush output buffer. */ + fflush(stdout); + + /* Make sure to release memory if needed. */ + if (currentString != NULL) + { + DataProcess_Deallocate_CString(¤tString); + } + if (previousString != NULL) + { + DataProcess_Deallocate_CString(&previousString); + } + if (randString != NULL) + { + DataProcess_Deallocate_CString(&randString); + } + } + else + { + /* Could not allocate a byte? */ + ret = -1; + TEST_ERROR_LOG(byteAllocationFailMSG); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, ".\n")) : + (printf("%s", errorSuccessNoResultMSG))); + } + + /* End test section. */ + printf("%s", END_TEST_SECTION); + + /* Exit function. */ + return ret; + + /* Undefine the macros. */ +#undef TEST_ERROR_LOG +#undef TEST_ERROR_LOG_REAL +#undef TEST_FAILURE_MSG_HEAD +#undef TEST_PASSED_MSG +} + +/*! + * int Unit_Tests_DataProcess_sizet_cstring_converter_string_verification_number_conversion_function() + * + * Takes a given number between zero (0) and ten (10) and returns it's arabic numeral text character. + * + * If the given number is outside the range of zero (0) and ten (10) then -1 is returned. + */ +int Unit_Tests_DataProcess_sizet_cstring_converter_string_verification_number_conversion_function(const size_t number) +{ + /* Define numbersLength. */ +#define NUMBERSLENGTH 11 + + /* Init vars. */ + int ret = -1; /* The result of this function. */ + const char numbers[NUMBERSLENGTH] = {"0123456789"}; /* Array to contain the text versions of the given numbers. */ + + /* Check given number argument. */ + if ((0 <= number) && (number < 10)) + { + /* Get the number so we can return it. */ + ret = (char)(numbers[number]); + } + + /* Exit function. */ + return ret; + + /* Undef NUMBERSLENGTH. */ +#undef NUMBERSLENGTH +} + +/*! + * int Unit_Tests_DataProcess_sizet_cstring_converter_string_verification_function(const size_t randomNumber, const char * string, const size_t stringLength) + * + * Ok, the purpose of this function is to verify that a generated string matches the number it was made from. + * + * To that extent, we wind up reimplimenting the functionality of DataProcess_getCStringFromSizeT(), + * but instead of creating a string we are checking it for accuracy. + */ +int Unit_Tests_DataProcess_sizet_cstring_converter_string_verification_function(const size_t randomNumber, const char * string, const size_t stringLength) +{ + /* Define the error messaging macros. */ +#define TEST_FAILURE_MSG_HEAD "TEST_FAILURE: Unit_Tests_DataProcess_sizet_cstring_converter_string_verification_function(): " +#define TEST_ERROR_LOG_REAL(ERR_MSG) printf("%s", TEST_FAILURE_MSG_HEAD); printf("%s", ERR_MSG); +#define TEST_ERROR_LOG(ERR_MSG) TEST_ERROR_LOG_REAL(ERR_MSG) + + /* Define the numeric base of DataProcess_getCStringFromSizeT(). */ +#define TEST_NUMERIC_BASE 10 + + /* Init vars. */ + char cNumber = '0'; /* Used to hold the current number we are checking for in the given string. */ + int ret = -1; /* The result of this function. */ + size_t currentValue = 0; /* The current number being verified. */ + size_t x = 0; /* Counter used in verififcation loop. */ + + /* Check for invalid arguments. */ + if ((string != NULL) && (stringLength > 0)) + { + /* Set current value. */ + currentValue = randomNumber; + + /* Begin verification loop. */ + for (x = 0; ((ret == -1) && (x < (stringLength - 1)) && (((stringLength - 2) - x) >= 0) && (currentValue > 0)); x++) + { + /* Devide off the last digit and convert it to a text character. */ + cNumber = Unit_Tests_DataProcess_sizet_cstring_converter_string_verification_number_conversion_function((currentValue % TEST_NUMERIC_BASE)); + + /* Check the text character for a match in the given string. */ + if (string[((stringLength - 2) - x)] == cNumber) + { + /* Proceed to the next value in the string. */ + currentValue /= TEST_NUMERIC_BASE; + } + else + { + /* Invalid text string. */ + ret = -3; + } + } + + /* Check counter value for success. */ + if ((ret == -1) && (x == (stringLength - 1))) + { + /* String matches the number. */ + ret = 0; + } + else + { + /* Invalid string. */ + ret = -3; + } + } + else + { + /* Invalid argument. */ + ret = -2; + TEST_ERROR_LOG("Invalid argument.\n"); + } + + /* Exit function. */ + return ret; + + /* Undef macros. */ +#undef TEST_NUMERIC_BASE +#undef TEST_ERROR_LOG +#undef TEST_ERROR_LOG_REAL +#undef TEST_FAILURE_MSG_HEAD +} + +/*! + * int Unit_Tests_DataProcess_sizet_cstring_converter() + * + * This function tests the DataProcess_getCStringFromSizeT() function. + */ +int Unit_Tests_DataProcess_sizet_cstring_converter() +{ + /* Define the name of the function. */ +#define MSYS_FUNCT_NAME "Unit_Tests_DataProcess_sizet_cstring_converter()" +#define MSYS_TESTING_FUNCT_NAME "DataProcess_getCStringFromSizeT()" + + /* Define the passed test message. */ +#define TEST_PASSED_MSG "Unit_Tests_DataProcess_sizet_cstring_converter(): TEST_PASSED" + + /* Define the error messaging macros. */ +#define TEST_FAILURE_MSG_HEAD "TEST_FAILURE: Unit_Tests_DataProcess_sizet_cstring_converter(): " +#define TEST_ERROR_LOG_REAL(ERR_MSG) printf("%s", TEST_FAILURE_MSG_HEAD); printf("%s", ERR_MSG); +#define TEST_ERROR_LOG(ERR_MSG) TEST_ERROR_LOG_REAL(ERR_MSG) + + /* Define the limits on the random number range. (MINIMAL value should be less than the MAXIMUM value and both should be positive.) */ +#define TEST_MINIMAL_RANDOM_NUMBER_VALUE 1 +#define TEST_MAXIMUM_RANDOM_NUMBER_VALUE 100 + + /* Init vars. */ + int ret = 0; /* The result of this test function. */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; /* The result of a call to an engine function. */ + size_t randVal = 0; /* Used to generate a random number to use with DataProcess_getCStringFromSizeT(). */ + size_t currentStringLength = 0; /* The length of the currentString.... string. */ + char * currentString = NULL; /* The current string pointer. */ + char * previousString = NULL; /* The previous string pointer. */ + + /* Start test section. */ + printf("%s", START_TEST_SECTION); + + /* Warn user about TRNG use. */ + printf("%s", TRNGUseMayFailMSG); + fflush(stdout); + + /* Generate a random number. */ + printf("%s%s", MSYS_FUNCT_NAME, " Attempting to generate a random number.\n"); + randVal = DataProcess_Trivial_Random_Number_Generator(TEST_MINIMAL_RANDOM_NUMBER_VALUE, TEST_MAXIMUM_RANDOM_NUMBER_VALUE, true); + if (randVal != 0) + { + /* Attempt to generate the string version of the random number. */ + printf("%s%s%i%s", MSYS_FUNCT_NAME, " Attempting to generate the string version of the random number: <", randVal, ">.\n"); + retFromCall = DataProcess_getCStringFromSizeT(randVal, ¤tString, ¤tStringLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentString != NULL) && (currentStringLength > 0)) + { + /* Check the result.... */ + printf("%s%s", MSYS_FUNCT_NAME, " checking result.\n"); + ret = Unit_Tests_DataProcess_sizet_cstring_converter_string_verification_function(randVal, currentString, currentStringLength); + if (ret == 0) + { + /* Copy the pointer. */ + previousString = currentString; + + /* Reset currentStringLength. */ + currentStringLength = 0; + + /* Check and see if the function will overwrite the given pointer. */ + printf("%s%s%s%s", MSYS_FUNCT_NAME, " checking to see if the given string pointer will be overwritten by ", MSYS_TESTING_FUNCT_NAME, periodAndNewlineMSG); + retFromCall = DataProcess_getCStringFromSizeT(randVal, ¤tString, ¤tStringLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (currentString != NULL) && (currentString != previousString) && (currentStringLength > 0)) + { + /* Deallocate the strings. */ + DataProcess_Deallocate_CString(&previousString); + DataProcess_Deallocate_CString(¤tString); + currentStringLength = 0; + + /* Check for INVALID_ARGUMENT error code if DataProcess_getCStringFromSizeT() is given a bad string pointer. */ + printf("%s%s%s", InvalidArgStringPointerTestMSG, MSYS_TESTING_FUNCT_NAME, periodAndNewlineMSG); + retFromCall = DataProcess_getCStringFromSizeT(randVal, NULL, ¤tStringLength); + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* Check for INVALID_ARGUMENT error code if DataProcess_getCStringFromSizeT() is given a bad stringLength pointer. */ + printf("%s%s%s", InvalidArgLengthPointerTestMSG, MSYS_TESTING_FUNCT_NAME, periodAndNewlineMSG); + retFromCall = DataProcess_getCStringFromSizeT(randVal, ¤tString, NULL); + if (retFromCall == COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test successful. */ + printf("%s%s", TEST_PASSED_MSG, periodAndNewlineMSG); + ret = 0; + } + else + { + /* Did not get invalid argument error code for bad string length pointer. */ + ret = -6; + TEST_ERROR_LOG(InvalidArgLengthPointerFailMSG); + printf("%s%s", MSYS_TESTING_FUNCT_NAME, periodAndNewlineMSG); + printf("%s%i%s", errorCodeReturnedMSG, retFromCall, periodAndNewlineMSG); + } + } + else + { + /* Did not get invalid argument error code for bad string pointer. */ + ret = -5; + TEST_ERROR_LOG(InvalidArgStringPointerFailMSG); + printf("%s%s", MSYS_TESTING_FUNCT_NAME, periodAndNewlineMSG); + printf("%s%i%s", errorCodeReturnedMSG, retFromCall, periodAndNewlineMSG); + } + } + else + { + /* Test of overwriting the given pointer failed. */ + ret = -4; + TEST_ERROR_LOG("overwriting the given pointer failed.\n"); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, periodAndNewlineMSG)) : + (currentString == previousString) ? (printf("%s", errorSamePtrMSG)) : + (printf("%s", errorSuccessNoResultMSG))); + } + } + else + { + /* Verification of string failed. */ + ret = -3; + TEST_ERROR_LOG("Verification of string failed.\n"); + printf("%s%i%s%s%s", "The random number <", randVal, "> does not match the generated string <", currentString, ">.\n"); + } + + /* Deallocate the result if needed. */ + if (currentString != NULL) + { + DataProcess_Deallocate_CString(¤tString); + currentStringLength = 0; + } + } + else + { + /* Could not generate c-string. */ + ret = -2; + TEST_ERROR_LOG("Could not generate c-string.\n"); + ((retFromCall != COMMON_ERROR_SUCCESS) ? (printf("%s%i%s", errorCodeReturnedMSG, retFromCall, periodAndNewlineMSG)) : + (printf("%s", errorSuccessNoResultMSG))); + } + } + else + { + /* Could not generate a random number. */ + ret = -1; + TEST_ERROR_LOG("Could not generate a random number.\n"); + } + + /* End test section. */ + printf("%s", END_TEST_SECTION); + + /* Flush output buffer. */ + fflush(stdout); + + /* Exit function. */ + return ret; + + /* Check for invalid random number range. */ +#if TEST_MINIMAL_RANDOM_NUMBER_VALUE < 1 +#error "Unit_Tests_DataProcess_sizet_cstring_converter(): TEST_MINIMAL_RANDOM_NUMBER_VALUE must be a greater than or equal to one (1)." +#endif /* TEST_MINIMAL_RANDOM_NUMBER_VALUE < 1 */ +#if TEST_MAXIMUM_RANDOM_NUMBER_VALUE < 2 +#error "Unit_Tests_DataProcess_sizet_cstring_converter(): TEST_MAXIMUM_RANDOM_NUMBER_VALUE must be a greater than or equal to two (2)." +#endif /* TEST_MAXIMUM_RANDOM_NUMBER_VALUE < 2 */ + + /* Undefine the macros. */ +#undef TEST_MAXIMUM_RANDOM_NUMBER_VALUE +#undef TEST_MINIMAL_RANDOM_NUMBER_VALUE +#undef TEST_ERROR_LOG +#undef TEST_ERROR_LOG_REAL +#undef TEST_FAILURE_MSG_HEAD +#undef TEST_PASSED_MSG +#undef MSYS_TESTING_FUNCT_NAME +#undef MSYS_FUNCT_NAME +} + +int Unit_Tests_DataProcess_Main() +{ + /* Init vars. */ + int ret = 0; /* Result of the tests. */ + int retFromTRNGTest = 0; /* Result of the TRNG test. */ + int retFromAllocatorTest = 0; /* Result of the allocator and deallocator tests. */ + int retFromSizeTCStringConversionTest = 0; /* Result of the size_t to c-string conversion tests. */ + + /* Begin tests for DataProcess_Trivial_Random_Number_Generator(). */ + retFromTRNGTest = Unit_Tests_DataProcess_TRNG(); + + /* Begin tests for DataProcess_Reallocate_C_String() and DataProcess_Deallocate_CString(). */ + retFromAllocatorTest = Unit_Tests_DataProcess_Allocator_and_Deallocator(); + + /* Begin tests for DataProcess_getCStringFromSizeT(). */ + retFromSizeTCStringConversionTest = Unit_Tests_DataProcess_sizet_cstring_converter(); + + /* Return ret. */ + return ret; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ diff --git a/src/Tests/Unit_Test_Data_Object.cpp b/src/Tests/Unit_Test_Data_Object.cpp index 43ba43c..33714e0 100644 --- a/src/Tests/Unit_Test_Data_Object.cpp +++ b/src/Tests/Unit_Test_Data_Object.cpp @@ -1,25 +1,29 @@ /*! Multiverse Engine Project 21/8/2012 Unit Tests Unit_Test_Data_Object.cpp - + Copyright (C) 2013 Multiverse Engine Project This program is free software; - you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along with this program; + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + Official source repository and project information can be found at https://github.com/codebase7/mengine */ +/* Internal includes. */ #include "Unit_Tests.h" +/* Special include for manually creating invalid MSYS_DataObject structures. (DON'T DO THIS AT HOME KIDS. Use the factories and public functions instead!)*/ +#include "../Core/Src/Data_Object/Data_Object_Private_Structure.c" + int Unit_Test_Data_Object() { /*! @@ -46,11 +50,17 @@ int Unit_Test_Data_Object() -1013 DataProcess::Data_Object::operator= char invalid char data after call to operator= char. -1014 DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. -1015 DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. - -1016 DataProcess::Data_Object::Compare() Comparasion failed should have passed. - -1017 DataProcess::Data_Object::NCompare() Comparasion passed should have failed. - -1018 DataProcess::Data_Object::Compare() Comparasion passed should have failed. - -1019 DataProcess::Data_Object::NCompare() Comparasion failed should have passed. - -1020 DataProcess::Data_Object::insert() Could not insert data (returned 0). + -1016 DataProcess::Data_Object::Data_Compare() Comparasion failed should have passed. + -1017 DataProcess::Data_Object::Data_NCompare() Comparasion passed should have failed. + -1018 DataProcess::Data_Object::Data_Compare() Comparasion passed should have failed. + -1019 DataProcess::Data_Object::Data_NCompare() Comparasion failed should have passed. + -1020 DataProcess::Data_Object::Compare() Comparasion failed should have passed. + -1021 DataProcess::Data_Object::NCompare() Comparasion passed should have failed. + -1022 DataProcess::Data_Object::Compare() Comparasion passed should have failed. + -1023 DataProcess::Data_Object::NCompare() Comparasion failed should have passed. + -1024 DataProcess::Data_Object::insert() Could not insert data (returned 0). + -1025 DataProcess::Data_Object::Shallow_Copy() failed. + -1026 DataProcess::Data_Object::Shallow_Copy() made a deep copy of the source object. -9001 Could not get pointer to string object. @@ -64,12 +74,27 @@ int Unit_Test_Data_Object() operator= (char) operator= (std::string) operator= (DataProcess::Data_Object) - Compare() - NCompare() + Compare() + NCompare() + Data_Compare() + Data_NCompare() reserve() operator+= (char) operator+= (std::string) insert (char) + insert (std::string) + insert (DataProcess::Data_Object) + insert_No_Allocation (char) + insert_No_Allocation (std::string) + insert_No_Allocation (DataProcess::Data_Object) + replace (char) + replace (std::string) + replace (DataProcess::Data_Object) + overwrite (char) + overwrite (std::string) + overwrite (DataProcess::Data_Object) + Get_Byte() + Set_Byte() Need to be added. operator+= (DataProcess::Data_Object) @@ -78,8 +103,6 @@ int Unit_Test_Data_Object() get_length() reset() substr() - insert (std::string) - insert (DataProcess::Data_Object) */ // Test data objects. @@ -95,6 +118,37 @@ int Unit_Test_Data_Object() const char * p1 = NULL; const char * p2 = NULL; + /* Result from Insert * tests. */ + int ret_Insert_Char = 0; + int ret_Insert_DO = 0; + int ret_Insert_CStr = 0; + + char byte = '\0'; /* For testing the Get / Set Byte functions. */ + + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + + MSYS_DataObject_T * test_object_1_c_ptr = NULL; + + void * pbadPrivPtr = NULL; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badPrivPtr = { &pbadPrivPtr }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badDataPriv = { NULL, 1, 1, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badLengthPriv = { "Bad_STR", SIZE_MAX, 7, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badCapacityPriv = { "Bad_STR", 7, 3, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadData = &badDataPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadLength = &badLengthPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadCapacity = &badCapacityPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badData = { &pbadData }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badLength = { &pbadLength }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badCapacity = { &pbadCapacity }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + + /* Get the pointers for the C object. */ + retFromCall = test_object_1.get_C_Struct(&test_object_1_c_ptr); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Could not get test_object C struct pointer. */ + return -1; + } + // First test DataProcess::Data_Object. /* @@ -800,15 +854,15 @@ int Unit_Test_Data_Object() } /* - Compare test 1 + Data Compare test 1 - This test checks the DataProcess::Data_Object::Compare() and DataProcess::Data_Object::NCompare() functions + This test checks the DataProcess::Data_Object::Data_Compare() and DataProcess::Data_Object::Data_NCompare() functions to see if they return correct values for equal DataProcess::Data_Objects. This test uses s1. - The result should be Compare() = true, - NCompare() = false. + The result should be Data_Compare() = true, + Data_NCompare() = false. */ // Clear both test objects. @@ -907,17 +961,17 @@ int Unit_Test_Data_Object() } } - // Now run the compare function. - if (!(test_object_1.Compare(test_object_2))) + // Now run the Data Compare function. + if (!(test_object_1.Data_Compare(test_object_2))) { - // DataProcess::Data_Object::Compare() Comparasion failed should have passed. + // DataProcess::Data_Object::Data_Compare() Comparasion failed should have passed. return -1016; } - // Now run the NCompare function. - if (test_object_1.NCompare(test_object_2)) + // Now run the Data NCompare function. + if (test_object_1.Data_NCompare(test_object_2)) { - // DataProcess::Data_Object::NCompare() Comparasion passed should have failed. + // DataProcess::Data_Object::Data_NCompare() Comparasion passed should have failed. return -1017; } @@ -939,15 +993,15 @@ int Unit_Test_Data_Object() /* - Compare test 2 + Data Compare test 2 - This test checks the DataProcess::Data_Object::Compare() and DataProcess::Data_Object::NCompare() functions + This test checks the DataProcess::Data_Object::Data_Compare() and DataProcess::Data_Object::Data_NCompare() functions to see if they return correct values for non-equal DataProcess::Data_Objects. This test uses s2 and s3. - The result should be Compare() = false, - NCompare() = true. + The result should be Data_Compare() = false, + Data_NCompare() = true. */ // Clear both test objects. @@ -1046,18 +1100,158 @@ int Unit_Test_Data_Object() } } - // Now run the compare function. - if (test_object_1.Compare(test_object_2)) + // Now run the Data Compare function. + if (test_object_1.Data_Compare(test_object_2)) { - // DataProcess::Data_Object::Compare() Comparasion passed should have failed. + // DataProcess::Data_Object::Data_Compare() Comparasion passed should have failed. return -1018; } + // Now run the Data NCompare function. + if (!(test_object_1.Data_NCompare(test_object_2))) + { + // DataProcess::Data_Object::Data_NCompare() Comparasion failed should have passed. + return -1019; + } + + // Clear both test objects. + test_object_1.clear(); + test_object_2.clear(); + + // Make sure that both test_object_1 and test_object_2 are empty. + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* + Compare test 1 + + This test checks the DataProcess::Data_Object::Compare() and DataProcess::Data_Object::NCompare() functions + to see if they return correct values for deep copied DataProcess::Data_Objects. + + This test uses s1. + + (The assignment operator creates a deep copy. So the Compare() and NCompare() functions should fail.) + + The result should be Compare() = false, + NCompare() = true. + */ + + // Clear both test objects. + test_object_1.clear(); + test_object_2.clear(); + + // Make sure that both test_object_1 and test_object_2 are empty. + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + // Set both test_objects to s1. + test_object_1 = s1; + test_object_2 = s1; + + // Make sure that test_object_1's size and capacity are equal to s1. + if (test_object_1.size() != s1.size()) + { + // DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. + return -1014; + } + if (test_object_1.get_Capacity() != s1.size()) + { + // DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. + return -1015; + } + + // Get pointer for test_object_1. + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + // DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. + return -1003; + } + + // Get pointer to string. + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + // Could not get pointer to string object. + return -9001; + } + + // Make sure that test_object_1 actually contains the correct data. + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + // DataProcess::Data_Object (Manual compare) Invalid data in the data_object. + return -1004; + } + } + + // Make sure that test_object_2's size and capacity are equal to s1. + if (test_object_2.size() != s1.size()) + { + // DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. + return -1014; + } + if (test_object_2.get_Capacity() != s1.size()) + { + // DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. + return -1015; + } + + // Get pointer for test_object_2. + p1 = NULL; + if ((p1 = test_object_2.get_Pointer()) == NULL) + { + // DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. + return -1003; + } + + // Get pointer to string. + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + // Could not get pointer to string object. + return -9001; + } + + // Make sure that test_object_2 actually contains the correct data. + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + // DataProcess::Data_Object (Manual compare) Invalid data in the data_object. + return -1004; + } + } + + // Now run the Compare function. + if (test_object_1.Compare(test_object_2)) + { + // DataProcess::Data_Object::Compare() Comparasion passed and should have failed. + return -1022; + } + // Now run the NCompare function. if (!(test_object_1.NCompare(test_object_2))) { - // DataProcess::Data_Object::NCompare() Comparasion failed should have passed. - return -1019; + // DataProcess::Data_Object::NCompare() Comparasion failed and should have passed. + return -1023; } // Clear both test objects. @@ -1076,18 +1270,769 @@ int Unit_Test_Data_Object() return -1006; } - // Tell User we are starting the insert test for (char). - cout << "DataProcess::Data_Object::insert() (char): "; - if (Unit_Test_Data_Object_Insert_char() == 0) + /* + Compare test 2 + + This test checks the DataProcess::Data_Object::Compare() and DataProcess::Data_Object::NCompare() functions + to see if they return correct values for shallow copied DataProcess::Data_Objects. + + This test uses s1. + + The result should be Compare() = true, + NCompare() = false. + */ + + // Clear both test objects. + test_object_1.clear(); + test_object_2.clear(); + + // Make sure that both test_object_1 and test_object_2 are empty. + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + // Set test_object_1 to s1. + test_object_1 = s1; + + // Shallow copy test_object_1 to test_object_2 + if (test_object_2.Shallow_Copy(test_object_1) != COMMON_ERROR_SUCCESS) + { + /* Shallow copy attempt failed. */ + return -1025; + } + + // Make sure that test_object_1's size and capacity are equal to s1. + if (test_object_1.size() != s1.size()) + { + // DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. + return -1014; + } + if (test_object_1.get_Capacity() != s1.size()) + { + // DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. + return -1015; + } + + // Get pointer for test_object_1. + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + // DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. + return -1003; + } + + // Get pointer to string. + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + // Could not get pointer to string object. + return -9001; + } + + // Make sure that test_object_1 actually contains the correct data. + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + // DataProcess::Data_Object (Manual compare) Invalid data in the data_object. + return -1004; + } + } + + // Make sure that test_object_2's size and capacity are equal to s1. + if (test_object_2.size() != s1.size()) + { + // DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. + return -1014; + } + if (test_object_2.get_Capacity() != s1.size()) + { + // DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. + return -1015; + } + + // Get pointer for test_object_2. + p1 = NULL; + if ((p1 = test_object_2.get_Pointer()) == NULL) + { + // DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. + return -1003; + } + + // Get pointer to string. + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + // Could not get pointer to string object. + return -9001; + } + + // Make sure that test_object_2 actually contains the correct data. + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + // DataProcess::Data_Object (Manual compare) Invalid data in the data_object. + return -1004; + } + } + + // Get pointer for test_object_1. + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + // DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. + return -1003; + } + + // Get pointer for test_object_2. + p2 = NULL; + if ((p2 = test_object_2.get_Pointer()) == NULL) + { + // DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. + return -1003; + } + + /* Make sure that test_object_1 & test_object_2 have the same pointer. */ + if (p1 != p2) + { + /* The objects have different pointers. */ + return -1026; + } + + // Now run the Compare function. + if (!(test_object_1.Compare(test_object_2))) + { + // DataProcess::Data_Object::Compare() Comparasion failed should have passed. + return -1020; + } + + // Now run the NCompare function. + if (test_object_1.NCompare(test_object_2)) + { + // DataProcess::Data_Object::NCompare() Comparasion passed should have failed. + return -1021; + } + + // Clear test_object_1 test objects. + test_object_1.clear(); + test_object_2.clear(); /* ERROR: Shallow Copied data object cannot be cleared. */ + + // Make sure that both test_object_1 and test_object_2 are empty. + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* + Get_Byte() test 1. + + This test checks the result of a valid call to Get_Byte(), using s1 and offset 3. + + The result should be that the char at offset 3 is stored in byte and the function + will return COMMON_ERROR_SUCCESS. + */ + std::cout << "MSYS_DataObject_Get_Byte() Tests: "; + std::cout.flush(); + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* Set test_object_1 to s1. */ + test_object_1 = s1; + + /* + Make sure that test_object_1's size and capacity are equal to s1. + */ + if (test_object_1.size() != s1.size()) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if (test_object_1.get_Capacity() != (s1.size())) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run Get_Byte() function. */ + retFromCall = MSYS_DataObject_Get_Byte(test_object_1_c_ptr, &byte, 3); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Test function failed. */ + return -1020; + } + + /* Check for valid byte. */ + if (byte != s1[3]) + { + /* Test function failed to return the correct byte. */ + return -1020; + } + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* + Get_Byte() test 2. + + This test checks the result of a invalid call to Get_Byte(), using a blank object and offset 3. + + The result should be that the byte remains NULL, and that the function returns COMMON_ERROR_NO_DATA. + */ + + /* Reset byte. */ + byte = '\0'; + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* Run Get_Byte() function. */ + retFromCall = MSYS_DataObject_Get_Byte(test_object_1_c_ptr, &byte, 3); + if (retFromCall != COMMON_ERROR_NO_DATA) + { + /* Test function failed. */ + return -1020; + } + + /* Check for valid byte. */ + if (byte != '\0') + { + /* Test function failed to return the correct byte. */ + return -1020; + } + + /* + Get_Byte() test 3. + + This test checks the result of a invalid call to Get_Byte(), using s1 an offset beyond the end of the buffer. + + The result should be that the byte remains NULL, and that the function returns COMMON_ERROR_RANGE_ERROR. + */ + + /* Reset byte. */ + byte = '\0'; + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* Set test_object_1 to s1. */ + test_object_1 = s1; + + /* + Make sure that test_object_1's size and capacity are equal to s1. + */ + if (test_object_1.size() != s1.size()) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if (test_object_1.get_Capacity() != (s1.size())) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run Get_Byte() function. */ + retFromCall = MSYS_DataObject_Get_Byte(test_object_1_c_ptr, &byte, SIZE_MAX); + if (retFromCall != COMMON_ERROR_RANGE_ERROR) + { + /* Test function failed. */ + return -1020; + } + + /* Check for valid byte. */ + if (byte != '\0') + { + /* Test function failed to return the correct byte. */ + return -1020; + } + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* + Get_Byte() test 4. + + This test checks the result of a invalid call to Get_Byte(), using a NULL Data Object. + + The result should be that the byte remains NULL, and that the function returns COMMON_ERROR_INVALID_ARGUMENT. + */ + + /* Reset byte. */ + byte = '\0'; + + /* Run Get_Byte() function. */ + retFromCall = MSYS_DataObject_Get_Byte(NULL, &byte, 0); + if (retFromCall != COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test function failed. */ + return -1020; + } + + /* Check for valid byte. */ + if (byte != '\0') + { + /* Test function failed to return the correct byte. */ + return -1020; + } + + /* + Get_Byte() test 5. + + This test checks the result of a invalid call to Get_Byte(), using inconsistant (malformed) Data Objects. + + The result should be that the byte remains NULL, and that the function returns COMMON_ERROR_DATA_CORRUPTION. + */ + /* Reset byte. */ + byte = '\0'; + + /* Call Get_Byte(). */ + retFromCall = MSYS_DataObject_Get_Byte(&badData, &byte, 0); + if ((retFromCall != COMMON_ERROR_DATA_CORRUPTION) || (byte != '\0')) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + retFromCall = MSYS_DataObject_Get_Byte(&badLength, &byte, 0); + if ((retFromCall != COMMON_ERROR_DATA_CORRUPTION) || (byte != '\0')) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + retFromCall = MSYS_DataObject_Get_Byte(&badCapacity, &byte, 0); + if ((retFromCall != COMMON_ERROR_DATA_CORRUPTION) || (byte != '\0')) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + std::cout << "PASS\n"; + std::cout.flush(); + + /* + Set_Byte() test 1. + + This test checks the result of a valid call to Set_Byte(), using s1 and offset 3. + + The result should be that the given byte is stored at offset 3 in test_object_1 and the function + will return COMMON_ERROR_SUCCESS. + */ + std::cout << "MSYS_DataObject_Set_Byte() Tests: "; + std::cout.flush(); + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) { - cout << "PASS\n"; + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; } - else + if (test_object_1.get_Capacity() != 0) { - cout << "FAIL\n"; - return -1; + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; } + /* Set test_object_1 to s1. */ + test_object_1 = s1; + + /* + Make sure that test_object_1's size and capacity are equal to s1. + */ + if (test_object_1.size() != s1.size()) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if (test_object_1.get_Capacity() != (s1.size())) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run Set_Byte() function. */ + retFromCall = MSYS_DataObject_Set_Byte(test_object_1_c_ptr, byte, 3); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Test function failed. */ + return -1020; + } + + /* Check for valid byte. */ + if (byte != p1[3]) + { + /* Test function failed to set the correct byte. */ + return -1020; + } + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* + Set_Byte() test 2. + + This test checks the result of a invalid call to Set_Byte(), using a blank object and offset 3. + + The result should be that the function returns COMMON_ERROR_NO_DATA. + */ + + /* Reset byte. */ + byte = '\0'; + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* Run Set_Byte() function. */ + retFromCall = MSYS_DataObject_Set_Byte(test_object_1_c_ptr, byte, 3); + if (retFromCall != COMMON_ERROR_NO_DATA) + { + /* Test function failed. */ + return -1020; + } + + /* + Set_Byte() test 3. + + This test checks the result of a invalid call to Set_Byte(), using s1 an offset beyond the end of the buffer. + + The result should be that the function returns COMMON_ERROR_RANGE_ERROR. + */ + + /* Reset byte. */ + byte = '\0'; + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* Set test_object_1 to s1. */ + test_object_1 = s1; + + /* + Make sure that test_object_1's size and capacity are equal to s1. + */ + if (test_object_1.size() != s1.size()) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if (test_object_1.get_Capacity() != (s1.size())) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run Set_Byte() function. */ + retFromCall = MSYS_DataObject_Set_Byte(test_object_1_c_ptr, byte, SIZE_MAX); + if (retFromCall != COMMON_ERROR_RANGE_ERROR) + { + /* Test function failed. */ + return -1020; + } + + // Clear test_object_1. + test_object_1.clear(); + + // Make sure that both test_object_1 is empty. + if (test_object_1.size() != 0) + { + // DataProcess::Data_Object::clear() Invalid size after clear(). + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + // DataProcess::Data_Object::clear() Invalid capacity after clear(). + return -1006; + } + + /* + Set_Byte() test 4. + + This test checks the result of a invalid call to Set_Byte(), using a NULL Data Object. + + The result should be that the function returns COMMON_ERROR_INVALID_ARGUMENT. + */ + + /* Reset byte. */ + byte = '\0'; + + /* Run Set_Byte() function. */ + retFromCall = MSYS_DataObject_Set_Byte(NULL, byte, 0); + if (retFromCall != COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test function failed. */ + return -1020; + } + + /* + Set_Byte() test 5. + + This test checks the result of a invalid call to Set_Byte(), using inconsistant (malformed) Data Objects. + + The result should be that the function returns COMMON_ERROR_DATA_CORRUPTION. + */ + /* Reset byte. */ + byte = '\0'; + + /* Call Set_Byte(). */ + retFromCall = MSYS_DataObject_Set_Byte(&badData, byte, 0); + if (retFromCall != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + retFromCall = MSYS_DataObject_Set_Byte(&badLength, byte, 0); + if (retFromCall != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + retFromCall = MSYS_DataObject_Set_Byte(&badCapacity, byte, 0); + if (retFromCall != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + std::cout << "PASS\n"; + std::cout.flush(); + + /* Run the tests for Insert, Insert No Allocation, Replace, and Overwrite functions. */ + ret_Insert_Char = Unit_Test_Data_Object_Insert_Replace_Overwrite_Char(); + if (ret_Insert_Char != 0) + { + std::cout << "FAIL\n"; + std::cout.flush(); + return ret_Insert_Char; + } + ret_Insert_DO = Unit_Test_Data_Object_Insert_Replace_Overwrite_Data_Object(); + if (ret_Insert_DO != 0) + { + std::cout << "FAIL\n"; + std::cout.flush(); + return ret_Insert_DO; + } + ret_Insert_CStr = Unit_Test_Data_Object_Insert_Replace_Overwrite_C_Str(); + if (ret_Insert_DO != 0) + { + std::cout << "FAIL\n"; + std::cout.flush(); + return ret_Insert_CStr; + } + // Default return. return 0; } diff --git a/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_C_Str.cpp b/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_C_Str.cpp new file mode 100644 index 0000000..57baee7 --- /dev/null +++ b/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_C_Str.cpp @@ -0,0 +1,823 @@ +/*! + Multiverse Engine Project 03/8/2016 Unit Tests Unit_Test_Data_Object_Insert_Replace_Overwrite_C_Str.cpp + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Unit_Tests.h" + +/* Special include for manually creating invalid MSYS_DataObject structures. (DON'T DO THIS AT HOME KIDS. Use the factories and public functions instead!)*/ +#include "../Core/Src/Data_Object/Data_Object_Private_Structure.c" + +/* Define test structure for C_Str function variants. */ +typedef struct Unit_Test_Data_Object_From_C_Str_Funct_Test { + int (*fp)(MSYS_DataObject_T * obj, const size_t offset, const char * src, const size_t srcLength); /* Function pointer to C_Str function to test. */ + char * functName; /* Human readable name of the function to test. */ + bool isInsert; /* Whether or not the function to test inserts data into the given data object, or if it changes existing data. */ + bool canReallocate; /* Whether or not the function can reallocate the given dest Data Object's memory buffer. */ +} Unit_Test_Data_Object_From_C_Str_Funct_Test_T; + +/* Define functions to test for Data_Object function variants. */ +const Unit_Test_Data_Object_From_C_Str_Funct_Test_T Unit_Test_Data_Object_From_C_Str_Functions_To_Test[] = { + {MSYS_DataObject_Insert_CString, "MSYS_DataObject_Insert_CString", true, true}, + {MSYS_DataObject_Insert_CString_No_Allocaton, "MSYS_DataObject_Insert_CString_No_Allocaton", true, false}, + {MSYS_DataObject_Replace_With_CString, "MSYS_DataObject_Replace_With_CString", false, false}, + {MSYS_DataObject_Overwrite_With_CString, "MSYS_DataObject_Overwrite_With_CString", false, true} +}; + +int Unit_Test_Data_Object_Insert_Replace_Overwrite_C_Str() +{ + /* Init retFromCall */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + + /* Init objects to test. */ + DataProcess::Data_Object test_object_1; + MSYS_DataObject * test_object_1_c_ptr = NULL; + void * pbadPrivPtr = NULL; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badPrivPtr = { &pbadPrivPtr }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badDataPriv = { NULL, 1, 1, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badLengthPriv = { "Bad_STR", SIZE_MAX, 7, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badCapacityPriv = { "Bad_STR", 7, 3, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadData = &badDataPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadLength = &badLengthPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadCapacity = &badCapacityPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badData = { &pbadData }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badLength = { &pbadLength }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badCapacity = { &pbadCapacity }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + + /* Hard coded test vars. */ + const std::string s1 = "Junk_Data"; + const std::string s2 = "Is_Not"; + const std::string s3 = "Why_is_it"; + + const char * p1 = NULL; + const char * p2 = NULL; + const char * p3 = NULL; /* Used for comparing data from both data objects. */ + int y = 0; + + /* Get the pointers for the C object. */ + retFromCall = test_object_1.get_C_Struct(&test_object_1_c_ptr); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Could not get test_object C struct pointer. */ + return -1; + } + + /* Send newline to output. */ + std::cout << '\n'; + std::cout.flush(); + + /* Begin test loop. */ + for (y = 0; y < (sizeof(Unit_Test_Data_Object_From_C_Str_Functions_To_Test) / sizeof(Unit_Test_Data_Object_From_C_Str_Funct_Test_T)); y++) + { + /* Output name of function we are testing. */ + std::cout << Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].functName << "() Tests: "; + std::cout.flush(); + + /* + C_Str Test 1 + + This test checks a char function for MSYS_DataObjects. (Reallocate) + + This test attempts to set test_object_1 to s1, and then run the test function with an offset of 0 and + the data of s2. + + The result for a non insert function should be that test_object_1 will have the data from s2 at offset 0, + and the remaining data will be equal to s1 NOT including the first s2 number of bytes from s1. + (Size and capacity should be: s1.size().) + + The result for an insert function should be that test_object_1 will have the data from s2 at offset 0, + and the remaining data will be equal to s1. (Size and capacity should be: s1.size() + s2.size().) + */ + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Set test_object_1 to s1. */ + test_object_1 = s1; + + /* + Make sure that test_object_1's size and capacity are equal to s1. + */ + if (test_object_1.size() != s1.size()) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != s1.size()) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, s2.c_str(), s2.size()) != + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? + /* Insert No Allocation function should return buffer too small here. All other functions should return success. */ + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? (COMMON_ERROR_SUCCESS) : (COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL)) : + (COMMON_ERROR_SUCCESS))) + { + /* Test function failed. */ + return -1020; + } + + /* Make sure that test_object_1's size and capacity are equal to (s1.size() + s2.size() insert and reallocate) OR + (s1.size() Non-insert or non-reallocation). */ + if (test_object_1.size() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? (s1.size() + s2.size()) : (s1.size())) : + (s1.size()))) + { + /* Invalid size after call to test function. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? (s1.size() + s2.size()) : (s1.size())) : + (s1.size()))) + { + /* Invalid capacity after call to test function. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to strings s1 and s2. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + p3 = NULL; + if ((p3 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? (s1.size() + s2.size()) : (s1.size())) : + (s1.size())); x++) + { + /* If the current function we are testing is Insert No Allocate, then the data will be identical to s1. */ + if ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) && + (!(Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate))) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + /* All other functions. */ + else + { + /* Check for data from s2, if we are within it's length. */ + if (x < s2.size()) + { + if (p1[x] != p3[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Check for s1 otherwise. */ + else + { + if (p1[x] != p2[((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? (x - (s2.size())) : (x)) : + (x))]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + } + } + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + C_Str Test 2 + + This test checks a C_Str function for MSYS_DataObjects. (Reallocate) + + This test attempts to store s3 in test_object_1, then run the test function with an offset of + position 4 and the data of s2. + + The result for a non allocation function should be that it returns COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL, as the + resulting string, if the non allocation function were to continue anyway, would have a capacity and size equaling + (s3.size() + s2.size() - (offset + 1)) which is beyond the allocated length of the buffer. + + The result for the insert() or overwrite() function should be that test_object_1 contains s3's data up to position 4, at + which point it contains s2's data followed by the data from s3 at position 4 onward. + Capacity and size equaling (s3.size() + s2.size()). + */ + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Set test_object_1 to s3. */ + test_object_1 = s3; + + /* + Make sure that test_object_1's size and capacity are equal to s3. + */ + if (test_object_1.size() != s3.size()) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != s3.size()) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(test_object_1_c_ptr, 4, s2.c_str(), s2.size()) != + /* Reallocation functions should succeed here, Non-allocation functions should return COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL. */ + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? (COMMON_ERROR_SUCCESS) : (COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL))) + { + /* Test function failed. */ + return -1020; + } + + /* + Make sure that test_object_1's size and capacity are equal to ((s3.size() + s2.size()) for Insert()), + ((s3.size() + s2.size() - (offset + 1)) for Overwrite()) or (s3.size() for everything else.). + */ + if (test_object_1.size() != + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : + (s3.size() + s2.size() - (4 + 1))) : + (s3.size()))) + { + /* Invalid size after call to test function. */ + return -1014; + } + if (test_object_1.get_Capacity() != + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : + (s3.size() + s2.size() - (4 + 1))) : + (s3.size()))) + { + /* Invalid capacity after call to test function. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to strings s3 and s2. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + p3 = NULL; + if ((p3 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* Unless this is the Insert() or Overwrite() function, the data will be identical to s3. */ + if (p1[x] != + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].canReallocate) ? ((x < 4) ? (p2[x]) : ((x < (4 + s2.size())) ? (p3[(x - 4)]) : (p2[(x - s2.size())]))) : + (p2[x]))) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + C_Str Test 3 + + This test checks a C_Str function for MSYS_DataObjects. (Non-Reallocate) + + This test attempts to reserve memory with the length of s3 + s2, then store + (concat) s3 in test_object_1, and then run the test function with an offset of 0 and the data of s2. + + The result for a non insert function should be that test_object_1 will have the data from s2 at offset 0 + and the remaining data will be equal to s3 starting at offset s2.size(). + (Size should be: s3.size(), capacity should be (s3.size() + s2.size()).) + + The result for an insert function should be that test_object_1 will have the data from s2 at offset 0 + and the remaining data will be equal to s3. (Size and capacity should be: (s3.size() + s2.size()).) + */ + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Reserve memory for s3.size() (Non-insert functions) OR (s3.size() + s2.size()) (Insert Functions). */ + test_object_1.reserve(((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))); + + /* Check for reserved memory. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to reserve(). */ + return -1008; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to reserve(). */ + return -1007; + } + + /* Concat s3 into test_object_1. */ + test_object_1 += s3; + + /* + Make sure that test_object_1's size and capacity are equal to s3. + */ + if (test_object_1.size() != s3.size()) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, s2.c_str(), s2.size()) != COMMON_ERROR_SUCCESS) + { + /* Test function failed. */ + return -1020; + } + + /* Make sure that test_object_1's size and capacity are equal to s3.size() (Non-insert function) OR + (s3.size() + s2.size()) (Insert function). */ + if (test_object_1.size() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to strings s3 and s2. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + p3 = NULL; + if ((p3 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* Check for s2's data if the current offset value is less than s2.size(). */ + if (x < s2.size()) + { + if (p1[x] != p3[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + /* Check for s3's data otherwise. */ + else + { + if (p1[x] != p2[((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (x - s2.size()) : (x))]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + } + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + C_Str test 4 + + This test checks a C_Str function for MSYS_DataObjects. (Non-Reallocate) + + This test attempts to reserve memory with the length of s1 + s2, then store + (concat) s1 in test_object_1, then run the test function with an offset of position 3 and data of s2. + + The result should be for a non insert function, that test_object_1 contains s1's data up to position 3, + at which it contains s2's data, and the remaining data is s1's remaining data after position 3. + Capacity and size equaling s1.size(). + + The result should be for a insert function, that test_object_1 contains s1's data up to position 3, + at which it contains s2's data, and the remaining data is s1's remaining data including position 3. + Capacity and size equaling (s1.size() + s2.size()). + */ + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 and test_object_2 are empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Reserve memory for s1.size() (Non-Insert function) OR (s1.size() + s2.size()) (Insert function). */ + test_object_1.reserve(((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))); + + /* Check for reserved memory. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to reserve(). */ + return -1008; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to reserve(). */ + return -1007; + } + + /* Concat s1 into test_object_1. */ + test_object_1 += s1; + + /* + Make sure that test_object_1's size and capacity are equal to s1. + */ + if (test_object_1.size() != s1.size()) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(test_object_1_c_ptr, 3, s2.c_str(), s2.size()) != COMMON_ERROR_SUCCESS) + { + /* Test function failed. */ + return -1020; + } + + /* + Make sure that test_object_1's size and capacity are equal to s1.size() (Non-insert function) OR + (s1.size() + s2.size()) (Insert function). + */ + if (test_object_1.size() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to strings s1 and s2. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + p3 = NULL; + if ((p3 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* + Check for s1 if the current offset is less than 3. + + Check for s2 if the current offset is greater than or equal to 3 but less than + s2's length. + + If the current offset is greater than 3 + s2's length, then: + - If the function we are testing is a non-insert function, check for the remaining s1 data at offset (x). + - Otherwise check for the remaining data from s1 at offset (x - s2.size()). + */ + if (p1[x] != ((x < 3) ? (p2[x]) : + ((x < (3 + s2.size())) ? (p3[(x - 3)]) : + ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].isInsert) ? (p2[(x - s2.size())]) : + (p2[x]))))) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Invalid pointer check. + + Should return COMMON_ERROR_INVALID_ARGUMENT. + */ + if ((Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(NULL, 3, s1.c_str(), s1.size()) != COMMON_ERROR_INVALID_ARGUMENT) || + (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(test_object_1_c_ptr, 3, NULL, 33) != COMMON_ERROR_INVALID_ARGUMENT)) + { + /* Test function failure. Should have returned COMMON_ERROR_INVALID_ARGUMENT. */ + return -1020; + } + + /* + Invalid private object pointer check. + + Should return COMMON_ERROR_INVALID_ARGUMENT. + */ + if (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(&badPrivPtr, 3, s1.c_str(), s1.size()) != COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test function failure. Should have returned COMMON_ERROR_INVALID_ARGUMENT. */ + return -1020; + } + + /* + Invalid C_Str tests. + + All of the following tests should return COMMON_ERROR_DATA_CORRUPTION. + */ + if (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(&badData, 0, s1.c_str(), s1.size()) != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + if (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(&badLength, 0, s1.c_str(), s2.size()) != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + if (Unit_Test_Data_Object_From_C_Str_Functions_To_Test[y].fp(&badCapacity, 0, s1.c_str(), s2.size()) != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + + /* Output result of tests. */ + std::cout << "PASS\n"; + std::cout.flush(); + + } /* End test loop. */ + + /* Exit function. */ + return 0; +} diff --git a/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_Char.cpp b/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_Char.cpp new file mode 100644 index 0000000..3510839 --- /dev/null +++ b/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_Char.cpp @@ -0,0 +1,816 @@ +/*! + Multiverse Engine Project 07/7/2016 Unit Tests Unit_Test_Data_Object_Insert_Replace_Overwrite_Char.cpp + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Unit_Tests.h" + +/* Special include for manually creating invalid MSYS_DataObject structures. (DON'T DO THIS AT HOME KIDS. Use the factories and public functions instead!)*/ +#include "../Core/Src/Data_Object/Data_Object_Private_Structure.c" + +/* Define test structure for char function variants. */ +typedef struct Unit_Test_Data_Object_Char_Funct_Test { + int (*fp)(MSYS_DataObject_T * obj, const size_t offset, const char data); /* Function pointer to char function to test. */ + char * functName; /* Human readable name of the function to test. */ + bool isInsert; /* Whether or not the function to test inserts data into the given data object, or if it changes existing data. */ + bool canReallocate; /* Whether or not the function can reallocate the given Data Object's memory buffer. */ +} Unit_Test_Data_Object_Char_Funct_Test_T; + +/* Define functions to test for char function variants. */ +const Unit_Test_Data_Object_Char_Funct_Test_T Unit_Test_Data_Object_Char_Functions_To_Test[] = { + {MSYS_DataObject_Insert_Char, "MSYS_DataObject_Insert_Char", true, true}, + {MSYS_DataObject_Insert_Char_No_Allocaton, "MSYS_DataObject_Insert_Char_No_Allocaton", true, false}, + {MSYS_DataObject_Replace_With_Char, "MSYS_DataObject_Replace_With_Char", false, false}, + {MSYS_DataObject_Overwrite_With_Char, "MSYS_DataObject_Overwrite_With_Char", false, true} +}; + +int Unit_Test_Data_Object_Insert_Replace_Overwrite_Char() +{ + /* Init retFromCall */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + + /* Init objects to test. */ + DataProcess::Data_Object test_object_1; + DataProcess::Data_Object test_object_2; + MSYS_DataObject * test_object_1_c_ptr = NULL; + MSYS_DataObject * test_object_2_c_ptr = NULL; + void * pbadPrivPtr = NULL; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badPrivPtr = { &pbadPrivPtr }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badDataPriv = { NULL, 1, 1, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badLengthPriv = { "Bad_STR", SIZE_MAX, 7, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badCapacityPriv = { "Bad_STR", 7, 3, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadData = &badDataPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadLength = &badLengthPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadCapacity = &badCapacityPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badData = { &pbadData }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badLength = { &pbadLength }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badCapacity = { &pbadCapacity }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + + /* Hard coded test vars. */ + const std::string s1 = "Junk_Data"; + const std::string s2 = "Is_Not"; + const std::string s3 = "Why_is_it"; + const char c1 = 'P'; + + const char * p1 = NULL; + const char * p2 = NULL; + int y = 0; + + /* Get the pointers for the C objects. */ + retFromCall = test_object_1.get_C_Struct(&test_object_1_c_ptr); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Could not get test_object C struct pointer. */ + return -1; + } + retFromCall = test_object_2.get_C_Struct(&test_object_2_c_ptr); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Could not get test_object C struct pointer. */ + return -1; + } + + /* Send newline to output. */ + std::cout << '\n'; + std::cout.flush(); + + /* Begin test loop. */ + for (y = 0; y < (sizeof(Unit_Test_Data_Object_Char_Functions_To_Test) / sizeof(Unit_Test_Data_Object_Char_Funct_Test_T)); y++) + { + /* Output name of function we are testing. */ + std::cout << Unit_Test_Data_Object_Char_Functions_To_Test[y].functName << "() Tests: "; + std::cout.flush(); + + /* + Char Test 1 + + This test checks a char function for MSYS_DataObjects. (Reallocate) + + This test attempts to set test_object_1 to s1, and then run the test function with an offset of 0 and + the data of c1. + + The result for a non insert function should be that test_object_1 will have the first char equal to c1 + and the remaining data will be equal to s1 NOT including the first char of s1. (Size and capacity should be: s1.size().) + + The result for an insert function should be that test_object_1 will have the first char equal to c1 + and the remaining data will be equal to s1 including the first char of s1. (Size and capacity should be: s1.size() + 1.) + */ + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Set test_object_1 to s1. */ + test_object_1 = s1; + + /* Make sure that test_object_1's size and capacity are equal to s1. */ + if (test_object_1.size() != s1.size()) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != s1.size()) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, c1) != + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + /* Insert No Allocation function should return buffer too small here. All other functions should return success. */ + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (COMMON_ERROR_SUCCESS) : (COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL)) : + (COMMON_ERROR_SUCCESS))) + { + /* Test function failed. */ + return -1020; + } + + /* Make sure that test_object_1's size and capacity are equal to s1.size() + 1. */ + if (test_object_1.size() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (s1.size() + 1) : (s1.size())) : + (s1.size()))) + { + /* Invalid size after call to test function. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (s1.size() + 1) : (s1.size())) : + (s1.size()))) + { + /* Invalid capacity after call to test function. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* Check for c1 if x = 0. */ + if (x == 0) + { + if (p1[x] != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + /* Only Insert No Allocation should check for the data from p2 here, all other functions should check for c1. */ + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (c1) : (p2[x])) : + (c1))) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Check for s1 otherwise. */ + else + { + if (p1[x] != p2[((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (x - 1) : (x)) : + (x))]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + } + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Replace() char Test 2 + + This test checks a char function for MSYS_DataObjects. (Reallocate) + + This test attempts to store s3 in test_object_1, then run the test function with an offset of + position 4 and the data of c1. + + The result for a non insert function should be that test_object_1 contains s3's data up to position 4, at + which point it contains c1 followed by the data from s3 at position 5 onward. + Capacity and size equaling s3.size(). + + The result for an insert function should be that test_object_1 contains s3's data up to position 4, at + which point it contains c1 followed by the data from s3 at position 4 onward. + Capacity and size equaling s3.size() + 1. + */ + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Set test_object_1 to s3. */ + test_object_1 = s3; + + /* Make sure that test_object_1's size and capacity are equal to s3. */ + if (test_object_1.size() != s3.size()) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != s3.size()) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(test_object_1_c_ptr, 4, c1) != + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + /* Insert No Allocation function should return buffer too small here. All other functions should return success. */ + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (COMMON_ERROR_SUCCESS) : (COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL)) : + (COMMON_ERROR_SUCCESS))) + { + /* Test function failed. */ + return -1020; + } + + /* Make sure that test_object_1's size and capacity are equal to s3.size(). */ + if (test_object_1.size() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (s3.size() + 1) : (s3.size())) : + (s3.size()))) + { + /* Invalid size after call to test function. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (s3.size() + 1) : (s3.size())) : + (s3.size()))) + { + /* Invalid capacity after call to test function. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* Check for c1 if x = 4. */ + if (x == 4) + { + if (p1[x] != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + /* Only Insert No Allocation should check for the data from p2 here, all other functions should check for c1. */ + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (c1) : (p2[x])) : + (c1))) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Check for s3 otherwise. + + Fix this, the multiple IFs are not needed. use a conditional. + */ + else + { + if (x < 4) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + if (x > 4) + { + if (p1[x] != p2[((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_Char_Functions_To_Test[y].canReallocate) ? (x - 1) : (x)) : + (x))]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + } + } + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Char Test 3 + + This test checks a char function for MSYS_DataObjects. (Non-Reallocate) + + This test attempts to reserve memory with the length of s3, then store + (concat) s3 in test_object_1, and then run the test function with an offset of 0 and the data of c1. + + The result for a non insert function should be that test_object_1 will have the first char equal to c1 and the remaining data will be + equal to s3 not including the first char of s3. (Size and capacity should be: s3.size().) + + The result for an insert function should be that test_object_1 will have the first char equal to c1 and the remaining data will be + equal to s3 including the first char. (Size and capacity should be: s3.size() + 1.) + */ + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Reserve memory for s3.size(). */ + test_object_1.reserve(((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s3.size() + 1) : (s3.size()))); + + /* Check for reserved memory. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to reserve(). */ + return -1008; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s3.size() + 1) : (s3.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to reserve(). */ + return -1007; + } + + /* Concat s3 into test_object_1. */ + test_object_1 += s3; + + /* Make sure that test_object_1's size and capacity are equal to s3. */ + if (test_object_1.size() != s3.size()) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s3.size() + 1) : (s3.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, c1) != COMMON_ERROR_SUCCESS) + { + /* Test function failed. */ + return -1020; + } + + /* Make sure that test_object_1's size and capacity are equal to s3.size(). */ + if (test_object_1.size() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s3.size() + 1) : (s3.size()))) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s3.size() + 1) : (s3.size()))) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* Check for c1 if x = 0. */ + if (x == 0) + { + if (p1[x] != c1) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Check for s3 otherwise. */ + else + { + if (p1[x] != p2[((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (x - 1) : (x))]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + } + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Char test 4 + + This test checks a char function for MSYS_DataObjects. (Non-Reallocate) + + This test attempts to reserve memory with the length of s1, then store + (concat) s1 in test_object_1, then run the test function with an offset of position 3 and data of c1. + + The result should be for a non insert function, that test_object_1 contains s1's data up to position 3, + at which it contains c1, and the remaining data is s1's remaining data after position 3. + Capacity and size equaling s1.size(). + + The result should be for a insert function, that test_object_1 contains s1's data up to position 3, + at which it contains c1, and the remaining data is s1's remaining data including position 3. + Capacity and size equaling s1.size() + 1. + */ + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Reserve memory for s1.size(). */ + test_object_1.reserve(((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s1.size() + 1) : (s1.size()))); + + /* Check for reserved memory. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to reserve(). */ + return -1008; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s1.size() + 1) : (s1.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to reserve(). */ + return -1007; + } + + /* Concat s1 into test_object_1. */ + test_object_1 += s1; + + /* Make sure that test_object_1's size and capacity are equal to s1. */ + if (test_object_1.size() != s1.size()) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s1.size() + 1) : (s1.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(test_object_1_c_ptr, 3, c1) != COMMON_ERROR_SUCCESS) + { + /* Test function failed. */ + return -1020; + } + + /* Make sure that test_object_1's size and capacity are equal to s1.size(). */ + if (test_object_1.size() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s1.size() + 1) : (s1.size()))) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (s1.size() + 1) : (s1.size()))) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* Check for c1 if x = 3. */ + if (x == 3) + { + if (p1[x] != c1) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Check for s1 otherwise. */ + else + { + if (x < 3) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + if (x > 3) + { + if (p1[x] != p2[((Unit_Test_Data_Object_Char_Functions_To_Test[y].isInsert) ? (x - 1) : (x))]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + } + } + + /* Clear test_object_1. */ + test_object_1.clear(); + + /* Make sure that test_object_1 is empty. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if (test_object_1.get_Capacity() != 0) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Invalid Data_Object pointer check. + + Should return COMMON_ERROR_INVALID_ARGUMENT. + */ + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(NULL, 3, c1) != COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test function failure. Should have returned COMMON_ERROR_INVALID_ARGUMENT. */ + return -1020; + } + + /* + Invalid private object pointer check. + + Should return COMMON_ERROR_INVALID_ARGUMENT. + */ + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(&badPrivPtr, 3, c1) != COMMON_ERROR_INVALID_ARGUMENT) + { + /* Test function failure. Should have returned COMMON_ERROR_INVALID_ARGUMENT. */ + return -1020; + } + + /* + Invalid Data Object tests. + + All of the following tests should return COMMON_ERROR_DATA_CORRUPTION. + */ + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(&badData, 0, c1) != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(&badLength, 0, c1) != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + if (Unit_Test_Data_Object_Char_Functions_To_Test[y].fp(&badCapacity, 0, c1) != COMMON_ERROR_DATA_CORRUPTION) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + + /* Output result of tests. */ + std::cout << "PASS\n"; + std::cout.flush(); + + } /* End test loop. */ + + /* Exit function. */ + return 0; +} diff --git a/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_Data_Object.cpp b/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_Data_Object.cpp new file mode 100644 index 0000000..7d2e7d2 --- /dev/null +++ b/src/Tests/Unit_Test_Data_Object_Insert_Replace_Overwrite_Data_Object.cpp @@ -0,0 +1,961 @@ +/*! + Multiverse Engine Project 29/7/2016 Unit Tests Unit_Test_Data_Object_Insert_Replace_Overwrite_Data_Object.cpp + + Copyright (C) 2016 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Internal includes. */ +#include "Unit_Tests.h" + +/* Special include for manually creating invalid MSYS_DataObject structures. (DON'T DO THIS AT HOME KIDS. Use the factories and public functions instead!)*/ +#include "../Core/Src/Data_Object/Data_Object_Private_Structure.c" + +/* Define test structure for Data_Object function variants. */ +typedef struct Unit_Test_Data_Object_From_Data_Object_Funct_Test { + int (*fp)(MSYS_DataObject_T * obj, const size_t offset, const MSYS_DataObject_T * src); /* Function pointer to Data_Object function to test. */ + char * functName; /* Human readable name of the function to test. */ + bool isInsert; /* Whether or not the function to test inserts data into the given data object, or if it changes existing data. */ + bool canReallocate; /* Whether or not the function can reallocate the given dest Data Object's memory buffer. */ +} Unit_Test_Data_Object_From_Data_Object_Funct_Test_T; + +/* Define functions to test for Data_Object function variants. */ +const Unit_Test_Data_Object_From_Data_Object_Funct_Test_T Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[] = { + {MSYS_DataObject_Insert_From_DataObject, "MSYS_DataObject_Insert_From_DataObject", true, true}, + {MSYS_DataObject_Insert_From_DataObject_No_Allocaton, "MSYS_DataObject_Insert_From_DataObject_No_Allocaton", true, false}, + {MSYS_DataObject_Replace_With_DataObject, "MSYS_DataObject_Replace_With_DataObject", false, false}, + {MSYS_DataObject_Overwrite_With_DataObject, "MSYS_DataObject_Overwrite_With_DataObject", false, true} +}; + +int Unit_Test_Data_Object_Insert_Replace_Overwrite_Data_Object() +{ + /* Init retFromCall */ + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + + /* Init objects to test. */ + DataProcess::Data_Object test_object_1; + DataProcess::Data_Object test_object_2; + MSYS_DataObject * test_object_1_c_ptr = NULL; + MSYS_DataObject * test_object_2_c_ptr = NULL; + void * pbadPrivPtr = NULL; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badPrivPtr = { &pbadPrivPtr }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badDataPriv = { NULL, 1, 1, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badLengthPriv = { "Bad_STR", SIZE_MAX, 7, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject_Private badCapacityPriv = { "Bad_STR", 7, 3, 0}; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadData = &badDataPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadLength = &badLengthPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + void * pbadCapacity = &badCapacityPriv; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badData = { &pbadData }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badLength = { &pbadLength }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + MSYS_DataObject badCapacity = { &pbadCapacity }; /* <---- DON'T DO THIS AT HOME KIDS. This is for testing purposes only. */ + + /* Hard coded test vars. */ + const std::string s1 = "Junk_Data"; + const std::string s2 = "Is_Not"; + const std::string s3 = "Why_is_it"; + const char c1 = 'P'; + + const char * p1 = NULL; + const char * p2 = NULL; + const char * p3 = NULL; /* Used for comparing data from both data objects. */ + int y = 0; + + /* Get the pointers for the C objects. */ + retFromCall = test_object_1.get_C_Struct(&test_object_1_c_ptr); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Could not get test_object C struct pointer. */ + return -1; + } + retFromCall = test_object_2.get_C_Struct(&test_object_2_c_ptr); + if (retFromCall != COMMON_ERROR_SUCCESS) + { + /* Could not get test_object C struct pointer. */ + return -1; + } + + /* Send newline to output. */ + std::cout << '\n'; + std::cout.flush(); + + /* Begin test loop. */ + for (y = 0; y < (sizeof(Unit_Test_Data_Object_From_Data_Object_Functions_To_Test) / sizeof(Unit_Test_Data_Object_From_Data_Object_Funct_Test_T)); y++) + { + /* Output name of function we are testing. */ + std::cout << Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].functName << "() Tests: "; + std::cout.flush(); + + /* + Data_Object Test 1 + + This test checks a char function for MSYS_DataObjects. (Reallocate) + + This test attempts to set test_object_1 to s1, test_object_2 to s2, and then run the test function with an offset of 0 and + the data of test_object_2. + + The result for a non insert function should be that test_object_1 will have the data from test_object_2 at offset 0, + and the remaining data will be equal to s1 NOT including the first test_object_2 number of bytes from s1. + (Size and capacity should be: s1.size().) + + The result for an insert function should be that test_object_1 will have the data from test_object_2 at offset 0, + and the remaining data will be equal to s1. (Size and capacity should be: s1.size() + s2.size().) + */ + + /* Clear test_object_1 and test_object_2. */ + test_object_1.clear(); + test_object_2.clear(); + + /* Make sure that test_object_1 and test_object_2 is empty. */ + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Set test_object_1 to s1. */ + test_object_1 = s1; + + /* Set test_object_2 to s2. */ + test_object_2 = s2; + + /* + Make sure that test_object_1's size and capacity are equal to s1. + Also make sure that test_object_2's size and capacity are equal to s2. + */ + if ((test_object_1.size() != s1.size()) || (test_object_2.size() != s2.size())) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if ((test_object_1.get_Capacity() != s1.size()) || (test_object_2.get_Capacity() != s2.size())) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Get pointer for test_object_2. */ + p1 = NULL; + if ((p1 = test_object_2.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_2 actually contains the correct data. */ + for (size_t x = 0; x < test_object_2.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, test_object_2_c_ptr) != + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? + /* Insert No Allocation function should return buffer too small here. All other functions should return success. */ + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? (COMMON_ERROR_SUCCESS) : (COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL)) : + (COMMON_ERROR_SUCCESS))) + { + /* Test function failed. */ + return -1020; + } + + /* Make sure that test_object_1's size and capacity are equal to (s1.size() + s2.size() insert and reallocate) OR + (s1.size() Non-insert or non-reallocation). */ + if (test_object_1.size() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? (s1.size() + s2.size()) : (s1.size())) : + (s1.size()))) + { + /* Invalid size after call to test function. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? (s1.size() + s2.size()) : (s1.size())) : + (s1.size()))) + { + /* Invalid capacity after call to test function. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to strings s1 and s2. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + p3 = NULL; + if ((p3 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? (s1.size() + s2.size()) : (s1.size())) : + (s1.size())); x++) + { + /* If the current function we are testing is Insert No Allocate, then the data will be identical to s1. */ + if ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) && + (!(Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate))) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + /* All other functions. */ + else + { + /* Check for data from s2, if we are within it's length. */ + if (x < s2.size()) + { + if (p1[x] != p3[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Check for s1 otherwise. */ + else + { + if (p1[x] != p2[((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? (x - (s2.size())) : (x)) : + (x))]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + } + } + + /* Clear test_object_1 and test_object_2. */ + test_object_1.clear(); + test_object_2.clear(); + + /* Make sure that test_object_1 and test_object_2 are empty. */ + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Data_Object Test 2 + + This test checks a char function for MSYS_DataObjects. (Reallocate) + + This test attempts to store s3 in test_object_1, s2 in test_object_2, then run the test function with an offset of + position 4 and the data of test_object_2. + + The result for a non allocation function should be that it returns COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL, as the + resulting string, if the non allocation function were to continue anyway, would have a capacity and size equaling + (s3.size() + s2.size() - (offset + 1)) which is beyond the allocated length of the buffer. + + The result for the insert() or overwrite() function should be that test_object_1 contains s3's data up to position 4, at + which point it contains s2's data followed by the data from s3 at position 4 onward. + Capacity and size equaling (s3.size() + s2.size()). + */ + + /* Clear test_object_1 and test_object_2. */ + test_object_1.clear(); + test_object_2.clear(); + + /* Make sure that test_object_1 and test_object_2 are empty. */ + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Set test_object_1 to s3 and test_object_2 to s2. */ + test_object_1 = s3; + test_object_2 = s2; + + /* + Make sure that test_object_1's size and capacity are equal to s3. + Also make sure that test_object_2's size and capacity are equal to s2. + */ + if ((test_object_1.size() != s3.size()) || (test_object_2.size() != s2.size())) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if ((test_object_1.get_Capacity() != s3.size()) || (test_object_2.get_Capacity() != s2.size())) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Get pointer for test_object_2. */ + p1 = NULL; + if ((p1 = test_object_2.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_2 actually contains the correct data. */ + for (size_t x = 0; x < test_object_2.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 4, test_object_2_c_ptr) != + /* Reallocation functions should succeed here, Non-allocation functions should return COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL. */ + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? (COMMON_ERROR_SUCCESS) : (COMMON_ERROR_MEMORY_BUFFER_TOO_SMALL))) + { + /* Test function failed. */ + return -1020; + } + + /* + Make sure that test_object_1's size and capacity are equal to ((s3.size() + s2.size()) for Insert()), + ((s3.size() + s2.size() - (offset + 1)) for Overwrite()) or (s3.size() for everything else.). + */ + if (test_object_1.size() != + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : + (s3.size() + s2.size() - (4 + 1))) : + (s3.size()))) + { + /* Invalid size after call to test function. */ + return -1014; + } + if (test_object_1.get_Capacity() != + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : + (s3.size() + s2.size() - (4 + 1))) : + (s3.size()))) + { + /* Invalid capacity after call to test function. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to strings s3 and s2. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + p3 = NULL; + if ((p3 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* Unless this is the Insert() or Overwrite() function, the data will be identical to s3. */ + if (p1[x] != + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].canReallocate) ? ((x < 4) ? (p2[x]) : ((x < (4 + s2.size())) ? (p3[(x - 4)]) : (p2[(x - s2.size())]))) : + (p2[x]))) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Clear test_object_1 and test_object_2. */ + test_object_1.clear(); + test_object_2.clear(); + + /* Make sure that test_object_1 and test_object_2 are empty. */ + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Data_Object Test 3 + + This test checks a Data_Object function for MSYS_DataObjects. (Non-Reallocate) + + This test attempts to reserve memory with the length of s3 + s2, then store + (concat) s3 in test_object_1, and then run the test function with an offset of 0 and the data of s2. + + The result for a non insert function should be that test_object_1 will have the data from s2 at offset 0 + and the remaining data will be equal to s3 starting at offset s2.size(). + (Size should be: s3.size(), capacity should be (s3.size() + s2.size()).) + + The result for an insert function should be that test_object_1 will have the data from s2 at offset 0 + and the remaining data will be equal to s3. (Size and capacity should be: (s3.size() + s2.size()).) + */ + + /* Clear test_object_1 and test_object_2. */ + test_object_1.clear(); + test_object_2.clear(); + + /* Make sure that test_object_1 and test_object_2 are empty. */ + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Reserve memory for s3.size() (Non-insert functions) OR (s3.size() + s2.size()) (Insert Functions). */ + test_object_1.reserve(((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))); + + /* Check for reserved memory. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to reserve(). */ + return -1008; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to reserve(). */ + return -1007; + } + + /* Concat s3 into test_object_1, and concat s2 into test_object_2. */ + test_object_1 += s3; + test_object_2 += s2; + + /* + Make sure that test_object_1's size and capacity are equal to s3. + Also make sure that test_object_2's size and capacity are equal to s2. + */ + if ((test_object_1.size() != s3.size()) || (test_object_2.size() != s2.size())) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if ((test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))) || + (test_object_2.get_Capacity() != s2.size())) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Get pointer for test_object_2. */ + p1 = NULL; + if ((p1 = test_object_2.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_2 actually contains the correct data. */ + for (size_t x = 0; x < test_object_2.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, test_object_2_c_ptr) != COMMON_ERROR_SUCCESS) + { + /* Test function failed. */ + return -1020; + } + + /* Make sure that test_object_1's size and capacity are equal to s3.size() (Non-insert function) OR + (s3.size() + s2.size()) (Insert function). */ + if (test_object_1.size() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s3.size() + s2.size()) : (s3.size()))) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to strings s3 and s2. */ + p2 = NULL; + if ((p2 = s3.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + p3 = NULL; + if ((p3 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* Check for s2's data if the current offset value is less than s2.size(). */ + if (x < s2.size()) + { + if (p1[x] != p3[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + /* Check for s3's data otherwise. */ + else + { + if (p1[x] != p2[((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (x - s2.size()) : (x))]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + } + + /* Clear test_object_1 and test_object_2. */ + test_object_1.clear(); + test_object_2.clear(); + + /* Make sure that test_object_1 and test_object_2 are empty. */ + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Data_Object test 4 + + This test checks a Data_Object function for MSYS_DataObjects. (Non-Reallocate) + + This test attempts to reserve memory with the length of s1 + s2, then store + (concat) s1 in test_object_1, then run the test function with an offset of position 3 and data of s2. + + The result should be for a non insert function, that test_object_1 contains s1's data up to position 3, + at which it contains s2's data, and the remaining data is s1's remaining data after position 3. + Capacity and size equaling s1.size(). + + The result should be for a insert function, that test_object_1 contains s1's data up to position 3, + at which it contains s2's data, and the remaining data is s1's remaining data including position 3. + Capacity and size equaling (s1.size() + s2.size()). + */ + + /* Clear test_object_1 and test_object_2. */ + test_object_1.clear(); + test_object_2.clear(); + + /* Make sure that test_object_1 and test_object_2 are empty. */ + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* Reserve memory for s1.size() (Non-Insert function) OR (s1.size() + s2.size()) (Insert function). */ + test_object_1.reserve(((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))); + + /* Check for reserved memory. */ + if (test_object_1.size() != 0) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to reserve(). */ + return -1008; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to reserve(). */ + return -1007; + } + + /* Concat s1 into test_object_1, and concat s2 into test_object_2. */ + test_object_1 += s1; + test_object_2 += s2; + + /* + Make sure that test_object_1's size and capacity are equal to s1. + Also make sure that test_object_2's size and capacity are equal to s2. + */ + if ((test_object_1.size() != s1.size()) || (test_object_2.size() != s2.size())) + { + /* DataProcess::Data_Object::reserve() Invalid size after call to operator+= std::string. */ + return -1010; + } + if ((test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))) + || (test_object_2.get_Capacity() != s2.size())) + { + /* DataProcess::Data_Object::reserve() Invalid capacity after call to operator+= std::string. */ + return -1009; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Get pointer for test_object_2. */ + p1 = NULL; + if ((p1 = test_object_2.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to string. */ + p2 = NULL; + if ((p2 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_2 actually contains the correct data. */ + for (size_t x = 0; x < test_object_2.size(); x++) + { + if (p1[x] != p2[x]) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Run test function. */ + if (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 3, test_object_2_c_ptr) != COMMON_ERROR_SUCCESS) + { + /* Test function failed. */ + return -1020; + } + + /* + Make sure that test_object_1's size and capacity are equal to s1.size() (Non-insert function) OR + (s1.size() + s2.size()) (Insert function). + */ + if (test_object_1.size() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))) + { + /* DataProcess::Data_Object::operator= Invalid size after call to DataProcess::Data_Object::operator= std::string. */ + return -1014; + } + if (test_object_1.get_Capacity() != ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (s1.size() + s2.size()) : (s1.size()))) + { + /* DataProcess::Data_Object::operator= Invalid capacity after call to DataProcess::Data_Object::operator= std::string. */ + return -1015; + } + + /* Get pointer for test_object_1. */ + p1 = NULL; + if ((p1 = test_object_1.get_Pointer()) == NULL) + { + /* DataProcess::Data_Object::get_Pointer() Could not get pointer to test_object's data. */ + return -1003; + } + + /* Get pointer to strings s1 and s2. */ + p2 = NULL; + if ((p2 = s1.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + p3 = NULL; + if ((p3 = s2.c_str()) == NULL) + { + /* Could not get pointer to string object. */ + return -9001; + } + + /* Make sure that test_object_1 actually contains the correct data. */ + for (size_t x = 0; x < test_object_1.size(); x++) + { + /* + Check for s1 if the current offset is less than 3. + + Check for s2 if the current offset is greater than or equal to 3 but less than + s2's length. + + If the current offset is greater than 3 + s2's length, then: + - If the function we are testing is a non-insert function, check for the remaining s1 data at offset (x). + - Otherwise check for the remaining data from s1 at offset (x - s2.size()). + */ + if (p1[x] != ((x < 3) ? (p2[x]) : + ((x < (3 + s2.size())) ? (p3[(x - 3)]) : + ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].isInsert) ? (p2[(x - s2.size())]) : + (p2[x]))))) + { + /* DataProcess::Data_Object (Manual compare) Invalid data in the data_object. */ + return -1004; + } + } + + /* Clear test_object_1 and test_object_2. */ + test_object_1.clear(); + test_object_2.clear(); + + /* Make sure that test_object_1 and test_object_2 are empty. */ + if ((test_object_1.size() != 0) || (test_object_2.size() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid size after clear(). */ + return -1005; + } + if ((test_object_1.get_Capacity() != 0) || (test_object_2.get_Capacity() != 0)) + { + /* DataProcess::Data_Object::clear() Invalid capacity after clear(). */ + return -1006; + } + + /* + Invalid Data_Object pointer check. + + Should return COMMON_ERROR_INVALID_ARGUMENT. + */ + if ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(NULL, 3, test_object_1_c_ptr) != COMMON_ERROR_INVALID_ARGUMENT) || + (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 3, NULL) != COMMON_ERROR_INVALID_ARGUMENT)) + { + /* Test function failure. Should have returned COMMON_ERROR_INVALID_ARGUMENT. */ + return -1020; + } + + /* + Invalid private object pointer check. + + Should return COMMON_ERROR_INVALID_ARGUMENT. + */ + if ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(&badPrivPtr, 3, test_object_1_c_ptr) != COMMON_ERROR_INVALID_ARGUMENT) || + (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 3, &badPrivPtr) != COMMON_ERROR_INVALID_ARGUMENT)) + { + /* Test function failure. Should have returned COMMON_ERROR_INVALID_ARGUMENT. */ + return -1020; + } + + /* + Invalid Data Object tests. + + All of the following tests should return COMMON_ERROR_DATA_CORRUPTION. + Note: The tests where test_object_1 is the source should return COMMON_ERROR_NO_DATA, as it was (should have been) cleared. + */ + if ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(&badData, 0, test_object_1_c_ptr) != COMMON_ERROR_NO_DATA) || + (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, &badData) != COMMON_ERROR_DATA_CORRUPTION)) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + if ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(&badLength, 0, test_object_1_c_ptr) != COMMON_ERROR_NO_DATA) || + (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, &badLength) != COMMON_ERROR_DATA_CORRUPTION)) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + if ((Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(&badCapacity, 0, test_object_1_c_ptr) != COMMON_ERROR_NO_DATA) || + (Unit_Test_Data_Object_From_Data_Object_Functions_To_Test[y].fp(test_object_1_c_ptr, 0, &badCapacity) != COMMON_ERROR_DATA_CORRUPTION)) + { + /* Test function failure. Should have returned COMMON_ERROR_DATA_CORRUPTION. */ + return -1020; + } + + /* Output result of tests. */ + std::cout << "PASS\n"; + std::cout.flush(); + + } /* End test loop. */ + + /* Exit function. */ + return 0; +} diff --git a/src/Tests/Unit_Test_Rendering_Subsystem.cpp b/src/Tests/Unit_Test_Rendering_Subsystem.cpp new file mode 100644 index 0000000..cd467e9 --- /dev/null +++ b/src/Tests/Unit_Test_Rendering_Subsystem.cpp @@ -0,0 +1,476 @@ +/*! + Basic Test for the Common namespace Rendering Subsystem. 25/12/2013 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +#include "Unit_Tests.h" + +bool Renderer_Text_Console_Blit_Text_No_Overlay_Test_Function(const char & blank_value, const char * testArray, const size_t & testArraySize, const size_t & xResolution, const size_t & yResolution, const size_t & colorDepth, + const bool & fullscreen) +{ + // Init vars. + bool result = false; // The result of this test function. +#ifndef _NDEBUG + const char * imageBuffer = NULL; // Only used in a debug build, this holds a pointer to the final image buffer made by the renderer. + const char * overlayBuffer = NULL; // Only used in a debug build, this holds a pointer to an overlay image buffer made by the renderer. +#endif + Common::Renderer::Renderer_Text_Console renderer; // The renderer class we are testing. + + // Set the blank value. + renderer.Set_Blank_Value(blank_value); + + // Set the resolution. + if (renderer.Change_Resolution(xResolution, yResolution, colorDepth, fullscreen)) + { + // Call Blit_Text(). + if (renderer.Blit_Text(testArray, 0, true, testArraySize, 1)) + { +// Show it. + std::cout << "Attempting to render the test plane:\n\n"; + if (renderer.Render()) + { +#ifndef _NDEBUG + // Because the Return_Current_Final_Image_Buffer() function is a debug function, we have to isolate it. + // Get the pointer. + imageBuffer = renderer.Return_Current_Final_Image_Buffer(); + if (imageBuffer != NULL) + { + // Inform user. + std::cout << "\n\nChecking result of render: "; + std::cout.flush(); + if (memcmp(testArray, imageBuffer, testArraySize) == 0) + { + // Test successful. + std::cout << "PASS\n"; + std::cout.flush(); + result = true; + } + else + { + // Test failure. + std::cout << "FAIL\n" << "Rendered image does not match expected result, there is a bug in the renderer.\n"; + std::cout << "The result should be:\n" << testArray << "\n"; + + // Attempt to get the working buffer pointer. + overlayBuffer = renderer.Return_Current_Overlay_Image_Buffer(); + if (overlayBuffer != NULL) + { + // Output the working buffer data. + std::cout << "The working buffer data was: " << overlayBuffer << '\n'; + + // Check to see if the working buffer is valid. + if (memcmp(testArray, overlayBuffer, testArraySize) == 0) + { + std::cout << "Generated overlay data is correct, bug is located in the Render() function.\n"; + std::cout.flush(); + } + else + { + std::cout << "Generated overlay data is invalid, at least one bug is present in the Blit_Text() function.\n"; + std::cout.flush(); + } + } + else + { + // Could not retrive pointer to the working buffer data. + std::cout << "ERROR: Unable to retrive a pointer to the working buffer data.\n"; + } + std::cout.flush(); + result = false; + } + } + else + { + // Error. + std::cout << "ERROR: Debug pointer to renderer's image buffer is invalid.\n\n"; + result = false; + } +#else + // Inform user. + std::cout << "\n\nRender complete, unable to check result as this is a release build of the Unit_Tests program.\n" << "If this is not the desired result, file a bug.\n"; + + // Set result. + result = true; +#endif + } + else + { + // Render() failed. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Render() failed.\n"; + std::cout << "Last error from renderer: " << renderer.Get_Last_Error() << "\n\n"; + } + } + else + { + // Blit_Text() failed. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Blit_Text() failed.\n"; + std::cout << "Last error from class: " << renderer.Get_Last_Error() << "\n\n"; + } + } + else + { + // Error could not change resolution. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Change_Resolution() failed.\n"; + std::cout << "Last error from class: " << renderer.Get_Last_Error() << "\n\n"; + } + + // Exit function. + return result; +} + +bool Renderer_Text_Console_Blit_Text_No_Overlay_Transformation_Test_Function(const char & blank_value, const char * testArray, const size_t & testArrayXResolution, const size_t & testArrayYResolution, + const size_t & testColorDepth, const long & testArrayXOffset, const long & testArrayYOffset, + const char * resultArray, const size_t & resultArraySize, const size_t & resultXResolution, const size_t & resultYResolution, + const size_t & resultColorDepth, const bool & fullscreen) +{ + // Init vars. + bool result = false; // The result of this test function. +#ifndef _NDEBUG + const char * imageBuffer = NULL; // Only used in a debug build, this holds a pointer to the final image buffer made by the renderer. + const char * overlayBuffer = NULL; // Only used in a debug build, this holds a pointer to an overlay image buffer made by the renderer. +#endif + Common::Renderer::Renderer_Text_Console renderer; // The renderer class we are testing. + + // Set the blank value. + renderer.Set_Blank_Value(blank_value); + + // Set the resolution. + if (renderer.Change_Resolution(resultXResolution, resultYResolution, resultColorDepth, fullscreen)) + { + // Call Blit_Text(). (The additional args control offsets.) + if (renderer.Blit_Text(testArray, 0, true, testArrayXResolution, testArrayYResolution)) + { + // Set the overlay's offsets. + renderer.Set_Overlay_Offsets(testArrayXOffset, testArrayYOffset); + + // Show it. + std::cout << "Attempting to render the test plane:\n\n"; + std::cout.flush(); + if (renderer.Render()) + { +#ifndef _NDEBUG + // Because the Return_Current_Final_Image_Buffer() function is a debug function, we have to isolate it. + // Get the pointer. + imageBuffer = renderer.Return_Current_Final_Image_Buffer(); + if (imageBuffer != NULL) + { + // Inform user. + std::cout << "\n\nChecking result of render: "; + std::cout.flush(); + if (memcmp(resultArray, imageBuffer, (testArrayXResolution * testArrayYResolution)) == 0) + { + // Test successful. + std::cout << "PASS\n"; + std::cout.flush(); + result = true; + } + else + { + // Test failure. + std::cout << "FAIL\n" << "Rendered image does not match expected result, there is a bug in the renderer.\n"; + std::cout << "The result should be:\n"; + for (size_t x = 0; x < (testArrayXResolution * testArrayYResolution); x++) + { + std::cout << resultArray[x]; + } + std::cout << '\n'; + std::cout.flush(); + + // Attempt to get the working buffer pointer. + overlayBuffer = renderer.Return_Current_Overlay_Image_Buffer(); + if (overlayBuffer != NULL) + { + // Output the working buffer data. + std::cout << "The working buffer data was:\n"; + for (size_t x = 0; x < (testArrayXResolution * testArrayYResolution); x++) + { + std::cout << overlayBuffer[x]; + } + std::cout << '\n'; + std::cout.flush(); + + // Check to see if the working buffer is valid. + if (memcmp(testArray, overlayBuffer, (testArrayXResolution * testArrayYResolution)) == 0) + { + std::cout << "Generated overlay data is correct, bug is located in the Render() function.\n"; + std::cout.flush(); + } + else + { + std::cout << "Generated overlay data is invalid, at least one bug is present in the Blit_Text() function.\n"; + std::cout << "The working buffer data should be:\n"; + for (size_t x = 0; x < (testArrayXResolution * testArrayYResolution); x++) + { + std::cout << testArray[x]; + } + std::cout << '\n'; + std::cout.flush(); + } + } + else + { + // Could not retrive pointer to the working buffer data. + std::cout << "ERROR: Unable to retrive a pointer to the working buffer data.\n"; + } + std::cout.flush(); + result = false; + } + } + else + { + // Error. + std::cout << "ERROR: Debug pointer to renderer's image buffer is invalid.\n\n"; + result = false; + } +#else + // Inform user. + std::cout << "\n\nRender complete, unable to check result as this is a release build of the Unit_Tests program.\n" << "If this is not the desired result, file a bug.\n"; + + // Set result. + result = true; +#endif + } + else + { + // Render() failed. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Render() failed.\n"; + std::cout << "Last error from renderer: " << renderer.Get_Last_Error() << "\n\n"; + } + } + else + { + // Blit_Text() failed. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Blit_Text() failed.\n"; + std::cout << "Last error from class: " << renderer.Get_Last_Error() << "\n\n"; + } + } + else + { + // Error could not change resolution. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Change_Resolution() failed.\n"; + std::cout << "Last error from class: " << renderer.Get_Last_Error() << "\n\n"; + } + + // Exit function. + return result; +} + +bool Renderer_Text_Console_Basic_No_Overlay_Test() +{ + // Init vars. + bool result = false; // The result of this test function. +#ifndef _NDEBUG + const char * imageBuffer = NULL; // Only used in a debug build, this holds a pointer to the final image buffer made by the renderer. + const char * overlayBuffer = NULL; // Only used in a debug build, this holds a pointer to an overlay image buffer made by the renderer. +#endif + Common::Renderer::Renderer_Text_Console renderer; // The renderer class we are testing. + + // Define the used values. + std::cout << "Dot value: (" << RENDER_TEXT_CHAR_DOT << ")\n"; + std::cout << "Blank value: (" << RENDER_TEXT_BLANK_VALUE << ")\n"; + std::cout.flush(); + + // Set the blank value. + renderer.Set_Blank_Value(RENDER_TEXT_BLANK_VALUE); + + // Set the resolution as 3x3. + if (renderer.Change_Resolution(3, 3, 2, false)) + { + // Blit a dot. + if (renderer.Blit_Text(RENDER_TEXT_BASIC_TEST_RESULT, 0, true, (sizeof(RENDER_TEXT_BASIC_TEST_RESULT) / sizeof(char)), 1)) // Should produce a '.' at the center of the plane. + { + // Show it. + std::cout << "Attempting to render a 3x3 plane with a dot at the center:\n\n"; + if (renderer.Render()) + { +#ifndef _NDEBUG + // Because the Return_Current_Final_Image_Buffer() function is a debug function, we have to isolate it. + // Get the pointer. + imageBuffer = renderer.Return_Current_Final_Image_Buffer(); + if (imageBuffer != NULL) + { + // Inform user. + std::cout << "\n\nChecking result of render: "; + std::cout.flush(); + if (memcmp(&RENDER_TEXT_BASIC_TEST_RESULT, imageBuffer, (sizeof(RENDER_TEXT_BASIC_TEST_RESULT) / sizeof(char))) == 0) + { + // Test successful. + std::cout << "PASS\n"; + std::cout.flush(); + result = true; + } + else + { + // Test failure. + std::cout << "FAIL\n" << "Rendered image does not match expected result, there is a bug in the renderer.\n"; + std::cout << "The result should be:\n" << RENDER_TEXT_BASIC_TEST_RESULT << "\n"; + + // Attempt to get the working buffer pointer. + overlayBuffer = renderer.Return_Current_Overlay_Image_Buffer(); + if (overlayBuffer != NULL) + { + // Output the working buffer data. + std::cout << "The working buffer data was: " << overlayBuffer << '\n'; + + // Check to see if the working buffer is valid. + if (memcmp(&RENDER_TEXT_BASIC_TEST_RESULT, overlayBuffer, (sizeof(RENDER_TEXT_BASIC_TEST_RESULT) / sizeof(char))) == 0) + { + std::cout << "Generated overlay data is correct, bug is located in the Render() function.\n"; + std::cout.flush(); + } + else + { + std::cout << "Generated overlay data is invalid, at least one bug is present in the Blit_Text() function.\n"; + std::cout.flush(); + } + } + else + { + // Could not retrive pointer to the working buffer data. + std::cout << "ERROR: Unable to retrive a pointer to the working buffer data.\n"; + } + std::cout.flush(); + result = false; + } + } + else + { + // Error. + std::cout << "ERROR: Debug pointer to renderer's image buffer is invalid.\n\n"; + result = false; + } +#else + // Inform user. + std::cout << "\n\nRender complete, unable to check result as this is a release build of the Unit_Tests program.\n" << "If this is not the desired result, file a bug.\n"; + + // Set result. + result = true; +#endif + } + else + { + // Render() failed. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Render() failed.\n"; + std::cout << "Last error from renderer: " << renderer.Get_Last_Error() << "\n\n"; + } + } + else + { + // Blit_Text() failed. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Blit_Text() failed.\n"; + std::cout << "Last error from class: " << renderer.Get_Last_Error() << "\n\n"; + } + } + else + { + // Error could not change resolution. + std::cout << "\n\nERROR: Renderer_Text_Console_Basic_No_Overlay_Test(): renderer.Change_Resolution() failed.\n"; + std::cout << "Last error from class: " << renderer.Get_Last_Error() << "\n\n"; + } + + // Exit function. + return result; +} + +short Unit_Test_Rendering_Subsystem_Main() +{ + // Init result. + short result = 0; + + // Output START OF TEST SECTION. + std::cout << START_TEST_SECTION; + + // Enable debug output. + Common::Renderer::DebugChannel.change_log_level(ERROR_VERBOSE); + + // Tell the user the Rendering Subsystem API Version. + std::cout << "Rendering Subsystem Version: v" << Common::Renderer::Get_Rendering_Subsystem_API_Major_Version_Number() << '.'; + std::cout << Common::Renderer::Get_Rendering_Subsystem_API_Minor_Version_Number() << '.'; + std::cout << Common::Renderer::Get_Rendering_Subsystem_API_Revision_Version_Number() << ".\n"; + std::cout.flush(); + + // Tell the user we are currently testing the Renderer_Text_Console class. + std::cout << "Performing tests on the Renderer_Text_Console class.\n"; + std::cout << "Please wait.\n"; + std::cout.flush(); + + // Call Renderer_Text_Console_Basic_No_Overlay_Test(). + if (!Renderer_Text_Console_Basic_No_Overlay_Test()) + { + result = -1; + } + else + { + // Run the basic test again, but use the generic test function to do it. + std::cout << "Rerunning basic test with generic test function.\n"; + std::cout.flush(); + if(!Renderer_Text_Console_Blit_Text_No_Overlay_Test_Function(RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BASIC_TEST_RESULT, (sizeof(RENDER_TEXT_BASIC_TEST_RESULT) / sizeof(char)), 3, 3, 2, false)) + { + result = -2; + } + else + { + // Run the edge test. + std::cout << "Running edge test.\n"; + std::cout.flush(); + if (!Renderer_Text_Console_Blit_Text_No_Overlay_Test_Function(RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_EDGE_TEST_RESULT, (sizeof(RENDER_TEXT_EDGE_TEST_RESULT) / sizeof(char)), 3, 3, 2, false)) + { + result = -3; + } + else + { + // Run the shift down tests. + /*std::cout << "Running the left shift down test.\n"; + std::cout.flush(); + if (!Renderer_Text_Console_Blit_Text_No_Overlay_Test_Function(RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_EDGE_TEST_RESULT, (sizeof(RENDER_TEXT_EDGE_TEST_RESULT) / sizeof(char)), 3, 3, 2, false)) + { + result = -4; + } + else + {*/ + std::cout << "Running the right shift down test.\n"; + std::cout.flush(); + if (!Renderer_Text_Console_Blit_Text_No_Overlay_Transformation_Test_Function(RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_EDGE_TEST_RESULT, 3, 3, 2, 1, 1, + RENDER_TEXT_SHIFT_RIGHT_DOWN_TEST_RESULT, (sizeof(RENDER_TEXT_SHIFT_RIGHT_DOWN_TEST_RESULT) / sizeof(char)), 3, 3, + 2, false)) + { + result = -5; + } + else + { + std::cout << "Running the left shift up test.\n"; + std::cout.flush(); + if (!Renderer_Text_Console_Blit_Text_No_Overlay_Transformation_Test_Function(RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_EDGE_TEST_RESULT, 3, 3, 2, -1, -1, + RENDER_TEXT_SHIFT_LEFT_UP_TEST_RESULT, (sizeof(RENDER_TEXT_SHIFT_LEFT_UP_TEST_RESULT) / sizeof(char)), 3, 3, + 2, false)) + { + result = -6; + } + } + //} + } + } + } + + // Output END OF TEST SECTION. + std::cout << END_TEST_SECTION; + std::cout.flush(); + + // Return result. + return result; +} diff --git a/src/Tests/Unit_Test_Rendering_Subsystem.h b/src/Tests/Unit_Test_Rendering_Subsystem.h new file mode 100644 index 0000000..97d1528 --- /dev/null +++ b/src/Tests/Unit_Test_Rendering_Subsystem.h @@ -0,0 +1,155 @@ +/*! + Basic Test for the Common namespace Rendering Subsystem. 25/12/2013 + + Copyright (C) 2014 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +// Include guard. +#ifndef RENDER_SUBSYS_UNIT_TESTS +#define RENDER_SUBSYS_UNIT_TESTS + +/* Only continue if the compiler is a C++ compiler. */ +#ifdef __cplusplus + +// Include base header from Rendering Subsystem. +#include "../Common/Src/Rendering_Subsystem/Renderer.h" + +// Define render data. +#ifndef RENDER_TEXT_CHAR_DOT +#define RENDER_TEXT_CHAR_DOT '.' +#endif + +#ifndef RENDER_TEXT_BLANK_VALUE +#define RENDER_TEXT_BLANK_VALUE 'X' +#endif + +// Define expected results from the render. +// Basic test result (Resolution: 3x3) +const static char RENDER_TEXT_BASIC_TEST_RESULT[] = {RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, + RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_BLANK_VALUE, + RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE}; + +// Edge test result (Resolution: 3x3) (Basicly an inverted basic test. Checks to see if the edges of a render are kept intact.) +const static char RENDER_TEXT_EDGE_TEST_RESULT[] = {RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, + RENDER_TEXT_CHAR_DOT, RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_CHAR_DOT, + RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT}; + +// Shift right down test result. (Resolution: 3x3) (The edge test result, shifted down and to the right by one.) +const static char RENDER_TEXT_SHIFT_RIGHT_DOWN_TEST_RESULT[] = {RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, + RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, + RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_BLANK_VALUE}; + +// Shift right up test result. (Resolution: 3x3) (The edge test result, shifted up and to the right by one.) +const static char RENDER_TEXT_SHIFT_RIGHT_UP_TEST_RESULT[] = {RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_BLANK_VALUE, + RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, + RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE}; + +// Shift left down test result. (Resolution: 3x3) (The edge test result, shifted down and to the left by one.) +const static char RENDER_TEXT_SHIFT_LEFT_DOWN_TEST_RESULT[] = {RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, + RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_BLANK_VALUE, + RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_BLANK_VALUE}; + +// Shift left up test result. (Resolution: 3x3) (The edge test result, shifted up and to the left by one.) +const static char RENDER_TEXT_SHIFT_LEFT_UP_TEST_RESULT[] = {RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_BLANK_VALUE, + RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_BLANK_VALUE, + RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE, RENDER_TEXT_BLANK_VALUE}; + +// Fill test result. (Resolution: 3x3) (A basic solid fill pattern, that fills up the entire overlay.) +const static char RENDER_TEXT_BASIC_FILL_TEST_RESULT[] = {RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, + RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, + RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT, RENDER_TEXT_CHAR_DOT}; + +// Define test functions. +/*! + * bool Renderer_Text_Console_Blit_Text_No_Overlay_Test_Function(const char & blank_value, const char * testArray, const size_t & testArraySize, const size_t & xResolution, const size_t & yResolution, + * const size_t & colorDepth, const bool & fullscreen) + * + * A generic version of Renderer_Text_Console_Basic_No_Overlay_Test() that accepts + * any test array, instead of using a hardcoded test. + * + * Pram: const char & blank_value, the value to use for empty space. (This will appear in the final image, wherever you do not place any data in the overlays / working buffer. It IS checked for in the result.) + * Pram: const char * testArray, a pointer to the array to test. (Array must be one dimentional.) + * Pram: const size_t & testArraySize, the total length of the array. + * Pram: const size_t & xResolution, the horazontal "row" length of the final image. + * Pram: const size_t & yResolution, the vertical "column" length of the final image. + * Pram: const size_t & colorDepth, the color depth of the final image. (Not currently used by the Common::Renderer::Renderer_Text_Console renderer.) + * Pram: const bool & fullscreen, whether or not the given resolution above takes up the entire window / screen. (If false a newline will be output after each row.) + * + * Returns true if the call to render() is successful. (Or in a debug build, if the rendered image matches the testArray data.) + * Returns false otherwise. + */ +bool Renderer_Text_Console_Blit_Text_No_Overlay_Test_Function(const char & blank_value, const char * testArray, const size_t & testArraySize, const size_t & xResolution, const size_t & yResolution, const size_t & colorDepth, + const bool & fullscreen); + +/*! + * bool Renderer_Text_Console_Blit_Text_No_Overlay_Transformation_Test_Function(const char & blank_value, const char * testArray, const size_t & testArraySize, const size_t & testXResolution, const size_t & testYResolution, + * const size_t & testColorDepth, const size_t & testResultXOffset, const size_t & testResultYOffset, + * const char * resultArray, const size_t & resultArraySize, const size_t & resultXResolution, const size_t & resultYResolution, + * const size_t & resultColorDepth, const bool & fullscreen) + * + * A function to test basic transformations on overlays. + * + * Pram: const char & blank_value, the value to use for empty space. (This will appear in the final image, wherever you do not place any data in the overlays / working buffer. It IS checked for in the result.) + * Pram: const char * testArray, a pointer to the array to test. (Array must be one dimentional.) + * Pram: const size_t & testXResolution, the horazontal "row" length of the testArray. + * Pram: const size_t & testYResolution, the vertical "column" length of the testArray. + * Pram: const size_t & testColorDepth, the color depth of the testArray. (Not currently used by the Common::Renderer::Renderer_Text_Console renderer.) + * Pram: const long & testArrayXOffset, the offset from the origin point on the generated final image where the x origin point of the testArray will be placed. + * Pram: const long & testArrayYOffset, the offset from the origin point on the generated final image where the y origin point of the testArray will be placed. + * Pram: const char * resultArray, a pointer to the array that contains the expected final image. (Array must be one dimentional.) + * Pram: const size_t & resultArraySize, the total length of the resultArray. + * Pram: const size_t & resultXResolution, the horazontal "row" length of the resultArray. + * Pram: const size_t & resultYResolution, the vertical "column" length of the resultArray. + * Pram: const size_t & resultColorDepth, the color depth of the resultArray. (Not currently used by the Common::Renderer::Renderer_Text_Console renderer.) + * Pram: const bool & fullscreen, whether or not the given resolution above takes up the entire window / screen. (If false a newline will be output after each row.) + * + * Returns true if the call to render() is successful. (Or in a debug build, if the rendered image matches the testArray data.) + * Returns false otherwise. + */ +bool Renderer_Text_Console_Blit_Text_No_Overlay_Transformation_Test_Function(const char & blank_value, const char * testArray, const size_t & testXResolution, const size_t & testYResolution, + const size_t & testColorDepth, const long & testArrayXOffset, const long & testArrayYOffset, + const char * resultArray, const size_t & resultArraySize, const size_t & resultXResolution, const size_t & resultYResolution, + const size_t & resultColorDepth, const bool & fullscreen); + +/*! + * bool Renderer_Text_Console_Basic_No_Overlay_Test() + * + * Performs a basic "does it do anything" test on the + * Renderer_Text_Console class, by trying to create + * a dot at the center of the grid. + * + * Returns true if the call to render() succeeds. + * Returns false otherwise. + */ +bool Renderer_Text_Console_Basic_No_Overlay_Test(); + +/*! + * short Unit_Test_Rendering_Subsystem_Main() + * + * Main test function for the rendering subsystem unit tests. + * Performs all tests. + * + * Returns zero if all tests succeed. + * Returns a negative number if a test fails. + */ +short Unit_Test_Rendering_Subsystem_Main(); + +#endif /* __cplusplus */ + +#endif /* RENDER_SUBSYS_UNIT_TESTS */ + +// End of Unit_Test_Rendering_Subsystem.h diff --git a/src/Tests/Unit_Tests.cpp b/src/Tests/Unit_Tests.cpp index 56f4df9..acabc05 100644 --- a/src/Tests/Unit_Tests.cpp +++ b/src/Tests/Unit_Tests.cpp @@ -24,15 +24,24 @@ int main() { // Delcare vars. + int error_code_dataprocess = 0; short error_code_data_object = 0; short error_code_fileutills = 0; short error_code_thread_utils = 0; + short error_code_rendering_subsys = 0; + int error_code_byte_order = 0; // Starting Unit tests. std::cout << "Multiverse_Engine_Project_Public Unit Tests Compiled on: " << TESTCOMPILEDATE << " " << TESTCOMPILETIME << "\n"; - std::cout << "Starting Unit tests for DataProcess::Data_Object. Please be pacent this can take some time.\n"; + std::cout << "Starting Unit tests for DataProcess functions.\n"; + error_code_dataprocess = Unit_Tests_DataProcess_Main(); + + std::cout << "Starting Unit tests for DataProcess::Data_Object. Please be patient this can take some time.\n"; error_code_data_object = Unit_Test_Data_Object(); + std::cout << "Starting unit tests for Byte_Order. Please wait.\n"; + error_code_byte_order = Unit_Test_Byte_Order_Main(); + /* Only call FileUtills tests if FileUtills was built. */ #ifdef MSYS_HAVE_FILEUTILLS std::cout << "Starting FileUtills Tests.\n"; @@ -49,6 +58,14 @@ int main() std::cout << "Thread_Utils was disabled at build time. Skipping Thread_Utils tests.\n"; #endif /* MSYS_HAVE_THREAD_UTILS */ +/* Only call Rendering_Subsystem tests if Rendering_Subsystem was built. */ +#ifdef MSYS_HAVE_RENDERING_SUBSYS + std::cout << "Starting Rendering_Subsystem Tests.\n"; + error_code_rendering_subsys = Unit_Test_Rendering_Subsystem_Main(); +#else + std::cout << "Rendering_Subsystem was disabled at build time. Skipping Rendering_Subsystem tests.\n"; +#endif /* MSYS_HAVE_RENDERING_SUBSYS */ + // Output overall test results. std::cout << "Overall Test Results:\n\n"; @@ -91,6 +108,19 @@ int main() } #endif /* MSYS_HAVE_THREAD_UTILS */ +#ifdef MSYS_HAVE_RENDERING_SUBSYS + std::cout << "Rendering_Subsystem Tests: "; + if (error_code_rendering_subsys != 0) + { + std::cout << "FAIL\n"; + std::cout << "Returned error code was: " << error_code_rendering_subsys << ".\n"; + } + else + { + std::cout << "PASS\n"; + } +#endif /* MSYS_HAVE_RENDERING_SUBSYS */ + /* Exit test program. */ return 0; } diff --git a/src/Tests/Unit_Tests_Byte_Order.h b/src/Tests/Unit_Tests_Byte_Order.h new file mode 100644 index 0000000..6f57575 --- /dev/null +++ b/src/Tests/Unit_Tests_Byte_Order.h @@ -0,0 +1,80 @@ +/*! + Multiverse Engine Project 11/7/2015 Unit Tests Unit_Tests_Byte_Order.h + + Copyright (C) 2015 Multiverse Engine Project + + This program is free software; + you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with this program; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Official source repository and project information can be found at + https://github.com/codebase7/mengine +*/ + +/* Include guard. */ +#ifndef UNIT_TESTS_BYTE_ORDER_H +#define UNIT_TESTS_BYTE_ORDER_H + +/* Internal includes. */ +#include "../Common/Src/Byte_Order/Byte_Order.h" + +/* Define test functions. */ +/*! + * void Print_Random_Bits(const char bits) + * + * Wrapper around Common_Print_Bytes_In_Binary() and + * Common_Deallocate_Print_Bytes_CString() to print + * the given byte to the standard output. + * + * This function has no return. + */ +void Print_Random_Bits(const char bits); + +/*! + * char Generate_Random_Bit_Mask(const char bitMask) + * + * Generates some random bits between 1 and 254, retrying if it + * generates the given bitMask, while telling the user + * it is doing so. + * + * Returns the generated bit mask. + */ +char Generate_Random_Bit_Mask(const char bitMask); + +/*! + * char Generate_Random_Bits(const char notThisValue) + * + * Generates some random bits between 2 and 254, retrying if it + * generates the given notThisValue, while telling the user + * it is doing so. + * + * Returns the generated bits. + */ +char Generate_Random_Bits(const char notThisValue); + +int unit_test_byte_order_Common_Print_Bytes_In_Binary_check(); + +/*! + * int unit_test_byte_order_comparison_check() + * + * Performs tests on the Byte_Order_Bit_Comparison() function. + */ +int unit_test_byte_order_comparison_check(); + +/*! + * int Unit_Test_Byte_Order_Main() + * + * Main test function for Byte_Order. + */ +int Unit_Test_Byte_Order_Main(); + +#endif /* UNIT_TESTS_BYTE_ORDER_H */ + +/* End of Unit_Tests_Byte_Order.h */ diff --git a/src/Tests/Unit_Tests_Common_Error_Handler.cpp b/src/Tests/Unit_Tests_Common_Error_Handler.cpp index 1dcf0c9..bd345bc 100644 --- a/src/Tests/Unit_Tests_Common_Error_Handler.cpp +++ b/src/Tests/Unit_Tests_Common_Error_Handler.cpp @@ -24,12 +24,26 @@ /* External includes. */ #include -void Common_Error_Log_Callback(const unsigned int logLevel, const char * errorMsg) +void Common_Error_Log_Callback(const int channelID, const unsigned int logLevel, const char * errorMsg) { + /* Init vars. */ + size_t nameLength = 0; + const char * name = NULL; + int retFromCall = COMMON_ERROR_UNKNOWN_ERROR; + // Print to std::cout. if (errorMsg != NULL) { - std::cout << errorMsg; + /* Attempt to get the Channel Name. */ + retFromCall = Common_Error_Get_Logging_Channel_Name_By_ID_Number(channelID, &name, &nameLength); + if ((retFromCall == COMMON_ERROR_SUCCESS) && (name != NULL) && (nameLength > 0)) + { + std::cout << name << ": " << errorMsg; + } + else + { + std::cout << errorMsg; + } std::cout.flush(); } diff --git a/src/Tests/Unit_Tests_Common_Error_Handler.h b/src/Tests/Unit_Tests_Common_Error_Handler.h index 31bf453..9e42f76 100644 --- a/src/Tests/Unit_Tests_Common_Error_Handler.h +++ b/src/Tests/Unit_Tests_Common_Error_Handler.h @@ -26,12 +26,12 @@ #include "../Common/Src/Error_Handler/Common_Error_Handler.h" /*! - * void Common_Error_Log_Callback(const unsigned int logLevel, const char * errorMsg) + * void Common_Error_Log_Callback(const int channelID, const unsigned int logLevel, const char * errorMsg) * * Callback function for Common::Register_Error_Log_Callback(). * Displays given error message on standard output. */ -void Common_Error_Log_Callback(const unsigned int logLevel, const char * errorMsg); +void Common_Error_Log_Callback(const int channelID, const unsigned int logLevel, const char * errorMsg); #endif /* UNIT_TESTS_COMMON_ERROR_HANDLER_H */ diff --git a/src/Common/Src/Mutexes/stdbool.h b/src/stdbool.h similarity index 76% rename from src/Common/Src/Mutexes/stdbool.h rename to src/stdbool.h index e7b9d91..056709f 100644 --- a/src/Common/Src/Mutexes/stdbool.h +++ b/src/stdbool.h @@ -23,9 +23,18 @@ #ifndef MSYS_STD_BOOL_H #define MSYS_STD_BOOL_H +/* This is only needed for C. */ +#ifndef __cplusplus + +/* Only define this if STD_BOOL is not defined. */ +#ifndef _STDBOOL + +/* Only define this if __bool_true_false_are_defined is not defined. */ +#ifndef __bool_true_false_are_defined + /* Define true and false. */ -#define TRUE 0x01 /* Really this could be anything. */ -#define FALSE 0x00 /* Litteral NULL byte to conform to ANYTHING NOT NULL IS TRUE. */ +#define TRUE 1 /* Really this could be anything. */ +#define FALSE 0 /* Litteral NULL byte to conform to ANYTHING NOT NULL IS TRUE. */ #define true TRUE #define false FALSE @@ -34,6 +43,9 @@ typedef char bool; /* This is a char to conform to the expectation in C++ that s /* Define __bool_true_false_are_defined (ISO) */ #define __bool_true_false_are_defined 1 +#endif /* __bool_true_false_are_defined */ +#endif /* _STDBOOL */ +#endif /* __cplusplus */ #endif /* MSYS_STD_BOOL_H */ diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/test.txt @@ -0,0 +1 @@ +test