Implemented SHCreateStdEnumFmtEtc.
[wine/hacks.git] / files / directory.c
blob8dfae7e4da93182779837296a0595a5dd31c6121
1 /*
2 * DOS directories functions
4 * Copyright 1995 Alexandre Julliard
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.
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.
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
21 #include "config.h"
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif
32 #include <errno.h>
33 #ifdef HAVE_SYS_ERRNO_H
34 #include <sys/errno.h>
35 #endif
37 #include "winbase.h"
38 #include "wine/winbase16.h"
39 #include "windef.h"
40 #include "wingdi.h"
41 #include "wine/winuser16.h"
42 #include "winerror.h"
43 #include "winreg.h"
44 #include "ntddk.h"
45 #include "wine/unicode.h"
46 #include "drive.h"
47 #include "file.h"
48 #include "heap.h"
49 #include "msdos.h"
50 #include "wine/debug.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
53 WINE_DECLARE_DEBUG_CHANNEL(file);
55 static DOS_FULL_NAME DIR_Windows;
56 static DOS_FULL_NAME DIR_System;
58 static const WCHAR wineW[] = {'w','i','n','e',0};
60 /***********************************************************************
61 * DIR_GetPath
63 * Get a path name from the wine.ini file and make sure it is valid.
65 static int DIR_GetPath( LPCWSTR keyname, LPCWSTR defval, DOS_FULL_NAME *full_name,
66 LPWSTR longname, INT longname_len, BOOL warn )
68 WCHAR path[MAX_PATHNAME_LEN];
69 BY_HANDLE_FILE_INFORMATION info;
70 const char *mess = "does not exist";
72 PROFILE_GetWineIniString( wineW, keyname, defval, path, MAX_PATHNAME_LEN );
74 if (!DOSFS_GetFullName( path, TRUE, full_name ) ||
75 (!FILE_Stat( full_name->long_name, &info ) && (mess=strerror(errno)))||
76 (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (mess="not a directory")) ||
77 (!(GetLongPathNameW(full_name->short_name, longname, longname_len))) )
79 if (warn)
80 MESSAGE("Invalid path %s for %s directory: %s\n",
81 debugstr_w(path), debugstr_w(keyname), mess);
82 return 0;
84 return 1;
88 /***********************************************************************
89 * DIR_Init
91 int DIR_Init(void)
93 char path[MAX_PATHNAME_LEN];
94 WCHAR longpath[MAX_PATHNAME_LEN];
95 DOS_FULL_NAME tmp_dir, profile_dir;
96 int drive;
97 const char *cwd;
98 static const WCHAR windowsW[] = {'w','i','n','d','o','w','s',0};
99 static const WCHAR systemW[] = {'s','y','s','t','e','m',0};
100 static const WCHAR tempW[] = {'t','e','m','p',0};
101 static const WCHAR profileW[] = {'p','r','o','f','i','l','e',0};
102 static const WCHAR windows_dirW[] = {'c',':','\\','w','i','n','d','o','w','s',0};
103 static const WCHAR system_dirW[] = {'c',':','\\','w','i','n','d','o','w','s','\\','s','y','s','t','e','m',0};
104 static const WCHAR pathW[] = {'p','a','t','h',0};
105 static const WCHAR path_dirW[] = {'c',':','\\','w','i','n','d','o','w','s',';',
106 'c',':','\\','w','i','n','d','o','w','s','\\','s','y','s','t','e','m',0};
107 static const WCHAR path_capsW[] = {'P','A','T','H',0};
108 static const WCHAR temp_capsW[] = {'T','E','M','P',0};
109 static const WCHAR tmp_capsW[] = {'T','M','P',0};
110 static const WCHAR windirW[] = {'w','i','n','d','i','r',0};
111 static const WCHAR winsysdirW[] = {'w','i','n','s','y','s','d','i','r',0};
112 static const WCHAR userprofileW[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
113 static const WCHAR systemrootW[] = {'S','Y','S','T','E','M','R','O','O','T',0};
114 static const WCHAR empty_strW[] = { 0 };
116 if (!getcwd( path, MAX_PATHNAME_LEN ))
118 perror( "Could not get current directory" );
119 return 0;
121 cwd = path;
122 if ((drive = DRIVE_FindDriveRoot( &cwd )) == -1)
124 MESSAGE("Warning: could not find wine config [Drive x] entry "
125 "for current working directory %s; "
126 "starting in windows directory.\n", cwd );
128 else
130 WCHAR szdrive[3]={drive+'A',':',0};
131 MultiByteToWideChar(DRIVE_GetCodepage(drive), 0, cwd, -1, longpath, MAX_PATHNAME_LEN);
132 DRIVE_SetCurrentDrive( drive );
133 DRIVE_Chdir( drive, longpath );
134 if(GetDriveTypeW(szdrive)==DRIVE_CDROM)
135 chdir("/"); /* change to root directory so as not to lock cdroms */
138 if (!(DIR_GetPath( windowsW, windows_dirW, &DIR_Windows, longpath, MAX_PATHNAME_LEN, TRUE )) ||
139 !(DIR_GetPath( systemW, system_dirW, &DIR_System, longpath, MAX_PATHNAME_LEN, TRUE )) ||
140 !(DIR_GetPath( tempW, windows_dirW, &tmp_dir, longpath, MAX_PATHNAME_LEN, TRUE )))
142 PROFILE_UsageWineIni();
143 return 0;
145 if (-1 == access( tmp_dir.long_name, W_OK ))
147 if (errno==EACCES)
149 MESSAGE("Warning: the temporary directory '%s' (specified in wine configuration file) is not writeable.\n", tmp_dir.long_name);
150 PROFILE_UsageWineIni();
152 else
153 MESSAGE("Warning: access to temporary directory '%s' failed (%s).\n",
154 tmp_dir.long_name, strerror(errno));
157 if (drive == -1)
159 drive = DIR_Windows.drive;
160 DRIVE_SetCurrentDrive( drive );
161 DRIVE_Chdir( drive, DIR_Windows.short_name + 2 );
164 PROFILE_GetWineIniString(wineW, pathW, path_dirW, longpath, MAX_PATHNAME_LEN);
165 if (strchrW(longpath, '/'))
167 MESSAGE("Fix your wine config to use DOS drive syntax in [wine] 'Path=' statement! (no '/' allowed)\n");
168 PROFILE_UsageWineIni();
169 ExitProcess(1);
172 /* Set the environment variables */
174 SetEnvironmentVariableW( path_capsW, longpath );
175 SetEnvironmentVariableW( temp_capsW, tmp_dir.short_name );
176 SetEnvironmentVariableW( tmp_capsW, tmp_dir.short_name );
177 SetEnvironmentVariableW( windirW, DIR_Windows.short_name );
178 SetEnvironmentVariableW( winsysdirW, DIR_System.short_name );
180 /* set COMSPEC only if it doesn't exist already */
181 if (!GetEnvironmentVariableA( "COMSPEC", NULL, 0 ))
182 SetEnvironmentVariableA( "COMSPEC", "c:\\command.com" );
184 TRACE("WindowsDir = %s (%s)\n",
185 debugstr_w(DIR_Windows.short_name), DIR_Windows.long_name );
186 TRACE("SystemDir = %s (%s)\n",
187 debugstr_w(DIR_System.short_name), DIR_System.long_name );
188 TRACE("TempDir = %s (%s)\n",
189 debugstr_w(tmp_dir.short_name), tmp_dir.long_name );
190 TRACE("Path = %s\n", debugstr_w(longpath) );
191 TRACE("Cwd = %c:\\%s\n",
192 'A' + drive, debugstr_w(DRIVE_GetDosCwd(drive)) );
194 if (DIR_GetPath( profileW, empty_strW, &profile_dir, longpath, MAX_PATHNAME_LEN, FALSE ))
196 TRACE("USERPROFILE= %s\n", debugstr_w(longpath) );
197 SetEnvironmentVariableW( userprofileW, longpath );
200 TRACE("SYSTEMROOT = %s\n", debugstr_w(DIR_Windows.short_name) );
201 SetEnvironmentVariableW( systemrootW, DIR_Windows.short_name );
203 return 1;
207 /***********************************************************************
208 * GetTempPathA (KERNEL32.@)
210 UINT WINAPI GetTempPathA( UINT count, LPSTR path )
212 WCHAR pathW[MAX_PATH];
213 UINT ret;
215 ret = GetTempPathW(MAX_PATH, pathW);
217 if (!ret)
218 return 0;
220 if (ret > MAX_PATH)
222 SetLastError(ERROR_FILENAME_EXCED_RANGE);
223 return 0;
226 ret = WideCharToMultiByte(CP_ACP, 0, pathW, -1, NULL, 0, NULL, NULL);
227 if (ret <= count)
229 WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, count, NULL, NULL);
230 ret--; /* length without 0 */
232 return ret;
236 /***********************************************************************
237 * GetTempPathW (KERNEL32.@)
239 UINT WINAPI GetTempPathW( UINT count, LPWSTR path )
241 static const WCHAR tmp[] = { 'T', 'M', 'P', 0 };
242 static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
243 WCHAR tmp_path[MAX_PATH];
244 UINT ret;
246 TRACE("%u,%p\n", count, path);
248 if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )))
249 if (!(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )))
250 if (!(ret = GetCurrentDirectoryW( MAX_PATH, tmp_path )))
251 return 0;
253 if (ret > MAX_PATH)
255 SetLastError(ERROR_FILENAME_EXCED_RANGE);
256 return 0;
259 ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
260 if (!ret) return 0;
262 if (ret > MAX_PATH - 2)
264 SetLastError(ERROR_FILENAME_EXCED_RANGE);
265 return 0;
268 if (tmp_path[ret-1] != '\\')
270 tmp_path[ret++] = '\\';
271 tmp_path[ret] = '\0';
274 ret++; /* add space for terminating 0 */
276 if (count)
278 lstrcpynW(path, tmp_path, count);
279 if (count >= ret)
280 ret--; /* return length without 0 */
281 else if (count < 4)
282 path[0] = 0; /* avoid returning ambiguous "X:" */
285 TRACE("returning %u, %s\n", ret, debugstr_w(path));
286 return ret;
290 /***********************************************************************
291 * DIR_GetWindowsUnixDir
293 UINT DIR_GetWindowsUnixDir( LPSTR path, UINT count )
295 if (path) lstrcpynA( path, DIR_Windows.long_name, count );
296 return strlen( DIR_Windows.long_name );
300 /***********************************************************************
301 * DIR_GetSystemUnixDir
303 UINT DIR_GetSystemUnixDir( LPSTR path, UINT count )
305 if (path) lstrcpynA( path, DIR_System.long_name, count );
306 return strlen( DIR_System.long_name );
310 /***********************************************************************
311 * GetTempDrive (KERNEL.92)
312 * A closer look at krnl386.exe shows what the SDK doesn't mention:
314 * returns:
315 * AL: driveletter
316 * AH: ':' - yes, some kernel code even does stosw with
317 * the returned AX.
318 * DX: 1 for success
320 UINT WINAPI GetTempDrive( BYTE ignored )
322 char *buffer;
323 BYTE ret;
324 UINT len = GetTempPathA( 0, NULL );
326 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len + 1 )) )
327 ret = DRIVE_GetCurrentDrive() + 'A';
328 else
330 /* FIXME: apparently Windows does something with the ignored byte */
331 if (!GetTempPathA( len, buffer )) buffer[0] = 'C';
332 ret = toupper(buffer[0]);
333 HeapFree( GetProcessHeap(), 0, buffer );
335 return MAKELONG( ret | (':' << 8), 1 );
339 /***********************************************************************
340 * GetWindowsDirectory (KERNEL.134)
342 UINT16 WINAPI GetWindowsDirectory16( LPSTR path, UINT16 count )
344 return (UINT16)GetWindowsDirectoryA( path, count );
348 /***********************************************************************
349 * GetWindowsDirectoryW (KERNEL32.@)
351 * See comment for GetWindowsDirectoryA.
353 UINT WINAPI GetWindowsDirectoryW( LPWSTR path, UINT count )
355 UINT len = strlenW( DIR_Windows.short_name ) + 1;
356 if (path && count >= len)
358 strcpyW( path, DIR_Windows.short_name );
359 len--;
361 return len;
365 /***********************************************************************
366 * GetWindowsDirectoryA (KERNEL32.@)
368 * Return value:
369 * If buffer is large enough to hold full path and terminating '\0' character
370 * function copies path to buffer and returns length of the path without '\0'.
371 * Otherwise function returns required size including '\0' character and
372 * does not touch the buffer.
374 UINT WINAPI GetWindowsDirectoryA( LPSTR path, UINT count )
376 UINT len = WideCharToMultiByte( CP_ACP, 0, DIR_Windows.short_name, -1, NULL, 0, NULL, NULL );
377 if (path && count >= len)
379 WideCharToMultiByte( CP_ACP, 0, DIR_Windows.short_name, -1, path, count, NULL, NULL );
380 len--;
382 return len;
386 /***********************************************************************
387 * GetSystemWindowsDirectoryA (KERNEL32.@) W2K, TS4.0SP4
389 UINT WINAPI GetSystemWindowsDirectoryA( LPSTR path, UINT count )
391 return GetWindowsDirectoryA( path, count );
395 /***********************************************************************
396 * GetSystemWindowsDirectoryW (KERNEL32.@) W2K, TS4.0SP4
398 UINT WINAPI GetSystemWindowsDirectoryW( LPWSTR path, UINT count )
400 return GetWindowsDirectoryW( path, count );
404 /***********************************************************************
405 * GetSystemDirectory (KERNEL.135)
407 UINT16 WINAPI GetSystemDirectory16( LPSTR path, UINT16 count )
409 return (UINT16)GetSystemDirectoryA( path, count );
413 /***********************************************************************
414 * GetSystemDirectoryW (KERNEL32.@)
416 * See comment for GetWindowsDirectoryA.
418 UINT WINAPI GetSystemDirectoryW( LPWSTR path, UINT count )
420 UINT len = strlenW( DIR_System.short_name ) + 1;
421 if (path && count >= len)
423 strcpyW( path, DIR_System.short_name );
424 len--;
426 return len;
430 /***********************************************************************
431 * GetSystemDirectoryA (KERNEL32.@)
433 * See comment for GetWindowsDirectoryA.
435 UINT WINAPI GetSystemDirectoryA( LPSTR path, UINT count )
437 UINT len = WideCharToMultiByte( CP_ACP, 0, DIR_System.short_name, -1, NULL, 0, NULL, NULL );
438 if (path && count >= len)
440 WideCharToMultiByte( CP_ACP, 0, DIR_System.short_name, -1, path, count, NULL, NULL );
441 len--;
443 return len;
447 /***********************************************************************
448 * CreateDirectory (KERNEL.144)
450 BOOL16 WINAPI CreateDirectory16( LPCSTR path, LPVOID dummy )
452 TRACE_(file)("(%s,%p)\n", path, dummy );
453 return (BOOL16)CreateDirectoryA( path, NULL );
457 /***********************************************************************
458 * CreateDirectoryW (KERNEL32.@)
459 * RETURNS:
460 * TRUE : success
461 * FALSE : failure
462 * ERROR_DISK_FULL: on full disk
463 * ERROR_ALREADY_EXISTS: if directory name exists (even as file)
464 * ERROR_ACCESS_DENIED: on permission problems
465 * ERROR_FILENAME_EXCED_RANGE: too long filename(s)
467 BOOL WINAPI CreateDirectoryW( LPCWSTR path,
468 LPSECURITY_ATTRIBUTES lpsecattribs )
470 DOS_FULL_NAME full_name;
472 if (!path || !*path)
474 SetLastError(ERROR_PATH_NOT_FOUND);
475 return FALSE;
478 TRACE_(file)("(%s,%p)\n", debugstr_w(path), lpsecattribs );
480 if (DOSFS_GetDevice( path ))
482 TRACE_(file)("cannot use device %s!\n", debugstr_w(path));
483 SetLastError( ERROR_ACCESS_DENIED );
484 return FALSE;
486 if (!DOSFS_GetFullName( path, FALSE, &full_name )) return 0;
487 if (mkdir( full_name.long_name, 0777 ) == -1) {
488 WARN_(file)("Error '%s' trying to create directory '%s'\n", strerror(errno), full_name.long_name);
489 /* the FILE_SetDosError() generated error codes don't match the
490 * CreateDirectory ones for some errnos */
491 switch (errno) {
492 case EEXIST:
494 if (!strcmp(DRIVE_GetRoot(full_name.drive), full_name.long_name))
495 SetLastError(ERROR_ACCESS_DENIED);
496 else
497 SetLastError(ERROR_ALREADY_EXISTS);
498 break;
500 case ENOSPC: SetLastError(ERROR_DISK_FULL); break;
501 default: FILE_SetDosError();break;
503 return FALSE;
505 return TRUE;
509 /***********************************************************************
510 * CreateDirectoryA (KERNEL32.@)
512 BOOL WINAPI CreateDirectoryA( LPCSTR path,
513 LPSECURITY_ATTRIBUTES lpsecattribs )
515 UNICODE_STRING pathW;
516 BOOL ret = FALSE;
518 if (!path || !*path)
520 SetLastError(ERROR_PATH_NOT_FOUND);
521 return FALSE;
524 if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
526 ret = CreateDirectoryW(pathW.Buffer, lpsecattribs);
527 RtlFreeUnicodeString(&pathW);
529 else
530 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
531 return ret;
535 /***********************************************************************
536 * CreateDirectoryExA (KERNEL32.@)
538 BOOL WINAPI CreateDirectoryExA( LPCSTR template, LPCSTR path,
539 LPSECURITY_ATTRIBUTES lpsecattribs)
541 return CreateDirectoryA(path,lpsecattribs);
545 /***********************************************************************
546 * CreateDirectoryExW (KERNEL32.@)
548 BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path,
549 LPSECURITY_ATTRIBUTES lpsecattribs)
551 return CreateDirectoryW(path,lpsecattribs);
555 /***********************************************************************
556 * RemoveDirectory (KERNEL.145)
558 BOOL16 WINAPI RemoveDirectory16( LPCSTR path )
560 return (BOOL16)RemoveDirectoryA( path );
564 /***********************************************************************
565 * RemoveDirectoryW (KERNEL32.@)
567 BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
569 DOS_FULL_NAME full_name;
571 if (!path)
573 SetLastError(ERROR_INVALID_PARAMETER);
574 return FALSE;
577 TRACE_(file)("%s\n", debugstr_w(path));
579 if (DOSFS_GetDevice( path ))
581 TRACE_(file)("cannot remove device %s!\n", debugstr_w(path));
582 SetLastError( ERROR_FILE_NOT_FOUND );
583 return FALSE;
585 if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
586 if (rmdir( full_name.long_name ) == -1)
588 FILE_SetDosError();
589 return FALSE;
591 return TRUE;
595 /***********************************************************************
596 * RemoveDirectoryA (KERNEL32.@)
598 BOOL WINAPI RemoveDirectoryA( LPCSTR path )
600 UNICODE_STRING pathW;
601 BOOL ret = FALSE;
603 if (!path)
605 SetLastError(ERROR_INVALID_PARAMETER);
606 return FALSE;
609 if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
611 ret = RemoveDirectoryW(pathW.Buffer);
612 RtlFreeUnicodeString(&pathW);
614 else
615 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
616 return ret;
620 /***********************************************************************
621 * DIR_TryPath
623 * Helper function for DIR_SearchPath.
625 static BOOL DIR_TryPath( const DOS_FULL_NAME *dir, LPCWSTR name,
626 DOS_FULL_NAME *full_name )
628 LPSTR p_l = full_name->long_name + strlen(dir->long_name) + 1;
629 LPWSTR p_s = full_name->short_name + strlenW(dir->short_name) + 1;
631 if ((p_s >= full_name->short_name + sizeof(full_name->short_name)/sizeof(full_name->short_name[0]) - 14) ||
632 (p_l >= full_name->long_name + sizeof(full_name->long_name) - 1))
634 SetLastError( ERROR_PATH_NOT_FOUND );
635 return FALSE;
637 if (!DOSFS_FindUnixName( dir, name, p_l,
638 sizeof(full_name->long_name) - (p_l - full_name->long_name),
639 p_s, !(DRIVE_GetFlags(dir->drive) & DRIVE_CASE_SENSITIVE) ))
640 return FALSE;
642 full_name->drive = dir->drive;
643 strcpy( full_name->long_name, dir->long_name );
644 p_l[-1] = '/';
645 strcpyW( full_name->short_name, dir->short_name );
646 p_s[-1] = '\\';
647 return TRUE;
650 static BOOL DIR_SearchSemicolonedPaths(LPCWSTR name, DOS_FULL_NAME *full_name, LPWSTR pathlist)
652 LPWSTR next, buffer = NULL;
653 INT len = strlenW(name), newlen, currlen = 0;
654 BOOL ret = FALSE;
656 next = pathlist;
657 while (!ret && next)
659 static const WCHAR bkslashW[] = {'\\',0};
660 LPWSTR cur = next;
661 while (*cur == ';') cur++;
662 if (!*cur) break;
663 next = strchrW( cur, ';' );
664 if (next) *next++ = '\0';
665 newlen = strlenW(cur) + len + 2;
667 if (newlen > currlen)
669 if (!(buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, newlen * sizeof(WCHAR))))
670 goto done;
671 currlen = newlen;
674 strcpyW( buffer, cur );
675 strcatW( buffer, bkslashW );
676 strcatW( buffer, name );
677 ret = DOSFS_GetFullName( buffer, TRUE, full_name );
679 done:
680 HeapFree( GetProcessHeap(), 0, buffer );
681 return ret;
685 /***********************************************************************
686 * DIR_TryEnvironmentPath
688 * Helper function for DIR_SearchPath.
689 * Search in the specified path, or in $PATH if NULL.
691 static BOOL DIR_TryEnvironmentPath( LPCWSTR name, DOS_FULL_NAME *full_name, LPCWSTR envpath )
693 LPWSTR path;
694 BOOL ret = FALSE;
695 DWORD size;
696 static const WCHAR pathW[] = {'P','A','T','H',0};
698 size = envpath ? strlenW(envpath)+1 : GetEnvironmentVariableW( pathW, NULL, 0 );
699 if (!size) return FALSE;
700 if (!(path = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
701 if (envpath) strcpyW( path, envpath );
702 else if (!GetEnvironmentVariableW( pathW, path, size )) goto done;
704 ret = DIR_SearchSemicolonedPaths(name, full_name, path);
706 done:
707 HeapFree( GetProcessHeap(), 0, path );
708 return ret;
712 /***********************************************************************
713 * DIR_TryModulePath
715 * Helper function for DIR_SearchPath.
717 static BOOL DIR_TryModulePath( LPCWSTR name, DOS_FULL_NAME *full_name, BOOL win32 )
719 /* FIXME: for now, GetModuleFileNameW can't return more */
720 /* than OFS_MAXPATHNAME. This may change with Win32. */
721 WCHAR bufferW[OFS_MAXPATHNAME];
722 LPWSTR p;
724 if (!win32)
726 char buffer[OFS_MAXPATHNAME];
727 if (!GetCurrentTask()) return FALSE;
728 if (!GetModuleFileName16( GetCurrentTask(), buffer, sizeof(buffer) ))
729 return FALSE;
730 MultiByteToWideChar(CP_ACP, 0, buffer, -1, bufferW, OFS_MAXPATHNAME);
731 } else {
732 if (!GetModuleFileNameW( 0, bufferW, OFS_MAXPATHNAME ) )
733 return FALSE;
735 if (!(p = strrchrW( bufferW, '\\' ))) return FALSE;
736 if (OFS_MAXPATHNAME - (++p - bufferW) <= strlenW(name)) return FALSE;
737 strcpyW( p, name );
738 return DOSFS_GetFullName( bufferW, TRUE, full_name );
742 /***********************************************************************
743 * DIR_TryAppPath
745 * Helper function for DIR_SearchPath.
747 static BOOL DIR_TryAppPath( LPCWSTR name, DOS_FULL_NAME *full_name )
749 HKEY hkAppPaths = 0, hkApp = 0;
750 WCHAR lpAppName[MAX_PATHNAME_LEN], lpAppPaths[MAX_PATHNAME_LEN];
751 LPWSTR lpFileName;
752 BOOL res = FALSE;
753 DWORD type, count;
754 static const WCHAR PathW[] = {'P','a','t','h',0};
756 if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths", &hkAppPaths) != ERROR_SUCCESS)
757 return FALSE;
759 if (!GetModuleFileNameW(0, lpAppName, MAX_PATHNAME_LEN))
761 WARN("huh, module not found ??\n");
762 goto end;
764 lpFileName = strrchrW(lpAppName, '\\');
765 if (!lpFileName)
766 goto end;
767 else lpFileName++; /* skip '\\' */
768 if (RegOpenKeyW(hkAppPaths, lpFileName, &hkApp) != ERROR_SUCCESS)
769 goto end;
770 count = sizeof(lpAppPaths);
771 if (RegQueryValueExW(hkApp, PathW, 0, &type, (LPBYTE)lpAppPaths, &count) != ERROR_SUCCESS)
772 goto end;
773 TRACE("successfully opened App Paths for %s\n", debugstr_w(lpFileName));
775 res = DIR_SearchSemicolonedPaths(name, full_name, lpAppPaths);
776 end:
777 if (hkApp)
778 RegCloseKey(hkApp);
779 if (hkAppPaths)
780 RegCloseKey(hkAppPaths);
781 return res;
784 /***********************************************************************
785 * DIR_SearchPath
787 * Implementation of SearchPathA. 'win32' specifies whether the search
788 * order is Win16 (module path last) or Win32 (module path first).
790 * FIXME: should return long path names.
792 DWORD DIR_SearchPath( LPCWSTR path, LPCWSTR name, LPCWSTR ext,
793 DOS_FULL_NAME *full_name, BOOL win32 )
795 LPCWSTR p;
796 LPWSTR tmp = NULL;
797 BOOL ret = TRUE;
799 /* First check the supplied parameters */
801 p = strrchrW( name, '.' );
802 if (p && !strchrW( p, '/' ) && !strchrW( p, '\\' ))
803 ext = NULL; /* Ignore the specified extension */
804 if (FILE_contains_pathW (name))
805 path = NULL; /* Ignore path if name already contains a path */
806 if (path && !*path) path = NULL; /* Ignore empty path */
808 /* Allocate a buffer for the file name and extension */
810 if (ext)
812 DWORD len = strlenW(name) + strlenW(ext);
813 if (!(tmp = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
815 SetLastError( ERROR_OUTOFMEMORY );
816 return 0;
818 strcpyW( tmp, name );
819 strcatW( tmp, ext );
820 name = tmp;
823 /* If the name contains an explicit path, everything's easy */
825 if (FILE_contains_pathW(name))
827 ret = DOSFS_GetFullName( name, TRUE, full_name );
828 goto done;
831 /* Search in the specified path */
833 if (path)
835 ret = DIR_TryEnvironmentPath( name, full_name, path );
836 goto done;
839 /* Try the path of the current executable (for Win32 search order) */
841 if (win32 && DIR_TryModulePath( name, full_name, win32 )) goto done;
843 /* Try the current directory */
845 if (DOSFS_GetFullName( name, TRUE, full_name )) goto done;
847 /* Try the Windows system directory */
849 if (DIR_TryPath( &DIR_System, name, full_name ))
850 goto done;
852 /* Try the Windows directory */
854 if (DIR_TryPath( &DIR_Windows, name, full_name ))
855 goto done;
857 /* Try the path of the current executable (for Win16 search order) */
859 if (!win32 && DIR_TryModulePath( name, full_name, win32 )) goto done;
861 /* Try the "App Paths" entry if existing (undocumented ??) */
862 if (DIR_TryAppPath(name, full_name))
863 goto done;
865 /* Try all directories in path */
867 ret = DIR_TryEnvironmentPath( name, full_name, NULL );
869 done:
870 if (tmp) HeapFree( GetProcessHeap(), 0, tmp );
871 return ret;
875 /***********************************************************************
876 * SearchPathW [KERNEL32.@]
878 * Searches for a specified file in the search path.
880 * PARAMS
881 * path [I] Path to search
882 * name [I] Filename to search for.
883 * ext [I] File extension to append to file name. The first
884 * character must be a period. This parameter is
885 * specified only if the filename given does not
886 * contain an extension.
887 * buflen [I] size of buffer, in characters
888 * buffer [O] buffer for found filename
889 * lastpart [O] address of pointer to last used character in
890 * buffer (the final '\')
892 * RETURNS
893 * Success: length of string copied into buffer, not including
894 * terminating null character. If the filename found is
895 * longer than the length of the buffer, the length of the
896 * filename is returned.
897 * Failure: Zero
899 * NOTES
900 * If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
901 * (tested on NT 4.0)
903 DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext, DWORD buflen,
904 LPWSTR buffer, LPWSTR *lastpart )
906 LPSTR res;
907 DOS_FULL_NAME full_name;
909 if (!DIR_SearchPath( path, name, ext, &full_name, TRUE ))
911 SetLastError(ERROR_FILE_NOT_FOUND);
912 return 0;
915 TRACE("found %s %s\n", full_name.long_name, debugstr_w(full_name.short_name));
916 TRACE("drive %c: root %s\n", 'A' + full_name.drive, DRIVE_GetRoot(full_name.drive));
918 lstrcpynW( buffer, full_name.short_name, buflen );
919 res = full_name.long_name +
920 strlen(DRIVE_GetRoot( full_name.drive ));
921 while (*res == '/') res++;
922 if (buflen)
924 LPWSTR p;
925 if (buflen > 3)
927 MultiByteToWideChar(DRIVE_GetCodepage(full_name.drive), 0,
928 res, -1, buffer + 3, buflen - 3);
929 buffer[buflen - 1] = 0;
931 for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
932 if (lastpart) *lastpart = strrchrW( buffer, '\\' ) + 1;
934 TRACE("Returning %s\n", debugstr_w(buffer) );
935 return strlenW(buffer);
939 /***********************************************************************
940 * SearchPathA (KERNEL32.@)
942 DWORD WINAPI SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext,
943 DWORD buflen, LPSTR buffer, LPSTR *lastpart )
945 UNICODE_STRING pathW, nameW, extW;
946 WCHAR bufferW[MAX_PATH];
947 DWORD ret, retW;
949 if (path) RtlCreateUnicodeStringFromAsciiz(&pathW, path);
950 else pathW.Buffer = NULL;
951 if (name) RtlCreateUnicodeStringFromAsciiz(&nameW, name);
952 else nameW.Buffer = NULL;
953 if (ext) RtlCreateUnicodeStringFromAsciiz(&extW, ext);
954 else extW.Buffer = NULL;
956 retW = SearchPathW(pathW.Buffer, nameW.Buffer, extW.Buffer, MAX_PATH, bufferW, NULL);
958 if (!retW)
959 ret = 0;
960 else if (retW > MAX_PATH)
962 SetLastError(ERROR_FILENAME_EXCED_RANGE);
963 ret = 0;
965 else
967 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
968 if (buflen >= ret)
970 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buflen, NULL, NULL);
971 ret--; /* length without 0 */
972 if (lastpart) *lastpart = strrchr(buffer, '\\') + 1;
976 RtlFreeUnicodeString(&pathW);
977 RtlFreeUnicodeString(&nameW);
978 RtlFreeUnicodeString(&extW);
979 return ret;
983 /***********************************************************************
984 * search_alternate_path
987 * FIXME: should return long path names.?
989 static BOOL search_alternate_path(LPCWSTR dll_path, LPCWSTR name, LPCWSTR ext,
990 DOS_FULL_NAME *full_name)
992 LPCWSTR p;
993 LPWSTR tmp = NULL;
994 BOOL ret = TRUE;
996 /* First check the supplied parameters */
998 p = strrchrW( name, '.' );
999 if (p && !strchrW( p, '/' ) && !strchrW( p, '\\' ))
1000 ext = NULL; /* Ignore the specified extension */
1002 /* Allocate a buffer for the file name and extension */
1004 if (ext)
1006 DWORD len = strlenW(name) + strlenW(ext);
1007 if (!(tmp = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
1009 SetLastError( ERROR_OUTOFMEMORY );
1010 return 0;
1012 strcpyW( tmp, name );
1013 strcatW( tmp, ext );
1014 name = tmp;
1017 if (DIR_TryEnvironmentPath (name, full_name, dll_path))
1019 else if (DOSFS_GetFullName (name, TRUE, full_name)) /* current dir */
1021 else if (DIR_TryPath (&DIR_System, name, full_name)) /* System dir */
1023 else if (DIR_TryPath (&DIR_Windows, name, full_name)) /* Windows dir */
1025 else
1026 ret = DIR_TryEnvironmentPath( name, full_name, NULL );
1028 if (tmp) HeapFree( GetProcessHeap(), 0, tmp );
1029 return ret;
1033 /***********************************************************************
1034 * DIR_SearchAlternatePath
1036 * Searches for a specified file in the search path.
1038 * PARAMS
1039 * dll_path [I] Path to search
1040 * name [I] Filename to search for.
1041 * ext [I] File extension to append to file name. The first
1042 * character must be a period. This parameter is
1043 * specified only if the filename given does not
1044 * contain an extension.
1045 * buflen [I] size of buffer, in characters
1046 * buffer [O] buffer for found filename
1047 * lastpart [O] address of pointer to last used character in
1048 * buffer (the final '\') (May be NULL)
1050 * RETURNS
1051 * Success: length of string copied into buffer, not including
1052 * terminating null character. If the filename found is
1053 * longer than the length of the buffer, the length of the
1054 * filename is returned.
1055 * Failure: Zero
1057 * NOTES
1058 * If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
1060 * FIXME: convert to unicode
1062 DWORD DIR_SearchAlternatePath( LPCSTR dll_path, LPCSTR name, LPCSTR ext,
1063 DWORD buflen, LPSTR buffer, LPSTR *lastpart )
1065 LPSTR p;
1066 DOS_FULL_NAME full_name;
1067 DWORD ret = 0;
1068 UNICODE_STRING dll_pathW, nameW, extW;
1070 if (dll_path) RtlCreateUnicodeStringFromAsciiz(&dll_pathW, dll_path);
1071 else dll_pathW.Buffer = NULL;
1072 if (name) RtlCreateUnicodeStringFromAsciiz(&nameW, name);
1073 else nameW.Buffer = NULL;
1074 if (ext) RtlCreateUnicodeStringFromAsciiz(&extW, ext);
1075 else extW.Buffer = NULL;
1077 if (search_alternate_path( dll_pathW.Buffer, nameW.Buffer, extW.Buffer, &full_name))
1079 ret = WideCharToMultiByte(CP_ACP, 0, full_name.short_name, -1, NULL, 0, NULL, NULL);
1080 if (buflen >= ret)
1082 WideCharToMultiByte(CP_ACP, 0, full_name.short_name, -1, buffer, buflen, NULL, NULL);
1083 for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
1084 if (lastpart) *lastpart = strrchr( buffer, '\\' ) + 1;
1085 ret--; /* length without 0 */
1088 else
1089 SetLastError(ERROR_FILE_NOT_FOUND);
1091 RtlFreeUnicodeString(&dll_pathW);
1092 RtlFreeUnicodeString(&nameW);
1093 RtlFreeUnicodeString(&extW);
1095 TRACE("Returning %ld\n", ret );
1096 return ret;