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