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