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