1// ArchiveCommandLine.cpp
2
3#include "StdAfx.h"
4#undef printf
5#undef sprintf
6
7#ifdef _WIN32
8#ifndef UNDER_CE
9#include <io.h>
10#endif
11#else
12// for isatty()
13#include <unistd.h>
14#endif
15
16#include <stdio.h>
17
18#ifdef Z7_LARGE_PAGES
19#include "../../../../C/Alloc.h"
20#endif
21
22#include "../../../Common/IntToString.h"
23#include "../../../Common/ListFileUtils.h"
24#include "../../../Common/StringConvert.h"
25#include "../../../Common/StringToInt.h"
26
27#include "../../../Windows/ErrorMsg.h"
28#include "../../../Windows/FileDir.h"
29#include "../../../Windows/FileName.h"
30#include "../../../Windows/System.h"
31#ifdef _WIN32
32#include "../../../Windows/FileMapping.h"
33#include "../../../Windows/MemoryLock.h"
34#include "../../../Windows/Synchronization.h"
35#endif
36
37#include "ArchiveCommandLine.h"
38#include "EnumDirItems.h"
39#include "Update.h"
40#include "UpdateAction.h"
41
42extern bool g_CaseSensitive;
43extern bool g_PathTrailReplaceMode;
44
45#ifdef Z7_LARGE_PAGES
46extern
47bool g_LargePagesMode;
48bool g_LargePagesMode = false;
49#endif
50
51/*
52#ifdef ENV_HAVE_LSTAT
53EXTERN_C_BEGIN
54extern int global_use_lstat;
55EXTERN_C_END
56#endif
57*/
58
59#ifdef UNDER_CE
60
61#define MY_IS_TERMINAL(x) false;
62
63#else
64
65// #define MY_isatty_fileno(x) (isatty(fileno(x)))
66// #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0);
67static inline bool MY_IS_TERMINAL(FILE *x)
68{
69  return (
70    #if defined(_MSC_VER) && (_MSC_VER >= 1400)
71      _isatty(_fileno(x))
72    #else
73      isatty(fileno(x))
74    #endif
75      != 0);
76}
77
78#endif
79
80using namespace NCommandLineParser;
81using namespace NWindows;
82using namespace NFile;
83
84static bool StringToUInt32(const wchar_t *s, UInt32 &v)
85{
86  if (*s == 0)
87    return false;
88  const wchar_t *end;
89  v = ConvertStringToUInt32(s, &end);
90  return *end == 0;
91}
92
93
94namespace NKey {
95enum Enum
96{
97  kHelp1 = 0,
98  kHelp2,
99  kHelp3,
100
101  kDisableHeaders,
102  kDisablePercents,
103  kShowTime,
104  kLogLevel,
105
106  kOutStream,
107  kErrStream,
108  kPercentStream,
109
110  kYes,
111
112  kShowDialog,
113  kOverwrite,
114
115  kArchiveType,
116  kExcludedArcType,
117
118  kProperty,
119  kOutputDir,
120  kWorkingDir,
121
122  kInclude,
123  kExclude,
124  kArInclude,
125  kArExclude,
126  kNoArName,
127
128  kUpdate,
129  kVolume,
130  kRecursed,
131
132  kAffinity,
133  kSfx,
134  kEmail,
135  kHash,
136  // kHashGenFile,
137  kHashDir,
138
139  kStdIn,
140  kStdOut,
141
142  kLargePages,
143  kListfileCharSet,
144  kConsoleCharSet,
145  kTechMode,
146  kListFields,
147
148  kPreserveATime,
149  kShareForWrite,
150  kStopAfterOpenError,
151  kCaseSensitive,
152  kArcNameMode,
153
154  kUseSlashMark,
155  kDisableWildcardParsing,
156  kElimDup,
157  kFullPathMode,
158
159  kHardLinks,
160  kSymLinks_AllowDangerous,
161  kSymLinks,
162  kNtSecurity,
163
164  kStoreOwnerId,
165  kStoreOwnerName,
166
167  kZoneFile,
168  kAltStreams,
169  kReplaceColonForAltStream,
170  kWriteToAltStreamIfColon,
171
172  kNameTrailReplace,
173
174  kDeleteAfterCompressing,
175  kSetArcMTime
176
177  #ifndef Z7_NO_CRYPTO
178  , kPassword
179  #endif
180};
181
182}
183
184
185static const wchar_t kRecursedIDChar = 'r';
186static const char * const kRecursedPostCharSet = "0-";
187
188static const char * const k_ArcNameMode_PostCharSet = "sea";
189
190static const char * const k_Stream_PostCharSet = "012";
191
192static inline EArcNameMode ParseArcNameMode(int postCharIndex)
193{
194  switch (postCharIndex)
195  {
196    case 1: return k_ArcNameMode_Exact;
197    case 2: return k_ArcNameMode_Add;
198    default: return k_ArcNameMode_Smart;
199  }
200}
201
202namespace NRecursedPostCharIndex {
203  enum EEnum
204  {
205    kWildcardRecursionOnly = 0,
206    kNoRecursion = 1
207  };
208}
209
210// static const char
211#define kImmediateNameID '!'
212#ifdef _WIN32
213#define kMapNameID '#'
214#endif
215#define kFileListID '@'
216
217static const Byte kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be
218static const Byte kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be
219
220static const char * const kOverwritePostCharSet = "asut";
221
222static const NExtract::NOverwriteMode::EEnum k_OverwriteModes[] =
223{
224  NExtract::NOverwriteMode::kOverwrite,
225  NExtract::NOverwriteMode::kSkip,
226  NExtract::NOverwriteMode::kRename,
227  NExtract::NOverwriteMode::kRenameExisting
228};
229
230
231
232#define SWFRM_3(t, mu, mi) t, mu, mi, NULL
233
234#define SWFRM_1(t) SWFRM_3(t, false, 0)
235#define SWFRM_SIMPLE SWFRM_1(NSwitchType::kSimple)
236#define SWFRM_MINUS  SWFRM_1(NSwitchType::kMinus)
237#define SWFRM_STRING SWFRM_1(NSwitchType::kString)
238
239#define SWFRM_STRING_SINGL(mi) SWFRM_3(NSwitchType::kString, false, mi)
240#define SWFRM_STRING_MULT(mi)  SWFRM_3(NSwitchType::kString, true, mi)
241
242
243static const CSwitchForm kSwitchForms[] =
244{
245  { "?", SWFRM_SIMPLE },
246  { "h", SWFRM_SIMPLE },
247  { "-help", SWFRM_SIMPLE },
248
249  { "ba", SWFRM_SIMPLE },
250  { "bd", SWFRM_SIMPLE },
251  { "bt", SWFRM_SIMPLE },
252  { "bb", SWFRM_STRING_SINGL(0) },
253
254  { "bso", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
255  { "bse", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
256  { "bsp", NSwitchType::kChar, false, 1, k_Stream_PostCharSet },
257
258  { "y", SWFRM_SIMPLE },
259
260  { "ad", SWFRM_SIMPLE },
261  { "ao", NSwitchType::kChar, false, 1, kOverwritePostCharSet},
262
263  { "t",  SWFRM_STRING_SINGL(1) },
264  { "stx", SWFRM_STRING_MULT(1) },
265
266  { "m",  SWFRM_STRING_MULT(1) },
267  { "o",  SWFRM_STRING_SINGL(1) },
268  { "w",  SWFRM_STRING },
269
270  { "i",  SWFRM_STRING_MULT(kSomeCludePostStringMinSize) },
271  { "x",  SWFRM_STRING_MULT(kSomeCludePostStringMinSize) },
272  { "ai", SWFRM_STRING_MULT(kSomeCludePostStringMinSize) },
273  { "ax", SWFRM_STRING_MULT(kSomeCludePostStringMinSize) },
274  { "an", SWFRM_SIMPLE },
275
276  { "u",  SWFRM_STRING_MULT(1) },
277  { "v",  SWFRM_STRING_MULT(1) },
278  { "r",  NSwitchType::kChar, false, 0, kRecursedPostCharSet },
279
280  { "stm", SWFRM_STRING },
281  { "sfx", SWFRM_STRING },
282  { "seml", SWFRM_STRING_SINGL(0) },
283  { "scrc", SWFRM_STRING_MULT(0) },
284  // { "scrf", SWFRM_STRING_SINGL(1) },
285  { "shd", SWFRM_STRING_SINGL(1) },
286
287  { "si", SWFRM_STRING },
288  { "so", SWFRM_SIMPLE },
289
290  { "slp", SWFRM_STRING },
291  { "scs", SWFRM_STRING },
292  { "scc", SWFRM_STRING },
293  { "slt", SWFRM_SIMPLE },
294  { "slf", SWFRM_STRING_SINGL(1) },
295
296  { "ssp", SWFRM_SIMPLE },
297  { "ssw", SWFRM_SIMPLE },
298  { "sse", SWFRM_SIMPLE },
299  { "ssc", SWFRM_MINUS },
300  { "sa",  NSwitchType::kChar, false, 1, k_ArcNameMode_PostCharSet },
301
302  { "spm", SWFRM_STRING_SINGL(0) },
303  { "spd", SWFRM_SIMPLE },
304  { "spe", SWFRM_MINUS },
305  { "spf", SWFRM_STRING_SINGL(0) },
306
307  { "snh", SWFRM_MINUS },
308  { "snld", SWFRM_MINUS },
309  { "snl", SWFRM_MINUS },
310  { "sni", SWFRM_SIMPLE },
311
312  { "snoi", SWFRM_MINUS },
313  { "snon", SWFRM_MINUS },
314
315  { "snz", SWFRM_STRING_SINGL(0) },
316  { "sns", SWFRM_MINUS },
317  { "snr", SWFRM_SIMPLE },
318  { "snc", SWFRM_SIMPLE },
319
320  { "snt", SWFRM_MINUS },
321
322  { "sdel", SWFRM_SIMPLE },
323  { "stl", SWFRM_SIMPLE }
324
325  #ifndef Z7_NO_CRYPTO
326  , { "p", SWFRM_STRING }
327  #endif
328};
329
330static const char * const kUniversalWildcard = "*";
331static const unsigned kMinNonSwitchWords = 1;
332static const unsigned kCommandIndex = 0;
333
334// static const char * const kUserErrorMessage  = "Incorrect command line";
335// static const char * const kCannotFindListFile = "Cannot find listfile";
336static const char * const kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch.";
337static const char * const kTerminalOutError = "I won't write compressed data to a terminal";
338static const char * const kSameTerminalError = "I won't write data and program's messages to same stream";
339static const char * const kEmptyFilePath = "Empty file path";
340
341bool CArcCommand::IsFromExtractGroup() const
342{
343  switch ((int)CommandType)
344  {
345    case NCommandType::kTest:
346    case NCommandType::kExtract:
347    case NCommandType::kExtractFull:
348      return true;
349    default:
350      return false;
351  }
352}
353
354NExtract::NPathMode::EEnum CArcCommand::GetPathMode() const
355{
356  switch ((int)CommandType)
357  {
358    case NCommandType::kTest:
359    case NCommandType::kExtractFull:
360      return NExtract::NPathMode::kFullPaths;
361    default:
362      return NExtract::NPathMode::kNoPaths;
363  }
364}
365
366bool CArcCommand::IsFromUpdateGroup() const
367{
368  switch ((int)CommandType)
369  {
370    case NCommandType::kAdd:
371    case NCommandType::kUpdate:
372    case NCommandType::kDelete:
373    case NCommandType::kRename:
374      return true;
375    default:
376      return false;
377  }
378}
379
380static NRecursedType::EEnum GetRecursedTypeFromIndex(int index)
381{
382  switch (index)
383  {
384    case NRecursedPostCharIndex::kWildcardRecursionOnly:
385      return NRecursedType::kWildcardOnlyRecursed;
386    case NRecursedPostCharIndex::kNoRecursion:
387      return NRecursedType::kNonRecursed;
388    default:
389      return NRecursedType::kRecursed;
390  }
391}
392
393static const char *g_Commands = "audtexlbih";
394
395static bool ParseArchiveCommand(const UString &commandString, CArcCommand &command)
396{
397  UString s (commandString);
398  s.MakeLower_Ascii();
399  if (s.Len() == 1)
400  {
401    if (s[0] > 0x7F)
402      return false;
403    int index = FindCharPosInString(g_Commands, (char)s[0]);
404    if (index < 0)
405      return false;
406    command.CommandType = (NCommandType::EEnum)index;
407    return true;
408  }
409  if (s.Len() == 2 && s[0] == 'r' && s[1] == 'n')
410  {
411    command.CommandType = (NCommandType::kRename);
412    return true;
413  }
414  return false;
415}
416
417// ------------------------------------------------------------------
418// filenames functions
419
420struct CNameOption
421{
422  bool Include;
423  bool WildcardMatching;
424  Byte MarkMode;
425  NRecursedType::EEnum RecursedType;
426
427  CNameOption():
428      Include(true),
429      WildcardMatching(true),
430      MarkMode(NWildcard::kMark_FileOrDir),
431      RecursedType(NRecursedType::kNonRecursed)
432      {}
433};
434
435
436static void AddNameToCensor(NWildcard::CCensor &censor,
437    const CNameOption &nop, const UString &name)
438{
439  bool recursed = false;
440
441  switch ((int)nop.RecursedType)
442  {
443    case NRecursedType::kWildcardOnlyRecursed:
444      recursed = DoesNameContainWildcard(name);
445      break;
446    case NRecursedType::kRecursed:
447      recursed = true;
448      break;
449    default:
450      break;
451  }
452
453  NWildcard::CCensorPathProps props;
454  props.Recursive = recursed;
455  props.WildcardMatching = nop.WildcardMatching;
456  props.MarkMode = nop.MarkMode;
457  censor.AddPreItem(nop.Include, name, props);
458}
459
460static void AddRenamePair(CObjectVector<CRenamePair> *renamePairs,
461    const UString &oldName, const UString &newName, NRecursedType::EEnum type,
462    bool wildcardMatching)
463{
464  CRenamePair &pair = renamePairs->AddNew();
465  pair.OldName = oldName;
466  pair.NewName = newName;
467  pair.RecursedType = type;
468  pair.WildcardParsing = wildcardMatching;
469
470  if (!pair.Prepare())
471  {
472    UString val;
473    val += pair.OldName;
474    val.Add_LF();
475    val += pair.NewName;
476    val.Add_LF();
477    if (type == NRecursedType::kRecursed)
478      val += "-r";
479    else if (type == NRecursedType::kWildcardOnlyRecursed)
480      val += "-r0";
481    throw CArcCmdLineException("Unsupported rename command:", val);
482  }
483}
484
485static void AddToCensorFromListFile(
486    CObjectVector<CRenamePair> *renamePairs,
487    NWildcard::CCensor &censor,
488    const CNameOption &nop, LPCWSTR fileName, UInt32 codePage)
489{
490  UStringVector names;
491  /*
492  if (!NFind::DoesFileExist_FollowLink(us2fs(fileName)))
493    throw CArcCmdLineException(kCannotFindListFile, fileName);
494  */
495  DWORD lastError = 0;
496  if (!ReadNamesFromListFile2(us2fs(fileName), names, codePage, lastError))
497  {
498    if (lastError != 0)
499    {
500      UString m;
501      m = "The file operation error for listfile";
502      m.Add_LF();
503      m += NError::MyFormatMessage(lastError);
504      throw CArcCmdLineException(m, fileName);
505    }
506    throw CArcCmdLineException(kIncorrectListFile, fileName);
507  }
508  if (renamePairs)
509  {
510    if ((names.Size() & 1) != 0)
511      throw CArcCmdLineException(kIncorrectListFile, fileName);
512    for (unsigned i = 0; i < names.Size(); i += 2)
513    {
514      // change type !!!!
515      AddRenamePair(renamePairs, names[i], names[i + 1], nop.RecursedType, nop.WildcardMatching);
516    }
517  }
518  else
519    FOR_VECTOR (i, names)
520      AddNameToCensor(censor, nop, names[i]);
521}
522
523static void AddToCensorFromNonSwitchesStrings(
524    CObjectVector<CRenamePair> *renamePairs,
525    unsigned startIndex,
526    NWildcard::CCensor &censor,
527    const UStringVector &nonSwitchStrings,
528    int stopSwitchIndex,
529    const CNameOption &nop,
530    bool thereAreSwitchIncludes, UInt32 codePage)
531{
532  // another default
533  if ((renamePairs || nonSwitchStrings.Size() == startIndex) && !thereAreSwitchIncludes)
534  {
535    /* for rename command: -i switch sets the mask for archive item reading.
536       if (thereAreSwitchIncludes), { we don't use UniversalWildcard. }
537       also for non-rename command: we set UniversalWildcard, only if there are no nonSwitches. */
538    // we use default fileds in (CNameOption) for UniversalWildcard.
539    CNameOption nop2;
540    // recursive mode is not important for UniversalWildcard (*)
541    // nop2.RecursedType = nop.RecursedType; // we don't need it
542    /*
543    nop2.RecursedType = NRecursedType::kNonRecursed;
544    nop2.Include = true;
545    nop2.WildcardMatching = true;
546    nop2.MarkMode = NWildcard::kMark_FileOrDir;
547    */
548    AddNameToCensor(censor, nop2, UString(kUniversalWildcard));
549  }
550
551  int oldIndex = -1;
552
553  if (stopSwitchIndex < 0)
554    stopSwitchIndex = (int)nonSwitchStrings.Size();
555
556  for (unsigned i = startIndex; i < nonSwitchStrings.Size(); i++)
557  {
558    const UString &s = nonSwitchStrings[i];
559    if (s.IsEmpty())
560      throw CArcCmdLineException(kEmptyFilePath);
561    if (i < (unsigned)stopSwitchIndex && s[0] == kFileListID)
562      AddToCensorFromListFile(renamePairs, censor, nop, s.Ptr(1), codePage);
563    else if (renamePairs)
564    {
565      if (oldIndex == -1)
566        oldIndex = (int)i;
567      else
568      {
569        // NRecursedType::EEnum type is used for global wildcard (-i! switches)
570        AddRenamePair(renamePairs, nonSwitchStrings[(unsigned)oldIndex], s, NRecursedType::kNonRecursed, nop.WildcardMatching);
571        // AddRenamePair(renamePairs, nonSwitchStrings[oldIndex], s, type);
572        oldIndex = -1;
573      }
574    }
575    else
576      AddNameToCensor(censor, nop, s);
577  }
578
579  if (oldIndex != -1)
580  {
581    throw CArcCmdLineException("There is no second file name for rename pair:", nonSwitchStrings[(unsigned)oldIndex]);
582  }
583}
584
585#ifdef _WIN32
586
587struct CEventSetEnd
588{
589  UString Name;
590
591  CEventSetEnd(const wchar_t *name): Name(name) {}
592  ~CEventSetEnd()
593  {
594    NSynchronization::CManualResetEvent event;
595    if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(Name)) == 0)
596      event.Set();
597  }
598};
599
600static const char * const k_IncorrectMapCommand = "Incorrect Map command";
601
602static const char *ParseMapWithPaths(
603    NWildcard::CCensor &censor,
604    const UString &s2,
605    const CNameOption &nop)
606{
607  UString s (s2);
608  int pos = s.Find(L':');
609  if (pos < 0)
610    return k_IncorrectMapCommand;
611  int pos2 = s.Find(L':', (unsigned)(pos + 1));
612  if (pos2 < 0)
613    return k_IncorrectMapCommand;
614
615  CEventSetEnd eventSetEnd((const wchar_t *)s + (unsigned)(pos2 + 1));
616  s.DeleteFrom((unsigned)pos2);
617  UInt32 size;
618  if (!StringToUInt32(s.Ptr((unsigned)(pos + 1)), size)
619      || size < sizeof(wchar_t)
620      || size > ((UInt32)1 << 31)
621      || size % sizeof(wchar_t) != 0)
622    return "Unsupported Map data size";
623
624  s.DeleteFrom((unsigned)pos);
625  CFileMapping map;
626  if (map.Open(FILE_MAP_READ, GetSystemString(s)) != 0)
627    return "Cannot open mapping";
628  LPVOID data = map.Map(FILE_MAP_READ, 0, size);
629  if (!data)
630    return "MapViewOfFile error";
631  CFileUnmapper unmapper(data);
632
633  UString name;
634  const wchar_t *p = (const wchar_t *)data;
635  if (*p != 0) // data format marker
636    return "Unsupported Map data";
637  UInt32 numChars = size / sizeof(wchar_t);
638  for (UInt32 i = 1; i < numChars; i++)
639  {
640    wchar_t c = p[i];
641    if (c == 0)
642    {
643      // MessageBoxW(0, name, L"7-Zip", 0);
644      AddNameToCensor(censor, nop, name);
645      name.Empty();
646    }
647    else
648      name += c;
649  }
650  if (!name.IsEmpty())
651    return "Map data error";
652
653  return NULL;
654}
655
656#endif
657
658static void AddSwitchWildcardsToCensor(
659    NWildcard::CCensor &censor,
660    const UStringVector &strings,
661    const CNameOption &nop,
662    UInt32 codePage)
663{
664  const char *errorMessage = NULL;
665  unsigned i;
666  for (i = 0; i < strings.Size(); i++)
667  {
668    const UString &name = strings[i];
669    unsigned pos = 0;
670
671    if (name.Len() < kSomeCludePostStringMinSize)
672    {
673      errorMessage = "Too short switch";
674      break;
675    }
676
677    if (!nop.Include)
678    {
679      if (name.IsEqualTo_Ascii_NoCase("td"))
680      {
681        censor.ExcludeDirItems = true;
682        continue;
683      }
684      if (name.IsEqualTo_Ascii_NoCase("tf"))
685      {
686        censor.ExcludeFileItems = true;
687        continue;
688      }
689    }
690
691    CNameOption nop2 = nop;
692
693    bool type_WasUsed = false;
694    bool recursed_WasUsed = false;
695    bool matching_WasUsed = false;
696    bool error = false;
697
698    for (;;)
699    {
700      wchar_t c = ::MyCharLower_Ascii(name[pos]);
701      if (c == kRecursedIDChar)
702      {
703        if (recursed_WasUsed)
704        {
705          error = true;
706          break;
707        }
708        recursed_WasUsed = true;
709        pos++;
710        c = name[pos];
711        int index = -1;
712        if (c <= 0x7F)
713          index = FindCharPosInString(kRecursedPostCharSet, (char)c);
714        nop2.RecursedType = GetRecursedTypeFromIndex(index);
715        if (index >= 0)
716        {
717          pos++;
718          continue;
719        }
720      }
721
722      if (c == 'w')
723      {
724        if (matching_WasUsed)
725        {
726          error = true;
727          break;
728        }
729        matching_WasUsed = true;
730        nop2.WildcardMatching = true;
731        pos++;
732        if (name[pos] == '-')
733        {
734          nop2.WildcardMatching = false;
735          pos++;
736        }
737      }
738      else if (c == 'm')
739      {
740        if (type_WasUsed)
741        {
742          error = true;
743          break;
744        }
745        type_WasUsed = true;
746        pos++;
747        nop2.MarkMode = NWildcard::kMark_StrictFile;
748        c = name[pos];
749        if (c == '-')
750        {
751          nop2.MarkMode = NWildcard::kMark_FileOrDir;
752          pos++;
753        }
754        else if (c == '2')
755        {
756          nop2.MarkMode = NWildcard::kMark_StrictFile_IfWildcard;
757          pos++;
758        }
759      }
760      else
761        break;
762    }
763
764    if (error)
765    {
766      errorMessage = "inorrect switch";
767      break;
768    }
769
770    if (name.Len() < pos + kSomeCludeAfterRecursedPostStringMinSize)
771    {
772      errorMessage = "Too short switch";
773      break;
774    }
775
776    const UString tail = name.Ptr(pos + 1);
777
778    const wchar_t c = name[pos];
779
780    if (c == kImmediateNameID)
781      AddNameToCensor(censor, nop2, tail);
782    else if (c == kFileListID)
783      AddToCensorFromListFile(NULL, censor, nop2, tail, codePage);
784    #ifdef _WIN32
785    else if (c == kMapNameID)
786    {
787      errorMessage = ParseMapWithPaths(censor, tail, nop2);
788      if (errorMessage)
789        break;
790    }
791    #endif
792    else
793    {
794      errorMessage = "Incorrect wildcard type marker";
795      break;
796    }
797  }
798
799  if (i != strings.Size())
800    throw CArcCmdLineException(errorMessage, strings[i]);
801}
802
803/*
804static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i)
805{
806  switch (i)
807  {
808    case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore;
809    case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy;
810    case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress;
811    case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti;
812  }
813  throw 98111603;
814}
815*/
816
817static const char * const kUpdatePairStateIDSet = "pqrxyzw";
818static const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1};
819
820static const unsigned kNumUpdatePairActions = 4;
821static const char * const kUpdateIgnoreItselfPostStringID = "-";
822static const wchar_t kUpdateNewArchivePostCharID = '!';
823
824
825static bool ParseUpdateCommandString2(const UString &command,
826    NUpdateArchive::CActionSet &actionSet, UString &postString)
827{
828  for (unsigned i = 0; i < command.Len();)
829  {
830    wchar_t c = MyCharLower_Ascii(command[i]);
831    int statePos = FindCharPosInString(kUpdatePairStateIDSet, (char)c);
832    if (c > 0x7F || statePos < 0)
833    {
834      postString = command.Ptr(i);
835      return true;
836    }
837    i++;
838    if (i >= command.Len())
839      return false;
840    c = command[i];
841    if (c < '0' || c >= (wchar_t)('0' + kNumUpdatePairActions))
842      return false;
843    unsigned actionPos = (unsigned)(c - '0');
844    actionSet.StateActions[(unsigned)statePos] = (NUpdateArchive::NPairAction::EEnum)(actionPos);
845    if (kUpdatePairStateNotSupportedActions[(unsigned)statePos] == (int)actionPos)
846      return false;
847    i++;
848  }
849  postString.Empty();
850  return true;
851}
852
853static void ParseUpdateCommandString(CUpdateOptions &options,
854    const UStringVector &updatePostStrings,
855    const NUpdateArchive::CActionSet &defaultActionSet)
856{
857  const char *errorMessage = "incorrect update switch command";
858  unsigned i;
859  for (i = 0; i < updatePostStrings.Size(); i++)
860  {
861    const UString &updateString = updatePostStrings[i];
862    if (updateString.IsEqualTo(kUpdateIgnoreItselfPostStringID))
863    {
864      if (options.UpdateArchiveItself)
865      {
866        options.UpdateArchiveItself = false;
867        options.Commands.Delete(0);
868      }
869    }
870    else
871    {
872      NUpdateArchive::CActionSet actionSet = defaultActionSet;
873
874      UString postString;
875      if (!ParseUpdateCommandString2(updateString, actionSet, postString))
876        break;
877      if (postString.IsEmpty())
878      {
879        if (options.UpdateArchiveItself)
880          options.Commands[0].ActionSet = actionSet;
881      }
882      else
883      {
884        if (postString[0] != kUpdateNewArchivePostCharID)
885          break;
886        CUpdateArchiveCommand uc;
887        UString archivePath = postString.Ptr(1);
888        if (archivePath.IsEmpty())
889          break;
890        uc.UserArchivePath = archivePath;
891        uc.ActionSet = actionSet;
892        options.Commands.Add(uc);
893      }
894    }
895  }
896  if (i != updatePostStrings.Size())
897    throw CArcCmdLineException(errorMessage, updatePostStrings[i]);
898}
899
900bool ParseComplexSize(const wchar_t *s, UInt64 &result);
901
902static void SetAddCommandOptions(
903    NCommandType::EEnum commandType,
904    const CParser &parser,
905    CUpdateOptions &options)
906{
907  NUpdateArchive::CActionSet defaultActionSet;
908  switch ((int)commandType)
909  {
910    case NCommandType::kAdd:
911      defaultActionSet = NUpdateArchive::k_ActionSet_Add;
912      break;
913    case NCommandType::kDelete:
914      defaultActionSet = NUpdateArchive::k_ActionSet_Delete;
915      break;
916    default:
917      defaultActionSet = NUpdateArchive::k_ActionSet_Update;
918  }
919
920  options.UpdateArchiveItself = true;
921
922  options.Commands.Clear();
923  CUpdateArchiveCommand updateMainCommand;
924  updateMainCommand.ActionSet = defaultActionSet;
925  options.Commands.Add(updateMainCommand);
926  if (parser[NKey::kUpdate].ThereIs)
927    ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings,
928        defaultActionSet);
929  if (parser[NKey::kWorkingDir].ThereIs)
930  {
931    const UString &postString = parser[NKey::kWorkingDir].PostStrings[0];
932    if (postString.IsEmpty())
933      NDir::MyGetTempPath(options.WorkingDir);
934    else
935      options.WorkingDir = us2fs(postString);
936  }
937  options.SfxMode = parser[NKey::kSfx].ThereIs;
938  if (options.SfxMode)
939    options.SfxModule = us2fs(parser[NKey::kSfx].PostStrings[0]);
940
941  if (parser[NKey::kVolume].ThereIs)
942  {
943    const UStringVector &sv = parser[NKey::kVolume].PostStrings;
944    FOR_VECTOR (i, sv)
945    {
946      UInt64 size;
947      if (!ParseComplexSize(sv[i], size))
948        throw CArcCmdLineException("Incorrect volume size:", sv[i]);
949      if (i == sv.Size() - 1 && size == 0)
950        throw CArcCmdLineException("zero size last volume is not allowed");
951      options.VolumesSizes.Add(size);
952    }
953  }
954}
955
956static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties)
957{
958  if (parser[NKey::kProperty].ThereIs)
959  {
960    FOR_VECTOR (i, parser[NKey::kProperty].PostStrings)
961    {
962      CProperty prop;
963      prop.Name = parser[NKey::kProperty].PostStrings[i];
964      int index = prop.Name.Find(L'=');
965      if (index >= 0)
966      {
967        prop.Value = prop.Name.Ptr((unsigned)(index + 1));
968        prop.Name.DeleteFrom((unsigned)index);
969      }
970      properties.Add(prop);
971    }
972  }
973}
974
975
976static inline void SetStreamMode(const CSwitchResult &sw, unsigned &res)
977{
978  if (sw.ThereIs)
979    res = (unsigned)sw.PostCharIndex;
980}
981
982
983#if defined(_WIN32) && !defined(UNDER_CE)
984static void PrintHex(UString &s, UInt64 v)
985{
986  char temp[32];
987  ConvertUInt64ToHex(v, temp);
988  s += temp;
989}
990#endif
991
992
993void CArcCmdLineParser::Parse1(const UStringVector &commandStrings,
994    CArcCmdLineOptions &options)
995{
996  Parse1Log.Empty();
997  if (!parser.ParseStrings(kSwitchForms, Z7_ARRAY_SIZE(kSwitchForms), commandStrings))
998    throw CArcCmdLineException(parser.ErrorMessage, parser.ErrorLine);
999
1000  options.IsInTerminal = MY_IS_TERMINAL(stdin);
1001  options.IsStdOutTerminal = MY_IS_TERMINAL(stdout);
1002  options.IsStdErrTerminal = MY_IS_TERMINAL(stderr);
1003
1004  options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs  || parser[NKey::kHelp3].ThereIs;
1005
1006  options.StdInMode = parser[NKey::kStdIn].ThereIs;
1007  options.StdOutMode = parser[NKey::kStdOut].ThereIs;
1008  options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs;
1009  if (parser[NKey::kListFields].ThereIs)
1010  {
1011    const UString &s = parser[NKey::kListFields].PostStrings[0];
1012    options.ListFields = GetAnsiString(s);
1013  }
1014  options.TechMode = parser[NKey::kTechMode].ThereIs;
1015  options.ShowTime = parser[NKey::kShowTime].ThereIs;
1016
1017  if (parser[NKey::kDisablePercents].ThereIs
1018      || options.StdOutMode
1019      || !options.IsStdOutTerminal)
1020    options.Number_for_Percents = k_OutStream_disabled;
1021
1022  if (options.StdOutMode)
1023    options.Number_for_Out = k_OutStream_disabled;
1024
1025  SetStreamMode(parser[NKey::kOutStream], options.Number_for_Out);
1026  SetStreamMode(parser[NKey::kErrStream], options.Number_for_Errors);
1027  SetStreamMode(parser[NKey::kPercentStream], options.Number_for_Percents);
1028
1029  if (parser[NKey::kLogLevel].ThereIs)
1030  {
1031    const UString &s = parser[NKey::kLogLevel].PostStrings[0];
1032    if (s.IsEmpty())
1033      options.LogLevel = 1;
1034    else
1035    {
1036      UInt32 v;
1037      if (!StringToUInt32(s, v))
1038        throw CArcCmdLineException("Unsupported switch postfix -bb", s);
1039      options.LogLevel = (unsigned)v;
1040    }
1041  }
1042
1043  if (parser[NKey::kCaseSensitive].ThereIs)
1044  {
1045    options.CaseSensitive =
1046    g_CaseSensitive = !parser[NKey::kCaseSensitive].WithMinus;
1047    options.CaseSensitive_Change = true;
1048  }
1049
1050
1051  #if defined(_WIN32) && !defined(UNDER_CE)
1052  NSecurity::EnablePrivilege_SymLink();
1053  #endif
1054
1055  // options.LargePages = false;
1056
1057  if (parser[NKey::kLargePages].ThereIs)
1058  {
1059    UInt32 slp = 0;
1060    const UString &s = parser[NKey::kLargePages].PostStrings[0];
1061    if (s.IsEmpty())
1062      slp = 1;
1063    else if (s != L"-")
1064    {
1065      if (!StringToUInt32(s, slp))
1066        throw CArcCmdLineException("Unsupported switch postfix for -slp", s);
1067    }
1068
1069    #ifdef Z7_LARGE_PAGES
1070    if (slp >
1071          #if defined(_WIN32) && !defined(UNDER_CE)
1072            (unsigned)NSecurity::Get_LargePages_RiskLevel()
1073          #else
1074            0
1075          #endif
1076        )
1077    {
1078      #ifdef _WIN32 // change it !
1079      SetLargePageSize();
1080      #endif
1081      // note: this process also can inherit that Privilege from parent process
1082      g_LargePagesMode =
1083      #if defined(_WIN32) && !defined(UNDER_CE)
1084        NSecurity::EnablePrivilege_LockMemory();
1085      #else
1086        true;
1087      #endif
1088    }
1089    #endif
1090  }
1091
1092
1093  #ifndef UNDER_CE
1094
1095  if (parser[NKey::kAffinity].ThereIs)
1096  {
1097    const UString &s = parser[NKey::kAffinity].PostStrings[0];
1098    if (!s.IsEmpty())
1099    {
1100      AString a;
1101      a.SetFromWStr_if_Ascii(s);
1102      Parse1Log += "Set process affinity mask: ";
1103
1104      #ifdef _WIN32
1105
1106      UInt64 v = 0;
1107      {
1108        const char *end;
1109        v = ConvertHexStringToUInt64(a, &end);
1110        if (*end != 0)
1111          a.Empty();
1112      }
1113      if (a.IsEmpty())
1114        throw CArcCmdLineException("Unsupported switch postfix -stm", s);
1115
1116      {
1117        #ifndef _WIN64
1118        if (v >= ((UInt64)1 << 32))
1119          throw CArcCmdLineException("unsupported value -stm", s);
1120        #endif
1121        {
1122          PrintHex(Parse1Log, v);
1123          if (!SetProcessAffinityMask(GetCurrentProcess(), (DWORD_PTR)v))
1124          {
1125            DWORD lastError = GetLastError();
1126            Parse1Log += " : ERROR : ";
1127            Parse1Log += NError::MyFormatMessage(lastError);
1128          }
1129        }
1130      }
1131
1132      #else // _WIN32
1133
1134      {
1135        Parse1Log += a;
1136        NSystem::CProcessAffinity aff;
1137        aff.CpuZero();
1138        for (unsigned i = 0; i < a.Len(); i++)
1139        {
1140          char c = a[i];
1141          unsigned v;
1142               if (c >= '0' && c <= '9') v =      (unsigned)(c - '0');
1143          else if (c >= 'A' && c <= 'F') v = 10 + (unsigned)(c - 'A');
1144          else if (c >= 'a' && c <= 'f') v = 10 + (unsigned)(c - 'a');
1145          else
1146            throw CArcCmdLineException("Unsupported switch postfix -stm", s);
1147          for (unsigned k = 0; k < 4; k++)
1148          {
1149            const unsigned cpu = (a.Len() - 1 - i) * 4 + k;
1150            if (v & ((unsigned)1 << k))
1151              aff.CpuSet(cpu);
1152          }
1153        }
1154
1155        if (!aff.SetProcAffinity())
1156        {
1157          DWORD lastError = GetLastError();
1158          Parse1Log += " : ERROR : ";
1159          Parse1Log += NError::MyFormatMessage(lastError);
1160        }
1161      }
1162      #endif // _WIN32
1163
1164      Parse1Log.Add_LF();
1165    }
1166  }
1167
1168  #endif
1169}
1170
1171
1172
1173struct CCodePagePair
1174{
1175  const char *Name;
1176  UInt32 CodePage;
1177};
1178
1179static const unsigned kNumByteOnlyCodePages = 3;
1180
1181static const CCodePagePair g_CodePagePairs[] =
1182{
1183  { "utf-8", CP_UTF8 },
1184  { "win", CP_ACP },
1185  { "dos", CP_OEMCP },
1186  { "utf-16le", Z7_WIN_CP_UTF16 },
1187  { "utf-16be", Z7_WIN_CP_UTF16BE }
1188};
1189
1190static Int32 FindCharset(const NCommandLineParser::CParser &parser, unsigned keyIndex,
1191    bool byteOnlyCodePages, Int32 defaultVal)
1192{
1193  if (!parser[keyIndex].ThereIs)
1194    return defaultVal;
1195
1196  UString name (parser[keyIndex].PostStrings.Back());
1197  UInt32 v;
1198  if (StringToUInt32(name, v))
1199    if (v < ((UInt32)1 << 16))
1200      return (Int32)v;
1201  name.MakeLower_Ascii();
1202  const unsigned num = byteOnlyCodePages ? kNumByteOnlyCodePages : Z7_ARRAY_SIZE(g_CodePagePairs);
1203  for (unsigned i = 0;; i++)
1204  {
1205    if (i == num) // to disable warnings from different compilers
1206      throw CArcCmdLineException("Unsupported charset:", name);
1207    const CCodePagePair &pair = g_CodePagePairs[i];
1208    if (name.IsEqualTo(pair.Name))
1209      return (Int32)pair.CodePage;
1210  }
1211}
1212
1213
1214static void SetBoolPair(NCommandLineParser::CParser &parser, unsigned switchID, CBoolPair &bp)
1215{
1216  bp.Def = parser[switchID].ThereIs;
1217  if (bp.Def)
1218    bp.Val = !parser[switchID].WithMinus;
1219}
1220
1221void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options)
1222{
1223  const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
1224  const unsigned numNonSwitchStrings = nonSwitchStrings.Size();
1225  if (numNonSwitchStrings < kMinNonSwitchWords)
1226    throw CArcCmdLineException("The command must be specified");
1227
1228  if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command))
1229    throw CArcCmdLineException("Unsupported command:", nonSwitchStrings[kCommandIndex]);
1230
1231  if (parser[NKey::kHash].ThereIs)
1232    options.HashMethods = parser[NKey::kHash].PostStrings;
1233
1234  /*
1235  if (parser[NKey::kHashGenFile].ThereIs)
1236  {
1237    const UString &s = parser[NKey::kHashGenFile].PostStrings[0];
1238    for (unsigned i = 0 ; i < s.Len();)
1239    {
1240      const wchar_t c = s[i++];
1241      if (!options.HashOptions.ParseFlagCharOption(c, true))
1242      {
1243        if (c != '=')
1244          throw CArcCmdLineException("Unsupported hash mode switch:", s);
1245        options.HashOptions.HashFilePath = s.Ptr(i);
1246        break;
1247      }
1248    }
1249  }
1250  */
1251
1252  if (parser[NKey::kHashDir].ThereIs)
1253    options.ExtractOptions.HashDir = parser[NKey::kHashDir].PostStrings[0];
1254
1255  if (parser[NKey::kElimDup].ThereIs)
1256  {
1257    options.ExtractOptions.ElimDup.Def = true;
1258    options.ExtractOptions.ElimDup.Val = !parser[NKey::kElimDup].WithMinus;
1259  }
1260
1261  NWildcard::ECensorPathMode censorPathMode = NWildcard::k_RelatPath;
1262  bool fullPathMode = parser[NKey::kFullPathMode].ThereIs;
1263  if (fullPathMode)
1264  {
1265    censorPathMode = NWildcard::k_AbsPath;
1266    const UString &s = parser[NKey::kFullPathMode].PostStrings[0];
1267    if (!s.IsEmpty())
1268    {
1269      if (s == L"2")
1270        censorPathMode = NWildcard::k_FullPath;
1271      else
1272        throw CArcCmdLineException("Unsupported -spf:", s);
1273    }
1274  }
1275
1276  if (parser[NKey::kNameTrailReplace].ThereIs)
1277    g_PathTrailReplaceMode = !parser[NKey::kNameTrailReplace].WithMinus;
1278
1279  CNameOption nop;
1280
1281  if (parser[NKey::kRecursed].ThereIs)
1282    nop.RecursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex);
1283
1284  if (parser[NKey::kDisableWildcardParsing].ThereIs)
1285    nop.WildcardMatching = false;
1286
1287  if (parser[NKey::kUseSlashMark].ThereIs)
1288  {
1289    const UString &s = parser[NKey::kUseSlashMark].PostStrings[0];
1290    if (s.IsEmpty())
1291      nop.MarkMode = NWildcard::kMark_StrictFile;
1292    else if (s.IsEqualTo_Ascii_NoCase("-"))
1293      nop.MarkMode = NWildcard::kMark_FileOrDir;
1294    else if (s.IsEqualTo_Ascii_NoCase("2"))
1295      nop.MarkMode = NWildcard::kMark_StrictFile_IfWildcard;
1296    else
1297      throw CArcCmdLineException("Unsupported -spm:", s);
1298  }
1299
1300
1301  options.ConsoleCodePage = FindCharset(parser, NKey::kConsoleCharSet, true, -1);
1302
1303  UInt32 codePage = (UInt32)FindCharset(parser, NKey::kListfileCharSet, false, CP_UTF8);
1304
1305  bool thereAreSwitchIncludes = false;
1306
1307  if (parser[NKey::kInclude].ThereIs)
1308  {
1309    thereAreSwitchIncludes = true;
1310    nop.Include = true;
1311    AddSwitchWildcardsToCensor(options.Censor,
1312        parser[NKey::kInclude].PostStrings, nop, codePage);
1313  }
1314
1315  if (parser[NKey::kExclude].ThereIs)
1316  {
1317    nop.Include = false;
1318    AddSwitchWildcardsToCensor(options.Censor,
1319        parser[NKey::kExclude].PostStrings, nop, codePage);
1320  }
1321
1322  unsigned curCommandIndex = kCommandIndex + 1;
1323  bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs &&
1324      options.Command.CommandType != NCommandType::kBenchmark &&
1325      options.Command.CommandType != NCommandType::kInfo &&
1326      options.Command.CommandType != NCommandType::kHash;
1327
1328  const bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
1329  const bool isExtractOrList = isExtractGroupCommand || options.Command.CommandType == NCommandType::kList;
1330  const bool isRename = options.Command.CommandType == NCommandType::kRename;
1331
1332  if ((isExtractOrList || isRename) && options.StdInMode)
1333    thereIsArchiveName = false;
1334
1335  if (parser[NKey::kArcNameMode].ThereIs)
1336    options.UpdateOptions.ArcNameMode = ParseArcNameMode(parser[NKey::kArcNameMode].PostCharIndex);
1337
1338  if (thereIsArchiveName)
1339  {
1340    if (curCommandIndex >= numNonSwitchStrings)
1341      throw CArcCmdLineException("Cannot find archive name");
1342    options.ArchiveName = nonSwitchStrings[curCommandIndex++];
1343    if (options.ArchiveName.IsEmpty())
1344      throw CArcCmdLineException("Archive name cannot by empty");
1345    #ifdef _WIN32
1346    // options.ArchiveName.Replace(L'/', WCHAR_PATH_SEPARATOR);
1347    #endif
1348  }
1349
1350  nop.Include = true;
1351  AddToCensorFromNonSwitchesStrings(isRename ? &options.UpdateOptions.RenamePairs : NULL,
1352      curCommandIndex, options.Censor,
1353      nonSwitchStrings, parser.StopSwitchIndex,
1354      nop,
1355      thereAreSwitchIncludes, codePage);
1356
1357  options.YesToAll = parser[NKey::kYes].ThereIs;
1358
1359
1360  #ifndef Z7_NO_CRYPTO
1361  options.PasswordEnabled = parser[NKey::kPassword].ThereIs;
1362  if (options.PasswordEnabled)
1363    options.Password = parser[NKey::kPassword].PostStrings[0];
1364  #endif
1365
1366  options.ShowDialog = parser[NKey::kShowDialog].ThereIs;
1367
1368  if (parser[NKey::kArchiveType].ThereIs)
1369    options.ArcType = parser[NKey::kArchiveType].PostStrings[0];
1370
1371  options.ExcludedArcTypes = parser[NKey::kExcludedArcType].PostStrings;
1372
1373  SetMethodOptions(parser, options.Properties);
1374
1375  if (parser[NKey::kNtSecurity].ThereIs) options.NtSecurity.SetTrueTrue();
1376
1377  SetBoolPair(parser, NKey::kAltStreams, options.AltStreams);
1378  SetBoolPair(parser, NKey::kHardLinks, options.HardLinks);
1379  SetBoolPair(parser, NKey::kSymLinks, options.SymLinks);
1380
1381  SetBoolPair(parser, NKey::kStoreOwnerId, options.StoreOwnerId);
1382  SetBoolPair(parser, NKey::kStoreOwnerName, options.StoreOwnerName);
1383
1384  CBoolPair symLinks_AllowDangerous;
1385  SetBoolPair(parser, NKey::kSymLinks_AllowDangerous, symLinks_AllowDangerous);
1386
1387
1388  /*
1389  bool supportSymLink = options.SymLinks.Val;
1390
1391  if (!options.SymLinks.Def)
1392  {
1393    if (isExtractOrList)
1394      supportSymLink = true;
1395    else
1396      supportSymLink = false;
1397  }
1398
1399  #ifdef ENV_HAVE_LSTAT
1400  if (supportSymLink)
1401    global_use_lstat = 1;
1402  else
1403    global_use_lstat = 0;
1404  #endif
1405  */
1406
1407
1408  if (isExtractOrList)
1409  {
1410    CExtractOptionsBase &eo = options.ExtractOptions;
1411
1412    eo.ExcludeDirItems = options.Censor.ExcludeDirItems;
1413    eo.ExcludeFileItems = options.Censor.ExcludeFileItems;
1414
1415    {
1416      CExtractNtOptions &nt = eo.NtOptions;
1417      nt.NtSecurity = options.NtSecurity;
1418
1419      nt.AltStreams = options.AltStreams;
1420      if (!options.AltStreams.Def)
1421        nt.AltStreams.Val = true;
1422
1423      nt.HardLinks = options.HardLinks;
1424      if (!options.HardLinks.Def)
1425        nt.HardLinks.Val = true;
1426
1427      nt.SymLinks = options.SymLinks;
1428      if (!options.SymLinks.Def)
1429        nt.SymLinks.Val = true;
1430
1431      nt.SymLinks_AllowDangerous = symLinks_AllowDangerous;
1432
1433      nt.ReplaceColonForAltStream = parser[NKey::kReplaceColonForAltStream].ThereIs;
1434      nt.WriteToAltStreamIfColon = parser[NKey::kWriteToAltStreamIfColon].ThereIs;
1435
1436      nt.ExtractOwner = options.StoreOwnerId.Val; // StoreOwnerName
1437
1438      if (parser[NKey::kPreserveATime].ThereIs)
1439        nt.PreserveATime = true;
1440      if (parser[NKey::kShareForWrite].ThereIs)
1441        nt.OpenShareForWrite = true;
1442    }
1443
1444    if (parser[NKey::kZoneFile].ThereIs)
1445    {
1446      eo.ZoneMode = NExtract::NZoneIdMode::kAll;
1447      const UString &s = parser[NKey::kZoneFile].PostStrings[0];
1448      if (!s.IsEmpty())
1449      {
1450             if (s == L"0") eo.ZoneMode = NExtract::NZoneIdMode::kNone;
1451        else if (s == L"1") eo.ZoneMode = NExtract::NZoneIdMode::kAll;
1452        else if (s == L"2") eo.ZoneMode = NExtract::NZoneIdMode::kOffice;
1453        else
1454          throw CArcCmdLineException("Unsupported -snz:", s);
1455      }
1456    }
1457
1458    options.Censor.AddPathsToCensor(NWildcard::k_AbsPath);
1459    options.Censor.ExtendExclude();
1460
1461    // are there paths that look as non-relative (!Prefix.IsEmpty())
1462    if (!options.Censor.AllAreRelative())
1463      throw CArcCmdLineException("Cannot use absolute pathnames for this command");
1464
1465    NWildcard::CCensor &arcCensor = options.arcCensor;
1466
1467    CNameOption nopArc;
1468    // nopArc.RecursedType = NRecursedType::kNonRecursed; // default:  we don't want recursing for archives, if -r specified
1469    // is it OK, external switches can disable WildcardMatching and MarcMode for arc.
1470    nopArc.WildcardMatching = nop.WildcardMatching;
1471    nopArc.MarkMode = nop.MarkMode;
1472
1473    if (parser[NKey::kArInclude].ThereIs)
1474    {
1475      nopArc.Include = true;
1476      AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArInclude].PostStrings, nopArc, codePage);
1477    }
1478    if (parser[NKey::kArExclude].ThereIs)
1479    {
1480      nopArc.Include = false;
1481      AddSwitchWildcardsToCensor(arcCensor, parser[NKey::kArExclude].PostStrings, nopArc, codePage);
1482    }
1483
1484    if (thereIsArchiveName)
1485    {
1486      nopArc.Include = true;
1487      AddNameToCensor(arcCensor, nopArc, options.ArchiveName);
1488    }
1489
1490    arcCensor.AddPathsToCensor(NWildcard::k_RelatPath);
1491
1492    #ifdef _WIN32
1493    ConvertToLongNames(arcCensor);
1494    #endif
1495
1496    arcCensor.ExtendExclude();
1497
1498    if (options.StdInMode)
1499      options.ArcName_for_StdInMode = parser[NKey::kStdIn].PostStrings.Front();
1500
1501    if (isExtractGroupCommand)
1502    {
1503      if (options.StdOutMode)
1504      {
1505        if (
1506                  options.Number_for_Percents == k_OutStream_stdout
1507            // || options.Number_for_Out      == k_OutStream_stdout
1508            // || options.Number_for_Errors   == k_OutStream_stdout
1509            ||
1510            (
1511              (options.IsStdOutTerminal && options.IsStdErrTerminal)
1512              &&
1513              (
1514                      options.Number_for_Percents != k_OutStream_disabled
1515                // || options.Number_for_Out      != k_OutStream_disabled
1516                // || options.Number_for_Errors   != k_OutStream_disabled
1517              )
1518            )
1519           )
1520          throw CArcCmdLineException(kSameTerminalError);
1521      }
1522
1523      if (parser[NKey::kOutputDir].ThereIs)
1524      {
1525        eo.OutputDir = us2fs(parser[NKey::kOutputDir].PostStrings[0]);
1526        #ifdef _WIN32
1527          NFile::NName::NormalizeDirSeparators(eo.OutputDir);
1528        #endif
1529        NFile::NName::NormalizeDirPathPrefix(eo.OutputDir);
1530      }
1531
1532      eo.OverwriteMode = NExtract::NOverwriteMode::kAsk;
1533      if (parser[NKey::kOverwrite].ThereIs)
1534      {
1535        eo.OverwriteMode = k_OverwriteModes[(unsigned)parser[NKey::kOverwrite].PostCharIndex];
1536        eo.OverwriteMode_Force = true;
1537      }
1538      else if (options.YesToAll)
1539      {
1540        eo.OverwriteMode = NExtract::NOverwriteMode::kOverwrite;
1541        eo.OverwriteMode_Force = true;
1542      }
1543    }
1544
1545    eo.PathMode = options.Command.GetPathMode();
1546    if (censorPathMode == NWildcard::k_AbsPath)
1547    {
1548      eo.PathMode = NExtract::NPathMode::kAbsPaths;
1549      eo.PathMode_Force = true;
1550    }
1551    else if (censorPathMode == NWildcard::k_FullPath)
1552    {
1553      eo.PathMode = NExtract::NPathMode::kFullPaths;
1554      eo.PathMode_Force = true;
1555    }
1556  }
1557  else if (options.Command.IsFromUpdateGroup())
1558  {
1559    if (parser[NKey::kArInclude].ThereIs)
1560      throw CArcCmdLineException("-ai switch is not supported for this command");
1561
1562    CUpdateOptions &updateOptions = options.UpdateOptions;
1563
1564    SetAddCommandOptions(options.Command.CommandType, parser, updateOptions);
1565
1566    updateOptions.MethodMode.Properties = options.Properties;
1567
1568    if (parser[NKey::kPreserveATime].ThereIs)
1569      updateOptions.PreserveATime = true;
1570    if (parser[NKey::kShareForWrite].ThereIs)
1571      updateOptions.OpenShareForWrite = true;
1572    if (parser[NKey::kStopAfterOpenError].ThereIs)
1573      updateOptions.StopAfterOpenError = true;
1574
1575    updateOptions.PathMode = censorPathMode;
1576
1577    updateOptions.AltStreams = options.AltStreams;
1578    updateOptions.NtSecurity = options.NtSecurity;
1579    updateOptions.HardLinks = options.HardLinks;
1580    updateOptions.SymLinks = options.SymLinks;
1581
1582    updateOptions.StoreOwnerId = options.StoreOwnerId;
1583    updateOptions.StoreOwnerName = options.StoreOwnerName;
1584
1585    updateOptions.EMailMode = parser[NKey::kEmail].ThereIs;
1586    if (updateOptions.EMailMode)
1587    {
1588      updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front();
1589      if (updateOptions.EMailAddress.Len() > 0)
1590        if (updateOptions.EMailAddress[0] == L'.')
1591        {
1592          updateOptions.EMailRemoveAfter = true;
1593          updateOptions.EMailAddress.Delete(0);
1594        }
1595    }
1596
1597    updateOptions.StdOutMode = options.StdOutMode;
1598    updateOptions.StdInMode = options.StdInMode;
1599
1600    updateOptions.DeleteAfterCompressing = parser[NKey::kDeleteAfterCompressing].ThereIs;
1601    updateOptions.SetArcMTime = parser[NKey::kSetArcMTime].ThereIs;
1602
1603    if (updateOptions.StdOutMode && updateOptions.EMailMode)
1604      throw CArcCmdLineException("stdout mode and email mode cannot be combined");
1605
1606    if (updateOptions.StdOutMode)
1607    {
1608      if (options.IsStdOutTerminal)
1609        throw CArcCmdLineException(kTerminalOutError);
1610
1611      if (options.Number_for_Percents == k_OutStream_stdout
1612          || options.Number_for_Out == k_OutStream_stdout
1613          || options.Number_for_Errors == k_OutStream_stdout)
1614        throw CArcCmdLineException(kSameTerminalError);
1615    }
1616
1617    if (updateOptions.StdInMode)
1618      updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front();
1619
1620    if (options.Command.CommandType == NCommandType::kRename)
1621      if (updateOptions.Commands.Size() != 1)
1622        throw CArcCmdLineException("Only one archive can be created with rename command");
1623  }
1624  else if (options.Command.CommandType == NCommandType::kBenchmark)
1625  {
1626    options.NumIterations = 1;
1627    options.NumIterations_Defined = false;
1628    if (curCommandIndex < numNonSwitchStrings)
1629    {
1630      if (!StringToUInt32(nonSwitchStrings[curCommandIndex], options.NumIterations))
1631        throw CArcCmdLineException("Incorrect number of benchmark iterations", nonSwitchStrings[curCommandIndex]);
1632      curCommandIndex++;
1633      options.NumIterations_Defined = true;
1634    }
1635  }
1636  else if (options.Command.CommandType == NCommandType::kHash)
1637  {
1638    options.Censor.AddPathsToCensor(censorPathMode);
1639    options.Censor.ExtendExclude();
1640
1641    CHashOptions &hashOptions = options.HashOptions;
1642    hashOptions.PathMode = censorPathMode;
1643    hashOptions.Methods = options.HashMethods;
1644    // hashOptions.HashFilePath = options.HashFilePath;
1645    if (parser[NKey::kPreserveATime].ThereIs)
1646      hashOptions.PreserveATime = true;
1647    if (parser[NKey::kShareForWrite].ThereIs)
1648      hashOptions.OpenShareForWrite = true;
1649    hashOptions.StdInMode = options.StdInMode;
1650    hashOptions.AltStreamsMode = options.AltStreams.Val;
1651    hashOptions.SymLinks = options.SymLinks;
1652  }
1653  else if (options.Command.CommandType == NCommandType::kInfo)
1654  {
1655  }
1656  else
1657    throw 20150919;
1658}
1659
1660
1661
1662#ifndef _WIN32
1663
1664static AString g_ModuleDirPrefix;
1665
1666void Set_ModuleDirPrefix_From_ProgArg0(const char *s);
1667void Set_ModuleDirPrefix_From_ProgArg0(const char *s)
1668{
1669  AString a (s);
1670  int sep = a.ReverseFind_PathSepar();
1671  a.DeleteFrom((unsigned)(sep + 1));
1672  g_ModuleDirPrefix = a;
1673}
1674
1675namespace NWindows {
1676namespace NDLL {
1677
1678FString GetModuleDirPrefix();
1679FString GetModuleDirPrefix()
1680{
1681  FString s;
1682
1683  s = fas2fs(g_ModuleDirPrefix);
1684  if (s.IsEmpty())
1685    s = FTEXT(".") FSTRING_PATH_SEPARATOR;
1686  return s;
1687  /*
1688  setenv("_7ZIP_HOME_DIR", "/test/", 0);
1689  const char *home = getenv("_7ZIP_HOME_DIR");
1690  if (home)
1691    s = home;
1692  else
1693    s = FTEXT(".") FSTRING_PATH_SEPARATOR;
1694  return s;
1695  */
1696}
1697
1698}}
1699
1700#endif // ! _WIN32
1701