(* Tux Commander - UVFSCore - VFS Core utilities and management Copyright (C) 2007 Tomas Bzatek Check for updates on tuxcmd.sourceforge.net This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) unit UVFSCore; interface uses GTKForms, ULibc, Classes, uVFSprototypes, UEngines, UCoreUtils; type TVFSPlugin = class private FVFSNew: TVFSNew; FVFSFree: TVFSFree; FVFSVersion: TVFSVersion; FVFSGetInfo: TVFSGetInfo; FVFSOpen: TVFSOpen; FVFSClose: TVFSClose; FVFSListFirst: TVFSListFirst; FVFSListNext: TVFSListNext; FVFSListClose: TVFSListClose; FVFSChangeDir: TVFSChangeDir; FVFSGetPath: TVFSGetPath; FVFSGetPathURI: TVFSGetPathURI; FVFSGetPrefix: TVFSGetPrefix; FVFSGetFileSystemSize: TVFSGetFileSystemSize; FVFSGetFileSystemFree: TVFSGetFileSystemFree; FVFSFileExists: TVFSFileExists; FVFSFileInfo: TVFSFileInfo; FVFSMkDir: TVFSMkDir; FVFSRemove: TVFSRemove; FVFSRename: TVFSRename; FVFSMakeSymLink: TVFSMakeSymLink; FVFSChmod: TVFSChmod; FVFSChown: TVFSChown; FVFSChangeTimes: TVFSChangeTimes; FVFSGetDirSize: TVFSGetDirSize; FVFSBreakGetDirSize: TVFSBreakGetDirSize; FVFSOpenFile: TVFSOpenFile; FVFSReadFile: TVFSReadFile; FVFSWriteFile: TVFSWriteFile; FVFSCloseFile: TVFSCloseFile; FVFSFileSeek: TVFSFileSeek; FVFSSetBlockSize: TVFSSetBlockSize; FVFSCopyToLocal: TVFSCopyToLocal; FVFSCopyFromLocal: TVFSCopyFromLocal; FVFSIsOnSameFS: TVFSIsOnSameFS; FVFSTwoSameFiles: TVFSTwoSameFiles; FVFSGetArchiveExts: TVFSGetArchiveExts; FVFSGetNetworkServices: TVFSGetNetworkServices; FVFSGetPasswordRequired: TVFSGetPasswordRequired; FVFSSetCallbacks: TVFSSetCallbacks; FVFSResetPassword: TVFSResetPassword; function GetHandlesArchives: boolean; function GetHandlesNetwork: boolean; public ModuleHandle: Pointer; FullPath: string; // module path Extensions, Services: TOpenStringArray; // the list of the extensions plugin can handle constructor Create(PluginHandle: Pointer); destructor Destroy; override; function VFSVersion: integer; function ModuleID: string; function ModuleName: string; function ModuleAbout: string; function ModuleCopyright: string; published property HandlesArchives: boolean read GetHandlesArchives; property HandlesNetwork: boolean read GetHandlesNetwork; end; TVFSEngine = class(TPanelEngine) private FGlobs: Pointer; FSourcePlugin: TVFSPlugin; FBlockSize: Cardinal; BreakProcessingKind: integer; function GetPluginID: string; public ArchiveMode: boolean; Password: string; PasswordUsed: boolean; RemoveFileOnClose: string; OpenedFromQuickConnect: boolean; CustomPluginIDSave: string; constructor Create(SourcePlugin: TVFSPlugin); function VFSOpenURI(URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function VFSOpenEx(OpenFile: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): TVFSResult; function VFSClose: boolean; destructor Destroy; override; function GetListing(var List: TList; const AddDotFiles: boolean): integer; override; function GetListing(var List: TList; const AddDotFiles: boolean; APath: string): integer; override; function ChangeDir(const NewPath: string): integer; override; function ChangeDirEx(const NewPath: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): integer; function ExplicitChDir(const NewPath: string): integer; override; function GetFileSystemSize: Int64; override; function GetFileSystemSize(const APath: string): Int64; override; function GetFileSystemFree: Int64; override; function GetFileSystemFree(const APath: string): Int64; override; function MakeDir(const NewDir: string): integer; override; function GetDirSize(APath: string): Int64; override; function Remove(APath: string): integer; override; procedure FillDirFiles(APath: string; List: TList; ALevel: word); override; function GetFileInfoSL(APath: string): PDataItemSL; override; function FileExists(const FileName: string; const Use_lstat: boolean = False): Boolean; override; function DirectoryExists(const FileName: string; const Use_lstat: boolean = False): Boolean; override; function MakeSymLink(const NewFileName, PointTo: string): integer; override; function Chmod(const FileName: string; const Mode: integer): integer; override; function Chown(const FileName: string; const UID, GID: integer): integer; override; procedure BreakProcessing(ProcessingKind: integer); override; function RenameFile(SourceFile, DestFile: string): integer; override; function ChangeTimes(APath: string; mtime, atime: Int64): integer; override; procedure GetFileSystemInfo(const APath: string; var FSSize, FSFree: Int64; var FSName: string); override; function OpenFile(const APath: string; Mode: integer; var Error: integer): TEngineFileDes; override; function ReadFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; ABlockSize: integer; var Error: integer): integer; override; function WriteFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; BytesCount: integer; var Error: integer): integer; override; function CloseFile(const FileDescriptor: TEngineFileDes): integer; override; function FileSeek(const FileDescriptor: TEngineFileDes; const AbsoluteOffset: Int64; var Error: integer): Int64; override; function IsOnROMedium(const FileName: string): boolean; override; function FileCanRun(const FileName: string): boolean; override; function GetPath: string; override; procedure SetPath(Value: string); override; function GetPrefix: string; override; function GetPathURI: string; function GetBlockSize: Cardinal; override; procedure SetBlockSize(Value: Cardinal); override; function CopyFileIn(Sender: Pointer; SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; override; function CopyFileOut(Sender: Pointer; SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; override; function CopyFileInEx(Sender: Pointer; SourceFile, DestFile: string; ErrorFunc: TEngineErrorFunc; Append: boolean; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function CopyFileOutEx(Sender: Pointer; SourceFile, DestFile: string; ErrorFunc: TEngineErrorFunc; Append: boolean; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function IsOnSameFS(const Path1, Path2: string): boolean; override; function TwoSameFiles(const Path1, Path2: string): boolean; override; function GetPasswordRequired: boolean; procedure ResetPassword; published property Path: string read GetPath write SetPath; property BlockSize: Cardinal read GetBlockSize write SetBlockSize; property PluginID: string read GetPluginID; end; procedure DoInitPlugins; var PluginList: TList; implementation uses SysUtils, UConfig, ULocale; const ConstGlobalModulePath1 = '/usr/lib/tuxcmd'; ConstGlobalModulePath2 = '/usr/local/lib/tuxcmd'; ConstLocalModulePath1 = '~/.tuxcmd/plugins'; ConstLocalModulePath2 = './plugins'; ConstLocalModulePath3 = '../lib/tuxcmd'; ConstGlobalModulePath3 = '/usr/lib64/tuxcmd'; ConstGlobalModulePath4 = '/usr/local/lib64/tuxcmd'; ConstLocalModulePath4 = '../lib64/tuxcmd'; ConstVFSVersionRequired = cVFSVersion; var BinaryPath: string; procedure VFSLogFunc(const s: PChar); cdecl; begin DebugMsg([' ### VFS ###: ', s]); end; (********************************************************************************************************************************) constructor TVFSPlugin.Create(PluginHandle: Pointer); var s: PChar; begin inherited Create; ModuleHandle := PluginHandle; // Find the symbols @FVFSNew := dlsym(ModuleHandle, 'VFSNew'); @FVFSFree := dlsym(ModuleHandle, 'VFSFree'); @FVFSVersion := dlsym(ModuleHandle, 'VFSVersion'); @FVFSGetInfo := dlsym(ModuleHandle, 'VFSGetInfo'); @FVFSOpen := dlsym(ModuleHandle, 'VFSOpen'); @FVFSClose := dlsym(ModuleHandle, 'VFSClose'); @FVFSListFirst := dlsym(ModuleHandle, 'VFSListFirst'); @FVFSListNext := dlsym(ModuleHandle, 'VFSListNext'); @FVFSListClose := dlsym(ModuleHandle, 'VFSListClose'); @FVFSGetPath := dlsym(ModuleHandle, 'VFSGetPath'); @FVFSGetPathURI := dlsym(ModuleHandle, 'VFSGetPathURI'); @FVFSChangeDir := dlsym(ModuleHandle, 'VFSChangeDir'); @FVFSGetPrefix := dlsym(ModuleHandle, 'VFSGetPrefix'); @FVFSGetFileSystemSize := dlsym(ModuleHandle, 'VFSGetFileSystemSize'); @FVFSGetFileSystemFree := dlsym(ModuleHandle, 'VFSGetFileSystemFree'); @FVFSFileExists := dlsym(ModuleHandle, 'VFSFileExists'); @FVFSFileInfo := dlsym(ModuleHandle, 'VFSFileInfo'); @FVFSMkDir := dlsym(ModuleHandle, 'VFSMkDir'); @FVFSRemove := dlsym(ModuleHandle, 'VFSRemove'); @FVFSRename := dlsym(ModuleHandle, 'VFSRename'); @FVFSMakeSymLink := dlsym(ModuleHandle, 'VFSMakeSymLink'); @FVFSChmod := dlsym(ModuleHandle, 'VFSChmod'); @FVFSChown := dlsym(ModuleHandle, 'VFSChown'); @FVFSChangeTimes := dlsym(ModuleHandle, 'VFSChangeTimes'); @FVFSGetDirSize := dlsym(ModuleHandle, 'VFSGetDirSize'); @FVFSBreakGetDirSize := dlsym(ModuleHandle, 'VFSBreakGetDirSize'); @FVFSCopyToLocal := dlsym(ModuleHandle, 'VFSCopyToLocal'); @FVFSCopyFromLocal := dlsym(ModuleHandle, 'VFSCopyFromLocal'); @FVFSOpenFile := dlsym(ModuleHandle, 'VFSOpenFile'); @FVFSReadFile := dlsym(ModuleHandle, 'VFSReadFile'); @FVFSWriteFile := dlsym(ModuleHandle, 'VFSWriteFile'); @FVFSCloseFile := dlsym(ModuleHandle, 'VFSCloseFile'); @FVFSFileSeek := dlsym(ModuleHandle, 'VFSFileSeek'); @FVFSSetBlockSize := dlsym(ModuleHandle, 'VFSSetBlockSize'); @FVFSIsOnSameFS := dlsym(ModuleHandle, 'VFSIsOnSameFS'); @FVFSTwoSameFiles := dlsym(ModuleHandle, 'VFSTwoSameFiles'); @FVFSGetArchiveExts := dlsym(ModuleHandle, 'VFSGetArchiveExts'); @FVFSGetNetworkServices := dlsym(ModuleHandle, 'VFSGetNetworkServices'); @FVFSGetPasswordRequired := dlsym(ModuleHandle, 'VFSGetPasswordRequired'); @FVFSSetCallbacks := dlsym(ModuleHandle, 'VFSSetCallbacks'); @FVFSResetPassword := dlsym(ModuleHandle, 'VFSResetPassword'); // Initialize the extensions list SetLength(Extensions, 0); SetLength(Services, 0); if @FVFSGetArchiveExts <> nil then begin s := FVFSGetArchiveExts; if s <> nil then begin ParseString(String(s), ';', Extensions); real_libc_free(s); end; end; if @FVFSGetNetworkServices <> nil then begin s := FVFSGetNetworkServices; if s <> nil then begin ParseString(String(s), ';', Services); real_libc_free(s); end; end; end; destructor TVFSPlugin.Destroy; begin SetLength(Extensions, 0); SetLength(Services, 0); if ModuleHandle <> nil then dlclose(ModuleHandle); inherited Destroy; end; (********************************************************************************************************************************) function TVFSPlugin.VFSVersion: integer; begin if @FVFSVersion <> nil then Result := FVFSVersion else Result := -1; end; procedure _free_PVFSInfo(Info: PVFSInfo); begin if Info <> nil then begin if Info^.Name <> nil then real_libc_free(Info^.Name); if Info^.ID <> nil then real_libc_free(Info^.ID); if Info^.About <> nil then real_libc_free(Info^.About); if Info^.Copyright <> nil then real_libc_free(Info^.Copyright); end; end; function TVFSPlugin.ModuleID: string; var Info: PVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := String(Info.ID); _free_PVFSInfo(Info); end else Result := ''; end; function TVFSPlugin.ModuleName: string; var Info: PVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := String(Info.Name); _free_PVFSInfo(Info); end else Result := ''; end; function TVFSPlugin.ModuleAbout: string; var Info: PVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := String(Info.About); _free_PVFSInfo(Info); end else Result := ''; end; function TVFSPlugin.ModuleCopyright: string; var Info: PVFSInfo; begin if @FVFSGetInfo <> nil then begin Info := FVFSGetInfo; Result := String(Info.Copyright); _free_PVFSInfo(Info); end else Result := ''; end; function TVFSPlugin.GetHandlesArchives: boolean; begin Result := Length(Extensions) > 0; end; function TVFSPlugin.GetHandlesNetwork: boolean; begin Result := Length(Services) > 0; end; (********************************************************************************************************************************) (********************************************************************************************************************************) constructor TVFSEngine.Create(SourcePlugin: TVFSPlugin); begin inherited Create; FSourcePlugin := SourcePlugin; FBlockSize := 65536; ArchiveMode := False; BreakProcessingKind := 0; FGlobs := nil; Password := ''; PasswordUsed := False; RemoveFileOnClose := ''; OpenedFromQuickConnect := False; CustomPluginIDSave := ''; if @FSourcePlugin.FVFSNew <> nil then FGlobs := FSourcePlugin.FVFSNew(@VFSLogFunc); end; destructor TVFSEngine.Destroy; begin try if @FSourcePlugin.FVFSFree <> nil then FSourcePlugin.FVFSFree(FGlobs); if Length(Trim(RemoveFileOnClose)) > 0 then if unlink(PChar(RemoveFileOnClose)) <> 0 then DebugMsg(['(EE) TVFSEngine.Destroy: error while removing ''', RemoveFileOnClose, ''': ', strerror(errno)]); except on E: Exception do DebugMsg(['*** TVFSEngine.Destroy() -Exception: ', E.Message]); end; end; function TVFSEngine.VFSOpenURI(URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; begin Result := False; if (FGlobs <> nil) and (@FSourcePlugin.FVFSOpen <> nil) then begin if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := FSourcePlugin.FVFSOpen(FGlobs, PChar(URI)) = cVFS_OK; if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; end; function TVFSEngine.VFSOpenEx(OpenFile: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): TVFSResult; begin Result := cVFS_OK; if (FGlobs <> nil) and (@FSourcePlugin.FVFSOpen <> nil) then begin if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := FSourcePlugin.FVFSOpen(FGlobs, PChar(OpenFile)); if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; end; function TVFSEngine.VFSClose: boolean; begin Result := False; if (FGlobs <> nil) and (@FSourcePlugin.FVFSClose <> nil) then Result := FSourcePlugin.FVFSClose(FGlobs) = cVFS_OK; end; function TVFSEngine.GetListing(var List: TList; const AddDotFiles: boolean; APath: string): integer; var P: PVFSItem; Item: PDataItem; i, Res: integer; begin DebugMsg(['^^VFS (II): GetListing begin']); Result := 0; try if @FSourcePlugin.FVFSListFirst = nil then Exit; P := real_libc_malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); // DebugMsg(['Item = ', Int64(P)]); // DebugMsg(['FVFSListFirst']); Res := FSourcePlugin.FVFSListFirst(FGlobs, PChar(APath), P); if Res <> cVFS_OK then begin FSourcePlugin.FVFSListClose(FGlobs); if Res = cVFS_Not_More_Files then Result := 0 else Result := Res; real_libc_free(P); Exit; end; repeat // DebugMsg(['begin--']); if AddDotFiles or (not ((Length(P^.FName) > 1) and (P^.FName[0] = '.') and (P^.FName[1] <> '.'))) then begin // DebugMsg(['Checkpoint 1']); Item := malloc(SizeOf(TDataItem)); memset(Item, 0, SizeOf(TDataItem)); // DebugMsg(['Checkpoint 2']); for i := 0 to Length(Item^.ColumnData) - 1 do Item^.ColumnData[i] := nil; // DebugMsg(['Checkpoint 3']); with Item^ do try FName := strdup(P^.FName); FDisplayName := strdup(P^.FDisplayName); if P^.sLinkTo <> nil then begin LnkPointTo := strdup(P^.sLinkTo); DebugMsg(['LnkPointTo = ', P^.sLinkTo]); end else LnkPointTo := nil; Mode := P^.iMode; // DebugMsg(['Checkpoint 4']); IsDotFile := (Length(FName) > 1) and (FName[0] = '.') and (FName[1] <> '.'); IsDir := TVFSItemType(P^.ItemType) = vDirectory; IsLnk := TVFSItemType(P^.ItemType) = vSymlink; IsBlk := TVFSItemType(P^.ItemType) = vBlockdev; IsChr := TVFSItemType(P^.ItemType) = vChardev; IsFIFO := TVFSItemType(P^.ItemType) = vFifo; IsSock := TVFSItemType(P^.ItemType) = vSock; // DebugMsg(['Checkpoint 5']); ModifyTime := P^.m_time; // DebugMsg(['Returned datetime: ', Longword(P^.m_time)]); // DebugMsg(['Checkpoint 6']); UID := P^.iUID; // DebugMsg(['Checkpoint 7']); GID := P^.iGID; // DebugMsg(['Checkpoint 8']); UpDir := False; // DebugMsg(['Checkpoint 9']); Selected := False; // DebugMsg(['Checkpoint 10']); Size := P^.iSize; // DebugMsg(['Checkpoint 11']); List.Add(Item); // DebugMsg(['Checkpoint 12']); except on E: Exception do DebugMsg(['^^VFS (EE): GetListing: Item-Exception: ', E.Message]); end; end; // of if AddDotFiles if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); // Not needed - just zero-erase the memory // DebugMsg(['Checkpoint 13']); P := real_libc_malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); // DebugMsg(['Item = ', Int64(P)]); // DebugMsg(['Checkpoint 14']); // DebugMsg(['FVFSListNext --begin']); Res := FSourcePlugin.FVFSListNext(FGlobs, PChar(APath), P); // DebugMsg(['FVFSListNext --end']); // Sleep(500); until (Res <> cVFS_OK) or (BreakProcessingKind = 2); if BreakProcessingKind <> 0 then DebugMsg(['^^VFS (WW): GetListing: stopped by BreakProcessing']); real_libc_free(P); FSourcePlugin.FVFSListClose(FGlobs); if Res <> cVFS_Not_More_Files then Result := Res; except on E: Exception do DebugMsg(['^^VFS (EE): GetListing: Exception: ', E.Message]); end; BreakProcessingKind := 0; DebugMsg(['^^VFS (II): GetListing end.']); end; function TVFSEngine.GetListing(var List: TList; const AddDotFiles: boolean): integer; begin Result := GetListing(List, AddDotFiles, GetPath); end; function TVFSEngine.ExplicitChDir(const NewPath: string): integer; begin Result := libc_chdir(PChar(NewPath)); if Result <> 0 then Result := errno; end; function TVFSEngine.GetFileSystemSize: Int64; begin Result := GetFileSystemSize(GetPath); end; function TVFSEngine.GetFileSystemSize(const APath: string): Int64; begin if (FGlobs <> nil) and (@FSourcePlugin.FVFSGetFileSystemSize <> nil) then Result := FSourcePlugin.FVFSGetFileSystemSize(FGlobs, PChar(APath)) else Result := 0; end; function TVFSEngine.GetFileSystemFree: Int64; begin Result := GetFileSystemFree(GetPath); end; function TVFSEngine.GetFileSystemFree(const APath: string): Int64; begin if (FGlobs <> nil) and (@FSourcePlugin.FVFSGetFileSystemFree <> nil) then Result := FSourcePlugin.FVFSGetFileSystemFree(FGlobs, PChar(APath)) else Result := 0; end; procedure TVFSEngine.GetFileSystemInfo(const APath: string; var FSSize, FSFree: Int64; var FSName: string); begin FSSize := GetFileSystemSize(APath); FSFree := GetFileSystemFree(APath); FSName := 'plugin'; end; function TVFSEngine.IsOnROMedium(const FileName: string): boolean; begin Result := True; end; function TVFSEngine.FileCanRun(const FileName: string): boolean; var Item: PDataItemSL; begin Item := GetFileInfoSL(FileName); Result := Assigned(Item) and Item^.IsExecutable; FreeDataItem(Item); end; function TVFSEngine.GetPrefix: string; var s: PChar; begin Result := 'VFS'; if (FGlobs <> nil) and (@FSourcePlugin.FVFSGetPrefix <> nil) then begin s := FSourcePlugin.FVFSGetPrefix(FGlobs); if s <> nil then begin Result := String(s); real_libc_free(s); end; end; end; function TVFSEngine.GetPath: string; var s: PChar; begin Result := '/'; if (FGlobs <> nil) and (@FSourcePlugin.FVFSGetPath <> nil) then begin s := FSourcePlugin.FVFSGetPath(FGlobs); if s <> nil then begin Result := String(s); real_libc_free(s); end; end; end; function TVFSEngine.GetPathURI: string; var s: PChar; begin Result := ''; if (FGlobs <> nil) and (@FSourcePlugin.FVFSGetPathURI <> nil) then begin s := FSourcePlugin.FVFSGetPathURI(FGlobs); if s <> nil then begin Result := String(s); real_libc_free(s); end; end; end; function TVFSEngine.ChangeDir(const NewPath: string): integer; begin DebugMsg(['^^VFS (II): ChangeDir begin']); Result := 0; try Result := FSourcePlugin.FVFSChangeDir(FGlobs, PChar(NewPath)); // Sleep(3000); except on E: Exception do DebugMsg(['^^VFS (EE): ChangeDir: Exception: ', E.Message]); end; DebugMsg(['^^VFS (II): ChangeDir end.']); end; function TVFSEngine.ChangeDirEx(const NewPath: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): integer; begin if (FGlobs <> nil) and (@FSourcePlugin.FVFSChangeDir <> nil) then begin if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := ChangeDir(NewPath); if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; end; procedure TVFSEngine.SetPath(Value: string); begin ChangeDir(Value); end; function TVFSEngine.FileExists(const FileName: string; const Use_lstat: boolean = False): Boolean; begin if (FGlobs <> nil) and (@FSourcePlugin.FVFSFileExists <> nil) then Result := FSourcePlugin.FVFSFileExists(FGlobs, PChar(FileName), Use_lstat) else Result := False; end; function TVFSEngine.DirectoryExists(const FileName: string; const Use_lstat: boolean = False): Boolean; var P: PVFSItem; Res: integer; begin if @FSourcePlugin.FVFSFileExists <> nil then begin Result := FSourcePlugin.FVFSFileExists(FGlobs, PChar(FileName), Use_lstat); if Result and (@FSourcePlugin.FVFSFileInfo <> nil) then begin P := real_libc_malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(FileName), P); if (Res <> cVFS_OK) or (P = nil) or (TVFSItemType(P^.ItemType) <> vDirectory) then Result := False; real_libc_free(P); end; end else Result := False; end; function TVFSEngine.GetFileInfoSL(APath: string): PDataItemSL; var P: PVFSItem; Item: PDataItemSL; Res: integer; begin Result := nil; if @FSourcePlugin.FVFSFileInfo = nil then Exit; P := real_libc_malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), P); if Res <> cVFS_OK then begin DebugMsg(['*** VFSFileInfo(', APath, ') failed. Code = ', Res]); Exit; end; try Item := malloc(SizeOf(TDataItemSL)); memset(Item, 0, SizeOf(TDataItemSL)); with Item^ do begin { FName := strdup(P^.FName); FDisplayName := StrToUTF8(P^.FName); } FName := strdup(PChar(APath)); //* TODO FDisplayName := StrToUTF8(PChar(APath)); if P^.sLinkTo <> nil then LnkPointTo := strdup(P^.sLinkTo) else LnkPointTo := nil; ADestination := nil; Stage1 := True; Level := 0; IsDir := TVFSItemType(P^.ItemType) = vDirectory; IsLnk := TVFSItemType(P^.ItemType) = vSymlink; ForceMove := False; {***} IsOnRO := True; IsExecutable := P^.iMode and S_IXUSR = S_IXUSR; Mode := P^.iMode; ModifyTime := P^.m_time; mtime := P^.m_time; atime := P^.a_time; UID := P^.iUID; GID := P^.iGID; Size := P^.iSize; PackedSize := P^.iPackedSize; if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); end; Result := Item; except on E: Exception do DebugMsg(['*** TVFSEngine.GetFileInfoSL(APath=', APath, ') -Exception: ', E.Message]); end; end; function TVFSEngine.MakeDir(const NewDir: string): integer; begin if @FSourcePlugin.FVFSMkDir <> nil then Result := FSourcePlugin.FVFSMkDir(FGlobs, PChar(NewDir)) else Result := cVFS_Failed; end; function TVFSEngine.Remove(APath: string): integer; begin if @FSourcePlugin.FVFSRemove <> nil then Result := FSourcePlugin.FVFSRemove(FGlobs, PChar(APath)) else Result := cVFS_Failed; end; function TVFSEngine.RenameFile(SourceFile, DestFile: string): integer; begin if @FSourcePlugin.FVFSRename <> nil then Result := FSourcePlugin.FVFSRename(FGlobs, PChar(SourceFile), PChar(DestFile)) else Result := cVFS_Failed; end; procedure TVFSEngine.FillDirFiles(APath: string; List: TList; ALevel: word); var Item: PDataItemSL; i, Res: integer; FilesList: TList; LocalList: TStringList; P: PVFSItem; procedure AddEntry(FPath: string; AddCurrDirStage, AStage1: boolean); begin Item := malloc(SizeOf(TDataItemSL)); memset(Item, 0, SizeOf(TDataItemSL)); with Item^ do begin // AName := malloc(Length(FPath) + 1); // memset(AName, 0, Length(FPath) + 1); FName := strdup(PChar(FPath)); //* TODO FDisplayName := StrToUTF8(PChar(FPath)); if P^.sLinkTo <> nil then LnkPointTo := strdup(P^.sLinkTo) else LnkPointTo := nil; ADestination := nil; Stage1 := AStage1; IsDir := TVFSItemType(P^.ItemType) = vDirectory; IsLnk := TVFSItemType(P^.ItemType) = vSymlink; if IsLnk and AddCurrDirStage then DebugMsg(['*** Assertion failed AddEntry: Item^.IsLnk = True']); ForceMove := False; {***} IsOnRO := True; IsExecutable := AddCurrDirStage or (P^.iMode and S_IXUSR = S_IXUSR); Mode := P^.iMode; ModifyTime := P^.m_time; mtime := P^.m_time; atime := P^.a_time; UID := P^.iUID; GID := P^.iGID; Size := P^.iSize; PackedSize := P^.iPackedSize; Level := ALevel + Ord(not AddCurrDirStage); end; if AddCurrDirStage then List.Add(Item) else FilesList.Add(Item); end; begin if not Assigned(List) then Exit; FilesList := TList.Create; LocalList := TStringList.Create; try try P := real_libc_malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), P); if Res <> cVFS_OK then DebugMsg(['*** FillDirFiles - VFSFileInfo(', APath, ') failed. Code = ', Res]); AddEntry(APath, True, True); if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); APath := IncludeTrailingPathDelimiter(APath); if @FSourcePlugin.FVFSChangeDir <> nil then Res := FSourcePlugin.FVFSChangeDir(FGlobs, PChar(APath)) else Exit; if Res <> 0 then Exit; if @FSourcePlugin.FVFSListFirst = nil then Exit; P := real_libc_malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSListFirst(FGlobs, PChar(APath), P); if Res <> cVFS_OK then begin FSourcePlugin.FVFSListClose(FGlobs); if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); Exit; end; repeat if TVFSItemType(P^.ItemType) = vDirectory then LocalList.Add(APath + String(P^.FName)) else AddEntry(APath + String(P^.FName), False, True); if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); P := real_libc_malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSListNext(FGlobs, PChar(GetPath), P); until (Res <> cVFS_OK); if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); FSourcePlugin.FVFSListClose(FGlobs); if LocalList.Count > 0 then for i := 0 to LocalList.Count - 1 do FillDirFiles(LocalList[i], List, ALevel + 1); if FilesList.Count > 0 then for i := 0 to FilesList.Count - 1 do List.Add(FilesList[i]); except on E: Exception do DebugMsg(['*** TVFSEngine.FillDirFiles(APath=', APath, ', Level=', ALevel, ') -Exception: ', E.Message]); end; finally P := real_libc_malloc(SizeOf(TVFSItem)); memset(P, 0, SizeOf(TVFSItem)); Res := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), P); if Res <> cVFS_OK then DebugMsg(['*** FillDirFiles - VFSFileInfo(', APath, ') failed. Code = ', Res]); AddEntry(APath, True, False); if P^.FName <> nil then real_libc_free(P^.FName); if P^.FDisplayName <> nil then real_libc_free(P^.FDisplayName); if P^.sLinkTo <> nil then real_libc_free(P^.sLinkTo); real_libc_free(P); LocalList.Free; FilesList.Free; end; end; function TVFSEngine.MakeSymLink(const NewFileName, PointTo: string): integer; begin if @FSourcePlugin.FVFSMakeSymLink <> nil then Result := FSourcePlugin.FVFSMakeSymLink(FGlobs, PChar(NewFileName), PChar(PointTo)) else Result := cVFS_Failed; end; function TVFSEngine.Chmod(const FileName: string; const Mode: integer): integer; begin if @FSourcePlugin.FVFSChmod <> nil then Result := FSourcePlugin.FVFSChmod(FGlobs, PChar(FileName), Mode) else Result := cVFS_Failed; end; function TVFSEngine.Chown(const FileName: string; const UID, GID: integer): integer; begin if @FSourcePlugin.FVFSChown <> nil then Result := FSourcePlugin.FVFSChown(FGlobs, PChar(FileName), UID, GID) else Result := cVFS_Failed; end; function TVFSEngine.ChangeTimes(APath: string; mtime, atime: Int64): integer; begin if @FSourcePlugin.FVFSChangeTimes <> nil then Result := FSourcePlugin.FVFSChangeTimes(FGlobs, PChar(APath), mtime, atime) else Result := cVFS_Failed; end; function TVFSEngine.GetDirSize(APath: string): Int64; begin if @FSourcePlugin.FVFSGetDirSize <> nil then Result := FSourcePlugin.FVFSGetDirSize(FGlobs, PChar(APath)) else Result := 0; end; procedure TVFSEngine.BreakProcessing(ProcessingKind: integer); begin case ProcessingKind of 1: if @FSourcePlugin.FVFSBreakGetDirSize <> nil then FSourcePlugin.FVFSBreakGetDirSize(FGlobs); end; end; function TVFSEngine.GetBlockSize: Cardinal; begin Result := FBlockSize; end; procedure TVFSEngine.SetBlockSize(Value: Cardinal); begin if (FGlobs <> nil) and (@FSourcePlugin.FVFSSetBlockSize <> nil) then FSourcePlugin.FVFSSetBlockSize(FGlobs, Value); end; (********************************************************************************************************************************) function TVFSEngine.IsOnSameFS(const Path1, Path2: string): boolean; begin if @FSourcePlugin.FVFSIsOnSameFS <> nil then Result := FSourcePlugin.FVFSIsOnSameFS(FGlobs, PChar(Path1), PChar(Path2)) else Result := True; end; function TVFSEngine.OpenFile(const APath: string; Mode: integer; var Error: integer): TEngineFileDes; var i: integer; begin if @FSourcePlugin.FVFSOpenFile <> nil then begin Result := FSourcePlugin.FVFSOpenFile(FGlobs, PChar(APath), Mode, @i); Error := i; end else Result := nil; end; function TVFSEngine.ReadFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; ABlockSize: integer; var Error: integer): integer; var i: integer; begin if @FSourcePlugin.FVFSReadFile <> nil then begin Result := FSourcePlugin.FVFSReadFile(FGlobs, FileDescriptor, Buffer, ABlockSize, @i); Error := i; end else Result := -1; end; function TVFSEngine.WriteFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; BytesCount: integer; var Error: integer): integer; var i: integer; begin if @FSourcePlugin.FVFSWriteFile <> nil then begin Result := FSourcePlugin.FVFSWriteFile(FGlobs, FileDescriptor, Buffer, BytesCount, @i); Error := i; end else Result := -1; end; function TVFSEngine.CloseFile(const FileDescriptor: TEngineFileDes): integer; begin if @FSourcePlugin.FVFSCloseFile <> nil then Result := FSourcePlugin.FVFSCloseFile(FGlobs, FileDescriptor) else Result := 0; end; function TVFSEngine.FileSeek(const FileDescriptor: TEngineFileDes; const AbsoluteOffset: Int64; var Error: integer): Int64; var i: integer; begin if @FSourcePlugin.FVFSFileSeek <> nil then begin Result := FSourcePlugin.FVFSFileSeek(FGlobs, FileDescriptor, AbsoluteOffset, @i); Error := i; end else Result := -1; end; function TVFSEngine.TwoSameFiles(const Path1, Path2: string): boolean; begin if @FSourcePlugin.FVFSTwoSameFiles <> nil then Result := FSourcePlugin.FVFSTwoSameFiles(FGlobs, PChar(Path1), PChar(Path2)) else Result := False; end; (********************************************************************************************************************************) function TVFSEngine.CopyFileIn(Sender: Pointer; SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; begin Result := CopyFileInEx(Sender, SourceFile, DestFile, ErrorFunc, Append, nil, nil, nil, nil); end; function TVFSEngine.CopyFileOut(Sender: Pointer; SourceFile, DestFile: string; ProgressFunc: TEngineProgressFunc; ErrorFunc: TEngineErrorFunc; Append: boolean): boolean; begin Result := CopyFileInEx(Sender, SourceFile, DestFile, ErrorFunc, Append, nil, nil, nil, nil); end; function TVFSEngine.CopyFileOutEx(Sender: Pointer; SourceFile, DestFile: string; ErrorFunc: TEngineErrorFunc; Append: boolean; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; var Res: TVFSResult; begin Result := False; try if @FSourcePlugin.FVFSCopyToLocal <> nil then begin // DebugMsg(['0 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']); // DebugMsg([' Pointer(FGlobs) = 0x', IntToHex(Int64(FGlobs), 16), ', Pointer(@NewVFSCopyCallBackFunc) = 0x', IntToHex(Int64(@NewVFSCopyCallBackFunc), 16), ', Pointer(Self) = 0x', IntToHex(Int64(Self), 16)]); try if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Res := FSourcePlugin.FVFSCopyToLocal(FGlobs, PChar(SourceFile), PChar(DestFile), Append); if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); except on E: Exception do begin DebugMsg(['*** Exception raised in TVFSEngine.CopyFileOut(Sender=', QWord(Sender), ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); Res := cVFS_WriteErr; end; end; // DebugMsg(['1 $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$']); Result := Res = cVFS_OK; if (Res <> cVFS_OK) and Assigned(ErrorFunc) then case Res of cVFS_ReadErr: Result := ErrorFunc(Sender, 6, 0, SourceFile); cVFS_WriteErr: Result := ErrorFunc(Sender, 7, 0, DestFile); cVFS_mallocFailed: ErrorFunc(Sender, 1, 0, SourceFile); cVFS_Cancelled: ErrorFunc(Sender, 0, 0, SourceFile); end; end else Result := False; except on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.CopyFileOut(Sender=', QWord(Sender), ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); end; end; function TVFSEngine.CopyFileInEx(Sender: Pointer; SourceFile, DestFile: string; ErrorFunc: TEngineErrorFunc; Append: boolean; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; var Res: TVFSResult; begin Result := False; try if @FSourcePlugin.FVFSCopyFromLocal <> nil then begin try if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Res := FSourcePlugin.FVFSCopyFromLocal(FGlobs, PChar(SourceFile), PChar(DestFile), Append); if @FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); except on E: Exception do begin DebugMsg(['*** Exception raised in TVFSEngine.CopyFileIn(Sender=', QWord(Sender), ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); Res := cVFS_WriteErr; end; end; Result := Res = cVFS_OK; if (Res <> cVFS_OK) and Assigned(ErrorFunc) then case Res of cVFS_ReadErr: Result := ErrorFunc(Sender, 6, 0, SourceFile); cVFS_WriteErr: Result := ErrorFunc(Sender, 7, 0, DestFile); cVFS_mallocFailed: ErrorFunc(Sender, 1, 0, SourceFile); cVFS_Cancelled: ErrorFunc(Sender, 0, 0, SourceFile); end; end else Result := False; except on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.CopyFileIn(Sender=', QWord(Sender), ', SourceFile=', SourceFile, ', DestFile=', DestFile, '): (', E.ClassName, '): ', E.Message]); end; end; (********************************************************************************************************************************) (********************************************************************************************************************************) function TVFSEngine.GetPasswordRequired: boolean; begin if @FSourcePlugin.FVFSGetPasswordRequired <> nil then Result := FSourcePlugin.FVFSGetPasswordRequired(FGlobs) else Result := False; end; procedure TVFSEngine.ResetPassword; begin if @FSourcePlugin.FVFSResetPassword <> nil then FSourcePlugin.FVFSResetPassword(FGlobs); end; (********************************************************************************************************************************) (********************************************************************************************************************************) function TVFSEngine.GetPluginID: string; begin if FSourcePlugin <> nil then Result := FSourcePlugin.ModuleID else Result := ''; end; (********************************************************************************************************************************) (********************************************************************************************************************************) function GetBinaryPath: string; var i: integer; Buf: array[0..1024] of char; begin Result := ParamStr(0); // Fallback try DebugMsg(['Trying to determine binary path...']); DebugMsg(['Fallback path = ', Result]); Buf[0] := #0; FillChar(Buf, 1025, 0); i := readlink(PChar(Format('/proc/%d/exe', [getpid])), @Buf, 1024); if i < 1 then begin DebugMsg(['Something went wrong during readlink: ', string(strerror(errno))]); Exit; end; Buf[i] := #0; Result := string(PChar(@Buf)); DebugMsg(['Path to executable = ', Result]); except on E: Exception do DebugMsg(['*** Exception raised in GetBinaryPath: (', E.ClassName, '): ', E.Message]); end; end; procedure QueryModules(APath: string); var Handle: PDIR; DirEnt: PDirent64; s: string; PluginItem: TVFSPlugin; ModuleHandler: Pointer; VFSVersionFunc: TVFSVersion; i: integer; Buf: PChar; b: boolean; begin if Length(APath) < 1 then Exit; try try APath := ReplaceStr(APath, '~', GetHomePath); DebugMsg(['------------------ Querying modules in ', APath, ' ---']); // First change to the program starting directory if APath[1] <> '/' then begin s := ExtractFilePath(BinaryPath); DebugMsg(['----> Changing dir to ', s]); i := libc_chdir(PChar(s)); if i <> 0 then DebugMsg(['Something went wrong during chdir: ', string(strerror(errno))]); s := get_current_dir_name; DebugMsg([' [II] Changed dir to ', s]); end; // Change to the required directory s := APath; DebugMsg(['----> Changing dir to ', s]); i := libc_chdir(PChar(s)); if i <> 0 then begin DebugMsg(['Something went wrong during chdir: ', string(strerror(errno))]); Exit; end; s := get_current_dir_name; DebugMsg([' [II] Changed dir to ', s, ', trying to list the files...']); // Going to read the directory and search for the files... Handle := opendir(PChar(s)); if not Assigned(Handle) then begin DebugMsg(['Something went wrong during opendir: ', string(strerror(errno))]); Exit; end; repeat DirEnt := readdir64(Handle); if Assigned(DirEnt) then begin Buf := PChar(@DirEnt^.d_name[0]); if Assigned(Buf) and (strcmp(Buf, '.') <> 0) and (strcmp(Buf, '..') <> 0) then begin DebugMsg(['$$$ Found ', Buf, ', trying to load...']); // Try to find the plugin in the plugin list to prevent multiple plugin loading b := False; if PluginList.Count > 0 then for i := 0 to PluginList.Count - 1 do if CompareStr(TVFSPlugin(PluginList[i]).FullPath, IncludeTrailingPathDelimiter(s) + Buf) = 0 then begin b := True; Break; end; if b then DebugMsg(['Module ', s, ' is already loaded --> skipping...']) else begin ModuleHandler := dlopen(PChar(IncludeTrailingPathDelimiter(s) + Buf), RTLD_NOW); if ModuleHandler = nil then DebugMsg([' XXX Error loading module: ', dlerror]) else try @VFSVersionFunc := dlsym(ModuleHandler, 'VFSVersion'); if (@VFSVersionFunc <> nil) and (VFSVersionFunc = ConstVFSVersionRequired) then begin PluginItem := TVFSPlugin.Create(ModuleHandler); PluginItem.FullPath := IncludeTrailingPathDelimiter(s) + Buf; PluginList.Add(PluginItem); end else DebugMsg([' $XXX: Error getting version info or version mismatch']); except end; end; end; end; until DirEnt = nil; closedir(Handle); except on E: Exception do DebugMsg(['*** Exception raised in QueryModules(APath = ', APath, '): (', E.ClassName, '): ', E.Message]); end; finally DebugMsg(['------------------ Done querying modules ---------------']); end; end; procedure UnloadModules; var i: integer; begin if Assigned(PluginList) and (PluginList.Count > 0) then for i := PluginList.Count - 1 downto 0 do try dlclose(TVFSPlugin(PluginList[i]).ModuleHandle); TVFSPlugin(PluginList[i]).Free; PluginList.Delete(i); except on E: Exception do DebugMsg(['*** Exception raised in UnloadModules(i = ', i, '): (', E.ClassName, '): ', E.Message]); end; end; procedure DoInitPlugins; begin PluginList := TList.Create; if not ParamDisablePlugins then begin BinaryPath := GetBinaryPath; QueryModules(ConstGlobalModulePath1); QueryModules(ConstGlobalModulePath2); QueryModules(ConstLocalModulePath1); QueryModules(ConstLocalModulePath2); QueryModules(ConstLocalModulePath3); {$IFDEF CPU64} QueryModules(ConstGlobalModulePath3); QueryModules(ConstGlobalModulePath4); QueryModules(ConstLocalModulePath4); {$ENDIF} end; end; initialization finalization UnloadModules; PluginList.Free; end.