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