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