1 #include "rar.hpp"
2 
3 #include "cmdfilter.cpp"
4 #include "cmdmix.cpp"
5 
CommandData()6 CommandData::CommandData()
7 {
8   Init();
9 }
10 
11 
Init()12 void CommandData::Init()
13 {
14   RAROptions::Init();
15 
16   *Command=0;
17   *ArcName=0;
18   FileLists=false;
19   NoMoreSwitches=false;
20 
21   ListMode=RCLM_AUTO;
22 
23   BareOutput=false;
24 
25 
26   FileArgs.Reset();
27   ExclArgs.Reset();
28   InclArgs.Reset();
29   StoreArgs.Reset();
30   ArcNames.Reset();
31   NextVolSizes.Reset();
32 }
33 
34 
35 // Return the pointer to next position in the string and store dynamically
36 // allocated command line parameter in Par.
AllocCmdParam(const wchar * CmdLine,wchar ** Par)37 static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par)
38 {
39   const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0);
40   if (NextCmd==NULL)
41     return NULL;
42   size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero.
43   *Par=(wchar *)malloc(ParSize*sizeof(wchar));
44   if (*Par==NULL)
45     return NULL;
46   return GetCmdParam(CmdLine,*Par,ParSize);
47 }
48 
49 
50 #if !defined(SFX_MODULE)
ParseCommandLine(bool Preprocess,int argc,char * argv[])51 void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[])
52 {
53   *Command=0;
54   NoMoreSwitches=false;
55 #ifdef CUSTOM_CMDLINE_PARSER
56   // In Windows we may prefer to implement our own command line parser
57   // to avoid replacing \" by " in standard parser. Such replacing corrupts
58   // destination paths like "dest path\" in extraction commands.
59   const wchar *CmdLine=GetCommandLine();
60 
61   wchar *Par;
62   for (bool FirstParam=true;;FirstParam=false)
63   {
64     if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL)
65       break;
66     if (!FirstParam) // First parameter is the executable name.
67       if (Preprocess)
68         PreprocessArg(Par);
69       else
70         ParseArg(Par);
71     free(Par);
72   }
73 #else
74   Array<wchar> Arg;
75   for (int I=1;I<argc;I++)
76   {
77     Arg.Alloc(strlen(argv[I])+1);
78     CharToWide(argv[I],&Arg[0],Arg.Size());
79     if (Preprocess)
80       PreprocessArg(&Arg[0]);
81     else
82       ParseArg(&Arg[0]);
83   }
84 #endif
85   if (!Preprocess)
86     ParseDone();
87 }
88 #endif
89 
90 
91 #if !defined(SFX_MODULE)
ParseArg(wchar * Arg)92 void CommandData::ParseArg(wchar *Arg)
93 {
94   if (IsSwitch(*Arg) && !NoMoreSwitches)
95     if (Arg[1]=='-' && Arg[2]==0)
96       NoMoreSwitches=true;
97     else
98       ProcessSwitch(Arg+1);
99   else
100     if (*Command==0)
101     {
102       wcsncpyz(Command,Arg,ASIZE(Command));
103 
104 
105       *Command=toupperw(*Command);
106       // 'I' and 'S' commands can contain case sensitive strings after
107       // the first character, so we must not modify their case.
108       // 'S' can contain SFX name, which case is important in Unix.
109       if (*Command!='I' && *Command!='S')
110         wcsupper(Command);
111     }
112     else
113       if (*ArcName==0)
114         wcsncpyz(ArcName,Arg,ASIZE(ArcName));
115       else
116       {
117         // Check if last character is the path separator.
118         size_t Length=wcslen(Arg);
119         wchar EndChar=Length==0 ? 0:Arg[Length-1];
120         bool EndSeparator=IsDriveDiv(EndChar) || IsPathDiv(EndChar);
121 
122         wchar CmdChar=toupperw(*Command);
123         bool Add=wcschr(L"AFUM",CmdChar)!=NULL;
124         bool Extract=CmdChar=='X' || CmdChar=='E';
125         bool Repair=CmdChar=='R' && Command[1]==0;
126         if (EndSeparator && !Add)
127           wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
128         else
129           if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS))
130             FileArgs.AddString(Arg);
131           else
132           {
133             FindData FileData;
134             bool Found=FindFile::FastFind(Arg,&FileData);
135             if ((!Found || ListMode==RCLM_ACCEPT_LISTS) &&
136                 ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg+1))
137             {
138               FileLists=true;
139 
140               ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true);
141 
142             }
143             else // We use 'destpath\' when extracting and reparing.
144               if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0)
145               {
146                 wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
147                 AddEndSlash(ExtrPath,ASIZE(ExtrPath));
148               }
149               else
150                 FileArgs.AddString(Arg);
151           }
152       }
153 }
154 #endif
155 
156 
ParseDone()157 void CommandData::ParseDone()
158 {
159   if (FileArgs.ItemsCount()==0 && !FileLists)
160     FileArgs.AddString(MASKALL);
161   wchar CmdChar=toupperw(Command[0]);
162   bool Extract=CmdChar=='X' || CmdChar=='E' || CmdChar=='P';
163   if (Test && Extract)
164     Test=false;        // Switch '-t' is senseless for 'X', 'E', 'P' commands.
165 
166   // Suppress the copyright message and final end of line for 'lb' and 'vb'.
167   if ((CmdChar=='L' || CmdChar=='V') && Command[1]=='B')
168     BareOutput=true;
169 }
170 
171 
172 #if !defined(SFX_MODULE)
ParseEnvVar()173 void CommandData::ParseEnvVar()
174 {
175   char *EnvStr=getenv("RAR");
176   if (EnvStr!=NULL)
177   {
178     Array<wchar> EnvStrW(strlen(EnvStr)+1);
179     CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size());
180     ProcessSwitchesString(&EnvStrW[0]);
181   }
182 }
183 #endif
184 
185 
186 
187 #if !defined(SFX_MODULE)
188 // Preprocess those parameters, which must be processed before the rest of
189 // command line. Return 'false' to stop further processing.
PreprocessArg(const wchar * Arg)190 void CommandData::PreprocessArg(const wchar *Arg)
191 {
192   if (IsSwitch(Arg[0]) && !NoMoreSwitches)
193   {
194     Arg++;
195     if (Arg[0]=='-' && Arg[1]==0) // Switch "--".
196       NoMoreSwitches=true;
197     if (wcsicomp(Arg,L"cfg-")==0)
198       ConfigDisabled=true;
199     if (wcsnicomp(Arg,L"ilog",4)==0)
200     {
201       // Ensure that correct log file name is already set
202       // if we need to report an error when processing the command line.
203       ProcessSwitch(Arg);
204       InitLogOptions(LogName,ErrlogCharset);
205     }
206     if (wcsnicomp(Arg,L"sc",2)==0)
207     {
208       // Process -sc before reading any file lists.
209       ProcessSwitch(Arg);
210       if (*LogName!=0)
211         InitLogOptions(LogName,ErrlogCharset);
212     }
213   }
214   else
215     if (*Command==0)
216       wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini.
217 }
218 #endif
219 
220 
221 #if !defined(SFX_MODULE)
ReadConfig()222 void CommandData::ReadConfig()
223 {
224   StringList List;
225   if (ReadTextFile(DefConfigName,&List,true))
226   {
227     wchar *Str;
228     while ((Str=List.GetString())!=NULL)
229     {
230       while (IsSpace(*Str))
231         Str++;
232       if (wcsnicomp(Str,L"switches=",9)==0)
233         ProcessSwitchesString(Str+9);
234       if (*Command!=0)
235       {
236         wchar Cmd[16];
237         wcsncpyz(Cmd,Command,ASIZE(Cmd));
238         wchar C0=toupperw(Cmd[0]);
239         wchar C1=toupperw(Cmd[1]);
240         if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V')
241           Cmd[1]=0;
242         if (C0=='R' && (C1=='R' || C1=='V'))
243           Cmd[2]=0;
244         wchar SwName[16+ASIZE(Cmd)];
245         swprintf(SwName,ASIZE(SwName),L"switches_%ls=",Cmd);
246         size_t Length=wcslen(SwName);
247         if (wcsnicomp(Str,SwName,Length)==0)
248           ProcessSwitchesString(Str+Length);
249       }
250     }
251   }
252 }
253 #endif
254 
255 
256 #if !defined(SFX_MODULE)
ProcessSwitchesString(const wchar * Str)257 void CommandData::ProcessSwitchesString(const wchar *Str)
258 {
259   wchar *Par;
260   while ((Str=AllocCmdParam(Str,&Par))!=NULL)
261   {
262     if (IsSwitch(*Par))
263       ProcessSwitch(Par+1);
264     free(Par);
265   }
266 }
267 #endif
268 
269 
270 #if !defined(SFX_MODULE)
ProcessSwitch(const wchar * Switch)271 void CommandData::ProcessSwitch(const wchar *Switch)
272 {
273 
274   switch(toupperw(Switch[0]))
275   {
276     case '@':
277       ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS;
278       break;
279     case 'A':
280       switch(toupperw(Switch[1]))
281       {
282         case 'C':
283           ClearArc=true;
284           break;
285         case 'D':
286           if (Switch[2]==0)
287             AppendArcNameToPath=APPENDARCNAME_DESTPATH;
288           else
289             if (Switch[2]=='1')
290               AppendArcNameToPath=APPENDARCNAME_OWNSUBDIR;
291             else
292               if (Switch[2]=='2')
293                 AppendArcNameToPath=APPENDARCNAME_OWNDIR;
294           break;
295 #ifndef SFX_MODULE
296         case 'G':
297           if (Switch[2]=='-' && Switch[3]==0)
298             GenerateArcName=0;
299           else
300             if (toupperw(Switch[2])=='F')
301               wcsncpyz(DefGenerateMask,Switch+3,ASIZE(DefGenerateMask));
302             else
303             {
304               GenerateArcName=true;
305               wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask));
306             }
307           break;
308 #endif
309         case 'I':
310           IgnoreGeneralAttr=true;
311           break;
312         case 'N': // Reserved for archive name.
313           break;
314         case 'O':
315           AddArcOnly=true;
316           break;
317         case 'P':
318           wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath));
319           break;
320         case 'S':
321           SyncFiles=true;
322           break;
323         default:
324           BadSwitch(Switch);
325           break;
326       }
327       break;
328     case 'C':
329       if (Switch[2]==0)
330         switch(toupperw(Switch[1]))
331         {
332           case '-':
333             DisableComment=true;
334             break;
335           case 'U':
336             ConvertNames=NAMES_UPPERCASE;
337             break;
338           case 'L':
339             ConvertNames=NAMES_LOWERCASE;
340             break;
341         }
342       break;
343     case 'D':
344       if (Switch[2]==0)
345         switch(toupperw(Switch[1]))
346         {
347           case 'S':
348             DisableSortSolid=true;
349             break;
350           case 'H':
351             OpenShared=true;
352             break;
353           case 'F':
354             DeleteFiles=true;
355             break;
356         }
357       break;
358     case 'E':
359       switch(toupperw(Switch[1]))
360       {
361         case 'P':
362           switch(Switch[2])
363           {
364             case 0:
365               ExclPath=EXCL_SKIPWHOLEPATH;
366               break;
367             case '1':
368               ExclPath=EXCL_BASEPATH;
369               break;
370             case '2':
371               ExclPath=EXCL_SAVEFULLPATH;
372               break;
373             case '3':
374               ExclPath=EXCL_ABSPATH;
375               break;
376           }
377           break;
378         default:
379           if (Switch[1]=='+')
380           {
381             InclFileAttr|=GetExclAttr(Switch+2,InclDir);
382             InclAttrSet=true;
383           }
384           else
385             ExclFileAttr|=GetExclAttr(Switch+1,ExclDir);
386           break;
387       }
388       break;
389     case 'F':
390       if (Switch[1]==0)
391         FreshFiles=true;
392       else
393         BadSwitch(Switch);
394       break;
395     case 'H':
396       switch (toupperw(Switch[1]))
397       {
398         case 'P':
399           EncryptHeaders=true;
400           if (Switch[2]!=0)
401           {
402             Password.Set(Switch+2);
403             cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
404           }
405           else
406             if (!Password.IsSet())
407             {
408               uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
409               eprintf(L"\n");
410             }
411           break;
412         default :
413           BadSwitch(Switch);
414           break;
415       }
416       break;
417     case 'I':
418       if (wcsnicomp(Switch+1,L"LOG",3)==0)
419       {
420         wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName));
421         break;
422       }
423       if (wcsnicomp(Switch+1,L"SND",3)==0)
424       {
425         Sound=Switch[4]=='-' ? SOUND_NOTIFY_OFF : SOUND_NOTIFY_ON;
426         break;
427       }
428       if (wcsicomp(Switch+1,L"ERR")==0)
429       {
430         MsgStream=MSG_STDERR;
431         // Set it immediately when parsing the command line, so it also
432         // affects messages issued while parsing the command line.
433         SetConsoleMsgStream(MSG_STDERR);
434         break;
435       }
436       if (wcsnicomp(Switch+1,L"EML",3)==0)
437       {
438         wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo));
439         break;
440       }
441       if (wcsicomp(Switch+1,L"M")==0) // For compatibility with pre-WinRAR 6.0 -im syntax. Replaced with -idv.
442       {
443         VerboseOutput=true;
444         break;
445       }
446       if (wcsicomp(Switch+1,L"NUL")==0)
447       {
448         MsgStream=MSG_NULL;
449         SetConsoleMsgStream(MSG_NULL);
450         break;
451       }
452       if (toupperw(Switch[1])=='D')
453       {
454         for (uint I=2;Switch[I]!=0;I++)
455           switch(toupperw(Switch[I]))
456           {
457             case 'Q':
458               MsgStream=MSG_ERRONLY;
459               SetConsoleMsgStream(MSG_ERRONLY);
460               break;
461             case 'C':
462               DisableCopyright=true;
463               break;
464             case 'D':
465               DisableDone=true;
466               break;
467             case 'P':
468               DisablePercentage=true;
469               break;
470             case 'N':
471               DisableNames=true;
472               break;
473             case 'V':
474               VerboseOutput=true;
475               break;
476           }
477         break;
478       }
479       if (wcsnicomp(Switch+1,L"OFF",3)==0)
480       {
481         switch(Switch[4])
482         {
483           case 0:
484           case '1':
485             Shutdown=POWERMODE_OFF;
486             break;
487           case '2':
488             Shutdown=POWERMODE_HIBERNATE;
489             break;
490           case '3':
491             Shutdown=POWERMODE_SLEEP;
492             break;
493           case '4':
494             Shutdown=POWERMODE_RESTART;
495             break;
496         }
497         break;
498       }
499       if (wcsicomp(Switch+1,L"VER")==0)
500       {
501         PrintVersion=true;
502         break;
503       }
504       break;
505     case 'K':
506       switch(toupperw(Switch[1]))
507       {
508         case 'B':
509           KeepBroken=true;
510           break;
511         case 0:
512           Lock=true;
513           break;
514       }
515       break;
516     case 'M':
517       switch(toupperw(Switch[1]))
518       {
519         case 'C':
520           {
521             const wchar *Str=Switch+2;
522             if (*Str=='-')
523               for (uint I=0;I<ASIZE(FilterModes);I++)
524                 FilterModes[I].State=FILTER_DISABLE;
525             else
526               while (*Str!=0)
527               {
528                 int Param1=0,Param2=0;
529                 FilterState State=FILTER_AUTO;
530                 FilterType Type=FILTER_NONE;
531                 if (IsDigit(*Str))
532                 {
533                   Param1=atoiw(Str);
534                   while (IsDigit(*Str))
535                     Str++;
536                 }
537                 if (*Str==':' && IsDigit(Str[1]))
538                 {
539                   Param2=atoiw(++Str);
540                   while (IsDigit(*Str))
541                     Str++;
542                 }
543                 switch(toupperw(*(Str++)))
544                 {
545                   case 'T': Type=FILTER_PPM;         break;
546                   case 'E': Type=FILTER_E8;          break;
547                   case 'D': Type=FILTER_DELTA;       break;
548                   case 'A': Type=FILTER_AUDIO;       break;
549                   case 'C': Type=FILTER_RGB;         break;
550                   case 'R': Type=FILTER_ARM;         break;
551                 }
552                 if (*Str=='+' || *Str=='-')
553                   State=*(Str++)=='+' ? FILTER_FORCE:FILTER_DISABLE;
554                 FilterModes[Type].State=State;
555                 FilterModes[Type].Param1=Param1;
556                 FilterModes[Type].Param2=Param2;
557               }
558             }
559           break;
560         case 'M':
561           break;
562         case 'D':
563           break;
564         case 'S':
565           {
566             wchar StoreNames[1024];
567             wcsncpyz(StoreNames,(Switch[2]==0 ? DefaultStoreList:Switch+2),ASIZE(StoreNames));
568             wchar *Names=StoreNames;
569             while (*Names!=0)
570             {
571               wchar *End=wcschr(Names,';');
572               if (End!=NULL)
573                 *End=0;
574               if (*Names=='.')
575                 Names++;
576               wchar Mask[NM];
577               if (wcspbrk(Names,L"*?.")==NULL)
578                 swprintf(Mask,ASIZE(Mask),L"*.%ls",Names);
579               else
580                 wcsncpyz(Mask,Names,ASIZE(Mask));
581               StoreArgs.AddString(Mask);
582               if (End==NULL)
583                 break;
584               Names=End+1;
585             }
586           }
587           break;
588 #ifdef RAR_SMP
589         case 'T':
590           Threads=atoiw(Switch+2);
591           if (Threads>MaxPoolThreads || Threads<1)
592             BadSwitch(Switch);
593           else
594           {
595           }
596           break;
597 #endif
598         default:
599           Method=Switch[1]-'0';
600           if (Method>5 || Method<0)
601             BadSwitch(Switch);
602           break;
603       }
604       break;
605     case 'N':
606     case 'X':
607       if (Switch[1]!=0)
608       {
609         StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs;
610         if (Switch[1]=='@' && !IsWildcard(Switch))
611           ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true);
612         else
613           Args->AddString(Switch+1);
614       }
615       break;
616     case 'O':
617       switch(toupperw(Switch[1]))
618       {
619         case '+':
620           Overwrite=OVERWRITE_ALL;
621           break;
622         case '-':
623           Overwrite=OVERWRITE_NONE;
624           break;
625         case 0:
626           Overwrite=OVERWRITE_FORCE_ASK;
627           break;
628 #ifdef _WIN_ALL
629         case 'C':
630           SetCompressedAttr=true;
631           break;
632 #endif
633         case 'H':
634           SaveHardLinks=true;
635           break;
636 
637 
638 #ifdef SAVE_LINKS
639         case 'L':
640           SaveSymLinks=true;
641           if (toupperw(Switch[2])=='A')
642             AbsoluteLinks=true;
643           break;
644 #endif
645 #ifdef _WIN_ALL
646         case 'N':
647           if (toupperw(Switch[2])=='I')
648             AllowIncompatNames=true;
649           break;
650 #endif
651         case 'R':
652           Overwrite=OVERWRITE_AUTORENAME;
653           break;
654 #ifdef _WIN_ALL
655         case 'S':
656           SaveStreams=true;
657           break;
658 #endif
659         case 'W':
660           ProcessOwners=true;
661           break;
662         default :
663           BadSwitch(Switch);
664           break;
665       }
666       break;
667     case 'P':
668       if (Switch[1]==0)
669       {
670         uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
671         eprintf(L"\n");
672       }
673       else
674       {
675         Password.Set(Switch+1);
676         cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
677       }
678       break;
679 #ifndef SFX_MODULE
680     case 'Q':
681       if (toupperw(Switch[1])=='O')
682         switch(toupperw(Switch[2]))
683         {
684           case 0:
685             QOpenMode=QOPEN_AUTO;
686             break;
687           case '-':
688             QOpenMode=QOPEN_NONE;
689             break;
690           case '+':
691             QOpenMode=QOPEN_ALWAYS;
692             break;
693           default:
694             BadSwitch(Switch);
695             break;
696         }
697       else
698         BadSwitch(Switch);
699       break;
700 #endif
701     case 'R':
702       switch(toupperw(Switch[1]))
703       {
704         case 0:
705           Recurse=RECURSE_ALWAYS;
706           break;
707         case '-':
708           Recurse=RECURSE_DISABLE;
709           break;
710         case '0':
711           Recurse=RECURSE_WILDCARDS;
712           break;
713         case 'I':
714           {
715             Priority=atoiw(Switch+2);
716             if (Priority<0 || Priority>15)
717               BadSwitch(Switch);
718             const wchar *ChPtr=wcschr(Switch+2,':');
719             if (ChPtr!=NULL)
720             {
721               SleepTime=atoiw(ChPtr+1);
722               if (SleepTime>1000)
723                 BadSwitch(Switch);
724               InitSystemOptions(SleepTime);
725             }
726             SetPriority(Priority);
727           }
728           break;
729       }
730       break;
731     case 'S':
732       if (IsDigit(Switch[1]))
733       {
734         Solid|=SOLID_COUNT;
735         SolidCount=atoiw(&Switch[1]);
736       }
737       else
738         switch(toupperw(Switch[1]))
739         {
740           case 0:
741             Solid|=SOLID_NORMAL;
742             break;
743           case '-':
744             Solid=SOLID_NONE;
745             break;
746           case 'E':
747             Solid|=SOLID_FILEEXT;
748             break;
749           case 'V':
750             Solid|=Switch[2]=='-' ? SOLID_VOLUME_DEPENDENT:SOLID_VOLUME_INDEPENDENT;
751             break;
752           case 'D':
753             Solid|=SOLID_VOLUME_DEPENDENT;
754             break;
755           case 'L':
756             if (IsDigit(Switch[2]))
757               FileSizeLess=atoilw(Switch+2);
758             break;
759           case 'M':
760             if (IsDigit(Switch[2]))
761               FileSizeMore=atoilw(Switch+2);
762             break;
763           case 'C':
764             {
765               bool AlreadyBad=false; // Avoid reporting "bad switch" several times.
766 
767               RAR_CHARSET rch=RCH_DEFAULT;
768               switch(toupperw(Switch[2]))
769               {
770                 case 'A':
771                   rch=RCH_ANSI;
772                   break;
773                 case 'O':
774                   rch=RCH_OEM;
775                   break;
776                 case 'U':
777                   rch=RCH_UNICODE;
778                   break;
779                 case 'F':
780                   rch=RCH_UTF8;
781                   break;
782                 default :
783                   BadSwitch(Switch);
784                   AlreadyBad=true;
785                   break;
786               };
787               if (!AlreadyBad)
788                 if (Switch[3]==0)
789                   CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch;
790                 else
791                   for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++)
792                     switch(toupperw(Switch[I]))
793                     {
794                       case 'C':
795                         CommentCharset=rch;
796                         break;
797                       case 'L':
798                         FilelistCharset=rch;
799                         break;
800                       case 'R':
801                         RedirectCharset=rch;
802                         break;
803                       default:
804                         BadSwitch(Switch);
805                         AlreadyBad=true;
806                         break;
807                     }
808               // Set it immediately when parsing the command line, so it also
809               // affects messages issued while parsing the command line.
810               SetConsoleRedirectCharset(RedirectCharset);
811             }
812             break;
813 
814         }
815       break;
816     case 'T':
817       switch(toupperw(Switch[1]))
818       {
819         case 'K':
820           ArcTime=ARCTIME_KEEP;
821           break;
822         case 'L':
823           ArcTime=ARCTIME_LATEST;
824           break;
825         case 'O':
826           SetTimeFilters(Switch+2,true,true);
827           break;
828         case 'N':
829           SetTimeFilters(Switch+2,false,true);
830           break;
831         case 'B':
832           SetTimeFilters(Switch+2,true,false);
833           break;
834         case 'A':
835           SetTimeFilters(Switch+2,false,false);
836           break;
837         case 'S':
838           SetStoreTimeMode(Switch+2);
839           break;
840         case '-':
841           Test=false;
842           break;
843         case 0:
844           Test=true;
845           break;
846         default:
847           BadSwitch(Switch);
848           break;
849       }
850       break;
851     case 'U':
852       if (Switch[1]==0)
853         UpdateFiles=true;
854       else
855         BadSwitch(Switch);
856       break;
857     case 'V':
858       switch(toupperw(Switch[1]))
859       {
860         case 'P':
861           VolumePause=true;
862           break;
863         case 'E':
864           if (toupperw(Switch[2])=='R')
865             VersionControl=atoiw(Switch+3)+1;
866           break;
867         case '-':
868           VolSize=0;
869           break;
870         default:
871           VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command.
872           break;
873       }
874       break;
875     case 'W':
876       wcsncpyz(TempPath,Switch+1,ASIZE(TempPath));
877       AddEndSlash(TempPath,ASIZE(TempPath));
878       break;
879     case 'Y':
880       AllYes=true;
881       break;
882     case 'Z':
883       if (Switch[1]==0)
884       {
885         // If comment file is not specified, we read data from stdin.
886         wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile));
887       }
888       else
889         wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile));
890       break;
891     case '?' :
892       OutHelp(RARX_SUCCESS);
893       break;
894     default :
895       BadSwitch(Switch);
896       break;
897   }
898 }
899 #endif
900 
901 
902 #if !defined(SFX_MODULE)
BadSwitch(const wchar * Switch)903 void CommandData::BadSwitch(const wchar *Switch)
904 {
905   mprintf(St(MUnknownOption),Switch);
906   ErrHandler.Exit(RARX_USERERROR);
907 }
908 #endif
909 
910 
ProcessCommand()911 void CommandData::ProcessCommand()
912 {
913 #ifndef SFX_MODULE
914 
915   const wchar *SingleCharCommands=L"FUADPXETK";
916   if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0)
917     OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
918 
919   const wchar *ArcExt=GetExt(ArcName);
920 #ifdef _UNIX
921   if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
922     wcsncatz(ArcName,L".rar",ASIZE(ArcName));
923 #else
924   if (ArcExt==NULL)
925     wcsncatz(ArcName,L".rar",ASIZE(ArcName));
926 #endif
927   // Treat arcname.part1 as arcname.part1.rar.
928   if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) &&
929       !FileExist(ArcName))
930   {
931     wchar Name[NM];
932     wcsncpyz(Name,ArcName,ASIZE(Name));
933     wcsncatz(Name,L".rar",ASIZE(Name));
934     if (FileExist(Name))
935       wcsncpyz(ArcName,Name,ASIZE(ArcName));
936   }
937 
938   if (wcschr(L"AFUMD",*Command)==NULL)
939   {
940     if (GenerateArcName)
941     {
942       const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask;
943       GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false);
944     }
945 
946     StringList ArcMasks;
947     ArcMasks.AddString(ArcName);
948     ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS);
949     FindData FindData;
950     while (Scan.GetNext(&FindData)==SCAN_SUCCESS)
951       AddArcName(FindData.Name);
952   }
953   else
954     AddArcName(ArcName);
955 #endif
956 
957   switch(Command[0])
958   {
959     case 'P':
960     case 'X':
961     case 'E':
962     case 'T':
963       {
964         CmdExtract Extract(this);
965         Extract.DoExtract();
966       }
967       break;
968 #ifndef SILENT
969     case 'V':
970     case 'L':
971       ListArchive(this);
972       break;
973     default:
974       OutHelp(RARX_USERERROR);
975 #endif
976   }
977   if (!BareOutput)
978     mprintf(L"\n");
979 }
980 
981 
AddArcName(const wchar * Name)982 void CommandData::AddArcName(const wchar *Name)
983 {
984   ArcNames.AddString(Name);
985 }
986 
987 
GetArcName(wchar * Name,int MaxSize)988 bool CommandData::GetArcName(wchar *Name,int MaxSize)
989 {
990   return ArcNames.GetString(Name,MaxSize);
991 }
992 
993 
IsSwitch(int Ch)994 bool CommandData::IsSwitch(int Ch)
995 {
996 #if defined(_WIN_ALL) || defined(_EMX)
997   return Ch=='-' || Ch=='/';
998 #else
999   return Ch=='-';
1000 #endif
1001 }
1002 
1003 
1004 #ifndef SFX_MODULE
GetExclAttr(const wchar * Str,bool & Dir)1005 uint CommandData::GetExclAttr(const wchar *Str,bool &Dir)
1006 {
1007   if (IsDigit(*Str))
1008     return wcstol(Str,NULL,0);
1009 
1010   uint Attr=0;
1011   while (*Str!=0)
1012   {
1013     switch(toupperw(*Str))
1014     {
1015       case 'D':
1016         Dir=true;
1017         break;
1018 #ifdef _UNIX
1019       case 'V':
1020         Attr|=S_IFCHR;
1021         break;
1022 #elif defined(_WIN_ALL) || defined(_EMX)
1023       case 'R':
1024         Attr|=0x1;
1025         break;
1026       case 'H':
1027         Attr|=0x2;
1028         break;
1029       case 'S':
1030         Attr|=0x4;
1031         break;
1032       case 'A':
1033         Attr|=0x20;
1034         break;
1035 #endif
1036     }
1037     Str++;
1038   }
1039   return Attr;
1040 }
1041 #endif
1042 
1043 
1044 
1045 
1046 #ifndef SFX_MODULE
CheckWinSize()1047 bool CommandData::CheckWinSize()
1048 {
1049   // Define 0x100000000 as macro to avoid troubles with older compilers.
1050   const uint64 MaxDictSize=INT32TO64(1,0);
1051   // Limit the dictionary size to 4 GB.
1052   for (uint64 I=0x10000;I<=MaxDictSize;I*=2)
1053     if (WinSize==I)
1054       return true;
1055   WinSize=0x400000;
1056   return false;
1057 }
1058 #endif
1059 
1060 
1061 #ifndef SFX_MODULE
ReportWrongSwitches(RARFORMAT Format)1062 void CommandData::ReportWrongSwitches(RARFORMAT Format)
1063 {
1064   if (Format==RARFMT15)
1065   {
1066     if (HashType!=HASH_CRC32)
1067       uiMsg(UIERROR_INCOMPATSWITCH,L"-ht",4);
1068 #ifdef _WIN_ALL
1069     if (SaveSymLinks)
1070       uiMsg(UIERROR_INCOMPATSWITCH,L"-ol",4);
1071 #endif
1072     if (SaveHardLinks)
1073       uiMsg(UIERROR_INCOMPATSWITCH,L"-oh",4);
1074 
1075 #ifdef _WIN_ALL
1076     // Do not report a wrong dictionary size here, because we are not sure
1077     // yet about archive format. We can switch to RAR5 mode later
1078     // if we update RAR5 archive.
1079 
1080 
1081 #endif
1082     if (QOpenMode!=QOPEN_AUTO)
1083       uiMsg(UIERROR_INCOMPATSWITCH,L"-qo",4);
1084   }
1085   if (Format==RARFMT50)
1086   {
1087   }
1088 }
1089 #endif
1090