Search For a File

From NSIS Wiki
Jump to navigationJump to search
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