FileMasks.cpp 16 KB

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