]> sjero.net Git - wget/blob - src/mswindows.c
[svn] Improved Windows power management logic. Submitted by David Fritz.
[wget] / src / mswindows.c
1 /* mswindows.c -- Windows-specific support
2    Copyright (C) 1995, 1996, 1997, 1998, 2004
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Wget.
6
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Wget; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 In addition, as a special exception, the Free Software Foundation
22 gives permission to link the code of its release of Wget with the
23 OpenSSL project's "OpenSSL" library (or with modified versions of it
24 that use the same license as the "OpenSSL" library), and distribute
25 the linked executables.  You must obey the GNU General Public License
26 in all respects for all of the code used other than "OpenSSL".  If you
27 modify this file, you may extend this exception to your version of the
28 file, but you are not obligated to do so.  If you do not wish to do
29 so, delete this exception statement from your version.  */
30
31 /* #### Someone please document what these functions do!  */
32
33 #include <config.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <assert.h>
39 #include <errno.h>
40 #include <math.h>
41
42 #ifdef HACK_BCC_UTIME_BUG
43 # include <io.h>
44 # include <fcntl.h>
45 # ifdef HAVE_UTIME_H
46 #  include <utime.h>
47 # endif
48 # ifdef HAVE_SYS_UTIME_H
49 #  include <sys/utime.h>
50 # endif
51 #endif
52
53 #include "wget.h"
54 #include "utils.h"
55 #include "url.h"
56
57 #ifndef errno
58 extern int errno;
59 #endif
60
61 #ifndef ES_SYSTEM_REQUIRED
62 #define ES_SYSTEM_REQUIRED  0x00000001
63 #endif
64
65 #ifndef ES_CONTINUOUS
66 #define ES_CONTINUOUS       0x80000000
67 #endif
68
69
70 /* Defined in log.c.  */
71 void log_request_redirect_output PARAMS ((const char *));
72
73 static int windows_nt_p;
74
75 /* Windows version of xsleep in utils.c.  */
76
77 void
78 xsleep (double seconds)
79 {
80 #ifdef HAVE_USLEEP
81   if (seconds > 1000)
82     {
83       /* Explained in utils.c. */
84       sleep (seconds);
85       seconds -= (long) seconds;
86     }
87   usleep (seconds * 1000000L);
88 #else  /* not HAVE_USLEEP */
89   SleepEx (seconds * 1000, FALSE);
90 #endif /* not HAVE_USLEEP */
91 }
92
93 void
94 windows_main_junk (int *argc, char **argv, char **exec_name)
95 {
96   char *p;
97
98   /* Remove .EXE from filename if it has one.  */
99   *exec_name = xstrdup (*exec_name);
100   p = strrchr (*exec_name, '.');
101   if (p)
102     *p = '\0';
103 }
104 \f
105 static void
106 ws_cleanup (void)
107 {
108   WSACleanup ();
109 }
110
111 static void
112 ws_hangup (void)
113 {
114   log_request_redirect_output ("CTRL+Break");
115 }
116
117 void
118 fork_to_background (void)
119 {
120   /* Whether we arrange our own version of opt.lfilename here.  */
121   int changedp = 0;
122
123   if (!opt.lfilename)
124     {
125       opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
126       changedp = 1;
127     }
128   printf (_("Continuing in background.\n"));
129   if (changedp)
130     printf (_("Output will be written to `%s'.\n"), opt.lfilename);
131
132   ws_hangup ();
133   if (!windows_nt_p)
134     FreeConsole ();
135 }
136
137 static BOOL WINAPI
138 ws_handler (DWORD dwEvent)
139 {
140   switch (dwEvent)
141     {
142 #ifdef CTRLC_BACKGND
143     case CTRL_C_EVENT:
144 #endif
145 #ifdef CTRLBREAK_BACKGND
146     case CTRL_BREAK_EVENT:
147 #endif
148       fork_to_background ();
149       break;
150     case CTRL_SHUTDOWN_EVENT:
151     case CTRL_CLOSE_EVENT:
152     case CTRL_LOGOFF_EVENT:
153     default:
154       ws_cleanup ();
155       return FALSE;
156     }
157   return TRUE;
158 }
159
160 static char *title_buf = NULL;
161 static char *curr_url  = NULL;
162 static int old_percentage = -1;
163
164 /* Updates the console title with the URL of the current file being
165    transferred.  */
166 void
167 ws_changetitle (const char *url)
168 {
169   xfree_null (title_buf);
170   xfree_null (curr_url);
171   title_buf = (char *)xmalloc (strlen (url) + 20);
172   curr_url = xstrdup (url);
173   old_percentage = -1;
174   sprintf (title_buf, "Wget %s", curr_url);
175   SetConsoleTitle (title_buf);
176 }
177
178 /* Updates the console title with the percentage of the current file
179    transferred.  */
180 void
181 ws_percenttitle (double percentage_float)
182 {
183   int percentage = (int) percentage_float;
184
185   /* Only update the title when the percentage has changed.  */
186   if (percentage == old_percentage)
187     return;
188
189   old_percentage = percentage;
190
191   if (percentage > 100)
192     return;
193
194   assert (title_buf != NULL);
195   assert (curr_url != NULL);
196
197   sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
198   SetConsoleTitle (title_buf);
199 }
200
201 /* Returns a pointer to the fully qualified name of the directory that
202    contains the Wget binary (wget.exe).  The returned path does not have a
203    trailing path separator.  Returns NULL on failure.  */
204 char *
205 ws_mypath (void)
206 {
207   static char *wspathsave = NULL;
208
209   if (!wspathsave)
210     {
211       char buf[MAX_PATH + 1];
212       char *p;
213       DWORD len;
214
215       len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
216       if (!len || (len >= sizeof (buf)))
217         return NULL;
218
219       p = strrchr (buf, PATH_SEPARATOR);
220       if (!p)
221         return NULL;
222
223       *p = '\0';
224       wspathsave = xstrdup (buf);
225     }
226
227   return wspathsave;
228 }
229
230 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
231    a lengthy transfer.  Windows does not, by default, consider network
232    activity in console-programs as activity!  Works on Win-98/ME/2K
233    and up.  */
234 static void
235 set_sleep_mode (void)
236 {
237   typedef DWORD (WINAPI *func_t) (DWORD);
238   func_t set_exec_state;
239
240   set_exec_state =
241       (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
242                                "SetThreadExecutionState");
243
244   if (set_exec_state)
245     set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
246 }
247
248 /* Perform Windows specific initialization.  */
249 void
250 ws_startup (void)
251 {
252   WORD requested;
253   WSADATA data;
254   int err;
255   OSVERSIONINFO os;
256
257   if (GetVersionEx (&os) == TRUE
258       && os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
259     windows_nt_p = 1;
260
261   requested = MAKEWORD (1, 1);
262   err = WSAStartup (requested, &data);
263   if (err != 0)
264     {
265       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
266                exec_name);
267       exit (1);
268     }
269
270   if (data.wVersion < requested)
271     {
272       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
273                exec_name);
274       WSACleanup ();
275       exit (1);
276     }
277
278   atexit (ws_cleanup);
279   set_sleep_mode ();
280   SetConsoleCtrlHandler (ws_handler, TRUE);
281 }
282
283 /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
284    (The Borland utime function only works on Windows NT.)  */
285
286 #ifdef HACK_BCC_UTIME_BUG
287 int
288 borland_utime (const char *path, const struct utimbuf *times)
289 {
290   int fd;
291   int res;
292   struct ftime ft;
293   struct tm *ptr_tm;
294
295   if ((fd = open (path, O_RDWR)) < 0)
296     return -1;
297
298   ptr_tm = localtime (&times->modtime);
299   ft.ft_tsec = ptr_tm->tm_sec >> 1;
300   ft.ft_min = ptr_tm->tm_min;
301   ft.ft_hour = ptr_tm->tm_hour;
302   ft.ft_day = ptr_tm->tm_mday;
303   ft.ft_month = ptr_tm->tm_mon + 1;
304   ft.ft_year = ptr_tm->tm_year - 80;
305   res = setftime (fd, &ft);
306   close (fd);
307   return res;
308 }
309 #endif
310 \f
311 /* run_with_timeout Windows implementation.  */
312
313 /* Stack size 0 uses default thread stack-size (reserve+commit).
314    Determined by what's in the PE header.  */
315 #define THREAD_STACK_SIZE  0
316
317 struct thread_data
318 {
319   void (*fun) (void *);
320   void *arg;
321   DWORD ws_error;
322 };
323
324 /* The callback that runs FUN(ARG) in a separate thread.  This
325    function exists for two reasons: a) to not require FUN to be
326    declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
327    which are per-thread.  The latter is useful when FUN calls Winsock
328    functions, which is how run_with_timeout is used in Wget.
329
330    [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
331    the default (wcc386 -3r).  */
332
333 static DWORD WINAPI
334 thread_helper (void *arg)
335 {
336   struct thread_data *td = (struct thread_data *) arg;
337
338   /* Initialize Winsock error to what it was in the parent.  That way
339      the subsequent call to WSAGetLastError will return the same value
340      if td->fun doesn't change Winsock error state.  */
341   WSASetLastError (td->ws_error);
342
343   td->fun (td->arg);
344
345   /* Return Winsock error to the caller, in case FUN ran Winsock
346      code.  */
347   td->ws_error = WSAGetLastError ();
348   return 0;
349 }
350
351 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
352    seconds.  Returns non-zero if the function was interrupted with a
353    timeout, zero otherwise.
354
355    This works by running FUN in a separate thread and terminating the
356    thread if it doesn't finish in the specified time.  */
357
358 int
359 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
360 {
361   static HANDLE thread_hnd = NULL;
362   struct thread_data thread_arg;
363   DWORD thread_id;
364   int rc = 0;
365
366   DEBUGP (("seconds %.2f, ", seconds));
367
368   if (seconds == 0)
369     {
370     blocking_fallback:
371       fun (arg);
372       return 0;
373     }
374
375   /* Should never happen, but test for recursivety anyway.  */
376   assert (thread_hnd == NULL);
377
378   thread_arg.fun = fun;
379   thread_arg.arg = arg;
380   thread_arg.ws_error = WSAGetLastError ();
381   thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
382                              &thread_arg, 0, &thread_id);
383   if (!thread_hnd)
384     {
385       DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
386       goto blocking_fallback;
387     }
388
389   if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
390       == WAIT_OBJECT_0)
391     {
392       /* Propagate error state (which is per-thread) to this thread,
393          so the caller can inspect it.  */
394       WSASetLastError (thread_arg.ws_error);
395       DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
396       rc = 0;
397     }
398   else
399     {
400       TerminateThread (thread_hnd, 1);
401       rc = 1;
402     }
403
404   CloseHandle (thread_hnd);     /* Clear-up after TerminateThread().  */
405   thread_hnd = NULL;
406   return rc;
407 }