- Make the drive letter to uppercase in RtlDosPathNameToNtPathName_U.
[reactos.git] / reactos / lib / ntdll / rtl / path.c
index 6a3ab18..3e60798 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: path.c,v 1.8 2001/03/08 22:48:42 dwelch Exp $
+/* $Id$
  *
  * COPYRIGHT:       See COPYING in the top level directory
  * PROJECT:         ReactOS system libraries
 
 #include <ddk/ntddk.h>
 #include <ntdll/rtl.h>
+#include <ntos/minmax.h>
 #include <string.h>
 #include <stdio.h>
 #include <ctype.h>
+#include <base.h>
+#include <ddk/obfuncs.h>
 
 #define NDEBUG
 #include <ntdll/ntdll.h>
 #define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
 
 
-/* FUNCTIONS *****************************************************************/
+/* GLOBALS ********************************************************************/
 
-static ULONG RtlpGetDotSequence (PWSTR p)
-{
-   ULONG Count = 0;
-   
-   for (;;)
-     {
-       if (*p == '.')
-         Count++;
-       else if ((*p == '\\' || *p == '\0') && Count)
-         return Count;
-       else
-         return 0;
-       p++;
-     }
-   return 0;
-}
+static const WCHAR DeviceRootW[] = L"\\\\.\\";
 
+static const UNICODE_STRING _condev = RTL_CONSTANT_STRING(L"\\\\.\\CON");
 
-static VOID RtlpEatPath (PWSTR Path)
-{
-   PWSTR p, prev;
-   
-   p = Path + 2;
-   prev = p;
-   
-   while ((*p) != 0 || ((*p) == L'\\' && (*(p+1)) == 0))
-     {
-       ULONG DotLen;
-       
-       DotLen = RtlpGetDotSequence (p+1);
-       DPRINT("DotSequenceLength %u\n", DotLen);
-       DPRINT("prev %S p %S\n",prev,p);
-       
-       if (DotLen == 0)
-         {
-            prev = p;
-            do
-              {
-                 p++;
-              }
-            while ((*p) != 0 && (*p) != L'\\');
-         }
-       else if (DotLen == 1)
-         {
-            wcscpy (p, p+2);
-         }
-       else
-         {
-            if (DotLen > 2)
-              {
-                 int n = DotLen - 2;
-                 
-                 while (n > 0 && prev > (Path + 2))
-                   {
-                      prev--;
-                      if ((*prev) == L'\\')
-                        n--;
-                   }
-              }
-            
-            if (*(p + DotLen + 1) == 0)
-              *(prev + 1) = 0;
-                       else
-              wcscpy (prev, p + DotLen + 1);
-            p = prev;
-            if (prev > (Path + 2))
-              {
-                 prev--;
-                 while ((*prev) != L'\\')
-                   {
-                      prev--;
-                   }
-              }
-         }
-     }
-}
+static const UNICODE_STRING _lpt = RTL_CONSTANT_STRING(L"LPT");
 
+static const UNICODE_STRING _com = RTL_CONSTANT_STRING(L"COM");
 
-ULONG STDCALL RtlGetLongestNtPathLength (VOID)
-{
-   return (MAX_PATH + 9);
-}
+static const UNICODE_STRING _prn = RTL_CONSTANT_STRING(L"PRN");
 
+static const UNICODE_STRING _aux = RTL_CONSTANT_STRING(L"AUX");
 
