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