]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Generalize connect_with_timeout into run_with_timeout.
[wget] / src / utils.c
index fd283710796a1003baa75e2067d0cd174ca3e8ad..cd1e645d42e7968ec4eb5d8dfdf5d9400b818e5a 100644 (file)
@@ -59,6 +59,27 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 # include <termios.h>
 #endif
 
 # 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 "fnmatch.h"
 #include "wget.h"
 #include "utils.h"
 #include "fnmatch.h"
@@ -84,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);
 }
@@ -464,134 +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
-/* Resolve "." and ".." elements of PATH by destructively modifying
-   PATH.  "." is resolved by removing that path element, and ".." is
-   resolved by removing the preceding path element.  Leading and
-   trailing slashes are preserved.
-
-   Return non-zero if any changes have been made.
-
-   For example, "a/b/c/./../d/.." will yield "a/b/".  More exhaustive
-   test examples are provided below.  If you change anything in this
-   function, run test_path_simplify to make sure you haven't broken a
-   test case.
 
 
-   A previous version of this function was based on path_simplify()
-   from GNU Bash, but it has been rewritten for Wget 1.8.1.  */
-
-int
-path_simplify (char *path)
-{
-  int change = 0;
-  char *p, *end;
-
-  if (path[0] == '/')
-    ++path;                    /* preserve the leading '/'. */
-
-  p = path;
-  end = p + strlen (p) + 1;    /* position past the terminating zero. */
-
-  while (1)
-    {
-    again:
-      /* P should point to the beginning of a path element. */
-
-      if (*p == '.' && (*(p + 1) == '/' || *(p + 1) == '\0'))
-       {
-         /* Handle "./foo" by moving "foo" two characters to the
-            left. */
-         if (*(p + 1) == '/')
-           {
-             change = 1;
-             memmove (p, p + 2, end - p);
-             end -= 2;
-             goto again;
-           }
-         else
-           {
-             change = 1;
-             *p = '\0';
-             break;
-           }
-       }
-      else if (*p == '.' && *(p + 1) == '.'
-              && (*(p + 2) == '/' || *(p + 2) == '\0'))
-       {
-         /* Handle "../foo" by moving "foo" one path element to the
-            left.  */
-         char *b = p;
-
-         /* Backtrack by one path element, but not past the beginning
-            of PATH. */
-
-         /* foo/bar/../baz */
-         /*         ^ p    */
-         /*     ^ b        */
-
-         if (b > path + 1)
-           {
-             /* Find the character preceded by slash or by the
-                beginning of path. */
-             for (--b; b > path && *(b - 1) != '/'; b--)
-               ;
-           }
-
-         change = 1;
-         if (*(p + 2) == '/')
-           {
-             memmove (b, p + 3, end - (p + 3));
-             end -= (p + 3) - b;
-             p = b;
-           }
-         else
-           {
-             *b = '\0';
-             break;
-           }
-
-         goto again;
-       }
-      else if (*p == '/')
-       {
-         /* Remove empty path elements.  Not mandated by rfc1808 et
-            al, but empty path elements are not all that useful, and
-            the rest of Wget might not deal with them well. */
-         char *q = p;
-         while (*q == '/')
-           ++q;
-         change = 1;
-         if (*q == '\0')
-           {
-             *p = '\0';
-             break;
-           }
-         memmove (p, q, end - q);
-         end -= q - p;
-         goto again;
-       }
-
-      /* Skip to the next path element. */
-      while (*p && *p != '/')
-       ++p;
-      if (*p == '\0')
-       break;
-
-      /* Make sure P points to the beginning of the next path element,
-        which is location after the slash. */
-      ++p;
-    }
-
-  return change;
+  /* 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.  */
@@ -706,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
@@ -721,18 +616,19 @@ 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
@@ -917,25 +813,48 @@ suffix (const char *str)
     return NULL;
 }
 
     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
-   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
-   between these two cases, use the stdio function ferror().
-
-   A future version of this function will be rewritten to use fread()
-   instead of fgets(), and to return the length of the line, which
-   will make the function usable on files with binary content.  */
+   between these two cases, use the stdio function ferror().  */
 
 char *
 read_whole_line (FILE *fp)
 {
   int length = 0;
 
 char *
 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))
   char *line = (char *)xmalloc (bufsize);
 
   while (fgets (line + length, bufsize - length, fp))
@@ -1343,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);
 }
 
@@ -1399,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'
@@ -1438,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;
@@ -1461,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)
@@ -1496,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
@@ -1775,6 +1702,49 @@ determine_screen_width (void)
 #endif /* TIOCGWINSZ */
 }
 
 #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. */
 
 #if 0
 /* A debugging function for checking whether an MD5 library works. */
 
