Browse code

Updated UnRAR 5.6.5 to 5.7.5. Library is still not-quite-vanilla, as we still have a hack in dll.cpp:332 allowing skipping of files in solid archives. For ClamAV, it only skips if encrypted, allowing it to continue scanning metadata for each encrypted file. This results in improved malware detection.

Micah Snyder authored on 2019/06/13 12:28:12
Showing 58 changed files
... ...
@@ -34,7 +34,7 @@ bool Archive::GetComment(Array<wchar> *CmtData)
34 34
 #ifndef SFX_MODULE
35 35
     // Old style (RAR 2.9) comment header embedded into the main 
36 36
     // archive header.
37
-    if (BrokenHeader)
37
+    if (BrokenHeader || CommHead.HeadSize<SIZEOF_COMMHEAD)
38 38
     {
39 39
       uiMsg(UIERROR_CMTBROKEN,FileName);
40 40
       return false;
... ...
@@ -57,6 +57,8 @@ bool Archive::GetComment(Array<wchar> *CmtData)
57 57
 #else
58 58
       UnpCmtLength=GetByte();
59 59
       UnpCmtLength+=(GetByte()<<8);
60
+      if (CmtLength<2)
61
+        return false;
60 62
       CmtLength-=2;
61 63
       DataIO.SetCmt13Encryption();
62 64
       CommHead.UnpVer=15;
... ...
@@ -85,15 +87,18 @@ bool Archive::GetComment(Array<wchar> *CmtData)
85 85
       byte *UnpData;
86 86
       size_t UnpDataSize;
87 87
       DataIO.GetUnpackedData(&UnpData,&UnpDataSize);
88
+      if (UnpDataSize>0)
89
+      {
88 90
 #ifdef _WIN_ALL
89
-      // If we ever decide to extend it to Android, we'll need to alloc
90
-      // 4x memory for OEM to UTF-8 output here.
91
-      OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
91
+        // If we ever decide to extend it to Android, we'll need to alloc
92
+        // 4x memory for OEM to UTF-8 output here.
93
+        OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
92 94
 #endif
93
-      CmtData->Alloc(UnpDataSize+1);
94
-      memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar));
95
-      CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size());
96
-      CmtData->Alloc(wcslen(CmtData->Addr(0)));
95
+        CmtData->Alloc(UnpDataSize+1);
96
+        memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar));
97
+        CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size());
98
+        CmtData->Alloc(wcslen(CmtData->Addr(0)));
99
+      }
97 100
     }
98 101
   }
99 102
   else
... ...
@@ -37,8 +37,7 @@ class Archive:public File
37 37
     void RequestArcPassword();
38 38
     void UnexpEndArcMsg();
39 39
     void BrokenHeaderMsg();
40
-    void UnkEncVerMsg(const wchar *Name);
41
-    void UnkEncVerMsg();
40
+    void UnkEncVerMsg(const wchar *Name,const wchar *Info);
42 41
     bool ReadCommentData(Array<wchar> *CmtData);
43 42
 
44 43
 #if !defined(RAR_NOCRYPT)
... ...
@@ -85,7 +84,7 @@ class Archive:public File
85 85
     void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile,
86 86
          const wchar *Name,uint Flags);
87 87
     bool ReadSubData(Array<byte> *UnpData,File *DestFile);
88
-    HEADER_TYPE GetHeaderType() {return CurHeaderType;};
88
+    HEADER_TYPE GetHeaderType() {return CurHeaderType;}
89 89
     RAROptions* GetRAROptions() {return Cmd;}
90 90
     void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
91 91
 #if 0
... ...
@@ -10,7 +10,10 @@ size_t Archive::ReadHeader()
10 10
 
11 11
   CurBlockPos=Tell();
12 12
 
13
-  size_t ReadSize;
13
+  // Other developers asked us to initialize it to suppress "may be used
14
+  // uninitialized" warning in code below in some compilers.
15
+  size_t ReadSize=0;
16
+
14 17
   switch(Format)
15 18
   {
16 19
 #ifndef SFX_MODULE
... ...
@@ -113,9 +116,9 @@ void Archive::BrokenHeaderMsg()
113 113
 }
114 114
 
115 115
 
116
-void Archive::UnkEncVerMsg(const wchar *Name)
116
+void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info)
117 117
 {
118
-  uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name);
118
+  uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info);
119 119
   ErrHandler.SetErrorCode(RARX_WARNING);
120 120
 }
121 121
 
... ...
@@ -201,7 +204,7 @@ size_t Archive::ReadHeader15()
201 201
     if (ShortBlock.HeaderType==HEAD_MAIN && (ShortBlock.Flags & MHD_COMMENT)!=0)
202 202
     {
203 203
       // Old style (up to RAR 2.9) main archive comment embedded into
204
-      // the main archive header found. While we can read the entire 
204
+      // the main archive header found. While we can read the entire
205 205
       // ShortBlock.HeadSize here and remove this part of "if", it would be
206 206
       // waste of memory, because we'll read and process this comment data
207 207
       // in other function anyway and we do not need them here now.
... ...
@@ -227,7 +230,7 @@ size_t Archive::ReadHeader15()
227 227
       Encrypted=(MainHead.Flags & MHD_PASSWORD)!=0;
228 228
       Signed=MainHead.PosAV!=0 || MainHead.HighPosAV!=0;
229 229
       MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0;
230
-    
230
+
231 231
       // Only for encrypted 3.0+ archives. 2.x archives did not have this
232 232
       // flag, so for non-encrypted archives, we'll set it later based on
233 233
       // file attributes.
... ...
@@ -254,7 +257,7 @@ size_t Archive::ReadHeader15()
254 254
         hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
255 255
         hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0;
256 256
         hd->Version=(hd->Flags & LHD_VERSION)!=0;
257
-        
257
+
258 258
         hd->DataSize=Raw.Get4();
259 259
         uint LowUnpSize=Raw.Get4();
260 260
         hd->HostOS=Raw.Get1();
... ...
@@ -279,7 +282,7 @@ size_t Archive::ReadHeader15()
279 279
           {
280 280
             case 13: hd->CryptMethod=CRYPT_RAR13; break;
281 281
             case 15: hd->CryptMethod=CRYPT_RAR15; break;
282
-            case 20: 
282
+            case 20:
283 283
             case 26: hd->CryptMethod=CRYPT_RAR20; break;
284 284
             default: hd->CryptMethod=CRYPT_RAR30; break;
285 285
           }
... ...
@@ -301,7 +304,7 @@ size_t Archive::ReadHeader15()
301 301
         }
302 302
 
303 303
         hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
304
-        
304
+
305 305
         hd->LargeFile=(hd->Flags & LHD_LARGE)!=0;
306 306
 
307 307
         uint HighPackSize,HighUnpSize;
... ...
@@ -311,7 +314,7 @@ size_t Archive::ReadHeader15()
311 311
           HighUnpSize=Raw.Get4();
312 312
           hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff);
313 313
         }
314
-        else 
314
+        else
315 315
         {
316 316
           HighPackSize=HighUnpSize=0;
317 317
           // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates
... ...
@@ -506,7 +509,7 @@ size_t Archive::ReadHeader15()
506 506
         NextBlockPos+=Raw.Get4();
507 507
       break;
508 508
   }
509
-  
509
+
510 510
   ushort HeaderCRC=Raw.GetCRC15(false);
511 511
 
512 512
   // Old AV header does not have header CRC properly set.
... ...
@@ -566,7 +569,7 @@ size_t Archive::ReadHeader50()
566 566
     // We repeat the password request only for manually entered passwords
567 567
     // and not for -p<pwd>. Wrong password can be intentionally provided
568 568
     // in -p<pwd> to not stop batch processing for encrypted archives.
569
-    bool GlobalPassword=Cmd->Password.IsSet();
569
+    bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
570 570
 
571 571
     while (true) // Repeat the password prompt for wrong passwords.
572 572
     {
... ...
@@ -641,7 +644,7 @@ size_t Archive::ReadHeader50()
641 641
     BrokenHeaderMsg();
642 642
     return 0;
643 643
   }
644
-  
644
+
645 645
   Raw.Read(SizeToRead);
646 646
 
647 647
   if (Raw.Size()<HeaderSize)
... ...
@@ -674,7 +677,7 @@ size_t Archive::ReadHeader50()
674 674
       return 0;
675 675
     }
676 676
   }
677
-  
677
+
678 678
   uint64 ExtraSize=0;
679 679
   if ((ShortBlock.Flags & HFL_EXTRA)!=0)
680 680
   {
... ...
@@ -702,7 +705,9 @@ size_t Archive::ReadHeader50()
702 702
         uint CryptVersion=(uint)Raw.GetV();
703 703
         if (CryptVersion>CRYPT_VERSION)
704 704
         {
705
-          UnkEncVerMsg(FileName);
705
+          wchar Info[20];
706
+          swprintf(Info,ASIZE(Info),L"h%u",CryptVersion);
707
+          UnkEncVerMsg(FileName,Info);
706 708
           return 0;
707 709
         }
708 710
         uint EncFlags=(uint)Raw.GetV();
... ...
@@ -710,7 +715,9 @@ size_t Archive::ReadHeader50()
710 710
         CryptHead.Lg2Count=Raw.Get1();
711 711
         if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
712 712
         {
713
-          UnkEncVerMsg(FileName);
713
+          wchar Info[20];
714
+          swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count);
715
+          UnkEncVerMsg(FileName,Info);
714 716
           return 0;
715 717
         }
716 718
         Raw.GetB(CryptHead.Salt,SIZE_SALT50);
... ...
@@ -763,7 +770,7 @@ size_t Archive::ReadHeader50()
763 763
           // to not break normal archive processing by calling function.
764 764
           int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
765 765
           HEADER_TYPE SaveCurHeaderType=CurHeaderType;
766
-          
766
+
767 767
           QOpen.Init(this,false);
768 768
           QOpen.Load(MainHead.QOpenOffset);
769 769
 
... ...
@@ -788,7 +795,7 @@ size_t Archive::ReadHeader50()
788 788
         hd->PackSize=DataSize;
789 789
         hd->FileFlags=(uint)Raw.GetV();
790 790
         hd->UnpSize=Raw.GetV();
791
-        
791
+
792 792
         hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0;
793 793
         if (hd->UnknownUnpSize)
794 794
           hd->UnpSize=INT64NDF;
... ...
@@ -815,6 +822,8 @@ size_t Archive::ReadHeader50()
815 815
         // but it was already used in RAR 1.5 and Unpack needs to distinguish
816 816
         // them.
817 817
         hd->UnpVer=(CompInfo & 0x3f) + 50;
818
+        if (hd->UnpVer!=50) // Only 5.0 compression is known now.
819
+          hd->UnpVer=VER_UNKNOWN;
818 820
 
819 821
         hd->HostOS=(byte)Raw.GetV();
820 822
         size_t NameSize=(size_t)Raw.GetV();
... ...
@@ -873,7 +882,7 @@ size_t Archive::ReadHeader50()
873 873
           RecoverySize=Header.RecSectionSize*Header.RecCount;
874 874
         }
875 875
 #endif
876
-          
876
+
877 877
         if (BadCRC) // Add the file name to broken header message displayed above.
878 878
           uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
879 879
       }
... ...
@@ -989,8 +998,12 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
989 989
           {
990 990
             FileHeader *hd=(FileHeader *)bb;
991 991
             uint EncVersion=(uint)Raw->GetV();
992
-            if (EncVersion > CRYPT_VERSION)
993
-              UnkEncVerMsg(hd->FileName);
992
+            if (EncVersion>CRYPT_VERSION)
993
+            {
994
+              wchar Info[20];
995
+              swprintf(Info,ASIZE(Info),L"x%u",EncVersion);
996
+              UnkEncVerMsg(hd->FileName,Info);
997
+            }
994 998
             else
995 999
             {
996 1000
               uint Flags=(uint)Raw->GetV();
... ...
@@ -998,7 +1011,11 @@ void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
998 998
               hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
999 999
               hd->Lg2Count=Raw->Get1();
1000 1000
               if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
1001
-                UnkEncVerMsg(hd->FileName);
1001
+              {
1002
+                wchar Info[20];
1003
+                swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count);
1004
+                UnkEncVerMsg(hd->FileName,Info);
1005
+              }
1002 1006
               Raw->GetB(hd->Salt,SIZE_SALT50);
1003 1007
               Raw->GetB(hd->InitV,SIZE_INITV);
1004 1008
               if (hd->UsePswCheck)
... ...
@@ -1290,7 +1307,7 @@ void Archive::ConvertAttributes()
1290 1290
 
1291 1291
   if (mask == (mode_t) -1)
1292 1292
   {
1293
-    // umask call returns the current umask value. Argument (022) is not 
1293
+    // umask call returns the current umask value. Argument (022) is not
1294 1294
     // really important here.
1295 1295
     mask = umask(022);
1296 1296
 
... ...
@@ -1367,8 +1384,8 @@ void Archive::ConvertFileHeader(FileHeader *hd)
1367 1367
 
1368 1368
     // ':' in file names is allowed in Unix, but not in Windows.
1369 1369
     // Even worse, file data will be written to NTFS stream on NTFS,
1370
-    // so automatic name correction on file create error in extraction 
1371
-    // routine does not work. In Windows and DOS versions we better 
1370
+    // so automatic name correction on file create error in extraction
1371
+    // routine does not work. In Windows and DOS versions we better
1372 1372
     // replace ':' now.
1373 1373
     if (*s==':')
1374 1374
       *s='_';
... ...
@@ -1,5 +1,8 @@
1 1
 #include "rar.hpp"
2 2
 
3
+#include "cmdfilter.cpp"
4
+#include "cmdmix.cpp"
5
+
3 6
 CommandData::CommandData()
4 7
 {
5 8
   Init();
... ...
@@ -120,6 +123,7 @@ void CommandData::ParseArg(wchar *Arg)
120 120
         wchar CmdChar=toupperw(*Command);
121 121
         bool Add=wcschr(L"AFUM",CmdChar)!=NULL;
122 122
         bool Extract=CmdChar=='X' || CmdChar=='E';
123
+        bool Repair=CmdChar=='R' && Command[1]==0;
123 124
         if (EndSeparator && !Add)
124 125
           wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
125 126
         else
... ...
@@ -130,15 +134,15 @@ void CommandData::ParseArg(wchar *Arg)
130 130
             FindData FileData;
131 131
             bool Found=FindFile::FastFind(Arg,&FileData);
132 132
             if ((!Found || ListMode==RCLM_ACCEPT_LISTS) && 
133
-                ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg))
133
+                ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg+1))
134 134
             {
135 135
               FileLists=true;
136 136
 
137 137
               ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true);
138 138
 
139 139
             }
140
-            else
141
-              if (Found && FileData.IsDir && Extract && *ExtrPath==0)
140
+            else // We use 'destpath\' when extracting and reparing.
141
+              if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0)
142 142
               {
143 143
                 wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
144 144
                 AddEndSlash(ExtrPath,ASIZE(ExtrPath));
... ...
@@ -280,7 +284,11 @@ void CommandData::ProcessSwitch(const wchar *Switch)
280 280
           ClearArc=true;
281 281
           break;
282 282
         case 'D':
283
-          AppendArcNameToPath=true;
283
+          if (Switch[2]==0)
284
+            AppendArcNameToPath=APPENDARCNAME_DESTPATH;
285
+          else
286
+            if (Switch[2]=='1')
287
+              AppendArcNameToPath=APPENDARCNAME_OWNDIR;
284 288
           break;
285 289
 #ifndef SFX_MODULE
286 290
         case 'G':
... ...
@@ -302,7 +310,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
302 302
           AddArcOnly=true;
303 303
           break;
304 304
         case 'P':
305
-          wcscpy(ArcPath,Switch+2);
305
+          wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath));
306 306
           break;
307 307
         case 'S':
308 308
           SyncFiles=true;
... ...
@@ -407,9 +415,9 @@ void CommandData::ProcessSwitch(const wchar *Switch)
407 407
         wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName));
408 408
         break;
409 409
       }
410
-      if (wcsicomp(Switch+1,L"SND")==0)
410
+      if (wcsnicomp(Switch+1,L"SND",3)==0)
411 411
       {
412
-        Sound=true;
412
+        Sound=Switch[4]=='-' ? SOUND_NOTIFY_OFF : SOUND_NOTIFY_ON;
413 413
         break;
414 414
       }
415 415
       if (wcsicomp(Switch+1,L"ERR")==0)
... ...
@@ -805,16 +813,16 @@ void CommandData::ProcessSwitch(const wchar *Switch)
805 805
           ArcTime=ARCTIME_LATEST;
806 806
           break;
807 807
         case 'O':
808
-          FileTimeBefore.SetAgeText(Switch+2);
808
+          SetTimeFilters(Switch+2,true,true);
809 809
           break;
810 810
         case 'N':
811
-          FileTimeAfter.SetAgeText(Switch+2);
811
+          SetTimeFilters(Switch+2,false,true);
812 812
           break;
813 813
         case 'B':
814
-          FileTimeBefore.SetIsoText(Switch+2);
814
+          SetTimeFilters(Switch+2,true,false);
815 815
           break;
816 816
         case 'A':
817
-          FileTimeAfter.SetIsoText(Switch+2);
817
+          SetTimeFilters(Switch+2,false,false);
818 818
           break;
819 819
         case 'S':
820 820
           {
... ...
@@ -897,7 +905,7 @@ void CommandData::ProcessSwitch(const wchar *Switch)
897 897
       if (Switch[1]==0)
898 898
       {
899 899
         // If comment file is not specified, we read data from stdin.
900
-        wcscpy(CommentFile,L"stdin");
900
+        wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile));
901 901
       }
902 902
       else
903 903
         wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile));
... ...
@@ -922,309 +930,6 @@ void CommandData::BadSwitch(const wchar *Switch)
922 922
 #endif
923 923
 
924 924
 
