doublecmd/src/platform/uOSUtils.pas
2018-09-25 17:18:47 +00:00

1157 lines
32 KiB
ObjectPascal

{
Double Commander
-------------------------------------------------------------------------
This unit contains platform depended functions.
Copyright (C) 2006-2016 Alexander Koblov (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}
{$IFDEF DARWIN}
{$modeswitch objectivec1}
{$ENDIF}
interface
uses
SysUtils, Classes, LCLType, uDrive, DCBasicTypes
{$IF DEFINED(UNIX)}
, DCFileAttributes
{$IFDEF DARWIN}
, MacOSAll
{$ENDIF}
{$ENDIF}
;
const
CnstUserCommand = '{command}';
{$IF DEFINED(MSWINDOWS)}
faFolder = faDirectory;
ReversePathDelim = '/';
RunTermCmd = 'cmd.exe'; // default terminal
RunTermParams = '';
RunInTermStayOpenCmd = 'cmd.exe'; // default run in terminal command AND Stay open after command
RunInTermStayOpenParams = '/K {command}';
RunInTermCloseCmd = 'cmd.exe'; // default run in terminal command AND Close after command
RunInTermCloseParams = '/C {command}';
fmtCommandPath = '%s>';
MonoSpaceFont = 'Courier New';
{$ELSEIF DEFINED(UNIX)}
faFolder = S_IFDIR;
ReversePathDelim = '\';
{$IFDEF DARWIN)}
RunTermCmd = '/Applications/Utilities/Terminal.app %D'; // default terminal
RunTermParams = '';
RunInTerm = ''; // default run in terminal command
RunInTermStayOpenCmd = '%COMMANDER_PATH%/scripts/terminal.sh'; // default run in terminal command AND Stay open after command
RunInTermStayOpenParams = '''{command}''';
RunInTermCloseCmd = ''; // default run in terminal command AND Close after command
RunInTermCloseParams = '';
MonoSpaceFont = 'Monaco';
{$ELSE}
RunTermCmd = 'xterm'; // default terminal
RunTermParams = '';
RunInTermStayOpenCmd = 'xterm'; // default run in terminal command AND Stay open after command
RunInTermStayOpenParams = '-e sh -c ''{command}; echo -n Press ENTER to exit... ; read a''';
RunInTermCloseCmd = 'xterm'; // default run in terminal command AND Close after command
RunInTermCloseParams = '-e sh -c {command}';
MonoSpaceFont = 'Monospace';
{$ENDIF}
fmtCommandPath = '[%s]$:';
{$ENDIF}
termStayOpen=True;
termClose=False;
type
tTerminalEndindMode = boolean;
EInvalidCommandLine = class(Exception);
EInvalidQuoting = class(EInvalidCommandLine)
constructor Create; reintroduce;
end;
{$IF DEFINED(MSWINDOWS) and DEFINED(FPC_HAS_CPSTRING)}
NativeString = UnicodeString;
{$ELSE}
NativeString = String;
{$ENDIF}
function NtfsHourTimeDelay(const SourceName, TargetName: String): Boolean;
function FileIsLinkToFolder(const FileName: String; out LinkTarget: String): Boolean;
function FileIsLinkToDirectory(const FileName: String; Attr: TFileAttrs): Boolean;
{en
Execute command line
}
function ExecCmdFork(sCmd: String): Boolean;
{en
Execute external commands
@param(sCmd The executable)
@param(sParams The optional parameters)
@param(sStartPath The initial working directory)
@param(bShowCommandLinePriorToExecute Flag indicating if we want the user to be prompted at the very last
seconds prior to launch execution by offering a dialog window where
he can adjust/confirm the three above parameters.)
@param(bTerm Flag indicating if it should be launch through terminal)
@param(bKeepTerminalOpen Value indicating the type of terminal to use (closed at the end, remain opened, etc.))
}
function ExecCmdFork(sCmd: String; sParams: String; sStartPath: String = ''; bShowCommandLinePriorToExecute: Boolean = False;
bTerm: Boolean = False; bKeepTerminalOpen: tTerminalEndindMode = termStayOpen): Boolean;
function ExecCmdAdmin(sCmd: String; sParams: String; sStartPath: String = ''): 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: String): 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 : String) : Int64;
{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: String;
function GetAppDataDir: String;
function GetTempFolder: String;
{ Similar to "GetTempFolder" but that we can unilaterally delete at the end when closin application}
function GetTempFolderDeletableAtTheEnd: String;
procedure DeleteTempFolderDeletableAtTheEnd;
{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.
}
procedure FormatTerminal(var sCmd: String; var sParams: String; bKeepTerminalOpen: tTerminalEndindMode);
{en
Same as mbFileGetAttr, but dereferences any encountered links.
}
function mbFileGetAttrNoLinks(const FileName: String): 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: String): String;
{en
Converts file name to native representation
}
function mbFileNameToNative(const FileName: String): NativeString; inline;
function mbGetEnvironmentVariable(const sName: String): String;
function mbSetEnvironmentVariable(const sName, sValue: String): 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: String): String;
procedure FixFormIcon(Handle: LCLType.HWND);
procedure HideConsoleWindow;
procedure FixDateNamesToUTF8;
function ParamStrU(Param: Integer): String; overload;
function ParamStrU(const Param: String): String; overload;
{en
Get the current username of the current session
}
function GetCurrentUserName : String;
{en
Get the current machine name
}
function GetComputerNetName: String;
var
AdministratorPrivileges: Boolean = False;
implementation
uses
StrUtils, uFileProcs, FileUtil, uDCUtils, DCOSUtils, DCStrUtils, uGlobs, uLng,
fConfirmCommandLine, uLog, DCConvertEncoding, LazUTF8
{$IF DEFINED(MSWINDOWS)}
, JwaWinCon, Windows, uMyWindows, JwaWinNetWk,
uShlObjAdditional, ShlObj, DCWindows, uNetworkThread
{$ENDIF}
{$IF DEFINED(UNIX)}
, BaseUnix, Unix, uMyUnix, dl
{$IF DEFINED(DARWIN)}
, CocoaAll, uMyDarwin
{$ELSE}
, uGio, uClipboard, uXdg, uKde
{$ENDIF}
{$ENDIF}
;
function FileIsLinkToFolder(const FileName: String; out
LinkTarget: String): 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}
function ExecCmdFork(sCmd, sParams, sStartPath:String; bShowCommandLinePriorToExecute, bTerm : Boolean; bKeepTerminalOpen: tTerminalEndindMode) : Boolean;
{$IFDEF UNIX}
var
Args : TDynamicStringArray;
bFlagKeepGoing: boolean = True;
begin
result:=False;
if bTerm then
FormatTerminal(sCmd, sParams, bKeepTerminalOpen);
if bShowCommandLinePriorToExecute then
bFlagKeepGoing:= ConfirmCommandLine(sCmd, sParams, sStartPath);
if bFlagKeepGoing then
begin
if (log_commandlineexecution in gLogOptions) then logWrite(rsMsgLogExtCmdLaunch+': '+rsSimpleWordFilename+'='+sCmd+' / '+rsSimpleWordParameter+'='+sParams+' / '+rsSimpleWordWorkDir+'='+sStartPath);
if sCmd = EmptyStr then Exit(False);
sCmd := UTF8ToSys(sCmd);
SplitCommandArgs(UTF8ToSys(sParams), Args);
Result := ExecuteCommand(sCmd, Args, sStartPath);
if (log_commandlineexecution in gLogOptions) then
begin
if Result then
logWrite(rsMsgLogExtCmdResult+': '+rsSimpleWordResult+'='+'Success!'+' / '+rsSimpleWordFilename+'='+sCmd+' / '+rsSimpleWordParameter+'='+sParams+' / '+rsSimpleWordWorkDir+'='+sStartPath)
else
logWrite(rsMsgLogExtCmdResult+': '+rsSimpleWordResult+'='+'Failed!'+' / '+rsSimpleWordFilename+'='+sCmd+' / '+rsSimpleWordParameter+'='+sParams+' / '+rsSimpleWordWorkDir+'='+sStartPath);
end;
end
else
begin
Result := True;
end;
end;
{$ELSE}
var
wFileName, wParams, wStartPath: WideString;
bFlagKeepGoing: boolean = True;
ExecutionResult:HINST;
begin
sStartPath:=RemoveQuotation(sStartPath);
if sStartPath='' then
sStartPath:=mbGetCurrentDir;
sCmd:= NormalizePathDelimiters(sCmd);
if bTerm then
begin
sCmd := ConcatenateStrWithSpace(sCmd,sParams);
if bKeepTerminalOpen = termStayOpen then
begin
sParams:=StringReplace(gRunInTermStayOpenParams, '{command}', QuoteFilenameIfNecessary(sCmd) , [rfIgnoreCase]);
sCmd := gRunInTermStayOpenCmd;
end
else
begin
sParams:=StringReplace(gRunInTermCloseParams, '{command}', QuoteFilenameIfNecessary(sCmd) , [rfIgnoreCase]);
sCmd := gRunInTermCloseCmd;
end;
end;
if bShowCommandLinePriorToExecute then
bFlagKeepGoing:= ConfirmCommandLine(sCmd, sParams, sStartPath);
if bFlagKeepGoing then
begin
wFileName:= UTF8Decode(sCmd);
wParams:= UTF8Decode(sParams);
wStartPath:= UTF8Decode(sStartPath);
if (log_commandlineexecution in gLogOptions) then logWrite(rsMsgLogExtCmdLaunch+': '+rsSimpleWordFilename+'='+sCmd+' / '+rsSimpleWordParameter+'='+sParams+' / '+rsSimpleWordWorkDir+'='+sStartPath);
ExecutionResult:=ShellExecuteW(0, nil, PWChar(wFileName), PWChar(wParams), PWChar(wStartPath), SW_SHOW);
if (log_commandlineexecution in gLogOptions) then logWrite(rsMsgLogExtCmdResult+': '+rsSimpleWordResult+'='+ifThen((ExecutionResult > 32),'Success!',IntToStr(ExecutionResult)+':'+SysErrorMessage(ExecutionResult))+' / '+rsSimpleWordFilename+'='+sCmd+' / '+rsSimpleWordParameter+'='+sParams+' / '+rsSimpleWordWorkDir+'='+sStartPath);
Result := (ExecutionResult > 32);
end
else
begin
result:=True; //User abort, so let's fake all things completed.
end;
end;
{$ENDIF}
function FileIsLinkToDirectory(const FileName: String; Attr: TFileAttrs): Boolean;
{$IFDEF UNIX}
var
Info: BaseUnix.Stat;
begin
Result:= FPS_ISLNK(Attr) and (fpStat(UTF8ToSys(FileName), Info) >= 0) and FPS_ISDIR(Info.st_mode);
end;
{$ELSE}
begin
Result:= FPS_ISLNK(Attr) and FPS_ISDIR(Attr);
end;
{$ENDIF}
function ExecCmdFork(sCmd: String): Boolean;
{$IFDEF UNIX}
var
Command: String;
Args : TDynamicStringArray;
begin
SplitCmdLine(sCmd, Command, Args);
if (log_commandlineexecution in gLogOptions) then logWrite(rsMsgLogExtCmdLaunch + ': ' + rsSimpleWordCommand + '=' + sCmd);
Result:= ExecuteCommand(Command, Args, EmptyStr);
if (log_commandlineexecution in gLogOptions) then
begin
if Result then
logWrite(rsMsgLogExtCmdResult + ': ' + rsSimpleWordResult + '=' + 'Success!' + ' / ' + rsSimpleWordCommand + '=' + sCmd)
else
logWrite(rsMsgLogExtCmdResult + ': ' + rsSimpleWordResult + '=' + 'Failed!' + ' / ' + rsSimpleWordCommand + '=' + sCmd);
end;
end;
{$ELSE}
var
sFileName,
sParams: String;
ExecutionResult: HINST;
wsStartPath: UnicodeString;
begin
SplitCmdLine(sCmd, sFileName, sParams);
wsStartPath:= UTF8Decode(mbGetCurrentDir());
sFileName:= NormalizePathDelimiters(sFileName);
if (log_commandlineexecution in gLogOptions) then
logWrite(rsMsgLogExtCmdLaunch + ': ' + rsSimpleWordFilename + '=' + sCmd + ' / ' + rsSimpleWordParameter + '=' + sParams);
ExecutionResult := ShellExecuteW(0, nil, PWideChar(UTF8Decode(sFileName)), PWideChar(UTF8Decode(sParams)), PWideChar(wsStartPath), SW_SHOW);
if (log_commandlineexecution in gLogOptions) then
begin
logWrite(rsMsgLogExtCmdResult + ': ' + rsSimpleWordResult + '=' + IfThen((ExecutionResult > 32), 'Success!',
IntToStr(ExecutionResult) + ':' + SysErrorMessage(ExecutionResult)) +' / ' + rsSimpleWordFilename +
'=' + sCmd + ' / ' + rsSimpleWordParameter + '=' + sParams);
end;
Result := (ExecutionResult > 32);
end;
{$ENDIF}
function ExecCmdAdmin(sCmd: String; sParams: String; sStartPath: String): Boolean;
{$IF DEFINED(MSWINDOWS)}
begin
sStartPath:= RemoveQuotation(sStartPath);
if sStartPath = '' then
sStartPath:= mbGetCurrentDir;
sCmd:= NormalizePathDelimiters(sCmd);
Result:= ShellExecuteW(0, 'runas', PWideChar(UTF8Decode(sCmd)),
PWideChar(UTF8Decode(sParams)),
PWideChar(UTF8Decode(sStartPath)), SW_SHOW) > 32;
end;
{$ELSE}
begin
Result:= False;
end;
{$ENDIF}
function ShellExecute(URL: String): Boolean;
{$IF DEFINED(MSWINDOWS)}
var
Return: HINST;
wsFileName: UnicodeString;
wsStartPath: UnicodeString;
begin
URL:= NormalizePathDelimiters(URL);
wsFileName:= UTF8Decode(QuoteDouble(URL));
wsStartPath:= UTF8Decode(mbGetCurrentDir());
Return:= ShellExecuteW(0, nil, PWideChar(wsFileName), nil, PWideChar(wsStartPath), 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
sCmdLine: String;
begin
Result:= False;
sCmdLine:= EmptyStr;
if FileIsUnixExecutable(URL) then
begin
if GetPathType(URL) <> ptAbsolute then
sCmdLine := './';
sCmdLine:= sCmdLine + QuoteStr(URL);
end
else
begin
if (DesktopEnv = DE_KDE) and (HasKdeOpen = True) then
Result:= KioOpen(URL) // Under KDE use "kioclient" to open files
else if HasGio and (DesktopEnv <> DE_XFCE) 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(PAnsiChar(CeUtf8ToSys(Path)), @sbfs) = 0);
if not Result then Exit;
FreeSize := (Int64(sbfs.bavail) * sbfs.bsize);
TotalSize := (Int64(sbfs.blocks) * sbfs.bsize);
end;
{$ELSE}
var
wPath: UnicodeString;
begin
wPath:= UTF16LongName(Path);
Result:= GetDiskFreeSpaceExW(PWideChar(wPath), FreeSize, TotalSize, nil);
end;
{$ENDIF}
function GetDiskMaxFileSize(const Path: String): Int64;
{$IFDEF UNIX}
const
MSDOS_SUPER_MAGIC = $4d44;
var
sbfs: TStatFS;
begin
Result := High(Int64);
if (fpStatFS(PAnsiChar(CeUtf8ToSys(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 NtfsHourTimeDelay(const SourceName, TargetName: String): Boolean;
{$IFDEF MSWINDOWS}
var
lpDummy: DWORD = 0;
lpSourceFileSystem,
lpTargetFileSystem: array [0..MAX_PATH] of WideChar;
begin
Result:= False;
if GetVolumeInformationW(PWideChar(UTF8Decode(ExtractFileDrive(SourceName)) + PathDelim),
nil, 0, nil, lpDummy, lpDummy, lpSourceFileSystem, MAX_PATH) and
GetVolumeInformationW(PWideChar(UTF8Decode(ExtractFileDrive(TargetName)) + PathDelim),
nil, 0, nil, lpDummy, lpDummy, lpTargetFileSystem, MAX_PATH) then
begin
Result:= (SameText(lpSourceFileSystem, 'FAT32') and SameText(lpTargetFileSystem, 'NTFS')) or
(SameText(lpTargetFileSystem, 'FAT32') and SameText(lpSourceFileSystem, 'NTFS'))
end;
end;
{$ELSE}
begin
Result:= False;
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: UnicodeString;
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(UTF16ToUTF8(wHomeDir));
end;
{$ELSE}
begin
Result:= ExcludeBackPathDelimiter(SysToUTF8(GetEnvironmentVariable('HOME')));
end;
{$ENDIF}
function GetShell : String;
{$IFDEF MSWINDOWS}
var
iSize: Integer;
wShell: UnicodeString;
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:= UTF16ToUTF8(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;
procedure FormatTerminal(var sCmd: String; var sParams: String; bKeepTerminalOpen: tTerminalEndindMode);
var
sConfigParam:string;
begin
{$IF DEFINED(UNIX)}
sParams := ConcatenateStrWithSpace(sCmd, sParams);
if bKeepTerminalOpen = termStayOpen then
begin
sCmd := gRunInTermStayOpenCmd;
sConfigParam := gRunInTermStayOpenParams;
end
else
begin
sCmd := gRunInTermCloseCmd;
sConfigParam := gRunInTermCloseParams;
end;
sCmd := ReplaceEnvVars(sCmd);
if Pos(CnstUserCommand, sConfigParam) <> 0 then
sParams := StringReplace(sConfigParam, CnstUserCommand, sParams , [rfIgnoreCase])
else
sParams := ConcatenateStrWithSpace(sConfigParam, sParams);
{$ELSEIF DEFINED(MSWINDOWS)}
// if bKeepTerminalOpen then
// Result := Format('%s %s', [gRunInTermStayOpenCmd, QuoteDouble(Command)])
// else
// Result := Format('%s %s', [gRunInTermCloseCmd, QuoteDouble(Command)]);
sParams := ConcatenateStrWithSpace(sCmd, sParams);
if bKeepTerminalOpen = termStayOpen then
begin
sCmd := gRunInTermStayOpenCmd;
sConfigParam := gRunInTermStayOpenParams;
end
else
begin
sCmd := gRunInTermCloseCmd;
sConfigParam := gRunInTermCloseParams;
end;
if pos(CnstUserCommand,sConfigParam)<>0 then
sParams := StringReplace(sConfigParam, CnstUserCommand, sParams , [rfIgnoreCase])
else
sParams:=ConcatenateStrWithSpace(sConfigParam, sParams);
{$ENDIF}
end;
function GetAppConfigDir: String;
{$IF DEFINED(MSWINDOWS)}
const
SHGFP_TYPE_CURRENT = 0;
var
wPath: array[0..MAX_PATH-1] of WideChar;
wUser: UnicodeString;
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 := UTF16ToUTF8(WideString(wPath));
end
else
begin
dwLength := UNLEN + 1;
SetLength(wUser, dwLength);
if GetUserNameW(PWideChar(wUser), @dwLength) then
begin
SetLength(wUser, dwLength - 1);
Result := GetTempDir + UTF16ToUTF8(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:= CeSysToUtf8(uinfo^.pw_dir) + '/.config/' + ApplicationName
else
Result:= ExcludeTrailingPathDelimiter(SysToUTF8(SysUtils.GetAppConfigDir(False)));
end;
{$ENDIF}
function GetAppCacheDir: String;
{$IF DEFINED(MSWINDOWS)}
var
APath: array[0..MAX_PATH] of WideChar;
begin
if SHGetSpecialFolderPathW(0, APath, CSIDL_LOCAL_APPDATA, True) then
Result:= UTF16ToUTF8(UnicodeString(APath)) + DirectorySeparator + ApplicationName
else
Result:= GetAppConfigDir;
end;
{$ELSEIF DEFINED(DARWIN)}
begin
Result:= NSGetFolderPath(NSCachesDirectory);
end;
{$ELSE}
var
uinfo: PPasswordRecord;
begin
uinfo:= getpwuid(fpGetUID);
if (uinfo <> nil) and (uinfo^.pw_dir <> '') then
Result:= CeSysToUtf8(uinfo^.pw_dir) + '/.cache/' + ApplicationName
else
Result:= GetHomeDir + '/.cache/' + ApplicationName;
end;
{$ENDIF}
function GetAppDataDir: String;
{$IF DEFINED(MSWINDOWS)}
begin
Result:= GetAppCacheDir;
end;
{$ELSEIF DEFINED(DARWIN)}
begin
Result:= NSGetFolderPath(NSApplicationSupportDirectory);
end;
{$ELSE}
begin
Result:= IncludeTrailingPathDelimiter(GetUserDataDir) + ApplicationName;
end;
{$ENDIF}
function GetTempFolder: String;
begin
Result:= GetTempDir + '_dc';
if not mbDirectoryExists(Result) then
mbCreateDir(Result);
Result:= Result + PathDelim;
end;
function GetTempFolderDeletableAtTheEnd: String;
begin
Result:= GetTempDir + '_dc~~~';
if not mbDirectoryExists(Result) then
mbCreateDir(Result);
Result:= Result + PathDelim;
end;
procedure DeleteTempFolderDeletableAtTheEnd;
var
TempFolderName:string;
begin
TempFolderName:= GetTempDir + '_dc~~~';
if mbDirectoryExists(TempFolderName) then
DelTree(TempFolderName);
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);
TNetworkThread.Connect(PWideChar(wsLocalName), PWideChar(wsRemoteName), RESOURCETYPE_DISK);
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 CeSysToUtf8(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: String): 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: String;
begin
LinkTarget := mbReadAllLinks(FileName);
if LinkTarget <> '' then
Result := mbFileGetAttr(LinkTarget)
else
Result := faInvalidAttributes;
end;
{$ENDIF}
function mbFileNameToSysEnc(const LongPath: String): String;
{$IFDEF MSWINDOWS}
begin
Result:= CeUtf8ToSys(LongPath);
if Pos('?', Result) <> 0 then
mbGetShortPathName(LongPath, Result);
end;
{$ELSE}
begin
Result:= CeUtf8ToSys(LongPath);
end;
{$ENDIF}
function mbGetEnvironmentVariable(const sName: String): String;
{$IFDEF MSWINDOWS}
var
wsName: UnicodeString;
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 := UTF16ToUTF8(UnicodeString(largeBuf));
finally
FreeMem(largeBuf);
end;
end
else if dwResult > 0 then
Result := UTF16ToUTF8(UnicodeString(smallBuf));
end;
{$ELSE}
begin
Result:= SysToUTF8(GetEnvironmentVariable(UTF8ToSys(sName)));
end;
{$ENDIF}
function mbSetEnvironmentVariable(const sName, sValue: String): Boolean;
{$IFDEF MSWINDOWS}
var
wsName,
wsValue: UnicodeString;
begin
wsName:= UTF8Decode(sName);
wsValue:= UTF8Decode(sValue);
Result:= SetEnvironmentVariableW(PWideChar(wsName), PWideChar(wsValue));
end;
{$ELSE}
begin
Result:= (setenv(PAnsiChar(CeUtf8ToSys(sName)), PAnsiChar(CeUtf8ToSys(sValue)), 1) = 0);
end;
{$ENDIF}
function ExtractRootDir(const FileName: String): String;
{$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): String;
{$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): String;
{$IFDEF UNIX}
begin
Result:= SysToUTF8(Param);
end;
{$ELSE}
begin
Result:= Param;
end;
{$ENDIF}
{ EInvalidQuoting }
constructor EInvalidQuoting.Create;
begin
inherited Create(rsMsgInvalidQuoting);
end;
{ GetCurrentUserName }
function GetCurrentUserName : String;
{$IF DEFINED(MSWINDOWS)}
var
wsUserName : UnicodeString;
dwUserNameLen : DWORD = UNLEN + 1;
begin
SetLength(wsUserName, dwUserNameLen);
if GetUserNameW(PWideChar(wsUserName), dwUserNameLen) then
begin
SetLength(wsUserName, dwUserNameLen - 1);
Result := UTF16ToUTF8(wsUserName);
end
else
Result := 'Unknown';
end;
{$ELSEIF DEFINED(UNIX)}
begin
Result:= SysToUTF8(GetEnvironmentVariable('USER'));
end;
{$ENDIF}
{ GetComputerNetName }
function GetComputerNetName: String;
{$IF DEFINED(MSWINDOWS)}
var
Size: DWORD = MAX_PATH;
Buffer: array[0..Pred(MAX_PATH)] of WideChar;
begin
if GetComputerNameW(Buffer, Size) then
Result := UTF16ToUTF8(UnicodeString(Buffer))
else
Result := ''
end;
{$ELSEIF DEFINED(UNIX)}
begin
Result:= SysToUTF8(GetHostName);
end;
{$ENDIF}
function mbFileNameToNative(const FileName: String): NativeString;
{$IF DEFINED(MSWINDOWS) and DEFINED(FPC_HAS_CPSTRING)}
begin
Result:= UTF16LongName(FileName);
end;
{$ELSE}
begin
Result:= Utf8ToSys(FileName);
end;
{$ENDIF}
initialization
{$IF DEFINED(UNIX)}
AdministratorPrivileges:= True; // (fpGetUID = 0); temporary while ExecCmdAdmin not implemented
{$ELSE}
AdministratorPrivileges:= IsUserAdmin;
{$ENDIF}
end.