[1/2] start.exe: add a ProgIDOpen2 option

Damjan Jovanovic damjan.jov at gmail.com
Sun Feb 27 10:40:59 CST 2011


Changelog:
* start.exe: add a ProgIDOpen2 option

Damjan Jovanovic

P.S. The idea behind this patchset is to generate a file open
association per application instead of per extension, so each
application appears in Freedesktop's right-click list and "Open with"
list only once.

The changes are roughly as follows:

In winemenubuilder:
* store ';' separated lists of MIME types and ProgIDs per "primary
extension" (first extension to reference a given application)
* generate multiple MIME types for the single .desktop file that opens
with this application
* specify the primary extension in the .desktop file instead of the
ProgID, and use the /ProgIDOpen2 option to distinguish from the old
way

In start.exe:
* look up the Freedesktop MIME type for the given file using xdg-mime
* search the primary extension for the MIME type and use the
corresponding ProgID
* in the case of a new association, no MIME type will match, so
default to the first ProgID

start.exe patches come first so there is no revision on which neither
the old nor the new way will work.
-------------- next part --------------
diff --git a/programs/start/Makefile.in b/programs/start/Makefile.in
index 7b8f943..a743c62 100644
--- a/programs/start/Makefile.in
+++ b/programs/start/Makefile.in
@@ -1,7 +1,7 @@
 EXTRADEFS = -DWINE_NO_UNICODE_MACROS
 MODULE    = start.exe
 APPMODE   = -mconsole -municode
-IMPORTS   = shell32 user32
+IMPORTS   = shell32 user32 advapi32
 
 C_SRCS = start.c
 
diff --git a/programs/start/start.c b/programs/start/start.c
index 81c1d27..8572fb3 100644
--- a/programs/start/start.c
+++ b/programs/start/start.c
@@ -173,6 +173,185 @@ static WCHAR *get_parent_dir(WCHAR* path)
 	return result;
 }
 
