How to access memory buffer natively in NSIS - winapi

I'm reading a file's content into a buffer using
System::Alloc $Size
pop $Buffer
System::Call "Kernel32::ReadFile(i r0, i $Buffer, i $Size, t.,)"
But I can't figure out how to read from (or write into) $Buffer.
Is there a way do to so (preferably natively, but any suggestion would be appreciated).
Thanks
P.S.
I know about System::Copy but it only lets you write into a buffer and only from another buffer
(trying System::Copy 1 $Buffer "A" crashes the executable)

The built-in NSIS functions FileRead/FileReadUTF16LE and FileReadByte can be used to read text and bytes but you can also call Windows functions directly. To read from a memory buffer you have to use the system struct syntax:
Section
InitPluginsDir
FileOpen $0 "$pluginsdir\test.txt" a
DetailPrint "NSIS:"
FileWrite $0 "Foo"
FileSeek $0 0 SET
FileRead $0 $1
DetailPrint |$1|
DetailPrint "System::Call:"
System::Alloc 100
Pop $1
System::Call '*$1(&m3 "Bar")' ; Write ASCII text into buffer using struct syntax
System::Call 'kernel32::WriteFile(i$0,i$1,i3,*i.r2,i0)i.r9'
DetailPrint "Write: OK=$9 ($2 bytes)"
System::Free $1
System::Call '*(&i100)i.r1' ; Alloc a 100 byte buffer using struct syntax
FileSeek $0 0 SET
System::Call 'kernel32::ReadFile(i$0,i$1,i6,*i.r2,i0)i.r9'
DetailPrint "Read: OK=$9 ($2 bytes)"
System::Call '*$1(&m${NSIS_MAX_STRLEN}.r2)' ; Read ASCII text into variable using struct syntax
System::Free $1
DetailPrint |$2|
FileClose $0
SectionEnd

Related

How do I compare whether two numbers have 1 number of difference between them?

