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