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