]> sjero.net Git - wget/blob - src/init.c
[svn] Fix ordering of the command array.
[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 depends on it.  Also, be sure to add any entries
104    that allocate memory (e.g. cmd_string and cmd_vector) to the
105    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   { "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 ok = decode_string (val, choices, countof (choices),
1138                           (int *) &opt.prefer_family);
1139   if (!ok)
1140     fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val);
1141   return ok;
1142 }
1143
1144 /* Set progress.type to VAL, but verify that it's a valid progress
1145    implementation before that.  */
1146
1147 static bool
1148 cmd_spec_progress (const char *com, const char *val, void *place_ignored)
1149 {
1150   if (!valid_progress_implementation_p (val))
1151     {
1152       fprintf (stderr, _("%s: %s: Invalid progress type `%s'.\n"),
1153                exec_name, com, val);
1154       return false;
1155     }
1156   xfree_null (opt.progress_type);
1157
1158   /* Don't call set_progress_implementation here.  It will be called
1159      in main() when it becomes clear what the log output is.  */
1160   opt.progress_type = xstrdup (val);
1161   return true;
1162 }
1163
1164 /* Set opt.recursive to VAL as with cmd_boolean.  If opt.recursive is
1165    set to true, also set opt.dirstruct to true, unless opt.no_dirstruct
1166    is specified.  */
1167
1168 static bool
1169 cmd_spec_recursive (const char *com, const char *val, void *place_ignored)
1170 {
1171   if (!cmd_boolean (com, val, &opt.recursive))
1172     return false;
1173   else
1174     {
1175       if (opt.recursive && !opt.no_dirstruct)
1176         opt.dirstruct = true;
1177     }
1178   return true;
1179 }
1180
1181 static bool
1182 cmd_spec_restrict_file_names (const char *com, const char *val, void *place_ignored)
1183 {
1184   int restrict_os = opt.restrict_files_os;
1185   int restrict_ctrl = opt.restrict_files_ctrl;
1186   int restrict_case = opt.restrict_files_case;
1187
1188   const char *end;
1189
1190 #define VAL_IS(string_literal) BOUNDED_EQUAL (val, end, string_literal)
1191
1192   do
1193     {
1194       end = strchr (val, ',');
1195       if (!end)
1196         end = val + strlen (val);
1197       
1198       if (VAL_IS ("unix"))
1199         restrict_os = restrict_unix;
1200       else if (VAL_IS ("windows"))
1201         restrict_os = restrict_windows;
1202       else if (VAL_IS ("lowercase"))
1203         restrict_case = restrict_lowercase;
1204       else if (VAL_IS ("uppercase"))
1205         restrict_case = restrict_uppercase;
1206       else if (VAL_IS ("nocontrol"))
1207         restrict_ctrl = false;
1208       else
1209         {
1210           fprintf (stderr,
1211                    _("%s: %s: Invalid restriction `%s', use [unix|windows],[lowercase|uppercase],[nocontrol].\n"),
1212                    exec_name, com, val);
1213           return false;
1214         }
1215
1216       if (*end) 
1217         val = end + 1;
1218     }
1219   while (*val && *end);
1220
1221 #undef VAL_IS
1222
1223   opt.restrict_files_os = restrict_os;
1224   opt.restrict_files_ctrl = restrict_ctrl;
1225   opt.restrict_files_case = restrict_case;
1226   
1227   return true;
1228 }
1229
1230 #ifdef HAVE_SSL
1231 static bool
1232 cmd_spec_secure_protocol (const char *com, const char *val, void *place)
1233 {
1234   static const struct decode_item choices[] = {
1235     { "auto", secure_protocol_auto },
1236     { "sslv2", secure_protocol_sslv2 },
1237     { "sslv3", secure_protocol_sslv3 },
1238     { "tlsv1", secure_protocol_tlsv1 },
1239   };
1240   int ok = decode_string (val, choices, countof (choices), place);
1241   if (!ok)
1242     fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val);
1243   return ok;
1244 }
1245 #endif
1246
1247 /* Set all three timeout values. */
1248
1249 static bool
1250 cmd_spec_timeout (const char *com, const char *val, void *place_ignored)
1251 {
1252   double value;
1253   if (!cmd_time (com, val, &value))
1254     return false;
1255   opt.read_timeout = value;
1256   opt.connect_timeout = value;
1257   opt.dns_timeout = value;
1258   return true;
1259 }
1260
1261 static bool
1262 cmd_spec_useragent (const char *com, const char *val, void *place_ignored)
1263 {
1264   /* Disallow embedded newlines.  */
1265   if (strchr (val, '\n'))
1266     {
1267       fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"),
1268                exec_name, com, val);
1269       return false;
1270     }
1271   xfree_null (opt.useragent);
1272   opt.useragent = xstrdup (val);
1273   return true;
1274 }
1275
1276 /* The "verbose" option cannot be cmd_boolean because the variable is
1277    not bool -- it's of type int (-1 means uninitialized because of
1278    some random hackery for disallowing -q -v).  */
1279
1280 static bool
1281 cmd_spec_verbose (const char *com, const char *val, void *place_ignored)
1282 {
1283   bool flag;
1284   if (cmd_boolean (com, val, &flag))
1285     {
1286       opt.verbose = flag;
1287       return true;
1288     }
1289   return false;
1290 }
1291 \f
1292 /* Miscellaneous useful routines.  */
1293
1294 /* A very simple atoi clone, more useful than atoi because it works on
1295    delimited strings, and has error reportage.  Returns true on success,
1296    false on failure.  If successful, stores result to *DEST.  */
1297
1298 static bool
1299 simple_atoi (const char *beg, const char *end, int *dest)
1300 {
1301   int result = 0;
1302   bool negative = false;
1303   const char *p = beg;
1304
1305   while (p < end && ISSPACE (*p))
1306     ++p;
1307   if (p < end && (*p == '-' || *p == '+'))
1308     {
1309       negative = (*p == '-');
1310       ++p;
1311     }
1312   if (p == end)
1313     return false;
1314
1315   /* Read negative numbers in a separate loop because the most
1316      negative integer cannot be represented as a positive number.  */
1317
1318   if (!negative)
1319     for (; p < end && ISDIGIT (*p); p++)
1320       {
1321         int next = (10 * result) + (*p - '0');
1322         if (next < result)
1323           return false;         /* overflow */
1324         result = next;
1325       }
1326   else
1327     for (; p < end && ISDIGIT (*p); p++)
1328       {
1329         int next = (10 * result) - (*p - '0');
1330         if (next > result)
1331           return false;         /* underflow */
1332         result = next;
1333       }
1334
1335   if (p != end)
1336     return false;
1337
1338   *dest = result;
1339   return true;
1340 }
1341
1342 /* Trivial atof, with error reporting.  Handles "<digits>[.<digits>]",
1343    doesn't handle exponential notation.  Returns true on success,
1344    false on failure.  In case of success, stores its result to
1345    *DEST.  */
1346
1347 static bool
1348 simple_atof (const char *beg, const char *end, double *dest)
1349 {
1350   double result = 0;
1351
1352   bool negative = false;
1353   bool seen_dot = false;
1354   bool seen_digit = false;
1355   double divider = 1;
1356
1357   const char *p = beg;
1358
1359   while (p < end && ISSPACE (*p))
1360     ++p;
1361   if (p < end && (*p == '-' || *p == '+'))
1362     {
1363       negative = (*p == '-');
1364       ++p;
1365     }
1366
1367   for (; p < end; p++)
1368     {
1369       char ch = *p;
1370       if (ISDIGIT (ch))
1371         {
1372           if (!seen_dot)
1373             result = (10 * result) + (ch - '0');
1374           else
1375             result += (ch - '0') / (divider *= 10);
1376           seen_digit = true;
1377         }
1378       else if (ch == '.')
1379         {
1380           if (!seen_dot)
1381             seen_dot = true;
1382           else
1383             return false;
1384         }
1385       else
1386         return false;
1387     }
1388   if (!seen_digit)
1389     return false;
1390   if (negative)
1391     result = -result;
1392
1393   *dest = result;
1394   return true;
1395 }
1396
1397 /* Verify that the user-specified header in S is valid.  It must
1398    contain a colon preceded by non-white-space characters and must not
1399    contain newlines.  */
1400
1401 static bool
1402 check_user_specified_header (const char *s)
1403 {
1404   const char *p;
1405
1406   for (p = s; *p && *p != ':' && !ISSPACE (*p); p++)
1407     ;
1408   /* The header MUST contain `:' preceded by at least one
1409      non-whitespace character.  */
1410   if (*p != ':' || p == s)
1411     return false;
1412   /* The header MUST NOT contain newlines.  */
1413   if (strchr (s, '\n'))
1414     return false;
1415   return true;
1416 }
1417
1418 /* Decode VAL into a number, according to ITEMS. */
1419
1420 static bool
1421 decode_string (const char *val, const struct decode_item *items, int itemcount,
1422                int *place)
1423 {
1424   int i;
1425   for (i = 0; i < itemcount; i++)
1426     if (0 == strcasecmp (val, items[i].name))
1427       {
1428         *place = items[i].code;
1429         return true;
1430       }
1431   return false;
1432 }
1433
1434 \f
1435 void cleanup_html_url (void);
1436
1437
1438 /* Free the memory allocated by global variables.  */
1439 void
1440 cleanup (void)
1441 {
1442   /* Free external resources, close files, etc. */
1443
1444   if (output_stream)
1445     fclose (output_stream);
1446   /* No need to check for error because Wget flushes its output (and
1447      checks for errors) after any data arrives.  */
1448
1449   /* We're exiting anyway so there's no real need to call free()
1450      hundreds of times.  Skipping the frees will make Wget exit
1451      faster.
1452
1453      However, when detecting leaks, it's crucial to free() everything
1454      because then you can find the real leaks, i.e. the allocated
1455      memory which grows with the size of the program.  */
1456
1457 #ifdef DEBUG_MALLOC
1458   convert_cleanup ();
1459   res_cleanup ();
1460   http_cleanup ();
1461   cleanup_html_url ();
1462   host_cleanup ();
1463   log_cleanup ();
1464
1465   {
1466     extern acc_t *netrc_list;
1467     free_netrc (netrc_list);
1468   }
1469   xfree_null (opt.lfilename);
1470   xfree_null (opt.dir_prefix);
1471   xfree_null (opt.input_filename);
1472   xfree_null (opt.output_document);
1473   free_vec (opt.accepts);
1474   free_vec (opt.rejects);
1475   free_vec (opt.excludes);
1476   free_vec (opt.includes);
1477   free_vec (opt.domains);
1478   free_vec (opt.follow_tags);
1479   free_vec (opt.ignore_tags);
1480   xfree_null (opt.progress_type);
1481   xfree_null (opt.ftp_user);
1482   xfree_null (opt.ftp_passwd);
1483   xfree_null (opt.ftp_proxy);
1484   xfree_null (opt.https_proxy);
1485   xfree_null (opt.http_proxy);
1486   free_vec (opt.no_proxy);
1487   xfree_null (opt.useragent);
1488   xfree_null (opt.referer);
1489   xfree_null (opt.http_user);
1490   xfree_null (opt.http_passwd);
1491   free_vec (opt.user_headers);
1492 # ifdef HAVE_SSL
1493   xfree_null (opt.cert_file);
1494   xfree_null (opt.private_key);
1495   xfree_null (opt.ca_directory);
1496   xfree_null (opt.ca_cert);
1497   xfree_null (opt.random_file);
1498   xfree_null (opt.egd_file);
1499 # endif
1500   xfree_null (opt.bind_address);
1501   xfree_null (opt.cookies_input);
1502   xfree_null (opt.cookies_output);
1503   xfree_null (opt.user);
1504   xfree_null (opt.passwd);
1505 #endif /* DEBUG_MALLOC */
1506 }
1507 \f
1508 /* Unit testing routines.  */
1509
1510 #ifdef TESTING
1511
1512 const char *
1513 test_cmd_spec_restrict_file_names()
1514 {
1515   int i;
1516   struct {
1517     char *val;
1518     int expected_restrict_files_os;
1519     int expected_restrict_files_ctrl;
1520     int expected_restrict_files_case;
1521     bool result;
1522   } test_array[] = {
1523     { "windows", restrict_windows, true, restrict_no_case_restriction, true },
1524     { "windows,", restrict_windows, true, restrict_no_case_restriction, true },
1525     { "windows,lowercase", restrict_windows, true, restrict_lowercase, true },
1526     { "unix,nocontrol,lowercase,", restrict_unix, false, restrict_lowercase, true },
1527   };
1528   
1529   for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i) 
1530     {
1531       bool res;
1532       
1533       defaults();
1534       res = cmd_spec_restrict_file_names ("dummy", test_array[i].val, NULL);
1535
1536       /*
1537       fprintf (stderr, "test_cmd_spec_restrict_file_names: TEST %d\n", i); fflush (stderr);
1538       fprintf (stderr, "opt.restrict_files_os: %d\n",   opt.restrict_files_os); fflush (stderr);
1539       fprintf (stderr, "opt.restrict_files_ctrl: %d\n", opt.restrict_files_ctrl); fflush (stderr);
1540       fprintf (stderr, "opt.restrict_files_case: %d\n", opt.restrict_files_case); fflush (stderr);
1541       */
1542       mu_assert ("test_cmd_spec_restrict_file_names: wrong result", 
1543                  res == test_array[i].result
1544                  && opt.restrict_files_os   == test_array[i].expected_restrict_files_os 
1545                  && opt.restrict_files_ctrl == test_array[i].expected_restrict_files_ctrl 
1546                  && opt.restrict_files_case == test_array[i].expected_restrict_files_case);
1547     }
1548
1549   return NULL;
1550 }
1551
1552 #endif /* TESTING */
1553