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