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