Find a string pattern in a file from an NSIS script - string

In an NSIS installer script, I'm trying to check if a given httpd.conf file contains the following line :
Include "c:\xxx\yyy.conf"
If so, then my installer script would not append it to the file, otherwise, it would append it.
I've come through {LineFind} but not sure this really makes what i'm trying to achieve.
What could be the simplest way to do a kind of "grep" on a text file from an NSIS script ?
Thank you !

Here is a sample for searching for a given line into a file, using the LogicLib for ease of syntax. The search is stopped as soon as the line is found. This sample works on the sample script itself:
# find.nsi : sample for LineFind from TextFunc.nsh
!include "textfunc.nsh"
!include "logiclib.nsh"
OutFile "find.exe"
!define lookfor `Section` ;will find
;!define lookfor `Sectionn` ;will not find
Var found
Section
StrCpy $found 0
${LineFind} "find.nsi" "/NUL" "1:-1" "GrepFunc"
${if} $found = 1
MessageBox MB_OK "string found"
${else}
MessageBox MB_OK "string NOT found"
${endIf}
SectionEnd
Function GrepFunc
${TrimNewLines} '$R9' $R9
DetailPrint "test for line $R8 `$R9`"
${if} $R9 == "${lookfor}"
StrCpy $found 1 ;set flag
Push "StopLineFind" ;stop find
${else}
Push 0 ;ignore -> continue
${endIf}
FunctionEnd

Related

How to fix "CreateDirectory: Relative paths not supported" when using a variable?

As a newbiew, I am still in the stage of experimenting and building little prototypes. The idea is to build a silent installer that has all settings in multiple sections of a .INI and the users calls the setup with parameter /config={NameOfSection}.
My current situation:
FooBar-install.ini
[PROD]
FOOHOME=c:\FooBar
FooBar.nsi
!include FileFunc.nsh
!include LogicLib.nsh
!insertmacro GetParameters
!insertmacro GetOptions
var /GLOBAL config
var /GLOBAL cmdLineParams
var /global REGAPPKEY
var /global FOOHOME
!define TheName "FooBar"
!define OutFileSuffix "-Install."
!define IniFile "$EXEDIR\${TheName}${OutFileSuffix}ini"
Name "${TheName} ${PRODUCT_VERSION}" ; bei 2 Kunden geht's auch kd-spezifisch ;)
OutFile ${TheName}${OutFileSuffix}exe
RequestExecutionLevel admin
Icon "${NSISDIR}\Contrib\Graphics\Icons\modern-install.ico"
UninstallIcon "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
Section "-aInstaller Section"
ReadINIStr $FOOHOME ${IniFile} $config "FOOHOME"
MessageBox MB_OK "ini=${IniFile} , config=$config, FOOHOME=$FOOHOME"
CreateDirectory "SFOOHOME"
SectionEnd
function .onInit
UserInfo::GetAccountType
pop $0
${If} $0 != "admin" ;Require admin rights on NT4+
MessageBox mb_iconstop "Administrator rights required!"
SetErrorLevel 740 ;ERROR_ELEVATION_REQUIRED
${Else}
MessageBox MB_OK "onInit"
${EndIf}
; Get parameters
${GetParameters} $cmdLineParams
; /? param (help)
ClearErrors
${GetOptions} $cmdLineParams '/?' $R0
IfErrors +3 0
MessageBox MB_OK "Befehlszeilenparameter /config={{name}} verweist auf einen Abschnitt aus ${TheName}${OutFileSuffix}ini mit div. Parametern zur Steuerung des Setup"
Abort
Call parseParameters
Pop $R0
FunctionEnd
Function parseParameters
; /config
${GetOptions} $cmdLineParams '/config=' $R0
${If} ${Errors}
StrCpy $config "errPROD"
${Else}
StrCpy $config $R0
${Endif}
FunctionEnd
Problem
If I try to compile this, I get the msg
CreateDirectory: Relative paths not supported
Usage: CreateDirectory directory_name
Questions
I do not understand why this error comes up at compile time. When using a variable (especially in this situation where the variable depends on user-input), it does not seem to make sense to complain about the argument when it is not known.
How can I avoid this probolem?
A little puzzle that messes me up is the syntax to refer to variables.The statement MessageBox MB_OK "ini=${IniFile} , config=$config, FOOHOME=$FOOHOME" shows that. I found that I needed to enclose IniFile in {} in order to display its value (I commented out the CreateDir-line to compile the installer and check my assumptions). When do I have to use {}?
If you see any other "unusual" things in my little script, I'd be happy to know ;)
You have a typo, change CreateDirectory "SFOOHOME" to CreateDirectory "$FOOHOME"
You might want to read the documentation again to learn the basics; ${define}, $(langstring) and $variable.

