]> sjero.net Git - wget/blob - src/init.c
38b065faeef4900d92ae3c04c7720bb539d92c71
[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   { "httppasswd",       &opt.http_passwd,       cmd_string },
126   { "httpproxy",        &opt.http_proxy,        cmd_string },
127   { "httpuser",         &opt.http_user,         cmd_string },
128   { "ignorelength",     &opt.ignore_length,     cmd_boolean },
129   { "ignoretags",       &opt.ignore_tags,       cmd_vector },
130   { "includedirectories", &opt.includes,        cmd_directory_vector },
131   { "input",            &opt.input_filename,    cmd_string },
132   { "killlonger",       &opt.kill_longer,       cmd_boolean },
133   { "logfile",          &opt.lfilename,         cmd_string },
134   { "login",            &opt.ftp_acc,           cmd_string },
135   { "mirror",           NULL,                   cmd_spec_mirror },
136   { "netrc",            &opt.netrc,             cmd_boolean },
137   { "noclobber",        &opt.noclobber,         cmd_boolean },
138   { "noparent",         &opt.no_parent,         cmd_boolean },
139   { "noproxy",          &opt.no_proxy,          cmd_vector },
140   { "numtries",         &opt.ntry,              cmd_number_inf },/* deprecated*/
141   { "outputdocument",   &opt.output_document,   cmd_string },
142   { "pagerequisites",   &opt.page_requisites,   cmd_boolean },
143   { "passiveftp",       &opt.ftp_pasv,          cmd_lockable_boolean },
144   { "passwd",           &opt.ftp_pass,          cmd_string },
145   { "proxypasswd",      &opt.proxy_passwd,      cmd_string },
146   { "proxyuser",        &opt.proxy_user,        cmd_string },
147   { "quiet",            &opt.quiet,             cmd_boolean },
148   { "quota",            &opt.quota,             cmd_bytes },
149   { "reclevel",         &opt.reclevel,          cmd_number_inf },
150   { "recursive",        NULL,                   cmd_spec_recursive },
151   { "referer",          &opt.referer,           cmd_string },
152   { "reject",           &opt.rejects,           cmd_vector },
153   { "relativeonly",     &opt.relative_only,     cmd_boolean },
154   { "removelisting",    &opt.remove_listing,    cmd_boolean },
155   { "retrsymlinks",     &opt.retr_symlinks,     cmd_boolean },
156   { "robots",           &opt.use_robots,        cmd_boolean },
157   { "saveheaders",      &opt.save_headers,      cmd_boolean },
158   { "serverresponse",   &opt.server_response,   cmd_boolean },
159   { "simplehostcheck",  &opt.simple_check,      cmd_boolean },
160   { "spanhosts",        &opt.spanhost,          cmd_boolean },
161   { "spider",           &opt.spider,            cmd_boolean },
162   { "timeout",          &opt.timeout,           cmd_time },
163   { "timestamping",     &opt.timestamping,      cmd_boolean },
164   { "tries",            &opt.ntry,              cmd_number_inf },
165   { "useproxy",         &opt.use_proxy,         cmd_boolean },
166   { "useragent",        NULL,                   cmd_spec_useragent },
167   { "verbose",          &opt.verbose,           cmd_boolean },
168   { "wait",             &opt.wait,              cmd_time },
169   { "waitretry",        &opt.waitretry,         cmd_time }
170 };
171
172 /* Return index of COM if it is a valid command, or -1 otherwise.  COM
173    is looked up in `commands' using binary search algorithm.  */
174 static int
175 comind (const char *com)
176 {
177   int min = 0, max = ARRAY_SIZE (commands) - 1;
178
179   do
180     {
181       int i = (min + max) / 2;
182       int cmp = strcasecmp (com, commands[i].name);
183       if (cmp == 0)
184         return i;
185       else if (cmp < 0)
186         max = i - 1;
187       else
188         min = i + 1;
189     }
190   while (min <= max);
191   return -1;
192 }
193 \f
194 /* Reset the variables to default values.  */
195 static void
196 defaults (void)
197 {
198   char *tmp;
199
200   /* Most of the default values are 0.  Just reset everything, and
201      fill in the non-zero values.  Note that initializing pointers to
202      NULL this way is technically illegal, but porting Wget to a
203      machine where NULL is not all-zero bit pattern will be the least
204      of the implementors' worries.  */
205   memset (&opt, 0, sizeof (opt));
206
207   opt.verbose = -1;
208   opt.dir_prefix = xstrdup (".");
209   opt.ntry = 20;
210   opt.reclevel = 5;
211   opt.add_hostdir = 1;
212   opt.ftp_acc = xstrdup ("anonymous");
213   /*opt.ftp_pass = xstrdup (ftp_getaddress ());*/
214   opt.netrc = 1;
215   opt.ftp_glob = 1;
216   opt.htmlify = 1;
217   opt.use_proxy = 1;
218   tmp = getenv ("no_proxy");
219   if (tmp)
220     opt.no_proxy = sepstring (tmp);
221   opt.proxy_cache = 1;
222
223 #ifdef HAVE_SELECT
224   opt.timeout = 900;
225 #endif
226   opt.use_robots = 1;
227
228   opt.remove_listing = 1;
229
230   opt.dot_bytes = 1024;
231   opt.dot_spacing = 10;
232   opt.dots_in_line = 50;
233 }
234 \f
235 /* Return the user's home directory (strdup-ed), or NULL if none is
236    found.  */
237 char *
238 home_dir (void)
239 {
240   char *home = getenv ("HOME");
241
242   if (!home)
243     {
244 #ifndef WINDOWS
245       /* If HOME is not defined, try getting it from the password
246          file.  */
247       struct passwd *pwd = getpwuid (getuid ());
248       if (!pwd || !pwd->pw_dir)
249         return NULL;
250       home = pwd->pw_dir;
251 #else  /* WINDOWS */
252       home = "C:\\";
253       /* #### Maybe I should grab home_dir from registry, but the best
254          that I could get from there is user's Start menu.  It sucks!  */
255 #endif /* WINDOWS */
256     }
257
258   return home ? xstrdup (home) : NULL;
259 }
260
261 /* Return the path to the user's .wgetrc.  This is either the value of
262    `WGETRC' environment variable, or `$HOME/.wgetrc'.
263
264    If the `WGETRC' variable exists but the file does not exist, the
265    function will exit().  */
266 static char *
267 wgetrc_file_name (void)
268 {
269   char *env, *home;
270   char *file = NULL;
271
272   /* Try the environment.  */
273   env = getenv ("WGETRC");
274   if (env && *env)
275     {
276       if (!file_exists_p (env))
277         {
278           fprintf (stderr, "%s: %s: %s.\n", exec_name, file, strerror (errno));
279           exit (1);
280         }
281       return xstrdup (env);
282     }
283
284 #ifndef WINDOWS
285   /* If that failed, try $HOME/.wgetrc.  */
286   home = home_dir ();
287   if (home)
288     {
289       file = (char *)xmalloc (strlen (home) + 1 + strlen (".wgetrc") + 1);
290       sprintf (file, "%s/.wgetrc", home);
291     }
292   FREE_MAYBE (home);
293 #else  /* WINDOWS */
294   /* Under Windows, "home" is (for the purposes of this function) the
295      directory where `wget.exe' resides, and `wget.ini' will be used
296      as file name.  SYSTEM_WGETRC should not be defined under WINDOWS.
297
298      It is not as trivial as I assumed, because on 95 argv[0] is full
299      path, but on NT you get what you typed in command line.  --dbudor */
300   home = ws_mypath ();
301   if (home)
302     {
303       file = (char *)xmalloc (strlen (home) + strlen ("wget.ini") + 1);
304       sprintf (file, "%swget.ini", home);
305     }
306 #endif /* WINDOWS */
307
308   if (!file)
309     return NULL;
310   if (!file_exists_p (file))
311     {
312       free (file);
313       return NULL;
314     }
315   return file;
316 }
317
318 /* Initialize variables from a wgetrc file */
319 static void
320 run_wgetrc (const char *file)
321 {
322   FILE *fp;
323   char *line;
324   int ln;
325
326   fp = fopen (file, "rb");
327   if (!fp)
328     {
329       fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name,
330                file, strerror (errno));
331       return;
332     }
333   /* Reset line number.  */
334   ln = 1;
335   while ((line = read_whole_line (fp)))
336     {
337       char *com, *val;
338       int status;
339       int length = strlen (line);
340
341       if (length && line[length - 1] == '\r')
342         line[length - 1] = '\0';
343       /* Parse the line.  */
344       status = parse_line (line, &com, &val);
345       free (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           free (com);
353           free (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   free (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 spaces.  */
415   while (*p == ' ' || *p == '\t')
416     ++p;
417
418   /* Don't process empty lines.  */
419   if (!*p || *p == '\n' || *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   *com = (char *)xmalloc (p - orig_comptr + 1);
428   for (new_comptr = *com; orig_comptr < p; orig_comptr++)
429     {
430       if (*orig_comptr == '_' || *orig_comptr == '-')
431         continue;
432       *new_comptr++ = *orig_comptr;
433     }
434   *new_comptr = '\0';
435   /* If the command is invalid, exit now.  */
436   if (comind (*com) == -1)
437     {
438       free (*com);
439       return 0;
440     }
441
442   /* Skip spaces before '='.  */
443   for (; ISSPACE (*p); p++);
444   /* If '=' not found, bail out.  */
445   if (*p != '=')
446     {
447       free (*com);
448       return 0;
449     }
450   /* Skip spaces after '='.  */
451   for (++p; ISSPACE (*p); p++);
452   /* Get the ending position.  */
453   for (end = p; *end && *end != '\n'; end++);
454   /* Allocate *val, and copy from line.  */
455   *val = strdupdelim (p, end);
456   return 1;
457 }
458
459 /* Set COM to VAL.  This is the meat behind processing `.wgetrc'.  No
460    fatals -- error signal prints a warning and resets to default
461    value.  All error messages are printed to stderr, *not* to
462    opt.lfile, since opt.lfile wasn't even generated yet.  */
463 int
464 setval (const char *com, const char *val)
465 {
466   int ind;
467
468   if (!com || !val)
469     return 0;
470   ind = comind (com);
471   if (ind == -1)
472     {
473       /* #### Should I just abort()?  */
474 #ifdef DEBUG
475       fprintf (stderr, _("%s: BUG: unknown command `%s', value `%s'.\n"),
476                exec_name, com, val);
477 #endif
478       return 0;
479     }
480   return ((*commands[ind].action) (com, val, commands[ind].closure));
481 }
482 \f
483 /* Generic helper functions, for use with `commands'. */
484
485 static int myatoi PARAMS ((const char *s));
486
487 /* Store the address (specified as hostname or dotted-quad IP address) from VAL
488    to CLOSURE.  COM is ignored, except for error messages.  */
489 static int
490 cmd_address (const char *com, const char *val, void *closure)
491 {
492     struct sockaddr_in *sin;
493     
494     sin = (struct sockaddr_in *) malloc(sizeof *sin);
495     if (sin == NULL)
496     {
497         fprintf (stderr, _("%s: Out of memory.\n"), exec_name);
498         return 0;
499     }
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     * (struct sockaddr_in **) closure = 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 /* Free the memory allocated by global variables.  */
982 void
983 cleanup (void)
984 {
985   extern acc_t *netrc_list;
986
987   recursive_cleanup ();
988   clean_hosts ();
989   free_netrc (netrc_list);
990   if (opt.dfp)
991     fclose (opt.dfp);
992   FREE_MAYBE (opt.lfilename);
993   free (opt.dir_prefix);
994   FREE_MAYBE (opt.input_filename);
995   FREE_MAYBE (opt.output_document);
996   free_vec (opt.accepts);
997   free_vec (opt.rejects);
998   free_vec (opt.excludes);
999   free_vec (opt.includes);
1000   free_vec (opt.domains);
1001   free_vec (opt.follow_tags);
1002   free_vec (opt.ignore_tags);
1003   free (opt.ftp_acc);
1004   free (opt.ftp_pass);
1005   FREE_MAYBE (opt.ftp_proxy);
1006   FREE_MAYBE (opt.http_proxy);
1007   free_vec (opt.no_proxy);
1008   FREE_MAYBE (opt.useragent);
1009   FREE_MAYBE (opt.referer);
1010   FREE_MAYBE (opt.http_user);
1011   FREE_MAYBE (opt.http_passwd);
1012   FREE_MAYBE (opt.user_header);
1013 }