]> sjero.net Git - wget/blob - src/mswindows.c
[svn] Large file support added. Published in <87psyr6jn7.fsf@xemacs.org>.
[wget] / src / mswindows.c
1 /* mswindows.c -- Windows-specific support
2    Copyright (C) 1995, 1996, 1997, 1998, 2004
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Wget.
6
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.
11
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.
16
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.
20
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.  */
30
31 #include <config.h>
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <assert.h>
37 #include <errno.h>
38 #include <math.h>
39
40 #ifdef HACK_BCC_UTIME_BUG
41 # include <io.h>
42 # include <fcntl.h>
43 # ifdef HAVE_UTIME_H
44 #  include <utime.h>
45 # endif
46 # ifdef HAVE_SYS_UTIME_H
47 #  include <sys/utime.h>
48 # endif
49 #endif
50
51 #include "wget.h"
52 #include "utils.h"
53 #include "url.h"
54
55 #ifndef errno
56 extern int errno;
57 #endif
58
59 #ifndef ES_SYSTEM_REQUIRED
60 #define ES_SYSTEM_REQUIRED  0x00000001
61 #endif
62
63 #ifndef ES_CONTINUOUS
64 #define ES_CONTINUOUS       0x80000000
65 #endif
66
67
68 /* Defined in log.c.  */
69 void log_request_redirect_output PARAMS ((const char *));
70
71 /* Windows version of xsleep in utils.c.  */
72
73 void
74 xsleep (double seconds)
75 {
76 #ifdef HAVE_USLEEP
77   if (seconds > 1000)
78     {
79       /* Explained in utils.c. */
80       sleep (seconds);
81       seconds -= (long) seconds;
82     }
83   usleep (seconds * 1000000L);
84 #else  /* not HAVE_USLEEP */
85   SleepEx (seconds * 1000, FALSE);
86 #endif /* not HAVE_USLEEP */
87 }
88
89 #if defined(_MSC_VER) && _MSC_VER < 1300
90
91 static inline int
92 char_value (char c, int base)
93 {
94   int value;
95   if (c < '0')
96     return -1;
97   if ('0' <= c && c <= '9')
98     value = c - '0';
99   else if ('a' <= c && c <= 'z')
100     value = c - 'a' + 10;
101   else if ('A' <= c && c <= 'Z')
102     value = c - 'A' + 10;
103   else
104     return -1;
105   if (value >= base)
106     return -1;
107   return value;
108 }
109
110 /* A fairly simple strtoll replacement for MS VC versions that don't
111    supply _strtoi64.  */
112
113 __int64
114 str_to_int64 (const char *nptr, char **endptr, int base)
115 {
116 #define OVERFLOW 9223372036854775807I64
117 #define UNDERFLOW (-OVERFLOW - 1)
118
119   __int64 result = 0;
120   int negative;
121
122   if (base != 0 && (base < 2 || base > 36))
123     {
124       errno = EINVAL;
125       return 0;
126     }
127
128   while (*nptr == ' ' || *nptr == '\t')
129     ++nptr;
130   if (*nptr == '-')
131     {
132       negative = 1;
133       ++nptr;
134     }
135   else if (*nptr == '+')
136     {
137       negative = 0;
138       ++nptr;
139     }
140   else
141     negative = 0;
142
143   /* If base is 0, determine the real base based on the beginning on
144      the number; octal numbers begin with "0", hexadecimal with "0x",
145      and the others are considered octal.  */
146   if (*nptr == '0')
147     {
148       if ((base == 0 || base == 16)
149           &&
150           (*(nptr + 1) == 'x' || *(nptr + 1) == 'X'))
151         {
152           base = 16;
153           nptr += 2;
154         }
155       else if (base == 0)
156         base = 8;
157     }
158   else if (base == 0)
159     base = 10;
160
161   if (!negative)
162     {
163       /* Parse positive number, checking for overflow. */
164       int val;
165       for (; (val = char_value (*nptr, base)) != -1; ++nptr)
166         {
167           __int64 newresult = base * result + val;
168           if (newresult < result)
169             {
170               result = OVERFLOW;
171               errno = ERANGE;
172               break;
173             }
174           result = newresult;
175         }
176     }
177   else
178     {
179       /* Parse negative number, checking for underflow. */
180       int val;
181       for (; (val = char_value (*nptr, base)) != -1; ++nptr)
182         {
183           __int64 newresult = base * result - val;
184           if (newresult > result)
185             {
186               result = UNDERFLOW;
187               errno = ERANGE;
188               break;
189             }
190           result = newresult;
191         }
192     }
193   if (endptr)
194     *endptr = (char *) nptr;
195   return result;
196 }
197
198 #else  /* !defined(_MSC_VER) || _MSC_VER >= 1300 */
199
200 __int64
201 str_to_int64 (const char *nptr, char **endptr, int base)
202 {
203 #ifdef _MSC_VER
204   return _strtoi64 (nptr, endptr, base);
205 #else
206   return strtoll (nptr, endptr, base);
207 #endif
208 }
209
210 #endif /* !defined(_MSC_VER) || _MSC_VER >= 1300 */
211
212 /* A simple clone of ftello.  The normal ftell doesn't work for large
213    files, so this is needed, and used by file_size(), which is itself
214    used for the --post-file option.
215
216    This function uses fgetpos incorrectly and should be considered a
217    hack until a better way to tell the stream position is found.  */
218
219 __int64
220 wget_ftello (FILE *fp)
221 {
222   fpos_t pos;
223   if (fgetpos (fp, &pos) != 0)
224     return -1;
225   else
226     return pos;
227 }
228
229 void
230 windows_main_junk (int *argc, char **argv, char **exec_name)
231 {
232   char *p;
233
234   /* Remove .EXE from filename if it has one.  */
235   *exec_name = xstrdup (*exec_name);
236   p = strrchr (*exec_name, '.');
237   if (p)
238     *p = '\0';
239 }
240 \f
241 static void
242 ws_cleanup (void)
243 {
244   WSACleanup ();
245 }
246
247 static void
248 ws_hangup (const char *reason)
249 {
250   /* Whether we arrange our own version of opt.lfilename here.  */
251   int changedp = 0;
252
253   if (!opt.lfilename)
254     {
255       opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
256       changedp = 1;
257     }
258   printf (_("Continuing in background.\n"));
259   if (changedp)
260     printf (_("Output will be written to `%s'.\n"), opt.lfilename);
261
262   log_request_redirect_output (reason);
263
264   /* Detach process from the current console.  Under Windows 9x, if we
265      were launched from a 16-bit process (which is usually the case;
266      command.com is 16-bit) the parent process should resume right away.
267      Under NT or if launched from a 32-process under 9x, this is a futile
268      gesture as the parent will wait for us to terminate before resuming.  */
269   FreeConsole ();
270 }
271
272 /* Construct the name for a named section (a.k.a. `file mapping') object.
273    The returned string is dynamically allocated and needs to be xfree()'d.  */
274 static char *
275 make_section_name (DWORD pid)
276 {
277   return aprintf ("gnu_wget_fake_fork_%lu", pid);
278 }
279
280 /* This structure is used to hold all the data that is exchanged between
281    parent and child.  */
282 struct fake_fork_info
283 {
284   HANDLE event;
285   int changedp;
286   char lfilename[MAX_PATH + 1];
287 };
288
289 /* Determines if we are the child and if so performs the child logic.
290    Return values:
291      < 0  error
292        0  parent
293      > 0  child
294 */
295 static int
296 fake_fork_child (void)
297 {
298   HANDLE section, event;
299   struct fake_fork_info *info;
300   char *name;
301
302   name = make_section_name (GetCurrentProcessId ());
303   section = OpenFileMapping (FILE_MAP_WRITE, FALSE, name);
304   xfree (name);
305   /* It seems that Windows 9x and NT set last-error inconsistently when
306      OpenFileMapping() fails; so we assume it failed because the section
307      object does not exist.  */
308   if (!section)
309     return 0;                   /* We are the parent.  */
310
311   info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
312   if (!info)
313     {
314       CloseHandle (section);
315       return -1;
316     }
317
318   event = info->event;
319
320   if (!opt.lfilename)
321     {
322       opt.lfilename = unique_name (DEFAULT_LOGFILE, 0);
323       info->changedp = 1;
324       strncpy (info->lfilename, opt.lfilename, sizeof (info->lfilename));
325       info->lfilename[sizeof (info->lfilename) - 1] = '\0';
326     }
327   else
328     info->changedp = 0;
329
330   UnmapViewOfFile (info);
331   CloseHandle (section);
332
333   /* Inform the parent that we've done our part.  */
334   if (!SetEvent (event))
335     return -1;
336
337   CloseHandle (event);
338   return 1;                     /* We are the child.  */
339 }
340
341 /* Windows doesn't support the fork() call; so we fake it by invoking
342    another copy of Wget with the same arguments with which we were
343    invoked.  The child copy of Wget should perform the same initialization
344    sequence as the parent; so we should have two processes that are
345    essentially identical.  We create a specially named section object that
346    allows the child to distinguish itself from the parent and is used to
347    exchange information between the two processes.  We use an event object
348    for synchronization.  */
349 static void
350 fake_fork (void)
351 {
352   char exe[MAX_PATH + 1];
353   DWORD exe_len, le;
354   SECURITY_ATTRIBUTES sa;
355   HANDLE section, event, h[2];
356   STARTUPINFO si;
357   PROCESS_INFORMATION pi;
358   struct fake_fork_info *info;
359   char *name;
360   BOOL rv;
361
362   event = section = pi.hProcess = pi.hThread = NULL;
363
364   /* Get the fully qualified name of our executable.  This is more reliable
365      than using argv[0].  */
366   exe_len = GetModuleFileName (GetModuleHandle (NULL), exe, sizeof (exe));
367   if (!exe_len || (exe_len >= sizeof (exe)))
368     return;
369
370   sa.nLength = sizeof (sa);
371   sa.lpSecurityDescriptor = NULL;
372   sa.bInheritHandle = TRUE;
373
374   /* Create an anonymous inheritable event object that starts out
375      non-signaled.  */
376   event = CreateEvent (&sa, FALSE, FALSE, NULL);
377   if (!event)
378     return;
379
380   /* Create the child process detached form the current console and in a
381      suspended state.  */
382   memset (&si, 0, sizeof (si));
383   si.cb = sizeof (si);
384   rv = CreateProcess (exe, GetCommandLine (), NULL, NULL, TRUE,
385                       CREATE_SUSPENDED | DETACHED_PROCESS,
386                       NULL, NULL, &si, &pi);
387   if (!rv)
388     goto cleanup;
389
390   /* Create a named section object with a name based on the process id of
391      the child.  */
392   name = make_section_name (pi.dwProcessId);
393   section =
394       CreateFileMapping (INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
395                          sizeof (struct fake_fork_info), name);
396   le = GetLastError();
397   xfree (name);
398   /* Fail if the section object already exists (should not happen).  */
399   if (!section || (le == ERROR_ALREADY_EXISTS))
400     {
401       rv = FALSE;
402       goto cleanup;
403     }
404
405   /* Copy the event handle into the section object.  */
406   info = MapViewOfFile (section, FILE_MAP_WRITE, 0, 0, 0);
407   if (!info)
408     {
409       rv = FALSE;
410       goto cleanup;
411     }
412
413   info->event = event;
414
415   UnmapViewOfFile (info);
416
417   /* Start the child process.  */
418   rv = ResumeThread (pi.hThread);
419   if (!rv)
420     {
421       TerminateProcess (pi.hProcess, (DWORD) -1);
422       goto cleanup;
423     }
424
425   /* Wait for the child to signal to us that it has done its part.  If it
426      terminates before signaling us it's an error.  */
427
428   h[0] = event;
429   h[1] = pi.hProcess;
430   rv = WAIT_OBJECT_0 == WaitForMultipleObjects (2, h, FALSE, 5 * 60 * 1000);
431   if (!rv)
432     goto cleanup;
433
434   info = MapViewOfFile (section, FILE_MAP_READ, 0, 0, 0);
435   if (!info)
436     {
437       rv = FALSE;
438       goto cleanup;
439     }
440
441   /* Ensure string is properly terminated.  */
442   if (info->changedp &&
443       !memchr (info->lfilename, '\0', sizeof (info->lfilename)))
444     {
445       rv = FALSE;
446       goto cleanup;
447     }
448
449   printf (_("Continuing in background, pid %lu.\n"), pi.dwProcessId);
450   if (info->changedp)
451     printf (_("Output will be written to `%s'.\n"), info->lfilename);
452
453   UnmapViewOfFile (info);
454
455 cleanup:
456
457   if (event)
458     CloseHandle (event);
459   if (section)
460     CloseHandle (section);
461   if (pi.hThread)
462     CloseHandle (pi.hThread);
463   if (pi.hProcess)
464     CloseHandle (pi.hProcess);
465
466   /* We're the parent.  If all is well, terminate.  */
467   if (rv)
468     exit (0);
469
470   /* We failed, return.  */
471 }
472
473 /* This is the corresponding Windows implementation of the
474    fork_to_background() function in utils.c.  */
475 void
476 fork_to_background (void)
477 {
478   int rv;
479
480   rv = fake_fork_child ();
481   if (rv < 0)
482     {
483       fprintf (stderr, "fake_fork_child() failed\n");
484       abort ();
485     }
486   else if (rv == 0)
487     {
488       /* We're the parent.  */
489       fake_fork ();
490       /* If fake_fork() returns, it failed.  */
491       fprintf (stderr, "fake_fork() failed\n");
492       abort ();
493     }
494   /* If we get here, we're the child.  */
495 }
496
497 static BOOL WINAPI
498 ws_handler (DWORD dwEvent)
499 {
500   switch (dwEvent)
501     {
502 #ifdef CTRLC_BACKGND
503     case CTRL_C_EVENT:
504       ws_hangup ("CTRL+C");
505       return TRUE;
506 #endif
507 #ifdef CTRLBREAK_BACKGND
508     case CTRL_BREAK_EVENT:
509       ws_hangup ("CTRL+Break");
510       return TRUE;
511 #endif
512     default:
513       return FALSE;
514     }
515 }
516
517 static char *title_buf = NULL;
518 static char *curr_url  = NULL;
519 static int old_percentage = -1;
520
521 /* Updates the console title with the URL of the current file being
522    transferred.  */
523 void
524 ws_changetitle (const char *url)
525 {
526   xfree_null (title_buf);
527   xfree_null (curr_url);
528   title_buf = (char *)xmalloc (strlen (url) + 20);
529   curr_url = xstrdup (url);
530   old_percentage = -1;
531   sprintf (title_buf, "Wget %s", curr_url);
532   SetConsoleTitle (title_buf);
533 }
534
535 /* Updates the console title with the percentage of the current file
536    transferred.  */
537 void
538 ws_percenttitle (double percentage_float)
539 {
540   int percentage;
541
542   if (!title_buf || !curr_url)
543     return;
544
545   percentage = (int) percentage_float;
546
547   /* Clamp percentage value.  */
548   if (percentage < 0)
549     percentage = 0;
550   if (percentage > 100)
551     percentage = 100;
552
553   /* Only update the title when the percentage has changed.  */
554   if (percentage == old_percentage)
555     return;
556
557   old_percentage = percentage;
558
559   sprintf (title_buf, "Wget [%d%%] %s", percentage, curr_url);
560   SetConsoleTitle (title_buf);
561 }
562
563 /* Returns a pointer to the fully qualified name of the directory that
564    contains the Wget binary (wget.exe).  The returned path does not have a
565    trailing path separator.  Returns NULL on failure.  */
566 char *
567 ws_mypath (void)
568 {
569   static char *wspathsave = NULL;
570
571   if (!wspathsave)
572     {
573       char buf[MAX_PATH + 1];
574       char *p;
575       DWORD len;
576
577       len = GetModuleFileName (GetModuleHandle (NULL), buf, sizeof (buf));
578       if (!len || (len >= sizeof (buf)))
579         return NULL;
580
581       p = strrchr (buf, PATH_SEPARATOR);
582       if (!p)
583         return NULL;
584
585       *p = '\0';
586       wspathsave = xstrdup (buf);
587     }
588
589   return wspathsave;
590 }
591
592 /* Prevent Windows entering sleep/hibernation-mode while Wget is doing
593    a lengthy transfer.  Windows does not, by default, consider network
594    activity in console-programs as activity!  Works on Win-98/ME/2K
595    and up.  */
596 static void
597 set_sleep_mode (void)
598 {
599   typedef DWORD (WINAPI *func_t) (DWORD);
600   func_t set_exec_state;
601
602   set_exec_state =
603       (func_t) GetProcAddress (GetModuleHandle ("KERNEL32.DLL"),
604                                "SetThreadExecutionState");
605
606   if (set_exec_state)
607     set_exec_state (ES_SYSTEM_REQUIRED | ES_CONTINUOUS);
608 }
609
610 /* Perform Windows specific initialization.  */
611 void
612 ws_startup (void)
613 {
614   WORD requested;
615   WSADATA data;
616   int err;
617
618   requested = MAKEWORD (1, 1);
619   err = WSAStartup (requested, &data);
620   if (err != 0)
621     {
622       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
623                exec_name);
624       exit (1);
625     }
626
627   if (data.wVersion < requested)
628     {
629       fprintf (stderr, _("%s: Couldn't find usable socket driver.\n"),
630                exec_name);
631       WSACleanup ();
632       exit (1);
633     }
634
635   atexit (ws_cleanup);
636   set_sleep_mode ();
637   SetConsoleCtrlHandler (ws_handler, TRUE);
638 }
639
640 /* Replacement utime function for buggy Borland C++Builder 5.5 compiler.
641    (The Borland utime function only works on Windows NT.)  */
642
643 #ifdef HACK_BCC_UTIME_BUG
644 int
645 borland_utime (const char *path, const struct utimbuf *times)
646 {
647   int fd;
648   int res;
649   struct ftime ft;
650   struct tm *ptr_tm;
651
652   if ((fd = open (path, O_RDWR)) < 0)
653     return -1;
654
655   ptr_tm = localtime (&times->modtime);
656   ft.ft_tsec = ptr_tm->tm_sec >> 1;
657   ft.ft_min = ptr_tm->tm_min;
658   ft.ft_hour = ptr_tm->tm_hour;
659   ft.ft_day = ptr_tm->tm_mday;
660   ft.ft_month = ptr_tm->tm_mon + 1;
661   ft.ft_year = ptr_tm->tm_year - 80;
662   res = setftime (fd, &ft);
663   close (fd);
664   return res;
665 }
666 #endif
667 \f
668 /* run_with_timeout Windows implementation.  */
669
670 /* Stack size 0 uses default thread stack-size (reserve+commit).
671    Determined by what's in the PE header.  */
672 #define THREAD_STACK_SIZE  0
673
674 struct thread_data
675 {
676   void (*fun) (void *);
677   void *arg;
678   DWORD ws_error;
679 };
680
681 /* The callback that runs FUN(ARG) in a separate thread.  This
682    function exists for two reasons: a) to not require FUN to be
683    declared WINAPI/__stdcall[1], and b) to retrieve Winsock errors,
684    which are per-thread.  The latter is useful when FUN calls Winsock
685    functions, which is how run_with_timeout is used in Wget.
686
687    [1] MSVC can use __fastcall globally (cl /Gr) and on Watcom this is
688    the default (wcc386 -3r).  */
689
690 static DWORD WINAPI
691 thread_helper (void *arg)
692 {
693   struct thread_data *td = (struct thread_data *) arg;
694
695   /* Initialize Winsock error to what it was in the parent.  That way
696      the subsequent call to WSAGetLastError will return the same value
697      if td->fun doesn't change Winsock error state.  */
698   WSASetLastError (td->ws_error);
699
700   td->fun (td->arg);
701
702   /* Return Winsock error to the caller, in case FUN ran Winsock
703      code.  */
704   td->ws_error = WSAGetLastError ();
705   return 0;
706 }
707
708 /* Call FUN(ARG), but don't allow it to run for more than TIMEOUT
709    seconds.  Returns non-zero if the function was interrupted with a
710    timeout, zero otherwise.
711
712    This works by running FUN in a separate thread and terminating the
713    thread if it doesn't finish in the specified time.  */
714
715 int
716 run_with_timeout (double seconds, void (*fun) (void *), void *arg)
717 {
718   static HANDLE thread_hnd = NULL;
719   struct thread_data thread_arg;
720   DWORD thread_id;
721   int rc = 0;
722
723   DEBUGP (("seconds %.2f, ", seconds));
724
725   if (seconds == 0)
726     {
727     blocking_fallback:
728       fun (arg);
729       return 0;
730     }
731
732   /* Should never happen, but test for recursivety anyway.  */
733   assert (thread_hnd == NULL);
734
735   thread_arg.fun = fun;
736   thread_arg.arg = arg;
737   thread_arg.ws_error = WSAGetLastError ();
738   thread_hnd = CreateThread (NULL, THREAD_STACK_SIZE, thread_helper,
739                              &thread_arg, 0, &thread_id);
740   if (!thread_hnd)
741     {
742       DEBUGP (("CreateThread() failed; %s\n", strerror (GetLastError ())));
743       goto blocking_fallback;
744     }
745
746   if (WaitForSingleObject (thread_hnd, (DWORD)(1000 * seconds))
747       == WAIT_OBJECT_0)
748     {
749       /* Propagate error state (which is per-thread) to this thread,
750          so the caller can inspect it.  */
751       WSASetLastError (thread_arg.ws_error);
752       DEBUGP (("Winsock error: %d\n", WSAGetLastError ()));
753       rc = 0;
754     }
755   else
756     {
757       TerminateThread (thread_hnd, 1);
758       rc = 1;
759     }
760
761   CloseHandle (thread_hnd);     /* Clear-up after TerminateThread().  */
762   thread_hnd = NULL;
763   return rc;
764 }