file, but you are not obligated to do so. If you do not wish to do
so, delete this exception statement from your version. */
-/* #### Someone please document what these functions do! */
-
#include <config.h>
#include <stdio.h>
/* Defined in log.c. */
void log_request_redirect_output PARAMS ((const char *));
-static int windows_nt_p;
-
/* Windows version of xsleep in utils.c. */
void
#endif /* not HAVE_USLEEP */
}
+#if defined(__BORLANDC__) || (defined(_MSC_VER) && _MSC_VER < 1300)
+
+static inline int
+char_value (char c, int base)
+{
+ int value;
+ if (c < '0')
+ return -1;
+ if ('0' <= c && c <= '9')
+ value = c - '0';
+ else if ('a' <= c && c <= 'z')
+ value = c - 'a' + 10;
+ else if ('A' <= c && c <= 'Z')
+ value = c - 'A' + 10;
+ else
+ return -1;
+ if (value >= base)
+ return -1;
+ return value;
+}
+
+/* A fairly simple strtoll replacement for MS VC versions that don't
+ supply _strtoi64. */
+
+__int64
+str_to_int64 (const char *nptr, char **endptr, int base)
+{
+#define OVERFLOW 9223372036854775807I64
+#define UNDERFLOW (-OVERFLOW - 1)
+
+ __int64 result = 0;
+ int negative;
+
+ if (base != 0 && (base < 2 || base > 36))
+ {
+ errno = EINVAL;
+ return 0;
+ }
+
+ while (*nptr == ' ' || *nptr == '\t')
+ ++nptr;
+ if (*nptr == '-')
+ {
+ negative = 1;
+ ++nptr;
+ }
+ else if (*nptr == '+')
+ {
+ negative = 0;
+ ++nptr;
+ }
+ else
+ negative = 0;
+
+ /* If base is 0, determine the real base based on the beginning on
+ the number; octal numbers begin with "0", hexadecimal with "0x",
+ and the others are considered octal. */
+ if (*nptr == '0')
+ {
+ if ((base == 0 || base == 16)
+ &&
+ (*(nptr + 1) == 'x' || *(nptr + 1) == 'X'))
+ {
+ base = 16;
+ nptr += 2;
+ }
+ else if (base == 0)
+ base = 8;
+ }
+ else if (base == 0)
+ base = 10;
+
+ if (!negative)
+ {
+ /* Parse positive number, checking for overflow. */
+ int val;
+ for (; (val = char_value (*nptr, base)) != -1; ++nptr)
+ {
+ __int64 newresult = base * result + val;
+ if (newresult < result)
+ {
+ result = OVERFLOW;
+ errno = ERANGE;
+ break;
+ }
+ result = newresult;
+ }
+ }
+ else
+ {
+ /* Parse negative number, checking for underflow. */
+ int val;
+ for (; (val = char_value (*nptr, base)) != -1; ++nptr)
+ {
+ __int64 newresult = base * result - val;
+ if (newresult > result)
+ {
+ result = UNDERFLOW;
+ errno = ERANGE;
+ break;
+ }
+ result = newresult;
+ }
+ }
+ if (endptr)
+ *endptr = (char *) nptr;
+ return result;
+}
+
+#else /* !defined(__BORLANDC__) && (!defined(_MSC_VER) || _MSC_VER >= 1300) */
+
+__int64
+str_to_int64 (const char *nptr, char **endptr, int base)
+{
+#ifdef _MSC_VER
+ return _strtoi64 (nptr, endptr, base);
+#else
+ return strtoll (nptr, endptr, base);
+#endif
+}
+
+#endif /* !defined(__BORLANDC__) && (!defined(_MSC_VER) || _MSC_VER >= 1300) */
+
void
windows_main_junk (int *argc, char **argv, char **exec_name)
{
}
static void
-ws_hangup (void)
+ws_hangup (const char *reason)
{
- log_request_redirect_output ("CTRL+Break");
+ fprintf (stderr, _("Continuing in background.\n"));
+ 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 ();
}
-void
-fork_to_background (void)
+/* 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;
+ int logfile_changed;
+ 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)
{
- /* Whether we arrange our own version of opt.lfilename here. */
- int changedp = 0;
+ HANDLE section, event;
+ struct fake_fork_info *info;
+ char *name;
+
+ name = make_section_name (GetCurrentProcessId ());
+ section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
+ 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)
+ return 0; /* We are the parent. */
+
+ info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
+ if (!info)
+ {
+ CloseHandle (section);
+ return -1;
+ }
+
+ event = info->event;
+ info->logfile_changed = 0;
if (!opt.lfilename)
{
- opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
- changedp = 1;
+ /* See utils:fork_to_background for explanation. */
+ FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, 0, &opt.lfilename);
+ if (new_log_fp)
+ {
+ info->logfile_changed = 1;
+ strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
+ info->lfilename[sizeof (info->lfilename) - 1] = '\0';
+ fclose (new_log_fp);
+ }
}
- printf (_("Continuing in background.\n"));
- if (changedp)
- printf (_("Output will be written to `%s'.\n"), opt.lfilename);
- ws_hangup ();
- if (!windows_nt_p)
- FreeConsole ();
+ 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. */
+}
+
+/* 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. */
+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;
+
+ event = section = pi.hProcess = pi.hThread = NULL;
+
+ /* 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;
+
+ /* Create the child process detached form the current console and in a
+ suspended state. */
+ memset (&si, 0, sizeof (si));
+ si.cb = sizeof (si);
+ rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
+ CREATE_SUSPENDED | DETACHED_PROCESS,
+ NULL, NULL, &si, &pi);
+ 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. */
+ if (info->logfile_changed &&
+ !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
+ {
+ rv = FALSE;
+ goto cleanup;
+ }
+
+ printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
+ if (info->logfile_changed)
+ 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. */
+}
+
+/* This is the corresponding Windows implementation of the
+ fork_to_background() function in utils.c. */
+void
+fork_to_background (void)
+{
+ 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. */
}
static BOOL WINAPI
{
#ifdef CTRLC_BACKGND
case CTRL_C_EVENT:
+ ws_hangup ("CTRL+C");
+ return TRUE;
#endif
#ifdef CTRLBREAK_BACKGND
case CTRL_BREAK_EVENT:
+ ws_hangup ("CTRL+Break");
+ return TRUE;
#endif
- fork_to_background ();
- break;
- case CTRL_SHUTDOWN_EVENT:
- case CTRL_CLOSE_EVENT:
- case CTRL_LOGOFF_EVENT:
default:
- ws_cleanup ();
return FALSE;
}
- return TRUE;
}
static char *title_buf = NULL;
WORD requested;
WSADATA data;
int err;
- OSVERSIONINFO os;
-
- if (GetVersionEx (&os) == TRUE
- && os.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
- windows_nt_p = 1;
requested = MAKEWORD (1, 1);
err = WSAStartup (requested, &data);