Only ignore certain keyboard events if a XLookupString returned a
[wine] / dlls / ntdll / path.c
1 /*
2  * Ntdll path functions
3  *
4  * Copyright 2002, 2003 Alexandre Julliard
5  * Copyright 2003 Eric Pouech
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "config.h"
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winternl.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
32 #include "ntdll_misc.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(file);
35
36 static const WCHAR DeviceRootW[] = {'\\','\\','.','\\',0};
37 static const WCHAR NTDosPrefixW[] = {'\\','?','?','\\',0};
38 static const WCHAR UncPfxW[] = {'U','N','C','\\',0};
39
40 #define IS_SEPARATOR(ch)  ((ch) == '\\' || (ch) == '/')
41
42 /***********************************************************************
43  *             RtlDetermineDosPathNameType_U   (NTDLL.@)
44  */
45 DOS_PATHNAME_TYPE WINAPI RtlDetermineDosPathNameType_U( PCWSTR path )
46 {
47     if (IS_SEPARATOR(path[0]))
48     {
49         if (!IS_SEPARATOR(path[1])) return ABSOLUTE_PATH;       /* "/foo" */
50         if (path[2] != '.') return UNC_PATH;                    /* "//foo" */
51         if (IS_SEPARATOR(path[3])) return DEVICE_PATH;          /* "//./foo" */
52         if (path[3]) return UNC_PATH;                           /* "//.foo" */
53         return UNC_DOT_PATH;                                    /* "//." */
54     }
55     else
56     {
57         if (!path[0] || path[1] != ':') return RELATIVE_PATH;   /* "foo" */
58         if (IS_SEPARATOR(path[2])) return ABSOLUTE_DRIVE_PATH;  /* "c:/foo" */
59         return RELATIVE_DRIVE_PATH;                             /* "c:foo" */
60     }
61 }
62
63 /******************************************************************
64  *              RtlDoesFileExists_U
65  *
66  *
67  */
68 BOOLEAN WINAPI RtlDoesFileExists_U(LPCWSTR file_name)
69 {       
70     FIXME("(%s): stub\n", debugstr_w(file_name));
71     
72     return TRUE;
73 }
74
75 /***********************************************************************
76  *             RtlIsDosDeviceName_U   (NTDLL.@)
77  *
78  * Check if the given DOS path contains a DOS device name.
79  *
80  * Returns the length of the device name in the low word and its
81  * position in the high word (both in bytes, not WCHARs), or 0 if no
82  * device name is found.
83  */
84 ULONG WINAPI RtlIsDosDeviceName_U( PCWSTR dos_name )
85 {
86     static const WCHAR consoleW[] = {'\\','\\','.','\\','C','O','N',0};
87     static const WCHAR auxW[3] = {'A','U','X'};
88     static const WCHAR comW[3] = {'C','O','M'};
89     static const WCHAR conW[3] = {'C','O','N'};
90     static const WCHAR lptW[3] = {'L','P','T'};
91     static const WCHAR nulW[3] = {'N','U','L'};
92     static const WCHAR prnW[3] = {'P','R','N'};
93
94     const WCHAR *start, *end, *p;
95
96     switch(RtlDetermineDosPathNameType_U( dos_name ))
97     {
98     case INVALID_PATH:
99     case UNC_PATH:
100         return 0;
101     case DEVICE_PATH:
102         if (!strcmpiW( dos_name, consoleW ))
103             return MAKELONG( sizeof(conW), 4 * sizeof(WCHAR) );  /* 4 is length of \\.\ prefix */
104         return 0;
105     default:
106         break;
107     }
108
109     end = dos_name + strlenW(dos_name) - 1;
110     if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' */
111
112     /* find start of file name */
113     for (start = end; start >= dos_name; start--)
114     {
115         if (IS_SEPARATOR(start[0])) break;
116         /* check for ':' but ignore if before extension (for things like NUL:.txt) */
117         if (start[0] == ':' && start[1] != '.') break;
118     }
119     start++;
120
121     /* remove extension */
122     if ((p = strchrW( start, '.' )))
123     {
124         end = p - 1;
125         if (end >= dos_name && *end == ':') end--;  /* remove trailing ':' before extension */
126     }
127     else
128     {
129         /* no extension, remove trailing spaces */
130         while (end >= dos_name && *end == ' ') end--;
131     }
132
133     /* now we have a potential device name between start and end, check it */
134     switch(end - start + 1)
135     {
136     case 3:
137         if (strncmpiW( start, auxW, 3 ) &&
138             strncmpiW( start, conW, 3 ) &&
139             strncmpiW( start, nulW, 3 ) &&
140             strncmpiW( start, prnW, 3 )) break;
141         return MAKELONG( 3 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
142     case 4:
143         if (strncmpiW( start, comW, 3 ) && strncmpiW( start, lptW, 3 )) break;
144         if (*end <= '0' || *end > '9') break;
145         return MAKELONG( 4 * sizeof(WCHAR), (start - dos_name) * sizeof(WCHAR) );
146     default:  /* can't match anything */
147         break;
148     }
149     return 0;
150 }
151
152
153 /**************************************************************************
154  *                 RtlDosPathNameToNtPathName_U         [NTDLL.@]
155  *
156  * dos_path: a DOS path name (fully qualified or not)
157  * ntpath:   pointer to a UNICODE_STRING to hold the converted
158  *           path name
159  * file_part:will point (in ntpath) to the file part in the path
160  * cd:       directory reference (optional)
161  *
162  * FIXME:
163  *      + fill the cd structure
164  */
165 BOOLEAN  WINAPI RtlDosPathNameToNtPathName_U(PWSTR dos_path,
166                                              PUNICODE_STRING ntpath,
167                                              PWSTR* file_part,
168                                              CURDIR* cd)
169 {
170     static const WCHAR LongFileNamePfxW[4] = {'\\','\\','?','\\'};
171     ULONG sz, ptr_sz, offset;
172     WCHAR local[MAX_PATH];
173     LPWSTR ptr;
174
175     TRACE("(%s,%p,%p,%p)\n",
176           debugstr_w(dos_path), ntpath, file_part, cd);
177
178     if (cd)
179     {
180         FIXME("Unsupported parameter\n");
181         memset(cd, 0, sizeof(*cd));
182     }
183
184     if (!dos_path || !*dos_path) return FALSE;
185
186     if (!memcmp(dos_path, LongFileNamePfxW, sizeof(LongFileNamePfxW)))
187     {
188         dos_path += sizeof(LongFileNamePfxW) / sizeof(WCHAR);
189         ptr = NULL;
190         ptr_sz = 0;
191     }
192     else
193     {
194         ptr = local;
195         ptr_sz = sizeof(local);
196     }
197     sz = RtlGetFullPathName_U(dos_path, ptr_sz, ptr, file_part);
198     if (sz == 0) return FALSE;
199     if (sz > ptr_sz)
200     {
201         ptr = RtlAllocateHeap(ntdll_get_process_heap(), 0, sz);
202         sz = RtlGetFullPathName_U(dos_path, sz, ptr, file_part);
203     }
204
205     ntpath->MaximumLength = sz + (4 /* unc\ */ + 4 /* \??\ */) * sizeof(WCHAR);
206     ntpath->Buffer = RtlAllocateHeap(ntdll_get_process_heap(), 0, ntpath->MaximumLength);
207     if (!ntpath->Buffer)
208     {
209         if (ptr != local) RtlFreeHeap(ntdll_get_process_heap(), 0, ptr);
210         return FALSE;
211     }
212
213     strcpyW(ntpath->Buffer, NTDosPrefixW);
214     offset = 0;
215     switch (RtlDetermineDosPathNameType_U(ptr))
216     {
217     case UNC_PATH: /* \\foo */
218         if (ptr[2] != '?')
219         {
220             offset = 2;
221             strcatW(ntpath->Buffer, UncPfxW);
222         }
223         break;
224     case DEVICE_PATH: /* \\.\foo */
225         offset = 4;
226         break;
227     default: break; /* needed to keep gcc quiet */
228     }
229
230     strcatW(ntpath->Buffer, ptr + offset);
231     ntpath->Length = strlenW(ntpath->Buffer) * sizeof(WCHAR);
232
233     if (file_part && *file_part)
234         *file_part = ntpath->Buffer + ntpath->Length / sizeof(WCHAR) - strlenW(*file_part);
235
236     /* FIXME: cd filling */
237
238     if (ptr != local) RtlFreeHeap(ntdll_get_process_heap(), 0, ptr);
239     return TRUE;
240 }
241
242 /******************************************************************
243  *              RtlDosSearchPath_U
244  *
245  * Searchs a file of name 'name' into a ';' separated list of paths
246  * (stored in paths)
247  * Doesn't seem to search elsewhere than the paths list
248  * Stores the result in buffer (file_part will point to the position
249  * of the file name in the buffer)
250  * FIXME:
251  * - how long shall the paths be ??? (MAX_PATH or larger with \\?\ constructs ???)
252  */
253 ULONG WINAPI RtlDosSearchPath_U(LPCWSTR paths, LPCWSTR search, LPCWSTR ext, 
254                                 ULONG buffer_size, LPWSTR buffer, 
255                                 LPWSTR* file_part)
256 {
257     DOS_PATHNAME_TYPE type = RtlDetermineDosPathNameType_U(search);
258     ULONG len = 0;
259
260     if (type == RELATIVE_PATH)
261     {
262         ULONG allocated = 0, needed, filelen;
263         WCHAR*  name = NULL;
264
265         filelen = 1 /* for \ */ + strlenW(search) + 1 /* \0 */;
266
267         if (strchrW(search, '.') != NULL) ext = NULL;
268         if (ext != NULL) filelen += strlenW(ext);
269
270         while (*paths)
271         {
272             LPCWSTR ptr;
273
274             for (needed = 0, ptr = paths; *ptr != 0 && *ptr++ != ';'; needed++);
275             if (needed + filelen > allocated)
276             {
277                 name = (WCHAR*)RtlReAllocateHeap(ntdll_get_process_heap(), 0,
278                                                  name, (needed + filelen) * sizeof(WCHAR));
279                 if (!name) return 0;
280                 allocated = needed + filelen;
281             }
282             memmove(name, paths, needed * sizeof(WCHAR));
283             /* append '\\' if none is present */
284             if (needed > 0 && name[needed - 1] != '\\') name[needed++] = '\\';
285             strcpyW(&name[needed], search);
286             if (ext) strcatW(&name[needed], ext);
287             if (RtlDoesFileExists_U(name))
288             {
289                 len = RtlGetFullPathName_U(name, buffer_size, buffer, file_part);
290                 break;
291             }
292             paths = ptr;
293         }
294         RtlFreeHeap(ntdll_get_process_heap(), 0, name);
295     }
296     else if (RtlDoesFileExists_U(search))
297     {
298         len = RtlGetFullPathName_U(search, buffer_size, buffer, file_part);
299     }
300
301     return len;
302 }
303
304 /******************************************************************
305  *              get_full_path_helper
306  *
307  * Helper for RtlGetFullPathName_U
308  */
309 static ULONG get_full_path_helper(LPCWSTR name, LPWSTR buffer, ULONG size)
310 {
311     ULONG               reqsize, mark = 0;
312     DOS_PATHNAME_TYPE   type;
313     LPWSTR              ptr;
314     UNICODE_STRING*     cd;
315
316     reqsize = sizeof(WCHAR); /* '\0' at the end */
317
318     RtlAcquirePebLock();
319     cd = &ntdll_get_process_pmts()->CurrentDirectoryName;
320
321     switch (type = RtlDetermineDosPathNameType_U(name))
322     {
323     case UNC_PATH:              /* \\foo   */
324     case DEVICE_PATH:           /* \\.\foo */
325         if (reqsize <= size) buffer[0] = '\0';
326         break;
327
328     case ABSOLUTE_DRIVE_PATH:   /* c:\foo  */
329         reqsize += sizeof(WCHAR);
330         if (reqsize <= size)
331         {
332             buffer[0] = toupperW(name[0]);
333             buffer[1] = '\0';
334         }
335         name++;
336         break;
337
338     case RELATIVE_DRIVE_PATH:   /* c:foo   */
339         if (toupperW(name[0]) != toupperW(cd->Buffer[0]) || cd->Buffer[1] != ':')
340         {
341             WCHAR               drive[4];
342             UNICODE_STRING      var, val;
343
344             drive[0] = '=';
345             drive[1] = name[0];
346             drive[2] = ':';
347             drive[3] = '\0';
348             var.Length = 6;
349             var.MaximumLength = 8;
350             var.Buffer = drive;
351             val.Length = 0;
352             val.MaximumLength = size;
353             val.Buffer = buffer;
354
355             name += 2;
356
357             switch (RtlQueryEnvironmentVariable_U(NULL, &var, &val))
358             {
359             case STATUS_SUCCESS:
360                 /* FIXME: Win2k seems to check that the environment variable actually points 
361                  * to an existing directory. If not, root of the drive is used
362                  * (this seems also to be the only spot in RtlGetFullPathName that the 
363                  * existence of a part of a path is checked)
364                  */
365                 /* fall thru */
366             case STATUS_BUFFER_TOO_SMALL:
367                 reqsize += val.Length;
368                 /* append trailing \\ */
369                 reqsize += sizeof(WCHAR);
370                 if (reqsize <= size)
371                 {
372                     buffer[reqsize / sizeof(WCHAR) - 2] = '\\';
373                     buffer[reqsize / sizeof(WCHAR) - 1] = '\0';
374                 }
375                 break;
376             case STATUS_VARIABLE_NOT_FOUND:
377                 reqsize += 3 * sizeof(WCHAR);
378                 if (reqsize <= size)
379                 {
380                     buffer[0] = drive[1];
381                     buffer[1] = ':';
382                     buffer[2] = '\\';
383                     buffer[3] = '\0';
384                 }
385                 break;
386             default:
387                 ERR("Unsupported status code\n");
388                 break;
389             }
390             break;
391         }
392         name += 2;
393         /* fall through */
394
395     case RELATIVE_PATH:         /* foo     */
396         reqsize += cd->Length;
397         mark = cd->Length / sizeof(WCHAR);
398         if (reqsize <= size)
399             strcpyW(buffer, cd->Buffer);
400         break;
401
402     case ABSOLUTE_PATH:         /* \xxx    */
403         if (cd->Buffer[1] == ':')
404         {
405             reqsize += 2 * sizeof(WCHAR);
406             if (reqsize <= size)
407             {
408                 buffer[0] = cd->Buffer[0];
409                 buffer[1] = ':';
410                 buffer[2] = '\0';
411             }
412         }
413         else
414         {
415             unsigned    len;
416
417             ptr = strchrW(cd->Buffer + 2, '\\');
418             if (ptr) ptr = strchrW(ptr + 1, '\\');
419             if (!ptr) ptr = cd->Buffer + strlenW(cd->Buffer);
420             len = (ptr - cd->Buffer) * sizeof(WCHAR);
421             reqsize += len;
422             mark = len / sizeof(WCHAR);
423             if (reqsize <= size)
424             {
425                 memcpy(buffer, cd->Buffer, len);
426                 buffer[len / sizeof(WCHAR)] = '\0';
427             }
428             else
429                 buffer[0] = '\0';
430         }
431         break;
432
433     case UNC_DOT_PATH:         /* \\.     */
434         reqsize += 4 * sizeof(WCHAR);
435         name += 3;
436         if (reqsize <= size)
437         {
438             buffer[0] = '\\';
439             buffer[1] = '\\';
440             buffer[2] = '.';
441             buffer[3] = '\\';
442             buffer[4] = '\0';
443         }
444         break;
445
446     case INVALID_PATH:
447         reqsize = 0;
448         goto done;
449     }
450
451     reqsize += strlenW(name) * sizeof(WCHAR);
452     if (reqsize > size) goto done;
453
454     strcatW(buffer, name);
455
456     /* convert every / into a \ */
457     for (ptr = buffer; ptr < buffer + size / sizeof(WCHAR); ptr++) 
458         if (*ptr == '/') *ptr = '\\';
459
460     reqsize -= sizeof(WCHAR); /* don't count trailing \0 */
461
462     /* mark is non NULL for UNC names, so start path collapsing after server & share name
463      * otherwise, it's a fully qualified DOS name, so start after the drive designation
464      */
465     for (ptr = buffer + (mark ? mark : 2); ptr < buffer + reqsize / sizeof(WCHAR); )
466     {
467         WCHAR* p = strchrW(ptr, '\\');
468         if (!p) break;
469
470         p++;
471         if (p[0] == '.')
472         {
473             switch (p[1])
474             {
475             case '.':
476                 switch (p[2])
477                 {
478                 case '\\':
479                     {
480                         WCHAR*      prev = p - 2;
481                         while (prev >= buffer + mark && *prev != '\\') prev--;
482                         /* either collapse \foo\.. into \ or \.. into \ */
483                         if (prev < buffer + mark) prev = p - 1;
484                         reqsize -= (p + 2 - prev) * sizeof(WCHAR);
485                         memmove(prev, p + 2, buffer + reqsize - prev + sizeof(WCHAR));
486                     }
487                     break;
488                 case '\0':
489                     reqsize -= 2 * sizeof(WCHAR);
490                     *p = 0;
491                     break;
492                 }
493                 break;
494             case '\\':
495                 reqsize -= 2 * sizeof(WCHAR);
496                 memmove(ptr + 2, ptr, buffer + reqsize - ptr + sizeof(WCHAR));
497                 break;
498             }
499         }
500         ptr = p;
501     }
502
503 done:
504     RtlReleasePebLock();
505     return reqsize;
506 }
507
508 /******************************************************************
509  *              RtlGetFullPathName_U  (NTDLL.@)
510  *
511  * Returns the number of bytes written to buffer (not including the
512  * terminating NULL) if the function succeeds, or the required number of bytes
513  * (including the terminating NULL) if the buffer is to small.
514  *
515  * file_part will point to the filename part inside buffer (except if we use
516  * DOS device name, in which case file_in_buf is NULL)
517  *
518  */
519 DWORD WINAPI RtlGetFullPathName_U(const WCHAR* name, ULONG size, WCHAR* buffer,
520                                   WCHAR** file_part)
521 {
522     DWORD       dosdev;
523     DWORD       reqsize;
524
525     TRACE("(%s %lu %p %p)\n", debugstr_w(name), size, buffer, file_part);
526
527     if (!name || !*name) return 0;
528
529     if (file_part) *file_part = NULL;
530
531     /* check for DOS device name */
532     dosdev = RtlIsDosDeviceName_U(name);
533     if (dosdev)
534     {
535         DWORD   offset = HIWORD(dosdev) / sizeof(WCHAR); /* get it in WCHARs, not bytes */
536         DWORD   sz = LOWORD(dosdev); /* in bytes */
537
538         if (8 + sz + 2 > size) return sz + 10;
539         strcpyW(buffer, DeviceRootW);
540         memmove(buffer + 4, name + offset, sz);
541         buffer[4 + sz / sizeof(WCHAR)] = '\0';
542         /* file_part isn't set in this case */
543         return sz + 8;
544     }
545
546     reqsize = get_full_path_helper(name, buffer, size);
547     if (reqsize > size)
548     {
549         LPWSTR  tmp = RtlAllocateHeap(ntdll_get_process_heap(), 0, reqsize);
550         reqsize = get_full_path_helper(name, tmp, reqsize) + sizeof(WCHAR);
551         RtlFreeHeap(ntdll_get_process_heap(), 0, tmp);
552     }
553     else
554     {
555         WCHAR*      ptr;
556         /* find file part */
557         if (file_part && (ptr = strrchrW(buffer, '\\')) != NULL && ptr >= buffer + 2 && *++ptr)
558             *file_part = ptr;
559     }
560     return reqsize;
561 }
562
563 /*************************************************************************
564  * RtlGetLongestNtPathLength    [NTDLL.@]
565  *
566  * Get the longest allowed path length
567  *
568  * PARAMS
569  *  None.
570  *
571  * RETURNS
572  *  The longest allowed path length (277 characters under Win2k).
573  */
574 DWORD WINAPI RtlGetLongestNtPathLength(void)
575 {
576     return 277;
577 }
578
579 /******************************************************************
580  *             RtlIsNameLegalDOS8Dot3   (NTDLL.@)
581  *
582  * Returns TRUE iff unicode is a valid DOS (8+3) name.
583  * If the name is valid, oem gets filled with the corresponding OEM string
584  * spaces is set to TRUE if unicode contains spaces
585  */
586 BOOLEAN WINAPI RtlIsNameLegalDOS8Dot3( const UNICODE_STRING *unicode,
587                                        OEM_STRING *oem, BOOLEAN *spaces )
588 {
589     int dot = -1;
590     unsigned int i;
591     char buffer[12];
592     OEM_STRING oem_str;
593     BOOLEAN got_space = FALSE;
594
595     if (!oem)
596     {
597         oem_str.Length = sizeof(buffer);
598         oem_str.MaximumLength = sizeof(buffer);
599         oem_str.Buffer = buffer;
600         oem = &oem_str;
601     }
602     if (RtlUpcaseUnicodeStringToCountedOemString( oem, unicode, FALSE ) != STATUS_SUCCESS)
603         return FALSE;
604
605     if (oem->Length > 12) return FALSE;
606
607     /* a starting . is invalid, except for . and .. */
608     if (oem->Buffer[0] == '.')
609     {
610         if (oem->Length != 1 && (oem->Length != 2 || oem->Buffer[1] != '.')) return FALSE;
611         if (spaces) *spaces = FALSE;
612         return TRUE;
613     }
614
615     for (i = 0; i < oem->Length; i++)
616     {
617         switch (oem->Buffer[i])
618         {
619         case ' ':
620             /* leading/trailing spaces not allowed */
621             if (!i || i == oem->Length-1 || oem->Buffer[i+1] == '.') return FALSE;
622             got_space = TRUE;
623             break;
624         case '.':
625             if (dot != -1) return FALSE;
626             dot = i;
627             break;
628         default:
629             /* FIXME: check for invalid chars */
630             break;
631         }
632     }
633     /* check file part is shorter than 8, extension shorter than 3
634      * dot cannot be last in string
635      */
636     if (dot == -1)
637     {
638         if (oem->Length > 8) return FALSE;
639     }
640     else
641     {
642         if (dot > 8 || (oem->Length - dot > 4) || dot == oem->Length - 1) return FALSE;
643     }
644     if (spaces) *spaces = got_space;
645     return TRUE;
646 }
647
648 /******************************************************************
649  *              RtlGetCurrentDirectory_U (NTDLL.@)
650  *
651  */
652 NTSTATUS WINAPI RtlGetCurrentDirectory_U(ULONG buflen, LPWSTR buf)
653 {
654     UNICODE_STRING*     us;
655     ULONG               len;
656
657     TRACE("(%lu %p)\n", buflen, buf);
658
659     RtlAcquirePebLock();
660
661     us = &ntdll_get_process_pmts()->CurrentDirectoryName;
662     len = us->Length / sizeof(WCHAR);
663     if (us->Buffer[len - 1] == '\\' && us->Buffer[len - 2] != ':')
664         len--;
665
666     if (buflen / sizeof(WCHAR) > len)
667     {
668         memcpy(buf, us->Buffer, len * sizeof(WCHAR));
669         buf[len] = '\0';
670     }
671     else
672     {
673         len++;
674     }
675
676     RtlReleasePebLock();
677
678     return len * sizeof(WCHAR);
679 }
680
681 /******************************************************************
682  *              RtlSetCurrentDirectory_U (NTDLL.@)
683  *
684  */
685 NTSTATUS WINAPI RtlSetCurrentDirectory_U(const UNICODE_STRING* dir)
686 {
687     UNICODE_STRING*     curdir;
688     NTSTATUS            nts = STATUS_SUCCESS;
689     ULONG               size;
690     PWSTR               buf = NULL;
691
692     TRACE("(%s)\n", debugstr_w(dir->Buffer));
693
694     RtlAcquirePebLock();
695
696     curdir = &ntdll_get_process_pmts()->CurrentDirectoryName;
697     size = curdir->MaximumLength;
698
699     buf = RtlAllocateHeap(ntdll_get_process_heap(), 0, size);
700     if (buf == NULL)
701     {
702         nts = STATUS_NO_MEMORY;
703         goto out;
704     }
705
706     size = RtlGetFullPathName_U(dir->Buffer, size, buf, 0);
707     if (!size)
708     {
709         nts = STATUS_OBJECT_NAME_INVALID;
710         goto out;
711     }
712
713     switch (RtlDetermineDosPathNameType_U(buf))
714     {
715     case ABSOLUTE_DRIVE_PATH:
716     case UNC_PATH:
717         break;
718     default:
719         FIXME("Don't support those cases yes\n");
720         nts = STATUS_NOT_IMPLEMENTED;
721         goto out;
722     }
723
724     /* FIXME: should check that the directory actually exists,
725      * and fill CurrentDirectoryHandle accordingly 
726      */
727
728     /* append trailing \ if missing */
729     if (buf[size / sizeof(WCHAR) - 1] != '\\')
730     {
731         buf[size / sizeof(WCHAR)] = '\\';
732         buf[size / sizeof(WCHAR) + 1] = '\0';
733         size += sizeof(WCHAR);
734     }
735
736     memmove(curdir->Buffer, buf, size + sizeof(WCHAR));
737     curdir->Length = size;
738
739 #if 0
740     if (curdir->Buffer[1] == ':')
741     {
742         UNICODE_STRING  env;
743         WCHAR           var[4];
744
745         var[0] = '=';
746         var[1] = curdir->Buffer[0];
747         var[2] = ':';
748         var[3] = 0;
749         env.Length = 3 * sizeof(WCHAR);
750         env.MaximumLength = 4 * sizeof(WCHAR);
751         env.Buffer = var;
752
753         RtlSetEnvironmentVariable(NULL, &env, curdir);
754     }
755 #endif
756
757  out:
758     if (buf) RtlFreeHeap(ntdll_get_process_heap(), 0, buf);
759
760     RtlReleasePebLock();
761
762     return nts;
763 }