Added support for Visual Studio 2019.6 (v16.6).
[MUtilities.git] / src / Global.cpp
blobd743248cd5862c0674401635b7f3988c563a48ef
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2019 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
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.
9 //
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 //////////////////////////////////////////////////////////////////////////////////
22 #if _MSC_VER
23 #define _CRT_RAND_S 1
24 #endif
26 //MUtils
27 #include <MUtils/Global.h>
28 #include <MUtils/OSSupport.h>
29 #include <MUtils/Version.h>
30 #include "Internal.h"
32 //Internal
33 #include "DirLocker.h"
34 #include "3rd_party/strnatcmp/include/strnatcmp.h"
36 //Qt
37 #include <QDir>
38 #include <QReadWriteLock>
39 #include <QProcess>
40 #include <QTextCodec>
41 #include <QPair>
42 #include <QHash>
43 #include <QListIterator>
44 #include <QMutex>
45 #include <QThreadStorage>
47 //CRT
48 #include <cstdlib>
49 #include <ctime>
50 #include <process.h>
52 //VLD
53 #ifdef _MSC_VER
54 #include <vld.h>
55 #endif
57 //Global
58 const QString MUtils::Internal::g_empty;
60 ///////////////////////////////////////////////////////////////////////////////
61 // Random Support
62 ///////////////////////////////////////////////////////////////////////////////
64 #ifndef _CRT_RAND_S
65 #define rand_s(X) (true)
66 #endif
68 //Per-thread init flag
69 static QThreadStorage<bool> g_srand_flag;
71 //32-Bit wrapper for qrand()
72 #define QRAND() ((static_cast<quint32>(qrand()) & 0xFFFF) | (static_cast<quint32>(qrand()) << 16U))
74 //Robert Jenkins' 96 bit Mix Function
75 static quint32 mix_function(quint32 a, quint32 b, quint32 c)
77 a=a-b; a=a-c; a=a^(c >> 13);
78 b=b-c; b=b-a; b=b^(a << 8);
79 c=c-a; c=c-b; c=c^(b >> 13);
80 a=a-b; a=a-c; a=a^(c >> 12);
81 b=b-c; b=b-a; b=b^(a << 16);
82 c=c-a; c=c-b; c=c^(b >> 5);
83 a=a-b; a=a-c; a=a^(c >> 3);
84 b=b-c; b=b-a; b=b^(a << 10);
85 c=c-a; c=c-b; c=c^(b >> 15);
87 return a ^ b ^ c;
90 static void seed_rand(void)
92 QDateTime build(MUtils::Version::lib_build_date(), MUtils::Version::lib_build_time());
93 const quint32 seed_0 = mix_function(MUtils::OS::process_id(), MUtils::OS::thread_id(), build.toMSecsSinceEpoch());
94 qsrand(mix_function(clock(), time(NULL), seed_0));
97 static quint32 rand_fallback(void)
99 Q_ASSERT(RAND_MAX >= 0x7FFF);
101 if (!(g_srand_flag.hasLocalData() && g_srand_flag.localData()))
103 seed_rand();
104 g_srand_flag.setLocalData(true);
107 quint32 rnd_val = mix_function(0x32288EA3, clock(), time(NULL));
109 for (size_t i = 0; i < 42; i++)
111 rnd_val = mix_function(rnd_val, QRAND(), QRAND());
112 rnd_val = mix_function(QRAND(), rnd_val, QRAND());
113 rnd_val = mix_function(QRAND(), QRAND(), rnd_val);
116 return rnd_val;
119 quint32 MUtils::next_rand_u32(void)
121 quint32 rnd;
122 if (rand_s(&rnd))
124 return rand_fallback();
126 return rnd;
129 quint32 MUtils::next_rand_u32(const quint32 max)
131 static const uint32_t DIV_LUT[64] =
133 0xFFFFFFFF, 0xFFFFFFFF, 0x80000000, 0x55555556, 0x40000000, 0x33333334, 0x2AAAAAAB, 0x24924925,
134 0x20000000, 0x1C71C71D, 0x1999999A, 0x1745D175, 0x15555556, 0x13B13B14, 0x12492493, 0x11111112,
135 0x10000000, 0x0F0F0F10, 0x0E38E38F, 0x0D79435F, 0x0CCCCCCD, 0x0C30C30D, 0x0BA2E8BB, 0x0B21642D,
136 0x0AAAAAAB, 0x0A3D70A4, 0x09D89D8A, 0x097B425F, 0x0924924A, 0x08D3DCB1, 0x08888889, 0x08421085,
137 0x08000000, 0x07C1F07D, 0x07878788, 0x07507508, 0x071C71C8, 0x06EB3E46, 0x06BCA1B0, 0x06906907,
138 0x06666667, 0x063E7064, 0x06186187, 0x05F417D1, 0x05D1745E, 0x05B05B06, 0x0590B217, 0x0572620B,
139 0x05555556, 0x0539782A, 0x051EB852, 0x05050506, 0x04EC4EC5, 0x04D4873F, 0x04BDA130, 0x04A7904B,
140 0x04924925, 0x047DC120, 0x0469EE59, 0x0456C798, 0x04444445, 0x04325C54, 0x04210843, 0x04104105
142 return (max < 64) ? (next_rand_u32() / DIV_LUT[max]) : (next_rand_u32() / (UINT32_MAX / max + 1U));
146 quint64 MUtils::next_rand_u64(void)
148 return (quint64(next_rand_u32()) << 32) | quint64(next_rand_u32());
151 QString MUtils::next_rand_str(const bool &bLong)
153 if (bLong)
155 return next_rand_str(false) + next_rand_str(false);
157 else
159 return QString::number(next_rand_u64(), 16).rightJustified(16, QLatin1Char('0'));
163 ///////////////////////////////////////////////////////////////////////////////
164 // STRING UTILITY FUNCTIONS
165 ///////////////////////////////////////////////////////////////////////////////
167 static QScopedPointer<QRegExp> g_str_trim_rx_r;
168 static QScopedPointer<QRegExp> g_str_trim_rx_l;
169 static QMutex g_str_trim_lock;
171 static QString& trim_helper(QString &str, QScopedPointer<QRegExp> &regex, const char *const pattern)
173 QMutexLocker lock(&g_str_trim_lock);
174 if (regex.isNull())
176 regex.reset(new QRegExp(QLatin1String(pattern)));
178 str.remove(*regex.data());
179 return str;
182 QString& MUtils::trim_right(QString &str)
184 static const char *const TRIM_RIGHT = "\\s+$";
185 return trim_helper(str, g_str_trim_rx_r, TRIM_RIGHT);
188 QString& MUtils::trim_left(QString &str)
190 static const char *const TRIM_LEFT = "^\\s+";
191 return trim_helper(str, g_str_trim_rx_l, TRIM_LEFT);
194 QString MUtils::trim_right(const QString &str)
196 QString temp(str);
197 return trim_right(temp);
200 QString MUtils::trim_left(const QString &str)
202 QString temp(str);
203 return trim_left(temp);
206 ///////////////////////////////////////////////////////////////////////////////
207 // GENERATE FILE NAME
208 ///////////////////////////////////////////////////////////////////////////////
210 QString MUtils::make_temp_file(const QString &basePath, const QString &extension, const bool placeholder)
212 return make_temp_file(QDir(basePath), extension, placeholder);
215 QString MUtils::make_temp_file(const QDir &basePath, const QString &extension, const bool placeholder)
217 if (extension.isEmpty())
219 qWarning("Cannot generate temp file name with invalid parameters!");
220 return QString();
223 for(int i = 0; i < 4096; i++)
225 const QString tempFileName = basePath.absoluteFilePath(QString("%1.%2").arg(next_rand_str(), extension));
226 if(!QFileInfo(tempFileName).exists())
228 if(placeholder)
230 QFile file(tempFileName);
231 if(file.open(QFile::ReadWrite))
233 file.close();
234 return tempFileName;
237 else
239 return tempFileName;
244 qWarning("Failed to generate temp file name!");
245 return QString();
248 QString MUtils::make_unique_file(const QString &basePath, const QString &baseName, const QString &extension, const bool fancy, const bool placeholder)
250 return make_unique_file(QDir(basePath), baseName, extension, fancy);
253 QString MUtils::make_unique_file(const QDir &basePath, const QString &baseName, const QString &extension, const bool fancy, const bool placeholder)
255 if (baseName.isEmpty() || extension.isEmpty())
257 qWarning("Cannot generate unique file name with invalid parameters!");
258 return QString();
261 quint32 n = fancy ? 2 : 0;
262 QString fileName = fancy ? basePath.absoluteFilePath(QString("%1.%2").arg(baseName, extension)) : QString();
263 while (fileName.isEmpty() || QFileInfo(fileName).exists())
265 if (n <= quint32(USHRT_MAX))
267 if (fancy)
269 fileName = basePath.absoluteFilePath(QString("%1 (%2).%3").arg(baseName, QString::number(n++), extension));
271 else
273 fileName = basePath.absoluteFilePath(QString("%1.%2.%3").arg(baseName, QString::number(n++, 16).rightJustified(4, QLatin1Char('0')), extension));
276 else
278 qWarning("Failed to generate unique file name!");
279 return QString();
283 if (placeholder && (!fileName.isEmpty()))
285 QFile placeholder(fileName);
286 if (placeholder.open(QIODevice::WriteOnly))
288 placeholder.close();
292 return fileName;
295 ///////////////////////////////////////////////////////////////////////////////
296 // COMPUTE PARITY
297 ///////////////////////////////////////////////////////////////////////////////
300 * Compute parity in parallel
301 * http://www.graphics.stanford.edu/~seander/bithacks.html#ParityParallel
303 bool MUtils::parity(quint32 value)
305 value ^= value >> 16;
306 value ^= value >> 8;
307 value ^= value >> 4;
308 value &= 0xf;
309 return ((0x6996 >> value) & 1) != 0;
312 ///////////////////////////////////////////////////////////////////////////////
313 // TEMP FOLDER
314 ///////////////////////////////////////////////////////////////////////////////
316 static QScopedPointer<MUtils::Internal::DirLock> g_temp_folder_file;
317 static QReadWriteLock g_temp_folder_lock;
319 static QString try_create_subfolder(const QString &baseDir, const QString &postfix)
321 const QString baseDirPath = QDir(baseDir).absolutePath();
322 for(int i = 0; i < 32; i++)
324 QDir directory(baseDirPath);
325 if(directory.mkpath(postfix) && directory.cd(postfix))
327 return directory.canonicalPath();
330 return QString();
333 static MUtils::Internal::DirLock *try_init_temp_folder(const QString &baseDir)
335 const QString tempPath = try_create_subfolder(baseDir, MUtils::next_rand_str());
336 if(!tempPath.isEmpty())
338 for(int i = 0; i < 32; i++)
340 MUtils::Internal::DirLock *lockFile = NULL;
343 lockFile = new MUtils::Internal::DirLock(tempPath);
344 return lockFile;
346 catch(MUtils::Internal::DirLockException&)
348 /*ignore error and try again*/
352 return NULL;
355 static bool temp_folder_cleanup_helper(const QString &tempPath)
357 size_t delay = 1;
358 static const size_t MAX_DELAY = 8192;
359 forever
361 QDir::setCurrent(QDir::rootPath());
362 if(MUtils::remove_directory(tempPath, true))
364 return true;
366 else
368 if(delay > MAX_DELAY)
370 return false;
372 MUtils::OS::sleep_ms(delay);
373 delay *= 2;
378 static void temp_folder_cleaup(void)
380 QWriteLocker writeLock(&g_temp_folder_lock);
382 //Clean the directory
383 while(!g_temp_folder_file.isNull())
385 const QString tempPath = g_temp_folder_file->getPath();
386 g_temp_folder_file.reset(NULL);
387 if(!temp_folder_cleanup_helper(tempPath))
389 MUtils::OS::system_message_wrn(L"Temp Cleaner", L"Warning: Not all temporary files could be removed!");
394 const QString &MUtils::temp_folder(void)
396 QReadLocker readLock(&g_temp_folder_lock);
398 //Already initialized?
399 if(!g_temp_folder_file.isNull())
401 return g_temp_folder_file->getPath();
404 //Obtain the write lock to initilaize
405 readLock.unlock();
406 QWriteLocker writeLock(&g_temp_folder_lock);
408 //Still uninitilaized?
409 if(!g_temp_folder_file.isNull())
411 return g_temp_folder_file->getPath();
414 //Try the %TMP% or %TEMP% directory first
415 if(MUtils::Internal::DirLock *lockFile = try_init_temp_folder(QDir::tempPath()))
417 g_temp_folder_file.reset(lockFile);
418 atexit(temp_folder_cleaup);
419 return lockFile->getPath();
422 qWarning("%%TEMP%% directory not found -> trying fallback mode now!");
423 static const OS::known_folder_t FOLDER_ID[2] = { OS::FOLDER_APPDATA_LOCA, OS::FOLDER_SYSROOT };
424 for(size_t id = 0; id < 2; id++)
426 const QString &knownFolder = OS::known_folder(FOLDER_ID[id]);
427 if(!knownFolder.isEmpty())
429 const QString tempRoot = try_create_subfolder(knownFolder, QLatin1String("TEMP"));
430 if(!tempRoot.isEmpty())
432 if(MUtils::Internal::DirLock *lockFile = try_init_temp_folder(tempRoot))
434 g_temp_folder_file.reset(lockFile);
435 atexit(temp_folder_cleaup);
436 return lockFile->getPath();
442 qFatal("Temporary directory could not be initialized !!!");
443 return (*((QString*)NULL));
446 ///////////////////////////////////////////////////////////////////////////////
447 // REMOVE DIRECTORY / FILE
448 ///////////////////////////////////////////////////////////////////////////////
450 static const QFile::Permissions FILE_PERMISSIONS_NONE = QFile::ReadOther | QFile::WriteOther;
452 bool MUtils::remove_file(const QString &fileName)
454 QFileInfo fileInfo(fileName);
456 for(size_t round = 0; round < 13; ++round)
458 if (round > 0)
460 MUtils::OS::sleep_ms(round);
461 fileInfo.refresh();
463 if (fileInfo.exists())
465 QFile file(fileName);
466 if (round > 0)
468 file.setPermissions(FILE_PERMISSIONS_NONE);
470 file.remove();
471 fileInfo.refresh();
473 if (!fileInfo.exists())
475 return true; /*success*/
479 qWarning("Could not delete \"%s\"", MUTILS_UTF8(fileName));
480 return false;
483 bool MUtils::remove_directory(const QString &folderPath, const bool &recursive)
485 const QDir folder(folderPath);
487 if(recursive && folder.exists())
489 const QFileInfoList entryList = folder.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
490 for(QFileInfoList::ConstIterator iter = entryList.constBegin(); iter != entryList.constEnd(); iter++)
492 if(iter->isDir())
494 remove_directory(iter->canonicalFilePath(), true);
496 else
498 remove_file(iter->canonicalFilePath());
503 for(size_t round = 0; round < 13; ++round)
505 if(round > 0)
507 MUtils::OS::sleep_ms(round);
508 folder.refresh();
510 if (folder.exists())
512 QDir parent = folder;
513 if (parent.cdUp())
515 if (round > 0)
517 QFile::setPermissions(folder.absolutePath(), FILE_PERMISSIONS_NONE);
519 parent.rmdir(folder.dirName());
520 folder.refresh();
523 if (!folder.exists())
525 return true; /*success*/
529 qWarning("Could not rmdir \"%s\"", MUTILS_UTF8(folderPath));
530 return false;
533 ///////////////////////////////////////////////////////////////////////////////
534 // PROCESS UTILS
535 ///////////////////////////////////////////////////////////////////////////////
537 static void prependToPath(QProcessEnvironment &env, const QString &value)
539 static const QLatin1String PATH("PATH");
540 const QString path = env.value(PATH, QString()).trimmed();
541 env.insert(PATH, path.isEmpty() ? value : QString("%1;%2").arg(value, path));
544 void MUtils::init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir, const QStringList *const extraPaths, const QHash<QString, QString> *const extraEnv)
546 //Environment variable names
547 static const char *const ENVVAR_NAMES_TMP[] =
549 "TEMP", "TMP", "TMPDIR", "HOME", "USERPROFILE", "HOMEPATH", NULL
551 static const char *const ENVVAR_NAMES_SYS[] =
553 "WINDIR", "SYSTEMROOT", NULL
555 static const char *const ENVVAR_NAMES_DEL[] =
557 "HTTP_PROXY", "FTP_PROXY", "NO_PROXY", "HOME", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
558 "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC", "LC_TIME", "LANG", NULL
561 //Initialize environment
562 QProcessEnvironment env = process.processEnvironment();
563 if (env.isEmpty())
565 env = QProcessEnvironment::systemEnvironment();
568 //Clean enviroment variables that might affect our tools
569 for(const char *const *ptr = ENVVAR_NAMES_DEL; *ptr; ++ptr)
571 env.remove(QString::fromLatin1(*ptr));
574 //Set up system root directory
575 const QString sysRoot = QDir::toNativeSeparators(OS::known_folder(OS::FOLDER_SYSROOT));
576 if (!sysRoot.isEmpty())
578 for (const char *const *ptr = ENVVAR_NAMES_SYS; *ptr; ++ptr)
580 env.insert(QString::fromLatin1(*ptr), sysRoot);
584 //Replace TEMP directory in environment
585 const QString tempDir = QDir::toNativeSeparators(temp_folder());
586 if(bReplaceTempDir)
588 for (const char *const *ptr = ENVVAR_NAMES_TMP; *ptr; ++ptr)
590 env.insert(QString::fromLatin1(*ptr), tempDir);
594 //Setup PATH variable
595 prependToPath(env, tempDir);
596 if (extraPaths && (!extraPaths->isEmpty()))
598 QListIterator<QString> iter(*extraPaths);
599 iter.toBack();
600 while (iter.hasPrevious())
602 prependToPath(env, QDir::toNativeSeparators(iter.previous()));
606 //Setup environment
607 if (extraEnv && (!extraEnv->isEmpty()))
609 for (QHash<QString, QString>::ConstIterator iter = extraEnv->constBegin(); iter != extraEnv->constEnd(); iter++)
611 env.insert(iter.key(), iter.value());
615 //Setup QPorcess object
616 process.setWorkingDirectory(wokringDir);
617 process.setProcessChannelMode(QProcess::MergedChannels);
618 process.setReadChannel(QProcess::StandardOutput);
619 process.setProcessEnvironment(env);
622 ///////////////////////////////////////////////////////////////////////////////
623 // NATURAL ORDER STRING COMPARISON
624 ///////////////////////////////////////////////////////////////////////////////
626 static bool natural_string_sort_helper(const QString &str1, const QString &str2)
628 return (MUtils::Internal::NaturalSort::strnatcmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
631 static bool natural_string_sort_helper_fold_case(const QString &str1, const QString &str2)
633 return (MUtils::Internal::NaturalSort::strnatcasecmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
636 void MUtils::natural_string_sort(QStringList &list, const bool bIgnoreCase)
638 qSort(list.begin(), list.end(), bIgnoreCase ? natural_string_sort_helper_fold_case : natural_string_sort_helper);
641 ///////////////////////////////////////////////////////////////////////////////
642 // CLEAN FILE PATH
643 ///////////////////////////////////////////////////////////////////////////////
645 static QMutex g_clean_file_name_mutex;
646 static QScopedPointer<const QList<QPair<QRegExp,QString>>> g_clean_file_name_regex;
648 static void clean_file_name_make_pretty(QString &str)
650 static const struct { const char *p; const char *r; } PATTERN[] =
652 { "^\\s*\"([^\"]*)\"\\s*$", "\\1" }, //Remove straight double quotes around the whole string
653 { "\"([^\"]*)\"", "\xE2\x80\x9C\\1\xE2\x80\x9D" }, //Replace remaining pairs of straight double quotes with opening/closing double quote
654 { "^[\\\\/:]+([^\\\\/:]+.*)$", "\\1" }, //Remove leading slash, backslash and colon characters
655 { "^(.*[^\\\\/:]+)[\\\\/:]+$", "\\1" }, //Remove trailing slash, backslash and colon characters
656 { "(\\s*[\\\\/:]\\s*)+", " - " }, //Replace any slash, backslash or colon character that appears in the middle
657 { NULL, NULL }
660 QMutexLocker locker(&g_clean_file_name_mutex);
662 if (g_clean_file_name_regex.isNull())
664 QScopedPointer<QList<QPair<QRegExp, QString>>> list(new QList<QPair<QRegExp, QString>>());
665 for (size_t i = 0; PATTERN[i].p; ++i)
667 list->append(qMakePair(QRegExp(QString::fromUtf8(PATTERN[i].p), Qt::CaseInsensitive), PATTERN[i].r ? QString::fromUtf8(PATTERN[i].r) : QString()));
669 g_clean_file_name_regex.reset(list.take());
672 bool keepOnGoing = !str.isEmpty();
673 while(keepOnGoing)
675 const QString prev = str;
676 keepOnGoing = false;
677 for (QList<QPair<QRegExp, QString>>::ConstIterator iter = g_clean_file_name_regex->constBegin(); iter != g_clean_file_name_regex->constEnd(); ++iter)
679 str.replace(iter->first, iter->second);
680 if (str.compare(prev))
682 str = str.simplified();
683 keepOnGoing = !str.isEmpty();
684 break;
690 QString MUtils::clean_file_name(const QString &name, const bool &pretty)
692 static const QLatin1Char REPLACEMENT_CHAR('_');
693 static const char FILENAME_ILLEGAL_CHARS[] = "<>:\"/\\|?*";
694 static const char *const FILENAME_RESERVED_NAMES[] =
696 "CON", "PRN", "AUX", "NUL",
697 "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
698 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", NULL
701 QString result(name);
702 if (pretty)
704 clean_file_name_make_pretty(result);
707 for(QString::Iterator iter = result.begin(); iter != result.end(); iter++)
709 if (iter->category() == QChar::Other_Control)
711 *iter = REPLACEMENT_CHAR;
715 for(size_t i = 0; FILENAME_ILLEGAL_CHARS[i]; i++)
717 result.replace(QLatin1Char(FILENAME_ILLEGAL_CHARS[i]), REPLACEMENT_CHAR);
720 trim_right(result);
721 while (result.endsWith(QLatin1Char('.')))
723 result.chop(1);
724 trim_right(result);
727 for (size_t i = 0; FILENAME_RESERVED_NAMES[i]; i++)
729 const QString reserved = QString::fromLatin1(FILENAME_RESERVED_NAMES[i]);
730 if ((!result.compare(reserved, Qt::CaseInsensitive)) || result.startsWith(reserved + QLatin1Char('.'), Qt::CaseInsensitive))
732 result.replace(0, reserved.length(), QString().leftJustified(reserved.length(), REPLACEMENT_CHAR));
736 return result;
739 static QPair<QString,QString> clean_file_path_get_prefix(const QString path)
741 static const char *const PREFIXES[] =
743 "//?/", "//", "/", NULL
745 const QString posixPath = QDir::fromNativeSeparators(path.trimmed());
746 for (int i = 0; PREFIXES[i]; i++)
748 const QString prefix = QString::fromLatin1(PREFIXES[i]);
749 if (posixPath.startsWith(prefix))
751 return qMakePair(prefix, posixPath.mid(prefix.length()));
754 return qMakePair(QString(), posixPath);
757 QString MUtils::clean_file_path(const QString &path, const bool &pretty)
759 const QPair<QString, QString> prefix = clean_file_path_get_prefix(path);
761 QStringList parts = prefix.second.split(QLatin1Char('/'), QString::SkipEmptyParts);
762 for(int i = 0; i < parts.count(); i++)
764 if((i == 0) && (parts[i].length() == 2) && parts[i][0].isLetter() && (parts[i][1] == QLatin1Char(':')))
766 continue; //handle case "c:\"
768 parts[i] = MUtils::clean_file_name(parts[i], pretty);
771 const QString cleanPath = parts.join(QLatin1String("/"));
772 return prefix.first.isEmpty() ? cleanPath : prefix.first + cleanPath;
775 ///////////////////////////////////////////////////////////////////////////////
776 // PARENT PATH
777 ///////////////////////////////////////////////////////////////////////////////
779 static void remove_trailing_separators(QString &pathStr)
781 while (pathStr.endsWith('/'))
783 pathStr.chop(1);
787 MUTILS_API QString MUtils::parent_path(const QString &path)
789 QString parentPath(QDir::fromNativeSeparators(path));
790 remove_trailing_separators(parentPath);
791 const int pos = parentPath.lastIndexOf(QLatin1Char('/'));
792 if (pos >= 0)
794 parentPath.truncate(pos);
795 remove_trailing_separators(parentPath);
796 if (parentPath.isEmpty())
798 return QLatin1String("/");
800 if ((parentPath.length() == 2) && parentPath.at(0).isLetter() && (parentPath.at(1) == QLatin1Char(':')))
802 parentPath += QLatin1Char('/');
804 return parentPath;
806 return QString();
809 ///////////////////////////////////////////////////////////////////////////////
810 // REGULAR EXPESSION HELPER
811 ///////////////////////////////////////////////////////////////////////////////
813 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 &value)
815 return regexp_parse_uint32(regexp, &value, 1U, 1U);
818 bool MUtils::regexp_parse_int32(const QRegExp &regexp, qint32 &value)
820 return regexp_parse_int32(regexp, &value, 1U, 1U);
823 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 &value, const size_t &offset)
825 return regexp_parse_uint32(regexp, &value, offset, 1U);
828 bool MUtils::regexp_parse_int32(const QRegExp &regexp, qint32 &value, const size_t &offset)
830 return regexp_parse_int32(regexp, &value, offset, 1U);
833 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 *values, const size_t &count)
835 return regexp_parse_uint32(regexp, values, 1U, count);
838 bool MUtils::regexp_parse_int32(const QRegExp &regexp, qint32 *values, const size_t &count)
840 return regexp_parse_int32(regexp, values, 1U, count);
843 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 *values, const size_t &offset, const size_t &count)
845 const QStringList caps = regexp.capturedTexts();
847 if (caps.isEmpty() || (quint32(caps.count()) <= count))
849 return false;
852 for (size_t i = 0; i < count; i++)
854 bool ok = false;
855 values[i] = caps[offset+i].toUInt(&ok);
856 if (!ok)
858 return false;
862 return true;
865 bool MUtils::regexp_parse_int32(const QRegExp &regexp, qint32 *values, const size_t &offset, const size_t &count)
867 const QStringList caps = regexp.capturedTexts();
869 if (caps.isEmpty() || (quint32(caps.count()) <= count))
871 return false;
874 for (size_t i = 0; i < count; i++)
876 bool ok = false;
877 values[i] = caps[offset+i].toInt(&ok);
878 if (!ok)
880 return false;
884 return true;
887 ///////////////////////////////////////////////////////////////////////////////
888 // AVAILABLE CODEPAGES
889 ///////////////////////////////////////////////////////////////////////////////
891 QStringList MUtils::available_codepages(const bool &noAliases)
893 QStringList codecList;
894 QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
896 while(!availableCodecs.isEmpty())
898 const QByteArray current = availableCodecs.takeFirst();
899 if(!current.toLower().startsWith("system"))
901 codecList << QString::fromLatin1(current.constData(), current.size());
902 if(noAliases)
904 if(QTextCodec *const currentCodec = QTextCodec::codecForName(current.constData()))
906 const QList<QByteArray> aliases = currentCodec->aliases();
907 for(QList<QByteArray>::ConstIterator iter = aliases.constBegin(); iter != aliases.constEnd(); iter++)
909 availableCodecs.removeAll(*iter);
916 return codecList;
919 ///////////////////////////////////////////////////////////////////////////////
920 // FP MATH SUPPORT
921 ///////////////////////////////////////////////////////////////////////////////
923 MUtils::fp_parts_t MUtils::break_fp(const double value)
925 fp_parts_t result = { };
926 if (_finite(value))
928 result.parts[1] = modf(value, &result.parts[0]);
930 else
932 result.parts[0] = std::numeric_limits<double>::quiet_NaN();
933 result.parts[1] = std::numeric_limits<double>::quiet_NaN();
935 return result;
938 ///////////////////////////////////////////////////////////////////////////////
939 // INITIALIZER
940 ///////////////////////////////////////////////////////////////////////////////
942 unsigned int MUtils::Internal::MUTILS_INITIALIZER(const unsigned int interfaceId)
944 if(interfaceId != ((unsigned int)MUTILS_INTERFACE))
946 OS::system_message_err(L"MUtils", L"ERROR: MUtils library initialization has failed!");
947 for(;;) _exit((int)0xC0000142);
950 volatile unsigned int _result = MUTILS_INTERFACE;
951 return _result;