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