925
-void CommandData::OutTitle()
926
-{
927
-  if (BareOutput || DisableCopyright)
928
-    return;
929
-#if defined(__GNUC__) && defined(SFX_MODULE)
930
-  mprintf(St(MCopyrightS));
931
-#else
932
-#ifndef SILENT
933
-  static bool TitleShown=false;
934
-  if (TitleShown)
935
-    return;
936
-  TitleShown=true;
937
-
938
-  wchar Version[80];
939
-  if (RARVER_BETA!=0)
940
-    swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
941
-  else
942
-    swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
943
-#if defined(_WIN_32) || defined(_WIN_64)
944
-  wcsncatz(Version,L" ",ASIZE(Version));
945
-#endif
946
-#ifdef _WIN_32
947
-  wcsncatz(Version,St(Mx86),ASIZE(Version));
948
-#endif
949
-#ifdef _WIN_64
950
-  wcsncatz(Version,St(Mx64),ASIZE(Version));
951
-#endif
952
-  if (PrintVersion)
953
-  {
954
-    mprintf(L"%s",Version);
955
-    exit(0);
956
-  }
957
-  mprintf(St(MUCopyright),Version,RARVER_YEAR);
958
-#endif
959
-#endif
960
-}
961
-
962
-
963
-inline bool CmpMSGID(MSGID i1,MSGID i2)
964
-{
965
-#ifdef MSGID_INT
966
-  return i1==i2;
967
-#else
968
-  // If MSGID is const char*, we cannot compare pointers only.
969
-  // Pointers to different instances of same string can differ,
970
-  // so we need to compare complete strings.
971
-  return wcscmp(i1,i2)==0;
972
-#endif
973
-}
974
-
975
-void CommandData::OutHelp(RAR_EXIT ExitCode)
976
-{
977
-#if !defined(SILENT)
978
-  OutTitle();
979
-  static MSGID Help[]={
980
-#ifdef SFX_MODULE
981
-    // Console SFX switches definition.
982
-    MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
983
-#else
984
-    // UnRAR switches definition.
985
-    MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
986
-    MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,
987
-    MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP,
988
-    MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,
989
-    MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
990
-    MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
991
-    MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,
992
-    MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM,
993
-    MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,
994
-    MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,
995
-    MCHelpSwY
996
-#endif
997
-  };
998
-
999
-  for (uint I=0;I<ASIZE(Help);I++)
1000
-  {
1001
-#ifndef SFX_MODULE
1002
-    if (CmpMSGID(Help[I],MCHelpSwV))
1003
-      continue;
1004
-#ifndef _WIN_ALL
1005
-    static MSGID Win32Only[]={
1006
-      MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF,
1007
-      MCHelpSwEP2,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI
1008
-    };
1009
-    bool Found=false;
1010
-    for (int J=0;J<sizeof(Win32Only)/sizeof(Win32Only[0]);J++)
1011
-      if (CmpMSGID(Help[I],Win32Only[J]))
1012
-      {
1013
-        Found=true;
1014
-        break;
1015
-      }
1016
-    if (Found)
1017
-      continue;
1018
-#endif
1019
-#if !defined(_UNIX) && !defined(_WIN_ALL)
1020
-    if (CmpMSGID(Help[I],MCHelpSwOW))
1021
-      continue;
1022
-#endif
1023
-#if !defined(_WIN_ALL) && !defined(_EMX)
1024
-    if (CmpMSGID(Help[I],MCHelpSwAC))
1025
-      continue;
1026
-#endif
1027
-#ifndef SAVE_LINKS
1028
-    if (CmpMSGID(Help[I],MCHelpSwOL))
1029
-      continue;
1030
-#endif
1031
-#ifndef RAR_SMP
1032
-    if (CmpMSGID(Help[I],MCHelpSwMT))
1033
-      continue;
1034
-#endif
1035
-#endif
1036
-    mprintf(St(Help[I]));
1037
-  }
1038
-  mprintf(L"\n");
1039
-  ErrHandler.Exit(ExitCode);
1040
-#endif
1041
-}
1042
-
1043
-
1044
-// Return 'true' if we need to exclude the file from processing as result
1045
-// of -x switch. If CheckInclList is true, we also check the file against
1046
-// the include list created with -n switch.
1047
-bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList)
1048
-{
1049
-  if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
1050
-    return true;
1051
-  if (!CheckInclList || InclArgs.ItemsCount()==0)
1052
-    return false;
1053
-  if (CheckArgs(&InclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
1054
-    return false;
1055
-  return true;
1056
-}
1057
-
1058
-
1059
-bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode)
1060
-{
1061
-  wchar *Name=ConvertPath(CheckName,NULL);
1062
-  wchar FullName[NM];
1063
-  wchar CurMask[NM];
1064
-  *FullName=0;
1065
-  Args->Rewind();
1066
-  while (Args->GetString(CurMask,ASIZE(CurMask)))
1067
-  {
1068
-    wchar *LastMaskChar=PointToLastChar(CurMask);
1069
-    bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only.
1070
-
1071
-    if (Dir)
1072
-    {
1073
-      // CheckName is a directory.
1074
-      if (DirMask)
1075
-      {
1076
-        // We process the directory and have the directory exclusion mask.
1077
-        // So let's convert "mask\" to "mask" and process it normally.
1078
-        
1079
-        *LastMaskChar=0;
1080
-      }
1081
-      else
1082
-      {
1083
-        // REMOVED, we want -npath\* to match empty folders too.
1084
-        // If mask has wildcards in name part and does not have the trailing
1085
-        // '\' character, we cannot use it for directories.
1086
-      
1087
-        // if (IsWildcard(PointToName(CurMask)))
1088
-        //  continue;
1089
-      }
1090
-    }
1091
-    else
1092
-    {
1093
-      // If we process a file inside of directory excluded by "dirmask\".
1094
-      // we want to exclude such file too. So we convert "dirmask\" to
1095
-      // "dirmask\*". It is important for operations other than archiving
1096
-      // with -x. When archiving with -x, directory matched by "dirmask\"
1097
-      // is excluded from further scanning.
1098
-
1099
-      if (DirMask)
1100
-        wcsncatz(CurMask,L"*",ASIZE(CurMask));
1101
-    }
1102
-
1103
-#ifndef SFX_MODULE
1104
-    if (CheckFullPath && IsFullPath(CurMask))
1105
-    {
1106
-      // We do not need to do the special "*\" processing here, because
1107
-      // unlike the "else" part of this "if", now we convert names to full
1108
-      // format, so they all include the path, which is matched by "*\"
1109
-      // correctly. Moreover, removing "*\" from mask would break
1110
-      // the comparison, because now all names have the path.
1111
-
1112
-      if (*FullName==0)
1113
-        ConvertNameToFull(CheckName,FullName,ASIZE(FullName));
1114
-      if (CmpName(CurMask,FullName,MatchMode))
1115
-        return true;
1116
-    }
1117
-    else
1118
-#endif
1119
-    {
1120
-      wchar NewName[NM+2],*CurName=Name;
1121
-
1122
-      // Important to convert before "*\" check below, so masks like
1123
-      // d:*\something are processed properly.
1124
-      wchar *CmpMask=ConvertPath(CurMask,NULL);
1125
-
1126
-      if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
1127
-      {
1128
-        // We want "*\name" to match 'name' not only in subdirectories,
1129
-        // but also in the current directory. We convert the name
1130
-        // from 'name' to '.\name' to be matched by "*\" part even if it is
1131
-        // in current directory.
1132
-        NewName[0]='.';
1133
-        NewName[1]=CPATHDIVIDER;
1134
-        wcsncpyz(NewName+2,Name,ASIZE(NewName)-2);
1135
-        CurName=NewName;
1136
-      }
1137
-
1138
-      if (CmpName(CmpMask,CurName,MatchMode))
1139
-        return true;
1140
-    }
1141
-  }
1142
-  return false;
1143
-}
1144
-
1145
-
1146
-#ifndef SFX_MODULE
1147
-// Now this function performs only one task and only in Windows version:
1148
-// it skips symlinks to directories if -e1024 switch is specified.
1149
-// Symlinks are skipped in ScanTree class, so their entire contents
1150
-// is skipped too. Without this function we would check the attribute
1151
-// only directly before archiving, so we would skip the symlink record,
1152
-// but not the contents of symlinked directory.
1153
-bool CommandData::ExclDirByAttr(uint FileAttr)
1154
-{
1155
-#ifdef _WIN_ALL
1156
-  if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 &&
1157
-      (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
1158
-    return true;
1159
-#endif
1160
-  return false;
1161
-}
1162
-#endif
1163
-
1164
-
1165
-
1166
-
1167
-#ifndef SFX_MODULE
1168
-// Return 'true' if we need to exclude the file from processing.
1169
-bool CommandData::TimeCheck(RarTime &ft)
1170
-{
1171
-  if (FileTimeBefore.IsSet() && ft>=FileTimeBefore)
1172
-    return true;
1173
-  if (FileTimeAfter.IsSet() && ft<=FileTimeAfter)
1174
-    return true;
1175
-  return false;
1176
-}
1177
-#endif
1178
-
1179
-
1180
-#ifndef SFX_MODULE
1181
-// Return 'true' if we need to exclude the file from processing.
1182
-bool CommandData::SizeCheck(int64 Size)
1183
-{
1184
-  if (FileSizeLess!=INT64NDF && Size>=FileSizeLess)
1185
-    return(true);
1186
-  if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
1187
-    return(true);
1188
-  return(false);
1189
-}
1190
-#endif
1191
-
1192
-
1193
-
1194
-
1195
-int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
1196
-                               wchar *MatchedArg,uint MatchedArgSize)
1197
-{
1198
-  if (MatchedArg!=NULL && MatchedArgSize>0)
1199
-    *MatchedArg=0;
1200
-//  if (wcslen(FileHead.FileName)>=NM)
1201
-//    return 0;
1202
-  bool Dir=FileHead.Dir;
1203
-  if (ExclCheck(FileHead.FileName,Dir,false,true))
1204
-    return 0;
1205
-#ifndef SFX_MODULE
1206
-  if (TimeCheck(FileHead.mtime))
1207
-    return 0;
1208
-  if ((FileHead.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0)
1209
-    return 0;
1210
-  if (!Dir && SizeCheck(FileHead.UnpSize))
1211
-    return 0;
1212
-#endif
1213
-  wchar *ArgName;
1214
-  FileArgs.Rewind();
1215
-  for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++)
1216
-    if (CmpName(ArgName,FileHead.FileName,MatchType))
1217
-    {
1218
-      if (ExactMatch!=NULL)
1219
-        *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
1220
-      if (MatchedArg!=NULL)
1221
-        wcsncpyz(MatchedArg,ArgName,MatchedArgSize);
1222
-      return StringCount;
1223
-    }
1224
-  return 0;
1225
-}
1226
-
1227
-
1228 925
 void CommandData::ProcessCommand()
1229 926
 {
1230 927
 #ifndef SFX_MODULE
... ...
@@ -6,6 +6,8 @@
6 6
 
7 7
 enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS};
8 8
 
9
+enum IS_PROCESS_FILE_FLAGS {IPFF_EXCLUDE_PARENT=1};
10
+
9 11
 class CommandData:public RAROptions
10 12
 {
11 13
   private:
... ...
@@ -13,6 +15,9 @@ class CommandData:public RAROptions
13 13
     void ProcessSwitch(const wchar *Switch);
14 14
     void BadSwitch(const wchar *Switch);
15 15
     uint GetExclAttr(const wchar *Str);
16
+#if !defined(SFX_MODULE)
17
+    void SetTimeFilters(const wchar *Mod,bool Before,bool Age);
18
+#endif
16 19
 
17 20
     bool FileLists;
18 21
     bool NoMoreSwitches;
... ...
@@ -34,11 +39,11 @@ class CommandData:public RAROptions
34 34
     bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList);
35 35
     static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode);
36 36
     bool ExclDirByAttr(uint FileAttr);
37
-    bool TimeCheck(RarTime &ft);
37
+    bool TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta);
38 38
     bool SizeCheck(int64 Size);
39 39
     bool AnyFiltersActive();
40
-    int IsProcessFile(FileHeader &FileHead,bool *ExactMatch=NULL,int MatchType=MATCH_WILDSUBPATH,
41
-                      wchar *MatchedArg=NULL,uint MatchedArgSize=0);
40
+    int IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
41
+                      bool Flags,wchar *MatchedArg,uint MatchedArgSize);
42 42
     void ProcessCommand();
43 43
     void AddArcName(const wchar *Name);
44 44
     bool GetArcName(wchar *Name,int MaxSize);