-ULONG
-STDCALL
-RtlDetermineDosPathNameType_U (
-       PWSTR Path
-       )
-{
-       DPRINT ("RtlDetermineDosPathNameType_U %S\n", Path);
-
-       if (Path == NULL)
-               return 0;
-
-       if (IS_PATH_SEPARATOR(Path[0]))
-       {
-               if (!IS_PATH_SEPARATOR(Path[1]))
-                       return 4;                       /* \xxx   */
+static const UNICODE_STRING _con = RTL_CONSTANT_STRING(L"CON");
 
-               if (Path[2] != L'.')
-                       return 1;                       /* \\xxx   */
+static const UNICODE_STRING _nul = RTL_CONSTANT_STRING(L"NUL");
 
-               if (IS_PATH_SEPARATOR(Path[3]))
-                       return 6;                       /* \\.\xxx */
+/* FUNCTIONS *****************************************************************/
 
-               if (Path[3])
-                       return 1;                       /* \\.xxxx */
 
-               return 7;                               /* \\.     */
-       }
-       else
-       {
-               if (Path[1] != L':')
-                       return 5;                       /* xxx     */
+/*
+ * @implemented
+ */
+ULONG STDCALL RtlGetLongestNtPathLength (VOID)
+{
+   return (MAX_PATH + 9);
+}
 
-               if (IS_PATH_SEPARATOR(Path[2]))
-                       return 2;                       /* x:\xxx  */
 
-               return 3;                               /* x:xxx   */
-       }
+/*
+ * @implemented
+ *
+ */
+ULONG STDCALL
+RtlDetermineDosPathNameType_U(PCWSTR Path)
+{
+   DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
+
+   if (Path == NULL)
+   {
+      return INVALID_PATH;
+   }
+
+   if (IS_PATH_SEPARATOR(Path[0]))
+   {
+      if (!IS_PATH_SEPARATOR(Path[1])) return ABSOLUTE_PATH;         /* \xxx   */
+      if (Path[2] != L'.') return UNC_PATH;                          /* \\xxx   */
+      if (IS_PATH_SEPARATOR(Path[3])) return DEVICE_PATH;            /* \\.\xxx */
+      if (Path[3]) return UNC_PATH;                                  /* \\.xxxx */
+
+      return UNC_DOT_PATH;                                           /* \\.     */
+   }
+   else
+   {
+      /* FIXME: the Wine version of this line reads:
+       * if (!Path[1] || Path[1] != L':')    return RELATIVE_PATH
+       * Should we do this too?
+       * -Gunnar
+       */ 
+      if (Path[1] != L':') return RELATIVE_PATH;                     /* xxx     */
+      if (IS_PATH_SEPARATOR(Path[2])) return ABSOLUTE_DRIVE_PATH;    /* x:\xxx  */
+
+      return RELATIVE_DRIVE_PATH;                                    /* x:xxx   */
+   }
 }
 
 
 /* returns 0 if name is not valid DOS device name, or DWORD with
  * offset in bytes to DOS device name from beginning of buffer in high word
  * and size in bytes of DOS device name in low word */
-ULONG
-STDCALL
-RtlIsDosDeviceName_U (
-       PWSTR DeviceName
-       )
-{
-       ULONG Type;
-       ULONG Length = 0;
-       ULONG Offset;
-       PWCHAR wc;
 
-       if (DeviceName == NULL)
-               return 0;
-
-       while (DeviceName[Length])
-               Length++;
+/*
+ * @implemented
+ */
+ULONG STDCALL
+RtlIsDosDeviceName_U(PWSTR DeviceName)
+{
+   ULONG Type;
+   ULONG Length = 0;
+   ULONG Offset;
+   PWCHAR wc;
+   UNICODE_STRING DeviceNameU;
 
-       Type = RtlDetermineDosPathNameType_U (DeviceName);
-       if (Type <= 1)
-               return 0;
-       if (Type == 6)
-       {
-               if (Length == 7 &&
-                   !_wcsnicmp (DeviceName, L"\\\\.\\CON", 7))
-                       return 0x00080006;
-               return 0;
-       }
+   if (DeviceName == NULL)
+     {
+       return 0;
+     }
 
-       /* name can end with ':' */
-       if (Length && DeviceName[Length - 1 ] == L':')
-               Length--;
+   while (DeviceName[Length])
+     {
+       Length++;
+     }
 
-       /* there can be spaces or points at the end of name */
-       wc = DeviceName + Length - 1;
-       while (Length && (*wc == L'.' || *wc == L' '))
-       {
-               Length--;
-               wc--;
-       }
+   Type = RtlDetermineDosPathNameType_U(DeviceName);
+   if (Type <= 1)
+     {
+       return 0;
+     }
 
-       /* let's find a beginning of name */
-       wc = DeviceName + Length - 1;
-       while (wc > DeviceName && !IS_PATH_SEPARATOR(*(wc - 1)))
-               wc--;
-       Offset = wc - DeviceName;
-       Length -= Offset;
+   if (Type == 6)
+     {
+        DeviceNameU.Length = DeviceNameU.MaximumLength = Length * sizeof(WCHAR);
+       DeviceNameU.Buffer = DeviceName;
+       if (Length == 7 &&
+           RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_condev, TRUE))
+               return 0x00080006;
+       return 0;
+     }
 
-       /* check for LPTx or COMx */
-       if (Length == 4 && wc[3] >= L'0' && wc[3] <= L'9')
-       {
-               if (wc[3] == L'0')
-                       return 0;
-               if (!_wcsnicmp (wc, L"LPT", 3) ||
-                   !_wcsnicmp (wc, L"COM", 3))
-               {
-                       return ((Offset * 2) << 16 ) | 8;
-               }
-               return 0;
-       }
+   /* name can end with ':' */
+   if (Length && DeviceName[Length - 1 ] == L':')
+     {
+       Length--;
+     }
 
-       /* check for PRN,AUX,NUL or CON */
-       if (Length == 3 &&
-           (!_wcsnicmp (wc, L"PRN", 3) ||
-            !_wcsnicmp (wc, L"AUX", 3) ||
-            !_wcsnicmp (wc, L"NUL", 3) ||
-            !_wcsnicmp (wc, L"CON", 3)))
-       {
-               return ((Offset * 2) << 16) | 6;
-       }
+   /* there can be spaces or points at the end of name */
+   wc = DeviceName + Length - 1;
+   while (Length && (*wc == L'.' || *wc == L' '))
+     {
+       Length--;
+       wc--;
+     }
 
+   /* let's find a beginning of name */
+   wc = DeviceName + Length - 1;
+   while (wc > DeviceName && !IS_PATH_SEPARATOR(*(wc - 1)))
+     {
+       wc--;
+     }
+   Offset = wc - DeviceName;
+   Length -= Offset;
+   DeviceNameU.Length = DeviceNameU.MaximumLength = 3 * sizeof(WCHAR);
+   DeviceNameU.Buffer = wc;
+   
+   /* check for LPTx or COMx */
+   if (Length == 4 && wc[3] >= L'0' && wc[3] <= L'9')
+     {
+       if (wc[3] == L'0')
+         {
+            return 0;
+         }
+   
+       if (RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_lpt, TRUE) ||
+           RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_com, TRUE))
+         {
+            return ((Offset * 2) << 16 ) | 8;
+         }
        return 0;
+     }
+   
+   /* check for PRN,AUX,NUL or CON */
+   if (Length == 3 &&
+       (RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_prn, TRUE) ||
+        RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_aux, TRUE) ||
+        RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_nul, TRUE) ||
+        RtlEqualUnicodeString(&DeviceNameU, (PUNICODE_STRING)&_con, TRUE)))
+     {
+       return ((Offset * 2) << 16) | 6;
+     }
+   
+   return 0;
 }
 
 
