Does NSIS have eval functionality? - scripting

For example, is there a way to do something along these lines?
Eval "MessageBox MB_OK 'Hello, World!'"
This is obviously a useless example, but I feel that such functionality would be useful.

The NSIS compiler (MakeNSIS) parses text files and writes out binary instructions into the generated setup. The setup application itself can only execute the instructions known at compile time.
Most instructions accept variables as their parameters so you can get different behavior. Here is a rather pointless example of that:
Page InstFiles
Function MaybeShowMessageBox
IntCmp $0 0 skip
MessageBox MB_OK "$1"
skip:
FunctionEnd
Section
StrCpy $0 1 ; Display it
StrCpy $1 "Hello World"
Call MaybeShowMessageBox
StrCpy $1 "Goodbye World"
Call MaybeShowMessageBox
StrCpy $0 0 ; Don't display it
Call MaybeShowMessageBox
StrCpy $0 0
StrCpy $2 "$WinDir" 1 ; Get the first character
StrCmp $2 "C" "" skipWinDirMessage
StrCpy $0 1
skipWinDirMessage:
StrCpy $1 "$WinDir is on drive C"
Call MaybeShowMessageBox
SectionEnd
You would ordinarily never write code like that. IntFmt is as close to Eval as you are going to get but it only operates on numbers:
ShowInstDetails show
Section
StrCpy $1 42
IntFmt $0 "%d" $1
DetailPrint "$1 as a number: $0"
IntFmt $0 "%#.4x" $1
DetailPrint "$1 as a hex number with a >= 4 width: $0"
IntFmt $0 "%c" $1
DetailPrint "$1 as a character: $0"
SectionEnd

Related

NSIS FileRead conditional override

I should please need your help as I am not a programmer (please have mercy with me).
I should need to add in an existing NSIS script a function that does the following:
Checks the presence of a value called 169.254.1.1 into a INI file named myfile.ini and placed in
c:\mydirectory
Should it find this value into myfile.ini it should not do anything else (i.e. let the script do the next steps).
Should it NOT find this value into myfile.ini it should override the file
with another one by silently copying it from c:\dummyfile\myfile.ini
and then let the script do the next steps.
I do not know how to tell NSIS to do this, may I please ask for your help?
Thanks in advance for your expertise.
Meleena.
If your file is an actual .INI file with [sections] then you can just use the INI functions:
Section "Prepare INI example"
InitPluginsDir
WriteIniStr "$PluginsDir\MyFile.ini" ServerInfo ServerAddress "169.254.1.1"
WriteIniStr "$PluginsDir\Dummy.ini" ServerInfo ServerAddress "127.0.0.1"
SectionEnd
!include LogicLib.nsh
Section
ReadIniStr $0 "$PluginsDir\MyFile.ini" ServerInfo ServerAddress
ReadIniStr $1 "$PluginsDir\MyFile.ini" ServerInfo ServerAddressBck
${If} $0 != "169.254.1.1"
${AndIf} $1 != "169.254.1.1"
CopyFiles /SILENT /FILESONLY "$PluginsDir\Dummy.ini" "$PluginsDir\MyFile.ini"
${EndIf}
SectionEnd
If you don't have a real .INI file, just a file with name=value pairs then you have to parse manually:
Section "Prepare fake INI example"
InitPluginsDir
FileOpen $0 "$PluginsDir\MyFile.ini" w
FileWrite $0 'Foo$\r$\n'
FileWrite $0 'Bar$\r$\n'
FileWrite $0 'ServerAddress=169.254.1.1$\r$\n'
FileWrite $0 'Baz$\r$\n'
FileClose $0
WriteIniStr "$PluginsDir\Dummy.ini" ServerInfo ServerAddress "127.0.0.1"
SectionEnd
!include LogicLib.nsh
Function TrimTrailingNewlinesAndSpaces
Exch $1
Push $2
again:
StrCpy $2 $1 "" -1
StrCmp $2 " " +3
StrCmp $2 "$\r" +2
StrCmp $2 "$\n" +1 +3
StrCpy $1 $1 -1
Goto again
Pop $2
Exch $1
FunctionEnd
Section
StrCpy $2 "" ; Have not found the magic line
ClearErrors
FileOpen $0 "$PluginsDir\MyFile.ini" r
loop:
FileRead $0 $1
IfErrors done
Push $1
Call TrimTrailingNewlinesAndSpaces
Pop $1
${If} $1 == "ServerAddress=169.254.1.1"
${OrIf} $1 == "ServerAddressBck=169.254.1.1"
StrCpy $2 1
${Else}
Goto loop
${EndIf}
done:
FileClose $0
${If} $2 = 0
CopyFiles /SILENT /FILESONLY "$PluginsDir\Dummy.ini" "$PluginsDir\MyFile.ini"
${EndIf}
SectionEnd

