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