avoid needless Unicode<->MB conversions in Unix wxExecute(); simplify the code; provide both versions taking char** and wchar_t** for compatibility; also use wxMacExecute() (renamed to wxMacLaunch() to avoid confusion) from all wxExecute() overloads but don't use it if wxEXEC_SYNC was requested as it doesn't support it

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@52722 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Vadim Zeitlin
2008-03-23 02:27:23 +00:00
parent 0c1cc9483e
commit 05718a98f9
5 changed files with 255 additions and 219 deletions

View File

@@ -347,10 +347,15 @@ enum
// If flags contain wxEXEC_SYNC, return -1 on failure and the exit code of the // If flags contain wxEXEC_SYNC, return -1 on failure and the exit code of the
// process if everything was ok. Otherwise (i.e. if wxEXEC_ASYNC), return 0 on // process if everything was ok. Otherwise (i.e. if wxEXEC_ASYNC), return 0 on
// failure and the PID of the launched process if ok. // failure and the PID of the launched process if ok.
WXDLLIMPEXP_BASE long wxExecute(wxChar **argv, int flags = wxEXEC_ASYNC, WXDLLIMPEXP_BASE long wxExecute(wchar_t **argv,
wxProcess *process = (wxProcess *) NULL); int flags = wxEXEC_ASYNC,
WXDLLIMPEXP_BASE long wxExecute(const wxString& command, int flags = wxEXEC_ASYNC, wxProcess *process = NULL);
wxProcess *process = (wxProcess *) NULL); WXDLLIMPEXP_BASE long wxExecute(char **argv,
int flags = wxEXEC_ASYNC,
wxProcess *process = NULL);
WXDLLIMPEXP_BASE long wxExecute(const wxString& command,
int flags = wxEXEC_ASYNC,
wxProcess *process = NULL);
// execute the command capturing its output into an array line by line, this is // execute the command capturing its output into an array line by line, this is
// always synchronous // always synchronous

View File

