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