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