Explaining my algorithm:
I'm trying to find out whether My current job for e.g. Write(W) is the same as my previous job, if my current job (W) is the same as my previous job (W) then check whether there's 1 integer of difference between them, for e.g. if the previous job was W9 and my current job is either W8 or W10, then append 0 to my seek array.
I've tried almost every way I could find on the internet to compare integers but none of them work, I continue to receive an invalid arithmetic syntax error when trying to compare current and previous job.
Any ideas?
# Jobs
lastJob=""
currentJob=""
lastNumber=0
currentNumber=0
# Arrays
seek=()
RW=()
shift 3
# Reads final into array
while IFS= read -r line
do
Job+=($line)
done < ./sim.job
#-----------------------------------
# Single HDD Algorithm
#-----------------------------------
for (( i=0; i<=${#Job[#]}; i++ ));
do
currentString="${Job[$i]}"
currentJob=${currentString:0:1}
currentNumber=${currentString:1:3}
if [[ $currentJob == $lastJob ]]
then
if [[ $currentNumber -eq $lastNumber-1 ]] || [[ $currentNumber -eq $lastNumber+1 ]]
then
seek+=(0)
RW+=(60)
else
seek+=(5)
RW+=(60)
fi
else
seek+=(5)
RW+=(60)
fi
lastString="${Job[$i]}"
lastJob=${lastString:0:1}
lastNumber=${currentString:1:3}
done
This prints output:
#-----------------------------------
# Print Information
#-----------------------------------
for (( i=0; i<${#Job[#]}; i++ ));
do
echo -e "${Job[$i]}:${seek[$i]}:${RW[$i]}"
done
Expected Output:
R9:5:60
W9:5:60
W10:0:60
R11:0:60
R13:5:60
R18:5:60
R19:0:60
R20:0:60
R21:0:60
Actual Output:
") syntax error: invalid arithmetic operator (error token is "
R9:5:60
W9:5:60
W10::
R11::
R13::
R18::
R19::
R20::
R21::
sim.job file (Input):
R9
W9
W10
R11
R13
R18
R19
R20
R21
rogue \r were found in my input file, to solve this I used the commands:
To check if \r are in the file: od -c <filename> | tr ' ' '\n' | grep '\r'
To remove rogue \r use: tr -d '\r' < filename
Thanks again #mark-fuso

replace the matched text with the contents of another file using sed inside a shell script

I have a Makefiel which call a shell script. Inside that shell script I have the following sed command:
sed -e '/.section\t.text/{' -e 'r anotherfile.s' -e 'd' -e '}' input.s > output.s
which does not work.
while it works fine when I run this into terminal directly.
I want to search a line ".section .text" in a file and replace it with another file.
What is wrong here?
input.s
.section .data
.addressing Word
_A:
.data.32 0
.section .text
.addressing Word
_main:
LW %GPR27, _C(%GPR0)
NOP
anotherfile.s
.addr_space 32 ; address space is 2^32
.addressing Byte ; byte addressing (default)
.bits_per_byte 8 ; 1 byte consists of 8 bit (default)
.endian Big ; Big endian (default)
.section .text
.org 0x00000000
output.s (should be like this)
.section .data
.addressing Word
_A:
.data.32 0
.addr_space 32 ; address space is 2^32
.addressing Byte ; byte addressing (default)
.bits_per_byte 8 ; 1 byte consists of 8 bit (default)
.endian Big ; Big endian (default)
.section .text
.org 0x00000000
.addressing Word
_main:
LW %GPR27, _C(%GPR0)
NOP
sed is for doing s/old/new, that is all. That's not what you're doing so you shouldn't be using sed so rather than trying to debug something you shouldn't be doing anyway, just use awk:
awk '
NR==FNR { rep = (rep=="" ? "" : rep ORS) $0; next }
/\.section\t\.text/ { $0 = rep }
{ print }
' anotherfile.s input.s > output.s

Parsing disks in zpool

I am looking for a simplest way to parse disks in zpool.
A list of disks in space separated format.
For example below output shows zpool information. Is there any command to get list of physical disks only?
# zpool status pool
pool: pool
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
pool ONLINE 0 0 0
c2d44s2 ONLINE 0 0 0
c2d45s2 ONLINE 0 0 0
c2d46s2 ONLINE 0 0 0
errors: No known data errors
This should work although it might need some fixes for complex zpool status output :
# cat parsezs
awk '
NF != 5 {next}
$1 == "NAME" {getline;show=1;next}
$1 ~ "mirror" {next}
$1 ~ "raidz" {next}
$1 ~ "replacing" {next}
$1 ~ "error" {next}
show == 1 {printf("%s ",$1)}
END {printf("\n")}'
# zpool status pool | parsezs
c2d44s2 c2d45s2 c2d46s2
This will also work and shows you the pool name too. Note that you need nawk, which is a more modern version of Awk on Solaris:
zpool status | nawk 'BEGIN{Disp=0}{if($1=="pool:") {if(Disp!=0) print ""; else Disp = 1; printf("%s ",$2)} ; if($1~"^c[0-9]") printf("%s ",$1)}END{print ""}'
The Disp variable is just to tidy up the output. This is a typcal result:
js_data_san c0t6006016049B13A00B337B4F7F1DDE411d0 c0t6006016093003B0022E5A8A8C833E711d0
rpool c0t5000CCA07D07C764d0 c0t5000CCA07D07C514d0
s10patchchk-ospool c0t6006016093003B00B488CFCF10D8E611d0
vmware_ds_nfs01 c0t6006016049B13A005AE1A9648112E511d0
So in that example rpool and js_data_san each has two devices. It doesn't indicate if they are mirrored or concatenated, but that would be easy to change in the script.

Convert a decimal number to hexadecimal and binary in a shell script

I have a decimal number in each line of a file.txt:
1
2
3
I am trying (for too long now) to write a one-liner script to have an output where each row has a column with the decimal, hexadecimal and the binary. To ease the task we can say that the original number is expressed in a byte. So the maximum value is 255.
I first try to decode each number as a bynary with prepended 0 so to have an 8 bits pattern:
awk '{print "ibase=10;obase=2;" $1}' $1 | bc | xargs printf "%08d\n"
where the outer $1 in the awk statement is file.txt. The output is :
00000001
00000010
00000011
Same for hex with one prepended 0
awk '{printf("0x%02x\n", $1)}' $1
Same as before. The Output is :
0x01
0x02
0x03
Well, the decimal should be just a print:
1
2
3
What I'd like to have is one liner where I have:
1 00000001 0x01
2 00000001 0x02
so basically to put 1. 2. and 3. in each line of the output.
I tried to execute bc (and other command) within awk using system() without success. And a zillion other ways. What is the way you would do it?
The following one-liner should work:
printf "%s %08d 0x%02x\n" "$1" $(bc <<< "ibase=10;obase=2;$1") "$1"
Example output:
$ for i in {1..10}; do printf "%s %08d 0x%02x\n" "$i" $(bc <<< "ibase=10;obase=2;$i") "$i"; done
1 00000001 0x01
2 00000010 0x02
3 00000011 0x03
4 00000100 0x04
5 00000101 0x05
6 00000110 0x06
7 00000111 0x07
8 00001000 0x08
9 00001001 0x09
10 00001010 0x0a
So I searched for a short and elegant awk binary converter. Not satisfied considered this as a challenge, so here you are. A little bit optimzed for size, so I put a readable version below.
The printf at the end specifies how large the numbers should be. In this case 8 bits.
Is this bad code? Hmm, yeah... it's awk :-)
Does of course not work with very huge numbers.
67 characters long awk code:
awk '{r="";a=$1;while(a){r=((a%2)?"1":"0")r;a=int(a/2)}printf"%08d\n",r}'
Edit: 55 characters awk code
awk '{r="";a=$1;while(a){r=a%2r;a=int(a/2)}printf"%08d\n",r}'
Readable version:
awk '{r="" # initialize result to empty (not 0)
a=$1 # get the number
while(a!=0){ # as long as number still has a value
r=((a%2)?"1":"0") r # prepend the modulos2 to the result
a=int(a/2) # shift right (integer division by 2)
}
printf "%08d\n",r # print result with fixed width
}'
And the asked one liner with bin and hex
awk '{r="";a=$1;while(a){r=a%2r;a=int(a/2)}printf"%08d 0x%02x\n",r,$1}'
You don't need bc. Here's a solution using only awk:
Fetch the bits2str function available in the manual
Add this minimal script:
{
printf("%s %s %x\n", $1, bits2str($1), $1)
}
This produces:
$ awk -f awkscr.awk nums
1 00000001 1
2 00000010 2
3 00000011 3

Get string length

Is there a way to find out the length of a string in NSIS?
I am trying to test if a file is empty(has no contents). One way is to read the file and store the contents in a string(called contentStr) then see how long that contentStr string is. If its > 0 then its not empty.
The other method is to check if contentStr == "" but as you can see below it doesn't work. Any empty file never returns 1 when it should:
!macro IsFileEmpty fName res
!insertmacro ReadFile "${fName}" ${res}
StrCmp ${res} "" +1 +2
IntOp ${res} 1 + 0
IntOp ${res} 0 + 0
!macroend
To get a string length, use StrLen :
StrLen $0 "123456" # ==> $0 = 6
If you want to get the file size before trying to read it, look at the technique pointed by Francisco in another answer.
Have you checked your file size is really 0 bytes? Maybe your file has spaces or newline characters... In those cases you'll need to StrRep or Trim your string.
If you just want to know the file size, you can use this macro and function:
!macro FileSize VAR FILE
Push "${FILE}"
Call FileSizeNew
Pop ${VAR}
!macroend
Function FileSizeNew
Exch $0
Push $1
FileOpen $1 $0 "r"
FileSeek $1 0 END $0
FileClose $1
Pop $1
Exch $0
FunctionEnd
More info here:
http://nsis.sourceforge.net/Getting_File_Size
It is a little bit weird to do it this way and it only partially works:
...
StrCmp ${res} "" 0 +2 ; +1 and 0 is the same, jumps to next instruction
StrCpy ${res} 1 ; No need to do math here
IntOp ${res} ${res} + 0 ; NaN + 0 = 0 but what if you read a number from the file?
If the file might start with a number you need to jump like zbynour suggested:
...
StrCmp ${res} "" 0 +3
StrCpy ${res} 1
Goto +2
StrCpy ${res} 0
If you flip the test you can get what you want with the same number of instructions:
!macro IsFileNOTEmpty fName res
!insertmacro ReadFile "${fName}" ${res}
StrCmp ${res} "" +2 0
StrCpy ${res} 1 ; Or IntOp ${res} ${res} | 1 if you really wanted to do extra math ;)
IntOp ${res} ${res} + 0
!macroend
or even better:
!macro IsFileEmpty fName res
!insertmacro ReadFile "${fName}" ${res}
StrLen ${res} ${res} ; If this is enough depends on how you want to deal with newlines at the start of the file (\r or \n)
!macroend
...all of this code assumes you want to test if the file starts with some text, if the file starts with a 0 byte it will always say the file is empty. So if the file has binary content you need to use Francisco R's code that tests the actual size in bytes.
That's because the last line is always executed (in case of ${res} is empty offset will be +1 but next instruction will not be skipped).
The following code should make it working as you expect:
!macro IsFileEmpty fName res
!insertmacro ReadFile "${fName}" ${res}
StrCmp ${res} "" +1 +3
IntOp ${res} 1 + 0
goto end
IntOp ${res} 0 + 0
end:
!macroend

Resources