45 45
new file mode 100644
... ...
@@ -0,0 +1,305 @@
0
+// Return 'true' if we need to exclude the file from processing as result
1
+// of -x switch. If CheckInclList is true, we also check the file against
2
+// the include list created with -n switch.
3
+bool CommandData::ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList)
4
+{
5
+  if (CheckArgs(&ExclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
6
+    return true;
7
+  if (!CheckInclList || InclArgs.ItemsCount()==0)
8
+    return false;
9
+  if (CheckArgs(&InclArgs,Dir,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
10
+    return false;
11
+  return true;
12
+}
13
+
14
+
15
+bool CommandData::CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode)
16
+{
17
+  wchar *Name=ConvertPath(CheckName,NULL,0);
18
+  wchar FullName[NM];
19
+  wchar CurMask[NM];
20
+  *FullName=0;
21
+  Args->Rewind();
22
+  while (Args->GetString(CurMask,ASIZE(CurMask)))
23
+  {
24
+    wchar *LastMaskChar=PointToLastChar(CurMask);
25
+    bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only.
26
+
27
+    if (Dir)
28
+    {
29
+      // CheckName is a directory.
30
+      if (DirMask)
31
+      {
32
+        // We process the directory and have the directory exclusion mask.
33
+        // So let's convert "mask\" to "mask" and process it normally.
34
+        
35
+        *LastMaskChar=0;
36
+      }
37
+      else
38
+      {
39
+        // REMOVED, we want -npath\* to match empty folders too.
40
+        // If mask has wildcards in name part and does not have the trailing
41
+        // '\' character, we cannot use it for directories.
42
+      
43
+        // if (IsWildcard(PointToName(CurMask)))
44
+        //  continue;
45
+      }
46
+    }
47
+    else
48
+    {
49
+      // If we process a file inside of directory excluded by "dirmask\".
50
+      // we want to exclude such file too. So we convert "dirmask\" to
51
+      // "dirmask\*". It is important for operations other than archiving
52
+      // with -x. When archiving with -x, directory matched by "dirmask\"
53
+      // is excluded from further scanning.
54
+
55
+      if (DirMask)
56
+        wcsncatz(CurMask,L"*",ASIZE(CurMask));
57
+    }
58
+
59
+#ifndef SFX_MODULE
60
+    if (CheckFullPath && IsFullPath(CurMask))
61
+    {
62
+      // We do not need to do the special "*\" processing here, because
63
+      // unlike the "else" part of this "if", now we convert names to full
64
+      // format, so they all include the path, which is matched by "*\"
65
+      // correctly. Moreover, removing "*\" from mask would break
66
+      // the comparison, because now all names have the path.
67
+
68
+      if (*FullName==0)
69
+        ConvertNameToFull(CheckName,FullName,ASIZE(FullName));
70
+      if (CmpName(CurMask,FullName,MatchMode))
71
+        return true;
72
+    }
73
+    else
74
+#endif
75
+    {
76
+      wchar NewName[NM+2],*CurName=Name;
77
+
78
+      // Important to convert before "*\" check below, so masks like
79
+      // d:*\something are processed properly.
80
+      wchar *CmpMask=ConvertPath(CurMask,NULL,0);
81
+
82
+      if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
83
+      {
84
+        // We want "*\name" to match 'name' not only in subdirectories,
85
+        // but also in the current directory. We convert the name
86
+        // from 'name' to '.\name' to be matched by "*\" part even if it is
87
+        // in current directory.
88
+        NewName[0]='.';
89
+        NewName[1]=CPATHDIVIDER;
90
+        wcsncpyz(NewName+2,Name,ASIZE(NewName)-2);
91
+        CurName=NewName;
92
+      }
93
+
94
+      if (CmpName(CmpMask,CurName,MatchMode))
95
+        return true;
96
+    }
97
+  }
98
+  return false;
99
+}
100
+
101
+
102
+
103
+
104
+#ifndef SFX_MODULE
105
+// Now this function performs only one task and only in Windows version:
106
+// it skips symlinks to directories if -e1024 switch is specified.
107
+// Symlinks are skipped in ScanTree class, so their entire contents
108
+// is skipped too. Without this function we would check the attribute
109
+// only directly before archiving, so we would skip the symlink record,
110
+// but not the contents of symlinked directory.
111
+bool CommandData::ExclDirByAttr(uint FileAttr)
112
+{
113
+#ifdef _WIN_ALL
114
+  if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 &&
115
+      (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
116
+    return true;
117
+#endif
118
+  return false;
119
+}
120
+#endif
121
+
122
+
123
+
124
+
125
+#if !defined(SFX_MODULE)
126
+void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age)
127
+{
128
+  bool ModeOR=false,TimeMods=false;
129
+  const wchar *S=Mod;
130
+  // Check if any 'mca' modifiers are present, set OR mode if 'o' is present,
131
+  // skip modifiers and set S to beginning of time string. Be sure to check
132
+  // *S!=0, because termination 0 is a part of string for wcschr.
133
+  for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++)
134
+    if (*S=='o' || *S=='O')
135
+      ModeOR=true;
136
+    else
137
+      TimeMods=true;
138
+
139
+  if (!TimeMods) // Assume 'm' if no modifiers are specified.
140
+    Mod=L"m";
141
+
142
+  // Set the specified time for every modifier. Be sure to check *Mod!=0,
143
+  // because termination 0 is a part of string for wcschr. This check is
144
+  // important when we set Mod to "m" above.
145
+  for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++)
146
+    switch(toupperw(*Mod))
147
+    {
148
+      case 'M': 
149
+        if (Before)
150
+        {
151
+          Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S);
152
+          FileMtimeBeforeOR=ModeOR;
153
+        }
154
+        else
155
+        {
156
+          Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S);
157
+          FileMtimeAfterOR=ModeOR;
158
+        }
159
+        break;
160
+      case 'C':
161
+        if (Before)
162
+        {
163
+          Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S);
164
+          FileCtimeBeforeOR=ModeOR;
165
+        }
166
+        else
167
+        {
168
+          Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S);
169
+          FileCtimeAfterOR=ModeOR;
170
+        }
171
+        break;
172
+      case 'A':
173
+        if (Before)
174
+        {
175
+          Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S);
176
+          FileAtimeBeforeOR=ModeOR;
177
+        }
178
+        else
179
+        {
180
+          Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S);
181
+          FileAtimeAfterOR=ModeOR;
182
+        }
183
+        break;
184
+    }
185
+}
186
+#endif
187
+
188
+
189
+#ifndef SFX_MODULE
190
+// Return 'true' if we need to exclude the file from processing.
191
+bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta)
192
+{
193
+  bool FilterOR=false;
194
+
195
+  if (FileMtimeBefore.IsSet()) // Filter present.
196
+    if (ftm>=FileMtimeBefore) // Condition not matched.
197
+      if (FileMtimeBeforeOR) 
198
+        FilterOR=true; // Not matched OR filter is present.
199
+      else
200
+        return true; // Exclude file in AND mode.
201
+    else  // Condition matched.
202
+      if (FileMtimeBeforeOR) 
203
+        return false; // Include file in OR mode.
204
+
205
+  if (FileMtimeAfter.IsSet()) // Filter present.
206
+    if (ftm<FileMtimeAfter) // Condition not matched.
207
+      if (FileMtimeAfterOR) 
208
+        FilterOR=true; // Not matched OR filter is present.
209
+      else
210
+        return true; // Exclude file in AND mode.
211
+    else  // Condition matched.
212
+      if (FileMtimeAfterOR) 
213
+        return false; // Include file in OR mode.
214
+
215
+  if (FileCtimeBefore.IsSet()) // Filter present.
216
+    if (ftc>=FileCtimeBefore) // Condition not matched.
217
+      if (FileCtimeBeforeOR) 
218
+        FilterOR=true; // Not matched OR filter is present.
219
+      else
220
+        return true; // Exclude file in AND mode.
221
+    else  // Condition matched.
222
+      if (FileCtimeBeforeOR) 
223
+        return false; // Include file in OR mode.
224
+
225
+  if (FileCtimeAfter.IsSet()) // Filter present.
226
+    if (ftc<FileCtimeAfter) // Condition not matched.
227
+      if (FileCtimeAfterOR) 
228
+        FilterOR=true; // Not matched OR filter is present.
229
+      else
230
+        return true; // Exclude file in AND mode.
231
+    else  // Condition matched.
232
+      if (FileCtimeAfterOR) 
233
+        return false; // Include file in OR mode.
234
+
235
+  if (FileAtimeBefore.IsSet()) // Filter present.
236
+    if (fta>=FileAtimeBefore) // Condition not matched.
237
+      if (FileAtimeBeforeOR) 
238
+        FilterOR=true; // Not matched OR filter is present.
239
+      else
240
+        return true; // Exclude file in AND mode.
241
+    else  // Condition matched.
242
+      if (FileAtimeBeforeOR) 
243
+        return false; // Include file in OR mode.
244
+
245
+  if (FileAtimeAfter.IsSet()) // Filter present.
246
+    if (fta<FileAtimeAfter) // Condition not matched.
247
+      if (FileAtimeAfterOR) 
248
+        FilterOR=true; // Not matched OR filter is present.
249
+      else
250
+        return true; // Exclude file in AND mode.
251
+    else  // Condition matched.
252
+      if (FileAtimeAfterOR) 
253
+        return false; // Include file in OR mode.
254
+
255
+  return FilterOR; // Exclude if all OR filters are not matched.
256
+}
257
+#endif
258
+
259
+
260
+#ifndef SFX_MODULE
261
+// Return 'true' if we need to exclude the file from processing.
262
+bool CommandData::SizeCheck(int64 Size)
263
+{
264
+  if (FileSizeLess!=INT64NDF && Size>=FileSizeLess)
265
+    return true;
266
+  if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
267
+    return true;
268
+  return false;
269
+}
270
+#endif
271
+
272
+
273
+
274
+
275
+// Return 0 if file must not be processed or a number of matched parameter otherwise.
276
+int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
277
+                               bool Flags,wchar *MatchedArg,uint MatchedArgSize)
278
+{
279
+  if (MatchedArg!=NULL && MatchedArgSize>0)
280
+    *MatchedArg=0;
281
+  bool Dir=FileHead.Dir;
282
+  if (ExclCheck(FileHead.FileName,Dir,false,true))
283
+    return 0;
284
+#ifndef SFX_MODULE
285
+  if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime))
286
+    return 0;
287
+  if ((FileHead.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0)
288
+    return 0;
289
+  if (!Dir && SizeCheck(FileHead.UnpSize))
290
+    return 0;
291
+#endif
292
+  wchar *ArgName;
293
+  FileArgs.Rewind();
294
+  for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++)
295
+    if (CmpName(ArgName,FileHead.FileName,MatchType))
296
+    {
297
+      if (ExactMatch!=NULL)
298
+        *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
299
+      if (MatchedArg!=NULL)
300
+        wcsncpyz(MatchedArg,ArgName,MatchedArgSize);
301
+      return StringCount;
302
+    }
303
+  return 0;
304
+}
0 305
new file mode 100644
... ...
@@ -0,0 +1,118 @@
0
+void CommandData::OutTitle()
1
+{
2
+  if (BareOutput || DisableCopyright)
3
+    return;
4
+#if defined(__GNUC__) && defined(SFX_MODULE)
5
+  mprintf(St(MCopyrightS));
6
+#else
7
+#ifndef SILENT
8
+  static bool TitleShown=false;
9
+  if (TitleShown)
10
+    return;
11
+  TitleShown=true;
12
+
13
+  wchar Version[80];
14
+  if (RARVER_BETA!=0)
15
+    swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
16
+  else
17
+    swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
18
+#if defined(_WIN_32) || defined(_WIN_64)
19
+  wcsncatz(Version,L" ",ASIZE(Version));
20
+#endif
21
+#ifdef _WIN_32
22
+  wcsncatz(Version,St(Mx86),ASIZE(Version));
23
+#endif
24
+#ifdef _WIN_64
25
+  wcsncatz(Version,St(Mx64),ASIZE(Version));
26
+#endif
27
+  if (PrintVersion)
28
+  {
29
+    mprintf(L"%s",Version);
30
+    exit(0);
31
+  }
32
+  mprintf(St(MUCopyright),Version,RARVER_YEAR);
33
+#endif
34
+#endif
35
+}
36
+
37
+
38
+inline bool CmpMSGID(MSGID i1,MSGID i2)
39
+{
40
+#ifdef MSGID_INT
41
+  return i1==i2;
42
+#else
43
+  // If MSGID is const char*, we cannot compare pointers only.
44
+  // Pointers to different instances of same string can differ,
45
+  // so we need to compare complete strings.
46
+  return wcscmp(i1,i2)==0;
47
+#endif
48
+}
49
+
50
+void CommandData::OutHelp(RAR_EXIT ExitCode)
51
+{
52
+#if !defined(SILENT)
53
+  OutTitle();
54
+  static MSGID Help[]={
55
+#ifdef SFX_MODULE
56
+    // Console SFX switches definition.
57
+    MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
58
+#else
59
+    // UnRAR switches definition.
60
+    MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
61
+    MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,
62
+    MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP,
63
+    MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,
64
+    MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
65
+    MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
66
+    MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,
67
+    MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM,
68
+    MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,
69
+    MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,
70
+    MCHelpSwY
71
+#endif
72
+  };
73
+
74
+  for (uint I=0;I<ASIZE(Help);I++)
75
+  {
76
+#ifndef SFX_MODULE
77
+    if (CmpMSGID(Help[I],MCHelpSwV))
78
+      continue;
79
+#ifndef _WIN_ALL
80
+    static MSGID Win32Only[]={
81
+      MCHelpSwIEML,MCHelpSwVD,MCHelpSwAO,MCHelpSwOS,MCHelpSwIOFF,
82
+      MCHelpSwEP2,MCHelpSwOC,MCHelpSwONI,MCHelpSwDR,MCHelpSwRI
83
+    };
84
+    bool Found=false;
85
+    for (uint J=0;J<ASIZE(Win32Only);J++)
86
+      if (CmpMSGID(Help[I],Win32Only[J]))
87
+      {
88
+        Found=true;
89
+        break;
90
+      }
91
+    if (Found)
92
+      continue;
93
+#endif
94
+#if !defined(_UNIX) && !defined(_WIN_ALL)
95
+    if (CmpMSGID(Help[I],MCHelpSwOW))
96
+      continue;
97
+#endif
98
+#if !defined(_WIN_ALL) && !defined(_EMX)
99
+    if (CmpMSGID(Help[I],MCHelpSwAC))
100
+      continue;
101
+#endif
102
+#ifndef SAVE_LINKS
103
+    if (CmpMSGID(Help[I],MCHelpSwOL))
104
+      continue;
105
+#endif
106
+#ifndef RAR_SMP
107
+    if (CmpMSGID(Help[I],MCHelpSwMT))
108
+      continue;
109
+#endif
110
+#endif
111
+    mprintf(St(Help[I]));
112
+  }
113
+  mprintf(L"\n");
114
+  ErrHandler.Exit(ExitCode);
115
+#endif
116
+}
117
+
... ...
@@ -28,8 +28,8 @@ void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,co
28 28
     sha1_context c;
29 29
     sha1_init(&c);
30 30
 
31
-    const int HashRounds=0x40000;
32
-    for (int I=0;I<HashRounds;I++)
31
+    const uint HashRounds=0x40000;
32
+    for (uint I=0;I<HashRounds;I++)
33 33
     {
34 34
       sha1_process_rar29( &c, RawPsw, RawLength );
35 35
       byte PswNum[3];
... ...
@@ -47,8 +47,8 @@ void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,co
47 47
     }
48 48
     uint32 digest[5];
49 49
     sha1_done( &c, digest );
50
-    for (int I=0;I<4;I++)
51
-      for (int J=0;J<4;J++)
50
+    for (uint I=0;I<4;I++)
51
+      for (uint J=0;J<4;J++)
52 52
         AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
53 53
 
54 54
     KDF3Cache[KDF3CachePos].Pwd=*Password;
... ...
@@ -42,7 +42,7 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
42 42
     Data->Cmd.DllError=0;
43 43
     Data->OpenMode=r->OpenMode;
44 44
     Data->Cmd.FileArgs.AddString(L"*");
45
-    Data->Cmd.KeepBroken = true;
45
+    Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0;
46 46
 
47 47
     char AnsiArcName[NM];
48 48
     *AnsiArcName=0;
... ...
@@ -93,9 +93,11 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
93 93
       return NULL;
94 94
     }
95 95
     r->Flags=0;
96
-    
96
+
97 97
     if (Data->Arc.Volume)
98 98
       r->Flags|=0x01;
99
+    if (Data->Arc.MainComment)
100
+      r->Flags|=0x02;
99 101
     if (Data->Arc.Locked)
100 102
       r->Flags|=0x04;
101 103
     if (Data->Arc.Solid)
... ...
@@ -114,17 +116,29 @@ HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
114 114
     Array<wchar> CmtDataW;
115 115
     if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW))
116 116
     {
117
-      Array<char> CmtData(CmtDataW.Size()*4+1);
118
-      memset(&CmtData[0],0,CmtData.Size());
119
-      WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1);
120
-      size_t Size=strlen(&CmtData[0])+1;
121
-
122
-      r->Flags|=2;
123
-      r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
124
-      r->CmtSize=(uint)Min(Size,r->CmtBufSize);
125
-      memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
126
-      if (Size<=r->CmtBufSize)
127
-        r->CmtBuf[r->CmtSize-1]=0;
117
+      if (r->CmtBufW!=NULL)
118
+      {
119
+        CmtDataW.Push(0);
120
+        size_t Size=wcslen(&CmtDataW[0])+1;
121
+
122
+        r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
123
+        r->CmtSize=(uint)Min(Size,r->CmtBufSize);
124
+        memcpy(r->CmtBufW,&CmtDataW[0],(r->CmtSize-1)*sizeof(*r->CmtBufW));
125
+        r->CmtBufW[r->CmtSize-1]=0;
126
+      }
127
+      else
128
+        if (r->CmtBuf!=NULL)
129
+        {
130
+          Array<char> CmtData(CmtDataW.Size()*4+1);
131
+          memset(&CmtData[0],0,CmtData.Size());
132
+          WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1);
133
+          size_t Size=strlen(&CmtData[0])+1;
134
+
135
+          r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
136
+          r->CmtSize=(uint)Min(Size,r->CmtBufSize);
137
+          memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
138
+          r->CmtBuf[r->CmtSize-1]=0;
139
+        }
128 140
     }
129 141
     else
130 142
       r->CmtState=r->CmtSize=0;
... ...
@@ -216,7 +230,7 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
216 216
       // open callback for RAR5 archives and if password is invalid.
217 217
       if (Data->Arc.FailedHeaderDecryption)
218 218
         return ERAR_BAD_PASSWORD;
219
-      
219
+
220 220
       return ERAR_END_ARCHIVE;
221 221
     }
222 222
     FileHeader *hd=&Data->Arc.FileHead;
... ...
@@ -254,13 +268,10 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
254 254
     D->UnpSize=uint(hd->UnpSize & 0xffffffff);
255 255
     D->UnpSizeHigh=uint(hd->UnpSize>>32);
256 256
     D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX;
257
-    if (Data->Arc.Format==RARFMT50)
258
-      D->UnpVer=Data->Arc.FileHead.UnpVer==0 ? 50 : 200; // If it is not 0, just set it to something big.
259
-    else
260
-      D->UnpVer=Data->Arc.FileHead.UnpVer;
257
+    D->UnpVer=Data->Arc.FileHead.UnpVer;
261 258
     D->FileCRC=hd->FileHash.CRC32;
262 259
     D->FileTime=hd->mtime.GetDos();
263
-    
260
+
264 261
     uint64 MRaw=hd->mtime.GetWin();
265 262
     D->MtimeLow=(uint)MRaw;
266 263
     D->MtimeHigh=(uint)(MRaw>>32);
... ...
@@ -373,7 +384,7 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa
373 373
       if (DestNameW!=NULL)
374 374
         wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName));
375 375
 
376
-      wcscpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T");
376
+      wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command));
377 377
       Data->Cmd.Test=Operation!=RAR_EXTRACT;
378 378
       bool Repeat=false;
379 379
       Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
... ...
@@ -385,7 +396,7 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa
385 385
       // if archive is still open to avoid calling file operations on
386 386
       // the invalid file handle. Some of our file operations like Seek()
387 387
       // process such invalid handle correctly, some not.
388
-      while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 && 
388
+      while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 &&
389 389
              Data->Arc.GetHeaderType()==HEAD_SERVICE)
390 390
       {
391 391
         Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
... ...
@@ -440,16 +451,16 @@ void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataPro
440 440
 }
441 441
 
442 442
 
443
-#ifndef RAR_NOCRYPT
444 443
 void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
445 444
 {
445
+#ifndef RAR_NOCRYPT
446 446
   DataSet *Data=(DataSet *)hArcData;
447 447
   wchar PasswordW[MAXPASSWORD];
448 448
   GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW));
449 449
   Data->Cmd.Password.Set(PasswordW);
450 450
   cleandata(PasswordW,sizeof(PasswordW));
451
-}
452 451
 #endif
452
+}
453 453
 
454 454
 
455 455
 int PASCAL RARGetDllVersion()
... ...
@@ -5,6 +5,7 @@ EXPORTS
5 5
   RARReadHeader
6 6
   RARReadHeaderEx
7 7
   RARProcessFile
8
+  RARProcessFileW
8 9
   RARSetCallback
9 10
   RARSetChangeVolProc
10 11
   RARSetProcessDataProc
... ...
@@ -1,7 +1,7 @@
1 1
 #ifndef _UNRAR_DLL_
2 2
 #define _UNRAR_DLL_
3 3
 
4
-#pragma pack(1)
4
+#pragma pack(push, 1)
5 5
 
6 6
 #define ERAR_SUCCESS             0
7 7
 #define ERAR_END_ARCHIVE        10
... ...
@@ -135,6 +135,8 @@ typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM
135 135
 #define ROADF_ENCHEADERS   0x0080
136 136
 #define ROADF_FIRSTVOLUME  0x0100
137 137
 
138
+#define ROADOF_KEEPBROKEN  0x0001
139
+
138 140
 struct RAROpenArchiveDataEx
139 141
 {
140 142
   char         *ArcName;
... ...
@@ -148,7 +150,9 @@ struct RAROpenArchiveDataEx
148 148
   unsigned int  Flags;
149 149
   UNRARCALLBACK Callback;
150 150
   LPARAM        UserData;
151
-  unsigned int  Reserved[28];
151
+  unsigned int  OpFlags;
152
+  wchar_t      *CmtBufW;
153
+  unsigned int  Reserved[25];
152 154
 };
153 155
 
154 156
 enum UNRARCALLBACK_MESSAGES {
... ...
@@ -180,6 +184,6 @@ int    PASCAL RARGetDllVersion();
180 180
 }
181 181
 #endif
182 182
 
183
-#pragma pack()
183
+#pragma pack(pop)
184 184
 
185 185
 #endif
... ...
@@ -2,8 +2,8 @@
2 2
 #include <commctrl.h>
3 3
 
4 4
 VS_VERSION_INFO VERSIONINFO
5
-FILEVERSION 5, 60, 100, 2736
6
-PRODUCTVERSION 5, 60, 100, 2736
5
+FILEVERSION 5, 71, 100, 3045
6
+PRODUCTVERSION 5, 71, 100, 3045
7 7
 FILEOS VOS__WINDOWS32
8 8
 FILETYPE VFT_APP
