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