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