How to use System::Call and MessageBox when calling the C++ dll method from the NSIS script

My requirement is I need to test "If the device is present before we try to disable the Native Power" from the system.
For that I need to call the below function that is there in testutil.dll
BOOL IsTherePower()
Below is the NSIS script to call this function:
Name "PowerTest"
OutFile "PowerTest.exe"
InstallDir $PROGRAMFILES\PowerTest
Section "PowerTest(required)"
SectionIn RO
DetailPrint "PowerTest"
; Set output path to the installation directory. Here is the path C:\Program Files\PowerTest
SetOutPath $INSTDIR
; Give the dll path
File E:\Code\Source\Validatepower.exe
File E:\Code\Source\testutil.dll
File E:\Code\Source\ntutil.dll
File E:\Code\Source\dlgdll.dll
System::Call "$INSTDIR\testutil.dll::IsTherePower() i.r0"
Pop $0
MessageBox MB_OK "Return value = $R0, lasterr = $0"
IntCmp $R0 1 OkToInstall CancelInstall
CancelInstall:
Abort "Not allowed to install"
OkToInstall:
Do the install
With the above code when i run the application i am getting "Return value=, lasterr = error". I am not sure why i am getting the "Return value" blank (null). Did I miss anything here?
I have written "System::Call" and "MessageBox" but not sure what they are doing.
Here I want to know what is "i.r0" from System::Call
And also what is "Pop $0"?
You are using the wrong register. r0 in System syntax is $0, not $R0 (R0 and r10 is $R0). System::Call "$INSTDIR\drvutil.dll::IsUPSPresent() i.r0" puts the INT32 return value in $0 and then you overwrite $0 with the Pop and your stack happened to be empty.
If you need to call GetLastError() then you must append the ?e option:
System::Call "$INSTDIR\drvutil.dll::IsUPSPresent() i.r0 ?e" ; Pushes error code on top of the stack
Pop $1 ; Get error code
DetailPrint "Return=$0 LastError=$1"
?e pushes the last error on the stack and Pop extracts the top item on the stack.
I can confirm that my code works, I tested in on a dummy .DLL. If it does not work for you then System::Call is unable to load the .DLL or find the exported function. The most likely issue is that you have not exported the function correctly in your .DLL.
Inspect your .DLL with Dependency Walker, it is supposed to look like this:
not
You can also try do verify it manually in NSIS:
!include LogicLib.nsh
Section
SetOutPath $InstDir
File drvutil.dll
System::Call 'KERNEL32::LoadLibrary(t "$InstDir\drvutil.dll")p.r8 ?e'
Pop $7
${If} $8 P<> 0
MessageBox MB_OK 'Successfully loaded "$InstDir\drvutil.dll" # $8'
System::Call 'KERNEL32::GetProcAddress(pr8, m "IsUPSPresent")p.r9 ?e'
Pop $7
${If} $9 P<> 0
MessageBox MB_OK 'Successfully found "IsUPSPresent" # $9'
${Else}
MessageBox MB_ICONSTOP 'Unable to find "IsUPSPresent", error $7'
${EndIf}
System::Call 'KERNEL32::FreeLibrary(pr8)'
${Else}
MessageBox MB_ICONSTOP 'Unable to load "$InstDir\drvutil.dll", error $7'
${EndIf}

NSIS write to a file on APPDATA inside a subdir

