' FindFiles.vbs
' VBScript program to find files containing specified text string(s).
'
' ----------------------------------------------------------------------
' Copyright (c) 2007 Richard L. Mueller
' Hilltop Lab web site - http://www.rlmueller.net
' Version 1.0 - October 11, 2006
' Version 1.1 - April 19, 2007 - Handle permission denied errors.
' Version 1.2 - April 2, 2008 - Modified syntax.
' Version 1.3 - July 22, 2009 - Added file last modified dates.
' Version 1.4 - October 9, 2009 - Handle up to 3 search strings,
'                                 added file size.
' Version 1.5 - October 22, 2009 - Allow case sensitive searches.
' Version 1.6 - November 24, 2009 - Handle out of memory errors.
'
' Syntax:
'     cscript //nologo FindFiles.vbs <string> <path> <ext> <subs> <case>
' Where:
'     <string> is the text string or strings to search for (required).
'         If more than one string (up to 3), delimit with semicolons.
'         If any string contains ;, escape with \.
'     <path> is the optional path to search.
'     <ext> is an optional comma delimited list of file.
'         extensions (with no spaces between). "." means all extensions.
'     <subs> is True to search all sub folders, False otherwise.
'     <case> is True if search case sensitive, False otherwise.
' For example:
'     cscript //nologo FindFiles.vbs strUser c:\scripts vbs True True
'
' You have a royalty-free right to use, modify, reproduce, and
' distribute this script file in any way you find useful, provided that
' you agree that the copyright owner above has no warranty, obligations,
' or liability for such use.

Option Explicit

Dim objFSO, strSearchPath, strFileExtension, arrFileExtensions
Dim strSearchString, strSearchSubs, strScript, intIndex
Dim intCount, intNumber, k, strCase

intCount = Wscript.Arguments.Count
If (intCount = 0) Then
    Wscript.Echo "Arguments missing"
    Call Syntax
    Wscript.Quit
End If

strSearchString = Wscript.Arguments(0)
Select Case LCase(strSearchString)
    Case "h", "help", "?", "/h", "/help", "/?", "-h", "-help", "-?"
        Call Syntax
        Wscript.Quit
End Select

If (intCount > 1) Then
    strSearchPath = Wscript.Arguments(1)
