OSDN Git Service

Overhaul of the temp_folder() function + use scoped pointers for global objects.
[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 #include <QProcess>
33
34 //CRT
35 #include <cstdlib>
36 #include <ctime>
37 #include <process.h>
38
39 ///////////////////////////////////////////////////////////////////////////////
40 // Random Support
41 ///////////////////////////////////////////////////////////////////////////////
42
43 //Robert Jenkins' 96 bit Mix Function
44 static unsigned int mix_function(const unsigned int x, const unsigned int y, const unsigned int z)
45 {
46         unsigned int a = x;
47         unsigned int b = y;
48         unsigned int c = z;
49         
50         a=a-b;  a=a-c;  a=a^(c >> 13);
51         b=b-c;  b=b-a;  b=b^(a << 8 ); 
52         c=c-a;  c=c-b;  c=c^(b >> 13);
53         a=a-b;  a=a-c;  a=a^(c >> 12);
54         b=b-c;  b=b-a;  b=b^(a << 16);
55         c=c-a;  c=c-b;  c=c^(b >> 5 );
56         a=a-b;  a=a-c;  a=a^(c >> 3 );
57         b=b-c;  b=b-a;  b=b^(a << 10);
58         c=c-a;  c=c-b;  c=c^(b >> 15);
59
60         return a ^ b ^ c;
61 }
62
63 void MUtils::seed_rand(void)
64 {
65         qsrand(mix_function(clock(), time(NULL), _getpid()));
66 }
67
68 quint32 MUtils::next_rand32(void)
69 {
70         quint32 rnd = 0xDEADBEEF;
71
72 #ifdef _CRT_RAND_S
73         if(rand_s(&rnd) == 0)
74         {
75                 return rnd;
76         }
77 #endif //_CRT_RAND_S
78
79         for(size_t i = 0; i < sizeof(quint32); i++)
80         {
81                 rnd = (rnd << 8) ^ qrand();
82         }
83
84         return rnd;
85 }
86
87 quint64 MUtils::next_rand64(void)
88 {
89         return (quint64(next_rand32()) << 32) | quint64(next_rand32());
90 }
91
92 QString MUtils::rand_str(const bool &bLong)
93 {
94         if(!bLong)
95         {
96                 return QString::number(next_rand64(), 16).rightJustified(16, QLatin1Char('0'));
97         }
98         return QString("%1%2").arg(rand_str(false), rand_str(false));
99 }
100
101 ///////////////////////////////////////////////////////////////////////////////
102 // TEMP FOLDER
103 ///////////////////////////////////////////////////////////////////////////////
104
105 static QScopedPointer<QFile>   g_temp_folder_file;
106 static QScopedPointer<QString> g_temp_folder_path;
107 static QReadWriteLock          g_temp_folder_lock;
108
109 static QString try_create_subfolder(const QString &baseDir, const QString &postfix)
110 {
111         const QString baseDirPath = QDir(baseDir).absolutePath();
112         for(int i = 0; i < 32; i++)
113         {
114                 QDir directory(baseDirPath);
115                 if(directory.mkpath(postfix) && directory.cd(postfix))
116                 {
117                         return directory.canonicalPath();
118                 }
119         }
120         return QString();
121 }
122
123 static QString try_init_temp_folder(const QString &baseDir)
124 {
125         static const char *TEST_DATA = "Lorem ipsum dolor sit amet, consectetur, adipisci velit!";
126         
127         QString tempPath = try_create_subfolder(baseDir, MUtils::rand_str());
128         if(!tempPath.isEmpty())
129         {
130                 const QByteArray testData = QByteArray(TEST_DATA);
131                 for(int i = 0; i < 32; i++)
132                 {
133                         g_temp_folder_file.reset(new QFile(QString("%1/~%2.lck").arg(tempPath, MUtils::rand_str())));
134                         if(g_temp_folder_file->open(QIODevice::ReadWrite | QIODevice::Truncate))
135                         {
136                                 if(g_temp_folder_file->write(testData) >= testData.size())
137                                 {
138                                         return tempPath;
139                                 }
140                                 g_temp_folder_file->remove();
141                         }
142                 }
143         }
144
145         return QString();
146 }
147
148 const QString &MUtils::temp_folder(void)
149 {
150         QReadLocker readLock(&g_temp_folder_lock);
151
152         //Already initialized?
153         if((!g_temp_folder_path.isNull()) && (!g_temp_folder_path->isEmpty()))
154         {
155                 return (*g_temp_folder_path.data());
156         }
157
158         //Obtain the write lock to initilaize
159         readLock.unlock();
160         QWriteLocker writeLock(&g_temp_folder_lock);
161         
162         //Still uninitilaized?
163         if((!g_temp_folder_path.isNull()) && (!g_temp_folder_path->isEmpty()))
164         {
165                 return (*g_temp_folder_path.data());
166         }
167
168         //Try the %TMP% or %TEMP% directory first
169         QString tempPath = try_init_temp_folder(QDir::tempPath());
170         if(!tempPath.isEmpty())
171         {
172                 g_temp_folder_path.reset(new QString(tempPath));
173                 return (*g_temp_folder_path.data());
174         }
175
176         qWarning("%%TEMP%% directory not found -> trying fallback mode now!");
177         static const OS::known_folder_t FOLDER_ID[2] = { OS::FOLDER_LOCALAPPDATA, OS::FOLDER_SYSTROOT_DIR };
178         for(size_t id = 0; id < 2; id++)
179         {
180                 const QString &knownFolder = OS::known_folder(FOLDER_ID[id]);
181                 if(!knownFolder.isEmpty())
182                 {
183                         const QString tempRoot = try_create_subfolder(knownFolder, QLatin1String("TEMP"));
184                         if(!tempRoot.isEmpty())
185                         {
186                                 tempPath = try_init_temp_folder(tempRoot);
187                                 if(!tempPath.isEmpty())
188                                 {
189                                         g_temp_folder_path.reset(new QString(tempPath));
190                                         return (*g_temp_folder_path.data());
191                                 }
192                         }
193                 }
194         }
195
196         qFatal("Temporary directory could not be initialized !!!");
197         return (*((QString*)NULL));
198 }
199
200 ///////////////////////////////////////////////////////////////////////////////
201 // PROCESS UTILS
202 ///////////////////////////////////////////////////////////////////////////////
203
204 void MUtils::init_process(QProcess &process, const QString &wokringDir, const bool bReplaceTempDir)
205 {
206         //Environment variable names
207         static const char *const s_envvar_names_temp[] =
208         {
209                 "TEMP", "TMP", "TMPDIR", "HOME", "USERPROFILE", "HOMEPATH", NULL
210         };
211         static const char *const s_envvar_names_remove[] =
212         {
213                 "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
214         };
215
216         //Initialize environment
217         QProcessEnvironment env = process.processEnvironment();
218         if(env.isEmpty()) env = QProcessEnvironment::systemEnvironment();
219
220         //Clean a number of enviroment variables that might affect our tools
221         for(size_t i = 0; s_envvar_names_remove[i]; i++)
222         {
223                 env.remove(QString::fromLatin1(s_envvar_names_remove[i]));
224                 env.remove(QString::fromLatin1(s_envvar_names_remove[i]).toLower());
225         }
226
227         const QString tempDir = QDir::toNativeSeparators(temp_folder());
228
229         //Replace TEMP directory in environment
230         if(bReplaceTempDir)
231         {
232                 for(size_t i = 0; s_envvar_names_temp[i]; i++)
233                 {
234                         env.insert(s_envvar_names_temp[i], tempDir);
235                 }
236         }
237
238         //Setup PATH variable
239         const QString path = env.value("PATH", QString()).trimmed();
240         env.insert("PATH", path.isEmpty() ? tempDir : QString("%1;%2").arg(tempDir, path));
241         
242         //Setup QPorcess object
243         process.setWorkingDirectory(wokringDir);
244         process.setProcessChannelMode(QProcess::MergedChannels);
245         process.setReadChannel(QProcess::StandardOutput);
246         process.setProcessEnvironment(env);
247 }
248
249 ///////////////////////////////////////////////////////////////////////////////
250 // LIB VERSION
251 ///////////////////////////////////////////////////////////////////////////////
252
253 const char* MUtils::mutils_build_date(void)
254 {
255         static const char *const BUILD_DATE = __DATE__;
256         return BUILD_DATE;
257 }
258
259 const char* MUtils::mutils_build_time(void)
260 {
261         static const char *const BUILD_TIME = __TIME__;
262         return BUILD_TIME;
263 }
264
265 ///////////////////////////////////////////////////////////////////////////////
266 // SELF-TEST
267 ///////////////////////////////////////////////////////////////////////////////
268
269 int MUtils::Internal::selfTest(const char *const date, const bool debug)
270 {
271         if(strcmp(date, __DATE__) || (MUTILS_DEBUG != debug))
272         {
273                 MUtils::OS::system_message_err(L"MUtils", L"FATAL ERROR: MUtils library version mismatch detected!");
274                 abort();
275         }
276         return 0;
277 }