@@ -1808,96 +1778,73 @@ debug_test_md5 (char *buf)
   return res;
 }
 #endif
   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)
 
 
-#if 0
-/* Debugging and testing support for path_simplify. */
+static sigjmp_buf run_with_timeout_env;
 
 
-/* Debug: run path_simplify on PATH and return the result in a new
-   string.  Useful for calling from the debugger.  */
-static char *
-ps (char *path)
+static RETSIGTYPE
+abort_run_with_timeout (int sig)
 {
 {
-  char *copy = xstrdup (path);
-  path_simplify (copy);
-  return copy;
+  assert (sig == SIGALRM);
+  siglongjmp (run_with_timeout_env, -1);
 }
 }
+#else  /* not HAVE_SIGSETJMP */
+#define SETJMP(env) setjmp (env)
 
 
-static void
-run_test (char *test, char *expected_result, int expected_change)
+static jmp_buf run_with_timeout_env;
+
+static RETSIGTYPE
+abort_run_with_timeout (int sig)
 {
 {
-  char *test_copy = xstrdup (test);
-  int modified = path_simplify (test_copy);
+  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);
 
 
-  if (0 != strcmp (test_copy, expected_result))
-    {
-      printf ("Failed path_simplify(\"%s\"): expected \"%s\", got \"%s\".\n",
-             test, expected_result, test_copy);
-    }
-  if (modified != expected_change)
-    {
-      if (expected_change == 1)
-       printf ("Expected no modification with path_simplify(\"%s\").\n",
-               test);
-      else
-       printf ("Expected modification with path_simplify(\"%s\").\n",
-               test);
-    }
-  xfree (test_copy);
+  /* Now it's safe to longjump. */
+  longjmp (run_with_timeout_env, -1);
 }
 }
+#endif /* not HAVE_SIGSETJMP */
 
 
-static void
-test_path_simplify (void)
-{
-  static struct {
-    char *test, *result;
-    int should_modify;
-  } tests[] = {
-    { "",              "",             0 },
-    { ".",             "",             1 },
-    { "..",            "",             1 },
-    { "foo",           "foo",          0 },
-    { "foo/bar",       "foo/bar",      0 },
-    { "foo///bar",     "foo/bar",      1 },
-    { "foo/.",         "foo/",         1 },
-    { "foo/./",                "foo/",         1 },
-    { "foo./",         "foo./",        0 },
-    { "foo/../bar",    "bar",          1 },
-    { "foo/../bar/",   "bar/",         1 },
-    { "foo/bar/..",    "foo/",         1 },
-    { "foo/bar/../x",  "foo/x",        1 },
-    { "foo/bar/../x/", "foo/x/",       1 },
-    { "foo/..",                "",             1 },
-    { "foo/../..",     "",             1 },
-    { "a/b/../../c",   "c",            1 },
-    { "./a/../b",      "b",            1 }
-  };
-  int i;
+int
+run_with_timeout (long timeout, void (*fun) (void *), void *arg)
+{
+#ifndef USE_SIGNAL_TIMEOUT
+  fun (arg);
+  return 0;
+#else
+  int saved_errno;
 
 
-  for (i = 0; i < ARRAY_SIZE (tests); i++)
+  if (timeout == 0)
     {
     {
-      char *test = tests[i].test;
-      char *expected_result = tests[i].result;
-      int   expected_change = tests[i].should_modify;
-      run_test (test, expected_result, expected_change);
+      fun (arg);
+      return 0;
     }
 
     }
 
-  /* Now run all the tests with a leading slash before the test case,
-     to prove that the slash is being preserved.  */
-  for (i = 0; i < ARRAY_SIZE (tests); i++)
+  signal (SIGALRM, abort_run_with_timeout);
+  if (SETJMP (run_with_timeout_env) != 0)
     {
     {
-      char *test, *expected_result;
-      int expected_change = tests[i].should_modify;
-
-      test = xmalloc (1 + strlen (tests[i].test) + 1);
-      sprintf (test, "/%s", tests[i].test);
-
-      expected_result = xmalloc (1 + strlen (tests[i].result) + 1);
-      sprintf (expected_result, "/%s", tests[i].result);
+      /* Longjumped out of FUN with a timeout. */
+      signal (SIGALRM, SIG_DFL);
+      return 1;
+    }
+  alarm (timeout);
+  fun (arg);
 
 
-      run_test (test, expected_result, expected_change);
+  /* Preserve errno in case alarm() or signal() modifies it. */
+  saved_errno = errno;
+  alarm (0);
+  signal (SIGALRM, SIG_DFL);
+  errno = saved_errno;
 
 
-      xfree (test);
-      xfree (expected_result);
-    }
-}
+  return 0;
 #endif
 #endif
+}
+