]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Generalize connect_with_timeout into run_with_timeout.
[wget] / src / utils.c
index fca15800248351052a573cb2582eb8eb466a91a1..cd1e645d42e7968ec4eb5d8dfdf5d9400b818e5a 100644 (file)
@@ -50,9 +50,35 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #endif
 #include <fcntl.h>
 #include <assert.h>
 #endif
 #include <fcntl.h>
 #include <assert.h>
+
+/* For TIOCGWINSZ and friends: */
 #ifdef HAVE_SYS_IOCTL_H
 # include <sys/ioctl.h>
 #endif
 #ifdef HAVE_SYS_IOCTL_H
 # include <sys/ioctl.h>
 #endif
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
+
+/* Needed for run_with_timeout. */
+#undef USE_SIGNAL_TIMEOUT
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
+#ifdef HAVE_SETJMP_H
+# include <setjmp.h>
+#endif
+/* If sigsetjmp is a macro, configure won't pick it up. */
+#ifdef sigsetjmp
+# define HAVE_SIGSETJMP
+#endif
+#ifdef HAVE_SIGNAL
+# ifdef HAVE_SIGSETJMP
+#  define USE_SIGNAL_TIMEOUT
+# endif
+# ifdef HAVE_SIGBLOCK
+#  define USE_SIGNAL_TIMEOUT
+# endif
+#endif
 
 #include "wget.h"
 #include "utils.h"
 
 #include "wget.h"
 #include "utils.h"
@@ -79,21 +105,9 @@ extern int errno;
 static void
 memfatal (const char *what)
 {
 static void
 memfatal (const char *what)
 {
-  /* HACK: expose save_log_p from log.c, so we can turn it off in
-     order to prevent saving the log.  Saving the log is dangerous
-     because logprintf() and logputs() can call malloc(), so this
-     could infloop.  When logging is turned off, infloop can no longer
-     happen.
-
-     #### This is no longer really necessary because the new routines
-     in log.c cons only if the line exceeds eighty characters.  But
-     this can come at the end of a line, so it's OK to be careful.
-
-     On a more serious note, it would be good to have a
-     log_forced_shutdown() routine that exposes this cleanly.  */
-  extern int save_log_p;
-
-  save_log_p = 0;
+  /* Make sure we don't try to store part of the log line, and thus
+     call malloc.  */
+  log_set_save_context (0);
   logprintf (LOG_ALWAYS, _("%s: %s: Not enough memory.\n"), exec_name, what);
   exit (1);
 }
   logprintf (LOG_ALWAYS, _("%s: %s: Not enough memory.\n"), exec_name, what);
   exit (1);
 }
@@ -307,6 +321,31 @@ xstrdup_debug (const char *s, const char *source_file, int source_line)
 
 #endif /* DEBUG_MALLOC */
 \f
 
 #endif /* DEBUG_MALLOC */
 \f
+/* Utility function: like xstrdup(), but also lowercases S.  */
+
+char *
+xstrdup_lower (const char *s)
+{
+  char *copy = xstrdup (s);
+  char *p = copy;
+  for (; *p; p++)
+    *p = TOLOWER (*p);
+  return copy;
+}
+
+/* Return a count of how many times CHR occurs in STRING. */
+
+int
+count_char (const char *string, char chr)
+{
+  const char *p;
+  int count = 0;
+  for (p = string; *p; p++)
+    if (*p == chr)
+      ++count;
+  return count;
+}
+
 /* Copy the string formed by two pointers (one on the beginning, other
    on the char after the last char) to a new, malloc-ed location.
    0-terminate it.  */
 /* Copy the string formed by two pointers (one on the beginning, other
    on the char after the last char) to a new, malloc-ed location.
    0-terminate it.  */
@@ -434,135 +473,19 @@ fork_to_background (void)
   else if (pid != 0)
     {
       /* parent, no error */
   else if (pid != 0)
     {
       /* parent, no error */
-      printf (_("Continuing in background.\n"));
+      printf (_("Continuing in background, pid %d.\n"), (int)pid);
       if (changedp)
        printf (_("Output will be written to `%s'.\n"), opt.lfilename);
       if (changedp)
        printf (_("Output will be written to `%s'.\n"), opt.lfilename);
-      exit (0);
+      exit (0);                        /* #### should we use _exit()? */
     }
     }
