]> sjero.net Git - wget/blob - src/main.c
[svn] Merging fix #20499: MAX_REDIRECTIONS should be configurable.
[wget] / src / main.c
1 /* Command line parsing.
2    Copyright (C) 1996-2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
18
19 In addition, as a special exception, the Free Software Foundation
20 gives permission to link the code of its release of Wget with the
21 OpenSSL project's "OpenSSL" library (or with modified versions of it
22 that use the same license as the "OpenSSL" library), and distribute
23 the linked executables.  You must obey the GNU General Public License
24 in all respects for all of the code used other than "OpenSSL".  If you
25 modify this file, you may extend this exception to your version of the
26 file, but you are not obligated to do so.  If you do not wish to do
27 so, delete this exception statement from your version.  */
28
29 #include <config.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #ifdef HAVE_UNISTD_H
34 # include <unistd.h>
35 #endif /* HAVE_UNISTD_H */
36 #include <string.h>
37 #include <signal.h>
38 #ifdef HAVE_NLS
39 # include <locale.h>
40 #endif
41 #include <assert.h>
42 #include <errno.h>
43 #include <time.h>
44
45 #include "wget.h"
46 #include "utils.h"
47 #include "init.h"
48 #include "retr.h"
49 #include "recur.h"
50 #include "host.h"
51 #include "url.h"
52 #include "progress.h"           /* for progress_handle_sigwinch */
53 #include "convert.h"
54 #include "spider.h"
55 #include "http.h"               /* for save_cookies */
56
57 /* On GNU system this will include system-wide getopt.h. */
58 #include "getopt.h"
59
60 #ifndef PATH_SEPARATOR
61 # define PATH_SEPARATOR '/'
62 #endif
63
64 struct options opt;
65
66 extern char *version_string;
67
68 #if defined(SIGHUP) || defined(SIGUSR1)
69 static void redirect_output_signal (int);
70 #endif
71
72 const char *exec_name;
73 \f
74 /* Initialize I18N/L10N.  That amounts to invoking setlocale, and
75    setting up gettext's message catalog using bindtextdomain and
76    textdomain.  Does nothing if NLS is disabled or missing.  */
77
78 static void
79 i18n_initialize (void)
80 {
81   /* HAVE_NLS implies existence of functions invoked here.  */
82 #ifdef HAVE_NLS
83   /* Set the current locale.  */
84   setlocale (LC_ALL, "");
85   /* Set the text message domain.  */
86   bindtextdomain ("wget", LOCALEDIR);
87   textdomain ("wget");
88 #endif /* HAVE_NLS */
89 }
90 \f
91 /* Definition of command-line options. */
92
93 static void print_help (void);
94 static void print_version (void);
95
96 #ifdef HAVE_SSL
97 # define IF_SSL(x) x
98 #else
99 # define IF_SSL(x) NULL
100 #endif
101
102 #ifdef ENABLE_DEBUG
103 # define WHEN_DEBUG(x) x
104 #else
105 # define WHEN_DEBUG(x) NULL
106 #endif
107
108 struct cmdline_option {
109   const char *long_name;
110   char short_name;
111   enum {
112     OPT_VALUE,
113     OPT_BOOLEAN,
114     OPT_FUNCALL,
115     /* Non-standard options that have to be handled specially in
116        main().  */
117     OPT__APPEND_OUTPUT,
118     OPT__CLOBBER,
119     OPT__DONT_REMOVE_LISTING,
120     OPT__EXECUTE,
121     OPT__NO,
122     OPT__PARENT
123   } type;
124   const void *data;             /* for standard options */
125   int argtype;                  /* for non-standard options */
126 };
127
128 static struct cmdline_option option_data[] =
129   {
130     { "accept", 'A', OPT_VALUE, "accept", -1 },
131     { "append-output", 'a', OPT__APPEND_OUTPUT, NULL, required_argument },
132     { "background", 'b', OPT_BOOLEAN, "background", -1 },
133     { "backup-converted", 'K', OPT_BOOLEAN, "backupconverted", -1 },
134     { "backups", 0, OPT_BOOLEAN, "backups", -1 },
135     { "base", 'B', OPT_VALUE, "base", -1 },
136     { "bind-address", 0, OPT_VALUE, "bindaddress", -1 },
137     { IF_SSL ("ca-certificate"), 0, OPT_VALUE, "cacertificate", -1 },
138     { IF_SSL ("ca-directory"), 0, OPT_VALUE, "cadirectory", -1 },
139     { "cache", 0, OPT_BOOLEAN, "cache", -1 },
140     { IF_SSL ("certificate"), 0, OPT_VALUE, "certificate", -1 },
141     { IF_SSL ("certificate-type"), 0, OPT_VALUE, "certificatetype", -1 },
142     { IF_SSL ("check-certificate"), 0, OPT_BOOLEAN, "checkcertificate", -1 },
143     { "clobber", 0, OPT__CLOBBER, NULL, optional_argument },
144     { "connect-timeout", 0, OPT_VALUE, "connecttimeout", -1 },
145     { "continue", 'c', OPT_BOOLEAN, "continue", -1 },
146     { "convert-links", 'k', OPT_BOOLEAN, "convertlinks", -1 },
147     { "content-disposition", 0, OPT_BOOLEAN, "contentdisposition", -1 },
148     { "cookies", 0, OPT_BOOLEAN, "cookies", -1 },
149     { "cut-dirs", 0, OPT_VALUE, "cutdirs", -1 },
150     { WHEN_DEBUG ("debug"), 'd', OPT_BOOLEAN, "debug", -1 },
151     { "delete-after", 0, OPT_BOOLEAN, "deleteafter", -1 },
152     { "directories", 0, OPT_BOOLEAN, "dirstruct", -1 },
153     { "directory-prefix", 'P', OPT_VALUE, "dirprefix", -1 },
154     { "dns-cache", 0, OPT_BOOLEAN, "dnscache", -1 },
155     { "dns-timeout", 0, OPT_VALUE, "dnstimeout", -1 },
156     { "domains", 'D', OPT_VALUE, "domains", -1 },
157     { "dont-remove-listing", 0, OPT__DONT_REMOVE_LISTING, NULL, no_argument },
158     { "dot-style", 0, OPT_VALUE, "dotstyle", -1 },
159     { "egd-file", 0, OPT_VALUE, "egdfile", -1 },
160     { "exclude-directories", 'X', OPT_VALUE, "excludedirectories", -1 },
161     { "exclude-domains", 0, OPT_VALUE, "excludedomains", -1 },
162     { "execute", 'e', OPT__EXECUTE, NULL, required_argument },
163     { "follow-ftp", 0, OPT_BOOLEAN, "followftp", -1 },
164     { "follow-tags", 0, OPT_VALUE, "followtags", -1 },
165     { "force-directories", 'x', OPT_BOOLEAN, "dirstruct", -1 },
166     { "force-html", 'F', OPT_BOOLEAN, "forcehtml", -1 },
167     { "ftp-password", 0, OPT_VALUE, "ftppassword", -1 },
168     { "ftp-user", 0, OPT_VALUE, "ftpuser", -1 },
169     { "glob", 0, OPT_BOOLEAN, "glob", -1 },
170     { "header", 0, OPT_VALUE, "header", -1 },
171     { "help", 'h', OPT_FUNCALL, (void *)print_help, no_argument },
172     { "host-directories", 0, OPT_BOOLEAN, "addhostdir", -1 },
173     { "html-extension", 'E', OPT_BOOLEAN, "htmlextension", -1 },
174     { "htmlify", 0, OPT_BOOLEAN, "htmlify", -1 },
175     { "http-keep-alive", 0, OPT_BOOLEAN, "httpkeepalive", -1 },
176     { "http-passwd", 0, OPT_VALUE, "httppassword", -1 }, /* deprecated */
177     { "http-password", 0, OPT_VALUE, "httppassword", -1 },
178     { "http-user", 0, OPT_VALUE, "httpuser", -1 },
179     { "ignore-case", 0, OPT_BOOLEAN, "ignorecase", -1 },
180     { "ignore-length", 0, OPT_BOOLEAN, "ignorelength", -1 },
181     { "ignore-tags", 0, OPT_VALUE, "ignoretags", -1 },
182     { "include-directories", 'I', OPT_VALUE, "includedirectories", -1 },
183 #ifdef ENABLE_IPV6
184     { "inet4-only", '4', OPT_BOOLEAN, "inet4only", -1 },
185     { "inet6-only", '6', OPT_BOOLEAN, "inet6only", -1 },
186 #endif
187     { "input-file", 'i', OPT_VALUE, "input", -1 },
188     { "keep-session-cookies", 0, OPT_BOOLEAN, "keepsessioncookies", -1 },
189     { "level", 'l', OPT_VALUE, "reclevel", -1 },
190     { "limit-rate", 0, OPT_VALUE, "limitrate", -1 },
191     { "load-cookies", 0, OPT_VALUE, "loadcookies", -1 },
192     { "max-redirect", 0, OPT_VALUE, "maxredirect", -1 },
193     { "mirror", 'm', OPT_BOOLEAN, "mirror", -1 },
194     { "no", 'n', OPT__NO, NULL, required_argument },
195     { "no-clobber", 0, OPT_BOOLEAN, "noclobber", -1 },
196     { "no-parent", 0, OPT_BOOLEAN, "noparent", -1 },
197     { "output-document", 'O', OPT_VALUE, "outputdocument", -1 },
198     { "output-file", 'o', OPT_VALUE, "logfile", -1 },
199     { "page-requisites", 'p', OPT_BOOLEAN, "pagerequisites", -1 },
200     { "parent", 0, OPT__PARENT, NULL, optional_argument },
201     { "passive-ftp", 0, OPT_BOOLEAN, "passiveftp", -1 },
202     { "password", 0, OPT_VALUE, "password", -1 },
203     { "post-data", 0, OPT_VALUE, "postdata", -1 },
204     { "post-file", 0, OPT_VALUE, "postfile", -1 },
205     { "prefer-family", 0, OPT_VALUE, "preferfamily", -1 },
206     { "preserve-permissions", 0, OPT_BOOLEAN, "preservepermissions", -1 },
207     { IF_SSL ("private-key"), 0, OPT_VALUE, "privatekey", -1 },
208     { IF_SSL ("private-key-type"), 0, OPT_VALUE, "privatekeytype", -1 },
209     { "progress", 0, OPT_VALUE, "progress", -1 },
210     { "protocol-directories", 0, OPT_BOOLEAN, "protocoldirectories", -1 },
211     { "proxy", 0, OPT_BOOLEAN, "useproxy", -1 },
212     { "proxy__compat", 'Y', OPT_VALUE, "useproxy", -1 }, /* back-compatible */
213     { "proxy-passwd", 0, OPT_VALUE, "proxypassword", -1 }, /* deprecated */
214     { "proxy-password", 0, OPT_VALUE, "proxypassword", -1 },
215     { "proxy-user", 0, OPT_VALUE, "proxyuser", -1 },
216     { "quiet", 'q', OPT_BOOLEAN, "quiet", -1 },
217     { "quota", 'Q', OPT_VALUE, "quota", -1 },
218     { "random-file", 0, OPT_VALUE, "randomfile", -1 },
219     { "random-wait", 0, OPT_BOOLEAN, "randomwait", -1 },
220     { "read-timeout", 0, OPT_VALUE, "readtimeout", -1 },
221     { "recursive", 'r', OPT_BOOLEAN, "recursive", -1 },
222     { "referer", 0, OPT_VALUE, "referer", -1 },
223     { "reject", 'R', OPT_VALUE, "reject", -1 },
224     { "relative", 'L', OPT_BOOLEAN, "relativeonly", -1 },
225     { "remove-listing", 0, OPT_BOOLEAN, "removelisting", -1 },
226     { "restrict-file-names", 0, OPT_BOOLEAN, "restrictfilenames", -1 },
227     { "retr-symlinks", 0, OPT_BOOLEAN, "retrsymlinks", -1 },
228     { "retry-connrefused", 0, OPT_BOOLEAN, "retryconnrefused", -1 },
229     { "save-cookies", 0, OPT_VALUE, "savecookies", -1 },
230     { "save-headers", 0, OPT_BOOLEAN, "saveheaders", -1 },
231     { IF_SSL ("secure-protocol"), 0, OPT_VALUE, "secureprotocol", -1 },
232     { "server-response", 'S', OPT_BOOLEAN, "serverresponse", -1 },
233     { "span-hosts", 'H', OPT_BOOLEAN, "spanhosts", -1 },
234     { "spider", 0, OPT_BOOLEAN, "spider", -1 },
235     { "strict-comments", 0, OPT_BOOLEAN, "strictcomments", -1 },
236     { "timeout", 'T', OPT_VALUE, "timeout", -1 },
237     { "timestamping", 'N', OPT_BOOLEAN, "timestamping", -1 },
238     { "tries", 't', OPT_VALUE, "tries", -1 },
239     { "user", 0, OPT_VALUE, "user", -1 },
240     { "user-agent", 'U', OPT_VALUE, "useragent", -1 },
241     { "verbose", 'v', OPT_BOOLEAN, "verbose", -1 },
242     { "verbose", 0, OPT_BOOLEAN, "verbose", -1 },
243     { "version", 'V', OPT_FUNCALL, (void *) print_version, no_argument },
244     { "wait", 'w', OPT_VALUE, "wait", -1 },
245     { "waitretry", 0, OPT_VALUE, "waitretry", -1 },
246   };
247
248 #undef WHEN_DEBUG
249 #undef IF_SSL
250
251 /* Return a string that contains S with "no-" prepended.  The string
252    is NUL-terminated and allocated off static storage at Wget
253    startup.  */
254
255 static char *
256 no_prefix (const char *s)
257 {
258   static char buffer[1024];
259   static char *p = buffer;
260
261   char *cp = p;
262   int size = 3 + strlen (s) + 1;  /* "no-STRING\0" */
263   if (p + size >= buffer + sizeof (buffer))
264     abort ();
265
266   cp[0] = 'n', cp[1] = 'o', cp[2] = '-';
267   strcpy (cp + 3, s);
268   p += size;
269   return cp;
270 }
271
272 /* The arguments that that main passes to getopt_long. */
273 static struct option long_options[2 * countof (option_data) + 1];
274 static char short_options[128];
275
276 /* Mapping between short option chars and option_data indices. */
277 static unsigned char optmap[96];
278
279 /* Marker for `--no-FOO' values in long_options.  */
280 #define BOOLEAN_NEG_MARKER 1024
281
282 /* Initialize the long_options array used by getopt_long from the data
283    in option_data.  */
284
285 static void
286 init_switches (void)
287 {
288   char *p = short_options;
289   int i, o = 0;
290   for (i = 0; i < countof (option_data); i++)
291     {
292       struct cmdline_option *opt = &option_data[i];
293       struct option *longopt;
294
295       if (!opt->long_name)
296         /* The option is disabled. */
297         continue;
298
299       longopt = &long_options[o++];
300       longopt->name = opt->long_name;
301       longopt->val = i;
302       if (opt->short_name)
303         {
304           *p++ = opt->short_name;
305           optmap[opt->short_name - 32] = longopt - long_options;
306         }
307       switch (opt->type)
308         {
309         case OPT_VALUE:
310           longopt->has_arg = required_argument;
311           if (opt->short_name)
312             *p++ = ':';
313           break;
314         case OPT_BOOLEAN:
315           /* Specify an optional argument for long options, so that
316              --option=off works the same as --no-option, for
317              compatibility with pre-1.10 Wget.  However, don't specify
318              optional arguments short-option booleans because they
319              prevent combining of short options.  */
320           longopt->has_arg = optional_argument;
321           /* For Boolean options, add the "--no-FOO" variant, which is
322              identical to "--foo", except it has opposite meaning and
323              it doesn't allow an argument.  */
324           longopt = &long_options[o++];
325           longopt->name = no_prefix (opt->long_name);
326           longopt->has_arg = no_argument;
327           /* Mask the value so we'll be able to recognize that we're
328              dealing with the false value.  */
329           longopt->val = i | BOOLEAN_NEG_MARKER;
330           break;
331         default:
332           assert (opt->argtype != -1);
333           longopt->has_arg = opt->argtype;
334           if (opt->short_name)
335             {
336               if (longopt->has_arg == required_argument)
337                 *p++ = ':';
338               /* Don't handle optional_argument */
339             }
340         }
341     }
342   /* Terminate short_options. */
343   *p = '\0';
344   /* No need for xzero(long_options[o]) because its storage is static
345      and it will be zeroed by default.  */
346   assert (o <= countof (long_options));
347 }
348
349 /* Print the usage message.  */
350 static void
351 print_usage (void)
352 {
353   printf (_("Usage: %s [OPTION]... [URL]...\n"), exec_name);
354 }
355
356 /* Print the help message, describing all the available options.  If
357    you add an option, be sure to update this list.  */
358 static void
359 print_help (void)
360 {
361   /* We split the help text this way to ease translation of individual
362      entries.  */
363   static const char *help[] = {
364     "\n",
365     N_("\
366 Mandatory arguments to long options are mandatory for short options too.\n\n"),
367     N_("\
368 Startup:\n"),
369     N_("\
370   -V,  --version           display the version of Wget and exit.\n"),
371     N_("\
372   -h,  --help              print this help.\n"),
373     N_("\
374   -b,  --background        go to background after startup.\n"),
375     N_("\
376   -e,  --execute=COMMAND   execute a `.wgetrc'-style command.\n"),
377     "\n",
378
379     N_("\
380 Logging and input file:\n"),
381     N_("\
382   -o,  --output-file=FILE    log messages to FILE.\n"),
383     N_("\
384   -a,  --append-output=FILE  append messages to FILE.\n"),
385 #ifdef ENABLE_DEBUG
386     N_("\
387   -d,  --debug               print lots of debugging information.\n"),
388 #endif
389     N_("\
390   -q,  --quiet               quiet (no output).\n"),
391     N_("\
392   -v,  --verbose             be verbose (this is the default).\n"),
393     N_("\
394   -nv, --no-verbose          turn off verboseness, without being quiet.\n"),
395     N_("\
396   -i,  --input-file=FILE     download URLs found in FILE.\n"),
397     N_("\
398   -F,  --force-html          treat input file as HTML.\n"),
399     N_("\
400   -B,  --base=URL            prepends URL to relative links in -F -i file.\n"),
401     "\n",
402
403     N_("\
404 Download:\n"),
405     N_("\
406   -t,  --tries=NUMBER            set number of retries to NUMBER (0 unlimits).\n"),
407     N_("\
408        --retry-connrefused       retry even if connection is refused.\n"),
409     N_("\
410   -O,  --output-document=FILE    write documents to FILE.\n"),
411     N_("\
412   -nc, --no-clobber              skip downloads that would download to\n\
413                                  existing files.\n"),
414     N_("\
415   -c,  --continue                resume getting a partially-downloaded file.\n"),
416     N_("\
417        --progress=TYPE           select progress gauge type.\n"),
418     N_("\
419   -N,  --timestamping            don't re-retrieve files unless newer than\n\
420                                  local.\n"),
421     N_("\
422   -S,  --server-response         print server response.\n"),
423     N_("\
424        --spider                  don't download anything.\n"),
425     N_("\
426   -T,  --timeout=SECONDS         set all timeout values to SECONDS.\n"),
427     N_("\
428        --dns-timeout=SECS        set the DNS lookup timeout to SECS.\n"),
429     N_("\
430        --connect-timeout=SECS    set the connect timeout to SECS.\n"),
431     N_("\
432        --read-timeout=SECS       set the read timeout to SECS.\n"),
433     N_("\
434   -w,  --wait=SECONDS            wait SECONDS between retrievals.\n"),
435     N_("\
436        --waitretry=SECONDS       wait 1..SECONDS between retries of a retrieval.\n"),
437     N_("\
438        --random-wait             wait from 0...2*WAIT secs between retrievals.\n"),
439     N_("\
440   -Y,  --proxy                   explicitly turn on proxy.\n"),
441     N_("\
442        --no-proxy                explicitly turn off proxy.\n"),
443     N_("\
444   -Q,  --quota=NUMBER            set retrieval quota to NUMBER.\n"),
445     N_("\
446        --bind-address=ADDRESS    bind to ADDRESS (hostname or IP) on local host.\n"),
447     N_("\
448        --limit-rate=RATE         limit download rate to RATE.\n"),
449     N_("\
450        --no-dns-cache            disable caching DNS lookups.\n"),
451     N_("\
452        --restrict-file-names=OS  restrict chars in file names to ones OS allows.\n"),
453     N_("\
454        --ignore-case             ignore case when matching files/directories.\n"),
455 #ifdef ENABLE_IPV6
456     N_("\
457   -4,  --inet4-only              connect only to IPv4 addresses.\n"),
458     N_("\
459   -6,  --inet6-only              connect only to IPv6 addresses.\n"),
460     N_("\
461        --prefer-family=FAMILY    connect first to addresses of specified family,\n\
462                                  one of IPv6, IPv4, or none.\n"),
463 #endif
464     N_("\
465        --user=USER               set both ftp and http user to USER.\n"),
466     N_("\
467        --password=PASS           set both ftp and http password to PASS.\n"),
468     "\n",
469
470     N_("\
471 Directories:\n"),
472     N_("\
473   -nd, --no-directories           don't create directories.\n"),
474     N_("\
475   -x,  --force-directories        force creation of directories.\n"),
476     N_("\
477   -nH, --no-host-directories      don't create host directories.\n"),
478     N_("\
479        --protocol-directories     use protocol name in directories.\n"),
480     N_("\
481   -P,  --directory-prefix=PREFIX  save files to PREFIX/...\n"),
482     N_("\
483        --cut-dirs=NUMBER          ignore NUMBER remote directory components.\n"),
484     "\n",
485
486     N_("\
487 HTTP options:\n"),
488     N_("\
489        --http-user=USER        set http user to USER.\n"),
490     N_("\
491        --http-password=PASS    set http password to PASS.\n"),
492     N_("\
493        --no-cache              disallow server-cached data.\n"),
494     N_("\
495   -E,  --html-extension        save HTML documents with `.html' extension.\n"),
496     N_("\
497        --ignore-length         ignore `Content-Length' header field.\n"),
498     N_("\
499        --header=STRING         insert STRING among the headers.\n"),
500     N_("\
501        --max-redirect          maximum redirections allowed per page.\n"),
502     N_("\
503        --proxy-user=USER       set USER as proxy username.\n"),
504     N_("\
505        --proxy-password=PASS   set PASS as proxy password.\n"),
506     N_("\
507        --referer=URL           include `Referer: URL' header in HTTP request.\n"),
508     N_("\
509        --save-headers          save the HTTP headers to file.\n"),
510     N_("\
511   -U,  --user-agent=AGENT      identify as AGENT instead of Wget/VERSION.\n"),
512     N_("\
513        --no-http-keep-alive    disable HTTP keep-alive (persistent connections).\n"),
514     N_("\
515        --no-cookies            don't use cookies.\n"),
516     N_("\
517        --load-cookies=FILE     load cookies from FILE before session.\n"),
518     N_("\
519        --save-cookies=FILE     save cookies to FILE after session.\n"),
520     N_("\
521        --keep-session-cookies  load and save session (non-permanent) cookies.\n"),
522     N_("\
523        --post-data=STRING      use the POST method; send STRING as the data.\n"),
524     N_("\
525        --post-file=FILE        use the POST method; send contents of FILE.\n"),
526     N_("\
527        --no-content-disposition  don't honor Content-Disposition header.\n"),
528     "\n",
529
530 #ifdef HAVE_SSL
531     N_("\
532 HTTPS (SSL/TLS) options:\n"),
533     N_("\
534        --secure-protocol=PR     choose secure protocol, one of auto, SSLv2,\n\
535                                 SSLv3, and TLSv1.\n"),
536     N_("\
537        --no-check-certificate   don't validate the server's certificate.\n"),
538     N_("\
539        --certificate=FILE       client certificate file.\n"),
540     N_("\
541        --certificate-type=TYPE  client certificate type, PEM or DER.\n"),
542     N_("\
543        --private-key=FILE       private key file.\n"),
544     N_("\
545        --private-key-type=TYPE  private key type, PEM or DER.\n"),
546     N_("\
547        --ca-certificate=FILE    file with the bundle of CA's.\n"),
548     N_("\
549        --ca-directory=DIR       directory where hash list of CA's is stored.\n"),
550     N_("\
551        --random-file=FILE       file with random data for seeding the SSL PRNG.\n"),
552     N_("\
553        --egd-file=FILE          file naming the EGD socket with random data.\n"),
554     "\n",
555 #endif /* HAVE_SSL */
556
557     N_("\
558 FTP options:\n"),
559     N_("\
560        --ftp-user=USER         set ftp user to USER.\n"),
561     N_("\
562        --ftp-password=PASS     set ftp password to PASS.\n"),
563     N_("\
564        --no-remove-listing     don't remove `.listing' files.\n"),
565     N_("\
566        --no-glob               turn off FTP file name globbing.\n"),
567     N_("\
568        --no-passive-ftp        disable the \"passive\" transfer mode.\n"),
569     N_("\
570        --retr-symlinks         when recursing, get linked-to files (not dir).\n"),
571     N_("\
572        --preserve-permissions  preserve remote file permissions.\n"),
573     "\n",
574
575     N_("\
576 Recursive download:\n"),
577     N_("\
578   -r,  --recursive          specify recursive download.\n"),
579     N_("\
580   -l,  --level=NUMBER       maximum recursion depth (inf or 0 for infinite).\n"),
581     N_("\
582        --delete-after       delete files locally after downloading them.\n"),
583     N_("\
584   -k,  --convert-links      make links in downloaded HTML point to local files.\n"),
585     N_("\
586   -K,  --backup-converted   before converting file X, back up as X.orig.\n"),
587     N_("\
588   -m,  --mirror             shortcut for -N -r -l inf --no-remove-listing.\n"),
589     N_("\
590   -p,  --page-requisites    get all images, etc. needed to display HTML page.\n"),
591     N_("\
592        --strict-comments    turn on strict (SGML) handling of HTML comments.\n"),
593     "\n",
594
595     N_("\
596 Recursive accept/reject:\n"),
597     N_("\
598   -A,  --accept=LIST               comma-separated list of accepted extensions.\n"),
599     N_("\
600   -R,  --reject=LIST               comma-separated list of rejected extensions.\n"),
601     N_("\
602   -D,  --domains=LIST              comma-separated list of accepted domains.\n"),
603     N_("\
604        --exclude-domains=LIST      comma-separated list of rejected domains.\n"),
605     N_("\
606        --follow-ftp                follow FTP links from HTML documents.\n"),
607     N_("\
608        --follow-tags=LIST          comma-separated list of followed HTML tags.\n"),
609     N_("\
610        --ignore-tags=LIST          comma-separated list of ignored HTML tags.\n"),
611     N_("\
612   -H,  --span-hosts                go to foreign hosts when recursive.\n"),
613     N_("\
614   -L,  --relative                  follow relative links only.\n"),
615     N_("\
616   -I,  --include-directories=LIST  list of allowed directories.\n"),
617     N_("\
618   -X,  --exclude-directories=LIST  list of excluded directories.\n"),
619     N_("\
620   -np, --no-parent                 don't ascend to the parent directory.\n"),
621     "\n",
622
623     N_("Mail bug reports and suggestions to <bug-wget@gnu.org>.\n")
624   };
625
626   int i;
627
628   printf (_("GNU Wget %s, a non-interactive network retriever.\n"),
629           version_string);
630   print_usage ();
631
632   for (i = 0; i < countof (help); i++)
633     fputs (_(help[i]), stdout);
634
635   exit (0);
636 }
637
638 /* Return a human-readable printed representation of INTERVAL,
639    measured in seconds.  */
640
641 static char *
642 secs_to_human_time (double interval)
643 {
644   static char buf[32];
645   int secs = (int) (interval + 0.5);
646   int hours, mins, days;
647
648   days = secs / 86400, secs %= 86400;
649   hours = secs / 3600, secs %= 3600;
650   mins = secs / 60, secs %= 60;
651
652   if (days)
653     sprintf (buf, "%dd %dh %dm %ds", days, hours, mins, secs);
654   else if (hours)
655     sprintf (buf, "%dh %dm %ds", hours, mins, secs);
656   else if (mins)
657     sprintf (buf, "%dm %ds", mins, secs);
658   else
659     sprintf (buf, "%ss", print_decimal (interval));
660
661   return buf;
662 }
663
664 static void
665 print_version (void)
666 {
667   printf ("GNU Wget %s\n\n", version_string);
668   fputs (_("\
669 Copyright (C) 2007 Free Software Foundation, Inc.\n"), stdout);
670   fputs (_("\
671 License GPLv3+: GNU GPL version 3 or later\n\
672 <http://www.gnu.org/licenses/gpl.html>.\n\
673 This is free software: you are free to change and redistribute it.\n\
674 There is NO WARRANTY, to the extent permitted by law.\n"), stdout);
675   fputs (_("\nOriginally written by Hrvoje Niksic <hniksic@xemacs.org>.\n"),
676          stdout);
677   fputs (_("Currently maintained by Micah Cowan <micah@cowan.name>.\n"),
678          stdout);
679   exit (0);
680 }
681 \f
682 #ifndef TESTING
683 int
684 main (int argc, char *const *argv)
685 {
686   char **url, **t;
687   int i, ret, longindex;
688   int nurl, status;
689   bool append_to_log = false;
690
691   i18n_initialize ();
692
693   /* Construct the name of the executable, without the directory part.  */
694   exec_name = strrchr (argv[0], PATH_SEPARATOR);
695   if (!exec_name)
696     exec_name = argv[0];
697   else
698     ++exec_name;
699
700 #ifdef WINDOWS
701   /* Drop extension (typically .EXE) from executable filename. */
702   windows_main (&argc, (char **) argv, (char **) &exec_name);
703 #endif
704
705   /* Set option defaults; read the system wgetrc and ~/.wgetrc.  */
706   initialize ();
707
708   init_switches ();
709   longindex = -1;
710   while ((ret = getopt_long (argc, argv,
711                              short_options, long_options, &longindex)) != -1)
712     {
713       int val;
714       struct cmdline_option *opt;
715
716       /* If LONGINDEX is unchanged, it means RET is referring a short
717          option.  */
718       if (longindex == -1)
719         {
720           if (ret == '?')
721             {
722               print_usage ();
723               printf ("\n");
724               printf (_("Try `%s --help' for more options.\n"), exec_name);
725               exit (2);
726             }
727           /* Find the short option character in the mapping.  */
728           longindex = optmap[ret - 32];
729         }
730       val = long_options[longindex].val;
731
732       /* Use the retrieved value to locate the option in the
733          option_data array, and to see if we're dealing with the
734          negated "--no-FOO" variant of the boolean option "--foo".  */
735       opt = &option_data[val & ~BOOLEAN_NEG_MARKER];
736       switch (opt->type)
737         {
738         case OPT_VALUE:
739           setoptval (opt->data, optarg, opt->long_name);
740           break;
741         case OPT_BOOLEAN:
742           if (optarg)
743             /* The user has specified a value -- use it. */
744             setoptval (opt->data, optarg, opt->long_name);
745           else
746             {
747               /* NEG is true for `--no-FOO' style boolean options. */
748               bool neg = !!(val & BOOLEAN_NEG_MARKER);
749               setoptval (opt->data, neg ? "0" : "1", opt->long_name);
750             }
751           break;
752         case OPT_FUNCALL:
753           {
754             void (*func) (void) = (void (*) (void)) opt->data;
755             func ();
756           }
757           break;
758         case OPT__APPEND_OUTPUT:
759           setoptval ("logfile", optarg, opt->long_name);
760           append_to_log = true;
761           break;
762         case OPT__EXECUTE:
763           run_command (optarg);
764           break;
765         case OPT__NO:
766           {
767             /* We support real --no-FOO flags now, but keep these
768                short options for convenience and backward
769                compatibility.  */
770             char *p;
771             for (p = optarg; *p; p++)
772               switch (*p)
773                 {
774                 case 'v':
775                   setoptval ("verbose", "0", opt->long_name);
776                   break;
777                 case 'H':
778                   setoptval ("addhostdir", "0", opt->long_name);
779                   break;
780                 case 'd':
781                   setoptval ("dirstruct", "0", opt->long_name);
782                   break;
783                 case 'c':
784                   setoptval ("noclobber", "1", opt->long_name);
785                   break;
786                 case 'p':
787                   setoptval ("noparent", "1", opt->long_name);
788                   break;
789                 default:
790                   printf (_("%s: illegal option -- `-n%c'\n"), exec_name, *p);
791                   print_usage ();
792                   printf ("\n");
793                   printf (_("Try `%s --help' for more options.\n"), exec_name);
794                   exit (1);
795                 }
796             break;
797           }
798         case OPT__PARENT:
799         case OPT__CLOBBER:
800           {
801             /* The wgetrc commands are named noparent and noclobber,
802                so we must revert the meaning of the cmdline options
803                before passing the value to setoptval.  */
804             bool flag = true;
805             if (optarg)
806               flag = (*optarg == '1' || TOLOWER (*optarg) == 'y'
807                       || (TOLOWER (optarg[0]) == 'o'
808                           && TOLOWER (optarg[1]) == 'n'));
809             setoptval (opt->type == OPT__PARENT ? "noparent" : "noclobber",
810                        flag ? "0" : "1", opt->long_name);
811             break;
812           }
813         case OPT__DONT_REMOVE_LISTING:
814           setoptval ("removelisting", "0", opt->long_name);
815           break;
816         }
817
818       longindex = -1;
819     }
820
821   nurl = argc - optind;
822
823   /* All user options have now been processed, so it's now safe to do
824      interoption dependency checks. */
825
826   if (opt.reclevel == 0)
827     opt.reclevel = INFINITE_RECURSION; /* see recur.h for commentary on this */
828
829   if (opt.page_requisites && !opt.recursive)
830     {
831       /* Don't set opt.recursive here because it would confuse the FTP
832          code.  Instead, call retrieve_tree below when either
833          page_requisites or recursive is requested.  */
834       opt.reclevel = 0;
835       if (!opt.no_dirstruct)
836         opt.dirstruct = 1;      /* normally handled by cmd_spec_recursive() */
837     }
838
839   if (opt.verbose == -1)
840     opt.verbose = !opt.quiet;
841
842   /* Sanity checks.  */
843   if (opt.verbose && opt.quiet)
844     {
845       printf (_("Can't be verbose and quiet at the same time.\n"));
846       print_usage ();
847       exit (1);
848     }
849   if (opt.timestamping && opt.noclobber)
850     {
851       printf (_("\
852 Can't timestamp and not clobber old files at the same time.\n"));
853       print_usage ();
854       exit (1);
855     }
856 #ifdef ENABLE_IPV6
857   if (opt.ipv4_only && opt.ipv6_only)
858     {
859       printf (_("Cannot specify both --inet4-only and --inet6-only.\n"));
860       print_usage ();
861       exit (1);
862     }
863 #endif
864   if (opt.output_document
865       && (opt.page_requisites
866           || opt.recursive
867           || opt.timestamping))
868     {
869           printf (_("Cannot specify -r, -p or -N if -O is given.\n"));
870           print_usage ();
871           exit (1);
872     }
873   if (opt.output_document
874       && opt.convert_links 
875       && nurl > 1)
876     {
877           printf (_("Cannot specify both -k and -O if multiple URLs are given.\n"));
878           print_usage ();
879           exit (1);
880     }
881
882   if (!nurl && !opt.input_filename)
883     {
884       /* No URL specified.  */
885       printf (_("%s: missing URL\n"), exec_name);
886       print_usage ();
887       printf ("\n");
888       /* #### Something nicer should be printed here -- similar to the
889          pre-1.5 `--help' page.  */
890       printf (_("Try `%s --help' for more options.\n"), exec_name);
891       exit (1);
892     }
893
894   if (opt.background)
895     fork_to_background ();
896
897   /* Initialize progress.  Have to do this after the options are
898      processed so we know where the log file is.  */
899   if (opt.verbose)
900     set_progress_implementation (opt.progress_type);
901
902   /* Fill in the arguments.  */
903   url = alloca_array (char *, nurl + 1);
904   for (i = 0; i < nurl; i++, optind++)
905     {
906       char *rewritten = rewrite_shorthand_url (argv[optind]);
907       if (rewritten)
908         url[i] = rewritten;
909       else
910         url[i] = xstrdup (argv[optind]);
911     }
912   url[i] = NULL;
913
914   /* Initialize logging.  */
915   log_init (opt.lfilename, append_to_log);
916
917   DEBUGP (("DEBUG output created by Wget %s on %s.\n\n", version_string,
918            OS_TYPE));
919
920   /* Open the output filename if necessary.  */
921   if (opt.output_document)
922     {
923       if (HYPHENP (opt.output_document))
924         output_stream = stdout;
925       else
926         {
927           struct_fstat st;
928           output_stream = fopen (opt.output_document,
929                                  opt.always_rest ? "ab" : "wb");
930           if (output_stream == NULL)
931             {
932               perror (opt.output_document);
933               exit (1);
934             }
935           if (fstat (fileno (output_stream), &st) == 0 && S_ISREG (st.st_mode))
936             output_stream_regular = true;
937         }
938     }
939
940 #ifdef WINDOWS
941   ws_startup ();
942 #endif
943
944 #ifdef SIGHUP
945   /* Setup the signal handler to redirect output when hangup is
946      received.  */
947   if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
948     signal(SIGHUP, redirect_output_signal);
949 #endif
950   /* ...and do the same for SIGUSR1.  */
951 #ifdef SIGUSR1
952   signal (SIGUSR1, redirect_output_signal);
953 #endif
954 #ifdef SIGPIPE
955   /* Writing to a closed socket normally signals SIGPIPE, and the
956      process exits.  What we want is to ignore SIGPIPE and just check
957      for the return value of write().  */
958   signal (SIGPIPE, SIG_IGN);
959 #endif
960 #ifdef SIGWINCH
961   signal (SIGWINCH, progress_handle_sigwinch);
962 #endif
963
964   status = RETROK;              /* initialize it, just-in-case */
965   /* Retrieve the URLs from argument list.  */
966   for (t = url; *t; t++)
967     {
968       char *filename = NULL, *redirected_URL = NULL;
969       int dt;
970
971       if ((opt.recursive || opt.page_requisites)
972           && (url_scheme (*t) != SCHEME_FTP || url_uses_proxy (*t)))
973         {
974           int old_follow_ftp = opt.follow_ftp;
975
976           /* Turn opt.follow_ftp on in case of recursive FTP retrieval */
977           if (url_scheme (*t) == SCHEME_FTP) 
978             opt.follow_ftp = 1;
979           
980           status = retrieve_tree (*t);
981
982           opt.follow_ftp = old_follow_ftp;
983         }
984       else
985         status = retrieve_url (*t, &filename, &redirected_URL, NULL, &dt, opt.recursive);
986
987       if (opt.delete_after && file_exists_p(filename))
988         {
989           DEBUGP (("Removing file due to --delete-after in main():\n"));
990           logprintf (LOG_VERBOSE, _("Removing %s.\n"), filename);
991           if (unlink (filename))
992             logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
993         }
994
995       xfree_null (redirected_URL);
996       xfree_null (filename);
997     }
998
999   /* And then from the input file, if any.  */
1000   if (opt.input_filename)
1001     {
1002       int count;
1003       status = retrieve_from_file (opt.input_filename, opt.force_html, &count);
1004       if (!count)
1005         logprintf (LOG_NOTQUIET, _("No URLs found in %s.\n"),
1006                    opt.input_filename);
1007     }
1008
1009   /* Print broken links. */
1010   if (opt.recursive && opt.spider)
1011     {
1012       print_broken_links();
1013     }
1014   
1015   /* Print the downloaded sum.  */
1016   if ((opt.recursive || opt.page_requisites
1017        || nurl > 1
1018        || (opt.input_filename && total_downloaded_bytes != 0))
1019       &&
1020       total_downloaded_bytes != 0)
1021     {
1022       logprintf (LOG_NOTQUIET,
1023                  _("FINISHED --%s--\nDownloaded: %d files, %s in %s (%s)\n"),
1024                  time_str (time (NULL)),
1025                  opt.numurls,
1026                  human_readable (total_downloaded_bytes),
1027                  secs_to_human_time (total_download_time),
1028                  retr_rate (total_downloaded_bytes, total_download_time));
1029       /* Print quota warning, if exceeded.  */
1030       if (opt.quota && total_downloaded_bytes > opt.quota)
1031         logprintf (LOG_NOTQUIET,
1032                    _("Download quota of %s EXCEEDED!\n"),
1033                    human_readable (opt.quota));
1034     }
1035
1036   if (opt.cookies_output)
1037     save_cookies ();
1038
1039   if (opt.convert_links && !opt.delete_after)
1040     convert_all_links ();
1041
1042   log_close ();
1043   for (i = 0; i < nurl; i++)
1044     xfree (url[i]);
1045   cleanup ();
1046
1047 #ifdef DEBUG_MALLOC
1048   print_malloc_debug_stats ();
1049 #endif
1050   if (status == RETROK)
1051     return 0;
1052   else
1053     return 1;
1054 }
1055 #endif /* TESTING */
1056 \f
1057 #if defined(SIGHUP) || defined(SIGUSR1)
1058
1059 /* So the signal_name check doesn't blow when only one is available. */
1060 #ifndef SIGHUP
1061 # define SIGHUP -1
1062 #endif
1063 #ifndef SIGUSR1
1064 # define SIGUSR1 -1
1065 #endif
1066
1067 /* Hangup signal handler.  When wget receives SIGHUP or SIGUSR1, it
1068    will proceed operation as usual, trying to write into a log file.
1069    If that is impossible, the output will be turned off.  */
1070
1071 static void
1072 redirect_output_signal (int sig)
1073 {
1074   const char *signal_name = (sig == SIGHUP ? "SIGHUP" :
1075                              (sig == SIGUSR1 ? "SIGUSR1" :
1076                               "WTF?!"));
1077   log_request_redirect_output (signal_name);
1078   progress_schedule_redirect ();
1079   signal (sig, redirect_output_signal);
1080 }
1081 #endif
1082
1083 /*
1084  * vim: et ts=2 sw=2
1085  */