Search For a File
Author: KiCHiK (talk, contrib) |
Description
This function searches a given directory and all of its subdirectories for a certain file (or directory). Whenever it finds a match it calls a given callback function. If the callback function returns "stop" the function will stop searching.
The function gets a directory to search, a file/directory name and an address to a callback function (use GetFunctionAddress) through the stack.
This function is based on a code originally written by Justin Frankel.
Warning: do not push a directory with a trailing backslash. To remove the trailing backslash automatically use (assuming the input is in $0):
Push $0 # directory to remove trailing backslash from. Stack: $0(with backslash) Exch $EXEDIR # exchange with a built-in dir var - exedir will do. # NSIS automatically removes the trailing backslash. Stack: $EXEDIR(original) Exch $EXEDIR # restore original dir var. Stack: $0(without backslash) Pop $0 # and pop the directory without the backslash. Stack: <clean>
Forum user [user:banaman8d banaman8d] have written a variant of this script that lets the callback function do the comparison. Click here to get it.
A simple macro is included to simplify the process of getting your callback function's address.
!macro CallFindFiles DIR FILE CBFUNC Push "${DIR}" Push "${FILE}" Push $0 GetFunctionAddress $0 "${CBFUNC}" Exch $0 Call FindFiles !macroend
Callback Function Syntax
- Do not use $R0-$R6 in the function unless you are saving their old content*Return a "stop" value if you want to stop searching, anything else to continue
- Always return a value through the stack (Push) to prevent stack corruption*Do not push values on the stack without poping them later unless it's the return value
Example of Callback Function
Function MyCallbackFunction Exch $0 DetailPrint $0 Pop $0 Push "stop" FunctionEnd
The Function Code
Function FindFiles Exch $R5 # callback function Exch Exch $R4 # file name Exch 2 Exch $R0 # directory Push $R1 Push $R2 Push $R3 Push $R6 Push $R0 # first dir to search StrCpy $R3 1 nextDir: Pop $R0 IntOp $R3 $R3 - 1 ClearErrors FindFirst $R1 $R2 "$R0\*.*" nextFile: StrCmp $R2 "." gotoNextFile StrCmp $R2 ".." gotoNextFile StrCmp $R2 $R4 0 isDir Push "$R0\$R2" Call $R5 Pop $R6 StrCmp $R6 "stop" 0 isDir loop: StrCmp $R3 0 done Pop $R0 IntOp $R3 $R3 - 1 Goto loop isDir: IfFileExists "$R0\$R2\*.*" 0 gotoNextFile IntOp $R3 $R3 + 1 Push "$R0\$R2" gotoNextFile: FindNext $R1 $R2 IfErrors 0 nextFile done: FindClose $R1 StrCmp $R3 0 0 nextDir Pop $R6 Pop $R3 Pop $R2 Pop $R1 Pop $R0 Pop $R5 Pop $R4 FunctionEnd
A Complete Example
This example will look for Winamp.exe versioned 2.9+ all over the C: drive.
Name "FindFiles example" OutFile FindFiles.exe ShowInstDetails show Function FindFiles Exch $R5 # callback function Exch Exch $R4 # file name Exch 2 Exch $R0 # directory Push $R1 Push $R2 Push $R3 Push $R6 Push $R0 # first dir to search StrCpy $R3 1 nextDir: Pop $R0 IntOp $R3 $R3 - 1 ClearErrors FindFirst $R1 $R2 "$R0\*.*" nextFile: StrCmp $R2 "." gotoNextFile StrCmp $R2 ".." gotoNextFile StrCmp $R2 $R4 0 isDir Push "$R0\$R2" Call $R5 Pop $R6 StrCmp $R6 "stop" 0 isDir loop: StrCmp $R3 0 done Pop $R0 IntOp $R3 $R3 - 1 Goto loop isDir: IfFileExists "$R0\$R2\*.*" 0 gotoNextFile IntOp $R3 $R3 + 1 Push "$R0\$R2" gotoNextFile: FindNext $R1 $R2 IfErrors 0 nextFile done: FindClose $R1 StrCmp $R3 0 0 nextDir Pop $R6 Pop $R3 Pop $R2 Pop $R1 Pop $R0 Pop $R5 Pop $R4 FunctionEnd !macro CallFindFiles DIR FILE CBFUNC Push "${DIR}" Push "${FILE}" Push $0 GetFunctionAddress $0 "${CBFUNC}" Exch $0 Call FindFiles !macroend Function FindWinamp29 Pop $0 GetDLLVersion $0 $1 $3 IntOp $2 $1 & 0x0000FFFF IntOp $1 $1 / 0x00010000 IntOp $4 $3 & 0x0000FFFF IntOp $3 $3 / 0x00010000 DetailPrint "$1.$2.$3.$4 - $0" StrCmp "$1.$2" "2.9" 0 not StrCpy $9 $0 DetailPrint "This is the one! Stopping search..." Push "stop" Return not: Push "go" FunctionEnd Section "Find Winamp" !insertmacro CallFindFiles C: winamp.exe FindWinamp29 StrCmp $9 "" 0 +2 DetailPrint "Couldn't find Winamp 2.9+" SectionEnd