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