+static HKEY open_associations_reg_key(void)
+{
+	static const WCHAR Software_Wine_FileOpenAssociationsW[] = {
+		'S','o','f','t','w','a','r','e','\\','W','i','n','e','\\',
+		'F','i','l','e','O','p','e','n','A','s','s','o','c','i','a','t','i','o','n','s',0};
+	HKEY assocKey;
+	if (RegCreateKeyW(HKEY_CURRENT_USER, Software_Wine_FileOpenAssociationsW, &assocKey) == ERROR_SUCCESS)
+		return assocKey;
+	return NULL;
+}
+
+static char* wchars_to_utf8_chars(LPCWSTR string)
+{
+	char *ret;
+	INT size = WideCharToMultiByte(CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL);
+	ret = HeapAlloc(GetProcessHeap(), 0, size);
+	if (ret)
+		WideCharToMultiByte(CP_UTF8, 0, string, -1, ret, size, NULL, NULL);
+	return ret;
+}
+
+static WCHAR* reg_get_valW(HKEY key, LPCWSTR subkey, LPCWSTR name)
+{
+	DWORD size;
+	if (RegGetValueW(key, subkey, name, RRF_RT_REG_SZ, NULL, NULL, &size) == ERROR_SUCCESS) {
+		WCHAR *ret = HeapAlloc(GetProcessHeap(), 0, size);
+		if (ret) {
+			if (RegGetValueW(key, subkey, name, RRF_RT_REG_SZ, NULL, ret, &size) == ERROR_SUCCESS)
+			return ret;
+		}
+		HeapFree(GetProcessHeap(), 0, ret);
+	}
+	return NULL;
+}
+
+static CHAR* reg_get_val_utf8(HKEY key, LPCWSTR subkey, LPCWSTR name)
+{
+	WCHAR *valW = reg_get_valW(key, subkey, name);
+	if (valW) {
+		char *val = wchars_to_utf8_chars(valW);
+		HeapFree(GetProcessHeap(), 0, valW);
+		return val;
+	}
+	return NULL;
+}
+
+static char* heap_printf(const char *format, ...)
+{
+	va_list args;
+	int size = 4096;
+	char *buffer, *ret;
+	int n;
+
+	va_start(args, format);
+	while (1) {
+		buffer = HeapAlloc(GetProcessHeap(), 0, size);
+		if (buffer == NULL)
+			break;
+		n = vsnprintf(buffer, size, format, args);
+		if (n == -1)
+			size *= 2;
+		else if (n >= size)
+			size = n + 1;
+		else
+			break;
+		HeapFree(GetProcessHeap(), 0, buffer);
+	}
+	va_end(args);
+	if (!buffer) return NULL;
+	ret = HeapReAlloc(GetProcessHeap(), 0, buffer, strlen(buffer) + 1 );
+	if (!ret) ret = buffer;
+	return ret;
+}
+
+static char* mime_type_for(LPCWSTR filename)
+{
+	char *filenameA = NULL;
+	char *exec = NULL;
+	FILE *xdg_mime = NULL;
+	char *buffer = NULL;
+	int size = 4096;
+	int pos = 0;
+
+	filenameA = wchars_to_utf8_chars(filename);
+	if (filenameA == NULL)
+		goto end;
+
+	exec = heap_printf("xdg-mime query filetype '%s'", filenameA);
+	if (exec == NULL)
+		goto end;
+
+	xdg_mime = popen(exec, "r");
+	if (xdg_mime == NULL)
+		goto end;
+
+	buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
+	if (buffer == NULL)
+		goto end;
+	for (;;) {
+		int i;
+		char *buffer2;
+		for (i = 0; i < size; i++) {
+			int ch;
+			ch = fgetc(xdg_mime);
+			if (ch == EOF || ch == '\r' || ch == '\n')
+				goto end;
+			buffer[pos++] = ch;
+		}
+		size *= 2;
+		buffer2 = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, buffer, size);
+		if (buffer2 == NULL) {
+			HeapFree(GetProcessHeap(), 0, buffer);
+			buffer = NULL;
+			goto end;
+		}
+		buffer = buffer2;
+	}
+
+end:
+	HeapFree(GetProcessHeap(), 0, filenameA);
+	HeapFree(GetProcessHeap(), 0, exec);
+	if (xdg_mime)
+		pclose(xdg_mime);
+	return buffer;
+}
+
+static WCHAR *lookup_progid(LPCWSTR dot_extension, LPCWSTR filename)
+{
+	static const WCHAR ProgIDW[] = {'P','r','o','g','I','D',0};
+	static const WCHAR MimeTypeW[] = {'M','i','m','e','T','y','p','e',0};
+
+	char *mimeTypeIn = NULL;
+	HKEY key = NULL;
+	char *mimeTypes = NULL;
+	WCHAR *progIds = NULL;
+	char *startA, *endA;
+	WCHAR *startW, *endW;
+
+	mimeTypeIn = mime_type_for(filename);
+	if (mimeTypeIn == NULL)
+		goto end;
+	key = open_associations_reg_key();
+	if (key == NULL)
+		goto end;
+	mimeTypes = reg_get_val_utf8(key, dot_extension, MimeTypeW);
+	if (mimeTypes == NULL)
+		goto end;
+	progIds = reg_get_valW(key, dot_extension, ProgIDW);
+	if (progIds == NULL)
+		goto end;
+	startA = mimeTypes;
+	startW = progIds;
+	for (;;) {
+		BOOL done = FALSE;
+		endA = startA;
+		endW = startW;
+		while (*endA && *endA != ';') endA++;
+		while (*endW && *endW != ';') endW++;
+		if (*endA == 0 || *endW == 0)
+			done = TRUE;
+		*endA = 0;
+		*endW = 0;
+		if (strcmp(startA, mimeTypeIn) == 0) {
+			memmove(progIds, startW, (strlenW(startW)+1)*sizeof(WCHAR));
+			break;
+		}
+		if (done)
+			break;
+		startA = endA + 1;
+		startW = endW + 1;
+	}
+
+end:
+	HeapFree(GetProcessHeap(), 0, mimeTypeIn);
+	RegCloseKey(key);
+	HeapFree(GetProcessHeap(), 0, mimeTypes);
+	return progIds;
+}
+
 int wmain (int argc, WCHAR *argv[])
 {
 	SHELLEXECUTEINFOW sei;
@@ -180,14 +359,19 @@ int wmain (int argc, WCHAR *argv[])
 	int i;
 	int unix_mode = 0;
 	int progid_open = 0;
+	int progid_open2 = 0;
 	WCHAR *dos_filename = NULL;
 	WCHAR *parent_directory = NULL;
 	DWORD binary_type;
+	WCHAR *extension = NULL;
+	WCHAR *progId = NULL;
 
 	static const WCHAR openW[] = { 'o', 'p', 'e', 'n', 0 };
 	static const WCHAR unixW[] = { 'u', 'n', 'i', 'x', 0 };
 	static const WCHAR progIDOpenW[] =
 		{ 'p', 'r', 'o', 'g', 'I', 'D', 'O', 'p', 'e', 'n', 0};
+	static const WCHAR progIDOpen2W[] =
+		{ 'p', 'r', 'o', 'g', 'I', 'D', 'O', 'p', 'e', 'n', '2', 0};
 
 	memset(&sei, 0, sizeof(sei));
 	sei.cbSize = sizeof(sei);
@@ -209,7 +393,7 @@ int wmain (int argc, WCHAR *argv[])
 			break;
 
 		/* Unix paths can start with / so we have to assume anything following /U is not a flag */
-		if (unix_mode || progid_open)
+		if (unix_mode || progid_open || progid_open2)
 			break;
 
 		/* Handle all options in this word */
@@ -251,6 +435,8 @@ int wmain (int argc, WCHAR *argv[])
 			case 'P':
 				if (strncmpiW(&argv[i][ci], progIDOpenW, 17) == 0)
 					progid_open = 1;
+				else if (strncmpiW(&argv[i][ci], progIDOpen2W, 18) == 0)
+					progid_open2 = 1;
 				else {
 					WINE_ERR("Option '%s' not recognized\n", wine_dbgstr_w( argv[i]+ci-1));
 					usage();
@@ -276,10 +462,21 @@ int wmain (int argc, WCHAR *argv[])
 	if (progid_open) {
 		sei.lpClass = argv[i++];
 		sei.fMask |= SEE_MASK_CLASSNAME;
+	} else if (progid_open2) {
+		extension = argv[i++];
 	}
 
 	sei.lpFile = argv[i++];
 
+	if (progid_open2) {
+		progId = lookup_progid(extension, sei.lpFile);
+		sei.lpClass = progId;
+		if (sei.lpClass)
+			sei.fMask |= SEE_MASK_CLASSNAME;
+		WINE_TRACE("extension=%s file=%s progId=%s\n", wine_dbgstr_w(extension),
+			wine_dbgstr_w(sei.lpFile), wine_dbgstr_w(sei.lpClass));
+	}
+
 	args = build_args( argc - i, &argv[i] );
 	sei.lpParameters = args;
 
@@ -349,6 +546,7 @@ done:
 	HeapFree( GetProcessHeap(), 0, args );
 	HeapFree( GetProcessHeap(), 0, dos_filename );
 	HeapFree( GetProcessHeap(), 0, parent_directory );
+	HeapFree( GetProcessHeap(), 0, progId );
 
 	if (sei.fMask & SEE_MASK_NOCLOSEPROCESS) {
 		DWORD exitcode;


More information about the wine-patches mailing list