OSDN Git Service

Removed seed_rand() function. Seeding will now be done automatically, if needed.
[mutilities/MUtilities.git] / src / Global.cpp
index 02c2a37..9f1ccb2 100644 (file)
@@ -1,6 +1,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 // MuldeR's Utilities for Qt
-// Copyright (C) 2004-2015 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2016 LoRd_MuldeR <MuldeR2@GMX.de>
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
 //////////////////////////////////////////////////////////////////////////////////
 
 #if _MSC_VER
-#define _CRT_RAND_S 1
+//#define _CRT_RAND_S 1
 #endif
 
 //MUtils
 #include <MUtils/Global.h>
 #include <MUtils/OSSupport.h>
+#include <MUtils/Version.h>
 
 //Internal
 #include "DirLocker.h"
 #include <QReadWriteLock>
 #include <QProcess>
 #include <QTextCodec>
+#include <QPair>
+#include <QListIterator>
+#include <QMutex>
+#include <QThreadStorage>
 
 //CRT
 #include <cstdlib>
 // Random Support
 ///////////////////////////////////////////////////////////////////////////////
 
+#ifndef _CRT_RAND_S
+#define rand_s(X) (true)
+#endif
+
+//Per-thread init flag
+static QThreadStorage<bool> g_srand_flag;
+
 //Robert Jenkins' 96 bit Mix Function
-static unsigned int mix_function(const unsigned int x, const unsigned int y, const unsigned int z)
+static quint32 mix_function(const quint32 x, const quint32 y, const quint32 z)
 {
-       unsigned int a = x;
-       unsigned int b = y;
-       unsigned int c = z;
+       quint32 a = x;
+       quint32 b = y;
+       quint32 c = z;
        
        a=a-b;  a=a-c;  a=a^(c >> 13);
-       b=b-c;  b=b-a;  b=b^(a << ); 
+       b=b-c;  b=b-a;  b=b^(a <<  8); 
        c=c-a;  c=c-b;  c=c^(b >> 13);
        a=a-b;  a=a-c;  a=a^(c >> 12);
        b=b-c;  b=b-a;  b=b^(a << 16);
-       c=c-a;  c=c-b;  c=c^(b >> );
-       a=a-b;  a=a-c;  a=a^(c >> );
+       c=c-a;  c=c-b;  c=c^(b >>  5);
+       a=a-b;  a=a-c;  a=a^(c >>  3);
        b=b-c;  b=b-a;  b=b^(a << 10);
        c=c-a;  c=c-b;  c=c^(b >> 15);
 
        return a ^ b ^ c;
 }
 
-void MUtils::seed_rand(void)
+static void seed_rand(void)
 {
-       qsrand(mix_function(clock(), time(NULL), _getpid()));
+       fprintf(stderr, "SEED RAND (TID: %u)\n", MUtils::OS::thread_id());
+       QDateTime build(MUtils::Version::lib_build_date(), MUtils::Version::lib_build_time());
+       const quint32 seed = mix_function(MUtils::OS::process_id(), MUtils::OS::thread_id(), build.toMSecsSinceEpoch());
+       qsrand(mix_function(clock(), time(NULL), seed));
 }
 
-quint32 MUtils::next_rand32(void)
+static quint32 rand_fallback(void)
 {
-       quint32 rnd = 0xDEADBEEF;
-
-#ifdef _CRT_RAND_S
-       if(rand_s(&rnd) == 0)
+       Q_ASSERT(RAND_MAX >= 0xFFF);
+       if (!(g_srand_flag.hasLocalData() && g_srand_flag.localData()))
        {
-               return rnd;
+               seed_rand();
+               g_srand_flag.setLocalData(true);
        }
-#endif //_CRT_RAND_S
-
-       for(size_t i = 0; i < sizeof(quint32); i++)
+       quint32 rnd = 0x32288EA3;
+       for (size_t i = 0; i < 3; i++)
        {
-               rnd = (rnd << 8) ^ qrand();
+               rnd = (rnd << 12) ^ qrand();
        }
+       return rnd;
+}
 
+quint32 MUtils::next_rand_u32(void)
+{
+       quint32 rnd;
+       if (rand_s(&rnd))
+       {
+               return rand_fallback();
+       }
        return rnd;
 }
 
-quint64 MUtils::next_rand64(void)
+quint64 MUtils::next_rand_u64(void)
 {
-       return (quint64(next_rand32()) << 32) | quint64(next_rand32());
+       return (quint64(next_rand_u32()) << 32) | quint64(next_rand_u32());
 }
 
