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