Fixed typo.
[wine/gsoc_dplay.git] / dlls / ntdll / path.c
blob6b1e863bcba8b7b8833de6424e13134018650e0e
1 /*
2 * Ntdll path functions
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
22 #include "config.h"
24 #include "winternl.h"
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; /* "//." */
50 else
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 * RtlDoesFileExists_U
63 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
65 FIXME("(%s): stub\n", debugstr_w(file_name));
67 return TRUE;
70 /***********************************************************************
71 * RtlIsDosDeviceName_U (NTDLL.@)
73 * Check if the given DOS path contains a DOS device name.
75 * Returns the length of the device name in the low word and its
76 * position in the high word (both in bytes, not WCHARs), or 0 if no
77 * device name is found.
79 ULONG WINAPI RtlIsDosDeviceName_U( PCWSTR dos_name )
81 static const WCHAR consoleW[] = {'\\','\\','.','\\','C','O','N',0};
82 static const WCHAR auxW[3] = {'A','U','X'};
83 static const WCHAR comW[3] = {'C','O','M'};
84 static const WCHAR conW[3] = {'C','O','N'};
85 static const WCHAR lptW[3] = {'L','P','T'};
86 static const WCHAR nulW[3] = {'N','U','L'};
87 static const WCHAR prnW[3] = {'P','R','N'};
89 const WCHAR *start, *end, *p;
91 switch(RtlDetermineDosPathNameType_U( dos_name ))
93 case INVALID_PATH:
94 case UNC_PATH:
95 return 0;
96 case DEVICE_PATH:
97 if (!strcmpiW( dos_name, consoleW ))
98 return MAKELONG( sizeof(conW), 4 * sizeof(WCHAR) ); /* 4 is length of \\.\ prefix */
99 return 0;
100 default:
101 break;
104 end = dos_name + strlenW(dos_name) - 1;
105 if (end >= dos_name && *end == ':') end--; /* remove trailing ':' */
107 /* find start of file name */
108 for (start = end; start >= dos_name; start--)
110 if (IS_SEPARATOR(start[0])) break;
111 /* check for ':' but ignore if before extension (for things like NUL:.txt) */
112 if (start[0] == ':' && start[1] != '.') break;
114 start++;
116 /* remove extension */
117 if ((p = strchrW( start, '.' )))
119 end = p - 1;
120 if (end >= dos_name && *end == ':') end--; /* remove trailing ':' before extension */
122 else
124 /* no extension, remove trailing spaces */
125 while (end >= dos_name && *end == ' ') end--;
128 /* now we have a potential device name between start and end, check it */
129 switch(end - start + 1)
131 case 3:
132 if (strncmpiW( start, auxW, 3 ) &&
133 strncmpiW( start, conW, 3 ) &&
134 strncmpiW( start, nulW, 3 ) &&
135 strncmpiW( start, prnW, 3 )) break;
136 return MAKELONG( 3 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
137 case 4:
138 if (strncmpiW( start, comW, 3 ) && strncmpiW( start, lptW, 3 )) break;
139 if (*end <= '0' || *end > '9') break;
140 return MAKELONG( 4 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
141 default: /* can't match anything */
142 break;
144 return 0;
148 /**************************************************************************
149 * RtlDosPathNameToNtPathName_U [NTDLL.@]
151 * dos_path: a DOS path name (fully qualified or not)
152 * ntpath: pointer to a UNICODE_STRING to hold the converted
153 * path name
154 * file_part:will point (in ntpath) to the file part in the path
155 * cd: directory reference (optional)
157 * FIXME:
158 * + fill the cd structure
160 BOOLEAN WINAPI RtlDosPathNameToNtPathName_U(PWSTR dos_path,
161 PUNICODE_STRING ntpath,
162 PWSTR* file_part,
163 CURDIR* cd)
165 static const WCHAR LongFileNamePfxW[4] = {'\\','\\','?','\\'};
166 ULONG sz, ptr_sz, offset;
167 WCHAR local[MAX_PATH];
168 LPWSTR ptr;
170 TRACE("(%s,%p,%p,%p)\n",
171 debugstr_w(dos_path), ntpath, file_part, cd);
173 if (cd)
175 FIXME("Unsupported parameter\n");
176 memset(cd, 0, sizeof(*cd));
179 if (!dos_path || !*dos_path) return FALSE;
181 if (!memcmp(dos_path, LongFileNamePfxW, sizeof(LongFileNamePfxW)))
183 dos_path += sizeof(LongFileNamePfxW) / sizeof(WCHAR);
184 ptr = NULL;
185 ptr_sz = 0;
187 else
189 ptr = local;
190 ptr_sz = sizeof(local);
192 sz = RtlGetFullPathName_U(dos_path, ptr_sz, ptr, file_part);
193 if (sz == 0) return FALSE;
194 if (sz > ptr_sz)
196 ptr = RtlAllocateHeap(ntdll_get_process_heap(), 0, sz);
197 sz = RtlGetFullPathName_U(dos_path, sz, ptr, file_part);
200 ntpath->MaximumLength = sz + (4 /* unc\ */ + 4 /* \??\ */) * sizeof(WCHAR);
201 ntpath->Buffer = RtlAllocateHeap(ntdll_get_process_heap(), 0, ntpath->MaximumLength);
202 if (!ntpath->Buffer)
204 if (ptr != local) RtlFreeHeap(ntdll_get_process_heap(), 0, ptr);
205 return FALSE;
208 strcpyW(ntpath->Buffer, NTDosPrefixW);
209 offset = 0;
210 switch (RtlDetermineDosPathNameType_U(ptr))
212 case UNC_PATH: /* \\foo */
213 if (ptr[2] != '?')
215 offset = 2;
216 strcatW(ntpath->Buffer, UncPfxW);
218 break;
219 case DEVICE_PATH: /* \\.\foo */
220 offset = 4;
221 break;
222 default: break; /* needed to keep gcc quiet */
225 strcatW(ntpath->Buffer, ptr + offset);
226 ntpath->Length = strlenW(ntpath->Buffer) * sizeof(WCHAR);
228 if (file_part && *file_part)
229 *file_part = ntpath->Buffer + ntpath->Length / sizeof(WCHAR) - lstrlenW(*file_part);
231 /* FIXME: cd filling */
233 if (ptr != local) RtlFreeHeap(ntdll_get_process_heap(), 0, ptr);
234 return TRUE;
237 /******************************************************************
238 * RtlDosSearchPath_U
240 * Searchs a file of name 'name' into a ';' separated list of paths
241 * (stored in paths)
242 * Doesn't seem to search elsewhere than the paths list
243 * Stores the result in buffer (file_part will point to the position
244 * of the file name in the buffer)
245 * FIXME:
246 * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
248 ULONG WINAPI RtlDosSearchPath_U(LPCWSTR paths, LPCWSTR search, LPCWSTR ext,
249 ULONG buffer_size, LPWSTR buffer,
250 LPWSTR* file_part)
252 DOS_PATHNAME_TYPE type = RtlDetermineDosPathNameType_U(search);
253 ULONG len = 0;
255 if (type == RELATIVE_PATH)
257 ULONG allocated = 0, needed, filelen;
258 WCHAR* name = NULL;
260 filelen = 1 /* for \ */ + strlenW(search) + 1 /* \0 */;
262 if (strchrW(search, '.') != NULL) ext = NULL;
263 if (ext != NULL) filelen += strlenW(ext);
265 while (*paths)
267 LPCWSTR ptr;
269 for (needed = 0, ptr = paths; *ptr != 0 && *ptr++ != ';'; needed++);
270 if (needed + filelen > allocated)
272 name = (WCHAR*)RtlReAllocateHeap(ntdll_get_process_heap(), 0,
273 name, (needed + filelen) * sizeof(WCHAR));
274 if (!name) return 0;
275 allocated = needed + filelen;
277 memmove(name, paths, needed * sizeof(WCHAR));
278 /* append '\\' if none is present */
279 if (needed > 0 && name[needed - 1] != '\\') name[needed++] = '\\';
280 strcpyW(&name[needed], search);
281 if (ext) strcatW(&name[needed], ext);
282 if (RtlDoesFileExists_U(name))
284 len = RtlGetFullPathName_U(name, buffer_size, buffer, file_part);
285 break;
287 paths = ptr;
289 RtlFreeHeap(ntdll_get_process_heap(), 0, name);
291 else if (RtlDoesFileExists_U(search))
293 len = RtlGetFullPathName_U(search, buffer_size, buffer, file_part);
296 return len;
299 /******************************************************************
300 * get_full_path_helper
302 * Helper for RtlGetFullPathName_U
304 static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
306 ULONG reqsize, mark = 0;
307 DOS_PATHNAME_TYPE type;
308 LPWSTR ptr;
309 UNICODE_STRING* cd;
311 reqsize = sizeof(WCHAR); /* '\0' at the end */
313 RtlAcquirePebLock();
314 cd = &ntdll_get_process_pmts()->CurrentDirectoryName;
316 switch (type = RtlDetermineDosPathNameType_U(name))
318 case UNC_PATH: /* \\foo */
319 case DEVICE_PATH: /* \\.\foo */
320 if (reqsize <= size) buffer[0] = '\0';
321 break;
323 case ABSOLUTE_DRIVE_PATH: /* c:\foo */
324 reqsize += sizeof(WCHAR);
325 if (reqsize <= size)
327 buffer[0] = toupperW(name[0]);
328 buffer[1] = '\0';
330 name++;
331 break;
333 case RELATIVE_DRIVE_PATH: /* c:foo */
334 if (toupperW(name[0]) != toupperW(cd->Buffer[0]) || cd->Buffer[1] != ':')
336 WCHAR drive[4];
337 UNICODE_STRING var, val;
339 drive[0] = '=';
340 drive[1] = name[0];
341 drive[2] = ':';
342 drive[3] = '\0';
343 var.Length = 6;
344 var.MaximumLength = 8;
345 var.Buffer = drive;
346 val.Length = 0;
347 val.MaximumLength = size;
348 val.Buffer = buffer;
350 name += 2;
352 switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
354 case STATUS_SUCCESS:
355 /* FIXME: Win2k seems to check that the environment variable actually points
356 * to an existing directory. If not, root of the drive is used
357 * (this seems also to be the only spot in RtlGetFullPathName that the
358 * existence of a part of a path is checked)
360 /* fall thru */
361 case STATUS_BUFFER_TOO_SMALL:
362 reqsize += val.Length;
363 /* append trailing \\ */
364 reqsize += sizeof(WCHAR);
365 if (reqsize <= size)
367 buffer[reqsize / sizeof(WCHAR) - 2] = '\\';
368 buffer[reqsize / sizeof(WCHAR) - 1] = '\0';
370 break;
371 case STATUS_VARIABLE_NOT_FOUND:
372 reqsize += 3 * sizeof(WCHAR);
373 if (reqsize <= size)
375 buffer[0] = drive[1];
376 buffer[1] = ':';
377 buffer[2] = '\\';
378 buffer[3] = '\0';
380 break;
381 default:
382 ERR("Unsupported status code\n");
383 break;
385 break;
387 name += 2;
388 /* fall through */
390 case RELATIVE_PATH: /* foo */
391 reqsize += cd->Length;
392 mark = cd->Length / sizeof(WCHAR);
393 if (reqsize <= size)
394 strcpyW(buffer, cd->Buffer);
395 break;
397 case ABSOLUTE_PATH: /* \xxx */
398 if (cd->Buffer[1] == ':')
400 reqsize += 2 * sizeof(WCHAR);
401 if (reqsize <= size)
403 buffer[0] = cd->Buffer[0];
404 buffer[1] = ':';
405 buffer[2] = '\0';
408 else
410 unsigned len;
412 ptr = strchrW(cd->Buffer + 2, '\\');
413 if (ptr) ptr = strchrW(ptr + 1, '\\');
414 if (!ptr) ptr = cd->Buffer + strlenW(cd->Buffer);
415 len = (ptr - cd->Buffer) * sizeof(WCHAR);
416 reqsize += len;
417 mark = len / sizeof(WCHAR);
418 if (reqsize <= size)
420 memcpy(buffer, cd->Buffer, len);
421 buffer[len / sizeof(WCHAR)] = '\0';
423 else
424 buffer[0] = '\0';
426 break;
428 case UNC_DOT_PATH: /* \\. */
429 reqsize += 4 * sizeof(WCHAR);
430 name += 3;
431 if (reqsize <= size)
433 buffer[0] = '\\';
434 buffer[1] = '\\';
435 buffer[2] = '.';
436 buffer[3] = '\\';
437 buffer[4] = '\0';
439 break;
441 case INVALID_PATH:
442 reqsize = 0;
443 goto done;
446 reqsize += strlenW(name) * sizeof(WCHAR);
447 if (reqsize > size) goto done;
449 strcatW(buffer, name);
451 /* convert every / into a \ */
452 for (ptr = buffer; ptr < buffer + size / sizeof(WCHAR); ptr++)
453 if (*ptr == '/') *ptr = '\\';
455 reqsize -= sizeof(WCHAR); /* don't count trailing \0 */
457 /* mark is non NULL for UNC names, so start path collapsing after server & share name
458 * otherwise, it's a fully qualified DOS name, so start after the drive designation
460 for (ptr = buffer + (mark ? mark : 2); ptr < buffer + reqsize / sizeof(WCHAR); )
462 WCHAR* p = strchrW(ptr, '\\');
463 if (!p) break;
465 p++;
466 if (p[0] == '.')
468 switch (p[1])
470 case '.':
471 switch (p[2])
473 case '\\':
475 WCHAR* prev = p - 2;
476 while (prev >= buffer + mark && *prev != '\\') prev--;
477 /* either collapse \foo\.. into \ or \.. into \ */
478 if (prev < buffer + mark) prev = p - 1;
479 reqsize -= (p + 2 - prev) * sizeof(WCHAR);
480 memmove(prev, p + 2, buffer + reqsize - prev + sizeof(WCHAR));
482 break;
483 case '\0':
484 reqsize -= 2 * sizeof(WCHAR);
485 *p = 0;
486 break;
488 break;
489 case '\\':
490 reqsize -= 2 * sizeof(WCHAR);
491 memmove(ptr + 2, ptr, buffer + reqsize - ptr + sizeof(WCHAR));
492 break;
495 ptr = p;
498 done:
499 RtlReleasePebLock();
500 return reqsize;
503 /******************************************************************
504 * RtlGetFullPathName_U (NTDLL.@)
506 * Returns the number of bytes written to buffer (not including the
507 * terminating NULL) if the function succeeds, or the required number of bytes
508 * (including the terminating NULL) if the buffer is to small.
510 * file_part will point to the filename part inside buffer (except if we use
511 * DOS device name, in which case file_in_buf is NULL)
514 DWORD WINAPI RtlGetFullPathName_U(const WCHAR* name, ULONG size, WCHAR* buffer,
515 WCHAR** file_part)
517 DWORD dosdev;
518 DWORD reqsize;
520 TRACE("(%s %lu %p %p)\n", debugstr_w(name), size, buffer, file_part);
522 if (!name || !*name) return 0;
524 if (file_part) *file_part = NULL;
526 /* check for DOS device name */
527 dosdev = RtlIsDosDeviceName_U(name);
528 if (dosdev)
530 DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
531 DWORD sz = LOWORD(dosdev); /* in bytes */
533 if (8 + sz + 2 > size) return sz + 10;
534 strcpyW(buffer, DeviceRootW);
535 memmove(buffer + 4, name + offset, sz);
536 buffer[4 + sz / sizeof(WCHAR)] = '\0';
537 /* file_part isn't set in this case */
538 return sz + 8;
541 reqsize = get_full_path_helper(name, buffer, size);
542 if (reqsize > size)
544 LPWSTR tmp = RtlAllocateHeap(ntdll_get_process_heap(), 0, reqsize);
545 reqsize = get_full_path_helper(name, tmp, reqsize) + sizeof(WCHAR);
546 RtlFreeHeap(ntdll_get_process_heap(), 0, tmp);
548 else
550 WCHAR* ptr;
551 /* find file part */
552 if (file_part && (ptr = strrchrW(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
553 *file_part = ptr;
555 return reqsize;
558 /*************************************************************************
559 * RtlGetLongestNtPathLength [NTDLL.@]
561 * Get the longest allowed path length
563 * PARAMS
564 * None.
566 * RETURNS
567 * The longest allowed path length (277 characters under Win2k).
569 DWORD WINAPI RtlGetLongestNtPathLength(void)
571 return 277;
574 /******************************************************************
575 * RtlIsNameLegalDOS8Dot3 (NTDLL.@)
577 * Returns TRUE iff unicode is a valid DOS (8+3) name.
578 * If the name is valid, oem gets filled with the corresponding OEM string
579 * spaces is set to TRUE if unicode contains spaces
581 BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3( const UNICODE_STRING *unicode,
582 OEM_STRING *oem, BOOLEAN *spaces )
584 int dot = -1;
585 unsigned int i;
586 char buffer[12];
587 OEM_STRING oem_str;
588 BOOLEAN got_space = FALSE;
590 if (!oem)
592 oem_str.Length = sizeof(buffer);
593 oem_str.MaximumLength = sizeof(buffer);
594 oem_str.Buffer = buffer;
595 oem = &oem_str;
597 if (RtlUpcaseUnicodeStringToCountedOemString( oem, unicode, FALSE ) != STATUS_SUCCESS)
598 return FALSE;
600 if (oem->Length > 12) return FALSE;
602 /* a starting . is invalid, except for . and .. */
603 if (oem->Buffer[0] == '.')
605 if (oem->Length != 1 && (oem->Length != 2 || oem->Buffer[1] != '.')) return FALSE;
606 if (spaces) *spaces = FALSE;
607 return TRUE;
610 for (i = 0; i < oem->Length; i++)
612 switch (oem->Buffer[i])
614 case ' ':
615 /* leading/trailing spaces not allowed */
616 if (!i || i == oem->Length-1 || oem->Buffer[i+1] == '.') return FALSE;
617 got_space = TRUE;
618 break;
619 case '.':
620 if (dot != -1) return FALSE;
621 dot = i;
622 break;
623 default:
624 /* FIXME: check for invalid chars */
625 break;
628 /* check file part is shorter than 8, extension shorter than 3
629 * dot cannot be last in string
631 if (dot == -1)
633 if (oem->Length > 8) return FALSE;
635 else
637 if (dot > 8 || (oem->Length - dot > 4) || dot == oem->Length - 1) return FALSE;
639 if (spaces) *spaces = got_space;
640 return TRUE;
643 /******************************************************************
644 * RtlGetCurrentDirectory_U (NTDLL.@)
647 NTSTATUS WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf)
649 UNICODE_STRING* us;
650 ULONG len;
652 TRACE("(%lu %p)\n", buflen, buf);
654 RtlAcquirePebLock();
656 us = &ntdll_get_process_pmts()->CurrentDirectoryName;
657 len = us->Length / sizeof(WCHAR);
658 if (us->Buffer[len - 1] == '\\' && us->Buffer[len - 2] != ':')
659 len--;
661 if (buflen / sizeof(WCHAR) > len)
663 memcpy(buf, us->Buffer, len * sizeof(WCHAR));
664 buf[len] = '\0';
666 else
668 len++;
671 RtlReleasePebLock();
673 return len * sizeof(WCHAR);
676 /******************************************************************
677 * RtlSetCurrentDirectory_U (NTDLL.@)
680 NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir)
682 UNICODE_STRING* curdir;
683 NTSTATUS nts = STATUS_SUCCESS;
684 ULONG size;
685 PWSTR buf = NULL;
687 TRACE("(%s)\n", debugstr_w(dir->Buffer));
689 RtlAcquirePebLock();
691 curdir = &ntdll_get_process_pmts()->CurrentDirectoryName;
692 size = curdir->MaximumLength;
694 buf = RtlAllocateHeap(ntdll_get_process_heap(), 0, size);
695 if (buf == NULL)
697 nts = STATUS_NO_MEMORY;
698 goto out;
701 size = RtlGetFullPathName_U(dir->Buffer, size, buf, 0);
702 if (!size)
704 nts = STATUS_OBJECT_NAME_INVALID;
705 goto out;
708 switch (RtlDetermineDosPathNameType_U(buf))
710 case ABSOLUTE_DRIVE_PATH:
711 case UNC_PATH:
712 break;
713 default:
714 FIXME("Don't support those cases yes\n");
715 nts = STATUS_NOT_IMPLEMENTED;
716 goto out;
719 /* FIXME: should check that the directory actually exists,
720 * and fill CurrentDirectoryHandle accordingly
723 /* append trailing \ if missing */
724 if (buf[size / sizeof(WCHAR) - 1] != '\\')
726 buf[size / sizeof(WCHAR)] = '\\';
727 buf[size / sizeof(WCHAR) + 1] = '\0';
728 size += sizeof(WCHAR);
731 memmove(curdir->Buffer, buf, size + sizeof(WCHAR));
732 curdir->Length = size;
734 #if 0
735 if (curdir->Buffer[1] == ':')
737 UNICODE_STRING env;
738 WCHAR var[4];
740 var[0] = '=';
741 var[1] = curdir->Buffer[0];
742 var[2] = ':';
743 var[3] = 0;
744 env.Length = 3 * sizeof(WCHAR);
745 env.MaximumLength = 4 * sizeof(WCHAR);
746 env.Buffer = var;
748 RtlSetEnvironmentVariable(NULL, &env, curdir);
750 #endif
752 out:
753 if (buf) RtlFreeHeap(ntdll_get_process_heap(), 0, buf);
755 RtlReleasePebLock();
757 return nts;