1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     generic Unix implementation of many wx functions 
   4 // Author:      Vadim Zeitlin 
   6 // Copyright:   (c) 1998 Robert Roebling, Vadim Zeitlin 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 // ============================================================================ 
  12 // ============================================================================ 
  14 // ---------------------------------------------------------------------------- 
  16 // ---------------------------------------------------------------------------- 
  19 #include "wx/string.h" 
  26 #include "wx/process.h" 
  27 #include "wx/thread.h" 
  29 #include "wx/stream.h" 
  32     #include "wx/unix/execute.h" 
  35 // SGI signal.h defines signal handler arguments differently depending on 
  36 // whether _LANGUAGE_C_PLUS_PLUS is set or not - do set it 
  37 #if defined(__SGI__) && !defined(_LANGUAGE_C_PLUS_PLUS) 
  38     #define _LANGUAGE_C_PLUS_PLUS 1 
  45 #include <sys/types.h> 
  52 #include <fcntl.h>          // for O_WRONLY and friends 
  53 #include <time.h>           // nanosleep() and/or usleep() 
  54 #include <ctype.h>          // isspace() 
  55 #include <sys/time.h>       // needed for FD_SETSIZE 
  58     #include <sys/utsname.h> // for uname() 
  61 // ---------------------------------------------------------------------------- 
  62 // conditional compilation 
  63 // ---------------------------------------------------------------------------- 
  65 // many versions of Unices have this function, but it is not defined in system 
  66 // headers - please add your system here if it is the case for your OS. 
  67 // SunOS < 5.6 (i.e. Solaris < 2.6) and DG-UX are like this. 
  68 #if !defined(HAVE_USLEEP) && \ 
  69     (defined(__SUN__) && !defined(__SunOs_5_6) && \ 
  70                          !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \ 
  71      defined(__osf__) || defined(__EMX__) 
  75             int usleep(unsigned int usec
); 
  78                 /* I copied this from the XFree86 diffs. AV. */ 
  79                 #define INCL_DOSPROCESS 
  81                 inline void usleep(unsigned long delay
) 
  83                     DosSleep(delay 
? (delay
/1000l) : 1l); 
  86                 void usleep(unsigned long usec
); 
  88         #endif // Sun/EMX/Something else 
  92 #endif // Unices without usleep() 
  94 // ============================================================================ 
  96 // ============================================================================ 
  98 // ---------------------------------------------------------------------------- 
 100 // ---------------------------------------------------------------------------- 
 102 void wxSleep(int nSecs
) 
 107 void wxUsleep(unsigned long milliseconds
) 
 109 #if defined(HAVE_NANOSLEEP) 
 111     tmReq
.tv_sec 
= (time_t)(milliseconds 
/ 1000); 
 112     tmReq
.tv_nsec 
= (milliseconds 
% 1000) * 1000 * 1000; 
 114     // we're not interested in remaining time nor in return value 
 115     (void)nanosleep(&tmReq
, (timespec 
*)NULL
); 
 116 #elif defined(HAVE_USLEEP) 
 117     // uncomment this if you feel brave or if you are sure that your version 
 118     // of Solaris has a safe usleep() function but please notice that usleep() 
 119     // is known to lead to crashes in MT programs in Solaris 2.[67] and is not 
 120     // documented as MT-Safe 
 121     #if defined(__SUN__) && wxUSE_THREADS 
 122         #error "usleep() cannot be used in MT programs under Solaris." 
 125     usleep(milliseconds 
* 1000); // usleep(3) wants microseconds 
 126 #elif defined(HAVE_SLEEP) 
 127     // under BeOS sleep() takes seconds (what about other platforms, if any?) 
 128     sleep(milliseconds 
* 1000); 
 129 #else // !sleep function 
 130     #error "usleep() or nanosleep() function required for wxUsleep" 
 131 #endif // sleep function 
 134 // ---------------------------------------------------------------------------- 
 135 // process management 
 136 // ---------------------------------------------------------------------------- 
 138 int wxKill(long pid
, wxSignal sig
) 
 140     return kill((pid_t
)pid
, (int)sig
); 
 143 #define WXEXECUTE_NARGS   127 
 145 long wxExecute( const wxString
& command
, bool sync
, wxProcess 
*process 
) 
 147     wxCHECK_MSG( !command
.IsEmpty(), 0, wxT("can't exec empty command") ); 
 150     wxChar 
*argv
[WXEXECUTE_NARGS
]; 
 152     const wxChar 
*cptr 
= command
.c_str(); 
 153     wxChar quotechar 
= wxT('\0'); // is arg quoted? 
 154     bool escaped 
= FALSE
; 
 156     // split the command line in arguments 
 160         quotechar 
= wxT('\0'); 
 162         // eat leading whitespace: 
 163         while ( wxIsspace(*cptr
) ) 
 166         if ( *cptr 
== wxT('\'') || *cptr 
== wxT('"') ) 
 171             if ( *cptr 
== wxT('\\') && ! escaped 
) 
 178             // all other characters: 
 182             // have we reached the end of the argument? 
 183             if ( (*cptr 
== quotechar 
&& ! escaped
) 
 184                  || (quotechar 
== wxT('\0') && wxIsspace(*cptr
)) 
 185                  || *cptr 
== wxT('\0') ) 
 187                 wxASSERT_MSG( argc 
< WXEXECUTE_NARGS
, 
 188                               wxT("too many arguments in wxExecute") ); 
 190                 argv
[argc
] = new wxChar
[argument
.length() + 1]; 
 191                 wxStrcpy(argv
[argc
], argument
.c_str()); 
 194                 // if not at end of buffer, swallow last character: 
 198                 break; // done with this one, start over 
 204     // do execute the command 
 205     long lRc 
= wxExecute(argv
, sync
, process
); 
 210         delete [] argv
[argc
++]; 
 215 // ---------------------------------------------------------------------------- 
 217 // ---------------------------------------------------------------------------- 
 219 static wxString 
wxMakeShellCommand(const wxString
& command
) 
 224         // just an interactive shell 
 229         // execute command in a shell 
 230         cmd 
<< _T("/bin/sh -c '") << command 
<< _T('\''); 
 236 bool wxShell(const wxString
& command
) 
 238     return wxExecute(wxMakeShellCommand(command
), TRUE 
/* sync */) == 0; 
 241 bool wxShell(const wxString
& command
, wxArrayString
& output
) 
 243     wxCHECK_MSG( !!command
, FALSE
, _T("can't exec shell non interactively") ); 
 245     return wxExecute(wxMakeShellCommand(command
), output
); 
 250 void wxHandleProcessTermination(wxEndProcessData 
*proc_data
) 
 252     int pid 
= (proc_data
->pid 
> 0) ? proc_data
->pid 
: -(proc_data
->pid
); 
 254     // waitpid is POSIX so should be available everywhere, however on older 
 255     // systems wait() might be used instead in a loop (until the right pid 
 260     // wait for child termination and if waitpid() was interrupted, try again 
 263        rc 
= waitpid(pid
, &status
, 0); 
 265     while ( rc 
== -1 && errno 
== EINTR 
); 
 268     if( rc 
== -1 || ! (WIFEXITED(status
) || WIFSIGNALED(status
)) ) 
 270        wxLogSysError(_("Waiting for subprocess termination failed")); 
 271        /* AFAIK, this can only happen if something went wrong within 
 272           wxGTK, i.e. due to a race condition or some serious bug. 
 273           After having fixed the order of statements in 
 274           GTK_EndProcessDetector(). (KB) 
 279         // notify user about termination if required 
 280         if (proc_data
->process
) 
 282             proc_data
->process
->OnTerminate(proc_data
->pid
, 
 283                                             WEXITSTATUS(status
)); 
 286         if ( proc_data
->pid 
> 0 ) 
 292            // wxExecute() will know about it 
 293            proc_data
->exitcode 
= status
; 
 302 // ---------------------------------------------------------------------------- 
 303 // wxStream classes to support IO redirection in wxExecute 
 304 // ---------------------------------------------------------------------------- 
 306 class wxProcessFileInputStream 
: public wxInputStream
 
 309     wxProcessFileInputStream(int fd
) { m_fd 
= fd
; } 
 310     ~wxProcessFileInputStream() { close(m_fd
); } 
 312     virtual bool Eof() const; 
 315     size_t OnSysRead(void *buffer
, size_t bufsize
); 
 321 class wxProcessFileOutputStream 
: public wxOutputStream
 
 324     wxProcessFileOutputStream(int fd
) { m_fd 
= fd
; } 
 325     ~wxProcessFileOutputStream() { close(m_fd
); } 
 328     size_t OnSysWrite(const void *buffer
, size_t bufsize
); 
 334 bool wxProcessFileInputStream::Eof() const 
 336     if ( m_lasterror 
== wxSTREAM_EOF 
) 
 339     // check if there is any input available 
 346     FD_SET(m_fd
, &readfds
); 
 347     switch ( select(m_fd 
+ 1, &readfds
, NULL
, NULL
, &tv
) ) 
 350             wxLogSysError(_("Impossible to get child process input")); 
 357             wxFAIL_MSG(_T("unexpected select() return value")); 
 358             // still fall through 
 361             // input available: check if there is any 
 362             return wxInputStream::Eof(); 
 366 size_t wxProcessFileInputStream::OnSysRead(void *buffer
, size_t bufsize
) 
 368     int ret 
= read(m_fd
, buffer
, bufsize
); 
 371         m_lasterror 
= wxSTREAM_EOF
; 
 373     else if ( ret 
== -1 ) 
 375         m_lasterror 
= wxSTREAM_READ_ERROR
; 
 380         m_lasterror 
= wxSTREAM_NOERROR
; 
 386 size_t wxProcessFileOutputStream::OnSysWrite(const void *buffer
, size_t bufsize
) 
 388     int ret 
= write(m_fd
, buffer
, bufsize
); 
 391         m_lasterror 
= wxSTREAM_WRITE_ERROR
; 
 396         m_lasterror 
= wxSTREAM_NOERROR
; 
 402 long wxExecute(wxChar 
**argv
, 
 406     // for the sync execution, we return -1 to indicate failure, but for async 
 407     // cse we return 0 which is never a valid PID 
 408     long errorRetCode 
= sync 
? -1 : 0; 
 410     wxCHECK_MSG( *argv
, errorRetCode
, wxT("can't exec empty command") ); 
 414     char *mb_argv
[WXEXECUTE_NARGS
]; 
 416     while (argv
[mb_argc
]) 
 418         wxWX2MBbuf mb_arg 
= wxConvertWX2MB(argv
[mb_argc
]); 
 419         mb_argv
[mb_argc
] = strdup(mb_arg
); 
 422     mb_argv
[mb_argc
] = (char *) NULL
; 
 424     // this macro will free memory we used above 
 425     #define ARGS_CLEANUP                                 \ 
 426         for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \ 
 427             free(mb_argv[mb_argc]) 
 429     // no need for cleanup 
 432     wxChar 
**mb_argv 
= argv
; 
 433 #endif // Unicode/ANSI 
 437     int end_proc_detect
[2]; 
 438     if ( pipe(end_proc_detect
) == -1 ) 
 440         wxLogSysError( _("Pipe creation failed") ); 
 441         wxLogError( _("Failed to execute '%s'\n"), *argv 
); 
 449     // pipes for inter process communication 
 450     int pipeIn
[2],      // stdin 
 451         pipeOut
[2],     // stdout 
 452         pipeErr
[2];     // stderr 
 454     pipeIn
[0] = pipeIn
[1] = 
 455     pipeOut
[0] = pipeOut
[1] = 
 456     pipeErr
[0] = pipeErr
[1] = -1; 
 458     if ( process 
&& process
->IsRedirected() ) 
 460         if ( pipe(pipeIn
) == -1 || pipe(pipeOut
) == -1 || pipe(pipeErr
) == -1 ) 
 463             // free previously allocated resources 
 464             close(end_proc_detect
[0]); 
 465             close(end_proc_detect
[1]); 
 468             wxLogSysError( _("Pipe creation failed") ); 
 469             wxLogError( _("Failed to execute '%s'\n"), *argv 
); 
 484     if ( pid 
== -1 )     // error? 
 487         close(end_proc_detect
[0]); 
 488         close(end_proc_detect
[1]); 
 497         wxLogSysError( _("Fork failed") ); 
 503     else if ( pid 
== 0 )  // we're in child 
 506         close(end_proc_detect
[0]); // close reading side 
 509         // These lines close the open file descriptors to to avoid any 
 510         // input/output which might block the process or irritate the user. If 
 511         // one wants proper IO for the subprocess, the right thing to do is to 
 512         // start an xterm executing it. 
 515             for ( int fd 
= 0; fd 
< FD_SETSIZE
; fd
++ ) 
 517                 if ( fd 
== pipeIn
[0] || fd 
== pipeOut
[1] || fd 
== pipeErr
[1] 
 519                      || fd 
== end_proc_detect
[1] 
 523                     // don't close this one, we still need it 
 527                 // leave stderr opened too, it won't do any hurm 
 528                 if ( fd 
!= STDERR_FILENO 
) 
 533         // redirect stdio, stdout and stderr 
 534         if ( pipeIn
[0] != -1 ) 
 536             if ( dup2(pipeIn
[0], STDIN_FILENO
) == -1 || 
 537                  dup2(pipeOut
[1], STDOUT_FILENO
) == -1 || 
 538                  dup2(pipeErr
[1], STDERR_FILENO
) == -1 ) 
 540                 wxLogSysError(_("Failed to redirect child process input/output")); 
 548         execvp (*mb_argv
, mb_argv
); 
 550         // there is no return after successful exec() 
 553     else // we're in parent 
 557         // pipe initialization: construction of the wxStreams 
 558         if ( process 
&& process
->IsRedirected() ) 
 560             // These two streams are relative to this process. 
 561             wxOutputStream 
*outStream 
= new wxProcessFileOutputStream(pipeIn
[1]); 
 562             wxInputStream 
*inStream 
= new wxProcessFileInputStream(pipeOut
[0]); 
 563             wxInputStream 
*errStream 
= new wxProcessFileInputStream(pipeErr
[0]); 
 565             close(pipeIn
[0]); // close reading side 
 566             close(pipeOut
[1]); // close writing side 
 567             close(pipeErr
[1]); // close writing side 
 569             process
->SetPipeStreams(inStream
, outStream
, errStream
); 
 573         wxEndProcessData 
*data 
= new wxEndProcessData
; 
 577             // we may have process for capturing the program output, but it's 
 578             // not used in wxEndProcessData in the case of sync execution 
 579             data
->process 
= NULL
; 
 581             // sync execution: indicate it by negating the pid 
 583             data
->tag 
= wxAddProcessCallback(data
, end_proc_detect
[0]); 
 585             close(end_proc_detect
[1]); // close writing side 
 590             // it will be set to 0 from GTK_EndProcessDetector 
 591             while (data
->pid 
!= 0) 
 594             int exitcode 
= data
->exitcode
; 
 600         else // async execution 
 602             // async execution, nothing special to do - caller will be 
 603             // notified about the process termination if process != NULL, data 
 604             // will be deleted in GTK_EndProcessDetector 
 605             data
->process  
= process
; 
 607             data
->tag      
= wxAddProcessCallback(data
, end_proc_detect
[0]); 
 609             close(end_proc_detect
[1]); // close writing side 
 614         wxASSERT_MSG( sync
, wxT("async execution not supported yet") ); 
 617         if ( waitpid(pid
, &exitcode
, 0) == -1 || !WIFEXITED(exitcode
) ) 
 619             wxLogSysError(_("Waiting for subprocess termination failed")); 
 626    // VMS does not recognise exit as a return and complains about 
 628    // I think VMS is wrong in this 
 636 // ---------------------------------------------------------------------------- 
 637 // file and directory functions 
 638 // ---------------------------------------------------------------------------- 
 640 const wxChar
* wxGetHomeDir( wxString 
*home  
) 
 642     *home 
= wxGetUserHome( wxString() ); 
 644     if ( home
->IsEmpty() ) 
 648    if ( tmp
.Last() != wxT(']')) 
 649      if ( tmp
.Last() != wxT('/')) *home 
<< wxT('/'); 
 651     return home
->c_str(); 
 655 const wxMB2WXbuf 
wxGetUserHome( const wxString 
&user 
) 
 656 #else // just for binary compatibility -- there is no 'const' here 
 657 char *wxGetUserHome( const wxString 
&user 
) 
 660     struct passwd 
*who 
= (struct passwd 
*) NULL
; 
 666         if ((ptr 
= wxGetenv(wxT("HOME"))) != NULL
) 
 670         if ((ptr 
= wxGetenv(wxT("USER"))) != NULL 
|| (ptr 
= wxGetenv(wxT("LOGNAME"))) != NULL
) 
 672             who 
= getpwnam(wxConvertWX2MB(ptr
)); 
 675         // We now make sure the the user exists! 
 678             who 
= getpwuid(getuid()); 
 683       who 
= getpwnam (user
.mb_str()); 
 686     return wxConvertMB2WX(who 
? who
->pw_dir 
: 0); 
 689 // ---------------------------------------------------------------------------- 
 690 // network and user id routines 
 691 // ---------------------------------------------------------------------------- 
 693 // retrieve either the hostname or FQDN depending on platform (caller must 
 694 // check whether it's one or the other, this is why this function is for 
 696 static bool wxGetHostNameInternal(wxChar 
*buf
, int sz
) 
 698     wxCHECK_MSG( buf
, FALSE
, wxT("NULL pointer in wxGetHostNameInternal") ); 
 702     // we're using uname() which is POSIX instead of less standard sysinfo() 
 703 #if defined(HAVE_UNAME) 
 705     bool ok 
= uname(&uts
) != -1; 
 708         wxStrncpy(buf
, wxConvertMB2WX(uts
.nodename
), sz 
- 1); 
 711 #elif defined(HAVE_GETHOSTNAME) 
 712     bool ok 
= gethostname(buf
, sz
) != -1; 
 713 #else // no uname, no gethostname 
 714     wxFAIL_MSG(wxT("don't know host name for this machine")); 
 717 #endif // uname/gethostname 
 721         wxLogSysError(_("Cannot get the hostname")); 
 727 bool wxGetHostName(wxChar 
*buf
, int sz
) 
 729     bool ok 
= wxGetHostNameInternal(buf
, sz
); 
 733         // BSD systems return the FQDN, we only want the hostname, so extract 
 734         // it (we consider that dots are domain separators) 
 735         wxChar 
*dot 
= wxStrchr(buf
, wxT('.')); 
 746 bool wxGetFullHostName(wxChar 
*buf
, int sz
) 
 748     bool ok 
= wxGetHostNameInternal(buf
, sz
); 
 752         if ( !wxStrchr(buf
, wxT('.')) ) 
 754             struct hostent 
*host 
= gethostbyname(wxConvertWX2MB(buf
)); 
 757                 wxLogSysError(_("Cannot get the official hostname")); 
 763                 // the canonical name 
 764                 wxStrncpy(buf
, wxConvertMB2WX(host
->h_name
), sz
); 
 767         //else: it's already a FQDN (BSD behaves this way) 
 773 bool wxGetUserId(wxChar 
*buf
, int sz
) 
 778     if ((who 
= getpwuid(getuid ())) != NULL
) 
 780         wxStrncpy (buf
, wxConvertMB2WX(who
->pw_name
), sz 
- 1); 
 787 bool wxGetUserName(wxChar 
*buf
, int sz
) 
 792     if ((who 
= getpwuid (getuid ())) != NULL
) 
 794         // pw_gecos field in struct passwd is not standard 
 796        char *comma 
= strchr(who
->pw_gecos
, ','); 
 798            *comma 
= '\0'; // cut off non-name comment fields 
 799        wxStrncpy (buf
, wxConvertMB2WX(who
->pw_gecos
), sz 
- 1); 
 800 #else // !HAVE_PW_GECOS 
 801        wxStrncpy (buf
, wxConvertMB2WX(who
->pw_name
), sz 
- 1); 
 802 #endif // HAVE_PW_GECOS/!HAVE_PW_GECOS 
 809 wxString 
wxGetOsDescription() 
 811 #ifndef WXWIN_OS_DESCRIPTION 
 812     #error WXWIN_OS_DESCRIPTION should be defined in config.h by configure 
 814     return WXWIN_OS_DESCRIPTION
; 
 818 // this function returns the GUI toolkit version in GUI programs, but OS 
 819 // version in non-GUI ones 
 822 int wxGetOsVersion(int *majorVsn
, int *minorVsn
) 
 827     if ( sscanf(WXWIN_OS_DESCRIPTION
, "%s %d.%d", name
, &major
, &minor
) != 3 ) 
 829         // unreckognized uname string format 
 843 long wxGetFreeMemory() 
 845 #if defined(__LINUX__) 
 846     // get it from /proc/meminfo 
 847     FILE *fp 
= fopen("/proc/meminfo", "r"); 
 853         if ( fgets(buf
, WXSIZEOF(buf
), fp
) && fgets(buf
, WXSIZEOF(buf
), fp
) ) 
 855             long memTotal
, memUsed
; 
 856             sscanf(buf
, "Mem: %ld %ld %ld", &memTotal
, &memUsed
, &memFree
); 
 863 #elif defined(__SUN__) && defined(_SC_AVPHYS_PAGES) 
 864     return sysconf(_SC_AVPHYS_PAGES
)*sysconf(_SC_PAGESIZE
); 
 865 //#elif defined(__FREEBSD__) -- might use sysctl() to find it out, probably 
 872 // ---------------------------------------------------------------------------- 
 874 // ---------------------------------------------------------------------------- 
 876 bool wxSetEnv(const wxString
& variable
, const wxChar 
*value
) 
 878 #if defined(HAVE_SETENV) 
 879     return setenv(variable
.mb_str(), value 
? wxString(value
).mb_str().data() 
 880                                            : NULL
, 1 /* overwrite */) == 0; 
 881 #elif defined(HAVE_PUTENV) 
 882     wxString s 
