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