2 * File path.c - managing path in debugging environments
4 * Copyright (C) 2004,2008, Eric Pouech
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "dbghelp_private.h"
26 #include "image_private.h"
29 #include "wine/debug.h"
30 #include "wine/heap.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
34 static inline BOOL
is_sepA(char ch
) {return ch
== '/' || ch
== '\\';}
35 static inline BOOL
is_sep(WCHAR ch
) {return ch
== '/' || ch
== '\\';}
37 const char* file_nameA(const char* str
)
41 for (p
= str
+ strlen(str
) - 1; p
>= str
&& !is_sepA(*p
); p
--);
45 const WCHAR
* file_name(const WCHAR
* str
)
49 for (p
= str
+ lstrlenW(str
) - 1; p
>= str
&& !is_sep(*p
); p
--);
53 static inline void file_pathW(const WCHAR
*src
, WCHAR
*dst
)
57 for (len
= lstrlenW(src
) - 1; (len
> 0) && (!is_sep(src
[len
])); len
--);
58 memcpy( dst
, src
, len
* sizeof(WCHAR
) );
62 /******************************************************************
63 * FindDebugInfoFile (DBGHELP.@)
66 HANDLE WINAPI
FindDebugInfoFile(PCSTR FileName
, PCSTR SymbolPath
, PSTR DebugFilePath
)
70 h
= CreateFileA(FileName
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
71 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
72 if (h
== INVALID_HANDLE_VALUE
)
74 if (!SearchPathA(SymbolPath
, file_nameA(FileName
), NULL
, MAX_PATH
, DebugFilePath
, NULL
))
76 h
= CreateFileA(DebugFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
77 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
79 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
82 /******************************************************************
83 * FindDebugInfoFileEx (DBGHELP.@)
86 HANDLE WINAPI
FindDebugInfoFileEx(PCSTR FileName
, PCSTR SymbolPath
,
88 PFIND_DEBUG_FILE_CALLBACK Callback
,
91 FIXME("(%s %s %s %p %p): stub\n", debugstr_a(FileName
), debugstr_a(SymbolPath
),
92 debugstr_a(DebugFilePath
), Callback
, CallerData
);
96 /******************************************************************
97 * FindExecutableImageExW (DBGHELP.@)
100 HANDLE WINAPI
FindExecutableImageExW(PCWSTR FileName
, PCWSTR SymbolPath
, PWSTR ImageFilePath
,
101 PFIND_EXE_FILE_CALLBACKW Callback
, PVOID user
)
105 if (Callback
) FIXME("Unsupported callback yet\n");
106 if (!SearchPathW(SymbolPath
, FileName
, NULL
, MAX_PATH
, ImageFilePath
, NULL
))
108 h
= CreateFileW(ImageFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
109 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
110 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
113 /******************************************************************
114 * FindExecutableImageEx (DBGHELP.@)
117 HANDLE WINAPI
FindExecutableImageEx(PCSTR FileName
, PCSTR SymbolPath
, PSTR ImageFilePath
,
118 PFIND_EXE_FILE_CALLBACK Callback
, PVOID user
)
122 if (Callback
) FIXME("Unsupported callback yet\n");
123 if (!SearchPathA(SymbolPath
, FileName
, NULL
, MAX_PATH
, ImageFilePath
, NULL
))
125 h
= CreateFileA(ImageFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
126 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
127 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
130 /******************************************************************
131 * FindExecutableImage (DBGHELP.@)
134 HANDLE WINAPI
FindExecutableImage(PCSTR FileName
, PCSTR SymbolPath
, PSTR ImageFilePath
)
136 return FindExecutableImageEx(FileName
, SymbolPath
, ImageFilePath
, NULL
, NULL
);
139 /***********************************************************************
140 * MakeSureDirectoryPathExists (DBGHELP.@)
142 BOOL WINAPI
MakeSureDirectoryPathExists(PCSTR DirPath
)
145 const char *p
= DirPath
;
148 if (p
[0] && p
[1] == ':') p
+= 2;
149 while (*p
== '\\') p
++; /* skip drive root */
150 while ((p
= strchr(p
, '\\')) != NULL
)
153 memcpy(path
, DirPath
, n
);
155 if( !CreateDirectoryA(path
, NULL
) &&
156 (GetLastError() != ERROR_ALREADY_EXISTS
))
160 if (GetLastError() == ERROR_ALREADY_EXISTS
)
161 SetLastError(ERROR_SUCCESS
);
166 /******************************************************************
167 * SymMatchFileNameW (DBGHELP.@)
170 BOOL WINAPI
SymMatchFileNameW(PCWSTR file
, PCWSTR match
,
171 PWSTR
* filestop
, PWSTR
* matchstop
)
176 TRACE("(%s %s %p %p)\n",
177 debugstr_w(file
), debugstr_w(match
), filestop
, matchstop
);
179 fptr
= file
+ lstrlenW(file
) - 1;
180 mptr
= match
+ lstrlenW(match
) - 1;
182 while (fptr
>= file
&& mptr
>= match
)
184 if (towupper(*fptr
) != towupper(*mptr
) && !(is_sep(*fptr
) && is_sep(*mptr
)))
188 if (filestop
) *filestop
= (PWSTR
)fptr
;
189 if (matchstop
) *matchstop
= (PWSTR
)mptr
;
191 return mptr
== match
- 1;
194 /******************************************************************
195 * SymMatchFileName (DBGHELP.@)
198 BOOL WINAPI
SymMatchFileName(PCSTR file
, PCSTR match
,
199 PSTR
* filestop
, PSTR
* matchstop
)
204 TRACE("(%s %s %p %p)\n", debugstr_a(file
), debugstr_a(match
), filestop
, matchstop
);
206 fptr
= file
+ strlen(file
) - 1;
207 mptr
= match
+ strlen(match
) - 1;
209 while (fptr
>= file
&& mptr
>= match
)
211 if (toupper(*fptr
) != toupper(*mptr
) && !(is_sepA(*fptr
) && is_sepA(*mptr
)))
215 if (filestop
) *filestop
= (PSTR
)fptr
;
216 if (matchstop
) *matchstop
= (PSTR
)mptr
;
218 return mptr
== match
- 1;
221 static BOOL
do_searchW(PCWSTR file
, PWSTR buffer
, BOOL recurse
,
222 PENUMDIRTREE_CALLBACKW cb
, PVOID user
)
228 static const WCHAR S_AllW
[] = {'*','.','*','\0'};
229 static const WCHAR S_DotW
[] = {'.','\0'};
230 static const WCHAR S_DotDotW
[] = {'.','.','\0'};
232 pos
= lstrlenW(buffer
);
233 if (pos
== 0) return FALSE
;
234 if (buffer
[pos
- 1] != '\\') buffer
[pos
++] = '\\';
235 lstrcpyW(buffer
+ pos
, S_AllW
);
236 if ((h
= FindFirstFileW(buffer
, &fd
)) == INVALID_HANDLE_VALUE
)
238 /* doc doesn't specify how the tree is enumerated...
239 * doing a depth first based on, but may be wrong
243 if (!wcscmp(fd
.cFileName
, S_DotW
) || !wcscmp(fd
.cFileName
, S_DotDotW
)) continue;
245 lstrcpyW(buffer
+ pos
, fd
.cFileName
);
246 if (recurse
&& (fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
247 found
= do_searchW(file
, buffer
, TRUE
, cb
, user
);
248 else if (SymMatchFileNameW(buffer
, file
, NULL
, NULL
))
250 if (!cb
|| cb(buffer
, user
)) found
= TRUE
;
252 } while (!found
&& FindNextFileW(h
, &fd
));
253 if (!found
) buffer
[--pos
] = '\0';
259 /***********************************************************************
260 * SearchTreeForFileW (DBGHELP.@)
262 BOOL WINAPI
SearchTreeForFileW(PCWSTR root
, PCWSTR file
, PWSTR buffer
)
264 TRACE("(%s, %s, %p)\n",
265 debugstr_w(root
), debugstr_w(file
), buffer
);
266 lstrcpyW(buffer
, root
);
267 return do_searchW(file
, buffer
, TRUE
, NULL
, NULL
);
270 /***********************************************************************
271 * SearchTreeForFile (DBGHELP.@)
273 BOOL WINAPI
SearchTreeForFile(PCSTR root
, PCSTR file
, PSTR buffer
)
275 WCHAR rootW
[MAX_PATH
];
276 WCHAR fileW
[MAX_PATH
];
277 WCHAR bufferW
[MAX_PATH
];
280 MultiByteToWideChar(CP_ACP
, 0, root
, -1, rootW
, MAX_PATH
);
281 MultiByteToWideChar(CP_ACP
, 0, file
, -1, fileW
, MAX_PATH
);
282 ret
= SearchTreeForFileW(rootW
, fileW
, bufferW
);
284 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
288 /******************************************************************
289 * EnumDirTreeW (DBGHELP.@)
293 BOOL WINAPI
EnumDirTreeW(HANDLE hProcess
, PCWSTR root
, PCWSTR file
,
294 PWSTR buffer
, PENUMDIRTREE_CALLBACKW cb
, PVOID user
)
296 TRACE("(%p %s %s %p %p %p)\n",
297 hProcess
, debugstr_w(root
), debugstr_w(file
), buffer
, cb
, user
);
299 lstrcpyW(buffer
, root
);
300 return do_searchW(file
, buffer
, TRUE
, cb
, user
);
303 /******************************************************************
304 * EnumDirTree (DBGHELP.@)
308 struct enum_dir_treeWA
310 PENUMDIRTREE_CALLBACK cb
;
315 static BOOL CALLBACK
enum_dir_treeWA(PCWSTR name
, PVOID user
)
317 struct enum_dir_treeWA
* edt
= user
;
319 WideCharToMultiByte(CP_ACP
, 0, name
, -1, edt
->name
, MAX_PATH
, NULL
, NULL
);
320 return edt
->cb(edt
->name
, edt
->user
);
323 BOOL WINAPI
EnumDirTree(HANDLE hProcess
, PCSTR root
, PCSTR file
,
324 PSTR buffer
, PENUMDIRTREE_CALLBACK cb
, PVOID user
)
326 WCHAR rootW
[MAX_PATH
];
327 WCHAR fileW
[MAX_PATH
];
328 WCHAR bufferW
[MAX_PATH
];
329 struct enum_dir_treeWA edt
;
334 MultiByteToWideChar(CP_ACP
, 0, root
, -1, rootW
, MAX_PATH
);
335 MultiByteToWideChar(CP_ACP
, 0, file
, -1, fileW
, MAX_PATH
);
336 if ((ret
= EnumDirTreeW(hProcess
, rootW
, fileW
, bufferW
, enum_dir_treeWA
, &edt
)))
337 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
343 PFINDFILEINPATHCALLBACKW cb
;
347 /* checks that buffer (as found by matching the name) matches the info
348 * (information is based on file type)
349 * returns TRUE when file is found, FALSE to continue searching
350 * (NB this is the opposite convention of SymFindFileInPathProc)
352 static BOOL CALLBACK
sffip_cb(PCWSTR buffer
, PVOID user
)
354 struct sffip
* s
= user
;
356 if (!s
->cb
) return TRUE
;
357 /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
358 * convention to stop/continue enumeration. sigh.
360 return !(s
->cb
)(buffer
, s
->user
);
363 /******************************************************************
364 * SymFindFileInPathW (DBGHELP.@)
367 BOOL WINAPI
SymFindFileInPathW(HANDLE hProcess
, PCWSTR searchPath
, PCWSTR full_path
,
368 PVOID id
, DWORD two
, DWORD three
, DWORD flags
,
369 PWSTR buffer
, PFINDFILEINPATHCALLBACKW cb
,
373 struct process
* pcs
= process_find_by_handle(hProcess
);
376 const WCHAR
* filename
;
378 TRACE("(hProcess = %p, searchPath = %s, full_path = %s, id = %p, two = 0x%08x, three = 0x%08x, flags = 0x%08x, buffer = %p, cb = %p, user = %p)\n",
379 hProcess
, debugstr_w(searchPath
), debugstr_w(full_path
),
380 id
, two
, three
, flags
, buffer
, cb
, user
);
382 if (!pcs
) return FALSE
;
383 if (!searchPath
) searchPath
= pcs
->search_path
;
388 filename
= file_name(full_path
);
390 /* first check full path to file */
391 if (sffip_cb(full_path
, &s
))
393 lstrcpyW(buffer
, full_path
);
399 ptr
= wcschr(searchPath
, ';');
402 memcpy(tmp
, searchPath
, (ptr
- searchPath
) * sizeof(WCHAR
));
403 tmp
[ptr
- searchPath
] = 0;
404 searchPath
= ptr
+ 1;
408 lstrcpyW(tmp
, searchPath
);
411 if (do_searchW(filename
, tmp
, FALSE
, sffip_cb
, &s
))
413 lstrcpyW(buffer
, tmp
);
420 /******************************************************************
421 * SymFindFileInPath (DBGHELP.@)
424 BOOL WINAPI
SymFindFileInPath(HANDLE hProcess
, PCSTR searchPath
, PCSTR full_path
,
425 PVOID id
, DWORD two
, DWORD three
, DWORD flags
,
426 PSTR buffer
, PFINDFILEINPATHCALLBACK cb
,
429 WCHAR searchPathW
[MAX_PATH
];
430 WCHAR full_pathW
[MAX_PATH
];
431 WCHAR bufferW
[MAX_PATH
];
432 struct enum_dir_treeWA edt
;
435 /* a PFINDFILEINPATHCALLBACK and a PENUMDIRTREE_CALLBACK have actually the
436 * same signature & semantics, hence we can reuse the EnumDirTree W->A
442 MultiByteToWideChar(CP_ACP
, 0, searchPath
, -1, searchPathW
, MAX_PATH
);
443 MultiByteToWideChar(CP_ACP
, 0, full_path
, -1, full_pathW
, MAX_PATH
);
444 if ((ret
= SymFindFileInPathW(hProcess
, searchPath
? searchPathW
: NULL
, full_pathW
,
445 id
, two
, three
, flags
,
446 bufferW
, enum_dir_treeWA
, &edt
)))
447 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
453 enum module_type kind
;
454 /* pe: dw1 DWORD:timestamp
455 * dw2 size of image (from PE header)
456 * pdb: guid PDB guid (if DS PDB file)
457 * or dw1 PDB timestamp (if JG PDB file)
459 * elf: dw1 DWORD:CRC 32 of ELF image (Wine only)
464 WCHAR filename
[MAX_PATH
];
468 /* checks that buffer (as found by matching the name) matches the info
469 * (information is based on file type)
470 * returns TRUE when file is found, FALSE to continue searching
471 * (NB this is the opposite convention of SymFindFileInPathProc)
473 static BOOL CALLBACK
module_find_cb(PCWSTR buffer
, PVOID user
)
475 struct module_find
* mf
= user
;
476 DWORD size
, timestamp
;
477 unsigned matched
= 0;
479 /* the matching weights:
480 * +1 if a file with same name is found and is a decent file of expected type
481 * +1 if first parameter and second parameter match
484 /* FIXME: should check that id/two match the file pointed
494 timestamp
= ~mf
->dw1
;
496 hFile
= CreateFileW(buffer
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
497 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
498 if (hFile
== INVALID_HANDLE_VALUE
) return FALSE
;
499 if ((hMap
= CreateFileMappingW(hFile
, NULL
, PAGE_READONLY
, 0, 0, NULL
)) != NULL
)
501 if ((mapping
= MapViewOfFile(hMap
, FILE_MAP_READ
, 0, 0, 0)) != NULL
)
503 IMAGE_NT_HEADERS
* nth
= RtlImageNtHeader(mapping
);
506 UnmapViewOfFile(mapping
);
512 timestamp
= nth
->FileHeader
.TimeDateStamp
;
513 size
= nth
->OptionalHeader
.SizeOfImage
;
514 UnmapViewOfFile(mapping
);
519 if (timestamp
!= mf
->dw1
)
520 WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer
));
522 WARN("Found %s, but wrong size\n", debugstr_w(buffer
));
523 if (timestamp
== mf
->dw1
&& size
== mf
->dw2
) matched
++;
528 struct pdb_lookup pdb_lookup
;
531 WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, fn
, MAX_PATH
, NULL
, NULL
);
532 pdb_lookup
.filename
= fn
;
536 pdb_lookup
.kind
= PDB_DS
;
537 pdb_lookup
.timestamp
= 0;
538 pdb_lookup
.guid
= *mf
->guid
;
542 pdb_lookup
.kind
= PDB_JG
;
543 pdb_lookup
.timestamp
= mf
->dw1
;
544 /* pdb_loopkup.guid = */
546 pdb_lookup
.age
= mf
->dw2
;
548 if (!pdb_fetch_file_info(&pdb_lookup
, &matched
)) return FALSE
;
556 timestamp
= ~mf
->dw1
;
557 hFile
= CreateFileW(buffer
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
558 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
559 if (hFile
== INVALID_HANDLE_VALUE
) return FALSE
;
560 if ((hMap
= CreateFileMappingW(hFile
, NULL
, PAGE_READONLY
, 0, 0, NULL
)) != NULL
)
562 if ((mapping
= MapViewOfFile(hMap
, FILE_MAP_READ
, 0, 0, 0)) != NULL
)
564 const IMAGE_SEPARATE_DEBUG_HEADER
* hdr
;
567 if (hdr
->Signature
== IMAGE_SEPARATE_DEBUG_SIGNATURE
)
570 timestamp
= hdr
->TimeDateStamp
;
572 UnmapViewOfFile(mapping
);
577 if (timestamp
== mf
->dw1
) matched
++;
578 else WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer
));
582 FIXME("What the heck??\n");
585 if (matched
> mf
->matched
)
587 lstrcpyW(mf
->filename
, buffer
);
588 mf
->matched
= matched
;
590 /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
591 * convention to stop/continue enumeration. sigh.
593 return mf
->matched
== 2;
596 BOOL
path_find_symbol_file(const struct process
* pcs
, const struct module
* module
,
597 PCSTR full_path
, enum module_type type
, const GUID
* guid
, DWORD dw1
, DWORD dw2
,
598 WCHAR
*buffer
, BOOL
* is_unmatched
)
600 struct module_find mf
;
601 WCHAR full_pathW
[MAX_PATH
];
603 const WCHAR
* filename
;
604 WCHAR
* searchPath
= pcs
->search_path
;
606 TRACE("(pcs = %p, full_path = %s, guid = %s, dw1 = 0x%08x, dw2 = 0x%08x, buffer = %p)\n",
607 pcs
, debugstr_a(full_path
), debugstr_guid(guid
), dw1
, dw2
, buffer
);
614 MultiByteToWideChar(CP_ACP
, 0, full_path
, -1, full_pathW
, MAX_PATH
);
615 filename
= file_name(full_pathW
);
617 *is_unmatched
= FALSE
;
619 /* first check full path to file */
620 if (module_find_cb(full_pathW
, &mf
))
622 lstrcpyW( buffer
, full_pathW
);
626 /* FIXME: Use Environment-Variables (see MS docs)
627 _NT_SYMBOL_PATH and _NT_ALT_SYMBOL_PATH
628 FIXME: Implement "Standard Path Elements" (Path) ... (see MS docs)
629 do a search for (every?) path-element like this ...
633 (dll may be exe, or sys depending on the file extension) */
635 /* 2. check module-path */
636 file_pathW(module
->module
.LoadedImageName
, buffer
);
637 if (do_searchW(filename
, buffer
, FALSE
, module_find_cb
, &mf
)) return TRUE
;
638 if (module
->real_path
)
640 file_pathW(module
->real_path
, buffer
);
641 if (do_searchW(filename
, buffer
, FALSE
, module_find_cb
, &mf
)) return TRUE
;
646 ptr
= wcschr(searchPath
, ';');
649 memcpy(buffer
, searchPath
, (ptr
- searchPath
) * sizeof(WCHAR
));
650 buffer
[ptr
- searchPath
] = '\0';
651 searchPath
= ptr
+ 1;
655 lstrcpyW(buffer
, searchPath
);
658 /* return first fully matched file */
659 if (do_searchW(filename
, buffer
, FALSE
, module_find_cb
, &mf
)) return TRUE
;
661 /* if no fully matching file is found, return the best matching file if any */
662 if ((dbghelp_options
& SYMOPT_LOAD_ANYTHING
) && mf
.matched
)
664 lstrcpyW( buffer
, mf
.filename
);
665 *is_unmatched
= TRUE
;
671 WCHAR
*get_dos_file_name(const WCHAR
*filename
)
676 if (*filename
== '/')
679 len
= WideCharToMultiByte(CP_UNIXCP
, 0, filename
, -1, NULL
, 0, NULL
, NULL
);
680 unix_path
= heap_alloc(len
* sizeof(WCHAR
));
681 WideCharToMultiByte(CP_UNIXCP
, 0, filename
, -1, unix_path
, len
, NULL
, NULL
);
682 dos_path
= wine_get_dos_file_name(unix_path
);
683 heap_free(unix_path
);
687 len
= lstrlenW(filename
);
688 dos_path
= heap_alloc((len
+ 1) * sizeof(WCHAR
));
689 memcpy(dos_path
, filename
, (len
+ 1) * sizeof(WCHAR
));
694 BOOL
search_dll_path(const struct process
*process
, const WCHAR
*name
, BOOL (*match
)(void*, HANDLE
, const WCHAR
*), void *param
)
702 name
= file_name(name
);
704 if ((env
= process_getenv(process
, L
"WINEBUILDDIR")))
707 const WCHAR dllsW
[] = { '\\','d','l','l','s','\\' };
708 const WCHAR programsW
[] = { '\\','p','r','o','g','r','a','m','s','\\' };
709 const WCHAR dot_dllW
[] = {'.','d','l','l',0};
710 const WCHAR dot_exeW
[] = {'.','e','x','e',0};
711 const WCHAR dot_soW
[] = {'.','s','o',0};
715 if (!(buf
= heap_alloc((len
+ 8 + 3 * lstrlenW(name
)) * sizeof(WCHAR
)))) return FALSE
;
719 memcpy(end
, dllsW
, sizeof(dllsW
));
720 lstrcpyW(end
+ ARRAY_SIZE(dllsW
), name
);
721 if ((p
= wcsrchr(end
, '.')) && !lstrcmpW(p
, dot_soW
)) *p
= 0;
722 if ((p
= wcsrchr(end
, '.')) && !lstrcmpW(p
, dot_dllW
)) *p
= 0;
723 p
= end
+ lstrlenW(end
);
726 file
= CreateFileW(buf
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
727 if (file
!= INVALID_HANDLE_VALUE
)
729 ret
= match(param
, file
, buf
);
734 memcpy(end
, programsW
, sizeof(programsW
));
735 end
+= ARRAY_SIZE(programsW
);
737 if ((p
= wcsrchr(end
, '.')) && !lstrcmpW(p
, dot_soW
)) *p
= 0;
738 if ((p
= wcsrchr(end
, '.')) && !lstrcmpW(p
, dot_exeW
)) *p
= 0;
739 p
= end
+ lstrlenW(end
);
742 file
= CreateFileW(buf
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
743 if (file
!= INVALID_HANDLE_VALUE
)
745 ret
= match(param
, file
, buf
);
756 swprintf(env_name
, ARRAY_SIZE(env_name
), L
"WINEDLLDIR%u", i
);
757 if (!(env
= process_getenv(process
, env_name
))) return FALSE
;
759 if (!(buf
= heap_alloc((len
+ lstrlenW(name
) + 2) * sizeof(WCHAR
)))) return FALSE
;
761 len
= GetEnvironmentVariableW(env_name
, buf
, len
);
763 lstrcpyW(buf
+ len
, name
);
764 file
= CreateFileW(buf
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
765 if (file
!= INVALID_HANDLE_VALUE
)
767 ret
= match(param
, file
, buf
);
777 TRACE("found %s\n", debugstr_w(buf
));
782 BOOL
search_unix_path(const WCHAR
*name
, const WCHAR
*path
, BOOL (*match
)(void*, HANDLE
, const WCHAR
*), void *param
)
784 const WCHAR
*iter
, *next
;
790 if (!path
) return FALSE
;
791 name
= file_name(name
);
793 size
= WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, NULL
, 0, NULL
, NULL
)
794 + WideCharToMultiByte(CP_UNIXCP
, 0, path
, -1, NULL
, 0, NULL
, NULL
);
795 if (!(buf
= heap_alloc(size
))) return FALSE
;
797 for (iter
= path
;; iter
= next
+ 1)
799 if (!(next
= wcschr(iter
, ':'))) next
= iter
+ lstrlenW(iter
);
802 len
= WideCharToMultiByte(CP_UNIXCP
, 0, iter
, next
- iter
, buf
, size
, NULL
, NULL
);
803 if (buf
[len
- 1] != '/') buf
[len
++] = '/';
804 WideCharToMultiByte(CP_UNIXCP
, 0, name
, -1, buf
+ len
, size
- len
, NULL
, NULL
);
805 if ((dos_path
= wine_get_dos_file_name(buf
)))
807 HANDLE file
= CreateFileW(dos_path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
808 if (file
!= INVALID_HANDLE_VALUE
)
810 ret
= match(param
, file
, dos_path
);
812 if (ret
) TRACE("found %s\n", debugstr_w(dos_path
));
818 if (*next
!= ':') break;