-/* $Id: path.c,v 1.22 2003/10/18 20:32:58 navaraf 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>
-#include <ntos/rtl.h>
#define NDEBUG
#include <ntdll/ntdll.h>
#define IS_PATH_SEPARATOR(x) (((x)==L'\\')||((x)==L'/'))
+
/* GLOBALS ********************************************************************/
-static const UNICODE_STRING _condev =
-{
- .Length = sizeof(L"\\\\.\\CON") - sizeof(WCHAR),
- .MaximumLength = sizeof(L"\\\\.\\CON"),
- .Buffer = L"\\\\.\\CON"
-};
+static const WCHAR DeviceRootW[] = L"\\\\.\\";
-static const UNICODE_STRING _lpt =
-{
- .Length = sizeof(L"LPT") - sizeof(WCHAR),
- .MaximumLength = sizeof(L"LPT"),
- .Buffer = L"LPT"
-};
+static const UNICODE_STRING _condev = RTL_CONSTANT_STRING(L"\\\\.\\CON");
-static const UNICODE_STRING _com =
-{
- .Length = sizeof(L"COM") - sizeof(WCHAR),
- .MaximumLength = sizeof(L"COM"),
- .Buffer = L"COM"
-};
+static const UNICODE_STRING _lpt = RTL_CONSTANT_STRING(L"LPT");
-static const UNICODE_STRING _prn =
-{
- .Length = sizeof(L"PRN") - sizeof(WCHAR),
- .MaximumLength = sizeof(L"PRN"),
- .Buffer = L"PRN"
-};
+static const UNICODE_STRING _com = RTL_CONSTANT_STRING(L"COM");
-static const UNICODE_STRING _aux =
-{
- .Length = sizeof(L"AUX") - sizeof(WCHAR),
- .MaximumLength = sizeof(L"AUX"),
- .Buffer = L"AUX"
-};
+static const UNICODE_STRING _prn = RTL_CONSTANT_STRING(L"PRN");
-static const UNICODE_STRING _con =
-{
- .Length = sizeof(L"CON") - sizeof(WCHAR),
- .MaximumLength = sizeof(L"CON"),
- .Buffer = L"CON"
-};
+static const UNICODE_STRING _aux = RTL_CONSTANT_STRING(L"AUX");
-static const UNICODE_STRING _nul =
-{
- .Length = sizeof(L"NUL") - sizeof(WCHAR),
- .MaximumLength = sizeof(L"NUL"),
- .Buffer = L"NUL"
-};
+static const UNICODE_STRING _con = RTL_CONSTANT_STRING(L"CON");
+
+static const UNICODE_STRING _nul = RTL_CONSTANT_STRING(L"NUL");
/* FUNCTIONS *****************************************************************/
+
/*
* @implemented
*/
/*
* @implemented
+ *
*/
ULONG STDCALL
-RtlDetermineDosPathNameType_U(PWSTR Path)
+RtlDetermineDosPathNameType_U(PCWSTR Path)
{
DPRINT("RtlDetermineDosPathNameType_U %S\n", Path);
if (Path == NULL)
- {
- return 0;
- }
+ {
+ return INVALID_PATH;
+ }
if (IS_PATH_SEPARATOR(Path[0]))
- {
- if (!IS_PATH_SEPARATOR(Path[1]))
- {
- return 4; /* \xxx */
- }
-
- if (Path[2] != L'.')
- return 1; /* \\xxx */
-
- if (IS_PATH_SEPARATOR(Path[3]))
- return 6; /* \\.\xxx */
-
- if (Path[3])
- return 1; /* \\.xxxx */
+ {
+ 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 7; /* \\. */
- }
+ return UNC_DOT_PATH; /* \\. */
+ }
else
- {
- if (Path[1] != L':')
- return 5; /* xxx */
-
- if (IS_PATH_SEPARATOR(Path[2]))
- return 2; /* x:\xxx */
-
- return 3; /* x:xxx */
+ {
+ /* 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 */
}
}
PWSTR buf = 0;
PFILE_NAME_INFORMATION filenameinfo;
ULONG backslashcount = 0;
- PWSTR cntr;
+ ULONG Index;
WCHAR var[4];
DPRINT ("RtlSetCurrentDirectory %wZ\n", name);
RtlReleasePebLock ();
return Status;
}
-
+
filenameinfo = RtlAllocateHeap(RtlGetProcessHeap(),
0,
MAX_PATH*sizeof(WCHAR)+sizeof(ULONG));
return(Status);
}
- if (filenameinfo->FileName[1]) // If it's just "\", we need special handling
+ /* If it's just "\", we need special handling */
+ if (filenameinfo->FileNameLength > sizeof(WCHAR))
{
wcs = buf + size / sizeof(WCHAR) - 1;
if (*wcs == L'\\')
size -= sizeof(WCHAR);
}
- for (cntr=filenameinfo->FileName;*cntr!=0;cntr++)
+ for (Index = 0;
+ Index < filenameinfo->FileNameLength / sizeof(WCHAR);
+ Index++)
{
- if (*cntr=='\\') backslashcount++;
+ if (filenameinfo->FileName[Index] == '\\') backslashcount++;
}
DPRINT("%d \n",backslashcount);
}
wcs++;
- wcscpy(wcs,filenameinfo->FileName);
+ RtlCopyMemory(wcs, filenameinfo->FileName, filenameinfo->FileNameLength);
+ wcs[filenameinfo->FileNameLength / sizeof(WCHAR)] = 0;
- size=((wcs-buf)+wcslen(filenameinfo->FileName))*sizeof(WCHAR);
+ size = (wcs - buf) * sizeof(WCHAR) + filenameinfo->FileNameLength;
}
RtlFreeHeap (RtlGetProcessHeap (),
buf,
size + sizeof(WCHAR));
cd->DosPath.Length = size;
-
+
if (cd->Handle)
NtClose(cd->Handle);
cd->Handle = handle;
return STATUS_SUCCESS;
}
+
+
+/******************************************************************
+ * 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;
+}
+
+
+
/******************************************************************
- * get_full_path_helper
+ * 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;
+}
+
+
+/******************************************************************
+ * 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)
+static ULONG get_full_path_helper(
+ LPCWSTR name,
+ LPWSTR buffer,
+ ULONG size)
{
- ULONG reqsize, mark = 0;
- /*DOS_PATHNAME_TYPE*/INT type;
- LPWSTR ptr;
- UNICODE_STRING* cd;
+ 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];
- reqsize = sizeof(WCHAR); /* '\0' at the end */
+ /* return error if name only consists of spaces */
+ for (ptr = name; *ptr; ptr++) if (*ptr != ' ') break;
+ if (!*ptr) return 0;
RtlAcquirePebLock();
- cd = &NtCurrentPeb ()->ProcessParameters->CurrentDirectoryName;
- switch (type = RtlDetermineDosPathNameType_U((LPWSTR)name))
+ cd = &((PCURDIR)&NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectoryName)->DosPath;
+
+ switch (type = RtlDetermineDosPathNameType_U(name))
{
- case 1 /*UNC_PATH*/: /* \\foo */
- case 6 /*DEVICE_PATH*/: /* \\.\foo */
- if (reqsize <= size) buffer[0] = '\0';
+ case UNC_PATH: /* \\foo */
+ ptr = skip_unc_prefix( name );
+ mark = (ptr - name);
break;
- case 2 /*ABSOLUTE_DRIVE_PATH*/: /* c:\foo */
- reqsize += sizeof(WCHAR);
- if (reqsize <= size)
- {
- buffer[0] = RtlUpcaseUnicodeChar(name[0]);
- buffer[1] = '\0';
- }
- name++;
+ 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 3 /*RELATIVE_DRIVE_PATH*/: /* c:foo */
- if (RtlUpcaseUnicodeChar(name[0]) != RtlUpcaseUnicodeChar(cd->Buffer[0]) ||
- cd->Buffer[1] != ':')
+ case RELATIVE_DRIVE_PATH: /* c:foo */
+ dep = 2;
+ if (towupper(name[0]) != towupper(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;
+ 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 = buffer;
-
- name += 2;
+ val.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, size);
switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
{
*/
/* 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';
- }
+ 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);
- if (reqsize <= size)
- {
- buffer[0] = drive[1];
- buffer[1] = ':';
- buffer[2] = '\\';
- buffer[3] = '\0';
- }
+ reqsize = 3 * sizeof(WCHAR);
+ tmp[0] = name[0];
+ tmp[1] = ':';
+ tmp[2] = '\\';
+ ins_str = tmp;
break;
default:
- DPRINT("Unsupported status code\n");
+ DPRINT1("Unsupported status code\n");
break;
}
+ mark = 3;
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;
- }
+ case RELATIVE_PATH: /* foo */
+ reqsize = cd->Length;
+ ins_str = cd->Buffer;
if (cd->Buffer[1] != ':')
{
- ptr = wcsrchr(cd->Buffer + 2, '\\');
- if (ptr) ptr = wcsrchr(ptr + 1, '\\');
- if (!ptr) ptr = cd->Buffer + wcslen(cd->Buffer);
+ ptr = skip_unc_prefix( cd->Buffer );
mark = ptr - cd->Buffer;
}
+ else mark = 3;
break;
- case 4 /*ABSOLUTE_PATH*/: /* \xxx */
- if (cd->Buffer[1] == ':')
+ case ABSOLUTE_PATH: /* \xxx */
+#ifdef __WINE__
+ if (name[0] == '/') /* may be a Unix path */
{
- reqsize += 2 * sizeof(WCHAR);
- if (reqsize <= size)
+ const WCHAR *ptr = name;
+ int drive = find_drive_root( &ptr );
+ if (drive != -1)
{
- buffer[0] = cd->Buffer[0];
- buffer[1] = ':';
- buffer[2] = '\0';
+ 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
{
- 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';
+ ptr = skip_unc_prefix( cd->Buffer );
+ reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
+ mark = reqsize / sizeof(WCHAR);
+ ins_str = cd->Buffer;
}
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';
- }
+ 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 0 /*INVALID_PATH*/:
- reqsize = 0;
+ case INVALID_PATH:
goto done;
}
- reqsize += wcslen(name) * sizeof(WCHAR);
- if (reqsize > size) goto done;
-
- wcscat(buffer, name);
+ /* 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;
+ }
- /* convert every / into a \ */
- for (ptr = buffer; ptr < buffer + size / sizeof(WCHAR); ptr++)
- if (*ptr == '/') *ptr = '\\';
+ memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
+ if (reqsize) memcpy(buffer, ins_str, reqsize);
+ reqsize += deplen;
- reqsize -= sizeof(WCHAR); /* don't count trailing \0 */
+ if (ins_str && ins_str != tmp && ins_str != cd->Buffer)
+ RtlFreeHeap(RtlGetProcessHeap(), 0, ins_str);
- /* 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;
- }
+ collapse_path( buffer, mark );
+ reqsize = wcslen(buffer) * sizeof(WCHAR);
done:
RtlReleasePebLock();
return reqsize;
}
-/*
+
+/******************************************************************
+ * 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(WCHAR* name, ULONG size, WCHAR* buffer,
- WCHAR** file_part)
+DWORD STDCALL RtlGetFullPathName_U(
+ const 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);
+ 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);
+ 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 */
+ 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, L"\\\\.\\");
+ wcscpy(buffer, DeviceRootW);
memmove(buffer + 4, name + offset, sz);
buffer[4 + sz / sizeof(WCHAR)] = '\0';
/* file_part isn't set in this case */
}
reqsize = get_full_path_helper(name, buffer, size);
+ if (!reqsize) return 0;
if (reqsize > size)
{
LPWSTR tmp = RtlAllocateHeap(RtlGetProcessHeap(), 0, reqsize);
return reqsize;
}
+
/*
- * @unimplemented
+ * @implemented
*/
BOOLEAN STDCALL
RtlDosPathNameToNtPathName_U(PWSTR dosname,
WCHAR fullname[MAX_PATH + 1];
PWSTR Buffer = NULL;
+
RtlAcquirePebLock ();
RtlInitUnicodeString (&us, dosname);
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);
}
RtlReleasePebLock();
-
return TRUE;
}
/*
- * @unimplemented
+ * @implemented
*/
BOOLEAN STDCALL
RtlDoesFileExists_U(IN PWSTR FileName)
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 */