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