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