Imported Upstream version 1.8.15
[platform/upstream/doxygen.git] / src / portable.cpp
1 #include <stdlib.h>
2 #include <ctype.h>
3 #if defined(_WIN32) && !defined(__CYGWIN__)
4 #undef UNICODE
5 #define _WIN32_DCOM
6 #include <windows.h>
7 #else
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <errno.h>
13 extern char **environ;
14 #endif
15
16 #include <qglobal.h>
17 #include <qdatetime.h>
18
19 #if defined(_MSC_VER) || defined(__BORLANDC__)
20 #define popen _popen
21 #define pclose _pclose
22 #endif
23
24 #include "portable.h"
25 #include "util.h"
26 #ifndef NODEBUG
27 #include "debug.h"
28 #endif
29
30 static double  g_sysElapsedTime;
31 static QTime   g_time;
32
33 int portable_system(const char *command,const char *args,bool commandHasConsole)
34 {
35
36   if (command==0) return 1;
37
38 #if defined(_WIN32) && !defined(__CYGWIN__)
39   QCString commandCorrectedPath = substitute(command,'/','\\');
40   QCString fullCmd=commandCorrectedPath;
41 #else
42   QCString fullCmd=command;
43 #endif
44   fullCmd=fullCmd.stripWhiteSpace();
45   if (fullCmd.at(0)!='"' && fullCmd.find(' ')!=-1)
46   {
47     // add quotes around command as it contains spaces and is not quoted already
48     fullCmd="\""+fullCmd+"\"";
49   }
50   fullCmd += " ";
51   fullCmd += args;
52 #ifndef NODEBUG
53   Debug::print(Debug::ExtCmd,0,"Executing external command `%s`\n",qPrint(fullCmd));
54 #endif
55
56 #if !defined(_WIN32) || defined(__CYGWIN__)
57   (void)commandHasConsole;
58   /*! taken from the system() manpage on my Linux box */
59   int pid,status=0;
60
61 #ifdef _OS_SOLARIS // for Solaris we use vfork since it is more memory efficient
62
63   // on Solaris fork() duplicates the memory usage
64   // so we use vfork instead
65   
66   // spawn shell
67   if ((pid=vfork())<0)
68   {
69     status=-1;
70   }
71   else if (pid==0)
72   {
73      execl("/bin/sh","sh","-c",fullCmd.data(),(char*)0);
74      _exit(127);
75   }
76   else
77   {
78     while (waitpid(pid,&status,0 )<0)
79     {
80       if (errno!=EINTR)
81       {
82         status=-1;
83         break;
84       }
85     }
86   }
87   return status;
88
89 #else  // Other Unices just use fork
90
91   pid = fork();
92   if (pid==-1)
93   {
94     perror("fork error");
95           return -1;
96   }
97   if (pid==0)
98   {
99     const char * argv[4];
100     argv[0] = "sh";
101     argv[1] = "-c";
102     argv[2] = fullCmd.data();
103     argv[3] = 0;
104     execve("/bin/sh",(char * const *)argv,environ);
105     exit(127);
106   }
107   for (;;)
108   {
109     if (waitpid(pid,&status,0)==-1)
110     {
111       if (errno!=EINTR) return -1;
112     }
113     else
114     {
115       if (WIFEXITED(status))
116       {
117         return WEXITSTATUS(status);
118       }
119       else
120       {
121         return status;
122       }
123     }
124   }
125 #endif // !_OS_SOLARIS
126
127 #else // Win32 specific
128   if (commandHasConsole)
129   {
130     return system(fullCmd);
131   }
132   else
133   {
134     // Because ShellExecuteEx can delegate execution to Shell extensions 
135     // (data sources, context menu handlers, verb implementations) that 
136     // are activated using Component Object Model (COM), COM should be 
137     // initialized before ShellExecuteEx is called. Some Shell extensions 
138     // require the COM single-threaded apartment (STA) type. 
139     // For that case COM is initialized as follows
140     CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
141
142     QString commandw = QString::fromUtf8( commandCorrectedPath );
143     QString argsw = QString::fromUtf8( args );
144
145     // gswin32 is a GUI api which will pop up a window and run
146     // asynchronously. To prevent both, we use ShellExecuteEx and
147     // WaitForSingleObject (thanks to Robert Golias for the code)
148
149     SHELLEXECUTEINFOW sInfo = {
150       sizeof(SHELLEXECUTEINFOW),   /* structure size */
151       SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI,  /* tell us the process
152                                                        *  handle so we can wait till it's done | 
153                                                        *  do not display msg box if error 
154                                                        */
155       NULL,                       /* window handle */
156       NULL,                       /* action to perform: open */
157       (LPCWSTR)commandw.ucs2(),   /* file to execute */
158       (LPCWSTR)argsw.ucs2(),      /* argument list */ 
159       NULL,                       /* use current working dir */
160       SW_HIDE,                    /* minimize on start-up */
161       0,                          /* application instance handle */
162       NULL,                       /* ignored: id list */
163       NULL,                       /* ignored: class name */
164       NULL,                       /* ignored: key class */
165       0,                          /* ignored: hot key */
166       NULL,                       /* ignored: icon */
167       NULL                        /* resulting application handle */
168     };
169
170     if (!ShellExecuteExW(&sInfo))
171     {
172       return -1;
173     }
174     else if (sInfo.hProcess)      /* executable was launched, wait for it to finish */
175     {
176       WaitForSingleObject(sInfo.hProcess,INFINITE); 
177       /* get process exit code */
178       DWORD exitCode;
179       if (!GetExitCodeProcess(sInfo.hProcess,&exitCode))
180       {
181         exitCode = -1;
182       }
183       CloseHandle(sInfo.hProcess);
184       return exitCode;
185     }
186   }
187 #endif
188   return 1; // we should never get here
189
190 }
191
192 uint portable_pid()
193 {
194   uint pid;
195 #if !defined(_WIN32) || defined(__CYGWIN__)
196   pid = (uint)getpid();
197 #else
198   pid = (uint)GetCurrentProcessId();
199 #endif
200   return pid;
201 }
202
203 #if defined(_WIN32) && !defined(__CYGWIN__)
204 #else
205   static char **last_environ;
206 #endif
207
208 void portable_setenv(const char *name,const char *value)
209 {
210     if (value==0) value="";
211 #if defined(_WIN32) && !defined(__CYGWIN__)
212     SetEnvironmentVariable(name,value);
213 #else
214     char **ep = 0;
215     size_t size;
216     const size_t namelen=qstrlen(name);
217     const size_t vallen=qstrlen(value) + 1;
218
219     size = 0;
220     if (environ!=0)
221     {
222       for (ep = environ; *ep; ++ep)
223       {
224         if (!qstrncmp (*ep, name, (uint)namelen) &&
225             (*ep)[namelen] == '=')
226           break;
227         else
228           ++size;
229       }
230     }
231
232     if (environ==0 || *ep==0) /* add new string */
233     {
234       char **new_environ;
235       if (environ == last_environ && environ!=0)
236       {
237         // We allocated this space; we can extend it. 
238         new_environ = (char **) realloc (last_environ, (size + 2) * sizeof (char *));
239       }
240       else
241       {
242         new_environ = (char **) malloc ((size + 2) * sizeof (char *));
243       }
244
245       if (new_environ==0) // no more memory 
246       {
247         return;
248       }
249
250       new_environ[size] = (char *)malloc (namelen + 1 + vallen);
251       if (new_environ[size]==0)
252       {
253         free (new_environ);
254         return;
255       }
256
257       if (environ != last_environ)
258       {
259         memcpy ((char *) new_environ, environ, size * sizeof (char *));
260       }
261
262       memcpy(new_environ[size], name, namelen);
263       new_environ[size][namelen] = '=';
264       memcpy(&new_environ[size][namelen + 1], value, vallen);
265       new_environ[size + 1] = 0;
266       last_environ = environ = new_environ;
267     }
268     else /* replace existing string */
269     {
270       size_t len = qstrlen (*ep);
271       if (len + 1 < namelen + 1 + vallen)
272       {
273         /* The existing string is too short; malloc a new one.  */
274         char *newString = (char *)malloc(namelen + 1 + vallen);
275         if (newString==0)
276         {
277           return;
278         }
279         *ep = newString;
280       }
281       memcpy(*ep, name, namelen);
282       (*ep)[namelen] = '=';
283       memcpy(&(*ep)[namelen + 1], value, vallen);
284     }
285
286 #endif
287 }
288
289 void portable_unsetenv(const char *variable)
290 {
291 #if defined(_WIN32) && !defined(__CYGWIN__)
292     SetEnvironmentVariable(variable,0);
293 #else
294     /* Some systems don't have unsetenv(), so we do it ourselves */
295     size_t len;
296     char **ep;
297
298     if (variable == NULL || *variable == '\0' || strchr (variable, '=') != NULL)
299     {
300       return; // not properly formatted
301     }
302
303     len = qstrlen(variable);
304
305     ep = environ;
306     while (*ep != NULL)
307     {
308       if (!qstrncmp(*ep, variable, (uint)len) && (*ep)[len]=='=')
309       {
310         /* Found it.  Remove this pointer by moving later ones back.  */
311         char **dp = ep;
312         do dp[0] = dp[1]; while (*dp++);
313         /* Continue the loop in case NAME appears again.  */
314       }
315       else
316       {
317         ++ep;
318       }
319     }
320 #endif
321 }
322
323 const char *portable_getenv(const char *variable)
324 {
325   return getenv(variable);
326 }
327
328 portable_off_t portable_fseek(FILE *f,portable_off_t offset, int whence)
329 {
330 #if defined(__MINGW32__)
331   return fseeko64(f,offset,whence);
332 #elif defined(_WIN32) && !defined(__CYGWIN__)
333   return _fseeki64(f,offset,whence);
334 #else
335   return fseeko(f,offset,whence);
336 #endif
337 }
338
339 portable_off_t portable_ftell(FILE *f)
340 {
341 #if defined(__MINGW32__)
342   return ftello64(f);  
343 #elif defined(_WIN32) && !defined(__CYGWIN__)
344   return _ftelli64(f);
345 #else
346   return ftello(f);
347 #endif
348 }
349
350 FILE *portable_fopen(const char *fileName,const char *mode)
351 {
352 #if defined(_WIN32) && !defined(__CYGWIN__)
353   QString fn(fileName);
354   QString m(mode);
355   return _wfopen((wchar_t*)fn.ucs2(),(wchar_t*)m.ucs2());
356 #else
357   return fopen(fileName,mode);
358 #endif
359 }
360
361 char  portable_pathSeparator()
362 {
363 #if defined(_WIN32) && !defined(__CYGWIN__)
364   return '\\';
365 #else
366   return '/';
367 #endif
368 }
369
370 char  portable_pathListSeparator()
371 {
372 #if defined(_WIN32) && !defined(__CYGWIN__)
373   return ';';
374 #else
375   return ':';
376 #endif
377 }
378
379 const char *portable_ghostScriptCommand()
380 {
381 #if defined(_WIN32) && !defined(__CYGWIN__)
382     return "gswin32c.exe";
383 #else
384     return "gs";
385 #endif
386 }
387
388 const char *portable_commandExtension()
389 {
390 #if defined(_WIN32) && !defined(__CYGWIN__)
391     return ".exe";
392 #else
393     return "";
394 #endif
395 }
396
397 bool portable_fileSystemIsCaseSensitive()
398 {
399 #if defined(_WIN32) || defined(macintosh) || defined(__MACOSX__) || defined(__APPLE__)
400   return FALSE;
401 #else
402   return TRUE;
403 #endif
404 }
405
406 FILE * portable_popen(const char *name,const char *type)
407 {
408   return popen(name,type);
409 }
410
411 int portable_pclose(FILE *stream)
412 {
413   return pclose(stream);
414 }
415
416 void portable_sysTimerStart()
417 {
418   g_time.start();
419 }
420
421 void portable_sysTimerStop()
422 {
423   g_sysElapsedTime+=((double)g_time.elapsed())/1000.0;
424 }
425
426 double portable_getSysElapsedTime()
427 {
428   return g_sysElapsedTime;
429 }
430
431 void portable_sleep(int ms)
432 {
433 #if defined(_WIN32) && !defined(__CYGWIN__)
434   Sleep(ms);
435 #else
436   usleep(1000*ms);
437 #endif
438 }
439
440 bool portable_isAbsolutePath(const char *fileName)
441 {
442 # ifdef _WIN32
443   if (isalpha (fileName [0]) && fileName[1] == ':')
444     fileName += 2;
445 # endif
446   char const fst = fileName [0];
447   if (fst == '/')  {
448     return true;
449   }
450 # ifdef _WIN32
451   if (fst == '\\')
452     return true;
453 # endif
454   return false;
455 }
456
457 /**
458  * Correct a possible wrong PATH variable
459  *
460  * This routine was inspired by the cause for bug 766059 was that in the Windows path there were forward slahes.
461  */
462 void portable_correct_path(void)
463 {
464 #if defined(_WIN32) && !defined(__CYGWIN__)
465   const char *p = portable_getenv("PATH");
466   QCString result = substitute(p,'/','\\');
467   if (result!=p) portable_setenv("PATH",result.data());
468 #endif
469 }