diff options
Diffstat (limited to 'src/kvilib/core')
-rw-r--r-- | src/kvilib/core/Makefile.am | 5 | ||||
-rw-r--r-- | src/kvilib/core/kvi_bswap.h | 63 | ||||
-rw-r--r-- | src/kvilib/core/kvi_error.cpp | 237 | ||||
-rw-r--r-- | src/kvilib/core/kvi_error.h | 188 | ||||
-rw-r--r-- | src/kvilib/core/kvi_heapobject.cpp | 96 | ||||
-rw-r--r-- | src/kvilib/core/kvi_heapobject.h | 50 | ||||
-rw-r--r-- | src/kvilib/core/kvi_inttypes.h | 95 | ||||
-rw-r--r-- | src/kvilib/core/kvi_malloc.cpp | 198 | ||||
-rw-r--r-- | src/kvilib/core/kvi_malloc.h | 88 | ||||
-rw-r--r-- | src/kvilib/core/kvi_memmove.cpp | 253 | ||||
-rw-r--r-- | src/kvilib/core/kvi_memmove.h | 105 | ||||
-rw-r--r-- | src/kvilib/core/kvi_pointerhashtable.h | 999 | ||||
-rw-r--r-- | src/kvilib/core/kvi_pointerlist.h | 1069 | ||||
-rw-r--r-- | src/kvilib/core/kvi_qcstring.h | 39 | ||||
-rw-r--r-- | src/kvilib/core/kvi_qstring.cpp | 1125 | ||||
-rw-r--r-- | src/kvilib/core/kvi_qstring.h | 293 | ||||
-rw-r--r-- | src/kvilib/core/kvi_strasm.h | 194 | ||||
-rw-r--r-- | src/kvilib/core/kvi_string.cpp | 3063 | ||||
-rw-r--r-- | src/kvilib/core/kvi_string.h | 552 | ||||
-rw-r--r-- | src/kvilib/core/kvi_stringarray.cpp | 119 | ||||
-rw-r--r-- | src/kvilib/core/kvi_stringarray.h | 55 | ||||
-rw-r--r-- | src/kvilib/core/kvi_valuelist.h | 37 |
22 files changed, 8923 insertions, 0 deletions
diff --git a/src/kvilib/core/Makefile.am b/src/kvilib/core/Makefile.am new file mode 100644 index 00000000..c84487eb --- /dev/null +++ b/src/kvilib/core/Makefile.am @@ -0,0 +1,5 @@ +############################################################################### +# KVirc IRC client Makefile - 16.12.98 Szymon Stefanek <stefanek@tin.it> +############################################################################### + +EXTRA_DIST = *.cpp *.h diff --git a/src/kvilib/core/kvi_bswap.h b/src/kvilib/core/kvi_bswap.h new file mode 100644 index 00000000..5d5ef5bb --- /dev/null +++ b/src/kvilib/core/kvi_bswap.h @@ -0,0 +1,63 @@ +#ifndef _KVI_BSWAP_H_ +#define _KVI_BSWAP_H_ + +//============================================================================= +// +// File : kvi_bswap.h +// Creation date : Fri Mar 19 1999 03:15:21 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#include "kvi_settings.h" +#include "kvi_inttypes.h" + + +// KVILIB_API has been removed from therse two functions +// these should always go inlined + +inline kvi_u64_t kvi_swap64(kvi_u64_t i) +{ + // abcdefgh to hgfedcba + return ((i << 56) | /* h to a */ + ((i & 0xff00) << 40) | /* g to b */ + ((i & 0xff0000) << 24) | /* f to c */ + ((i & 0xff000000) << 8) | /* e to d */ + ((i >> 8) & 0xff000000) | /* d to e */ + ((i >> 24) & 0xff0000) | /* c to f */ + ((i >> 40) & 0xff00) | /* b to g */ + (i >> 56)); /* a to h */ +} + +inline kvi_u32_t kvi_swap32(kvi_u32_t i) +{ + // abcd to dcba + return ((i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24)); +} + +inline kvi_u16_t kvi_swap16(kvi_u16_t i) +{ + // ab to ba + return ((i << 8) | (i >> 8)); +} + + + + +#endif // !_KVI_BSWAP_H_ diff --git a/src/kvilib/core/kvi_error.cpp b/src/kvilib/core/kvi_error.cpp new file mode 100644 index 00000000..6497c757 --- /dev/null +++ b/src/kvilib/core/kvi_error.cpp @@ -0,0 +1,237 @@ +//============================================================================= +// +// File : kvi_error.cpp +// Creation date : Sun Jul 02 2000 18:37:02 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#define __KVILIB__ + +#define _KVI_ERROR_CPP_ + +#include "kvi_locale.h" +#include "kvi_error.h" +#include "kvi_settings.h" + + +#ifdef COMPILE_ON_WINDOWS +#include <winsock2.h> // for the WSAE* error codes +#endif + +#include <errno.h> + +#ifdef HAVE_STRERROR +#include <string.h> // for strerror() +#endif + +// FIXME: This stuff should basically die and be eventually replaced with some +// helper functions for handling ONLY system errors. +// +// WARNING: getDescription() is not even thread safe... it will die in the near future + +const char * g_errorTable[KVI_NUM_ERRORS]= +{ + __tr_no_lookup("Success"), // 000: success + __tr_no_lookup("Unknown error"), // 001: unkonwnError + __tr_no_lookup("Internal error"), // 002: internalError + __tr_no_lookup("Unknown command"), // 003: unknownCommand + __tr_no_lookup("Missing closing brace"), // 004: missingClosingBrace + __tr_no_lookup("Unexpected end of command in string"), // 005: unexpectedEndInString + __tr_no_lookup("Unexpected end of command in dictionary key"), // 006: unexpectedEndInDictionaryKey + __tr_no_lookup("Switch dash without switch letter"), // 007: switchDashWithoutSwitchLetter + __tr_no_lookup("Unknown function"), // 008: unknownFunction + __tr_no_lookup("Unexpected end of command in parenthesis"), // 009: unexpectedEndInParenthesis + __tr_no_lookup("Unexpected end of command in function parameters"), // 010: unexpectedEndInFunctionParams + __tr_no_lookup("Missing variable name"), // 011: missingVariableName + __tr_no_lookup("Variable or identifier expected"), // 012: variableOrIdentifierExpected + __tr_no_lookup("Left operand is not a number"), // 013: leftOperandIsNotANumber + __tr_no_lookup("Multiple operations not supported for numeric operators"), // 014: multipleOpsNotSupportedForOperator + __tr_no_lookup("Division by zero"), // 015: divisionByZero + __tr_no_lookup("Modulo by zero"), // 016: moduloByZero + __tr_no_lookup("Right operand is not a number"), // 017: rightOperandIsNotANumber + __tr_no_lookup("Unterminated expression (missing ')' ?)"), // 018: unterminatedExpression + __tr_no_lookup("Unterminated subexpression (Parenthesis mismatch)"), // 019: unterminatedSubexpression + __tr_no_lookup("Unexpected character"), // 020: unexpectedCharacter + __tr_no_lookup("Unknown operator"), // 021: unknownOperator + __tr_no_lookup("No host to resolve"), // 022 + __tr_no_lookup("(DNS Internal) Unsupported address family"), // 023 + __tr_no_lookup("Valid name but the host has no IP address"), // 024 + __tr_no_lookup("Unrecoverable nameserver error (crashed ?)"), // 025 + __tr_no_lookup("Dns temporaneous fault (try again)"), // 026 + __tr_no_lookup("(DNS Internal) Bad flags"), // 027 + __tr_no_lookup("(DNS Internal) Out of memory"), // 028 + __tr_no_lookup("(DNS Internal) Service not supported"), // 029 + __tr_no_lookup("Unknown node (host not found)"), // 030 + __tr_no_lookup("(DNS Internal) Unsupported socket type"), // 031 + __tr_no_lookup("Dns query failed"), // 032 + __tr_no_lookup("This KVIrc executable has no IPV6 support"), // 033 + __tr_no_lookup("Host not found"), // 034 + __tr_no_lookup("(DNS Internal) IPC failure (slave data corrupted)"), // 035 + __tr_no_lookup("Another connection in progress"), // 036 + __tr_no_lookup("Invalid IP address"), // 037 + __tr_no_lookup("Socket creation failed"), // 038 + __tr_no_lookup("Failed to put the socket in non blocking mode"), // 039 + __tr_no_lookup("Bad file descriptor"), // 040 + __tr_no_lookup("Out of address space"), // 041 + __tr_no_lookup("Connection refused"), // 042 + __tr_no_lookup("Kernel networking panic"), // 043 + __tr_no_lookup("Connection timed out"), // 044 + __tr_no_lookup("Network is unreachable"), // 045 + __tr_no_lookup("Broken pipe"), // 046 + __tr_no_lookup("Invalid proxy address"), // 047 + __tr_no_lookup("Remote end has closed the connection"), // 048 + __tr_no_lookup("Invalid irc context id"), // 049 + __tr_no_lookup("Error in loading module"), // 050 + __tr_no_lookup("No such module command"), // 051 + __tr_no_lookup("No such module function"), // 052 + __tr_no_lookup("Left operand is not a dictionary reference"), // 053 + __tr_no_lookup("Right operand is not a dictionary reference"), // 054 + __tr_no_lookup("Missing object class name"), // 055 + __tr_no_lookup("No such object class"), // 056 + __tr_no_lookup("No such object"), // 057 + __tr_no_lookup("No such object function"), // 058 + __tr_no_lookup("Invalid left operand"), // 059 + __tr_no_lookup("Not enough parameters"), // 060 + __tr_no_lookup("Integer parameter expected"), // 061 + __tr_no_lookup("Invalid parameter"), // 062 + __tr_no_lookup("No such file"), // 063 + __tr_no_lookup("Open parenthesis expected"), // 064 + __tr_no_lookup("Open brace expected"), // 065 + __tr_no_lookup("Can't kill a builtin class"), // 066 + __tr_no_lookup("The SOCKSV4 protocol lacks IpV6 support"), // 067 + __tr_no_lookup("Unrecognized proxy reply"), // 068 + __tr_no_lookup("Proxy response: auth failed: access denied"), + __tr_no_lookup("Proxy response: No acceptable auth method: request rejected"), + __tr_no_lookup("Proxy response: request failed"), + __tr_no_lookup("Proxy response: ident failed"), + __tr_no_lookup("Proxy response: ident not matching"), + __tr_no_lookup("Proxy response: general SOCKS failure"), + __tr_no_lookup("Proxy response: connection not allowed"), + __tr_no_lookup("Proxy response: network unreachable"), + __tr_no_lookup("Proxy response: host unreachable"), + __tr_no_lookup("Proxy response: connection refused"), + __tr_no_lookup("Proxy response: TTL expired"), + __tr_no_lookup("Proxy response: command not supported"), + __tr_no_lookup("Proxy response: address type not supported"), + __tr_no_lookup("Proxy response: invalid address"), + __tr_no_lookup("Invalid port number"), + __tr_no_lookup("Socket not connected"), + __tr_no_lookup("Insufficient resources to complete the operation"), + __tr_no_lookup("Can't setup a listening socket : bind failed"), + __tr_no_lookup("Can't resolve the localhost name"), + __tr_no_lookup("Unsupported image format"), + __tr_no_lookup("Can't open file for appending"), + __tr_no_lookup("Can't open file for writing"), + __tr_no_lookup("File I/O error"), + __tr_no_lookup("Acknowledge error"), + __tr_no_lookup("Can't open file for reading"), + __tr_no_lookup("Can't send a zero-size file"), + __tr_no_lookup("Missing popup name"), + __tr_no_lookup("'item', 'popup', 'label' or 'separator' keyword expected"), + __tr_no_lookup("Self modification not allowed"), + __tr_no_lookup("UNUSED"), + __tr_no_lookup("Feature not available"), + __tr_no_lookup("Unexpected characters in array index"), + __tr_no_lookup("Unexpected end in expression"), + __tr_no_lookup("Unexpected end in array index"), + __tr_no_lookup("Connection thru HTTP proxy failed"), + __tr_no_lookup("Case , match , regexp , default or break keyword expected"), + __tr_no_lookup("Access denied"), + __tr_no_lookup("Address already in use"), + __tr_no_lookup("Can't assign the requested address"), + __tr_no_lookup("Connection reset by peer"), + __tr_no_lookup("Host unreachable (no route to host)"), + __tr_no_lookup("Variable expected"), + __tr_no_lookup("Invalid array index: positive integer expected"), + __tr_no_lookup("listen() call failed"), + __tr_no_lookup("This executable has been compiled without SSL support"), + __tr_no_lookup("Secure Socket Layer error"), + __tr_no_lookup("Slash (/) character expected"), + __tr_no_lookup("Unknown string manipulation operation"), + __tr_no_lookup("Operation aborted"), + __tr_no_lookup("Unexpected token"), + __tr_no_lookup("Scope object already defined (unexpected @)"), + __tr_no_lookup("There is no $this pointer in this scope (unexpected @)") +}; + +namespace KviError +{ + const char * getUntranslatedDescription(int iErrorCode) + { + if((iErrorCode < KVI_NUM_ERRORS) && (iErrorCode >= 0)) + return g_errorTable[iErrorCode]; +#ifdef HAVE_STRERROR + if(iErrorCode < 0)return strerror(-iErrorCode); +#endif + return g_errorTable[KviError_unknownError]; + } + + QString getDescription(int iErrorCode) + { + return __tr2qs_no_xgettext(getUntranslatedDescription(iErrorCode)); + } + + int translateSystemError(int iErrNo) + { +#ifdef COMPILE_ON_WINDOWS + switch(iErrNo) + { + case EBADF: return KviError_badFileDescriptor; break; + case WSAEINVAL: + case WSAEFAULT: + case EFAULT: return KviError_outOfAddressSpace; break; + case WSAECONNREFUSED: return KviError_connectionRefused; break; + case WSAENOTSOCK: return KviError_kernelNetworkingPanic; break; + case WSAETIMEDOUT: return KviError_connectionTimedOut; break; + case WSAENETUNREACH: return KviError_networkUnreachable; break; + case EPIPE: return KviError_brokenPipe; break; + case WSAENOTCONN: return KviError_socketNotConnected; break; + + case WSAEACCES: return KviError_accessDenied; break; + case WSAEADDRINUSE: return KviError_addressAlreadyInUse; break; + case WSAEADDRNOTAVAIL: return KviError_cantAssignRequestedAddress; break; + case WSAEAFNOSUPPORT: return KviError_unsupportedAddressFamily; break; + case WSAECONNRESET: return KviError_connectionResetByPeer; break; + case WSAEHOSTUNREACH: return KviError_hostUnreachable; break; + + //case ENOBUFS: return KviError_insufficientResources; break; + // Unhandled error...pass errno to the strerror function + default: return -iErrNo; break; + } +#else + switch(iErrNo) + { + case EBADF: return KviError_badFileDescriptor; break; + case EFAULT: return KviError_outOfAddressSpace; break; + case ECONNREFUSED: return KviError_connectionRefused; break; + case ENOTSOCK: return KviError_kernelNetworkingPanic; break; + case ETIMEDOUT: return KviError_connectionTimedOut; break; + case ENETUNREACH: return KviError_networkUnreachable; break; + case EPIPE: return KviError_brokenPipe; break; + case ENOTCONN: return KviError_socketNotConnected; break; + case ENOBUFS: return KviError_insufficientResources; break; + case EHOSTUNREACH: return KviError_hostUnreachable; break; + // Unhandled error...pass errno to the strerror function + default: return -iErrNo; break; + } +#endif + } +}; + diff --git a/src/kvilib/core/kvi_error.h b/src/kvilib/core/kvi_error.h new file mode 100644 index 00000000..7ab55e88 --- /dev/null +++ b/src/kvilib/core/kvi_error.h @@ -0,0 +1,188 @@ +#ifndef _KVI_ERROR_H_ +#define _KVI_ERROR_H_ +//============================================================================= +// +// File : kvi_error.h +// Creation date : Sun Jul 02 2000 18:35:56 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#include "kvi_settings.h" +#include "kvi_qstring.h" + +#define KviError_success 0 +#define KviError_unknownError 1 +#define KviError_internalError 2 +#define KviError_unknownCommand 3 +#define KviError_missingClosingBrace 4 +#define KviError_unexpectedEndInString 5 +#define KviError_unexpectedEndInDictionaryKey 6 +#define KviError_switchDashWithoutSwitchLetter 7 +#define KviError_unknownFunction 8 +#define KviError_unexpectedEndInParenthesis 9 +#define KviError_unexpectedEndInFunctionParams 10 +#define KviError_missingVariableName 11 +#define KviError_variableOrIdentifierExpected 12 +#define KviError_leftOperandIsNotANumber 13 +#define KviError_multipleOpsNotSupportedForOperator 14 +#define KviError_divisionByZero 15 +#define KviError_moduloByZero 16 +#define KviError_rightOperandIsNotANumber 17 +#define KviError_unterminatedExpression 18 +#define KviError_unterminatedSubexpression 19 +#define KviError_unexpectedCharacter 20 +#define KviError_unknownOperator 21 + +#define KviError_noHostToResolve 22 +#define KviError_unsupportedAddressFamily 23 +#define KviError_validNameButNoIpAddress 24 +#define KviError_unrecoverableNameserverError 25 +#define KviError_dnsTemporaneousFault 26 +#define KviError_dnsInternalErrorBadFlags 27 +#define KviError_dnsInternalErrorOutOfMemory 28 +#define KviError_dnsInternalErrorServiceNotSupported 29 +#define KviError_dnsNoName 30 +#define KviError_dnsInternalErrorUnsupportedSocketType 31 +#define KviError_dnsQueryFailed 32 +#define KviError_noIpV6Support 33 +#define KviError_hostNotFound 34 +#define KviError_dnsInternalIPCFailure 35 + +#define KviError_anotherConnectionInProgress 36 +#define KviError_invalidIpAddress 37 +#define KviError_socketCreationFailed 38 +#define KviError_asyncSocketFailed 39 +#define KviError_badFileDescriptor 40 +#define KviError_outOfAddressSpace 41 +#define KviError_connectionRefused 42 +#define KviError_kernelNetworkingPanic 43 +#define KviError_connectionTimedOut 44 +#define KviError_networkUnreachable 45 +#define KviError_brokenPipe 46 +#define KviError_invalidProxyAddress 47 +#define KviError_remoteEndClosedConnection 48 + +#define KviError_invalidIrcContextId 49 +#define KviError_errorInLoadingModule 50 +#define KviError_noSuchModuleCommand 51 +#define KviError_noSuchModuleFunction 52 + +#define KviError_leftOperandIsNotADictionaryReference 53 +#define KviError_rightOperandIsNotADictionaryReference 54 + +#define KviError_missingObjectClassName 55 +#define KviError_noSuchObjectClass 56 +#define KviError_noSuchObject 57 +#define KviError_noSuchObjectFunction 58 + +#define KviError_invalidLeftOperand 59 + +#define KviError_notEnoughParameters 60 +#define KviError_integerParameterExpected 61 +#define KviError_invalidParameter 62 + +#define KviError_noSuchFile 63 + +#define KviError_openParenthesisExpected 64 +#define KviError_openBraceExpected 65 + +#define KviError_cantKillABuiltinClass 66 +#define KviError_socksV4LacksIpV6Support 67 +#define KviError_unrecognizedProxyReply 68 +#define KviError_proxyAuthFailed 69 +#define KviError_proxyNoAcceptableAuthMethod 70 + +#define KviError_proxyReply91RequestFailed 71 +#define KviError_proxyReply92IdentFailed 72 +#define KviError_proxyReply93IdentNotMatching 73 +#define KviError_proxyReply01GeneralSOCKSFailure 74 +#define KviError_proxyReply02ConnectionNotAllowed 75 +#define KviError_proxyReply03NetworkUnreachable 76 +#define KviError_proxyReply04HostUnreachable 77 +#define KviError_proxyReply05ConnectionRefused 78 +#define KviError_proxyReply06TTLExpired 79 +#define KviError_proxyReply07CommandNotSupported 80 +#define KviError_proxyReply08AddressTypeNotSupported 81 +#define KviError_proxyReply09InvalidAddress 82 + +#define KviError_invalidPortNumber 83 +#define KviError_socketNotConnected 84 +#define KviError_insufficientResources 85 +#define KviError_bindFailed 86 +#define KviError_cantResolveLocalhost 87 + +#define KviError_unsupportedImageFormat 88 + +#define KviError_cantOpenFileForAppending 89 +#define KviError_cantOpenFileForWriting 90 +#define KviError_fileIOError 91 +#define KviError_acknowledgeError 92 +#define KviError_cantOpenFileForReading 93 +#define KviError_cantSendAZeroSizeFile 94 + +#define KviError_missingPopupName 95 +#define KviError_itemPopupOrSeparatorExpected 96 +#define KviError_selfModificationNotAllowed 97 + +//#define KviError_recursionTooDeep 98 +#define KviError_featureNotAvailable 99 + +#define KviError_unexpectedCharactersInArrayIndex 100 +#define KviError_unexpectedEndInExpression 101 +#define KviError_unexpectedEndInArrayIndex 102 + +#define KviError_proxyHttpFailure 103 +#define KviError_caseMatchRegexpDefaultOrBreakExpected 104 + + +#define KviError_accessDenied 105 +#define KviError_addressAlreadyInUse 106 +#define KviError_cantAssignRequestedAddress 107 +#define KviError_connectionResetByPeer 108 +#define KviError_hostUnreachable 109 + +#define KviError_variableExpected 110 +#define KviError_invalidArrayIndex 111 + +#define KviError_listenFailed 112 + +#define KviError_noSSLSupport 113 +#define KviError_SSLError 114 + +#define KviError_slashCharacterExpected 115 +#define KviError_unknownStringManipulationOperator 116 + +#define KviError_operationAborted 117 + +#define KviError_unexpectedToken 118 + +#define KviError_scopeObjectAlreadyDefined 119 +#define KviError_noThisObject 120 + +#define KVI_NUM_ERRORS 121 + +namespace KviError +{ + KVILIB_API QString getDescription(int iErrorCode); + KVILIB_API const char * getUntranslatedDescription(int iErrorCode); + KVILIB_API int translateSystemError(int iErrNo); +}; + +#endif //_KVI_ERROR_H_ diff --git a/src/kvilib/core/kvi_heapobject.cpp b/src/kvilib/core/kvi_heapobject.cpp new file mode 100644 index 00000000..7568086d --- /dev/null +++ b/src/kvilib/core/kvi_heapobject.cpp @@ -0,0 +1,96 @@ +//============================================================================= +// +// File : kvi_heapobject.cpp +// Created on Wed 24 Mar 2004 04:45:17 by Szymon Stefanek +// +// This file is part of the KVIrc IRC client distribution +// Copyright (C) 2004-2006 Szymon Stefanek <pragma at kvirc dot net> +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#define __KVILIB__ + + +#include "kvi_heapobject.h" +#include "kvi_malloc.h" + +// On windows we need to override new and delete operators +// to ensure that always the right new/delete pair is called for an object instance +// This bug jumps out because windows uses a local heap for each +// executable module (exe or dll). +// (this is a well known bug described in Q122675 of MSDN) + +// on Linux it is not needed: there is a single global heap + + + +// 05.02.2005 : scalar/vector deleting destructors in modules +// +// There are also other issues involving the MSVC compiler. +// When the operator new is called on an object with a virtual +// destructor the compiler generates a helper function +// called "vector deleting destructor" that is used to both +// free the object's memory and call the object's destructor. +// (In fact there is also a "scalar deleting destructor" but +// MSVC seems to call the vector version also for scalar deletes ?!?) +// The problem arises when operator new is called in a module: +// the helper function gets stuffed in one of the module's sections +// and when the module is unloaded any attempt to delete +// the object will simply jump into no man's land. + +// An "unhandled exception" in a "call [%eax]" corresponding +// to a delete <pointer> may be a symptom of this problem. + +// I haven't been able to find a solution nicer than having +// a static allocation function in each class that can be +// created from inside a module and destroyed anywhere else +// and has a virtual destructor. + +#ifdef COMPILE_ON_WINDOWS + void * KviHeapObject::operator new(size_t uSize) + { + return kvi_malloc(uSize); + } + + void KviHeapObject::operator delete(void * pData) + { + kvi_free(pData); + } + + void * KviHeapObject::operator new[](size_t uSize) + { + return kvi_malloc(uSize); + } + + void KviHeapObject::operator delete[](void * pData) + { + kvi_free(pData); + } + + // these are the debug versions... + void * KviHeapObject::operator new(size_t uSize,const char *,int) + { + return kvi_malloc(uSize); + } + + void KviHeapObject::operator delete(void * pData,const char *,int) + { + kvi_free(pData); + } +#endif + + diff --git a/src/kvilib/core/kvi_heapobject.h b/src/kvilib/core/kvi_heapobject.h new file mode 100644 index 00000000..3d1638cf --- /dev/null +++ b/src/kvilib/core/kvi_heapobject.h @@ -0,0 +1,50 @@ +#ifndef _KVI_HEAPOBJECT_H_ +#define _KVI_HEAPOBJECT_H_ +//============================================================================= +// +// File : kvi_heapobject.h +// Created on Wed 24 Mar 2004 04:45:17 by Szymon Stefanek +// +// This file is part of the KVIrc IRC client distribution +// Copyright (C) 2004-2006 Szymon Stefanek <pragma at kvirc dot net> +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#include "kvi_settings.h" + +// See kvi_heapobject.cpp for comments on this class + +#ifdef COMPILE_ON_WINDOWS + + class KVILIB_API KviHeapObject + { + public: + void * operator new(size_t uSize); + void operator delete(void * pData); + void * operator new[](size_t uSize); + void operator delete[](void * pData); + void * operator new(size_t uSize,const char *,int); + void operator delete(void * pData,const char *,int); + }; +#else //!COMPILE_ON_WINDOWS + class KVILIB_API KviHeapObject + { + // on other platforms this crap is not necessary + }; +#endif //!COMPILE_ON_WINDOWS + +#endif //!_KVI_HEAPOBJECT_H_ diff --git a/src/kvilib/core/kvi_inttypes.h b/src/kvilib/core/kvi_inttypes.h new file mode 100644 index 00000000..6405ee79 --- /dev/null +++ b/src/kvilib/core/kvi_inttypes.h @@ -0,0 +1,95 @@ +#ifndef _KVI_INTTYPES_H_ +#define _KVI_INTTYPES_H_ +//============================================================================= +// +// File : kvi_inttypes.h +// Creation date : Wed Sep 4 22:28:00 2002 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#include "kvi_settings.h" + +#ifdef COMPILE_ON_WINDOWS + // we don't have a configure script here + // so we can't check the size of types + // We rely on the ms specific definitions then + typedef __int64 kvi_i64_t; + typedef unsigned __int64 kvi_u64_t; + typedef int kvi_i32_t; + typedef unsigned int kvi_u32_t; + typedef short int kvi_i16_t; + typedef short unsigned int kvi_u16_t; + typedef char kvi_i8_t; + typedef unsigned char kvi_u8_t; +#else + #if SIZEOF_LONG_INT == 8 + // the most common case on 64 bit machines + typedef long int kvi_i64_t; + typedef unsigned long int kvi_u64_t; + #elif SIZEOF_INT == 8 + // 64 bit ints ?.. a Cray ? :D + typedef int kvi_i64_t; + typedef unsigned int kvi_u64_t; + #elif SIZEOF_LONG_LONG_INT == 8 + // the most common case on 32 bit machines + typedef long long int kvi_i64_t; + typedef unsigned long long int kvi_u64_t; + #else + // attempt to live without a 64bit integer type anyway... + // dunno if it will work tough... + typedef long long int kvi_i64_t; + typedef unsigned long long int kvi_u64_t; + #endif + + #if SIZEOF_INT == 4 + // the most common case + typedef int kvi_i32_t; + typedef unsigned int kvi_u32_t; + #elif SIZEOF_SHORT_INT == 4 + // 32 bit shorts ?.. a Cray ? :D + typedef short int kvi_i32_t; + typedef short unsigned int kvi_u32_t; + #elif SIZEOF_LONG_INT == 4 + typedef long int kvi_i32_t; + typedef unsigned long int kvi_u32_t; + #else + #error "Can't find a 32 bit integral type on this system" + #error "Please report to pragma at kvirc dot net" + #endif + + #if SIZEOF_SHORT_INT == 2 + // the most common case + typedef short int kvi_i16_t; + typedef short unsigned int kvi_u16_t; + #elif SIZEOF_INT == 2 + // this isn't going to work anyway, I think.. + typedef int kvi_i16_t; + typedef long int kvi_u16_t; + #else + #error "Can't find a 16 bit integral type on this system" + #error "Please report to pragma at kvirc dot net" + #endif + + // assume that char is always 8 bit + typedef char kvi_i8_t; + typedef unsigned char kvi_u8_t; +#endif + +#endif //_KVI_INTTYPES_H_ diff --git a/src/kvilib/core/kvi_malloc.cpp b/src/kvilib/core/kvi_malloc.cpp new file mode 100644 index 00000000..9c418ec5 --- /dev/null +++ b/src/kvilib/core/kvi_malloc.cpp @@ -0,0 +1,198 @@ +//============================================================================= +// +// File : kvi_malloc.cpp +// Creation date : Sun Jun 18 2000 18:26:27 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +//============================================================================= +// C memory allocation routines +// This stuff is rather unused, because in normal compilations +// kvi_malloc , kvi_free and kvi_realloc are macros (see kvi_malloc.h) +//============================================================================= + +#define __KVILIB__ + +#define _KVI_MALLOC_CPP_ +#include "kvi_malloc.h" + +#include <stdio.h> + + + +#ifdef COMPILE_MEMORY_PROFILE + + // + // Memory profile stuff + // Used to find memory leaks etc... + // + + #include "kvi_pointerlist.h" + + typedef struct _KviMallocEntry { + struct _KviMallocEntry * prev; + void * pointer; + int size; + void * return_addr1; + void * return_addr2; + struct _KviMallocEntry * next; + } KviMallocEntry; + + int g_iMaxRequestSize = 0; + void * g_pMaxRequestReturnAddress1 = 0; + void * g_pMaxRequestReturnAddress2 = 0; + unsigned int g_iMallocCalls = 0; + unsigned int g_iReallocCalls = 0; + unsigned int g_iFreeCalls = 0; + unsigned int g_iTotalMemAllocated = 0; + unsigned int g_uAllocationPeak = 0; + KviMallocEntry * g_pEntries = 0; + + void * kvi_malloc(int size) + { + g_iMallocCalls ++; + g_iTotalMemAllocated += size; + if(g_iTotalMemAllocated > g_uAllocationPeak)g_uAllocationPeak = g_iTotalMemAllocated; + if(g_iMaxRequestSize < size){ + g_iMaxRequestSize = size; + g_pMaxRequestReturnAddress1 = __builtin_return_address(1); + g_pMaxRequestReturnAddress2 = __builtin_return_address(2); + } + KviMallocEntry * e = (KviMallocEntry *)malloc(sizeof(KviMallocEntry)); + e->pointer = malloc(size); + e->size = size; + e->return_addr1 = __builtin_return_address(1); + e->return_addr2 = __builtin_return_address(2); + e->next = g_pEntries; + e->prev = 0; + if(g_pEntries)g_pEntries->prev = e; + g_pEntries = e; + return e->pointer; + } + + void * kvi_realloc(void * ptr,int size) + { + g_iReallocCalls ++; + if(ptr == 0)return kvi_malloc(size); + if(g_iMaxRequestSize < size){ + g_iMaxRequestSize = size; + g_pMaxRequestReturnAddress1 = __builtin_return_address(1); + g_pMaxRequestReturnAddress2 = __builtin_return_address(2); + } + KviMallocEntry *e = g_pEntries; + while(e){ + if(e->pointer == ptr){ + g_iTotalMemAllocated -= e->size; + g_iTotalMemAllocated += size; + if(g_iTotalMemAllocated > g_uAllocationPeak)g_uAllocationPeak = g_iTotalMemAllocated; + e->pointer = realloc(ptr,size); + e->size = size; + e->return_addr1 = __builtin_return_address(1); + e->return_addr2 = __builtin_return_address(2); + return e->pointer; + } + e = e->next; + } + fprintf(stderr,"Attempt to realloc an inexisting pointer (%p) (called from %p (%p))\n",ptr,__builtin_return_address(1),__builtin_return_address(2)); + return realloc(ptr,size); + } + + void kvi_free(void * ptr) + { + g_iFreeCalls++; + if(ptr == 0){ + fprintf(stderr,"Attempt to free a null pointer (called from %p (%p))\n",__builtin_return_address(1),__builtin_return_address(2)); + exit(-1); + } + KviMallocEntry * e= g_pEntries; + while(e){ + if(e->pointer == ptr){ + g_iTotalMemAllocated -= e->size; + if(e->prev){ + if(e == g_pEntries)fprintf(stderr,"Mem profiling internal error!\n"); + e->prev->next = e->next; + if(e->next)e->next->prev = e->prev; + } else { + if(e != g_pEntries)fprintf(stderr,"Mem profiling internal error!\n"); + if(e->next)e->next->prev = 0; + g_pEntries = e->next; + } + free(e); + return; + } + e = e->next; + } + fprintf(stderr,"Attempt to free an inexisting pointer (%p) (called from %p (%p))\n",ptr,__builtin_return_address(1),__builtin_return_address(2)); + } + + void kvi_memory_profile() __attribute__((destructor)); + void kvi_memory_profile() + { + unsigned int countUnfreed = 0; + KviMallocEntry * e = g_pEntries; + while(e){ + countUnfreed++; + e = e->next; + } + fprintf(stderr,"|====|====|====|====|====|====|====|====\n"); + fprintf(stderr,"| Memory profile for KVIrc\n"); + fprintf(stderr,"| Unfreed chunks : %d\n",countUnfreed); + fprintf(stderr,"| Total unfreed memory : %u bytes\n",g_iTotalMemAllocated); + fprintf(stderr,"|====|====|====|====|====|====|====|====\n"); + fprintf(stderr,"| Possible unfreed chunks dump:\n"); + e = g_pEntries; + while(e){ + fprintf(stderr,"|====|====|\n"); + fprintf(stderr,"| Currently unfreed chunk: %p\n",e->pointer); + fprintf(stderr,"| Size: %d\n",e->size); + fprintf(stderr,"| Caller address 1: %p\n",e->return_addr1); + fprintf(stderr,"| Caller address 2: %p\n",e->return_addr2); + if(e->size > 10)fprintf(stderr,"| Data: %.10s\n",e->pointer); + else if(e->size > 5)fprintf(stderr,"| Data: %.5s\n",e->pointer); + KviMallocEntry *toFree = e; + e = e->next; + free(toFree); + } + fprintf(stderr,"|====|====|====|====|====|====|====|====\n"); + fprintf(stderr,"| Allocation peak : %u bytes\n",g_uAllocationPeak); + fprintf(stderr,"|====|====|====|====|====|====|====|====\n"); + fprintf(stderr,"| Max request size : %d bytes\n",g_iMaxRequestSize); + fprintf(stderr,"| Called from %p (%p)\n",g_pMaxRequestReturnAddress1,g_pMaxRequestReturnAddress2); + fprintf(stderr,"|====|====|====|====|====|====|====|====\n"); + fprintf(stderr,"| Malloc calls: %u\n",g_iMallocCalls); + fprintf(stderr,"| Realloc calls: %u\n",g_iReallocCalls); + fprintf(stderr,"| Free calls: %u\n",g_iFreeCalls); + fprintf(stderr,"|====|====|====|====|====|====|====|====\n"); + } + +#else + + #ifdef COMPILE_MEMORY_CHECKS + + void outOfMemory() + { + //What a cool message :) + fprintf(stderr,"Virtual memory exhausted in malloc call....bye!\n"); + exit(-1); + } + + #endif + +#endif diff --git a/src/kvilib/core/kvi_malloc.h b/src/kvilib/core/kvi_malloc.h new file mode 100644 index 00000000..8a7204a5 --- /dev/null +++ b/src/kvilib/core/kvi_malloc.h @@ -0,0 +1,88 @@ +#ifndef _KVI_MALLOC_H_ +#define _KVI_MALLOC_H_ + +//============================================================================= +// +// File : kvi_malloc.h +// Creation date : Sun Jun 18 2000 18:18:36 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +//============================================================================= +// C memory allocation routines: macros in common compilations +//============================================================================= + +#include "kvi_settings.h" + +#include <stdlib.h> + +#ifdef COMPILE_MEMORY_PROFILE + + #ifdef COMPILE_ON_WINDOWS + #error "This stuff should be never compiled on Windows" + #endif + + extern void * kvi_malloc(int size); + extern void * kvi_realloc(void * ptr,int size); + extern void kvi_free(void * ptr); + +#else + + #ifndef COMPILE_MEMORY_CHECKS + + // These two are the "common" ones + #define kvi_malloc(__size_) malloc(__size_) + #define kvi_realloc(__ptr_,__size_) realloc((void *)__ptr_,__size_) + + #else + + #ifdef COMPILE_ON_WINDOWS + #error "This stuff should be never compiled on Windows" + #endif + + // Want to check all the pointers + #define kvi_malloc(__size_) kvi_safe_malloc(__size_) + #define kvi_realloc(__ptr_,__size_) kvi_safe_realloc((void *)__ptr_,__size_) + + #ifndef _KVI_MALLOC_CPP_ + extern void outOfMemory(); + #endif + + inline void * kvi_safe_malloc(int size) + { + void * ptr = malloc(size); + if(!ptr)outOfMemory(); + return ptr; + } + + inline void * kvi_safe_realloc(void * ptr,int size) + { + ptr = realloc(ptr,size); + if(!ptr)outOfMemory(); + return ptr; + } + + #endif //COMPILE_MEMORY_CHECKS + + #define kvi_free(__ptr_) free((void *)__ptr_) + +#endif + +#endif //_KVI_MALLOC_H_ diff --git a/src/kvilib/core/kvi_memmove.cpp b/src/kvilib/core/kvi_memmove.cpp new file mode 100644 index 00000000..1beb920a --- /dev/null +++ b/src/kvilib/core/kvi_memmove.cpp @@ -0,0 +1,253 @@ +//============================================================================= +// +// File : kvi_memmove.cpp +// Creation date : Sun Jun 18 2000 18:27:50 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#define __KVILIB__ + +#define _KVI_DEBUG_CHECK_RANGE_ +#include "kvi_debug.h" + +#define _KVI_MEMMOVE_CPP_ +#include "kvi_memmove.h" + +// FIXME: #warning "With system memmove could be guessed by configure" + +#ifndef COMPILE_WITH_SYSTEM_MEMMOVE + + #ifdef COMPILE_ix86_ASM + + + void *kvi_memmove(void * dst_ptr,const void *src_ptr,int len) + { + __range_valid(dst_ptr); + __range_valid(src_ptr); + __range_valid(len >= 0); + // Save pointer registers + asm(" pushl %esi"); // save %esi + asm(" pushl %edi"); // save %edi + // Load arguments + asm(" movl 16(%ebp),%ecx"); // %ecx = len + asm(" movl 12(%ebp),%esi"); // %esi = src + asm(" movl 8(%ebp),%edi"); // %edi = dst + // Compare src and dest + asm(" cmpl %esi,%edi"); // %edi - %esi + asm(" jbe move_from_bottom_to_top"); // if(%edi < %esi) jump to move_from_bottom_to_top + // dst_ptr > src_ptr + asm(" addl %ecx,%esi"); // %esi += %ecx (src_ptr += len); + asm(" addl %ecx,%edi"); // %edi += %ecx (dst_ptr += len); + asm(" decl %esi"); // %esi--; (src_ptr--); + asm(" decl %edi"); // %edi--; (dst_ptr--); + asm(" std"); // set direction flag (decrement esi and edi in movsb) + // Optimization : check for non-odd len (1,3,5,7...) + asm(" shr $1,%ecx"); // %ecx >> 1 , shifted bit -> CF + asm(" jnc move_two_bytes_top_to_bottom_directly"); // if !carry (CF == 0) skip this move + // Move the first byte (non-odd) + asm(" movsb %ds:(%esi),%es:(%edi)"); // *dst-- = *src-- if DF else *dst++ = *src++ + asm("move_two_bytes_top_to_bottom_directly:"); + asm(" decl %esi"); // %esi--; (src_ptr--); + asm(" decl %edi"); // %edi--; (dst_ptr--); + asm("move_two_bytes_top_to_bottom:"); + asm(" shr $1,%ecx"); // %ecx >> 1 , shifted bit -> CF + asm(" jnc move_the_rest_top_to_bottom_directly"); // if !carry (CF == 0) skip this move + // Move the next two bytes + asm(" movsw %ds:(%esi),%es:(%edi)"); // *((word *)dst)-- = *((word)src)-- if DF else *((word *)dst)++ = *((word)src)++ + asm("move_the_rest_top_to_bottom_directly:"); + asm(" subl $2,%esi"); // %esi-=2; (src-=2); + asm(" subl $2,%edi"); // %edi-=2; (dst-=2); + asm(" jmp move_the_rest"); // call last repnz movsl + // dst_ptr <= src_ptr + asm("move_from_bottom_to_top:"); + asm(" cld"); // clear direction flag (increment esi and edi in movsb) + // Optimization : check for non-odd len (1,3,5,7...) + asm(" shr $1,%ecx"); // %ecx >> 1 , shifted bit -> CF + asm(" jnc move_two_bytes"); // if !carry (CF == 0) skip this move + // Move the first byte (non-odd) + asm(" movsb %ds:(%esi),%es:(%edi)"); // *dst-- = *src-- if DF else *dst++ = *src++ + // Optimization : pass 2 , check for %2 and %3 + asm("move_two_bytes:"); + asm(" shr $1,%ecx"); // %ecx >> 1 , shifted bit -> CF + asm(" jnc move_the_rest"); // if !carry (CF == 0) skip this move + // Move the next two bytes + asm(" movsw %ds:(%esi),%es:(%edi)"); // *((word *)dst)-- = *((word)src)-- if DF else *((word *)dst)++ = *((word)src)++ + // Main move remaining part + asm("move_the_rest:"); + asm(" repnz; movsl %ds:(%esi),%es:(%edi)"); // loop moving 4 bytes at once (increment or decrement as above) + // Restore pointer registers + asm(" popl %edi"); // restore %edi + asm(" popl %esi"); // restore %esi + return dst_ptr; //asm(" movl 8(%ebp),%eax"); <-- gcc will put that (AFTER THE OPTIMISATION PASS!) + } + + void *kvi_memmoveodd(void * dst_ptr,const void *src_ptr,int len) + { + __range_valid(dst_ptr); + __range_valid(src_ptr); + __range_valid(len >= 0); + // Save pointer registers + asm(" pushl %esi"); // save %esi + asm(" pushl %edi"); // save %edi + // Load arguments + asm(" movl 16(%ebp),%ecx"); // %ecx = len + asm(" movl 12(%ebp),%esi"); // %esi = src + asm(" movl 8(%ebp),%edi"); // %edi = dst + // Compare src and dest + asm(" cmpl %esi,%edi"); // %edi - %esi + asm(" jbe xmove_from_bottom_to_top"); // if(%edi < %esi) jump to move_from_bottom_to_top + // dst_ptr > src_ptr + asm(" addl %ecx,%esi"); // %esi += %ecx (src_ptr += len); + asm(" addl %ecx,%edi"); // %edi += %ecx (dst_ptr += len); + asm(" std"); // set direction flag (decrement esi and edi in movsb) + // start moving + asm(" shr $2,%ecx"); // %ecx >> 2 , last shifted bit -> CF + asm(" jnc xmove_the_rest_top_to_bottom_directly"); // if !carry (CF == 0) skip this move + // Move the next two bytes + asm(" subl $2,%esi"); // %esi-=2; (src_ptr-=2); + asm(" subl $2,%edi"); // %edi-=2; (dst_ptr-=2); + asm(" movsw %ds:(%esi),%es:(%edi)"); // *((word *)dst)-- = *((word)src)-- if DF else *((word *)dst)++ = *((word)src)++ + asm(" subl $2,%esi"); // %esi-=2; (src_ptr-=2); + asm(" subl $2,%edi"); // %edi-=2; (dst_ptr-=2); + asm(" jmp xmove_the_rest"); + asm("xmove_the_rest_top_to_bottom_directly:"); + asm(" subl $4,%esi"); // %esi-=4; (src-=4); + asm(" subl $4,%edi"); // %edi-=4; (dst-=4); + asm(" jmp xmove_the_rest"); // call last repnz movsl + // dst_ptr <= src_ptr + asm("xmove_from_bottom_to_top:"); + asm(" cld"); // clear direction flag (increment esi and edi in movsb) + // move it + asm(" shr $2,%ecx"); // %ecx >> 2 , last shifted bit -> CF + asm(" jnc xmove_the_rest"); // if !carry (CF == 0) skip this move + // Move the next two bytes + asm(" movsw %ds:(%esi),%es:(%edi)"); // *((word *)dst)-- = *((word)src)-- if DF else *((word *)dst)++ = *((word)src)++ + // Main move remaining part + asm("xmove_the_rest:"); + asm(" repnz; movsl %ds:(%esi),%es:(%edi)"); // loop moving 4 bytes at once (increment or decrement as above) + // Restore pointer registers + asm(" popl %edi"); // restore %edi + asm(" popl %esi"); // restore %esi + return dst_ptr; //asm(" movl 8(%ebp),%eax"); <-- gcc will put that (AFTER THE OPTIMISATION PASS!) + } + + #else // ndef COMPILE_ix86_ASM + + + + // The next 4 functions could be optimized with the & and shift technique + // used in the assembly implementations but the compilers usually + // will not translate the carry bit trick producing code + // that works slower on short block of memory (really near the average case) + + // The trick would be: + // + // if(len & 1) // the length is even + // *dst-- = *src--; // move one byte + // len >> 1; // drop the last bit (thus divide by 2) + // if(len & 1) // the length is still even + // *((short *)dst)-- = *((short *)src)--; // move two bytes + // len >> 1; // again drop the last bit (thus divide by 2) + // while(len--)*((int *)dst)-- = *((int *)src)--; // move four bytes at a time + // + // + + void *kvi_memmove(void *dst_ptr,const void *src_ptr,int len) + { + __range_valid(dst_ptr); + __range_valid(src_ptr); + __range_valid(len >= 0); + register char *dst; + register const char *src; + if(dst_ptr > src_ptr){ + dst = (char *)dst_ptr + len - 1; + src = (const char *)src_ptr + len - 1; + while(len--)*dst-- = *src--; + } else { //it is valid even if dst_ptr == src_ptr + dst = (char *)dst_ptr; + src = (const char *)src_ptr; + while(len--)*dst++ = *src++; + } + return dst_ptr; + } + + void *kvi_memmoveodd(void *dst_ptr,const void *src_ptr,int len) + { + __range_valid(dst_ptr); + __range_valid(src_ptr); + __range_valid(len >= 0); + __range_valid((len & 1) == 0); + register short *dst; + register const short *src; + if(dst_ptr > src_ptr){ + dst = (short *) (((char *)dst_ptr) + len - 2); + src = (const short *) (((const char *)src_ptr) + len - 2); + while(len > 0) + { + *dst-- = *src--; + len -= 2; + } + } else { //it is valid even if dst_ptr == src_ptr + dst = (short *)dst_ptr; + src = (const short *)src_ptr; + while(len > 0) + { + *dst++ = *src++; + len -= 2; + } + } + return dst_ptr; + } + + void kvi_fastmove(void *dst_ptr,const void *src_ptr,int len) + { + __range_valid(dst_ptr); + __range_valid(src_ptr); + __range_valid(len >= 0); + register const char *src = (const char *)src_ptr; + register char *dst = (char *)dst_ptr; + while(len--)*dst++ = *src++; + } + + void kvi_fastmoveodd(void *dst_ptr,const void *src_ptr,int len) + { + __range_valid(dst_ptr); + __range_valid(src_ptr); + __range_valid(len >= 0); + __range_valid((len & 1) == 0); + register const short *src = (const short *)src_ptr; + register short *dst = (short *)dst_ptr; + while(len > 0){ + *dst++ = *src++; + len -= 2; + } + } + + #endif // !COMPILE_ix86_ASM + + void kvi_memset(void *dst_ptr,char c,int len) + { + __range_valid(dst_ptr); + __range_valid(len >= 0); + register char *dst = (char *)dst_ptr; + while(len--)*dst++ = c; + } + +#endif // !COMPILE_WITH_SYSTEM_MEMMOVE diff --git a/src/kvilib/core/kvi_memmove.h b/src/kvilib/core/kvi_memmove.h new file mode 100644 index 00000000..d1319a41 --- /dev/null +++ b/src/kvilib/core/kvi_memmove.h @@ -0,0 +1,105 @@ +#ifndef _KVI_MEMMOVE_H_ +#define _KVI_MEMMOVE_H_ + +//============================================================================= +// +// File : kvi_memmove.h +// Creation date : Fri Mar 19 1999 03:15:21 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#define __KVILIB__ + +#include "kvi_settings.h" + +//#undef COMPILE_WITH_SYSTEM_MEMMOVE +//#define COMPILE_MMX_ASM + +#ifndef _KVI_MEMMOVE_CPP_ + + #ifdef COMPILE_WITH_SYSTEM_MEMMOVE + + #include <string.h> + + #define kvi_memmove memmove + #define kvi_memmoveodd memmove + #define kvi_memset memset + #define kvi_fastmove memcpy + #define kvi_fastmoveodd memcpy + + #else + + #ifdef COMPILE_ON_WINDOWS + #error "This stuff should be never compiled on Windows" + #endif + + extern void *kvi_memmove(void *dst_ptr,const void *src_ptr,int len); + extern void *kvi_memmoveodd(void *dst_ptr,const void *src_ptr,int len); + extern void *kvi_memset(void *dst_ptr,char c,int len); + // In fastmove the src and dst may not overlap + + #ifdef COMPILE_ix86_ASM + + // WE WANT repnz; movsq\n"!!! + + inline void kvi_fastmove(void * dst_ptr,const void *src_ptr,int len) + { + __asm__ __volatile__( + " cld\n" + " shr $1,%0\n" + " jnc 1f\n" + " movsb\n" + "1:\n" + " shr $1,%0\n" + " jnc 2f\n" + " movsw\n" + "2:\n" + " repnz; movsl\n" + : "=c" (len), "=&S" (src_ptr), "=&D" (dst_ptr) + : "0" (len), "1" (src_ptr), "2" (dst_ptr) + ); + } + + inline void kvi_fastmoveodd(void * dst_ptr,const void *src_ptr,int len) + { + __asm__ __volatile__( + " cld\n" + " shr $2,%0\n" + " jnc 1f\n" + " movsw\n" + "1:\n" + " repnz; movsl\n" + : "=c" (len), "=&S" (src_ptr), "=&D" (dst_ptr) + : "0" (len), "1" (src_ptr), "2" (dst_ptr) + ); + } + + #else // ! COMPILE_ix86_ASM + + extern void kvi_fastmove(void *dst_ptr,const void *src_ptr,int len); + extern void kvi_fastmoveodd(void *dst_ptr,const void *src_ptr,int len); + + #endif // !COMPILE_ix86_ASM + + #endif // COMPILE_WITH_SYSTEM_MEMMOVE + +#endif // _KVI_MEMMOVE_CPP_ + +#endif // !_KVI_MEMMOVE_H_ diff --git a/src/kvilib/core/kvi_pointerhashtable.h b/src/kvilib/core/kvi_pointerhashtable.h new file mode 100644 index 00000000..9066c091 --- /dev/null +++ b/src/kvilib/core/kvi_pointerhashtable.h @@ -0,0 +1,999 @@ +#ifndef _KVI_POINTERHASHTABLE_H_ +#define _KVI_POINTERHASHTABLE_H_ +//================================================================================================= +// +// File : kvi_pointerhashtable.h +// Creation date : Sat Jan 12 2008 04:53 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2008 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//================================================================================================= + +#include "kvi_settings.h" +#include "kvi_pointerlist.h" +#include "kvi_string.h" +#include "kvi_qstring.h" +#include "kvi_malloc.h" +#include "kvi_memmove.h" + +#include <ctype.h> + +/// +/// Hash functions for various data types +/// + +inline unsigned int kvi_hash_hash(const char * szKey,bool bCaseSensitive) +{ + unsigned int uResult = 0; + if(bCaseSensitive) + { + while(*szKey) + { + uResult += (unsigned char)(*(szKey)); + szKey++; + } + } else { + while(*szKey) + { + uResult += (unsigned char)tolower(*(szKey)); + szKey++; + } + } + return uResult; +} + +inline bool kvi_hash_key_equal(const char * szKey1,const char * szKey2,bool bCaseSensitive) +{ + if(bCaseSensitive) + { + while(*szKey1 && *szKey2) + { + if(*szKey1 != *szKey2) + return false; + szKey1++; + szKey2++; + } + } else { + while(*szKey1 && *szKey2) + { + if(tolower(*szKey1) != tolower(*szKey2)) + return false; + szKey1++; + szKey2++; + } + } + return true; +} + +inline void kvi_hash_key_copy(const char * const &szFrom,const char * &szTo,bool bDeepCopy) +{ + if(bDeepCopy) + { + int len = kvi_strLen(szFrom); + char * dst = (char *)kvi_malloc(len+1); + kvi_fastmove(dst,szFrom,len+1); + szTo = dst; + } else { + szTo = szFrom; // we never modify it anyway + } +} + +inline void kvi_hash_key_destroy(const char * &szKey,bool bDeepCopy) +{ + if(bDeepCopy) + kvi_free(szKey); +} + +inline const char * & kvi_hash_key_default(const char **) +{ + static const char * static_null = NULL; + return static_null; +} + +inline unsigned int kvi_hash_hash(const KviStr &szKey,bool bCaseSensitive) +{ + unsigned int uResult = 0; + const char * p = szKey.ptr(); + if(bCaseSensitive) + { + while(*p) + { + uResult += *((const unsigned char *)p); + p++; + } + } else { + while(*p) + { + uResult += tolower(*((const unsigned char *)p)); + p++; + } + } + return uResult; +} + +inline bool kvi_hash_key_equal(const KviStr &szKey1,const KviStr &szKey2) +{ + return kvi_hash_key_equal(szKey1.ptr(),szKey2.ptr()); +} + +inline void kvi_hash_key_copy(const KviStr &szFrom,KviStr &szTo,bool) +{ + szTo = szFrom; +} + +inline void kvi_hash_key_destroy(KviStr &szKey,bool) +{ +} + +inline const KviStr & kvi_hash_key_default(KviStr *) +{ + return KviStr::emptyString(); +} + +inline unsigned int kvi_hash_hash(const int &iKey,bool) +{ + return (unsigned int)iKey; +} + +inline bool kvi_hash_key_equal(const int &iKey1,const int &iKey2,bool) +{ + return iKey1 == iKey2; +} + +inline void kvi_hash_key_copy(const int &iKeyFrom,int &iKeyTo,bool) +{ + iKeyTo = iKeyFrom; +} + +inline void kvi_hash_key_destroy(int &iKey,bool) +{ +} + +inline const int & kvi_hash_key_default(int *) +{ + static int static_default = 0; + return static_default; +} + +inline unsigned int kvi_hash_hash(const unsigned short &iKey,bool) +{ + return (unsigned int)iKey; +} + +inline bool kvi_hash_key_equal(const unsigned short &iKey1,const unsigned short &iKey2,bool) +{ + return iKey1 == iKey2; +} + +inline void kvi_hash_key_copy(const unsigned short &iKeyFrom,unsigned short &iKeyTo,bool) +{ + iKeyTo = iKeyFrom; +} + +inline void kvi_hash_key_destroy(unsigned short &iKey,bool) +{ +} + +inline const unsigned short & kvi_hash_key_default(unsigned short *) +{ + static unsigned short static_default = 0; + return static_default; +} + + +inline unsigned int kvi_hash_hash(void * pKey,bool) +{ + unsigned char * pBytes = (unsigned char *)&(pKey); + unsigned char * pEnd = pBytes + sizeof(void *); + unsigned int uSum = 0; + while(pBytes < pEnd) + { + uSum += *pBytes; + pBytes++; + } + return uSum; +} + +inline bool kvi_hash_key_equal(void *pKey1,void *pKey2,bool) +{ + return pKey1 == pKey2; +} + +inline void kvi_hash_key_copy(void * const &pKeyFrom,void *&pKeyTo,bool) +{ + pKeyTo = pKeyFrom; +} + +inline void kvi_hash_key_destroy(void *iKey,bool) +{ +} + +inline void * & kvi_hash_key_default(void *) +{ + static void * static_default = NULL; + return static_default; +} + +inline unsigned int kvi_hash_hash(const QString &szKey,bool bCaseSensitive) +{ + unsigned int uResult = 0; + const QChar * p = KviQString::nullTerminatedArray(szKey); + if(!p)return 0; + if(bCaseSensitive) + { + while(p->unicode()) + { + uResult += p->unicode(); + p++; + } + } else { + while(p->unicode()) + { +#ifdef COMPILE_USE_QT4 + uResult += p->toLower().unicode(); +#else + uResult += p->lower().unicode(); +#endif + p++; + } + } + return uResult; +} + +inline bool kvi_hash_key_equal(const QString &szKey1,const QString &szKey2,bool bCaseSensitive) +{ + if(bCaseSensitive) + return KviQString::equalCS(szKey1,szKey2); + return KviQString::equalCI(szKey1,szKey2); +} + +inline void kvi_hash_key_copy(const QString &szFrom,QString &szTo,bool) +{ + szTo = szFrom; +} + +inline void kvi_hash_key_destroy(QString &szKey,bool) +{ +} + +inline const QString & kvi_hash_key_default(QString *) +{ + return KviQString::empty; +} + +template<typename Key,typename T> class KviPointerHashTable; +template<typename Key,typename T> class KviPointerHashTableIterator; + +template<typename Key,typename T> class KviPointerHashTableEntry +{ + friend class KviPointerHashTable<Key,T>; +protected: + T * pData; + Key hKey; +public: + Key & key(){ return hKey; }; + T * data(){ return pData; }; +}; + +/// +/// +/// \class KviPointerHashTable +/// \brief A fast pointer hash table implementation +/// +/// A very cool, very fast hash table implementation :P +/// +/// To use this hash table you need to provide implementations +/// for the following functions: +/// +/// \verbatim +/// +/// unsigned int kvi_hash_hash(const Key &hKey,bool bCaseSensitive); +/// bool kvi_hash_key_equal(const Key &hKey1,const Key &hKey2,bool bCaseSensitive); +/// void kvi_hash_key_copy(const Key &hKeyFrom,Key &hKeyTo,bool bDeepCopy); +/// void kvi_hash_key_destroy(Key &hKey,bool bIsDeepCopy); +/// const Key & kvi_hash_key_default(Key *); +/// +/// \endverbatim +/// +/// Implementations for the most likey Key data types are provided below. +/// KviPointerHashTable will automagically work with const char *,QString,KviStr +/// and integer types as keys. +/// +/// For string Key types, the hash table may or may not be case sensitive. +/// For other Key types the case sensitive flag has no meaning and will +/// (hopefully) be optimized out by the compiler. +/// +/// For pointer based keys the hash table may or may not mantain deep copies +/// of Key data. For example, with char * keys, if deep copying is enabled +/// then a private copy of the string data will be mantained. With deep +/// copying disabled only char * pointers will be kept. For types +/// that do not have meaning of deep copy the deep copying code will +/// (hopefully) be optimized out by the compiler. +/// +/// The hashtable mantains an array of KviPointerList based buckets. +/// The number of buckets may be specified by the application user +/// and does NOT need to be a prime number. Yet better to have it a power +/// of two so the memory allocation routines will feel better and are +/// less likely to waste space. +/// +template<class Key,class T> class KviPointerHashTable +{ + friend class KviPointerHashTableIterator<Key,T>; +protected: + KviPointerList<KviPointerHashTableEntry<Key,T> > ** m_pDataArray; + bool m_bAutoDelete; + unsigned int m_uSize; + unsigned int m_uCount; + bool m_bCaseSensitive; + bool m_bDeepCopyKeys; + unsigned int m_uIteratorIdx; +public: + /// + /// Returns the item associated to the key hKey + /// or NULL if no such item exists in the hash table. + /// Places the hash table iterator at the position + /// of the item found. + /// + T * find(const Key & hKey) + { + m_uIteratorIdx = kvi_hash_hash(hKey,m_bCaseSensitive) % m_uSize; + if(!m_pDataArray[m_uIteratorIdx])return 0; + for(KviPointerHashTableEntry<Key,T> * e = m_pDataArray[m_uIteratorIdx]->first();e;e = m_pDataArray[m_uIteratorIdx]->next()) + { + if(kvi_hash_key_equal(e->hKey,hKey,m_bCaseSensitive))return (T *)e->pData; + } + return 0; + } + + /// + /// Returns the item associated to the key hKey + /// or NULL if no such item exists in the hash table. + /// Places the hash table iterator at the position + /// of the item found. This is an alias to find(). + /// + T * operator[](const Key & hKey) + { + return find(hKey); + } + + /// + /// Returns the number of items in this hash table + /// + unsigned int count() const + { + return m_uCount; + } + + /// + /// Returns true if the hash table is empty + /// + bool isEmpty() const + { + return m_uCount == 0; + } + + /// + /// Inserts the item pData at the position specified by the key hKey. + /// Replaces any previous item with the same key + /// The replaced item is deleted if autodelete is enabled. + /// The hash table iterator is placed at the newly inserted item. + /// + void insert(const Key & hKey,T * pData) + { + if(!pData)return; + unsigned int uEntry = kvi_hash_hash(hKey,m_bCaseSensitive) % m_uSize; + if(!m_pDataArray[uEntry])m_pDataArray[uEntry] = new KviPointerList<KviPointerHashTableEntry<Key,T> >(true); + for(KviPointerHashTableEntry<Key,T> * e = m_pDataArray[uEntry]->first();e;e = m_pDataArray[uEntry]->next()) + { + if(kvi_hash_key_equal(e->hKey,hKey,m_bCaseSensitive)) + { + if(!m_bCaseSensitive) + { + // must change the key too + kvi_hash_key_destroy(e->hKey,m_bDeepCopyKeys); + kvi_hash_key_copy(hKey,e->hKey,m_bDeepCopyKeys); + } + if(m_bAutoDelete)delete e->pData; + e->pData = pData; + return; + } + } + KviPointerHashTableEntry<Key,T> * n = new KviPointerHashTableEntry<Key,T>; + kvi_hash_key_copy(hKey,n->hKey,m_bDeepCopyKeys); + n->pData = pData; + m_pDataArray[uEntry]->append(n); + m_uCount++; + } + + /// + /// Inserts the item pData at the position specified by the key hKey. + /// Replaces any previous item with the same key + /// The replaced item is deleted if autodelete is enabled. + /// The hash table iterator is placed at the newly inserted item. + /// This is just an alias to insert() with a different name. + /// + void replace(const Key & hKey,T * pData) + { + insert(hKey,pData); + } + + /// + /// Removes the item pointer associated to the key hKey, if such an item + /// exists in the hash table. The item is deleted if autodeletion + /// is enabled. Returns true if the item was found and removed and false if it wasn't found. + /// Invalidates the hash table iterator. + /// + bool remove(const Key & hKey) + { + unsigned int uEntry = kvi_hash_hash(hKey,m_bCaseSensitive) % m_uSize; + if(!m_pDataArray[uEntry])return false; + for(KviPointerHashTableEntry<Key,T> * e = m_pDataArray[uEntry]->first();e;e = m_pDataArray[uEntry]->next()) + { + if(kvi_hash_key_equal(e->hKey,hKey,m_bCaseSensitive)) + { + kvi_hash_key_destroy(e->hKey,m_bDeepCopyKeys); + if(m_bAutoDelete)delete ((T *)(e->pData)); + m_pDataArray[uEntry]->removeRef(e); + if(m_pDataArray[uEntry]->isEmpty()) + { + delete m_pDataArray[uEntry]; + m_pDataArray[uEntry] = 0; + } + m_uCount--; + return true; + } + } + return false; + } + + /// + /// Removes the first occurence of the item pointer pRef. The item is deleted if autodeletion + /// is enabled. Returns true if the pointer was found and false otherwise + /// Invalidates the hash table iterator. + /// + bool removeRef(const T * pRef) + { + for(unsigned int i=0;i<m_uSize;i++) + { + if(m_pDataArray[i]) + { + for(KviPointerHashTableEntry<Key,T> * e = m_pDataArray[i]->first();e;e = m_pDataArray[i]->next()) + { + if(e->pData == pRef) + { + kvi_hash_key_destroy(e->hKey,m_bDeepCopyKeys); + if(m_bAutoDelete)delete ((T *)(e->pData)); + m_pDataArray[i]->removeRef(e); + if(m_pDataArray[i]->isEmpty()) + { + delete m_pDataArray[i]; + m_pDataArray[i] = 0; + } + m_uCount--; + return true; + } + } + } + } + return false; + } + + /// + /// Removes all the items from the hash table. + /// The items are deleted if autodeletion is enabled. + /// Invalidates the hash table iterator. + /// + void clear() + { + for(unsigned int i=0;i<m_uSize;i++) + { + if(m_pDataArray[i]) + { + for(KviPointerHashTableEntry<Key,T> * e = m_pDataArray[i]->first();e;e = m_pDataArray[i]->next()) + { + kvi_hash_key_destroy(e->hKey,m_bDeepCopyKeys); + if(m_bAutoDelete) + delete ((T *)(e->pData)); + } + delete m_pDataArray[i]; + m_pDataArray[i] = 0; + } + } + m_uCount = 0; + } + + /// + /// Searches for the item pointer pRef and returns + /// it's hash table entry, if found, and NULL otherwise. + /// The hash table iterator is placed at the item found. + /// + KviPointerHashTableEntry<Key,T> * findRef(const T * pRef) + { + for(m_uIteratorIdx = 0;m_uIteratorIdx<m_uSize;m_uIteratorIdx++) + { + if(m_pDataArray[m_uIteratorIdx]) + { + for(KviPointerHashTableEntry<Key,T> * e = m_pDataArray[m_uIteratorIdx]->first();e;e = m_pDataArray[m_uIteratorIdx]->next()) + { + if(e->pData == pRef)return e; + } + } + } + return 0; + } + + /// + /// Returns the entry pointed by the hash table iterator. + /// This function must be preceeded by a call to firstEntry(), first() + /// or findRef(). + /// + KviPointerHashTableEntry<Key,T> * currentEntry() + { + if(m_uIteratorIdx >= m_uSize)return 0; + if(m_pDataArray[m_uIteratorIdx])return m_pDataArray[m_uIteratorIdx]->current(); + return 0; + } + + /// + /// Places the hash table iterator at the first entry + /// and returns it. + /// + KviPointerHashTableEntry<Key,T> * firstEntry() + { + m_uIteratorIdx = 0; + while(m_uIteratorIdx < m_uSize && (!m_pDataArray[m_uIteratorIdx])) + { + m_uIteratorIdx++; + } + if(m_uIteratorIdx == m_uSize)return 0; + return m_pDataArray[m_uIteratorIdx]->first(); + } + + /// + /// Places the hash table iterator at the next entry + /// and returns it. + /// This function must be preceeded by a call to firstEntry(), first() + /// or findRef(). + /// + KviPointerHashTableEntry<Key,T> * nextEntry() + { + if(m_uIteratorIdx >= m_uSize)return 0; + + if(m_uIteratorIdx < m_uSize) + { + KviPointerHashTableEntry<Key,T> * t = m_pDataArray[m_uIteratorIdx]->next(); + if(t)return t; + } + + m_uIteratorIdx++; + + while(m_uIteratorIdx < m_uSize && (!m_pDataArray[m_uIteratorIdx])) + { + m_uIteratorIdx++; + } + + if(m_uIteratorIdx == m_uSize)return 0; + + return m_pDataArray[m_uIteratorIdx]->first(); + + } + + /// + /// Returns the data value pointer pointed by the hash table iterator. + /// This function must be preceeded by a call to firstEntry(), first() + /// or findRef(). + /// + T * current() + { + if(m_uIteratorIdx >= m_uSize)return 0; + if(m_pDataArray[m_uIteratorIdx]) + { + KviPointerHashTableEntry<Key,T> * e = m_pDataArray[m_uIteratorIdx]->current(); + if(!e)return 0; + return e->data(); + } + return 0; + } + + /// + /// Returns the key pointed by the hash table iterator. + /// This function must be preceeded by a call to firstEntry(), first() + /// or findRef(). + /// + const Key & currentKey() + { + if(m_uIteratorIdx >= m_uSize)return kvi_hash_key_default(((Key *)NULL)); + if(m_pDataArray[m_uIteratorIdx]) + { + KviPointerHashTableEntry<Key,T> * e = m_pDataArray[m_uIteratorIdx]->current(); + if(!e)return kvi_hash_key_default(((Key *)NULL)); + return e->key(); + } + return kvi_hash_key_default(((Key *)NULL)); + } + + /// + /// Places the hash table iterator at the first entry + /// and returns the associated data value pointer. + /// + T * first() + { + m_uIteratorIdx = 0; + while(m_uIteratorIdx < m_uSize && (!m_pDataArray[m_uIteratorIdx])) + { + m_uIteratorIdx++; + } + if(m_uIteratorIdx == m_uSize)return 0; + KviPointerHashTableEntry<Key,T> * e = m_pDataArray[m_uIteratorIdx]->first(); + if(!e)return 0; + return e->data(); + } + + /// + /// Places the hash table iterator at the next entry + /// and returns the associated data value pointer. + /// This function must be preceeded by a call to firstEntry(), first() + /// or findRef(). + /// + T * next() + { + if(m_uIteratorIdx >= m_uSize)return 0; + + if(m_uIteratorIdx < m_uSize) + { + KviPointerHashTableEntry<Key,T> * t = m_pDataArray[m_uIteratorIdx]->next(); + if(t) + { + return t->data(); + } + } + + m_uIteratorIdx++; + + while(m_uIteratorIdx < m_uSize && (!m_pDataArray[m_uIteratorIdx])) + { + m_uIteratorIdx++; + } + + if(m_uIteratorIdx == m_uSize)return 0; + + KviPointerHashTableEntry<Key,T> * e = m_pDataArray[m_uIteratorIdx]->first(); + if(!e)return 0; + return e->data(); + } + + /// + /// Removes all items in the hash table and then + /// makes a complete shallow copy of the data contained in t. + /// The removed items are deleted if autodeletion is enabled. + /// The hash table iterator is invalidated. + /// Does not change autodelete flag: make sure you not delete the items twice :) + /// + void copyFrom(KviPointerHashTable<Key,T> &t) + { + clear(); + for(KviPointerHashTableEntry<Key,T> * e = t.firstEntry();e;e = t.nextEntry()) + insert(e->key(),e->data()); + } + + /// + /// Inserts a complete shallow copy of the data contained in t. + /// The hash table iterator is invalidated. + /// + void insert(KviPointerHashTable<Key,T> &t) + { + for(KviPointerHashTableEntry<Key,T> * e = t.firstEntry();e;e = t.nextEntry()) + insert(e->key(),e->data()); + } + + /// + /// Enables or disabled the autodeletion feature. + /// Items are deleted upon removal when the feature is enabled. + /// + void setAutoDelete(bool bAutoDelete) + { + m_bAutoDelete = bAutoDelete; + } + + /// + /// Creates an empty hash table. + /// Automatic deletion is enabled. + /// + /// \param uSize The number of hash buckets: does NOT necesairly need to be prime + /// \param bCaseSensitive Are the key comparisons case sensitive ? + /// \param Do we need to mantain deep copies of keys ? + /// + KviPointerHashTable(unsigned int uSize = 32,bool bCaseSensitive = true,bool bDeepCopyKeys = true) + { + m_uCount = 0; + m_bCaseSensitive = bCaseSensitive; + m_bAutoDelete = true; + m_bDeepCopyKeys = bDeepCopyKeys; + m_uSize = uSize > 0 ? uSize : 32; + m_pDataArray = new KviPointerList<KviPointerHashTableEntry<Key,T> > *[m_uSize]; + for(unsigned int i=0;i<m_uSize;i++)m_pDataArray[i] = NULL; + } + + /// + /// First creates an empty hash table + /// and then inserts a copy of all the item pointers present in t. + /// The autodelete feature is automatically disabled (take care!). + /// + KviPointerHashTable(KviPointerHashTable<Key,T> &t) + { + m_uCount = 0; + m_bAutoDelete = false; + m_bCaseSensitive = t.m_bCaseSensitive; + m_bDeepCopyKeys = t.m_bDeepCopyKeys; + m_uSize = t.m_uSize; + m_pDataArray = new KviPointerList<KviPointerHashTableEntry<Key,T> > *[m_uSize]; + for(unsigned int i=0;i<m_uSize;i++)m_pDataArray[i] = NULL; + copyFrom(t); + } + + /// + /// Destroys the hash table and all the items contained within. + /// Items are deleted if autodeletion is enabled. + /// + ~KviPointerHashTable() + { + clear(); + delete [] m_pDataArray; + } +}; + +template<typename Key,typename T> class KviPointerHashTableIterator +{ +protected: + const KviPointerHashTable<Key,T> * m_pHashTable; + unsigned int m_uEntryIndex; + KviPointerListIterator<KviPointerHashTableEntry<Key,T> > * m_pIterator; +public: + /// + /// Creates an iterator copy. + /// The new iterator points exactly to the item pointed by src. + /// + void operator = (const KviPointerHashTableIterator<Key,T> &src) + { + m_pHashTable = src.m_pHashTable; + m_uEntryIndex = src.m_uEntryIndex; + if(src.m_pIterator) + m_pIterator = new KviPointerListIterator<KviPointerHashTableEntry<Key,T> >(*(src.m_pIterator)); + else + m_pIterator = NULL; + } + + /// + /// Moves the iterator to the first element of the hash table. + /// Returns true in case of success or false if the hash table is empty. + /// + bool moveFirst() + { + if(m_pIterator) + { + delete m_pIterator; + m_pIterator = NULL; + } + + m_uEntryIndex = 0; + while((m_uEntryIndex < m_pHashTable->m_uSize) && (!(m_pHashTable->m_pDataArray[m_uEntryIndex]))) + { + m_uEntryIndex++; + } + + if(m_uEntryIndex == m_pHashTable->m_uSize) + return false; + + m_pIterator = new KviPointerListIterator<KviPointerHashTableEntry<Key,T> >(*(m_pHashTable->m_pDataArray[m_uEntryIndex])); + bool bRet = m_pIterator->moveFirst(); + if(!bRet) + { + delete m_pIterator; + m_pIterator = NULL; + } + return bRet; + } + + /// + /// Moves the iterator to the last element of the hash table. + /// Returns true in case of success or false if the hash table is empty. + /// + bool moveLast() + { + if(m_pIterator) + { + delete m_pIterator; + m_pIterator = NULL; + } + + m_uEntryIndex = m_pHashTable->m_uSize; + while(m_uEntryIndex > 0) + { + m_uEntryIndex--; + if(m_pHashTable->m_pDataArray[m_uEntryIndex]) + { + m_pIterator = new KviPointerListIterator<KviPointerHashTableEntry<Key,T> >(*(m_pHashTable->m_pDataArray[m_uEntryIndex])); + bool bRet = m_pIterator->moveLast(); + if(!bRet) + { + delete m_pIterator; + m_pIterator = NULL; + } + return bRet; + } + } + return false; + } + + /// + /// Moves the iterator to the next element of the hash table. + /// The iterator must be actually valid for this function to work. + /// Returns true in case of success or false if there is no next item. + /// + bool moveNext() + { + if(!m_pIterator) + return false; + if(m_pIterator->moveNext()) + return true; + if(m_pIterator) + { + delete m_pIterator; + m_pIterator = NULL; + } + m_uEntryIndex++; + while((m_uEntryIndex < m_pHashTable->m_uSize) && (!(m_pHashTable->m_pDataArray[m_uEntryIndex]))) + { + m_uEntryIndex++; + } + if(m_uEntryIndex == m_pHashTable->m_uSize) + return false; + m_pIterator = new KviPointerListIterator<KviPointerHashTableEntry<Key,T> >(*(m_pHashTable->m_pDataArray[m_uEntryIndex])); + bool bRet = m_pIterator->moveFirst(); + if(!bRet) + { + delete m_pIterator; + m_pIterator = NULL; + } + return bRet; + } + + /// + /// Moves the iterator to the next element of the hash table. + /// The iterator must be actually valid for this function to work. + /// Returns true in case of success or false if there is no next item. + /// This is just an alias to moveNext(). + /// + bool operator ++() + { + return moveNext(); + } + + /// + /// Moves the iterator to the previous element of the hash table. + /// The iterator must be actually valid for this function to work. + /// Returns true in case of success or false if there is no previous item. + /// + bool movePrev() + { + if(!m_pIterator) + return false; + if(m_pIterator->movePrev()) + return true; + if(m_pIterator) + { + delete m_pIterator; + m_pIterator = NULL; + } + if(m_uEntryIndex >= m_pHashTable->m_uSize) + return false; + while(m_uEntryIndex > 0) + { + m_uEntryIndex--; + if(m_pHashTable->m_pDataArray[m_uEntryIndex]) + { + m_pIterator = new KviPointerListIterator<KviPointerHashTableEntry<Key,T> >(*(m_pHashTable->m_pDataArray[m_uEntryIndex])); + bool bRet = m_pIterator->moveLast(); + if(!bRet) + { + delete m_pIterator; + m_pIterator = NULL; + } + return bRet; + } + } + return false; + } + + + /// + /// Moves the iterator to the previous element of the hash table. + /// The iterator must be actually valid for this function to work. + /// Returns true in case of success or false if there is no previous item. + /// This is just an alias to movePrev() with a different name. + /// + bool operator --() + { + return movePrev(); + } + + /// + /// Returs the value pointed by the iterator + /// or a default constructed value if the iterator is not valid. + /// This is an alias to operator *() with just a different name. + /// + T * current() const + { + return m_pIterator ? m_pIterator->current()->data() : NULL; + } + + /// + /// Returs the value pointed by the iterator + /// or a default constructed value if the iterator is not valid. + /// This is an alias to current() with just a different name. + /// + T * operator *() const + { + return m_pIterator ? m_pIterator->current()->data() : NULL; + } + + /// + /// Returs the key pointed by the iterator + /// or a default constructed key if the iterator is not valid. + /// + const Key & currentKey() const + { + return m_pIterator ? m_pIterator->current()->key() : kvi_hash_key_default(((Key *)NULL)); + } + + /// + /// Moves the iterator to the first element of the hash table. + /// Returns the first item found or NULL if the hash table is empty. + /// + T * toFirst() + { + if(!moveFirst()) + return NULL; + return current(); + } +public: + /// + /// Creates an iterator pointing to the first item in the hash table, if any. + /// + KviPointerHashTableIterator(const KviPointerHashTable<Key,T> &hTable) + { + m_pHashTable = &hTable; + m_uEntryIndex = 0; + m_pIterator = NULL; + moveFirst(); + } + + /// + /// Destroys the iterator + /// + ~KviPointerHashTableIterator() + { + if(m_pIterator) + delete m_pIterator; + } +}; + + + + +#endif //_KVI_POINTERHASHTABLE_H_ diff --git a/src/kvilib/core/kvi_pointerlist.h b/src/kvilib/core/kvi_pointerlist.h new file mode 100644 index 00000000..381780c8 --- /dev/null +++ b/src/kvilib/core/kvi_pointerlist.h @@ -0,0 +1,1069 @@ +#ifndef _KVI_POINTERLIST_H_ +#define _KVI_POINTERLIST_H_ +//================================================================================================= +// +// File : kvi_pointerlist.h +// Creation date : Tue Jul 6 1999 14:52:20 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2007 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//================================================================================================= +//============================================================================= +// +// C++ Template based double linked pointer list class +// Original ss_list.h Created on 10 Dec 2001 +// Copyright (C) 2001-2007 Szymon Stefanek (pragma at kvirc dot net) +// Added to KVIrc on 02 Jan 2008. +// +//============================================================================= + +// Qt changes the collection classes too much and too frequently. +// I think we need to be independent of that to the maximum degree possible. +// That's why we have our own fast pointer list class. +// This does not depend on Qt AT ALL and has an interface similar +// to the Qt<=3.x series. The pointer lists with the autodelete +// feature was great and I don't completly understand why they have +// been removed from Qt4 in favor of the value based non-autodeleting +// lists... anyway: here we go :) + +#include "kvi_settings.h" + +template<typename T> class KviPointerList; +template<typename T> class KviPointerListIterator; + +#ifndef NULL + #define NULL 0 +#endif + +/// +/// \internal +/// +class KviPointerListNode +{ +public: + KviPointerListNode * m_pPrev; + void * m_pData; + KviPointerListNode * m_pNext; +}; + +/// +/// \class KviPointerListIterator +/// \brief A fast KviPointerList iterator. +/// +/// This class allows traversing the list sequentially. +/// Multilpe iterators can traverse the list at the same time. +/// +/// Iteration example 1: +/// +/// \verbatim +/// KviPointerListIterator<T> it(list); +/// for(bool b = it.moveFirst();b;b = it.moveNext()) +/// { +/// T * pData = it.data(); +/// doSomethingWithData(pData); +/// } +/// \endverbatim +/// +/// Iteration example 2: +/// +/// \verbatim +/// KviPointerListIterator<T> it(list); +/// if(it.moveFirst()) +/// { +/// do { +/// T * pData = it.data(); +/// doSomethingWithData(pData); +/// } while(it.moveNext()); +/// } +/// \endverbatim +/// +/// Iteration example 3: +/// +/// \verbatim +/// KviPointerListIterator<T> it(list.iteratorAt(10)); +/// if(it.isValid()) +/// { +/// do { +/// T * pData = it.data(); +/// doSomethingWithData(pData); +/// while(it.movePrev()); +/// } +/// \endverbatim +/// +/// Please note that you must NOT remove any item from +/// the list when using the iterators. An iterator pointing +/// to a removed item will crash your application if you use it. +/// The following code will NOT work (and crash): +/// +/// \verbatim +/// KviPointerList<T> l; +/// l.append(new KviStr("x")); +/// l.append(new KviStr("y")); +/// KviPointerListIterator<T> it(l); +/// it.moveFirst(); +/// l.removeFirst(); +/// KviStr * tmp = it.data(); <-- this will crash +/// \endverbatim +/// +/// In the rare cases in that you need to remove items +/// while traversing the list you should put them +/// in a temporary list and remove them after the iteration. +/// +/// I've choosen this way because usually you don't modify +/// the list while traversing it and a fix for this +/// would add a constant overhead to several list operation. +/// You just must take care of it yourself. +/// +/// \warning This class is not thread safe by itself. +/// +template<typename T> class KviPointerListIterator +{ +protected: + KviPointerList<T> * m_pList; + KviPointerListNode * m_pNode; +public: + /// + /// Creates an iterator copy. + /// The new iterator points exactly to the item pointed by src. + /// + KviPointerListIterator(const KviPointerListIterator<T> &src) + { + m_pList = src.m_pList; + m_pNode = src.m_pNode; + } + + /// + /// Creates an iterator for the list l. + /// The iterator points to the first list item, if any. + /// + KviPointerListIterator(KviPointerList<T> &l) + { + m_pList = (KviPointerList<T> *)&l; + m_pNode = m_pList->m_pHead; + } + + /// + /// Creates an iterator for the list l. + /// The iterator points to the specified list node. + /// + KviPointerListIterator(KviPointerList<T> &l,KviPointerListNode * pNode) + { + m_pList = (KviPointerList<T> *)&l; + m_pNode = pNode; + } + + /// + /// Creates an iterator copy. + /// The new iterator points exactly to the item pointed by src. + /// + void operator = (const KviPointerListIterator<T> &src) + { + m_pList = src.m_pList; + m_pNode = src.m_pNode; + } +public: + /// + /// Moves the iterator to the first element of the list. + /// Returns true in case of success or false if the list is empty. + /// + bool moveFirst() + { + m_pNode = m_pList->m_pHead; + return m_pNode != NULL; + } + + /// + /// Moves the iterator to the last element of the list. + /// Returns true in case of success or false if the list is empty. + /// + bool moveLast() + { + m_pNode = m_pList->m_pTail; + return m_pNode != NULL; + } + + /// + /// Moves the iterator to the next element of the list. + /// The iterator must be actually valid for this function to work. + /// Returns true in case of success or false if there is no next item. + /// + bool moveNext() + { + if(!m_pNode)return false; + m_pNode = m_pNode->m_pNext; + return m_pNode != NULL; + } + + /// + /// Moves the iterator to the next element of the list. + /// The iterator must be actually valid for this operator to work. + /// Returns true in case of success or false if there is no next item. + /// This is just a convenient alias to moveNext(). + /// + bool operator ++() + { + if(!m_pNode)return false; + m_pNode = m_pNode->m_pNext; + return m_pNode != NULL; + } + + /// + /// Moves the iterator to the previous element of the list. + /// The iterator must be actually valid for this function to work. + /// Returns true in case of success or false if there is no previous item. + /// + bool movePrev() + { + if(!m_pNode)return false; + m_pNode = m_pNode->m_pPrev; + return m_pNode != NULL; + } + + /// + /// Moves the iterator to the previous element of the list. + /// The iterator must be actually valid for this operator to work. + /// Returns true in case of success or false if there is no previous item. + /// This is just a convenient alias to movePrev(). + /// + bool operator --() + { + if(!m_pNode)return false; + m_pNode = m_pNode->m_pPrev; + return m_pNode != NULL; + } + + /// + /// Returs the value pointed by the iterator + /// or NULL if the iterator is not valid. + /// + T * current() + { + return m_pNode ? (T *)(m_pNode->m_pData) : NULL; + } + + /// + /// Returs the value pointed by the iterator + /// or NULL if the iterator is not valid. + /// This is just an alias to current(). + /// + T * operator *() + { + return m_pNode ? (T *)(m_pNode->m_pData) : NULL; + } + + /// + /// Returns true if this iterator points to a valid + /// element of the list and false otherwise. + /// + bool isValid() + { + return m_pNode != NULL; + } +}; + +/// +/// \class KviPointerList +/// \brief A template double linked list of pointers. +/// +/// The main advantage of this type of list is speed. +/// Insertion of pointers is very fast when compared +/// to the typical "copy constructor" call used +/// in the "plain type" template list implementations. +/// +/// Iterating over pointers is also very fast and this +/// class contains an internal iterator that allows to +/// write loops in a compact and clean way. +/// See the first(), next(), current() and findRef() +/// functions for the description of this feature. +/// +/// There is also a non-const external iterator +/// that you can use to traverse the list concurrently. +/// There is no const iterator (and no const access methods) +/// since the list provides the autoDelete() method +/// which vould implicitly violate constness. +/// If you have to deal with const objects then +/// you need to use a QList instead. +/// +/// Your objects also do not need to support copy constructors +/// or >= operators. This class will work fine without them +/// as opposed to a plain QList. +/// +/// This class also supports automatic deletion of the inseted items. +/// See the setAutoDelete() and autoDelete() members for the +/// description of the feature. +/// +/// Typcal usage: +/// +/// \verbatim +/// KviPointerList<MyClass> list(); +/// list.append(new MyClass()); +/// list.append(new MyClass()); +/// ... +/// for(MyClass * c = list.first();c;c = list.next())doSomethingWith(c); +/// delete list; // autodelete is set to true in the constructor +/// \endverbatim +/// +/// \warning This class is absolutely NOT thread safe. You must +/// protect concurrent access from multiple threads by +/// using an external synchronization tool (such as KviMutex). +/// +template<typename T> class KviPointerList +{ + friend class KviPointerListIterator<T>; +protected: + bool m_bAutoDelete; //< do we automatically delete items when they are removed ? + + KviPointerListNode * m_pHead; //< our list head pointer (NULL if there are no items in the list) + KviPointerListNode * m_pTail; //< our list tail + KviPointerListNode * m_pAux; //< our iteration pointer + + unsigned int m_uCount; //< the count of items in the list +protected: + /// + /// \internal + /// + /// inserts the item d before the item ref or at the beginning + /// if ref is not found in the list + /// also sets the current iteration pointer to the newly inserted item + /// + void insertBeforeSafe(KviPointerListNode * ref,const T * d) + { + m_pAux = ref; + KviPointerListNode * n = new KviPointerListNode; + n->m_pPrev = m_pAux->m_pPrev; + n->m_pNext = m_pAux; + if(m_pAux->m_pPrev) + { + m_pAux->m_pPrev->m_pNext = n; + } else { + m_pHead = n; + } + m_pAux->m_pPrev = n; + n->m_pData = (void *)d; + m_uCount++; + } + + /// + /// \internal + /// + /// Grabs the first element from the list src + /// and puts it as the first element of this list. + /// + void grabFirstAndPrepend(KviPointerList<T> * src) + { + KviPointerListNode * pNewHead = src->m_pHead; + if(!pNewHead) + return; + + if(pNewHead->m_pNext) + { + src->m_pHead = pNewHead->m_pNext; + src->m_pHead->m_pPrev = NULL; + } else { + src->m_pHead = NULL; + src->m_pTail = NULL; + } + + if(m_pHead) + { + m_pHead->m_pPrev = pNewHead; + pNewHead->m_pNext = m_pHead; + m_pHead = pNewHead; + } else { + m_pHead = pNewHead; + m_pTail = pNewHead; + m_pHead->m_pNext = NULL; + } + m_uCount++; + src->m_uCount--; + } + + /// + /// \internal + /// + /// Removes the current iteration item assuming that it is valid. + /// + void removeCurrentSafe() + { + if(m_pAux->m_pPrev) + m_pAux->m_pPrev->m_pNext = m_pAux->m_pNext; + else + m_pHead = m_pAux->m_pNext; + if(m_pAux->m_pNext) + m_pAux->m_pNext->m_pPrev = m_pAux->m_pPrev; + else + m_pTail = m_pAux->m_pPrev; + const T * pAuxData = (const T *)(m_pAux->m_pData); + delete m_pAux; + m_pAux = NULL; + m_uCount--; + if(m_bAutoDelete) + delete pAuxData; // this can cause recursion, so do it at the end + } + +public: + /// + /// Inserts the list src inside this list + /// by respecting the sort order. + /// The src list elements are removed. + /// + void merge(KviPointerList<T> * src) + { + m_pAux = m_pHead; + KviPointerListNode * n = src->m_pHead; + m_uCount += src->m_uCount; + while(m_pAux && n) + { + if(kvi_compare((const T *)(m_pAux->m_pData),(const T *)(n->m_pData)) > 0) + { + // our element is greater, n->m_pData goes first + KviPointerListNode * pNext = n->m_pNext; + n->m_pPrev = m_pAux->m_pPrev; // his prev becomes + n->m_pNext = m_pAux; + if(m_pAux->m_pPrev) + m_pAux->m_pPrev->m_pNext = n; + else + m_pHead = n; + m_pAux->m_pPrev = n; + n = pNext; + } else { + // that element is greater + m_pAux = m_pAux->m_pNext; + } + } + if(n) + { + // last items to append + if(m_pTail) + { + m_pTail->m_pNext = n; + n->m_pPrev = m_pTail; + } else { + m_pHead = n; + m_pTail = n; + n->m_pPrev = NULL; + } + m_pTail = src->m_pTail; + } + + src->m_pHead = NULL; + src->m_pTail = NULL; + src->m_uCount = 0; + } + + void swap(KviPointerList<T> * src) + { + KviPointerListNode * n = m_pHead; + m_pHead = src->m_pHead; + src->m_pHead = n; + n = m_pTail; + m_pTail = src->m_pTail; + src->m_pTail = n; + unsigned int uCount = m_uCount; + m_uCount = src->m_uCount; + src->m_uCount = uCount; + } + + + /// + /// Sorts this list in ascending order. + /// There must be an int kvi_compare(const T *p1,const T *p2) function + /// which returns a value less than, equal to + /// or greater than zero when the item p1 is considered lower than, + /// equal to or greater than p2. + /// + void sort() + { + if(m_uCount < 2)return; + + KviPointerList<T> carry; + KviPointerList<T> tmp[64]; + KviPointerList * fill = &tmp[0]; + KviPointerList * counter; + + do { + carry.grabFirstAndPrepend(this); + + for(counter = &tmp[0];counter != fill && !counter->isEmpty();++counter) + { + counter->merge(&carry); + carry.swap(counter); + } + carry.swap(counter); + if(counter == fill) + ++fill; + } while(m_uCount > 0); + + for(counter = &tmp[1];counter != fill;++counter) + counter->merge(counter-1); + swap(fill-1); + } + + /// + /// Inserts the item respecting the sorting order inside the list. + /// The list itself must be already sorted for this to work correctly. + /// There must be a int kvi_compare(const T *p1,const T * p2) + /// that returns a value less than, equal to + /// or greater than zero when the item p1 is considered lower than, + /// equal to or greater than p2. + /// + void inSort(T * t) + { + KviPointerListNode * x = m_pHead; + while(x && (kvi_compare(((T *)x->m_pData),t) > 0))x = x->m_pNext; + if(!x)append(t); + else insertBeforeSafe(x,t); + } + + /// + /// Returns true if the list is empty + /// + bool isEmpty() const + { + return (m_pHead == NULL); + } + + /// + /// Returns the count of the items in the list + /// + unsigned int count() const + { + return m_uCount; + } + + /// + /// Sets the iteration pointer to the first item in the list + /// and returns that item (or 0 if the list is empty) + /// + T * first() + { + if(!m_pHead) + { + m_pAux = NULL; + return NULL; + } + m_pAux = m_pHead; + return (T *)(m_pAux->m_pData); + } + + /// + /// Removes the first element from the list + /// and returns it to the caller. This function + /// obviously never deletes the item (regadless of autoDeletion()). + /// + T * takeFirst() + { + if(!m_pHead)return NULL; + T * pData = (T *)m_pHead->m_pData; + if(m_pHead->m_pNext) + { + m_pHead = m_pHead->m_pNext; + delete m_pHead->m_pPrev; + m_pHead->m_pPrev = NULL; + } else { + delete m_pHead; + m_pHead = NULL; + m_pTail = NULL; + } + m_uCount--; + return pData; + } + + /// + /// Returns an iterator pointing to the first item of the list. + /// + KviPointerListIterator<T> iteratorAtFirst() + { + return KviPointerListIterator<T>(*this,m_pHead); + } + + /// + /// Sets the iteration pointer to the last item in the list + /// and returns that item (or 0 if the list is empty) + /// + T * last() + { + if(!m_pTail) + { + m_pAux = NULL; + return NULL; + } + m_pAux = m_pTail; + return (T *)(m_pAux->m_pData); + } + + /// + /// Returns an iterator pointing to the first item of the list. + /// + KviPointerListIterator<T> iteratorAtLast() + { + return KviPointerListIterator<T>(*this,m_pTail); + } + + /// + /// Returns the current iteration item + /// A call to this function MUST be preceded by a call to + /// first(),last(),at() or findRef() + /// + T * current() + { + return (T *)(m_pAux->m_pData); + } + + /// + /// Returns the current iteration item + /// A call to this function should be preceded by a call to + /// first(),last(),at() or findRef(). + /// This function will return a NULL pointer if the current + /// item has been invalidated due to a remove operation. + /// + T * safeCurrent() + { + return m_pAux ? (T *)(m_pAux->m_pData) : NULL; + } + + + /// + /// Returns an iterator pointing to the current item in the list. + /// A call to this function MUST be preceded by a call to + /// first(),last(),at() or findRef() + /// + KviPointerListIterator<T> iteratorAtCurrent() + { + return KviPointerListIterator<T>(*this,m_pAux); + } + + /// + /// Sets the iteration pointer to the next item in the list + /// and returns that item (or 0 if the end of the list has been reached) + /// A call to this function MUST be preceded by a _succesfull_ call to + /// first(),last(),at() or findRef(). + /// + T * next() + { + if(!m_pAux)return NULL; + m_pAux = m_pAux->m_pNext; + if(m_pAux)return (T *)(m_pAux->m_pData); + return NULL; + } + + /// + /// Sets the iteration pointer to the previous item in the list + /// and returns that item (or 0 if the beginning of the list has been reached) + /// A call to this function MUST be preceded by a _succesfull_ call to + /// first(),last(),at() or findRef() + /// + T * prev() + { + if(!m_pAux)return NULL; + m_pAux = m_pAux->m_pPrev; + if(m_pAux)return (T *)(m_pAux->m_pData); + return NULL; + } + + /// + /// Sets the iteration pointer to the nTh item in the list + /// and returns that item (or 0 if the index is out of range) + /// + T * at(int idx) + { + T * t = first(); + int cnt = 0; + while(t) + { + if(idx == cnt)return t; + t = next(); + cnt++; + } + return 0; + } + + /// + /// Returns an iterator pointing to the item at the specified index. + /// + KviPointerListIterator<T> iteratorAt(int idx) + { + KviPointerListNode * n = m_pHead; + int cnt = 0; + while(n) + { + if(idx == cnt) + return KviPointerListIterator<T>(*this,n); + n = n->m_pNext; + cnt++; + } + return KviPointerListIterator<T>(*this,NULL); + } + + /// + /// Sets the iteration pointer to the item with pointer d + /// and returns its position (zero based index) in the list or -1 if the + /// item cannot be found + /// + int findRef(const T * d) + { + int ret = 0; + for(T * t = first();t;t = next()) + { + if(t == d)return ret; + ret++; + } + return -1; + } + + /// + /// Returns an iterator pointing to the item with pointer d. + /// + KviPointerListIterator<T> iteratorAtRef(const T * d) + { + KviPointerListNode * n = m_pHead; + while(n) + { + if(n->m_pData == d) + return KviPointerListIterator<T>(*this,n); + n = n->m_pNext; + } + return KviPointerListIterator<T>(*this,NULL); + } + + /// + /// Appends an item at the end of the list + /// + void append(const T * d) + { + if(!m_pHead) + { + m_pHead = new KviPointerListNode; + m_pHead->m_pPrev = NULL; + m_pHead->m_pNext = NULL; + m_pHead->m_pData = (void *)d; + m_pTail = m_pHead; + } else { + m_pTail->m_pNext = new KviPointerListNode; + m_pTail->m_pNext->m_pPrev = m_pTail; + m_pTail->m_pNext->m_pNext = NULL; + m_pTail->m_pNext->m_pData = (void *)d; + m_pTail = m_pTail->m_pNext; + } + m_uCount++; + } + + /// + /// Appends all the items from the list l to this list + /// + void append(KviPointerList<T> * l) + { + for(T * t = l->first();t;t = l->next())append(t); + } + + /// + /// Prepends (inserts in head position) all the items from + /// the list l to this list + /// + void prepend(KviPointerList<T> * l) + { + for(T * t = l->last();t;t = l->prev())prepend(t); + } + + /// + /// Inserts the item d in the head position + /// + void prepend(const T * d) + { + if(!m_pHead) + { + m_pHead = new KviPointerListNode; + m_pHead->m_pPrev = NULL; + m_pHead->m_pNext = NULL; + m_pHead->m_pData = (void *)d; + m_pTail = m_pHead; + } else { + m_pHead->m_pPrev = new KviPointerListNode; + m_pHead->m_pPrev->m_pNext = m_pHead; + m_pHead->m_pPrev->m_pPrev = NULL; + m_pHead->m_pPrev->m_pData = (void *)d; + m_pHead = m_pHead->m_pPrev; + m_uCount++; + } + } + + /// + /// Inserts the item d at the zero-based position + /// specified by iIndex. If the specified position + /// is out of the list then the item is appended. + /// Note that this function costs O(n). + /// It's really better to use insertAfter() or + /// insertBefore(), if possible. + /// + void insert(int iIndex,const T * d) + { + m_pAux = m_pHead; + while(m_pAux && iIndex > 0) + { + iIndex--; + m_pAux = m_pAux->m_pNext; + } + if(m_pAux) + insertBeforeSafe(m_pAux,d); + else + append(d); + } + + /// + /// Removes the firstitem (if any) + /// the item is deleted if autoDelete() is set to true + /// + bool removeFirst() + { + if(!m_pHead)return false; + const T * pAuxData; + if(m_pHead->m_pNext) + { + m_pHead = m_pHead->m_pNext; + pAuxData = (const T *)(m_pHead->m_pPrev->m_pData); + delete m_pHead->m_pPrev; + m_pHead->m_pPrev = NULL; + } else { + pAuxData = (const T *)(m_pHead->m_pData); + delete m_pHead; + m_pHead = NULL; + m_pTail = NULL; + } + m_pAux = NULL; + m_uCount--; + if(m_bAutoDelete) + delete pAuxData; + return true; + } + + /// + /// Removes the firstitem (if any) + /// the item is deleted if autoDelete() is set to true + /// + bool removeLast() + { + if(!m_pTail)return false; + const T * pAuxData; + if(m_pTail->m_pPrev) + { + m_pTail = m_pTail->m_pPrev; + pAuxData = (const T *)(m_pTail->m_pNext->m_pData); + delete m_pTail->m_pNext; + m_pTail->m_pNext = NULL; + } else { + pAuxData = (const T *)(m_pTail->m_pData); + delete m_pTail; + m_pHead = NULL; + m_pTail = NULL; + } + m_pAux = NULL; + m_uCount--; + if(m_bAutoDelete) + delete pAuxData; + return true; + } + + /// + /// Removes the item at zero-based position iIndex. + /// Does nothing and returns false if iIndex is out of the list. + /// Please note that this function costs O(n). + /// + bool remove(int iIndex) + { + m_pAux = m_pHead; + while(m_pAux && iIndex > 0) + { + iIndex--; + m_pAux = m_pAux->m_pNext; + } + if(!m_pAux) + return false; + removeCurrentSafe(); + return true; + } + + /// + /// Sets the autodelete flag + /// When this flag is on (default) , all the items + /// are deleted when removed from the list (or when the list is destroyed + /// or cleared explicitly) + /// + void setAutoDelete(bool bAutoDelete) + { + m_bAutoDelete = bAutoDelete; + } + + /// + /// Returns the autodelete flag. + /// + bool autoDelete() + { + return m_bAutoDelete; + }; + + /// + /// Removes all the items from the list + /// (the items are deleted if the autoDelete() flag is set to true) + /// + void clear() + { + while(m_pHead)removeFirst(); + } + + /// + /// Removes the current iteration item. + /// Returns true if the current iteration item was valid (and was removed) + /// and false otherwise. + /// + bool removeCurrent() + { + if(!m_pAux) + return false; + removeCurrentSafe(); + return true; + } + + /// + /// Removes the item pointed by d (if found in the list) + /// the item is deleted if the autoDelete() flag is set to true) + /// Returns true if the item was in the list and false otherwise. + /// + bool removeRef(const T * d) + { + if(findRef(d) == -1)return false; + removeCurrentSafe(); + return true; + } + + /// + /// inserts the item d after the item ref or at the end + /// if ref is not found in the list + /// also sets the current iteration pointer to the newly inserted item + /// + void insertAfter(const T * ref,const T * d) + { + if(findRef(ref) == -1) + { + append(d); + return; + } + KviPointerListNode * n = new KviPointerListNode; + n->m_pPrev = m_pAux; + n->m_pNext = m_pAux->m_pNext; + if(m_pAux->m_pNext) + m_pAux->m_pNext->m_pPrev = n; + else + m_pTail = n; + m_pAux->m_pNext = n; + n->m_pData = (void *)d; + m_uCount++; + } + + /// + /// inserts the item d before the item ref or at the beginning + /// if ref is not found in the list + /// also sets the current iteration pointer to the newly inserted item + /// + void insertBefore(const T * ref,const T * d) + { + if(findRef(ref) == -1) + { + prepend(d); + return; + } + KviPointerListNode * n = new KviPointerListNode; + n->m_pPrev = m_pAux->m_pPrev; + n->m_pNext = m_pAux; + if(m_pAux->m_pPrev) + m_pAux->m_pPrev->m_pNext = n; + else + m_pHead = n; + m_pAux->m_pPrev = n; + n->m_pData = (void *)d; + m_uCount++; + } + + /// + /// Inverts the elements in the list. + /// + void invert() + { + if(!m_pHead)return; + KviPointerListNode * oldHead = m_pHead; + KviPointerListNode * oldTail = m_pTail; + KviPointerListNode * n = m_pHead; + while(n) + { + KviPointerListNode * next = n->m_pNext; + n->m_pNext = n->m_pPrev; + n->m_pPrev = next; + n = next; + } + m_pTail = oldHead; + m_pHead = oldTail; + } + + /// + /// clears the list and inserts all the items from the list l + /// + void copyFrom(KviPointerList<T> * l) + { + clear(); + for(T * t = l->first();t;t = l->next())append(t); + } + + /// + /// equivalent to copyFrom(l) + /// + KviPointerList<T> & operator = (KviPointerList<T> &l) + { + copyFrom(&l); + return *this; + } + + /// + /// creates a template list + /// + KviPointerList<T>(bool bAutoDelete = true) + { + m_bAutoDelete = bAutoDelete; + m_pHead = NULL; + m_pTail = NULL; + m_uCount = 0; + m_pAux = NULL; + }; + + /// + /// destroys the list + /// if autoDelete() is set to true, all the items are deleted + /// + virtual ~KviPointerList<T>() + { + clear(); + }; +}; + +#define KviPointerListBase KviPointerList + +// BROKEN MSVC LINKER +#ifdef COMPILE_ON_WINDOWS + #include "kvi_string.h" + template class KVILIB_API KviPointerList<KviStr>; +#endif + +#endif //_KVI_POINTERLIST_H_ diff --git a/src/kvilib/core/kvi_qcstring.h b/src/kvilib/core/kvi_qcstring.h new file mode 100644 index 00000000..0693e205 --- /dev/null +++ b/src/kvilib/core/kvi_qcstring.h @@ -0,0 +1,39 @@ +#ifndef _KVI_QCSTRING_H_ +#define _KVI_QCSTRING_H_ + +//============================================================================= +// +// File : kvi_qcstring.h +// Creation date : Thu Jan 18 2007 00:34:33 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2007 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#include "kvi_settings.h" + +#ifdef COMPILE_USE_QT4 + #include <q3cstring.h> // includes <qbytearray.h> + #define KviQCString QByteArray +#else + // this is dead in Qt 4.x + #include <qcstring.h> + #define KviQCString QCString +#endif + +#endif //!_KVI_QCSTRING_H_ diff --git a/src/kvilib/core/kvi_qstring.cpp b/src/kvilib/core/kvi_qstring.cpp new file mode 100644 index 00000000..eba255aa --- /dev/null +++ b/src/kvilib/core/kvi_qstring.cpp @@ -0,0 +1,1125 @@ +//============================================================================= +// +// File : kvi_qstring.cpp +// Creation date : Mon Aug 04 2003 13:36:33 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2003-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +//============================================================================= +// +// Helper functions for the QString class +// +//============================================================================= + +#define __KVILIB__ + + +#include "kvi_qstring.h" +#include "kvi_string.h" +#include "kvi_malloc.h" +#include "kvi_locale.h" + +#include <ctype.h> // for tolower() +#include <stdio.h> // for sprintf() +#include <qregexp.h> + +// kvi_string.cpp +extern unsigned char iso88591_toLower_map[256]; +extern unsigned char iso88591_toUpper_map[256]; + +#define MY_MIN(a,b) (((a) < (b)) ? (a) : (b)) + +namespace KviQString +{ + // The global empty (and null) string + const QString empty; + + bool equalCSN(const QString &sz1,const QString &sz2,unsigned int len) + { + if(len == 0)return true; // assume equal + const QChar * c1 = sz1.unicode(); + const QChar * c2 = sz2.unicode(); + unsigned int lmin = MY_MIN(sz1.length(),sz2.length()); + if(lmin < len)return false; + const QChar * c1e = c1 + len; + + if(!c1 || !c2)return (c1 == c2); + + while(c1 < c1e) + { + if(c1->unicode() != c2->unicode())return false; + c1++; + c2++; + } + return (c1 == c1e); + } + + bool equalCIN(const QString &sz1,const QString &sz2,unsigned int len) + { + if(len == 0)return true; // assume equal + const QChar * c1 = sz1.unicode(); + const QChar * c2 = sz2.unicode(); + unsigned int lmin = MY_MIN(sz1.length(),sz2.length()); + if(lmin < len)return false; + const QChar * c1e = c1 + len; + + if(!c1 || !c2)return (c1 == c2); + + while(c1 < c1e) + { +#ifdef COMPILE_USE_QT4 + if(c1->toLower().unicode() != c2->toLower().unicode())return false; +#else + if(c1->lower().unicode() != c2->lower().unicode())return false; +#endif + c1++; + c2++; + } + return (c1 == c1e); + } + + bool equalCSN(const QString &sz1,const char * sz2,unsigned int len) + { + if(len == 0)return true; // assume equal + const QChar * c1 = sz1.unicode(); + if(sz1.length() < len)return false; + const QChar * c1e = c1 + len; + + if(!sz2)return !c1; + if(!c1)return !sz2; + + while((c1 < c1e) && (*sz2)) + { + if(c1->unicode() != *sz2)return false; + c1++; + sz2++; + } + return (c1 == c1e); + } + + bool equalCIN(const QString &sz1,const char * sz2,unsigned int len) + { + if(len == 0)return true; // assume equal + const QChar * c1 = sz1.unicode(); + if(sz1.length() < len)return false; + const QChar * c1e = c1 + len; + + if(!sz2)return !c1; + if(!c1)return !(*sz2); + + while((c1 < c1e) && (*sz2)) + { +#ifdef COMPILE_USE_QT4 + if(c1->toLower().unicode() != tolower(*sz2))return false; +#else + if(c1->lower().unicode() != tolower(*sz2))return false; +#endif + c1++; + sz2++; + } + return (c1 == c1e); + } + + // sz2 is assumed to be null terminated, sz1 is not! + bool equalCIN(const QString &sz1,const QChar *sz2,unsigned int len) + { + if(len == 0)return true; // assume equal + const QChar * c1 = sz1.unicode(); + if(sz1.length() < len)return false; + const QChar * c1e = c1 + len; + + if(!sz2)return !c1; + if(!c1)return !(sz2->unicode()); + + while((c1 < c1e) && (sz2->unicode())) + { +#ifdef COMPILE_USE_QT4 + if(c1->toLower().unicode() != sz2->toLower().unicode())return false; +#else + if(c1->lower().unicode() != sz2->lower().unicode())return false; +#endif + c1++; + sz2++; + } + return (c1 == c1e); + } + + QString makeSizeReadable(size_t bytes) + { + double size = bytes; + if(size<900) + return QString(__tr2qs("%1 bytes")).arg(size,0,'f',3); + + size/=1024; + if(size<900) + return QString(__tr2qs("%1 KB")).arg(size,0,'f',3); + + size/=1024; + if(size<900) + return QString(__tr2qs("%1 MB")).arg(size,0,'f',3); + + //Pirated DVD?;) + size/=1024; + if(size<900) + return QString(__tr2qs("%1 GB")).arg(size,0,'f',3); + + //Uhm.. We are downloading a whole internet:))) + size/=1024; + return QString(__tr2qs("%1 TB")).arg(size,0,'f',3); + } + + bool equalCS(const QString &sz1,const QString &sz2) + { + if(sz1.length() != sz2.length())return false; + + const QChar * c1 = sz1.unicode(); + const QChar * c2 = sz2.unicode(); + const QChar * c1e = c1 + sz1.length(); + + if(!c1 || !c2)return (c1 == c2); + + while(c1 < c1e) + { + if(c1->unicode() != c2->unicode())return false; + c1++; + c2++; + } + return (c1 == c1e); + } + + bool equalCI(const QString &sz1,const QString &sz2) + { + if(sz1.length() != sz2.length())return false; + + const QChar * c1 = sz1.unicode(); + const QChar * c2 = sz2.unicode(); + const QChar * c1e = c1 + sz1.length(); + + if(!c1 || !c2)return (c1 == c2); + + while(c1 < c1e) + { +#ifdef COMPILE_USE_QT4 + if(c1->toLower().unicode() != c2->toLower().unicode())return false; +#else + if(c1->lower().unicode() != c2->lower().unicode())return false; +#endif + c1++; + c2++; + } + return (c1 == c1e); + } + + // sz2 is assumed to be null terminated, sz1 is not! + bool equalCI(const QString &sz1,const QChar *sz2) + { + const QChar * c1 = sz1.unicode(); + const QChar * c1e = c1 + sz1.length(); + + if(!c1 || !sz2)return (c1 == sz2); + + while(c1 < c1e) + { + if(!sz2->unicode())return false; // sz1 has at least another character +#ifdef COMPILE_USE_QT4 + if(c1->toLower().unicode() != sz2->toLower().unicode())return false; +#else + if(c1->lower().unicode() != sz2->lower().unicode())return false; +#endif + c1++; + sz2++; + } + return (c1 == c1e) && (!sz2->unicode()); + } + + bool equalCS(const QString &sz1,const char * sz2) + { + const QChar * c1 = sz1.unicode(); + const QChar * c1e = c1 + sz1.length(); + + if(!c1)return !sz2; + + while((c1 < c1e) && (*sz2)) + { + if(c1->unicode() != *sz2)return false; + c1++; + sz2++; + } + return ((c1 == c1e) && (*sz2 == '\0')); + } + + bool equalCI(const QString &sz1,const char * sz2) + { + const QChar * c1 = sz1.unicode(); + const QChar * c1e = c1 + sz1.length(); + + if(!c1)return !sz2; + + while((c1 < c1e) && (*sz2)) + { +#ifdef COMPILE_USE_QT4 + if(c1->toLower().unicode() != tolower(*sz2))return false; +#else + if(c1->lower().unicode() != tolower(*sz2))return false; +#endif + c1++; + sz2++; + } + return ((c1 == c1e) && (*sz2 == '\0')); + } + + int cmpCS(const QString &sz1,const QString &sz2) + { + const QChar * c1 = sz1.unicode(); + const QChar * c2 = sz2.unicode(); + const QChar * c1e = c1 + sz1.length(); + const QChar * c2e = c2 + sz2.length(); + + if(!c1) + { + if(!c2)return 0; + return -1; + } + if(!c2)return 1; + + + for(;;) + { + if(c1 >= c1e) + { + if(c2 < c2e)return /* 0 */ - (c2->unicode()); + return 0; + } + if(c2 >= c2e)return c1->unicode() /* - 0 */; + + int diff = c1->unicode() - c2->unicode(); + if(diff)return diff; + + c1++; + c2++; + } + + return 0; // never here + } + + int cmpCI(const QString &sz1,const QString &sz2) + { + const QChar * c1 = sz1.unicode(); + const QChar * c2 = sz2.unicode(); + const QChar * c1e = c1 + sz1.length(); + const QChar * c2e = c2 + sz2.length(); + + if(!c1) + { + if(!c2)return 0; + return -1; + } + if(!c2)return 1; + + for(;;) + { + if(c1 >= c1e) + { +#ifdef COMPILE_USE_QT4 + if(c2 < c2e)return /* 0 */ - (c2->toLower().unicode()); +#else + if(c2 < c2e)return /* 0 */ - (c2->lower().unicode()); +#endif + return 0; + } +#ifdef COMPILE_USE_QT4 + if(c2 >= c2e)return c1->toLower().unicode() /* - 0 */; +#else + if(c2 >= c2e)return c1->lower().unicode() /* - 0 */; +#endif + +#ifdef COMPILE_USE_QT4 + int diff = c1->toLower().unicode() - c2->toLower().unicode(); +#else + int diff = c1->lower().unicode() - c2->lower().unicode(); +#endif + if(diff)return diff; + + c1++; + c2++; + } + + return 0; // never here + } + + int cmpCIN(const QString &sz1,const QString &sz2,unsigned int len) + { + if(len == 0)return 0; // assume equal + unsigned int l1 = MY_MIN(len,sz1.length()); + unsigned int l = MY_MIN(l1,sz2.length()); // FIXME: THIS IS NOT OK + + const QChar * c1 = sz1.unicode(); + const QChar * c2 = sz2.unicode(); + const QChar * c1e = c1 + l; + + if(!c1) + { + if(!c2)return 0; + return -1; + } + if(!c2)return 1; + + int diff = 0; + +#ifdef COMPILE_USE_QT4 + while((c1 < c1e) && !(diff = (c1->toLower().unicode() - c2->toLower().unicode()))) +#else + while((c1 < c1e) && !(diff = (c1->lower().unicode() - c2->lower().unicode()))) +#endif + { + c1++; + c2++; + } + + return diff; + } + + void ensureLastCharIs(QString &szString,const QChar &c) + { + if(!lastCharIs(szString,c))szString.append(c); + } + + QString getToken(QString &szString,const QChar &sep) + { + int i=0; + while(i < szString.length()) + { + if(szString[i] == sep)break; + i++; + } + QString ret; + if(i == szString.length()) + { + ret = szString; + szString = ""; + } else { + ret = szString.left(i); + while(i < szString.length()) + { + if(szString[i] != sep)break; + i++; + } + if(i == szString.length())szString = ""; + else szString.remove(0,i); + } + return ret; + } + + void stripRightWhiteSpace(QString &s) + { + int iRemove = 0; + while(iRemove < s.length()) + { + if(s.at(s.length() - (iRemove + 1)).isSpace())iRemove++; + else break; + } + if(iRemove > 0)s.remove(s.length() - iRemove,iRemove); + } + + void stripRight(QString &s,const QChar &c) + { + int iRemove = 0; + while(iRemove < s.length()) + { + if(s.at(s.length() - (iRemove + 1)) == c)iRemove++; + else break; + } + if(iRemove > 0)s.remove(s.length() - iRemove,iRemove); + } + + void stripLeft(QString &s,const QChar &c) + { + int iRemove = 0; + while(iRemove < s.length()) + { + if(s[iRemove] == c) + iRemove++; + else + break; + } + if(iRemove > 0)s.remove(0,iRemove); + } + + void detach(QString &sz) + { +#ifdef COMPILE_USE_QT4 + sz.resize(sz.length()); +#else + sz.setLength(sz.length()); +#endif + } + + const QChar * nullTerminatedArray(const QString &sz) + { + //sz.setLength(sz.length()); // detach! +#ifdef COMPILE_USE_QT4 + return sz.constData(); +#else + return (const QChar *)sz.ucs2(); // MAY BE NULL! +#endif + } + + void appendNumber(QString &s,double dReal) + { + char buffer[512]; + ::sprintf(buffer,"%f",dReal); + s.append(buffer); + } + + void appendNumber(QString &s,int iInteger) + { + char buffer[64]; + ::sprintf(buffer,"%d",iInteger); + s.append(buffer); + } + + void appendNumber(QString &s,kvi_i64_t iInteger) + { + char buffer[64]; + ::sprintf(buffer,"%ld",iInteger); + s.append(buffer); + } + + void appendNumber(QString &s,kvi_u64_t uInteger) + { + char buffer[64]; + ::sprintf(buffer,"%lu",uInteger); + s.append(buffer); + } + + void appendNumber(QString &s,unsigned int uInteger) + { + char buffer[64]; + ::sprintf(buffer,"%u",uInteger); + s.append(buffer); + } + + void vsprintf(QString &s,const QString &szFmt,kvi_va_list list) + { +#define MEMINCREMENT 32 + + int reallen = 0; + int allocsize = MEMINCREMENT; + + //s.setLength(allocsize); + + const QChar * fmt = nullTerminatedArray(szFmt); + if(!fmt) + { + s = QString::null; + return; + } + + QChar * buffer = (QChar *)kvi_malloc(sizeof(QChar) * allocsize); + //QChar * p = (QChar *)s.unicode(); + + char *argString; + long argValue; + unsigned long argUValue; + + //9999999999999999999999999999999\0 + char numberBuffer[32]; //enough ? 10 is enough for 32bit unsigned int... + char *pNumBuf; + unsigned int tmp; + + QChar * p = buffer; + +#define INCREMENT_MEM \ + { \ + allocsize += MEMINCREMENT; \ + buffer = (QChar *)kvi_realloc(buffer,sizeof(QChar) * allocsize); \ + p = buffer + reallen; \ + } + +#define INCREMENT_MEM_BY(numchars) \ + { \ + allocsize += numchars + MEMINCREMENT; \ + buffer = (QChar *)kvi_realloc(buffer,sizeof(QChar) * allocsize); \ + p = buffer + reallen; \ + } + + + for(; fmt->unicode() ; ++fmt) + { + if(reallen == allocsize)INCREMENT_MEM + + //copy up to a '%' + if(fmt->unicode() != '%') + { + *p++ = *fmt; + reallen++; + continue; + } + + ++fmt; //skip this '%' + switch(fmt->unicode()) + { + case 's': // char * string + { + argString = kvi_va_arg(list,char *); + if(!argString)argString = "[!NULL!]"; + QString str(argString); + if(str.isEmpty())continue; + int len = str.length(); + const QChar * ch = str.unicode(); + if(!ch)continue; + if((allocsize - reallen) < len)INCREMENT_MEM_BY(len) + while(len--)*p++ = *ch++; + reallen += str.length(); + continue; + } + case 'S': // KviStr * string + { + KviStr * str = kvi_va_arg(list,KviStr *); + if(!str)continue; + if((allocsize - reallen) < str->len())INCREMENT_MEM_BY(str->len()) + argString = str->ptr(); + while(*argString)*p++ = QChar(*argString++); + reallen += str->len(); + continue; + } + case 'Q': // QString * string + { + QString * str = kvi_va_arg(list,QString *); + if(!str)continue; + if(str->isEmpty())continue; + int len = str->length(); + const QChar * ch = str->unicode(); + if(!ch)continue; + if((allocsize - reallen) < len)INCREMENT_MEM_BY(len) + while(len--)*p++ = *ch++; + reallen += str->length(); + continue; + } + case 'c': //char + { + // + // I'm not sure about this... + // In the linux kernel source the + // unsigned char is extracted from an integer type. + // We assume that gcc stacks a char argument + // as sizeof(int) bytes value. + // Is this always true ? + // + *p++ = (char)kvi_va_arg(list,int); + reallen++; + continue; + } + case 'q': // QChar * + { + // + // I'm not sure about this... + // In the linux kernel source the + // unsigned char is extracted from an integer type. + // We assume that gcc stacks a char argument + // as sizeof(int) bytes value. + // Is this always true ? + // + *p++ = *((QChar *)kvi_va_arg(list,QChar *)); + reallen++; + continue; + } + case 'd': //signed integer + { + argValue = kvi_va_arg(list,int); + if(argValue < 0) + { //negative integer + *p++ = '-'; + reallen++; + argValue = -argValue; //need to have it positive + // most negative integer exception (avoid completely senseless (non digit) responses) + if(argValue < 0)argValue = 0; //we get -0 here + } + //write the number in a temporary buffer + pNumBuf = numberBuffer; + do { + tmp = argValue / 10; + *pNumBuf++ = argValue - (tmp * 10) + '0'; + } while((argValue = tmp)); + //copy now.... + argUValue = pNumBuf - numberBuffer; //length of the number string + if((allocsize - reallen) < (int)argUValue)INCREMENT_MEM_BY(argUValue) + do { *p++ = QChar(*--pNumBuf); } while(pNumBuf != numberBuffer); + reallen += argUValue; + continue; + } + case 'u': //unsigned integer + { + argUValue = kvi_va_arg(list,unsigned int); //many implementations place int here + //write the number in a temporary buffer + pNumBuf = numberBuffer; + do { + tmp = argUValue / 10; + *pNumBuf++ = argUValue - (tmp * 10) + '0'; + } while((argUValue = tmp)); + //copy now.... + argValue = pNumBuf - numberBuffer; //length of the number string + if((allocsize - reallen) < argValue)INCREMENT_MEM_BY(argValue) + do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer); + reallen += argValue; + continue; + } + case 'h': + case 'x': // hexadecimal unsigned integer + { + static char hexsmalldigits[]="0123456789abcdef"; + argUValue = kvi_va_arg(list,unsigned int); //many implementations place int here + //write the number in a temporary buffer + pNumBuf = numberBuffer; + do { + tmp = argUValue / 16; + *pNumBuf++ = hexsmalldigits[argUValue - (tmp * 16)]; + } while((argUValue = tmp)); + //copy now.... + argValue = pNumBuf - numberBuffer; //length of the number string + if((allocsize - reallen) < argValue)INCREMENT_MEM_BY(argValue) + do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer); + reallen += argValue; + continue; + } + case 'H': + case 'X': // hexadecimal unsigned integer + { + static char hexbigdigits[]="0123456789ABCDEF"; + argUValue = kvi_va_arg(list,unsigned int); //many implementations place int here + //write the number in a temporary buffer + pNumBuf = numberBuffer; + do { + tmp = argUValue / 16; + *pNumBuf++ = hexbigdigits[argUValue - (tmp * 16)]; + } while((argUValue = tmp)); + //copy now.... + argValue = pNumBuf - numberBuffer; //length of the number string + if((allocsize - reallen) < argValue)INCREMENT_MEM_BY(argValue) + do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer); + reallen += argValue; + continue; + } + default: //a normal percent followed by some char + { + *p++ = '%'; //write it + reallen++; + if(fmt->unicode()) + { + if(reallen == allocsize)INCREMENT_MEM + *p++ = *fmt; + reallen++; + } + continue; + } + } + } + + s.setUnicode(buffer,reallen); + kvi_free(buffer); + //s.squeeze(); + } + + + QString & sprintf(QString &s,const QString &szFmt,...) + { + kvi_va_list list; + kvi_va_start_by_reference(list,szFmt); + //print...with max 256 chars + KviQString::vsprintf(s,szFmt,list); + kvi_va_end(list); + return s; + } + + void appendFormatted(QString &s,const QString &szFmt,...) + { + QString tmp; + kvi_va_list list; + kvi_va_start_by_reference(list,szFmt); + //print...with max 256 chars + KviQString::vsprintf(tmp,szFmt,list); + kvi_va_end(list); + s.append(tmp); + } + + bool matchWildExpressionsCI(const QString &szM1,const QString &szM2) + { + //Matches two regular expressions containging wildcards (* and ?) + + // s1 + // m1 + // mask1 : *xor + // mask2 : xorand*xor + // m2 + // s2 + + // s2 + // m2 + // | + // XorT!xor@111.111.111.11 + // + // *!*@*.net + // | + // m1 + // s1 + // + +#ifdef COMPILE_USE_QT4 + const QChar * m1 = (const QChar *)szM1.constData(); + const QChar * m2 = (const QChar *)szM2.constData(); +#else + const QChar * m1 = (const QChar *)szM1.ucs2(); + const QChar * m2 = (const QChar *)szM2.ucs2(); +#endif + + if(!(m1 && m2 && (m1->unicode())))return false; + const QChar * savePos1 = 0; + const QChar * savePos2 = m2; + while(m1->unicode()) + { + //loop managed by m1 (initially first mask) + if(m1->unicode()=='*') + { + //Found a wildcard in m1 + savePos1 = ++m1; //move to the next char and save the position...this is our jolly + if(!savePos1->unicode())return true; //last was a wildcard , matches everything ahead... + savePos2 = m2+1; //next return state for the second string + continue; //and return + } + if(!m2->unicode())return false; //m2 finished and we had something to match here! +#ifdef COMPILE_USE_QT4 + if(m1->toLower()==m2->toLower()) +#else + if(m1->lower()==m2->lower()) +#endif + { + //chars matched + m1++; //Go ahead in the two strings + m2++; // + if((!(m1->unicode())) && m2->unicode() && savePos1) + { + //m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)... + //retry matching the string following the * from the savePos2 (one char ahead last time) + m1 = savePos1; //back to char after wildcard + m2 = savePos2; //back to last savePos2 + savePos2++; //next savePos2 will be next char + } + } else { + if(m2->unicode() == '*') + { + //A wlidcard in the second string + //Invert the game : mask1 <-> mask2 + //mask2 now leads the game... + savePos1 = m1; //aux + m1 = m2; //...swap + m2 = savePos1; //...swap + savePos1 = m1; //sync save pos1 + savePos2 = m2 + 1; //sync save pos2 + continue; //...and again + } + // m1 != m2 , m1 != * , m2 != * + if((m1->unicode() == '?') || (m2->unicode() == '?')) + { + m1++; + m2++; + if((!(m1->unicode())) && m2->unicode() && savePos1) + { + //m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)... + //retry matching the string following the * from the savePos2 (one char ahead last time) + m1 = savePos1; //back to char after wildcard + m2 = savePos2; //back to last savePos2 + savePos2++; //next savePos2 will be next char + } + } else { + if(savePos1) + { + //Have a jolly man...allow not matching... + m1 = savePos1; //go back to char after wildcard...need to rematch... + m2 = savePos2; //back to last savePos2 + savePos2++; //and set next savePos2 + } else return false; //No previous wildcards...not matched! + } + } + } + return (!(m2->unicode())); //m1 surely finished , so for the match , m2 must be finished too + } + + bool matchStringCI(const QString &szExp,const QString &szStr,bool bIsRegExp,bool bExact) + { + QString szWildcard; +#ifdef COMPILE_USE_QT4 + QChar* ptr=(QChar*)szExp.constData(); +#else + QChar* ptr=(QChar*)szExp.ucs2(); +#endif + if(!ptr) return 0; + while(ptr->unicode()) + { + if((ptr->unicode()=='[') || (ptr->unicode()==']')) + { + szWildcard.append("["); + szWildcard.append(*ptr); + szWildcard.append("]"); + } else { + szWildcard.append(*ptr); + } + ptr++; + } +#ifdef COMPILE_USE_QT4 + QRegExp re(szWildcard,Qt::CaseInsensitive,bIsRegExp ? QRegExp::RegExp : QRegExp::Wildcard); +#else + QRegExp re(szWildcard,false,!bIsRegExp); +#endif + if(bExact) return re.exactMatch(szStr); +#ifdef COMPILE_USE_QT4 + return re.indexIn(szStr) != -1; +#else + return re.search(szStr) != -1; +#endif + } + + bool matchStringCS(const QString &szExp,const QString &szStr,bool bIsRegExp,bool bExact) + { + QString szWildcard; +#ifdef COMPILE_USE_QT4 + QChar* ptr=(QChar*)szExp.constData(); +#else + QChar* ptr=(QChar*)szExp.ucs2(); +#endif + if(!ptr) return 0; + while(ptr->unicode()) + { + if((ptr->unicode()=='[')) // <-- hum ? + { + szWildcard.append("["); + szWildcard.append(*ptr); + szWildcard.append("]"); + } else { + szWildcard.append(*ptr); + } + ptr++; + } +#ifdef COMPILE_USE_QT4 + QRegExp re(szWildcard,Qt::CaseSensitive,bIsRegExp ? QRegExp::RegExp : QRegExp::Wildcard); +#else + QRegExp re(szWildcard,true,!bIsRegExp); +#endif + if(bExact) return re.exactMatch(szStr); +#ifdef COMPILE_USE_QT4 + return re.indexIn(szStr) != -1; +#else + return re.search(szStr) != -1; +#endif + } + + void cutFromFirst(QString &s,const QChar &c,bool bIncluded) + { +#ifdef COMPILE_USE_QT4 + int idx = s.indexOf(c); +#else + int idx = s.find(c); +#endif + if(idx == -1)return; + s.truncate(bIncluded ? idx : idx + 1); + } + + void cutFromLast(QString &s,const QChar &c,bool bIncluded) + { +#ifdef COMPILE_USE_QT4 + int idx = s.lastIndexOf(c); +#else + int idx = s.findRev(c); +#endif + if(idx == -1)return; + s.truncate(bIncluded ? idx : idx + 1); + } + + void cutToFirst(QString &s,const QChar &c,bool bIncluded,bool bClearIfNotFound) + { +#ifdef COMPILE_USE_QT4 + int idx = s.indexOf(c); +#else + int idx = s.find(c); +#endif + if(idx == -1) + { + if(bClearIfNotFound)s = ""; + return; + } + s.remove(0,bIncluded ? idx + 1 : idx); + } + + void cutToLast(QString &s,const QChar &c,bool bIncluded,bool bClearIfNotFound) + { +#ifdef COMPILE_USE_QT4 + int idx = s.lastIndexOf(c); +#else + int idx = s.findRev(c); +#endif + if(idx == -1) + { + if(bClearIfNotFound)s = ""; + return; + } + s.remove(0,bIncluded ? idx + 1 : idx); + } + + void cutFromFirst(QString &s,const QString &c,bool bIncluded) + { +#ifdef COMPILE_USE_QT4 + int idx = s.indexOf(c); +#else + int idx = s.find(c); +#endif + if(idx == -1)return; + s.truncate(bIncluded ? idx : idx + c.length()); + } + + void cutFromLast(QString &s,const QString &c,bool bIncluded) + { +#ifdef COMPILE_USE_QT4 + int idx = s.lastIndexOf(c); +#else + int idx = s.findRev(c); +#endif + if(idx == -1)return; + s.truncate(bIncluded ? idx : idx + c.length()); + } + + void cutToFirst(QString &s,const QString &c,bool bIncluded,bool bClearIfNotFound) + { +#ifdef COMPILE_USE_QT4 + int idx = s.indexOf(c); +#else + int idx = s.find(c); +#endif + if(idx == -1) + { + if(bClearIfNotFound)s = ""; + return; + } + s.remove(0,bIncluded ? idx + c.length() : idx); + } + + void cutToLast(QString &s,const QString &c,bool bIncluded,bool bClearIfNotFound) + { +#ifdef COMPILE_USE_QT4 + int idx = s.lastIndexOf(c); +#else + int idx = s.findRev(c); +#endif + if(idx == -1) + { + if(bClearIfNotFound)s = ""; + return; + } + s.remove(0,bIncluded ? idx + c.length() : idx); + } + + QString upperISO88591(const QString &szSrc) + { + const QChar * c = nullTerminatedArray(szSrc); + if(!c) + { + QString ret; + return ret; + } + QChar * buffer = (QChar *)kvi_malloc(sizeof(QChar) * szSrc.length()); + QChar * b = buffer; + unsigned short us = c->unicode(); + while(us) + { + if(us < 256) + *b=QChar((unsigned short)iso88591_toUpper_map[us]); + else + *b = *c; + c++; + b++; + us = c->unicode(); + } + QString ret(buffer,szSrc.length()); + kvi_free(buffer); + return ret; + } + + QString lowerISO88591(const QString &szSrc) + { + const QChar * c = nullTerminatedArray(szSrc); + if(!c) + { + QString ret; + return ret; + } + QChar * buffer = (QChar *)kvi_malloc(sizeof(QChar) * szSrc.length()); + QChar * b = buffer; + unsigned short us = c->unicode(); + while(us) + { + if(us < 256) + { + *b=QChar((unsigned short)iso88591_toLower_map[us]); + } else + *b = *c; + c++; + b++; + us = c->unicode(); + } + QString ret(buffer,szSrc.length()); + kvi_free(buffer); + return ret; + } + + void transliterate(QString &s,const QString &szToFind,const QString &szReplacement) + { + int i=0; + int il = MY_MIN(szToFind.length(),szReplacement.length()); + while(i < il) + { + int k=0; + int kl = s.length(); + while(k < kl) + { + if(s[k] == szToFind[i])s[k] = szReplacement[i]; + k++; + } + i++; + } + } + + static char hexdigits[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; + + void bufferToHex(QString &szRetBuffer,const unsigned char * buffer,unsigned int len) + { +#ifdef COMPILE_USE_QT4 + szRetBuffer.resize(len * 2); +#else + szRetBuffer.setLength(len * 2); +#endif + unsigned int i=0; + while(i < (len*2)) + { + szRetBuffer[int(i)] = QChar( (unsigned int) hexdigits[(*buffer) / 16] ); + i++; + szRetBuffer[int(i)] = QChar( (unsigned int)hexdigits[(*buffer) % 16] ); + i++; + buffer++; + } + } +}; diff --git a/src/kvilib/core/kvi_qstring.h b/src/kvilib/core/kvi_qstring.h new file mode 100644 index 00000000..c82063e9 --- /dev/null +++ b/src/kvilib/core/kvi_qstring.h @@ -0,0 +1,293 @@ +#ifndef _KVI_QSTRING_H_ +#define _KVI_QSTRING_H_ + +//============================================================================= +// +// File : kvi_qstring.h +// Creation date : Mon Aug 04 2003 13:36:33 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2003-2006 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +//============================================================================= +// +// Helper functions for the QString class +// +//============================================================================= + +#include "kvi_settings.h" +#include "kvi_inttypes.h" +#include "kvi_stdarg.h" +#include "kvi_qcstring.h" + +#include <qstring.h> + +/// +/// \namespace KviQString +/// +/// \brief A namespace for QString helper functions +/// +/// This namespace contains several helper functions +/// that are used when dealing with QString. +/// +namespace KviQString +{ + extern KVILIB_API QString makeSizeReadable(size_t size); + extern KVILIB_API bool equalCS(const QString &sz1,const QString &sz2); + extern KVILIB_API bool equalCI(const QString &sz1,const QString &sz2); + extern KVILIB_API bool equalCS(const QString &sz1,const char * sz2); + extern KVILIB_API bool equalCI(const QString &sz1,const char * sz2); + // sz2 is assumed to be null terminated here! + extern KVILIB_API bool equalCI(const QString &sz1,const QChar * sz2); + inline bool equalCS(const char * sz1,const QString &sz2) + { return equalCS(sz2,sz1); }; + inline bool equalCI(const char * sz1,const QString &sz2) + { return equalCI(sz2,sz1); }; + // sz1 is assumed to be null terminated here! + inline bool equalCI(const QChar * sz1,const QString &sz2) + { return equalCI(sz2,sz1); }; + + extern KVILIB_API bool equalCSN(const QString &sz1,const QString &sz2,unsigned int len); + extern KVILIB_API bool equalCIN(const QString &sz1,const QString &sz2,unsigned int len); + extern KVILIB_API bool equalCSN(const QString &sz1,const char * sz2,unsigned int len); + extern KVILIB_API bool equalCIN(const QString &sz1,const char * sz2,unsigned int len); + // sz2 is assumed to be null terminated here! + extern KVILIB_API bool equalCIN(const QString &sz1,const QChar * sz2,unsigned int len); + inline bool equalCSN(const char * sz1,const QString &sz2,unsigned int len) + { return equalCSN(sz2,sz1,len); }; + inline bool equalCIN(const char * sz1,const QString &sz2,unsigned int len) + { return equalCIN(sz2,sz1,len); }; + // sz1 is assumed to be null terminated here! + inline bool equalCIN(const QChar * sz1,const QString &sz2,unsigned int len) + { return equalCIN(sz2,sz1,len); }; + + //note that greater here means that come AFTER in the alphabetic order + // return < 0 ---> str1 < str2 + // return = 0 ---> str1 = str2 + // return > 0 ---> str1 > str2 + extern KVILIB_API int cmpCI(const QString &sz1,const QString &sz2); + extern KVILIB_API int cmpCIN(const QString &sz1,const QString &sz2,unsigned int len); + extern KVILIB_API int cmpCS(const QString &sz1,const QString &sz2); + + extern KVILIB_API void detach(QString &sz); + + // this makes the QString sz appear as a null terminated array + // it MAY RETURN 0 when the QString is null! + extern KVILIB_API const QChar * nullTerminatedArray(const QString &sz); + + inline bool lastCharIs(QString &szString,const QChar &c) + { return szString.endsWith(c); }; + + extern KVILIB_API void ensureLastCharIs(QString &szString,const QChar &c); + + // wild expression matching + extern KVILIB_API bool matchWildExpressionsCI(const QString &szM1,const QString &szM2); + // wild or regexp matching + extern KVILIB_API bool matchStringCI(const QString &szExp,const QString &szStr,bool bIsRegExp = false,bool bExact = false); + extern KVILIB_API bool matchStringCS(const QString &szExp,const QString &szStr,bool bIsRegExp = false,bool bExact = false); + + extern KVILIB_API void vsprintf(QString &s,const QString &szFmt,kvi_va_list list); + extern KVILIB_API QString & sprintf(QString &s,const QString &szFmt,...); + extern KVILIB_API void stripRightWhiteSpace(QString &s); + extern KVILIB_API void stripLeft(QString &s,const QChar &c); + extern KVILIB_API void stripRight(QString &s,const QChar &c); + extern KVILIB_API void appendFormatted(QString &s,const QString &szFmt,...); + extern KVILIB_API void appendNumber(QString &s,double dReal); + extern KVILIB_API void appendNumber(QString &s,kvi_i64_t iInteger); + extern KVILIB_API void appendNumber(QString &s,int iInteger); + extern KVILIB_API void appendNumber(QString &s,unsigned int uInteger); + extern KVILIB_API void appendNumber(QString &s,kvi_u64_t uInteger); + + extern KVILIB_API void cutFromFirst(QString &s,const QChar &c,bool bIncluded = true); + extern KVILIB_API void cutFromLast(QString &s,const QChar &c,bool bIncluded = true); + extern KVILIB_API void cutToFirst(QString &s,const QChar &c,bool bIncluded = true,bool bClearIfNotFound = false); + extern KVILIB_API void cutToLast(QString &s,const QChar &c,bool bIncluded = true,bool bClearIfNotFound = false); + extern KVILIB_API void cutFromFirst(QString &s,const QString &c,bool bIncluded = true); + extern KVILIB_API void cutFromLast(QString &s,const QString &c,bool bIncluded = true); + extern KVILIB_API void cutToFirst(QString &s,const QString &c,bool bIncluded = true,bool bClearIfNotFound = false); + extern KVILIB_API void cutToLast(QString &s,const QString &c,bool bIncluded = true,bool bClearIfNotFound = false); + + extern KVILIB_API QString upperISO88591(const QString &szSrc); + extern KVILIB_API QString lowerISO88591(const QString &szSrc); + extern KVILIB_API QString getToken(QString &szString,const QChar &sep); + + extern KVILIB_API void transliterate(QString &s,const QString &szToFind,const QString &szReplacement); + + extern KVILIB_API void bufferToHex(QString &szRetBuffer,const unsigned char * buffer,unsigned int len); + + // a global empty string (note that this is ALSO NULL under Qt 3.x) + extern KVILIB_API const QString empty; + + /// + /// A portability wrapper which with Qt3 and Qt4. + /// Returns a lowcase version of the parameter string. + /// + inline QString toLower(const QString &s) + { +#ifdef COMPILE_USE_QT4 + return s.toLower(); +#else + return s.lower(); +#endif + } + + inline int find(const QString &s,QChar c,int index = 0,bool cs = true) + { +#ifdef COMPILE_USE_QT4 + return s.indexOf(c,index,cs ? Qt::CaseSensitive : Qt::CaseInsensitive); +#else + return s.find(c,index,cs); +#endif + } + + inline int find(const QString &s,char c,int index = 0,bool cs = true) + { +#ifdef COMPILE_USE_QT4 + return s.indexOf(c,index,cs ? Qt::CaseSensitive : Qt::CaseInsensitive); +#else + return s.find(c,index,cs); +#endif + } + + inline int find(const QString &s,const QString & str,int index = 0,bool cs = true) + { +#ifdef COMPILE_USE_QT4 + return s.indexOf(str,index,cs ? Qt::CaseSensitive : Qt::CaseInsensitive); +#else + return s.find(str,index,cs); +#endif + } + + inline int find(const QString &s,const QRegExp & rx,int index = 0) + { +#ifdef COMPILE_USE_QT4 + return s.indexOf(rx,index); +#else + return s.find(rx,index); +#endif + } + + inline int find(const QString &s,const char * str,int index = 0) + { +#ifdef COMPILE_USE_QT4 + return s.indexOf(QString(str),index); +#else + return s.find(str,index); +#endif + } + + inline int findRev(const QString &s,QChar c,int index = -1,bool cs = true) + { +#ifdef COMPILE_USE_QT4 + return s.lastIndexOf(c,index,cs ? Qt::CaseSensitive : Qt::CaseInsensitive); +#else + return s.findRev(c,index,cs); +#endif + } + + inline int findRev(const QString &s,char c,int index = -1,bool cs = true) + { +#ifdef COMPILE_USE_QT4 + return s.lastIndexOf(c,index,cs ? Qt::CaseSensitive : Qt::CaseInsensitive); +#else + return s.findRev(c,index,cs); +#endif + } + + inline int findRev(const QString &s,const QString & str,int index = -1,bool cs = true) + { +#ifdef COMPILE_USE_QT4 + return s.lastIndexOf(str,index,cs ? Qt::CaseSensitive : Qt::CaseInsensitive); +#else + return s.findRev(str,index,cs); +#endif + } + + inline int findRev(const QString &s,const QRegExp & rx,int index = -1) + { +#ifdef COMPILE_USE_QT4 + return s.lastIndexOf(rx,index); +#else + return s.findRev(rx,index); +#endif + } + + inline int findRev(const QString &s,const char * str,int index = -1) + { +#ifdef COMPILE_USE_QT4 + return s.lastIndexOf(QString(str),index); +#else + return s.findRev(str,index); +#endif + } + + inline QString trimmed(const QString &s) + { +#ifdef COMPILE_USE_QT4 + return s.trimmed(); +#else + return s.stripWhiteSpace(); +#endif + } + + // WARNING: DO NOT USE CONSTRUCTS LIKE char * c = KviQString::toUtf8(something).data(); + // They are dangerous since with many compilers the returned string gets destroyed + // at the end of the instruction and the c pointer gets thus invalidated. + // Use + // KviQCString tmp = KviQString::toUtf8(something); + // char * c = tmp.data(); + // instead. + // Yes, I know that it sucks, but it's the only way to + // transit to qt 4.x more or less cleanly... + inline KviQCString toUtf8(const QString &s) + { +#ifdef COMPILE_USE_QT4 + return s.toUtf8(); +#else + return s.utf8(); +#endif + } + + inline KviQCString toLocal8Bit(const QString &s) + { + return s.local8Bit(); + } + + inline kvi_i64_t toI64(QString &szNumber,bool * bOk) + { +#if SIZEOF_LONG_INT == 8 + return szNumber.toLong(bOk); +#else + return szNumber.toLongLong(bOk); +#endif + } + + inline kvi_u64_t toU64(QString &szNumber,bool * bOk) + { +#if SIZEOF_LONG_INT == 8 + return szNumber.toULong(bOk); +#else + return szNumber.toULongLong(bOk); +#endif + } +}; + +// QT4SUX: Because QString::null is gone. QString() is SLOWER than QString::null since it invokes a constructor and destructor. + +#endif //!_KVI_QSTRING_H_ diff --git a/src/kvilib/core/kvi_strasm.h b/src/kvilib/core/kvi_strasm.h new file mode 100644 index 00000000..5d3b19ca --- /dev/null +++ b/src/kvilib/core/kvi_strasm.h @@ -0,0 +1,194 @@ +#ifndef _KVI_STRASM_H_ +#define _KVI_STRASM_H_ + +//============================================================================= +// +// File : kvi_strasm.h +// Creation date : Sun Jun 18 2000 18:38:26 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2000 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +//============================================================================= +// +// Inline assembly implementations of the commonly used string functions +// These will work only on i386 based machines and can be compiled +// only by gcc +// +//============================================================================= + +extern inline bool kvi_strEqualCS(const char * str1,const char * str2) +{ + // An instruction pattern is really useful in this case. + // When inlining, GCC can optimize to load esi and edi + // directly with the strings , without pushing and getting it + // from the stack... + register bool eax; + __asm__ __volatile__ ( + " cld\n" + "1:\n" + " lodsb %%ds:(%%esi),%%al\n" + " scasb %%es:(%%edi),%%al\n" + " jne 2f\n" + " testb %%al,%%al\n" + " jne 1b\n" + " movl $0x1,%%eax\n" + " jmp 3f\n" + "2:\n" + " xorl %%eax,%%eax\n" + "3:" + : "=a" (eax), "=&S" (str1), "=&D" (str2) + : "1" (str1), "2" (str2) + ); + return eax; +} + +extern inline bool kvi_strEqualCSN(const char * str1,const char * str2,int len) +{ + register bool eax; + __asm__ __volatile__ ( + "1:\n" + " decl %3\n" + " js 2f\n" + " movb (%1),%%al\n" + " incl %1\n" + " cmpb %%al,(%2)\n" + " jne 3f\n" + " incl %2\n" + " testb %%al,%%al\n" + " jne 1b\n" + "2:\n" + " movl $0x1,%%eax\n" + " jmp 4f\n" + "3:\n" + " xorl %%eax,%%eax\n" + "4:\n" + : "=a" (eax), "=r" (str1), "=r" (str2), "=r" (len) + : "1" (str1), "2" (str2), "3" (len) + ); + return eax; +} + +// OPTIMIZATION +// The following two functions are used to compare a variable string with one in that +// only A-Z<->a-z case insensivity is significant. +// For example +// kvi_strEqualNoLocalCI("a string that does not contain any strange char",str2) +// will always give the correct result +// These will NOT work with localizable characters: +// 'a' with umlaut will be not equal to 'A' with umlaut + +extern inline bool kvi_strEqualNoLocaleCI(const char *str1,const char *str2) +{ + // Trivial implementation + // Ignores completely locales....only A-Z chars are transformed to a-z + // Anyway...it will work for IRC :) + register int reg; + register bool eax; + __asm__ __volatile__ ( + "1:\n" + " movb (%2),%%al\n" + " cmpb $65,%%al\n" + " jb 2f\n" + " cmpb $90,%%al\n" + " ja 2f\n" + " addb $32,%%al\n" + "2:\n" + " movb (%3),%b1\n" + " cmpb $65,%b1\n" + " jb 3f\n" + " cmpb $90,%b1\n" + " ja 3f\n" + " addb $32,%b1\n" + "3:\n" + " cmpb %%al,%b1\n" + " jne 4f\n" + " incl %2\n" + " incl %3\n" + " testb %%al,%%al\n" + " jne 1b\n" + " movl $1,%%eax\n" + " jmp 5f\n" + "4:\n" + " xorl %%eax,%%eax\n" + "5:\n" + : "=a" (eax), "=q" (reg), "=r" (str1), "=r" (str2) + : "2" (str1), "3" (str2) + ); + return eax; +} + +extern inline bool kvi_strEqualNoLocaleCIN(const char *str1,const char *str2,int len) +{ + + register int reg; + register bool eax; + __asm__ __volatile__ ( + "1:\n" + " decl %4\n" + " js 4f\n" + " movb (%2),%%al\n" + " cmpb $65,%%al\n" + " jb 2f\n" + " cmpb $90,%%al\n" + " ja 2f\n" + " addb $32,%%al\n" + "2:\n" + " movb (%3),%b1\n" + " cmpb $65,%b1\n" + " jb 3f\n" + " cmpb $90,%b1\n" + " ja 3f\n" + " addb $32,%b1\n" + "3:\n" + " cmpb %%al,%b1\n" + " jne 5f\n" + " incl %2\n" + " incl %3\n" + " testb %%al,%%al\n" + " jne 1b\n" + "4:\n" + " movl $1,%%eax\n" + " jmp 6f\n" + "5:\n" + " xorl %%eax,%%eax\n" + "6:\n" + : "=a" (eax), "=q" (reg), "=r" (str1), "=r" (str2), "=r" (len) + : "2" (str1), "3" (str2), "4" (len) + ); + return eax; +} + + +extern inline int kvi_strLen(const char * str) +{ + register int ecx; + __asm__ __volatile__( + " cld\n" + " repne\n" + " scasb\n" + " notl %0\n" + " decl %0" + : "=c" (ecx), "=&D" (str) + : "0" (0xffffffff), "1" (str), "a" (0) + ); + return ecx; +} + +#endif //_KVI_STRASM_H_ diff --git a/src/kvilib/core/kvi_string.cpp b/src/kvilib/core/kvi_string.cpp new file mode 100644 index 00000000..3f201352 --- /dev/null +++ b/src/kvilib/core/kvi_string.cpp @@ -0,0 +1,3063 @@ +//============================================================================= +// +// File : kvi_string.cpp +// Creation date : Fri Mar 19 1999 03:20:45 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2001 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#define __KVILIB__ + + +#define _KVI_DEBUG_CHECK_RANGE_ +#include "kvi_debug.h" + +#define _KVI_STRING_CPP_ +#include "kvi_string.h" + +#include "kvi_memmove.h" +#include "kvi_malloc.h" + +#include "kvi_qstring.h" + +kvi_wslen_t kvi_wstrlen(const kvi_wchar_t * str) +{ + const kvi_wchar_t * ptr = str; + while(*ptr)ptr++; + return (ptr - str); +} + + +// %s = Latin1 char string (can't be null) +// %d = signed int (short,char) +// %u = unsigned int (short,char) +// %c = char value (kvi_wchar_t value) + +// %f = double value + +// %w = kvi_wchar_t string (can't be null) + +// %S = Latin1 KviStr pointer (#ifdef WSTRINGCONFIG_USE_KVISTR) : can't be NULL! +// %W = KviWStr pointer : can't be NULL! +// %Q = QString pointer : can't be NULL! + +#define _WSTRING_WMEMCPY(_dst,_src,_len) kvi_fastmoveodd((void *)(_dst),(const void *)(_src),sizeof(kvi_wchar_t) * (_len)) +#define _WSTRING_STRLEN(_str) kvi_strLen(_str) + +#define WVSNPRINTF_BODY \ +\ + register kvi_wchar_t *p; \ + long int argValue; \ + unsigned long argUValue; \ +\ + kvi_wchar_t numberBuffer[32]; \ + kvi_wchar_t *pNumBuf; \ + unsigned int tmp; \ +\ + for(p=buffer ; *fmt ; ++fmt) \ + { \ + if(len < 1)return (-1); \ +\ + if(*fmt != '%') \ + { \ + *p++ = *fmt; \ + --len; \ + continue; \ + } \ +\ + ++fmt; \ +\ + switch(*fmt) \ + { \ + case 's': \ + { \ + char * argString = kvi_va_arg(list,char *); \ + argValue = (int)_WSTRING_STRLEN(argString); \ + if(len <= argValue)return (-1); \ + while(*argString)*p++ = *argString++; \ + len -= argValue; \ + } \ + break; \ + case 'S': \ + { \ + KviStr * pString = kvi_va_arg(list,KviStr *); \ + char * argString = pString->ptr(); \ + if(len <= ((int)(pString->len())))return (-1); \ + while(*argString)*p++ = *argString++; \ + len -= pString->len(); \ + } \ + break; \ + case 'Q': \ + { \ + QString * pString = kvi_va_arg(list,QString *); \ + if(pString->length() > 0) \ + { \ + if(len <= ((int)(pString->length())))return (-1); \ + _WSTRING_WMEMCPY(p,pString->unicode(),pString->length()); \ + p += pString->length(); \ + len -= pString->length(); \ + } \ + } \ + break; \ + case 'd': \ + argValue = kvi_va_arg(list,int); \ + if(argValue < 0) \ + { \ + *p++ = '-'; \ + if(--len == 0)return (-1); \ + argValue = -argValue; \ + if(argValue < 0)argValue = 0; \ + } \ + pNumBuf = numberBuffer; \ + do { \ + tmp = argValue / 10; \ + *pNumBuf++ = argValue - (tmp * 10) + '0'; \ + } while((argValue = tmp)); \ + argUValue = pNumBuf - numberBuffer; \ + if(((unsigned int)len) <= argUValue)return (-1); \ + do { \ + *p++ = *--pNumBuf; \ + } while(pNumBuf != numberBuffer); \ + len -= argUValue; \ + break; \ + case 'u': \ + argUValue = kvi_va_arg(list,unsigned int); \ + pNumBuf = numberBuffer; \ + do { \ + tmp = argUValue / 10; \ + *pNumBuf++ = argUValue - (tmp * 10) + '0'; \ + } while((argUValue = tmp)); \ + argValue = pNumBuf - numberBuffer; \ + if(len <= argValue)return (-1); \ + do { \ + *p++ = *--pNumBuf; \ + } while(pNumBuf != numberBuffer); \ + len -= argValue; \ + break; \ + case 'f': \ + { \ + double dVal = (double)kvi_va_arg(list,double); \ + char sprintfBuffer[32]; \ + argValue = sprintf(sprintfBuffer,"%f",dVal); \ + if(len <= argValue)return (-1); \ + char * pSprintfBuffer = sprintfBuffer; \ + while(*pSprintfBuffer)*p++ = *pSprintfBuffer++; \ + len -= argValue; \ + } \ + break; \ + case 'c': \ + *p++ = (kvi_wchar_t)kvi_va_arg(list,int); \ + --len; \ + break; \ + default: \ + *p++ = '%'; \ + if(--len == 0)return (-1); \ + if(*fmt){ \ + *p++ = *fmt; \ + --len; \ + } \ + break; \ + } \ + continue; \ + } \ + if(len < 1)return (-1); \ + *p = 0; \ + return p-buffer; + +int kvi_wvsnprintcf(kvi_wchar_t *buffer,kvi_wslen_t len,const char *fmt,kvi_va_list list) +{ + WVSNPRINTF_BODY +} + +int kvi_wvsnprintf(kvi_wchar_t *buffer,kvi_wslen_t len,const kvi_wchar_t *fmt,kvi_va_list list) +{ + WVSNPRINTF_BODY +} + +bool kvi_qstringEqualCI(const QString &s1,const QString &s2) +{ + const QChar * p1 = s1.unicode(); + const QChar * p2 = s2.unicode(); + int l = s1.length() < s2.length() ? s1.length() : s2.length(); +#ifdef COMPILE_USE_QT4 + while(l-- && (p1->toLower() == p2->toLower()))p1++,p2++; +#else + while(l-- && (p1->lower() == p2->lower()))p1++,p2++; +#endif + if(l==-1)return true; + return false; +} + +bool kvi_matchStringCI(register const char * exp,register const char * str) +{ + // a + // . + // exp = a*x?mem*a + // str = arexoxmexamemizazv + // . + // n + const char * afterWild = 0; + const char * nextStrToCheck = 0; + + while(*exp) + { + if(*exp == '*') + { + // exp is a wildcard... + afterWild = ++exp; + nextStrToCheck = str + 1; + if(!(*exp))return true; // and it's the last char in the string: matches everything ahead + continue; + } + + if(!(*str))return false; // str finished but we had something to match :( + + if(tolower(*exp) == tolower(*str)) + { + // chars matched + ++exp; + ++str; + if((!(*exp)) && *str)goto check_recovery; + continue; + } + + if(*exp == '?') + { + // any-char wildcard + ++exp; + ++str; + continue; + } + +check_recovery: + // chars unmatched!!! + if(afterWild) + { + // we had a wildcard in exp... + // let's use this jolly then + exp = afterWild; + str = nextStrToCheck; + nextStrToCheck++; + // and try to compare now + continue; + } + + return false; // no match :( + } + return (!(*str)); +} + + +bool kvi_matchStringCS(register const char * exp,register const char * str) +{ + // a + // . + // exp = a*x?mem*a + // str = arexoxmexamemizazv + // . + // n + const char * afterWild = 0; + const char * nextStrToCheck = 0; + + while(*exp) + { + if(*exp == '*') + { + // exp is a wildcard... + afterWild = ++exp; + nextStrToCheck = str + 1; + if(!(*exp))return true; // and it's the last char in the string: matches everything ahead + continue; + } + + if(!(*str))return false; // str finished but we had something to match :( + + if(*exp == *str) + { + // chars matched + ++exp; + ++str; + if((!(*exp)) && *str)goto check_recovery; + continue; + } + + if(*exp == '?') + { + // any-char wildcard + ++exp; + ++str; + continue; + } + +check_recovery: + // chars unmatched!!! + if(afterWild) + { + // we had a wildcard in exp... + // let's use this jolly then + exp = afterWild; + str = nextStrToCheck; + nextStrToCheck++; + // and try to compare now + continue; + } + + return false; // no match :( + } + return (!(*str)); +} + + + +bool kvi_matchStringWithTerminator(register const char * exp,register const char * str,char terminator,const char ** r1,const char ** r2) +{ +#define NOT_AT_END(__str) (*__str && (*__str != terminator)) + + // a + // . + // exp = a*x?mem*a + // str = arexoxmexamemizazv + // . + // n + const char * afterWild = 0; + const char * nextStrToCheck = 0; + + while(NOT_AT_END(exp)) + { + if(*exp == '*') + { + // exp is a wildcard... + afterWild = ++exp; + nextStrToCheck = str + 1; + if(!(NOT_AT_END(exp))) + { + while(NOT_AT_END(str))str++; + *r1 = exp; + *r2 = str; + return true; // and it's the last char in the string: matches everything ahead + } + continue; + } + + if(!(*str))return false; // str finished but we had something to match :( + + if(tolower(*exp) == tolower(*str)) + { + // chars matched + ++exp; + ++str; + if((!(NOT_AT_END(exp))) && NOT_AT_END(str))goto check_recovery; + continue; + } + + if(*exp == '?') + { + // any-char wildcard + ++exp; + ++str; + continue; + } + +check_recovery: + // chars unmatched!!! + if(afterWild) + { + // we had a wildcard in exp... + // let's use this jolly then + exp = afterWild; + str = nextStrToCheck; + nextStrToCheck++; + // and try to compare now + continue; + } + + return false; // no match :( + } + *r1 = exp; + *r2 = str; + return (!(NOT_AT_END(str))); + +#undef NOT_AT_END +} + +bool kvi_matchWildExpr(register const char *m1,register const char *m2) +{ + //Matches two regular expressions containging wildcards (* and ?) + + // s1 + // m1 + // mask1 : *xor + // mask2 : xorand*xor + // m2 + // s2 + + // s2 + // m2 + // | + // XorT!xor@111.111.111.11 + // + // *!*@*.net + // | + // m1 + // s1 + // + + if(!(m1 && m2 && (*m1)))return false; + const char * savePos1 = 0; + const char * savePos2 = m2; + while(*m1) + { + //loop managed by m1 (initially first mask) + if(*m1=='*') + { + //Found a wildcard in m1 + savePos1 = ++m1; //move to the next char and save the position...this is our jolly + if(!*savePos1)return true; //last was a wildcard , matches everything ahead... + savePos2 = m2+1; //next return state for the second string + continue; //and return + } + if(!(*m2))return false; //m2 finished and we had something to match here! + if(tolower(*m1)==tolower(*m2)) + { + //chars matched + m1++; //Go ahead in the two strings + m2++; // + if((!(*m1)) && *m2 && savePos1) + { + //m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)... + //retry matching the string following the * from the savePos2 (one char ahead last time) + m1 = savePos1; //back to char after wildcard + m2 = savePos2; //back to last savePos2 + savePos2++; //next savePos2 will be next char + } + } else { + if(*m2 == '*') + { + //A wlidcard in the second string + //Invert the game : mask1 <-> mask2 + //mask2 now leads the game... + savePos1 = m1; //aux + m1 = m2; //...swap + m2 = savePos1; //...swap + savePos1 = m1; //sync save pos1 + savePos2 = m2 + 1; //sync save pos2 + continue; //...and again + } + // m1 != m2 , m1 != * , m2 != * + if((*m1 == '?') || (*m2 == '?')) + { + m1++; + m2++; + if((!(*m1)) && *m2 && savePos1) + { + //m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)... + //retry matching the string following the * from the savePos2 (one char ahead last time) + m1 = savePos1; //back to char after wildcard + m2 = savePos2; //back to last savePos2 + savePos2++; //next savePos2 will be next char + } + } else { + if(savePos1) + { + //Have a jolly man...allow not matching... + m1 = savePos1; //go back to char after wildcard...need to rematch... + m2 = savePos2; //back to last savePos2 + savePos2++; //and set next savePos2 + } else return false; //No previous wildcards...not matched! + } + } + } + return (!(*m2)); //m1 surely finished , so for the match , m2 must be finished too + +} + +/* + + WARNING: Don't remove: working code but actually unused in KVIrc + Later it might become useful + +bool kvi_matchWildExprCS(register const char *m1,register const char *m2) +{ + if(!(m1 && m2 && (*m1)))return false; + const char * savePos1 = 0; + const char * savePos2 = m2; + while(*m1){ //loop managed by m1 (initially first mask) + if(*m1=='*'){ + //Found a wildcard in m1 + savePos1 = ++m1; //move to the next char and save the position...this is our jolly + if(!*savePos1)return true; //last was a wildcard , matches everything ahead... + savePos2 = m2+1; //next return state for the second string + continue; //and return + } + if(!(*m2))return false; //m2 finished and we had something to match here! + if((*m1)==(*m2)){ + //chars matched + m1++; //Go ahead in the two strings + m2++; // + if((!(*m1)) && *m2 && savePos1){ + //m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)... + //retry matching the string following the * from the savePos2 (one char ahead last time) + m1 = savePos1; //back to char after wildcard + m2 = savePos2; //back to last savePos2 + savePos2++; //next savePos2 will be next char + } + } else { + if(*m2 == '*'){ + //A wlidcard in the second string + //Invert the game : mask1 <-> mask2 + //mask2 now leads the game... + savePos1 = m1; //aux + m1 = m2; //...swap + m2 = savePos1; //...swap + savePos1 = m1; //sync save pos1 + savePos2 = m2 + 1; //sync save pos2 + continue; //...and again + } + if(savePos1){ //Have a jolly man...allow not matching... + m1 = savePos1; //go back to char after wildcard...need to rematch... + m2 = savePos2; //back to last savePos2 + savePos2++; //and set next savePos2 + } else return false; //No previous wildcards...not matched! + } + } + return (!(*m2)); //m1 surely finished , so for the match , m2 must be finished too + +} +*/ + +bool kvi_matchWildExprWithTerminator(register const char *m1,register const char *m2,char terminator, + const char ** r1,const char ** r2) +{ + //Matches two regular expressions containging wildcards + +#define NOT_AT_END(__str) (*__str && (*__str != terminator)) + + bool bSwapped = false; + if(!(m1 && m2 && (NOT_AT_END(m1))))return false; + const char * savePos1 = 0; + const char * savePos2 = m2; + while(NOT_AT_END(m1)) + { + //loop managed by m1 (initially first mask) + if(*m1=='*') + { + //Found a wildcard in m1 + savePos1 = ++m1; //move to the next char and save the position...this is our jolly + if(!NOT_AT_END(savePos1)) + { + //last was a wildcard , matches everything ahead... + while(NOT_AT_END(m2))m2++; + *r1 = bSwapped ? m2 : m1; + *r2 = bSwapped ? m1 : m2; + return true; + } + savePos2 = m2+1; //next return state for the second string + continue; //and return + } + if(!NOT_AT_END(m2))return false; //m2 finished and we had something to match here! + if(tolower(*m1)==tolower(*m2)) + { + //chars matched + m1++; //Go ahead in the two strings + m2++; // + if((!NOT_AT_END(m1)) && NOT_AT_END(m2) && savePos1) + { + //m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)... + //retry matching the string following the * from the savePos2 (one char ahead last time) + m1 = savePos1; //back to char after wildcard + m2 = savePos2; //back to last savePos2 + savePos2++; //next savePos2 will be next char + } + } else { + if(*m2 == '*') + { + //A wlidcard in the second string + //Invert the game : mask1 <-> mask2 + //mask2 now leads the game... + bSwapped = !bSwapped; + savePos1 = m1; //aux + m1 = m2; //...swap + m2 = savePos1; //...swap + savePos1 = m1; //sync save pos1 + savePos2 = m2 + 1; //sync save pos2 + continue; //...and again + } + // m1 != m2 , m1 != * , m2 != * + if((*m1 == '?') || (*m2 == '?')) + { + m1++; + m2++; + if((!NOT_AT_END(m1)) && NOT_AT_END(m2) && savePos1) + { + //m1 finished , but m2 not yet and we have a savePosition for m1 (there was a wildcard)... + //retry matching the string following the * from the savePos2 (one char ahead last time) + m1 = savePos1; //back to char after wildcard + m2 = savePos2; //back to last savePos2 + savePos2++; //next savePos2 will be next char + } + } else { + if(savePos1) + { + //Have a jolly man...allow not matching... + m1 = savePos1; //go back to char after wildcard...need to rematch... + m2 = savePos2; //back to last savePos2 + savePos2++; //and set next savePos2 + } else return false; //No previous wildcards...not matched! + } + } + } + *r1 = bSwapped ? m2 : m1; + *r2 = bSwapped ? m1 : m2; + + return (!NOT_AT_END(m2)); //m1 surely finished , so for the match , m2 must be finished too + +#undef NOT_AT_END +} + + + +const char * kvi_extractToken(KviStr &str,const char *aux_ptr,char sep) +{ + __range_valid(aux_ptr); + while(*aux_ptr && (*aux_ptr == sep))aux_ptr++; + const char *p=aux_ptr; + while(*p && (*p != sep))p++; + str.m_len=p-aux_ptr; + str.m_ptr = (char *)kvi_realloc(str.m_ptr,str.m_len+1); + kvi_fastmove(str.m_ptr,aux_ptr,str.m_len); + *(str.m_ptr+str.m_len)='\0'; + while(*p && (*p == sep))p++; + return p; +} + +const char * kvi_extractUpTo(KviStr &str,const char *aux_ptr,char sep) +{ + __range_valid(aux_ptr); + const char *p=aux_ptr; + while(*p && (*p != sep))p++; + str.m_len=p-aux_ptr; + str.m_ptr = (char *)kvi_realloc(str.m_ptr,str.m_len+1); + kvi_fastmove(str.m_ptr,aux_ptr,str.m_len); + *(str.m_ptr+str.m_len)='\0'; + return p; +} + +int kvi_vsnprintf(char *buffer,int len,const char *fmt,kvi_va_list list) +{ + __range_valid(fmt); + __range_valid(buffer); + __range_valid(len > 0); //printing 0 characters is senseless + + register char *p; + char *argString; + long argValue; + unsigned long argUValue; + + //9999999999999999999999999999999\0 + char numberBuffer[32]; //enough ? 10 is enough for 32bit unsigned int... + char *pNumBuf; + unsigned int tmp; + + + for(p=buffer ; *fmt ; ++fmt) + { + if(len < 1)return (-1); //not enough space ... (in fact this could be len < 2 for the terminator) + //copy up to a '%' + if(*fmt != '%') + { + *p++ = *fmt; + --len; + continue; + } + + ++fmt; //skip this '%' + switch(*fmt) + { + case 's': //string + argString = kvi_va_arg(list,char *); + if(!argString)continue; + argValue = (long)strlen(argString); + //check for space... + if(len <= argValue)return (-1); //not enough space for buffer and terminator + while(*argString)*p++ = *argString++; + len -= argValue; + continue; + case 'd': //signed integer + argValue = kvi_va_arg(list,int); + if(argValue < 0){ //negative integer + *p++ = '-'; + if(--len == 0)return (-1); + argValue = -argValue; //need to have it positive + // most negative integer exception (avoid completely senseless (non digit) responses) + if(argValue < 0)argValue = 0; //we get -0 here + } + //write the number in a temporary buffer + pNumBuf = numberBuffer; + do { + tmp = argValue / 10; + *pNumBuf++ = argValue - (tmp * 10) + '0'; + } while((argValue = tmp)); + //copy now.... + argUValue = pNumBuf - numberBuffer; //length of the number string + if(((uint)len) <= argUValue)return (-1); //not enough space for number and terminator + do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer); + len -= argUValue; + continue; + case 'u': //unsigned integer + argUValue = kvi_va_arg(list,unsigned int); //many implementations place int here + //write the number in a temporary buffer + pNumBuf = numberBuffer; + do { + tmp = argUValue / 10; + *pNumBuf++ = argUValue - (tmp * 10) + '0'; + } while((argUValue = tmp)); + //copy now.... + argValue = pNumBuf - numberBuffer; //length of the number string + if(len <= argValue)return (-1); //not enough space for number and terminator + do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer); + len -= argValue; + continue; + case 'c': //char + // + // I'm not sure about this... + // In the linux kernel source the + // unsigned char is extracted from an integer type. + // We assume that gcc stacks a char argument + // as sizeof(int) bytes value. + // Is this always true ? + // + *p++ = (char)kvi_va_arg(list,int); + --len; + continue; + case 'Q': // QString! (this should almost never happen) + { + QString * s = kvi_va_arg(list,QString *); + KviQCString cs = KviQString::toUtf8(*s); + const char * t = cs.data(); + if(!t)continue; // nothing to do + //check for space... + if(len <= (int)cs.length())return (-1); //not enough space for buffer and terminator + while(*t)*p++ = *t++; + len -= cs.length(); + continue; + } + default: //a normal percent + *p++ = '%'; //write it + if(--len == 0)return (-1); //not enough space for next char or terminator + if(*fmt){ //this if is just in case that we have a % at the end of the string. + *p++ = *fmt; //and write this char + --len; + } + continue; + } + } + if(len < 1)return (-1); //missing space for terminator + *p = '\0'; + return p-buffer; +} + +// +// Nearly the same as the above function... +// + +int kvi_irc_vsnprintf(char *buffer,const char *fmt,kvi_va_list list,bool *bTruncated) +{ + __range_valid(fmt); + __range_valid(buffer); + if( !( buffer && fmt) ) return false; + register char *p; + char *argString; + long argValue; + unsigned long argUValue; + char numberBuffer[64]; //enough ? 10 is enough for 32bit unsigned int... + char *pNumBuf; + unsigned int tmp; + *bTruncated = false; + int len = 512; + + for (p=buffer ; *fmt ; ++fmt) { + if(len < 3)goto truncate; + //copy up to a '%' + if (*fmt != '%') { + *p++ = *fmt; + --len; + continue; + } + ++fmt; //skip this '%' + switch(*fmt){ + case 's': //string + argString = kvi_va_arg(list,char *); + if(!argString)continue; + //check for space... + while(*argString){ + *p++ = *argString++; + if(--len < 3)goto truncate; + } + continue; + case 'Q': // QString! (this should almost never happen) + { + QString * s = kvi_va_arg(list,QString *); + KviQCString cs = KviQString::toUtf8(*s); + const char * t = cs.data(); + if(!t)continue; // nothing to do + while(*t) + { + *p++ = *t++; + if(--len < 3)goto truncate; + } + continue; + } + case 'd': //signed integer + argValue = kvi_va_arg(list,int); + if(argValue < 0){ //negative integer + *p++ = '-'; + if(--len < 3)goto truncate; //place just for CRLF + argValue = -argValue; //need to have it positive + if(argValue < 0)argValue = 0; // -0 (hack the exception) + } + //write the number in a temporary buffer + pNumBuf = numberBuffer; + do { + tmp = argValue / 10; + *pNumBuf++ = argValue - (tmp * 10) + '0'; + } while((argValue = tmp)); + //copy now.... + do { + *p++ = *--pNumBuf; + if(--len < 3)goto truncate; + } while(pNumBuf != numberBuffer); + continue; + case 'u': //unsigned integer + argUValue = kvi_va_arg(list,unsigned int); //many implementations place int here + //write the number in a temporary buffer + pNumBuf = numberBuffer; + do { + tmp = argUValue / 10; + *pNumBuf++ = argUValue - (tmp * 10) + '0'; + } while((argUValue = tmp)); + //copy now.... + if(--len < 3)goto truncate; //no place for digits + do { + *p++ = *--pNumBuf; + if(--len < 3)goto truncate; + } while(pNumBuf != numberBuffer); + continue; + case 'c': //char + *p++ = (char)kvi_va_arg(list,int); + --len; + continue; + default: //a normal percent + *p++ = '%'; //write it + if(--len < 3)goto truncate; //not enough space for next char + if(*fmt){ //this if is just in case that we have a % at the end of the string. + *p++ = *fmt; //and write this char + --len; + } + continue; + } + } + //succesfull finish + __range_valid(len >= 2); + *p++ = '\r'; + *p = '\n'; + return ((p-buffer)+1); +truncate: + __range_valid(len >= 2); + *bTruncated = true; + *p++ = '\r'; + *p = '\n'; + return ((p-buffer)+1); +} + +#ifndef COMPILE_ix86_ASM + +bool kvi_strEqualCS(const char *str1,const char *str2) +{ + __range_valid(str1); + __range_valid(str2); + if( !( str1 && str2 ) ) return false; + register unsigned char *s1 = (unsigned char *)str1; + register unsigned char *s2 = (unsigned char *)str2; + while(*s1)if(*s1++ != *s2++)return false; + return (*s1 == *s2); +} + +bool kvi_strEqualCSN(const char *str1,const char *str2,int len) +{ + __range_valid(str1); + __range_valid(str2); + __range_valid(len >= 0); + if( !( str1 && str2 && (len >= 0) ) ) return false; + register unsigned char *s1 = (unsigned char *)str1; + register unsigned char *s2 = (unsigned char *)str2; + while(len-- && *s1)if(*s1++ != *s2++)return false; + return (len < 0); +} + +#endif + +bool kvi_strEqualCIN(const char *str1,const char *str2,int len) +{ + __range_valid(str1); + __range_valid(str2); + __range_valid(len >= 0); + if( !( str1 && str2 && (len >= 0) ) ) return false; + register unsigned char *s1 = (unsigned char *)str1; + register unsigned char *s2 = (unsigned char *)str2; + while(len-- && *s1)if(tolower(*s1++) != tolower(*s2++))return false; + return (len < 0); +} + +bool kvi_strEqualCI(const char *str1,const char *str2) +{ + __range_valid(str1); + __range_valid(str2); + if( !( str1 && str2) ) return false; + register unsigned char *s1 = (unsigned char *)str1; + register unsigned char *s2 = (unsigned char *)str2; + while(*s1)if(tolower(*s1++) != tolower(*s2++))return false; + return (*s1 == *s2); +} + +//note that greater here means that come AFTER in the alphabetic order +// return < 0 ---> str1 < str2 +// return = 0 ---> str1 = str2 +// return > 0 ---> str1 > str2 +int kvi_strcmpCI(const char *str1,const char *str2) +{ + //abcd abce + __range_valid(str1); + __range_valid(str2); + if( !( str1 && str2) ) return false; + register unsigned char *s1 = (unsigned char *)str1; + register unsigned char *s2 = (unsigned char *)str2; + int diff; + unsigned char rightchar; + while(!(diff=(rightchar=tolower(*s1++)) - tolower(*s2++)))if(!rightchar)break; + return diff; //diff is nonzero or end of both was reached (it is positive if *s2 > *s1 +} + +// +////note that greater here means that come AFTER in the alphabetic order +//// return < 0 ---> str1 < str2 +//// return = 0 ---> str1 = str2 +//// return > 0 ---> str1 > str2 +//int kvi_strcmpCIN(const char *str1,const char *str2,int len) +//{ +// //abcd abce +// __range_valid(str1); +// __range_valid(str2); +// register unsigned char *s1 = (unsigned char *)str1; +// register unsigned char *s2 = (unsigned char *)str2; +// int diff; +// unsigned char rightchar; +// while(len--) +// { +// if(!(diff=(rightchar=tolower(*s1++)) - tolower(*s2++)))break; +// if(!rightchar)break; +// } +// return diff; //diff is nonzero or end of both was reached (it is positive if *s2 > *s1 +//} + +int kvi_strcmpCS(const char *str1,const char *str2) +{ + //abcd abce + __range_valid(str1); + __range_valid(str2); + if( !( str1 && str2) ) return false; + register unsigned char *s1 = (unsigned char *)str1; + register unsigned char *s2 = (unsigned char *)str2; + int diff; + while(!(diff=(*s1)-(*s2++)))if(!*s1++)break; + return diff; //diff is nonzero or end of both was reached (it is positive if *s2 > *s1 +} + +int kvi_strMatchRevCS(const char *str1, const char *str2, int index) +{ + __range_valid(str1); + __range_valid(str2); + if( !( str1 && str2) ) return false; + register char *s1=(char *)str1; + register char *s2=(char *)str2; + + int curlen=(int)strlen(str1); + int diff; + + if (index<0 || index >= curlen) index = curlen-1; + + s1+=index; + while (*s2) s2++; + s2--; + + // now start comparing + while (1){ + /* in this case, we have str1 = "lo" and str2 = "hello" */ + if (s1<str1 && !(s2<str2)) return 256; + if (s2<str2) return 0; + if ((diff=(*s1)-(*s2))) return diff; + s1--; + s2--; + } +} + +KviStr::KviStr() +{ + m_ptr = (char *)kvi_malloc(1); + *m_ptr = '\0'; + m_len = 0; +} + +KviStr::KviStr(const char *str) +{ + //Deep copy constructor + if(str){ + //Deep copy + m_len = (int)strlen(str); + m_ptr = (char *)kvi_malloc(m_len+1); + kvi_fastmove(m_ptr,str,m_len+1); + } else { + m_ptr = (char *)kvi_malloc(1); + *m_ptr = '\0'; + m_len = 0; + } +} + +KviStr::KviStr(const KviQCString &str) +{ + //Deep copy constructor + if(str.data()) + { + //Deep copy + m_len = str.length(); + m_ptr = (char *)kvi_malloc(m_len+1); + kvi_fastmove(m_ptr,str,m_len+1); + } else { + m_ptr = (char *)kvi_malloc(1); + *m_ptr = '\0'; + m_len = 0; + } +} + + +KviStr::KviStr(const char *str,int len) +{ + __range_valid(str); + //__range_valid(len <= ((int)strlen(str))); <-- we trust the user here (and a strlen() call may run AFTER len if data is not null terminated) + __range_valid(len >= 0); + m_len = len; + m_ptr = (char *)kvi_malloc(m_len+1); + kvi_fastmove(m_ptr,str,m_len); + *(m_ptr+m_len) = '\0'; +} + +KviStr::KviStr(const char *bg,const char *end) +{ + __range_valid(bg); + __range_valid(end); + __range_valid(bg <= end); + m_len = end-bg; + m_ptr = (char *)kvi_malloc(m_len +1); + kvi_fastmove(m_ptr,bg,m_len); + *(m_ptr + m_len)='\0'; +} + +KviStr::KviStr(KviFormatConstructorTag tag,const char *fmt,...) +{ + m_ptr=(char *)kvi_malloc(256); + //First try + kvi_va_list list; + kvi_va_start(list,fmt); + //print...with max 256 chars + m_len=kvi_vsnprintf(m_ptr,256,fmt,list); + kvi_va_end(list); + + //check if we failed + if(m_len < 0){ + //yes , failed.... + int dummy=256; + do{ //we failed , so retry with 256 more chars + dummy+=256; + //realloc + m_ptr=(char *)kvi_realloc(m_ptr,dummy); + //print... + kvi_va_start(list,fmt); + m_len=kvi_vsnprintf(m_ptr,dummy,fmt,list); + kvi_va_end(list); + } while(m_len < 0); + } + //done... + //now m_len is the length of the written string not including the terminator... + //perfect! :) + m_ptr=(char *)kvi_realloc(m_ptr,m_len+1); +} + +KviStr::KviStr(const KviStr &str) +{ + __range_valid(str.m_ptr); + m_len = str.m_len; + m_ptr = (char *)kvi_malloc(m_len+1); + kvi_fastmove(m_ptr,str.m_ptr,m_len+1); +} + +KviStr::KviStr(const QString &str) +{ + KviQCString sz = KviQString::toUtf8(str); + if(sz.length() > 0) + { + m_len = sz.length(); + m_ptr = (char *)kvi_malloc(m_len+1); + kvi_fastmove(m_ptr,sz.data(),m_len+1); + } else { + m_ptr = (char *)kvi_malloc(1); + *m_ptr = '\0'; + m_len = 0; + } +} + +KviStr::KviStr(char c,int fillLen) +{ + __range_valid(fillLen >= 0); + m_len = fillLen; + m_ptr = (char *)kvi_malloc(m_len+1); + register char *p=m_ptr; + while(fillLen--)*p++=c; + *p='\0'; +} + + +KviStr::KviStr(const kvi_wchar_t * unicode) +{ + if(!unicode) + { + m_len = 0; + m_ptr = (char *)kvi_malloc(1); + *m_ptr = 0; + } else { + m_len = kvi_wstrlen(unicode); + m_ptr = (char *)kvi_malloc(m_len + 1); + register char * p = m_ptr; + while(*unicode)*p++ = *unicode++; + *p = 0; + } +} + +KviStr::KviStr(const kvi_wchar_t * unicode,int len) +{ + m_len = len; + m_ptr = (char *)kvi_malloc(m_len + 1); + register char * p = m_ptr; + char * end = p + len; + while(p != end) + { + *p++ = *unicode++; + } + *p = 0; +} + + + + +KviStr::~KviStr() +{ + kvi_free(m_ptr); +} + +void KviStr::setLength(int iLen) +{ + __range_valid(iLen >= 0); + m_len = iLen; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + *(m_ptr + m_len) = '\0'; +} + +KviStr & KviStr::operator=(const KviStr &str) +{ + __range_valid(str.m_ptr); + __range_valid(str.m_ptr != m_ptr); + m_len = str.m_len; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + kvi_fastmove(m_ptr,str.m_ptr,m_len+1); + return (*this); +} + +KviStr & KviStr::operator=(const KviQCString &str) +{ + m_len = str.length(); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + if(str.data())kvi_fastmove(m_ptr,str.data(),m_len+1); + else *m_ptr = 0; + return (*this); +} + +KviStr & KviStr::operator=(const char *str) +{ + //__range_valid(str); + if(str){ + m_len = (int)strlen(str); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + kvi_memmove(m_ptr,str,m_len+1); + } else { + m_ptr = (char *)kvi_realloc(m_ptr,1); + *m_ptr = '\0'; + m_len = 0; + } + return (*this); +} + +void KviStr::clear() +{ + m_ptr = (char *)kvi_realloc(m_ptr,1); + *m_ptr = '\0'; + m_len = 0; +} + + +bool KviStr::hasNonWhiteSpaceData() const +{ + const char * aux = m_ptr; + while(*aux) + { + if(((*aux) != ' ') && ((*aux) != '\t'))return true; + aux++; + } + return false; +} + +static char hexdigits[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; + +void KviStr::bufferToHex(const char *buffer,int len) +{ + __range_valid(buffer); + m_len = (len * 2); + m_ptr = (char *)kvi_realloc(m_ptr,m_len + 1); + char * aux = m_ptr; + while(len) + { + *aux = hexdigits[(unsigned int)(((unsigned char)(*buffer)) / 16)]; + aux++; + *aux = hexdigits[(unsigned int)(((unsigned char)(*buffer)) % 16)]; + aux++; + len--; + buffer++; + } + *(m_ptr+m_len) = '\0'; +} + + + +static char get_decimal_from_hex_digit_char(char dgt) +{ + if((dgt >= '0') && (dgt <= '9'))return (dgt - '0'); + if((dgt >= 'A') && (dgt <= 'F'))return (10 + (dgt - 'A')); + if((dgt >= 'a') && (dgt <= 'f'))return (10 + (dgt - 'a')); + return -1; +} + +// This is just error-correcting...it treats non hex stuff as zeros +/* +static inline char get_decimal_from_hex_digit_char(char dgt) +{ + char c = pedantic_get_decimal_from_hex_digit(dgt); + if(c == -1)return 0; + return c; +} + +int KviStr::hexToBuffer(char ** buffer,bool bNullToNewlines) +{ + int len; + if(m_len % 2)len = (m_len / 2) + 1; + else len = (m_len / 2); + *buffer = (char *)kvi_malloc(len); + + char * ptr = *buffer; + + char * aux = m_ptr; + while(*aux) + { + *ptr = get_decimal_from_hex_digit_char(*aux) * 16; + aux++; + if(*aux) + { + *ptr += get_decimal_from_hex_digit_char(*aux); + aux++; + } + if(bNullToNewlines)if(!(*ptr))*ptr = '\n'; + ptr++; + } + return len; +} +*/ + +int KviStr::hexToBuffer(char ** buffer,bool bNullToNewlines) +{ + *buffer = 0; + if((m_len == 0) || (m_len & 1))return -1; // this is an error + int len = (m_len / 2); + if(len < 1)return -1; + *buffer = (char *)kvi_malloc(len); + + char * ptr = *buffer; + char * aux = m_ptr; + + char aux2; + + while(*aux) + { + *ptr = get_decimal_from_hex_digit_char(*aux) * 16; + if(*ptr == -1) + { + kvi_free(*buffer); + *buffer = 0; + return -1; + } + aux++; + aux2 = get_decimal_from_hex_digit_char(*aux); + if(aux2 == -1) + { + kvi_free(*buffer); + *buffer = 0; + return -1; + } + *ptr += aux2; + aux++; + if(bNullToNewlines)if(!(*ptr))*ptr = '\n'; + ptr++; + } + return len; +} + +static const char * base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + +void KviStr::bufferToBase64(const char * buffer,int len) +{ + m_len = (len / 3) << 2; + if(len % 3)m_len += 4; + + m_ptr = (char *)kvi_realloc(m_ptr,m_len + 1); + + unsigned char aux1,aux2,aux3; + char * aux_ptr = m_ptr; + while(len > 2) + { + aux1 = (unsigned char)*buffer++; + aux2 = (unsigned char)*buffer++; + aux3 = (unsigned char)*buffer++; + *aux_ptr++ = base64_chars[(aux1 & 0xFC) >> 2]; + *aux_ptr++ = base64_chars[((aux1 & 0x03) << 4) | ((aux2 & 0xF0) >> 4)]; + *aux_ptr++ = base64_chars[((aux2 & 0x0F) << 2) | ((aux3 & 0xC0) >> 6)]; + *aux_ptr++ = base64_chars[(aux3 & 0x3F)]; + len -= 3; + } + switch(len) + { + case 2: + aux1 = (unsigned char)*buffer++; + aux2 = (unsigned char)*buffer++; + *aux_ptr++ = base64_chars[(aux1 & 0xFC) >> 2]; + *aux_ptr++ = base64_chars[((aux1 & 0x03) << 4) | ((aux2 & 0xF0) >> 4)]; + *aux_ptr++ = base64_chars[((aux2 & 0x0F) << 2)]; + *aux_ptr++ = '='; + break; + case 1: + aux1 = (unsigned char)*buffer++; + aux2 = (unsigned char)*buffer++; + *aux_ptr++ = base64_chars[(aux1 & 0xFC) >> 2]; + *aux_ptr++ = base64_chars[((aux1 & 0x03) << 4)]; + *aux_ptr++ = '='; + *aux_ptr++ = '='; + break; + } + *aux_ptr = 0; +} + +static unsigned char get_base64_idx(char base64) +{ + if((base64 >= 'A') && (base64 <= 'Z'))return (base64 - 'A'); + if((base64 >= 'a') && (base64 <= 'z'))return ((base64 - 'a') + 26); + if((base64 >= '0') && (base64 <= '9'))return ((base64 - '0') + 52); + if(base64 == '+')return 62; + if(base64 == '/')return 63; + if(base64 == '=')return 64; + return 65; +} + + +int KviStr::base64ToBuffer(char ** buffer,bool bNullToNewlines) +{ + *buffer = 0; + if((m_len == 0) || (m_len & 3))return -1; // this is an error + int len = (m_len >> 2) * 3; + *buffer = (char *)kvi_malloc(len); + + char * auxBuf = *buffer; + + unsigned char aux1,aux2,aux3,aux4; + char * aux_ptr = m_ptr; + + int newLen = len; + + while(*aux_ptr) + { + if(newLen != len) + { + // ops... there was a padding and we still have chars after it + // this is an error + kvi_free(*buffer); + *buffer = 0; + return -1; + } + aux1 = get_base64_idx(*aux_ptr++); + aux2 = get_base64_idx(*aux_ptr++); + aux3 = get_base64_idx(*aux_ptr++); + aux4 = get_base64_idx(*aux_ptr++); + if((aux3 > 64) || (aux4 > 64)) + { + // error + kvi_free(*buffer); + *buffer = 0; + return -1; + } + if((aux1 | aux2) > 63) + { + // again error...impossible padding + kvi_free(*buffer); + *buffer = 0; + return -1; + } + if(aux4 == 64) + { + if(aux3 == 64) + { + // Double padding, only one digit here + *auxBuf++ = (char)((aux1 << 2) | (aux2 >> 4)); + newLen -= 2; + } else { + // Single padding, two digits here + *auxBuf++ = (char)((aux1 << 2) | (aux2 >> 4)); // >> 4 is a shr , not a ror! :) + *auxBuf++ = (char)((aux2 << 4) | (aux3 >> 2)); + newLen -= 1; + } + } else { + if(aux3 == 64) + { + // error... impossible padding + kvi_free(*buffer); + *buffer = 0; + return -1; + } else { + // Ok , no padding, three digits here + *auxBuf++ = (char)((aux1 << 2) | (aux2 >> 4)); + *auxBuf++ = (char)((aux2 << 4) | (aux3 >> 2)); + *auxBuf++ = (char)((aux3 << 6) | aux4); + } + } + } + + if(newLen != len)*buffer = (char *)kvi_realloc(*buffer,newLen); + return newLen; +} + +KviStr & KviStr::setStr(const char *str,int len) +{ + if(!str) + { + clear(); + return *this; + } + int alen = (int)strlen(str); + if((len < 0) || (len > alen))m_len = alen; + else m_len = len; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + kvi_memmove(m_ptr,str,m_len); + *(m_ptr+m_len) = '\0'; + return (*this); +} + +KviStr & KviStr::operator=(const QString &str) +{ + KviQCString sz = KviQString::toUtf8(str); + if(sz.length() > 0){ + m_len = sz.length(); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + kvi_fastmove(m_ptr,sz.data(),m_len+1); + } else { + m_ptr = (char *)kvi_realloc(m_ptr,1); + *m_ptr = '\0'; + m_len = 0; + } + return (*this); +} + +KviStr & KviStr::operator=(char c) +{ + m_len = 1; + m_ptr = (char *)kvi_realloc(m_ptr,2); + *m_ptr = c; + *(m_ptr+1)='\0'; + return (*this); +} + +void KviStr::append(char c) +{ + m_ptr = (char *)kvi_realloc(m_ptr,m_len+2); + *(m_ptr+m_len)=c; + m_len++; + *(m_ptr+m_len)='\0'; +} + +void KviStr::append(const KviStr &str) +{ + __range_valid(str.m_ptr); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+str.m_len+1); + kvi_fastmove((m_ptr+m_len),str.m_ptr,str.m_len+1); + m_len += str.m_len; +} + +void KviStr::append(const char *str) +{ + if(!str)return; + int len = (int)strlen(str); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+len+1); + kvi_fastmove((m_ptr+m_len),str,len+1); + m_len += len; +} + +void KviStr::append(const QString &str) +{ + KviQCString sz = KviQString::toUtf8(str); + if(sz.length() < 1)return; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+sz.length()+1); + kvi_fastmove((m_ptr+m_len),sz.data(),sz.length()+1); + m_len += sz.length(); +} + +void KviStr::append(const char *str,int len) +{ + __range_valid(str); +// __range_valid(len <= ((int)strlen(str))); + __range_valid(len >= 0); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+len+1); + kvi_fastmove((m_ptr+m_len),str,len); + m_len += len; + *(m_ptr + m_len)='\0'; +} + +void KviStr::append(KviFormatConstructorTag tag,const char *fmt,...) +{ + int auxLen; + m_ptr=(char *)kvi_realloc(m_ptr,m_len + 256); + //First try + kvi_va_list list; + kvi_va_start(list,fmt); + //print...with max 256 chars + auxLen =kvi_vsnprintf(m_ptr + m_len,256,fmt,list); + kvi_va_end(list); + + //check if we failed + if(auxLen < 0){ + //yes , failed.... + int dummy=256; + do{ //we failed , so retry with 256 more chars + dummy+=256; + //realloc + m_ptr=(char *)kvi_realloc(m_ptr,m_len + dummy); + //print... + kvi_va_start(list,fmt); + auxLen=kvi_vsnprintf(m_ptr + m_len,dummy,fmt,list); + kvi_va_end(list); + } while(auxLen < 0); + } + m_len += auxLen; + //done... + //now m_len is the length of the written string not including the terminator... + //perfect! :) + m_ptr=(char *)kvi_realloc(m_ptr,m_len+1); +} + +void KviStr::extractFromString(const char *begin,const char *end) +{ + __range_valid(begin); + __range_valid(end); + __range_valid(end >= begin); + m_len = end-begin; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + kvi_fastmove(m_ptr,begin,m_len); + *(m_ptr + m_len)='\0'; +} + +void KviStr::prepend(const KviStr &str) +{ + __range_valid(str.m_ptr); + __range_valid(str.m_ptr != m_ptr); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+str.m_len+1); + kvi_memmove((m_ptr+str.m_len),m_ptr,m_len+1); //move self + kvi_fastmove(m_ptr,str.m_ptr,str.m_len); + m_len += str.m_len; +} + +void KviStr::prepend(const char *str) +{ + if(!str)return; + int len = (int)strlen(str); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+len+1); + kvi_memmove((m_ptr+len),m_ptr,m_len+1); //move self + kvi_fastmove(m_ptr,str,len); + m_len += len; +} + +void KviStr::prepend(const char *str,int len) +{ + __range_valid(str); + __range_valid(len <= ((int)strlen(str))); + __range_valid(len >= 0); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+len+1); + kvi_memmove((m_ptr+len),m_ptr,m_len+1); //move self + kvi_fastmove(m_ptr,str,len); + m_len += len; +} + +unsigned char iso88591_toUpper_map[256]= +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +void KviStr::toUpperISO88591() +{ + register char *p=m_ptr; + while(*p) + { + *p=(char)iso88591_toUpper_map[(unsigned char)*p]; + p++; + } +} + +void KviStr::toUpper() +{ + register char *p=m_ptr; + while(*p) + { + *p=toupper(*p); + p++; + } +} + +unsigned char iso88591_toLower_map[256]= +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +void KviStr::toLowerISO88591() +{ + register char *p=m_ptr; + while(*p) + { + *p=(char)iso88591_toLower_map[(unsigned char)*p]; + p++; + } +} + + +void KviStr::toLower() +{ + register char *p=m_ptr; + while(*p) + { + *p=tolower(*p); + p++; + } +} + +KviStr KviStr::upper() const +{ + KviStr tmp(*this); + tmp.toUpper(); + return tmp; +} + +KviStr KviStr::upperISO88591() const +{ + KviStr tmp(*this); + tmp.toUpperISO88591(); + return tmp; +} + +KviStr KviStr::lower() const +{ + KviStr tmp(*this); + tmp.toLower(); + return tmp; +} + +KviStr KviStr::lowerISO88591() const +{ + KviStr tmp(*this); + tmp.toLowerISO88591(); + return tmp; +} + +KviStr KviStr::left(int maxLen) const +{ + if(maxLen <= 0) + { + KviStr empty; + return empty; + } + if(maxLen > m_len)maxLen=m_len; + KviStr str(m_ptr,maxLen); + return str; +} + +KviStr KviStr::right(int maxLen) const +{ + if(maxLen <= 0) + { + KviStr empty; + return empty; + } + if(maxLen > m_len)maxLen=m_len; + KviStr str((m_ptr+(m_len-maxLen)),maxLen); + return str; +} + +KviStr KviStr::middle(int idx,int maxLen) const +{ + __range_valid(maxLen >= 0); + __range_valid(idx >= 0); + if((maxLen <= 0) || (idx < 0)){ //max len negative...invalid params + KviStr ret; + return ret; + } + if((maxLen + idx) <= m_len){ //valid params + KviStr str(m_ptr+idx,maxLen); + return str; + } + if(idx < m_len){ //string shorter than requested + KviStr str(m_ptr+idx); + return str; + } + // idx out of bounds + KviStr ret; + return ret; +} + +KviStr ** KviStr::splitToArray(char sep,int max,int * realCount) const +{ + KviStr ** strings = (KviStr **)kvi_malloc(sizeof(KviStr *)); + int number = 0; + char * ptr = m_ptr; + char * last = ptr; + while((max > 0) && *ptr) + { + strings = (KviStr **)kvi_realloc((void *)strings,sizeof(KviStr *) * (number + 2)); + if(max > 1) + { + while(*ptr && (*ptr != sep))ptr++; + strings[number] = new KviStr(last,ptr - last); + } else { + strings[number] = new KviStr(ptr); + } + number++; + max--; + if(*ptr) + { + ptr++; + last = ptr; + } + } + if(realCount)*realCount = number; + strings[number] = 0; + return strings; +} +/* + WORKING BUT UNUSED + +KviStr ** KviStr::splitToArray(const char * sep,int max,int * realCount) const +{ + KviStr ** strings = (KviStr **)kvi_malloc(sizeof(KviStr *)); + KviStr tmp = *this; + int idx = tmp.findFirstIdx(sep); + int number = 0; + int seplen = kvi_strLen(sep); + + + while(idx != -1) + { + strings = (KviStr **)kvi_realloc(sizeof(KviStr *) * (number + 2)); + strings[number] = new KviStr(tmp.ptr(),idx); + tmp.cutLeft(idx + seplen); + number++; + idx = tmp.findFirstIdx(sep); + } + + if(tmp.hasData()) + { + strings = (KviStr **)kvi_realloc(sizeof(KviStr *) * (number + 2)); + strings[number] = new KviStr(tmp); + number++; + } + + if(realCount)*realCount = number; + strings[number] = 0; + return strings; +} +*/ +void KviStr::freeArray(KviStr ** strings) +{ + if(!strings)return; + KviStr ** aux = strings; + while(*aux) + { + delete (*aux); // delete (KviStr *) + aux++; + } + kvi_free(strings); +} + +void KviStr::freeBuffer(char * buffer) +{ + if(!buffer)return; + kvi_free(buffer); +} + +void KviStr::joinFromArray(KviStr ** strings,const char * sep,bool bLastSep) +{ + setLen(0); + if(!strings)return; + + while(*strings) + { + append(*(*strings)); + strings++; + if(*strings) + { + if(sep)append(sep); + } else { + if(sep && bLastSep)append(sep); + } + } +} + +KviStr & KviStr::insert(int idx,const char *data) +{ + __range_valid(data); + if(idx <= m_len){ + int len = (int)strlen(data); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+len+1); + kvi_memmove(m_ptr+idx+len,m_ptr+idx,(m_len - idx)+1); + kvi_fastmove(m_ptr+idx,data,len); + m_len+=len; + } + return (*this); +} + +KviStr & KviStr::insert(int idx,char c) +{ + if(idx <= m_len){ + m_ptr = (char *)kvi_realloc(m_ptr,m_len+2); + kvi_memmove(m_ptr+idx+1,m_ptr+idx,(m_len - idx)+1); + m_len++; + *(m_ptr + idx) = c; + } + return (*this); +} + +// FIXME: #warning "Double check the following two functions !!!" + +KviStr & KviStr::hexEncodeWithTable(const unsigned char table[256]) +{ + char * aux = m_ptr; + char * begin = m_ptr; + + char * n = 0; + int curSize = 0; + + while(*aux) + { + if(table[*((unsigned char *)aux)] || (*aux == '%')) + { + int len = aux - begin; + n = (char *)kvi_realloc(n,curSize + len + 3); + kvi_memmove(n + curSize,begin,len); + curSize += len; + + n[curSize] = '%'; + curSize++; + n[curSize] = hexdigits[(unsigned int)(((unsigned char)(*aux)) / 16)]; + curSize++; + n[curSize] = hexdigits[(unsigned int)(((unsigned char)(*aux)) % 16)]; + curSize++; + + aux++; + begin = aux; + + } else aux++; + } + + int len = aux - begin; + n = (char *)kvi_realloc(n,curSize + len + 1); + kvi_memmove(n + curSize,begin,len); + curSize += len; + + n[curSize] = '\0'; + + kvi_free((void *)m_ptr); + m_ptr = n; + m_len = curSize; + + return (*this); +} + +KviStr & KviStr::hexEncodeWhiteSpace() +{ + static unsigned char ascii_jump_table[256]= + { + // 000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 + // NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI + 1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 , + // 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 + // DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US + 1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 ,1 , + // 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 + // ! " # $ % & ' ( ) * + , - . / + 1 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 + // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 + // @ A B C D E F G H I J K L M N O + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 + // P Q R S T U V W X Y Z [ \ ] ^ _ + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 + // ` a b c d e f g h i j k l m n o + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 + // p q r s t u v w x y z { | } ~ + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 + // + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 + // + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 + // + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 + // + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 + // � � � � � � � � � � � � � � � � + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 + // � � � � � � � � � � � � � � � � + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 + // � � � � � � � � � � � � � � � � + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , + // 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 + // � � � � � � � � + 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 + }; + + return hexEncodeWithTable(ascii_jump_table); +} + +KviStr & KviStr::hexDecode(const char * pFrom) +{ + // WARNING: pFrom can be also m_ptr here! + const char * aux = pFrom; + const char * begin = pFrom; + + char * n = 0; + int curSize = 0; + + while(*aux) + { + if(*aux == '%') + { + // move last block + int len = aux - begin; + n = (char *)kvi_realloc(n,curSize + len + 1); + kvi_memmove(n + curSize,begin,len); + curSize += len; + + // get the hex code + aux++; + + char theChar = get_decimal_from_hex_digit_char(*aux); + if(theChar < 0) + { + n[curSize] = '%'; // wrong code...just a '%' + curSize++; + } else { + aux++; + char theChar2 = get_decimal_from_hex_digit_char(*aux); + if(theChar2 < 0) + { + // wrong code...just a '%' and step back + n[curSize] = '%'; + curSize++; + aux--; + } else { + n[curSize] = (theChar * 16) + theChar2; + curSize++; + aux++; + } + } + + begin = aux; + + } else aux++; + } + + int len = aux - begin; + n = (char *)kvi_realloc(n,curSize + len + 2); + kvi_memmove(n + curSize,begin,len); + curSize += len; + n[curSize] = '\0'; + + kvi_free((void *)m_ptr); + m_ptr = n; + m_len = curSize; + + return (*this); +} + +KviStr & KviStr::replaceAll(char c,const char *str) +{ + int idx = findFirstIdx(c); + KviStr tmp; + while(idx >= 0){ + if(idx > 0)tmp += left(idx); + cutLeft(idx+1); + tmp.append(str); + idx = findFirstIdx(c); + } + tmp.append(*this); + // Now copy + m_len = tmp.m_len; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + kvi_fastmove(m_ptr,tmp.m_ptr,m_len+1); + return (*this); +} + +KviStr & KviStr::replaceAll(char *toFind,const char *str,bool bCaseS) +{ + int len = (int)strlen(toFind); + int idx = findFirstIdx(toFind,bCaseS); + KviStr tmp; + while(idx >= 0) + { + if(idx > 0)tmp += left(idx); + cutLeft(idx+len); + tmp.append(str); + idx = findFirstIdx(toFind,bCaseS); + } + tmp.append(*this); + // Now copy + m_len = tmp.m_len; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + kvi_fastmove(m_ptr,tmp.m_ptr,m_len+1); + return (*this); +} + +KviStr & KviStr::transliterate(const char * szToFind,const char * szReplacement) +{ + while(*szToFind && *szReplacement) + { + char * p = m_ptr; + while(*p) + { + if(*p == *szToFind)*p = *szReplacement; + ++p; + } + ++szToFind; + ++szReplacement; + } + return (*this); +} + + +int KviStr::occurences(char c,bool caseS) const +{ + register char *p = m_ptr; + int cnt=0; + if(caseS){ + while(*p){ + if(*p == c)cnt++; + p++; + } + } else { + char b=tolower(c); + while(*p){ + if(tolower(*p) == b)cnt++; + p++; + } + } + return cnt; +} + +int KviStr::occurences(const char *str,bool caseS) const +{ + __range_valid(str); + register char *p = m_ptr; + int cnt=0; + int len = (int)strlen(str); + if(caseS){ + while(*p){ + if(*p == *str){ + if(kvi_strEqualCSN(p,str,len))cnt++; + } + p++; + } + } else { + while(*p){ + char c = tolower(*str); + if(tolower(*p) == c){ + if(kvi_strEqualCIN(p,str,len))cnt++; + } + p++; + } + } + return cnt; +} + +bool KviStr::contains(char c,bool caseS) const +{ + register char *p = m_ptr; + if(caseS) + { + while(*p) + { + if(*p == c)return true; + p++; + } + } else { + char b=tolower(c); + while(*p) + { + if(tolower(*p) == b)return true; + p++; + } + } + return false; +} + +bool KviStr::contains(const char *str,bool caseS) const +{ + __range_valid(str); + register char *p = m_ptr; + int len = (int)strlen(str); + if(caseS) + { + while(*p) + { + if(*p == *str) + { + if(kvi_strEqualCSN(p,str,len))return true; + } + p++; + } + } else { + while(*p) + { + char c = tolower(*str); + if(tolower(*p) == c) + { + if(kvi_strEqualCIN(p,str,len))return true; + } + p++; + } + } + return false; +} + + +KviStr & KviStr::setNum(long num) +{ + char numberBuffer[30]; + bool bNegative = false; + long tmp; + register char *p; + register char *pNumBuf = numberBuffer; + + // somebody can explain me why -(-2147483648) = -2147483648 ? (2^31) + // it is like signed char x = 128 ---> 10000000 that is signed -0 (!?) + // mmmmh...or it is assumed to be -128 (a number rappresentation exception) + // at least on my machine it happens... + + // found the solution by myself today... + // + // ABS(3) Linux Programmer's Manual ABS(3) + // NAME + // abs - computes the absolute value of an integer. + // ... + // DESCRIPTION + // The abs() function computes the absolute value of the integer argument j. + // RETURN VALUE + // Returns the absolute value of the integer argument. + // CONFORMING TO + // SVID 3, POSIX, BSD 4.3, ISO 9899 + // NOTE ################################################################################## + // Trying to take the absolute value of the most negative integer is not defined. + // ####################################################################################### + + // so should i use temporaneous doubles to make calculations ? + + if(num < 0){ //negative integer + bNegative = true; + num = -num; //need to have it positive + if(num < 0){ // 2^31 exception + // We need to avoid absurd responses like ".(./),." :) + num = 0; // we get a negative zero here...it is still an exception + } + } + + //write the number in a temporary buffer (at least '0') + do { + tmp = num / 10; + *pNumBuf++ = num - (tmp * 10) + '0'; + } while((num = tmp)); + + //copy now.... + m_len = pNumBuf - numberBuffer; //length of the number string + if(bNegative){ + m_len++; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + p=m_ptr; + *p++='-'; + } else { + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + p=m_ptr; + } + do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer); + *(m_ptr + m_len)='\0'; + return (*this); +} + +KviStr & KviStr::setNum(unsigned long num) +{ + char numberBuffer[30]; + unsigned long tmp; + register char *p; + register char *pNumBuf = numberBuffer; + + //write the number in a temporary buffer (at least '0') + do { + tmp = num / 10; + *pNumBuf++ = num - (tmp * 10) + '0'; + } while((num = tmp)); + + //copy now.... + m_len = pNumBuf - numberBuffer; //length of the number string + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + p=m_ptr; + do { *p++ = *--pNumBuf; } while(pNumBuf != numberBuffer); + *(m_ptr + m_len)='\0'; + return (*this); +} + +long KviStr::toLong(bool *bOk) const +{ + long result = 0; + if(bOk)*bOk = false; + register char *p=m_ptr; + bool bNeg = false; + while(isspace(*p))p++; //skip spaces + if(*p == '-'){ + bNeg = true; + p++; + } else { + if(*p == '+')p++; + } + if(isdigit(*p)){ //point to something interesting ? + do{ + result = (result * 10) + (*p - '0'); + p++; + } while(isdigit(*p)); + if(bNeg)result = -result; + while(isspace(*p))p++; //skip trailing spaces + if(*p)return 0; //if this is not the end...die. + if(bOk)*bOk = true; + return result; + } + return 0; +} + +unsigned long KviStr::toULong(bool *bOk) const +{ + unsigned long result = 0; + if(bOk)*bOk = false; + register char *p=m_ptr; + while(isspace(*p))p++; //skip spaces + if(isdigit(*p)){ //point to something interesting ? + do{ + result = (result * 10) + (*p - '0'); + p++; + } while(isdigit(*p)); + while(isspace(*p))p++; //skip trailing spaces + if(*p)return 0; //if this is not the end...die. + if(bOk)*bOk = true; + return result; + } + return 0; +} + +long KviStr::toLongExt(bool *bOk,int base) +{ + if(m_len == 0){ + if(bOk)*bOk = false; + return 0; + } + char * endptr; + long result = strtol(m_ptr,&endptr,base); + if(*endptr){ + // must be whitespaces , otherwise there is trailing garbage inside + while(isspace(*endptr) && (*endptr))endptr++; + if(*endptr){ + // still not at the end + // trailing garbage not allowed + if(bOk)*bOk = false; + return result; + } + } + if(bOk)*bOk = true; + return result; +} + +// +//working code , but unused in kvirc +// +//unsigned long KviStr::toULongExt(bool *bOk = 0,int base = 0) +//{ +// if(m_len == 0){ +// if(bOk)*bOk = false; +// return 0; +// } +// char * endptr; +// unsigned long result = strtoul(m_ptr,&endptr,base); +// if(*endptr != '\0'){ +// if(bOk)*bOk = false; +// } +// return result; +//} + +KviStr & KviStr::cutLeft(int len) +{ + __range_valid(len >= 0); + if(len <= m_len){ + m_len -= len; + kvi_memmove(m_ptr,m_ptr+len,m_len+1); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + } else { + m_ptr = (char *)kvi_realloc(m_ptr,1); + *m_ptr = '\0'; + m_len = 0; + } + return (*this); +} + +KviStr & KviStr::cutRight(int len) +{ + __range_valid(len >= 0); + if(len <= m_len){ + m_len -= len; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + *(m_ptr +m_len)='\0'; + } else { + m_ptr = (char *)kvi_realloc(m_ptr,1); + *m_ptr = '\0'; + m_len = 0; + } + return (*this); +} + +KviStr & KviStr::cut(int idx,int len) +{ + __range_valid(idx >= 0); + __range_valid(len >= 0); + if(idx < m_len){ + // idx = 3 len = 3 m_len = 10 + // 0123456789 + // abcdefghij + // ^ ^ + // p1 p2 + char * p1 = m_ptr+idx; + if(len + idx > m_len)len = m_len - idx; + char * p2 = p1+len; + kvi_memmove(p1,p2,(m_len - (len+idx)) +1); + m_len -= len; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + } + return (*this); +} + +KviStr & KviStr::cutToFirst(char c,bool bIncluded) +{ + int idx = findFirstIdx(c); + if(idx != -1)cutLeft(bIncluded ? idx + 1 : idx); + return (*this); +} + +KviStr KviStr::leftToFirst(char c,bool bIncluded) const +{ + int idx = findFirstIdx(c); + if(idx == -1)return KviStr(*this); + return KviStr(m_ptr,bIncluded ? idx + 1 : idx); +} + + +KviStr KviStr::leftToLast(char c,bool bIncluded) const +{ + int idx = findLastIdx(c); + return KviStr(m_ptr,bIncluded ? idx + 1 : idx); +} + +KviStr & KviStr::cutFromFirst(char c,bool bIncluded) +{ + int idx = findFirstIdx(c); + if(idx != -1)cutRight(bIncluded ? (m_len - idx) : (m_len - (idx + 1))); + return (*this); +} + +KviStr & KviStr::cutToLast(char c,bool bIncluded) +{ + int idx = findLastIdx(c); + if(idx != -1)cutLeft(bIncluded ? idx + 1 : idx); + return (*this); +} + +KviStr & KviStr::cutFromLast(char c,bool bIncluded) +{ + int idx = findLastIdx(c); + if(idx != -1)cutRight(bIncluded ? (m_len - idx) : (m_len - (idx + 1))); + return (*this); +} + +KviStr & KviStr::cutToFirst(const char *c,bool bIncluded) +{ + int len = (int)strlen(c); + int idx = findFirstIdx(c); + if(idx != -1)cutLeft(bIncluded ? idx + len : idx); + return (*this); +} + +KviStr & KviStr::cutFromFirst(const char *c,bool bIncluded) +{ + int len = (int)strlen(c); + int idx = findFirstIdx(c); + if(idx != -1)cutRight(bIncluded ? (m_len - idx) : (m_len - (idx + len))); + return (*this); +} + +KviStr & KviStr::cutToLast(const char *c,bool bIncluded) +{ + int len = (int)strlen(c); + int idx = findLastIdx(c); + if(idx != -1)cutLeft(bIncluded ? idx + len : idx); + return (*this); +} + +KviStr & KviStr::cutFromLast(const char *c,bool bIncluded) +{ + int len = (int)strlen(c); + int idx = findLastIdx(c); + if(idx != -1)cutRight(bIncluded ? (m_len - idx) : (m_len - (idx + len))); + return (*this); +} + +KviStr & KviStr::setLen(int len) +{ + __range_valid(len >= 0); + m_ptr = (char *)kvi_realloc(m_ptr,len+1); + *(m_ptr+len)='\0'; + m_len = len; + return (*this); +} + +KviStr & KviStr::stripLeftWhiteSpace() +{ + register char *p=m_ptr; + while(isspace(*p))p++; + m_len -= (p-m_ptr); + kvi_memmove(m_ptr,p,m_len+1); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + return (*this); +} + +KviStr & KviStr::stripLeft(char c) +{ + __range_valid(c != '\0'); + register char *p=m_ptr; + while(*p == c)p++; + m_len -= (p-m_ptr); + kvi_memmove(m_ptr,p,m_len+1); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + return (*this); +} + +bool KviStr::getToken(KviStr & str,char sep) +{ + __range_valid(str.m_ptr); + __range_valid(str.m_ptr != m_ptr); + register char *p=m_ptr; + //skip to the end + while(*p && (*p != sep))p++; + //0123456789 + //abcd xyz + //^ ^ + str.m_len = p-m_ptr; + str.m_ptr = (char *)kvi_realloc(str.m_ptr,str.m_len+1); + kvi_fastmove(str.m_ptr,m_ptr,str.m_len); + *(str.m_ptr + str.m_len)='\0'; + while(*p && (*p == sep))p++; + cutLeft(p-m_ptr); + return (m_len != 0); +} + +bool KviStr::getLine(KviStr &str) +{ + __range_valid(str.m_ptr); + __range_valid(str.m_ptr != m_ptr); + if(m_len == 0)return false; + register char *p=m_ptr; + //skip to the end + while(*p && (*p != '\n'))p++; + //0123456789 + //abcd xyz + //^ ^ + str.m_len = p-m_ptr; + str.m_ptr = (char *)kvi_realloc(str.m_ptr,str.m_len+1); + kvi_fastmove(str.m_ptr,m_ptr,str.m_len); + *(str.m_ptr + str.m_len)='\0'; + p++; + cutLeft(p-m_ptr); + return true; +} + +KviStr KviStr::getToken(char sep) +{ + register char *p=m_ptr; + while(*p && (*p != sep))p++; + KviStr ret(m_ptr,p); + while(*p && (*p == sep))p++; + cutLeft(p-m_ptr); + return ret; +} + +KviStr & KviStr::sprintf(const char *fmt,...) +{ + m_ptr=(char *)kvi_realloc(m_ptr,256); + //First try + kvi_va_list list; + kvi_va_start(list,fmt); + //print...with max 256 chars + m_len=kvi_vsnprintf(m_ptr,256,fmt,list); + kvi_va_end(list); + + //check if we failed + if(m_len < 0){ + //yes , failed.... + int dummy=256; + do{ //we failed , so retry with 256 more chars + dummy+=256; + //realloc + m_ptr=(char *)kvi_realloc(m_ptr,dummy); + //print... + kvi_va_start(list,fmt); + m_len=kvi_vsnprintf(m_ptr,dummy,fmt,list); + kvi_va_end(list); + } while(m_len < 0); + } + //done... + //now m_len is the length of the written string not including the terminator... + //perfect! :) + m_ptr=(char *)kvi_realloc(m_ptr,m_len+1); + return (*this); +} + +int KviStr::find(const char *str,int idx,bool caseS) const +{ + if(idx >= m_len)return -1; + register char *p=m_ptr + idx; + int len = (int)strlen(str); + if(caseS){ + for(;;){ + while(*p && (*p != *str))p++; + if(*p){ + if(kvi_strEqualCSN(str,p,len))return (p-m_ptr); + else p++; + } else return -1; + } + } else { + for(;;){ + char tmp = toupper(*str); + while(*p && (toupper(*p) != tmp))p++; + if(*p){ + if(kvi_strEqualCIN(str,p,len))return (p-m_ptr); + else p++; + } else return -1; + } + } +} + +int KviStr::find(char c,int idx) const +{ + if(idx >= m_len)return -1; + register char *p=m_ptr + idx; + while(*p && (*p != c))p++; + return (*p ? p-m_ptr : -1); +} + + +int KviStr::findRev(const char *str,int idx,bool caseS) const +{ + if((m_len + idx) < 0)return -1; + register char *p=m_ptr + m_len + idx; + int len = (int)strlen(str); + if(caseS) + { + for(;;) + { + while((p >= m_ptr) && (*p != *str))p--; + if(p >= m_ptr){ + if(kvi_strEqualCSN(str,p,len))return (p-m_ptr); + else p--; + } else return -1; + } + } else { + for(;;){ + char tmp = toupper(*str); + while((p >= m_ptr) && (toupper(*p) != tmp))p--; + if(p >= m_ptr){ + if(kvi_strEqualCIN(str,p,len))return (p-m_ptr); + else p--; + } else return -1; + } + } +} + +int KviStr::findFirstIdx(char c) const +{ + register char *p=m_ptr; + while(*p && (*p != c))p++; + return (*p ? p-m_ptr : -1); +} + +int KviStr::findFirstIdx(const char *str,bool caseS) const +{ + // This function can't be used to search inside + // multibyte encoded strings... convert your + // code to QString and use QString::findRev(). + // We must throw away KviStr at all in this case... + + // return QString(m_ptr).find(QString(str),0,caseS);; + + // Both this KviStr and the const char * str are assumed + // to be in the proper (and same) encoding. + // If KviStr is in encoding A then QString(m_ptr) might + // or not be decoded correctly. + // Also if KviStr is in UTF-8 (for example), then + // a position in QString() does not map to the position in the char array + // since a single UNICODE char may use one or more bytes... + + __range_valid(str); + register char *p=m_ptr; + int len = (int)strlen(str); + if(caseS){ + for(;;){ + while(*p && (*p != *str))p++; + if(*p){ + if(kvi_strEqualCSN(str,p,len))return (p-m_ptr); + else p++; + } else return -1; + } + } else { + // this will NOT work for strings that aren't in the current system encoding :( + for(;;){ + char tmp = toupper(*str); + while(*p && (toupper(*p) != tmp))p++; + if(*p){ + if(kvi_strEqualCIN(str,p,len))return (p-m_ptr); + else p++; + } else return -1; + } + } +} + +int KviStr::findLastIdx(char c) const +{ + //Empty string ? + if(m_len < 1)return -1; + //p points to the last character in the string + register char *p=((m_ptr+m_len)-1); + //go back until we find a match or we run to the first char in the string. + while((*p != c) && (p > m_ptr))p--; + //if *p == c --> matched , else we are at the beginning of the string. + return ((*p == c)? p-m_ptr : -1); +} + +int KviStr::findLastIdx(const char *str,bool caseS) const +{ + // This function can't be used to search inside + // multibyte encoded strings... convert your + // code to QString and use QString::findRev(). + // We must throw away KviStr at all in this case... + + // return QString(m_ptr).findRev(QString(str),-1,caseS); + + __range_valid(str); + //Calc the len of the searched string + int len = (int)strlen(str); + //Too long ? + if(m_len < len)return -1; + //p points to the last character in the string + register char *p=((m_ptr+m_len)-1); + if(caseS){ + for(;;){ + //go back until we find a character that mathes or we run to the first char. + while((*p != *str) && (p > m_ptr))p--; + if(*p == *str){ + //maybe occurence.... + if(kvi_strEqualCSN(str,p,len))return (p-m_ptr); + else { + //Nope...continue if there is more data to check... + if(p == m_ptr)return -1; + p--; + } + } else return -1; //Beginning of the string + } + } else { + // case insensitive + for(;;){ + //go back until we find a character that mathes or we run to the first char. + char tmp = toupper(*str); + while((toupper(*p) != tmp) && (p > m_ptr))p--; + if(toupper(*p) == tmp){ + //maybe occurence.... + if(kvi_strEqualCIN(str,p,len))return (p-m_ptr); + else { + //Nope...continue if there is more data to check... + if(p == m_ptr)return -1; + p--; + } + } else return -1; //Beginning of the string + } + } +} + +KviStr & KviStr::stripWhiteSpace() +{ + // 0123456789 + // abcd 0 + // ^ ^ + // left right + register char *left=m_ptr; + register char *right=m_ptr+m_len-1; + // skip initial spaces + while(isspace(*left))left++; + if(*left){ + // valid string , left points to first non-space + while((right >= left) && isspace(*right))right--; + // 0123456789 + // abcd 0 + // ^ ^ + // left right + m_len = (right - left)+1; + kvi_memmove(m_ptr,left,m_len); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + *(m_ptr+m_len)='\0'; + } else { + m_ptr = (char *)kvi_realloc(m_ptr,1); + *m_ptr = '\0'; + m_len = 0; + } + return (*this); +} + +KviStr & KviStr::stripRightWhiteSpace() +{ + if(*m_ptr) + { + register char *right=m_ptr+m_len-1; + const char *start=right; + while((right >= m_ptr) && isspace( *right ))right--; + if(right != start) + { + m_len = (right - m_ptr) + 1; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + *(m_ptr+m_len)='\0'; + } + } + return (*this); +} + +KviStr & KviStr::stripRight(char c) +{ + if(*m_ptr) + { + register char *right=m_ptr+m_len-1; + const char *start=right; + while((right >= m_ptr) && (*right == c))right--; + if(right != start) + { + m_len = (right - m_ptr) + 1; + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + *(m_ptr+m_len)='\0'; + } + } + return (*this); +} + +KviStr & KviStr::stripSpace() +{ + // 0123456789 + // abcd 0 + // ^ ^ + // left right + register char *left=m_ptr; + register char *right=m_ptr+m_len-1; + // skip initial spaces + while((*left == ' ') || (*left == '\t'))left++; + if(*left){ + // valid string , left points to first non-space + while((right >= left) && ((*right == ' ') || (*right == '\t')))right--; + // 0123456789 + // abcd 0 + // ^ ^ + // left right + m_len = (right - left)+1; + kvi_memmove(m_ptr,left,m_len); + m_ptr = (char *)kvi_realloc(m_ptr,m_len+1); + *(m_ptr+m_len)='\0'; + } else { + m_ptr = (char *)kvi_realloc(m_ptr,1); + *m_ptr = '\0'; + m_len = 0; + } + return (*this); +} + +bool KviStr::isNum() const +{ + register char *p=m_ptr; + while(isspace(*p))p++; + if(*p=='-')p++; + if(!isdigit(*p))return false; + while(isdigit(*p))p++; + while(isspace(*p))p++; + return (*p=='\0'); +} + +bool KviStr::isUnsignedNum() const +{ + register char *p=m_ptr; + while(isspace(*p))p++; + if(!isdigit(*p))return false; + while(isdigit(*p))p++; + while(isspace(*p))p++; + return (*p=='\0'); +} + +static KviStr g_szApplicationWideEmptyString; + +KviStr & KviStr::emptyString() +{ + return g_szApplicationWideEmptyString; +} + + +bool KviStr::ext_contains(register const char * data,const char * item,bool caseS) +{ + if(item && data) + { + int len = (int)strlen(item); + char c = tolower(*item); + if(caseS) + { + while(*data) + { + while(*data && (tolower(*data) != c))data++; + if(*data) + { + if(kvi_strEqualCSN(item,data,len))return true; + else data++; + } + } + } else { + while(*data) + { + while(*data && (tolower(*data) != c))data++; + if(*data) + { + if(kvi_strEqualCIN(item,data,len))return true; + else data++; + } + } + } + } + return false; +} + + +//void KviStr::pointerToBitString(const void * ptr) +//{ +// m_len = (sizeof(void *) * 8); +// m_ptr = kvi_realloc(m_ptr,m_len + 1); +// for(int i=0;i < m_len;i++) +// { +// m_ptr[i] = (ptr & 1) ? '1' : '0'; +// ptr >> 1; +// } +// m_ptr[i] = '\0'; +//} +// +//void * KviStr::bitStringToPointer() +//{ +// if(m_len != (sizeof(void *) * 8))return 0; +// const char * aux = m_ptr; +// void * ptr = 0; +// for(int i=m_len - 1;i >= 0;i--) +// { +// if(m_ptr[i] == '1')ptr &= 1; +// else if(m_ptr[i] !='0')return 0; +// ptr << 1; +// } +// return ptr; +//} + + + + +// static char ascii_jump_table[256]= +// { +// // 000 001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 +// // NUL SOH STX ETX EOT ENQ ACK BEL BS HT LF VT FF CR SO SI +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 +// // DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 +// // ! " # $ % & ' ( ) * + , - . / +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 +// // 0 1 2 3 4 5 6 7 8 9 : ; < = > ? +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 +// // @ A B C D E F G H I J K L M N O +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 +// // P Q R S T U V W X Y Z [ \ ] ^ _ +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 +// // ` a b c d e f g h i j k l m n o +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 +// // p q r s t u v w x y z { | } ~ +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 +// // +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 +// // +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 +// // +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 +// // +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 +// // � � � � � � � � � � � � � � � � +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 +// // � � � � � � � � � � � � � � � � +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 +// // � � � � � � � � � � � � � � � � +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 , +// // 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 +// // � � � � � � � � +// 0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 ,0 +// }; diff --git a/src/kvilib/core/kvi_string.h b/src/kvilib/core/kvi_string.h new file mode 100644 index 00000000..602173cd --- /dev/null +++ b/src/kvilib/core/kvi_string.h @@ -0,0 +1,552 @@ +#ifndef _KVI_STRING_H_ +#define _KVI_STRING_H_ +//============================================================================= +// +// File : kvi_string.h +// Creation date : Fri Mar 19 1999 03:06:26 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2001 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//============================================================================= + +#include "kvi_settings.h" + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#ifdef HAVE_STRINGS_H + #include <strings.h> // useless ? +#endif + +#include <qglobal.h> +#include <qstring.h> + + +#include "kvi_qcstring.h" +#include "kvi_inttypes.h" +#include "kvi_heapobject.h" +#include "kvi_stdarg.h" + + + +// +// sigh... +// IRC is not UNICODE ...(yet) :( +// + +#undef __KVI_EXTERN +#ifdef _KVI_STRING_CPP_ + #define __KVI_EXTERN +#else + #define __KVI_EXTERN extern +#endif + + +__KVI_EXTERN KVILIB_API bool kvi_qstringEqualCI(const QString &s1,const QString &s2); + + +// Include inlined assembly implementations if required +#ifdef COMPILE_ix86_ASM + #include "kvi_strasm.h" +#else + // Returns true if the string str1 is equal to str2. case sensitive. + __KVI_EXTERN KVILIB_API bool kvi_strEqualCS(const char *str1,const char *str2); + // Returns true if the forst len characters of string str1 are equal to str2. + // case sensitive. + // Note that if str1 or str2 are shorter than len characters then are considered as NOT equal! + __KVI_EXTERN KVILIB_API bool kvi_strEqualCSN(const char *str1,const char *str2,int len); + // no such tricks in non-asm + #define kvi_strEqualNoLocaleCI(str1,str2) kvi_strEqualCI(str1,str2) + #define kvi_strEqualNoLocaleCIN(str1,str2,len) kvi_strEqualCIN(str1,str2,len) + #define kvi_strLen(str) strlen(str) +#endif + +// Returns true if the string str1 is equal to str2. +// case insensitive. +__KVI_EXTERN KVILIB_API bool kvi_strEqualCI(const char *str1,const char *str2); +// Returns true if the forst len characters of string str1 are equal to str2. +// case insensitive. +// Note that if str1 or str2 are shorter than len characters then are considered as NOT equal! +__KVI_EXTERN KVILIB_API bool kvi_strEqualCIN(const char *str1,const char *str2,int len); +// My own implementations of strcmp and strncasecmp +// Once I wrote it , I KNOW what they do : ALWAYS :) +// Note that greater here means that comes AFTER in the alphabetic order. +__KVI_EXTERN KVILIB_API int kvi_strcmpCI(const char *str1,const char *str2); +//__KVI_EXTERN KVILIB_API int kvi_strcmpCIN(const char *str1,const char *str2,int len); +__KVI_EXTERN KVILIB_API int kvi_strcmpCS(const char *str1,const char *str2); + +// some wide char stuff +typedef kvi_u16_t kvi_wchar_t; +typedef kvi_u32_t kvi_wslen_t; + +__KVI_EXTERN KVILIB_API kvi_wslen_t kvi_wstrlen(const kvi_wchar_t * str); +__KVI_EXTERN KVILIB_API int kvi_wvsnprintcf(kvi_wchar_t * buffer,kvi_wslen_t len,const char *fmt,kvi_va_list list); +__KVI_EXTERN KVILIB_API int kvi_wvsnprintf(kvi_wchar_t * buffer,kvi_wslen_t len,const kvi_wchar_t *fmt,kvi_va_list list); + +//============================================================================= +// +// A simple string class.<br> +// -No data sharing.<br> +// -Not UNICODE.<br> +// -Has ALWAYS NON-NULL DATA.<br> +// -(Maybe)Unsafe :)<br> +// WARNING : Handle with care and use at own risk :)<br> +// +//============================================================================= + +class KVILIB_API KviStr : public KviHeapObject +{ +public: + // No particular reason for these two names... + // It is just because I like it :) + + enum KviFormatConstructorTag { Format , Sprintf }; + + //============================================================================= + // Constructors + //============================================================================= + + // Empty string == "", len = 0, 1 byte allocated + KviStr(); + + // Deep copy of the NULL TERMINATED string (NULL str SAFE) + KviStr(const char *str); + + // Copy len characters from string str (NOT NULL str SAFE, str MUST be at least len chars long) + KviStr(const char *str,int len); + + // bg and end are pointers to a SINGLE string.<br> + // A string is extracted starting from bg and ending at end (not included).<br> + KviStr(const char *bg,const char *end); + + // Format constructor.<br> + // tag is....yes....a dummy number used to resolve ambiguities.<br> + // It is SAFE: will fail only if we run out of memory,<br> + // but can handle only %s %d %u and %c. + KviStr(KviFormatConstructorTag tag,const char *fmt,...); + + // Carbon copy :)...fast + KviStr(const KviStr &str); + + // Compat with QT...<br> + // WARNING : With QT2.x it WILL loose UNICODE data.<br> + // Safe even if the QString is null. + KviStr(const QString &str); + + KviStr(const KviQCString &str); + + // Fill sonstructor. + // Creates a string long fillLen characters filled with character c.<br> + KviStr(char c,int fillLen = 1); + + KviStr(const kvi_wchar_t * unicode); + + KviStr(const kvi_wchar_t * unicode,int len); + + // just free(m_ptr) + ~KviStr(); +public: + //yes...public..but think it as private...:) + char *m_ptr; // pointer to allocated buffer , do not change this! + int m_len; // string data length not including the terminator + +public: + //============================================================================= + // Basic const interface (read stuff) + //============================================================================= + + // Internal data buffer + char * ptr() const { return m_ptr; }; + // Length: fast, cached + int len() const { return m_len; }; + + // I hate this operator...but sometimes it is really useful + // especially in macros (kvi_options.cpp) + operator const char * () const { return m_ptr; }; + + bool isEmpty() const { return (m_len == 0); }; + bool hasData() const { return (m_len != 0); }; + + // this is better than string = "", it does not call strlen + void clear(); + + // forces the length of this string to be iLen (iLen does NOT include the trailing null : it is automatically added) + void setLength(int iLen); + + // Returns true if there is something "readable" inside the string + bool hasNonWhiteSpaceData() const; + + // Character at zero-based index : always safe! + char & at(int idx) const { return ((idx < m_len) ? m_ptr[idx] : m_ptr[m_len]); }; + + // character checks + bool lastCharIs(char ch) const { return (m_len > 0) ? (*(m_ptr + m_len - 1) == ch) : false; }; + bool firstCharIs(char ch) const { return (*m_ptr == ch); }; + + // upper and lower case copies + KviStr upper() const; + KviStr lower() const; + KviStr upperISO88591() const; + KviStr lowerISO88591() const; + + // left , right & co. + // all parameters are safety-checked + KviStr left(int maxLen) const; + KviStr right(int maxLen) const ; + KviStr middle(int idx,int maxLen) const; + + KviStr leftToFirst(char c,bool bIncluded = false) const; + KviStr leftToLast(char c,bool bIncluded = false) const; +// KviStr leftToFirst(const char * str); const; + + //============================================================================= + // Non-const interface (write stuff) + //============================================================================= + + // Null terminator is NOT included in len + KviStr & setLen(int len); + // str must not be 0, but len can be anything (it is checked) + KviStr & setStr(const char *str,int len = -1); + // Like the special constructor that gets the same args. + void extractFromString(const char *begin,const char *end); + + + // Safe sprintf. This one will never write past the end of the string + // It can handle only %s %d %u and %c format flags. + KviStr & sprintf(const char *fmt,...); + + // append functions + void append(const KviStr &str); + void append(const QString &str); + void append(char c); + void append(const char *str); // str CAN be 0 + void append(const char *str,int len); // str CAN NOT be 0, and MUST be at least len chars long + void append(KviFormatConstructorTag dummy,const char *fmt,...); + + // prepend stuff , same as above + void prepend(const KviStr &str); + void prepend(const char *str); // str CAN be 0 + void prepend(const char *str,int len); // str CAN NOT be 0, and MUST be at least len chars long + + // if lastCharIs ch does nothing otherwise appends it + void ensureLastCharIs(char ch) { if(!lastCharIs(ch))append(ch); }; + + // Change THIS string to uppercase or lowercase + void toUpperISO88591(); + void toUpper(); // this is LOCALE AWARE (in Turkish it maps i to Ý!) + void toLowerISO88591(); + void toLower(); + + // Assignment + KviStr & operator=(const KviStr &str); // deep copy + KviStr & operator=(const char *str); // str can be NULL here + KviStr & operator=(char c); // 2 bytes allocated ,m_len = 1 + KviStr & operator=(const QString &str); + KviStr & operator=(const KviQCString &str); + + // Append operators + KviStr & operator+=(const KviStr &str) { append(str); return (*this); }; + KviStr & operator+=(const char *str) { append(str); return (*this); }; + KviStr & operator+=(char c) { append(c); return (*this); }; + KviStr & operator+=(const QString &str) { append(str); return (*this); }; + + // Comparison + bool equalsCI(const KviStr &other) const { if(m_len != other.m_len)return false; return kvi_strEqualCI(m_ptr,other.m_ptr); }; + bool equalsCS(const KviStr &other) const { if(m_len != other.m_len)return false; return kvi_strEqualCS(m_ptr,other.m_ptr); }; + bool equalsCI(const char * other) const { return kvi_strEqualCI(m_ptr,other); }; + bool equalsCS(const char * other) const { return kvi_strEqualCS(m_ptr,other); }; + bool equalsCIN(const char * other,int len) const { return kvi_strEqualCIN(m_ptr,other,len); }; + bool equalsCSN(const char * other,int len) const { return kvi_strEqualCSN(m_ptr,other,len); }; + + //============================================================================= + // HEX and Base64 stuff + //============================================================================= + + // HEX transforms functions + void bufferToHex(const char *buffer,int len); + // Allocates the needed buffer and returns the allocated length, + // returns -1 in case of error (and allocates nothing) + // The string MUST contain only hex digits, and the digits MUST be in couples. (len % 2) must equal 0! + // So this will fail also if there are leading or trailing spaces! + int hexToBuffer(char ** buffer,bool bNullToNewlines = false); + // BASE64 stuff + void bufferToBase64(const char * buffer,int len); + // same as hexToBuffer but obviously transforms base64 notation to binary data (len % 4) must equal 0! + int base64ToBuffer(char ** buffer,bool bNullToNewlines = false); + + // frees a buffer allocated by hexToBuffer or base64ToBuffer + static void freeBuffer(char * buffer); + + //============================================================================= + // Splitters + //============================================================================= + + // cut + KviStr & cutLeft(int len); // kills the first len characters + KviStr & cutRight(int len); // kills the last len characters + KviStr & cut(int idx,int len); + KviStr & cutToFirst(char c,bool bIncluded = true); // cuts the left part of the string up to the first character c or does nothing if the char c is not in the string + KviStr & cutToLast(char c,bool bIncluded = true); + KviStr & cutFromFirst(char c,bool bIncluded = true); + KviStr & cutFromLast(char c,bool bIncluded = true); + KviStr & cutToFirst(const char *c,bool bIncluded = true); // cuts the left part of the string up to the first character c or does nothing if the char c is not in the string + KviStr & cutToLast(const char *c,bool bIncluded = true); + KviStr & cutFromFirst(const char *c,bool bIncluded = true); + KviStr & cutFromLast(const char *c,bool bIncluded = true); + // & paste + KviStr & insert(int idx,const char *data); + KviStr & insert(int idx,char c); + //Replaces all occurences of char c with the string str + KviStr & replaceAll(char c,const char *str); + //same as above but with a string + KviStr & replaceAll(char *toFind,const char *str,bool bCaseS = true); + + KviStr & transliterate(const char * szToFind,const char * szReplacement); + + // Strips whitespace characters from beginning of this string. + KviStr & stripLeftWhiteSpace(); + KviStr & stripRightWhiteSpace(); + // Stips inital and final WHITESPACE characters (see man isspace),<br> + // and returns a reference to this string. + KviStr & stripWhiteSpace(); + + // Strips spaces and tabs only + KviStr & stripSpace(); + // Strips all occurences of the character c from the beginning of the string.<br> + // Note that c can not be '\0' :) + KviStr & stripLeft(char c); + KviStr & stripRight(char c); + + //============================================================================= + // Tokenize + //============================================================================= + + // Extracts (copy to str and remove) a token from this string ,<br> + // and returns true if there are more tokens to extract<br> + // Does not strip initial separators!!<br> + // str can NOT be this string. + bool getToken(KviStr &str,char sep); + // Does not strip initial separators!<br> + // Can assign also to this string. + KviStr getToken(char sep); + // Extracts a line from the string.<br> + // Returns false if there was no data to extract + bool getLine(KviStr &str); + + // splits this string in a null-terminated array of strings + // separated by sep. + KviStr ** splitToArray(char sep,int max,int * realCount) const; + //KviStr ** splitToArray(const char * sep,int max,int * realCount) const; + static void freeArray(KviStr ** strings); + // joins the array to this string + // if sep is not 0 , it is inserted between the strings + // if bLastSep is true and sep is non 0 , then sep is also appended at the end + // of the buffer (after the last string) + void joinFromArray(KviStr ** strings,const char * sep = 0,bool bLastSep = false); + + //============================================================================= + // Utils + //============================================================================= + + // encodes chars that have nonzero in the jumptable + // into %HH equivalents + KviStr & hexEncodeWithTable(const unsigned char table[256]); + KviStr & hexEncodeWhiteSpace(); + KviStr & hexDecode(const char * pFrom); + KviStr & hexDecode(){ return hexDecode(m_ptr); }; + + //============================================================================= + // Contains / occurence count + //============================================================================= + + // Returns true if at least one occurence of str is found + bool contains(const char *str,bool caseS=true) const; + // Returns true if at least one occurence of character c is found in this string + bool contains(char c,bool caseS=true) const; + // Returns the number of occurences of string str in this string.<br> + // Overlapped matches are counted. + int occurences(const char *str,bool caseS=true) const; + // Returns the number of occurences of character c in this string + int occurences(char c,bool caseS=true) const; + + //============================================================================= + // Find + //============================================================================= + + // Finds the first occurence of the character c in this string,<br> + // and returns its zero-based index or -1 if c can not be found.<br> + // c can NOT be '\0' here. + int findFirstIdx(char c) const; + // Finds the first occurence of the sub-string str in this string,<br> + // and returns its zero-based index or -1 if the sub-string can not be found.<br> + // str can NOT be 0 here. + int findFirstIdx(const char *str,bool caseS = true) const; + // Finds the last occurence of the character c in this string,<br> + // and returns its zero-based index or -1 if the character can not be found. + int findLastIdx(char c) const; + // Finds the last occurence of the sub-string str in this string,<br> + // and returns its zero-based index or -1 if the sub-string can not be found.<br> + // str can NOT be 0 here. + int findLastIdx(const char *str,bool caseS = true) const; + + int find(char c,int startIdx) const; + int find(const char * str,int startIdx,bool caseS = true) const; + int findRev(const char * str,int startIdx,bool caseS = true) const; + + //============================================================================= + // Numbers + //============================================================================= + + // everything in base 10.... no overflow checks here + long toLong(bool *bOk=0) const; + unsigned long toULong(bool *bOk=0) const; + char toChar(bool *bOk=0) const { return (char)toLong(bOk); }; + unsigned char toUChar(bool *bOk=0) const { return (unsigned char)toULong(bOk); }; + int toInt(bool *bOk=0) const { return (int)toLong(bOk); }; + unsigned int toUInt(bool *bOk=0) const { return (unsigned int)toULong(bOk); }; + short toShort(bool *bOk=0) const { return (short)toLong(bOk); }; + unsigned short toUShort(bool *bOk=0) const { return (unsigned short)toLong(bOk); }; + + KviStr & setNum(long num); + KviStr & setNum(unsigned long num); + + KviStr & setNum(int num) { return setNum((long)num); }; + KviStr & setNum(unsigned int num) { return setNum((unsigned long)num); }; + KviStr & setNum(short num) { return setNum((long)num); }; + KviStr & setNum(unsigned short num) { return setNum((unsigned long)num); }; + KviStr & setNum(char num) { return setNum((long)num); }; + KviStr & setNum(unsigned char num) { return setNum((unsigned long)num); }; + + // Retuns true if the string contains only digits and an optional '-' character + // at the beginning.<be> + // Space characters are allowed at the begginning and the end.<br> + // There is no overflow check! + bool isNum() const; + bool isUnsignedNum() const; + + // special functions for multiple bases + long toLongExt(bool *bOk = 0,int base = 0); + // unsigned long toULongExt(bool *bOk = 0,int base = 0); //never used + + // returns an empty string... + // this if often useful! + static KviStr & emptyString(); + + //============================================================================= + // Dead interface + //============================================================================= + + // Transform a pointer to a string with all 0 and 1 + // void pointerToBitString(const void * ptr); + // Get a pointer from a string all of 0 and 1 : return 0 if invalid + // void * bitStringToPointer(); + + //============================================================================= + // "External string" helper functions + //============================================================================= + + // FIXME: Should it be KviStrExt::contains namespace ? + static bool ext_contains(register const char * data,const char * item,bool caseS = true); +}; + +// FIXME: the functions below should end in the KviStr namespace ??? + + +// Cool string parsing function. +// It will extract the first found token from the string aux_ptr , and return +// a pointer to the beginning of the next token , or end of the string. +// It skips the initial sep characters! +__KVI_EXTERN KVILIB_API const char * kvi_extractToken(KviStr &str,const char *aux_ptr,char sep =' '); +// Does not skip the beginning separators! +// Extracts data from the string up to the next separator character or the end of the string. +// and returns a pointer to that separator (or string end). +__KVI_EXTERN KVILIB_API const char * kvi_extractUpTo(KviStr &str,const char *aux_ptr,char sep=' '); +// Reduced vsnprintf... +// Handles %s,%c,%d,%u (%% are TWO percents here and not one.) +// Returns -1 if the formatted string exceeded the buffer length. +// Otherwise returns the length of the formatted buffer...(not including '\0') +__KVI_EXTERN KVILIB_API int kvi_vsnprintf(char *buffer,int len,const char *fmt,kvi_va_list list); +// Reduced vsnprintf: special version for irc. +// Handles %s,%c,%d,%u (%% are TWO percents here and not one.) +// Writes up to 510 characters and terminates the string with a CRLF +// Sets bTruncated if the requested format string was too large to fit in 512 bytes +// otherwise sets it to false; The buffer MUST be at least 512 bytes long. +// Always returns the length of the formatted buffer...(max 512 - min 2=CRLF) +__KVI_EXTERN KVILIB_API int kvi_irc_vsnprintf(char *buffer,const char *fmt,kvi_va_list list,bool *bTruncated); + +// WILDCARD EXPRESSION MATCHING FUNCTIONS + +// Returns true if the two regular expressions with wildcards matches +__KVI_EXTERN KVILIB_API bool kvi_matchWildExpr(register const char *m1,register const char *m2); +// Returns true if the two regular expressions with wildcards matches, case sensitive +//__KVI_EXTERN bool kvi_matchWildExprCS(register const char *m1,register const char *m2); // actually unused +// Same as kvi_matchWildExpr but with an additional char that acts as string terminator +// If there is a match this function returns true and puts the pointers where it stopped in r1 and r2 +__KVI_EXTERN KVILIB_API bool kvi_matchWildExprWithTerminator(register const char *m1,register const char *m2,char terminator, + const char ** r1,const char ** r2); + +// Returns true if the wildcard expression exp matches the string str +__KVI_EXTERN KVILIB_API bool kvi_matchStringCI(register const char * exp,register const char * str); +#define kvi_matchString kvi_matchStringCI +__KVI_EXTERN KVILIB_API bool kvi_matchStringCS(register const char * exp,register const char * str); +__KVI_EXTERN KVILIB_API bool kvi_matchStringWithTerminator(register const char * exp,register const char * str,char terminator,const char ** r1,const char ** r2); + +// This function works like a particular case of strncmp. +// It evaluates if str2 is the terminal part of str1. +// example: if str1 is "this is an experiment" and str2 is "xperiment" +// return 0. +// With the index parameter, the match start on str1 from the specified +// index. For example: +// if str1 is "this is an experiment" and str2 is "an" we have return !0 +// but "this is an experiment" +// 012345678901234567890 +// if we call kvi_strsubRevCS("this is an experiment","an", 9) we got a match. +__KVI_EXTERN KVILIB_API int kvi_strMatchRevCS(const char *str1, const char *str2, int index=-1); + +// KviStr comparison non-member operators +__KVI_EXTERN KVILIB_API inline bool operator==(const KviStr &left,const KviStr &right) +{ return (left.m_len == right.m_len) ? kvi_strEqualCS(left.m_ptr,right.m_ptr) : false; } +__KVI_EXTERN KVILIB_API inline bool operator==(const KviStr &left,const char *right) +{ return kvi_strEqualCS(left.m_ptr,right); } +__KVI_EXTERN KVILIB_API inline bool operator==(const char *left,const KviStr &right) +{ return kvi_strEqualCS(left,right.m_ptr); } +__KVI_EXTERN KVILIB_API inline bool operator!=(const KviStr &left,const KviStr &right) +{ return !kvi_strEqualCS(left.m_ptr,right.m_ptr); } +__KVI_EXTERN KVILIB_API inline bool operator!=(const KviStr &left,const char *right) +{ return !kvi_strEqualCS(left.m_ptr,right); } +__KVI_EXTERN KVILIB_API inline bool operator!=(const char *left,const KviStr &right) +{ return !kvi_strEqualCS(left,right.m_ptr); } + +__KVI_EXTERN KVILIB_API inline KviStr operator+(const KviStr &left,const KviStr &right) +{ KviStr ret(left); ret += right; return ret; } +__KVI_EXTERN KVILIB_API inline KviStr operator+(const KviStr &left,const char *right) +{ KviStr ret(left); ret += right; return ret; } +__KVI_EXTERN KVILIB_API inline KviStr operator+(const char *left,const KviStr &right) +{ KviStr ret(left); ret += right; return ret; } +__KVI_EXTERN KVILIB_API inline KviStr operator+(const KviStr &left,char right) +{ KviStr ret(left); ret += right; return ret; } +__KVI_EXTERN KVILIB_API inline KviStr operator+(char left,const KviStr &right) +{ KviStr ret(left); ret += right; return ret; } + +inline int kvi_compare(const KviStr * p1,const KviStr * p2) +{ + return kvi_strcmpCI(p1->ptr(),p2->ptr()); +} + +#endif //_KVI_STRING_H_ diff --git a/src/kvilib/core/kvi_stringarray.cpp b/src/kvilib/core/kvi_stringarray.cpp new file mode 100644 index 00000000..d160ce28 --- /dev/null +++ b/src/kvilib/core/kvi_stringarray.cpp @@ -0,0 +1,119 @@ +//================================================================================================= +// +// File : kvi_stringarray.cpp +// Creation date : Tue Jun 6 02:20:20 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//================================================================================================= + +#define __KVILIB__ + + +#include "kvi_stringarray.h" +#include "kvi_malloc.h" + +KviStringArray::KviStringArray() +{ + m_uSize = 0; + m_pData = 0; + m_uHighestIdx = 0; +} + +KviStringArray::~KviStringArray() +{ + if(m_pData)clear(); +} + + +void KviStringArray::clear() +{ + if(!m_pData)return; + for(unsigned int i=0;i<m_uSize;i++) + { + if(m_pData[i])delete m_pData[i]; + } + kvi_free(m_pData); + m_pData = 0; + m_uHighestIdx = 0; + m_uSize = 0; +} + +void KviStringArray::insert(unsigned int uIdx,KviStr * pVal) +{ + if(m_uSize <= uIdx) + { + unsigned int uOldSize = m_uSize; + m_uSize = uIdx + KVI_STRING_ARRAY_FREESPACE_SIZE; + m_pData = (KviStr **)kvi_realloc(m_pData,m_uSize * sizeof(KviStr *)); + for(unsigned int u = uOldSize;u < m_uSize;u++) + { + m_pData[u] = 0; + } + } else { + if(m_pData[uIdx])delete m_pData[uIdx]; + } + if(uIdx > m_uHighestIdx)m_uHighestIdx = uIdx; + m_pData[uIdx] = pVal; +} + +void KviStringArray::remove(unsigned int uIdx) +{ + if(uIdx > m_uHighestIdx)return; + if(m_pData[uIdx]) + { + delete m_pData[uIdx]; + m_pData[uIdx] = 0; + if(uIdx == m_uHighestIdx) + { + // shrink the array + if(m_uHighestIdx == 0)clear(); + else { + unsigned int u = m_uHighestIdx - 1; + while(!m_pData[u])u--; + if((m_uHighestIdx - u) > KVI_STRING_ARRAY_FREESPACE_SIZE)shrink(u); + else m_uHighestIdx = u; // just set the max index + } + } + } +} + +void KviStringArray::shrink(unsigned int uMaxItem) +{ + m_uHighestIdx = uMaxItem; + m_uSize = uMaxItem + 1; + m_pData = (KviStr **)kvi_realloc(m_pData,sizeof(KviStr *) * m_uSize); +} + +void KviStringArray::copyFrom(KviStringArray * a) +{ + clear(); + m_uSize = a->m_uSize; + m_uHighestIdx = a->m_uHighestIdx; + if(m_uSize > 0) + { + m_pData = (KviStr **)kvi_malloc(sizeof(KviStr *) * m_uSize); + for(unsigned int i=0;i<m_uSize;i++) + { + if(a->m_pData[i])m_pData[i] = new KviStr(*(a->m_pData[i])); + else m_pData[i] = 0; + } + } else { + m_pData = 0; + } +} diff --git a/src/kvilib/core/kvi_stringarray.h b/src/kvilib/core/kvi_stringarray.h new file mode 100644 index 00000000..3db9a564 --- /dev/null +++ b/src/kvilib/core/kvi_stringarray.h @@ -0,0 +1,55 @@ +#ifndef _KVI_STRINGARRAY_H_ +#define _KVI_STRINGARRAY_H_ +//================================================================================================= +// +// File : kvi_stringarray.h +// Creation date : Tue Jun 6 02:20:20 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//================================================================================================= + +#include "kvi_settings.h" +#include "kvi_string.h" +#include "kvi_heapobject.h" + +#define KVI_STRING_ARRAY_FREESPACE_SIZE 16 + +class KVILIB_API KviStringArray : public KviHeapObject +{ +public: + KviStringArray(); + ~KviStringArray(); +public: + unsigned int m_uSize; + unsigned int m_uHighestIdx; + KviStr ** m_pData; +public: + void clear(); + void insert(unsigned int uIdx,KviStr * pVal); + void copyFrom(KviStringArray * a); + unsigned int size(){ return (m_uSize == 0) ? 0 : (m_uHighestIdx + 1); }; + bool isEmpty(){ return m_uSize == 0; }; + void remove(unsigned int uIdx); + void shrink(unsigned int uMaxItem); + KviStr * uncheckedAt(unsigned int uIdx){ return m_pData[uIdx]; }; + KviStr * at(unsigned int uIdx){ return m_uSize > uIdx ? m_pData[uIdx] : 0; }; + KviStr * getAt(unsigned int uIdx){ KviStr * t = at(uIdx); if(t)m_pData[uIdx] = 0; return t; }; +}; + +#endif //_KVI_STRINGARRAY_H_ diff --git a/src/kvilib/core/kvi_valuelist.h b/src/kvilib/core/kvi_valuelist.h new file mode 100644 index 00000000..fde9d5b6 --- /dev/null +++ b/src/kvilib/core/kvi_valuelist.h @@ -0,0 +1,37 @@ +#ifndef _KVI_VALUELIST_H_ +#define _KVI_VALUELIST_H_ +//================================================================================================= +// +// File : kvi_valuelist.h +// Creation date : Mon Jan 15 2007 04:53 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2007 Szymon Stefanek (pragma at kvirc dot net) +// +// This program is FREE software. You can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your opinion) any later version. +// +// This program is distributed in the HOPE that it will be USEFUL, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, write to the Free Software Foundation, +// Inc. ,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +//================================================================================================= + +#include "kvi_settings.h" + +#ifdef COMPILE_USE_QT4 + #include <q3valuelist.h> + #define KviValueList Q3ValueList +#else + #include <qvaluelist.h> + #define KviValueList QValueList +#endif + +#endif //_KVI_VALUELIST_H_ |