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