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