Fixed RtlDosPathNameToNtPathName_U prototype.
[wine/multimedia.git] / dlls / ntdll / path.c
blob52d1335c68388991e61a7d41a0fb7302c328d2ef
1 /*
2 * Ntdll path functions
4 * Copyright 2002, 2003, 2004 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 <stdarg.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winreg.h"
31 #include "winternl.h"
32 #include "wine/unicode.h"
33 #include "wine/debug.h"
34 #include "wine/library.h"
35 #include "ntdll_misc.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(file);
39 static const WCHAR DeviceRootW[] = {'\\','\\','.','\\',0};
40 static const WCHAR NTDosPrefixW[] = {'\\','?','?','\\',0};
41 static const WCHAR UncPfxW[] = {'U','N','C','\\',0};
43 /* FIXME: hack! */
44 HANDLE (WINAPI *pCreateFileW)( LPCWSTR filename, DWORD access, DWORD sharing,
45 LPSECURITY_ATTRIBUTES sa, DWORD creation,
46 DWORD attributes, HANDLE template );
48 #define IS_SEPARATOR(ch) ((ch) == '\\' || (ch) == '/')
50 #define MAX_DOS_DRIVES 26
52 struct drive_info
54 dev_t dev;
55 ino_t ino;
58 /***********************************************************************
59 * get_drives_info
61 * Retrieve device/inode number for all the drives. Helper for find_drive_root.
63 static inline int get_drives_info( struct drive_info info[MAX_DOS_DRIVES] )
65 const char *config_dir = wine_get_config_dir();
66 char *buffer, *p;
67 struct stat st;
68 int i, ret;
70 buffer = RtlAllocateHeap( GetProcessHeap(), 0, strlen(config_dir) + sizeof("/dosdevices/a:") );
71 if (!buffer) return 0;
72 strcpy( buffer, config_dir );
73 strcat( buffer, "/dosdevices/a:" );
74 p = buffer + strlen(buffer) - 2;
76 for (i = ret = 0; i < MAX_DOS_DRIVES; i++)
78 *p = 'a' + i;
79 if (!stat( buffer, &st ))
81 info[i].dev = st.st_dev;
82 info[i].ino = st.st_ino;
83 ret++;
85 else
87 info[i].dev = 0;
88 info[i].ino = 0;
91 RtlFreeHeap( GetProcessHeap(), 0, buffer );
92 return ret;
96 /***********************************************************************
97 * remove_last_component
99 * Remove the last component of the path. Helper for find_drive_root.
101 static inline int remove_last_component( const WCHAR *path, int len )
103 int level = 0;
105 while (level < 1)
107 /* find start of the last path component */
108 int prev = len;
109 if (prev <= 1) break; /* reached root */
110 while (prev > 1 && !IS_SEPARATOR(path[prev - 1])) prev--;
111 /* does removing it take us up a level? */
112 if (len - prev != 1 || path[prev] != '.') /* not '.' */
114 if (len - prev == 2 && path[prev] == '.' && path[prev+1] == '.') /* is it '..'? */
115 level--;
116 else
117 level++;
119 /* strip off trailing slashes */
120 while (prev > 1 && IS_SEPARATOR(path[prev - 1])) prev--;
121 len = prev;
123 return len;
127 /***********************************************************************
128 * find_drive_root
130 * Find a drive for which the root matches the beginning of the given path.
131 * This can be used to translate a Unix path into a drive + DOS path.
132 * Return value is the drive, or -1 on error. On success, ppath is modified
133 * to point to the beginning of the DOS path.
135 static int find_drive_root( LPCWSTR *ppath )
137 /* Starting with the full path, check if the device and inode match any of
138 * the wine 'drives'. If not then remove the last path component and try
139 * again. If the last component was a '..' then skip a normal component
140 * since it's a directory that's ascended back out of.
142 int drive, lenA, lenW;
143 char *buffer, *p;
144 const WCHAR *path = *ppath;
145 struct stat st;
146 struct drive_info info[MAX_DOS_DRIVES];
148 /* get device and inode of all drives */
149 if (!get_drives_info( info )) return -1;
151 /* strip off trailing slashes */
152 lenW = strlenW(path);
153 while (lenW > 1 && IS_SEPARATOR(path[lenW - 1])) lenW--;
155 /* convert path to Unix encoding */
156 lenA = ntdll_wcstoumbs( 0, path, lenW, NULL, 0, NULL, NULL );
157 if (!(buffer = RtlAllocateHeap( GetProcessHeap(), 0, lenA + 1 ))) return -1;
158 lenA = ntdll_wcstoumbs( 0, path, lenW, buffer, lenA, NULL, NULL );
159 buffer[lenA] = 0;
160 for (p = buffer; *p; p++) if (*p == '\\') *p = '/';
162 for (;;)
164 if (!stat( buffer, &st ) && S_ISDIR( st.st_mode ))
166 /* Find the drive */
167 for (drive = 0; drive < MAX_DOS_DRIVES; drive++)
169 if ((info[drive].dev == st.st_dev) && (info[drive].ino == st.st_ino))
171 if (lenW == 1) lenW = 0; /* preserve root slash in returned path */
172 TRACE( "%s -> drive %c:, root=%s, name=%s\n",
173 debugstr_w(path), 'A' + drive, debugstr_a(buffer), debugstr_w(path + lenW));
174 *ppath += lenW;
175 RtlFreeHeap( GetProcessHeap(), 0, buffer );
176 return drive;
180 if (lenW <= 1) break; /* reached root */
181 lenW = remove_last_component( path, lenW );
183 /* we only need the new length, buffer already contains the converted string */
184 lenA = ntdll_wcstoumbs( 0, path, lenW, NULL, 0, NULL, NULL );
185 buffer[lenA] = 0;
187 RtlFreeHeap( GetProcessHeap(), 0, buffer );
188 return -1;
192 /***********************************************************************
193 * RtlDetermineDosPathNameType_U (NTDLL.@)
195 DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U( PCWSTR path )
197 if (IS_SEPARATOR(path[0]))
199 if (!IS_SEPARATOR(path[1])) return ABSOLUTE_PATH; /* "/foo" */
200 if (path[2] != '.') return UNC_PATH; /* "//foo" */
201 if (IS_SEPARATOR(path[3])) return DEVICE_PATH; /* "//./foo" */
202 if (path[3]) return UNC_PATH; /* "//.foo" */
203 return UNC_DOT_PATH; /* "//." */
205 else
207 if (!path[0] || path[1] != ':') return RELATIVE_PATH; /* "foo" */
208 if (IS_SEPARATOR(path[2])) return ABSOLUTE_DRIVE_PATH; /* "c:/foo" */
209 return RELATIVE_DRIVE_PATH; /* "c:foo" */
213 /******************************************************************
214 * RtlDoesFileExists_U
216 * FIXME: should not use CreateFileW
218 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
220 HANDLE handle = pCreateFileW( file_name, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
221 NULL, OPEN_EXISTING, 0, 0 );
222 if (handle == INVALID_HANDLE_VALUE) return FALSE;
223 NtClose( handle );
224 return TRUE;
227 /***********************************************************************
228 * RtlIsDosDeviceName_U (NTDLL.@)
230 * Check if the given DOS path contains a DOS device name.
232 * Returns the length of the device name in the low word and its
233 * position in the high word (both in bytes, not WCHARs), or 0 if no
234 * device name is found.
236 ULONG WINAPI RtlIsDosDeviceName_U( PCWSTR dos_name )
238 static const WCHAR consoleW[] = {'\\','\\','.','\\','C','O','N',0};
239 static const WCHAR auxW[3] = {'A','U','X'};
240 static const WCHAR comW[3] = {'C','O','M'};
241 static const WCHAR conW[3] = {'C','O','N'};
242 static const WCHAR lptW[3] = {'L','P','T'};
243 static const WCHAR nulW[3] = {'N','U','L'};
244 static const WCHAR prnW[3] = {'P','R','N'};
246 const WCHAR *start, *end, *p;
248 switch(RtlDetermineDosPathNameType_U( dos_name ))
250 case INVALID_PATH:
251 case UNC_PATH:
252 return 0;
253 case DEVICE_PATH:
254 if (!strcmpiW( dos_name, consoleW ))
255 return MAKELONG( sizeof(conW), 4 * sizeof(WCHAR) ); /* 4 is length of \\.\ prefix */
256 return 0;
257 default:
258 break;
261 end = dos_name + strlenW(dos_name) - 1;
262 if (end >= dos_name && *end == ':') end--; /* remove trailing ':' */
264 /* find start of file name */
265 for (start = end; start >= dos_name; start--)
267 if (IS_SEPARATOR(start[0])) break;
268 /* check for ':' but ignore if before extension (for things like NUL:.txt) */
269 if (start[0] == ':' && start[1] != '.') break;
271 start++;
273 /* remove extension */
274 if ((p = strchrW( start, '.' )))
276 end = p - 1;
277 if (end >= dos_name && *end == ':') end--; /* remove trailing ':' before extension */
279 else
281 /* no extension, remove trailing spaces */
282 while (end >= dos_name && *end == ' ') end--;
285 /* now we have a potential device name between start and end, check it */
286 switch(end - start + 1)
288 case 3:
289 if (strncmpiW( start, auxW, 3 ) &&
290 strncmpiW( start, conW, 3 ) &&
291 strncmpiW( start, nulW, 3 ) &&
292 strncmpiW( start, prnW, 3 )) break;
293 return MAKELONG( 3 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
294 case 4:
295 if (strncmpiW( start, comW, 3 ) && strncmpiW( start, lptW, 3 )) break;
296 if (*end <= '0' || *end > '9') break;
297 return MAKELONG( 4 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
298 default: /* can't match anything */
299 break;
301 return 0;
305 /**************************************************************************
306 * RtlDosPathNameToNtPathName_U [NTDLL.@]
308 * dos_path: a DOS path name (fully qualified or not)
309 * ntpath: pointer to a UNICODE_STRING to hold the converted
310 * path name
311 * file_part:will point (in ntpath) to the file part in the path
312 * cd: directory reference (optional)
314 * FIXME:
315 * + fill the cd structure
317 BOOLEAN WINAPI RtlDosPathNameToNtPathName_U(PCWSTR dos_path,
318 PUNICODE_STRING ntpath,
319 PWSTR* file_part,
320 CURDIR* cd)
322 static const WCHAR LongFileNamePfxW[4] = {'\\','\\','?','\\'};
323 ULONG sz, ptr_sz, offset;
324 WCHAR local[MAX_PATH];
325 LPWSTR ptr;
327 TRACE("(%s,%p,%p,%p)\n",
328 debugstr_w(dos_path), ntpath, file_part, cd);
330 if (cd)
332 FIXME("Unsupported parameter\n");
333 memset(cd, 0, sizeof(*cd));
336 if (!dos_path || !*dos_path) return FALSE;
338 if (!memcmp(dos_path, LongFileNamePfxW, sizeof(LongFileNamePfxW)))
340 dos_path += sizeof(LongFileNamePfxW) / sizeof(WCHAR);
341 ptr = NULL;
342 ptr_sz = 0;
344 else
346 ptr = local;
347 ptr_sz = sizeof(local);
349 sz = RtlGetFullPathName_U(dos_path, ptr_sz, ptr, file_part);
350 if (sz == 0) return FALSE;
351 if (sz > ptr_sz)
353 ptr = RtlAllocateHeap(GetProcessHeap(), 0, sz);
354 sz = RtlGetFullPathName_U(dos_path, sz, ptr, file_part);
357 ntpath->MaximumLength = sz + (4 /* unc\ */ + 4 /* \??\ */) * sizeof(WCHAR);
358 ntpath->Buffer = RtlAllocateHeap(GetProcessHeap(), 0, ntpath->MaximumLength);
359 if (!ntpath->Buffer)
361 if (ptr != local) RtlFreeHeap(GetProcessHeap(), 0, ptr);
362 return FALSE;
365 strcpyW(ntpath->Buffer, NTDosPrefixW);
366 offset = 0;
367 switch (RtlDetermineDosPathNameType_U(ptr))
369 case UNC_PATH: /* \\foo */
370 if (ptr[2] != '?')
372 offset = 2;
373 strcatW(ntpath->Buffer, UncPfxW);
375 break;
376 case DEVICE_PATH: /* \\.\foo */
377 offset = 4;
378 break;
379 default: break; /* needed to keep gcc quiet */
382 strcatW(ntpath->Buffer, ptr + offset);
383 ntpath->Length = strlenW(ntpath->Buffer) * sizeof(WCHAR);
385 if (file_part && *file_part)
386 *file_part = ntpath->Buffer + ntpath->Length / sizeof(WCHAR) - strlenW(*file_part);
388 /* FIXME: cd filling */
390 if (ptr != local) RtlFreeHeap(GetProcessHeap(), 0, ptr);
391 return TRUE;
394 /******************************************************************
395 * RtlDosSearchPath_U
397 * Searchs a file of name 'name' into a ';' separated list of paths
398 * (stored in paths)
399 * Doesn't seem to search elsewhere than the paths list
400 * Stores the result in buffer (file_part will point to the position
401 * of the file name in the buffer)
402 * FIXME:
403 * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
405 ULONG WINAPI RtlDosSearchPath_U(LPCWSTR paths, LPCWSTR search, LPCWSTR ext,
406 ULONG buffer_size, LPWSTR buffer,
407 LPWSTR* file_part)
409 DOS_PATHNAME_TYPE type = RtlDetermineDosPathNameType_U(search);
410 ULONG len = 0;
412 if (type == RELATIVE_PATH)
414 ULONG allocated = 0, needed, filelen;
415 WCHAR *name = NULL;
417 filelen = 1 /* for \ */ + strlenW(search) + 1 /* \0 */;
419 /* Windows only checks for '.' without worrying about path components */
420 if (strchrW( search, '.' )) ext = NULL;
421 if (ext != NULL) filelen += strlenW(ext);
423 while (*paths)
425 LPCWSTR ptr;
427 for (needed = 0, ptr = paths; *ptr != 0 && *ptr++ != ';'; needed++);
428 if (needed + filelen > allocated)
430 if (!name) name = RtlAllocateHeap(GetProcessHeap(), 0,
431 (needed + filelen) * sizeof(WCHAR));
432 else
434 WCHAR *newname = RtlReAllocateHeap(GetProcessHeap(), 0, name,
435 (needed + filelen) * sizeof(WCHAR));
436 if (!newname) RtlFreeHeap(GetProcessHeap(), 0, name);
437 name = newname;
439 if (!name) return 0;
440 allocated = needed + filelen;
442 memmove(name, paths, needed * sizeof(WCHAR));
443 /* append '\\' if none is present */
444 if (needed > 0 && name[needed - 1] != '\\') name[needed++] = '\\';
445 strcpyW(&name[needed], search);
446 if (ext) strcatW(&name[needed], ext);
447 if (RtlDoesFileExists_U(name))
449 len = RtlGetFullPathName_U(name, buffer_size, buffer, file_part);
450 break;
452 paths = ptr;
454 RtlFreeHeap(GetProcessHeap(), 0, name);
456 else if (RtlDoesFileExists_U(search))
458 len = RtlGetFullPathName_U(search, buffer_size, buffer, file_part);
461 return len;
465 /******************************************************************
466 * collapse_path
468 * Helper for RtlGetFullPathName_U.
469 * Get rid of . and .. components in the path.
471 static inline void collapse_path( WCHAR *path, UINT mark )
473 WCHAR *p, *next;
475 /* convert every / into a \ */
476 for (p = path; *p; p++) if (*p == '/') *p = '\\';
478 /* collapse duplicate backslashes */
479 next = path + max( 1, mark );
480 for (p = next; *p; p++) if (*p != '\\' || next[-1] != '\\') *next++ = *p;
481 *next = 0;
483 p = path + mark;
484 while (*p)
486 if (*p == '.')
488 switch(p[1])
490 case '\\': /* .\ component */
491 next = p + 2;
492 memmove( p, next, (strlenW(next) + 1) * sizeof(WCHAR) );
493 continue;
494 case 0: /* final . */
495 if (p > path + mark) p--;
496 *p = 0;
497 continue;
498 case '.':
499 if (p[2] == '\\') /* ..\ component */
501 next = p + 3;
502 if (p > path + mark)
504 p--;
505 while (p > path + mark && p[-1] != '\\') p--;
507 memmove( p, next, (strlenW(next) + 1) * sizeof(WCHAR) );
508 continue;
510 else if (!p[2]) /* final .. */
512 if (p > path + mark)
514 p--;
515 while (p > path + mark && p[-1] != '\\') p--;
516 if (p > path + mark) p--;
518 *p = 0;
519 continue;
521 break;
524 /* skip to the next component */
525 while (*p && *p != '\\') p++;
526 if (*p == '\\') p++;
530 /******************************************************************
531 * get_full_path_helper
533 * Helper for RtlGetFullPathName_U
534 * Note: name and buffer are allowed to point to the same memory spot
536 static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
538 ULONG reqsize = 0, mark = 0, dep = 0, deplen;
539 DOS_PATHNAME_TYPE type;
540 LPWSTR ins_str = NULL;
541 LPCWSTR ptr;
542 const UNICODE_STRING* cd;
543 WCHAR tmp[4];
545 RtlAcquirePebLock();
547 cd = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectoryName;
549 switch (type = RtlDetermineDosPathNameType_U(name))
551 case UNC_PATH: /* \\foo */
552 ptr = name + 2;
553 while (*ptr && !IS_SEPARATOR(*ptr)) ptr++; /* share name */
554 while (*ptr && IS_SEPARATOR(*ptr)) ptr++;
555 while (*ptr && !IS_SEPARATOR(*ptr)) ptr++; /* dir name */
556 mark = (ptr - name);
557 break;
559 case DEVICE_PATH: /* \\.\foo */
560 mark = 4;
561 break;
563 case ABSOLUTE_DRIVE_PATH: /* c:\foo */
564 reqsize = sizeof(WCHAR);
565 tmp[0] = toupperW(name[0]);
566 ins_str = tmp;
567 dep = 1;
568 mark = 3;
569 break;
571 case RELATIVE_DRIVE_PATH: /* c:foo */
572 dep = 2;
573 if (toupperW(name[0]) != toupperW(cd->Buffer[0]) || cd->Buffer[1] != ':')
575 UNICODE_STRING var, val;
577 tmp[0] = '=';
578 tmp[1] = name[0];
579 tmp[2] = ':';
580 tmp[3] = '\0';
581 var.Length = 3 * sizeof(WCHAR);
582 var.MaximumLength = 4 * sizeof(WCHAR);
583 var.Buffer = tmp;
584 val.Length = 0;
585 val.MaximumLength = size;
586 val.Buffer = RtlAllocateHeap(GetProcessHeap(), 0, size);
588 switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
590 case STATUS_SUCCESS:
591 /* FIXME: Win2k seems to check that the environment variable actually points
592 * to an existing directory. If not, root of the drive is used
593 * (this seems also to be the only spot in RtlGetFullPathName that the
594 * existence of a part of a path is checked)
596 /* fall thru */
597 case STATUS_BUFFER_TOO_SMALL:
598 reqsize = val.Length + sizeof(WCHAR); /* append trailing '\\' */
599 val.Buffer[val.Length / sizeof(WCHAR)] = '\\';
600 ins_str = val.Buffer;
601 break;
602 case STATUS_VARIABLE_NOT_FOUND:
603 reqsize = 3 * sizeof(WCHAR);
604 tmp[0] = name[0];
605 tmp[1] = ':';
606 tmp[2] = '\\';
607 ins_str = tmp;
608 break;
609 default:
610 ERR("Unsupported status code\n");
611 break;
613 mark = 3;
614 break;
616 /* fall through */
618 case RELATIVE_PATH: /* foo */
619 reqsize = cd->Length;
620 ins_str = cd->Buffer;
621 if (cd->Buffer[1] != ':')
623 ptr = strchrW(cd->Buffer + 2, '\\');
624 if (ptr) ptr = strchrW(ptr + 1, '\\');
625 if (!ptr) ptr = cd->Buffer + strlenW(cd->Buffer);
626 mark = ptr - cd->Buffer;
628 else mark = 3;
629 break;
631 case ABSOLUTE_PATH: /* \xxx */
632 if (name[0] == '/') /* may be a Unix path */
634 const WCHAR *ptr = name;
635 int drive = find_drive_root( &ptr );
636 if (drive != -1)
638 reqsize = 3 * sizeof(WCHAR);
639 tmp[0] = 'A' + drive;
640 tmp[1] = ':';
641 tmp[2] = '\\';
642 ins_str = tmp;
643 mark = 3;
644 dep = ptr - name;
645 break;
648 if (cd->Buffer[1] == ':')
650 reqsize = 2 * sizeof(WCHAR);
651 tmp[0] = cd->Buffer[0];
652 tmp[1] = ':';
653 ins_str = tmp;
654 mark = 3;
656 else
658 ptr = strchrW(cd->Buffer + 2, '\\');
659 if (ptr) ptr = strchrW(ptr + 1, '\\');
660 if (!ptr) ptr = cd->Buffer + strlenW(cd->Buffer);
661 reqsize = (ptr - cd->Buffer) * sizeof(WCHAR);
662 mark = reqsize / sizeof(WCHAR);
663 ins_str = cd->Buffer;
665 break;
667 case UNC_DOT_PATH: /* \\. */
668 reqsize = 4 * sizeof(WCHAR);
669 dep = 3;
670 tmp[0] = '\\';
671 tmp[1] = '\\';
672 tmp[2] = '.';
673 tmp[3] = '\\';
674 ins_str = tmp;
675 mark = 4;
676 break;
678 case INVALID_PATH:
679 goto done;
682 /* enough space ? */
683 deplen = strlenW(name + dep) * sizeof(WCHAR);
684 if (reqsize + deplen + sizeof(WCHAR) > size)
686 /* not enough space, return need size (including terminating '\0') */
687 reqsize += deplen + sizeof(WCHAR);
688 goto done;
691 memmove(buffer + reqsize / sizeof(WCHAR), name + dep, deplen + sizeof(WCHAR));
692 if (reqsize) memcpy(buffer, ins_str, reqsize);
693 reqsize += deplen;
695 if (ins_str && ins_str != tmp && ins_str != cd->Buffer)
696 RtlFreeHeap(GetProcessHeap(), 0, ins_str);
698 collapse_path( buffer, mark );
699 reqsize = strlenW(buffer) * sizeof(WCHAR);
701 done:
702 RtlReleasePebLock();
703 return reqsize;
706 /******************************************************************
707 * RtlGetFullPathName_U (NTDLL.@)
709 * Returns the number of bytes written to buffer (not including the
710 * terminating NULL) if the function succeeds, or the required number of bytes
711 * (including the terminating NULL) if the buffer is too small.
713 * file_part will point to the filename part inside buffer (except if we use
714 * DOS device name, in which case file_in_buf is NULL)
717 DWORD WINAPI RtlGetFullPathName_U(const WCHAR* name, ULONG size, WCHAR* buffer,
718 WCHAR** file_part)
720 WCHAR* ptr;
721 DWORD dosdev;
722 DWORD reqsize;
724 TRACE("(%s %lu %p %p)\n", debugstr_w(name), size, buffer, file_part);
726 if (!name || !*name) return 0;
728 if (file_part) *file_part = NULL;
730 /* check for DOS device name */
731 dosdev = RtlIsDosDeviceName_U(name);
732 if (dosdev)
734 DWORD offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
735 DWORD sz = LOWORD(dosdev); /* in bytes */
737 if (8 + sz + 2 > size) return sz + 10;
738 strcpyW(buffer, DeviceRootW);
739 memmove(buffer + 4, name + offset, sz);
740 buffer[4 + sz / sizeof(WCHAR)] = '\0';
741 /* file_part isn't set in this case */
742 return sz + 8;
745 reqsize = get_full_path_helper(name, buffer, size);
746 if (reqsize > size)
748 LPWSTR tmp = RtlAllocateHeap(GetProcessHeap(), 0, reqsize);
749 reqsize = get_full_path_helper(name, tmp, reqsize);
750 if (reqsize > size) /* it may have worked the second time */
752 RtlFreeHeap(GetProcessHeap(), 0, tmp);
753 return reqsize + sizeof(WCHAR);
755 memcpy( buffer, tmp, reqsize + sizeof(WCHAR) );
756 RtlFreeHeap(GetProcessHeap(), 0, tmp);
759 /* find file part */
760 if (file_part && (ptr = strrchrW(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
761 *file_part = ptr;
762 return reqsize;
765 /*************************************************************************
766 * RtlGetLongestNtPathLength [NTDLL.@]
768 * Get the longest allowed path length
770 * PARAMS
771 * None.
773 * RETURNS
774 * The longest allowed path length (277 characters under Win2k).
776 DWORD WINAPI RtlGetLongestNtPathLength(void)
778 return 277;
781 /******************************************************************
782 * RtlIsNameLegalDOS8Dot3 (NTDLL.@)
784 * Returns TRUE iff unicode is a valid DOS (8+3) name.
785 * If the name is valid, oem gets filled with the corresponding OEM string
786 * spaces is set to TRUE if unicode contains spaces
788 BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3( const UNICODE_STRING *unicode,
789 OEM_STRING *oem, BOOLEAN *spaces )
791 static const char* illegal = "*?<>|\"+=,;[]:/\\\345";
792 int dot = -1;
793 unsigned int i;
794 char buffer[12];
795 OEM_STRING oem_str;
796 BOOLEAN got_space = FALSE;
798 if (!oem)
800 oem_str.Length = sizeof(buffer);
801 oem_str.MaximumLength = sizeof(buffer);
802 oem_str.Buffer = buffer;
803 oem = &oem_str;
805 if (RtlUpcaseUnicodeStringToCountedOemString( oem, unicode, FALSE ) != STATUS_SUCCESS)
806 return FALSE;
808 if (oem->Length > 12) return FALSE;
810 /* a starting . is invalid, except for . and .. */
811 if (oem->Buffer[0] == '.')
813 if (oem->Length != 1 && (oem->Length != 2 || oem->Buffer[1] != '.')) return FALSE;
814 if (spaces) *spaces = FALSE;
815 return TRUE;
818 for (i = 0; i < oem->Length; i++)
820 switch (oem->Buffer[i])
822 case ' ':
823 /* leading/trailing spaces not allowed */
824 if (!i || i == oem->Length-1 || oem->Buffer[i+1] == '.') return FALSE;
825 got_space = TRUE;
826 break;
827 case '.':
828 if (dot != -1) return FALSE;
829 dot = i;
830 break;
831 default:
832 if (strchr(illegal, oem->Buffer[i])) return FALSE;
833 break;
836 /* check file part is shorter than 8, extension shorter than 3
837 * dot cannot be last in string
839 if (dot == -1)
841 if (oem->Length > 8) return FALSE;
843 else
845 if (dot > 8 || (oem->Length - dot > 4) || dot == oem->Length - 1) return FALSE;
847 if (spaces) *spaces = got_space;
848 return TRUE;
851 /******************************************************************
852 * RtlGetCurrentDirectory_U (NTDLL.@)
855 NTSTATUS WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf)
857 UNICODE_STRING* us;
858 ULONG len;
860 TRACE("(%lu %p)\n", buflen, buf);
862 RtlAcquirePebLock();
864 us = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectoryName;
865 len = us->Length / sizeof(WCHAR);
866 if (us->Buffer[len - 1] == '\\' && us->Buffer[len - 2] != ':')
867 len--;
869 if (buflen / sizeof(WCHAR) > len)
871 memcpy(buf, us->Buffer, len * sizeof(WCHAR));
872 buf[len] = '\0';
874 else
876 len++;
879 RtlReleasePebLock();
881 return len * sizeof(WCHAR);
884 /******************************************************************
885 * RtlSetCurrentDirectory_U (NTDLL.@)
888 NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir)
890 UNICODE_STRING* curdir;
891 NTSTATUS nts = STATUS_SUCCESS;
892 ULONG size;
893 PWSTR buf = NULL;
895 TRACE("(%s)\n", debugstr_w(dir->Buffer));
897 RtlAcquirePebLock();
899 curdir = &NtCurrentTeb()->Peb->ProcessParameters->CurrentDirectoryName;
900 size = curdir->MaximumLength;
902 buf = RtlAllocateHeap(GetProcessHeap(), 0, size);
903 if (buf == NULL)
905 nts = STATUS_NO_MEMORY;
906 goto out;
909 size = RtlGetFullPathName_U(dir->Buffer, size, buf, 0);
910 if (!size)
912 nts = STATUS_OBJECT_NAME_INVALID;
913 goto out;
916 switch (RtlDetermineDosPathNameType_U(buf))
918 case ABSOLUTE_DRIVE_PATH:
919 case UNC_PATH:
920 break;
921 default:
922 FIXME("Don't support those cases yes\n");
923 nts = STATUS_NOT_IMPLEMENTED;
924 goto out;
927 /* FIXME: should check that the directory actually exists,
928 * and fill CurrentDirectoryHandle accordingly
931 /* append trailing \ if missing */
932 if (buf[size / sizeof(WCHAR) - 1] != '\\')
934 buf[size / sizeof(WCHAR)] = '\\';
935 buf[size / sizeof(WCHAR) + 1] = '\0';
936 size += sizeof(WCHAR);
939 memmove(curdir->Buffer, buf, size + sizeof(WCHAR));
940 curdir->Length = size;
942 #if 0
943 if (curdir->Buffer[1] == ':')
945 UNICODE_STRING env;
946 WCHAR var[4];
948 var[0] = '=';
949 var[1] = curdir->Buffer[0];
950 var[2] = ':';
951 var[3] = 0;
952 env.Length = 3 * sizeof(WCHAR);
953 env.MaximumLength = 4 * sizeof(WCHAR);
954 env.Buffer = var;
956 RtlSetEnvironmentVariable(NULL, &env, curdir);
958 #endif
960 out:
961 if (buf) RtlFreeHeap(GetProcessHeap(), 0, buf);
963 RtlReleasePebLock();
965 return nts;