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