OSDN Git Service

Removed seed_rand() function. Seeding will now be done automatically, if needed.
[mutilities/MUtilities.git] / src / Global.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2016 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
31 //Internal
32 #include "DirLocker.h"
33 #include "3rd_party/strnatcmp/include/strnatcmp.h"
34
35 //Qt
36 #include <QDir>
37 #include <QReadWriteLock>
38 #include <QProcess>
39 #include <QTextCodec>
40 #include <QPair>
41 #include <QListIterator>
42 #include <QMutex>
43 #include <QThreadStorage>
44
45 //CRT
46 #include <cstdlib>
47 #include <ctime>
48 #include <process.h>
49
50 //VLD
51 #ifdef _MSC_VER
52 #include <vld.h>
53 #endif
54
55 ///////////////////////////////////////////////////////////////////////////////
56 // Random Support
57 ///////////////////////////////////////////////////////////////////////////////
58
59 #ifndef _CRT_RAND_S
60 #define rand_s(X) (true)
61 #endif
62
63 //Per-thread init flag
64 static QThreadStorage<bool> g_srand_flag;
65
66 //Robert Jenkins' 96 bit Mix Function
67 static quint32 mix_function(const quint32 x, const quint32 y, const quint32 z)
68 {
69         quint32 a = x;
70         quint32 b = y;
71         quint32 c = z;
72         
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);
82
83         return a ^ b ^ c;
84 }
85
86 static void seed_rand(void)
87 {
88         fprintf(stderr, "SEED RAND (TID: %u)\n", MUtils::OS::thread_id());
89         QDateTime build(MUtils::Version::lib_build_date(), MUtils::Version::lib_build_time());
90         const quint32 seed = mix_function(MUtils::OS::process_id(), MUtils::OS::thread_id(), build.toMSecsSinceEpoch());
91         qsrand(mix_function(clock(), time(NULL), seed));
92 }
93
94 static quint32 rand_fallback(void)
95 {
96         Q_ASSERT(RAND_MAX >= 0xFFF);
97         if (!(g_srand_flag.hasLocalData() && g_srand_flag.localData()))
98         {
99                 seed_rand();
100                 g_srand_flag.setLocalData(true);
101         }
102         quint32 rnd = 0x32288EA3;
103         for (size_t i = 0; i < 3; i++)
104         {
105                 rnd = (rnd << 12) ^ qrand();
106         }
107         return rnd;
108 }
109
110 quint32 MUtils::next_rand_u32(void)
111 {
112         quint32 rnd;
113         if (rand_s(&rnd))
114         {
115                 return rand_fallback();
116         }
117         return rnd;
118 }
119
120 quint64 MUtils::next_rand_u64(void)
121 {
122         return (quint64(next_rand_u32()) << 32) | quint64(next_rand_u32());
123 }
124
125 QString MUtils::next_rand_str(const bool &bLong)
126 {
127         if (bLong)
128         {
129                 return next_rand_str(false) + next_rand_str(false);
130         }
131         else
132         {
133                 return QString::number(next_rand_u64(), 16).rightJustified(16, QLatin1Char('0'));
134         }
135 }
136
137 ///////////////////////////////////////////////////////////////////////////////
138 // STRING UTILITY FUNCTIONS
139 ///////////////////////////////////////////////////////////////////////////////
140
141 static QScopedPointer<QRegExp> g_str_trim_rx_r;
142 static QScopedPointer<QRegExp> g_str_trim_rx_l;
143 static QMutex                  g_str_trim_lock;
144
145 static QString& trim_helper(QString &str, QScopedPointer<QRegExp> &regex, const char *const pattern)
146 {
147         QMutexLocker lock(&g_str_trim_lock);
148         if (regex.isNull())
149         {
150                 regex.reset(new QRegExp(QLatin1String(pattern)));
151         }
152         str.remove(*regex.data());
153         return str;
154 }
155
156 QString& MUtils::trim_right(QString &str)
157 {
158         static const char *const TRIM_RIGHT = "\\s+$";
159         return trim_helper(str, g_str_trim_rx_r, TRIM_RIGHT);
160 }
161
162 QString& MUtils::trim_left(QString &str)
163 {
164         static const char *const TRIM_LEFT = "^\\s+";
165         return trim_helper(str, g_str_trim_rx_l, TRIM_LEFT);
166 }
167
168 QString MUtils::trim_right(const QString &str)
169 {
170         QString temp(str);
171         return trim_right(temp);
172 }
173
174 QString MUtils::trim_left(const QString &str)
175 {
176         QString temp(str);
177         return trim_left(temp);
178 }
179
180 ///////////////////////////////////////////////////////////////////////////////
181 // GENERATE FILE NAME
182 ///////////////////////////////////////////////////////////////////////////////
183
184 QString MUtils::make_temp_file(const QString &basePath, const QString &extension, const bool placeholder)
185 {
186         for(int i = 0; i < 4096; i++)
187         {
188                 const QString tempFileName = QString("%1/%2.%3").arg(basePath, next_rand_str(), extension);
189                 if(!QFileInfo(tempFileName).exists())
190                 {
191                         if(placeholder)
192                         {
193                                 QFile file(tempFileName);
194                                 if(file.open(QFile::ReadWrite))
195                                 {
196                                         file.close();
197                                         return tempFileName;
198                                 }
199                         }
200                         else
201                         {
202                                 return tempFileName;
203                         }
204                 }
205         }
206
207         qWarning("Failed to generate temp file name!");
208         return QString();
209 }
210
211 QString MUtils::make_unique_file(const QString &basePath, const QString &baseName, const QString &extension, const bool fancy)
212 {
213         quint32 n = fancy ? 2 : 0;
214         QString fileName = fancy ? QString("%1/%2.%3").arg(basePath, baseName, extension) : QString();
215         while (fileName.isEmpty() || QFileInfo(fileName).exists())
216         {
217                 if (n <= quint32(USHRT_MAX))
218                 {
219                         if (fancy)
220                         {
221                                 fileName = QString("%1/%2 (%3).%4").arg(basePath, baseName, QString::number(n++), extension);
222                         }
223                         else
224                         {
225                                 fileName = QString("%1/%2.%3.%4").arg(basePath, baseName, QString::number(n++, 16).rightJustified(4, QLatin1Char('0')), extension);
226                         }
227                 }
228                 else
229                 {
230                         qWarning("Failed to generate unique file name!");
231                         return QString();
232                 }
233         }
234         return fileName;
235 }
236
237 ///////////////////////////////////////////////////////////////////////////////
238 // COMPUTE PARITY
239 ///////////////////////////////////////////////////////////////////////////////
240
241 /*
242  * Compute parity in parallel
243  * http://www.graphics.stanford.edu/~seander/bithacks.html#ParityParallel
244  */
245 bool MUtils::parity(quint32 value)
246 {
247         value ^= value >> 16;
248         value ^= value >> 8;
249         value ^= value >> 4;
250         value &= 0xf;
251         return ((0x6996 >> value) & 1) != 0;
252 }
253
254 ///////////////////////////////////////////////////////////////////////////////
255 // TEMP FOLDER
256 ///////////////////////////////////////////////////////////////////////////////
257
258 static QScopedPointer<MUtils::Internal::DirLock> g_temp_folder_file;
259 static QReadWriteLock                            g_temp_folder_lock;
260
261 static QString try_create_subfolder(const QString &baseDir, const QString &postfix)
262 {
263         const QString baseDirPath = QDir(baseDir).absolutePath();
264         for(int i = 0; i < 32; i++)
265         {
266                 QDir directory(baseDirPath);
267                 if(directory.mkpath(postfix) && directory.cd(postfix))
268                 {
269                         return directory.canonicalPath();
270                 }
271         }
272         return QString();
273 }
274
275 static MUtils::Internal::DirLock *try_init_temp_folder(const QString &baseDir)
276 {
277         const QString tempPath = try_create_subfolder(baseDir, MUtils::next_rand_str());
278         if(!tempPath.isEmpty())
279         {
280                 for(int i = 0; i < 32; i++)
281                 {
282                         MUtils::Internal::DirLock *lockFile = NULL;
283                         try
284                         {
285                                 lockFile = new MUtils::Internal::DirLock(tempPath);
286                                 return lockFile;
287                         }
288                         catch(MUtils::Internal::DirLockException&)
289                         {
290                                 /*ignore error and try again*/
291                         }
292                 }
293         }
294         return NULL;
295 }
296
297 static bool temp_folder_cleanup_helper(const QString &tempPath)
298 {
299         size_t delay = 1;
300         static const size_t MAX_DELAY = 8192;
301         forever
302         {
303                 QDir::setCurrent(QDir::rootPath());
304                 if(MUtils::remove_directory(tempPath, true))
305                 {
306                         return true;
307                 }
308                 else
309                 {
310                         if(delay > MAX_DELAY)
311                         {
312                                 return false;
313                         }
314                         MUtils::OS::sleep_ms(delay);
315                         delay *= 2;
316                 }
317         }
318 }
319
320 static void temp_folder_cleaup(void)
321 {
322         QWriteLocker writeLock(&g_temp_folder_lock);
323
324         //Clean the directory
325         while(!g_temp_folder_file.isNull())
326         {
327                 const QString tempPath = g_temp_folder_file->getPath();
328                 g_temp_folder_file.reset(NULL);
329                 if(!temp_folder_cleanup_helper(tempPath))
330                 {
331                         MUtils::OS::system_message_wrn(L"Temp Cleaner", L"Warning: Not all temporary files could be removed!");
332                 }
333         }
334 }
335
336 const QString &MUtils::temp_folder(void)
337 {
338         QReadLocker readLock(&g_temp_folder_lock);
339
340         //Already initialized?
341         if(!g_temp_folder_file.isNull())
342         {
343                 return g_temp_folder_file->getPath();
344         }
345
346         //Obtain the write lock to initilaize
347         readLock.unlock();
348         QWriteLocker writeLock(&g_temp_folder_lock);
349         
350         //Still uninitilaized?
351         if(!g_temp_folder_file.isNull())
352         {
353                 return g_temp_folder_file->getPath();
354         }
355
356         //Try the %TMP% or %TEMP% directory first
357         if(MUtils::Internal::DirLock *lockFile = try_init_temp_folder(QDir::tempPath()))
358         {
359                 g_temp_folder_file.reset(lockFile);
360                 atexit(temp_folder_cleaup);
361                 return lockFile->getPath();
362         }
363
364         qWarning("%%TEMP%% directory not found -> trying fallback mode now!");
365         static const OS::known_folder_t FOLDER_ID[2] = { OS::FOLDER_LOCALAPPDATA, OS::FOLDER_SYSTROOT_DIR };
366         for(size_t id = 0; id < 2; id++)
367         {
368                 const QString &knownFolder = OS::known_folder(FOLDER_ID[id]);
369                 if(!knownFolder.isEmpty())
370                 {
371                         const QString tempRoot = try_create_subfolder(knownFolder, QLatin1String("TEMP"));
372                         if(!tempRoot.isEmpty())
373                         {
374                                 if(MUtils::Internal::DirLock *lockFile = try_init_temp_folder(tempRoot))
375                                 {
376                                         g_temp_folder_file.reset(lockFile);
377                                         atexit(temp_folder_cleaup);
378                                         return lockFile->getPath();
379                                 }
380                         }
381                 }
382         }
383
384         qFatal("Temporary directory could not be initialized !!!");
385         return (*((QString*)NULL));
386 }
387
388 ///////////////////////////////////////////////////////////////////////////////
389 // REMOVE DIRECTORY / FILE
390 ///////////////////////////////////////////////////////////////////////////////
391
392 static const QFile::Permissions FILE_PERMISSIONS_NONE = QFile::ReadOther | QFile::WriteOther;
393
394 bool MUtils::remove_file(const QString &fileName)
395 {
396         QFileInfo fileInfo(fileName);
397         if(!(fileInfo.exists() && fileInfo.isFile()))
398         {
399                 return true;
400         }
401
402         for(int i = 0; i < 32; i++)
403         {
404                 QFile file(fileName);
405                 file.setPermissions(FILE_PERMISSIONS_NONE);
406                 if((!(fileInfo.exists() && fileInfo.isFile())) || file.remove())
407                 {
408                         return true;
409                 }
410                 fileInfo.refresh();
411         }
412
413         qWarning("Could not delete \"%s\"", MUTILS_UTF8(fileName));
414         return false;
415 }
416
417 static bool remove_directory_helper(const QDir &folder)
418 {
419         if(!folder.exists())
420         {
421                 return true;
422         }
423         const QString dirName = folder.dirName();
424         if(!dirName.isEmpty())
425         {
426                 QDir parent(folder);
427                 if(parent.cdUp())
428                 {
429                         QFile::setPermissions(folder.absolutePath(), FILE_PERMISSIONS_NONE);
430                         if(parent.rmdir(dirName))
431                         {
432                                 return true;
433                         }
434                 }
435         }
436         return false;
437 }
438
439 bool MUtils::remove_directory(const QString &folderPath, const bool &recursive)
440 {
441         QDir folder(folderPath);
442         if(!folder.exists())
443         {
444                 return true;
445         }
446
447         if(recursive)
448         {
449                 const QFileInfoList entryList = folder.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
450                 for(QFileInfoList::ConstIterator iter = entryList.constBegin(); iter != entryList.constEnd(); iter++)
451                 {
452                         if(iter->isDir())
453                         {
454                                 remove_directory(iter->canonicalFilePath(), true);
455                         }
456                         else if(iter->isFile())
457                         {
458                                 remove_file(iter->canonicalFilePath());
459                         }
460                 }
461         }
462
463         for(int i = 0; i < 32; i++)
464         {
465                 if(remove_directory_helper(folder))
466                 {
467                         return true;
468                 }
469                 folder.refresh();
470         }
471         
472         qWarning("Could not rmdir \"%s\"", MUTILS_UTF8(folderPath));
473         return false;
474 }
475
476 ///////////////////////////////////////////////////////////////////////////////
477 // PROCESS UTILS
478 ///////////////////////////////////////////////////////////////////////////////
479
480 static void prependToPath(QProcessEnvironment &env, const QString &value)
481 {
482         const QLatin1String PATH = QLatin1String("PATH");
483         const QString path = env.value(PATH, QString()).trimmed();
484         env.insert(PATH, path.isEmpty() ? value : QString("%1;%2").arg(value, path));
485 }
486
487 void MUtils::init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir, const QStringList *const extraPaths)
488 {
489         //Environment variable names
490         static const char *const s_envvar_names_temp[] =
491         {
492                 "TEMP", "TMP", "TMPDIR", "HOME", "USERPROFILE", "HOMEPATH", NULL
493         };
494         static const char *const s_envvar_names_remove[] =
495         {
496                 "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
497         };
498
499         //Initialize environment
500         QProcessEnvironment env = process.processEnvironment();
501         if(env.isEmpty()) env = QProcessEnvironment::systemEnvironment();
502
503         //Clean a number of enviroment variables that might affect our tools
504         for(size_t i = 0; s_envvar_names_remove[i]; i++)
505         {
506                 env.remove(QString::fromLatin1(s_envvar_names_remove[i]));
507                 env.remove(QString::fromLatin1(s_envvar_names_remove[i]).toLower());
508         }
509
510         const QString tempDir = QDir::toNativeSeparators(temp_folder());
511
512         //Replace TEMP directory in environment
513         if(bReplaceTempDir)
514         {
515                 for(size_t i = 0; s_envvar_names_temp[i]; i++)
516                 {
517                         env.insert(s_envvar_names_temp[i], tempDir);
518                 }
519         }
520
521         //Setup PATH variable
522         prependToPath(env, tempDir);
523         if (extraPaths && (!extraPaths->isEmpty()))
524         {
525                 QListIterator<QString> iter(*extraPaths);
526                 iter.toBack();
527                 while (iter.hasPrevious())
528                 {
529                         prependToPath(env, QDir::toNativeSeparators(iter.previous()));
530                 }
531         }
532         
533         //Setup QPorcess object
534         process.setWorkingDirectory(wokringDir);
535         process.setProcessChannelMode(QProcess::MergedChannels);
536         process.setReadChannel(QProcess::StandardOutput);
537         process.setProcessEnvironment(env);
538 }
539
540 ///////////////////////////////////////////////////////////////////////////////
541 // NATURAL ORDER STRING COMPARISON
542 ///////////////////////////////////////////////////////////////////////////////
543
544 static bool natural_string_sort_helper(const QString &str1, const QString &str2)
545 {
546         return (MUtils::Internal::NaturalSort::strnatcmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
547 }
548
549 static bool natural_string_sort_helper_fold_case(const QString &str1, const QString &str2)
550 {
551         return (MUtils::Internal::NaturalSort::strnatcasecmp(MUTILS_WCHR(str1), MUTILS_WCHR(str2)) < 0);
552 }
553
554 void MUtils::natural_string_sort(QStringList &list, const bool bIgnoreCase)
555 {
556         qSort(list.begin(), list.end(), bIgnoreCase ? natural_string_sort_helper_fold_case : natural_string_sort_helper);
557 }
558
559 ///////////////////////////////////////////////////////////////////////////////
560 // CLEAN FILE PATH
561 ///////////////////////////////////////////////////////////////////////////////
562
563 QString MUtils::clean_file_name(const QString &name)
564 {
565         static const QLatin1Char REPLACEMENT_CHAR('_');
566         static const char FILENAME_ILLEGAL_CHARS[] = "<>:\"/\\|?*";
567         static const char *const FILENAME_RESERVED_NAMES[] =
568         {
569                 "CON", "PRN", "AUX", "NUL",
570                 "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
571                 "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", NULL
572         };
573
574         QString result(name);
575         if (result.contains(QLatin1Char('"')))
576         {
577                 QRegExp quoted("\"(.+)\"");
578                 quoted.setMinimal(true);
579                 result.replace(quoted, "``\\1ยดยด");
580         }
581
582         for(QString::Iterator iter = result.begin(); iter != result.end(); iter++)
583         {
584                 if (iter->category() == QChar::Other_Control)
585                 {
586                         *iter = REPLACEMENT_CHAR;
587                 }
588         }
589                 
590         for(size_t i = 0; FILENAME_ILLEGAL_CHARS[i]; i++)
591         {
592                 result.replace(QLatin1Char(FILENAME_ILLEGAL_CHARS[i]), REPLACEMENT_CHAR);
593         }
594         
595         trim_right(result);
596         while (result.endsWith(QLatin1Char('.')))
597         {
598                 result.chop(1);
599                 trim_right(result);
600         }
601
602         for (size_t i = 0; FILENAME_RESERVED_NAMES[i]; i++)
603         {
604                 const QString reserved = QString::fromLatin1(FILENAME_RESERVED_NAMES[i]);
605                 if ((!result.compare(reserved, Qt::CaseInsensitive)) || result.startsWith(reserved + QLatin1Char('.'), Qt::CaseInsensitive))
606                 {
607                         result.replace(0, reserved.length(), QString().leftJustified(reserved.length(), REPLACEMENT_CHAR));
608                 }
609         }
610
611         return result;
612 }
613
614 static QPair<QString,QString> clean_file_path_get_prefix(const QString path)
615 {
616         static const char *const PREFIXES[] =
617         {
618                 "//?/", "//", "/", NULL
619         };
620         const QString posixPath = QDir::fromNativeSeparators(path.trimmed());
621         for (int i = 0; PREFIXES[i]; i++)
622         {
623                 const QString prefix = QString::fromLatin1(PREFIXES[i]);
624                 if (posixPath.startsWith(prefix))
625                 {
626                         return qMakePair(prefix, posixPath.mid(prefix.length()));
627                 }
628         }
629         return qMakePair(QString(), posixPath);
630 }
631
632 QString MUtils::clean_file_path(const QString &path)
633 {
634         const QPair<QString, QString> prefix = clean_file_path_get_prefix(path);
635
636         QStringList parts = prefix.second.split(QLatin1Char('/'), QString::SkipEmptyParts);
637         for(int i = 0; i < parts.count(); i++)
638         {
639                 if((i == 0) && (parts[i].length() == 2) && parts[i][0].isLetter() && (parts[i][1] == QLatin1Char(':')))
640                 {
641                         continue; //handle case "c:\"
642                 }
643                 parts[i] = MUtils::clean_file_name(parts[i]);
644         }
645
646         const QString cleanPath = parts.join(QLatin1String("/"));
647         return prefix.first.isEmpty() ? cleanPath : prefix.first + cleanPath;
648 }
649
650 ///////////////////////////////////////////////////////////////////////////////
651 // REGULAR EXPESSION HELPER
652 ///////////////////////////////////////////////////////////////////////////////
653
654 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 &value)
655 {
656         return regexp_parse_uint32(regexp, &value, 1);
657 }
658
659 bool MUtils::regexp_parse_uint32(const QRegExp &regexp, quint32 *values, const size_t &count)
660 {
661         const QStringList caps = regexp.capturedTexts();
662         
663         if(caps.isEmpty() || (quint32(caps.count()) <= count))
664         {
665                 return false;
666         }
667
668         for(size_t i = 0; i < count; i++)
669         {
670                 bool ok = false;
671                 values[i] = caps[i+1].toUInt(&ok);
672                 if(!ok)
673                 {
674                         return false;
675                 }
676         }
677
678         return true;
679 }
680
681 ///////////////////////////////////////////////////////////////////////////////
682 // AVAILABLE CODEPAGES
683 ///////////////////////////////////////////////////////////////////////////////
684
685 QStringList MUtils::available_codepages(const bool &noAliases)
686 {
687         QStringList codecList;
688         QList<QByteArray> availableCodecs = QTextCodec::availableCodecs();
689
690         while(!availableCodecs.isEmpty())
691         {
692                 const QByteArray current = availableCodecs.takeFirst();
693                 if(!current.toLower().startsWith("system"))
694                 {
695                         codecList << QString::fromLatin1(current.constData(), current.size());
696                         if(noAliases)
697                         {
698                                 if(QTextCodec *const currentCodec = QTextCodec::codecForName(current.constData()))
699                                 {
700                                         const QList<QByteArray> aliases = currentCodec->aliases();
701                                         for(QList<QByteArray>::ConstIterator iter = aliases.constBegin(); iter != aliases.constEnd(); iter++)
702                                         {
703                                                 availableCodecs.removeAll(*iter);
704                                         }
705                                 }
706                         }
707                 }
708         }
709
710         return codecList;
711 }
712
713 ///////////////////////////////////////////////////////////////////////////////
714 // SELF-TEST
715 ///////////////////////////////////////////////////////////////////////////////
716
717 int MUtils::Internal::selfTest(const char *const buildKey, const bool debug)
718 {
719         static const bool MY_DEBUG_FLAG = MUTILS_DEBUG;
720         static const char *const MY_BUILD_KEY = __DATE__ "@" __TIME__;
721
722         if(strncmp(buildKey, MY_BUILD_KEY, 13) || (MY_DEBUG_FLAG != debug))
723         {
724                 MUtils::OS::system_message_err(L"MUtils", L"FATAL ERROR: MUtils library version mismatch detected!");
725                 MUtils::OS::system_message_wrn(L"MUtils", L"Perform a clean(!) re-install of the application to fix the problem!");
726                 abort();
727         }
728         return 0;
729 }