# HG changeset patch # User tomkiewicz@cpw.pidgin.im # Date 1318886753 0 # Node ID 326591e64aaa81c7f227d138bd347b6e57acbb42 # Parent 9c9143e32b6cc83ce3ef87ee26b52ad3a7bc2e5c Gadu-Gadu: move our win32 resolver out of libgadu sources. Refs #343 diff -r 9c9143e32b6c -r 326591e64aaa libpurple/protocols/gg/Makefile.am --- a/libpurple/protocols/gg/Makefile.am Mon Oct 17 20:09:23 2011 +0000 +++ b/libpurple/protocols/gg/Makefile.am Mon Oct 17 21:25:53 2011 +0000 @@ -1,5 +1,7 @@ EXTRA_DIST = \ Makefile.mingw \ + win32-resolver.c \ + win32-resolver.h \ lib/common.c \ lib/compat.h \ lib/COPYING \ diff -r 9c9143e32b6c -r 326591e64aaa libpurple/protocols/gg/Makefile.mingw --- a/libpurple/protocols/gg/Makefile.mingw Mon Oct 17 20:09:23 2011 +0000 +++ b/libpurple/protocols/gg/Makefile.mingw Mon Oct 17 21:25:53 2011 +0000 @@ -60,7 +60,8 @@ confer.c \ gg.c \ search.c \ - gg-utils.c + gg-utils.c \ + win32-resolver.c OBJECTS = $(C_SRC:%.c=%.o) diff -r 9c9143e32b6c -r 326591e64aaa libpurple/protocols/gg/gg.c --- a/libpurple/protocols/gg/gg.c Mon Oct 17 20:09:23 2011 +0000 +++ b/libpurple/protocols/gg/gg.c Mon Oct 17 21:25:53 2011 +0000 @@ -47,6 +47,10 @@ #include "buddylist.h" #include "gg-utils.h" +#ifdef _WIN32 +# include "win32-resolver.h" +#endif + static PurplePlugin *my_protocol = NULL; /* Prototypes */ @@ -3000,6 +3004,11 @@ my_protocol = plugin; gg_debug_handler = purple_gg_debug_handler; + +#ifdef _WIN32 + gg_global_set_custom_resolver(ggp_resolver_win32thread_start, + ggp_resolver_win32thread_cleanup); +#endif } PURPLE_INIT_PLUGIN(gg, init_plugin, info); diff -r 9c9143e32b6c -r 326591e64aaa libpurple/protocols/gg/lib/libgadu.h --- a/libpurple/protocols/gg/lib/libgadu.h Mon Oct 17 20:09:23 2011 +0000 +++ b/libpurple/protocols/gg/lib/libgadu.h Mon Oct 17 21:25:53 2011 +0000 @@ -95,6 +95,12 @@ /* Defined if uintX_t types are defined in . */ #undef GG_CONFIG_HAVE_SYS_TYPES_H +#ifdef _WIN32 +# undef GG_CONFIG_HAVE_FORK +#else +# define GG_CONFIG_HAVE_FORK +#endif + #ifdef GG_CONFIG_HAVE_OPENSSL #include #endif @@ -195,7 +201,6 @@ GG_RESOLVER_DEFAULT = 0, /**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */ GG_RESOLVER_FORK, /**< Rozwiązywanie nazw bazujące na procesach */ GG_RESOLVER_PTHREAD, /**< Rozwiązywanie nazw bazujące na wątkach */ - GG_RESOLVER_WIN32, GG_RESOLVER_CUSTOM, /**< Funkcje rozwiązywania nazw dostarczone przed aplikację */ GG_RESOLVER_INVALID = -1 /**< Nieprawidłowy sposób rozwiązywania nazw (wynik \c gg_session_get_resolver) */ } gg_resolver_t; diff -r 9c9143e32b6c -r 326591e64aaa libpurple/protocols/gg/lib/resolver.c --- a/libpurple/protocols/gg/lib/resolver.c Mon Oct 17 20:09:23 2011 +0000 +++ b/libpurple/protocols/gg/lib/resolver.c Mon Oct 17 21:25:53 2011 +0000 @@ -319,283 +319,6 @@ int pid; /*< Identyfikator procesu */ }; -#ifdef _WIN32 -/** - * Deal with the fact that you can't select() on a win32 file fd. - * This makes it practically impossible to tie into purple's event loop. - * - * -This is thanks to Tor Lillqvist. - * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. - */ -static int -socket_pipe (int *fds) -{ - SOCKET temp, socket1 = -1, socket2 = -1; - struct sockaddr_in saddr; - int len; - u_long arg; - fd_set read_set, write_set; - struct timeval tv; - - temp = socket(AF_INET, SOCK_STREAM, 0); - - if (temp == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { - goto out0; - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { - goto out0; - } - - if (listen(temp, 1) == SOCKET_ERROR) { - goto out0; - } - - len = sizeof(saddr); - if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { - goto out0; - } - - socket1 = socket(AF_INET, SOCK_STREAM, 0); - - if (socket1 == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out1; - } - - if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || - WSAGetLastError() != WSAEWOULDBLOCK) { - goto out1; - } - - FD_ZERO(&read_set); - FD_SET(temp, &read_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { - goto out1; - } - - if (!FD_ISSET(temp, &read_set)) { - goto out1; - } - - socket2 = accept(temp, (struct sockaddr *) &saddr, &len); - if (socket2 == INVALID_SOCKET) { - goto out1; - } - - FD_ZERO(&write_set); - FD_SET(socket1, &write_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { - goto out2; - } - - if (!FD_ISSET(socket1, &write_set)) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - fds[0] = socket1; - fds[1] = socket2; - - closesocket (temp); - - return 0; - -out2: - closesocket (socket2); -out1: - closesocket (socket1); -out0: - closesocket (temp); - errno = EIO; /* XXX */ - - return -1; -} -#endif - - - -#ifdef _WIN32 -struct gg_resolve_win32thread_data { - char *hostname; - int fd; -}; - -static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) -{ - struct gg_resolve_win32thread_data *d = arg; - struct in_addr addr_ip[2], *addr_list; - int addr_count; - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() host: %s, fd: %i called\n", d->hostname, d->fd); - - if ((addr_ip[0].s_addr = inet_addr(d->hostname)) == INADDR_NONE) { - /* W przypadku błędu gg_gethostbyname_real() zwróci -1 - * i nie zmieni &addr. Tam jest już INADDR_NONE, - * więc nie musimy robić nic więcej. */ - if (gg_gethostbyname_real(d->hostname, &addr_list, &addr_count, 0) == -1) - { - addr_list = addr_ip; - } - } else { - addr_list = addr_ip; - addr_ip[1].s_addr = INADDR_NONE; - addr_count = 1; - } - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() count = %d\n", addr_count); - - write(d->fd, addr_list, (addr_count+1) * sizeof(struct in_addr)); - close(d->fd); - - free(d->hostname); - d->hostname = NULL; - - free(d); - - if (addr_list != addr_ip) - free(addr_list); - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() done\n"); - - return 0; -} - - -static int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) -{ - struct gg_resolve_win32thread_data *d = NULL; - HANDLE h; - DWORD dwTId; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); - - if (!resolver || !fd || !hostname) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() creating pipes...\n"); - - if (socket_pipe(pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); - return -1; - } - - if (!(d = malloc(sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->hostname = NULL; - - if (!(d->hostname = strdup(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->fd = pipes[1]; - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() creating thread...\n"); - - h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, - d, 0, &dwTId); - - if (h == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); - new_errno = errno; - goto cleanup; - } - - *resolver = h; - *fd = pipes[0]; - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() done\n"); - - return 0; - -cleanup: - if (d) { - free(d->hostname); - free(d); - } - - close(pipes[0]); - close(pipes[1]); - - errno = new_errno; - - return -1; - -} - -static void gg_resolve_win32thread_cleanup(void **priv_data, int force) -{ - struct gg_resolve_win32thread_data *data; - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force: %i called\n", force); - - if (priv_data == NULL || *priv_data == NULL) - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() priv_data: NULL\n"); - return; - - data = (struct gg_resolve_win32thread_data*) *priv_data; - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() data: %s called\n", data->hostname); - *priv_data = NULL; - - if (force) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force called\n", force); - //pthread_cancel(data->thread); - //pthread_join(data->thread, NULL); - } - - free(data->hostname); - data->hostname = NULL; - - if (data->fd != -1) { - close(data->fd); - data->fd = -1; - } - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() done\n"); - free(data); -} -#endif - #ifndef _WIN32 /** * \internal Rozwiązuje nazwę serwera w osobnym procesie. @@ -873,24 +596,14 @@ } #if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) -# ifdef _WIN32 - type = GG_RESOLVER_WIN32; -# else type = GG_RESOLVER_FORK; -# endif #else type = GG_RESOLVER_PTHREAD; #endif } switch (type) { -#ifdef _WIN32 - case GG_RESOLVER_WIN32: - gs->resolver_type = type; - gs->resolver_start = gg_resolve_win32thread; - gs->resolver_cleanup = gg_resolve_win32thread_cleanup; - return 0; -#else +#ifdef GG_CONFIG_HAVE_FORK case GG_RESOLVER_FORK: gs->resolver_type = type; gs->resolver_start = gg_resolver_fork_start; @@ -994,24 +707,14 @@ } #if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT) -# ifdef _WIN32 - type = GG_RESOLVER_WIN32; -# else type = GG_RESOLVER_FORK; -# endif #else type = GG_RESOLVER_PTHREAD; #endif } switch (type) { -#ifdef _WIN32 - case GG_RESOLVER_WIN32: - gh->resolver_type = type; - gh->resolver_start = gg_resolve_win32thread; - gh->resolver_cleanup = gg_resolve_win32thread_cleanup; - return 0; -#else +#ifdef GG_CONFIG_HAVE_FORK case GG_RESOLVER_FORK: gh->resolver_type = type; gh->resolver_start = gg_resolver_fork_start; @@ -1089,13 +792,7 @@ gg_global_resolver_cleanup = NULL; return 0; -#ifdef _WIN32 - case GG_RESOLVER_WIN32: - gg_global_resolver_type = type; - gg_global_resolver_start = gg_resolve_win32thread; - gg_global_resolver_cleanup = gg_resolve_win32thread_cleanup; - return 0; -#else +#ifdef GG_CONFIG_HAVE_FORK case GG_RESOLVER_FORK: gg_global_resolver_type = type; gg_global_resolver_start = gg_resolver_fork_start; diff -r 9c9143e32b6c -r 326591e64aaa libpurple/protocols/gg/win32-resolver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/win32-resolver.c Mon Oct 17 21:25:53 2011 +0000 @@ -0,0 +1,321 @@ +/** + * @file win32-resolver.c + * + * purple + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "win32-resolver.h" + +#include +#include +#include "debug.h" + +#ifndef _WIN32 +#error "win32thread resolver is not supported on current platform" +#endif + +/** + * Deal with the fact that you can't select() on a win32 file fd. + * This makes it practically impossible to tie into purple's event loop. + * + * -This is thanks to Tor Lillqvist. + */ +static int ggp_resolver_win32thread_socket_pipe(int *fds) +{ + SOCKET temp, socket1 = -1, socket2 = -1; + struct sockaddr_in saddr; + int len; + u_long arg; + fd_set read_set, write_set; + struct timeval tv; + + purple_debug_misc("gg", "ggp_resolver_win32thread_socket_pipe(&%d)\n", + *fds); + + temp = socket(AF_INET, SOCK_STREAM, 0); + + if (temp == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { + goto out0; + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { + goto out0; + } + + if (listen(temp, 1) == SOCKET_ERROR) { + goto out0; + } + + len = sizeof(saddr); + if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { + goto out0; + } + + socket1 = socket(AF_INET, SOCK_STREAM, 0); + + if (socket1 == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out1; + } + + if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || + WSAGetLastError() != WSAEWOULDBLOCK) { + goto out1; + } + + FD_ZERO(&read_set); + FD_SET(temp, &read_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { + goto out1; + } + + if (!FD_ISSET(temp, &read_set)) { + goto out1; + } + + socket2 = accept(temp, (struct sockaddr *) &saddr, &len); + if (socket2 == INVALID_SOCKET) { + goto out1; + } + + FD_ZERO(&write_set); + FD_SET(socket1, &write_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { + goto out2; + } + + if (!FD_ISSET(socket1, &write_set)) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + fds[0] = socket1; + fds[1] = socket2; + + closesocket (temp); + + return 0; + +out2: + closesocket (socket2); +out1: + closesocket (socket1); +out0: + closesocket (temp); + errno = EIO; /* XXX */ + + return -1; +} + +struct ggp_resolver_win32thread_data { + char *hostname; + int fd; +}; + +static DWORD WINAPI ggp_resolver_win32thread_thread(LPVOID arg) +{ + struct ggp_resolver_win32thread_data *data = arg; + struct in_addr addr_ip[2], *addr_list; + int addr_count; + + purple_debug_info("gg", "ggp_resolver_win32thread_thread() host: %s, " + "fd: %i called\n", data->hostname, data->fd); + + if ((addr_ip[0].s_addr = inet_addr(data->hostname)) == INADDR_NONE) { + /* W przypadku błędu gg_gethostbyname_real() zwróci -1 + * i nie zmieni &addr. Tam jest już INADDR_NONE, + * więc nie musimy robić nic więcej. */ + if (gg_gethostbyname_real(data->hostname, &addr_list, + &addr_count, 0) == -1) { + addr_list = addr_ip; + } + } else { + addr_list = addr_ip; + addr_ip[1].s_addr = INADDR_NONE; + addr_count = 1; + } + + purple_debug_misc("gg", "ggp_resolver_win32thread_thread() " + "count = %d\n", addr_count); + + write(data->fd, addr_list, (addr_count+1) * sizeof(struct in_addr)); + close(data->fd); + + free(data->hostname); + data->hostname = NULL; + + free(data); + + if (addr_list != addr_ip) + free(addr_list); + + purple_debug_misc("gg", "ggp_resolver_win32thread_thread() done\n"); + + return 0; +} + + +int ggp_resolver_win32thread_start(int *fd, void **private_data, + const char *hostname) +{ + struct ggp_resolver_win32thread_data *data = NULL; + HANDLE h; + DWORD dwTId; + int pipes[2], new_errno; + + purple_debug_info("gg", "ggp_resolver_win32thread_start(%p, %p, " + "\"%s\");\n", fd, private_data, hostname); + + if (!private_data || !fd || !hostname) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() " + "invalid arguments\n"); + errno = EFAULT; + return -1; + } + + purple_debug_misc("gg", "ggp_resolver_win32thread_start() creating " + "pipes...\n"); + + if (ggp_resolver_win32thread_socket_pipe(pipes) == -1) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() " + "unable to create pipes (errno=%d, %s)\n", + errno, strerror(errno)); + return -1; + } + + if (!(data = malloc(sizeof(*data)))) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() out " + "of memory\n"); + new_errno = errno; + goto cleanup; + } + + data->hostname = NULL; + + if (!(data->hostname = strdup(hostname))) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() out " + "of memory\n"); + new_errno = errno; + goto cleanup; + } + + data->fd = pipes[1]; + + purple_debug_misc("gg", "ggp_resolver_win32thread_start() creating " + "thread...\n"); + + h = CreateThread(NULL, 0, ggp_resolver_win32thread_thread, data, 0, + &dwTId); + + if (h == NULL) { + purple_debug_error("gg", "ggp_resolver_win32thread_start() " + "unable to create thread\n"); + new_errno = errno; + goto cleanup; + } + + *private_data = h; + *fd = pipes[0]; + + purple_debug_misc("gg", "ggp_resolver_win32thread_start() done\n"); + + return 0; + +cleanup: + if (data) { + free(data->hostname); + free(data); + } + + close(pipes[0]); + close(pipes[1]); + + errno = new_errno; + + return -1; + +} + +void ggp_resolver_win32thread_cleanup(void **private_data, int force) +{ + struct ggp_resolver_win32thread_data *data; + + purple_debug_info("gg", "ggp_resolver_win32thread_cleanup() force: %i " + "called\n", force); + + if (private_data == NULL || *private_data == NULL) { + purple_debug_error("gg", "ggp_resolver_win32thread_cleanup() " + "private_data: NULL\n"); + return; + } + return; /* XXX */ + + data = (struct ggp_resolver_win32thread_data*) *private_data; + purple_debug_misc("gg", "ggp_resolver_win32thread_cleanup() data: " + "%s called\n", data->hostname); + *private_data = NULL; + + if (force) { + purple_debug_misc("gg", "ggp_resolver_win32thread_cleanup() " + "force called\n"); + //pthread_cancel(data->thread); + //pthread_join(data->thread, NULL); + } + + free(data->hostname); + data->hostname = NULL; + + if (data->fd != -1) { + close(data->fd); + data->fd = -1; + } + purple_debug_info("gg", "ggp_resolver_win32thread_cleanup() done\n"); + free(data); +} + +/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r 9c9143e32b6c -r 326591e64aaa libpurple/protocols/gg/win32-resolver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/win32-resolver.h Mon Oct 17 21:25:53 2011 +0000 @@ -0,0 +1,46 @@ +/** + * @file win32-resolver.h + * + * purple + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_GG_WIN32_RESOLVER +#define _PURPLE_GG_WIN32_RESOLVER + +/** + * Starts hostname resolving in new win32 thread. + * + * @param fd Pointer to variable, where pipe descriptor will be saved. + * @param private_data Pointer to variable, where pointer to private data will + * be saved. + * @param hostname Hostname to resolve. + */ +int ggp_resolver_win32thread_start(int *fd, void **private_data, + const char *hostname); + +/** + * Cleans up resources after hostname resolving. + * + * @param private_data Pointer to variable storing pointer to private data. + * @param force TRUE, if resources should be cleaned up even, if + * resolving process didn't finished. + */ +void ggp_resolver_win32thread_cleanup(void **private_data, int force); + +#endif /* _PURPLE_GG_WIN32_RESOLVER */ + +/* vim: set ts=8 sts=0 sw=8 noet: */