]> git.saurik.com Git - wxWidgets.git/blame - src/unix/utilsunx.cpp
wxMSW update for CW, wxMac updated
[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"
23
24#include "wx/utils.h"
25#include "wx/process.h"
26
27#include "wx/unix/execute.h"
28
29#include <stdarg.h>
30#include <dirent.h>
31#include <string.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <unistd.h>
35#include <sys/wait.h>
36#include <pwd.h>
37#include <errno.h>
38#include <netdb.h>
39#include <signal.h>
40#include <fcntl.h> // for O_WRONLY and friends
41#include <time.h> // nanosleep() and/or usleep()
fad866f4 42#include <ctype.h> // isspace()
0ed9a934 43
7bcb11d3
JS
44// JACS: needed for FD_SETSIZE
45#include <sys/time.h>
46
0fcdf6dc 47#ifdef HAVE_UNAME
518b5d2f
VZ
48 #include <sys/utsname.h> // for uname()
49#endif // HAVE_UNAME
50
51// ----------------------------------------------------------------------------
52// conditional compilation
53// ----------------------------------------------------------------------------
54
55// many versions of Unices have this function, but it is not defined in system
56// headers - please add your system here if it is the case for your OS.
57// SunOS < 5.6 (i.e. Solaris < 2.6) and DG-UX are like this.
1363811b
VZ
58#if !defined(HAVE_USLEEP) && \
59 (defined(__SUN__) && !defined(__SunOs_5_6) && \
518b5d2f 60 !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
fd9811b1 61 defined(__osf__) || defined(__EMX__)
518b5d2f
VZ
62 extern "C"
63 {
1363811b
VZ
64 #ifdef __SUN__
65 int usleep(unsigned int usec);
66 #else // !Sun
9f2d09aa 67 #ifdef __EMX__
e6daf794
RR
68 /* I copied this from the XFree86 diffs. AV. */
69 #define INCL_DOSPROCESS
70 #include <os2.h>
71 void usleep(unsigned long delay)
72 {
73 DosSleep(delay ? (delay/1000l) : 1l);
74 }
e90c1d2a
VZ
75 #else
76 void usleep(unsigned long usec);
77 #endif
e6daf794 78 #endif // Sun/EMX/Something else
518b5d2f 79 };
e6daf794 80#define HAVE_USLEEP 1
518b5d2f
VZ
81#endif // Unices without usleep()
82
518b5d2f
VZ
83// ============================================================================
84// implementation
85// ============================================================================
86
87// ----------------------------------------------------------------------------
88// sleeping
89// ----------------------------------------------------------------------------
90
91void wxSleep(int nSecs)
92{
93 sleep(nSecs);
94}
95
96void wxUsleep(unsigned long milliseconds)
97{
0fcdf6dc 98#ifdef HAVE_NANOSLEEP
518b5d2f
VZ
99 timespec tmReq;
100 tmReq.tv_sec = milliseconds / 1000;
101 tmReq.tv_nsec = (milliseconds % 1000) * 1000 * 1000;
102
103 // we're not interested in remaining time nor in return value
104 (void)nanosleep(&tmReq, (timespec *)NULL);
0fcdf6dc 105#elif defined( HAVE_USLEEP )
518b5d2f
VZ
106 // uncomment this if you feel brave or if you are sure that your version
107 // of Solaris has a safe usleep() function but please notice that usleep()
108 // is known to lead to crashes in MT programs in Solaris 2.[67] and is not
109 // documented as MT-Safe
ea18eed9 110 #if defined(__SUN__) && wxUSE_THREADS
518b5d2f
VZ
111 #error "usleep() cannot be used in MT programs under Solaris."
112 #endif // Sun
113
114 usleep(milliseconds * 1000); // usleep(3) wants microseconds
115#else // !sleep function
116 #error "usleep() or nanosleep() function required for wxUsleep"
117#endif // sleep function
118}
119
120// ----------------------------------------------------------------------------
121// process management
122// ----------------------------------------------------------------------------
123
0fb67cd1 124int wxKill(long pid, wxSignal sig)
518b5d2f 125{
0fb67cd1 126 return kill(pid, (int)sig);
518b5d2f
VZ
127}
128
fad866f4
KB
129#define WXEXECUTE_NARGS 127
130
518b5d2f
VZ
131long wxExecute( const wxString& command, bool sync, wxProcess *process )
132{
223d09f6 133 wxCHECK_MSG( !command.IsEmpty(), 0, wxT("can't exec empty command") );
518b5d2f
VZ
134
135 int argc = 0;
05079acc 136 wxChar *argv[WXEXECUTE_NARGS];
fad866f4 137 wxString argument;
05079acc 138 const wxChar *cptr = command.c_str();
223d09f6 139 wxChar quotechar = wxT('\0'); // is arg quoted?
fad866f4 140 bool escaped = FALSE;
518b5d2f 141
0ed9a934 142 // split the command line in arguments
fad866f4
KB
143 do
144 {
223d09f6
KB
145 argument=wxT("");
146 quotechar = wxT('\0');
0ed9a934 147
fad866f4 148 // eat leading whitespace:
05079acc 149 while ( wxIsspace(*cptr) )
fad866f4 150 cptr++;
0ed9a934 151
223d09f6 152 if ( *cptr == wxT('\'') || *cptr == wxT('"') )
fad866f4 153 quotechar = *cptr++;
0ed9a934 154
fad866f4
KB
155 do
156 {
223d09f6 157 if ( *cptr == wxT('\\') && ! escaped )
fad866f4
KB
158 {
159 escaped = TRUE;
160 cptr++;
161 continue;
162 }
0ed9a934 163
fad866f4 164 // all other characters:
0ed9a934 165 argument += *cptr++;
fad866f4 166 escaped = FALSE;
0ed9a934
VZ
167
168 // have we reached the end of the argument?
169 if ( (*cptr == quotechar && ! escaped)
223d09f6
KB
170 || (quotechar == wxT('\0') && wxIsspace(*cptr))
171 || *cptr == wxT('\0') )
fad866f4 172 {
0ed9a934 173 wxASSERT_MSG( argc < WXEXECUTE_NARGS,
223d09f6 174 wxT("too many arguments in wxExecute") );
0ed9a934 175
05079acc
OK
176 argv[argc] = new wxChar[argument.length() + 1];
177 wxStrcpy(argv[argc], argument.c_str());
fad866f4 178 argc++;
0ed9a934 179
fad866f4 180 // if not at end of buffer, swallow last character:
0ed9a934
VZ
181 if(*cptr)
182 cptr++;
183
fad866f4
KB
184 break; // done with this one, start over
185 }
0ed9a934
VZ
186 } while(*cptr);
187 } while(*cptr);
fad866f4 188 argv[argc] = NULL;
0ed9a934
VZ
189
190 // do execute the command
518b5d2f
VZ
191 long lRc = wxExecute(argv, sync, process);
192
0ed9a934 193 // clean up
fad866f4 194 argc = 0;
0ed9a934 195 while( argv[argc] )
fad866f4 196 delete [] argv[argc++];
518b5d2f
VZ
197
198 return lRc;
199}
200
201bool wxShell(const wxString& command)
202{
203 wxString cmd;
204 if ( !!command )
223d09f6 205 cmd.Printf(wxT("xterm -e %s"), command.c_str());
518b5d2f
VZ
206 else
207 cmd = command;
208
209 return wxExecute(cmd) != 0;
210}
211
212void wxHandleProcessTermination(wxEndProcessData *proc_data)
213{
214 int pid = (proc_data->pid > 0) ? proc_data->pid : -(proc_data->pid);
215
0ed9a934
VZ
216 // waitpid is POSIX so should be available everywhere, however on older
217 // systems wait() might be used instead in a loop (until the right pid
218 // terminates)
518b5d2f 219 int status = 0;
ab857a4e
KB
220 int rc;
221
222 do
223 rc = waitpid(pid, &status, 0);
224 while(rc == -1 && ( /* errno == ERESTARTSYS || */ errno == EINTR) );
225 // waitpid() was interrupted, try again
226
227
228 if( rc == -1 || ! (WIFEXITED(status) || WIFSIGNALED(status)) )
0ed9a934 229 {
ab857a4e
KB
230 wxLogSysError(_("Waiting for subprocess termination failed"));
231 /* AFAIK, this can only happen if something went wrong within
232 wxGTK, i.e. due to a racecondition or some serious bug.
233 After having fixed the order of statements in
234 GTK_EndProcessDetector(). (KB)
235 */
0ed9a934
VZ
236 }
237 else
238 {
239 // notify user about termination if required
240 if (proc_data->process)
241 {
242 proc_data->process->OnTerminate(proc_data->pid,
243 WEXITSTATUS(status));
244 }
ab857a4e
KB
245 // clean up
246 if ( proc_data->pid > 0 )
247 {
248 delete proc_data;
249 }
250 else
251 {
252 // wxExecute() will know about it
253 proc_data->exitcode = status;
254
255 proc_data->pid = 0;
256 }
518b5d2f
VZ
257 }
258}
259
05079acc 260long wxExecute( wxChar **argv, bool sync, wxProcess *process )
518b5d2f 261{
223d09f6 262 wxCHECK_MSG( *argv, 0, wxT("can't exec empty command") );
518b5d2f 263
05079acc
OK
264#if wxUSE_UNICODE
265 int mb_argc = 0;
266 char *mb_argv[WXEXECUTE_NARGS];
267
e90c1d2a
VZ
268 while (argv[mb_argc])
269 {
270 wxWX2MBbuf mb_arg = wxConvertWX2MB(argv[mb_argc]);
05079acc
OK
271 mb_argv[mb_argc] = strdup(mb_arg);
272 mb_argc++;
273 }
274 mb_argv[mb_argc] = (char *) NULL;
e90c1d2a
VZ
275
276 // this macro will free memory we used above
277 #define ARGS_CLEANUP \
345b0247 278 for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
e90c1d2a
VZ
279 free(mb_argv[mb_argc])
280#else // ANSI
281 // no need for cleanup
282 #define ARGS_CLEANUP
283
05079acc 284 wxChar **mb_argv = argv;
e90c1d2a 285#endif // Unicode/ANSI
518b5d2f 286
e90c1d2a 287#if wxUSE_GUI
518b5d2f 288 // create pipes
e90c1d2a 289 int end_proc_detect[2];
518b5d2f
VZ
290 if (pipe(end_proc_detect) == -1)
291 {
292 wxLogSysError( _("Pipe creation failed") );
e90c1d2a
VZ
293
294 ARGS_CLEANUP;
295
518b5d2f
VZ
296 return 0;
297 }
e90c1d2a 298#endif // wxUSE_GUI
518b5d2f
VZ
299
300 // fork the process
0fcdf6dc 301#ifdef HAVE_VFORK
518b5d2f
VZ
302 pid_t pid = vfork();
303#else
304 pid_t pid = fork();
305#endif
306 if (pid == -1)
307 {
308 wxLogSysError( _("Fork failed") );
e90c1d2a
VZ
309
310 ARGS_CLEANUP;
311
518b5d2f
VZ
312 return 0;
313 }
314 else if (pid == 0)
315 {
e90c1d2a 316#if wxUSE_GUI
518b5d2f
VZ
317 // we're in child
318 close(end_proc_detect[0]); // close reading side
e90c1d2a 319#endif // wxUSE_GUI
518b5d2f
VZ
320
321 // These three lines close the open file descriptors to to avoid any
322 // input/output which might block the process or irritate the user. If
d6086ea6 323 // one wants proper IO for the subprocess, the right thing to do is
518b5d2f
VZ
324 // to start an xterm executing it.
325 if (sync == 0)
326 {
327 // leave stderr opened, it won't do any hurm
328 for ( int fd = 0; fd < FD_SETSIZE; fd++ )
329 {
e90c1d2a
VZ
330#if wxUSE_GUI
331 if ( fd == end_proc_detect[1] )
332 continue;
333#endif // wxUSE_GUI
334
335 if ( fd != STDERR_FILENO )
518b5d2f
VZ
336 close(fd);
337 }
338 }
339
340#if 0
341 close(STDERR_FILENO);
342
343 // some programs complain about stderr not being open, so redirect
344 // them:
345 open("/dev/null", O_RDONLY); // stdin
346 open("/dev/null", O_WRONLY); // stdout
347 open("/dev/null", O_WRONLY); // stderr
348#endif
349
05079acc 350 execvp (*mb_argv, mb_argv);
518b5d2f
VZ
351
352 // there is no return after successful exec()
05079acc 353 wxFprintf(stderr, _("Can't execute '%s'\n"), *argv);
518b5d2f
VZ
354
355 _exit(-1);
356 }
357 else
358 {
e90c1d2a 359#if wxUSE_GUI
518b5d2f 360 wxEndProcessData *data = new wxEndProcessData;
ab857a4e 361
518b5d2f 362
e90c1d2a 363 ARGS_CLEANUP;
05079acc 364
518b5d2f
VZ
365 if ( sync )
366 {
223d09f6 367 wxASSERT_MSG( !process, wxT("wxProcess param ignored for sync exec") );
518b5d2f
VZ
368 data->process = NULL;
369
370 // sync execution: indicate it by negating the pid
371 data->pid = -pid;
ab857a4e
KB
372 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
373 // we're in parent
374 close(end_proc_detect[1]); // close writing side
518b5d2f
VZ
375
376 // it will be set to 0 from GTK_EndProcessDetector
377 while (data->pid != 0)
378 wxYield();
379
380 int exitcode = data->exitcode;
381
382 delete data;
383
384 return exitcode;
385 }
386 else
387 {
388 // async execution, nothing special to do - caller will be
ab857a4e 389 // notified about the process termination if process != NULL, data
518b5d2f
VZ
390 // will be deleted in GTK_EndProcessDetector
391 data->process = process;
392 data->pid = pid;
ab857a4e
KB
393 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
394 // we're in parent
395 close(end_proc_detect[1]); // close writing side
518b5d2f
VZ
396
397 return pid;
398 }
e90c1d2a 399#else // !wxUSE_GUI
223d09f6 400 wxASSERT_MSG( sync, wxT("async execution not supported yet") );
e90c1d2a
VZ
401
402 int exitcode = 0;
403 if ( waitpid(pid, &exitcode, 0) == -1 || !WIFEXITED(exitcode) )
404 {
405 wxLogSysError(_("Waiting for subprocess termination failed"));
406 }
407
408 return exitcode;
409#endif // wxUSE_GUI
518b5d2f 410 }
338dd992 411 return 0;
e90c1d2a
VZ
412
413 #undef ARGS_CLEANUP
518b5d2f
VZ
414}
415
416// ----------------------------------------------------------------------------
417// file and directory functions
418// ----------------------------------------------------------------------------
419
05079acc 420const wxChar* wxGetHomeDir( wxString *home )
518b5d2f
VZ
421{
422 *home = wxGetUserHome( wxString() );
423 if ( home->IsEmpty() )
223d09f6 424 *home = wxT("/");
518b5d2f
VZ
425
426 return home->c_str();
427}
428
05079acc
OK
429#if wxUSE_UNICODE
430const wxMB2WXbuf wxGetUserHome( const wxString &user )
e90c1d2a 431#else // just for binary compatibility -- there is no 'const' here
518b5d2f 432char *wxGetUserHome( const wxString &user )
05079acc 433#endif
518b5d2f
VZ
434{
435 struct passwd *who = (struct passwd *) NULL;
436
0fb67cd1 437 if ( !user )
518b5d2f 438 {
e90c1d2a 439 wxChar *ptr;
518b5d2f 440
223d09f6 441 if ((ptr = wxGetenv(wxT("HOME"))) != NULL)
518b5d2f
VZ
442 {
443 return ptr;
444 }
223d09f6 445 if ((ptr = wxGetenv(wxT("USER"))) != NULL || (ptr = wxGetenv(wxT("LOGNAME"))) != NULL)
518b5d2f 446 {
e90c1d2a 447 who = getpwnam(wxConvertWX2MB(ptr));
518b5d2f
VZ
448 }
449
450 // We now make sure the the user exists!
451 if (who == NULL)
452 {
453 who = getpwuid(getuid());
454 }
455 }
456 else
457 {
05079acc 458 who = getpwnam (user.mb_str());
518b5d2f
VZ
459 }
460
af111fc3 461 return wxConvertMB2WX(who ? who->pw_dir : 0);
518b5d2f
VZ
462}
463
464// ----------------------------------------------------------------------------
0fb67cd1 465// network and user id routines
518b5d2f
VZ
466// ----------------------------------------------------------------------------
467
0fb67cd1
VZ
468// retrieve either the hostname or FQDN depending on platform (caller must
469// check whether it's one or the other, this is why this function is for
470// private use only)
05079acc 471static bool wxGetHostNameInternal(wxChar *buf, int sz)
518b5d2f 472{
223d09f6 473 wxCHECK_MSG( buf, FALSE, wxT("NULL pointer in wxGetHostNameInternal") );
518b5d2f 474
223d09f6 475 *buf = wxT('\0');
518b5d2f
VZ
476
477 // we're using uname() which is POSIX instead of less standard sysinfo()
478#if defined(HAVE_UNAME)
cc743a6f 479 struct utsname uts;
518b5d2f
VZ
480 bool ok = uname(&uts) != -1;
481 if ( ok )
482 {
e90c1d2a 483 wxStrncpy(buf, wxConvertMB2WX(uts.nodename), sz - 1);
223d09f6 484 buf[sz] = wxT('\0');
518b5d2f
VZ
485 }
486#elif defined(HAVE_GETHOSTNAME)
487 bool ok = gethostname(buf, sz) != -1;
0fb67cd1 488#else // no uname, no gethostname
223d09f6 489 wxFAIL_MSG(wxT("don't know host name for this machine"));
518b5d2f
VZ
490
491 bool ok = FALSE;
0fb67cd1 492#endif // uname/gethostname
518b5d2f
VZ
493
494 if ( !ok )
495 {
496 wxLogSysError(_("Cannot get the hostname"));
497 }
498
499 return ok;
500}
501
05079acc 502bool wxGetHostName(wxChar *buf, int sz)
0fb67cd1
VZ
503{
504 bool ok = wxGetHostNameInternal(buf, sz);
505
506 if ( ok )
507 {
508 // BSD systems return the FQDN, we only want the hostname, so extract
509 // it (we consider that dots are domain separators)
223d09f6 510 wxChar *dot = wxStrchr(buf, wxT('.'));
0fb67cd1
VZ
511 if ( dot )
512 {
513 // nuke it
223d09f6 514 *dot = wxT('\0');
0fb67cd1
VZ
515 }
516 }
517
518 return ok;
519}
520
05079acc 521bool wxGetFullHostName(wxChar *buf, int sz)
0fb67cd1
VZ
522{
523 bool ok = wxGetHostNameInternal(buf, sz);
524
525 if ( ok )
526 {
223d09f6 527 if ( !wxStrchr(buf, wxT('.')) )
0fb67cd1 528 {
e90c1d2a 529 struct hostent *host = gethostbyname(wxConvertWX2MB(buf));
0fb67cd1
VZ
530 if ( !host )
531 {
532 wxLogSysError(_("Cannot get the official hostname"));
533
534 ok = FALSE;
535 }
536 else
537 {
538 // the canonical name
e90c1d2a 539 wxStrncpy(buf, wxConvertMB2WX(host->h_name), sz);
0fb67cd1
VZ
540 }
541 }
542 //else: it's already a FQDN (BSD behaves this way)
543 }
544
545 return ok;
546}
547
05079acc 548bool wxGetUserId(wxChar *buf, int sz)
518b5d2f
VZ
549{
550 struct passwd *who;
551
223d09f6 552 *buf = wxT('\0');
518b5d2f
VZ
553 if ((who = getpwuid(getuid ())) != NULL)
554 {
e90c1d2a 555 wxStrncpy (buf, wxConvertMB2WX(who->pw_name), sz - 1);
518b5d2f
VZ
556 return TRUE;
557 }
558
559 return FALSE;
560}
561
05079acc 562bool wxGetUserName(wxChar *buf, int sz)
518b5d2f
VZ
563{
564 struct passwd *who;
565 char *comma;
566
223d09f6 567 *buf = wxT('\0');
518b5d2f 568 if ((who = getpwuid (getuid ())) != NULL) {
0fcdf6dc 569#ifndef __VMS__
518b5d2f
VZ
570 comma = strchr(who->pw_gecos, ',');
571 if (comma)
572 *comma = '\0'; // cut off non-name comment fields
e90c1d2a 573 wxStrncpy (buf, wxConvertMB2WX(who->pw_gecos), sz - 1);
0fcdf6dc
JJ
574#else
575 wxStrncpy (buf, wxConvertMB2WX(who->pw_name), sz - 1);
576#endif
518b5d2f
VZ
577 return TRUE;
578 }
579
580 return FALSE;
581}
582
583// ----------------------------------------------------------------------------
584// error and debug output routines (deprecated, use wxLog)
585// ----------------------------------------------------------------------------
586
587void wxDebugMsg( const char *format, ... )
588{
589 va_list ap;
590 va_start( ap, format );
591 vfprintf( stderr, format, ap );
592 fflush( stderr );
593 va_end(ap);
594}
595
596void wxError( const wxString &msg, const wxString &title )
597{
05079acc 598 wxFprintf( stderr, _("Error ") );
223d09f6
KB
599 if (!title.IsNull()) wxFprintf( stderr, wxT("%s "), WXSTRINGCAST(title) );
600 if (!msg.IsNull()) wxFprintf( stderr, wxT(": %s"), WXSTRINGCAST(msg) );
601 wxFprintf( stderr, wxT(".\n") );
518b5d2f
VZ
602}
603
604void wxFatalError( const wxString &msg, const wxString &title )
605{
05079acc 606 wxFprintf( stderr, _("Error ") );
223d09f6
KB
607 if (!title.IsNull()) wxFprintf( stderr, wxT("%s "), WXSTRINGCAST(title) );
608 if (!msg.IsNull()) wxFprintf( stderr, wxT(": %s"), WXSTRINGCAST(msg) );
609 wxFprintf( stderr, wxT(".\n") );
518b5d2f
VZ
610 exit(3); // the same exit code as for abort()
611}
93ccaed8 612