= variable
; 
 884         s 
<< _T('=') << value
; 
 887     const char *p 
= s
.mb_str(); 
 889     // the string will be free()d by libc 
 890     char *buf 
= (char *)malloc(strlen(p
) + 1); 
 893     return putenv(buf
) == 0; 
 894 #else // no way to set an env var 
 899 // ---------------------------------------------------------------------------- 
 901 // ---------------------------------------------------------------------------- 
 903 #if wxUSE_ON_FATAL_EXCEPTION 
 907 static void wxFatalSignalHandler(wxTYPE_SA_HANDLER
) 
 911         // give the user a chance to do something special about this 
 912         wxTheApp
->OnFatalException(); 
 918 bool wxHandleFatalExceptions(bool doit
) 
 921     static bool s_savedHandlers 
= FALSE
; 
 922     static struct sigaction s_handlerFPE
, 
 928     if ( doit 
&& !s_savedHandlers 
) 
 930         // install the signal handler 
 931         struct sigaction act
; 
 933         // some systems extend it with non std fields, so zero everything 
 934         memset(&act
, 0, sizeof(act
)); 
 936         act
.sa_handler 
= wxFatalSignalHandler
; 
 937         sigemptyset(&act
.sa_mask
); 
 940         ok 
&= sigaction(SIGFPE
, &act
, &s_handlerFPE
) == 0; 
 941         ok 
