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