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