-  /* child: keep running */
-}
-#endif /* not WINDOWS */
-\f
-char *
-ps (char *orig)
-{
-  char *r = xstrdup (orig);
-  path_simplify (r);
-  return r;
-}
-
-/* Canonicalize PATH, and return a new path.  The new path differs from PATH
-   in that:
-       Multple `/'s are collapsed to a single `/'.
-       Leading `./'s and trailing `/.'s are removed.
-       Trailing `/'s are removed.
-       Non-leading `../'s and trailing `..'s are handled by removing
-       portions of the path.
-
-   E.g. "a/b/c/./../d/.." will yield "a/b".  This function originates
-   from GNU Bash.
-
-   Changes for Wget:
-       Always use '/' as stub_char.
-       Don't check for local things using canon_stat.
-       Change the original string instead of strdup-ing.
-       React correctly when beginning with `./' and `../'.
-       Don't zip out trailing slashes.  */
-void
-path_simplify (char *path)
-{
-  register int i, start, ddot;
-  char stub_char;
-
-  if (!*path)
-    return;
 
 
-  /*stub_char = (*path == '/') ? '/' : '.';*/
-  stub_char = '/';
-
-  /* Addition: Remove all `./'-s preceding the string.  If `../'-s
-     precede, put `/' in front and remove them too.  */
-  i = 0;
-  ddot = 0;
-  while (1)
-    {
-      if (path[i] == '.' && path[i + 1] == '/')
-       i += 2;
-      else if (path[i] == '.' && path[i + 1] == '.' && path[i + 2] == '/')
-       {
-         i += 3;
-         ddot = 1;
-       }
-      else
-       break;
-    }
-  if (i)
-    strcpy (path, path + i - ddot);
-
-  /* Replace single `.' or `..' with `/'.  */
-  if ((path[0] == '.' && path[1] == '\0')
-      || (path[0] == '.' && path[1] == '.' && path[2] == '\0'))
-    {
-      path[0] = stub_char;
-      path[1] = '\0';
-      return;
-    }
-  /* Walk along PATH looking for things to compact.  */
-  i = 0;
-  while (1)
-    {
-      if (!path[i])
-       break;
-
-      while (path[i] && path[i] != '/')
-       i++;
-
-      start = i++;
-
-      /* If we didn't find any slashes, then there is nothing left to do.  */
-      if (!path[start])
-       break;
-
-      /* Handle multiple `/'s in a row.  */
-      while (path[i] == '/')
-       i++;
-
-      if ((start + 1) != i)
-       {
-         strcpy (path + start + 1, path + i);
-         i = start + 1;
-       }
-
-      /* Check for `../', `./' or trailing `.' by itself.  */
-      if (path[i] == '.')
-       {
-         /* Handle trailing `.' by itself.  */
-         if (!path[i + 1])
-           {
-             path[--i] = '\0';
-             break;
-           }
-
-         /* Handle `./'.  */
-         if (path[i + 1] == '/')
-           {
-             strcpy (path + i, path + i + 1);
-             i = (start < 0) ? 0 : start;
-             continue;
-           }
-
-         /* Handle `../' or trailing `..' by itself.  */
-         if (path[i + 1] == '.' &&
-             (path[i + 2] == '/' || !path[i + 2]))
-           {
-             while (--start > -1 && path[start] != '/');
-             strcpy (path + start + 1, path + i + 2);
-             i = (start < 0) ? 0 : start;
-             continue;
-           }
-       }       /* path == '.' */
-    } /* while */
+  /* child: give up the privileges and keep running. */
+  setsid ();
+  freopen ("/dev/null", "r", stdin);
+  freopen ("/dev/null", "w", stdout);
+  freopen ("/dev/null", "w", stderr);
 }
 }
+#endif /* not WINDOWS */
 \f
 /* "Touch" FILE, i.e. make its atime and mtime equal to the time
    specified with TM.  */
 \f
 /* "Touch" FILE, i.e. make its atime and mtime equal to the time
    specified with TM.  */
