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