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