]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/unix/utilsunx.cpp
test
[wxWidgets.git] / src / unix / utilsunx.cpp
... / ...
CommitLineData
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()
42#include <ctype.h> // isspace()
43
44// JACS: needed for FD_SETSIZE
45#include <sys/time.h>
46
47#ifdef HAVE_UNAME
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.
58#if !defined(HAVE_USLEEP) && \
59 (defined(__SUN__) && !defined(__SunOs_5_6) && \
60 !defined(__SunOs_5_7) && !defined(__SUNPRO_CC)) || \
61 defined(__osf__) || defined(__EMX__)
62 extern "C"
63 {
64 #ifdef __SUN__
65 int usleep(unsigned int usec);
66 #else // !Sun
67 #ifdef __EMX__
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 }
75 #else
76 void usleep(unsigned long usec);
77 #endif
78 #endif // Sun/EMX/Something else
79 };
80#define HAVE_USLEEP 1
81#endif // Unices without usleep()
82
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{
98#ifdef HAVE_NANOSLEEP
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);
105#elif defined( HAVE_USLEEP )
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
110 #if defined(__SUN__) && wxUSE_THREADS
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
124int wxKill(long pid, wxSignal sig)
125{
126 return kill(pid, (int)sig);
127}
128
129#define WXEXECUTE_NARGS 127
130
131long wxExecute( const wxString& command, bool sync, wxProcess *process )
132{
133 wxCHECK_MSG( !command.IsEmpty(), 0, wxT("can't exec empty command") );
134
135 int argc = 0;
136 wxChar *argv[WXEXECUTE_NARGS];
137 wxString argument;
138 const wxChar *cptr = command.c_str();
139 wxChar quotechar = wxT('\0'); // is arg quoted?
140 bool escaped = FALSE;
141
142 // split the command line in arguments
143 do
144 {
145 argument=wxT("");
146 quotechar = wxT('\0');
147
148 // eat leading whitespace:
149 while ( wxIsspace(*cptr) )
150 cptr++;
151
152 if ( *cptr == wxT('\'') || *cptr == wxT('"') )
153 quotechar = *cptr++;
154
155 do
156 {
157 if ( *cptr == wxT('\\') && ! escaped )
158 {
159 escaped = TRUE;
160 cptr++;
161 continue;
162 }
163
164 // all other characters:
165 argument += *cptr++;
166 escaped = FALSE;
167
168 // have we reached the end of the argument?
169 if ( (*cptr == quotechar && ! escaped)
170 || (quotechar == wxT('\0') && wxIsspace(*cptr))
171 || *cptr == wxT('\0') )
172 {
173 wxASSERT_MSG( argc < WXEXECUTE_NARGS,
174 wxT("too many arguments in wxExecute") );
175
176 argv[argc] = new wxChar[argument.length() + 1];
177 wxStrcpy(argv[argc], argument.c_str());
178 argc++;
179
180 // if not at end of buffer, swallow last character:
181 if(*cptr)
182 cptr++;
183
184 break; // done with this one, start over
185 }
186 } while(*cptr);
187 } while(*cptr);
188 argv[argc] = NULL;
189
190 // do execute the command
191 long lRc = wxExecute(argv, sync, process);
192
193 // clean up
194 argc = 0;
195 while( argv[argc] )
196 delete [] argv[argc++];
197
198 return lRc;
199}
200
201bool wxShell(const wxString& command)
202{
203 wxString cmd;
204 if ( !!command )
205 cmd.Printf(wxT("xterm -e %s"), command.c_str());
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
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)
219 int status = 0;
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 }
233
234 // clean up
235 if ( proc_data->pid > 0 )
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
248long wxExecute( wxChar **argv, bool sync, wxProcess *process )
249{
250 wxCHECK_MSG( *argv, 0, wxT("can't exec empty command") );
251
252#if wxUSE_UNICODE
253 int mb_argc = 0;
254 char *mb_argv[WXEXECUTE_NARGS];
255
256 while (argv[mb_argc])
257 {
258 wxWX2MBbuf mb_arg = wxConvertWX2MB(argv[mb_argc]);
259 mb_argv[mb_argc] = strdup(mb_arg);
260 mb_argc++;
261 }
262 mb_argv[mb_argc] = (char *) NULL;
263
264 // this macro will free memory we used above
265 #define ARGS_CLEANUP \
266 for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
267 free(mb_argv[mb_argc])
268#else // ANSI
269 // no need for cleanup
270 #define ARGS_CLEANUP
271
272 wxChar **mb_argv = argv;
273#endif // Unicode/ANSI
274
275#if wxUSE_GUI
276 // create pipes
277 int end_proc_detect[2];
278 if (pipe(end_proc_detect) == -1)
279 {
280 wxLogSysError( _("Pipe creation failed") );
281
282 ARGS_CLEANUP;
283
284 return 0;
285 }
286#endif // wxUSE_GUI
287
288 // fork the process
289#ifdef HAVE_VFORK
290 pid_t pid = vfork();
291#else
292 pid_t pid = fork();
293#endif
294 if (pid == -1)
295 {
296 wxLogSysError( _("Fork failed") );
297
298 ARGS_CLEANUP;
299
300 return 0;
301 }
302 else if (pid == 0)
303 {
304#if wxUSE_GUI
305 // we're in child
306 close(end_proc_detect[0]); // close reading side
307#endif // wxUSE_GUI
308
309 // These three lines close the open file descriptors to to avoid any
310 // input/output which might block the process or irritate the user. If
311 // one wants proper IO for the subprocess, the right thing to do is
312 // to start an xterm executing it.
313 if (sync == 0)
314 {
315 // leave stderr opened, it won't do any hurm
316 for ( int fd = 0; fd < FD_SETSIZE; fd++ )
317 {
318#if wxUSE_GUI
319 if ( fd == end_proc_detect[1] )
320 continue;
321#endif // wxUSE_GUI
322
323 if ( fd != STDERR_FILENO )
324 close(fd);
325 }
326 }
327
328#if 0
329 close(STDERR_FILENO);
330
331 // some programs complain about stderr not being open, so redirect
332 // them:
333 open("/dev/null", O_RDONLY); // stdin
334 open("/dev/null", O_WRONLY); // stdout
335 open("/dev/null", O_WRONLY); // stderr
336#endif
337
338 execvp (*mb_argv, mb_argv);
339
340 // there is no return after successful exec()
341 wxFprintf(stderr, _("Can't execute '%s'\n"), *argv);
342
343 _exit(-1);
344 }
345 else
346 {
347#if wxUSE_GUI
348 // we're in parent
349 close(end_proc_detect[1]); // close writing side
350
351 wxEndProcessData *data = new wxEndProcessData;
352 data->tag = wxAddProcessCallback(data, end_proc_detect[0]);
353
354 ARGS_CLEANUP;
355
356 if ( sync )
357 {
358 wxASSERT_MSG( !process, wxT("wxProcess param ignored for sync exec") );
359 data->process = NULL;
360
361 // sync execution: indicate it by negating the pid
362 data->pid = -pid;
363
364 // it will be set to 0 from GTK_EndProcessDetector
365 while (data->pid != 0)
366 wxYield();
367
368 int exitcode = data->exitcode;
369
370 delete data;
371
372 return exitcode;
373 }
374 else
375 {
376 // async execution, nothing special to do - caller will be
377 // notified about the process terminationif process != NULL, data
378 // will be deleted in GTK_EndProcessDetector
379 data->process = process;
380 data->pid = pid;
381
382 return pid;
383 }
384#else // !wxUSE_GUI
385 wxASSERT_MSG( sync, wxT("async execution not supported yet") );
386
387 int exitcode = 0;
388 if ( waitpid(pid, &exitcode, 0) == -1 || !WIFEXITED(exitcode) )
389 {
390 wxLogSysError(_("Waiting for subprocess termination failed"));
391 }
392
393 return exitcode;
394#endif // wxUSE_GUI
395 }
396 return 0;
397
398 #undef ARGS_CLEANUP
399}
400
401// ----------------------------------------------------------------------------
402// file and directory functions
403// ----------------------------------------------------------------------------
404
405const wxChar* wxGetHomeDir( wxString *home )
406{
407 *home = wxGetUserHome( wxString() );
408 if ( home->IsEmpty() )
409 *home = wxT("/");
410
411 return home->c_str();
412}
413
414#if wxUSE_UNICODE
415const wxMB2WXbuf wxGetUserHome( const wxString &user )
416#else // just for binary compatibility -- there is no 'const' here
417char *wxGetUserHome( const wxString &user )
418#endif
419{
420 struct passwd *who = (struct passwd *) NULL;
421
422 if ( !user )
423 {
424 wxChar *ptr;
425
426 if ((ptr = wxGetenv(wxT("HOME"))) != NULL)
427 {
428 return ptr;
429 }
430 if ((ptr = wxGetenv(wxT("USER"))) != NULL || (ptr = wxGetenv(wxT("LOGNAME"))) != NULL)
431 {
432 who = getpwnam(wxConvertWX2MB(ptr));
433 }
434
435 // We now make sure the the user exists!
436 if (who == NULL)
437 {
438 who = getpwuid(getuid());
439 }
440 }
441 else
442 {
443 who = getpwnam (user.mb_str());
444 }
445
446 return wxConvertMB2WX(who ? who->pw_dir : 0);
447}
448
449// ----------------------------------------------------------------------------
450// network and user id routines
451// ----------------------------------------------------------------------------
452
453// retrieve either the hostname or FQDN depending on platform (caller must
454// check whether it's one or the other, this is why this function is for
455// private use only)
456static bool wxGetHostNameInternal(wxChar *buf, int sz)
457{
458 wxCHECK_MSG( buf, FALSE, wxT("NULL pointer in wxGetHostNameInternal") );
459
460 *buf = wxT('\0');
461
462 // we're using uname() which is POSIX instead of less standard sysinfo()
463#if defined(HAVE_UNAME)
464 struct utsname uts;
465 bool ok = uname(&uts) != -1;
466 if ( ok )
467 {
468 wxStrncpy(buf, wxConvertMB2WX(uts.nodename), sz - 1);
469 buf[sz] = wxT('\0');
470 }
471#elif defined(HAVE_GETHOSTNAME)
472 bool ok = gethostname(buf, sz) != -1;
473#else // no uname, no gethostname
474 wxFAIL_MSG(wxT("don't know host name for this machine"));
475
476 bool ok = FALSE;
477#endif // uname/gethostname
478
479 if ( !ok )
480 {
481 wxLogSysError(_("Cannot get the hostname"));
482 }
483
484 return ok;
485}
486
487bool wxGetHostName(wxChar *buf, int sz)
488{
489 bool ok = wxGetHostNameInternal(buf, sz);
490
491 if ( ok )
492 {
493 // BSD systems return the FQDN, we only want the hostname, so extract
494 // it (we consider that dots are domain separators)
495 wxChar *dot = wxStrchr(buf, wxT('.'));
496 if ( dot )
497 {
498 // nuke it
499 *dot = wxT('\0');
500 }
501 }
502
503 return ok;
504}
505
506bool wxGetFullHostName(wxChar *buf, int sz)
507{
508 bool ok = wxGetHostNameInternal(buf, sz);
509
510 if ( ok )
511 {
512 if ( !wxStrchr(buf, wxT('.')) )
513 {
514 struct hostent *host = gethostbyname(wxConvertWX2MB(buf));
515 if ( !host )
516 {
517 wxLogSysError(_("Cannot get the official hostname"));
518
519 ok = FALSE;
520 }
521 else
522 {
523 // the canonical name
524 wxStrncpy(buf, wxConvertMB2WX(host->h_name), sz);
525 }
526 }
527 //else: it's already a FQDN (BSD behaves this way)
528 }
529
530 return ok;
531}
532
533bool wxGetUserId(wxChar *buf, int sz)
534{
535 struct passwd *who;
536
537 *buf = wxT('\0');
538 if ((who = getpwuid(getuid ())) != NULL)
539 {
540 wxStrncpy (buf, wxConvertMB2WX(who->pw_name), sz - 1);
541 return TRUE;
542 }
543
544 return FALSE;
545}
546
547bool wxGetUserName(wxChar *buf, int sz)
548{
549 struct passwd *who;
550 char *comma;
551
552 *buf = wxT('\0');
553 if ((who = getpwuid (getuid ())) != NULL) {
554#ifndef __VMS__
555 comma = strchr(who->pw_gecos, ',');
556 if (comma)
557 *comma = '\0'; // cut off non-name comment fields
558 wxStrncpy (buf, wxConvertMB2WX(who->pw_gecos), sz - 1);
559#else
560 wxStrncpy (buf, wxConvertMB2WX(who->pw_name), sz - 1);
561#endif
562 return TRUE;
563 }
564
565 return FALSE;
566}
567
568// ----------------------------------------------------------------------------
569// error and debug output routines (deprecated, use wxLog)
570// ----------------------------------------------------------------------------
571
572void wxDebugMsg( const char *format, ... )
573{
574 va_list ap;
575 va_start( ap, format );
576 vfprintf( stderr, format, ap );
577 fflush( stderr );
578 va_end(ap);
579}
580
581void wxError( const wxString &msg, const wxString &title )
582{
583 wxFprintf( stderr, _("Error ") );
584 if (!title.IsNull()) wxFprintf( stderr, wxT("%s "), WXSTRINGCAST(title) );
585 if (!msg.IsNull()) wxFprintf( stderr, wxT(": %s"), WXSTRINGCAST(msg) );
586 wxFprintf( stderr, wxT(".\n") );
587}
588
589void wxFatalError( const wxString &msg, const wxString &title )
590{
591 wxFprintf( stderr, _("Error ") );
592 if (!title.IsNull()) wxFprintf( stderr, wxT("%s "), WXSTRINGCAST(title) );
593 if (!msg.IsNull()) wxFprintf( stderr, wxT(": %s"), WXSTRINGCAST(msg) );
594 wxFprintf( stderr, wxT(".\n") );
595 exit(3); // the same exit code as for abort()
596}
597