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