-
-
Save harasakih/0f2a95e7945856d1c68e531c31684156 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl | |
# | |
# ファイルあるいはディクレトリ下全ファイルを対象に、検索・置換する | |
# =ディレクトリ指定時は、1階層下まで。再起検索はしない。 | |
# = ".." '..' `..` は検索・置換の対象外 | |
# = HS|HR は検索・置換対象を16進で指定する | |
# | |
########################################################### | |
# Next TODO | |
# 1. 16進モード時は、置換前後の表示を16進数に変更する | |
########################################################### | |
# package Replace | |
# Public-VAL | |
# $LogLevel : ログ出力レベル、dbglog()でSTDERRへ出力 | |
# @Srch : 検索対象語句 | |
# @Repl : 置換語句 | |
# @SrchF : 検索したファイル一覧 | |
# @MatchF : 検索にマッチしたファイル一覧 | |
# $FdOUT : 出力ファイルのFD。呼出側でオープンする。 | |
# $FdLOG : ログファイルのFD。呼出側でオープンする。 | |
# | |
use strict; | |
use warnings; | |
use File::Basename 'basename', 'dirname'; | |
use Getopt::Long 'GetOptions'; | |
# CONSTANT | |
my $version = "00.00"; | |
my $revision = "20220111" ; | |
my $OK = 1; | |
my $NG = 0; | |
my $ModeSRCH = "S" ; | |
my $ModeREPL = "R" ; | |
my $ModeHS = "HS" ; | |
my $ModeHR = "HR" ; | |
# OPTIONS | |
my $Opthelp = 0 ; # FALSE | |
my $Optmode = ""; # S|R|HR|HS | |
my $Optsrch = ""; | |
my $Optdir = ""; | |
my $Optout = ""; | |
my $Optlog = ""; | |
my $Optsummary = 0; # FALSE | |
my $Optsrchdump = 0; # FALSE | |
my $Optloglevel = 1; | |
GetOptions( | |
'help' => \$Opthelp, | |
'mode=s' => \$Optmode, | |
'srch=s' => \$Optsrch, | |
'dir=s' => \$Optdir, | |
'out=s' => \$Optout, | |
'log=s' => \$Optlog, | |
'summary' => \$Optsummary, | |
'srchdump' => \$Optsrchdump, | |
'LL=i' => \$Optloglevel | |
); | |
# --------------------------------------------------------- | |
package Replace; | |
# --------------------------------------------------------- | |
# Public-VAL | |
our $Loglevel = 0; # DBGログの出力レベル | |
our @Srch; # 検索対象HEX-VALの配列 | |
our @Repl; # 置換後HEX-VALの配列 | |
our @SrchF; # 検索したファイル名の配列 | |
our @MatchF; # マッチしたファイル名の配列 | |
our $FdOUT; # 出力ファイルのFD | |
our $FdLOG; # ログファイルのFD | |
our $Mode; # 動作モード S(earch)|R(eplace) | |
our $DnOUT; # 出力ディレクトリ | |
############################# | |
# SHARED only package | |
############################# | |
# LOG-LEVEL | |
my $Ldie = 0; | |
my $Lerr = 1; | |
my $Linf = 3; | |
my $Ldbg = 5; | |
my $Lfnc = 6; | |
my @Msgtag = ("DIE", "ERR", "2", "INF", "4", "DBG", "FNC", "7"); | |
my $Fn = ""; # 処理中ファイル名 | |
my $Lno = 0; # 処理中行番号 | |
my $TokenNo = 0; # 処理中のトークン番号 | |
# | |
################################################# | |
# dbglog : STDERRに出力する | |
# -- PARM -- | |
# $msglevel i : @msgの出力レベル、$Loglevelより小さ時に出力する | |
# @msg i : 出力MSGの配列 | |
# -- RETURN -- | |
# $OK | |
################################################# | |
sub dbglog { | |
my ($msglevel, @msg) = @_; | |
# my $myfunc = (caller(0))[3]; print "$myfunc\n"; | |
# | |
($msglevel > 7 || $msglevel < 0) && die "!!DIE msglevel invalid:$msglevel:$!"; | |
if($msglevel <= $Loglevel) { | |
foreach my $msg(@msg) { | |
printf STDERR ("!!%s:%s\n", $Msgtag[$msglevel], $msg); | |
} | |
} | |
return $OK; | |
} | |
################################################# | |
# make_Srch : 検索ファイルをOPEN/CLOSEし、@Srch,@Replに設定する | |
# -- PARM -- | |
# $fnSRCH i : 検索ファイルのファイル名 | |
# $mode i : 検索(S)か、置換(R)か | |
# -- RETURN -- | |
# $OK | |
# -- GLOVAL -- | |
# @Srch G : 検索対象語句 | |
# @Repl G : 置換先語句 | |
################################################# | |
sub make_Srch { | |
my ($fnSRCH, $mode) = @_; | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
## | |
open(SRCH, $fnSRCH) || die "!!DIE $fnSRCH:$!"; | |
my $i = 0; | |
while( <SRCH> ) { | |
chomp; | |
my $l = $_; | |
next if ($l =~ /^#/); # コメント | |
next if ($l =~ /^\s*$/); # 空行 | |
my @srch = split(/\s+/, $l); | |
if( $mode eq $ModeREPL ) { | |
# $# は配列の要素数ではなく、添字の最大数 | |
$Srch[ $i ] = ($#srch >= 0) ? $srch[0] : ""; | |
$Repl[ $i ] = ($#srch >= 1) ? $srch[1] : ""; | |
$i++; | |
} elsif( $mode eq $ModeSRCH ) { | |
$Srch[ $i ] = ($#srch >= 0) ? $srch[0] : ""; | |
$i++; | |
} elsif( ($mode eq $ModeHR) && | |
($srch[0] =~ /^[0-9a-f]+$/) && ($srch[1] =~ /^[0-9a-f]+$/) ) | |
{ | |
$Srch[ $i ] = ($#srch >= 0) ? $srch[0] : ""; | |
$Repl[ $i ] = ($#srch >= 1) ? $srch[1] : ""; | |
$i++; | |
} elsif( ($mode eq $ModeHS) && | |
($srch[0] =~ /^[0-9a-f]+$/) ) | |
{ | |
$Srch[ $i ] = ($#srch >= 0) ? $srch[0] : ""; | |
$i++; | |
} else { | |
die "!!DIE mode invalid $mode:$!"; | |
} | |
} | |
close(SRCH); | |
return $OK; | |
} | |
################################################# | |
# srchdump : @Srch,@Replを、DBGログと、ログに出力する | |
# -- PARM -- | |
# -- RETURN -- | |
# $OK | |
# -- GLOVAL -- | |
# $FdLOG G : ログフィルのFD | |
# @Srch G : 検索対象語句 | |
# @Repl G : 置換先語句 | |
################################################# | |
sub srchdump { | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# STDERRへ出力 | |
&dbglog($Linf, ("--- SRCH ---", @Srch)); | |
&dbglog($Linf, ("--- REPL ---", @Repl)); | |
# LOGへ出力 | |
print $FdLOG "--- SRCH ---\n"; | |
foreach my $s(@Srch) { | |
print $FdLOG "$s\n"; | |
} | |
print $FdLOG "--- REPL ---\n"; | |
foreach my $r(@Repl) { | |
print $FdLOG "$r\n"; | |
} | |
# | |
return $OK; | |
} | |
################################################# | |
# isclosed : ” ' ` の数を数え、ペアになっていつかチェックする | |
# -- PARM -- | |
# $line i : チェック対象 | |
# -- RETURN -- | |
# $OK o : ペアになっている時 | |
# $NG o : ペアでない時 | |
# -- GLOVAL -- | |
################################################# | |
sub isclosed { | |
my ($line) = @_; | |
# my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# QUOTE,APOSTの数を数えて、非閉を検知する | |
my $nquote = 0; # "の数 | |
my $napost = 0; # ’の数 | |
my $backquote = 0; # `の数 | |
# 引用符の数をカウントする | |
$nquote++ while $line =~ /"/g; | |
$napost++ while $line =~ /'/g; | |
$backquote++ while $line =~ /`/g; | |
if( ($nquote % 2 != 0) || ($napost % 2 != 0) || ($backquote %2 != 0) ) { | |
return $NG; | |
} else { | |
return $OK; | |
} | |
} | |
################################################# | |
# delpairsub : $aのペア部分を削除する | |
# -- PARM -- | |
# $line i : 処理対象の文字列 | |
# $a i : ペアとする文字 | |
# -- RETURN -- | |
# $cnt o : 削除したペアの数 | |
# $retstr o : $lineから、ペアを削除した文字列 | |
# @pair o : $lineから抽出したペアを配列で持つ | |
# -- GLOVAL -- | |
################################################# | |
sub delpairsub { | |
my ($line, $a) = @_; | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my @pair = (); | |
my $retstr = ""; | |
my $cnt = 0; | |
# $aのペアを検索する、最短一致なので ? を付加 | |
$_ = $line; | |
while( /$a.*?$a/ ) { # 最短一致を繰り返す | |
$cnt++; | |
my $str =$&; # 一致した文字列 | |
$retstr .= $`; # 一致より前(一致しない部分) を 連結 | |
push(@pair, $str); # 一致した文字列 を PUSH | |
$_ = $'; # 一致した残りを次に検索する | |
} | |
$retstr .= $_; # 最後に一致しなかった残り を 連結 | |
return ($cnt, $retstr, @pair); | |
} | |
################################################# | |
# delpair : ” ' ` のペアを削除する | |
# -- PARM -- | |
# $line i : 処理対象の文字列 | |
# -- RETURN -- | |
# $n o : 削除したペアの数 | |
# $retline o : $lineから、ペアを削除した文字列 | |
# @pair o : $lineから抽出したペアを配列で持つ | |
# -- GLOVAL -- | |
################################################# | |
sub delpair { | |
my ($line) = @_; | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my $cnt = 0; | |
my @pair = (); | |
my @a = (); | |
# delete-pair | |
my $retline = $line; # 削除後ライン | |
my $n = 0; # 削除した数 | |
# 削除して $n を加算して ペア部分をpush | |
($cnt, $retline, @a) = &delpairsub($retline, "\"") ; $n += $cnt; push(@pair, @a); | |
($cnt, $retline, @a) = &delpairsub($retline, "\'") ; $n += $cnt; push(@pair, @a); | |
($cnt, $retline, @a) = &delpairsub($retline, "\`") ; $n += $cnt; push(@pair, @a); | |
# | |
return ($n, $retline, @pair); | |
} | |
################################################# | |
# getpairsub : $aのペア部分を抽出する | |
# -- PARM -- | |
# $line i : 処理対象の文字列 | |
# $a i : ペアとする文字 | |
# -- RETURN -- | |
# $cnt o : 削除したペアの数 | |
# @pair o : $lineから抽出したペアを配列で持つ | |
# -- GLOVAL -- | |
################################################# | |
sub getpairsub { | |
my ($line, $a) = @_; | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my @pair = (); | |
my $cnt = 0; | |
# QUOTEの検索 | |
$_ = $line; | |
while( /$a.*?$a/ ) { # 最短一致を繰り返す | |
$cnt++; | |
my $str =$&; # 一致した文字列 | |
push(@pair, $str); | |
$_ = $'; # 一致した残りを次に検索する | |
} | |
return ($cnt, @pair); | |
} | |
################################################# | |
# replaceL : $lineを $from を $to に置換する、引用符内も置換する | |
# 通常文字列版 | |
# -- PARM -- | |
# $line i : 処理対象の文字列 | |
# $from i : 置換元 | |
# $to i : 置換先 | |
# -- RETURN -- | |
# $cnt o : 置換した数 | |
# $modline o : 置換後の文字列 | |
# -- GLOVAL -- | |
################################################# | |
sub replaceL { | |
my ($line, $from, $to) = @_; | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my $ll = length($line); | |
my $lF = length($from); | |
my $lT = length($to); | |
my $cnt = 0; | |
# $modlineを書き換えるので、初期値設定 | |
my $modline = $line; | |
for(my $ii = 0; $ii <= $ll - $lF; ){ | |
if(substr($modline, $ii, $lF) eq $from) { | |
# 置換対象があった場合、置換前の行を初回のみ出力する | |
&dbglog($Ldbg, "$Fn($Lno)($from,TK=$TokenNo,OF=,LN=):$modline") if ($cnt == 0) ; | |
$cnt++; | |
my $offset = $ii; | |
my $tmp1 = substr($modline, 0, $offset); # 一致部分より前 | |
my $tmp2 = substr($modline, $offset + $lF); # 一致した部分より後ろ | |
$modline = $tmp1 . $to . $tmp2; # 前 + 置換後 + 後 | |
$ii += $lT; # 次の検索開始位置は、置換後文字列分分インクリメント | |
$ll = length($modline); # ストッパーを再計算する | |
# 置換ごとに出力 | |
&dbglog($Ldbg, "$Fn($Lno)($from,TK=$TokenNo,OF=$offset,LN=$lT):$modline"); | |
} else { | |
$ii += 1; # 通常文字列なので、+1インクリメント | |
} | |
} | |
return ($cnt, $modline); | |
} | |
################################################# | |
# replaceLH : 16進文字列の$lineを $from を $to に置換する、引用符内も置換する | |
# 16進文字列版 | |
# -- PARM -- | |
# $line i : 処理対象の文字列 | |
# $from i : 置換元 | |
# $to i : 置換先 | |
# -- RETURN -- | |
# $cnt o : 置換した数 | |
# $modline o : 置換後の文字列 | |
# -- GLOVAL -- | |
################################################# | |
sub replaceLH { | |
my ($line, $from, $to) = @_; | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my $ll = length($line); | |
my $lF = length($from); | |
my $lT = length($to); | |
my $cnt = 0; | |
# $modlineを書き換えるので、初期値設定 | |
my $modline = $line; | |
for(my $ii = 0; $ii <= $ll - $lF; ){ | |
if(substr($modline, $ii, $lF) eq $from) { | |
# 置換対象があった場合、置換前の行を初回のみ出力する | |
&dbglog($Ldbg, "$Fn($Lno)($from,TK=$TokenNo,OF=,LN=):$modline") if ($cnt == 0) ; | |
$cnt++; | |
my $offset = $ii; | |
my $tmp1 = substr($modline, 0, $offset); # 一致より前の部分 | |
my $tmp2 = substr($modline, $offset + $lF); # 一致より後ろの部分 | |
$modline = $tmp1 . $to . $tmp2; # 前 + 置換度 + 後 | |
$ii += $lT; # 次の検索開始位置は、置換後の長さをインクリメント | |
$ll = length($modline); # ストッパーを再計算 | |
# 置換ごとに出力 | |
my $offset2 = $offset / 2; # 16進は÷2 | |
my $lT2 = $lT / 2; # 16進は÷2 | |
&dbglog($Ldbg, "$Fn($Lno)($from,TK=$TokenNo,OF=$offset2,LN=$lT2):$modline"); | |
} else { | |
$ii += 2; # 16進は、2バイトインクリメント | |
} | |
} # 1-line | |
return ($cnt, $modline); | |
} | |
################################################# | |
# replaceF : ファイル単位に、@Srch を @Repl に置換する | |
# -- PARM -- | |
# $fname i : 処理対象のファイル名 | |
# $mode i : R|HR | |
# -- RETURN -- | |
# $OK | |
# -- GLOVAL -- | |
# @Srch G : 置換元 | |
# @Repl G : 置換先 | |
# $Fn G : 処理対象ファイル名 | |
# $Lno G : 処理対象行番号 | |
# @SrchF G : 検索対象のファイル名 | |
# @MatchF G : 置換を行なったファイル名 | |
################################################# | |
sub replaceF { | |
my ($fname, $mode) = @_; # 引数の取得 | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my $srch_cnt = @Srch; # @SRchの要素数 | |
# Open IN-file | |
open(IN, $fname) || die "!!DIE $fname:$!"; | |
binmode(IN); | |
$Fn = $fname; | |
# Open OUT-file | |
my $outbasen = File::Basename::basename $Fn; | |
my $path = $DnOUT . "/" . $outbasen; | |
if (-f $path) { | |
die "!!DIE outfile exist,$path:$!"; | |
} else { | |
open(OUT, ">$path") || die "!!DIE open,$path:$!" ; | |
binmode(OUT); | |
&dbglog($Linf,("processing in $myfunc,$fname -> $path")); | |
} | |
push(@SrchF, $fname); # 検索対象ファイル名のプッシュ | |
my $match = 0; # ファイル単位でマッチしたか | |
# LOOP until EOF | |
while( <IN> ) { | |
chomp; # 改行は検索対象外。xx80 0Aを除くため | |
my $line = $_; # 入力行 | |
my $lno = $.; # 入力ファイルの行番号 | |
$Lno = $lno; | |
#################################### | |
# ペアが閉じていない時は、ログ出力して次行へ | |
#################################### | |
if(&isclosed($line) == $NG) { | |
print $FdLOG "$fname($lno)!:$line\n"; | |
next; | |
} | |
#################################### | |
# $line -> @token -> 置換 -> @token2 | |
# 引用符内は置換しない。 | |
#################################### | |
my @token = &parseline($line); | |
my $scnt = 0; # 1行内の置換箇所数 | |
my @token2 = (); # 置換後の token | |
my $ntoken = 0; # 処理中のトークン番号 | |
foreach my $t(@token) { | |
my $tt = $t; | |
$TokenNo = $ntoken; | |
if( $tt =~ /["'`][^"'`]*["'`]/ ) { | |
# 引用符の時は、トークンをそのまま @token2にpush | |
push(@token2, $tt); | |
} elsif($mode eq $ModeREPL) { | |
# 引用符以外&通常文字列の時は、$ttを置換して @token2にpush | |
# 通常の文字列の時 | |
my $cnt = 0; | |
for(my $i = 0; $i < $srch_cnt; $i++) { | |
my $srchword = $Srch[$i]; | |
my $replword = $Repl[$i]; | |
($cnt, $tt) = &replaceL($tt, $srchword, $replword); | |
$scnt += $cnt; | |
} # every srch work | |
push(@token2, $tt); | |
} elsif($mode eq $ModeHR) { | |
# 引用符以外の時&16進文字列は、$ttをPACK&置換し・UNPACKして @token2にpush | |
# 16進の時 | |
my $cnt = 0; | |
my $hexstr = unpack("H*", $tt); | |
# print "UNP>:$Fn($Lno)$srch_cnt:$tt\n" ; | |
# print "DBG>:$Fn($Lno)$srch_cnt:$hexstr\n" ; | |
for(my $i = 0; $i < $srch_cnt; $i++) { | |
my $srchword = $Srch[$i]; | |
my $replword = $Repl[$i]; | |
($cnt, $hexstr) = &replaceLH($hexstr, $srchword, $replword); | |
$scnt += $cnt ; | |
} # every srchsword | |
# print "DBG<:$Fn($Lno)$cnt:$hexstr\n" ; | |
$tt = pack("H*",$hexstr) ; | |
# print "PACK:$Fn($Lno)$cnt:$tt\n" ; | |
push(@token2, $tt); | |
} | |
$ntoken++; | |
} # every token | |
#################################### | |
# @token2 -> $line2 | |
#################################### | |
my $line2 = ""; | |
foreach my $t(@token2) { | |
$line2 .= $t; | |
} | |
#################################### | |
# 置換があった時は、ログ出力 | |
#################################### | |
if($line2 ne $line) { | |
$match = 1 ; | |
print $FdLOG "$fname($lno)R>:$line\n"; | |
print $FdLOG "$fname($lno)R<:$line2\n"; | |
} | |
print OUT "$line2\n"; | |
} # while <> | |
if($match != 0) { push(@MatchF, $fname); } | |
# Close IN-file | |
close(IN); | |
close(OUT); | |
return $OK; | |
} | |
################################################# | |
# searchL : $lineを $srchで検索する | |
# -- PARM -- | |
# $line i : 処理対象の文字列 | |
# $srch i : 検索対象 | |
# -- RETURN -- | |
# $cnt o : ヒットした数 | |
# $line o : 処理対象をそのまま返却 | |
# -- GLOVAL -- | |
################################################# | |
sub searchL { | |
my ($line, $srch) = @_; | |
# my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my $ll = length($line); | |
my $lS = length($srch); | |
my $cnt = 0; | |
for(my $ii = 0; $ii <= $ll - $lS; ){ | |
if(substr($line, $ii, $lS) eq $srch) { | |
$cnt++; | |
my $offset = $ii; | |
$ii += $lS; | |
# 検索ごとにDBG出力 | |
&dbglog($Ldbg, "$Fn($Lno)($srch,TK=$TokenNo,OF=$offset,LN=$lS):$line"); | |
} else { | |
$ii += 1; # 通常文字列なので、1バイトづつインクリメント | |
} | |
} # 1-line | |
return ($cnt, $line); | |
} | |
################################################# | |
# searchLH : 16進文字列 $lineを $srchで検索する | |
# -- PARM -- | |
# $line i : 処理対象の文字列 | |
# $srch i : 検索対象 | |
# -- RETURN -- | |
# $cnt o : ヒットした数 | |
# $line o : 処理対象をそのまま返却 | |
# -- GLOVAL -- | |
################################################# | |
sub searchLH { | |
my ($line, $srch) = @_; | |
# my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my $ll = length($line); | |
my $lS = length($srch); | |
my $cnt = 0; | |
for(my $ii = 0; $ii <= $ll - $lS; ){ | |
if(substr($line, $ii, $lS) eq $srch) { | |
$cnt++; | |
my $offset = $ii / 2; | |
my $lS2 = $lS / 2; | |
# 検索ごとにDBG出力 | |
&dbglog($Ldbg, "$Fn($Lno)($srch,TK=$TokenNo,OF=$offset,LN=$lS2):$line"); | |
$ii += $lS; # 検索の16進文字列分インクリメント | |
} else { | |
$ii += 2; # 16進文字列なので、2バイトインクリメント | |
} | |
} # 1-line | |
return ($cnt, $line); | |
} | |
################################################# | |
# searchF : ファイル単位に、@Srch を 検索する。引用符ないは検索しない | |
# -- PARM -- | |
# $fname i : 処理対象のファイル名 | |
# -- RETURN -- | |
# $OK | |
# -- GLOVAL -- | |
# @Srch G : 検索対象 | |
# $Fn G : 処理対象ファイル名 | |
# $Lno G : 処理対象行番号 | |
# @SrchF G : 検索対象のファイル名 | |
# @MatchF G : 置換を行なったファイル名 | |
################################################# | |
sub searchF { | |
my ($fname, $mode) = @_; # 引数の取得 | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my $srch_cnt = @Srch; # @SRchの要素数 | |
# Open IN-file | |
open(IN, $fname) || die "!!DIE $fname:$!"; | |
binmode(IN); | |
&dbglog($Linf,("processing in $myfunc,$fname")); | |
$Fn = $fname; | |
# | |
push(@SrchF, $fname); # 検索対象ファイル名のプッシュ | |
my $match = 0; # ファイル単位でマッチしたか | |
# LOOP until EOF | |
while( <IN> ) { | |
chomp; # 改行は検索対象外。xx80 0Aを除くため | |
my $line = $_; # 入力行 | |
my $lno = $.; # 入力ファイルの行番号 | |
$Lno = $lno; | |
#################################### | |
# ペアが閉じていない時は、ログ出力して次へ | |
#################################### | |
if(&isclosed($line) == $NG) { | |
print $FdLOG "$fname($lno)!:$line\n"; | |
next; | |
} | |
#################################### | |
# $line -> @token -> 検索 | |
# 引用符内は検索しない。 | |
#################################### | |
my @token = &parseline($line); # 引用符/非引用符に分解 | |
my $scnt = 0; # 1行内の検索ヒット数 | |
my $ntoken = 0; # 処理中トークン番号 | |
my @match = (); # 検索にマッチした文字列 | |
foreach my $t(@token) { | |
my $tt = $t; | |
$TokenNo = $ntoken; | |
if( $tt =~ /["'`][^"'`]*["'`]/ ) { | |
# 引用符の時は、次へ | |
$ntoken++; | |
next; | |
} elsif($mode eq $ModeSRCH) { | |
# 引用符以外の時 & 通常の文字列の時 | |
my $cnt = 0; | |
for(my $i = 0; $i < $srch_cnt; $i++) { | |
my $srchword = $Srch[$i]; | |
($cnt, $tt) = &searchL($tt, $srchword); | |
$scnt += $cnt; | |
push(@match, $srchword) if($cnt != 0) ; | |
} # every srchword | |
# 引用符以外の時 & 16進の時 | |
# UNPACKして検索 | |
} elsif ($mode eq $ModeHS){ | |
my $cnt = 0; | |
my $hexstr = unpack("H*", $tt) ; | |
# print "UNP>:$Fn($Lno)$srch_cnt:$tt\n" ; | |
# print "DBG>:$Fn($Lno)$srch_cnt:$hexstr\n" ; | |
for(my $i = 0; $i < $srch_cnt; $i++) { | |
my $srchword = $Srch[$i]; # 検索16進 | |
($cnt, $hexstr) = &searchLH($hexstr, $srchword); | |
$scnt += $cnt; | |
push(@match, $srchword) if($cnt != 0); | |
} # every srchword | |
# print "DBG<:$Fn($Lno)$cnt:$hexstr\n" ; | |
$tt = pack("H*",$hexstr) ; | |
# print "PACK:$Fn($Lno)$cnt:$tt\n" ; | |
} # $mode | |
$ntoken++; | |
} # every token | |
#################################### | |
# 検索がヒットしたら、ログ出力 | |
#################################### | |
if($scnt != 0) { | |
$match = 1 ; | |
print $FdLOG "$fname($lno)S :"; | |
foreach my $m(@match) { | |
print $FdLOG "$m "; | |
} | |
print $FdLOG "\n"; | |
} | |
} # while <> | |
if($match != 0) { push(@MatchF, $fname); } | |
# Close IN-file | |
close(IN); | |
return $OK; | |
} | |
################################################# | |
# recursiveD : ディレクトリ配下の全ファイルを処理する | |
# -- PARM -- | |
# $dname i : ディレクトリ名 | |
# $mode i : S(earch)|R(eplace) | |
# -- RETURN -- | |
# $OK | |
# $NG : パラメタエラー時 | |
# -- GLOVAL -- | |
################################################# | |
sub recursiveD { | |
my ($dname, $mode) = @_; # 引数の取得 | |
my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
opendir(DIR1, $dname) || die "!!DIE $dname:$!"; | |
LBL_DIR: | |
while(my $in_fname = readdir(DIR1)) { | |
my $fname = "$dname\/$in_fname"; | |
next LBL_DIR if -d $fname; # ディレクトリは処理対象外 | |
&dbglog($Ldbg,("processing in $myfunc,$fname")); # ファイルは処理対象 | |
if ($mode eq $ModeREPL) { &replaceF( $fname, $mode ); } | |
elsif ($mode eq $ModeHR) { &replaceF( $fname, $mode ); } | |
elsif ($mode eq $ModeSRCH) { &searchF( $fname, $mode ); } | |
elsif ($mode eq $ModeHS) { &searchF( $fname, $mode ); } | |
else { die "!!DIE mode invalid,$mode:$!"; } | |
} | |
return $OK; | |
} | |
################################################# | |
# parseline : 引用符と引用符以外の部分に分解する | |
# -- PARM -- | |
# $line i : 処理対象 | |
# -- RETURN -- | |
# @token : 分解結果 | |
# -- GLOVAL -- | |
################################################# | |
sub parseline { | |
my ($line) = @_; | |
# my $myfunc = (caller(0))[3]; &dbglog($Lfnc, "$myfunc"); | |
# | |
my $cnt = 0; | |
my @token = (); | |
$_ = $line; | |
while( /["'`][^"'`]*?["'`]/ ) { # 最短一致を繰り返す | |
$cnt++; | |
my $pre = $`; # 一致した文字列より前 | |
my $match =$&; # 一致した文字列 | |
push(@token, ($pre,$match)); | |
$_ = $'; # 一致した残りを次に検索する | |
} | |
if($cnt == 0) { | |
push(@token, $line); # マッチしていない時は、そのまま返却 | |
} else { | |
push(@token, $_); # 最後に残りを追加 | |
} | |
return (@token); | |
} | |
# --------------------------------------------------------- | |
package main; | |
# --------------------------------------------------------- | |
sub usage { | |
my $basename = basename $0; | |
print STDERR "\n"; | |
print STDERR "usage: $basename Ver" . $version . "_Rev_" . $revision . "\n"; | |
print STDERR "\n"; | |
print STDERR "$basename --mode S|R|HS|HR --srch SRCH-FILE --dir SEARCHDIR|SEARCHFILE --out OUTDIR --log LOGFILE\n"; | |
print STDERR " [--summary] [--srchdump: [--LL=n]\n"; | |
print STDERR "\n"; | |
print STDERR " --mode : S(earch) | R(eplace) | H(ex)S | H(ex)R \n"; | |
print STDERR " --LL(Loglevel) $Lfnc:fn-trace $Ldbg:dbg $Linf:info $Lerr:err $Ldie:die \n"; | |
print STDERR " --summary : print Searched-file Matched-file to STDOUT\n"; | |
print STDERR " --srchdump : print SRCH-FILE to STDOUT\n"; | |
print STDERR "....\n"; | |
return $OK; | |
} | |
sub optdisp { | |
my $msgtag = ($Optloglevel =~ /^[0-7]$/) ? $Msgtag[$Optloglevel] : "" ; | |
if( $Optloglevel > 1) { | |
foreach my $msg ( | |
"--- OPTIONS ---", | |
"mode=$Optmode" , | |
"srch=$Optsrch" , | |
"dir=$Optdir" , | |
"out=$Optout" , | |
"log=$Optlog" , | |
"summary=$Optsummary", | |
"srchdump=$Optsrchdump", | |
"LL=$Optloglevel,$msgtag", | |
"---------------" ) | |
{ | |
print STDERR "$msg\n"; | |
} | |
} | |
return $OK; | |
} | |
sub optck { | |
# | |
if ($Opthelp) { | |
&usage(); | |
exit 1; | |
} | |
&optdisp(); | |
############################# | |
# IN-CHECK | |
############################# | |
# Optmode | |
if ($Optmode eq $ModeSRCH) { $Replace::Mode = $ModeSRCH; } | |
elsif ($Optmode eq $ModeREPL) { $Replace::Mode = $ModeREPL; } | |
elsif ($Optmode eq $ModeHS) { $Replace::Mode = $ModeHS; } | |
elsif ($Optmode eq $ModeHR) { $Replace::Mode = $ModeHR; } | |
else { die "!!DIE invalid mode,$Optmode:$!"; } | |
# FILE,DIR | |
($Optsrch eq '') && &usage() && die "!!DIE SRCH-FILE is null:$!" ; | |
($Optdir eq '' ) && &usage() && die "!!DIE SEARCH DIR/FILE is null:$!" ; | |
($Optout eq '' ) && &usage() && die "!!DIE OUTDIR is null:$!" ; | |
($Optlog eq '') && &usage() && die "!!DIE LOG-FILE is null:$!" ; | |
############################# | |
# FILE-CHECK | |
############################# | |
# FILE-CHECK | |
(-f $Optsrch) || &usage() && die "!!DIE SRCH-FILE,$Optsrch:$!"; | |
(-e $Optdir) || &usage() && die "!!DIE SEARECH DIR/FILE,$Optdir:$!"; | |
(-d $Optout) || &usage() && die "!!DIE OUTDIR not found,$Optout:$!"; | |
# loglevel | |
( $Optloglevel =~ /^[0-7]$/) || die "!!DIE LogLevel is 0-7,$Optloglevel:$!" ; | |
# (-f $fnLOG) || &usage() && die "!!DIE LOGFILE is exist:$!"; | |
############################# | |
# CROSS-CHECK | |
############################# | |
# Optdir, Optout | |
(-d $Optdir && ($Optdir eq $Optout)) && die "!!DIE dir[$Optdir] must not same as out[$Optout]:$!" ; | |
my $dir = dirname $Optdir; | |
($dir eq $Optout) && die "!!DIE dir[$dir] must not same as out[$Optout]:$!" ; | |
$Replace::DnOUT = $Optout; | |
return $OK; | |
} | |
############################# | |
# main | |
############################# | |
sub main { | |
# | |
my $fnSRCH = $Optsrch; | |
my $srchdir = $Optdir; | |
my $fnOUT = $Optout; | |
my $fnLOG = $Optlog; | |
my $mode = $Optmode; | |
# Open outfile | |
open($FdLOG, ">$fnLOG") || die "!!DIE $fnLOG:$!"; | |
# SRCH-FILE | |
(-f $fnSRCH) && &Replace::make_Srch( $fnSRCH, $mode ); | |
( $Optsrchdump ) && &Replace::srchdump(); | |
# MAIN | |
if( $mode eq $ModeREPL || $mode eq $ModeHR ) { | |
if (-f $srchdir) { | |
&Replace::replaceF( $srchdir, $mode ) || die "replaceF,dir=$srchdir,mode=$mode:$!"; | |
} elsif (-d $srchdir) { | |
&Replace::recursiveD( $srchdir, $mode ) || die "recursiveD,dir=$srchdir,mode=$mode:$!"; | |
} else { | |
die "!!DIE in main:$!"; | |
} | |
} elsif( $mode eq $ModeSRCH || $mode eq $ModeHS ) { | |
if (-f $srchdir) { | |
&Replace::searchF( $srchdir, $mode ) || die "searchF,dir=$srchdir,mode=$mode:$!"; | |
} elsif (-d $srchdir) { | |
&Replace::recursiveD( $srchdir, $mode ) || die "recursiveD,dir=$srchdir,mode=$mode:$!"; | |
} else { | |
die "!!DIE in main:$!" | |
} | |
} else { | |
die "!!DIE mode invalid $mode:$!" | |
} | |
my $nSrchF = @SrchF; | |
my $nMatchF = @MatchF; | |
my $basename = basename $0; | |
print STDOUT "$basename Ver" . $version . "_Rev_" . $revision . "\n"; | |
print STDOUT "mode=$Optmode\n" ; | |
print STDOUT "Searched file is : $nSrchF\n"; | |
if( $Optsummary ) { | |
foreach my $s (@SrchF) { | |
print STDOUT " $s\n"; | |
} | |
} | |
print STDOUT "Matched file is : $nMatchF\n"; | |
if( $Optsummary ) { | |
foreach my $s (@MatchF) { | |
print STDOUT " $s\n"; | |
} | |
} | |
# Close outfile | |
close($FdLOG); | |
return $OK; | |
} | |
# ----------------------------------------------------------- | |
# コマンドラインスタート | |
# ----------------------------------------------------------- | |
# optck is false. exit1 | |
$Replace::Loglevel = $Optloglevel; | |
(&optck() == $NG) && &usage() && die "!!DIE option invalid:$!"; | |
# SRCH-DUMP | |
&main(); | |
1; # RETURN TRUE |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment