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