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