https://bugs.winehq.org/show_bug.cgi?id=56399
Bug ID: 56399 Summary: cmd.exe's %~s modifier (short DOS path) expands to mixed short/long format Product: Wine Version: 9.2 Hardware: x86-64 OS: Linux Status: UNCONFIRMED Severity: normal Priority: P2 Component: cmd Assignee: wine-bugs@winehq.org Reporter: tncgyp+uypezc35u0dyxed6tszn0wk59xjphxb2gcxycxno8mxg4od 48@sharklasers.com Distribution: ---
Try the following:
cd /tmp && printf 'for %%%%P in ("%%ProgramFiles%%\Internet Explorer") do winepath.exe -s %%%%P\r\n' > shortpath.bat && wine cmd.exe /c shortpath.bat && printf 'for %%%%P in ("%%ProgramFiles%%\Internet Explorer") do echo %%%%~sP\r\n' > shortpath.bat && wine cmd.exe /c shortpath.bat && rm shortpath.bat && cd "$OLDPWD"
The first batch file uses winepath.exe -s and returns 'C:\PROG~5P2\INTE~H1A'. The second batch file uses cmd.exe's %~s modifier and returns 'C:\PROG~5P2\Internet Explorer'. Wine's cmd.exe %~s modifier seems to be broken. It should produce the same path as winepath.exe -s. The second batch file works as expected with MS cmd.exe in a WinXP VM and a Win7 VM.
Tested on wine 9.2 with fresh prefixes and: - WINEARCH=win32 and wine cmd.exe - WINEARCH=win64 and wine cmd.exe - WINEARCH=win64 and wine64 cmd.exe
https://bugs.winehq.org/show_bug.cgi?id=56399
Eric Pouech eric.pouech@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |eric.pouech@gmail.com
--- Comment #1 from Eric Pouech eric.pouech@gmail.com --- do you have a real application that suffers from this?
https://bugs.winehq.org/show_bug.cgi?id=56399
--- Comment #2 from tncgyp+uypezc35u0dyxed6tszn0wk59xjphxb2gcxycxno8mxg4od48@sharklasers.com --- Well, I want to write a script that changes the startup behavior of a Windows 95 program. One of the tasks of this script is to modify a configuration file that contains paths. Since the program itself was once ported from DOS, the paths are in short format. At first I wanted to use VBScript for this, but it turned out that wine's wscript.exe doesn't implement the .ShortPath method. Therefore I switched to batch and found this bug. At the moment it doesn't seem to be possible to solve the problem using a script that can run under both Wine and Windows.
https://bugs.winehq.org/show_bug.cgi?id=56399
--- Comment #3 from tncgyp+uypezc35u0dyxed6tszn0wk59xjphxb2gcxycxno8mxg4od48@sharklasers.com --- The problematic code is in: wine/programs/cmd/batch.c, function WCMD_HandleTildeModifiers @ line 546
/* 4. Handle 's' : Use short paths (File doesn't have to exist) */ if (wmemchr(firstModifier, 's', modifierLen) != NULL) { if (finaloutput[0] != 0x00) lstrcatW(finaloutput, L" ");
/* Convert fullfilename's path to a short path - Save filename away as only path is valid, name may not exist which causes GetShortPathName to fail if it is provided */ if (filepart) { lstrcpyW(thisoutput, filepart); *filepart = 0x00; GetShortPathNameW(fullfilename, fullfilename, ARRAY_SIZE(fullfilename)); lstrcatW(fullfilename, thisoutput); } }
The first question is, why does the conversion always take place without the file at the end of the path? This is always wrong for file names with more than 8 characters.
The second question is why a directory at the end of the path is recognized as a file?
To solve the first question, my proposal would be the following change:
/* 4. Handle 's' : Use short paths (File doesn't have to exist) */ if (wmemchr(firstModifier, 's', modifierLen) != NULL) { if (finaloutput[0] != 0x00) lstrcatW(finaloutput, L" ");
if (exists) { GetShortPathNameW(fullfilename, fullfilename, ARRAY_SIZE(fullfilename)); } else { WIN32_FILE_ATTRIBUTE_DATA dirInfo; int i_end = lstrlenW(fullfilename); int i_cut = i_end; i = i_end; while (i-- > 0) { /* Cut the last element of the path chain until the path resolves to a existing directory */ if (fullfilename[i] == L'\' || fullfilename[i] == L'/') { /* Undo the previous cut */ if (i_cut < i_end) fullfilename[i_cut] = L'\'; fullfilename[i] = 0x00; i_cut = i; if (GetFileAttributesExW(fullfilename, GetFileExInfoStandard, &dirInfo) != FALSE) { /* Keep the non-existing end of the path chain and append it to the new short path */ lstrcpyW(&thisoutput[1], &fullfilename[i_cut + 1]); thisoutput[0] = L'\'; GetShortPathNameW(fullfilename, fullfilename, ARRAY_SIZE(fullfilename)); lstrcatW(fullfilename, thisoutput); break; } } } } }
Unfortunately I can't test this at the moment so I would be happy if someone could perhaps take this on.
https://bugs.winehq.org/show_bug.cgi?id=56399
--- Comment #4 from Eric Pouech eric.pouech@gmail.com --- the hard part here is to ensure that the modification will not break any other existing program the claim cmd %~s should behave as winepath has to be proven (esp. when part of the path don't exist)
https://bugs.winehq.org/show_bug.cgi?id=56399
--- Comment #5 from tncgyp+uypezc35u0dyxed6tszn0wk59xjphxb2gcxycxno8mxg4od48@sharklasers.com --- I wouldn't say in general that cmd %~s should behave like winepath -s. Wine's cmd %~s should behave like Microsoft's cmd %~s. That means the existing part of the path is converted to short format and the non-existing part remains unchanged. After some playing around I can say that winepath -s seems to do exactly that, i.e. winepath -s is at least significantly closer to Microsoft's cmd %~s than Wine's current cmd %~s. All winepath -s does to achieve this is to call GetShortPathNameW, nothing else.
The proposed modification therefore simplifies to:
/* 4. Handle 's' : Use short paths (File doesn't have to exist) */ if (wmemchr(firstModifier, 's', modifierLen) != NULL) { if (finaloutput[0] != 0x00) lstrcatW(finaloutput, L" "); GetShortPathNameW(fullfilename, fullfilename, ARRAY_SIZE(fullfilename)); }
I think this modification can only break batch scripts written exclusively for Wine that rely on the current cmd %~s behavior.
https://bugs.winehq.org/show_bug.cgi?id=56399
--- Comment #6 from tncgyp+uypezc35u0dyxed6tszn0wk59xjphxb2gcxycxno8mxg4od48@sharklasers.com --- Addendum: Ok, I was also able to create cases where GetShortPathNameW fails, so forgot about my last post.