]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libiberty/lrealpath.c
Update libbid according to the latest Intel Decimal Floating-Point Math Library.
[thirdparty/gcc.git] / libiberty / lrealpath.c
index c662f8fd37edf9e1fe21b2670ef4bf1f555da365..c683837dd9298d1c3f32879c3118c2481bfc1e87 100644 (file)
@@ -1,7 +1,7 @@
 /* Libiberty realpath.  Like realpath, but more consistent behavior.
    Based on gdb_realpath from GDB.
 
-   Copyright (C) 2003-2023 Free Software Foundation, Inc.
+   Copyright (C) 2003-2024 Free Software Foundation, Inc.
 
    This file is part of the libiberty library.
 
@@ -68,8 +68,135 @@ extern char *canonicalize_file_name (const char *);
   /* cygwin has realpath, so it won't get here.  */ 
 # if defined (_WIN32)
 #  define WIN32_LEAN_AND_MEAN
-#  include <windows.h> /* for GetFullPathName */
-# endif
+#  include <windows.h> /* for GetFullPathName/GetFinalPathNameByHandle/
+                          CreateFile/CloseHandle */
+#  define WIN32_REPLACE_SLASHES(_ptr, _len) \
+     for (unsigned i = 0; i != (_len); ++i) \
+       if ((_ptr)[i] == '\\') (_ptr)[i] = '/';
+
+#  define WIN32_UNC_PREFIX "//?/UNC/"
+#  define WIN32_UNC_PREFIX_LEN (sizeof(WIN32_UNC_PREFIX)-1)
+#  define WIN32_IS_UNC_PREFIX(ptr) \
+  (0 == memcmp(ptr, WIN32_UNC_PREFIX, WIN32_UNC_PREFIX_LEN))
+
+#  define WIN32_NON_UNC_PREFIX "//?/"
+#  define WIN32_NON_UNC_PREFIX_LEN (sizeof(WIN32_NON_UNC_PREFIX)-1)
+#  define WIN32_IS_NON_UNC_PREFIX(ptr) \
+  (0 == memcmp(ptr, WIN32_NON_UNC_PREFIX, WIN32_NON_UNC_PREFIX_LEN))
+
+/* Get full path name without symlinks resolution.
+   It also converts all forward slashes to back slashes.
+*/
+char* get_full_path_name(const char *filename) {
+  DWORD len;
+  char *buf, *ptr, *res;
+
+  /* determining the required buffer size.
+     from the man: `If the lpBuffer buffer is too small to contain
+     the path, the return value is the size, in TCHARs, of the buffer
+     that is required to hold the path _and_the_terminating_null_character_`
+  */
+  len = GetFullPathName(filename, 0, NULL, NULL);
+
+  if ( len == 0 )
+    return strdup(filename);
+
+  buf = (char *)malloc(len);
+
+  /* no point to check the result again */
+  len = GetFullPathName(filename, len, buf, NULL);
+  buf[len] = 0;
+
+  /* replace slashes */
+  WIN32_REPLACE_SLASHES(buf, len);
+
+  /* calculate offset based on prefix type */
+  len = WIN32_IS_UNC_PREFIX(buf)
+    ? (WIN32_UNC_PREFIX_LEN - 2)
+    : WIN32_IS_NON_UNC_PREFIX(buf)
+      ? WIN32_NON_UNC_PREFIX_LEN
+      : 0
+  ;
+
+  ptr = buf + len;
+  if ( WIN32_IS_UNC_PREFIX(buf) ) {
+    ptr[0] = '/';
+    ptr[1] = '/';
+  }
+
+  res = strdup(ptr);
+
+  free(buf);
+
+  return res;
+}
+
+# if _WIN32_WINNT >= 0x0600
+
+/* Get full path name WITH symlinks resolution.
+   It also converts all forward slashes to back slashes.
+*/
+char* get_final_path_name(HANDLE fh) {
+  DWORD len;
+  char *buf, *ptr, *res;
+
+  /* determining the required buffer size.
+     from the  man: `If the function fails because lpszFilePath is too
+     small to hold the string plus the terminating null character,
+     the return value is the required buffer size, in TCHARs. This
+     value _includes_the_size_of_the_terminating_null_character_`.
+     but in my testcase I have path with 26 chars, the function
+     returns 26 also, ie without the trailing zero-char...
+  */
+  len = GetFinalPathNameByHandle(
+     fh
+    ,NULL
+    ,0
+    ,FILE_NAME_NORMALIZED | VOLUME_NAME_DOS
+  );
+
+  if ( len == 0 )
+    return NULL;
+
+  len += 1; /* for zero-char */
+  buf = (char *)malloc(len);
+
+  /* no point to check the result again */
+  len = GetFinalPathNameByHandle(
+     fh
+    ,buf
+    ,len
+    ,FILE_NAME_NORMALIZED | VOLUME_NAME_DOS
+  );
+  buf[len] = 0;
+
+  /* replace slashes */
+  WIN32_REPLACE_SLASHES(buf, len);
+
+  /* calculate offset based on prefix type */
+  len = WIN32_IS_UNC_PREFIX(buf)
+    ? (WIN32_UNC_PREFIX_LEN - 2)
+    : WIN32_IS_NON_UNC_PREFIX(buf)
+      ? WIN32_NON_UNC_PREFIX_LEN
+      : 0
+  ;
+
+  ptr = buf + len;
+  if ( WIN32_IS_UNC_PREFIX(buf) ) {
+    ptr[0] = '/';
+    ptr[1] = '/';
+  }
+
+  res = strdup(ptr);
+
+  free(buf);
+
+  return res;
+}
+
+# endif // _WIN32_WINNT >= 0x0600
+
+# endif // _WIN32
 #endif
 
 char *