9 9
 {
... ...
@@ -14,9 +14,9 @@ FILETYPE VFT_APP
14 14
       VALUE "CompanyName", "Alexander Roshal\0"
15 15
       VALUE "ProductName", "RAR decompression library\0"
16 16
       VALUE "FileDescription", "RAR decompression library\0"
17
-      VALUE "FileVersion", "5.60.0\0"
18
-      VALUE "ProductVersion", "5.60.0\0"
19

                
17
+      VALUE "FileVersion", "5.71.0\0"
18
+      VALUE "ProductVersion", "5.71.0\0"
19

                
20 20
       VALUE "OriginalFilename", "Unrar.dll\0"
21 21
     }
22 22
   }
23 23
new file mode 100644
... ...
@@ -0,0 +1,13 @@
0
+EXPORTS
1
+  RAROpenArchive
2
+  RAROpenArchiveEx
3
+  RARCloseArchive
4
+  RARReadHeader
5
+  RARReadHeaderEx
6
+  RARProcessFile
7
+  RARProcessFileW
8
+  RARSetCallback
9
+  RARSetChangeVolProc
10
+  RARSetProcessDataProc
11
+;  RARSetPassword
12
+  RARGetDllVersion
... ...
@@ -56,7 +56,7 @@ class ErrorHandler
56 56
     uint GetErrorCount() {return ErrCount;}
57 57
     void SetSignalHandlers(bool Enable);
58 58
     void Throw(RAR_EXIT Code);
59
-    void SetSilent(bool Mode) {Silent=Mode;};
59
+    void SetSilent(bool Mode) {Silent=Mode;}
60 60
     bool GetSysErrMsg(wchar *Msg,size_t Size);
61 61
     void SysErrMsg();
62 62
     int GetSystemErrorCode();
... ...
@@ -87,7 +87,7 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc)
87 87
   FirstFile=true;
88 88
 #endif
89 89
 
90
-  GlobalPassword=Cmd->Password.IsSet();
90
+  GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
91 91
 
92 92
   DataIO.UnpVolume=false;
93 93
 
... ...
@@ -161,7 +161,7 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
161 161
     // This size is necessary to display the correct total progress indicator.
162 162
 
163 163
     wchar NextName[NM];
164
-    wcscpy(NextName,Arc.FileName);
164
+    wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
165 165
 
166 166
     while (true)
167 167
     {
... ...
@@ -261,15 +261,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
261 261
     if (HeaderType==HEAD_ENDARC)
262 262
       if (Arc.EndArcHead.NextVolume)
263 263
       {
264
-#ifndef NOVOLUME
264
+#ifdef NOVOLUME
265
+        return false;
266
+#else
265 267
         if (!MergeArchive(Arc,&DataIO,false,Command))
266 268
         {
267 269
           ErrHandler.SetErrorCode(RARX_WARNING);
268 270
           return false;
269 271
         }
270
-#endif
271 272
         Arc.Seek(Arc.CurBlockPos,SEEK_SET);
272 273
         return true;
274
+#endif
273 275
       }
274 276
       else
275 277
         return false;
... ...
@@ -298,7 +300,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
298 298
 
299 299
   bool EqualNames=false;
300 300
   wchar MatchedArg[NM];
301
-  int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,MatchedArg,ASIZE(MatchedArg));
301
+  int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg));
302 302
   bool MatchFound=MatchNumber!=0;
303 303
 #ifndef SFX_MODULE
304 304
   if (Cmd->ExclPath==EXCL_BASEPATH)
... ...
@@ -323,6 +325,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
323 323
 
324 324
     if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
325 325
     {
326
+      wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction".
326 327
       // If first volume name does not match the current name and if such
327 328
       // volume name really exists, let's unpack from this first volume.
328 329
       Repeat=true;
... ...
@@ -344,7 +347,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
344 344
 #endif
345 345
 
346 346
   wchar ArcFileName[NM];
347
-  ConvertPath(Arc.FileHead.FileName,ArcFileName);
347
+  ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName));
348 348
 
349 349
   if (Arc.FileHead.Version)
350 350
   {
... ...
@@ -472,7 +475,7 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
472 472
           memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 &&
473 473
           !Arc.BrokenHeader)
474 474
       {
475
-        if (GlobalPassword) // For -p<pwd> or Ctrl+P.
475
+        if (GlobalPassword) // For -p<pwd> or Ctrl+P to avoid the infinite loop.
476 476
         {
477 477
           // This message is used by Android GUI to reset cached passwords.
478 478
           // Update appropriate code if changed.
... ...
@@ -613,10 +616,14 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
613 613
       }
614 614
 #endif
615 615
 
616
-      if (!TestMode && !Arc.BrokenHeader &&
617
-          (Arc.FileHead.PackSize<<11)>Arc.FileHead.UnpSize &&
616
+      uint64 Preallocated=0;
617
+      if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 &&
618
+          Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize &&
618 619
           (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize))
620
+      {
619 621
         CurFile.Prealloc(Arc.FileHead.UnpSize);
622
+        Preallocated=Arc.FileHead.UnpSize;
623
+      }
620 624
 
621 625
       CurFile.SetAllowDelete(!Cmd->KeepBroken);
622 626
 
... ...
@@ -733,8 +740,9 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
733 733
           (!LinkEntry || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && 
734 734
           (!BrokenFile || Cmd->KeepBroken))
735 735
       {
736
-        // We could preallocate more space that really written to broken file.
737
-        if (BrokenFile)
736
+        // We could preallocate more space that really written to broken file
737
+        // or file with crafted header.
738
+        if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated))
738 739
           CurFile.Truncate();
739 740
 
740 741
 #if defined(_WIN_ALL) || defined(_EMX)
... ...
@@ -850,9 +858,12 @@ void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *De
850 850
   }
851 851
 
852 852
 #ifndef SFX_MODULE
853
-  if (Cmd->AppendArcNameToPath)
853
+  if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE)
854 854
   {
855
-    wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize);
855
+    if (Cmd->AppendArcNameToPath==APPENDARCNAME_DESTPATH)
856
+      wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize);
857
+    else
858
+      wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); // To archive own dir.
856 859
     SetExt(DestName,NULL,DestSize);
857 860
     AddEndSlash(DestName,DestSize);
858 861
   }
... ...
@@ -965,7 +976,11 @@ bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
965 965
     if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/)
966 966
     {
967 967
       // Suppress "test is ok" message if user cancelled the password prompt.
968
-      uiMsg(UIERROR_INCERRCOUNT);
968
+// 2019.03.23: If some archives are tested ok and prompt is cancelled for others,
969
+// do we really need to suppress "test is ok"? Also if we set an empty password
970
+// and "Use for all archives" in WinRAR Ctrl+P and skip some encrypted archives.
971
+// We commented out this UIERROR_INCERRCOUNT for now.
972
+//      uiMsg(UIERROR_INCERRCOUNT);
969 973
       return false;
970 974
     }
971 975
     Cmd->ManualPassword=true;
... ...
@@ -1081,7 +1096,7 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
1081 1081
   {
1082 1082
 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
1083 1083
     if (Cmd->SetCompressedAttr &&
1084
-        (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT())
1084
+        (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()!=WNT_NONE)
1085 1085
       SetFileCompression(DestFileName,true);
1086 1086
 #endif
1087 1087
     SetFileHeaderExtra(Cmd,Arc,DestFileName);
... ...
@@ -271,7 +271,7 @@ bool File::Rename(const wchar *NewName)
271 271
     Success=RenameFile(FileName,NewName);
272 272
 
273 273
   if (Success)
274
-    wcscpy(FileName,NewName);
274
+    wcsncpyz(FileName,NewName,ASIZE(FileName));
275 275
 
276 276
   return Success;
277 277
 }
... ...
@@ -99,7 +99,7 @@ class File
99 99
     void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
100 100
     static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta);
101 101
     void GetOpenFileTime(RarTime *ft);
102
-    virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;}; // 'virtual' for MultiFile class.
102
+    virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
103 103
     int64 FileLength();
104 104
     void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
105 105
     FILE_HANDLETYPE GetHandleType() {return HandleType;}
... ...
@@ -348,7 +348,7 @@ wchar *MkTemp(wchar *Name,size_t MaxSize)
348 348
     swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext);
349 349
     if (Length+wcslen(RndText)>=MaxSize || Attempt==1000)
350 350
       return NULL;
351
-    wcscpy(Name+Length,RndText);
351
+    wcsncpyz(Name+Length,RndText,MaxSize-Length);
352 352
     if (!FileExist(Name))
353 353
       break;
354 354
   }
... ...
@@ -26,7 +26,7 @@ FindFile::~FindFile()
26 26
 
27 27
 void FindFile::SetMask(const wchar *Mask)
28 28
 {
29
-  wcscpy(FindMask,Mask);
29
+  wcsncpyz(FindMask,Mask,ASIZE(FindMask));
30 30
   FirstCall=true;
31 31
 }
32 32
 
... ...
@@ -52,7 +52,7 @@ bool FindFile::Next(FindData *fd,bool GetSymLink)
52 52
     wcsncpyz(DirName,FindMask,ASIZE(DirName));
53 53
     RemoveNameFromPath(DirName);
54 54
     if (*DirName==0)
55
-      wcscpy(DirName,L".");
55
+      wcsncpyz(DirName,L".",ASIZE(DirName));
56 56
     char DirNameA[NM];
57 57
     WideToChar(DirName,DirNameA,ASIZE(DirNameA));
58 58
     if ((dirp=opendir(DirNameA))==NULL)
... ...
@@ -75,20 +75,20 @@ bool FindFile::Next(FindData *fd,bool GetSymLink)
75 75
     if (CmpName(FindMask,Name,MATCH_NAMES))
76 76
     {
77 77
       wchar FullName[NM];
78
-      wcscpy(FullName,FindMask);
78
+      wcsncpyz(FullName,FindMask,ASIZE(FullName));
79 79
       *PointToName(FullName)=0;
80 80
       if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1)
81 81
       {
82 82
         uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
83 83
         return false;
84 84
       }
85
-      wcscat(FullName,Name);
85
+      wcsncatz(FullName,Name,ASIZE(FullName));
86 86
       if (!FastFind(FullName,fd,GetSymLink))
87 87
       {
88 88
         ErrHandler.OpenErrorMsg(FullName);
89 89
         continue;
90 90
       }
91
-      wcscpy(fd->Name,FullName);
91
+      wcsncpyz(fd->Name,FullName,ASIZE(fd->Name));
92 92
       break;
93 93
     }
94 94
   }
... ...
@@ -14,10 +14,11 @@
14 14
 #define  SIZEOF_UOHEAD          18
15 15
 #define  SIZEOF_STREAMHEAD      26
16 16
 
17
-#define  VER_PACK               29
18
-#define  VER_PACK5              50 // It is stored as 0, but we subtract 50 when saving an archive.
19
-#define  VER_UNPACK             29
20
-#define  VER_UNPACK5            50 // It is stored as 0, but we add 50 when reading an archive.
17
+#define  VER_PACK               29U
18
+#define  VER_PACK5              50U // It is stored as 0, but we subtract 50 when saving an archive.
19
+#define  VER_UNPACK             29U
20
+#define  VER_UNPACK5            50U // It is stored as 0, but we add 50 when reading an archive.
21
+#define  VER_UNKNOWN          9999U // Just some large value.
21 22
 
22 23
 #define  MHD_VOLUME         0x0001U
23 24
 
... ...
@@ -174,7 +175,7 @@ struct MainHeader:BaseBlock
174 174
 struct FileHeader:BlockHeader
175 175
 {
176 176
   byte HostOS;
177
-  byte UnpVer;
177
+  uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5.
178 178
   byte Method;
179 179
   union {
180 180
     uint FileAttr;
... ...
@@ -190,7 +191,7 @@ struct FileHeader:BlockHeader
190 190
 
191 191
   int64 PackSize;
192 192
   int64 UnpSize;
193
-  int64 MaxSize; // Reserve size bytes for vint of this size.
193
+  int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size.
194 194
 
195 195
   HashValue FileHash;
196 196
 
... ...
@@ -71,6 +71,7 @@ void ListArchive(CommandData *Cmd)
71 71
         *VolNumText=0;
72 72
         while(Arc.ReadHeader()>0)
73 73
         {
74
+          Wait(); // Allow quit listing with Ctrl+C.
74 75
           HEADER_TYPE HeaderType=Arc.GetHeaderType();
75 76
           if (HeaderType==HEAD_ENDARC)
76 77
           {
... ...
@@ -91,7 +92,7 @@ void ListArchive(CommandData *Cmd)
91 91
           switch(HeaderType)
92 92
           {
93 93
             case HEAD_FILE:
94
-              FileMatched=Cmd->IsProcessFile(Arc.FileHead)!=0;
94
+              FileMatched=Cmd->IsProcessFile(Arc.FileHead,NULL,MATCH_WILDSUBPATH,0,NULL,0)!=0;
95 95
               if (FileMatched)
96 96
               {
97 97
                 ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare);
... ...
@@ -215,7 +216,7 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
215 215
 
216 216
   wchar UnpSizeText[30],PackSizeText[30];
217 217
   if (hd.UnpSize==INT64NDF)
218
-    wcscpy(UnpSizeText,L"?");
218
+    wcsncpyz(UnpSizeText,L"?",ASIZE(UnpSizeText));
219 219
   else
220 220
     itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText));
221 221
   itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText));
... ...
@@ -229,13 +230,13 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
229 229
   wchar RatioStr[10];
230 230
 
231 231
   if (hd.SplitBefore && hd.SplitAfter)
232
-    wcscpy(RatioStr,L"<->");
232
+    wcsncpyz(RatioStr,L"<->",ASIZE(RatioStr));
233 233
   else
234 234
     if (hd.SplitBefore)
235
-      wcscpy(RatioStr,L"<--");
235
+      wcsncpyz(RatioStr,L"<--",ASIZE(RatioStr));
236 236
     else
237 237
       if (hd.SplitAfter)
238
-        wcscpy(RatioStr,L"-->");
238
+        wcsncpyz(RatioStr,L"-->",ASIZE(RatioStr));
239 239
       else
240 240
         swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize));
241 241
 
... ...
@@ -344,7 +345,8 @@ void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bo
344 344
       mprintf(L"\n%12ls: %ls",St(MListHostOS),HostOS);
345 345
 
346 346
     mprintf(L"\n%12ls: RAR %ls(v%d) -m%d -md=%d%s",St(MListCompInfo),
347
-            Format==RARFMT15 ? L"3.0":L"5.0",hd.UnpVer,hd.Method,
347
+            Format==RARFMT15 ? L"3.0":L"5.0",
348
+            hd.UnpVer==VER_UNKNOWN ? 0 : hd.UnpVer,hd.Method,
348 349
             hd.WinSize>=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400,
349 350
             hd.WinSize>=0x100000 ? L"M":L"K");
350 351
 
... ...
@@ -466,7 +468,7 @@ void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSiz
466 466
               (A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-');
467 467
       break;
468 468
     case HSYS_UNKNOWN:
469
-      wcscpy(AttrStr,L"?");
469
+      wcsncpyz(AttrStr,L"?",AttrSize);
470 470
       break;
471 471
   }
472 472
 }
... ...
@@ -85,7 +85,7 @@
85 85
 #define   MCHelpSwILOG       L"\n  ilog[name]    Log errors to file"
86 86
 #define   MCHelpSwINUL       L"\n  inul          Disable all messages"
87 87
 #define   MCHelpSwIOFF       L"\n  ioff[n]       Turn PC off after completing an operation"
88
-#define   MCHelpSwISND       L"\n  isnd          Enable sound"
88
+#define   MCHelpSwISND       L"\n  isnd[-]       Control notification sounds"
89 89
 #define   MCHelpSwIVER       L"\n  iver          Display the version number"
90 90
 #define   MCHelpSwK          L"\n  k             Lock archive"
91 91
 #define   MCHelpSwKB         L"\n  kb            Keep broken extracted files"
... ...
@@ -127,11 +127,11 @@
127 127
 #define   MCHelpSwT          L"\n  t             Test files after archiving"
128 128
 #define   MCHelpSwTK         L"\n  tk            Keep original archive time"
129 129
 #define   MCHelpSwTL         L"\n  tl            Set archive time to latest file"
130
-#define   MCHelpSwTN         L"\n  tn<time>      Process files newer than <time>"
131
-#define   MCHelpSwTO         L"\n  to<time>      Process files older than <time>"
132
-#define   MCHelpSwTA         L"\n  ta<date>      Process files modified after <date> in YYYYMMDDHHMMSS format"
133
-#define   MCHelpSwTB         L"\n  tb<date>      Process files modified before <date> in YYYYMMDDHHMMSS format"
134
-#define   MCHelpSwTS         L"\n  ts[m|c|a]     Save or restore file time (modification, creation, access)"
130
+#define   MCHelpSwTN         L"\n  tn[mcao]<t>   Process files newer than <t> time"
131
+#define   MCHelpSwTO         L"\n  to[mcao]<t>   Process files older than <t> time"
132
+#define   MCHelpSwTA         L"\n  ta[mcao]<d>   Process files modified after <d> YYYYMMDDHHMMSS date"
133
+#define   MCHelpSwTB         L"\n  tb[mcao]<d>   Process files modified before <d> YYYYMMDDHHMMSS date"
134
+#define   MCHelpSwTS         L"\n  ts[m,c,a]     Save or restore file time (modification, creation, access)"
135 135
 #define   MCHelpSwU          L"\n  u             Update files"
136 136
 #define   MCHelpSwV          L"\n  v             Create volumes with size autodetection or list all volumes"
137 137
 #define   MCHelpSwVUnr       L"\n  v             List all volumes"
... ...
@@ -354,7 +354,7 @@
354 354
 #define   MCannotDelete      L"\nCannot delete %s"
355 355
 #define   MRecycleFailed     L"\nCannot move some files and folders to Recycle Bin"
356 356
 #define   MCalcCRC           L"\nCalculating the checksum"
357
-#define   MTooLargeSFXArc    L"\nWARNING: Too large SFX archive. Windows cannot run the executable file exceeding 4 GB."
357
+#define   MTooLargeSFXArc    L"\nToo large SFX archive. Windows cannot run the executable file exceeding 4 GB."
358 358
 #define   MCalcCRCAllVol     L"\nCalculating checksums of all volumes."
