ADD: Feature [0001425] Search the contents of files (Alt-F7) in several encodings simultaneously

This commit is contained in:
Alexander Koblov 2023-04-15 18:10:09 +03:00
commit 20d1ccd840
7 changed files with 465 additions and 112 deletions

View file

@ -307,7 +307,7 @@ end;"/>
<PackageName Value="Image32"/>
</Item13>
</RequiredPackages>
<Units Count="269">
<Units Count="270">
<Unit0>
<Filename Value="doublecmd.lpr"/>
<IsPartOfProject Value="True"/>
@ -1972,6 +1972,14 @@ end;"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uDarwinFSWatch"/>
</Unit268>
<Unit269>
<Filename Value="fchooseencoding.pas"/>
<IsPartOfProject Value="True"/>
<ComponentName Value="frmChooseEncoding"/>
<HasResources Value="True"/>
<ResourceBaseClass Value="Form"/>
<UnitName Value="fChooseEncoding"/>
</Unit269>
</Units>
</ProjectOptions>
<CompilerOptions>

View file

@ -413,6 +413,22 @@ object frmFindDlg: TfrmFindDlg
Style = csDropDownList
TabOrder = 7
end
object btnEncoding: TKASButton
AnchorSideLeft.Control = cmbEncoding
AnchorSideLeft.Side = asrBottom
AnchorSideTop.Control = cmbEncoding
AnchorSideBottom.Control = cmbEncoding
AnchorSideBottom.Side = asrBottom
Left = 219
Height = 23
Top = 77
Width = 24
Anchors = [akTop, akLeft, akBottom]
BorderSpacing.Left = 6
TabOrder = 8
TabStop = True
OnClick = btnEncodingClick
end
object cmbFindText: TComboBoxWithDelItems
AnchorSideLeft.Control = CheksPanel
AnchorSideLeft.Side = asrBottom
@ -518,7 +534,7 @@ object frmFindDlg: TfrmFindDlg
Width = 88
Caption = 'Hexadeci&mal'
OnChange = chkHexChange
TabOrder = 8
TabOrder = 9
end
object cbOfficeXML: TCheckBox
AnchorSideLeft.Control = cbTextRegExp

View file

