]> git.saurik.com Git - wxWidgets.git/blame - src/unix/utilsunx.cpp
wxMemoryStream doc 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
47#if 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
VZ
60 !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
61 defined(__osf__)
62 extern "C"
63 {
1363811b
VZ
64 #ifdef __SUN__
65 int usleep(unsigned int usec);
66 #else // !Sun
67 void usleep(unsigned long usec);
68 #endif // Sun/!Sun
518b5d2f
VZ
69 };
70#endif // Unices without usleep()
71
91b8de8d
RR
72#ifdef __EMX__
73 /* I copied this from the XFree86 diffs. AV. */
74extern void DosSleep(unsigned long);
75#define INCL_DOSPROCESS
76#include <os2.h>
77void usleep(unsigned long delay)
78{
79 DosSleep(delay ? (delay/1000l) : 1l);
80}
81#endif
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{
7bcb11d3 98#if 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);
7bcb11d3 105#elif 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{
05079acc 133 wxCHECK_MSG( !command.IsEmpty(), 0, _T("can't exec empty command") );
518b5d2f
VZ
134
135 int argc = 0;
05079acc 136 wxChar *argv[WXEXECUTE_NARGS];
fad866f4 137 wxString argument;
05079acc
OK
138 const wxChar *cptr = command.c_str();
139 wxChar quotechar = _T('\0'); // is arg quoted?
fad866f4 140 bool escaped = FALSE;
518b5d2f 141
0ed9a934 142 // split the command line in arguments
fad866f4
KB
143 do
144 {
05079acc
OK
145 argument=_T("");
146 quotechar = _T('\0');
0ed9a934 147
fad866f4 148 // eat leading whitespace:
05079acc 149 while ( wxIsspace(*cptr) )
fad866f4 150 cptr++;
0ed9a934 151
05079acc 152 if ( *cptr == _T('\'') || *cptr == _T('"') )
fad866f4 153 quotechar = *cptr++;
0ed9a934 154
fad866f4
KB
155 do
156 {
05079acc 157 if ( *cptr == _T('\\') && ! 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)
05079acc
OK
170 || (quotechar == _T('\0') && wxIsspace(*cptr))
171 || *cptr == _T('\0') )
fad866f4 172 {
0ed9a934 173 wxASSERT_MSG( argc < WXEXECUTE_NARGS,
05079acc 174 _T("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 )
05079acc 205 cmd.Printf(_T("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;
0ed9a934
VZ
220 if ( waitpid(pid, &status, 0) == -1 || !WIFEXITED(status) )
221 {
222 wxLogSysError(_("Waiting for subprocess termination failed"));
223 }
224 else
225 {
226 // notify user about termination if required
227 if (proc_data->process)
228 {
229 proc_data->process->OnTerminate(proc_data->pid,
230 WEXITSTATUS(status));
231 }
232 }
518b5d2f 233
0ed9a934
VZ
234 // clean up
235 if ( proc_data->pid > 0 )
518b5d2f
VZ
236 {
237 delete proc_data;
238 }
239 else
240 {
241 // wxExecute() will know about it
242 proc_data->exitcode = status;
243
244 proc_data->pid = 0;
245 }
246}
247
05079acc 248long wxExecute( wxChar **argv, bool sync, wxProcess *process )
518b5d2f 249{
05079acc 250 wxCHECK_MSG( *argv, 0, _T("can't exec empty command") );
518b5d2f
VZ
251
252 int end_proc_detect[2];
05079acc
OK
253#if wxUSE_UNICODE
254 int mb_argc = 0;
255 char *mb_argv[WXEXECUTE_NARGS];
256
257 while (argv[mb_argc]) {
dcf924a3 258 wxWX2MBbuf mb_arg = wxConvCurrent->cWX2MB(argv[mb_argc]);
05079acc
OK
259 mb_argv[mb_argc] = strdup(mb_arg);
260 mb_argc++;
261 }
262 mb_argv[mb_argc] = (char *) NULL;
263#else
264 wxChar **mb_argv = argv;
265#endif
518b5d2f
VZ
266
267 // create pipes
268 if (pipe(end_proc_detect) == -1)
269 {
270 wxLogSysError( _("Pipe creation failed") );
5fbecd99
OK
271#if wxUSE_UNICODE
272 mb_argc = 0;
273 while (mb_argv[mb_argc])
274 free(mb_argv[mb_argc++]);
275#endif
518b5d2f
VZ
276 return 0;
277 }
278
279 // fork the process
01bc089c 280#if HAVE_VFORK
518b5d2f
VZ
281 pid_t pid = vfork();
282#else
283 pid_t pid = fork();
284#endif
285 if (pid == -1)
286 {
287 wxLogSysError( _("Fork failed") );
5fbecd99
OK
288#if wxUSE_UNICODE
289 mb_argc = 0;
290 while (mb_argv[mb_argc])
291 free(mb_argv[mb_argc++]);
292#endif
518b5d2f
VZ
293 return 0;
294 }
295 else if (pid == 0)
296 {
297 // we're in child
298 close(end_proc_detect[0]); // close reading side
299
300 // These three lines close the open file descriptors to to avoid any
301 // input/output which might block the process or irritate the user. If
d6086ea6 302 // one wants proper IO for the subprocess, the right thing to do is
518b5d2f
VZ
303 // to start an xterm executing it.
304 if (sync == 0)
305 {
306 // leave stderr opened, it won't do any hurm
307 for ( int fd = 0; fd < FD_SETSIZE; fd++ )
308 {
309 if ( fd != end_proc_detect[1] && fd != STDERR_FILENO )
310 close(fd);
311 }
312 }
313
314#if 0
315 close(STDERR_FILENO);
316
317 // some programs complain about stderr not being open, so redirect
318 // them:
319 open("/dev/null", O_RDONLY); // stdin
320 open("/dev/null", O_WRONLY); // stdout
321 open("/dev/null", O_WRONLY); // stderr
322#endif
323
05079acc 324 execvp (*mb_argv, mb_argv);
518b5d2f
VZ
325
326 // there is no return after successful exec()
05079acc 327 wxFprintf(stderr, _("Can't execute '%s'\n"), *argv);
518b5d2f
VZ
328
329 _exit(-1);
330 }
331 else
332 {
333 // we're in parent
334 close(end_proc_detect[1]); // close writing side
335
336 wxEndProcessData *data = new wxEndProcessData;
337 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
338
05079acc
OK
339#if wxUSE_UNICODE
340 mb_argc = 0;
341 while (mb_argv[mb_argc])
342 free(mb_argv[mb_argc++]);
343#endif
344
518b5d2f
VZ
345 if ( sync )
346 {
05079acc 347 wxASSERT_MSG( !process, _T("wxProcess param ignored for sync exec") );
518b5d2f
VZ
348 data->process = NULL;
349
350 // sync execution: indicate it by negating the pid
351 data->pid = -pid;
352
353 // it will be set to 0 from GTK_EndProcessDetector
354 while (data->pid != 0)
355 wxYield();
356
357 int exitcode = data->exitcode;
358
359 delete data;
360
361 return exitcode;
362 }
363 else
364 {
365 // async execution, nothing special to do - caller will be
366 // notified about the process terminationif process != NULL, data
367 // will be deleted in GTK_EndProcessDetector
368 data->process = process;
369 data->pid = pid;
370
371 return pid;
372 }
373 }
374}
375
376// ----------------------------------------------------------------------------
377// file and directory functions
378// ----------------------------------------------------------------------------
379
05079acc 380const wxChar* wxGetHomeDir( wxString *home )
518b5d2f
VZ
381{
382 *home = wxGetUserHome( wxString() );
383 if ( home->IsEmpty() )
05079acc 384 *home = _T("/");
518b5d2f
VZ
385
386 return home->c_str();
387}
388
05079acc
OK
389#if wxUSE_UNICODE
390const wxMB2WXbuf wxGetUserHome( const wxString &user )
391#else // just for binary compatibility
518b5d2f 392char *wxGetUserHome( const wxString &user )
05079acc 393#endif
518b5d2f
VZ
394{
395 struct passwd *who = (struct passwd *) NULL;
396
0fb67cd1 397 if ( !user )
518b5d2f 398 {
05079acc 399 register wxChar *ptr;
518b5d2f 400
05079acc 401 if ((ptr = wxGetenv(_T("HOME"))) != NULL)
518b5d2f
VZ
402 {
403 return ptr;
404 }
05079acc 405 if ((ptr = wxGetenv(_T("USER"))) != NULL || (ptr = wxGetenv(_T("LOGNAME"))) != NULL)
518b5d2f 406 {
dcf924a3 407 who = getpwnam(wxConvCurrent->cWX2MB(ptr));
518b5d2f
VZ
408 }
409
410 // We now make sure the the user exists!
411 if (who == NULL)
412 {
413 who = getpwuid(getuid());
414 }
415 }
416 else
417 {
05079acc 418 who = getpwnam (user.mb_str());
518b5d2f
VZ
419 }
420
05079acc 421#if wxUSE_UNICODE
dcf924a3 422 return who ? wxConvCurrent->cMB2WX(who->pw_dir) : (wxMB2WXbuf)((wxChar*)NULL);
05079acc
OK
423#else
424 return who ? who->pw_dir : ((char*)NULL);
425#endif
518b5d2f
VZ
426}
427
428// ----------------------------------------------------------------------------
0fb67cd1 429// network and user id routines
518b5d2f
VZ
430// ----------------------------------------------------------------------------
431
0fb67cd1
VZ
432// retrieve either the hostname or FQDN depending on platform (caller must
433// check whether it's one or the other, this is why this function is for
434// private use only)
05079acc 435static bool wxGetHostNameInternal(wxChar *buf, int sz)
518b5d2f 436{
05079acc 437 wxCHECK_MSG( buf, FALSE, _T("NULL pointer in wxGetHostNameInternal") );
518b5d2f 438
05079acc 439 *buf = _T('\0');
518b5d2f
VZ
440
441 // we're using uname() which is POSIX instead of less standard sysinfo()
442#if defined(HAVE_UNAME)
cc743a6f 443 struct utsname uts;
518b5d2f
VZ
444 bool ok = uname(&uts) != -1;
445 if ( ok )
446 {
dcf924a3 447 wxStrncpy(buf, wxConvCurrent->cMB2WX(uts.nodename), sz - 1);
05079acc 448 buf[sz] = _T('\0');
518b5d2f
VZ
449 }
450#elif defined(HAVE_GETHOSTNAME)
451 bool ok = gethostname(buf, sz) != -1;
0fb67cd1 452#else // no uname, no gethostname
05079acc 453 wxFAIL_MSG(_T("don't know host name for this machine"));
518b5d2f
VZ
454
455 bool ok = FALSE;
0fb67cd1 456#endif // uname/gethostname
518b5d2f
VZ
457
458 if ( !ok )
459 {
460 wxLogSysError(_("Cannot get the hostname"));
461 }
462
463 return ok;
464}
465
05079acc 466bool wxGetHostName(wxChar *buf, int sz)
0fb67cd1
VZ
467{
468 bool ok = wxGetHostNameInternal(buf, sz);
469
470 if ( ok )
471 {
472 // BSD systems return the FQDN, we only want the hostname, so extract
473 // it (we consider that dots are domain separators)
05079acc 474 wxChar *dot = wxStrchr(buf, _T('.'));
0fb67cd1
VZ
475 if ( dot )
476 {
477 // nuke it
05079acc 478 *dot = _T('\0');
0fb67cd1
VZ
479 }
480 }
481
482 return ok;
483}
484
05079acc 485bool wxGetFullHostName(wxChar *buf, int sz)
0fb67cd1
VZ
486{
487 bool ok = wxGetHostNameInternal(buf, sz);
488
489 if ( ok )
490 {
05079acc 491 if ( !wxStrchr(buf, _T('.')) )
0fb67cd1 492 {
dcf924a3 493 struct hostent *host = gethostbyname(wxConvCurrent->cWX2MB(buf));
0fb67cd1
VZ
494 if ( !host )
495 {
496 wxLogSysError(_("Cannot get the official hostname"));
497
498 ok = FALSE;
499 }
500 else
501 {
502 // the canonical name
dcf924a3 503 wxStrncpy(buf, wxConvCurrent->cMB2WX(host->h_name), sz);
0fb67cd1
VZ
504 }
505 }
506 //else: it's already a FQDN (BSD behaves this way)
507 }
508
509 return ok;
510}
511
05079acc 512bool wxGetUserId(wxChar *buf, int sz)
518b5d2f
VZ
513{
514 struct passwd *who;
515
05079acc 516 *buf = _T('\0');
518b5d2f
VZ
517 if ((who = getpwuid(getuid ())) != NULL)
518 {
dcf924a3 519 wxStrncpy (buf, wxConvCurrent->cMB2WX(who->pw_name), sz - 1);
518b5d2f
VZ
520 return TRUE;
521 }
522
523 return FALSE;
524}
525
05079acc 526bool wxGetUserName(wxChar *buf, int sz)
518b5d2f
VZ
527{
528 struct passwd *who;
529 char *comma;
530
05079acc 531 *buf = _T('\0');
518b5d2f
VZ
532 if ((who = getpwuid (getuid ())) != NULL) {
533 comma = strchr(who->pw_gecos, ',');
534 if (comma)
535 *comma = '\0'; // cut off non-name comment fields
dcf924a3 536 wxStrncpy (buf, wxConvCurrent->cMB2WX(who->pw_gecos), sz - 1);
518b5d2f
VZ
537 return TRUE;
538 }
539
540 return FALSE;
541}
542
543// ----------------------------------------------------------------------------
544// error and debug output routines (deprecated, use wxLog)
545// ----------------------------------------------------------------------------
546
547void wxDebugMsg( const char *format, ... )
548{
549 va_list ap;
550 va_start( ap, format );
551 vfprintf( stderr, format, ap );
552 fflush( stderr );
553 va_end(ap);
554}
555
556void wxError( const wxString &msg, const wxString &title )
557{
05079acc
OK
558 wxFprintf( stderr, _("Error ") );
559 if (!title.IsNull()) wxFprintf( stderr, _T("%s "), WXSTRINGCAST(title) );
560 if (!msg.IsNull()) wxFprintf( stderr, _T(": %s"), WXSTRINGCAST(msg) );
561 wxFprintf( stderr, _T(".\n") );
518b5d2f
VZ
562}
563
564void wxFatalError( const wxString &msg, const wxString &title )
565{
05079acc
OK
566 wxFprintf( stderr, _("Error ") );
567 if (!title.IsNull()) wxFprintf( stderr, _T("%s "), WXSTRINGCAST(title) );
568 if (!msg.IsNull()) wxFprintf( stderr, _T(": %s"), WXSTRINGCAST(msg) );
569 wxFprintf( stderr, _T(".\n") );
518b5d2f
VZ
570 exit(3); // the same exit code as for abort()
571}