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