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