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