diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 02:13:59 +0000 |
commit | a6d58bb6052ac8cb01805a48c4ad2f129126116f (patch) | |
tree | dd867a099fcbb263a8009a9fb22695b87855dad6 /src/kvilib/system | |
download | kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.tar.gz kvirc-a6d58bb6052ac8cb01805a48c4ad2f129126116f.zip |
Added KDE3 version of kvirc
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kvirc@1095341 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/kvilib/system')
-rw-r--r-- | src/kvilib/system/Makefile.am | 5 | ||||
-rw-r--r-- | src/kvilib/system/kvi_byteorder.h | 62 | ||||
-rw-r--r-- | src/kvilib/system/kvi_env.cpp | 89 | ||||
-rw-r--r-- | src/kvilib/system/kvi_env.h | 60 | ||||
-rw-r--r-- | src/kvilib/system/kvi_library.h | 115 | ||||
-rw-r--r-- | src/kvilib/system/kvi_locale.cpp | 1191 | ||||
-rw-r--r-- | src/kvilib/system/kvi_locale.h | 146 | ||||
-rw-r--r-- | src/kvilib/system/kvi_process.h | 37 | ||||
-rw-r--r-- | src/kvilib/system/kvi_stdarg.h | 65 | ||||
-rw-r--r-- | src/kvilib/system/kvi_thread.cpp | 644 | ||||
-rw-r--r-- | src/kvilib/system/kvi_thread.h | 378 | ||||
-rw-r--r-- | src/kvilib/system/kvi_time.cpp | 135 | ||||
-rw-r--r-- | src/kvilib/system/kvi_time.h | 92 | ||||
-rw-r--r-- | src/kvilib/system/moc_kvi_locale.cpp | 92 | ||||
-rw-r--r-- | src/kvilib/system/moc_kvi_thread.cpp | 104 |
15 files changed, 3215 insertions, 0 deletions
diff --git a/src/kvilib/system/Makefile.am b/src/kvilib/system/Makefile.am new file mode 100644 index 00000000..c84487eb --- /dev/null +++ b/src/kvilib/system/Makefile.am @@ -0,0 +1,5 @@ +############################################################################### +# KVirc IRC client Makefile - 16.12.98 Szymon Stefanek <[email protected]> +############################################################################### + +EXTRA_DIST = *.cpp *.h diff --git a/src/kvilib/system/kvi_byteorder.h b/src/kvilib/system/kvi_byteorder.h new file mode 100644 index 00000000..dea1902d --- /dev/null +++ b/src/kvilib/system/kvi_byteorder.h @@ -0,0 +1,62 @@ +#ifndef _KVI_BYTEORDER_H_ +#define _KVI_BYTEORDER_H_ + +//============================================================================= +// +// File : kvi_byteorder.h +// Creation date : Mon Dec 25 2006 19:56:16 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 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_bswap.h" +#include "kvi_inttypes.h" + + +// +// Byte Orders Reminder +// Number 0xaabbccdd +// Little Endian Stores 0xdd 0xcc 0xbb 0xaa +// Big Endian Stores 0xaa 0xbb 0xcc 0xdd +// Perverse Middle Endian 0xbb 0xaa 0xdd 0xcc or another braindamaged combination (unsupported) +// Network Byte Order is Big Endian +// Intel Stuff uses Little Endian +// + +#ifdef BIG_ENDIAN_MACHINE_BYTE_ORDER + #define kvi_localCpuToLittleEndian16(u) kvi_swap16((kvi_u16_t)(u)) + #define kvi_localCpuToLittleEndian32(u) kvi_swap32((kvi_u32_t)(u)) + #define kvi_localCpuToLittleEndian64(u) kvi_swap64((kvi_u64_t)(u)) + #define kvi_littleEndianToLocalCpu16(u) kvi_swap16((kvi_u16_t)(u)) + #define kvi_littleEndianToLocalCpu32(u) kvi_swap32((kvi_u32_t)(u)) + #define kvi_littleEndianToLocalCpu64(u) kvi_swap64((kvi_u64_t)(u)) +#else + // We ASSUME that the local cpu is little endian.. if it isn't.. well :) + #define LOCAL_CPU_LITTLE_ENDIAN + #define kvi_localCpuToLittleEndian16(u) (u) + #define kvi_localCpuToLittleEndian32(u) (u) + #define kvi_localCpuToLittleEndian64(u) (u) + #define kvi_littleEndianToLocalCpu16(u) (u) + #define kvi_littleEndianToLocalCpu32(u) (u) + #define kvi_littleEndianToLocalCpu64(u) (u) +#endif + + +#endif // !_KVI_BYTEORDER_H_ diff --git a/src/kvilib/system/kvi_env.cpp b/src/kvilib/system/kvi_env.cpp new file mode 100644 index 00000000..1497632e --- /dev/null +++ b/src/kvilib/system/kvi_env.cpp @@ -0,0 +1,89 @@ +//============================================================================= +// +// File : kvi_env.cpp +// Creation date : Sat May 05 2002 02:15:21 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002 Szymon Stefanek ([email protected]) +// +// 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_ENV_CPP_ + +#include "kvi_env.h" +#include "kvi_string.h" +#include "kvi_malloc.h" +#include "kvi_memmove.h" + +#ifndef COMPILE_ON_WINDOWS + +bool kvi_setenv(const char * name,const char * value) +{ +#ifdef HAVE_SETENV + return (setenv(name,value,1) == 0); +#else + #ifdef HAVE_PUTENV + int iLen1 = kvi_strLen(name); + int iLen2 = kvi_strLen(value); + char * buf = (char *)kvi_malloc(iLen1 + iLen2 + 2); + kvi_memmove(buf,name,iLen1); + *(buf + iLen1) = '='; + kvi_memmove(buf + iLen1 + 1,value,iLen2); + *(buf + iLen1 + iLen2 + 1) = '\0'; + int iRet = putenv(buf); + if(iRet != 0) + { + kvi_free(buf); + return false; + } + return true; + #else + // no setenv , no putenv.. what the hell of system is this ? + return false; + #endif +#endif +} + +void kvi_unsetenv(const char * name) +{ +#ifdef HAVE_UNSETENV + unsetenv(name); +#else + #ifdef HAVE_PUTENV + int iLen1 = kvi_strLen(name); + char * buf = (char *)kvi_malloc(iLen1 + 1); + kvi_memmove(buf,name,iLen1); + *(buf + iLen1) = '\0'; + int iRet = putenv(buf); + if(iRet != 0) + { + kvi_free(buf); + } else { + // hmmm + if(kvi_getenv(name) == 0) + { + // ok , the string is not in the environment + // we can free it + kvi_free(buf); + } // else this system sux + } + #endif +#endif +} + +#endif //!COMPILE_ON_WINDOWS diff --git a/src/kvilib/system/kvi_env.h b/src/kvilib/system/kvi_env.h new file mode 100644 index 00000000..b3b24a2f --- /dev/null +++ b/src/kvilib/system/kvi_env.h @@ -0,0 +1,60 @@ +#ifndef _KVI_ENV_H_ +#define _KVI_ENV_H_ + +//============================================================================= +// +// File : kvi_env.h +// Creation date : Sat May 05 2002 02:15:21 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2002 Szymon Stefanek ([email protected]) +// +// 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. +// +//============================================================================= + +//============================================================================= +// Enviroinement function wrappers +//============================================================================= + +#include "kvi_settings.h" + + + +#include <stdlib.h> + +inline char * kvi_getenv(const char * name) +{ +#ifdef HAVE_GETENV + return getenv(name); +#else + return 0; +#endif +} + +#ifdef COMPILE_ON_WINDOWS + #define kvi_setenv(__name,__value) SetEnvironmentVariable(__name,__value) + #define kvi_unsetenv(__name) SetEnvironmentVariable(__name,NULL) +#else + #ifndef _KVI_ENV_CPP_ + KVILIB_API extern bool kvi_setenv(const char * name,const char * value); + KVILIB_API extern void kvi_unsetenv(const char * name); + #endif +#endif + + + + +#endif //_KVI_ENV_H_ diff --git a/src/kvilib/system/kvi_library.h b/src/kvilib/system/kvi_library.h new file mode 100644 index 00000000..393ed5c7 --- /dev/null +++ b/src/kvilib/system/kvi_library.h @@ -0,0 +1,115 @@ +#ifndef _KVI_LIBRARY_H_ +#define _KVI_LIBRARY_H_ + +//===================================================================================== +// +// File : kvi_library.h +// Creation date : Tue Sep 25 16:20:40 2001 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 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. +// +//===================================================================================== + + +//===================================================================================== +// System dynamic linker interface abstraction +//===================================================================================== + + +#include "kvi_settings.h" + + +#ifdef COMPILE_ON_WINDOWS + + //#include <windows.h> + #include <winsock2.h> // this will pull in windows.h + + typedef HMODULE kvi_library_t; + + inline kvi_library_t kvi_library_open(const char * path) + { +#ifndef DEBUG + // this is to avoid the ugly message boxes when the dll has + // ... but do it only in release mode + UINT nOldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); +#endif + kvi_library_t ret = LoadLibrary(path); +#ifndef DEBUG + SetErrorMode(nOldErrorMode); +#endif + return ret; + }; + + inline void kvi_library_close(kvi_library_t lib) + { + FreeLibrary(lib); + }; + + inline void * kvi_library_symbol(kvi_library_t lib,const char * symName) + { + return GetProcAddress(lib,symName); + }; + + inline const char * kvi_library_error() + { + return "Windoze-like error"; + }; + +#else + + #include <dlfcn.h> + + // sparc-unknown-openbsd3.0 (At least) has only RTLD_LAZY + #ifndef RTLD_NOW + #define RTLD_NOW RTLD_LAZY + #endif + #ifndef RTLD_GLOBAL + #define RTLD_GLOBAL 0 + #endif + + typedef void * kvi_library_t; + + inline kvi_library_t kvi_library_open(const char * path) + { + return dlopen(path,RTLD_GLOBAL | RTLD_NOW); + }; + + inline void kvi_library_close(kvi_library_t lib) + { + dlclose(lib); + }; + + + inline void * kvi_library_symbol(kvi_library_t lib,const char * symName) + { + return dlsym(lib,symName); + }; + + inline const char * kvi_library_error() + { + return dlerror(); + }; + + +#endif //!COMPILE_ON_WINDOWS + + +#define kvi_library_load kvi_library_open +#define kvi_library_unload kvi_library_close + +#endif //_KVI_LIBRARY_H_ diff --git a/src/kvilib/system/kvi_locale.cpp b/src/kvilib/system/kvi_locale.cpp new file mode 100644 index 00000000..f49eabe4 --- /dev/null +++ b/src/kvilib/system/kvi_locale.cpp @@ -0,0 +1,1191 @@ +//============================================================================= +// +// File : kvi_locale.cpp +// Creation date : Fri Mar 19 1999 19:08:41 by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-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__ + + +//#define _KVI_DEBUG_CHECK_RANGE_ +#include "kvi_debug.h" +#include "kvi_malloc.h" +#include "kvi_bswap.h" + +#define _KVI_LOCALE_CPP_ +#include "kvi_locale.h" + +#include <qglobal.h> //for debug() +#include <qtextcodec.h> +#include <qdir.h> + +#ifdef COMPILE_USE_QT4 + #include <qlocale.h> +#endif + +#include "kvi_string.h" +#include "kvi_qcstring.h" +#include "kvi_env.h" +#include "kvi_fileutils.h" +#include "kvi_file.h" + + +KVILIB_API KviMessageCatalogue * g_pMainCatalogue = 0; + +static KviStr g_szLang; +static KviTranslator * g_pTranslator = 0; +static KviPointerHashTable<const char *,KviMessageCatalogue> * g_pCatalogueDict = 0; +static QTextCodec * g_pUtf8TextCodec = 0; + + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// +// The following code was extracted and adapted from gutf8.c +// from the GNU GLIB2 package. +// +// gutf8.c - Operations on UTF-8 strings. +// +// Copyright (C) 1999 Tom Tromey +// Copyright (C) 2000 Red Hat, Inc. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the +// Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. +// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef char gchar; +typedef unsigned char guchar; +typedef signed int gssize; +typedef unsigned int gunichar; + + + +#define UNICODE_VALID(Char) \ + ((Char) < 0x110000 && \ + (((Char) & 0xFFFFF800) != 0xD800) && \ + ((Char) < 0xFDD0 || (Char) > 0xFDEF) && \ + ((Char) & 0xFFFE) != 0xFFFE) + +#define CONTINUATION_CHAR \ + if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */ \ + goto error; \ + val <<= 6; \ + val |= (*(guchar *)p) & 0x3f; + + +static const char * +fast_validate (const char *str) + +{ + gunichar val = 0; + gunichar min = 0; + const gchar *p; + + for (p = str; *p; p++) + { + if (*(guchar *)p < 128) + /* done */; + else + { + const gchar *last; + + last = p; + if ((*(guchar *)p & 0xe0) == 0xc0) /* 110xxxxx */ + { + if ((*(guchar *)p & 0x1e) == 0) + goto error; + p++; + if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */ + goto error; + } + else + { + if ((*(guchar *)p & 0xf0) == 0xe0) /* 1110xxxx */ + { + min = (1 << 11); + val = *(guchar *)p & 0x0f; + goto TWO_REMAINING; + } + else if ((*(guchar *)p & 0xf8) == 0xf0) /* 11110xxx */ + { + min = (1 << 16); + val = *(guchar *)p & 0x07; + } + else + goto error; + + p++; + CONTINUATION_CHAR; + TWO_REMAINING: + p++; + CONTINUATION_CHAR; + p++; + CONTINUATION_CHAR; + + if (val < min) + goto error; + + if (!UNICODE_VALID(val)) + goto error; + } + + continue; + + error: + return last; + } + } + + return p; +} + +static const gchar * +fast_validate_len (const char *str, + gssize max_len) + +{ + gunichar val = 0; + gunichar min = 0; + const gchar *p; + + for (p = str; (max_len < 0 || (p - str) < max_len) && *p; p++) + { + if (*(guchar *)p < 128) + /* done */; + else + { + const gchar *last; + + last = p; + if ((*(guchar *)p & 0xe0) == 0xc0) /* 110xxxxx */ + { + if (max_len >= 0 && max_len - (p - str) < 2) + goto error; + + if ((*(guchar *)p & 0x1e) == 0) + goto error; + p++; + if ((*(guchar *)p & 0xc0) != 0x80) /* 10xxxxxx */ + goto error; + } + else + { + if ((*(guchar *)p & 0xf0) == 0xe0) /* 1110xxxx */ + { + if (max_len >= 0 && max_len - (p - str) < 3) + goto error; + + min = (1 << 11); + val = *(guchar *)p & 0x0f; + goto TWO_REMAINING; + } + else if ((*(guchar *)p & 0xf8) == 0xf0) /* 11110xxx */ + { + if (max_len >= 0 && max_len - (p - str) < 4) + goto error; + + min = (1 << 16); + val = *(guchar *)p & 0x07; + } + else + goto error; + + p++; + CONTINUATION_CHAR; + TWO_REMAINING: + p++; + CONTINUATION_CHAR; + p++; + CONTINUATION_CHAR; + + if (val < min) + goto error; + if (!UNICODE_VALID(val)) + goto error; + } + + continue; + + error: + return last; + } + } + + return p; +} + +static bool g_utf8_validate (const char *str, + gssize max_len, + const gchar **end) + +{ + const gchar *p; + + if (max_len < 0) + p = fast_validate (str); + else + p = fast_validate_len (str, max_len); + + if (end) + *end = p; + + if ((max_len >= 0 && p != str + max_len) || + (max_len < 0 && *p != '\0')) + return false; + else + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +// End of gutf8.c +/////////////////////////////////////////////////////////////////////////////////////////////// + + +class KviSmartTextCodec : public QTextCodec +{ +protected: + KviQCString m_szName; + QTextCodec * m_pRecvCodec; + QTextCodec * m_pSendCodec; +public: + KviSmartTextCodec(const char * szName,const char * szChildCodecName,bool bSendInUtf8) + : QTextCodec() + { + m_szName = szName; + if(!g_pUtf8TextCodec) + { + g_pUtf8TextCodec = QTextCodec::codecForName("UTF-8"); + if(!g_pUtf8TextCodec) + { + debug("Can't find the global utf8 text codec!"); + g_pUtf8TextCodec = QTextCodec::codecForLocale(); // try anything else... + } + } + m_pRecvCodec = QTextCodec::codecForName(szChildCodecName); + if(!m_pRecvCodec) + { + debug("Can't find the codec for name %s (composite codec creation)",szName); + m_pRecvCodec = g_pUtf8TextCodec; + } + if(bSendInUtf8) + m_pSendCodec = g_pUtf8TextCodec; + else + m_pSendCodec = m_pRecvCodec; + } +public: + bool ok(){ return m_pRecvCodec && g_pUtf8TextCodec; }; + + virtual int mibEnum () const { return 0; }; + +#ifdef COMPILE_USE_QT4 + virtual QByteArray name() const { return m_szName; }; +protected: + virtual QByteArray convertFromUnicode(const QChar * input,int number,ConverterState * state) const + { + return m_pSendCodec->fromUnicode(input,number,state); + } + virtual QString convertToUnicode(const char * chars,int len,ConverterState * state) const + { + if(g_utf8_validate(chars,len,NULL))return g_pUtf8TextCodec->toUnicode(chars,len,state); + return m_pRecvCodec->toUnicode(chars,len,state); + } +#else +public: + virtual const char * mimeName () const { return m_pRecvCodec->mimeName(); }; + virtual const char * name () const { return m_szName.data(); }; + virtual QTextDecoder * makeDecoder () const { return m_pRecvCodec->makeDecoder(); }; + virtual QTextEncoder * makeEncoder () const { return m_pSendCodec->makeEncoder(); }; + QCString fromUnicode (const QString & uc) const { return m_pSendCodec->fromUnicode(uc); }; + virtual QCString fromUnicode (const QString & uc,int & lenInOut) const { return m_pSendCodec->fromUnicode(uc,lenInOut); }; + QString toUnicode(const char * chars) const + { + if(g_utf8_validate(chars,-1,NULL))return g_pUtf8TextCodec->toUnicode(chars); + return m_pRecvCodec->toUnicode(chars); + }; + virtual QString toUnicode(const char * chars,int len) const + { + if(g_utf8_validate(chars,len,NULL))return g_pUtf8TextCodec->toUnicode(chars,len); + return m_pRecvCodec->toUnicode(chars,len); + }; + QString toUnicode(const QByteArray & a,int len) const + { + if(g_utf8_validate(a.data(),len,NULL))return g_pUtf8TextCodec->toUnicode(a,len); + return m_pRecvCodec->toUnicode(a,len); + }; + QString toUnicode(const QByteArray & a) const + { + if(g_utf8_validate(a.data(),a.size(),NULL))return g_pUtf8TextCodec->toUnicode(a); + return m_pRecvCodec->toUnicode(a); + }; + QString toUnicode(const QCString & a,int len) const + { + if(g_utf8_validate(a.data(),len,NULL))return g_pUtf8TextCodec->toUnicode(a,len); + return m_pRecvCodec->toUnicode(a,len); + }; + QString toUnicode(const QCString & a) const + { + if(g_utf8_validate(a.data(),-1,NULL))return g_pUtf8TextCodec->toUnicode(a); + return m_pRecvCodec->toUnicode(a); + }; + + virtual bool canEncode(QChar ch) const { return m_pSendCodec->canEncode(ch); }; + virtual bool canEncode(const QString &s) const { return m_pSendCodec->canEncode(s); }; + virtual int heuristicContentMatch(const char * chars,int len) const + { + int iii = g_pUtf8TextCodec->heuristicContentMatch(chars,len); + if(iii < 0)return m_pRecvCodec->heuristicContentMatch(chars,len); + return iii; + } + virtual int heuristicNameMatch(const char * hint) const { return 0; }; +#endif +}; + +static KviPointerHashTable<const char *,KviSmartTextCodec> * g_pSmartCodecDict = 0; + + + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// +// The following code was extracted and adapted from gettext.h and gettextP.h +// from the GNU gettext package. +// +// Internal header for GNU gettext internationalization functions. +// Copyright (C) 1995, 1997 Free Software Foundation, Inc. +// +// 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, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with the GNU C Library; see the file COPYING.LIB. If not, +// write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +// Boston, MA 02110-1301, USA. +// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +#include <stdio.h> + +#if HAVE_LIMITS_H || _LIBC + #include <limits.h> +#endif + +// The magic number of the GNU message catalog format. +#define KVI_LOCALE_MAGIC 0x950412de +#define KVI_LOCALE_MAGIC_SWAPPED 0xde120495 + +// Revision number of the currently used .mo (binary) file format. +#define MO_REVISION_NUMBER 0 + + +// Header for binary .mo file format. +struct GnuMoFileHeader +{ + // The magic number. + kvi_u32_t magic; + // The revision number of the file format. + kvi_u32_t revision; + // The number of strings pairs. + kvi_u32_t nstrings; + // Offset of table with start offsets of original strings. + kvi_u32_t orig_tab_offset; + // Offset of table with start offsets of translation strings. + kvi_u32_t trans_tab_offset; + // Size of hashing table. + kvi_u32_t hash_tab_size; + // Offset of first hashing entry. + kvi_u32_t hash_tab_offset; +}; + +struct GnuMoStringDescriptor +{ + // Length of addressed string. + kvi_u32_t length; + // Offset of string in file. + kvi_u32_t offset; +}; + +#define KVI_SWAP_IF_NEEDED(flag,value) (flag ? kvi_swap32(value) : (value)) + +/////////////////////////////////////////////////////////////////////////////////////////////// +// End of gettext.h & gettextP.h +/////////////////////////////////////////////////////////////////////////////////////////////// + + +// HELPERS + +static int somePrimeNumbers[90]= +{ + 257 , 521 , 769 , 1031, 1087, 1091, 1103, 1117, 1123, 1151, // Incomplete *.mo files + 1163, 1171, 1181, 1193, 1201, 1213, 1217, 1223, 1229, 1231, // Complete *.mo files + 1237, 1249, 1259, 1277, 1283, 1289, 1291, 1297, 1307, 1319, + 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1433, + 1447, 1459, 1471, 1481, 1493, 1511, 1523, 1531, 1543, 1553, + 1567, 1571, 1583, 1597, 1609, 1619, 1627, 1637, 1657, 1667, // Too big for KVIrc *.mo files + 1693, 1709, 1721, 1733, 1741, 1753, 1777, 1789, 1811, 1831, + 1907, 2069, 2111, 2221, 2309, 2441, 2531, 2617, 2731, 2837, + 2903, 3121, 3329, 3331, 3767, 4127, 5051, 6089, 7039, 9973 +}; + +int kvi_getFirstBiggerPrime(int number) +{ + for(int i=0;i<90;i++){ + if(somePrimeNumbers[i] >= number)return somePrimeNumbers[i]; + } + return 9973; //error! +} + + +KviMessageCatalogue::KviMessageCatalogue() +{ + //m_uEncoding = 0; + m_pTextCodec = QTextCodec::codecForLocale(); + + //m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(1123,true,false); // dictSize, case sensitive , don't copy keys + m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(32,true,false); // dictSize, case sensitive , don't copy keys + m_pMessages->setAutoDelete(true); +} + +KviMessageCatalogue::~KviMessageCatalogue() +{ + if(m_pMessages) + delete m_pMessages; +} + +bool KviMessageCatalogue::load(const QString& name) +{ + QString szCatalogueFile(name); + + // Try to load the header + KviFile f(szCatalogueFile); + if(!f.openForReading()) + { + debug("[KviLocale]: Failed to open the messages file %s: probably doesn't exist",KviQString::toUtf8(szCatalogueFile).data()); + return false; + } + + GnuMoFileHeader hdr; + + if(f.readBlock((char *)&hdr,sizeof(GnuMoFileHeader)) < (int)sizeof(GnuMoFileHeader)) + { + debug("KviLocale: Failed to read header of %s",KviQString::toUtf8(szCatalogueFile).data()); + f.close(); + return false; + } + + bool bMustSwap = false; + + if(hdr.magic != KVI_LOCALE_MAGIC) + { + if(hdr.magic == KVI_LOCALE_MAGIC_SWAPPED) + { + debug("KviLocale: Swapped magic for file %s: swapping data too",KviQString::toUtf8(szCatalogueFile).data()); + bMustSwap = true; + } else { + debug("KviLocale: Bad locale magic for file %s: not a *.mo file ?",KviQString::toUtf8(szCatalogueFile).data()); + f.close(); + return false; + } + } + + if(KVI_SWAP_IF_NEEDED(bMustSwap,hdr.revision) != MO_REVISION_NUMBER) + { + debug("KviLocale: Invalid *.mo file revision number for file %s",KviQString::toUtf8(szCatalogueFile).data()); + f.close(); + return false; + } + + int numberOfStrings = KVI_SWAP_IF_NEEDED(bMustSwap,hdr.nstrings); + + if(numberOfStrings <= 0) + { + debug("KviLocale: No translated messages found in file %s",KviQString::toUtf8(szCatalogueFile).data()); + f.close(); + return false; + } + + if(numberOfStrings >= 9972) + { + debug("Number of strings too big...sure that it is a KVIrc catalog file ?"); + numberOfStrings = 9972; + } + + // return back + f.seek(0); + + unsigned int fSize = f.size(); + char * buffer = (char *)kvi_malloc(fSize); + + // FIXME: maybe read it in blocks eh ? + if(f.readBlock(buffer,fSize) < (int)fSize) + { + debug("KviLocale: Error while reading the translation file %s",KviQString::toUtf8(szCatalogueFile).data()); + kvi_free(buffer); + f.close(); + return false; + } + + // Check for broken *.mo files + if(fSize < (24 + (sizeof(GnuMoStringDescriptor) * numberOfStrings))) + { + debug("KviLocale: Broken translation file %s (too small for all descriptors)",KviQString::toUtf8(szCatalogueFile).data()); + kvi_free(buffer); + f.close(); + return false; + } + + GnuMoStringDescriptor * origDescriptor = (GnuMoStringDescriptor *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,hdr.orig_tab_offset)); + GnuMoStringDescriptor * transDescriptor = (GnuMoStringDescriptor *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,hdr.trans_tab_offset)); + + // Check again for broken *.mo files + int expectedFileSize = KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[numberOfStrings - 1].offset) + + KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[numberOfStrings - 1].length); + + if(fSize < (unsigned int)expectedFileSize) + { + debug("KviLocale: Broken translation file %s (too small for all the message strings)",KviQString::toUtf8(szCatalogueFile).data()); + kvi_free(buffer); + f.close(); + return false; + } + + // Ok...we can run now + + int dictSize = kvi_getFirstBiggerPrime(numberOfStrings); + if(m_pMessages) + delete m_pMessages; + m_pMessages = new KviPointerHashTable<const char *,KviTranslationEntry>(dictSize,true,false); // dictSize, case sensitive , don't copy keys + m_pMessages->setAutoDelete(true); + + KviStr szHeader; + + for(int i=0;i < numberOfStrings;i++) + { + // FIXME: "Check for NULL inside strings here ?" + //debug("original seems to be at %u and %u byttes long",KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].offset), + // KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].length)); + //debug("translated seems to be at %u and %u byttes long",KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].offset), + // KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].length)); + + KviTranslationEntry * e = new KviTranslationEntry( + (char *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].offset)), + KVI_SWAP_IF_NEEDED(bMustSwap,origDescriptor[i].length), + (char *)(buffer + KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].offset)), + KVI_SWAP_IF_NEEDED(bMustSwap,transDescriptor[i].length)); + + // In some (or all?) *.mo files the first string + // is zero bytes long and the translated one contains + // informations about the translation + if(e->m_szKey.len() == 0) + { + szHeader = e->m_szEncodedTranslation; + delete e; + continue; + } + + m_pMessages->insert(e->m_szKey.ptr(),e); + } + + kvi_free(buffer); + f.close(); + + m_pTextCodec = 0; + + // find out the text encoding , if possible + if(szHeader.hasData()) + { + // find "charset=*\n" + int idx = szHeader.findFirstIdx("charset="); + if(idx != -1) + { + szHeader.cutLeft(idx + 8); + szHeader.cutFromFirst('\n'); + szHeader.stripWhiteSpace(); + m_pTextCodec = KviLocale::codecForName(szHeader.ptr()); + if(!m_pTextCodec) + { + debug("Can't find the codec for charset=%s",szHeader.ptr()); + debug("Falling back to codecForLocale()"); + m_pTextCodec = QTextCodec::codecForLocale(); + } + } + } + + if(!m_pTextCodec) + { + debug("The message catalogue does not have a \"charset\" header"); + debug("Assuming utf8"); // FIXME: or codecForLocale() ? + m_pTextCodec = QTextCodec::codecForName("UTF-8"); + } + + return true; +} + +const char * KviMessageCatalogue::translate(const char *text) +{ + KviTranslationEntry * aux = m_pMessages->find(text); + if(aux)return aux->m_szEncodedTranslation.ptr(); + return text; +} + +const QString & KviMessageCatalogue::translateToQString(const char *text) +{ + KviTranslationEntry * aux = m_pMessages->find(text); + if(aux) + { + if(aux->m_pQTranslation)return *(aux->m_pQTranslation); + aux->m_pQTranslation = new QString(m_pTextCodec->toUnicode(aux->m_szEncodedTranslation.ptr())); + return *(aux->m_pQTranslation); + } + // no translation is available: let's avoid continous string decoding + aux = new KviTranslationEntry(text); + m_pMessages->insert(aux->m_szKey.ptr(),aux); + aux->m_pQTranslation = new QString(m_pTextCodec->toUnicode(aux->m_szEncodedTranslation.ptr())); + return *(aux->m_pQTranslation); +} + + + + +namespace KviLocale +{ +#ifndef QT_NO_BIG_CODECS + #define NUM_ENCODINGS 109 +#else + #define NUM_ENCODINGS 85 +#endif + + + + static EncodingDescription supported_encodings[]= + { + { "UTF-8" , 0 , 0 , "8-bit Unicode" }, + { "ISO-8859-1" , 0 , 0 , "Western, Latin-1" }, + { "ISO-8859-2" , 0 , 0 , "Central European 1" }, + { "ISO-8859-3" , 0 , 0 , "Central European 2" }, + { "ISO-8859-4" , 0 , 0 , "Baltic, Standard" }, + { "ISO-8859-5" , 0 , 0 , "Cyrillic, ISO" }, + { "ISO-8859-6" , 0 , 0 , "Arabic, Standard" }, + { "ISO-8859-7" , 0 , 0 , "Greek" }, + { "ISO-8859-8" , 0 , 0 , "Hebrew, visually ordered" }, + { "ISO-8859-8-i" , 0 , 0 , "Hebrew, logically ordered" }, + { "ISO-8859-9" , 0 , 0 , "Turkish, Latin-5" }, + { "ISO-8859-15" , 0 , 0 , "Western, Latin-1 + Euro" }, + { "KOI8-R" , 0 , 0 , "Cyrillic, KOI" }, + { "KOI8-U" , 0 , 0 , "Ukrainian" }, + { "CP-1250" , 0 , 0 , "Central European 3" }, + { "CP-1251" , 0 , 0 , "Cyrillic, Windows" }, + { "CP-1252" , 0 , 0 , "Western, CP" }, + { "CP-1253" , 0 , 0 , "Greek, CP" }, + { "CP-1256" , 0 , 0 , "Arabic, CP" }, + { "CP-1257" , 0 , 0 , "Baltic, CP" }, + { "CP-1255" , 0 , 0 , "Hebrew, CP" }, + { "CP-1254" , 0 , 0 , "Turkish, CP" }, + { "TIS-620" , 0 , 0 , "Thai" }, +#ifndef QT_NO_BIG_CODECS + { "Big5" , 0 , 0 , "Chinese Traditional" }, + { "Big5-HKSCS" , 0 , 0 , "Chinese Traditional, Hong Kong" }, + { "GB18030" , 0 , 0 , "Chinese Simplified" }, + { "JIS7" , 0 , 0 , "Japanese (JIS7)" }, + { "Shift-JIS" , 0 , 0 , "Japanese (Shift-JIS)" }, + { "EUC-JP" , 0 , 0 , "Japanese (EUC-JP)" }, + { "EUC-KR" , 0 , 0 , "Korean" }, + { "TSCII" , 0 , 0 , "Tamil" }, +#endif + { "ISO-8859-10" , 0 , 0 , "ISO-8859-10" }, + { "ISO-8859-13" , 0 , 0 , "ISO-8859-13" }, + { "ISO-8859-14" , 0 , 0 , "ISO-8859-14" }, + { "IBM-850" , 0 , 0 , "IBM-850" }, + { "IBM-866" , 0 , 0 , "IBM-866" }, + { "CP874" , 0 , 0 , "CP874" }, + + // smart codecs that send in the local charset + { "ISO-8859-1 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western Latin-1, O: Western Latin-1" }, + { "ISO-8859-2 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 1, O: Central European 1" }, + { "ISO-8859-3 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 2, O: Central European 2" }, + { "ISO-8859-4 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Baltic, Standard, O: Baltic, Standard" }, + { "ISO-8859-5 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, ISO, O: Cyrillic, ISO" }, + { "ISO-8859-6 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Arabic, Standard, O: Arabic, Standard" }, + { "ISO-8859-7 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Greek, O: Greek" }, + { "ISO-8859-8 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, visually ordered, O: Hebrew, visually ordered" }, + { "ISO-8859-8-i [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, logically ordered, O: Hebrew, logically ordered" }, + { "ISO-8859-9 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Turkish, Latin-5, O: Turkish, Latin-5" }, + { "ISO-8859-15 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western, Latin-1 + Euro, O: Western, Latin-1 + Euro" }, + { "KOI8-R [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, KOI, O: Cyrillic, KOI" }, + { "KOI8-U [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Ukrainian, O: Ukrainian" }, + { "CP-1250 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Central European 3, O: Central European 3" }, + { "CP-1251 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Cyrillic, Windows, O: Cyrillic, Windows" }, + { "CP-1252 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Western, CP, O: Western, CP" }, + { "CP-1253 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Greek, CP, O: Greek, CP" }, + { "CP-1256 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Arabic, CP, O: Arabic, CP" }, + { "CP-1257 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Baltic, CP, O: Baltic, CP" }, + { "CP-1255 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Hebrew, CP, O: Hebrew, CP" }, + { "CP-1254 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Turkish, CP, O: Turkish, CP" }, + { "TIS-620 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Thai, O: Thai" }, +#ifndef QT_NO_BIG_CODECS + { "Big5 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Traditional, O: Chinese Traditional" }, + { "Big5-HKSCS [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Traditional, Hong Kong, O: Chinese Traditional, Hong Kong" }, + { "GB18030 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Chinese Simplified, O: Chinese Simplified" }, + { "JIS7 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (JIS7), O: Japanese " }, + { "Shift-JIS [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (Shift-JIS), O: Japanese (Shift-JIS)" }, + { "EUC-JP [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Japanese (EUC-JP), O: Japanese (EUC-JP)" }, + { "EUC-KR [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Korean, O: Korean" }, + { "TSCII [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / Tamil, O: Tamil" }, +#endif + { "ISO-8859-10 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-10, O: ISO-8859-10" }, + { "ISO-8859-13 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-13, O: ISO-8859-13" }, + { "ISO-8859-14 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / ISO-8859-14, O: ISO-8859-14" }, + { "IBM-850 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / IBM-850, O: IBM-850" }, + { "IBM-866 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / IBM-866, O: IBM-866" }, + { "CP874 [UTF-8]" , 1 , 0 , "I: 8-bit Unicode / CP874, O: CP874" }, + + // smart codecs that send in utf8 + { "UTF-8 [ISO-8859-1]" , 1 , 1 , "I: 8-bit Unicode / Western Latin-1, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-2]" , 1 , 1 , "I: 8-bit Unicode / Central European 1, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-3]" , 1 , 1 , "I: 8-bit Unicode / Central European 2, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-4]" , 1 , 1 , "I: 8-bit Unicode / Baltic, Standard, O: 8-bit Unicode" }, + + { "UTF-8 [ISO-8859-5]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, ISO, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-6]" , 1 , 1 , "I: 8-bit Unicode / Arabic, Standard, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-7]" , 1 , 1 , "I: 8-bit Unicode / Greek, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-8]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, visually ordered, O: 8-bit Unicode" }, + + { "UTF-8 [ISO-8859-8-i]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, logically ordered, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-9]" , 1 , 1 , "I: 8-bit Unicode / Turkish, Latin-5, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-15]" , 1 , 1 , "I: 8-bit Unicode / Western, Latin-1 + Euro, O: 8-bit Unicode" }, + { "UTF-8 [KOI8-R]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, KOI, O: 8-bit Unicode" }, + + { "UTF-8 [KOI8-U]" , 1 , 1 , "I: 8-bit Unicode / Ukrainian, O: 8-bit Unicode" }, + { "UTF-8 [CP-1250]" , 1 , 1 , "I: 8-bit Unicode / Central European 3, O: 8-bit Unicode" }, + { "UTF-8 [CP-1251]" , 1 , 1 , "I: 8-bit Unicode / Cyrillic, Windows, O: 8-bit Unicode" }, + { "UTF-8 [CP-1252]" , 1 , 1 , "I: 8-bit Unicode / Western, CP, O: 8-bit Unicode" }, + + { "UTF-8 [CP-1253]" , 1 , 1 , "I: 8-bit Unicode / Greek, CP, O: 8-bit Unicode" }, + { "UTF-8 [CP-1256]" , 1 , 1 , "I: 8-bit Unicode / Arabic, CP, O: 8-bit Unicode" }, + { "UTF-8 [CP-1257]" , 1 , 1 , "I: 8-bit Unicode / Baltic, CP, O: 8-bit Unicode" }, + { "UTF-8 [CP-1255]" , 1 , 1 , "I: 8-bit Unicode / Hebrew, CP, O: 8-bit Unicode" }, + + { "UTF-8 [CP-1254]" , 1 , 1 , "I: 8-bit Unicode / Turkish, CP, O: 8-bit Unicode" }, + { "UTF-8 [TIS-620]" , 1 , 1 , "I: 8-bit Unicode / Thai, O: 8-bit Unicode" }, +#ifndef QT_NO_BIG_CODECS + { "UTF-8 [Big5]" , 1 , 1 , "I: 8-bit Unicode / Chinese Traditional, O: 8-bit Unicode" }, + { "UTF-8 [Big5-HKSCS]" , 1 , 1 , "I: 8-bit Unicode / Chinese Traditional, Hong Kong, O: 8-bit Unicode" }, + + { "UTF-8 [GB18030]" , 1 , 1 , "I: 8-bit Unicode / Chinese Simplified, O: 8-bit Unicode" }, + { "UTF-8 [JIS7]" , 1 , 1 , "I: 8-bit Unicode / Japanese (JIS7), O: 8-bit Unicode" }, + { "UTF-8 [Shift-JIS]" , 1 , 1 , "I: 8-bit Unicode / Japanese (Shift-JIS), O: Japanese (Shift-JIS)" }, + { "UTF-8 [EUC-JP]" , 1 , 1 , "I: 8-bit Unicode / Japanese (EUC-JP), O: Japanese (EUC-JP)" }, + + { "UTF-8 [EUC-KR]" , 1 , 1 , "I: 8-bit Unicode / Korean, O: 8-bit Unicode" }, + { "UTF-8 [TSCII]" , 1 , 1 , "I: 8-bit Unicode / Tamil, O: 8-bit Unicode" }, +#endif + { "UTF-8 [ISO-8859-10]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-10, O: 8-bit Unicode" }, + { "UTF-8 [ISO-8859-13]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-13, O: 8-bit Unicode" }, + + { "UTF-8 [ISO-8859-14]" , 1 , 1 , "I: 8-bit Unicode / ISO-8859-14, O: 8-bit Unicode" }, + { "UTF-8 [IBM-850]" , 1 , 1 , "I: 8-bit Unicode / IBM-850, O: 8-bit Unicode" }, + { "UTF-8 [IBM-866]" , 1 , 1 , "I: 8-bit Unicode / IBM-866, O: 8-bit Unicode" }, + { "UTF-8 [CP874]" , 1 , 1 , "I: 8-bit Unicode / CP874, O: 8-bit Unicode" }, + + { 0 , 0 , 0 , 0 } + }; + + EncodingDescription * encodingDescription(int iIdx) + { + if(iIdx > NUM_ENCODINGS)return &(supported_encodings[NUM_ENCODINGS]); + return &(supported_encodings[iIdx]); + } + + QTextCodec * codecForName(const char * szName) + { + KviStr szTmp = szName; + int idx = szTmp.findFirstIdx('['); + if(idx != -1) + { + // composite codec: either UTF-8 [child codec] or child codec [UTF-8] + KviSmartTextCodec * c = g_pSmartCodecDict->find(szName); + if(c)return c; + + + if(kvi_strEqualCIN("UTF-8 [",szName,7)) + { + szTmp.replaceAll("UTF-8 [",""); + szTmp.replaceAll("]",""); + // smart codec that sends UTF-8 + c = new KviSmartTextCodec(szName,szTmp.ptr(),true); + } else { + szTmp.cutFromFirst(' '); + // smart codec that sends child encoding + c = new KviSmartTextCodec(szName,szTmp.ptr(),false); + } + if(c->ok()) + { + g_pSmartCodecDict->replace(szName,c); + return c; + } else { + delete c; + } + } + return QTextCodec::codecForName(szName); + } + + const KviStr & localeName() + { + return g_szLang; + } + + bool loadCatalogue(const QString &name,const QString &szLocaleDir) + { + //debug("Looking up catalogue %s",name); + if(g_pCatalogueDict->find(KviQString::toUtf8(name).data()))return true; // already loaded + + QString szBuffer; + + if(findCatalogue(szBuffer,name,szLocaleDir)) + { + KviMessageCatalogue * c = new KviMessageCatalogue(); + if(c->load(szBuffer)) + { + //debug("KviLocale: loaded catalogue %s",name); + g_pCatalogueDict->insert(KviQString::toUtf8(name).data(),c); + return true; + } + } + return false; + } + + bool unloadCatalogue(const QString &name) + { + //debug("Unloading catalogue : %s",name); + return g_pCatalogueDict->remove(KviQString::toUtf8(name).data()); + } + + bool findCatalogue(QString &szBuffer,const QString& name,const QString& szLocaleDir) + { + KviStr szLocale = g_szLang; + + QString szLocDir = szLocaleDir; + KviQString::ensureLastCharIs(szLocDir,KVI_PATH_SEPARATOR_CHAR); + + KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr()); + + if(KviFileUtils::fileExists(szBuffer))return true; + + if(szLocale.findFirstIdx('.') != -1) + { + // things like en_GB.utf8 + // kill them + szLocale.cutFromFirst('.'); + + KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr()); + if(KviFileUtils::fileExists(szBuffer))return true; + } + + if(szLocale.findFirstIdx('@') != -1) + { + // things like @euro ? + // kill them + szLocale.cutFromFirst('@'); + KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr()); + if(KviFileUtils::fileExists(szBuffer))return true; + } + + if(szLocale.findFirstIdx('_') != -1) + { + // things like en_GB + // kill them + szLocale.cutFromFirst('_'); + KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr()); + if(KviFileUtils::fileExists(szBuffer))return true; + } + + // try the lower case version too + szLocale.toLower(); + KviQString::sprintf(szBuffer,"%Q%Q_%s.mo",&szLocDir,&name,szLocale.ptr()); + if(KviFileUtils::fileExists(szBuffer))return true; + + return false; + } + + // + // This function attempts to determine the current locale + // and then load the corresponding translation file + // from the KVIrc locale directory + // Returns true if the locale was correctly set + // i.e. the locale is C or POSIX (no translation needed) + // or the locale is correctly defined and the + // translation map was sucesfully loaded + // + + void init(QApplication * app,const QString &localeDir) + { + // first of all try to find out the current locale + g_szLang=""; +#ifdef COMPILE_USE_QT4 + QString szLangFile=QString("%1/.kvirc_force_locale").arg(QDir::homePath()); +#else + QString szLangFile=QString("%1/.kvirc_force_locale").arg(QDir::homeDirPath()); +#endif + if(KviFileUtils::fileExists(szLangFile)) + { + QString szTmp; + KviFileUtils::readFile(szLangFile,szTmp); + g_szLang=szTmp; + } + if(g_szLang.isEmpty())g_szLang = kvi_getenv("KVIRC_LANG"); +#ifdef COMPILE_USE_QT4 + if(g_szLang.isEmpty())g_szLang = QLocale::system().name(); +#else + if(g_szLang.isEmpty())g_szLang = QTextCodec::locale(); +#endif + if(g_szLang.isEmpty())g_szLang = kvi_getenv("LC_MESSAGES"); + if(g_szLang.isEmpty())g_szLang = kvi_getenv("LANG"); + if(g_szLang.isEmpty())g_szLang = "en"; + g_szLang.stripWhiteSpace(); + + // the main catalogue is supposed to be kvirc_<language>.mo + g_pMainCatalogue = new KviMessageCatalogue(); + // the catalogue dict + g_pCatalogueDict = new KviPointerHashTable<const char *,KviMessageCatalogue>; + g_pCatalogueDict->setAutoDelete(true); + + // the smart codec dict + g_pSmartCodecDict = new KviPointerHashTable<const char *,KviSmartTextCodec>; + // the Qt docs explicitly state that we shouldn't delete + // the codecs by ourselves... + g_pSmartCodecDict->setAutoDelete(false); + + if(g_szLang.hasData()) + { + QString szBuffer; + if(findCatalogue(szBuffer,"kvirc",localeDir)) + { + g_pMainCatalogue->load(szBuffer); + g_pTranslator = new KviTranslator(app,"kvirc_translator"); + app->installTranslator(g_pTranslator); + } else { + KviStr szTmp = g_szLang; + szTmp.cutFromFirst('.'); + szTmp.cutFromFirst('_'); + szTmp.cutFromFirst('@'); + szTmp.toLower(); + if(!(kvi_strEqualCI(szTmp.ptr(),"en") || + kvi_strEqualCI(szTmp.ptr(),"c") || + kvi_strEqualCI(szTmp.ptr(),"us") || + kvi_strEqualCI(szTmp.ptr(),"gb") || + kvi_strEqualCI(szTmp.ptr(),"posix"))) + { + // FIXME: THIS IS NO LONGER VALID!!! + debug("Can't find the catalogue for locale \"%s\" (%s)",g_szLang.ptr(),szTmp.ptr()); + debug("There is no such translation or the $LANG variable was incorrectly set"); + debug("You can use $KVIRC_LANG to override the catalogue name"); + debug("For example you can set KVIRC_LANG to it_IT to force usage of the it.mo catalogue"); + } + } + } + + //g_pTextCodec = QTextCodec::codecForLocale(); + //if(!g_pTextCodec)g_pTextCodec = QTextCodec::codecForLocale(); + } + + void done(QApplication * app) + { + delete g_pMainCatalogue; + delete g_pCatalogueDict; + delete g_pSmartCodecDict; + g_pMainCatalogue = 0; + g_pCatalogueDict = 0; + g_pSmartCodecDict = 0; + if(g_pTranslator) + { + app->removeTranslator(g_pTranslator); + delete g_pTranslator; + g_pTranslator = 0; + } + } + + KviMessageCatalogue * getLoadedCatalogue(const QString& name) + { + return g_pCatalogueDict->find(KviQString::toUtf8(name).data()); + } + + + const char * translate(const char * text,const char * context) + { + if(context) + { + KviMessageCatalogue * c = g_pCatalogueDict->find(context); + if(!c) + { + // FIXME: Should really try to load the catalogue here! + c = new KviMessageCatalogue(); + g_pCatalogueDict->insert(context,c); + } + return c->translate(text); + } + return g_pMainCatalogue->translate(text); + } + + const QString & translateToQString(const char * text,const char * context) + { + if(context) + { + KviMessageCatalogue * c = g_pCatalogueDict->find(context); + if(!c) + { + // FIXME: Should really try to load the catalogue here! + c = new KviMessageCatalogue(); + g_pCatalogueDict->insert(context,c); + } + return c->translateToQString(text); + } + return g_pMainCatalogue->translateToQString(text); + } +}; + +KviTranslator::KviTranslator(QObject * par,const char * nam) +#ifdef COMPILE_USE_QT4 +: QTranslator(par) +#else +: QTranslator(par,nam) +#endif +{ +} + +KviTranslator::~KviTranslator() +{ +} + +#ifdef COMPILE_USE_QT4 +QString KviTranslator::translate(const char *context,const char * message,const char * comment) const +{ + // we ignore contexts and comments for qt translations + return g_pMainCatalogue->translateToQString(message); +} +#endif + +QString KviTranslator::find(const char *context,const char * message) const +{ + // we ignore contexts for qt translations + return g_pMainCatalogue->translateToQString(message); +} + +#ifndef COMPILE_USE_QT4 +QTranslatorMessage KviTranslator::findMessage(const char * context,const char * sourceText,const char * comment) const +{ + // we ignore contexts for qt translations + return QTranslatorMessage(context,sourceText,comment,g_pMainCatalogue->translateToQString(sourceText)); +} +#endif + +#if 0 + +// a fake table that will force these translations +// to be included in the *.pot file + +static QString fake_translations_table[]= +{ + // global + __tr2qs("OK"), + __tr2qs("Cancel"), + // color dialog + __tr2qs("Select color"), + __tr2qs("&Basic colors"), + __tr2qs("&Custom colors"), + __tr2qs("&Red"), + __tr2qs("&Green"), + __tr2qs("Bl&ue"), + __tr2qs("&Define Custom Colors >>"), + __tr2qs("&Add to Custom Colors"), + // font dialog + __tr2qs("Select Font"), + __tr2qs("&Font"), + __tr2qs("Font st&yle"), + __tr2qs("&Size"), + __tr2qs("Sample"), + __tr2qs("Effects"), + __tr2qs("Stri&keout"), + __tr2qs("&Underline"), + __tr2qs("Scr&ipt"), + //File selector + __tr2qs("Parent Directory"), + __tr2qs("Back"), + __tr2qs("Forward"), + __tr2qs("Reload"), + __tr2qs("New Directory"), + __tr2qs("Bookmarks"), + __tr2qs("Add Bookmark"), + __tr2qs("&Edit Bookmarks"), + __tr2qs("New Bookmark Folder..."), + __tr2qs("Configure"), + __tr2qs("Sorting"), + __tr2qs("By Name"), + __tr2qs("By Date"), + __tr2qs("By Size"), + __tr2qs("Reverse"), + __tr2qs("Directories First"), + __tr2qs("Case Insensitive"), + __tr2qs("Short View"), + __tr2qs("Detailed View"), + __tr2qs("Show Hidden Files"), + __tr2qs("Show Quick Access Navigation Panel"), + __tr2qs("Show Preview"), + __tr2qs("Separate Directories"), + __tr2qs("Often used directories"), + __tr2qs("Desktop"), + __tr2qs("Home Directory"), + __tr2qs("Floppy"), + __tr2qs("Temporary Files"), + __tr2qs("Network"), + __tr2qs("New Directory..."), + __tr2qs("Delete"), + __tr2qs("Thumbnail Previews"), + __tr2qs("Large Icons"), + __tr2qs("Small Icons"), + __tr2qs("Properties..."), + __tr2qs("&Automatic Preview"), + __tr2qs("&Preview"), + __tr2qs("&Location:"), + __tr2qs("&Filter:"), + __tr2qs("All Files"), + __tr2qs("&OK"), + __tr2qs("&Cancel") + +} + +#endif diff --git a/src/kvilib/system/kvi_locale.h b/src/kvilib/system/kvi_locale.h new file mode 100644 index 00000000..bc3ed8eb --- /dev/null +++ b/src/kvilib/system/kvi_locale.h @@ -0,0 +1,146 @@ +#ifndef _KVI_LOCALE_H_ +#define _KVI_LOCALE_H_ + +//============================================================================= +// +// File : kvi_locale.h +// Creation date : Sat Jan 16 1999 18:15:01 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. +// +//============================================================================= + + +#include "kvi_settings.h" +#include "kvi_qstring.h" +#include "kvi_string.h" +#include "kvi_pointerhashtable.h" + +#include <qapplication.h> + +class QTextCodec; +class KviMessageCatalogue; + +namespace KviLocale +{ + typedef struct _EncodingDescription + { + char * szName; + char bSmart; // is it a smart codec ? + char bSendUtf8; // does it send utf8 or the local charset ? + char * szDescription; + } EncodingDescription; + + // you MUST start iterating from 0 and terminate when + // you get an entry with a NULL szName + KVILIB_API EncodingDescription * encodingDescription(int iIdx); + KVILIB_API QTextCodec * codecForName(const char * szName); + KVILIB_API const KviStr & localeName(); + KVILIB_API bool findCatalogue(QString &szBuffer,const QString& name,const QString& szLocaleDir); + KVILIB_API bool loadCatalogue(const QString& name,const QString& szLocaleDir); + KVILIB_API KviMessageCatalogue * getLoadedCatalogue(const QString& name); + KVILIB_API bool unloadCatalogue(const QString& name); + KVILIB_API void init(QApplication * app,const QString& localeDir); + KVILIB_API void done(QApplication * app); + KVILIB_API const char * translate(const char * text,const char * context); + KVILIB_API const QString & translateToQString(const char * text,const char * context); +}; + +// not exported +class KviTranslationEntry +{ +public: + KviStr m_szKey; + KviStr m_szEncodedTranslation; + QString * m_pQTranslation; +public: + KviTranslationEntry(char * keyptr,int keylen,char * trptr,int trlen) + : m_szKey(keyptr,keylen) , m_szEncodedTranslation(trptr,trlen) + { + m_pQTranslation = 0; + } + + KviTranslationEntry(const char * keyandtr) + : m_szKey(keyandtr) , m_szEncodedTranslation(keyandtr) + { + m_pQTranslation = 0; + } + + ~KviTranslationEntry() + { + if(m_pQTranslation)delete m_pQTranslation; + } +}; + + +class KVILIB_API KviMessageCatalogue +{ +public: + KviMessageCatalogue(); + ~KviMessageCatalogue(); +protected: + //KviPointerHashTable<const char *,KviTranslationEntry> * m_pMessages; + KviPointerHashTable<const char *,KviTranslationEntry> * m_pMessages; + QTextCodec * m_pTextCodec; +public: + bool load(const QString& name); + const char * translate(const char * text); + const QString & translateToQString(const char * text); +}; + +#ifndef _KVI_LOCALE_CPP_ + extern KVILIB_API KviMessageCatalogue * g_pMainCatalogue; +#endif // !_KVI_LOCALE_CPP_ + +#define __tr(__text__) g_pMainCatalogue->translate(__text__) +#define __tr_no_lookup(__text__) __text__ +#define __tr_no_xgettext(__text__) g_pMainCatalogue->translate(__text__) + +#define __tr2qs(__text__) g_pMainCatalogue->translateToQString(__text__) +#define __tr2qs_no_xgettext(__text__) g_pMainCatalogue->translateToQString(__text__) + +#define __tr_ctx(__text__,__context__) KviLocale::translate(__text__,__context__) +#define __tr_no_lookup_ctx(__text__,__context__) __text__ +#define __tr_no_xgettext_ctx(__text__,__context__) KviLocale::translate(__text__,__context__) +#define __tr2qs_ctx(__text__,__context__) KviLocale::translateToQString(__text__,__context__) +#define __tr2qs_ctx_no_xgettext(__text__,__context__) KviLocale::translateToQString(__text__,__context__) +#define __tr2qs_no_lookup(__text__) __text__ + +#include <qtranslator.h> +#include <qstring.h> + +class KVILIB_API KviTranslator : public QTranslator +{ + Q_OBJECT + public: + KviTranslator(QObject * parent,const char * name); + ~KviTranslator(); + public: +#ifdef COMPILE_USE_QT4 + virtual QString translate(const char * context,const char * message,const char * comment) const; +#endif + // Deprecated in qt 4.x + virtual QString find(const char * context,const char * message) const; +#ifndef COMPILE_USE_QT4 + // Dead in qt 4.x + virtual QTranslatorMessage findMessage(const char * context,const char * sourceText,const char * comment = 0) const; +#endif +}; + + +#endif //!_KVI_LOCALE_H_ diff --git a/src/kvilib/system/kvi_process.h b/src/kvilib/system/kvi_process.h new file mode 100644 index 00000000..ea2275dc --- /dev/null +++ b/src/kvilib/system/kvi_process.h @@ -0,0 +1,37 @@ +#ifndef _KVI_PROCESS_H_ +#define _KVI_PROCESS_H_ +//============================================================================= +// +// File : kvi_process.h +// Creation date : Tue Jan 30 2007 04:05:41 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 <q3process.h> + #define KviProcess Q3Process +#else + #include <qprocess.h> + #define KviProcess QProcess +#endif + +#endif //!_KVI_PROCESS_H_ diff --git a/src/kvilib/system/kvi_stdarg.h b/src/kvilib/system/kvi_stdarg.h new file mode 100644 index 00000000..15c5e078 --- /dev/null +++ b/src/kvilib/system/kvi_stdarg.h @@ -0,0 +1,65 @@ +#ifndef _KVI_STDARG_H_ +#define _KVI_STDARG_H_ + +//============================================================================= +// +// File : kvi_stdarg.h +// Creation date : Sat Jan 03 2004 02:08:14 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2004 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 <stdarg.h> + +#define kvi_va_list va_list +#define kvi_va_start va_start +// +// kvi_va_start_by_reference should be used when the last known argument +// is a reference type and not a pointer +// +// int SomeClass::sprintf(const QString &fmt,...) +// { +// kvi_va_list list; +// kvi_va_start_by_reference(list,fmt); +// ... +// } +// +// +#ifdef COMPILE_ON_WINDOWS + #define kvi_va_start_by_reference(__list,__arg) \ + { \ + int supercalifragilisticoespiralidoso=_INTSIZEOF(__arg); \ + __asm lea eax,__arg \ + __asm add eax,supercalifragilisticoespiralidoso \ + __asm mov __list,eax \ + } +#elif defined(__GNUC__) + // gcc doesn't use the second argument + // so we just fool it to avoid the warnings + #define kvi_va_start_by_reference(__list,__arg) va_start(__list,((const char *)(&(__arg)))) +#else + #define kvi_va_start_by_reference va_start +#endif +#define kvi_va_arg va_arg +#define kvi_va_end va_end + + + +#endif //_KVI_STDARG_H_ diff --git a/src/kvilib/system/kvi_thread.cpp b/src/kvilib/system/kvi_thread.cpp new file mode 100644 index 00000000..e9ec3ac5 --- /dev/null +++ b/src/kvilib/system/kvi_thread.cpp @@ -0,0 +1,644 @@ +//============================================================================= +// +// File : kvi_thread.cpp +// Creation date : Tue Jul 6 1999 16:04:45 CEST by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 1999-2005 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__ + + +#ifndef _GNU_SOURCE + #define _GNU_SOURCE +#endif + +#include "kvi_thread.h" + +#ifdef COMPILE_ON_WINDOWS + #include <io.h> // for _pipe() +#else + #include <unistd.h> //for pipe() and other tricks + #include <signal.h> // on Windows it is useless + #include <fcntl.h> +#endif + +#include <errno.h> + + +#include "kvi_string.h" +#include "kvi_settings.h" +#include "kvi_error.h" + + +#include <qapplication.h> + + +static void kvi_threadIgnoreSigalarm() +{ + // On Windows this stuff is useless anyway +#ifdef COMPILE_IGNORE_SIGALARM + #ifndef COMPILE_ON_WINDOWS + // Funky hack for some Solaris machines (maybe others ?) + // For an obscure (at least to me) reason + // when using threads ,some part of the system + // starts kidding us by sending a SIGALRM in apparently + // "random" circumstances. (Xlib ?) (XServer ?) + // The default action for SIGALRM is to exit the application. + // Could not guess more about this stuff... + // Here goes a "blind" hack for that. + + // Update: now we have an explaination too + // + // From: "Andre Stechert" (astechert at email dot com) + // To: pragma at kvirc dot net + // Subject: sigalarm on solaris ... + // Date: 26/7/2005 09:36 + + // Hi, + // I noticed in your readme that you were having problems with sigalarm + // in your solaris port and you weren't sure why. I quickly scanned your + // source code and noticed that you use usleep and threads. That's the problem, + // if you haven't already figured it out. On Solaris, usleep is implemented with + // SIGALARM. So is threading. So if you the active thread changes while + // a usleep is in progress, bang, the process is dead. + // + // There is no real feedback on this at the moment: if somebody + // experiences the problems please drop me a mail at pragma at kvirc dot net + // and we'll try to look for a better solution. + // If the explaination is correct then KVIrc could even lock up on those machines + // (never returning from an usleep() call ?)... + + struct sigaction ignr_act; + ignr_act.sa_handler = SIG_IGN; + sigemptyset(&ignr_act.sa_mask); + + #ifdef SA_NOMASK + ignr_act.sa_flags = SA_NOMASK; + #else + ignr_act.sa_flags = 0; + #endif + + #ifdef SA_RESTART + ignr_act.sa_flags |= SA_RESTART; + #endif + + if(sigaction(SIGALRM,&ignr_act,0) == -1)debug("Failed to set SIG_IGN for SIGALRM."); + #endif +#endif +} + +#ifndef COMPILE_ON_WINDOWS + +static void kvi_threadSigpipeHandler(int) +{ + debug("Thread ????: Caught SIGPIPE: ignoring."); +} + +#endif + +static void kvi_threadCatchSigpipe() +{ + // On windows this stuff is useless +#ifndef COMPILE_ON_WINDOWS + struct sigaction act; + act.sa_handler=&kvi_threadSigpipeHandler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGPIPE); + // CC: take care of SunOS which automatically restarts interrupted system + // calls (and thus does not have SA_RESTART) +#ifdef SA_NOMASK + act.sa_flags = SA_NOMASK; +#else + act.sa_flags = 0; +#endif + +#ifdef SA_RESTART + act.sa_flags |= SA_RESTART; +#endif + + if(sigaction(SIGPIPE,&act,0L) == -1)debug("Failed to set the handler for SIGPIPE."); +#endif +} + +static void kvi_threadInitialize() +{ +#ifndef COMPILE_ON_WINDOWS + kvi_threadIgnoreSigalarm(); + kvi_threadCatchSigpipe(); +#endif +} + + + +#define KVI_THREAD_PIPE_SIDE_MASTER 0 +#define KVI_THREAD_PIPE_SIDE_SLAVE 1 + +// the maximum length of the slave->master queue +// over this length , the slave is forced to usleep() +#define KVI_THREAD_MAX_EVENT_QUEUE_LENGTH 50 + +static KviThreadManager * g_pThreadManager = 0; + +void KviThreadManager::globalInit() +{ + kvi_threadInitialize(); // we want this to apply to the main thread too + g_pThreadManager = new KviThreadManager(); +} + +void KviThreadManager::globalDestroy() +{ + delete g_pThreadManager; + g_pThreadManager = 0; +} + +KviThreadManager::KviThreadManager() +: QObject() +{ + if(g_pThreadManager)debug("Hey...what are ya doing ?"); + + + m_pMutex = new KviMutex(); + m_pThreadList = new KviPointerList<KviThread>; + m_pThreadList->setAutoDelete(false); + + m_iWaitingThreads = 0; + +#ifndef COMPILE_ON_WINDOWS + + m_iTriggerCount = 0; + + m_pEventQueue = new KviPointerList<KviThreadPendingEvent>; + m_pEventQueue->setAutoDelete(true); + + if(pipe(m_fd) != 0) + { + debug("Ops...thread manager pipe creation failed (%s)",KviQString::toUtf8(KviError::getDescription(KviError::translateSystemError(errno))).data()); + } + + if(fcntl(m_fd[KVI_THREAD_PIPE_SIDE_SLAVE],F_SETFL,O_NONBLOCK) == -1) + { + debug("Ops...thread manager slave pipe initialisation failed (%s)",KviQString::toUtf8(KviError::getDescription(KviError::translateSystemError(errno))).data()); + } + + if(fcntl(m_fd[KVI_THREAD_PIPE_SIDE_MASTER],F_SETFL,O_NONBLOCK) == -1) + { + debug("Ops...thread manager master pipe initialisation failed (%s)",KviQString::toUtf8(KviError::getDescription(KviError::translateSystemError(errno))).data()); + } + + m_pSn = new QSocketNotifier(m_fd[KVI_THREAD_PIPE_SIDE_MASTER],QSocketNotifier::Read); + connect(m_pSn,SIGNAL(activated(int)),this,SLOT(eventsPending(int))); + m_pSn->setEnabled(true); +#endif +} + + +KviThreadManager::~KviThreadManager() +{ + m_pMutex->lock(); + // Terminate all the slaves + while(KviThread *t = m_pThreadList->first()) + { + m_pMutex->unlock(); + delete t; + m_pMutex->lock(); + } + + // there are no more child threads + // thus no more slave events are sent. + // Disable the socket notifier, we no longer need it +#ifndef COMPILE_ON_WINDOWS + m_pSn->setEnabled(false); + delete m_pSn; + m_pSn = 0; +#endif + + // we're no longer in this world + g_pThreadManager = 0; + +#ifndef COMPILE_ON_WINDOWS + // close the pipes + close(m_fd[KVI_THREAD_PIPE_SIDE_SLAVE]); + close(m_fd[KVI_THREAD_PIPE_SIDE_MASTER]); + // Kill the pending events + while(KviThreadPendingEvent *ev = m_pEventQueue->first()) + { + delete ev->e; + m_pEventQueue->removeFirst(); + } + delete m_pEventQueue; + m_pEventQueue = 0; +#endif + + m_pMutex->unlock(); + + // finish the cleanup + delete m_pMutex; + m_pMutex = 0; + delete m_pThreadList; + m_pThreadList = 0; + + // byez :) +} + +void KviThreadManager::killPendingEvents(QObject * receiver) +{ +#ifndef COMPILE_ON_WINDOWS + if(!g_pThreadManager)return; + g_pThreadManager->killPendingEventsByReceiver(receiver); +#endif +} + +void KviThreadManager::killPendingEventsByReceiver(QObject * receiver) +{ +#ifndef COMPILE_ON_WINDOWS + KviPointerList<KviThreadPendingEvent> l; + l.setAutoDelete(false); + m_pMutex->lock(); + for(KviThreadPendingEvent * ev = m_pEventQueue->first();ev;ev = m_pEventQueue->next()) + { + if(ev->o == receiver)l.append(ev); + } + for(KviThreadPendingEvent * ev = l.first();ev;ev = l.next()) + { + delete ev->e; + m_pEventQueue->removeRef(ev); + } + m_pMutex->unlock(); +#endif +} + +void KviThreadManager::registerSlaveThread(KviThread *t) +{ + m_pMutex->lock(); + m_pThreadList->append(t); + m_pMutex->unlock(); +} + +void KviThreadManager::unregisterSlaveThread(KviThread *t) +{ + m_pMutex->lock(); + m_pThreadList->removeRef(t); + m_pMutex->unlock(); +} + +void KviThreadManager::postSlaveEvent(QObject *o,QEvent *e) +{ +#ifdef COMPILE_ON_WINDOWS + QApplication::postEvent(o,e); // we believe this to be thread-safe +#else + KviThreadPendingEvent * ev = new KviThreadPendingEvent; + ev->o = o; + ev->e = e; + + m_pMutex->lock(); + + // if the queue gets too long , make this (slave) thread sleep + + // there is a special case where we can't stop the slaves posting events + // it's when a thread-master-side is waiting for it's thread-slave-side + // it the thread-master-side runs in the application main thread then + // the main thread is sleeping and can't process events. + // Since we can't be really sure that the thread-master-side will be running + // on the main application thread we also can't artificially process the events. + // So the solution is to skip this algorithm when at least one + // thread is in waiting state. + while((m_pEventQueue->count() > KVI_THREAD_MAX_EVENT_QUEUE_LENGTH) && (m_iWaitingThreads < 1)) + { + // wait for the master to process the queue + + m_pMutex->unlock(); + + // WARNING : This will fail if for some reason + // the master thread gets here! It will wait indefinitely for itself + // if(pthread_self() != m_hMasterThread) ... ???? + +#ifdef COMPILE_ON_WINDOWS + ::Sleep(1); // 1ms +#else + // FIXME : use nanosleep() ? + ::usleep(1000); // 1 ms +#endif + m_pMutex->lock(); + } + + m_pEventQueue->append(ev); + // Write bulk to the pipe... but only if there is no other wakeup pending + if(m_iTriggerCount < 1) + { + // I don't know if writing to a pipe is reentrant + // thus, in doubt, the write is interlocked (it's non blocking anyway) + int written = write(m_fd[KVI_THREAD_PIPE_SIDE_SLAVE],"?",1); + if(written < 1) + { + // ops.. failed to write down the event.. + // this is quite irritating now... + debug("Ops.. failed to write down the trigger"); + // FIXME: maybe a single shot timer ? + } else { + m_iTriggerCount++; + } + } // else no need to trigger : there is a wakeup pending in there + + m_pMutex->unlock(); + +#endif +} + +void KviThreadManager::eventsPending(int fd) +{ +#ifndef COMPILE_ON_WINDOWS + char buf[10]; + // do we need to check for errors here ? + int readed = read(fd,buf,10); + + m_pMutex->lock(); + // welcome to the critical section :) + + // grab the first event in the queue + while(KviThreadPendingEvent *ev = m_pEventQueue->first()) + { + // allow the other threads to post events: + // unlock the event queue + m_pMutex->unlock(); + // let the app process the event + // DANGER ! + QApplication::postEvent(ev->o,ev->e); + + // jump out of the loop if we have been destroyed + if(!g_pThreadManager)return; + // ufff... we're still alive :))) + + // regrab the event queue + m_pMutex->lock(); + // remove the event we have just processed + m_pEventQueue->removeRef(ev); + // here we're looping locked and havn't decremended the trigger count + } + // decrement the trigger count on the line: still atomic + if(readed >= 0) + { + if(readed < m_iTriggerCount) + { + m_iTriggerCount -= readed; + } else { + m_iTriggerCount = 0; + } + } + + // ok , job done.. can relax now + m_pMutex->unlock(); + +#endif +} + +void KviThreadManager::threadEnteredWaitState() +{ + m_pMutex->lock(); + m_iWaitingThreads++; + m_pMutex->unlock(); +} + +void KviThreadManager::threadLeftWaitState() +{ + m_pMutex->lock(); + m_iWaitingThreads--; + if(m_iWaitingThreads < 0) + { + debug("Ops.. got a negative number of waiting threads ?"); + m_iWaitingThreads = 0; + } + m_pMutex->unlock(); +} + +#ifndef COMPILE_ON_WINDOWS + bool KviMutex::locked() + { + if(!kvi_threadMutexTryLock(&m_mutex))return true; + kvi_threadMutexUnlock(&m_mutex); + return false; + } +#endif + +#ifdef COMPILE_ON_WINDOWS +DWORD WINAPI internal_start_thread(LPVOID arg) +{ + // Slave thread... + ((KviThread *)arg)->internalThreadRun_doNotTouchThis(); + return 0; +} +#else +static void * internal_start_thread(void * arg) +{ + // Slave thread... + ((KviThread *)arg)->internalThreadRun_doNotTouchThis(); + return 0; +} +#endif + +KviThread::KviThread() +{ + g_pThreadManager->registerSlaveThread(this); + m_pRunningMutex = new KviMutex(); + setRunning(false); + setStartingUp(false); +} + +KviThread::~KviThread() +{ +// debug(">> KviThread::~KviThread() : (this = %d)",this); + wait(); + delete m_pRunningMutex; + g_pThreadManager->unregisterSlaveThread(this); +// debug("<< KviThread::~KviThread() : (this = %d)",this); +} + +void KviThread::setRunning(bool bRunning) +{ + m_pRunningMutex->lock(); + m_bRunning = bRunning; + m_pRunningMutex->unlock(); +} + +void KviThread::setStartingUp(bool bStartingUp) +{ + m_pRunningMutex->lock(); + m_bStartingUp = bStartingUp; + m_pRunningMutex->unlock(); +} + +bool KviThread::isRunning() +{ + bool bRunning = true; + m_pRunningMutex->lock(); + bRunning = m_bRunning; + m_pRunningMutex->unlock(); + return bRunning; +} + +bool KviThread::isStartingUp() +{ + bool bIsStartingUp = true; + m_pRunningMutex->lock(); + bIsStartingUp = m_bStartingUp; + m_pRunningMutex->unlock(); + return bIsStartingUp; +} + +bool KviThread::start() +{ + // We're on the master side thread here! + if(isStartingUp() || isRunning())return false; + setStartingUp(true); + return kvi_threadCreate(&m_thread,internal_start_thread,this); +} + +void KviThread::wait() +{ + // We're on the master side here...and we're waiting the slave to exit +// debug(">> KviThread::wait() (this=%d)",this); + while(isStartingUp())usleep(500); // sleep 500 microseconds +// debug("!! KviThread::wait() (this=%d)",this); + g_pThreadManager->threadEnteredWaitState(); + while(isRunning()) + { + usleep(500); // sleep 500 microseconds + } + g_pThreadManager->threadLeftWaitState(); +// debug("<< KviThread::wait() (this=%d)",this); +} + +void KviThread::exit() +{ + // We're on the slave side thread here! (m_bRunning is true , m_bStartingUp is false) + setRunning(false); + kvi_threadExit(); +} + +void KviThread::internalThreadRun_doNotTouchThis() +{ + // we're on the slave thread here! +// debug(">> KviThread::internalRun (this=%d)",this); + setRunning(true); + setStartingUp(false); + kvi_threadInitialize(); + run(); + setRunning(false); +// debug("<< KviThread::internalRun (this=%d",this); +} + +void KviThread::usleep(unsigned long usec) +{ +#ifdef COMPILE_ON_WINDOWS + int s = usec / 1000; + if(s < 1)s = 1; + ::Sleep(s); // Sleep one millisecond...this is the best that we can do +#else + // FIXME : use nanosleep() ? + ::usleep(usec); +#endif +} + +void KviThread::msleep(unsigned long msec) +{ +#ifdef COMPILE_ON_WINDOWS + ::Sleep(msec); +#else + // FIXME : use nanosleep() ? + ::usleep(msec * 1000); +#endif +} + +void KviThread::sleep(unsigned long sec) +{ +#ifdef COMPILE_ON_WINDOWS + ::Sleep(sec * 1000); +#else + ::sleep(sec); +#endif +} + +void KviThread::postEvent(QObject * o,QEvent *e) +{ + // slave side + g_pThreadManager->postSlaveEvent(o,e); +} + + + +KviSensitiveThread::KviSensitiveThread() +: KviThread() +{ + m_pLocalEventQueueMutex = new KviMutex(); + m_pLocalEventQueue = new KviPointerList<KviThreadEvent>; + m_pLocalEventQueue->setAutoDelete(false); +} + +KviSensitiveThread::~KviSensitiveThread() +{ +// debug("Entering KviSensitiveThread::~KviSensitiveThread (this=%d)",this); + terminate(); +// debug("KviSensitiveThread::~KviSensitiveThread : terminate called (This=%d)",this); + m_pLocalEventQueueMutex->lock(); + m_pLocalEventQueue->setAutoDelete(true); + delete m_pLocalEventQueue; + m_pLocalEventQueue = 0; + m_pLocalEventQueueMutex->unlock(); + delete m_pLocalEventQueueMutex; + m_pLocalEventQueueMutex = 0; +// debug("Exiting KviSensitiveThread::~KviSensitiveThread (this=%d)",this); +} + +void KviSensitiveThread::enqueueEvent(KviThreadEvent *e) +{ +// debug(">>> KviSensitiveThread::enqueueEvent() (this=%d)",this); + m_pLocalEventQueueMutex->lock(); + if(!m_pLocalEventQueue) + { + // ops...already terminated (???)...eat the event and return + delete e; + m_pLocalEventQueueMutex->unlock(); + return; + } + m_pLocalEventQueue->append(e); + m_pLocalEventQueueMutex->unlock(); +// debug("<<< KviSensitiveThread::enqueueEvent() (this=%d)",this); +} + +KviThreadEvent * KviSensitiveThread::dequeueEvent() +{ +// debug(">>> KviSensitiveThread::dequeueEvent() (this=%d)",this); + KviThreadEvent * ret; + m_pLocalEventQueueMutex->lock(); + ret = m_pLocalEventQueue->first(); + if(ret)m_pLocalEventQueue->removeFirst(); + m_pLocalEventQueueMutex->unlock(); +// debug("<<< KviSensitiveThread::dequeueEvent() (this=%d)",this); + return ret; +} + +void KviSensitiveThread::terminate() +{ +// debug("Entering KviSensitiveThread::terminate (this=%d)",this); + enqueueEvent(new KviThreadEvent(KVI_THREAD_EVENT_TERMINATE)); +// debug("KviSensitiveThread::terminate() : event enqueued waiting (this=%d)",this); + wait(); +// debug("Exiting KviSensitiveThread::terminate (this=%d)",this); +} + diff --git a/src/kvilib/system/kvi_thread.h b/src/kvilib/system/kvi_thread.h new file mode 100644 index 00000000..bd6dab3b --- /dev/null +++ b/src/kvilib/system/kvi_thread.h @@ -0,0 +1,378 @@ +#ifndef _KVI_THREAD_H_ +#define _KVI_THREAD_H_ +// +// File : kvi_thread.h +// Creation date : Mon May 17 1999 04:26:41 CEST 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. +// + +#include "kvi_settings.h" +#include "kvi_heapobject.h" +#include "kvi_string.h" + +#include <qnamespace.h> +#include <qobject.h> +#include <qsocketnotifier.h> +#include "kvi_pointerlist.h" +#include <qevent.h> + + +// +// Simple thread implementation +// This is enough for KVIrc needs +// HANDLE WITH CARE +// + + +// Portability stuff + +#ifdef COMPILE_ON_WINDOWS + + #include <winsock2.h> // this will pull in windows.h and will avoid windock.h inclusion + //#include <windows.h> + // Windoze thread abstraction layer + #define kvi_mutex_t HANDLE + inline void kvi_threadMutexInit(kvi_mutex_t * _pMutex_t) + { + *_pMutex_t = CreateMutex(0,0,NULL); + } + #define kvi_threadMutexLock(_pMutex_t) WaitForSingleObject(*_pMutex_t,INFINITE) + #define kvi_threadMutexUnlock(_pMutex_t) ReleaseMutex(*_pMutex_t) + #define kvi_threadMutexDestroy(_pMutex_t) CloseHandle(*_pMutex_t) + inline bool kvi_threadMutexTryLock(kvi_mutex_t *_pMutex_t) + { + return (WaitForSingleObject(*_pMutex_t,0) == WAIT_OBJECT_0); + } + + #define kvi_thread_t HANDLE + + inline bool kvi_threadCreate(kvi_thread_t *t,LPTHREAD_START_ROUTINE start_routine,void * arg) + { + DWORD dwThreadId; + *t = CreateThread(NULL,0,start_routine,arg,0,&dwThreadId); + return (*t != NULL); + } + + #define kvi_threadExit() ExitThread(0) + +#else + #ifdef COMPILE_THREADS_USE_POSIX + // Glibc pthread implementation + + #include <pthread.h> + #include <errno.h> // for EBUSY + + // Mutex stuff + #define kvi_mutex_t pthread_mutex_t + #define kvi_threadMutexInit(_pMutex_t) pthread_mutex_init(_pMutex_t,0) + #define kvi_threadMutexLock(_pMutex_t) pthread_mutex_lock(_pMutex_t) + #define kvi_threadMutexUnlock(_pMutex_t) pthread_mutex_unlock(_pMutex_t) + #define kvi_threadMutexDestroy(_pMutex_t) pthread_mutex_destroy(_pMutex_t) + inline bool kvi_threadMutexTryLock(kvi_mutex_t *_pMutex_t) + { + return (pthread_mutex_trylock(_pMutex_t) != EBUSY); + } + // Actually unused + // #define kvi_threadMutexTryLock(_pMutex_t) pthread_mutex_trylock(_pMutex_t) + + // Thread stuff + #define kvi_thread_t pthread_t + + inline bool kvi_threadCreate(kvi_thread_t *t,void * (*start_routine)(void *),void * arg) + { + pthread_attr_t a; + pthread_attr_init(&a); + pthread_attr_setinheritsched(&a,PTHREAD_INHERIT_SCHED); + pthread_attr_setdetachstate(&a,PTHREAD_CREATE_DETACHED); + + int ret = pthread_create(t,&a,start_routine,arg); + + pthread_attr_destroy(&a); + return (ret == 0); + } + + // We don't care about exit codes at all + #define kvi_threadExit() pthread_exit(0) + #else + #ifdef COMPILE_THREADS_USE_SOLARIS_LIBTHREAD + // Native solaris implementation + #include <thread.h> + #include <synch.h> + #include <errno.h> + + // Mutex stuff + #define kvi_mutex_t mutex_t + #define kvi_threadMutexInit(_pMutex_t) mutex_init(_pMutex_t,0,0) + #define kvi_threadMutexLock(_pMutex_t) mutex_lock(_pMutex_t) + #define kvi_threadMutexUnlock(_pMutex_t) mutex_unlock(_pMutex_t) + #define kvi_threadMutexDestroy(_pMutex_t) mutex_destroy(_pMutex_t) + inline bool kvi_threadMutexTryLock(kvi_mutex_t *_pMutex_t) + { + return (mutex_trylock(_pMutex_t) != EBUSY); + }; + // Actually unused + // #define kvi_threadMutexTryLock(_pMutex_t) mutex_trylock(_pMutex_t) + + // Thread stuff + #define kvi_thread_t thread_t + + inline bool kvi_threadCreate(kvi_thread_t *t,void * (*start_routine)(void *),void *arg) + { + return (thr_create(0,0,start_routine,arg,THR_DETACHED,t) == 0); + } + + // We don't care about exit codes at all + #define kvi_threadExit() thr_exit(0) + #else +// FIXME: #warning "Missing a decent thread implementation: we're going to fail , sorry!" + #endif + #endif +#endif + +class KVILIB_API KviMutex : public KviHeapObject +{ +private: + kvi_mutex_t m_mutex; +#ifdef COMPILE_ON_WINDOWS + bool m_bLocked; +#endif +public: + KviMutex(){ kvi_threadMutexInit(&m_mutex); }; + virtual ~KviMutex(){ kvi_threadMutexDestroy(&m_mutex); }; +public: +#ifdef COMPILE_ON_WINDOWS + void lock(){ kvi_threadMutexLock(&m_mutex); m_bLocked = true; }; + void unlock(){ m_bLocked = false; kvi_threadMutexUnlock(&m_mutex); }; + bool locked(){ return m_bLocked; }; +#else + void lock(){ kvi_threadMutexLock(&m_mutex); }; + void unlock(){ kvi_threadMutexUnlock(&m_mutex); }; + bool locked(); +#endif +}; + + +// simple thread class implementation +// this is also called "Blind" thread class + +class KVILIB_API KviThread : public KviHeapObject +{ +public: + KviThread(); + virtual ~KviThread(); +private: + kvi_thread_t m_thread; + bool m_bRunning; + bool m_bStartingUp; + KviMutex * m_pRunningMutex; + KviPointerList<QEvent> * m_pLocalEventQueue; +public: + // public KviThread interface + // HANDLE WITH CARE + + // Runs the thread...call only from external threads!!! :) + // This function returns true if the child thread has been succesfully created + // this des not mean that run() is being already executed... + // isStartingUp() will return true from this moment until + // the child thread jumps into run() where it will be set to running state (isRunning() == true) + // and removed from startingUp state. + bool start(); + // Returns the state of the thread...safe to call from anywhere + bool isRunning(); + // Returns the state of the thread...safe to call from anywhere + bool isStartingUp(); // start() called , but not in run() yet... + // Waits for the termination of this thread: call only from external threads!!! :) + void wait(); + // DO NOT TOUCH THIS ONE! + void internalThreadRun_doNotTouchThis(); + + static void sleep(unsigned long sec); + static void msleep(unsigned long msec); + static void usleep(unsigned long usec); +protected: + // protected KviThread interface + // HANDLE WITH CARE TOO! + + // Reimplement this with your job + virtual void run(){}; + // Terminates the execution of the calling thread + void exit(); + // The tricky part: threadsafe event dispatching + // Slave thread -> main thread objects + void postEvent(QObject *o,QEvent *e); +private: + void setRunning(bool bRunning); + void setStartingUp(bool bStartingUp); +}; + +// QEvent::Type for Thread events +#define KVI_THREAD_EVENT (((int)QEvent::User) + 2000) + +// CONSTANTS FOR KviThreadEvent::eventId(); + +/////////////////////////////////////////////////////////////// +// extern -> slave thread + +// Your reimplementation of KviSensitiveThread MUST handle this +// and exit when this event is received + +// Terminate is a plain KviThreadEvent +#define KVI_THREAD_EVENT_TERMINATE 0 + +/////////////////////////////////////////////////////////////// +// slave thread -> master object + +// The following standard events are sent from the thread to the master object + +// The following are plain KviThreadEvent objects +#define KVI_THREAD_EVENT_SUCCESS 100 + +// The following are KviThreadDataEvent<int> +#define KVI_THREAD_EVENT_STATECHANGE 150 + +// The following are KviThreadDataEvent<KviStr> +#define KVI_THREAD_EVENT_MESSAGE 200 +#define KVI_THREAD_EVENT_WARNING 201 +#define KVI_THREAD_EVENT_ERROR 202 +#define KVI_THREAD_EVENT_DATA 203 + +// The following is KviThreadDataEvent<KviDataBuffer> +#define KVI_THREAD_EVENT_BINARYDATA 300 + +// The user events +#define KVI_THREAD_USER_EVENT_BASE 1000 + +// #warning "Get rid of the m_szMessage member of KviThreadEvent : eventual data should be passed with a KviThreadDataEvent" + +// Base class for all thread events +class KVILIB_API KviThreadEvent : public QEvent, public KviHeapObject +{ +protected: + int m_eventId; + KviThread * m_pSender; +public: + KviThreadEvent(int evId,KviThread * sender = 0) + : QEvent((QEvent::Type)KVI_THREAD_EVENT) , m_eventId(evId) , m_pSender(sender) {}; + virtual ~KviThreadEvent(){}; +public: + // This is the sender of the event + // WARNING : this MAY be null , threads CAN send anonymous events + KviThread * sender(){ return m_pSender; }; + int id(){ return m_eventId; }; +}; + +template<class TData> class KviThreadDataEvent : public KviThreadEvent +{ +protected: + TData * m_pData; +public: + KviThreadDataEvent(int evId,TData * pData = 0,KviThread * sender = 0) + : KviThreadEvent(evId,sender){ m_pData = pData; }; + virtual ~KviThreadDataEvent(){ if(m_pData)delete m_pData; }; +public: + void setData(TData * d){ if(m_pData)delete m_pData; m_pData = d; }; + TData * getData(){ TData * aux = m_pData; m_pData = 0; return aux; }; + TData * data(){ return m_pData; }; +}; + +// A thread that has also an internal event queue +// so events can be posted from the master side to the slave one +// Reimplementations of this class should periodically check +// dequeueEvent() and eventually process the incoming events (and then DELETE it) + +// KVI_THREAD_EVENT_TERMINATE should be always handled by the reimplementation +// and it should always exit (cleanly) when this event is received + + +class KVILIB_API KviSensitiveThread : public KviThread +{ +public: + KviSensitiveThread(); + virtual ~KviSensitiveThread(); +protected: + KviMutex * m_pLocalEventQueueMutex; + KviPointerList<KviThreadEvent> * m_pLocalEventQueue; +public: + // enqueues an event directed to THIS thread + // the event must be allocated with NEW and + // will be destroyed on the slave side + void enqueueEvent(KviThreadEvent *e); + // enqueues a terminate event and waits() for the slave thread + // the slave thread MUST handle KVI_THREAD_EVENT_TERMINATE + void terminate(); +protected: + // slave side: + // returns the first event in the local queue + // the event MUST BE DELETED after processing + KviThreadEvent * dequeueEvent(); +}; + +// =============================================================================================// +// This is private stuff...only KviThread and KviApp may use it +// and may call only specific functions...don't touch. + +typedef struct _KviThreadPendingEvent +{ + QObject *o; + QEvent *e; +} KviThreadPendingEvent; + +class KVILIB_API KviThreadManager : public QObject +{ + friend class KviApp; + friend class KviThread; + Q_OBJECT +protected: + // These should be private...but we don't want anyone to complain + // Treat as private plz. + KviThreadManager(); + ~KviThreadManager(); +public: + static void killPendingEvents(QObject * receiver); +private: +#ifndef COMPILE_ON_WINDOWS + QSocketNotifier * m_pSn; +#endif + KviMutex * m_pMutex; // This class performs only atomic operations + KviPointerList<KviThread> * m_pThreadList; + int m_iWaitingThreads; +#ifndef COMPILE_ON_WINDOWS + KviPointerList<KviThreadPendingEvent> * m_pEventQueue; + int m_fd[2]; + int m_iTriggerCount; +#endif +protected: + // Public to KviThread only + void registerSlaveThread(KviThread *t); + void unregisterSlaveThread(KviThread *t); + + void threadEnteredWaitState(); + void threadLeftWaitState(); + + void postSlaveEvent(QObject *o,QEvent *e); + void killPendingEventsByReceiver(QObject * receiver); + // Public to KviApp only + static void globalInit(); + static void globalDestroy(); +private slots: + void eventsPending(int fd); +}; + + +#endif //!_KVI_THREAD_H_ diff --git a/src/kvilib/system/kvi_time.cpp b/src/kvilib/system/kvi_time.cpp new file mode 100644 index 00000000..f500b7d4 --- /dev/null +++ b/src/kvilib/system/kvi_time.cpp @@ -0,0 +1,135 @@ +//============================================================================= +// +// File : kvi_time.cpp +// Creation date : Tue Sep 25 17:35:13 2001 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 2001-2004 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_time.h" +#include "kvi_qstring.h" +#include "kvi_locale.h" + +#ifdef COMPILE_ON_WINDOWS + #include <windows.h> // GetSystemTime + + // Call SystemTimeToFileTime to copy the system time to a FILETIME structure. + // Call GetSystemTime to get the current system time to pass to SystemTimeToFileTime. + // Copy the contents of the FILETIME structure to a ULARGE_INTEGER structure. + // Initialize a SYSTEMTIME structure with the date and time of the first second of January 1, 1970. + // Call SystemTimeToFileTime, passing the SYSTEMTIME structure initialized in Step 3 to the call. + // Copy the contents of the FILETIME structure returned by SystemTimeToFileTime in Step 4 to + // a second ULARGE_INTEGER. The copied value should be greater than or equal to the value copied + // in Step 2. Subtract the 64-bit value in the ULARGE_INTEGER structure initialized in Step 2 + // from the 64-bit value of the ULARGE_INTEGER structure initialized in Step 5. + // This produces a value in 100-nanosecond intervals since January 1, 1970. + // To convert this value to seconds, divide by 10,000,000. + + // buah buah buahhhh lol ghgh :DDDDDDDDD + + void kvi_gettimeofday(struct timeval * tmv,struct timezone *) + { + SYSTEMTIME st; + GetSystemTime(&st); + + // this is simply fucked up.. + // to minimize the possibility of wrapping we use also the day field. + // we actually give something that is near the number of seconds from the beginning + // of the current month... + // We cannot use the wMonth field since the months have variable length :/ + tmv->tv_sec = (st.wDay * 86400) + (st.wHour * 3600) + (st.wMinute * 60) + (st.wSecond); + tmv->tv_usec = st.wMilliseconds * 1000; + } +#endif + +KviMSecTimeInterval::KviMSecTimeInterval() +{ + m_uReferenceSecs = 0; + m_uReferenceUSecs = 0; +} + + +unsigned long KviMSecTimeInterval::mark() +{ + struct timeval tmv; + kvi_gettimeofday(&tmv,0); + unsigned long uDiff = ((((unsigned long)(tmv.tv_sec)) - m_uReferenceSecs) * 1000); + if(((unsigned long)(tmv.tv_usec)) > m_uReferenceUSecs)uDiff += (((unsigned long)(tmv.tv_usec) - m_uReferenceUSecs) / 1000); + else uDiff -= ((m_uReferenceUSecs - (unsigned long)(tmv.tv_usec)) / 1000); + m_uReferenceSecs = (unsigned long)tmv.tv_sec; + m_uReferenceUSecs = (unsigned long)tmv.tv_usec; + return uDiff; +} + +namespace KviTimeUtils +{ + void secondsToDaysHoursMinsSecs(unsigned int uSecs, + unsigned int * uD,unsigned int * uH,unsigned int * uM,unsigned int * uS) + { + *uD = uSecs / 86400; + uSecs = uSecs % 86400; + *uH = uSecs / 3600; + uSecs = uSecs % 3600; + *uM = uSecs / 60; + *uS = uSecs % 60; + } + + QString formatTimeInterval(unsigned int uSeconds,int iFlags) + { + unsigned int d,h,m,s; + secondsToDaysHoursMinsSecs(uSeconds,&d,&h,&m,&s); + QString ret; + // the following tricks maybe will help translators a bit... + if(iFlags & FillWithHypens) + { + ret = __tr2qs("- d -- h -- m -- s"); + } else { + if((iFlags & NoLeadingEmptyIntervals) && (d == 0)) + { + if(h > 0) + { + if(iFlags & NoLeadingZeroes) + KviQString::sprintf(ret,__tr2qs("%u h %u m %u s"),h,m,s); + else + KviQString::sprintf(ret,__tr2qs("%u h %u%u m %u%u s"),h,m / 10,m % 10,s / 10,s % 10); + } else { + if(m > 0) + { + if(iFlags & NoLeadingZeroes) + KviQString::sprintf(ret,__tr2qs("%u m %u s"),m,s); + else + KviQString::sprintf(ret,__tr2qs("%u m %u%u s"),m,s / 10,s % 10); + } else { + KviQString::sprintf(ret,__tr2qs("%u s"),s); + } + } + } else { + if(iFlags & NoLeadingZeroes) + KviQString::sprintf(ret,__tr2qs("%u d %u h %u m %u s"),d,h,m,s); + else + KviQString::sprintf(ret,__tr2qs("%u d %u%u h %u%u m %u%u s"),d,h / 10,h % 10,m / 10,m % 10,s / 10,s % 10); + } + } + return ret; + } + +} diff --git a/src/kvilib/system/kvi_time.h b/src/kvilib/system/kvi_time.h new file mode 100644 index 00000000..309eec10 --- /dev/null +++ b/src/kvilib/system/kvi_time.h @@ -0,0 +1,92 @@ +#ifndef _KVI_TIME_H_ +#define _KVI_TIME_H_ + +//============================================================================= +// +// File : kvi_time.h +// Creation date : Tue Sep 25 17:28:46 2001 GMT by Szymon Stefanek +// +// This file is part of the KVirc irc client distribution +// Copyright (C) 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 <qstring.h> + + +#include <time.h> // for time() + +#define kvi_unixTime() time(0) +#define kvi_timeSpan(_time_now,_time_before) ((_time_now) - (_time_before)) +#define kvi_secondsSince(_that_time_t) kvi_timeSpan(kvi_unixTime(),_that_time_t) + +#define kvi_time_t time_t + +#ifdef COMPILE_ON_WINDOWS + + #include <winsock2.h> // struct timeval + + extern KVILIB_API void kvi_gettimeofday(struct timeval * tmv,struct timezone * tmz); + +#else //!COMPILE_ON_WINDOWS + + #include <sys/time.h> // gettimeofday() , struct timeval + + inline void kvi_gettimeofday(struct timeval * tmv,struct timezone * tmz) + { + gettimeofday(tmv,tmz); + }; + +#endif //!COMPILE_ON_WINDOWS + +// this works for time intervals a bit longer than 24 days +class KVILIB_API KviMSecTimeInterval +{ +public: + KviMSecTimeInterval(); +protected: + unsigned long m_uReferenceSecs; + unsigned long m_uReferenceUSecs; +public: + // returns the number of milliseconds since + // mark() was last called (and thus marks + // the beginning of a new interval). + unsigned long mark(); + // this wors ONLY in the same second that mark was called in + // and returns the tv_sec field of the gettimeofday() + // (remember that gettimeofday() is broken on windows) + unsigned long secondsCounter(){ return m_uReferenceSecs; }; +}; + +namespace KviTimeUtils +{ + + + // splits the time span uSecs in days, hours, minutes and seconds + KVILIB_API void secondsToDaysHoursMinsSecs(unsigned int uSecs, + unsigned int * uD,unsigned int * uH,unsigned int * uM,unsigned int * uS); + // returns a string formatted like x d x h xx m xx s + enum FormatTimeSpanFlags { + NoLeadingEmptyIntervals = 1, // causes the leading empty intervals to be omitted + NoLeadingZeroes = 2, // no leading zeroes are printed in hours and seconds + FillWithHypens = 4 // uses only -- %d -- %h -- etc.. discards all other flags + }; + KVILIB_API QString formatTimeInterval(unsigned int uSeconds,int iFlags = 0); +}; + +#endif //_KVI_TIME_H_ diff --git a/src/kvilib/system/moc_kvi_locale.cpp b/src/kvilib/system/moc_kvi_locale.cpp new file mode 100644 index 00000000..52e2ae22 --- /dev/null +++ b/src/kvilib/system/moc_kvi_locale.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** KviTranslator meta object code from reading C++ file 'kvi_locale.h' +** +** Created: Sun Mar 23 20:56:24 2008 +** by: The Qt MOC ($Id: qt/moc_yacc.cpp 3.3.8 edited Feb 2 14:59 $) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#undef QT_NO_COMPAT +#include "kvi_locale.h" +#include <qmetaobject.h> +#include <qapplication.h> + +#include <private/qucomextra_p.h> +#if !defined(Q_MOC_OUTPUT_REVISION) || (Q_MOC_OUTPUT_REVISION != 26) +#error "This file was generated using the moc from 3.3.8. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +const char *KviTranslator::className() const +{ + return "KviTranslator"; +} + +QMetaObject *KviTranslator::metaObj = 0; +static QMetaObjectCleanUp cleanUp_KviTranslator( "KviTranslator", &KviTranslator::staticMetaObject ); + +#ifndef QT_NO_TRANSLATION +QString KviTranslator::tr( const char *s, const char *c ) +{ + if ( qApp ) + return qApp->translate( "KviTranslator", s, c, QApplication::DefaultCodec ); + else + return QString::fromLatin1( s ); +} +#ifndef QT_NO_TRANSLATION_UTF8 +QString KviTranslator::trUtf8( const char *s, const char *c ) +{ + if ( qApp ) + return qApp->translate( "KviTranslator", s, c, QApplication::UnicodeUTF8 ); + else + return QString::fromUtf8( s ); +} +#endif // QT_NO_TRANSLATION_UTF8 + +#endif // QT_NO_TRANSLATION + +QMetaObject* KviTranslator::staticMetaObject() +{ + if ( metaObj ) + return metaObj; + QMetaObject* parentObject = QTranslator::staticMetaObject(); + metaObj = QMetaObject::new_metaobject( + "KviTranslator", parentObject, + 0, 0, + 0, 0, +#ifndef QT_NO_PROPERTIES + 0, 0, + 0, 0, +#endif // QT_NO_PROPERTIES + 0, 0 ); + cleanUp_KviTranslator.setMetaObject( metaObj ); + return metaObj; +} + +void* KviTranslator::qt_cast( const char* clname ) +{ + if ( !qstrcmp( clname, "KviTranslator" ) ) + return this; + return QTranslator::qt_cast( clname ); +} + +bool KviTranslator::qt_invoke( int _id, QUObject* _o ) +{ + return QTranslator::qt_invoke(_id,_o); +} + +bool KviTranslator::qt_emit( int _id, QUObject* _o ) +{ + return QTranslator::qt_emit(_id,_o); +} +#ifndef QT_NO_PROPERTIES + +bool KviTranslator::qt_property( int id, int f, QVariant* v) +{ + return QTranslator::qt_property( id, f, v); +} + +bool KviTranslator::qt_static_property( QObject* , int , int , QVariant* ){ return FALSE; } +#endif // QT_NO_PROPERTIES diff --git a/src/kvilib/system/moc_kvi_thread.cpp b/src/kvilib/system/moc_kvi_thread.cpp new file mode 100644 index 00000000..556849a9 --- /dev/null +++ b/src/kvilib/system/moc_kvi_thread.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** KviThreadManager meta object code from reading C++ file 'kvi_thread.h' +** +** Created: Sun Mar 23 20:56:25 2008 +** by: The Qt MOC ($Id: qt/moc_yacc.cpp 3.3.8 edited Feb 2 14:59 $) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#undef QT_NO_COMPAT +#include "kvi_thread.h" +#include <qmetaobject.h> +#include <qapplication.h> + +#include <private/qucomextra_p.h> +#if !defined(Q_MOC_OUTPUT_REVISION) || (Q_MOC_OUTPUT_REVISION != 26) +#error "This file was generated using the moc from 3.3.8. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +const char *KviThreadManager::className() const +{ + return "KviThreadManager"; +} + +QMetaObject *KviThreadManager::metaObj = 0; +static QMetaObjectCleanUp cleanUp_KviThreadManager( "KviThreadManager", &KviThreadManager::staticMetaObject ); + +#ifndef QT_NO_TRANSLATION +QString KviThreadManager::tr( const char *s, const char *c ) +{ + if ( qApp ) + return qApp->translate( "KviThreadManager", s, c, QApplication::DefaultCodec ); + else + return QString::fromLatin1( s ); +} +#ifndef QT_NO_TRANSLATION_UTF8 +QString KviThreadManager::trUtf8( const char *s, const char *c ) +{ + if ( qApp ) + return qApp->translate( "KviThreadManager", s, c, QApplication::UnicodeUTF8 ); + else + return QString::fromUtf8( s ); +} +#endif // QT_NO_TRANSLATION_UTF8 + +#endif // QT_NO_TRANSLATION + +QMetaObject* KviThreadManager::staticMetaObject() +{ + if ( metaObj ) + return metaObj; + QMetaObject* parentObject = QObject::staticMetaObject(); + static const QUParameter param_slot_0[] = { + { "fd", &static_QUType_int, 0, QUParameter::In } + }; + static const QUMethod slot_0 = {"eventsPending", 1, param_slot_0 }; + static const QMetaData slot_tbl[] = { + { "eventsPending(int)", &slot_0, QMetaData::Private } + }; + metaObj = QMetaObject::new_metaobject( + "KviThreadManager", parentObject, + slot_tbl, 1, + 0, 0, +#ifndef QT_NO_PROPERTIES + 0, 0, + 0, 0, +#endif // QT_NO_PROPERTIES + 0, 0 ); + cleanUp_KviThreadManager.setMetaObject( metaObj ); + return metaObj; +} + +void* KviThreadManager::qt_cast( const char* clname ) +{ + if ( !qstrcmp( clname, "KviThreadManager" ) ) + return this; + return QObject::qt_cast( clname ); +} + +bool KviThreadManager::qt_invoke( int _id, QUObject* _o ) +{ + switch ( _id - staticMetaObject()->slotOffset() ) { + case 0: eventsPending((int)static_QUType_int.get(_o+1)); break; + default: + return QObject::qt_invoke( _id, _o ); + } + return TRUE; +} + +bool KviThreadManager::qt_emit( int _id, QUObject* _o ) +{ + return QObject::qt_emit(_id,_o); +} +#ifndef QT_NO_PROPERTIES + +bool KviThreadManager::qt_property( int id, int f, QVariant* v) +{ + return QObject::qt_property( id, f, v); +} + +bool KviThreadManager::qt_static_property( QObject* , int , int , QVariant* ){ return FALSE; } +#endif // QT_NO_PROPERTIES |