]> sjero.net Git - wget/blob - src/mswindows.c
[svn] Formatting tweaks 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 DWORD set_sleep_mode (DWORD mode);
74
75 static DWORD pwr_mode = 0;
76 static int windows_nt_p;
77
78 /* Windows version of xsleep in utils.c.  */
79
80 void
81 xsleep (double seconds)
82 {
83 #ifdef HAVE_USLEEP
84   if (seconds > 1000)
85     {
86       /* Explained in utils.c. */
87       sleep (seconds);
88       seconds -= (long) seconds;
89     }
90   usleep (seconds * 1000000L);
91 #else  /* not HAVE_USLEEP */
92   SleepEx (seconds * 1000, FALSE);
93 #endif /* not HAVE_USLEEP */
94 }
95
96 void
97 windows_main_junk (int *argc, char **argv, char **exec_name)
98 {
99   char *p;
100
101   /* Remove .EXE from filename if it has one.  */
102   *exec_name = xstrdup (*exec_name);
103   p = strrchr (*exec_name, '.');
104   if (p)
105     *p = '\0';
106 }
107 \f
108 static void
109 ws_cleanup (void)
110 {
111   WSACleanup ();
112   if (pwr_mode)
113      set_sleep_mode (pwr_mode);
114   pwr_mode = 0;
115 }
116
117 static void
118 ws_hangup (void)
119 {
120   log_request_redirect_output ("CTRL+Break");
121 }
122
123 void
124 fork_to_background (void)
125 {
126   /* Whether we arrange our own version of opt.lfilename here.  */
127   int changedp = 0;
128
129   if (!opt.lfilename)
130     {
131       opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
132       changedp = 1;
133     }
134   printf (_("Continuing in background.\n"));
135   if (changedp)
136     printf (_("Output will be written to `%s'.\n"), opt.lfilename);
137
138   ws_hangup ();
139   if (!windows_nt_p)
140     FreeConsole ();
141 }
142
143 static BOOL WINAPI
144 ws_handler (DWORD dwEvent)
145 {
146   switch (dwEvent)
147     {
148 #ifdef CTRLC_BACKGND
149     case CTRL_C_EVENT:
150 #endif
151 #ifdef CTRLBREAK_BACKGND
152     case CTRL_BREAK_EVENT:
153 #endif
154       fork_to_background ();
155       break;
156     case CTRL_SHUTDOWN_EVENT:
157     case CTRL_CLOSE_EVENT:
158     case CTRL_LOGOFF_EVENT:
159     default:
160       ws_cleanup ();
161       return FALSE;
162     }
163   return TRUE;
164 }
165
166 static char *title_buf = NULL;
167 static char *curr_url  = NULL;
168 static int   num_urls  = 0;
169
170 void
171 ws_changetitle (const char *url, int nurl)
172 {
173   if (!nurl)
174     return;
175
176   num_urls = nurl;
177   if (title_buf)
178      xfree(title_buf);
179   if (curr_url)
180      xfree(curr_url);
181   title_buf = (char *)xmalloc (strlen (url) + 20);
182   curr_url = xstrdup(url);
183   sprintf(title_buf, "Wget %s%s", url, nurl == 1 ? "" : " ...");
184   SetConsoleTitle(title_buf);
185 }
186
187 void
188 ws_percenttitle (double percent)
189 {
190   if (num_urls == 1 && title_buf && curr_url && fabs(percent) <= 100.0)
191     {
192       sprintf (title_buf, "Wget [%.0f%%] %s", percent, curr_url);
193       SetConsoleTitle (title_buf);
194     }
195 }
196
197 /* Returns a pointer to the fully qualified name of the directory that
198    contains the Wget binary (wget.exe).  The returned path does not have a
199    trailing path separator.  Returns NULL on failure.  */
200 char *
201 ws_mypath (void)
202 {
203   static char *wspathsave = NULL;
204
205   if (!wspathsave)
206     {
207       char buf[MAX_PATH + 1];
208       char *p;
209       DWORD len;
210
211       len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
212       if (!len || (len >= sizeof (buf)))
213         return NULL;
214
215       p = strrchr (buf, PATH_SEPARATOR);
216       if (!p)
217         return NULL;
218
219       *p = '\0';
220       wspathsave = xstrdup (buf);
221     }
222
223   return wspathsave;
224 }
225
226 /* Perform Windows specific initialization.  */
227 void
228 ws_startup (void)
229 {
230   WORD requested;
231   WSADATA data;
232   int err;
233   OSVERSIONINFO os;
234
235   if (GetVersionEx (&os) == TRUE
236       && os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
237     windows_nt_p = 1;
238
239   requested = MAKEWORD (1, 1);
240   err = WSAStartup (requested, &data);
241   if (err != 0)
242     {
243       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
244                exec_name);
245       exit (1);
246     }
247
248   if (data.wVersion < requested)
249     {
250       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
251                exec_name);
252       WSACleanup ();
253       exit (1);
254     }
255
256   atexit (ws_cleanup);
257   pwr_mode = set_sleep_mode (0);
258   SetConsoleCtrlHandler (ws_handler, TRUE);
259 }
260
261 /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
262    (The Borland utime function only works on Windows NT.)  */
263
264 #ifdef HACK_BCC_UTIME_BUG
265 int
266 borland_utime (const char *path, const struct utimbuf *times)
267 {
268   int fd;
269   int res;
270   struct ftime ft;
271   struct tm *ptr_tm;
272
273   if ((fd = open (path, O_RDWR)) < 0)
274     return -1;
275
276   ptr_tm = localtime (&times->modtime);
277   ft.ft_tsec = ptr_tm->tm_sec >> 1;
278   ft.ft_min = ptr_tm->tm_min;
279   ft.ft_hour = ptr_tm->tm_hour;
280   ft.ft_day = ptr_tm->tm_mday;
281   ft.ft_month = ptr_tm->tm_mon + 1;
282   ft.ft_year = ptr_tm->tm_year - 80;
283   res = setftime (fd, &ft);
284   close (fd);
285   return res;
286 }
287 #endif
288
289 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
290    a lengthy transfer.  Windows does not, by default, consider network
291    activity in console-programs as activity!  Works on Win-98/ME/2K
292    and up.  */
293 static DWORD
294 set_sleep_mode (DWORD mode)
295 {
296   HMODULE mod = LoadLibrary ("kernel32.dll");
297   DWORD (WINAPI *_SetThreadExecutionState) (DWORD) = NULL;
298   DWORD rc = (DWORD)-1;
299
300   if (mod)
301      (void *)_SetThreadExecutionState
302        = GetProcAddress ((HINSTANCE)mod, "SetThreadExecutionState");
303
304   if (_SetThreadExecutionState)
305     {
306       if (mode == 0)  /* first time */
307         mode = (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
308       rc = (*_SetThreadExecutionState) (mode);
309     }
310   if (mod)
311     FreeLibrary (mod);
312   DEBUGP (("set_sleep_mode(): mode 0x%08lX, rc 0x%08lX\n", mode, rc));
313   return rc;
314 }
315 \f
316 /* run_with_timeout Windows implementation.  */
317
318 /* Stack size 0 uses default thread stack-size (reserve+commit).
319    Determined by what's in the PE header.  */
320 #define THREAD_STACK_SIZE  0
321
322 struct thread_data
323 {
324   void (*fun) (void *);
325   void *arg;
326   DWORD ws_error;
327 };
328
329 /* The callback that runs FUN(ARG) in a separate thread.  This
330    function exists for two reasons: a) to not require FUN to be
331    declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
332    which are per-thread.  The latter is useful when FUN calls Winsock
333    functions, which is how run_with_timeout is used in Wget.
334
335    [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
336    the default (wcc386 -3r).  */
337
338 static DWORD WINAPI
339 thread_helper (void *arg)
340 {
341   struct thread_data *td = (struct thread_data *) arg;
342
343   /* Initialize Winsock error to what it was in the parent.  That way
344      the subsequent call to WSAGetLastError will return the same value
345      if td->fun doesn't change Winsock error state.  */
346   WSASetLastError (td->ws_error);
347
348   td->fun (td->arg);
349
350   /* Return Winsock error to the caller, in case FUN ran Winsock
351      code.  */
352   td->ws_error = WSAGetLastError ();
353   return 0;
354 }
355
356 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
357    seconds.  Returns non-zero if the function was interrupted with a
358    timeout, zero otherwise.
359
360    This works by running FUN in a separate thread and terminating the
361    thread if it doesn't finish in the specified time.  */
362
363 int
364 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
365 {
366   static HANDLE thread_hnd = NULL;
367   struct thread_data thread_arg;
368   DWORD thread_id;
369   int rc = 0;
370
371   DEBUGP (("seconds %.2f, ", seconds));
372
373   if (seconds == 0)
374     {
375     blocking_fallback:
376       fun (arg);
377       return 0;
378     }
379
380   /* Should never happen, but test for recursivety anyway.  */
381   assert (thread_hnd == NULL);
382
383   thread_arg.fun = fun;
384   thread_arg.arg = arg;
385   thread_arg.ws_error = WSAGetLastError ();
386   thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
387                              &thread_arg, 0, &thread_id);
388   if (!thread_hnd)
389     {
390       DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
391       goto blocking_fallback;
392     }
393
394   if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
395       == WAIT_OBJECT_0)
396     {
397       /* Propagate error state (which is per-thread) to this thread,
398          so the caller can inspect it.  */
399       WSASetLastError (thread_arg.ws_error);
400       DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
401       rc = 0;
402     }
403   else
404     {
405       TerminateThread (thread_hnd, 1);
406       rc = 1;
407     }
408
409   CloseHandle (thread_hnd);     /* Clear-up after TerminateThread().  */
410   thread_hnd = NULL;
411   return rc;
412 }