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