]> sjero.net Git - wget/blobdiff - src/mswindows.c
[svn] New function xsleep that resumes sleeps interrupted by signals
[wget] / src / mswindows.c
index 95a0099fac5d4708060049fc5a7a38b11cb7109f..36aef00f8317fb33b12554f65bcf58ecfcb82d63 100644 (file)
@@ -33,10 +33,10 @@ so, delete this exception statement from your version.  */
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <winsock.h>
 #include <string.h>
 #include <assert.h>
 #include <errno.h>
+#include <math.h>
 
 #ifdef HACK_BCC_UTIME_BUG
 # include <io.h>
@@ -57,49 +57,39 @@ so, delete this exception statement from your version.  */
 extern int errno;
 #endif
 
-/* Defined in log.c.  */
-void log_request_redirect_output PARAMS ((const char *));
-
-static int windows_nt_p;
+#ifndef ES_SYSTEM_REQUIRED
+#define ES_SYSTEM_REQUIRED  0x00000001
+#endif
 
+#ifndef ES_CONTINUOUS
+#define ES_CONTINUOUS       0x80000000
+#endif
 
-/* Emulation of Unix sleep.  */
 
-unsigned int
-sleep (unsigned seconds)
-{
-  return SleepEx (1000 * seconds, TRUE) ? 0U : 1000 * seconds;
-}
-
-/* Emulation of Unix usleep().  This has a granularity of
-   milliseconds, but that's ok because:
+/* Defined in log.c.  */
+void log_request_redirect_output PARAMS ((const char *));
 
-   a) Wget is only using it with milliseconds;
+static DWORD set_sleep_mode (DWORD mode);
 
-   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.  */
+static DWORD pwr_mode = 0;
+static int windows_nt_p;
 
-int
-usleep (unsigned long usec)
-{
-  SleepEx (usec / 1000, TRUE);
-  return 0;
-}
+/* Windows version of xsleep in utils.c.  */
 
-static char *
-read_registry (HKEY hkey, char *subkey, char *valuename, char *buf, int *len)
+void
+xsleep (double seconds)
 {
-  HKEY result;
-  DWORD size = *len;
-  DWORD type = REG_SZ;
-  if (RegOpenKeyEx (hkey, subkey, NULL, KEY_READ, &result) != ERROR_SUCCESS)
-    return NULL;
-  if (RegQueryValueEx (result, valuename, NULL, &type, buf, &size) != ERROR_SUCCESS)
-    buf = NULL;
-  *len = size;
-  RegCloseKey (result);
-  return buf;
+#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 */
 }
 
 void
@@ -120,6 +110,9 @@ static void
 ws_cleanup (void)
 {
   WSACleanup ();
+  if (pwr_mode)
+     set_sleep_mode (pwr_mode);
+  pwr_mode = 0;
 }
 
 static void
@@ -136,7 +129,7 @@ fork_to_background (void)
 
   if (!opt.lfilename)
     {
-      opt.lfilename = unique_name (DEFAULT_LOGFILE);
+      opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
       changedp = 1;
     }
   printf (_("Continuing in background.\n"));
@@ -165,28 +158,47 @@ ws_handler (DWORD dwEvent)
     case CTRL_CLOSE_EVENT:
     case CTRL_LOGOFF_EVENT:
     default:
-      WSACleanup ();
+      ws_cleanup ();
       return FALSE;
     }
   return TRUE;
 }
 
+static char *title_buf = NULL;
+static char *curr_url  = NULL;
+static int   num_urls  = 0;
+
 void
