]> git.saurik.com Git - wxWidgets.git/blame - src/unix/utilsunx.cpp
More build system polishing.. mostly. Some stuff ported over from
[wxWidgets.git] / src / unix / utilsunx.cpp
CommitLineData
518b5d2f
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: utilsunx.cpp
3// Purpose: generic Unix implementation of many wx functions
4// Author: Vadim Zeitlin
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// ============================================================================
11// declarations
12// ============================================================================
13
14// ----------------------------------------------------------------------------
15// headers
16// ----------------------------------------------------------------------------
17
18#include "wx/defs.h"
19#include "wx/string.h"
20
21#include "wx/intl.h"
22#include "wx/log.h"
a37a5a73 23#include "wx/app.h"
518b5d2f
VZ
24
25#include "wx/utils.h"
26#include "wx/process.h"
bdc72a22 27#include "wx/thread.h"
518b5d2f 28
8b33ae2d
GL
29#include "wx/stream.h"
30
eadd7bd2
VZ
31#ifdef HAVE_STATFS
32 #include <sys/vfs.h>
33#endif // HAVE_STATFS
34
6dc6fda6
VZ
35#if wxUSE_GUI
36 #include "wx/unix/execute.h"
37#endif
518b5d2f 38
f6bcfd97
BP
39// SGI signal.h defines signal handler arguments differently depending on
40// whether _LANGUAGE_C_PLUS_PLUS is set or not - do set it
41#if defined(__SGI__) && !defined(_LANGUAGE_C_PLUS_PLUS)
42 #define _LANGUAGE_C_PLUS_PLUS 1
43#endif // SGI hack
44
518b5d2f
VZ
45#include <stdarg.h>
46#include <dirent.h>
47#include <string.h>
48#include <sys/stat.h>
49#include <sys/types.h>
50#include <unistd.h>
51#include <sys/wait.h>
52#include <pwd.h>
53#include <errno.h>
54#include <netdb.h>
55#include <signal.h>
56#include <fcntl.h> // for O_WRONLY and friends
57#include <time.h> // nanosleep() and/or usleep()
fad866f4 58#include <ctype.h> // isspace()
b12915c1 59#include <sys/time.h> // needed for FD_SETSIZE
7bcb11d3 60
0fcdf6dc 61#ifdef HAVE_UNAME
518b5d2f
VZ
62 #include <sys/utsname.h> // for uname()
63#endif // HAVE_UNAME
64
65// ----------------------------------------------------------------------------
66// conditional compilation
67// ----------------------------------------------------------------------------
68
69// many versions of Unices have this function, but it is not defined in system
70// headers - please add your system here if it is the case for your OS.
71// SunOS < 5.6 (i.e. Solaris < 2.6) and DG-UX are like this.
1363811b
VZ
72#if !defined(HAVE_USLEEP) && \
73 (defined(__SUN__) && !defined(__SunOs_5_6) && \
518b5d2f 74 !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
fd9811b1 75 defined(__osf__) || defined(__EMX__)
518b5d2f
VZ
76 extern "C"
77 {
1363811b
VZ
78 #ifdef __SUN__
79 int usleep(unsigned int usec);
80 #else // !Sun
bdc72a22
VZ
81 #ifdef __EMX__
82 /* I copied this from the XFree86 diffs. AV. */
83 #define INCL_DOSPROCESS
84 #include <os2.h>
85 inline void usleep(unsigned long delay)
86 {
87 DosSleep(delay ? (delay/1000l) : 1l);
88 }
89 #else // !Sun && !EMX
90 void usleep(unsigned long usec);
91 #endif
e6daf794 92 #endif // Sun/EMX/Something else
518b5d2f 93 };
bdc72a22
VZ
94
95 #define HAVE_USLEEP 1
518b5d2f
VZ
96#endif // Unices without usleep()
97
518b5d2f
VZ
98// ============================================================================
99// implementation
100// ============================================================================
101
102// ----------------------------------------------------------------------------
103// sleeping
104// ----------------------------------------------------------------------------
105
106void wxSleep(int nSecs)
107{
108 sleep(nSecs);
109}
110
111void wxUsleep(unsigned long milliseconds)
112{
b12915c1 113#if defined(HAVE_NANOSLEEP)
518b5d2f 114 timespec tmReq;
13111b2a 115 tmReq.tv_sec = (time_t)(milliseconds / 1000);
518b5d2f
VZ
116 tmReq.tv_nsec = (milliseconds % 1000) * 1000 * 1000;
117
118 // we're not interested in remaining time nor in return value
119 (void)nanosleep(&tmReq, (timespec *)NULL);
b12915c1 120#elif defined(HAVE_USLEEP)
518b5d2f
VZ
121 // uncomment this if you feel brave or if you are sure that your version
122 // of Solaris has a safe usleep() function but please notice that usleep()
123 // is known to lead to crashes in MT programs in Solaris 2.[67] and is not
124 // documented as MT-Safe
ea18eed9 125 #if defined(__SUN__) && wxUSE_THREADS
518b5d2f
VZ
126 #error "usleep() cannot be used in MT programs under Solaris."
127 #endif // Sun
128
129 usleep(milliseconds * 1000); // usleep(3) wants microseconds
b12915c1
VZ
130#elif defined(HAVE_SLEEP)
131 // under BeOS sleep() takes seconds (what about other platforms, if any?)
132 sleep(milliseconds * 1000);
518b5d2f
VZ
133#else // !sleep function
134 #error "usleep() or nanosleep() function required for wxUsleep"
135#endif // sleep function
136}
137
138// ----------------------------------------------------------------------------
139// process management
140// ----------------------------------------------------------------------------
141
50567b69 142int wxKill(long pid, wxSignal sig, wxKillError *rc)
518b5d2f 143{
50567b69
VZ
144 int err = kill((pid_t)pid, (int)sig);
145 if ( rc )
146 {
147 switch ( err )
148 {
149 case 0:
150 *rc = wxKILL_OK;
151 break;
152
153 case EINVAL:
154 *rc = wxKILL_BAD_SIGNAL;
155 break;
156
157 case EPERM:
158 *rc = wxKILL_ACCESS_DENIED;
159 break;
160
161 case ESRCH:
162 *rc = wxKILL_NO_PROCESS;
163 break;
164
165 default:
166 // this goes against Unix98 docs so log it
167 wxLogDebug(_T("unexpected kill(2) return value %d"), err);
168
169 // something else...
170 *rc = wxKILL_ERROR;
171 }
172 }
173
174 return err;
518b5d2f
VZ
175}
176
fad866f4
KB
177#define WXEXECUTE_NARGS 127
178
518b5d2f
VZ
179long wxExecute( const wxString& command, bool sync, wxProcess *process )
180{
223d09f6 181 wxCHECK_MSG( !command.IsEmpty(), 0, wxT("can't exec empty command") );
518b5d2f
VZ
182
183 int argc = 0;
05079acc 184 wxChar *argv[WXEXECUTE_NARGS];
fad866f4 185 wxString argument;
05079acc 186 const wxChar *cptr = command.c_str();
223d09f6 187 wxChar quotechar = wxT('\0'); // is arg quoted?
fad866f4 188 bool escaped = FALSE;
518b5d2f 189
0ed9a934 190 // split the command line in arguments
fad866f4
KB
191 do
192 {
223d09f6
KB
193 argument=wxT("");
194 quotechar = wxT('\0');
0ed9a934 195
fad866f4 196 // eat leading whitespace:
05079acc 197 while ( wxIsspace(*cptr) )
fad866f4 198 cptr++;
0ed9a934 199
223d09f6 200 if ( *cptr == wxT('\'') || *cptr == wxT('"') )
fad866f4 201 quotechar = *cptr++;
0ed9a934 202
fad866f4
KB
203 do
204 {
223d09f6 205 if ( *cptr == wxT('\\') && ! escaped )
fad866f4
KB
206 {
207 escaped = TRUE;
208 cptr++;
209 continue;
210 }
0ed9a934 211
fad866f4 212 // all other characters:
0ed9a934 213 argument += *cptr++;
fad866f4 214 escaped = FALSE;
0ed9a934
VZ
215
216 // have we reached the end of the argument?
217 if ( (*cptr == quotechar && ! escaped)
223d09f6
KB
218 || (quotechar == wxT('\0') && wxIsspace(*cptr))
219 || *cptr == wxT('\0') )
fad866f4 220 {
0ed9a934 221 wxASSERT_MSG( argc < WXEXECUTE_NARGS,
223d09f6 222 wxT("too many arguments in wxExecute") );
0ed9a934 223
05079acc
OK
224 argv[argc] = new wxChar[argument.length() + 1];
225 wxStrcpy(argv[argc], argument.c_str());
fad866f4 226 argc++;
0ed9a934 227
fad866f4 228 // if not at end of buffer, swallow last character:
0ed9a934
VZ
229 if(*cptr)
230 cptr++;
231
fad866f4
KB
232 break; // done with this one, start over
233 }
0ed9a934
VZ
234 } while(*cptr);
235 } while(*cptr);
fad866f4 236 argv[argc] = NULL;
0ed9a934
VZ
237
238 // do execute the command
518b5d2f
VZ
239 long lRc = wxExecute(argv, sync, process);
240
0ed9a934 241 // clean up
fad866f4 242 argc = 0;
0ed9a934 243 while( argv[argc] )
fad866f4 244 delete [] argv[argc++];
518b5d2f
VZ
245
246 return lRc;
247}
248
2c8e4738
VZ
249// ----------------------------------------------------------------------------
250// wxShell
251// ----------------------------------------------------------------------------
252
253static wxString wxMakeShellCommand(const wxString& command)
518b5d2f
VZ
254{
255 wxString cmd;
cd6ce4a9 256 if ( !command )
2c8e4738
VZ
257 {
258 // just an interactive shell
cd6ce4a9 259 cmd = _T("xterm");
2c8e4738 260 }
518b5d2f 261 else
2c8e4738
VZ
262 {
263 // execute command in a shell
264 cmd << _T("/bin/sh -c '") << command << _T('\'');
265 }
266
267 return cmd;
268}
269
270bool wxShell(const wxString& command)
271{
272 return wxExecute(wxMakeShellCommand(command), TRUE /* sync */) == 0;
273}
274
275bool wxShell(const wxString& command, wxArrayString& output)
276{
277 wxCHECK_MSG( !!command, FALSE, _T("can't exec shell non interactively") );
518b5d2f 278
2c8e4738 279 return wxExecute(wxMakeShellCommand(command), output);
518b5d2f
VZ
280}
281
6dc6fda6
VZ
282#if wxUSE_GUI
283
518b5d2f
VZ
284void wxHandleProcessTermination(wxEndProcessData *proc_data)
285{
286 int pid = (proc_data->pid > 0) ? proc_data->pid : -(proc_data->pid);
287
0ed9a934
VZ
288 // waitpid is POSIX so should be available everywhere, however on older
289 // systems wait() might be used instead in a loop (until the right pid
290 // terminates)
518b5d2f 291 int status = 0;
ab857a4e
KB
292 int rc;
293
bdc72a22 294 // wait for child termination and if waitpid() was interrupted, try again
ab857a4e 295 do
bdc72a22 296 {
ab857a4e 297 rc = waitpid(pid, &status, 0);
bdc72a22
VZ
298 }
299 while ( rc == -1 && errno == EINTR );
300
c556f198
JS
301 if (rc == -1)
302 {
303 // JACS: this could happen if the process was terminated and waitpid called,
304 // so commenting out for now.
305 //wxLogSysError(_("Waiting for subprocess termination failed (return code = -1)"));
306 }
307 else if (! (WIFEXITED(status)))
0ed9a934 308 {
c556f198
JS
309 wxLogSysError(_("Waiting for subprocess termination failed (WIFEXITED returned zero)"));
310
311 /* AFAIK, this can only happen if something went wrong within
312 wxGTK, i.e. due to a race condition or some serious bug.
313 After having fixed the order of statements in
314 GTK_EndProcessDetector(). (KB)
315 */
316 }
317 else if (WIFSIGNALED(status))
318 {
319 wxLogSysError(_("Waiting for subprocess termination failed (signal not caught)"));
320
ab857a4e 321 /* AFAIK, this can only happen if something went wrong within
bdc72a22 322 wxGTK, i.e. due to a race condition or some serious bug.
ab857a4e
KB
323 After having fixed the order of statements in
324 GTK_EndProcessDetector(). (KB)
325 */
0ed9a934 326 }
09d68ec1 327 // else
0ed9a934
VZ
328 {
329 // notify user about termination if required
330 if (proc_data->process)
331 {
332 proc_data->process->OnTerminate(proc_data->pid,
333 WEXITSTATUS(status));
334 }
ab857a4e
KB
335 // clean up
336 if ( proc_data->pid > 0 )
337 {
338 delete proc_data;
339 }
340 else
341 {
342 // wxExecute() will know about it
343 proc_data->exitcode = status;
bdc72a22 344
ab857a4e
KB
345 proc_data->pid = 0;
346 }
518b5d2f
VZ
347 }
348}
349
6dc6fda6
VZ
350#endif // wxUSE_GUI
351
cd6ce4a9
VZ
352// ----------------------------------------------------------------------------
353// wxStream classes to support IO redirection in wxExecute
354// ----------------------------------------------------------------------------
6dc6fda6 355
1e6feb95
VZ
356#if wxUSE_STREAMS
357
cd6ce4a9
VZ
358class wxProcessFileInputStream : public wxInputStream
359{
360public:
361 wxProcessFileInputStream(int fd) { m_fd = fd; }
362 ~wxProcessFileInputStream() { close(m_fd); }
8b33ae2d 363
cd6ce4a9 364 virtual bool Eof() const;
8b33ae2d 365
cd6ce4a9 366protected:
8b33ae2d
GL
367 size_t OnSysRead(void *buffer, size_t bufsize);
368
cd6ce4a9 369protected:
8b33ae2d
GL
370 int m_fd;
371};
372
cd6ce4a9
VZ
373class wxProcessFileOutputStream : public wxOutputStream
374{
375public:
376 wxProcessFileOutputStream(int fd) { m_fd = fd; }
377 ~wxProcessFileOutputStream() { close(m_fd); }
8b33ae2d 378
cd6ce4a9 379protected:
8b33ae2d
GL
380 size_t OnSysWrite(const void *buffer, size_t bufsize);
381
cd6ce4a9 382protected:
8b33ae2d
GL
383 int m_fd;
384};
385
cd6ce4a9 386bool wxProcessFileInputStream::Eof() const
8b33ae2d 387{
cd6ce4a9
VZ
388 if ( m_lasterror == wxSTREAM_EOF )
389 return TRUE;
390
391 // check if there is any input available
392 struct timeval tv;
393 tv.tv_sec = 0;
394 tv.tv_usec = 0;
395
396 fd_set readfds;
397 FD_ZERO(&readfds);
398 FD_SET(m_fd, &readfds);
399 switch ( select(m_fd + 1, &readfds, NULL, NULL, &tv) )
400 {
401 case -1:
402 wxLogSysError(_("Impossible to get child process input"));
403 // fall through
8b33ae2d 404
cd6ce4a9
VZ
405 case 0:
406 return TRUE;
8b33ae2d 407
cd6ce4a9
VZ
408 default:
409 wxFAIL_MSG(_T("unexpected select() return value"));
410 // still fall through
411
412 case 1:
413 // input available: check if there is any
414 return wxInputStream::Eof();
8b33ae2d 415 }
8b33ae2d
GL
416}
417
cd6ce4a9 418size_t wxProcessFileInputStream::OnSysRead(void *buffer, size_t bufsize)
8b33ae2d 419{
cd6ce4a9
VZ
420 int ret = read(m_fd, buffer, bufsize);
421 if ( ret == 0 )
422 {
423 m_lasterror = wxSTREAM_EOF;
424 }
425 else if ( ret == -1 )
426 {
427 m_lasterror = wxSTREAM_READ_ERROR;
428 ret = 0;
429 }
430 else
431 {
432 m_lasterror = wxSTREAM_NOERROR;
433 }
8b33ae2d 434
cd6ce4a9 435 return ret;
8b33ae2d
GL
436}
437
438size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
439{
cd6ce4a9
VZ
440 int ret = write(m_fd, buffer, bufsize);
441 if ( ret == -1 )
442 {
443 m_lasterror = wxSTREAM_WRITE_ERROR;
444 ret = 0;
8b33ae2d 445 }
cd6ce4a9
VZ
446 else
447 {
448 m_lasterror = wxSTREAM_NOERROR;
449 }
450
8b33ae2d
GL
451 return ret;
452}
453
0e300ddd
VZ
454// ----------------------------------------------------------------------------
455// wxStreamTempBuffer
456// ----------------------------------------------------------------------------
457
458/*
459 Extract of a mail to wx-users to give the context of the problem we are
460 trying to solve here:
461
462 MC> If I run the command:
463 MC> find . -name "*.h" -exec grep linux {} \;
464 MC> in the exec sample synchronously from the 'Capture command output'
465 MC> menu, wxExecute never returns. I have to xkill it. Has anyone
466 MC> else encountered this?
467
468 Yes, I can reproduce it too.
469
470 I even think I understand why it happens: before launching the external
471 command we set up a pipe with a valid file descriptor on the reading side
472 when the output is redirected. So the subprocess happily writes to it ...
473 until the pipe buffer (which is usually quite big on Unix, I think the
474 default is 4Mb) is full. Then the writing process stops and waits until we
475 read some data from the pipe to be able to continue writing to it but we
476 never do it because we wait until it terminates to start reading and so we
477 have a classical deadlock.
478
479 Here is the fix: we now read the output as soon as it appears into a temp
480 buffer (wxStreamTempBuffer object) and later just stuff it back into the
481 stream when the process terminates. See supporting code in wxExecute()
482 itself as well.
483*/
484
485class wxStreamTempBuffer
486{
487public:
488 wxStreamTempBuffer();
489
490 // call to associate a stream with this buffer, otherwise nothing happens
491 // at all
492 void Init(wxInputStream *stream);
493
494 // check for input on our stream and cache it in our buffer if any
495 void Update();
496
497 ~wxStreamTempBuffer();
498
499private:
500 // the stream we're buffering, if NULL we don't do anything at all
501 wxInputStream *m_stream;
502
503 // the buffer of size m_size (NULL if m_size == 0)
504 void *m_buffer;
505
506 // the size of the buffer
507 size_t m_size;
508};
509
510wxStreamTempBuffer::wxStreamTempBuffer()
511{
512 m_stream = NULL;
513 m_buffer = NULL;
514 m_size = 0;
515}
516
517void wxStreamTempBuffer::Init(wxInputStream *stream)
518{
519 m_stream = stream;
520}
521
522void wxStreamTempBuffer::Update()
523{
524 if ( m_stream && !m_stream->Eof() )
525 {
526 // realloc in blocks of 1Kb - surely not the best strategy but which
527 // one is?
528 static const size_t incSize = 1024;
529
530 void *buf = realloc(m_buffer, m_size + incSize);
531 if ( !buf )
532 {
533 // don't read any more, we don't have enough memory to do it
534 m_stream = NULL;
535 }
536 else // got memory for the buffer
537 {
538 m_buffer = buf;
539 m_stream->Read((char *)m_buffer + m_size, incSize);
540 m_size += incSize;
541 }
542 }
543}
544
545wxStreamTempBuffer::~wxStreamTempBuffer()
546{
547 if ( m_buffer )
548 {
549 m_stream->Ungetch(m_buffer, m_size);
550 free(m_buffer);
551 }
552}
553
1e6feb95
VZ
554#endif // wxUSE_STREAMS
555
6dc6fda6
VZ
556long wxExecute(wxChar **argv,
557 bool sync,
cd6ce4a9 558 wxProcess *process)
518b5d2f 559{
f6bcfd97 560 // for the sync execution, we return -1 to indicate failure, but for async
accb3257
VZ
561 // case we return 0 which is never a valid PID
562 //
563 // we define this as a macro, not a variable, to avoid compiler warnings
564 // about "ERROR_RETURN_CODE value may be clobbered by fork()"
565 #define ERROR_RETURN_CODE ((sync) ? -1 : 0)
f6bcfd97 566
accb3257 567 wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") );
518b5d2f 568
05079acc
OK
569#if wxUSE_UNICODE
570 int mb_argc = 0;
571 char *mb_argv[WXEXECUTE_NARGS];
572
e90c1d2a
VZ
573 while (argv[mb_argc])
574 {
cd6ce4a9
VZ
575 wxWX2MBbuf mb_arg = wxConvertWX2MB(argv[mb_argc]);
576 mb_argv[mb_argc] = strdup(mb_arg);
577 mb_argc++;
05079acc
OK
578 }
579 mb_argv[mb_argc] = (char *) NULL;
e90c1d2a
VZ
580
581 // this macro will free memory we used above
582 #define ARGS_CLEANUP \
345b0247 583 for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
e90c1d2a
VZ
584 free(mb_argv[mb_argc])
585#else // ANSI
586 // no need for cleanup
587 #define ARGS_CLEANUP
588
05079acc 589 wxChar **mb_argv = argv;
e90c1d2a 590#endif // Unicode/ANSI
518b5d2f 591
e90c1d2a 592#if wxUSE_GUI
518b5d2f 593 // create pipes
e90c1d2a 594 int end_proc_detect[2];
cd6ce4a9 595 if ( pipe(end_proc_detect) == -1 )
518b5d2f
VZ
596 {
597 wxLogSysError( _("Pipe creation failed") );
cd6ce4a9 598 wxLogError( _("Failed to execute '%s'\n"), *argv );
e90c1d2a
VZ
599
600 ARGS_CLEANUP;
601
accb3257 602 return ERROR_RETURN_CODE;
518b5d2f 603 }
e90c1d2a 604#endif // wxUSE_GUI
518b5d2f 605
f6bcfd97
BP
606 // pipes for inter process communication
607 int pipeIn[2], // stdin
608 pipeOut[2], // stdout
609 pipeErr[2]; // stderr
610
cd6ce4a9 611 pipeIn[0] = pipeIn[1] =
f6bcfd97
BP
612 pipeOut[0] = pipeOut[1] =
613 pipeErr[0] = pipeErr[1] = -1;
cd6ce4a9
VZ
614
615 if ( process && process->IsRedirected() )
8b33ae2d 616 {
f6bcfd97 617 if ( pipe(pipeIn) == -1 || pipe(pipeOut) == -1 || pipe(pipeErr) == -1 )
8b33ae2d 618 {
cd6ce4a9
VZ
619#if wxUSE_GUI
620 // free previously allocated resources
8b33ae2d
GL
621 close(end_proc_detect[0]);
622 close(end_proc_detect[1]);
cd6ce4a9
VZ
623#endif // wxUSE_GUI
624
625 wxLogSysError( _("Pipe creation failed") );
626 wxLogError( _("Failed to execute '%s'\n"), *argv );
8b33ae2d
GL
627
628 ARGS_CLEANUP;
629
accb3257 630 return ERROR_RETURN_CODE;
8b33ae2d
GL
631 }
632 }
8b33ae2d 633
518b5d2f 634 // fork the process
0fcdf6dc 635#ifdef HAVE_VFORK
518b5d2f
VZ
636 pid_t pid = vfork();
637#else
638 pid_t pid = fork();
639#endif
cd6ce4a9
VZ
640
641 if ( pid == -1 ) // error?
518b5d2f 642 {
8b33ae2d
GL
643#if wxUSE_GUI
644 close(end_proc_detect[0]);
645 close(end_proc_detect[1]);
cd6ce4a9
VZ
646 close(pipeIn[0]);
647 close(pipeIn[1]);
648 close(pipeOut[0]);
649 close(pipeOut[1]);
f6bcfd97
BP
650 close(pipeErr[0]);
651 close(pipeErr[1]);
cd6ce4a9
VZ
652#endif // wxUSE_GUI
653
518b5d2f 654 wxLogSysError( _("Fork failed") );
e90c1d2a
VZ
655
656 ARGS_CLEANUP;
657
accb3257 658 return ERROR_RETURN_CODE;
518b5d2f 659 }
cd6ce4a9 660 else if ( pid == 0 ) // we're in child
518b5d2f 661 {
e90c1d2a 662#if wxUSE_GUI
518b5d2f 663 close(end_proc_detect[0]); // close reading side
e90c1d2a 664#endif // wxUSE_GUI
518b5d2f 665
cd6ce4a9 666 // These lines close the open file descriptors to to avoid any
518b5d2f 667 // input/output which might block the process or irritate the user. If
cd6ce4a9
VZ
668 // one wants proper IO for the subprocess, the right thing to do is to
669 // start an xterm executing it.
670 if ( !sync )
518b5d2f 671 {
518b5d2f
VZ
672 for ( int fd = 0; fd < FD_SETSIZE; fd++ )
673 {
f6bcfd97 674 if ( fd == pipeIn[0] || fd == pipeOut[1] || fd == pipeErr[1]
e90c1d2a 675#if wxUSE_GUI
cd6ce4a9 676 || fd == end_proc_detect[1]
e90c1d2a 677#endif // wxUSE_GUI
cd6ce4a9
VZ
678 )
679 {
680 // don't close this one, we still need it
681 continue;
682 }
e90c1d2a 683
cd6ce4a9 684 // leave stderr opened too, it won't do any hurm
e90c1d2a 685 if ( fd != STDERR_FILENO )
518b5d2f
VZ
686 close(fd);
687 }
688 }
689
f6bcfd97 690 // redirect stdio, stdout and stderr
cd6ce4a9
VZ
691 if ( pipeIn[0] != -1 )
692 {
693 if ( dup2(pipeIn[0], STDIN_FILENO) == -1 ||
f6bcfd97
BP
694 dup2(pipeOut[1], STDOUT_FILENO) == -1 ||
695 dup2(pipeErr[1], STDERR_FILENO) == -1 )
cd6ce4a9 696 {
f6bcfd97 697 wxLogSysError(_("Failed to redirect child process input/output"));
cd6ce4a9 698 }
518b5d2f 699
cd6ce4a9
VZ
700 close(pipeIn[0]);
701 close(pipeOut[1]);
f6bcfd97 702 close(pipeErr[1]);
cd6ce4a9 703 }
518b5d2f 704
05079acc 705 execvp (*mb_argv, mb_argv);
518b5d2f
VZ
706
707 // there is no return after successful exec()
518b5d2f 708 _exit(-1);
1d8dd65e
VZ
709
710 // some compilers complain about missing return - of course, they
711 // should know that exit() doesn't return but what else can we do if
712 // they don't?
713#if defined(__VMS) || defined(__INTEL_COMPILER)
714 return 0;
715#endif
518b5d2f 716 }
cd6ce4a9 717 else // we're in parent
518b5d2f 718 {
cd6ce4a9
VZ
719 ARGS_CLEANUP;
720
721 // pipe initialization: construction of the wxStreams
0e300ddd
VZ
722#if wxUSE_STREAMS
723 wxStreamTempBuffer bufIn, bufErr;
724#endif // wxUSE_STREAMS
725
cd6ce4a9
VZ
726 if ( process && process->IsRedirected() )
727 {
1e6feb95 728#if wxUSE_STREAMS
0e300ddd 729 // in/out for subprocess correspond to our out/in
cd6ce4a9
VZ
730 wxOutputStream *outStream = new wxProcessFileOutputStream(pipeIn[1]);
731 wxInputStream *inStream = new wxProcessFileInputStream(pipeOut[0]);
f6bcfd97
BP
732 wxInputStream *errStream = new wxProcessFileInputStream(pipeErr[0]);
733
1e6feb95 734 process->SetPipeStreams(inStream, outStream, errStream);
0e300ddd
VZ
735
736 bufIn.Init(inStream);
737 bufErr.Init(inStream);
1e6feb95
VZ
738#endif // wxUSE_STREAMS
739
cd6ce4a9
VZ
740 close(pipeIn[0]); // close reading side
741 close(pipeOut[1]); // close writing side
f6bcfd97 742 close(pipeErr[1]); // close writing side
cd6ce4a9
VZ
743 }
744
fac3b423 745#if wxUSE_GUI && !defined(__WXMICROWIN__)
518b5d2f 746 wxEndProcessData *data = new wxEndProcessData;
ab857a4e 747
518b5d2f
VZ
748 if ( sync )
749 {
cd6ce4a9
VZ
750 // we may have process for capturing the program output, but it's
751 // not used in wxEndProcessData in the case of sync execution
518b5d2f
VZ
752 data->process = NULL;
753
754 // sync execution: indicate it by negating the pid
cd6ce4a9
VZ
755 data->pid = -pid;
756 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
757
ab857a4e 758 close(end_proc_detect[1]); // close writing side
518b5d2f 759
cd6ce4a9
VZ
760 wxBusyCursor bc;
761 wxWindowDisabler wd;
762
0e300ddd
VZ
763 // data->pid will be set to 0 from GTK_EndProcessDetector when the
764 // process terminates
765 while ( data->pid != 0 )
766 {
767#if wxUSE_STREAMS
768 bufIn.Update();
769 bufErr.Update();
770#endif // wxUSE_STREAMS
771
772 // give GTK+ a chance to call GTK_EndProcessDetector here and
773 // also repaint the GUI
518b5d2f 774 wxYield();
0e300ddd 775 }
518b5d2f
VZ
776
777 int exitcode = data->exitcode;
778
779 delete data;
780
781 return exitcode;
782 }
cd6ce4a9 783 else // async execution
518b5d2f
VZ
784 {
785 // async execution, nothing special to do - caller will be
ab857a4e 786 // notified about the process termination if process != NULL, data
518b5d2f 787 // will be deleted in GTK_EndProcessDetector
8b33ae2d
GL
788 data->process = process;
789 data->pid = pid;
790 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
cd6ce4a9 791
ab857a4e 792 close(end_proc_detect[1]); // close writing side
518b5d2f
VZ
793
794 return pid;
795 }
e90c1d2a 796#else // !wxUSE_GUI
223d09f6 797 wxASSERT_MSG( sync, wxT("async execution not supported yet") );
e90c1d2a
VZ
798
799 int exitcode = 0;
800 if ( waitpid(pid, &exitcode, 0) == -1 || !WIFEXITED(exitcode) )
801 {
802 wxLogSysError(_("Waiting for subprocess termination failed"));
803 }
804
805 return exitcode;
806#endif // wxUSE_GUI
518b5d2f
VZ
807 }
808}
809
accb3257 810#undef ERROR_RETURN_CODE
f6bcfd97
BP
811#undef ARGS_CLEANUP
812
518b5d2f
VZ
813// ----------------------------------------------------------------------------
814// file and directory functions
815// ----------------------------------------------------------------------------
816
05079acc 817const wxChar* wxGetHomeDir( wxString *home )
518b5d2f
VZ
818{
819 *home = wxGetUserHome( wxString() );
181cbcf4 820 wxString tmp;
518b5d2f 821 if ( home->IsEmpty() )
223d09f6 822 *home = wxT("/");
181cbcf4
JJ
823#ifdef __VMS
824 tmp = *home;
825 if ( tmp.Last() != wxT(']'))
826 if ( tmp.Last() != wxT('/')) *home << wxT('/');
827#endif
518b5d2f
VZ
828 return home->c_str();
829}
830
05079acc
OK
831#if wxUSE_UNICODE
832const wxMB2WXbuf wxGetUserHome( const wxString &user )
e90c1d2a 833#else // just for binary compatibility -- there is no 'const' here
518b5d2f 834char *wxGetUserHome( const wxString &user )
05079acc 835#endif
518b5d2f
VZ
836{
837 struct passwd *who = (struct passwd *) NULL;
838
0fb67cd1 839 if ( !user )
518b5d2f 840 {
e90c1d2a 841 wxChar *ptr;
518b5d2f 842
223d09f6 843 if ((ptr = wxGetenv(wxT("HOME"))) != NULL)
518b5d2f
VZ
844 {
845 return ptr;
846 }
223d09f6 847 if ((ptr = wxGetenv(wxT("USER"))) != NULL || (ptr = wxGetenv(wxT("LOGNAME"))) != NULL)
518b5d2f 848 {
e90c1d2a 849 who = getpwnam(wxConvertWX2MB(ptr));
518b5d2f
VZ
850 }
851
852 // We now make sure the the user exists!
853 if (who == NULL)
854 {
855 who = getpwuid(getuid());
856 }
857 }
858 else
859 {
05079acc 860 who = getpwnam (user.mb_str());
518b5d2f
VZ
861 }
862
af111fc3 863 return wxConvertMB2WX(who ? who->pw_dir : 0);
518b5d2f
VZ
864}
865
866// ----------------------------------------------------------------------------
0fb67cd1 867// network and user id routines
518b5d2f
VZ
868// ----------------------------------------------------------------------------
869
0fb67cd1
VZ
870// retrieve either the hostname or FQDN depending on platform (caller must
871// check whether it's one or the other, this is why this function is for
872// private use only)
05079acc 873static bool wxGetHostNameInternal(wxChar *buf, int sz)
518b5d2f 874{
223d09f6 875 wxCHECK_MSG( buf, FALSE, wxT("NULL pointer in wxGetHostNameInternal") );
518b5d2f 876
223d09f6 877 *buf = wxT('\0');
518b5d2f
VZ
878
879 // we're using uname() which is POSIX instead of less standard sysinfo()
880#if defined(HAVE_UNAME)
cc743a6f 881 struct utsname uts;
518b5d2f
VZ
882 bool ok = uname(&uts) != -1;
883 if ( ok )
884 {
e90c1d2a 885 wxStrncpy(buf, wxConvertMB2WX(uts.nodename), sz - 1);
223d09f6 886 buf[sz] = wxT('\0');
518b5d2f
VZ
887 }
888#elif defined(HAVE_GETHOSTNAME)
889 bool ok = gethostname(buf, sz) != -1;
0fb67cd1 890#else // no uname, no gethostname
223d09f6 891 wxFAIL_MSG(wxT("don't know host name for this machine"));
518b5d2f
VZ
892
893 bool ok = FALSE;
0fb67cd1 894#endif // uname/gethostname
518b5d2f
VZ
895
896 if ( !ok )
897 {
898 wxLogSysError(_("Cannot get the hostname"));
899 }
900
901 return ok;
902}
903
05079acc 904bool wxGetHostName(wxChar *buf, int sz)
0fb67cd1
VZ
905{
906 bool ok = wxGetHostNameInternal(buf, sz);
907
908 if ( ok )
909 {
910 // BSD systems return the FQDN, we only want the hostname, so extract
911 // it (we consider that dots are domain separators)
223d09f6 912 wxChar *dot = wxStrchr(buf, wxT('.'));
0fb67cd1
VZ
913 if ( dot )
914 {
915 // nuke it
223d09f6 916 *dot = wxT('\0');
0fb67cd1
VZ
917 }
918 }
919
920 return ok;
921}
922
05079acc 923bool wxGetFullHostName(wxChar *buf, int sz)
0fb67cd1
VZ
924{
925 bool ok = wxGetHostNameInternal(buf, sz);
926
927 if ( ok )
928 {
223d09f6 929 if ( !wxStrchr(buf, wxT('.')) )
0fb67cd1 930 {
e90c1d2a 931 struct hostent *host = gethostbyname(wxConvertWX2MB(buf));
0fb67cd1
VZ
932 if ( !host )
933 {
934 wxLogSysError(_("Cannot get the official hostname"));
935
936 ok = FALSE;
937 }
938 else
939 {
940 // the canonical name
e90c1d2a 941 wxStrncpy(buf, wxConvertMB2WX(host->h_name), sz);
0fb67cd1
VZ
942 }
943 }
944 //else: it's already a FQDN (BSD behaves this way)
945 }
946
947 return ok;
948}
949
05079acc 950bool wxGetUserId(wxChar *buf, int sz)
518b5d2f
VZ
951{
952 struct passwd *who;
953
223d09f6 954 *buf = wxT('\0');
518b5d2f
VZ
955 if ((who = getpwuid(getuid ())) != NULL)
956 {
e90c1d2a 957 wxStrncpy (buf, wxConvertMB2WX(who->pw_name), sz - 1);
518b5d2f
VZ
958 return TRUE;
959 }
960
961 return FALSE;
962}
963
05079acc 964bool wxGetUserName(wxChar *buf, int sz)
518b5d2f
VZ
965{
966 struct passwd *who;
518b5d2f 967
223d09f6 968 *buf = wxT('\0');
b12915c1
VZ
969 if ((who = getpwuid (getuid ())) != NULL)
970 {
971 // pw_gecos field in struct passwd is not standard
bd3277fe 972#ifdef HAVE_PW_GECOS
b12915c1 973 char *comma = strchr(who->pw_gecos, ',');
518b5d2f
VZ
974 if (comma)
975 *comma = '\0'; // cut off non-name comment fields
e90c1d2a 976 wxStrncpy (buf, wxConvertMB2WX(who->pw_gecos), sz - 1);
b12915c1 977#else // !HAVE_PW_GECOS
0fcdf6dc 978 wxStrncpy (buf, wxConvertMB2WX(who->pw_name), sz - 1);
b12915c1 979#endif // HAVE_PW_GECOS/!HAVE_PW_GECOS
518b5d2f
VZ
980 return TRUE;
981 }
982
983 return FALSE;
984}
985
bdc72a22
VZ
986wxString wxGetOsDescription()
987{
988#ifndef WXWIN_OS_DESCRIPTION
989 #error WXWIN_OS_DESCRIPTION should be defined in config.h by configure
990#else
991 return WXWIN_OS_DESCRIPTION;
992#endif
993}
994
bd3277fe
VZ
995// this function returns the GUI toolkit version in GUI programs, but OS
996// version in non-GUI ones
997#if !wxUSE_GUI
998
999int wxGetOsVersion(int *majorVsn, int *minorVsn)
1000{
1001 int major, minor;
1002 char name[256];
1003
1004 if ( sscanf(WXWIN_OS_DESCRIPTION, "%s %d.%d", name, &major, &minor) != 3 )
1005 {
1006 // unreckognized uname string format
1007 major = minor = -1;
1008 }
1009
1010 if ( majorVsn )
1011 *majorVsn = major;
1012 if ( minorVsn )
1013 *minorVsn = minor;
1014
1015 return wxUNIX;
1016}
1017
1018#endif // !wxUSE_GUI
1019
1020long wxGetFreeMemory()
1021{
1022#if defined(__LINUX__)
1023 // get it from /proc/meminfo
1024 FILE *fp = fopen("/proc/meminfo", "r");
1025 if ( fp )
1026 {
1027 long memFree = -1;
1028
1029 char buf[1024];
1030 if ( fgets(buf, WXSIZEOF(buf), fp) && fgets(buf, WXSIZEOF(buf), fp) )
1031 {
1032 long memTotal, memUsed;
1033 sscanf(buf, "Mem: %ld %ld %ld", &memTotal, &memUsed, &memFree);
1034 }
1035
1036 fclose(fp);
1037
1038 return memFree;
1039 }
1040#elif defined(__SUN__) && defined(_SC_AVPHYS_PAGES)
1041 return sysconf(_SC_AVPHYS_PAGES)*sysconf(_SC_PAGESIZE);
1042//#elif defined(__FREEBSD__) -- might use sysctl() to find it out, probably
1043#endif
1044
1045 // can't find it out
1046 return -1;
1047}
1048
eadd7bd2
VZ
1049bool wxGetDiskSpace(const wxString& path, wxLongLong *pTotal, wxLongLong *pFree)
1050{
1051#ifdef HAVE_STATFS
1052
1053 struct statfs fs;
1054 if ( statfs(path, &fs) != 0 )
1055 {
1056 wxLogSysError("Failed to get file system statistics");
1057
1058 return FALSE;
1059 }
1060
1061 if ( pTotal )
1062 {
1063 *pTotal = wxLongLong(fs.f_blocks) * fs.f_bsize;
1064 }
1065
1066 if ( pFree )
1067 {
1068 *pFree = wxLongLong(fs.f_bavail) * fs.f_bsize;
1069 }
1070
1071 return TRUE;
1072#endif // HAVE_STATFS
1073
1074 return FALSE;
1075}
1076
8fd0d89b
VZ
1077// ----------------------------------------------------------------------------
1078// env vars
1079// ----------------------------------------------------------------------------
1080
97b305b7 1081bool wxGetEnv(const wxString& var, wxString *value)
308978f6
VZ
1082{
1083 // wxGetenv is defined as getenv()
1084 wxChar *p = wxGetenv(var);
1085 if ( !p )
1086 return FALSE;
1087
1088 if ( value )
1089 {
1090 *value = p;
1091 }
1092
1093 return TRUE;
1094}
1095
8fd0d89b
VZ
1096bool wxSetEnv(const wxString& variable, const wxChar *value)
1097{
1098#if defined(HAVE_SETENV)
1099 return setenv(variable.mb_str(), value ? wxString(value).mb_str().data()
1100 : NULL, 1 /* overwrite */) == 0;
1101#elif defined(HAVE_PUTENV)
1102 wxString s = variable;
1103 if ( value )
1104 s << _T('=') << value;
1105
1106 // transform to ANSI
1107 const char *p = s.mb_str();
1108
1109 // the string will be free()d by libc
1110 char *buf = (char *)malloc(strlen(p) + 1);
1111 strcpy(buf, p);
1112
1113 return putenv(buf) == 0;
1114#else // no way to set an env var
1115 return FALSE;
1116#endif
1117}
1118
a37a5a73
VZ
1119// ----------------------------------------------------------------------------
1120// signal handling
1121// ----------------------------------------------------------------------------
1122
1123#if wxUSE_ON_FATAL_EXCEPTION
1124
1125#include <signal.h>
1126
f6bcfd97 1127static void wxFatalSignalHandler(wxTYPE_SA_HANDLER)
a37a5a73
VZ
1128{
1129 if ( wxTheApp )
1130 {
1131 // give the user a chance to do something special about this
1132 wxTheApp->OnFatalException();
1133 }
1134
1135 abort();
1136}
1137
1138bool wxHandleFatalExceptions(bool doit)
1139{
1140 // old sig handlers
1141 static bool s_savedHandlers = FALSE;
1142 static struct sigaction s_handlerFPE,
1143 s_handlerILL,
1144 s_handlerBUS,
1145 s_handlerSEGV;
1146
1147 bool ok = TRUE;
1148 if ( doit && !s_savedHandlers )
1149 {
1150 // install the signal handler
1151 struct sigaction act;
1152
1153 // some systems extend it with non std fields, so zero everything
1154 memset(&act, 0, sizeof(act));
1155
1156 act.sa_handler = wxFatalSignalHandler;
1157 sigemptyset(&act.sa_mask);
1158 act.sa_flags = 0;
1159
1160 ok &= sigaction(SIGFPE, &act, &s_handlerFPE) == 0;
1161 ok &= sigaction(SIGILL, &act, &s_handlerILL) == 0;
1162 ok &= sigaction(SIGBUS, &act, &s_handlerBUS) == 0;
1163 ok &= sigaction(SIGSEGV, &act, &s_handlerSEGV) == 0;
1164 if ( !ok )
1165 {
1166 wxLogDebug(_T("Failed to install our signal handler."));
1167 }
1168
1169 s_savedHandlers = TRUE;
1170 }
1171 else if ( s_savedHandlers )
1172 {
1173 // uninstall the signal handler
1174 ok &= sigaction(SIGFPE, &s_handlerFPE, NULL) == 0;
1175 ok &= sigaction(SIGILL, &s_handlerILL, NULL) == 0;
1176 ok &= sigaction(SIGBUS, &s_handlerBUS, NULL) == 0;
1177 ok &= sigaction(SIGSEGV, &s_handlerSEGV, NULL) == 0;
1178 if ( !ok )
1179 {
1180 wxLogDebug(_T("Failed to uninstall our signal handler."));
1181 }
1182
1183 s_savedHandlers = FALSE;
1184 }
1185 //else: nothing to do
1186
1187 return ok;
1188}
1189
1190#endif // wxUSE_ON_FATAL_EXCEPTION
1191
518b5d2f
VZ
1192// ----------------------------------------------------------------------------
1193// error and debug output routines (deprecated, use wxLog)
1194// ----------------------------------------------------------------------------
1195
1196void wxDebugMsg( const char *format, ... )
1197{
1198 va_list ap;
1199 va_start( ap, format );
1200 vfprintf( stderr, format, ap );
1201 fflush( stderr );
1202 va_end(ap);
1203}
1204
1205void wxError( const wxString &msg, const wxString &title )
1206{
05079acc 1207 wxFprintf( stderr, _("Error ") );
223d09f6
KB
1208 if (!title.IsNull()) wxFprintf( stderr, wxT("%s "), WXSTRINGCAST(title) );
1209 if (!msg.IsNull()) wxFprintf( stderr, wxT(": %s"), WXSTRINGCAST(msg) );
1210 wxFprintf( stderr, wxT(".\n") );
518b5d2f
VZ
1211}
1212
1213void wxFatalError( const wxString &msg, const wxString &title )
1214{
05079acc 1215 wxFprintf( stderr, _("Error ") );
223d09f6
KB
1216 if (!title.IsNull()) wxFprintf( stderr, wxT("%s "), WXSTRINGCAST(title) );
1217 if (!msg.IsNull()) wxFprintf( stderr, wxT(": %s"), WXSTRINGCAST(msg) );
1218 wxFprintf( stderr, wxT(".\n") );
518b5d2f
VZ
1219 exit(3); // the same exit code as for abort()
1220}
93ccaed8 1221