ArchiveCommandLine.cpp 29 KB

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