winemenubuilder: add Wine's MIME types to freedesktop

Damjan Jovanovic damjan.jov at gmail.com
Mon Apr 27 08:47:12 CDT 2009


Changelog:
* add Wine's MIME types to freedesktop

Damjan Jovanovic
-------------- next part --------------
diff --git a/programs/winemenubuilder/Makefile.in b/programs/winemenubuilder/Makefile.in
index 050b3b3..3d0af13 100644
--- a/programs/winemenubuilder/Makefile.in
+++ b/programs/winemenubuilder/Makefile.in
@@ -5,7 +5,7 @@ SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = winemenubuilder.exe
 APPMODE   = -mwindows
-IMPORTS   = uuid shell32 ole32 user32 advapi32 kernel32
+IMPORTS   = uuid shell32 ole32 user32 advapi32 kernel32 shlwapi
 
 C_SRCS = \
 	winemenubuilder.c
diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c
index 58e84c6..df8c58c 100644
--- a/programs/winemenubuilder/winemenubuilder.c
+++ b/programs/winemenubuilder/winemenubuilder.c
@@ -64,6 +64,7 @@
 #endif
 #include <errno.h>
 #include <stdarg.h>
+#include <fnmatch.h>
 
 #define COBJMACROS
 
@@ -74,6 +75,7 @@
 #include <appmgmt.h>
 #include <tlhelp32.h>
 #include <intshcut.h>
+#include <shlwapi.h>
 
 #include "wine/unicode.h"
 #include "wine/debug.h"
@@ -1251,6 +1253,38 @@ static HRESULT get_cmdline( IShellLinkW *sl, LPWSTR szPath, DWORD pathSize,
     return hr;
 }
 
