FileMasks.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include "FileMasks.h"
  5. namespace Masks
  6. {
  7. bool __fastcall MatchesMask(const AnsiString Filename, const AnsiString Mask);
  8. }
  9. using namespace Masks;
  10. #include "Common.h"
  11. #include "TextsCore.h"
  12. #include "RemoteFiles.h"
  13. #include "PuttyTools.h"
  14. //---------------------------------------------------------------------------
  15. AnsiString __fastcall MaskFilePart(const AnsiString Part, const AnsiString Mask, bool& Masked)
  16. {
  17. AnsiString Result;
  18. int RestStart = 1;
  19. bool Delim = false;
  20. for (int Index = 1; Index <= Mask.Length(); Index++)
  21. {
  22. switch (Mask[Index])
  23. {
  24. case '\\':
  25. if (!Delim)
  26. {
  27. Delim = true;
  28. Masked = true;
  29. break;
  30. }
  31. case '*':
  32. if (!Delim)
  33. {
  34. Result += Part.SubString(RestStart, Part.Length() - RestStart + 1);
  35. RestStart = Part.Length() + 1;
  36. Masked = true;
  37. break;
  38. }
  39. case '?':
  40. if (!Delim)
  41. {
  42. if (RestStart <= Part.Length())
  43. {
  44. Result += Part[RestStart];
  45. RestStart++;
  46. }
  47. Masked = true;
  48. break;
  49. }
  50. default:
  51. Result += Mask[Index];
  52. RestStart++;
  53. Delim = false;
  54. break;
  55. }
  56. }
  57. return Result;
  58. }
  59. //---------------------------------------------------------------------------
  60. AnsiString __fastcall MaskFileName(AnsiString FileName, const AnsiString Mask)
  61. {
  62. if (!Mask.IsEmpty() && (Mask != "*") && (Mask != "*.*"))
  63. {
  64. bool Masked;
  65. int P = Mask.LastDelimiter(".");
  66. if (P > 0)
  67. {
  68. int P2 = FileName.LastDelimiter(".");
  69. // only dot at beginning of file name is not considered as
  70. // name/ext separator
  71. AnsiString FileExt = P2 > 1 ?
  72. FileName.SubString(P2 + 1, FileName.Length() - P2) : AnsiString();
  73. FileExt = MaskFilePart(FileExt, Mask.SubString(P + 1, Mask.Length() - P), Masked);
  74. if (P2 > 1)
  75. {
  76. FileName.SetLength(P2 - 1);
  77. }
  78. FileName = MaskFilePart(FileName, Mask.SubString(1, P - 1), Masked);
  79. if (!FileExt.IsEmpty())
  80. {
  81. FileName += "." + FileExt;
  82. }
  83. }
  84. else
  85. {
  86. FileName = MaskFilePart(FileName, Mask, Masked);
  87. }
  88. }
  89. return FileName;
  90. }
  91. //---------------------------------------------------------------------------
  92. bool __fastcall IsFileNameMask(const AnsiString Mask)
  93. {
  94. bool Masked = false;
  95. MaskFilePart("", Mask, Masked);
  96. return Masked;
  97. }
  98. //---------------------------------------------------------------------------
  99. AnsiString __fastcall DelimitFileNameMask(AnsiString Mask)
  100. {
  101. for (int i = 1; i <= Mask.Length(); i++)
  102. {
  103. if (strchr("\\*?", Mask[i]) != NULL)
  104. {
  105. Mask.Insert("\\", i);
  106. i++;
  107. }
  108. }
  109. return Mask;
  110. }
  111. //---------------------------------------------------------------------------
  112. //---------------------------------------------------------------------------
  113. TFileMasks::TParams::TParams() :
  114. Size(0)
  115. {
  116. }
  117. //---------------------------------------------------------------------------
  118. //---------------------------------------------------------------------------
  119. bool __fastcall TFileMasks::IsMask(const AnsiString Mask)
  120. {
  121. return (Mask.LastDelimiter("?*[") > 0);
  122. }
  123. //---------------------------------------------------------------------------
  124. bool __fastcall TFileMasks::SingleMaskMatch(const AnsiString Mask, const AnsiString FileName)
  125. {
  126. return MatchesFileMask(FileName, Mask);
  127. }
  128. //---------------------------------------------------------------------------
  129. __fastcall TFileMasks::TFileMasks()
  130. {
  131. FMasks = "";
  132. }
  133. //---------------------------------------------------------------------------
  134. __fastcall TFileMasks::TFileMasks(const TFileMasks & Source)
  135. {
  136. Masks = Source.Masks;
  137. }
  138. //---------------------------------------------------------------------------
  139. __fastcall TFileMasks::TFileMasks(const AnsiString AMasks)
  140. {
  141. FMasks = AMasks;
  142. }
  143. //---------------------------------------------------------------------------
  144. bool __fastcall TFileMasks::MatchesFileMask(const AnsiString & Filename, const AnsiString & Mask)
  145. {
  146. bool Result;
  147. if (Mask == "*.*")
  148. {
  149. Result = true;
  150. }
  151. else if ((Mask == "*.") && (Filename.Pos(".") == 0))
  152. {
  153. Result = true;
  154. }
  155. else
  156. {
  157. Result = ::MatchesMask(Filename, Mask);
  158. }
  159. return Result;
  160. }
  161. //---------------------------------------------------------------------------
  162. bool __fastcall TFileMasks::MatchesMask(AnsiString FileName, bool Directory,
  163. AnsiString Path, const TParams * Params, AnsiString Masks)
  164. {
  165. bool Result = true;
  166. char NextDelimiter = '\0';
  167. while (Result && !Masks.IsEmpty())
  168. {
  169. AnsiString M;
  170. char Delimiter = NextDelimiter;
  171. M = CutToChars(Masks, "<>", true, &NextDelimiter);
  172. if (Delimiter != '\0')
  173. {
  174. if (Directory || (Params == NULL))
  175. {
  176. Result = false;
  177. }
  178. else
  179. {
  180. enum { Less, LessEqual, Greater, GreaterEqual } Op;
  181. int Offset = 0;
  182. Op = ((Delimiter == '>') ? Greater : Less);
  183. if ((M.Length() >= 1) && (M[1] == '='))
  184. {
  185. Op = ((Op == Less) ? LessEqual : GreaterEqual);
  186. Offset++;
  187. }
  188. __int64 Size = ParseSize(M.SubString(Offset + 1, M.Length() - Offset));
  189. switch (Op)
  190. {
  191. case Less:
  192. Result = (Params->Size < Size);
  193. break;
  194. case LessEqual:
  195. Result = (Params->Size <= Size);
  196. break;
  197. case Greater:
  198. Result = (Params->Size > Size);
  199. break;
  200. case GreaterEqual:
  201. Result = (Params->Size >= Size);
  202. break;
  203. }
  204. }
  205. }
  206. else if (!M.IsEmpty())
  207. {
  208. int D = M.LastDelimiter("\\/");
  209. bool DirectoryMask = (D > 0) && (D == M.Length());
  210. // directory masks match only directories,
  211. // non-directory masks match anything
  212. Result = (!DirectoryMask || Directory);
  213. if (Result)
  214. {
  215. bool PathMatch = true;
  216. if (DirectoryMask)
  217. {
  218. M.SetLength(M.Length() - 1);
  219. D = M.LastDelimiter("\\/");
  220. }
  221. if (D > 0)
  222. {
  223. AnsiString MP = ToUnixPath(M.SubString(1, D - 1));
  224. // 'Path' must already have unix slashes
  225. PathMatch = ::MatchesMask(Path, MP);
  226. M = M.SubString(D + 1, M.Length() - D);
  227. }
  228. Result = (PathMatch && MatchesFileMask(FileName, M));
  229. }
  230. }
  231. }
  232. return Result;
  233. }
  234. //---------------------------------------------------------------------------
  235. bool __fastcall TFileMasks::Matches(AnsiString FileName, bool Directory,
  236. AnsiString Path, const TParams * Params) const
  237. {
  238. AnsiString S = Masks;
  239. while (!S.IsEmpty())
  240. {
  241. AnsiString M;
  242. M = CutToChars(S, ",;", true);
  243. if (MatchesMask(FileName, Directory, Path, Params, M))
  244. {
  245. return true;
  246. }
  247. }
  248. return false;
  249. }
  250. //---------------------------------------------------------------------------
  251. bool __fastcall TFileMasks::Matches(AnsiString FileName, bool Local,
  252. bool Directory, const TParams * Params) const
  253. {
  254. AnsiString Path;
  255. if (Local)
  256. {
  257. Path = ExtractFilePath(FileName);
  258. if (!Path.IsEmpty())
  259. {
  260. Path = ToUnixPath(ExcludeTrailingBackslash(Path));
  261. }
  262. FileName = ExtractFileName(FileName);
  263. }
  264. else
  265. {
  266. Path = UnixExcludeTrailingBackslash(UnixExtractFilePath(FileName));
  267. FileName = UnixExtractFileName(FileName);
  268. }
  269. return Matches(FileName, Directory, Path, Params);
  270. }
  271. //---------------------------------------------------------------------------
  272. bool __fastcall TFileMasks::IsValid()
  273. {
  274. int Start, Length;
  275. return IsValid(Start, Length);
  276. }
  277. //---------------------------------------------------------------------------
  278. bool __fastcall TFileMasks::IsValid(int & Start, int & Length)
  279. {
  280. AnsiString S = Masks;
  281. int IStart = 1;
  282. while (!S.IsEmpty())
  283. {
  284. AnsiString M;
  285. M = CutToChars(S, ",;", false);
  286. int P = M.Length() + 1;
  287. if (!M.IsEmpty())
  288. {
  289. try
  290. {
  291. MatchesMask("*.*", false, "", NULL, Trim(M));
  292. }
  293. catch (Exception &E)
  294. {
  295. // Ignore leading/trainling spaces
  296. while (!M.IsEmpty() && (M[1] == ' '))
  297. {
  298. IStart++;
  299. M.Delete(1, 1);
  300. }
  301. Start = IStart-1;
  302. Length = M.Trim().Length();
  303. return False;
  304. }
  305. }
  306. if (P) IStart += P;
  307. }
  308. return true;
  309. }
  310. //---------------------------------------------------------------------------
  311. bool __fastcall TFileMasks::operator ==(const TFileMasks & rhm) const
  312. {
  313. return (Masks == rhm.Masks);
  314. }
  315. //---------------------------------------------------------------------------
  316. TFileMasks & __fastcall TFileMasks::operator =(const AnsiString rhs)
  317. {
  318. Masks = rhs;
  319. return *this;
  320. }
  321. //---------------------------------------------------------------------------
  322. TFileMasks & __fastcall TFileMasks::operator =(const TFileMasks & rhm)
  323. {
  324. Masks = rhm.Masks;
  325. return *this;
  326. }
  327. //---------------------------------------------------------------------------
  328. bool __fastcall TFileMasks::operator ==(const AnsiString rhs) const
  329. {
  330. return (Masks == rhs);
  331. }
  332. //---------------------------------------------------------------------------
  333. TFileMasks & __fastcall TFileMasks::operator =(const char * rhs)
  334. {
  335. Masks = rhs;
  336. return *this;
  337. }
  338. //---------------------------------------------------------------------------
  339. //---------------------------------------------------------------------------
  340. #define TEXT_TOKEN '\255'
  341. //---------------------------------------------------------------------------
  342. const char TCustomCommand::NoQuote = '\0';
  343. const AnsiString TCustomCommand::Quotes = "\"'";
  344. //---------------------------------------------------------------------------
  345. TCustomCommand::TCustomCommand()
  346. {
  347. }
  348. //---------------------------------------------------------------------------
  349. void __fastcall TCustomCommand::GetToken(
  350. const AnsiString & Command, int Index, int & Len, char & PatternCmd)
  351. {
  352. assert(Index <= Command.Length());
  353. const char * Ptr = Command.c_str() + Index - 1;
  354. if (Ptr[0] == '!')
  355. {
  356. PatternCmd = Ptr[1];
  357. if (PatternCmd == '!')
  358. {
  359. Len = 2;
  360. }
  361. else
  362. {
  363. Len = PatternLen(Index, PatternCmd);
  364. }
  365. if (Len < 0)
  366. {
  367. throw Exception(FMTLOAD(CUSTOM_COMMAND_UNKNOWN, (PatternCmd, Index)));
  368. }
  369. else if (Len > 0)
  370. {
  371. if ((Command.Length() - Index + 1) < Len)
  372. {
  373. throw Exception(FMTLOAD(CUSTOM_COMMAND_UNTERMINATED, (PatternCmd, Index)));
  374. }
  375. }
  376. else if (Len == 0)
  377. {
  378. const char * PatternEnd = strchr(Ptr + 1, '!');
  379. if (PatternEnd == NULL)
  380. {
  381. throw Exception(FMTLOAD(CUSTOM_COMMAND_UNTERMINATED, (PatternCmd, Index)));
  382. }
  383. Len = PatternEnd - Ptr + 1;
  384. }
  385. }
  386. else
  387. {
  388. PatternCmd = TEXT_TOKEN;
  389. const char * NextPattern = strchr(Ptr, '!');
  390. if (NextPattern == NULL)
  391. {
  392. Len = Command.Length() - Index + 1;
  393. }
  394. else
  395. {
  396. Len = NextPattern - Ptr;
  397. }
  398. }
  399. }
  400. //---------------------------------------------------------------------------
  401. AnsiString __fastcall TCustomCommand::Complete(const AnsiString & Command,
  402. bool LastPass)
  403. {
  404. AnsiString Result;
  405. int Index = 1;
  406. while (Index <= Command.Length())
  407. {
  408. int Len;
  409. char PatternCmd;
  410. GetToken(Command, Index, Len, PatternCmd);
  411. if (PatternCmd == TEXT_TOKEN)
  412. {
  413. Result += Command.SubString(Index, Len);
  414. }
  415. else if (PatternCmd == '!')
  416. {
  417. if (LastPass)
  418. {
  419. Result += '!';
  420. }
  421. else
  422. {
  423. Result += Command.SubString(Index, Len);
  424. }
  425. }
  426. else
  427. {
  428. char Quote = NoQuote;
  429. if ((Index > 1) && (Index + Len - 1 < Command.Length()) &&
  430. Command.IsDelimiter(Quotes, Index - 1) &&
  431. Command.IsDelimiter(Quotes, Index + Len) &&
  432. (Command[Index - 1] == Command[Index + Len]))
  433. {
  434. Quote = Command[Index - 1];
  435. }
  436. AnsiString Pattern = Command.SubString(Index, Len);
  437. AnsiString Replacement;
  438. bool Delimit = true;
  439. if (PatternReplacement(Index, Pattern, Replacement, Delimit))
  440. {
  441. if (!LastPass)
  442. {
  443. Replacement = StringReplace(Replacement, "!", "!!",
  444. TReplaceFlags() << rfReplaceAll);
  445. }
  446. if (Delimit)
  447. {
  448. DelimitReplacement(Replacement, Quote);
  449. }
  450. Result += Replacement;
  451. }
  452. else
  453. {
  454. Result += Pattern;
  455. }
  456. }
  457. Index += Len;
  458. }
  459. return Result;
  460. }
  461. //---------------------------------------------------------------------------
  462. void __fastcall TCustomCommand::DelimitReplacement(AnsiString & Replacement, char Quote)
  463. {
  464. Replacement = ShellDelimitStr(Replacement, Quote);
  465. }
  466. //---------------------------------------------------------------------------
  467. void __fastcall TCustomCommand::Validate(const AnsiString & Command)
  468. {
  469. CustomValidate(Command, NULL);
  470. }
  471. //---------------------------------------------------------------------------
  472. void __fastcall TCustomCommand::CustomValidate(const AnsiString & Command,
  473. void * Arg)
  474. {
  475. int Index = 1;
  476. while (Index <= Command.Length())
  477. {
  478. int Len;
  479. char PatternCmd;
  480. GetToken(Command, Index, Len, PatternCmd);
  481. ValidatePattern(Command, Index, Len, PatternCmd, Arg);
  482. Index += Len;
  483. }
  484. }
  485. //---------------------------------------------------------------------------
  486. bool __fastcall TCustomCommand::FindPattern(const AnsiString & Command,
  487. char PatternCmd)
  488. {
  489. bool Result = false;
  490. int Index = 1;
  491. while (!Result && (Index <= Command.Length()))
  492. {
  493. int Len;
  494. char APatternCmd;
  495. GetToken(Command, Index, Len, APatternCmd);
  496. if (((PatternCmd != '!') && (PatternCmd == APatternCmd)) ||
  497. ((PatternCmd == '!') && (Len == 1) && (APatternCmd != TEXT_TOKEN)))
  498. {
  499. Result = true;
  500. }
  501. Index += Len;
  502. }
  503. return Result;
  504. }
  505. //---------------------------------------------------------------------------
  506. void __fastcall TCustomCommand::ValidatePattern(const AnsiString & /*Command*/,
  507. int /*Index*/, int /*Len*/, char /*PatternCmd*/, void * /*Arg*/)
  508. {
  509. }
  510. //---------------------------------------------------------------------------
  511. //---------------------------------------------------------------------------
  512. TInteractiveCustomCommand::TInteractiveCustomCommand(
  513. TCustomCommand * ChildCustomCommand)
  514. {
  515. FChildCustomCommand = ChildCustomCommand;
  516. }
  517. //---------------------------------------------------------------------------
  518. void __fastcall TInteractiveCustomCommand::Prompt(int /*Index*/,
  519. const AnsiString & /*Prompt*/, AnsiString & Value)
  520. {
  521. Value = "";
  522. }
  523. //---------------------------------------------------------------------------
  524. int __fastcall TInteractiveCustomCommand::PatternLen(int Index, char PatternCmd)
  525. {
  526. int Len;
  527. switch (PatternCmd)
  528. {
  529. case '?':
  530. Len = 0;
  531. break;
  532. default:
  533. Len = FChildCustomCommand->PatternLen(Index, PatternCmd);
  534. break;
  535. }
  536. return Len;
  537. }
  538. //---------------------------------------------------------------------------
  539. bool __fastcall TInteractiveCustomCommand::PatternReplacement(int Index, const AnsiString & Pattern,
  540. AnsiString & Replacement, bool & Delimit)
  541. {
  542. bool Result;
  543. if ((Pattern.Length() >= 3) && (Pattern[2] == '?'))
  544. {
  545. AnsiString PromptStr;
  546. int Pos = Pattern.SubString(3, Pattern.Length() - 2).Pos("?");
  547. if (Pos > 0)
  548. {
  549. Replacement = Pattern.SubString(3 + Pos, Pattern.Length() - 3 - Pos);
  550. if ((Pos > 1) && (Pattern[3 + Pos - 2] == '\\'))
  551. {
  552. Delimit = false;
  553. Pos--;
  554. }
  555. PromptStr = Pattern.SubString(3, Pos - 1);
  556. }
  557. else
  558. {
  559. PromptStr = Pattern.SubString(3, Pattern.Length() - 3);
  560. }
  561. Prompt(Index, PromptStr, Replacement);
  562. Result = true;
  563. }
  564. else
  565. {
  566. Result = false;
  567. }
  568. return Result;
  569. }
  570. //---------------------------------------------------------------------------
  571. //---------------------------------------------------------------------------
  572. TFileCustomCommand::TFileCustomCommand() :
  573. TCustomCommand()
  574. {
  575. }
  576. //---------------------------------------------------------------------------
  577. TFileCustomCommand::TFileCustomCommand(const AnsiString & FileName, const AnsiString & FileList) :
  578. TCustomCommand()
  579. {
  580. FFileName = FileName;
  581. FFileList = FileList;
  582. }
  583. //---------------------------------------------------------------------------
  584. int __fastcall TFileCustomCommand::PatternLen(int /*Index*/, char PatternCmd)
  585. {
  586. int Len;
  587. switch (PatternCmd)
  588. {
  589. case '&':
  590. Len = 2;
  591. break;
  592. default:
  593. Len = 1;
  594. break;
  595. }
  596. return Len;
  597. }
  598. //---------------------------------------------------------------------------
  599. bool __fastcall TFileCustomCommand::PatternReplacement(int /*Index*/,
  600. const AnsiString & Pattern, AnsiString & Replacement, bool & Delimit)
  601. {
  602. if (Pattern == "!&")
  603. {
  604. Replacement = FFileList;
  605. // already delimited
  606. Delimit = false;
  607. }
  608. else
  609. {
  610. assert(Pattern.Length() == 1);
  611. Replacement = FFileName;
  612. }
  613. return true;
  614. }
  615. //---------------------------------------------------------------------------
  616. void __fastcall TFileCustomCommand::Validate(const AnsiString & Command)
  617. {
  618. int Found[2] = { 0, 0 };
  619. CustomValidate(Command, &Found);
  620. if ((Found[0] > 0) && (Found[1] > 0))
  621. {
  622. throw Exception(FMTLOAD(CUSTOM_COMMAND_FILELIST_ERROR,
  623. (Found[1], Found[0])));
  624. }
  625. }
  626. //---------------------------------------------------------------------------
  627. void __fastcall TFileCustomCommand::ValidatePattern(const AnsiString & /*Command*/,
  628. int Index, int /*Len*/, char PatternCmd, void * Arg)
  629. {
  630. int * Found = static_cast<int *>(Arg);
  631. assert(Index > 0);
  632. if (PatternCmd == '&')
  633. {
  634. Found[0] = Index;
  635. }
  636. else if ((PatternCmd != TEXT_TOKEN) && (PatternLen(Index, PatternCmd) == 1))
  637. {
  638. Found[1] = Index;
  639. }
  640. }
  641. //---------------------------------------------------------------------------
  642. bool __fastcall TFileCustomCommand::IsFileListCommand(const AnsiString & Command)
  643. {
  644. return FindPattern(Command, '&');
  645. }
  646. //---------------------------------------------------------------------------
  647. bool __fastcall TFileCustomCommand::IsFileCommand(const AnsiString & Command)
  648. {
  649. return FindPattern(Command, '!') || FindPattern(Command, '&');
  650. }
  651. //---------------------------------------------------------------------------