]> sjero.net Git - wget/blob - src/init.c
Turn --debug into no-op if compiled without debugging support
[wget] / src / init.c
1 /* Reading/parsing the initialization file.
2    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3    2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation,
4    Inc.
5
6 This file is part of GNU Wget.
7
8 GNU Wget is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
12
13 GNU Wget is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
20
21 Additional permission under GNU GPL version 3 section 7
22
23 If you modify this program, or any covered work, by linking or
24 combining it with the OpenSSL project's OpenSSL library (or a
25 modified version of that library), containing parts covered by the
26 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
27 grants you additional permission to convey the resulting work.
28 Corresponding Source for a non-source form of such a combination
29 shall include the source code for the parts of OpenSSL used as well
30 as that of the covered work.  */
31
32 #include "wget.h"
33 #include "exits.h"
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdbool.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <limits.h>
42 /* not all systems provide PATH_MAX in limits.h */
43 #ifndef PATH_MAX
44 # include <sys/param.h>
45 # ifndef PATH_MAX
46 #  define PATH_MAX MAXPATHLEN
47 # endif
48 #endif
49
50 #include <regex.h>
51 #ifdef HAVE_LIBPCRE
52 # include <pcre.h>
53 #endif
54
55 #ifdef HAVE_PWD_H
56 # include <pwd.h>
57 #endif
58 #include <assert.h>
59
60 #include "utils.h"
61 #include "init.h"
62 #include "host.h"
63 #include "netrc.h"
64 #include "progress.h"
65 #include "recur.h"              /* for INFINITE_RECURSION */
66 #include "convert.h"            /* for convert_cleanup */
67 #include "res.h"                /* for res_cleanup */
68 #include "http.h"               /* for http_cleanup */
69 #include "retr.h"               /* for output_stream */
70 #include "warc.h"               /* for warc_close */
71
72 #ifdef TESTING
73 #include "test.h"
74 #endif
75
76
77
78 #define CMD_DECLARE(func) static bool func (const char *, const char *, void *)
79
80 CMD_DECLARE (cmd_boolean);
81 CMD_DECLARE (cmd_bytes);
82 CMD_DECLARE (cmd_bytes_sum);
83 #ifdef HAVE_SSL
84 CMD_DECLARE (cmd_cert_type);
85 #endif
86 CMD_DECLARE (cmd_directory_vector);
87 CMD_DECLARE (cmd_number);
88 CMD_DECLARE (cmd_number_inf);
89 CMD_DECLARE (cmd_string);
90 CMD_DECLARE (cmd_string_uppercase);
91 CMD_DECLARE (cmd_file);
92 CMD_DECLARE (cmd_directory);
93 CMD_DECLARE (cmd_time);
94 CMD_DECLARE (cmd_vector);
95
96 CMD_DECLARE (cmd_spec_dirstruct);
97 CMD_DECLARE (cmd_spec_header);
98 CMD_DECLARE (cmd_spec_warc_header);
99 CMD_DECLARE (cmd_spec_htmlify);
100 CMD_DECLARE (cmd_spec_mirror);
101 CMD_DECLARE (cmd_spec_prefer_family);
102 CMD_DECLARE (cmd_spec_progress);
103 CMD_DECLARE (cmd_spec_recursive);
104 CMD_DECLARE (cmd_spec_regex_type);
105 CMD_DECLARE (cmd_spec_restrict_file_names);
106 CMD_DECLARE (cmd_spec_report_speed);
107 #ifdef HAVE_SSL
108 CMD_DECLARE (cmd_spec_secure_protocol);
109 #endif
110 CMD_DECLARE (cmd_spec_timeout);
111 CMD_DECLARE (cmd_spec_useragent);
112 CMD_DECLARE (cmd_spec_verbose);
113
114 /* List of recognized commands, each consisting of name, place and
115    function.  When adding a new command, simply add it to the list,
116    but be sure to keep the list sorted alphabetically, as
117    command_by_name's binary search depends on it.  Also, be sure to
118    add any entries that allocate memory (e.g. cmd_string and
119    cmd_vector) to the cleanup() function below. */
120
121 static const struct {
122   const char *name;
123   void *place;
124   bool (*action) (const char *, const char *, void *);
125 } commands[] = {
126   /* KEEP THIS LIST ALPHABETICALLY SORTED */
127   { "accept",           &opt.accepts,           cmd_vector },
128   { "acceptregex",      &opt.acceptregex_s,     cmd_string },
129   { "addhostdir",       &opt.add_hostdir,       cmd_boolean },
130   { "adjustextension",  &opt.adjust_extension,  cmd_boolean },
131   { "alwaysrest",       &opt.always_rest,       cmd_boolean }, /* deprecated */
132   { "askpassword",      &opt.ask_passwd,        cmd_boolean },
133   { "authnochallenge",  &opt.auth_without_challenge,
134                                                 cmd_boolean },
135   { "background",       &opt.background,        cmd_boolean },
136   { "backupconverted",  &opt.backup_converted,  cmd_boolean },
137   { "backups",          &opt.backups,           cmd_number },
138   { "base",             &opt.base_href,         cmd_string },
139   { "bindaddress",      &opt.bind_address,      cmd_string },
140   { "bodydata",         &opt.body_data,         cmd_string },
141   { "bodyfile",         &opt.body_file,         cmd_string },
142 #ifdef HAVE_SSL
143   { "cacertificate",    &opt.ca_cert,           cmd_file },
144 #endif
145   { "cache",            &opt.allow_cache,       cmd_boolean },
146 #ifdef HAVE_SSL
147   { "cadirectory",      &opt.ca_directory,      cmd_directory },
148   { "certificate",      &opt.cert_file,         cmd_file },
149   { "certificatetype",  &opt.cert_type,         cmd_cert_type },
150   { "checkcertificate", &opt.check_cert,        cmd_boolean },
151 #endif
152   { "chooseconfig",     &opt.choose_config,     cmd_file },
153   { "connecttimeout",   &opt.connect_timeout,   cmd_time },
154   { "contentdisposition", &opt.content_disposition, cmd_boolean },
155   { "contentonerror",   &opt.content_on_error,  cmd_boolean },
156   { "continue",         &opt.always_rest,       cmd_boolean },
157   { "convertlinks",     &opt.convert_links,     cmd_boolean },
158   { "cookies",          &opt.cookies,           cmd_boolean },
159   { "cutdirs",          &opt.cut_dirs,          cmd_number },
160   { "debug",            &opt.debug,             cmd_boolean },
161   { "defaultpage",      &opt.default_page,      cmd_string },
162   { "deleteafter",      &opt.delete_after,      cmd_boolean },
163   { "dirprefix",        &opt.dir_prefix,        cmd_directory },
164   { "dirstruct",        NULL,                   cmd_spec_dirstruct },
165   { "dnscache",         &opt.dns_cache,         cmd_boolean },
166   { "dnstimeout",       &opt.dns_timeout,       cmd_time },
167   { "domains",          &opt.domains,           cmd_vector },
168   { "dotbytes",         &opt.dot_bytes,         cmd_bytes },
169   { "dotsinline",       &opt.dots_in_line,      cmd_number },
170   { "dotspacing",       &opt.dot_spacing,       cmd_number },
171   { "dotstyle",         &opt.dot_style,         cmd_string }, /* deprecated */
172 #ifdef HAVE_SSL
173   { "egdfile",          &opt.egd_file,          cmd_file },
174 #endif
175   { "excludedirectories", &opt.excludes,        cmd_directory_vector },
176   { "excludedomains",   &opt.exclude_domains,   cmd_vector },
177   { "followftp",        &opt.follow_ftp,        cmd_boolean },
178   { "followtags",       &opt.follow_tags,       cmd_vector },
179   { "forcehtml",        &opt.force_html,        cmd_boolean },
180   { "ftppasswd",        &opt.ftp_passwd,        cmd_string }, /* deprecated */
181   { "ftppassword",      &opt.ftp_passwd,        cmd_string },
182   { "ftpproxy",         &opt.ftp_proxy,         cmd_string },
183 #ifdef __VMS
184   { "ftpstmlf",         &opt.ftp_stmlf,         cmd_boolean },
185 #endif /* def __VMS */
186   { "ftpuser",          &opt.ftp_user,          cmd_string },
187   { "glob",             &opt.ftp_glob,          cmd_boolean },
188   { "header",           NULL,                   cmd_spec_header },
189   { "htmlextension",    &opt.adjust_extension,  cmd_boolean }, /* deprecated */
190   { "htmlify",          NULL,                   cmd_spec_htmlify },
191   { "httpkeepalive",    &opt.http_keep_alive,   cmd_boolean },
192   { "httppasswd",       &opt.http_passwd,       cmd_string }, /* deprecated */
193   { "httppassword",     &opt.http_passwd,       cmd_string },
194   { "httpproxy",        &opt.http_proxy,        cmd_string },
195 #ifdef HAVE_SSL
196   { "httpsonly",        &opt.https_only,        cmd_boolean },
197 #endif
198   { "httpsproxy",       &opt.https_proxy,       cmd_string },
199   { "httpuser",         &opt.http_user,         cmd_string },
200   { "ignorecase",       &opt.ignore_case,       cmd_boolean },
201   { "ignorelength",     &opt.ignore_length,     cmd_boolean },
202   { "ignoretags",       &opt.ignore_tags,       cmd_vector },
203   { "includedirectories", &opt.includes,        cmd_directory_vector },
204 #ifdef ENABLE_IPV6
205   { "inet4only",        &opt.ipv4_only,         cmd_boolean },
206   { "inet6only",        &opt.ipv6_only,         cmd_boolean },
207 #endif
208   { "input",            &opt.input_filename,    cmd_file },
209   { "iri",              &opt.enable_iri,        cmd_boolean },
210   { "keepsessioncookies", &opt.keep_session_cookies, cmd_boolean },
211   { "limitrate",        &opt.limit_rate,        cmd_bytes },
212   { "loadcookies",      &opt.cookies_input,     cmd_file },
213   { "localencoding",    &opt.locale,            cmd_string },
214   { "logfile",          &opt.lfilename,         cmd_file },
215   { "login",            &opt.ftp_user,          cmd_string },/* deprecated*/
216   { "maxredirect",      &opt.max_redirect,      cmd_number },
217   { "method",           &opt.method,            cmd_string_uppercase },
218   { "mirror",           NULL,                   cmd_spec_mirror },
219   { "netrc",            &opt.netrc,             cmd_boolean },
220   { "noclobber",        &opt.noclobber,         cmd_boolean },
221   { "noconfig",         &opt.noconfig,          cmd_boolean },
222   { "noparent",         &opt.no_parent,         cmd_boolean },
223   { "noproxy",          &opt.no_proxy,          cmd_vector },
224   { "numtries",         &opt.ntry,              cmd_number_inf },/* deprecated*/
225   { "outputdocument",   &opt.output_document,   cmd_file },
226   { "pagerequisites",   &opt.page_requisites,   cmd_boolean },
227   { "passiveftp",       &opt.ftp_pasv,          cmd_boolean },
228   { "passwd",           &opt.ftp_passwd,        cmd_string },/* deprecated*/
229   { "password",         &opt.passwd,            cmd_string },
230   { "postdata",         &opt.post_data,         cmd_string },
231   { "postfile",         &opt.post_file_name,    cmd_file },
232   { "preferfamily",     NULL,                   cmd_spec_prefer_family },
233   { "preservepermissions", &opt.preserve_perm,  cmd_boolean },
234 #ifdef HAVE_SSL
235   { "privatekey",       &opt.private_key,       cmd_file },
236   { "privatekeytype",   &opt.private_key_type,  cmd_cert_type },
237 #endif
238   { "progress",         &opt.progress_type,     cmd_spec_progress },
239   { "protocoldirectories", &opt.protocol_directories, cmd_boolean },
240   { "proxypasswd",      &opt.proxy_passwd,      cmd_string }, /* deprecated */
241   { "proxypassword",    &opt.proxy_passwd,      cmd_string },
242   { "proxyuser",        &opt.proxy_user,        cmd_string },
243   { "quiet",            &opt.quiet,             cmd_boolean },
244   { "quota",            &opt.quota,             cmd_bytes_sum },
245 #ifdef HAVE_SSL
246   { "randomfile",       &opt.random_file,       cmd_file },
247 #endif
248   { "randomwait",       &opt.random_wait,       cmd_boolean },
249   { "readtimeout",      &opt.read_timeout,      cmd_time },
250   { "reclevel",         &opt.reclevel,          cmd_number_inf },
251   { "recursive",        NULL,                   cmd_spec_recursive },
252   { "referer",          &opt.referer,           cmd_string },
253   { "regextype",        &opt.regex_type,        cmd_spec_regex_type },
254   { "reject",           &opt.rejects,           cmd_vector },
255   { "rejectregex",      &opt.rejectregex_s,     cmd_string },
256   { "relativeonly",     &opt.relative_only,     cmd_boolean },
257   { "remoteencoding",   &opt.encoding_remote,   cmd_string },
258   { "removelisting",    &opt.remove_listing,    cmd_boolean },
259   { "reportspeed",             &opt.report_bps, cmd_spec_report_speed},
260   { "restrictfilenames", NULL,                  cmd_spec_restrict_file_names },
261   { "retrsymlinks",     &opt.retr_symlinks,     cmd_boolean },
262   { "retryconnrefused", &opt.retry_connrefused, cmd_boolean },
263   { "robots",           &opt.use_robots,        cmd_boolean },
264   { "savecookies",      &opt.cookies_output,    cmd_file },
265   { "saveheaders",      &opt.save_headers,      cmd_boolean },
266 #ifdef HAVE_SSL
267   { "secureprotocol",   &opt.secure_protocol,   cmd_spec_secure_protocol },
268 #endif
269   { "serverresponse",   &opt.server_response,   cmd_boolean },
270   { "showalldnsentries", &opt.show_all_dns_entries, cmd_boolean },
271   { "spanhosts",        &opt.spanhost,          cmd_boolean },
272   { "spider",           &opt.spider,            cmd_boolean },
273   { "strictcomments",   &opt.strict_comments,   cmd_boolean },
274   { "timeout",          NULL,                   cmd_spec_timeout },
275   { "timestamping",     &opt.timestamping,      cmd_boolean },
276   { "tries",            &opt.ntry,              cmd_number_inf },
277   { "trustservernames", &opt.trustservernames,  cmd_boolean },
278   { "unlink",           &opt.unlink,            cmd_boolean },
279   { "useproxy",         &opt.use_proxy,         cmd_boolean },
280   { "user",             &opt.user,              cmd_string },
281   { "useragent",        NULL,                   cmd_spec_useragent },
282   { "useservertimestamps", &opt.useservertimestamps, cmd_boolean },
283   { "verbose",          NULL,                   cmd_spec_verbose },
284   { "wait",             &opt.wait,              cmd_time },
285   { "waitretry",        &opt.waitretry,         cmd_time },
286   { "warccdx",          &opt.warc_cdx_enabled,  cmd_boolean },
287   { "warccdxdedup",     &opt.warc_cdx_dedup_filename,  cmd_file },
288 #ifdef HAVE_LIBZ
289   { "warccompression",  &opt.warc_compression_enabled, cmd_boolean },
290 #endif
291   { "warcdigests",      &opt.warc_digests_enabled, cmd_boolean },
292   { "warcfile",         &opt.warc_filename,     cmd_file },
293   { "warcheader",       NULL,                   cmd_spec_warc_header },
294   { "warckeeplog",      &opt.warc_keep_log,     cmd_boolean },
295   { "warcmaxsize",      &opt.warc_maxsize,      cmd_bytes },
296   { "warctempdir",      &opt.warc_tempdir,      cmd_directory },
297 #ifdef USE_WATT32
298   { "wdebug",           &opt.wdebug,            cmd_boolean },
299 #endif
300 };
301
302 /* Look up CMDNAME in the commands[] and return its position in the
303    array.  If CMDNAME is not found, return -1.  */
304
305 static int
306 command_by_name (const char *cmdname)
307 {
308   /* Use binary search for speed.  Wget has ~100 commands, which
309      guarantees a worst case performance of 7 string comparisons.  */
310   int lo = 0, hi = countof (commands) - 1;
311
312   while (lo <= hi)
313     {
314       int mid = (lo + hi) >> 1;
315       int cmp = strcasecmp (cmdname, commands[mid].name);
316       if (cmp < 0)
317         hi = mid - 1;
318       else if (cmp > 0)
319         lo = mid + 1;
320       else
321         return mid;
322     }
323   return -1;
324 }
325 \f
326 /* Reset the variables to default values.  */
327 void
328 defaults (void)
329 {
330   char *tmp;
331
332   /* Most of the default values are 0 (and 0.0, NULL, and false).
333      Just reset everything, and fill in the non-zero values.  Note
334      that initializing pointers to NULL this way is technically
335      illegal, but porting Wget to a machine where NULL is not all-zero
336      bit pattern will be the least of the implementors' worries.  */
337   xzero (opt);
338
339   opt.cookies = true;
340   opt.verbose = -1;
341   opt.ntry = 20;
342   opt.reclevel = 5;
343   opt.add_hostdir = true;
344   opt.netrc = true;
345   opt.ftp_glob = true;
346   opt.htmlify = true;
347   opt.http_keep_alive = true;
348   opt.use_proxy = true;
349   tmp = getenv ("no_proxy");
350   if (tmp)
351     opt.no_proxy = sepstring (tmp);
352   opt.prefer_family = prefer_none;
353   opt.allow_cache = true;
354
355   opt.read_timeout = 900;
356   opt.use_robots = true;
357
358   opt.remove_listing = true;
359
360   opt.dot_bytes = 1024;
361   opt.dot_spacing = 10;
362   opt.dots_in_line = 50;
363
364   opt.dns_cache = true;
365   opt.ftp_pasv = true;
366
367 #ifdef HAVE_SSL
368   opt.check_cert = true;
369 #endif
370
371   /* The default for file name restriction defaults to the OS type. */
372 #if defined(WINDOWS) || defined(MSDOS) || defined(__CYGWIN__)
373   opt.restrict_files_os = restrict_windows;
374 #else
375   opt.restrict_files_os = restrict_unix;
376 #endif
377   opt.restrict_files_ctrl = true;
378   opt.restrict_files_nonascii = false;
379   opt.restrict_files_case = restrict_no_case_restriction;
380
381   opt.regex_type = regex_type_posix;
382
383   opt.max_redirect = 20;
384
385   opt.waitretry = 10;
386
387 #ifdef ENABLE_IRI
388   opt.enable_iri = true;
389 #else
390   opt.enable_iri = false;
391 #endif
392   opt.locale = NULL;
393   opt.encoding_remote = NULL;
394
395   opt.useservertimestamps = true;
396   opt.show_all_dns_entries = false;
397
398   opt.warc_maxsize = 0; /* 1024 * 1024 * 1024; */
399 #ifdef HAVE_LIBZ
400   opt.warc_compression_enabled = true;
401 #else
402   opt.warc_compression_enabled = false;
403 #endif
404   opt.warc_digests_enabled = true;
405   opt.warc_cdx_enabled = false;
406   opt.warc_cdx_dedup_filename = NULL;
407   opt.warc_tempdir = NULL;
408   opt.warc_keep_log = true;
409 }
410 \f
411 /* Return the user's home directory (strdup-ed), or NULL if none is
412    found.  */
413 char *
414 home_dir (void)
415 {
416   static char *buf = NULL;
417   static char *home, *ret;
418
419   if (!home)
420     {
421       home = getenv ("HOME");
422       if (!home)
423         {
424 #if defined(MSDOS)
425           int len;
426
427           /* Under MSDOS, if $HOME isn't defined, use the directory where
428              `wget.exe' resides.  */
429           const char *_w32_get_argv0 (void); /* in libwatt.a/pcconfig.c */
430           char *p;
431
432           buff = _w32_get_argv0 ();
433
434           p = strrchr (buf, '/');            /* djgpp */
435           if (!p)
436             p = strrchr (buf, '\\');          /* others */
437           assert (p);
438
439           len = p - buff + 1;
440           buff = malloc (len + 1);
441           if (buff == NULL)
442             return NULL;
443
444           strncpy (buff, _w32_get_argv0 (), len);
445           buff[len] = '\0';
446
447           home = buf;
448 #elif !defined(WINDOWS)
449           /* If HOME is not defined, try getting it from the password
450              file.  */
451           struct passwd *pwd = getpwuid (getuid ());
452           if (!pwd || !pwd->pw_dir)
453             return NULL;
454           home = pwd->pw_dir;
455 #else  /* !WINDOWS */
456           /* Under Windows, if $HOME isn't defined, use the directory where
457              `wget.exe' resides.  */
458           home = ws_mypath ();
459 #endif /* WINDOWS */
460         }
461     }
462
463   ret = home ? xstrdup (home) : NULL;
464   free (buf);
465
466   return ret;
467 }
468
469 /* Check the 'WGETRC' environment variable and return the file name
470    if  'WGETRC' is set and is a valid file.
471    If the `WGETRC' variable exists but the file does not exist, the
472    function will exit().  */
473 char *
474 wgetrc_env_file_name (void)
475 {
476   char *env = getenv ("WGETRC");
477   if (env && *env)
478     {
479       if (!file_exists_p (env))
480         {
481           fprintf (stderr, _("%s: WGETRC points to %s, which doesn't exist.\n"),
482                    exec_name, env);
483           exit (1);
484         }
485       return xstrdup (env);
486     }
487   return NULL;
488 }
489
490 /* Check for the existance of '$HOME/.wgetrc' and return its path
491    if it exists and is set.  */
492 char *
493 wgetrc_user_file_name (void)
494 {
495   char *home;
496   char *file = NULL;
497   /* If that failed, try $HOME/.wgetrc (or equivalent).  */
498
499 #ifdef __VMS
500   file = "SYS$LOGIN:.wgetrc";
501 #else /* def __VMS */
502   home = home_dir ();
503   if (home)
504     file = aprintf ("%s/.wgetrc", home);
505   xfree_null (home);
506 #endif /* def __VMS [else] */
507
508   if (!file)
509     return NULL;
510   if (!file_exists_p (file))
511     {
512       xfree (file);
513       return NULL;
514     }
515   return file;
516 }
517
518 /* Return the path to the user's .wgetrc.  This is either the value of
519    `WGETRC' environment variable, or `$HOME/.wgetrc'.
520
521    Additionally, for windows, look in the directory where wget.exe
522    resides.  */
523 char *
524 wgetrc_file_name (void)
525 {
526   char *file = wgetrc_env_file_name ();
527   if (file && *file)
528     return file;
529
530   file = wgetrc_user_file_name ();
531
532 #ifdef WINDOWS
533   /* Under Windows, if we still haven't found .wgetrc, look for the file
534      `wget.ini' in the directory where `wget.exe' resides; we do this for
535      backward compatibility with previous versions of Wget.
536      SYSTEM_WGETRC should not be defined under WINDOWS.  */
537   if (!file)
538     {
539       char *home = home_dir ();
540       xfree_null (file);
541       file = NULL;
542       home = ws_mypath ();
543       if (home)
544         {
545           file = aprintf ("%s/wget.ini", home);
546           if (!file_exists_p (file))
547             {
548               xfree (file);
549               file = NULL;
550             }
551           xfree (home);
552         }
553     }
554 #endif /* WINDOWS */
555
556   return file;
557 }
558
559 /* Return values of parse_line. */
560 enum parse_line {
561   line_ok,
562   line_empty,
563   line_syntax_error,
564   line_unknown_command
565 };
566
567 static enum parse_line parse_line (const char *, char **, char **, int *);
568 static bool setval_internal (int, const char *, const char *);
569 static bool setval_internal_tilde (int, const char *, const char *);
570
571 /* Initialize variables from a wgetrc file.  Returns zero (failure) if
572    there were errors in the file.  */
573
574 bool
575 run_wgetrc (const char *file)
576 {
577   FILE *fp;
578   char *line = NULL;
579   size_t bufsize = 0;
580   int ln;
581   int errcnt = 0;
582
583   fp = fopen (file, "r");
584   if (!fp)
585     {
586       fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
587                file, strerror (errno));
588       return true;                      /* not a fatal error */
589     }
590   ln = 1;
591   while (getline (&line, &bufsize, fp) > 0)
592     {
593       char *com = NULL, *val = NULL;
594       int comind;
595
596       /* Parse the line.  */
597       switch (parse_line (line, &com, &val, &comind))
598         {
599         case line_ok:
600           /* If everything is OK, set the value.  */
601           if (!setval_internal_tilde (comind, com, val))
602             {
603               fprintf (stderr, _("%s: Error in %s at line %d.\n"),
604                        exec_name, file, ln);
605               ++errcnt;
606             }
607           break;
608         case line_syntax_error:
609           fprintf (stderr, _("%s: Syntax error in %s at line %d.\n"),
610                    exec_name, file, ln);
611           ++errcnt;
612           break;
613         case line_unknown_command:
614           fprintf (stderr, _("%s: Unknown command %s in %s at line %d.\n"),
615                    exec_name, quote (com), file, ln);
616           ++errcnt;
617           break;
618         case line_empty:
619           break;
620         default:
621           abort ();
622         }
623       xfree_null (com);
624       xfree_null (val);
625       ++ln;
626     }
627   xfree (line);
628   fclose (fp);
629
630   return errcnt == 0;
631 }
632
633 /* Initialize the defaults and run the system wgetrc and user's own
634    wgetrc.  */
635 void
636 initialize (void)
637 {
638   char *file, *env_sysrc;
639   bool ok = true;
640
641   /* Run a non-standard system rc file when the according environment
642      variable has been set. For internal testing purposes only!  */
643   env_sysrc = getenv ("SYSTEM_WGETRC");
644   if (env_sysrc && file_exists_p (env_sysrc))
645     {
646       ok &= run_wgetrc (env_sysrc);
647       /* If there are any problems parsing the system wgetrc file, tell
648          the user and exit */
649       if (! ok)
650         {
651           fprintf (stderr, _("\
652 Parsing system wgetrc file (env SYSTEM_WGETRC) failed.  Please check\n\
653 '%s',\n\
654 or specify a different file using --config.\n"), env_sysrc);
655           exit (2);
656         }
657     }
658   /* Otherwise, if SYSTEM_WGETRC is defined, use it.  */
659 #ifdef SYSTEM_WGETRC
660   else if (file_exists_p (SYSTEM_WGETRC))
661     ok &= run_wgetrc (SYSTEM_WGETRC);
662   /* If there are any problems parsing the system wgetrc file, tell
663      the user and exit */
664   if (! ok)
665     {
666       fprintf (stderr, _("\
667 Parsing system wgetrc file failed.  Please check\n\
668 '%s',\n\
669 or specify a different file using --config.\n"), SYSTEM_WGETRC);
670       exit (2);
671     }
672 #endif
673   /* Override it with your own, if one exists.  */
674   file = wgetrc_file_name ();
675   if (!file)
676     return;
677   /* #### We should canonicalize `file' and SYSTEM_WGETRC with
678      something like realpath() before comparing them with `strcmp'  */
679 #ifdef SYSTEM_WGETRC
680   if (!strcmp (file, SYSTEM_WGETRC))
681     {
682       fprintf (stderr, _("\
683 %s: Warning: Both system and user wgetrc point to %s.\n"),
684                exec_name, quote (file));
685     }
686   else
687 #endif
688     ok &= run_wgetrc (file);
689
690   /* If there were errors processing either `.wgetrc', abort. */
691   if (!ok)
692     exit (2);
693
694   xfree (file);
695   return;
696 }
697
698 /* Remove dashes and underscores from S, modifying S in the
699    process. */
700
701 static void
702 dehyphen (char *s)
703 {
704   char *t = s;                  /* t - tortoise */
705   char *h = s;                  /* h - hare     */
706   while (*h)
707     if (*h == '_' || *h == '-')
708       ++h;
709     else
710       *t++ = *h++;
711   *t = '\0';
712 }
713
714 /* Parse the line pointed by line, with the syntax:
715    <sp>* command <sp>* = <sp>* value <sp>*
716    Uses malloc to allocate space for command and value.
717
718    Returns one of line_ok, line_empty, line_syntax_error, or
719    line_unknown_command.
720
721    In case of line_ok, *COM and *VAL point to freshly allocated
722    strings, and *COMIND points to com's index.  In case of error or
723    empty line, their values are unmodified.  */
724
725 static enum parse_line
726 parse_line (const char *line, char **com, char **val, int *comind)
727 {
728   const char *p;
729   const char *end = line + strlen (line);
730   const char *cmdstart, *cmdend;
731   const char *valstart, *valend;
732
733   char *cmdcopy;
734   int ind;
735
736   /* Skip leading and trailing whitespace.  */
737   while (*line && c_isspace (*line))
738     ++line;
739   while (end > line && c_isspace (end[-1]))
740     --end;
741
742   /* Skip empty lines and comments.  */
743   if (!*line || *line == '#')
744     return line_empty;
745
746   p = line;
747
748   cmdstart = p;
749   while (p < end && (c_isalnum (*p) || *p == '_' || *p == '-'))
750     ++p;
751   cmdend = p;
752
753   /* Skip '=', as well as any space before or after it. */
754   while (p < end && c_isspace (*p))
755     ++p;
756   if (p == end || *p != '=')
757     return line_syntax_error;
758   ++p;
759   while (p < end && c_isspace (*p))
760     ++p;
761
762   valstart = p;
763   valend   = end;
764
765   /* The syntax is valid (even though the command might not be).  Fill
766      in the command name and value.  */
767   *com = strdupdelim (cmdstart, cmdend);
768   *val = strdupdelim (valstart, valend);
769
770   /* The line now known to be syntactically correct.  Check whether
771      the command is valid.  */
772   BOUNDED_TO_ALLOCA (cmdstart, cmdend, cmdcopy);
773   dehyphen (cmdcopy);
774   ind = command_by_name (cmdcopy);
775   if (ind == -1)
776     return line_unknown_command;
777
778   /* Report success to the caller. */
779   *comind = ind;
780   return line_ok;
781 }
782
783 #if defined(WINDOWS) || defined(MSDOS)
784 # define ISSEP(c) ((c) == '/' || (c) == '\\')
785 #else
786 # define ISSEP(c) ((c) == '/')
787 #endif
788
789 /* Run commands[comind].action. */
790
791 static bool
792 setval_internal (int comind, const char *com, const char *val)
793 {
794   assert (0 <= comind && ((size_t) comind) < countof (commands));
795   DEBUGP (("Setting %s (%s) to %s\n", com, commands[comind].name, val));
796   return commands[comind].action (com, val, commands[comind].place);
797 }
798
799 static bool
800 setval_internal_tilde (int comind, const char *com, const char *val)
801 {
802   bool ret;
803   int homelen;
804   char *home;
805   char **pstring;
806   ret = setval_internal (comind, com, val);
807
808   /* We make tilde expansion for cmd_file and cmd_directory */
809   if (((commands[comind].action == cmd_file) ||
810        (commands[comind].action == cmd_directory))
811       && ret && (*val == '~' && ISSEP (val[1])))
812     {
813       pstring = commands[comind].place;
814       home = home_dir ();
815       if (home)
816         {
817           homelen = strlen (home);
818           while (homelen && ISSEP (home[homelen - 1]))
819             home[--homelen] = '\0';
820
821           /* Skip the leading "~/". */
822           for (++val; ISSEP (*val); val++)
823             ;
824           *pstring = concat_strings (home, "/", val, (char *)0);
825         }
826     }
827   return ret;
828 }
829
830 /* Run command COM with value VAL.  If running the command produces an
831    error, report the error and exit.
832
833    This is intended to be called from main() to modify Wget's behavior
834    through command-line switches.  Since COM is hard-coded in main(),
835    it is not canonicalized, and this aborts when COM is not found.
836
837    If COMIND's are exported to init.h, this function will be changed
838    to accept COMIND directly.  */
839
840 void
841 setoptval (const char *com, const char *val, const char *optname)
842 {
843   /* Prepend "--" to OPTNAME. */
844   char *dd_optname = (char *) alloca (2 + strlen (optname) + 1);
845   dd_optname[0] = '-';
846   dd_optname[1] = '-';
847   strcpy (dd_optname + 2, optname);
848
849   assert (val != NULL);
850   if (!setval_internal (command_by_name (com), dd_optname, val))
851     exit (2);
852 }
853
854 /* Parse OPT into command and value and run it.  For example,
855    run_command("foo=bar") is equivalent to setoptval("foo", "bar").
856    This is used by the `--execute' flag in main.c.  */
857
858 void
859 run_command (const char *opt)
860 {
861   char *com, *val;
862   int comind;
863   switch (parse_line (opt, &com, &val, &comind))
864     {
865     case line_ok:
866       if (!setval_internal (comind, com, val))
867         exit (2);
868       xfree (com);
869       xfree (val);
870       break;
871     default:
872       fprintf (stderr, _("%s: Invalid --execute command %s\n"),
873                exec_name, quote (opt));
874       exit (2);
875     }
876 }
877 \f
878 /* Generic helper functions, for use with `commands'. */
879
880 /* Forward declarations: */
881 struct decode_item {
882   const char *name;
883   int code;
884 };
885 static bool decode_string (const char *, const struct decode_item *, int, int *);
886 static bool simple_atoi (const char *, const char *, int *);
887 static bool simple_atof (const char *, const char *, double *);
888
889 #define CMP1(p, c0) (c_tolower((p)[0]) == (c0) && (p)[1] == '\0')
890
891 #define CMP2(p, c0, c1) (c_tolower((p)[0]) == (c0)        \
892                          && c_tolower((p)[1]) == (c1)     \
893                          && (p)[2] == '\0')
894
895 #define CMP3(p, c0, c1, c2) (c_tolower((p)[0]) == (c0)    \
896                      && c_tolower((p)[1]) == (c1)         \
897                      && c_tolower((p)[2]) == (c2)         \
898                      && (p)[3] == '\0')
899
900
901 /* Store the boolean value from VAL to PLACE.  COM is ignored,
902    except for error messages.  */
903 static bool
904 cmd_boolean (const char *com, const char *val, void *place)
905 {
906   bool value;
907
908   if (CMP2 (val, 'o', 'n') || CMP3 (val, 'y', 'e', 's') || CMP1 (val, '1'))
909     /* "on", "yes" and "1" mean true. */
910     value = true;
911   else if (CMP3 (val, 'o', 'f', 'f') || CMP2 (val, 'n', 'o') || CMP1 (val, '0'))
912     /* "off", "no" and "0" mean false. */
913     value = false;
914   else
915     {
916       fprintf (stderr,
917                _("%s: %s: Invalid boolean %s; use `on' or `off'.\n"),
918                exec_name, com, quote (val));
919       return false;
920     }
921
922   *(bool *) place = value;
923   return true;
924 }
925
926 /* Set the non-negative integer value from VAL to PLACE.  With
927    incorrect specification, the number remains unchanged.  */
928 static bool
929 cmd_number (const char *com, const char *val, void *place)
930 {
931   if (!simple_atoi (val, val + strlen (val), place)
932       || *(int *) place < 0)
933     {
934       fprintf (stderr, _("%s: %s: Invalid number %s.\n"),
935                exec_name, com, quote (val));
936       return false;
937     }
938   return true;
939 }
940
941 /* Similar to cmd_number(), only accepts `inf' as a synonym for 0.  */
942 static bool
943 cmd_number_inf (const char *com, const char *val, void *place)
944 {
945   if (!strcasecmp (val, "inf"))
946     {
947       *(int *) place = 0;
948       return true;
949     }
950   return cmd_number (com, val, place);
951 }
952
953 /* Copy (strdup) the string at COM to a new location and place a
954    pointer to *PLACE.  */
955 static bool
956 cmd_string (const char *com, const char *val, void *place)
957 {
958   char **pstring = (char **)place;
959
960   xfree_null (*pstring);
961   *pstring = xstrdup (val);
962   return true;
963 }
964
965 /* Like cmd_string but ensure the string is upper case.  */
966 static bool
967 cmd_string_uppercase (const char *com, const char *val, void *place)
968 {
969   char *q, **pstring;
970   pstring = (char **)place;
971   xfree_null (*pstring);
972
973   *pstring = xmalloc (strlen (val) + 1);
974
975   for (q = *pstring; *val; val++, q++)
976     *q = c_toupper (*val);
977
978   *q = '\0';
979   return true;
980 }
981
982
983 /* Like cmd_string, but handles tilde-expansion when reading a user's
984    `.wgetrc'.  In that case, and if VAL begins with `~', the tilde
985    gets expanded to the user's home directory.  */
986 static bool
987 cmd_file (const char *com, const char *val, void *place)
988 {
989   char **pstring = (char **)place;
990
991   xfree_null (*pstring);
992
993   /* #### If VAL is empty, perhaps should set *PLACE to NULL.  */
994
995   *pstring = xstrdup (val);
996
997 #if defined(WINDOWS) || defined(MSDOS)
998   /* Convert "\" to "/". */
999   {
1000     char *s;
1001     for (s = *pstring; *s; s++)
1002       if (*s == '\\')
1003         *s = '/';
1004   }
1005 #endif
1006   return true;
1007 }
1008
1009 /* Like cmd_file, but strips trailing '/' characters.  */
1010 static bool
1011 cmd_directory (const char *com, const char *val, void *place)
1012 {
1013   char *s, *t;
1014
1015   /* Call cmd_file() for tilde expansion and separator
1016      canonicalization (backslash -> slash under Windows).  These
1017      things should perhaps be in a separate function.  */
1018   if (!cmd_file (com, val, place))
1019     return false;
1020
1021   s = *(char **)place;
1022   t = s + strlen (s);
1023   while (t > s && *--t == '/')
1024     *t = '\0';
1025
1026   return true;
1027 }
1028
1029 /* Split VAL by space to a vector of values, and append those values
1030    to vector pointed to by the PLACE argument.  If VAL is empty, the
1031    PLACE vector is cleared instead.  */
1032
1033 static bool
1034 cmd_vector (const char *com, const char *val, void *place)
1035 {
1036   char ***pvec = (char ***)place;
1037
1038   if (*val)
1039     *pvec = merge_vecs (*pvec, sepstring (val));
1040   else
1041     {
1042       free_vec (*pvec);
1043       *pvec = NULL;
1044     }
1045   return true;
1046 }
1047
1048 static bool
1049 cmd_directory_vector (const char *com, const char *val, void *place)
1050 {
1051   char ***pvec = (char ***)place;
1052
1053   if (*val)
1054     {
1055       /* Strip the trailing slashes from directories.  */
1056       char **t, **seps;
1057
1058       seps = sepstring (val);
1059       for (t = seps; t && *t; t++)
1060         {
1061           int len = strlen (*t);
1062           /* Skip degenerate case of root directory.  */
1063           if (len > 1)
1064             {
1065               if ((*t)[len - 1] == '/')
1066                 (*t)[len - 1] = '\0';
1067             }
1068         }
1069       *pvec = merge_vecs (*pvec, seps);
1070     }
1071   else
1072     {
1073       free_vec (*pvec);
1074       *pvec = NULL;
1075     }
1076   return true;
1077 }
1078
1079 /* Engine for cmd_bytes and cmd_bytes_sum: converts a string such as
1080    "100k" or "2.5G" to a floating point number.  */
1081
1082 static bool
1083 parse_bytes_helper (const char *val, double *result)
1084 {
1085   double number, mult;
1086   const char *end = val + strlen (val);
1087
1088   /* Check for "inf".  */
1089   if (0 == strcmp (val, "inf"))
1090     {
1091       *result = 0;
1092       return true;
1093     }
1094
1095   /* Strip trailing whitespace.  */
1096   while (val < end && c_isspace (end[-1]))
1097     --end;
1098   if (val == end)
1099     return false;
1100
1101   switch (c_tolower (end[-1]))
1102     {
1103     case 'k':
1104       --end, mult = 1024.0;
1105       break;
1106     case 'm':
1107       --end, mult = 1048576.0;
1108       break;
1109     case 'g':
1110       --end, mult = 1073741824.0;
1111       break;
1112     case 't':
1113       --end, mult = 1099511627776.0;
1114       break;
1115     default:
1116       /* Not a recognized suffix: assume it's a digit.  (If not,
1117          simple_atof will raise an error.)  */
1118       mult = 1;
1119     }
1120
1121   /* Skip leading and trailing whitespace. */
1122   while (val < end && c_isspace (*val))
1123     ++val;
1124   while (val < end && c_isspace (end[-1]))
1125     --end;
1126   if (val == end)
1127     return false;
1128
1129   if (!simple_atof (val, end, &number) || number < 0)
1130     return false;
1131
1132   *result = number * mult;
1133   return true;
1134 }
1135
1136 /* Parse VAL as a number and set its value to PLACE (which should
1137    point to a wgint).
1138
1139    By default, the value is assumed to be in bytes.  If "K", "M", or
1140    "G" are appended, the value is multiplied with 1<<10, 1<<20, or
1141    1<<30, respectively.  Floating point values are allowed and are
1142    cast to integer before use.  The idea is to be able to use things
1143    like 1.5k instead of "1536".
1144
1145    The string "inf" is returned as 0.
1146
1147    In case of error, false is returned and memory pointed to by PLACE
1148    remains unmodified.  */
1149
1150 static bool
1151 cmd_bytes (const char *com, const char *val, void *place)
1152 {
1153   double byte_value;
1154   if (!parse_bytes_helper (val, &byte_value))
1155     {
1156       fprintf (stderr, _("%s: %s: Invalid byte value %s\n"),
1157                exec_name, com, quote (val));
1158       return false;
1159     }
1160   *(wgint *)place = (wgint)byte_value;
1161   return true;
1162 }
1163
1164 /* Like cmd_bytes, but PLACE is interpreted as a pointer to
1165    SIZE_SUM.  It works by converting the string to double, therefore
1166    working with values up to 2^53-1 without loss of precision.  This
1167    value (8192 TB) is large enough to serve for a while.  */
1168
1169 static bool
1170 cmd_bytes_sum (const char *com, const char *val, void *place)
1171 {
1172   double byte_value;
1173   if (!parse_bytes_helper (val, &byte_value))
1174     {
1175       fprintf (stderr, _("%s: %s: Invalid byte value %s\n"),
1176                exec_name, com, quote (val));
1177       return false;
1178     }
1179   *(SUM_SIZE_INT *) place = (SUM_SIZE_INT) byte_value;
1180   return true;
1181 }
1182
1183 /* Store the value of VAL to *OUT.  The value is a time period, by
1184    default expressed in seconds, but also accepting suffixes "m", "h",
1185    "d", and "w" for minutes, hours, days, and weeks respectively.  */
1186
1187 static bool
1188 cmd_time (const char *com, const char *val, void *place)
1189 {
1190   double number, mult;
1191   const char *end = val + strlen (val);
1192
1193   /* Strip trailing whitespace.  */
1194   while (val < end && c_isspace (end[-1]))
1195     --end;
1196
1197   if (val == end)
1198     {
1199     err:
1200       fprintf (stderr, _("%s: %s: Invalid time period %s\n"),
1201                exec_name, com, quote (val));
1202       return false;
1203     }
1204
1205   switch (c_tolower (end[-1]))
1206     {
1207     case 's':
1208       --end, mult = 1;          /* seconds */
1209       break;
1210     case 'm':
1211       --end, mult = 60;         /* minutes */
1212       break;
1213     case 'h':
1214       --end, mult = 3600;       /* hours */
1215       break;
1216     case 'd':
1217       --end, mult = 86400.0;    /* days */
1218       break;
1219     case 'w':
1220       --end, mult = 604800.0;   /* weeks */
1221       break;
1222     default:
1223       /* Not a recognized suffix: assume it belongs to the number.
1224          (If not, simple_atof will raise an error.)  */
1225       mult = 1;
1226     }
1227
1228   /* Skip leading and trailing whitespace. */
1229   while (val < end && c_isspace (*val))
1230     ++val;
1231   while (val < end && c_isspace (end[-1]))
1232     --end;
1233   if (val == end)
1234     goto err;
1235
1236   if (!simple_atof (val, end, &number))
1237     goto err;
1238
1239   *(double *)place = number * mult;
1240   return true;
1241 }
1242
1243 #ifdef HAVE_SSL
1244 static bool
1245 cmd_cert_type (const char *com, const char *val, void *place)
1246 {
1247   static const struct decode_item choices[] = {
1248     { "pem", keyfile_pem },
1249     { "der", keyfile_asn1 },
1250     { "asn1", keyfile_asn1 },
1251   };
1252   int ok = decode_string (val, choices, countof (choices), place);
1253   if (!ok)
1254     fprintf (stderr, _("%s: %s: Invalid value %s.\n"), exec_name, com, quote (val));
1255   return ok;
1256 }
1257 #endif
1258 \f
1259 /* Specialized helper functions, used by `commands' to handle some
1260    options specially.  */
1261
1262 static bool check_user_specified_header (const char *);
1263
1264 static bool
1265 cmd_spec_dirstruct (const char *com, const char *val, void *place_ignored)
1266 {
1267   if (!cmd_boolean (com, val, &opt.dirstruct))
1268     return false;
1269   /* Since dirstruct behaviour is explicitly changed, no_dirstruct
1270      must be affected inversely.  */
1271   if (opt.dirstruct)
1272     opt.no_dirstruct = false;
1273   else
1274     opt.no_dirstruct = true;
1275   return true;
1276 }
1277
1278 static bool
1279 cmd_spec_header (const char *com, const char *val, void *place_ignored)
1280 {
1281   /* Empty value means reset the list of headers. */
1282   if (*val == '\0')
1283     {
1284       free_vec (opt.user_headers);
1285       opt.user_headers = NULL;
1286       return true;
1287     }
1288
1289   if (!check_user_specified_header (val))
1290     {
1291       fprintf (stderr, _("%s: %s: Invalid header %s.\n"),
1292                exec_name, com, quote (val));
1293       return false;
1294     }
1295   opt.user_headers = vec_append (opt.user_headers, val);
1296   return true;
1297 }
1298
1299 static bool
1300 cmd_spec_warc_header (const char *com, const char *val, void *place_ignored)
1301 {
1302   /* Empty value means reset the list of headers. */
1303   if (*val == '\0')
1304     {
1305       free_vec (opt.warc_user_headers);
1306       opt.warc_user_headers = NULL;
1307       return true;
1308     }
1309
1310   if (!check_user_specified_header (val))
1311     {
1312       fprintf (stderr, _("%s: %s: Invalid WARC header %s.\n"),
1313                exec_name, com, quote (val));
1314       return false;
1315     }
1316   opt.warc_user_headers = vec_append (opt.warc_user_headers, val);
1317   return true;
1318 }
1319
1320 static bool
1321 cmd_spec_htmlify (const char *com, const char *val, void *place_ignored)
1322 {
1323   int flag = cmd_boolean (com, val, &opt.htmlify);
1324   if (flag && !opt.htmlify)
1325     opt.remove_listing = false;
1326   return flag;
1327 }
1328
1329 /* Set the "mirror" mode.  It means: recursive download, timestamping,
1330    no limit on max. recursion depth, and don't remove listings.  */
1331
1332 static bool
1333 cmd_spec_mirror (const char *com, const char *val, void *place_ignored)
1334 {
1335   int mirror;
1336
1337   if (!cmd_boolean (com, val, &mirror))
1338     return false;
1339   if (mirror)
1340     {
1341       opt.recursive = true;
1342       if (!opt.no_dirstruct)
1343         opt.dirstruct = true;
1344       opt.timestamping = true;
1345       opt.reclevel = INFINITE_RECURSION;
1346       opt.remove_listing = false;
1347     }
1348   return true;
1349 }
1350
1351 /* Validate --prefer-family and set the choice.  Allowed values are
1352    "IPv4", "IPv6", and "none".  */
1353
1354 static bool
1355 cmd_spec_prefer_family (const char *com, const char *val, void *place_ignored)
1356 {
1357   static const struct decode_item choices[] = {
1358     { "IPv4", prefer_ipv4 },
1359     { "IPv6", prefer_ipv6 },
1360     { "none", prefer_none },
1361   };
1362   int prefer_family = prefer_none;
1363   int ok = decode_string (val, choices, countof (choices), &prefer_family);
1364   if (!ok)
1365     fprintf (stderr, _("%s: %s: Invalid value %s.\n"), exec_name, com, quote (val));
1366   opt.prefer_family = prefer_family;
1367   return ok;
1368 }
1369
1370 /* Set progress.type to VAL, but verify that it's a valid progress
1371    implementation before that.  */
1372
1373 static bool
1374 cmd_spec_progress (const char *com, const char *val, void *place_ignored)
1375 {
1376   if (!valid_progress_implementation_p (val))
1377     {
1378       fprintf (stderr, _("%s: %s: Invalid progress type %s.\n"),
1379                exec_name, com, quote (val));
1380       return false;
1381     }
1382   xfree_null (opt.progress_type);
1383
1384   /* Don't call set_progress_implementation here.  It will be called
1385      in main() when it becomes clear what the log output is.  */
1386   opt.progress_type = xstrdup (val);
1387   return true;
1388 }
1389
1390 /* Set opt.recursive to VAL as with cmd_boolean.  If opt.recursive is
1391    set to true, also set opt.dirstruct to true, unless opt.no_dirstruct
1392    is specified.  */
1393
1394 static bool
1395 cmd_spec_recursive (const char *com, const char *val, void *place_ignored)
1396 {
1397   if (!cmd_boolean (com, val, &opt.recursive))
1398     return false;
1399   else
1400     {
1401       if (opt.recursive && !opt.no_dirstruct)
1402         opt.dirstruct = true;
1403     }
1404   return true;
1405 }
1406
1407 /* Validate --regex-type and set the choice.  */
1408
1409 static bool
1410 cmd_spec_regex_type (const char *com, const char *val, void *place_ignored)
1411 {
1412   static const struct decode_item choices[] = {
1413     { "posix", regex_type_posix },
1414 #ifdef HAVE_LIBPCRE
1415     { "pcre",  regex_type_pcre },
1416 #endif
1417   };
1418   int regex_type = regex_type_posix;
1419   int ok = decode_string (val, choices, countof (choices), &regex_type);
1420   if (!ok)
1421     fprintf (stderr, _("%s: %s: Invalid value %s.\n"), exec_name, com, quote (val));
1422   opt.regex_type = regex_type;
1423   return ok;
1424 }
1425
1426 static bool
1427 cmd_spec_restrict_file_names (const char *com, const char *val, void *place_ignored)
1428 {
1429   int restrict_os = opt.restrict_files_os;
1430   int restrict_ctrl = opt.restrict_files_ctrl;
1431   int restrict_case = opt.restrict_files_case;
1432   int restrict_nonascii = opt.restrict_files_nonascii;
1433
1434   const char *end;
1435
1436 #define VAL_IS(string_literal) BOUNDED_EQUAL (val, end, string_literal)
1437
1438   do
1439     {
1440       end = strchr (val, ',');
1441       if (!end)
1442         end = val + strlen (val);
1443
1444       if (VAL_IS ("unix"))
1445         restrict_os = restrict_unix;
1446       else if (VAL_IS ("windows"))
1447         restrict_os = restrict_windows;
1448       else if (VAL_IS ("lowercase"))
1449         restrict_case = restrict_lowercase;
1450       else if (VAL_IS ("uppercase"))
1451         restrict_case = restrict_uppercase;
1452       else if (VAL_IS ("nocontrol"))
1453         restrict_ctrl = false;
1454       else if (VAL_IS ("ascii"))
1455         restrict_nonascii = true;
1456       else
1457         {
1458           fprintf (stderr, _("\
1459 %s: %s: Invalid restriction %s,\n\
1460     use [unix|windows],[lowercase|uppercase],[nocontrol],[ascii].\n"),
1461                    exec_name, com, quote (val));
1462           return false;
1463         }
1464
1465       if (*end)
1466         val = end + 1;
1467     }
1468   while (*val && *end);
1469
1470 #undef VAL_IS
1471
1472   opt.restrict_files_os = restrict_os;
1473   opt.restrict_files_ctrl = restrict_ctrl;
1474   opt.restrict_files_case = restrict_case;
1475   opt.restrict_files_nonascii = restrict_nonascii;
1476
1477   return true;
1478 }
1479
1480 static bool
1481 cmd_spec_report_speed (const char *com, const char *val, void *place_ignored)
1482 {
1483   opt.report_bps = strcasecmp (val, "bits") == 0;
1484   if (!opt.report_bps)
1485     fprintf (stderr, _("%s: %s: Invalid value %s.\n"), exec_name, com, quote (val));
1486   return opt.report_bps;
1487 }
1488
1489 #ifdef HAVE_SSL
1490 static bool
1491 cmd_spec_secure_protocol (const char *com, const char *val, void *place)
1492 {
1493   static const struct decode_item choices[] = {
1494     { "auto", secure_protocol_auto },
1495     { "sslv2", secure_protocol_sslv2 },
1496     { "sslv3", secure_protocol_sslv3 },
1497     { "tlsv1", secure_protocol_tlsv1 },
1498     { "pfs", secure_protocol_pfs },
1499   };
1500   int ok = decode_string (val, choices, countof (choices), place);
1501   if (!ok)
1502     fprintf (stderr, _("%s: %s: Invalid value %s.\n"), exec_name, com, quote (val));
1503   return ok;
1504 }
1505 #endif
1506
1507 /* Set all three timeout values. */
1508
1509 static bool
1510 cmd_spec_timeout (const char *com, const char *val, void *place_ignored)
1511 {
1512   double value;
1513   if (!cmd_time (com, val, &value))
1514     return false;
1515   opt.read_timeout = value;
1516   opt.connect_timeout = value;
1517   opt.dns_timeout = value;
1518   return true;
1519 }
1520
1521 static bool
1522 cmd_spec_useragent (const char *com, const char *val, void *place_ignored)
1523 {
1524   /* Disallow embedded newlines.  */
1525   if (strchr (val, '\n'))
1526     {
1527       fprintf (stderr, _("%s: %s: Invalid value %s.\n"),
1528                exec_name, com, quote (val));
1529       return false;
1530     }
1531   xfree_null (opt.useragent);
1532   opt.useragent = xstrdup (val);
1533   return true;
1534 }
1535
1536 /* The "verbose" option cannot be cmd_boolean because the variable is
1537    not bool -- it's of type int (-1 means uninitialized because of
1538    some random hackery for disallowing -q -v).  */
1539
1540 static bool
1541 cmd_spec_verbose (const char *com, const char *val, void *place_ignored)
1542 {
1543   bool flag;
1544   if (cmd_boolean (com, val, &flag))
1545     {
1546       opt.verbose = flag;
1547       return true;
1548     }
1549   return false;
1550 }
1551 \f
1552 /* Miscellaneous useful routines.  */
1553
1554 /* A very simple atoi clone, more useful than atoi because it works on
1555    delimited strings, and has error reportage.  Returns true on success,
1556    false on failure.  If successful, stores result to *DEST.  */
1557
1558 static bool
1559 simple_atoi (const char *beg, const char *end, int *dest)
1560 {
1561   int result = 0;
1562   bool negative = false;
1563   const char *p = beg;
1564
1565   while (p < end && c_isspace (*p))
1566     ++p;
1567   if (p < end && (*p == '-' || *p == '+'))
1568     {
1569       negative = (*p == '-');
1570       ++p;
1571     }
1572   if (p == end)
1573     return false;
1574
1575   /* Read negative numbers in a separate loop because the most
1576      negative integer cannot be represented as a positive number.  */
1577
1578   if (!negative)
1579     for (; p < end && c_isdigit (*p); p++)
1580       {
1581         int next = (10 * result) + (*p - '0');
1582         if (next < result)
1583           return false;         /* overflow */
1584         result = next;
1585       }
1586   else
1587     for (; p < end && c_isdigit (*p); p++)
1588       {
1589         int next = (10 * result) - (*p - '0');
1590         if (next > result)
1591           return false;         /* underflow */
1592         result = next;
1593       }
1594
1595   if (p != end)
1596     return false;
1597
1598   *dest = result;
1599   return true;
1600 }
1601
1602 /* Trivial atof, with error reporting.  Handles "<digits>[.<digits>]",
1603    doesn't handle exponential notation.  Returns true on success,
1604    false on failure.  In case of success, stores its result to
1605    *DEST.  */
1606
1607 static bool
1608 simple_atof (const char *beg, const char *end, double *dest)
1609 {
1610   double result = 0;
1611
1612   bool negative = false;
1613   bool seen_dot = false;
1614   bool seen_digit = false;
1615   double divider = 1;
1616
1617   const char *p = beg;
1618
1619   while (p < end && c_isspace (*p))
1620     ++p;
1621   if (p < end && (*p == '-' || *p == '+'))
1622     {
1623       negative = (*p == '-');
1624       ++p;
1625     }
1626
1627   for (; p < end; p++)
1628     {
1629       char ch = *p;
1630       if (c_isdigit (ch))
1631         {
1632           if (!seen_dot)
1633             result = (10 * result) + (ch - '0');
1634           else
1635             result += (ch - '0') / (divider *= 10);
1636           seen_digit = true;
1637         }
1638       else if (ch == '.')
1639         {
1640           if (!seen_dot)
1641             seen_dot = true;
1642           else
1643             return false;
1644         }
1645       else
1646         return false;
1647     }
1648   if (!seen_digit)
1649     return false;
1650   if (negative)
1651     result = -result;
1652
1653   *dest = result;
1654   return true;
1655 }
1656
1657 /* Verify that the user-specified header in S is valid.  It must
1658    contain a colon preceded by non-white-space characters and must not
1659    contain newlines.  */
1660
1661 static bool
1662 check_user_specified_header (const char *s)
1663 {
1664   const char *p;
1665
1666   for (p = s; *p && *p != ':' && !c_isspace (*p); p++)
1667     ;
1668   /* The header MUST contain `:' preceded by at least one
1669      non-whitespace character.  */
1670   if (*p != ':' || p == s)
1671     return false;
1672   /* The header MUST NOT contain newlines.  */
1673   if (strchr (s, '\n'))
1674     return false;
1675   return true;
1676 }
1677
1678 /* Decode VAL into a number, according to ITEMS. */
1679
1680 static bool
1681 decode_string (const char *val, const struct decode_item *items, int itemcount,
1682                int *place)
1683 {
1684   int i;
1685   for (i = 0; i < itemcount; i++)
1686     if (0 == strcasecmp (val, items[i].name))
1687       {
1688         *place = items[i].code;
1689         return true;
1690       }
1691   return false;
1692 }
1693
1694 \f
1695 void cleanup_html_url (void);
1696 void spider_cleanup (void);
1697
1698
1699 /* Free the memory allocated by global variables.  */
1700 void
1701 cleanup (void)
1702 {
1703   /* Free external resources, close files, etc. */
1704
1705   /* Close WARC file. */
1706   if (opt.warc_filename != 0)
1707     warc_close ();
1708
1709   log_close ();
1710
1711   if (output_stream)
1712     if (fclose (output_stream) == EOF)
1713       inform_exit_status (CLOSEFAILED);
1714
1715   /* No need to check for error because Wget flushes its output (and
1716      checks for errors) after any data arrives.  */
1717
1718   /* We're exiting anyway so there's no real need to call free()
1719      hundreds of times.  Skipping the frees will make Wget exit
1720      faster.
1721
1722      However, when detecting leaks, it's crucial to free() everything
1723      because then you can find the real leaks, i.e. the allocated
1724      memory which grows with the size of the program.  */
1725
1726 #ifdef DEBUG_MALLOC
1727   convert_cleanup ();
1728   res_cleanup ();
1729   http_cleanup ();
1730   cleanup_html_url ();
1731   spider_cleanup ();
1732   host_cleanup ();
1733   log_cleanup ();
1734
1735   for (i = 0; i < nurl; i++)
1736     xfree (url[i]);
1737
1738   {
1739     extern acc_t *netrc_list;
1740     free_netrc (netrc_list);
1741   }
1742   xfree_null (opt.choose_config);
1743   xfree_null (opt.lfilename);
1744   xfree_null (opt.dir_prefix);
1745   xfree_null (opt.input_filename);
1746   xfree_null (opt.output_document);
1747   free_vec (opt.accepts);
1748   free_vec (opt.rejects);
1749   free_vec (opt.excludes);
1750   free_vec (opt.includes);
1751   free_vec (opt.domains);
1752   free_vec (opt.follow_tags);
1753   free_vec (opt.ignore_tags);
1754   xfree_null (opt.progress_type);
1755   xfree_null (opt.ftp_user);
1756   xfree_null (opt.ftp_passwd);
1757   xfree_null (opt.ftp_proxy);
1758   xfree_null (opt.https_proxy);
1759   xfree_null (opt.http_proxy);
1760   free_vec (opt.no_proxy);
1761   xfree_null (opt.useragent);
1762   xfree_null (opt.referer);
1763   xfree_null (opt.http_user);
1764   xfree_null (opt.http_passwd);
1765   free_vec (opt.user_headers);
1766   free_vec (opt.warc_user_headers);
1767 # ifdef HAVE_SSL
1768   xfree_null (opt.cert_file);
1769   xfree_null (opt.private_key);
1770   xfree_null (opt.ca_directory);
1771   xfree_null (opt.ca_cert);
1772   xfree_null (opt.random_file);
1773   xfree_null (opt.egd_file);
1774 # endif
1775   xfree_null (opt.bind_address);
1776   xfree_null (opt.cookies_input);
1777   xfree_null (opt.cookies_output);
1778   xfree_null (opt.user);
1779   xfree_null (opt.passwd);
1780   xfree_null (opt.base_href);
1781   xfree_null (opt.method);
1782
1783 #endif /* DEBUG_MALLOC */
1784 }
1785 \f
1786 /* Unit testing routines.  */
1787
1788 #ifdef TESTING
1789
1790 const char *
1791 test_commands_sorted()
1792 {
1793   int prev_idx = 0, next_idx = 1;
1794   int command_count = countof (commands) - 1;
1795   int cmp = 0;
1796   while (next_idx <= command_count)
1797     {
1798       cmp = strcasecmp (commands[prev_idx].name, commands[next_idx].name);
1799       if (cmp > 0)
1800         {
1801           mu_assert ("FAILED", false);
1802           break;
1803         }
1804       else
1805         {
1806           prev_idx ++;
1807           next_idx ++;
1808         }
1809     }
1810   return NULL;
1811 }
1812
1813 const char *
1814 test_cmd_spec_restrict_file_names()
1815 {
1816   int i;
1817   struct {
1818     char *val;
1819     int expected_restrict_files_os;
1820     int expected_restrict_files_ctrl;
1821     int expected_restrict_files_case;
1822     bool result;
1823   } test_array[] = {
1824     { "windows", restrict_windows, true, restrict_no_case_restriction, true },
1825     { "windows,", restrict_windows, true, restrict_no_case_restriction, true },
1826     { "windows,lowercase", restrict_windows, true, restrict_lowercase, true },
1827     { "unix,nocontrol,lowercase,", restrict_unix, false, restrict_lowercase, true },
1828   };
1829
1830   for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i)
1831     {
1832       bool res;
1833
1834       defaults();
1835       res = cmd_spec_restrict_file_names ("dummy", test_array[i].val, NULL);
1836
1837       /*
1838       fprintf (stderr, "test_cmd_spec_restrict_file_names: TEST %d\n", i); fflush (stderr);
1839       fprintf (stderr, "opt.restrict_files_os: %d\n",   opt.restrict_files_os); fflush (stderr);
1840       fprintf (stderr, "opt.restrict_files_ctrl: %d\n", opt.restrict_files_ctrl); fflush (stderr);
1841       fprintf (stderr, "opt.restrict_files_case: %d\n", opt.restrict_files_case); fflush (stderr);
1842       */
1843       mu_assert ("test_cmd_spec_restrict_file_names: wrong result",
1844                  res == test_array[i].result
1845                  && opt.restrict_files_os   == test_array[i].expected_restrict_files_os
1846                  && opt.restrict_files_ctrl == test_array[i].expected_restrict_files_ctrl
1847                  && opt.restrict_files_case == test_array[i].expected_restrict_files_case);
1848     }
1849
1850   return NULL;
1851 }
1852
1853 #endif /* TESTING */
1854