1999-12-02 02:42:23 -05:00
|
|
|
|
/* mswindows.c -- Windows-specific support
|
2007-09-28 18:45:31 -04:00
|
|
|
|
Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
|
|
|
|
|
2004, 2005, 2006, 2007 Free Software Foundation, Inc.
|
1999-12-02 02:42:23 -05:00
|
|
|
|
|
2001-05-27 15:35:15 -04:00
|
|
|
|
This file is part of GNU Wget.
|
1999-12-02 02:42:23 -05:00
|
|
|
|
|
2001-05-27 15:35:15 -04:00
|
|
|
|
GNU Wget is free software; you can redistribute it and/or modify
|
1999-12-02 02:42:23 -05:00
|
|
|
|
it under the terms of the GNU General Public License as published by
|
2007-07-10 01:53:22 -04:00
|
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
1999-12-02 02:42:23 -05:00
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
2001-05-27 15:35:15 -04:00
|
|
|
|
GNU Wget is distributed in the hope that it will be useful,
|
1999-12-02 02:42:23 -05:00
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
2007-07-10 01:53:22 -04:00
|
|
|
|
along with Wget. If not, see <http://www.gnu.org/licenses/>.
|
2002-05-17 22:16:36 -04:00
|
|
|
|
|
|
|
|
|
In addition, as a special exception, the Free Software Foundation
|
|
|
|
|
gives permission to link the code of its release of Wget with the
|
|
|
|
|
OpenSSL project's "OpenSSL" library (or with modified versions of it
|
|
|
|
|
that use the same license as the "OpenSSL" library), and distribute
|
|
|
|
|
the linked executables. You must obey the GNU General Public License
|
|
|
|
|
in all respects for all of the code used other than "OpenSSL". If you
|
|
|
|
|
modify this file, you may extend this exception to your version of the
|
|
|
|
|
file, but you are not obligated to do so. If you do not wish to do
|
|
|
|
|
so, delete this exception statement from your version. */
|
1999-12-02 02:42:23 -05:00
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <assert.h>
|
2000-12-17 13:12:02 -05:00
|
|
|
|
#include <errno.h>
|
2003-09-30 17:24:36 -04:00
|
|
|
|
#include <math.h>
|
1999-12-02 02:42:23 -05:00
|
|
|
|
|
2005-03-06 11:36:45 -05:00
|
|
|
|
#define INHIBIT_WRAP /* avoid wrapping of socket, bind, ... */
|
|
|
|
|
|
1999-12-02 02:42:23 -05:00
|
|
|
|
#include "wget.h"
|
2002-03-20 13:56:45 -05:00
|
|
|
|
#include "utils.h"
|
1999-12-02 02:42:23 -05:00
|
|
|
|
#include "url.h"
|
|
|
|
|
|
2003-09-26 20:35:31 -04:00
|
|
|
|
#ifndef ES_SYSTEM_REQUIRED
|
|
|
|
|
#define ES_SYSTEM_REQUIRED 0x00000001
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef ES_CONTINUOUS
|
|
|
|
|
#define ES_CONTINUOUS 0x80000000
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
1999-12-02 02:42:23 -05:00
|
|
|
|
/* Defined in log.c. */
|
2005-06-19 18:34:58 -04:00
|
|
|
|
void log_request_redirect_output (const char *);
|
1999-12-02 02:42:23 -05:00
|
|
|
|
|
2003-11-03 16:57:04 -05:00
|
|
|
|
/* Windows version of xsleep in utils.c. */
|
2003-09-26 20:35:31 -04:00
|
|
|
|
|
2003-11-03 16:57:04 -05:00
|
|
|
|
void
|
|
|
|
|
xsleep (double seconds)
|
2001-11-29 09:15:11 -05:00
|
|
|
|
{
|
2003-11-03 16:57:04 -05:00
|
|
|
|
#ifdef HAVE_USLEEP
|
|
|
|
|
if (seconds > 1000)
|
|
|
|
|
{
|
|
|
|
|
/* Explained in utils.c. */
|
|
|
|
|
sleep (seconds);
|
|
|
|
|
seconds -= (long) seconds;
|
|
|
|
|
}
|
2005-07-06 18:09:50 -04:00
|
|
|
|
usleep (seconds * 1000000);
|
2003-11-03 16:57:04 -05:00
|
|
|
|
#else /* not HAVE_USLEEP */
|
2005-03-20 05:41:46 -05:00
|
|
|
|
SleepEx ((DWORD) (seconds * 1000 + .5), FALSE);
|
2003-11-03 16:57:04 -05:00
|
|
|
|
#endif /* not HAVE_USLEEP */
|
2001-11-29 09:15:11 -05:00
|
|
|
|
}
|
|
|
|
|
|
1999-12-02 02:42:23 -05:00
|
|
|
|
void
|
2005-03-06 11:36:45 -05:00
|
|
|
|
windows_main (int *argc, char **argv, char **exec_name)
|
1999-12-02 02:42:23 -05:00
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
/* Remove .EXE from filename if it has one. */
|
|
|
|
|
*exec_name = xstrdup (*exec_name);
|
|
|
|
|
p = strrchr (*exec_name, '.');
|
|
|
|
|
if (p)
|
|
|
|
|
*p = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ws_cleanup (void)
|
|
|
|
|
{
|
|
|
|
|
WSACleanup ();
|
|
|
|
|
}
|
|
|
|
|
|
2006-12-29 12:23:20 -05:00
|
|
|
|
#if defined(CTRLBREAK_BACKGND) || defined(CTRLC_BACKGND)
|
1999-12-02 02:42:23 -05:00
|
|
|
|
static void
|
2004-03-19 18:54:27 -05:00
|
|
|
|
ws_hangup (const char *reason)
|
1999-12-02 02:42:23 -05:00
|
|
|
|
{
|
2005-03-04 14:26:18 -05:00
|
|
|
|
fprintf (stderr, _("Continuing in background.\n"));
|
2004-03-19 18:54:27 -05:00
|
|
|
|
log_request_redirect_output (reason);
|
|
|
|
|
|
|
|
|
|
/* Detach process from the current console. Under Windows 9x, if we
|
|
|
|
|
were launched from a 16-bit process (which is usually the case;
|
|
|
|
|
command.com is 16-bit) the parent process should resume right away.
|
|
|
|
|
Under NT or if launched from a 32-process under 9x, this is a futile
|
|
|
|
|
gesture as the parent will wait for us to terminate before resuming. */
|
|
|
|
|
FreeConsole ();
|
|
|
|
|
}
|
2006-12-29 12:23:20 -05:00
|
|
|
|
#endif
|
2004-03-19 18:54:27 -05:00
|
|
|
|
|
2004-03-24 14:16:08 -05:00
|
|
|
|
/* Construct the name for a named section (a.k.a. `file mapping') object.
|
|
|
|
|
The returned string is dynamically allocated and needs to be xfree()'d. */
|
|
|
|
|
static char *
|
|
|
|
|
make_section_name (DWORD pid)
|
|
|
|
|
{
|
|
|
|
|
return aprintf ("gnu_wget_fake_fork_%lu", pid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This structure is used to hold all the data that is exchanged between
|
|
|
|
|
parent and child. */
|
|
|
|
|
struct fake_fork_info
|
|
|
|
|
{
|
|
|
|
|
HANDLE event;
|
2005-06-22 15:38:10 -04:00
|
|
|
|
bool logfile_changed;
|
2004-03-24 14:16:08 -05:00
|
|
|
|
char lfilename[MAX_PATH + 1];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Determines if we are the child and if so performs the child logic.
|
|
|
|
|
Return values:
|
|
|
|
|
< 0 error
|
|
|
|
|
0 parent
|
|
|
|
|
> 0 child
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
fake_fork_child (void)
|
|
|
|
|
{
|
|
|
|
|
HANDLE section, event;
|
|
|
|
|
struct fake_fork_info *info;
|
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
|
|
name = make_section_name (GetCurrentProcessId ());
|
|
|
|
|
section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
|
|
|
|
|
xfree (name);
|
2004-03-25 14:18:54 -05:00
|
|
|
|
/* It seems that Windows 9x and NT set last-error inconsistently when
|
|
|
|
|
OpenFileMapping() fails; so we assume it failed because the section
|
|
|
|
|
object does not exist. */
|
2004-03-24 14:16:08 -05:00
|
|
|
|
if (!section)
|
2004-03-25 14:18:54 -05:00
|
|
|
|
return 0; /* We are the parent. */
|
2004-03-24 14:16:08 -05:00
|
|
|
|
|
|
|
|
|
info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
|
|
|
|
|
if (!info)
|
|
|
|
|
{
|
|
|
|
|
CloseHandle (section);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
event = info->event;
|
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
info->logfile_changed = false;
|
2004-03-24 14:16:08 -05:00
|
|
|
|
if (!opt.lfilename)
|
|
|
|
|
{
|
2005-03-04 14:26:18 -05:00
|
|
|
|
/* See utils:fork_to_background for explanation. */
|
2005-06-22 15:38:10 -04:00
|
|
|
|
FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, false, &opt.lfilename);
|
2005-03-04 14:26:18 -05:00
|
|
|
|
if (new_log_fp)
|
2007-08-02 23:38:21 -04:00
|
|
|
|
{
|
|
|
|
|
info->logfile_changed = true;
|
|
|
|
|
strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
|
|
|
|
|
info->lfilename[sizeof (info->lfilename) - 1] = '\0';
|
|
|
|
|
fclose (new_log_fp);
|
|
|
|
|
}
|
2004-03-24 14:16:08 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UnmapViewOfFile (info);
|
|
|
|
|
CloseHandle (section);
|
|
|
|
|
|
|
|
|
|
/* Inform the parent that we've done our part. */
|
|
|
|
|
if (!SetEvent (event))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
CloseHandle (event);
|
|
|
|
|
return 1; /* We are the child. */
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-24 19:06:04 -05:00
|
|
|
|
/* Windows doesn't support the fork() call; so we fake it by invoking
|
|
|
|
|
another copy of Wget with the same arguments with which we were
|
|
|
|
|
invoked. The child copy of Wget should perform the same initialization
|
|
|
|
|
sequence as the parent; so we should have two processes that are
|
|
|
|
|
essentially identical. We create a specially named section object that
|
|
|
|
|
allows the child to distinguish itself from the parent and is used to
|
|
|
|
|
exchange information between the two processes. We use an event object
|
|
|
|
|
for synchronization. */
|
2004-03-24 14:16:08 -05:00
|
|
|
|
static void
|
|
|
|
|
fake_fork (void)
|
|
|
|
|
{
|
|
|
|
|
char exe[MAX_PATH + 1];
|
|
|
|
|
DWORD exe_len, le;
|
|
|
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
|
HANDLE section, event, h[2];
|
|
|
|
|
STARTUPINFO si;
|
|
|
|
|
PROCESS_INFORMATION pi;
|
|
|
|
|
struct fake_fork_info *info;
|
|
|
|
|
char *name;
|
|
|
|
|
BOOL rv;
|
|
|
|
|
|
2005-03-06 11:36:45 -05:00
|
|
|
|
section = pi.hProcess = pi.hThread = NULL;
|
2004-03-24 14:16:08 -05:00
|
|
|
|
|
|
|
|
|
/* Get the fully qualified name of our executable. This is more reliable
|
|
|
|
|
than using argv[0]. */
|
|
|
|
|
exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
|
|
|
|
|
if (!exe_len || (exe_len >= sizeof (exe)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
sa.nLength = sizeof (sa);
|
|
|
|
|
sa.lpSecurityDescriptor = NULL;
|
|
|
|
|
sa.bInheritHandle = TRUE;
|
|
|
|
|
|
|
|
|
|
/* Create an anonymous inheritable event object that starts out
|
|
|
|
|
non-signaled. */
|
|
|
|
|
event = CreateEvent (&sa, FALSE, FALSE, NULL);
|
|
|
|
|
if (!event)
|
|
|
|
|
return;
|
|
|
|
|
|
2004-03-25 14:18:54 -05:00
|
|
|
|
/* Create the child process detached form the current console and in a
|
2004-03-24 14:16:08 -05:00
|
|
|
|
suspended state. */
|
2005-04-06 16:42:22 -04:00
|
|
|
|
xzero (si);
|
2004-03-24 14:16:08 -05:00
|
|
|
|
si.cb = sizeof (si);
|
2004-05-09 17:50:00 -04:00
|
|
|
|
rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
|
|
|
|
|
CREATE_SUSPENDED | DETACHED_PROCESS,
|
|
|
|
|
NULL, NULL, &si, &pi);
|
2004-03-24 14:16:08 -05:00
|
|
|
|
if (!rv)
|
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
|
|
/* Create a named section object with a name based on the process id of
|
|
|
|
|
the child. */
|
|
|
|
|
name = make_section_name (pi.dwProcessId);
|
|
|
|
|
section =
|
|
|
|
|
CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
|
|
|
|
|
sizeof (struct fake_fork_info), name);
|
|
|
|
|
le = GetLastError();
|
|
|
|
|
xfree (name);
|
|
|
|
|
/* Fail if the section object already exists (should not happen). */
|
|
|
|
|
if (!section || (le == ERROR_ALREADY_EXISTS))
|
|
|
|
|
{
|
|
|
|
|
rv = FALSE;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy the event handle into the section object. */
|
|
|
|
|
info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
|
|
|
|
|
if (!info)
|
|
|
|
|
{
|
|
|
|
|
rv = FALSE;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info->event = event;
|
|
|
|
|
|
|
|
|
|
UnmapViewOfFile (info);
|
|
|
|
|
|
|
|
|
|
/* Start the child process. */
|
|
|
|
|
rv = ResumeThread (pi.hThread);
|
|
|
|
|
if (!rv)
|
|
|
|
|
{
|
|
|
|
|
TerminateProcess (pi.hProcess, (DWORD) -1);
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wait for the child to signal to us that it has done its part. If it
|
|
|
|
|
terminates before signaling us it's an error. */
|
|
|
|
|
|
|
|
|
|
h[0] = event;
|
|
|
|
|
h[1] = pi.hProcess;
|
|
|
|
|
rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
|
|
|
|
|
if (!rv)
|
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
|
|
info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
|
|
|
|
|
if (!info)
|
|
|
|
|
{
|
|
|
|
|
rv = FALSE;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ensure string is properly terminated. */
|
2005-03-04 14:26:18 -05:00
|
|
|
|
if (info->logfile_changed &&
|
2004-03-24 14:16:08 -05:00
|
|
|
|
!memchr (info->lfilename, '\0', sizeof (info->lfilename)))
|
|
|
|
|
{
|
|
|
|
|
rv = FALSE;
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
|
2005-03-04 14:26:18 -05:00
|
|
|
|
if (info->logfile_changed)
|
2004-03-24 14:16:08 -05:00
|
|
|
|
printf (_("Output will be written to `%s'.\n"), info->lfilename);
|
|
|
|
|
|
|
|
|
|
UnmapViewOfFile (info);
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
|
|
|
|
if (event)
|
|
|
|
|
CloseHandle (event);
|
|
|
|
|
if (section)
|
|
|
|
|
CloseHandle (section);
|
|
|
|
|
if (pi.hThread)
|
|
|
|
|
CloseHandle (pi.hThread);
|
|
|
|
|
if (pi.hProcess)
|
|
|
|
|
CloseHandle (pi.hProcess);
|
|
|
|
|
|
|
|
|
|
/* We're the parent. If all is well, terminate. */
|
|
|
|
|
if (rv)
|
|
|
|
|
exit (0);
|
|
|
|
|
|
|
|
|
|
/* We failed, return. */
|
|
|
|
|
}
|
|
|
|
|
|
2004-03-24 19:06:04 -05:00
|
|
|
|
/* This is the corresponding Windows implementation of the
|
|
|
|
|
fork_to_background() function in utils.c. */
|
2004-03-19 18:54:27 -05:00
|
|
|
|
void
|
|
|
|
|
fork_to_background (void)
|
|
|
|
|
{
|
2004-03-24 14:16:08 -05:00
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
|
|
rv = fake_fork_child ();
|
|
|
|
|
if (rv < 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "fake_fork_child() failed\n");
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
else if (rv == 0)
|
|
|
|
|
{
|
|
|
|
|
/* We're the parent. */
|
|
|
|
|
fake_fork ();
|
|
|
|
|
/* If fake_fork() returns, it failed. */
|
|
|
|
|
fprintf (stderr, "fake_fork() failed\n");
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
/* If we get here, we're the child. */
|
1999-12-02 02:42:23 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BOOL WINAPI
|
|
|
|
|
ws_handler (DWORD dwEvent)
|
|
|
|
|
{
|
|
|
|
|
switch (dwEvent)
|
|
|
|
|
{
|
|
|
|
|
#ifdef CTRLC_BACKGND
|
|
|
|
|
case CTRL_C_EVENT:
|
2004-03-19 18:54:27 -05:00
|
|
|
|
ws_hangup ("CTRL+C");
|
|
|
|
|
return TRUE;
|
1999-12-02 02:42:23 -05:00
|
|
|
|
#endif
|
|
|
|
|
#ifdef CTRLBREAK_BACKGND
|
|
|
|
|
case CTRL_BREAK_EVENT:
|
2004-03-19 18:54:27 -05:00
|
|
|
|
ws_hangup ("CTRL+Break");
|
|
|
|
|
return TRUE;
|
1999-12-02 02:42:23 -05:00
|
|
|
|
#endif
|
|
|
|
|
default:
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-30 17:24:36 -04:00
|
|
|
|
static char *title_buf = NULL;
|
|
|
|
|
static char *curr_url = NULL;
|
2004-02-25 18:45:24 -05:00
|
|
|
|
static int old_percentage = -1;
|
2003-09-30 17:24:36 -04:00
|
|
|
|
|
2004-02-25 18:45:24 -05:00
|
|
|
|
/* Updates the console title with the URL of the current file being
|
|
|
|
|
transferred. */
|
1999-12-02 02:42:23 -05:00
|
|
|
|
void
|
2004-02-25 18:45:24 -05:00
|
|
|
|
ws_changetitle (const char *url)
|
1999-12-02 02:42:23 -05:00
|
|
|
|
{
|
2004-02-25 18:45:24 -05:00
|
|
|
|
xfree_null (title_buf);
|
|
|
|
|
xfree_null (curr_url);
|
2005-06-19 19:03:27 -04:00
|
|
|
|
title_buf = xmalloc (strlen (url) + 20);
|
2004-02-25 18:45:24 -05:00
|
|
|
|
curr_url = xstrdup (url);
|
|
|
|
|
old_percentage = -1;
|
|
|
|
|
sprintf (title_buf, "Wget %s", curr_url);
|
|
|
|
|
SetConsoleTitle (title_buf);
|
2003-09-30 17:24:36 -04:00
|
|
|
|
}
|
|
|
|
|
|
2004-02-25 18:45:24 -05:00
|
|
|
|
/* Updates the console title with the percentage of the current file
|
|
|
|
|
transferred. */
|
2003-09-30 17:24:36 -04:00
|
|
|
|
void
|
2004-02-25 18:45:24 -05:00
|
|
|
|
ws_percenttitle (double percentage_float)
|
2003-09-30 17:24:36 -04:00
|
|
|
|
{
|
2004-03-03 19:06:46 -05:00
|
|
|
|
int percentage;
|
2004-02-25 18:45:24 -05:00
|
|
|
|
|
2004-03-03 19:06:46 -05:00
|
|
|
|
if (!title_buf || !curr_url)
|
2004-02-25 18:45:24 -05:00
|
|
|
|
return;
|
|
|
|
|
|
2004-03-03 19:06:46 -05:00
|
|
|
|
percentage = (int) percentage_float;
|
2004-02-25 18:45:24 -05:00
|
|
|
|
|
2004-03-03 19:06:46 -05:00
|
|
|
|
/* Clamp percentage value. */
|
|
|
|
|
if (percentage < 0)
|
|
|
|
|
percentage = 0;
|
2004-02-25 18:45:24 -05:00
|
|
|
|
if (percentage > 100)
|
2004-03-03 19:06:46 -05:00
|
|
|
|
percentage = 100;
|
|
|
|
|
|
|
|
|
|
/* Only update the title when the percentage has changed. */
|
|
|
|
|
if (percentage == old_percentage)
|
2004-02-25 18:45:24 -05:00
|
|
|
|
return;
|
|
|
|
|
|
2004-03-03 19:06:46 -05:00
|
|
|
|
old_percentage = percentage;
|
2004-02-25 18:45:24 -05:00
|
|
|
|
|
|
|
|
|
sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
|
|
|
|
|
SetConsoleTitle (title_buf);
|
1999-12-02 02:42:23 -05:00
|
|
|
|
}
|
|
|
|
|
|
2004-02-23 22:29:55 -05:00
|
|
|
|
/* Returns a pointer to the fully qualified name of the directory that
|
|
|
|
|
contains the Wget binary (wget.exe). The returned path does not have a
|
|
|
|
|
trailing path separator. Returns NULL on failure. */
|
1999-12-02 02:42:23 -05:00
|
|
|
|
char *
|
|
|
|
|
ws_mypath (void)
|
|
|
|
|
{
|
2003-09-30 17:24:36 -04:00
|
|
|
|
static char *wspathsave = NULL;
|
1999-12-02 02:42:23 -05:00
|
|
|
|
|
2004-02-17 10:37:31 -05:00
|
|
|
|
if (!wspathsave)
|
1999-12-02 02:42:23 -05:00
|
|
|
|
{
|
2004-02-17 10:37:31 -05:00
|
|
|
|
char buf[MAX_PATH + 1];
|
|
|
|
|
char *p;
|
|
|
|
|
DWORD len;
|
2001-04-03 14:22:04 -04:00
|
|
|
|
|
2004-02-17 10:37:31 -05:00
|
|
|
|
len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
|
|
|
|
|
if (!len || (len >= sizeof (buf)))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
p = strrchr (buf, PATH_SEPARATOR);
|
|
|
|
|
if (!p)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
|
wspathsave = xstrdup (buf);
|
1999-12-02 02:42:23 -05:00
|
|
|
|
}
|
2004-02-17 10:37:31 -05:00
|
|
|
|
|
2001-04-03 14:22:04 -04:00
|
|
|
|
return wspathsave;
|
1999-12-02 02:42:23 -05:00
|
|
|
|
}
|
|
|
|
|
|
2004-02-26 09:34:17 -05:00
|
|
|
|
/* Prevent Windows entering sleep/hibernation-mode while Wget is doing
|
|
|
|
|
a lengthy transfer. Windows does not, by default, consider network
|
|
|
|
|
activity in console-programs as activity! Works on Win-98/ME/2K
|
|
|
|
|
and up. */
|
|
|
|
|
static void
|
|
|
|
|
set_sleep_mode (void)
|
|
|
|
|
{
|
|
|
|
|
typedef DWORD (WINAPI *func_t) (DWORD);
|
|
|
|
|
func_t set_exec_state;
|
|
|
|
|
|
|
|
|
|
set_exec_state =
|
|
|
|
|
(func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
|
|
|
|
|
"SetThreadExecutionState");
|
|
|
|
|
|
|
|
|
|
if (set_exec_state)
|
|
|
|
|
set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
|
|
|
|
|
}
|
|
|
|
|
|
2004-02-23 22:29:55 -05:00
|
|
|
|
/* Perform Windows specific initialization. */
|
1999-12-02 02:42:23 -05:00
|
|
|
|
void
|
|
|
|
|
ws_startup (void)
|
|
|
|
|
{
|
|
|
|
|
WSADATA data;
|
2005-03-20 05:41:46 -05:00
|
|
|
|
WORD requested = MAKEWORD (1, 1);
|
|
|
|
|
int err = WSAStartup (requested, &data);
|
1999-12-02 02:42:23 -05:00
|
|
|
|
if (err != 0)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
|
2007-08-02 23:38:21 -04:00
|
|
|
|
exec_name);
|
1999-12-02 02:42:23 -05:00
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
2001-04-03 14:22:04 -04:00
|
|
|
|
if (data.wVersion < requested)
|
1999-12-02 02:42:23 -05:00
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
|
2007-08-02 23:38:21 -04:00
|
|
|
|
exec_name);
|
1999-12-02 02:42:23 -05:00
|
|
|
|
WSACleanup ();
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
2004-02-23 22:29:55 -05:00
|
|
|
|
|
1999-12-02 02:42:23 -05:00
|
|
|
|
atexit (ws_cleanup);
|
2004-02-26 09:34:17 -05:00
|
|
|
|
set_sleep_mode ();
|
1999-12-02 02:42:23 -05:00
|
|
|
|
SetConsoleCtrlHandler (ws_handler, TRUE);
|
|
|
|
|
}
|
2003-10-02 19:28:49 -04:00
|
|
|
|
|
2004-02-23 22:29:55 -05:00
|
|
|
|
/* run_with_timeout Windows implementation. */
|
2003-10-02 19:28:49 -04:00
|
|
|
|
|
2004-02-23 22:29:55 -05:00
|
|
|
|
/* Stack size 0 uses default thread stack-size (reserve+commit).
|
|
|
|
|
Determined by what's in the PE header. */
|
2003-10-04 17:54:12 -04:00
|
|
|
|
#define THREAD_STACK_SIZE 0
|
2003-10-02 19:28:49 -04:00
|
|
|
|
|
2004-02-23 22:29:55 -05:00
|
|
|
|
struct thread_data
|
|
|
|
|
{
|
|
|
|
|
void (*fun) (void *);
|
|
|
|
|
void *arg;
|
|
|
|
|
DWORD ws_error;
|
2003-10-02 19:28:49 -04:00
|
|
|
|
};
|
|
|
|
|
|
2003-10-04 17:54:12 -04:00
|
|
|
|
/* The callback that runs FUN(ARG) in a separate thread. This
|
|
|
|
|
function exists for two reasons: a) to not require FUN to be
|
|
|
|
|
declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
|
|
|
|
|
which are per-thread. The latter is useful when FUN calls Winsock
|
|
|
|
|
functions, which is how run_with_timeout is used in Wget.
|
|
|
|
|
|
|
|
|
|
[1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
|
|
|
|
|
the default (wcc386 -3r). */
|
|
|
|
|
|
2004-02-23 22:29:55 -05:00
|
|
|
|
static DWORD WINAPI
|
2003-10-02 19:28:49 -04:00
|
|
|
|
thread_helper (void *arg)
|
|
|
|
|
{
|
|
|
|
|
struct thread_data *td = (struct thread_data *) arg;
|
2003-10-04 17:54:12 -04:00
|
|
|
|
|
|
|
|
|
/* Initialize Winsock error to what it was in the parent. That way
|
|
|
|
|
the subsequent call to WSAGetLastError will return the same value
|
|
|
|
|
if td->fun doesn't change Winsock error state. */
|
|
|
|
|
WSASetLastError (td->ws_error);
|
|
|
|
|
|
|
|
|
|
td->fun (td->arg);
|
|
|
|
|
|
|
|
|
|
/* Return Winsock error to the caller, in case FUN ran Winsock
|
|
|
|
|
code. */
|
2003-10-07 20:09:56 -04:00
|
|
|
|
td->ws_error = WSAGetLastError ();
|
2004-02-23 22:29:55 -05:00
|
|
|
|
return 0;
|
2003-10-02 19:28:49 -04:00
|
|
|
|
}
|
|
|
|
|
|
2003-10-07 20:16:43 -04:00
|
|
|
|
/* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
|
2005-06-22 15:38:10 -04:00
|
|
|
|
seconds. Returns true if the function was interrupted with a
|
|
|
|
|
timeout, false otherwise.
|
2003-10-07 20:16:43 -04:00
|
|
|
|
|
|
|
|
|
This works by running FUN in a separate thread and terminating the
|
|
|
|
|
thread if it doesn't finish in the specified time. */
|
2003-10-02 19:28:49 -04:00
|
|
|
|
|
2005-06-22 15:38:10 -04:00
|
|
|
|
bool
|
2003-10-02 19:28:49 -04:00
|
|
|
|
run_with_timeout (double seconds, void (*fun) (void *), void *arg)
|
|
|
|
|
{
|
2006-02-09 07:39:27 -05:00
|
|
|
|
HANDLE thread_hnd;
|
2003-10-02 19:28:49 -04:00
|
|
|
|
struct thread_data thread_arg;
|
2004-02-23 22:29:55 -05:00
|
|
|
|
DWORD thread_id;
|
2005-06-22 15:38:10 -04:00
|
|
|
|
bool rc;
|
2003-10-02 19:28:49 -04:00
|
|
|
|
|
2003-10-04 17:54:12 -04:00
|
|
|
|
DEBUGP (("seconds %.2f, ", seconds));
|
2004-02-23 22:29:55 -05:00
|
|
|
|
|
2003-10-02 19:28:49 -04:00
|
|
|
|
if (seconds == 0)
|
|
|
|
|
{
|
|
|
|
|
blocking_fallback:
|
|
|
|
|
fun (arg);
|
2005-06-22 15:38:10 -04:00
|
|
|
|
return false;
|
2003-10-02 19:28:49 -04:00
|
|
|
|
}
|
2003-09-26 20:35:31 -04:00
|
|
|
|
|
2003-10-02 19:28:49 -04:00
|
|
|
|
thread_arg.fun = fun;
|
2003-10-04 17:54:12 -04:00
|
|
|
|
thread_arg.arg = arg;
|
|
|
|
|
thread_arg.ws_error = WSAGetLastError ();
|
|
|
|
|
thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
&thread_arg, 0, &thread_id);
|
2003-10-02 19:28:49 -04:00
|
|
|
|
if (!thread_hnd)
|
|
|
|
|
{
|
2007-08-27 13:48:16 -04:00
|
|
|
|
DEBUGP (("CreateThread() failed; [%#lx]\n",
|
|
|
|
|
(unsigned long) GetLastError ()));
|
2003-10-02 19:28:49 -04:00
|
|
|
|
goto blocking_fallback;
|
|
|
|
|
}
|
|
|
|
|
|
2003-10-07 20:09:56 -04:00
|
|
|
|
if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
|
|
|
|
|
== WAIT_OBJECT_0)
|
2003-10-02 19:28:49 -04:00
|
|
|
|
{
|
|
|
|
|
/* Propagate error state (which is per-thread) to this thread,
|
2007-08-02 23:38:21 -04:00
|
|
|
|
so the caller can inspect it. */
|
2003-10-02 19:28:49 -04:00
|
|
|
|
WSASetLastError (thread_arg.ws_error);
|
2003-10-07 20:09:56 -04:00
|
|
|
|
DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
|
2005-06-22 15:38:10 -04:00
|
|
|
|
rc = false;
|
2003-10-04 17:54:12 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
TerminateThread (thread_hnd, 1);
|
2005-06-22 15:38:10 -04:00
|
|
|
|
rc = true;
|
2003-10-02 19:28:49 -04:00
|
|
|
|
}
|
2003-10-04 17:54:12 -04:00
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
CloseHandle (thread_hnd); /* Clear-up after TerminateThread(). */
|
2003-10-02 19:28:49 -04:00
|
|
|
|
thread_hnd = NULL;
|
2003-10-04 17:54:12 -04:00
|
|
|
|
return rc;
|
2003-10-02 19:28:49 -04:00
|
|
|
|
}
|
2005-03-06 11:36:45 -05:00
|
|
|
|
|
2005-03-20 05:41:46 -05:00
|
|
|
|
/* Wget expects network calls such as connect, recv, send, etc., to set
|
|
|
|
|
errno on failure. To achieve that, Winsock calls are wrapped with code
|
|
|
|
|
that, in case of error, sets errno to the value of WSAGetLastError().
|
|
|
|
|
In addition, we provide a wrapper around strerror, which recognizes
|
|
|
|
|
Winsock errors and prints the appropriate error message. */
|
2005-03-06 11:36:45 -05:00
|
|
|
|
|
|
|
|
|
/* Define a macro that creates a function definition that wraps FUN into
|
|
|
|
|
a function that sets errno the way the rest of the code expects. */
|
|
|
|
|
|
2007-08-02 23:38:21 -04:00
|
|
|
|
#define WRAP(fun, decl, call) int wrapped_##fun decl { \
|
|
|
|
|
int retval = fun call; \
|
|
|
|
|
if (retval < 0) \
|
|
|
|
|
errno = WSAGetLastError (); \
|
|
|
|
|
return retval; \
|
2005-03-06 11:36:45 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WRAP (socket, (int domain, int type, int protocol), (domain, type, protocol))
|
|
|
|
|
WRAP (bind, (int s, struct sockaddr *a, int alen), (s, a, alen))
|
|
|
|
|
WRAP (connect, (int s, const struct sockaddr *a, int alen), (s, a, alen))
|
2005-07-03 16:43:57 -04:00
|
|
|
|
WRAP (listen, (int s, int backlog), (s, backlog))
|
|
|
|
|
WRAP (accept, (int s, struct sockaddr *a, int *alen), (s, a, alen))
|
2005-03-06 11:36:45 -05:00
|
|
|
|
WRAP (recv, (int s, void *buf, int len, int flags), (s, buf, len, flags))
|
|
|
|
|
WRAP (send, (int s, const void *buf, int len, int flags), (s, buf, len, flags))
|
|
|
|
|
WRAP (select, (int n, fd_set *r, fd_set *w, fd_set *e, const struct timeval *tm),
|
|
|
|
|
(n, r, w, e, tm))
|
|
|
|
|
WRAP (getsockname, (int s, struct sockaddr *n, int *nlen), (s, n, nlen))
|
|
|
|
|
WRAP (getpeername, (int s, struct sockaddr *n, int *nlen), (s, n, nlen))
|
|
|
|
|
WRAP (setsockopt, (int s, int level, int opt, const void *val, int len),
|
|
|
|
|
(s, level, opt, val, len))
|
2005-03-20 05:41:46 -05:00
|
|
|
|
WRAP (closesocket, (int s), (s))
|
2005-03-06 11:36:45 -05:00
|
|
|
|
|
|
|
|
|
/* Return the text of the error message for Winsock error WSERR. */
|
|
|
|
|
|
|
|
|
|
static const char *
|
|
|
|
|
get_winsock_error (int wserr)
|
|
|
|
|
{
|
|
|
|
|
switch (wserr) {
|
|
|
|
|
case WSAEINTR: return "Interrupted system call";
|
|
|
|
|
case WSAEBADF: return "Bad file number";
|
|
|
|
|
case WSAEACCES: return "Permission denied";
|
|
|
|
|
case WSAEFAULT: return "Bad address";
|
|
|
|
|
case WSAEINVAL: return "Invalid argument";
|
|
|
|
|
case WSAEMFILE: return "Too many open files";
|
|
|
|
|
case WSAEWOULDBLOCK: return "Resource temporarily unavailable";
|
|
|
|
|
case WSAEINPROGRESS: return "Operation now in progress";
|
|
|
|
|
case WSAEALREADY: return "Operation already in progress";
|
|
|
|
|
case WSAENOTSOCK: return "Socket operation on nonsocket";
|
|
|
|
|
case WSAEDESTADDRREQ: return "Destination address required";
|
|
|
|
|
case WSAEMSGSIZE: return "Message too long";
|
|
|
|
|
case WSAEPROTOTYPE: return "Protocol wrong type for socket";
|
|
|
|
|
case WSAENOPROTOOPT: return "Bad protocol option";
|
|
|
|
|
case WSAEPROTONOSUPPORT: return "Protocol not supported";
|
|
|
|
|
case WSAESOCKTNOSUPPORT: return "Socket type not supported";
|
|
|
|
|
case WSAEOPNOTSUPP: return "Operation not supported";
|
|
|
|
|
case WSAEPFNOSUPPORT: return "Protocol family not supported";
|
|
|
|
|
case WSAEAFNOSUPPORT: return "Address family not supported by protocol family";
|
|
|
|
|
case WSAEADDRINUSE: return "Address already in use";
|
|
|
|
|
case WSAEADDRNOTAVAIL: return "Cannot assign requested address";
|
|
|
|
|
case WSAENETDOWN: return "Network is down";
|
|
|
|
|
case WSAENETUNREACH: return "Network is unreachable";
|
|
|
|
|
case WSAENETRESET: return "Network dropped connection on reset";
|
|
|
|
|
case WSAECONNABORTED: return "Software caused connection abort";
|
|
|
|
|
case WSAECONNRESET: return "Connection reset by peer";
|
|
|
|
|
case WSAENOBUFS: return "No buffer space available";
|
|
|
|
|
case WSAEISCONN: return "Socket is already connected";
|
|
|
|
|
case WSAENOTCONN: return "Socket is not connected";
|
|
|
|
|
case WSAESHUTDOWN: return "Cannot send after socket shutdown";
|
|
|
|
|
case WSAETOOMANYREFS: return "Too many references";
|
|
|
|
|
case WSAETIMEDOUT: return "Connection timed out";
|
|
|
|
|
case WSAECONNREFUSED: return "Connection refused";
|
|
|
|
|
case WSAELOOP: return "Too many levels of symbolic links";
|
|
|
|
|
case WSAENAMETOOLONG: return "File name too long";
|
|
|
|
|
case WSAEHOSTDOWN: return "Host is down";
|
|
|
|
|
case WSAEHOSTUNREACH: return "No route to host";
|
|
|
|
|
case WSAENOTEMPTY: return "Not empty";
|
|
|
|
|
case WSAEPROCLIM: return "Too many processes";
|
|
|
|
|
case WSAEUSERS: return "Too many users";
|
|
|
|
|
case WSAEDQUOT: return "Bad quota";
|
|
|
|
|
case WSAESTALE: return "Something is stale";
|
|
|
|
|
case WSAEREMOTE: return "Remote error";
|
|
|
|
|
case WSAEDISCON: return "Disconnected";
|
|
|
|
|
|
|
|
|
|
/* Extended Winsock errors */
|
|
|
|
|
case WSASYSNOTREADY: return "Winsock library is not ready";
|
|
|
|
|
case WSANOTINITIALISED: return "Winsock library not initalised";
|
|
|
|
|
case WSAVERNOTSUPPORTED: return "Winsock version not supported";
|
|
|
|
|
|
|
|
|
|
case WSAHOST_NOT_FOUND: return "Host not found";
|
|
|
|
|
case WSATRY_AGAIN: return "Host not found, try again";
|
|
|
|
|
case WSANO_RECOVERY: return "Unrecoverable error in call to nameserver";
|
|
|
|
|
case WSANO_DATA: return "No data record of requested type";
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the error message corresponding to ERR. This is different
|
|
|
|
|
from Windows libc strerror() in that it handles Winsock errors
|
|
|
|
|
correctly. */
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
windows_strerror (int err)
|
|
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
if (err >= 0 && err < sys_nerr)
|
|
|
|
|
return strerror (err);
|
|
|
|
|
else if ((p = get_winsock_error (err)) != NULL)
|
|
|
|
|
return p;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
static char buf[32];
|
|
|
|
|
snprintf (buf, sizeof (buf), "Unknown error %d (%#x)", err, err);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-06-30 09:48:10 -04:00
|
|
|
|
|
|
|
|
|
#ifdef ENABLE_IPV6
|
2005-08-09 08:45:45 -04:00
|
|
|
|
/* An inet_ntop implementation that uses WSAAddressToString.
|
|
|
|
|
Prototype complies with POSIX 1003.1-2004. This is only used under
|
|
|
|
|
IPv6 because Wget prints IPv4 addresses using inet_ntoa. */
|
2005-06-30 09:48:10 -04:00
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
inet_ntop (int af, const void *src, char *dst, socklen_t cnt)
|
|
|
|
|
{
|
2005-08-09 08:45:45 -04:00
|
|
|
|
/* struct sockaddr can't accomodate struct sockaddr_in6. */
|
|
|
|
|
union {
|
|
|
|
|
struct sockaddr_in6 sin6;
|
|
|
|
|
struct sockaddr_in sin;
|
|
|
|
|
} sa;
|
2005-06-30 09:48:10 -04:00
|
|
|
|
DWORD dstlen = cnt;
|
2005-08-09 08:45:45 -04:00
|
|
|
|
size_t srcsize;
|
2005-06-30 09:48:10 -04:00
|
|
|
|
|
2005-08-09 08:45:45 -04:00
|
|
|
|
xzero (sa);
|
|
|
|
|
switch (af)
|
|
|
|
|
{
|
|
|
|
|
case AF_INET:
|
|
|
|
|
sa.sin.sin_family = AF_INET;
|
|
|
|
|
sa.sin.sin_addr = *(struct in_addr *) src;
|
|
|
|
|
srcsize = sizeof (sa.sin);
|
|
|
|
|
break;
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
sa.sin6.sin6_family = AF_INET6;
|
|
|
|
|
sa.sin6.sin6_addr = *(struct in6_addr *) src;
|
|
|
|
|
srcsize = sizeof (sa.sin6);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
abort ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (WSAAddressToString ((struct sockaddr *) &sa, srcsize, NULL, dst, &dstlen) != 0)
|
2005-06-30 09:48:10 -04:00
|
|
|
|
{
|
|
|
|
|
errno = WSAGetLastError();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return (const char *) dst;
|
|
|
|
|
}
|
|
|
|
|
#endif
|