FileMasks.cpp 20 KB

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