Portability fix.
[wine/wine64.git] / files / directory.c
blob9d5cae6368da859f5c8ebcd0a4e7d2fafcfc1bf7
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 #include <unistd.h>
30 #include <errno.h>
31 #ifdef HAVE_SYS_ERRNO_H
32 #include <sys/errno.h>
33 #endif
35 #include "winbase.h"
36 #include "wine/winbase16.h"
37 #include "windef.h"
38 #include "wingdi.h"
39 #include "wine/winuser16.h"
40 #include "winerror.h"
41 #include "winreg.h"
42 #include "drive.h"
43 #include "file.h"
44 #include "heap.h"
45 #include "msdos.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(dosfs);
49 WINE_DECLARE_DEBUG_CHANNEL(file);
51 static DOS_FULL_NAME DIR_Windows;
52 static DOS_FULL_NAME DIR_System;
55 /***********************************************************************
56 * DIR_GetPath
58 * Get a path name from the wine.ini file and make sure it is valid.
60 static int DIR_GetPath( const char *keyname, const char *defval,
61 DOS_FULL_NAME *full_name, char * longname, BOOL warn )
63 char path[MAX_PATHNAME_LEN];
64 BY_HANDLE_FILE_INFORMATION info;
65 const char *mess = "does not exist";
67 PROFILE_GetWineIniString( "wine", keyname, defval, path, sizeof(path) );
68 if (!DOSFS_GetFullName( path, TRUE, full_name ) ||
69 (!FILE_Stat( full_name->long_name, &info ) && (mess=strerror(errno)))||
70 (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (mess="not a directory")) ||
71 (!(GetLongPathNameA(full_name->short_name, longname, MAX_PATHNAME_LEN))) )
73 if (warn)
74 MESSAGE("Invalid path '%s' for %s directory: %s\n", path, keyname, mess);
75 return 0;
77 return 1;
81 /***********************************************************************
82 * DIR_Init
84 int DIR_Init(void)
86 char path[MAX_PATHNAME_LEN];
87 char longpath[MAX_PATHNAME_LEN];
88 DOS_FULL_NAME tmp_dir, profile_dir;
89 int drive;
90 const char *cwd;
92 if (!getcwd( path, MAX_PATHNAME_LEN ))
94 perror( "Could not get current directory" );
95 return 0;
97 cwd = path;
98 if ((drive = DRIVE_FindDriveRoot( &cwd )) == -1)
100 MESSAGE("Warning: could not find wine config [Drive x] entry "
101 "for current working directory %s; "
102 "starting in windows directory.\n", cwd );
104 else
106 DRIVE_SetCurrentDrive( drive );
107 DRIVE_Chdir( drive, cwd );
110 if (!(DIR_GetPath( "windows", "c:\\windows", &DIR_Windows, longpath, TRUE )) ||
111 !(DIR_GetPath( "system", "c:\\windows\\system", &DIR_System, longpath, TRUE )) ||
112 !(DIR_GetPath( "temp", "c:\\windows", &tmp_dir, longpath, TRUE )))
114 PROFILE_UsageWineIni();
115 return 0;
117 if (-1 == access( tmp_dir.long_name, W_OK ))
119 if (errno==EACCES)
121 MESSAGE("Warning: the temporary directory '%s' (specified in wine configuration file) is not writeable.\n", tmp_dir.long_name);
122 PROFILE_UsageWineIni();
124 else
125 MESSAGE("Warning: access to temporary directory '%s' failed (%s).\n",
126 tmp_dir.long_name, strerror(errno));
129 if (drive == -1)
131 drive = DIR_Windows.drive;
132 DRIVE_SetCurrentDrive( drive );
133 DRIVE_Chdir( drive, DIR_Windows.short_name + 2 );
136 PROFILE_GetWineIniString("wine", "path", "c:\\windows;c:\\windows\\system",
137 path, sizeof(path) );
138 if (strchr(path, '/'))
140 MESSAGE("Fix your wine config to use DOS drive syntax in [wine] 'Path=' statement! (no '/' allowed)\n");
141 PROFILE_UsageWineIni();
142 ExitProcess(1);
145 /* Set the environment variables */
147 SetEnvironmentVariableA( "PATH", path );
148 SetEnvironmentVariableA( "TEMP", tmp_dir.short_name );
149 SetEnvironmentVariableA( "TMP", tmp_dir.short_name );
150 SetEnvironmentVariableA( "windir", DIR_Windows.short_name );
151 SetEnvironmentVariableA( "winsysdir", DIR_System.short_name );
153 /* set COMSPEC only if it doesn't exist already */
154 if (!GetEnvironmentVariableA( "COMSPEC", NULL, 0 ))
155 SetEnvironmentVariableA( "COMSPEC", "c:\\command.com" );
157 TRACE("WindowsDir = %s (%s)\n",
158 DIR_Windows.short_name, DIR_Windows.long_name );
159 TRACE("SystemDir = %s (%s)\n",
160 DIR_System.short_name, DIR_System.long_name );
161 TRACE("TempDir = %s (%s)\n",
162 tmp_dir.short_name, tmp_dir.long_name );
163 TRACE("Path = %s\n", path );
164 TRACE("Cwd = %c:\\%s\n",
165 'A' + drive, DRIVE_GetDosCwd( drive ) );
167 if (DIR_GetPath( "profile", "", &profile_dir, longpath, FALSE ))
169 TRACE("USERPROFILE= %s\n", longpath );
170 SetEnvironmentVariableA( "USERPROFILE", longpath );
173 TRACE("SYSTEMROOT = %s\n", DIR_Windows.short_name );
174 SetEnvironmentVariableA( "SYSTEMROOT", DIR_Windows.short_name );
176 return 1;
180 /***********************************************************************
181 * GetTempPathA (KERNEL32.@)
183 UINT WINAPI GetTempPathA( UINT count, LPSTR path )
185 UINT ret;
186 if (!(ret = GetEnvironmentVariableA( "TMP", path, count )))
187 if (!(ret = GetEnvironmentVariableA( "TEMP", path, count )))
188 if (!(ret = GetCurrentDirectoryA( count, path )))
189 return 0;
190 if (count && (ret < count - 1) && (path[ret-1] != '\\'))
192 path[ret++] = '\\';
193 path[ret] = '\0';
195 return ret;
199 /***********************************************************************
200 * GetTempPathW (KERNEL32.@)
202 UINT WINAPI GetTempPathW( UINT count, LPWSTR path )
204 static const WCHAR tmp[] = { 'T', 'M', 'P', 0 };
205 static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
206 UINT ret;
207 if (!(ret = GetEnvironmentVariableW( tmp, path, count )))
208 if (!(ret = GetEnvironmentVariableW( temp, path, count )))
209 if (!(ret = GetCurrentDirectoryW( count, path )))
210 return 0;
211 if (count && (ret < count - 1) && (path[ret-1] != '\\'))
213 path[ret++] = '\\';
214 path[ret] = '\0';
216 return ret;
220 /***********************************************************************
221 * DIR_GetWindowsUnixDir
223 UINT DIR_GetWindowsUnixDir( LPSTR path, UINT count )
225 if (path) lstrcpynA( path, DIR_Windows.long_name, count );
226 return strlen( DIR_Windows.long_name );
230 /***********************************************************************
231 * DIR_GetSystemUnixDir
233 UINT DIR_GetSystemUnixDir( LPSTR path, UINT count )
235 if (path) lstrcpynA( path, DIR_System.long_name, count );
236 return strlen( DIR_System.long_name );
240 /***********************************************************************
241 * GetTempDrive (KERNEL.92)
242 * A closer look at krnl386.exe shows what the SDK doesn't mention:
244 * returns:
245 * AL: driveletter
246 * AH: ':' - yes, some kernel code even does stosw with
247 * the returned AX.
248 * DX: 1 for success
250 UINT WINAPI GetTempDrive( BYTE ignored )
252 char *buffer;
253 BYTE ret;
254 UINT len = GetTempPathA( 0, NULL );
256 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, len + 1 )) )
257 ret = DRIVE_GetCurrentDrive() + 'A';
258 else
260 /* FIXME: apparently Windows does something with the ignored byte */
261 if (!GetTempPathA( len, buffer )) buffer[0] = 'C';
262 ret = toupper(buffer[0]);
263 HeapFree( GetProcessHeap(), 0, buffer );
265 return MAKELONG( ret | (':' << 8), 1 );
269 /***********************************************************************
270 * GetWindowsDirectory (KERNEL.134)
272 UINT16 WINAPI GetWindowsDirectory16( LPSTR path, UINT16 count )
274 return (UINT16)GetWindowsDirectoryA( path, count );
278 /***********************************************************************
279 * GetWindowsDirectoryA (KERNEL32.@)
281 * See comment for GetWindowsDirectoryW.
283 UINT WINAPI GetWindowsDirectoryA( LPSTR path, UINT count )
285 UINT len = strlen( DIR_Windows.short_name ) + 1;
286 if (path && count >= len)
288 strcpy( path, DIR_Windows.short_name );
289 len--;
291 return len;
295 /***********************************************************************
296 * GetWindowsDirectoryW (KERNEL32.@)
298 * Return value:
299 * If buffer is large enough to hold full path and terminating '\0' character
300 * function copies path to buffer and returns length of the path without '\0'.
301 * Otherwise function returns required size including '\0' character and
302 * does not touch the buffer.
304 UINT WINAPI GetWindowsDirectoryW( LPWSTR path, UINT count )
306 UINT len = MultiByteToWideChar( CP_ACP, 0, DIR_Windows.short_name, -1, NULL, 0 );
307 if (path && count >= len)
309 MultiByteToWideChar( CP_ACP, 0, DIR_Windows.short_name, -1, path, count );
310 len--;
312 return len;
316 /***********************************************************************
317 * GetSystemWindowsDirectoryA (KERNEL32.@) W2K, TS4.0SP4
319 UINT WINAPI GetSystemWindowsDirectoryA( LPSTR path, UINT count )
321 return GetWindowsDirectoryA( path, count );
325 /***********************************************************************
326 * GetSystemWindowsDirectoryW (KERNEL32.@) W2K, TS4.0SP4
328 UINT WINAPI GetSystemWindowsDirectoryW( LPWSTR path, UINT count )
330 return GetWindowsDirectoryW( path, count );
334 /***********************************************************************
335 * GetSystemDirectory (KERNEL.135)
337 UINT16 WINAPI GetSystemDirectory16( LPSTR path, UINT16 count )
339 return (UINT16)GetSystemDirectoryA( path, count );
343 /***********************************************************************
344 * GetSystemDirectoryA (KERNEL32.@)
346 * See comment for GetWindowsDirectoryW.
348 UINT WINAPI GetSystemDirectoryA( LPSTR path, UINT count )
350 UINT len = strlen( DIR_System.short_name ) + 1;
351 if (path && count >= len)
353 strcpy( path, DIR_System.short_name );
354 len--;
356 return len;
360 /***********************************************************************
361 * GetSystemDirectoryW (KERNEL32.@)
363 * See comment for GetWindowsDirectoryW.
365 UINT WINAPI GetSystemDirectoryW( LPWSTR path, UINT count )
367 UINT len = MultiByteToWideChar( CP_ACP, 0, DIR_System.short_name, -1, NULL, 0 );
368 if (path && count >= len)
370 MultiByteToWideChar( CP_ACP, 0, DIR_System.short_name, -1, path, count );
371 len--;
373 return len;
377 /***********************************************************************
378 * CreateDirectory (KERNEL.144)
380 BOOL16 WINAPI CreateDirectory16( LPCSTR path, LPVOID dummy )
382 TRACE_(file)("(%s,%p)\n", path, dummy );
383 return (BOOL16)CreateDirectoryA( path, NULL );
387 /***********************************************************************
388 * CreateDirectoryA (KERNEL32.@)
389 * RETURNS:
390 * TRUE : success
391 * FALSE : failure
392 * ERROR_DISK_FULL: on full disk
393 * ERROR_ALREADY_EXISTS: if directory name exists (even as file)
394 * ERROR_ACCESS_DENIED: on permission problems
395 * ERROR_FILENAME_EXCED_RANGE: too long filename(s)
397 BOOL WINAPI CreateDirectoryA( LPCSTR path,
398 LPSECURITY_ATTRIBUTES lpsecattribs )
400 DOS_FULL_NAME full_name;
402 TRACE_(file)("(%s,%p)\n", path, lpsecattribs );
403 if (DOSFS_GetDevice( path ))
405 TRACE_(file)("cannot use device '%s'!\n",path);
406 SetLastError( ERROR_ACCESS_DENIED );
407 return FALSE;
409 if (!DOSFS_GetFullName( path, FALSE, &full_name )) return 0;
410 if (mkdir( full_name.long_name, 0777 ) == -1) {
411 WARN_(file)("Error '%s' trying to create directory '%s'\n", strerror(errno), full_name.long_name);
412 /* the FILE_SetDosError() generated error codes don't match the
413 * CreateDirectory ones for some errnos */
414 switch (errno) {
415 case EEXIST: SetLastError(ERROR_ALREADY_EXISTS); break;
416 case ENOSPC: SetLastError(ERROR_DISK_FULL); break;
417 default: FILE_SetDosError();break;
419 return FALSE;
421 return TRUE;
425 /***********************************************************************
426 * CreateDirectoryW (KERNEL32.@)
428 BOOL WINAPI CreateDirectoryW( LPCWSTR path,
429 LPSECURITY_ATTRIBUTES lpsecattribs )
431 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
432 BOOL ret = CreateDirectoryA( xpath, lpsecattribs );
433 HeapFree( GetProcessHeap(), 0, xpath );
434 return ret;
438 /***********************************************************************
439 * CreateDirectoryExA (KERNEL32.@)
441 BOOL WINAPI CreateDirectoryExA( LPCSTR template, LPCSTR path,
442 LPSECURITY_ATTRIBUTES lpsecattribs)
444 return CreateDirectoryA(path,lpsecattribs);
448 /***********************************************************************
449 * CreateDirectoryExW (KERNEL32.@)
451 BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path,
452 LPSECURITY_ATTRIBUTES lpsecattribs)
454 return CreateDirectoryW(path,lpsecattribs);
458 /***********************************************************************
459 * RemoveDirectory (KERNEL.145)
461 BOOL16 WINAPI RemoveDirectory16( LPCSTR path )
463 return (BOOL16)RemoveDirectoryA( path );
467 /***********************************************************************
468 * RemoveDirectoryA (KERNEL32.@)
470 BOOL WINAPI RemoveDirectoryA( LPCSTR path )
472 DOS_FULL_NAME full_name;
474 TRACE_(file)("'%s'\n", path );
476 if (DOSFS_GetDevice( path ))
478 TRACE_(file)("cannot remove device '%s'!\n", path);
479 SetLastError( ERROR_FILE_NOT_FOUND );
480 return FALSE;
482 if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
483 if (rmdir( full_name.long_name ) == -1)
485 FILE_SetDosError();
486 return FALSE;
488 return TRUE;
492 /***********************************************************************
493 * RemoveDirectoryW (KERNEL32.@)
495 BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
497 LPSTR xpath = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
498 BOOL ret = RemoveDirectoryA( xpath );
499 HeapFree( GetProcessHeap(), 0, xpath );
500 return ret;
504 /***********************************************************************
505 * DIR_TryPath
507 * Helper function for DIR_SearchPath.
509 static BOOL DIR_TryPath( const DOS_FULL_NAME *dir, LPCSTR name,
510 DOS_FULL_NAME *full_name )
512 LPSTR p_l = full_name->long_name + strlen(dir->long_name) + 1;
513 LPSTR p_s = full_name->short_name + strlen(dir->short_name) + 1;
515 if ((p_s >= full_name->short_name + sizeof(full_name->short_name) - 14) ||
516 (p_l >= full_name->long_name + sizeof(full_name->long_name) - 1))
518 SetLastError( ERROR_PATH_NOT_FOUND );
519 return FALSE;
521 if (!DOSFS_FindUnixName( dir->long_name, name, p_l,
522 sizeof(full_name->long_name) - (p_l - full_name->long_name),
523 p_s, !(DRIVE_GetFlags(dir->drive) & DRIVE_CASE_SENSITIVE) ))
524 return FALSE;
525 strcpy( full_name->long_name, dir->long_name );
526 p_l[-1] = '/';
527 strcpy( full_name->short_name, dir->short_name );
528 p_s[-1] = '\\';
529 return TRUE;
532 static BOOL DIR_SearchSemicolonedPaths(LPCSTR name, DOS_FULL_NAME *full_name, LPSTR pathlist)
534 LPSTR next, buffer = NULL;
535 INT len = strlen(name), newlen, currlen = 0;
536 BOOL ret = FALSE;
538 next = pathlist;
539 while (!ret && next)
541 LPSTR cur = next;
542 while (*cur == ';') cur++;
543 if (!*cur) break;
544 next = strchr( cur, ';' );
545 if (next) *next++ = '\0';
546 newlen = strlen(cur) + len + 2;
547 if (newlen > currlen)
549 if (!(buffer = HeapReAlloc( GetProcessHeap(), 0, buffer, newlen)))
550 goto done;
551 currlen = newlen;
553 strcpy( buffer, cur );
554 strcat( buffer, "\\" );
555 strcat( buffer, name );
556 ret = DOSFS_GetFullName( buffer, TRUE, full_name );
558 done:
559 HeapFree( GetProcessHeap(), 0, buffer );
560 return ret;
564 /***********************************************************************
565 * DIR_TryEnvironmentPath
567 * Helper function for DIR_SearchPath.
568 * Search in the specified path, or in $PATH if NULL.
570 static BOOL DIR_TryEnvironmentPath( LPCSTR name, DOS_FULL_NAME *full_name, LPCSTR envpath )
572 LPSTR path;
573 BOOL ret = FALSE;
574 DWORD size;
576 size = envpath ? strlen(envpath)+1 : GetEnvironmentVariableA( "PATH", NULL, 0 );
577 if (!size) return FALSE;
578 if (!(path = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
579 if (envpath) strcpy( path, envpath );
580 else if (!GetEnvironmentVariableA( "PATH", path, size )) goto done;
582 ret = DIR_SearchSemicolonedPaths(name, full_name, path);
584 done:
585 HeapFree( GetProcessHeap(), 0, path );
586 return ret;
590 /***********************************************************************
591 * DIR_TryModulePath
593 * Helper function for DIR_SearchPath.
595 static BOOL DIR_TryModulePath( LPCSTR name, DOS_FULL_NAME *full_name, BOOL win32 )
597 /* FIXME: for now, GetModuleFileNameA can't return more */
598 /* than OFS_MAXPATHNAME. This may change with Win32. */
600 char buffer[OFS_MAXPATHNAME];
601 LPSTR p;
603 if (!win32)
605 if (!GetCurrentTask()) return FALSE;
606 if (!GetModuleFileName16( GetCurrentTask(), buffer, sizeof(buffer) ))
607 buffer[0]='\0';
608 } else {
609 if (!GetModuleFileNameA( 0, buffer, sizeof(buffer) ))
610 buffer[0]='\0';
612 if (!(p = strrchr( buffer, '\\' ))) return FALSE;
613 if (sizeof(buffer) - (++p - buffer) <= strlen(name)) return FALSE;
614 strcpy( p, name );
615 return DOSFS_GetFullName( buffer, TRUE, full_name );
619 /***********************************************************************
620 * DIR_TryAppPath
622 * Helper function for DIR_SearchPath.
624 static BOOL DIR_TryAppPath( LPCSTR name, DOS_FULL_NAME *full_name )
626 HKEY hkAppPaths, hkApp;
627 char lpAppName[MAX_PATHNAME_LEN], lpAppPaths[MAX_PATHNAME_LEN];
628 LPSTR lpFileName;
629 BOOL res = FALSE;
630 DWORD type, count;
632 if (RegOpenKeyA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths", &hkAppPaths) != ERROR_SUCCESS)
633 return FALSE;
635 if (GetModuleFileNameA(0, lpAppName, sizeof(lpAppName)) == 0)
637 WARN("huh, module not found ??\n");
638 goto end;
640 lpFileName = strrchr(lpAppName, '\\');
641 if (!lpFileName)
642 goto end;
643 else lpFileName++; /* skip '\\' */
644 if (RegOpenKeyA(hkAppPaths, lpFileName, &hkApp) != ERROR_SUCCESS)
645 goto end;
646 count = sizeof(lpAppPaths);
647 if (RegQueryValueExA(hkApp, "Path", 0, &type, (LPBYTE)lpAppPaths, &count) != ERROR_SUCCESS)
648 goto end;
649 TRACE("successfully opened App Paths for '%s'\n", lpFileName);
651 res = DIR_SearchSemicolonedPaths(name, full_name, lpAppPaths);
652 end:
653 if (hkApp)
654 RegCloseKey(hkApp);
655 if (hkAppPaths)
656 RegCloseKey(hkAppPaths);
657 return res;
660 /***********************************************************************
661 * DIR_SearchPath
663 * Implementation of SearchPathA. 'win32' specifies whether the search
664 * order is Win16 (module path last) or Win32 (module path first).
666 * FIXME: should return long path names.
668 DWORD DIR_SearchPath( LPCSTR path, LPCSTR name, LPCSTR ext,
669 DOS_FULL_NAME *full_name, BOOL win32 )
671 LPCSTR p;
672 LPSTR tmp = NULL;
673 BOOL ret = TRUE;
675 /* First check the supplied parameters */
677 p = strrchr( name, '.' );
678 if (p && !strchr( p, '/' ) && !strchr( p, '\\' ))
679 ext = NULL; /* Ignore the specified extension */
680 if (FILE_contains_path (name))
681 path = NULL; /* Ignore path if name already contains a path */
682 if (path && !*path) path = NULL; /* Ignore empty path */
684 /* Allocate a buffer for the file name and extension */
686 if (ext)
688 DWORD len = strlen(name) + strlen(ext);
689 if (!(tmp = HeapAlloc( GetProcessHeap(), 0, len + 1 )))
691 SetLastError( ERROR_OUTOFMEMORY );
692 return 0;
694 strcpy( tmp, name );
695 strcat( tmp, ext );
696 name = tmp;
699 /* If the name contains an explicit path, everything's easy */
701 if (FILE_contains_path(name))
703 ret = DOSFS_GetFullName( name, TRUE, full_name );
704 goto done;
707 /* Search in the specified path */
709 if (path)
711 ret = DIR_TryEnvironmentPath( name, full_name, path );
712 goto done;
715 /* Try the path of the current executable (for Win32 search order) */
717 if (win32 && DIR_TryModulePath( name, full_name, win32 )) goto done;
719 /* Try the current directory */
721 if (DOSFS_GetFullName( name, TRUE, full_name )) goto done;
723 /* Try the Windows system directory */
725 if (DIR_TryPath( &DIR_System, name, full_name ))
726 goto done;
728 /* Try the Windows directory */
730 if (DIR_TryPath( &DIR_Windows, name, full_name ))
731 goto done;
733 /* Try the path of the current executable (for Win16 search order) */
735 if (!win32 && DIR_TryModulePath( name, full_name, win32 )) goto done;
737 /* Try the "App Paths" entry if existing (undocumented ??) */
738 if (DIR_TryAppPath(name, full_name))
739 goto done;
741 /* Try all directories in path */
743 ret = DIR_TryEnvironmentPath( name, full_name, NULL );
745 done:
746 if (tmp) HeapFree( GetProcessHeap(), 0, tmp );
747 return ret;
751 /***********************************************************************
752 * SearchPathA [KERNEL32.@]
754 * Searches for a specified file in the search path.
756 * PARAMS
757 * path [I] Path to search
758 * name [I] Filename to search for.
759 * ext [I] File extension to append to file name. The first
760 * character must be a period. This parameter is
761 * specified only if the filename given does not
762 * contain an extension.
763 * buflen [I] size of buffer, in characters
764 * buffer [O] buffer for found filename
765 * lastpart [O] address of pointer to last used character in
766 * buffer (the final '\')
768 * RETURNS
769 * Success: length of string copied into buffer, not including
770 * terminating null character. If the filename found is
771 * longer than the length of the buffer, the length of the
772 * filename is returned.
773 * Failure: Zero
775 * NOTES
776 * If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
777 * (tested on NT 4.0)
779 DWORD WINAPI SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext, DWORD buflen,
780 LPSTR buffer, LPSTR *lastpart )
782 LPSTR p, res;
783 DOS_FULL_NAME full_name;
785 if (!DIR_SearchPath( path, name, ext, &full_name, TRUE ))
787 SetLastError(ERROR_FILE_NOT_FOUND);
788 return 0;
790 lstrcpynA( buffer, full_name.short_name, buflen );
791 res = full_name.long_name +
792 strlen(DRIVE_GetRoot( full_name.short_name[0] - 'A' ));
793 while (*res == '/') res++;
794 if (buflen)
796 if (buflen > 3) lstrcpynA( buffer + 3, res, buflen - 3 );
797 for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
798 if (lastpart) *lastpart = strrchr( buffer, '\\' ) + 1;
800 TRACE("Returning %d\n", strlen(res) + 3 );
801 return strlen(res) + 3;
805 /***********************************************************************
806 * SearchPathW (KERNEL32.@)
808 DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext,
809 DWORD buflen, LPWSTR buffer, LPWSTR *lastpart )
811 LPWSTR p;
812 LPSTR res;
813 DOS_FULL_NAME full_name;
815 LPSTR pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, path );
816 LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, name );
817 LPSTR extA = HEAP_strdupWtoA( GetProcessHeap(), 0, ext );
818 DWORD ret = DIR_SearchPath( pathA, nameA, extA, &full_name, TRUE );
819 HeapFree( GetProcessHeap(), 0, extA );
820 HeapFree( GetProcessHeap(), 0, nameA );
821 HeapFree( GetProcessHeap(), 0, pathA );
822 if (!ret) return 0;
824 if (buflen > 0 && !MultiByteToWideChar( CP_ACP, 0, full_name.short_name, -1, buffer, buflen ))
825 buffer[buflen-1] = 0;
826 res = full_name.long_name +
827 strlen(DRIVE_GetRoot( full_name.short_name[0] - 'A' ));
828 while (*res == '/') res++;
829 if (buflen)
831 if (buflen > 3)
833 if (!MultiByteToWideChar( CP_ACP, 0, res, -1, buffer+3, buflen-3 ))
834 buffer[buflen-1] = 0;
836 for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
837 if (lastpart)
839 for (p = *lastpart = buffer; *p; p++)
840 if (*p == '\\') *lastpart = p + 1;
843 return strlen(res) + 3;
847 /***********************************************************************
848 * search_alternate_path
851 * FIXME: should return long path names.?
853 static BOOL search_alternate_path(LPCSTR dll_path, LPCSTR name, LPCSTR ext,
854 DOS_FULL_NAME *full_name)
856 LPCSTR p;
857 LPSTR tmp = NULL;
858 BOOL ret = TRUE;
860 /* First check the supplied parameters */
862 p = strrchr( name, '.' );
863 if (p && !strchr( p, '/' ) && !strchr( p, '\\' ))
864 ext = NULL; /* Ignore the specified extension */
866 /* Allocate a buffer for the file name and extension */
868 if (ext)
870 DWORD len = strlen(name) + strlen(ext);
871 if (!(tmp = HeapAlloc( GetProcessHeap(), 0, len + 1 )))
873 SetLastError( ERROR_OUTOFMEMORY );
874 return 0;
876 strcpy( tmp, name );
877 strcat( tmp, ext );
878 name = tmp;
881 if (DIR_TryEnvironmentPath (name, full_name, dll_path))
883 else if (DOSFS_GetFullName (name, TRUE, full_name)) /* current dir */
885 else if (DIR_TryPath (&DIR_System, name, full_name)) /* System dir */
887 else if (DIR_TryPath (&DIR_Windows, name, full_name)) /* Windows dir */
889 else
890 ret = DIR_TryEnvironmentPath( name, full_name, NULL );
892 if (tmp) HeapFree( GetProcessHeap(), 0, tmp );
893 return ret;
897 /***********************************************************************
898 * DIR_SearchAlternatePath
900 * Searches for a specified file in the search path.
902 * PARAMS
903 * dll_path [I] Path to search
904 * name [I] Filename to search for.
905 * ext [I] File extension to append to file name. The first
906 * character must be a period. This parameter is
907 * specified only if the filename given does not
908 * contain an extension.
909 * buflen [I] size of buffer, in characters
910 * buffer [O] buffer for found filename
911 * lastpart [O] address of pointer to last used character in
912 * buffer (the final '\') (May be NULL)
914 * RETURNS
915 * Success: length of string copied into buffer, not including
916 * terminating null character. If the filename found is
917 * longer than the length of the buffer, the length of the
918 * filename is returned.
919 * Failure: Zero
921 * NOTES
922 * If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
924 DWORD DIR_SearchAlternatePath( LPCSTR dll_path, LPCSTR name, LPCSTR ext,
925 DWORD buflen, LPSTR buffer, LPSTR *lastpart )
927 LPSTR p, res;
928 DOS_FULL_NAME full_name;
930 if (!search_alternate_path( dll_path, name, ext, &full_name))
932 SetLastError(ERROR_FILE_NOT_FOUND);
933 return 0;
935 lstrcpynA( buffer, full_name.short_name, buflen );
936 res = full_name.long_name +
937 strlen(DRIVE_GetRoot( full_name.short_name[0] - 'A' ));
938 while (*res == '/') res++;
939 if (buflen)
941 if (buflen > 3) lstrcpynA( buffer + 3, res, buflen - 3 );
942 for (p = buffer; *p; p++) if (*p == '/') *p = '\\';
943 if (lastpart) *lastpart = strrchr( buffer, '\\' ) + 1;
945 TRACE("Returning %d\n", strlen(res) + 3 );
946 return strlen(res) + 3;