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