@@ -677,6 +600,7 @@ make_directory (const char *directory)
 {
   int quit = 0;
   int i;
 {
   int quit = 0;
   int i;
+  int ret = 0;
   char *dir;
 
   /* Make a copy of dir, to be able to write to it.  Otherwise, the
   char *dir;
 
   /* Make a copy of dir, to be able to write to it.  Otherwise, the
@@ -692,24 +616,27 @@ make_directory (const char *directory)
       if (!dir[i])
        quit = 1;
       dir[i] = '\0';
       if (!dir[i])
        quit = 1;
       dir[i] = '\0';
-      /* Check whether the directory already exists.  */
+      /* Check whether the directory already exists.  Allow creation of
+        of intermediate directories to fail, as the initial path components
+        are not necessarily directories!  */
       if (!file_exists_p (dir))
       if (!file_exists_p (dir))
-       {
-         if (mkdir (dir, 0777) < 0)
-           return -1;
-       }
+       ret = mkdir (dir, 0777);
+      else
+       ret = 0;
       if (quit)
        break;
       else
        dir[i] = '/';
     }
       if (quit)
        break;
       else
        dir[i] = '/';
     }
-  return 0;
+  return ret;
 }
 
 /* Merge BASE with FILE.  BASE can be a directory or a file name, FILE
 }
 
 /* Merge BASE with FILE.  BASE can be a directory or a file name, FILE
-   should be a file name.  For example, file_merge("/foo/bar", "baz")
-   will return "/foo/baz".  file_merge("/foo/bar/", "baz") will return
-   "foo/bar/baz".
+   should be a file name.
+
+   file_merge("/foo/bar", "baz")  => "/foo/baz"
+   file_merge("/foo/bar/", "baz") => "/foo/bar/baz"
+   file_merge("foo", "bar")       => "bar"
 
    In other words, it's a simpler and gentler version of uri_merge_1.  */
 
 
    In other words, it's a simpler and gentler version of uri_merge_1.  */
 
@@ -720,7 +647,7 @@ file_merge (const char *base, const char *file)
   const char *cut = (const char *)strrchr (base, '/');
 
   if (!cut)
   const char *cut = (const char *)strrchr (base, '/');
 
   if (!cut)
-    cut = base + strlen (base);
+    return xstrdup (file);
 
   result = (char *)xmalloc (cut - base + 1 + strlen (file) + 1);
   memcpy (result, base, cut - base);
 
   result = (char *)xmalloc (cut - base + 1 + strlen (file) + 1);
   memcpy (result, base, cut - base);
@@ -817,8 +744,8 @@ accdir (const char *directory, enum accd flags)
    match_backwards ("abc", "bc") -> 1
    match_backwards ("abc", "ab") -> 0
    match_backwards ("abc", "abc") -> 1 */
    match_backwards ("abc", "bc") -> 1
    match_backwards ("abc", "ab") -> 0
    match_backwards ("abc", "abc") -> 1 */
