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