-ws_changetitle (char *url, int nurl)
+ws_changetitle (const char *url, int nurl)
 {
-  char *title_buf;
   if (!nurl)
     return;
 
-  title_buf = (char *)alloca (strlen (url) + 20);
-  sprintf (title_buf, "Wget %s%s", url, nurl == 1 ? "" : " ...");
-  SetConsoleTitle (title_buf);
+  num_urls = nurl;
+  if (title_buf)
+     xfree(title_buf);
+  if (curr_url)
+     xfree(curr_url);
+  title_buf = (char *)xmalloc (strlen (url) + 20);
+  curr_url = xstrdup(url);
+  sprintf(title_buf, "Wget %s%s", url, nurl == 1 ? "" : " ...");
+  SetConsoleTitle(title_buf);
+}
+
+void
+ws_percenttitle (double percent)
+{
+  if (num_urls == 1 && title_buf && curr_url && fabs(percent) <= 100.0)
+    {
+      sprintf (title_buf, "Wget [%.0f%%] %s", percent, curr_url);
+      SetConsoleTitle (title_buf);
+    }
 }
 
 char *
 ws_mypath (void)
 {
-  static char *wspathsave;
+  static char *wspathsave = NULL;
   char buffer[MAX_PATH];
   char *ptr;
 
@@ -195,14 +207,11 @@ ws_mypath (void)
       return wspathsave;
     }
 
-  GetModuleFileName (NULL, buffer, MAX_PATH);
-
-  ptr = strrchr (buffer, '\\');
-  if (ptr)
+  if (GetModuleFileName (NULL, buffer, MAX_PATH) &&
+      (ptr = strrchr (buffer, PATH_SEPARATOR)) != NULL)
     {
       *(ptr + 1) = '\0';
-      wspathsave = (char*) xmalloc (strlen (buffer) + 1);
-      strcpy (wspathsave, buffer);
+      wspathsave = xstrdup (buffer);
     }
   else
     wspathsave = NULL;
@@ -222,7 +231,7 @@ ws_help (const char *name)
       if (stat (buf, &sbuf) == 0) 
        {
           printf (_("Starting WinHelp %s\n"), buf);
-          WinHelp (NULL, buf, HELP_INDEX, NULL);
+          WinHelp (NULL, buf, HELP_INDEX, 0);
         }
       else
         {
@@ -261,6 +270,7 @@ ws_startup (void)
       exit (1);
     }
   atexit (ws_cleanup);
+  pwr_mode = set_sleep_mode (0);
   SetConsoleCtrlHandler (ws_handler, TRUE);
 }
 
@@ -268,7 +278,8 @@ ws_startup (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;
@@ -290,3 +301,130 @@ int borland_utime(const char *path, const struct utimbuf *times)
   return 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.
+ */
+static DWORD
+set_sleep_mode (DWORD mode)
+{
+  HMODULE mod = LoadLibrary ("kernel32.dll");
+  DWORD (WINAPI *_SetThreadExecutionState) (DWORD) = NULL;
+  DWORD rc = (DWORD)-1;
+
+  if (mod)
+     (void *)_SetThreadExecutionState
+       = GetProcAddress ((HINSTANCE)mod, "SetThreadExecutionState");
+
+  if (_SetThreadExecutionState)
+    {
+      if (mode == 0)  /* first time */
+       mode = (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
+      rc = (*_SetThreadExecutionState) (mode);
+    }
+  if (mod)
+    FreeLibrary (mod);
+  DEBUGP (("set_sleep_mode(): mode 0x%08lX, rc 0x%08lX\n", mode, rc));
+  return rc;
+}
+\f
+/* run_with_timeout Windows implementation. */
+
+ /* Stack size 0 uses default thread stack-size (reserve+commit).
+  * Determined by what's in the PE header.
+ */
+#define THREAD_STACK_SIZE  0
+
+struct thread_data {
+   void (*fun) (void *);
+   void  *arg;
+   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;
+
+  /* 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; 
+}
+
+/* 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.
+
+   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;
+  DWORD  thread_id;
+  int    rc = 0;
+
+  DEBUGP (("seconds %.2f, ", seconds));
+  
+  if (seconds == 0)
+    {
+    blocking_fallback:
+      fun (arg);
+      return 0;
+    }
+
+  /* Should never happen, but test for recursivety anyway */
+  assert (thread_hnd == NULL);  
+
+  thread_arg.fun = fun;
+  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 ())));
+      goto blocking_fallback;
+    }
+
+  if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
+      == WAIT_OBJECT_0)
+    {
+      /* 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 rc;
+}