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