359 359
 #define   MNotEnoughDisk     L"\nERROR: Not enough disk space for %s."
360 360
 #define   MNewerRAR          L"\nYou may need a newer version of RAR."
... ...
@@ -25,10 +25,10 @@ bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode)
25 25
   if (CmpMode!=MATCH_NAMES)
26 26
   {
27 27
     size_t WildLength=wcslen(Wildcard);
28
-    if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH &&
28
+    if (CmpMode!=MATCH_EXACT && CmpMode!=MATCH_EXACTPATH && CmpMode!=MATCH_ALLWILD &&
29 29
         mwcsnicompc(Wildcard,Name,WildLength,ForceCase)==0)
30 30
     {
31
-      // For all modes except MATCH_NAMES, MATCH_EXACT and MATCH_EXACTPATH
31
+      // For all modes except MATCH_NAMES, MATCH_EXACT, MATCH_EXACTPATH, MATCH_ALLWILD,
32 32
       // "path1" mask must match "path1\path2\filename.ext" and "path1" names.
33 33
       wchar NextCh=Name[WildLength];
34 34
       if (NextCh==L'\\' || NextCh==L'/' || NextCh==0)
... ...
@@ -46,6 +46,8 @@ bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode)
46 46
     if ((CmpMode==MATCH_EXACT || CmpMode==MATCH_EXACTPATH) &&
47 47
         mwcsicompc(Path1,Path2,ForceCase)!=0)
48 48
       return(false);
49
+    if (CmpMode==MATCH_ALLWILD)
50
+      return match(Wildcard,Name,ForceCase);
49 51
     if (CmpMode==MATCH_SUBPATH || CmpMode==MATCH_WILDSUBPATH)
50 52
       if (IsWildcard(Path1))
51 53
         return(match(Wildcard,Name,ForceCase));
... ...
@@ -64,8 +66,8 @@ bool CmpName(const wchar *Wildcard,const wchar *Name,int CmpMode)
64 64
 
65 65
   // Always return false for RAR temporary files to exclude them
66 66
   // from archiving operations.
67
-  if (mwcsnicompc(L"__rar_",Name2,6,false)==0)
68
-    return(false);
67
+//  if (mwcsnicompc(L"__rar_",Name2,6,false)==0)
68
+//    return(false);
69 69
 
70 70
   if (CmpMode==MATCH_EXACT)
71 71
     return(mwcsicompc(Name1,Name2,ForceCase)==0);
... ...
@@ -14,6 +14,10 @@ enum {
14 14
    MATCH_EXACT,        // Paths must match exactly.
15 15
                        // Names must match exactly.
16 16
 
17
+   MATCH_ALLWILD,      // Paths and names are compared using wildcards.
18
+                       // Unlike MATCH_SUBPATH, paths do not match subdirs
19
+                       // unless a wildcard tells so.
20
+
17 21
    MATCH_EXACTPATH,    // Paths must match exactly.
18 22
                        // Names are compared using wildcards.
19 23
 
... ...
@@ -12,11 +12,7 @@ enum PATH_EXCL_MODE {
12 12
   EXCL_SKIPWHOLEPATH,  // -ep  (exclude the path completely)
13 13
   EXCL_BASEPATH,       // -ep1 (exclude the base part of path)
14 14
   EXCL_SAVEFULLPATH,   // -ep2 (the full path without the disk letter)
15
-  EXCL_ABSPATH,        // -ep3 (the full path with the disk letter)
16
-
17
-  EXCL_SKIPABSPATH     // Works as EXCL_BASEPATH for fully qualified paths
18
-                       // and as EXCL_UNCHANGED for relative paths.
19
-                       // Used by WinRAR GUI only.
15
+  EXCL_ABSPATH         // -ep3 (the full path with the disk letter)
20 16
 };
21 17
 
22 18
 enum {SOLID_NONE=0,SOLID_NORMAL=1,SOLID_COUNT=2,SOLID_FILEEXT=4,
... ...
@@ -63,11 +59,20 @@ enum SAVECOPY_MODE {
63 63
   SAVECOPY_DUPLISTEXIT
64 64
 };
65 65
 
66
+enum APPENDARCNAME_MODE
67
+{
68
+  APPENDARCNAME_NONE=0,APPENDARCNAME_DESTPATH,APPENDARCNAME_OWNDIR
69
+};
70
+
66 71
 enum POWER_MODE {
67 72
   POWERMODE_KEEP=0,POWERMODE_OFF,POWERMODE_HIBERNATE,POWERMODE_SLEEP,
68 73
   POWERMODE_RESTART
69 74
 };
70 75
 
76
+
77
+// Need "forced off" state to turn off sound in GUI command line.
78
+enum SOUND_NOTIFY_MODE {SOUND_NOTIFY_DEFAULT=0,SOUND_NOTIFY_ON,SOUND_NOTIFY_OFF};
79
+
71 80
 struct FilterMode
72 81
 {
73 82
   FilterState State;
... ...
@@ -112,7 +117,7 @@ class RAROptions
112 112
 
113 113
     wchar LogName[NM];
114 114
     MESSAGE_TYPE MsgStream;
115
-    bool Sound;
115
+    SOUND_NOTIFY_MODE Sound;
116 116
     OVERWRITE_MODE Overwrite;
117 117
     int Method;
118 118
     HASH_TYPE HashType;
... ...
@@ -163,8 +168,10 @@ class RAROptions
163 163
     bool SaveStreams;
164 164
     bool SetCompressedAttr;
165 165
     bool IgnoreGeneralAttr;
166
-    RarTime FileTimeBefore;
167
-    RarTime FileTimeAfter;
166
+    RarTime FileMtimeBefore,FileCtimeBefore,FileAtimeBefore;
167
+    bool FileMtimeBeforeOR,FileCtimeBeforeOR,FileAtimeBeforeOR;
168
+    RarTime FileMtimeAfter,FileCtimeAfter,FileAtimeAfter;
169
+    bool FileMtimeAfterOR,FileCtimeAfterOR,FileAtimeAfterOR;
168 170
     int64 FileSizeLess;
169 171
     int64 FileSizeMore;
170 172
     bool Lock;
... ...
@@ -173,9 +180,9 @@ class RAROptions
173 173
     FilterMode FilterModes[MAX_FILTER_TYPES];
174 174
     wchar EmailTo[NM];
175 175
     uint VersionControl;
176
-    bool AppendArcNameToPath;
176
+    APPENDARCNAME_MODE AppendArcNameToPath;
177 177
     POWER_MODE Shutdown;
178
-    EXTTIME_MODE xmtime;
178
+    EXTTIME_MODE xmtime; // Extended time modes (time precision to store).
179 179
     EXTTIME_MODE xctime;
180 180
     EXTTIME_MODE xatime;
181 181
     wchar CompressStdin[NM];
... ...
@@ -32,7 +32,12 @@
32 32
 #define STRICT 1
33 33
 #endif
34 34
 
35
+// 'ifndef' check here is needed for unrar.dll header to avoid macro
36
+// re-definition warnings in third party projects.
37
+#ifndef UNICODE
35 38
 #define UNICODE
39
+#endif
40
+
36 41
 #undef WINVER
37 42
 #undef _WIN32_WINNT
38 43
 #define WINVER 0x0501
... ...
@@ -16,7 +16,7 @@ wchar* PointToLastChar(const wchar *Path)
16 16
 }
17 17
 
18 18
 
19
-wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath)
19
+wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize)
20 20
 {
21 21
   const wchar *DestPtr=SrcPath;
22 22
 
... ...
@@ -25,7 +25,7 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath)
25 25
     if (IsPathDiv(s[0]) && s[1]=='.' && s[2]=='.' && IsPathDiv(s[3]))
26 26
       DestPtr=s+4;
27 27
 
28
-  // Remove <d>:\ and any sequence of . and \ in the beginning of path string.
28
+  // Remove any amount of <d>:\ and any sequence of . and \ in the beginning of path string.
29 29
   while (*DestPtr!=0)
30 30
   {
31 31
     const wchar *s=DestPtr;
... ...
@@ -58,7 +58,7 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath)
58 58
     // so we use the temporary buffer for copying.
59 59
     wchar TmpStr[NM];
60 60
     wcsncpyz(TmpStr,DestPtr,ASIZE(TmpStr));
61
-    wcscpy(DestPath,TmpStr);
61
+    wcsncpyz(DestPath,TmpStr,DestSize);
62 62
   }
63 63
   return (wchar *)DestPtr;
64 64
 }
... ...
@@ -120,7 +120,14 @@ bool CmpExt(const wchar *Name,const wchar *Ext)
120 120
 
121 121
 bool IsWildcard(const wchar *Str)
122 122
 {
123
-  return Str==NULL ? false:wcspbrk(Str,L"*?")!=NULL;
123
+  if (Str==NULL)
124
+    return false;
125
+#ifdef _WIN_ALL
126
+  // Not treat the special NTFS \\?\d: path prefix as a wildcard.
127
+  if (Str[0]=='\\' && Str[1]=='\\' && Str[2]=='?' && Str[3]=='\\')
128
+    Str+=4;
129
+#endif
130
+  return wcspbrk(Str,L"*?")!=NULL;
124 131
 }
125 132
 
126 133
 
... ...
@@ -163,8 +170,8 @@ int GetPathDisk(const wchar *Path)
163 163
 void AddEndSlash(wchar *Path,size_t MaxLength)
164 164
 {
165 165
   size_t Length=wcslen(Path);
166
-  if (Length>0 && Path[Length-1]!=CPATHDIVIDER && Length+1<MaxLength)
167
-    wcscat(Path,SPATHDIVIDER);
166
+  if (Length>0 && Path[Length-1]!=CPATHDIVIDER)
167
+    wcsncatz(Path,SPATHDIVIDER,MaxLength);
168 168
 }
169 169
 
170 170
 
... ...
@@ -303,9 +310,13 @@ void GetConfigName(const wchar *Name,wchar *FullName,size_t MaxSize,bool CheckEx
303 303
 #endif
304 304
 
305 305
 
306
-// Returns a pointer to rightmost digit of volume number.
306
+// Returns a pointer to rightmost digit of volume number or to beginning
307
+// of file name if numeric part is missing.
307 308
 wchar* GetVolNumPart(const wchar *ArcName)
308 309
 {
310
+  if (*ArcName==0)
311
+    return (wchar *)ArcName;
312
+
309 313
   // Pointing to last name character.
310 314
   const wchar *ChPtr=ArcName+wcslen(ArcName)-1;
311 315
 
... ...
@@ -346,18 +357,33 @@ void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering)
346 346
     ChPtr=GetExt(ArcName);
347 347
   }
348 348
   else
349
-    if (ChPtr[1]==0 && wcslen(ArcName)<MaxLength-3 || wcsicomp(ChPtr+1,L"exe")==0 || wcsicomp(ChPtr+1,L"sfx")==0)
350
-      wcscpy(ChPtr+1,L"rar");
349
+    if (ChPtr[1]==0 || wcsicomp(ChPtr,L".exe")==0 || wcsicomp(ChPtr,L".sfx")==0)
350
+      wcsncpyz(ChPtr,L".rar",MaxLength-(ChPtr-ArcName));
351
+
352
+  if (ChPtr==NULL || *ChPtr!='.' || ChPtr[1]==0)
353
+  {
354
+    // Normally we shall have some extension here. If we don't, it means
355
+    // the name has no extension and buffer has no free space to append one.
356
+    // Let's clear the name to prevent a new call with same name and return.
357
+    *ArcName=0;
358
+    return;
359
+  }
360
+
351 361
   if (!OldNumbering)
352 362
   {
353 363
     ChPtr=GetVolNumPart(ArcName);
354 364
 
365
+    // We should not check for IsDigit(*ChPtr) here and should increment
366
+    // even non-digits. If we got a corrupt archive with volume flag,
367
+    // but without numeric part, we still need to modify its name somehow,
368
+    // so while (exist(name)) {NextVolumeName()} loops do not run infinitely.
355 369
     while ((++(*ChPtr))=='9'+1)
356 370
     {
357 371
       *ChPtr='0';
358 372
       ChPtr--;
359 373
       if (ChPtr<ArcName || !IsDigit(*ChPtr))
360 374
       {
375
+        // Convert .part:.rar (.part9.rar after increment) to part10.rar.
361 376
         for (wchar *EndPtr=ArcName+wcslen(ArcName);EndPtr!=ChPtr;EndPtr--)
362 377
           *(EndPtr+1)=*EndPtr;
363 378
         *(ChPtr+1)='1';
... ...
@@ -366,15 +392,15 @@ void NextVolumeName(wchar *ArcName,uint MaxLength,bool OldNumbering)
366 366
     }
367 367
   }
368 368
   else
369
-    if (!IsDigit(*(ChPtr+2)) || !IsDigit(*(ChPtr+3)))
370
-      wcscpy(ChPtr+2,L"00");
369
+    if (!IsDigit(ChPtr[2]) || !IsDigit(ChPtr[3]))
370
+      wcsncpyz(ChPtr+2,L"00",MaxLength-(ChPtr-ArcName)-2); // From .rar to .r00.
371 371
     else
372 372
     {
373
-      ChPtr+=3;
374
-      while ((++(*ChPtr))=='9'+1)
375
-        if (*(ChPtr-1)=='.')
373
+      ChPtr+=wcslen(ChPtr)-1; // Set to last character.
374
+      while (++(*ChPtr)=='9'+1)
375
+        if (ChPtr<=ArcName || *(ChPtr-1)=='.')
376 376
         {
377
-          *ChPtr='A';
377
+          *ChPtr='a'; // From .999 to .a00 if started from .001 or for too short names.
378 378
           break;
379 379
         }
380 380
         else
... ...
@@ -585,8 +611,7 @@ int ParseVersionFileName(wchar *Name,bool Truncate)
585 585
   wchar *VerText=wcsrchr(Name,';');
586 586
   if (VerText!=NULL)
587 587
   {
588
-    if (Version==0)
589
-      Version=atoiw(VerText+1);
588
+    Version=atoiw(VerText+1);
590 589
     if (Truncate)
591 590
       *VerText=0;
592 591
   }
... ...
@@ -652,7 +677,7 @@ wchar* VolNameToFirstName(const wchar *VolName,wchar *FirstName,size_t MaxSize,b
652 652
 
653 653
 
654 654
 #ifndef SFX_MODULE
655
-static void GenArcName(wchar *ArcName,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent)
655
+static void GenArcName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,uint ArcNumber,bool &ArcNumPresent)
656 656
 {
657 657
   bool Prefix=false;
658 658
   if (*GenerateMask=='+')
... ...
@@ -713,7 +738,7 @@ static void GenArcName(wchar *ArcName,const wchar *GenerateMask,uint ArcNumber,b
713 713
   wchar Ext[NM],*Dot=GetExt(ArcName);
714 714
   *Ext=0;
715 715
   if (Dot==NULL)
716
-    wcscpy(Ext,*PointToName(ArcName)==0 ? L".rar":L"");
716
+    wcsncpyz(Ext,*PointToName(ArcName)==0 ? L".rar":L"",ASIZE(Ext));
717 717
   else
718 718
   {
719 719
     wcsncpyz(Ext,Dot,ASIZE(Ext));
... ...
@@ -749,7 +774,7 @@ static void GenArcName(wchar *ArcName,const wchar *GenerateMask,uint ArcNumber,b
749 749
   int CField[sizeof(Field)/sizeof(Field[0])];
750 750
   memset(CField,0,sizeof(CField));
751 751
   QuoteMode=false;
752
-  for (int I=0;Mask[I]!=0;I++)
752
+  for (uint I=0;Mask[I]!=0;I++)
753 753
   {
754 754
     if (Mask[I]=='{' || Mask[I]=='}')
755 755
     {
... ...
@@ -810,21 +835,17 @@ static void GenArcName(wchar *ArcName,const wchar *GenerateMask,uint ArcNumber,b
810 810
     AddEndSlash(NewName,ASIZE(NewName));
811 811
     wcsncatz(NewName,DateText,ASIZE(NewName));
812 812
     wcsncatz(NewName,PointToName(ArcName),ASIZE(NewName));
813
-    wcscpy(ArcName,NewName);
813
+    wcsncpyz(ArcName,NewName,MaxSize);
814 814
   }
815 815
   else
816
-    wcscat(ArcName,DateText);
817
-  wcscat(ArcName,Ext);
816
+    wcsncatz(ArcName,DateText,MaxSize);
817
+  wcsncatz(ArcName,Ext,MaxSize);
818 818
 }
819 819
 
820 820
 
821 821
 void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask,bool Archiving)
822 822
 {
823
-  // Must be enough space for archive name plus all stuff in mask plus
824
-  // extra overhead produced by mask 'N' (archive number) characters.
825
-  // One 'N' character can result in several numbers if we process more
826
-  // than 9 archives.
827
-  wchar NewName[NM+MAX_GENERATE_MASK+20];
823
+  wchar NewName[NM];
828 824
 
829 825
   uint ArcNumber=1;
830 826
   while (true) // Loop for 'N' (archive number) processing.
... ...
@@ -833,7 +854,7 @@ void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask
833 833
     
834 834
     bool ArcNumPresent=false;
835 835
 
836
-    GenArcName(NewName,GenerateMask,ArcNumber,ArcNumPresent);
836
+    GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber,ArcNumPresent);
837 837
     
838 838
     if (!ArcNumPresent)
839 839
       break;
... ...
@@ -845,7 +866,7 @@ void GenerateArchiveName(wchar *ArcName,size_t MaxSize,const wchar *GenerateMask
845 845
         // existing archive before the first unused name. So we generate
846 846
         // the name for (ArcNumber-1) below.
847 847
         wcsncpyz(NewName,NullToEmpty(ArcName),ASIZE(NewName));
848
-        GenArcName(NewName,GenerateMask,ArcNumber-1,ArcNumPresent);
848
+        GenArcName(NewName,ASIZE(NewName),GenerateMask,ArcNumber-1,ArcNumPresent);
849 849
       }
850 850
       break;
851 851
     }
... ...
@@ -895,8 +916,8 @@ bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize)
895 895
     {
896 896
       if (MaxSize<=PrefixLength+SrcLength)
897 897
         return false;
898
-      wcsncpy(Dest,Prefix,PrefixLength);
899
-      wcscpy(Dest+PrefixLength,Src);
898
+      wcsncpyz(Dest,Prefix,MaxSize);
899
+      wcsncatz(Dest,Src,MaxSize); // "\\?\D:\very long path".
900 900
       return true;
901 901
     }
902 902
     else
... ...
@@ -904,9 +925,9 @@ bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize)
904 904
       {
905 905
         if (MaxSize<=PrefixLength+SrcLength+2)
906 906
           return false;
907
-        wcsncpy(Dest,Prefix,PrefixLength);
908
-        wcscpy(Dest+PrefixLength,L"UNC");
909
-        wcscpy(Dest+PrefixLength+3,Src+1);
907
+        wcsncpyz(Dest,Prefix,MaxSize);
908
+        wcsncatz(Dest,L"UNC",MaxSize);
909
+        wcsncatz(Dest,Src+1,MaxSize); // "\\?\UNC\server\share".
910 910
         return true;
911 911
       }
912 912
     // We may be here only if we modify IsFullPath in the future.
... ...
@@ -923,9 +944,10 @@ bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize)
923 923
     {
924 924
       if (MaxSize<=PrefixLength+SrcLength+2)
925 925
         return false;
926
-      wcsncpy(Dest,Prefix,PrefixLength);
927
-      wcsncpy(Dest+PrefixLength,CurDir,2); // Copy drive letter 'd:'.
928
-      wcscpy(Dest+PrefixLength+2,Src);
926
+      wcsncpyz(Dest,Prefix,MaxSize);
927
+      CurDir[2]=0;
928
+      wcsncatz(Dest,CurDir,MaxSize); // Copy drive letter 'd:'.
929
+      wcsncatz(Dest,Src,MaxSize);
929 930
       return true;
930 931
     }
931 932
     else  // Paths in path\name format.
... ...
@@ -933,8 +955,8 @@ bool GetWinLongPath(const wchar *Src,wchar *Dest,size_t MaxSize)
933 933
       AddEndSlash(CurDir,ASIZE(CurDir));
934 934
       if (MaxSize<=PrefixLength+wcslen(CurDir)+SrcLength)
935 935
         return false;
936
-      wcsncpy(Dest,Prefix,PrefixLength);
937
-      wcscpy(Dest+PrefixLength,CurDir);
936
+      wcsncpyz(Dest,Prefix,MaxSize);
937
+      wcsncatz(Dest,CurDir,MaxSize);
938 938
 
939 939
       if (Src[0]=='.' && IsPathDiv(Src[1])) // Remove leading .\ in pathname.
940 940
         Src+=2;
... ...
@@ -3,7 +3,7 @@
3 3
 
4 4
 wchar* PointToName(const wchar *Path);
5 5
 wchar* PointToLastChar(const wchar *Path);
6
-wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath);
6
+wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize);
7 7
 void SetName(wchar *FullName,const wchar *Name,size_t MaxSize);
