Make sure a cursor is always returned if one exists.
[wine] / dlls / dbghelp / path.c
1 /*
2  * File path.c - managing path in debugging environments
3  *
4  * Copyright (C) 2004, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25
26 #include "dbghelp_private.h"
27 #include "ntstatus.h"
28 #include "winnls.h"
29 #include "winreg.h"
30 #include "winternl.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
34
35 static inline BOOL is_sep(char ch) {return ch == '/' || ch == '\\';}
36
37 static inline char* file_name(char* str)
38 {
39     char*       p;
40
41     for (p = str + strlen(str) - 1; p >= str && !is_sep(*p); p--);
42     return p + 1;
43 }
44
45 /******************************************************************
46  *              FindDebugInfoFile (DBGHELP.@)
47  *
48  */
49 HANDLE WINAPI FindDebugInfoFile(PSTR FileName, PSTR SymbolPath, PSTR DebugFilePath)
50 {
51     HANDLE      h;
52
53     h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
54                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
55     if (h == INVALID_HANDLE_VALUE)
56     {
57         if (!SearchPathA(SymbolPath, file_name(FileName), NULL, MAX_PATH, DebugFilePath, NULL))
58             return NULL;
59         h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
60                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
61     }
62     return (h == INVALID_HANDLE_VALUE) ? NULL : h;
63 }
64  
65 /******************************************************************
66  *              FindDebugInfoFileEx (DBGHELP.@)
67  *
68  */
69 HANDLE WINAPI FindDebugInfoFileEx(PSTR FileName, PSTR SymbolPath,
70                                   PSTR DebugFilePath, 
71                                   PFIND_DEBUG_FILE_CALLBACK Callback,
72                                   PVOID CallerData)
73 {
74     FIXME("(%s %s %p %p %p): stub\n", 
75           FileName, SymbolPath, DebugFilePath, Callback, CallerData);
76     return NULL;
77 }
78
79 /******************************************************************
80  *              FindExecutableImage (DBGHELP.@)
81  *
82  */
83 HANDLE WINAPI FindExecutableImage(PSTR FileName, PSTR SymbolPath, PSTR ImageFilePath)
84 {
85     HANDLE h;
86     if (!SearchPathA(SymbolPath, FileName, NULL, MAX_PATH, ImageFilePath, NULL))
87         return NULL;
88     h = CreateFileA(ImageFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, 
89                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
90     return (h == INVALID_HANDLE_VALUE) ? NULL : h;
91 }
92
93 /***********************************************************************
94  *           MakeSureDirectoryPathExists (DBGHELP.@)
95  */
96 BOOL WINAPI MakeSureDirectoryPathExists(LPCSTR DirPath)
97 {
98     char path[MAX_PATH];
99     const char *p = DirPath;
100     int  n;
101
102     if (p[0] && p[1] == ':') p += 2;
103     while (*p == '\\') p++; /* skip drive root */
104     while ((p = strchr(p, '\\')) != NULL)
105     {
106        n = p - DirPath + 1;
107        memcpy(path, DirPath, n);
108        path[n] = '\0';
109        if( !CreateDirectoryA(path, NULL)            &&
110            (GetLastError() != ERROR_ALREADY_EXISTS))
111            return FALSE;
112        p++;
113     }
114     if (GetLastError() == ERROR_ALREADY_EXISTS)
115        SetLastError(ERROR_SUCCESS);
116
117     return TRUE;
118 }
119
120 /******************************************************************
121  *              SymMatchFileName (DBGHELP.@)
122  *
123  */
124 BOOL WINAPI SymMatchFileName(char* file, char* match,
125                              char** filestop, char** matchstop)
126 {
127     char*       fptr;
128     char*       mptr;
129
130     TRACE("(%s %s %p %p)\n", file, match, filestop, matchstop);
131
132     fptr = file + strlen(file) - 1;
133     mptr = match + strlen(match) - 1;
134
135     while (fptr >= file && mptr >= match)
136     {
137         if (toupper(*fptr) != toupper(*mptr) && !(is_sep(*fptr) && is_sep(*mptr)))
138             break;
139         fptr--; mptr--;
140     }
141     if (filestop) *filestop = fptr;
142     if (matchstop) *matchstop = mptr;
143
144     return mptr == match - 1;
145 }
146
147 static BOOL do_search(const char* file, char* buffer, BOOL recurse,
148                       PENUMDIRTREE_CALLBACK cb, void* user)
149 {
150     HANDLE              h;
151     WIN32_FIND_DATAA    fd;
152     unsigned            pos;
153     BOOL                found = FALSE;
154
155     pos = strlen(buffer);
156     if (buffer[pos - 1] != '\\') buffer[pos++] = '\\';
157     strcpy(buffer + pos, "*.*");
158     if ((h = FindFirstFileA(buffer, &fd)) == INVALID_HANDLE_VALUE)
159         return FALSE;
160     /* doc doesn't specify how the tree is enumerated... 
161      * doing a depth first based on, but may be wrong
162      */
163     do
164     {
165         if (!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, "..")) continue;
166
167         strcpy(buffer + pos, fd.cFileName);
168         if (recurse && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
169             found = do_search(file, buffer, TRUE, cb, user);
170         else if (SymMatchFileName(buffer, (char*)file, NULL, NULL))
171         {
172             if (!cb || cb(buffer, user)) found = TRUE;
173         }
174     } while (!found && FindNextFileA(h, &fd));
175     if (!found) buffer[--pos] = '\0';
176     FindClose(h);
177
178     return found;
179 }
180
181 /***********************************************************************
182  *           SearchTreeForFile (DBGHELP.@)
183  */
184 BOOL WINAPI SearchTreeForFile(LPSTR root, LPSTR file, LPSTR buffer)
185 {
186     TRACE("(%s, %s, %p)\n", 
187           debugstr_a(root), debugstr_a(file), buffer);
188     strcpy(buffer, root);
189     return do_search(file, buffer, TRUE, NULL, NULL);
190 }
191
192 /******************************************************************
193  *              EnumDirTree (DBGHELP.@)
194  *
195  *
196  */
197 BOOL WINAPI EnumDirTree(HANDLE hProcess, PCSTR root, PCSTR file,
198                         LPSTR buffer, PENUMDIRTREE_CALLBACK cb, PVOID user)
199 {
200     TRACE("(%p %s %s %p %p %p)\n", hProcess, root, file, buffer, cb, user);
201
202     strcpy(buffer, root);
203     return do_search(file, buffer, TRUE, cb, user);
204 }
205
206 struct sffip
207 {
208     enum module_type            kind;
209     /* pe:  id  -> DWORD:timestamp
210      *      two -> size of image (from PE header)
211      * pdb: id  -> PDB signature
212      *            I think either DWORD:timestamp or GUID:guid depending on PDB version
213      *      two -> PDB age ???
214      * elf: id  -> DWORD:CRC 32 of ELF image (Wine only)
215      */
216     PVOID                       id;
217     DWORD                       two;
218     DWORD                       three;
219     DWORD                       flags;
220     PFINDFILEINPATHCALLBACK     cb;
221     void*                       user;
222 };
223
224 static BOOL CALLBACK sffip_cb(LPCSTR buffer, void* user)
225 {
226     struct sffip*       s = (struct sffip*)user;
227     DWORD               size, checksum;
228     DWORD_PTR           timestamp;
229     /* FIXME: should check that id/two/three match the file pointed
230      * by buffer
231      */
232     switch (s->kind)
233     {
234     case DMT_PE:
235         {
236             HANDLE  hFile, hMap;
237             void*   mapping;
238
239             timestamp = ~(DWORD_PTR)s->id;
240             size = ~s->two;
241             hFile = CreateFileA(buffer, GENERIC_READ, FILE_SHARE_READ, NULL, 
242                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
243             if (hFile == INVALID_HANDLE_VALUE) return TRUE;
244             if ((hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
245             {
246                 if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
247                 {
248                     IMAGE_NT_HEADERS*   nth = RtlImageNtHeader(mapping);
249                     timestamp = nth->FileHeader.TimeDateStamp;
250                     size = nth->OptionalHeader.SizeOfImage;
251                     UnmapViewOfFile(mapping);
252                 }
253                 CloseHandle(hMap);
254             }
255             CloseHandle(hFile);
256             if (timestamp != (DWORD_PTR)s->id || size != s->two)
257             {
258                 WARN("Found %s, but wrong size or timestamp\n", buffer);
259                 return TRUE;
260             }
261         }
262         break;
263     case DMT_ELF:
264         if (elf_fetch_file_info(buffer, 0, &size, &checksum))
265         {
266             if (checksum != (DWORD_PTR)s->id)
267             {
268                 WARN("Found %s, but wrong checksums: %08lx %08lx\n",
269                       buffer, checksum, (DWORD_PTR)s->id);
270                 return TRUE;
271             }
272         }
273         else
274         {
275             WARN("Couldn't read %s\n", buffer);
276             return TRUE;
277         }
278         break;
279     case DMT_PDB:
280         FIXME("NIY on '%s'\n", buffer);
281         break;
282     default:
283         FIXME("What the heck??\n");
284         return TRUE;
285     }
286     /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
287      * convention to stop/continue enumeration. sigh.
288      */
289     return !(s->cb)((char*)buffer, s->user);
290 }
291
292 /******************************************************************
293  *              SymFindFileInPath (DBGHELP.@)
294  *
295  */
296 BOOL WINAPI SymFindFileInPath(HANDLE hProcess, LPSTR searchPath, LPSTR full_path,
297                               PVOID id, DWORD two, DWORD three, DWORD flags,
298                               LPSTR buffer, PFINDFILEINPATHCALLBACK cb,
299                               PVOID user)
300 {
301     struct sffip        s;
302     struct process*     pcs = process_find_by_handle(hProcess);
303     char                tmp[MAX_PATH];
304     char*               ptr;
305     char*               filename;
306
307     TRACE("(%p %s %s %p %08lx %08lx %08lx %p %p %p)\n",
308           hProcess, searchPath, full_path, id, two, three, flags, 
309           buffer, cb, user);
310
311     if (!pcs) return FALSE;
312     if (!searchPath) searchPath = pcs->search_path;
313
314     s.id = id;
315     s.two = two;
316     s.three = three;
317     s.flags = flags;
318     s.cb = cb;
319     s.user = user;
320
321     filename = file_name(full_path);
322     s.kind = module_get_type_by_name(filename);
323
324     /* first check full path to file */
325     if (sffip_cb(full_path, &s))
326     {
327         strcpy(buffer, full_path);
328         return TRUE;
329     }
330
331     while (searchPath)
332     {
333         ptr = strchr(searchPath, ';');
334         if (ptr)
335         {
336             memcpy(tmp, searchPath, ptr - searchPath);
337             tmp[ptr - searchPath] = 0;
338             searchPath = ptr + 1;
339         }
340         else
341         {
342             strcpy(tmp, searchPath);
343             searchPath = NULL;
344         }
345         if (do_search(filename, tmp, FALSE, sffip_cb, &s)) {
346             strcpy(buffer, tmp);
347             return TRUE;
348         }
349     }
350     return FALSE;
351 }