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