I'm trying to append 1 line of text into a file on the $APPDATA folder which is inside of a folder that's generated randomly, so I don't know it's full path like:
C:\Users\MyUser\AppData\Roaming\MyApp\RANDOM_CRAP\config.json
While RANDOM_CRAP looks like some random string for a folder, like G4F6Hh3L.
What are my options here? Do I need to use either Search For a File or Search for a File or Directory (Alternative) ? It's a given that the only subfolder of MyApp folder is the RANDOM_CRAP folder, that contains the file I want to edit.
If there's no other way to access this file without searching for it, I've tried doing so but couldn't get this to work. (I'm very new to NSIS)
This is what I've tried (With the alternative approach):
Push "config.json"
Push "$APPDATA"
Push $0
GetFunctionAddress $0 "myCallback"
Exch $0
Push "1" ; include subfolders because my desired file is in the random folder
Push "0" ; no need the . option
Call SearchFile
Than I've copied the SearchFile code from this post and put a callback:
Function myCallback
Exch 3
Pop $R4
MessageBox MB_OK "Callback executing!"
MessageBox MB_OK "File is at : $R4"
FunctionEnd
I know that SearchFile is running (I've put a MessageBox inside) but myCallback isn't seemed to be called.
Many thanks.
If you are looking for a known file and only one directory in the path is unknown then you can probably just do a basic FindFirst search:
Section
; Create "random" folders:
CreateDirectory "$temp\MyApp\foo"
System::Call kernel32::GetTickCount()i.r1 ; random enough
CreateDirectory "$temp\MyApp\bar$1"
FileOpen $0 "$temp\MyApp\bar$1\config.json" a
FileWrite $0 '{bogus:"data"}$\n'
FileClose $0
CreateDirectory "$temp\MyApp\baz"
!include LogicLib.nsh
; Do the actual search:
StrCpy $9 "$temp\MyApp" ; The folder we are going to search in
FindFirst $0 $1 "$temp\MyApp\*"
loop:
StrCmp $1 "" done
${If} ${FileExists} "$9\$1\config.json"
DetailPrint "Found: $9\$1\config.json"
${EndIf}
FindNext $0 $1
Goto loop
done:
FindClose $0
SectionEnd

why need in Locate plugin find section the following line?

Section
${Locate} "C:\ftp" "/L=F /M=RPC DCOM.rar /S=1K" "Example1"
; 'RPC DCOM.rar' file in 'C:\ftp' with size 1 Kb or more
IfErrors 0 +2
MessageBox MB_OK "Error" IDOK +2
MessageBox MB_OK "$$R0=$R0"
SectionEnd
Function Example1
StrCpy $R0 $R9
; $R0="C:\ftp\files\RPC DCOM.rar"
MessageBox MB_YESNO '$R0$\n$\nFind next?' IDYES +2
** StrCpy $0 StopLocate ** -> why needs this line?
Push $0
FunctionEnd
Thx for the help!
${Locate} has a loop that looks for files that matches your input and when it finds one it calls your callback-function (Example1 in this case). It searches subdirectories by default so there could be more than one "RPC DCOM.rar" file.
If you only care about the first file then you can stop it from searching other subdirectories by pushing the string "StopLocate" to the stack. Pushing anything else will continue the search...

How to include one jre based on os in nsis

I have two jre file in my source code.I have created two exe files based on user inputs and changed the outfile named.
Now based on outfile I want to include jre file.if outfile contains 64 then include 64 bit jre else 32 bit jre.
I have tried below code.but its not working?
!ifdef PLATFORM64
OutFile setup64.exe
!else
OutFile setup32.exe
!endif
Function .Oninit
${StrContains} $0 "64" OutFile
File /oname=$JRE_INSTALLER_FILE_NAME "$TEMP\jre-6u29-windows-x64.exe"
FunctionEnd
Questions:
1.How to include any one jre file based on outfile named?
Probably something like this:
!ifdef PLATFORM64
OutFile setup64.exe
!else
OutFile setup32.exe
!endif
Function .Oninit
var JRE_PLATFORM_FILE
${StrContains} $0 "64" $OutFile
StrCmp $0 "" notfound
StrCpy $JRE_PLATFORM_FILE "file-name-for-64-bit-JRE"
Goto done
notfound:
StrCpy $JRE_PLATFORM_FILE "file-name-for-32-bit-JRE"
done:
File /oname=$JRE_INSTALLER_FILE_NAME $JRE_PLATFORM_FILE
FunctionEnd

Resources