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