misc/libphysfs/lzma/CPP/7zip/UI/Common/ArchiveCommandLine.cpp
changeset 12218 bb5522e88ab2
equal deleted inserted replaced
12217:ea891871f481 12218:bb5522e88ab2
       
     1 // ArchiveCommandLine.cpp
       
     2 
       
     3 #include "StdAfx.h"
       
     4 
       
     5 #ifdef _WIN32
       
     6 #include <io.h>
       
     7 #endif
       
     8 #include <stdio.h>
       
     9 
       
    10 #include "Common/ListFileUtils.h"
       
    11 #include "Common/StringConvert.h"
       
    12 #include "Common/StringToInt.h"
       
    13 
       
    14 #include "Windows/FileName.h"
       
    15 #include "Windows/FileDir.h"
       
    16 #ifdef _WIN32
       
    17 #include "Windows/FileMapping.h"
       
    18 #include "Windows/Synchronization.h"
       
    19 #endif
       
    20 
       
    21 #include "ArchiveCommandLine.h"
       
    22 #include "UpdateAction.h"
       
    23 #include "Update.h"
       
    24 #include "SortUtils.h"
       
    25 #include "EnumDirItems.h"
       
    26 
       
    27 extern bool g_CaseSensitive;
       
    28 
       
    29 #if _MSC_VER >= 1400
       
    30 #define MY_isatty_fileno(x) _isatty(_fileno(x))
       
    31 #else
       
    32 #define MY_isatty_fileno(x) isatty(fileno(x))
       
    33 #endif
       
    34 
       
    35 #define MY_IS_TERMINAL(x) (MY_isatty_fileno(x) != 0); 
       
    36 
       
    37 using namespace NCommandLineParser;
       
    38 using namespace NWindows;
       
    39 using namespace NFile;
       
    40 
       
    41 namespace NKey {
       
    42 enum Enum
       
    43 {
       
    44   kHelp1 = 0,
       
    45   kHelp2,
       
    46   kHelp3,
       
    47   kDisableHeaders,
       
    48   kDisablePercents,
       
    49   kArchiveType,
       
    50   kYes,
       
    51   kPassword,
       
    52   kProperty,
       
    53   kOutputDir,
       
    54   kWorkingDir,
       
    55   kInclude,
       
    56   kExclude,
       
    57   kArInclude,
       
    58   kArExclude,
       
    59   kNoArName,
       
    60   kUpdate,
       
    61   kVolume,
       
    62   kRecursed,
       
    63   kSfx,
       
    64   kStdIn,
       
    65   kStdOut,
       
    66   kOverwrite,
       
    67   kEmail,
       
    68   kShowDialog,
       
    69   kLargePages,
       
    70   kCharSet,
       
    71   kTechMode,
       
    72   kShareForWrite,
       
    73   kCaseSensitive
       
    74 };
       
    75 
       
    76 }
       
    77 
       
    78 
       
    79 static const wchar_t kRecursedIDChar = 'R';
       
    80 static const wchar_t *kRecursedPostCharSet = L"0-";
       
    81 
       
    82 namespace NRecursedPostCharIndex {
       
    83   enum EEnum 
       
    84   {
       
    85     kWildCardRecursionOnly = 0, 
       
    86     kNoRecursion = 1
       
    87   };
       
    88 }
       
    89 
       
    90 static const char kImmediateNameID = '!';
       
    91 static const char kMapNameID = '#';
       
    92 static const char kFileListID = '@';
       
    93 
       
    94 static const char kSomeCludePostStringMinSize = 2; // at least <@|!><N>ame must be
       
    95 static const char kSomeCludeAfterRecursedPostStringMinSize = 2; // at least <@|!><N>ame must be
       
    96 
       
    97 static const wchar_t *kOverwritePostCharSet = L"asut";
       
    98 
       
    99 NExtract::NOverwriteMode::EEnum k_OverwriteModes[] =
       
   100 {
       
   101   NExtract::NOverwriteMode::kWithoutPrompt,
       
   102   NExtract::NOverwriteMode::kSkipExisting,
       
   103   NExtract::NOverwriteMode::kAutoRename,
       
   104   NExtract::NOverwriteMode::kAutoRenameExisting
       
   105 };
       
   106 
       
   107 static const CSwitchForm kSwitchForms[] = 
       
   108   {
       
   109     { L"?",  NSwitchType::kSimple, false },
       
   110     { L"H",  NSwitchType::kSimple, false },
       
   111     { L"-HELP",  NSwitchType::kSimple, false },
       
   112     { L"BA", NSwitchType::kSimple, false },
       
   113     { L"BD", NSwitchType::kSimple, false },
       
   114     { L"T",  NSwitchType::kUnLimitedPostString, false, 1 },
       
   115     { L"Y",  NSwitchType::kSimple, false },
       
   116     { L"P",  NSwitchType::kUnLimitedPostString, false, 0 },
       
   117     { L"M",  NSwitchType::kUnLimitedPostString, true, 1 },
       
   118     { L"O",  NSwitchType::kUnLimitedPostString, false, 1 },
       
   119     { L"W",  NSwitchType::kUnLimitedPostString, false, 0 },
       
   120     { L"I",  NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize},
       
   121     { L"X",  NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize},
       
   122     { L"AI", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize},
       
   123     { L"AX", NSwitchType::kUnLimitedPostString, true, kSomeCludePostStringMinSize},
       
   124     { L"AN", NSwitchType::kSimple, false },
       
   125     { L"U",  NSwitchType::kUnLimitedPostString, true, 1},
       
   126     { L"V",  NSwitchType::kUnLimitedPostString, true, 1},
       
   127     { L"R",  NSwitchType::kPostChar, false, 0, 0, kRecursedPostCharSet },
       
   128     { L"SFX", NSwitchType::kUnLimitedPostString, false, 0 },
       
   129     { L"SI", NSwitchType::kUnLimitedPostString, false, 0 },
       
   130     { L"SO", NSwitchType::kSimple, false, 0 },
       
   131     { L"AO", NSwitchType::kPostChar, false, 1, 1, kOverwritePostCharSet},
       
   132     { L"SEML", NSwitchType::kUnLimitedPostString, false, 0},
       
   133     { L"AD",  NSwitchType::kSimple, false },
       
   134     { L"SLP", NSwitchType::kUnLimitedPostString, false, 0},
       
   135     { L"SCS", NSwitchType::kUnLimitedPostString, false, 0},
       
   136     { L"SLT", NSwitchType::kSimple, false },
       
   137     { L"SSW", NSwitchType::kSimple, false },
       
   138     { L"SSC", NSwitchType::kPostChar, false, 0, 0, L"-" }
       
   139   };
       
   140 
       
   141 static const CCommandForm g_CommandForms[] = 
       
   142 {
       
   143   { L"A", false },
       
   144   { L"U", false },
       
   145   { L"D", false },
       
   146   { L"T", false },
       
   147   { L"E", false },
       
   148   { L"X", false },
       
   149   { L"L", false },
       
   150   { L"B", false },
       
   151   { L"I", false }
       
   152 };
       
   153 
       
   154 static const int kNumCommandForms = sizeof(g_CommandForms) /  sizeof(g_CommandForms[0]);
       
   155 
       
   156 static const wchar_t *kUniversalWildcard = L"*";
       
   157 static const int kMinNonSwitchWords = 1;
       
   158 static const int kCommandIndex = 0;
       
   159 
       
   160 // ---------------------------
       
   161 // exception messages
       
   162 
       
   163 static const char *kUserErrorMessage  = "Incorrect command line";
       
   164 static const char *kIncorrectListFile = "Incorrect item in listfile.\nCheck charset encoding and -scs switch.";
       
   165 static const char *kIncorrectWildCardInListFile = "Incorrect wildcard in listfile";
       
   166 static const char *kIncorrectWildCardInCommandLine  = "Incorrect wildcard in command line";
       
   167 static const char *kTerminalOutError = "I won't write compressed data to a terminal";
       
   168 static const char *kSameTerminalError = "I won't write data and program's messages to same terminal";
       
   169 
       
   170 static void ThrowException(const char *errorMessage)
       
   171 {
       
   172   throw CArchiveCommandLineException(errorMessage);
       
   173 };
       
   174 
       
   175 static void ThrowUserErrorException()
       
   176 {
       
   177   ThrowException(kUserErrorMessage);
       
   178 };
       
   179 
       
   180 // ---------------------------
       
   181 
       
   182 bool CArchiveCommand::IsFromExtractGroup() const
       
   183 {
       
   184   switch(CommandType)
       
   185   {
       
   186     case NCommandType::kTest:
       
   187     case NCommandType::kExtract:
       
   188     case NCommandType::kFullExtract:
       
   189       return true;
       
   190     default:
       
   191       return false;
       
   192   }
       
   193 }
       
   194 
       
   195 NExtract::NPathMode::EEnum CArchiveCommand::GetPathMode() const
       
   196 {
       
   197   switch(CommandType)
       
   198   {
       
   199     case NCommandType::kTest:
       
   200     case NCommandType::kFullExtract:
       
   201       return NExtract::NPathMode::kFullPathnames;
       
   202     default:
       
   203       return NExtract::NPathMode::kNoPathnames;
       
   204   }
       
   205 }
       
   206 
       
   207 bool CArchiveCommand::IsFromUpdateGroup() const
       
   208 {
       
   209   return (CommandType == NCommandType::kAdd || 
       
   210     CommandType == NCommandType::kUpdate ||
       
   211     CommandType == NCommandType::kDelete);
       
   212 }
       
   213 
       
   214 static NRecursedType::EEnum GetRecursedTypeFromIndex(int index)
       
   215 {
       
   216   switch (index)
       
   217   {
       
   218     case NRecursedPostCharIndex::kWildCardRecursionOnly: 
       
   219       return NRecursedType::kWildCardOnlyRecursed;
       
   220     case NRecursedPostCharIndex::kNoRecursion: 
       
   221       return NRecursedType::kNonRecursed;
       
   222     default:
       
   223       return NRecursedType::kRecursed;
       
   224   }
       
   225 }
       
   226 
       
   227 static bool ParseArchiveCommand(const UString &commandString, CArchiveCommand &command)
       
   228 {
       
   229   UString commandStringUpper = commandString;
       
   230   commandStringUpper.MakeUpper();
       
   231   UString postString;
       
   232   int commandIndex = ParseCommand(kNumCommandForms, g_CommandForms, commandStringUpper, 
       
   233       postString) ;
       
   234   if (commandIndex < 0)
       
   235     return false;
       
   236   command.CommandType = (NCommandType::EEnum)commandIndex;
       
   237   return true;
       
   238 }
       
   239 
       
   240 // ------------------------------------------------------------------
       
   241 // filenames functions
       
   242 
       
   243 static bool AddNameToCensor(NWildcard::CCensor &wildcardCensor, 
       
   244     const UString &name, bool include, NRecursedType::EEnum type)
       
   245 {
       
   246   bool isWildCard = DoesNameContainWildCard(name);
       
   247   bool recursed = false;
       
   248 
       
   249   switch (type)
       
   250   {
       
   251     case NRecursedType::kWildCardOnlyRecursed:
       
   252       recursed = isWildCard;
       
   253       break;
       
   254     case NRecursedType::kRecursed:
       
   255       recursed = true;
       
   256       break;
       
   257     case NRecursedType::kNonRecursed:
       
   258       recursed = false;
       
   259       break;
       
   260   }
       
   261   wildcardCensor.AddItem(include, name, recursed);
       
   262   return true;
       
   263 }
       
   264 
       
   265 static void AddToCensorFromListFile(NWildcard::CCensor &wildcardCensor, 
       
   266     LPCWSTR fileName, bool include, NRecursedType::EEnum type, UINT codePage)
       
   267 {
       
   268   UStringVector names;
       
   269   if (!ReadNamesFromListFile(fileName, names, codePage))
       
   270     throw kIncorrectListFile;
       
   271   for (int i = 0; i < names.Size(); i++)
       
   272     if (!AddNameToCensor(wildcardCensor, names[i], include, type))
       
   273       throw kIncorrectWildCardInListFile;
       
   274 }
       
   275 
       
   276 static void AddCommandLineWildCardToCensr(NWildcard::CCensor &wildcardCensor, 
       
   277     const UString &name, bool include, NRecursedType::EEnum recursedType)
       
   278 {
       
   279   if (!AddNameToCensor(wildcardCensor, name, include, recursedType))
       
   280     throw kIncorrectWildCardInCommandLine;
       
   281 }
       
   282 
       
   283 static void AddToCensorFromNonSwitchesStrings(
       
   284     int startIndex,
       
   285     NWildcard::CCensor &wildcardCensor, 
       
   286     const UStringVector &nonSwitchStrings, NRecursedType::EEnum type, 
       
   287     bool thereAreSwitchIncludes, UINT codePage)
       
   288 {
       
   289   if(nonSwitchStrings.Size() == startIndex && (!thereAreSwitchIncludes)) 
       
   290     AddCommandLineWildCardToCensr(wildcardCensor, kUniversalWildcard, true, type);
       
   291   for(int i = startIndex; i < nonSwitchStrings.Size(); i++)
       
   292   {
       
   293     const UString &s = nonSwitchStrings[i];
       
   294     if (s[0] == kFileListID)
       
   295       AddToCensorFromListFile(wildcardCensor, s.Mid(1), true, type, codePage);
       
   296     else
       
   297       AddCommandLineWildCardToCensr(wildcardCensor, s, true, type);
       
   298   }
       
   299 }
       
   300 
       
   301 #ifdef _WIN32
       
   302 static void ParseMapWithPaths(NWildcard::CCensor &wildcardCensor, 
       
   303     const UString &switchParam, bool include, 
       
   304     NRecursedType::EEnum commonRecursedType)
       
   305 {
       
   306   int splitPos = switchParam.Find(L':');
       
   307   if (splitPos < 0)
       
   308     ThrowUserErrorException();
       
   309   UString mappingName = switchParam.Left(splitPos);
       
   310   
       
   311   UString switchParam2 = switchParam.Mid(splitPos + 1);
       
   312   splitPos = switchParam2.Find(L':');
       
   313   if (splitPos < 0)
       
   314     ThrowUserErrorException();
       
   315   
       
   316   UString mappingSize = switchParam2.Left(splitPos);
       
   317   UString eventName = switchParam2.Mid(splitPos + 1);
       
   318   
       
   319   UInt64 dataSize64 = ConvertStringToUInt64(mappingSize, NULL);
       
   320   UInt32 dataSize = (UInt32)dataSize64;
       
   321   {
       
   322     CFileMapping fileMapping;
       
   323     if (!fileMapping.Open(FILE_MAP_READ, false, GetSystemString(mappingName)))
       
   324       ThrowException("Can not open mapping");
       
   325     LPVOID data = fileMapping.MapViewOfFile(FILE_MAP_READ, 0, dataSize);
       
   326     if (data == NULL)
       
   327       ThrowException("MapViewOfFile error");
       
   328     try
       
   329     {
       
   330       const wchar_t *curData = (const wchar_t *)data;
       
   331       if (*curData != 0)
       
   332         ThrowException("Incorrect mapping data");
       
   333       UInt32 numChars = dataSize / sizeof(wchar_t);
       
   334       UString name;
       
   335       for (UInt32 i = 1; i < numChars; i++)
       
   336       {
       
   337         wchar_t c = curData[i];
       
   338         if (c == L'\0')
       
   339         {
       
   340           AddCommandLineWildCardToCensr(wildcardCensor, 
       
   341               name, include, commonRecursedType);
       
   342           name.Empty();
       
   343         }
       
   344         else
       
   345           name += c;
       
   346       }
       
   347       if (!name.IsEmpty())
       
   348         ThrowException("data error");
       
   349     }
       
   350     catch(...)
       
   351     {
       
   352       UnmapViewOfFile(data);
       
   353       throw;
       
   354     }
       
   355     UnmapViewOfFile(data);
       
   356   }
       
   357   
       
   358   {
       
   359     NSynchronization::CManualResetEvent event;
       
   360     if (event.Open(EVENT_MODIFY_STATE, false, GetSystemString(eventName)) == S_OK)
       
   361       event.Set();
       
   362   }
       
   363 }
       
   364 #endif
       
   365 
       
   366 static void AddSwitchWildCardsToCensor(NWildcard::CCensor &wildcardCensor, 
       
   367     const UStringVector &strings, bool include, 
       
   368     NRecursedType::EEnum commonRecursedType, UINT codePage)
       
   369 {
       
   370   for(int i = 0; i < strings.Size(); i++)
       
   371   {
       
   372     const UString &name = strings[i];
       
   373     NRecursedType::EEnum recursedType;
       
   374     int pos = 0;
       
   375     if (name.Length() < kSomeCludePostStringMinSize)
       
   376       ThrowUserErrorException();
       
   377     if (::MyCharUpper(name[pos]) == kRecursedIDChar)
       
   378     {
       
   379       pos++;
       
   380       int index = UString(kRecursedPostCharSet).Find(name[pos]);
       
   381       recursedType = GetRecursedTypeFromIndex(index);
       
   382       if (index >= 0)
       
   383         pos++;
       
   384     }
       
   385     else
       
   386       recursedType = commonRecursedType;
       
   387     if (name.Length() < pos + kSomeCludeAfterRecursedPostStringMinSize)
       
   388       ThrowUserErrorException();
       
   389     UString tail = name.Mid(pos + 1);
       
   390     if (name[pos] == kImmediateNameID)
       
   391       AddCommandLineWildCardToCensr(wildcardCensor, tail, include, recursedType);
       
   392     else if (name[pos] == kFileListID)
       
   393       AddToCensorFromListFile(wildcardCensor, tail, include, recursedType, codePage);
       
   394     #ifdef _WIN32
       
   395     else if (name[pos] == kMapNameID)
       
   396       ParseMapWithPaths(wildcardCensor, tail, include, recursedType);
       
   397     #endif
       
   398     else
       
   399       ThrowUserErrorException();
       
   400   }
       
   401 }
       
   402 
       
   403 #ifdef _WIN32
       
   404 
       
   405 // This code converts all short file names to long file names.
       
   406 
       
   407 static void ConvertToLongName(const UString &prefix, UString &name)
       
   408 {
       
   409   if (name.IsEmpty() || DoesNameContainWildCard(name))
       
   410     return;
       
   411   NFind::CFileInfoW fileInfo;
       
   412   if (NFind::FindFile(prefix + name, fileInfo))
       
   413     name = fileInfo.Name;
       
   414 }
       
   415 
       
   416 static void ConvertToLongNames(const UString &prefix, CObjectVector<NWildcard::CItem> &items)
       
   417 {
       
   418   for (int i = 0; i < items.Size(); i++)
       
   419   {
       
   420     NWildcard::CItem &item = items[i];
       
   421     if (item.Recursive || item.PathParts.Size() != 1)
       
   422       continue;
       
   423     ConvertToLongName(prefix, item.PathParts.Front());
       
   424   }
       
   425 }
       
   426 
       
   427 static void ConvertToLongNames(const UString &prefix, NWildcard::CCensorNode &node)
       
   428 {
       
   429   ConvertToLongNames(prefix, node.IncludeItems);
       
   430   ConvertToLongNames(prefix, node.ExcludeItems);
       
   431   int i;
       
   432   for (i = 0; i < node.SubNodes.Size(); i++)
       
   433     ConvertToLongName(prefix, node.SubNodes[i].Name);
       
   434   // mix folders with same name
       
   435   for (i = 0; i < node.SubNodes.Size(); i++)
       
   436   {
       
   437     NWildcard::CCensorNode &nextNode1 = node.SubNodes[i];
       
   438     for (int j = i + 1; j < node.SubNodes.Size();)
       
   439     {
       
   440       const NWildcard::CCensorNode &nextNode2 = node.SubNodes[j];
       
   441       if (nextNode1.Name.CompareNoCase(nextNode2.Name) == 0)
       
   442       {
       
   443         nextNode1.IncludeItems += nextNode2.IncludeItems;
       
   444         nextNode1.ExcludeItems += nextNode2.ExcludeItems;
       
   445         node.SubNodes.Delete(j);
       
   446       }
       
   447       else
       
   448         j++;
       
   449     }
       
   450   }
       
   451   for (i = 0; i < node.SubNodes.Size(); i++)
       
   452   {
       
   453     NWildcard::CCensorNode &nextNode = node.SubNodes[i];
       
   454     ConvertToLongNames(prefix + nextNode.Name + wchar_t(NFile::NName::kDirDelimiter), nextNode); 
       
   455   }
       
   456 }
       
   457 
       
   458 static void ConvertToLongNames(NWildcard::CCensor &censor)
       
   459 {
       
   460   for (int i = 0; i < censor.Pairs.Size(); i++)
       
   461   {
       
   462     NWildcard::CPair &pair = censor.Pairs[i];
       
   463     ConvertToLongNames(pair.Prefix, pair.Head);
       
   464   }
       
   465 }
       
   466 
       
   467 #endif
       
   468 
       
   469 static NUpdateArchive::NPairAction::EEnum GetUpdatePairActionType(int i)
       
   470 {
       
   471   switch(i)
       
   472   {
       
   473     case NUpdateArchive::NPairAction::kIgnore: return NUpdateArchive::NPairAction::kIgnore;
       
   474     case NUpdateArchive::NPairAction::kCopy: return NUpdateArchive::NPairAction::kCopy;
       
   475     case NUpdateArchive::NPairAction::kCompress: return NUpdateArchive::NPairAction::kCompress;
       
   476     case NUpdateArchive::NPairAction::kCompressAsAnti: return NUpdateArchive::NPairAction::kCompressAsAnti;
       
   477   }
       
   478   throw 98111603;
       
   479 }
       
   480 
       
   481 const UString kUpdatePairStateIDSet = L"PQRXYZW";
       
   482 const int kUpdatePairStateNotSupportedActions[] = {2, 2, 1, -1, -1, -1, -1};
       
   483 
       
   484 const UString kUpdatePairActionIDSet = L"0123"; //Ignore, Copy, Compress, Create Anti
       
   485 
       
   486 const wchar_t *kUpdateIgnoreItselfPostStringID = L"-"; 
       
   487 const wchar_t kUpdateNewArchivePostCharID = '!'; 
       
   488 
       
   489 
       
   490 static bool ParseUpdateCommandString2(const UString &command, 
       
   491     NUpdateArchive::CActionSet &actionSet, UString &postString)
       
   492 {
       
   493   for(int i = 0; i < command.Length();)
       
   494   {
       
   495     wchar_t c = MyCharUpper(command[i]);
       
   496     int statePos = kUpdatePairStateIDSet.Find(c);
       
   497     if (statePos < 0)
       
   498     {
       
   499       postString = command.Mid(i);
       
   500       return true;
       
   501     }
       
   502     i++;
       
   503     if (i >= command.Length())
       
   504       return false;
       
   505     int actionPos = kUpdatePairActionIDSet.Find(::MyCharUpper(command[i]));
       
   506     if (actionPos < 0)
       
   507       return false;
       
   508     actionSet.StateActions[statePos] = GetUpdatePairActionType(actionPos);
       
   509     if (kUpdatePairStateNotSupportedActions[statePos] == actionPos)
       
   510       return false;
       
   511     i++;
       
   512   }
       
   513   postString.Empty();
       
   514   return true;
       
   515 }
       
   516 
       
   517 static void ParseUpdateCommandString(CUpdateOptions &options, 
       
   518     const UStringVector &updatePostStrings, 
       
   519     const NUpdateArchive::CActionSet &defaultActionSet)
       
   520 {
       
   521   for(int i = 0; i < updatePostStrings.Size(); i++)
       
   522   {
       
   523     const UString &updateString = updatePostStrings[i];
       
   524     if(updateString.CompareNoCase(kUpdateIgnoreItselfPostStringID) == 0)
       
   525     {
       
   526       if(options.UpdateArchiveItself)
       
   527       {
       
   528         options.UpdateArchiveItself = false;
       
   529         options.Commands.Delete(0);
       
   530       }
       
   531     }
       
   532     else
       
   533     {
       
   534       NUpdateArchive::CActionSet actionSet = defaultActionSet;
       
   535 
       
   536       UString postString;
       
   537       if (!ParseUpdateCommandString2(updateString, actionSet, postString))
       
   538         ThrowUserErrorException();
       
   539       if(postString.IsEmpty())
       
   540       {
       
   541         if(options.UpdateArchiveItself)
       
   542           options.Commands[0].ActionSet = actionSet;
       
   543       }
       
   544       else
       
   545       {
       
   546         if(MyCharUpper(postString[0]) != kUpdateNewArchivePostCharID)
       
   547           ThrowUserErrorException();
       
   548         CUpdateArchiveCommand uc;
       
   549         UString archivePath = postString.Mid(1);
       
   550         if (archivePath.IsEmpty())
       
   551           ThrowUserErrorException();
       
   552         uc.UserArchivePath = archivePath;
       
   553         uc.ActionSet = actionSet;
       
   554         options.Commands.Add(uc);
       
   555       }
       
   556     }
       
   557   }
       
   558 }
       
   559 
       
   560 static const char kByteSymbol = 'B';
       
   561 static const char kKiloSymbol = 'K';
       
   562 static const char kMegaSymbol = 'M';
       
   563 static const char kGigaSymbol = 'G';
       
   564 
       
   565 static bool ParseComplexSize(const UString &src, UInt64 &result)
       
   566 {
       
   567   UString s = src;
       
   568   s.MakeUpper();
       
   569 
       
   570   const wchar_t *start = s;
       
   571   const wchar_t *end;
       
   572   UInt64 number = ConvertStringToUInt64(start, &end);
       
   573   int numDigits = (int)(end - start);
       
   574   if (numDigits == 0 || s.Length() > numDigits + 1)
       
   575     return false;
       
   576   if (s.Length() == numDigits)
       
   577   {
       
   578     result = number;
       
   579     return true;
       
   580   }
       
   581   int numBits;
       
   582   switch (s[numDigits])
       
   583   {
       
   584     case kByteSymbol:
       
   585       result = number;
       
   586       return true;
       
   587     case kKiloSymbol:
       
   588       numBits = 10;
       
   589       break;
       
   590     case kMegaSymbol:
       
   591       numBits = 20;
       
   592       break;
       
   593     case kGigaSymbol:
       
   594       numBits = 30;
       
   595       break;
       
   596     default:
       
   597       return false;
       
   598   }
       
   599   if (number >= ((UInt64)1 << (64 - numBits)))
       
   600     return false;
       
   601   result = number << numBits;
       
   602   return true;
       
   603 }
       
   604 
       
   605 static void SetAddCommandOptions(
       
   606     NCommandType::EEnum commandType, 
       
   607     const CParser &parser, 
       
   608     CUpdateOptions &options)
       
   609 {
       
   610   NUpdateArchive::CActionSet defaultActionSet;
       
   611   switch(commandType)
       
   612   {
       
   613     case NCommandType::kAdd: 
       
   614       defaultActionSet = NUpdateArchive::kAddActionSet;
       
   615       break;
       
   616     case NCommandType::kDelete: 
       
   617       defaultActionSet = NUpdateArchive::kDeleteActionSet;
       
   618       break;
       
   619     default: 
       
   620       defaultActionSet = NUpdateArchive::kUpdateActionSet;
       
   621   }
       
   622   
       
   623   options.UpdateArchiveItself = true;
       
   624   
       
   625   options.Commands.Clear();
       
   626   CUpdateArchiveCommand updateMainCommand;
       
   627   updateMainCommand.ActionSet = defaultActionSet;
       
   628   options.Commands.Add(updateMainCommand);
       
   629   if(parser[NKey::kUpdate].ThereIs)
       
   630     ParseUpdateCommandString(options, parser[NKey::kUpdate].PostStrings, 
       
   631         defaultActionSet);
       
   632   if(parser[NKey::kWorkingDir].ThereIs)
       
   633   {
       
   634     const UString &postString = parser[NKey::kWorkingDir].PostStrings[0];
       
   635     if (postString.IsEmpty())
       
   636       NDirectory::MyGetTempPath(options.WorkingDir);
       
   637     else
       
   638       options.WorkingDir = postString;
       
   639   }
       
   640   options.SfxMode = parser[NKey::kSfx].ThereIs;
       
   641   if (options.SfxMode)
       
   642     options.SfxModule = parser[NKey::kSfx].PostStrings[0];
       
   643 
       
   644   if (parser[NKey::kVolume].ThereIs)
       
   645   {
       
   646     const UStringVector &sv = parser[NKey::kVolume].PostStrings;
       
   647     for (int i = 0; i < sv.Size(); i++)
       
   648     {
       
   649       UInt64 size;
       
   650       if (!ParseComplexSize(sv[i], size))
       
   651         ThrowException("Incorrect volume size");
       
   652       options.VolumesSizes.Add(size);
       
   653     }
       
   654   }
       
   655 }
       
   656 
       
   657 static void SetMethodOptions(const CParser &parser, CObjectVector<CProperty> &properties)
       
   658 {
       
   659   if (parser[NKey::kProperty].ThereIs)
       
   660   {
       
   661     // options.MethodMode.Properties.Clear();
       
   662     for(int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++)
       
   663     {
       
   664       CProperty property;
       
   665       const UString &postString = parser[NKey::kProperty].PostStrings[i];
       
   666       int index = postString.Find(L'=');
       
   667       if (index < 0)
       
   668         property.Name = postString;
       
   669       else
       
   670       {
       
   671         property.Name = postString.Left(index);
       
   672         property.Value = postString.Mid(index + 1);
       
   673       }
       
   674       properties.Add(property);
       
   675     }
       
   676   }
       
   677 }
       
   678 
       
   679 CArchiveCommandLineParser::CArchiveCommandLineParser(): 
       
   680   parser(sizeof(kSwitchForms) / sizeof(kSwitchForms[0])) {}
       
   681 
       
   682 void CArchiveCommandLineParser::Parse1(const UStringVector &commandStrings,
       
   683     CArchiveCommandLineOptions &options)
       
   684 {
       
   685   try
       
   686   {
       
   687     parser.ParseStrings(kSwitchForms, commandStrings);
       
   688   }
       
   689   catch(...) 
       
   690   {
       
   691     ThrowUserErrorException();
       
   692   }
       
   693 
       
   694   options.IsInTerminal = MY_IS_TERMINAL(stdin);
       
   695   options.IsStdOutTerminal = MY_IS_TERMINAL(stdout);
       
   696   options.IsStdErrTerminal = MY_IS_TERMINAL(stderr);
       
   697   options.StdOutMode = parser[NKey::kStdOut].ThereIs;
       
   698   options.EnableHeaders = !parser[NKey::kDisableHeaders].ThereIs;
       
   699   options.HelpMode = parser[NKey::kHelp1].ThereIs || parser[NKey::kHelp2].ThereIs  || parser[NKey::kHelp3].ThereIs;
       
   700 
       
   701   #ifdef _WIN32
       
   702   options.LargePages = false;
       
   703   if (parser[NKey::kLargePages].ThereIs)
       
   704   {
       
   705     const UString &postString = parser[NKey::kLargePages].PostStrings.Front();
       
   706     if (postString.IsEmpty())
       
   707       options.LargePages = true;
       
   708   }
       
   709   #endif
       
   710 }
       
   711 
       
   712 struct CCodePagePair
       
   713 {
       
   714   const wchar_t *Name;
       
   715   UINT CodePage;
       
   716 };
       
   717 
       
   718 static CCodePagePair g_CodePagePairs[] = 
       
   719 {
       
   720   { L"UTF-8", CP_UTF8 },
       
   721   { L"WIN",   CP_ACP },
       
   722   { L"DOS",   CP_OEMCP }
       
   723 };
       
   724 
       
   725 static const int kNumCodePages = sizeof(g_CodePagePairs) / sizeof(g_CodePagePairs[0]);
       
   726 
       
   727 static bool ConvertStringToUInt32(const wchar_t *s, UInt32 &v)
       
   728 {
       
   729   const wchar_t *end;
       
   730   UInt64 number = ConvertStringToUInt64(s, &end);
       
   731   if (*end != 0)
       
   732     return false;
       
   733   if (number > (UInt32)0xFFFFFFFF)
       
   734     return false;
       
   735   v = (UInt32)number;
       
   736   return true;
       
   737 }
       
   738 
       
   739 void CArchiveCommandLineParser::Parse2(CArchiveCommandLineOptions &options)
       
   740 {
       
   741   const UStringVector &nonSwitchStrings = parser.NonSwitchStrings;
       
   742   int numNonSwitchStrings = nonSwitchStrings.Size();
       
   743   if(numNonSwitchStrings < kMinNonSwitchWords)  
       
   744     ThrowUserErrorException();
       
   745 
       
   746   if (!ParseArchiveCommand(nonSwitchStrings[kCommandIndex], options.Command))
       
   747     ThrowUserErrorException();
       
   748 
       
   749   options.TechMode = parser[NKey::kTechMode].ThereIs;
       
   750 
       
   751   if (parser[NKey::kCaseSensitive].ThereIs)
       
   752     g_CaseSensitive = (parser[NKey::kCaseSensitive].PostCharIndex < 0);
       
   753 
       
   754   NRecursedType::EEnum recursedType;
       
   755   if (parser[NKey::kRecursed].ThereIs)
       
   756     recursedType = GetRecursedTypeFromIndex(parser[NKey::kRecursed].PostCharIndex);
       
   757   else
       
   758     recursedType = NRecursedType::kNonRecursed;
       
   759 
       
   760   UINT codePage = CP_UTF8;
       
   761   if (parser[NKey::kCharSet].ThereIs)
       
   762   {
       
   763     UString name = parser[NKey::kCharSet].PostStrings.Front();
       
   764     name.MakeUpper();
       
   765     int i;
       
   766     for (i = 0; i < kNumCodePages; i++)
       
   767     {
       
   768       const CCodePagePair &pair = g_CodePagePairs[i];
       
   769       if (name.Compare(pair.Name) == 0)
       
   770       {
       
   771         codePage = pair.CodePage;
       
   772         break;
       
   773       }
       
   774     }
       
   775     if (i >= kNumCodePages)
       
   776       ThrowUserErrorException();
       
   777   }
       
   778 
       
   779   bool thereAreSwitchIncludes = false;
       
   780   if (parser[NKey::kInclude].ThereIs)
       
   781   {
       
   782     thereAreSwitchIncludes = true;
       
   783     AddSwitchWildCardsToCensor(options.WildcardCensor, 
       
   784         parser[NKey::kInclude].PostStrings, true, recursedType, codePage);
       
   785   }
       
   786   if (parser[NKey::kExclude].ThereIs)
       
   787     AddSwitchWildCardsToCensor(options.WildcardCensor, 
       
   788         parser[NKey::kExclude].PostStrings, false, recursedType, codePage);
       
   789  
       
   790   int curCommandIndex = kCommandIndex + 1;
       
   791   bool thereIsArchiveName = !parser[NKey::kNoArName].ThereIs && 
       
   792       options.Command.CommandType != NCommandType::kBenchmark && 
       
   793       options.Command.CommandType != NCommandType::kInfo;
       
   794   if (thereIsArchiveName)
       
   795   {
       
   796     if(curCommandIndex >= numNonSwitchStrings)  
       
   797       ThrowUserErrorException();
       
   798     options.ArchiveName = nonSwitchStrings[curCommandIndex++];
       
   799   }
       
   800 
       
   801   AddToCensorFromNonSwitchesStrings(
       
   802       curCommandIndex, options.WildcardCensor, 
       
   803       nonSwitchStrings, recursedType, thereAreSwitchIncludes, codePage);
       
   804 
       
   805   options.YesToAll = parser[NKey::kYes].ThereIs;
       
   806 
       
   807   bool isExtractGroupCommand = options.Command.IsFromExtractGroup();
       
   808 
       
   809   options.PasswordEnabled = parser[NKey::kPassword].ThereIs;
       
   810 
       
   811   if(options.PasswordEnabled)
       
   812     options.Password = parser[NKey::kPassword].PostStrings[0];
       
   813 
       
   814   options.StdInMode = parser[NKey::kStdIn].ThereIs;
       
   815   options.ShowDialog = parser[NKey::kShowDialog].ThereIs;
       
   816 
       
   817   if(isExtractGroupCommand || options.Command.CommandType == NCommandType::kList)
       
   818   {
       
   819     if (options.StdInMode)
       
   820       ThrowException("Reading archives from stdin is not implemented");
       
   821     if (!options.WildcardCensor.AllAreRelative())
       
   822       ThrowException("Cannot use absolute pathnames for this command");
       
   823 
       
   824     NWildcard::CCensor archiveWildcardCensor;
       
   825 
       
   826     if (parser[NKey::kArInclude].ThereIs)
       
   827     {
       
   828       AddSwitchWildCardsToCensor(archiveWildcardCensor, 
       
   829         parser[NKey::kArInclude].PostStrings, true, NRecursedType::kNonRecursed, codePage);
       
   830     }
       
   831     if (parser[NKey::kArExclude].ThereIs)
       
   832       AddSwitchWildCardsToCensor(archiveWildcardCensor, 
       
   833       parser[NKey::kArExclude].PostStrings, false, NRecursedType::kNonRecursed, codePage);
       
   834 
       
   835     if (thereIsArchiveName)
       
   836       AddCommandLineWildCardToCensr(archiveWildcardCensor, options.ArchiveName, true, NRecursedType::kNonRecursed);
       
   837 
       
   838     #ifdef _WIN32
       
   839     ConvertToLongNames(archiveWildcardCensor);
       
   840     #endif
       
   841 
       
   842     archiveWildcardCensor.ExtendExclude();
       
   843 
       
   844     CObjectVector<CDirItem> dirItems;
       
   845     {
       
   846       UStringVector errorPaths;
       
   847       CRecordVector<DWORD> errorCodes;
       
   848       HRESULT res = EnumerateItems(archiveWildcardCensor, dirItems, NULL, errorPaths, errorCodes);
       
   849       if (res != S_OK || errorPaths.Size() > 0)
       
   850         throw "cannot find archive";
       
   851     }
       
   852     UStringVector archivePaths;
       
   853     int i;
       
   854     for (i = 0; i < dirItems.Size(); i++)
       
   855     {
       
   856       const CDirItem &dirItem = dirItems[i];
       
   857       if (!dirItem.IsDirectory())
       
   858         archivePaths.Add(dirItem.FullPath);
       
   859     }
       
   860 
       
   861     if (archivePaths.Size() == 0)
       
   862       throw "there is no such archive";
       
   863 
       
   864     UStringVector archivePathsFull;
       
   865 
       
   866     for (i = 0; i < archivePaths.Size(); i++)
       
   867     {
       
   868       UString fullPath;
       
   869       NFile::NDirectory::MyGetFullPathName(archivePaths[i], fullPath);
       
   870       archivePathsFull.Add(fullPath);
       
   871     }
       
   872     CIntVector indices;
       
   873     SortFileNames(archivePathsFull, indices);
       
   874     options.ArchivePathsSorted.Reserve(indices.Size());
       
   875     options.ArchivePathsFullSorted.Reserve(indices.Size());
       
   876     for (i = 0; i < indices.Size(); i++)
       
   877     {
       
   878       options.ArchivePathsSorted.Add(archivePaths[indices[i]]);
       
   879       options.ArchivePathsFullSorted.Add(archivePathsFull[indices[i]]);
       
   880     }
       
   881 
       
   882     if (isExtractGroupCommand)
       
   883     {
       
   884       SetMethodOptions(parser, options.ExtractProperties); 
       
   885       if (options.StdOutMode && options.IsStdOutTerminal && options.IsStdErrTerminal)
       
   886         throw kSameTerminalError;
       
   887       if(parser[NKey::kOutputDir].ThereIs)
       
   888       {
       
   889         options.OutputDir = parser[NKey::kOutputDir].PostStrings[0];
       
   890         NFile::NName::NormalizeDirPathPrefix(options.OutputDir);
       
   891       }
       
   892 
       
   893       options.OverwriteMode = NExtract::NOverwriteMode::kAskBefore;
       
   894       if(parser[NKey::kOverwrite].ThereIs)
       
   895         options.OverwriteMode = 
       
   896             k_OverwriteModes[parser[NKey::kOverwrite].PostCharIndex];
       
   897       else if (options.YesToAll)
       
   898         options.OverwriteMode = NExtract::NOverwriteMode::kWithoutPrompt;
       
   899     }
       
   900   }
       
   901   else if(options.Command.IsFromUpdateGroup())
       
   902   {
       
   903     CUpdateOptions &updateOptions = options.UpdateOptions;
       
   904 
       
   905     if(parser[NKey::kArchiveType].ThereIs)
       
   906       options.ArcType = parser[NKey::kArchiveType].PostStrings[0];
       
   907 
       
   908     SetAddCommandOptions(options.Command.CommandType, parser, updateOptions); 
       
   909     
       
   910     SetMethodOptions(parser, updateOptions.MethodMode.Properties); 
       
   911 
       
   912     if (parser[NKey::kShareForWrite].ThereIs)
       
   913       updateOptions.OpenShareForWrite = true;
       
   914 
       
   915     options.EnablePercents = !parser[NKey::kDisablePercents].ThereIs;
       
   916 
       
   917     if (options.EnablePercents)
       
   918     {
       
   919       if ((options.StdOutMode && !options.IsStdErrTerminal) || 
       
   920          (!options.StdOutMode && !options.IsStdOutTerminal))  
       
   921         options.EnablePercents = false;
       
   922     }
       
   923 
       
   924     updateOptions.EMailMode = parser[NKey::kEmail].ThereIs;
       
   925     if (updateOptions.EMailMode)
       
   926     {
       
   927       updateOptions.EMailAddress = parser[NKey::kEmail].PostStrings.Front();
       
   928       if (updateOptions.EMailAddress.Length() > 0)
       
   929         if (updateOptions.EMailAddress[0] == L'.')
       
   930         {
       
   931           updateOptions.EMailRemoveAfter = true;
       
   932           updateOptions.EMailAddress.Delete(0);
       
   933         }
       
   934     }
       
   935 
       
   936     updateOptions.StdOutMode = options.StdOutMode;
       
   937     updateOptions.StdInMode = options.StdInMode;
       
   938 
       
   939     if (updateOptions.StdOutMode && updateOptions.EMailMode)
       
   940       throw "stdout mode and email mode cannot be combined";
       
   941     if (updateOptions.StdOutMode && options.IsStdOutTerminal)
       
   942       throw kTerminalOutError;
       
   943     if(updateOptions.StdInMode)
       
   944       updateOptions.StdInFileName = parser[NKey::kStdIn].PostStrings.Front();
       
   945 
       
   946     #ifdef _WIN32
       
   947     ConvertToLongNames(options.WildcardCensor);
       
   948     #endif
       
   949   }
       
   950   else if(options.Command.CommandType == NCommandType::kBenchmark)
       
   951   {
       
   952     options.NumThreads = (UInt32)-1;
       
   953     options.DictionarySize = (UInt32)-1;
       
   954     options.NumIterations = 1;
       
   955     if (curCommandIndex < numNonSwitchStrings)  
       
   956     {
       
   957       if (!ConvertStringToUInt32(nonSwitchStrings[curCommandIndex++], options.NumIterations))
       
   958         ThrowUserErrorException();
       
   959     }
       
   960     for (int i = 0; i < parser[NKey::kProperty].PostStrings.Size(); i++)
       
   961     {
       
   962       UString postString = parser[NKey::kProperty].PostStrings[i];
       
   963       postString.MakeUpper();
       
   964       if (postString.Length() < 2)
       
   965         ThrowUserErrorException();
       
   966       if (postString[0] == 'D')
       
   967       {
       
   968         int pos = 1;
       
   969         if (postString[pos] == '=')
       
   970           pos++;
       
   971         UInt32 logSize;
       
   972         if (!ConvertStringToUInt32((const wchar_t *)postString + pos, logSize))
       
   973           ThrowUserErrorException();
       
   974         if (logSize > 31)
       
   975           ThrowUserErrorException();
       
   976         options.DictionarySize = 1 << logSize;
       
   977       }
       
   978       else if (postString[0] == 'M' && postString[1] == 'T' )
       
   979       {
       
   980         int pos = 2;
       
   981         if (postString[pos] == '=')
       
   982           pos++;
       
   983         if (postString[pos] != 0)
       
   984           if (!ConvertStringToUInt32((const wchar_t *)postString + pos, options.NumThreads))
       
   985             ThrowUserErrorException();
       
   986       }
       
   987       else if (postString[0] == 'M' && postString[1] == '=' )
       
   988       {
       
   989         int pos = 2;
       
   990         if (postString[pos] != 0)
       
   991           options.Method = postString.Mid(2);
       
   992       }
       
   993       else
       
   994         ThrowUserErrorException();
       
   995     }
       
   996   }
       
   997   else if(options.Command.CommandType == NCommandType::kInfo)
       
   998   {
       
   999   }
       
  1000   else 
       
  1001     ThrowUserErrorException();
       
  1002   options.WildcardCensor.ExtendExclude();
       
  1003 }