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