[3/6] winemenubuilder: Use a dispatch table to access XDG specific parts, extracted to different file.
Per Johansson
per at morth.org
Wed Oct 24 15:55:36 CDT 2012
Temporarily disables icon extraction for OS X.
---
programs/winemenubuilder/Makefile.in | 3 +-
programs/winemenubuilder/tests/xdg.c | 13 +
programs/winemenubuilder/winemenubuilder.c | 998 +++--------------------------
programs/winemenubuilder/winemenubuilder.h | 75 +++
programs/winemenubuilder/xdg.c | 985 ++++++++++++++++++++++++++++
5 files changed, 1171 insertions(+), 903 deletions(-)
create mode 100644 programs/winemenubuilder/winemenubuilder.h
create mode 100644 programs/winemenubuilder/xdg.c
diff --git a/programs/winemenubuilder/Makefile.in b/programs/winemenubuilder/Makefile.in
index 0c60b35..fd68ae2 100644
--- a/programs/winemenubuilder/Makefile.in
+++ b/programs/winemenubuilder/Makefile.in
@@ -3,6 +3,7 @@ APPMODE = -mwindows -municode
IMPORTS = uuid windowscodecs shell32 shlwapi ole32 user32 advapi32
C_SRCS = \
- winemenubuilder.c
+ winemenubuilder.c \
+ xdg.c
@MAKE_PROG_RULES@
diff --git a/programs/winemenubuilder/tests/xdg.c b/programs/winemenubuilder/tests/xdg.c
index 5f1b400..0619369 100644
--- a/programs/winemenubuilder/tests/xdg.c
+++ b/programs/winemenubuilder/tests/xdg.c
@@ -432,18 +432,31 @@ void remove_association_keys(void)
static void setup_environment(void)
{
+ HKEY key;
+
putenv((char*)"XDG_CONFIG_HOME=xdg_config");
putenv((char*)"XDG_DATA_HOME=xdg_data");
+
+ if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\MenuBuilder", &key))
+ delete_key(key);
+ ok(!RegCreateKeyA(HKEY_CURRENT_USER, "Software\\Wine\\MenuBuilder", &key), "Failed to create dispatch key\n");
+ ok(!RegSetValueExA(key, "Dispatch", 0, REG_SZ, (BYTE*)"xdg", sizeof("xdg")), "Failed to set dispatch value\n");
+ RegCloseKey(key);
}
static void cleanup_environment(void)
{
+ HKEY key;
char buffer[MAX_PATH];
strcpy(buffer, "xdg_config");
ok(remove_recursive(buffer), "Failed to cleanup xdg_config\n");
strcpy(buffer, "xdg_data");
ok(remove_recursive(buffer), "Failed to cleanup xdg_data\n");
+
+ /* TODO: restore old contents, if any */
+ if (!RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\MenuBuilder", &key))
+ delete_key(key);
}
static void test_create_association(void)
diff --git a/programs/winemenubuilder/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c
index 9e700c4..8af388b 100644
--- a/programs/winemenubuilder/winemenubuilder.c
+++ b/programs/winemenubuilder/winemenubuilder.c
@@ -7,6 +7,7 @@
* Copyright 2004 Dmitry Timoshkov
* Copyright 2005 Bill Medland
* Copyright 2008 Damjan Jovanovic
+ * Copyright 2011 - 2012 Per Johansson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -97,6 +98,8 @@
#include "wine/list.h"
#include "wine/rbtree.h"
+#include "winemenubuilder.h"
+
WINE_DEFAULT_DEBUG_CHANNEL(menubuilder);
#define in_desktop_dir(csidl) ((csidl)==CSIDL_DESKTOPDIRECTORY || \
@@ -130,25 +133,6 @@ typedef struct
typedef struct
{
- BYTE bWidth;
- BYTE bHeight;
- BYTE bColorCount;
- BYTE bReserved;
- WORD wPlanes;
- WORD wBitCount;
- DWORD dwBytesInRes;
- DWORD dwImageOffset;
-} ICONDIRENTRY;
-
-typedef struct
-{
- WORD idReserved;
- WORD idType;
- WORD idCount;
-} ICONDIR;
-
-typedef struct
-{
WORD offset;
WORD length;
WORD flags;
@@ -175,14 +159,6 @@ typedef struct
int nIndex;
} ENUMRESSTRUCT;
-struct xdg_mime_type
-{
- char *mimeType;
- char *glob;
- char *lower_glob;
- struct list entry;
-};
-
struct rb_string_entry
{
char *string;
@@ -191,35 +167,10 @@ struct rb_string_entry
DEFINE_GUID(CLSID_WICIcnsEncoder, 0x312fb6f1,0xb767,0x409d,0x8a,0x6d,0x0f,0xc1,0x54,0xd4,0xf0,0x5c);
-static char *xdg_config_dir;
-static char *xdg_data_dir;
-static char *xdg_desktop_dir;
+const struct winemenubuilder_dispatch *wmb_dispatch;
-static WCHAR* assoc_query(ASSOCSTR assocStr, LPCWSTR name, LPCWSTR extra);
static HRESULT open_icon(LPCWSTR filename, int index, BOOL bWait, IStream **ppStream);
-/* Utility routines */
-#ifndef __APPLE__
-static unsigned short crc16(const char* string)
-{
- unsigned short crc = 0;
- int i, j, xor_poly;
-
- for (i = 0; string[i] != 0; i++)
- {
- char c = string[i];
- for (j = 0; j < 8; c >>= 1, j++)
- {
- xor_poly = (c ^ crc) & 1;
- crc >>= 1;
- if (xor_poly)
- crc ^= 0xa001;
- }
- }
- return crc;
-}
-#endif
-
static char *strdupA( const char *str )
{
char *ret;
@@ -229,7 +180,7 @@ static char *strdupA( const char *str )
return ret;
}
-static char* heap_printf(const char *format, ...)
+char* heap_printf(const char *format, ...)
{
va_list args;
int size = 4096;
@@ -296,27 +247,7 @@ static const struct wine_rb_functions winemenubuilder_rb_functions =
winemenubuilder_rb_string_compare,
};
-static void write_xml_text(FILE *file, const char *text)
-{
- int i;
- for (i = 0; text[i]; i++)
- {
- if (text[i] == '&')
- fputs("&", file);
- else if (text[i] == '<')
- fputs("<", file);
- else if (text[i] == '>')
- fputs(">", file);
- else if (text[i] == '\'')
- fputs("'", file);
- else if (text[i] == '"')
- fputs(""", file);
- else
- fputc(text[i], file);
- }
-}
-
-static BOOL create_directories(char *directory)
+BOOL create_directories(char *directory)
{
BOOL ret = TRUE;
int i;
@@ -336,7 +267,7 @@ static BOOL create_directories(char *directory)
return ret;
}
-static char* wchars_to_utf8_chars(LPCWSTR string)
+char* wchars_to_utf8_chars(LPCWSTR string)
{
char *ret;
INT size = WideCharToMultiByte(CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
@@ -356,7 +287,7 @@ static char* wchars_to_unix_chars(LPCWSTR string)
return ret;
}
-static WCHAR* utf8_chars_to_wchars(LPCSTR string)
+WCHAR* utf8_chars_to_wchars(LPCSTR string)
{
WCHAR *ret;
INT size = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0);
@@ -372,7 +303,7 @@ static WCHAR* utf8_chars_to_wchars(LPCSTR string)
* FIXME: should not use stdio
*/
-static HRESULT convert_to_native_icon(IStream *icoFile, int *indices, int numIndices,
+HRESULT convert_to_native_icon(IStream *icoFile, int *indices, int numIndices,
const CLSID *outputFormat, const char *outputFileName, LPCWSTR commentW)
{
WCHAR *dosOutputFileName = NULL;
@@ -929,7 +860,7 @@ static HRESULT open_module_icon(LPCWSTR szFileName, int nIndex, IStream **ppStre
return hr;
}
-static HRESULT read_ico_direntries(IStream *icoStream, ICONDIRENTRY **ppIconDirEntries, int *numEntries)
+HRESULT read_ico_direntries(IStream *icoStream, ICONDIRENTRY **ppIconDirEntries, int *numEntries)
{
ICONDIR iconDir;
ULONG bytesRead;
@@ -1069,7 +1000,7 @@ static HRESULT open_icon(LPCWSTR filename, int index, BOOL bWait, IStream **ppSt
return hr;
}
-#ifdef __APPLE__
+#if 0
#define ICNS_SLOTS 6
static inline int size_to_slot(int size)
@@ -1217,150 +1148,10 @@ end:
HeapFree(GetProcessHeap(), 0, icnsPath);
return hr;
}
-#else
-static void refresh_icon_cache(const char *iconsDir)
-{
- /* The icon theme spec only requires the mtime on the "toplevel"
- * directory (whatever that is) to be changed for a refresh,
- * but on GNOME you have to create a file in that directory
- * instead. Creating a file also works on KDE, Xfce and LXDE.
- */
- char *filename = heap_printf("%s/.wine-refresh-XXXXXX", iconsDir);
- if (filename != NULL)
- {
- int fd = mkstemps(filename, 0);
- if (fd >= 0)
- {
- close(fd);
- unlink(filename);
- }
- HeapFree(GetProcessHeap(), 0, filename);
- }
-}
-
-static HRESULT platform_write_icon(IStream *icoStream, int exeIndex, LPCWSTR icoPathW,
- const char *destFilename, char **nativeIdentifier)
-{
- ICONDIRENTRY *iconDirEntries = NULL;
- int numEntries;
- int i;
- char *icoPathA = NULL;
- char *iconsDir = NULL;
- unsigned short crc;
- char *p, *q;
- HRESULT hr = S_OK;
- LARGE_INTEGER zero;
-
- hr = read_ico_direntries(icoStream, &iconDirEntries, &numEntries);
- if (FAILED(hr))
- goto end;
-
- icoPathA = wchars_to_utf8_chars(icoPathW);
- if (icoPathA == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto end;
- }
- crc = crc16(icoPathA);
- p = strrchr(icoPathA, '\\');
- if (p == NULL)
- p = icoPathA;
- else
- {
- *p = 0;
- p++;
- }
- q = strrchr(p, '.');
- if (q)
- *q = 0;
- if (destFilename)
- *nativeIdentifier = heap_printf("%s", destFilename);
- else
- *nativeIdentifier = heap_printf("%04X_%s.%d", crc, p, exeIndex);
- if (*nativeIdentifier == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto end;
- }
- iconsDir = heap_printf("%s/icons/hicolor", xdg_data_dir);
- if (iconsDir == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto end;
- }
-
- for (i = 0; i < numEntries; i++)
- {
- int bestIndex = i;
- int j;
- BOOLEAN duplicate = FALSE;
- int w, h;
- char *iconDir = NULL;
- char *pngPath = NULL;
-
- WINE_TRACE("[%d]: %d x %d @ %d\n", i, iconDirEntries[i].bWidth,
- iconDirEntries[i].bHeight, iconDirEntries[i].wBitCount);
-
- for (j = 0; j < i; j++)
- {
- if (iconDirEntries[j].bWidth == iconDirEntries[i].bWidth &&
- iconDirEntries[j].bHeight == iconDirEntries[i].bHeight)
- {
- duplicate = TRUE;
- break;
- }
- }
- if (duplicate)
- continue;
- for (j = i + 1; j < numEntries; j++)
- {
- if (iconDirEntries[j].bWidth == iconDirEntries[i].bWidth &&
- iconDirEntries[j].bHeight == iconDirEntries[i].bHeight &&
- iconDirEntries[j].wBitCount >= iconDirEntries[bestIndex].wBitCount)
- {
- bestIndex = j;
- }
- }
- WINE_TRACE("Selected: %d\n", bestIndex);
-
- w = iconDirEntries[bestIndex].bWidth ? iconDirEntries[bestIndex].bWidth : 256;
- h = iconDirEntries[bestIndex].bHeight ? iconDirEntries[bestIndex].bHeight : 256;
- iconDir = heap_printf("%s/%dx%d/apps", iconsDir, w, h);
- if (iconDir == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto endloop;
- }
- create_directories(iconDir);
- pngPath = heap_printf("%s/%s.png", iconDir, *nativeIdentifier);
- if (pngPath == NULL)
- {
- hr = E_OUTOFMEMORY;
- goto endloop;
- }
- zero.QuadPart = 0;
- hr = IStream_Seek(icoStream, zero, STREAM_SEEK_SET, NULL);
- if (FAILED(hr))
- goto endloop;
- hr = convert_to_native_icon(icoStream, &bestIndex, 1, &CLSID_WICPngEncoder,
- pngPath, icoPathW);
-
- endloop:
- HeapFree(GetProcessHeap(), 0, iconDir);
- HeapFree(GetProcessHeap(), 0, pngPath);
- }
- refresh_icon_cache(iconsDir);
-
-end:
- HeapFree(GetProcessHeap(), 0, iconDirEntries);
- HeapFree(GetProcessHeap(), 0, icoPathA);
- HeapFree(GetProcessHeap(), 0, iconsDir);
- return hr;
-}
-#endif /* defined(__APPLE__) */
+#endif
/* extract an icon from an exe or icon file; helper for IPersistFile_fnSave */
-static char *extract_icon(LPCWSTR icoPathW, int index, const char *destFilename, BOOL bWait)
+char *extract_icon(LPCWSTR icoPathW, int index, const char *destFilename, BOOL bWait)
{
IStream *stream = NULL;
HRESULT hr;
@@ -1374,7 +1165,7 @@ static char *extract_icon(LPCWSTR icoPathW, int index, const char *destFilename,
WINE_WARN("opening icon %s index %d failed, hr=0x%08X\n", wine_dbgstr_w(icoPathW), index, hr);
goto end;
}
- hr = platform_write_icon(stream, index, icoPathW, destFilename, &nativeIdentifier);
+ hr = wmb_dispatch->write_icon(stream, index, icoPathW, destFilename, &nativeIdentifier);
if (FAILED(hr))
WINE_WARN("writing icon failed, error 0x%08X\n", hr);
@@ -1402,7 +1193,7 @@ static HKEY open_menus_reg_key(void)
return NULL;
}
-static DWORD register_menus_entry(const char *unix_file, const char *windows_file)
+DWORD register_menus_entry(const char *unix_file, const char *windows_file)
{
WCHAR *unix_fileW;
WCHAR *windows_fileW;
@@ -1440,233 +1231,9 @@ static DWORD register_menus_entry(const char *unix_file, const char *windows_fil
return ret;
}
-static BOOL write_desktop_entry(const char *unix_link, const char *location, const char *linkname,
- const char *path, const char *args, const char *descr,
- const char *workdir, const char *icon)
-{
- FILE *file;
-
- WINE_TRACE("(%s,%s,%s,%s,%s,%s,%s,%s)\n", wine_dbgstr_a(unix_link), wine_dbgstr_a(location),
- wine_dbgstr_a(linkname), wine_dbgstr_a(path), wine_dbgstr_a(args),
- wine_dbgstr_a(descr), wine_dbgstr_a(workdir), wine_dbgstr_a(icon));
-
- file = fopen(location, "w");
- if (file == NULL)
- return FALSE;
-
- fprintf(file, "[Desktop Entry]\n");
- fprintf(file, "Name=%s\n", linkname);
- fprintf(file, "Exec=env WINEPREFIX=\"%s\" wine %s %s\n",
- wine_get_config_dir(), path, args);
- fprintf(file, "Type=Application\n");
- fprintf(file, "StartupNotify=true\n");
- if (descr && lstrlenA(descr))
- fprintf(file, "Comment=%s\n", descr);
- if (workdir && lstrlenA(workdir))
- fprintf(file, "Path=%s\n", workdir);
- if (icon && lstrlenA(icon))
- fprintf(file, "Icon=%s\n", icon);
-
- fclose(file);
-
- if (unix_link)
- {
- DWORD ret = register_menus_entry(location, unix_link);
- if (ret != ERROR_SUCCESS)
- return FALSE;
- }
-
- return TRUE;
-}
-
-static BOOL write_directory_entry(const char *directory, const char *location)
-{
- FILE *file;
-
- WINE_TRACE("(%s,%s)\n", wine_dbgstr_a(directory), wine_dbgstr_a(location));
-
- file = fopen(location, "w");
- if (file == NULL)
- return FALSE;
-
- fprintf(file, "[Desktop Entry]\n");
- fprintf(file, "Type=Directory\n");
- if (strcmp(directory, "wine") == 0)
- {
- fprintf(file, "Name=Wine\n");
- fprintf(file, "Icon=wine\n");
- }
- else
- {
- fprintf(file, "Name=%s\n", directory);
- fprintf(file, "Icon=folder\n");
- }
-
- fclose(file);
- return TRUE;
-}
-
-static BOOL write_menu_file(const char *unix_link, const char *filename)
-{
- char *tempfilename;
- FILE *tempfile = NULL;
- char *lastEntry;
- char *name = NULL;
- char *menuPath = NULL;
- int i;
- int count = 0;
- BOOL ret = FALSE;
-
- WINE_TRACE("(%s)\n", wine_dbgstr_a(filename));
-
- while (1)
- {
- tempfilename = heap_printf("%s/wine-menu-XXXXXX", xdg_config_dir);
- if (tempfilename)
- {
- int tempfd = mkstemps(tempfilename, 0);
- if (tempfd >= 0)
- {
- tempfile = fdopen(tempfd, "w");
- if (tempfile)
- break;
- close(tempfd);
- goto end;
- }
- else if (errno == EEXIST)
- {
- HeapFree(GetProcessHeap(), 0, tempfilename);
- continue;
- }
- HeapFree(GetProcessHeap(), 0, tempfilename);
- }
- return FALSE;
- }
-
- fprintf(tempfile, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\"\n");
- fprintf(tempfile, "\"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd\">\n");
- fprintf(tempfile, "<Menu>\n");
- fprintf(tempfile, " <Name>Applications</Name>\n");
-
- name = HeapAlloc(GetProcessHeap(), 0, lstrlenA(filename) + 1);
- if (name == NULL) goto end;
- lastEntry = name;
- for (i = 0; filename[i]; i++)
- {
- name[i] = filename[i];
- if (filename[i] == '/')
- {
- char *dir_file_name;
- struct stat st;
- name[i] = 0;
- fprintf(tempfile, " <Menu>\n");
- fprintf(tempfile, " <Name>%s", count ? "" : "wine-");
- write_xml_text(tempfile, name);
- fprintf(tempfile, "</Name>\n");
- fprintf(tempfile, " <Directory>%s", count ? "" : "wine-");
- write_xml_text(tempfile, name);
- fprintf(tempfile, ".directory</Directory>\n");
- dir_file_name = heap_printf("%s/desktop-directories/%s%s.directory",
- xdg_data_dir, count ? "" : "wine-", name);
- if (dir_file_name)
- {
- if (stat(dir_file_name, &st) != 0 && errno == ENOENT)
- write_directory_entry(lastEntry, dir_file_name);
- HeapFree(GetProcessHeap(), 0, dir_file_name);
- }
- name[i] = '-';
- lastEntry = &name[i+1];
- ++count;
- }
- }
- name[i] = 0;
-
- fprintf(tempfile, " <Include>\n");
- fprintf(tempfile, " <Filename>");
- write_xml_text(tempfile, name);
- fprintf(tempfile, "</Filename>\n");
- fprintf(tempfile, " </Include>\n");
- for (i = 0; i < count; i++)
- fprintf(tempfile, " </Menu>\n");
- fprintf(tempfile, "</Menu>\n");
-
- menuPath = heap_printf("%s/%s", xdg_config_dir, name);
- if (menuPath == NULL) goto end;
- strcpy(menuPath + strlen(menuPath) - strlen(".desktop"), ".menu");
- ret = TRUE;
-
-end:
- if (tempfile)
- fclose(tempfile);
- if (ret)
- ret = (rename(tempfilename, menuPath) == 0);
- if (!ret && tempfilename)
- remove(tempfilename);
- HeapFree(GetProcessHeap(), 0, tempfilename);
- if (ret)
- register_menus_entry(menuPath, unix_link);
- HeapFree(GetProcessHeap(), 0, name);
- HeapFree(GetProcessHeap(), 0, menuPath);
- return ret;
-}
-
-static BOOL write_menu_entry(const char *unix_link, const char *link, const char *path, const char *args,
- const char *descr, const char *workdir, const char *icon)
-{
- const char *linkname;
- char *desktopPath = NULL;
- char *desktopDir;
- char *filename = NULL;
- BOOL ret = TRUE;
-
- WINE_TRACE("(%s, %s, %s, %s, %s, %s, %s)\n", wine_dbgstr_a(unix_link), wine_dbgstr_a(link),
- wine_dbgstr_a(path), wine_dbgstr_a(args), wine_dbgstr_a(descr),
- wine_dbgstr_a(workdir), wine_dbgstr_a(icon));
-
- linkname = strrchr(link, '/');
- if (linkname == NULL)
- linkname = link;
- else
- ++linkname;
-
- desktopPath = heap_printf("%s/applications/wine/%s.desktop", xdg_data_dir, link);
- if (!desktopPath)
- {
- WINE_WARN("out of memory creating menu entry\n");
- ret = FALSE;
- goto end;
- }
- desktopDir = strrchr(desktopPath, '/');
- *desktopDir = 0;
- if (!create_directories(desktopPath))
- {
- WINE_WARN("couldn't make parent directories for %s\n", wine_dbgstr_a(desktopPath));
- ret = FALSE;
- goto end;
- }
- *desktopDir = '/';
- if (!write_desktop_entry(unix_link, desktopPath, linkname, path, args, descr, workdir, icon))
- {
- WINE_WARN("couldn't make desktop entry %s\n", wine_dbgstr_a(desktopPath));
- ret = FALSE;
- goto end;
- }
-
- filename = heap_printf("wine/%s.desktop", link);
- if (!filename || !write_menu_file(unix_link, filename))
- {
- WINE_WARN("couldn't make menu file %s\n", wine_dbgstr_a(filename));
- ret = FALSE;
- }
-
-end:
- HeapFree(GetProcessHeap(), 0, desktopPath);
- HeapFree(GetProcessHeap(), 0, filename);
- return ret;
-}
/* This escapes reserved characters in .desktop files' Exec keys. */
-static LPSTR escape(LPCWSTR arg)
+LPSTR escape(LPCWSTR arg)
{
int i, j;
WCHAR *escaped_string;
@@ -1941,7 +1508,7 @@ static HRESULT get_cmdline( IShellLinkW *sl, LPWSTR szPath, DWORD pathSize,
return hr;
}
-static WCHAR* assoc_query(ASSOCSTR assocStr, LPCWSTR name, LPCWSTR extra)
+WCHAR* assoc_query(ASSOCSTR assocStr, LPCWSTR name, LPCWSTR extra)
{
HRESULT hr;
WCHAR *value = NULL;
@@ -1982,243 +1549,6 @@ static char *slashes_to_minuses(const char *string)
return NULL;
}
-static BOOL next_line(FILE *file, char **line, int *size)
-{
- int pos = 0;
- char *cr;
- if (*line == NULL)
- {
- *size = 4096;
- *line = HeapAlloc(GetProcessHeap(), 0, *size);
- }
- while (*line != NULL)
- {
- if (fgets(&(*line)[pos], *size - pos, file) == NULL)
- {
- HeapFree(GetProcessHeap(), 0, *line);
- *line = NULL;
- if (feof(file))
- return TRUE;
- return FALSE;
- }
- pos = strlen(*line);
- cr = strchr(*line, '\n');
- if (cr == NULL)
- {
- char *line2;
- (*size) *= 2;
- line2 = HeapReAlloc(GetProcessHeap(), 0, *line, *size);
- if (line2)
- *line = line2;
- else
- {
- HeapFree(GetProcessHeap(), 0, *line);
- *line = NULL;
- }
- }
- else
- {
- *cr = 0;
- return TRUE;
- }
- }
- return FALSE;
-}
-
-static BOOL add_mimes(const char *xdg_data_dir, struct list *mime_types)
-{
- char *globs_filename = NULL;
- BOOL ret = TRUE;
- globs_filename = heap_printf("%s/mime/globs", xdg_data_dir);
- if (globs_filename)
- {
- FILE *globs_file = fopen(globs_filename, "r");
- if (globs_file) /* doesn't have to exist */
- {
- char *line = NULL;
- int size = 0;
- while (ret && (ret = next_line(globs_file, &line, &size)) && line)
- {
- char *pos;
- struct xdg_mime_type *mime_type_entry = NULL;
- if (line[0] != '#' && (pos = strchr(line, ':')))
- {
- mime_type_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(struct xdg_mime_type));
- if (mime_type_entry)
- {
- *pos = 0;
- mime_type_entry->mimeType = strdupA(line);
- mime_type_entry->glob = strdupA(pos + 1);
- mime_type_entry->lower_glob = strdupA(pos + 1);
- if (mime_type_entry->lower_glob)
- {
- char *l;
- for (l = mime_type_entry->lower_glob; *l; l++)
- *l = tolower(*l);
- }
- if (mime_type_entry->mimeType && mime_type_entry->glob && mime_type_entry->lower_glob)
- list_add_tail(mime_types, &mime_type_entry->entry);
- else
- {
- HeapFree(GetProcessHeap(), 0, mime_type_entry->mimeType);
- HeapFree(GetProcessHeap(), 0, mime_type_entry->glob);
- HeapFree(GetProcessHeap(), 0, mime_type_entry->lower_glob);
- HeapFree(GetProcessHeap(), 0, mime_type_entry);
- ret = FALSE;
- }
- }
- else
- ret = FALSE;
- }
- }
- HeapFree(GetProcessHeap(), 0, line);
- fclose(globs_file);
- }
- HeapFree(GetProcessHeap(), 0, globs_filename);
- }
- else
- ret = FALSE;
- return ret;
-}
-
-static void free_native_mime_types(struct list *native_mime_types)
-{
- struct xdg_mime_type *mime_type_entry, *mime_type_entry2;
-
- 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->lower_glob);
- HeapFree(GetProcessHeap(), 0, mime_type_entry->mimeType);
- HeapFree(GetProcessHeap(), 0, mime_type_entry);
- }
- HeapFree(GetProcessHeap(), 0, native_mime_types);
-}
-
-static BOOL build_native_mime_types(const char *xdg_data_home, struct list **mime_types)
-{
- char *xdg_data_dirs;
- BOOL ret;
-
- *mime_types = NULL;
-
- xdg_data_dirs = getenv("XDG_DATA_DIRS");
- if (xdg_data_dirs == NULL)
- xdg_data_dirs = heap_printf("/usr/local/share/:/usr/share/");
- else
- xdg_data_dirs = strdupA(xdg_data_dirs);
-
- if (xdg_data_dirs)
- {
- *mime_types = HeapAlloc(GetProcessHeap(), 0, sizeof(struct list));
- if (*mime_types)
- {
- const char *begin;
- char *end;
-
- list_init(*mime_types);
- ret = add_mimes(xdg_data_home, *mime_types);
- if (ret)
- {
- for (begin = xdg_data_dirs; (end = strchr(begin, ':')); begin = end + 1)
- {
- *end = '\0';
- ret = add_mimes(begin, *mime_types);
- *end = ':';
- if (!ret)
- break;
- }
- if (ret)
- ret = add_mimes(begin, *mime_types);
- }
- }
- else
- ret = FALSE;
- HeapFree(GetProcessHeap(), 0, xdg_data_dirs);
- }
- else
- ret = FALSE;
- if (!ret && *mime_types)
- {
- free_native_mime_types(*mime_types);
- *mime_types = NULL;
- }
- return ret;
-}
-
-static BOOL match_glob(struct list *native_mime_types, const char *extension,
- int ignoreGlobCase, char **match)
-{
-#ifdef HAVE_FNMATCH
- struct xdg_mime_type *mime_type_entry;
- int matchLength = 0;
-
- *match = NULL;
-
- LIST_FOR_EACH_ENTRY(mime_type_entry, native_mime_types, struct xdg_mime_type, entry)
- {
- const char *glob = ignoreGlobCase ? mime_type_entry->lower_glob : mime_type_entry->glob;
- if (fnmatch(glob, extension, 0) == 0)
- {
- if (*match == NULL || matchLength < strlen(glob))
- {
- *match = mime_type_entry->mimeType;
- matchLength = strlen(glob);
- }
- }
- }
-
- if (*match != NULL)
- {
- *match = strdupA(*match);
- if (*match == NULL)
- return FALSE;
- }
-#else
- *match = NULL;
-#endif
- return TRUE;
-}
-
-static BOOL freedesktop_mime_type_for_extension(struct list *native_mime_types,
- const char *extensionA,
- LPCWSTR extensionW,
- char **mime_type)
-{
- WCHAR *lower_extensionW;
- INT len;
- BOOL ret = match_glob(native_mime_types, extensionA, 0, mime_type);
- if (ret == FALSE || *mime_type != NULL)
- return ret;
- len = strlenW(extensionW);
- lower_extensionW = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
- if (lower_extensionW)
- {
- char *lower_extensionA;
- memcpy(lower_extensionW, extensionW, (len + 1)*sizeof(WCHAR));
- strlwrW(lower_extensionW);
- lower_extensionA = wchars_to_utf8_chars(lower_extensionW);
- if (lower_extensionA)
- {
- ret = match_glob(native_mime_types, lower_extensionA, 1, mime_type);
- HeapFree(GetProcessHeap(), 0, lower_extensionA);
- }
- else
- {
- ret = FALSE;
- WINE_FIXME("out of memory\n");
- }
- HeapFree(GetProcessHeap(), 0, lower_extensionW);
- }
- else
- {
- ret = FALSE;
- WINE_FIXME("out of memory\n");
- }
- return ret;
-}
-
static WCHAR* reg_get_valW(HKEY key, LPCWSTR subkey, LPCWSTR name)
{
DWORD size;
@@ -2372,7 +1702,7 @@ done:
HeapFree(GetProcessHeap(), 0, openWithIconW);
}
-static BOOL cleanup_associations(char *applications_dir)
+static BOOL cleanup_associations(void *user)
{
static const WCHAR openW[] = {'o','p','e','n',0};
HKEY assocKey;
@@ -2410,18 +1740,14 @@ static BOOL cleanup_associations(char *applications_dir)
char *extensionA = wchars_to_utf8_chars(strlwrW(extensionW));
if (extensionA)
{
- char *desktopFile = heap_printf("%s/wine-extension-%s.desktop", applications_dir, &extensionA[1]);
+ wmb_dispatch->remove_file_type_association(user, extensionA, extensionW);
+ RegDeleteKeyW(assocKey, extensionW);
+ hasChanged = TRUE;
- if (desktopFile)
- {
- WINE_TRACE("removing file type association for %s\n", wine_dbgstr_w(extensionW));
- remove(desktopFile);
- HeapFree(GetProcessHeap(), 0, desktopFile);
- }
HeapFree(GetProcessHeap(), 0, extensionA);
- }
- RegDeleteKeyW(assocKey, extensionW);
- hasChanged = TRUE;
+ }
+ else
+ WINE_ERR("out of memory\n");
}
else
i++;
@@ -2442,49 +1768,6 @@ static BOOL cleanup_associations(char *applications_dir)
return hasChanged;
}
-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;
-
- WINE_TRACE("writing MIME type %s, extension=%s, comment=%s\n", wine_dbgstr_a(mime_type),
- wine_dbgstr_a(dot_extension), wine_dbgstr_a(comment));
-
- 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=\"");
- write_xml_text(packageFile, mime_type);
- fprintf(packageFile, "\">\n");
- fprintf(packageFile, " <glob pattern=\"*");
- write_xml_text(packageFile, dot_extension);
- fprintf(packageFile, "\"/>\n");
- if (comment)
- {
- fprintf(packageFile, " <comment>");
- write_xml_text(packageFile, comment);
- fprintf(packageFile, "</comment>\n");
- }
- 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 is_extension_blacklisted(LPCWSTR extension)
{
/* These are managed through external tools like wine.desktop, to evade malware created file type associations */
@@ -2507,42 +1790,10 @@ static const char* get_special_mime_type(LPCWSTR extension)
return NULL;
}
-static BOOL write_freedesktop_association_entry(const char *desktopPath, const char *dot_extension,
- const char *friendlyAppName, const char *mimeType,
- const char *progId, const char *openWithIcon)
-{
- BOOL ret = FALSE;
- FILE *desktop;
-
- WINE_TRACE("writing association for file type %s, friendlyAppName=%s, MIME type %s, progID=%s, icon=%s to file %s\n",
- wine_dbgstr_a(dot_extension), wine_dbgstr_a(friendlyAppName), wine_dbgstr_a(mimeType),
- wine_dbgstr_a(progId), wine_dbgstr_a(openWithIcon), wine_dbgstr_a(desktopPath));
-
- desktop = fopen(desktopPath, "w");
- if (desktop)
- {
- fprintf(desktop, "[Desktop Entry]\n");
- fprintf(desktop, "Type=Application\n");
- fprintf(desktop, "Name=%s\n", friendlyAppName);
- fprintf(desktop, "MimeType=%s;\n", mimeType);
- fprintf(desktop, "Exec=env WINEPREFIX=\"%s\" wine start /ProgIDOpen %s %%f\n", wine_get_config_dir(), progId);
- fprintf(desktop, "NoDisplay=true\n");
- fprintf(desktop, "StartupNotify=true\n");
- if (openWithIcon)
- fprintf(desktop, "Icon=%s\n", openWithIcon);
- ret = TRUE;
- fclose(desktop);
- }
- else
- WINE_ERR("error writing association file %s\n", wine_dbgstr_a(desktopPath));
- return ret;
-}
-
-static BOOL generate_associations(const char *xdg_data_home, const char *packages_dir, const char *applications_dir)
+static BOOL generate_associations(void *user)
{
static const WCHAR openW[] = {'o','p','e','n',0};
struct wine_rb_tree mimeProgidTree;
- struct list *nativeMimeTypes = NULL;
LSTATUS ret = 0;
int i;
BOOL hasChanged = FALSE;
@@ -2552,11 +1803,6 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
WINE_ERR("wine_rb_init failed\n");
return FALSE;
}
- if (!build_native_mime_types(xdg_data_home, &nativeMimeTypes))
- {
- WINE_ERR("could not build native MIME types\n");
- return FALSE;
- }
for (i = 0; ; i++)
{
@@ -2619,7 +1865,7 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
if (contentTypeW)
strlwrW(contentTypeW);
- if (!freedesktop_mime_type_for_extension(nativeMimeTypes, extensionA, extensionW, &mimeTypeA))
+ if (!wmb_dispatch->mime_type_for_extension(user, extensionA, extensionW, &mimeTypeA))
goto end;
if (mimeTypeA == NULL)
@@ -2653,7 +1899,7 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
}
}
- write_freedesktop_mime_type_entry(packages_dir, extensionA, mimeTypeA, friendlyDocNameA);
+ wmb_dispatch->write_mime_type_entry(user, extensionA, mimeTypeA, friendlyDocNameA);
hasChanged = TRUE;
}
else
@@ -2731,15 +1977,10 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
if (has_association_changed(extensionW, mimeTypeA, progIdW, friendlyAppNameA, openWithIconA))
{
- char *desktopPath = heap_printf("%s/wine-extension-%s.desktop", applications_dir, &extensionA[1]);
- if (desktopPath)
+ if (wmb_dispatch->write_association_entry(user, extensionA, friendlyAppNameA, friendlyDocNameA, mimeTypeA, progIdA, openWithIconA))
{
- if (write_freedesktop_association_entry(desktopPath, extensionA, friendlyAppNameA, mimeTypeA, progIdA, openWithIconA))
- {
- hasChanged = TRUE;
- update_association(extensionW, mimeTypeA, progIdW, friendlyAppNameA, openWithIconA);
- }
- HeapFree(GetProcessHeap(), 0, desktopPath);
+ hasChanged = TRUE;
+ update_association(extensionW, mimeTypeA, progIdW, friendlyAppNameA, openWithIconA);
}
}
@@ -2765,7 +2006,6 @@ static BOOL generate_associations(const char *xdg_data_home, const char *package
}
wine_rb_destroy(&mimeProgidTree, winemenubuilder_rb_destroy, NULL);
- free_native_mime_types(nativeMimeTypes);
return hasChanged;
}
@@ -2810,6 +2050,7 @@ static BOOL InvokeShellLinker( IShellLinkW *sl, LPCWSTR link, BOOL bWait )
HANDLE hsem = NULL;
char *unix_link = NULL;
char *start_path = NULL;
+ const char *lastEntry;
if ( !link )
{
@@ -2949,33 +2190,27 @@ static BOOL InvokeShellLinker( IShellLinkW *sl, LPCWSTR link, BOOL bWait )
goto cleanup;
}
+ lastEntry = strrchr(link_name, '/');
+ if (lastEntry == NULL)
+ lastEntry = link_name;
+ else
+ ++lastEntry;
+
if (in_desktop_dir(csidl))
{
- char *location;
- const char *lastEntry;
- lastEntry = strrchr(link_name, '/');
- if (lastEntry == NULL)
- lastEntry = link_name;
- else
- ++lastEntry;
- location = heap_printf("%s/%s.desktop", xdg_desktop_dir, lastEntry);
- if (location)
+ if (csidl == CSIDL_COMMON_DESKTOPDIRECTORY)
{
- if (csidl == CSIDL_COMMON_DESKTOPDIRECTORY)
+ char *link_arg = escape_unix_link_arg(unix_link);
+ if (link_arg)
{
- char *link_arg = escape_unix_link_arg(unix_link);
- if (link_arg)
- {
- r = !write_desktop_entry(unix_link, location, lastEntry,
+ r = wmb_dispatch->build_desktop_link(unix_link, link_name, lastEntry,
start_path, link_arg, description, work_dir, icon_name);
- HeapFree(GetProcessHeap(), 0, link_arg);
- }
+ HeapFree(GetProcessHeap(), 0, link_arg);
}
- else
- r = !write_desktop_entry(NULL, location, lastEntry, escaped_path, escaped_args, description, work_dir, icon_name);
- if (r == 0)
- chmod(location, 0755);
- HeapFree(GetProcessHeap(), 0, location);
+ }
+ else
+ {
+ r = wmb_dispatch->build_desktop_link(NULL, link_name, lastEntry, escaped_path, escaped_args, description, work_dir, icon_name);
}
}
else
@@ -2983,7 +2218,7 @@ static BOOL InvokeShellLinker( IShellLinkW *sl, LPCWSTR link, BOOL bWait )
char *link_arg = escape_unix_link_arg(unix_link);
if (link_arg)
{
- r = !write_menu_entry(unix_link, link_name, start_path, link_arg, description, work_dir, icon_name);
+ r = wmb_dispatch->build_menu_link(unix_link, link_name, lastEntry, start_path, link_arg, description, work_dir, icon_name);
HeapFree(GetProcessHeap(), 0, link_arg);
}
}
@@ -3024,6 +2259,7 @@ static BOOL InvokeShellLinkerForURL( IUniformResourceLocatorW *url, LPCWSTR link
PROPVARIANT pv[2];
char *start_path = NULL;
BOOL has_icon = FALSE;
+ char *lastEntry;
if ( !link )
{
@@ -3121,26 +2357,15 @@ static BOOL InvokeShellLinkerForURL( IUniformResourceLocatorW *url, LPCWSTR link
WINE_ERR("failed wait for semaphore\n");
goto cleanup;
}
+ lastEntry = strrchr(link_name, '/');
+ if (lastEntry == NULL)
+ lastEntry = link_name;
+ else
+ ++lastEntry;
if (in_desktop_dir(csidl))
- {
- char *location;
- const char *lastEntry;
- lastEntry = strrchr(link_name, '/');
- if (lastEntry == NULL)
- lastEntry = link_name;
- else
- ++lastEntry;
- location = heap_printf("%s/%s.desktop", xdg_desktop_dir, lastEntry);
- if (location)
- {
- r = !write_desktop_entry(NULL, location, lastEntry, start_path, escaped_urlPath, NULL, NULL, icon_name);
- if (r == 0)
- chmod(location, 0755);
- HeapFree(GetProcessHeap(), 0, location);
- }
- }
+ r = wmb_dispatch->build_desktop_link(NULL, link_name, lastEntry, start_path, escaped_urlPath, NULL, NULL, icon_name);
else
- r = !write_menu_entry(unix_link, link_name, start_path, escaped_urlPath, NULL, NULL, icon_name);
+ r = wmb_dispatch->build_menu_link(unix_link, link_name, lastEntry, start_path, escaped_urlPath, NULL, NULL, icon_name);
ret = (r == 0);
ReleaseSemaphore(hSem, 1, NULL);
@@ -3322,10 +2547,8 @@ static BOOL Process_URL( LPCWSTR urlname, BOOL bWait )
static void RefreshFileTypeAssociations(void)
{
HANDLE hSem = NULL;
- char *mime_dir = NULL;
- char *packages_dir = NULL;
- char *applications_dir = NULL;
BOOL hasChanged;
+ void *user;
hSem = CreateSemaphoreA( NULL, 1, 1, "winemenubuilder_semaphore");
if( WAIT_OBJECT_0 != MsgWaitForMultipleObjects( 1, &hSem, FALSE, INFINITE, QS_ALLINPUT ) )
@@ -3336,45 +2559,15 @@ static void RefreshFileTypeAssociations(void)
goto end;
}
- mime_dir = heap_printf("%s/mime", xdg_data_dir);
- if (mime_dir == NULL)
- {
- WINE_ERR("out of memory\n");
- goto end;
- }
- create_directories(mime_dir);
-
- packages_dir = heap_printf("%s/packages", mime_dir);
- if (packages_dir == NULL)
- {
- WINE_ERR("out of memory\n");
- goto end;
- }
- create_directories(packages_dir);
+ user = wmb_dispatch->refresh_file_type_associations_init();
- applications_dir = heap_printf("%s/applications", xdg_data_dir);
- if (applications_dir == NULL)
- {
- WINE_ERR("out of memory\n");
+ if (!user)
goto end;
- }
- create_directories(applications_dir);
- hasChanged = generate_associations(xdg_data_dir, packages_dir, applications_dir);
- hasChanged |= cleanup_associations(applications_dir);
- if (hasChanged)
- {
- const char *argv[3];
-
- argv[0] = "update-mime-database";
- argv[1] = mime_dir;
- argv[2] = NULL;
- spawnvp( _P_DETACH, argv[0], argv );
+ hasChanged = generate_associations(user);
+ hasChanged |= cleanup_associations(user);
- argv[0] = "update-desktop-database";
- argv[1] = applications_dir;
- spawnvp( _P_DETACH, argv[0], argv );
- }
+ wmb_dispatch->refresh_file_type_associations_cleanup(user, hasChanged);
end:
if (hSem)
@@ -3382,9 +2575,6 @@ end:
ReleaseSemaphore(hSem, 1, NULL);
CloseHandle(hSem);
}
- HeapFree(GetProcessHeap(), 0, mime_dir);
- HeapFree(GetProcessHeap(), 0, packages_dir);
- HeapFree(GetProcessHeap(), 0, applications_dir);
}
static void cleanup_menus(void)
@@ -3590,45 +2780,46 @@ static WCHAR *next_token( LPWSTR *p )
return token;
}
-static BOOL init_xdg(void)
+
+static BOOL dispatch_init(void)
{
- WCHAR shellDesktopPath[MAX_PATH];
- HRESULT hr = SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, shellDesktopPath);
- if (SUCCEEDED(hr))
- xdg_desktop_dir = wine_get_unix_file_name(shellDesktopPath);
- if (xdg_desktop_dir == NULL)
+ extern struct winemenubuilder_dispatch xdg_dispatch;
+ const char *dispatch = NULL;
+ unsigned char buffer[256];
+ HKEY hkey;
+
+ if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\MenuBuilder", 0, KEY_READ, &hkey) == ERROR_SUCCESS)
{
- WINE_ERR("error looking up the desktop directory\n");
- return FALSE;
- }
+ DWORD type, count = sizeof(buffer);
- if (getenv("XDG_CONFIG_HOME"))
- xdg_config_dir = heap_printf("%s/menus/applications-merged", getenv("XDG_CONFIG_HOME"));
+ buffer[0] = 0;
+
+ RegQueryValueExA(hkey, "Dispatch", 0, &type, buffer, &count);
+ if (buffer[0] && type == REG_SZ)
+ {
+ dispatch = (char*)buffer;
+ WINE_TRACE("Dispatch set to %s from registry\n", dispatch);
+ }
+ else
+ WINE_TRACE("No Dispatch key in registry or type != string\n");
+ }
else
- xdg_config_dir = heap_printf("%s/.config/menus/applications-merged", getenv("HOME"));
- if (xdg_config_dir)
+ WINE_TRACE("No MenuBuilder key in registry\n");
+
+ if (dispatch)
{
- create_directories(xdg_config_dir);
- if (getenv("XDG_DATA_HOME"))
- xdg_data_dir = strdupA(getenv("XDG_DATA_HOME"));
- else
- xdg_data_dir = heap_printf("%s/.local/share", getenv("HOME"));
- if (xdg_data_dir)
+ if (strcmp(dispatch, "xdg") == 0)
{
- char *buffer;
- create_directories(xdg_data_dir);
- buffer = heap_printf("%s/desktop-directories", xdg_data_dir);
- if (buffer)
- {
- mkdir(buffer, 0777);
- HeapFree(GetProcessHeap(), 0, buffer);
- }
+ wmb_dispatch = &xdg_dispatch;
return TRUE;
}
- HeapFree(GetProcessHeap(), 0, xdg_config_dir);
+
+ WINE_WARN("Unknown Wine MenuBuilder Dispatch \"%s\"\n", dispatch);
}
- WINE_ERR("out of memory\n");
- return FALSE;
+
+ wmb_dispatch = &xdg_dispatch;
+ WINE_TRACE("Dispatch set to xdg by default\n");
+ return TRUE;
}
/***********************************************************************
@@ -3649,7 +2840,10 @@ int PASCAL wWinMain (HINSTANCE hInstance, HINSTANCE prev, LPWSTR cmdline, int sh
HRESULT hr;
int ret = 0;
- if (!init_xdg())
+ if (!dispatch_init())
+ return 1;
+
+ if (!wmb_dispatch->init())
return 1;
hr = CoInitialize(NULL);
diff --git a/programs/winemenubuilder/winemenubuilder.h b/programs/winemenubuilder/winemenubuilder.h
new file mode 100644
index 0000000..939b366
--- /dev/null
+++ b/programs/winemenubuilder/winemenubuilder.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2011 - 2012 Per Johansson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+typedef struct
+{
+ BYTE bWidth;
+ BYTE bHeight;
+ BYTE bColorCount;
+ BYTE bReserved;
+ WORD wPlanes;
+ WORD wBitCount;
+ DWORD dwBytesInRes;
+ DWORD dwImageOffset;
+} ICONDIRENTRY;
+
+typedef struct
+{
+ WORD idReserved;
+ WORD idType;
+ WORD idCount;
+} ICONDIR;
+
+char* heap_printf(const char *format, ...);
+WCHAR* assoc_query(ASSOCSTR assocStr, LPCWSTR name, LPCWSTR extra);
+BOOL create_directories(char *directory);
+DWORD register_menus_entry(const char *unix_file, const char *windows_file);
+HRESULT read_ico_direntries(IStream *icoStream, ICONDIRENTRY **ppIconDirEntries, int *numEntries);
+
+char* wchars_to_utf8_chars(LPCWSTR string);
+
+HRESULT convert_to_native_icon(IStream *icoFile, int *indeces, int numIndeces,
+ const CLSID *outputFormat, const char *outputFileName, LPCWSTR commentW);
+
+char *extract_icon(LPCWSTR icoPathW, int index, const char *destFilename, BOOL bWait);
+
+LPSTR escape(LPCWSTR arg);
+WCHAR* utf8_chars_to_wchars(LPCSTR string);
+
+struct winemenubuilder_dispatch
+{
+ BOOL (*init)(void);
+
+ int (*build_desktop_link)(const char *unix_link, const char *link, const char *link_name, const char *path,
+ const char *args, const char *descr, const char *workdir, char *icon);
+ int (*build_menu_link)(const char *unix_link, const char *link, const char *link_name, const char *path,
+ const char *args, const char *descr, const char *workdir, char *icon);
+
+ HRESULT (*write_icon)(IStream *icoStream, int exeIndex, LPCWSTR icoPathW,
+ const char *destFilename, char **nativeIdentifier);
+
+
+ void *(*refresh_file_type_associations_init)(void);
+ BOOL (*mime_type_for_extension)(void *user, const char *extensionA, LPCWSTR extensionW, char **mime_type);
+ BOOL (*write_mime_type_entry)(void *user, const char *extensionA, const char *mimeTypeA, const char *friendlyDocNameA);
+ BOOL (*write_association_entry)(void *user, const char *extensionA, const char *friendlyAppNameA,
+ const char *friendlyDocNameA, const char *mimeTypeA, const char *progIdA,
+ char *appIconA);
+ BOOL (*remove_file_type_association)(void *user, const char *extensionA, LPCWSTR extensionW);
+ void (*refresh_file_type_associations_cleanup)(void *user, BOOL hasChanged);
+};
diff --git a/programs/winemenubuilder/xdg.c b/programs/winemenubuilder/xdg.c
new file mode 100644
index 0000000..b9490d0
--- /dev/null
+++ b/programs/winemenubuilder/xdg.c
@@ -0,0 +1,985 @@
+/*
+ * XDG implementation for winemenubuilder.
+ *
+ * Copyright 1997 Marcus Meissner
+ * Copyright 1998 Juergen Schmied
+ * Copyright 2003 Mike McCormack for CodeWeavers
+ * Copyright 2004 Dmitry Timoshkov
+ * Copyright 2005 Bill Medland
+ * Copyright 2008 Damjan Jovanovic
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <stdarg.h>
+#ifdef HAVE_FNMATCH_H
+#include <fnmatch.h>
+#endif
+#include <dirent.h>
+#include <limits.h>
+
+
+#define COBJMACROS
+
+#include <windows.h>
+#include <shlobj.h>
+#include <objidl.h>
+#include <shlguid.h>
+#include <appmgmt.h>
+#include <tlhelp32.h>
+#include <intshcut.h>
+#include <shlwapi.h>
+#include <wincodec.h>
+
+#include "wine/unicode.h"
+#include "wine/debug.h"
+#include "wine/library.h"
+#include "wine/list.h"
+#include "wine/rbtree.h"
+
+
+#include "winemenubuilder.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(menubuilder);
+
+struct xdg_file_type_user_data
+{
+ char *mime_dir;
+ char *packages_dir;
+ char *applications_dir;
+
+ struct list *native_mime_types;
+};
+
+struct xdg_mime_type
+{
+ char *mimeType;
+ char *glob;
+ char *lower_glob;
+ struct list entry;
+};
+
+struct rb_string_entry
+{
+ char *string;
+ struct wine_rb_entry entry;
+};
+
+static char *xdg_config_dir;
+static char *xdg_data_dir;
+static char *xdg_desktop_dir;
+
+static char *strdupA( const char *str )
+{
+ char *ret;
+
+ if (!str) return NULL;
+ if ((ret = HeapAlloc( GetProcessHeap(), 0, strlen(str) + 1 ))) strcpy( ret, str );
+ return ret;
+}
+
+static BOOL next_line(FILE *file, char **line, int *size)
+{
+ int pos = 0;
+ char *cr;
+ if (*line == NULL)
+ {
+ *size = 4096;
+ *line = HeapAlloc(GetProcessHeap(), 0, *size);
+ }
+ while (*line != NULL)
+ {
+ if (fgets(&(*line)[pos], *size - pos, file) == NULL)
+ {
+ HeapFree(GetProcessHeap(), 0, *line);
+ *line = NULL;
+ if (feof(file))
+ return TRUE;
+ return FALSE;
+ }
+ pos = strlen(*line);
+ cr = strchr(*line, '\n');
+ if (cr == NULL)
+ {
+ char *line2;
+ (*size) *= 2;
+ line2 = HeapReAlloc(GetProcessHeap(), 0, *line, *size);
+ if (line2)
+ *line = line2;
+ else
+ {
+ HeapFree(GetProcessHeap(), 0, *line);
+ *line = NULL;
+ }
+ }
+ else
+ {
+ *cr = 0;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static unsigned short crc16(const char* string)
+{
+ unsigned short crc = 0;
+ int i, j, xor_poly;
+
+ for (i = 0; string[i] != 0; i++)
+ {
+ char c = string[i];
+ for (j = 0; j < 8; c >>= 1, j++)
+ {
+ xor_poly = (c ^ crc) & 1;
+ crc >>= 1;
+ if (xor_poly)
+ crc ^= 0xa001;
+ }
+ }
+ return crc;
+}
+
+static void write_xml_text(FILE *file, const char *text)
+{
+ int i;
+ for (i = 0; text[i]; i++)
+ {
+ if (text[i] == '&')
+ fputs("&", file);
+ else if (text[i] == '<')
+ fputs("<", file);
+ else if (text[i] == '>')
+ fputs(">", file);
+ else if (text[i] == '\'')
+ fputs("'", file);
+ else if (text[i] == '"')
+ fputs(""", file);
+ else
+ fputc(text[i], file);
+ }
+}
+
+static void refresh_icon_cache(const char *iconsDir)
+{
+ /* The icon theme spec only requires the mtime on the "toplevel"
+ * directory (whatever that is) to be changed for a refresh,
+ * but on GNOME you have to create a file in that directory
+ * instead. Creating a file also works on KDE, Xfce and LXDE.
+ */
+ char *filename = heap_printf("%s/.wine-refresh-XXXXXX", iconsDir);
+ if (filename != NULL)
+ {
+ int fd = mkstemps(filename, 0);
+ if (fd >= 0)
+ {
+ close(fd);
+ unlink(filename);
+ }
+ HeapFree(GetProcessHeap(), 0, filename);
+ }
+}
+
+HRESULT xdg_write_icon(IStream *icoStream, int exeIndex, LPCWSTR icoPathW,
+ const char *destFilename, char **nativeIdentifier)
+{
+ ICONDIRENTRY *iconDirEntries = NULL;
+ int numEntries;
+ int i;
+ char *icoPathA = NULL;
+ char *iconsDir = NULL;
+ unsigned short crc;
+ char *p, *q;
+ HRESULT hr = S_OK;
+ LARGE_INTEGER zero;
+
+ hr = read_ico_direntries(icoStream, &iconDirEntries, &numEntries);
+ if (FAILED(hr))
+ goto end;
+
+ icoPathA = wchars_to_utf8_chars(icoPathW);
+ if (icoPathA == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto end;
+ }
+ crc = crc16(icoPathA);
+ p = strrchr(icoPathA, '\\');
+ if (p == NULL)
+ p = icoPathA;
+ else
+ {
+ *p = 0;
+ p++;
+ }
+ q = strrchr(p, '.');
+ if (q)
+ *q = 0;
+ if (destFilename)
+ *nativeIdentifier = heap_printf("%s", destFilename);
+ else
+ *nativeIdentifier = heap_printf("%04X_%s.%d", crc, p, exeIndex);
+ if (*nativeIdentifier == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto end;
+ }
+ iconsDir = heap_printf("%s/icons/hicolor", xdg_data_dir);
+ if (iconsDir == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto end;
+ }
+
+ for (i = 0; i < numEntries; i++)
+ {
+ int bestIndex = i;
+ int j;
+ BOOLEAN duplicate = FALSE;
+ int w, h;
+ char *iconDir = NULL;
+ char *pngPath = NULL;
+
+ WINE_TRACE("[%d]: %d x %d @ %d\n", i, iconDirEntries[i].bWidth,
+ iconDirEntries[i].bHeight, iconDirEntries[i].wBitCount);
+
+ for (j = 0; j < i; j++)
+ {
+ if (iconDirEntries[j].bWidth == iconDirEntries[i].bWidth &&
+ iconDirEntries[j].bHeight == iconDirEntries[i].bHeight)
+ {
+ duplicate = TRUE;
+ break;
+ }
+ }
+ if (duplicate)
+ continue;
+ for (j = i + 1; j < numEntries; j++)
+ {
+ if (iconDirEntries[j].bWidth == iconDirEntries[i].bWidth &&
+ iconDirEntries[j].bHeight == iconDirEntries[i].bHeight &&
+ iconDirEntries[j].wBitCount >= iconDirEntries[bestIndex].wBitCount)
+ {
+ bestIndex = j;
+ }
+ }
+ WINE_TRACE("Selected: %d\n", bestIndex);
+
+ w = iconDirEntries[bestIndex].bWidth ? iconDirEntries[bestIndex].bWidth : 256;
+ h = iconDirEntries[bestIndex].bHeight ? iconDirEntries[bestIndex].bHeight : 256;
+ iconDir = heap_printf("%s/%dx%d/apps", iconsDir, w, h);
+ if (iconDir == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto endloop;
+ }
+ create_directories(iconDir);
+ pngPath = heap_printf("%s/%s.png", iconDir, *nativeIdentifier);
+ if (pngPath == NULL)
+ {
+ hr = E_OUTOFMEMORY;
+ goto endloop;
+ }
+ zero.QuadPart = 0;
+ hr = IStream_Seek(icoStream, zero, STREAM_SEEK_SET, NULL);
+ if (FAILED(hr))
+ goto endloop;
+ hr = convert_to_native_icon(icoStream, &bestIndex, 1, &CLSID_WICPngEncoder,
+ pngPath, icoPathW);
+
+ endloop:
+ HeapFree(GetProcessHeap(), 0, iconDir);
+ HeapFree(GetProcessHeap(), 0, pngPath);
+ }
+ refresh_icon_cache(iconsDir);
+
+end:
+ HeapFree(GetProcessHeap(), 0, iconDirEntries);
+ HeapFree(GetProcessHeap(), 0, icoPathA);
+ HeapFree(GetProcessHeap(), 0, iconsDir);
+ return hr;
+}
+
+static BOOL write_desktop_entry(const char *unix_link, const char *location, const char *linkname,
+ const char *path, const char *args, const char *descr,
+ const char *workdir, const char *icon)
+{
+ FILE *file;
+
+ WINE_TRACE("(%s,%s,%s,%s,%s,%s,%s,%s)\n", wine_dbgstr_a(unix_link), wine_dbgstr_a(location),
+ wine_dbgstr_a(linkname), wine_dbgstr_a(path), wine_dbgstr_a(args),
+ wine_dbgstr_a(descr), wine_dbgstr_a(workdir), wine_dbgstr_a(icon));
+
+ file = fopen(location, "w");
+ if (file == NULL)
+ return FALSE;
+
+ fprintf(file, "[Desktop Entry]\n");
+ fprintf(file, "Name=%s\n", linkname);
+ fprintf(file, "Exec=env WINEPREFIX=\"%s\" wine %s %s\n",
+ wine_get_config_dir(), path, args);
+ fprintf(file, "Type=Application\n");
+ fprintf(file, "StartupNotify=true\n");
+ if (descr && lstrlenA(descr))
+ fprintf(file, "Comment=%s\n", descr);
+ if (workdir && lstrlenA(workdir))
+ fprintf(file, "Path=%s\n", workdir);
+ if (icon && lstrlenA(icon))
+ fprintf(file, "Icon=%s\n", icon);
+
+ fclose(file);
+
+ if (unix_link)
+ {
+ DWORD ret = register_menus_entry(location, unix_link);
+ if (ret != ERROR_SUCCESS)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL write_directory_entry(const char *directory, const char *location)
+{
+ FILE *file;
+
+ WINE_TRACE("(%s,%s)\n", wine_dbgstr_a(directory), wine_dbgstr_a(location));
+
+ file = fopen(location, "w");
+ if (file == NULL)
+ return FALSE;
+
+ fprintf(file, "[Desktop Entry]\n");
+ fprintf(file, "Type=Directory\n");
+ if (strcmp(directory, "wine") == 0)
+ {
+ fprintf(file, "Name=Wine\n");
+ fprintf(file, "Icon=wine\n");
+ }
+ else
+ {
+ fprintf(file, "Name=%s\n", directory);
+ fprintf(file, "Icon=folder\n");
+ }
+
+ fclose(file);
+ return TRUE;
+}
+
+static BOOL write_menu_file(const char *unix_link, const char *filename)
+{
+ char *tempfilename;
+ FILE *tempfile = NULL;
+ char *lastEntry;
+ char *name = NULL;
+ char *menuPath = NULL;
+ int i;
+ int count = 0;
+ BOOL ret = FALSE;
+
+ WINE_TRACE("(%s)\n", wine_dbgstr_a(filename));
+
+ while (1)
+ {
+ tempfilename = heap_printf("%s/wine-menu-XXXXXX", xdg_config_dir);
+ if (tempfilename)
+ {
+ int tempfd = mkstemps(tempfilename, 0);
+ if (tempfd >= 0)
+ {
+ tempfile = fdopen(tempfd, "w");
+ if (tempfile)
+ break;
+ close(tempfd);
+ goto end;
+ }
+ else if (errno == EEXIST)
+ {
+ HeapFree(GetProcessHeap(), 0, tempfilename);
+ continue;
+ }
+ HeapFree(GetProcessHeap(), 0, tempfilename);
+ }
+ return FALSE;
+ }
+
+ fprintf(tempfile, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\"\n");
+ fprintf(tempfile, "\"http://www.freedesktop.org/standards/menu-spec/menu-1.0.dtd\">\n");
+ fprintf(tempfile, "<Menu>\n");
+ fprintf(tempfile, " <Name>Applications</Name>\n");
+
+ name = HeapAlloc(GetProcessHeap(), 0, lstrlenA(filename) + 1);
+ if (name == NULL) goto end;
+ lastEntry = name;
+ for (i = 0; filename[i]; i++)
+ {
+ name[i] = filename[i];
+ if (filename[i] == '/')
+ {
+ char *dir_file_name;
+ struct stat st;
+ name[i] = 0;
+ fprintf(tempfile, " <Menu>\n");
+ fprintf(tempfile, " <Name>%s", count ? "" : "wine-");
+ write_xml_text(tempfile, name);
+ fprintf(tempfile, "</Name>\n");
+ fprintf(tempfile, " <Directory>%s", count ? "" : "wine-");
+ write_xml_text(tempfile, name);
+ fprintf(tempfile, ".directory</Directory>\n");
+ dir_file_name = heap_printf("%s/desktop-directories/%s%s.directory",
+ xdg_data_dir, count ? "" : "wine-", name);
+ if (dir_file_name)
+ {
+ if (stat(dir_file_name, &st) != 0 && errno == ENOENT)
+ write_directory_entry(lastEntry, dir_file_name);
+ HeapFree(GetProcessHeap(), 0, dir_file_name);
+ }
+ name[i] = '-';
+ lastEntry = &name[i+1];
+ ++count;
+ }
+ }
+ name[i] = 0;
+
+ fprintf(tempfile, " <Include>\n");
+ fprintf(tempfile, " <Filename>");
+ write_xml_text(tempfile, name);
+ fprintf(tempfile, "</Filename>\n");
+ fprintf(tempfile, " </Include>\n");
+ for (i = 0; i < count; i++)
+ fprintf(tempfile, " </Menu>\n");
+ fprintf(tempfile, "</Menu>\n");
+
+ menuPath = heap_printf("%s/%s", xdg_config_dir, name);
+ if (menuPath == NULL) goto end;
+ strcpy(menuPath + strlen(menuPath) - strlen(".desktop"), ".menu");
+ ret = TRUE;
+
+end:
+ if (tempfile)
+ fclose(tempfile);
+ if (ret)
+ ret = (rename(tempfilename, menuPath) == 0);
+ if (!ret && tempfilename)
+ remove(tempfilename);
+ HeapFree(GetProcessHeap(), 0, tempfilename);
+ if (ret)
+ register_menus_entry(menuPath, unix_link);
+ HeapFree(GetProcessHeap(), 0, name);
+ HeapFree(GetProcessHeap(), 0, menuPath);
+ return ret;
+}
+
+static BOOL write_menu_entry(const char *unix_link, const char *link, const char *link_name, const char *path,
+ const char *args, const char *descr, const char *workdir, const char *icon)
+{
+ char *desktopPath = NULL;
+ char *desktopDir;
+ char *filename = NULL;
+ BOOL ret = TRUE;
+
+ WINE_TRACE("(%s, %s, %s, %s, %s, %s, %s)\n", wine_dbgstr_a(unix_link), wine_dbgstr_a(link),
+ wine_dbgstr_a(path), wine_dbgstr_a(args), wine_dbgstr_a(descr),
+ wine_dbgstr_a(workdir), wine_dbgstr_a(icon));
+
+ desktopPath = heap_printf("%s/applications/wine/%s.desktop", xdg_data_dir, link);
+ if (!desktopPath)
+ {
+ WINE_WARN("out of memory creating menu entry\n");
+ ret = FALSE;
+ goto end;
+ }
+ desktopDir = strrchr(desktopPath, '/');
+ *desktopDir = 0;
+ if (!create_directories(desktopPath))
+ {
+ WINE_WARN("couldn't make parent directories for %s\n", wine_dbgstr_a(desktopPath));
+ ret = FALSE;
+ goto end;
+ }
+ *desktopDir = '/';
+ if (!write_desktop_entry(unix_link, desktopPath, link_name, path, args, descr, workdir, icon))
+ {
+ WINE_WARN("couldn't make desktop entry %s\n", wine_dbgstr_a(desktopPath));
+ ret = FALSE;
+ goto end;
+ }
+
+ filename = heap_printf("wine/%s.desktop", link);
+ if (!filename || !write_menu_file(unix_link, filename))
+ {
+ WINE_WARN("couldn't make menu file %s\n", wine_dbgstr_a(filename));
+ ret = FALSE;
+ }
+
+end:
+ HeapFree(GetProcessHeap(), 0, desktopPath);
+ HeapFree(GetProcessHeap(), 0, filename);
+ return ret;
+}
+
+static BOOL add_mimes(const char *xdg_data_dir, struct list *mime_types)
+{
+ char *globs_filename = NULL;
+ BOOL ret = TRUE;
+ globs_filename = heap_printf("%s/mime/globs", xdg_data_dir);
+ if (globs_filename)
+ {
+ FILE *globs_file = fopen(globs_filename, "r");
+ if (globs_file) /* doesn't have to exist */
+ {
+ char *line = NULL;
+ int size = 0;
+ while (ret && (ret = next_line(globs_file, &line, &size)) && line)
+ {
+ char *pos;
+ struct xdg_mime_type *mime_type_entry = NULL;
+ if (line[0] != '#' && (pos = strchr(line, ':')))
+ {
+ mime_type_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(struct xdg_mime_type));
+ if (mime_type_entry)
+ {
+ *pos = 0;
+ mime_type_entry->mimeType = strdupA(line);
+ mime_type_entry->glob = strdupA(pos + 1);
+ mime_type_entry->lower_glob = strdupA(pos + 1);
+ if (mime_type_entry->lower_glob)
+ {
+ char *l;
+ for (l = mime_type_entry->lower_glob; *l; l++)
+ *l = tolower(*l);
+ }
+ if (mime_type_entry->mimeType && mime_type_entry->glob && mime_type_entry->lower_glob)
+ list_add_tail(mime_types, &mime_type_entry->entry);
+ else
+ {
+ HeapFree(GetProcessHeap(), 0, mime_type_entry->mimeType);
+ HeapFree(GetProcessHeap(), 0, mime_type_entry->glob);
+ HeapFree(GetProcessHeap(), 0, mime_type_entry->lower_glob);
+ HeapFree(GetProcessHeap(), 0, mime_type_entry);
+ ret = FALSE;
+ }
+ }
+ else
+ ret = FALSE;
+ }
+ }
+ HeapFree(GetProcessHeap(), 0, line);
+ fclose(globs_file);
+ }
+ HeapFree(GetProcessHeap(), 0, globs_filename);
+ }
+ else
+ ret = FALSE;
+ return ret;
+}
+
+static void free_native_mime_types(struct list *native_mime_types)
+{
+ struct xdg_mime_type *mime_type_entry, *mime_type_entry2;
+
+ 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->lower_glob);
+ HeapFree(GetProcessHeap(), 0, mime_type_entry->mimeType);
+ HeapFree(GetProcessHeap(), 0, mime_type_entry);
+ }
+ HeapFree(GetProcessHeap(), 0, native_mime_types);
+}
+
+static BOOL build_native_mime_types(const char *xdg_data_home, struct list **mime_types)
+{
+ char *xdg_data_dirs;
+ BOOL ret;
+
+ *mime_types = NULL;
+
+ xdg_data_dirs = getenv("XDG_DATA_DIRS");
+ if (xdg_data_dirs == NULL)
+ xdg_data_dirs = heap_printf("/usr/local/share/:/usr/share/");
+ else
+ xdg_data_dirs = strdupA(xdg_data_dirs);
+
+ if (xdg_data_dirs)
+ {
+ *mime_types = HeapAlloc(GetProcessHeap(), 0, sizeof(struct list));
+ if (*mime_types)
+ {
+ const char *begin;
+ char *end;
+
+ list_init(*mime_types);
+ ret = add_mimes(xdg_data_home, *mime_types);
+ if (ret)
+ {
+ for (begin = xdg_data_dirs; (end = strchr(begin, ':')); begin = end + 1)
+ {
+ *end = '\0';
+ ret = add_mimes(begin, *mime_types);
+ *end = ':';
+ if (!ret)
+ break;
+ }
+ if (ret)
+ ret = add_mimes(begin, *mime_types);
+ }
+ }
+ else
+ ret = FALSE;
+ HeapFree(GetProcessHeap(), 0, xdg_data_dirs);
+ }
+ else
+ ret = FALSE;
+ if (!ret && *mime_types)
+ {
+ free_native_mime_types(*mime_types);
+ *mime_types = NULL;
+ }
+ return ret;
+}
+
+static BOOL match_glob(struct list *native_mime_types, const char *extension,
+ int ignoreGlobCase, char **match)
+{
+#ifdef HAVE_FNMATCH
+ struct xdg_mime_type *mime_type_entry;
+ int matchLength = 0;
+
+ *match = NULL;
+
+ LIST_FOR_EACH_ENTRY(mime_type_entry, native_mime_types, struct xdg_mime_type, entry)
+ {
+ const char *glob = ignoreGlobCase ? mime_type_entry->lower_glob : mime_type_entry->glob;
+ if (fnmatch(glob, extension, 0) == 0)
+ {
+ if (*match == NULL || matchLength < strlen(glob))
+ {
+ *match = mime_type_entry->mimeType;
+ matchLength = strlen(glob);
+ }
+ }
+ }
+
+ if (*match != NULL)
+ {
+ *match = strdupA(*match);
+ if (*match == NULL)
+ return FALSE;
+ }
+#else
+ *match = NULL;
+#endif
+ return TRUE;
+}
+
+static BOOL freedesktop_mime_type_for_extension(void *user,
+ const char *extensionA,
+ LPCWSTR extensionW,
+ char **mime_type)
+{
+ struct xdg_file_type_user_data *ud = user;
+ WCHAR *lower_extensionW;
+ INT len;
+ BOOL ret = match_glob(ud->native_mime_types, extensionA, 0, mime_type);
+ if (ret == FALSE || *mime_type != NULL)
+ return ret;
+ len = strlenW(extensionW);
+ lower_extensionW = HeapAlloc(GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
+ if (lower_extensionW)
+ {
+ char *lower_extensionA;
+ memcpy(lower_extensionW, extensionW, (len + 1)*sizeof(WCHAR));
+ strlwrW(lower_extensionW);
+ lower_extensionA = wchars_to_utf8_chars(lower_extensionW);
+ if (lower_extensionA)
+ {
+ ret = match_glob(ud->native_mime_types, lower_extensionA, 1, mime_type);
+ HeapFree(GetProcessHeap(), 0, lower_extensionA);
+ }
+ else
+ {
+ ret = FALSE;
+ WINE_FIXME("out of memory\n");
+ }
+ HeapFree(GetProcessHeap(), 0, lower_extensionW);
+ }
+ else
+ {
+ ret = FALSE;
+ WINE_FIXME("out of memory\n");
+ }
+ return ret;
+}
+
+static BOOL write_freedesktop_mime_type_entry(void *user, const char *dot_extension,
+ const char *mime_type, const char *comment)
+{
+ struct xdg_file_type_user_data *ud = user;
+ BOOL ret = FALSE;
+ char *filename;
+
+ WINE_TRACE("writing MIME type %s, extension=%s, comment=%s\n", wine_dbgstr_a(mime_type),
+ wine_dbgstr_a(dot_extension), wine_dbgstr_a(comment));
+
+ filename = heap_printf("%s/x-wine-extension-%s.xml", ud->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=\"");
+ write_xml_text(packageFile, mime_type);
+ fprintf(packageFile, "\">\n");
+ fprintf(packageFile, " <glob pattern=\"*");
+ write_xml_text(packageFile, dot_extension);
+ fprintf(packageFile, "\"/>\n");
+ if (comment)
+ {
+ fprintf(packageFile, " <comment>");
+ write_xml_text(packageFile, comment);
+ fprintf(packageFile, "</comment>\n");
+ }
+ 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 write_freedesktop_association_entry(void *user, const char *dot_extension,
+ const char *friendlyAppName, const char *friendlyDocNameA,
+ const char *mimeType, const char *progId,
+ char *openWithIcon)
+{
+ struct xdg_file_type_user_data *ud = user;
+ BOOL ret = FALSE;
+ FILE *desktop;
+
+ char *desktopPath = heap_printf("%s/wine-extension-%s.desktop", ud->applications_dir, &dot_extension[1]);
+ if (!desktopPath) {
+ WINE_ERR("out of memory\n");
+ return FALSE;
+ }
+
+ WINE_TRACE("writing association for file type %s, friendlyAppName=%s, MIME type %s, progID=%s, icon=%s to file %s\n",
+ wine_dbgstr_a(dot_extension), wine_dbgstr_a(friendlyAppName), wine_dbgstr_a(mimeType),
+ wine_dbgstr_a(progId), wine_dbgstr_a(openWithIcon), wine_dbgstr_a(desktopPath));
+
+ desktop = fopen(desktopPath, "w");
+ if (desktop)
+ {
+ fprintf(desktop, "[Desktop Entry]\n");
+ fprintf(desktop, "Type=Application\n");
+ fprintf(desktop, "Name=%s\n", friendlyAppName);
+ fprintf(desktop, "MimeType=%s;\n", mimeType);
+ fprintf(desktop, "Exec=env WINEPREFIX=\"%s\" wine start /ProgIDOpen %s %%f\n", wine_get_config_dir(), progId);
+ fprintf(desktop, "NoDisplay=true\n");
+ fprintf(desktop, "StartupNotify=true\n");
+ if (openWithIcon)
+ fprintf(desktop, "Icon=%s\n", openWithIcon);
+ ret = TRUE;
+ fclose(desktop);
+ }
+ else
+ WINE_ERR("error writing association file %s\n", wine_dbgstr_a(desktopPath));
+
+ HeapFree(GetProcessHeap(), 0, desktopPath);
+ return ret;
+}
+
+BOOL xdg_remove_file_type_association(void *user, const char *dot_extension, LPCWSTR extensionW)
+{
+ struct xdg_file_type_user_data *ud = user;
+ char *desktopPath = heap_printf("%s/wine-extension-%s.desktop", ud->applications_dir, &dot_extension[1]);
+
+ if (desktopPath)
+ {
+ WINE_TRACE("removing file type association for %s\n", wine_dbgstr_w(extensionW));
+ remove(desktopPath);
+ HeapFree(GetProcessHeap(), 0, desktopPath);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void *xdg_refresh_file_type_associations_init(void)
+{
+ struct xdg_file_type_user_data *ud = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ud));
+
+ if (ud == NULL)
+ {
+ WINE_ERR("out of memory\n");
+ return NULL;
+ }
+
+ ud->mime_dir = heap_printf("%s/mime", xdg_data_dir);
+ if (ud->mime_dir == NULL)
+ {
+ WINE_ERR("out of memory\n");
+ return NULL;
+ }
+ create_directories(ud->mime_dir);
+
+ ud->packages_dir = heap_printf("%s/packages", ud->mime_dir);
+ if (ud->packages_dir == NULL)
+ {
+ WINE_ERR("out of memory\n");
+ return NULL;
+ }
+ create_directories(ud->packages_dir);
+
+ ud->applications_dir = heap_printf("%s/applications", xdg_data_dir);
+ if (ud->applications_dir == NULL)
+ {
+ WINE_ERR("out of memory\n");
+ return NULL;
+ }
+ create_directories(ud->applications_dir);
+
+ if (!build_native_mime_types(xdg_data_dir, &ud->native_mime_types))
+ return NULL;
+
+ return ud;
+}
+
+void xdg_refresh_file_type_associations_cleanup(void *user, BOOL hasChanged)
+{
+ struct xdg_file_type_user_data *ud = user;
+
+ if (hasChanged)
+ {
+ const char *argv[3];
+
+ argv[0] = "update-mime-database";
+ argv[1] = ud->mime_dir;
+ argv[2] = NULL;
+ spawnvp( _P_DETACH, argv[0], argv );
+
+ argv[0] = "update-desktop-database";
+ argv[1] = ud->applications_dir;
+ spawnvp( _P_DETACH, argv[0], argv );
+ }
+
+ HeapFree(GetProcessHeap(), 0, ud->mime_dir);
+ HeapFree(GetProcessHeap(), 0, ud->packages_dir);
+ HeapFree(GetProcessHeap(), 0, ud->applications_dir);
+ HeapFree(GetProcessHeap(), 0, ud);
+}
+
+int xdg_build_desktop_link(const char *unix_link, const char *link, const char *link_name, const char *path,
+ const char *args, const char *descr, const char *workdir, char *icon)
+{
+ char *location;
+ int r = -1;
+
+ location = heap_printf("%s/%s.desktop", xdg_desktop_dir, link_name);
+ if (location)
+ {
+ r = !write_desktop_entry(unix_link, location, link_name,
+ path, args, descr, workdir, icon);
+ if (r == 0)
+ chmod(location, 0755);
+ }
+ return r;
+}
+
+int xdg_build_menu_link(const char *unix_link, const char *link, const char *link_name, const char *path,
+ const char *args, const char *descr, const char *workdir, char *icon)
+{
+ return !write_menu_entry(unix_link, link, link_name, path, args, descr, workdir, icon);
+}
+
+
+BOOL xdg_init(void)
+{
+ WCHAR shellDesktopPath[MAX_PATH];
+ HRESULT hr = SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, shellDesktopPath);
+ if (SUCCEEDED(hr))
+ xdg_desktop_dir = wine_get_unix_file_name(shellDesktopPath);
+ if (xdg_desktop_dir == NULL)
+ {
+ WINE_ERR("error looking up the desktop directory\n");
+ return FALSE;
+ }
+
+ if (getenv("XDG_CONFIG_HOME"))
+ xdg_config_dir = heap_printf("%s/menus/applications-merged", getenv("XDG_CONFIG_HOME"));
+ else
+ xdg_config_dir = heap_printf("%s/.config/menus/applications-merged", getenv("HOME"));
+ if (xdg_config_dir)
+ {
+ create_directories(xdg_config_dir);
+ if (getenv("XDG_DATA_HOME"))
+ xdg_data_dir = strdupA(getenv("XDG_DATA_HOME"));
+ else
+ xdg_data_dir = heap_printf("%s/.local/share", getenv("HOME"));
+ if (xdg_data_dir)
+ {
+ char *buffer;
+ create_directories(xdg_data_dir);
+ buffer = heap_printf("%s/desktop-directories", xdg_data_dir);
+ if (buffer)
+ {
+ mkdir(buffer, 0777);
+ HeapFree(GetProcessHeap(), 0, buffer);
+ }
+ return TRUE;
+ }
+ HeapFree(GetProcessHeap(), 0, xdg_config_dir);
+ }
+ WINE_ERR("out of memory\n");
+ return FALSE;
+}
+
+const struct winemenubuilder_dispatch xdg_dispatch =
+{
+ xdg_init,
+
+ xdg_build_desktop_link,
+ xdg_build_menu_link,
+
+ xdg_write_icon,
+
+ xdg_refresh_file_type_associations_init,
+ freedesktop_mime_type_for_extension,
+ write_freedesktop_mime_type_entry,
+ write_freedesktop_association_entry,
+ xdg_remove_file_type_association,
+ xdg_refresh_file_type_associations_cleanup
+};
+
--
1.8.0
More information about the wine-patches
mailing list