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