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