]> sjero.net Git - wget/blob - src/init.c
[svn] Warn to keep the option list sorted.
[wget] / src / init.c
1 /* Reading/parsing the initialization file.
2    Copyright (C) 1996-2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables.  You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL".  If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so.  If you do not wish to do
28 so, delete this exception statement from your version.  */
29
30 #include <config.h>
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #ifdef HAVE_UNISTD_H
35 # include <unistd.h>
36 #endif
37 #include <string.h>
38 #include <errno.h>
39
40 #ifdef HAVE_PWD_H
41 # include <pwd.h>
42 #endif
43 #include <assert.h>
44
45 #include "wget.h"
46 #include "utils.h"
47 #include "init.h"
48 #include "host.h"
49 #include "netrc.h"
50 #include "progress.h"
51 #include "recur.h"              /* for INFINITE_RECURSION */
52 #include "convert.h"            /* for convert_cleanup */
53 #include "res.h"                /* for res_cleanup */
54 #include "http.h"               /* for http_cleanup */
55 #include "retr.h"               /* for output_stream */
56
57 #ifdef TESTING
58 #include "test.h"
59 #endif
60
61 /* We want tilde expansion enabled only when reading `.wgetrc' lines;
62    otherwise, it will be performed by the shell.  This variable will
63    be set by the wgetrc-reading function.  */
64
65 static bool enable_tilde_expansion;
66
67
68 #define CMD_DECLARE(func) static bool func (const char *, const char *, void *)
69
70 CMD_DECLARE (cmd_boolean);
71 CMD_DECLARE (cmd_bytes);
72 CMD_DECLARE (cmd_bytes_sum);
73 #ifdef HAVE_SSL
74 CMD_DECLARE (cmd_cert_type);
75 #endif
76 CMD_DECLARE (cmd_directory_vector);
77 CMD_DECLARE (cmd_number);
78 CMD_DECLARE (cmd_number_inf);
79 CMD_DECLARE (cmd_string);
80 CMD_DECLARE (cmd_file);
81 CMD_DECLARE (cmd_directory);
82 CMD_DECLARE (cmd_time);
83 CMD_DECLARE (cmd_vector);
84
85 CMD_DECLARE (cmd_spec_dirstruct);
86 CMD_DECLARE (cmd_spec_header);
87 CMD_DECLARE (cmd_spec_htmlify);
88 CMD_DECLARE (cmd_spec_mirror);
89 CMD_DECLARE (cmd_spec_prefer_family);
90 CMD_DECLARE (cmd_spec_progress);
91 CMD_DECLARE (cmd_spec_recursive);
92 CMD_DECLARE (cmd_spec_restrict_file_names);
93 #ifdef HAVE_SSL
94 CMD_DECLARE (cmd_spec_secure_protocol);
95 #endif
96 CMD_DECLARE (cmd_spec_timeout);
97 CMD_DECLARE (cmd_spec_useragent);
98 CMD_DECLARE (cmd_spec_verbose);
99
100 /* List of recognized commands, each consisting of name, place and
101    function.  When adding a new command, simply add it to the list,
102    but be sure to keep the list sorted alphabetically, as
103    command_by_name's binary search depends on it.  Also, be sure to
104    add any entries that allocate memory (e.g. cmd_string and
105    cmd_vector) to the cleanup() function below. */
106
107 static struct {
108   const char *name;
109   void *place;
110   bool (*action) (const char *, const char *, void *);
111 } commands[] = {
112   /* KEEP THIS LIST ALPHABETICALLY SORTED */
113   { "accept",           &opt.accepts,           cmd_vector },
114   { "addhostdir",       &opt.add_hostdir,       cmd_boolean },
115   { "alwaysrest",       &opt.always_rest,       cmd_boolean }, /* deprecated */
116   { "background",       &opt.background,        cmd_boolean },
117   { "backupconverted",  &opt.backup_converted,  cmd_boolean },
118   { "backups",          &opt.backups,           cmd_number },
119   { "base",             &opt.base_href,         cmd_string },
120   { "bindaddress",      &opt.bind_address,      cmd_string },
121 #ifdef HAVE_SSL
122   { "cacertificate",    &opt.ca_cert,           cmd_file },
123 #endif
124   { "cache",            &opt.allow_cache,       cmd_boolean },
125 #ifdef HAVE_SSL
126   { "cadirectory",      &opt.ca_directory,      cmd_directory },
127   { "certificate",      &opt.cert_file,         cmd_file },
128   { "certificatetype",  &opt.cert_type,         cmd_cert_type },
129   { "checkcertificate", &opt.check_cert,        cmd_boolean },
130 #endif
131   { "connecttimeout",   &opt.connect_timeout,   cmd_time },
132   { "contentdisposition", &opt.content_disposition, cmd_boolean },
133   { "continue",         &opt.always_rest,       cmd_boolean },
134   { "convertlinks",     &opt.convert_links,     cmd_boolean },
135   { "cookies",          &opt.cookies,           cmd_boolean },
136   { "cutdirs",          &opt.cut_dirs,          cmd_number },
137 #ifdef ENABLE_DEBUG
138   { "debug",            &opt.debug,             cmd_boolean },
139 #endif
140   { "deleteafter",      &opt.delete_after,      cmd_boolean },
141   { "dirprefix",        &opt.dir_prefix,        cmd_directory },
142   { "dirstruct",        NULL,                   cmd_spec_dirstruct },
143   { "dnscache",         &opt.dns_cache,         cmd_boolean },
144   { "dnstimeout",       &opt.dns_timeout,       cmd_time },
145   { "domains",          &opt.domains,           cmd_vector },
146   { "dotbytes",         &opt.dot_bytes,         cmd_bytes },
147   { "dotsinline",       &opt.dots_in_line,      cmd_number },
148   { "dotspacing",       &opt.dot_spacing,       cmd_number },
149   { "dotstyle",         &opt.dot_style,         cmd_string },
150 #ifdef HAVE_SSL
151   { "egdfile",          &opt.egd_file,          cmd_file },
152 #endif
153   { "excludedirectories", &opt.excludes,        cmd_directory_vector },
154   { "excludedomains",   &opt.exclude_domains,   cmd_vector },
155   { "followftp",        &opt.follow_ftp,        cmd_boolean },
156   { "followtags",       &opt.follow_tags,       cmd_vector },
157   { "forcehtml",        &opt.force_html,        cmd_boolean },
158   { "ftppasswd",        &opt.ftp_passwd,        cmd_string }, /* deprecated */
159   { "ftppassword",      &opt.ftp_passwd,        cmd_string },
160   { "ftpproxy",         &opt.ftp_proxy,         cmd_string },
161   { "ftpuser",          &opt.ftp_user,          cmd_string },
162   { "glob",             &opt.ftp_glob,          cmd_boolean },
163   { "header",           NULL,                   cmd_spec_header },
164   { "htmlextension",    &opt.html_extension,    cmd_boolean },
165   { "htmlify",          NULL,                   cmd_spec_htmlify },
166   { "httpkeepalive",    &opt.http_keep_alive,   cmd_boolean },
167   { "httppasswd",       &opt.http_passwd,       cmd_string }, /* deprecated */
168   { "httppassword",     &opt.http_passwd,       cmd_string },
169   { "httpproxy",        &opt.http_proxy,        cmd_string },
170   { "httpsproxy",       &opt.https_proxy,       cmd_string },
171   { "httpuser",         &opt.http_user,         cmd_string },
172   { "ignorecase",       &opt.ignore_case,       cmd_boolean },
173   { "ignorelength",     &opt.ignore_length,     cmd_boolean },
174   { "ignoretags",       &opt.ignore_tags,       cmd_vector },
175   { "includedirectories", &opt.includes,        cmd_directory_vector },
176 #ifdef ENABLE_IPV6
177   { "inet4only",        &opt.ipv4_only,         cmd_boolean },
178   { "inet6only",        &opt.ipv6_only,         cmd_boolean },
179 #endif
180   { "input",            &opt.input_filename,    cmd_file },
181   { "keepsessioncookies", &opt.keep_session_cookies, cmd_boolean },
182   { "limitrate",        &opt.limit_rate,        cmd_bytes },
183   { "loadcookies",      &opt.cookies_input,     cmd_file },
184   { "logfile",          &opt.lfilename,         cmd_file },
185   { "login",            &opt.ftp_user,          cmd_string },/* deprecated*/
186   { "mirror",           NULL,                   cmd_spec_mirror },
187   { "netrc",            &opt.netrc,             cmd_boolean },
188   { "noclobber",        &opt.noclobber,         cmd_boolean },
189   { "noparent",         &opt.no_parent,         cmd_boolean },
190   { "noproxy",          &opt.no_proxy,          cmd_vector },
191   { "numtries",         &opt.ntry,              cmd_number_inf },/* deprecated*/
192   { "outputdocument",   &opt.output_document,   cmd_file },
193   { "pagerequisites",   &opt.page_requisites,   cmd_boolean },
194   { "passiveftp",       &opt.ftp_pasv,          cmd_boolean },
195   { "passwd",           &opt.ftp_passwd,        cmd_string },/* deprecated*/
196   { "password",         &opt.passwd,            cmd_string },
197   { "postdata",         &opt.post_data,         cmd_string },
198   { "postfile",         &opt.post_file_name,    cmd_file },
199   { "preferfamily",     NULL,                   cmd_spec_prefer_family },
200   { "preservepermissions", &opt.preserve_perm,  cmd_boolean },
201 #ifdef HAVE_SSL
202   { "privatekey",       &opt.private_key,       cmd_file },
203   { "privatekeytype",   &opt.private_key_type,  cmd_cert_type },
204 #endif
205   { "progress",         &opt.progress_type,     cmd_spec_progress },
206   { "protocoldirectories", &opt.protocol_directories, cmd_boolean },
207   { "proxypasswd",      &opt.proxy_passwd,      cmd_string }, /* deprecated */
208   { "proxypassword",    &opt.proxy_passwd,      cmd_string },
209   { "proxyuser",        &opt.proxy_user,        cmd_string },
210   { "quiet",            &opt.quiet,             cmd_boolean },
211   { "quota",            &opt.quota,             cmd_bytes_sum },
212 #ifdef HAVE_SSL
213   { "randomfile",       &opt.random_file,       cmd_file },
214 #endif
215   { "randomwait",       &opt.random_wait,       cmd_boolean },
216   { "readtimeout",      &opt.read_timeout,      cmd_time },
217   { "reclevel",         &opt.reclevel,          cmd_number_inf },
218   { "recursive",        NULL,                   cmd_spec_recursive },
219   { "referer",          &opt.referer,           cmd_string },
220   { "reject",           &opt.rejects,           cmd_vector },
221   { "relativeonly",     &opt.relative_only,     cmd_boolean },
222   { "removelisting",    &opt.remove_listing,    cmd_boolean },
223   { "restrictfilenames", NULL,                  cmd_spec_restrict_file_names },
224   { "retrsymlinks",     &opt.retr_symlinks,     cmd_boolean },
225   { "retryconnrefused", &opt.retry_connrefused, cmd_boolean },
226   { "robots",           &opt.use_robots,        cmd_boolean },
227   { "savecookies",      &opt.cookies_output,    cmd_file },
228   { "saveheaders",      &opt.save_headers,      cmd_boolean },
229 #ifdef HAVE_SSL
230   { "secureprotocol",   &opt.secure_protocol,   cmd_spec_secure_protocol },
231 #endif
232   { "serverresponse",   &opt.server_response,   cmd_boolean },
233   { "spanhosts",        &opt.spanhost,          cmd_boolean },
234   { "spider",           &opt.spider,            cmd_boolean },
235   { "strictcomments",   &opt.strict_comments,   cmd_boolean },
236   { "timeout",          NULL,                   cmd_spec_timeout },
237   { "timestamping",     &opt.timestamping,      cmd_boolean },
238   { "tries",            &opt.ntry,              cmd_number_inf },
239   { "useproxy",         &opt.use_proxy,         cmd_boolean },
240   { "user",             &opt.user,              cmd_string },
241   { "useragent",        NULL,                   cmd_spec_useragent },
242   { "verbose",          NULL,                   cmd_spec_verbose },
243   { "wait",             &opt.wait,              cmd_time },
244   { "waitretry",        &opt.waitretry,         cmd_time }
245 };
246
247 /* Look up CMDNAME in the commands[] and return its position in the
248    array.  If CMDNAME is not found, return -1.  */
249
250 static int
251 command_by_name (const char *cmdname)
252 {
253   /* Use binary search for speed.  Wget has ~100 commands, which
254      guarantees a worst case performance of 7 string comparisons.  */
255   int lo = 0, hi = countof (commands) - 1;
256
257   while (lo <= hi)
258     {
259       int mid = (lo + hi) >> 1;
260       int cmp = strcasecmp (cmdname, commands[mid].name);
261       if (cmp < 0)
262         hi = mid - 1;
263       else if (cmp > 0)
264         lo = mid + 1;
265       else
266         return mid;
267     }
268   return -1;
269 }
270 \f
271 /* Reset the variables to default values.  */
272 static void
273 defaults (void)
274 {
275   char *tmp;
276
277   /* Most of the default values are 0 (and 0.0, NULL, and false).
278      Just reset everything, and fill in the non-zero values.  Note
279      that initializing pointers to NULL this way is technically
280      illegal, but porting Wget to a machine where NULL is not all-zero
281      bit pattern will be the least of the implementors' worries.  */
282   xzero (opt);
283
284   opt.cookies = true;
285   opt.verbose = -1;
286   opt.ntry = 20;
287   opt.reclevel = 5;
288   opt.add_hostdir = true;
289   opt.netrc = true;
290   opt.ftp_glob = true;
291   opt.htmlify = true;
292   opt.http_keep_alive = true;
293   opt.use_proxy = true;
294   tmp = getenv ("no_proxy");
295   if (tmp)
296     opt.no_proxy = sepstring (tmp);
297   opt.allow_cache = true;
298
299   opt.read_timeout = 900;
300   opt.use_robots = true;
301
302   opt.remove_listing = true;
303
304   opt.dot_bytes = 1024;
305   opt.dot_spacing = 10;
306   opt.dots_in_line = 50;
307
308   opt.dns_cache = true;
309   opt.ftp_pasv = true;
310
311 #ifdef HAVE_SSL
312   opt.check_cert = true;
313 #endif
314
315   /* The default for file name restriction defaults to the OS type. */
316 #if !defined(WINDOWS) && !defined(__CYGWIN__)
317   opt.restrict_files_os = restrict_unix;
318 #else
319   opt.restrict_files_os = restrict_windows;
320 #endif
321   opt.restrict_files_ctrl = true;
322   opt.restrict_files_case = restrict_no_case_restriction;
323
324   opt.content_disposition = true;
325 }
326 \f
327 /* Return the user's home directory (strdup-ed), or NULL if none is
328    found.  */
329 char *
330 home_dir (void)
331 {
332   char *home = getenv ("HOME");
333
334   if (!home)
335     {
336 #ifndef WINDOWS
337       /* If HOME is not defined, try getting it from the password
338          file.  */
339       struct passwd *pwd = getpwuid (getuid ());
340       if (!pwd || !pwd->pw_dir)
341         return NULL;
342       home = pwd->pw_dir;
343 #else  /* WINDOWS */
344       /* Under Windows, if $HOME isn't defined, use the directory where
345          `wget.exe' resides.  */
346       home = ws_mypath ();
347 #endif /* WINDOWS */
348     }
349
350   return home ? xstrdup (home) : NULL;
351 }
352
353 /* Return the path to the user's .wgetrc.  This is either the value of
354    `WGETRC' environment variable, or `$HOME/.wgetrc'.
355
356    If the `WGETRC' variable exists but the file does not exist, the
357    function will exit().  */
358 static char *
359 wgetrc_file_name (void)
360 {
361   char *env, *home;
362   char *file = NULL;
363
364   /* Try the environment.  */
365   env = getenv ("WGETRC");
366   if (env && *env)
367     {
368       if (!file_exists_p (env))
369         {
370           fprintf (stderr, _("%s: WGETRC points to %s, which doesn't exist.\n"),
371                    exec_name, env);
372           exit (1);
373         }
374       return xstrdup (env);
375     }
376
377   /* If that failed, try $HOME/.wgetrc.  */
378   home = home_dir ();
379   if (home)
380     file = aprintf ("%s/.wgetrc", home);
381   xfree_null (home);
382
383 #ifdef WINDOWS
384   /* Under Windows, if we still haven't found .wgetrc, look for the file
385      `wget.ini' in the directory where `wget.exe' resides; we do this for
386      backward compatibility with previous versions of Wget.
387      SYSTEM_WGETRC should not be defined under WINDOWS.  */
388   if (!file || !file_exists_p (file))
389     {
390       xfree_null (file);
391       file = NULL;
392       home = ws_mypath ();
393       if (home)
394         file = aprintf ("%s/wget.ini", home);
395     }
396 #endif /* WINDOWS */
397
398   if (!file)
399     return NULL;
400   if (!file_exists_p (file))
401     {
402       xfree (file);
403       return NULL;
404     }
405   return file;
406 }
407
408 /* Return values of parse_line. */
409 enum parse_line {
410   line_ok,
411   line_empty,
412   line_syntax_error,
413   line_unknown_command
414 };
415
416 static enum parse_line parse_line (const char *, char **, char **, int *);
417 static bool setval_internal (int, const char *, const char *);
418
419 /* Initialize variables from a wgetrc file.  Returns zero (failure) if
420    there were errors in the file.  */
421
422 static bool
423 run_wgetrc (const char *file)
424 {
425   FILE *fp;
426   char *line;
427   int ln;
428   int errcnt = 0;
429
430   fp = fopen (file, "rb");
431   if (!fp)
432     {
433       fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
434                file, strerror (errno));
435       return true;                      /* not a fatal error */
436     }
437   enable_tilde_expansion = true;
438   ln = 1;
439   while ((line = read_whole_line (fp)) != NULL)
440     {
441       char *com = NULL, *val = NULL;
442       int comind;
443
444       /* Parse the line.  */
445       switch (parse_line (line, &com, &val, &comind))
446         {
447         case line_ok:
448           /* If everything is OK, set the value.  */
449           if (!setval_internal (comind, com, val))
450             {
451               fprintf (stderr, _("%s: Error in %s at line %d.\n"),
452                        exec_name, file, ln);
453               ++errcnt;
454             }
455           break;
456         case line_syntax_error:
457           fprintf (stderr, _("%s: Syntax error in %s at line %d.\n"),
458                    exec_name, file, ln);
459           ++errcnt;
460           break;
461         case line_unknown_command:
462           fprintf (stderr, _("%s: Unknown command `%s' in %s at line %d.\n"),
463                    exec_name, com, file, ln);
464           ++errcnt;
465           break;
466         case line_empty:
467           break;
468         default:
469           abort ();
470         }
471       xfree_null (com);
472       xfree_null (val);
473       xfree (line);
474       ++ln;
475     }
476   enable_tilde_expansion = false;
477   fclose (fp);
478
479   return errcnt == 0;
480 }
481
482 /* Initialize the defaults and run the system wgetrc and user's own
483    wgetrc.  */
484 void
485 initialize (void)
486 {
487   char *file;
488   int ok = true;
489
490   /* Load the hard-coded defaults.  */
491   defaults ();
492
493   /* If SYSTEM_WGETRC is defined, use it.  */
494 #ifdef SYSTEM_WGETRC
495   if (file_exists_p (SYSTEM_WGETRC))
496     ok &= run_wgetrc (SYSTEM_WGETRC);
497 #endif
498   /* Override it with your own, if one exists.  */
499   file = wgetrc_file_name ();
500   if (!file)
501     return;
502   /* #### We should canonicalize `file' and SYSTEM_WGETRC with
503      something like realpath() before comparing them with `strcmp'  */
504 #ifdef SYSTEM_WGETRC
505   if (!strcmp (file, SYSTEM_WGETRC))
506     {
507       fprintf (stderr, _("\
508 %s: Warning: Both system and user wgetrc point to `%s'.\n"),
509                exec_name, file);
510     }
511   else
512 #endif
513     ok &= run_wgetrc (file);
514
515   /* If there were errors processing either `.wgetrc', abort. */
516   if (!ok)
517     exit (2);
518
519   xfree (file);
520   return;
521 }
522 \f
523 /* Remove dashes and underscores from S, modifying S in the
524    process. */
525
526 static void
527 dehyphen (char *s)
528 {
529   char *t = s;                  /* t - tortoise */
530   char *h = s;                  /* h - hare     */
531   while (*h)
532     if (*h == '_' || *h == '-')
533       ++h;
534     else
535       *t++ = *h++;
536   *t = '\0';
537 }
538
539 /* Parse the line pointed by line, with the syntax:
540    <sp>* command <sp>* = <sp>* value <sp>*
541    Uses malloc to allocate space for command and value.
542
543    Returns one of line_ok, line_empty, line_syntax_error, or
544    line_unknown_command.
545
546    In case of line_ok, *COM and *VAL point to freshly allocated
547    strings, and *COMIND points to com's index.  In case of error or
548    empty line, their values are unmodified.  */
549
550 static enum parse_line
551 parse_line (const char *line, char **com, char **val, int *comind)
552 {
553   const char *p;
554   const char *end = line + strlen (line);
555   const char *cmdstart, *cmdend;
556   const char *valstart, *valend;
557
558   char *cmdcopy;
559   int ind;
560
561   /* Skip leading and trailing whitespace.  */
562   while (*line && ISSPACE (*line))
563     ++line;
564   while (end > line && ISSPACE (end[-1]))
565     --end;
566
567   /* Skip empty lines and comments.  */
568   if (!*line || *line == '#')
569     return line_empty;
570
571   p = line;
572
573   cmdstart = p;
574   while (p < end && (ISALNUM (*p) || *p == '_' || *p == '-'))
575     ++p;
576   cmdend = p;
577
578   /* Skip '=', as well as any space before or after it. */
579   while (p < end && ISSPACE (*p))
580     ++p;
581   if (p == end || *p != '=')
582     return line_syntax_error;
583   ++p;
584   while (p < end && ISSPACE (*p))
585     ++p;
586
587   valstart = p;
588   valend   = end;
589
590   /* The syntax is valid (even though the command might not be).  Fill
591      in the command name and value.  */
592   *com = strdupdelim (cmdstart, cmdend);
593   *val = strdupdelim (valstart, valend);
594
595   /* The line now known to be syntactically correct.  Check whether
596      the command is valid.  */
597   BOUNDED_TO_ALLOCA (cmdstart, cmdend, cmdcopy);
598   dehyphen (cmdcopy);
599   ind = command_by_name (cmdcopy);
600   if (ind == -1)
601     return line_unknown_command;
602
603   /* Report success to the caller. */
604   *comind = ind;
605   return line_ok;
606 }
607
608 /* Run commands[comind].action. */
609
610 static bool
611 setval_internal (int comind, const char *com, const char *val)
612 {
613   assert (0 <= comind && comind < countof (commands));
614   DEBUGP (("Setting %s (%s) to %s\n", com, commands[comind].name, val));
615   return commands[comind].action (com, val, commands[comind].place);
616 }
617
618 /* Run command COM with value VAL.  If running the command produces an
619    error, report the error and exit.
620
621    This is intended to be called from main() to modify Wget's behavior
622    through command-line switches.  Since COM is hard-coded in main(),
623    it is not canonicalized, and this aborts when COM is not found.
624
625    If COMIND's are exported to init.h, this function will be changed
626    to accept COMIND directly.  */
627
628 void
629 setoptval (const char *com, const char *val, const char *optname)
630 {
631   /* Prepend "--" to OPTNAME. */
632   char *dd_optname = (char *) alloca (2 + strlen (optname) + 1);
633   dd_optname[0] = '-';
634   dd_optname[1] = '-';
635   strcpy (dd_optname + 2, optname);
636
637   assert (val != NULL);
638   if (!setval_internal (command_by_name (com), dd_optname, val))
639     exit (2);
640 }
641
642 /* Parse OPT into command and value and run it.  For example,
643    run_command("foo=bar") is equivalent to setoptval("foo", "bar").
644    This is used by the `--execute' flag in main.c.  */
645
646 void
647 run_command (const char *opt)
648 {
649   char *com, *val;
650   int comind;
651   switch (parse_line (opt, &com, &val, &comind))
652     {
653     case line_ok:
654       if (!setval_internal (comind, com, val))
655         exit (2);
656       xfree (com);
657       xfree (val);
658       break;
659     default:
660       fprintf (stderr, _("%s: Invalid --execute command `%s'\n"),
661                exec_name, opt);
662       exit (2);
663     }
664 }
665 \f
666 /* Generic helper functions, for use with `commands'. */
667
668 /* Forward declarations: */
669 struct decode_item {
670   const char *name;
671   int code;
672 };
673 static bool decode_string (const char *, const struct decode_item *, int, int *);
674 static bool simple_atoi (const char *, const char *, int *);
675 static bool simple_atof (const char *, const char *, double *);
676
677 #define CMP1(p, c0) (TOLOWER((p)[0]) == (c0) && (p)[1] == '\0')
678
679 #define CMP2(p, c0, c1) (TOLOWER((p)[0]) == (c0)        \
680                          && TOLOWER((p)[1]) == (c1)     \
681                          && (p)[2] == '\0')
682
683 #define CMP3(p, c0, c1, c2) (TOLOWER((p)[0]) == (c0)    \
684                      && TOLOWER((p)[1]) == (c1)         \
685                      && TOLOWER((p)[2]) == (c2)         \
686                      && (p)[3] == '\0')
687
688
689 /* Store the boolean value from VAL to PLACE.  COM is ignored,
690    except for error messages.  */
691 static bool
692 cmd_boolean (const char *com, const char *val, void *place)
693 {
694   bool value;
695
696   if (CMP2 (val, 'o', 'n') || CMP3 (val, 'y', 'e', 's') || CMP1 (val, '1'))
697     /* "on", "yes" and "1" mean true. */
698     value = true;
699   else if (CMP3 (val, 'o', 'f', 'f') || CMP2 (val, 'n', 'o') || CMP1 (val, '0'))
700     /* "off", "no" and "0" mean false. */
701     value = false;
702   else
703     {
704       fprintf (stderr,
705                _("%s: %s: Invalid boolean `%s'; use `on' or `off'.\n"),
706                exec_name, com, val);
707       return false;
708     }
709
710   *(bool *) place = value;
711   return true;
712 }
713
714 /* Set the non-negative integer value from VAL to PLACE.  With
715    incorrect specification, the number remains unchanged.  */
716 static bool
717 cmd_number (const char *com, const char *val, void *place)
718 {
719   if (!simple_atoi (val, val + strlen (val), place)
720       || *(int *) place < 0)
721     {
722       fprintf (stderr, _("%s: %s: Invalid number `%s'.\n"),
723                exec_name, com, val);
724       return false;
725     }
726   return true;
727 }
728
729 /* Similar to cmd_number(), only accepts `inf' as a synonym for 0.  */
730 static bool
731 cmd_number_inf (const char *com, const char *val, void *place)
732 {
733   if (!strcasecmp (val, "inf"))
734     {
735       *(int *) place = 0;
736       return true;
737     }
738   return cmd_number (com, val, place);
739 }
740
741 /* Copy (strdup) the string at COM to a new location and place a
742    pointer to *PLACE.  */
743 static bool
744 cmd_string (const char *com, const char *val, void *place)
745 {
746   char **pstring = (char **)place;
747
748   xfree_null (*pstring);
749   *pstring = xstrdup (val);
750   return true;
751 }
752
753 #ifndef WINDOWS
754 # define ISSEP(c) ((c) == '/')
755 #else
756 # define ISSEP(c) ((c) == '/' || (c) == '\\')
757 #endif
758
759 /* Like the above, but handles tilde-expansion when reading a user's
760    `.wgetrc'.  In that case, and if VAL begins with `~', the tilde
761    gets expanded to the user's home directory.  */
762 static bool
763 cmd_file (const char *com, const char *val, void *place)
764 {
765   char **pstring = (char **)place;
766
767   xfree_null (*pstring);
768
769   /* #### If VAL is empty, perhaps should set *PLACE to NULL.  */
770
771   if (!enable_tilde_expansion || !(*val == '~' && ISSEP (val[1])))
772     {
773     noexpand:
774       *pstring = xstrdup (val);
775     }
776   else
777     {
778       int homelen;
779       char *home = home_dir ();
780       if (!home)
781         goto noexpand;
782
783       homelen = strlen (home);
784       while (homelen && ISSEP (home[homelen - 1]))
785         home[--homelen] = '\0';
786
787       /* Skip the leading "~/". */
788       for (++val; ISSEP (*val); val++)
789         ;
790
791       *pstring = concat_strings (home, "/", val, (char *) 0);
792     }
793
794 #ifdef WINDOWS
795   /* Convert "\" to "/". */
796   {
797     char *s;
798     for (s = *pstring; *s; s++)
799       if (*s == '\\')
800         *s = '/';
801   }
802 #endif
803   return true;
804 }
805
806 /* Like cmd_file, but strips trailing '/' characters.  */
807 static bool
808 cmd_directory (const char *com, const char *val, void *place)
809 {
810   char *s, *t;
811
812   /* Call cmd_file() for tilde expansion and separator
813      canonicalization (backslash -> slash under Windows).  These
814      things should perhaps be in a separate function.  */
815   if (!cmd_file (com, val, place))
816     return false;
817
818   s = *(char **)place;
819   t = s + strlen (s);
820   while (t > s && *--t == '/')
821     *t = '\0';
822
823   return true;
824 }
825
826 /* Split VAL by space to a vector of values, and append those values
827    to vector pointed to by the PLACE argument.  If VAL is empty, the
828    PLACE vector is cleared instead.  */
829
830 static bool
831 cmd_vector (const char *com, const char *val, void *place)
832 {
833   char ***pvec = (char ***)place;
834
835   if (*val)
836     *pvec = merge_vecs (*pvec, sepstring (val));
837   else
838     {
839       free_vec (*pvec);
840       *pvec = NULL;
841     }
842   return true;
843 }
844
845 static bool
846 cmd_directory_vector (const char *com, const char *val, void *place)
847 {
848   char ***pvec = (char ***)place;
849
850   if (*val)
851     {
852       /* Strip the trailing slashes from directories.  */
853       char **t, **seps;
854
855       seps = sepstring (val);
856       for (t = seps; t && *t; t++)
857         {
858           int len = strlen (*t);
859           /* Skip degenerate case of root directory.  */
860           if (len > 1)
861             {
862               if ((*t)[len - 1] == '/')
863                 (*t)[len - 1] = '\0';
864             }
865         }
866       *pvec = merge_vecs (*pvec, seps);
867     }
868   else
869     {
870       free_vec (*pvec);
871       *pvec = NULL;
872     }
873   return true;
874 }
875
876 /* Engine for cmd_bytes and cmd_bytes_sum: converts a string such as
877    "100k" or "2.5G" to a floating point number.  */
878
879 static bool
880 parse_bytes_helper (const char *val, double *result)
881 {
882   double number, mult;
883   const char *end = val + strlen (val);
884
885   /* Check for "inf".  */
886   if (0 == strcmp (val, "inf"))
887     {
888       *result = 0;
889       return true;
890     }
891
892   /* Strip trailing whitespace.  */
893   while (val < end && ISSPACE (end[-1]))
894     --end;
895   if (val == end)
896     return false;
897
898   switch (TOLOWER (end[-1]))
899     {
900     case 'k':
901       --end, mult = 1024.0;
902       break;
903     case 'm':
904       --end, mult = 1048576.0;
905       break;
906     case 'g':
907       --end, mult = 1073741824.0;
908       break;
909     case 't':
910       --end, mult = 1099511627776.0;
911       break;
912     default:
913       /* Not a recognized suffix: assume it's a digit.  (If not,
914          simple_atof will raise an error.)  */
915       mult = 1;
916     }
917
918   /* Skip leading and trailing whitespace. */
919   while (val < end && ISSPACE (*val))
920     ++val;
921   while (val < end && ISSPACE (end[-1]))
922     --end;
923   if (val == end)
924     return false;
925
926   if (!simple_atof (val, end, &number) || number < 0)
927     return false;
928
929   *result = number * mult;
930   return true;
931 }
932
933 /* Parse VAL as a number and set its value to PLACE (which should
934    point to a wgint).
935
936    By default, the value is assumed to be in bytes.  If "K", "M", or
937    "G" are appended, the value is multiplied with 1<<10, 1<<20, or
938    1<<30, respectively.  Floating point values are allowed and are
939    cast to integer before use.  The idea is to be able to use things
940    like 1.5k instead of "1536".
941
942    The string "inf" is returned as 0.
943
944    In case of error, false is returned and memory pointed to by PLACE
945    remains unmodified.  */
946
947 static bool
948 cmd_bytes (const char *com, const char *val, void *place)
949 {
950   double byte_value;
951   if (!parse_bytes_helper (val, &byte_value))
952     {
953       fprintf (stderr, _("%s: %s: Invalid byte value `%s'\n"),
954                exec_name, com, val);
955       return false;
956     }
957   *(wgint *)place = (wgint)byte_value;
958   return true;
959 }
960
961 /* Like cmd_bytes, but PLACE is interpreted as a pointer to
962    SIZE_SUM.  It works by converting the string to double, therefore
963    working with values up to 2^53-1 without loss of precision.  This
964    value (8192 TB) is large enough to serve for a while.  */
965
966 static bool
967 cmd_bytes_sum (const char *com, const char *val, void *place)
968 {
969   double byte_value;
970   if (!parse_bytes_helper (val, &byte_value))
971     {
972       fprintf (stderr, _("%s: %s: Invalid byte value `%s'\n"),
973                exec_name, com, val);
974       return false;
975     }
976   *(SUM_SIZE_INT *) place = (SUM_SIZE_INT) byte_value;
977   return true;
978 }
979
980 /* Store the value of VAL to *OUT.  The value is a time period, by
981    default expressed in seconds, but also accepting suffixes "m", "h",
982    "d", and "w" for minutes, hours, days, and weeks respectively.  */
983
984 static bool
985 cmd_time (const char *com, const char *val, void *place)
986 {
987   double number, mult;
988   const char *end = val + strlen (val);
989
990   /* Strip trailing whitespace.  */
991   while (val < end && ISSPACE (end[-1]))
992     --end;
993
994   if (val == end)
995     {
996     err:
997       fprintf (stderr, _("%s: %s: Invalid time period `%s'\n"),
998                exec_name, com, val);
999       return false;
1000     }
1001
1002   switch (TOLOWER (end[-1]))
1003     {
1004     case 's':
1005       --end, mult = 1;          /* seconds */
1006       break;
1007     case 'm':
1008       --end, mult = 60;         /* minutes */
1009       break;
1010     case 'h':
1011       --end, mult = 3600;       /* hours */
1012       break;
1013     case 'd':
1014       --end, mult = 86400.0;    /* days */
1015       break;
1016     case 'w':
1017       --end, mult = 604800.0;   /* weeks */
1018       break;
1019     default:
1020       /* Not a recognized suffix: assume it belongs to the number.
1021          (If not, simple_atof will raise an error.)  */
1022       mult = 1;
1023     }
1024
1025   /* Skip leading and trailing whitespace. */
1026   while (val < end && ISSPACE (*val))
1027     ++val;
1028   while (val < end && ISSPACE (end[-1]))
1029     --end;
1030   if (val == end)
1031     goto err;
1032
1033   if (!simple_atof (val, end, &number))
1034     goto err;
1035
1036   *(double *)place = number * mult;
1037   return true;
1038 }
1039
1040 #ifdef HAVE_SSL
1041 static bool
1042 cmd_cert_type (const char *com, const char *val, void *place)
1043 {
1044   static const struct decode_item choices[] = {
1045     { "pem", keyfile_pem },
1046     { "der", keyfile_asn1 },
1047     { "asn1", keyfile_asn1 },
1048   };
1049   int ok = decode_string (val, choices, countof (choices), place);
1050   if (!ok)
1051     fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val);
1052   return ok;
1053 }
1054 #endif
1055 \f
1056 /* Specialized helper functions, used by `commands' to handle some
1057    options specially.  */
1058
1059 static bool check_user_specified_header (const char *);
1060
1061 static bool
1062 cmd_spec_dirstruct (const char *com, const char *val, void *place_ignored)
1063 {
1064   if (!cmd_boolean (com, val, &opt.dirstruct))
1065     return false;
1066   /* Since dirstruct behaviour is explicitly changed, no_dirstruct
1067      must be affected inversely.  */
1068   if (opt.dirstruct)
1069     opt.no_dirstruct = false;
1070   else
1071     opt.no_dirstruct = true;
1072   return true;
1073 }
1074
1075 static bool
1076 cmd_spec_header (const char *com, const char *val, void *place_ignored)
1077 {
1078   /* Empty value means reset the list of headers. */
1079   if (*val == '\0')
1080     {
1081       free_vec (opt.user_headers);
1082       opt.user_headers = NULL;
1083       return true;
1084     }
1085
1086   if (!check_user_specified_header (val))
1087     {
1088       fprintf (stderr, _("%s: %s: Invalid header `%s'.\n"),
1089                exec_name, com, val);
1090       return false;
1091     }
1092   opt.user_headers = vec_append (opt.user_headers, val);
1093   return true;
1094 }
1095
1096 static bool
1097 cmd_spec_htmlify (const char *com, const char *val, void *place_ignored)
1098 {
1099   int flag = cmd_boolean (com, val, &opt.htmlify);
1100   if (flag && !opt.htmlify)
1101     opt.remove_listing = false;
1102   return flag;
1103 }
1104
1105 /* Set the "mirror" mode.  It means: recursive download, timestamping,
1106    no limit on max. recursion depth, and don't remove listings.  */
1107
1108 static bool
1109 cmd_spec_mirror (const char *com, const char *val, void *place_ignored)
1110 {
1111   int mirror;
1112
1113   if (!cmd_boolean (com, val, &mirror))
1114     return false;
1115   if (mirror)
1116     {
1117       opt.recursive = true;
1118       if (!opt.no_dirstruct)
1119         opt.dirstruct = true;
1120       opt.timestamping = true;
1121       opt.reclevel = INFINITE_RECURSION;
1122       opt.remove_listing = false;
1123     }
1124   return true;
1125 }
1126
1127 /* Validate --prefer-family and set the choice.  Allowed values are
1128    "IPv4", "IPv6", and "none".  */
1129
1130 static bool
1131 cmd_spec_prefer_family (const char *com, const char *val, void *place_ignored)
1132 {
1133   static const struct decode_item choices[] = {
1134     { "IPv4", prefer_ipv4 },
1135     { "IPv6", prefer_ipv6 },
1136     { "none", prefer_none },
1137   };
1138   int ok = decode_string (val, choices, countof (choices),
1139                           (int *) &opt.prefer_family);
1140   if (!ok)
1141     fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val);
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