You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

979 lines
27 KiB

  1. {
  2. Virtual File System.
  3. Overlay identifiziert über Layer-Nummer (oder direkt als Overlay-Objekt)
  4. UnionFS: Shadowing, höhere Overlay überdecken untere
  5. Lesen: oberste existierende Datei wird abgerufen
  6. Schreiben: Nur direkt über Layer; ggf. via Layer ID abrufbar
  7. Mountpunkte werden prefix-artig verwendet; erlauben das Verwenden von Datenquellen
  8. als Unterverzeichnisse
  9. }
  10. unit uvfsManager;
  11. interface
  12. uses Classes, SysUtils, Contnrs;
  13. type
  14. TvfsLayer = type Word; // größere Werte überdecken kleinere
  15. TvfsFileInfo = record
  16. Size: Int64;
  17. Attributes: Integer;
  18. ModDate: TDateTime;
  19. end;
  20. TvfsOverlay = class;
  21. TvfsDirectoryList = class;
  22. TvfsListingOptions = set of (loLayer, loPath, loRecursive, loFilter, loAttrib);
  23. TvfsDirectoryEntry = class
  24. Source: TvfsOverlay;
  25. FileInfo: TvfsFileInfo;
  26. end;
  27. EvfsError = class(Exception);
  28. IStreamHandle = interface
  29. ['{57F8D713-B231-4268-81CA-EE3CE25664FE}']
  30. function GetStream: TStream;
  31. end;
  32. TvfsDirectoryAddFunc = procedure (FileName: string; Entry: TvfsDirectoryEntry; List: TvfsDirectoryList) of object;
  33. { TvfsStreamHandleRead }
  34. TvfsStreamHandleRead = class(TInterfacedObject, IStreamHandle)
  35. private
  36. fStream: TStream;
  37. public
  38. constructor Create(aStream: TStream);
  39. destructor Destroy; override;
  40. function GetStream: TStream;
  41. end;
  42. { TvfsStreamHandleWrite }
  43. TvfsStreamHandleWrite = class;
  44. TvfsWriteFunc = procedure(Handle: TvfsStreamHandleWrite; Data: Pointer; DataSize: integer) of object;
  45. TvfsStreamHandleWrite = class(TvfsStreamHandleRead)
  46. private
  47. fData: Pointer;
  48. fSize: Integer;
  49. fFlushFunction: TvfsWriteFunc;
  50. public
  51. constructor Create(aStream: TStream; aFlushFunction: TvfsWriteFunc; Data: Pointer; DataSize: integer);
  52. destructor Destroy; override;
  53. end;
  54. (* Alles, was mit den wirklichen Daten spricht ist von [TvfsProvider] abgeleitet.
  55. *
  56. * Methoden:
  57. * FileExists - Existiert diese Datei?
  58. * Open - Öffnen einer Datei, siehe TvfsFileOpenMode
  59. * Rename - Datei oder Verzeichnis umbenennen
  60. * DirectoryIndex - Alle verfügbaren Dateien listen.
  61. * StorageName - Name, unter dem dieser Provider in VFSTAB geführt wird
  62. * StorageGetData - Daten für das ExtendedData-Feld der VFSTAB erzeugen
  63. *
  64. * Dateinamen sind immer relativ auf den Mountpunkt, also FileSpec im Konstruktor
  65. *)
  66. { TvfsProvider }
  67. TvfsFileOpenMode = (omReadOnly, // read-only, fail if not exists
  68. omReadWrite, // read/write, fail if not exists
  69. omReadWriteCreate, // read/write, create if not exists
  70. omCreateAlways); // read/write, always create empty
  71. TvfsProvider = class
  72. // Achievement Get: WTF FPC? -- virtueller Konstruktor, damit "class of" funktioniert, H- damit der leere Body keine Warnungen spammt
  73. constructor Create(const {%H-}FileSpec: string; const {%H-}ExtendedData: string=''); virtual;
  74. function GetFileInfo(const FileName: string; out FileInfo: TvfsFileInfo): boolean; virtual; abstract;
  75. function Open(const FileName: string; const OpenMode: TvfsFileOpenMode; out Stream: IStreamHandle): boolean; virtual; abstract;
  76. function Rename(const OldName, NewName: string): boolean; virtual; abstract;
  77. function Delete(const aName: String): boolean; virtual; abstract;
  78. procedure DirectoryIndex(AddFunction: TvfsDirectoryAddFunc; List: TvfsDirectoryList; Path: string; Subdirs: boolean); virtual; abstract;
  79. class function StorageName: string; virtual; abstract;
  80. function StorageGetFileSpec: string; virtual; abstract;
  81. function StorageGetData: string; virtual;
  82. end;
  83. TvfsProviderClass = class of TvfsProvider;
  84. { TvfsOverlay }
  85. TvfsOverlay = class
  86. private
  87. FListingAttrib: integer;
  88. FListingFilter: string;
  89. procedure DirectoryAdd(FileName: string; Entry: TvfsDirectoryEntry; List: TvfsDirectoryList);
  90. public
  91. Layer: TvfsLayer;
  92. Provider: TvfsProvider;
  93. Mountpoint: string;
  94. function TranslatePath(const FileName: string; out RelativeName: string): boolean;
  95. constructor Create(aLayer: TvfsLayer; aProvider: TvfsProvider; aMountpoint: string);
  96. destructor Destroy; override;
  97. function GetFileInfo(const FileName: string; out FileInfo: TvfsFileInfo): boolean;
  98. function OpenRead(const FileName: string; out Stream: IStreamHandle): boolean;
  99. function OpenWrite(const FileName: string; const CanCreate: boolean; out Stream: IStreamHandle): boolean;
  100. function CreateFile(const FileName: string; out Stream: IStreamHandle): boolean;
  101. function Rename(const OldName, NewName: string): boolean;
  102. function Delete(const aName: String): Boolean;
  103. procedure Listing(List: TvfsDirectoryList; const Options: TvfsListingOptions; const Path: string; const Filter: string; const Attrib: integer);
  104. end;
  105. TvfsDirectoryList = class(TStringList)
  106. private
  107. function GetEntry(Index: Integer): TvfsDirectoryEntry;
  108. protected
  109. procedure ClearObjects;
  110. public
  111. destructor Destroy; override;
  112. procedure Delete(Index: Integer); override;
  113. procedure Clear; override;
  114. property Entry[Index: Integer]: TvfsDirectoryEntry read GetEntry;
  115. // Eintrag einfügen. Im Fall eines Duplikats wird AObject direkt freigegeben
  116. function AddEntry(const S: String; AObject: TvfsDirectoryEntry): Integer;
  117. end;
  118. (*
  119. * Here's the magic :)
  120. *)
  121. { TvfsManager }
  122. TvfsManager = class
  123. private
  124. FLayers: TObjectList;
  125. function LocateFile(const Filename: string; const FilterLayer: boolean; const Layer: TvfsLayer): TvfsOverlay;
  126. function GetCount: integer;
  127. function GetOverlay(Index: integer): TvfsOverlay;
  128. protected
  129. FRegisteredProviders: TClassList;
  130. procedure InsertOverlay(Overlay: TvfsOverlay);
  131. public
  132. constructor Create;
  133. destructor Destroy; override;
  134. // Overlay hinzufügen
  135. function AddOverlay(const Layer: TvfsLayer; const Mountpoint: string; Provider: TvfsProvider): TvfsOverlay; overload;
  136. // Overlay (vollständig) entfernen
  137. procedure Remove(const Layer: TvfsLayer); overload;
  138. procedure Remove(const Overlay: TvfsOverlay); overload;
  139. procedure RemoveAll;
  140. // Zugriff auf den obersten Overlay mit dieser LayerID
  141. function FindOverlay(const Layer: TvfsLayer): TvfsOverlay;
  142. // Direktzugriff auf Provider
  143. property OverlayCount: integer read GetCount;
  144. property Overlay[Index: integer]: TvfsOverlay read GetOverlay;
  145. // Verzeichnislisting
  146. // List -> muss vorher erstellt werden
  147. // Options: loRecursive, sonst gibts nur das gewählte Verzeichnis (oder Root, wenn loPath nicht verwendet wird)
  148. // andere Options aktivieren die Parameter
  149. procedure Listing(List: TvfsDirectoryList; const Options: TvfsListingOptions; const Layer: TvfsLayer = 0;
  150. const Path: string = ''; const Filter: string = '*.*';
  151. const Attrib: integer = 0);
  152. // Datei aus der obersten Ebene lesen.
  153. function FileExists(const FileName: String): Boolean;
  154. function DirectoryExists(const FileName: String): Boolean;
  155. function ReadFile(const Filename: string; out Stream: IStreamHandle): Boolean;
  156. function WriteFile(const Filename: String; const CanCreate: boolean; out Stream: IStreamHandle): Boolean;
  157. function CreateFile(const Filename: String; out Stream: IStreamHandle): Boolean;
  158. function DeleteFile(const Filename: String): Integer;
  159. function RenameFile(const aOld, aNew: String): Integer;
  160. function ImmediateOverlay(const Filename: string): TvfsOverlay;
  161. // Provider registrieren
  162. procedure RegisterProvider(const ClassRef: TvfsProviderClass);
  163. // Provider zum Storage-Name suchen, nil wenn keiner gefunden
  164. function FindProvider(const StorageName: string): TvfsProviderClass;
  165. // Provider nach Einfügereihenfolge, nil wenn Index ungültig
  166. function GetProvider(const Index: integer): TvfsProviderClass;
  167. // Konfigurationsdaten speichern
  168. procedure SaveConfigFile(const Filename: string; PathBase: string='');
  169. // Konfigurationsdaten dazuladen
  170. procedure ApplyConfigFile(const Filename: string; PathBase: string='');
  171. end;
  172. const
  173. VFS_PATH_DELIM = '/';
  174. operator := (hdl: IStreamHandle): TStream;
  175. function vfsManager: TvfsManager;
  176. function vfsSystemPathDelim(s: String): string;
  177. function vfsVirtualPathDelim(s: String): string;
  178. function vfsChangePathDelim(s: String; const newPathDelim: char): string;
  179. function vfsExpandFileName(const Filename, Base: string): string;
  180. function vfsFileNameLike(const AString, APattern: String): Boolean;
  181. function vfsFileNameGlob(const AString, APattern: String): Boolean;
  182. implementation
  183. uses uutlCommon;
  184. const
  185. VFSTAB_COMMENT = '#';
  186. VFSTAB_QUOTE = '"';
  187. VFSTAB_SEPARATOR = #9;
  188. operator:=(hdl: IStreamHandle): TStream;
  189. begin
  190. Result:= hdl.GetStream;
  191. end;
  192. var
  193. VFSSingleton: TvfsManager = nil;
  194. function vfsManager: TvfsManager;
  195. begin
  196. if not Assigned(VFSSingleton) then
  197. VFSSingleton:= TvfsManager.Create;
  198. Result:= VFSSingleton;
  199. end;
  200. function vfsSystemPathDelim(s: String): string;
  201. var i:integer;
  202. begin
  203. for i:= 1 to Length(s) do
  204. if s[i] in ['\','/'] then
  205. s[i]:= PathDelim;
  206. Result:= S;
  207. end;
  208. function vfsVirtualPathDelim(s: String): string;
  209. var i:integer;
  210. begin
  211. for i:= 1 to Length(s) do
  212. if s[i] in ['\','/'] then
  213. s[i]:= VFS_PATH_DELIM;
  214. Result:= S;
  215. end;
  216. function vfsChangePathDelim(s: String; const newPathDelim: char): string;
  217. var i:integer;
  218. begin
  219. for i:= 1 to Length(s) do
  220. if s[i] in ['\','/'] then
  221. s[i]:= newPathDelim;
  222. Result:= S;
  223. end;
  224. function vfsExpandFileName(const Filename, Base: string): string;
  225. begin
  226. {$IF defined(WIN32) or defined(WIN64)}
  227. if (ExtractFileDrive(Filename)>'') then
  228. {$ELSE}
  229. if (Copy(Filename,1,1)=PathDelim) then
  230. {$IFEND}
  231. Result:= Filename
  232. else
  233. Result:= IncludeTrailingPathDelimiter(Base)+Filename;
  234. end;
  235. { Like('Delphi', 'D*p?i') -> true.}
  236. {Michael Winter}
  237. function Like(const AString, APattern: String): Boolean;
  238. var
  239. StringPtr, PatternPtr: PChar;
  240. StringRes, PatternRes: PChar;
  241. begin
  242. Result:=false;
  243. StringPtr:=PChar(AString);
  244. PatternPtr:=PChar(APattern);
  245. StringRes:=nil;
  246. PatternRes:=nil;
  247. if APattern='*' then begin Result:= true; exit end;
  248. repeat
  249. repeat // ohne vorangegangenes "*"
  250. case PatternPtr^ of
  251. #0: begin
  252. Result:=StringPtr^=#0;
  253. if Result or (StringRes=nil) or (PatternRes=nil) then
  254. Exit;
  255. StringPtr:=StringRes;
  256. PatternPtr:=PatternRes;
  257. Break;
  258. end;
  259. '*': begin
  260. inc(PatternPtr);
  261. PatternRes:=PatternPtr;
  262. Break;
  263. end;
  264. '?': begin
  265. if StringPtr^=#0 then
  266. Exit;
  267. inc(StringPtr);
  268. inc(PatternPtr);
  269. end;
  270. else begin
  271. if StringPtr^=#0 then
  272. Exit;
  273. if StringPtr^<>PatternPtr^ then begin
  274. if (StringRes=nil) or (PatternRes=nil) then
  275. Exit;
  276. StringPtr:=StringRes;
  277. PatternPtr:=PatternRes;
  278. Break;
  279. end
  280. else begin
  281. inc(StringPtr);
  282. inc(PatternPtr);
  283. end;
  284. end;
  285. end;
  286. until false;
  287. repeat // mit vorangegangenem "*"
  288. case PatternPtr^ of
  289. #0: begin
  290. Result:=true;
  291. Exit;
  292. end;
  293. '*': begin
  294. inc(PatternPtr);
  295. PatternRes:=PatternPtr;
  296. end;
  297. '?': begin
  298. if StringPtr^=#0 then
  299. Exit;
  300. inc(StringPtr);
  301. inc(PatternPtr);
  302. end;
  303. else begin
  304. repeat
  305. if StringPtr^=#0 then
  306. Exit;
  307. if StringPtr^=PatternPtr^ then
  308. Break;
  309. inc(StringPtr);
  310. until false;
  311. inc(StringPtr);
  312. StringRes:=StringPtr;
  313. inc(PatternPtr);
  314. Break;
  315. end;
  316. end;
  317. until false;
  318. until false;
  319. end;
  320. function vfsFileNameLike(const AString, APattern: String): Boolean;
  321. begin
  322. Result:= Like(AnsiLowerCaseFileName(AString), AnsiLowerCaseFileName(APattern));
  323. end;
  324. function GlobMatchPattern(Str, Pattern: PChar): boolean;
  325. var
  326. pe: PChar;
  327. sp: TStringList;
  328. pp: string;
  329. begin
  330. Result:= false;
  331. if (Str^=#0) or (Pattern^=#0) then
  332. Exit(Str^ = Pattern^);
  333. case Pattern^ of
  334. '?': ; //can't be wrong, since we already know we have at least one character left
  335. '*': begin
  336. inc(Pattern);
  337. repeat
  338. inc(Str);
  339. until (Str^= #0) or GlobMatchPattern(Str, Pattern);
  340. if (Str^= #0) then
  341. Exit(StrLen(Pattern) = 0);
  342. end;
  343. '{': begin
  344. inc(Pattern);
  345. pe:= strscan(Pattern, '}');
  346. if pe = nil then
  347. Exit(false);
  348. sp:= TStringList.Create;
  349. try
  350. sp.Delimiter:= ',';
  351. sp.DelimitedText:= Copy(Pattern, 1, {%H-}PtrUInt(pe)-{%H-}PtrUInt(Pattern));
  352. inc(pe);
  353. Pattern:= pe;
  354. for pp in sp do
  355. if GlobMatchPattern(Str, PChar(pp + Pattern)) then
  356. Exit(true);
  357. Exit(false);
  358. finally
  359. FreeAndNil(sp);
  360. end;
  361. end
  362. else
  363. if Pattern^ <> Str^ then
  364. Exit(false);
  365. end;
  366. inc(Pattern);
  367. inc(Str);
  368. Result:= GlobMatchPattern(Str, Pattern);
  369. end;
  370. function vfsFileNameGlob(const AString, APattern: String): Boolean;
  371. begin
  372. Result:= GlobMatchPattern(PChar(AString), PChar(APattern));
  373. end;
  374. { TvfsStreamHandleRead }
  375. constructor TvfsStreamHandleRead.Create(aStream: TStream);
  376. begin
  377. inherited Create;
  378. fStream:= aStream;
  379. end;
  380. destructor TvfsStreamHandleRead.Destroy;
  381. begin
  382. fStream.Free;
  383. fStream:= nil;
  384. inherited Destroy;
  385. end;
  386. function TvfsStreamHandleRead.GetStream: TStream;
  387. begin
  388. Result:= fStream;
  389. end;
  390. { TvfsStreamHandleWrite }
  391. constructor TvfsStreamHandleWrite.Create(aStream: TStream; aFlushFunction: TvfsWriteFunc; Data: Pointer; DataSize: integer);
  392. begin
  393. inherited Create(aStream);
  394. fFlushFunction:= aFlushFunction;
  395. if Assigned(Data) and (DataSize>0) then begin
  396. GetMem(fData, DataSize);
  397. Move(Data^, fData^, DataSize);
  398. end else
  399. fData:= nil;
  400. fSize:= DataSize;
  401. end;
  402. destructor TvfsStreamHandleWrite.Destroy;
  403. begin
  404. if Assigned(fFlushFunction) then
  405. fFlushFunction(Self, fData, fSize);
  406. if Assigned(fData) then
  407. Freememory(fData);
  408. inherited Destroy;
  409. end;
  410. { TvfsProvider }
  411. constructor TvfsProvider.Create(const FileSpec: string; const ExtendedData: string);
  412. begin
  413. inherited Create;
  414. end;
  415. function TvfsProvider.StorageGetData: string;
  416. begin
  417. Result:= '';
  418. end;
  419. { TvfsOverlay }
  420. constructor TvfsOverlay.Create(aLayer: TvfsLayer; aProvider: TvfsProvider; aMountpoint: string);
  421. var mp: string;
  422. begin
  423. Layer:= aLayer;
  424. Provider:= aProvider;
  425. mp:= vfsSystemPathDelim(aMountpoint);
  426. mp:= IncludeTrailingPathDelimiter(mp);
  427. while (mp>'') and (mp[1]=PathDelim) do
  428. System.Delete(mp, 1, 1);
  429. Self.Mountpoint:= mp;
  430. end;
  431. destructor TvfsOverlay.Destroy;
  432. begin
  433. FreeAndNil(Provider);
  434. inherited;
  435. end;
  436. function TvfsOverlay.GetFileInfo(const FileName: string; out FileInfo: TvfsFileInfo): boolean;
  437. var fn: string;
  438. begin
  439. Result:= TranslatePath(Filename, fn) and Provider.GetFileInfo(fn, FileInfo);
  440. end;
  441. function TvfsOverlay.OpenRead(const FileName: string; out Stream: IStreamHandle): boolean;
  442. var fn: string;
  443. begin
  444. Result:= TranslatePath(Filename, fn) and Provider.Open(fn, omReadOnly, Stream);
  445. end;
  446. function TvfsOverlay.OpenWrite(const FileName: string; const CanCreate: boolean; out Stream: IStreamHandle): boolean;
  447. const
  448. OpenModes:array[Boolean] of TvfsFileOpenMode = (omReadWrite, omReadWriteCreate);
  449. var
  450. fn: string;
  451. begin
  452. Result:= TranslatePath(Filename, fn) and Provider.Open(fn, OpenModes[CanCreate], Stream);
  453. end;
  454. function TvfsOverlay.CreateFile(const FileName: string; out Stream: IStreamHandle): boolean;
  455. var fn: string;
  456. begin
  457. Result:= TranslatePath(Filename, fn) and Provider.Open(fn, omCreateAlways, Stream);
  458. end;
  459. function TvfsOverlay.Rename(const OldName, NewName: string): boolean;
  460. var fon, fnn: string;
  461. begin
  462. Result:= TranslatePath(OldName, fon) and TranslatePath(NewName, fnn) and Provider.Rename(fon, fnn);
  463. end;
  464. function TvfsOverlay.Delete(const aName: String): Boolean;
  465. var fName: string;
  466. begin
  467. Result := TranslatePath(aName, fName) and Provider.Delete(fName);
  468. end;
  469. procedure TvfsOverlay.Listing(List: TvfsDirectoryList; const Options: TvfsListingOptions; const Path: string; const Filter: string; const Attrib: integer);
  470. var
  471. subpath: string;
  472. e: TvfsDirectoryEntry;
  473. m: string;
  474. begin
  475. subpath:= vfsSystemPathDelim(IncludeTrailingPathDelimiter(Path));
  476. if not (loPath in Options) or TranslatePath(Path, subpath) then begin
  477. m:= vfsSystemPathDelim(Mountpoint);
  478. while m>'' do begin
  479. if (not (loPath in Options)) or (0=AnsiCompareStr(Path, Copy(m, 1, Length(Path)))) then begin
  480. e:= TvfsDirectoryEntry.Create;
  481. e.FileInfo.Attributes:= faDirectory or faSymLink{%H-};
  482. e.FileInfo.ModDate:= 0;
  483. e.FileInfo.Size:= 0;
  484. e.Source:= Self;
  485. List.AddEntry(m, e);
  486. end;
  487. System.Delete(m, Length(m),1);
  488. m:= copy(m, 1, LastDelimiter(PathDelim, m));
  489. end;
  490. if loAttrib in Options then
  491. FListingAttrib:= Attrib
  492. else
  493. FListingAttrib:= Integer($FFFFFFFF);
  494. if loFilter in Options then
  495. FListingFilter:= Filter
  496. else
  497. FListingFilter:= '*';
  498. if not (loPath in Options) then
  499. subpath:= '';
  500. Provider.DirectoryIndex({$IFDEF FPC}@{$ENDIF}DirectoryAdd, List, subpath, loRecursive in Options);
  501. end;
  502. end;
  503. procedure TvfsOverlay.DirectoryAdd(FileName: string; Entry: TvfsDirectoryEntry; List: TvfsDirectoryList);
  504. var fn: string;
  505. begin
  506. Entry.Source:= Self;
  507. fn:= ExtractFileName(FileName);
  508. if ((FListingAttrib and Entry.FileInfo.Attributes) > 0) and // Attrib passt
  509. ({(Entry.FileInfo.Attributes and faDirectory >0) or } // Ist Verzeichnis, oder... ACHTUNG!!! vorerst deaktiviert. KP warum das so drin war...
  510. vfsFileNameLike(fn, FListingFilter)) then // ...DATEIname passt auf Maske
  511. List.AddEntry(vfsVirtualPathDelim(Mountpoint+Filename), Entry)
  512. else
  513. Entry.Free;
  514. end;
  515. function TvfsOverlay.TranslatePath(const FileName: string; out RelativeName: string): boolean;
  516. var ff: string;
  517. begin
  518. ff:= Copy(vfsSystemPathDelim(FileName),1, Length(Mountpoint));
  519. Result:= 0 = AnsiCompareText(ff, Mountpoint);
  520. if Result then
  521. RelativeName:= Copy(vfsSystemPathDelim(FileName),length(ff)+1, Maxint);
  522. end;
  523. { TvfsDirectoryList }
  524. destructor TvfsDirectoryList.Destroy;
  525. begin
  526. ClearObjects;
  527. inherited;
  528. end;
  529. procedure TvfsDirectoryList.Clear;
  530. begin
  531. ClearObjects;
  532. inherited;
  533. end;
  534. procedure TvfsDirectoryList.Delete(Index: Integer);
  535. var
  536. f: TvfsDirectoryEntry;
  537. begin
  538. f:= TvfsDirectoryEntry(Objects[Index]);
  539. Objects[Index]:= nil;
  540. F.Free;
  541. inherited;
  542. end;
  543. procedure TvfsDirectoryList.ClearObjects;
  544. var
  545. i: integer;
  546. f: TvfsDirectoryEntry;
  547. begin
  548. for i:= 0 to Count-1 do begin
  549. f:= TvfsDirectoryEntry(Objects[i]);
  550. Objects[i]:= nil;
  551. F.Free;
  552. end;
  553. end;
  554. function TvfsDirectoryList.GetEntry(Index: Integer): TvfsDirectoryEntry;
  555. begin
  556. Result:= TvfsDirectoryEntry(Objects[Index]);
  557. end;
  558. function TvfsDirectoryList.AddEntry(const S: String; AObject: TvfsDirectoryEntry): Integer;
  559. begin
  560. if IndexOf(S)>=0 then begin
  561. Result:= -1;
  562. AObject.Free;
  563. end else
  564. Result:= AddObject(S, AObject);
  565. end;
  566. { TvfsManager }
  567. constructor TvfsManager.Create;
  568. begin
  569. inherited Create;
  570. FLayers:= TObjectList.Create(true);
  571. FRegisteredProviders:= TClassList.Create;
  572. end;
  573. destructor TvfsManager.Destroy;
  574. begin
  575. FreeAndNil(FRegisteredProviders);
  576. FreeAndNil(FLayers);
  577. inherited;
  578. end;
  579. function TvfsManager.AddOverlay(const Layer: TvfsLayer; const Mountpoint: string; Provider: TvfsProvider): TvfsOverlay;
  580. var ol: TvfsOverlay;
  581. begin
  582. Result:= nil;
  583. ol:= TvfsOverlay.Create(Layer, Provider, Mountpoint);
  584. try
  585. InsertOverlay(ol);
  586. Result:= ol;
  587. except
  588. FreeAndNil(ol);
  589. raise;
  590. end;
  591. end;
  592. procedure TvfsManager.InsertOverlay(Overlay: TvfsOverlay);
  593. var
  594. i: integer;
  595. begin
  596. // add on top of the matching layer
  597. for i:= 0 to FLayers.Count-1 do begin
  598. if TvfsOverlay(FLayers[i]).Layer > Overlay.Layer then begin
  599. FLayers.Insert(i, Overlay);
  600. Exit;
  601. end;
  602. end;
  603. // not inserted anything? then new layer is larger than anything before
  604. FLayers.Add(Overlay);
  605. end;
  606. function TvfsManager.LocateFile(const Filename: string; const FilterLayer: boolean; const Layer: TvfsLayer): TvfsOverlay;
  607. var
  608. i: integer;
  609. ol: TvfsOverlay;
  610. dummy: TvfsFileInfo;
  611. begin
  612. Result:= nil;
  613. for i:= FLayers.Count-1 downto 0 do begin
  614. ol:= TvfsOverlay(FLayers[i]);
  615. if not FilterLayer or (ol.Layer=Layer) then begin
  616. if ol.GetFileInfo(FileName, dummy) then begin
  617. Result:= ol;
  618. exit;
  619. end;
  620. end;
  621. end;
  622. end;
  623. function TvfsManager.ReadFile(const Filename: string; out Stream: IStreamHandle): Boolean;
  624. var
  625. ol: TvfsOverlay;
  626. begin
  627. ol:= LocateFile(Filename,false,0);
  628. Result:= Assigned(ol) and ol.OpenRead(FileName, Stream);
  629. end;
  630. function TvfsManager.WriteFile(const Filename: String; const CanCreate: boolean; out Stream: IStreamHandle): Boolean;
  631. var
  632. ol: TvfsOverlay;
  633. begin
  634. ol := ImmediateOverlay(Filename);
  635. result := Assigned(ol) and ol.OpenWrite(Filename, CanCreate, Stream);
  636. end;
  637. function TvfsManager.CreateFile(const Filename: String; out Stream: IStreamHandle): Boolean;
  638. var
  639. ol: TvfsOverlay;
  640. begin
  641. ol := ImmediateOverlay(Filename);
  642. result := Assigned(ol) and ol.CreateFile(Filename, Stream);
  643. end;
  644. function TvfsManager.DeleteFile(const Filename: String): Integer;
  645. var
  646. i: integer;
  647. ol: TvfsOverlay;
  648. d: string;
  649. begin
  650. result := 0;
  651. for i := FLayers.Count-1 downto 0 do begin
  652. ol := TvfsOverlay(FLayers[i]);
  653. if ol.TranslatePath(Filename, d) then begin
  654. if ol.Delete(Filename) then
  655. inc(result);
  656. end;
  657. end;
  658. end;
  659. function TvfsManager.RenameFile(const aOld, aNew: String): Integer;
  660. var
  661. i: integer;
  662. ol: TvfsOverlay;
  663. d: string;
  664. begin
  665. result := 0;
  666. for i := FLayers.Count-1 downto 0 do begin
  667. ol := TvfsOverlay(FLayers[i]);
  668. if ol.TranslatePath(aOld, d) and ol.TranslatePath(aNew, d) then begin
  669. if ol.Rename(aOld, aNew) then
  670. inc(result);
  671. end;
  672. end;
  673. end;
  674. function TvfsManager.ImmediateOverlay(const Filename: string): TvfsOverlay;
  675. var
  676. i: integer;
  677. ol: TvfsOverlay;
  678. d: string;
  679. begin
  680. Result:= nil;
  681. for i:= FLayers.Count-1 downto 0 do begin
  682. ol:= TvfsOverlay(FLayers[i]);
  683. if ol.TranslatePath(Filename, d) then begin
  684. Result:= ol;
  685. exit;
  686. end;
  687. end;
  688. end;
  689. procedure TvfsManager.Remove(const Layer: TvfsLayer);
  690. var
  691. i: integer;
  692. begin
  693. for i:= FLayers.Count-1 downto 0 do
  694. if TvfsOverlay(FLayers[i]).Layer=Layer then
  695. FLayers.Delete(i);
  696. end;
  697. procedure TvfsManager.Remove(const Overlay: TvfsOverlay);
  698. begin
  699. FLayers.Remove(Overlay);
  700. end;
  701. procedure TvfsManager.RemoveAll;
  702. begin
  703. FLayers.Clear;
  704. end;
  705. function TvfsManager.FindOverlay(const Layer: TvfsLayer): TvfsOverlay;
  706. var
  707. i: integer;
  708. begin
  709. Result:= nil;
  710. for i:= FLayers.Count-1 downto 0 do
  711. if TvfsOverlay(FLayers[i]).Layer=Layer then begin
  712. Result:= TvfsOverlay(FLayers[i]);
  713. exit;
  714. end;
  715. end;
  716. procedure TvfsManager.Listing(List: TvfsDirectoryList; const Options: TvfsListingOptions;
  717. const Layer: TvfsLayer; const Path: string; const Filter: string;
  718. const Attrib: integer);
  719. var
  720. i: integer;
  721. begin
  722. List.Sorted:= true;
  723. List.Duplicates:= dupIgnore;
  724. for i:= FLayers.Count-1 downto 0 do
  725. if not (loLayer in Options) or (TvfsOverlay(FLayers[i]).Layer=Layer) then begin
  726. TvfsOverlay(FLayers[i]).Listing(List, Options - [loLayer], Path, Filter, Attrib);
  727. end;
  728. end;
  729. function TvfsManager.FileExists(const FileName: String): Boolean;
  730. var
  731. ol: TvfsOverlay;
  732. fi: TvfsFileInfo;
  733. begin
  734. ol:= LocateFile(Filename,false,0);
  735. Result:= Assigned(ol) and ol.GetFileInfo(FileName, fi) and ((fi.Attributes and faDirectory)=0);
  736. end;
  737. function TvfsManager.DirectoryExists(const FileName: String): Boolean;
  738. var
  739. ol: TvfsOverlay;
  740. fi: TvfsFileInfo;
  741. begin
  742. ol:= LocateFile(Filename,false,0);
  743. Result:= Assigned(ol) and ol.GetFileInfo(FileName, fi) and ((fi.Attributes and faDirectory)>0);
  744. end;
  745. function TvfsManager.GetCount: integer;
  746. begin
  747. Result:= FLayers.Count;
  748. end;
  749. function TvfsManager.GetOverlay(Index: integer): TvfsOverlay;
  750. begin
  751. Result:= TvfsOverlay(FLayers[Index]);
  752. end;
  753. procedure TvfsManager.RegisterProvider(const ClassRef: TvfsProviderClass);
  754. begin
  755. if Assigned(ClassRef) and (FRegisteredProviders.IndexOf(ClassRef)<0) then
  756. FRegisteredProviders.Add(ClassRef);
  757. end;
  758. function TvfsManager.FindProvider(const StorageName: string): TvfsProviderClass;
  759. var
  760. i: integer;
  761. begin
  762. Result:= nil;
  763. for i:= FRegisteredProviders.Count-1 downto 0 do
  764. if AnsiCompareText(StorageName, TvfsProviderClass(FRegisteredProviders[i]).StorageName)=0 then begin
  765. Result:= TvfsProviderClass(FRegisteredProviders[i]);
  766. break;
  767. end;
  768. end;
  769. function TvfsManager.GetProvider(const Index: integer): TvfsProviderClass;
  770. begin
  771. if (Index>=0) and (Index<FRegisteredProviders.Count) then
  772. Result:= TvfsProviderClass(FRegisteredProviders[Index])
  773. else
  774. Result:= nil;
  775. end;
  776. procedure TvfsManager.SaveConfigFile(const Filename: string; PathBase: string);
  777. var
  778. tab: TStringList;
  779. cols: TutlCSVList;
  780. i: integer;
  781. o: TvfsOverlay;
  782. begin
  783. if PathBase='' then
  784. PathBase:= ExtractFilePath(Filename);
  785. PathBase:= IncludeTrailingPathDelimiter(vfsSystemPathDelim(PathBase));
  786. tab:= TStringList.Create;
  787. try
  788. tab.Add(VFSTAB_COMMENT+'GENERATED FILE; DO NOT EDIT');
  789. cols:= TutlCSVList.Create;
  790. try
  791. cols.Delimiter:= VFSTAB_SEPARATOR;
  792. cols.QuoteChar:= VFSTAB_QUOTE;
  793. for i:= 0 to OverlayCount-1 do begin
  794. o:= Overlay[i];
  795. cols.Clear;
  796. cols.Add(IntToStr(o.Layer));
  797. cols.Add(o.Mountpoint);
  798. cols.Add(o.Provider.StorageName);
  799. cols.Add(ExtractRelativepath(PathBase,o.Provider.StorageGetFileSpec));
  800. cols.Add(o.Provider.StorageGetData);
  801. tab.Add(cols.StrictDelimitedText);
  802. end;
  803. tab.SaveToFile(Filename);
  804. finally
  805. FreeAndNil(cols);
  806. end;
  807. finally
  808. FreeAndNil(tab);
  809. end;
  810. end;
  811. procedure TvfsManager.ApplyConfigFile(const Filename: string; PathBase: string);
  812. var
  813. tab: TStringList;
  814. cols: TutlCSVList;
  815. l: integer;
  816. line, s: string;
  817. ly: TvfsLayer;
  818. mp,tp,fs,mo: string;
  819. tc: TvfsProvider;
  820. tcc: TvfsProviderClass;
  821. begin
  822. if PathBase = '' then begin
  823. s := IncludeTrailingPathDelimiter(GetCurrentDir) + Filename;
  824. if SysUtils.FileExists(s) then
  825. PathBase := ExtractFilePath(s)
  826. else
  827. PathBase := ExtractFilePath(Filename);
  828. end;
  829. PathBase:= IncludeTrailingPathDelimiter(vfsSystemPathDelim(PathBase));
  830. tab:= TStringList.Create;
  831. try
  832. cols:= TutlCSVList.Create;
  833. try
  834. cols.Delimiter:= VFSTAB_SEPARATOR;
  835. cols.QuoteChar:= VFSTAB_QUOTE;
  836. cols.SkipDelims:= true;
  837. tab.LoadFromFile(Filename);
  838. for l:= 0 to tab.Count - 1 do begin
  839. line:= trim(tab[l]);
  840. if (line='') or (line[1]=VFSTAB_COMMENT) then
  841. continue;
  842. cols.StrictDelimitedText:= line;
  843. ly:= StrToInt(cols[0]);
  844. mp:= cols[1];
  845. tp:= cols[2];
  846. fs:= vfsExpandFileName(cols[3], PathBase);
  847. if cols.Count>4 then
  848. mo:= cols[4]
  849. else
  850. mo:= '';
  851. tcc:= FindProvider(tp);
  852. if Assigned(tcc) then begin
  853. tc:= tcc.Create(fs, mo);
  854. AddOverlay(ly, mp, tc);
  855. end else
  856. raise EvfsError.CreateFmt('Unsupported Overlay Provider: "%s"',[tp]);
  857. end;
  858. finally
  859. FreeAndNil(cols);
  860. end;
  861. finally
  862. FreeAndNil(tab);
  863. end;
  864. end;
  865. initialization
  866. finalization
  867. FreeAndNil(VFSSingleton);
  868. end.