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