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