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