]> sjero.net Git - wget/blob - src/init.c
[svn] New option --random-wait.
[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 sockaddr_in sin;
525   struct sockaddr_in **target = (struct sockaddr_in **)closure;
526
527   memset (&sin, '\0', sizeof (sin));
528
529   if (!lookup_host (val, (unsigned char *)&sin.sin_addr))
530     {
531       fprintf (stderr, _("%s: %s: Cannot convert `%s' to an IP address.\n"),
532                exec_name, com, val);
533       return 0;
534     }
535   sin.sin_family = AF_INET;
536   sin.sin_port = 0;
537
538   FREE_MAYBE (*target);
539
540   *target = xmalloc (sizeof (sin));
541   memcpy (*target, &sin, sizeof (sin));
542
543   return 1;
544 }
545
546 /* Store the boolean value from VAL to CLOSURE.  COM is ignored,
547    except for error messages.  */
548 static int
549 cmd_boolean (const char *com, const char *val, void *closure)
550 {
551   int bool_value;
552
553   if (!strcasecmp (val, "on")
554       || (*val == '1' && !*(val + 1)))
555     bool_value = 1;
556   else if (!strcasecmp (val, "off")
557            || (*val == '0' && !*(val + 1)))
558     bool_value = 0;
559   else
560     {
561       fprintf (stderr, _("%s: %s: Please specify on or off.\n"),
562                exec_name, com);
563       return 0;
564     }
565
566   *(int *)closure = bool_value;
567   return 1;
568 }
569
570 /* Store the lockable_boolean {2, 1, 0, -1} value from VAL to CLOSURE.  COM is
571    ignored, except for error messages.  Values 2 and -1 indicate that once
572    defined, the value may not be changed by successive wgetrc files or
573    command-line arguments.
574
575    Values: 2 - Enable a particular option for good ("always")
576            1 - Enable an option ("on")
577            0 - Disable an option ("off")
578           -1 - Disable an option for good ("never") */
579 static int
580 cmd_lockable_boolean (const char *com, const char *val, void *closure)
581 {
582   int lockable_boolean_value;
583
584   /*
585    * If a config file said "always" or "never", don't allow command line
586    * arguments to override the config file.
587    */
588   if (*(int *)closure == -1 || *(int *)closure == 2)
589     return 1;
590
591   if (!strcasecmp (val, "always")
592       || (*val == '2' && !*(val + 1)))
593     lockable_boolean_value = 2;
594   else if (!strcasecmp (val, "on")
595       || (*val == '1' && !*(val + 1)))
596     lockable_boolean_value = 1;
597   else if (!strcasecmp (val, "off")
598           || (*val == '0' && !*(val + 1)))
599     lockable_boolean_value = 0;
600   else if (!strcasecmp (val, "never")
601       || (*val == '-' && *(val + 1) == '1' && !*(val + 2)))
602     lockable_boolean_value = -1;
603   else
604     {
605       fprintf (stderr, _("%s: %s: Please specify always, on, off, "
606                          "or never.\n"),
607                exec_name, com);
608       return 0;
609     }
610
611   *(int *)closure = lockable_boolean_value;
612   return 1;
613 }
614
615 /* Set the non-negative integer value from VAL to CLOSURE.  With
616    incorrect specification, the number remains unchanged.  */
617 static int
618 cmd_number (const char *com, const char *val, void *closure)
619 {
620   int num = myatoi (val);
621
622   if (num == -1)
623     {
624       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
625                exec_name, com, val);
626       return 0;
627     }
628   *(int *)closure = num;
629   return 1;
630 }
631
632 /* Similar to cmd_number(), only accepts `inf' as a synonym for 0.  */
633 static int
634 cmd_number_inf (const char *com, const char *val, void *closure)
635 {
636   if (!strcasecmp (val, "inf"))
637     {
638       *(int *)closure = 0;
639       return 1;
640     }
641   return cmd_number (com, val, closure);
642 }
643
644 /* Copy (strdup) the string at COM to a new location and place a
645    pointer to *CLOSURE.  */
646 static int
647 cmd_string (const char *com, const char *val, void *closure)
648 {
649   char **pstring = (char **)closure;
650
651   FREE_MAYBE (*pstring);
652   *pstring = xstrdup (val);
653   return 1;
654 }
655
656 /* Like the above, but handles tilde-expansion when reading a user's
657    `.wgetrc'.  In that case, and if VAL begins with `~', the tilde
658    gets expanded to the user's home directory.  */
659 static int
660 cmd_file (const char *com, const char *val, void *closure)
661 {
662   char **pstring = (char **)closure;
663
664   FREE_MAYBE (*pstring);
665   if (!enable_tilde_expansion || !(*val == '~' && *(val + 1) == '/'))
666     {
667     noexpand:
668       *pstring = xstrdup (val);
669     }
670   else
671     {
672       char *result;
673       int homelen;
674       char *home = home_dir ();
675       if (!home)
676         goto noexpand;
677
678       homelen = strlen (home);
679       while (homelen && home[homelen - 1] == '/')
680         home[--homelen] = '\0';
681
682       /* Skip the leading "~/". */
683       for (++val; *val == '/'; val++)
684         ;
685
686       result = xmalloc (homelen + 1 + strlen (val));
687       memcpy (result, home, homelen);
688       result[homelen] = '/';
689       strcpy (result + homelen + 1, val);
690
691       *pstring = result;
692     }
693   return 1;
694 }
695
696 /* Merge the vector (array of strings separated with `,') in COM with
697    the vector (NULL-terminated array of strings) pointed to by
698    CLOSURE.  */
699 static int
700 cmd_vector (const char *com, const char *val, void *closure)
701 {
702   char ***pvec = (char ***)closure;
703
704   if (*val)
705     *pvec = merge_vecs (*pvec, sepstring (val));
706   else
707     {
708       free_vec (*pvec);
709       *pvec = NULL;
710     }
711   return 1;
712 }
713
714 static int
715 cmd_directory_vector (const char *com, const char *val, void *closure)
716 {
717   char ***pvec = (char ***)closure;
718
719   if (*val)
720     {
721       /* Strip the trailing slashes from directories.  */
722       char **t, **seps;
723
724       seps = sepstring (val);
725       for (t = seps; t && *t; t++)
726         {
727           int len = strlen (*t);
728           /* Skip degenerate case of root directory.  */
729           if (len > 1)
730             {
731               if ((*t)[len - 1] == '/')
732                 (*t)[len - 1] = '\0';
733             }
734         }
735       *pvec = merge_vecs (*pvec, seps);
736     }
737   else
738     {
739       free_vec (*pvec);
740       *pvec = NULL;
741     }
742   return 1;
743 }
744
745 /* Set the value stored in VAL to CLOSURE (which should point to a
746    long int), allowing several postfixes, with the following syntax
747    (regexp):
748
749    [0-9]+       -> bytes
750    [0-9]+[kK]   -> bytes * 1024
751    [0-9]+[mM]   -> bytes * 1024 * 1024
752    inf          -> 0
753
754    Anything else is flagged as incorrect, and CLOSURE is unchanged.  */
755 static int
756 cmd_bytes (const char *com, const char *val, void *closure)
757 {
758   long result;
759   long *out = (long *)closure;
760   const char *p;
761
762   result = 0;
763   p = val;
764   /* Check for "inf".  */
765   if (p[0] == 'i' && p[1] == 'n' && p[2] == 'f' && p[3] == '\0')
766     {
767       *out = 0;
768       return 1;
769     }
770   /* Search for digits and construct result.  */
771   for (; *p && ISDIGIT (*p); p++)
772     result = (10 * result) + (*p - '0');
773   /* If no digits were found, or more than one character is following
774      them, bail out.  */
775   if (p == val || (*p != '\0' && *(p + 1) != '\0'))
776     {
777       printf (_("%s: Invalid specification `%s'\n"), com, val);
778       return 0;
779     }
780   /* Search for a designator.  */
781   switch (TOLOWER (*p))
782     {
783     case '\0':
784       /* None */
785       break;
786     case 'k':
787       /* Kilobytes */
788       result *= 1024;
789       break;
790     case 'm':
791       /* Megabytes */
792       result *= (long)1024 * 1024;
793       break;
794     case 'g':
795       /* Gigabytes */
796       result *= (long)1024 * 1024 * 1024;
797       break;
798     default:
799       printf (_("%s: Invalid specification `%s'\n"), com, val);
800       return 0;
801     }
802   *out = result;
803   return 1;
804 }
805
806 /* Store the value of VAL to *OUT, allowing suffixes for minutes and
807    hours.  */
808 static int
809 cmd_time (const char *com, const char *val, void *closure)
810 {
811   long result = 0;
812   const char *p = val;
813
814   /* Search for digits and construct result.  */
815   for (; *p && ISDIGIT (*p); p++)
816     result = (10 * result) + (*p - '0');
817   /* If no digits were found, or more than one character is following
818      them, bail out.  */
819   if (p == val || (*p != '\0' && *(p + 1) != '\0'))
820     {
821       printf (_("%s: Invalid specification `%s'\n"), com, val);
822       return 0;
823     }
824   /* Search for a suffix.  */
825   switch (TOLOWER (*p))
826     {
827     case '\0':
828       /* None */
829       break;
830     case 'm':
831       /* Minutes */
832       result *= 60;
833       break;
834     case 'h':
835       /* Seconds */
836       result *= 3600;
837       break;
838     case 'd':
839       /* Days (overflow on 16bit machines) */
840       result *= 86400L;
841       break;
842     case 'w':
843       /* Weeks :-) */
844       result *= 604800L;
845       break;
846     default:
847       printf (_("%s: Invalid specification `%s'\n"), com, val);
848       return 0;
849     }
850   *(long *)closure = result;
851   return 1;
852 }
853 \f
854 /* Specialized helper functions, used by `commands' to handle some
855    options specially.  */
856
857 static int check_user_specified_header PARAMS ((const char *));
858
859 static int
860 cmd_spec_dirstruct (const char *com, const char *val, void *closure)
861 {
862   if (!cmd_boolean (com, val, &opt.dirstruct))
863     return 0;
864   /* Since dirstruct behaviour is explicitly changed, no_dirstruct
865      must be affected inversely.  */
866   if (opt.dirstruct)
867     opt.no_dirstruct = 0;
868   else
869     opt.no_dirstruct = 1;
870   return 1;
871 }
872
873 static int
874 cmd_spec_header (const char *com, const char *val, void *closure)
875 {
876   if (!*val)
877     {
878       /* Empty header means reset headers.  */
879       FREE_MAYBE (opt.user_header);
880       opt.user_header = NULL;
881     }
882   else
883     {
884       int i;
885
886       if (!check_user_specified_header (val))
887         {
888           fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
889                    exec_name, com, val);
890           return 0;
891         }
892       i = opt.user_header ? strlen (opt.user_header) : 0;
893       opt.user_header = (char *)xrealloc (opt.user_header, i + strlen (val)
894                                           + 2 + 1);
895       strcpy (opt.user_header + i, val);
896       i += strlen (val);
897       opt.user_header[i++] = '\r';
898       opt.user_header[i++] = '\n';
899       opt.user_header[i] = '\0';
900     }
901   return 1;
902 }
903
904 static int
905 cmd_spec_htmlify (const char *com, const char *val, void *closure)
906 {
907   int flag = cmd_boolean (com, val, &opt.htmlify);
908   if (flag && !opt.htmlify)
909     opt.remove_listing = 0;
910   return flag;
911 }
912
913 static int
914 cmd_spec_mirror (const char *com, const char *val, void *closure)
915 {
916   int mirror;
917
918   if (!cmd_boolean (com, val, &mirror))
919     return 0;
920   if (mirror)
921     {
922       opt.recursive = 1;
923       if (!opt.no_dirstruct)
924         opt.dirstruct = 1;
925       opt.timestamping = 1;
926       opt.reclevel = INFINITE_RECURSION;
927       opt.remove_listing = 0;
928     }
929   return 1;
930 }
931
932 #if 0
933 static int
934 cmd_spec_progress (const char *com, const char *val, void *closure)
935 {
936   if (!valid_progress_implementation_p (val))
937     {
938       fprintf (stderr, _("%s: %s: Invalid progress type `%s'.\n"),
939                exec_name, com, val);
940       return 0;
941     }
942   set_progress_implementation (val);
943   return 1;
944 }
945 #endif
946
947 static int
948 cmd_spec_recursive (const char *com, const char *val, void *closure)
949 {
950   if (!cmd_boolean (com, val, &opt.recursive))
951     return 0;
952   else
953     {
954       if (opt.recursive && !opt.no_dirstruct)
955         opt.dirstruct = 1;
956     }
957   return 1;
958 }
959
960 static int
961 cmd_spec_useragent (const char *com, const char *val, void *closure)
962 {
963   /* Just check for empty string and newline, so we don't throw total
964      junk to the server.  */
965   if (!*val || strchr (val, '\n'))
966     {
967       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
968                exec_name, com, val);
969       return 0;
970     }
971   opt.useragent = xstrdup (val);
972   return 1;
973 }
974 \f
975 /* Miscellaneous useful routines.  */
976
977 /* Return the integer value of a positive integer written in S, or -1
978    if an error was encountered.  */
979 static int
980 myatoi (const char *s)
981 {
982   int res;
983   const char *orig = s;
984
985   for (res = 0; *s && ISDIGIT (*s); s++)
986     res = 10 * res + (*s - '0');
987   if (*s || orig == s)
988     return -1;
989   else
990     return res;
991 }
992
993 #define ISODIGIT(x) ((x) >= '0' && (x) <= '7')
994
995 static int
996 check_user_specified_header (const char *s)
997 {
998   const char *p;
999
1000   for (p = s; *p && *p != ':' && !ISSPACE (*p); p++);
1001   /* The header MUST contain `:' preceded by at least one
1002      non-whitespace character.  */
1003   if (*p != ':' || p == s)
1004     return 0;
1005   /* The header MUST NOT contain newlines.  */
1006   if (strchr (s, '\n'))
1007     return 0;
1008   return 1;
1009 }
1010 \f
1011 void cleanup_html_url PARAMS ((void));
1012 void res_cleanup PARAMS ((void));
1013 void downloaded_files_free PARAMS ((void));
1014
1015
1016 /* Free the memory allocated by global variables.  */
1017 void
1018 cleanup (void)
1019 {
1020   /* Free external resources, close files, etc. */
1021
1022   if (opt.dfp)
1023     fclose (opt.dfp);
1024
1025   /* We're exiting anyway so there's no real need to call free()
1026      hundreds of times.  Skipping the frees will make Wget exit
1027      faster.
1028
1029      However, when detecting leaks, it's crucial to free() everything
1030      because then you can find the real leaks, i.e. the allocated
1031      memory which grows with the size of the program.  */
1032
1033 #ifdef DEBUG_MALLOC
1034   recursive_cleanup ();
1035   res_cleanup ();
1036   host_cleanup ();
1037   {
1038     extern acc_t *netrc_list;
1039     free_netrc (netrc_list);
1040   }
1041   cleanup_html_url ();
1042   downloaded_files_free ();
1043   cookies_cleanup ();
1044   FREE_MAYBE (opt.lfilename);
1045   xfree (opt.dir_prefix);
1046   FREE_MAYBE (opt.input_filename);
1047   FREE_MAYBE (opt.output_document);
1048   free_vec (opt.accepts);
1049   free_vec (opt.rejects);
1050   free_vec (opt.excludes);
1051   free_vec (opt.includes);
1052   free_vec (opt.domains);
1053   free_vec (opt.follow_tags);
1054   free_vec (opt.ignore_tags);
1055   FREE_MAYBE (opt.progress_type);
1056   xfree (opt.ftp_acc);
1057   FREE_MAYBE (opt.ftp_pass);
1058   FREE_MAYBE (opt.ftp_proxy);
1059   FREE_MAYBE (opt.https_proxy);
1060   FREE_MAYBE (opt.http_proxy);
1061   free_vec (opt.no_proxy);
1062   FREE_MAYBE (opt.useragent);
1063   FREE_MAYBE (opt.referer);
1064   FREE_MAYBE (opt.http_user);
1065   FREE_MAYBE (opt.http_passwd);
1066   FREE_MAYBE (opt.user_header);
1067 #ifdef HAVE_SSL
1068   FREE_MAYBE (opt.sslcertkey);
1069   FREE_MAYBE (opt.sslcertfile);
1070 #endif /* HAVE_SSL */
1071   FREE_MAYBE (opt.bind_address);
1072   FREE_MAYBE (opt.cookies_input);
1073   FREE_MAYBE (opt.cookies_output);
1074 #endif
1075 }