bd4893b6d37435054a3acf03b9effec2dc33cd61
[reactos.git] / dll / win32 / dbghelp / path.c
1 /*
2 * File path.c - managing path in debugging environments
3 *
4 * Copyright (C) 2004,2008, Eric Pouech
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "dbghelp_private.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
24
25 static inline BOOL is_sep(char ch) {return ch == '/' || ch == '\\';}
26 static inline BOOL is_sepW(WCHAR ch) {return ch == '/' || ch == '\\';}
27
28 static inline const char* file_name(const char* str)
29 {
30 const char* p;
31
32 for (p = str + strlen(str) - 1; p >= str && !is_sep(*p); p--);
33 return p + 1;
34 }
35
36 static inline const WCHAR* file_nameW(const WCHAR* str)
37 {
38 const WCHAR* p;
39
40 for (p = str + strlenW(str) - 1; p >= str && !is_sepW(*p); p--);
41 return p + 1;
42 }
43
44 /******************************************************************
45 * FindDebugInfoFile (DBGHELP.@)
46 *
47 */
48 HANDLE WINAPI FindDebugInfoFile(PCSTR FileName, PCSTR SymbolPath, PSTR DebugFilePath)
49 {
50 HANDLE h;
51
52 h = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ, NULL,
53 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
54 if (h == INVALID_HANDLE_VALUE)
55 {
56 if (!SearchPathA(SymbolPath, file_name(FileName), NULL, MAX_PATH, DebugFilePath, NULL))
57 return NULL;
58 h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
59 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
60 }
61 return (h == INVALID_HANDLE_VALUE) ? NULL : h;
62 }
63
64 /******************************************************************
65 * FindDebugInfoFileEx (DBGHELP.@)
66 *
67 */
68 HANDLE WINAPI FindDebugInfoFileEx(PCSTR FileName, PCSTR SymbolPath,
69 PSTR DebugFilePath,
70 PFIND_DEBUG_FILE_CALLBACK Callback,
71 PVOID CallerData)
72 {
73 FIXME("(%s %s %s %p %p): stub\n", debugstr_a(FileName), debugstr_a(SymbolPath),
74 debugstr_a(DebugFilePath), Callback, CallerData);
75 return NULL;
76 }
77
78 /******************************************************************
79 * FindExecutableImageExW (DBGHELP.@)
80 *
81 */
82 HANDLE WINAPI FindExecutableImageExW(PCWSTR FileName, PCWSTR SymbolPath, PWSTR ImageFilePath,
83 PFIND_EXE_FILE_CALLBACKW Callback, PVOID user)
84 {
85 HANDLE h;
86
87 if (Callback) FIXME("Unsupported callback yet\n");
88 if (!SearchPathW(SymbolPath, FileName, NULL, MAX_PATH, ImageFilePath, NULL))
89 return NULL;
90 h = CreateFileW(ImageFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
91 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
92 return (h == INVALID_HANDLE_VALUE) ? NULL : h;
93 }
94
95 /******************************************************************
96 * FindExecutableImageEx (DBGHELP.@)
97 *
98 */
99 HANDLE WINAPI FindExecutableImageEx(PCSTR FileName, PCSTR SymbolPath, PSTR ImageFilePath,
100 PFIND_EXE_FILE_CALLBACK Callback, PVOID user)
101 {
102 HANDLE h;
103
104 if (Callback) FIXME("Unsupported callback yet\n");
105 if (!SearchPathA(SymbolPath, FileName, NULL, MAX_PATH, ImageFilePath, NULL))
106 return NULL;
107 h = CreateFileA(ImageFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
108 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
109 return (h == INVALID_HANDLE_VALUE) ? NULL : h;
110 }
111
112 /******************************************************************
113 * FindExecutableImage (DBGHELP.@)
114 *
115 */
116 HANDLE WINAPI FindExecutableImage(PCSTR FileName, PCSTR SymbolPath, PSTR ImageFilePath)
117 {
118 return FindExecutableImageEx(FileName, SymbolPath, ImageFilePath, NULL, NULL);
119 }
120
121 /***********************************************************************
122 * MakeSureDirectoryPathExists (DBGHELP.@)
123 */
124 BOOL WINAPI MakeSureDirectoryPathExists(PCSTR DirPath)
125 {
126 char path[MAX_PATH];
127 const char *p = DirPath;
128 int n;
129
130 if (p[0] && p[1] == ':') p += 2;
131 while (*p == '\\') p++; /* skip drive root */
132 while ((p = strchr(p, '\\')) != NULL)
133 {
134 n = p - DirPath + 1;
135 memcpy(path, DirPath, n);
136 path[n] = '\0';
137 if( !CreateDirectoryA(path, NULL) &&
138 (GetLastError() != ERROR_ALREADY_EXISTS))
139 return FALSE;
140 p++;
141 }
142 if (GetLastError() == ERROR_ALREADY_EXISTS)
143 SetLastError(ERROR_SUCCESS);
144
145 return TRUE;
146 }
147
148 /******************************************************************
149 * SymMatchFileNameW (DBGHELP.@)
150 *
151 */
152 BOOL WINAPI SymMatchFileNameW(PCWSTR file, PCWSTR match,
153 PWSTR* filestop, PWSTR* matchstop)
154 {
155 PCWSTR fptr;
156 PCWSTR mptr;
157
158 TRACE("(%s %s %p %p)\n",
159 debugstr_w(file), debugstr_w(match), filestop, matchstop);
160
161 fptr = file + strlenW(file) - 1;
162 mptr = match + strlenW(match) - 1;
163
164 while (fptr >= file && mptr >= match)
165 {
166 if (toupperW(*fptr) != toupperW(*mptr) && !(is_sepW(*fptr) && is_sepW(*mptr)))
167 break;
168 fptr--; mptr--;
169 }
170 if (filestop) *filestop = (PWSTR)fptr;
171 if (matchstop) *matchstop = (PWSTR)mptr;
172
173 return mptr == match - 1;
174 }
175
176 /******************************************************************
177 * SymMatchFileName (DBGHELP.@)
178 *
179 */
180 BOOL WINAPI SymMatchFileName(PCSTR file, PCSTR match,
181 PSTR* filestop, PSTR* matchstop)
182 {
183 PCSTR fptr;
184 PCSTR mptr;
185
186 TRACE("(%s %s %p %p)\n", debugstr_a(file), debugstr_a(match), filestop, matchstop);
187
188 fptr = file + strlen(file) - 1;
189 mptr = match + strlen(match) - 1;
190
191 while (fptr >= file && mptr >= match)
192 {
193 if (toupper(*fptr) != toupper(*mptr) && !(is_sep(*fptr) && is_sep(*mptr)))
194 break;
195 fptr--; mptr--;
196 }
197 if (filestop) *filestop = (PSTR)fptr;
198 if (matchstop) *matchstop = (PSTR)mptr;
199
200 return mptr == match - 1;
201 }
202
203 static BOOL do_searchW(PCWSTR file, PWSTR buffer, BOOL recurse,
204 PENUMDIRTREE_CALLBACKW cb, PVOID user)
205 {
206 HANDLE h;
207 WIN32_FIND_DATAW fd;
208 unsigned pos;
209 BOOL found = FALSE;
210 static const WCHAR S_AllW[] = {'*','.','*','\0'};
211 static const WCHAR S_DotW[] = {'.','\0'};
212 static const WCHAR S_DotDotW[] = {'.','.','\0'};
213
214 pos = strlenW(buffer);
215 if (buffer[pos - 1] != '\\') buffer[pos++] = '\\';
216 strcpyW(buffer + pos, S_AllW);
217 if ((h = FindFirstFileW(buffer, &fd)) == INVALID_HANDLE_VALUE)
218 return FALSE;
219 /* doc doesn't specify how the tree is enumerated...
220 * doing a depth first based on, but may be wrong
221 */
222 do
223 {
224 if (!strcmpW(fd.cFileName, S_DotW) || !strcmpW(fd.cFileName, S_DotDotW)) continue;
225
226 strcpyW(buffer + pos, fd.cFileName);
227 if (recurse && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
228 found = do_searchW(file, buffer, TRUE, cb, user);
229 else if (SymMatchFileNameW(buffer, file, NULL, NULL))
230 {
231 if (!cb || cb(buffer, user)) found = TRUE;
232 }
233 } while (!found && FindNextFileW(h, &fd));
234 if (!found) buffer[--pos] = '\0';
235 FindClose(h);
236
237 return found;
238 }
239
240 /***********************************************************************
241 * SearchTreeForFileW (DBGHELP.@)
242 */
243 BOOL WINAPI SearchTreeForFileW(PCWSTR root, PCWSTR file, PWSTR buffer)
244 {
245 TRACE("(%s, %s, %p)\n",
246 debugstr_w(root), debugstr_w(file), buffer);
247 strcpyW(buffer, root);
248 return do_searchW(file, buffer, TRUE, NULL, NULL);
249 }
250
251 /***********************************************************************
252 * SearchTreeForFile (DBGHELP.@)
253 */
254 BOOL WINAPI SearchTreeForFile(PCSTR root, PCSTR file, PSTR buffer)
255 {
256 WCHAR rootW[MAX_PATH];
257 WCHAR fileW[MAX_PATH];
258 WCHAR bufferW[MAX_PATH];
259 BOOL ret;
260
261 MultiByteToWideChar(CP_ACP, 0, root, -1, rootW, MAX_PATH);
262 MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, MAX_PATH);
263 ret = SearchTreeForFileW(rootW, fileW, bufferW);
264 if (ret)
265 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
266 return ret;
267 }
268
269 /******************************************************************
270 * EnumDirTreeW (DBGHELP.@)
271 *
272 *
273 */
274 BOOL WINAPI EnumDirTreeW(HANDLE hProcess, PCWSTR root, PCWSTR file,
275 PWSTR buffer, PENUMDIRTREE_CALLBACKW cb, PVOID user)
276 {
277 TRACE("(%p %s %s %p %p %p)\n",
278 hProcess, debugstr_w(root), debugstr_w(file), buffer, cb, user);
279
280 strcpyW(buffer, root);
281 return do_searchW(file, buffer, TRUE, cb, user);
282 }
283
284 /******************************************************************
285 * EnumDirTree (DBGHELP.@)
286 *
287 *
288 */
289 struct enum_dir_treeWA
290 {
291 PENUMDIRTREE_CALLBACK cb;
292 void* user;
293 char name[MAX_PATH];
294 };
295
296 static BOOL CALLBACK enum_dir_treeWA(PCWSTR name, PVOID user)
297 {
298 struct enum_dir_treeWA* edt = user;
299
300 WideCharToMultiByte(CP_ACP, 0, name, -1, edt->name, MAX_PATH, NULL, NULL);
301 return edt->cb(edt->name, edt->user);
302 }
303
304 BOOL WINAPI EnumDirTree(HANDLE hProcess, PCSTR root, PCSTR file,
305 PSTR buffer, PENUMDIRTREE_CALLBACK cb, PVOID user)
306 {
307 WCHAR rootW[MAX_PATH];
308 WCHAR fileW[MAX_PATH];
309 WCHAR bufferW[MAX_PATH];
310 struct enum_dir_treeWA edt;
311 BOOL ret;
312
313 edt.cb = cb;
314 edt.user = user;
315 MultiByteToWideChar(CP_ACP, 0, root, -1, rootW, MAX_PATH);
316 MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, MAX_PATH);
317 if ((ret = EnumDirTreeW(hProcess, rootW, fileW, bufferW, enum_dir_treeWA, &edt)))
318 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
319 return ret;
320 }
321
322 struct sffip
323 {
324 PFINDFILEINPATHCALLBACKW cb;
325 void* user;
326 };
327
328 /* checks that buffer (as found by matching the name) matches the info
329 * (information is based on file type)
330 * returns TRUE when file is found, FALSE to continue searching
331 * (NB this is the opposite convention of SymFindFileInPathProc)
332 */
333 static BOOL CALLBACK sffip_cb(PCWSTR buffer, PVOID user)
334 {
335 struct sffip* s = user;
336
337 if (!s->cb) return TRUE;
338 /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
339 * convention to stop/continue enumeration. sigh.
340 */
341 return !(s->cb)(buffer, s->user);
342 }
343
344 /******************************************************************
345 * SymFindFileInPathW (DBGHELP.@)
346 *
347 */
348 BOOL WINAPI SymFindFileInPathW(HANDLE hProcess, PCWSTR searchPath, PCWSTR full_path,
349 PVOID id, DWORD two, DWORD three, DWORD flags,
350 PWSTR buffer, PFINDFILEINPATHCALLBACKW cb,
351 PVOID user)
352 {
353 struct sffip s;
354 struct process* pcs = process_find_by_handle(hProcess);
355 WCHAR tmp[MAX_PATH];
356 WCHAR* ptr;
357 const WCHAR* filename;
358
359 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",
360 hProcess, debugstr_w(searchPath), debugstr_w(full_path),
361 id, two, three, flags, buffer, cb, user);
362
363 if (!pcs) return FALSE;
364 if (!searchPath) searchPath = pcs->search_path;
365
366 s.cb = cb;
367 s.user = user;
368
369 filename = file_nameW(full_path);
370
371 /* first check full path to file */
372 if (sffip_cb(full_path, &s))
373 {
374 strcpyW(buffer, full_path);
375 return TRUE;
376 }
377
378 while (searchPath)
379 {
380 ptr = strchrW(searchPath, ';');
381 if (ptr)
382 {
383 memcpy(tmp, searchPath, (ptr - searchPath) * sizeof(WCHAR));
384 tmp[ptr - searchPath] = 0;
385 searchPath = ptr + 1;
386 }
387 else
388 {
389 strcpyW(tmp, searchPath);
390 searchPath = NULL;
391 }
392 if (do_searchW(filename, tmp, FALSE, sffip_cb, &s))
393 {
394 strcpyW(buffer, tmp);
395 return TRUE;
396 }
397 }
398 return FALSE;
399 }
400
401 /******************************************************************
402 * SymFindFileInPath (DBGHELP.@)
403 *
404 */
405 BOOL WINAPI SymFindFileInPath(HANDLE hProcess, PCSTR searchPath, PCSTR full_path,
406 PVOID id, DWORD two, DWORD three, DWORD flags,
407 PSTR buffer, PFINDFILEINPATHCALLBACK cb,
408 PVOID user)
409 {
410 WCHAR searchPathW[MAX_PATH];
411 WCHAR full_pathW[MAX_PATH];
412 WCHAR bufferW[MAX_PATH];
413 struct enum_dir_treeWA edt;
414 BOOL ret;
415
416 /* a PFINDFILEINPATHCALLBACK and a PENUMDIRTREE_CALLBACK have actually the
417 * same signature & semantics, hence we can reuse the EnumDirTree W->A
418 * conversion helper
419 */
420 edt.cb = cb;
421 edt.user = user;
422 if (searchPath)
423 MultiByteToWideChar(CP_ACP, 0, searchPath, -1, searchPathW, MAX_PATH);
424 MultiByteToWideChar(CP_ACP, 0, full_path, -1, full_pathW, MAX_PATH);
425 if ((ret = SymFindFileInPathW(hProcess, searchPath ? searchPathW : NULL, full_pathW,
426 id, two, three, flags,
427 bufferW, enum_dir_treeWA, &edt)))
428 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
429 return ret;
430 }
431
432 struct module_find
433 {
434 enum module_type kind;
435 /* pe: dw1 DWORD:timestamp
436 * dw2 size of image (from PE header)
437 * pdb: guid PDB guid (if DS PDB file)
438 * or dw1 PDB timestamp (if JG PDB file)
439 * dw2 PDB age
440 * elf: dw1 DWORD:CRC 32 of ELF image (Wine only)
441 */
442 const GUID* guid;
443 DWORD dw1;
444 DWORD dw2;
445 WCHAR filename[MAX_PATH];
446 unsigned matched;
447 };
448
449 /* checks that buffer (as found by matching the name) matches the info
450 * (information is based on file type)
451 * returns TRUE when file is found, FALSE to continue searching
452 * (NB this is the opposite convention of SymFindFileInPathProc)
453 */
454 static BOOL CALLBACK module_find_cb(PCWSTR buffer, PVOID user)
455 {
456 struct module_find* mf = user;
457 DWORD size, checksum, timestamp;
458 unsigned matched = 0;
459
460 /* the matching weights:
461 * +1 if a file with same name is found and is a decent file of expected type
462 * +1 if first parameter and second parameter match
463 */
464
465 /* FIXME: should check that id/two match the file pointed
466 * by buffer
467 */
468 switch (mf->kind)
469 {
470 case DMT_PE:
471 {
472 HANDLE hFile, hMap;
473 void* mapping;
474
475 timestamp = ~mf->dw1;
476 size = ~mf->dw2;
477 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL,
478 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
479 if (hFile == INVALID_HANDLE_VALUE) return FALSE;
480 if ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
481 {
482 if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
483 {
484 IMAGE_NT_HEADERS* nth = RtlImageNtHeader(mapping);
485 if (!nth)
486 {
487 UnmapViewOfFile(mapping);
488 CloseHandle(hMap);
489 CloseHandle(hFile);
490 return FALSE;
491 }
492 matched++;
493 timestamp = nth->FileHeader.TimeDateStamp;
494 size = nth->OptionalHeader.SizeOfImage;
495 UnmapViewOfFile(mapping);
496 }
497 CloseHandle(hMap);
498 }
499 CloseHandle(hFile);
500 if (timestamp != mf->dw1)
501 WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer));
502 if (size != mf->dw2)
503 WARN("Found %s, but wrong size\n", debugstr_w(buffer));
504 if (timestamp == mf->dw1 && size == mf->dw2) matched++;
505 }
506 break;
507 case DMT_ELF:
508 if (elf_fetch_file_info(buffer, 0, &size, &checksum))
509 {
510 matched++;
511 if (checksum == mf->dw1) matched++;
512 else
513 WARN("Found %s, but wrong checksums: %08x %08x\n",
514 debugstr_w(buffer), checksum, mf->dw1);
515 }
516 else
517 {
518 WARN("Couldn't read %s\n", debugstr_w(buffer));
519 return FALSE;
520 }
521 break;
522 case DMT_MACHO:
523 if (macho_fetch_file_info(NULL, buffer, 0, 0, &size, &checksum))
524 {
525 matched++;
526 if (checksum == mf->dw1) matched++;
527 else
528 WARN("Found %s, but wrong checksums: %08x %08x\n",
529 debugstr_w(buffer), checksum, mf->dw1);
530 }
531 else
532 {
533 WARN("Couldn't read %s\n", debugstr_w(buffer));
534 return FALSE;
535 }
536 break;
537 case DMT_PDB:
538 {
539 struct pdb_lookup pdb_lookup;
540 char fn[MAX_PATH];
541
542 WideCharToMultiByte(CP_ACP, 0, buffer, -1, fn, MAX_PATH, NULL, NULL);
543 pdb_lookup.filename = fn;
544
545 if (mf->guid)
546 {
547 pdb_lookup.kind = PDB_DS;
548 pdb_lookup.timestamp = 0;
549 pdb_lookup.guid = *mf->guid;
550 }
551 else
552 {
553 pdb_lookup.kind = PDB_JG;
554 pdb_lookup.timestamp = mf->dw1;
555 /* pdb_loopkup.guid = */
556 }
557 pdb_lookup.age = mf->dw2;
558
559 if (!pdb_fetch_file_info(&pdb_lookup, &matched)) return FALSE;
560 }
561 break;
562 case DMT_DBG:
563 {
564 HANDLE hFile, hMap;
565 void* mapping;
566
567 timestamp = ~mf->dw1;
568 hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL,
569 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
570 if (hFile == INVALID_HANDLE_VALUE) return FALSE;
571 if ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
572 {
573 if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
574 {
575 const IMAGE_SEPARATE_DEBUG_HEADER* hdr;
576 hdr = mapping;
577
578 if (hdr->Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE)
579 {
580 matched++;
581 timestamp = hdr->TimeDateStamp;
582 }
583 UnmapViewOfFile(mapping);
584 }
585 CloseHandle(hMap);
586 }
587 CloseHandle(hFile);
588 if (timestamp == mf->dw1) matched++;
589 else WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer));
590 }
591 break;
592 default:
593 FIXME("What the heck??\n");
594 return FALSE;
595 }
596 if (matched > mf->matched)
597 {
598 strcpyW(mf->filename, buffer);
599 mf->matched = matched;
600 }
601 /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
602 * convention to stop/continue enumeration. sigh.
603 */
604 return mf->matched == 2;
605 }
606
607 BOOL path_find_symbol_file(const struct process* pcs, PCSTR full_path,
608 const GUID* guid, DWORD dw1, DWORD dw2, PSTR buffer,
609 BOOL* is_unmatched)
610 {
611 struct module_find mf;
612 WCHAR full_pathW[MAX_PATH];
613 WCHAR tmp[MAX_PATH];
614 WCHAR* ptr;
615 const WCHAR* filename;
616 WCHAR* searchPath = pcs->search_path;
617
618 TRACE("(pcs = %p, full_path = %s, guid = %s, dw1 = 0x%08x, dw2 = 0x%08x, buffer = %p)\n",
619 pcs, debugstr_a(full_path), debugstr_guid(guid), dw1, dw2, buffer);
620
621 mf.guid = guid;
622 mf.dw1 = dw1;
623 mf.dw2 = dw2;
624 mf.matched = 0;
625
626 MultiByteToWideChar(CP_ACP, 0, full_path, -1, full_pathW, MAX_PATH);
627 filename = file_nameW(full_pathW);
628 mf.kind = module_get_type_by_name(filename);
629 *is_unmatched = FALSE;
630
631 /* first check full path to file */
632 if (module_find_cb(full_pathW, &mf))
633 {
634 WideCharToMultiByte(CP_ACP, 0, full_pathW, -1, buffer, MAX_PATH, NULL, NULL);
635 return TRUE;
636 }
637
638 while (searchPath)
639 {
640 ptr = strchrW(searchPath, ';');
641 if (ptr)
642 {
643 memcpy(tmp, searchPath, (ptr - searchPath) * sizeof(WCHAR));
644 tmp[ptr - searchPath] = '\0';
645 searchPath = ptr + 1;
646 }
647 else
648 {
649 strcpyW(tmp, searchPath);
650 searchPath = NULL;
651 }
652 if (do_searchW(filename, tmp, FALSE, module_find_cb, &mf))
653 {
654 /* return first fully matched file */
655 WideCharToMultiByte(CP_ACP, 0, tmp, -1, buffer, MAX_PATH, NULL, NULL);
656 return TRUE;
657 }
658 }
659 /* if no fully matching file is found, return the best matching file if any */
660 if ((dbghelp_options & SYMOPT_LOAD_ANYTHING) && mf.matched)
661 {
662 WideCharToMultiByte(CP_ACP, 0, mf.filename, -1, buffer, MAX_PATH, NULL, NULL);
663 *is_unmatched = TRUE;
664 return TRUE;
665 }
666 return FALSE;
667 }