8 8
 void SetExt(wchar *Name,const wchar *NewExt,size_t MaxSize);
9 9
 void SetSFXExt(wchar *SFXName,size_t MaxSize);
... ...
@@ -84,7 +84,7 @@ void QuickOpen::Load(uint64 BlockPos)
84 84
     if (ReadSize==0 || Arc->GetHeaderType()!=HEAD_SERVICE ||
85 85
         !Arc->SubHead.CmpName(SUBHEAD_TYPE_QOPEN))
86 86
       return;
87
-    QLHeaderPos=Arc->CurBlockPos;
87
+    QOHeaderPos=Arc->CurBlockPos;
88 88
     RawDataStart=Arc->Tell();
89 89
     RawDataSize=Arc->SubHead.UnpSize;
90 90
 
... ...
@@ -172,7 +172,7 @@ bool QuickOpen::Seek(int64 Offset,int Method)
172 172
   // archive updating involve several passes. So if we detect that file
173 173
   // pointer is moved back, we reload quick open data from beginning.
174 174
   if (Method==SEEK_SET && (uint64)Offset<SeekPos && (uint64)Offset<LastReadHeaderPos)
175
-    Load(QLHeaderPos);
175
+    Load(QOHeaderPos);
176 176
 
177 177
   if (Method==SEEK_SET)
178 178
     SeekPos=Offset;
... ...
@@ -250,10 +250,10 @@ bool QuickOpen::ReadRaw(RawRead &Raw)
250 250
     return false;
251 251
   }
252 252
 
253
-  // If rest of block data crosses buffer boundary, read it in loop.
254
-  size_t DataLeft=ReadBufSize-ReadBufPos;
253
+  // If rest of block data crosses Buf boundary, read it in loop.
255 254
   while (SizeToRead>0)
256 255
   {
256
+    size_t DataLeft=ReadBufSize-ReadBufPos;
257 257
     size_t CurSizeToRead=Min(DataLeft,(size_t)SizeToRead);
258 258
     Raw.Read(Buf+ReadBufPos,CurSizeToRead);
259 259
     ReadBufPos+=CurSizeToRead;
... ...
@@ -285,6 +285,6 @@ bool QuickOpen::ReadNext()
285 285
   LastReadHeader.Alloc(HeaderSize);
286 286
   Raw.GetB(&LastReadHeader[0],HeaderSize);
287 287
   // Calculate the absolute position as offset from quick open service header.
288
-  LastReadHeaderPos=QLHeaderPos-Offset;
288
+  LastReadHeaderPos=QOHeaderPos-Offset;
289 289
   return true;
290 290
 }
... ...
@@ -29,24 +29,24 @@ class QuickOpen
29 29
     QuickOpenItem *ListStart;
30 30
     QuickOpenItem *ListEnd;
31 31
     
32
-    byte *Buf;
33
-    static const size_t MaxBufSize=0x10000; // Must be multiple of CRYPT_BLOCK_SIZE.
34
-    size_t CurBufSize;
32
+    byte *Buf; // Read quick open data here.
33
+    static const size_t MaxBufSize=0x10000; // Buf size, must be multiple of CRYPT_BLOCK_SIZE.
34
+    size_t CurBufSize; // Current size of buffered data in write mode.
35 35
 #ifndef RAR_NOCRYPT // For shell extension.
36 36
     CryptData Crypt;
37 37
 #endif
38 38
 
39 39
     bool Loaded;
40
-    uint64 QLHeaderPos;
41
-    uint64 RawDataStart;
42
-    uint64 RawDataSize;
43
-    uint64 RawDataPos;
44
-    size_t ReadBufSize;
45
-    size_t ReadBufPos;
40
+    uint64 QOHeaderPos;  // Main QO header position.
41
+    uint64 RawDataStart; // Start of QO data, just after the main header.
42
+    uint64 RawDataSize;  // Size of entire QO data.
43
+    uint64 RawDataPos;   // Current read position in QO data.
44
+    size_t ReadBufSize;  // Size of Buf data currently read from QO.
45
+    size_t ReadBufPos;   // Current read position in Buf data.
46 46
     Array<byte> LastReadHeader;
47 47
     uint64 LastReadHeaderPos;
48 48
     uint64 SeekPos;
49
-    bool UnsyncSeekPos; // QOpen SeekPos does not match an actual file pointer.
49
+    bool UnsyncSeekPos;  // QOpen SeekPos does not match an actual file pointer.
50 50
   public:
51 51
     QuickOpen();
52 52
     ~QuickOpen();
... ...
@@ -37,7 +37,7 @@ int main(int argc, char *argv[])
37 37
   
38 38
     CommandData *Cmd=new CommandData;
39 39
 #ifdef SFX_MODULE
40
-    wcscpy(Cmd->Command,L"X");
40
+    wcsncpyz(Cmd->Command,L"X",ASIZE(Cmd->Command));
41 41
     char *Switch=argc>1 ? argv[1]:NULL;
42 42
     if (Switch!=NULL && Cmd->IsSwitch(Switch[0]))
43 43
     {
... ...
@@ -68,6 +68,8 @@ int main(int argc, char *argv[])
68 68
 
69 69
 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
70 70
     ShutdownOnClose=Cmd->Shutdown;
71
+    if (ShutdownOnClose)
72
+      ShutdownCheckAnother(true);
71 73
 #endif
72 74
 
73 75
     uiInit(Cmd->Sound);
... ...
@@ -93,7 +95,8 @@ int main(int argc, char *argv[])
93 93
   }
94 94
 
95 95
 #if defined(_WIN_ALL) && !defined(SFX_MODULE)
96
-  if (ShutdownOnClose!=POWERMODE_KEEP && ErrHandler.IsShutdownEnabled())
96
+  if (ShutdownOnClose!=POWERMODE_KEEP && ErrHandler.IsShutdownEnabled() &&
97
+      !ShutdownCheckAnother(false))
97 98
     Shutdown(ShutdownOnClose);
98 99
 #endif
99 100
   ErrHandler.MainExit=true;
... ...
@@ -25,6 +25,7 @@ void ComprDataIO::Init()
25 25
   NextVolumeMissing=false;
26 26
   SrcFile=NULL;
27 27
   DestFile=NULL;
28
+  UnpWrAddr=NULL;
28 29
   UnpWrSize=0;
29 30
   Command=NULL;
30 31
   Encryption=false;
... ...
@@ -111,7 +111,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
111 111
     NewStyle=IsNewStyleRev(ArcName);
112 112
     while (Ext>ArcName+1 && (IsDigit(*(Ext-1)) || *(Ext-1)=='_'))
113 113
       Ext--;
114
-    wcscpy(Ext,L"*.*");
114
+    wcsncpyz(Ext,L"*.*",ASIZE(ArcName)-(Ext-ArcName));
115 115
     
116 116
     FindFile Find;
117 117
     Find.SetMask(ArcName);
... ...
@@ -235,7 +235,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
235 235
     }
236 236
     RecVolNumber=P[1];
237 237
     FileNumber=P[2];
238
-    wcscpy(PrevName,CurName);
238
+    wcsncpyz(PrevName,CurName,ASIZE(PrevName));
239 239
     File *NewFile=new File;
240 240
     NewFile->TOpen(CurName);
241 241
     SrcFile[FileNumber+P[0]-1]=NewFile;
... ...
@@ -247,7 +247,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
247 247
   if (!Silent || FoundRecVolumes!=0)
248 248
     uiMsg(UIMSG_RECVOLFOUND,FoundRecVolumes);
249 249
   if (FoundRecVolumes==0)
250
-    return(false);
250
+    return false;
251 251
 
252 252
   bool WriteFlags[256];
253 253
   memset(WriteFlags,0,sizeof(WriteFlags));
... ...
@@ -290,8 +290,8 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
290 290
       {
291 291
         NewFile->Close();
292 292
         wchar NewName[NM];
293
-        wcscpy(NewName,ArcName);
294
-        wcscat(NewName,L".bad");
293
+        wcsncpyz(NewName,ArcName,ASIZE(NewName));
294
+        wcsncatz(NewName,L".bad",ASIZE(NewName));
295 295
 
296 296
         uiMsg(UIMSG_BADARCHIVE,ArcName);
297 297
         uiMsg(UIMSG_RENAMING,ArcName,NewName);
... ...
@@ -322,7 +322,7 @@ bool RecVolumes3::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
322 322
       MissingVolumes++;
323 323
 
324 324
       if (CurArcNum==FileNumber-1)
325
-        wcscpy(LastVolName,ArcName);
325
+        wcsncpyz(LastVolName,ArcName,ASIZE(LastVolName));
326 326
 
327 327
       uiMsg(UIMSG_MISSINGVOL,ArcName);
328 328
       uiMsg(UIEVENT_NEWARCHIVE,ArcName);
... ...
@@ -139,12 +139,10 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
139 139
   wcsncpyz(ArcName,Name,ASIZE(ArcName));
140 140
 
141 141
   wchar *Num=GetVolNumPart(ArcName);
142
-  if (Num==ArcName)
143
-    return false; // Number part is missing in the name.
144 142
   while (Num>ArcName && IsDigit(*(Num-1)))
145 143
     Num--;
146 144
   if (Num==ArcName)
147
-    return false; // Entire volume name is numeric, not possible for REV file.
145
+    return false; // Numeric part is missing or entire volume name is numeric, not possible for RAR or REV volume.
148 146
   wcsncpyz(Num,L"*.*",ASIZE(ArcName)-(Num-ArcName));
149 147
   
150 148
   wchar FirstVolName[NM];
... ...
@@ -289,8 +287,8 @@ bool RecVolumes5::Restore(RAROptions *Cmd,const wchar *Name,bool Silent)
289 289
       Item->f->Close();
290 290
 
291 291
       wchar NewName[NM];
292
-      wcscpy(NewName,Item->Name);
293
-      wcscat(NewName,L".bad");
292
+      wcsncpyz(NewName,Item->Name,ASIZE(NewName));
293
+      wcsncatz(NewName,L".bad",ASIZE(NewName));
294 294
 
295 295
       uiMsg(UIMSG_BADARCHIVE,Item->Name);
296 296
       uiMsg(UIMSG_RENAMING,Item->Name,NewName);
... ...
@@ -79,7 +79,10 @@ void Rijndael::Init(bool Encrypt,const byte *key,uint keyLen,const byte * initVe
79 79
   AES_NI=(CPUInfo[2] & 0x2000000)!=0;
80 80
 #endif
81 81
 
82
-  uint uKeyLenInBytes;
82
+  // Other developers asked us to initialize it to suppress "may be used
83
+  // uninitialized" warning in code below in some compilers.
84
+  uint uKeyLenInBytes=0;
85
+
83 86
   switch(keyLen)
84 87
   {
85 88
     case 128:
... ...
@@ -27,7 +27,7 @@ RSCoder16::~RSCoder16()
27 27
   delete[] MX;
28 28
   delete[] ValidFlags;
29 29
 }
30
-    
30
+
31 31
 
32 32
 // Initialize logarithms and exponents Galois field tables.
33 33
 void RSCoder16::gfInit()
... ...
@@ -41,7 +41,7 @@ void RSCoder16::gfInit()
41 41
     gfExp[L]=E;
42 42
     gfExp[L+gfSize]=E;  // Duplicate the table to avoid gfExp overflow check.
43 43
     E<<=1;
44
-    if (E>gfSize) 
44
+    if (E>gfSize)
45 45
       E^=0x1100B; // Irreducible field-generator polynomial.
46 46
   }
47 47
 
... ...
@@ -59,7 +59,7 @@ uint RSCoder16::gfAdd(uint a,uint b) // Addition in Galois field.
59 59
 }
60 60
 
61 61
 
62
-uint RSCoder16::gfMul(uint a,uint b) // Multiplication in Galois field. 
62
+uint RSCoder16::gfMul(uint a,uint b) // Multiplication in Galois field.
63 63
 {
64 64
   return gfExp[gfLog[a]+gfLog[b]];
65 65
 }
... ...
@@ -156,7 +156,7 @@ void RSCoder16::InvertDecoderMatrix()
156 156
   for (uint Kr = 0, Kf = 0; Kr < NE; Kr++, Kf++)
157 157
   {
158 158
     while (ValidFlags[Kf]) // Skip trivial rows.
159
-      Kf++;                 
159
+      Kf++;
160 160
     MI[Kr * ND + Kf] = 1;  // Set diagonal 1.
161 161
   }
162 162
 
... ...
@@ -174,7 +174,7 @@ void RSCoder16::InvertDecoderMatrix()
174 174
       // after MI[..]^=, but we do not need it for matrix inversion.
175 175
       for (uint I = 0; I < NE; I++)
176 176
         MI[I * ND + Kf] ^= MX[I * ND + Kf];
177
-      Kf++;                 
177
+      Kf++;
178 178
     }
179 179
 
180 180
     if (Kf == ND)
... ...
@@ -186,14 +186,14 @@ void RSCoder16::InvertDecoderMatrix()
186 186
     uint PInv = gfInv( MXk[Kf] ); // Pivot inverse.
187 187
     // Divide the pivot row by pivot, so pivot cell contains 1.
188 188
     for (uint I = 0; I < ND; I++)