@@ -128,30 +255,52 @@ lrealpath (const char *filename)
   }
 #endif
 
-  /* The MS Windows method.  If we don't have realpath, we assume we
-     don't have symlinks and just canonicalize to a Windows absolute
-     path.  GetFullPath converts ../ and ./ in relative paths to
-     absolute paths, filling in current drive if one is not given
-     or using the current directory of a specified drive (eg, "E:foo").
-     It also converts all forward slashes to back slashes.  */
+  /* The MS Windows method */
 #if defined (_WIN32)
   {
-    char buf[MAX_PATH];
-    char* basename;
-    DWORD len = GetFullPathName (filename, MAX_PATH, buf, &basename);
-    if (len == 0 || len > MAX_PATH - 1)
-      return strdup (filename);
-    else
-      {
-       /* The file system is case-preserving but case-insensitive,
-          Canonicalize to lowercase, using the codepage associated
-          with the process locale.  */
-        CharLowerBuff (buf, len);
-        return strdup (buf);
-      }
-  }
-#endif
+    char *res;
+
+    /* For Windows Vista and greater */
+#if _WIN32_WINNT >= 0x0600
+
+    /* For some reason the function receives just empty `filename`, but not NULL.
+       What should we do in that case?
+       According to `strdup()` implementation
+         (https://elixir.bootlin.com/glibc/latest/source/string/strdup.c)
+       it will alloc 1 byte even for empty but non NULL string.
+       OK, will use `strdup()` for that case.
+    */
+    if ( 0 == strlen(filename) )
+      return strdup(filename);
+
+    HANDLE fh = CreateFile(
+       filename
+      ,FILE_READ_ATTRIBUTES
+      ,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE
+      ,NULL
+      ,OPEN_EXISTING
+      ,FILE_FLAG_BACKUP_SEMANTICS
+      ,NULL
+    );
+
+    if ( fh == INVALID_HANDLE_VALUE ) {
+      res = get_full_path_name(filename);
+    } else {
+      res = get_final_path_name(fh);
+      CloseHandle(fh);
 
-  /* This system is a lost cause, just duplicate the filename.  */
-  return strdup (filename);
+      if ( !res )
+        res = get_full_path_name(filename);
+    }
+
+#else
+
+    /* For Windows XP */
+    res = get_full_path_name(filename);
+
+#endif // _WIN32_WINNT >= 0x0600
+
+    return res;
+  }
+#endif // _WIN32
 }