@@ -624,8 +624,10 @@ wxString wxGetUserHome(const wxString& user = "");
array, any additional ones are the command parameters and the array must be array, any additional ones are the command parameters and the array must be
terminated with a @NULL pointer. terminated with a @NULL pointer.
@param flags @param flags
Combination of bit masks wxEXEC_ASYNC, Must include either wxEXEC_ASYNC or wxEXEC_SYNC and can also include
wxEXEC_SYNC and wxEXEC_NOHIDE wxEXEC_NOHIDE, wxEXEC_MAKE_GROUP_LEADER (in either case) or
wxEXEC_NODISABLE and wxEXEC_NOEVENTS or wxEXEC_BLOCK, which is equal to
their combination, in wxEXEC_SYNC case.
@param callback @param callback
An optional pointer to wxProcess An optional pointer to wxProcess
@@ -633,18 +635,22 @@ wxString wxGetUserHome(const wxString& user = "");
@header{wx/utils.h} @header{wx/utils.h}
*/ */
long wxExecute(const wxString& command, int sync = wxEXEC_ASYNC, long wxExecute(const wxString& command,
wxProcess* callback = NULL); int sync = wxEXEC_ASYNC,
wxPerl note: long wxExecute(char** argv, wxProcess* callback = NULL);
int flags = wxEXEC_ASYNC, long wxExecute(char** argv,
wxProcess* callback = NULL); int flags = wxEXEC_ASYNC,
wxPerl note: long wxExecute(const wxString& command, wxProcess* callback = NULL);
wxArrayString& output, long wxExecute(wchar_t** argv,
int flags = 0); int flags = wxEXEC_ASYNC,
wxPerl note: long wxExecute(const wxString& command, wxProcess* callback = NULL);
wxArrayString& output, long wxExecute(const wxString& command,
wxArrayString& errors, wxArrayString& output,
int flags = 0); int flags = 0);
long wxExecute(const wxString& command,
wxArrayString& output,
wxArrayString& errors,
int flags = 0);
//@} //@}
/** /**

View File

@@ -1,6 +1,6 @@
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// Name: mac/corefoundation/utilsexc_base.cpp // Name: mac/corefoundation/utilsexc_base.cpp
// Purpose: wxMacExecute // Purpose: wxMacLaunch
// Author: Ryan Norton // Author: Ryan Norton
// Modified by: // Modified by:
// Created: 2005-06-21 // Created: 2005-06-21
@@ -48,25 +48,19 @@
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// //
// wxMacExecute // wxMacLaunch
// //
// argv is the command line split up, with the application path first // argv is the command line split up, with the application path first
// flags are the flags from wxExecute // flags are the flags from wxExecute
// process is the process passed from wxExecute for pipe streams etc. // process is the process passed from wxExecute for pipe streams etc.
// returns -1 on error for wxEXEC_SYNC and 0 on error for wxEXEC_ASYNC // returns -1 on error for wxEXEC_SYNC and 0 on error for wxEXEC_ASYNC
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
long wxMacExecute(wxChar **argv, bool wxMacLaunch(char **argv)
int flags,
wxProcess *WXUNUSED(process))
{ {
// Semi-macros used for return value of wxMacExecute
const long errorCode = ((flags & wxEXEC_SYNC) ? -1 : 0);
const long successCode = ((flags & wxEXEC_SYNC) ? 0 : -1); // fake PID
// Obtains the number of arguments for determining the size of // Obtains the number of arguments for determining the size of
// the CFArray used to hold them // the CFArray used to hold them
CFIndex cfiCount = 0; CFIndex cfiCount = 0;
for(wxChar** argvcopy = argv; *argvcopy != NULL ; ++argvcopy) for(char** argvcopy = argv; *argvcopy != NULL ; ++argvcopy)
{ {
++cfiCount; ++cfiCount;
} }
@@ -75,8 +69,8 @@ long wxMacExecute(wxChar **argv,
// to launch // to launch
if(cfiCount == 0) if(cfiCount == 0)
{ {
wxLogDebug(wxT("wxMacExecute No file to launch!")); wxLogDebug(wxT("wxMacLaunch No file to launch!"));
return errorCode ; return false ;
} }
// Path to bundle // Path to bundle
@@ -94,8 +88,8 @@ long wxMacExecute(wxChar **argv,
// Check for error from the CFURL // Check for error from the CFURL
if(!cfurlApp) if(!cfurlApp)
{ {
wxLogDebug(wxT("wxMacExecute Can't open path: %s"), path.c_str()); wxLogDebug(wxT("wxMacLaunch Can't open path: %s"), path.c_str());
return errorCode ; return false ;
} }
// Create a CFBundle from the CFURL created earlier // Create a CFBundle from the CFURL created earlier
@@ -106,9 +100,9 @@ long wxMacExecute(wxChar **argv,
// at all (maybe a simple directory etc.) // at all (maybe a simple directory etc.)
if(!cfbApp) if(!cfbApp)
{ {
wxLogDebug(wxT("wxMacExecute Bad bundle: %s"), path.c_str()); wxLogDebug(wxT("wxMacLaunch Bad bundle: %s"), path.c_str());
CFRelease(cfurlApp); CFRelease(cfurlApp);
return errorCode ; return false ;
} }
// Get the bundle type and make sure its an 'APPL' bundle // Get the bundle type and make sure its an 'APPL' bundle
@@ -117,10 +111,10 @@ long wxMacExecute(wxChar **argv,
CFBundleGetPackageInfo(cfbApp, &dwBundleType, &dwBundleCreator); CFBundleGetPackageInfo(cfbApp, &dwBundleType, &dwBundleCreator);
if(dwBundleType != 'APPL') if(dwBundleType != 'APPL')
{ {
wxLogDebug(wxT("wxMacExecute Not an APPL bundle: %s"), path.c_str()); wxLogDebug(wxT("wxMacLaunch Not an APPL bundle: %s"), path.c_str());
CFRelease(cfbApp); CFRelease(cfbApp);
CFRelease(cfurlApp); CFRelease(cfurlApp);
return errorCode ; return false ;
} }
// Create a CFArray for dealing with the command line // Create a CFArray for dealing with the command line
@@ -129,10 +123,10 @@ long wxMacExecute(wxChar **argv,
cfiCount-1, &kCFTypeArrayCallBacks); cfiCount-1, &kCFTypeArrayCallBacks);
if(!cfaFiles) //This should never happen if(!cfaFiles) //This should never happen
{ {
wxLogDebug(wxT("wxMacExecute Could not create CFMutableArray")); wxLogDebug(wxT("wxMacLaunch Could not create CFMutableArray"));
CFRelease(cfbApp); CFRelease(cfbApp);
CFRelease(cfurlApp); CFRelease(cfurlApp);
return errorCode ; return false ;
} }
// Loop through command line arguments to the bundle, // Loop through command line arguments to the bundle,
@@ -184,7 +178,7 @@ long wxMacExecute(wxChar **argv,
if(!cfurlCurrentFile) if(!cfurlCurrentFile)
{ {
wxLogDebug( wxLogDebug(
wxT("wxMacExecute Could not create CFURL for argument:%s"), wxT("wxMacLaunch Could not create CFURL for argument:%s"),
*argv); *argv);
continue; continue;
} }
@@ -222,12 +216,12 @@ long wxMacExecute(wxChar **argv,
// Check for error from LSOpenFromURLSpec // Check for error from LSOpenFromURLSpec
if(status != noErr) if(status != noErr)
{ {
wxLogDebug(wxT("wxMacExecute LSOpenFromURLSpec Error: %d"), wxLogDebug(wxT("wxMacLaunch LSOpenFromURLSpec Error: %d"),
(int)status); (int)status);
return errorCode ; return false ;
} }
// No error from LSOpenFromURLSpec, so app was launched // No error from LSOpenFromURLSpec, so app was launched
return successCode; return true ;
} }

View File

@@ -1012,9 +1012,11 @@ long wxExecute(const wxString& cmd, int flags, wxProcess *handler)
return dwExitCode; return dwExitCode;
} }
long wxExecute(wxChar **argv, int flags, wxProcess *handler) template <typename CharType>
long wxExecuteImpl(CharType **argv, int flags, wxProcess *handler)
{ {
wxString command; wxString command;
command.reserve(1024);
for ( ;; ) for ( ;; )
{ {
@@ -1022,8 +1024,18 @@ long wxExecute(wxChar **argv, int flags, wxProcess *handler)
if ( !*argv ) if ( !*argv )
break; break;
command += _T(' '); command += ' ';
} }
return wxExecute(command, flags, handler); return wxExecute(command, flags, handler);
} }
long wxExecute(char **argv, int flags, wxProcess *handler)
{
return wxExecuteImpl(argv, flags, handler);
}
long wxExecute(wchar_t **argv, int flags, wxProcess *handler)
{
return wxExecuteImpl(argv, flags, handler);
}

View File

@@ -263,139 +263,6 @@ int wxKill(long pid, wxSignal sig, wxKillError *rc, int flags)
return err; return err;
} }
#define WXEXECUTE_NARGS 127
#if defined(__DARWIN__)
long wxMacExecute(wxChar **argv,
int flags,
wxProcess *process);
#endif
long wxExecute( const wxString& command, int flags, wxProcess *process )
{
wxCHECK_MSG( !command.empty(), 0, wxT("can't exec empty command") );
wxLogTrace(wxT("exec"), wxT("Executing \"%s\""), command.c_str());
#if wxUSE_THREADS
// fork() doesn't mix well with POSIX threads: on many systems the program
// deadlocks or crashes for some reason. Probably our code is buggy and
// doesn't do something which must be done to allow this to work, but I
// don't know what yet, so for now just warn the user (this is the least we
// can do) about it
wxASSERT_MSG( wxThread::IsMain(),
_T("wxExecute() can be called only from the main thread") );
#endif // wxUSE_THREADS
int argc = 0;
wxChar *argv[WXEXECUTE_NARGS];
wxString argument;
const wxChar *cptr = command.c_str();
wxChar quotechar = wxT('\0'); // is arg quoted?
bool escaped = false;
// split the command line in arguments
do
{
argument = wxEmptyString;
quotechar = wxT('\0');
// eat leading whitespace:
while ( wxIsspace(*cptr) )
cptr++;
if ( *cptr == wxT('\'') || *cptr == wxT('"') )
quotechar = *cptr++;
do
{
if ( *cptr == wxT('\\') && ! escaped )
{
escaped = true;
cptr++;
continue;
}
// all other characters:
argument += *cptr++;
escaped = false;
// have we reached the end of the argument?
if ( (*cptr == quotechar && ! escaped)
|| (quotechar == wxT('\0') && wxIsspace(*cptr))
|| *cptr == wxT('\0') )
{
wxASSERT_MSG( argc < WXEXECUTE_NARGS,
wxT("too many arguments in wxExecute") );
argv[argc] = new wxChar[argument.length() + 1];
wxStrcpy(argv[argc], argument.c_str());
argc++;
// if not at end of buffer, swallow last character:
if(*cptr)
cptr++;
break; // done with this one, start over
}
} while(*cptr);
} while(*cptr);
argv[argc] = NULL;
long lRc;
#if defined(__DARWIN__)
// wxMacExecute only executes app bundles.
// It returns an error code if the target is not an app bundle, thus falling
// through to the regular wxExecute for non app bundles.
lRc = wxMacExecute(argv, flags, process);
if( lRc != ((flags & wxEXEC_SYNC) ? -1 : 0))
return lRc;
#endif
// do execute the command
lRc = wxExecute(argv, flags, process);
// clean up
argc = 0;
while( argv[argc] )
delete [] argv[argc++];
return lRc;
}
// ----------------------------------------------------------------------------
// wxShell
// ----------------------------------------------------------------------------
static wxString wxMakeShellCommand(const wxString& command)
{
wxString cmd;
if ( !command )
{
// just an interactive shell
cmd = _T("xterm");
}
else
{
// execute command in a shell
cmd << _T("/bin/sh -c '") << command << _T('\'');
}
return cmd;
}
bool wxShell(const wxString& command)
{
return wxExecute(wxMakeShellCommand(command), wxEXEC_SYNC) == 0;
}
bool wxShell(const wxString& command, wxArrayString& output)
{
wxCHECK_MSG( !command.empty(), false, _T("can't exec shell non interactively") );
return wxExecute(wxMakeShellCommand(command), output);
}
// Shutdown or reboot the PC // Shutdown or reboot the PC
bool wxShutdown(wxShutdownFlags wFlags) bool wxShutdown(wxShutdownFlags wFlags)
{ {
@@ -465,10 +332,174 @@ bool wxPipeInputStream::CanRead() const
#endif // HAS_PIPE_INPUT_STREAM #endif // HAS_PIPE_INPUT_STREAM
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// wxExecute: the real worker function // wxShell
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
long wxExecute(wxChar **argv, int flags, wxProcess *process) static wxString wxMakeShellCommand(const wxString& command)
{
wxString cmd;
if ( !command )
{
// just an interactive shell
cmd = _T("xterm");
}
else
{
// execute command in a shell
cmd << _T("/bin/sh -c '") << command << _T('\'');
}
return cmd;
}
bool wxShell(const wxString& command)
{
return wxExecute(wxMakeShellCommand(command), wxEXEC_SYNC) == 0;
}
bool wxShell(const wxString& command, wxArrayString& output)
{
wxCHECK_MSG( !command.empty(), false, _T("can't exec shell non interactively") );
return wxExecute(wxMakeShellCommand(command), output);
}
namespace
{
// helper class for storing arguments as char** array suitable for passing to
// execvp(), whatever form they were passed to us
class ArgsArray
{
public:
ArgsArray(const wxArrayString& args)
{
Init(args.size());
for ( int i = 0; i < m_argc; i++ )
{
m_argv[i] = wxStrdup(args[i]);
}
}
ArgsArray(wchar_t **wargv)
{
int argc = 0;
while ( *wargv++ )
argc++;
Init(argc);
for ( int i = 0; i < m_argc; i++ )
{
m_argv[i] = wxSafeConvertWX2MB(wargv[i]).release();
}
}
~ArgsArray()
{
for ( int i = 0; i < m_argc; i++ )
{
free(m_argv[i]);
}
delete [] m_argv;
}
operator char**() const { return m_argv; }
private:
void Init(int argc)
{
m_argc = argc;
m_argv = new char *[m_argc + 1];
m_argv[m_argc] = NULL;
}
int m_argc;
char **m_argv;
DECLARE_NO_COPY_CLASS(ArgsArray);
};
} // anonymous namespace
// ----------------------------------------------------------------------------
// wxExecute implementations
// ----------------------------------------------------------------------------
#if defined(__DARWIN__)
bool wxMacLaunch(char **argv);
#endif
long wxExecute(const wxString& command, int flags, wxProcess *process)
{
wxArrayString args;
const char *cptr = command.c_str();
// split the command line in arguments
//
// TODO: combine this with wxCmdLineParser::ConvertStringToArgs(), it
// doesn't do exactly the same thing right now but it's pretty close
// and we shouldn't maintain 2 copies of this code
do
{
wxString argument;
char quotechar = '\0'; // is arg quoted?
bool escaped = false;
// eat leading whitespace:
while ( wxIsspace(*cptr) )
cptr++;
if ( *cptr == '\'' || *cptr == '"' )
quotechar = *cptr++;
do
{
if ( *cptr == '\\' && !escaped )
{
escaped = true;
cptr++;
continue;
}
// all other characters:
argument += *cptr++;
escaped = false;
// have we reached the end of the argument?
if ( (*cptr == quotechar && !escaped)
|| (quotechar == '\0' && wxIsspace(*cptr))
|| *cptr == '\0' )
{
args.push_back(argument);
// if not at end of buffer, swallow last character:
if ( *cptr )
cptr++;
break; // done with this one, start over
}
} while ( *cptr );
} while ( *cptr );
ArgsArray argv(args);
// do execute the command
return wxExecute(argv, flags, process);
}
long wxExecute(wchar_t **wargv, int flags, wxProcess *process)
{
ArgsArray argv(wargv);
return wxExecute(argv, flags, process);
}
// wxExecute: the real worker function
long wxExecute(char **argv, int flags, wxProcess *process)
{ {
// for the sync execution, we return -1 to indicate failure, but for async // for the sync execution, we return -1 to indicate failure, but for async
// case we return 0 which is never a valid PID // case we return 0 which is never a valid PID
@@ -479,38 +510,29 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") ); wxCHECK_MSG( *argv, ERROR_RETURN_CODE, wxT("can't exec empty command") );
#if wxUSE_UNICODE #if wxUSE_THREADS
int mb_argc = 0; // fork() doesn't mix well with POSIX threads: on many systems the program
char *mb_argv[WXEXECUTE_NARGS]; // deadlocks or crashes for some reason. Probably our code is buggy and
// doesn't do something which must be done to allow this to work, but I
// don't know what yet, so for now just warn the user (this is the least we
// can do) about it
wxASSERT_MSG( wxThread::IsMain(),
_T("wxExecute() can be called only from the main thread") );
#endif // wxUSE_THREADS
while (argv[mb_argc]) #if defined(__DARWIN__)
// wxMacLaunch() only executes app bundles and only does it asynchronously.
// It returns false if the target is not an app bundle, thus falling
// through to the regular code for non app bundles.
if ( !(flags & wxEXEC_SYNC) && wxMacLaunch(argv) )
{ {
wxWX2MBbuf mb_arg = wxSafeConvertWX2MB(argv[mb_argc]); // we don't have any PID to return so just make up something non null
mb_argv[mb_argc] = strdup(mb_arg); return -1;
mb_argc++;
} }
mb_argv[mb_argc] = (char *) NULL; #endif // __DARWIN__
// this macro will free memory we used above
#define ARGS_CLEANUP \
for ( mb_argc = 0; mb_argv[mb_argc]; mb_argc++ ) \
free(mb_argv[mb_argc])
#else // ANSI
// no need for cleanup
#define ARGS_CLEANUP
wxChar **mb_argv = argv; // this struct contains all information which we use for housekeeping
#endif // Unicode/ANSI
// we want this function to work even if there is no wxApp so ensure that
// we have a valid traits pointer
wxConsoleAppTraits traitsConsole;
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
if ( !traits )
traits = &traitsConsole;
// this struct contains all information which we pass to and from
// wxAppTraits methods
wxExecuteData execData; wxExecuteData execData;
execData.flags = flags; execData.flags = flags;
execData.process = process; execData.process = process;
@@ -520,8 +542,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
{ {
wxLogError( _("Failed to execute '%s'\n"), *argv ); wxLogError( _("Failed to execute '%s'\n"), *argv );
ARGS_CLEANUP;
return ERROR_RETURN_CODE; return ERROR_RETURN_CODE;
} }
@@ -536,8 +556,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
{ {
wxLogError( _("Failed to execute '%s'\n"), *argv ); wxLogError( _("Failed to execute '%s'\n"), *argv );
ARGS_CLEANUP;
return ERROR_RETURN_CODE; return ERROR_RETURN_CODE;
} }
} }
@@ -557,8 +575,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
{ {
wxLogSysError( _("Fork failed") ); wxLogSysError( _("Fork failed") );
ARGS_CLEANUP;
return ERROR_RETURN_CODE; return ERROR_RETURN_CODE;
} }
else if ( pid == 0 ) // we're in child else if ( pid == 0 ) // we're in child
@@ -618,12 +634,11 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
pipeErr.Close(); pipeErr.Close();
} }
execvp (*mb_argv, mb_argv); execvp(*argv, argv);
fprintf(stderr, "execvp("); fprintf(stderr, "execvp(");
// CS changed ppc to ppc_ as ppc is not available under mac os CW Mach-O for ( char **a = argv; *a; a++ )
for ( char **ppc_ = mb_argv; *ppc_; ppc_++ ) fprintf(stderr, "%s%s", a == argv ? "" : ", ", *a);
fprintf(stderr, "%s%s", ppc_ == mb_argv ? "" : ", ", *ppc_);
fprintf(stderr, ") failed with error %d!\n", errno); fprintf(stderr, ") failed with error %d!\n", errno);
// there is no return after successful exec() // there is no return after successful exec()
@@ -641,8 +656,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
} }
else // we're in parent else // we're in parent
{ {
ARGS_CLEANUP;
// save it for WaitForChild() use // save it for WaitForChild() use
execData.pid = pid; execData.pid = pid;
@@ -685,6 +698,13 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
pipeErr.Close(); pipeErr.Close();
} }
// we want this function to work even if there is no wxApp so ensure
// that we have a valid traits pointer
wxConsoleAppTraits traitsConsole;
wxAppTraits *traits = wxTheApp ? wxTheApp->GetTraits() : NULL;
if ( !traits )
traits = &traitsConsole;
return traits->WaitForChild(execData); return traits->WaitForChild(execData);
} }
@@ -694,7 +714,6 @@ long wxExecute(wxChar **argv, int flags, wxProcess *process)
} }
#undef ERROR_RETURN_CODE #undef ERROR_RETURN_CODE
#undef ARGS_CLEANUP
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// file and directory functions // file and directory functions