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