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 ENVVAR_NAMES_TMP[] =
545 "TEMP", "TMP", "TMPDIR", "HOME", "USERPROFILE", "HOMEPATH", NULL
547 static const char *const ENVVAR_NAMES_SYS[] =
549 "WINDIR", "SYSTEMROOT", NULL
551 static const char *const ENVVAR_NAMES_DEL[] =
553 "HTTP_PROXY", "FTP_PROXY", "NO_PROXY", "HOME", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
554 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LANG", NULL
557 //Initialize environment
558 QProcessEnvironment env = process.processEnvironment();
561 env = QProcessEnvironment::systemEnvironment();
564 //Clean enviroment variables that might affect our tools
565 for(const char *const *ptr = ENVVAR_NAMES_DEL; *ptr; ++ptr)
567 env.remove(QString::fromLatin1(*ptr));
570 //Set up system root directory
571 const QString sysRoot = QDir::toNativeSeparators(OS::known_folder(OS::FOLDER_SYSTROOT_DIR));
572 if (!sysRoot.isEmpty())
574 for (const char *const *ptr = ENVVAR_NAMES_SYS; *ptr; ++ptr)
576 env.insert(QString::fromLatin1(*ptr), sysRoot);
580 //Replace TEMP directory in environment
581 const QString tempDir = QDir::toNativeSeparators(temp_folder());
584 for (const char *const *ptr = ENVVAR_NAMES_TMP; *ptr; ++ptr)
586 env.insert(QString::fromLatin1(*ptr), tempDir);
590 //Setup PATH variable
591 prependToPath(env, tempDir);
592 if (extraPaths && (!extraPaths->isEmpty()))
594 QListIterator<QString> iter(*extraPaths);
596 while (iter.hasPrevious())
598 prependToPath(env, QDir::toNativeSeparators(iter.previous()));
603 if (extraEnv && (!extraEnv->isEmpty()))
605 for (QHash<QString, QString>::ConstIterator iter = extraEnv->constBegin(); iter != extraEnv->constEnd(); iter++)
607 env.insert(iter.key(), iter.value());
611 //Setup QPorcess object
612 process.setWorkingDirectory(wokringDir);
613 process.setProcessChannelMode(QProcess::MergedChannels);
614 process.setReadChannel(QProcess::StandardOutput);
615 process.setProcessEnvironment(env);
618 ///////////////////////////////////////////////////////////////////////////////
619 // NATURAL ORDER STRING COMPARISON
620 ///////////////////////////////////////////////////////////////////////////////
622 static bool natural_string_sort_helper(const QString &str1, const QString &str2)
624 return (MUtils::Internal::NaturalSort::strnatcmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
627 static bool natural_string_sort_helper_fold_case(const QString &str1, const QString &str2)
629 return (MUtils::Internal::NaturalSort::strnatcasecmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
632 void MUtils::natural_string_sort(QStringList &list, const bool bIgnoreCase)
634 qSort(list.begin(), list.end(), bIgnoreCase ? natural_string_sort_helper_fold_case : natural_string_sort_helper);
637 ///////////////////////////////////////////////////////////////////////////////
639 ///////////////////////////////////////////////////////////////////////////////
641 static QMutex g_clean_file_name_mutex;
642 static QScopedPointer<const QList<QPair<QRegExp,QString>>> g_clean_file_name_regex;
644 static void clean_file_name_make_pretty(QString &str)
646 static const struct { const char *p; const char *r; } PATTERN[] =
648 { "^\\s*\"([^\"]*)\"\\s*$", "\\1" }, //Remove straight double quotes around the whole string
649 { "\"([^\"]*)\"", "\xE2\x80\x9C\\1\xE2\x80\x9D" }, //Replace remaining pairs of straight double quotes with opening/closing double quote
650 { "^[\\\\/:]+([^\\\\/:]+.*)$", "\\1" }, //Remove leading slash, backslash and colon characters
651 { "^(.*[^\\\\/:]+)[\\\\/:]+$", "\\1" }, //Remove trailing slash, backslash and colon characters
652 { "(\\s*[\\\\/:]\\s*)+", " - " }, //Replace any slash, backslash or colon character that appears in the middle
656 QMutexLocker locker(&g_clean_file_name_mutex);
658 if (g_clean_file_name_regex.isNull())
660 QScopedPointer<QList<QPair<QRegExp, QString>>> list(new QList<QPair<QRegExp, QString>>());
661 for (size_t i = 0; PATTERN[i].p; ++i)
663 list->append(qMakePair(QRegExp(QString::fromUtf8(PATTERN[i].p), Qt::CaseInsensitive), PATTERN[i].r ? QString::fromUtf8(PATTERN[i].r) : QString()));
665 g_clean_file_name_regex.reset(list.take());
668 bool keepOnGoing = !str.isEmpty();
671 const QString prev = str;
673 for (QList<QPair<QRegExp, QString>>::ConstIterator iter = g_clean_file_name_regex->constBegin(); iter != g_clean_file_name_regex->constEnd(); ++iter)
675 str.replace(iter->first, iter->second);
676 if (str.compare(prev))
678 str = str.simplified();
679 keepOnGoing = !str.isEmpty();
686 QString MUtils::clean_file_name(const QString &name, const bool &pretty)
688 static const QLatin1Char REPLACEMENT_CHAR('_');
689 static const char FILENAME_ILLEGAL_CHARS[] = "<>:\"/\\|?*";
690 static const char *const FILENAME_RESERVED_NAMES[] =
692 "CON", "PRN", "AUX", "NUL",
693 "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
694 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", NULL
697 QString result(name);
700 clean_file_name_make_pretty(result);
703 for(QString::Iterator iter = result.begin(); iter != result.end(); iter++)
705 if (iter->category() == QChar::Other_Control)
707 *iter = REPLACEMENT_CHAR;
711 for(size_t i = 0; FILENAME_ILLEGAL_CHARS[i]; i++)
713 result.replace(QLatin1Char(FILENAME_ILLEGAL_CHARS[i]), REPLACEMENT_CHAR);
717 while (result.endsWith(QLatin1Char('.')))
723 for (size_t i = 0; FILENAME_RESERVED_NAMES[i]; i++)
725 const QString reserved = QString::fromLatin1(FILENAME_RESERVED_NAMES[i]);
726 if ((!result.compare(reserved, Qt::CaseInsensitive)) || result.startsWith(reserved + QLatin1Char('.'), Qt::CaseInsensitive))
728 result.replace(0, reserved.length(), QString().leftJustified(reserved.length(), REPLACEMENT_CHAR));
735 static QPair<QString,QString> clean_file_path_get_prefix(const QString path)
737 static const char *const PREFIXES[] =
739 "//?/", "//", "/", NULL
741 const QString posixPath = QDir::fromNativeSeparators(path.trimmed());
742 for (int i = 0; PREFIXES[i]; i++)
744 const QString prefix = QString::fromLatin1(PREFIXES[i]);
745 if (posixPath.startsWith(prefix))
747 return qMakePair(prefix, posixPath.mid(prefix.length()));
750 return qMakePair(QString(), posixPath);
753 QString MUtils::clean_file_path(const QString &path, const bool &pretty)
755 const QPair<QString, QString> prefix = clean_file_path_get_prefix(path);
757 QStringList parts = prefix.second.split(QLatin1Char('/'), QString::SkipEmptyParts);
758 for(int i = 0; i < parts.count(); i++)
760 if((i == 0) && (parts[i].length() == 2) && parts[i][0].isLetter() && (parts[i][1] == QLatin1Char(':')))
762 continue; //handle case "c:\"
764 parts[i] = MUtils::clean_file_name(parts[i], pretty);
767 const QString cleanPath = parts.join(QLatin1String("/"));
768 return prefix.first.isEmpty() ? cleanPath : prefix.first + cleanPath;
771 ///////////////////////////////////////////////////////////////////////////////
772 // REGULAR EXPESSION HELPER
773 ///////////////////////////////////////////////////////////////////////////////
775 bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 &value)
777 return regexp_parse_uint32(regexp, &value, 1U, 1U);
780 bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 &value)
782 return regexp_parse_int32(regexp, &value, 1U, 1U);
785 bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 &value, const size_t &offset)
787 return regexp_parse_uint32(regexp, &value, offset, 1U);
790 bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 &value, const size_t &offset)
792 return regexp_parse_int32(regexp, &value, offset, 1U);
795 bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 *values, const size_t &count)
797 return regexp_parse_uint32(regexp, values, 1U, count);
800 bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 *values, const size_t &count)
802 return regexp_parse_int32(regexp, values, 1U, count);
805 bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 *values, const size_t &offset, const size_t &count)
807 const QStringList caps = regexp.capturedTexts();
809 if (caps.isEmpty() || (quint32(caps.count()) <= count))
814 for (size_t i = 0; i < count; i++)
817 values[i] = caps[offset+i].toUInt(&ok);
827 bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 *values, const size_t &offset, const size_t &count)
829 const QStringList caps = regexp.capturedTexts();
831 if (caps.isEmpty() || (quint32(caps.count()) <= count))
836 for (size_t i = 0; i < count; i++)
839 values[i] = caps[offset+i].toInt(&ok);
849 ///////////////////////////////////////////////////////////////////////////////
850 // AVAILABLE CODEPAGES
851 ///////////////////////////////////////////////////////////////////////////////
853 QStringList MUtils::available_codepages(const bool &noAliases)
855 QStringList codecList;
856 QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
858 while(!availableCodecs.isEmpty())
860 const QByteArray current = availableCodecs.takeFirst();
861 if(!current.toLower().startsWith("system"))
863 codecList << QString::fromLatin1(current.constData(), current.size());
866 if(QTextCodec *const currentCodec = QTextCodec::codecForName(current.constData()))
868 const QList<QByteArray> aliases = currentCodec->aliases();
869 for(QList<QByteArray>::ConstIterator iter = aliases.constBegin(); iter != aliases.constEnd(); iter++)
871 availableCodecs.removeAll(*iter);
881 ///////////////////////////////////////////////////////////////////////////////
883 ///////////////////////////////////////////////////////////////////////////////
885 MUtils::fp_parts_t MUtils::break_fp(const double value)
887 fp_parts_t result = { };
890 result.parts[1] = modf(value, &result.parts[0]);
894 result.parts[0] = std::numeric_limits<double>::quiet_NaN();
895 result.parts[1] = std::numeric_limits<double>::quiet_NaN();
900 ///////////////////////////////////////////////////////////////////////////////
902 ///////////////////////////////////////////////////////////////////////////////
904 int MUtils::Internal::selfTest(const char *const buildKey, const bool debug)
906 static const bool MY_DEBUG_FLAG = MUTILS_DEBUG;
907 static const char *const MY_BUILD_KEY = __DATE__ "@" __TIME__;
909 if(strncmp(buildKey, MY_BUILD_KEY, 13) || (MY_DEBUG_FLAG != debug))
911 MUtils::OS::system_message_err(L"MUtils", L"FATAL ERROR: MUtils library version mismatch detected!");
912 MUtils::OS::system_message_wrn(L"MUtils", L"Perform a clean(!) re-install of the application to fix the problem!");