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