Don't print FIXME for FILE_ATTRIBUTE_TEMPORARY.
[wine] / files / directory.c
1 /*
2  * DOS directories functions
3  *
4  * Copyright 1995 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #include <errno.h>
34 #ifdef HAVE_SYS_ERRNO_H
35 #include <sys/errno.h>
36 #endif
37
38 #include "ntstatus.h"
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wine/winbase16.h"
42 #include "wingdi.h"
43 #include "wine/winuser16.h"
44 #include "winerror.h"
45 #include "winreg.h"
46 #include "winternl.h"
47 #include "wine/unicode.h"
48 #include "drive.h"
49 #include "file.h"
50 #include "wine/debug.h"
51
52 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
53 WINE_DECLARE_DEBUG_CHANNEL(file);
54
55 static DOS_FULL_NAME DIR_Windows;
56 static DOS_FULL_NAME DIR_System;
57
58 /***********************************************************************
59  *           FILE_contains_pathW
60  */
61 inline static int FILE_contains_pathW (LPCWSTR name)
62 {
63     return ((*name && (name[1] == ':')) ||
64             strchrW (name, '/') || strchrW (name, '\\'));
65 }
66
67 /***********************************************************************
68  *           DIR_GetPath
69  *
70  * Get a path name from the wine.ini file and make sure it is valid.
71  */
72 static int DIR_GetPath( HKEY hkey, LPCWSTR keyname, LPCWSTR defval, DOS_FULL_NAME *full_name,
73                         LPWSTR longname, INT longname_len, BOOL warn )
74 {
75     UNICODE_STRING nameW;
76     DWORD dummy;
77     WCHAR tmp[MAX_PATHNAME_LEN];
78     BY_HANDLE_FILE_INFORMATION info;
79     const WCHAR *path = defval;
80     const char *mess = "does not exist";
81
82     RtlInitUnicodeString( &nameW, keyname );
83     if (hkey && !NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
84         path = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
85
86     if (!DOSFS_GetFullName( path, TRUE, full_name ) ||
87         (!FILE_Stat( full_name->long_name, &info, NULL ) && (mess=strerror(errno)))||
88         (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (mess="not a directory")) ||
89         (!(GetLongPathNameW(full_name->short_name, longname, longname_len))) )
90     {
91         if (warn)
92         {
93             MESSAGE("Invalid path %s for %s directory: %s.\n",
94                     debugstr_w(path), debugstr_w(keyname), mess);
95             MESSAGE("Perhaps you have not properly edited your Wine configuration file (%s/config)\n",
96                     wine_get_config_dir());
97         }
98         return 0;
99     }
100     return 1;
101 }
102
103
104 /***********************************************************************
105  *           DIR_Init
106  */
107 int DIR_Init(void)
108 {
109     OBJECT_ATTRIBUTES attr;
110     UNICODE_STRING nameW;
111     HKEY hkey;
112     char path[MAX_PATHNAME_LEN];
113     WCHAR longpath[MAX_PATHNAME_LEN];
114     DOS_FULL_NAME tmp_dir, profile_dir;
115     int drive;
116     const char *cwd;
117     static const WCHAR wineW[] = {'M','a','c','h','i','n','e','\\',
118                                   'S','o','f','t','w','a','r','e','\\',
119                                   'W','i','n','e','\\','W','i','n','e','\\',
120                                   'C','o','n','f','i','g','\\','W','i','n','e',0};
121     static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
122     static const WCHAR systemW[] = {'s','y','s','t','e','m',0};
123     static const WCHAR tempW[] = {'t','e','m','p',0};
124     static const WCHAR profileW[] = {'p','r','o','f','i','l','e',0};
125     static const WCHAR windows_dirW[] = {'c',':','\\','w','i','n','d','o','w','s',0};
126     static const WCHAR system_dirW[] = {'c',':','\\','w','i','n','d','o','w','s','\\','s','y','s','t','e','m',0};
127     static const WCHAR pathW[] = {'p','a','t','h',0};
128     static const WCHAR path_dirW[] = {'c',':','\\','w','i','n','d','o','w','s',';',
129                                       'c',':','\\','w','i','n','d','o','w','s','\\','s','y','s','t','e','m',0};
130     static const WCHAR path_capsW[] = {'P','A','T','H',0};
131     static const WCHAR temp_capsW[] = {'T','E','M','P',0};
132     static const WCHAR tmp_capsW[] = {'T','M','P',0};
133     static const WCHAR windirW[] = {'w','i','n','d','i','r',0};
134     static const WCHAR winsysdirW[] = {'w','i','n','s','y','s','d','i','r',0};
135     static const WCHAR userprofileW[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
136     static const WCHAR systemrootW[] = {'S','Y','S','T','E','M','R','O','O','T',0};
137     static const WCHAR wcmdW[] = {'\\','w','c','m','d','.','e','x','e',0};
138     static const WCHAR comspecW[] = {'C','O','M','S','P','E','C',0};
139     static const WCHAR empty_strW[] = { 0 };
140
141     if (!getcwd( path, MAX_PATHNAME_LEN ))
142     {
143         perror( "Could not get current directory" );
144         return 0;
145     }
146     cwd = path;
147     if ((drive = DRIVE_FindDriveRoot( &cwd )) == -1)
148     {
149         MESSAGE("Warning: could not find wine config [Drive x] entry "
150             "for current working directory %s; "
151             "starting in windows directory.\n", cwd );
152     }
153     else
154     {
155         WCHAR szdrive[3]={drive+'A',':',0};
156         MultiByteToWideChar(CP_UNIXCP, 0, cwd, -1, longpath, MAX_PATHNAME_LEN);
157         DRIVE_SetCurrentDrive( drive );
158         DRIVE_Chdir( drive, longpath );
159         if(GetDriveTypeW(szdrive)==DRIVE_CDROM)
160             chdir("/"); /* change to root directory so as not to lock cdroms */
161     }
162
163     attr.Length = sizeof(attr);
164     attr.RootDirectory = 0;
165     attr.ObjectName = &nameW;
166     attr.Attributes = 0;
167     attr.SecurityDescriptor = NULL;
168     attr.SecurityQualityOfService = NULL;
169
170     RtlInitUnicodeString( &nameW, wineW );
171     if (NtCreateKey( &hkey, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL )) hkey = 0;
172
173     if (!(DIR_GetPath( hkey, windowsW, windows_dirW, &DIR_Windows, longpath, MAX_PATHNAME_LEN, TRUE )) ||
174         !(DIR_GetPath( hkey, systemW, system_dirW, &DIR_System, longpath, MAX_PATHNAME_LEN, TRUE )) ||
175         !(DIR_GetPath( hkey, tempW, windows_dirW, &tmp_dir, longpath, MAX_PATHNAME_LEN, TRUE )))
176     {
177         if (hkey) NtClose( hkey );
178         return 0;
179     }
180     if (-1 == access( tmp_dir.long_name, W_OK ))
181     {
182         if (errno==EACCES)
183         {
184                 MESSAGE("Warning: the temporary directory '%s' specified in your\n"
185                         "configuration file (%s) is not writeable.\n",
186                         tmp_dir.long_name, wine_get_config_dir() );
187         }
188         else
189                 MESSAGE("Warning: access to temporary directory '%s' failed (%s).\n",
190                     tmp_dir.long_name, strerror(errno));
191     }
192
193     if (drive == -1)
194     {
195         drive = DIR_Windows.drive;
196         DRIVE_SetCurrentDrive( drive );
197         DRIVE_Chdir( drive, DIR_Windows.short_name + 2 );
198     }
199
200     /* Set the environment variables */
201
202     /* set COMSPEC only if it doesn't exist already */
203     if (!GetEnvironmentVariableW( comspecW, NULL, 0 ))
204     {
205         strcpyW( longpath, DIR_System.short_name );
206         strcatW( longpath, wcmdW );
207         SetEnvironmentVariableW( comspecW, longpath );
208     }
209
210     /* set PATH only if not set already */
211     if (!GetEnvironmentVariableW( path_capsW, NULL, 0 ))
212     {
213         WCHAR tmp[MAX_PATHNAME_LEN];
214         DWORD dummy;
215         const WCHAR *path = path_dirW;
216
217         RtlInitUnicodeString( &nameW, pathW );
218         if (hkey && !NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
219                                       tmp, sizeof(tmp), &dummy ))
220         {
221             path = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
222         }
223
224         if (strchrW(path, '/'))
225         {
226             MESSAGE("Fix your wine config (%s/config) to use DOS drive syntax in [wine] 'Path=' statement! (no '/' allowed)\n", wine_get_config_dir() );
227             ExitProcess(1);
228         }
229         SetEnvironmentVariableW( path_capsW, path );
230         TRACE("Path       = %s\n", debugstr_w(path) );
231     }
232
233     SetEnvironmentVariableW( temp_capsW, tmp_dir.short_name );
234     SetEnvironmentVariableW( tmp_capsW, tmp_dir.short_name );
235     SetEnvironmentVariableW( windirW, DIR_Windows.short_name );
236     SetEnvironmentVariableW( winsysdirW, DIR_System.short_name );
237
238     TRACE("WindowsDir = %s (%s)\n",
239           debugstr_w(DIR_Windows.short_name), DIR_Windows.long_name );
240     TRACE("SystemDir  = %s (%s)\n",
241           debugstr_w(DIR_System.short_name), DIR_System.long_name );
242     TRACE("TempDir    = %s (%s)\n",
243           debugstr_w(tmp_dir.short_name), tmp_dir.long_name );
244     TRACE("Cwd        = %c:\\%s\n",
245           'A' + drive, debugstr_w(DRIVE_GetDosCwd(drive)) );
246
247     if (DIR_GetPath( hkey, profileW, empty_strW, &profile_dir, longpath, MAX_PATHNAME_LEN, FALSE ))
248     {
249         TRACE("USERPROFILE= %s\n", debugstr_w(longpath) );
250         SetEnvironmentVariableW( userprofileW, longpath );
251     }
252
253     TRACE("SYSTEMROOT = %s\n", debugstr_w(DIR_Windows.short_name) );
254     SetEnvironmentVariableW( systemrootW, DIR_Windows.short_name );
255     if (hkey) NtClose( hkey );
256
257     return 1;
258 }
259
260
261 /***********************************************************************
262  *           GetTempPathA   (KERNEL32.@)
263  */
264 UINT WINAPI GetTempPathA( UINT count, LPSTR path )
265 {
266     WCHAR pathW[MAX_PATH];
267     UINT ret;
268
269     ret = GetTempPathW(MAX_PATH, pathW);
270
271     if (!ret)
272         return 0;
273
274     if (ret > MAX_PATH)
275     {
276         SetLastError(ERROR_FILENAME_EXCED_RANGE);
277         return 0;
278     }
279
280     ret = WideCharToMultiByte(CP_ACP, 0, pathW, -1, NULL, 0, NULL, NULL);
281     if (ret <= count)
282     {
283         WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, count, NULL, NULL);
284         ret--; /* length without 0 */
285     }
286     return ret;
287 }
288
289
290 /***********************************************************************
291  *           GetTempPathW   (KERNEL32.@)
292  */
293 UINT WINAPI GetTempPathW( UINT count, LPWSTR path )
294 {
295     static const WCHAR tmp[]  = { 'T', 'M', 'P', 0 };
296     static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
297     WCHAR tmp_path[MAX_PATH];
298     UINT ret;
299
300     TRACE("%u,%p\n", count, path);
301
302     if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )))
303         if (!(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )))
304             if (!(ret = GetCurrentDirectoryW( MAX_PATH, tmp_path )))
305                 return 0;
306
307     if (ret > MAX_PATH)
308     {
309         SetLastError(ERROR_FILENAME_EXCED_RANGE);
310         return 0;
311     }
312
313     ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
314     if (!ret) return 0;
315
316     if (ret > MAX_PATH - 2)
317     {
318         SetLastError(ERROR_FILENAME_EXCED_RANGE);
319         return 0;
320     }
321
322     if (tmp_path[ret-1] != '\\')
323     {
324         tmp_path[ret++] = '\\';
325         tmp_path[ret]   = '\0';
326     }
327
328     ret++; /* add space for terminating 0 */
329
330     if (count)
331     {
332         lstrcpynW(path, tmp_path, count);
333         if (count >= ret)
334             ret--; /* return length without 0 */
335         else if (count < 4)
336             path[0] = 0; /* avoid returning ambiguous "X:" */
337     }
338
339     TRACE("returning %u, %s\n", ret, debugstr_w(path));
340     return ret;
341 }
342
343
344 /***********************************************************************
345  *           DIR_GetWindowsUnixDir
346  */
347 UINT DIR_GetWindowsUnixDir( LPSTR path, UINT count )
348 {
349     if (path) lstrcpynA( path, DIR_Windows.long_name, count );
350     return strlen( DIR_Windows.long_name );
351 }
352
353
354 /***********************************************************************
355  *           DIR_GetSystemUnixDir
356  */
357 UINT DIR_GetSystemUnixDir( LPSTR path, UINT count )
358 {
359     if (path) lstrcpynA( path, DIR_System.long_name, count );
360     return strlen( DIR_System.long_name );
361 }
362
363
364 /***********************************************************************
365  *           GetTempDrive   (KERNEL.92)
366  * A closer look at krnl386.exe shows what the SDK doesn't mention:
367  *
368  * returns:
369  *   AL: driveletter
370  *   AH: ':'            - yes, some kernel code even does stosw with
371  *                            the returned AX.
372  *   DX: 1 for success
373  */
374 UINT WINAPI GetTempDrive( BYTE ignored )
375 {
376     char *buffer;
377     BYTE ret;
378     UINT len = GetTempPathA( 0, NULL );
379
380     if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len + 1 )) )
381         ret = DRIVE_GetCurrentDrive() + 'A';
382     else
383     {
384         /* FIXME: apparently Windows does something with the ignored byte */
385         if (!GetTempPathA( len, buffer )) buffer[0] = 'C';
386         ret = toupper(buffer[0]);
387         HeapFree( GetProcessHeap(), 0, buffer );
388     }
389     return MAKELONG( ret | (':' << 8), 1 );
390 }
391
392
393 /***********************************************************************
394  *           GetWindowsDirectory   (KERNEL.134)
395  */
396 UINT16 WINAPI GetWindowsDirectory16( LPSTR path, UINT16 count )
397 {
398     return (UINT16)GetWindowsDirectoryA( path, count );
399 }
400
401
402 /***********************************************************************
403  *           GetWindowsDirectoryW   (KERNEL32.@)
404  *
405  * See comment for GetWindowsDirectoryA.
406  */
407 UINT WINAPI GetWindowsDirectoryW( LPWSTR path, UINT count )
408 {
409     UINT len = strlenW( DIR_Windows.short_name ) + 1;
410     if (path && count >= len)
411     {
412         strcpyW( path, DIR_Windows.short_name );
413         len--;
414     }
415     return len;
416 }
417
418
419 /***********************************************************************
420  *           GetWindowsDirectoryA   (KERNEL32.@)
421  *
422  * Return value:
423  * If buffer is large enough to hold full path and terminating '\0' character
424  * function copies path to buffer and returns length of the path without '\0'.
425  * Otherwise function returns required size including '\0' character and
426  * does not touch the buffer.
427  */
428 UINT WINAPI GetWindowsDirectoryA( LPSTR path, UINT count )
429 {
430     UINT len = WideCharToMultiByte( CP_ACP, 0, DIR_Windows.short_name, -1, NULL, 0, NULL, NULL );
431     if (path && count >= len)
432     {
433         WideCharToMultiByte( CP_ACP, 0, DIR_Windows.short_name, -1, path, count, NULL, NULL );
434         len--;
435     }
436     return len;
437 }
438
439
440 /***********************************************************************
441  *           GetSystemWindowsDirectoryA   (KERNEL32.@) W2K, TS4.0SP4
442  */
443 UINT WINAPI GetSystemWindowsDirectoryA( LPSTR path, UINT count )
444 {
445     return GetWindowsDirectoryA( path, count );
446 }
447
448
449 /***********************************************************************
450  *           GetSystemWindowsDirectoryW   (KERNEL32.@) W2K, TS4.0SP4
451  */
452 UINT WINAPI GetSystemWindowsDirectoryW( LPWSTR path, UINT count )
453 {
454     return GetWindowsDirectoryW( path, count );
455 }
456
457
458 /***********************************************************************
459  *           GetSystemDirectory   (KERNEL.135)
460  */
461 UINT16 WINAPI GetSystemDirectory16( LPSTR path, UINT16 count )
462 {
463     return (UINT16)GetSystemDirectoryA( path, count );
464 }
465
466
467 /***********************************************************************
468  *           GetSystemDirectoryW   (KERNEL32.@)
469  *
470  * See comment for GetWindowsDirectoryA.
471  */
472 UINT WINAPI GetSystemDirectoryW( LPWSTR path, UINT count )
473 {
474     UINT len = strlenW( DIR_System.short_name ) + 1;
475     if (path && count >= len)
476     {
477         strcpyW( path, DIR_System.short_name );
478         len--;
479     }
480     return len;
481 }
482
483
484 /***********************************************************************
485  *           GetSystemDirectoryA   (KERNEL32.@)
486  *
487  * See comment for GetWindowsDirectoryA.
488  */
489 UINT WINAPI GetSystemDirectoryA( LPSTR path, UINT count )
490 {
491     UINT len = WideCharToMultiByte( CP_ACP, 0, DIR_System.short_name, -1, NULL, 0, NULL, NULL );
492     if (path && count >= len)
493     {
494         WideCharToMultiByte( CP_ACP, 0, DIR_System.short_name, -1, path, count, NULL, NULL );
495         len--;
496     }
497     return len;
498 }
499
500
501 /***********************************************************************
502  *           CreateDirectory   (KERNEL.144)
503  */
504 BOOL16 WINAPI CreateDirectory16( LPCSTR path, LPVOID dummy )
505 {
506     TRACE_(file)("(%s,%p)\n", path, dummy );
507     return (BOOL16)CreateDirectoryA( path, NULL );
508 }
509
510
511 /***********************************************************************
512  *           CreateDirectoryW   (KERNEL32.@)
513  * RETURNS:
514  *      TRUE : success
515  *      FALSE : failure
516  *              ERROR_DISK_FULL:        on full disk
517  *              ERROR_ALREADY_EXISTS:   if directory name exists (even as file)
518  *              ERROR_ACCESS_DENIED:    on permission problems
519  *              ERROR_FILENAME_EXCED_RANGE: too long filename(s)
520  */
521 BOOL WINAPI CreateDirectoryW( LPCWSTR path,
522                                   LPSECURITY_ATTRIBUTES lpsecattribs )
523 {
524     DOS_FULL_NAME full_name;
525
526     if (!path || !*path)
527     {
528         SetLastError(ERROR_PATH_NOT_FOUND);
529         return FALSE;
530     }
531
532     TRACE_(file)("(%s,%p)\n", debugstr_w(path), lpsecattribs );
533
534     if (DOSFS_GetDevice( path ))
535     {
536         TRACE_(file)("cannot use device %s!\n", debugstr_w(path));
537         SetLastError( ERROR_ACCESS_DENIED );
538         return FALSE;
539     }
540     if (!DOSFS_GetFullName( path, FALSE, &full_name )) return 0;
541     if (mkdir( full_name.long_name, 0777 ) == -1) {
542         WARN_(file)("Error '%s' trying to create directory '%s'\n", strerror(errno), full_name.long_name);
543         /* the FILE_SetDosError() generated error codes don't match the
544          * CreateDirectory ones for some errnos */
545         switch (errno) {
546         case EEXIST:
547         {
548             if (!strcmp(DRIVE_GetRoot(full_name.drive), full_name.long_name))
549                 SetLastError(ERROR_ACCESS_DENIED);
550             else
551                 SetLastError(ERROR_ALREADY_EXISTS);
552             break;
553         }
554         case ENOSPC: SetLastError(ERROR_DISK_FULL); break;
555         default: FILE_SetDosError();break;
556         }
557         return FALSE;
558     }
559     return TRUE;
560 }
561
562
563 /***********************************************************************
564  *           CreateDirectoryA   (KERNEL32.@)
565  */
566 BOOL WINAPI CreateDirectoryA( LPCSTR path,
567                                   LPSECURITY_ATTRIBUTES lpsecattribs )
568 {
569     UNICODE_STRING pathW;
570     BOOL ret = FALSE;
571
572     if (!path || !*path)
573     {
574         SetLastError(ERROR_PATH_NOT_FOUND);
575         return FALSE;
576     }
577
578     if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
579     {
580         ret = CreateDirectoryW(pathW.Buffer, lpsecattribs);
581         RtlFreeUnicodeString(&pathW);
582     }
583     else
584         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
585     return ret;
586 }
587
588
589 /***********************************************************************
590  *           CreateDirectoryExA   (KERNEL32.@)
591  */
592 BOOL WINAPI CreateDirectoryExA( LPCSTR template, LPCSTR path,
593                                     LPSECURITY_ATTRIBUTES lpsecattribs)
594 {
595     return CreateDirectoryA(path,lpsecattribs);
596 }
597
598
599 /***********************************************************************
600  *           CreateDirectoryExW   (KERNEL32.@)
601  */
602 BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path,
603                                     LPSECURITY_ATTRIBUTES lpsecattribs)
604 {
605     return CreateDirectoryW(path,lpsecattribs);
606 }
607
608
609 /***********************************************************************
610  *           RemoveDirectory   (KERNEL.145)
611  */
612 BOOL16 WINAPI RemoveDirectory16( LPCSTR path )
613 {
614     return (BOOL16)RemoveDirectoryA( path );
615 }
616
617
618 /***********************************************************************
619  *           RemoveDirectoryW   (KERNEL32.@)
620  */
621 BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
622 {
623     DOS_FULL_NAME full_name;
624
625     if (!path)
626     {
627         SetLastError(ERROR_INVALID_PARAMETER);
628         return FALSE;
629     }
630
631     TRACE_(file)("%s\n", debugstr_w(path));
632
633     if (DOSFS_GetDevice( path ))
634     {
635         TRACE_(file)("cannot remove device %s!\n", debugstr_w(path));
636         SetLastError( ERROR_FILE_NOT_FOUND );
637         return FALSE;
638     }
639     if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
640     if (rmdir( full_name.long_name ) == -1)
641     {
642         FILE_SetDosError();
643         return FALSE;
644     }
645     return TRUE;
646 }
647
648
649 /***********************************************************************
650  *           RemoveDirectoryA   (KERNEL32.@)
651  */
652 BOOL WINAPI RemoveDirectoryA( LPCSTR path )
653 {
654     UNICODE_STRING pathW;
655     BOOL ret = FALSE;
656
657     if (!path)
658     {
659         SetLastError(ERROR_INVALID_PARAMETER);
660         return FALSE;
661     }
662
663     if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
664     {
665         ret = RemoveDirectoryW(pathW.Buffer);
666         RtlFreeUnicodeString(&pathW);
667     }
668     else
669         SetLastError(ERROR_NOT_ENOUGH_MEMORY);
670     return ret;
671 }
672
673
674 /***********************************************************************
675  *           DIR_TryPath
676  *
677  * Helper function for DIR_SearchPath.
678  */
679 static BOOL DIR_TryPath( const DOS_FULL_NAME *dir, LPCWSTR name,
680                            DOS_FULL_NAME *full_name )
681 {
682     LPSTR p_l = full_name->long_name + strlen(dir->long_name) + 1;
683     LPWSTR p_s = full_name->short_name + strlenW(dir->short_name) + 1;
684
685     if ((p_s >= full_name->short_name + sizeof(full_name->short_name)/sizeof(full_name->short_name[0]) - 14) ||
686         (p_l >= full_name->long_name + sizeof(full_name->long_name) - 1))
687     {
688         SetLastError( ERROR_PATH_NOT_FOUND );
689         return FALSE;
690     }
691     if (!DOSFS_FindUnixName( dir, name, p_l,
692                    sizeof(full_name->long_name) - (p_l - full_name->long_name),
693                    p_s, !(DRIVE_GetFlags(dir->drive) & DRIVE_CASE_SENSITIVE) ))
694         return FALSE;
695
696     full_name->drive = dir->drive;
697     strcpy( full_name->long_name, dir->long_name );
698     p_l[-1] = '/';
699     strcpyW( full_name->short_name, dir->short_name );
700     p_s[-1] = '\\';
701     return TRUE;
702 }
703
704 static BOOL DIR_SearchSemicolonedPaths(LPCWSTR name, DOS_FULL_NAME *full_name, LPWSTR pathlist)
705 {
706     LPWSTR next, buffer = NULL;
707     INT len = strlenW(name), newlen, currlen = 0;
708     BOOL ret = FALSE;
709
710     next = pathlist;
711     while (!ret && next)
712     {
713         static const WCHAR bkslashW[] = {'\\',0};
714         LPWSTR cur = next;
715         while (*cur == ';') cur++;
716         if (!*cur) break;
717         next = strchrW( cur, ';' );
718         if (next) *next++ = '\0';
719         newlen = strlenW(cur) + len + 2;
720
721         if (newlen > currlen)
722         {
723             if (buffer) 
724                 buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, newlen * sizeof(WCHAR));
725             else
726                 buffer = HeapAlloc( GetProcessHeap(), 0, newlen * sizeof(WCHAR));
727
728             if(!buffer)
729                 goto done;
730             currlen = newlen;
731         }
732
733         strcpyW( buffer, cur );
734         strcatW( buffer, bkslashW );
735         strcatW( buffer, name );
736         ret = DOSFS_GetFullName( buffer, TRUE, full_name );
737     }
738 done:
739     HeapFree( GetProcessHeap(), 0, buffer );
740     return ret;
741 }
742
743
744 /***********************************************************************
745  *           DIR_TryEnvironmentPath
746  *
747  * Helper function for DIR_SearchPath.
748  * Search in the specified path, or in $PATH if NULL.
749  */
750 static BOOL DIR_TryEnvironmentPath( LPCWSTR name, DOS_FULL_NAME *full_name, LPCWSTR envpath )
751 {
752     LPWSTR path;
753     BOOL ret = FALSE;
754     DWORD size;
755     static const WCHAR pathW[] = {'P','A','T','H',0};
756
757     size = envpath ? strlenW(envpath)+1 : GetEnvironmentVariableW( pathW, NULL, 0 );
758     if (!size) return FALSE;
759     if (!(path = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
760     if (envpath) strcpyW( path, envpath );
761     else if (!GetEnvironmentVariableW( pathW, path, size )) goto done;
762
763     ret = DIR_SearchSemicolonedPaths(name, full_name, path);
764
765 done:
766     HeapFree( GetProcessHeap(), 0, path );
767     return ret;
768 }
769
770
771 /***********************************************************************
772  *           DIR_TryModulePath
773  *
774  * Helper function for DIR_SearchPath.
775  */
776 static BOOL DIR_TryModulePath( LPCWSTR name, DOS_FULL_NAME *full_name, BOOL win32 )
777 {
778     WCHAR bufferW[MAX_PATH];
779     LPWSTR p;
780
781     if (!win32)
782     {
783         char buffer[OFS_MAXPATHNAME];
784         if (!GetCurrentTask()) return FALSE;
785         if (!GetModuleFileName16( GetCurrentTask(), buffer, sizeof(buffer) ))
786             return FALSE;
787         MultiByteToWideChar(CP_ACP, 0, buffer, -1, bufferW, MAX_PATH);
788     } else {
789         if (!GetModuleFileNameW( 0, bufferW, MAX_PATH ) )
790             return FALSE;
791     }
792     if (!(p = strrchrW( bufferW, '\\' ))) return FALSE;
793     if (MAX_PATH - (++p - bufferW) <= strlenW(name)) return FALSE;
794     strcpyW( p, name );
795     return DOSFS_GetFullName( bufferW, TRUE, full_name );
796 }
797
798
799 /***********************************************************************
800  *           DIR_SearchPath
801  *
802  * Implementation of SearchPathA. 'win32' specifies whether the search
803  * order is Win16 (module path last) or Win32 (module path first).
804  *
805  * FIXME: should return long path names.
806  */
807 DWORD DIR_SearchPath( LPCWSTR path, LPCWSTR name, LPCWSTR ext,
808                       DOS_FULL_NAME *full_name, BOOL win32 )
809 {
810     LPCWSTR p;
811     LPWSTR tmp = NULL;
812     BOOL ret = TRUE;
813
814     /* First check the supplied parameters */
815
816     p = strrchrW( name, '.' );
817     if (p && !strchrW( p, '/' ) && !strchrW( p, '\\' ))
818         ext = NULL;  /* Ignore the specified extension */
819     if (FILE_contains_pathW (name))
820         path = NULL;  /* Ignore path if name already contains a path */
821     if (path && !*path) path = NULL;  /* Ignore empty path */
822
823     /* Allocate a buffer for the file name and extension */
824
825     if (ext)
826     {
827         DWORD len = strlenW(name) + strlenW(ext);
828         if (!(tmp = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
829         {
830             SetLastError( ERROR_OUTOFMEMORY );
831             return 0;
832         }
833         strcpyW( tmp, name );
834         strcatW( tmp, ext );
835         name = tmp;
836     }
837
838     /* If the name contains an explicit path, everything's easy */
839
840     if (FILE_contains_pathW(name))
841     {
842         ret = DOSFS_GetFullName( name, TRUE, full_name );
843         goto done;
844     }
845
846     /* Search in the specified path */
847
848     if (path)
849     {
850         ret = DIR_TryEnvironmentPath( name, full_name, path );
851         goto done;
852     }
853
854     /* Try the path of the current executable (for Win32 search order) */
855
856     if (win32 && DIR_TryModulePath( name, full_name, win32 )) goto done;
857
858     /* Try the current directory */
859
860     if (DOSFS_GetFullName( name, TRUE, full_name )) goto done;
861
862     /* Try the Windows system directory */
863
864     if (DIR_TryPath( &DIR_System, name, full_name ))
865         goto done;
866
867     /* Try the Windows directory */
868
869     if (DIR_TryPath( &DIR_Windows, name, full_name ))
870         goto done;
871
872     /* Try the path of the current executable (for Win16 search order) */
873
874     if (!win32 && DIR_TryModulePath( name, full_name, win32 )) goto done;
875
876     /* Try all directories in path */
877
878     ret = DIR_TryEnvironmentPath( name, full_name, NULL );
879
880 done:
881     if (tmp) HeapFree( GetProcessHeap(), 0, tmp );
882     return ret;
883 }
884
885
886 /***********************************************************************
887  * SearchPathW [KERNEL32.@]
888  *
889  * Searches for a specified file in the search path.
890  *
891  * PARAMS
892  *    path      [I] Path to search
893  *    name      [I] Filename to search for.
894  *    ext       [I] File extension to append to file name. The first
895  *                  character must be a period. This parameter is
896  *                  specified only if the filename given does not
897  *                  contain an extension.
898  *    buflen    [I] size of buffer, in characters
899  *    buffer    [O] buffer for found filename
900  *    lastpart  [O] address of pointer to last used character in
901  *                  buffer (the final '\')
902  *
903  * RETURNS
904  *    Success: length of string copied into buffer, not including
905  *             terminating null character. If the filename found is
906  *             longer than the length of the buffer, the length of the
907  *             filename is returned.
908  *    Failure: Zero
909  *
910  * NOTES
911  *    If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
912  *    (tested on NT 4.0)
913  */
914 DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext, DWORD buflen,
915                           LPWSTR buffer, LPWSTR *lastpart )
916 {
917     LPSTR res;
918     DOS_FULL_NAME full_name;
919
920     if (!DIR_SearchPath( path, name, ext, &full_name, TRUE ))
921     {
922         SetLastError(ERROR_FILE_NOT_FOUND);
923         return 0;
924     }
925
926 TRACE("found %s %s\n", full_name.long_name, debugstr_w(full_name.short_name));
927 TRACE("drive %c: root %s\n", 'A' + full_name.drive, DRIVE_GetRoot(full_name.drive));
928
929     lstrcpynW( buffer, full_name.short_name, buflen );
930     res = full_name.long_name +
931               strlen(DRIVE_GetRoot( full_name.drive ));
932     while (*res == '/') res++;
933     if (buflen)
934     {
935         LPWSTR p;
936         if (buflen > 3)
937         {
938             MultiByteToWideChar(CP_UNIXCP, 0, res, -1, buffer + 3, buflen - 3);
939             buffer[buflen - 1] = 0;
940         }
941         for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
942         if (lastpart) *lastpart = strrchrW( buffer, '\\' ) + 1;
943     }
944     TRACE("Returning %s\n", debugstr_w(buffer) );
945     return strlenW(buffer);
946 }
947
948
949 /***********************************************************************
950  *           SearchPathA   (KERNEL32.@)
951  */
952 DWORD WINAPI SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext,
953                           DWORD buflen, LPSTR buffer, LPSTR *lastpart )
954 {
955     UNICODE_STRING pathW, nameW, extW;
956     WCHAR bufferW[MAX_PATH];
957     DWORD ret, retW;
958
959     if (path) RtlCreateUnicodeStringFromAsciiz(&pathW, path);
960     else pathW.Buffer = NULL;
961     if (name) RtlCreateUnicodeStringFromAsciiz(&nameW, name);
962     else nameW.Buffer = NULL;
963     if (ext) RtlCreateUnicodeStringFromAsciiz(&extW, ext);
964     else extW.Buffer = NULL;
965
966     retW = SearchPathW(pathW.Buffer, nameW.Buffer, extW.Buffer, MAX_PATH, bufferW, NULL);
967
968     if (!retW)
969         ret = 0;
970     else if (retW > MAX_PATH)
971     {
972         SetLastError(ERROR_FILENAME_EXCED_RANGE);
973         ret = 0;
974     }
975     else
976     {
977         ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
978         if (buflen >= ret)
979         {
980             WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buflen, NULL, NULL);
981             ret--; /* length without 0 */
982             if (lastpart) *lastpart = strrchr(buffer, '\\') + 1;
983         }
984     }
985
986     RtlFreeUnicodeString(&pathW);
987     RtlFreeUnicodeString(&nameW);
988     RtlFreeUnicodeString(&extW);
989     return ret;
990 }