Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1 | daniel-mar | 1 | unit ZMZipFile19; |
2 | |||
3 | (* |
||
4 | ZMZipFile19.pas - Represents the 'Directory' of a Zip file |
||
5 | Copyright (C) 2009, 2010 by Russell J. Peters, Roger Aelbrecht, |
||
6 | Eric W. Engler and Chris Vleghert. |
||
7 | |||
8 | This file is part of TZipMaster Version 1.9. |
||
9 | |||
10 | TZipMaster is free software: you can redistribute it and/or modify |
||
11 | it under the terms of the GNU Lesser General Public License as published by |
||
12 | the Free Software Foundation, either version 3 of the License, or |
||
13 | (at your option) any later version. |
||
14 | |||
15 | TZipMaster is distributed in the hope that it will be useful, |
||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
18 | GNU Lesser General Public License for more details. |
||
19 | |||
20 | You should have received a copy of the GNU Lesser General Public License |
||
21 | along with TZipMaster. If not, see <http://www.gnu.org/licenses/>. |
||
22 | |||
23 | contact: problems@delphizip.org (include ZipMaster in the subject). |
||
24 | updates: http://www.delphizip.org |
||
25 | DelphiZip maillist subscribe at http://www.freelists.org/list/delphizip |
||
26 | |||
27 | modified 2010-06-20 |
||
28 | ---------------------------------------------------------------------------*) |
||
29 | interface |
||
30 | |||
31 | uses |
||
32 | Classes, Windows, ZipMstr19, ZMCore19, ZMIRec19, ZMHash19, ZMWorkFile19, ZMCompat19, ZMEOC19; |
||
33 | |||
34 | type |
||
35 | TZCChanges = (zccNone, zccBegin, zccCount, zccAdd, zccEdit, zccDelete, |
||
36 | zccEnd, zccCheckNo); |
||
37 | TZCChangeEvent = procedure(Sender: TObject; idx: Integer; |
||
38 | change: TZCChanges) of object; |
||
39 | |||
40 | type |
||
41 | TVariableData = array of Byte; |
||
42 | |||
43 | type |
||
44 | TZMCXFields = (zcxUncomp, zcxComp, zcxOffs, zcxStart); |
||
45 | |||
46 | |||
47 | |||
48 | type |
||
49 | TZMZipFile = class(TZMEOC) |
||
50 | private |
||
51 | FAddOptions: TZMAddOpts; |
||
52 | fCheckNo: Cardinal; |
||
53 | FEncodeAs: TZMEncodingOpts; |
||
54 | fEncoding: TZMEncodingOpts; |
||
55 | fEncoding_CP: Cardinal; |
||
56 | fEntries: TList; |
||
57 | fEOCFileTime: TFileTime; |
||
58 | FFirst: Integer; |
||
59 | FIgnoreDirOnly: boolean; |
||
60 | fOnChange: TZCChangeEvent; |
||
61 | fOpenRet: Integer; |
||
62 | FSelCount: integer; |
||
63 | fSFXOfs: Cardinal; |
||
64 | fShowAll: Boolean; |
||
65 | fStub: TMemoryStream; |
||
66 | fUseSFX: Boolean; |
||
67 | FWriteOptions: TZMWriteOpts; |
||
68 | function GetCount: Integer; |
||
69 | function GetItems(Idx: Integer): TZMIRec; |
||
70 | function SelectEntry(t: TZMIRec; How: TZipSelects): Boolean; |
||
71 | procedure SetCount(const Value: Integer); |
||
72 | procedure SetEncoding(const Value: TZMEncodingOpts); |
||
73 | procedure SetEncoding_CP(const Value: Cardinal); |
||
74 | procedure SetItems(Idx: Integer; const Value: TZMIRec); |
||
75 | procedure SetShowAll(const Value: Boolean); |
||
76 | procedure SetStub(const Value: TMemoryStream); |
||
77 | protected |
||
78 | fHashList: TZMDirHashList; |
||
79 | function BeforeCommit: Integer; virtual; |
||
80 | function CalcSizes(var NoEntries: Integer; var ToProcess: Int64; |
||
81 | var CenSize: Cardinal): Integer; |
||
82 | procedure ClearCachedNames; |
||
83 | procedure ClearEntries; |
||
84 | function EOCSize(Is64: Boolean): Cardinal; |
||
85 | procedure InferNumbering; |
||
86 | function Load: Integer; |
||
87 | procedure MarkDirty; |
||
88 | function Open1(EOConly: Boolean): Integer; |
||
89 | function WriteCentral: Integer; |
||
90 | property Entries: TList Read fEntries; |
||
91 | public |
||
92 | constructor Create(Wrkr: TZMCore); override; |
||
93 | function Add(rec: TZMIRec): Integer; |
||
94 | procedure AssignFrom(Src: TZMWorkFile); override; |
||
95 | procedure AssignStub(from: TZMZipFile); |
||
96 | procedure BeforeDestruction; override; |
||
97 | procedure ClearSelection; |
||
98 | function Commit(MarkLatest: Boolean): Integer; |
||
99 | function CommitAppend(Last: Integer; MarkLatest: Boolean): Integer; |
||
100 | procedure Replicate(Src: TZMZipFile; LastEntry: Integer); |
||
101 | function Entry(Chk: Cardinal; Idx: Integer): TZMIRec; |
||
102 | function FindName(const pattern: TZMString; var idx: Integer): TZMIRec; |
||
103 | overload; |
||
104 | function FindName(const pattern: TZMString; var idx: Integer; const myself: |
||
105 | TZMIRec): TZMIRec; overload; |
||
106 | function FindNameEx(const pattern: TZMString; var idx: Integer; IsWild: |
||
107 | boolean): TZMIRec; |
||
108 | function HasDupName(const rec: TZMIRec): Integer; |
||
109 | //1 Returns the number of duplicates |
||
110 | function HashContents(var HList: TZMDirHashList; what: integer): Integer; |
||
111 | //1 Mark as Contents Invalid |
||
112 | procedure Invalidate; |
||
113 | function Next(Current: Integer): integer; |
||
114 | function NextSelected(Current: Integer): integer; |
||
115 | function Open(EOConly, NoLoad: Boolean): Integer; |
||
116 | function PrepareWrite(typ: TZipWrites): Boolean; |
||
117 | function Reopen(Mode: Cardinal): integer; |
||
118 | function Select(const Pattern: TZMString; How: TZipSelects): Integer; |
||
119 | function Select1(const Pattern, reject: TZMString; How: TZipSelects): Integer; |
||
120 | function SelectFiles(const want, reject: TStrings; skipped: TStrings): Integer; |
||
121 | function VerifyOpen: Integer; |
||
122 | property AddOptions: TZMAddOpts read FAddOptions write FAddOptions; |
||
123 | property CheckNo: Cardinal Read fCheckNo; |
||
124 | property Count: Integer Read GetCount Write SetCount; |
||
125 | // how new/modified entries will be encoded |
||
126 | property EncodeAs: TZMEncodingOpts read FEncodeAs write FEncodeAs; |
||
127 | // how to interpret entry strings |
||
128 | property Encoding: TZMEncodingOpts read fEncoding write SetEncoding; |
||
129 | property Encoding_CP: Cardinal Read fEncoding_CP Write SetEncoding_CP; |
||
130 | property EOCFileTime: TFileTime Read fEOCFileTime; |
||
131 | property First: Integer read FFirst; |
||
132 | property IgnoreDirOnly: boolean read FIgnoreDirOnly write FIgnoreDirOnly; |
||
133 | property Items[Idx: Integer]: TZMIRec Read GetItems Write SetItems; default; |
||
134 | property OpenRet: Integer Read fOpenRet Write fOpenRet; |
||
135 | property SelCount: integer read FSelCount; |
||
136 | property SFXOfs: Cardinal Read fSFXOfs Write fSFXOfs; |
||
137 | property ShowAll: Boolean Read fShowAll Write SetShowAll; |
||
138 | property Stub: TMemoryStream Read fStub Write SetStub; |
||
139 | property UseSFX: Boolean Read fUseSFX Write fUseSFX; |
||
140 | property WriteOptions: TZMWriteOpts read FWriteOptions write FWriteOptions; |
||
141 | property OnChange: TZCChangeEvent Read fOnChange Write fOnChange; |
||
142 | end; |
||
143 | |||
144 | type |
||
145 | TZMCopyRec = class(TZMIRec) |
||
146 | private |
||
147 | fLink: TZMIRec; |
||
148 | procedure SetLink(const Value: TZMIRec); |
||
149 | public |
||
150 | constructor Create(theOwner: TZMWorkFile); |
||
151 | procedure AfterConstruction; override; |
||
152 | function Process: Int64; override; |
||
153 | function ProcessSize: Int64; override; |
||
154 | property Link: TZMIRec Read fLink Write SetLink; |
||
155 | end; |
||
156 | |||
157 | type |
||
158 | TZMZipCopy = class(TZMZipFile) |
||
159 | protected |
||
160 | function AffixZippedFile(rec: TZMIRec): Integer; |
||
161 | public |
||
162 | constructor Create(Wrkr: TZMCore); override; |
||
163 | function AffixZippedFiles(Src: TZMZipFile; All: Boolean): Integer; |
||
164 | function WriteFile(InZip: TZMZipFile; All: Boolean): Int64; |
||
165 | end; |
||
166 | |||
167 | const |
||
168 | BadIndex = -HIGH(Integer); |
||
169 | |||
170 | const |
||
171 | zfi_Loaded: cardinal = $1000; // central loaded |
||
172 | zfi_DidLoad: cardinal = $2000; // central loaded |
||
173 | zfi_Invalid: Cardinal = $8000; // needs reload |
||
174 | |||
175 | implementation |
||
176 | |||
177 | uses |
||
178 | SysUtils, ZMMsg19, ZMXcpt19, ZMMsgStr19, ZMStructs19, ZMDelZip19, |
||
179 | ZMUtils19, ZMMatch19, ZMUTF819; |
||
180 | |||
181 | {$INCLUDE '.\ZipVers19.inc'} |
||
182 | |||
183 | const |
||
184 | AllSpec: String = '*.*'; |
||
185 | AnySpec: String = '*'; |
||
186 | |||
187 | constructor TZMZipFile.Create(Wrkr: TZMCore); |
||
188 | begin |
||
189 | inherited; |
||
190 | fEntries := TList.Create; |
||
191 | fHashList := TZMDirHashList.Create; |
||
192 | {$IFNDEF UNICODE} |
||
193 | fHashList.Worker := Worker; |
||
194 | {$ENDIF} |
||
195 | fEncoding := Wrkr.Encoding; |
||
196 | fAddOptions := wrkr.AddOptions; |
||
197 | fEncodeAs := wrkr.EncodeAs; |
||
198 | fEncoding_CP := wrkr.Encoding_CP; |
||
199 | fIgnoreDirOnly := Wrkr.IgnoreDirOnly; |
||
200 | FWriteOptions := Wrkr.WriteOptions; |
||
201 | end; |
||
202 | |||
203 | function TZMZipFile.Add(rec: TZMIRec): Integer; |
||
204 | begin |
||
205 | Result := fEntries.Add(rec); |
||
206 | if fHashList.Empty then |
||
207 | fHashList.Add(rec); |
||
208 | end; |
||
209 | |||
210 | procedure TZMZipFile.AssignFrom(Src: TZMWorkFile); |
||
211 | begin |
||
212 | inherited; |
||
213 | if (Src is TZMZipFile) and (Src <> Self) then |
||
214 | begin |
||
215 | Replicate(TZMZipFile(Src), -1); // copy all entries |
||
216 | end; |
||
217 | end; |
||
218 | |||
219 | procedure TZMZipFile.AssignStub(from: TZMZipFile); |
||
220 | begin |
||
221 | FreeAndNil(fStub); |
||
222 | fStub := from.Stub; |
||
223 | from.fStub := nil; |
||
224 | end; |
||
225 | |||
226 | function TZMZipFile.BeforeCommit: Integer; |
||
227 | begin |
||
228 | Result := 0; |
||
229 | end; |
||
230 | |||
231 | procedure TZMZipFile.BeforeDestruction; |
||
232 | begin |
||
233 | ClearEntries; |
||
234 | FreeAndNil(fEntries); |
||
235 | FreeAndNil(fStub); |
||
236 | FreeAndNil(fHashList); |
||
237 | inherited; |
||
238 | end; |
||
239 | |||
240 | function TZMZipFile.CalcSizes(var NoEntries: Integer; var ToProcess: Int64; |
||
241 | var CenSize: Cardinal): Integer; |
||
242 | var |
||
243 | i: Integer; |
||
244 | rec: TZMIRec; |
||
245 | begin |
||
246 | Result := 0; |
||
247 | for i := 0 to Count - 1 do |
||
248 | begin |
||
249 | rec := Items[i]; |
||
250 | ToProcess := ToProcess + rec.ProcessSize; |
||
251 | CenSize := CenSize + rec.CentralSize; |
||
252 | Inc(NoEntries); |
||
253 | end; |
||
254 | end; |
||
255 | |||
256 | procedure TZMZipFile.ClearCachedNames; |
||
257 | var |
||
258 | i: Integer; |
||
259 | tmp: TObject; |
||
260 | begin |
||
261 | for i := 0 to Count - 1 do |
||
262 | begin |
||
263 | tmp := fEntries[i]; |
||
264 | if tmp is TZMIRec then |
||
265 | TZMIRec(tmp).ClearCachedName; |
||
266 | end; |
||
267 | fHashList.Clear; |
||
268 | end; |
||
269 | |||
270 | procedure TZMZipFile.ClearEntries; |
||
271 | var |
||
272 | i: Integer; |
||
273 | tmp: TObject; |
||
274 | begin |
||
275 | for i := 0 to pred(fEntries.Count) do |
||
276 | begin |
||
277 | tmp := fEntries.Items[i]; |
||
278 | if tmp <> nil then |
||
279 | begin |
||
280 | fEntries.Items[i] := nil; |
||
281 | tmp.Free; |
||
282 | end; |
||
283 | end; |
||
284 | fEntries.Clear; |
||
285 | fHashList.Clear; |
||
286 | FFirst := -1; |
||
287 | fSelCount := 0; |
||
288 | end; |
||
289 | |||
290 | procedure TZMZipFile.ClearSelection; |
||
291 | var |
||
292 | i: Integer; |
||
293 | t: TZMIRec; |
||
294 | begin |
||
295 | FSelCount := 0; |
||
296 | for i := 0 to fEntries.Count - 1 do |
||
297 | begin |
||
298 | t := fEntries[i]; |
||
299 | t.Selected := False; |
||
300 | end; |
||
301 | end; |
||
302 | |||
303 | function TZMZipFile.Commit(MarkLatest: Boolean): Integer; |
||
304 | var |
||
305 | i: Integer; |
||
306 | latest: Cardinal; |
||
307 | NoEntries: Integer; |
||
308 | ToDo: Int64; |
||
309 | r: Integer; |
||
310 | rec: TZMIRec; |
||
311 | s: Cardinal; |
||
312 | ToProcess: Int64; |
||
313 | TotalProcess: Int64; |
||
314 | w64: Int64; |
||
315 | wrote: Int64; |
||
316 | begin |
||
317 | Diag('Commit file'); |
||
318 | latest := 0; |
||
319 | wrote := 0; |
||
320 | Result := BeforeCommit; |
||
321 | if Result < 0 then |
||
322 | exit; |
||
323 | // calculate sizes |
||
324 | NoEntries := 0; |
||
325 | ToProcess := 0; |
||
326 | for i := 0 to Count - 1 do |
||
327 | begin |
||
328 | Boss.CheckCancel; |
||
329 | rec := TZMIRec(Items[i]); |
||
330 | Assert(assigned(rec), ' no rec'); |
||
331 | ToProcess := ToProcess + rec.ProcessSize; |
||
332 | Inc(NoEntries); |
||
333 | if MarkLatest and (rec.ModifDateTime > Latest) then |
||
334 | Latest := rec.ModifDateTime; |
||
335 | end; |
||
336 | // mostly right ToProcess = total compressed sizes |
||
337 | TotalProcess := ToProcess; |
||
338 | if UseSFX and assigned(Stub) and (Stub.size > 0) then |
||
339 | TotalProcess := TotalProcess + Stub.Size; |
||
340 | ProgReport(zacCount, PR_Writing, '', NoEntries + 1); |
||
341 | ProgReport(zacSize, PR_Writing, '', TotalProcess); |
||
342 | Diag(' to process ' + IntToStr(NoEntries) + ' entries'); |
||
343 | Diag(' size = ' + IntToStr(TotalProcess)); |
||
344 | Result := 0; |
||
345 | if MarkLatest then |
||
346 | begin |
||
347 | // Diag(' latest date = ' + DateTimeToStr(FileDateToLocalDateTime(latest))); |
||
348 | StampDate := latest; |
||
349 | end; |
||
350 | try |
||
351 | // if out is going to split should write proper signal |
||
352 | if IsMultiPart then |
||
353 | begin |
||
354 | s := ExtLocalSig; |
||
355 | Result := Write(s, -4); |
||
356 | if (Result <> 4) and (Result > 0) then |
||
357 | Result := -DS_NoWrite; |
||
358 | Sig := zfsMulti; |
||
359 | end |
||
360 | else // write stub if required |
||
361 | if UseSFX and assigned(Stub) and (Stub.size > 0) then |
||
362 | begin |
||
363 | // write the sfx stub |
||
364 | ProgReport(zacItem, PR_SFX, '', Stub.Size); |
||
365 | Stub.Position := 0; |
||
366 | Result := WriteFrom(Stub, Stub.Size); |
||
367 | if Result > 0 then |
||
368 | begin |
||
369 | wrote := Stub.Size; |
||
370 | ProgReport(zacProgress, PR_SFX, '', Stub.Size); |
||
371 | if ShowProgress = zspFull then |
||
372 | Boss.ProgDetail.Written(wrote); |
||
373 | Sig := zfsDOS; // assume correct |
||
374 | end; |
||
375 | end |
||
376 | else |
||
377 | Sig := zfsLocal; |
||
378 | if (Result >= 0) and (ToProcess > 0) then |
||
379 | begin |
||
380 | for i := 0 to Count - 1 do |
||
381 | begin |
||
382 | Boss.CheckCancel; |
||
383 | rec := TZMIRec(Items[i]); |
||
384 | ToDo := rec.ProcessSize; |
||
385 | if ToDo > 0 then |
||
386 | begin |
||
387 | w64 := rec.Process; |
||
388 | if w64 < 0 then |
||
389 | begin |
||
390 | Result := w64; |
||
391 | Break; |
||
392 | end; |
||
393 | wrote := wrote + w64; |
||
394 | if ShowProgress = zspFull then |
||
395 | Boss.TotalWritten := wrote; |
||
396 | end; |
||
397 | end; |
||
398 | end; |
||
399 | // finished locals and data |
||
400 | if Result >= 0 then |
||
401 | begin |
||
402 | // write central |
||
403 | Boss.ReportMsg(GE_Copying, [Boss.ZipLoadStr(DS_CopyCentral)]); |
||
404 | r := WriteCentral; // uses XProgress |
||
405 | if r >= 0 then |
||
406 | wrote := wrote + r; |
||
407 | Diag(' wrote = ' + IntToStr(wrote)); |
||
408 | if r > 0 then |
||
409 | begin |
||
410 | Result := FinishWrite; |
||
411 | if r >= 0 then |
||
412 | begin |
||
413 | Result := 0; |
||
414 | File_Size := wrote; |
||
415 | Diag(' finished ok'); |
||
416 | end; |
||
417 | end; |
||
418 | end; |
||
419 | finally |
||
420 | ProgReport(zacEndOfBatch, 7, '', 0); |
||
421 | end; |
||
422 | end; |
||
423 | |||
424 | function TZMZipFile.CommitAppend(Last: Integer; MarkLatest: Boolean): Integer; |
||
425 | var |
||
426 | i: Integer; |
||
427 | latest: Cardinal; |
||
428 | NoEntries: Integer; |
||
429 | ToDo: Int64; |
||
430 | r: Integer; |
||
431 | rec: TZMIRec; |
||
432 | ToProcess: Int64; |
||
433 | TotalProcess: Int64; |
||
434 | w64: Int64; |
||
435 | wrote: Int64; |
||
436 | begin |
||
437 | Diag('CommitAppend file'); |
||
438 | latest := 0; |
||
439 | wrote := 0; |
||
440 | // calculate sizes |
||
441 | NoEntries := 0; |
||
442 | ToProcess := 0; |
||
443 | for i := 0 to Count - 1 do |
||
444 | begin |
||
445 | Boss.CheckCancel; |
||
446 | rec := TZMIRec(Items[i]); |
||
447 | Assert(assigned(rec), ' no rec'); |
||
448 | if i >= Last then |
||
449 | begin |
||
450 | ToProcess := ToProcess + rec.ProcessSize; |
||
451 | Inc(NoEntries); |
||
452 | end; |
||
453 | if MarkLatest and (rec.ModifDateTime > latest) then |
||
454 | latest := rec.ModifDateTime; |
||
455 | end; |
||
456 | // mostly right ToProcess = total compressed sizes |
||
457 | TotalProcess := ToProcess; |
||
458 | if UseSFX and assigned(Stub) and (Stub.size > 0) and (First < 0) then |
||
459 | TotalProcess := TotalProcess + Stub.size; |
||
460 | ProgReport(zacCount, PR_Writing, '', NoEntries + 1); |
||
461 | ProgReport(zacSize, PR_Writing, '', TotalProcess); |
||
462 | Diag(' to process ' + IntToStr(NoEntries) + ' entries'); |
||
463 | Diag(' size = ' + IntToStr(TotalProcess)); |
||
464 | Result := 0; |
||
465 | if MarkLatest then |
||
466 | begin |
||
467 | // Diag(' latest date = ' + DateTimeToStr(FileDateToLocalDateTime(latest))); |
||
468 | StampDate := latest; |
||
469 | end; |
||
470 | try |
||
471 | // write stub if required |
||
472 | if UseSFX and assigned(Stub) and (Stub.size > 0) and (First < 0) then |
||
473 | begin |
||
474 | // write the sfx stub |
||
475 | ProgReport(zacItem, PR_SFX, '', Stub.size); |
||
476 | Stub.Position := 0; |
||
477 | Result := WriteFrom(Stub, Stub.size); |
||
478 | if Result > 0 then |
||
479 | begin |
||
480 | wrote := Stub.size; |
||
481 | ProgReport(zacProgress, PR_SFX, '', Stub.size); |
||
482 | if ShowProgress = zspFull then |
||
483 | Boss.ProgDetail.Written(wrote); |
||
484 | Sig := zfsDOS; // assume correct |
||
485 | end; |
||
486 | end |
||
487 | else |
||
488 | Sig := zfsLocal; |
||
489 | if (Result >= 0) and (ToProcess > 0) then |
||
490 | begin |
||
491 | for i := Last to Count - 1 do |
||
492 | begin |
||
493 | Boss.CheckCancel; |
||
494 | rec := TZMIRec(Items[i]); |
||
495 | ToDo := rec.ProcessSize; |
||
496 | if ToDo > 0 then |
||
497 | begin |
||
498 | w64 := rec.Process; |
||
499 | if w64 < 0 then |
||
500 | begin |
||
501 | Result := w64; |
||
502 | Break; |
||
503 | end; |
||
504 | wrote := wrote + w64; |
||
505 | if ShowProgress = zspFull then |
||
506 | Boss.TotalWritten := wrote; |
||
507 | end; |
||
508 | end; |
||
509 | end; |
||
510 | // finished locals and data |
||
511 | if Result >= 0 then |
||
512 | begin |
||
513 | // write central |
||
514 | Boss.ReportMsg(GE_Copying, [Boss.ZipLoadStr(DS_CopyCentral)]); |
||
515 | r := WriteCentral; // uses XProgress |
||
516 | if r >= 0 then |
||
517 | wrote := wrote + r; |
||
518 | Diag(' wrote = ' + IntToStr(wrote)); |
||
519 | if r > 0 then |
||
520 | begin |
||
521 | Result := 0; |
||
522 | File_Size := wrote; |
||
523 | Diag(' finished ok'); |
||
524 | end; |
||
525 | end; |
||
526 | finally |
||
527 | ProgReport(zacEndOfBatch, 7, '', 0); |
||
528 | end; |
||
529 | end; |
||
530 | |||
531 | function TZMZipFile.Entry(Chk: Cardinal; Idx: Integer): TZMIRec; |
||
532 | begin |
||
533 | Result := nil; |
||
534 | if (Chk = CheckNo) and (Idx >= 0) and (Idx < Count) then |
||
535 | Result := Items[Idx]; |
||
536 | end; |
||
537 | |||
538 | // Zip64 size aproximate only |
||
539 | function TZMZipFile.EOCSize(Is64: Boolean): Cardinal; |
||
540 | begin |
||
541 | Result := Cardinal(sizeof(TZipEndOfCentral) + Length(ZipComment)); |
||
542 | if Is64 then |
||
543 | Result := Result + sizeof(TZip64EOCLocator) + sizeof(TZipEOC64) + |
||
544 | (3 * sizeof(Int64)); |
||
545 | end; |
||
546 | |||
547 | function TZMZipFile.FindName(const pattern: TZMString; var idx: Integer): |
||
548 | TZMIRec; |
||
549 | begin |
||
550 | Result := FindNameEx(pattern, idx, CanHash(pattern)); |
||
551 | end; |
||
552 | |||
553 | function TZMZipFile.FindName(const pattern: TZMString; var idx: Integer; const |
||
554 | myself: TZMIRec): TZMIRec; |
||
555 | begin |
||
556 | if myself = nil then |
||
557 | Result := FindNameEx(pattern, idx, CanHash(pattern)) |
||
558 | else |
||
559 | begin |
||
560 | myself.SetStatusBit(zsbIgnore); // prevent 'finding' myself |
||
561 | Result := FindNameEx(pattern, idx, CanHash(pattern)); |
||
562 | myself.ClearStatusBit(zsbIgnore); |
||
563 | end; |
||
564 | end; |
||
565 | |||
566 | function TZMZipFile.FindNameEx(const pattern: TZMString; var idx: Integer; |
||
567 | IsWild: boolean): TZMIRec; |
||
568 | var |
||
569 | found: Boolean; |
||
570 | hash: Cardinal; |
||
571 | begin |
||
572 | found := False; |
||
573 | Result := nil; // keep compiler happy |
||
574 | hash := 0; // keep compiler happy |
||
575 | if (pattern <> '') then |
||
576 | begin |
||
577 | // if it wild or multiple we must try to match - else only if same hash |
||
578 | if (not IsWild) and (idx < 0) and (fHashList.Size > 0) then |
||
579 | Result := fHashList.Find(pattern) // do it quick |
||
580 | else |
||
581 | Begin |
||
582 | if not IsWild then |
||
583 | hash := HashFunc(pattern); |
||
584 | repeat |
||
585 | idx := Next(idx); |
||
586 | if idx < 0 then |
||
587 | break; |
||
588 | Result := Entries[idx]; |
||
589 | if IsWild or (Result.Hash = hash) then |
||
590 | begin |
||
591 | found := Worker.FNMatch(pattern, Result.Filename); |
||
592 | if Result.StatusBit[zsbIgnore] <> 0 then |
||
593 | found := false; |
||
594 | end; |
||
595 | until (found); |
||
596 | if not found then |
||
597 | Result := nil; |
||
598 | End; |
||
599 | end; |
||
600 | if Result = nil then |
||
601 | idx := BadIndex; |
||
602 | end; |
||
603 | |||
604 | function TZMZipFile.GetCount: Integer; |
||
605 | begin |
||
606 | Result := fEntries.Count; |
||
607 | end; |
||
608 | |||
609 | function TZMZipFile.GetItems(Idx: Integer): TZMIRec; |
||
610 | begin |
||
611 | if Idx >= Count then |
||
612 | Result := nil |
||
613 | else |
||
614 | Result := Entries[Idx]; |
||
615 | end; |
||
616 | |||
617 | // searches for record with same name |
||
618 | function TZMZipFile.HasDupName(const rec: TZMIRec): Integer; |
||
619 | var |
||
620 | nrec: TZMIRec; |
||
621 | begin |
||
622 | Result := -1; |
||
623 | if fHashList.Size = 0 then |
||
624 | HashContents(fHashList, 0); |
||
625 | nrec := fHashList.Add(rec); |
||
626 | if nrec <> nil then// exists |
||
627 | begin |
||
628 | Diag('Duplicate FileName: ' + rec.FileName); |
||
629 | for Result := 0 to Count - 1 do |
||
630 | begin |
||
631 | if nrec = TZMIRec(Items[Result]) then |
||
632 | break; |
||
633 | end; |
||
634 | end; |
||
635 | end; |
||
636 | |||
637 | // zsbDirty = $1; |
||
638 | // zsbSelected = $2; |
||
639 | // zsbSkipped = $4; |
||
640 | // zsbIgnore = $8; |
||
641 | // zsbDirOnly = $10; |
||
642 | // zsbInvalid = $20; |
||
643 | // what = -1 _ all |
||
644 | // else ignore rubbish |
||
645 | // what = 0 _ any non rubbish |
||
646 | function TZMZipFile.HashContents(var HList: TZMDirHashList; what: integer): |
||
647 | Integer; |
||
648 | const |
||
649 | Skip = zsbInvalid or zsbIgnore or zsbSkipped; |
||
650 | var |
||
651 | I: Integer; |
||
652 | rec: TZMIRec; |
||
653 | use: boolean; |
||
654 | begin |
||
655 | Result := 0; |
||
656 | HList.AutoSize(Count); // make required size |
||
657 | for I := 0 to Count - 1 do |
||
658 | begin |
||
659 | rec := Entries[i]; |
||
660 | if rec = nil then |
||
661 | continue; |
||
662 | use := what = -1; |
||
663 | if (not use) then |
||
664 | begin |
||
665 | if (rec.StatusBit[Skip] <> 0) then |
||
666 | continue; |
||
667 | use := (what = 0) or (rec.StatusBit[what] <> 0); |
||
668 | end; |
||
669 | if use then |
||
670 | begin |
||
671 | if HList.Add(Entries[I]) <> nil then |
||
672 | Inc(Result); // count duplicates |
||
673 | end; |
||
674 | end; |
||
675 | end; |
||
676 | |||
677 | // Use after EOC found and FileName is last part |
||
678 | // if removable has proper numbered volume name we assume it is numbered volume |
||
679 | procedure TZMZipFile.InferNumbering; |
||
680 | var |
||
681 | fname: string; |
||
682 | num: Integer; |
||
683 | numStr: string; |
||
684 | begin |
||
685 | // only if unknown |
||
686 | if (Numbering = znsNone) and (TotalDisks > 1) then |
||
687 | begin |
||
688 | if WorkDrive.DriveIsFloppy and AnsiSameText(WorkDrive.DiskName, VolName(DiskNr)) then |
||
689 | Numbering := znsVolume |
||
690 | else |
||
691 | begin |
||
692 | numStr := ''; |
||
693 | fname := ExtractNameOfFile(FileName); |
||
694 | Numbering := znsExt; |
||
695 | if Length(fname) > 3 then |
||
696 | begin |
||
697 | numStr := Copy(fname, length(fname) - 2, 3); |
||
698 | num := StrToIntDef(numStr, -1); |
||
699 | if num = (DiskNr + 1) then |
||
700 | begin |
||
701 | // ambiguous conflict |
||
702 | if WorkDrive.DriveIsFixed then |
||
703 | begin |
||
704 | if HasSpanSig(ChangeNumberedName(FileName, 1, True)) then |
||
705 | Numbering := znsName; // unless there is an orphan |
||
706 | end; |
||
707 | end; |
||
708 | end; |
||
709 | end; |
||
710 | end; |
||
711 | end; |
||
712 | |||
713 | procedure TZMZipFile.Invalidate; |
||
714 | begin |
||
715 | info := info or zfi_Invalid; |
||
716 | end; |
||
717 | |||
718 | function TZMZipFile.Load: Integer; |
||
719 | var |
||
720 | i: Integer; |
||
721 | LiE: Integer; |
||
722 | OffsetDiff: Int64; |
||
723 | r: Integer; |
||
724 | rec: TZMIRec; |
||
725 | sgn: Cardinal; |
||
726 | SOCOfs: Int64; |
||
727 | begin |
||
728 | if not IsOpen then |
||
729 | begin |
||
730 | Result := DS_FileOpen; |
||
731 | exit; |
||
732 | end; |
||
733 | Result := -LI_ErrorUnknown; |
||
734 | if (info and zfi_EOC) = 0 then |
||
735 | exit; // should not get here if eoc has not been read |
||
736 | LiE := 1; |
||
737 | OffsetDiff := 0; |
||
738 | ClearEntries; |
||
739 | fCheckNo := TZMCore(Worker).NextCheckNo; |
||
740 | if Assigned(OnChange) then |
||
741 | OnChange(Self, CheckNo, zccBegin); |
||
742 | SOCOfs := CentralOffset; |
||
743 | try |
||
744 | OffsetDiff := CentralOffset; |
||
745 | // Do we have to request for a previous disk first? |
||
746 | if DiskNr <> CentralDiskNo then |
||
747 | begin |
||
748 | SeekDisk(CentralDiskNo); |
||
749 | File_Size := Seek(0, 2); |
||
750 | end |
||
751 | else |
||
752 | if not Z64 then |
||
753 | begin |
||
754 | // Due to the fact that v1.3 and v1.4x programs do not change the archives |
||
755 | // EOC and CEH records in case of a SFX conversion (and back) we have to |
||
756 | // make this extra check. |
||
757 | OffsetDiff := File_Size - (Integer(CentralSize) + |
||
758 | SizeOf(TZipEndOfCentral) + ZipCommentLen); |
||
759 | end; |
||
760 | SOCOfs := OffsetDiff; |
||
761 | // save the location of the Start Of Central dir |
||
762 | SFXOfs := Cardinal(OffsetDiff); |
||
763 | if SFXOfs <> SOCOfs then |
||
764 | SFXOfs := 0; |
||
765 | // initialize this - we will reduce it later |
||
766 | if File_Size = 22 then |
||
767 | SFXOfs := 0; |
||
768 | |||
769 | if CentralOffset <> OffsetDiff then |
||
770 | begin |
||
771 | // We need this in the ConvertXxx functions. |
||
772 | Boss.ShowZipMessage(LI_WrongZipStruct, ''); |
||
773 | CheckSeek(CentralOffset, 0, LI_ReadZipError); |
||
774 | CheckRead(sgn, 4, DS_CEHBadRead); |
||
775 | if sgn = CentralFileHeaderSig then |
||
776 | begin |
||
777 | SOCOfs := CentralOffset; |
||
778 | // TODO warn - central size error |
||
779 | end; |
||
780 | end; |
||
781 | |||
782 | // Now we can go to the start of the Central directory. |
||
783 | CheckSeek(SOCOfs, 0, LI_ReadZipError); |
||
784 | ProgReport(zacItem, PR_Loading, '', TotalEntries); |
||
785 | // Read every entry: The central header and save the information. |
||
786 | {$IFDEF DEBUG} |
||
787 | if Boss.Verbosity >= zvTrace then |
||
788 | Diag(Format('List - expecting %d files', [TotalEntries])); |
||
789 | {$ENDIF} |
||
790 | fEntries.Capacity := TotalEntries; |
||
791 | rec := nil; |
||
792 | if Assigned(OnChange) then |
||
793 | OnChange(Self, TotalEntries, zccCount); |
||
794 | fHashList.AutoSize(TotalEntries); |
||
795 | for i := 0 to (TotalEntries - 1) do |
||
796 | begin |
||
797 | FreeAndNil(rec); |
||
798 | rec := TZMIRec.Create(Self); |
||
799 | r := rec.Read(Self); |
||
800 | if r < 0 then |
||
801 | begin |
||
802 | FreeAndNil(rec); |
||
803 | raise EZipMaster.CreateResDisp(r, True); |
||
804 | end; |
||
805 | if r > 0 then |
||
806 | Z64 := True; |
||
807 | {$IFDEF DEBUG} |
||
808 | if Boss.Verbosity >= zvTrace then //Trace then |
||
809 | Diag(Format('List - [%d] "%s"', [i, rec.FileName])); |
||
810 | {$ENDIF} |
||
811 | fEntries.Add(rec); |
||
812 | fHashList.Add(rec); |
||
813 | // Notify user, when needed, of the NextSelected entry in the ZipDir. |
||
814 | if Assigned(OnChange) then |
||
815 | OnChange(Self, i, zccAdd); // change event to give TZipDirEntry |
||
816 | |||
817 | // Calculate the earliest Local Header start |
||
818 | if SFXOfs > rec.RelOffLocal then |
||
819 | SFXOfs := rec.RelOffLocal; |
||
820 | rec := nil; // used |
||
821 | ProgReport(zacProgress, PR_Loading, '', 1); |
||
822 | Boss.CheckCancel; |
||
823 | end; // for |
||
824 | LiE := 0; // finished ok |
||
825 | Result := 0; |
||
826 | info := (info and not (zfi_MakeMask)) or zfi_Loaded; |
||
827 | finally |
||
828 | ProgReport(zacEndOfBatch, PR_Loading, '', 0); |
||
829 | if LiE = 1 then |
||
830 | begin |
||
831 | FileName := ''; |
||
832 | SFXOfs := 0; |
||
833 | File_Close; |
||
834 | end |
||
835 | else |
||
836 | begin |
||
837 | CentralOffset := SOCOfs; // corrected |
||
838 | // Correct the offset for v1.3 and 1.4x |
||
839 | SFXOfs := SFXOfs + Cardinal(OffsetDiff - CentralOffset); |
||
840 | end; |
||
841 | |||
842 | // Let the user's program know we just refreshed the zip dir contents. |
||
843 | if Assigned(OnChange) then |
||
844 | OnChange(Self, Count, zccEnd); |
||
845 | end; |
||
846 | end; |
||
847 | |||
848 | procedure TZMZipFile.MarkDirty; |
||
849 | begin |
||
850 | info := info or zfi_Dirty; |
||
851 | end; |
||
852 | |||
853 | // allow current = -1 to get first |
||
854 | // get next index, if IgnoreDirOnly = True skip DirOnly entries |
||
855 | function TZMZipFile.Next(Current: Integer): Integer; |
||
856 | var |
||
857 | cnt: Integer; |
||
858 | begin |
||
859 | Result := BadIndex; |
||
860 | if Current >= -1 then |
||
861 | begin |
||
862 | cnt := Entries.Count; |
||
863 | if IgnoreDirOnly then |
||
864 | begin |
||
865 | repeat |
||
866 | Inc(Current); |
||
867 | until (Current >= cnt) or ((TZMIRec(Entries[Current]).StatusBits and zsbDirOnly) = 0); |
||
868 | end |
||
869 | else |
||
870 | Inc(Current); |
||
871 | if Current < cnt then |
||
872 | Result := Current; |
||
873 | end; |
||
874 | end; |
||
875 | |||
876 | // return BadIndex when no more |
||
877 | function TZMZipFile.NextSelected(Current: Integer): integer; |
||
878 | var |
||
879 | k: Cardinal; |
||
880 | mask: cardinal; |
||
881 | rec: TZMIRec; |
||
882 | begin |
||
883 | Result := BadIndex; |
||
884 | mask := zsbSkipped or zsbSelected; |
||
885 | if IgnoreDirOnly then |
||
886 | mask := mask or zsbDirOnly; |
||
887 | if Current >= -1 then |
||
888 | begin |
||
889 | while Current < Entries.Count -1 do |
||
890 | begin |
||
891 | inc(Current); |
||
892 | rec := TZMIRec(Entries[Current]); |
||
893 | if rec <> nil then |
||
894 | begin |
||
895 | k := rec.StatusBit[mask]; |
||
896 | if k = zsbSelected then |
||
897 | begin |
||
898 | Result := Current; |
||
899 | break; |
||
900 | end; |
||
901 | end; |
||
902 | end; |
||
903 | end; |
||
904 | end; |
||
905 | |||
906 | function TZMZipFile.Open(EOConly, NoLoad: Boolean): Integer; |
||
907 | var |
||
908 | r: Integer; |
||
909 | begin |
||
910 | // verify disk loaded |
||
911 | ClearFileInformation; |
||
912 | info := (info and zfi_MakeMask) or zfi_Loading; |
||
913 | if WorkDrive.DriveIsFixed or WorkDrive.HasMedia(False) then |
||
914 | begin |
||
915 | Result := Open1(EOConly); |
||
916 | if (Result >= 0) then |
||
917 | begin |
||
918 | LastWriteTime(fEOCFileTime); |
||
919 | InferNumbering; |
||
920 | if not (EOConly or NoLoad) then |
||
921 | begin |
||
922 | info := info or zfi_EOC; |
||
923 | if (Result and EOCBadComment) <> 0 then |
||
924 | Boss.ShowZipMessage(DS_CECommentLen, ''); |
||
925 | if (Result and EOCBadStruct) <> 0 then |
||
926 | Boss.ShowZipMessage(LI_WrongZipStruct, ''); |
||
927 | r := Load; |
||
928 | if r <> 0 then |
||
929 | Result := r |
||
930 | else |
||
931 | begin |
||
932 | info := info or zfi_Loaded or zfi_DidLoad; |
||
933 | SaveFileInformation; // get details |
||
934 | end; |
||
935 | end; |
||
936 | end; |
||
937 | end |
||
938 | else |
||
939 | Result := -DS_NoInFile; |
||
940 | OpenRet := Result; |
||
941 | if Boss.Verbosity >= zvTrace then |
||
942 | begin |
||
943 | if Result < 0 then |
||
944 | Diag('Open = ' + Boss.ZipLoadStr(-Result)) |
||
945 | else |
||
946 | Diag('Open = ' + IntToStr(Result)); |
||
947 | end; |
||
948 | end; |
||
949 | |||
950 | function TZMZipFile.Open1(EOConly: Boolean): Integer; |
||
951 | var |
||
952 | fn: string; |
||
953 | SfxType: Integer; |
||
954 | size: Integer; |
||
955 | begin |
||
956 | SfxType := 0; // keep compiler happy |
||
957 | ReqFileName := FileName; |
||
958 | fn := FileName; |
||
959 | Result := OpenEOC(EOConly); |
||
960 | if (Result >= 0) and (Sig = zfsDOS) then |
||
961 | begin |
||
962 | stub := nil; |
||
963 | SfxType := CheckSFXType(handle, fn, size); |
||
964 | if SfxType >= cstSFX17 then |
||
965 | begin |
||
966 | if Seek(0, 0) <> 0 then |
||
967 | exit; |
||
968 | stub := TMemoryStream.Create; |
||
969 | try |
||
970 | if ReadTo(stub, size) <> size then |
||
971 | begin |
||
972 | stub := nil; |
||
973 | end; |
||
974 | except |
||
975 | stub := nil; |
||
976 | end; |
||
977 | end; |
||
978 | end; |
||
979 | if not (spExactName in SpanOptions) then |
||
980 | begin |
||
981 | if (Result >= 0) and (SfxType >= cstDetached) then |
||
982 | begin // it is last part of detached sfx |
||
983 | File_Close; |
||
984 | // Get proper path and name |
||
985 | FileName := IncludeTrailingBackslash(ExtractFilePath(ReqFileName)) + fn; |
||
986 | // find last part |
||
987 | Result := -DS_NoInFile; |
||
988 | end; |
||
989 | if Result < 0 then |
||
990 | Result := OpenLast(EOConly, Result); |
||
991 | end; |
||
992 | end; |
||
993 | |||
994 | function TZMZipFile.PrepareWrite(typ: TZipWrites): Boolean; |
||
995 | begin |
||
996 | case typ of |
||
997 | zwSingle: |
||
998 | Result := false; |
||
999 | zwMultiple: |
||
1000 | Result := True; |
||
1001 | else |
||
1002 | Result := zwoDiskSpan in WriteOptions; |
||
1003 | end; |
||
1004 | IsMultiPart := Result; |
||
1005 | if Result then |
||
1006 | begin |
||
1007 | DiskNr := 0; |
||
1008 | File_Close; |
||
1009 | end |
||
1010 | else |
||
1011 | begin |
||
1012 | DiskNr := -1; |
||
1013 | end; |
||
1014 | end; |
||
1015 | |||
1016 | function TZMZipFile.Reopen(Mode: Cardinal): integer; |
||
1017 | begin |
||
1018 | Result := 0; |
||
1019 | if (not IsOpen) or (OpenMode <> Mode) then |
||
1020 | begin |
||
1021 | File_Close; |
||
1022 | if Boss.Verbosity >= zvTrace then |
||
1023 | Diag('Trace: Reopening ' + RealFileName); |
||
1024 | if not File_Open(Mode) then |
||
1025 | begin |
||
1026 | Diag('Could not reopen: ' + RealFileName); |
||
1027 | Result := -DS_FileOpen; |
||
1028 | end; |
||
1029 | end; |
||
1030 | if (Result = 0) and ((info and zfi_Loaded) <> 0) and |
||
1031 | not VerifyFileInformation then |
||
1032 | begin |
||
1033 | Worker.Diag('File has changed! ' + RealFileName); |
||
1034 | // close it? |
||
1035 | Result := GE_FileChanged; // just complain at moment |
||
1036 | end; |
||
1037 | end; |
||
1038 | |||
1039 | procedure TZMZipFile.Replicate(Src: TZMZipFile; LastEntry: Integer); |
||
1040 | var |
||
1041 | I: Integer; |
||
1042 | rec: TZMIRec; |
||
1043 | begin |
||
1044 | if (Src <> nil) and (Src <> Self) then |
||
1045 | begin |
||
1046 | inherited AssignFrom(Src); |
||
1047 | fCheckNo := Worker.NextCheckNo; |
||
1048 | // FAddOptions := Src.FAddOptions; |
||
1049 | // FEncodeAs := Src.FEncodeAs; |
||
1050 | // fEncoding := Src.fEncoding; |
||
1051 | // fEncoding_CP := Src.fEncoding_CP; |
||
1052 | // FIgnoreDirOnly := Src.FIgnoreDirOnly; |
||
1053 | fEOCFileTime := Src.fEOCFileTime; |
||
1054 | FFirst := Src.FFirst; |
||
1055 | fOnChange := Src.fOnChange; |
||
1056 | fOpenRet := Src.fOpenRet; |
||
1057 | FSelCount := Src.FSelCount; |
||
1058 | fSFXOfs := Src.fSFXOfs; |
||
1059 | fShowAll := Src.fShowAll; |
||
1060 | fStub := nil; |
||
1061 | fUseSFX := False; |
||
1062 | if Src.UseSFX and Assigned(Src.fStub) then |
||
1063 | begin |
||
1064 | fStub := TMemoryStream.Create; |
||
1065 | Src.fStub.Position := 0; |
||
1066 | if fStub.CopyFrom(Src.fStub, Src.fStub.Size) = Src.fStub.Size then |
||
1067 | fUseSFX := True |
||
1068 | else |
||
1069 | FreeAndNil(fStub); |
||
1070 | end; |
||
1071 | // add records from Src |
||
1072 | if (LastEntry < 0) or (LastEntry > Src.Count) then |
||
1073 | LastEntry := Src.Count - 1; |
||
1074 | for I := 0 to LastEntry do |
||
1075 | begin |
||
1076 | rec := TZMIRec.Create(self); |
||
1077 | rec.AssignFrom(Src[I]); |
||
1078 | Add(rec); |
||
1079 | end; |
||
1080 | end; |
||
1081 | end; |
||
1082 | |||
1083 | // select entries matching external pattern - return number of selected entries |
||
1084 | function TZMZipFile.Select(const Pattern: TZMString; How: TZipSelects): Integer; |
||
1085 | var |
||
1086 | i: Integer; |
||
1087 | srch: Integer; |
||
1088 | t: TZMIRec; |
||
1089 | wild: Boolean; |
||
1090 | begin |
||
1091 | Result := 0; |
||
1092 | // if it wild or multiple we must try to match - else only if same hash |
||
1093 | wild := not CanHash(pattern); |
||
1094 | if (Pattern = '') or (wild and ((Pattern = AllSpec) or (Pattern = AnySpec))) then |
||
1095 | begin |
||
1096 | // do all |
||
1097 | for i := 0 to fEntries.Count - 1 do |
||
1098 | begin |
||
1099 | t := fEntries[i]; |
||
1100 | if SelectEntry(t, How) then |
||
1101 | Inc(Result); |
||
1102 | end; |
||
1103 | end |
||
1104 | else |
||
1105 | begin |
||
1106 | // select specific pattern |
||
1107 | i := -1; |
||
1108 | srch := 1; |
||
1109 | while srch <> 0 do |
||
1110 | begin |
||
1111 | t := FindNameEx(Pattern, i, wild); |
||
1112 | if t = nil then |
||
1113 | break; |
||
1114 | if SelectEntry(t, How) then |
||
1115 | Inc(Result); |
||
1116 | if srch > 0 then |
||
1117 | begin |
||
1118 | if wild then |
||
1119 | srch := -1 // search all |
||
1120 | else |
||
1121 | srch := 0; // done |
||
1122 | end; |
||
1123 | end; |
||
1124 | end; |
||
1125 | end; |
||
1126 | |||
1127 | // Select1 entries matching external pattern |
||
1128 | function TZMZipFile.Select1(const Pattern, reject: TZMString; |
||
1129 | How: TZipSelects): Integer; |
||
1130 | var |
||
1131 | args: string; |
||
1132 | i: Integer; |
||
1133 | exc: string; |
||
1134 | ptn: string; |
||
1135 | aRec: TZMIRec; |
||
1136 | wild: Boolean; |
||
1137 | begin |
||
1138 | Result := 0; |
||
1139 | args := ''; // default args - empty |
||
1140 | exc := reject; // default excludes |
||
1141 | ptn := Pattern; // need to remove switches |
||
1142 | // split Pattern into pattern and switches |
||
1143 | // if it wild or multiple we must try to match - else only if same hash |
||
1144 | wild := not CanHash(ptn); |
||
1145 | if (ptn = '') or (wild and ((ptn = AllSpec) or (ptn = AnySpec))) then |
||
1146 | begin |
||
1147 | // do all |
||
1148 | for i := 0 to fEntries.Count - 1 do |
||
1149 | begin |
||
1150 | aRec := fEntries[i]; |
||
1151 | if (exc <> '') and (Worker.FNMatch(exc, aRec.Filename)) then |
||
1152 | Continue; |
||
1153 | if SelectEntry(aRec, How) then |
||
1154 | begin |
||
1155 | // set SelectArgs |
||
1156 | aRec.SelectArgs := args; |
||
1157 | end; |
||
1158 | Inc(Result); |
||
1159 | end; |
||
1160 | end |
||
1161 | else |
||
1162 | begin |
||
1163 | // Select1 specific pattern |
||
1164 | i := -1; |
||
1165 | while True do |
||
1166 | begin |
||
1167 | aRec := FindNameEx(ptn, i, wild); |
||
1168 | if aRec = nil then |
||
1169 | break; // no matches |
||
1170 | if (exc = '') or not (Worker.FNMatch(exc, aRec.Filename)) then |
||
1171 | begin |
||
1172 | if SelectEntry(aRec, How) then |
||
1173 | begin |
||
1174 | // set SelectArgs |
||
1175 | aRec.SelectArgs := args; |
||
1176 | end; |
||
1177 | Inc(Result); |
||
1178 | end; |
||
1179 | if not wild then |
||
1180 | Break; // old find first |
||
1181 | end; |
||
1182 | end; |
||
1183 | end; |
||
1184 | |||
1185 | function TZMZipFile.SelectEntry(t: TZMIRec; How: TZipSelects): Boolean; |
||
1186 | begin |
||
1187 | Result := t.Select(How); |
||
1188 | if Result then |
||
1189 | inc(FSelCount) |
||
1190 | else |
||
1191 | dec(FSelCount); |
||
1192 | end; |
||
1193 | |||
1194 | function TZMZipFile.SelectFiles(const want, reject: TStrings; skipped: |
||
1195 | TStrings): Integer; |
||
1196 | var |
||
1197 | a: Integer; |
||
1198 | SelectsCount: Integer; |
||
1199 | exc: string; |
||
1200 | I: Integer; |
||
1201 | NoSelected: Integer; |
||
1202 | spec: String; |
||
1203 | begin |
||
1204 | Result := 0; |
||
1205 | ClearSelection; // clear all |
||
1206 | SelectsCount := want.Count; |
||
1207 | if (SelectsCount < 1) or (Count < 1) then |
||
1208 | exit; |
||
1209 | exc := ''; |
||
1210 | // combine rejects into a string |
||
1211 | if (reject <> nil) and (reject.Count > 0) then |
||
1212 | begin |
||
1213 | exc := reject[0]; |
||
1214 | for I := 1 to reject.Count - 1 do |
||
1215 | exc := exc + ZSwitchFollows + reject[I]; |
||
1216 | end; |
||
1217 | // attempt to select each wanted spec |
||
1218 | for a := 0 to SelectsCount - 1 do |
||
1219 | begin |
||
1220 | spec := want[a]; |
||
1221 | NoSelected := Select1(spec, exc, zzsSet); |
||
1222 | if NoSelected < 1 then |
||
1223 | begin |
||
1224 | // none found |
||
1225 | if Boss.Verbosity >= zvVerbose then |
||
1226 | Diag('Skipped filespec ' + spec); |
||
1227 | if assigned(skipped) then |
||
1228 | skipped.Add(spec); |
||
1229 | end; |
||
1230 | if NoSelected > 0 then |
||
1231 | Result := Result + NoSelected; |
||
1232 | if NoSelected >= Count then |
||
1233 | break; // all have been done |
||
1234 | end; |
||
1235 | end; |
||
1236 | |||
1237 | procedure TZMZipFile.SetCount(const Value: Integer); |
||
1238 | begin |
||
1239 | // not allowed |
||
1240 | end; |
||
1241 | |||
1242 | procedure TZMZipFile.SetEncoding(const Value: TZMEncodingOpts); |
||
1243 | begin |
||
1244 | if fEncoding <> Value then |
||
1245 | begin |
||
1246 | ClearCachedNames; |
||
1247 | fEncoding := Value; |
||
1248 | end; |
||
1249 | end; |
||
1250 | |||
1251 | procedure TZMZipFile.SetEncoding_CP(const Value: Cardinal); |
||
1252 | begin |
||
1253 | if fEncoding_CP <> Value then |
||
1254 | begin |
||
1255 | ClearCachedNames; |
||
1256 | fEncoding_CP := Value; |
||
1257 | end; |
||
1258 | end; |
||
1259 | |||
1260 | procedure TZMZipFile.SetItems(Idx: Integer; const Value: TZMIRec); |
||
1261 | var |
||
1262 | tmp: TObject; |
||
1263 | begin |
||
1264 | tmp := fEntries[Idx]; |
||
1265 | if tmp <> Value then |
||
1266 | begin |
||
1267 | fEntries[Idx] := Value; |
||
1268 | tmp.Free; |
||
1269 | end; |
||
1270 | end; |
||
1271 | |||
1272 | procedure TZMZipFile.SetShowAll(const Value: Boolean); |
||
1273 | begin |
||
1274 | fShowAll := Value; |
||
1275 | end; |
||
1276 | |||
1277 | procedure TZMZipFile.SetStub(const Value: TMemoryStream); |
||
1278 | begin |
||
1279 | if fStub <> Value then |
||
1280 | begin |
||
1281 | if assigned(fStub) then |
||
1282 | fStub.Free; |
||
1283 | fStub := Value; |
||
1284 | end; |
||
1285 | end; |
||
1286 | |||
1287 | function TZMZipFile.VerifyOpen: Integer; |
||
1288 | var |
||
1289 | ft: TFileTime; |
||
1290 | begin |
||
1291 | Result := DS_FileOpen; |
||
1292 | if not IsOpen and not File_Open(fmOpenRead or fmShareDenyWrite) then |
||
1293 | exit; |
||
1294 | if LastWriteTime(ft) then |
||
1295 | begin |
||
1296 | Result := 0; |
||
1297 | |||
1298 | LastWriteTime(fEOCFileTime); |
||
1299 | if CompareFileTime(EOCFileTime, ft) <> 0 then |
||
1300 | Result := -DS_FileChanged; |
||
1301 | end; |
||
1302 | end; |
||
1303 | |||
1304 | // returns bytes written or <0 _ error |
||
1305 | function TZMZipFile.WriteCentral: Integer; |
||
1306 | var |
||
1307 | i: Integer; |
||
1308 | rec: TZMIRec; |
||
1309 | wrote: Integer; |
||
1310 | begin |
||
1311 | Result := 0; |
||
1312 | wrote := 0; |
||
1313 | CentralOffset := Position; |
||
1314 | CentralDiskNo := DiskNr; |
||
1315 | TotalEntries := 0; |
||
1316 | CentralEntries := 0; |
||
1317 | CentralSize := 0; |
||
1318 | ProgReport(zacXItem, PR_CentrlDir, '', Count); |
||
1319 | for i := 0 to Count - 1 do |
||
1320 | begin |
||
1321 | rec := TZMIRec(Items[i]); |
||
1322 | if rec.StatusBit[zsbError] = 0 then |
||
1323 | begin |
||
1324 | // no processing error |
||
1325 | if Boss.Verbosity >= zvTrace then |
||
1326 | Diag('Writing central [' + IntToStr(i) + '] ' + rec.FileName); |
||
1327 | // check for deleted? |
||
1328 | Result := rec.Write; |
||
1329 | if Result < 0 then |
||
1330 | break; // error |
||
1331 | if Position <= Result then // started new part |
||
1332 | CentralEntries := 0; |
||
1333 | wrote := wrote + Result; |
||
1334 | CentralSize := CentralSize + Cardinal(Result); |
||
1335 | TotalEntries := TotalEntries + 1; |
||
1336 | CentralEntries := CentralEntries + 1; |
||
1337 | ProgReport(zacXProgress, PR_CentrlDir, '', 1); |
||
1338 | end |
||
1339 | else |
||
1340 | Diag('skipped Writing central ['+ IntToStr(i) + '] ' + rec.FileName); |
||
1341 | end; |
||
1342 | // finished Central |
||
1343 | if Result >= 0 then |
||
1344 | begin |
||
1345 | Result := WriteEOC; |
||
1346 | if Result >= 0 then |
||
1347 | begin |
||
1348 | ProgReport(zacXProgress, PR_CentrlDir, '', 1); |
||
1349 | Result := wrote + Result; |
||
1350 | if Result > 0 then |
||
1351 | begin |
||
1352 | Diag(' finished ok'); |
||
1353 | end; |
||
1354 | end; |
||
1355 | end; |
||
1356 | end; |
||
1357 | |||
1358 | constructor TZMCopyRec.Create(theOwner: TZMWorkFile); |
||
1359 | begin |
||
1360 | inherited Create(theOwner); |
||
1361 | end; |
||
1362 | |||
1363 | procedure TZMCopyRec.AfterConstruction; |
||
1364 | begin |
||
1365 | inherited; |
||
1366 | fLink := nil; |
||
1367 | end; |
||
1368 | |||
1369 | // process record, return bytes written; <0 = -error |
||
1370 | function TZMCopyRec.Process: Int64; |
||
1371 | var |
||
1372 | did: Int64; |
||
1373 | InRec: TZMIRec; |
||
1374 | InWorkFile: TZMWorkFile; |
||
1375 | stNr: Integer; |
||
1376 | stt: Int64; |
||
1377 | ToWrite: Int64; |
||
1378 | wrt: Int64; |
||
1379 | begin |
||
1380 | // ASSERT(assigned(Owner), 'no owner'); |
||
1381 | if Owner.Boss.Verbosity >= zvVerbose then |
||
1382 | Owner.Boss.ReportMsg(GE_Copying, [FileName]); |
||
1383 | InRec := Link; |
||
1384 | InWorkFile := InRec.Owner; |
||
1385 | if Owner.Boss.Verbosity >= zvVerbose then |
||
1386 | Diag('Copying local'); |
||
1387 | Result := InRec.SeekLocalData; |
||
1388 | if Result < 0 then |
||
1389 | exit; // error |
||
1390 | stNr := Owner.DiskNr; |
||
1391 | stt := Owner.Position; |
||
1392 | Result := WriteAsLocal1(ModifDateTime, CRC32); |
||
1393 | if Result < 0 then |
||
1394 | exit; // error |
||
1395 | wrt := Result; |
||
1396 | Owner.ProgReport(zacProgress, PR_Copying, '', wrt); |
||
1397 | // Diag(' finished copy local'); |
||
1398 | // ok so update positions |
||
1399 | RelOffLocal := stt; |
||
1400 | DiskStart := stNr; |
||
1401 | ToWrite := CompressedSize; |
||
1402 | // Diag('copying zipped data'); |
||
1403 | Owner.ProgReport(zacItem, zprCompressed, FileName, ToWrite); |
||
1404 | did := Owner.CopyFrom(InWorkFile, ToWrite); |
||
1405 | if did <> ToWrite then |
||
1406 | begin |
||
1407 | if did < 0 then |
||
1408 | Result := did // write error |
||
1409 | else |
||
1410 | Result := -DS_DataCopy; |
||
1411 | exit; |
||
1412 | end; |
||
1413 | wrt := wrt + did; |
||
1414 | if (Flag and 8) <> 0 then |
||
1415 | begin |
||
1416 | did := WriteDataDesc(Owner); |
||
1417 | if did < 0 then |
||
1418 | begin |
||
1419 | Result := did; // error |
||
1420 | exit; |
||
1421 | end; |
||
1422 | wrt := wrt + did; |
||
1423 | Owner.ProgReport(zacProgress, PR_Copying, '', did); |
||
1424 | end; |
||
1425 | Result := wrt; |
||
1426 | end; |
||
1427 | |||
1428 | // return bytes to be processed |
||
1429 | function TZMCopyRec.ProcessSize: Int64; |
||
1430 | begin |
||
1431 | Result := CompressedSize + LocalSize; |
||
1432 | if (Flag and 8) <> 0 then |
||
1433 | Result := Result + sizeof(TZipDataDescriptor); |
||
1434 | end; |
||
1435 | |||
1436 | procedure TZMCopyRec.SetLink(const Value: TZMIRec); |
||
1437 | begin |
||
1438 | if fLink <> Value then |
||
1439 | begin |
||
1440 | fLink := Value; |
||
1441 | end; |
||
1442 | end; |
||
1443 | |||
1444 | constructor TZMZipCopy.Create(Wrkr: TZMCore); |
||
1445 | begin |
||
1446 | inherited Create(Wrkr); |
||
1447 | end; |
||
1448 | |||
1449 | // Add a copy of source record if name is unique |
||
1450 | function TZMZipCopy.AffixZippedFile(rec: TZMIRec): Integer; |
||
1451 | var |
||
1452 | nrec: TZMCopyRec; |
||
1453 | begin |
||
1454 | Result := -1; |
||
1455 | if HasDupName(rec) < 0 then |
||
1456 | begin |
||
1457 | // accept it |
||
1458 | nrec := TZMCopyRec.Create(self); // make a copy |
||
1459 | nrec.AssignFrom(rec); |
||
1460 | // clear unknowns ? |
||
1461 | nrec.Link := rec; // link to original |
||
1462 | Result := Add(nrec); |
||
1463 | end; |
||
1464 | end; |
||
1465 | |||
1466 | // return >=0 number added <0 error |
||
1467 | function TZMZipCopy.AffixZippedFiles(Src: TZMZipFile; All: Boolean): Integer; |
||
1468 | var |
||
1469 | i: Integer; |
||
1470 | r: Integer; |
||
1471 | rec: TZMIRec; |
||
1472 | begin |
||
1473 | Result := 0; |
||
1474 | for i := 0 to Src.Count - 1 do |
||
1475 | begin |
||
1476 | rec := Src[i]; |
||
1477 | if not assigned(rec) then |
||
1478 | continue; |
||
1479 | if All or rec.TestStatusBit(zsbSelected) then |
||
1480 | begin |
||
1481 | Diag('including: ' + rec.FileName); |
||
1482 | r := AffixZippedFile(rec); |
||
1483 | if (r >= 0) then |
||
1484 | Inc(Result) // added |
||
1485 | else |
||
1486 | begin |
||
1487 | // error |
||
1488 | if r < 0 then |
||
1489 | Result := r; |
||
1490 | end; |
||
1491 | end |
||
1492 | else |
||
1493 | Diag('ignoring: ' + rec.FileName); |
||
1494 | end; |
||
1495 | end; |
||
1496 | |||
1497 | |||
1498 | // copies selected files from InZip |
||
1499 | function TZMZipCopy.WriteFile(InZip: TZMZipFile; All: Boolean): Int64; |
||
1500 | begin |
||
1501 | ASSERT(assigned(InZip), 'no input'); |
||
1502 | Diag('Write file'); |
||
1503 | Result := InZip.VerifyOpen; // verify unchanged and open |
||
1504 | if Result < 0 then |
||
1505 | exit; |
||
1506 | ZipComment := InZip.ZipComment; |
||
1507 | Result := AffixZippedFiles(InZip, All); |
||
1508 | if Result >= 0 then |
||
1509 | Result := Commit(zwoZipTime in {Worker.}WriteOptions); |
||
1510 | end; |
||
1511 | |||
1512 | |||
1513 | end. |