Else
    strScript = Wscript.ScriptName
    strSearchPath = Wscript.ScriptFullName
    intIndex = InStr(LCase(strSearchPath), LCase(strScript))
    strSearchPath = Left(strSearchPath, intIndex - 1)
    If (Right(strSearchPath, 1) = "\") Then
        strSearchPath = Left(strSearchPath, Len(strSearchPath) - 1)
    End If
End If

If (intCount > 2) Then
    strFileExtension = Wscript.Arguments(2)
    If (strFileExtension = ".") Then
        strFileExtension = ""
    End If
Else
    strFileExtension = ""
End If

If (intCount > 3) Then
    strSearchSubs = Wscript.Arguments(3)
Else
    strSearchSubs = "False"
End If

If (intCount > 4) Then
    strCase = Wscript.Arguments(4)
Else
    strCase = "False"
End If

If (intCount > 5) Then
    Wscript.Echo "Too many arguments"
    Call Syntax
    Wscript.Quit
End If

If (LCase(strSearchSubs) = "t") Then
    strSearchSubs = "True"
End If
If (LCase(strSearchSubs) = "f") Then
    strSearchSubs = "False"
End If

If (LCase(strSearchSubs) <> "true") _
        And (LCase(strSearchSubs) <> "false") Then
    Wscript.Echo "Invalid argument"
    Call Syntax
    Wscript.Quit
End If

If (LCase(strCase) = "t") Then
    strCase = "True"
End If
If (LCase(strCase) = "f") Then
    strCase = "False"
End If

If (LCase(strCase) <> "true") And (LCase(strCase) <> "false") Then
    Wscript.Echo "Invalid argument"
    Call Syntax
    Wscript.Quit
End If

arrFileExtensions = Split(strFileExtension, ",")
For k = 0 To UBound(arrFileExtensions)
    If (Left(arrFileExtensions(k), 1) = ".") Then
        arrFileExtensions(k) = Mid(arrFileExtensions(k), 2)
    End If
Next

Set objFSO = CreateObject("Scripting.FileSystemObject")

If Not objFSO.FolderExists(strSearchPath) Then
    Wscript.Echo "Path not found"
    Call Syntax
    Wscript.Quit
End If

intNumber = 0
Call SearchFiles(strSearchPath, arrFileExtensions, _
    CBool(strSearchSubs), strSearchString, CBool(strCase))
Wscript.Echo CStr(intNumber) & " files found with string " & strSearchString

Sub SearchFiles(ByVal strPath, ByVal arrExtensions, _
                ByVal blnIncludeSub, ByVal strSearch, ByVal blnCase)
    ' Recursive subroutine to search files in a folder for a string.
    ' Modified April 19, 2007, to handle errors raised if user
    ' lacks permissions in some folders.
    ' Modified October 9, 2009, to handle up to 3 search strings.
    ' Modified October 22, 2009, to allow case sensitive searches.
    
    Dim objFolder, objFiles, strFile, strFolder
    Dim intMax, k, arrstrSearches

    strSearch = Replace(strSearch, "\;", "^^^#")
    arrstrSearches = Split(strSearch, ";")
    If (UBound(arrstrSearches) > 2) Then
        Wscript.Echo "Too many search strings specified, program aborted"
        Exit Sub
    End If
    For k = 0 To UBound(arrstrSearches)
        If (blnCase = False) Then
            arrstrSearches(k) = LCase(Replace(arrstrSearches(k), "^^^#", ";"))
        Else
            arrstrSearches(k) = Replace(arrstrSearches(k), "^^^#", ";")
        End If
    Next

    Set objFolder = objFSO.GetFolder(strPath)
    Set objFiles = objFolder.Files

    On Error Resume Next
    For Each strFile In objFiles
        If (Err.Number <> 0) Then
            Wscript.Echo "## Error accessing folder: " & objFolder.Path
            Wscript.Echo "  Description: " & Err.Description
        End If
        On Error GoTo 0
        intMax = UBound(arrExtensions)
        If (intMax = -1) Then
            Call FindStrings(strFile, arrstrSearches, intNumber, blnCase)
        Else
            For k = 0 To UBound(arrExtensions)
                If (LCase(objFSO.GetExtensionName(strFile)) _
                        = LCase(arrExtensions(k))) Then
                    Call FindStrings(strFile, arrstrSearches, intNumber, blnCase)
                End If
            Next
        End If
        On Error Resume Next
    Next
    If (Err.Number <> 0) Then
        Wscript.Echo "## Error accessing folder: " & objFolder.Path
        Wscript.Echo "  Description: " & Err.Description
     End If
    On Error GoTo 0

    If (blnIncludeSub = True) Then
        On Error Resume Next
        For Each strfolder In objFolder.SubFolders
            If (Err.Number <> 0) Then
                Wscript.Echo "## Error accessing folder: " & objFolder.Path
                Wscript.Echo "  Description: " & Err.Description
            Else
                On Error GoTo 0
                Call SearchFiles(strFolder, arrExtensions, _
                    blnIncludeSub, strSearch, blnCase)
            End If
            On Error Resume Next
        Next
        On Error GoTo 0
    End If
End Sub

Sub FindStrings(ByVal strFileName, ByVal arrstrStrings, _
        ByRef intCount, ByVal blnSearchCase)
    ' Subroutine to search a file for one or more strings.

    Dim objFile, objStream, strText

    Const ForReading = 1

    Set objFile = objFSO.GetFile(strFileName)
    If (objFile.Size > 0) Then
        On Error Resume Next
        Set objStream = objFSO.OpenTextFile(strFileName, ForReading)
        If (Err.Number <> 0) Then
            Wscript.Echo "## Error accessing file: " & strFileName
            Wscript.Echo "  Description: " & Err.Description
            On Error GoTo 0
        Else
            On Error Resume Next
            strText = objStream.ReadAll
            If (Err.Number <> 0) Then
                Wscript.Echo "## Error accessing file: " & strFileName
                Wscript.Echo "  Description: " & Err.Description
                On Error GoTo 0
            Else
                On Error GoTo 0
                If (blnSearchCase = False) Then
                    strText = LCase(strText)
                End If
                Select Case UBound(arrstrStrings)
                    Case 0
                        If (InStr(strText, arrstrStrings(0)) > 0) Then
                            Wscript.Echo strFileName & vbTab & " (" _
                                & objFile.DateLastModified & " " _
                                & FormatNumber(objFile.Size, 0) & ")"
                            intCount = intCount + 1
                        End If
                    Case 1
                        If (InStr(strText, arrstrStrings(0)) > 0) _
                                And (InStr(strText, arrstrStrings(1)) > 0) Then
                            Wscript.Echo strFileName & vbTab & " (" _
                                & objFile.DateLastModified & " " _
                                & FormatNumber(objFile.Size, 0) & ")"
                            intCount = intCount + 1
                        End If
                    Case Else
                        If (InStr(strText, arrstrStrings(0)) > 0) _
                                And (InStr(strText, arrstrStrings(1)) > 0) _
                                And (InStr(strText, arrstrStrings(2)) > 0) Then
                            Wscript.Echo strFileName & vbTab & " (" _
                                & objFile.DateLastModified & " " _
                                & FormatNumber(objFile.Size, 0) & ")"
                            intCount = intCount + 1
                        End If
                End Select
            End If
            objStream.Close
        End If
    End If
End Sub

Sub Syntax
    ' Subroutine to output syntax message.
    Wscript.Echo "Program to find files containing specified text string(s)."
    Wscript.Echo "Syntax:"
    Wscript.Echo "    cscript //nologo FindFiles.vbs <string> " _
        & "<path> <ext> <subs> <case>"
    Wscript.Echo "Where:"
    Wscript.Echo "    <string> is 1 to 3 text strings to search for"
    Wscript.Echo "        Enclose any string in quotes if it has spaces"
    Wscript.Echo "        If more than one string, delimit with semicolons"
    Wscript.Echo "        If any string has "";"", escape with ""\"""
    Wscript.Echo "    <path> is the path to search"
    Wscript.Echo "        Enclose in quotes if it has spaces"
    Wscript.Echo "        If no path or ""."" is specified, the current directory is used"
    Wscript.Echo "    <ext> is comma delimited list of file extensions,"
    Wscript.Echo "        with no spaces. Use ""."" to indicate all extensions"
    Wscript.Echo "        The default if no extensions are specified is all"
    Wscript.Echo "    <subs> is True to search all sub folders, " _
        & "False is the default"
    Wscript.Echo "    <case> is True if search is case sensitive, " _
        & "False is the default"
    Wscript.Echo "Examples:"
    Wscript.Echo "cscript //nologo FindFiles.vbs strUser c:\scripts vbs True"
    Wscript.Echo "cscript //nologo FindFiles.vbs LDAP:;WinNT: ""c:\My Folder"" vbs,txt,csv T T"
    Wscript.Echo "cscript //nologo FindFiles.vbs ""Option Explicit"" c:\scripts . F"
    Wscript.Echo "cscript //nologo FindFiles.vbs ""Test \; 2"";Test1 . vbs"
End Sub