{ ******************************************************************************** YaPeTaVi - Yet another Periodic Table Viewer Copyright (C) 2009-2012 Geoffray Levasseur . Parts inspired from PeaZip souce code: Copyright (C) 2006 Giorgio Tani 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 |- |- 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.