]> sjero.net Git - wget/blob - src/init.c
5cbb2ff5251731923d5f515431fc003ee9367eba
[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.allow_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 ("-wget@");
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.allow_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   memset (&sin, '\0', sizeof (sin));
525
526   if (!store_hostaddress ((unsigned char *)&sin.sin_addr, val))
527     {
528       fprintf (stderr, _("%s: %s: Cannot convert `%s' to an IP address.\n"),
529                exec_name, com, val);
530       return 0;
531     }
532   sin.sin_family = AF_INET;
533   sin.sin_port = 0;
534
535   FREE_MAYBE (*target);
536
537   *target = xmalloc (sizeof (sin));
538   memcpy (*target, &sin, sizeof (sin));
539
540   return 1;
541 }
542
543 /* Store the boolean value from VAL to CLOSURE.  COM is ignored,
544    except for error messages.  */
545 static int
546 cmd_boolean (const char *com, const char *val, void *closure)
547 {
548   int bool_value;
549
550   if (!strcasecmp (val, "on")
551       || (*val == '1' && !*(val + 1)))
552     bool_value = 1;
553   else if (!strcasecmp (val, "off")
554            || (*val == '0' && !*(val + 1)))
555     bool_value = 0;
556   else
557     {
558       fprintf (stderr, _("%s: %s: Please specify on or off.\n"),
559                exec_name, com);
560       return 0;
561     }
562
563   *(int *)closure = bool_value;
564   return 1;
565 }
566
567 /* Store the lockable_boolean {2, 1, 0, -1} value from VAL to CLOSURE.  COM is
568    ignored, except for error messages.  Values 2 and -1 indicate that once
569    defined, the value may not be changed by successive wgetrc files or
570    command-line arguments.
571
572    Values: 2 - Enable a particular option for good ("always")
573            1 - Enable an option ("on")
574            0 - Disable an option ("off")
575           -1 - Disable an option for good ("never") */
576 static int
577 cmd_lockable_boolean (const char *com, const char *val, void *closure)
578 {
579   int lockable_boolean_value;
580
581   /*
582    * If a config file said "always" or "never", don't allow command line
583    * arguments to override the config file.
584    */
585   if (*(int *)closure == -1 || *(int *)closure == 2)
586     return 1;
587
588   if (!strcasecmp (val, "always")
589       || (*val == '2' && !*(val + 1)))
590     lockable_boolean_value = 2;
591   else if (!strcasecmp (val, "on")
592       || (*val == '1' && !*(val + 1)))
593     lockable_boolean_value = 1;
594   else if (!strcasecmp (val, "off")
595           || (*val == '0' && !*(val + 1)))
596     lockable_boolean_value = 0;
597   else if (!strcasecmp (val, "never")
598       || (*val == '-' && *(val + 1) == '1' && !*(val + 2)))
599     lockable_boolean_value = -1;
600   else
601     {
602       fprintf (stderr, _("%s: %s: Please specify always, on, off, "
603                          "or never.\n"),
604                exec_name, com);
605       return 0;
606     }
607
608   *(int *)closure = lockable_boolean_value;
609   return 1;
610 }
611
612 /* Set the non-negative integer value from VAL to CLOSURE.  With
613    incorrect specification, the number remains unchanged.  */
614 static int
615 cmd_number (const char *com, const char *val, void *closure)
616 {
617   int num = myatoi (val);
618
619   if (num == -1)
620     {
621       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
622                exec_name, com, val);
623       return 0;
624     }
625   *(int *)closure = num;
626   return 1;
627 }
628
629 /* Similar to cmd_number(), only accepts `inf' as a synonym for 0.  */
630 static int
631 cmd_number_inf (const char *com, const char *val, void *closure)
632 {
633   if (!strcasecmp (val, "inf"))
634     {
635       *(int *)closure = 0;
636       return 1;
637     }
638   return cmd_number (com, val, closure);
639 }
640
641 /* Copy (strdup) the string at COM to a new location and place a
642    pointer to *CLOSURE.  */
643 static int
644 cmd_string (const char *com, const char *val, void *closure)
645 {
646   char **pstring = (char **)closure;
647
648   FREE_MAYBE (*pstring);
649   *pstring = xstrdup (val);
650   return 1;
651 }
652
653 /* Like the above, but handles tilde-expansion when reading a user's
654    `.wgetrc'.  In that case, and if VAL begins with `~', the tilde
655    gets expanded to the user's home directory.  */
656 static int
657 cmd_file (const char *com, const char *val, void *closure)
658 {
659   char **pstring = (char **)closure;
660
661   FREE_MAYBE (*pstring);
662   if (!enable_tilde_expansion || !(*val == '~' && *(val + 1) == '/'))
663     {
664     noexpand:
665       *pstring = xstrdup (val);
666     }
667   else
668     {
669       char *result;
670       int homelen;
671       char *home = home_dir ();
672       if (!home)
673         goto noexpand;
674
675       homelen = strlen (home);
676       while (homelen && home[homelen - 1] == '/')
677         home[--homelen] = '\0';
678
679       /* Skip the leading "~/". */
680       for (++val; *val == '/'; val++)
681         ;
682
683       result = xmalloc (homelen + 1 + strlen (val));
684       memcpy (result, home, homelen);
685       result[homelen] = '/';
686       strcpy (result + homelen + 1, val);
687
688       *pstring = result;
689     }
690   return 1;
691 }
692
693 /* Merge the vector (array of strings separated with `,') in COM with
694    the vector (NULL-terminated array of strings) pointed to by
695    CLOSURE.  */
696 static int
697 cmd_vector (const char *com, const char *val, void *closure)
698 {
699   char ***pvec = (char ***)closure;
700
701   if (*val)
702     *pvec = merge_vecs (*pvec, sepstring (val));
703   else
704     {
705       free_vec (*pvec);
706       *pvec = NULL;
707     }
708   return 1;
709 }
710
711 static int
712 cmd_directory_vector (const char *com, const char *val, void *closure)
713 {
714   char ***pvec = (char ***)closure;
715
716   if (*val)
717     {
718       /* Strip the trailing slashes from directories.  */
719       char **t, **seps;
720
721       seps = sepstring (val);
722       for (t = seps; t && *t; t++)
723         {
724           int len = strlen (*t);
725           /* Skip degenerate case of root directory.  */
726           if (len > 1)
727             {
728               if ((*t)[len - 1] == '/')
729                 (*t)[len - 1] = '\0';
730             }
731         }
732       *pvec = merge_vecs (*pvec, seps);
733     }
734   else
735     {
736       free_vec (*pvec);
737       *pvec = NULL;
738     }
739   return 1;
740 }
741
742 /* Set the value stored in VAL to CLOSURE (which should point to a
743    long int), allowing several postfixes, with the following syntax
744    (regexp):
745
746    [0-9]+       -> bytes
747    [0-9]+[kK]   -> bytes * 1024
748    [0-9]+[mM]   -> bytes * 1024 * 1024
749    inf          -> 0
750
751    Anything else is flagged as incorrect, and CLOSURE is unchanged.  */
752 static int
753 cmd_bytes (const char *com, const char *val, void *closure)
754 {
755   long result;
756   long *out = (long *)closure;
757   const char *p;
758
759   result = 0;
760   p = val;
761   /* Check for "inf".  */
762   if (p[0] == 'i' && p[1] == 'n' && p[2] == 'f' && p[3] == '\0')
763     {
764       *out = 0;
765       return 1;
766     }
767   /* Search for digits and construct result.  */
768   for (; *p && ISDIGIT (*p); p++)
769     result = (10 * result) + (*p - '0');
770   /* If no digits were found, or more than one character is following
771      them, bail out.  */
772   if (p == val || (*p != '\0' && *(p + 1) != '\0'))
773     {
774       printf (_("%s: Invalid specification `%s'\n"), com, val);
775       return 0;
776     }
777   /* Search for a designator.  */
778   switch (TOLOWER (*p))
779     {
780     case '\0':
781       /* None */
782       break;
783     case 'k':
784       /* Kilobytes */
785       result *= 1024;
786       break;
787     case 'm':
788       /* Megabytes */
789       result *= (long)1024 * 1024;
790       break;
791     case 'g':
792       /* Gigabytes */
793       result *= (long)1024 * 1024 * 1024;
794       break;
795     default:
796       printf (_("%s: Invalid specification `%s'\n"), com, val);
797       return 0;
798     }
799   *out = result;
800   return 1;
801 }
802
803 /* Store the value of VAL to *OUT, allowing suffixes for minutes and
804    hours.  */
805 static int
806 cmd_time (const char *com, const char *val, void *closure)
807 {
808   long result = 0;
809   const char *p = val;
810
811   /* Search for digits and construct result.  */
812   for (; *p && ISDIGIT (*p); p++)
813     result = (10 * result) + (*p - '0');
814   /* If no digits were found, or more than one character is following
815      them, bail out.  */
816   if (p == val || (*p != '\0' && *(p + 1) != '\0'))
817     {
818       printf (_("%s: Invalid specification `%s'\n"), com, val);
819       return 0;
820     }
821   /* Search for a suffix.  */
822   switch (TOLOWER (*p))
823     {
824     case '\0':
825       /* None */
826       break;
827     case 'm':
828       /* Minutes */
829       result *= 60;
830       break;
831     case 'h':
832       /* Seconds */
833       result *= 3600;
834       break;
835     case 'd':
836       /* Days (overflow on 16bit machines) */
837       result *= 86400L;
838       break;
839     case 'w':
840       /* Weeks :-) */
841       result *= 604800L;
842       break;
843     default:
844       printf (_("%s: Invalid specification `%s'\n"), com, val);
845       return 0;
846     }
847   *(long *)closure = result;
848   return 1;
849 }
850 \f
851 /* Specialized helper functions, used by `commands' to handle some
852    options specially.  */
853
854 static int check_user_specified_header PARAMS ((const char *));
855
856 static int
857 cmd_spec_dirstruct (const char *com, const char *val, void *closure)
858 {
859   if (!cmd_boolean (com, val, &opt.dirstruct))
860     return 0;
861   /* Since dirstruct behaviour is explicitly changed, no_dirstruct
862      must be affected inversely.  */
863   if (opt.dirstruct)
864     opt.no_dirstruct = 0;
865   else
866     opt.no_dirstruct = 1;
867   return 1;
868 }
869
870 static int
871 cmd_spec_dotstyle (const char *com, const char *val, void *closure)
872 {
873   /* Retrieval styles.  */
874   if (!strcasecmp (val, "default"))
875     {
876       /* Default style: 1K dots, 10 dots in a cluster, 50 dots in a
877          line.  */
878       opt.dot_bytes = 1024;
879       opt.dot_spacing = 10;
880       opt.dots_in_line = 50;
881     }
882   else if (!strcasecmp (val, "binary"))
883     {
884       /* "Binary" retrieval: 8K dots, 16 dots in a cluster, 48 dots
885          (384K) in a line.  */
886       opt.dot_bytes = 8192;
887       opt.dot_spacing = 16;
888       opt.dots_in_line = 48;
889     }
890   else if (!strcasecmp (val, "mega"))
891     {
892       /* "Mega" retrieval, for retrieving very long files; each dot is
893          64K, 8 dots in a cluster, 6 clusters (3M) in a line.  */
894       opt.dot_bytes = 65536L;
895       opt.dot_spacing = 8;
896       opt.dots_in_line = 48;
897     }
898   else if (!strcasecmp (val, "giga"))
899     {
900       /* "Giga" retrieval, for retrieving very very *very* long files;
901          each dot is 1M, 8 dots in a cluster, 4 clusters (32M) in a
902          line.  */
903       opt.dot_bytes = (1L << 20);
904       opt.dot_spacing = 8;
905       opt.dots_in_line = 32;
906     }
907   else if (!strcasecmp (val, "micro"))
908     {
909       /* "Micro" retrieval, for retrieving very small files (and/or
910          slow connections); each dot is 128 bytes, 8 dots in a
911          cluster, 6 clusters (6K) in a line.  */
912       opt.dot_bytes = 128;
913       opt.dot_spacing = 8;
914       opt.dots_in_line = 48;
915     }
916   else
917     {
918       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
919                exec_name, com, val);
920       return 0;
921     }
922   return 1;
923 }
924
925 static int
926 cmd_spec_header (const char *com, const char *val, void *closure)
927 {
928   if (!*val)
929     {
930       /* Empty header means reset headers.  */
931       FREE_MAYBE (opt.user_header);
932       opt.user_header = NULL;
933     }
934   else
935     {
936       int i;
937
938       if (!check_user_specified_header (val))
939         {
940           fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
941                    exec_name, com, val);
942           return 0;
943         }
944       i = opt.user_header ? strlen (opt.user_header) : 0;
945       opt.user_header = (char *)xrealloc (opt.user_header, i + strlen (val)
946                                           + 2 + 1);
947       strcpy (opt.user_header + i, val);
948       i += strlen (val);
949       opt.user_header[i++] = '\r';
950       opt.user_header[i++] = '\n';
951       opt.user_header[i] = '\0';
952     }
953   return 1;
954 }
955
956 static int
957 cmd_spec_htmlify (const char *com, const char *val, void *closure)
958 {
959   int flag = cmd_boolean (com, val, &opt.htmlify);
960   if (flag && !opt.htmlify)
961     opt.remove_listing = 0;
962   return flag;
963 }
964
965 static int
966 cmd_spec_mirror (const char *com, const char *val, void *closure)
967 {
968   int mirror;
969
970   if (!cmd_boolean (com, val, &mirror))
971     return 0;
972   if (mirror)
973     {
974       opt.recursive = 1;
975       if (!opt.no_dirstruct)
976         opt.dirstruct = 1;
977       opt.timestamping = 1;
978       opt.reclevel = INFINITE_RECURSION;
979       opt.remove_listing = 0;
980     }
981   return 1;
982 }
983
984 static int
985 cmd_spec_recursive (const char *com, const char *val, void *closure)
986 {
987   if (!cmd_boolean (com, val, &opt.recursive))
988     return 0;
989   else
990     {
991       if (opt.recursive && !opt.no_dirstruct)
992         opt.dirstruct = 1;
993     }
994   return 1;
995 }
996
997 static int
998 cmd_spec_useragent (const char *com, const char *val, void *closure)
999 {
1000   /* Just check for empty string and newline, so we don't throw total
1001      junk to the server.  */
1002   if (!*val || strchr (val, '\n'))
1003     {
1004       fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"),
1005                exec_name, com, val);
1006       return 0;
1007     }
1008   opt.useragent = xstrdup (val);
1009   return 1;
1010 }
1011 \f
1012 /* Miscellaneous useful routines.  */
1013
1014 /* Return the integer value of a positive integer written in S, or -1
1015    if an error was encountered.  */
1016 static int
1017 myatoi (const char *s)
1018 {
1019   int res;
1020   const char *orig = s;
1021
1022   for (res = 0; *s && ISDIGIT (*s); s++)
1023     res = 10 * res + (*s - '0');
1024   if (*s || orig == s)
1025     return -1;
1026   else
1027     return res;
1028 }
1029
1030 #define ISODIGIT(x) ((x) >= '0' && (x) <= '7')
1031
1032 static int
1033 check_user_specified_header (const char *s)
1034 {
1035   const char *p;
1036
1037   for (p = s; *p && *p != ':' && !ISSPACE (*p); p++);
1038   /* The header MUST contain `:' preceded by at least one
1039      non-whitespace character.  */
1040   if (*p != ':' || p == s)
1041     return 0;
1042   /* The header MUST NOT contain newlines.  */
1043   if (strchr (s, '\n'))
1044     return 0;
1045   return 1;
1046 }
1047 \f
1048 void cleanup_html_url PARAMS ((void));
1049 void downloaded_files_free PARAMS ((void));
1050
1051
1052 /* Free the memory allocated by global variables.  */
1053 void
1054 cleanup (void)
1055 {
1056   extern acc_t *netrc_list;
1057
1058   recursive_cleanup ();
1059   clean_hosts ();
1060   free_netrc (netrc_list);
1061   if (opt.dfp)
1062     fclose (opt.dfp);
1063   cleanup_html_url ();
1064   downloaded_files_free ();
1065   cookies_cleanup ();
1066   FREE_MAYBE (opt.lfilename);
1067   xfree (opt.dir_prefix);
1068   FREE_MAYBE (opt.input_filename);
1069   FREE_MAYBE (opt.output_document);
1070   free_vec (opt.accepts);
1071   free_vec (opt.rejects);
1072   free_vec (opt.excludes);
1073   free_vec (opt.includes);
1074   free_vec (opt.domains);
1075   free_vec (opt.follow_tags);
1076   free_vec (opt.ignore_tags);
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 }