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