Passing a commandline parameter containing quotes to installer - inno-setup

I'm trying to pass a custom commandline parameter to an installer created with Inno Setup. The parameter value actually consists of several parameters that will be used to launch the installed program when installation is complete, so the value contains spaces as well as quotes to group together parameters.
For example, when -arg "C:\path with spaces" -moreargs should be used as Parameters in a [Run] section entry, I would like to launch the installer like this:
setup.exe /abc="-arg "C:\path with spaces" -moreargs"
Outputting the parameters that the installer receives in a [Code] section via ParamStr() shows them (of course) split up: /abc=-arg C:\path, with, spaces -moreargs
How do I escape the quotes to retain them?
I tried doubling the inner quotes:
setup.exe /abc="-arg ""C:\path with spaces"" -moreargs"
This correctly keeps the parameter together (/abc=-arg C:\path with spaces -moreargs), however it seems that ParamStr() removes all quotes.
Is there a way to retain quotes within a parameter retrieved with ParamStr() or a param constant {param:abc|DefaultValue}?
Alternatives seem to be to either do my own parameter parsing from GetCmdTail (which contains the original parameter string) or use another character instead of the inner quotes that are retained in ParamStr() and then replace them with quotes afterwards. But I would prefer not doing that if there is a way to use the built-in functions.

