Command parameters truncated when containing '&' charact

Bug reports will be moved here when the described bug has been fixed

Moderators: Hacker, petermad, Stefan2, white

PascalT
Junior Member
Junior Member
Posts: 34
Joined: 2015-08-27, 08:00 UTC

Command parameters truncated when containing '&' charact

Post by *PascalT »

When I create a button with an associated command, I found impossible to give this command a parameter containing the character <&> while not containing spaces. This character causes the truncation of the parameter string.


OS : Windows XP SP3 and Windows 7 Pro SP1

Example with a button that creates in the target panel a directory with the same name as the selected directory or file in the source panel :

Command :
dupdir.bat

Parameters :
%T%N

Content of dupdir.bat :
@md %1


It still does not work if I quote the parameters ("%T%N") or add quotes in dupdir.bat.

I think the problem is due to total Commander quoting the parameters string only if this string contains at least one space.
Maybe always quoting the string or never quoting it and letting the user adding the quotes in the parameter string would solve the problem.
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 50549
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Post by *ghisler(Author) »

Do you mean that the file name contained the "&" character? Or the path?
Maybe it helps to change the dupdir.bat to:
@md "%1"
Author of Total Commander
https://www.ghisler.com
PascalT
Junior Member
Junior Member
Posts: 34
Joined: 2015-08-27, 08:00 UTC

Post by *PascalT »

It does not work, whether the & is in the path or in the file name.
The string %T%N is truncated before the character &.
This is a problem related with quoting. Without quoting the & has a special meaning in the windows/dos command line.

As I said quoting inside dupdir.bat does not work either.

The parameter string should be quoted before starting <command> <parameter>.
According to my tests, I really think Total Commander only quotes this string when it contains at least one space.
If you try a string with both & and a space, it works.
User avatar
MVV
Power Member
Power Member
Posts: 8711
Joined: 2008-08-03, 12:51 UTC
Location: Russian Federation

Post by *MVV »

It is not a TC bug, it is a proper quoting issue for particular external program.

When you start a batch, it is launched via cmd.exe which treats unquoted & as a command separator. So generic rule is to quote every path that is passed to cmd.exe (and batch files too).

"%1" won't help in a batch: it will break quoted parameter, and it will quote only part of unquoted one.

Well, %P%N and %T%N quote parameters if they contain spaces (i.e. time to time) so it is impossible to create 100% working button with these parameters (w/o quotes it will not work with paths containing & and no spaces, with quotes it will not work with paths containing both & and spaces - there were topics asking for explicit quoting for parameters like %P%N). However you can use something like "%T%O.%E" - in this case TC won't add quotes at all so explicit quoting will work.
User avatar
Dalai
Power Member
Power Member
Posts: 9966
Joined: 2005-01-28, 22:17 UTC
Location: Meiningen (Südthüringen)

Post by *Dalai »

Code: Select all

md "%~1"
should work. That removes the quotes from the first parameter (if any) and adds them again, so it's one pair of quotes only after that in any case (whether the parameter was quoted or not).

Regards
Dalai
#101164 Personal licence
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64

Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
User avatar
MVV
Power Member
Power Member
Posts: 8711
Joined: 2008-08-03, 12:51 UTC
Location: Russian Federation

Post by *MVV »

Dalai,
It is easy to try it and it doesn't work. Most probably because cmd.exe parses command line arguments before starting batch and unquoted & isn't treated as parameter part at all.
PascalT
Junior Member
Junior Member
Posts: 34
Joined: 2015-08-27, 08:00 UTC

Post by *PascalT »

Thank you for your quick answers and tricks.
md "%~1" in the .bat file does not seem to work, because the parameter string must be quoted when Total Commander calls the command, and not afterwards in the .bat file.
But "%T%O.%E" in the parameter field is the solution. With these parameters Total Commander never quotes the parameter string whether or not there are spaces in the string, and the quotes are manually added in the field.
-> Solved
User avatar
ghisler(Author)
Site Admin
Site Admin
Posts: 50549
Joined: 2003-02-04, 09:46 UTC
Location: Switzerland
Contact:

Post by *ghisler(Author) »

OK, I will quote %P%N if it contains & characters. Are there any other special characters other than & and space I need to put in quotes?
Author of Total Commander
https://www.ghisler.com
User avatar
Dalai
Power Member
Power Member
Posts: 9966
Joined: 2005-01-28, 22:17 UTC
Location: Meiningen (Südthüringen)

Post by *Dalai »

