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