/* mswindows.c -- Windows-specific support
- Copyright (C) 1995, 1996, 1997, 1998, 2004
- Free Software Foundation, Inc.
+ Copyright (C) 1996-2006 Free Software Foundation, Inc.
This file is part of GNU Wget.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+along with Wget; if not, write to the Free Software Foundation, Inc.,
+51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
In addition, as a special exception, the Free Software Foundation
gives permission to link the code of its release of Wget with the
#include <errno.h>
#include <math.h>
-#ifdef HACK_BCC_UTIME_BUG
-# include <io.h>
-# include <fcntl.h>
-# ifdef HAVE_UTIME_H
-# include <utime.h>
-# endif
-# ifdef HAVE_SYS_UTIME_H
-# include <sys/utime.h>
-# endif
-#endif
+#define INHIBIT_WRAP /* avoid wrapping of socket, bind, ... */
#include "wget.h"
#include "utils.h"
#include "url.h"
-#ifndef errno
-extern int errno;
-#endif
-
#ifndef ES_SYSTEM_REQUIRED
#define ES_SYSTEM_REQUIRED 0x00000001
#endif
/* Defined in log.c. */
-void log_request_redirect_output PARAMS ((const char *));
+void log_request_redirect_output (const char *);
/* Windows version of xsleep in utils.c. */
sleep (seconds);
seconds -= (long) seconds;
}
- usleep (seconds * 1000000L);
+ usleep (seconds * 1000000);
#else /* not HAVE_USLEEP */
- SleepEx (seconds * 1000, FALSE);
+ SleepEx ((DWORD) (seconds * 1000 + .5), FALSE);
#endif /* not HAVE_USLEEP */
}
void
-windows_main_junk (int *argc, char **argv, char **exec_name)
+windows_main (int *argc, char **argv, char **exec_name)
{
char *p;
WSACleanup ();
}
+#if defined(CTRLBREAK_BACKGND) || defined(CTRLC_BACKGND)
static void
ws_hangup (const char *reason)
{
- /* Whether we arrange our own version of opt.lfilename here. */
- int changedp = 0;
-
- if (!opt.lfilename)
- {
- opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
- changedp = 1;
- }
- printf (_("Continuing in background.\n"));
- if (changedp)
- printf (_("Output will be written to `%s'.\n"), opt.lfilename);
-
+ fprintf (stderr, _("Continuing in background.\n"));
log_request_redirect_output (reason);
/* Detach process from the current console. Under Windows 9x, if we
gesture as the parent will wait for us to terminate before resuming. */
FreeConsole ();
}
+#endif
/* 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. */
struct fake_fork_info
{
HANDLE event;
- int changedp;
+ bool logfile_changed;
char lfilename[MAX_PATH + 1];
};
HANDLE section, event;
struct fake_fork_info *info;
char *name;
- DWORD le;
name = make_section_name (GetCurrentProcessId ());
section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
- le = GetLastError ();
xfree (name);
+ /* 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. */
if (!section)
- {
- if (le == ERROR_FILE_NOT_FOUND)
- return 0; /* Section object does not exist; we are the parent. */
- else
- return -1;
- }
+ return 0; /* We are the parent. */
info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
if (!info)
event = info->event;
+ info->logfile_changed = false;
if (!opt.lfilename)
{
- opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
- info->changedp = 1;
- strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
- info->lfilename[sizeof (info->lfilename) - 1] = '\0';
+ /* See utils:fork_to_background for explanation. */
+ FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, false, &opt.lfilename);
+ if (new_log_fp)
+ {
+ info->logfile_changed = true;
+ strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
+ info->lfilename[sizeof (info->lfilename) - 1] = '\0';
+ fclose (new_log_fp);
+ }
}
- else
- info->changedp = 0;
UnmapViewOfFile (info);
CloseHandle (section);
static void
fake_fork (void)
{
- char *cmdline, *args;
char exe[MAX_PATH + 1];
DWORD exe_len, le;
SECURITY_ATTRIBUTES sa;
char *name;
BOOL rv;
- event = section = pi.hProcess = pi.hThread = NULL;
-
- /* Get command line arguments to pass to the child process.
- We need to skip the name of the command (what amounts to argv[0]). */
- cmdline = GetCommandLine ();
- if (*cmdline == '"')
- {
- args = strchr (cmdline + 1, '"');
- if (args)
- ++args;
- }
- else
- args = strchr (cmdline, ' ');
-
- /* It's ok if args is NULL, that would mean there were no arguments
- after the command name. As it is now though, we would never get here
- if that were true. */
+ section = pi.hProcess = pi.hThread = NULL;
/* Get the fully qualified name of our executable. This is more reliable
than using argv[0]. */
if (!event)
return;
- /* Creat the child process detached form the current console and in a
+ /* Create the child process detached form the current console and in a
suspended state. */
- memset (&si, 0, sizeof (si));
+ xzero (si);
si.cb = sizeof (si);
- rv = CreateProcess (exe, args, NULL, NULL, TRUE, CREATE_SUSPENDED |
- DETACHED_PROCESS, NULL, NULL, &si, &pi);
+ rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
+ CREATE_SUSPENDED | DETACHED_PROCESS,
+ NULL, NULL, &si, &pi);
if (!rv)
goto cleanup;
}
/* Ensure string is properly terminated. */
- if (info->changedp &&
+ if (info->logfile_changed &&
!memchr (info->lfilename, '\0', sizeof (info->lfilename)))
{
rv = FALSE;
}
printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
- if (info->changedp)
+ if (info->logfile_changed)
printf (_("Output will be written to `%s'.\n"), info->lfilename);
UnmapViewOfFile (info);
{
xfree_null (title_buf);
xfree_null (curr_url);
- title_buf = (char *)xmalloc (strlen (url) + 20);
+ title_buf = xmalloc (strlen (url) + 20);
curr_url = xstrdup (url);
old_percentage = -1;
sprintf (title_buf, "Wget %s", curr_url);
void
ws_startup (void)
{
- WORD requested;
WSADATA data;
- int err;
-
- requested = MAKEWORD (1, 1);
- err = WSAStartup (requested, &data);
+ WORD requested = MAKEWORD (1, 1);
+ int err = WSAStartup (requested, &data);
if (err != 0)
{
fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
set_sleep_mode ();
SetConsoleCtrlHandler (ws_handler, TRUE);
}
-
-/* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
- (The Borland utime function only works on Windows NT.) */
-
-#ifdef HACK_BCC_UTIME_BUG
-int
-borland_utime (const char *path, const struct utimbuf *times)
-{
- int fd;
- int res;
- struct ftime ft;
- struct tm *ptr_tm;
-
- if ((fd = open (path, O_RDWR)) < 0)
- return -1;
-
- ptr_tm = localtime (×->modtime);
- ft.ft_tsec = ptr_tm->tm_sec >> 1;
- ft.ft_min = ptr_tm->tm_min;
- ft.ft_hour = ptr_tm->tm_hour;
- ft.ft_day = ptr_tm->tm_mday;
- ft.ft_month = ptr_tm->tm_mon + 1;
- ft.ft_year = ptr_tm->tm_year - 80;
- res = setftime (fd, &ft);
- close (fd);
- return res;
-}
-#endif
\f
/* run_with_timeout Windows implementation. */
}
/* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
- seconds. Returns non-zero if the function was interrupted with a
- timeout, zero otherwise.
+ seconds. Returns true if the function was interrupted with a
+ timeout, false otherwise.
This works by running FUN in a separate thread and terminating the
thread if it doesn't finish in the specified time. */
-int
+bool
run_with_timeout (double seconds, void (*fun) (void *), void *arg)
{
- static HANDLE thread_hnd = NULL;
+ HANDLE thread_hnd;
struct thread_data thread_arg;
DWORD thread_id;
- int rc = 0;
+ bool rc;
DEBUGP (("seconds %.2f, ", seconds));
{
blocking_fallback:
fun (arg);
- return 0;
+ return false;
}
- /* Should never happen, but test for recursivety anyway. */
- assert (thread_hnd == NULL);
-
thread_arg.fun = fun;
thread_arg.arg = arg;
thread_arg.ws_error = WSAGetLastError ();
&thread_arg, 0, &thread_id);
if (!thread_hnd)
{
- DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
+ DEBUGP (("CreateThread() failed; [0x%x]\n", GetLastError ()));
goto blocking_fallback;
}
so the caller can inspect it. */
WSASetLastError (thread_arg.ws_error);
DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
- rc = 0;
+ rc = false;
}
else
{
TerminateThread (thread_hnd, 1);
- rc = 1;
+ rc = true;
}
CloseHandle (thread_hnd); /* Clear-up after TerminateThread(). */
thread_hnd = NULL;
return rc;
}
+\f
+/* 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. */
+
+/* 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. */
+
+#define WRAP(fun, decl, call) int wrapped_##fun decl { \
+ int retval = fun call; \
+ if (retval < 0) \
+ errno = WSAGetLastError (); \
+ return retval; \
+}
+
+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))
+WRAP (listen, (int s, int backlog), (s, backlog))
+WRAP (accept, (int s, struct sockaddr *a, int *alen), (s, a, alen))
+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))
+WRAP (closesocket, (int s), (s))
+
+/* 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;
+ }
+}
+\f
+#ifdef ENABLE_IPV6
+/* 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. */
+
+const char *
+inet_ntop (int af, const void *src, char *dst, socklen_t cnt)
+{
+ /* struct sockaddr can't accomodate struct sockaddr_in6. */
+ union {
+ struct sockaddr_in6 sin6;
+ struct sockaddr_in sin;
+ } sa;
+ DWORD dstlen = cnt;
+ size_t srcsize;
+
+ 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)
+ {
+ errno = WSAGetLastError();
+ return NULL;
+ }
+ return (const char *) dst;
+}
+#endif