Coverage Report

Created: 2024-10-29 06:31

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