189
-    { 
189
+    {
190 190
       MXk[I] = gfMul( MXk[I], PInv );
191 191
       MIk[I] = gfMul( MIk[I], PInv );
192 192
     }
193 193
 
194 194
     for (uint I = 0; I < NE; I++)
195 195
       if (I != Kr) // For all rows except containing the pivot cell.
196
-      { 
196
+      {
197 197
         // Apply Gaussian elimination Mij -= Mkj * Mik / pivot.
198 198
         // Since pivot is already 1, it is reduced to Mij -= Mkj * Mik.
199 199
         uint *MXi = MX + I * ND; // i-th row of main matrix.
... ...
@@ -361,7 +361,7 @@ bool RSCoder16::SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte
361 361
     __m128i LowBytes1=_mm_and_si128(D[1],LowByteMask);
362 362
     __m128i HighBytes=_mm_packus_epi16(HighBytes0,HighBytes1);
363 363
     __m128i LowBytes=_mm_packus_epi16(LowBytes0,LowBytes1);
364
-    
364
+
365 365
     // Multiply bits 0..3 of low bytes. Store low and high product bytes
366 366
     // separately in cumulative sum variables.
367 367
     __m128i LowBytesLow4=_mm_and_si128(LowBytes,Low4Mask);
... ...
@@ -377,7 +377,7 @@ bool RSCoder16::SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte
377 377
     // Add new product to existing sum, low and high bytes separately.
378 378
     LowBytesMultSum=_mm_xor_si128(LowBytesMultSum,LowBytesHigh4MultLow);
379 379
     HighBytesMultSum=_mm_xor_si128(HighBytesMultSum,LowBytesHigh4MultHigh);
380
-    
380
+
381 381
     // Multiply bits 0..3 of high bytes. Store low and high product bytes separately.
382 382
     __m128i HighBytesLow4=_mm_and_si128(HighBytes,Low4Mask);
383 383
     __m128i HighBytesLow4MultLow=_mm_shuffle_epi8(T2L,HighBytesLow4);
... ...
@@ -413,7 +413,7 @@ bool RSCoder16::SSE_UpdateECC(uint DataNum, uint ECCNum, const byte *Data, byte
413 413
   // because Data and ECC can have different alignment offsets.
414 414
   for (; Pos<BlockSize; Pos+=2)
415 415
     *(ushort*)(ECC+Pos) ^= gfMul( M, *(ushort*)(Data+Pos) );
416
-  
416
+
417 417
   return true;
418 418
 }
419 419
 #endif
... ...
@@ -142,7 +142,12 @@ bool ScanTree::GetFilteredMask()
142 142
   bool WildcardFound=false;
143 143
   uint FolderWildcardCount=0;
144 144
   uint SlashPos=0;
145
-  for (int I=0;CurMask[I]!=0;I++)
145
+  uint StartPos=0;
146
+#ifdef _WIN_ALL // Not treat the special NTFS \\?\d: path prefix as a wildcard.
147
+  if (CurMask[0]=='\\' && CurMask[1]=='\\' && CurMask[2]=='?' && CurMask[3]=='\\')
148
+    StartPos=4;
149
+#endif
150
+  for (uint I=StartPos;CurMask[I]!=0;I++)
146 151
   {
147 152
     if (CurMask[I]=='?' || CurMask[I]=='*')
148 153
       WildcardFound=true;
... ...
@@ -171,7 +176,7 @@ bool ScanTree::GetFilteredMask()
171 171
 
172 172
   wchar Filter[NM];
173 173
   // Convert path\dir*\ to *\dir filter to search for 'dir' in all 'path' subfolders.
174
-  wcscpy(Filter,L"*");
174
+  wcsncpyz(Filter,L"*",ASIZE(Filter));
175 175
   AddEndSlash(Filter,ASIZE(Filter));
176 176
   // SlashPos might point or not point to path separator for masks like 'dir*', '\dir*' or 'd:dir*'
177 177
   wchar *WildName=IsPathDiv(CurMask[SlashPos]) || IsDriveDiv(CurMask[SlashPos]) ? CurMask+SlashPos+1 : CurMask+SlashPos;
... ...
@@ -360,7 +365,7 @@ SCAN_CODE ScanTree::FindProc(FindData *FD)
360 360
         wcsncpyz(CurMask,Mask+1,ASIZE(CurMask));
361 361
       else
362 362
       {
363
-        *(PrevSlash+1)=0;
363
+        *PrevSlash=0;
364 364
         wcsncatz(CurMask,Mask,ASIZE(CurMask));
365 365
       }
366 366
     }
... ...
@@ -64,7 +64,7 @@ class ScanTree
64 64
     ScanTree(StringList *FileMasks,RECURSE_MODE Recurse,bool GetLinks,SCAN_DIRS GetDirs);
65 65
     ~ScanTree();
66 66
     SCAN_CODE GetNext(FindData *FindData);
67
-    size_t GetSpecPathLength() {return SpecPathLength;};
67
+    size_t GetSpecPathLength() {return SpecPathLength;}
68 68
     int GetErrors() {return Errors;};
69 69
     void SetErrArcName(const wchar *Name) {wcsncpyz(ErrArcName,Name,ASIZE(ErrArcName));}
70 70
     void SetCommandData(CommandData *Cmd) {ScanTree::Cmd=Cmd;}
... ...
@@ -281,53 +281,49 @@ int wcsnicompc(const wchar *s1,const wchar *s2,size_t n)
281 281
 }
282 282
 
283 283
 
284
-// Safe strncpy: copies maxlen-1 max and always returns zero terminated dest.
285
-char* strncpyz(char *dest, const char *src, size_t maxlen)
284
+// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest.
285
+void strncpyz(char *dest, const char *src, size_t maxlen)
286 286
 {
287 287
   if (maxlen>0)
288 288
   {
289
-    strncpy(dest,src,maxlen-1);
290
-    dest[maxlen-1]=0;
289
+    while (--maxlen>0 && *src!=0)
290
+      *dest++=*src++;
291
+    *dest=0;
291 292
   }
292
-  return dest;
293 293
 }
294 294
 
295 295
 
296
-// Safe wcsncpy: copies maxlen-1 max and always returns zero terminated dest.
297
-wchar* wcsncpyz(wchar *dest, const wchar *src, size_t maxlen)
296
+// Safe copy: copies maxlen-1 max and for maxlen>0 returns zero terminated dest.
297
+void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen)
298 298
 {
299 299
   if (maxlen>0)
300 300
   {
301
-    wcsncpy(dest,src,maxlen-1);
302
-    dest[maxlen-1]=0;
301
+    while (--maxlen>0 && *src!=0)
302
+      *dest++=*src++;
303
+    *dest=0;
303 304
   }
304
-  return dest;
305 305
 }
306 306
 
307 307
 
308
-// Safe strncat: resulting dest length cannot exceed maxlen and dest 
309
-// is always zero terminated. Note that 'maxlen' parameter defines the entire
310
-// dest buffer size and is not compatible with standard strncat.
311
-char* strncatz(char* dest, const char* src, size_t maxlen)
308
+// Safe append: resulting dest length cannot exceed maxlen and dest 
309
+// is always zero terminated. 'maxlen' parameter defines the entire
310
+// dest buffer size and is not compatible with wcsncat.
311
+void strncatz(char* dest, const char* src, size_t maxlen)
312 312
 {
313
-  size_t Length = strlen(dest);
314
-  int avail=int(maxlen - Length - 1);
315
-  if (avail > 0)
316
-    strncat(dest, src, avail);
317
-  return dest;
313
+  size_t length = strlen(dest);
314
+  if (maxlen > length)
315
+    strncpyz(dest + length, src, maxlen - length);
318 316
 }
319 317
 
320 318
 
321
-// Safe wcsncat: resulting dest length cannot exceed maxlen and dest 
322
-// is always zero terminated. Note that 'maxlen' parameter defines the entire
323
-// dest buffer size and is not compatible with standard wcsncat.
324
-wchar* wcsncatz(wchar* dest, const wchar* src, size_t maxlen)
319
+// Safe append: resulting dest length cannot exceed maxlen and dest 
320
+// is always zero terminated. 'maxlen' parameter defines the entire
321
+// dest buffer size and is not compatible with wcsncat.
322
+void wcsncatz(wchar* dest, const wchar* src, size_t maxlen)
325 323
 {
326
-  size_t Length = wcslen(dest);
327
-  int avail=int(maxlen - Length - 1);
328
-  if (avail > 0)
329
-    wcsncat(dest, src, avail);
330
-  return dest;
324
+  size_t length = wcslen(dest);
325
+  if (maxlen > length)
326
+    wcsncpyz(dest + length, src, maxlen - length);
331 327
 }
332 328
 
333 329
 
... ...
@@ -16,10 +16,10 @@ wchar* RemoveLF(wchar *Str);
16 16
 unsigned char loctolower(unsigned char ch);
17 17
 unsigned char loctoupper(unsigned char ch);
18 18
 
19
-char* strncpyz(char *dest, const char *src, size_t maxlen);
20
-wchar* wcsncpyz(wchar *dest, const wchar *src, size_t maxlen);
21
-char* strncatz(char* dest, const char* src, size_t maxlen);
22
-wchar* wcsncatz(wchar* dest, const wchar* src, size_t maxlen);
19
+void strncpyz(char *dest, const char *src, size_t maxlen);
20
+void wcsncpyz(wchar *dest, const wchar *src, size_t maxlen);
21
+void strncatz(char* dest, const char* src, size_t maxlen);
22
+void wcsncatz(wchar* dest, const wchar* src, size_t maxlen);
23 23
 
24 24
 unsigned char etoupper(unsigned char ch);
25 25
 wchar etoupperw(wchar ch);
... ...
@@ -77,7 +77,7 @@ class SubAllocator
77 77
     inline void* ExpandUnits(void* ptr,int OldNU);
78 78
     inline void* ShrinkUnits(void* ptr,int OldNU,int NewNU);
79 79
     inline void  FreeUnits(void* ptr,int OldNU);
80
-    long GetAllocatedMemory() {return(SubAllocatorSize);};
80
+    long GetAllocatedMemory() {return(SubAllocatorSize);}
81 81
 
82 82
     byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart;
83 83
 };
... ...
@@ -123,6 +123,28 @@ void Shutdown(POWER_MODE Mode)
123 123
   if (Mode==POWERMODE_RESTART)
124 124
     ExitWindowsEx(EWX_REBOOT|EWX_FORCE,SHTDN_REASON_FLAG_PLANNED);
125 125
 }
126
+
127
+
128
+bool ShutdownCheckAnother(bool Open)
129
+{
130
+  const wchar *EventName=L"rar -ioff";
131
+  static HANDLE hEvent=NULL;
132
+  bool Result=false; // Return false if no other RAR -ioff are running.
133
+  if (Open) // Create or open the event.
134
+    hEvent=CreateEvent(NULL,FALSE,FALSE,EventName);
135
+  else
136
+  {
137
+    if (hEvent!=NULL)
138
+      CloseHandle(hEvent); // Close our event.
139
+    // Check if other copies still own the event. While race conditions
140
+    // are possible, they are improbable and their harm is minimal.
141
+    hEvent=CreateEvent(NULL,FALSE,FALSE,EventName);
142
+    Result=GetLastError()==ERROR_ALREADY_EXISTS;
143
+    if (hEvent!=NULL)
144
+      CloseHandle(hEvent);
145
+  }
146
+  return Result;
147
+}
126 148
 #endif
127 149
 
128 150
 
... ...
@@ -23,6 +23,7 @@ clock_t MonoClock();
23 23
 void Wait();
24 24
 bool EmailFile(const wchar *FileName,const wchar *MailToW);
25 25
 void Shutdown(POWER_MODE Mode);
26
+bool ShutdownCheckAnother(bool Open);
26 27
 
27 28
 #ifdef _WIN_ALL
28 29
 HMODULE WINAPI LoadSysLibrary(const wchar *Name);
... ...
@@ -53,25 +53,22 @@ static struct GlobalPoolCreateSync
53 53
 
54 54
 ThreadPool* CreateThreadPool()
55 55
 {
56
+#ifdef RARDLL
57
+  // We use a simple thread pool, which does not allow to add tasks from
58
+  // different functions and threads in the same time. It is ok for RAR,
59
+  // but UnRAR.dll can be used in multithreaded environment. So we return
60
+  // a new pool for UnRAR.dll every time.
61
+  return new ThreadPool(MaxPoolThreads);
62
+#else
63
+  // Reuse the existing pool for RAR.
56 64
   CriticalSectionStart(&PoolCreateSync.CritSection); 
57
-
65
+  
58 66
   if (GlobalPoolUseCount++ == 0)
59 67
     GlobalPool=new ThreadPool(MaxPoolThreads);
60 68
 
61
-  // We use a simple thread pool, which does not allow to add tasks from
62
-  // different functions and threads in the same time. It is ok for RAR,
63
-  // but UnRAR.dll can be used in multithreaded environment. So if one of
64
-  // threads requests a copy of global pool and another copy is already
65
-  // in use, we create and return a new pool instead of existing global.
66
-  if (GlobalPoolUseCount > 1)
67
-  {
68
-    ThreadPool *Pool = new ThreadPool(MaxPoolThreads);
69
-    CriticalSectionEnd(&PoolCreateSync.CritSection); 
70
-    return Pool;
71
-  }
72
-
73 69
   CriticalSectionEnd(&PoolCreateSync.CritSection); 
74 70
   return GlobalPool;
71
+#endif
75 72
 }
76 73
 
77 74
 
... ...
@@ -79,17 +76,16 @@ void DestroyThreadPool(ThreadPool *Pool)
79 79
 {
80 80
   if (Pool!=NULL)
81 81
   {
82
+#ifdef RARDLL
83
+    delete Pool;
84
+#else
82 85
     CriticalSectionStart(&PoolCreateSync.CritSection); 
83 86
 
84 87
     if (Pool==GlobalPool && GlobalPoolUseCount > 0 && --GlobalPoolUseCount == 0)
85 88
       delete GlobalPool;
86 89
 
87
-    // To correctly work in multithreaded environment UnRAR.dll creates
88
-    // new pools if global pool is already in use. We delete such pools here.
89
-    if (Pool!=GlobalPool)
90
-      delete Pool;
91
-
92 90
     CriticalSectionEnd(&PoolCreateSync.CritSection); 
91
+#endif
93 92
   }
94 93
 }
95 94
 
... ...
@@ -236,7 +236,7 @@ void RarTime::GetText(wchar *DateStr,size_t MaxSize,bool FullMS)
236 236
   else
237 237
   {
238 238
     // We use escape before '?' to avoid weird C trigraph characters.
239
-    wcscpy(DateStr,L"\?\?\?\?-\?\?-\?\? \?\?:\?\?");
239
+    wcsncpyz(DateStr,L"\?\?\?\?-\?\?-\?\? \?\?:\?\?",MaxSize);
240 240
   }
241 241
 }
242 242
 
... ...
@@ -271,7 +271,7 @@ void RarTime::SetIsoText(const wchar *TimeText)
271 271
 void RarTime::SetAgeText(const wchar *TimeText)
