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