Upload 2.0.2
[physicsfs] / lzma / CPP / 7zip / UI / Common / ArchiveCommandLine.cpp
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 }