-ULONG
-STDCALL
-RtlGetCurrentDirectory_U (
-       ULONG MaximumLength,
-       PWSTR Buffer
-       )
+/*
+ * @implemented
+ */
+ULONG STDCALL
+RtlGetCurrentDirectory_U(ULONG MaximumLength,
+                        PWSTR Buffer)
 {
        ULONG Length;
        PCURDIR cd;
 
        DPRINT ("RtlGetCurrentDirectory %lu %p\n", MaximumLength, Buffer);
 
-       cd = &(NtCurrentPeb ()->ProcessParameters->CurrentDirectory);
-
        RtlAcquirePebLock();
+
+       cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName);
        Length = cd->DosPath.Length / sizeof(WCHAR);
        if (cd->DosPath.Buffer[Length - 1] == L'\\' &&
            cd->DosPath.Buffer[Length - 2] != L':')
@@ -272,9 +237,14 @@ RtlGetCurrentDirectory_U (
 }
 
 
-NTSTATUS STDCALL RtlSetCurrentDirectory_U (PUNICODE_STRING name)
+/*
+ * @implemented
+ */
+NTSTATUS STDCALL
+RtlSetCurrentDirectory_U(PUNICODE_STRING name)
 {
    UNICODE_STRING full;
+   UNICODE_STRING envvar;
    OBJECT_ATTRIBUTES Attr;
    IO_STATUS_BLOCK iosb;
    PCURDIR cd;
@@ -283,13 +253,17 @@ NTSTATUS STDCALL RtlSetCurrentDirectory_U (PUNICODE_STRING name)
    HANDLE handle = NULL;
    PWSTR wcs;
    PWSTR buf = 0;
+   PFILE_NAME_INFORMATION filenameinfo;
+   ULONG backslashcount = 0;
+   ULONG Index;
+   WCHAR var[4];
    
    DPRINT ("RtlSetCurrentDirectory %wZ\n", name);
-
+   
    RtlAcquirePebLock ();
-   cd = &NtCurrentPeb ()->ProcessParameters->CurrentDirectory;
+   cd = (PCURDIR)&NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName;
+
    size = cd->DosPath.MaximumLength;
-   
    buf = RtlAllocateHeap (RtlGetProcessHeap(),
                          0,
                          size);
@@ -344,6 +318,65 @@ NTSTATUS STDCALL RtlSetCurrentDirectory_U (PUNICODE_STRING name)
        RtlReleasePebLock ();
        return Status;
      }
