4 * Copyright 2002, 2003 Alexandre Julliard
5 * Copyright 2003 Eric Pouech
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "wine/unicode.h"
26 #include "wine/debug.h"
27 #include "ntdll_misc.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(file);
31 static const WCHAR DeviceRootW[] = {'\\','\\','.','\\',0};
32 static const WCHAR NTDosPrefixW[] = {'\\','?','?','\\',0};
33 static const WCHAR UncPfxW[] = {'U','N','C','\\',0};
35 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
37 /***********************************************************************
38 * RtlDetermineDosPathNameType_U (NTDLL.@)
40 DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U( PCWSTR path )
42 if (IS_SEPARATOR(path[0]))
44 if (!IS_SEPARATOR(path[1])) return ABSOLUTE_PATH; /* "/foo" */
45 if (path[2] != '.') return UNC_PATH; /* "//foo" */
46 if (IS_SEPARATOR(path[3])) return DEVICE_PATH; /* "//./foo" */
47 if (path[3]) return UNC_PATH; /* "//.foo" */
48 return UNC_DOT_PATH; /* "//." */
52 if (!path[0] || path[1] != ':') return RELATIVE_PATH; /* "foo" */
53 if (IS_SEPARATOR(path[2])) return ABSOLUTE_DRIVE_PATH; /* "c:/foo" */
54 return RELATIVE_DRIVE_PATH; /* "c:foo" */
58 /***********************************************************************
59 * RtlIsDosDeviceName_U (NTDLL.@)
61 * Check if the given DOS path contains a DOS device name.
63 * Returns the length of the device name in the low word and its
64 * position in the high word (both in bytes, not WCHARs), or 0 if no
65 * device name is found.
67 ULONG WINAPI RtlIsDosDeviceName_U( PCWSTR dos_name )
69 static const WCHAR consoleW[] = {'\\','\\','.','\\','C','O','N',0};
70 static const WCHAR auxW[3] = {'A','U','X'};
71 static const WCHAR comW[3] = {'C','O','M'};
72 static const WCHAR conW[3] = {'C','O','N'};
73 static const WCHAR lptW[3] = {'L','P','T'};
74 static const WCHAR nulW[3] = {'N','U','L'};
75 static const WCHAR prnW[3] = {'P','R','N'};
77 const WCHAR *start, *end, *p;
79 switch(RtlDetermineDosPathNameType_U( dos_name ))
85 if (!strcmpiW( dos_name, consoleW ))
86 return MAKELONG( sizeof(conW), 4 * sizeof(WCHAR) ); /* 4 is length of \\.\ prefix */
92 end = dos_name + strlenW(dos_name) - 1;
93 if (end >= dos_name && *end == ':') end--; /* remove trailing ':' */
95 /* find start of file name */
96 for (start = end; start >= dos_name; start--)
98 if (IS_SEPARATOR(start[0])) break;
99 /* check for ':' but ignore if before extension (for things like NUL:.txt) */
100 if (start[0] == ':' && start[1] != '.') break;
104 /* remove extension */
105 if ((p = strchrW( start, '.' )))
108 if (end >= dos_name && *end == ':') end--; /* remove trailing ':' before extension */
112 /* no extension, remove trailing spaces */
113 while (end >= dos_name && *end == ' ') end--;
116 /* now we have a potential device name between start and end, check it */
117 switch(end - start + 1)
120 if (strncmpiW( start, auxW, 3 ) &&
121 strncmpiW( start, conW, 3 ) &&
122 strncmpiW( start, nulW, 3 ) &&
123 strncmpiW( start, prnW, 3 )) break;
124 return MAKELONG( 3 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
126 if (strncmpiW( start, comW, 3 ) && strncmpiW( start, lptW, 3 )) break;
127 if (*end <= '0' || *end > '9') break;
128 return MAKELONG( 4 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
129 default: /* can't match anything */
136 /**************************************************************************
137 * RtlDosPathNameToNtPathName_U [NTDLL.@]
139 * dos_path: a DOS path name (fully qualified or not)
140 * ntpath: pointer to a UNICODE_STRING to hold the converted
142 * file_part:will point (in ntpath) to the file part in the path
143 * cd: directory reference (optional)
146 * + fill the cd structure
148 BOOLEAN WINAPI RtlDosPathNameToNtPathName_U(PWSTR dos_path,
149 PUNICODE_STRING ntpath,
153 static const WCHAR LongFileNamePfxW[4] = {'\\','\\','?','\\'};
154 ULONG sz, ptr_sz, offset;
155 WCHAR local[MAX_PATH];
158 TRACE("(%s,%p,%p,%p)\n",
159 debugstr_w(dos_path), ntpath, file_part, cd);
163 FIXME("Unsupported parameter\n");
164 memset(cd, 0, sizeof(*cd));
167 if (!dos_path || !*dos_path) return FALSE;
169 if (!memcmp(dos_path, LongFileNamePfxW, sizeof(LongFileNamePfxW)))
171 dos_path += sizeof(LongFileNamePfxW) / sizeof(WCHAR);
178 ptr_sz = sizeof(local);
180 sz = RtlGetFullPathName_U(dos_path, ptr_sz, ptr, file_part);
181 if (sz == 0) return FALSE;
184 ptr = RtlAllocateHeap(ntdll_get_process_heap(), 0, sz);
185 sz = RtlGetFullPathName_U(dos_path, sz, ptr, file_part);
188 ntpath->MaximumLength = sz + (4 /* unc\ */ + 4 /* \??\ */) * sizeof(WCHAR);
189 ntpath->Buffer = RtlAllocateHeap(ntdll_get_process_heap(), 0, ntpath->MaximumLength);
192 if (ptr != local) RtlFreeHeap(ntdll_get_process_heap(), 0, ptr);
196 strcpyW(ntpath->Buffer, NTDosPrefixW);
198 switch (RtlDetermineDosPathNameType_U(ptr))
200 case UNC_PATH: /* \\foo */
204 strcatW(ntpath->Buffer, UncPfxW);
207 case DEVICE_PATH: /* \\.\foo */
210 default: break; /* needed to keep gcc quiet */
213 strcatW(ntpath->Buffer, ptr + offset);
214 ntpath->Length = strlenW(ntpath->Buffer) * sizeof(WCHAR);
216 if (file_part && *file_part)
217 *file_part = ntpath->Buffer + ntpath->Length / sizeof(WCHAR) - lstrlenW(*file_part);
219 /* FIXME: cd filling */
221 if (ptr != local) RtlFreeHeap(ntdll_get_process_heap(), 0, ptr);
225 /******************************************************************
226 * get_full_path_helper
228 * Helper for RtlGetFullPathName_U
230 static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
232 ULONG reqsize, mark = 0;
233 DOS_PATHNAME_TYPE type;
237 reqsize = sizeof(WCHAR); /* '\0' at the end */
240 cd = &ntdll_get_process_pmts()->CurrentDirectoryName;
242 switch (type = RtlDetermineDosPathNameType_U(name))
244 case UNC_PATH: /* \\foo */
245 case DEVICE_PATH: /* \\.\foo */
246 if (reqsize <= size) buffer[0] = '\0';
249 case ABSOLUTE_DRIVE_PATH: /* c:\foo */
250 reqsize += sizeof(WCHAR);
253 buffer[0] = toupperW(name[0]);
259 case RELATIVE_DRIVE_PATH: /* c:foo */
260 if (toupperW(name[0]) != toupperW(cd->Buffer[0]) || cd->Buffer[1] != ':')
263 UNICODE_STRING var, val;
270 var.MaximumLength = 8;
273 val.MaximumLength = size;
278 switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
281 /* FIXME: Win2k seems to check that the environment variable actually points
282 * to an existing directory. If not, root of the drive is used
283 * (this seems also to be the only spot in RtlGetFullPathName that the
284 * existence of a part of a path is checked)
287 case STATUS_BUFFER_TOO_SMALL:
288 reqsize += val.Length;
289 /* append trailing \\ */
290 reqsize += sizeof(WCHAR);
293 buffer[reqsize / sizeof(WCHAR) - 2] = '\\';
294 buffer[reqsize / sizeof(WCHAR) - 1] = '\0';
297 case STATUS_VARIABLE_NOT_FOUND:
298 reqsize += 3 * sizeof(WCHAR);
301 buffer[0] = drive[1];
308 ERR("Unsupported status code\n");
316 case RELATIVE_PATH: /* foo */
317 reqsize += cd->Length;
318 mark = cd->Length / sizeof(WCHAR);
320 strcpyW(buffer, cd->Buffer);
323 case ABSOLUTE_PATH: /* \xxx */
324 if (cd->Buffer[1] == ':')
326 reqsize += 2 * sizeof(WCHAR);
329 buffer[0] = cd->Buffer[0];
338 ptr = strchrW(cd->Buffer + 2, '\\');
339 if (ptr) ptr = strchrW(ptr + 1, '\\');
340 if (!ptr) ptr = cd->Buffer + strlenW(cd->Buffer);
341 len = (ptr - cd->Buffer) * sizeof(WCHAR);
343 mark = len / sizeof(WCHAR);
346 memcpy(buffer, cd->Buffer, len);
347 buffer[len / sizeof(WCHAR)] = '\0';
354 case UNC_DOT_PATH: /* \\. */
355 reqsize += 4 * sizeof(WCHAR);
372 reqsize += strlenW(name) * sizeof(WCHAR);
373 if (reqsize > size) goto done;
375 strcatW(buffer, name);
377 /* convert every / into a \ */
378 for (ptr = buffer; ptr < buffer + size / sizeof(WCHAR); ptr++)
379 if (*ptr == '/') *ptr = '\\';
381 reqsize -= sizeof(WCHAR); /* don't count trailing \0 */
383 /* mark is non NULL for UNC names, so start path collapsing after server & share name
384 * otherwise, it's a fully qualified DOS name, so start after the drive designation
386 for (ptr = buffer + (mark ? mark : 2); ptr < buffer + reqsize / sizeof(WCHAR); )
388 WCHAR* p = strchrW(ptr, '\\');
402 while (prev >= buffer + mark && *prev != '\\') prev--;
403 /* either collapse \foo\.. into \ or \.. into \ */
404 if (prev < buffer + mark) prev = p - 1;
405 reqsize -= (p + 2 - prev) * sizeof(WCHAR);
406 memmove(prev, p + 2, buffer + reqsize - prev + sizeof(WCHAR));
410 reqsize -= 2 * sizeof(WCHAR);
416 reqsize -= 2 * sizeof(WCHAR);
417 memmove(ptr + 2, ptr, buffer + reqsize - ptr + sizeof(WCHAR));
429 /******************************************************************
430 * RtlGetFullPathName_U (NTDLL.@)
432 * Returns the number of bytes written to buffer (not including the
433 * terminating NULL) if the function succeeds, or the required number of bytes
434 * (including the terminating NULL) if the buffer is to small.
436 * file_part will point to the filename part inside buffer (except if we use
437 * DOS device name, in which case file_in_buf is NULL)
440 DWORD WINAPI RtlGetFullPathName_U(const WCHAR* name, ULONG size, WCHAR* buffer,
446 TRACE("(%s %lu %p %p)\n", debugstr_w(name), size, buffer, file_part);
448 if (!name || !*name) return 0;
450 if (file_part) *file_part = NULL;
452 /* check for DOS device name */
453 dosdev = RtlIsDosDeviceName_U(name);
456 DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
457 DWORD sz = LOWORD(dosdev); /* in bytes */
459 if (8 + sz + 2 > size) return sz + 10;
460 strcpyW(buffer, DeviceRootW);
461 memmove(buffer + 4, name + offset, sz);
462 buffer[4 + sz / sizeof(WCHAR)] = '\0';
463 /* file_part isn't set in this case */
467 reqsize = get_full_path_helper(name, buffer, size);
470 LPWSTR tmp = RtlAllocateHeap(ntdll_get_process_heap(), 0, reqsize);
471 reqsize = get_full_path_helper(name, tmp, reqsize) + sizeof(WCHAR);
472 RtlFreeHeap(ntdll_get_process_heap(), 0, tmp);
478 if (file_part && (ptr = strrchrW(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
484 /*************************************************************************
485 * RtlGetLongestNtPathLength [NTDLL.@]
487 * Get the longest allowed path length
493 * The longest allowed path length (277 characters under Win2k).
495 DWORD WINAPI RtlGetLongestNtPathLength(void)
500 /******************************************************************
501 * RtlIsNameLegalDOS8Dot3 (NTDLL.@)
503 * Returns TRUE iff unicode is a valid DOS (8+3) name.
504 * If the name is valid, oem gets filled with the corresponding OEM string
505 * spaces is set to TRUE if unicode contains spaces
507 BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3( const UNICODE_STRING *unicode,
508 OEM_STRING *oem, BOOLEAN *spaces )
514 BOOLEAN got_space = FALSE;
518 oem_str.Length = sizeof(buffer);
519 oem_str.MaximumLength = sizeof(buffer);
520 oem_str.Buffer = buffer;
523 if (RtlUpcaseUnicodeStringToCountedOemString( oem, unicode, FALSE ) != STATUS_SUCCESS)
526 if (oem->Length > 12) return FALSE;
528 /* a starting . is invalid, except for . and .. */
529 if (oem->Buffer[0] == '.')
531 if (oem->Length != 1 && (oem->Length != 2 || oem->Buffer[1] != '.')) return FALSE;
532 if (spaces) *spaces = FALSE;
536 for (i = 0; i < oem->Length; i++)
538 switch (oem->Buffer[i])
541 /* leading/trailing spaces not allowed */
542 if (!i || i == oem->Length-1 || oem->Buffer[i+1] == '.') return FALSE;
546 if (dot != -1) return FALSE;
550 /* FIXME: check for invalid chars */
554 /* check file part is shorter than 8, extension shorter than 3
555 * dot cannot be last in string
559 if (oem->Length > 8) return FALSE;
563 if (dot > 8 || (oem->Length - dot > 4) || dot == oem->Length - 1) return FALSE;
565 if (spaces) *spaces = got_space;
569 /******************************************************************
570 * RtlGetCurrentDirectory_U (NTDLL.@)
573 NTSTATUS WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf)
578 TRACE("(%lu %p)\n", buflen, buf);
582 us = &ntdll_get_process_pmts()->CurrentDirectoryName;
583 len = us->Length / sizeof(WCHAR);
584 if (us->Buffer[len - 1] == '\\' && us->Buffer[len - 2] != ':')
587 if (buflen / sizeof(WCHAR) > len)
589 memcpy(buf, us->Buffer, len * sizeof(WCHAR));
599 return len * sizeof(WCHAR);
602 /******************************************************************
603 * RtlSetCurrentDirectory_U (NTDLL.@)
606 NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir)
608 UNICODE_STRING* curdir;
609 NTSTATUS nts = STATUS_SUCCESS;
613 TRACE("(%s)\n", debugstr_w(dir->Buffer));
617 curdir = &ntdll_get_process_pmts()->CurrentDirectoryName;
618 size = curdir->MaximumLength;
620 buf = RtlAllocateHeap(ntdll_get_process_heap(), 0, size);
623 nts = STATUS_NO_MEMORY;
627 size = RtlGetFullPathName_U(dir->Buffer, size, buf, 0);
630 nts = STATUS_OBJECT_NAME_INVALID;
634 switch (RtlDetermineDosPathNameType_U(buf))
636 case ABSOLUTE_DRIVE_PATH:
640 FIXME("Don't support those cases yes\n");
641 nts = STATUS_NOT_IMPLEMENTED;
645 /* FIXME: should check that the directory actually exists,
646 * and fill CurrentDirectoryHandle accordingly
649 /* append trailing \ if missing */
650 if (buf[size / sizeof(WCHAR) - 1] != '\\')
652 buf[size / sizeof(WCHAR)] = '\\';
653 buf[size / sizeof(WCHAR) + 1] = '\0';
654 size += sizeof(WCHAR);
657 memmove(curdir->Buffer, buf, size + sizeof(WCHAR));
658 curdir->Length = size;
661 if (curdir->Buffer[1] == ':')
667 var[1] = curdir->Buffer[0];
670 env.Length = 3 * sizeof(WCHAR);
671 env.MaximumLength = 4 * sizeof(WCHAR);
674 RtlSetEnvironmentVariable(NULL, &env, curdir);
679 if (buf) RtlFreeHeap(ntdll_get_process_heap(), 0, buf);