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