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