1 /* mswindows.c -- Windows-specific support
2 Copyright (C) 1995, 1996, 1997, 1998, 2004
3 Free Software Foundation, Inc.
5 This file is part of GNU Wget.
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.
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.
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.
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. */
40 #ifdef HACK_BCC_UTIME_BUG
46 # ifdef HAVE_SYS_UTIME_H
47 # include <sys/utime.h>
59 #ifndef ES_SYSTEM_REQUIRED
60 #define ES_SYSTEM_REQUIRED 0x00000001
64 #define ES_CONTINUOUS 0x80000000
68 /* Defined in log.c. */
69 void log_request_redirect_output PARAMS ((const char *));
71 /* Windows version of xsleep in utils.c. */
74 xsleep (double seconds)
79 /* Explained in utils.c. */
81 seconds -= (long) seconds;
83 usleep (seconds * 1000000L);
84 #else /* not HAVE_USLEEP */
85 SleepEx (seconds * 1000, FALSE);
86 #endif /* not HAVE_USLEEP */
90 windows_main_junk (int *argc, char **argv, char **exec_name)
94 /* Remove .EXE from filename if it has one. */
95 *exec_name = xstrdup (*exec_name);
96 p = strrchr (*exec_name, '.');
108 ws_hangup (const char *reason)
110 /* Whether we arrange our own version of opt.lfilename here. */
115 opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
118 printf (_("Continuing in background.\n"));
120 printf (_("Output will be written to `%s'.\n"), opt.lfilename);
122 log_request_redirect_output (reason);
124 /* Detach process from the current console. Under Windows 9x, if we
125 were launched from a 16-bit process (which is usually the case;
126 command.com is 16-bit) the parent process should resume right away.
127 Under NT or if launched from a 32-process under 9x, this is a futile
128 gesture as the parent will wait for us to terminate before resuming. */
132 /* Construct the name for a named section (a.k.a. `file mapping') object.
133 The returned string is dynamically allocated and needs to be xfree()'d. */
135 make_section_name (DWORD pid)
137 return aprintf ("gnu_wget_fake_fork_%lu", pid);
140 /* This structure is used to hold all the data that is exchanged between
142 struct fake_fork_info
146 char lfilename[MAX_PATH + 1];
149 /* Determines if we are the child and if so performs the child logic.
156 fake_fork_child (void)
158 HANDLE section, event;
159 struct fake_fork_info *info;
163 name = make_section_name (GetCurrentProcessId ());
164 section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
165 le = GetLastError ();
169 if (le == ERROR_FILE_NOT_FOUND)
170 return 0; /* Section object does not exist; we are the parent. */
175 info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
178 CloseHandle (section);
186 opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
188 strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
189 info->lfilename[sizeof (info->lfilename) - 1] = '\0';
194 UnmapViewOfFile (info);
195 CloseHandle (section);
197 /* Inform the parent that we've done our part. */
198 if (!SetEvent (event))
202 return 1; /* We are the child. */
209 char *cmdline, *args;
210 char exe[MAX_PATH + 1];
212 SECURITY_ATTRIBUTES sa;
213 HANDLE section, event, h[2];
215 PROCESS_INFORMATION pi;
216 struct fake_fork_info *info;
220 event = section = pi.hProcess = pi.hThread = NULL;
222 /* Get command line arguments to pass to the child process.
223 We need to skip the name of the command (what amounts to argv[0]). */
224 cmdline = GetCommandLine ();
227 args = strchr (cmdline + 1, '"');
232 args = strchr (cmdline, ' ');
234 /* It's ok if args is NULL, that would mean there were no arguments
235 after the command name. As it is now though, we would never get here
236 if that were true. */
238 /* Get the fully qualified name of our executable. This is more reliable
239 than using argv[0]. */
240 exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
241 if (!exe_len || (exe_len >= sizeof (exe)))
244 sa.nLength = sizeof (sa);
245 sa.lpSecurityDescriptor = NULL;
246 sa.bInheritHandle = TRUE;
248 /* Create an anonymous inheritable event object that starts out
250 event = CreateEvent (&sa, FALSE, FALSE, NULL);
254 /* Creat the child process detached form the current console and in a
256 memset (&si, 0, sizeof (si));
258 rv = CreateProcess (exe, args, NULL, NULL, TRUE, CREATE_SUSPENDED |
259 DETACHED_PROCESS, NULL, NULL, &si, &pi);
263 /* Create a named section object with a name based on the process id of
265 name = make_section_name (pi.dwProcessId);
267 CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
268 sizeof (struct fake_fork_info), name);
271 /* Fail if the section object already exists (should not happen). */
272 if (!section || (le == ERROR_ALREADY_EXISTS))
278 /* Copy the event handle into the section object. */
279 info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
288 UnmapViewOfFile (info);
290 /* Start the child process. */
291 rv = ResumeThread (pi.hThread);
294 TerminateProcess (pi.hProcess, (DWORD) -1);
298 /* Wait for the child to signal to us that it has done its part. If it
299 terminates before signaling us it's an error. */
303 rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
307 info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
314 /* Ensure string is properly terminated. */
315 if (info->changedp &&
316 !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
322 printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
324 printf (_("Output will be written to `%s'.\n"), info->lfilename);
326 UnmapViewOfFile (info);
333 CloseHandle (section);
335 CloseHandle (pi.hThread);
337 CloseHandle (pi.hProcess);
339 /* We're the parent. If all is well, terminate. */
343 /* We failed, return. */
347 fork_to_background (void)
351 rv = fake_fork_child ();
354 fprintf (stderr, "fake_fork_child() failed\n");
359 /* We're the parent. */
361 /* If fake_fork() returns, it failed. */
362 fprintf (stderr, "fake_fork() failed\n");
365 /* If we get here, we're the child. */
369 ws_handler (DWORD dwEvent)
375 ws_hangup ("CTRL+C");
378 #ifdef CTRLBREAK_BACKGND
379 case CTRL_BREAK_EVENT:
380 ws_hangup ("CTRL+Break");
388 static char *title_buf = NULL;
389 static char *curr_url = NULL;
390 static int old_percentage = -1;
392 /* Updates the console title with the URL of the current file being
395 ws_changetitle (const char *url)
397 xfree_null (title_buf);
398 xfree_null (curr_url);
399 title_buf = (char *)xmalloc (strlen (url) + 20);
400 curr_url = xstrdup (url);
402 sprintf (title_buf, "Wget %s", curr_url);
403 SetConsoleTitle (title_buf);
406 /* Updates the console title with the percentage of the current file
409 ws_percenttitle (double percentage_float)
413 if (!title_buf || !curr_url)
416 percentage = (int) percentage_float;
418 /* Clamp percentage value. */
421 if (percentage > 100)
424 /* Only update the title when the percentage has changed. */
425 if (percentage == old_percentage)
428 old_percentage = percentage;
430 sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
431 SetConsoleTitle (title_buf);
434 /* Returns a pointer to the fully qualified name of the directory that
435 contains the Wget binary (wget.exe). The returned path does not have a
436 trailing path separator. Returns NULL on failure. */
440 static char *wspathsave = NULL;
444 char buf[MAX_PATH + 1];
448 len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
449 if (!len || (len >= sizeof (buf)))
452 p = strrchr (buf, PATH_SEPARATOR);
457 wspathsave = xstrdup (buf);
463 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
464 a lengthy transfer. Windows does not, by default, consider network
465 activity in console-programs as activity! Works on Win-98/ME/2K
468 set_sleep_mode (void)
470 typedef DWORD (WINAPI *func_t) (DWORD);
471 func_t set_exec_state;
474 (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
475 "SetThreadExecutionState");
478 set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
481 /* Perform Windows specific initialization. */
489 requested = MAKEWORD (1, 1);
490 err = WSAStartup (requested, &data);
493 fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
498 if (data.wVersion < requested)
500 fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
508 SetConsoleCtrlHandler (ws_handler, TRUE);
511 /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
512 (The Borland utime function only works on Windows NT.) */
514 #ifdef HACK_BCC_UTIME_BUG
516 borland_utime (const char *path, const struct utimbuf *times)
523 if ((fd = open (path, O_RDWR)) < 0)
526 ptr_tm = localtime (×->modtime);
527 ft.ft_tsec = ptr_tm->tm_sec >> 1;
528 ft.ft_min = ptr_tm->tm_min;
529 ft.ft_hour = ptr_tm->tm_hour;
530 ft.ft_day = ptr_tm->tm_mday;
531 ft.ft_month = ptr_tm->tm_mon + 1;
532 ft.ft_year = ptr_tm->tm_year - 80;
533 res = setftime (fd, &ft);
539 /* run_with_timeout Windows implementation. */
541 /* Stack size 0 uses default thread stack-size (reserve+commit).
542 Determined by what's in the PE header. */
543 #define THREAD_STACK_SIZE 0
547 void (*fun) (void *);
552 /* The callback that runs FUN(ARG) in a separate thread. This
553 function exists for two reasons: a) to not require FUN to be
554 declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
555 which are per-thread. The latter is useful when FUN calls Winsock
556 functions, which is how run_with_timeout is used in Wget.
558 [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
559 the default (wcc386 -3r). */
562 thread_helper (void *arg)
564 struct thread_data *td = (struct thread_data *) arg;
566 /* Initialize Winsock error to what it was in the parent. That way
567 the subsequent call to WSAGetLastError will return the same value
568 if td->fun doesn't change Winsock error state. */
569 WSASetLastError (td->ws_error);
573 /* Return Winsock error to the caller, in case FUN ran Winsock
575 td->ws_error = WSAGetLastError ();
579 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
580 seconds. Returns non-zero if the function was interrupted with a
581 timeout, zero otherwise.
583 This works by running FUN in a separate thread and terminating the
584 thread if it doesn't finish in the specified time. */
587 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
589 static HANDLE thread_hnd = NULL;
590 struct thread_data thread_arg;
594 DEBUGP (("seconds %.2f, ", seconds));
603 /* Should never happen, but test for recursivety anyway. */
604 assert (thread_hnd == NULL);
606 thread_arg.fun = fun;
607 thread_arg.arg = arg;
608 thread_arg.ws_error = WSAGetLastError ();
609 thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
610 &thread_arg, 0, &thread_id);
613 DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
614 goto blocking_fallback;
617 if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
620 /* Propagate error state (which is per-thread) to this thread,
621 so the caller can inspect it. */
622 WSASetLastError (thread_arg.ws_error);
623 DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
628 TerminateThread (thread_hnd, 1);
632 CloseHandle (thread_hnd); /* Clear-up after TerminateThread(). */