&= sigaction(SIGILL
, &act
, &s_handlerILL
) == 0; 
 942         ok 
&= sigaction(SIGBUS
, &act
, &s_handlerBUS
) == 0; 
 943         ok 
&= sigaction(SIGSEGV
, &act
, &s_handlerSEGV
) == 0; 
 946             wxLogDebug(_T("Failed to install our signal handler.")); 
 949         s_savedHandlers 
= TRUE
; 
 951     else if ( s_savedHandlers 
) 
 953         // uninstall the signal handler 
 954         ok 
&= sigaction(SIGFPE
, &s_handlerFPE
, NULL
) == 0; 
 955         ok 
&= sigaction(SIGILL
, &s_handlerILL
, NULL
) == 0; 
 956         ok 
&= sigaction(SIGBUS
, &s_handlerBUS
, NULL
) == 0; 
 957         ok 
&= sigaction(SIGSEGV
, &s_handlerSEGV
, NULL
) == 0; 
 960             wxLogDebug(_T("Failed to uninstall our signal handler.")); 
 963         s_savedHandlers 
= FALSE
; 
 965     //else: nothing to do 
 970 #endif // wxUSE_ON_FATAL_EXCEPTION 
 972 // ---------------------------------------------------------------------------- 
 973 // error and debug output routines (deprecated, use wxLog) 
 974 // ---------------------------------------------------------------------------- 
 976 void wxDebugMsg( const char *format
, ... ) 
 979   va_start( ap
, format 
); 
 980   vfprintf( stderr
, format
, ap 
); 
 985 void wxError( const wxString 
&msg
, const wxString 
&title 
) 
 987   wxFprintf( stderr
, _("Error ") ); 
 988   if (!title
.IsNull()) wxFprintf( stderr
, wxT("%s "), WXSTRINGCAST(title
) ); 
 989   if (!msg
.IsNull()) wxFprintf( stderr
, wxT(": %s"), WXSTRINGCAST(msg
) ); 
 990   wxFprintf( stderr
, wxT(".\n") ); 
 993 void wxFatalError( const wxString 
&msg
, const wxString 
&title 
) 
 995   wxFprintf( stderr
, _("Error ") ); 
 996   if (!title
.IsNull()) wxFprintf( stderr
, wxT("%s "), WXSTRINGCAST(title
) ); 
 997   if (!msg
.IsNull()) wxFprintf( stderr
, wxT(": %s"), WXSTRINGCAST(msg
) ); 
 998   wxFprintf( stderr
, wxT(".\n") ); 
 999   exit(3); // the same exit code as for abort()