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