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