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