OSDN Git Service

Implemented known_folder() function.
[mutilities/MUtilities.git] / src / Global.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2014 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 #include <MUtils/Global.h>
27 #include <MUtils/OSSupport.h>
28
29 //Qt
30 #include <QDir>
31 #include <QReadWriteLock>
32
33 //CRT
34 #include <cstdlib>
35 #include <ctime>
36 #include <process.h>
37
38 ///////////////////////////////////////////////////////////////////////////////
39 // Random Support
40 ///////////////////////////////////////////////////////////////////////////////
41
42 //Robert Jenkins' 96 bit Mix Function
43 static unsigned int mix_function(const unsigned int x, const unsigned int y, const unsigned int z)
44 {
45         unsigned int a = x;
46         unsigned int b = y;
47         unsigned int c = z;
48         
49         a=a-b;  a=a-c;  a=a^(c >> 13);
50         b=b-c;  b=b-a;  b=b^(a << 8 ); 
51         c=c-a;  c=c-b;  c=c^(b >> 13);
52         a=a-b;  a=a-c;  a=a^(c >> 12);
53         b=b-c;  b=b-a;  b=b^(a << 16);
54         c=c-a;  c=c-b;  c=c^(b >> 5 );
55         a=a-b;  a=a-c;  a=a^(c >> 3 );
56         b=b-c;  b=b-a;  b=b^(a << 10);
57         c=c-a;  c=c-b;  c=c^(b >> 15);
58
59         return a ^ b ^ c;
60 }
61
62 void MUtils::seed_rand(void)
63 {
64         qsrand(mix_function(clock(), time(NULL), _getpid()));
65 }
66
67 quint32 MUtils::next_rand32(void)
68 {
69         quint32 rnd = 0xDEADBEEF;
70
71 #ifdef _CRT_RAND_S
72         if(rand_s(&rnd) == 0)
73         {
74                 return rnd;
75         }
76 #endif //_CRT_RAND_S
77
78         for(size_t i = 0; i < sizeof(quint32); i++)
79         {
80                 rnd = (rnd << 8) ^ qrand();
81         }
82
83         return rnd;
84 }
85
86 quint64 MUtils::next_rand64(void)
87 {
88         return (quint64(next_rand32()) << 32) | quint64(next_rand32());
89 }
90
91 QString MUtils::rand_str(const bool &bLong)
92 {
93         if(!bLong)
94         {
95                 return QString::number(next_rand64(), 16).rightJustified(16, QLatin1Char('0'));
96         }
97         return QString("%1%2").arg(rand_str(false), rand_str(false));
98 }
99
100 ///////////////////////////////////////////////////////////////////////////////
101 // TEMP FOLDER
102 ///////////////////////////////////////////////////////////////////////////////
103
104 static QReadWriteLock g_temp_folder_lock;
105 static QFile *g_temp_folder_file;
106 static QString *g_temp_folder_path = NULL;
107
108 #define INIT_TEMP_FOLDER_RAND(OUT_PTR, FILE_PTR, BASE_DIR) do \
109 { \
110         for(int _i = 0; _i < 128; _i++) \
111         { \
112                 const QString _randDir = QString("%1/%2").arg((BASE_DIR), rand_str()); \
113                 if(!QDir(_randDir).exists()) \
114                 { \
115                         *(OUT_PTR) = try_init_folder(_randDir, (FILE_PTR)); \
116                         if(!(OUT_PTR)->isEmpty()) break; \
117                 } \
118         } \
119 } \
120 while(0)
121
122 static QString try_init_folder(const QString &folderPath, QFile *&lockFile)
123 {
124         static const char *TEST_DATA = "Lorem ipsum dolor sit amet, consectetur, adipisci velit!";
125         
126         bool success = false;
127
128         const QFileInfo folderInfo(folderPath);
129         const QDir folderDir(folderInfo.absoluteFilePath());
130
131         //Remove existing lock file
132         if(lockFile)
133         {
134                 lockFile->remove();
135                 MUTILS_DELETE(lockFile);
136         }
137
138         //Create folder, if it does *not* exist yet
139         if(!folderDir.exists())
140         {
141                 for(int i = 0; i < 16; i++)
142                 {
143                         if(folderDir.mkpath(".")) break;
144                 }
145         }
146
147         //Make sure folder exists now *and* is writable
148         if(folderDir.exists())
149         {
150                 const QByteArray testData = QByteArray(TEST_DATA);
151                 for(int i = 0; i < 32; i++)
152                 {
153                         lockFile = new QFile(folderDir.absoluteFilePath(QString("~%1.tmp").arg(MUtils::rand_str())));
154                         if(lockFile->open(QIODevice::ReadWrite | QIODevice::Truncate))
155                         {
156                                 if(lockFile->write(testData) >= testData.size())
157                                 {
158                                         success = true;
159                                         break;
160                                 }
161                                 lockFile->remove();
162                                 MUTILS_DELETE(lockFile);
163                         }
164                 }
165         }
166
167         return (success ? folderDir.canonicalPath() : QString());
168 }
169
170 const QString &MUtils::temp_folder(void)
171 {
172         QReadLocker readLock(&g_temp_folder_lock);
173
174         //Already initialized?
175         if(g_temp_folder_path && (!g_temp_folder_path->isEmpty()))
176         {
177                 return (*g_temp_folder_path);
178         }
179
180         //Obtain the write lock to initilaize
181         readLock.unlock();
182         QWriteLocker writeLock(&g_temp_folder_lock);
183         
184         //Still uninitilaized?
185         if(g_temp_folder_path && (!g_temp_folder_path->isEmpty()))
186         {
187                 return (*g_temp_folder_path);
188         }
189
190         //Create the string, if not done yet
191         if(!g_temp_folder_path)
192         {
193                 g_temp_folder_path = new QString();
194         }
195         
196         g_temp_folder_path->clear();
197
198         //Try the %TMP% or %TEMP% directory first
199         QString tempPath = try_init_folder(QDir::temp().absolutePath(), g_temp_folder_file);
200         if(!tempPath.isEmpty())
201         {
202                 INIT_TEMP_FOLDER_RAND(g_temp_folder_path, g_temp_folder_file, tempPath);
203         }
204
205         //Otherwise create TEMP folder in %LOCALAPPDATA% or %SYSTEMROOT%
206         if(g_temp_folder_path->isEmpty())
207         {
208                 qWarning("%%TEMP%% directory not found -> trying fallback mode now!");
209                 static const OS::known_folder_t folderId[2] = { OS::FOLDER_LOCALAPPDATA, OS::FOLDER_SYSTROOT_DIR };
210                 for(size_t id = 0; (g_temp_folder_path->isEmpty() && (id < 2)); id++)
211                 {
212                         const QString &knownFolder = OS::known_folder(folderId[id]);
213                         if(!knownFolder.isEmpty())
214                         {
215                                 tempPath = try_init_folder(QString("%1/Temp").arg(knownFolder), g_temp_folder_file);
216                                 if(!tempPath.isEmpty())
217                                 {
218                                         INIT_TEMP_FOLDER_RAND(g_temp_folder_path, g_temp_folder_file, tempPath);
219                                 }
220                         }
221                 }
222         }
223
224         //Failed to create TEMP folder?
225         if(g_temp_folder_path->isEmpty())
226         {
227                 qFatal("Temporary directory could not be initialized !!!");
228         }
229         
230         return (*g_temp_folder_path);
231 }