[CMAKE]
[reactos.git] / dll / win32 / dbghelp / path.c
index 2e191af..12c595b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * File path.c - managing path in debugging environments
  *
- * Copyright (C) 2004, Eric Pouech
+ * Copyright (C) 2004,2008, Eric Pouech
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -57,7 +57,7 @@ HANDLE WINAPI FindDebugInfoFile(PCSTR FileName, PCSTR SymbolPath, PSTR DebugFile
 {
     HANDLE      h;
 
-    h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
+    h = CreateFileA(FileName, GENERIC_READ, FILE_SHARE_READ, NULL,
                     OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
     if (h == INVALID_HANDLE_VALUE)
     {
@@ -79,7 +79,7 @@ HANDLE WINAPI FindDebugInfoFileEx(PCSTR FileName, PCSTR SymbolPath,
                                   PVOID CallerData)
 {
     FIXME("(%s %s %p %p %p): stub\n", 
-          FileName, SymbolPath, DebugFilePath, Callback, CallerData);
+          debugstr_a(FileName), debugstr_a(SymbolPath), debugstr_a(DebugFilePath), Callback, CallerData);
     return NULL;
 }
 
@@ -191,7 +191,7 @@ BOOL WINAPI SymMatchFileName(PCSTR file, PCSTR match,
     PCSTR fptr;
     PCSTR mptr;
 
-    TRACE("(%s %s %p %p)\n", file, match, filestop, matchstop);
+    TRACE("(%s %s %p %p)\n", debugstr_a(file), debugstr_a(match), filestop, matchstop);
 
     fptr = file + strlen(file) - 1;
     mptr = match + strlen(match) - 1;
@@ -217,7 +217,7 @@ static BOOL do_searchW(PCWSTR file, PWSTR buffer, BOOL recurse,
     BOOL                found = FALSE;
     static const WCHAR  S_AllW[] = {'*','.','*','\0'};
     static const WCHAR  S_DotW[] = {'.','\0'};
-    static const WCHAR  S_DotDotW[] = {'.','\0'};
+    static const WCHAR  S_DotDotW[] = {'.','.','\0'};
 
     pos = strlenW(buffer);
     if (buffer[pos - 1] != '\\') buffer[pos++] = '\\';
@@ -234,7 +234,7 @@ static BOOL do_searchW(PCWSTR file, PWSTR buffer, BOOL recurse,
         strcpyW(buffer + pos, fd.cFileName);
         if (recurse && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
             found = do_searchW(file, buffer, TRUE, cb, user);
-        else if (SymMatchFileNameW(buffer, (WCHAR*)file, NULL, NULL))
+        else if (SymMatchFileNameW(buffer, file, NULL, NULL))
         {
             if (!cb || cb(buffer, user)) found = TRUE;
         }
@@ -329,18 +329,6 @@ BOOL WINAPI EnumDirTree(HANDLE hProcess, PCSTR root, PCSTR file,
 
 struct sffip
 {
-    enum module_type            kind;
-    /* pe:  id  -> DWORD:timestamp
-     *      two -> size of image (from PE header)
-     * pdb: id  -> PDB signature
-     *            I think either DWORD:timestamp or GUID:guid depending on PDB version
-     *      two -> PDB age ???
-     * elf: id  -> DWORD:CRC 32 of ELF image (Wine only)
-     */
-    PVOID                       id;
-    DWORD                       two;
-    DWORD                       three;
-    DWORD                       flags;
     PFINDFILEINPATHCALLBACKW    cb;
     void*                       user;
 };
@@ -352,13 +340,140 @@ struct sffip
  */
 static BOOL CALLBACK sffip_cb(PCWSTR buffer, PVOID user)
 {
-    struct sffip*       s = (struct sffip*)user;
-    DWORD               size, checksum;
+    struct sffip*       s = user;
+
+    if (!s->cb) return TRUE;
+    /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
+     * convention to stop/continue enumeration. sigh.
+     */
+    return !(s->cb)(buffer, s->user);
+}
+
+/******************************************************************
+ *             SymFindFileInPathW (DBGHELP.@)
+ *
+ */
+BOOL WINAPI SymFindFileInPathW(HANDLE hProcess, PCWSTR searchPath, PCWSTR full_path,
+                               PVOID id, DWORD two, DWORD three, DWORD flags,
+                               PWSTR buffer, PFINDFILEINPATHCALLBACKW cb,
+                               PVOID user)
+{
+    struct sffip        s;
+    struct process*     pcs = process_find_by_handle(hProcess);
+    WCHAR               tmp[MAX_PATH];
+    WCHAR*              ptr;
+    const WCHAR*        filename;
+
+    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",
+          hProcess, debugstr_w(searchPath), debugstr_w(full_path),
+          id, two, three, flags, buffer, cb, user);
+
+    if (!pcs) return FALSE;
+    if (!searchPath) searchPath = pcs->search_path;
+
+    s.cb = cb;
+    s.user = user;
+
+    filename = file_nameW(full_path);
+
+    /* first check full path to file */
+    if (sffip_cb(full_path, &s))
+    {
+        strcpyW(buffer, full_path);
+        return TRUE;
+    }
+
+    while (searchPath)
+    {
+        ptr = strchrW(searchPath, ';');
+        if (ptr)
+        {
+            memcpy(tmp, searchPath, (ptr - searchPath) * sizeof(WCHAR));
+            tmp[ptr - searchPath] = 0;
+            searchPath = ptr + 1;
+        }
+        else
+        {
+            strcpyW(tmp, searchPath);
+            searchPath = NULL;
+        }
+        if (do_searchW(filename, tmp, FALSE, sffip_cb, &s))
+        {
+            strcpyW(buffer, tmp);
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/******************************************************************
+ *             SymFindFileInPath (DBGHELP.@)
+ *
+ */
+BOOL WINAPI SymFindFileInPath(HANDLE hProcess, PCSTR searchPath, PCSTR full_path,
+                              PVOID id, DWORD two, DWORD three, DWORD flags,
+                              PSTR buffer, PFINDFILEINPATHCALLBACK cb,
+                              PVOID user)
+{
+    WCHAR                       searchPathW[MAX_PATH];
+    WCHAR                       full_pathW[MAX_PATH];
+    WCHAR                       bufferW[MAX_PATH];
+    struct enum_dir_treeWA      edt;
+    BOOL                        ret;
 
-    /* FIXME: should check that id/two/three match the file pointed
+    /* a PFINDFILEINPATHCALLBACK and a PENUMDIRTREE_CALLBACK have actually the
+     * same signature & semantics, hence we can reuse the EnumDirTree W->A
+     * conversion helper
+     */
+    edt.cb = cb;
+    edt.user = user;
+    if (searchPath)
+        MultiByteToWideChar(CP_ACP, 0, searchPath, -1, searchPathW, MAX_PATH);
+    MultiByteToWideChar(CP_ACP, 0, full_path, -1, full_pathW, MAX_PATH);
+    if ((ret =  SymFindFileInPathW(hProcess, searchPath ? searchPathW : NULL, full_pathW,
+                                   id, two, three, flags,
+                                   bufferW, enum_dir_treeWA, &edt)))
+        WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
+    return ret;
+}
+
+struct module_find
+{
+    enum module_type            kind;
+    /* pe:  dw1         DWORD:timestamp
+     *      dw2         size of image (from PE header)
+     * pdb: guid        PDB guid (if DS PDB file)
+     *      or dw1      PDB timestamp (if JG PDB file)
+     *      dw2         PDB age
+     * elf: dw1         DWORD:CRC 32 of ELF image (Wine only)
+     */
+    const GUID*                 guid;
+    DWORD                       dw1;
+    DWORD                       dw2;
+    WCHAR                       filename[MAX_PATH];
+    unsigned                    matched;
+};
+
+/* checks that buffer (as found by matching the name) matches the info
+ * (information is based on file type)
+ * returns TRUE when file is found, FALSE to continue searching
+ * (NB this is the opposite convention of SymFindFileInPathProc)
+ */
+static BOOL CALLBACK module_find_cb(PCWSTR buffer, PVOID user)
+{
+    struct module_find* mf = user;
+    DWORD               size, checksum, timestamp;
+    unsigned            matched = 0;
+
+    /* the matching weights:
+     * +1 if a file with same name is found and is a decent file of expected type
+     * +1 if first parameter and second parameter match
+     */
+
+    /* FIXME: should check that id/two match the file pointed
      * by buffer
      */
-    switch (s->kind)
+    switch (mf->kind)
     {
     case DMT_PE:
         {
@@ -366,8 +481,8 @@ static BOOL CALLBACK sffip_cb(PCWSTR buffer, PVOID user)
             void*   mapping;
             DWORD   timestamp;
 
-            timestamp = ~(DWORD_PTR)s->id;
-            size = ~s->two;
+            timestamp = ~mf->dw1;
+            size = ~mf->dw2;
             hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL,
                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
             if (hFile == INVALID_HANDLE_VALUE) return FALSE;
@@ -376,6 +491,8 @@ static BOOL CALLBACK sffip_cb(PCWSTR buffer, PVOID user)
                 if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
                 {
                     IMAGE_NT_HEADERS*   nth = RtlImageNtHeader(mapping);
+
+                    matched++;
                     timestamp = nth->FileHeader.TimeDateStamp;
                     size = nth->OptionalHeader.SizeOfImage;
                     UnmapViewOfFile(mapping);
@@ -383,22 +500,36 @@ static BOOL CALLBACK sffip_cb(PCWSTR buffer, PVOID user)
                 CloseHandle(hMap);
             }
             CloseHandle(hFile);
-            if (timestamp != (DWORD_PTR)s->id || size != s->two)
-            {
-                WARN("Found %s, but wrong size or timestamp\n", debugstr_w(buffer));
-                return FALSE;
-            }
+            if (timestamp != mf->dw1)
+                WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer));
+            if (size != mf->dw2)
+                WARN("Found %s, but wrong size\n", debugstr_w(buffer));
+            if (timestamp == mf->dw1 && size == mf->dw2) matched++;
         }
         break;
     case DMT_ELF:
         if (elf_fetch_file_info(buffer, 0, &size, &checksum))
         {
-            if (checksum != (DWORD_PTR)s->id)
-            {
-                WARN("Found %s, but wrong checksums: %08x %08lx\n",
-                     debugstr_w(buffer), checksum, (DWORD_PTR)s->id);
-                return FALSE;
-            }
+            matched++;
+            if (checksum == mf->dw1) matched++;
+            else
+                WARN("Found %s, but wrong checksums: %08x %08x\n",
+                     debugstr_w(buffer), checksum, mf->dw1);
+        }
+        else
+        {
+            WARN("Couldn't read %s\n", debugstr_w(buffer));
+            return FALSE;
+        }
+        break;
+    case DMT_MACHO:
+        if (macho_fetch_file_info(buffer, 0, &size, &checksum))
+        {
+            matched++;
+            if (checksum == mf->dw1) matched++;
+            else
+                WARN("Found %s, but wrong checksums: %08x %08x\n",
+                     debugstr_w(buffer), checksum, mf->dw1);
         }
         else
         {
@@ -415,90 +546,114 @@ static BOOL CALLBACK sffip_cb(PCWSTR buffer, PVOID user)
             pdb_lookup.filename = fn;
 
             if (!pdb_fetch_file_info(&pdb_lookup)) return FALSE;
+            matched++;
             switch (pdb_lookup.kind)
             {
             case PDB_JG:
-                if (s->flags & SSRVOPT_GUIDPTR)
+                if (mf->guid)
                 {
                     WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer));
-                    return FALSE;
-                }
-                if (pdb_lookup.u.jg.timestamp != (DWORD_PTR)s->id)
-                {
-                    WARN("Found %s, but wrong signature: %08x %08lx\n",
-                         debugstr_w(buffer), pdb_lookup.u.jg.timestamp, (DWORD_PTR)s->id);
-                    return FALSE;
                 }
+                else if (pdb_lookup.u.jg.timestamp == mf->dw1)
+                    matched++;
+                else
+                    WARN("Found %s, but wrong signature: %08x %08x\n",
+                         debugstr_w(buffer), pdb_lookup.u.jg.timestamp, mf->dw1);
                 break;
             case PDB_DS:
-                if (!(s->flags & SSRVOPT_GUIDPTR))
+                if (!mf->guid)
                 {
                     WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer));
-                    return FALSE;
                 }
-                if (memcmp(&pdb_lookup.u.ds.guid, (GUID*)s->id, sizeof(GUID)))
-                {
+                else if (!memcmp(&pdb_lookup.u.ds.guid, mf->guid, sizeof(GUID)))
+                    matched++;
+                else
                     WARN("Found %s, but wrong GUID: %s %s\n",
                          debugstr_w(buffer), debugstr_guid(&pdb_lookup.u.ds.guid),
-                         debugstr_guid((GUID*)s->id));
-                    return FALSE;
-                }
+                         debugstr_guid(mf->guid));
                 break;
             }
-            if (pdb_lookup.age != s->two)
+            if (pdb_lookup.age != mf->dw2)
             {
+                matched--;
                 WARN("Found %s, but wrong age: %08x %08x\n",
-                     debugstr_w(buffer), pdb_lookup.age, s->two);
-                return FALSE;
+                     debugstr_w(buffer), pdb_lookup.age, mf->dw2);
             }
         }
         break;
+    case DMT_DBG:
+        {
+            HANDLE  hFile, hMap;
+            void*   mapping;
+
+            timestamp = ~mf->dw1;
+            hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL,
+                                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+            if (hFile == INVALID_HANDLE_VALUE) return FALSE;
+            if ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
+            {
+                if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
+                {
+                    const IMAGE_SEPARATE_DEBUG_HEADER*  hdr;
+                    hdr = mapping;
+
+                    if (hdr->Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE)
+                    {
+                        matched++;
+                        timestamp = hdr->TimeDateStamp;
+                    }
+                    UnmapViewOfFile(mapping);
+                }
+                CloseHandle(hMap);
+            }
+            CloseHandle(hFile);
+            if (timestamp == mf->dw1) matched++;
+            else WARN("Found %s, but wrong timestamp\n", debugstr_w(buffer));
+        }
+        break;
     default:
         FIXME("What the heck??\n");
         return FALSE;
     }
+    if (matched > mf->matched)
+    {
+        strcpyW(mf->filename, buffer);
+        mf->matched = matched;
+    }
     /* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
      * convention to stop/continue enumeration. sigh.
      */
-    return !(s->cb)((WCHAR*)buffer, s->user);
+    return mf->matched == 2;
 }
 
-/******************************************************************
- *             SymFindFileInPathW (DBGHELP.@)
- *
- */
-BOOL WINAPI SymFindFileInPathW(HANDLE hProcess, PCWSTR searchPath, PCWSTR full_path,
-                               PVOID id, DWORD two, DWORD three, DWORD flags,
-                               PWSTR buffer, PFINDFILEINPATHCALLBACKW cb,
-                               PVOID user)
+BOOL path_find_symbol_file(const struct process* pcs, PCSTR full_path,
+                           const GUID* guid, DWORD dw1, DWORD dw2, PSTR buffer,
+                           BOOL* is_unmatched)
 {
-    struct sffip        s;
-    struct process*     pcs = process_find_by_handle(hProcess);
+    struct module_find  mf;
+    WCHAR               full_pathW[MAX_PATH];
     WCHAR               tmp[MAX_PATH];
     WCHAR*              ptr;
     const WCHAR*        filename;
+    WCHAR*              searchPath = pcs->search_path;
 
-    TRACE("(%p %s %s %p %08x %08x %08x %p %p %p)\n",
-          hProcess, debugstr_w(searchPath), debugstr_w(full_path),
-          id, two, three, flags, buffer, cb, user);
+    TRACE("(pcs = %p, full_path = %s, guid = %s, dw1 = 0x%08x, dw2 = 0x%08x, buffer = %p)\n",
+          pcs, debugstr_a(full_path), debugstr_guid(guid), dw1, dw2, buffer);
 
-    if (!pcs) return FALSE;
-    if (!searchPath) searchPath = pcs->search_path;
+    mf.guid = guid;
+    mf.dw1 = dw1;
+    mf.dw2 = dw2;
+    mf.matched = 0;
 
-    s.id = id;
-    s.two = two;
-    s.three = three;
-    s.flags = flags;
-    s.cb = cb;
-    s.user = user;
-
-    filename = file_nameW(full_path);
-    s.kind = module_get_type_by_name(filename);
+    MultiByteToWideChar(CP_ACP, 0, full_path, -1, full_pathW, MAX_PATH);
+    filename = file_nameW(full_pathW);
+    mf.kind = module_get_type_by_name(filename);
+    *is_unmatched = FALSE;
 
     /* first check full path to file */
-    if (sffip_cb(full_path, &s))
+    if (module_find_cb(full_pathW, &mf))
     {
-        strcpyW(buffer, full_path);
+        WideCharToMultiByte(CP_ACP, 0, full_pathW, -1, buffer, MAX_PATH, NULL, NULL);
         return TRUE;
     }
 
@@ -508,7 +663,7 @@ BOOL WINAPI SymFindFileInPathW(HANDLE hProcess, PCWSTR searchPath, PCWSTR full_p
         if (ptr)
         {
             memcpy(tmp, searchPath, (ptr - searchPath) * sizeof(WCHAR));
-            tmp[ptr - searchPath] = 0;
+            tmp[ptr - searchPath] = '\0';
             searchPath = ptr + 1;
         }
         else
@@ -516,42 +671,19 @@ BOOL WINAPI SymFindFileInPathW(HANDLE hProcess, PCWSTR searchPath, PCWSTR full_p
             strcpyW(tmp, searchPath);
             searchPath = NULL;
         }
-        if (do_searchW(filename, tmp, FALSE, sffip_cb, &s))
+        if (do_searchW(filename, tmp, FALSE, module_find_cb, &mf))
         {
-            strcpyW(buffer, tmp);
+            /* return first fully matched file */
+            WideCharToMultiByte(CP_ACP, 0, tmp, -1, buffer, MAX_PATH, NULL, NULL);
             return TRUE;
         }
     }
+    /* if no fully matching file is found, return the best matching file if any */
+    if ((dbghelp_options & SYMOPT_LOAD_ANYTHING) && mf.matched)
+    {
+        WideCharToMultiByte(CP_ACP, 0, mf.filename, -1, buffer, MAX_PATH, NULL, NULL);
+        *is_unmatched = TRUE;
+        return TRUE;
+    }
     return FALSE;
 }
-
-/******************************************************************
- *             SymFindFileInPath (DBGHELP.@)
- *
- */
-BOOL WINAPI SymFindFileInPath(HANDLE hProcess, PCSTR searchPath, PCSTR full_path,
-                              PVOID id, DWORD two, DWORD three, DWORD flags,
-                              PSTR buffer, PFINDFILEINPATHCALLBACK cb,
-                              PVOID user)
-{
-    WCHAR                       searchPathW[MAX_PATH];
-    WCHAR                       full_pathW[MAX_PATH];
-    WCHAR                       bufferW[MAX_PATH];
-    struct enum_dir_treeWA      edt;
-    BOOL                        ret;
-
-    /* a PFINDFILEINPATHCALLBACK and a PENUMDIRTREE_CALLBACK have actually the
-     * same signature & semantics, hence we can reuse the EnumDirTree W->A
-     * conversion helper
-     */
-    edt.cb = cb;
-    edt.user = user;
-    if (searchPath)
-        MultiByteToWideChar(CP_ACP, 0, searchPath, -1, searchPathW, MAX_PATH);
-    MultiByteToWideChar(CP_ACP, 0, full_path, -1, full_pathW, MAX_PATH);
-    if ((ret =  SymFindFileInPathW(hProcess, searchPath ? searchPathW : NULL, full_pathW,
-                                   id, two, three, flags,
-                                   bufferW, enum_dir_treeWA, &edt)))
-        WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
-    return ret;
-}