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