]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Logging system bugfixes and improvements.
[wget] / src / utils.c
index ac3567330419d2546c403525e9be65eb5a9eda6e..2c6fd784570ed187ed54f424cd743716d06443a4 100644 (file)
@@ -50,9 +50,14 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #endif
 #include <fcntl.h>
 #include <assert.h>
+
+/* For TIOCGWINSZ and friends: */
 #ifdef HAVE_SYS_IOCTL_H
 # include <sys/ioctl.h>
 #endif
+#ifdef HAVE_TERMIOS_H
+# include <termios.h>
+#endif
 
 #include "wget.h"
 #include "utils.h"
@@ -79,21 +84,9 @@ extern int errno;
 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);
 }
@@ -459,146 +452,138 @@ fork_to_background (void)
   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);
-      exit (0);
+      exit (0);                        /* #### should we use _exit()? */
     }
-  /* child: keep running */
+
+  /* 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
-#if 0
-/* debug */
-char *
-ps (char *orig)
-{
-  char *r = xstrdup (orig);
-  path_simplify (r);
-  return r;
-}
-#endif
+/* 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.  */
 
-/* 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.  */
 int
 path_simplify (char *path)
 {
-  register int i, start;
-  int changes = 0;
-  char stub_char;
-
-  if (!*path)
-    return 0;
-
-  stub_char = '/';
+  int change = 0;
+  char *p, *end;
 
   if (path[0] == '/')
-    /* Preserve initial '/'. */
-    ++path;
+    ++path;                    /* preserve the leading '/'. */
 
-  /* Nix out leading `.' or `..' with.  */
-  if ((path[0] == '.' && path[1] == '\0')
-      || (path[0] == '.' && path[1] == '.' && path[2] == '\0'))
-    {
-      path[0] = '\0';
-      changes = 1;
-      return changes;
-    }
+  p = path;
+  end = p + strlen (p) + 1;    /* position past the terminating zero. */
 
-  /* Walk along PATH looking for things to compact.  */
-  i = 0;
   while (1)
     {
-      if (!path[i])
-       break;
+    again:
+      /* P should point to the beginning of a path element. */
 
-      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)
+      if (*p == '.' && (*(p + 1) == '/' || *(p + 1) == '\0'))
        {
-         strcpy (path + start + 1, path + i);
-         i = start + 1;
-         changes = 1;
+         /* 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;
+           }
        }
-
-      /* Check for `../', `./' or trailing `.' by itself.  */
-      if (path[i] == '.')
+      else if (*p == '.' && *(p + 1) == '.'
+              && (*(p + 2) == '/' || *(p + 2) == '\0'))
        {
-         /* Handle trailing `.' by itself.  */
-         if (!path[i + 1])
+         /* Handle "../foo" by moving "foo" one path element to the
+            left.  */
+         char *b = p;          /* not p-1 because P can equal PATH */
+
+         /* Backtrack by one path element, but not past the beginning
+            of PATH. */
+
+         /* foo/bar/../baz */
+         /*         ^ p    */
+         /*     ^ b        */
+
+         if (b > path)
            {
-             path[--i] = '\0';
-             changes = 1;
-             break;
+             /* Move backwards until B hits the beginning of the
+                previous path element or the beginning of path. */
+             for (--b; b > path && *(b - 1) != '/'; b--)
+               ;
            }
 
-         /* Handle `./'.  */
-         if (path[i + 1] == '/')
+         change = 1;
+         if (*(p + 2) == '/')
+           {
+             memmove (b, p + 3, end - (p + 3));
+             end -= (p + 3) - b;
+             p = b;
+           }
+         else
            {
-             strcpy (path + i, path + i + 1);
-             i = (start < 0) ? 0 : start;
-             changes = 1;
-             continue;
+             *b = '\0';
+             break;
            }
 
-         /* Handle `../' or trailing `..' by itself.  */
-         if (path[i + 1] == '.' &&
-             (path[i + 2] == '/' || !path[i + 2]))
+         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')
            {
-             while (--start > -1 && path[start] != '/');
-             strcpy (path + start + 1, path + i + 2 + (start == -1 && path[i + 2]));
-             i = (start < 0) ? 0 : start;
-             changes = 1;
-             continue;
+             *p = '\0';
+             break;
            }
-       }       /* path == '.' */
-    } /* while */
+         memmove (p, q, end - q);
+         end -= q - p;
+         goto again;
+       }
 
-  /* Addition: Remove all `./'-s and `../'-s preceding the string.  */
-  i = 0;
-  while (1)
-    {
-      if (path[i] == '.' && path[i + 1] == '/')
-       i += 2;
-      else if (path[i] == '.' && path[i + 1] == '.' && path[i + 2] == '/')
-       i += 3;
-      else
+      /* Skip to the next path element. */
+      while (*p && *p != '/')
+       ++p;
+      if (*p == '\0')
        break;
-    }
-  if (i)
-    {
-      strcpy (path, path + i - 0);
-      changes = 1;
+
+      /* Make sure P points to the beginning of the next path element,
+        which is location after the slash. */
+      ++p;
     }
 
-  return changes;
+  return change;
 }
 \f
 /* "Touch" FILE, i.e. make its atime and mtime equal to the time
@@ -744,9 +729,11 @@ make_directory (const char *directory)
 }
 
 /* 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.  */
 
@@ -757,7 +744,7 @@ file_merge (const char *base, const char *file)
   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);
@@ -854,8 +841,8 @@ accdir (const char *directory, enum accd flags)
    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;
 
@@ -870,7 +857,7 @@ match_backwards (const char *string, const char *pattern)
 }
 
 /* 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
@@ -891,7 +878,7 @@ in_acclist (const char *const *accepts, const char *s, int backward)
        {
          if (backward)
            {
-             if (match_backwards (s, *accepts))
+             if (match_tail (s, *accepts))
                return 1;
            }
          else
@@ -923,11 +910,13 @@ suffix (const char *str)
     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.
+/* 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
@@ -937,15 +926,20 @@ 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))
     {
       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;
+
       /* fgets() guarantees to read the whole line, or to use up the
          space we've given it.  We can double the buffer
          unconditionally.  */
@@ -1340,7 +1334,7 @@ legible (long l)
 {
   char inbuf[24];
   /* Print the number into the buffer.  */
-  long_to_string (inbuf, l);
+  number_to_string (inbuf, l);
   return legible_1 (inbuf);
 }
 
@@ -1396,17 +1390,17 @@ legible_very_long (VERY_LONG_TYPE l)
 
 /* 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'
@@ -1435,21 +1429,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)
 
-/* 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.
 
-   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;
@@ -1458,6 +1457,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);
+  p += strlen (buffer);
 #else  /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
 
   if (n < 0)
@@ -1493,6 +1493,8 @@ long_to_string (char *buffer, long number)
 
   *p = '\0';
 #endif /* (SIZEOF_LONG == 4) || (SIZEOF_LONG == 8) */
+
+  return p;
 }
 
 #undef ONE_DIGIT
@@ -1771,3 +1773,130 @@ determine_screen_width (void)
   return wsz.ws_col;
 #endif /* TIOCGWINSZ */
 }
+
+#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
+
+#if 0
+/* Debugging and testing support for path_simplify. */
+
+/* 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)
+{
+  char *copy = xstrdup (path);
+  path_simplify (copy);
+  return copy;
+}
+
+static void
+run_test (char *test, char *expected_result, int expected_change)
+{
+  char *test_copy = xstrdup (test);
+  int modified = path_simplify (test_copy);
+
+  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);
+}
+
+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;
+
+  for (i = 0; i < ARRAY_SIZE (tests); i++)
+    {
+      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);
+    }
+
+  /* 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++)
+    {
+      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);
+
+      run_test (test, expected_result, expected_change);
+
+      xfree (test);
+      xfree (expected_result);
+    }
+}
+#endif