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