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