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