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