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