diff --git a/packaging/win32/Makefile.am b/packaging/win32/Makefile.am index c11d0d85cb..d69f325ef8 100644 --- a/packaging/win32/Makefile.am +++ b/packaging/win32/Makefile.am @@ -11,3 +11,12 @@ EXTRA_DIST = \ opensp-1.5.2-patch.diff \ pi.sh \ reset.sh + +if OS_WIN32 + bin_PROGRAMS = redirect + redirect_SOURCES = redirect.c + redirect_LDADD = $(GLIB_LIBS) + AM_CFLAGS = $(GLIB_CFLAGS) +else + EXTRA_DIST += redirect.c +endif diff --git a/packaging/win32/redirect.c b/packaging/win32/redirect.c new file mode 100644 index 0000000000..c9a495b1aa --- /dev/null +++ b/packaging/win32/redirect.c @@ -0,0 +1,166 @@ +/* + * redirect.c -- + * Copyright (C) 2007 Andreas Koehler + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +#include "config.h" +#include +#include + +/* This program will search for an existing file in + * .\bin\$i + * ..\bin\$i + * ..\..\bin\$i + * and so on, where $i is the base name of argv[0]. Once found, that + * program is spawned synchronously with the same parameters and its + * exit status will be the one of this program as well. + * To avoid recursion, do not use it in a directory ending on "bin". + */ + +/* __wgetmainargs() is an undocumented function to get the program's + * argv in wide char format. + */ +typedef struct { + int newmode; +} _startupinfo; + +extern void __wgetmainargs(int *argc, + wchar_t ***wargv, + wchar_t ***wenviron, + int expand_wildcards, + _startupinfo *startupinfo); + +static gboolean +wcharv_to_utf8_charv(wchar_t **wcharv, + gchar ***utf8_charv, + gint *error_index) +{ + gchar **retval = NULL; + + *utf8_charv = NULL; + if (wcharv != NULL) { + int n = 0, i; + + while (wcharv[n]) + n++; + retval = g_new(gchar *, n + 1); + + for (i = 0; i < n; i++) { + retval[i] = g_utf16_to_utf8 (wcharv[i], -1, NULL, NULL, NULL); + if (retval[i] == NULL) { + if (error_index) + *error_index = i; + while (i) + g_free(retval[--i]); + g_free (retval); + return FALSE; + } + } + + retval[n] = NULL; + } + *utf8_charv = retval; + return TRUE; +} + +static gboolean +redirect_program(gchar **argv) +{ + gchar *orig_path, *orig_base, *dir, *path; + gchar *index; + gboolean retval = FALSE; + + g_return_val_if_fail(*argv, FALSE); + if (g_path_is_absolute(*argv)) { + orig_path = g_strdup(*argv); + } else { + gchar *cd = g_get_current_dir(); + orig_path = g_build_filename(cd, *argv, (gchar*) NULL); + g_free(cd); + } + + orig_base = g_path_get_basename(orig_path); + dir = g_strdup(orig_path); + while (((index = strrchr(dir, '\\')) != NULL) + || ((index = strrchr(dir, '/')) != NULL)) { + *index = '\0'; + path = g_build_filename(dir, "bin", orig_base, (gchar*) NULL); + g_debug("Testing %s", path); + if (g_file_test(path, G_FILE_TEST_EXISTS)) { + g_free(orig_path); + *argv = path; + retval = TRUE; + break; + } + g_free(path); + } + + g_free(dir); + g_free(orig_base); + g_free(orig_path); + + return retval; +} + +#ifdef __GNUC__ +# ifndef _stdcall +# define _stdcall __attribute__((stdcall)) +# endif +#endif + +int _stdcall +WinMain(struct HINSTANCE__ *hInstance, + struct HINSTANCE__ *hPrevInstance, + char *lpszCmdLine, + int nCmdShow) +{ + int argc; + wchar_t **wargv, **wenvp; + _startupinfo si = { 0 }; + gint index, status; + gchar **utf8_argv; + GError *error = NULL; + + __wgetmainargs(&argc, &wargv, &wenvp, 0, &si); + g_assert(argc == __argc); + + if (!wcharv_to_utf8_charv(wargv, &utf8_argv, &index)) { + g_warning("Invalid argument at position %d", index); + return -1; + } + + if (!redirect_program(utf8_argv)) { + g_warning("Could not find destination for %s", *utf8_argv); + g_strfreev(utf8_argv); + return -1; + } + + if (!g_spawn_sync(NULL, utf8_argv, NULL, + G_SPAWN_LEAVE_DESCRIPTORS_OPEN + | G_SPAWN_CHILD_INHERITS_STDIN, + NULL, NULL, NULL, NULL, &status, &error)) { + g_warning("Could not spawn program: %s", error->message); + g_error_free(error); + g_strfreev(utf8_argv); + return -1; + } + + return status; +}