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