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