2 * File path.c - managing path in debugging environments
4 * Copyright (C) 2004, 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
26 #include "dbghelp_private.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
33 static inline BOOL
is_sep(char ch
) {return ch
== '/' || ch
== '\\';}
34 static inline BOOL
is_sepW(WCHAR ch
) {return ch
== '/' || ch
== '\\';}
36 static inline const char* file_name(const char* str
)
40 for (p
= str
+ strlen(str
) - 1; p
>= str
&& !is_sep(*p
); p
--);
44 static inline const WCHAR
* file_nameW(const WCHAR
* str
)
48 for (p
= str
+ strlenW(str
) - 1; p
>= str
&& !is_sepW(*p
); p
--);
52 /******************************************************************
53 * FindDebugInfoFile (DBGHELP.@)
56 HANDLE WINAPI
FindDebugInfoFile(PCSTR FileName
, PCSTR SymbolPath
, PSTR DebugFilePath
)
60 h
= CreateFileA(DebugFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
61 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
62 if (h
== INVALID_HANDLE_VALUE
)
64 if (!SearchPathA(SymbolPath
, file_name(FileName
), NULL
, MAX_PATH
, DebugFilePath
, NULL
))
66 h
= CreateFileA(DebugFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
67 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
69 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
72 /******************************************************************
73 * FindDebugInfoFileEx (DBGHELP.@)
76 HANDLE WINAPI
FindDebugInfoFileEx(PCSTR FileName
, PCSTR SymbolPath
,
78 PFIND_DEBUG_FILE_CALLBACK Callback
,
81 FIXME("(%s %s %p %p %p): stub\n",
82 FileName
, SymbolPath
, DebugFilePath
, Callback
, CallerData
);
86 /******************************************************************
87 * FindExecutableImageExW (DBGHELP.@)
90 HANDLE WINAPI
FindExecutableImageExW(PCWSTR FileName
, PCWSTR SymbolPath
, PWSTR ImageFilePath
,
91 PFIND_EXE_FILE_CALLBACKW Callback
, PVOID user
)
95 if (Callback
) FIXME("Unsupported callback yet\n");
96 if (!SearchPathW(SymbolPath
, FileName
, NULL
, MAX_PATH
, ImageFilePath
, NULL
))
98 h
= CreateFileW(ImageFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
99 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
100 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
103 /******************************************************************
104 * FindExecutableImageEx (DBGHELP.@)
107 HANDLE WINAPI
FindExecutableImageEx(PCSTR FileName
, PCSTR SymbolPath
, PSTR ImageFilePath
,
108 PFIND_EXE_FILE_CALLBACK Callback
, PVOID user
)
112 if (Callback
) FIXME("Unsupported callback yet\n");
113 if (!SearchPathA(SymbolPath
, FileName
, NULL
, MAX_PATH
, ImageFilePath
, NULL
))
115 h
= CreateFileA(ImageFilePath
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
116 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
117 return (h
== INVALID_HANDLE_VALUE
) ? NULL
: h
;
120 /******************************************************************
121 * FindExecutableImage (DBGHELP.@)
124 HANDLE WINAPI
FindExecutableImage(PCSTR FileName
, PCSTR SymbolPath
, PSTR ImageFilePath
)
126 return FindExecutableImageEx(FileName
, SymbolPath
, ImageFilePath
, NULL
, NULL
);
129 /***********************************************************************
130 * MakeSureDirectoryPathExists (DBGHELP.@)
132 BOOL WINAPI
MakeSureDirectoryPathExists(PCSTR DirPath
)
135 const char *p
= DirPath
;
138 if (p
[0] && p
[1] == ':') p
+= 2;
139 while (*p
== '\\') p
++; /* skip drive root */
140 while ((p
= strchr(p
, '\\')) != NULL
)
143 memcpy(path
, DirPath
, n
);
145 if( !CreateDirectoryA(path
, NULL
) &&
146 (GetLastError() != ERROR_ALREADY_EXISTS
))
150 if (GetLastError() == ERROR_ALREADY_EXISTS
)
151 SetLastError(ERROR_SUCCESS
);
156 /******************************************************************
157 * SymMatchFileNameW (DBGHELP.@)
160 BOOL WINAPI
SymMatchFileNameW(PCWSTR file
, PCWSTR match
,
161 PWSTR
* filestop
, PWSTR
* matchstop
)
166 TRACE("(%s %s %p %p)\n",
167 debugstr_w(file
), debugstr_w(match
), filestop
, matchstop
);
169 fptr
= file
+ strlenW(file
) - 1;
170 mptr
= match
+ strlenW(match
) - 1;
172 while (fptr
>= file
&& mptr
>= match
)
174 if (toupperW(*fptr
) != toupperW(*mptr
) && !(is_sepW(*fptr
) && is_sepW(*mptr
)))
178 if (filestop
) *filestop
= (PWSTR
)fptr
;
179 if (matchstop
) *matchstop
= (PWSTR
)mptr
;
181 return mptr
== match
- 1;
184 /******************************************************************
185 * SymMatchFileName (DBGHELP.@)
188 BOOL WINAPI
SymMatchFileName(PCSTR file
, PCSTR match
,
189 PSTR
* filestop
, PSTR
* matchstop
)
194 TRACE("(%s %s %p %p)\n", file
, match
, filestop
, matchstop
);
196 fptr
= file
+ strlen(file
) - 1;
197 mptr
= match
+ strlen(match
) - 1;
199 while (fptr
>= file
&& mptr
>= match
)
201 if (toupper(*fptr
) != toupper(*mptr
) && !(is_sep(*fptr
) && is_sep(*mptr
)))
205 if (filestop
) *filestop
= (PSTR
)fptr
;
206 if (matchstop
) *matchstop
= (PSTR
)mptr
;
208 return mptr
== match
- 1;
211 static BOOL
do_searchW(PCWSTR file
, PWSTR buffer
, BOOL recurse
,
212 PENUMDIRTREE_CALLBACKW cb
, PVOID user
)
218 static const WCHAR S_AllW
[] = {'*','.','*','\0'};
219 static const WCHAR S_DotW
[] = {'.','\0'};
220 static const WCHAR S_DotDotW
[] = {'.','\0'};
222 pos
= strlenW(buffer
);
223 if (buffer
[pos
- 1] != '\\') buffer
[pos
++] = '\\';
224 strcpyW(buffer
+ pos
, S_AllW
);
225 if ((h
= FindFirstFileW(buffer
, &fd
)) == INVALID_HANDLE_VALUE
)
227 /* doc doesn't specify how the tree is enumerated...
228 * doing a depth first based on, but may be wrong
232 if (!strcmpW(fd
.cFileName
, S_DotW
) || !strcmpW(fd
.cFileName
, S_DotDotW
)) continue;
234 strcpyW(buffer
+ pos
, fd
.cFileName
);
235 if (recurse
&& (fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
))
236 found
= do_searchW(file
, buffer
, TRUE
, cb
, user
);
237 else if (SymMatchFileNameW(buffer
, (WCHAR
*)file
, NULL
, NULL
))
239 if (!cb
|| cb(buffer
, user
)) found
= TRUE
;
241 } while (!found
&& FindNextFileW(h
, &fd
));
242 if (!found
) buffer
[--pos
] = '\0';
248 /***********************************************************************
249 * SearchTreeForFileW (DBGHELP.@)
251 BOOL WINAPI
SearchTreeForFileW(PCWSTR root
, PCWSTR file
, PWSTR buffer
)
253 TRACE("(%s, %s, %p)\n",
254 debugstr_w(root
), debugstr_w(file
), buffer
);
255 strcpyW(buffer
, root
);
256 return do_searchW(file
, buffer
, TRUE
, NULL
, NULL
);
259 /***********************************************************************
260 * SearchTreeForFile (DBGHELP.@)
262 BOOL WINAPI
SearchTreeForFile(PCSTR root
, PCSTR file
, PSTR buffer
)
264 WCHAR rootW
[MAX_PATH
];
265 WCHAR fileW
[MAX_PATH
];
266 WCHAR bufferW
[MAX_PATH
];
269 MultiByteToWideChar(CP_ACP
, 0, root
, -1, rootW
, MAX_PATH
);
270 MultiByteToWideChar(CP_ACP
, 0, file
, -1, fileW
, MAX_PATH
);
271 ret
= SearchTreeForFileW(rootW
, fileW
, bufferW
);
273 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
277 /******************************************************************
278 * EnumDirTreeW (DBGHELP.@)
282 BOOL WINAPI
EnumDirTreeW(HANDLE hProcess
, PCWSTR root
, PCWSTR file
,
283 PWSTR buffer
, PENUMDIRTREE_CALLBACKW cb
, PVOID user
)
285 TRACE("(%p %s %s %p %p %p)\n",
286 hProcess
, debugstr_w(root
), debugstr_w(file
), buffer
, cb
, user
);
288 strcpyW(buffer
, root
);
289 return do_searchW(file
, buffer
, TRUE
, cb
, user
);
292 /******************************************************************
293 * EnumDirTree (DBGHELP.@)
297 struct enum_dir_treeWA
299 PENUMDIRTREE_CALLBACK cb
;
304 static BOOL CALLBACK
enum_dir_treeWA(PCWSTR name
, PVOID user
)
306 struct enum_dir_treeWA
* edt
= user
;
308 WideCharToMultiByte(CP_ACP
, 0, name
, -1, edt
->name
, MAX_PATH
, NULL
, NULL
);
309 return edt
->cb(edt
->name
, edt
->user
);
312 BOOL WINAPI
EnumDirTree(HANDLE hProcess
, PCSTR root
, PCSTR file
,
313 PSTR buffer
, PENUMDIRTREE_CALLBACK cb
, PVOID user
)
315 WCHAR rootW
[MAX_PATH
];
316 WCHAR fileW
[MAX_PATH
];
317 WCHAR bufferW
[MAX_PATH
];
318 struct enum_dir_treeWA edt
;
323 MultiByteToWideChar(CP_ACP
, 0, root
, -1, rootW
, MAX_PATH
);
324 MultiByteToWideChar(CP_ACP
, 0, file
, -1, fileW
, MAX_PATH
);
325 if ((ret
= EnumDirTreeW(hProcess
, rootW
, fileW
, bufferW
, enum_dir_treeWA
, &edt
)))
326 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
332 enum module_type kind
;
333 /* pe: id -> DWORD:timestamp
334 * two -> size of image (from PE header)
335 * pdb: id -> PDB signature
336 * I think either DWORD:timestamp or GUID:guid depending on PDB version
338 * elf: id -> DWORD:CRC 32 of ELF image (Wine only)
344 PFINDFILEINPATHCALLBACKW cb
;
348 /* checks that buffer (as found by matching the name) matches the info
349 * (information is based on file type)
350 * returns TRUE when file is found, FALSE to continue searching
351 * (NB this is the opposite convention of SymFindFileInPathProc)
353 static BOOL CALLBACK
sffip_cb(PCWSTR buffer
, PVOID user
)
355 struct sffip
* s
= (struct sffip
*)user
;
356 DWORD size
, checksum
;
358 /* FIXME: should check that id/two/three match the file pointed
369 timestamp
= ~(DWORD_PTR
)s
->id
;
371 hFile
= CreateFileW(buffer
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
372 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
373 if (hFile
== INVALID_HANDLE_VALUE
) return FALSE
;
374 if ((hMap
= CreateFileMappingW(hFile
, NULL
, PAGE_READONLY
, 0, 0, NULL
)) != NULL
)
376 if ((mapping
= MapViewOfFile(hMap
, FILE_MAP_READ
, 0, 0, 0)) != NULL
)
378 IMAGE_NT_HEADERS
* nth
= RtlImageNtHeader(mapping
);
379 timestamp
= nth
->FileHeader
.TimeDateStamp
;
380 size
= nth
->OptionalHeader
.SizeOfImage
;
381 UnmapViewOfFile(mapping
);
386 if (timestamp
!= (DWORD_PTR
)s
->id
|| size
!= s
->two
)
388 WARN("Found %s, but wrong size or timestamp\n", debugstr_w(buffer
));
394 if (elf_fetch_file_info(buffer
, 0, &size
, &checksum
))
396 if (checksum
!= (DWORD_PTR
)s
->id
)
398 WARN("Found %s, but wrong checksums: %08x %08lx\n",
399 debugstr_w(buffer
), checksum
, (DWORD_PTR
)s
->id
);
405 WARN("Couldn't read %s\n", debugstr_w(buffer
));
411 struct pdb_lookup pdb_lookup
;
414 WideCharToMultiByte(CP_ACP
, 0, buffer
, -1, fn
, MAX_PATH
, NULL
, NULL
);
415 pdb_lookup
.filename
= fn
;
417 if (!pdb_fetch_file_info(&pdb_lookup
)) return FALSE
;
418 switch (pdb_lookup
.kind
)
421 if (s
->flags
& SSRVOPT_GUIDPTR
)
423 WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer
));
426 if (pdb_lookup
.u
.jg
.timestamp
!= (DWORD_PTR
)s
->id
)
428 WARN("Found %s, but wrong signature: %08x %08lx\n",
429 debugstr_w(buffer
), pdb_lookup
.u
.jg
.timestamp
, (DWORD_PTR
)s
->id
);
434 if (!(s
->flags
& SSRVOPT_GUIDPTR
))
436 WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer
));
439 if (memcmp(&pdb_lookup
.u
.ds
.guid
, (GUID
*)s
->id
, sizeof(GUID
)))
441 WARN("Found %s, but wrong GUID: %s %s\n",
442 debugstr_w(buffer
), debugstr_guid(&pdb_lookup
.u
.ds
.guid
),
443 debugstr_guid((GUID
*)s
->id
));
448 if (pdb_lookup
.age
!= s
->two
)
450 WARN("Found %s, but wrong age: %08x %08x\n",
451 debugstr_w(buffer
), pdb_lookup
.age
, s
->two
);
457 FIXME("What the heck??\n");
460 /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
461 * convention to stop/continue enumeration. sigh.
463 return !(s
->cb
)((WCHAR
*)buffer
, s
->user
);
466 /******************************************************************
467 * SymFindFileInPathW (DBGHELP.@)
470 BOOL WINAPI
SymFindFileInPathW(HANDLE hProcess
, PCWSTR searchPath
, PCWSTR full_path
,
471 PVOID id
, DWORD two
, DWORD three
, DWORD flags
,
472 PWSTR buffer
, PFINDFILEINPATHCALLBACKW cb
,
476 struct process
* pcs
= process_find_by_handle(hProcess
);
479 const WCHAR
* filename
;
481 TRACE("(%p %s %s %p %08x %08x %08x %p %p %p)\n",
482 hProcess
, debugstr_w(searchPath
), debugstr_w(full_path
),
483 id
, two
, three
, flags
, buffer
, cb
, user
);
485 if (!pcs
) return FALSE
;
486 if (!searchPath
) searchPath
= pcs
->search_path
;
495 filename
= file_nameW(full_path
);
496 s
.kind
= module_get_type_by_name(filename
);
498 /* first check full path to file */
499 if (sffip_cb(full_path
, &s
))
501 strcpyW(buffer
, full_path
);
507 ptr
= strchrW(searchPath
, ';');
510 memcpy(tmp
, searchPath
, (ptr
- searchPath
) * sizeof(WCHAR
));
511 tmp
[ptr
- searchPath
] = 0;
512 searchPath
= ptr
+ 1;
516 strcpyW(tmp
, searchPath
);
519 if (do_searchW(filename
, tmp
, FALSE
, sffip_cb
, &s
))
521 strcpyW(buffer
, tmp
);
528 /******************************************************************
529 * SymFindFileInPath (DBGHELP.@)
532 BOOL WINAPI
SymFindFileInPath(HANDLE hProcess
, PCSTR searchPath
, PCSTR full_path
,
533 PVOID id
, DWORD two
, DWORD three
, DWORD flags
,
534 PSTR buffer
, PFINDFILEINPATHCALLBACK cb
,
537 WCHAR searchPathW
[MAX_PATH
];
538 WCHAR full_pathW
[MAX_PATH
];
539 WCHAR bufferW
[MAX_PATH
];
540 struct enum_dir_treeWA edt
;
543 /* a PFINDFILEINPATHCALLBACK and a PENUMDIRTREE_CALLBACK have actually the
544 * same signature & semantics, hence we can reuse the EnumDirTree W->A
550 MultiByteToWideChar(CP_ACP
, 0, searchPath
, -1, searchPathW
, MAX_PATH
);
551 MultiByteToWideChar(CP_ACP
, 0, full_path
, -1, full_pathW
, MAX_PATH
);
552 if ((ret
= SymFindFileInPathW(hProcess
, searchPath
? searchPathW
: NULL
, full_pathW
,
553 id
, two
, three
, flags
,
554 bufferW
, enum_dir_treeWA
, &edt
)))
555 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);