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