1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2018 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
27 #include <MUtils/Global.h>
28 #include <MUtils/OSSupport.h>
29 #include <MUtils/Version.h>
32 #include "DirLocker.h"
33 #include "3rd_party/strnatcmp/include/strnatcmp.h"
37 #include <QReadWriteLock>
42 #include <QListIterator>
44 #include <QThreadStorage>
56 ///////////////////////////////////////////////////////////////////////////////
58 ///////////////////////////////////////////////////////////////////////////////
61 #define rand_s(X) (true)
64 //Per-thread init flag
65 static QThreadStorage<bool> g_srand_flag;
67 //32-Bit wrapper for qrand()
68 #define QRAND() ((static_cast<quint32>(qrand()) & 0xFFFF) | (static_cast<quint32>(qrand()) << 16U))
70 //Robert Jenkins' 96 bit Mix Function
71 static quint32 mix_function(quint32 a, quint32 b, quint32 c)
73 a=a-b; a=a-c; a=a^(c >> 13);
74 b=b-c; b=b-a; b=b^(a << 8);
75 c=c-a; c=c-b; c=c^(b >> 13);
76 a=a-b; a=a-c; a=a^(c >> 12);
77 b=b-c; b=b-a; b=b^(a << 16);
78 c=c-a; c=c-b; c=c^(b >> 5);
79 a=a-b; a=a-c; a=a^(c >> 3);
80 b=b-c; b=b-a; b=b^(a << 10);
81 c=c-a; c=c-b; c=c^(b >> 15);
86 static void seed_rand(void)
88 QDateTime build(MUtils::Version::lib_build_date(), MUtils::Version::lib_build_time());
89 const quint32 seed_0 = mix_function(MUtils::OS::process_id(), MUtils::OS::thread_id(), build.toMSecsSinceEpoch());
90 qsrand(mix_function(clock(), time(NULL), seed_0));
93 static quint32 rand_fallback(void)
95 Q_ASSERT(RAND_MAX >= 0x7FFF);
97 if (!(g_srand_flag.hasLocalData() && g_srand_flag.localData()))
100 g_srand_flag.setLocalData(true);
103 quint32 rnd_val = mix_function(0x32288EA3, clock(), time(NULL));
105 for (size_t i = 0; i < 42; i++)
107 rnd_val = mix_function(rnd_val, QRAND(), QRAND());
108 rnd_val = mix_function(QRAND(), rnd_val, QRAND());
109 rnd_val = mix_function(QRAND(), QRAND(), rnd_val);
115 quint32 MUtils::next_rand_u32(void)
120 return rand_fallback();
125 quint32 MUtils::next_rand_u32(const quint32 max)
127 static const uint32_t DIV_LUT[64] =
129 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x55555556, 0x40000000, 0x33333334, 0x2AAAAAAB, 0x24924925,
130 0x20000000, 0x1C71C71D, 0x1999999A, 0x1745D175, 0x15555556, 0x13B13B14, 0x12492493, 0x11111112,
131 0x10000000, 0x0F0F0F10, 0x0E38E38F, 0x0D79435F, 0x0CCCCCCD, 0x0C30C30D, 0x0BA2E8BB, 0x0B21642D,
132 0x0AAAAAAB, 0x0A3D70A4, 0x09D89D8A, 0x097B425F, 0x0924924A, 0x08D3DCB1, 0x08888889, 0x08421085,
133 0x08000000, 0x07C1F07D, 0x07878788, 0x07507508, 0x071C71C8, 0x06EB3E46, 0x06BCA1B0, 0x06906907,
134 0x06666667, 0x063E7064, 0x06186187, 0x05F417D1, 0x05D1745E, 0x05B05B06, 0x0590B217, 0x0572620B,
135 0x05555556, 0x0539782A, 0x051EB852, 0x05050506, 0x04EC4EC5, 0x04D4873F, 0x04BDA130, 0x04A7904B,
136 0x04924925, 0x047DC120, 0x0469EE59, 0x0456C798, 0x04444445, 0x04325C54, 0x04210843, 0x04104105
138 return (max < 64) ? (next_rand_u32() / DIV_LUT[max]) : (next_rand_u32() / (UINT32_MAX / max + 1U));
142 quint64 MUtils::next_rand_u64(void)
144 return (quint64(next_rand_u32()) << 32) | quint64(next_rand_u32());
147 QString MUtils::next_rand_str(const bool &bLong)
151 return next_rand_str(false) + next_rand_str(false);
155 return QString::number(next_rand_u64(), 16).rightJustified(16, QLatin1Char('0'));
159 ///////////////////////////////////////////////////////////////////////////////
160 // STRING UTILITY FUNCTIONS
161 ///////////////////////////////////////////////////////////////////////////////
163 static QScopedPointer<QRegExp> g_str_trim_rx_r;
164 static QScopedPointer<QRegExp> g_str_trim_rx_l;
165 static QMutex g_str_trim_lock;
167 static QString& trim_helper(QString &str, QScopedPointer<QRegExp> ®ex, const char *const pattern)
169 QMutexLocker lock(&g_str_trim_lock);
172 regex.reset(new QRegExp(QLatin1String(pattern)));
174 str.remove(*regex.data());
178 QString& MUtils::trim_right(QString &str)
180 static const char *const TRIM_RIGHT = "\\s+$";
181 return trim_helper(str, g_str_trim_rx_r, TRIM_RIGHT);
184 QString& MUtils::trim_left(QString &str)
186 static const char *const TRIM_LEFT = "^\\s+";
187 return trim_helper(str, g_str_trim_rx_l, TRIM_LEFT);
190 QString MUtils::trim_right(const QString &str)
193 return trim_right(temp);
196 QString MUtils::trim_left(const QString &str)
199 return trim_left(temp);
202 ///////////////////////////////////////////////////////////////////////////////
203 // GENERATE FILE NAME
204 ///////////////////////////////////////////////////////////////////////////////
206 QString MUtils::make_temp_file(const QString &basePath, const QString &extension, const bool placeholder)
208 return make_temp_file(QDir(basePath), extension, placeholder);
211 QString MUtils::make_temp_file(const QDir &basePath, const QString &extension, const bool placeholder)
213 if (extension.isEmpty())
215 qWarning("Cannot generate temp file name with invalid parameters!");
219 for(int i = 0; i < 4096; i++)
221 const QString tempFileName = basePath.absoluteFilePath(QString("%1.%2").arg(next_rand_str(), extension));
222 if(!QFileInfo(tempFileName).exists())
226 QFile file(tempFileName);
227 if(file.open(QFile::ReadWrite))
240 qWarning("Failed to generate temp file name!");
244 QString MUtils::make_unique_file(const QString &basePath, const QString &baseName, const QString &extension, const bool fancy, const bool placeholder)
246 return make_unique_file(QDir(basePath), baseName, extension, fancy);
249 QString MUtils::make_unique_file(const QDir &basePath, const QString &baseName, const QString &extension, const bool fancy, const bool placeholder)
251 if (baseName.isEmpty() || extension.isEmpty())
253 qWarning("Cannot generate unique file name with invalid parameters!");
257 quint32 n = fancy ? 2 : 0;
258 QString fileName = fancy ? basePath.absoluteFilePath(QString("%1.%2").arg(baseName, extension)) : QString();
259 while (fileName.isEmpty() || QFileInfo(fileName).exists())
261 if (n <= quint32(USHRT_MAX))
265 fileName = basePath.absoluteFilePath(QString("%1 (%2).%3").arg(baseName, QString::number(n++), extension));
269 fileName = basePath.absoluteFilePath(QString("%1.%2.%3").arg(baseName, QString::number(n++, 16).rightJustified(4, QLatin1Char('0')), extension));
274 qWarning("Failed to generate unique file name!");
279 if (placeholder && (!fileName.isEmpty()))
281 QFile placeholder(fileName);
282 if (placeholder.open(QIODevice::WriteOnly))
291 ///////////////////////////////////////////////////////////////////////////////
293 ///////////////////////////////////////////////////////////////////////////////
296 * Compute parity in parallel
297 * http://www.graphics.stanford.edu/~seander/bithacks.html#ParityParallel
299 bool MUtils::parity(quint32 value)
301 value ^= value >> 16;
305 return ((0x6996 >> value) & 1) != 0;
308 ///////////////////////////////////////////////////////////////////////////////
310 ///////////////////////////////////////////////////////////////////////////////
312 static QScopedPointer<MUtils::Internal::DirLock> g_temp_folder_file;
313 static QReadWriteLock g_temp_folder_lock;
315 static QString try_create_subfolder(const QString &baseDir, const QString &postfix)
317 const QString baseDirPath = QDir(baseDir).absolutePath();
318 for(int i = 0; i < 32; i++)
320 QDir directory(baseDirPath);
321 if(directory.mkpath(postfix) && directory.cd(postfix))
323 return directory.canonicalPath();
329 static MUtils::Internal::DirLock *try_init_temp_folder(const QString &baseDir)
331 const QString tempPath = try_create_subfolder(baseDir, MUtils::next_rand_str());
332 if(!tempPath.isEmpty())
334 for(int i = 0; i < 32; i++)
336 MUtils::Internal::DirLock *lockFile = NULL;
339 lockFile = new MUtils::Internal::DirLock(tempPath);
342 catch(MUtils::Internal::DirLockException&)
344 /*ignore error and try again*/
351 static bool temp_folder_cleanup_helper(const QString &tempPath)
354 static const size_t MAX_DELAY = 8192;
357 QDir::setCurrent(QDir::rootPath());
358 if(MUtils::remove_directory(tempPath, true))
364 if(delay > MAX_DELAY)
368 MUtils::OS::sleep_ms(delay);
374 static void temp_folder_cleaup(void)
376 QWriteLocker writeLock(&g_temp_folder_lock);
378 //Clean the directory
379 while(!g_temp_folder_file.isNull())
381 const QString tempPath = g_temp_folder_file->getPath();
382 g_temp_folder_file.reset(NULL);
383 if(!temp_folder_cleanup_helper(tempPath))
385 MUtils::OS::system_message_wrn(L"Temp Cleaner", L"Warning: Not all temporary files could be removed!");
390 const QString &MUtils::temp_folder(void)
392 QReadLocker readLock(&g_temp_folder_lock);
394 //Already initialized?
395 if(!g_temp_folder_file.isNull())
397 return g_temp_folder_file->getPath();
400 //Obtain the write lock to initilaize
402 QWriteLocker writeLock(&g_temp_folder_lock);
404 //Still uninitilaized?
405 if(!g_temp_folder_file.isNull())
407 return g_temp_folder_file->getPath();
410 //Try the %TMP% or %TEMP% directory first
411 if(MUtils::Internal::DirLock *lockFile = try_init_temp_folder(QDir::tempPath()))
413 g_temp_folder_file.reset(lockFile);
414 atexit(temp_folder_cleaup);
415 return lockFile->getPath();
418 qWarning("%%TEMP%% directory not found -> trying fallback mode now!");
419 static const OS::known_folder_t FOLDER_ID[2] = { OS::FOLDER_LOCALAPPDATA, OS::FOLDER_SYSTROOT_DIR };
420 for(size_t id = 0; id < 2; id++)
422 const QString &knownFolder = OS::known_folder(FOLDER_ID[id]);
423 if(!knownFolder.isEmpty())
425 const QString tempRoot = try_create_subfolder(knownFolder, QLatin1String("TEMP"));
426 if(!tempRoot.isEmpty())
428 if(MUtils::Internal::DirLock *lockFile = try_init_temp_folder(tempRoot))
430 g_temp_folder_file.reset(lockFile);
431 atexit(temp_folder_cleaup);
432 return lockFile->getPath();
438 qFatal("Temporary directory could not be initialized !!!");
439 return (*((QString*)NULL));
442 ///////////////////////////////////////////////////////////////////////////////
443 // REMOVE DIRECTORY / FILE
444 ///////////////////////////////////////////////////////////////////////////////
446 static const QFile::Permissions FILE_PERMISSIONS_NONE = QFile::ReadOther | QFile::WriteOther;
448 bool MUtils::remove_file(const QString &fileName)
450 QFileInfo fileInfo(fileName);
452 for(size_t round = 0; round < 13; ++round)
456 MUtils::OS::sleep_ms(round);
459 if (fileInfo.exists())
461 QFile file(fileName);
464 file.setPermissions(FILE_PERMISSIONS_NONE);
469 if (!fileInfo.exists())
471 return true; /*success*/
475 qWarning("Could not delete \"%s\"", MUTILS_UTF8(fileName));
479 bool MUtils::remove_directory(const QString &folderPath, const bool &recursive)
481 const QDir folder(folderPath);
483 if(recursive && folder.exists())
485 const QFileInfoList entryList = folder.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
486 for(QFileInfoList::ConstIterator iter = entryList.constBegin(); iter != entryList.constEnd(); iter++)
490 remove_directory(iter->canonicalFilePath(), true);
494 remove_file(iter->canonicalFilePath());
499 for(size_t round = 0; round < 13; ++round)
503 MUtils::OS::sleep_ms(round);
508 QDir parent = folder;
513 QFile::setPermissions(folder.absolutePath(), FILE_PERMISSIONS_NONE);
515 parent.rmdir(folder.dirName());
519 if (!folder.exists())
521 return true; /*success*/
525 qWarning("Could not rmdir \"%s\"", MUTILS_UTF8(folderPath));
529 ///////////////////////////////////////////////////////////////////////////////
531 ///////////////////////////////////////////////////////////////////////////////
533 static void prependToPath(QProcessEnvironment &env, const QString &value)
535 static const QLatin1String PATH("PATH");
536 const QString path = env.value(PATH, QString()).trimmed();
537 env.insert(PATH, path.isEmpty() ? value : QString("%1;%2").arg(value, path));
540 void MUtils::init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir, const QStringList *const extraPaths, const QHash<QString, QString> *const extraEnv)
542 //Environment variable names
543 static const char *const s_envvar_names_temp[] =
545 "TEMP", "TMP", "TMPDIR", "HOME", "USERPROFILE", "HOMEPATH", NULL
547 static const char *const s_envvar_names_remove[] =
549 "WGETRC", "SYSTEM_WGETRC", "HTTP_PROXY", "FTP_PROXY", "NO_PROXY", "GNUPGHOME", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LANG", NULL
552 //Initialize environment
553 QProcessEnvironment env = process.processEnvironment();
554 if(env.isEmpty()) env = QProcessEnvironment::systemEnvironment();
556 //Clean a number of enviroment variables that might affect our tools
557 for(size_t i = 0; s_envvar_names_remove[i]; i++)
559 env.remove(QString::fromLatin1(s_envvar_names_remove[i]));
560 env.remove(QString::fromLatin1(s_envvar_names_remove[i]).toLower());
563 const QString tempDir = QDir::toNativeSeparators(temp_folder());
565 //Replace TEMP directory in environment
568 for(size_t i = 0; s_envvar_names_temp[i]; i++)
570 env.insert(s_envvar_names_temp[i], tempDir);
574 //Setup PATH variable
575 prependToPath(env, tempDir);
576 if (extraPaths && (!extraPaths->isEmpty()))
578 QListIterator<QString> iter(*extraPaths);
580 while (iter.hasPrevious())
582 prependToPath(env, QDir::toNativeSeparators(iter.previous()));
587 if (extraEnv && (!extraEnv->isEmpty()))
589 for (QHash<QString, QString>::ConstIterator iter = extraEnv->constBegin(); iter != extraEnv->constEnd(); iter++)
591 env.insert(iter.key(), iter.value());
595 //Setup QPorcess object
596 process.setWorkingDirectory(wokringDir);
597 process.setProcessChannelMode(QProcess::MergedChannels);
598 process.setReadChannel(QProcess::StandardOutput);
599 process.setProcessEnvironment(env);
602 ///////////////////////////////////////////////////////////////////////////////
603 // NATURAL ORDER STRING COMPARISON
604 ///////////////////////////////////////////////////////////////////////////////
606 static bool natural_string_sort_helper(const QString &str1, const QString &str2)
608 return (MUtils::Internal::NaturalSort::strnatcmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
611 static bool natural_string_sort_helper_fold_case(const QString &str1, const QString &str2)
613 return (MUtils::Internal::NaturalSort::strnatcasecmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
616 void MUtils::natural_string_sort(QStringList &list, const bool bIgnoreCase)
618 qSort(list.begin(), list.end(), bIgnoreCase ? natural_string_sort_helper_fold_case : natural_string_sort_helper);
621 ///////////////////////////////////////////////////////////////////////////////
623 ///////////////////////////////////////////////////////////////////////////////
625 static QMutex g_clean_file_name_mutex;
626 static QScopedPointer<const QList<QPair<QRegExp,QString>>> g_clean_file_name_regex;
628 static void clean_file_name_make_pretty(QString &str)
630 static const struct { const char *p; const char *r; } PATTERN[] =
632 { "^\\s*\"([^\"]*)\"\\s*$", "\\1" }, //Remove straight double quotes around the whole string
633 { "\"([^\"]*)\"", "\xE2\x80\x9C\\1\xE2\x80\x9D" }, //Replace remaining pairs of straight double quotes with opening/closing double quote
634 { "^[\\\\/:]+([^\\\\/:]+.*)$", "\\1" }, //Remove leading slash, backslash and colon characters
635 { "^(.*[^\\\\/:]+)[\\\\/:]+$", "\\1" }, //Remove trailing slash, backslash and colon characters
636 { "(\\s*[\\\\/:]\\s*)+", " - " }, //Replace any slash, backslash or colon character that appears in the middle
640 QMutexLocker locker(&g_clean_file_name_mutex);
642 if (g_clean_file_name_regex.isNull())
644 QScopedPointer<QList<QPair<QRegExp, QString>>> list(new QList<QPair<QRegExp, QString>>());
645 for (size_t i = 0; PATTERN[i].p; ++i)
647 list->append(qMakePair(QRegExp(QString::fromUtf8(PATTERN[i].p), Qt::CaseInsensitive), PATTERN[i].r ? QString::fromUtf8(PATTERN[i].r) : QString()));
649 g_clean_file_name_regex.reset(list.take());
652 bool keepOnGoing = !str.isEmpty();
655 const QString prev = str;
657 for (QList<QPair<QRegExp, QString>>::ConstIterator iter = g_clean_file_name_regex->constBegin(); iter != g_clean_file_name_regex->constEnd(); ++iter)
659 str.replace(iter->first, iter->second);
660 if (str.compare(prev))
662 str = str.simplified();
663 keepOnGoing = !str.isEmpty();
670 QString MUtils::clean_file_name(const QString &name, const bool &pretty)
672 static const QLatin1Char REPLACEMENT_CHAR('_');
673 static const char FILENAME_ILLEGAL_CHARS[] = "<>:\"/\\|?*";
674 static const char *const FILENAME_RESERVED_NAMES[] =
676 "CON", "PRN", "AUX", "NUL",
677 "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
678 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", NULL
681 QString result(name);
684 clean_file_name_make_pretty(result);
687 for(QString::Iterator iter = result.begin(); iter != result.end(); iter++)
689 if (iter->category() == QChar::Other_Control)
691 *iter = REPLACEMENT_CHAR;
695 for(size_t i = 0; FILENAME_ILLEGAL_CHARS[i]; i++)
697 result.replace(QLatin1Char(FILENAME_ILLEGAL_CHARS[i]), REPLACEMENT_CHAR);
701 while (result.endsWith(QLatin1Char('.')))
707 for (size_t i = 0; FILENAME_RESERVED_NAMES[i]; i++)
709 const QString reserved = QString::fromLatin1(FILENAME_RESERVED_NAMES[i]);
710 if ((!result.compare(reserved, Qt::CaseInsensitive)) || result.startsWith(reserved + QLatin1Char('.'), Qt::CaseInsensitive))
712 result.replace(0, reserved.length(), QString().leftJustified(reserved.length(), REPLACEMENT_CHAR));
719 static QPair<QString,QString> clean_file_path_get_prefix(const QString path)
721 static const char *const PREFIXES[] =
723 "//?/", "//", "/", NULL
725 const QString posixPath = QDir::fromNativeSeparators(path.trimmed());
726 for (int i = 0; PREFIXES[i]; i++)
728 const QString prefix = QString::fromLatin1(PREFIXES[i]);
729 if (posixPath.startsWith(prefix))
731 return qMakePair(prefix, posixPath.mid(prefix.length()));
734 return qMakePair(QString(), posixPath);
737 QString MUtils::clean_file_path(const QString &path, const bool &pretty)
739 const QPair<QString, QString> prefix = clean_file_path_get_prefix(path);
741 QStringList parts = prefix.second.split(QLatin1Char('/'), QString::SkipEmptyParts);
742 for(int i = 0; i < parts.count(); i++)
744 if((i == 0) && (parts[i].length() == 2) && parts[i][0].isLetter() && (parts[i][1] == QLatin1Char(':')))
746 continue; //handle case "c:\"
748 parts[i] = MUtils::clean_file_name(parts[i], pretty);
751 const QString cleanPath = parts.join(QLatin1String("/"));
752 return prefix.first.isEmpty() ? cleanPath : prefix.first + cleanPath;
755 ///////////////////////////////////////////////////////////////////////////////
756 // REGULAR EXPESSION HELPER
757 ///////////////////////////////////////////////////////////////////////////////
759 bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 &value)
761 return regexp_parse_uint32(regexp, &value, 1U, 1U);
764 bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 &value)
766 return regexp_parse_int32(regexp, &value, 1U, 1U);
769 bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 &value, const size_t &offset)
771 return regexp_parse_uint32(regexp, &value, offset, 1U);
774 bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 &value, const size_t &offset)
776 return regexp_parse_int32(regexp, &value, offset, 1U);
779 bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 *values, const size_t &count)
781 return regexp_parse_uint32(regexp, values, 1U, count);
784 bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 *values, const size_t &count)
786 return regexp_parse_int32(regexp, values, 1U, count);
789 bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 *values, const size_t &offset, const size_t &count)
791 const QStringList caps = regexp.capturedTexts();
793 if (caps.isEmpty() || (quint32(caps.count()) <= count))
798 for (size_t i = 0; i < count; i++)
801 values[i] = caps[offset+i].toUInt(&ok);
811 bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 *values, const size_t &offset, const size_t &count)
813 const QStringList caps = regexp.capturedTexts();
815 if (caps.isEmpty() || (quint32(caps.count()) <= count))
820 for (size_t i = 0; i < count; i++)
823 values[i] = caps[offset+i].toInt(&ok);
833 ///////////////////////////////////////////////////////////////////////////////
834 // AVAILABLE CODEPAGES
835 ///////////////////////////////////////////////////////////////////////////////
837 QStringList MUtils::available_codepages(const bool &noAliases)
839 QStringList codecList;
840 QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
842 while(!availableCodecs.isEmpty())
844 const QByteArray current = availableCodecs.takeFirst();
845 if(!current.toLower().startsWith("system"))
847 codecList << QString::fromLatin1(current.constData(), current.size());
850 if(QTextCodec *const currentCodec = QTextCodec::codecForName(current.constData()))
852 const QList<QByteArray> aliases = currentCodec->aliases();
853 for(QList<QByteArray>::ConstIterator iter = aliases.constBegin(); iter != aliases.constEnd(); iter++)
855 availableCodecs.removeAll(*iter);
865 ///////////////////////////////////////////////////////////////////////////////
867 ///////////////////////////////////////////////////////////////////////////////
869 MUtils::fp_parts_t MUtils::break_fp(const double value)
871 fp_parts_t result = { };
874 result.parts[1] = modf(value, &result.parts[0]);
878 result.parts[0] = std::numeric_limits<double>::quiet_NaN();
879 result.parts[1] = std::numeric_limits<double>::quiet_NaN();
884 ///////////////////////////////////////////////////////////////////////////////
886 ///////////////////////////////////////////////////////////////////////////////
888 int MUtils::Internal::selfTest(const char *const buildKey, const bool debug)
890 static const bool MY_DEBUG_FLAG = MUTILS_DEBUG;
891 static const char *const MY_BUILD_KEY = __DATE__ "@" __TIME__;
893 if(strncmp(buildKey, MY_BUILD_KEY, 13) || (MY_DEBUG_FLAG != debug))
895 MUtils::OS::system_message_err(L"MUtils", L"FATAL ERROR: MUtils library version mismatch detected!");
896 MUtils::OS::system_message_wrn(L"MUtils", L"Perform a clean(!) re-install of the application to fix the problem!");