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