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