]> sjero.net Git - wget/blob - src/init.c
[svn] Make errors in command-line options fatal.
[wget] / src / init.c
1 /* Reading/parsing the initialization file.
2    Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001
3    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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 In addition, as a special exception, the Free Software Foundation
22 gives permission to link the code of its release of Wget with the
23 OpenSSL project's "OpenSSL" library (or with modified versions of it
24 that use the same license as the "OpenSSL" library), and distribute
25 the linked executables.  You must obey the GNU General Public License
26 in all respects for all of the code used other than "OpenSSL".  If you
27 modify this file, you may extend this exception to your version of the
28 file, but you are not obligated to do so.  If you do not wish to do
29 so, delete this exception statement from your version.  */
30
31 #include <config.h>
32
33 #include <stdio.h>
34 #include <sys/types.h>
35 #include <stdlib.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif
39 #ifdef HAVE_STRING_H
40 # include <string.h>
41 #else
42 # include <strings.h>
43 #endif
44 #include <errno.h>
45
46 #ifdef WINDOWS
47 # include <winsock.h>
48 #else
49 # include <sys/socket.h>
50 # include <netinet/in.h>
51 #ifndef __BEOS__
52 # include <arpa/inet.h>
53 #endif
54 #endif
55
56 #ifdef HAVE_PWD_H
57 # include <pwd.h>
58 #endif
59 #include <assert.h>
60
61 #include "wget.h"
62 #include "utils.h"
63 #include "init.h"
64 #include "host.h"
65 #include "recur.h"
66 #include "netrc.h"
67 #include "cookies.h"            /* for cookie_jar_delete */
68 #include "progress.h"
69
70 #ifndef errno
71 extern int errno;
72 #endif
73
74 extern struct cookie_jar *wget_cookie_jar;
75
76 /* We want tilde expansion enabled only when reading `.wgetrc' lines;
77    otherwise, it will be performed by the shell.  This variable will
78    be set by the wgetrc-reading function.  */
79
80 static int enable_tilde_expansion;
81
82
83 #define CMD_DECLARE(func) static int func \
84   PARAMS ((const char *, const char *, void *))
85
86 CMD_DECLARE (cmd_boolean);
87 CMD_DECLARE (cmd_bytes);
88 CMD_DECLARE (cmd_directory_vector);
89 CMD_DECLARE (cmd_lockable_boolean);
90 CMD_DECLARE (cmd_number);
91 CMD_DECLARE (cmd_number_inf);
92 CMD_DECLARE (cmd_string);
93 CMD_DECLARE (cmd_file);
94 CMD_DECLARE (cmd_directory);
95 CMD_DECLARE (cmd_time);
96 CMD_DECLARE (cmd_vector);
97
98 CMD_DECLARE (cmd_spec_dirstruct);
99 CMD_DECLARE (cmd_spec_header);
100 CMD_DECLARE (cmd_spec_htmlify);
101 CMD_DECLARE (cmd_spec_mirror);
102 CMD_DECLARE (cmd_spec_progress);
103 CMD_DECLARE (cmd_spec_recursive);
104 CMD_DECLARE (cmd_spec_restrict_file_names);
105 CMD_DECLARE (cmd_spec_useragent);
106
107 /* List of recognized commands, each consisting of name, closure and function.
108    When adding a new command, simply add it to the list, but be sure to keep the
109    list sorted alphabetically, as findcmd() depends on it.  Also, be sure to add
110    any entries that allocate memory (e.g. cmd_string and cmd_vector guys) to the
111    cleanup() function below. */
112 static struct {
113   char *name;
114   void *closure;
115   int (*action) PARAMS ((const char *, const char *, void *));
116 } commands[] = {
117   { "accept",           &opt.accepts,           cmd_vector },
118   { "addhostdir",       &opt.add_hostdir,       cmd_boolean },
119   { "alwaysrest",       &opt.always_rest,       cmd_boolean }, /* deprecated */
120   { "background",       &opt.background,        cmd_boolean },
121   { "backupconverted",  &opt.backup_converted,  cmd_boolean },
122   { "backups",          &opt.backups,           cmd_number },
123   { "base",             &opt.base_href,         cmd_string },
124   { "bindaddress",      &opt.bind_address,      cmd_string },
125   { "cache",            &opt.allow_cache,       cmd_boolean },
126   { "continue",         &opt.always_rest,       cmd_boolean },
127   { "convertlinks",     &opt.convert_links,     cmd_boolean },
128   { "cookies",          &opt.cookies,           cmd_boolean },
129   { "cutdirs",          &opt.cut_dirs,          cmd_number },
130 #ifdef DEBUG
131   { "debug",            &opt.debug,             cmd_boolean },
132 #endif
133   { "deleteafter",      &opt.delete_after,      cmd_boolean },
134   { "dirprefix",        &opt.dir_prefix,        cmd_directory },
135   { "dirstruct",        NULL,                   cmd_spec_dirstruct },
136   { "dnscache",         &opt.dns_cache,         cmd_boolean },
137   { "domains",          &opt.domains,           cmd_vector },
138   { "dotbytes",         &opt.dot_bytes,         cmd_bytes },
139   { "dotsinline",       &opt.dots_in_line,      cmd_number },
140   { "dotspacing",       &opt.dot_spacing,       cmd_number },
141   { "dotstyle",         &opt.dot_style,         cmd_string },
142 #ifdef HAVE_SSL
143   { "egdfile",          &opt.sslegdsock,        cmd_file },
144 #endif
145   { "excludedirectories", &opt.excludes,        cmd_directory_vector },
146   { "excludedomains",   &opt.exclude_domains,   cmd_vector },
147   { "followftp",        &opt.follow_ftp,        cmd_boolean },
148   { "followtags",       &opt.follow_tags,       cmd_vector },
149   { "forcehtml",        &opt.force_html,        cmd_boolean },
150   { "ftpproxy",         &opt.ftp_proxy,         cmd_string },
151   { "glob",             &opt.ftp_glob,          cmd_boolean },
152   { "header",           NULL,                   cmd_spec_header },
153   { "htmlextension",    &opt.html_extension,    cmd_boolean },
154   { "htmlify",          NULL,                   cmd_spec_htmlify },
155   { "httpkeepalive",    &opt.http_keep_alive,   cmd_boolean },
156   { "httppasswd",       &opt.http_passwd,       cmd_string },
157   { "httpproxy",        &opt.http_proxy,        cmd_string },
158   { "httpsproxy",       &opt.https_proxy,       cmd_string },
159   { "httpuser",         &opt.http_user,         cmd_string },
160   { "ignorelength",     &opt.ignore_length,     cmd_boolean },
161   { "ignoretags",       &opt.ignore_tags,       cmd_vector },
162   { "includedirectories", &opt.includes,        cmd_directory_vector },
163   { "input",            &opt.input_filename,    cmd_file },
164   { "killlonger",       &opt.kill_longer,       cmd_boolean },
165   { "limitrate",        &opt.limit_rate,        cmd_bytes },
166   { "loadcookies",      &opt.cookies_input,     cmd_file },
167   { "logfile",          &opt.lfilename,         cmd_file },
168   { "login",            &opt.ftp_acc,           cmd_string },
169   { "mirror",           NULL,                   cmd_spec_mirror },
170   { "netrc",            &opt.netrc,             cmd_boolean },
171   { "noclobber",        &opt.noclobber,         cmd_boolean },
172   { "noparent",         &opt.no_parent,         cmd_boolean },
173   { "noproxy",          &opt.no_proxy,          cmd_vector },
174   { "numtries",         &opt.ntry,              cmd_number_inf },/* deprecated*/
175   { "outputdocument",   &opt.output_document,   cmd_file },
176   { "pagerequisites",   &opt.page_requisites,   cmd_boolean },
177   { "passiveftp",       &opt.ftp_pasv,          cmd_lockable_boolean },
178   { "passwd",           &opt.ftp_pass,          cmd_string },
179   { "postdata",         &opt.post_data,         cmd_string },
180   { "postfile",         &opt.post_file_name,    cmd_file },
181   { "progress",         &opt.progress_type,     cmd_spec_progress },
182   { "proxypasswd",      &opt.proxy_passwd,      cmd_string },
183   { "proxyuser",        &opt.proxy_user,        cmd_string },
184   { "quiet",            &opt.quiet,             cmd_boolean },
185   { "quota",            &opt.quota,             cmd_bytes },
186   { "randomwait",       &opt.random_wait,       cmd_boolean },
187   { "reclevel",         &opt.reclevel,          cmd_number_inf },
188   { "recursive",        NULL,                   cmd_spec_recursive },
189   { "referer",          &opt.referer,           cmd_string },
190   { "reject",           &opt.rejects,           cmd_vector },
191   { "relativeonly",     &opt.relative_only,     cmd_boolean },
192   { "removelisting",    &opt.remove_listing,    cmd_boolean },
193   { "restrictfilenames", NULL,                  cmd_spec_restrict_file_names },
194   { "retrsymlinks",     &opt.retr_symlinks,     cmd_boolean },
195   { "retryconnrefused", &opt.retry_connrefused, cmd_boolean },
196   { "robots",           &opt.use_robots,        cmd_boolean },
197   { "savecookies",      &opt.cookies_output,    cmd_file },
198   { "saveheaders",      &opt.save_headers,      cmd_boolean },
199   { "serverresponse",   &opt.server_response,   cmd_boolean },
200   { "spanhosts",        &opt.spanhost,          cmd_boolean },
201   { "spider",           &opt.spider,            cmd_boolean },
202 #ifdef HAVE_SSL
203   { "sslcadir",         &opt.sslcadir,          cmd_directory },
204   { "sslcafile",        &opt.sslcafile,         cmd_file },
205   { "sslcertfile",      &opt.sslcertfile,       cmd_file },
206   { "sslcertkey",       &opt.sslcertkey,        cmd_file },
207   { "sslcerttype",      &opt.sslcerttype,       cmd_number },
208   { "sslcheckcert",     &opt.sslcheckcert,      cmd_number },
209   { "sslprotocol",      &opt.sslprotocol,       cmd_number },
210 #endif /* HAVE_SSL */
211   { "strictcomments",   &opt.strict_comments,   cmd_boolean },
212   { "timeout",          &opt.timeout,           cmd_time },
213   { "timestamping",     &opt.timestamping,      cmd_boolean },
214   { "tries",            &opt.ntry,              cmd_number_inf },
215   { "useproxy",         &opt.use_proxy,         cmd_boolean },
216   { "useragent",        NULL,                   cmd_spec_useragent },
217   { "verbose",          &opt.verbose,           cmd_boolean },
218   { "wait",             &opt.wait,              cmd_time },
219   { "waitretry",        &opt.waitretry,         cmd_time }
220 };
221
222 /* Look up COM in the commands[] array and return its index.  If COM
223    is not found, -1 is returned.  This function uses binary search.  */
224
225 static int
226 findcmd (const char *com)
227 {
228   int lo = 0, hi = countof (commands) - 1;
229
230   while (lo <= hi)
231     {
232       int mid = (lo + hi) >> 1;
233       int cmp = strcasecmp (com, commands[mid].name);
234       if (cmp < 0)
235         hi = mid - 1;
236       else if (cmp > 0)
237         lo = mid + 1;
238       else
239         return mid;
240     }
241   return -1;
242 }
243 \f
244 /* Reset the variables to default values.  */
245 static void
246 defaults (void)
247 {
248   char *tmp;
249
250   /* Most of the default values are 0.  Just reset everything, and
251      fill in the non-zero values.  Note that initializing pointers to
252      NULL this way is technically illegal, but porting Wget to a
253      machine where NULL is not all-zero bit pattern will be the least
254      of the implementors' worries.  */
255   memset (&opt, 0, sizeof (opt));
256
257   opt.cookies = 1;
258
259   opt.verbose = -1;
260   opt.ntry = 20;
261   opt.reclevel = 5;
262   opt.add_hostdir = 1;
263   opt.ftp_acc  = xstrdup ("anonymous");
264   opt.ftp_pass = xstrdup ("-wget@");
265   opt.netrc = 1;
266   opt.ftp_glob = 1;
267   opt.htmlify = 1;
268   opt.http_keep_alive = 1;
269   opt.use_proxy = 1;
270   tmp = getenv ("no_proxy");
271   if (tmp)
272     opt.no_proxy = sepstring (tmp);
273   opt.allow_cache = 1;
274
275 #ifdef HAVE_SELECT
276   opt.timeout = 900;
277 #endif
278   opt.use_robots = 1;
279
280   opt.remove_listing = 1;
281
282   opt.dot_bytes = 1024;
283   opt.dot_spacing = 10;
284   opt.dots_in_line = 50;
285
286   opt.dns_cache = 1;
287
288   /* The default for file name restriction defaults to the OS type. */
289 #if !defined(WINDOWS) && !defined(__CYGWIN__)
290   opt.restrict_files_os = restrict_unix;
291 #else
292   opt.restrict_files_os = restrict_windows;
293 #endif
294   opt.restrict_files_ctrl = 1;
295 }
296 \f
297 /* Return the user's home directory (strdup-ed), or NULL if none is
298    found.  */
299 char *
300 home_dir (void)
301 {
302   char *home = getenv ("HOME");
303
304   if (!home)
305     {
306 #ifndef WINDOWS
307       /* If HOME is not defined, try getting it from the password
308          file.  */
309       struct passwd *pwd = getpwuid (getuid ());
310       if (!pwd || !pwd->pw_dir)
311         return NULL;
312       home = pwd->pw_dir;
313 #else  /* WINDOWS */
314       home = "C:\\";
315       /* #### Maybe I should grab home_dir from registry, but the best
316          that I could get from there is user's Start menu.  It sucks!  */
317 #endif /* WINDOWS */
318     }
319
320   return home ? xstrdup (home) : NULL;
321 }
322
323 /* Return the path to the user's .wgetrc.  This is either the value of
324    `WGETRC' environment variable, or `$HOME/.wgetrc'.
325
326    If the `WGETRC' variable exists but the file does not exist, the
327    function will exit().  */
328 static char *
329 wgetrc_file_name (void)
330 {
331   char *env, *home;
332   char *file = NULL;
333
334   /* Try the environment.  */
335   env = getenv ("WGETRC");
336   if (env && *env)
337     {
338       if (!file_exists_p (env))
339         {
340           fprintf (stderr, "%s: %s: %s.\n", exec_name, env, strerror (errno));
341           exit (1);
342         }
343       return xstrdup (env);
344     }
345
346 #ifndef WINDOWS
347   /* If that failed, try $HOME/.wgetrc.  */
348   home = home_dir ();
349   if (home)
350     {
351       file = (char *)xmalloc (strlen (home) + 1 + strlen (".wgetrc") + 1);
352       sprintf (file, "%s/.wgetrc", home);
353     }
354   FREE_MAYBE (home);
355 #else  /* WINDOWS */
356   /* Under Windows, "home" is (for the purposes of this function) the
357      directory where `wget.exe' resides, and `wget.ini' will be used
358      as file name.  SYSTEM_WGETRC should not be defined under WINDOWS.
359
360      It is not as trivial as I assumed, because on 95 argv[0] is full
361      path, but on NT you get what you typed in command line.  --dbudor */
362   home = ws_mypath ();
363   if (home)
364     {
365       file = (char *)xmalloc (strlen (home) + strlen ("wget.ini") + 1);
366       sprintf (file, "%swget.ini", home);
367     }
368 #endif /* WINDOWS */
369
370   if (!file)
371     return NULL;
372   if (!file_exists_p (file))
373     {
374       xfree (file);
375       return NULL;
376     }
377   return file;
378 }
379
380 static int parse_line PARAMS ((const char *, char **, char **, int *));
381 static int setval_internal PARAMS ((int, const char *, const char *));
382
383 /* Initialize variables from a wgetrc file.  */
384
385 static void
386 run_wgetrc (const char *file)
387 {
388   FILE *fp;
389   char *line;
390   int ln;
391
392   fp = fopen (file, "rb");
393   if (!fp)
394     {
395       fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
396                file, strerror (errno));
397       return;
398     }
399   enable_tilde_expansion = 1;
400   ln = 1;
401   while ((line = read_whole_line (fp)))
402     {
403       char *com, *val;
404       int comind, status;
405
406       /* Parse the line.  */
407       status = parse_line (line, &com, &val, &comind);
408       xfree (line);
409       /* If everything is OK, set the value.  */
410       if (status == 1)
411         {
412           if (!setval_internal (comind, com, val))
413             fprintf (stderr, _("%s: Error in %s at line %d.\n"), exec_name,
414                      file, ln);
415           xfree (com);
416           xfree (val);
417         }
418       else if (status == 0)
419         fprintf (stderr, _("%s: Error in %s at line %d.\n"), exec_name,
420                  file, ln);
421       ++ln;
422     }
423   enable_tilde_expansion = 0;
424   fclose (fp);
425 }
426
427 /* Initialize the defaults and run the system wgetrc and user's own
428    wgetrc.  */
429 void
430 initialize (void)
431 {
432   char *file;
433
434   /* Load the hard-coded defaults.  */
435   defaults ();
436
437   /* If SYSTEM_WGETRC is defined, use it.  */
438 #ifdef SYSTEM_WGETRC
439   if (file_exists_p (SYSTEM_WGETRC))
440     run_wgetrc (SYSTEM_WGETRC);
441 #endif
442   /* Override it with your own, if one exists.  */
443   file = wgetrc_file_name ();
444   if (!file)
445     return;
446   /* #### We should canonicalize `file' and SYSTEM_WGETRC with
447      something like realpath() before comparing them with `strcmp'  */
448 #ifdef SYSTEM_WGETRC
449   if (!strcmp (file, SYSTEM_WGETRC))
450     {
451       fprintf (stderr, _("\
452 %s: Warning: Both system and user wgetrc point to `%s'.\n"),
453                exec_name, file);
454     }
455   else
456 #endif
457     run_wgetrc (file);
458   xfree (file);
459   return;
460 }
461 \f
462 /* Remove dashes and underscores from S, modifying S in the
463    process. */
464
465 static void
466 dehyphen (char *s)
467 {
468   char *t = s;                  /* t - tortoise */
469   char *h = s;                  /* h - hare     */
470   while (*h)
471     if (*h == '_' || *h == '-')
472       ++h;
473     else
474       *t++ = *h++;
475   *t = '\0';
476 }
477
478 /* Parse the line pointed by line, with the syntax:
479    <sp>* command <sp>* = <sp>* value <newline>
480    Uses malloc to allocate space for command and value.
481    If the line is invalid, data is freed and 0 is returned.
482
483    Return values:
484     1 - success
485     0 - failure
486    -1 - empty */
487
488 static int
489 parse_line (const char *line, char **com, char **val, int *comind)
490 {
491   const char *p;
492   const char *end = line + strlen (line);
493   const char *cmdstart, *cmdend;
494   const char *valstart, *valend;
495
496   char *cmdcopy;
497   int ind;
498
499   /* Skip leading and trailing whitespace.  */
500   while (*line && ISSPACE (*line))
501     ++line;
502   while (end > line && ISSPACE (end[-1]))
503     --end;
504
505   /* Skip empty lines and comments.  */
506   if (!*line || *line == '#')
507     return -1;
508
509   p = line;
510
511   cmdstart = p;
512   while (p < end && (ISALPHA (*p) || *p == '_' || *p == '-'))
513     ++p;
514   cmdend = p;
515
516   /* Skip '=', as well as any space before or after it. */
517   while (p < end && ISSPACE (*p))
518     ++p;
519   if (p == end || *p != '=')
520     return 0;
521   ++p;
522   while (p < end && ISSPACE (*p))
523     ++p;
524
525   valstart = p;
526   valend   = end;
527
528   /* The line now known to be syntactically correct.  Check whether
529      the command is valid.  */
530   BOUNDED_TO_ALLOCA (cmdstart, cmdend, cmdcopy);
531   dehyphen (cmdcopy);
532   ind = findcmd (cmdcopy);
533   if (ind == -1)
534     return 0;
535
536   /* The command is valid.  Now fill in the values and report success
537      to the caller.  */
538   *comind = ind;
539   *com = strdupdelim (cmdstart, cmdend);
540   *val = strdupdelim (valstart, valend);
541   return 1;
542 }
543
544 /* Run commands[comind].action. */
545
546 static int
547 setval_internal (int comind, const char *com, const char *val)
548 {
549   assert (0 <= comind && comind < countof (commands));
550   return ((*commands[comind].action) (com, val, commands[comind].closure));
551 }
552
553 /* Run command COM with value VAL.  If running the command produces an
554    error, report the error and exit.
555
556    This is intended to be called from main() with commands not
557    provided by the user, therefore it aborts when an unknown command
558    is encountered.  Once the COMIND's are exported to init.h, this
559    function will be changed to accept COMIND directly.  */
560
561 void
562 setoptval (const char *com, const char *val)
563 {
564   int comind = findcmd (com);
565   assert (comind != -1);
566   if (!setval_internal (comind, com, val))
567     exit (2);
568 }
569
570 void
571 run_command (const char *opt)
572 {
573   char *com, *val;
574   int comind;
575   int status = parse_line (opt, &com, &val, &comind);
576   if (status == 1)
577     {
578       if (!setval_internal (comind, com, val))
579         exit (2);
580       xfree (com);
581       xfree (val);
582     }
583   else if (status == 0)
584     {
585       fprintf (stderr, "Invalid command `%s'\n", opt);
586       exit (2);
587     }
588 }
589 \f
590 /* Generic helper functions, for use with `commands'. */
591
592 static int myatoi PARAMS ((const char *s));
593
594 /* Store the boolean value from VAL to CLOSURE.  COM is ignored,
595    except for error messages.  */
596 static int
597 cmd_boolean (const char *com, const char *val, void *closure)
598 {
599   int bool_value;
600   const char *v = val;
601 #define LC(x) TOLOWER(x)
602
603   if ((LC(v[0]) == 'o' && LC(v[1]) == 'n' && !v[2])
604       ||
605       (LC(v[0]) == 'y' && LC(v[1]) == 'e' && LC(v[2]) == 's' && !v[3])
606       ||
607       (v[0] == '1' && !v[1]))
608     /* "on", "yes" and "1" mean true. */
609     bool_value = 1;
610   else if ((LC(v[0]) == 'o' && LC(v[1]) == 'f' && LC(v[2]) == 'f' && !v[3])
611            ||
612            (LC(v[0]) == 'n' && LC(v[1]) == 'o' && !v[2])
613            ||
614            (v[0] == '0' && !v[1]))
615     /* "off", "no" and "0" mean false. */
616     bool_value = 0;
617   else
618     {
619       fprintf (stderr, _("%s: %s: Please specify on or off.\n"),
620                exec_name, com);
621       return 0;
622     }
623
624   *(int *)closure = bool_value;
625   return 1;
626 }
627
628 /* Store the lockable_boolean {2, 1, 0, -1} value from VAL to CLOSURE.  COM is
629    ignored, except for error messages.  Values 2 and -1 indicate that once
630    defined, the value may not be changed by successive wgetrc files or
631    command-line arguments.
632
633    Values: 2 - Enable a particular option for good ("always")
634            1 - Enable an option ("on")
635            0 - Disable an option ("off")
636           -1 - Disable an option for good ("never") */
637 static int
638 cmd_lockable_boolean (const char *com, const char *val, void *closure)
639 {
640   int lockable_boolean_value;
641
642   /*
643    * If a config file said "always" or "never", don't allow command line
644    * arguments to override the config file.
645    */
646   if (*(int *)closure == -1 || *(int *)closure == 2)
647     return 1;
648
649   if (!strcasecmp (val, "always") || !strcmp (val, "2"))
650     lockable_boolean_value = 2;
651   else if (!strcasecmp (val, "on")
652            || !strcasecmp (val, "yes")
653            || !strcmp (val, "1"))
654     lockable_boolean_value = 1;
655   else if (!strcasecmp (val, "off")
656            || !strcasecmp (val, "no")
657            || !strcmp (val, "0"))
658     lockable_boolean_value = 0;
659   else if (!strcasecmp (val, "never") || !strcmp (val, "-1"))
660     lockable_boolean_value = -1;
661   else
662     {
663       fprintf (stderr, _("%s: %s: Please specify always, on, off, "
664                          "or never.\n"),
665                exec_name, com);
666       return 0;
667     }
668
669   *(int *)closure = lockable_boolean_value;
670   return 1;
671 }
672
673 /* Set the non-negative integer value from VAL to CLOSURE.  With
674    incorrect specification, the number remains unchanged.  */
675 static int
676 cmd_number (const char *com, const char *val, void *closure)
677 {
678   int num = myatoi (val);
679
680   if (num == -1)
681     {
682       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
683                exec_name, com, val);
684       return 0;
685     }
686   *(int *)closure = num;
687   return 1;
688 }
689
690 /* Similar to cmd_number(), only accepts `inf' as a synonym for 0.  */
691 static int
692 cmd_number_inf (const char *com, const char *val, void *closure)
693 {
694   if (!strcasecmp (val, "inf"))
695     {
696       *(int *)closure = 0;
697       return 1;
698     }
699   return cmd_number (com, val, closure);
700 }
701
702 /* Copy (strdup) the string at COM to a new location and place a
703    pointer to *CLOSURE.  */
704 static int
705 cmd_string (const char *com, const char *val, void *closure)
706 {
707   char **pstring = (char **)closure;
708
709   FREE_MAYBE (*pstring);
710   *pstring = xstrdup (val);
711   return 1;
712 }
713
714 #ifndef WINDOWS
715 # define ISSEP(c) ((c) == '/')
716 #else
717 # define ISSEP(c) ((c) == '/' || (c) == '\\')
718 #endif
719
720 /* Like the above, but handles tilde-expansion when reading a user's
721    `.wgetrc'.  In that case, and if VAL begins with `~', the tilde
722    gets expanded to the user's home directory.  */
723 static int
724 cmd_file (const char *com, const char *val, void *closure)
725 {
726   char **pstring = (char **)closure;
727
728   FREE_MAYBE (*pstring);
729
730   /* #### If VAL is empty, perhaps should set *CLOSURE to NULL.  */
731
732   if (!enable_tilde_expansion || !(*val == '~' && ISSEP (val[1])))
733     {
734     noexpand:
735       *pstring = xstrdup (val);
736     }
737   else
738     {
739       char *result;
740       int homelen;
741       char *home = home_dir ();
742       if (!home)
743         goto noexpand;
744
745       homelen = strlen (home);
746       while (homelen && ISSEP (home[homelen - 1]))
747         home[--homelen] = '\0';
748
749       /* Skip the leading "~/". */
750       for (++val; ISSEP (*val); val++)
751         ;
752
753       result = xmalloc (homelen + 1 + strlen (val) + 1);
754       memcpy (result, home, homelen);
755       result[homelen] = '/';
756       strcpy (result + homelen + 1, val);
757
758       *pstring = result;
759     }
760
761 #ifdef WINDOWS
762   /* Convert "\" to "/". */
763   {
764     char *s;
765     for (s = *pstring; *s; s++)
766       if (*s == '\\')
767         *s = '/';
768   }
769 #endif
770   return 1;
771 }
772
773 /* Like cmd_file, but strips trailing '/' characters.  */
774 static int
775 cmd_directory (const char *com, const char *val, void *closure)
776 {
777   char *s, *t;
778
779   /* Call cmd_file() for tilde expansion and separator
780      canonicalization (backslash -> slash under Windows).  These
781      things should perhaps be in a separate function.  */
782   if (!cmd_file (com, val, closure))
783     return 0;
784
785   s = *(char **)closure;
786   t = s + strlen (s);
787   while (t > s && *--t == '/')
788     *t = '\0';
789
790   return 1;
791 }
792
793 /* Merge the vector (array of strings separated with `,') in COM with
794    the vector (NULL-terminated array of strings) pointed to by
795    CLOSURE.  */
796 static int
797 cmd_vector (const char *com, const char *val, void *closure)
798 {
799   char ***pvec = (char ***)closure;
800
801   if (*val)
802     *pvec = merge_vecs (*pvec, sepstring (val));
803   else
804     {
805       free_vec (*pvec);
806       *pvec = NULL;
807     }
808   return 1;
809 }
810
811 static int
812 cmd_directory_vector (const char *com, const char *val, void *closure)
813 {
814   char ***pvec = (char ***)closure;
815
816   if (*val)
817     {
818       /* Strip the trailing slashes from directories.  */
819       char **t, **seps;
820
821       seps = sepstring (val);
822       for (t = seps; t && *t; t++)
823         {
824           int len = strlen (*t);
825           /* Skip degenerate case of root directory.  */
826           if (len > 1)
827             {
828               if ((*t)[len - 1] == '/')
829                 (*t)[len - 1] = '\0';
830             }
831         }
832       *pvec = merge_vecs (*pvec, seps);
833     }
834   else
835     {
836       free_vec (*pvec);
837       *pvec = NULL;
838     }
839   return 1;
840 }
841
842 /* Set the value stored in VAL to CLOSURE (which should point to a
843    long int), allowing several postfixes, with the following syntax
844    (regexp):
845
846    [0-9]+       -> bytes
847    [0-9]+[kK]   -> bytes * 1024
848    [0-9]+[mM]   -> bytes * 1024 * 1024
849    inf          -> 0
850
851    Anything else is flagged as incorrect, and CLOSURE is unchanged.  */
852 static int
853 cmd_bytes (const char *com, const char *val, void *closure)
854 {
855   long result;
856   long *out = (long *)closure;
857   const char *p;
858
859   result = 0;
860   p = val;
861   /* Check for "inf".  */
862   if (p[0] == 'i' && p[1] == 'n' && p[2] == 'f' && p[3] == '\0')
863     {
864       *out = 0;
865       return 1;
866     }
867   /* Search for digits and construct result.  */
868   for (; *p && ISDIGIT (*p); p++)
869     result = (10 * result) + (*p - '0');
870   /* If no digits were found, or more than one character is following
871      them, bail out.  */
872   if (p == val || (*p != '\0' && *(p + 1) != '\0'))
873     {
874       printf (_("%s: Invalid specification `%s'\n"), com, val);
875       return 0;
876     }
877   /* Search for a designator.  */
878   switch (TOLOWER (*p))
879     {
880     case '\0':
881       /* None */
882       break;
883     case 'k':
884       /* Kilobytes */
885       result *= 1024;
886       break;
887     case 'm':
888       /* Megabytes */
889       result *= (long)1024 * 1024;
890       break;
891     case 'g':
892       /* Gigabytes */
893       result *= (long)1024 * 1024 * 1024;
894       break;
895     default:
896       printf (_("%s: Invalid specification `%s'\n"), com, val);
897       return 0;
898     }
899   *out = result;
900   return 1;
901 }
902
903 /* Store the value of VAL to *OUT.  The value is a time period, by
904    default expressed in seconds, but also accepting suffixes "m", "h",
905    "d", and "w" for minutes, hours, days, and weeks respectively.  */
906
907 static int
908 cmd_time (const char *com, const char *val, void *closure)
909 {
910   double result, mult, divider;
911   int seen_dot, seen_digit;
912
913   const char *p;
914   const char *end = val + strlen (val);
915
916   /* Skip trailing whitespace.  */
917   while (end > val && ISSPACE (end[-1]))
918     --end;
919
920   if (val == end)
921     {
922     err:
923       fprintf (stderr, _("%s: Invalid time specification `%s'\n"), com, val);
924       return 0;
925     }
926
927   switch (TOLOWER (end[-1]))
928     {
929     case 's':
930       --end, mult = 1;          /* seconds */
931       break;
932     case 'm':
933       --end, mult = 60;         /* minutes */
934       break;
935     case 'h':
936       --end, mult = 3600;       /* hours */
937       break;
938     case 'd':
939       --end, mult = 86400;      /* days */
940       break;
941     case 'w':
942       --end, mult = 604800;     /* weeks */
943       break;
944     default:
945       /* Not a recognized suffix: treat it as part of number, and
946          assume seconds. */
947       mult = 1;
948     }
949
950   /* Skip leading and trailing whitespace. */
951   while (val < end && ISSPACE (*val))
952     ++val;
953   while (val > end && ISSPACE (end[-1]))
954     --end;
955   if (val == end)
956     goto err;
957
958   /* Poor man's strtod: */
959   result = 0;
960   seen_dot = seen_digit = 0;
961   divider = 1;
962
963   p = val;
964   while (p < end)
965     {
966       char ch = *p++;
967       if (ISDIGIT (ch))
968         {
969           if (!seen_dot)
970             result = (10 * result) + (ch - '0');
971           else
972             result += (ch - '0') / (divider *= 10);
973           seen_digit = 1;
974         }
975       else if (ch == '.')
976         {
977           if (!seen_dot)
978             seen_dot = 1;
979           else
980             goto err;
981         }
982     }
983   if (!seen_digit)
984     goto err;
985   result *= mult;
986   *(double *)closure = result;
987   return 1;
988 }
989 \f
990 /* Specialized helper functions, used by `commands' to handle some
991    options specially.  */
992
993 static int check_user_specified_header PARAMS ((const char *));
994
995 static int
996 cmd_spec_dirstruct (const char *com, const char *val, void *closure)
997 {
998   if (!cmd_boolean (com, val, &opt.dirstruct))
999     return 0;
1000   /* Since dirstruct behaviour is explicitly changed, no_dirstruct
1001      must be affected inversely.  */
1002   if (opt.dirstruct)
1003     opt.no_dirstruct = 0;
1004   else
1005     opt.no_dirstruct = 1;
1006   return 1;
1007 }
1008
1009 static int
1010 cmd_spec_header (const char *com, const char *val, void *closure)
1011 {
1012   if (!*val)
1013     {
1014       /* Empty header means reset headers.  */
1015       FREE_MAYBE (opt.user_header);
1016       opt.user_header = NULL;
1017     }
1018   else
1019     {
1020       int i;
1021
1022       if (!check_user_specified_header (val))
1023         {
1024           fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
1025                    exec_name, com, val);
1026           return 0;
1027         }
1028       i = opt.user_header ? strlen (opt.user_header) : 0;
1029       opt.user_header = (char *)xrealloc (opt.user_header, i + strlen (val)
1030                                           + 2 + 1);
1031       strcpy (opt.user_header + i, val);
1032       i += strlen (val);
1033       opt.user_header[i++] = '\r';
1034       opt.user_header[i++] = '\n';
1035       opt.user_header[i] = '\0';
1036     }
1037   return 1;
1038 }
1039
1040 static int
1041 cmd_spec_htmlify (const char *com, const char *val, void *closure)
1042 {
1043   int flag = cmd_boolean (com, val, &opt.htmlify);
1044   if (flag && !opt.htmlify)
1045     opt.remove_listing = 0;
1046   return flag;
1047 }
1048
1049 static int
1050 cmd_spec_mirror (const char *com, const char *val, void *closure)
1051 {
1052   int mirror;
1053
1054   if (!cmd_boolean (com, val, &mirror))
1055     return 0;
1056   if (mirror)
1057     {
1058       opt.recursive = 1;
1059       if (!opt.no_dirstruct)
1060         opt.dirstruct = 1;
1061       opt.timestamping = 1;
1062       opt.reclevel = INFINITE_RECURSION;
1063       opt.remove_listing = 0;
1064     }
1065   return 1;
1066 }
1067
1068 static int
1069 cmd_spec_progress (const char *com, const char *val, void *closure)
1070 {
1071   if (!valid_progress_implementation_p (val))
1072     {
1073       fprintf (stderr, _("%s: %s: Invalid progress type `%s'.\n"),
1074                exec_name, com, val);
1075       return 0;
1076     }
1077   FREE_MAYBE (opt.progress_type);
1078
1079   /* Don't call set_progress_implementation here.  It will be called
1080      in main() when it becomes clear what the log output is.  */
1081   opt.progress_type = xstrdup (val);
1082   return 1;
1083 }
1084
1085 static int
1086 cmd_spec_recursive (const char *com, const char *val, void *closure)
1087 {
1088   if (!cmd_boolean (com, val, &opt.recursive))
1089     return 0;
1090   else
1091     {
1092       if (opt.recursive && !opt.no_dirstruct)
1093         opt.dirstruct = 1;
1094     }
1095   return 1;
1096 }
1097
1098 static int
1099 cmd_spec_restrict_file_names (const char *com, const char *val, void *closure)
1100 {
1101   int restrict_os = opt.restrict_files_os;
1102   int restrict_ctrl = opt.restrict_files_ctrl;
1103
1104   const char *end = strchr (val, ',');
1105   if (!end)
1106     end = val + strlen (val);
1107
1108 #define VAL_IS(string_literal) BOUNDED_EQUAL (val, end, string_literal)
1109
1110   if (VAL_IS ("unix"))
1111     restrict_os = restrict_unix;
1112   else if (VAL_IS ("windows"))
1113     restrict_os = restrict_windows;
1114   else if (VAL_IS ("nocontrol"))
1115     restrict_ctrl = 0;
1116   else
1117     {
1118     err:
1119       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
1120                exec_name, com, val);
1121       return 0;
1122     }
1123
1124 #undef VAL_IS
1125
1126   if (*end)
1127     {
1128       if (!strcmp (end + 1, "nocontrol"))
1129         restrict_ctrl = 0;
1130       else
1131         goto err;
1132     }
1133
1134   opt.restrict_files_os = restrict_os;
1135   opt.restrict_files_ctrl = restrict_ctrl;
1136   return 1;
1137 }
1138
1139 static int
1140 cmd_spec_useragent (const char *com, const char *val, void *closure)
1141 {
1142   /* Just check for empty string and newline, so we don't throw total
1143      junk to the server.  */
1144   if (!*val || strchr (val, '\n'))
1145     {
1146       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
1147                exec_name, com, val);
1148       return 0;
1149     }
1150   opt.useragent = xstrdup (val);
1151   return 1;
1152 }
1153 \f
1154 /* Miscellaneous useful routines.  */
1155
1156 /* Return the integer value of a positive integer written in S, or -1
1157    if an error was encountered.  */
1158 static int
1159 myatoi (const char *s)
1160 {
1161   int res;
1162   const char *orig = s;
1163
1164   for (res = 0; *s && ISDIGIT (*s); s++)
1165     res = 10 * res + (*s - '0');
1166   if (*s || orig == s)
1167     return -1;
1168   else
1169     return res;
1170 }
1171
1172 static int
1173 check_user_specified_header (const char *s)
1174 {
1175   const char *p;
1176
1177   for (p = s; *p && *p != ':' && !ISSPACE (*p); p++);
1178   /* The header MUST contain `:' preceded by at least one
1179      non-whitespace character.  */
1180   if (*p != ':' || p == s)
1181     return 0;
1182   /* The header MUST NOT contain newlines.  */
1183   if (strchr (s, '\n'))
1184     return 0;
1185   return 1;
1186 }
1187 \f
1188 void cleanup_html_url PARAMS ((void));
1189 void res_cleanup PARAMS ((void));
1190 void downloaded_files_free PARAMS ((void));
1191 void http_cleanup PARAMS ((void));
1192
1193
1194 /* Free the memory allocated by global variables.  */
1195 void
1196 cleanup (void)
1197 {
1198   /* Free external resources, close files, etc. */
1199
1200   if (opt.dfp)
1201     fclose (opt.dfp);
1202
1203   /* We're exiting anyway so there's no real need to call free()
1204      hundreds of times.  Skipping the frees will make Wget exit
1205      faster.
1206
1207      However, when detecting leaks, it's crucial to free() everything
1208      because then you can find the real leaks, i.e. the allocated
1209      memory which grows with the size of the program.  */
1210
1211 #ifdef DEBUG_MALLOC
1212   recursive_cleanup ();
1213   res_cleanup ();
1214   http_cleanup ();
1215   cleanup_html_url ();
1216   downloaded_files_free ();
1217   host_cleanup ();
1218   cookie_jar_delete (wget_cookie_jar);
1219
1220   {
1221     extern acc_t *netrc_list;
1222     free_netrc (netrc_list);
1223   }
1224   FREE_MAYBE (opt.lfilename);
1225   FREE_MAYBE (opt.dir_prefix);
1226   FREE_MAYBE (opt.input_filename);
1227   FREE_MAYBE (opt.output_document);
1228   free_vec (opt.accepts);
1229   free_vec (opt.rejects);
1230   free_vec (opt.excludes);
1231   free_vec (opt.includes);
1232   free_vec (opt.domains);
1233   free_vec (opt.follow_tags);
1234   free_vec (opt.ignore_tags);
1235   FREE_MAYBE (opt.progress_type);
1236   xfree (opt.ftp_acc);
1237   FREE_MAYBE (opt.ftp_pass);
1238   FREE_MAYBE (opt.ftp_proxy);
1239   FREE_MAYBE (opt.https_proxy);
1240   FREE_MAYBE (opt.http_proxy);
1241   free_vec (opt.no_proxy);
1242   FREE_MAYBE (opt.useragent);
1243   FREE_MAYBE (opt.referer);
1244   FREE_MAYBE (opt.http_user);
1245   FREE_MAYBE (opt.http_passwd);
1246   FREE_MAYBE (opt.user_header);
1247 #ifdef HAVE_SSL
1248   FREE_MAYBE (opt.sslcertkey);
1249   FREE_MAYBE (opt.sslcertfile);
1250 #endif /* HAVE_SSL */
1251   FREE_MAYBE (opt.bind_address);
1252   FREE_MAYBE (opt.cookies_input);
1253   FREE_MAYBE (opt.cookies_output);
1254 #endif
1255 }