]> sjero.net Git - wget/blob - src/init.c
59a2aab9bf14af3d43b2cc5f540d9be5a92a7b2f
[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, allowing suffixes for minutes and
860    hours.  */
861 static int
862 cmd_time (const char *com, const char *val, void *closure)
863 {
864   long result = 0;
865   const char *p = val;
866
867   /* Search for digits and construct result.  */
868   for (; *p && ISDIGIT (*p); p++)
869     result = (10 * result) + (*p - '0');
870   /* If no digits were found, or more than one character is following
871      them, bail out.  */
872   if (p == val || (*p != '\0' && *(p + 1) != '\0'))
873     {
874       printf (_("%s: Invalid specification `%s'\n"), com, val);
875       return 0;
876     }
877   /* Search for a suffix.  */
878   switch (TOLOWER (*p))
879     {
880     case '\0':
881       /* None */
882       break;
883     case 'm':
884       /* Minutes */
885       result *= 60;
886       break;
887     case 'h':
888       /* Seconds */
889       result *= 3600;
890       break;
891     case 'd':
892       /* Days (overflow on 16bit machines) */
893       result *= 86400L;
894       break;
895     case 'w':
896       /* Weeks :-) */
897       result *= 604800L;
898       break;
899     default:
900       printf (_("%s: Invalid specification `%s'\n"), com, val);
901       return 0;
902     }
903   *(long *)closure = result;
904   return 1;
905 }
906 \f
907 /* Specialized helper functions, used by `commands' to handle some
908    options specially.  */
909
910 static int check_user_specified_header PARAMS ((const char *));
911
912 static int
913 cmd_spec_dirstruct (const char *com, const char *val, void *closure)
914 {
915   if (!cmd_boolean (com, val, &opt.dirstruct))
916     return 0;
917   /* Since dirstruct behaviour is explicitly changed, no_dirstruct
918      must be affected inversely.  */
919   if (opt.dirstruct)
920     opt.no_dirstruct = 0;
921   else
922     opt.no_dirstruct = 1;
923   return 1;
924 }
925
926 static int
927 cmd_spec_header (const char *com, const char *val, void *closure)
928 {
929   if (!*val)
930     {
931       /* Empty header means reset headers.  */
932       FREE_MAYBE (opt.user_header);
933       opt.user_header = NULL;
934     }
935   else
936     {
937       int i;
938
939       if (!check_user_specified_header (val))
940         {
941           fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
942                    exec_name, com, val);
943           return 0;
944         }
945       i = opt.user_header ? strlen (opt.user_header) : 0;
946       opt.user_header = (char *)xrealloc (opt.user_header, i + strlen (val)
947                                           + 2 + 1);
948       strcpy (opt.user_header + i, val);
949       i += strlen (val);
950       opt.user_header[i++] = '\r';
951       opt.user_header[i++] = '\n';
952       opt.user_header[i] = '\0';
953     }
954   return 1;
955 }
956
957 static int
958 cmd_spec_htmlify (const char *com, const char *val, void *closure)
959 {
960   int flag = cmd_boolean (com, val, &opt.htmlify);
961   if (flag && !opt.htmlify)
962     opt.remove_listing = 0;
963   return flag;
964 }
965
966 static int
967 cmd_spec_mirror (const char *com, const char *val, void *closure)
968 {
969   int mirror;
970
971   if (!cmd_boolean (com, val, &mirror))
972     return 0;
973   if (mirror)
974     {
975       opt.recursive = 1;
976       if (!opt.no_dirstruct)
977         opt.dirstruct = 1;
978       opt.timestamping = 1;
979       opt.reclevel = INFINITE_RECURSION;
980       opt.remove_listing = 0;
981     }
982   return 1;
983 }
984
985 static int
986 cmd_spec_progress (const char *com, const char *val, void *closure)
987 {
988   if (!valid_progress_implementation_p (val))
989     {
990       fprintf (stderr, _("%s: %s: Invalid progress type `%s'.\n"),
991                exec_name, com, val);
992       return 0;
993     }
994   FREE_MAYBE (opt.progress_type);
995
996   /* Don't call set_progress_implementation here.  It will be called
997      in main() when it becomes clear what the log output is.  */
998   opt.progress_type = xstrdup (val);
999   return 1;
1000 }
1001
1002 static int
1003 cmd_spec_recursive (const char *com, const char *val, void *closure)
1004 {
1005   if (!cmd_boolean (com, val, &opt.recursive))
1006     return 0;
1007   else
1008     {
1009       if (opt.recursive && !opt.no_dirstruct)
1010         opt.dirstruct = 1;
1011     }
1012   return 1;
1013 }
1014
1015 static int
1016 cmd_spec_restrict_file_names (const char *com, const char *val, void *closure)
1017 {
1018   int restrict_os = opt.restrict_files_os;
1019   int restrict_ctrl = opt.restrict_files_ctrl;
1020
1021   const char *end = strchr (val, ',');
1022   if (!end)
1023     end = val + strlen (val);
1024
1025 #define VAL_IS(string_literal) BOUNDED_EQUAL (val, end, string_literal)
1026
1027   if (VAL_IS ("unix"))
1028     restrict_os = restrict_unix;
1029   else if (VAL_IS ("windows"))
1030     restrict_os = restrict_windows;
1031   else if (VAL_IS ("nocontrol"))
1032     restrict_ctrl = 0;
1033   else
1034     {
1035     err:
1036       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
1037                exec_name, com, val);
1038       return 0;
1039     }
1040
1041 #undef VAL_IS
1042
1043   if (*end)
1044     {
1045       if (!strcmp (end + 1, "nocontrol"))
1046         restrict_ctrl = 0;
1047       else
1048         goto err;
1049     }
1050
1051   opt.restrict_files_os = restrict_os;
1052   opt.restrict_files_ctrl = restrict_ctrl;
1053   return 1;
1054 }
1055
1056 static int
1057 cmd_spec_useragent (const char *com, const char *val, void *closure)
1058 {
1059   /* Just check for empty string and newline, so we don't throw total
1060      junk to the server.  */
1061   if (!*val || strchr (val, '\n'))
1062     {
1063       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
1064                exec_name, com, val);
1065       return 0;
1066     }
1067   opt.useragent = xstrdup (val);
1068   return 1;
1069 }
1070 \f
1071 /* Miscellaneous useful routines.  */
1072
1073 /* Return the integer value of a positive integer written in S, or -1
1074    if an error was encountered.  */
1075 static int
1076 myatoi (const char *s)
1077 {
1078   int res;
1079   const char *orig = s;
1080
1081   for (res = 0; *s && ISDIGIT (*s); s++)
1082     res = 10 * res + (*s - '0');
1083   if (*s || orig == s)
1084     return -1;
1085   else
1086     return res;
1087 }
1088
1089 static int
1090 check_user_specified_header (const char *s)
1091 {
1092   const char *p;
1093
1094   for (p = s; *p && *p != ':' && !ISSPACE (*p); p++);
1095   /* The header MUST contain `:' preceded by at least one
1096      non-whitespace character.  */
1097   if (*p != ':' || p == s)
1098     return 0;
1099   /* The header MUST NOT contain newlines.  */
1100   if (strchr (s, '\n'))
1101     return 0;
1102   return 1;
1103 }
1104 \f
1105 void cleanup_html_url PARAMS ((void));
1106 void res_cleanup PARAMS ((void));
1107 void downloaded_files_free PARAMS ((void));
1108 void http_cleanup PARAMS ((void));
1109
1110
1111 /* Free the memory allocated by global variables.  */
1112 void
1113 cleanup (void)
1114 {
1115   /* Free external resources, close files, etc. */
1116
1117   if (opt.dfp)
1118     fclose (opt.dfp);
1119
1120   /* We're exiting anyway so there's no real need to call free()
1121      hundreds of times.  Skipping the frees will make Wget exit
1122      faster.
1123
1124      However, when detecting leaks, it's crucial to free() everything
1125      because then you can find the real leaks, i.e. the allocated
1126      memory which grows with the size of the program.  */
1127
1128 #ifdef DEBUG_MALLOC
1129   recursive_cleanup ();
1130   res_cleanup ();
1131   http_cleanup ();
1132   cleanup_html_url ();
1133   downloaded_files_free ();
1134   host_cleanup ();
1135   cookie_jar_delete (wget_cookie_jar);
1136
1137   {
1138     extern acc_t *netrc_list;
1139     free_netrc (netrc_list);
1140   }
1141   FREE_MAYBE (opt.lfilename);
1142   FREE_MAYBE (opt.dir_prefix);
1143   FREE_MAYBE (opt.input_filename);
1144   FREE_MAYBE (opt.output_document);
1145   free_vec (opt.accepts);
1146   free_vec (opt.rejects);
1147   free_vec (opt.excludes);
1148   free_vec (opt.includes);
1149   free_vec (opt.domains);
1150   free_vec (opt.follow_tags);
1151   free_vec (opt.ignore_tags);
1152   FREE_MAYBE (opt.progress_type);
1153   xfree (opt.ftp_acc);
1154   FREE_MAYBE (opt.ftp_pass);
1155   FREE_MAYBE (opt.ftp_proxy);
1156   FREE_MAYBE (opt.https_proxy);
1157   FREE_MAYBE (opt.http_proxy);
1158   free_vec (opt.no_proxy);
1159   FREE_MAYBE (opt.useragent);
1160   FREE_MAYBE (opt.referer);
1161   FREE_MAYBE (opt.http_user);
1162   FREE_MAYBE (opt.http_passwd);
1163   FREE_MAYBE (opt.user_header);
1164 #ifdef HAVE_SSL
1165   FREE_MAYBE (opt.sslcertkey);
1166   FREE_MAYBE (opt.sslcertfile);
1167 #endif /* HAVE_SSL */
1168   FREE_MAYBE (opt.bind_address);
1169   FREE_MAYBE (opt.cookies_input);
1170   FREE_MAYBE (opt.cookies_output);
1171 #endif
1172 }