]> sjero.net Git - wget/blob - src/mswindows.c
[svn] Under Windows, if $HOME is not defined, use the directory that
[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_help (const char *name)
226 {
227   char *mypath = ws_mypath ();
228
229   if (mypath)
230     {
231       struct stat sbuf;
232       char *buf = (char *)alloca (strlen (mypath) + strlen (name) + 4 + 1);
233       sprintf (buf, "%s%s.HLP", mypath, name);
234       if (stat (buf, &sbuf) == 0) 
235         {
236           printf (_("Starting WinHelp %s\n"), buf);
237           WinHelp (NULL, buf, HELP_INDEX, 0);
238         }
239       else
240         {
241           printf ("%s: %s\n", buf, strerror (errno));
242         }
243     }
244 }
245
246 void
247 ws_startup (void)
248 {
249   WORD requested;
250   WSADATA data;
251   int err;
252   OSVERSIONINFO os;
253
254   if (GetVersionEx (&os) == TRUE
255       && os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
256     windows_nt_p = 1;
257
258   requested = MAKEWORD (1, 1);
259   err = WSAStartup (requested, &data);
260
261   if (err != 0)
262     {
263       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
264                exec_name);
265       exit (1);
266     }
267
268   if (data.wVersion < requested)
269     {
270       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
271                exec_name);
272       WSACleanup ();
273       exit (1);
274     }
275   atexit (ws_cleanup);
276   pwr_mode = set_sleep_mode (0);
277   SetConsoleCtrlHandler (ws_handler, TRUE);
278 }
279
280 /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
281    (The Borland utime function only works on Windows NT.)  */
282
283 #ifdef HACK_BCC_UTIME_BUG
284 int
285 borland_utime (const char *path, const struct utimbuf *times)
286 {
287   int fd;
288   int res;
289   struct ftime ft;
290   struct tm *ptr_tm;
291
292   if ((fd = open (path, O_RDWR)) < 0)
293     return -1;
294
295   ptr_tm = localtime (&times->modtime);
296   ft.ft_tsec = ptr_tm->tm_sec >> 1;
297   ft.ft_min = ptr_tm->tm_min;
298   ft.ft_hour = ptr_tm->tm_hour;
299   ft.ft_day = ptr_tm->tm_mday;
300   ft.ft_month = ptr_tm->tm_mon + 1;
301   ft.ft_year = ptr_tm->tm_year - 80;
302   res = setftime (fd, &ft);
303   close (fd);
304   return res;
305 }
306 #endif
307
308 /*
309  * Prevent Windows entering sleep/hibernation-mode while wget is doing
310  * a lengthy transfer.  Windows does by default not consider network
311  * activity in console-programs as activity !  Works on Win-98/ME/2K
312  * and up.
313  */
314 static DWORD
315 set_sleep_mode (DWORD mode)
316 {
317   HMODULE mod = LoadLibrary ("kernel32.dll");
318   DWORD (WINAPI *_SetThreadExecutionState) (DWORD) = NULL;
319   DWORD rc = (DWORD)-1;
320
321   if (mod)
322      (void *)_SetThreadExecutionState
323        = GetProcAddress ((HINSTANCE)mod, "SetThreadExecutionState");
324
325   if (_SetThreadExecutionState)
326     {
327       if (mode == 0)  /* first time */
328         mode = (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
329       rc = (*_SetThreadExecutionState) (mode);
330     }
331   if (mod)
332     FreeLibrary (mod);
333   DEBUGP (("set_sleep_mode(): mode 0x%08lX, rc 0x%08lX\n", mode, rc));
334   return rc;
335 }
336 \f
337 /* run_with_timeout Windows implementation. */
338
339  /* Stack size 0 uses default thread stack-size (reserve+commit).
340   * Determined by what's in the PE header.
341  */
342 #define THREAD_STACK_SIZE  0
343
344 struct thread_data {
345    void (*fun) (void *);
346    void  *arg;
347    DWORD ws_error; 
348 };
349
350 /* The callback that runs FUN(ARG) in a separate thread.  This
351    function exists for two reasons: a) to not require FUN to be
352    declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
353    which are per-thread.  The latter is useful when FUN calls Winsock
354    functions, which is how run_with_timeout is used in Wget.
355
356    [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
357    the default (wcc386 -3r).  */
358
359 static DWORD WINAPI 
360 thread_helper (void *arg)
361 {
362   struct thread_data *td = (struct thread_data *) arg;
363
364   /* Initialize Winsock error to what it was in the parent.  That way
365      the subsequent call to WSAGetLastError will return the same value
366      if td->fun doesn't change Winsock error state.  */
367   WSASetLastError (td->ws_error);
368
369   td->fun (td->arg);
370
371   /* Return Winsock error to the caller, in case FUN ran Winsock
372      code.  */
373   td->ws_error = WSAGetLastError ();
374   return 0; 
375 }
376
377 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
378    seconds.  Returns non-zero if the function was interrupted with a
379    timeout, zero otherwise.
380
381    This works by running FUN in a separate thread and terminating the
382    thread if it doesn't finish in the specified time.  */
383
384 int
385 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
386 {
387   static HANDLE thread_hnd = NULL;
388   struct thread_data thread_arg;
389   DWORD  thread_id;
390   int    rc = 0;
391
392   DEBUGP (("seconds %.2f, ", seconds));
393   
394   if (seconds == 0)
395     {
396     blocking_fallback:
397       fun (arg);
398       return 0;
399     }
400
401   /* Should never happen, but test for recursivety anyway */
402   assert (thread_hnd == NULL);  
403
404   thread_arg.fun = fun;
405   thread_arg.arg = arg;
406   thread_arg.ws_error = WSAGetLastError ();
407   thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
408                              &thread_arg, 0, &thread_id); 
409   if (!thread_hnd)
410     {
411       DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
412       goto blocking_fallback;
413     }
414
415   if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
416       == WAIT_OBJECT_0)
417     {
418       /* Propagate error state (which is per-thread) to this thread,
419          so the caller can inspect it.  */
420       WSASetLastError (thread_arg.ws_error);
421       DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
422       rc = 0;
423     }
424   else
425     {
426       TerminateThread (thread_hnd, 1);
427       rc = 1;
428     }
429
430   CloseHandle (thread_hnd); /* clear-up after TerminateThread() */
431   thread_hnd = NULL;
432   return rc;
433 }