|
Link Here
|
| 5 |
CmdExtract::Cmd=Cmd; |
5 |
CmdExtract::Cmd=Cmd; |
| 6 |
|
6 |
|
| 7 |
*ArcName=0; |
7 |
*ArcName=0; |
| 8 |
|
|
|
| 9 |
*DestFileName=0; |
8 |
*DestFileName=0; |
| 10 |
|
9 |
|
|
|
10 |
ArcAnalyzed=false; |
| 11 |
Analyze=new AnalyzeData; |
| 12 |
memset(Analyze,0,sizeof(*Analyze)); |
| 13 |
|
| 11 |
TotalFileCount=0; |
14 |
TotalFileCount=0; |
|
|
15 |
|
| 16 |
// Common for all archives involved. Set here instead of DoExtract() |
| 17 |
// to use in unrar.dll too. Allows to avoid LinksToDirs() calls |
| 18 |
// and save CPU time in no symlinks including ".." in target were extracted. |
| 19 |
#if defined(_WIN_ALL) |
| 20 |
// We can't expand symlink path components in another symlink target |
| 21 |
// in Windows. We can't create symlinks in Android now. Even though we do not |
| 22 |
// really need LinksToDirs() calls in these systems, we still call it |
| 23 |
// for extra safety, but only if symlink with ".." in target was extracted. |
| 24 |
ConvertSymlinkPaths=false; |
| 25 |
#else |
| 26 |
// We enable it by default in Unix to care about the case when several |
| 27 |
// archives are unpacked to same directory with several independent RAR runs. |
| 28 |
// Worst case performance penalty for a lot of small files seems to be ~3%. |
| 29 |
ConvertSymlinkPaths=true; |
| 30 |
#endif |
| 31 |
|
| 12 |
Unp=new Unpack(&DataIO); |
32 |
Unp=new Unpack(&DataIO); |
| 13 |
#ifdef RAR_SMP |
33 |
#ifdef RAR_SMP |
| 14 |
Unp->SetThreads(Cmd->Threads); |
34 |
Unp->SetThreads(Cmd->Threads); |
|
Link Here
|
| 18 |
|
38 |
|
| 19 |
CmdExtract::~CmdExtract() |
39 |
CmdExtract::~CmdExtract() |
| 20 |
{ |
40 |
{ |
|
|
41 |
FreeAnalyzeData(); |
| 21 |
delete Unp; |
42 |
delete Unp; |
|
|
43 |
delete Analyze; |
| 22 |
} |
44 |
} |
| 23 |
|
45 |
|
| 24 |
|
46 |
|
|
|
47 |
void CmdExtract::FreeAnalyzeData() |
| 48 |
{ |
| 49 |
for (size_t I=0;I<RefList.Size();I++) |
| 50 |
{ |
| 51 |
// We can have undeleted temporary reference source here if extraction |
| 52 |
// was interrupted early or if user refused to overwrite prompt. |
| 53 |
if (RefList[I].TmpName!=NULL) |
| 54 |
DelFile(RefList[I].TmpName); |
| 55 |
free(RefList[I].RefName); |
| 56 |
free(RefList[I].TmpName); |
| 57 |
} |
| 58 |
RefList.Reset(); |
| 59 |
|
| 60 |
memset(Analyze,0,sizeof(*Analyze)); |
| 61 |
} |
| 62 |
|
| 63 |
|
| 25 |
void CmdExtract::DoExtract() |
64 |
void CmdExtract::DoExtract() |
| 26 |
{ |
65 |
{ |
| 27 |
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) |
66 |
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) |
|
Link Here
|
| 30 |
PasswordCancelled=false; |
69 |
PasswordCancelled=false; |
| 31 |
DataIO.SetCurrentCommand(Cmd->Command[0]); |
70 |
DataIO.SetCurrentCommand(Cmd->Command[0]); |
| 32 |
|
71 |
|
| 33 |
FindData FD; |
72 |
if (*Cmd->UseStdin==0) |
| 34 |
while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) |
73 |
{ |
| 35 |
if (FindFile::FastFind(ArcName,&FD)) |
74 |
FindData FD; |
| 36 |
DataIO.TotalArcSize+=FD.Size; |
75 |
while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) |
|
|
76 |
if (FindFile::FastFind(ArcName,&FD)) |
| 77 |
DataIO.TotalArcSize+=FD.Size; |
| 78 |
} |
| 37 |
|
79 |
|
| 38 |
Cmd->ArcNames.Rewind(); |
80 |
Cmd->ArcNames.Rewind(); |
| 39 |
while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) |
81 |
while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) |
|
Link Here
|
| 97 |
AllMatchesExact=true; |
139 |
AllMatchesExact=true; |
| 98 |
AnySolidDataUnpackedWell=false; |
140 |
AnySolidDataUnpackedWell=false; |
| 99 |
|
141 |
|
|
|
142 |
ArcAnalyzed=false; |
| 143 |
|
| 100 |
StartTime.SetCurrentTime(); |
144 |
StartTime.SetCurrentTime(); |
|
|
145 |
|
| 146 |
LastCheckedSymlink.clear(); |
| 101 |
} |
147 |
} |
| 102 |
|
148 |
|
| 103 |
|
149 |
|
|
Link Here
|
| 168 |
} |
214 |
} |
| 169 |
#endif |
215 |
#endif |
| 170 |
|
216 |
|
|
|
217 |
Arc.ViewComment(); // Must be before possible EXTRACT_ARC_REPEAT. |
| 218 |
|
| 171 |
int64 VolumeSetSize=0; // Total size of volumes after the current volume. |
219 |
int64 VolumeSetSize=0; // Total size of volumes after the current volume. |
| 172 |
|
220 |
|
|
|
221 |
#ifndef SFX_MODULE |
| 222 |
if (!ArcAnalyzed && *Cmd->UseStdin==0) |
| 223 |
{ |
| 224 |
AnalyzeArchive(Arc.FileName,Arc.Volume,Arc.NewNumbering); |
| 225 |
ArcAnalyzed=true; // Avoid repeated analysis on EXTRACT_ARC_REPEAT. |
| 226 |
} |
| 227 |
#endif |
| 228 |
|
| 173 |
if (Arc.Volume) |
229 |
if (Arc.Volume) |
| 174 |
{ |
230 |
{ |
| 175 |
#ifndef SFX_MODULE |
231 |
#ifndef SFX_MODULE |
| 176 |
// Try to speed up extraction for independent solid volumes by starting |
232 |
// Try to speed up extraction for independent solid volumes by starting |
| 177 |
// extraction from non-first volume if we can. |
233 |
// extraction from non-first volume if we can. |
| 178 |
if (!UseExactVolName && Arc.Solid && DetectStartVolume(Arc.FileName,Arc.NewNumbering)) |
234 |
if (*Analyze->StartName!=0) |
| 179 |
{ |
235 |
{ |
|
|
236 |
wcsncpyz(ArcName,Analyze->StartName,ASIZE(ArcName)); |
| 237 |
*Analyze->StartName=0; |
| 238 |
|
| 180 |
UseExactVolName=true; |
239 |
UseExactVolName=true; |
| 181 |
return EXTRACT_ARC_REPEAT; |
240 |
return EXTRACT_ARC_REPEAT; |
| 182 |
} |
241 |
} |
| 183 |
#endif |
242 |
#endif |
| 184 |
|
243 |
|
| 185 |
// Calculate the total size of all accessible volumes. |
244 |
// Calculate the total size of all accessible volumes. |
| 186 |
// This size is necessary to display the correct total progress indicator. |
245 |
// This size is necessary to display the correct total progress indicator. |
| 187 |
|
246 |
|
|
Link Here
|
| 216 |
else |
275 |
else |
| 217 |
uiStartArchiveExtract(!Cmd->Test,ArcName); |
276 |
uiStartArchiveExtract(!Cmd->Test,ArcName); |
| 218 |
|
277 |
|
| 219 |
Arc.ViewComment(); |
278 |
#ifndef SFX_MODULE |
|
|
279 |
if (Analyze->StartPos!=0) |
| 280 |
{ |
| 281 |
Arc.Seek(Analyze->StartPos,SEEK_SET); |
| 282 |
Analyze->StartPos=0; |
| 283 |
} |
| 284 |
#endif |
| 220 |
|
285 |
|
| 221 |
|
286 |
|
| 222 |
while (1) |
287 |
while (1) |
|
Link Here
|
| 272 |
return false; |
337 |
return false; |
| 273 |
|
338 |
|
| 274 |
HEADER_TYPE HeaderType=Arc.GetHeaderType(); |
339 |
HEADER_TYPE HeaderType=Arc.GetHeaderType(); |
| 275 |
if (HeaderType!=HEAD_FILE) |
340 |
if (HeaderType==HEAD_FILE) |
| 276 |
{ |
341 |
{ |
|
|
342 |
// Unlike Arc.FileName, ArcName might store an old volume name here. |
| 343 |
if (Analyze->EndPos!=0 && Analyze->EndPos==Arc.CurBlockPos && |
| 344 |
(*Analyze->EndName==0 || wcscmp(Analyze->EndName,Arc.FileName)==0)) |
| 345 |
return false; |
| 346 |
} |
| 347 |
else |
| 348 |
{ |
| 277 |
#ifndef SFX_MODULE |
349 |
#ifndef SFX_MODULE |
| 278 |
if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) |
350 |
if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed) |
| 279 |
SetExtraInfo20(Cmd,Arc,DestFileName); |
351 |
SetExtraInfo20(Cmd,Arc,DestFileName); |
|
Link Here
|
| 315 |
if (Arc.FileHead.UnpSize<0) |
387 |
if (Arc.FileHead.UnpSize<0) |
| 316 |
Arc.FileHead.UnpSize=0; |
388 |
Arc.FileHead.UnpSize=0; |
| 317 |
|
389 |
|
|
|
390 |
// 2022.03.20: We might remove this check in the future. |
| 391 |
// It duplicates Analyze->EndPos and Analyze->EndName in all cases except |
| 392 |
// volumes on removable media. |
| 318 |
if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) |
393 |
if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) |
| 319 |
return false; |
394 |
return false; |
| 320 |
|
395 |
|
|
Link Here
|
| 413 |
FirstFile=false; |
488 |
FirstFile=false; |
| 414 |
#endif |
489 |
#endif |
| 415 |
|
490 |
|
|
|
491 |
bool RefTarget=false; |
| 492 |
if (!MatchFound) |
| 493 |
for (size_t I=0;I<RefList.Size();I++) |
| 494 |
if (wcscmp(ArcFileName,RefList[I].RefName)==0) |
| 495 |
{ |
| 496 |
ExtractRef *MatchedRef=&RefList[I]; |
| 497 |
|
| 498 |
if (!Cmd->Test) // While harmless, it is useless for 't'. |
| 499 |
{ |
| 500 |
// If reference source isn't selected, but target is selected, |
| 501 |
// we unpack the source under the temporary name and then rename |
| 502 |
// or copy it to target name. We do not unpack it under the target |
| 503 |
// name immediately, because the same source can be used by multiple |
| 504 |
// targets and it is possible that first target isn't unpacked |
| 505 |
// for some reason. Also targets might have associated service blocks |
| 506 |
// like ACLs. All this would complicate processing a lot. |
| 507 |
wcsncpyz(DestFileName,*Cmd->TempPath!=0 ? Cmd->TempPath:Cmd->ExtrPath,ASIZE(DestFileName)); |
| 508 |
AddEndSlash(DestFileName,ASIZE(DestFileName)); |
| 509 |
wcsncatz(DestFileName,L"__tmp_reference_source_",ASIZE(DestFileName)); |
| 510 |
MkTemp(DestFileName,ASIZE(DestFileName)); |
| 511 |
MatchedRef->TmpName=wcsdup(DestFileName); |
| 512 |
} |
| 513 |
RefTarget=true; // Need it even for 't' to test the reference source. |
| 514 |
break; |
| 515 |
} |
| 516 |
|
| 416 |
if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted) |
517 |
if (Arc.FileHead.Encrypted && Cmd->SkipEncrypted) |
| 417 |
if (Arc.Solid) |
518 |
if (Arc.Solid) |
| 418 |
return false; // Abort the entire extraction for solid archive. |
519 |
return false; // Abort the entire extraction for solid archive. |
| 419 |
else |
520 |
else |
| 420 |
MatchFound=false; // Skip only the current file for non-solid archive. |
521 |
MatchFound=false; // Skip only the current file for non-solid archive. |
| 421 |
|
522 |
|
| 422 |
if (MatchFound || (SkipSolid=Arc.Solid)!=0) |
523 |
if (MatchFound || RefTarget || (SkipSolid=Arc.Solid)!=0) |
| 423 |
{ |
524 |
{ |
| 424 |
// First common call of uiStartFileExtract. It is done before overwrite |
525 |
// First common call of uiStartFileExtract. It is done before overwrite |
| 425 |
// prompts, so if SkipSolid state is changed below, we'll need to make |
526 |
// prompts, so if SkipSolid state is changed below, we'll need to make |
|
Link Here
|
| 427 |
if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) |
528 |
if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) |
| 428 |
return false; |
529 |
return false; |
| 429 |
|
530 |
|
| 430 |
ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); |
531 |
if (!RefTarget) |
|
|
532 |
ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); |
| 431 |
|
533 |
|
| 432 |
// DestFileName can be set empty in case of excessive -ap switch. |
534 |
// DestFileName can be set empty in case of excessive -ap switch. |
| 433 |
ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; |
535 |
ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; |
|
Link Here
|
| 464 |
return !Arc.Solid; // Can try extracting next file only in non-solid archive. |
566 |
return !Arc.Solid; // Can try extracting next file only in non-solid archive. |
| 465 |
} |
567 |
} |
| 466 |
|
568 |
|
| 467 |
while (true) // Repeat the password prompt for wrong and empty passwords. |
569 |
if (Arc.FileHead.Encrypted) |
| 468 |
{ |
570 |
{ |
| 469 |
if (Arc.FileHead.Encrypted) |
571 |
RarCheckPassword CheckPwd; |
|
|
572 |
if (Arc.Format==RARFMT50 && Arc.FileHead.UsePswCheck && !Arc.BrokenHeader) |
| 573 |
CheckPwd.Set(Arc.FileHead.Salt,Arc.FileHead.InitV,Arc.FileHead.Lg2Count,Arc.FileHead.PswCheck); |
| 574 |
|
| 575 |
while (true) // Repeat the password prompt for wrong and empty passwords. |
| 470 |
{ |
576 |
{ |
| 471 |
// Stop archive extracting if user cancelled a password prompt. |
577 |
// Stop archive extracting if user cancelled a password prompt. |
| 472 |
#ifdef RARDLL |
578 |
#ifdef RARDLL |
|
Link Here
|
| 476 |
return false; |
582 |
return false; |
| 477 |
} |
583 |
} |
| 478 |
#else |
584 |
#else |
| 479 |
if (!ExtrGetPassword(Arc,ArcFileName)) |
585 |
if (!ExtrGetPassword(Arc,ArcFileName,CheckPwd.IsSet() ? &CheckPwd:NULL)) |
| 480 |
{ |
586 |
{ |
| 481 |
PasswordCancelled=true; |
587 |
PasswordCancelled=true; |
| 482 |
return false; |
588 |
return false; |
| 483 |
} |
589 |
} |
| 484 |
#endif |
590 |
#endif |
| 485 |
} |
|
|
| 486 |
|
591 |
|
| 487 |
// Set a password before creating the file, so we can skip creating |
592 |
// Set a password before creating the file, so we can skip creating |
| 488 |
// in case of wrong password. |
593 |
// in case of wrong password. |
| 489 |
SecPassword FilePassword=Cmd->Password; |
594 |
SecPassword FilePassword=Cmd->Password; |
| 490 |
#if defined(_WIN_ALL) && !defined(SFX_MODULE) |
595 |
#if defined(_WIN_ALL) && !defined(SFX_MODULE) |
| 491 |
ConvertDosPassword(Arc,FilePassword); |
596 |
ConvertDosPassword(Arc,FilePassword); |
| 492 |
#endif |
597 |
#endif |
| 493 |
|
598 |
|
| 494 |
byte PswCheck[SIZE_PSWCHECK]; |
599 |
byte PswCheck[SIZE_PSWCHECK]; |
| 495 |
DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, |
600 |
DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, |
| 496 |
Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, |
601 |
Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, |
| 497 |
Arc.FileHead.InitV,Arc.FileHead.Lg2Count, |
602 |
Arc.FileHead.InitV,Arc.FileHead.Lg2Count, |
| 498 |
Arc.FileHead.HashKey,PswCheck); |
603 |
Arc.FileHead.HashKey,PswCheck); |
| 499 |
|
604 |
|
| 500 |
// If header is damaged, we cannot rely on password check value, |
605 |
// If header is damaged, we cannot rely on password check value, |
| 501 |
// because it can be damaged too. |
606 |
// because it can be damaged too. |
| 502 |
if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck && |
607 |
if (Arc.FileHead.UsePswCheck && !Arc.BrokenHeader && |
| 503 |
memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 && |
608 |
memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0) |
| 504 |
!Arc.BrokenHeader) |
|
|
| 505 |
{ |
| 506 |
if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop. |
| 507 |
{ |
609 |
{ |
| 508 |
// This message is used by Android GUI to reset cached passwords. |
610 |
if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop. |
| 509 |
// Update appropriate code if changed. |
611 |
{ |
| 510 |
uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName); |
612 |
// This message is used by Android GUI to reset cached passwords. |
| 511 |
} |
613 |
// Update appropriate code if changed. |
| 512 |
else // For passwords entered manually. |
614 |
uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName); |
| 513 |
{ |
615 |
} |
| 514 |
// This message is used by Android GUI and Windows GUI and SFX to |
616 |
else // For passwords entered manually. |
| 515 |
// reset cached passwords. Update appropriate code if changed. |
617 |
{ |
| 516 |
uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); |
618 |
// This message is used by Android GUI and Windows GUI and SFX to |
| 517 |
Cmd->Password.Clean(); |
619 |
// reset cached passwords. Update appropriate code if changed. |
|
|
620 |
uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName); |
| 621 |
Cmd->Password.Clean(); |
| 518 |
|
622 |
|
| 519 |
// Avoid new requests for unrar.dll to prevent the infinite loop |
623 |
// Avoid new requests for unrar.dll to prevent the infinite loop |
| 520 |
// if app always returns the same password. |
624 |
// if app always returns the same password. |
| 521 |
#ifndef RARDLL |
625 |
#ifndef RARDLL |
| 522 |
continue; // Request a password again. |
626 |
continue; // Request a password again. |
| 523 |
#endif |
627 |
#endif |
|
|
628 |
} |
| 629 |
#ifdef RARDLL |
| 630 |
// If we already have ERAR_EOPEN as result of missing volume, |
| 631 |
// we should not replace it with less precise ERAR_BAD_PASSWORD. |
| 632 |
if (Cmd->DllError!=ERAR_EOPEN) |
| 633 |
Cmd->DllError=ERAR_BAD_PASSWORD; |
| 634 |
#endif |
| 635 |
ErrHandler.SetErrorCode(RARX_BADPWD); |
| 636 |
ExtrFile=false; |
| 524 |
} |
637 |
} |
| 525 |
#ifdef RARDLL |
638 |
break; |
| 526 |
// If we already have ERAR_EOPEN as result of missing volume, |
|
|
| 527 |
// we should not replace it with less precise ERAR_BAD_PASSWORD. |
| 528 |
if (Cmd->DllError!=ERAR_EOPEN) |
| 529 |
Cmd->DllError=ERAR_BAD_PASSWORD; |
| 530 |
#endif |
| 531 |
ErrHandler.SetErrorCode(RARX_BADPWD); |
| 532 |
ExtrFile=false; |
| 533 |
} |
639 |
} |
| 534 |
break; |
|
|
| 535 |
} |
640 |
} |
|
|
641 |
else |
| 642 |
DataIO.SetEncryption(false,CRYPT_NONE,NULL,NULL,NULL,0,NULL,NULL); |
| 536 |
|
643 |
|
| 537 |
#ifdef RARDLL |
644 |
#ifdef RARDLL |
| 538 |
if (*Cmd->DllDestName!=0) |
645 |
if (*Cmd->DllDestName!=0) |
| 539 |
wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); |
646 |
wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); |
| 540 |
#endif |
647 |
#endif |
| 541 |
|
648 |
|
|
|
649 |
if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks && |
| 650 |
ConvertSymlinkPaths) |
| 651 |
ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); |
| 652 |
|
| 542 |
File CurFile; |
653 |
File CurFile; |
| 543 |
|
654 |
|
| 544 |
bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; |
655 |
bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; |
| 545 |
if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY) |
656 |
if (LinkEntry && (Arc.FileHead.RedirType!=FSREDIR_FILECOPY)) |
| 546 |
{ |
657 |
{ |
| 547 |
if (ExtrFile && Command!='P' && !Cmd->Test) |
658 |
if (ExtrFile && Command!='P' && !Cmd->Test) |
| 548 |
{ |
659 |
{ |
| 549 |
// Overwrite prompt for symbolic and hard links. |
660 |
// Overwrite prompt for symbolic and hard links and when we move |
|
|
661 |
// a temporary file to the file reference instead of copying it. |
| 550 |
bool UserReject=false; |
662 |
bool UserReject=false; |
| 551 |
if (FileExist(DestFileName) && !UserReject) |
663 |
if (FileExist(DestFileName) && !UserReject) |
| 552 |
FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); |
664 |
FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); |
|
Link Here
|
| 667 |
if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) |
779 |
if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) |
| 668 |
{ |
780 |
{ |
| 669 |
wchar RedirName[NM]; |
781 |
wchar RedirName[NM]; |
| 670 |
ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName)); |
782 |
|
|
|
783 |
// 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with |
| 784 |
// Unix RAR. WinRAR 5.0 used \ path separators here, when beginning |
| 785 |
// from 5.10 even Windows version uses / internally and converts |
| 786 |
// them to \ when reading FHEXTRA_REDIR. |
| 787 |
// We must perform this conversion before ConvertPath call, |
| 788 |
// so paths mixing different slashes like \dir1/dir2\file are |
| 789 |
// processed correctly. |
| 790 |
SlashToNative(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName)); |
| 671 |
|
791 |
|
|
|
792 |
ConvertPath(RedirName,RedirName,ASIZE(RedirName)); |
| 793 |
|
| 672 |
wchar NameExisting[NM]; |
794 |
wchar NameExisting[NM]; |
| 673 |
ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); |
795 |
ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting)); |
| 674 |
if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. |
796 |
if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. |
| 675 |
if (Type==FSREDIR_HARDLINK) |
797 |
if (Type==FSREDIR_HARDLINK) |
| 676 |
LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); |
798 |
LinkSuccess=ExtractHardlink(Cmd,DestFileName,NameExisting,ASIZE(NameExisting)); |
| 677 |
else |
799 |
else |
| 678 |
LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); |
800 |
LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,RedirName,DestFileName,NameExisting,ASIZE(NameExisting),Arc.FileHead.UnpSize); |
| 679 |
} |
801 |
} |
| 680 |
else |
802 |
else |
| 681 |
if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) |
803 |
if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) |
| 682 |
{ |
804 |
{ |
| 683 |
if (FileCreateMode) |
805 |
if (FileCreateMode) |
| 684 |
LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName); |
806 |
{ |
|
|
807 |
bool UpLink; |
| 808 |
LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink); |
| 809 |
ConvertSymlinkPaths|=LinkSuccess && UpLink; |
| 810 |
|
| 811 |
// We do not actually need to reset the cache here if we cache |
| 812 |
// only the single last checked path, because at this point |
| 813 |
// it will always contain the link own path and link can't |
| 814 |
// overwrite its parent folder. But if we ever decide to cache |
| 815 |
// several already checked paths, we'll need to reset them here. |
| 816 |
// Otherwise if no files were created in one of such paths, |
| 817 |
// let's say because of file create error, it might be possible |
| 818 |
// to overwrite the path with link and avoid checks. We keep this |
| 819 |
// code here as a reminder in case of possible modifications. |
| 820 |
LastCheckedSymlink.clear(); // Reset cache for safety reason. |
| 821 |
} |
| 685 |
} |
822 |
} |
| 686 |
else |
823 |
else |
| 687 |
{ |
824 |
{ |
| 688 |
uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,DestFileName); |
825 |
uiMsg(UIERROR_UNKNOWNEXTRA,Arc.FileName,ArcFileName); |
| 689 |
LinkSuccess=false; |
826 |
LinkSuccess=false; |
| 690 |
} |
827 |
} |
| 691 |
|
828 |
|
|
Link Here
|
| 709 |
Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); |
846 |
Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); |
| 710 |
Unp->SetDestSize(Arc.FileHead.UnpSize); |
847 |
Unp->SetDestSize(Arc.FileHead.UnpSize); |
| 711 |
#ifndef SFX_MODULE |
848 |
#ifndef SFX_MODULE |
|
|
849 |
// RAR 1.3 - 1.5 archives do not set per file solid flag. |
| 712 |
if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) |
850 |
if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) |
| 713 |
Unp->DoUnpack(15,FileCount>1 && Arc.Solid); |
851 |
Unp->DoUnpack(15,FileCount>1 && Arc.Solid); |
| 714 |
else |
852 |
else |
|
Link Here
|
| 866 |
} |
1004 |
} |
| 867 |
|
1005 |
|
| 868 |
|
1006 |
|
| 869 |
bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) |
1007 |
bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize) |
| 870 |
{ |
1008 |
{ |
| 871 |
SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. |
|
|
| 872 |
|
| 873 |
File Existing; |
1009 |
File Existing; |
| 874 |
if (!Existing.WOpen(NameExisting)) |
1010 |
if (!Existing.Open(NameExisting)) |
| 875 |
{ |
1011 |
{ |
| 876 |
uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); |
1012 |
bool OpenFailed=true; |
| 877 |
uiMsg(UIERROR_FILECOPYHINT,ArcName); |
1013 |
// If we couldn't find the existing file, check if match is present |
|
|
1014 |
// in temporary reference sources list. |
| 1015 |
for (size_t I=0;I<RefList.Size();I++) |
| 1016 |
if (wcscmp(RedirName,RefList[I].RefName)==0 && RefList[I].TmpName!=NULL) |
| 1017 |
{ |
| 1018 |
// If only one reference left targeting to this temporary file, |
| 1019 |
// it is faster to move the file instead of copying and deleting it. |
| 1020 |
bool RefMove=RefList[I].RefCount-- == 1; |
| 1021 |
NameExisting=RefList[I].TmpName; |
| 1022 |
if (RefMove) // Only one reference left for this temporary file. |
| 1023 |
{ |
| 1024 |
New.Delete(); // Delete the previously opened destination file. |
| 1025 |
// Try moving the file first. |
| 1026 |
bool MoveFailed=!RenameFile(NameExisting,NameNew); |
| 1027 |
if (MoveFailed) |
| 1028 |
{ |
| 1029 |
// If move failed, re-create the destination and try coping. |
| 1030 |
if (!New.WCreate(NameNew,FMF_WRITE|FMF_SHAREREAD)) |
| 1031 |
return false; |
| 1032 |
RefMove=false; // Try copying below. |
| 1033 |
} |
| 1034 |
else |
| 1035 |
{ |
| 1036 |
// If moved successfully, reopen the destination file and seek to |
| 1037 |
// end for SetOpenFileTime() and possible Truncate() calls later. |
| 1038 |
if (New.Open(NameNew)) |
| 1039 |
New.Seek(0,SEEK_END); |
| 1040 |
// We already moved the file, so clean the name to not try |
| 1041 |
// deleting non-existent temporary file later. |
| 1042 |
free(RefList[I].TmpName); |
| 1043 |
RefList[I].TmpName=NULL; |
| 1044 |
return true; |
| 1045 |
} |
| 1046 |
} |
| 1047 |
if (!RefMove) |
| 1048 |
OpenFailed=!Existing.Open(NameExisting); |
| 1049 |
break; |
| 1050 |
} |
| 1051 |
|
| 1052 |
if (OpenFailed) |
| 1053 |
{ |
| 1054 |
ErrHandler.OpenErrorMsg(NameExisting); |
| 1055 |
uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); |
| 1056 |
uiMsg(UIERROR_FILECOPYHINT,ArcName); |
| 878 |
#ifdef RARDLL |
1057 |
#ifdef RARDLL |
| 879 |
Cmd->DllError=ERAR_EREFERENCE; |
1058 |
Cmd->DllError=ERAR_EREFERENCE; |
| 880 |
#endif |
1059 |
#endif |
| 881 |
return false; |
1060 |
return false; |
|
|
1061 |
} |
| 882 |
} |
1062 |
} |
| 883 |
|
1063 |
|
| 884 |
Array<char> Buffer(0x100000); |
1064 |
Array<byte> Buffer(0x100000); |
| 885 |
int64 CopySize=0; |
1065 |
int64 CopySize=0; |
| 886 |
|
1066 |
|
| 887 |
while (true) |
1067 |
while (true) |
|
Link Here
|
| 890 |
int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); |
1070 |
int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); |
| 891 |
if (ReadSize==0) |
1071 |
if (ReadSize==0) |
| 892 |
break; |
1072 |
break; |
|
|
1073 |
// Update only the current file progress in WinRAR, set the total to 0 |
| 1074 |
// to keep it as is. It looks better for WinRAR. |
| 1075 |
uiExtractProgress(CopySize,UnpSize,0,0); |
| 1076 |
|
| 893 |
New.Write(&Buffer[0],ReadSize); |
1077 |
New.Write(&Buffer[0],ReadSize); |
| 894 |
CopySize+=ReadSize; |
1078 |
CopySize+=ReadSize; |
| 895 |
} |
1079 |
} |
|
Link Here
|
| 900 |
|
1084 |
|
| 901 |
void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) |
1085 |
void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) |
| 902 |
{ |
1086 |
{ |
|
|
1087 |
if (Cmd->Test) |
| 1088 |
{ |
| 1089 |
// Destination name conversion isn't needed for simple archive test. |
| 1090 |
// This check also allows to avoid issuing "Attempting to correct... |
| 1091 |
// Renaming..." messages in MakeNameCompatible() below for problematic |
| 1092 |
// names like aux.txt when testing an archive. |
| 1093 |
wcsncpyz(DestName,ArcFileName,DestSize); |
| 1094 |
return; |
| 1095 |
} |
| 1096 |
|
| 903 |
wcsncpyz(DestName,Cmd->ExtrPath,DestSize); |
1097 |
wcsncpyz(DestName,Cmd->ExtrPath,DestSize); |
| 904 |
|
1098 |
|
| 905 |
if (*Cmd->ExtrPath!=0) |
1099 |
if (*Cmd->ExtrPath!=0) |
|
Link Here
|
| 1033 |
|
1227 |
|
| 1034 |
|
1228 |
|
| 1035 |
#ifndef RARDLL |
1229 |
#ifndef RARDLL |
| 1036 |
bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) |
1230 |
bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName,RarCheckPassword *CheckPwd) |
| 1037 |
{ |
1231 |
{ |
| 1038 |
if (!Cmd->Password.IsSet()) |
1232 |
if (!Cmd->Password.IsSet()) |
| 1039 |
{ |
1233 |
{ |
| 1040 |
if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/) |
1234 |
if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)/* || !Cmd->Password.IsSet()*/) |
| 1041 |
{ |
1235 |
{ |
| 1042 |
// Suppress "test is ok" message if user cancelled the password prompt. |
1236 |
// Suppress "test is ok" message if user cancelled the password prompt. |
| 1043 |
uiMsg(UIERROR_INCERRCOUNT); |
1237 |
uiMsg(UIERROR_INCERRCOUNT); |
|
Link Here
|
| 1055 |
case -1: |
1249 |
case -1: |
| 1056 |
ErrHandler.Exit(RARX_USERBREAK); |
1250 |
ErrHandler.Exit(RARX_USERBREAK); |
| 1057 |
case 2: |
1251 |
case 2: |
| 1058 |
if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)) |
1252 |
if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password,CheckPwd)) |
| 1059 |
return false; |
1253 |
return false; |
| 1060 |
break; |
1254 |
break; |
| 1061 |
case 3: |
1255 |
case 3: |
|
Link Here
|
| 1131 |
DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); |
1325 |
DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName)); |
| 1132 |
if (!DirExist) |
1326 |
if (!DirExist) |
| 1133 |
{ |
1327 |
{ |
|
|
1328 |
if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) |
| 1329 |
LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); |
| 1134 |
CreatePath(DestFileName,true,Cmd->DisableNames); |
1330 |
CreatePath(DestFileName,true,Cmd->DisableNames); |
| 1135 |
MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); |
1331 |
MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); |
| 1136 |
} |
1332 |
} |
|
Link Here
|
| 1212 |
|
1408 |
|
| 1213 |
MakeNameUsable(DestFileName,true); |
1409 |
MakeNameUsable(DestFileName,true); |
| 1214 |
|
1410 |
|
|
|
1411 |
if (!Cmd->AbsoluteLinks && ConvertSymlinkPaths) |
| 1412 |
LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink); |
| 1215 |
CreatePath(DestFileName,true,Cmd->DisableNames); |
1413 |
CreatePath(DestFileName,true,Cmd->DisableNames); |
| 1216 |
if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) |
1414 |
if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) |
| 1217 |
{ |
1415 |
{ |
|
Link Here
|
| 1258 |
|
1456 |
|
| 1259 |
|
1457 |
|
| 1260 |
#ifndef SFX_MODULE |
1458 |
#ifndef SFX_MODULE |
| 1261 |
// To speed up solid volumes extraction, try to find a non-first start volume, |
1459 |
// Find non-matched reference sources in solid and non-solid archives. |
| 1262 |
// which still allows to unpack all files. It is possible for independent |
1460 |
// Detect the optimal start position for semi-solid archives |
| 1263 |
// solid volumes with solid statistics reset in the beginning. |
1461 |
// and optimal start volume for independent solid volumes. |
| 1264 |
bool CmdExtract::DetectStartVolume(const wchar *VolName,bool NewNumbering) |
1462 |
// |
|
|
1463 |
// Alternatively we could collect references while extracting an archive |
| 1464 |
// and perform the second extraction pass for references only. |
| 1465 |
// But it would be slower for solid archives than scaning headers |
| 1466 |
// in first pass and extracting everything in second, as implemented now. |
| 1467 |
// |
| 1468 |
void CmdExtract::AnalyzeArchive(const wchar *ArcName,bool Volume,bool NewNumbering) |
| 1265 |
{ |
1469 |
{ |
|
|
1470 |
FreeAnalyzeData(); // If processing non-first archive in multiple archives set. |
| 1471 |
|
| 1266 |
wchar *ArgName=Cmd->FileArgs.GetString(); |
1472 |
wchar *ArgName=Cmd->FileArgs.GetString(); |
| 1267 |
Cmd->FileArgs.Rewind(); |
1473 |
Cmd->FileArgs.Rewind(); |
| 1268 |
if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0)) |
1474 |
if (ArgName!=NULL && (wcscmp(ArgName,L"*")==0 || wcscmp(ArgName,L"*.*")==0)) |
| 1269 |
return false; // No need to check further for * and *.* masks. |
1475 |
return; // No need to check further for * and *.* masks. |
| 1270 |
|
1476 |
|
| 1271 |
wchar StartName[NM]; |
|
|
| 1272 |
*StartName=0; |
| 1273 |
|
| 1274 |
// Start search from first volume if all volumes preceding current are available. |
1477 |
// Start search from first volume if all volumes preceding current are available. |
| 1275 |
wchar NextName[NM]; |
1478 |
wchar NextName[NM]; |
| 1276 |
GetFirstVolIfFullSet(VolName,NewNumbering,NextName,ASIZE(NextName)); |
1479 |
if (Volume) |
|
|
1480 |
GetFirstVolIfFullSet(ArcName,NewNumbering,NextName,ASIZE(NextName)); |
| 1481 |
else |
| 1482 |
wcsncpyz(NextName,ArcName,ASIZE(NextName)); |
| 1483 |
|
| 1484 |
bool MatchFound=false; |
| 1485 |
bool PrevMatched=false; |
| 1486 |
bool OpenNext=false; |
| 1277 |
|
1487 |
|
| 1278 |
bool Matched=false; |
1488 |
bool FirstVolume=true; |
| 1279 |
while (!Matched) |
1489 |
|
|
|
1490 |
// We shall set FirstFile once for all volumes and not for each volume. |
| 1491 |
// So we do not reuse the outdated Analyze->StartPos from previous volume |
| 1492 |
// if extracted file resides completely in the beginning of current one. |
| 1493 |
bool FirstFile=true; |
| 1494 |
|
| 1495 |
while (true) |
| 1280 |
{ |
1496 |
{ |
| 1281 |
Archive Arc(Cmd); |
1497 |
Archive Arc(Cmd); |
| 1282 |
if (!Arc.Open(NextName) || !Arc.IsArchive(false) || !Arc.Volume) |
1498 |
if (!Arc.Open(NextName) || !Arc.IsArchive(false)) |
|
|
1499 |
{ |
| 1500 |
if (OpenNext) |
| 1501 |
{ |
| 1502 |
// If we couldn't open trailing volumes, we can't set early exit |
| 1503 |
// parameters. It is possible that some volume are on removable media |
| 1504 |
// and will be provided by user when extracting. |
| 1505 |
*Analyze->EndName=0; |
| 1506 |
Analyze->EndPos=0; |
| 1507 |
} |
| 1283 |
break; |
1508 |
break; |
|
|
1509 |
} |
| 1284 |
|
1510 |
|
| 1285 |
bool OpenNext=false; |
1511 |
OpenNext=false; |
| 1286 |
while (Arc.ReadHeader()>0) |
1512 |
while (Arc.ReadHeader()>0) |
| 1287 |
{ |
1513 |
{ |
| 1288 |
Wait(); |
1514 |
Wait(); |
|
Link Here
|
| 1295 |
} |
1521 |
} |
| 1296 |
if (HeaderType==HEAD_FILE) |
1522 |
if (HeaderType==HEAD_FILE) |
| 1297 |
{ |
1523 |
{ |
|
|
1524 |
if ((Arc.Format==RARFMT14 || Arc.Format==RARFMT15) && Arc.FileHead.UnpVer<=15) |
| 1525 |
{ |
| 1526 |
// RAR versions earlier than 2.0 do not set per file solid flag. |
| 1527 |
// They have only the global archive solid flag, so we can't |
| 1528 |
// reliably analyze them here. |
| 1529 |
OpenNext=false; |
| 1530 |
break; |
| 1531 |
} |
| 1532 |
|
| 1298 |
if (!Arc.FileHead.SplitBefore) |
1533 |
if (!Arc.FileHead.SplitBefore) |
| 1299 |
{ |
1534 |
{ |
| 1300 |
if (!Arc.FileHead.Solid) // Can start extraction from here. |
1535 |
if (!MatchFound && !Arc.FileHead.Solid) // Can start extraction from here. |
| 1301 |
wcsncpyz(StartName,NextName,ASIZE(StartName)); |
1536 |
{ |
|
|
1537 |
// We would gain nothing and unnecessarily complicate extraction |
| 1538 |
// if we set StartName for first volume or StartPos for first |
| 1539 |
// archived file. |
| 1540 |
if (!FirstVolume) |
| 1541 |
wcsncpyz(Analyze->StartName,NextName,ASIZE(Analyze->StartName)); |
| 1302 |
|
1542 |
|
|
|
1543 |
// We shall set FirstFile once for all volumes for this code |
| 1544 |
// to work properly. Alternatively we could append |
| 1545 |
// "|| Analyze->StartPos!=0" to the condition, so we do not reuse |
| 1546 |
// the outdated Analyze->StartPos value from previous volume. |
| 1547 |
if (!FirstFile) |
| 1548 |
Analyze->StartPos=Arc.CurBlockPos; |
| 1549 |
} |
| 1550 |
|
| 1303 |
if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0) |
1551 |
if (Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0) |
| 1304 |
{ |
1552 |
{ |
| 1305 |
Matched=true; // First matched file found, must stop further scan. |
1553 |
MatchFound = true; |
| 1306 |
break; |
1554 |
PrevMatched = true; |
|
|
1555 |
|
| 1556 |
// Reset the previously set early exit position, if any, because |
| 1557 |
// we found a new matched file. |
| 1558 |
Analyze->EndPos=0; |
| 1559 |
|
| 1560 |
// Matched file reference pointing at maybe non-matched source file. |
| 1561 |
// Even though we know RedirName, we can't check if source file |
| 1562 |
// is certainly non-matched, because it can be filtered out by |
| 1563 |
// date or attributes, which we do not know here. |
| 1564 |
if (Arc.FileHead.RedirType==FSREDIR_FILECOPY) |
| 1565 |
{ |
| 1566 |
bool AlreadyAdded=false; |
| 1567 |
for (size_t I=0;I<RefList.Size();I++) |
| 1568 |
if (wcscmp(Arc.FileHead.RedirName,RefList[I].RefName)==0) |
| 1569 |
{ |
| 1570 |
// Increment the reference count if we added such reference |
| 1571 |
// source earlier. |
| 1572 |
RefList[I].RefCount++; |
| 1573 |
AlreadyAdded=true; |
| 1574 |
break; |
| 1575 |
} |
| 1576 |
|
| 1577 |
// Limit the maximum size of reference sources list to some |
| 1578 |
// sensible value to prevent the excessive memory allocation. |
| 1579 |
size_t MaxListSize=1000000; |
| 1580 |
|
| 1581 |
if (!AlreadyAdded && RefList.Size()<MaxListSize) |
| 1582 |
{ |
| 1583 |
ExtractRef Ref={0}; |
| 1584 |
Ref.RefName=wcsdup(Arc.FileHead.RedirName); |
| 1585 |
Ref.RefCount=1; |
| 1586 |
RefList.Push(Ref); |
| 1587 |
} |
| 1588 |
} |
| 1307 |
} |
1589 |
} |
|
|
1590 |
else |
| 1591 |
{ |
| 1592 |
if (PrevMatched) // First non-matched item after matched. |
| 1593 |
{ |
| 1594 |
// We would perform the unnecessarily string comparison |
| 1595 |
// when extracting if we set this value for first volume |
| 1596 |
// or non-volume archive. |
| 1597 |
if (!FirstVolume) |
| 1598 |
wcsncpyz(Analyze->EndName,NextName,ASIZE(Analyze->EndName)); |
| 1599 |
Analyze->EndPos=Arc.CurBlockPos; |
| 1600 |
} |
| 1601 |
PrevMatched=false; |
| 1602 |
} |
| 1308 |
} |
1603 |
} |
|
|
1604 |
|
| 1605 |
FirstFile=false; |
| 1309 |
if (Arc.FileHead.SplitAfter) |
1606 |
if (Arc.FileHead.SplitAfter) |
| 1310 |
{ |
1607 |
{ |
| 1311 |
OpenNext=true; // Allow open next volume. |
1608 |
OpenNext=true; // Allow open next volume. |
|
Link Here
|
| 1316 |
} |
1613 |
} |
| 1317 |
Arc.Close(); |
1614 |
Arc.Close(); |
| 1318 |
|
1615 |
|
| 1319 |
if (!OpenNext) |
1616 |
if (Volume && OpenNext) |
| 1320 |
break; |
1617 |
{ |
|
|
1618 |
NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); |
| 1619 |
FirstVolume=false; |
| 1321 |
|
1620 |
|
| 1322 |
NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); |
1621 |
// Needed for multivolume archives. Added in case some 'break' |
|
|
1622 |
// will quit early from loop above, so we do not set it in the loop. |
| 1623 |
// Now it can happen for hypothetical archive without file records |
| 1624 |
// and with HEAD_ENDARC record. |
| 1625 |
FirstFile=false; |
| 1626 |
} |
| 1627 |
else |
| 1628 |
break; |
| 1323 |
} |
1629 |
} |
| 1324 |
bool NewStartFound=wcscmp(VolName,StartName)!=0; |
1630 |
|
| 1325 |
if (NewStartFound) // Found a new volume to start extraction. |
1631 |
// If file references are present, we can't reliably skip in semi-solid |
| 1326 |
wcsncpyz(ArcName,StartName,ASIZE(ArcName)); |
1632 |
// archives, because reference source can be present in skipped data. |
| 1327 |
|
1633 |
if (RefList.Size()!=0) |
| 1328 |
return NewStartFound; |
1634 |
memset(Analyze,0,sizeof(*Analyze)); |
| 1329 |
} |
1635 |
} |
| 1330 |
#endif |
1636 |
#endif |
| 1331 |
|
1637 |
|