-/* $Id: path.c,v 1.21 2003/10/14 19:36:26 ekohl Exp $
+/* $Id: path.c,v 1.22 2003/10/18 20:32:58 navaraf Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS system libraries
#include <stdio.h>
#include <ctype.h>
#include <ddk/obfuncs.h>
+#include <ntos/rtl.h>
#define NDEBUG
#include <ntdll/ntdll.h>
/* FUNCTIONS *****************************************************************/
-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 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;
- p = wcschr(p + 1, L'\\');
- if (p == NULL)
- {
- break;
- }
- }
- 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--;
- }
- }
- }
- }
- if (Path[2] == 0)
- {
- Path[2] = L'\\';
- Path[3] = 0;
- }
-}
-
-
/*
* @implemented
*/
return STATUS_SUCCESS;
}
-/*
- * @implemented
+/******************************************************************
+ * get_full_path_helper
+ *
+ * Helper for RtlGetFullPathName_U
*/
-ULONG STDCALL
-RtlGetFullPathName_U(PWSTR DosName,
- ULONG size,
- PWSTR buf,
- PWSTR *FilePart)
+static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
{
- WCHAR *wcs, var[4], drive;
- ULONG len;
- ULONG templen = 0;
- DWORD offs, sz, type;
- UNICODE_STRING usvar, pfx;
- PCURDIR cd;
- NTSTATUS Status;
- WCHAR TempFullPathName[MAX_PATH] = L"";
-
- 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 = NULL;
- *buf = 0;
-
-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 = (PCURDIR)&(NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName);
-DPRINT("type %ld\n", type);
- switch (type)
- {
- case 1: /* \\xxx or \\.xxx */
- case 6: /* \\.\xxx */
- break;
-
- case 2: /* x:\xxx */
- *DosName = RtlUpcaseUnicodeChar (*DosName);
- break;
-
- case 3: /* x:xxx */
- drive = RtlUpcaseUnicodeChar (*DosName);
- DosName += 2;
- len -= 2;
-CHECKPOINT;
- if (drive == RtlUpcaseUnicodeChar (cd->DosPath.Buffer[0]))
- {
-CHECKPOINT;
- memcpy (TempFullPathName, cd->DosPath.Buffer, cd->DosPath.Length);
- templen = cd->DosPath.Length / sizeof(WCHAR);
- }
- else
- {
-CHECKPOINT;
- var[0] = L'=';
- var[1] = drive;
- var[2] = L':';
- var[3] = 0;
- usvar.Length = 3 * sizeof(WCHAR);
- usvar.MaximumLength = 4 * sizeof(WCHAR);
- usvar.Buffer = var;
- pfx.Length = 0;
- pfx.MaximumLength = MAX_PATH;
- pfx.Buffer = TempFullPathName;
- Status = RtlQueryEnvironmentVariable_U (NULL,
- &usvar,
- &pfx);
-CHECKPOINT;
- if (!NT_SUCCESS(Status))
- {
-CHECKPOINT;
- if (Status == STATUS_BUFFER_TOO_SMALL)
- return pfx.Length + len * 2 + 2;
-CHECKPOINT;
- TempFullPathName[0] = drive;
- TempFullPathName[1] = L':';
- TempFullPathName[2] = L'\\';
- TempFullPathName[3] = 0;
- templen = 3;
- }
- else
- {
-CHECKPOINT;
- templen = pfx.Length / sizeof(WCHAR);
- }
-
- }
- break;
-
- case 4: /* \xxx */
- wcsncpy (TempFullPathName, cd->DosPath.Buffer, 2);
- TempFullPathName[2] = 0;
- templen = wcslen(TempFullPathName);
- break;
-
- case 5: /* xxx */
- memcpy (TempFullPathName, cd->DosPath.Buffer, cd->DosPath.Length);
- templen = cd->DosPath.Length / sizeof(WCHAR);
- break;
-
- case 7: /* \\. */
- memcpy (TempFullPathName, L"\\\\.\\", 8);
- templen = 4;
- break;
-
- default:
- return 0;
- }
-
- RtlReleasePebLock();
-
- DPRINT("TempFullPathName \'%S\' DosName \'%S\' len %ld\n", TempFullPathName, DosName, len);
- /* add dosname to prefix */
- memcpy (TempFullPathName + templen, DosName, len * sizeof(WCHAR));
- len += templen;
- TempFullPathName[len] = 0;
-
- CHECKPOINT;
- /* replace slashes */
- wcs = wcschr(TempFullPathName, L'/');
- while(wcs)
- {
- *wcs = L'\\';
- wcs = wcschr(wcs + 1, L'/');
- }
-
- if (len == 2 && TempFullPathName[1] == L':')
- {
- TempFullPathName[len++] = L'\\';
- TempFullPathName[len] = 0;
- }
-
-
- DPRINT("TempFullPathName \'%S\'\n", TempFullPathName);
- RtlpEatPath (TempFullPathName);
- DPRINT("TempFullPathName \'%S\'\n", TempFullPathName);
-
- len = wcslen (TempFullPathName);
-
- if (len < (size / sizeof(WCHAR)))
- {
- memcpy (buf, TempFullPathName, (len + 1) * sizeof(WCHAR));
-
- /* find file part */
- if (FilePart)
- {
- *FilePart = wcsrchr(buf, L'\\');
- if (*FilePart)
- {
- (*FilePart)++;
- }
- else
- {
- *FilePart = buf;
- }
- }
+ ULONG reqsize, mark = 0;
+ /*DOS_PATHNAME_TYPE*/INT type;
+ LPWSTR ptr;
+ UNICODE_STRING* cd;
+
+ reqsize = sizeof(WCHAR); /* '\0' at the end */
+
+ RtlAcquirePebLock();
+ cd = &NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName;
+
+ switch (type = RtlDetermineDosPathNameType_U((LPWSTR)name))
+ {
+ case 1 /*UNC_PATH*/: /* \\foo */
+ case 6 /*DEVICE_PATH*/: /* \\.\foo */
+ if (reqsize <= size) buffer[0] = '\0';
+ break;
+
+ case 2 /*ABSOLUTE_DRIVE_PATH*/: /* c:\foo */
+ reqsize += sizeof(WCHAR);
+ if (reqsize <= size)
+ {
+ buffer[0] = RtlUpcaseUnicodeChar(name[0]);
+ buffer[1] = '\0';
+ }
+ name++;
+ break;
+
+ case 3 /*RELATIVE_DRIVE_PATH*/: /* c:foo */
+ if (RtlUpcaseUnicodeChar(name[0]) != RtlUpcaseUnicodeChar(cd->Buffer[0]) ||
+ cd->Buffer[1] != ':')
+ {
+ WCHAR drive[4];
+ UNICODE_STRING var, val;
+
+ drive[0] = '=';
+ drive[1] = name[0];
+ drive[2] = ':';
+ drive[3] = '\0';
+ var.Length = 6;
+ var.MaximumLength = 8;
+ var.Buffer = drive;
+ val.Length = 0;
+ val.MaximumLength = size;
+ val.Buffer = buffer;
+
+ name += 2;
+
+ 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;
+ /* append trailing \\ */
+ reqsize += sizeof(WCHAR);
+ if (reqsize <= size)
+ {
+ buffer[reqsize / sizeof(WCHAR) - 2] = '\\';
+ buffer[reqsize / sizeof(WCHAR) - 1] = '\0';
+ }
+ break;
+ case STATUS_VARIABLE_NOT_FOUND:
+ reqsize += 3 * sizeof(WCHAR);
+ if (reqsize <= size)
+ {
+ buffer[0] = drive[1];
+ buffer[1] = ':';
+ buffer[2] = '\\';
+ buffer[3] = '\0';
+ }
+ break;
+ default:
+ DPRINT("Unsupported status code\n");
+ break;
+ }
+ break;
+ }
+ name += 2;
+ /* fall through */
+
+ case 5 /*RELATIVE_PATH*/: /* foo */
+ reqsize += cd->Length;
+ if (reqsize <= size)
+ {
+ memcpy(buffer, cd->Buffer, cd->Length);
+ buffer[cd->Length / sizeof(WCHAR)] = 0;
}
+ if (cd->Buffer[1] != ':')
+ {
+ ptr = wcsrchr(cd->Buffer + 2, '\\');
+ if (ptr) ptr = wcsrchr(ptr + 1, '\\');
+ if (!ptr) ptr = cd->Buffer + wcslen(cd->Buffer);
+ mark = ptr - cd->Buffer;
+ }
+ break;
+
+ case 4 /*ABSOLUTE_PATH*/: /* \xxx */
+ if (cd->Buffer[1] == ':')
+ {
+ reqsize += 2 * sizeof(WCHAR);
+ if (reqsize <= size)
+ {
+ buffer[0] = cd->Buffer[0];
+ buffer[1] = ':';
+ buffer[2] = '\0';
+ }
+ }
+ else
+ {
+ unsigned len;
+
+ ptr = wcsrchr(cd->Buffer + 2, '\\');
+ if (ptr) ptr = wcsrchr(ptr + 1, '\\');
+ if (!ptr) ptr = cd->Buffer + wcslen(cd->Buffer);
+ len = (ptr - cd->Buffer) * sizeof(WCHAR);
+ reqsize += len;
+ mark = len / sizeof(WCHAR);
+ if (reqsize <= size)
+ {
+ memcpy(buffer, cd->Buffer, len);
+ buffer[len / sizeof(WCHAR)] = '\0';
+ }
+ else
+ buffer[0] = '\0';
+ }
+ break;
+
+ case 7 /*UNC_DOT_PATH*/: /* \\. */
+ reqsize += 4 * sizeof(WCHAR);
+ name += 3;
+ if (reqsize <= size)
+ {
+ buffer[0] = '\\';
+ buffer[1] = '\\';
+ buffer[2] = '.';
+ buffer[3] = '\\';
+ buffer[4] = '\0';
+ }
+ break;
+
+ case 0 /*INVALID_PATH*/:
+ reqsize = 0;
+ goto done;
+ }
+
+ reqsize += wcslen(name) * sizeof(WCHAR);
+ if (reqsize > size) goto done;
+
+ wcscat(buffer, name);
+
+ /* convert every / into a \ */
+ for (ptr = buffer; ptr < buffer + size / sizeof(WCHAR); ptr++)
+ if (*ptr == '/') *ptr = '\\';
+
+ reqsize -= sizeof(WCHAR); /* don't count trailing \0 */
+
+ /* mark is non NULL for UNC names, so start path collapsing after server & share name
+ * otherwise, it's a fully qualified DOS name, so start after the drive designation
+ */
+ for (ptr = buffer + (mark ? mark : 2); ptr < buffer + reqsize / sizeof(WCHAR); )
+ {
+ WCHAR* p = wcsrchr(ptr, '\\');
+ if (!p) break;
+
+ p++;
+ if (p[0] == '.')
+ {
+ switch (p[1])
+ {
+ case '.':
+ switch (p[2])
+ {
+ case '\\':
+ {
+ WCHAR* prev = p - 2;
+ while (prev >= buffer + mark && *prev != '\\') prev--;
+ /* either collapse \foo\.. into \ or \.. into \ */
+ if (prev < buffer + mark) prev = p - 1;
+ reqsize -= (p + 2 - prev) * sizeof(WCHAR);
+ memmove(prev, p + 2, buffer + reqsize - prev + sizeof(WCHAR));
+ p = prev;
+ }
+ break;
+ case '\0':
+ reqsize -= 2 * sizeof(WCHAR);
+ *p = 0;
+ break;
+ }
+ break;
+ case '\\':
+ reqsize -= 2 * sizeof(WCHAR);
+ memmove(ptr, ptr + 2, buffer + reqsize - ptr + sizeof(WCHAR));
+ break;
+ }
+ }
+ ptr = p;
+ }
- return len * sizeof(WCHAR);
+done:
+ RtlReleasePebLock();
+ return reqsize;
}
+/*
+ * @implemented
+ */
+DWORD STDCALL RtlGetFullPathName_U(WCHAR* name, ULONG size, WCHAR* buffer,
+ WCHAR** file_part)
+{
+ WCHAR* ptr;
+ DWORD dosdev;
+ DWORD reqsize;
+
+ DPRINT("(%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 = ((dosdev & 0xffff0000) >> 16) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
+ DWORD sz = dosdev & 0xffff; /* in bytes */
+
+ if (8 + sz + 2 > size) return sz + 10;
+ wcscpy(buffer, L"\\\\.\\");
+ 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 > 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;
+}
/*
* @unimplemented