Archive: I wrote some handy NSIS functions for C Programmers


I wrote some handy NSIS functions for C Programmers
NSIS, unlike Python, does not have a List Type and this is good because it makes the language more like Assembly and therefore faster. However, the handy dandy C functions strtok and strchr allow list-like operations on pure strings. This IMHO goes well beyond the quite limited functionality of SPLIT_STRING and allows arbitrary delimiters, sets of delimiters (i.e. use ",.!? $\n$\t$\r" to split the input as words) and can be called repeatedly until all tokens are found. The resulting list should be tested for "" to terminate any loop. Here is the code:

; StrTok works just like ISO C strtok, where input is modified as in that function
; Parameter 2 is popped and it is the delimiter to split the string with
; Parameter 1 is then popped and it is the string to parse
; The resulting remaining part of input is pushed onto the stack
; The resulting substring is pushed onto the stack
;
; Example:
; Push $Input
; Push $Delim
; Call StrTok
; Pop $0
; Pop $Input
; MessageBox MB_OK "My next token is $0"
; MessageBox MB_OK "The remaining text is $Input"
;
; StrChr works just like ISO C strchr, where DELIM must be 1 char long and error is returned if DELIM is not found
; Parameter 2 is popped and it is the delimiter to split the string with
; Parameter 1 is then popped and it is the string to parse
; The resulting integer offset is pushed onto the stack
;
; Example:
; Push $Input
; Push $Delim
; Call StrChr
; Pop $0
; StrCmp $0 error +3
; MessageBox MB_OK "Delimiter found at offset $R0"
; goto +2
; MessageBox MB_OK "Delemiter not found"

Function StrTok
; Get the Delimiter ($R0)
Exch $R0

; Get the Input ($R1)
; This is second on the stack so switch it to the first and then $R0 becomes second
Exch 1
Exch $R1

; Need 2 more variables
Push $R2 ; A Character from the Input
Push $R3 ; An index into the Input

StrCpy $R3 -1

; Loop
StrTokLoop:
IntOp $R3 $R3 + 1
StrCpy $R2 $R1 1 $R3
StrCmp $R2 "" StrTokBreak
Push $R0 ; Delimiter
Push $R2 ; Character to find in the Delimeter
Call StrChr
Pop $R2 ; The position of old $R2 in the Delimeter
StrCmp $R2 error StrTokLoop

StrTokBreak:
Push $R5
StrCpy $R5 $R0 ; Store the Delimeter in $R5

StrCpy $R0 $R1 $R3 ; $R0 now has the parsed string

; Need to find all the delimeter and delete them
StrTokIsDelim:
IntOp $R3 $R3 + 1
StrCpy $R2 $R1 1 $R3
StrCmp $R2 "" StrTokDone
Push $R5 ; Delimiter
Push $R2 ; Character to find in the Delimeter
Call StrChr
Pop $R2 ; The position of old $R2 in the Delimeter
StrCmp $R2 error "" StrTokIsDelim

StrTokDone:
Pop $R5 ; Don't need the Delimeter copy anymore

StrCpy $R1 $R1 "" $R3 ; $R1 now has the rest of the input

Pop $R3 ; Reversal
Pop $R2 ; Reversal
Exch $R1 ; Push Input back on Stack where $R1 is

; Swap Input with stored $R0 and swap it for the result
Exch 1
Exch $R0 ; Result
FunctionEnd

Function StrChr
; Get the Delimiter ($R0) (What you're looking for)
Exch $R0

; Get the Input ($R1)
; This is second on the stack so switch it to the first and then $R0 becomes second
Exch 1
Exch $R1

; Need 2 more variables
Push $R2 ; A Character from the Input
Push $R3 ; An index into the Input

StrLen $R3 $R0 ; $R2 has the String Length
StrCmp $R3 1 +3

; Error -- Delim has to be 1 char
StrCpy $R0 error
Goto StrChrDone

StrCpy $R3 -1

; Loop
StrChrLoop:
IntOp $R3 $R3 + 1
StrCpy $R2 $R1 1 $R3
StrCmp $R2 "" "" +3

; Error -- Delim not found
StrCpy $R0 error
Goto StrChrDone

StrCmp $R2 $R0 "" StrChrLoop
; End Loop

; Need to move result to $R0
StrCpy $R0 $R3

StrChrDone:
Pop $R3 ; Reversal
Pop $R2 ; Reversal
Pop $R1 ; Reversal
Exch $R0 ; Result
FunctionEnd


Ahnnn... StrTok was already included inside StrFunc.nsh header file (look for it at "Include" subdir) as a command ${StrTok}. And about StrChr, I don't use it much often...

But you can post your functions in the Archive if you want to.


I think StrChr would be handy. Please post to the Archive.


Is better you do it, as not everytime I'm available to update and fix the function at the time. If you know, I have several projects and manage more one is even worse for me. Register at Archive or create a page as anonymous (which doesn't need to register, but anyone can edit the page).