sys.cpp revision e9b6af083e34e2397a8ddbe9781920733d09d151
/*
* System abstraction utility routines
*
* Authors:
* Jon A. Cruz <jon@joncruz.org>
*
* Copyright (C) 2004-2005 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <glibmm/fileutils.h>
#endif
#include <gtk/gtkmessagedialog.h>
#include "preferences.h"
#include "sys.h"
#ifdef WIN32
#define BYPASS_GLIB_SPAWN 1
#ifdef BYPASS_GLIB_SPAWN
#include <process.h> // declares spawn functions
#include <wchar.h> // declares _wspawn functions
#ifdef __cplusplus
extern "C" {
#endif
_CRTIMP int __cdecl __MINGW_NOTHROW _wspawnve (int, const wchar_t*, const wchar_t* const*, const wchar_t* const*);
_CRTIMP int __cdecl __MINGW_NOTHROW _wspawnvpe (int, const wchar_t*, const wchar_t* const*, const wchar_t* const*);
#ifdef __cplusplus
}
#endif
#include <unistd.h>
#include <fcntl.h>
#include <io.h>
#endif // BYPASS_GLIB_SPAWN
// For now to get at is_os_wide().
#endif // WIN32
//#define INK_DUMP_FILENAME_CONV 1
//#define INK_DUMP_FOPEN 1
extern guint update_in_progress;
#define DEBUG_MESSAGE(key, ...) \
{\
if ( dump )\
{\
g_message( __VA_ARGS__ );\
\
}\
if ( dumpD )\
{\
);\
dialog); \
}\
}
{
#ifdef INK_DUMP_FOPEN
for ( int i = 0; utf8name[i]; i++ )
{
if ( utf8name[i] == '\\' )
{
str += "\\\\";
}
{
}
else
{
}
}
#else
(void)utf8name;
(void)id;
#endif
}
{
#ifndef WIN32
if ( filename )
{
filename = 0;
}
#else
#endif
return fp;
}
{
int retval = -1;
#ifndef WIN32
if ( filename )
{
filename = 0;
}
#else
// Mode should be ingnored inside of glib on the way in
#endif
return retval;
}
/*
* Wrapper around Glib::file_open_tmp()
* Returns a handle to the temp file
* name_used contains the actual name used
*
* Returns:
* A file handle (as from open()) to the file opened for reading and writing.
* The file is opened in binary mode on platforms where there is a difference.
* The file handle should be closed with close().
*
* Note:
* On Windows Vista Glib::file_open_tmp fails with the current version of glibmm
* A special case is implemented for WIN32. This can be removed if the issue is fixed
* in future versions of glibmm
* */
{
#ifndef WIN32
#else
/* Special case for WIN32 due to a bug in glibmm
* (only needed for Windows Vista, but since there is only one windows build all builds get the workaround)
* The workaround can be removed if the bug is fixed in glibmm
*
* The code is mostly identical to the implementation in glibmm
* */
if(error)
return fileno;
#endif
}
{
bool exists = false;
if ( utf8name ) {
/* FIXME: Trying to guess whether or not a filename is already in utf8 is unreliable.
If any callers pass non-utf8 data (e.g. using g_get_home_dir), then change caller to
use simple g_file_test. Then add g_return_val_if_fail(g_utf_validate(...), false)
to beginning of this function. */
// Looks like g_get_home_dir isn't safe.
//g_warning("invalid UTF-8 detected internally. HUNT IT DOWN AND KILL IT!!!");
} else {
}
if ( filename ) {
} else {
g_warning( "Unable to convert filename in IO:file_test" );
}
}
return exists;
}
/** Wrapper around g_dir_open, but taking a utf8name as first argument. */
GDir *
{
if (opsys_name) {
return ret;
} else {
return NULL;
}
}
/**
* Like g_dir_read_name, but returns a utf8name (which must be freed, unlike g_dir_read_name).
*
* N.B. Skips over any dir entries that fail to convert to utf8.
*/
gchar *
{
for (;;) {
if (!opsys_name) {
return NULL;
}
if (utf8_name) {
return utf8_name;
}
}
}
{
if ( opsysstring ) {
if ( newFileName ) {
g_warning( "input filename did not yield UTF-8" );
g_free( newFileName );
} else {
}
newFileName = 0;
// This *might* be a case that we want
// g_warning( "input failed filename->utf8, fell back to original" );
// TODO handle cases when len >= 0
} else {
}
}
return result;
}
#ifdef BYPASS_GLIB_SPAWN
/*
this code was taken from the original glib sources
*/
#define GSPAWN_HELPER
enum
{
};
enum {
ARG_CHILD_ERR_REPORT = 1,
};
static int debug = 0;
#define HELPER_PROCESS "gspawn-win32-helper"
static int
dup_noninherited (int fd,
int mode)
{
GetCurrentProcess (), &filehandle,
0, FALSE, DUPLICATE_SAME_ACCESS);
}
/* The helper process writes a status report back to us, through a
* pipe, consisting of two ints.
*/
static gboolean
read_helper_report (int fd,
{
{
if (debug)
g_print ("%s:read_helper_report: read %d...\n",
if (debug)
if (chunk < 0)
{
/* Some weird shit happened, bail out */
_("Failed to read from child pipe (%s)"),
g_strerror (errno));
return FALSE;
}
else if (chunk == 0)
{
_("Failed to read from child pipe (%s)"),
"EOF");
break; /* EOF */
}
else
}
return FALSE;
return TRUE;
}
static void
const gchar *working_directory,
{
switch (report[0])
{
case CHILD_CHDIR_FAILED:
_("Failed to change to directory '%s' (%s)"),
break;
case CHILD_SPAWN_FAILED:
_("Failed to execute child process (%s)"),
break;
default:
}
}
static gchar *
{
while (*p)
{
if (*p == ' ' || *p == '\t')
else if (*p == '"')
len++;
else if (*p == '\\')
{
pp++;
if (*pp == '"')
len++;
}
len++;
p++;
}
p = string;
if (need_dblquotes)
*q++ = '"';
while (*p)
{
if (*p == '"')
*q++ = '\\';
else if (*p == '\\')
{
pp++;
if (*pp == '"')
*q++ = '\\';
}
*q++ = *p;
p++;
}
if (need_dblquotes)
*q++ = '"';
*q++ = '\0';
return retval;
}
static gint
{
gint i;
++argc;
/* Quote each argv element if necessary, so that it will get
* reconstructed correctly in the C runtime startup code. Note that
* the unquoting algorithm in the C runtime is really weird, and
* rather different than what Unix shells do. See stdargv.c in the C
*
* Note that an new_argv[0] constructed by this function should
* *not* be passed as the filename argument to a spawn* or exec*
* family function. That argument should be the real file name
* without any quoting.
*/
for (i = 0; i < argc; i++)
return argc;
}
static gboolean
utf8_charv_to_wcharv (char **utf8_charv,
wchar_t ***wcharv,
int *error_index,
{
if (utf8_charv != NULL)
{
int n = 0, i;
while (utf8_charv[n])
n++;
for (i = 0; i < n; i++)
{
{
if (error_index)
*error_index = i;
while (i)
return FALSE;
}
}
}
return TRUE;
}
/* Avoids a danger in threaded situations (calling close()
* on a file descriptor twice, and another thread has
* re-opened it since the first close)
*/
static void
{
if (*fd < 0)
return;
*fd = -1;
}
static gboolean
char **envp,
char **protected_argv,
{
char **new_argv;
int rc = -1;
int saved_errno;
{
_("Invalid program name: %s"),
return FALSE;
}
{
_("Invalid string in argument vector at %d: %s"),
return FALSE;
}
{
_("Invalid string in environment: %s"),
return FALSE;
}
if (child_setup)
(* child_setup) (user_data);
if (flags & G_SPAWN_SEARCH_PATH)
else
else
else
saved_errno = errno;
{
_("Failed to execute child process (%s)"),
return FALSE;
}
if (exit_status == NULL)
{
if (child_handle && do_return_handle)
else
{
if (child_handle)
*child_handle = 0;
}
}
else
*exit_status = rc;
return TRUE;
}
static gboolean
{
{
_("Failed to create pipe for communicating with child process (%s)"),
g_strerror (errno));
return FALSE;
}
else
return TRUE;
}
static gboolean
const gchar *working_directory,
char **envp,
{
char **protected_argv;
char **new_argv;
int i;
int rc = -1;
int saved_errno;
int argc;
int helper_report[2];
//extern gchar *_glib_get_installation_directory (void);
if (child_setup && !warned_about_child_setup)
{
g_warning ("passing a child setup function to the g_spawn functions is pointless and dangerous on Win32");
}
!(flags & G_SPAWN_STDOUT_TO_DEV_NULL) &&
!(flags & G_SPAWN_STDERR_TO_DEV_NULL) &&
{
/* We can do without the helper process */
error);
return retval;
}
goto cleanup_and_fail;
goto cleanup_and_fail;
goto cleanup_and_fail;
goto cleanup_and_fail;
goto cleanup_and_fail;
else
{
}
else
/* Make the read end of the child error report pipe
* noninherited. Otherwise it will needlessly be inherited by the
* helper process, and the started actual user process. As such that
* shouldn't harm, but it is unnecessary.
*/
if (flags & G_SPAWN_FILE_AND_ARGV_ZERO)
{
/* Overload ARG_CHILD_ERR_REPORT to also encode the
* G_SPAWN_FILE_AND_ARGV_ZERO functionality.
*/
}
/* Make the write end of the sync pipe noninherited. Otherwise the
* helper process will inherit it, and thus if this process happens
* to crash before writing the sync byte to the pipe, the helper
* process won't read but won't get any EOF either, as it has the
* write end open itself.
*/
if (standard_input)
{
}
else if (flags & G_SPAWN_CHILD_INHERITS_STDIN)
{
/* Let stdin be alone */
}
else
{
/* Keep process from blocking on a read of stdin */
}
if (standard_output)
{
}
else if (flags & G_SPAWN_STDOUT_TO_DEV_NULL)
{
}
else
{
}
if (standard_error)
{
}
else if (flags & G_SPAWN_STDERR_TO_DEV_NULL)
{
}
else
{
}
if (working_directory && *working_directory)
else
if (!(flags & G_SPAWN_LEAVE_DESCRIPTORS_OPEN))
else
if (flags & G_SPAWN_SEARCH_PATH)
else
if (exit_status == NULL)
else
for (i = 0; i <= argc; i++)
//SETUP_DEBUG();
if (debug)
{
}
{
_("Invalid working directory: %s"),
else
_("Invalid string in argument vector at %d: %s"),
goto cleanup_and_fail;
}
{
_("Invalid string in environment: %s"),
goto cleanup_and_fail;
}
if (child_setup)
(* child_setup) (user_data);
else
saved_errno = errno;
/* Close the other process's ends of the pipes in this process,
* otherwise the reader will never get EOF.
*/
close_and_invalidate (&stdin_pipe[0]);
/* Check if gspawn-win32-helper couldn't be run */
{
_("Failed to execute helper program (%s)"),
goto cleanup_and_fail;
}
if (exit_status != NULL)
{
/* Synchronous case. Pass helper's report pipe back to caller,
* which takes care of reading it after the grandchild has
* finished.
*/
*err_report = child_err_report_pipe[0];
}
else
{
/* Asynchronous case. We read the helper's report right away. */
goto cleanup_and_fail;
switch (helper_report[0])
{
case CHILD_NO_ERROR:
if (child_handle && do_return_handle)
{
/* rc is our HANDLE for gspawn-win32-helper. It has
* told us the HANDLE of its child. Duplicate that into
* a HANDLE valid in this process.
*/
0, TRUE, DUPLICATE_SAME_ACCESS))
{
*child_handle = 0;
}
}
else if (child_handle)
*child_handle = 0;
break;
default:
goto cleanup_and_fail;
}
}
/* Success against all odds! return the information */
if (standard_input)
if (standard_output)
*standard_output = stdout_pipe[0];
if (standard_error)
*standard_error = stderr_pipe[0];
if (rc != -1)
return TRUE;
if (rc != -1)
if (child_err_report_pipe[0] != -1)
close (child_err_report_pipe[0]);
if (helper_sync_pipe[0] != -1)
close (helper_sync_pipe[0]);
if (stdin_pipe[0] != -1)
close (stdin_pipe[0]);
if (stdout_pipe[0] != -1)
close (stdout_pipe[0]);
if (stderr_pipe[0] != -1)
close (stderr_pipe[0]);
return FALSE;
}
{
/* can't inherit stdin if we have an input pipe. */
return do_spawn_with_pipes (NULL,
argv,
envp,
NULL,
error);
}
// _WRAP_ENUM(SpawnFlags, GSpawnFlags, NO_GTYPE)
/* Helper callback to invoke the actual sigc++ slot.
* We don't need to worry about (un)referencing, since the
* child process gets its own copy of the parent's memory anyway.
*/
static void child_setup_callback(void* user_data)
{
#ifdef GLIBMM_EXCEPTIONS_ENABLED
try
{
#endif //GLIBMM_EXCEPTIONS_ENABLED
#ifdef GLIBMM_EXCEPTIONS_ENABLED
}
catch(...)
{
}
#endif //GLIBMM_EXCEPTIONS_ENABLED
}
int* standard_input,
int* standard_output,
int* standard_error)
{
static_cast<GSpawnFlags>(unsigned(flags)),
(setup_slot) ? &child_setup_callback : 0,
(setup_slot) ? &child_setup_ : 0,
&error);
if(error)
}
#endif
void
int* standard_input,
int* standard_output,
int* standard_error)
{
#ifndef BYPASS_GLIB_SPAWN
argv,
#else
argv,
static_cast<GSpawnFlags>(flags),
#endif
}
{
if ( str ) {
} else {
while ( *ptr )
{
if ( *ptr == '\\' )
{
} else if ( *ptr < 0x80 ) {
} else {
}
ptr++;
}
}
}
return result;
}
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :