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