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