Subversion Repositories autosfx

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 daniel-mar 1
unit ZMWrkr19;
2
 
3
(*
4
  ZMWrkr19.pas - Does most of the work
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
 
30
{$I '.\ZipVers19.inc'}
31
{$IFDEF VER180}
32
{$WARN SYMBOL_DEPRECATED OFF}
33
{$ENDIF}
34
 
35
interface
36
 
37
uses
38
  SysUtils, Windows, Classes, Graphics,
39
  ZipMstr19, ZMCompat19, ZMCore19, ZMWAUX19, ZMZipFile19;
40
 
41
//------------------------------------------------------------------------
42
 
43
type
44
  TSFXOps = (sfoNew, sfoZip, sfoExe);
45
 
46
type
47
  TZMWorker = class(TZMWAux)
48
  private
49
    function AddZippedFilesWrite(DstZip: TZMZipFile; DstCnt: Integer; SrcZip:
50
        TZMZipFile; SrcCnt: Integer): integer;
51
    function Prepare(MustExist: Boolean; SafePart: boolean = false): TZMZipFile;
52
  protected
53
    function Delete1: integer;
54
    function IsDetachedSFX(const fn: String): Boolean;
55
    //1 Rewrite via an intermediate
56
    function Remake(CurZip: TZMZipFile; ReqCnt: Integer; All: boolean): Integer;
57
    procedure ResolveMerge(Merge: TZMMergeOpts; SrcZip, DstZip: TZMZipFile; var
58
        SrcCnt, DstCnt: Integer);
59
    procedure VerifySource(SrcZip: TZMZipFile);
60
  public
61
    procedure AddZippedFiles(SrcWorker: TZMWorker; Merge: TZMMergeOpts);
62
    function AddZippedFilesAppend(DstZip, SrcZip: TZMZipFile; Last: Integer):
63
        integer;
64
    procedure AfterConstruction; override;
65
    procedure BeforeDestruction; override;
66
    function ChangeFileDetails(func: TZMChangeFunction; var data): Integer;
67
    procedure Clear; override;
68
    procedure CopyZippedFiles(DestWorker: TZMWorker; DeleteFromSource: boolean;
69
        OverwriteDest: TZMMergeOpts); overload;
70
    procedure Delete;
71
    function ForEach(func: TZMForEachFunction; var data): Integer;
72
    function IsDestWritable(const fname: String; AllowEmpty: Boolean): Boolean;
73
    procedure List;
74
    procedure Rename(RenameList: TList; NewDateTime: Integer; How: TZMRenameOpts =
75
        htrDefault);
76
    procedure Set_ZipComment(const zComment: AnsiString);
77
    procedure StartUp; override;
78
    property TotalSizeToProcess: Int64 read GetTotalSizeToProcess;
79
  end;
80
 
81
implementation
82
 
83
uses
84
  Dialogs, ZMStructs19, ZMDelZip19, ZMXcpt19, ZMUtils19, ZMDlg19, ZMCtx19,
85
  ZMMsgStr19, ZMMsg19, ZMWorkFile19, ZMDrv19, ZMMatch19, ZMIRec19, ZMEOC19;
86
 
87
const
88
  BufSize = 10240;
89
  //8192;   // Keep under 12K to avoid Winsock problems on Win95.
90
  // If chunks are too large, the Winsock stack can
91
  // lose bytes being sent or received.
92
 
93
type
94
  pRenData = ^TRenData;
95
 
96
  TRenData = record
97
    Owner: TZMCore;
98
    RenList: TList;
99
    DTime:   Integer;
100
    How:  TZMRenameOpts;
101
    cnt:     Integer;
102
  end;
103
 
104
// 'ForEach' function to rename files
105
function RenFunc(rec: TZMDirRec; var data): Integer;
106
var
107
  ChangeName: boolean;
108
  FileName: String;
109
  How:  TZMRenameOpts;
110
  i: Integer;
111
  k: Integer;
112
  ncomment: String;
113
  newname: String;
114
  newStamp: integer;
115
  pData: pRenData;
116
  pRenRec: PZMRenameRec;
117
  RenSource: TZMString;
118
begin
119
  filename := rec.FileName;
120
  pData := @data;
121
  How := pData.How;
122
  Result := 0;
123
  for i := 0 to pData^.RenList.Count - 1 do
124
  begin
125
    pRenRec := PZMRenameRec(pData^.RenList[i]);
126
    RenSource := pRenRec.Source;
127
    newname := pRenRec.Dest;
128
    ncomment := pRenRec.Comment;
129
    newStamp := pRenRec.DateTime;
130
    ChangeName := (newname <> '|') and (CompareStr(filename, newname) <> 0);
131
    if How = htrFull then
132
    begin
133
      if pData^.Owner.FNMatch(pRenRec.Source, FileName) then
134
        k := -1
135
      else
136
        k := 0;
137
    end
138
    else
139
    begin
140
      k := Pos(UpperCase(RenSource), UpperCase(FileName));
141
    end;
142
    if k <> 0 then
143
    begin
144
      inc(pData^.cnt);   // I am selected
145
      if not ChangeName then
146
        Result := 0
147
      else
148
      begin
149
        if k > 0 then
150
        begin
151
          newname := FileName;
152
          System.Delete(newname, k, Length(RenSource));
153
          Insert(pRenRec.Dest, newname, k);
154
        end;
155
        Result := rec.ChangeName(newname);
156
        if Result = 0 then
157
          filename := rec.FileName;
158
      end;
159
      if Result = 0 then
160
      begin
161
        if ncomment <> '' then
162
        begin
163
          if ncomment[1] = #0 then
164
            ncomment := '';
165
          Result := rec.ChangeComment(ncomment);
166
        end;
167
      end;
168
      if Result = 0 then
169
      begin
170
        if newStamp = 0 then
171
          newStamp := pData^.DTime;
172
        if newStamp <> 0 then
173
          Result := rec.ChangeDate(newStamp);
174
      end;
175
      if How <> htrDefault then
176
        break;
177
    end;
178
  end;
179
end;
180
 
181
(* TZMWorker.AddZippedFiles
182
  Add zipped files from source ZipMaster selected from source FSpecArgs
183
  When finished
184
    FSpecArgs will contain source files copied
185
    FSpecArgsExcl will contain source files skipped
186
*)
187
procedure TZMWorker.AddZippedFiles(SrcWorker: TZMWorker; Merge: TZMMergeOpts);
188
var
189
  BadSkip: Boolean;
190
  DstCnt: Integer;
191
  DstZip: TZMZipFile;
192
  idx: Integer;
193
  res: Integer;
194
  SrcCnt: Integer;
195
  SrcZip: TZMZipFile;
196
begin
197
  ShowProgress := zspNone;
198
  ClearErr;
199
  // Are source and destination different?
200
  SrcZip := SrcWorker.CentralDir.Current;
201
  VerifySource(SrcZip); // make sure we have some valid
202
  DstZip := Prepare(false, true);
203
  if (SrcWorker = Self) or IsSameFile(ZipFileName, SrcWorker.ZipFileName) then
204
    raise EZipMaster.CreateResDisp(CF_SourceIsDest, true);
205
 
206
  if (SrcZip.WorkDrive.DriveLetter = DstZip.WorkDrive.DriveLetter) and
207
    (not DstZip.WorkDrive.DriveIsFixed) and
208
    (DstZip.MultiDisk or SrcZip.MultiDisk or (zwoDiskSpan in WriteOptions))
209
    then
210
    raise EZipMaster.CreateResDisp(AZ_SameAsSource, true);
211
 
212
  BadSkip := false;
213
  FSpecArgs.Clear;
214
  SrcCnt := SrcZip.SelectFiles(SrcWorker.FSpecArgs, SrcWorker.FSpecArgsExcl,
215
    FSpecArgs);
216
  FSpecArgsExcl.Clear; // will contain source files not copied
217
  if SrcCnt > 0 then
218
  begin
219
    // copy the list of not found specs adding the correct error
220
    for idx := 0 to FSpecArgs.Count - 1 do
221
    begin
222
      FSpecArgsExcl.AddObject(FSpecArgs[idx], pointer(stNotFound));
223
      if ReportSkipping(FSpecArgs[idx], 0, stNotFound) then
224
        BadSkip := true;
225
    end;
226
  end;
227
  FSpecArgs.Clear; // will contain files copied from source
228
  if BadSkip then
229
    raise EZipMaster.CreateResDisp(GE_NoSkipping, true);
230
  if SrcCnt < 1 then
231
    raise EZipMaster.CreateResDisp(AZ_NothingToDo, true);
232
 
233
  DstCnt := DstZip.Select('*', zzsSet); // initial want all
234
  if DstCnt > 0 then
235
  begin
236
    //  Resolve merge conflicts
237
    //  Src files to be copied are appended to FSpecArgs
238
    //  Dst files to be copied instead of Src files appended to FSpecArgsExcl
239
    ResolveMerge(Merge, SrcZip, DstZip, SrcCnt, DstCnt);
240
  end;
241
  if SrcCnt < 1 then
242
    raise EZipMaster.CreateResDisp(AZ_NothingToDo, true);
243
  // write the results
244
  res := AddZippedFilesWrite(DstZip, DstCnt, SrcZip, SrcCnt);
245
  CentralDir.Current := nil; // must reload
246
  if res < 0 then
247
    raise EZipMaster.CreateResDisp(-res, res <> -GE_Abort);
248
  // Update the Zip Directory by calling List method
249
  // for spanned exe avoid swapping to last disk
250
  if not IsDetachedSFX(ZipFileName) then
251
    List;
252
end;
253
 
254
function TZMWorker.AddZippedFilesAppend(DstZip, SrcZip: TZMZipFile; Last:
255
    Integer): integer;
256
var
257
  r: Integer;
258
  Zip: TZMZipCopy;
259
  TruncPosn: Int64;
260
begin
261
  DstZip.File_Close;
262
  Zip := TZMZipCopy.Create(Self);
263
  try
264
    Zip.Replicate(DstZip, Last);
265
    Zip.DiskNr := 0;
266
    Zip.ShowProgress := zspFull;
267
    Result := 0;
268
    r := Zip.Count;
269
    // add copied entries
270
    r := r + Zip.AffixZippedFiles(SrcZip, false);
271
    if r > 0 then
272
    begin
273
      Result := SrcZip.Reopen(fmOpenRead);
274
      if (Result >= 0) then
275
      begin
276
        if Last >= 0 then
277
        begin
278
          // we must append
279
          Result := Zip.Reopen(fmOpenReadWrite);
280
          if Result >= 0 then
281
          begin
282
            // get truncate position
283
            if (Last + 1) >= DstZip.Count then
284
              TruncPosn := DstZip.CentralOffset  // at SOC
285
            else
286
              TruncPosn := DstZip[Last + 1].RelOffLocal; // at start of next local
287
            if Zip.Seek(TruncPosn, 0) <> TruncPosn then
288
              Result := -DS_SeekError
289
            else
290
            if not Zip.SetEndOfFile then
291
              Result := -DS_SeekError;
292
          end;
293
          if Result >= 0 then
294
          begin
295
            Diag('Append to zip');
296
            Result := Zip.CommitAppend(Last, zwoZipTime in WriteOptions);
297
          end;
298
        end
299
        else
300
        begin
301
          // new zip
302
          Diag('Write new zip');
303
          if not Zip.File_Create(Zip.FileName) then
304
            Result := -DS_FileError
305
          else
306
            Result := Zip.Commit(zwoZipTime in WriteOptions);
307
        end;
308
      end;
309
    end;
310
    SrcZip.File_Close;
311
    Zip.File_Close;
312
    if Result >= 0 then
313
    begin
314
      if Zip.Count <> r then
315
        Result := AZ_InternalError;
316
      SuccessCnt := Zip.Count; // number of remaining files
317
    end;
318
  finally
319
    FreeAndNil(Zip);
320
  end;
321
end;
322
 
323
function TZMWorker.AddZippedFilesWrite(DstZip: TZMZipFile; DstCnt: Integer;
324
    SrcZip: TZMZipFile; SrcCnt: Integer): integer;
325
var
326
  CanAppend: boolean;
327
  existed: boolean;
328
  FirstReplaced: Integer;
329
  Intermed: TZMZipCopy;
330
  LastKept: Integer;
331
  r: Integer;
332
  WillSpilt: boolean;
333
  I: Integer;
334
begin
335
  existed := (zfi_Loaded and DstZip.info) <> 0;
336
  WillSpilt := DstZip.MultiDisk or ((not existed) and (zwoDiskSpan in DstZip.WriteOptions));
337
 
338
  if (not WillSpilt) and not (existed and (AddSafe in DstZip.AddOptions)) then
339
  begin
340
    // check can append
341
    LastKept := -1;
342
    FirstReplaced := -1;
343
    for I := 0 to DstZip.Count - 1 do
344
    begin
345
      if DstZip[I].Selected then
346
      begin
347
        LastKept := I;
348
        if FirstReplaced >= 0 then
349
          Break;  // cannot append
350
      end
351
      else
352
      if FirstReplaced < 0 then
353
        FirstReplaced := I;
354
    end;
355
    CanAppend :=(FirstReplaced < 0) or (LastKept < FirstReplaced);
356
    if (Verbosity >= zvVerbose) and CanAppend then
357
      Diag('Should be able to append starting after index: '+ IntToStr(LastKept));
358
    if CanAppend then
359
    begin
360
      Result := AddZippedFilesAppend(DstZip, SrcZip, LastKept);
361
      Exit;
362
    end;
363
  end;
364
  // write to intermediate
365
  Intermed := TZMZipCopy.Create(self);
366
  try
367
    if WillSpilt then
368
      Intermed.File_CreateTemp(PRE_INTER, '')
369
    else
370
      Intermed.File_CreateTemp(PRE_INTER, DstZip.FileName); // initial temporary destination
371
    if not WillSpilt then
372
    begin
373
      if assigned(DstZip.stub) and DstZip.UseSFX then
374
      begin
375
        Intermed.AssignStub(DstZip);
376
        Intermed.UseSFX := true;
377
      end;
378
      Intermed.DiskNr := 0;
379
      Intermed.ZipComment := DstZip.ZipComment; // keep orig
380
    end;
381
    Intermed.ShowProgress := zspFull;
382
    Result := 0;
383
    r := 0;
384
    if DstCnt > 0 then
385
      r := Intermed.AffixZippedFiles(DstZip, false);
386
    r := r + Intermed.AffixZippedFiles(SrcZip, false);
387
    if r > 0 then
388
    begin
389
      Result := SrcZip.Reopen(fmOpenRead);
390
      if (Result >= 0) and (DstCnt > 0) then
391
        Result := DstZip.Reopen(fmOpenRead);
392
      if Result >= 0 then
393
        Result := Intermed.Commit(zwoZipTime in DstZip.WriteOptions);
394
    end;
395
    SrcZip.File_Close;
396
    DstZip.File_Close;
397
    Intermed.File_Close;
398
    if Result >= 0 then
399
    begin
400
      if Intermed.Count <> r then
401
        Result := -AZ_InternalError
402
      else
403
      begin
404
        SuccessCnt := Intermed.Count; // number of remaining files
405
        // all correct so Recreate source
406
        Result := Recreate(Intermed, DstZip);
407
      end;
408
    end;
409
  finally
410
    FreeAndNil(Intermed);
411
  end;
412
end;
413
 
414
procedure TZMWorker.AfterConstruction;
415
begin
416
  inherited;
417
  fIsDestructing := False;
418
end;
419
 
420
(*? TZMWorker.BeforeDestruction
421
1.73 3 July 2003 RP stop callbacks
422
*)
423
procedure TZMWorker.BeforeDestruction;
424
begin
425
  fIsDestructing := True;                   // stop callbacks
426
  inherited;
427
end;
428
 
429
(* TZMWorker.ChangeFileDetails
430
  Add zipped files from source ZipMaster selected from source FSpecArgs
431
  When finished
432
    FSpecArgs will contain source files copied
433
    FSpecArgsExcl will contain source files skipped  (data = error code)
434
*)
435
function TZMWorker.ChangeFileDetails(func: TZMChangeFunction; var data):
436
    Integer;
437
var
438
  Changes: Integer;
439
  CurZip: TZMZipFile;
440
  idx: Integer;
441
  rec: TZMIRec;
442
  SelCnt: Integer;
443
  SkipCnt: Integer;
444
  SkippedFiles: TStringList;
445
begin
446
  ClearErr;
447
  Result := 0;
448
  SuccessCnt := 0;
449
  SkippedFiles := TStringList.Create;
450
  try
451
    if Verbosity >= zvVerbose then
452
      Diag('StartUp ChangeFileDetails');
453
    CurZip := Prepare(true);  // prepare the current zip
454
    SelCnt := CurZip.SelectFiles(FSpecArgs, FSpecArgsExcl, SkippedFiles);
455
    FSpecArgs.Clear; // will contain files processed
456
    FSpecArgsExcl.Clear; // will contain source files skipped
457
    SkipCnt := SkippedFiles.Count;
458
    for idx := 0 to SkippedFiles.Count - 1 do
459
    begin
460
      FSpecArgsExcl.AddObject(SkippedFiles[idx], pointer(stNotFound));
461
      if ReportSkipping(SkippedFiles[idx], 0, stNotFound) then
462
        Result := -GE_NoSkipping
463
      else
464
        Dec(SkipCnt);  // user chose to ignore
465
    end;
466
    if (Result = 0) and ((SelCnt <= 0) or (SkipCnt <> 0)) then
467
    begin
468
      if Verbosity >= zvVerbose then
469
        Diag('nothing selected');
470
      ShowZipMessage(AZ_NothingToDo, '');
471
      Result := -AZ_NothingToDo;
472
    end;
473
  finally
474
    SkippedFiles.Free;
475
  end;
476
  // process selected files
477
  Changes := 0;
478
  idx := -1;  // from beginning
479
  try
480
    while Result = 0 do
481
    begin
482
      idx := CurZip.NextSelected(idx);
483
      if idx < 0 then
484
        break; // no more - finished
485
      rec := CurZip[idx];
486
      if Verbosity >= zvVerbose then
487
        Diag('Changing: ' + rec.FileName);
488
      Result := func(rec, data);
489
      if Result <> 0 then
490
      begin
491
        if Verbosity >= zvVerbose then
492
          Diag(Format('error [%d] for: %s',[Result, rec.FileName]));
493
 
494
        FSpecArgsExcl.AddObject(rec.FileName, pointer(Result));
495
        if ReportSkipping(rec.FileName, Result, stCannotDo) then
496
          Result := -GE_NoSkipping
497
        else
498
          Result := 0;   // ignore error
499
      end;
500
      if Result = 0 then
501
      begin
502
        FSpecArgs.Add(rec.FileName);
503
        if rec.HasChanges then
504
        begin
505
          if Verbosity >= zvVerbose then
506
            Diag('Changed: ' + rec.FileName);
507
          inc(Changes);
508
        end;
509
        CheckCancel;
510
      end;
511
    end;
512
  except
513
    on E: EZipMaster do
514
    begin
515
      Result := -E.ResId;
516
    end;
517
    on E: Exception do
518
      Result := -GE_ExceptErr;
519
  end;
520
  if (Result = 0) and (Changes > 0) then
521
  begin
522
    if Verbosity >= zvVerbose then
523
      Diag('saving changes');
524
    Remake(CurZip, -1, True);
525
    SuccessCnt := Changes;
526
    CentralDir.Current := nil;
527
    // Update the Zip Directory by calling List method
528
    // for spanned exe avoid swapping to last disk
529
    if not IsDetachedSFX(ZipFileName) then
530
      List;
531
  end;
532
  if Verbosity >= zvVerbose then
533
    Diag('finished ChangeFileDetails');
534
end;
535
 
536
(*? TZMWorker.Clear
537
 Clears lists and strings
538
*)
539
procedure TZMWorker.Clear;
540
begin
541
  Cancel := -1;
542
  SuccessCnt := 0;
543
  inherited;
544
end;
545
 
546
(*
547
  Enter FSpecArgs and FSpecArgsExcl specify files to be copied
548
  Exit FSpecArgs = files copied
549
       FSpecArgsExcl = files skipped
550
*)
551
procedure TZMWorker.CopyZippedFiles(DestWorker: TZMWorker; DeleteFromSource:
552
    boolean; OverwriteDest: TZMMergeOpts);
553
var
554
  DestName: string;
555
  DstZip: TZMZipFile;
556
  DstCnt: Integer;
557
  I: Integer;
558
  idx: Integer;
559
  SavedDone: TStringList;
560
  res: integer;
561
  Skipped: TStringList;
562
  SrcCnt: Integer;
563
  SrcZip: TZMZipFile;
564
begin
565
  ShowProgress := zspNone;
566
  ClearErr;
567
  res := 0;
568
  SrcZip := CurrentZip(True, False);
569
  // validate dest
570
  DestName := DestWorker.ZipFileName;
571
  if DestName = '' then
572
    raise EZipMaster.CreateResDisp(GE_NoZipSpecified, true);
573
  // Are source and destination different?
574
  if IsSameFile(ZipFileName, DestName) then
575
    raise EZipMaster.CreateResDisp(CF_SourceIsDest, true);
576
  DstZip := DestWorker.CentralDir.Current;
577
  if DstZip.FileName = '' then
578
  begin
579
    // creating new file
580
    DstZip.FileName := DestName;
581
    DstZip.ReqFileName := DestName;
582
  end;
583
  if (zfi_Cancelled and DstZip.info) <> 0 then
584
  begin
585
    if DstZip.AskAnotherDisk(DestName) = idCancel then
586
      raise EZipMaster.CreateResDisp(GE_Abort, false);
587
    DstZip.info := 0; // clear error
588
  end;
589
 
590
  VerifySource(SrcZip); // make sure we have some valid
591
  Skipped := TStringList.Create;
592
  try
593
    SrcCnt := SrcZip.SelectFiles(FSpecArgs, FSpecArgsExcl, Skipped);
594
    FSpecArgsExcl.Clear; // will contain source files not copied
595
    if SrcCnt > 0 then
596
    begin
597
      // copy the list of not found specs adding the correct error
598
      for idx := 0 to Skipped.Count - 1 do
599
      begin
600
        FSpecArgsExcl.AddObject(Skipped[idx], pointer(stNotFound));
601
        if ReportSkipping(FSpecArgs[idx], 0, stNotFound) then
602
          res := -GE_NoSkipping;
603
      end;
604
    end;
605
  finally
606
    Skipped.Free;
607
  end;
608
  FSpecArgs.Clear; // will contain files copied from source
609
  if (res = 0) and (SrcCnt < 1) then
610
    res := -AZ_NothingToDo;
611
  // we now know what files are selected to be merged
612
  if res = 0 then
613
  begin
614
    DstZip.Boss := Self;
615
    if res >= 0 then
616
    begin
617
      DstCnt := DstZip.Select('*', zzsSet); // initial want all
618
      if DstCnt > 0 then
619
      begin
620
        //  Resolve merge conflicts
621
        //  Src files to be copied are appended to FSpecArgs
622
        //  Dst files to be copied instead of Src files appended to FSpecArgsExcl
623
        ResolveMerge(OverwriteDest, SrcZip, DstZip, SrcCnt, DstCnt);
624
      end;
625
      // Write the resulting zip
626
      if SrcCnt < 1 then
627
        res := -AZ_NothingToDo
628
      else
629
        res :=  AddZippedFilesWrite(DstZip, DstCnt, SrcZip, SrcCnt);
630
      // did it work?
631
      if res = 0 then
632
      begin
633
        if not IsDetachedSFX(DestName) then
634
        begin
635
          // try to load the destination
636
          DstZip.FileName := DestName;
637
          res := DstZip.Open(False, False);
638
        end;
639
      end;
640
    end;
641
  end;
642
  if (res = 0) and DeleteFromSource then
643
  begin
644
    // delete the copied files
645
    Skipped := nil;
646
    SavedDone := TStringList.Create;
647
    try
648
      // save done and skipped files
649
      SavedDone.AddStrings(FSpecArgs);
650
      Skipped := TStringList.Create;
651
      for I := 0 to FSpecArgsExcl.Count - 1 do
652
        Skipped.AddObject(FSpecArgsExcl.Strings[I], FSpecArgsExcl.Objects[i]);
653
      FSpecArgsExcl.Clear;
654
      res := Delete1;  // delete from current zip
655
      FSpecArgs.Assign(SavedDone);  // restore done files
656
      for I := 0 to Skipped.Count - 1 do
657
        FSpecArgsExcl.AddObject(Skipped.Strings[I], Skipped.Objects[i]);
658
    finally
659
      SavedDone.Free;
660
      if Skipped <> nil then
661
        Skipped.Free;
662
    end;
663
    CentralDir.Current := nil; // must reload
664
    // Update the Zip Directory by calling List method
665
    // for spanned exe avoid swapping to last disk
666
    if not IsDetachedSFX(ZipFileName) then
667
      List;
668
  end;
669
  if res < 0 then
670
    raise EZipMaster.CreateResDisp(-res, res <> -GE_Abort);
671
  SuccessCnt := FSpecArgs.Count;
672
end;
673
 
674
(*? TZMWorker.Delete
675
  Deletes files specified in FSpecArgs from current Zip
676
  exit: FSpecArgs = files deleted,
677
        FSpecArgsExcl = files skipped
678
        SuccessCnt = number of files deleted
679
*)
680
procedure TZMWorker.Delete;
681
var
682
  res: integer;
683
begin
684
  ClearErr;
685
  if {(not assigned(CentralDir.Current)) or} (CentralDir.Current.Count < 1) or
686
    (FSpecArgs.Count = 0) then
687
    res := -DL_NothingToDel
688
  else
689
    res := Delete1;
690
  if res < 0 then
691
    ShowZipMessage(-res, '')
692
  else
693
    SuccessCnt := res;
694
  // Update the Zip Directory by calling List method
695
  // for spanned exe avoid swapping to last disk
696
  if (res <> -DL_NothingToDel) and not IsDetachedSFX(ZipFileName) then
697
    List;
698
end;
699
 
700
(*? TZMWorker.Delete1
701
  Deletes files specified in FSpecArgs from current Zip
702
  exit: FSpecArgs = files deleted,
703
        FSpecArgsExcl = files skipped
704
        Result = >=0 number of files deleted, <0 error
705
*)
706
function TZMWorker.Delete1: integer;
707
var
708
  BeforeCnt: Integer;
709
  CurZip: TZMZipFile;
710
  DelCnt: Integer;
711
  idx: Integer;
712
  SkippedFiles: TStringList;
713
begin
714
  CurZip := Prepare(true);  // prepare the Current zip
715
  Result := 0;
716
  SkippedFiles := TStringList.Create;
717
  try
718
    DelCnt := CurZip.SelectFiles(FSpecArgs, FSpecArgsExcl, SkippedFiles);
719
    FSpecArgs.Clear;     // will contain files deleted
720
    FSpecArgsExcl.Clear; // will contain files skipped
721
    for idx := 0 to SkippedFiles.Count - 1 do
722
    begin
723
      FSpecArgsExcl.AddObject(SkippedFiles[idx], pointer(stNotFound));
724
      if ReportSkipping(SkippedFiles[idx], 0, stNotFound) then
725
        Result := - GE_NoSkipping;
726
    end;
727
  finally
728
    SkippedFiles.Free;
729
  end;
730
  if (Result = 0) and (DelCnt <= 0) then
731
    Result := -DL_NothingToDel;
732
  if Result = 0 then
733
  begin
734
    ASSERT(DelCnt = CurZip.SelCount, 'selcount wrong 1');
735
    DelCnt := CurZip.Count - DelCnt;
736
    if DelCnt < 1 then
737
    begin
738
      // no files left
739
      CurZip.File_Close;
740
      SysUtils.DeleteFile(CurZip.FileName);
741
      Result := DelCnt; // number of files deleted
742
    end
743
    else
744
    begin
745
      idx := -1;  // from beginning
746
      while true do
747
      begin
748
        idx := CurZip.NextSelected(idx);
749
        if idx < 0 then
750
          break; // no more - finished
751
        FSpecArgs.Add(CurZip[idx].FileName);
752
      end;
753
      BeforeCnt := CurZip.Count;
754
      CurZip.Select('*', zzsToggle); // select entries to keep
755
      ASSERT(DelCnt = CurZip.SelCount, 'selcount wrong 2');
756
      // write the result
757
      Result := Remake(CurZip, DelCnt, False);
758
      if Result >= 0 then
759
        Result := BeforeCnt - Result;   // if no error
760
    end;
761
  end;
762
  CurZip.Invalidate;
763
  CentralDir.Current := nil;   // force reload
764
end;
765
 
766
function TZMWorker.ForEach(func: TZMForEachFunction; var data): Integer;
767
var
768
  BadSkip: Boolean;
769
  CurZip: TZMZipFile;
770
  good: Integer;
771
  i: Integer;
772
  idx: Integer;
773
  rec: TZMDirEntry;
774
  SelCnt: Integer;
775
  SkippedFiles: TStringList;
776
begin
777
  ClearErr;
778
  Result := 0;
779
  SuccessCnt := 0;
780
  good := 0;
781
  SkippedFiles := TStringList.Create;
782
  try
783
    if Verbosity >= zvVerbose then
784
      Diag('StartUp ForEach');
785
    CurZip := CurrentZip(True);
786
    SelCnt := CurZip.SelectFiles(FSpecArgs, FSpecArgsExcl, SkippedFiles);
787
    if SelCnt <= 0 then
788
    begin
789
      if Verbosity >= zvVerbose then
790
        Diag('nothing selected');
791
      ShowZipMessage(AZ_NothingToDo, '');
792
      Exit;
793
    end;
794
    FSpecArgs.Clear;      // will contain files processed
795
    FSpecArgsExcl.Clear;  // will contain files skipped
796
    BadSkip := False;
797
    for idx := 0 to SkippedFiles.Count - 1 do
798
    begin
799
      FSpecArgsExcl.AddObject(SkippedFiles[idx], pointer(stNotFound));
800
      if ReportSkipping(SkippedFiles[idx], 0, stNotFound) then
801
        BadSkip := True;
802
    end;
803
  finally
804
    SkippedFiles.Free;
805
  end;
806
  if BadSkip then
807
  begin
808
    ShowZipMessage(GE_NoSkipping, '');
809
    Exit;
810
  end;
811
  i := -1;
812
  while True do
813
  begin
814
    i := CurZip.NextSelected(i);
815
    if i < 0 then
816
      break;
817
    rec := CurZip[i];
818
    if Verbosity >= zvVerbose then
819
      Diag('Processing: ' + rec.FileName);
820
    Result := func(rec, data);
821
    if Result <> 0 then
822
    begin
823
      FSpecArgsExcl.Add(rec.FileName);
824
      break;
825
    end;
826
    inc(good);
827
    FSpecArgs.Add(rec.FileName);
828
    CheckCancel;
829
  end;
830
  SuccessCnt := good;
831
  if Verbosity >= zvVerbose then
832
    Diag('finished ForEach');
833
end;
834
 
835
(*? TZMWorker.IsDestWritable
836
1.79  2005 Jul 9
837
*)
838
function TZMWorker.IsDestWritable(const fname: String; AllowEmpty: Boolean):
839
    Boolean;
840
var
841
  hFile: Integer;
842
  sr: TSearchRec;
843
  wd: TZMWorkDrive;
844
  xname: String;
845
begin
846
  Result := False;
847
  wd := TZMWorkDrive.Create;
848
  try
849
    xname := ExpandUNCFileName(fname);
850
    // test if destination can be written
851
    wd.DriveStr := xname;
852
    if not wd.HasMedia(false) then
853
    begin
854
      Result := AllowEmpty and (wd.DriveType = DRIVE_REMOVABLE);
855
      // assume can put in writable disk
856
      exit;
857
    end;
858
    if WinXP or (wd.DriveType <> DRIVE_CDROM) then
859
    begin
860
      if sysUtils.FindFirst(xname, faAnyFile, sr) = 0 then
861
      begin
862
        Result := (sr.Attr and faReadOnly) = 0;
863
        sysUtils.FindClose(sr);
864
        if Result then
865
        begin
866
          // exists and is not read-only - test locked
867
          hFile := SysUtils.FileOpen(xname, fmOpenWrite);
868
          Result := hFile > -1;
869
          if Result then
870
            SysUtils.FileClose(hFile);
871
        end;
872
        exit;
873
      end;
874
      // file did not exist - try to create it
875
      hFile := FileCreate(xname);
876
      if hFile > -1 then
877
      begin
878
        Result := True;
879
        FileClose(hFile);
880
        SysUtils.DeleteFile(xname);
881
      end;
882
    end;
883
  finally
884
    wd.Free;
885
  end;
886
end;
887
 
888
function TZMWorker.IsDetachedSFX(const fn: String): Boolean;
889
var
890
  ext: String;
891
  wz: TZMZipFile;
892
begin
893
  Result := False;
894
  ext := ExtractFileExt(fn);
895
  if AnsiSameText(ext, '.exe') then
896
  begin
897
    wz := TZMZipFile.Create(self);
898
    try
899
      wz.FileName := fn;
900
      if (wz.OpenEOC(true) >= 0) and IsDetachSFX(wz) then
901
        Result := true;
902
    finally
903
      wz.Free;
904
    end;
905
  end;
906
end;
907
 
908
procedure TZMWorker.List;
909
begin
910
  LoadZip(ZipFileName, false);
911
end;
912
 
913
(* TZMWorker.Prepare
914
  Prepare destination and get SFX stub as needed
915
*)
916
function TZMWorker.Prepare(MustExist: Boolean; SafePart: boolean = false):
917
    TZMZipFile;
918
begin
919
  Result := CurrentZip(MustExist, SafePart);
920
  if Unattended and not Result.WorkDrive.DriveIsFixed then
921
    raise EZipMaster.CreateResDisp(DS_NoUnattSpan, true);
922
  if (Uppercase(ExtractFileExt(Result.ReqFileName)) = EXT_EXE) then
923
  begin
924
    Result.UseSFX := true;
925
    Result.Stub := NewSFXStub;
926
    Result.UseSFX := true;
927
  end;
928
end;
929
 
930
// write to intermediate then recreate as original
931
function TZMWorker.Remake(CurZip: TZMZipFile; ReqCnt: Integer; All: boolean):
932
    Integer;
933
var
934
  Intermed: TZMZipCopy;
935
  Res: Integer;
936
begin
937
  Result := 0;
938
  Intermed := TZMZipCopy.Create(self);
939
  try
940
    if not Intermed.File_CreateTemp(PRE_INTER, '') then
941
      raise EZipMaster.CreateResDisp(DS_NoOutFile, True);
942
    Intermed.ShowProgress := zspFull;
943
    Intermed.ZipComment := CurZip.ZipComment;
944
    CurZip.Reopen(fmOpenRead);
945
    Res := Intermed.WriteFile(CurZip, All);
946
    CurZip.File_Close;
947
    Intermed.File_Close;
948
    if Res < 0 then
949
      raise EZipMaster.CreateResDisp(-Res, true);
950
    Result := Intermed.Count; // number of remaining files
951
    if (ReqCnt >= 0) and (Result <> ReqCnt) then
952
      raise EZipMaster.CreateResDisp(AZ_InternalError, true);
953
    // Recreate like orig
954
    Res := Recreate(Intermed, CurZip);
955
    if Res < 0 then
956
      raise EZipMaster.CreateResDisp(-Res, true);
957
  finally
958
    Intermed.Free; // also delete temp file
959
  end;
960
end;
961
 
962
(*? TZMWorker.Rename
963
 Function to read a Zip archive and change one or more file specifications.
964
 Source and Destination should be of the same type. (path or file)
965
 If NewDateTime is 0 then no change is made in the date/time fields.
966
*)
967
procedure TZMWorker.Rename(RenameList: TList; NewDateTime: Integer; How:
968
    TZMRenameOpts = htrDefault);
969
var
970
  i: Integer;
971
  RenDat: TRenData;
972
  RenRec: PZMRenameRec;
973
  res: Integer;
974
begin
975
  for i := 0 to RenameList.Count - 1 do
976
  begin
977
    RenRec := RenameList.Items[i];
978
    if IsWild(RenRec.Source) then
979
       raise EZipMaster.CreateResDisp(AD_InvalidName, true);
980
    RenRec^.Source := SetSlash(RenRec^.Source, psdExternal);
981
    RenRec^.Dest := SetSlash(RenRec^.Dest, psdExternal);
982
  end;
983
  RenDat.Owner := Self;
984
  RenDat.RenList := RenameList;
985
  RenDat.DTime := NewDateTime;
986
  RenDat.How := How;
987
  RenDat.cnt := 0;
988
  if FSpecArgs.Count < 1 then
989
    FSpecArgs.Add('*.*');
990
  res := ChangeFileDetails(@RenFunc, RenDat);
991
  if res < 0 then
992
    raise EZipMaster.CreateResDisp(-res, true);
993
  SuccessCnt := RenDat.cnt;
994
end;
995
 
996
(*
997
  Resolve merge conflicts
998
  Src files to be copied are appended to FSpecArgs
999
  Dst files to be copied instead of Src files appended to FSpecArgsExcl
1000
*)
1001
procedure TZMWorker.ResolveMerge(Merge: TZMMergeOpts; SrcZip, DstZip:
1002
    TZMZipFile; var SrcCnt, DstCnt: Integer);
1003
var
1004
  DstRec: TZMIRec;
1005
  i: Integer;
1006
  idx: Integer;
1007
  k: Cardinal;
1008
  SrcRec: TZMIRec;
1009
  tmpCopyZippedOverwrite: TZMCopyZippedOverwriteEvent;
1010
  WantSrc: Boolean;
1011
begin
1012
  i := -1; // from beginning
1013
  k := 0;
1014
  while true do
1015
  begin
1016
    i := SrcZip.NextSelected(i);
1017
    if i < 0 then
1018
      break;
1019
    Inc(k);
1020
    if (k and 127) = 0 then
1021
      CheckCancel;
1022
    SrcRec := SrcZip[i];
1023
    // check conflicts
1024
    idx := -1;
1025
    DstRec := nil; // keep compiler happy
1026
    if DstCnt > 0 then
1027
      DstRec := DstZip.FindName(SrcRec.FileName, idx);
1028
    if idx < 0 then
1029
    begin
1030
      FSpecArgs.Add(SrcRec.FileName); // ext name
1031
      continue;
1032
    end;
1033
    if Verbosity >= zvVerbose then
1034
      Diag('file conflict: ' + SrcRec.FileName);
1035
    // file exists in both
1036
    WantSrc := false;
1037
    case Merge of
1038
      zmoConfirm:
1039
        begin
1040
          // Do we have a event assigned for this then don't ask.
1041
          tmpCopyZippedOverwrite := Master.OnCopyZippedOverwrite;
1042
          if Assigned(tmpCopyZippedOverwrite) then
1043
            tmpCopyZippedOverwrite(Master, SrcRec, DstRec, WantSrc)
1044
          else if ZipMessageDlgEx('', Format(ZipLoadStr(CF_OverwriteYN),
1045
              [SrcZip.FileName, DstZip.FileName]),
1046
            zmtConfirmation + DHC_CpyZipOvr, [mbYes, mbNo]) = idYes then
1047
            WantSrc := true;
1048
        end;
1049
      zmoAlways:
1050
        WantSrc := true;
1051
      zmoNewer:
1052
        WantSrc := SrcRec.ModifDateTime > DstRec.ModifDateTime;
1053
      zmoOlder:
1054
        WantSrc := SrcRec.ModifDateTime < DstRec.ModifDateTime;
1055
      zmoNever:
1056
        WantSrc := false;
1057
    end;
1058
    if WantSrc then
1059
    begin
1060
      if Verbosity >= zvVerbose then
1061
        Diag('to copy source');
1062
      DstRec.ClearStatusBit(zsbSelected);
1063
      dec(DstCnt);
1064
      FSpecArgs.Add(SrcRec.FileName);
1065
    end
1066
    else
1067
    begin
1068
      if Verbosity >= zvVerbose then
1069
        Diag('to copy destination');
1070
      SrcRec.ClearStatusBit(zsbSelected);
1071
      dec(SrcCnt);
1072
      FSpecArgsExcl.Add(SrcRec.FileName);
1073
    end;
1074
  end;
1075
end;
1076
 
1077
procedure TZMWorker.Set_ZipComment(const zComment: AnsiString);
1078
var
1079
  EOC: TZipEndOfCentral;
1080
  len: Integer;
1081
  wz: TZMZipFile;
1082
  zcom: AnsiString;
1083
begin
1084
  wz := TZMZipFile.Create(self);
1085
  try
1086
    try
1087
      if Length(ZipFileName) <> 0 then
1088
      begin
1089
        wz.SpanOptions := wz.SpanOptions - [spExactName];
1090
        wz.FileName := ZipFileName;
1091
        wz.Open(true, true);// ignore errors
1092
      end
1093
      else
1094
        raise EZipMaster.CreateResDisp(GE_NoZipSpecified, true);
1095
      ZipComment := zComment;
1096
      // opened by OpenEOC() only for Read
1097
      if wz.IsOpen then     // file exists
1098
      begin
1099
        wz.File_Close;
1100
        if wz.ZipComment <> zComment then
1101
        begin     // change it
1102
          // must reopen for read/write
1103
          zcom := zComment;
1104
          len := Length(zCom);
1105
          wz.File_Open(fmShareDenyWrite or fmOpenReadWrite);
1106
          if not wz.IsOpen then
1107
            raise EZipMaster.CreateResDisp(DS_FileOpen, True);
1108
          if wz.MultiDisk and (wz.StampDate = 0) then
1109
            wz.StampDate := wz.LastWritten;  // keep date of set
1110
          wz.CheckSeek(wz.EOCOffset, 0, DS_FailedSeek);
1111
          wz.CheckRead(EOC, SizeOf(EOC), DS_EOCBadRead);
1112
          if (EOC.HeaderSig <> EndCentralDirSig) then
1113
            raise EZipMaster.CreateResDisp(DS_EOCBadRead, True);
1114
          EOC.ZipCommentLen := len;
1115
          wz.CheckSeek(-Sizeof(EOC), 1, DS_FailedSeek);
1116
          wz.CheckWrite(EOC, sizeof(EOC), DS_EOCBadWrite);
1117
          if len > 0 then
1118
            wz.CheckWrite(zCom[1], len, DS_EOCBadWrite);
1119
          // if SetEOF fails we get garbage at the end of the file, not nice but
1120
          // also not important.
1121
          wz.SetEndOfFile;
1122
        end;
1123
      end;
1124
    except
1125
      on ews: EZipMaster do
1126
      begin
1127
        ShowExceptionError(ews);
1128
        ZipComment := '';
1129
      end;
1130
      on EOutOfMemory do
1131
      begin
1132
        ShowZipMessage(GE_NoMem, '');
1133
        ZipComment := '';
1134
      end;
1135
    end;
1136
  finally
1137
    wz.Free;
1138
  end;
1139
  // Update the Zip Directory by calling List method
1140
  // for spanned exe avoid swapping to last disk
1141
  if not IsDetachedSFX(ZipFileName) then
1142
    List
1143
end;
1144
 
1145
(*? TZMWorker.StartUp
1146
*)
1147
procedure TZMWorker.StartUp;
1148
var
1149
  CurZip: TZMZipFile;
1150
begin
1151
  SuccessCnt := 0;
1152
  CentralDir.IgnoreDirOnly := not Master.UseDirOnlyEntries;
1153
  inherited;
1154
  // update values that may have changed since CurZip was made
1155
  CurZip := CentralDir.Current;
1156
  CurZip.AddOptions := AddOptions;
1157
  CurZip.SpanOptions := SpanOptions;
1158
  CurZip.WriteOptions := WriteOptions;
1159
  CurZip.IgnoreDirOnly := IgnoreDirOnly;
1160
  CurZip.Encoding := Encoding;
1161
  CurZip.EncodeAs := EncodeAs;
1162
  CurZip.Encoding_CP := Encoding_CP;
1163
end;
1164
 
1165
procedure TZMWorker.VerifySource(SrcZip: TZMZipFile);
1166
begin
1167
  if not assigned(SrcZip) then
1168
    raise EZipMaster.CreateResDisp(AZ_NothingToDo, true);
1169
  if (SrcZip.info and zfi_Cancelled) <> 0 then
1170
    raise EZipMaster.CreateResDisp(DS_Canceled, true);
1171
  if (SrcZip.info and zfi_loaded) = 0 then
1172
    raise EZipMaster.CreateResDisp(AD_InvalidZip, true);
1173
end;
1174
 
1175
end.