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