(* 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, lazglib2, Classes, uVFSprototypes, UEngines, UCoreUtils; type TVFSPlugin = class private FVFSNew: TVFSNew; FVFSFree: TVFSFree; FVFSVersion: TVFSVersion; FVFSGetInfo: TVFSGetInfo; FVFSOpenArchive: TVFSOpenArchive; FVFSOpenURI: TVFSOpenURI; FVFSClose: TVFSClose; FVFSListFirst: TVFSListFirst; FVFSListNext: TVFSListNext; FVFSListClose: TVFSListClose; FVFSChangeDir: TVFSChangeDir; FVFSGetPath: TVFSGetPath; FVFSGetPathURI: TVFSGetPathURI; FVFSGetFileSystemInfo: TVFSGetFileSystemInfo; 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; FVFSStartCopyOperation: TVFSStartCopyOperation; FVFSStopCopyOperation: TVFSStopCopyOperation; FVFSGetCapabilities: TVFSGetCapabilities; function GetHandlesArchives: boolean; function GetHandlesNetwork: boolean; public ModuleHandle: Pointer; FullModulePath: 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 BreakProcessingType: integer; FGlobs: Pointer; FSourcePlugin: TVFSPlugin; FBlockSize: Cardinal; FArchiveMode: boolean; FArchivePath: string; FCopyProgressCallback: PVFSProgressCallback; FCopyCallbackData: Pointer; function GetPluginID: string; function GetDataItemFromVFSItem(P: PVFSItem): PDataItem; function GetArchiveStreamingType: boolean; public Password: string; PasswordUsed: boolean; RemoveFileOnClose: string; OpenedFromQuickConnect: boolean; CustomPluginIDSave: string; constructor Create(SourcePlugin: TVFSPlugin); destructor Destroy; override; function GetListing(List: TList; const APath: string; AddDotFiles, FollowSymlinks, AddFullPath: boolean; Error: PPGError): boolean; override; function GetFileInfo(const APath: string; FollowSymlinks, AddFullPath: boolean; Error: PPGError): PDataItem; override; function ChangeDir(const NewPath: string; Error: PPGError): boolean; override; function GetPath: string; override; procedure SetPath(Value: string); override; function GetDirSize(const APath: string): Int64; override; procedure BreakProcessing(ProcessingKind: integer); override; function FileExists(const FileName: string; FollowSymlinks: boolean): boolean; override; function DirectoryExists(const FileName: string; FollowSymlinks: boolean): boolean; override; procedure GetFileSystemInfo(const APath: string; var FSSize, FSFree: Int64; var FSName: string); override; function IsOnROMedium(const FileName: string): boolean; override; function FileCanRun(const FileName: string): boolean; override; function MakeDir(const NewDir: string; Error: PPGError): boolean; override; function Remove(const APath: string; Error: PPGError): boolean; override; function MakeSymLink(const NewFileName, PointTo: string; Error: PPGError): boolean; override; function Chmod(const FileName: string; Mode: cuLong; Error: PPGError): boolean; override; function Chown(const FileName: string; UID, GID: cuLong; Error: PPGError): boolean; override; function RenameFile(const SourceFile, DestFile: string; Error: PPGError): boolean; override; function ChangeTimes(const APath: string; mtime, atime: time_t; Error: PPGError): boolean; override; function GetBlockSize: guint32; override; procedure SetBlockSize(Value: guint32); override; function CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; override; function CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; override; function IsOnSameFS(const Path1, Path2: string; FollowSymlinks: boolean): boolean; override; function TwoSameFiles(const Path1, Path2: string; FollowSymlinks: boolean): boolean; override; function OpenFile(const APath: string; Mode: integer; Error: PPGError): TEngineFileDes; override; function ReadFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; ABlockSize: integer; Error: PPGError): integer; override; function WriteFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; BytesCount: integer; Error: PPGError): integer; override; function CloseFile(const FileDescriptor: TEngineFileDes; Error: PPGError): boolean; override; function FileSeek(const FileDescriptor: TEngineFileDes; const AbsoluteOffset: Int64; Error: PPGError): Int64; override; // VFS additions function VFSOpenURI(const URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer; Error: PPGError): boolean; function VFSOpenEx(const AFile: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer; Error: PPGError): boolean; function VFSClose(Error: PPGError): boolean; function ChangeDirEx(const NewPath: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer; Error: PPGError): boolean; function GetPathURI: string; function GetPasswordRequired: boolean; procedure ResetPassword; // callbacks here are used for next volume prompts, password prompts (encrypted archives) function StartCopyOperation(AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function StopCopyOperation(ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; function CopyFileOutEx(const SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; function CopyFileInEx(const SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; published property PluginID: string read GetPluginID; property ArchiveMode: boolean read FArchiveMode; property ArchivePath: string read FArchivePath; property ArchiveStreamingType: boolean read GetArchiveStreamingType; end; procedure DoInitPlugins; function LookupVFSPlugin(const ModuleID: string): TVFSPlugin; var PluginList: TList; implementation uses SysUtils, UConfig, ULocale, UError; 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 := TVFSNew(dlsym(ModuleHandle, 'VFSNew')); FVFSFree := TVFSFree(dlsym(ModuleHandle, 'VFSFree')); FVFSVersion := TVFSVersion(dlsym(ModuleHandle, 'VFSVersion')); FVFSGetInfo := TVFSGetInfo(dlsym(ModuleHandle, 'VFSGetInfo')); FVFSOpenArchive := TVFSOpenArchive(dlsym(ModuleHandle, 'VFSOpenArchive')); FVFSOpenURI := TVFSOpenURI(dlsym(ModuleHandle, 'VFSOpenURI')); FVFSClose := TVFSClose(dlsym(ModuleHandle, 'VFSClose')); FVFSListFirst := TVFSListFirst(dlsym(ModuleHandle, 'VFSListFirst')); FVFSListNext := TVFSListNext(dlsym(ModuleHandle, 'VFSListNext')); FVFSListClose := TVFSListClose(dlsym(ModuleHandle, 'VFSListClose')); FVFSGetPath := TVFSGetPath(dlsym(ModuleHandle, 'VFSGetPath')); FVFSGetPathURI := TVFSGetPathURI(dlsym(ModuleHandle, 'VFSGetPathURI')); FVFSChangeDir := TVFSChangeDir(dlsym(ModuleHandle, 'VFSChangeDir')); FVFSGetFileSystemInfo := TVFSGetFileSystemInfo(dlsym(ModuleHandle, 'VFSGetFileSystemInfo')); FVFSFileInfo := TVFSFileInfo(dlsym(ModuleHandle, 'VFSFileInfo')); FVFSMkDir := TVFSMkDir(dlsym(ModuleHandle, 'VFSMkDir')); FVFSRemove := TVFSRemove(dlsym(ModuleHandle, 'VFSRemove')); FVFSRename := TVFSRename(dlsym(ModuleHandle, 'VFSRename')); FVFSMakeSymLink := TVFSMakeSymLink(dlsym(ModuleHandle, 'VFSMakeSymLink')); FVFSChmod := TVFSChmod(dlsym(ModuleHandle, 'VFSChmod')); FVFSChown := TVFSChown(dlsym(ModuleHandle, 'VFSChown')); FVFSChangeTimes := TVFSChangeTimes(dlsym(ModuleHandle, 'VFSChangeTimes')); FVFSGetDirSize := TVFSGetDirSize(dlsym(ModuleHandle, 'VFSGetDirSize')); FVFSBreakGetDirSize := TVFSBreakGetDirSize(dlsym(ModuleHandle, 'VFSBreakGetDirSize')); FVFSCopyToLocal := TVFSCopyToLocal(dlsym(ModuleHandle, 'VFSCopyToLocal')); FVFSCopyFromLocal := TVFSCopyFromLocal(dlsym(ModuleHandle, 'VFSCopyFromLocal')); FVFSOpenFile := TVFSOpenFile(dlsym(ModuleHandle, 'VFSOpenFile')); FVFSReadFile := TVFSReadFile(dlsym(ModuleHandle, 'VFSReadFile')); FVFSWriteFile := TVFSWriteFile(dlsym(ModuleHandle, 'VFSWriteFile')); FVFSCloseFile := TVFSCloseFile(dlsym(ModuleHandle, 'VFSCloseFile')); FVFSFileSeek := TVFSFileSeek(dlsym(ModuleHandle, 'VFSFileSeek')); FVFSSetBlockSize := TVFSSetBlockSize(dlsym(ModuleHandle, 'VFSSetBlockSize')); FVFSIsOnSameFS := TVFSIsOnSameFS(dlsym(ModuleHandle, 'VFSIsOnSameFS')); FVFSTwoSameFiles := TVFSTwoSameFiles(dlsym(ModuleHandle, 'VFSTwoSameFiles')); FVFSGetArchiveExts := TVFSGetArchiveExts(dlsym(ModuleHandle, 'VFSGetArchiveExts')); FVFSGetNetworkServices := TVFSGetNetworkServices(dlsym(ModuleHandle, 'VFSGetNetworkServices')); FVFSGetPasswordRequired := TVFSGetPasswordRequired(dlsym(ModuleHandle, 'VFSGetPasswordRequired')); FVFSSetCallbacks := TVFSSetCallbacks(dlsym(ModuleHandle, 'VFSSetCallbacks')); FVFSResetPassword := TVFSResetPassword(dlsym(ModuleHandle, 'VFSResetPassword')); FVFSStartCopyOperation := TVFSStartCopyOperation(dlsym(ModuleHandle, 'VFSStartCopyOperation')); FVFSStopCopyOperation := TVFSStopCopyOperation(dlsym(ModuleHandle, 'VFSStopCopyOperation')); FVFSGetCapabilities := TVFSGetCapabilities(dlsym(ModuleHandle, 'VFSGetCapabilities')); // Initialize the extensions list SetLength(Extensions, 0); SetLength(Services, 0); if (FVFSGetArchiveExts <> nil) and (FVFSOpenArchive <> nil) then begin s := FVFSGetArchiveExts(); if s <> nil then begin ParseString(String(s), ';', Extensions); real_libc_free(s); end; end; if (FVFSGetNetworkServices <> nil) and (FVFSOpenURI <> 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) and (FVFSOpenArchive <> nil); end; function TVFSPlugin.GetHandlesNetwork: boolean; begin Result := (Length(Services) > 0) and (FVFSOpenURI <> nil); end; (********************************************************************************************************************************) (********************************************************************************************************************************) constructor TVFSEngine.Create(SourcePlugin: TVFSPlugin); begin inherited Create; BreakProcessingType := 0; FSourcePlugin := SourcePlugin; FBlockSize := 65536; FArchiveMode := False; FArchivePath := ''; FGlobs := nil; Password := ''; PasswordUsed := False; RemoveFileOnClose := ''; OpenedFromQuickConnect := False; CustomPluginIDSave := ''; FCopyProgressCallback := nil; FCopyCallbackData := nil; if FSourcePlugin.FVFSNew <> nil then FGlobs := FSourcePlugin.FVFSNew(PVFSLogFunc(@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(const URI: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer; Error: PPGError): boolean; begin Result := False; try if (FGlobs <> nil) and (FSourcePlugin.FVFSOpenURI <> nil) then begin if FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := FSourcePlugin.FVFSOpenURI(FGlobs, PChar(URI), Error); FArchiveMode := False; FArchivePath := ''; if FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; except on E: Exception do begin tuxcmd_set_error_from_exception(Error, E); Result := False; end; end; end; function TVFSEngine.VFSOpenEx(const AFile: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer; Error: PPGError): boolean; begin Result := False; try if (FGlobs <> nil) and (FSourcePlugin.FVFSOpenArchive <> nil) then begin if FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := FSourcePlugin.FVFSOpenArchive(FGlobs, PChar(AFile), Error); FArchiveMode := True; if Result then FArchivePath := AFile else FArchivePath := ''; if FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; except on E: Exception do begin tuxcmd_set_error_from_exception(Error, E); Result := False; end; end; end; (********************************************************************************************************************************) function TVFSEngine.VFSClose(Error: PPGError): boolean; begin Result := False; if (FGlobs <> nil) and (FSourcePlugin.FVFSClose <> nil) then Result := FSourcePlugin.FVFSClose(FGlobs, Error); end; function TVFSEngine.GetDataItemFromVFSItem(P: PVFSItem): PDataItem; var Item: PDataItem; begin Item := malloc(sizeof(TDataItem)); memset(Item, 0, sizeof(TDataItem)); Item^.UpDir := False; Item^.Selected := False; Item^.FName := g_strdup(P^.FName); Item^.FDisplayName := g_strdup(P^.FDisplayName); Item^.LnkPointTo := g_strdup(P^.sLinkTo); Item^.Mode := P^.iMode; Item^.IsDotFile := (Length(P^.FName) > 1) and (P^.FName[0] = '.'); Item^.IsExecutable := (P^.iMode and S_IXUSR) = S_IXUSR; Item^.IsDir := TVFSItemType(P^.ItemType) = vDirectory; Item^.IsLnk := P^.IsLink; Item^.IsBlk := TVFSItemType(P^.ItemType) = vBlockdev; Item^.IsChr := TVFSItemType(P^.ItemType) = vChardev; Item^.IsFIFO := TVFSItemType(P^.ItemType) = vFifo; Item^.IsSock := TVFSItemType(P^.ItemType) = vSock; Item^.mtime := P^.m_time; Item^.atime := P^.a_time; Item^.ctime := P^.c_time; Item^.UID := P^.iUID; Item^.GID := P^.iGID; Item^.Size := P^.iSize; Item^.PackedSize := P^.iPackedSize; Item^.inode_no := P^.inode_no; Result := Item; end; function TVFSEngine.GetListing(List: TList; const APath: string; AddDotFiles, FollowSymlinks, AddFullPath: boolean; Error: PPGError): boolean; var P: PVFSItem; Item: PDataItem; begin DebugMsg(['^^VFS (II): GetListing begin']); Result := False; try if (FSourcePlugin.FVFSListFirst = nil) or (FSourcePlugin.FVFSListNext = nil) or (FSourcePlugin.FVFSListClose = nil) then begin g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'File listing not supported by the VFS module.'); Exit; end; P := FSourcePlugin.FVFSListFirst(FGlobs, PChar(APath), FollowSymlinks, AddFullPath, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_OPENDIR); while (P <> nil) and (BreakProcessingType <> 2) do begin if (strlen(P^.FName) > 0) and (AddDotFiles or (P^.FName[0] <> '.')) then begin Item := GetDataItemFromVFSItem(P); List.Add(Item); end; 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 := FSourcePlugin.FVFSListNext(FGlobs, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_OPENDIR); end; Result := Error^ = nil; if BreakProcessingType <> 0 then begin DebugMsg(['^^VFS (WW): GetListing: stopped by BreakProcessing']); if Error^ <> nil then g_error_free(Error^); Error^ := nil; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_CANCELLED), 'The operation has been cancelled.'); Result := False; end; real_libc_free(P); FSourcePlugin.FVFSListClose(FGlobs, nil); except on E: Exception do begin tuxcmd_set_error_from_exception(Error, E); DebugMsg(['^^VFS (EE): GetListing: Exception: ', E.Message]); Result := False; end; end; BreakProcessingType := 0; DebugMsg(['^^VFS (II): GetListing end.']); end; function TVFSEngine.GetFileInfo(const APath: string; FollowSymlinks, AddFullPath: boolean; Error: PPGError): PDataItem; var P: PVFSItem; begin DebugMsg(['^^VFS (II): GetFileInfo begin']); Result := nil; if FSourcePlugin.FVFSFileInfo = nil then begin g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Querying file info is not supported by the VFS module.'); Exit; end; try P := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(APath), FollowSymlinks, AddFullPath, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_STAT); if P <> nil then begin Result := GetDataItemFromVFSItem(P); 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; except on E: Exception do begin DebugMsg(['^^VFS (EE): GetFileInfo: Exception: ', E.Message]); tuxcmd_set_error_from_exception(Error, E); Result := nil; end; end; DebugMsg(['^^VFS (II): GetFileInfo end.']); end; (********************************************************************************************************************************) procedure TVFSEngine.GetFileSystemInfo(const APath: string; var FSSize, FSFree: Int64; var FSName: string); var AFSSize, AFSFree: Int64; AFSName: PChar; begin FSSize := -1; FSFree := -1; FSName := 'plugin'; if FSourcePlugin.FVFSGetFileSystemInfo <> nil then begin AFSSize := -1; AFSFree := -1; AFSName := nil; FSourcePlugin.FVFSGetFileSystemInfo(FGlobs, PChar(APath), @AFSSize, @AFSFree, @AFSName); FSSize := AFSSize; FSFree := AFSFree; if AFSName <> nil then begin FSName := string(AFSName); real_libc_free(AFSName); end; end; end; function TVFSEngine.IsOnROMedium(const FileName: string): boolean; begin Result := FArchiveMode; end; function TVFSEngine.FileCanRun(const FileName: string): boolean; var Item: PDataItem; begin Item := GetFileInfo(FileName, True, True, nil); Result := (Item <> nil) and Item^.IsExecutable; FreeDataItem(Item); 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; Error: PPGError): boolean; begin Result := False; try Result := FSourcePlugin.FVFSChangeDir(FGlobs, PChar(NewPath), Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_CHDIR); except on E: Exception do begin DebugMsg(['^^VFS (EE): ChangeDir: Exception: ', E.Message]); tuxcmd_set_error_from_exception(Error, E); Result := False; end; end; end; function TVFSEngine.ChangeDirEx(const NewPath: string; AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer; Error: PPGError): boolean; begin Result := False; try if (FGlobs <> nil) and (FSourcePlugin.FVFSChangeDir <> nil) then begin if FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); Result := ChangeDir(NewPath, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_CHDIR); if FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); end; except on E: Exception do begin DebugMsg(['^^VFS (EE): ChangeDirEx: Exception: ', E.Message]); tuxcmd_set_error_from_exception(Error, E); Result := False; end; end; end; procedure TVFSEngine.SetPath(Value: string); begin ChangeDir(Value, nil); end; function TVFSEngine.FileExists(const FileName: string; FollowSymlinks: boolean): boolean; var P: PVFSItem; begin Result := False; if (FGlobs = nil) or (FSourcePlugin.FVFSFileInfo = nil) then Exit; try P := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(FileName), FollowSymlinks, False, nil); Result := P <> nil; real_libc_free(P); except on E: Exception do DebugMsg(['^^VFS (EE): FileExists: Exception: ', E.Message]); end; end; function TVFSEngine.DirectoryExists(const FileName: string; FollowSymlinks: boolean): boolean; var P: PVFSItem; begin Result := False; if (FGlobs = nil) or (FSourcePlugin.FVFSFileInfo = nil) then Exit; try P := FSourcePlugin.FVFSFileInfo(FGlobs, PChar(FileName), FollowSymlinks, False, nil); Result := (P <> nil) and (TVFSItemType(P^.ItemType) = vDirectory); real_libc_free(P); except on E: Exception do DebugMsg(['^^VFS (EE): FileExists: Exception: ', E.Message]); end; end; function TVFSEngine.MakeDir(const NewDir: string; Error: PPGError): boolean; begin if FSourcePlugin.FVFSMkDir <> nil then begin Result := FSourcePlugin.FVFSMkDir(FGlobs, PChar(NewDir), Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_MKDIR); end else begin Result := False; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Creating directories is not supported by the VFS module.'); end; end; function TVFSEngine.Remove(const APath: string; Error: PPGError): boolean; begin if FSourcePlugin.FVFSRemove <> nil then begin Result := FSourcePlugin.FVFSRemove(FGlobs, PChar(APath), Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_REMOVE); end else begin Result := False; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Removing files is not supported by the VFS module.'); end; end; function TVFSEngine.RenameFile(const SourceFile, DestFile: string; Error: PPGError): boolean; begin if FSourcePlugin.FVFSRename <> nil then begin Result := FSourcePlugin.FVFSRename(FGlobs, PChar(SourceFile), PChar(DestFile), Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_RENAME); end else begin Result := False; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Renaming files is not supported by the VFS module.'); end; end; function TVFSEngine.MakeSymLink(const NewFileName, PointTo: string; Error: PPGError): boolean; begin if FSourcePlugin.FVFSMakeSymLink <> nil then begin Result := FSourcePlugin.FVFSMakeSymLink(FGlobs, PChar(NewFileName), PChar(PointTo), Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_SYMLINK); end else begin Result := False; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Creating symbolic links is not supported by the VFS module.'); end; end; function TVFSEngine.Chmod(const FileName: string; Mode: cuLong; Error: PPGError): boolean; begin if FSourcePlugin.FVFSChmod <> nil then begin Result := FSourcePlugin.FVFSChmod(FGlobs, PChar(FileName), Mode, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_CHMOD); end else begin Result := False; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Chmod is not supported by the VFS module.'); end; end; function TVFSEngine.Chown(const FileName: string; UID, GID: cuLong; Error: PPGError): boolean; begin if FSourcePlugin.FVFSChown <> nil then begin Result := FSourcePlugin.FVFSChown(FGlobs, PChar(FileName), UID, GID, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_CHOWN); end else begin Result := False; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Chown is not supported by the VFS module.'); end; end; function TVFSEngine.ChangeTimes(const APath: string; mtime, atime: time_t; Error: PPGError): boolean; begin if FSourcePlugin.FVFSChangeTimes <> nil then begin Result := FSourcePlugin.FVFSChangeTimes(FGlobs, PChar(APath), mtime, atime, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_TIMESTAMPS); end else begin Result := False; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Changing timestamps is not supported by the VFS module.'); end; end; function TVFSEngine.GetDirSize(const 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; FollowSymlinks: boolean): boolean; begin if FSourcePlugin.FVFSIsOnSameFS <> nil then Result := FSourcePlugin.FVFSIsOnSameFS(FGlobs, PChar(Path1), PChar(Path2), FollowSymlinks) else Result := True; end; function TVFSEngine.OpenFile(const APath: string; Mode: integer; Error: PPGError): TEngineFileDes; begin if FSourcePlugin.FVFSOpenFile <> nil then begin Result := FSourcePlugin.FVFSOpenFile(FGlobs, PChar(APath), Mode, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_OPEN_FILE); end else begin Result := nil; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Manual opening files is not supported by the VFS module.'); end; end; function TVFSEngine.ReadFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; ABlockSize: integer; Error: PPGError): integer; begin if FSourcePlugin.FVFSReadFile <> nil then begin Result := FSourcePlugin.FVFSReadFile(FGlobs, FileDescriptor, Buffer, ABlockSize, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_READ_FILE); end else begin Result := -1; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Manual read is not supported by the VFS module.'); end; end; function TVFSEngine.WriteFile(const FileDescriptor: TEngineFileDes; Buffer: Pointer; BytesCount: integer; Error: PPGError): integer; begin if FSourcePlugin.FVFSWriteFile <> nil then begin Result := FSourcePlugin.FVFSWriteFile(FGlobs, FileDescriptor, Buffer, BytesCount, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_WRITE_FILE); end else begin Result := -1; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Manual write is not supported by the VFS module.'); end; end; function TVFSEngine.CloseFile(const FileDescriptor: TEngineFileDes; Error: PPGError): boolean; begin if FSourcePlugin.FVFSCloseFile <> nil then begin Result := FSourcePlugin.FVFSCloseFile(FGlobs, FileDescriptor, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_CLOSE_FILE); end else begin Result := False; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Manual closing files is not supported by the VFS module.'); end; end; function TVFSEngine.FileSeek(const FileDescriptor: TEngineFileDes; const AbsoluteOffset: Int64; Error: PPGError): Int64; begin if FSourcePlugin.FVFSFileSeek <> nil then begin Result := FSourcePlugin.FVFSFileSeek(FGlobs, FileDescriptor, AbsoluteOffset, Error); VFSToTuxcmdError(Error, TUXCMD_ERROR_SEEK); end else begin Result := -1; g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'Manual seek is not supported by the VFS module.'); end; end; function TVFSEngine.TwoSameFiles(const Path1, Path2: string; FollowSymlinks: boolean): boolean; begin if FSourcePlugin.FVFSTwoSameFiles <> nil then Result := FSourcePlugin.FVFSTwoSameFiles(FGlobs, PChar(Path1), PChar(Path2), FollowSymlinks) else Result := False; end; (********************************************************************************************************************************) function TVFSEngine.CopyFileIn(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; begin DebugMsg(['** (WW): deprecated method CopyFileIn called from a TVFSEngine instance!']); Result := StartCopyOperation(nil, nil, nil, nil); Result := Result and CopyFileInEx(SourceFile, DestFile, Append, Error); Result := Result and StopCopyOperation(nil, nil); end; function TVFSEngine.CopyFileOut(const SourceFile, DestFile: string; Append: boolean; ProgressFunc: TEngineProgressFunc; Sender: Pointer; Error: PPGError): boolean; begin DebugMsg(['** (WW): deprecated method CopyFileOut called from a TVFSEngine instance!']); Result := StartCopyOperation(nil, nil, nil, nil); Result := Result and CopyFileOutEx(SourceFile, DestFile, Append, Error); Result := Result and StopCopyOperation(nil, nil); end; function TVFSEngine.CopyFileOutEx(const SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; begin Result := False; try if FSourcePlugin.FVFSCopyToLocal <> nil then try Result := FSourcePlugin.FVFSCopyToLocal(FGlobs, PChar(SourceFile), PChar(DestFile), Append, Error) except on E: Exception do tuxcmd_set_error_from_exception(Error, E); end else g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'VFSCopyToLocal not supported by the VFS module.'); if Error^ <> nil then begin if FCopyProgressCallback <> nil then Result := TVFSProgressCallback(FCopyProgressCallback)(0, Error^, FCopyCallbackData); end; except on E: Exception do begin Result := False; tuxcmd_set_error_from_exception(Error, E); DebugMsg(['*** Exception raised in TVFSEngine.CopyFileOutEx(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append,'): (', E.ClassName, '): ', E.Message]); end; end; end; function TVFSEngine.CopyFileInEx(const SourceFile, DestFile: string; Append: boolean; Error: PPGError): boolean; begin Result := False; try if FSourcePlugin.FVFSCopyFromLocal <> nil then try Result := FSourcePlugin.FVFSCopyFromLocal(FGlobs, PChar(SourceFile), PChar(DestFile), Append, Error) except on E: Exception do tuxcmd_set_error_from_exception(Error, E); end else g_set_error_literal(Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'VFSCopyFromLocal not supported by the VFS module.'); if Error <> nil then begin if FCopyProgressCallback <> nil then Result := TVFSProgressCallback(FCopyProgressCallback)(0, Error^, FCopyCallbackData); end; except on E: Exception do begin Result := False; tuxcmd_set_error_from_exception(Error, E); DebugMsg(['*** Exception raised in TVFSEngine.CopyFileInEx(SourceFile=', SourceFile, ', DestFile=', DestFile, ', Append=', Append,'): (', E.ClassName, '): ', E.Message]); end; end; end; function TVFSEngine.StartCopyOperation(AskQuestionCallback: PVFSAskQuestionCallback; AskPasswordCallback: PVFSAskPasswordCallback; ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; var Error: PGError; begin Result := not ArchiveMode; try if FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, AskQuestionCallback, AskPasswordCallback, ProgressCallback, CallbackData); FCopyProgressCallback := ProgressCallback; FCopyCallbackData := CallbackData; if ArchiveMode then begin Error := nil; if FSourcePlugin.FVFSStartCopyOperation <> nil then try Result := FSourcePlugin.FVFSStartCopyOperation(FGlobs, @Error) except on E: Exception do tuxcmd_set_error_from_exception(@Error, E); end else g_set_error_literal(@Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'VFSStartCopyOperation not supported by the VFS module.'); if Error <> nil then begin if ProgressCallback <> nil then Result := ProgressCallback^(0, Error, CallbackData); g_error_free(Error); end; end; except on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.StartCopyOperation(CallbackData=', CallbackData, '): (', E.ClassName, '): ', E.Message]); end; end; function TVFSEngine.StopCopyOperation(ProgressCallback: PVFSProgressCallback; CallbackData: Pointer): boolean; var Error: PGError; begin Result := not ArchiveMode; try if FSourcePlugin.FVFSSetCallbacks <> nil then FSourcePlugin.FVFSSetCallbacks(FGlobs, nil, nil, nil, nil); if ArchiveMode then begin Error := nil; if FSourcePlugin.FVFSStopCopyOperation <> nil then try Result := FSourcePlugin.FVFSStopCopyOperation(FGlobs, @Error) except on E: Exception do tuxcmd_set_error_from_exception(@Error, E); end else g_set_error_literal(@Error, TUXCMD_ERROR, gint(TUXCMD_ERROR_NOT_SUPPORTED), 'VFSStopCopyOperation not supported by the VFS module.'); if Error <> nil then begin if ProgressCallback <> nil then Result := ProgressCallback^(0, Error, CallbackData); g_error_free(Error); end; end; FCopyProgressCallback := nil; FCopyCallbackData := nil; except on E: Exception do DebugMsg(['*** Exception raised in TVFSEngine.StopCopyOperation(CallbackData=', CallbackData, '): (', 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 TVFSEngine.GetArchiveStreamingType: boolean; begin Result := False; if (FSourcePlugin.FVFSGetCapabilities <> nil) then Result := (FSourcePlugin.FVFSGetCapabilities() and VFS_CAP_ARCHIVE_STREAMING) = VFS_CAP_ARCHIVE_STREAMING; end; (********************************************************************************************************************************) (********************************************************************************************************************************) function LookupVFSPlugin(const ModuleID: string): TVFSPlugin; var i: integer; begin Result := nil; for i := 0 to PluginList.Count - 1 do if Trim(TVFSPlugin(PluginList[i]).ModuleID) = ModuleID then begin Result := TVFSPlugin(PluginList[i]); Break; end; 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]).FullModulePath, 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 := TVFSVersion(dlsym(ModuleHandler, 'VFSVersion')); if (VFSVersionFunc <> nil) and (VFSVersionFunc() = ConstVFSVersionRequired) then begin PluginItem := TVFSPlugin.Create(ModuleHandler); PluginItem.FullModulePath := 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 if ParamDebug then setenv('TUXCMD_DEBUG', '1', 1); 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.