Doxygen
Loading...
Searching...
No Matches
portable.cpp
Go to the documentation of this file.
1#include "portable.h"
2#include "qcstring.h"
3
4#include <stdlib.h>
5#include <stdio.h>
6#include <chrono>
7#include <thread>
8#include <mutex>
9#include <map>
10
11#if defined(_WIN32) && !defined(__CYGWIN__)
12#undef UNICODE
13#define _WIN32_DCOM
14#include <windows.h>
15#else
16#include <unistd.h>
17#include <sys/types.h>
18#include <sys/wait.h>
19#include <errno.h>
20extern char **environ;
21#endif
22
23#include <assert.h>
24#include <ctype.h>
25#include <string>
26
27#include "fileinfo.h"
28#include "message.h"
29
30#include "util.h"
31#include "dir.h"
32#ifndef NODEBUG
33#include "debug.h"
34#endif
35
36#if !defined(_WIN32) || defined(__CYGWIN__)
37static bool environmentLoaded = false;
38static std::map<std::string,std::string> proc_env = std::map<std::string,std::string>();
39#endif
40
41
42//---------------------------------------------------------------------------------------------------------
43
44/*! Helper class to keep time interval per thread */
46{
47 public:
48 static SysTimeKeeper &instance();
49 //! start a timer for this thread
50 void start()
51 {
52 std::lock_guard<std::mutex> lock(m_mutex);
53 m_startTimes[std::this_thread::get_id()] = std::chrono::steady_clock::now();
54 }
55 //! ends a timer for this thread, accumulate time difference since start
56 void stop()
57 {
58 std::lock_guard<std::mutex> lock(m_mutex);
59 std::chrono::steady_clock::time_point endTime = std::chrono::steady_clock::now();
60 auto it = m_startTimes.find(std::this_thread::get_id());
61 if (it == m_startTimes.end())
62 {
63 err("SysTimeKeeper stop() called without matching start()\n");
64 return;
65 }
66 double timeSpent = static_cast<double>(std::chrono::duration_cast<
67 std::chrono::microseconds>(endTime - it->second).count())/1000000.0;
68 //printf("timeSpent on thread %zu: %.4f seconds\n",std::hash<std::thread::id>{}(std::this_thread::get_id()),timeSpent);
69 m_elapsedTime += timeSpent;
70 }
71
72 double elapsedTime() const { return m_elapsedTime; }
73
74 private:
75 struct TimeData
76 {
77 std::chrono::steady_clock::time_point startTime;
78 };
79 std::map<std::thread::id,std::chrono::steady_clock::time_point> m_startTimes;
80 double m_elapsedTime = 0;
81 std::mutex m_mutex;
82};
83
85{
86 static SysTimeKeeper theInstance;
87 return theInstance;
88}
89
96
101
102//---------------------------------------------------------------------------------------------------------
103
104
105int Portable::system(const QCString &command,const QCString &args,bool commandHasConsole)
106{
107 if (command.isEmpty()) return 1;
108 AutoTimeKeeper timeKeeper;
109
110#if defined(_WIN32) && !defined(__CYGWIN__)
111 QCString commandCorrectedPath = substitute(command,'/','\\');
112 QCString fullCmd=commandCorrectedPath;
113#else
114 QCString fullCmd=command;
115#endif
116 fullCmd=fullCmd.stripWhiteSpace();
117 if (fullCmd.at(0)!='"' && fullCmd.find(' ')!=-1)
118 {
119 // add quotes around command as it contains spaces and is not quoted already
120 fullCmd="\""+fullCmd+"\"";
121 }
122 fullCmd += " ";
123 fullCmd += args;
124#ifndef NODEBUG
125 Debug::print(Debug::ExtCmd,0,"Executing external command `{}`\n",fullCmd);
126#endif
127
128#if !defined(_WIN32) || defined(__CYGWIN__)
129 (void)commandHasConsole;
130 /*! taken from the system() manpage on my Linux box */
131 int pid,status=0;
132
133#ifdef _OS_SOLARIS // for Solaris we use vfork since it is more memory efficient
134
135 // on Solaris fork() duplicates the memory usage
136 // so we use vfork instead
137
138 // spawn shell
139 if ((pid=vfork())<0)
140 {
141 status=-1;
142 }
143 else if (pid==0)
144 {
145 execl("/bin/sh","sh","-c",fullCmd.data(),(char*)0);
146 _exit(127);
147 }
148 else
149 {
150 while (waitpid(pid,&status,0 )<0)
151 {
152 if (errno!=EINTR)
153 {
154 status=-1;
155 break;
156 }
157 }
158 }
159 return status;
160
161#else // Other Unices just use fork
162
163 pid = fork();
164 if (pid==-1)
165 {
166 perror("fork error");
167 return -1;
168 }
169 if (pid==0)
170 {
171 const char * const argv[4] = { "sh", "-c", fullCmd.data(), 0 };
172 execve("/bin/sh",const_cast<char * const*>(argv),environ);
173 exit(127);
174 }
175 for (;;)
176 {
177 if (waitpid(pid,&status,0)==-1)
178 {
179 if (errno!=EINTR) return -1;
180 }
181 else
182 {
183 if (WIFEXITED(status))
184 {
185 return WEXITSTATUS(status);
186 }
187 else
188 {
189 return status;
190 }
191 }
192 }
193#endif // !_OS_SOLARIS
194
195#else // Win32 specific
196 if (commandHasConsole)
197 {
198 return ::system(fullCmd.data());
199 }
200 else
201 {
202 uint16_t* fullCmdW = nullptr;
203 recodeUtf8StringToW(fullCmd, &fullCmdW);
204
205 STARTUPINFOW sStartupInfo;
206 std::memset(&sStartupInfo, 0, sizeof(sStartupInfo));
207 sStartupInfo.cb = sizeof(sStartupInfo);
208 sStartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
209 sStartupInfo.wShowWindow = SW_HIDE;
210
211 PROCESS_INFORMATION sProcessInfo;
212 std::memset(&sProcessInfo, 0, sizeof(sProcessInfo));
213
214 if (!CreateProcessW(
215 nullptr, // No module name (use command line)
216 reinterpret_cast<wchar_t*>(fullCmdW), // Command line, can be mutated by CreateProcessW
217 nullptr, // Process handle not inheritable
218 nullptr, // Thread handle not inheritable
219 FALSE, // Set handle inheritance to FALSE
220 CREATE_NO_WINDOW,
221 nullptr, // Use parent's environment block
222 nullptr, // Use parent's starting directory
223 &sStartupInfo,
224 &sProcessInfo
225 ))
226 {
227 delete[] fullCmdW;
228 return -1;
229 }
230 else if (sProcessInfo.hProcess) /* executable was launched, wait for it to finish */
231 {
232 WaitForSingleObject(sProcessInfo.hProcess,INFINITE);
233 /* get process exit code */
234 DWORD exitCode;
235 bool retval = GetExitCodeProcess(sProcessInfo.hProcess,&exitCode);
236 CloseHandle(sProcessInfo.hProcess);
237 CloseHandle(sProcessInfo.hThread);
238 delete[] fullCmdW;
239 if (!retval) return -1;
240 return exitCode;
241 }
242 }
243#endif
244 return 1; // we should never get here
245
246}
247
249{
250 uint32_t pid;
251#if !defined(_WIN32) || defined(__CYGWIN__)
252 pid = static_cast<uint32_t>(getpid());
253#else
254 pid = static_cast<uint32_t>(GetCurrentProcessId());
255#endif
256 return pid;
257}
258
259#if !defined(_WIN32) || defined(__CYGWIN__)
261{
262 if(environ != nullptr)
263 {
264 unsigned int i = 0;
265 char* current = environ[i];
266
267 while(current != nullptr) // parse all strings contained by environ til the last element (nullptr)
268 {
269 std::string env_var(current); // load current environment variable string
270 size_t pos = env_var.find("=");
271 if(pos != std::string::npos) // only parse the variable, if it is a valid environment variable...
272 { // ...which has to contain an equal sign as delimiter by definition
273 std::string name = env_var.substr(0,pos); // the string til the equal sign contains the name
274 std::string value = env_var.substr(pos + 1); // the string from the equal sign contains the value
275 proc_env[name] = std::move(value); // save the value by the name as its key in the classes map
276 }
277 i++;
278 current = environ[i];
279 }
280 }
281
282 environmentLoaded = true;
283}
284#endif
285
286void Portable::setenv(const QCString &name,const QCString &value)
287{
288#if defined(_WIN32) && !defined(__CYGWIN__)
289 SetEnvironmentVariable(name.data(),!value.isEmpty() ? value.data() : "");
290#else
291 if(!environmentLoaded) // if the environment variables are not loaded already...
292 { // ...call loadEnvironment to store them in class
294 }
295
296 proc_env[name.str()] = value.str(); // create or replace existing value
297 ::setenv(name.data(),value.data(),1);
298#endif
299}
300
301void Portable::unsetenv(const QCString &variable)
302{
303#if defined(_WIN32) && !defined(__CYGWIN__)
304 SetEnvironmentVariable(variable.data(),nullptr);
305#else
306 /* Some systems don't have unsetenv(), so we do it ourselves */
307 if (variable.isEmpty() || variable.find('=')!=-1)
308 {
309 return; // not properly formatted
310 }
311
312 auto it = proc_env.find(variable.str());
313 if (it != proc_env.end())
314 {
315 proc_env.erase(it);
316 ::unsetenv(variable.data());
317 }
318#endif
319}
320
322{
323#if defined(_WIN32) && !defined(__CYGWIN__)
324 #define ENV_BUFSIZE 32768
325 LPTSTR pszVal = (LPTSTR) malloc(ENV_BUFSIZE*sizeof(TCHAR));
326 if (GetEnvironmentVariable(variable.data(),pszVal,ENV_BUFSIZE) == 0) return "";
327 QCString out;
328 out = pszVal;
329 free(pszVal);
330 return out;
331 #undef ENV_BUFSIZE
332#else
333 if(!environmentLoaded) // if the environment variables are not loaded already...
334 { // ...call loadEnvironment to store them in class
336 }
337
338 if (proc_env.find(variable.str()) != proc_env.end())
339 {
340 return QCString(proc_env[variable.str()]);
341 }
342 else
343 {
344 return QCString();
345 }
346#endif
347}
348
349FILE *Portable::fopen(const QCString &fileName,const QCString &mode)
350{
351#if defined(_WIN32) && !defined(__CYGWIN__)
352 uint16_t *fn = nullptr;
353 size_t fn_len = recodeUtf8StringToW(fileName,&fn);
354 uint16_t *m = nullptr;
355 size_t m_len = recodeUtf8StringToW(mode,&m);
356 FILE *result = nullptr;
357 if (fn_len!=(size_t)-1 && m_len!=(size_t)-1)
358 {
359 result = _wfopen((wchar_t*)fn,(wchar_t*)m);
360 }
361 delete[] fn;
362 delete[] m;
363 return result;
364#else
365 return ::fopen(fileName.data(),mode.data());
366#endif
367}
368
370{
371 return ::fclose(f);
372}
373
375{
376#if defined(_WIN32) && !defined(__CYGWIN__)
377 return "\\";
378#else
379 return "/";
380#endif
381}
382
384{
385#if defined(_WIN32) && !defined(__CYGWIN__)
386 return ";";
387#else
388 return ":";
389#endif
390}
391
392static bool ExistsOnPath(const QCString &fileName)
393{
394 FileInfo fi1(fileName.str());
395 if (fi1.exists()) return true;
396
397 QCString paths = Portable::getenv("PATH");
398 char listSep = Portable::pathListSeparator()[0];
399 char pathSep = Portable::pathSeparator()[0];
400 int strt = 0;
401 int idx;
402 while ((idx = paths.find(listSep,strt)) != -1)
403 {
404 QCString locFile(paths.mid(strt,idx-strt));
405 locFile += pathSep;
406 locFile += fileName;
407 FileInfo fi(locFile.str());
408 if (fi.exists()) return true;
409 strt = idx + 1;
410 }
411 // to be sure the last path component is checked as well
412 QCString locFile(paths.mid(strt));
413 if (!locFile.isEmpty())
414 {
415 locFile += pathSep;
416 locFile += fileName;
417 FileInfo fi(locFile.str());
418 if (fi.exists()) return true;
419 }
420 return false;
421}
422
424{
425#if defined(_WIN32) && !defined(__CYGWIN__)
426 const char *extensions[] = {".bat",".com",".exe"};
427 for (int i = 0; i < sizeof(extensions) / sizeof(*extensions); i++)
428 {
429 if (ExistsOnPath(fileName + extensions[i])) return true;
430 }
431 return false;
432#else
433 return ExistsOnPath(fileName);
434#endif
435}
436
438{
439#if defined(_WIN32) && !defined(__CYGWIN__)
440 static const char *gsexe = nullptr;
441 if (!gsexe)
442 {
443 const char *gsExec[] = {"gswin32c.exe","gswin64c.exe"};
444 for (int i = 0; i < sizeof(gsExec) / sizeof(*gsExec); i++)
445 {
446 if (ExistsOnPath(gsExec[i]))
447 {
448 gsexe = gsExec[i];
449 return gsexe;
450 }
451 }
452 gsexe = gsExec[0];
453 return gsexe;
454 }
455 return gsexe;
456#else
457 return "gs";
458#endif
459}
460
462{
463#if defined(_WIN32) && !defined(__CYGWIN__)
464 return ".exe";
465#else
466 return "";
467#endif
468}
469
471{
472#if defined(_WIN32) || defined(macintosh) || defined(__MACOSX__) || defined(__APPLE__) || defined(__CYGWIN__)
473 return FALSE;
474#else
475 return TRUE;
476#endif
477}
478
479FILE * Portable::popen(const QCString &name,const QCString &type)
480{
481 #if defined(_MSC_VER) || defined(__BORLANDC__)
482 return ::_popen(name.data(),type.data());
483 #else
484 return ::popen(name.data(),type.data());
485 #endif
486}
487
488int Portable::pclose(FILE *stream)
489{
490 #if defined(_MSC_VER) || defined(__BORLANDC__)
491 return ::_pclose(stream);
492 #else
493 return ::pclose(stream);
494 #endif
495}
496
498{
499 const char *fn = fileName.data();
500# ifdef _WIN32
501 if (fileName.length()>1 && isalpha(fileName[0]) && fileName[1]==':') fn+=2;
502# endif
503 char const fst = fn[0];
504 if (fst == '/') return true;
505# ifdef _WIN32
506 if (fst == '\\') return true;
507# endif
508 return false;
509}
510
511/**
512 * Correct a possible wrong PATH variable
513 *
514 * This routine was inspired by the cause for bug 766059 was that in the Windows path there were forward slashes.
515 */
516void Portable::correctPath(const StringVector &extraPaths)
517{
518 QCString p = Portable::getenv("PATH");
519 bool first=true;
520 QCString result;
521#if defined(_WIN32) && !defined(__CYGWIN__)
522 for (const auto &path : extraPaths)
523 {
524 if (!first) result+=';';
525 first=false;
526 result += substitute(path,"/","\\");
527 }
528 if (!result.isEmpty() && !p.isEmpty()) result+=';';
529 result += substitute(p,"/","\\");
530#else
531 for (const auto &path : extraPaths)
532 {
533 if (!first) result+=':';
534 first=false;
535 result += path;
536 }
537 if (!result.isEmpty() && !p.isEmpty()) result+=':';
538 result += p;
539#endif
540 if (result!=p) Portable::setenv("PATH",result.data());
541 //printf("settingPath(%s) #extraPaths=%zu\n",Portable::getenv("PATH").data(),extraPaths.size());
542}
543
544void Portable::unlink(const QCString &fileName)
545{
546#if defined(_WIN32) && !defined(__CYGWIN__)
547 _unlink(fileName.data());
548#else
549 ::unlink(fileName.data());
550#endif
551}
552
554{
555#if defined(_WIN32) && !defined(__CYGWIN__)
556 long length = 0;
557 TCHAR* buffer = nullptr;
558 // First obtain the size needed by passing nullptr and 0.
559 length = GetShortPathName(Dir::currentDirPath().c_str(), nullptr, 0);
560 // Dynamically allocate the correct size
561 // (terminating null char was included in length)
562 buffer = new TCHAR[length];
563 // Now simply call again using same (long) path.
564 length = GetShortPathName(Dir::currentDirPath().c_str(), buffer, length);
565 // Set the correct directory (short name)
566 Dir::setCurrent(buffer);
567 delete [] buffer;
568#endif
569}
570
571/* Return the first occurrence of NEEDLE in HAYSTACK. */
572static const char * portable_memmem (const char *haystack, size_t haystack_len,
573 const char *needle, size_t needle_len)
574{
575 const char *const last_possible = haystack + haystack_len - needle_len;
576
577 if (needle_len == 0)
578 // The first occurrence of the empty string should to occur at the beginning of the string.
579 {
580 return haystack;
581 }
582
583 // Sanity check
584 if (haystack_len < needle_len)
585 {
586 return nullptr;
587 }
588
589 for (const char *begin = haystack; begin <= last_possible; ++begin)
590 {
591 if (begin[0] == needle[0] && !memcmp(&begin[1], needle + 1, needle_len - 1))
592 {
593 return begin;
594 }
595 }
596
597 return nullptr;
598}
599
600const char *Portable::strnstr(const char *haystack, const char *needle, size_t haystack_len)
601{
602 size_t needle_len = strnlen(needle, haystack_len);
603 if (needle_len < haystack_len || !needle[needle_len])
604 {
605 const char *x = portable_memmem(haystack, haystack_len, needle, needle_len);
606 if (x && !memchr(haystack, 0, x - haystack))
607 {
608 return x;
609 }
610 }
611 return nullptr;
612}
613
614const char *Portable::devNull()
615{
616#if defined(_WIN32) && !defined(__CYGWIN__)
617 return "NUL";
618#else
619 return "/dev/null";
620#endif
621}
622
623size_t Portable::recodeUtf8StringToW(const QCString &inputStr,uint16_t **outBuf)
624{
625 if (inputStr.isEmpty() || outBuf==nullptr) return 0; // empty input or invalid output
626 void *handle = portable_iconv_open("UTF-16LE","UTF-8");
627 if (handle==reinterpret_cast<void *>(-1)) return 0; // invalid encoding
628 size_t len = inputStr.length();
629 uint16_t *buf = new uint16_t[len+1];
630 *outBuf = buf;
631 size_t inRemains = len;
632 size_t outRemains = len*sizeof(uint16_t)+2; // chars + \0
633 const char *p = inputStr.data();
634 portable_iconv(handle,&p,&inRemains,reinterpret_cast<char **>(&buf),&outRemains);
635 *buf=0;
636 portable_iconv_close(handle);
637 return len;
638}
639
640//----------------------------------------------------------------------------------------
641// We need to do this part last as including filesystem.hpp earlier
642// causes the code above to fail to compile on Windows.
643
644#include "filesystem.hpp"
645
646namespace fs = ghc::filesystem;
647
648std::ofstream Portable::openOutputStream(const QCString &fileName,bool append)
649{
650 std::ios_base::openmode mode = std::ofstream::out | std::ofstream::binary;
651 if (append) mode |= std::ofstream::app;
652#if defined(__clang__) && defined(__MINGW32__)
653 return std::ofstream(fs::path(fileName.str()).wstring(), mode);
654#else
655 return std::ofstream(fs::path(fileName.str()), mode);
656#endif
657}
658
659std::ifstream Portable::openInputStream(const QCString &fileName,bool binary, bool openAtEnd)
660{
661 std::ios_base::openmode mode = std::ifstream::in | std::ifstream::binary;
662 if (binary) mode |= std::ios::binary;
663 if (openAtEnd) mode |= std::ios::ate;
664#if defined(__clang__) && defined(__MINGW32__)
665 return std::ifstream(fs::path(fileName.str()).wstring(), mode);
666#else
667 return std::ifstream(fs::path(fileName.str()), mode);
668#endif
669}
670
@ ExtCmd
Definition debug.h:36
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:76
static std::string currentDirPath()
Definition dir.cpp:342
static bool setCurrent(const std::string &path)
Definition dir.cpp:350
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
This is an alternative implementation of QCString.
Definition qcstring.h:101
int find(char c, int index=0, bool cs=TRUE) const
Definition qcstring.cpp:43
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
QCString stripWhiteSpace() const
returns a copy of this string with leading and trailing whitespace removed
Definition qcstring.h:260
const std::string & str() const
Definition qcstring.h:552
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
std::mutex m_mutex
Definition portable.cpp:81
double m_elapsedTime
Definition portable.cpp:80
void stop()
ends a timer for this thread, accumulate time difference since start
Definition portable.cpp:56
void start()
start a timer for this thread
Definition portable.cpp:50
double elapsedTime() const
Definition portable.cpp:72
static SysTimeKeeper & instance()
Definition portable.cpp:84
std::map< std::thread::id, std::chrono::steady_clock::time_point > m_startTimes
Definition portable.cpp:79
std::vector< std::string > StringVector
Definition containers.h:33
DirIterator begin(DirIterator it) noexcept
Definition dir.cpp:170
#define err(fmt,...)
Definition message.h:127
std::ifstream openInputStream(const QCString &name, bool binary=false, bool openAtEnd=false)
Definition portable.cpp:659
void correctPath(const StringVector &list)
Correct a possible wrong PATH variable.
Definition portable.cpp:516
bool isAbsolutePath(const QCString &fileName)
Definition portable.cpp:497
QCString pathSeparator()
Definition portable.cpp:374
FILE * popen(const QCString &name, const QCString &type)
Definition portable.cpp:479
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:648
double getSysElapsedTime()
Definition portable.cpp:97
QCString pathListSeparator()
Definition portable.cpp:383
bool checkForExecutable(const QCString &fileName)
Definition portable.cpp:423
void unlink(const QCString &fileName)
Definition portable.cpp:544
const char * ghostScriptCommand()
Definition portable.cpp:437
FILE * fopen(const QCString &fileName, const QCString &mode)
Definition portable.cpp:349
uint32_t pid()
Definition portable.cpp:248
int pclose(FILE *stream)
Definition portable.cpp:488
size_t recodeUtf8StringToW(const QCString &inputStr, uint16_t **buf)
Definition portable.cpp:623
bool fileSystemIsCaseSensitive()
Definition portable.cpp:470
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:105
void setenv(const QCString &variable, const QCString &value)
Definition portable.cpp:286
void unsetenv(const QCString &variable)
Definition portable.cpp:301
const char * commandExtension()
Definition portable.cpp:461
const char * strnstr(const char *haystack, const char *needle, size_t haystack_len)
Definition portable.cpp:600
QCString getenv(const QCString &variable)
Definition portable.cpp:321
const char * devNull()
Definition portable.cpp:614
int fclose(FILE *f)
Definition portable.cpp:369
void setShortDir()
Definition portable.cpp:553
static bool ExistsOnPath(const QCString &fileName)
Definition portable.cpp:392
static bool environmentLoaded
Definition portable.cpp:37
static std::map< std::string, std::string > proc_env
Definition portable.cpp:38
static const char * portable_memmem(const char *haystack, size_t haystack_len, const char *needle, size_t needle_len)
Definition portable.cpp:572
char ** environ
void loadEnvironment()
Definition portable.cpp:260
Portable versions of functions that are platform dependent.
int portable_iconv_close(void *cd)
size_t portable_iconv(void *cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
void * portable_iconv_open(const char *tocode, const char *fromcode)
QCString substitute(const QCString &s, const QCString &src, const QCString &dst)
substitute all occurrences of src in s by dst
Definition qcstring.cpp:571
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
std::chrono::steady_clock::time_point startTime
Definition portable.cpp:77
A bunch of utility functions.