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