mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-28 10:02:14 +00:00
970 lines
26 KiB
ObjectPascal
970 lines
26 KiB
ObjectPascal
{
|
|
Double Commander
|
|
-------------------------------------------------------------------------
|
|
This unit contains platform depended functions.
|
|
|
|
Copyright (C) 2006-2013 Koblov Alexander (Alexx2000@mail.ru)
|
|
|
|
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 uOSUtils;
|
|
|
|
{$mode delphi}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes, LCLType, uDrive, DCBasicTypes
|
|
{$IF DEFINED(UNIX)}
|
|
, DCFileAttributes
|
|
{$IFDEF DARWIN}
|
|
, MacOSAll
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
;
|
|
|
|
const
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
faFolder = faDirectory;
|
|
RunTerm = 'cmd.exe'; // default terminal
|
|
RunInTerm = 'cmd.exe /K'; // default run in terminal command
|
|
fmtRunInTerm = '%s "%s"';
|
|
fmtRunInShell = '%s /C "%s"';
|
|
fmtCommandPath = '%s>';
|
|
MonoSpaceFont = 'Fixedsys';
|
|
{$ELSEIF DEFINED(UNIX)}
|
|
faFolder = S_IFDIR;
|
|
{$IFDEF DARWIN)}
|
|
RunTerm = '/Applications/Utilities/Terminal.app'; // default terminal
|
|
RunInTerm = ''; // default run in terminal command
|
|
fmtRunInTerm = '';
|
|
{$ELSE}
|
|
RunTerm = 'xterm'; // default terminal
|
|
RunInTerm = 'xterm -e sh -c'; // default run in terminal command
|
|
fmtRunInTerm = '%s ''%s ; echo -n Press ENTER to exit... ; read a''';
|
|
{$ENDIF}
|
|
fmtRunInShell = '%s -c ''%s''';
|
|
fmtCommandPath = '[%s]$:';
|
|
MonoSpaceFont = 'Monospace';
|
|
{$ENDIF}
|
|
|
|
type
|
|
EInvalidCommandLine = class(Exception);
|
|
EInvalidQuoting = class(EInvalidCommandLine)
|
|
constructor Create; reintroduce;
|
|
end;
|
|
|
|
function FileIsLinkToFolder(const FileName: UTF8String; out LinkTarget: UTF8String): Boolean;
|
|
function ExecCmdFork(sCmdLine:String; bTerm : Boolean = False; sTerm : String = ''; bKeepTerminalOpen: Boolean = True):Boolean;
|
|
{en
|
|
Opens a file or URL in the user's preferred application
|
|
@param(URL File name or URL)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function ShellExecute(URL: UTF8String): Boolean;
|
|
function GetDiskFreeSpace(const Path : String; out FreeSize, TotalSize : Int64) : Boolean;
|
|
{en
|
|
Get maximum file size for a mounted file system
|
|
@param(Path The pathname of any file within the mounted file system)
|
|
@returns(The maximum file size for a mounted file system)
|
|
}
|
|
function GetDiskMaxFileSize(const Path : UTF8String) : Int64;
|
|
{en
|
|
Create a hard link to a file
|
|
@param(Path Name of file)
|
|
@param(LinkName Name of hard link)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function CreateHardLink(const Path, LinkName: String) : Boolean;
|
|
{en
|
|
Create a symbolic link
|
|
@param(Path Name of file)
|
|
@param(LinkName Name of symbolic link)
|
|
@returns(The function returns @true if successful, @false otherwise)
|
|
}
|
|
function CreateSymLink(const Path, LinkName: string) : Boolean;
|
|
{en
|
|
Read destination of symbolic link
|
|
@param(LinkName Name of symbolic link)
|
|
@returns(The file name/path the symbolic link name is pointing to.
|
|
The path may be relative to link's location.)
|
|
}
|
|
function ReadSymLink(const LinkName : String) : String;
|
|
{en
|
|
Reads the concrete file's name that the link points to.
|
|
If the link points to a link then it's resolved recursively
|
|
until a valid file name that is not a link is found.
|
|
@param(PathToLink Name of symbolic link (absolute path))
|
|
@returns(The absolute filename the symbolic link name is pointing to,
|
|
or an empty string when the link is invalid or
|
|
the file it points to does not exist.)
|
|
}
|
|
function mbReadAllLinks(const PathToLink : String) : String;
|
|
{en
|
|
If PathToLink points to a link then it returns file that the link points to (recursively).
|
|
If PathToLink does not point to a link then PathToLink value is returned.
|
|
}
|
|
function mbCheckReadLinks(const PathToLink : String) : String;
|
|
{en
|
|
Get the user home directory
|
|
@returns(The user home directory)
|
|
}
|
|
function GetHomeDir : String;
|
|
{en
|
|
Get the appropriate directory for the application's configuration files
|
|
@returns(The directory for the application's configuration files)
|
|
}
|
|
function GetAppConfigDir: String;
|
|
{en
|
|
Get the appropriate directory for the application's cache files
|
|
@returns(The directory for the application's cache files)
|
|
}
|
|
function GetAppCacheDir: UTF8String;
|
|
function GetTempFolder: String;
|
|
{en
|
|
Get the system specific self extracting archive extension
|
|
@returns(Self extracting archive extension)
|
|
}
|
|
function GetSfxExt: String;
|
|
|
|
function IsAvailable(Drive: PDrive; TryMount: Boolean = True) : Boolean;
|
|
function GetShell : String;
|
|
{en
|
|
Formats a string which will execute Command via shell.
|
|
}
|
|
function FormatShell(const Command: String): String;
|
|
{en
|
|
Formats a string which will execute Command in a terminal.
|
|
}
|
|
function FormatTerminal(Command: String; bKeepTerminalOpen: Boolean): String;
|
|
{en
|
|
Same as mbFileGetAttr, but dereferences any encountered links.
|
|
}
|
|
function mbFileGetAttrNoLinks(const FileName: UTF8String): TFileAttrs;
|
|
{en
|
|
Convert file name to system encoding, if name can not be represented in
|
|
current locale then use short file name under Windows.
|
|
}
|
|
function mbFileNameToSysEnc(const LongPath: UTF8String): String;
|
|
function mbGetEnvironmentVariable(const sName: UTF8String): UTF8String;
|
|
function mbSetEnvironmentVariable(const sName, sValue: UTF8String): Boolean;
|
|
{en
|
|
Extract the root directory part of a file name.
|
|
@returns(Drive letter under Windows and mount point under Unix)
|
|
}
|
|
function ExtractRootDir(const FileName: UTF8String): UTF8String;
|
|
|
|
procedure FixFormIcon(Handle: LCLType.HWND);
|
|
procedure HideConsoleWindow;
|
|
procedure FixDateNamesToUTF8;
|
|
|
|
function ParamStrU(Param: Integer): UTF8String; overload;
|
|
function ParamStrU(const Param: String): UTF8String; overload;
|
|
|
|
implementation
|
|
|
|
uses
|
|
FileUtil, uDCUtils, DCOSUtils, DCStrUtils, uGlobs, uLng
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
, JwaWinCon, Windows, uNTFSLinks, uMyWindows, JwaWinNetWk, uShlObjAdditional
|
|
, shlobj
|
|
{$ENDIF}
|
|
{$IF DEFINED(UNIX)}
|
|
, BaseUnix, Unix, uMyUnix, dl
|
|
{$IF NOT DEFINED(DARWIN)}
|
|
, uGio
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
;
|
|
|
|
{$IFDEF UNIX}
|
|
type
|
|
{en
|
|
Waits for a child process to finish and collects its exit status,
|
|
causing it to be released by the system (prevents defunct processes).
|
|
|
|
Instead of the wait-thread we could just ignore or handle SIGCHLD signal
|
|
for the process, but this way we don't interfere with the signal handling.
|
|
The downside is that there's a thread for every child process running.
|
|
|
|
Another method is to periodically do a cleanup, for example from OnIdle
|
|
or OnTimer event. Remember PIDs of spawned child processes and when
|
|
cleaning call FpWaitpid(PID, nil, WNOHANG) on each PID. Downside is they
|
|
are not released immediately after the child process finish (may be relevant
|
|
if we want to display exit status to the user).
|
|
}
|
|
TWaitForPidThread = class(TThread)
|
|
private
|
|
FPID: TPid;
|
|
protected
|
|
procedure Execute; override;
|
|
public
|
|
constructor Create(WaitForPid: TPid); overload;
|
|
end;
|
|
|
|
constructor TWaitForPidThread.Create(WaitForPid: TPid);
|
|
begin
|
|
inherited Create(True);
|
|
FPID := WaitForPid;
|
|
FreeOnTerminate := True;
|
|
end;
|
|
|
|
procedure TWaitForPidThread.Execute;
|
|
begin
|
|
while (FpWaitPid(FPID, nil, 0) = -1) and (fpgeterrno() = ESysEINTR) do;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function FileIsLinkToFolder(const FileName: UTF8String; out
|
|
LinkTarget: UTF8String): Boolean;
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
begin
|
|
Result:= False;
|
|
if LowerCase(ExtractOnlyFileExt(FileName)) = 'lnk' then
|
|
Result:= SHFileIsLinkToFolder(FileName, LinkTarget);
|
|
end;
|
|
{$ELSEIF DEFINED(UNIX)}
|
|
begin
|
|
Result:= False;
|
|
if LowerCase(ExtractOnlyFileExt(FileName)) = 'desktop' then
|
|
Result:= uMyUnix.FileIsLinkToFolder(FileName, LinkTarget);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
(* Execute external commands *)
|
|
|
|
function ExecCmdFork(sCmdLine:String; bTerm : Boolean; sTerm : String; bKeepTerminalOpen: Boolean) : Boolean;
|
|
{$IFDEF UNIX}
|
|
var
|
|
Command : String;
|
|
pid : LongInt;
|
|
Args : TDynamicStringArray;
|
|
WaitForPidThread: TWaitForPidThread;
|
|
begin
|
|
if bTerm then
|
|
sCmdLine := FormatTerminal(sCmdLine, bKeepTerminalOpen);
|
|
|
|
SplitCmdLine(UTF8ToSys(sCmdLine), Command, Args);
|
|
{$IFDEF DARWIN}
|
|
// If we run application bundle (*.app) then
|
|
// execute it by 'open -a' command (see 'man open' for details)
|
|
if StrEnds(Command, '.app') then
|
|
begin
|
|
SetLength(Args, Length(Args) + 2);
|
|
for pid := High(Args) downto Low(Args) + 2 do
|
|
Args[pid]:= Args[pid - 2];
|
|
Args[0] := '-a';
|
|
Args[1] := Command;
|
|
Command := 'open';
|
|
end;
|
|
{$ENDIF}
|
|
if Command = EmptyStr then Exit(False);
|
|
|
|
pid := fpFork;
|
|
|
|
if pid = 0 then
|
|
begin
|
|
{ The child does the actual exec, and then exits }
|
|
if FpExecLP(Command, Args) = -1 then
|
|
Writeln(Format('Execute error %d: %s', [fpgeterrno, SysErrorMessageUTF8(fpgeterrno)]));
|
|
|
|
{ If the FpExecLP fails, we return an exitvalue of 127, to let it be known }
|
|
fpExit(127);
|
|
end
|
|
else if pid = -1 then { Fork failed }
|
|
begin
|
|
raise Exception.Create('Fork failed: ' + Command);
|
|
end
|
|
else if pid > 0 then { Parent }
|
|
begin
|
|
WaitForPidThread := TWaitForPidThread.Create(pid);
|
|
WaitForPidThread.Resume;
|
|
end;
|
|
|
|
Result := (pid > 0);
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
sFileName,
|
|
sParams: String;
|
|
wFileName,
|
|
wParams,
|
|
wWorkDir: WideString;
|
|
begin
|
|
wWorkDir:= UTF8Decode(mbGetCurrentDir);
|
|
|
|
if bTerm then
|
|
begin
|
|
if sTerm = '' then sTerm := RunInTerm;
|
|
sCmdLine := Format(fmtRunInTerm, [sTerm, sCmdLine]);
|
|
end;
|
|
|
|
SplitCmdLine(sCmdLine, sFileName, sParams);
|
|
sFileName:= NormalizePathDelimiters(sFileName);
|
|
wFileName:= UTF8Decode(sFileName);
|
|
wParams:= UTF8Decode(sParams);
|
|
Result := (ShellExecuteW(0, nil, PWChar(wFileName), PWChar(wParams), PWChar(wWorkDir), SW_SHOW) > 32);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function ShellExecute(URL: UTF8String): Boolean;
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
var
|
|
Return: HINST;
|
|
wsFileName: WideString;
|
|
begin
|
|
URL:= NormalizePathDelimiters(URL);
|
|
wsFileName:= UTF8Decode(QuoteDouble(URL));
|
|
Return:= ShellExecuteW(0, nil, PWideChar(wsFileName), nil, nil, SW_SHOWNORMAL);
|
|
if Return = SE_ERR_NOASSOC then
|
|
Result:= ExecCmdFork('rundll32 shell32.dll OpenAs_RunDLL ' + URL)
|
|
else
|
|
Result:= Return > 32;
|
|
end;
|
|
{$ELSEIF DEFINED(DARWIN)}
|
|
var
|
|
theFileNameCFRef: CFStringRef = nil;
|
|
theFileNameUrlRef: CFURLRef = nil;
|
|
theFileNameFSRef: FSRef;
|
|
begin
|
|
Result:= False;
|
|
try
|
|
theFileNameCFRef:= CFStringCreateWithFileSystemRepresentation(nil, PAnsiChar(URL));
|
|
theFileNameUrlRef:= CFURLCreateWithFileSystemPath(nil, theFileNameCFRef, kCFURLPOSIXPathStyle, False);
|
|
if (CFURLGetFSRef(theFileNameUrlRef, theFileNameFSRef)) then
|
|
begin
|
|
Result:= (LSOpenFSRef(theFileNameFSRef, nil) = noErr);
|
|
end;
|
|
finally
|
|
if Assigned(theFileNameCFRef) then
|
|
CFRelease(theFileNameCFRef);
|
|
if Assigned(theFileNameUrlRef) then
|
|
CFRelease(theFileNameUrlRef);
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
DesktopEnv: Cardinal;
|
|
sCmdLine: UTF8String;
|
|
begin
|
|
Result:= False;
|
|
sCmdLine:= EmptyStr;
|
|
if FileIsUnixExecutable(URL) then
|
|
begin
|
|
if GetPathType(URL) <> ptAbsolute then
|
|
sCmdLine := './';
|
|
sCmdLine:= sCmdLine + QuoteStr(URL);
|
|
end
|
|
else
|
|
begin
|
|
DesktopEnv:= GetDesktopEnvironment;
|
|
if (DesktopEnv = DE_KDE) and (FindDefaultExecutablePath('kioclient') <> EmptyStr) then
|
|
sCmdLine:= 'kioclient exec ' + QuoteStr(URL) // Under KDE use "kioclient" to open files
|
|
else if (DesktopEnv = DE_XFCE) and (FindDefaultExecutablePath('exo-open') <> EmptyStr) then
|
|
sCmdLine:= 'exo-open ' + QuoteStr(URL) // Under Xfce use "exo-open" to open files
|
|
else if HasGio then
|
|
Result:= GioOpen(URL) // Under GNOME, Unity and LXDE use "GIO" to open files
|
|
else
|
|
begin
|
|
if GetPathType(URL) = ptAbsolute then
|
|
sCmdLine:= URL
|
|
else
|
|
begin
|
|
sCmdLine := IncludeTrailingPathDelimiter(mbGetCurrentDir);
|
|
sCmdLine:= GetAbsoluteFileName(sCmdLine, URL)
|
|
end;
|
|
sCmdLine:= GetDefaultAppCmd(sCmdLine);
|
|
end;
|
|
end;
|
|
if Length(sCmdLine) <> 0 then
|
|
Result:= ExecCmdFork(sCmdLine);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
(* Get Disk Free Space *)
|
|
|
|
function GetDiskFreeSpace(const Path : String; out FreeSize, TotalSize : Int64) : Boolean;
|
|
{$IFDEF UNIX}
|
|
var
|
|
sbfs: TStatFS;
|
|
begin
|
|
Result:= (fpStatFS(PChar(UTF8ToSys(Path)), @sbfs) = 0);
|
|
if not Result then Exit;
|
|
FreeSize := (Int64(sbfs.bavail)*sbfs.bsize);
|
|
{$IF DEFINED(CPU32) or (FPC_VERSION>2) or ((FPC_VERSION=2) and ((FPC_RELEASE>2) or ((FPC_RELEASE=2) and (FPC_PATCH>=3))))}
|
|
TotalSize := (Int64(sbfs.blocks)*sbfs.bsize);
|
|
{$ENDIF}
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
wPath: WideString;
|
|
begin
|
|
wPath:= UTF8Decode(Path);
|
|
Result:= GetDiskFreeSpaceExW(PWChar(wPath), FreeSize, TotalSize, nil);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function GetDiskMaxFileSize(const Path: UTF8String): Int64;
|
|
{$IFDEF UNIX}
|
|
const
|
|
MSDOS_SUPER_MAGIC = $4d44;
|
|
var
|
|
sbfs: TStatFS;
|
|
begin
|
|
Result := High(Int64);
|
|
if (fpStatFS(PChar(UTF8ToSys(Path)), @sbfs) = 0) then
|
|
begin
|
|
{$IFDEF BSD}
|
|
if (sbfs.ftype = MSDOS_SUPER_MAGIC) then
|
|
{$ELSE}
|
|
if (sbfs.fstype = MSDOS_SUPER_MAGIC) then
|
|
{$ENDIF}
|
|
Result:= $FFFFFFFF; // 4 Gb
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
lpVolumeNameBuffer,
|
|
lpFileSystemNameBuffer : array [0..255] of WideChar;
|
|
lpMaximumComponentLength,
|
|
lpFileSystemFlags : DWORD;
|
|
begin
|
|
Result := High(Int64);
|
|
if GetVolumeInformationW(PWideChar(UTF8Decode(ExtractFileDrive(Path)) + PathDelim),
|
|
lpVolumeNameBuffer, SizeOf(lpVolumeNameBuffer),
|
|
nil,
|
|
lpMaximumComponentLength,
|
|
lpFileSystemFlags,
|
|
lpFileSystemNameBuffer, SizeOf(lpFileSystemNameBuffer)) then
|
|
begin
|
|
if SameText(lpFileSystemNameBuffer, 'FAT') then
|
|
Result:= $80000000 // 2 Gb
|
|
else if SameText(lpFileSystemNameBuffer, 'FAT32') then
|
|
Result:= $FFFFFFFF; // 4 Gb
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function CreateHardLink(const Path, LinkName: String) : Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wsPath, wsLinkName: WideString;
|
|
begin
|
|
Result:= True;
|
|
try
|
|
wsPath:= UTF8Decode(Path);
|
|
wsLinkName:= UTF8Decode(LinkName);
|
|
Result:= uNTFSLinks.CreateHardlink(wsPath, wsLinkName);
|
|
except
|
|
Result:= False;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result := (fplink(PChar(UTF8ToSys(Path)),PChar(UTF8ToSys(LinkName)))=0);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function CreateSymLink(const Path, LinkName: string) : Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wsPath, wsLinkName: WideString;
|
|
begin
|
|
Result := True;
|
|
try
|
|
wsPath:= UTF8Decode(Path);
|
|
wsLinkName:= UTF8Decode(LinkName);
|
|
Result:= uNTFSLinks.CreateSymlink(wsPath, wsLinkName);
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result := (fpsymlink(PChar(UTF8ToSys(Path)),PChar(UTF8ToSys(LinkName)))=0);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
(* Get symlink target *)
|
|
|
|
function ReadSymLink(const LinkName : String) : String;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wsLinkName,
|
|
wsTarget: WideString;
|
|
begin
|
|
try
|
|
wsLinkName:= UTF8Decode(LinkName);
|
|
if uNTFSLinks.ReadSymLink(wsLinkName, wsTarget) then
|
|
Result := UTF8Encode(wsTarget)
|
|
else
|
|
Result := '';
|
|
except
|
|
Result := '';
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result := SysToUTF8(fpReadlink(UTF8ToSys(LinkName)));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbReadAllLinks(const PathToLink: String) : String;
|
|
var
|
|
Attrs: TFileAttrs;
|
|
LinkTargets: TStringList; // A list of encountered filenames (for detecting cycles)
|
|
|
|
function mbReadAllLinksRec(const PathToLink: String): String;
|
|
begin
|
|
Result := ReadSymLink(PathToLink);
|
|
if Result <> '' then
|
|
begin
|
|
if GetPathType(Result) <> ptAbsolute then
|
|
Result := GetAbsoluteFileName(ExtractFilePath(PathToLink), Result);
|
|
|
|
if LinkTargets.IndexOf(Result) >= 0 then
|
|
begin
|
|
// Link already encountered - links form a cycle.
|
|
Result := '';
|
|
{$IFDEF UNIX}
|
|
fpseterrno(ESysELOOP);
|
|
{$ENDIF}
|
|
Exit;
|
|
end;
|
|
|
|
Attrs := mbFileGetAttr(Result);
|
|
if (Attrs <> faInvalidAttributes) then
|
|
begin
|
|
if FPS_ISLNK(Attrs) then
|
|
begin
|
|
// Points to a link - read recursively.
|
|
LinkTargets.Add(Result);
|
|
Result := mbReadAllLinksRec(Result);
|
|
end;
|
|
// else points to a file/dir
|
|
end
|
|
else
|
|
begin
|
|
Result := ''; // Target of link doesn't exist
|
|
{$IFDEF UNIX}
|
|
fpseterrno(ESysENOENT);
|
|
{$ENDIF}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
LinkTargets := TStringList.Create;
|
|
try
|
|
Result := mbReadAllLinksRec(PathToLink);
|
|
finally
|
|
FreeAndNil(LinkTargets);
|
|
end;
|
|
end;
|
|
|
|
function mbCheckReadLinks(const PathToLink : String): String;
|
|
var
|
|
Attrs: TFileAttrs;
|
|
begin
|
|
Attrs := mbFileGetAttr(PathToLink);
|
|
if (Attrs <> faInvalidAttributes) and FPS_ISLNK(Attrs) then
|
|
Result := mbReadAllLinks(PathToLink)
|
|
else
|
|
Result := PathToLink;
|
|
end;
|
|
|
|
function GetHomeDir : String;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
iSize: Integer;
|
|
wHomeDir: WideString;
|
|
begin
|
|
iSize:= GetEnvironmentVariableW('USERPROFILE', nil, 0);
|
|
if iSize > 0 then
|
|
begin
|
|
SetLength(wHomeDir, iSize);
|
|
GetEnvironmentVariableW('USERPROFILE', PWChar(wHomeDir), iSize);
|
|
end;
|
|
Delete(wHomeDir, iSize, 1);
|
|
Result:= ExcludeBackPathDelimiter(UTF8Encode(wHomeDir));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= ExcludeBackPathDelimiter(SysToUTF8(GetEnvironmentVariable('HOME')));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function GetShell : String;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
iSize: Integer;
|
|
wShell: WideString;
|
|
begin
|
|
iSize:= GetEnvironmentVariableW('ComSpec', nil, 0);
|
|
if iSize > 0 then
|
|
begin
|
|
SetLength(wShell, iSize);
|
|
GetEnvironmentVariableW('ComSpec', PWChar(wShell), iSize);
|
|
end;
|
|
Delete(wShell, iSize, 1);
|
|
Result:= UTF8Encode(wShell);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= SysToUTF8(GetEnvironmentVariable('SHELL'));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function FormatShell(const Command: String): String;
|
|
begin
|
|
{$IF DEFINED(UNIX)}
|
|
Result := Format('%s -c %s', [GetShell, QuoteSingle(Command)]);
|
|
{$ELSEIF DEFINED(MSWINDOWS)}
|
|
Result := Format('%s /C %s', [GetShell, QuoteDouble(Command)]);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function FormatTerminal(Command: String; bKeepTerminalOpen: Boolean): String;
|
|
begin
|
|
{$IF DEFINED(UNIX)}
|
|
if bKeepTerminalOpen then
|
|
Command := Command + '; echo -n Press ENTER to exit... ; read a';
|
|
Result := Format('%s %s', [gRunInTerm, QuoteSingle(Command)]);
|
|
{$ELSEIF DEFINED(MSWINDOWS)}
|
|
// TODO: See if keeping terminal window open can be implemented on Windows.
|
|
Result := Format('%s %s', [gRunInTerm, QuoteDouble(Command)]);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function GetAppConfigDir: String;
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
const
|
|
SHGFP_TYPE_CURRENT = 0;
|
|
var
|
|
wPath: array[0..MAX_PATH-1] of WideChar;
|
|
wUser: WideString;
|
|
dwLength: DWORD;
|
|
begin
|
|
if SUCCEEDED(SHGetFolderPathW(0, CSIDL_APPDATA or CSIDL_FLAG_CREATE, 0, SHGFP_TYPE_CURRENT, @wPath[0])) or
|
|
SUCCEEDED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA or CSIDL_FLAG_CREATE, 0, SHGFP_TYPE_CURRENT, @wPath[0])) then
|
|
begin
|
|
Result := UTF8Encode(WideString(wPath));
|
|
end
|
|
else
|
|
begin
|
|
dwLength := UNLEN + 1;
|
|
SetLength(wUser, dwLength);
|
|
if GetUserNameW(PWideChar(wUser), @dwLength) then
|
|
begin
|
|
SetLength(wUser, dwLength - 1);
|
|
Result := GetTempDir + UTF8Encode(wUser);
|
|
end
|
|
else
|
|
Result := EmptyStr;
|
|
end;
|
|
if Result <> '' then
|
|
Result := Result + DirectorySeparator + ApplicationName;
|
|
end;
|
|
{$ELSEIF DEFINED(DARWIN)}
|
|
begin
|
|
Result:= GetHomeDir + '/Library/Preferences/' + ApplicationName;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
uinfo: PPasswordRecord;
|
|
begin
|
|
uinfo:= getpwuid(fpGetUID);
|
|
if (uinfo <> nil) and (uinfo^.pw_dir <> '') then
|
|
Result:= SysToUTF8(uinfo^.pw_dir) + '/.config/' + ApplicationName
|
|
else
|
|
Result:= ExcludeTrailingPathDelimiter(SysToUTF8(SysUtils.GetAppConfigDir(False)));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function GetAppCacheDir: UTF8String;
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
var
|
|
APath: array[0..MAX_PATH] of WideChar;
|
|
begin
|
|
if SHGetSpecialFolderPathW(0, APath, CSIDL_LOCAL_APPDATA, True) then
|
|
Result:= UTF8Encode(WideString(APath)) + DirectorySeparator + ApplicationName
|
|
else
|
|
Result:= GetAppConfigDir;
|
|
end;
|
|
{$ELSEIF DEFINED(DARWIN)}
|
|
begin
|
|
Result:= GetHomeDir + '/Library/Caches/' + ApplicationName;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
uinfo: PPasswordRecord;
|
|
begin
|
|
uinfo:= getpwuid(fpGetUID);
|
|
if (uinfo <> nil) and (uinfo^.pw_dir <> '') then
|
|
Result:= SysToUTF8(uinfo^.pw_dir) + '/.cache/' + ApplicationName
|
|
else
|
|
Result:= GetHomeDir + '/.cache/' + ApplicationName;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function GetTempFolder: String;
|
|
begin
|
|
Result:= GetTempDir + '_dc';
|
|
if not mbDirectoryExists(Result) then
|
|
mbCreateDir(Result);
|
|
Result:= Result + PathDelim;
|
|
end;
|
|
|
|
function GetSfxExt: String;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result:= '.exe';
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= '.run';
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function IsAvailable(Drive: PDrive; TryMount: Boolean): Boolean;
|
|
{$IF DEFINED(MSWINDOWS)}
|
|
var
|
|
Drv: String;
|
|
DriveLabel: String;
|
|
NetResource: TNetResourceW;
|
|
wsLocalName, wsRemoteName: WideString;
|
|
begin
|
|
Drv:= ExtractFileDrive(Drive^.Path) + PathDelim;
|
|
|
|
// Try to close CD/DVD drive
|
|
if (GetDriveType(PChar(Drv)) = DRIVE_CDROM) and
|
|
TryMount and (not mbDriveReady(Drv)) then
|
|
begin
|
|
DriveLabel:= mbGetVolumeLabel(Drv, False);
|
|
mbCloseCD(Drv);
|
|
if mbDriveReady(Drv) then
|
|
mbWaitLabelChange(Drv, DriveLabel);
|
|
end;
|
|
// Try to connect to mapped network drive
|
|
if (Drive^.DriveType = dtNetwork) and
|
|
TryMount and (not mbDriveReady(Drv)) then
|
|
begin
|
|
wsLocalName := UTF8Decode(ExtractFileDrive(Drive^.Path));
|
|
wsRemoteName := UTF8Decode(Drive^.DriveLabel);
|
|
FillChar(NetResource, SizeOf(NetResource), #0);
|
|
NetResource.dwType:= RESOURCETYPE_DISK;
|
|
NetResource.lpLocalName:= PWideChar(wsLocalName);
|
|
NetResource.lpRemoteName:= PWideChar(wsRemoteName);
|
|
WNetAddConnection2W(NetResource, nil, nil, CONNECT_INTERACTIVE);
|
|
end;
|
|
Result:= mbDriveReady(Drv);
|
|
end;
|
|
{$ELSEIF DEFINED(DARWIN)}
|
|
begin
|
|
// Because we show under Mac OS X only mounted volumes
|
|
Result:= True;
|
|
end;
|
|
{$ELSEIF DEFINED(LINUX)}
|
|
var
|
|
mtab: PIOFile;
|
|
pme: PMountEntry;
|
|
begin
|
|
Result:= False;
|
|
mtab:= setmntent(_PATH_MOUNTED,'r');
|
|
if not Assigned(mtab) then exit;
|
|
pme:= getmntent(mtab);
|
|
while (pme <> nil) do
|
|
begin
|
|
if SysToUTF8(pme.mnt_dir) = Drive^.Path then
|
|
begin
|
|
Result:= True;
|
|
Break;
|
|
end;
|
|
pme:= getmntent(mtab);
|
|
end;
|
|
endmntent(mtab);
|
|
|
|
if not Result and TryMount then
|
|
Result := MountDrive(Drive);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= True;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileGetAttrNoLinks(const FileName: UTF8String): TFileAttrs;
|
|
{$IFDEF UNIX}
|
|
var
|
|
Info: BaseUnix.Stat;
|
|
begin
|
|
if fpStat(UTF8ToSys(FileName), Info) >= 0 then
|
|
Result := Info.st_mode
|
|
else
|
|
Result := faInvalidAttributes;
|
|
end;
|
|
{$ELSE}
|
|
var
|
|
LinkTarget: UTF8String;
|
|
begin
|
|
LinkTarget := mbReadAllLinks(FileName);
|
|
if LinkTarget <> '' then
|
|
Result := mbFileGetAttr(LinkTarget)
|
|
else
|
|
Result := faInvalidAttributes;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbFileNameToSysEnc(const LongPath: UTF8String): String;
|
|
{$IFDEF MSWINDOWS}
|
|
begin
|
|
Result:= UTF8ToSys(LongPath);
|
|
if Pos('?', Result) <> 0 then
|
|
mbGetShortPathName(LongPath, Result);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= UTF8ToSys(LongPath);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbGetEnvironmentVariable(const sName: UTF8String): UTF8String;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wsName: WideString;
|
|
smallBuf: array[0..1023] of WideChar;
|
|
largeBuf: PWideChar;
|
|
dwResult: DWORD;
|
|
begin
|
|
Result := EmptyStr;
|
|
wsName := UTF8Decode(sName);
|
|
dwResult := GetEnvironmentVariableW(PWideChar(wsName), @smallBuf[0], Length(smallBuf));
|
|
if dwResult > Length(smallBuf) then
|
|
begin
|
|
// Buffer not large enough.
|
|
largeBuf := GetMem(SizeOf(WideChar) * dwResult);
|
|
if Assigned(largeBuf) then
|
|
try
|
|
dwResult := GetEnvironmentVariableW(PWideChar(wsName), largeBuf, dwResult);
|
|
if dwResult > 0 then
|
|
Result := UTF8Encode(WideString(largeBuf));
|
|
finally
|
|
FreeMem(largeBuf);
|
|
end;
|
|
end
|
|
else if dwResult > 0 then
|
|
Result := UTF8Encode(WideString(smallBuf));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= SysToUTF8(GetEnvironmentVariable(UTF8ToSys(sName)));
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function mbSetEnvironmentVariable(const sName, sValue: UTF8String): Boolean;
|
|
{$IFDEF MSWINDOWS}
|
|
var
|
|
wsName,
|
|
wsValue: WideString;
|
|
begin
|
|
wsName:= UTF8Decode(sName);
|
|
wsValue:= UTF8Decode(sValue);
|
|
Result:= SetEnvironmentVariableW(PWideChar(wsName), PWideChar(wsValue));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= (setenv(PChar(UTF8ToSys(sName)), PChar(UTF8ToSys(sValue)), 1) = 0);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function ExtractRootDir(const FileName: UTF8String): UTF8String;
|
|
{$IFDEF UNIX}
|
|
begin
|
|
Result:= ExcludeTrailingPathDelimiter(FindMountPointPath(ExcludeTrailingPathDelimiter(FileName)));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= ExtractFileDrive(FileName);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
procedure FixFormIcon(Handle: LCLType.HWND);
|
|
begin
|
|
// Workaround for Lazarus issue 0018484.
|
|
// Any form that sets its own icon should call this in FormCreate.
|
|
{$IFDEF WINDOWS}
|
|
Windows.SetClassLong(Handle, GCL_HICONSM, 0);
|
|
Windows.SetClassLong(Handle, GCL_HICON, 0);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure HideConsoleWindow;
|
|
begin
|
|
{$IFDEF WINDOWS}
|
|
if isConsole then ShowWindow(GetConsoleWindow, SW_HIDE);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure FixDateNamesToUTF8;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
with DefaultFormatSettings do
|
|
begin
|
|
for i := Low(ShortMonthNames) to High(ShortMonthNames) do
|
|
ShortMonthNames[i] := SysToUTF8(ShortMonthNames[i]);
|
|
for i := Low(ShortDayNames) to High(ShortDayNames) do
|
|
ShortDayNames[i] := SysToUTF8(ShortDayNames[i]);
|
|
for i := Low(LongMonthNames) to High(LongMonthNames) do
|
|
LongMonthNames[i] := SysToUTF8(LongMonthNames[i]);
|
|
for i := Low(LongDayNames) to High(LongDayNames) do
|
|
LongDayNames[i] := SysToUTF8(LongDayNames[i]);
|
|
end;
|
|
end;
|
|
|
|
function ParamStrU(Param: Integer): UTF8String;
|
|
{$IFDEF UNIX}
|
|
begin
|
|
Result:= SysToUTF8(ObjPas.ParamStr(Param));
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
if (Param >= 0) and (Param < argc) then
|
|
Result:= StrPas(argv[Param])
|
|
else
|
|
Result:= EmptyStr;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function ParamStrU(const Param: String): UTF8String;
|
|
{$IFDEF UNIX}
|
|
begin
|
|
Result:= SysToUTF8(Param);
|
|
end;
|
|
{$ELSE}
|
|
begin
|
|
Result:= Param;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ EInvalidQuoting }
|
|
|
|
constructor EInvalidQuoting.Create;
|
|
begin
|
|
inherited Create(rsMsgInvalidQuoting);
|
|
end;
|
|
|
|
end.
|