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