272 272
 {
273 273
   uint Seconds=0,Value=0;
274
-  for (int I=0;TimeText[I]!=0;I++)
274
+  for (uint I=0;TimeText[I]!=0;I++)
275 275
   {
276 276
     int Ch=TimeText[I];
277 277
     if (IsDigit(Ch))
... ...
@@ -18,7 +18,8 @@ enum UIMESSAGE_CODE {
18 18
   UIERROR_HEADERBROKEN, UIERROR_MHEADERBROKEN, UIERROR_FHEADERBROKEN,
19 19
   UIERROR_SUBHEADERBROKEN, UIERROR_SUBHEADERUNKNOWN,
20 20
   UIERROR_SUBHEADERDATABROKEN, UIERROR_RRDAMAGED, UIERROR_UNKNOWNMETHOD,
21
-  UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR, UIERROR_NOTSFX, UIERROR_OLDTOSFX,
21
+  UIERROR_UNKNOWNENCMETHOD, UIERROR_RENAMING, UIERROR_NEWERRAR,
22
+  UIERROR_NOTSFX, UIERROR_OLDTOSFX,
22 23
   UIERROR_WRONGSFXVER, UIERROR_ALREADYENC, UIERROR_DICTOUTMEM,
23 24
   UIERROR_USESMALLERDICT, UIERROR_MODIFYUNKNOWN, UIERROR_MODIFYOLD,
24 25
   UIERROR_MODIFYLOCKED, UIERROR_MODIFYVOLUME, UIERROR_NOTVOLUME,
... ...
@@ -31,12 +32,12 @@ enum UIMESSAGE_CODE {
31 31
   UIERROR_NOFILESTOADD, UIERROR_NOFILESTODELETE, UIERROR_NOFILESTOEXTRACT,
32 32
   UIERROR_MISSINGVOL, UIERROR_NEEDPREVVOL, UIERROR_UNKNOWNEXTRA,
33 33
   UIERROR_CORRUPTEXTRA, UIERROR_NTFSREQUIRED, UIERROR_ZIPVOLSFX,
34
-  UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_EMAIL, UIERROR_ACLGET,
35
-  UIERROR_ACLBROKEN, UIERROR_ACLUNKNOWN, UIERROR_ACLSET, UIERROR_STREAMBROKEN,
36
-  UIERROR_STREAMUNKNOWN, UIERROR_INCOMPATSWITCH, UIERROR_PATHTOOLONG,
37
-  UIERROR_DIRSCAN, UIERROR_UOWNERGET, UIERROR_UOWNERBROKEN,
38
-  UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID, UIERROR_UOWNERSET,
39
-  UIERROR_ULINKREAD, UIERROR_ULINKEXIST,
34
+  UIERROR_FILERO, UIERROR_TOOLARGESFX, UIERROR_NOZIPSFX, UIERROR_EMAIL,
35
+  UIERROR_ACLGET, UIERROR_ACLBROKEN, UIERROR_ACLUNKNOWN, UIERROR_ACLSET,
36
+  UIERROR_STREAMBROKEN, UIERROR_STREAMUNKNOWN, UIERROR_INCOMPATSWITCH,
37
+  UIERROR_PATHTOOLONG, UIERROR_DIRSCAN, UIERROR_UOWNERGET,
38
+  UIERROR_UOWNERBROKEN, UIERROR_UOWNERGETOWNERID, UIERROR_UOWNERGETGROUPID,
39
+  UIERROR_UOWNERSET, UIERROR_ULINKREAD, UIERROR_ULINKEXIST,
40 40
 
41 41
   UIMSG_FIRST,
42 42
   UIMSG_STRING, UIMSG_BUILD, UIMSG_RRSEARCH, UIMSG_ANALYZEFILEDATA,
... ...
@@ -75,7 +76,7 @@ enum UIASKREP_RESULT {
75 75
 UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags);
76 76
 UIASKREP_RESULT uiAskReplaceEx(RAROptions *Cmd,wchar *Name,size_t MaxNameSize,int64 FileSize,RarTime *FileTime,uint Flags);
77 77
 
78
-void uiInit(bool Sound);
78
+void uiInit(SOUND_NOTIFY_MODE Sound);
79 79
 
80 80
 
81 81
 void uiStartArchiveExtract(bool Extract,const wchar *ArcName);
... ...
@@ -85,6 +86,7 @@ void uiProcessProgress(const char *Command,int64 CurSize,int64 TotalSize);
85 85
 
86 86
 enum UIPASSWORD_TYPE {UIPASSWORD_GLOBAL,UIPASSWORD_FILE,UIPASSWORD_ARCHIVE};
87 87
 bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password);
88
+bool uiIsGlobalPasswordSet();
88 89
 
89 90
 enum UIALARM_TYPE {UIALARM_ERROR, UIALARM_INFO, UIALARM_QUESTION};
90 91
 void uiAlarm(UIALARM_TYPE Type);
... ...
@@ -110,6 +112,11 @@ class uiMsgStore
110 110
   public:
111 111
     uiMsgStore(UIMESSAGE_CODE Code)
112 112
     {
113
+      // Init arrays in case a caller passes fewer parameters than expected.
114
+      for (uint I=0;I<ASIZE(Str);I++)
115
+        Str[I]=L"";
116
+      memset(Num,0,sizeof(Num));
117
+
113 118
       NumSize=StrSize=0;
114 119
       this->Code=Code;
115 120
     }
... ...
@@ -1,8 +1,8 @@
1
-static bool uiSoundEnabled;
1
+static SOUND_NOTIFY_MODE uiSoundNotify;
2 2
 
3
-void uiInit(bool Sound)
3
+void uiInit(SOUND_NOTIFY_MODE Sound)
4 4
 {
5
-  uiSoundEnabled = Sound;
5
+  uiSoundNotify = Sound;
6 6
 }
7 7
 
8 8
 
... ...
@@ -18,7 +18,10 @@ UIASKREP_RESULT uiAskReplace(wchar *Name,size_t MaxNameSize,int64 FileSize,RarTi
18 18
   {
19 19
     itoa(FileSize,SizeText2,ASIZE(SizeText2));
20 20
     FileTime->GetText(DateStr2,ASIZE(DateStr2),false);
21
-    eprintf(St(MAskReplace),Name,SizeText1,DateStr1,SizeText2,DateStr2);
21
+    if ((Flags & UIASKREP_F_EXCHSRCDEST)==0)
22
+      eprintf(St(MAskReplace),Name,SizeText1,DateStr1,SizeText2,DateStr2);
23
+    else
24
+      eprintf(St(MAskReplace),Name,SizeText2,DateStr2,SizeText1,DateStr1);
22 25
   }
23 26
 
24 27
   bool AllowRename=(Flags & UIASKREP_F_NORENAME)==0;
... ...
@@ -186,7 +189,11 @@ void uiMsgStore::Msg()
186 186
       Log(Str[0],St(MUnknownMeth),Str[1]);
187 187
       break;
188 188
     case UIERROR_UNKNOWNENCMETHOD:
189
-      Log(Str[0],St(MUnkEncMethod),Str[1]);
189
+      {
190
+        wchar Msg[256];
191
+        swprintf(Msg,ASIZE(Msg),St(MUnkEncMethod),Str[1]);
192
+        Log(Str[0],L"%s: %s",Msg,Str[2]);
193
+      }
190 194
       break;
191 195
 #ifndef SFX_MODULE
192 196
    case UIERROR_RENAMING:
... ...
@@ -219,6 +226,7 @@ void uiMsgStore::Msg()
219 219
       break;
220 220
     case UIERROR_INVALIDNAME:
221 221
       Log(Str[0],St(MInvalidName),Str[1]);
222
+      mprintf(L"\n"); // Needed when called from CmdExtract::ExtractCurrentFile.
222 223
       break;
223 224
 #ifndef SFX_MODULE
224 225
     case UIERROR_NEWRARFORMAT:
... ...
@@ -354,9 +362,15 @@ bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Passw
354 354
 }
355 355
 
356 356
 
357
+bool uiIsGlobalPasswordSet()
358
+{
359
+  return false;
360
+}
361
+
362
+
357 363
 void uiAlarm(UIALARM_TYPE Type)
358 364
 {
359
-  if (uiSoundEnabled)
365
+  if (uiSoundNotify==SOUND_NOTIFY_ON)
360 366
   {
361 367
     static clock_t LastTime=-10; // Negative to always beep first time.
362 368
     if ((MonoClock()-LastTime)/CLOCKS_PER_SEC>5)
... ...
@@ -39,6 +39,12 @@ bool uiGetPassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Passw
39 39
 }
40 40
 
41 41
 
42
+bool uiIsGlobalPasswordSet()
43
+{
44
+  return false;
45
+}
46
+
47
+
42 48
 void uiAlarm(UIALARM_TYPE Type)
43 49
 {
44 50
 }
... ...
@@ -70,7 +70,7 @@ bool WideToChar(const wchar *Src,char *Dest,size_t DestSize)
70 70
 #endif
71 71
   if (DestSize>0)
72 72
     Dest[DestSize-1]=0;
73
-  
73
+
74 74
   // We tried to return the empty string if conversion is failed,
75 75
   // but it does not work well. WideCharToMultiByte returns 'failed' code
76 76
   // and partially converted string even if we wanted to convert only a part
... ...
@@ -138,6 +138,11 @@ bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success)
138 138
   if (wcschr(Src,(wchar)MappedStringMark)==NULL)
139 139
     return false;
140 140
 
141
+  // Seems to be that wcrtomb in some memory analyzing libraries
142
+  // can produce uninitilized output while reporting success on garbage input.
143
+  // So we clean the destination to calm analyzers.
144
+  memset(Dest,0,DestSize);
145
+  
141 146
   Success=true;
142 147
   uint SrcPos=0,DestPos=0;
143 148
   while (Src[SrcPos]!=0 && DestPos<DestSize-MB_CUR_MAX)
... ...
@@ -173,7 +178,7 @@ bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success)
173 173
 
174 174
 
175 175
 #if defined(_UNIX) && defined(MBFUNCTIONS)
176
-// Convert and map inconvertible Unicode characters. 
176
+// Convert and map inconvertible Unicode characters.
177 177
 // We use it for extended ASCII names in Unix.
178 178
 void CharToWideMap(const char *Src,wchar *Dest,size_t DestSize,bool &Success)
179 179
 {
... ...
@@ -11,7 +11,7 @@ void Unpack::Unpack5(bool Solid)
11 11
     // Check TablesRead5 to be sure that we read tables at least once
12 12
     // regardless of current block header TablePresent flag.
13 13
     // So we can safefly use these tables below.
14
-    if (!ReadBlockHeader(Inp,BlockHeader) || 
14
+    if (!ReadBlockHeader(Inp,BlockHeader) ||
15 15
         !ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5)
16 16
       return;
17 17
   }
... ...
@@ -536,11 +536,11 @@ bool Unpack::ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header)
536 536
     if (!UnpReadBuf())
537 537
       return false;
538 538
   Inp.faddbits((8-Inp.InBit)&7);
539
-  
539
+
540 540
   byte BlockFlags=Inp.fgetbits()>>8;
541 541
   Inp.faddbits(8);
542 542
   uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count.
543
-  
543
+
544 544
   if (ByteCount==4)
545 545
     return false;
546 546
 
... ...
@@ -48,7 +48,7 @@ void FragmentedWindow::Init(size_t WinSize)
48 48
     }
49 49
     if (NewMem==NULL)
50 50
       throw std::bad_alloc();
51
-    
51
+
52 52
     // Clean the window to generate the same output when unpacking corrupt
53 53
     // RAR files, which may access to unused areas of sliding dictionary.
54 54
     memset(NewMem,0,Size);
... ...
@@ -165,7 +165,7 @@ void Unpack::Unpack5MT(bool Solid)
165 165
         if (DataLeft<TooSmallToProcess)
166 166
           break;
167 167
       }
168
-      
168
+
169 169
 //#undef USE_THREADS
170 170
       UnpackThreadDataList UTDArray[MaxPoolThreads];
171 171
       uint UTDArrayPos=0;
... ...
@@ -180,7 +180,7 @@ void Unpack::Unpack5MT(bool Solid)
180 180
         UnpackThreadDataList *UTD=UTDArray+UTDArrayPos++;
181 181
         UTD->D=UnpThreadData+CurBlock;
182 182
         UTD->BlockCount=Min(MaxBlockPerThread,BlockNumberMT-CurBlock);
183
-      
183
+
184 184
 #ifdef USE_THREADS
185 185
         if (BlockNumber==1)
186 186
           UnpackDecode(*UTD->D);
... ...
@@ -200,7 +200,7 @@ void Unpack::Unpack5MT(bool Solid)
200 200
 #endif
201 201
 
202 202
       bool IncompleteThread=false;
203
-      
203
+
204 204
       for (uint Block=0;Block<BlockNumber;Block++)
205 205
       {
206 206
         UnpackThreadData *CurData=UnpThreadData+Block;
... ...
@@ -251,7 +251,7 @@ void Unpack::Unpack5MT(bool Solid)
251 251
             break;
252 252
           }
253 253
       }
254
-      
254
+
255 255
       if (IncompleteThread || Done)
256 256
         break; // Current buffer is done, read more data or quit.
257 257
       else
... ...
@@ -303,7 +303,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D)
303 303
     D.DamagedData=true;
304 304
     return;
305 305
   }
306
-  
306
+
307 307
   D.DecodedSize=0;
308 308
   int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1;
309 309
 
... ...
@@ -413,7 +413,7 @@ void Unpack::UnpackDecode(UnpackThreadData &D)
413 413
     {
414 414
       UnpackFilter Filter;
415 415
       ReadFilter(D.Inp,Filter);
416
-      
416
+
417 417
       CurItem->Type=UNPDT_FILTER;
418 418
       CurItem->Length=Filter.Type;
419 419
       CurItem->Distance=Filter.BlockStart;
... ...
@@ -498,7 +498,7 @@ bool Unpack::ProcessDecoded(UnpackThreadData &D)
498 498
             if (Item->Type==UNPDT_FILTER)
499 499
             {
500 500
               UnpackFilter Filter;
501
-              
501
+
502 502
               Filter.Type=(byte)Item->Length;
503 503
               Filter.BlockStart=Item->Distance;
504 504
 
... ...
@@ -534,7 +534,7 @@ bool Unpack::UnpackLargeBlock(UnpackThreadData &D)
534 534
     D.DamagedData=true;
535 535
     return false;
536 536
   }
537
-  
537
+
538 538
   int BlockBorder=D.BlockHeader.BlockStart+D.BlockHeader.BlockSize-1;
539 539
 
540 540
   // Reserve enough space even for filter entry.
... ...
@@ -1,6 +1,6 @@
1 1
 #define RARVER_MAJOR     5
2
-#define RARVER_MINOR    60
2
+#define RARVER_MINOR    71
3 3
 #define RARVER_BETA      0
4
-#define RARVER_DAY      24
5
-#define RARVER_MONTH     6
6
-#define RARVER_YEAR   2018
4
+#define RARVER_DAY      28
5
+#define RARVER_MONTH     4
6
+#define RARVER_YEAR   2019
... ...
@@ -34,7 +34,7 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma
34 34
   Arc.Close();
35 35
 
36 36
   wchar NextName[NM];
37
-  wcscpy(NextName,Arc.FileName);
37
+  wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
38 38
   NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
39 39
 
40 40
 #if !defined(SFX_MODULE) && !defined(RARDLL)
... ...
@@ -67,12 +67,12 @@ bool MergeArchive(Archive &Arc,ComprDataIO *DataIO,bool ShowFileName,wchar Comma
67 67
         // Checking for new style volumes renamed by user to old style
68 68
         // name format. Some users did it for unknown reason.
69 69
         wchar AltNextName[NM];
70
-        wcscpy(AltNextName,Arc.FileName);
70
+        wcsncpyz(AltNextName,Arc.FileName,ASIZE(AltNextName));
71 71
         NextVolumeName(AltNextName,ASIZE(AltNextName),true);
72 72
         OldSchemeTested=true;
73 73
         if (Arc.Open(AltNextName,OpenMode))
74 74
         {
75
-          wcscpy(NextName,AltNextName);
75
+          wcsncpyz(NextName,AltNextName,ASIZE(NextName));
76 76
           break;
77 77
         }
78 78
       }
... ...
@@ -185,7 +185,7 @@ bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
185 185
   if (Cmd->Callback!=NULL)
186 186
   {
187 187
     wchar OrgNextName[NM];
188
-    wcscpy(OrgNextName,NextName);
188
+    wcsncpyz(OrgNextName,NextName,ASIZE(OrgNextName));
189 189
     if (Cmd->Callback(UCM_CHANGEVOLUMEW,Cmd->UserData,(LPARAM)NextName,RAR_VOL_ASK)==-1)
190 190
       DllVolAborted=true;
191 191
     else
... ...
@@ -195,7 +195,7 @@ bool DllVolChange(RAROptions *Cmd,wchar *NextName,size_t NameSize)
195 195
       {
196 196
         char NextNameA[NM],OrgNextNameA[NM];
197 197
         WideToChar(NextName,NextNameA,ASIZE(NextNameA));
198
-        strcpy(OrgNextNameA,NextNameA);
198
+        strncpyz(OrgNextNameA,NextNameA,ASIZE(OrgNextNameA));
199 199
         if (Cmd->Callback(UCM_CHANGEVOLUME,Cmd->UserData,(LPARAM)NextNameA,RAR_VOL_ASK)==-1)
200 200
           DllVolAborted=true;
201 201
         else
... ...
@@ -54,7 +54,10 @@ void ExtractACL20(Archive &Arc,const wchar *FileName)
54 54
   if (!SetCode)
55 55
   {
56 56
     uiMsg(UIERROR_ACLSET,Arc.FileName,FileName);
57
+    DWORD LastError=GetLastError();
57 58
     ErrHandler.SysErrMsg();
59
+    if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin())
60
+      uiMsg(UIERROR_NEEDADMIN);
58 61
     ErrHandler.SetErrorCode(RARX_WARNING);
59 62
   }
60 63
 }
... ...
@@ -86,7 +89,10 @@ void ExtractACL(Archive &Arc,const wchar *FileName)
86 86
   if (!SetCode)
87 87
   {
88 88
     uiMsg(UIERROR_ACLSET,Arc.FileName,FileName);
89
+    DWORD LastError=GetLastError();
89 90
     ErrHandler.SysErrMsg();
91
+    if (LastError==ERROR_ACCESS_DENIED && !IsUserAdmin())
92
+      uiMsg(UIERROR_NEEDADMIN);
90 93
     ErrHandler.SetErrorCode(RARX_WARNING);
91 94
   }
92 95
 }
... ...
@@ -20,11 +20,13 @@ void ExtractStreams20(Archive &Arc,const wchar *FileName)
20 20
   wchar StreamName[NM+2];
21 21
   if (FileName[0]!=0 && FileName[1]==0)
22 22
   {
23
-    wcscpy(StreamName,L".\\");
24
-    wcscpy(StreamName+2,FileName);
23
+    // Convert single character names like f:stream to .\f:stream to
24
+    // resolve the ambiguity with drive letters.
25
+    wcsncpyz(StreamName,L".\\",ASIZE(StreamName));
26
+    wcsncatz(StreamName,FileName,ASIZE(StreamName));
25 27
   }
26 28
   else
27
-    wcscpy(StreamName,FileName);
29
+    wcsncpyz(StreamName,FileName,ASIZE(StreamName));
28 30
   if (wcslen(StreamName)+strlen(Arc.StreamHead.StreamName)>=ASIZE(StreamName) ||
29 31
       Arc.StreamHead.StreamName[0]!=':')
30 32
   {
... ...
@@ -35,7 +37,7 @@ void ExtractStreams20(Archive &Arc,const wchar *FileName)
35 35
 
36 36
   wchar StoredName[NM];
37 37
   CharToWide(Arc.StreamHead.StreamName,StoredName,ASIZE(StoredName));
38
-  ConvertPath(StoredName+1,StoredName+1);
38
+  ConvertPath(StoredName+1,StoredName+1,ASIZE(StoredName)-1);
39 39
 
40 40
   wcsncatz(StreamName,StoredName,ASIZE(StreamName));
41 41
 
... ...
@@ -83,8 +85,10 @@ void ExtractStreams(Archive &Arc,const wchar *FileName,bool TestMode)
83 83
   wchar FullName[NM+2];
84 84
   if (FileName[0]!=0 && FileName[1]==0)
85 85
   {
86
-    wcscpy(FullName,L".\\");
87
-    wcsncpyz(FullName+2,FileName,ASIZE(FullName)-2);
86
+    // Convert single character names like f:stream to .\f:stream to
87
+    // resolve the ambiguity with drive letters.
88
+    wcsncpyz(FullName,L".\\",ASIZE(FullName));
89
+    wcsncatz(FullName,FileName,ASIZE(FullName));
88 90
   }
89 91
   else
90 92
     wcsncpyz(FullName,FileName,ASIZE(FullName));
... ...
@@ -91,7 +91,7 @@ uint8_t unrar_debug = 0;
91 91
 
92 92
 /**
93 93
  * @brief  Translate an ERAR_<code> to the appropriate UNRAR_<code>
94
- * 
94
+ *
95 95
  * @param errorCode ERAR_<code>
96 96
  * @return cl_unrar_error_t UNRAR_OK, UNRAR_ENCRYPTED, or UNRAR_ERR.
97 97
  */
... ...
@@ -213,6 +213,7 @@ cl_unrar_error_t unrar_open(const char* filename, void** hArchive, char** commen
213 213
     }
214 214
     archiveData->ArcName = (char *)filename;
215 215
     archiveData->OpenMode = RAR_OM_EXTRACT;
216
+    archiveData->OpFlags |= ROADOF_KEEPBROKEN;
216 217
     archiveData->CmtBuf = (char*)calloc(1, CMTBUFSIZE);
217 218
     if (archiveData->CmtBuf == NULL) {
218 219
         unrar_dbgmsg("unrar_open: Not enough memory to allocate main archive header comment buffer.\n");
... ...
@@ -291,7 +292,7 @@ done:
291 291
 
292 292
 /**
293 293
  * @brief  Get file metadata from the next file header.
294
- * 
294
+ *
295 295
  * @param hArchive              Handle to the archive we're extracting.
296 296
  * @param[in/out] file_metadata Pointer to a pre-allocated metadata structure.
297 297
  * @return cl_unrar_error_t     UNRAR_OK if metadata retrieved, UNRAR_BREAK if no more files, UNRAR_ENCRYPTED if header was encrypted, else maybe UNRAR_EMEM or UNRAR_ERR.
... ...
@@ -320,7 +321,7 @@ cl_unrar_error_t unrar_peek_file_header(void* hArchive, unrar_metadata_t* file_m
320 320
      */
321 321
     headerData.CmtBuf = NULL;
322 322
     headerData.CmtBufSize = 0;
323
-    
323
+
324 324
     headerData.RedirNameSize = 1024 * sizeof(wchar_t);
325 325
     headerData.RedirName = (wchar_t*)&RedirName;
326 326
     memset(headerData.RedirName, 0, headerData.RedirNameSize);