+static WCHAR* assoc_query(ASSOCSTR assocStr, LPCWSTR name, LPCWSTR extra)
+{
+    HRESULT hr;
+    WCHAR *value = NULL;
+    DWORD size = 0;
+    hr = AssocQueryStringW(0, assocStr, name, extra, NULL, &size);
+    if (SUCCEEDED(hr))
+    {
+        value = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
+        if (value)
+        {
+            hr = AssocQueryStringW(0, assocStr, name, extra, value, &size);
+            if (FAILED(hr))
+            {
+                HeapFree(GetProcessHeap(), 0, value);
+                value = NULL;
+            }
+        }
+    }
+    return value;
+}
+
+static char* wchars_to_unix_chars(LPCWSTR string)
+{
+    char *ret;
+    INT size = WideCharToMultiByte(CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
+    ret = HeapAlloc(GetProcessHeap(), 0, size);
+    if (ret)
+        WideCharToMultiByte(CP_UTF8, 0, string, -1, ret, size, NULL, NULL);
+    return ret;
+}
+
 static BOOL next_line(FILE *file, char **line, int *size)
 {
     int pos = 0;
@@ -1316,9 +1350,12 @@ static BOOL add_mimes(const char *xdg_data_dir, struct list *mime_types)
                     if (mime_type_entry)
                     {
                         *pos = 0;
-                        mime_type_entry->mimeType = line;
-                        mime_type_entry->glob = pos + 1;
-                        list_add_tail(mime_types, &mime_type_entry->entry);
+                        mime_type_entry->mimeType = heap_printf("%s", line);
+                        mime_type_entry->glob = heap_printf("%s", pos + 1);
+                        if (mime_type_entry->mimeType && mime_type_entry->glob)
+                            list_add_tail(mime_types, &mime_type_entry->entry);
+                        else
+                            ret = FALSE;
                     }
                     else
                         ret = FALSE;
@@ -1341,6 +1378,7 @@ static void free_native_mime_types(struct list *native_mime_types)
     LIST_FOR_EACH_ENTRY_SAFE(mime_type_entry, mime_type_entry2, native_mime_types, struct xdg_mime_type, entry)
     {
         list_remove(&mime_type_entry->entry);
+        HeapFree(GetProcessHeap(), 0, mime_type_entry->glob);
         HeapFree(GetProcessHeap(), 0, mime_type_entry->mimeType);
         HeapFree(GetProcessHeap(), 0, mime_type_entry);
     }
@@ -1398,16 +1436,179 @@ static BOOL build_native_mime_types(const char *xdg_data_home, struct list **mim
     return ret;
 }
 
+static char* match_glob(struct list *native_mime_types, const char *extension, BOOL caseSensitive)
+{
+    struct xdg_mime_type *mime_type_entry;
+    char *match = NULL;
+    int matchLength = 0;
+
+    LIST_FOR_EACH_ENTRY(mime_type_entry, native_mime_types, struct xdg_mime_type, entry)
+    {
+        BOOL matched;
+        if (caseSensitive)
+            matched = (fnmatch(mime_type_entry->glob, extension, 0) == 0);
+        else
+#ifdef FNM_CASEFOLD
+            matched = (fnmatch(mime_type_entry->glob, extension, FNM_CASEFOLD) == 0);
+#else
+            WINE_FIXME("FNM_CASEFOLD is not implemented on your system\n");
+#endif
+        if (matched)
+        {
+            if (match == NULL || matchLength < strlen(mime_type_entry->glob))
+            {
+                match = mime_type_entry->mimeType;
+                matchLength = strlen(mime_type_entry->glob);
+            }
+        }
+    }
+    return match ? heap_printf("%s", match) : NULL;
+}
+
+static char* native_mime_type_for_extension(struct list *native_mime_types, const char *extension)
+{
+    char *mime_type;
+
+    mime_type = match_glob(native_mime_types, extension, TRUE);
+    if (mime_type)
+        return mime_type;
+    return match_glob(native_mime_types, extension, FALSE);
+}
+
+static BOOL write_freedesktop_mime_type_entry(const char *packages_dir, const char *dot_extension,
+                                              const char *mime_type, const char *comment)
+{
+    BOOL ret = FALSE;
+    char *filename;
+
+    filename = heap_printf("%s/x-wine-extension-%s.xml", packages_dir, &dot_extension[1]);
+    if (filename)
+    {
+        FILE *packageFile = fopen(filename, "w");
+        if (packageFile)
+        {
+            fprintf(packageFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+            fprintf(packageFile, "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n");
+            fprintf(packageFile, "  <mime-type type=\"%s\">\n", mime_type);
+            fprintf(packageFile, "    <glob pattern=\"*%s\"/>\n", dot_extension);
+            if (comment)
+                fprintf(packageFile, "    <comment>%s</comment>\n", comment);
+            fprintf(packageFile, "  </mime-type>\n");
+            fprintf(packageFile, "</mime-info>\n");
+            ret = TRUE;
+            fclose(packageFile);
+        }
+        else
+            WINE_ERR("error writing file %s\n", filename);
+        HeapFree(GetProcessHeap(), 0, filename);
+    }
+    else
+        WINE_ERR("out of memory\n");
+    return ret;
+}
+
 static BOOL generate_associations(const char *xdg_data_home, const char *packages_dir, const char *applications_dir)
 {
     struct list *nativeMimeTypes = NULL;
+    LSTATUS ret = 0;
+    int i;
+    BOOL hasChanged = FALSE;
 
     if (!build_native_mime_types(xdg_data_home, &nativeMimeTypes))
     {
         WINE_ERR("could not build native MIME types\n");
         return FALSE;
     }
-    return TRUE;
+
+    for (i = 0; ; i++)
+    {
+        WCHAR *extensionW;
+        DWORD size = 0;
+
+        ret = RegQueryInfoKeyW(HKEY_CLASSES_ROOT, 0, 0, 0, 0, &size, 0, 0, 0, 0, 0, 0);
+        if (ret != 0)
+        {
+            WINE_ERR("error reading registry\n");
+            break;
+        }
+        ++size;
+        extensionW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
+        if (extensionW == NULL)
+        {
+            WINE_ERR("out of memory\n");
+            break;
+        }
+        ret = RegEnumKeyExW(HKEY_CLASSES_ROOT, i, extensionW, &size, NULL, NULL, NULL, NULL);
+        if (ret == ERROR_SUCCESS && extensionW[0] == '.')
+        {
+            char *extensionA = NULL;
+            WCHAR *commandW = NULL;
+            WCHAR *friendlyDocNameW = NULL;
+            char *friendlyDocNameA = NULL;
+            WCHAR *contentTypeW = NULL;
+            char *mimeTypeA = NULL;
+
+            extensionA = wchars_to_unix_chars(extensionW);
+            if (extensionA == NULL)
+            {
+                WINE_ERR("out of memory\n");
+                goto end;
+            }
+
+            commandW = assoc_query(ASSOCSTR_COMMAND, extensionW, NULL);
+            if (commandW == NULL)
+                /* no command -> unusable extension */
+                goto end;
+
+            friendlyDocNameW = assoc_query(ASSOCSTR_FRIENDLYDOCNAME, extensionW, NULL);
+            if (friendlyDocNameW)
+            {
+                friendlyDocNameA = wchars_to_unix_chars(friendlyDocNameW);
+                if (friendlyDocNameA == NULL)
+                {
+                    WINE_ERR("out of memory\n");
+                    goto end;
+                }
+            }
+
+            contentTypeW = assoc_query(ASSOCSTR_CONTENTTYPE, extensionW, NULL);
+
+            mimeTypeA = native_mime_type_for_extension(nativeMimeTypes, extensionA);
+            if (mimeTypeA == NULL)
+            {
+                if (contentTypeW != NULL)
+                    mimeTypeA = wchars_to_unix_chars(contentTypeW);
+                else
+                    mimeTypeA = heap_printf("application/x-wine-extension-%s", &extensionA[1]);
+
+                if (mimeTypeA != NULL)
+                {
+                    write_freedesktop_mime_type_entry(packages_dir, extensionA, mimeTypeA, friendlyDocNameA);
+                    hasChanged = TRUE;
+                }
+                else
+                {
+                    WINE_ERR("out of memory\n");
+                    goto end;
+                }
+            }
+
+
+        end:
+            HeapFree(GetProcessHeap(), 0, extensionA);
+            HeapFree(GetProcessHeap(), 0, commandW);
+            HeapFree(GetProcessHeap(), 0, friendlyDocNameW);
+            HeapFree(GetProcessHeap(), 0, friendlyDocNameA);
+            HeapFree(GetProcessHeap(), 0, contentTypeW);
+            HeapFree(GetProcessHeap(), 0, mimeTypeA);
+        }
+        HeapFree(GetProcessHeap(), 0, extensionW);
+        if (ret != ERROR_SUCCESS)
+            break;
+    }
+
+    free_native_mime_types(nativeMimeTypes);
+    return hasChanged;
 }
 
 static BOOL InvokeShellLinker( IShellLinkW *sl, LPCWSTR link, BOOL bWait )
@@ -1841,6 +2042,7 @@ static void RefreshFileTypeAssociations(void)
     char *mime_dir = NULL;
     char *packages_dir = NULL;
     char *applications_dir = NULL;
+    BOOL hasChanged;
 
     hSem = CreateSemaphoreA( NULL, 1, 1, "winemenubuilder_semaphore");
     if( WAIT_OBJECT_0 != MsgWaitForMultipleObjects( 1, &hSem, FALSE, INFINITE, QS_ALLINPUT ) )
@@ -1875,7 +2077,23 @@ static void RefreshFileTypeAssociations(void)
     }
     create_directories(applications_dir);
 
-    generate_associations(xdg_data_dir, packages_dir, applications_dir);
+    hasChanged = generate_associations(xdg_data_dir, packages_dir, applications_dir);
+    if (hasChanged)
+    {
+        int unused;
+        char *command;
+        command = heap_printf("update-mime-database %s", mime_dir);
+        if (command)
+        {
+            unused = system(command);
+            HeapFree(GetProcessHeap(), 0, command);
+        }
+        else
+        {
+            WINE_ERR("out of memory\n");
+            goto end;
+        }
+    }
 
 end:
     if (hSem)


More information about the wine-patches mailing list