How to replace all escape sequences with non-escaped equivalent with unix utilities (sed/tr/awk)

I'm processing a Wireshark config file (dfilter_buttons) for display filters and would like to print out the filter of a given name. The content of file is like:
Sample input
"TRUE","test","sip contains \x22Hello, world\x5cx22\x22",""
And the resulting output should have the escape sequences replaced, so I can use them later in my script:
Desired output
sip contains "Hello, world\x22"
My first pass is like this:
Current parser
filter_name=test
awk -v filter_name="$filter_name" 'BEGIN {FS="\",\""} ($2 == filter_name) {print $3}' "$config_file"
And my output is this:
Current output
sip contains \x22Hello, world\x5cx22\x22
I know I can handle these exact two escape sequences by piping to sed and matching those exact two sequences, but is there a generic way to substitutes all escape sequences? Future filters I build may utilize more escape sequences than just " and , and I would like to handle future scenarios.
Using gnu-awk you can do this using split, gensub and strtonum functions:
awk -F '","' -v filt='test' '$2 == filt {n = split($3, subj, /\\x[0-9a-fA-F]{2}/, seps); for (i=1; i<n; ++i) printf "%s%c", subj[i], strtonum("0" substr(seps[i], 2)); print subj[i]}' file
sip contains "Hello, world\x22"
A more readable form:
awk -F '","' -v filt='test' '
$2 == filt {
n = split($3, subj, /\\x[0-9a-fA-F]{2}/, seps)
for (i=1; i<n; ++i)
printf "%s%c", subj[i], strtonum("0" substr(seps[i], 2))
print subj[i]
}' file
Explanation:
Using -F '","' we split input using delimiter ","
$2 == filt we filter input for $2 == "test" condition
Using /\\x[0-9a-fA-F]{2}/ as regex (that matches 2 digit hex strings) we split $3 and save split tokens into array subj and matched separators into array seps
Using substr we remove first char i.e \\ and prepend 0
Using strtonum we convert hex string to equivalent ascii number
Using %c in printf we print corresponding ascii character
Last for loop joins $3 back using subj and seps array elements
Using GNU awk for FPAT, gensub(), strtonum(), and the 3rd arg to match():
$ cat tst.awk
BEGIN { FPAT="([^,]*)|(\"[^\"]*\")"; OFS="," }
$2 == ("\"" filter_name "\"") {
gsub(/^"|"$/,"",$3)
while ( match($3,/(\\x[0-9a-fA-F]{2})(.*)/,a) ) {
printf "%s%c", substr($3,1,RSTART-1), strtonum(gensub(/./,0,1,a[1]))
$3 = a[2]
}
print $3
}
$ awk -v filter_name='test' -f tst.awk file
sip contains "Hello, world\x22"
The above assumes your escape sequences are always \x followed by exactly 2 hex digits. It isolates every \xHH string in the input, replaces \ with 0 in that string so that strtonum() can then convert the string to a number, then uses %c in the printf formatting string to convert that number to a character.
Note that GNU awk has a debugger (see https://www.gnu.org/software/gawk/manual/gawk.html#Debugger) so if you're ever not sure what any part of a program does you can just run it in the debugger (-D) and trace it, e.g. in the following I plant a breakpoint to tell awk to stop at line 1 of the script (b 1), then start running (r) and the step (s) through the script printing the value of $3 (p $3) at each line so I can see how it changes after the gsub():
$ awk -D -v filter_name='test' -f tst.awk file
gawk> b 1
Breakpoint 1 set at file `tst.awk', line 1
gawk> r
Starting program:
Stopping in BEGIN ...
Breakpoint 1, main() at `tst.awk':1
1 BEGIN { FPAT="([^,]*)|(\"[^\"]*\")"; OFS="," }
gawk> p $3
$3 = uninitialized field
gawk> s
Stopping in Rule ...
2 $2 == "\"" filter_name "\"" {
gawk> p $3
$3 = "\"sip contains \\x22Hello, world\\x5cx22\\x22\""
gawk> s
3 gsub(/^"|"$/,"",$3)
gawk> p $3
$3 = "\"sip contains \\x22Hello, world\\x5cx22\\x22\""
gawk> s
4 while ( match($3,/(\\x[0-9a-fA-F]{2})(.*)/,a) ) {
gawk> p $3
$3 = "sip contains \\x22Hello, world\\x5cx22\\x22"

Why doesn't awk and gsub remove only the dot?

This awk command:
awk -F ',' 'BEGIN {line=1} {print line "\n0" gsub(/\./, ",", $2) "0 --> 0" gsub(/\./, ",", $3) "0\n" $10 "\n"; line++}' file
is supposed to convert these lines:
Dialogue: 0,1:51:19.56,1:51:21.13,Default,,0000,0000,0000,,Hello!
into these:
1273
01:51:19.560 --> 01:51:21.130
Hello!
But somehow I'm not able to make gsub behave to replace the . by , and instead get 010 as both gsub results. Can anyone spot the issue?
Thanks
The return value from gsub is not the result from the substitution. It returns the number of substitutions it performed.
You want to gsub first, then print the modified string, which is the third argument you pass to gsub.
awk -F ',' 'BEGIN {line=1}
{ gsub(/\./, ",", $2);
gsub(/\./, ",", $3);
print line "\n0" $2 "0 --> 0" $3 "0\n" $10 "\n";
line++}' file
Another way is to use GNU awk's gensub instead of gsub:
$ awk -F ',' '
{
print NR ORS "0" gensub(/\./, ",","g", $2) "0 --> 0" gensub(/\./, ",","g",$3) "0" ORS $10 ORS
}' file
Output:
1
01:51:19,560 --> 01:51:21,130
Hello!
It's not as readable as the gsub solution by #tripleee but there is a place for it.
Also, I replace the line with builtin NR and \ns with ORS.

How to supress original line at gawk

why does gawk write the input line first?
ws#i7$ echo "8989889898 jAAA_ALL_filenames.txt" | gawk 'match($0, /([X0-9\\\-]{9,13})/, arr); {print arr[1];}'
my output
8989889898 jAAA_ALL_filenames.txt
8989889898
I do not want that the same first line is printed.
Thanks
Walter
You have a stray semicolon in there.
$ echo "8989889898 jAAA_ALL_filenames.txt" | gawk 'match($0, /([X0-9\\\-]{9,13})/, arr); {print arr[1];}'
8989889898 jAAA_ALL_filenames.txt
8989889898
$ echo "8989889898 jAAA_ALL_filenames.txt" | gawk 'match($0, /([X0-9\\\-]{9,13})/, arr) {print arr[1];}'
8989889898
The semicolon after match($0, /([X0-9\\\-]{9,13})/, arr) means that your script is effectively:
match($0, /([X0-9\\\-]{9,13})/, arr) { print $0 } # default action block inserted
1 {print arr[1];} # default condition inserted
match returns a "true" value so the whole line gets printed.
To fix it, remove the semicolon:
match($0, /([X0-9\\\-]{9,13})/, arr) {print arr[1];}
Now the code only has one condition { action } structure, as you intended, so it does what you want.

Why does awk "not in" array work just like awk "in" array?

Here's an awk script that attempts to set difference of two files based on their first column:
BEGIN{
OFS=FS="\t"
file = ARGV[1]
while (getline < file)
Contained[$1] = $1
delete ARGV[1]
}
$1 not in Contained{
print $0
}
Here is TestFileA:
cat
dog
frog
Here is TestFileB:
ee
cat
dog
frog
However, when I run the following command:
gawk -f Diff.awk TestFileA TestFileB
I get the output just as if the script had contained "in":
cat
dog
frog
While I am uncertain about whether "not in" is correct syntax for my intent, I'm very curious about why it behaves exactly the same way as when I wrote "in".
I cannot find any doc about element not in array.
Try !(element in array).
I guess: awk sees not as an uninitialized variable, so not is evaluated as an empty string.
$1 not == $1 "" == $1
I figured this one out. The ( x in array ) returns a value, so to do "not in array", you have to do this:
if ( x in array == 0 )
print "x is not in the array"
or in your example:
($1 in Contained == 0){
print $0
}
In my solution for this problem I use the following if-else statement:
if($1 in contained);else{print "Here goes your code for \"not in\""}
Not sure if this is anything like you were trying to do.
#! /bin/awk
# will read in the second arg file and make a hash of the token
# found in column one. Then it will read the first arg file and print any
# lines with a token in column one not matching the tokens already defined
BEGIN{
OFS=FS="\t"
file = ARGV[1]
while (getline &lt file)
Contained[$1] = $1
# delete ARGV[1] # I don't know what you were thinking here
# for(i in Contained) {print Contained[i]} # debuging, not just for sadists
close (ARGV[1])
}
{
if ($1 in Contained){} else { print $1 }
}
In awk commande line I use:
! ($1 in a)
$1 pattern
a array
Example:
awk 'NR==FNR{a[$1];next}! ($1 in a) {print $1}' file1 file2