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