Rev 2 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 2 | Rev 4 | ||
---|---|---|---|
1 | unit RedundancyForm; |
1 | unit RedundancyForm; |
2 | 2 | ||
- | 3 | // TODO: man soll einstellen können, dass er redundanzen nur innerhalb eines datenträgers (= root node) findet |
|
- | 4 | ||
3 | interface |
5 | interface |
4 | 6 | ||
5 | uses |
7 | uses |
6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, |
8 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, |
7 | System.Classes, Vcl.Graphics, AdoDb, |
9 | System.Classes, Vcl.Graphics, AdoDb, |
8 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Samples.Gauges; |
10 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Samples.Gauges; |
9 | 11 | ||
10 | type |
12 | type |
11 | TfrmRedundancy = class(TForm) |
13 | TfrmRedundancy = class(TForm) |
12 | Button1: TButton; |
14 | Button1: TButton; |
13 | Memo1: TMemo; |
15 | Memo1: TMemo; |
14 | Gauge1: TGauge; |
16 | Gauge1: TGauge; |
15 | Edit1: TEdit; |
17 | Edit1: TEdit; |
16 | Label1: TLabel; |
18 | Label1: TLabel; |
17 | Label2: TLabel; |
19 | Label2: TLabel; |
18 | ComboBox1: TComboBox; |
20 | ComboBox1: TComboBox; |
19 | Label3: TLabel; |
21 | Label3: TLabel; |
20 | Label4: TLabel; |
22 | Label4: TLabel; |
21 | procedure Button1Click(Sender: TObject); |
23 | procedure Button1Click(Sender: TObject); |
22 | procedure FormClose(Sender: TObject; var Action: TCloseAction); |
24 | procedure FormClose(Sender: TObject; var Action: TCloseAction); |
23 | procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); |
25 | procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); |
24 | procedure FormShow(Sender: TObject); |
26 | procedure FormShow(Sender: TObject); |
25 | private |
27 | private |
26 | procedure Mode2Rec(StartDir: string; const FileMask: string; |
28 | procedure Mode2Rec(StartDir: string; const FileMask: string; |
27 | var cntRedundant: integer; var cntUnique: integer); |
29 | var cntRedundant: integer; var cntUnique: integer); |
28 | protected |
30 | protected |
29 | StopRequest: Boolean; |
31 | StopRequest: Boolean; |
30 | procedure EnableDisableControls(v: Boolean); |
32 | procedure EnableDisableControls(v: Boolean); |
31 | function TableName: string; |
33 | function TableName: string; |
32 | function conn: TAdoConnection; |
34 | function conn: TAdoConnection; |
33 | end; |
35 | end; |
34 | 36 | ||
35 | implementation |
37 | implementation |
36 | 38 | ||
37 | {$R *.dfm} |
39 | {$R *.dfm} |
38 | 40 | ||
39 | uses |
41 | uses |
40 | DB, AdoConnHelper, IdHashMessageDigest, idHash, MainForm, IniFiles; |
42 | DB, AdoConnHelper, IdHashMessageDigest, idHash, MainForm, IniFiles; |
41 | 43 | ||
42 | function MD5File(const FileName: string): string; |
44 | function MD5File(const FileName: string): string; |
43 | var |
45 | var |
44 | IdMD5: TIdHashMessageDigest5; |
46 | IdMD5: TIdHashMessageDigest5; |
45 | FS: TFileStream; |
47 | FS: TFileStream; |
46 | begin |
48 | begin |
47 | IdMD5 := TIdHashMessageDigest5.Create; |
49 | IdMD5 := TIdHashMessageDigest5.Create; |
48 | FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); |
50 | FS := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); |
49 | try |
51 | try |
50 | {$IFDEF UNICODE} // I actually do not know at which version of Delphi/Indy, this has changed. |
52 | {$IFDEF UNICODE} // I actually do not know at which version of Delphi/Indy, this has changed. |
51 | Result := IdMD5.HashStreamAsHex(FS); |
53 | Result := IdMD5.HashStreamAsHex(FS); |
52 | {$ELSE} |
54 | {$ELSE} |
53 | Result := IdMD5.AsHex(IdMD5.HashValue(FS)); |
55 | Result := IdMD5.AsHex(IdMD5.HashValue(FS)); |
54 | {$ENDIF} |
56 | {$ENDIF} |
55 | finally |
57 | finally |
56 | FS.Free; |
58 | FS.Free; |
57 | IdMD5.Free; |
59 | IdMD5.Free; |
58 | end; |
60 | end; |
59 | end; |
61 | end; |
60 | 62 | ||
61 | procedure TfrmRedundancy.Mode2Rec(StartDir: string; const FileMask: string; |
63 | procedure TfrmRedundancy.Mode2Rec(StartDir: string; const FileMask: string; |
62 | var cntRedundant: integer; var cntUnique: integer); |
64 | var cntRedundant: integer; var cntUnique: integer); |
63 | 65 | ||
64 | procedure CheckFile(aFilename: string); |
66 | procedure CheckFile(aFilename: string); |
65 | var |
67 | var |
66 | md5: string; |
68 | md5: string; |
67 | begin |
69 | begin |
68 | try |
70 | try |
69 | Label4.Caption := aFilename; |
71 | Label4.Caption := aFilename; |
70 | md5 := MD5File(aFilename); |
72 | md5 := MD5File(aFilename); |
71 | if conn.GetScalar('select count(*) from ' + TableName + |
73 | if conn.GetScalar('select count(*) from ' + TableName + |
72 | ' where md5hash = ' + conn.SQLStringEscape(md5)) = 0 then |
74 | ' where md5hash = ' + conn.SQLStringEscape(md5)) = 0 then |
73 | begin |
75 | begin |
74 | Memo1.Lines.Add(aFilename); |
76 | Memo1.Lines.Add(aFilename); |
75 | Inc(cntUnique); |
77 | Inc(cntUnique); |
76 | end |
78 | end |
77 | else |
79 | else |
78 | begin |
80 | begin |
79 | Inc(cntRedundant); |
81 | Inc(cntRedundant); |
80 | end; |
82 | end; |
81 | except |
83 | except |
82 | on E: Exception do |
84 | on E: Exception do |
83 | begin |
85 | begin |
84 | Memo1.Lines.Add(Format('Error: Cannot process %s : %s', |
86 | Memo1.Lines.Add(Format('Error: Cannot process %s : %s', |
85 | [aFilename, E.Message])) |
87 | [aFilename, E.Message])) |
86 | end; |
88 | end; |
87 | end; |
89 | end; |
88 | end; |
90 | end; |
89 | 91 | ||
90 | var |
92 | var |
91 | SR: TSearchRec; |
93 | SR: TSearchRec; |
92 | DirList: TStrings; |
94 | DirList: TStrings; |
93 | IsFound: Boolean; |
95 | IsFound: Boolean; |
94 | i: integer; |
96 | i: integer; |
95 | begin |
97 | begin |
96 | StartDir := IncludeTrailingPathDelimiter(StartDir); |
98 | StartDir := IncludeTrailingPathDelimiter(StartDir); |
97 | 99 | ||
98 | i := 0; |
100 | i := 0; |
99 | IsFound := FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0; |
101 | IsFound := FindFirst(StartDir + FileMask, faAnyFile - faDirectory, SR) = 0; |
100 | try |
102 | try |
101 | while IsFound do |
103 | while IsFound do |
102 | begin |
104 | begin |
103 | Inc(i); |
105 | Inc(i); |
104 | Application.ProcessMessages; |
106 | Application.ProcessMessages; |
105 | if Application.Terminated or StopRequest then |
107 | if Application.Terminated or StopRequest then |
106 | Abort; |
108 | Abort; |
107 | 109 | ||
108 | CheckFile(StartDir + SR.Name); |
110 | CheckFile(StartDir + SR.Name); |
109 | IsFound := FindNext(SR) = 0; |
111 | IsFound := FindNext(SR) = 0; |
110 | end; |
112 | end; |
111 | finally |
113 | finally |
112 | FindClose(SR); |
114 | FindClose(SR); |
113 | end; |
115 | end; |
114 | 116 | ||
115 | // Build a list of subdirectories |
117 | // Build a list of subdirectories |
116 | DirList := TStringList.Create; |
118 | DirList := TStringList.Create; |
117 | try |
119 | try |
118 | IsFound := FindFirst(StartDir + '*', faDirectory, SR) = 0; |
120 | IsFound := FindFirst(StartDir + '*', faDirectory, SR) = 0; |
119 | try |
121 | try |
120 | while IsFound do |
122 | while IsFound do |
121 | begin |
123 | begin |
122 | if (SR.Name <> '.') and (SR.Name <> '..') then |
124 | if (SR.Name <> '.') and (SR.Name <> '..') then |
123 | begin |
125 | begin |
124 | Application.ProcessMessages; |
126 | Application.ProcessMessages; |
125 | if Application.Terminated or StopRequest then |
127 | if Application.Terminated or StopRequest then |
126 | Abort; |
128 | Abort; |
127 | 129 | ||
128 | DirList.Add(StartDir + SR.Name); |
130 | DirList.Add(StartDir + SR.Name); |
129 | end; |
131 | end; |
130 | IsFound := FindNext(SR) = 0; |
132 | IsFound := FindNext(SR) = 0; |
131 | end; |
133 | end; |
132 | finally |
134 | finally |
133 | FindClose(SR); |
135 | FindClose(SR); |
134 | end; |
136 | end; |
135 | 137 | ||
136 | // Scan the list of subdirectories |
138 | // Scan the list of subdirectories |
137 | for i := 0 to DirList.Count - 1 do |
139 | for i := 0 to DirList.Count - 1 do |
138 | begin |
140 | begin |
139 | try |
141 | try |
140 | Mode2Rec(DirList[i], FileMask, cntRedundant, cntUnique); |
142 | Mode2Rec(DirList[i], FileMask, cntRedundant, cntUnique); |
141 | except |
143 | except |
142 | on E: Exception do |
144 | on E: Exception do |
143 | begin |
145 | begin |
144 | if E is EAbort then |
146 | if E is EAbort then |
145 | Abort; |
147 | Abort; |
146 | Memo1.Lines.Add('Unexpected error at directory ' + DirList[i] + ': ' + |
148 | Memo1.Lines.Add('Unexpected error at directory ' + DirList[i] + ': ' + |
147 | E.Message); |
149 | E.Message); |
148 | end; |
150 | end; |
149 | end; |
151 | end; |
150 | end; |
152 | end; |
151 | finally |
153 | finally |
152 | DirList.Free; |
154 | DirList.Free; |
153 | end; |
155 | end; |
154 | end; |
156 | end; |
155 | 157 | ||
156 | function TfrmRedundancy.TableName: string; |
158 | function TfrmRedundancy.TableName: string; |
157 | begin |
159 | begin |
158 | result := frmMain.TableName; |
160 | result := frmMain.TableName; |
159 | end; |
161 | end; |
160 | 162 | ||
161 | procedure TfrmRedundancy.Button1Click(Sender: TObject); |
163 | procedure TfrmRedundancy.Button1Click(Sender: TObject); |
162 | var |
164 | var |
163 | q: TADODataSet; |
165 | q: TADODataSet; |
164 | fMD5: TField; |
166 | fMD5: TField; |
165 | fFilename: TField; |
167 | fFilename: TField; |
166 | dirMask: string; |
168 | dirMask: string; |
167 | cntRedundant: integer; |
169 | cntRedundant: integer; |
168 | cntUnique: integer; |
170 | cntUnique: integer; |
169 | begin |
171 | begin |
170 | EnableDisableControls(False); |
172 | EnableDisableControls(False); |
171 | if ComboBox1.ItemIndex = 1 then |
173 | if ComboBox1.ItemIndex = 1 then |
172 | Gauge1.Visible := False; |
174 | Gauge1.Visible := False; |
173 | Memo1.Lines.Clear; |
175 | Memo1.Lines.Clear; |
174 | try |
176 | try |
175 | {$REGION 'Mode 1'} |
177 | {$REGION 'Mode 1'} |
176 | if ComboBox1.ItemIndex = 0 then |
178 | if ComboBox1.ItemIndex = 0 then |
177 | begin |
179 | begin |
178 | dirMask := IncludeTrailingPathDelimiter(Edit1.Text) + '%'; |
180 | dirMask := IncludeTrailingPathDelimiter(Edit1.Text) + '%'; |
179 | q := conn.GetTable |
181 | q := conn.GetTable |
180 | ('select filename, md5hash from '+TableName+' where filename like ' + |
182 | ('select filename, md5hash from '+TableName+' where filename like ' + |
181 | conn.SQLStringEscape(dirMask) + ' order by filename'); |
183 | conn.SQLStringEscape(dirMask) + ' order by filename'); |
182 | try |
184 | try |
183 | Gauge1.MinValue := 0; |
185 | Gauge1.MinValue := 0; |
184 | Gauge1.MaxValue := q.RecordCount; |
186 | Gauge1.MaxValue := q.RecordCount; |
185 | Gauge1.Progress := 0; |
187 | Gauge1.Progress := 0; |
186 | cntRedundant := 0; |
188 | cntRedundant := 0; |
187 | cntUnique := 0; |
189 | cntUnique := 0; |
188 | fMD5 := q.FieldByName('md5hash'); |
190 | fMD5 := q.FieldByName('md5hash'); |
189 | fFilename := q.FieldByName('filename'); |
191 | fFilename := q.FieldByName('filename'); |
190 | while not q.Eof do |
192 | while not q.Eof do |
191 | begin |
193 | begin |
192 | if conn.GetScalar('select count(*) from '+TableName+' where md5hash = ' + |
194 | if conn.GetScalar('select count(*) from '+TableName+' where md5hash = ' + |
193 | conn.SQLStringEscape(fMD5.AsString) + ' and filename not like ' + |
195 | conn.SQLStringEscape(fMD5.AsString) + ' and filename not like ' + |
194 | conn.SQLStringEscape(dirMask)) = 0 then |
196 | conn.SQLStringEscape(dirMask)) = 0 then |
195 | begin |
197 | begin |
196 | Memo1.Lines.Add(fFilename.AsString); |
198 | Memo1.Lines.Add(fFilename.AsString); |
197 | Inc(cntUnique); |
199 | Inc(cntUnique); |
198 | end |
200 | end |
199 | else |
201 | else |
200 | begin |
202 | begin |
201 | Inc(cntRedundant); |
203 | Inc(cntRedundant); |
202 | end; |
204 | end; |
203 | Gauge1.Progress := Gauge1.Progress + 1; |
205 | Gauge1.Progress := Gauge1.Progress + 1; |
204 | Application.ProcessMessages; |
206 | Application.ProcessMessages; |
205 | if Application.Terminated then |
207 | if Application.Terminated then |
206 | Abort; |
208 | Abort; |
207 | q.Next; |
209 | q.Next; |
208 | end; |
210 | end; |
209 | finally |
211 | finally |
210 | q.Free; |
212 | q.Free; |
211 | end; |
213 | end; |
212 | end; |
214 | end; |
213 | {$ENDREGION} |
215 | {$ENDREGION} |
214 | {$REGION 'Mode 2'} |
216 | {$REGION 'Mode 2'} |
215 | if ComboBox1.ItemIndex = 1 then |
217 | if ComboBox1.ItemIndex = 1 then |
216 | begin |
218 | begin |
217 | cntRedundant := 0; |
219 | cntRedundant := 0; |
218 | cntUnique := 0; |
220 | cntUnique := 0; |
219 | Mode2Rec(Edit1.Text, '*', cntRedundant, cntUnique); |
221 | Mode2Rec(Edit1.Text, '*', cntRedundant, cntUnique); |
220 | end; |
222 | end; |
221 | {$ENDREGION} |
223 | {$ENDREGION} |
222 | if (cntRedundant = 0) and (cntUnique = 0) then |
224 | if (cntRedundant = 0) and (cntUnique = 0) then |
223 | raise Exception.Create('No files found. Is the string correct?') |
225 | raise Exception.Create('No files found. Is the string correct?') |
224 | else |
226 | else |
225 | ShowMessageFmt('Done. %d files are redundant. %d are unique.', |
227 | ShowMessageFmt('Done. %d files are redundant. %d are unique.', |
226 | [cntRedundant, cntUnique]); |
228 | [cntRedundant, cntUnique]); |
227 | 229 | ||
228 | if ComboBox1.ItemIndex = 0 then |
230 | if ComboBox1.ItemIndex = 0 then |
229 | begin |
231 | begin |
230 | ShowMessage |
232 | ShowMessage |
231 | ('Attention: Only check 1 directory at a time, then delete redundant files, then re-index and only then continue with checking the redundancy of the any other directory.'); |
233 | ('Attention: Only check 1 directory at a time, then delete redundant files, then re-index and only then continue with checking the redundancy of the any other directory.'); |
232 | end; |
234 | end; |
233 | finally |
235 | finally |
234 | EnableDisableControls(True); |
236 | EnableDisableControls(True); |
235 | Gauge1.Progress := 0; |
237 | Gauge1.Progress := 0; |
236 | Gauge1.Visible := True; |
238 | Gauge1.Visible := True; |
237 | Label4.Caption := ''; |
239 | Label4.Caption := ''; |
238 | end; |
240 | end; |
239 | end; |
241 | end; |
240 | 242 | ||
241 | function TfrmRedundancy.conn: TAdoConnection; |
243 | function TfrmRedundancy.conn: TAdoConnection; |
242 | begin |
244 | begin |
243 | Result := frmMain.AdoConnection1; |
245 | Result := frmMain.AdoConnection1; |
244 | end; |
246 | end; |
245 | 247 | ||
246 | procedure TfrmRedundancy.EnableDisableControls(v: Boolean); |
248 | procedure TfrmRedundancy.EnableDisableControls(v: Boolean); |
247 | begin |
249 | begin |
248 | Edit1.Enabled := v; |
250 | Edit1.Enabled := v; |
249 | Button1.Enabled := v; |
251 | Button1.Enabled := v; |
250 | ComboBox1.Enabled := v; |
252 | ComboBox1.Enabled := v; |
251 | end; |
253 | end; |
252 | 254 | ||
253 | procedure TfrmRedundancy.FormClose(Sender: TObject; var Action: TCloseAction); |
255 | procedure TfrmRedundancy.FormClose(Sender: TObject; var Action: TCloseAction); |
254 | begin |
256 | begin |
255 | Action := caFree; |
257 | Action := caFree; |
256 | end; |
258 | end; |
257 | 259 | ||
258 | procedure TfrmRedundancy.FormCloseQuery(Sender: TObject; var CanClose: Boolean); |
260 | procedure TfrmRedundancy.FormCloseQuery(Sender: TObject; var CanClose: Boolean); |
259 | begin |
261 | begin |
260 | StopRequest := True; |
262 | StopRequest := True; |
261 | end; |
263 | end; |
262 | 264 | ||
263 | procedure TfrmRedundancy.FormShow(Sender: TObject); |
265 | procedure TfrmRedundancy.FormShow(Sender: TObject); |
264 | var |
266 | var |
265 | ini: TMemIniFile; |
267 | ini: TMemIniFile; |
266 | begin |
268 | begin |
267 | ini := frmMain.ini; |
269 | ini := frmMain.ini; |
268 | Edit1.Text := ini.ReadString('RedundancyFinder', 'DefaultDir', ''); |
270 | Edit1.Text := ini.ReadString('RedundancyFinder', 'DefaultDir', ''); |
269 | ComboBox1.ItemIndex := ini.ReadInteger('RedundancyFinder', 'DefaultMode', 1)-1; |
271 | ComboBox1.ItemIndex := ini.ReadInteger('RedundancyFinder', 'DefaultMode', 1)-1; |
270 | end; |
272 | end; |
271 | 273 | ||
272 | end. |
274 | end. |
273 | 275 |