]> sjero.net Git - wget/blob - src/init.c
[svn] Implement EGD support.
[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   { "sslegdsock",       &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   if (!enable_tilde_expansion || !(*val == '~' && *(val + 1) == '/'))
674     {
675     noexpand:
676       *pstring = xstrdup (val);
677     }
678   else
679     {
680       char *result;
681       int homelen;
682       char *home = home_dir ();
683       if (!home)
684         goto noexpand;
685
686       homelen = strlen (home);
687       while (homelen && home[homelen - 1] == '/')
688         home[--homelen] = '\0';
689
690       /* Skip the leading "~/". */
691       for (++val; *val == '/'; val++)
692         ;
693
694       result = xmalloc (homelen + 1 + strlen (val));
695       memcpy (result, home, homelen);
696       result[homelen] = '/';
697       strcpy (result + homelen + 1, val);
698
699       *pstring = result;
700     }
701   return 1;
702 }
703
704 /* Merge the vector (array of strings separated with `,') in COM with
705    the vector (NULL-terminated array of strings) pointed to by
706    CLOSURE.  */
707 static int
708 cmd_vector (const char *com, const char *val, void *closure)
709 {
710   char ***pvec = (char ***)closure;
711
712   if (*val)
713     *pvec = merge_vecs (*pvec, sepstring (val));
714   else
715     {
716       free_vec (*pvec);
717       *pvec = NULL;
718     }
719   return 1;
720 }
721
722 static int
723 cmd_directory_vector (const char *com, const char *val, void *closure)
724 {
725   char ***pvec = (char ***)closure;
726
727   if (*val)
728     {
729       /* Strip the trailing slashes from directories.  */
730       char **t, **seps;
731
732       seps = sepstring (val);
733       for (t = seps; t && *t; t++)
734         {
735           int len = strlen (*t);
736           /* Skip degenerate case of root directory.  */
737           if (len > 1)
738             {
739               if ((*t)[len - 1] == '/')
740                 (*t)[len - 1] = '\0';
741             }
742         }
743       *pvec = merge_vecs (*pvec, seps);
744     }
745   else
746     {
747       free_vec (*pvec);
748       *pvec = NULL;
749     }
750   return 1;
751 }
752
753 /* Set the value stored in VAL to CLOSURE (which should point to a
754    long int), allowing several postfixes, with the following syntax
755    (regexp):
756
757    [0-9]+       -> bytes
758    [0-9]+[kK]   -> bytes * 1024
759    [0-9]+[mM]   -> bytes * 1024 * 1024
760    inf          -> 0
761
762    Anything else is flagged as incorrect, and CLOSURE is unchanged.  */
763 static int
764 cmd_bytes (const char *com, const char *val, void *closure)
765 {
766   long result;
767   long *out = (long *)closure;
768   const char *p;
769
770   result = 0;
771   p = val;
772   /* Check for "inf".  */
773   if (p[0] == 'i' && p[1] == 'n' && p[2] == 'f' && p[3] == '\0')
774     {
775       *out = 0;
776       return 1;
777     }
778   /* Search for digits and construct result.  */
779   for (; *p && ISDIGIT (*p); p++)
780     result = (10 * result) + (*p - '0');
781   /* If no digits were found, or more than one character is following
782      them, bail out.  */
783   if (p == val || (*p != '\0' && *(p + 1) != '\0'))
784     {
785       printf (_("%s: Invalid specification `%s'\n"), com, val);
786       return 0;
787     }
788   /* Search for a designator.  */
789   switch (TOLOWER (*p))
790     {
791     case '\0':
792       /* None */
793       break;
794     case 'k':
795       /* Kilobytes */
796       result *= 1024;
797       break;
798     case 'm':
799       /* Megabytes */
800       result *= (long)1024 * 1024;
801       break;
802     case 'g':
803       /* Gigabytes */
804       result *= (long)1024 * 1024 * 1024;
805       break;
806     default:
807       printf (_("%s: Invalid specification `%s'\n"), com, val);
808       return 0;
809     }
810   *out = result;
811   return 1;
812 }
813
814 /* Store the value of VAL to *OUT, allowing suffixes for minutes and
815    hours.  */
816 static int
817 cmd_time (const char *com, const char *val, void *closure)
818 {
819   long result = 0;
820   const char *p = val;
821
822   /* Search for digits and construct result.  */
823   for (; *p && ISDIGIT (*p); p++)
824     result = (10 * result) + (*p - '0');
825   /* If no digits were found, or more than one character is following
826      them, bail out.  */
827   if (p == val || (*p != '\0' && *(p + 1) != '\0'))
828     {
829       printf (_("%s: Invalid specification `%s'\n"), com, val);
830       return 0;
831     }
832   /* Search for a suffix.  */
833   switch (TOLOWER (*p))
834     {
835     case '\0':
836       /* None */
837       break;
838     case 'm':
839       /* Minutes */
840       result *= 60;
841       break;
842     case 'h':
843       /* Seconds */
844       result *= 3600;
845       break;
846     case 'd':
847       /* Days (overflow on 16bit machines) */
848       result *= 86400L;
849       break;
850     case 'w':
851       /* Weeks :-) */
852       result *= 604800L;
853       break;
854     default:
855       printf (_("%s: Invalid specification `%s'\n"), com, val);
856       return 0;
857     }
858   *(long *)closure = result;
859   return 1;
860 }
861 \f
862 /* Specialized helper functions, used by `commands' to handle some
863    options specially.  */
864
865 static int check_user_specified_header PARAMS ((const char *));
866
867 static int
868 cmd_spec_dirstruct (const char *com, const char *val, void *closure)
869 {
870   if (!cmd_boolean (com, val, &opt.dirstruct))
871     return 0;
872   /* Since dirstruct behaviour is explicitly changed, no_dirstruct
873      must be affected inversely.  */
874   if (opt.dirstruct)
875     opt.no_dirstruct = 0;
876   else
877     opt.no_dirstruct = 1;
878   return 1;
879 }
880
881 static int
882 cmd_spec_header (const char *com, const char *val, void *closure)
883 {
884   if (!*val)
885     {
886       /* Empty header means reset headers.  */
887       FREE_MAYBE (opt.user_header);
888       opt.user_header = NULL;
889     }
890   else
891     {
892       int i;
893
894       if (!check_user_specified_header (val))
895         {
896           fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
897                    exec_name, com, val);
898           return 0;
899         }
900       i = opt.user_header ? strlen (opt.user_header) : 0;
901       opt.user_header = (char *)xrealloc (opt.user_header, i + strlen (val)
902                                           + 2 + 1);
903       strcpy (opt.user_header + i, val);
904       i += strlen (val);
905       opt.user_header[i++] = '\r';
906       opt.user_header[i++] = '\n';
907       opt.user_header[i] = '\0';
908     }
909   return 1;
910 }
911
912 static int
913 cmd_spec_htmlify (const char *com, const char *val, void *closure)
914 {
915   int flag = cmd_boolean (com, val, &opt.htmlify);
916   if (flag && !opt.htmlify)
917     opt.remove_listing = 0;
918   return flag;
919 }
920
921 static int
922 cmd_spec_mirror (const char *com, const char *val, void *closure)
923 {
924   int mirror;
925
926   if (!cmd_boolean (com, val, &mirror))
927     return 0;
928   if (mirror)
929     {
930       opt.recursive = 1;
931       if (!opt.no_dirstruct)
932         opt.dirstruct = 1;
933       opt.timestamping = 1;
934       opt.reclevel = INFINITE_RECURSION;
935       opt.remove_listing = 0;
936     }
937   return 1;
938 }
939
940 #if 0
941 static int
942 cmd_spec_progress (const char *com, const char *val, void *closure)
943 {
944   if (!valid_progress_implementation_p (val))
945     {
946       fprintf (stderr, _("%s: %s: Invalid progress type `%s'.\n"),
947                exec_name, com, val);
948       return 0;
949     }
950   set_progress_implementation (val);
951   return 1;
952 }
953 #endif
954
955 static int
956 cmd_spec_recursive (const char *com, const char *val, void *closure)
957 {
958   if (!cmd_boolean (com, val, &opt.recursive))
959     return 0;
960   else
961     {
962       if (opt.recursive && !opt.no_dirstruct)
963         opt.dirstruct = 1;
964     }
965   return 1;
966 }
967
968 static int
969 cmd_spec_useragent (const char *com, const char *val, void *closure)
970 {
971   /* Just check for empty string and newline, so we don't throw total
972      junk to the server.  */
973   if (!*val || strchr (val, '\n'))
974     {
975       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
976                exec_name, com, val);
977       return 0;
978     }
979   opt.useragent = xstrdup (val);
980   return 1;
981 }
982 \f
983 /* Miscellaneous useful routines.  */
984
985 /* Return the integer value of a positive integer written in S, or -1
986    if an error was encountered.  */
987 static int
988 myatoi (const char *s)
989 {
990   int res;
991   const char *orig = s;
992
993   for (res = 0; *s && ISDIGIT (*s); s++)
994     res = 10 * res + (*s - '0');
995   if (*s || orig == s)
996     return -1;
997   else
998     return res;
999 }
1000
1001 #define ISODIGIT(x) ((x) >= '0' && (x) <= '7')
1002
1003 static int
1004 check_user_specified_header (const char *s)
1005 {
1006   const char *p;
1007
1008   for (p = s; *p && *p != ':' && !ISSPACE (*p); p++);
1009   /* The header MUST contain `:' preceded by at least one
1010      non-whitespace character.  */
1011   if (*p != ':' || p == s)
1012     return 0;
1013   /* The header MUST NOT contain newlines.  */
1014   if (strchr (s, '\n'))
1015     return 0;
1016   return 1;
1017 }
1018 \f
1019 void cleanup_html_url PARAMS ((void));
1020 void res_cleanup PARAMS ((void));
1021 void downloaded_files_free PARAMS ((void));
1022 void http_cleanup PARAMS ((void));
1023
1024
1025 /* Free the memory allocated by global variables.  */
1026 void
1027 cleanup (void)
1028 {
1029   /* Free external resources, close files, etc. */
1030
1031   if (opt.dfp)
1032     fclose (opt.dfp);
1033
1034   /* We're exiting anyway so there's no real need to call free()
1035      hundreds of times.  Skipping the frees will make Wget exit
1036      faster.
1037
1038      However, when detecting leaks, it's crucial to free() everything
1039      because then you can find the real leaks, i.e. the allocated
1040      memory which grows with the size of the program.  */
1041
1042 #ifdef DEBUG_MALLOC
1043   recursive_cleanup ();
1044   res_cleanup ();
1045   http_cleanup ();
1046   cleanup_html_url ();
1047   downloaded_files_free ();
1048   cookies_cleanup ();
1049   host_cleanup ();
1050
1051   {
1052     extern acc_t *netrc_list;
1053     free_netrc (netrc_list);
1054   }
1055   FREE_MAYBE (opt.lfilename);
1056   xfree (opt.dir_prefix);
1057   FREE_MAYBE (opt.input_filename);
1058   FREE_MAYBE (opt.output_document);
1059   free_vec (opt.accepts);
1060   free_vec (opt.rejects);
1061   free_vec (opt.excludes);
1062   free_vec (opt.includes);
1063   free_vec (opt.domains);
1064   free_vec (opt.follow_tags);
1065   free_vec (opt.ignore_tags);
1066   FREE_MAYBE (opt.progress_type);
1067   xfree (opt.ftp_acc);
1068   FREE_MAYBE (opt.ftp_pass);
1069   FREE_MAYBE (opt.ftp_proxy);
1070   FREE_MAYBE (opt.https_proxy);
1071   FREE_MAYBE (opt.http_proxy);
1072   free_vec (opt.no_proxy);
1073   FREE_MAYBE (opt.useragent);
1074   FREE_MAYBE (opt.referer);
1075   FREE_MAYBE (opt.http_user);
1076   FREE_MAYBE (opt.http_passwd);
1077   FREE_MAYBE (opt.user_header);
1078 #ifdef HAVE_SSL
1079   FREE_MAYBE (opt.sslcertkey);
1080   FREE_MAYBE (opt.sslcertfile);
1081 #endif /* HAVE_SSL */
1082   FREE_MAYBE (opt.bind_address);
1083   FREE_MAYBE (opt.cookies_input);
1084   FREE_MAYBE (opt.cookies_output);
1085 #endif
1086 }