OSDN Git Service

Bump version.
[mutilities/MUtilities.git] / src / Global.cpp
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.
14 //
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
18 //
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
21
22 #if _MSC_VER
23 #define _CRT_RAND_S 1
24 #endif
25
26 //MUtils
27 #include <MUtils/Global.h>
28 #include <MUtils/OSSupport.h>
29 #include <MUtils/Version.h>
30 #include "Internal.h"
31
32 //Internal
33 #include "DirLocker.h"
34 #include "3rd_party/strnatcmp/include/strnatcmp.h"
35
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>
46
47 //CRT
48 #include <cstdlib>
49 #include <ctime>
50 #include <process.h>
51
52 //VLD
53 #ifdef _MSC_VER
54 #include <vld.h>
55 #endif
56
57 //Global
58 const QString MUtils::Internal::g_empty;
59
60 ///////////////////////////////////////////////////////////////////////////////
61 // Random Support
62 ///////////////////////////////////////////////////////////////////////////////
63
64 #ifndef _CRT_RAND_S
65 #define rand_s(X) (true)
66 #endif
67
68 //Per-thread init flag
69 static QThreadStorage<bool> g_srand_flag;
70
71 //32-Bit wrapper for qrand()
72 #define QRAND() ((static_cast<quint32>(qrand()) & 0xFFFF) | (static_cast<quint32>(qrand()) << 16U))
73
74 //Robert Jenkins' 96 bit Mix Function
75 static quint32 mix_function(quint32 a, quint32 b, quint32 c)
76 {
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);
86
87         return a ^ b ^ c;
88 }
89
90 static void seed_rand(void)
91 {
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));
95 }
96
97 static quint32 rand_fallback(void)
98 {
99         Q_ASSERT(RAND_MAX >= 0x7FFF);
100
101         if (!(g_srand_flag.hasLocalData() && g_srand_flag.localData()))
102         {
103                 seed_rand();
104                 g_srand_flag.setLocalData(true);
105         }
106
107         quint32 rnd_val = mix_function(0x32288EA3, clock(), time(NULL));
108
109         for (size_t i = 0; i < 42; i++)
110         {
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);
114         }
115
116         return rnd_val;
117 }
118
119 quint32 MUtils::next_rand_u32(void)
120 {
121         quint32 rnd;
122         if (rand_s(&rnd))
123         {
124                 return rand_fallback();
125         }
126         return rnd;
127 }
128
129 quint32 MUtils::next_rand_u32(const quint32 max)
130 {
131         static const uint32_t DIV_LUT[64] =
132         {
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
141         };
142         return (max < 64) ? (next_rand_u32() / DIV_LUT[max]) : (next_rand_u32() / (UINT32_MAX / max + 1U));
143 }
144
145
146 quint64 MUtils::next_rand_u64(void)
147 {
148         return (quint64(next_rand_u32()) << 32) | quint64(next_rand_u32());
149 }
150
151 QString MUtils::next_rand_str(const bool &bLong)
152 {
153         if (bLong)
154         {
155                 return next_rand_str(false) + next_rand_str(false);
156         }
157         else
158         {
159                 return QString::number(next_rand_u64(), 16).rightJustified(16, QLatin1Char('0'));
160         }
161 }
162
163 ///////////////////////////////////////////////////////////////////////////////
164 // STRING UTILITY FUNCTIONS
165 ///////////////////////////////////////////////////////////////////////////////
166
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;
170
171 static QString& trim_helper(QString &str, QScopedPointer<QRegExp> &regex, const char *const pattern)
172 {
173         QMutexLocker lock(&g_str_trim_lock);
174         if (regex.isNull())
175         {
176                 regex.reset(new QRegExp(QLatin1String(pattern)));
177         }
178         str.remove(*regex.data());
179         return str;
180 }
181
182 QString& MUtils::trim_right(QString &str)
183 {
184         static const char *const TRIM_RIGHT = "\\s+$";
185         return trim_helper(str, g_str_trim_rx_r, TRIM_RIGHT);
186 }
187
188 QString& MUtils::trim_left(QString &str)
189 {
190         static const char *const TRIM_LEFT = "^\\s+";
191         return trim_helper(str, g_str_trim_rx_l, TRIM_LEFT);
192 }
193
194 QString MUtils::trim_right(const QString &str)
195 {
196         QString temp(str);
197         return trim_right(temp);
198 }
199
200 QString MUtils::trim_left(const QString &str)
201 {
202         QString temp(str);
203         return trim_left(temp);
204 }
205
206 ///////////////////////////////////////////////////////////////////////////////
207 // GENERATE FILE NAME
208 ///////////////////////////////////////////////////////////////////////////////
209
210 QString MUtils::make_temp_file(const QString &basePath, const QString &extension, const bool placeholder)
211 {
212         return make_temp_file(QDir(basePath), extension, placeholder);
213 }
214
215 QString MUtils::make_temp_file(const QDir &basePath, const QString &extension, const bool placeholder)
216 {
217         if (extension.isEmpty())
218         {
219                 qWarning("Cannot generate temp file name with invalid parameters!");
220                 return QString();
221         }
222
223         for(int i = 0; i < 4096; i++)
224         {
225                 const QString tempFileName = basePath.absoluteFilePath(QString("%1.%2").arg(next_rand_str(), extension));
226                 if(!QFileInfo(tempFileName).exists())
227                 {
228                         if(placeholder)
229                         {
230                                 QFile file(tempFileName);
231                                 if(file.open(QFile::ReadWrite))
232                                 {
233                                         file.close();
234                                         return tempFileName;
235                                 }
236                         }
237                         else
238                         {
239                                 return tempFileName;
240                         }
241                 }
242         }
243
244         qWarning("Failed to generate temp file name!");
245         return QString();
246 }
247
248 QString MUtils::make_unique_file(const QString &basePath, const QString &baseName, const QString &extension, const bool fancy, const bool placeholder)
249 {
250         return make_unique_file(QDir(basePath), baseName, extension, fancy);
251 }
252
253 QString MUtils::make_unique_file(const QDir &basePath, const QString &baseName, const QString &extension, const bool fancy, const bool placeholder)
254 {
255         if (baseName.isEmpty() || extension.isEmpty())
256         {
257                 qWarning("Cannot generate unique file name with invalid parameters!");
258                 return QString();
259         }
260
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())
264         {
265                 if (n <= quint32(USHRT_MAX))
266                 {
267                         if (fancy)
268                         {
269                                 fileName = basePath.absoluteFilePath(QString("%1 (%2).%3").arg(baseName, QString::number(n++), extension));
270                         }
271                         else
272                         {
273                                 fileName = basePath.absoluteFilePath(QString("%1.%2.%3").arg(baseName, QString::number(n++, 16).rightJustified(4, QLatin1Char('0')), extension));
274                         }
275                 }
276                 else
277                 {
278                         qWarning("Failed to generate unique file name!");
279                         return QString();
280                 }
281         }
282
283         if (placeholder && (!fileName.isEmpty()))
284         {
285                 QFile placeholder(fileName);
286                 if (placeholder.open(QIODevice::WriteOnly))
287                 {
288                         placeholder.close();
289                 }
290         }
291
292         return fileName;
293 }
294
295 ///////////////////////////////////////////////////////////////////////////////
296 // COMPUTE PARITY
297 ///////////////////////////////////////////////////////////////////////////////
298
299 /*
300  * Compute parity in parallel
301  * http://www.graphics.stanford.edu/~seander/bithacks.html#ParityParallel
302  */
303 bool MUtils::parity(quint32 value)
304 {
305         value ^= value >> 16;
306         value ^= value >> 8;
307         value ^= value >> 4;
308         value &= 0xf;
309         return ((0x6996 >> value) & 1) != 0;
310 }
311
312 ///////////////////////////////////////////////////////////////////////////////
313 // TEMP FOLDER
314 ///////////////////////////////////////////////////////////////////////////////
315
316 static QScopedPointer<MUtils::Internal::DirLock> g_temp_folder_file;
317 static QReadWriteLock                            g_temp_folder_lock;
318
319 static QString try_create_subfolder(const QString &baseDir, const QString &postfix)
320 {
321         const QString baseDirPath = QDir(baseDir).absolutePath();
322         for(int i = 0; i < 32; i++)
323         {
324                 QDir directory(baseDirPath);
325                 if(directory.mkpath(postfix) && directory.cd(postfix))
326                 {
327                         return directory.canonicalPath();
328                 }
329         }
330         return QString();
331 }
332
333 static MUtils::Internal::DirLock *try_init_temp_folder(const QString &baseDir)
334 {
335         const QString tempPath = try_create_subfolder(baseDir, MUtils::next_rand_str());
336         if(!tempPath.isEmpty())
337         {
338                 for(int i = 0; i < 32; i++)
339                 {
340                         MUtils::Internal::DirLock *lockFile = NULL;
341                         try
342                         {
343                                 lockFile = new MUtils::Internal::DirLock(tempPath);
344                                 return lockFile;
345                         }
346                         catch(MUtils::Internal::DirLockException&)
347                         {
348                                 /*ignore error and try again*/
349                         }
350                 }
351         }
352         return NULL;
353 }
354
355 static bool temp_folder_cleanup_helper(const QString &tempPath)
356 {
357         size_t delay = 1;
358         static const size_t MAX_DELAY = 8192;
359         forever
360         {
361                 QDir::setCurrent(QDir::rootPath());
362                 if(MUtils::remove_directory(tempPath, true))
363                 {
364                         return true;
365                 }
366                 else
367                 {
368                         if(delay > MAX_DELAY)
369                         {
370                                 return false;
371                         }
372                         MUtils::OS::sleep_ms(delay);
373                         delay *= 2;
374                 }
375         }
376 }
377
378 static void temp_folder_cleaup(void)
379 {
380         QWriteLocker writeLock(&g_temp_folder_lock);
381
382         //Clean the directory
383         while(!g_temp_folder_file.isNull())
384         {
385                 const QString tempPath = g_temp_folder_file->getPath();
386                 g_temp_folder_file.reset(NULL);
387                 if(!temp_folder_cleanup_helper(tempPath))
388                 {
389                         MUtils::OS::system_message_wrn(L"Temp Cleaner", L"Warning: Not all temporary files could be removed!");
390                 }
391         }
392 }
393
394 const QString &MUtils::temp_folder(void)
395 {
396         QReadLocker readLock(&g_temp_folder_lock);
397
398         //Already initialized?
399         if(!g_temp_folder_file.isNull())
400         {
401                 return g_temp_folder_file->getPath();
402         }
403
404         //Obtain the write lock to initilaize
405         readLock.unlock();
406         QWriteLocker writeLock(&g_temp_folder_lock);
407         
408         //Still uninitilaized?
409         if(!g_temp_folder_file.isNull())
410         {
411                 return g_temp_folder_file->getPath();
412         }
413
414         //Try the %TMP% or %TEMP% directory first
415         if(MUtils::Internal::DirLock *lockFile = try_init_temp_folder(QDir::tempPath()))
416         {
417                 g_temp_folder_file.reset(lockFile);
418                 atexit(temp_folder_cleaup);
419                 return lockFile->getPath();
420         }
421
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++)
425         {
426                 const QString &knownFolder = OS::known_folder(FOLDER_ID[id]);
427                 if(!knownFolder.isEmpty())
428                 {
429                         const QString tempRoot = try_create_subfolder(knownFolder, QLatin1String("TEMP"));
430                         if(!tempRoot.isEmpty())
431                         {
432                                 if(MUtils::Internal::DirLock *lockFile = try_init_temp_folder(tempRoot))
433                                 {
434                                         g_temp_folder_file.reset(lockFile);
435                                         atexit(temp_folder_cleaup);
436                                         return lockFile->getPath();
437                                 }
438                         }
439                 }
440         }
441
442         qFatal("Temporary directory could not be initialized !!!");
443         return (*((QString*)NULL));
444 }
445
446 ///////////////////////////////////////////////////////////////////////////////
447 // REMOVE DIRECTORY / FILE
448 ///////////////////////////////////////////////////////////////////////////////
449
450 static const QFile::Permissions FILE_PERMISSIONS_NONE = QFile::ReadOther | QFile::WriteOther;
451
452 bool MUtils::remove_file(const QString &fileName)
453 {
454         QFileInfo fileInfo(fileName);
455
456         for(size_t round = 0; round < 13; ++round)
457         {
458                 if (round > 0)
459                 {
460                         MUtils::OS::sleep_ms(round);
461                         fileInfo.refresh();
462                 }
463                 if (fileInfo.exists())
464                 {
465                         QFile file(fileName);
466                         if (round > 0)
467                         {
468                                 file.setPermissions(FILE_PERMISSIONS_NONE);
469                         }
470                         file.remove();
471                         fileInfo.refresh();
472                 }
473                 if (!fileInfo.exists())
474                 {
475                         return true; /*success*/
476                 }
477         }
478
479         qWarning("Could not delete \"%s\"", MUTILS_UTF8(fileName));
480         return false;
481 }
482
483 bool MUtils::remove_directory(const QString &folderPath, const bool &recursive)
484 {
485         const QDir folder(folderPath);
486
487         if(recursive && folder.exists())
488         {
489                 const QFileInfoList entryList = folder.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
490                 for(QFileInfoList::ConstIterator iter = entryList.constBegin(); iter != entryList.constEnd(); iter++)
491                 {
492                         if(iter->isDir())
493                         {
494                                 remove_directory(iter->canonicalFilePath(), true);
495                         }
496                         else
497                         {
498                                 remove_file(iter->canonicalFilePath());
499                         }
500                 }
501         }
502
503         for(size_t round = 0; round < 13; ++round)
504         {
505                 if(round > 0)
506                 {
507                         MUtils::OS::sleep_ms(round);
508                         folder.refresh();
509                 }
510                 if (folder.exists())
511                 {
512                         QDir parent = folder;
513                         if (parent.cdUp())
514                         {
515                                 if (round > 0)
516                                 {
517                                         QFile::setPermissions(folder.absolutePath(), FILE_PERMISSIONS_NONE);
518                                 }
519                                 parent.rmdir(folder.dirName());
520                                 folder.refresh();
521                         }
522                 }
523                 if (!folder.exists())
524                 {
525                         return true; /*success*/
526                 }
527         }
528         
529         qWarning("Could not rmdir \"%s\"", MUTILS_UTF8(folderPath));
530         return false;
531 }
532
533 ///////////////////////////////////////////////////////////////////////////////
534 // PROCESS UTILS
535 ///////////////////////////////////////////////////////////////////////////////
536
537 static void prependToPath(QProcessEnvironment &env, const QString &value)
538 {
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));
542 }
543
544 void MUtils::init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir, const QStringList *const extraPaths, const QHash<QString, QString> *const extraEnv)
545 {
546         //Environment variable names
547         static const char *const ENVVAR_NAMES_TMP[] =
548         {
549                 "TEMP", "TMP", "TMPDIR", "HOME", "USERPROFILE", "HOMEPATH", NULL
550         };
551         static const char *const ENVVAR_NAMES_SYS[] =
552         {
553                 "WINDIR", "SYSTEMROOT", NULL
554         };
555         static const char *const ENVVAR_NAMES_DEL[] =
556         {
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
559         };
560
561         //Initialize environment
562         QProcessEnvironment env = process.processEnvironment();
563         if (env.isEmpty())
564         {
565                 env = QProcessEnvironment::systemEnvironment();
566         }
567
568         //Clean enviroment variables that might affect our tools
569         for(const char *const *ptr = ENVVAR_NAMES_DEL; *ptr; ++ptr)
570         {
571                 env.remove(QString::fromLatin1(*ptr));
572         }
573
574         //Set up system root directory
575         const QString sysRoot = QDir::toNativeSeparators(OS::known_folder(OS::FOLDER_SYSROOT));
576         if (!sysRoot.isEmpty())
577         {
578                 for (const char *const *ptr = ENVVAR_NAMES_SYS; *ptr; ++ptr)
579                 {
580                         env.insert(QString::fromLatin1(*ptr), sysRoot);
581                 }
582         }
583
584         //Replace TEMP directory in environment
585         const QString tempDir = QDir::toNativeSeparators(temp_folder());
586         if(bReplaceTempDir)
587         {
588                 for (const char *const *ptr = ENVVAR_NAMES_TMP; *ptr; ++ptr)
589                 {
590                         env.insert(QString::fromLatin1(*ptr), tempDir);
591                 }
592         }
593
594         //Setup PATH variable
595         prependToPath(env, tempDir);
596         if (extraPaths && (!extraPaths->isEmpty()))
597         {
598                 QListIterator<QString> iter(*extraPaths);
599                 iter.toBack();
600                 while (iter.hasPrevious())
601                 {
602                         prependToPath(env, QDir::toNativeSeparators(iter.previous()));
603                 }
604         }
605         
606         //Setup environment
607         if (extraEnv && (!extraEnv->isEmpty()))
608         {
609                 for (QHash<QString, QString>::ConstIterator iter = extraEnv->constBegin(); iter != extraEnv->constEnd(); iter++)
610                 {
611                         env.insert(iter.key(), iter.value());
612                 }
613         }
614
615         //Setup QPorcess object
616         process.setWorkingDirectory(wokringDir);
617         process.setProcessChannelMode(QProcess::MergedChannels);
618         process.setReadChannel(QProcess::StandardOutput);
619         process.setProcessEnvironment(env);
620 }
621
622 ///////////////////////////////////////////////////////////////////////////////
623 // NATURAL ORDER STRING COMPARISON
624 ///////////////////////////////////////////////////////////////////////////////
625
626 static bool natural_string_sort_helper(const QString &str1, const QString &str2)
627 {
628         return (MUtils::Internal::NaturalSort::strnatcmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
629 }
630
631 static bool natural_string_sort_helper_fold_case(const QString &str1, const QString &str2)
632 {
633         return (MUtils::Internal::NaturalSort::strnatcasecmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
634 }
635
636 void MUtils::natural_string_sort(QStringList &list, const bool bIgnoreCase)
637 {
638         qSort(list.begin(), list.end(), bIgnoreCase ? natural_string_sort_helper_fold_case : natural_string_sort_helper);
639 }
640
641 ///////////////////////////////////////////////////////////////////////////////
642 // CLEAN FILE PATH
643 ///////////////////////////////////////////////////////////////////////////////
644
645 static QMutex                                              g_clean_file_name_mutex;
646 static QScopedPointer<const QList<QPair<QRegExp,QString>>> g_clean_file_name_regex;
647
648 static void clean_file_name_make_pretty(QString &str)
649 {
650         static const struct { const char *p; const char *r; } PATTERN[] =
651         {
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 }
658         };
659
660         QMutexLocker locker(&g_clean_file_name_mutex);
661
662         if (g_clean_file_name_regex.isNull())
663         {
664                 QScopedPointer<QList<QPair<QRegExp, QString>>> list(new QList<QPair<QRegExp, QString>>());
665                 for (size_t i = 0; PATTERN[i].p; ++i)
666                 {
667                         list->append(qMakePair(QRegExp(QString::fromUtf8(PATTERN[i].p), Qt::CaseInsensitive), PATTERN[i].r ? QString::fromUtf8(PATTERN[i].r) : QString()));
668                 }
669                 g_clean_file_name_regex.reset(list.take());
670         }
671
672         bool keepOnGoing = !str.isEmpty();
673         while(keepOnGoing)
674         {
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)
678                 {
679                         str.replace(iter->first, iter->second);
680                         if (str.compare(prev))
681                         {
682                                 str = str.simplified();
683                                 keepOnGoing = !str.isEmpty();
684                                 break;
685                         }
686                 }
687         }
688 }
689
690 QString MUtils::clean_file_name(const QString &name, const bool &pretty)
691 {
692         static const QLatin1Char REPLACEMENT_CHAR('_');
693         static const char FILENAME_ILLEGAL_CHARS[] = "<>:\"/\\|?*";
694         static const char *const FILENAME_RESERVED_NAMES[] =
695         {
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
699         };
700
701         QString result(name);
702         if (pretty)
703         {
704                 clean_file_name_make_pretty(result);
705         }
706
707         for(QString::Iterator iter = result.begin(); iter != result.end(); iter++)
708         {
709                 if (iter->category() == QChar::Other_Control)
710                 {
711                         *iter = REPLACEMENT_CHAR;
712                 }
713         }
714                 
715         for(size_t i = 0; FILENAME_ILLEGAL_CHARS[i]; i++)
716         {
717                 result.replace(QLatin1Char(FILENAME_ILLEGAL_CHARS[i]), REPLACEMENT_CHAR);
718         }
719         
720         trim_right(result);
721         while (result.endsWith(QLatin1Char('.')))
722         {
723                 result.chop(1);
724                 trim_right(result);
725         }
726
727         for (size_t i = 0; FILENAME_RESERVED_NAMES[i]; i++)
728         {
729                 const QString reserved = QString::fromLatin1(FILENAME_RESERVED_NAMES[i]);
730                 if ((!result.compare(reserved, Qt::CaseInsensitive)) || result.startsWith(reserved + QLatin1Char('.'), Qt::CaseInsensitive))
731                 {
732                         result.replace(0, reserved.length(), QString().leftJustified(reserved.length(), REPLACEMENT_CHAR));
733                 }
734         }
735
736         return result;
737 }
738
739 static QPair<QString,QString> clean_file_path_get_prefix(const QString path)
740 {
741         static const char *const PREFIXES[] =
742         {
743                 "//?/", "//", "/", NULL
744         };
745         const QString posixPath = QDir::fromNativeSeparators(path.trimmed());
746         for (int i = 0; PREFIXES[i]; i++)
747         {
748                 const QString prefix = QString::fromLatin1(PREFIXES[i]);
749                 if (posixPath.startsWith(prefix))
750                 {
751                         return qMakePair(prefix, posixPath.mid(prefix.length()));
752                 }
753         }
754         return qMakePair(QString(), posixPath);
755 }
756
757 QString MUtils::clean_file_path(const QString &path, const bool &pretty)
758 {
759         const QPair<QString, QString> prefix = clean_file_path_get_prefix(path);
760
761         QStringList parts = prefix.second.split(QLatin1Char('/'), QString::SkipEmptyParts);
762         for(int i = 0; i < parts.count(); i++)
763         {
764                 if((i == 0) && (parts[i].length() == 2) && parts[i][0].isLetter() && (parts[i][1] == QLatin1Char(':')))
765                 {
766                         continue; //handle case "c:\"
767                 }
768                 parts[i] = MUtils::clean_file_name(parts[i], pretty);
769         }
770
771         const QString cleanPath = parts.join(QLatin1String("/"));
772         return prefix.first.isEmpty() ? cleanPath : prefix.first + cleanPath;
773 }
774
775 ///////////////////////////////////////////////////////////////////////////////
776 // PARENT PATH
777 ///////////////////////////////////////////////////////////////////////////////
778
779 static void remove_trailing_separators(QString &pathStr)
780 {
781         while (pathStr.endsWith('/'))
782         {
783                 pathStr.chop(1);
784         }
785 }
786
787 MUTILS_API QString MUtils::parent_path(const QString &path)
788 {
789         QString parentPath(QDir::fromNativeSeparators(path));
790         remove_trailing_separators(parentPath);
791         const int pos = parentPath.lastIndexOf(QLatin1Char('/'));
792         if (pos >= 0)
793         {
794                 parentPath.truncate(pos);
795                 remove_trailing_separators(parentPath);
796                 if (parentPath.isEmpty())
797                 {
798                         return QLatin1String("/");
799                 }
800                 if ((parentPath.length() == 2) && parentPath.at(0).isLetter() && (parentPath.at(1) == QLatin1Char(':')))
801                 {
802                         parentPath += QLatin1Char('/');
803                 }
804                 return parentPath;
805         }
806         return QString();
807 }
808
809 ///////////////////////////////////////////////////////////////////////////////
810 // REGULAR EXPESSION HELPER
811 ///////////////////////////////////////////////////////////////////////////////
812
813 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 &value)
814 {
815         return regexp_parse_uint32(regexp, &value, 1U, 1U);
816 }
817
818 bool MUtils::regexp_parse_int32(const QRegExp &regexp, qint32 &value)
819 {
820         return regexp_parse_int32(regexp, &value, 1U, 1U);
821 }
822
823 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 &value, const size_t &offset)
824 {
825         return regexp_parse_uint32(regexp, &value, offset, 1U);
826 }
827
828 bool MUtils::regexp_parse_int32(const QRegExp &regexp, qint32 &value, const size_t &offset)
829 {
830         return regexp_parse_int32(regexp, &value, offset, 1U);
831 }
832
833 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 *values, const size_t &count)
834 {
835         return regexp_parse_uint32(regexp, values, 1U, count);
836 }
837
838 bool MUtils::regexp_parse_int32(const QRegExp &regexp, qint32 *values, const size_t &count)
839 {
840         return regexp_parse_int32(regexp, values, 1U, count);
841 }
842
843 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 *values, const size_t &offset, const size_t &count)
844 {
845         const QStringList caps = regexp.capturedTexts();
846
847         if (caps.isEmpty() || (quint32(caps.count()) <= count))
848         {
849                 return false;
850         }
851
852         for (size_t i = 0; i < count; i++)
853         {
854                 bool ok = false;
855                 values[i] = caps[offset+i].toUInt(&ok);
856                 if (!ok)
857                 {
858                         return false;
859                 }
860         }
861
862         return true;
863 }
864
865 bool MUtils::regexp_parse_int32(const QRegExp &regexp, qint32 *values, const size_t &offset, const size_t &count)
866 {
867         const QStringList caps = regexp.capturedTexts();
868
869         if (caps.isEmpty() || (quint32(caps.count()) <= count))
870         {
871                 return false;
872         }
873
874         for (size_t i = 0; i < count; i++)
875         {
876                 bool ok = false;
877                 values[i] = caps[offset+i].toInt(&ok);
878                 if (!ok)
879                 {
880                         return false;
881                 }
882         }
883
884         return true;
885 }
886
887 ///////////////////////////////////////////////////////////////////////////////
888 // AVAILABLE CODEPAGES
889 ///////////////////////////////////////////////////////////////////////////////
890
891 QStringList MUtils::available_codepages(const bool &noAliases)
892 {
893         QStringList codecList;
894         QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
895
896         while(!availableCodecs.isEmpty())
897         {
898                 const QByteArray current = availableCodecs.takeFirst();
899                 if(!current.toLower().startsWith("system"))
900                 {
901                         codecList << QString::fromLatin1(current.constData(), current.size());
902                         if(noAliases)
903                         {
904                                 if(QTextCodec *const currentCodec = QTextCodec::codecForName(current.constData()))
905                                 {
906                                         const QList<QByteArray> aliases = currentCodec->aliases();
907                                         for(QList<QByteArray>::ConstIterator iter = aliases.constBegin(); iter != aliases.constEnd(); iter++)
908                                         {
909                                                 availableCodecs.removeAll(*iter);
910                                         }
911                                 }
912                         }
913                 }
914         }
915
916         return codecList;
917 }
918
919 ///////////////////////////////////////////////////////////////////////////////
920 // FP MATH SUPPORT
921 ///////////////////////////////////////////////////////////////////////////////
922
923 MUtils::fp_parts_t MUtils::break_fp(const double value)
924 {
925         fp_parts_t result = { };
926         if (_finite(value))
927         {
928                 result.parts[1] = modf(value, &result.parts[0]);
929         }
930         else
931         {
932                 result.parts[0] = std::numeric_limits<double>::quiet_NaN();
933                 result.parts[1] = std::numeric_limits<double>::quiet_NaN();
934         }
935         return result;
936 }
937
938 ///////////////////////////////////////////////////////////////////////////////
939 // INITIALIZER
940 ///////////////////////////////////////////////////////////////////////////////
941
942 unsigned int MUtils::Internal::MUTILS_INITIALIZER(const unsigned int interfaceId)
943 {
944         if(interfaceId != ((unsigned int)MUTILS_INTERFACE))
945         {
946                 OS::system_message_err(L"MUtils", L"ERROR: MUtils library initialization has failed!");
947                 for(;;) _exit((int)0xC0000142);
948         }
949
950         volatile unsigned int _result = MUTILS_INTERFACE;
951         return _result;
952 }