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