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