[3/10] winemenubuilder: Extract XDG specific parts to separate file. (resend)
Per Johansson
per at morth.org
Wed Mar 20 02:43:27 CDT 2013
Contains no code changes, except to remove static from functions
called in main file from xdg.c
---
programs/winemenubuilder/Makefile.in | 3 +-
programs/winemenubuilder/winemenubuilder.c | 890 +--------------------------
programs/winemenubuilder/winemenubuilder.h | 10 +
programs/winemenubuilder/xdg.c | 942 +++++++++++++++++++++++++++++
4 files changed, 964 insertions(+), 881 deletions(-)
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/winemenubuilder.c b/programs/winemenubuilder/winemenubuilder.c
index 5425854..364945e 100644
--- a/programs/winemenubuilder/winemenubuilder.c
+++ b/programs/winemenubuilder/winemenubuilder.c
@@ -159,23 +159,6 @@ typedef struct
int nIndex;
} ENUMRESSTRUCT;
-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;
@@ -186,10 +169,6 @@ DEFINE_GUID(CLSID_WICIcnsEncoder, 0x312fb6f1,0xb767,0x409d,0x8a,0x6d,0x0f,0xc1,0
const struct winemenubuilder_dispatch *wmb_dispatch;
-static char *xdg_config_dir;
-static char *xdg_data_dir;
-static char *xdg_desktop_dir;
-
static WCHAR* assoc_query(ASSOCSTR assocStr, LPCWSTR name, LPCWSTR extra);
static HRESULT open_icon(LPCWSTR filename, int index, BOOL bWait, IStream **ppStream);
@@ -213,7 +192,7 @@ static unsigned short crc16(const char* string)
return crc;
}
-static char *strdupA( const char *str )
+char *strdupA( const char *str )
{
char *ret;
@@ -222,7 +201,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;
@@ -289,27 +268,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;
@@ -329,7 +288,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);
@@ -365,7 +324,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;
@@ -922,7 +881,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;
@@ -1071,7 +1030,7 @@ static HRESULT open_icon(LPCWSTR filename, int index, BOOL bWait, IStream **ppSt
return hr;
}
-static char* compute_native_identifier(int exeIndex, LPCWSTR icoPathW)
+char* compute_native_identifier(int exeIndex, LPCWSTR icoPathW)
{
char* nativeIdentifier;
char *icoPathA;
@@ -1121,7 +1080,7 @@ static inline int size_to_slot(int size)
#define CLASSIC_SLOT 3
-static HRESULT osx_write_icon(IStream *icoStream, int exeIndex, LPCWSTR icoPathW,
+HRESULT osx_write_icon(IStream *icoStream, int exeIndex, LPCWSTR icoPathW,
const char *destFilename, char **nativeIdentifier)
{
ICONDIRENTRY *iconDirEntries = NULL;
@@ -1230,124 +1189,6 @@ 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 xdg_write_icon(IStream *icoStream, int exeIndex, LPCWSTR icoPathW,
- const char *destFilename, char **nativeIdentifier)
-{
- ICONDIRENTRY *iconDirEntries = NULL;
- int numEntries;
- int i;
- char *iconsDir = NULL;
- HRESULT hr = S_OK;
- LARGE_INTEGER zero;
-
- hr = read_ico_direntries(icoStream, &iconDirEntries, &numEntries);
- if (FAILED(hr))
- goto end;
-
- if (destFilename)
- *nativeIdentifier = heap_printf("%s", destFilename);
- else
- *nativeIdentifier = compute_native_identifier(exeIndex, icoPathW);
- 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, iconsDir);
- return hr;
-}
#endif /* defined(__APPLE__) */
/* extract an icon from an exe or icon file; helper for IPersistFile_fnSave */
@@ -1393,7 +1234,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;
@@ -1431,231 +1272,6 @@ 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)
{
@@ -1973,244 +1589,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(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 WCHAR* reg_get_valW(HKEY key, LPCWSTR subkey, LPCWSTR name)
{
DWORD size;
@@ -2428,50 +1806,6 @@ static BOOL cleanup_associations(void *user)
return hasChanged;
}
-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 is_extension_blacklisted(LPCWSTR extension)
{
/* These are managed through external tools like wine.desktop, to evade malware created file type associations */
@@ -2494,46 +1828,6 @@ static const char* get_special_mime_type(LPCWSTR extension)
return NULL;
}
-static BOOL write_freedesktop_association_entry(void *user, const char *dot_extension,
- const char *friendlyAppName,
- const char *friendlyDocNameA,
- const char *mimeType, const char *progId,
- const 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));
- return ret;
-}
-
static BOOL generate_associations(void *user)
{
static const WCHAR openW[] = {'o','p','e','n',0};
@@ -3524,176 +2818,12 @@ static WCHAR *next_token( LPWSTR *p )
return token;
}
-static BOOL init_xdg(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;
-}
-
-static 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;
-}
-
-static 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, path, args, descr, workdir, icon);
-}
-
-static 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;
-}
-
-static 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;
-}
-
-static 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);
-}
-
-const struct winemenubuilder_dispatch xdg_dispatch =
-{
- init_xdg,
-
- xdg_build_desktop_link,
- xdg_build_menu_link,
-
-#ifdef __APPLE__
- osx_write_icon,
-#else
- xdg_write_icon,
-#endif
-
- 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
-};
-
static BOOL dispatch_init(void)
{
const char *dispatch = NULL;
unsigned char buffer[256];
HKEY hkey;
+ extern const struct winemenubuilder_dispatch xdg_dispatch;
if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\MenuBuilder", 0, KEY_READ, &hkey) == ERROR_SUCCESS)
{
diff --git a/programs/winemenubuilder/winemenubuilder.h b/programs/winemenubuilder/winemenubuilder.h
index 0bd3425..708b0d2 100644
--- a/programs/winemenubuilder/winemenubuilder.h
+++ b/programs/winemenubuilder/winemenubuilder.h
@@ -35,6 +35,16 @@ typedef struct
WORD idCount;
} ICONDIR;
+char *strdupA( const char *str );
+char* heap_printf(const char *format, ...);
+BOOL create_directories(char *directory);
+DWORD register_menus_entry(const char *unix_file, const char *windows_file);
+char* wchars_to_utf8_chars(LPCWSTR string);
+HRESULT read_ico_direntries(IStream *icoStream, ICONDIRENTRY **ppIconDirEntries, int *numEntries);
+char* compute_native_identifier(int exeIndex, LPCWSTR icoPathW);
+HRESULT convert_to_native_icon(IStream *icoFile, int *indices, int numIndices,
+ const CLSID *outputFormat, const char *outputFileName, LPCWSTR commentW);
+
struct winemenubuilder_dispatch
{
BOOL (*init)(void);
diff --git a/programs/winemenubuilder/xdg.c b/programs/winemenubuilder/xdg.c
new file mode 100644
index 0000000..fd3a038
--- /dev/null
+++ b/programs/winemenubuilder/xdg.c
@@ -0,0 +1,942 @@
+/*
+ * 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;
+};
+
+static char *xdg_config_dir;
+static char *xdg_data_dir;
+static char *xdg_desktop_dir;
+
+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);
+ }
+}
+
+#ifdef __APPLE__
+extern HRESULT osx_write_icon(IStream *icoStream, int exeIndex, LPCWSTR icoPathW,
+ const char *destFilename, char **nativeIdentifier);
+#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 xdg_write_icon(IStream *icoStream, int exeIndex, LPCWSTR icoPathW,
+ const char *destFilename, char **nativeIdentifier)
+{
+ ICONDIRENTRY *iconDirEntries = NULL;
+ int numEntries;
+ int i;
+ char *iconsDir = NULL;
+ HRESULT hr = S_OK;
+ LARGE_INTEGER zero;
+
+ hr = read_ico_direntries(icoStream, &iconDirEntries, &numEntries);
+ if (FAILED(hr))
+ goto end;
+
+ if (destFilename)
+ *nativeIdentifier = heap_printf("%s", destFilename);
+ else
+ *nativeIdentifier = compute_native_identifier(exeIndex, icoPathW);
+ 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, iconsDir);
+ return hr;
+}
+#endif /* defined(__APPLE__) */
+
+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;
+}
+
+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(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,
+ const 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));
+ return ret;
+}
+
+static BOOL init_xdg(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;
+}
+
+static 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;
+}
+
+static 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, path, args, descr, workdir, icon);
+}
+
+static 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;
+}
+
+static 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;
+}
+
+static 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);
+}
+
+const struct winemenubuilder_dispatch xdg_dispatch =
+{
+ init_xdg,
+
+ xdg_build_desktop_link,
+ xdg_build_menu_link,
+
+#ifdef __APPLE__
+ osx_write_icon,
+#else
+ xdg_write_icon,
+#endif
+
+ 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.2
More information about the wine-patches
mailing list