<# : バッチコマンド(PowerShellコメント)開始
@echo off & setlocal
rem
rem  正規表現を用いたテキスト複数行検索
rem
rem
rem     RegExS.bat  [正規表現] [検索対象フォルダー] [ファイル名のフィルター] 
rem                 [-e 文字コード種別] [-g グループ名/番号] 
rem                 [-h] [-i] [-n] [-r] [-s] [-x 除外フィルター]
rem                 [-bc マッチ箇所の背景色] [-cc マッチ箇所(キャプチャグループ)の文字色]
rem                 [-fc マッチ箇所の文字色] [-Q/-Interactive]
rem
rem
rem     正規表現                  .NET準拠の正規表現を指定します。
rem                               ※ "^"と"$"はそれぞれ行頭と行末にマッチします。
rem                               ※ "."を改行文字にもマッチさせるためには先頭に"(?s)"を付加
rem				    してください。
rem                               指定がない場合は入力プロンプトが表示されます。
rem     検索対象フォルダー        指定がない場合は選択ダイアログボックスが表示されます。
rem     対象ファイル名            対象ファイル名をカンマ区切りで指定します。ワイルドカードの
rem                               使用が可能です。既定値は"*"です。(例: "*.c, *.h")
rem     -e 文字コード種別         BOM(Byte Order Mark)のないファイルの文字コード。(例: "UTF8")
rem     -g キャプチャグループ     キャプチャグループの名前または番号を指定します。既定値は"0"です。
rem     -h                        HTMLファイルを出力します。
rem     -i                        検索時に大文字小文字を区別しません。
rem     -n                        全角文字を半角文字に変換してから検索します。
rem                               カナ・数字の全角/半角を区別したくない場合に使用します。
rem     -r                        サブフォルダーに対する再帰的検索のスイッチです。
rem     -s                        単純文字列検索のスイッチです。
rem     -x 除外フィルター         除外ファイル名をカンマ区切りで指定します。ワイルドカードの
rem                               使用が可能です。(例: "*.img, *.dat")
rem                               なお、以下のバイナリ形式ファイルは既定で除外されます。
rem                               "*.exe, *.dll, *.lnk, *.zip, *.bmp, *.gif, *.jpg, *.png"
rem     -bc ConsoleColor          マッチ箇所の背景色
rem     -cc ConsoleColor          マッチ箇所(キャプチャグループ)の文字色
rem     -fc ConsoleColor          マッチ箇所の文字色
rem     -Q                        対話モードを強制します。
rem
rem     備考:.docx/.pptx/.xlsx/.xlsm 形式のファイルにも実験的に対応しています。
rem           更に、本スクリプトの設置フォルダー内に itextsharp.dll が存在する場合は
rem           PDFファイルの検索も可能です。(ラスタ化された文字列やEXCELの数値・日付を除く。)
rem
rem     Created by earthdiver1
rem     Version 2.00
rem     クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンスの下に提供されています。
rem
rem ----------------------------------------------------------------------------
echo %CMDCMDLINE% | findstr /i /c:"%~nx0" >NUL && set DC=1
rem 以下はPowerShellスクリプトをバッチファイルの中に埋め込むためのプリアンブルです。
rem デバッグ時は拡張子を.ps1に変更してPowerShellコンソール上から実行してください。
set BATCH_ARGS=%*
if defined BATCH_ARGS set BATCH_ARGS=%BATCH_ARGS:"=\"%
if defined BATCH_ARGS set BATCH_ARGS=%BATCH_ARGS:^^=^% 
set P_CMD=$DoubleClicked=0%DC%;$PSScriptRoot='%~dp0'.TrimEnd('\');$input^|^
&([ScriptBlock]::Create((${%~f0}^|Out-String)))
endlocal & PowerShell -NoProfile -Command "%P_CMD%" %BATCH_ARGS%
exit/b
rem ----------------------------------------------------------------------------
: バッチコマンド(PowerShellコメント)終了 #>
#Requires -Version 3
param (
    [String]$Pattern,
    [String]$Dir,
    [String]$Include                              = "*",
    [Alias("e")][String]$Encoding                 = "Default",
    [Alias("x")][String]$Exclude                  = "",
    [Alias("g")][String]$Group                    = "0",
    [Alias("h")][Switch]$HtmlOutput               = $False,
    [Alias("i")][Switch]$IgnoreCase               = $False,
    [Alias("n")][Switch]$Narrow                   = $False,
    [Alias("r")][Switch]$Recurse                  = $False,
    [Alias("s")][Switch]$SimpleMatch              = $False,
    [Alias("bc")][ConsoleColor]$BackgroundColor   = "Blue",
    [Alias("cc")][ConsoleColor]$CapturegroupColor = "Red",
    [Alias("fc")][ConsoleColor]$ForegroundColor   = "White",

    [Alias("Q")][Switch]$Interactive = $False
)

#$DebugPreference = "Continue"

Function RegExS {
<#
	.SYNOPSIS
		正規表現を用いたテキスト検索をおこないます。
	
	.DESCRIPTION
		フォルダーツリーの中の複数のテキストファイルに対して正規表現による一括検索を
		おこなうことができます。複数行に渡るパターンにマッチする検索が可能です。
	
	.PARAMETER Pattern
		正規表現の文字列です。"^" や "$" は行頭・行末にマッチします。
		"."(ピリオド)を改行文字(\n)にもマッチさせるためには先頭に(?s)を付加して
		ください。

	.PARAMETER Dir
		検索対象のフォルダーのパスを指定します。
	
	.PARAMETER Include
		対象ファイル名をカンマ区切りで指定します。ワイルドカードの使用が可能で、
		既定値は"*"です。(例:"*.cpp, *.h")
	
	.PARAMETER Encoding
		BOM(Byte Order Mark)の無いテキストファイルの文字コードを指定します。
		規定値は"Default"です。(別名: -e) 

	.PARAMETER BackgroundColor
		マッチ箇所の背景色を指定します。(別名: -bc)
		規定値は "Blue" です。

	.PARAMETER CapturegroupColor
		マッチ箇所(キャプチャグループ)の文字色を指定します。(別名: -cc)
		規定値は "Red" です。

	.PARAMETER ForegroundColor
		マッチ箇所の文字色を指定します。(別名: -fc)
		規定値は "White" です。

	.PARAMETER Group
		キャプチャグループの名前または番号を指定します。(別名: -g)
		規定値は "0" です。

	.PARAMETER HtmlOutput
		本スイッチの指定時にはHTMLファイルを出力します。(別名: -h)

	.PARAMETER IgnoreCase
		本スイッチの指定時には大文字、小文字を区別しません。(別名: -i)

	.PARAMETER Narrow
		本スイッチの指定時には検索前に全角文字を半角文字に変換します。(別名: -n)
		カナ・数字の全角/半角を区別したくない場合に使用します。

	.PARAMETER Recurse
		本スイッチの指定時にはサブフォルダー内を再帰的に検索します。(別名: -r)
	
	.PARAMETER SimpleMatch
		本スイッチの指定時には単純な文字列検索をおこないます。(別名: -s)

	.PARAMETER Exclude
		除外ファイル名をカンマ区切りで指定します。(別名: -x)
		ワイルドカードの使用が可能です。(例: "*.img, *.dat")
		なお、以下のバイナリ形式ファイルは既定で除外されます。
		*.exe, *.dll, *.lnk, *.zip, *.bmp, *.gif, *.jpg, *.png
	
	.NOTES
		Author:   earthdiver1
		Version:  V2.00
		クリエイティブ・コモンズ 表示 - 継承 4.0 国際 ライセンスの下に提供されています。

	.EXAMPLE
		./RegExS.ps1 """(?:[^""\\\n]|\\.)*""|'(?:[^'\\\n]|\\.)*'|(/\*[\S\s]*?\*/|//.*$)" . "*.cpp,*.h" -g 1
		カレントフォルダー内のC++言語ソースファイル内のコメント行を検索します。

	.REMARKS
		実験的に .docx/.pptx/.xlsx/.xlsm 形式のファイルにも対応しています。更に本スクリプト
		の設置フォルダー内に itextsharp.dll が存在する場合はPDFファイルの検索も可能です
		(ラスタ化された文字列やEXCELの数値・日付を除く)。
#>
    param (
        [String]$Pattern,
        [String]$Dir,
        [String]$Include = "*",
        [ValidateSet("", "Unknown", "String", "Unicode", "Byte", "BigEndianUnicode", `
                     "UTF8", "UTF7", "UTF32", "Ascii", "Default", "Oem", "BigEndianUTF32")]
        [Alias("e")][String]$Encoding                 = "Default",
        [Alias("x")][String]$Exclude                  = "",
        [Alias("g")][String]$Group                    = "0",
        [Alias("h")][Switch]$HtmlOutput,
        [Alias("i")][Switch]$IgnoreCase,
        [Alias("n")][Switch]$Narrow,
        [Alias("r")][Switch]$Recurse,
        [Alias("s")][Switch]$SimpleMatch,
        [Alias("bc")][ConsoleColor]$BackgroundColor   = "Blue",
        [Alias("cc")][ConsoleColor]$CapturegroupColor = "Red",
        [Alias("fc")][ConsoleColor]$ForegroundColor   = "White"
    )
############ Edit Here ############
    $BinaryFiles = "*.exe, *.dll, *.lnk, *.zip, *.bmp, *.gif, *.jpg, *.png"
    $HtmlFile = "$($script:PSScriptRoot)\RegExS_Output.htm"
#   $HtmlFile = "$($script:PSScriptRoot)\RegExS_Output_$((Get-Date).ToString('yyyyMMddHHmm')).htm"
###################################
    if (-not $Pattern) { Write-Host "Pattern の指定がありません。" -Fore Red; Get-Help RegExS; return }
    if (-not $Dir)     { Write-Host "Dir の指定がありません。"     -Fore Red; Get-Help RegExS; return }
    $Dir = Convert-Path -LiteralPath $Dir
    if (-not $Dir)     { Write-Host "Dir は存在しません。"         -Fore Red; Get-Help RegExS; return }
    [Array]$IsContainer = Get-Item $Dir | %{ $_.PSIsContainer }
    if ((-not $Recurse) -and $IsContainer.Length -eq 1 -and $IsContainer[0]) { $Dir += "\*" }
    if ($SimpleMatch) {
        $Pattern = [regex]::Escape($Pattern) 
    } else {
        $Pattern = "(?m)" + $Pattern
    }
    if ($IgnoreCase) { $Pattern = "(?i)" + $Pattern }
    if ($HtmlOutput) { HtmlHeader }
    [String[]]$IncludeList = $Include -Split "," | %{ $_.Trim() }
    [String[]]$ExcludeList = ($Exclude + "," + $BinaryFiles) -Split "," | %{ $_.Trim() } | ?{ $_ }
    $global:ErrorView = "CategoryView"
    if ($IncludeList.Length -eq 1) { # "-Filter" の方が "-Include" よりも速い
        $files = Get-ChildItem $Dir -Filter $Include      -Exclude $ExcludeList -Recurse:$Recurse -File
    } else {
        $files = Get-ChildItem $Dir -Include $IncludeList -Exclude $ExcludeList -Recurse:$Recurse -File
    }
    $global:ErrorView = "NormalView"
    $ErrorActionPreference = "Stop"
    trap [System.Exception] {
        Write-Host "システム例外が発生しました。" -Fore Red
        Write-Host $Error[0].ToString() $Error[0].InvocationInfo.PositionMessage
        if ($HtmlOutput) {
            HtmlFooter
            try { 
                [Text.Encoding]::UTF8.GetBytes($script:Html.ToString()) | Set-Content -Path $HtmlFile -Encoding Byte # UTF-8N
                [void]$script:Html.Clear()
            } catch [System.Exception] {}
        }
        return
    }
    [Int]$Nmfile = 0
    [Int]$Nmatch = 0
    [Int]$Nmline = 0
    [Int]$Nmchar = 0
    if ($files) {
        :LOOP foreach ($file in $files) {
            Write-Debug "RegExS: $($file.FullName)"
            try {
                switch ($file.Extension) {
                    ".docx" { $io = ReadDocxFile $file.FullName ; break }
                    ".pptx" { $io = ReadPptxFile $file.FullName ; break }
                    ".xlsm" { $io = ReadXlsxFile $file.FullName ; break }
                    ".xlsx" { $io = ReadXlsxFile $file.FullName ; break }
                    ".pdf"  { $io = ReadPdfFile  $file.FullName ; break }
                    Default {
                        $enc = GetEncodingFromBOM $file
                        if (-not $enc) { 
                            if (IsBinary $file) {
                                Write-Host "バイナリ形式ファイル $($file.FullName) をスキップします。" -Fore Green
                                continue LOOP
                            }
                            $enc = $Encoding 
                        }
                        $io = (Get-Content $file.FullName -Encoding $enc -Raw) -Replace "\r",""
                    }
                }
            } catch [System.UnauthorizedAccessException], `
                    [System.Management.Automation.ItemNotFoundException], `
                    [System.IO.IOException] {
                Write-Host "$($Error[0].Exception.Message) 処理を継続します。" -Fore Red
                if ($HtmlOutput) {
                    [void]$script:Html.Append("<span class=`"y`">$(Htmlify $Error[0].Exception.Message) 処理を継続します。</span>`n")
                }
                continue
            }
            if ($Narrow) {
                Add-Type -AssemblyName "Microsoft.VisualBasic"
                $io = [Microsoft.VisualBasic.Strings]::StrConv($io,[Microsoft.VisualBasic.VbStrConv]::Narrow)
                $Pattern = [Microsoft.VisualBasic.Strings]::StrConv($Pattern,[Microsoft.VisualBasic.VbStrConv]::Narrow)
            }
            [Array]$Matches = Select-String -InputObject $io -Pattern $Pattern -CaseSensitive -AllMatches `
                            | %{ $_.Matches } | ?{ $_.Groups[$Group].Success }
            if ($Matches) {
                $Nmfile++
                Write-Host $file.FullName -Fore Yellow
                $bol = [Array](Select-String -InputObject $io -Pattern '(?m)^' -AllMatches | %{ $_.Matches } | %{ $_.Index }) `
                     + ($io.Length + 1)
                [Int]$nl = -1
                if (-not $HtmlOutput) {
                    if ($Group -eq "0") {
                        for ([Int]$i=0; $i -lt $Matches.Length; $i++) {
                            $MatchIndex  = $Matches[$i].Groups[0].Index
                            $MatchLength = $Matches[$i].Groups[0].Length
                            $MatchString = $Matches[$i].Groups[0].Value -Replace "`n","`n`t"
                            $NextMatch   = $Matches[$i+1]
                            if ($bol[$nl+1] -le $MatchIndex) {
                                while ($bol[$nl+1] -le $MatchIndex) { $nl++ }
                                $index = $bol[$nl]
                                if ($index -ge $io.Length) { break }
                                $Nmline++
                                if ($file.Extension -eq ".xlsx" -or $file.Extension -eq ".xlsm") {
                                    Write-Host $("{0,5}:" -F $script:cell[$nl]) -NoNewline
                                } else {
                                    Write-Host $("{0,5}:" -F ($nl+1)) -NoNewline
                                }
                            }
                            $Nmatch++
                            if ($MatchLength -gt 0) {
                                $Nmchar += $MatchLength
                                Write-Host $io.SubString($index, $MatchIndex - $index) -NoNewline
                                Write-Host $MatchString -Back $BackgroundColor -Fore $ForegroundColor -NoNewline
                                $index = $MatchIndex + $MatchLength
                                while ($bol[$nl+1] -le $index) {
                                    $nl++
                                    $Nmline++
                                }
                            }
                            if ($NextMatch -and $NextMatch.Index -lt $bol[$nl+1]) { continue }
                            $eol = $bol[$nl+1] - 1
                            if ($eol -eq $io.Length) { $eol-- }
                            if ($io[$eol] -eq "`n")  { $eol-- }
                            if ($index -le $eol) {
                                Write-Host $io.SubString($index, $eol+1 - $index)
                            } else {
                                Write-Host
                            }
                        }
                    } else {
                        for ([Int]$i=0; $i -lt $Matches.Length; $i++) {
                            $Match0Index  = $Matches[$i].Groups[0].Index
                            $Match0Length = $Matches[$i].Groups[0].Length
                            $MatchIndex   = $Matches[$i].Groups[$Group].Index
                            $MatchLength  = $Matches[$i].Groups[$Group].Length
                            $MatchString  = $Matches[$i].Groups[$Group].Value -Replace "`n","`n`t"
                            $NextMatch    = $Matches[$i+1]
                            if ($bol[$nl+1] -le $Match0Index) {
                                while ($bol[$nl+1] -le $Match0Index) { $nl++ }
                                $index = $bol[$nl]
                                if ($index -ge $io.Length) { break }
                                $Nmline++
                                if ($file.Extension -eq ".xlsx" -or $file.Extension -eq ".xlsm") {
                                    Write-Host $("{0,5}:" -F $script:cell[$nl]) -NoNewline
                                } else {
                                    Write-Host $("{0,5}:" -F ($nl+1)) -NoNewline
                                }
                            }
                            $Nmatch++
                            if ($Match0Length -gt 0) {
                                $Nmchar += $MatchLength
                                Write-Host $io.SubString($index, $Match0Index - $index) -NoNewline
                                Write-Host $($io.SubString($Match0Index, $MatchIndex - $Match0Index) -Replace "`n","`n`t") `
                                           -Back $BackgroundColor -Fore $ForegroundColor -NoNewline
                                Write-Host $MatchString -Back $BackgroundColor -Fore $CapturegroupColor -NoNewline 
                                $index  = $Match0Index + $Match0Length
                                $index0 = $MatchIndex + $MatchLength
                                Write-Host $($io.SubString($index0, $index - $index0) -Replace "`n","`n`t") `
                                           -Back $BackgroundColor -Fore $ForegroundColor -NoNewline
                                while ($bol[$nl+1] -le $index) {
                                    $nl++
                                    $Nmline++
                                }
                            }
                            if ($NextMatch -and $NextMatch.Index -lt $bol[$nl+1]) { continue }
                            $eol = $bol[$nl+1] - 1
                            if ($eol -eq $io.Length) { $eol-- }
                            if ($io[$eol] -eq "`n")  { $eol-- }
                            if ($index -le $eol) {
                                Write-Host $io.SubString($index, $eol+1 - $index)
                            } else {
                                Write-Host
                            }
                        }
                    }
                } else {
                    [void]$script:Html.Append("<a href=`"file:///$($file.FullName)`" type=`"text`" class=`"y`">$($file.FullName)</a>`n")
                    if ($Group -eq "0") {
                        for ([Int]$i=0; $i -lt $Matches.Length; $i++) {
                            $MatchIndex  = $Matches[$i].Groups[0].Index
                            $MatchLength = $Matches[$i].Groups[0].Length
                            $MatchString = $Matches[$i].Groups[0].Value -Replace "`n","`n`t"
                            $NextMatch   = $Matches[$i+1]
                            if ($bol[$nl+1] -le $MatchIndex) {
                                while ($bol[$nl+1] -le $MatchIndex) { $nl++ }
                                $index = $bol[$nl]
                                if ($index -ge $io.Length) { break }
                                $Nmline++
                                if ($file.Extension -eq ".xlsx" -or $file.Extension -eq ".xlsm") {
                                    [void]$script:Html.Append(("{0,5}:" -F $script:cell[$nl]))
                                } else {
                                    [void]$script:Html.Append(("{0,5}:" -F ($nl+1)))
                                }
                            }
                            $Nmatch++
                            if ($MatchLength -gt 0) {
                                $Nmchar += $MatchLength
                                [void]$script:Html.Append($(Htmlify $io.SubString($index, $MatchIndex - $index)))
                                [void]$script:Html.Append("<span class=`"fc`">$(Htmlify $MatchString)</span>")
                                $index = $MatchIndex + $MatchLength
                                while ($bol[$nl+1] -le $index) {
                                    $nl++
                                    $Nmline++
                                }
                            }
                            if ($NextMatch -and $NextMatch.Index -lt $bol[$nl+1]) { continue }
                            $eol = $bol[$nl+1] - 1
                            if ($eol -eq $io.Length) { $eol-- }
                            if ($io[$eol] -eq "`n")  { $eol-- }
                            if ($index -le $eol) {
                                [void]$script:Html.Append("$(Htmlify $io.SubString($index, $eol+1 - $index))`n")
                            } else {
                                [void]$script:Html.Append("`n")
                            }
                        }
                    } else {
                        for ([Int]$i=0; $i -lt $Matches.Length; $i++) {
                            $Match0Index  = $Matches[$i].Groups[0].Index
                            $Match0Length = $Matches[$i].Groups[0].Length
                            $MatchIndex   = $Matches[$i].Groups[$Group].Index
                            $MatchLength  = $Matches[$i].Groups[$Group].Length
                            $MatchString  = $Matches[$i].Groups[$Group].Value -Replace "`n","`n`t"
                            $NextMatch    = $Matches[$i+1]
                            if ($bol[$nl+1] -le $Match0Index) {
                                while ($bol[$nl+1] -le $Match0Index) { $nl++ }
                                $index = $bol[$nl]
                                if ($index -ge $io.Length) { break }
                                $Nmline++
                                if ($file.Extension -eq ".xlsx" -or $file.Extension -eq ".xlsm") {
                                    [void]$script:Html.Append(("{0,5}:" -F $script:cell[$nl]))
                                } else {
                                    [void]$script:Html.Append(("{0,5}:" -F ($nl+1)))
                                }
                            }
                            $Nmatch++
                            if ($Match0Length -gt 0) {
                                $Nmchar += $MatchLength
                                [void]$script:Html.Append((Htmlify $io.SubString($index, $Match0Index - $index)))
                                [void]$script:Html.Append("<span class=`"fc`">$(Htmlify $io.SubString($Match0Index, $MatchIndex - $Match0Index))</span>")
                                [void]$script:Html.Append("<span class=`"cc`">$(Htmlify $MatchString)</span>")
                                $index = $Match0Index + $Match0Length
                                $index0 = $MatchIndex + $MatchLength
                                [void]$script:Html.Append("<span class=`"fc`">$(Htmlify $io.SubString($index0, $index - $index0))</span>")
                                while ($bol[$nl+1] -le $index) {
                                    $nl++
                                    $Nmline++
                                }
                            }
                            if ($NextMatch -and $NextMatch.Index -lt $bol[$nl+1]) { continue }
                            $eol = $bol[$nl+1] - 1
                            if ($eol -eq $io.Length) { $eol-- }
                            if ($io[$eol] -eq "`n")  { $eol-- }
                            if ($index -le $eol) {
                                [void]$script:Html.Append("$(Htmlify $io.SubString($index, $eol+1 - $index))`n")
                            } else {
                                [void]$script:Html.Append("`n")
                            }
                        }
                    }
                }
            }
            if ($file.Extension -eq ".xlsx" -or $file.Extension -eq ".xlsm") {
                $script:cell.Clear()
            }
        }
    }
    Write-Host "$Nmfile ファイル、$Nmline 行、$Nmatch 件、$Nmchar 文字の一致がありました。" -Fore Green
    if ($HtmlOutput) {
        [void]$script:Html.Append("<span class=`"g`">$Nmfile ファイル、$Nmline 行、$Nmatch 件、$Nmchar 文字の一致がありました。</span>`n")
        HtmlFooter
        try {
            [Text.Encoding]::UTF8.GetBytes($script:Html.ToString()) | Set-Content -Path $HtmlFile -Encoding Byte # UTF-8N
            [void]$script:Html.Clear()
            Write-Host "$HtmlFile に結果を出力しました。" -Fore Green
            Start-Process -FilePath "file:///$HtmlFile"
        } catch [System.Exception] {
            Write-Host "Htmlファイルの出力に失敗しました。" -Fore Red
            Write-Host $Error[0].Exception.Message
        }
    }
}

Function IsBinary($File) {
    if ($File.Length -lt 2)        { return $False }
    if ($File.Length -gt 20000000) { return $True  }
    $bytes = Get-Content $File.FullName -ReadCount 4096 -TotalCount 4096 -Encoding Byte
    [Int]$Nbo=0
    [Int]$Nbe=0
    [Int]$Nzo=0
    [Int]$Nze=0
    for ([Int]$i=0; $i -lt $bytes.Length; $i+=2) {
        $Nbo++
        if ($bytes[$i] -eq 0) { $Nzo++ }
    }
    for ([Int]$i=1; $i -lt $bytes.Length; $i+=2) {
        $Nbe++
        if ($bytes[$i] -eq 0) { $Nze++ }
    }
    if (($Nzo+$Nze -gt 0) -and ([System.Math]::Abs($Nzo/$Nbo-$Nze/$Nbe)/($Nbo+$Nbe) -lt 0.1)) { return $True }
    Write-Debug "IsBinary: $($file.Name) $($Nzo+$Nze), $([System.Math]::Abs($Nzo/$Nbo-$Nze/$Nbe)/($Nbo+$Nbe))"
    return $False
}

Function GetEncodingFromBOM($File) {
    $bytes = Get-Content $File.FullName -ReadCount 4 -TotalCount 4 -Encoding Byte
    $string = ($bytes | %{ "{0:X2}" -F $_ }) -Join ""
    switch -Regex ($string) {
       "^EFBBBF"              { $enc="UTF8"             ; break }
       "^FFFE0000"            { $enc="UTF32"            ; break }
       "^FFFE"                { $enc="Unicode"          ; break }
       "^0000FEFF"            { $enc="BigEndianUTF32"   ; break }
       "^FEFF"                { $enc="BigEndianUnicode" ; break }
       "^2B2F76(38|39|2B|2F)" { $enc="UTF7"             ; break }
       Default                { $enc="" }
    }
    Write-Debug "GetEncodingFromBOM: $($File.Name) $($string) $($enc)"
    return $enc
}

Function ReadDocxFile($DocxFile) {
    Add-Type -AssemblyName WindowsBase
    $file      = (Get-Childitem -Path $DocxFile).FullName
    $package   = [System.IO.Packaging.Package]::Open($file, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
    $parts     = $package.GetParts() | %{ $_ }
    $document  = $parts | ?{ $_.Uri.OriginalString -eq "/word/document.xml"  }
    $footnotes = $parts | ?{ $_.Uri.OriginalString -eq "/word/footnotes.xml" }
    $endnotes  = $parts | ?{ $_.Uri.OriginalString -eq "/word/endnotes.xml"  }
    $comments  = $parts | ?{ $_.Uri.OriginalString -eq "/word/comments.xml"  }
    $enc       = [System.Text.Encoding]::UTF8
    $sr        = New-Object System.IO.StreamReader $document.GetStream(),$enc
    $text      = New-Object System.Text.StringBuilder
    [void]$text.Append($sr.ReadToEnd())
    $sr.Close()
    if ($footnotes) {
        $sr = New-Object System.IO.StreamReader $footnotes.GetStream(),$enc
        [void]$text.Append($sr.ReadToEnd())
        $sr.Close()
    }
    if ($endnotes) {
        $sr = New-Object System.IO.StreamReader $endnotes.GetStream(),$enc
        [void]$text.Append($sr.ReadToEnd())
        $sr.Close()
    }
    if ($comments) {
        $sr = New-Object System.IO.StreamReader $comments.GetStream(),$enc
        [void]$text.Append("</w:r></w:p>" + $sr.ReadToEnd())
        $sr.Close()
    }
    $package.Close()
    $t = $text.ToString()
    [void]$text.Clear()
    $t = $t -Replace "\r?\n",""
    $t = $t -Replace "</w:r></w:p></w:tc><w:tc>"," "
    $t = $t -Replace "\s+"," "
    $t = $t -Replace "</w:r></w:p>","`n"
    $t = $t -Replace "<[^>]+>",""
    $t = $t -Replace "&amp;","&"
    $t = $t -Replace "&lt;","<"
    $t = $t -Replace "&gt;",">"
    $t = $t -Replace "(?m)^ ?\r?\n",""
    return $t
}

Function ReadPptxFile($PptxFile) {
    Add-Type -AssemblyName WindowsBase
    $file    = (Get-Childitem -Path $PptxFile).FullName
    $package = [System.IO.Packaging.Package]::Open($file, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
    $parts   = $package.GetParts() | %{ $_ }
    $enc     = [System.Text.Encoding]::UTF8
    $text    = New-Object System.Text.StringBuilder
    for ([Int]$i = 1; $i -le 999; $i++) {
        $slide = $parts | ?{ $_.Uri.OriginalString -eq "/ppt/slides/slide$i.xml" }
        if (-not $slide) { break }
        $sr  = New-Object System.IO.StreamReader $slide.GetStream(),$enc
        $tmp = $sr.ReadToEnd()
        $sr.Close()
        $sliderels = $parts | ?{ $_.Uri.OriginalString -eq "/ppt/slides/_rels/slide$i.xml.rels" }
        if ($sliderels) {
            $sr  = New-Object System.IO.StreamReader $sliderels.GetStream(),$enc
            $rel = [xml]$sr.ReadToEnd() | %{ $_.Relationships.Relationship } | ?{ $_.Type -Match "notesSlide" }
            $sr.Close()
            if ($rel) {
                $nfile = $([io.path]::GetFileName($rel.Target))
                $note  = $parts | ?{ $_.Uri.OriginalString -eq "/ppt/notesSlides/$nfile" }
                $sr    = New-Object System.IO.StreamReader $note.GetStream(),$enc
                $tmp  += " " + $sr.ReadToEnd()
                $sr.Close()
            }
        }
        $tmp = $tmp -Replace "\r?\n",""
        $tmp = $tmp -Replace "\s+"," "
        [void]$text.Append($tmp + "`n")
    }
    $package.Close()
    $t = $text.ToString()
    [void]$text.Clear()
    $t = $t -Replace "<[^>]+>",""
    $t = $t -Replace "&amp;","&"
    $t = $t -Replace "&lt;","<"
    $t = $t -Replace "&gt;",">"
    return $t
}

Function ReadXlsxFile($XlsxFile) {
    Add-Type -AssemblyName WindowsBase
    $enc     = [System.Text.Encoding]::UTF8
    $file    = (Get-Childitem -Path $XlsxFile).FullName
    $package = [System.IO.Packaging.Package]::Open($file, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
    $parts   = $package.GetParts() | %{ $_ }
    $text    = New-Object System.Text.StringBuilder
    $script:Cell = New-Object 'System.Collections.Generic.List[System.String]' 100000
    $sst = $parts | ?{ $_.Uri.OriginalString -eq "/xl/sharedStrings.xml" }
    if ($sst) {
        $strings = New-Object 'System.Collections.Generic.List[System.String]' 5000
        $sr = New-Object System.IO.StreamReader $sst.GetStream(),$enc
        $si = [xml]$sr.ReadToEnd() | %{ $_.sst.si }
        foreach ($s in $si) {
            if ($s.t -and $s.t.GetType().Name -eq "String") {
                $tmp = $s.t
            } elseif ($s.t."#text") {
                $tmp = $s.t."#text"
            } elseif ($s.r) {
                $tmp = ""
                foreach ($r in $s.r) {
                    if ($r.t -and $r.t.GetType().Name -eq "String") {
                        $tmp += $r.t
                    } elseif ($r.t."#text") { 
                        $tmp += $r.t."#text"
                    }
                }
            }
            $tmp = $tmp -Replace "\r?\n",""
            $tmp = $tmp -Replace "\s+"," "
            [void]$strings.Add($tmp)
        }
        $sr.Close()
    }
    for ([Int]$i = 1; $i -le 999; $i++) {
        $sheet = $parts | ?{ $_.Uri.OriginalString -eq "/xl/worksheets/sheet$i.xml" }
        if (-not $sheet) { break }
        $sr   = New-Object System.IO.StreamReader $sheet.GetStream(),$enc
        $rows = [xml]$sr.ReadToEnd() | %{ $_.worksheet.sheetdata.row }
        foreach ($row in $rows) {
            foreach ($col in $row.c) {
                switch ($col.t) {
                    "s"         { [void]$text.Append($strings[$col.v] + "`n") 
                                  [void]$script:Cell.Add("S$($i.ToString())$($col.r)") }
                    "inlineStr" { if ($col.is.t -and $col.is.t.GetType().Name -eq "String") {
                                      $tmp = $col.is.t
                                  } elseif ($col.is.t."#text") {
                                      $tmp = $col.is.t."#text"
                                  } elseif ($col.is.r) {
                                      $tmp = ""
                                      foreach ($r in $col.is.r) {
                                          if ($r.t -and $r.t.GetType().Name -eq "String") {
                                              $tmp += $r.t
                                          } elseif ($r.t."#text") { 
                                              $tmp += $r.t."#text"
                                          }
                                      }
                                  }
                                  $tmp = $tmp -Replace "\r?\n",""
                                  $tmp = $tmp -Replace "\s+"," "
                                  [void]$text.Append($tmp + "`n")
                                  [void]$script:Cell.Add("S$($i.ToString())$($col.r)") }
                }
            }
        }
        $sr.Close()
    }
    if ($strings) { $strings.Clear() }
    $NumSheets = ($parts | %{ $_.Uri.OriginalString -Match "/xl/worksheets/sheet[0-9]+.xml" }).Length
    for ([Int]$i = 1; $i -le $NumSheets; $i++) {
        $sheetrels = $parts | ?{ $_.Uri.OriginalString -eq "/xl/worksheets/_rels/sheet$i.xml.rels" }
        if (-not $sheetrels) { continue }
        $sr  = New-Object System.IO.StreamReader $sheetrels.GetStream(),$enc
        $rel = [xml]$sr.ReadToEnd() | %{ $_.Relationships.Relationship } | ?{ $_.Type -Match "comments" }
        $sr.Close()
        if (-not $rel) { continue }
        $cfile = $([io.path]::GetFileName($rel.Target))
        $comment = $parts | ?{ $_.Uri.OriginalString -eq "/xl/$cfile" }
        $sr = New-Object System.IO.StreamReader $comment.GetStream(),$enc
        $cc = [xml]$sr.ReadToEnd() | %{ $_.comments.commentList.comment }
        foreach ($c in $cc) {
            $tmp = ""
            foreach ($r in $c.text.r) {
                if ($r.t -and $r.t.GetType().Name -eq "String") {
                    $tmp += $r.t
                } elseif ($r.t."#text") {
                    $tmp += $r.t."#text"
                }
            }
            $tmp = $tmp -Replace "\r?\n",""
            $tmp = $tmp -Replace "\s+"," "
            [void]$text.Append($tmp + "`n")
            [void]$script:Cell.Add("S$($i.ToString())$($c.ref)C")
        }
        $sr.Close()
    }
    $package.Close()
    $t = $text.ToString()
    [void]$text.Clear()
    return $t
}

Function ReadPdfFile($PdfFile) {
    if (-not (Test-Path $script:PSScriptRoot\itextsharp.dll)) { 
        Write-Host "isharptext.dllがないため $PdfFile の内容を検索できません。" -Fore Red
        return 
    }
    Add-Type -Path  $script:PSScriptRoot\itextsharp.dll
    $reader = New-Object iTextSharp.text.pdf.pdfreader -ArgumentList $PdfFile
    $text   = New-Object System.Text.StringBuilder
    for ([Int]$i = 1; $i -le $reader.NumberOfPages; $i++) {
        $strategy = New-Object iTextSharp.text.pdf.parser.SimpleTextExtractionStrategy
        [void]$text.Append([iTextSharp.text.pdf.parser.PdfTextExtractor]::GetTextFromPage($reader, $i, $strategy))
        remove-variable strategy
    }
    $reader.Close()
    $t = $text.ToString()
    [void]$text.Clear()
    return $t
}

Function RGBColor([ConsoleColor]$Color) {
    switch -Exact ($Color) {
        "Black"       { return "#000000" }
        "DarkBlue"    { return "#000080" }
        "DarkGreen"   { return "#008000" }
        "DarkCyan"    { return "#008080" }
        "DarkRed"     { return "#800000" }
        "DarkMagenta" { return "#012456" }
        "DarkYellow"  { return "#EEEDF0" }
        "Gray"        { return "#C0C0C0" }
        "DarkGray"    { return "#808080" }
        "Blue"        { return "#0000FF" }
        "Green"       { return "#008000" }
        "Cyan"        { return "#00FFFF" }
        "Red"         { return "#FF0000" }
        "Magenta"     { return "#FF00FF" }
        "Yellow"      { return "#FFFF00" }
        "White"       { return "#FFFFFF" }
        Default       { return ""        }
    }
}

Function HtmlHeader() {
    $script:Html = New-Object System.Text.StringBuilder
    [void]$script:Html.Append("<!DOCTYPE html>`n")
    [void]$script:Html.Append("<html lang=`"ja`">`n")        # <- You may want to change this
    [void]$script:Html.Append("<meta charset=`"UTF-8`">`n")
    [void]$script:Html.Append("<head>`n")
    [void]$script:Html.Append("<title>RegExS Output</title>`n")
    [void]$script:Html.Append("<style type=`"text/css`">`n")
    [void]$script:Html.Append("body {color:#ffffff; background-color:$(RGBColor("DarkMagenta"))}`n")
    [void]$script:Html.Append("pre{white-space:pre-wrap}`n")
    [void]$script:Html.Append(".fc {color:$(RGBColor($ForegroundColor)); background-color:$(RGBColor($BackgroundColor))}`n")
    [void]$script:Html.Append(".cc {color:$(RGBColor($CapturegroupColor)); background-color:$(RGBColor($BackgroundColor))}`n")
    [void]$script:Html.Append(".g {color:lime}`n")
    [void]$script:Html.Append(".y {color:yellow}`n")
    [void]$script:Html.Append("</style>`n")
    [void]$script:Html.Append("</head>`n<body>`n<pre>`n")
}

Function HtmlFooter() {
    [void]$script:Html.Append("</pre>`n</body>`n</html>")
}

Function Htmlify($t) {
    $t = $t -Replace "&", "&amp;"
    $t = $t -Replace ">", "&gt;"
    $t = $t -Replace "<", "&lt;"
    $t = $t -Replace """", "&quot;"
    return $t
}

Function Pause_and_Exit() {
    Write-Host "終了するには何かキーを押してください . . ." -Fore Green
    $Host.UI.RawUI.FlushInputBuffer()
    $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") | Out-Null
    exit
}

Function Main() {
    if ($DoubleClicked) { $Interactive = $True  }
    if ($Interactive) {
        if ($Pattern) {
            Write-Host "検索対象の正規表現を入力してください。[$Pattern]:" -NoNewline -Fore Green
            $Answer = ""
            try { $Answer = Read-Host } catch [System.Exception] {}
            if ($Answer) { $Pattern = $Answer }
        } else {
            Write-Host "検索対象の正規表現を入力してください。:" -NoNewline -Fore Green
            try { $Pattern = Read-Host } catch [System.Exception] {}
            if (-not $Pattern) { Pause_and_Exit }
        }

        Write-Host "キャプチャグループ(名前/番号)を指定してください。[$Group]:" -NoNewline -Fore Green
        $Answer = ""
        try { $Answer = (Read-Host).Trim() } catch [System.Exception] {}
        if ($Answer) { $Group = $Answer }

        Write-Host "検索対象のフォルダーを指定してください。" -Fore Green
        Add-Type -AssemblyName System.Windows.Forms
        $fbd = New-Object System.Windows.Forms.FolderBrowserDialog
        $fbd.ShowNewFolderButton = $false
        $fbd.Description = "検索対象のフォルダーを指定してください。"
        if ($Dir -and (Test-Path -LiteralPath $Dir)) {
            $fbd.SelectedPath = Convert-Path -LiteralPath $Dir
        } else {
            $fbd.SelectedPath = [string]$PWD
        }
        $Result = $fbd.ShowDialog((New-Object System.Windows.Forms.Form -Property @{TopMost = $true}))
        if ($Result -eq [System.Windows.Forms.DialogResult]::OK) { 
            $Dir = $fbd.SelectedPath 
        } else {
            Write-Host "入力値が不正です" -Fore Red; Pause_and_Exit
        }
        $fbd.Dispose()
        #Begin Get-WindowFocus
        Add-Type @"
            using System;
            using System.Runtime.InteropServices;
            public class SFW {
                [DllImport("user32.dll")]
                [return: MarshalAs(UnmanagedType.Bool)]
                public static extern bool SetForegroundWindow(IntPtr hWnd);
            }
"@
        $PPID=$PID
        For ([Int]$i=0; $i -lt 2; $i++) {
            $PPID=(Get-WmiObject Win32_Process -Filter "ProcessID=$PPID").ParentProcessID
            try {
                $WindowTitle  = (Get-Process -ID $PPID -ErrorAction SilentlyContinue).MainWindowTitle
                $WindowHandle = (Get-Process -ID $PPID -ErrorAction SilentlyContinue).MainWindowHandle
                if ($WindowTitle) { 
                    [void][SFW]::SetForegroundWindow($WindowHandle)
                    break
                }
            } catch [System.Exception] { break }
        }
        #End Get-WindowFocus

        Write-Host "サブフォルダーを検索に含めますか。Y/N [$(if($Recurse){"Y"}else{"N"})]:" -NoNewline -Fore Green
        try { $Answer = (Read-Host).Trim().ToUpper() } catch [System.Exception] {}
        switch ($Answer) {
               "Y" { $Recurse = $True  }
               "N" { $Recurse = $False }
               ""  { }
          default  { Write-Host "入力値が不正です" -Fore Red; Pause_and_Exit }
        }

        Write-Host "対象ファイル名を入力してください(ワイルドカード使用可)。[$Include]:" -NoNewline -Fore Green
        $Answer = ""
        try { $Answer = (Read-Host).Trim() } catch [System.Exception] {}
        if ($Answer) { $Include = $Answer }

        Write-Host "その他のオプションを指定しますか。Y/N [N]:" -NoNewline -Fore Green
        $Answer = ""
        try { $Answer = (Read-Host).Trim().ToUpper() } catch [System.Exception] {}
        if ($Answer -eq "Y") {
            Write-Host "Htmlファイルを出力しまますか。Y/N [$(if($HtmlOutput){"Y"}else{"N"})]:" -NoNewline -Fore Green
            try { $Answer = (Read-Host).Trim().ToUpper() } catch [System.Exception] {}
            switch ($Answer) {
                   "Y" { $HtmlOutput = $True  }
                   "N" { $HtmlOutput = $False }
                   ""  { }
              default  { Write-Host "入力値が不正です" -Fore Red; Pause_and_Exit }
            }

            Write-Host "単純文字列検索を有効にしますか。Y/N [$(if($SimpleMatch){"Y"}else{"N"})]:" -NoNewline -Fore Green
            try { $Answer = (Read-Host).Trim().ToUpper() } catch [System.Exception] {}
            switch ($Answer) {
                   "Y" { $SimpleMatch = $True  }
                   "N" { $SimpleMatch = $False }
                   ""  { }
              default  { Write-Host "入力値が不正です" -Fore Red; Pause_and_Exit }
            }

            Write-Host "大小文字を区別しないようにしますか。Y/N [$(if($IgnoreCase){"Y"}else{"N"})]:" -NoNewline -Fore Green
            try { $Answer = (Read-Host).Trim().ToUpper() } catch [System.Exception] {}
            switch ($Answer) {
                   "Y" { $IgnoreCase = $True  }
                   "N" { $IgnoreCase = $False }
                   ""  { }
              default  { Write-Host "入力値が不正です" -Fore Red; Pause_and_Exit }
            }

            Write-Host "検索前に全角文字を半角文字に変換しますか。Y/N [$(if($Narrow){"Y"}else{"N"})]:" -NoNewline -Fore Green
            try { $Answer = (Read-Host).Trim().ToUpper() } catch [System.Exception] {}
            switch ($Answer) {
                   "Y" { $Narrow = $True  }
                   "N" { $Narrow = $False }
                   ""  { }
              default  { Write-Host "入力値が不正です" -Fore Red; Pause_and_Exit }
            }

            Write-Host "除外ファイル名を入力してください(ワイルドカード使用可)。[$Exclude]:" -NoNewline -Fore Green
            $Answer = ""
            try { $Answer = (Read-Host).Trim() } catch [System.Exception] {}
            if ($Answer) { $Exclude = $Answer }

            Write-Host "Byte Order Markの無いファイルの文字コード種別を入力してください。[$Encoding]:" -NoNewline -Fore Green
            $Answer = ""
            try { $Answer = (Read-Host).Trim() } catch [System.Exception] {}
            if ($Answer) { 
                $Encoding = $Answer 
                $EncodingList = @("unkown", "string", "unicode", "byte", "bigendianunicode", `
                                  "utf8", "utf7", "utf32", "ascii", "default", "oem", "bigendianutf32")
                if (-not ($EncodingList -Contains $Encoding.ToLower())) {
                    Write-Host "入力値が不正です" -Fore Red; Pause_and_Exit
                }
            }
        } elseif ($Answer -ne "N" -and $Answer -ne "") {
            Write-Host "入力値が不正です" -Fore Red; Pause_and_Exit
        }
    }

    RegExS $Pattern $Dir $Include -e $Encoding -g $Group -h:$HtmlOutput -i:$IgnoreCase -n:$Narrow -r:$Recurse `
                                  -s:$SimpleMatch -x $Exclude `
                                  -bc $BackgroundColor -cc $CapturegroupColor -fc $ForegroundColor 

    if ($Interactive) { Pause_and_Exit }
}

Main