It seems both {param} and ParamStr() strip out double quotes, but as you pointed out (thanks!) the GetCmdTail function returns the original.
So here's a function to get the original parameters with quotes:
function ParamStrWithQuotes(ParamName: String) : string;
var
fullCmd : String;
currentParamName : string;
i : Integer;
startPos : Integer;
endPos : Integer;
begin
fullCmd := GetCmdTail
// default to end of string, in case the option is the last item
endPos := Length(fullCmd);
for i := 0 to ParamCount-1 do
begin
// extract parameter name (eg, "/Option=")
currentParamName := Copy(ParamStr(i), 0, pos('=',ParamStr(i)));
// once found, we want the following item
if (startPos > 0) then
begin
endPos := pos(currentParamName,fullCmd)-2; // -1 to move back to actual end position, -1 for space
break; // exit loop
end;
if (CompareText(currentParamName, '/'+ParamName+'=') = 0) then // case-insensitive compare
begin
// found target item, so save its string position
StartPos := pos(currentParamName,fullCmd)+2+Length(ParamName);
end;
end;
if ((fullCmd[StartPos] = fullCmd[EndPos])
and ((fullCmd[StartPos] = '"') or (fullCmd[StartPos] = ''''))) then
begin
// exclude surrounding quotes
Result := Copy(fullCmd, StartPos+1, EndPos-StartPos-1);
end
else
begin
// return as-is
Result := Copy(fullCmd, StartPos, EndPos-StartPos+1);
end;
end;
You can access this with {code:ParamStrWithQuotes|abc}
When invoking the setup.exe, you do have to escape the quotes, so one of the following works for that:
setup.exe /abc="-arg ""C:\path with spaces"" -moreargs"
or
setup.exe /abc='-arg "C:\path with spaces" -moreargs'

Related

Replacing all the instances of a given character in a string except when it is framed by an other specific character

I'm looking for a simple/performant/elegant way for replacing all the instances of a given charater within a string except when it is framed by an other specific charater. As an Example :
I want to replace in the string a,b,c,"d,e,f,g",h,i,j all the , characters by # except when they are framed by ". The expected result is : a#b#c#"d,e,f,g"#h#i#j.
Any idea welcomed.
Here is my suggestion as a PL/pgSQL block that - if relevant - can be amended/shaped as a function.
Basically it extracts, stores and replaces the "immune" parts of the string (these enclosed in double quotes), replaces the commas with hashes and then replaces back the "immune" parts. IMMUNE_PATTERN may need to be amended too.
do language plpgsql
$$
declare
target_text text := 'a,b,c,"d,e,f,g",h,i,"d2,e2,f2,g2",j'::text;
IMMUNE_PATTERN constant text := '__%s__';
immune_parts text[];
immune text;
i integer;
begin
immune_parts := array(select * from regexp_matches(target_text,'"[^"]+"','g'));
for immune, i in select * from unnest(immune_parts) with ordinality loop
target_text := replace(target_text, immune, format(IMMUNE_PATTERN, i));
end loop;
target_text := replace(target_text, ',', '#');
for immune, i in select * from unnest(immune_parts) with ordinality loop
target_text := replace(target_text, format(IMMUNE_PATTERN, i), immune);
end loop;
raise notice '%', target_text;
end;
$$;
The result is that
a,b,c,"d,e,f,g",h,i,"d2,e2,f2,g2",j becomes
a#b#c#"d,e,f,g"#h#i#"d2,e2,f2,g2"#j

procedure Get (Item : out String); ( in a Function)

I'm trying to return a global variable's String value , and want to use the function who use it , later in a procedure .
function get_name return String
is begin
Put_line("Your name?");
Get(name); -- name is in "globals"
put(name);
return name;
end get_name;
The package file =
package globals
is
name : String(1..20) ;
end globals;
Here the "Get" which is used in the function =
procedure Get (Item : out String);
Now , if i use the fonction in a procedure , it compile but =
At launching , no get is executing , the program "create" a "skip" line !!?
So , is it possible to use this procedure Get in a function ??
And how do you call the function who contain it , after ??
If you call the procedure Get(Item : out String), then the number of characters you read will have to be exactly 20 characters.
If you want to use the function get, you'll need to initialise a variable with its value, or pass it through as a parameter. e.g.
x : string := get_line; -- functional version that will read an entire line
or
put(get_line); -- read and entire line, pass it immediately to a procedure
As to why your input is skipping over the get, and not reading anything, this is probably because you have previously read some input, and have left a newline/end of line marker in the input. This happens often if you read numbers.
e.g. input is
34\nThe next line\n
If you read an integer, the file pointer will show you are at...
34\nThe next line\n
..^
Then you ask for a get_line, and you'll end up only reading to the end of the line (where you currently are) and you'll have a empty string, and not have read the next line.
The solution is to have a skip_line after each get.
So
get(number); skip_line;
declare
input : string := get_line;
begin
...

inno setup - Delete arbitrary substring from a string

I need to delete a substring which is arbitrary each time. For example:
..\HDTP\System\*.u should become ..\System\*.u and/or ..\New Vision\Textures\*.utx should become ..\Textures\*.utx. More specifically: Ignore the first three characters, delete whatever comes after, until the next \ character (including that character), leave the rest of the string intact. Could you, please, help me with this? I know, I have the worst explaining skills in the whole world, if something isn't clear, I'll try to explain again.
This is a little bit of copy and split work for Inno Setup but I have here a function for you with some extra comments.
Read it carefully as it isn't tested properly and if you have to edit it you will
have to know what it is doing ;)
function FormatPathString(str : String) : String;
var
firstThreeChars : String;
charsAfterFirstThree : String;
tempString : String;
finalString : String;
dividerPosition : Integer;
begin
firstThreeChars := Copy(str, 0, 3); //First copy the first thee character which we want to keep
charsAfterFirstThree := Copy(str,4,Length(str)); //copy the rest of the string into a new variable
dividerPosition := Pos('\', charsAfterFirstThree); //find the position of the following '\'
tempString := Copy(charsAfterFirstThree,dividerPosition+1,Length(charsAfterFirstThree)-dividerPosition); //Take everything after the position of '\' (dividerPosition+1) and copy it into a temporary string
finalString := firstThreeChars+tempString; //put your first three characters and your temporary string together
Result := finalString; //return your final string
end;
And this is how you would call it
FormatPathString('..\New Vision\Textures\*.utx');
You will have to rename the function and the var's so that it will match your program but I think this will help you.

String manipulation in ada

I am getting a path of directory in a string , like "C:\Users\Me\Desktop\Hello”, and I am trying to get the last directory, but without success.
I tried a lot of manipulation on the string but in the end of the day i stayed with nothing... i will be grateful to get some help. Thanks !
Here was my first idea :
Get_Line(Line, Len);
while (Line /="") loop
FirstWord:=Index(Line(1..Len),"\")+1;
declare
NewLine :String := (Line(FirstWord .. Len));
begin
Line:=NewLine ;
end;
end loop;
I know its not working (I can’t assign NewLine to Line because there isn't a match between their lengths), and now I am stuck.
I’m assuming you want to manipulate directory (and file) names, rather than just any old string?
In which case you should look at the standard library packages Ada.Directories (ARM A.16) and Ada.Directories.Hierarchical_File_Names (ARM A.16.1):
with Ada.Directories;
with Ada.Text_IO; use Ada.Text_IO;
procedure Tal is
Line : constant String := "C:\Users\Me\Desktop\Hello";
begin
Put_Line ("Full_Name: "
& Ada.Directories.Full_Name (Line));
Put_Line ("Simple_Name: "
& Ada.Directories.Simple_Name (Line));
Put_Line ("Containing_Directory: "
& Ada.Directories.Containing_Directory (Line));
Put_Line ("Base_Name: "
& Ada.Directories.Base_Name (Line));
end Tal;
On the other hand, if you’re trying to work out plain string manipulation, you could use something like
with Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;
procedure Tal is
function Get_Last_Word (From : String;
With_Separator : String)
return String is
Separator_Position : constant Natural :=
Ada.Strings.Fixed.Index (Source => From,
Pattern => With_Separator,
Going => Ada.Strings.Backward);
begin
-- This will fail if there are no separators in From
return From (Separator_Position + 1 .. From'Last); --'
end Get_Last_Word;
Line : constant String := "C:\Users\Me\Desktop\Hello";
Last_Name : constant String := Get_Last_Word (Line, "\");
begin
Put_Line (Last_Name);
end Tal;
As you can see, putting the logic in Get_Last_Word allows you to hoist Last_Name out of a declare block. But it will never be possible to overwrite a fixed string with a substring of itself (unless you’re prepared to deal with trailing blanks, that is): it’s much better never to try.

Printing a string as a variable in Pascal

This is the string variable that I have:
question1 := 'Please enter 1, 2 or 3.';
I also have a function which is supposed to print out the question1 variable, but it generates the question number before printing. Here's a fragment of the function, which turns the question number (question : integer) into a string variable (test : string) and then concatenates the string 'question' with this string variable (test : string).
str(question,test);
test := concat('question',test);
writeln(test);
The result of this writeln is 'question1' (without the quotes). I want it to output the question1 variable as a text string, not just this variable's name, so that the writeln prints Please enter 1, 2 or 3. I've tried writeln(question1) and it works, however, it appears that my function above (or the fragment of it) does this: writeln('question1'). How do I solve this?
Pascal doesn't support dynamic name resolution, like you want. You might consider using arrays instead:
…
var
Questions: array[1..3] of string;
…
procedure InitQuestions;
begin
Questions[1] := 'Please enter 1, 2 or 3.';
Questions[2] := '…';
Questions[3] := '…';
end;
procedure YourFunction(question: Integer);
begin
…
WriteLn(Questions[question]);
…
end;
…
begin
…
InitQuestions;
…
YourFunction(1);
…
end.
As far as I can figure out from your question, you need the following
writeln (question1, 'question ', question);
You don't need to concatenate string values, nor do you need to convert the question number to a string - writeln has the magical ability to accept any number of variables and print them according to their default format (strings, integers, reals and booleans).
In fact, you don't really need to create the 'question1' variable - you could simply write
writeln ('Please enter 1, 2 or 3. Question', question);

Resources