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