+
+   filenameinfo = RtlAllocateHeap(RtlGetProcessHeap(),
+                                 0,
+                                 MAX_PATH*sizeof(WCHAR)+sizeof(ULONG));
+   
+   Status = NtQueryInformationFile(handle,
+                                  &iosb,
+                                  filenameinfo,
+                                  MAX_PATH*sizeof(WCHAR)+sizeof(ULONG),
+                                  FileNameInformation);
+   if (!NT_SUCCESS(Status))
+     {
+       RtlFreeHeap(RtlGetProcessHeap(),
+                   0,
+                   filenameinfo);
+       RtlFreeHeap(RtlGetProcessHeap(),
+                   0,
+                   buf);
+       RtlFreeHeap(RtlGetProcessHeap(),
+                   0,
+                   full.Buffer);
+       RtlReleasePebLock();
+       return(Status);
+     }
+   
+   /* If it's just "\", we need special handling */
+   if (filenameinfo->FileNameLength > sizeof(WCHAR))
+     {
+       wcs = buf + size / sizeof(WCHAR) - 1;
+       if (*wcs == L'\\')
+         {
+           *(wcs) = 0;
+           wcs--;
+           size -= sizeof(WCHAR);
+         }
+
+       for (Index = 0;
+            Index < filenameinfo->FileNameLength / sizeof(WCHAR);
+            Index++)
+         {
+            if (filenameinfo->FileName[Index] == '\\') backslashcount++;
+         }
+
+       DPRINT("%d \n",backslashcount);
+       for (;backslashcount;wcs--)
+         {
+            if (*wcs=='\\') backslashcount--;
+         }
+       wcs++;
+
+       RtlCopyMemory(wcs, filenameinfo->FileName, filenameinfo->FileNameLength);
+       wcs[filenameinfo->FileNameLength / sizeof(WCHAR)] = 0;
+
+       size = (wcs - buf) * sizeof(WCHAR) + filenameinfo->FileNameLength;
+     }
+   
+   RtlFreeHeap (RtlGetProcessHeap (),
+               0,
+               filenameinfo);
    
    /* append backslash if missing */
    wcs = buf + size / sizeof(WCHAR) - 1;
@@ -354,14 +387,25 @@ NTSTATUS STDCALL RtlSetCurrentDirectory_U (PUNICODE_STRING name)
        size += sizeof(WCHAR);
      }
    
-   memmove (cd->DosPath.Buffer,
-           buf,
-           size + sizeof(WCHAR));
+   memmove(cd->DosPath.Buffer,
+          buf,
+          size + sizeof(WCHAR));
    cd->DosPath.Length = size;
-   
+
    if (cd->Handle)
-     NtClose (cd->Handle);
+     NtClose(cd->Handle);
    cd->Handle = handle;
+
+   if (cd->DosPath.Buffer[1]==':')
+     {
+       envvar.Length = 2 * swprintf (var, L"=%c:", cd->DosPath.Buffer[0]);
+       envvar.MaximumLength = 8;
+       envvar.Buffer = var;
+   
+       RtlSetEnvironmentVariable(NULL,
+                                 &envvar,
+                                 &cd->DosPath);
+   }
    
    RtlFreeHeap (RtlGetProcessHeap (),
                0,
@@ -377,192 +421,360 @@ NTSTATUS STDCALL RtlSetCurrentDirectory_U (PUNICODE_STRING name)
 }
 
 
