1036 lines
29 KiB
ObjectPascal
1036 lines
29 KiB
ObjectPascal
{
|
|
********************************************************************************
|
|
|
|
YaPeTaVi - Yet another Periodic Table Viewer
|
|
Copyright (C) 2009-2012 Geoffray Levasseur <geoffray.levasseurbrandin@numericable.fr>.
|
|
Parts inspired from PeaZip souce code:
|
|
Copyright (C) 2006 Giorgio Tani <giorgiotani@interfree.it>
|
|
Parts inspired from Lazarus source code:
|
|
Copyright (c) 2001-2010 The Lazarus developpers
|
|
|
|
All rights reserved.
|
|
http://www.geoffray-levasseur.org/
|
|
|
|
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 3 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.
|
|
|
|
********************************************************************************
|
|
|
|
Description:
|
|
Various utilities and functions
|
|
|
|
}
|
|
|
|
unit uUtils;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Graphics, StdCtrls;
|
|
|
|
type
|
|
TOperatingSystem = (osWindows, osWin32, osWin64, osWinCE, osGo32V2, osOS2,
|
|
osGenUnix, osFreeBSD, osNetBSD, osLinux, osBeOS, osQNX, osSun, osDarwin,
|
|
osAmiga, osAtari, osMacOS, osPalmOS, osUnknow);
|
|
|
|
TPathType = (ptIsDircetory, ptIsFile, ptIsExecutable, ptNotExists,
|
|
ptInvalid, ptDosPathOnUnix, ptUnixPathOnDos);
|
|
|
|
TDesktopEnv = (deUnknow, deGnome, deKDE, deMacOSX, deWindows, deLXDE, deXFCE);
|
|
Str4 = string[4];
|
|
|
|
const
|
|
AOSDesc: array[TOperatingSystem] of string = ('Microsoft Windows (generic)',
|
|
'Microsoft Windows (32 bits)', 'Microsoft Windows (64 bits)',
|
|
'Microsoft Windows CE', 'Microsoft Windows with Go32 v2', 'IBM OS/2',
|
|
'Generic Unix', 'Free BSD', 'Net BSD', 'Linux', 'BeOS', 'QNX',
|
|
'Sun Solaris', 'Mac OS X (Darwin)', 'Amiga OS', 'Atari OS', 'MacOS',
|
|
'Palm OS', 'Unknow Operating System');
|
|
|
|
{$warning we should be able to detect more desktop environment}
|
|
ADescEnvDesc: array[TDesktopEnv] of string = ('Unknow', 'Gnome', 'KDE',
|
|
'Mac OS X', 'Microsoft Windows', 'LXDE', 'XFCE');
|
|
|
|
cpuUnknow = $00;
|
|
cpu32Bit = $10;
|
|
cpu64Bit = $20;
|
|
cpuGenericMoto = $30;
|
|
cpuMiscRisc = $40;
|
|
cpuGenericPPC = $50;
|
|
cpu86 = $11;
|
|
cpu87 = $12;
|
|
cpu386 = $13;
|
|
cpuX86_64 = $21;
|
|
cpu68k = $31;
|
|
cpuM68020 = $32;
|
|
cpu68 = $33;
|
|
cpuAlpha = $41;
|
|
cpuSparc = $42;
|
|
cpuMips = $43;
|
|
cpuPowerPC = $51;
|
|
cpuArm = $60;
|
|
cpuBigEndian = $80;
|
|
|
|
{$IFDEF WIN32}
|
|
Delimiter = '"';
|
|
{$ELSE}
|
|
{$IFDEF WIN64}
|
|
Delimiter = '"';
|
|
{$ELSE}
|
|
{$IFDEF LINUX}
|
|
Delimiter = '''';
|
|
{$ELSE}
|
|
{$IFDEF FREEBSD}
|
|
Delimiter = '''';
|
|
{$ELSE}
|
|
{$IFDEF NETBSD}
|
|
Delimiter = '''';
|
|
{$ELSE}
|
|
{$IFDEF DARWIN}
|
|
Delimiter = '''';
|
|
{$ELSE}
|
|
Delimiter = ''''; //Default value (for other Unixes)
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
var //default values and variable declaration for configuration
|
|
TwoDigitDateLimit: byte = 30; {$deprecated} //means 29 is 2029 and 30 is 1930 (deprecated)
|
|
DateFormat: string = 'dd/mm/yyyy';
|
|
|
|
//get the user name from personal directory path (multiplatform way)
|
|
//note that builtin FPC GetUserName function is for Windows only
|
|
//on unix su command is keeping the username as before launching it: find a workarround
|
|
function GetUserName: string;
|
|
|
|
//get computer name
|
|
function GetComputerName: string;
|
|
|
|
//Get the running operating system
|
|
function GetCurrentOS: TOperatingSystem;
|
|
|
|
//Find the default browser according system configuration
|
|
function FindDefaultBrowser(out ABrowser, AParams: String): Boolean;
|
|
|
|
//Open URL in the default browser
|
|
function OpenURL(AURL: String): Boolean;
|
|
|
|
//convert a 4 char string (maximum) into a Word
|
|
function StrToWord(const S: Str4): Word;
|
|
|
|
//Convert a 3 number date into a string (deprecated)
|
|
function DateToStr(Y, M, D: Word): string; deprecated;
|
|
|
|
//Remove the extention of a file name
|
|
function RemoveFileExt(const aFileName: string): string;
|
|
|
|
//Get the executable path of the program with final / or \
|
|
function GetProgramPath: string;
|
|
|
|
//get program prefix (see explanation in its code)
|
|
function GetProgramPrefix: string;
|
|
|
|
//Give the number of items present in a listed string for the given separator
|
|
function CountItemsInStr(Str: string; Separator: Char): integer;
|
|
|
|
//Return the item in a listed string at the given index
|
|
function GetItemInStr(Str: string; Separator: Char; Index: integer): string;
|
|
|
|
//find which desktop environment is currently running
|
|
function GetDesktopEnv: TDesktopEnv; //0 unknown, 1 Gnome, 2 KDE, 20 MS Windows, 30 OSX
|
|
|
|
//return the position of a string in a stringlist, -1 if no occurence
|
|
function StrExistsInList(const StrList: TStringList; S: string): integer;
|
|
function StrExistsInListBox(const StrList: TListBox; S: string): integer;
|
|
|
|
//remove and change (repectively) a string from a StringList, returning position
|
|
//of the change in the SL, -1 if no occurence
|
|
function StrRemove(var StrList: TStringList; S: string): integer;
|
|
function StrChange(var StrList: TStringList; const sOld, sNew: string): integer;
|
|
|
|
//check filename before use regarding the environment
|
|
function FormatFileName(S: String; out VS: string): Integer;
|
|
|
|
//check if a directory is empty
|
|
function DirectoryIsEmpty(ADir: string): Boolean;
|
|
|
|
//check for possible problematic chars in a string that could be used as filename
|
|
function RemoveSpecialChar(const S: UnicodeString): string;
|
|
|
|
//create a lock file containing the computer name and user name of the locker
|
|
function CreateLockFile(const AName: string): Boolean;
|
|
procedure GetLockingInfo(const AName: string; out User, Computer: string);
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$ifdef WINDOWS}Windows,{$endif}
|
|
{$ifdef UNIX}BaseUnix,{$endif}
|
|
Forms, FileUtil, UTF8Process, Dos, Dialogs, uDebug, uConst;
|
|
|
|
{$ifdef UNIX}
|
|
function GetUserName: string;
|
|
begin
|
|
{$warning Find a workarround to get the good username in case where su is being used}
|
|
Result := GetEnvironmentVariable('USER');
|
|
end;
|
|
{$endif}
|
|
|
|
{$ifdef WINDOWS}
|
|
function GetUserName: string;
|
|
var
|
|
pcName: PChar;
|
|
nSize : DWord;
|
|
begin
|
|
nSize := 100;
|
|
pcName := StrAlloc(nSize);
|
|
Windows.GetUserName(pcName, nSize);
|
|
Result := StrPas(pcName);
|
|
StrDispose(pcName);
|
|
end;
|
|
{$endif}
|
|
|
|
{$ifdef UNIX}
|
|
function GetComputerName: String;
|
|
var
|
|
AUtsName: UtsName;
|
|
begin
|
|
Result := '';
|
|
AUtsName.Nodename[0] := #0; //this is just to avoid the compiler warning
|
|
FillChar(AUtsName, SizeOf(AUtsName), 0);
|
|
if FpUname(AUtsName) <> -1 then
|
|
Result := AUtsName.Nodename;
|
|
end;
|
|
{$endif}
|
|
|
|
{$ifdef WINDOWS}
|
|
function GetComputerName: String;
|
|
var
|
|
aBuf: array[0..255] of Char;
|
|
bRet: Boolean;
|
|
nSize: DWord;
|
|
begin
|
|
aBuf[0] := #0;
|
|
nSize := SizeOf(aBuf);
|
|
bRet := Windows.GetComputerName(aBuf, nSize);
|
|
aBuf[SizeOf(aBuf) - 1] := #0; // Just for safety reasons
|
|
Result := Trim(aBuf);
|
|
end;
|
|
{$endif}
|
|
|
|
function GetCurrentOS: TOperatingSystem;
|
|
begin
|
|
//unrecognized system, it is overwritten if one of the following conditions is met
|
|
Result := osUnknow;
|
|
// DOS/Windows type systems
|
|
{$IFDEF MSWINDOWS}
|
|
//generic Windows, replaced with following $0* codes
|
|
//if a more specific match is found
|
|
Result := osWindows;
|
|
{$ENDIF}
|
|
{$IFDEF WIN32}
|
|
Result := osWin32;
|
|
{$ENDIF}
|
|
{$IFDEF WIN64}
|
|
Result := osWin64;
|
|
{$ENDIF}
|
|
{$IFDEF WINCE}
|
|
Result := osWinCE;
|
|
{$ENDIF}
|
|
{$IFDEF GO32V2}
|
|
Result := Go32V2;
|
|
{$ENDIF}
|
|
{$IFDEF OS2}
|
|
Result := osOS2;
|
|
{$ENDIF}
|
|
// Unix type systems
|
|
{$IFDEF UNIX}
|
|
//generic UNIX, replaced with some following codes
|
|
//if a more specific match is found
|
|
Result := osGenUnix;
|
|
{$ENDIF}
|
|
{$IFDEF FREEBSD}
|
|
Result := osFreeBSD;
|
|
{$ENDIF}
|
|
{$IFDEF NETBSD}
|
|
Result := osNetBSD;
|
|
{$ENDIF}
|
|
{$IFDEF LINUX}
|
|
Result := osLinux;
|
|
{$ENDIF}
|
|
{$IFDEF BEOS}
|
|
Result := osBeOS;
|
|
{$ENDIF}
|
|
{$IFDEF QNX}
|
|
Result := osQNX;
|
|
{$ENDIF}
|
|
{$IFDEF SUNOS}
|
|
Result := osSunOS;
|
|
{$ENDIF}
|
|
{$IFDEF DARWIN} // MacOS <= 10.x
|
|
Result := osDarwin;
|
|
{$ENDIF}
|
|
//Other systems
|
|
{$IFDEF AMIGA}
|
|
Result := osAmiga;
|
|
{$ENDIF}
|
|
{$IFDEF ATARI} //Atari OS
|
|
Result := osAtari;
|
|
{$ENDIF}
|
|
{$IFDEF MAC} // MacOS => 9.x
|
|
Result := osMacOS;
|
|
{$ENDIF}
|
|
{$IFDEF PALMOS}
|
|
Result := osPalmOS;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
//if cpu type, < $80 (encoded in ls 7 bit);
|
|
//0 if unrecognized, replaced if a match is found
|
|
function GetUsedCPU: Word; //CPU and endianness (in MSB)
|
|
begin
|
|
Result := cpuUnknow; //cpu type, < $80 (encoded in ls 7 bit); 0:= unrecognized, replaced if a match is found
|
|
{$IFDEF CPU32}
|
|
//generic 32 bit CPU, replaced if a more specific match is found
|
|
Result := cpu32Bit;
|
|
{$ENDIF}
|
|
{$IFDEF CPU64}
|
|
//generic 64 bit CPU, replaced if a more specific match is found
|
|
Result := cpu64Bit;
|
|
{$ENDIF}
|
|
{$IFDEF CPU86}
|
|
Result := cpu86;
|
|
{$ENDIF}
|
|
{$IFDEF CPU87}
|
|
Result := cpu87;
|
|
{$ENDIF}
|
|
{$IFDEF CPUI386}
|
|
Result := cpu386;
|
|
{$ENDIF}
|
|
{$IFDEF CPUX86_64}
|
|
Result := cpuX86_64;
|
|
{$ENDIF}
|
|
{$IFDEF CPU68k}
|
|
Result := cpu68k;
|
|
{$ENDIF}
|
|
{$IFDEF CPUM68020}
|
|
Result := cpuM68020;
|
|
{$ENDIF}
|
|
{$IFDEF CPU68}
|
|
Result := cpu68;
|
|
{$ENDIF}
|
|
{$IFDEF CPUSPARC}
|
|
Result := cpuSpark;
|
|
{$ENDIF}
|
|
{$IFDEF CPUALPHA}
|
|
Result := cpuAlpha;
|
|
{$ENDIF}
|
|
{$IFDEF CPUMIPS}
|
|
Result := cpuMips;
|
|
{$ENDIF}
|
|
{$IFDEF CPUPOWERPC}
|
|
Result := cpuPowerPC;
|
|
{$ENDIF}
|
|
{$IFDEF CPUARM}
|
|
Result := cpuArm;
|
|
{$ENDIF}
|
|
{$IFDEF ENDIAN_BIG}
|
|
//if processor is declared big endian (some processors don't have a fixed
|
|
//endian setting) the msb is set to 1, otherwise is 0
|
|
Result := Result + cpuBigEndian;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
//This is a somewhat altered version of the Lazarus' source code
|
|
//FindDefaultBrowser function
|
|
function FindDefaultBrowser(out ABrowser, AParams: String): Boolean;
|
|
|
|
function Find(const ShortFilename: String; out ABrowser: String): Boolean; inline;
|
|
begin
|
|
ABrowser := SearchFileInPath(ShortFilename + GetExeExt, '',
|
|
GetEnvironmentVariable('PATH'), PathSeparator, [sffDontSearchInBasePath]);
|
|
Result := ABrowser <> '';
|
|
end;
|
|
|
|
begin
|
|
{$IFDEF MSWindows}
|
|
Find('rundll32', ABrowser);
|
|
AParams := 'url.dll,FileProtocolHandler %s';
|
|
{$ELSE}
|
|
{$IFDEF DARWIN}
|
|
// open command launches url in the appropriate browser under Mac OS X
|
|
Find('open', ABrowser);
|
|
AParams := '%s';
|
|
{$ELSE}
|
|
ABrowser := '';
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
if ABrowser = '' then
|
|
begin
|
|
AParams := '%s';
|
|
// Then search in path. Prefer open source ;)
|
|
if Find('xdg-open', ABrowser) // Portland OSDL/FreeDesktop standard on Linux
|
|
or Find('htmlview', ABrowser) // some redhat systems
|
|
or Find('konqueror', ABrowser)
|
|
or Find('firefox', ABrowser)
|
|
or Find('mozilla', ABrowser)
|
|
or Find('galeon', ABrowser)
|
|
or Find('safari', ABrowser)
|
|
or Find('netscape', ABrowser)
|
|
or Find('opera', ABrowser)
|
|
or Find('iexplore', ABrowser) then ;// some windows systems
|
|
end;
|
|
Result := ABrowser <> '';
|
|
end;
|
|
|
|
//each system has a different OpenURL working. this is mainly took from
|
|
//Lazarus source code with minor changes
|
|
{$IFDEF Windows}
|
|
function OpenURL(AURL: String): Boolean;
|
|
var
|
|
{$IFDEF WinCE}
|
|
Info: SHELLEXECUTEINFO;
|
|
{$ELSE}
|
|
ws: WideString;
|
|
ans: AnsiString;
|
|
{$ENDIF}
|
|
begin
|
|
Result := False;
|
|
if AURL = '' then
|
|
Exit;
|
|
{$IFDEF WinCE}
|
|
FillChar(Info, SizeOf(Info), 0);
|
|
Info.cbSize := SizeOf(Info);
|
|
Info.fMask := SEE_MASK_FLAG_NO_UI;
|
|
Info.lpVerb := 'open';
|
|
Info.lpFile := PWideChar(UTF8Decode(AURL));
|
|
Result := ShellExecuteEx(@Info);
|
|
{$ELSE}
|
|
if (GetCurrentOS = osWin32) or (GetCurrentOS = osWin64) then
|
|
begin
|
|
ws := UTF8Decode(AURL);
|
|
Result := ShellExecuteW(0, 'open', PWideChar(ws), nil, nil, 0) > 32;
|
|
end else
|
|
begin
|
|
ans := Utf8ToAnsi(AURL); // utf8 must be converted to Windows Ansi-codepage
|
|
//this should handle every kind of address if software available
|
|
Result := ShellExecute(0, 'open', PAnsiChar(ans), nil, nil, 0) > 32;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
{$ELSE}
|
|
{$IFDEF DARWIN}
|
|
function OpenURL(AURL: string): Boolean;
|
|
var
|
|
cf: CFStringRef;
|
|
url: CFURLRef;
|
|
w: WideString;
|
|
begin
|
|
if AURL = '' then
|
|
Exit(False);
|
|
cf := CFStringCreateWithCString(kCFAllocatorDefault, @AURL[1],
|
|
kCFStringEncodingUTF8);
|
|
if not Assigned(cf) then
|
|
Exit(False);
|
|
url := CFURLCreateWithString(nil, cf, nil);
|
|
Result := LSOpenCFURLRef(url, nil) = 0;
|
|
CFRelease(url);
|
|
CFRelease(cf);
|
|
end;
|
|
{$ELSE} //we suppose here a Unix system... exotic or very old OS not supported.
|
|
function OpenURL(AURL: String): Boolean;
|
|
var
|
|
ABrowser, AParams: String;
|
|
BrowserProcess: TProcessUTF8;
|
|
begin
|
|
Result := FindDefaultBrowser(ABrowser, AParams) and
|
|
FileExists(ABrowser);
|
|
if not Result then
|
|
Exit;
|
|
//run
|
|
BrowserProcess := TProcessUTF8.Create(nil);
|
|
try
|
|
{$warning test if @ exists and add mailto to open it with default mail composer!}
|
|
BrowserProcess.CommandLine := ABrowser + ' ' + Format(AParams, [AURL]);
|
|
BrowserProcess.Execute;
|
|
finally
|
|
BrowserProcess.Free;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
//if your string is more than 4 char you may have a range error
|
|
function StrToWord(const S: Str4): Word;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := 0;
|
|
I := 1;
|
|
while I <= Length(S) do
|
|
begin
|
|
Result := Result * 10 + Ord(S[I]) - Ord('0');
|
|
Inc(I);
|
|
end;
|
|
end;
|
|
|
|
// Deprecated with Lazarus > 0.9.27
|
|
function DateToStr(Y, M, D: Word): string; deprecated;
|
|
begin
|
|
{$ifdef fpc}
|
|
if Y < TwoDigitDateLimit then
|
|
Inc(Y, 2000);
|
|
if (Y < 100) and (Y > TwoDigitDateLimit) then
|
|
Inc(Y, 1900);
|
|
Result := FormatDateTime(DateFormat, EncodeDate(Y, M, D));
|
|
{$endif}
|
|
{$ifdef delphi} // FormatDateTime seems to be buggy with Delphi 7 at least
|
|
if D < 10 then
|
|
Result := '0' + IntToStr(D) + '/'
|
|
else Result := IntToStr(D) + '/';
|
|
if M < 10 then
|
|
Result := Result + '0' + IntToStr(M) + '/'
|
|
else Result := Result + IntToStr(M) + '/';
|
|
if Y <= 99 then
|
|
if Y < TwoDigitDateLimit then
|
|
Result := Result + '20' + IntToStr(Y)
|
|
else Result := Result + '19' + IntToStr(Y)
|
|
else Result := Result + IntToStr(Y);
|
|
{$endif}
|
|
end;
|
|
|
|
function RemoveFileExt(const aFileName: string): string;
|
|
var
|
|
I: integer;
|
|
begin
|
|
I := Length(aFileName);
|
|
while aFileName[I] <> '.' do
|
|
Dec(I);
|
|
if I > 1 then
|
|
Result := Copy(aFileName, 1, I - 1)
|
|
else Result := aFileName; //on unix this is not an extention but an hidden file
|
|
end;
|
|
|
|
function GetProgramPath: string; //this may work on all platform
|
|
begin
|
|
Result := Trim(ExtractFilePath(Application.ExeName));
|
|
if Result[Length(Result)] <> DirectorySeparator then
|
|
Result := Result + DirectorySeparator;
|
|
end;
|
|
|
|
function GetProgramPrefix: string;
|
|
{this is finding the program prefix dependant on platform...
|
|
On Windows / OS/2 this look like:
|
|
Program Dir
|
|
|- bin\yapetavi.exe
|
|
|- db\
|
|
|- icons\
|
|
|- pics\
|
|
|- i18n\
|
|
That simply means ProgramPrefix = ..\ProgramPath
|
|
On Unix like we have:
|
|
Prefix
|
|
|- bin/yapetavi (executable is in ProgramDir)
|
|
|- share
|
|
|- icons (shared icons)
|
|
|- yapetavi (yapetavi specific data files)
|
|
| |- pics/
|
|
| |- db/
|
|
|- locale
|
|
|- <lang_code>
|
|
|- LC_MESSAGES (where to find langage file)
|
|
This is less complex than we can think... Prefix = ../ProgramPath
|
|
Nevertheless if share/yapetavi is not found we should try standard Unix prefix
|
|
like /usr or /usr/local
|
|
}
|
|
var
|
|
Err
|
|
: integer;
|
|
begin //I don't know if it's really reliable, but that's quitte simple
|
|
{$ifdef WINDOWS}
|
|
Result := GetProgramPath + '..\';
|
|
if (Result[1] = '\') and (Result[2] <> '\') then //in case where the program path is on a server
|
|
Result := '\' + Result;
|
|
{$else}
|
|
{$ifdef wince}
|
|
Result := GetProgramPath + '..\';
|
|
{$else} //many others OS have unix directory structure but this have to be checked
|
|
Result := GetProgramPath + '../';
|
|
Err := FormatFileName(Result, Result);
|
|
if Err <= 0 then
|
|
begin
|
|
PrintLnDbg('Invalid installation prefix, is it dev mode?', vlLow);
|
|
if DirectoryExists('/usr/local/share/yapetavi') then
|
|
Result := '/usr/local/';
|
|
if DirectoryExists('/usr/share/yapetavi') then //overhide last result
|
|
Result := '/usr/';
|
|
end;
|
|
{$endif}
|
|
{$endif}
|
|
PrintLnDbg('Found prefix: ' + Result, vlHigh);
|
|
end;
|
|
|
|
function CountItemsInStr(Str: string; Separator: Char): integer;
|
|
var
|
|
I: integer;
|
|
begin
|
|
Str := Trim(Str);
|
|
if Str = '' then
|
|
begin
|
|
Result := 0;
|
|
Exit;
|
|
end;
|
|
Result := 1;
|
|
for I := 1 to Length(Str) do
|
|
if Str[I] = Separator then
|
|
Inc(Result);
|
|
if Str[Length(Str)] = Separator then
|
|
Dec(Result);
|
|
end;
|
|
|
|
function GetItemInStr(Str: string; Separator: Char; Index: integer): string;
|
|
var
|
|
I, J: integer;
|
|
S: string;
|
|
begin
|
|
//note that result = '' is equivalent to an error
|
|
Result := '';
|
|
S := '';
|
|
Str := Trim(Str);
|
|
if Str = '' then
|
|
Exit;
|
|
I := 1;
|
|
J := 1;
|
|
while (I < Index) and (J < Length(Str)) do
|
|
begin
|
|
if Str[J] = Separator then
|
|
Inc(I);
|
|
Inc(J);
|
|
end;
|
|
if J >= Length(Str) then //in case of a terminal separator
|
|
Exit;
|
|
if Str[J] = Separator then
|
|
Inc(J);
|
|
repeat
|
|
S := S + Str[J];
|
|
Inc(J);
|
|
until (J > Length(Str)) or (Str[J] = Separator);
|
|
Result := Trim(S);
|
|
end;
|
|
|
|
function GetDesktopEnv: TDesktopEnv;
|
|
begin
|
|
Result := deUnknow; //unrecognized desktop environment
|
|
{$IFDEF MSWINDOWS}
|
|
Result := deWindows;
|
|
{$ENDIF}
|
|
{$IFDEF DARWIN}
|
|
Result := deMacOSX;
|
|
{$ENDIF}
|
|
{$IFDEF UNIX}
|
|
//if this Gnome specific env variable is set, probably the user is running Gnome
|
|
if GetEnvironmentVariable('GNOME_DESKTOP_SESSION_ID') <> '' then
|
|
Result := deGnome;
|
|
//if this KDE specific env variable is set, probably the user is running KDE
|
|
if GetEnvironmentVariable('KDE_FULL_SESSION') <> '' then
|
|
Result := deKDE;
|
|
//if gnome or kde is explicitely declared in DESKTOP_SESSION env variable,
|
|
//override previously assumed result
|
|
{$warning Complete with other unixes desktop environment}
|
|
if GetEnvironmentVariable('DESKTOP_SESSION') = 'gnome' then
|
|
Result := deGnome
|
|
else
|
|
if getenvironmentvariable('DESKTOP_SESSION') = 'kde' then
|
|
Result := deKDE
|
|
else
|
|
if GetEnvironmentVariable('DESKTOP_SESSION') = 'xfce' then
|
|
Result := deXFCE
|
|
else
|
|
if GetEnvironmentVariable('DESKTOP_SESSION') = 'lxde' then
|
|
Result := deLXDE;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function GetDesktopPath: string;
|
|
//superseeded in Windows
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
Result := SysUtils.GetEnvironmentVariable('USERPROFILE') + '\Desktop\';
|
|
{$ENDIF}
|
|
{$IFDEF LINUX} //this should work on every unixes but have to be tested
|
|
Result := GetEnvironmentVariable('HOME') + '/Desktop/';
|
|
{$ENDIF}
|
|
{$IFDEF FREEBSD}
|
|
Result := GetEnvironmentVariable('HOME') + '/Desktop/';
|
|
{$ENDIF}
|
|
{$IFDEF NETBSD}
|
|
Result := GetEnvironmentVariable('HOME') + '/Desktop/';
|
|
{$ENDIF}
|
|
//generic, superseeded by system specific values, if not empty
|
|
if Result = '' then Result := GetCurrentDir;
|
|
if Result[Length(Result)] <> DirectorySeparator then
|
|
Result := Result + DirectorySeparator;
|
|
end;
|
|
|
|
function GetHomePath: string;
|
|
//superseeded in Windows
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
Result := SysUtils.GetEnvironmentVariable('USERPROFILE');
|
|
{$ENDIF}
|
|
{$IFDEF LINUX} //this should work on every unixes but have to be tested
|
|
Result := GetEnvironmentVariable('HOME');
|
|
{$ENDIF}
|
|
{$IFDEF FREEBSD}
|
|
Result := GetEnvironmentVariable('HOME');
|
|
{$ENDIF}
|
|
{$IFDEF NETBSD}
|
|
Result := GetEnvironmentVariable('HOME');
|
|
{$ENDIF}
|
|
//generic, superseeded by system specific values, if not empty
|
|
if Result = '' then Result := GetCurrentDir;
|
|
if Result[Length(Result)] <> DirectorySeparator then
|
|
Result := Result + DirectorySeparator;
|
|
end;
|
|
|
|
//return the first occurence, not usefull if you need multiple ones
|
|
function StrExistsInList(const StrList: TStringList; S: string): integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := -1;
|
|
if (StrList = nil) or (not Assigned(StrList)) then
|
|
Exit;
|
|
for I := 0 to StrList.Count - 1 do
|
|
if StrList[I] = S then
|
|
begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
function StrExistsInListBox(const StrList: TListBox; S: string): integer;
|
|
var
|
|
SL: TStringList;
|
|
begin
|
|
SL := TStringList.Create;
|
|
SL.Assign(StrList.Items);
|
|
Result := StrExistsInList(SL, S);
|
|
end;
|
|
|
|
//for both following functions a "repeat [...] until StrXXX(<...>) = -1;"
|
|
//will make it happen on every possible occurence if any
|
|
function StrRemove(var StrList: TStringList; S: string): integer;
|
|
begin
|
|
Result := StrExistsInList(StrList, S);
|
|
StrList.Delete(Result);
|
|
end;
|
|
|
|
function StrChange(var StrList: TStringList; const sOld, sNew: string): integer;
|
|
begin
|
|
Result := StrExistsInList(StrList, sOld);
|
|
StrList[Result] := sNew;
|
|
end;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//in Linux and BSDs if filename contains delimiter ' change the character in ?,
|
|
//and checking special cases for Gnome and KDE in Windows delimiter is " and it's
|
|
//not a valid character in filenames, so this control returns the input string
|
|
//(which doesn't need to be variable) on other systems filenames are not escaped
|
|
//
|
|
//- convert %XX (where XX is an hexa value) to the equivalent char by code
|
|
//- braces management (' sous unix et " sous Windows) as unneeded with Lazarus.
|
|
//- deleting « file:// » if here.
|
|
//- Supporting environnement variables (eg. $(USER)\MonProg).
|
|
//- deleting double directory separators (eg. \\ or // -> \ or /).
|
|
//- automatic separator convertion (eg. with Windows a / would be \)
|
|
//- adds terminal \ or / for directories if not exists.
|
|
//- detect invalid chars (depending on the OS)
|
|
//
|
|
//Returns: Negative when errors, positive when Ok where
|
|
//Low(Result) is pos of the error when applicable and High(Result) the error code
|
|
//
|
|
//List of error codes:
|
|
//-$01XX: error in %XX convertion (mostly non hexa value)
|
|
//-$02XX: "$(" found but no ")" while checking environment (XX pos of "$(")
|
|
//-$0300: directory separator in a filename
|
|
//-$0400: Comma in a filename
|
|
//-$0500: windows only: use of a system reserved file extention
|
|
//-$10XX: empty env var (XX pos of "$(")
|
|
//-$20XX: invalid char where XX is the invalid char code
|
|
//+$0000: valid filename, no such file or directory
|
|
//+$1000: valid filename, directory exists
|
|
//+$2000: valid filename, file exist
|
|
function FormatFileName(S: String; out VS: string): Integer;
|
|
var
|
|
Str, TmpS, EnvVar: UnicodeString;
|
|
I, J: Integer;
|
|
Ch: Char;
|
|
const
|
|
FilePrefix = 'file://';
|
|
|
|
procedure InvalidChar(C: Char);
|
|
begin
|
|
VS := Str;
|
|
Result := $2000 + Ord(C);
|
|
end;
|
|
|
|
begin
|
|
Result := $0000;
|
|
Str := Trim(S);
|
|
// Delete ' on Unix or " on Windows as it is not needed when assuming there's
|
|
// one file path per string
|
|
I := 1;
|
|
repeat
|
|
I := {$ifndef WINDOWS} Pos('''', Str) {$ELSE} Pos('"', Str) {$ENDIF};
|
|
if I > 0 then
|
|
Delete(Str, I, 1);
|
|
until I = 0;
|
|
//find and delete 'file://' (and any part before) if it is passed as part of
|
|
//filename (it happens sometimes in Gnome, i.e. using "open with" context
|
|
//menu entry) or if it has been entered as is by the user
|
|
I := Pos(FilePrefix, Str);
|
|
if I > 0 then
|
|
Str := Copy(Str, I + Length(FilePrefix), Length(Str) - I - Length(FilePrefix) - 1);
|
|
//replace %xx with the appropriate character used sometimes to pass non
|
|
//available keyboards char or spaces. xx must be 2 hexa char
|
|
repeat
|
|
I := Pos('%', Str);
|
|
if I <> 0 then
|
|
try //if there's synthax error like non hexa char
|
|
Ch := Char(StrToWord(Str[I + 1] + Str[I + 2]));
|
|
Delete(Str, I, 3);
|
|
Insert(Ch, Str, I);
|
|
except
|
|
Result := $0100 + I;
|
|
end;
|
|
//if error leave everything else as is as it's not valid path anyway
|
|
until (I = 0) or (Result <> 0);
|
|
//the application support the use of environment variable in filenames just
|
|
//like Unix command line does, so this extract environment variable when $()
|
|
//symbol is found
|
|
repeat
|
|
I := Pos('$(', Str);
|
|
if I <> 0 then
|
|
try
|
|
J := I;
|
|
while Str[J] <> ')' do
|
|
Inc(J);
|
|
//on windows we use Windows unt that have an incompatible GetEnvironmentVariable
|
|
EnvVar := SysUtils.GetEnvironmentVariable(Copy(Str, I + 2, J - I - 2));
|
|
if EnvVar <> '' then
|
|
begin
|
|
Delete(Str, I, I - J);
|
|
Insert(EnvVar, Str, I);
|
|
end else
|
|
Result := $1000 + I;
|
|
except
|
|
Result := $0200 + I;
|
|
end;
|
|
until (I = 0) or (Result <> 0);
|
|
// remove double / on unix or double \\ on windows
|
|
{$warning This is not working when we have network address}
|
|
repeat
|
|
{$ifdef WINDOWS}
|
|
I := Pos('\\', Str);
|
|
{$else}
|
|
I := Pos('//', Str);
|
|
{$endif}
|
|
if I = 1 then
|
|
I := 0;
|
|
if I <> 0 then
|
|
if Str[I - 1] <> ':' then //it's looking like an URL so don't erase it
|
|
Delete(Str, I, 1);
|
|
until I = 0;
|
|
// check for remaining invalid char
|
|
for I := 0 to 31 do
|
|
if (Pos(Char(I), Str) <> 0) then
|
|
InvalidChar(Char(I));
|
|
if Pos('*', Str) <> 0 then
|
|
InvalidChar('*');
|
|
if Pos('?' , Str) <> 0 then
|
|
InvalidChar('?');
|
|
{$IFDEF MSWINDOWS}
|
|
if Pos('"', Str) <> 0 then
|
|
InvalidChar('"');
|
|
{$ELSE}
|
|
if Pos('''', Str) <> 0 then
|
|
InvalidChar('''');
|
|
if Pos(':', Str) <> 0 then
|
|
InvalidChar(':');
|
|
{$ENDIF}
|
|
if Pos('<', Str) <> 0 then
|
|
InvalidChar('<');
|
|
if Pos('>', Str) <> 0 then
|
|
InvalidChar('>');
|
|
if Pos('|', Str) <> 0 then
|
|
InvalidChar('|');
|
|
TmpS := ExtractFileName(Str);
|
|
//reserved characters, filename only (others are checked for the full name)
|
|
if (Pos('\', TmpS) <> 0) or (Pos('/', TmpS) <> 0) then
|
|
Result := $0300; //we should get the pos for low(result)
|
|
if Pos(':', TmpS) <> 0 then
|
|
Result := $0400; //we should get the pos for low(result)
|
|
//reserved filenames (Windows)
|
|
{$IFDEF MSWINDOWS}
|
|
TmpS := UpCase(ExtractFileExt(TmpS));
|
|
if (TmpS = 'CON') or (TmpS = 'PRN') or (TmpS = 'AUX') or (TmpS = 'NUL') or
|
|
(TmpS = 'COM1') or (TmpS = 'COM2') or (TmpS = 'COM3') or (TmpS = 'COM4') or
|
|
(TmpS = 'COM5') or (TmpS = 'COM6') or (TmpS = 'COM7') or (TmpS = 'COM8') or
|
|
(TmpS = 'COM9') or (TmpS = 'LPT1') or (TmpS = 'LPT2') or (TmpS = 'LPT3') or
|
|
(TmpS = 'LPT4') or (TmpS = 'LPT5') or (TmpS = 'LPT6') or (TmpS = 'LPT7') or
|
|
(TmpS = 'LPT8') or (TmpS = 'LPT9') then
|
|
Result := $0500; //pos is not needed as it's the file extention
|
|
{$ENDIF}
|
|
if Result <> $0000 then
|
|
Result := -Result
|
|
else
|
|
if DirectoryExists(Str) then
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
if Str[Length(Str)] <> '\' then
|
|
Str := Str + '\';
|
|
{$ELSE}
|
|
if Str[Length(Str)] <> '/' then
|
|
Str := Str + '/';
|
|
{$ENDIF}
|
|
Result := $1000;
|
|
end else
|
|
if FileExists(Str) then
|
|
Result := $2000;
|
|
VS := Str;
|
|
end;
|
|
|
|
function DirectoryIsEmpty(ADir: string): Boolean;
|
|
var
|
|
SR: SearchRec;
|
|
Mask: string;
|
|
begin
|
|
{$IFDEF MSWINDOWS}
|
|
Mask := '*.*';
|
|
{$ELSE}
|
|
Mask := '*';
|
|
{$ENDIF}
|
|
if ADir[Length(ADir)] <> DirectorySeparator then
|
|
ADir := ADir + DirectorySeparator;
|
|
try
|
|
FindFirst(ADir + Mask, faAnyFile, SR);
|
|
if DosError = 0 then //first is directory itself (./)
|
|
begin
|
|
FindNext(SR);
|
|
if DosError = 0 then //second is parent directory (../)
|
|
begin
|
|
FindNext(SR);
|
|
Result := DosError <> 0;
|
|
end else
|
|
Result := False; //this is a file (or damaged directory)
|
|
end else
|
|
Result := False; //the directory do not exists (or the filesystem is damaged)
|
|
finally
|
|
FindClose(SR) ;
|
|
end;
|
|
end;
|
|
|
|
function RemoveSpecialChar(const S: UnicodeString): string;
|
|
var
|
|
I: Integer;
|
|
Tmp: UnicodeString;
|
|
begin
|
|
Tmp := S;
|
|
for I := 1 to Length(S) do
|
|
if not (S[I] in ['0'..'9', '.', '-', '_', 'A'..'Z', 'a'..'z']) then
|
|
Tmp[I] := '_'
|
|
else
|
|
Tmp[I] := S[I];
|
|
Result := Tmp;
|
|
end;
|
|
|
|
function CreateLockFile(const AName: string): Boolean;
|
|
var
|
|
AFile: Text;
|
|
Test: string;
|
|
begin
|
|
Result := True;
|
|
try
|
|
if FileExists(AName) then
|
|
//use SysUtils and not Windows one when compiling with Windows target
|
|
if not SysUtils.DeleteFile(AName) then
|
|
begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
{$I-}
|
|
Assign(AFile, AName);
|
|
Rewrite(AFile);
|
|
Writeln(AFile, GetUserName + '|' + GetComputerName);
|
|
Close(AFile);
|
|
Reset(AFile);
|
|
Readln(AFile, Test);
|
|
Close(AFile);
|
|
{$I+}
|
|
if Test = '' then
|
|
begin
|
|
ShowMessage('Error 5: unable to write lock file');
|
|
//trigger an error here as there was an error writing file
|
|
Halt(5);
|
|
end;
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
procedure GetLockingInfo(const AName: string; out User, Computer: string);
|
|
var
|
|
AFile: Text;
|
|
I: integer;
|
|
AStr: string;
|
|
begin
|
|
if not FileExists(AName) then
|
|
begin
|
|
User := '';
|
|
Computer := '';
|
|
end else
|
|
begin
|
|
{$I-}
|
|
Assign(AFile, AName);
|
|
Reset(AFile);
|
|
Readln(AFile, AStr);
|
|
Close(AFile);
|
|
{$I+}
|
|
if AStr <> '' then
|
|
begin
|
|
I := Pos('|', AStr);
|
|
if I <> 0 then
|
|
begin
|
|
User := Copy(AStr, 1, I - 1);
|
|
Computer := Copy(AStr, I + 1, Length(AStr) - I);
|
|
end else
|
|
User := AStr; //this is not suposed to happen
|
|
end else
|
|
begin
|
|
ShowMessage('Error 6: the lock file is on readable');
|
|
//trigger an error here as the file was probably unreadable
|
|
Halt(6);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
end.
|
|
|