-static int
-match_backwards (const char *string, const char *pattern)
+int
+match_tail (const char *string, const char *pattern)
 {
   int i, j;
 
 {
   int i, j;
 
@@ -833,7 +760,7 @@ match_backwards (const char *string, const char *pattern)
 }
 
 /* Checks whether string S matches each element of ACCEPTS.  A list
 }
 
 /* Checks whether string S matches each element of ACCEPTS.  A list
-   element are matched either with fnmatch() or match_backwards(),
+   element are matched either with fnmatch() or match_tail(),
    according to whether the element contains wildcards or not.
 
    If the BACKWARD is 0, don't do backward comparison -- just compare
    according to whether the element contains wildcards or not.
 
    If the BACKWARD is 0, don't do backward comparison -- just compare
@@ -854,7 +781,7 @@ in_acclist (const char *const *accepts, const char *s, int backward)
        {
          if (backward)
            {
        {
          if (backward)
            {
-             if (match_backwards (s, *accepts))
+             if (match_tail (s, *accepts))
                return 1;
            }
          else
                return 1;
            }
          else
@@ -867,7 +794,7 @@ in_acclist (const char *const *accepts, const char *s, int backward)
   return 0;
 }
 
   return 0;
 }
 
-/* Return the malloc-ed suffix of STR.  For instance:
+/* Return the location of STR's suffix (file extension).  Examples:
    suffix ("foo.bar")       -> "bar"
    suffix ("foo.bar.baz")   -> "baz"
    suffix ("/foo/bar")      -> NULL
    suffix ("foo.bar")       -> "bar"
    suffix ("foo.bar.baz")   -> "baz"
    suffix ("/foo/bar")      -> NULL
@@ -877,18 +804,47 @@ suffix (const char *str)
 {
   int i;
 
 {
   int i;
 
-  for (i = strlen (str); i && str[i] != '/' && str[i] != '.'; i--);
+  for (i = strlen (str); i && str[i] != '/' && str[i] != '.'; i--)
+    ;
+
   if (str[i++] == '.')
   if (str[i++] == '.')
-    return xstrdup (str + i);
+    return (char *)str + i;
   else
     return NULL;
 }
 
   else
     return NULL;
 }
 
-/* Read a line from FP.  The function reallocs the storage as needed
-   to accomodate for any length of the line.  Reallocs are done
-   storage exponentially, doubling the storage after each overflow to
-   minimize the number of calls to realloc() and fgets().  The newline
-   character at the end of line is retained.
+/* Return non-zero if FNAME ends with a typical HTML suffix.  The
+   following (case-insensitive) suffixes are presumed to be HTML files:
+   
+     html
+     htm
+     ?html (`?' matches one character)
+
+   #### CAVEAT.  This is not necessarily a good indication that FNAME
+   refers to a file that contains HTML!  */
+int
+has_html_suffix_p (const char *fname)
+{
+  char *suf;
+
+  if ((suf = suffix (fname)) == NULL)
+    return 0;
+  if (!strcasecmp (suf, "html"))
+    return 1;
+  if (!strcasecmp (suf, "htm"))
+    return 1;
+  if (suf[0] && !strcasecmp (suf + 1, "html"))
+    return 1;
+  return 0;
+}
+
+/* Read a line from FP and return the pointer to freshly allocated
+   storage.  The stoarage space is obtained through malloc() and
+   should be freed with free() when it is no longer needed.
+
+   The length of the line is not limited, except by available memory.
+   The newline character at the end of line is retained.  The line is
+   terminated with a zero character.
 
    After end-of-file is encountered without anything being read, NULL
    is returned.  NULL is also returned on error.  To distinguish
 
    After end-of-file is encountered without anything being read, NULL
    is returned.  NULL is also returned on error.  To distinguish
@@ -898,15 +854,20 @@ char *
 read_whole_line (FILE *fp)
 {
   int length = 0;
 read_whole_line (FILE *fp)
 {
   int length = 0;
-  int bufsize = 81;
+  int bufsize = 82;
   char *line = (char *)xmalloc (bufsize);
 
   while (fgets (line + length, bufsize - length, fp))
     {
       length += strlen (line + length);
   char *line = (char *)xmalloc (bufsize);
 
   while (fgets (line + length, bufsize - length, fp))
     {
       length += strlen (line + length);
-      assert (length > 0);
+      if (length == 0)
+       /* Possible for example when reading from a binary file where
+          a line begins with \0.  */
+       continue;
+
       if (line[length - 1] == '\n')
        break;
       if (line[length - 1] == '\n')
        break;
+
       /* fgets() guarantees to read the whole line, or to use up the
          space we've given it.  We can double the buffer
          unconditionally.  */
       /* fgets() guarantees to read the whole line, or to use up the
          space we've given it.  We can double the buffer
          unconditionally.  */
@@ -1301,7 +1262,7 @@ legible (long l)
 {
   char inbuf[24];
   /* Print the number into the buffer.  */
 {
   char inbuf[24];
   /* Print the number into the buffer.  */
-  long_to_string (inbuf, l);
+  number_to_string (inbuf, l);
   return legible_1 (inbuf);
 }
 
   return legible_1 (inbuf);
 }
 
@@ -1357,17 +1318,17 @@ legible_very_long (VERY_LONG_TYPE l)
 
 /* Count the digits in a (long) integer.  */
 int
 
 /* Count the digits in a (long) integer.  */
 int
-numdigit (long a)
+numdigit (long number)
 {
 {
-  int res = 1;
-  if (a < 0)
+  int cnt = 1;
+  if (number < 0)
     {
     {
-      a = -a;
-      ++res;
+      number = -number;
+      ++cnt;
     }
     }
-  while ((a /= 10) != 0)
-    ++res;
-  return res;
+  while ((number /= 10) > 0)
+    ++cnt;
+  return cnt;
 }
 
 #define ONE_DIGIT(figure) *p++ = n / (figure) + '0'
 }
 
 #define ONE_DIGIT(figure) *p++ = n / (figure) + '0'
@@ -1396,21 +1357,26 @@ numdigit (long a)
 #define DIGITS_18(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_17 ((figure) / 10)
 #define DIGITS_19(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_18 ((figure) / 10)
 
 #define DIGITS_18(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_17 ((figure) / 10)
 #define DIGITS_19(figure) ONE_DIGIT_ADVANCE (figure); DIGITS_18 ((figure) / 10)
 
-/* Print NUMBER to BUFFER in base 10.  This is completely equivalent
-   to `sprintf(buffer, "%ld", number)', only much faster.
+/* Print NUMBER to BUFFER in base 10.  This should be completely
+   equivalent to `sprintf(buffer, "%ld", number)', only much faster.
 
    The speedup may make a difference in programs that frequently
    convert numbers to strings.  Some implementations of sprintf,
    particularly the one in GNU libc, have been known to be extremely
    slow compared to this function.
 
 
    The speedup may make a difference in programs that frequently
    convert numbers to strings.  Some implementations of sprintf,
    particularly the one in GNU libc, have been known to be extremely
    slow compared to this function.
 
-   BUFFER should accept as many bytes as you expect the number to take
-   up.  On machines with 64-bit longs the maximum needed size is 24
-   bytes.  That includes the worst-case digits, the optional `-' sign,
-   and the trailing \0.  */
+   Return the pointer to the location where the terminating zero was
+   printed.  (Equivalent to calling buffer+strlen(buffer) after the
+   function is done.)
 
 
-void
-long_to_string (char *buffer, long number)
+   BUFFER should be big enough to accept as many bytes as you expect
+   the number to take up.  On machines with 64-bit longs the maximum
+   needed size is 24 bytes.  That includes the digits needed for the
+   largest 64-bit number, the `-' sign in case it's negative, and the
+   terminating '\0'.  */
+
+char *
+number_to_string (char *buffer, long number)
 {
   char *p = buffer;
   long n = number;
 {
   char *p = buffer;
   long n = number;
@@ -1419,6 +1385,7 @@ long_to_string (char *buffer, long number)
   /* We are running in a strange or misconfigured environment.  Let
      sprintf cope with it.  */
   sprintf (buffer, "%ld", n);
   /* We are running in a strange or misconfigured environment.  Let
      sprintf cope with it.  */
   sprintf (buffer, "%ld", n);
+  p += strlen (buffer);
 #else  /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
 
   if (n < 0)
 #else  /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
 
   if (n < 0)
@@ -1454,6 +1421,8 @@ long_to_string (char *buffer, long number)
 
   *p = '\0';
 #endif /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
 
   *p = '\0';
 #endif /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
+
+  return p;
 }
 
 #undef ONE_DIGIT
 }
 
 #undef ONE_DIGIT
@@ -1732,3 +1701,150 @@ determine_screen_width (void)
   return wsz.ws_col;
 #endif /* TIOCGWINSZ */
 }
   return wsz.ws_col;
 #endif /* TIOCGWINSZ */
 }
+
+/* Return a random number between 0 and MAX-1, inclusive.
+
+   If MAX is greater than the value of RAND_MAX+1 on the system, the
+   returned value will be in the range [0, RAND_MAX].  This may be
+   fixed in a future release.
+
+   The random number generator is seeded automatically the first time
+   it is called.
+
+   This uses rand() for portability.  It has been suggested that
+   random() offers better randomness, but this is not required for
+   Wget, so I chose to go for simplicity and use rand
+   unconditionally.  */
+
+int
+random_number (int max)
+{
+  static int seeded;
+  double bounded;
+  int rnd;
+
+  if (!seeded)
+    {
+      srand (time (NULL));
+      seeded = 1;
+    }
+  rnd = rand ();
+
+  /* On systems that don't define RAND_MAX, assume it to be 2**15 - 1,
+     and enforce that assumption by masking other bits.  */
+#ifndef RAND_MAX
+# define RAND_MAX 32767
+  rnd &= RAND_MAX;
+#endif
+
+  /* This is equivalent to rand() % max, but uses the high-order bits
+     for better randomness on architecture where rand() is implemented
+     using a simple congruential generator.  */
+
+  bounded = (double)max * rnd / (RAND_MAX + 1.0);
+  return (int)bounded;
+}
+
+#if 0
+/* A debugging function for checking whether an MD5 library works. */
+
+#include "gen-md5.h"
+
+char *
+debug_test_md5 (char *buf)
+{
+  unsigned char raw[16];
+  static char res[33];
+  unsigned char *p1;
+  char *p2;
+  int cnt;
+  ALLOCA_MD5_CONTEXT (ctx);
+
+  gen_md5_init (ctx);
+  gen_md5_update ((unsigned char *)buf, strlen (buf), ctx);
+  gen_md5_finish (ctx, raw);
+
+  p1 = raw;
+  p2 = res;
+  cnt = 16;
+  while (cnt--)
+    {
+      *p2++ = XDIGIT_TO_xchar (*p1 >> 4);
+      *p2++ = XDIGIT_TO_xchar (*p1 & 0xf);
+      ++p1;
+    }
+  *p2 = '\0';
+
+  return res;
+}
+#endif
+\f
+/* Implementation of run_with_timeout, a generic timeout handler for
+   systems with Unix-like signal handling.  */
+#ifdef HAVE_SIGSETJMP
+#define SETJMP(env) sigsetjmp (env, 1)
+
+static sigjmp_buf run_with_timeout_env;
+
+static RETSIGTYPE
+abort_run_with_timeout (int sig)
+{
+  assert (sig == SIGALRM);
+  siglongjmp (run_with_timeout_env, -1);
+}
+#else  /* not HAVE_SIGSETJMP */
+#define SETJMP(env) setjmp (env)
+
+static jmp_buf run_with_timeout_env;
+
+static RETSIGTYPE
+abort_run_with_timeout (int sig)
+{
+  assert (sig == SIGALRM);
+  /* We don't have siglongjmp to preserve the set of blocked signals;
+     if we longjumped out of the handler at this point, SIGALRM would
+     remain blocked.  We must unblock it manually. */
+  int mask = siggetmask ();
+  mask &= ~sigmask(SIGALRM);
+  sigsetmask (mask);
+
+  /* Now it's safe to longjump. */
+  longjmp (run_with_timeout_env, -1);
+}
+#endif /* not HAVE_SIGSETJMP */
+
+int
+run_with_timeout (long timeout, void (*fun) (void *), void *arg)
+{
+#ifndef USE_SIGNAL_TIMEOUT
+  fun (arg);
+  return 0;
+#else
+  int saved_errno;
+
+  if (timeout == 0)
+    {
+      fun (arg);
+      return 0;
+    }
+
+  signal (SIGALRM, abort_run_with_timeout);
+  if (SETJMP (run_with_timeout_env) != 0)
+    {
+      /* Longjumped out of FUN with a timeout. */
+      signal (SIGALRM, SIG_DFL);
+      return 1;
+    }
+  alarm (timeout);
+  fun (arg);
+
+  /* Preserve errno in case alarm() or signal() modifies it. */
+  saved_errno = errno;
+  alarm (0);
+  signal (SIGALRM, SIG_DFL);
+  errno = saved_errno;
+
+  return 0;
+#endif
+}
+