/* mswindows.c -- Windows-specific support
- Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1995, 1996, 1997, 1998, 2004 Free Software Foundation, Inc.
This file is part of GNU Wget.
#include <stdio.h>
#include <stdlib.h>
-#include <winsock.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
static DWORD pwr_mode = 0;
static int windows_nt_p;
-#ifndef HAVE_SLEEP
+/* Windows version of xsleep in utils.c. */
-/* Emulation of Unix sleep. */
-
-unsigned int
-sleep (unsigned seconds)
-{
- return SleepEx (1000 * seconds, TRUE) ? 0U : 1000 * seconds;
-}
-#endif
-
-#ifndef HAVE_USLEEP
-/* Emulation of Unix usleep(). This has a granularity of
- milliseconds, but that's ok because:
-
- a) Wget is only using it with milliseconds [not anymore, but b)
- still applies];
-
- b) You can't rely on usleep's granularity anyway. If a caller
- expects usleep to respect every microsecond, he's in for a
- surprise. */
-
-int
-usleep (unsigned long usec)
+void
+xsleep (double seconds)
{
- SleepEx (usec / 1000, TRUE);
- return 0;
+#ifdef HAVE_USLEEP
+ if (seconds > 1000)
+ {
+ /* Explained in utils.c. */
+ sleep (seconds);
+ seconds -= (long) seconds;
+ }
+ usleep (seconds * 1000000L);
+#else /* not HAVE_USLEEP */
+ SleepEx (seconds * 1000, FALSE);
+#endif /* not HAVE_USLEEP */
}
-#endif /* HAVE_USLEEP */
void
windows_main_junk (int *argc, char **argv, char **exec_name)
{
if (num_urls == 1 && title_buf && curr_url && fabs(percent) <= 100.0)
{
- sprintf (title_buf, "Wget [%.1f%%] %s", percent, curr_url);
+ sprintf (title_buf, "Wget [%.0f%%] %s", percent, curr_url);
SetConsoleTitle (title_buf);
}
}
ws_mypath (void)
{
static char *wspathsave = NULL;
- char buffer[MAX_PATH];
- char *ptr;
- if (wspathsave)
+ if (!wspathsave)
{
- return wspathsave;
- }
+ char buf[MAX_PATH + 1];
+ char *p;
+ DWORD len;
- if (GetModuleFileName (NULL, buffer, MAX_PATH) &&
- (ptr = strrchr (buffer, PATH_SEPARATOR)) != NULL)
- {
- *(ptr + 1) = '\0';
- wspathsave = xstrdup (buffer);
- }
- else
- wspathsave = NULL;
- return wspathsave;
-}
+ len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
+ if (!len || (len >= sizeof (buf)))
+ return NULL;
-void
-ws_help (const char *name)
-{
- char *mypath = ws_mypath ();
+ p = strrchr (buf, PATH_SEPARATOR);
+ if (!p)
+ return NULL;
- if (mypath)
- {
- struct stat sbuf;
- char *buf = (char *)alloca (strlen (mypath) + strlen (name) + 4 + 1);
- sprintf (buf, "%s%s.HLP", mypath, name);
- if (stat (buf, &sbuf) == 0)
- {
- printf (_("Starting WinHelp %s\n"), buf);
- WinHelp (NULL, buf, HELP_INDEX, NULL);
- }
- else
- {
- printf ("%s: %s\n", buf, strerror (errno));
- }
+ *p = '\0';
+ wspathsave = xstrdup (buf);
}
+
+ return wspathsave;
}
void
(The Borland utime function only works on Windows NT.) */
#ifdef HACK_BCC_UTIME_BUG
-int borland_utime(const char *path, const struct utimbuf *times)
+int
+borland_utime (const char *path, const struct utimbuf *times)
{
int fd;
int res;
#endif
/*
- * Prevent Windows entering sleep/hibernation-mode while wget is doing a lengthy transfer.
- * Windows does by default not consider network activity in console-programs as activity !
- * Works on Win-98/ME/2K and up.
+ * Prevent Windows entering sleep/hibernation-mode while wget is doing
+ * a lengthy transfer. Windows does by default not consider network
+ * activity in console-programs as activity ! Works on Win-98/ME/2K
+ * and up.
*/
-static
-DWORD set_sleep_mode (DWORD mode)
+static DWORD
+set_sleep_mode (DWORD mode)
{
HMODULE mod = LoadLibrary ("kernel32.dll");
- DWORD (*_SetThreadExecutionState) (DWORD) = NULL;
+ DWORD (WINAPI *_SetThreadExecutionState) (DWORD) = NULL;
DWORD rc = (DWORD)-1;
if (mod)
- (void*)_SetThreadExecutionState = GetProcAddress ((HINSTANCE)mod, "SetThreadExecutionState");
+ (void *)_SetThreadExecutionState
+ = GetProcAddress ((HINSTANCE)mod, "SetThreadExecutionState");
if (_SetThreadExecutionState)
{
if (mode == 0) /* first time */
- mode = (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
+ mode = (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
rc = (*_SetThreadExecutionState) (mode);
}
if (mod)
- FreeLibrary (mod);
+ FreeLibrary (mod);
DEBUGP (("set_sleep_mode(): mode 0x%08lX, rc 0x%08lX\n", mode, rc));
- return (rc);
+ return rc;
}
\f
/* run_with_timeout Windows implementation. */
-/* Wait for thread completion in 0.1s intervals (a tradeoff between
- * CPU loading and resolution).
+ /* Stack size 0 uses default thread stack-size (reserve+commit).
+ * Determined by what's in the PE header.
*/
-#define THREAD_WAIT_INTV 100
-#define THREAD_STACK_SIZE 4096
+#define THREAD_STACK_SIZE 0
struct thread_data {
void (*fun) (void *);
DWORD ws_error;
};
+/* The callback that runs FUN(ARG) in a separate thread. This
+ function exists for two reasons: a) to not require FUN to be
+ declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
+ which are per-thread. The latter is useful when FUN calls Winsock
+ functions, which is how run_with_timeout is used in Wget.
+
+ [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
+ the default (wcc386 -3r). */
+
static DWORD WINAPI
thread_helper (void *arg)
{
struct thread_data *td = (struct thread_data *) arg;
-
- WSASetLastError (0);
- td->ws_error = 0;
- (*td->fun) (td->arg);
-
- /* Since run_with_timeout() is only used for Winsock functions and
- * Winsock errors are per-thread, we must return this to caller.
- */
- td->ws_error = WSAGetLastError();
- return (0);
+
+ /* Initialize Winsock error to what it was in the parent. That way
+ the subsequent call to WSAGetLastError will return the same value
+ if td->fun doesn't change Winsock error state. */
+ WSASetLastError (td->ws_error);
+
+ td->fun (td->arg);
+
+ /* Return Winsock error to the caller, in case FUN ran Winsock
+ code. */
+ td->ws_error = WSAGetLastError ();
+ return 0;
}
-#ifdef GV_DEBUG /* I'll remove this eventually */
-# define DEBUGN(lvl,x) do { if (opt.verbose >= (lvl)) DEBUGP (x); } while (0)
-#else
-# define DEBUGN(lvl,x) ((void)0)
-#endif
+/* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
+ seconds. Returns non-zero if the function was interrupted with a
+ timeout, zero otherwise.
-/*
- * Run FUN with timeout. This is done by creating a thread for 'fun'
- * to run in. Since call-convention of 'fun' is undefined [1], we
- * must call it via thread_helper() which must be __stdcall/WINAPI.
- *
- * [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is the
- * default (wcc386 -3r).
- */
+ This works by running FUN in a separate thread and terminating the
+ thread if it doesn't finish in the specified time. */
int
run_with_timeout (double seconds, void (*fun) (void *), void *arg)
{
static HANDLE thread_hnd = NULL;
struct thread_data thread_arg;
- struct wget_timer *timer;
- DWORD thread_id, exitCode;
+ DWORD thread_id;
+ int rc = 0;
- DEBUGN (2, ("seconds %.2f, ", seconds));
+ DEBUGP (("seconds %.2f, ", seconds));
if (seconds == 0)
{
/* Should never happen, but test for recursivety anyway */
assert (thread_hnd == NULL);
- thread_arg.arg = arg;
+
thread_arg.fun = fun;
- thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE,
- thread_helper, (void*)&thread_arg,
- 0, &thread_id);
+ thread_arg.arg = arg;
+ thread_arg.ws_error = WSAGetLastError ();
+ thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
+ &thread_arg, 0, &thread_id);
if (!thread_hnd)
{
- DEBUGP (("CreateThread() failed; %s\n", strerror(GetLastError())));
+ DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
goto blocking_fallback;
}
- timer = wtimer_new();
-
- exitCode = 0;
-
- /* Keep checking for thread's state until the timeout expires. */
- while (wtimer_elapsed (timer) < 1000 * seconds)
+ if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
+ == WAIT_OBJECT_0)
{
- GetExitCodeThread (thread_hnd, &exitCode);
- DEBUGN (2, ("thread exit-code %lu\n", exitCode));
- if (exitCode != STILL_ACTIVE)
- break;
- Sleep (THREAD_WAIT_INTV);
- }
-
- DEBUGN (2, ("elapsed %.2f, wtimer_elapsed %.2f, ", elapsed,
- wtimer_elapsed (timer)));
-
- wtimer_delete (timer);
-
- /* If we timed out kill the thread. Normal thread exitCode would be 0.
- */
- if (exitCode == STILL_ACTIVE)
- {
- DEBUGN (2, ("thread timed out\n"));
- TerminateThread (thread_hnd, 0);
- }
- else
- {
- DEBUGN (2, ("thread exit-code %lu, WS error %lu\n", exitCode, thread_arg.ws_error));
-
/* Propagate error state (which is per-thread) to this thread,
so the caller can inspect it. */
WSASetLastError (thread_arg.ws_error);
+ DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
+ rc = 0;
+ }
+ else
+ {
+ TerminateThread (thread_hnd, 1);
+ rc = 1;
}
+
+ CloseHandle (thread_hnd); /* clear-up after TerminateThread() */
thread_hnd = NULL;
- return exitCode == STILL_ACTIVE;
+ return rc;
}