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