@ -3,7 +3,7 @@
-------------------------------------------------------------------------
Find dialog, with searching in thread
Copyright (C) 2006-2021 Alexander Koblov (alexx2000@mail.ru)
Copyright (C) 2006-2023 Alexander Koblov (alexx2000@mail.ru)
Copyright (C) 2003-2004 Radek Cervinka (radek.cervinka@centrum.cz)
This program is free software; you can redistribute it and/or modify
@ -35,8 +35,8 @@ interface
uses
Graphics, SysUtils, Classes, Controls, Forms, Dialogs, StdCtrls, ComCtrls,
ExtCtrls, Menus, EditBtn, Spin, Buttons, DateTimePicker, KASComboBox,
fAttributesEdit, uDsxModule, DsxPlugin, uFindThread, uFindFiles, uRegExprU,
uSearchTemplate, fSearchPlugin, uFileView, types, DCStrUtils,
KASButton, fAttributesEdit, uDsxModule, DsxPlugin, uFindThread, uFindFiles,
uRegExprU, uSearchTemplate, fSearchPlugin, uFileView, types, DCStrUtils,
ActnList, uOSForms, uShellContextMenu, uExceptions, uFileSystemFileSource,
uFormCommands, uHotkeyManager, LCLVersion, uWcxModule, uFileSource;
@ -127,6 +127,7 @@ type
frmContentPlugins: TfrmSearchPlugin;
gbDirectories: TGroupBox;
gbFiles: TGroupBox;
btnEncoding: TKASButton;
lblAttributes: TLabel;
lblExcludeDirectories: TLabel;
lblCurrent: TLabel;
@ -208,6 +209,7 @@ type
procedure actExecute(Sender: TObject);
procedure btnAddAttributeClick(Sender: TObject);
procedure btnAttrsHelpClick(Sender: TObject);
procedure btnEncodingClick(Sender: TObject);
procedure btnNewSearchKeyDown(Sender: TObject; var Key: word;
{%H-}Shift: TShiftState);
procedure btnSearchDeleteClick(Sender: TObject);
@ -309,6 +311,10 @@ type
procedure AfterSearchStopped; //update button states after stop search(ThreadTerminate call this method)
procedure AfterSearchFocus; //set correct focus after search stopped
procedure UpdateEncodings;
function GetEncodings(AList: TCustomComboBox): String;
procedure SetEncodings(const AEncodings: String; AList: TCustomComboBox);
procedure FindInArchive(AFileView: TFileView);
procedure FillFindOptions(out FindOptions: TSearchTemplateRec; SetStartPath: boolean);
procedure FindOptionsToDSXSearchRec(const AFindOptions: TSearchTemplateRec;
@ -405,7 +411,7 @@ uses
uFileViewNotebook, uKeyboard, uOSUtils, uArchiveFileSourceUtil,
DCOSUtils, uRegExprA, uRegExprW, uDebug, uShowMsg, uConvEncoding,
uColumns, uFileFunctions, uFileSorting, uWcxArchiveFileSource,
DCConvertEncoding, WcxPlugin
DCConvertEncoding, WcxPlugin, fChooseEncoding, dmCommonData
{$IFDEF DARKWIN}
, uDarkStyle
{$ENDIF}
@ -692,6 +698,7 @@ begin
if I >= 0 then cmbEncoding.Items.Delete(I);
cmbEncoding.Items.Insert(0, 'Default');
cmbEncoding.ItemIndex := 0;
cmbEncoding.Items.Objects[0]:= TObject(PtrInt(True));
// gray disabled fields
cbUsePluginChange(Sender);
@ -747,23 +754,17 @@ end;
{ TfrmFindDlg.cmbEncodingSelect }
procedure TfrmFindDlg.cmbEncodingSelect(Sender: TObject);
var
SupportedEncoding: Boolean;
Encoding: String;
Index, ItemIndex: Integer;
begin
Encoding := cmbEncoding.Text;
SupportedEncoding:= SingleByteEncoding(Encoding);
if (not SupportedEncoding) and TRegExprU.AvailableNew then
if (cmbEncoding.Tag = 1) then
begin
Encoding := NormalizeEncoding(Encoding);
if Encoding = EncodingDefault then Encoding := GetDefaultTextEncoding;
SupportedEncoding := Encoding = EncodingUTF8;
ItemIndex:= cmbEncoding.ItemIndex;
for Index:= 0 to cmbEncoding.Items.Count - 1 do
begin
cmbEncoding.Items.Objects[Index]:= TObject(PtrInt((ItemIndex = Index)));
end;
end;
cbTextRegExp.Enabled := cbFindText.Checked and SupportedEncoding and (not chkHex.Checked);
if not cbTextRegExp.Enabled then cbTextRegExp.Checked := False;
cbCaseSens.Enabled:= cbFindText.Checked and (not cbReplaceText.Checked) and (not chkHex.Checked) and (not cbTextRegExp.Checked);
if cbFindText.Checked and (not cbCaseSens.Enabled) then cbCaseSens.Checked := not cbTextRegExp.Checked;
UpdateEncodings;
end;
{ TfrmFindDlg.Create }
@ -786,6 +787,8 @@ begin
C.Free;
end;
dmComData.ilEditorImages.GetBitmap(44, btnEncoding.Glyph);
FCommands := TFormCommands.Create(Self, actList);
end;
@ -831,7 +834,7 @@ begin
EnableControl(cbOfficeXML, cbFindText.Checked);
lblEncoding.Enabled := cbFindText.Checked;
cbReplaceText.Checked := False;
cmbEncodingSelect(nil);
UpdateEncodings;
if not FUpdating and cmbFindText.Enabled and cmbFindText.CanSetFocus and (Sender = cbFindText) then
begin
@ -907,6 +910,7 @@ begin
cbOfficeXML.Checked := False;
cbNotContainingText.Checked := False;
cmbEncoding.ItemIndex := 0;
cmbEncoding.Tag := 1;
cmbEncodingSelect(nil);
// duplicates
@ -962,6 +966,39 @@ begin
ShowHelpOrErrorForKeyword('', edtAttrib.HelpKeyword);
end;
procedure TfrmFindDlg.btnEncodingClick(Sender: TObject);
var
I, Index, ACount: Integer;
begin
if ChooseEncoding(Self, cmbEncoding.Items) then
begin
I:= 0;
ACount:= 0;
for Index:= 0 to cmbEncoding.Items.Count - 1 do
begin
if (PtrInt(cmbEncoding.Items.Objects[Index]) <> 0) then
begin
I:= Index;
Inc(ACount);
end;
end;
if ACount > 1 then
begin
I:= 0;
end
else if (ACount = 0) then
begin
I:= 0;
ACount:= 1;
cmbEncoding.Items.Objects[I]:= TObject(PtrInt(True));
end;
cmbEncoding.Tag:= ACount;
cmbEncoding.ItemIndex:= I;
cmbEncoding.Enabled:= (ACount <= 1);
UpdateEncodings;
end;
end;
{ TfrmFindDlg.actExecute }
procedure TfrmFindDlg.actExecute(Sender: TObject);
var
@ -1073,7 +1110,7 @@ begin
begin
cbCaseSens.Checked := Boolean(cbCaseSens.Tag);
end;
cmbEncodingSelect(cmbEncoding);
UpdateEncodings;
end;
{ TfrmFindDlg.cbSelectedFilesChange }
@ -1135,9 +1172,8 @@ begin
begin
cbCaseSens.Checked := Boolean(cbCaseSens.Tag);
end;
cmbEncoding.Enabled:= not chkHex.Checked;
cbReplaceText.Enabled:= not (chkHex.Checked or cbOfficeXML.Checked);
cmbEncodingSelect(cmbEncoding);
UpdateEncodings;
end;
{ TfrmFindDlg.btnSelDirClick }
@ -1229,7 +1265,7 @@ begin
CaseSensitive := cbCaseSens.Checked;
NotContainingText := cbNotContainingText.Checked;
TextRegExp := cbTextRegExp.Checked;
TextEncoding := cmbEncoding.Text;
TextEncoding := GetEncodings(cmbEncoding);
OfficeXML := cbOfficeXML.Checked;
{ Duplicates }
Duplicates:= chkDuplicates.Checked;
@ -1380,6 +1416,80 @@ begin
end;
end;
procedure TfrmFindDlg.UpdateEncodings;
var
Index: Integer;
Encoding: String;
SupportedEncoding: Boolean;
begin
SupportedEncoding:= True;
for Index:= 0 to cmbEncoding.Items.Count - 1 do
begin
if (PtrInt(cmbEncoding.Items.Objects[Index]) <> 0) then
begin
Encoding:= cmbEncoding.Items[Index];
SupportedEncoding:= SingleByteEncoding(Encoding);
if (not SupportedEncoding) and TRegExprU.AvailableNew then
begin
Encoding := NormalizeEncoding(Encoding);
if Encoding = EncodingDefault then Encoding := GetDefaultTextEncoding;
SupportedEncoding := Encoding = EncodingUTF8;
end;
if not SupportedEncoding then Break;
end;
end;
btnEncoding.Visible:= not cbReplaceText.Checked;
btnEncoding.Enabled:= cbFindText.Checked and (not chkHex.Checked);
cmbEncoding.Enabled:= btnEncoding.Enabled and (cmbEncoding.Tag < 2);
cbTextRegExp.Enabled := cbFindText.Checked and SupportedEncoding and (not chkHex.Checked);
if not cbTextRegExp.Enabled then cbTextRegExp.Checked := False;
cbCaseSens.Enabled:= cbFindText.Checked and (not cbReplaceText.Checked) and (not chkHex.Checked) and (not cbTextRegExp.Checked);
if cbFindText.Checked and (not cbCaseSens.Enabled) then cbCaseSens.Checked := not cbTextRegExp.Checked;
end;
function TfrmFindDlg.GetEncodings(AList: TCustomComboBox): String;
var
Index: Integer;
begin
Result:= EmptyStr;
for Index:= 0 to AList.Items.Count - 1 do
begin
if (PtrInt(AList.Items.Objects[Index]) <> 0) then
begin
Result+= AList.Items[Index] + '|';
end;
end;
if Length(Result) = 0 then Result:= AList.Text;
end;
procedure TfrmFindDlg.SetEncodings(const AEncodings: String;
AList: TCustomComboBox);
var
S: TStringArray;
I, Index, ACount: Integer;
begin
ACount:= 0;
S:= SplitString(AEncodings, '|');
for Index:= 0 to High(S) do
begin
I:= AList.Items.IndexOf(S[Index]);
if (I >= 0) then
begin
Inc(ACount);
AList.Items.Objects[I]:= TObject(PtrInt(True));
end;
end;
AList.Tag:= ACount;
if ACount = 1 then AList.ItemIndex:= I;
end;
procedure TfrmFindDlg.FindInArchive(AFileView: TFileView);
var
AEnabled: Boolean;
@ -1481,12 +1591,31 @@ end;
{ TfrmFindDlg.cbReplaceTextChange }
procedure TfrmFindDlg.cbReplaceTextChange(Sender: TObject);
var
Index: Integer;
begin
EnableControl(cmbReplaceText, cbReplaceText.Checked and cbFindText.Checked);
cbNotContainingText.Checked := False;
cbNotContainingText.Enabled := (not cbReplaceText.Checked and cbFindText.Checked);
cmbEncodingSelect(cmbEncoding);
if cmbReplaceText.Enabled and (cmbEncoding.Tag > 1) then
begin
for Index:= cmbEncoding.Items.Count - 1 downto 0 do
begin
if (PtrInt(cmbEncoding.Items.Objects[Index]) <> 0) then
begin
if cmbEncoding.Tag = 1 then
begin
cmbEncoding.ItemIndex:= Index;
Break;
end;
cmbEncoding.Tag:= cmbEncoding.Tag - 1;
cmbEncoding.Items.Objects[Index]:= nil;
end;
end;
end;
UpdateEncodings;
if not FUpdating and cmbReplaceText.Enabled and cmbReplaceText.CanSetFocus then
begin
@ -2336,8 +2465,9 @@ begin
cbCaseSens.Checked := CaseSensitive;
cbNotContainingText.Checked := NotContainingText;
cbTextRegExp.Checked := TextRegExp;
cmbEncoding.Text := TextEncoding;
SetEncodings(TextEncoding, cmbEncoding);
cbOfficeXML.Checked := OfficeXML;
UpdateEncodings;
if cbFindInArchive.Enabled then
begin

44
src/fchooseencoding.lfm Normal file
View file

@ -0,0 +1,44 @@
object frmChooseEncoding: TfrmChooseEncoding
Left = 522
Height = 240
Top = 145
Width = 320
BorderStyle = bsToolWindow
Caption = 'Encoding'
ClientHeight = 240
ClientWidth = 320
OnCreate = FormCreate
Position = poOwnerFormCenter
LCLVersion = '2.2.5.0'
object ButtonPanel: TButtonPanel
Left = 6
Height = 34
Top = 200
Width = 308
OKButton.Name = 'OKButton'
OKButton.DefaultCaption = True
HelpButton.Name = 'HelpButton'
HelpButton.DefaultCaption = True
CloseButton.Name = 'CloseButton'
CloseButton.DefaultCaption = True
CancelButton.Name = 'CancelButton'
CancelButton.DefaultCaption = True
TabOrder = 0
ShowButtons = [pbOK, pbCancel]
end
object ScrollBox: TScrollBox
Left = 0
Height = 194
Top = 0
Width = 320
HorzScrollBar.Page = 1
VertScrollBar.Page = 1
Align = alClient
BorderStyle = bsNone
ChildSizing.LeftRightSpacing = 6
ChildSizing.TopBottomSpacing = 6
ChildSizing.Layout = cclLeftToRightThenTopToBottom
ChildSizing.ControlsPerLine = 1
TabOrder = 1
end
end

3
src/fchooseencoding.lrj Normal file
View file

@ -0,0 +1,3 @@
{"version":1,"strings":[
{"hash":77966471,"name":"tfrmchooseencoding.caption","sourcebytes":[69,110,99,111,100,105,110,103],"value":"Encoding"}
]}

86
src/fchooseencoding.pas Normal file
View file

@ -0,0 +1,86 @@
unit fChooseEncoding;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ButtonPanel, StdCtrls;
type
{ TfrmChooseEncoding }
TfrmChooseEncoding = class(TForm)
ButtonPanel: TButtonPanel;
ScrollBox: TScrollBox;
procedure FormCreate(Sender: TObject);
procedure CheckBoxChange(Sender: TObject);
private
FList: TStrings;
public
constructor Create(TheOwner: TComponent; AList: TStrings); reintroduce;
destructor Destroy; override;
end;
function ChooseEncoding(TheOwner: TComponent; AList: TStrings): Boolean;
implementation
uses
uConvEncoding;
function ChooseEncoding(TheOwner: TComponent; AList: TStrings): Boolean;
begin
with TfrmChooseEncoding.Create(TheOwner, AList) do
try
Result:= (ShowModal = mrOK);
if Result then AList.Assign(FList);
finally
Free;
end;
end;
{$R *.lfm}
{ TfrmChooseEncoding }
procedure TfrmChooseEncoding.CheckBoxChange(Sender: TObject);
begin
with TCheckBox(Sender) do
begin
FList.Objects[Tag]:= TObject(PtrInt(Checked));
end;
end;
constructor TfrmChooseEncoding.Create(TheOwner: TComponent; AList: TStrings);
begin
inherited Create(TheOwner);
FList:= TStringList.Create;
FList.Assign(AList);
end;
destructor TfrmChooseEncoding.Destroy;
begin
inherited Destroy;
FList.Free;
end;
procedure TfrmChooseEncoding.FormCreate(Sender: TObject);
var
Index: Integer;
CheckBox: TCheckBox;
begin
for Index:= 0 to FList.Count - 1 do
begin
CheckBox:= TCheckBox.Create(Self);
CheckBox.Parent:= ScrollBox;
CheckBox.Caption:= FList[Index];
CheckBox.Tag:= Index;
CheckBox.OnChange:= @CheckBoxChange;
CheckBox.Checked:= Boolean(PtrInt(FList.Objects[Index]));
end;
end;
end.

View file

@ -28,8 +28,8 @@ unit uFindThread;
interface
uses
Classes, SysUtils, DCStringHashListUtf8, uFindFiles, uFindEx, uFindByrMr,
uMasks, uRegExpr, uRegExprW, uWcxModule;
Classes, SysUtils, Contnrs, DCStringHashListUtf8, uFindFiles, uFindEx,
uFindByrMr, uMasks, uRegExpr, uRegExprW, uWcxModule;
type
@ -44,6 +44,18 @@ type
function Clone: TDuplicate;
end;
{ TEncoding }
TEncoding = class
FindText: String;
ReplaceText: String;
FRegExpr: TRegExprEx;
RecodeTable: TRecodeTable;
FTextSearchType: TTextSearch;
public
destructor Destroy; override;
end;
{ TFindThread }
TFindThread = class(TThread)
@ -54,19 +66,17 @@ type
FFilesFound:Integer;
FFoundFile:String;
FCurrentDepth: Integer;
FTextSearchType: TTextSearch;
FSearchText: String;
FSearchTemplate: TSearchTemplateRec;
FSelectedFiles: TStringList;
FFileChecks: TFindFileChecks;
FLinkTargets: TStringList; // A list of encountered directories (for detecting cycles)
RecodeTable:TRecodeTable;
FFilesMasks: TMaskList;
FExcludeFiles: TMaskList;
FEncodings: TObjectList;
FExcludeDirectories: TMaskList;
FFilesMasksRegExp: TRegExprW;
FExcludeFilesRegExp: TRegExprW;
FRegExpr: TRegExprEx;
FArchive: TWcxModule;
FHeader: TWcxHeader;
@ -86,8 +96,8 @@ type
function CheckFile(const Folder : String; const sr : TSearchRecEx) : Boolean;
function CheckDirectory(const CurrentDir, FolderName : String) : Boolean;
function CheckDuplicate(const Folder : String; const sr : TSearchRecEx): Boolean;
function FindInFile(const sFileName: String;sData: String; bCase, bRegExp: Boolean): Boolean;
procedure FileReplaceString(const FileName, SearchString, ReplaceString: string; bCase, bRegExp: Boolean);
function FindInFile(const sFileName: String; bCase, bRegExp: Boolean): Boolean;
procedure FileReplaceString(const FileName: String; bCase, bRegExp: Boolean);
protected
procedure Execute; override;
@ -141,12 +151,27 @@ begin
Result.Index:= Self.Index;
end;
{ TEncoding }
destructor TEncoding.Destroy;
begin
FRegExpr.Free;
inherited Destroy;
end;
{ TFindThread }
constructor TFindThread.Create(const AFindOptions: TSearchTemplateRec; SelectedFiles: TStringList);
var
S: String;
Index: Integer;
AEncoding: TEncoding;
ATextEncoding: String;
AEncodings: TStringArray;
begin
inherited Create(True);
FEncodings:= TObjectList.Create(True);
FLinkTargets := TStringList.Create;
FSearchTemplate := AFindOptions;
FSelectedFiles := SelectedFiles;
@ -162,40 +187,55 @@ begin
begin
FSearchText := FindText;
if HexValue then
begin
TextEncoding := EncodingAnsi;
FindText := HexToBin(FindText);
end
else begin
TextEncoding := NormalizeEncoding(TextEncoding);
if TextRegExp then FRegExpr := TRegExprEx.Create(TextEncoding, True);
FindText := ConvertEncoding(FindText, EncodingUTF8, TextEncoding);
ReplaceText := ConvertEncoding(ReplaceText, EncodingUTF8, TextEncoding);
end;
AEncodings:= SplitString(TextEncoding, '|');
// Determine search type
if SingleByteEncoding(TextEncoding) then
for Index:= 0 to High(AEncodings) do
begin
FTextSearchType := tsAnsi;
RecodeTable := InitRecodeTable(TextEncoding, CaseSensitive);
end
else if (CaseSensitive = False) then
begin
if TextEncoding = EncodingDefault then begin
TextEncoding := GetDefaultTextEncoding;
AEncoding:= TEncoding.Create;
ATextEncoding:= AEncodings[Index];
if HexValue then
begin
ATextEncoding := EncodingAnsi;
FindText := HexToBin(FindText);
end
else begin
ATextEncoding := NormalizeEncoding(ATextEncoding);
AEncoding.FindText := ConvertEncoding(FindText, EncodingUTF8, ATextEncoding);
AEncoding.ReplaceText := ConvertEncoding(ReplaceText, EncodingUTF8, ATextEncoding);
if TextRegExp then
begin
AEncoding.FRegExpr := TRegExprEx.Create(ATextEncoding, True);
AEncoding.FRegExpr.Expression := FSearchText;
end;
end;
if ((TextEncoding = EncodingUTF8) or (TextEncoding = EncodingUTF8BOM)) then
FTextSearchType:= tsUtf8
else if (TextEncoding = EncodingUTF16LE) then
FTextSearchType:= tsUtf16le
else if (TextEncoding = EncodingUTF16BE) then
FTextSearchType:= tsUtf16be
else
FTextSearchType:= tsOther;
end
else begin
FTextSearchType:= tsOther;
// Determine search type
if SingleByteEncoding(ATextEncoding) then
begin
AEncoding.FTextSearchType := tsAnsi;
AEncoding.RecodeTable := InitRecodeTable(ATextEncoding, CaseSensitive);
end
else if (CaseSensitive = False) then
begin
if ATextEncoding = EncodingDefault then begin
ATextEncoding := GetDefaultTextEncoding;
end;
if ((ATextEncoding = EncodingUTF8) or (ATextEncoding = EncodingUTF8BOM)) then
AEncoding.FTextSearchType:= tsUtf8
else if (ATextEncoding = EncodingUTF16LE) then
AEncoding.FTextSearchType:= tsUtf16le
else if (ATextEncoding = EncodingUTF16BE) then
AEncoding.FTextSearchType:= tsUtf16be
else
AEncoding.FTextSearchType:= tsOther;
end
else begin
AEncoding.FTextSearchType:= tsOther;
end;
FEncodings.Add(AEncoding);
if HexValue then Break;
end;
end
end;
@ -228,7 +268,7 @@ var
Index: Integer;
begin
// FItems.Add('End');
FreeAndNil(FRegExpr);
FreeAndNil(FEncodings);
FreeAndNil(FFilesMasks);
FreeAndNil(FExcludeFiles);
FreeThenNil(FLinkTargets);
@ -328,7 +368,7 @@ begin
end;
end;
function TFindThread.FindInFile(const sFileName: String; sData: String;
function TFindThread.FindInFile(const sFileName: String;
bCase, bRegExp: Boolean): Boolean;
var
fs: TFileStreamEx;
@ -347,15 +387,18 @@ var
end;
var
lastPos,
sDataLength,
DataRead: Longint;
BufferSize: Integer;
Buffer: PAnsiChar = nil;
S: String;
Index: Integer;
lastPos: Pointer;
DataRead: Integer;
fmr : TFileMapRec;
BufferSize: Integer;
sDataLength: Integer;
AEncoding: TEncoding;
Buffer: PAnsiChar = nil;
begin
Result := False;
if sData = '' then Exit;
if FSearchText = '' then Exit;
if FSearchTemplate.OfficeXML and OfficeMask.Matches(sFileName) then
begin
@ -387,47 +430,66 @@ begin
finally
fs.Free;
end;
FRegExpr.Expression := sData;
FRegExpr.SetInputString(Pointer(S), Length(S));
Exit(FRegExpr.Exec());
for Index:= 0 to FEncodings.Count - 1 do
begin
AEncoding:= TEncoding(FEncodings[Index]);
AEncoding.FRegExpr.SetInputString(Pointer(S), Length(S));
if AEncoding.FRegExpr.Exec() then Exit(True);
end;
Exit;
end;
if gUseMmapInSearch then
begin
// Memory mapping should be slightly faster and use less memory
case FTextSearchType of
tsAnsi: lastPos:= FindMmapBM(sFileName, sData, RecodeTable, @IsAborting);
tsUtf8: lastPos:= FindMmapU(sFileName, sData);
tsUtf16le: lastPos:= FindMmapW(sFileName, sData, True);
tsUtf16be: lastPos:= FindMmapW(sFileName, sData, False);
else lastPos:= FindMmap(sFileName, sData, bCase, @IsAborting);
end;
case lastPos of
0 : Exit(False);
1 : Exit(True);
// else fall back to searching via stream reading
if MapFile(sFileName, fmr) then
try
for Index:= 0 to FEncodings.Count - 1 do
begin
AEncoding:= TEncoding(FEncodings[Index]);
with AEncoding do
begin
case FTextSearchType of
tsAnsi: lastPos:= Pointer(PosMemBoyerMur(fmr.MappedFile, fmr.FileSize, FindText, RecodeTable));
tsUtf8: lastPos:= PosMemU(fmr.MappedFile, fmr.FileSize, 0, FindText, False);
tsUtf16le: lastPos:= PosMemW(fmr.MappedFile, fmr.FileSize, 0, FindText, False, True);
tsUtf16be: lastPos:= PosMemW(fmr.MappedFile, fmr.FileSize, 0, FindText, False, False);
else lastPos:= PosMem(fmr.MappedFile, fmr.FileSize, 0, FindText, bCase, False);
end;
end;
if (lastPos <> Pointer(-1)) then Exit(True);
end;
Exit;
finally
UnMapFile(fmr);
end;
// else fall back to searching via stream reading
end;
BufferSize := gCopyBlockSize;
sDataLength := Length(sData);
if sDataLength > BufferSize then
raise Exception.Create(rsMsgErrSmallBuf);
fs := TFileStreamEx.Create(sFileName, fmOpenRead or fmShareDenyNone or fmOpenNoATime);
try
if sDataLength > fs.Size then // string longer than file, cannot search
Exit;
BufferSize := gCopyBlockSize;
// Buffer is extended by sDataLength-1 and BufferSize + sDataLength - 1
// bytes are read. Then strings of length sDataLength are compared with
// sData starting from offset 0 to BufferSize-1. The remaining part of the
// buffer [BufferSize, BufferSize+sDataLength-1] is moved to the beginning,
// buffer is filled up with BufferSize bytes and the search continues.
for Index:= 0 to FEncodings.Count - 1 do
begin
fs.Seek(0, soFromBeginning);
AEncoding:= TEncoding(FEncodings[Index]);
sDataLength := Length(AEncoding.FindText);
GetMem(Buffer, BufferSize + sDataLength - 1);
if Assigned(Buffer) then
if sDataLength > BufferSize then
raise Exception.Create(rsMsgErrSmallBuf);
if sDataLength > fs.Size then // string longer than file, cannot search
Continue;
// Buffer is extended by sDataLength-1 and BufferSize + sDataLength - 1
// bytes are read. Then strings of length sDataLength are compared with
// sData starting from offset 0 to BufferSize-1. The remaining part of the
// buffer [BufferSize, BufferSize+sDataLength-1] is moved to the beginning,
// buffer is filled up with BufferSize bytes and the search continues.
GetMem(Buffer, BufferSize + sDataLength - 1);
if Assigned(Buffer) then
try
if FillBuffer(Buffer, sDataLength-1) = sDataLength-1 then
begin
@ -437,26 +499,26 @@ begin
if DataRead = 0 then
Break;
case FTextSearchType of
case AEncoding.FTextSearchType of
tsAnsi:
begin
if PosMemBoyerMur(@Buffer[0], DataRead + sDataLength - 1, sData, RecodeTable) <> -1 then
if PosMemBoyerMur(@Buffer[0], DataRead + sDataLength - 1, AEncoding.FindText, AEncoding.RecodeTable) <> -1 then
Exit(True);
end;
tsUtf8:
begin
if PosMemU(@Buffer[0], DataRead + sDataLength - 1, 0, sData, False) <> Pointer(-1) then
if PosMemU(@Buffer[0], DataRead + sDataLength - 1, 0, AEncoding.FindText, False) <> Pointer(-1) then
Exit(True);
end;
tsUtf16le,
tsUtf16be:
begin
if PosMemW(@Buffer[0], DataRead + sDataLength - 1, 0, sData, False, FTextSearchType = tsUtf16le) <> Pointer(-1) then
if PosMemW(@Buffer[0], DataRead + sDataLength - 1, 0, AEncoding.FindText, False, AEncoding.FTextSearchType = tsUtf16le) <> Pointer(-1) then
Exit(True);
end;
else
begin
if PosMem(@Buffer[0], DataRead + sDataLength - 1, 0, sData, bCase, False) <> Pointer(-1) then
if PosMem(@Buffer[0], DataRead + sDataLength - 1, 0, AEncoding.FindText, bCase, False) <> Pointer(-1) then
Exit(True);
end;
end;
@ -469,6 +531,7 @@ begin
end;
except
end;
end;
finally
FreeAndNil(fs);
@ -480,10 +543,11 @@ begin
end;
end;
procedure TFindThread.FileReplaceString(const FileName, SearchString, ReplaceString: string; bCase, bRegExp: Boolean);
procedure TFindThread.FileReplaceString(const FileName: String; bCase, bRegExp: Boolean);
var
S: String;
fs: TFileStreamEx;
AEncoding: TEncoding;
Flags : TReplaceFlags = [];
begin
fs := TFileStreamEx.Create(FileName, fmOpenRead or fmShareDenyNone);
@ -499,13 +563,15 @@ begin
fs.Free;
end;
AEncoding:= TEncoding(FEncodings[0]);
if bRegExp then
S := FRegExpr.ReplaceAll(SearchString, S, replaceString)
S := AEncoding.FRegExpr.ReplaceAll(AEncoding.FindText, S, AEncoding.ReplaceText)
else
begin
Include(Flags, rfReplaceAll);
if not bCase then Include(Flags, rfIgnoreCase);
S := StringReplace(S, SearchString, replaceString, Flags);
S := StringReplace(S, AEncoding.FindText, AEncoding.ReplaceText, Flags);
end;
fs := TFileStreamEx.Create(FileName, fmCreate);
@ -626,7 +692,7 @@ begin
begin
if Result and IsFindText then
begin
Result:= FindInFile(TargetFileName, FindText, CaseSensitive, TextRegExp);
Result:= FindInFile(TargetFileName, CaseSensitive, TextRegExp);
if NotContainingText then Result:= not Result;
mbDeleteFile(TargetFileName);
end;
@ -862,10 +928,10 @@ begin
Exit(False);
try
Result := FindInFile(IncludeTrailingBackslash(Folder) + sr.Name, FindText, CaseSensitive, TextRegExp);
Result := FindInFile(IncludeTrailingBackslash(Folder) + sr.Name, CaseSensitive, TextRegExp);
if (Result and IsReplaceText) then
FileReplaceString(IncludeTrailingBackslash(Folder) + sr.Name, FindText, ReplaceText, CaseSensitive, TextRegExp);
FileReplaceString(IncludeTrailingBackslash(Folder) + sr.Name, CaseSensitive, TextRegExp);
if NotContainingText then
Result := not Result;