Fix problem with getdate on mingw32 reported by Simon Josefsson
[gnulib.git] / lib / canonicalize.c
index eed7f40..f0a4762 100644 (file)
@@ -1,10 +1,10 @@
 /* Return the canonical absolute name of a given file.
    Copyright (C) 1996-2007 Free Software Foundation, Inc.
 
-   This program is free software; you can redistribute it and/or modify
+   This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2, or (at your option)
-   any later version.
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,9 +12,7 @@
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this program; see the file COPYING.
-   If not, write to the Free Software Foundation,
-   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 
@@ -34,8 +32,9 @@
 #include <errno.h>
 #include <stddef.h>
 
-#include "cycle-check.h"
+#include "file-set.h"
 #include "filenamecat.h"
+#include "hash-triple.h"
 #include "xalloc.h"
 #include "xgetcwd.h"
 
@@ -47,7 +46,7 @@
 #endif
 
 #include "pathmax.h"
-#include "mreadlink.h"
+#include "areadlink.h"
 
 #if !HAVE_CANONICALIZE_FILE_NAME
 /* Return the canonical absolute name of file NAME.  A canonical name
@@ -123,6 +122,30 @@ canonicalize_file_name (const char *name)
 }
 #endif /* !HAVE_CANONICALIZE_FILE_NAME */
 
+/* Return true if we've already seen the triple, <FILENAME, dev, ino>.
+   If *HT is not initialized, initialize it.  */
+static bool
+seen_triple (Hash_table **ht, char const *filename, struct stat const *st)
+{
+  if (*ht == NULL)
+    {
+      size_t initial_capacity = 7;
+      *ht = hash_initialize (initial_capacity,
+                           NULL,
+                           triple_hash,
+                           triple_compare_ino_str,
+                           triple_free);
+      if (*ht == NULL)
+       xalloc_die ();
+    }
+
+  if (seen_file (*ht, filename, st))
+    return true;
+
+  record_file (*ht, filename, st);
+  return false;
+}
+
 /* Return the canonical absolute name of file NAME.  A canonical name
    does not contain any `.', `..' components nor any repeated file name
    separators ('/') or symlinks.  Whether components must exist
@@ -136,7 +159,7 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
   char const *end;
   char const *rname_limit;
   size_t extra_len = 0;
-  struct cycle_check_state cycle_state;
+  Hash_table *ht = NULL;
 
   if (name == NULL)
     {
@@ -176,7 +199,6 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
       dest = rname + 1;
     }
 
-  cycle_check_init (&cycle_state);
   for (start = end = name; *start; start = end)
     {
       /* Skip sequence of multiple file name separators.  */
@@ -237,7 +259,11 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
              char *buf;
              size_t n, len;
 
-             if (cycle_check (&cycle_state, &st))
+             /* Detect loops.  We cannot use the cycle-check module here,
+                since it's actually possible to encounter the same symlink
+                more than once in a given traversal.  However, encountering
+                the same symlink,NAME pair twice does indicate a loop.  */
+             if (seen_triple (&ht, name, &st))
                {
                  __set_errno (ELOOP);
                  if (can_mode == CAN_MISSING)
@@ -246,7 +272,7 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
                    goto error;
                }
 
-             buf = mreadlink_with_size (rname, st.st_size);
+             buf = areadlink_with_size (rname, st.st_size);
              if (!buf)
                {
                  if (can_mode == CAN_MISSING && errno != ENOMEM)
@@ -298,10 +324,14 @@ canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
   *dest = '\0';
 
   free (extra_buf);
+  if (ht)
+    hash_free (ht);
   return rname;
 
 error:
   free (extra_buf);
   free (rname);
+  if (ht)
+    hash_free (ht);
   return NULL;
 }