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