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