I guess, you would have to quote if there are characters that have a special meaning in CMD and are allowed in file names at the same time. This applies to &, but also to brackets () and I'm not sure if there's more of them.

Regards
Dalai
#101164 Personal licence
Ryzen 5 2600, 16 GiB RAM, ASUS Prime X370-A, Win7 x64

Plugins: Services2, Startups, CertificateInfo, SignatureInfo, LineBreakInfo - Download-Mirror
User avatar
MVV
Power Member
Power Member
Posts: 8711
Joined: 2008-08-03, 12:51 UTC
Location: Russian Federation

Post by *MVV »

ghisler(Author),
Try cmd.exe /?, it lists special characters. E.g. in Russian:
Специальные символы, которые требуют обязательного заключения в кавычки:
<пробел>
&()[]{}^=;!'+,`~
There are many ones so it would be faster to define a bit map to check all of them at once.

Code: Select all

static const unsigned char cmdSpecialMap[16] = {
	0x00, 0x00, 0x00, 0x00, 0xC2, 0x1B, 0x00, 0x28, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x68
};

// memset(cmdSpecialMap, 0, sizeof(cmdSpecialMap));
// for (const char* p = "&()[]{}^=;!'+,`~"; *p; ++p) cmdSpecialMap[*p >> 3] |= 1 << (*p & 0x7);


bool isCmdSpecial(wchar_t c) {
	if (c > 127) return 0;
	return (cmdSpecialMap[c >> 3] & (1 << (c & 0x07))) != 0;
}

const wchar_t* path = "c:\\you & me";
for (const wchar_t* p = path; *p; ++p) {
	if (isCmdSpecial(*p)) return true;
}
User avatar
milo1012
Power Member
Power Member
Posts: 1158
Joined: 2012-02-02, 19:23 UTC

Post by *milo1012 »

2MVV, just for my understanding:
Was there REALLY a reason to show Christian how to use char checks in code?

And concerning your code:
Is there REALLY a special reason to entirely use bit masks?
A simple static 128-sized bool/char lookup table is what people normally use, because you'd spare all future bit operations.
TC plugins: PCREsearch and RegXtract
User avatar
MVV
Power Member
Power Member
Posts: 8711
Joined: 2008-08-03, 12:51 UTC
Location: Russian Federation

Post by *MVV »

How do you know what people normally use if you can't ask all people?
Bit masks take much less memory and allow the same thing so personally I would use them everywhere when it is possible.

BTW standard C++ STL vector<bool> realization uses bit arrays instead of byte arrays just because there is no any need to use a byte per boolean value.
PascalT
Junior Member
Junior Member
Posts: 34
Joined: 2015-08-27, 08:00 UTC

Post by *PascalT »

ghisler(Author)

If you quote %P%N if it contains special characters, maybe you could also quote %T%N ?
It would be neatier that the trick "%T%O.%E".
User avatar
MVV
Power Member
Power Member
Posts: 8711
Joined: 2008-08-03, 12:51 UTC
Location: Russian Federation

Post by *MVV »

PascalT,
It is already quoted when path contains spaces.
User avatar
milo1012
Power Member
Power Member
Posts: 1158
Joined: 2012-02-02, 19:23 UTC

Post by *milo1012 »

MVV wrote:How do you know what people normally use if you can't ask all people?
Well, for one thing because I have experience in programming in teams,
and because you can read it in every book and online forums: for more than just a handful of results it's a common practice to use LUTs.
MVV wrote:BTW standard C++ STL vector<bool> realization uses bit arrays instead of byte arrays just because there is no any need to use a byte per boolean value.
I'm talking about sth. like this:

Code: Select all

In main module/header, etc. :

static const struct _special_lut {
	bool lut[128];
	_special_lut() {
		const char* lut_chars = "&()[]{}^=;!'+,`~";
		for(size_t i = 0; i < (sizeof(lut) / sizeof(bool)); i++) {
			const char* p = lut_chars;
			while(*p && *p != static_cast<char>(i))
				p++;
			lut[i] = *p ? true : false;
		}
	}
	bool check(const wchar_t* path) const {
		for(const wchar_t* p = path; *p; p++) {
			if(*p < (sizeof(lut) / sizeof(bool))) {
				if(lut[static_cast<size_t>(*p)])
					return true;
			}
		}
		return false;
	}
} special_lut;

Call:

const wchar_t* path1 = L"c:\\you & me";
if(... special_lut.check(path1))
 ...
TC plugins: PCREsearch and RegXtract
Post Reply