-QString MUtils::rand_str(const bool &bLong)
+QString MUtils::next_rand_str(const bool &bLong)
 {
-       if(!bLong)
+       if (bLong)
        {
-               return QString::number(next_rand64(), 16).rightJustified(16, QLatin1Char('0'));
+               return next_rand_str(false) + next_rand_str(false);
+       }
+       else
+       {
+               return QString::number(next_rand_u64(), 16).rightJustified(16, QLatin1Char('0'));
        }
-       return QString("%1%2").arg(rand_str(false), rand_str(false));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// GET TEMP FILE NAME
+// STRING UTILITY FUNCTIONS
+///////////////////////////////////////////////////////////////////////////////
+
+static QScopedPointer<QRegExp> g_str_trim_rx_r;
+static QScopedPointer<QRegExp> g_str_trim_rx_l;
+static QMutex                  g_str_trim_lock;
+
+static QString& trim_helper(QString &str, QScopedPointer<QRegExp> &regex, const char *const pattern)
+{
+       QMutexLocker lock(&g_str_trim_lock);
+       if (regex.isNull())
+       {
+               regex.reset(new QRegExp(QLatin1String(pattern)));
+       }
+       str.remove(*regex.data());
+       return str;
+}
+
+QString& MUtils::trim_right(QString &str)
+{
+       static const char *const TRIM_RIGHT = "\\s+$";
+       return trim_helper(str, g_str_trim_rx_r, TRIM_RIGHT);
+}
+
+QString& MUtils::trim_left(QString &str)
+{
+       static const char *const TRIM_LEFT = "^\\s+";
+       return trim_helper(str, g_str_trim_rx_l, TRIM_LEFT);
+}
+
+QString MUtils::trim_right(const QString &str)
+{
+       QString temp(str);
+       return trim_right(temp);
+}
+
+QString MUtils::trim_left(const QString &str)
+{
+       QString temp(str);
+       return trim_left(temp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GENERATE FILE NAME
 ///////////////////////////////////////////////////////////////////////////////
 
 QString MUtils::make_temp_file(const QString &basePath, const QString &extension, const bool placeholder)
 {
        for(int i = 0; i < 4096; i++)
        {
-               const QString tempFileName = QString("%1/%2.%3").arg(basePath, rand_str(), extension);
+               const QString tempFileName = QString("%1/%2.%3").arg(basePath, next_rand_str(), extension);
                if(!QFileInfo(tempFileName).exists())
                {
                        if(placeholder)
@@ -136,10 +204,36 @@ QString MUtils::make_temp_file(const QString &basePath, const QString &extension
                }
        }
 
-       qWarning("Failed to generate unique temp file name!");
+       qWarning("Failed to generate temp file name!");
        return QString();
 }
 
+QString MUtils::make_unique_file(const QString &basePath, const QString &baseName, const QString &extension, const bool fancy)
+{
+       quint32 n = fancy ? 2 : 0;
+       QString fileName = fancy ? QString("%1/%2.%3").arg(basePath, baseName, extension) : QString();
+       while (fileName.isEmpty() || QFileInfo(fileName).exists())
+       {
+               if (n <= quint32(USHRT_MAX))
+               {
+                       if (fancy)
+                       {
+                               fileName = QString("%1/%2 (%3).%4").arg(basePath, baseName, QString::number(n++), extension);
+                       }
+                       else
+                       {
+                               fileName = QString("%1/%2.%3.%4").arg(basePath, baseName, QString::number(n++, 16).rightJustified(4, QLatin1Char('0')), extension);
+                       }
+               }
+               else
+               {
+                       qWarning("Failed to generate unique file name!");
+                       return QString();
+               }
+       }
+       return fileName;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // COMPUTE PARITY
 ///////////////////////////////////////////////////////////////////////////////
@@ -180,7 +274,7 @@ static QString try_create_subfolder(const QString &baseDir, const QString &postf
 
 static MUtils::Internal::DirLock *try_init_temp_folder(const QString &baseDir)
 {
-       const QString tempPath = try_create_subfolder(baseDir, MUtils::rand_str());
+       const QString tempPath = try_create_subfolder(baseDir, MUtils::next_rand_str());
        if(!tempPath.isEmpty())
        {
                for(int i = 0; i < 32; i++)
@@ -383,7 +477,14 @@ bool MUtils::remove_directory(const QString &folderPath, const bool &recursive)
 // PROCESS UTILS
 ///////////////////////////////////////////////////////////////////////////////
 
-void MUtils::init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir)
+static void prependToPath(QProcessEnvironment &env, const QString &value)
+{
+       const QLatin1String PATH = QLatin1String("PATH");
+       const QString path = env.value(PATH, QString()).trimmed();
+       env.insert(PATH, path.isEmpty() ? value : QString("%1;%2").arg(value, path));
+}
+
+void MUtils::init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir, const QStringList *const extraPaths)
 {
        //Environment variable names
        static const char *const s_envvar_names_temp[] =
@@ -418,8 +519,16 @@ void MUtils::init_process(QProcess &process, const QString &wokringDir, const bo
        }
 
        //Setup PATH variable
-       const QString path = env.value("PATH", QString()).trimmed();
-       env.insert("PATH", path.isEmpty() ? tempDir : QString("%1;%2").arg(tempDir, path));
+       prependToPath(env, tempDir);
+       if (extraPaths && (!extraPaths->isEmpty()))
+       {
+               QListIterator<QString> iter(*extraPaths);
+               iter.toBack();
+               while (iter.hasPrevious())
+               {
+                       prependToPath(env, QDir::toNativeSeparators(iter.previous()));
+               }
+       }
        
        //Setup QPorcess object
        process.setWorkingDirectory(wokringDir);
@@ -451,61 +560,91 @@ void MUtils::natural_string_sort(QStringList &list, const bool bIgnoreCase)
 // CLEAN FILE PATH
 ///////////////////////////////////////////////////////////////////////////////
 
-static const struct
-{
-       const char *const search;
-       const char *const replace;
-}
-CLEAN_FILE_NAME[] =
-{
-       { "\\",  "-"  },
-       { " / ", ", " },
-       { "/",   ","  },
-       { ":",   "-"  },
-       { "*",   "x"  },
-       { "?",   "!"  },
-       { "<",   "["  },
-       { ">",   "]"  },
-       { "|",   "!"  },
-       { "\"",  "'"  },
-       { NULL,  NULL }
-};
-
 QString MUtils::clean_file_name(const QString &name)
 {
-       QRegExp regExp("\"(.+)\"");
-       regExp.setMinimal(true);
+       static const QLatin1Char REPLACEMENT_CHAR('_');
+       static const char FILENAME_ILLEGAL_CHARS[] = "<>:\"/\\|?*";
+       static const char *const FILENAME_RESERVED_NAMES[] =
+       {
+               "CON", "PRN", "AUX", "NUL",
+               "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+               "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", NULL
+       };
 
-       QString str = QString(name).replace(regExp, "``\\1´´").trimmed();
-       for(size_t i = 0; CLEAN_FILE_NAME[i].search; i++) 
+       QString result(name);
+       if (result.contains(QLatin1Char('"')))
        {
-               str.replace(CLEAN_FILE_NAME[i].search, CLEAN_FILE_NAME[i].replace);
+               QRegExp quoted("\"(.+)\"");
+               quoted.setMinimal(true);
+               result.replace(quoted, "``\\1´´");
+       }
+
+       for(QString::Iterator iter = result.begin(); iter != result.end(); iter++)
+       {
+               if (iter->category() == QChar::Other_Control)
+               {
+                       *iter = REPLACEMENT_CHAR;
+               }
+       }
+               
+       for(size_t i = 0; FILENAME_ILLEGAL_CHARS[i]; i++)
+       {
+               result.replace(QLatin1Char(FILENAME_ILLEGAL_CHARS[i]), REPLACEMENT_CHAR);
        }
        
-       while(str.endsWith(QLatin1Char('.')))
+       trim_right(result);
+       while (result.endsWith(QLatin1Char('.')))
        {
-               str.chop(1);
-               str = str.trimmed();
+               result.chop(1);
+               trim_right(result);
+       }
+
+       for (size_t i = 0; FILENAME_RESERVED_NAMES[i]; i++)
+       {
+               const QString reserved = QString::fromLatin1(FILENAME_RESERVED_NAMES[i]);
+               if ((!result.compare(reserved, Qt::CaseInsensitive)) || result.startsWith(reserved + QLatin1Char('.'), Qt::CaseInsensitive))
+               {
+                       result.replace(0, reserved.length(), QString().leftJustified(reserved.length(), REPLACEMENT_CHAR));
+               }
        }
 
-       return str.trimmed();
+       return result;
+}
+
+static QPair<QString,QString> clean_file_path_get_prefix(const QString path)
+{
+       static const char *const PREFIXES[] =
+       {
+               "//?/", "//", "/", NULL
+       };
+       const QString posixPath = QDir::fromNativeSeparators(path.trimmed());
+       for (int i = 0; PREFIXES[i]; i++)
+       {
+               const QString prefix = QString::fromLatin1(PREFIXES[i]);
+               if (posixPath.startsWith(prefix))
+               {
+                       return qMakePair(prefix, posixPath.mid(prefix.length()));
+               }
+       }
+       return qMakePair(QString(), posixPath);
 }
 
 QString MUtils::clean_file_path(const QString &path)
 {
-       const bool root = path.startsWith(QLatin1Char('/')) || path.startsWith(QLatin1Char('\\'));
-       QStringList parts = QDir::fromNativeSeparators(path.trimmed()).split(QLatin1Char('/'), QString::SkipEmptyParts);
+       const QPair<QString, QString> prefix = clean_file_path_get_prefix(path);
 
+       QStringList parts = prefix.second.split(QLatin1Char('/'), QString::SkipEmptyParts);
        for(int i = 0; i < parts.count(); i++)
        {
-               if((i == 0) && (!root) && (parts[i].length() == 2) && parts[i][0].isLetter() && (parts[i][1] == QLatin1Char(':')))
+               if((i == 0) && (parts[i].length() == 2) && parts[i][0].isLetter() && (parts[i][1] == QLatin1Char(':')))
                {
                        continue; //handle case "c:\"
                }
                parts[i] = MUtils::clean_file_name(parts[i]);
        }
 
-       return root ? parts.join(QLatin1String("/")).prepend(QLatin1Char('/')) : parts.join(QLatin1String("/"));
+       const QString cleanPath = parts.join(QLatin1String("/"));
+       return prefix.first.isEmpty() ? cleanPath : prefix.first + cleanPath;
 }
 
 ///////////////////////////////////////////////////////////////////////////////