From: Bas Weelinck Subject: cmd: Detect and handle start console title. Message-Id: <20170319194736.1455-1-bas.weelinck@gmail.com> Date: Sun, 19 Mar 2017 20:47:36 +0100 Fixes: https://bugs.winehq.org/show_bug.cgi?id=42508 Implements proper start built-in console title detection and handling in a way that will not break anything relying on the separate 'start.exe' binary behaviour that is present in wine but not in modern day Windows. This code will break if WCMD_parameter_with_delims ever starts handling quote escapes, which I for now assume it is designed not to. Patch tested on Gentoo Linux. Original start behaviour tested on Windows XP and Windows 7. Signed-off-by: Bas Weelinck --- programs/cmd/builtins.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++-- programs/cmd/wcmd.h | 2 +- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 7301f54..1da1f67 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -4311,10 +4311,15 @@ void WCMD_shift (const WCHAR *args) { /**************************************************************************** * WCMD_start */ -void WCMD_start(const WCHAR *args) +void WCMD_start(WCHAR *args) { static const WCHAR exeW[] = {'\\','c','o','m','m','a','n','d', '\\','s','t','a','r','t','.','e','x','e',0}; + static const WCHAR startDelims[] = { ' ', '\t', '/', '\0' }; + static const WCHAR prefixQuote[] = {'"','\\','"','\0'}; + static const WCHAR postfixQuote[] = {'\\','"','"','\0'}; + int argno; + int have_title; WCHAR file[MAX_PATH]; WCHAR *cmdline; STARTUPINFOW st; @@ -4322,10 +4327,90 @@ void WCMD_start(const WCHAR *args) GetWindowsDirectoryW( file, MAX_PATH ); strcatW( file, exeW ); - cmdline = heap_alloc( (strlenW(file) + strlenW(args) + 2) * sizeof(WCHAR) ); + cmdline = heap_alloc( (strlenW(file) + strlenW(args) + 8) * sizeof(WCHAR) ); strcpyW( cmdline, file ); strcatW( cmdline, spaceW ); - strcatW( cmdline, args ); + + /* The start built-in has some special command-line parsing properties + * which will be outlined here. + * + * both '\t' and ' ' are argument separators + * '/' has a special double role as both separator and switch prefix, e.g. + * + * > start /low/i + * or + * > start "title"/i + * + * are valid ways to pass multiple options to start. In the latter case + * '/i' is not a part of the title but parsed as a switch. + * + * However, '=', ';' and ',' are not separators: + * > start "deus"=ex,machina + * + * will in fact open a console titled 'deus=ex,machina' + * + * The title argument parsing code is only interested in quotes themselves, + * it does not respect escaping of any kind and all quotes are dropped + * from the resulting title, therefore: + * + * > start "\"" hello"/low + * + * actually opens a console titled '\ hello' with low priorities. + * + * To not break compatibility with wine programs relying on + * wine's separate 'start.exe', this program's peculiar console + * title parsing is actually implemented in 'cmd.exe' which is the + * application native Windows programs will use to invoke 'start'. + * + * WCMD_parameter_with_delims will take care of everything for us. + */ + have_title = FALSE; + for (argno=0; ; argno++) { + WCHAR *thisArg, *argN; + + argN = NULL; + thisArg = WCMD_parameter_with_delims(args, argno, &argN, FALSE, FALSE, startDelims); + + /* No more parameters */ + if (!argN) + break; + + /* Found the title */ + if (argN[0] == '"') { + TRACE("detected console title: %s\n", wine_dbgstr_w(thisArg)); + have_title = TRUE; + + /* Copy all of the cmdline processed */ + memcpy(cmdline, args, sizeof(WCHAR) * (argN - args)); + cmdline[argN - args] = '\0'; + + /* Add quoted title */ + strcatW(cmdline, prefixQuote); + strcatW(cmdline, thisArg); + strcatW(cmdline, postfixQuote); + + /* Concatenate remaining command-line */ + thisArg = WCMD_parameter_with_delims(args, argno, &argN, TRUE, FALSE, startDelims); + strcatW(cmdline, argN + strlenW(thisArg)); + + break; + } + + /* Skipping a regular argument? */ + else if (argN != args && argN[-1] == '/') { + continue; + + /* Not an argument nor the title, start of program arguments, + * stop looking for title. + */ + } else + break; + } + + /* build command-line if not built yet */ + if (!have_title) { + strcatW( cmdline, args ); + } memset( &st, 0, sizeof(STARTUPINFOW) ); st.cb = sizeof(STARTUPINFOW); diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index c135621..1180251 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -95,7 +95,7 @@ void WCMD_setshow_path (const WCHAR *args); void WCMD_setshow_prompt (void); void WCMD_setshow_time (void); void WCMD_shift (const WCHAR *args); -void WCMD_start (const WCHAR *args); +void WCMD_start (WCHAR *args); void WCMD_title (const WCHAR *); void WCMD_type (WCHAR *); void WCMD_verify (const WCHAR *args); -- 2.10.2