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