-ULONG
-STDCALL
-RtlGetFullPathName_U (
-       PWSTR DosName,
-       ULONG size,
-       PWSTR buf,
-       PWSTR *FilePart
-       )
-{
-       WCHAR           *wcs, var[4], drive;
-       int             len;
-       DWORD           offs, sz, type;
-       UNICODE_STRING  usvar, pfx;
-       PCURDIR cd;
-       NTSTATUS Status;
-
-       DPRINT("RtlGetFullPathName_U %S %ld %p %p\n",
-              DosName, size, buf, FilePart);
-
-       if (!DosName || !*DosName)
-               return 0;
-
-       len = wcslen (DosName);
 
-       /* strip trailing spaces */
-       while (len && DosName[len - 1] == L' ')
-               len--;
-       if (!len)
-               return 0;
-
-       /* strip trailing path separator (but don't change '\') */
-       if ((len > 1) &&
-           IS_PATH_SEPARATOR(DosName[len - 1]))
-               len--;
-       if (FilePart)
-               *FilePart = L'\0';
-       memset (buf, 0, size);
-
-CHECKPOINT;
-       /* check for DOS device name */
-       sz = RtlIsDosDeviceName_U (DosName);
-       if (sz)
-       {
-               offs = sz >> 17;
-               sz &= 0x0000FFFF;
-               if (sz + 8 >= size)
-                       return sz + 10;
-               wcscpy (buf, L"\\\\.\\");
-               wcsncat (buf, DosName + offs, sz / sizeof(WCHAR));
-               return sz + 8;
-       }
-
-CHECKPOINT;
-       type = RtlDetermineDosPathNameType_U (DosName);
-
-       RtlAcquirePebLock();
-
-       cd = &(NtCurrentPeb ()->ProcessParameters->CurrentDirectory);
-DPRINT("type %ld\n", type);
-       switch (type)
-       {
-               case 1:         /* \\xxx or \\.xxx */
-               case 6:         /* \\.\xxx */
-                       break;
-
-               case 2:         /* x:\xxx  */
-                       *DosName = towupper (*DosName);
-                       break;
-
-               case 3:         /* x:xxx   */
-                       drive = towupper (*DosName);
-                       DosName += 2;
-                       len     -= 2;
-CHECKPOINT;
-                       if (drive == towupper (cd->DosPath.Buffer[0]))
-                       {
-CHECKPOINT;
-                               wcscpy (buf, cd->DosPath.Buffer);
-                       }
-                       else
-                       {
-CHECKPOINT;
-                               usvar.Length = 2 * swprintf (var, L"=%c:", drive);
-                               usvar.MaximumLength = 8;
-                               usvar.Buffer = var;
-                               pfx.Length = 0;
-                               pfx.MaximumLength = size;
-                               pfx.Buffer = buf;
-                               Status = RtlQueryEnvironmentVariable_U (NULL,
-                                                                       &usvar,
-                                                                       &pfx);
-CHECKPOINT;
-                               if (!NT_SUCCESS(Status))
-                               {
-CHECKPOINT;
-                                       if (Status == STATUS_BUFFER_TOO_SMALL)
-                                               return pfx.Length + len * 2 + 2;
-                                       swprintf (buf, L"%c:\\", drive);
-                               }
-                               else
-                               {
-CHECKPOINT;
-                                       if (pfx.Length > 6)
-                                       {
-CHECKPOINT;
-                                               buf[pfx.Length / 2] = L'\\';
-                                               pfx.Length += 2;
-                                       }
-                               }
-                       }
-                       break;
-
-               case 4:         /* \xxx    */
-                       wcsncpy (buf, cd->DosPath.Buffer, 2);
-                       break;
-
-               case 5:         /* xxx     */
-                       wcscpy (buf, cd->DosPath.Buffer);
-                       break;
-
-               case 7:         /* \\.     */
-                       wcscpy (buf, L"\\\\.\\");
-                       len = 0;
-                       break;
-
-               default:
-                       return 0;
-       }
-
-       DPRINT("buf \'%S\' DosName \'%S\' len %ld\n", buf, DosName, len);
-       /* add dosname to prefix */
-       wcsncat (buf, DosName, len);
+/******************************************************************
+ *    collapse_path
+ *
+ * Helper for RtlGetFullPathName_U.
+ * 1) Convert slashes into backslashes
+ * 2) Get rid of duplicate backslashes
+ * 3) Get rid of . and .. components in the path.
+ */
+static inline void collapse_path( WCHAR *path, UINT mark )
+{
+    WCHAR *p, *next;
+
+    /* convert every / into a \ */
+    for (p = path; *p; p++) if (*p == '/') *p = '\\';
+
+    /* collapse duplicate backslashes */
+    next = path + max( 1, mark );
+    for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
+    *next = 0;
+
+    p = path + mark;
+    while (*p)
+    {
+        if (*p == '.')
+        {
+            switch(p[1])
+            {
+            case '\\': /* .\ component */
+                next = p + 2;
+                memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
+                continue;
+            case 0:  /* final . */
+                if (p > path + mark) p--;
+                *p = 0;
+                continue;
+            case '.':
+                if (p[2] == '\\')  /* ..\ component */
+                {
+                    next = p + 3;
+                    if (p > path + mark)
+                    {
+                        p--;
+                        while (p > path + mark && p[-1] != '\\') p--;
+                    }
+                    memmove( p, next, (wcslen(next) + 1) * sizeof(WCHAR) );
+                    continue;
+                }
+                else if (!p[2])  /* final .. */
+                {
+                    if (p > path + mark)
+                    {
+                        p--;
+                        while (p > path + mark && p[-1] != '\\') p--;
+                        if (p > path + mark) p--;
+                    }
+                    *p = 0;
+                    continue;
+                }
+                break;
+            }
+        }
+        /* skip to the next component */
+        while (*p && *p != '\\') p++;
+        if (*p == '\\') p++;
+    }
+
+    /* remove trailing spaces and dots (yes, Windows really does that, don't ask) */
+    while (p > path + mark && (p[-1] == ' ' || p[-1] == '.')) p--;
+    *p = 0;
+}
 
-       CHECKPOINT;
-       /* replace slashes */
-       for (wcs = buf; *wcs; wcs++)
-               if (*wcs == L'/')
-                       *wcs = L'\\';
 
-       len = wcslen (buf);
-       if (len < 3 && buf[len-1] == L':')
-               wcscat (buf, L"\\");
 
-       DPRINT("buf \'%S\'\n", buf);
-       RtlpEatPath (buf);
-       DPRINT("buf \'%S\'\n", buf);
+/******************************************************************
+ *    skip_unc_prefix
+ *
+ * Skip the \\share\dir\ part of a file name. Helper for RtlGetFullPathName_U.
+ */
+static const WCHAR *skip_unc_prefix( const WCHAR *ptr )
+{
+    ptr += 2;
+    while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++;  /* share name */
+    while (IS_PATH_SEPARATOR(*ptr)) ptr++;
+    while (*ptr && !IS_PATH_SEPARATOR(*ptr)) ptr++;  /* dir name */
+    while (IS_PATH_SEPARATOR(*ptr)) ptr++;
+    return ptr;
+}
 
-       len = wcslen (buf);
 
-       /* find file part */
-       if (FilePart)
-       {
-               for (wcs = buf + len - 1; wcs >= buf; wcs--)
-               {
-                       if (*wcs == L'\\')
-                       {
-                               *FilePart = wcs + 1;
-                               break;
-                       }
-               }
-       }
+/******************************************************************
+ *    get_full_path_helper
+ *
+ * Helper for RtlGetFullPathName_U
+ * Note: name and buffer are allowed to point to the same memory spot
+ */
+static ULONG get_full_path_helper(
+   LPCWSTR name, 
+   LPWSTR buffer, 
+   ULONG size)
+{
+    ULONG                       reqsize = 0, mark = 0, dep = 0, deplen;
+    DOS_PATHNAME_TYPE           type;
+    LPWSTR                      ins_str = NULL;
+    LPCWSTR                     ptr;
+    const UNICODE_STRING*       cd;
+    WCHAR                       tmp[4];
+
+    /* return error if name only consists of spaces */
+    for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
+    if (!*ptr) return 0;
+
+    RtlAcquirePebLock();
+
+    cd = &((PCURDIR)&NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectoryName)->DosPath;
+
+    switch (type = RtlDetermineDosPathNameType_U(name))
+    {
+    case UNC_PATH:              /* \\foo   */
+        ptr = skip_unc_prefix( name );
+        mark = (ptr - name);
+        break;
+
+    case DEVICE_PATH:           /* \\.\foo */
+        mark = 4;
+        break;
+
+    case ABSOLUTE_DRIVE_PATH:   /* c:\foo  */
+        reqsize = sizeof(WCHAR);
+        tmp[0] = towupper(name[0]);
+        ins_str = tmp;
+        dep = 1;
+        mark = 3;
+        break;
+
+    case RELATIVE_DRIVE_PATH:   /* c:foo   */
+        dep = 2;
+        if (towupper(name[0]) != towupper(cd->Buffer[0]) || cd->Buffer[1] != ':')
+        {
+            UNICODE_STRING      var, val;
+
+            tmp[0] = '=';
+            tmp[1] = name[0];
+            tmp[2] = ':';
+            tmp[3] = '\0';
+            var.Length = 3 * sizeof(WCHAR);
+            var.MaximumLength = 4 * sizeof(WCHAR);
+            var.Buffer = tmp;
+            val.Length = 0;
+            val.MaximumLength = size;
+            val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
+
+            switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
+            {
+            case STATUS_SUCCESS:
+                /* FIXME: Win2k seems to check that the environment variable actually points 
+                 * to an existing directory. If not, root of the drive is used
+                 * (this seems also to be the only spot in RtlGetFullPathName that the 
+                 * existence of a part of a path is checked)
+                 */
+                /* fall thru */
+            case STATUS_BUFFER_TOO_SMALL:
+                reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
+                val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
+                ins_str = val.Buffer;
+                break;
+            case STATUS_VARIABLE_NOT_FOUND:
+                reqsize = 3 * sizeof(WCHAR);
+                tmp[0] = name[0];
+                tmp[1] = ':';
+                tmp[2] = '\\';
+                ins_str = tmp;
+                break;
+            default:
+                DPRINT1("Unsupported status code\n");
+                break;
+            }
+            mark = 3;
+            break;
+        }
+        /* fall through */
+
+    case RELATIVE_PATH:         /* foo     */
+        reqsize = cd->Length;
+        ins_str = cd->Buffer;
+        if (cd->Buffer[1] != ':')
+        {
+            ptr = skip_unc_prefix( cd->Buffer );
+            mark = ptr - cd->Buffer;
+        }
+        else mark = 3;
+        break;
+
+    case ABSOLUTE_PATH:         /* \xxx    */
+#ifdef __WINE__
+        if (name[0] == '/')  /* may be a Unix path */
+        {
+            const WCHAR *ptr = name;
+            int drive = find_drive_root( &ptr );
+            if (drive != -1)
+            {
+                reqsize = 3 * sizeof(WCHAR);
+                tmp[0] = 'A' + drive;
+                tmp[1] = ':';
+                tmp[2] = '\\';
+                ins_str = tmp;
+                mark = 3;
+                dep = ptr - name;
+                break;
+            }
+        }
+#endif
+        if (cd->Buffer[1] == ':')
+        {
+            reqsize = 2 * sizeof(WCHAR);
+            tmp[0] = cd->Buffer[0];
+            tmp[1] = ':';
+            ins_str = tmp;
+            mark = 3;
+        }
+        else
+        {
+            ptr = skip_unc_prefix( cd->Buffer );
+            reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
+            mark = reqsize / sizeof(WCHAR);
+            ins_str = cd->Buffer;
+        }
+        break;
+
+    case UNC_DOT_PATH:         /* \\.     */
+        reqsize = 4 * sizeof(WCHAR);
+        dep = 3;
+        tmp[0] = '\\';
+        tmp[1] = '\\';
+        tmp[2] = '.';
+        tmp[3] = '\\';
+        ins_str = tmp;
+        mark = 4;
+        break;
+
+    case INVALID_PATH:
+        goto done;
+    }
+
+    /* enough space ? */
+    deplen = wcslen(name + dep) * sizeof(WCHAR);
+    if (reqsize + deplen + sizeof(WCHAR) > size)
+    {
+        /* not enough space, return need size (including terminating '\0') */
+        reqsize += deplen + sizeof(WCHAR);
+        goto done;
+    }
+
+    memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
+    if (reqsize) memcpy(buffer, ins_str, reqsize);
+    reqsize += deplen;
+
+    if (ins_str && ins_str != tmp && ins_str != cd->Buffer)
+        RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
+
+    collapse_path( buffer, mark );
+    reqsize = wcslen(buffer) * sizeof(WCHAR);
+
+done:
+    RtlReleasePebLock();
+    return reqsize;
+}
 
-       RtlReleasePebLock();
 
-       return len * sizeof(WCHAR);
+/******************************************************************
+ *    RtlGetFullPathName_U  (NTDLL.@)
+ *
+ * Returns the number of bytes written to buffer (not including the
+ * terminating NULL) if the function succeeds, or the required number of bytes
+ * (including the terminating NULL) if the buffer is too small.
+ *
+ * file_part will point to the filename part inside buffer (except if we use
+ * DOS device name, in which case file_in_buf is NULL)
+ *
+ * @implemented
+ */
+DWORD STDCALL RtlGetFullPathName_U(
+   const WCHAR* name, 
+   ULONG size, 
+   WCHAR* buffer,
+   WCHAR** file_part)
+{
+    WCHAR*      ptr;
+    DWORD       dosdev;
+    DWORD       reqsize;
+
+    DPRINT("RtlGetFullPathName_U(%S %lu %p %p)\n", name, size, buffer, file_part);
+
+    if (!name || !*name) return 0;
+
+    if (file_part) *file_part = NULL;
+
+    /* check for DOS device name */
+    dosdev = RtlIsDosDeviceName_U((WCHAR*)name);
+    if (dosdev)
+    {
+        DWORD   offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
+        DWORD   sz = LOWORD(dosdev); /* in bytes */
+
+        if (8 + sz + 2 > size) return sz + 10;
+        wcscpy(buffer, DeviceRootW);
+        memmove(buffer + 4, name + offset, sz);
+        buffer[4 + sz / sizeof(WCHAR)] = '\0';
+        /* file_part isn't set in this case */
+        return sz + 8;
+    }
+
+    reqsize = get_full_path_helper(name, buffer, size);
+    if (!reqsize) return 0;
+    if (reqsize > size)
+    {
+        LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize);
+        reqsize = get_full_path_helper(name, tmp, reqsize);
+        if (reqsize > size)  /* it may have worked the second time */
+        {
+            RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
+            return reqsize + sizeof(WCHAR);
+        }
+        memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
+        RtlFreeHeap(RtlGetProcessHeap(), 0, tmp);
+    }
+
+    /* find file part */
+    if (file_part && (ptr = wcsrchr(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
+        *file_part = ptr;
+    return reqsize;
 }
 
 
-BOOLEAN
-STDCALL
-RtlDosPathNameToNtPathName_U (
-       PWSTR           dosname,
-       PUNICODE_STRING ntname,
-       PWSTR           *FilePart,
-       PCURDIR         nah
-       )
+/*
+ * @implemented
+ */
+BOOLEAN STDCALL
+RtlDosPathNameToNtPathName_U(PWSTR dosname,
+                            PUNICODE_STRING ntname,
+                            PWSTR *FilePart,
+                            PCURDIR nah)
 {
        UNICODE_STRING  us;
        PCURDIR cd;
        ULONG Type;
        ULONG Size;
        ULONG Length;
+       ULONG tmpLength;
        ULONG Offset;
-       WCHAR fullname[2*MAX_PATH];
+       WCHAR fullname[MAX_PATH + 1];
        PWSTR Buffer = NULL;
 
+
        RtlAcquirePebLock ();
 
        RtlInitUnicodeString (&us, dosname);
@@ -608,13 +820,15 @@ RtlDosPathNameToNtPathName_U (
 
        /* Set NT prefix */
        Offset = 0;
-       wcscpy (Buffer, L"\\??\\");
+       memcpy (Buffer, L"\\??\\", 4 * sizeof(WCHAR));
+       tmpLength = 4;
 
        Type = RtlDetermineDosPathNameType_U (fullname);
        switch (Type)
        {
                case 1:
-                       wcscat (Buffer, L"UNC\\");
+                       memcpy (Buffer + tmpLength, L"UNC\\", 4 * sizeof(WCHAR));
+                       tmpLength += 4;
                        Offset = 2;
                        break; /* \\xxx   */
 
@@ -622,8 +836,15 @@ RtlDosPathNameToNtPathName_U (
                        Offset = 4;
                        break; /* \\.\xxx */
        }
-       wcscat (Buffer, fullname + Offset);
-       Length = wcslen (Buffer);
+       Length = wcslen(fullname + Offset);
+       memcpy (Buffer + tmpLength, fullname + Offset, (Length + 1) * sizeof(WCHAR));
+       Length += tmpLength;
+       if (Type == ABSOLUTE_DRIVE_PATH ||
+           Type == RELATIVE_DRIVE_PATH)
+       {
+           /* make the drive letter to uppercase */
+           Buffer[tmpLength] = towupper(Buffer[tmpLength]);
+       }
 
        /* set NT filename */
        ntname->Length        = Length * sizeof(WCHAR);
@@ -638,24 +859,29 @@ RtlDosPathNameToNtPathName_U (
        if (nah)
        {
                memset (nah, 0, sizeof(CURDIR));
-               cd = &(NtCurrentPeb ()->ProcessParameters->CurrentDirectory);
-               if (Type == 5 && cd->Handle &&
-                   !_wcsnicmp (cd->DosPath.Buffer, fullname, cd->DosPath.Length / 2))
+               cd = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName);
+               if (Type == 5 && cd->Handle)
                {
+                   RtlInitUnicodeString(&us, fullname);
+                   if (RtlEqualUnicodeString(&us, &cd->DosPath, TRUE))
+                   {
                        Length = ((cd->DosPath.Length / sizeof(WCHAR)) - Offset) + ((Type == 1) ? 8 : 4);
                        nah->DosPath.Buffer = Buffer + Length;
                        nah->DosPath.Length = ntname->Length - (Length * sizeof(WCHAR));
                        nah->DosPath.MaximumLength = nah->DosPath.Length;
                        nah->Handle = cd->Handle;
+                   }
                }
        }
 
        RtlReleasePebLock();
-
        return TRUE;
 }
 
 
+/*
+ * @implemented
+ */
 ULONG
 STDCALL
 RtlDosSearchPath_U (
@@ -730,79 +956,11 @@ RtlDosSearchPath_U (
 }
 
 
-BOOLEAN
-STDCALL
-RtlIsNameLegalDOS8Dot3 (
-       PUNICODE_STRING UnicodeName,
-       PANSI_STRING    AnsiName,
-       PBOOLEAN        SpacesFound
-       )
-{
-       PANSI_STRING name = AnsiName;
-       ANSI_STRING DummyString;
-       CHAR Buffer[12];
-       char *str;
-       ULONG Length;
-       ULONG i;
-       NTSTATUS Status;
-       BOOLEAN HasSpace = FALSE;
-       BOOLEAN HasDot = FALSE;
-
-       if (UnicodeName->Length > 24)
-               return FALSE; /* name too long */
-
-       if (!name)
-       {
-               name = &DummyString;
-               name->Length = 0;
-               name->MaximumLength = 12;
-               name->Buffer = Buffer;
-       }
-
-       Status = RtlUpcaseUnicodeStringToCountedOemString (name,
-                                                          UnicodeName,
-                                                          FALSE);
-       if (!NT_SUCCESS(Status))
-               return FALSE;
-
-       Length = name->Length;
-       str = name->Buffer;
-
-       if (!(Length == 1 && *str == '.') &&
-           !(Length == 2 && *str == '.' && *(str + 1) == '.'))
-       {
-               for (i = 0; i < Length; i++, str++)
-               {
-                       switch (*str)
-                       {
-                               case ' ':
-                                       HasSpace = TRUE;
-                                       break;
-
-                               case '.':
-                                       if ((HasDot) ||         /* two points */
-                                           (!i) ||             /* point is first char */
-                                           (i + 1 == Length) ||/* point is last char */
-                                           (Length - i > 4))   /* more than 3 chars of extension */
-                                               return FALSE;
-                                       HasDot = TRUE;
-                                       break;
-                       }
-               }
-       }
-
-       if (SpacesFound)
-               *SpacesFound = HasSpace;
-
-       return TRUE;
-}
-
-
-BOOLEAN
-STDCALL
-RtlDoesFileExists_U (
-       IN      PWSTR   FileName
-       )
+/*
+ * @implemented
+ */
+BOOLEAN STDCALL
+RtlDoesFileExists_U(IN PWSTR FileName)
 {
        UNICODE_STRING NtFileName;
        OBJECT_ATTRIBUTES Attr;
@@ -860,4 +1018,18 @@ RtlDoesFileExists_U (
        return FALSE;
 }
 
+NTSTATUS STDCALL
+RtlpEnsureBufferSize(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3)
+{
+    DPRINT1("RtlpEnsureBufferSize: stub\n");
+    return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS STDCALL
+RtlNtPathNameToDosPathName(ULONG Unknown1, ULONG Unknown2, ULONG Unknown3, ULONG Unknown4)
+{
+    DPRINT1("RtlNtPathNameToDosPathName: stub\n");
+    return STATUS_NOT_IMPLEMENTED;
+}
+
 /* EOF */