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