commit aa1126569bdd3b12e8e1c317e9f51fc7c4ea1921 Author: JustinAJ Date: Fri May 16 12:45:19 2014 -0400 Initial Commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1bc915c --- /dev/null +++ b/.gitignore @@ -0,0 +1,156 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results + +[Dd]ebug/ +[Rr]elease/ +x64/ +build/ +[Bb]in/ +[Oo]bj/ + +# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets +!packages/*/build/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.log +*.scc + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.Publish.xml + +# NuGet Packages Directory +## TODO: If you have NuGet Package Restore enabled, uncomment the next line +#packages/ + +# Windows Azure Build Output +csx +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.[Pp]ublish.xml +*.pfx +*.publishsettings + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +App_Data/*.mdf +App_Data/*.ldf + + +#LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml + +# ========================= +# Windows detritus +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Mac desktop service store files +.DS_Store diff --git a/Jupiter.sln b/Jupiter.sln new file mode 100644 index 0000000..c4ef9ae --- /dev/null +++ b/Jupiter.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Jupiter", "Jupiter\Jupiter.vcxproj", "{367CBCA8-6F27-484A-BC6C-2FC087FBB0C8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tester", "Tester\Tester.vcxproj", "{0F041791-1047-4C6A-A4C1-814E6957D5EB}" + ProjectSection(ProjectDependencies) = postProject + {367CBCA8-6F27-484A-BC6C-2FC087FBB0C8} = {367CBCA8-6F27-484A-BC6C-2FC087FBB0C8} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {367CBCA8-6F27-484A-BC6C-2FC087FBB0C8}.Debug|Win32.ActiveCfg = Release|Win32 + {367CBCA8-6F27-484A-BC6C-2FC087FBB0C8}.Debug|Win32.Build.0 = Release|Win32 + {367CBCA8-6F27-484A-BC6C-2FC087FBB0C8}.Debug|Win32.Deploy.0 = Release|Win32 + {367CBCA8-6F27-484A-BC6C-2FC087FBB0C8}.Release|Win32.ActiveCfg = Release|Win32 + {367CBCA8-6F27-484A-BC6C-2FC087FBB0C8}.Release|Win32.Build.0 = Release|Win32 + {0F041791-1047-4C6A-A4C1-814E6957D5EB}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F041791-1047-4C6A-A4C1-814E6957D5EB}.Debug|Win32.Build.0 = Debug|Win32 + {0F041791-1047-4C6A-A4C1-814E6957D5EB}.Release|Win32.ActiveCfg = Release|Win32 + {0F041791-1047-4C6A-A4C1-814E6957D5EB}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Jupiter/ArrayList.h b/Jupiter/ArrayList.h new file mode 100644 index 0000000..caba7c2 --- /dev/null +++ b/Jupiter/ArrayList.h @@ -0,0 +1,179 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _ARRAYLIST_H_HEADER +#define _ARRAYLIST_H_HEADER + +#include "Jupiter.h" +#include "List.h" + +/** +* @file ArrayList.h +* @brief Provides an array-based list implementation using the List interface. +*/ + +namespace Jupiter +{ + /** + * @brief Provides an array-based list implementation using the List interface. + */ + template class ArrayList : public List + { + public: + + /** + * @brief Gets the data at a specified index. + * + * @param index Index of the data to get. + * @return Data stored at the specified index. + */ + T *get(unsigned int index) const; + + /** + * @brief Removes the data at a specified index from the list, and returns the removed data. + * + * @param n Index of the node to remove. + * @return Data removed. + */ + T *remove(unsigned int index); + + /** + * @brief Adds data to the list at a specified index. + * + * @param data Data to add to the list. + * @param index Position in the list to add the data to. + */ + void add(T *data, unsigned int index); + + /** + * @brief Adds data to the end of the list. + * + * @param data Data to add to the list. + */ + void add(T *data); + + /** + * @brief Empties the ArrayList of all elements. + */ + void empty(); + + /** + * @brief Remove and deletes all elements within the ArrayList. + */ + void emptyAndDelete(); + + /** + * @brief Default constructor for the ArrayList class. + */ + ArrayList(); + + /** + * @brief Copy constructor for the ArrayList class. + */ + ArrayList(const ArrayList &); + + /** + * @brief Destructor for the ArrayList class. + * Note: This does not delete data added to the list. + */ + ~ArrayList(); + + /** Access Operator */ + inline T *operator[](size_t index) { return this->get(index); }; + + /** Private members */ + private: + T **data; + unsigned int dataSize; + unsigned int expandArray(); + }; + +} + +// Implementation + +const unsigned int INIT_SIZE = 8; + +template unsigned int Jupiter::ArrayList::expandArray() +{ + T **tmp = new T *[Jupiter::ArrayList::dataSize * 2]; + for (unsigned int i = 0; i < Jupiter::ArrayList::dataSize; i++) tmp[i] = data[i]; + delete[] Jupiter::ArrayList::data; + Jupiter::ArrayList::data = tmp; + Jupiter::ArrayList::dataSize *= 2; + return Jupiter::ArrayList::dataSize; +} + +template Jupiter::ArrayList::ArrayList() +{ + Jupiter::ArrayList::dataSize = INIT_SIZE; + Jupiter::ArrayList::data = new T*[Jupiter::ArrayList::dataSize]; + Jupiter::List::length = 0; +} + +template Jupiter::ArrayList::ArrayList(const Jupiter::ArrayList &source) +{ + Jupiter::ArrayList::dataSize = source.dataSize; + Jupiter::ArrayList::data = new T*[Jupiter::ArrayList::dataSize]; + Jupiter::List::length = source.length; + for (unsigned int i = 0; i < Jupiter::List::length; i++) Jupiter::ArrayList::data[i] = source.data[i]; +} + +template Jupiter::ArrayList::~ArrayList() +{ + delete[] Jupiter::ArrayList::data; +} + +template T *Jupiter::ArrayList::get(unsigned int index) const +{ + return Jupiter::ArrayList::data[index]; +} + +template T *Jupiter::ArrayList::remove(unsigned int index) +{ + T *r = Jupiter::ArrayList::data[index]; + Jupiter::ArrayList::data[index] = nullptr; + for (unsigned int i = index + 1; i < Jupiter::List::length; i++) Jupiter::ArrayList::data[i - 1] = Jupiter::ArrayList::data[i]; + Jupiter::List::length--; + return r; +} + +template void Jupiter::ArrayList::add(T *ndata, unsigned int index) +{ + if (Jupiter::List::length == Jupiter::ArrayList::dataSize) Jupiter::ArrayList::expandArray(); + for (unsigned int i = Jupiter::List::length; i > index; i--) Jupiter::ArrayList::data[i] = Jupiter::ArrayList::data[i - 1]; + Jupiter::ArrayList::data[index] = ndata; + Jupiter::List::length++; +} + +template void Jupiter::ArrayList::add(T *ndata) +{ + Jupiter::ArrayList::add(ndata, Jupiter::List::length); +} + +template void Jupiter::ArrayList::empty() +{ + Jupiter::List::length = 0; +} + +template void Jupiter::ArrayList::emptyAndDelete() +{ + for (unsigned int i = 0; i < Jupiter::List::length; i++) delete Jupiter::ArrayList::data[i]; + Jupiter::List::length = 0; +} + +#endif // _ARRAYLIST_H_HEADER \ No newline at end of file diff --git a/Jupiter/Base64.cpp b/Jupiter/Base64.cpp new file mode 100644 index 0000000..05f037c --- /dev/null +++ b/Jupiter/Base64.cpp @@ -0,0 +1,49 @@ +#include "Base64.h" +#include "Functions.h" + +unsigned int Jupiter::base64encode(const void *data, size_t dataLength, char *result) +{ + return Jupiter_base64encode(data, dataLength, result); +} + +unsigned int Jupiter::base64encode(const void *data, size_t dataLength, char *result, size_t resultSize) +{ + if (resultSize < (dataLength / 3) * 4 + 5) return 0; + return Jupiter::base64encode(data, dataLength, result); +} + +char *Jupiter::base64encode(const void *data, size_t dataLength) +{ + size_t resultSize = (dataLength / 3) * 4 + 5; + char *result = new char[resultSize]; + Jupiter::base64encode(data, dataLength, result, resultSize); + return result; +} + +char *Jupiter::base64encode(const char *str) +{ + return Jupiter::base64encode(str, strlen(str)); +} + +unsigned int Jupiter::base64decode(const char *data, size_t dataLength, unsigned char *result) +{ + return Jupiter_base64decode2(data, dataLength, result); +} + +unsigned int Jupiter::base64decode(const char *data, size_t dataLength, unsigned char *result, size_t resultSize) +{ + if (resultSize < Jupiter_minBase64DecodeLength(data, dataLength)) return 0; + return Jupiter::base64decode(data, dataLength, result); +} + +char *Jupiter::base64decode(const char *data, size_t dataLength) +{ + char *result = new char[Jupiter_minBase64DecodeLength(data, dataLength) + 1]; + result[Jupiter_base64decode(data, (unsigned char *)result)] = 0; + return result; +} + +char *Jupiter::base64decode(const char *data) +{ + return Jupiter::base64decode(data, strlen(data)); +} diff --git a/Jupiter/Base64.h b/Jupiter/Base64.h new file mode 100644 index 0000000..683150f --- /dev/null +++ b/Jupiter/Base64.h @@ -0,0 +1,175 @@ +/** + * Copyright (C) 2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _BASE64_H_HEADER +#define _BASE64_H_HEADER + +#include "Jupiter.h" + +/** + * @file Base64.h + * @brief Provides C and C++ functions to encode/decode using base64. + */ + +#if defined __cplusplus + +#include + +namespace Jupiter +{ + /** + * @brief Encodes an input buffer into a base64 C-String. + * + * @param data Data to encode. + * @param dataLength Number of bytes to encode. + * @param result Character buffer for output. + * @return Number of bytes written to the output buffer. + */ + JUPITER_API unsigned int base64encode(const void *data, size_t dataLength, char *result); + + /** + * @brief Checks if a buffer is large enough, and if so, encodes an input buffer into a base64 C-String. + * + * @param data Data to encode. + * @param dataLength Number of bytes to encode. + * @param result Character buffer for output. + * @param outputSize Size of the "result" buffer. + * @return Number of bytes written to the output buffer. + */ + JUPITER_API unsigned int base64encode(const void *data, size_t dataLength, char *result, size_t outputSize); + + /** + * @brief Encodes an input C-String into a base64 C-String. + * + * @param data String to encode. + * @param dataLength Number of bytes to encode. + * @return Buffer containing the encoded base64 string. + */ + JUPITER_API char *base64encode(const void *data, size_t dataLength); + + /** + * @brief Encodes an input C-String into a base64 C-String. + * + * @param data String to encode. + * @return Buffer containing the encoded base64 string. + */ + JUPITER_API char *base64encode(const char *data); + + /** + * @brief Decodes an input base64 C-String into a data buffer. + * + * @param str Data to decode. + * @param result Data buffer for output. + * @return Number of bytes written to the output buffer. + */ + JUPITER_API unsigned int base64decode(const char *data, size_t dataLength, unsigned char *result, size_t resultSize); + + /** + * @brief Decodes an input base64 character string into a C-String buffer. + * + * @param data Data to decode. + * @param dataLength Length of the data to decode. + * @return Number of bytes written to the output buffer. + */ + JUPITER_API unsigned int base64decode(const char *data, size_t dataLength, unsigned char *result); + + /** + * @brief Decodes an input base64 character string into a C-String buffer. + * + * @param data Data to decode. + * @param dataLength Length of the data to decode. + * @return C-String containing the decoded buffer. + */ + JUPITER_API char *base64decode(const char *data, size_t dataLength); + + /** + * @brief Decodes an input base64 C-String into a C-String buffer. + * + * @param str Data to decode. + * @return C-String containing the decoded buffer. + */ + JUPITER_API char *base64decode(const char *str); +} + +extern "C" +{ +#else +#include +#include +#endif // __cplusplus + +/** +* @brief Encodes an input buffer into a base64 C-String. +* +* @param data Data to encode. +* @param dataLength Number of bytes to encode. +* @param result Character buffer for output. +* @return Number of bytes written to the output buffer. +*/ +JUPITER_API unsigned int Jupiter_base64encode(const void *data, size_t dataLength, char *result); + +/** +* @brief Calculates the minimum buffer size to decode a specified base64 string. +* Note: This does NOT include the neccessary space for a null-terminator. +* +* @param data Data which would be decoded. +* @param inLen Length of the input string. +* @return Minimum number of bytes to fit the decoded buffer. +*/ +JUPITER_API unsigned int Jupiter_minBase64DecodeLength(const char *data, size_t inLen); + +/** +* @brief Checks if a buffer is a valid base64 string. +* +* @param str C-String containing data to check. +* @return True if the input buffer is a valid base64 string, false otherwise. +*/ +JUPITER_API bool Jupiter_isBase64(const char *str); + +/** +* @brief Checks if a buffer is a valid base64 string. +* +* @param data Buffer to check. +* @param dataLength Length of the data to check. +* @return True if the input buffer is a valid base64 string, false otherwise. +*/ +JUPITER_API bool Jupiter_isBase642(const char *data, size_t dataLength); + +/** +* @brief Decodes an input base64 C-String into a data buffer. +* +* @param str C-String containing data to decode. +* @param result Data buffer for output. +* @return Number of bytes written to the output buffer. +*/ +JUPITER_API unsigned int Jupiter_base64decode(const char *str, unsigned char *result); + +/** +* @brief Decodes an input base64 string into a data buffer. +* +* @param data Data to decode. +* @param dataLength Length of the base64 string. +* @param result Data buffer for output. +* @return Number of bytes written to the output buffer. +*/ +JUPITER_API unsigned int Jupiter_base64decode2(const char *data, size_t dataLength, unsigned char *result); + +#if defined __cplusplus +} +#endif + +#endif _BASE64_H_HEADER \ No newline at end of file diff --git a/Jupiter/Base64C.c b/Jupiter/Base64C.c new file mode 100644 index 0000000..70f2890 --- /dev/null +++ b/Jupiter/Base64C.c @@ -0,0 +1,235 @@ +#include "Base64.h" + +unsigned int Jupiter_base64encode(const void *data_buf, size_t dataLength, char *result) +{ + const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + const uint8_t *data = (const uint8_t *)data_buf; + char *resultPtr = result; + uint32_t n = 0; + int padCount = dataLength % 3; + uint8_t n0, n1, n2, n3; + + /* increment over the length of the string, three characters at a time */ + for (size_t x = 0; x < dataLength; x += 3) + { + /* these three 8-bit (ASCII) characters become one 24-bit number */ + n = data[x] << 16; + + if ((x + 1) < dataLength) n += data[x + 1] << 8; + if ((x + 2) < dataLength) n += data[x + 2]; + + /* this 24-bit number gets separated into four 6-bit numbers */ + n0 = (uint8_t)(n >> 18) & 63; + n1 = (uint8_t)(n >> 12) & 63; + n2 = (uint8_t)(n >> 6) & 63; + n3 = (uint8_t)n & 63; + + /* + * if we have one byte available, then its encoding is spread + * out over two characters + */ + *resultPtr = base64chars[n0]; + resultPtr++; + *resultPtr = base64chars[n1]; + resultPtr++; + + /* + * if we have only two bytes available, then their encoding is + * spread out over three chars + */ + if ((x + 1) < dataLength) + { + *resultPtr = base64chars[n2]; + resultPtr++; + } + + /* + * if we have all three bytes available, then their encoding is spread + * out over four characters + */ + if ((x + 2) < dataLength) + { + *resultPtr = base64chars[n3]; + resultPtr++; + } + } + + /* + * create and add padding that is required if we did not have a multiple of 3 + * number of characters available + */ + if (padCount > 0) + { + while (padCount < 3) + { + *resultPtr = '='; + resultPtr++; + padCount++; + } + } + *resultPtr = 0; + return resultPtr - result; +} + +static const unsigned char Jupiter_base64DecodeTable[256] = { + 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 62, 66, 66, 66, 63, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 66, 66, 66, 65, 66, 66, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 66, 66, 66, 66, 66, 66, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66 +}; + +unsigned int Jupiter_minBase64DecodeLength(const char *data, size_t inLen) +{ + if (inLen == 0) return 0; + data += inLen - 1; + if (*data == '=') + { + data--; + if (*data == '=') return inLen / 4 * 3 - 2; + return inLen / 4 * 3 - 1; + } + return inLen / 4 * 3; +} + +bool Jupiter_isBase64(const char *in) +{ + while (*in != 0) + { + switch (Jupiter_base64DecodeTable[*in++]) + { + case 66: + return false; + default: + break; + } + } + return true; +} + +bool Jupiter_isBase642(const char *in, size_t inLen) +{ + if (inLen % 4 != 0) + { + return false; + } + const char *end = in + inLen; + while (in != end) + { + switch (Jupiter_base64DecodeTable[*in++]) + { + case 66: + return false; + default: + break; + } + } + return true; +} + +/** Disable warning 4244 */ +#if defined _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4244) // conversion from 'size_t' to 'unsigned char', possible loss of data +#endif + +unsigned int Jupiter_base64decode(const char *in, unsigned char *out) +{ + unsigned char *outOrig = out; + size_t buf = 1; + + while (*in != 0) + { + unsigned char c = Jupiter_base64DecodeTable[*in++]; + + switch (c) + { + case 66: + /* invalid input */ + return 0; + case 65: + /* pad character, end of data */ + goto endLoop; + case 64: + break; + default: + buf = buf << 6 | c; + + /* If the buffer is full, split it into bytes */ + if (buf & 0x1000000) + { + *out++ = buf >> 16; + *out++ = buf >> 8; + *out++ = buf; + buf = 1; + } + break; + } + } +endLoop: + + if (buf & 0x40000) + { + *out++ = buf >> 10; + *out++ = buf >> 2; + } + else if (buf & 0x1000) *out++ = buf >> 4; + + return out - outOrig; +} + +unsigned int Jupiter_base64decode2(const char *in, size_t inLen, unsigned char *out) +{ + unsigned char *outOrig = out; + const char *end = in + inLen; + size_t buf = 1; + + while (in != end) + { + unsigned char c = Jupiter_base64DecodeTable[*in++]; + + switch (c) + { + case 66: + /* invalid input */ + return false; + case 65: + /* pad character, end of data */ + in = end; + case 64: + break; + default: + buf = buf << 6 | c; + + /* If the buffer is full, split it into bytes */ + if (buf & 0x1000000) + { + *out++ = buf >> 16; + *out++ = buf >> 8; + *out++ = buf; + buf = 1; + } + break; + } + } + + if (buf & 0x40000) + { + *out++ = buf >> 10; + *out++ = buf >> 2; + } + else if (buf & 0x1000) *out++ = buf >> 4; + + return out - outOrig; +} + +/** Re-enable warning */ +#if defined _MSC_VER +#pragma warning(pop) +#endif \ No newline at end of file diff --git a/Jupiter/CString.h b/Jupiter/CString.h new file mode 100644 index 0000000..a129af2 --- /dev/null +++ b/Jupiter/CString.h @@ -0,0 +1,556 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _CSTRING_H_HEADER +#define _CSTRING_H_HEADER + +#include +#include "String_Type.h" + +/** + * @file CString.h + * @brief Defines the base CString_Type, as well as a series of CString types. + * Note: Functions which take "case" or "wildcards" into consideration will only function + * for types char and wchar_t; inputs with other types will simply return false. + */ + +namespace Jupiter +{ + /** + * @brief Provides the basis for CString classes by providing the implementations for many abstract methods in String_Type. + * Note: This is an abstract type. + * + * @param T Element type which the CString will store. Defaults to char. + */ + template class CString_Type : public String_Type + { + public: + + /** + * @brief Returns the C-Style string behind the CString. + * + * @return C-Style string that the CString represents. + */ + const T *c_str() const; + + /** + * @brief Returns the number of elements in the CString. + * + * @return Number of elements in the string. + */ + size_t size() const; + + /** + * @brief Compares another string against the CString. + * + * @param in String to compare against. + * @return 0 if the strings are equal, negative if the first mismatched character is greater in the CString, or positive if it's less. + */ + int compare(const String_Type &in) const; + int compare(const std::basic_string &in) const; + int compare(const T *in) const; + int compare(const T in) const; + + /** + * @brief Checks if the strings are equal. + * Note: Case sensitive. + * + * @param in String to compare against. + * @return True if the contents of the strings are equal, false otherwise. + */ + bool equals(const String_Type &in) const; + bool equals(const std::basic_string &in) const; + bool equals(const T *in) const; + bool equals(const T in) const; + + /** + * @brief Checks if the strings are equal. + * Note: Case insensitive. Returns false for any type other than char and wchar_t. + * + * @param in String to compare against. + * @return True if the contents of the strings are equal, false otherwise. + */ + bool equalsi(const String_Type &in) const; + bool equalsi(const std::basic_string &in) const; + bool equalsi(const T *in) const; + bool equalsi(const T in) const; + + /** + * @brief Checks if the CString matches a wildcard format. + * Note: Case sensitive. Returns false for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @return True if the CString matches the wildcard format, false otherwise. + */ + bool match(const String_Type &format) const; + bool match(const std::basic_string &format) const; + bool match(const T *format) const; + + /** + * @brief Checks if the CString matches a wildcard format. + * Note: Case insensitive. Returns false for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @return True if the CString matches the wildcard format, false otherwise. + */ + bool matchi(const String_Type &format) const; + bool matchi(const std::basic_string &format) const; + bool matchi(const T *format) const; + + /** + * @brief Sets the CString's contents based on the format string and input variables. + * Note: Format specifiers similar to printf. Returns 0 for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @param ... Inputs to match the format specifiers. + * @return Number of characters written. + */ + virtual size_t format(const String_Type &format, ...); + virtual size_t format(const std::basic_string &format, ...); + virtual size_t format(const T *format, ...); + virtual size_t vformat(const T *format, va_list args) = 0; + + /** + * @brief Appends to a CString's contents based on the format string and input variables. + * Note: Format specifiers similar to printf. Returns 0 for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @param ... Inputs to match the format specifiers. + * @return Number of characters written. + */ + virtual size_t aformat(const String_Type &format, ...); + virtual size_t aformat(const std::basic_string &format, ...); + virtual size_t aformat(const T *format, ...); + virtual size_t avformat(const T *format, va_list args) = 0; + + /** + * @brief Counts the number of token deliminated words. + * + * @param whitespace A string of tokens used to deliminate words. + * @return Number of words found. + */ + unsigned int wordCount(const T *whitespace) const; + + /** + * @brief Truncates the string by a specified number of elements. + * + * @param n Number of elements to remove from the tail. + * @return New size of the CString. + */ + size_t truncate(size_t n); + + /** + * @brief Fetches an element from the string. + * + * @param index Index of the element to return. + * @return The element located at the specified index. + */ + T &get(size_t index) const; + + /** + * @brief Shifts the string pointer to the left. + * + * @param length Number of elements to shift + * @return Number of elements shifted to the left. + */ + size_t shiftLeft(size_t length); + + /** + * @brief Shifts the string pointer to the right. + * + * @param length Number of elements to shift + * @return Number of elements shifted. + */ + size_t shiftRight(size_t length); + + /** + * @brief Removes the first instance of an element from the string. + * + * @param value Value of the element to remove. + * @return True if an element was removed, false otherwise. + */ + bool remove(T &value); + + /** + * @brief Checks if the string contains an element with the specified value. + * + * @param value Value of the element to search for. + * @return True if a match is found, false otherwise. + */ + bool contains(const T &value); + + /** Access Operator */ + inline T &operator[](size_t pos) { return Jupiter::CString_Type::curr[pos]; }; + + /** Assignment Operators */ + inline CString_Type &operator=(const CString_Type &right) { this->set(right); return *this; }; + inline CString_Type &operator=(const String_Type &right) { this->set(right); return *this; }; + inline CString_Type &operator=(const std::basic_string &right) { this->set(right); return *this; }; + inline CString_Type &operator=(const T *right) { this->set(right); return *this; }; + inline CString_Type &operator=(const T right) { this->set(right); return *this; }; + + protected: + T *base; /** Base pointer for the underlying C-style string */ + T *curr; /** Active pointer for the underlying C-style string */ + size_t strLen; /** Length of underlying C-style string */ + }; + + /** + * @brief Provides a "strict" CString implementation that's more optimized for minimal memory usage. + * Note: This recreates the underlying C-style string with every concatenation. + * + * @param T Element type which the CString will store. Defaults to char. + */ + template class CString_Strict : public CString_Type + { + public: + + /** + * @brief Sets the CString's contents based on the format string and input variables. + * Note: Format specifiers similar to printf. Returns 0 for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @param args Inputs to match the format specifiers. + * @return Number of characters written. + */ + size_t vformat(const T *format, va_list args); + + /** + * @brief Appends to a CString's contents based on the format string and input variables. + * Note: Format specifiers similar to printf. Returns 0 for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @param args Inputs to match the format specifiers. + * @return Number of characters written. + */ + size_t avformat(const T *format, va_list args); + + /** + * @brief Sets the CString's contents based on the format string and input variables. + * Note: Format specifiers similar to printf. Returns 0 for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @param ... Inputs to match the format specifiers. + * @return String containing the new format. + */ + static CString_Strict Format(const T *format, ...); + + /** + * @brief Creates a partial copy of the string. + * + * @param pos Position in the string to start copying from. + * @param length Number of characters to copy. + * @return String containing a partial copy of the original string. + */ + CString_Strict substring(size_t pos, size_t length) const; + + /** + * @brief Creates a partial copy of the string, based on a set of tokens. + * + * @param pos Position of word in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + CString_Strict getWord(size_t pos, const T *whitespace) const; + + /** + * @brief Creates a partial copy of an input string, based on a set of tokens. + * + * @param in String to get a partial copy of. + * @param pos Position of word in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + static CString_Strict getWord(const Jupiter::String_Type &in, size_t pos, const T *whitespace); + + /** + * @brief Creates a partial copy of an input string, based on a set of tokens. + * + * @param in C-Style string to get a partial copy of. + * @param pos Position of word in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + static CString_Strict getWord(const T *in, size_t pos, const T *whitespace); + + /** + * @brief Creates a partial copy of the string, based on a set of tokens. + * + * @param pos Position in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + CString_Strict gotoWord(size_t pos, const T *whitespace) const; + + /** + * @brief Creates a partial copy of the string, based on a set of tokens. + * + * @param in String to get a partial copy of. + * @param pos Position in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + static CString_Strict gotoWord(const Jupiter::String_Type &in, size_t pos, const T *whitespace); + + /** + * @brief Copies the data from the input string to the CString. + * + * @param in String containing the data to be copied. + * @return New size of the CString. + */ + size_t set(const String_Type &in); + size_t set(const std::basic_string &in); + size_t set(const T *in); + size_t set(const T in); + + /** + * @brief Sets the string buffer. + * Note: This class will free the buffer for you when it's done. + * + * @param in New buffer to be used. + * @return The length of the string. + */ + size_t setString(T *in); + + /** + * @brief Sets the string buffer. + * Note: This class will free the buffer for you when it's done. DO NOT DELETE THE INPUT BUFFER. + * Note: This method is unique to the CString_Strict template class, and does not appear in CString_Loose. + * + * @param in New buffer to be used. + * @param size At least the number of characters in the buffer, not including the null-terminator. + * @return The length of the string. + */ + size_t setString(T *in, size_t size); + + /** + * @brief Copies the data from the input string and concatenates it to the end of CString. + * + * @param in String containing the data to be concatenated. + * @return New size of the CString. + */ + size_t concat(const String_Type &in); + size_t concat(const std::basic_string &in); + size_t concat(const T *in); + size_t concat(const T in); + + /** Default Constructor */ + CString_Strict(); + + /** Copy Constructors */ + CString_Strict(const CString_Strict &in); + CString_Strict(const String_Type &in); + CString_Strict(const std::basic_string &in); + CString_Strict(const T *in); + CString_Strict(const T in); + + /** Destructor */ + virtual ~CString_Strict(); + + /** Assignment Operators */ + inline CString_Strict &operator=(const CString_Strict &right) { this->set(right); return *this; }; + inline CString_Strict &operator=(const CString_Type &right) { this->set(right); return *this; }; + inline CString_Strict &operator=(const String_Type &right) { this->set(right); return *this; }; + inline CString_Strict &operator=(const std::basic_string &right) { this->set(right); return *this; }; + inline CString_Strict &operator=(const T *right) { this->set(right); return *this; }; + inline CString_Strict &operator=(const T right) { this->set(right); return *this; }; + }; + + /** + * @brief Provides a "loose" CString implementation that's more optimized for repeated concatenations. + * Note: The underlying C-style string will always have a size which is a power of 2, but no fewer than 8 elements. + * + * @param T Element type which the CString will store. Defaults to char. + */ + template class CString_Loose : public CString_Type + { + public: + + /** + * @brief Sets the CString's contents based on the format string and input variables. + * Note: Format specifiers similar to printf. Returns 0 for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @param ... Inputs to match the format specifiers. + * @return Number of characters written. + */ + size_t vformat(const T *format, va_list args); + + /** + * @brief Appends to a CString's contents based on the format string and input variables. + * Note: Format specifiers similar to printf. Returns 0 for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @param ... Inputs to match the format specifiers. + * @return Number of characters written. + */ + size_t avformat(const T *format, va_list args); + + /** + * @brief Sets the CString's contents based on the format string and input variables. + * Note: Format specifiers similar to printf. Returns 0 for any type other than char and wchar_t. + * + * @param format Format that the string is compared against. + * @param ... Inputs to match the format specifiers. + * @return String containing the new format. + */ + static CString_Loose Format(const T *format, ...); + + /** + * @brief Creates a partial copy of the string. + * + * @param pos Position in the string to start copying from. + * @param length Number of characters to copy. + * @return String containing a partial copy of the original string. + */ + CString_Loose substring(size_t pos, size_t length) const; + + /** + * @brief Creates a partial copy of the string, based on a set of tokens. + * + * @param pos Position in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + CString_Loose getWord(size_t pos, const T *whitespace) const; + + /** + * @brief Creates a partial copy of an input string, based on a set of tokens. + * Useful when the input string's type isn't known. + * + * @param in String to get a partial copy of. + * @param pos Position of word in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + static CString_Loose getWord(const Jupiter::String_Type &in, size_t pos, const T *whitespace); + + /** + * @brief Creates a partial copy of an input string, based on a set of tokens. + * Useful when the input string's type isn't known. + * + * @param in C-Style string to get a partial copy of. + * @param pos Position of word in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + static CString_Loose getWord(const T *in, size_t pos, const T *whitespace); + + /** + * @brief Creates a partial copy of the string, based on a set of tokens. + * + * @param pos Position in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + CString_Loose gotoWord(size_t pos, const T *whitespace) const; + + /** + * @brief Creates a partial copy of the string, based on a set of tokens. + * + * @param in String to get a partial copy of. + * @param pos Position in the string to start copying from. + * @param whitespace A string of tokens used to deliminate words. + * @return String containing a partial copy of the original string. + */ + static CString_Loose gotoWord(const Jupiter::String_Type &in, size_t pos, const T *whitespace); + + /** + * @brief Copies the data from the input string to the CString. + * + * @param in String containing the data to be copied. + * @return New size of the CString. + */ + size_t set(const CString_Loose &in); + size_t set(const String_Type &in); + size_t set(const std::basic_string &in); + size_t set(const T *in); + size_t set(const T in); + + /** + * @brief Copies the data from the input string and concatenats it to the end of CString. + * + * @param in String containing the data to be concatenated. + * @return New size of the CString. + */ + size_t concat(const String_Type &in); + size_t concat(const std::basic_string &in); + size_t concat(const T *in); + size_t concat(const T in); + + /** Default constructor */ + CString_Loose(); + + /** Copy Constructors */ + CString_Loose(const CString_Loose &in); + CString_Loose(const String_Type &in); + CString_Loose(const std::basic_string &in); + CString_Loose(const T *in); + CString_Loose(const T in); + + /** Destructor */ + virtual ~CString_Loose(); + + static const size_t start_size = 8; /** Starting size for loose CStrings. */ + + /** Assignment Operators */ + inline CString_Loose &operator=(const CString_Loose &right) { this->set(right); return *this; }; + inline CString_Loose &operator=(const CString_Type &right) { this->set(right); return *this; }; + inline CString_Loose &operator=(const String_Type &right) { this->set(right); return *this; }; + inline CString_Loose &operator=(const std::basic_string &right) { this->set(right); return *this; }; + inline CString_Loose &operator=(const T *right) { this->set(right); return *this; }; + inline CString_Loose &operator=(const T right) { this->set(right); return *this; }; + + protected: + size_t strSize; /** Size of underlying C-string buffer */ + }; + + /** Definition of a Loose CString. */ + typedef CString_Loose CStringL; + + /** Definition of a Loose Wide CString */ + typedef CString_Loose WCStringL; + + /** Definition of a Strict CString. */ + typedef CString_Strict CStringS; + + /** Definition of a Strict Wide CString */ + typedef CString_Strict WCStringS; + + /** Definition of a CString. */ + typedef CStringL CString; + + /** Definition of a Wide CString */ + typedef WCStringL WCString; + + /** Generic CString Type */ + typedef CString_Type CStringType; + + /** Generic Wide CString Type */ + typedef CString_Type WCStringType; + + /** Empty String constants */ + static const Jupiter::CStringS emptyCStringS; + static const Jupiter::CStringS emptyCStringL; + static const Jupiter::CStringS &emptyCString = emptyCStringS; + static const Jupiter::CStringS &emptyString = emptyCString; +} + +/** Implementation for CString_Type, CString_Strict, and CString_Loose. Very scary. */ +#include "CString_IMP.h" + +#endif // _CSTRING_H_HEADER \ No newline at end of file diff --git a/Jupiter/CString_Imp.h b/Jupiter/CString_Imp.h new file mode 100644 index 0000000..6a3e2df --- /dev/null +++ b/Jupiter/CString_Imp.h @@ -0,0 +1,1203 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _CSTRING_IMP_H_HEADER +#define _CSTRING_IMP_H_HEADER + +#include "Functions.h" +#include "CString.h" + +/** + * @file CString_Imp.h + * @brief Provides the implementations for CString_Type functions, as well as extending classes. + * Note: Modification of this file is not supported in any way. + */ + +/** +* IMPLEMENTATION: +* CString_Type +*/ + +template const T *Jupiter::CString_Type::c_str() const +{ + return Jupiter::CString_Type::curr; +} + +template size_t Jupiter::CString_Type::size() const +{ + return Jupiter::CString_Type::strLen; +} + +template int Jupiter::CString_Type::compare(const Jupiter::String_Type &in) const +{ + return Jupiter::CString_Type::compare(in.c_str()); +} + +template int Jupiter::CString_Type::compare(const std::basic_string &in) const +{ + return Jupiter::CString_Type::compare(in.c_str()); +} + +template int Jupiter::CString_Type::compare(const T *s2) const +{ + const T *s1 = Jupiter::CString_Type::curr; + while (*s1 != 0 && *s1 == *s2) + { + s1++; + s2++; + } + return *s1 - *s2; +} + +template int Jupiter::CString_Type::compare(const T s2) const +{ + return *Jupiter::CString_Type::curr - s2; +} + +template bool Jupiter::CString_Type::equals(const Jupiter::String_Type &in) const +{ + if (Jupiter::CString_Type::strLen != in.size()) return false; + return Jupiter::CString_Type::equals(in.c_str()); +} + +template bool Jupiter::CString_Type::equals(const std::basic_string &in) const +{ + if (Jupiter::CString_Type::strLen != in.size()) return false; + return Jupiter::CString_Type::equals(in.c_str()); +} + +template bool Jupiter::CString_Type::equals(const T *in) const +{ + if (in == nullptr) return Jupiter::CString_Type::strLen == 0; + return Jupiter::streql(Jupiter::CString_Type::curr, in); +} + +template bool Jupiter::CString_Type::equals(const T in) const +{ + return *Jupiter::CString_Type::curr == in && ((in == 0) || Jupiter::CString_Type::curr[1] == 0); +} + +// equalsi() + +template bool Jupiter::CString_Type::equalsi(const Jupiter::String_Type &in) const +{ + if (Jupiter::CString_Type::strLen != in.size()) return false; + return Jupiter::CString_Type::equalsi(in.c_str()); +} + +template bool Jupiter::CString_Type::equalsi(const std::basic_string &in) const +{ + if (Jupiter::CString_Type::strLen != in.size()) return false; + return Jupiter::CString_Type::equalsi(in.c_str()); +} + +template<> bool inline Jupiter::CString_Type::equalsi(const char *in) const +{ + return streqli(Jupiter::CString_Type::curr, in); +} + +template<> bool inline Jupiter::CString_Type::equalsi(const wchar_t *in) const +{ + return wstreqli(Jupiter::CString_Type::curr, in); +} + +template bool Jupiter::CString_Type::equalsi(const T *in) const +{ + return false; // Concept of "case" not supported for type. +} + +template<> bool inline Jupiter::CString_Type::equalsi(const char in) const +{ + return toupper(*Jupiter::CString_Type::curr) == toupper(in) && ((in == 0) || Jupiter::CString_Type::curr[1] == 0); +} + +template<> bool inline Jupiter::CString_Type::equalsi(const wchar_t in) const +{ + return towupper(*Jupiter::CString_Type::curr) == towupper(in) && ((in == 0) || Jupiter::CString_Type::curr[1] == 0); +} + +template bool Jupiter::CString_Type::equalsi(const T in) const +{ + return false; // Concept of "case" not supported for type. +} + +// match() + +template bool Jupiter::CString_Type::match(const Jupiter::String_Type &format) const +{ + return Jupiter::CString_Type::match(format.c_str()); +} + +template bool Jupiter::CString_Type::match(const std::basic_string &format) const +{ + return Jupiter::CString_Type::match(format.c_str()); +} + +template<> inline bool Jupiter::CString_Type::match(const char *format) const +{ + return strmatch(format, Jupiter::CString_Type::curr); +} + +template<> inline bool Jupiter::CString_Type::match(const wchar_t *format) const +{ + return wstrmatch(format, Jupiter::CString_Type::curr); +} + +template bool Jupiter::CString_Type::match(const T *format) const +{ + return false; // Type is not comparable to wildcards. +} + +// matchi() + +template bool Jupiter::CString_Type::matchi(const Jupiter::String_Type &format) const +{ + return Jupiter::CString_Type::matchi(format.c_str()); +} + +template bool Jupiter::CString_Type::matchi(const std::basic_string &format) const +{ + return Jupiter::CString_Type::matchi(format.c_str()); +} + +template<> bool inline Jupiter::CString_Type::matchi(const char *format) const +{ + return strmatchi(format, Jupiter::CString_Type::curr); +} + +template<> bool inline Jupiter::CString_Type::matchi(const wchar_t *format) const +{ + return wstrmatchi(format, Jupiter::CString_Type::curr); +} + +template bool Jupiter::CString_Type::matchi(const T *format) const +{ + return false; // Type is not comparable to wildcards. Concept of "case" not supported for type. +} + +// format forwards + +template size_t Jupiter::CString_Type::format(const String_Type &format, ...) +{ + size_t r; + va_list args; + va_start(args, format); + r = this->vformat(format.c_str(), args); + va_end(args); + return r; +} + +template size_t Jupiter::CString_Type::format(const std::basic_string &format, ...) +{ + size_t r; + va_list args; + va_start(args, format); + r = this->vformat(format.c_str(), args); + va_end(args); + return r; +} + +template size_t Jupiter::CString_Type::format(const T *format, ...) +{ + size_t r; + va_list args; + va_start(args, format); + r = this->vformat(format, args); + va_end(args); + return r; +} + +// aformat forwards + +template size_t Jupiter::CString_Type::aformat(const String_Type &format, ...) +{ + size_t r; + va_list args; + va_start(args, format); + r = this->avformat(format.c_str(), args); + va_end(args); + return r; +} + +template size_t Jupiter::CString_Type::aformat(const std::basic_string &format, ...) +{ + size_t r; + va_list args; + va_start(args, format); + r = this->avformat(format.c_str(), args); + va_end(args); + return r; +} + +template size_t Jupiter::CString_Type::aformat(const T *format, ...) +{ + size_t r; + va_list args; + va_start(args, format); + r = this->avformat(format, args); + va_end(args); + return r; +} + +template unsigned int Jupiter::CString_Type::wordCount(const T *whitespace) const +{ + unsigned int result = 0; + T *p = Jupiter::CString_Type::curr; + bool prev = true; + while (*p != 0) + { + if (Jupiter::strpbrk(whitespace, *p) == nullptr) // This isn't whitespace! + { + if (prev == true) // We just left whitespace! + { + prev = false; + result++; + } + } + else prev = true; // This is whitespace! + p++; + } + return result; +} + +template size_t Jupiter::CString_Type::truncate(size_t n) +{ + if (n >= Jupiter::CString_Type::strLen) + { + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = 0; + Jupiter::CString_Type::strLen = 0; + return 0; + } + Jupiter::CString_Type::strLen -= n; + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template T &Jupiter::CString_Type::get(size_t index) const +{ + return Jupiter::CString_Type::curr[index]; +} + +template size_t Jupiter::CString_Type::shiftLeft(size_t length) +{ + size_t offset = Jupiter::CString_Type::curr - Jupiter::CString_Type::base; + if (length > offset) length = offset; + Jupiter::CString_Type::curr -= length; + Jupiter::CString_Type::strLen += length; + return length; +} + +template size_t Jupiter::CString_Type::shiftRight(size_t length) +{ + if (length > Jupiter::CString_Type::strLen) length = Jupiter::CString_Type::strLen; + Jupiter::CString_Type::curr += length; + Jupiter::CString_Type::strLen -= length; + return length; +} + +template bool Jupiter::CString_Type::remove(T &value) +{ + for (unsigned int i = 0; i < Jupiter::CString_Type::strLen; i++) + { + if (Jupiter::CString_Type::curr[i] == value) + { + if (i == strLen - 1) Jupiter::CString_Type::truncate(1); + else if (i == 0) + { + if (Jupiter::CString_Type::strLen == 1) Jupiter::CString_Type::truncate(1); + else Jupiter::CString_Type::shiftRight(1); + } + else + { + Jupiter::strcpy(Jupiter::CString_Type::curr + i, Jupiter::CString_Type::curr + i + 1); + Jupiter::CString_Type::strLen--; + } + return true; + } + } + return false; +} + +template bool Jupiter::CString_Type::contains(const T &value) +{ + for (unsigned int i = 0; i < Jupiter::CString_Type::strLen; i++) if (Jupiter::CString_Type::curr[i] == value) return true; + return false; +} + +/** +* IMPLEMENTATION: +* CString_Strict +*/ + +template Jupiter::CString_Strict::CString_Strict() +{ + Jupiter::CString_Type::base = new T[1]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = 0; + Jupiter::CString_Type::strLen = 0; +} + +template Jupiter::CString_Strict::CString_Strict(const Jupiter::CString_Strict &in) +{ + Jupiter::CString_Type::strLen = in.strLen; + Jupiter::CString_Type::base = new T[Jupiter::CString_Type::strLen + 1]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.curr); +} + +template Jupiter::CString_Strict::CString_Strict(const Jupiter::String_Type &in) +{ + Jupiter::CString_Type::strLen = in.size(); + Jupiter::CString_Type::base = new T[Jupiter::CString_Type::strLen + 1]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.c_str()); +} + +template Jupiter::CString_Strict::CString_Strict(const std::basic_string &in) +{ + Jupiter::CString_Type::strLen = in.size(); + Jupiter::CString_Type::base = new T[Jupiter::CString_Type::strLen + 1]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.c_str()); +} + +template Jupiter::CString_Strict::CString_Strict(const T *in) +{ + if (in == nullptr) + { + Jupiter::CString_Type::strLen = 0; + Jupiter::CString_Type::base = new T[1]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = 0; + } + else + { + Jupiter::CString_Type::strLen = Jupiter::strlen(in); + Jupiter::CString_Type::base = new T[Jupiter::CString_Type::strLen + 1]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::strcpy(Jupiter::CString_Type::curr, in); + } +} + +template Jupiter::CString_Strict::CString_Strict(const T c) +{ + if (c == 0) + { + Jupiter::CString_Type::strLen = 0; + Jupiter::CString_Type::base = new T[1]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = 0; + } + else + { + Jupiter::CString_Type::strLen = 1; + Jupiter::CString_Type::base = new T[Jupiter::CString_Type::strLen + 1]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = c; + Jupiter::CString_Type::curr[1] = 0; + } +} + +template Jupiter::CString_Strict::~CString_Strict() +{ + delete[] Jupiter::CString_Type::base; +} + +// vformat() + +template<> size_t inline Jupiter::CString_Strict::vformat(const char *format, va_list args) +{ + int minLen; + va_list sargs; + va_copy(sargs, args); + minLen = vsnprintf(nullptr, 0, format, sargs); + va_end(sargs); + if (minLen < 0) return 0; // We simply can not work with this. + if ((unsigned)minLen > Jupiter::CString_Type::strLen) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = new char[minLen + 1]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = minLen; + vsnprintf(Jupiter::CString_Type::curr, Jupiter::CString_Type::strLen, format, args); + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template<> size_t inline Jupiter::CString_Strict::vformat(const wchar_t *format, va_list args) +{ + int minLen; + va_list sargs; + va_copy(sargs, args); + minLen = vswprintf(nullptr, 0, format, sargs); + va_end(sargs); + if (minLen < 0) return 0; // We simply can not work with this. + if ((unsigned)minLen > Jupiter::CString_Type::strLen) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = new wchar_t[minLen + 1]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = minLen; + vswprintf(Jupiter::CString_Type::curr, Jupiter::CString_Type::strLen, format, args); + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Strict::vformat(const T *format, va_list args) +{ + return 0; +} + +// avformat() + +template<> size_t inline Jupiter::CString_Strict::avformat(const char *format, va_list args) +{ + int minLen; + va_list sargs; + va_copy(sargs, args); + minLen = vsnprintf(nullptr, 0, format, sargs); + va_end(sargs); + if (minLen < 0) return 0; // We simply can not work with this. + + char *t = new char[minLen + Jupiter::CString_Type::strLen + 1]; + Jupiter::strcpy(t, Jupiter::CString_Type::curr); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = t; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + + vsnprintf(Jupiter::CString_Type::curr + Jupiter::CString_Type::strLen, minLen, format, args); + Jupiter::CString_Type::strLen += minLen; + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template<> size_t inline Jupiter::CString_Strict::avformat(const wchar_t *format, va_list args) +{ + int minLen; + va_list sargs; + va_copy(sargs, args); + minLen = vswprintf(nullptr, 0, format, sargs); + va_end(sargs); + if (minLen < 0) return 0; // We simply can not work with this. + + wchar_t *t = new wchar_t[minLen + Jupiter::CString_Type::strLen + 1]; + Jupiter::strcpy(t, Jupiter::CString_Type::curr); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = t; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + + vswprintf(Jupiter::CString_Type::curr + Jupiter::CString_Type::strLen, minLen, format, args); + Jupiter::CString_Type::strLen += minLen; + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Strict::avformat(const T *format, va_list args) +{ + return 0; +} + +template Jupiter::CString_Strict Jupiter::CString_Strict::Format(const T *format, ...) +{ + CString_Strict r; + va_list args; + va_start(args, format); + r.vformat(format, args); + va_end(args); + return r; +} + +template Jupiter::CString_Strict Jupiter::CString_Strict::substring(size_t pos, size_t length) const +{ + Jupiter::CString_Strict r; + r.setString(new char[length + 1], length); + r.strLen = Jupiter::strlen(Jupiter::strcpy(r.curr, Jupiter::CString_Type::curr + pos, length)); + return r; +} + +template Jupiter::CString_Strict Jupiter::CString_Strict::getWord(size_t pos, const T *whitespace) const +{ + return Jupiter::CString_Strict::getWord(*this, pos, whitespace); +} + +template Jupiter::CString_Strict Jupiter::CString_Strict::getWord(const Jupiter::String_Type &in, size_t pos, const T *whitespace) +{ + return Jupiter::CString_Strict::getWord(in.c_str(), pos, whitespace); +} + +template Jupiter::CString_Strict Jupiter::CString_Strict::getWord(const T *in, size_t pos, const T *whitespace) +{ + Jupiter::CString_Strict r; + unsigned int x = 0; + unsigned int y = 1; + unsigned int i; + for (i = 0; i < pos || y == 1; x++) + { + if (in[x] == 0) return r; + if (Jupiter::strpbrk(whitespace, in[x]) != nullptr) + { + if (y != 1) + { + y = 1; + i++; + } + } + else + { + if (i >= pos) break; + y = 0; + } + } + + for (y = x; Jupiter::strpbrk(whitespace, in[y]) == nullptr && in[y] != 0; y++); + r.strLen = y - x; + delete[] r.base; + r.base = new char[r.strLen + 1]; + r.curr = r.base; + for (i = 0; x < y; i++) + { + r.curr[i] = in[x]; + x++; + } + r.curr[r.strLen] = 0; + return r; +} + +template Jupiter::CString_Strict Jupiter::CString_Strict::gotoWord(size_t pos, const T *whitespace) const +{ + return Jupiter::CString_Strict::gotoWord(*this, pos, whitespace); +} + +template Jupiter::CString_Strict Jupiter::CString_Strict::gotoWord(const Jupiter::String_Type &in, size_t pos, const T *whitespace) +{ + Jupiter::CString_Strict r; + unsigned int x = 0; + bool y = true; + for (unsigned int i = 0; i < pos || y == true; x++) + { + if (in.get(x) == 0) return r; + if (Jupiter::strpbrk(whitespace, in.get(x)) != nullptr) + { + if (y != 1) + { + y = true; + i++; + } + } + else + { + if (i >= pos) break; + y = false; + } + } + // Remake later to get character-by-character -- c_str not guaranteed to be fast. + r = in.c_str() + x; + return r; +} + +template size_t Jupiter::CString_Strict::set(const String_Type &in) +{ + size_t sSize = in.size(); + if (Jupiter::CString_Type::strLen < sSize) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = new T[sSize + 1]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = sSize; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.c_str()); + return sSize; +} + +template size_t Jupiter::CString_Strict::set(const std::basic_string &in) +{ + size_t sSize = in.size(); + if (Jupiter::CString_Type::strLen < sSize) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = new T[sSize + 1]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = sSize; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.c_str()); + return sSize; +} + +template size_t Jupiter::CString_Strict::set(const T *in) +{ + size_t sSize = Jupiter::strlen(in); + if (Jupiter::CString_Type::strLen < sSize) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = new T[sSize + 1]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = sSize; + Jupiter::strcpy(Jupiter::CString_Type::curr, in); + return sSize; +} + +template size_t Jupiter::CString_Strict::set(const T in) +{ + if (in == 0) + { + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = 0; + return Jupiter::CString_Type::strLen = 0; + } + if (Jupiter::CString_Type::strLen < 1) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = new T[2]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = in; + Jupiter::CString_Type::curr[1] = 0; + return Jupiter::CString_Type::strLen = 1; +} + +template size_t Jupiter::CString_Strict::setString(T *in) +{ + return Jupiter::CString_Strict::setString(in, Jupiter::strlen(in)); +} + +template size_t Jupiter::CString_Strict::setString(T *in, size_t size) +{ + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = in; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + return Jupiter::CString_Type::strLen = size; +} + +template size_t Jupiter::CString_Strict::concat(const String_Type &in) +{ + size_t nSize = Jupiter::CString_Type::strLen + in.size(); + T *tmp = new T[nSize + 1]; + Jupiter::strcpy(tmp, Jupiter::CString_Type::curr); + Jupiter::strcpy(tmp + Jupiter::CString_Type::strLen, in.c_str()); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmp; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = nSize; + return nSize; +} + +template size_t Jupiter::CString_Strict::concat(const std::basic_string &in) +{ + size_t nSize = Jupiter::CString_Type::strLen + in.size(); + T *tmp = new T[nSize + 1]; + Jupiter::strcpy(tmp, Jupiter::CString_Type::curr); + Jupiter::strcpy(tmp + Jupiter::CString_Type::strLen, in.c_str()); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmp; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = nSize; + return nSize; +} + +template size_t Jupiter::CString_Strict::concat(const T *in) +{ + size_t nSize = Jupiter::CString_Type::strLen + Jupiter::strlen(in); + T *tmp = new T[nSize + 1]; + Jupiter::strcpy(tmp, Jupiter::CString_Type::curr); + Jupiter::strcpy(tmp + Jupiter::CString_Type::strLen, in); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmp; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = nSize; + return nSize; +} + +template size_t Jupiter::CString_Strict::concat(const T c) +{ + size_t nSize = Jupiter::CString_Type::strLen + 1; + T *tmp = new T[nSize + 1]; + Jupiter::strcpy(tmp, Jupiter::CString_Type::curr); + tmp[nSize - 1] = c; + tmp[nSize] = 0; + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmp; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = nSize; + return nSize; +} + +/** +* IMPLEMENTATION: +* CString_Loose +*/ + +template Jupiter::CString_Loose::CString_Loose() +{ + Jupiter::CString_Loose::strSize = Jupiter::CString_Loose::start_size; + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = 0; + Jupiter::CString_Type::strLen = 0; +} + +template Jupiter::CString_Loose::CString_Loose(const Jupiter::CString_Loose &in) +{ + Jupiter::CString_Loose::strSize = in.strSize; + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.curr); + Jupiter::CString_Type::strLen = in.strLen; +} + +template Jupiter::CString_Loose::CString_Loose(const Jupiter::String_Type &in) +{ + Jupiter::CString_Type::strLen = in.size(); + Jupiter::CString_Loose::strSize = getPowerTwo32(Jupiter::CString_Type::strLen + 1); + if (Jupiter::CString_Loose::strSize < 8) Jupiter::CString_Loose::strSize = 8; + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.c_str()); +} + +template Jupiter::CString_Loose::CString_Loose(const std::basic_string &in) +{ + Jupiter::CString_Type::strLen = in.size(); + Jupiter::CString_Loose::strSize = getPowerTwo32(Jupiter::CString_Type::strLen + 1); + if (Jupiter::CString_Loose::strSize < 8) Jupiter::CString_Loose::strSize = 8; + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.c_str()); +} + +template Jupiter::CString_Loose::CString_Loose(const T *in) +{ + if (in == nullptr) + { + Jupiter::CString_Loose::strSize = Jupiter::CString_Loose::start_size; + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = 0; + Jupiter::CString_Type::strLen = 0; + } + else + { + Jupiter::CString_Type::strLen = Jupiter::strlen(in); + Jupiter::CString_Loose::strSize = getPowerTwo32(Jupiter::CString_Type::strLen + 1); + if (Jupiter::CString_Loose::strSize < 8) Jupiter::CString_Loose::strSize = 8; + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::strcpy(Jupiter::CString_Type::curr, in); + } +} + +template Jupiter::CString_Loose::CString_Loose(const T in) +{ + Jupiter::CString_Loose::strSize = Jupiter::CString_Loose::start_size; + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + if (in == 0) + { + Jupiter::CString_Type::strLen = 0; + *Jupiter::CString_Type::curr = 0; + } + else + { + Jupiter::CString_Type::strLen = 1; + *Jupiter::CString_Type::curr = in; + Jupiter::CString_Type::curr[1] = 0; + } +} + +template Jupiter::CString_Loose::~CString_Loose() +{ + delete[] Jupiter::CString_Type::base; +} + +template<> size_t inline Jupiter::CString_Loose::vformat(const char *format, va_list args) +{ + int minLen; + va_list sargs; + va_copy(sargs, args); + minLen = vsnprintf(nullptr, 0, format, sargs); // Portable as of C++11. + va_end(sargs); + if (minLen < 0) return 0; // We simply can not work with this. + if ((unsigned)minLen > Jupiter::CString_Loose::strSize) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Loose::strSize = getPowerTwo32(minLen + 1); + Jupiter::CString_Type::base = new char[Jupiter::CString_Loose::strSize]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = minLen; + vsnprintf(Jupiter::CString_Type::curr, Jupiter::CString_Loose::strSize, format, args); + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template<> size_t inline Jupiter::CString_Loose::vformat(const wchar_t *format, va_list args) +{ + int minLen; + va_list sargs; + va_copy(sargs, args); + minLen = vswprintf(nullptr, 0, format, sargs); // Portable as of C++11. + va_end(sargs); + if (minLen < 0) return 0; // We simply can not work with this. + if ((unsigned)minLen > Jupiter::CString_Loose::strSize) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Loose::strSize = getPowerTwo32(minLen + 1); + Jupiter::CString_Type::base = new wchar_t[Jupiter::CString_Loose::strSize]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = minLen; + vswprintf(Jupiter::CString_Type::curr, Jupiter::CString_Loose::strSize, format, args); + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::vformat(const T *format, va_list args) +{ + return 0; +} + +template<> size_t inline Jupiter::CString_Loose::avformat(const char *format, va_list args) +{ + int minLen; + va_list sargs; + va_copy(sargs, args); + minLen = vsnprintf(nullptr, 0, format, sargs); // Portable as of C++11. + va_end(sargs); + if (minLen < 0) return 0; // We simply can not work with this. + minLen += Jupiter::CString_Type::strLen; + if ((unsigned)minLen + 1 > Jupiter::CString_Loose::strSize) + { + Jupiter::CString_Loose::strSize = getPowerTwo32(minLen + 1); + char *tmpStr = new char[Jupiter::CString_Loose::strSize]; + Jupiter::strcpy(tmpStr, Jupiter::CString_Type::curr); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmpStr; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + else if ((unsigned)minLen + 1 + (Jupiter::CString_Type::curr - Jupiter::CString_Type::base) > Jupiter::CString_Loose::strSize) + { + Jupiter::strcpy(Jupiter::CString_Type::base, Jupiter::CString_Type::curr); + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + vsnprintf(Jupiter::CString_Type::curr + Jupiter::CString_Type::strLen, Jupiter::CString_Loose::strSize - Jupiter::CString_Type::strLen, format, args); + Jupiter::CString_Type::strLen = minLen; + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template<> size_t inline Jupiter::CString_Loose::avformat(const wchar_t *format, va_list args) +{ + int minLen; + va_list sargs; + va_copy(sargs, args); + minLen = vswprintf(nullptr, 0, format, sargs); // Portable as of C++11. + va_end(sargs); + if (minLen < 0) return 0; // We simply can not work with this. + minLen += Jupiter::CString_Type::strLen; + if ((unsigned)minLen + 1 > Jupiter::CString_Loose::strSize) + { + Jupiter::CString_Loose::strSize = getPowerTwo32(minLen + 1); + wchar_t *tmpStr = new wchar_t[Jupiter::CString_Loose::strSize]; + Jupiter::strcpy(tmpStr, Jupiter::CString_Type::curr); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmpStr; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + else if ((unsigned)minLen + 1 + (Jupiter::CString_Type::curr - Jupiter::CString_Type::base) > Jupiter::CString_Loose::strSize) + { + Jupiter::strcpy(Jupiter::CString_Type::base, Jupiter::CString_Type::curr); + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + vswprintf(Jupiter::CString_Type::curr + Jupiter::CString_Type::strLen, Jupiter::CString_Loose::strSize - Jupiter::CString_Type::strLen, format, args); + Jupiter::CString_Type::strLen = minLen; + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::avformat(const T *format, va_list args) +{ + return 0; +} + +template Jupiter::CString_Loose Jupiter::CString_Loose::Format(const T *format, ...) +{ + CString_Loose r; + va_list args; + va_start(args, format); + r.vformat(format, args); + va_end(args); + return r; +} + +template Jupiter::CString_Loose Jupiter::CString_Loose::substring(size_t pos, size_t length) const +{ + Jupiter::CString_Loose r; + r.strSize = getPowerTwo32(length + 1); + delete[] r.base; + r.base = new char[r.strSize]; + r.curr = r.base; + r.strLen = length; + Jupiter::strcpy(r.curr, Jupiter::CString_Type::curr + pos, length); + return r; +} + +template Jupiter::CString_Loose Jupiter::CString_Loose::getWord(size_t pos, const T *whitespace) const +{ + return Jupiter::CString_Loose::getWord(*this, pos, whitespace); +} + +template Jupiter::CString_Loose Jupiter::CString_Loose::getWord(const Jupiter::String_Type &in, size_t pos, const T *whitespace) +{ + return Jupiter::CString_Loose::getWord(in.c_str(), pos, whitespace); +} + +template Jupiter::CString_Loose Jupiter::CString_Loose::getWord(const T *in, size_t pos, const T *whitespace) +{ + Jupiter::CString_Loose r; + unsigned int x = 0; + unsigned int y = 1; + unsigned int i; + for (i = 0; i < pos || y == 1; x++) + { + if (in[x] == 0) return r; + if (Jupiter::strpbrk(whitespace, in[x]) != nullptr) + { + if (y != 1) + { + y = 1; + i++; + } + } + else + { + if (i >= pos) break; + y = 0; + } + } + for (y = x; Jupiter::strpbrk(whitespace, in[y]) == nullptr && in[y] != 0; y++); + r.strLen = y - x; + r.strSize = getPowerTwo32(r.strLen + 1); + delete[] r.base; + r.base = new char[r.strSize]; + r.curr = r.base; + for (i = 0; x < y; i++) + { + r.curr[i] = in[x]; + x++; + } + r.curr[i] = 0; + return r; +} + +template Jupiter::CString_Loose Jupiter::CString_Loose::gotoWord(size_t pos, const T *whitespace) const +{ + return Jupiter::CString_Loose::gotoWord(*this, pos, whitespace); +} + +template Jupiter::CString_Loose Jupiter::CString_Loose::gotoWord(const Jupiter::String_Type &in, size_t pos, const T *whitespace) +{ + Jupiter::CString_Loose r; + unsigned int x = 0; + bool y = true; + for (unsigned int i = 0; i < pos || y == true; x++) + { + if (in.get(x) == 0) return r; + if (Jupiter::strpbrk(whitespace, in.get(x)) != nullptr) + { + if (y == false) + { + y = true; + i++; + } + } + else + { + if (i >= pos) break; + y = false; + } + } + // Remake later to get character-by-character -- c_str not guaranteed to be fast. + r = in.c_str() + x; + return r; +} + +template size_t Jupiter::CString_Loose::set(const CString_Loose &in) +{ + if (Jupiter::CString_Loose::strSize <= in.strLen) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Loose::strSize = in.strSize; + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = in.strLen; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.curr); + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::set(const String_Type &in) +{ + size_t sSize = in.size() + 1; + if (Jupiter::CString_Loose::strSize < sSize) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Loose::strSize = getPowerTwo32(sSize); + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = sSize - 1; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.c_str()); + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::set(const std::basic_string &in) +{ + size_t sSize = in.size() + 1; + if (Jupiter::CString_Loose::strSize < sSize) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Loose::strSize = getPowerTwo32(sSize); + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = sSize - 1; + Jupiter::strcpy(Jupiter::CString_Type::curr, in.c_str()); + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::set(const T *in) +{ + size_t sSize = Jupiter::strlen(in) + 1; + if (Jupiter::CString_Loose::strSize < sSize) + { + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Loose::strSize = getPowerTwo32(sSize); + Jupiter::CString_Type::base = new T[Jupiter::CString_Loose::strSize]; + } + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + Jupiter::CString_Type::strLen = sSize - 1; + Jupiter::strcpy(Jupiter::CString_Type::curr, in); + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::set(const T in) +{ + if (in == 0) + { + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = 0; + return Jupiter::CString_Type::strLen = 0; + } + Jupiter::CString_Type::strLen = 1; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + *Jupiter::CString_Type::curr = in; + Jupiter::CString_Type::curr[1] = 0; + return 1; +} + +template size_t Jupiter::CString_Loose::concat(const String_Type &in) +{ + size_t nLen = Jupiter::CString_Type::strLen + in.size(); + if (nLen + 1 > Jupiter::CString_Loose::strSize) + { + Jupiter::CString_Loose::strSize = getPowerTwo32(nLen + 1); + T *tmp = new T[Jupiter::CString_Loose::strSize]; + Jupiter::strcpy(tmp, Jupiter::CString_Type::curr); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmp; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + else if (nLen + 1 + (Jupiter::CString_Type::curr - Jupiter::CString_Type::base) > Jupiter::CString_Loose::strSize) + { + Jupiter::strcpy(Jupiter::CString_Type::base, Jupiter::CString_Type::curr); + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + Jupiter::strcpy(Jupiter::CString_Type::curr + Jupiter::CString_Type::strLen, in.c_str()); + Jupiter::CString_Type::strLen = nLen; + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::concat(const std::basic_string &in) +{ + size_t nLen = Jupiter::CString_Type::strLen + in.size(); + if (nLen + 1 > Jupiter::CString_Loose::strSize) + { + Jupiter::CString_Loose::strSize = getPowerTwo32(nLen + 1); + T *tmp = new T[Jupiter::CString_Loose::strSize]; + Jupiter::strcpy(tmp, Jupiter::CString_Type::curr); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmp; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + else if (nLen + 1 + (Jupiter::CString_Type::curr - Jupiter::CString_Type::base) > Jupiter::CString_Loose::strSize) + { + Jupiter::strcpy(Jupiter::CString_Type::base, Jupiter::CString_Type::curr); + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + Jupiter::strcpy(Jupiter::CString_Type::curr + Jupiter::CString_Type::strLen, in.c_str()); + Jupiter::CString_Type::strLen = nLen; + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::concat(const T *in) +{ + size_t nLen = Jupiter::CString_Type::strLen + Jupiter::strlen(in); + if (nLen + 1 > Jupiter::CString_Loose::strSize) + { + Jupiter::CString_Loose::strSize = getPowerTwo32(nLen + 1); + T *tmp = new T[Jupiter::CString_Loose::strSize]; + Jupiter::strcpy(tmp, Jupiter::CString_Type::curr); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmp; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + else if (nLen + 1 + (Jupiter::CString_Type::curr - Jupiter::CString_Type::base) > Jupiter::CString_Loose::strSize) + { + Jupiter::strcpy(Jupiter::CString_Type::base, Jupiter::CString_Type::curr); + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + Jupiter::strcpy(Jupiter::CString_Type::curr + Jupiter::CString_Type::strLen, in); + Jupiter::CString_Type::strLen = nLen; + return Jupiter::CString_Type::strLen; +} + +template size_t Jupiter::CString_Loose::concat(const T in) +{ + if (Jupiter::CString_Type::strLen + 1 == Jupiter::CString_Loose::strSize) + { + Jupiter::CString_Loose::strSize = getPowerTwo32(Jupiter::CString_Loose::strSize + 1); + T *tmp = new T[Jupiter::CString_Loose::strSize]; + Jupiter::strcpy(tmp, Jupiter::CString_Type::curr); + delete[] Jupiter::CString_Type::base; + Jupiter::CString_Type::base = tmp; + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + else if (Jupiter::CString_Type::strLen + 1 + (Jupiter::CString_Type::curr - Jupiter::CString_Type::base) >= Jupiter::CString_Loose::strSize) + { + Jupiter::strcpy(Jupiter::CString_Type::base, Jupiter::CString_Type::curr); + Jupiter::CString_Type::curr = Jupiter::CString_Type::base; + } + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = in; + Jupiter::CString_Type::strLen++; + Jupiter::CString_Type::curr[Jupiter::CString_Type::strLen] = 0; + return Jupiter::CString_Type::strLen; +} + +#endif // _CSTRING_IMP_H_HEADER \ No newline at end of file diff --git a/Jupiter/Command.cpp b/Jupiter/Command.cpp new file mode 100644 index 0000000..6237da1 --- /dev/null +++ b/Jupiter/Command.cpp @@ -0,0 +1,74 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#include +#include "ArrayList.h" +#include "Functions.h" +#include "Command.h" + +struct Jupiter::Command::Data +{ +public: + Jupiter::ArrayList> triggers; +}; + +Jupiter::Command::Command() +{ + Jupiter::Command::data_ = new Jupiter::Command::Data(); +} + +Jupiter::Command::Command(const Command &command) +{ + Jupiter::Command::data_ = new Jupiter::Command::Data(); + Jupiter::CStringS *trigger; + for (int i = command.data_->triggers.size() - 1; i >= 0; i--) + { + trigger = new Jupiter::CStringS(*command.data_->triggers.get(i)); + Jupiter::Command::data_->triggers.add(trigger); + } +} + +Jupiter::Command::~Command() +{ + for (int i = Jupiter::Command::data_->triggers.size() - 1; i >= 0; i--) delete Jupiter::Command::data_->triggers.remove(i); + delete Jupiter::Command::data_; +} + +// Command Functions + +void Jupiter::Command::addTrigger(const char *trigger) +{ + Jupiter::CStringS *aTrigger = new Jupiter::CStringS(trigger); + Jupiter::Command::data_->triggers.add(aTrigger); +} + +const char *Jupiter::Command::getTrigger(short index) const +{ + return Jupiter::Command::data_->triggers.get(index)->c_str(); +} + +unsigned int Jupiter::Command::getTriggerCount() const +{ + return Jupiter::Command::data_->triggers.size(); +} + +bool Jupiter::Command::matches(const char *trigger) const +{ + unsigned int size = Jupiter::Command::data_->triggers.size(); + for (unsigned int i = 0; i < size; i++) if (Jupiter::Command::data_->triggers.get(i)->equalsi(trigger)) return true; + return false; +} diff --git a/Jupiter/Command.h b/Jupiter/Command.h new file mode 100644 index 0000000..3cdc7e3 --- /dev/null +++ b/Jupiter/Command.h @@ -0,0 +1,100 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _COMMAND_H_HEADER +#define _COMMAND_H_HEADER + +/** + * @file Command.h + * @brief Provides an extendable command system. + */ + +#include "Jupiter.h" +#include "ArrayList.h" +#include "CString.h" + +namespace Jupiter +{ + + /** + * @brief Provides the basis of a command system. + */ + class JUPITER_API Command + { + public: + /** + * @brief Adds a trigger to the command. + * + * @param trigger Trigger to add to the command. + */ + void addTrigger(const char *trigger); + + /** + * @brief Fetches a command's specified trigger. + * + * @param index Index of the trigger to return. + * @return Trigger of the command at the specified index. + */ + const char *getTrigger(short index = 0) const; + + /** + * @brief Returns the number of triggers accepted by the command. + * + * @return Number of triggers the command accepts. + */ + unsigned int getTriggerCount() const; + + /** + * @brief Checks if a specified trigger matches one of the stored triggers. + * Note: Case insensitive. + * + * @param trigger Trigger to check against the trigger list. + * @return True if a match was found, false otherwise. + */ + bool matches(const char *trigger) const; + + /** + * @brief Returns a brief explanation and syntax description about a command. + * + * @return Brief description of command and syntax. + */ + virtual const char *getHelp() = 0; + + /** + * @brief Default constructor for command class. + */ + Command(); + + /** + * @brief Copy constructor for command class. + */ + Command(const Command &command); + + /** + * @brief destructor for command class. + */ + virtual ~Command(); + + /** Private members */ + private: + struct Data; + Data *data_; + }; + +} + +#endif // _COMMAND_H_HEADER \ No newline at end of file diff --git a/Jupiter/DLList.h b/Jupiter/DLList.h new file mode 100644 index 0000000..681b75f --- /dev/null +++ b/Jupiter/DLList.h @@ -0,0 +1,276 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _DLLIST_H_HEADER +#define _DLLIST_H_HEADER + +#include "Jupiter.h" +#include "List.h" + +/** +* @file DLList.h +* @brief Provides a generic Doubly Linked List implementation using the List interface. +*/ + +namespace Jupiter +{ + /** + * @brief Provides a Doubly Linked List implementation using the List interface. + */ + template class DLList : public List + { + public: + + /** + * @brief Stores a pointer to data, and a pointer to the next node in the list. + */ + struct Node + { + Node *next; + Node *previous; + T *data; + }; + + /** + * @brief Returns the n'th Node in the list. + * + * @param n Index of the node to return. + * @return n'th Node in the list. + */ + Node *getNode(unsigned int n) const; + + /** + * @brief Gets the data at a specified index. + * + * @param index Index of the data to get. + * @return Data stored at the specified index. + */ + T *get(unsigned int index) const; + + /** + * @brief Removes the n'th Node in the list, and returns its contents. + * + * @param n Index of the node to remove. + * @return Contents of the node removed. + */ + T *remove(unsigned int n); + + /** + * @brief Removes a node from the list. + * + * @param data Node to remove. + * @return Contents of the node removed. + */ + T *remove(Node *data); + + /** + * @brief Adds data to the list at a specified index. + * + * @param data Data to add to the list. + * @param index Position in the list to add the data to. + */ + void add(T *data, unsigned int index); + + /** + * @brief Adds data to the end of the list. + * + * @param data Data to add to the list. + */ + void add(T *data); + + /** + * @brief Default constructor for the DLList class. + */ + DLList(); + + /** + * @brief Copy constructor for the DLList class. + */ + DLList(const DLList &); + + /** + * @brief Destructor for the DLList class. + * Note: This does not delete data added to the list. + */ + ~DLList(); + + /** Private members */ + private: + Node *head; + Node *end; + }; + +} + +// Implementation + +template Jupiter::DLList::DLList() +{ + Jupiter::DLList::head = nullptr; + Jupiter::DLList::end = nullptr; + Jupiter::List::length = 0; +} + +template Jupiter::DLList::DLList(const Jupiter::DLList &source) +{ + Jupiter::List::length = source.length; + if (Jupiter::List::length == 0) + { + Jupiter::DLList::head = nullptr; + Jupiter::DLList::end = nullptr; + } + else if (Jupiter::List::length == 1) + { + Jupiter::DLList::Node *n = new Jupiter::DLList::Node; + n->data = source.getNode(0)->data; + Jupiter::DLList::head = n; + Jupiter::DLList::end = n; + } + else + { + Jupiter::DLList::Node *sourceNode = source.getNode(0); + Jupiter::DLList::Node *n = new Jupiter::DLList::Node; + n->data = sourceNode->data; + Jupiter::DLList::head = n; + sourceNode = sourceNode->next; + + while (sourceNode->next != nullptr) + { + n->next = new Jupiter::DLList::Node; + n = n->next; + n->data = sourceNode->data; + sourceNode = sourceNode->next; + } + + n->next = new Jupiter::DLList::Node; + n = n->next; + n->data = sourceNode->data; + + Jupiter::DLList::end = n; + } +} + +template Jupiter::DLList::~DLList() +{ + Jupiter::DLList::Node *p; + Jupiter::DLList::Node *c = Jupiter::DLList::head; + while (c != nullptr) + { + p = c; + c = c->next; + delete p; + } +} + +template typename Jupiter::DLList::Node *Jupiter::DLList::getNode(unsigned int index) const +{ + Jupiter::DLList::Node *r; + if (index * 2 < Jupiter::List::length) + { + r = Jupiter::DLList::head; + for (unsigned int i = 0; i < index; i++) r = r->next; + return r; + } + r = Jupiter::DLList::end; + for (unsigned int i = Jupiter::List::length - 1; i > index; i--) r = r->previous; + return r; +} + +template T *Jupiter::DLList::get(unsigned int index) const +{ + return Jupiter::DLList::getNode(index)->data; +} + +template T *Jupiter::DLList::remove(unsigned int index) +{ + return Jupiter::DLList::remove(Jupiter::DLList::getNode(index)); +} + +template T *Jupiter::DLList::remove(Node *data) +{ + if (Jupiter::DLList::head == data) + { + Jupiter::DLList::head = data->next; + if (data->next != nullptr) data->next->previous = data->previous; + else Jupiter::DLList::end = nullptr; + } + else if (Jupiter::DLList::end == data) + { + Jupiter::DLList::end = data->previous; + Jupiter::DLList::end->next = nullptr; + } + else + { + data->next->previous = data->previous; + data->previous->next = data->next; + } + T *r = data->data; + delete data; + Jupiter::List::length--; + return r; +} + +template void Jupiter::DLList::add(T *data, unsigned int index) +{ + Jupiter::DLList::Node *node = new Jupiter::DLList::Node(); + node->data = data; + if (index == 0) + { + node->next = Jupiter::DLList::head; + Jupiter::DLList::head->previous = node; + Jupiter::DLList::head = node; + node->previous = nullptr; + } + else if (index == Jupiter::List::length) + { + node->previous = Jupiter::DLList::end; + Jupiter::DLList::end->next = node; + Jupiter::DLList::end = node; + node->next = nullptr; + } + else + { + Jupiter::DLList::Node *n = Jupiter::DLList::getNode(index); + node->next = n; + node->previous = n->previous; + node->previous->next = node; + n->previous = node; + } + Jupiter::List::length++; +} + +template void Jupiter::DLList::add(T *data) +{ + Jupiter::DLList::Node *n = new Jupiter::DLList::Node(); + n->data = data; + n->next = nullptr; + if (Jupiter::List::length == 0) + { + Jupiter::DLList::head = n; + Jupiter::DLList::end = n; + n->previous = nullptr; + } + else + { + n->previous = Jupiter::DLList::end; + Jupiter::DLList::end->next = n; + Jupiter::DLList::end = n; + } + Jupiter::List::length++; +} + +#endif // _DLLIST_H_HEADER \ No newline at end of file diff --git a/Jupiter/File.cpp b/Jupiter/File.cpp new file mode 100644 index 0000000..efaacde --- /dev/null +++ b/Jupiter/File.cpp @@ -0,0 +1,206 @@ +/** + * Copyright (C) Justin James - All Rights Reserved. + * Unauthorized use or copying of this file via any medium is strictly prohibited. + * This document is proprietary and confidential. + * This document should be immediately destroyed unless given explicit permission by Justin James. + * Written by Justin James + */ + +#include +#include "File.h" + +/** +* TODO: +* Upon writing Jupiter::String, port Jupiter::File to use Jupiter::String isntead of Jupiter::CString. +* Instead of an array of String, consider an ArrayList to remove the cost of constant construction/destruction when expanding. +*/ + +#if defined _WIN32 +#define stat64 _stat64 +#endif + +int64_t getFileSize(const char *file) +{ + struct stat64 data; + if (stat64(file, &data) == 0) return data.st_size; + return -1; +} + +const size_t defaultBufferSize = 8192; + +template class JUPITER_API Jupiter::CString_Strict; + +struct JUPITER_API Jupiter::File::Data +{ + Jupiter::CStringS fileName; + size_t lineCount; + Jupiter::CStringS *lines; + + Data(); + Data(const Data &data); + ~Data(); +}; + +Jupiter::File::Data::Data() +{ + Jupiter::File::Data::lineCount = 0; + Jupiter::File::Data::lines = nullptr; +} + +Jupiter::File::Data::Data(const Jupiter::File::Data &data) +{ + Jupiter::File::Data::fileName = data.fileName; + if ((Jupiter::File::Data::lineCount = data.lineCount) != 0) + { + Jupiter::File::Data::lines = new Jupiter::CStringS[Jupiter::File::Data::lineCount]; + for (unsigned int i = 0; i != Jupiter::File::Data::lineCount; i++) Jupiter::File::Data::lines[i] = data.lines[i]; + } + else Jupiter::File::Data::lines = nullptr; +} + +Jupiter::File::Data::~Data() +{ + if (Jupiter::File::Data::lines != nullptr) delete[] Jupiter::File::Data::lines; +} + +Jupiter::File::File() +{ + Jupiter::File::data_ = new Jupiter::File::Data(); +} + +Jupiter::File::File(const File &file) +{ + Jupiter::File::data_ = new Jupiter::File::Data(*file.data_); +} + +Jupiter::File::~File() +{ + delete Jupiter::File::data_; +} + +unsigned int Jupiter::File::getLineCount() const +{ + return Jupiter::File::data_->lineCount; +} + +const Jupiter::StringType &Jupiter::File::getLine(unsigned int line) const +{ + return Jupiter::File::data_->lines[line]; +} + +const Jupiter::StringType &Jupiter::File::getFileName() const +{ + return Jupiter::File::data_->fileName; +} + +bool Jupiter::File::addData(const Jupiter::StringType &data) +{ + if (Jupiter::File::data_->lineCount == 0) + { + if ((Jupiter::File::data_->lineCount = data.wordCount(ENDL)) != 0) + { + Jupiter::File::data_->lines = new Jupiter::CStringS[Jupiter::File::data_->lineCount]; + for (unsigned int i = Jupiter::File::data_->lineCount - 1; i != 0; i--) Jupiter::File::data_->lines[i] = Jupiter::CStringS::getWord(data, i, ENDL); + Jupiter::File::data_->lines[0] = Jupiter::CStringS::getWord(data, 0, ENDL); + return true; + } + return false; + } + else + { + unsigned int wc = data.wordCount(ENDL); + if (wc != 0) + { + Jupiter::CStringS *oldLines = Jupiter::File::data_->lines; + Jupiter::File::data_->lines = new Jupiter::CStringS[Jupiter::File::data_->lineCount + wc]; + + unsigned int i; + for (i = 0; i < Jupiter::File::data_->lineCount; i++) Jupiter::File::data_->lines[i] = oldLines[i]; + delete[] oldLines; + + Jupiter::File::data_->lineCount += wc; + for (unsigned int b = 0; b < wc; i++, b++) Jupiter::File::data_->lines[i] = Jupiter::CStringS::getWord(data, b, ENDL); + return true; + } + return false; + } +} + +bool Jupiter::File::addData(const char *data) +{ + return Jupiter::File::addData(Jupiter::CStringS(data)); +} + +bool Jupiter::File::load(const char *file) +{ + FILE *filePtr = fopen(file, "rb"); + if (filePtr == nullptr) return false; + if (Jupiter::File::data_->fileName.size() == 0) Jupiter::File::data_->fileName = file; + bool r = Jupiter::File::load(filePtr); + fclose(filePtr); + return r; +} + +bool Jupiter::File::load(FILE *file) +{ + Jupiter::CStringL fileBuffer; + char buffer[defaultBufferSize]; + while (fgets(buffer, defaultBufferSize, file) != nullptr) + { + fileBuffer += buffer; + if (feof(file)) break; + } + + return Jupiter::File::addData(fileBuffer); +} + +void Jupiter::File::unload() +{ + delete Jupiter::File::data_; + Jupiter::File::data_ = new Jupiter::File::Data(); +} + +bool Jupiter::File::reload() +{ + if (Jupiter::File::data_->fileName.size() == 0) return false; + Jupiter::CStringS fileName = Jupiter::File::data_->fileName; + Jupiter::File::unload(); + return Jupiter::File::load(fileName.c_str()); +} + +bool Jupiter::File::reload(const char *file) +{ + Jupiter::File::unload(); + return Jupiter::File::load(file); +} + +bool Jupiter::File::reload(FILE *file) +{ + Jupiter::File::unload(); + return Jupiter::File::load(file); +} + +bool Jupiter::File::sync() +{ + if (Jupiter::File::data_->fileName.size() == 0) return false; + return Jupiter::File::sync(Jupiter::File::data_->fileName.c_str()); +} + +bool Jupiter::File::sync(const char *file) +{ + FILE *filePtr = fopen(file, "wb"); + if (filePtr == nullptr) return false; + Jupiter::File::sync(filePtr); // Always returns true. + fclose(filePtr); + return true; +} + +bool Jupiter::File::sync(FILE *file) +{ + for (unsigned int i = 0; i < Jupiter::File::data_->lineCount; i++) + { + fputs(Jupiter::File::data_->lines[i].c_str(), file); + fputs(ENDL, file); + } + return true; +} \ No newline at end of file diff --git a/Jupiter/File.h b/Jupiter/File.h new file mode 100644 index 0000000..f4c6121 --- /dev/null +++ b/Jupiter/File.h @@ -0,0 +1,165 @@ +/** + * Copyright (C) 2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _FILE_H_HEADER +#define _FILE_H_HEADER + +/** + * @file File.h + * @brief Provides a fast interface for accessing stored file data. + */ + +#include "Jupiter.h" +#include "CString.h" +#include + +namespace Jupiter +{ + + class JUPITER_API File + { + public: + + /** + * @brief Fetches the number of lines in the file. + * + * @return Total number of lines. + */ + unsigned int getLineCount() const; + + /** + * @brief Fetches a line of the file. + * + * @param line Index of the line to fetch. + * @return Line of text at the specified index. + */ + const Jupiter::StringType &getLine(unsigned int line) const; + + /** + * @brief Returns the name of the first raw file originally loaded. + * + * @return String containing the name of the first file loaded into this file. + */ + const Jupiter::StringType &getFileName() const; + + /** + * @brief Adds data to a file, which may consist of one or more lines. + * + * @param data Data to add to the file. + * @param True if data was added to the file, false otherwise. + */ + bool addData(const Jupiter::StringType &data); + + /** + * @brief Adds data to a file, which may consist of one or more lines. + * + * @param data Data to add to the file. + * @param True if data was added to the file, false otherwise. + */ + bool addData(const char *data); + + /** + * @brief Loads a file from the drive into this file. + * + * @param file name of the file to load from drive. + * @return True if a file was successfully loaded from the drive, false otherwise. + */ + bool load(const char *file); + + /** + * @brief Loads a file from the drive into this file. + * + * @param file C FILE stream to read from. This must not be an endless stream (such as stdin). + * @return True if a file was successfully loaded from the drive, false otherwise. + */ + bool load(FILE *file); + + /** + * @brief Frees all of the stored data of the file, and puts it in a new state, ready for loading. + */ + void unload(); + + /** + * @brief Unloads all of a file's contents, and attempts to load from the originally loaded file. + * + * @return True if a file was successfully loaded from the drive, false otherwise. + */ + bool reload(); + + /** + * @brief Unloads all of a file's contents, and attempts to load from a specified file. + * + * @param file Name of a file to load from the drive. + * @return True if a file was successfully loaded from the drive, false otherwise. + */ + bool reload(const char *file); + + /** + * @brief Unloads all of a file's contents, and attempts to load from a specified file stream. + * + * @param file C FILE stream to read from. This must not be an endless stream (such as stdin). + * @return True if a file was successfully loaded from the drive, false otherwise. + */ + bool reload(FILE *file); + + /** + * @brief Syncs data from the file to the drive. This will write to the first loaded file. + * + * @return True if the file was successfully written to the drive, false otherwise. + */ + bool sync(); + + /** + * @brief Syncs data from the file to the drive. + * + * @param file Name of the file to write to. + * @return True if the file was successfully written to the drive, false otherwise. + */ + bool sync(const char *file); + + /** + * @brief Syncs data from the file to the drive. + * + * @param file C FILE stream to output to. + * @return True if the file was successfully written to the drive, false otherwise. + */ + bool sync(FILE *file); + + /** + * @brief Default constructor for File class. + */ + File(); + + /** + * @brief Copy constructor for File class. + */ + File(const File &file); + + /** + * @brief Destructor for File class. + */ + virtual ~File(); + + /** Private members */ + private: + struct Data; + Data *data_; + }; + +} + +#endif // _FILE_H_HEADER \ No newline at end of file diff --git a/Jupiter/Functions.c b/Jupiter/Functions.c new file mode 100644 index 0000000..7b1111a --- /dev/null +++ b/Jupiter/Functions.c @@ -0,0 +1,523 @@ +/** + * Copyright (C) Justin James - All Rights Reserved. + * Unauthorized use or copying of this file via any medium is strictly prohibited. + * This document is proprietary and confidential. + * This document should be immediately destroyed unless given explicit permission by Justin James. + * Written by Justin James + */ + +#include // malloc +#include // uintxx_t typedefs. +#include // fprintf and stderr +#include // Used by getTime() +#include // toupper +#include // towupper +#include "Functions.h" + +// Little Endian +unsigned int getZeroByte(uint32_t *v) +{ + if ((*v & 0x000000FF) == 0) return 0; + if ((*v & 0x0000FF00) == 0) return 1; + if ((*v & 0x00FF0000) == 0) return 2; + if ((*v & 0xFF000000) == 0) return 3; + return 4; +} + +unsigned int getZeroByte2(uint64_t v) +{ + if ((v & 0x00000000000000FF) == 0) return 0; + if ((v & 0x000000000000FF00) == 0) return 1; + if ((v & 0x0000000000FF0000) == 0) return 2; + if ((v & 0x00000000FF000000) == 0) return 3; + if ((v & 0x000000FF00000000) == 0) return 4; + if ((v & 0x0000FF0000000000) == 0) return 5; + if ((v & 0x00FF000000000000) == 0) return 6; + if ((v & 0xFF00000000000000) == 0) return 7; + return 8; +} + +/* +// Big Endian +unsigned int getZeroByte(uint32_t v) +{ + if ((v & 0xFF000000) == 0) return 0; + if ((v & 0x00FF0000) == 0) return 1; + if ((v & 0x0000FF00) == 0) return 2; + if ((v & 0x000000FF) == 0) return 3; + return 4; +} + +unsigned int getZeroByte64(uint64_t v) +{ + if ((v & 0xFF00000000000000) == 0) return 0; + if ((v & 0x00FF000000000000) == 0) return 1; + if ((v & 0x0000FF0000000000) == 0) return 2; + if ((v & 0x000000FF00000000) == 0) return 3; + if ((v & 0x00000000FF000000) == 0) return 4; + if ((v & 0x0000000000FF0000) == 0) return 5; + if ((v & 0x000000000000FF00) == 0) return 6; + if ((v & 0x00000000000000FF) == 0) return 7; + return 8; +} +*/ + +uint32_t getPowerTwo32(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; + +} + +uint64_t getPowerTwo64(uint64_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +#define STRLEN_WRAPPER(TYPE) \ + register const TYPE *s = str; \ + while (*s != 0) s++; \ + return s - str; + +size_t Jupiter_strlen(const char *str) +{ + STRLEN_WRAPPER(char) +} + +size_t Jupiter_wstrlen(const wchar_t *str) +{ + STRLEN_WRAPPER(wchar_t) +} + +size_t Jupiter_strlen8(const uint8_t *str) +{ + STRLEN_WRAPPER(uint8_t) +} + +size_t Jupiter_strlen16(const uint16_t *str) +{ + STRLEN_WRAPPER(uint16_t) +} + +size_t Jupiter_strlen32(const uint32_t *str) +{ + STRLEN_WRAPPER(uint32_t) +} + +size_t Jupiter_strlen64(const uint64_t *str) +{ + STRLEN_WRAPPER(uint64_t) +} + +size_t Jupiter_vstrlen(const void *str, size_t size) +{ + const char *s; + const char *s2; + switch (size) + { + case 0: + return 0; + case 1: + return Jupiter_strlen((const char *)str); + case 2: + return Jupiter_strlen16((const uint16_t *)str); + case 4: + return Jupiter_strlen32((const uint32_t *)str); + case 8: + return Jupiter_strlen64((const uint64_t *)str); + default: + s = (const char *)str; + s2 = s; + while ((unsigned)(s2 - s) != size) + { + s2 = s; + while ((unsigned)(s2 - s) > size) + { + if (*s2 != 0) break; + s2++; + } + s += size; + } + return (s - (const char *)str) / size; + } +} + +const char *stristr(const char *str1, const char *str2) +{ + const char *strr; + int i; + int a; + for (i = 0; str1[i] != 0; i++) + { + if (toupper(str1[i]) == toupper(str2[0])) + { + a = 1; + while (str2[a] != 0) + { + if (toupper(str1[i + a]) != toupper(str2[a])) break; + a++; + } + if (str2[a] == 0) + { + strr = &str1[i]; + return strr; + } + } + } + return NULL; +} + +bool streql(const char *s1, const char *s2) +{ + if (s1 == s2) return true; + while (*s1 != 0 && *s1 == *s2) + { + s1++; + s2++; + } + return (*s1 == *s2); +} + +bool streqli(const char *s1, const char *s2) +{ + if (s1 == s2) return true; + while (*s1 != 0 && toupper(*s1) == toupper(*s2)) + { + s1++; + s2++; + } + if (*s1 == *s2) return true; + return false; +} + +bool wstreql(const wchar_t *s1, const wchar_t *s2) +{ + if (s1 == s2) return true; + while (*s1 != 0 && *s1 == *s2) + { + s1++; + s2++; + } + return (*s1 == *s2); +} + +bool wstreqli(const wchar_t *s1, const wchar_t *s2) +{ + if (s1 == s2) return true; + while (*s1 != 0 && towupper(*s1) == towupper(*s2)) + { + s1++; + s2++; + } + if (*s1 == *s2) return true; + return false; +} + +bool strmatch(const char *f, const char *s) +{ + while (*f != 0) + { + if (*f == '*') + { + f++; + if (*f == 0) return true; + while (*s != 0) + { + if (*f == *s) break; + s++; + } + if (*s == 0) return false; + } + else if (*f != '?' && *f != *s) return false; + f++; + s++; + } + return *f == *s; +} + +bool strmatchi(const char *f, const char *s) +{ + while (*f != 0) + { + if (*f == '*') + { + f++; + if (*f == 0) return true; + while (*s != 0) + { + if (*f == *s) break; + s++; + } + if (*s == 0) return false; + } + else if (*f != '?' && toupper(*f) != toupper(*s)) return false; + f++; + s++; + } + return *f == *s; +} + +bool wstrmatch(const wchar_t *f, const wchar_t *s) +{ + while (*f != 0) + { + if (*f == L'*') + { + f++; + if (*f == 0) return true; + while (*s != 0) + { + if (*f == *s) break; + s++; + } + if (*s == 0) return false; + } + else if (*f != L'?' && *f != *s) return false; + f++; + s++; + } + return *f == *s; +} + +bool wstrmatchi(const wchar_t *f, const wchar_t *s) +{ + while (*f != 0) + { + if (*f == L'*') + { + f++; + if (*f == 0) return true; + while (*s != 0) + { + if (*f == *s) break; + s++; + } + if (*s == 0) return false; + } + else if (*f != L'?' && towupper(*f) != towupper(*s)) return false; + f++; + s++; + } + return *f == *s; +} + +char *charToChar(const char *a, int b, int c) +{ + char *r = (char *) malloc(sizeof(char) * (c-b+1)); + if (r != NULL) + { + int i = 0; + while (b < c) + { + r[i] = a[b]; + i++; + b++; + } + r[i] = 0; + } + else fprintf(stderr, "ERROR: UNABLE TO ALLOCATE IN %s" ENDL, __FUNCTION__); + return r; +} + +wchar_t *wcharToChar(const wchar_t *a, int b, int c) +{ + wchar_t *r = (wchar_t *)malloc(sizeof(wchar_t)* (c - b + 1)); + if (r != NULL) + { + int i = 0; + while (b < c) + { + r[i] = a[b]; + i++; + b++; + } + r[i] = 0; + } + else fprintf(stderr, "ERROR: UNABLE TO ALLOCATE IN %s" ENDL, __FUNCTION__); + return r; +} + +void trim(char *str) +{ + int p = 0; + int x = 0; + int i; + while (str[p] != 0) + { + if (str[p] == 10) + { + str[p] = 0; + } + else if (str[p] == 13) + { + str[p] = 0; + } + p++; + } + for (i = 0; i < p; i++) + { + if (str[i] != 0) + { + str[x] = str[i]; + x++; + } + } + str[x] = 0; +} + +char *getWord(const char *str, int w) +{ + char *result; + int x = 0; + int y; + int i; + for (i = 0; i < w; x++) + { + if (str[x] == 0) return NULL; + if (str[x] == ' ') i++; + } + for (y = x; str[y] != ' ' && str[y] != 0; y++); + result = (char *) malloc(sizeof(char) * (y-x+1)); + if (result != NULL) + { + for (i = 0; x < y; i++) + { + result[i] = str[x]; + x++; + } + result[i] = 0; + } + else fprintf(stderr, "ERROR: UNABLE TO ALLOCATE IN %s" ENDL, __FUNCTION__); + return result; +} + +unsigned int countSymbol(const char *str, char c) +{ + unsigned int result = 0; + while(*str != 0) + { + if (*str == c) result++; + str++; + } + return result; +} + +unsigned int wordCount(const char *str) +{ + unsigned int result = 0; + int i; + for (i = 0; str[i] != 0; i++) + { + if (str[i] == ' ') + { + if (i > 0) + { + if (str[i-1] > ' ') result++; + } + } + else if (str[i+1] < ' ') result++; + } + return result; +} + +unsigned int countLines(const char *str) +{ + unsigned int r = 0; + while (*str != 0) + { + if ((*str == '\r' && *(str + 1) != '\n') || *str == '\n') r++; + str++; + } + return r; +} + +char *getLine(const char *str, unsigned int l) +{ + char *result; + unsigned int x = 0; + unsigned int y; + unsigned int i; + for (i = 0; i < l; x++) + { + if (str[x] == 0) break; + if (str[x] == '\n' || (str[x] == '\r' && str[x+1] != '\n')) i++; + } + for (y = x; str[y] != '\r' && str[y] != '\n' && str[y] != 0; y++);// if (str[y] == 0) break; + result = (char *) malloc(sizeof(char) * (y-x+1)); + if (result != NULL) + { + for (i = 0; x < y; i++) + { + result[i] = str[x]; + x++; + } + result[i] = 0; + } + else fprintf(stderr, "ERROR: UNABLE TO ALLOCATE IN %s" ENDL, __FUNCTION__); + return result; +} + +int findSymbol(const char *str, char c, int pos) +{ + int r = 0; + int a = 0; + while (str[r] != 0) + { + if (str[r] == c) + { + if (a == pos) return r; + a++; + } + r++; + } + return -1; +} + +bool containsSymbol(const char *str, char c) +{ + int i; + for (i = 0; str[i] != 0; i++) if (str[i] == c) return true; + return false; +} + +char *makestr(const char *str) +{ + char *r; + size_t len; + if (str == NULL) return NULL; + len = Jupiter_strlen(str); + r = (char *) malloc(sizeof(char) * (len + 1)); + if (r != NULL) + { + r[len] = 0; + while (len != 0) + { + len--; + r[len] = str[len]; + } + return r; + } + return NULL; +} + +char *getTime() +{ + time_t rawtime = time(0); + static char rtime[256]; + strftime(rtime, sizeof(rtime), "%a %b %d %H:%M:%S %Y %Z", localtime(&rawtime)); + return rtime; +} diff --git a/Jupiter/Functions.h b/Jupiter/Functions.h new file mode 100644 index 0000000..49d0e1c --- /dev/null +++ b/Jupiter/Functions.h @@ -0,0 +1,391 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _FUNCTIONS_H_HEADER +#define _FUNCTIONS_H_HEADER + +#include "Jupiter.h" +#define ENDL "\r\n" +#define WHITESPACE " \t" + +/** + * @file Functions.h + * @brief Provides some functions to be used at your convenience. + */ + +#if defined __cplusplus + +#include + +namespace Jupiter +{ + /** + * @brief Returns the length of a C-style string. + * This will often get optimized by the compiler such that the calculation time is 0. + * + * @param str C-style string to get the length of. + * @return Length of the C-style string. + */ + template inline size_t strlen(const T *str); + + /** + * @brief Copies a string of elements from one buffer to another. + * Note: The destination buffer must be at least as long as the source + 1. + * + * @param dest Destination buffer for the string of elements. + * @param source Buffer containing the source elements. + * @return Pointer to the destination. + */ + template inline T *strcpy(T *dest, const T *source); + + /** + * @brief Copies a string of elements from one buffer to another. + * Note: The destination buffer must be at least as long as sourceLength + 1. + * + * @param dest Destination buffer for the string of elements. + * @param source Buffer containing the source elements. + * @param sourceLength Number of elements to copy, excluding null-terminator. + * @return Pointer to the destination. + */ + template inline T *strcpy(T *dest, const T *source, size_t sourceLength); + + /** + * @brief Returns a pointer to the first occurance of an element from str2 appearing in str1, or nullptr. + * Note: The destination buffer must be at least as long as sourceLength + 1. + * + * @param str1 String to be scanned. + * @param str2 String containing elements to find. + * @return Pointer to the first instance of a character in str2 being in str1, or nullptr if there is none. + */ + template inline const T *strpbrk(const T *str1, const T *str2); + template inline const T *strpbrk(const T *str1, const T str2); + + /** + * @brief Checks if two strings are equal. + * + * @param str1 String to be compared. + * @param str2 String to be compared. + * @return True if the strings are equal, false otherwise. + */ + template bool streql(const T *str1, const T *str2); +} + +extern "C" +{ +#else // __cplusplus +#include +#include +#endif // __cplusplus + +/** +* @brief Case insensitive version of strstr(). +* +* @param str1 String to be scanned. +* @param str2 String to be matched. +* @return Pointer to the first occurance of str2 in str1 if it exists, NULL otherwise. +*/ +JUPITER_API const char *stristr(const char *str1, const char *str2); + +/** +* @brief Returns the length of a C-style string. +* +* @param str C-style string to get the length of. +* @return Length of the C-style string. +*/ +JUPITER_API size_t Jupiter_strlen(const char *str); + +/** +* @brief Returns the length of a C-style string. +* +* @param str C-style string to get the length of. +* @return Length of the C-style string. +*/ +JUPITER_API size_t Jupiter_wstrlen(const wchar_t *str); + +/** +* @brief Returns the length of a C-style string. +* +* @param str C-style string to get the length of. +* @return Length of the C-style string. +*/ +JUPITER_API size_t Jupiter_strlen8(const uint8_t *str); + +/** +* @brief Returns the length of a C-style string. +* +* @param str C-style string to get the length of. +* @return Length of the C-style string. +*/ +JUPITER_API size_t Jupiter_strlen16(const uint16_t *str); + +/** +* @brief Returns the length of a C-style string. +* +* @param str C-style string to get the length of. +* @return Length of the C-style string. +*/ +JUPITER_API size_t Jupiter_strlen32(const uint32_t *str); + +/** +* @brief Returns the length of a C-style string. +* +* @param str C-style string to get the length of. +* @return Length of the C-style string. +*/ +JUPITER_API size_t Jupiter_strlen64(const uint64_t *str); + +/** +* @brief Returns the length of a C-style string. +* +* @param str C-style string to get the length of. +* @return Length of the C-style string. +*/ +JUPITER_API size_t Jupiter_vstrlen(const void *str, size_t size); + +/** +* @brief Checks if two strings are equal. This function is case-sensitive. +* +* @param str1 String to be compared. +* @param str2 String to be compared. +* @return True if the strings are equal, false otherwise. +*/ +JUPITER_API bool streql(const char *str1, const char *str2); +JUPITER_API bool wstreql(const wchar_t *str1, const wchar_t *str2); + +/** +* @brief Checks if two strings are equal. This function is not case-sensitive. +* +* @param str1 String to be compared. +* @param str2 String to be compared. +* @return True if the strings are equal, false otherwise. +*/ +JUPITER_API bool streqli(const char *str1, const char *str2); +JUPITER_API bool wstreqli(const wchar_t *str1, const wchar_t *str2); + +/** +* @brief Checks if a string matches a wildcard format. +* Note: Case sensitive. +* +* @param format Sring containing the wildcard format information. +* @param str2 String to be compared. +* @return True if the string matches the wildcard format, false otherwise. +*/ +JUPITER_API bool strmatch(const char *format, const char *str); +JUPITER_API bool wstrmatch(const wchar_t *format, const wchar_t *str); + +/** +* @brief Checks if a string matches a wildcard format. +* Note: Case insensitive. +* +* @param format Sring containing the wildcard format information. +* @param str2 String to be compared. +* @return True if the string matches the wildcard format, false otherwise. +*/ +JUPITER_API bool strmatchi(const char *format, const char *str); +JUPITER_API bool wstrmatchi(const wchar_t *format, const wchar_t *str); + +/** +* @brief Returns a copy of a substring. +* +* @param str String +* @param a String to be compared. +* @param b String to be compared. +* @return String containing the substring, or NULL on malloc failure. +*/ +JUPITER_API char *charToChar(const char *str, int a, int b); +JUPITER_API wchar_t *wcharToChar(const wchar_t *str, int a, int b); + +/** +* @brief Removes any carriage-returns (\r) and new lines (\n) from a string. +* +* @param str String to be trimed. +*/ +JUPITER_API void trim(char *str); + +/** +* @brief Returns a copy of a "word" from a string. +* +* @param str String to be parsed. +* @param w Position of the word, starting at 0. +* @return String containing the word on success, or NULL otherwise. +*/ +JUPITER_API char *getWord(const char *str, int w); + +/** +* @brief Returns the number of occurances of a given character. +* +* @param str String to be scanned. +* @param c Character to be counted. +* @return Total number of occurances of a character in a string. +*/ +JUPITER_API unsigned int countSymbol(const char *str, char c); + +/** +* @brief Returns the number of space-deliminated words in a string. +* +* @param str String to be scanned. +* @return Total number of space-deliminated words in a string. +*/ +JUPITER_API unsigned int wordCount(const char *str); + +/** +* @brief Returns the number of newlines in a string. +* +* @param str String to be scanned. +* @return Total number of newlines in a string. +*/ +JUPITER_API unsigned int countLines(const char *str); + +/** +* @brief Returns a substring in the form of a new String, +* +* @param str String to be scanned. +* @return String representing a single line of the input string on success, NULL otherwise. +*/ +JUPITER_API char *getLine(const char *str, unsigned int l); + +/** +* @brief Returns the position of the n'th occurance of a character in a string. +* +* @param str String to be scanned. +* @param c Character to find. +* @param n Position of the character to search for. +* @return Position of the n'th occurance of a character if it exists, -1 otherwise. +*/ +JUPITER_API int findSymbol(const char *str, char c, int n); + +/** +* @brief Checks if a character exists in a string. +* +* @param str String to scan. +* @return True if it contains a given character, false otherwise. +*/ +JUPITER_API bool containsSymbol(const char *str, char c); + +/** +* @brief Creates a copy of a string. +* +* @param str String to copy. +* @return String containing a copy of the input string on success, NULL otherwise. +*/ +JUPITER_API char *makestr(const char *str); + +/** +* @brief returns the current time in a string format. +* This will vary depending on locale. +* Format: Day_of_week Month Day hh:mm:ss Year Timezone. +* Example 1: Sun Aug 18 13:52:21 2013 EST +* Example 2: Thu Nov 14 03:52:57 2013 Eastern Standard Time +* Exmaple 3: Sun Nov 17 15:06:19 2013 Tokyo Standard Time +* +* @return A string containing the time in the format specified above. +*/ +JUPITER_API char *getTime(); + +/** +* @brief Gets the next power of 2 after a specified number. +* +* @param number 32-bit integer to start from. +* @return Next highest power of 2. +*/ +JUPITER_API uint32_t getPowerTwo32(uint32_t number); + +/** +* @brief Gets the next power of 2 after a specified number. +* +* @param number 64-bit integer to start from. +* @return Next highest power of 2. +*/ +JUPITER_API uint64_t getPowerTwo64(uint64_t number); + +#if defined __cplusplus +} + +/** strlen implementation */ +template inline size_t Jupiter::strlen(const T *str) +{ + register const T *s = str; + while (*s != 0) s++; + return s - str; +} + +/** strcpy implementation */ +template inline T *Jupiter::strcpy(T *dest, const T *source) +{ + register T *d = dest; + while (*source != 0) + { + *d = *source; + d++; + source++; + } + *d = *source; + return dest; +} + +/** strcpy implementation */ +template inline T *Jupiter::strcpy(T *dest, const T *source, size_t length) +{ + dest[length] = 0; + while (length > 0) + { + length--; + dest[length] = source[length]; + } + return dest; +} + +template inline const T *Jupiter::strpbrk(const T *str1, const T *str2) +{ + const T *s; + while (*str1 != 0) + { + s = str2; + while (*s != 0) + { + if (*str1 == *s) return str1; + s++; + } + str1++; + } + return nullptr; +} + +template inline const T *Jupiter::strpbrk(const T *str1, const T e) +{ + while (*str1 != 0) + { + if (*str1 == e) return str1; + str1++; + } + return nullptr; +} + +template inline bool Jupiter::streql(const T *str1, const T *str2) +{ + if (str1 == str2) return true; + while (*str1 != 0 && *str1 == *str2) + { + str1++; + str2++; + } + return (*str1 == *str2); +} + +#endif // __cplusplus + +#endif // _FUNCTIONS_H_HEADER \ No newline at end of file diff --git a/Jupiter/INIFile.cpp b/Jupiter/INIFile.cpp new file mode 100644 index 0000000..5fe932d --- /dev/null +++ b/Jupiter/INIFile.cpp @@ -0,0 +1,581 @@ +/** + * Copyright (C) Justin James - All Rights Reserved. + * Unauthorized use or copying of this file via any medium is strictly prohibited. + * This document is proprietary and confidential. + * This document should be immediately destroyed unless given explicit permission by Justin James. + * Written by Justin James + */ + +#include +#include +#include +#include "Functions.h" +#include "INIFile.h" +#include "CString.h" +#include "ArrayList.h" +#include "InvalidIndex.h" + +/** +* TODO: +* Rewrite using String, and ArrayList. Consider using File when reading data. +* Add support for unspecified section. +* Consider sort/search optimization. +*/ + +/** KeyValuePair implementation */ + +struct Jupiter::INIFile::Section::KeyValuePair::Data +{ + Jupiter::CStringS key; + Jupiter::CStringS value; +}; + +Jupiter::INIFile::Section::KeyValuePair::KeyValuePair() +{ + Jupiter::INIFile::Section::KeyValuePair::data_ = new Jupiter::INIFile::Section::KeyValuePair::Data(); +} + +Jupiter::INIFile::Section::KeyValuePair::KeyValuePair(const KeyValuePair &source) +{ + Jupiter::INIFile::Section::KeyValuePair::data_ = new Jupiter::INIFile::Section::KeyValuePair::Data(); + Jupiter::INIFile::Section::KeyValuePair::data_->key = source.data_->key; + Jupiter::INIFile::Section::KeyValuePair::data_->value = source.data_->value; +} + +Jupiter::INIFile::Section::KeyValuePair::~KeyValuePair() +{ + delete Jupiter::INIFile::Section::KeyValuePair::data_; +} + +const Jupiter::StringType &Jupiter::INIFile::Section::KeyValuePair::getKey() const +{ + return Jupiter::INIFile::Section::KeyValuePair::data_->key; +} + +const Jupiter::StringType &Jupiter::INIFile::Section::KeyValuePair::getValue() const +{ + return Jupiter::INIFile::Section::KeyValuePair::data_->value; +} + +void Jupiter::INIFile::Section::KeyValuePair::setValue(const Jupiter::StringType &value) +{ + Jupiter::INIFile::Section::KeyValuePair::data_->value = value; +} + +void Jupiter::INIFile::Section::KeyValuePair::setValue(const char *value) +{ + Jupiter::INIFile::Section::KeyValuePair::data_->value = value; +} + +/** Section implementation */ + +struct Jupiter::INIFile::Section::Data +{ + Jupiter::CStringS name; + Jupiter::ArrayList data; + ~Data(); +}; + +Jupiter::INIFile::Section::Data::~Data() +{ + Jupiter::INIFile::Section::Data::data.emptyAndDelete(); +} + +Jupiter::INIFile::Section::Section() +{ + Jupiter::INIFile::Section::data_ = new Jupiter::INIFile::Section::Data(); +} + +Jupiter::INIFile::Section::Section(const Jupiter::StringType &name) +{ + Jupiter::INIFile::Section::data_ = new Jupiter::INIFile::Section::Data(); + Jupiter::INIFile::Section::data_->name = name; +} + +Jupiter::INIFile::Section::Section(const char *name) +{ + Jupiter::INIFile::Section::data_ = new Jupiter::INIFile::Section::Data(); + Jupiter::INIFile::Section::data_->name = name; +} + +Jupiter::INIFile::Section::Section(const Jupiter::INIFile::Section &source) +{ + Jupiter::INIFile::Section::data_ = new Jupiter::INIFile::Section::Data(); + Jupiter::INIFile::Section::data_->name = source.data_->name; + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + for (unsigned int i = 0; i < size; i++) + { + Jupiter::INIFile::Section::data_->data.add(new Jupiter::INIFile::Section::KeyValuePair(*source.data_->data.get(i))); + } +} + +Jupiter::INIFile::Section::~Section() +{ + delete Jupiter::INIFile::Section::data_; +} + +const Jupiter::StringType &Jupiter::INIFile::Section::getName() const +{ + return Jupiter::INIFile::Section::data_->name; +} + +const Jupiter::StringType &Jupiter::INIFile::Section::getValue(unsigned int index) const +{ + return Jupiter::INIFile::Section::data_->data.get(index)->data_->value; +} + +const Jupiter::StringType &Jupiter::INIFile::Section::getValue(const Jupiter::StringType &key) const +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + Jupiter::INIFile::Section::KeyValuePair *pair; + for (unsigned int i = 0; i < size; i++) + { + pair = Jupiter::INIFile::Section::data_->data.get(i); + if (pair->getKey().equalsi(key)) return pair->getValue(); + } + return Jupiter::emptyString; +} + +const Jupiter::StringType &Jupiter::INIFile::Section::getValue(const char *key) const +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + Jupiter::INIFile::Section::KeyValuePair *pair; + for (unsigned int i = 0; i < size; i++) + { + pair = Jupiter::INIFile::Section::data_->data.get(i); + if (pair->getKey().equalsi(key)) return pair->getValue(); + } + return Jupiter::emptyString; +} + +Jupiter::INIFile::Section::KeyValuePair *Jupiter::INIFile::Section::getPair(unsigned int index) const +{ + return Jupiter::INIFile::Section::data_->data.get(index); +} + +Jupiter::INIFile::Section::KeyValuePair *Jupiter::INIFile::Section::getPair(const Jupiter::StringType &key) const +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + Jupiter::INIFile::Section::KeyValuePair *pair; + for (unsigned int i = 0; i < size; i++) + { + pair = Jupiter::INIFile::Section::data_->data.get(i); + if (pair->getKey().equalsi(key)) return pair; + } + return nullptr; +} + +Jupiter::INIFile::Section::KeyValuePair *Jupiter::INIFile::Section::getPair(const char *key) const +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + Jupiter::INIFile::Section::KeyValuePair *pair; + for (unsigned int i = 0; i < size; i++) + { + pair = Jupiter::INIFile::Section::data_->data.get(i); + if (pair->getKey().equalsi(key)) return pair; + } + return nullptr; +} + +unsigned int Jupiter::INIFile::Section::size() const +{ + return Jupiter::INIFile::Section::data_->data.size(); +} + +bool Jupiter::INIFile::Section::hasKey(const Jupiter::StringType &key) const +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + Jupiter::INIFile::Section::KeyValuePair *pair; + for (unsigned int i = 0; i < size; i++) + { + pair = Jupiter::INIFile::Section::data_->data.get(i); + if (pair->getKey().equalsi(key)) return true; + } + return false; +} + +bool Jupiter::INIFile::Section::hasKey(const char *key) const +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + Jupiter::INIFile::Section::KeyValuePair *pair; + for (unsigned int i = 0; i < size; i++) + { + pair = Jupiter::INIFile::Section::data_->data.get(i); + if (pair->getKey().equalsi(key)) return true; + } + return false; +} + +bool Jupiter::INIFile::Section::set(Jupiter::StringType &key, Jupiter::StringType &value) +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + Jupiter::INIFile::Section::KeyValuePair *pair; + for (unsigned int i = 0; i < size; i++) + { + pair = Jupiter::INIFile::Section::data_->data.get(i); + if (pair->getKey().equalsi(key)) + { + pair->data_->value = value; + return false; + } + } + pair = new Jupiter::INIFile::Section::KeyValuePair(); + pair->data_->key = key; + pair->data_->value = value; + Jupiter::INIFile::Section::data_->data.add(pair); + return true; +} + +bool Jupiter::INIFile::Section::set(const char *key, const char *value) +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + Jupiter::INIFile::Section::KeyValuePair *pair; + for (unsigned int i = 0; i < size; i++) + { + pair = Jupiter::INIFile::Section::data_->data.get(i); + if (pair->getKey().equalsi(key)) + { + pair->data_->value = value; + return false; + } + } + pair = new Jupiter::INIFile::Section::KeyValuePair(); + pair->data_->key = key; + pair->data_->value = value; + Jupiter::INIFile::Section::data_->data.add(pair); + return true; +} + +void Jupiter::INIFile::Section::setName(const Jupiter::StringType &name) +{ + Jupiter::INIFile::Section::data_->name = name; +} + +void Jupiter::INIFile::Section::setName(const char *name) +{ + Jupiter::INIFile::Section::data_->name = name; +} + +bool Jupiter::INIFile::Section::remove(Jupiter::StringType &key) +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + for (unsigned int i = 0; i < size; i++) + { + if (Jupiter::INIFile::Section::data_->data.get(i)->getKey().equalsi(key)) + { + delete Jupiter::INIFile::Section::data_->data.remove(i); + return true; + } + } + return false; +} + +bool Jupiter::INIFile::Section::remove(const char *key) +{ + unsigned int size = Jupiter::INIFile::Section::data_->data.size(); + for (unsigned int i = 0; i < size; i++) + { + if (Jupiter::INIFile::Section::data_->data.get(i)->getKey().equalsi(key)) + { + delete Jupiter::INIFile::Section::data_->data.remove(i); + return true; + } + } + return false; +} + +/** INIFile implementation */ + +struct Jupiter::INIFile::Data +{ + Jupiter::ArrayList
data; + Jupiter::CStringS fName; + ~Data(); +}; + +Jupiter::INIFile::Data::~Data() +{ + Jupiter::INIFile::Data::data.emptyAndDelete(); +} + +Jupiter::INIFile::INIFile() +{ + Jupiter::INIFile::data_ = new Jupiter::INIFile::Data; +} + +Jupiter::INIFile::INIFile(const INIFile &source) +{ + Jupiter::INIFile::data_ = new Jupiter::INIFile::Data; + Jupiter::INIFile::data_->fName = source.data_->fName; + unsigned int size = Jupiter::INIFile::data_->data.size(); + for (unsigned int i = 0; i < size; i++) + { + Jupiter::INIFile::data_->data.add(new Jupiter::INIFile::Section(*source.data_->data.get(i))); + } +} + +Jupiter::INIFile::~INIFile() +{ + delete Jupiter::INIFile::data_; +} + +void Jupiter::INIFile::flushData() +{ + delete Jupiter::INIFile::data_; + Jupiter::INIFile::data_ = new Jupiter::INIFile::Data(); +} + +// TODO: Rewrite this. +unsigned int Jupiter::INIFile::readFile(const char *fileName) +{ + int count = 0; + if (Jupiter::INIFile::data_->fName.size() != 0) + { + Jupiter::INIFile::data_->fName = ""; + } + Jupiter::INIFile::data_->fName = fileName; + FILE *file = fopen(Jupiter::INIFile::data_->fName.c_str(), "rb"); + if (file == nullptr) return Jupiter::ERROR_INDICATOR; + char fileBuffer[8192]; + char *section; + //int nSection = 0; + while (!feof(file)) + { + if (fgets(fileBuffer, sizeof(fileBuffer), file)) + { +do1: + if (fileBuffer[strspn(fileBuffer, WHITESPACE)] != ';' && fileBuffer[strspn(fileBuffer, WHITESPACE)] == '[') + { + section = charToChar(fileBuffer, strspn(fileBuffer, WHITESPACE) + 1, strcspn(fileBuffer, "]")); +do2: + if (fgets(fileBuffer, sizeof(fileBuffer), file)) + { + size_t eqlpos = strcspn(fileBuffer, "="); + if (fileBuffer[strspn(fileBuffer, WHITESPACE)] != ';' && eqlpos != strlen(fileBuffer)) + { + size_t a = strspn(fileBuffer, WHITESPACE); + char *tkey = charToChar(fileBuffer, a, a + strcspn(fileBuffer + a, WHITESPACE "=")); + char *tval = charToChar(fileBuffer, eqlpos + 1 + strspn(fileBuffer + eqlpos + 1, WHITESPACE), strcspn(fileBuffer, ENDL)); + if (Jupiter::INIFile::set(section, tkey, tval)) count++; + delete[] tval; + delete[] tkey; + } + } + if (!feof(file)) + { + if (fileBuffer[strspn(fileBuffer, WHITESPACE)] != '[') goto do2; + else goto do1; + } + } + } + } + fclose(file); + return count; +} + +unsigned int Jupiter::INIFile::reload() +{ + Jupiter::CStringS fileName = Jupiter::INIFile::data_->fName; + Jupiter::INIFile::flushData(); + return Jupiter::INIFile::readFile(fileName.c_str()); +} + +bool Jupiter::INIFile::sync(const char *fileName) +{ + unsigned int size = Jupiter::INIFile::data_->data.size(); + unsigned int sectionSize; + Jupiter::INIFile::Section *section; + Jupiter::INIFile::Section::KeyValuePair *pair; + + FILE *file = fopen(fileName, "wb"); + if (file == nullptr) return false; + fputs("; This file was automatically generated by Agent's INI parser." ENDL "; The currently used data values are as follows." ENDL ENDL, file); + for (unsigned int a = 0; a < size; a++) + { + section = Jupiter::INIFile::data_->data.get(a); + fputc('[', file); + fputs(section->getName().c_str(), file); + fputs("]" ENDL, file); + + sectionSize = section->size(); + for (unsigned int b = 0; b < sectionSize; b++) + { + pair = section->getPair(b); + fputs(pair->getKey().c_str(), file); + fputc('=', file); + fputs(pair->getValue().c_str(), file); + fputs(ENDL, file); + } + fputs(ENDL, file); + } + fputs("; EOF", file); + fclose(file); + return true; +} + +bool Jupiter::INIFile::sync() +{ + if (Jupiter::INIFile::data_->fName.size() == 0) return false; + return Jupiter::INIFile::sync(Jupiter::INIFile::data_->fName.c_str()); +} + +bool Jupiter::INIFile::set(const char *section, const char *key, const char *value) +{ + unsigned int i = 0; + unsigned int size = Jupiter::INIFile::data_->data.size(); + Jupiter::INIFile::Section *sec; + while (i < size) + { + sec = Jupiter::INIFile::data_->data.get(i); + if (sec->getName().equalsi(section)) + { + sec->set(key, value); + return false; + } + i++; + } + sec = new Jupiter::INIFile::Section(section); + sec->set(key, value); + Jupiter::INIFile::data_->data.add(sec); + return true; +} + +bool Jupiter::INIFile::remove(const char *section, const char *key) +{ + unsigned int size = Jupiter::INIFile::data_->data.size(); + Jupiter::INIFile::Section *sec; + for (unsigned int i = 0; i < size; i++) + { + sec = Jupiter::INIFile::data_->data.get(i); + if (sec->getName().equalsi(section)) return sec->remove(key); + i++; + } + return false; +} + +unsigned int Jupiter::INIFile::getSections() const +{ + return Jupiter::INIFile::data_->data.size(); +} + +Jupiter::INIFile::Section *Jupiter::INIFile::getSection(unsigned int index) const +{ + return Jupiter::INIFile::data_->data.get(index); +} + +unsigned int Jupiter::INIFile::getSectionIndex(const char *section) const +{ + unsigned int size = Jupiter::INIFile::data_->data.size(); + for (unsigned int i = 0; i < size; i++) + { + if (Jupiter::INIFile::data_->data.get(i)->getName().equalsi(section)) return i; + } + return Jupiter::INVALID_INDEX; +} + +unsigned int Jupiter::INIFile::getSectionLength(unsigned int index) const +{ + return Jupiter::INIFile::data_->data.get(index)->size(); +} + +const char *Jupiter::INIFile::getKey(const char *section, unsigned int index) const +{ + unsigned int sectionIndex = Jupiter::INIFile::getSectionIndex(section); + if (sectionIndex == Jupiter::INVALID_INDEX) return nullptr; + return Jupiter::INIFile::data_->data.get(sectionIndex)->getPair(index)->getKey().c_str(); +} + +unsigned int Jupiter::INIFile::getKeyIndex(const char *section, const char *key) const +{ + unsigned int sectionIndex = Jupiter::INIFile::getSectionIndex(section); + if (sectionIndex == Jupiter::INVALID_INDEX) return Jupiter::INVALID_INDEX; + Jupiter::INIFile::Section *sect = Jupiter::INIFile::data_->data.get(sectionIndex); + unsigned int sectionSize = sect->size(); + for (unsigned int i = 0; i < sectionSize; i++) if (sect->getPair(i)->getKey().equalsi(key)) return i; + return Jupiter::INVALID_INDEX; +} + +const char *Jupiter::INIFile::get(const char *section, const char *key, const char *defaultValue) const +{ + unsigned int size = Jupiter::INIFile::data_->data.size(); + Jupiter::INIFile::Section *sect; + + for (unsigned int i = 0; i < size; i++) + { + sect = Jupiter::INIFile::data_->data.get(i); + if (sect->getName().equalsi(section)) + { + Jupiter::INIFile::Section::KeyValuePair *pair = sect->getPair(key); + if (pair == nullptr) return defaultValue; + return pair->getValue().c_str(); + } + } + return defaultValue; +} + +bool Jupiter::INIFile::getBool(const char *section, const char *key, bool defaultValue) const +{ + const char *val = Jupiter::INIFile::get(section, key); + if (val != nullptr) + { + if (streqli(val, "FALSE")) return false; + if (streql(val, "0")) return false; + if (streqli(val, "OFF")) return false; + return true; + } + return defaultValue; +} + +/*char Jupiter::INIFile::getChar(const char *section, const char *key) const +{ + const char *val = Jupiter::INIFile::get(section,key); + if (val != nullptr) return val[0]; + return 0; +}*/ + +short Jupiter::INIFile::getShort(const char *section, const char *key, short defaultValue) const +{ + return (short)Jupiter::INIFile::getInt(section, key, defaultValue); +} + +int Jupiter::INIFile::getInt(const char *section, const char *key, int defaultValue) const +{ + const char *val = Jupiter::INIFile::get(section, key); + if (val != nullptr) return atoi(val); + return defaultValue; +} + +long Jupiter::INIFile::getLong(const char *section, const char *key, long defaultValue) const +{ + const char *val = Jupiter::INIFile::get(section, key); + if (val != nullptr) return strtol(val, nullptr, 0); + return defaultValue; +} + +long long Jupiter::INIFile::getLongLong(const char *section, const char *key, long long defaultValue) const +{ + const char *val = Jupiter::INIFile::get(section, key); + if (val != nullptr) return strtoll(val, nullptr, 0); + return defaultValue; +} + +float Jupiter::INIFile::getFloat(const char *section, const char *key, float defaultValue) const +{ + const char *val = Jupiter::INIFile::get(section, key); + if (val != nullptr) return strtof(val, nullptr); + return defaultValue; +} + +double Jupiter::INIFile::getDouble(const char *section, const char *key, double defaultValue) const +{ + const char *val = Jupiter::INIFile::get(section, key); + if (val != nullptr) return strtod(val, nullptr); + return defaultValue; +} + +long double Jupiter::INIFile::getLongDouble(const char *section, const char *key, long double defaultValue) const +{ + const char *val = Jupiter::INIFile::get(section, key); + if (val != nullptr) return strtold(val, nullptr); + return defaultValue; +} diff --git a/Jupiter/INIFile.h b/Jupiter/INIFile.h new file mode 100644 index 0000000..7690455 --- /dev/null +++ b/Jupiter/INIFile.h @@ -0,0 +1,474 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _INIFILE_H_HEADER +#define _INIFILE_H_HEADER + +/** + * @file INIFile.h + * @brief Provides interaction with INI files. + */ + +#include "Jupiter.h" +#include "String_Type.h" +#include "InvalidIndex.h" + +namespace Jupiter +{ + /** + * @brief Provides interfacing with INI files. + * INIFile objects store the contents of .INI files into memory, + * and access them as neccessary. The INIFile object can store + * the contents of multiple files at a time, but does not store + * where data came from. As such, you can read from multiple files, + * however the contents will always go to a single file when synced. + * Disk contents are only modified when sync() is called. + */ + class JUPITER_API INIFile + { + public: + + class JUPITER_API Section + { + public: + class JUPITER_API KeyValuePair + { + friend class Jupiter::INIFile::Section; + public: + + /** + * @brief Fetches the key in the key-value pair. + * + * @return Reference to the key String in the pair. + */ + const Jupiter::StringType &getKey() const; + + /** + * @brief Fetches the value in the key-value pair. + * + * @return Reference to the value String in the pair. + */ + const Jupiter::StringType &getValue() const; + + /** + * @brief Sets the value of a key-value pair. + * + * @param value Value to set the key-value pair to. + */ + void setValue(const Jupiter::StringType &value); + + /** + * @brief Sets the value of a key-value pair. + * + * @param value Value to set the key-value pair to. + */ + void setValue(const char *value); + + /** + * @brief Default constructor for the KeyValuePair class. + */ + KeyValuePair(); + + /** + * @brief Copy constructor for the KeyValuePair class. + */ + KeyValuePair(const KeyValuePair &); + + /** + * @brief Destructor for the KeyValuePair class. + */ + ~KeyValuePair(); + + private: + struct Data; + Data *data_; + }; + + /** + * @brief Fetches the name of a section. + * + * @return Name of the section in a String. + */ + const Jupiter::StringType &getName() const; + + /** + * @brief Fetches the value of a key-value pair at a specified index. + * + * @param index Index of the key-value pair. + * @return Value of a key-value pair at a specified index. + */ + const Jupiter::StringType &getValue(unsigned int index) const; + + /** + * @brief Fetches the value of a key-value pair. + * + * @param key Key of the key-value pair. + * @return Value of a key-value pair, or an empty string if none is found. + */ + const Jupiter::StringType &getValue(const Jupiter::StringType &key) const; + + /** + * @brief Fetches the value of a key-value pair. + * + * @param key Key of the key-value pair. + * @return Value of a key-value pair, or an empty string if none is found. + */ + const Jupiter::StringType &getValue(const char *key) const; + + /** + * @brief Fetches a key-value pair at a specified index. + * + * @param index Index of the key-value pair to fetch. + * @return The key-value pair at the specified index. + */ + Jupiter::INIFile::Section::KeyValuePair *getPair(unsigned int index) const; + + /** + * @brief Fetches a key-value pair. + * + * @param key Key of the key-value pair. + * @return A key-value pair if a match is found, nullptr otherwise. + */ + Jupiter::INIFile::Section::KeyValuePair *getPair(const Jupiter::StringType &key) const; + + /** + * @brief Fetches a key-value pair. + * + * @param key Key of the key-value pair. + * @return A key-value pair if a match is found, nullptr otherwise. + */ + Jupiter::INIFile::Section::KeyValuePair *getPair(const char *key) const; + + /** + * @brief Fetches the number of key-value pairs in the section. + * + * @return Number of key-value pairs in the section. + */ + unsigned int size() const; + + /** + * @brief Checks if a section has a key-value pair with a specified key. + * + * @param key Key of the key-value pair to search for. + * @return True if a match is found, false otherwise. + */ + bool hasKey(const Jupiter::StringType &key) const; + + /** + * @brief Checks if a section has a key-value pair with a specified key. + * + * @param key Key of the key-value pair to search for. + * @return True if a match is found, false otherwise. + */ + bool hasKey(const char *key) const; + + /** + * @brief Sets a key-value pair's value to a specified value. Creates a new pair if none is found. + * + * @param key Key of the key-value pair to search for or create. + * @param value Value to set the key-value pair to. + * @return True if a new key-value pair was created, false otherwise. + */ + bool set(Jupiter::StringType &key, Jupiter::StringType &value); + + /** + * @brief Sets a key-value pair's value to a specified value. Creates a new pair if none is found. + * + * @param key Key of the key-value pair to search for or create. + * @param value Value to set the key-value pair to. + * @return True if a new key-value pair was created, false otherwise. + */ + bool set(const char *key, const char *value); + + /** + * @brief Sets the name of a section. + * + * @param name String to set the section name to. + */ + void setName(const Jupiter::StringType &name); + + /** + * @brief Sets the name of a section. + * + * @param name String to set the section name to. + */ + void setName(const char *name); + + /** + * @brief Removes a key-value pair from the section. + * + * @param key Key of the key-value pair to remove. + * @return True if a key-value pair was removed, false otherwise. + */ + bool remove(Jupiter::StringType &key); + + /** + * @brief Removes a key-value pair from the section. + * + * @param key Key of the key-value pair to remove. + * @return True if a key-value pair was removed, false otherwise. + */ + bool remove(const char *key); + + /** + * @brief Default constructor for the Section class. + */ + Section(); + + /** + * @brief Name-specifiable constructors for the Section class + */ + Section(const Jupiter::StringType &name); + Section(const char *name); + + /** + * @brief Copy constructor for the Section class. + */ + Section(const Section &); + + /** + * @brief Destructor for the Section class. + */ + ~Section(); + + private: + struct Data; + Data *data_; + }; + + /** + * @brief Flushes all stored data. + */ + void flushData(); + + /** + * @brief Reads data from a file. + * The readFile function does not currently support keys which + * are not under a section, though this is not currently an issue, + * and may still be added later. The parser will also automatically + * remove any excessive whitespace, including any whitespace prefixing + * a key name, prefixing a key value, trailing a key name, or traling + * a key value. + * + * @param fileName String containing the name of a file. + * @return The number of entries added on success, ERROR_INDICATOR (-1) otherwise. + */ + unsigned int readFile(const char *fileName); + + /** + * @brief Flushes all stored data and recollects from the last read file. + * + * @return The number of entries added on success, ERROR_INDICATOR (-1) otherwise. + */ + unsigned int reload(); + + /** + * @brief Syncs data to a file. + * + * @param fileName String containing the name of a file. + * @return True on success, false otherwise. + */ + bool sync(const char *fileName); + + /** + * @brief Syncs data to the file that was last read from. + * + * @return True on success, false otherwise. + */ + bool sync(); + + /** + * @brief Sets a key's value. Adds a key if an existing one is not found. Does not modify any files. + * + * @param section Section within which the data is to be stored. + * @param keyName String containing key name. + * @param keyValue String containing new key value. + * @return True if a new key was added, false otherwise. + */ + bool set(const char *section, const char *keyName, const char *keyValue); + + /** + * @brief Removes a key. Does not modify any files. + * + * @param section String containing section. + * @param keyName String containing key name. + * @return True if an entry was removed, false otherwise. + */ + bool remove(const char *section, const char *keyName); + + /** + * @brief Returns the number of sections in memory. + * + * @return Number of sections. + */ + unsigned int getSections() const; + + /** + * @brief Returns a section. + * + * @param index Index of the section to get. + * @return String containing section if it exists, nullptr otherwise. + */ + Jupiter::INIFile::Section *getSection(unsigned int index) const; + + /** + * @brief Returns the index of a section. + * + * @param section The name of the section. + * @return The index of section if it exists, INVALID_INDEX (-1) otherwise. + */ + unsigned int getSectionIndex(const char *section) const; + + /** + * @brief Returns the number of keys in a section. + * + * @return Number of data entries in a section. + */ + unsigned int getSectionLength(unsigned int index) const; + + /** + * @brief Returns a key name. + * + * @param section String containing section. + * @param keyIndex Index of the key. + * @return Key name on success, nullptr otherwise. + */ + const char *getKey(const char *section, unsigned int index) const; + + /** + * @brief Returns the index of a key. + * + * @param section String containing section. + * @param key String containing key name. + * @return Index of key if it exists, INVALID_INDEX (-1) otherwise. + */ + unsigned int getKeyIndex(const char *section, const char *key) const; + + /** + * @brief Returns the value of a key. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return Pointer to a string containing key value if it exists, defaultValue otherwise. + */ + const char *get(const char *section, const char *key, const char *defaultValue = nullptr) const; + + /** + * @brief Translates and returns the value of a key as a boolean. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return true if key exists and is not "false" or "0", defaultValue otherwise. + */ + bool getBool(const char *section, const char *key, bool defaultValue = false) const; + + /** + * @brief Translates and returns the value of a key as a short. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return short value of the key if it exits, defaultValue otherwise. + */ + short getShort(const char *section, const char *key, short defaultValue = 0) const; + + /** + * @brief Translates and returns the value of a key as an int. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return int value of the key if it exits, defaultValue otherwise. + */ + int getInt(const char *section, const char *key, int defaultValue = 0) const; + + /** + * @brief Translates and returns the value of a key as a long. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return long value of the key if it exits, defaultValue otherwise. + */ + long getLong(const char *section, const char *key, long defaultValue = 0) const; + + /** + * @brief Translates and returns the value of a key as a long long. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return long long value of the key if it exits, defaultValue otherwise. + */ + long long getLongLong(const char *section, const char *key, long long defaultValue = 0) const; + + /** + * @brief Translates and returns the value of a key as a float. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return float value of the key if it exits, defaultValue otherwise. + */ + float getFloat(const char *section, const char *key, float defaultValue = 0) const; + + /** + * @brief Translates and returns the value of a key as a double. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return double value of the key if it exits, defaultValue otherwise. + */ + double getDouble(const char *section, const char *key, double defaultValue = 0) const; + + /** + * @brief Translates and returns the value of a key as a long double. + * + * @param section String containing section. + * @param key String containing key name. + * @param defaultValue Value to return if none is found. + * @return long double value of the key if it exits, defaultValue otherwise. + */ + long double getLongDouble(const char *section, const char *key, long double defaultValue = 0) const; + + /** + * @brief Default constructor for the INIFile class. + */ + INIFile(); + + /** + * @brief Copy constructor for the INIFile class. + */ + INIFile(const INIFile &); + + /** + * @brief Destructor for the INIFile class. + */ + ~INIFile(); + + /** Private members */ + private: + struct Data; + Data *data_; + }; + +} + +#endif // _INIFILE_H_HEADER \ No newline at end of file diff --git a/Jupiter/IRC.cpp b/Jupiter/IRC.cpp new file mode 100644 index 0000000..608538e --- /dev/null +++ b/Jupiter/IRC.cpp @@ -0,0 +1,8 @@ +#include "IRC.h" + +const char Jupiter::IRC::CTCP = IRCCTCP[0]; /** IRC CTCP character */ +const char Jupiter::IRC::bold = IRCBOLD[0]; /** IRC bold character */ +const char Jupiter::IRC::color = IRCCOLOR[0]; /** IRC color character */ +const char Jupiter::IRC::normal = IRCNORMAL[0]; /** IRC normal character */ +const char Jupiter::IRC::reverse = IRCREVERSE[0]; /** IRC reverse character */ +const char Jupiter::IRC::underline = IRCUNDERLINE[0]; /** IRC underline character */ \ No newline at end of file diff --git a/Jupiter/IRC.h b/Jupiter/IRC.h new file mode 100644 index 0000000..0d633d5 --- /dev/null +++ b/Jupiter/IRC.h @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _IRC_H_HEADER +#define _IRC_H_HEADER + +/** + * @file IRC.h + * @brief Defines IRC protocol constants. + */ + +#include "Jupiter.h" + +/** Special Character Definitions */ + +#define IRCCTCP "\001" /** IRC CTCP character contained in a string literal */ +#define IRCBOLD "\002" /** IRC bold character contained in a string literal */ +#define IRCCOLOR "\003" /** IRC color character contained in a string literal */ +#define IRCNORMAL "\017" /** IRC normal character contained in a string literal */ +#define IRCREVERSE "\026" /** IRC reverse character contained in a string literal */ +#define IRCUNDERLINE "\037" /** IRC underline character contained in a string literal */ + +#if defined __cplusplus + +namespace Jupiter +{ + namespace IRC + { + JUPITER_API extern const char CTCP; /** IRC CTCP character */ + JUPITER_API extern const char bold; /** IRC bold character */ + JUPITER_API extern const char color; /** IRC color character */ + JUPITER_API extern const char normal; /** IRC normal character */ + JUPITER_API extern const char reverse; /** IRC reverse character */ + JUPITER_API extern const char underline; /** IRC underline character */ + } +} + +#endif // __cplusplus + +#endif // _IRC_H_HEADER \ No newline at end of file diff --git a/Jupiter/IRC_Client.cpp b/Jupiter/IRC_Client.cpp new file mode 100644 index 0000000..fbb5171 --- /dev/null +++ b/Jupiter/IRC_Client.cpp @@ -0,0 +1,1743 @@ +/** + * Copyright (C) Justin James - All Rights Reserved. + * Unauthorized use or copying of this file via any medium is strictly prohibited. + * This document is proprietary and confidential. + * This document should be immediately destroyed unless given explicit permission by Justin James. + * Written by Justin James + */ + +#include +#include +#include +#include "Jupiter.h" +#include "Functions.h" +#include "IRC_Client.h" +#include "INIFile.h" +#include "TCPSocket.h" +#include "CString.h" +#include "Plugin.h" +#include "Base64.h" + +//#define SHORT_IRC_MACROS +#include "IRC_Numerics.h" + +#if defined _WIN32 +#include +#else // _WIN32 +#include +#endif // _WIN32 + +Jupiter::INIFile _Config; +Jupiter::INIFile *Jupiter::IRC::Client::Config = &_Config; + +template class JUPITER_API Jupiter::CString_Strict; +template class JUPITER_API Jupiter::ArrayList; +template class JUPITER_API Jupiter::ArrayList; + +struct JUPITER_API Jupiter::IRC::Client::Data +{ + Jupiter::IRC::Client *iFace; + Jupiter::Socket *sock; + bool ssl; + Jupiter::CStringS SSLCertificate; + Jupiter::CStringS SSLKey; + Jupiter::CStringS saslAccount; + Jupiter::CStringS saslPass; + int connectionStatus; + Jupiter::CStringS configSectionName; + Jupiter::CStringS logFileName; + Jupiter::CStringS buffer; + unsigned short serverPort; + Jupiter::CStringS serverHostname; + Jupiter::CStringS serverName; + Jupiter::CStringS nickname; + Jupiter::CStringS realName; + Jupiter::CStringS prefixModes = "ov"; + Jupiter::CStringS prefixes = "@+"; + Jupiter::CStringS chanTypes = "#"; + Jupiter::CStringS modeA = "b"; + Jupiter::CStringS modeB = "k"; + Jupiter::CStringS modeC = "l"; + Jupiter::CStringS modeD = "psitnm"; + Jupiter::ArrayList channels; + void delChannel(unsigned int index); + void delChannel(const char *chan); + void addNamesToChannel(unsigned int channelIndex, Jupiter::CStringS &names); + unsigned int addChannel(const char *chan); + bool joinOnKick; + Jupiter::CStringS autoPartMessage; + time_t reconnectDelay; + time_t reconnectTime; + int maxReconnectAttempts; + int reconnectAttempts; + FILE *printOutput; + FILE *logFile; + short dChanType; + bool dead = false; + Jupiter::ArrayList users; + + bool startCAP(); + bool registerClient(); + Data(Jupiter::IRC::Client *interFace); + Jupiter::IRC::Client::User *findUser(const char *nick) const; + Jupiter::IRC::Client::User *findUserOrAdd(Jupiter::CStringS &nick); +}; + +Jupiter::IRC::Client::Data::Data(Jupiter::IRC::Client *interFace) +{ + Jupiter::IRC::Client::Data::iFace = interFace; +} + +struct Jupiter::IRC::Client::User::Data +{ + unsigned int channelCount = 0; + Jupiter::CStringS nickname; + Jupiter::CStringS username; + Jupiter::CStringS hostname; +}; + +struct Jupiter::IRC::Client::Channel::User::Data +{ + Jupiter::IRC::Client::User *user; + Jupiter::CStringS prefixes; +}; + +struct Jupiter::IRC::Client::Channel::Data +{ + Jupiter::CStringS channel; + Jupiter::ArrayList users; + const char *serverPrefixSetPtr; + int type; + bool isAddingNames; +}; + +Jupiter::IRC::Client::Client(const char *configSection) +{ + Jupiter::IRC::Client::data_ = new Jupiter::IRC::Client::Data(this); + if (Jupiter::IRC::Client::Config == nullptr) + { + Jupiter::IRC::Client::Config = new INIFile(); + Jupiter::IRC::Client::Config->readFile(CONFIG_INI); + } + Jupiter::IRC::Client::data_->configSectionName = configSection; + Jupiter::IRC::Client::data_->serverHostname = Jupiter::IRC::Client::readConfigValue("Hostname", "irc.tibitek.com"); + + Jupiter::IRC::Client::data_->logFileName = Jupiter::IRC::Client::readConfigValue("LogFile"); + Jupiter::IRC::Client::data_->nickname = Jupiter::IRC::Client::readConfigValue("Nick", "Jupiter"); + + Jupiter::IRC::Client::data_->realName = Jupiter::IRC::Client::readConfigValue("Realname", "Jupiter IRC Client"); + + Jupiter::IRC::Client::data_->saslPass = Jupiter::IRC::Client::readConfigValue("SASL.Password"); + if (Jupiter::IRC::Client::data_->saslPass.size() == 0) Jupiter::IRC::Client::data_->saslPass = Jupiter::IRC::Client::readConfigValue("SASL.Pass"); + + Jupiter::IRC::Client::data_->saslAccount = Jupiter::IRC::Client::readConfigValue("SASL.Account"); + if (Jupiter::IRC::Client::data_->saslAccount.size() == 0) Jupiter::IRC::Client::data_->saslAccount = Jupiter::IRC::Client::data_->nickname; + + Jupiter::IRC::Client::data_->autoPartMessage = Jupiter::IRC::Client::readConfigValue("AutoPartMessage"); + + Jupiter::IRC::Client::data_->ssl = Jupiter::IRC::Client::readConfigBool("SSL"); + Jupiter::IRC::Client::data_->SSLCertificate = Jupiter::IRC::Client::readConfigValue("Certificate"); + Jupiter::IRC::Client::data_->SSLKey = Jupiter::IRC::Client::readConfigValue("Key"); + if (Jupiter::IRC::Client::data_->SSLCertificate.size() == 0) + { + Jupiter::IRC::Client::data_->SSLCertificate = Jupiter::IRC::Client::readConfigValue("Cert"); + if (Jupiter::IRC::Client::data_->SSLCertificate.size() == 0) Jupiter::IRC::Client::data_->SSLCertificate = Jupiter::IRC::Client::data_->SSLKey; + } + if (Jupiter::IRC::Client::data_->SSLKey.size() == 0) Jupiter::IRC::Client::data_->SSLKey = Jupiter::IRC::Client::data_->SSLCertificate; + + Jupiter::IRC::Client::data_->joinOnKick = Jupiter::IRC::Client::readConfigBool("AutoJoinOnKick"); + Jupiter::IRC::Client::data_->reconnectDelay = Jupiter::IRC::Client::readConfigInt("AutoReconnectDelay"); + Jupiter::IRC::Client::data_->maxReconnectAttempts = Jupiter::IRC::Client::readConfigInt("MaxReconnectAttempts"); + Jupiter::IRC::Client::data_->serverPort = (unsigned short) Jupiter::IRC::Client::readConfigInt("Port"); + if (Jupiter::IRC::Client::data_->serverPort == 0) + { + if (Jupiter::IRC::Client::data_->ssl) Jupiter::IRC::Client::data_->serverPort = 994; + else Jupiter::IRC::Client::data_->serverPort = 194; + } + Jupiter::IRC::Client::data_->dChanType = (short) Jupiter::IRC::Client::readConfigInt("Channel.Type"); + + if (Jupiter::IRC::Client::readConfigBool("PrintOutput", true)) Jupiter::IRC::Client::data_->printOutput = stdout; + else Jupiter::IRC::Client::data_->printOutput = nullptr; + if (Jupiter::IRC::Client::data_->logFileName.size() != 0) Jupiter::IRC::Client::data_->logFile = fopen(Jupiter::IRC::Client::data_->logFileName.c_str(), "a+b"); + else Jupiter::IRC::Client::data_->logFile = nullptr; + + if (Jupiter::IRC::Client::data_->ssl) + { + Jupiter::SecureTCPSocket *t = new Jupiter::SecureTCPSocket(); + if (Jupiter::IRC::Client::data_->SSLCertificate.size() != 0) + { + t->setCertificate(Jupiter::IRC::Client::data_->SSLCertificate.c_str(), Jupiter::IRC::Client::data_->SSLKey.c_str()); + } + Jupiter::IRC::Client::data_->sock = t; + } + else Jupiter::IRC::Client::data_->sock = new Jupiter::TCPSocket(); + + Jupiter::IRC::Client::data_->connectionStatus = 0; +} + +Jupiter::IRC::Client::~Client() +{ + Jupiter::IRC::Client::data_->sock->closeSocket(); + + Jupiter::IRC::Client::data_->channels.emptyAndDelete(); + Jupiter::IRC::Client::data_->users.emptyAndDelete(); + + if (Jupiter::IRC::Client::data_->sock != nullptr) delete Jupiter::IRC::Client::data_->sock; + if (Jupiter::IRC::Client::data_->logFile != nullptr) fclose(Jupiter::IRC::Client::data_->logFile); + delete Jupiter::IRC::Client::data_; + Jupiter::IRC::Client::data_ = nullptr; +} + +void Jupiter::IRC::Client::OnConnect() +{ + return; +} + +void Jupiter::IRC::Client::OnDisconnect() +{ + return; +} + +void Jupiter::IRC::Client::OnReconnectAttempt(bool) +{ + return; +} + +void Jupiter::IRC::Client::OnRaw(const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnNumeric(long int, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnError(const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnChat(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnNotice(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnServerNotice(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnCTCP(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnAction(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnInvite(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnJoin(const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnPart(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnNick(const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnKick(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnQuit(const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +void Jupiter::IRC::Client::OnMode(const Jupiter::StringType &, const Jupiter::StringType &, const Jupiter::StringType &) +{ + return; +} + +const Jupiter::StringType &Jupiter::IRC::Client::getConfigSection() const +{ + return Jupiter::IRC::Client::data_->configSectionName; +} + +const Jupiter::StringType &Jupiter::IRC::Client::getLogFile() const +{ + return Jupiter::IRC::Client::data_->logFileName; +} + +const Jupiter::StringType &Jupiter::IRC::Client::getPrefixes() const +{ + return Jupiter::IRC::Client::data_->prefixes; +} + +const Jupiter::StringType &Jupiter::IRC::Client::getNickname() const +{ + return Jupiter::IRC::Client::data_->nickname; +} + +const Jupiter::StringType &Jupiter::IRC::Client::getRealname() const +{ + return Jupiter::IRC::Client::data_->realName; +} + +const Jupiter::StringType &Jupiter::IRC::Client::getServerName() const +{ + return Jupiter::IRC::Client::data_->serverName; +} + +const Jupiter::StringType &Jupiter::IRC::Client::getServerHostname() const +{ + return Jupiter::IRC::Client::data_->serverHostname; +} + +unsigned short Jupiter::IRC::Client::getServerPort() const +{ + return Jupiter::IRC::Client::data_->serverPort; +} + +time_t Jupiter::IRC::Client::getReconnectDelay() const +{ + return Jupiter::IRC::Client::data_->reconnectDelay; +} + +time_t Jupiter::IRC::Client::getReconnectTime() const +{ + return Jupiter::IRC::Client::data_->reconnectTime; +} + +int Jupiter::IRC::Client::getReconnectAttempts() const +{ + return Jupiter::IRC::Client::data_->reconnectAttempts; +} + +int Jupiter::IRC::Client::getMaxReconnectAttempts() const +{ + return Jupiter::IRC::Client::data_->maxReconnectAttempts; +} + +short Jupiter::IRC::Client::getDefaultChanType() const +{ + return Jupiter::IRC::Client::data_->dChanType; +} + +FILE *Jupiter::IRC::Client::getPrintOutput() const +{ + return Jupiter::IRC::Client::data_->printOutput; +} + +void Jupiter::IRC::Client::setPrintOutput(FILE *f) +{ + Jupiter::IRC::Client::data_->printOutput = f; +} + +inline Jupiter::CStringS getSender(Jupiter::CStringS &line) +{ + return line.getWord(0, ":! "); +} + +int Jupiter::IRC::Client::getAccessLevel(const char *chan, const char *nick) const +{ + Jupiter::IRC::Client::getChannelIndex(chan); + unsigned int i = 0; + Jupiter::IRC::Client::Channel *channel; + while (i < Jupiter::IRC::Client::data_->channels.size()) + { + channel = Jupiter::IRC::Client::data_->channels.get(i); + if (channel->getName().equalsi(chan)) + { + char prefix = channel->getUserPrefix(nick); + if (prefix == 0) return 0; + return Jupiter::IRC::Client::data_->prefixes.size() - findSymbol(Jupiter::IRC::Client::data_->prefixes.c_str(), prefix, 0); + } + i++; + } + return 0; +} + +void Jupiter::IRC::Client::send(const char *rawMessage) +{ + char *sentMessage = new char[strlen(rawMessage) + 3]; + sprintf(sentMessage, "%s" ENDL, rawMessage); + Jupiter::IRC::Client::data_->sock->send(sentMessage); +} + +Jupiter::IRC::Client::User *Jupiter::IRC::Client::getUser(unsigned int index) const +{ + return Jupiter::IRC::Client::data_->users.get(index); +} + +Jupiter::IRC::Client::User *Jupiter::IRC::Client::getUser(const char *nickname) const +{ + Jupiter::IRC::Client::User *r; + for (unsigned int i = 0; i < Jupiter::IRC::Client::data_->users.size(); i++) + { + r = Jupiter::IRC::Client::data_->users.get(i); + if (r->getNickname().equalsi(nickname)) return r; + } + return nullptr; +} + +int Jupiter::IRC::Client::getUserIndex(const char *nickname) const +{ + for (int i = Jupiter::IRC::Client::data_->users.size() - 1; i >= 0; i--) if (Jupiter::IRC::Client::data_->users.get(i)->getNickname().equalsi(nickname)) return i; + return -1; +} + +int Jupiter::IRC::Client::getUserIndex(Jupiter::IRC::Client::User *user) const +{ + for (int i = Jupiter::IRC::Client::data_->users.size() - 1; i >= 0; i--) if (Jupiter::IRC::Client::data_->users.get(i) == user) return i; + return -1; +} + +unsigned int Jupiter::IRC::Client::getUserCount() const +{ + return Jupiter::IRC::Client::data_->users.size(); +} + +unsigned int Jupiter::IRC::Client::getChannelCount() const +{ + return Jupiter::IRC::Client::data_->channels.size(); +} + +Jupiter::IRC::Client::Channel *Jupiter::IRC::Client::getChannel(unsigned int index) const +{ + return Jupiter::IRC::Client::data_->channels.get(index); +} + +Jupiter::IRC::Client::Channel *Jupiter::IRC::Client::getChannel(const char *chanName) const +{ + unsigned int index = Jupiter::IRC::Client::getChannelIndex(chanName); + if (index < 0) return nullptr; + return Jupiter::IRC::Client::getChannel(index); +} + +const Jupiter::StringType &Jupiter::IRC::Client::getChannelName(unsigned int index) const +{ + return Jupiter::IRC::Client::data_->channels.get(index)->getName(); +} + +int Jupiter::IRC::Client::getChannelIndex(const char *chanName) const +{ + for (unsigned int i = 0; i < Jupiter::IRC::Client::data_->channels.size(); i++) if (Jupiter::IRC::Client::data_->channels.get(i)->getName().equalsi(chanName)) return i; + return -1; +} + +bool Jupiter::IRC::Client::isAutoReconnect() const +{ + return Jupiter::IRC::Client::data_->maxReconnectAttempts != 0; +} + +void Jupiter::IRC::Client::setAutoReconnect(int val) +{ + Jupiter::IRC::Client::data_->maxReconnectAttempts = val; +} + +void Jupiter::IRC::Client::joinChannel(const char *channel) +{ + char *msg = new char[strlen(channel) + 8]; + sprintf(msg, "JOIN %s" ENDL, channel); + Jupiter::IRC::Client::data_->sock->send(msg); + delete[] msg; +} + +void Jupiter::IRC::Client::joinChannel(const char *channel, const char *password) +{ + char *msg = new char[strlen(channel) + strlen(password) + 9]; + sprintf(msg, "JOIN %s %s" ENDL, channel, password); + Jupiter::IRC::Client::data_->sock->send(msg); + delete[] msg; +} + +void Jupiter::IRC::Client::partChannel(const char *channel) +{ + char *msg = new char[strlen(channel) + 8]; + sprintf(msg, "PART %s" ENDL, channel); + Jupiter::IRC::Client::data_->sock->send(msg); + delete[] msg; + int index = Jupiter::IRC::Client::getChannelIndex(channel); + if (index >= 0) Jupiter::IRC::Client::data_->channels.get(index)->setType(-2); +} + +void Jupiter::IRC::Client::partChannel(const char *channel, const char *message) +{ + char *msg = new char[strlen(channel) + strlen(message) + 10]; + sprintf(msg, "PART %s :%s" ENDL, channel, message); + Jupiter::IRC::Client::data_->sock->send(msg); + delete[] msg; + int index = Jupiter::IRC::Client::getChannelIndex(channel); + if (index >= 0) Jupiter::IRC::Client::data_->channels.get(index)->setType(-2); +} + +void Jupiter::IRC::Client::sendMessage(const char *dest, const char *message) +{ + char *msg = new char[strlen(dest) + strlen(message) + 13]; + sprintf(msg, "PRIVMSG %s :%s" ENDL, dest, message); + Jupiter::IRC::Client::data_->sock->send(msg); + delete[] msg; +} + +void Jupiter::IRC::Client::sendNotice(const char *dest, const char *message) +{ + char *msg = new char[strlen(dest) + strlen(message) + 12]; + sprintf(msg, "NOTICE %s :%s" ENDL, dest, message); + Jupiter::IRC::Client::data_->sock->send(msg); + delete[] msg; +} + +unsigned int Jupiter::IRC::Client::messageChannels(short type, const char *message) +{ + unsigned int total = 0; + Jupiter::IRC::Client::Channel *channel; + for (unsigned int i = 0; i < Jupiter::IRC::Client::data_->channels.size(); i++) + { + channel = Jupiter::IRC::Client::data_->channels.get(i); + if (channel->getType() == type) + { + Jupiter::IRC::Client::sendMessage(channel->getName().c_str(), message); + total++; + } + } + return total; +} + +unsigned int Jupiter::IRC::Client::messageChannels(const char *message) +{ + unsigned int total = 0; + Jupiter::IRC::Client::Channel *channel; + for (unsigned int i = 0; i < Jupiter::IRC::Client::data_->channels.size(); i++) + { + channel = Jupiter::IRC::Client::data_->channels.get(i); + if (channel->getType() >= 0) + { + Jupiter::IRC::Client::sendMessage(channel->getName().c_str(), message); + total++; + } + } + return total; +} + +int Jupiter::IRC::Client::primaryHandler() +{ + if (Jupiter::IRC::Client::data_->connectionStatus == 0) return -1; + int recvVal = Jupiter::IRC::Client::data_->sock->recv(); + if (recvVal > 0) + { + Jupiter::IRC::Client::data_->buffer = Jupiter::IRC::Client::data_->sock->getBuffer(); + unsigned int totalLines = countLines(Jupiter::IRC::Client::data_->buffer.c_str()); + for (unsigned int currentLine = 0; currentLine < totalLines; currentLine++) + { + Jupiter::CStringS buff = Jupiter::IRC::Client::data_->buffer.getWord(currentLine, ENDL); + if (buff.size() > 0) + { + Jupiter::IRC::Client::writeToLogs(buff.c_str()); + if (Jupiter::IRC::Client::data_->printOutput != nullptr) + { + fputs(buff.c_str(), Jupiter::IRC::Client::data_->printOutput); + fputs(ENDL, Jupiter::IRC::Client::data_->printOutput); + } + Jupiter::CStringS w1 = buff.getWord(0, WHITESPACE); + if (w1.size() > 0) + { + Jupiter::CStringS w2 = buff.getWord(1, WHITESPACE); + long int numeric = strtol(w2.c_str(), nullptr, 10); + if (w1[0] == ':') //Messages + { + if (w2.size() > 0) + { + switch (numeric) // Numerics that don't rely on a specific connectionStatus. + { + case IRC_RPL_BOUNCE: // 010 + { + Jupiter::CStringS portToken = buff.getWord(4, " "); + unsigned short port; + if (portToken[0] == '+') // This is most likely not used anywhere. + { + port = (unsigned short)strtoul(portToken.c_str() + 1, nullptr, 10); + if (Jupiter::IRC::Client::data_->ssl == false) + { + Jupiter::IRC::Client::data_->ssl = true; + delete Jupiter::IRC::Client::data_->sock; + Jupiter::IRC::Client::data_->sock = new Jupiter::SecureTCPSocket(); + } + } + else + { + port = (unsigned short)strtoul(portToken.c_str(), nullptr, 10); + if (Jupiter::IRC::Client::data_->ssl == true) + { + Jupiter::IRC::Client::data_->ssl = false; + delete Jupiter::IRC::Client::data_->sock; + Jupiter::IRC::Client::data_->sock = new Jupiter::TCPSocket(); + } + } + if (port != 0) // Don't default -- could be non-compliant input. + { + Jupiter::IRC::Client::data_->serverHostname = buff.getWord(3, WHITESPACE); + Jupiter::IRC::Client::data_->serverPort = port; + puts("Reconnecting due to old bounce."); + this->reconnect(); + } + else puts("Error: Failed to parse bounce token."); + } + break; + } // numeric switch + if (Jupiter::IRC::Client::data_->connectionStatus == 1) // Socket established -- attempting STARTTLS + { + switch (numeric) + { + case IRC_RPL_BOUNCEOLD: // 005 + if (buff.matchi("*:Try server *, port *")) + { + Jupiter::CStringS portToken = buff.getWord(6, " "); + unsigned short bouncePort = (unsigned short) atoi(buff.getWord(6, " ").c_str()); + + if (portToken[0] == '+') // This is almost certainly not used anywhere. + { + bouncePort = (unsigned short)strtoul(portToken.c_str() + 1, nullptr, 10); + if (Jupiter::IRC::Client::data_->ssl == false) + { + Jupiter::IRC::Client::data_->ssl = true; + delete Jupiter::IRC::Client::data_->sock; + Jupiter::IRC::Client::data_->sock = new Jupiter::SecureTCPSocket(); + } + } + else + { + bouncePort = (unsigned short)strtoul(portToken.c_str(), nullptr, 10); + if (Jupiter::IRC::Client::data_->ssl == true) + { + Jupiter::IRC::Client::data_->ssl = false; + delete Jupiter::IRC::Client::data_->sock; + Jupiter::IRC::Client::data_->sock = new Jupiter::TCPSocket(); + } + } + if (bouncePort != 0) + { + Jupiter::IRC::Client::data_->serverHostname = buff.getWord(4, " "); + Jupiter::IRC::Client::data_->serverHostname.truncate(1); // trailing comma + Jupiter::IRC::Client::data_->serverPort = bouncePort; + puts("Reconnecting due to old bounce."); + this->reconnect(); + } + else puts("Error: Failed to parse old bounce token."); + } + break; + case IRC_ERR_UNKNOWNCOMMAND: // 421 + { + Jupiter::CStringS command = buff.getWord(2, " "); + if (command.equalsi("STARTTLS")) // Server doesn't support STARTTLS + { + Jupiter::IRC::Client::data_->startCAP(); + } + } + break; + case IRC_RPL_STARTTLS: // 670 + { + Jupiter::SecureTCPSocket *t = new Jupiter::SecureTCPSocket(std::move(*Jupiter::IRC::Client::data_->sock)); + delete Jupiter::IRC::Client::data_->sock; + Jupiter::IRC::Client::data_->sock = t; + Jupiter::IRC::Client::data_->ssl = true; + // toggle blocking to prevent error + if (Jupiter::IRC::Client::data_->SSLCertificate.size() != 0) + { + t->setCertificate(Jupiter::IRC::Client::data_->SSLCertificate.c_str(), Jupiter::IRC::Client::data_->SSLKey.c_str()); + } + + bool goodSSL; + if (t->getBlockingMode() == false) + { + t->setBlocking(true); + goodSSL = t->initSSL(); + t->setBlocking(false); + } + else goodSSL = t->initSSL(); + + if (goodSSL) Jupiter::IRC::Client::data_->startCAP(); + else + { + // Something went wrong. Kill the socket. + t->closeSocket(); + } + } + break; + case IRC_ERR_STARTTLS: // 691 + Jupiter::IRC::Client::data_->startCAP(); + break; + default: + break; + } // numeric switch + } + else if (Jupiter::IRC::Client::data_->connectionStatus == 2) // Capability negotiation + { + switch (numeric) + { + case 0: + if (w2.equalsi("CAP")) + { + Jupiter::CStringS w4 = buff.getWord(3, WHITESPACE); + if (w4.equals("LS")) + { + Jupiter::CStringS listParams = buff.gotoWord(4, WHITESPACE); + if (listParams[0] == ':') listParams.shiftRight(1); + unsigned int len = listParams.wordCount(WHITESPACE); + Jupiter::CStringS curr; + Jupiter::CStringL req = "CAP REQ :"; + bool sasl = false; + for (unsigned int i = 0; i < len; i++) + { + curr = listParams.getWord(i, WHITESPACE); + if (curr.equalsi("multi-prefix")) req += "multi-prefix "; + else if (curr.equalsi("userhost-in-names")) req += "userhost-in-names "; + else if (curr.equalsi("sasl")) + { + if (Jupiter::IRC::Client::data_->saslPass.size() != 0) + { + req += "sasl "; + sasl = true; + } + } + // else; // We don't know what this is! + } + if (req.size() > 9) + { + req -= 1; // Trim off the extra space byte. + req += ENDL; + Jupiter::IRC::Client::data_->sock->send(req.c_str()); + if (sasl) + { + Jupiter::IRC::Client::data_->sock->send("AUTHENTICATE PLAIN" ENDL); + } + } + if (!sasl) + { + Jupiter::IRC::Client::data_->sock->send("CAP END" ENDL); + Jupiter::IRC::Client::data_->registerClient(); + } + } + } + break; + case IRC_ERR_UNKNOWNCOMMAND: // 421 + if (w2.equalsi("CAP")) // Server doesn't support CAP + { + Jupiter::IRC::Client::data_->registerClient(); + } + break; + default: + break; + } // numeric switch + } + else if (Jupiter::IRC::Client::data_->connectionStatus == 3) // Registration sent, but not verified. + { + bool completelyBadNick = false; + switch (numeric) + { + // We'll take any of these 4, just in-case any of them are missing. In general, this will trigger on 001. + case IRC_RPL_MYINFO: // 004 + Jupiter::IRC::Client::data_->serverName = buff.getWord(3, " "); + case IRC_RPL_WELCOME: // 001 + case IRC_RPL_YOURHOST: // 002 + case IRC_RPL_CREATED: // 003 + Jupiter::IRC::Client::data_->connectionStatus = 4; + break; + + // You have a bad nickname! Try the alt. + //case IRC_ERR_NONICKNAMEGIVEN: // 431 -- Not consistently usable due to lack of command field. + case IRC_ERR_ERRONEOUSNICKNAME: // 432 + completelyBadNick = true; + case IRC_ERR_NICKNAMEINUSE: // 433 + case IRC_ERR_NICKCOLLISION: // 436 + case IRC_ERR_BANNICKCHANGE: // 437 -- Note: This conflicts with another token. + Jupiter::CStringS altNick = Jupiter::IRC::Client::readConfigValue("AltNick", "Jupiter"); + Jupiter::CStringS configNick = Jupiter::IRC::Client::readConfigValue("Nick", "Jupiter"); + /* + * Possible Issues: + * altNick != nickname after first nick change -- loop initiated? + * completelyBadNick will apply to altNick if it was tried, not the actual nick. + */ + if (Jupiter::IRC::Client::data_->nickname.equalsi(altNick)) // The alternate nick failed. + { + Jupiter::IRC::Client::data_->nickname = configNick; + Jupiter::IRC::Client::data_->nickname += "1"; + } + else if (Jupiter::IRC::Client::data_->nickname.equalsi(configNick)) + { + Jupiter::IRC::Client::data_->nickname = altNick; + if (Jupiter::IRC::Client::data_->nickname.size() != 0) + { + char *messageToSend = new char[Jupiter::IRC::Client::data_->nickname.size() + 8]; + sprintf(messageToSend, "NICK %s" ENDL, Jupiter::IRC::Client::data_->nickname.c_str()); + Jupiter::IRC::Client::data_->sock->send(messageToSend); + delete[] messageToSend; + } + } + // Note: Add a series of contains() functions to String_Type. + else //if (stristr(Jupiter::IRC::Client::data_->nickname.c_str(), configNick.c_str()) == Jupiter::IRC::Client::data_->nickname.c_str()) // We're already adding numbers. + { + if (completelyBadNick == false) // If this nick is invalid, adding numbers won't help. + { + if (Jupiter::IRC::Client::data_->nickname.size() > configNick.size()) + { + int n = atoi(Jupiter::IRC::Client::data_->nickname.c_str() + configNick.size()) + 1; + Jupiter::IRC::Client::data_->nickname.format("%s%d", configNick.c_str(), n); + } + else + { + // Something strange is going on -- did somebody rehash? + // This can be somewhat edgy -- this will only trigger if someone rehashes AND the new nickname is shorter. + // However, it won't be fatal even if the new nickname's length is >= the old. + Jupiter::IRC::Client::data_->nickname = configNick; + } + } + else + { + // Disconnect and don't try again. + // Consider passing this to plugins so that they can figure it out (i.e: a plugin could display a prompt and ask for input). + } + } + break; + } + } + else if (Jupiter::IRC::Client::data_->connectionStatus == 4) // Registration verified, but connection process in progress. + { + switch (numeric) + { + case IRC_RPL_ISUPPORT: // 005 + { + const char *ptr = strstr(buff.c_str(), "PREFIX=("); + if (ptr != nullptr) + { + ptr += 8; + int poff = findSymbol(ptr, ')', 0); + Jupiter::IRC::Client::data_->prefixModes = Jupiter::CStringS::getWord(ptr, 0, ")"); + ptr += poff + 1; + Jupiter::IRC::Client::data_->prefixes = Jupiter::CStringS::getWord(ptr, 0, " " ENDL); + ptr = nullptr; + } + ptr = strstr(buff.c_str(), "CHANMODES="); + if (ptr != nullptr) + { + ptr += 10; + int pos = findSymbol(ptr, ',', 0); + if (pos >= 0) + { + Jupiter::IRC::Client::data_->modeA = Jupiter::CStringS::getWord(ptr, 0, ", "); + ptr += pos + 1; + pos = findSymbol(ptr, ',', 0); + if (pos >= 0) + { + Jupiter::IRC::Client::data_->modeB = Jupiter::CStringS::getWord(ptr, 0, ", "); + ptr += pos + 1; + pos = findSymbol(ptr, ',', 0); + if (pos >= 0) + { + Jupiter::IRC::Client::data_->modeC = Jupiter::CStringS::getWord(ptr, 0, ", "); + ptr += pos + 1; + pos = strcspn(ptr, " "); + if (pos >= 0) Jupiter::IRC::Client::data_->modeD = Jupiter::CStringS::getWord(ptr, 0, ", "); + } + } + } + } + ptr = strstr(buff.c_str(), "CHANTYPES="); + if (ptr != nullptr) + { + ptr += 10; + Jupiter::IRC::Client::data_->chanTypes = Jupiter::CStringS::getWord(ptr, 0, " "); + } + } + break; + case IRC_RPL_LUSERCLIENT: // 251 + { + Jupiter::CStringL key = "RawData."; + unsigned int offset; + + Jupiter::CStringS tVal; + unsigned int i = 1; + do + { + offset = key.aformat("%u", i); + tVal = Jupiter::IRC::Client::readConfigValue(key.c_str()); + if (tVal.size() == 0) break; + key -= offset; + Jupiter::IRC::Client::send(tVal.c_str()); + i++; + } while (1); + key = "Channel."; + i = 1; + do + { + offset = key.aformat("%u", i); + tVal = Jupiter::IRC::Client::readConfigValue(key.c_str()); + if (tVal.size() == 0) break; + key -= offset; + Jupiter::IRC::Client::joinChannel(tVal.c_str()); + i++; + } while (1); + + Jupiter::IRC::Client::data_->connectionStatus = 5; + Jupiter::IRC::Client::data_->reconnectAttempts = 0; + this->OnConnect(); + for (i = 0; i < Jupiter::plugins->size(); i++) Jupiter::plugins->get(i)->OnConnect(this); + } + break; + } + } + else // Post-registration. + { + if (streqli(w2.c_str(), "PRIVMSG")) + { + Jupiter::CStringS chan = buff.getWord(2, WHITESPACE); + if (chan.size() > 0) + { + Jupiter::CStringS nick = getSender(buff); + if (nick.size() > 0) + { + const char *premessage = buff.c_str() + findSymbol(buff.c_str(), ':', 1) + 1; + if (premessage[0] == '\001') //CTCP (ACTIONs are included) + { + Jupiter::CStringS rawmessage = premessage + 1; + Jupiter::CStringS command = rawmessage.getWord(0, WHITESPACE); + if (command[command.size() - 1] == IRC::CTCP) command.truncate(1); + Jupiter::CStringS message = rawmessage.substring(findSymbol(rawmessage.c_str(), ' ', 0) + 1, findSymbol(rawmessage.c_str(), IRC::CTCP, 0)); + if (message[message.size() - 1] == IRC::CTCP) message.truncate(1); + + if (command.equals("ACTION")) + { + this->OnAction(chan, nick, message); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnAction(this, chan, nick, message); + } + else + { + Jupiter::CStringL response = "NOTICE "; + response += nick; + response += " :" IRCCTCP; + response += command; + response += ' '; + if (command.equals("PING")) response += message; + else if (command.equals("VERSION")) response += Jupiter::version; + else if (command.equals("FINGER")) response += "Oh, yeah, a little to the left."; + else if (command.equals("SOURCE")) response += "irc.tibitek.com"; + else if (command.equals("USERINFO")) response += "Hey, I'm Jupiter! If you have questions, ask Justin! (irc.tibitek.com)"; + else if (command.equals("CLIENTINFO")) response += "I'll tell you what I don't know: This command!"; + else if (command.equals("TIME")) response += getTime(); + else if (command.equals("ERRMSG")) response += message; + else + { + response = "NOTICE "; + response += nick; + response += " :" IRCCTCP "ERRMSG "; + response += command; + response += " :Query is unknown"; + } + response += IRCCTCP ENDL; + Jupiter::IRC::Client::data_->sock->send(response.c_str()); + this->OnCTCP(chan, nick, command, message); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnCTCP(this, chan, nick, message); + } + } + else + { + Jupiter::CStringS message = premessage; + this->OnChat(chan, nick, message); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnChat(this, chan, nick, message); + } + } + } + } + else if (streqli(w2.c_str(), "NOTICE")) + { + CStringS chan = buff.getWord(2, WHITESPACE); + if (chan.size() > 0) + { + int pos = findSymbol(buff.c_str(), '!', 0); + auto message = buff.substring(findSymbol(buff.c_str(), ':', 1) + 1, buff.size()); + if (pos >= 0 && strcspn(buff.c_str(), "!") < strcspn(buff.c_str(), " ")) + { + auto nick = buff.substring(1, pos); + this->OnNotice(chan, nick, message); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnNotice(this, chan, nick, message); + } + else + { + auto sender = getSender(buff); + if (sender.size() != 0) + { + this->OnServerNotice(chan, sender, message); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnServerNotice(this, chan, sender, message); + } + } + } + } + else if (streqli(w2.c_str(), "NICK")) + { + auto nick = getSender(buff); + const char *tnewnick = buff.c_str() + findSymbol(buff.c_str(), ' ', 1) + 1; + if (*tnewnick == ':') tnewnick++; + Jupiter::CStringS newnick = tnewnick; + if (nick.equalsi(Jupiter::IRC::Client::data_->nickname)) + { + Jupiter::IRC::Client::data_->nickname = newnick; + } + Jupiter::IRC::Client::User *user = Jupiter::IRC::Client::data_->findUser(nick.c_str()); + if (user != nullptr) + { + user->data_->nickname = newnick; + this->OnNick(nick, newnick); + } + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnNick(this, nick, newnick); + } + else if (streqli(w2.c_str(), "JOIN")) + { + auto nick = getSender(buff); + CStringS chan = buff.getWord(2, WHITESPACE); + if (chan[0] == ':') chan.shiftRight(1); + int i = Jupiter::IRC::Client::getChannelIndex(chan.c_str()); + if (Jupiter::IRC::Client::data_->nickname.equalsi(nick)) + { + if (i >= 0) Jupiter::IRC::Client::data_->delChannel(i); + Jupiter::IRC::Client::Channel *channel = Jupiter::IRC::Client::data_->channels.get(Jupiter::IRC::Client::data_->addChannel(chan.c_str())); + channel->data_->isAddingNames = true; + if (channel->getType() < 0) + { + if (Jupiter::IRC::Client::data_->autoPartMessage.size() != 0) Jupiter::IRC::Client::partChannel(chan.c_str(), Jupiter::IRC::Client::data_->autoPartMessage.c_str()); + else Jupiter::IRC::Client::partChannel(chan.c_str()); + } + } + else if (i >= 0) Jupiter::IRC::Client::data_->channels.get(i)->addUser(Jupiter::IRC::Client::data_->findUserOrAdd(nick)); + this->OnJoin(chan, nick); + for (i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnJoin(this, chan, nick); + } + else if (streqli(w2.c_str(), "PART")) + { + auto nick = getSender(buff); + if (nick.size() != 0) + { + auto chan = buff.getWord(2, WHITESPACE); + if (chan.size() != 0) + { + int i = Jupiter::IRC::Client::getChannelIndex(chan.c_str()); + if (i >= 0) + { + int userIndex = Jupiter::IRC::Client::getUserIndex(nick.c_str()); + if (userIndex >= 0) + { + Jupiter::IRC::Client::User *user = Jupiter::IRC::Client::data_->users.get(userIndex); + Jupiter::IRC::Client::data_->channels.get(i)->delUser(nick.c_str()); + int pos = findSymbol(buff.c_str(), ':', 1); + Jupiter::CStringS reason = buff.c_str() + pos + 1; + this->OnPart(chan, nick, reason); + for (i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnPart(this, chan, nick, reason); + if (nick.equalsi(Jupiter::IRC::Client::data_->nickname)) Jupiter::IRC::Client::data_->delChannel(chan.c_str()); + if (user->getChannelCount() == 0) Jupiter::IRC::Client::data_->users.remove(userIndex); + } + } + } + } + } + else if (streqli(w2.c_str(), "KICK")) + { + auto chan = buff.getWord(2, WHITESPACE); + if (chan.size() != 0) + { + Jupiter::CStringS kicker = getSender(buff); + if (kicker.size() > 0) + { + auto kicked = buff.getWord(3, WHITESPACE); + if (kicked.size() != 0) + { + int i = Jupiter::IRC::Client::getChannelIndex(chan.c_str()); + if (i >= 0) + { + int userIndex = Jupiter::IRC::Client::getUserIndex(kicked.c_str()); + if (userIndex >= 0) + { + Jupiter::IRC::Client::User *user = Jupiter::IRC::Client::data_->users.get(userIndex); + Jupiter::IRC::Client::data_->channels.get(i)->delUser(kicked.c_str()); + int pos = findSymbol(buff.c_str(), ':', 1); + Jupiter::CStringS reason; + if (pos >= 0) reason += buff.c_str() + pos + 1; + this->OnKick(chan, kicker, kicked, reason); + for (i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnKick(this, chan, kicker, kicked, reason); + if (kicked.equalsi(Jupiter::IRC::Client::data_->nickname)) + { + Jupiter::IRC::Client::data_->delChannel(chan.c_str()); + if (Jupiter::IRC::Client::data_->joinOnKick) Jupiter::IRC::Client::joinChannel(chan.c_str()); + } + if (user->getChannelCount() == 0) Jupiter::IRC::Client::data_->users.remove(userIndex); + } + } + } + } + } + } + else if (streqli(w2.c_str(), "QUIT")) + { + Jupiter::CStringS nick = getSender(buff); + Jupiter::CStringS message = buff.substring(findSymbol(buff.c_str(), ':', 1) + 1, buff.size()); + int userIndex = Jupiter::IRC::Client::getUserIndex(nick.c_str()); + if (userIndex >= 0) + { + Jupiter::IRC::Client::User *user = Jupiter::IRC::Client::data_->users.get(userIndex); + unsigned int i; + for (i = 0; i < Jupiter::IRC::Client::data_->channels.size(); i++) Jupiter::IRC::Client::data_->channels.get(i)->delUser(nick.c_str()); + this->OnQuit(nick, message); + for (i = 0; i < Jupiter::plugins->size(); i++) Jupiter::plugins->get(i)->OnQuit(this, nick, message); + if (user->getChannelCount() == 0) Jupiter::IRC::Client::data_->users.remove(userIndex); + } + } + else if (streqli(w2.c_str(), "INVITE")) + { + Jupiter::CStringS inviter = getSender(buff); + auto invited = buff.getWord(2, WHITESPACE); + Jupiter::CStringS chan = buff.c_str() + findSymbol(buff.c_str(), ':', 1) + 1; + this->OnInvite(chan, inviter, invited); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnInvite(this, chan, inviter, invited); + } + else if (streqli(w2.c_str(), "MODE")) + { + auto chan = buff.getWord(2, WHITESPACE); + if (chan.size() > 0) + { + if (containsSymbol(Jupiter::IRC::Client::data_->chanTypes.c_str(), chan[0])) + { + auto nick = getSender(buff); + if (nick.size() != 0) + { + const char *modestring = buff.c_str() + findSymbol(buff.c_str(), ' ', 2) + 1; + if (wordCount(modestring) > 1) + { + char *modes = getWord(modestring, 0); + if (modes != nullptr) + { + const char *params = modestring + findSymbol(modestring, ' ', 0) + 1; + char *tword; + unsigned char g = 0; + char symb = 0; + for (uint8_t z = 0; modes[z] != 0; z++) + { + if (modes[z] == '+' || modes[z] == '-') symb = modes[z]; + else if (findSymbol(Jupiter::IRC::Client::data_->prefixModes.c_str(), modes[z], 0) >= 0) + { + tword = getWord(params, g); + if (tword != nullptr) + { + Jupiter::IRC::Client::Channel *channel; + for (unsigned int channelIndex = 0; channelIndex < Jupiter::IRC::Client::data_->channels.size(); channelIndex++) + { + channel = Jupiter::IRC::Client::data_->channels.get(channelIndex); + if (channel->getName().equalsi(chan.c_str())) + { + if (symb == '+') channel->addUserPrefix(tword, Jupiter::IRC::Client::data_->prefixes[findSymbol(Jupiter::IRC::Client::data_->prefixModes.c_str(), modes[z], 0)]); + else channel->delUserPrefix(tword, Jupiter::IRC::Client::data_->prefixes[findSymbol(Jupiter::IRC::Client::data_->prefixModes.c_str(), modes[z], 0)]); + break; + } + } + free(tword); + } + g++; + } + else + { + if (containsSymbol(Jupiter::IRC::Client::data_->modeA.c_str(), modes[z])) g++; + else if (containsSymbol(Jupiter::IRC::Client::data_->modeB.c_str(), modes[z])) g++; + else if (containsSymbol(Jupiter::IRC::Client::data_->modeC.c_str(), modes[z]) && symb == '+') g++; + } + } + free(modes); + } + } + Jupiter::CStringS modeString = modestring; + this->OnMode(chan, nick, modeString); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnMode(this, chan, nick, modeString); + } + } + } + } + // else if ACCOUNT + // else if CHGHOST + else if (numeric == IRC_RPL_NAMREPLY) // Some names. + { + Jupiter::CStringS chan = buff.getWord(4, " "); + Jupiter::CStringS names = buff.c_str() + findSymbol(buff.c_str(), ':', 1) + 1; + int i = Jupiter::IRC::Client::getChannelIndex(chan.c_str()); + if (i >= 0) + { + if (Jupiter::IRC::Client::getChannel(i)->data_->isAddingNames == false) + { + Jupiter::IRC::Client::data_->delChannel(i); + Jupiter::IRC::Client::getChannel(Jupiter::IRC::Client::data_->addChannel(chan.c_str()))->data_->isAddingNames = true; + } + Jupiter::IRC::Client::data_->addNamesToChannel(i, names); + } + } + else if (numeric == IRC_RPL_ENDOFNAMES) // We're done here. + { + Jupiter::CStringS chan = buff.getWord(3, " "); + int i = Jupiter::IRC::Client::getChannelIndex(chan.c_str()); + if (i >= 0) Jupiter::IRC::Client::data_->channels.get(i)->data_->isAddingNames = false; + } + } + } + } + else + { + if (w1.equals("PING")) + { + char *tempstr; + tempstr = new char[w2.size() + 8]; + sprintf(tempstr, "PONG %s" ENDL, w2.c_str()); + Jupiter::IRC::Client::data_->sock->send(tempstr); + delete[] tempstr; + } + else if (w1.equals("NICK")) + { + if (w2.size() > 0) Jupiter::IRC::Client::data_->nickname = w2; + } + else if (w1.equals("ERROR")) + { + Jupiter::CStringS reason = buff.c_str() + strcspn(buff.c_str(), ":") + 1; + this->OnError(reason); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnError(this, reason); + Jupiter::IRC::Client::disconnect(); + } + else if (w1.equals("AUTHENTICATE")) + { + if (Jupiter::IRC::Client::data_->saslPass.size() != 0) + { + size_t authStringLen = Jupiter::IRC::Client::data_->nickname.size() + Jupiter::IRC::Client::data_->saslAccount.size() + Jupiter::IRC::Client::data_->saslPass.size() + 2; + char *authString = new char[authStringLen + 1]; + int offset = sprintf(authString, "%s", Jupiter::IRC::Client::data_->nickname.c_str()) + 1; + offset += sprintf(authString + offset, "%s", Jupiter::IRC::Client::data_->saslAccount.c_str()) + 1; + offset += sprintf(authString + offset, "%s", Jupiter::IRC::Client::data_->saslPass.c_str()); + + char *enc = Jupiter::base64encode(authString, authStringLen); + delete[] authString; + + char *out = new char[strlen(enc) + 16]; + int len = sprintf(out, "AUTHENTICATE %s" ENDL, enc); + delete[] enc; + Jupiter::IRC::Client::data_->sock->send(out, len); + delete[] out; + } + Jupiter::IRC::Client::data_->sock->send("CAP END" ENDL); + Jupiter::IRC::Client::data_->registerClient(); + } + } + if (numeric != 0) + { + this->OnNumeric(numeric, buff); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnNumeric(this, numeric, buff); + } + } + this->OnRaw(buff); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnRaw(this, buff); + } + } + return 0; + } + int lastError = Jupiter::IRC::Client::data_->sock->getLastError(); + if (lastError == 10035) return 0; + printf(ENDL ENDL "recv Value: %d -- Conneciton Error: %d -- Connection Status: %d" ENDL ENDL ENDL, recvVal, lastError, Jupiter::IRC::Client::data_->connectionStatus); + if (Jupiter::IRC::Client::data_->connectionStatus) Jupiter::IRC::Client::disconnect(); + return lastError; +} + +bool Jupiter::IRC::Client::connect() +{ + const char *clientAddress = Jupiter::IRC::Client::readConfigValue("ClientAddress").c_str(); + if (*clientAddress == 0) clientAddress = nullptr; + bool r = Jupiter::IRC::Client::data_->sock->connectToHost(Jupiter::IRC::Client::data_->serverHostname.c_str(), Jupiter::IRC::Client::data_->serverPort, clientAddress, (unsigned short) Jupiter::IRC::Client::readConfigLong("ClientPort")); + if (r) + { + Jupiter::IRC::Client::data_->sock->setBlocking(false); + if (Jupiter::IRC::Client::data_->ssl == false) + { + Jupiter::IRC::Client::data_->sock->send("STARTTLS" ENDL); + Jupiter::IRC::Client::data_->connectionStatus = 1; + } + else Jupiter::IRC::Client::data_->startCAP(); + } + return r; +} + +void Jupiter::IRC::Client::disconnect(bool stayDead) +{ + Jupiter::IRC::Client::data_->channels.emptyAndDelete(); + Jupiter::IRC::Client::data_->connectionStatus = 0; + Jupiter::IRC::Client::data_->sock->closeSocket(); + Jupiter::IRC::Client::data_->reconnectTime = time(0) + Jupiter::IRC::Client::data_->reconnectDelay; + Jupiter::IRC::Client::data_->dead = stayDead; + this->OnDisconnect(); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnDisconnect(this); +} + +void Jupiter::IRC::Client::disconnect(const char *message, bool stayDead) +{ + char *msg = new char[strlen(message) + 9]; + sprintf(msg, "QUIT :%s" ENDL, message); + Jupiter::IRC::Client::data_->sock->send(msg); + delete[] msg; + Jupiter::IRC::Client::disconnect(stayDead); +} + +void Jupiter::IRC::Client::reconnect() +{ + if (Jupiter::IRC::Client::data_->connectionStatus) Jupiter::IRC::Client::disconnect(); + Jupiter::IRC::Client::data_->reconnectAttempts++; + bool successConnect = Jupiter::IRC::Client::connect(); + this->OnReconnectAttempt(successConnect); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnReconnectAttempt(this, successConnect); +} + +int Jupiter::IRC::Client::think() +{ + int r = Jupiter::IRC::Client::primaryHandler(); + for (int i = Jupiter::plugins->size() - 1; i >= 0; i--) Jupiter::plugins->get(i)->OnThink(this); + if (r) + { + if (Jupiter::IRC::Client::data_->dead == true) return r; + if (Jupiter::IRC::Client::data_->maxReconnectAttempts < 0 || Jupiter::IRC::Client::data_->reconnectAttempts < Jupiter::IRC::Client::data_->maxReconnectAttempts) + { + if (!Jupiter::IRC::Client::data_->reconnectDelay || Jupiter::IRC::Client::data_->reconnectTime < time(0)) Jupiter::IRC::Client::reconnect(); + } + else return r; + } + return 0; +} + +Jupiter::CStringS Jupiter::IRC::Client::readConfigValue(const char *key, const char *defaultValue) const +{ + const char *val = Jupiter::IRC::Client::Config->get(Jupiter::IRC::Client::data_->configSectionName.c_str(), key); + if (val == nullptr) val = Jupiter::IRC::Client::Config->get("Default", key, defaultValue); + return val; +} + +bool Jupiter::IRC::Client::readConfigBool(const char *key, bool defaultValue) const +{ + if (Jupiter::IRC::Client::Config->get(Jupiter::IRC::Client::data_->configSectionName.c_str(), key)) return Jupiter::IRC::Client::Config->getBool(Jupiter::IRC::Client::data_->configSectionName.c_str(), key); + return Jupiter::IRC::Client::Config->getBool("Default", key, defaultValue); +} + +int Jupiter::IRC::Client::readConfigInt(const char *key, int defaultValue) const +{ + if (Jupiter::IRC::Client::Config->get(Jupiter::IRC::Client::data_->configSectionName.c_str(), key)) return Jupiter::IRC::Client::Config->getInt(Jupiter::IRC::Client::data_->configSectionName.c_str(), key); + return Jupiter::IRC::Client::Config->getInt("Default", key, defaultValue); +} + +long Jupiter::IRC::Client::readConfigLong(const char *key, long defaultValue) const +{ + if (Jupiter::IRC::Client::Config->get(Jupiter::IRC::Client::data_->configSectionName.c_str(), key)) return Jupiter::IRC::Client::Config->getLong(Jupiter::IRC::Client::data_->configSectionName.c_str(), key); + return Jupiter::IRC::Client::Config->getLong("Default", key, defaultValue); +} + +double Jupiter::IRC::Client::readConfigDouble(const char *key, double defaultValue) const +{ + if (Jupiter::IRC::Client::Config->get(Jupiter::IRC::Client::data_->configSectionName.c_str(), key)) return Jupiter::IRC::Client::Config->getDouble(Jupiter::IRC::Client::data_->configSectionName.c_str(), key); + return Jupiter::IRC::Client::Config->getDouble("Default", key, defaultValue); +} + +void Jupiter::IRC::Client::writeToLogs(const char *message) +{ + if (Jupiter::IRC::Client::data_->logFile != nullptr) + { + fputs(message, Jupiter::IRC::Client::data_->logFile); + fputs(ENDL, Jupiter::IRC::Client::data_->logFile); + fflush(Jupiter::IRC::Client::data_->logFile); + } +} + +/** +* @brief IRC Client Data Implementation +*/ + +void Jupiter::IRC::Client::Data::delChannel(const char *chan) +{ + Jupiter::IRC::Client::Channel *channel; + for (unsigned int i = 0; i < Jupiter::IRC::Client::Data::channels.size(); i++) + { + channel = Jupiter::IRC::Client::Data::channels.get(i); + if (channel->getName().equalsi(chan)) + { + Jupiter::IRC::Client::Data::delChannel(i); + return; + } + } +} + +void Jupiter::IRC::Client::Data::delChannel(unsigned int index) +{ + delete Jupiter::IRC::Client::Data::channels.remove(index); +} + +Jupiter::IRC::Client::User *Jupiter::IRC::Client::Data::findUser(const char *nick) const +{ + Jupiter::IRC::Client::User *r; + for (unsigned int i = 0; i < Jupiter::IRC::Client::Data::users.size(); i++) + { + r = Jupiter::IRC::Client::Data::users.get(i); + if (r->getNickname().equalsi(nick)) return r; + } + return nullptr; +} + +Jupiter::IRC::Client::User *Jupiter::IRC::Client::Data::findUserOrAdd(Jupiter::CStringS &name) +{ + unsigned int wc = name.wordCount("!@"); + Jupiter::CStringS nick = (wc == 1) ? name : name.getWord(0, "!@"); + Jupiter::IRC::Client::User *r = Jupiter::IRC::Client::Data::findUser(nick.c_str()); + if (r == nullptr) + { + r = new Jupiter::IRC::Client::User(); + switch (wc) + { + case 3: + r->data_->hostname = name.getWord(2, "!@"); + case 2: // This shouldn't EVER happen. + r->data_->username = name.getWord(1, "!@"); + case 1: // No user/host is in the string. + r->data_->nickname = nick; + break; + default: + fprintf(stderr, "ERROR: Failed to parse name mask: %s" ENDL, name.c_str()); + break; + } + Jupiter::IRC::Client::Data::users.add(r); + } + return r; +} + +void Jupiter::IRC::Client::Data::addNamesToChannel(unsigned int index, Jupiter::CStringS &names) +{ + Jupiter::CStringS t; + size_t offset; + Jupiter::IRC::Client::Channel *channel = Jupiter::IRC::Client::Data::channels.get(index); + int nameLen = names.wordCount(" "); + for (short i = 0; i < nameLen; i++) + { + t = names.getWord(i, " "); + if (t.size() != 0) + { + offset = strspn(t.c_str(), Jupiter::IRC::Client::Data::prefixes.c_str()); + t.shiftRight(offset); + Jupiter::IRC::Client::User *user = Jupiter::IRC::Client::Data::findUserOrAdd(t); + t.shiftLeft(offset); + unsigned int userIndex = channel->addUser(user); + if (offset > 0) + { + while (offset > 0) + { + offset--; + channel->addUserPrefix(userIndex, t[offset]); + } + } + } + } +} + +unsigned int Jupiter::IRC::Client::Data::addChannel(const char *chan) +{ + Jupiter::IRC::Client::Data::channels.add(new Channel(chan, Jupiter::IRC::Client::Data::iFace)); + return Jupiter::IRC::Client::Data::channels.size() - 1; +} + +bool Jupiter::IRC::Client::Data::startCAP() +{ + Jupiter::IRC::Client::Data::connectionStatus = 2; + return Jupiter::IRC::Client::Data::sock->send("CAP LS" ENDL, 8) > 0; +} + +bool Jupiter::IRC::Client::Data::registerClient() +{ + bool r = true; + const char *localHostname = Jupiter::Socket::getLocalHostname(); + Jupiter::CStringL messageToSend; + messageToSend.format("USER %s %s %s :%s" ENDL, Jupiter::IRC::Client::Data::nickname.c_str(), localHostname, Jupiter::IRC::Client::Data::serverHostname.c_str(), Jupiter::IRC::Client::Data::realName.c_str()); + if (Jupiter::IRC::Client::Data::sock->send(messageToSend.c_str(), messageToSend.size()) <= 0) r = false; + messageToSend.format("NICK %s" ENDL, Jupiter::IRC::Client::Data::nickname.c_str()); + if (Jupiter::IRC::Client::Data::sock->send(messageToSend.c_str(), messageToSend.size()) <= 0) r = false; + Jupiter::IRC::Client::Data::connectionStatus = 3; + return r; +} + +/** +* User Implementation +*/ + +Jupiter::IRC::Client::User::User() +{ + Jupiter::IRC::Client::User::data_ = new Jupiter::IRC::Client::User::Data(); +} + +Jupiter::IRC::Client::User::~User() +{ + delete Jupiter::IRC::Client::User::data_; +} + +const Jupiter::StringType &Jupiter::IRC::Client::User::getNickname() const +{ + return Jupiter::IRC::Client::User::data_->nickname; +} + +const Jupiter::StringType &Jupiter::IRC::Client::User::getUsername() const +{ + return Jupiter::IRC::Client::User::data_->username; +} + +const Jupiter::StringType &Jupiter::IRC::Client::User::getHostname() const +{ + return Jupiter::IRC::Client::User::data_->hostname; +} + +unsigned int Jupiter::IRC::Client::User::getChannelCount() const +{ + return Jupiter::IRC::Client::User::data_->channelCount; +} + +/** +* Channel Implementation +*/ + +Jupiter::IRC::Client::Channel::Channel(const char *channelName, Jupiter::IRC::Client *iFace) +{ + Jupiter::IRC::Client::Channel::data_ = new Jupiter::IRC::Client::Channel::Data(); + unsigned int i = 0; + const char *ptr = nullptr; + const char *section = iFace->getConfigSection().c_str(); + char key[64]; + int offset = sprintf(key, "Channel."); + Jupiter::CStringL channelKeyStr; + + // TODO: Make this more efficient -- consider moving responsibility elsewhere. + while (1) + { + i++; + sprintf(key + offset, "%u", i); + ptr = Jupiter::IRC::Client::Config->get(section, key); + if (ptr == nullptr) + { + if (section != iFace->getConfigSection().c_str()) break; + i = 0; + section = "Default"; + continue; + } + channelKeyStr = ptr; + if (channelKeyStr.getWord(0, WHITESPACE).equalsi(channelName)) + { + sprintf(key + offset, "%u.Type", i); + ptr = Jupiter::IRC::Client::Config->get(section, key); + if (ptr != nullptr) + { + Jupiter::IRC::Client::Channel::data_->type = atoi(ptr); + break; + } + } + } + if (ptr == nullptr) + { + sprintf(key + offset, "%s.Type", channelName); + ptr = iFace->readConfigValue(key).c_str(); + if (*ptr != 0) Jupiter::IRC::Client::Channel::data_->type = atoi(ptr); + else Jupiter::IRC::Client::Channel::data_->type = iFace->getDefaultChanType(); + } + Jupiter::IRC::Client::Channel::data_->channel = channelName; + Jupiter::IRC::Client::Channel::data_->serverPrefixSetPtr = iFace->getPrefixes().c_str(); +} + +Jupiter::IRC::Client::Channel::~Channel() +{ + while (Jupiter::IRC::Client::Channel::data_->users.size() != 0) delete Jupiter::IRC::Client::Channel::data_->users.remove(0); + delete Jupiter::IRC::Client::Channel::data_; +} + +unsigned int Jupiter::IRC::Client::Channel::addUser(Jupiter::IRC::Client::User *user) +{ + Jupiter::IRC::Client::Channel::User *localUser = new Jupiter::IRC::Client::Channel::User(); + localUser->data_->user = user; + user->data_->channelCount++; + Jupiter::IRC::Client::Channel::data_->users.add(localUser); + return Jupiter::IRC::Client::Channel::data_->users.size() - 1; +} + +unsigned int Jupiter::IRC::Client::Channel::addUser(Jupiter::IRC::Client::User *user, const char prefix) +{ + unsigned int r = Jupiter::IRC::Client::Channel::addUser(user); + Jupiter::IRC::Client::Channel::data_->users.get(r)->data_->prefixes = prefix; + return r; +} + +void Jupiter::IRC::Client::Channel::delUser(const char *nickname) +{ + int index = getUserIndex(nickname); + if (index >= 0) Jupiter::IRC::Client::Channel::delUser(index); +} + +void Jupiter::IRC::Client::Channel::delUser(unsigned int index) +{ + if (index < Jupiter::IRC::Client::Channel::data_->users.size()) + { + Jupiter::IRC::Client::Channel::User *user = Jupiter::IRC::Client::Channel::data_->users.remove(index); + user->data_->user->data_->channelCount--; + delete user; + } +} + +void Jupiter::IRC::Client::Channel::addUserPrefix(unsigned int index, char prefix) +{ + if (index < Jupiter::IRC::Client::Channel::data_->users.size()) + { + if (Jupiter::IRC::Client::Channel::data_->users.get(index)->data_->prefixes.contains(prefix) == false) + { + Jupiter::IRC::Client::Channel::data_->users.get(index)->data_->prefixes += prefix; + } + } +} + +void Jupiter::IRC::Client::Channel::addUserPrefix(const char *user, char prefix) +{ + int i = Jupiter::IRC::Client::Channel::getUserIndex(user); + if (i >= 0) Jupiter::IRC::Client::Channel::addUserPrefix(i, prefix); +} + +void Jupiter::IRC::Client::Channel::delUserPrefix(unsigned int index, char prefix) +{ + if (index < Jupiter::IRC::Client::Channel::data_->users.size()) Jupiter::IRC::Client::Channel::data_->users.get(index)->data_->prefixes.remove(prefix); +} + +void Jupiter::IRC::Client::Channel::delUserPrefix(const char *user, char prefix) +{ + int i = Jupiter::IRC::Client::Channel::getUserIndex(user); + if (i >= 0) Jupiter::IRC::Client::Channel::delUserPrefix(i, prefix); +} + +const Jupiter::StringType &Jupiter::IRC::Client::Channel::getName() const +{ + return Jupiter::IRC::Client::Channel::data_->channel; +} + +Jupiter::IRC::Client::Channel::User *Jupiter::IRC::Client::Channel::getUser(unsigned int index) const +{ + Jupiter::ArrayList &users = Jupiter::IRC::Client::Channel::data_->users; + if (index < users.size()) return users.get(index); + return nullptr; +} + +Jupiter::IRC::Client::Channel::User *Jupiter::IRC::Client::Channel::getUser(const char *nickname) const +{ + Jupiter::ArrayList &users = Jupiter::IRC::Client::Channel::data_->users; + Jupiter::IRC::Client::Channel::User *user; + for (unsigned int i = 0; i < users.size(); i++) + { + user = users.get(i); + if (user->data_->user->getNickname().equalsi(nickname)) return user; + } + return nullptr; +} + +int Jupiter::IRC::Client::Channel::getUserIndex(const char *user) const +{ + for (unsigned int i = 0; i < Jupiter::IRC::Client::Channel::data_->users.size(); i++) if (Jupiter::IRC::Client::Channel::data_->users[i] && Jupiter::IRC::Client::Channel::data_->users[i]->data_->user->getNickname().equalsi(user)) return i; + return -1; +} + +int Jupiter::IRC::Client::Channel::getUserIndexByPartName(const char *user) const +{ + for (unsigned int i = 0; i < Jupiter::IRC::Client::Channel::data_->users.size(); i++) if (Jupiter::IRC::Client::Channel::data_->users[i] && stristr(Jupiter::IRC::Client::Channel::data_->users[i]->data_->user->getNickname().c_str(), user)) return i; + return -1; +} + +char Jupiter::IRC::Client::Channel::getUserPrefix(unsigned int index) const +{ + if (index < Jupiter::IRC::Client::Channel::data_->users.size()) + for (unsigned char i = 0; Jupiter::IRC::Client::Channel::data_->serverPrefixSetPtr[i]; i++) + if (containsSymbol(Jupiter::IRC::Client::Channel::data_->users.get(index)->data_->prefixes.c_str(), Jupiter::IRC::Client::Channel::data_->serverPrefixSetPtr[i])) return Jupiter::IRC::Client::Channel::data_->serverPrefixSetPtr[i]; + return 0; +} + +char Jupiter::IRC::Client::Channel::getUserPrefix(const char *user) const +{ + int i = Jupiter::IRC::Client::Channel::getUserIndex(user); + if (i >= 0) return Jupiter::IRC::Client::Channel::getUserPrefix(i); + return 0; +} + +unsigned int Jupiter::IRC::Client::Channel::getUserCount() const +{ + return Jupiter::IRC::Client::Channel::data_->users.size(); +} + +int Jupiter::IRC::Client::Channel::getType() const +{ + return Jupiter::IRC::Client::Channel::data_->type; +} + +void Jupiter::IRC::Client::Channel::setType(int iType) +{ + Jupiter::IRC::Client::Channel::data_->type = iType; +} + +/** +* Channel User Implementation +*/ + +Jupiter::IRC::Client::Channel::User::User() +{ + Jupiter::IRC::Client::Channel::User::data_ = new Jupiter::IRC::Client::Channel::User::Data(); +} + +Jupiter::IRC::Client::Channel::User::~User() +{ + delete Jupiter::IRC::Client::Channel::User::data_; +} + +Jupiter::IRC::Client::User *Jupiter::IRC::Client::Channel::User::getUser() const +{ + return Jupiter::IRC::Client::Channel::User::data_->user; +} + +const Jupiter::StringType &Jupiter::IRC::Client::Channel::User::getPrefixes() const +{ + return Jupiter::IRC::Client::Channel::User::data_->prefixes; +} + +const Jupiter::StringType &Jupiter::IRC::Client::Channel::User::getNickname() const +{ + return Jupiter::IRC::Client::Channel::User::data_->user->getNickname(); +} + +const Jupiter::StringType &Jupiter::IRC::Client::Channel::User::getUsername() const +{ + return Jupiter::IRC::Client::Channel::User::data_->user->getUsername(); +} + +const Jupiter::StringType &Jupiter::IRC::Client::Channel::User::getHostname() const +{ + return Jupiter::IRC::Client::Channel::User::data_->user->getHostname(); +} + +unsigned int Jupiter::IRC::Client::Channel::User::getChannelCount() const +{ + return Jupiter::IRC::Client::Channel::User::data_->user->getChannelCount(); +} \ No newline at end of file diff --git a/Jupiter/IRC_Client.h b/Jupiter/IRC_Client.h new file mode 100644 index 0000000..7226dbb --- /dev/null +++ b/Jupiter/IRC_Client.h @@ -0,0 +1,874 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _IRC_CLIENT_H_HEADER +#define _IRC_CLIENT_H_HEADER + +/** + * @file IRC Client.h + * @brief Provides connectivity to IRC servers. + */ + +#include +#include +#include "Jupiter.h" +#include "Thinker.h" +#include "IRC.h" +#include "CString.h" + +#define CONFIG_INI "Config.ini" /** Default location of the Config file. */ + +namespace Jupiter +{ + + class INIFile; /** Forward declaration for Jupiter::INIFile. See Jupiter::INIFile in INIFile.h. */ + + namespace IRC + { + + /** + * @brief Provides connectivity to IRC servers. + */ + class JUPITER_API Client : public Thinker + { + protected: + + /** + * @brief This is called when a connection has been successfully established. + * The current implementation calls this after the MOTD. + */ + virtual void OnConnect(); + + /** + * @brief This is called when at the end of disconnect(). + * This is called upon any connection failure or error. + */ + virtual void OnDisconnect(); + + /** + * @brief This is called when at the end of reconnect(). + * + * @param successConnect Used to determine if a connection was successfully established. + * @see connect(). + */ + virtual void OnReconnectAttempt(bool successConnect); + + /** + * @brief This is called after a message has been normally processed. + * This always happens last. All other hooks will be called before this. + * + * @param raw The raw message. + */ + virtual void OnRaw(const StringType &raw); + + /** + * @brief This is called after an IRC numeric has been processed. + * + * @param raw The raw message. + */ + virtual void OnNumeric(long int numeric, const StringType &raw); + + /** + * @brief This is called when an ERROR is received. + * This indicates a connection termination, and thus, disconnect() is called immediately after this. + * + * @param message Message sent by the server. + */ + virtual void OnError(const StringType &message); + + /** + * @brief This is called when a chat message is received. + * + * @param channel String containing the destination of the message. + * @param nick String containing the nickname of the sender. + * @param message String containing the message sent. + */ + virtual void OnChat(const StringType &channel, const StringType &nick, const StringType &message); + + /** + * @brief This is called when a notice is received. + * + * @param chan String containing the destination of the message. + * @param nick String containing the nickname of the sender. + * @param message String containing the message sent. + */ + virtual void OnNotice(const StringType &chan, const StringType &sender, const StringType &message); + + /** + * @brief This is called when a server notice is received. + * + * @param chan String containing the destination of the message. + * @param nick String containing the sender. + * @param message String containing the message sent. + */ + virtual void OnServerNotice(const StringType &chan, const StringType &sender, const StringType &message); + + /** + * @brief This is called when a CTCP message is received. + * + * @param channel String containing the destination of the message. + * @param nick String containing the nickname of the sender. + * @param message String containing the message sent. + */ + virtual void OnCTCP(const StringType &channel, const StringType &nick, const StringType &command, const StringType &message); + + /** + * @brief This is called when an action message is received. + * + * @param chan String containing the destination of the message. + * @param nick String containing the nickname of the sender. + * @param message String containing the message sent. + */ + virtual void OnAction(const StringType &chan, const StringType &nick, const StringType &message); + + /** + * @brief This is called when an invite is received. + * + * @param chan String containing the destination of the message. + * @param inviter String containing the nickname of the inviter. + * @param invited String containing the nickname of the user invited. + */ + virtual void OnInvite(const StringType &chan, const StringType &inviter, const StringType &invited); + + /** + * @brief This is called when a chat message is received. + * + * @param chan String containing the destination of the message. + * @param nick String containing the nickname of the sender. + * @param message String containing the message sent. + */ + virtual void OnJoin(const StringType &chan, const StringType &nick); + + /** + * @brief This is called when a user parts a channel. + * + * @param chan String containing the channel parted from. + * @param nick String containing the nickname of the user. + * @param reason String containing the reason for parting, or nullptr if none is specified. + */ + virtual void OnPart(const StringType &chan, const StringType &nick, const StringType &reason); + + /** + * @brief This is called when a user changes their nickname. + * + * @param oldnick String containing the old nickname of the user. + * @param newnick String containing the new nickname of the user. + */ + virtual void OnNick(const StringType &oldnick, const StringType &newnick); + + /** + * @brief This is called when a user is kicked from a channel. + * + * @param chan String containing the channel kicked from. + * @param kicker String containing the nickname of the kicker. + * @param kicked String containing the nickname of the user kicked. + * @param reason String containing the reason for the kick, or nullptr if none is specified. + */ + virtual void OnKick(const StringType &chan, const StringType &kicker, const StringType &kicked, const StringType &reason); + + /** + * @brief This is called when a user quits the server. + * + * @param nick String containing the nickname of the user. + * @param message String containing the reason for quiting. + */ + virtual void OnQuit(const StringType &nick, const StringType &message); + + /** + * @brief This is called when a channel mode is changed. + * + * @param chan String containing the affected channel. + * @param nick String containing the nickname of the user. + * @param modeString String containing the modes changed. + */ + virtual void OnMode(const Jupiter::StringType &chan, const Jupiter::StringType &nick, const Jupiter::StringType &modeString); + public: + + static INIFile *Config; /** IRC client config file. This is automatically instantiated upon library initialization. */ + class Channel; + + /** + * @brief Stores data about users. + */ + class JUPITER_API User + { + friend class Jupiter::IRC::Client; + friend class Jupiter::IRC::Client::Channel; + public: + + /** + * @brief Fetches the user's nickname. + * + * @return String containing the user's nickname. + */ + const Jupiter::StringType &getNickname() const; + + /** + * @brief Fetches the user's username. + * + * @return String containing the user's username. + */ + const Jupiter::StringType &getUsername() const; + + /** + * @brief Fetches the user's hostname. + * + * @return String containing the user's hostname. + */ + const Jupiter::StringType &getHostname() const; + + /** + * @brief Returns the number of channels the user shares with the local client. + * + * @return Number of channels. + */ + unsigned int getChannelCount() const; + + User(); + ~User(); + + /** Private members */ + private: + struct Data; + Data *data_; + }; + + /** + * @brief Provides interfacing with channels and stores channel data. + */ + class JUPITER_API Channel + { + friend class Jupiter::IRC::Client; + public: + + /** + * @brief Stores data about users. + */ + class JUPITER_API User + { + friend class Jupiter::IRC::Client::Channel; + public: + + /** + * @brief Returns the local user's global user object. + * + * @return Local user's global user object. + */ + Jupiter::IRC::Client::User *getUser() const; + + /** + * @brief Returns the user's string of channel prefixes. + * + * @return String containing the user's channel prefixes. + */ + const Jupiter::StringType &getPrefixes() const; + + /** + * @brief Fetches the user's nickname. + * + * @return String containing the user's nickname. + */ + const Jupiter::StringType &getNickname() const; + + /** + * @brief Fetches the user's username. + * + * @return String containing the user's username. + */ + const Jupiter::StringType &getUsername() const; + + /** + * @brief Fetches the user's hostname. + * + * @return String containing the user's hostname. + */ + const Jupiter::StringType &getHostname() const; + + /** + * @brief Returns the number of channels the user shares with the local client. + * + * @return Number of channels. + */ + unsigned int getChannelCount() const; + + User(); + ~User(); + + /** Private members */ + private: + struct Data; + Data *data_; + }; + + /** + * @brief Returns the name of the channel. + * + * @return String containing the name of the channel. + */ + const StringType &getName() const; + + /** + * @brief Returns a user at an index. + * + * @param index The index of the user. + * @return A user if within range, nullptr otherwise. + */ + Jupiter::IRC::Client::Channel::User *getUser(unsigned int index) const; + + /** + * @brief Searches for a user based on their nickname. + * + * @param nickname String containing the nickname of the user to find. + * @return A user if a match is found, nullptr otherwise. + */ + Jupiter::IRC::Client::Channel::User *getUser(const char *nickname) const; + + /** + * @brief Adds a user to the channel + * + * @param nickname Nickname of the user. + * @return Index of the new user. + */ + unsigned int addUser(Jupiter::IRC::Client::User *user); + + /** + * @brief Adds a user to the channel + * + * @param nickname Nickname of the user. + * @param prefix The user's prefix. + * @return Index of the new user. + */ + unsigned int addUser(Jupiter::IRC::Client::User *user, const char prefix); + + /** + * @brief Removes a user from the channel. + * + * @param nickname String containing the nickname of the user. + */ + void delUser(const char *nickname); + + /** + * @brief Removes a user from the channel. + * + * @param index Index of a user. + */ + void delUser(unsigned int index); + + /** + * @brief Adds a prefix to a user. + * + * @param index Index of a user. + * @param prefix Prefix to add to the user. + */ + void addUserPrefix(unsigned int index, char prefix); + + /** + * @brief Adds a prefix to a user. + * + * @param user String containing the nickname of the user. + * @param prefix Prefix to add to the user. + */ + void addUserPrefix(const char *user, char prefix); + + /** + * @brief Removes a prefix from a user. + * + * @param index Index of a user. + * @param prefix Prefix to remove from the user. + */ + void delUserPrefix(unsigned int index, char prefix); + + /** + * @brief Removes a prefix from a user. + * + * @param user String containing the nickname of a user. + * @param prefix Prefix to remove from the user. + */ + void delUserPrefix(const char *user, char prefix); + + /** + * @brief Returns the index of a user. + * + * @param user String containing the nickname of a user. + * @return Index of a user if they're found, -1 otherwise. + */ + int getUserIndex(const char *user) const; + + /** + * @brief Returns the index of a user. + * + * @param user String containing part of the nickname of a user. + * @return Index of a user if they're found, -1 otherwise. + */ + int getUserIndexByPartName(const char *user) const; + + /** + * @brief Returns a user's most significant prefix. + * + * @param index Index of a user. + * @return User's most significant prefix. + */ + char getUserPrefix(unsigned int index) const; + + /** + * @brief Returns a user's most significant prefix. + * + * @param user String containing the nickname of a user. + * @return User's most significant prefix. + */ + char getUserPrefix(const char *user) const; + + /** + * @brief Returns the number of users in this channel. + * + * @return Number of users. + */ + unsigned int getUserCount() const; + + /** + * @brief Returns the type of this channel. + * + * @return Type of this channel. + */ + int getType() const; + + /** + * @brief Sets the type of this channel. + * + * @param iType Type to set this channel to. + */ + void setType(int iType); + + /** + * @brief Constructor for Channel + * + * @param channelName String containing the name of a channel. + * @param iFace Server in which this channel is located. + */ + Channel(const char *channelName, Client *iFace); + + /** + * @brief Destructor for Channel + */ + ~Channel(); + + /** Private members */ + private: + struct Data; + Data *data_; + }; // Jupiter::IRC::Client::Channel class + + /** + * @brief Returns the config section this refers to. + * + * @return String containing a config section. + */ + const StringType &getConfigSection() const; + + /** + * @brief Returns the name of the file this logs to. + * + * @return String containing a log file's name. + */ + const StringType &getLogFile() const; + + /** + * @brief Returns the nickname prefixes supported by the connected server. + * + * @return String containing nickname prefixes. + */ + const StringType &getPrefixes() const; + + /** + * @brief Returns the client's current nickname. + * + * @return String containing a nickame. + */ + const StringType &getNickname() const; + + /** + * @brief Returns the client's real name. + * + * @return String containing a name. + */ + const StringType &getRealname() const; + + /** + * @brief Returns the server's name + * + * @return String containing the server's name. + */ + const StringType &getServerName() const; + + /** + * @brief Returns the server's hostname. + * Note: This is the same as the config's value. + * + * @return String containing a hostname. + */ + const StringType &getServerHostname() const; + + /** + * @brief Returns the server's port. + * Note: This is the same as the config's value. + * + * @return Port used to connect. + */ + unsigned short getServerPort() const; + + /** + * @brief Returns the time delay between reconnect attempts. + * + * @return Time delay between reconnect attemps. + */ + time_t getReconnectDelay() const; + + /** + * @brief Returns the time scheduled to make a reconenct attempt. + * This is the sum of the time of disconnection and the reconnection time delay. + * + * @return Time scheduled. + */ + time_t getReconnectTime() const; + + /** + * @brief Returns the number of consecutive reconnect attempts executed so far. + * + * @return Number of reconnects attempted. + */ + int getReconnectAttempts() const; + + /** + * @brief Returns the maximum number of consecutive reconnect attempts to make before failing. + * + * @return Maximum number of reconnect attempts. + */ + int getMaxReconnectAttempts() const; + + /** + * @brief Returns the default channel type. + * + * @return Default channel type. + */ + short getDefaultChanType() const; + + /** + * @brief Returns if this prints to stdout. + * + * @return True if it prints, false otherwise. + */ + FILE *getPrintOutput() const; + + /** + * @brief Sets where to print output. + * + * @param outf FILE object to output to. + * Note: outf must be either a valid and open FILE pointer, or nullptr to disable output. + */ + void setPrintOutput(FILE *outf); + + /** + * @brief Fetches a user from the user list. + * + * @param index Index of the user to fetch. + * @return User located at the specified index. + */ + Jupiter::IRC::Client::User *getUser(unsigned int index) const; + + /** + * @brief Fetches a user from the user list. + * + * @param nickname String containing the nickname of the user to fetch. + * @return A User if a match is found, nullptr otherwise. + */ + Jupiter::IRC::Client::User *getUser(const char *nickname) const; + + /** + * @brief Fetches a user's index from the user list. + * + * @param nickname String containing the nickname of the user to fetch. + * @return The index of a user if a match is found, -1 otherwise. + */ + int getUserIndex(const char *nickname) const; + + /** + * @brief Fetches a user's index from the user list. + * + * @param user Pointer to a user's data. + * @return The index of a user if a match is found, -1 otherwise. + */ + int getUserIndex(Jupiter::IRC::Client::User *user) const; + + /** + * @brief Fetches the size of the user list. + * + * @return Size of the user list. + */ + unsigned int getUserCount() const; + + /** + * @brief Returns the number of channels. + * + * @return Number of channels. + */ + unsigned int getChannelCount() const; + + /** + * @brief Returns a channel. + * + * @param index Index of the chanel to return. + * @return The channel at index. + */ + Channel *getChannel(unsigned int index) const; + + /** + * @brief Returns a channel. + * + * @param chanName String containing the name of a channel. + * @return The channel at index. + */ + Channel *getChannel(const char *chanName) const; + + /** + * @brief Returns a channel's name at index. + * + * @param index Index of the channel. + * @return Channel's name. + */ + const StringType &getChannelName(unsigned int index) const; + + /** + * @brief Returns the index of the channel with a given name. + * + * @param chanName String containing the name of a channel. + * @return Index of a channel if a match is found, -1 otherwise. + */ + int getChannelIndex(const char *chanName) const; + + /** + * @brief Sends a join request. + * + * @param channel Channel to join. + */ + void joinChannel(const char *channel); + + /** + * @brief Sends a join request with a password. + * + * @param channel Channel to join. + * @param password Password to use. + */ + void joinChannel(const char *channel, const char *password); + + /** + * @brief Parts a channel. + * + * @param channel Channel to part. + */ + void partChannel(const char *channel); + + /** + * @brief Parts a channel. + * + * @param channel Channel to part. + * @param message Reason for parting. + */ + void partChannel(const char *channel, const char *message); + + /** + * @brief Gets the access level of a user. + * + * @param chan String containing a channel name to get the access level from. + * @param nick String containing the nickname of the user. + * @return Access level of the user. + */ + int getAccessLevel(const char *chan, const char *nick) const; + + /** + * @brief Sends a message. + * + * @param dest String containing the destination of the message (nickname or channel). + * @param message String containing the message to send. + */ + void sendMessage(const char *dest, const char *message); + + /** + * @brief Sends a notice. + * + * @param dest String containing the destination of the message (nickname or channel). + * @param message String containing the message to send. + */ + void sendNotice(const char *dest, const char *message); + + /** + * @brief Sends a message to all channels of a given type. + * + * @param type Type of channel to messasge. + * @param message String containing the message to send. + * @return Number of messages sent. + */ + unsigned int messageChannels(short type, const char *message); + + /** + * @brief Sends a message to all channels with a type of at least 0. + * + * @param message String containing the message to send. + * @return Number of messages sent. + */ + unsigned int messageChannels(const char *message); + + /** + * @brief Returns if the client will automatically reconnect upon failure. + * + * @return True if the server will automatically reconnect, false otherwise. + */ + bool isAutoReconnect() const; + + /** + * @brief Sets how many times the client should attempt to automatically reconnect. + * + * @param autoReconnect Maximum number of reconnect attempts. A negative value indicates infinity. + */ + void setAutoReconnect(int autoReconnect); + + /** + * @brief Sends data to the server. + * Endlines are automatically added. + * + * @param rawMessage String containing the data to send. + */ + void send(const char *rawMessage); + + /** + * @brief Method that is called by think() to handle all of the IRC protocol. + * Note: This command should not be used unless you wish to bypass auto-reconnect abilities. Use think() instead. + * + * @return 0 upon success, an error code otherwise. + */ + int primaryHandler(); + + /** + * @brief Returns a key's value. + * This reads from the client's config section first, then default if it doesn't exist. + * + * @param key String containing the key name. + * @return String containing the key value if it exists, defaultValue otherwise. + */ + Jupiter::CStringS readConfigValue(const char *key, const char *defaultValue = nullptr) const; + + /** + * @brief Returns a key's value as a boolean. + * This reads from the client's config section first, then default if it doesn't exist. + * + * @param key String containing the key name. + * @return Boolean value of the key value if it exists, defaultValue otherwise. + */ + bool readConfigBool(const char *key, bool defaultValue = false) const; + + /** + * @brief Returns a key's value as an integer. + * This reads from the client's config section first, then default if it doesn't exist. + * + * @param key String containing the key name. + * @return Integer value of the key value if it exists, defaultValue otherwise. + */ + int readConfigInt(const char *key, int defaultValue = 0) const; + + /** + * @brief Returns a key's value as a long integer. + * This reads from the client's config section first, then default if it doesn't exist. + * + * @param key String containing the key name. + * @return Long integer value of the key value if it exists, defaultValue otherwise. + */ + long readConfigLong(const char *key, long defaultValue = 0) const; + + /** + * @brief Returns a key's value as a double. + * This reads from the client's config section first, then default if it doesn't exist. + * + * @param key String containing the key name. + * @return Double value of the key value if it exists, defaultValue otherwise. + */ + double readConfigDouble(const char *key, double defaultValue = 0) const; + + /** + * @brief Writes to the server's log file. + * + * @param message String containing the text to write to the file. + */ + void writeToLogs(const char *message); + + /** + * @brief Connects the client to its server. + * Note: This should not be called unless it is not already connected to a server. + * Also, a successful socket connection does not mean the server accepted the connection. + * + * @return True upon successful socket connection, false otherwise. + */ + bool connect(); + + /** + * @brief Completely disconnects the client from a server. + * Note: This does not send any type of QUIT message. + */ + void disconnect(bool stayDead = false); + + /** + * @brief Completely disconnects the client from a server. + * + * @param message String containing the QUIT message to send prior to disconnecting. + */ + void disconnect(const char *message, bool stayDead = false); + + /** + * @brief Calls disconnect() if the client has not already, then calls connect(). + * Note: This will increment the current reconnect attempts by 1. + */ + void reconnect(); + + /** + * @brief Method to be looped to sustain the IRC connection. + * Note: This client should be deleted if this returns anything other than 0. + * + * @return 0 if the client should still exist, error code otherwise. + */ + virtual int think(); + + /** + * @brief Constructor for a client. + * + * @param configSection String containing the config section for the client to read from, before defaulting to "Default". + */ + Client(const char *configSection); + + /** + * @brief Destructor for a client. + */ + virtual ~Client(); + + /** Private members */ + private: + struct Data; + Data *data_; + }; // Jupiter::IRC::Client class + + } // Jupiter::IRC namespace + +} // Jupiter namespace + +#endif // _IRC_CLIENT_H_HEADER \ No newline at end of file diff --git a/Jupiter/IRC_Numerics.h b/Jupiter/IRC_Numerics.h new file mode 100644 index 0000000..c5af7d3 --- /dev/null +++ b/Jupiter/IRC_Numerics.h @@ -0,0 +1,708 @@ +/** + * Copyright (C) 2013-2014 Justin James. + * + * This license must be preserved. + * Any applications, libraries, or code which make any use of any + * component of this program must not be commercial, unless explicit + * permission is granted from the original author. The use of this + * program for non-profit purposes is permitted. + * + * 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. + * + * In the event that this license restricts you from making desired use of this program, contact the original author. + * Written by Justin James + */ + +#if !defined _IRC_NUMERICS_H_HEADER +#define _IRC_NUMERICS_H_HEADER + +/** +* These numerics are in large part found here: https://www.alien.net.au/irc/irc2numerics.html +* Not all numerics from the above list are included (generally conflicting numerics such as RPL_MAP). +*/ + +/** +* To enable the shorter definitions (those without IRC_ in front), define "SHORT_IRC_MACROS" prior to including this file. +*/ + +#if !defined SKIP_IRC_MACROS + +#define IRC_RPL_WELCOME 1 /** RFC2812: Post-registration welcome message. */ +#define IRC_RPL_YOURHOST 2 /** RFC2812: Post-registration. Your host is this server and I am running some daemon */ +#define IRC_RPL_CREATED 3 /** RFC2812: Post-registration. This was was created at some point in time */ +#define IRC_RPL_MYINFO 4 /** RFC2812: Post-registration. */ +#define IRC_RPL_BOUNCEOLD 5 /** RFC2812/DEPRECATED: 005 is rarely used as a bounce indicator, but was defined in RFC2812. */ +#define IRC_RPL_ISUPPORT 5 /** Used to indicate what a server supports. Does not appear in any RFC, but was drafted in 2004. */ +#define IRC_RPL_PROTOCTL IRC_RPL_ISUPPORT /** Use IRC_RPL_ISUPPORT instead */ +#define IRC_RPL_SNOMASK 8 /** Server notice mask */ +#define IRC_RPL_STATMEMTOT 9 /** I have no idea what this does. */ +#define IRC_RPL_BOUNCE 10 /** Bounce server message. Different syntax from IRC_RPL_BOUNCEOLD */ +#define IRC_RPL_STATMEM 10 /** I have no idea what this does. */ +#define IRC_RPL_YOURCOOKIE 14 /** I have no idea what this does. */ +#define IRC_RPL_YOURID 42 /** Informs you of your unique ID. */ +#define IRC_RPL_SAVENICK 43 /** Your nick was force-changed due to a nick collision */ +#define IRC_RPL_ATTEMPTINGJUNC 50 /** I have no idea what this does. */ +#define IRC_RPL_ATTEMPTINGREROUTE 51 /** I have no idea what this does. */ +#define IRC_RPL_TRACELINK 200 /** RFC1459: "If the TRACE message is destined for another server, all intermediate servers must return a IRC_RPL_TRACELINK reply to indicate that the TRACE passed through it and where its going next."*/ +#define IRC_RPL_TRACECONNECTING 201 /** RFC1459 */ +#define IRC_RPL_TRACEHANDSHAKE 202 /** RFC1459 */ +#define IRC_RPL_TRACEUNKNOWN 203 /** RFC1459 */ +#define IRC_RPL_TRACEOPERATOR 204 /** RFC1459 */ +#define IRC_RPL_TRACEUSER 205 /** RFC1459 */ +#define IRC_RPL_TRACESERVER 206 /** RFC1459 */ +#define IRC_RPL_TRACESERVICE 207 /** RFC2812 */ +#define IRC_RPL_TRACENEWTYPE 208 /** RFC1459 */ +#define IRC_RPL_TRACECLASS 209 /** RFC2812 */ +#define IRC_RPL_TRACERECONNECT 210 /** RFC2812 */ +#define IRC_RPL_STATS 210 /** I have no idea what this does. */ +#define IRC_RPL_STATSLINKINFO 211 /** RFC1459 */ +#define IRC_RPL_STATSCOMMANDS 212 /** RFC1459 */ +#define IRC_RPL_STATSCLINE 213 /** RFC1459 */ +#define IRC_RPL_STATSNLINE 214 /** RFC1459 */ +#define IRC_RPL_STATSILINE 215 /** RFC1459 */ +#define IRC_RPL_STATSKLINE 216 /** RFC1459 */ +#define IRC_RPL_STATSQLINE 217 /** RFC1459 */ +#define IRC_RPL_STATSYLINE 218 /** RFC1459 */ +#define IRC_RPL_ENDOFSTATS 219 /** RFC1459 */ +#define IRC_RPL_UMODEIS 221 /** RFC1459 */ +#define IRC_RPL_SERVICEINFO 231 /** RFC1459 */ +#define IRC_RPL_ENDOFSERVICES 232 /** RFC1459 */ +#define IRC_RPL_SERVICE 233 /** RFC1459 */ +#define IRC_RPL_SERVLIST 234 /** RFC2812 */ +#define IRC_RPL_STATSVERBOSE 236 /** I have no idea what this does. */ +#define IRC_RPL_STATSENGINE 237 /** I have no idea what this does. */ +#define IRC_RPL_STATSIAUTH 239 /** I have no idea what this does. */ +#define IRC_RPL_STATSLLINE 241 /** RFC1459 */ +#define IRC_RPL_STATSUPTIME 242 /** RFC1459 */ +#define IRC_RPL_STATSOLINE 243 /** RFC1459 */ +#define IRC_RPL_STATSHLINE 244 /** RFC1459 */ +#define IRC_RPL_STATSSLINE 245 /** I have no idea what this does. */ +#define IRC_RPL_STATSPING 246 /** RFC2812 */ +#define IRC_RPL_STATSCONN 250 /** I have no idea what this does. */ +#define IRC_RPL_LUSERCLIENT 251 /** RFC1459 */ +#define IRC_RPL_LUSEROP 252 /** RFC1459 */ +#define IRC_RPL_LUSERUNKNOWN 253 /** RFC1459 */ +#define IRC_RPL_LUSERCHANNELS 254 /** RFC1459 */ +#define IRC_RPL_LUSERME 255 /** RFC1459 */ +#define IRC_RPL_ADMINME 256 /** RFC1459 */ +#define IRC_RPL_ADMINLOC1 257 /** RFC1459 */ +#define IRC_RPL_ADMINLOC2 258 /** RFC1459 */ +#define IRC_RPL_ADMINEMAIL 259 /** RFC1459 */ +#define IRC_RPL_TRACELOG 261 /** RFC1459 */ +#define IRC_RPL_TRACEEND 262 /** RFC2812 */ +#define IRC_RPL_TRYAGAIN 263 /** RFC2812 */ +#define IRC_RPL_LOCALUSERS 265 /** I have no idea what this does. */ +#define IRC_RPL_CURRENT_LOCAL IRC_RPL_LOCALUSERS /** Use IRC_RPL_GLOBALUSERS */ +#define IRC_RPL_GLOBALUSERS 266 /** I have no idea what this does. */ +#define IRC_RPL_CURRENT_GLOBAL IRC_RPL_GLOBALUSERS /** Use IRC_RPL_GLOBALUSERS */ +#define IRC_RPL_START_NETSTAT 267 /** I have no idea what this does. */ +#define IRC_RPL_NETSTAT 268 /** I have no idea what this does. */ +#define IRC_RPL_END_NETSTAT 269 /** I have no idea what this does. */ +#define IRC_RPL_PRIVS 270 /** I have no idea what this does. */ +#define IRC_RPL_SILELIST 271 /** I have no idea what this does. */ +#define IRC_RPL_ENDOFSILELIST 272 /** I have no idea what this does. */ +#define IRC_RPL_NOTIFY 273 /** I have no idea what this does. */ +#define IRC_RPL_VCHANEXIST 276 /** I have no idea what this does. */ +#define IRC_RPL_VCHANLIST 277 /** I have no idea what this does. */ +#define IRC_RPL_VCHANHELP 278 /** I have no idea what this does. */ +#define IRC_RPL_GLIST 280 /** I have no idea what this does. */ +#define IRC_RPL_CHANINFO_KICKS 296 /** I have no idea what this does. */ +#define IRC_RPL_END_CHANINFO 299 /** I have no idea what this does. */ +#define IRC_RPL_NONE 300 /** RFC1459 */ +#define IRC_RPL_AWAY 301 /** RFC1459 */ +#define IRC_RPL_USERHOST 302 /** RFC1459 */ +#define IRC_RPL_ISON 303 /** RFC1459 */ +#define IRC_RPL_TEXT 304 /** I have no idea what this does. Supposedly deprecated? */ +#define IRC_RPL_UNAWAY 305 /** RFC1459 */ +#define IRC_RPL_NOWAWAY 306 /** RFC1459 */ +#define IRC_RPL_WHOISUSER 311 /** RFC1459 */ +#define IRC_RPL_WHOISSERVER 312 /** RFC1459 */ +#define IRC_RPL_WHOISOPERATOR 313 /** RFC1459 */ +#define IRC_RPL_WHOWASUSER 314 /** RFC1459 */ +#define IRC_RPL_ENDOFWHO 315 /** RFC1459 */ +#define IRC_RPL_WHOISCHANOP 316 /** RFC1459 */ +#define IRC_RPL_WHOISIDLE 317 /** RFC1459 */ +#define IRC_RPL_ENDOFWHOIS 318 /** RFC1459 */ +#define IRC_RPL_WHOISCHANNELS 319 /** RFC1459 */ +#define IRC_RPL_WHOISVIRT 320 /** I have no idea what this does. */ +#define IRC_RPL_WHOIS_HIDDEN 320 /** I have no idea what this does. */ +#define IRC_RPL_WHOISSPECIAL 320 /** I have no idea what this does. */ +#define IRC_RPL_LISTSTART 321 /** RFC1459 */ +#define IRC_RPL_LIST 322 /** RFC1459 */ +#define IRC_RPL_LISTEND 323 /** RFC1459 */ +#define IRC_RPL_CHANNELMODEIS 324 /** RFC1459 */ +#define IRC_RPL_UNIQOPIS 325 /** RFC2812 */ +#define IRC_RPL_NOCHANPASS 326 /** I have no idea what this does. */ +#define IRC_RPL_CHPASSUNKNOWN 327 /** I have no idea what this does. */ +#define IRC_RPL_CHANNEL_URL 328 /** I have no idea what this does. */ +#define IRC_RPL_CREATIONTIME 329 /** I have no idea what this does. */ +#define IRC_RPL_NOTOPIC 331 /** RFC1459 */ +#define IRC_RPL_TOPIC 332 /** RFC1459 */ +#define IRC_RPL_TOPICWHOTIME 333 /** I have no idea what this does. */ +#define IRC_RPL_BADCHANPASS 339 /** I have no idea what this does. */ +#define IRC_RPL_USERIP 340 /** I have no idea what this does. */ +#define IRC_RPL_INVITING 341 /** RFC1459 */ +#define IRC_RPL_SUMMONING 342 /** RFC1459 */ +#define IRC_RPL_INVITED 345 /** I have no idea what this does. */ +#define IRC_RPL_INVITELIST 346 /** RFC2812 */ +#define IRC_RPL_ENDOFINVITELIST 347 /** RFC2812 */ +#define IRC_RPL_EXCEPTLIST 348 /** RFC2812 */ +#define IRC_RPL_ENDOFEXCEPTLIST 349 /** RFC2812 */ +#define IRC_RPL_VERSION 351 /** RFC1459 */ +#define IRC_RPL_WHOREPLY 352 /** RFC1459 */ +#define IRC_RPL_NAMREPLY 353 /** RFC1459 */ +#define IRC_RPL_WHOSPCRPL 354 /** I have no idea what this does. */ +#define IRC_RPL_NAMREPLY_ 355 /** I have no idea what this does. */ +#define IRC_RPL_KILLDONE 361 /** RFC1459 */ +#define IRC_RPL_CLOSING 362 /** RFC1459 */ +#define IRC_RPL_CLOSEEND 363 /** RFC1459 */ +#define IRC_RPL_LINKS 364 /** RFC1459 */ +#define IRC_RPL_ENDOFLINKS 365 /** RFC1459 */ +#define IRC_RPL_ENDOFNAMES 366 /** RFC1459 */ +#define IRC_RPL_BANLIST 367 /** RFC1459 */ +#define IRC_RPL_ENDOFBANLIST 368 /** RFC1459 */ +#define IRC_RPL_ENDOFWHOWAS 369 /** RFC1459 */ +#define IRC_RPL_INFO 371 /** RFC1459 */ +#define IRC_RPL_MOTD 372 /** RFC1459 */ +#define IRC_RPL_INFOSTART 373 /** RFC1459 */ +#define IRC_RPL_ENDOFINFO 374 /** RFC1459 */ +#define IRC_RPL_MOTDSTART 375 /** RFC1459 */ +#define IRC_RPL_ENDOFMOTD 376 /** RFC1459 */ +#define IRC_RPL_YOUREOPER 381 /** RFC1459 */ +#define IRC_RPL_REHASHING 382 /** RFC1459 */ +#define IRC_RPL_YOURESERVICE 383 /** RFC2812 */ +#define IRC_RPL_MYPORTIS 384 /** RFC1459 */ +#define IRC_RPL_NOTOPERANYMORE 385 /** You are no longer a server operator */ +#define IRC_RPL_ALIST 388 /** I have no idea what this does. */ +#define IRC_RPL_ENDOFALIST 389 /** I have no idea what this does. */ +#define IRC_RPL_TIME 391 /** RFC1459: TIME command response. [possible extension data] :