Strange Escaping

Hello,

i have a strange string escaping problem. With the crate rust-ini i read some keys with values and i have say rust-ini please deliver the quotes as is.

[test]
path="%Userprofile/followingpathwithnowhitespace".

So an now i want to give an command this path and i have thinked that the quotes will be delivered, but at my tests i think rust do it own logic.

A string with whitespaces became quotes and a string without become no quotes.

So Problem %Userprofile" inlcude whitespace :slight_smile: .

Tja the i have thinked, cool the make i and condition:

if (opt_ytdl["save_location"].contains ("%UserProfile%") && !(opt_ytdl["save_location"].contains (" ")))

and then i format the string without quotes with quotes

let test: String = format!("{}",&opt_ytdl["save_location"]);

println!("{:?}", &test); // "\"%UserProfile%\\pfadwithnows\""
println!("{}", &test); // "%UserProfile%\pfadwithnows"

Okay so when i push this &test to an command.arg(&test), then he put the debuggend escpade string with " in that argument.

Seems like that i overwrite an rust internal thing. Can it be that rust take the choice what variable became quotes and what variable not?

When the condition is not met and i overgive the string as is to command.arg then all functioned one string with quotes and the cmd /k echo %1 gives the correct path:
"c:\user\user with ws\pfadwithnows"

I think rust-ini make it right an give an string with quotes to rust and rust say "oooh string without space needs no double quotes" and patch the double quotes away, right?

Edit, when i put an whitespace in rust-ini

path="asdsa asdl sadsa dsa d"
the raw string {}-> "asdsa asdl sadsa dsa d"

path="asdsaasdlsadsadsad"
the raw string {}-> asdsa asdl sadsa dsa d

INI doesn't require quotes (and if you supply them they are treated as literal). Can't you just write:

[test]
path=%Userprofile%/followingpathwithnowhitespace

path=c:\user\user with ws\pfadwithnows

?

Oh, this appears to be a quirk of rust-ini.

Try setting ParseOption.enabled_quote to false so that it always treats quotes as literal. Then don't use quotes for something like a path that you're trying to parse as a single string.

(or switch to a better-specified format like toml or yaml, where everybody can agree on the exact rules for quoting)

@ExpHP Thx but how i wrote in my reply to the other Thread, this is an problem with rust, the same appears when i deliver the String or &str that i use pure to rust as an variable

let test1: &str = r"%Userprofile%\pathwithnospace";
let test2: String = r"%Userprofile%\pathwithnospace".to_string();

as an example:
Rust see the string doesnt have any whitespace and he escape the string without double quotes, why rust see i don't need double quotes.

let test3: &str = r"c:\users\user with space\or path with space";

Rust say hey cool whitespaces you need doubles quotes :slight_smile: and in both rust debug functions says:
{:?} debug with all tes1..test3 double quotes and correct escaped \
{} without double quotes and \

let mut process = Command::new("cmd");
			process.arg ("/c");
			process.arg ("start");
			process.arg ("cmd");
			process.arg ("/k");
			process.arg ("cd");
process.arg (oneofthetests);
process.output;

strange construct but the only possible choice to test it if cmd/ps take the correct escaped string.

When i put my seld made string with space to the command.arg the rust deliver double quotes without rust don't deliver double quotes.

Debug says in both variables the same but the deliver to command.arg is different.

So rust-ini and self variables will don't be delivered correctly when rust detect no whitespace in string.

It can only be rust, or?

First, mind that you're dealing with a number of different styles of quoting and parsing here.

{:?} debug with all tes1…test3 double quotes and correct escaped \

Here, you mean that you tried println!("{:?}", test1);? That escaped the string using the rules for rust string literals, not for Windows command line arguments.

Command::new("cmd");
    process.arg ("/c");
    process.arg ("start");
    process.arg ("cmd");
    process.arg ("/k");
    process.arg ("cd");
process.arg (oneofthetests);

Yikes! Okay, a number of things happen here:

  • When rust writes the command, it should escape arguments in a manner which guarantees that the process called receives exactly the same number of strings (with the same content) in ARGV as the ones you supplied using .arg. This means it quotes windows command-line arguments, putting quotes around things with spaces and doing whatever.
  • However, your command is cmd /c. AFAICT, this unescapes CMD syntax (e.g. replacing ^\ with \) and expands environment variables (expanding %USERPROFILE% to a path that may contain spaces, and it won't get quotes added to it) without ever parsing arguments.
  • start, as far as I can tell, parses a single command-line argument to extract the binary name, then forwards the rest of the command line identically.
  • Your next command is cmd /k. Again, this unescapes CMD syntax and expands environment variables, and forwards everything as a single argument string. I think.
  • Finally, cd parses the argument string according to bizarre, made-up rules that are only used by cd.

And there's probably a couple of things I missed in there. Initially, I had assumed that cmd and start would all parse the argument list and quote the remaining args, but bizarrely, I found that

cmd /c start cmd /k cd C:\Program      Files

(with too many spaces in Program Files) fails, meaning that all of these intermediate commands preserve the structure of the argument string.

So you may be correct that rust is inserting quotes around things that have spaces, and not putting quotes around things that don't have spaces. But if you can even tell that it's doing so, then you have way too many layers of parsing and quoting going on. There's so much going on here, you're bound to run into problems no matter what!

1 Like

@ExpHP Thanx for your investigating. Yes you are right not rust make the escape failure, it is the cmd calling with start. When i call the executable direct with Command::new all functioned.

Says this i don't can use cmd /c start in my special case?

I want to replicate an cmd/powershell batch/script what calling an executable, start a new thread with cmd->start and discard the called script and let the user only show the new thread with all errors and good messages:).

cmd /c start was so easy for, why i have problems to catch the output from and .spawn child thread, like an normal calling from an batch/powershell.

command.output catch the output only after the called exe ist finished and so long he shows nothing or and error
command.spawn calls an child thread and i don't know how i can catch the output correctly.

ps: the double cmd /c and cmd /k was only for have an case with start where i can cd or echo the problem variable.

@ExpHP Okay i have looked in the std::docs from rust and there i have found a good example.

https://doc.rust-lang.org/std/process/struct.Child.html

And i must say, yes this is the better handling and my snaky/cringe cmd/start handling is not good :slight_smile: .

I have build me a stack of problems and rust says "bulls**t, no oldscool hacking in my program code :slight_smile:, make it in the rust way and right or leave it.

Rust cures old scool diseases by the coder :slight_smile:.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.