kolmck/KOL_Linux.inc

544 lines
16 KiB
PHP
Raw Normal View History

{$IFDEF global_declare}
type DWORD = LongWord;
PDWORD = ^DWORD;
PPoint = ^TPoint;
TPoint = packed record
X: Longint;
Y: Longint;
end;
PRect = ^TRect;
TRect = packed record
case Integer of
0: (Left, Top, Right, Bottom: Longint);
1: (TopLeft, BottomRight: TPoint);
end;
const
INVALID_HANDLE_VALUE = Cardinal(-1);
MAX_PATH = 4095; // From /usr/include/linux/limits.h PATH_MAX
const
{ File attribute constants }
FILE_ATTRIBUTE_READONLY = $00000001;
FILE_ATTRIBUTE_HIDDEN = $00000002;
FILE_ATTRIBUTE_SYSTEM = $00000004;
FILE_ATTRIBUTE_VOLUME = $00000008;
FILE_ATTRIBUTE_DIRECTORY = $00000010;
FILE_ATTRIBUTE_ARCHIVE = $00000020;
FILE_ATTRIBUTE_SYMLINK = $00000040;
FILE_ATTRIBUTE_ANYFILE = $0000003F;
FILE_ATTRIBUTE_NORMAL = FILE_ATTRIBUTE_ARCHIVE;
type
TFilename = type string;
PFileTime = ^TFileTime;
TFileTime = __time_t;
PFindFileData = ^TFindFileData;
TFindFileData = packed record
// from TWin32FindData: -------------
dwFileAttributes: DWORD;
ftCreationTime: TFileTime;
ftLastAccessTime: TFileTime;
ftLastWriteTime: TFileTime;
nFileSizeHigh: DWORD;
nFileSizeLow: DWORD;
//dwReserved0: DWORD;
//dwReserved1: DWORD;
cFileName: array[0..MAX_PATH - 1] of Char;
//cAlternateFileName: array[0..13] of KOLChar; - no in Linux
//-------- + handle:
FindHandle: Pointer;
ExcludeAttr: Integer;
Mode: mode_t;
PathOnly: String;
Pattern: String;
end;
const
ExeBaseAddress = Pointer($8048000); // Kylix only?
function GetModuleFileName(Module: HMODULE; Buffer: PChar; BufLen: Integer): Integer;
function DeleteFile(lpFileName: PChar): Boolean;
{$ENDIF global_declare}
{$IFDEF implementation}
//------------------ Unicode strings
function WAnsiUpperCase(const S: WideString): WideString;
var
I: Integer;
P: PWideChar;
begin
SetLength(Result, Length(S));
P := @Result[1];
for I := 1 to Length(S) do
P[I-1] := WideChar(towupper(UCS4Char(S[I])));
end;
function WAnsiLowerCase(const S: WideString): WideString;
var
I: Integer;
P: PWideChar;
begin
SetLength(Result, Length(S));
P := @Result[1];
for I := 1 to Length(S) do
P[I-1] := WideChar(towlower(UCS4Char(S[I])));
end;
//------------------ Ansi strings
function AnsiUpperCase(const S: string): string;
begin
Result := WAnsiUpperCase( S );
end;
function AnsiLowerCase(const S: string): string;
begin
Result := WAnsiLowerCase( S );
end;
function AnsiCompareStr(const S1, S2: string): Integer;
begin
// glibc 2.1.2 / 2.1.3 implementations of strcoll() and strxfrm()
// have severe capacity limits. Comparing two 100k strings may
// exhaust the stack and kill the process.
// Fixed in glibc 2.1.91 and later.
Result := strcoll(PChar(S1), PChar(S2));
end;
function _AnsiCompareStr(S1, S2: PChar): Integer;
begin
// glibc 2.1.2 / 2.1.3 implementations of strcoll() and strxfrm()
// have severe capacity limits. Comparing two 100k strings may
// exhaust the stack and kill the process.
// Fixed in glibc 2.1.91 and later.
Result := strcoll(S1, S2);
end;
function AnsiCompareStrNoCase(const S1, S2: string): Integer;
begin
// glibc 2.1.2 / 2.1.3 implementations of strcoll() and strxfrm()
// have severe capacity limits. Comparing two 100k strings may
// exhaust the stack and kill the process.
// Fixed in glibc 2.1.91 and later.
Result := AnsiCompareStr( AnsiUpperCase( S1 ), AnsiUpperCase( S2 ) );
end;
function _AnsiCompareStrNoCase(S1, S2: PChar): Integer;
begin
// glibc 2.1.2 / 2.1.3 implementations of strcoll() and strxfrm()
// have severe capacity limits. Comparing two 100k strings may
// exhaust the stack and kill the process.
// Fixed in glibc 2.1.91 and later.
Result := AnsiCompareStrNoCase( S1, S2 );
end;
//--------------- File functions
function FileCreate(const FileName: string; OpenFlags: DWord): THandle;
begin
Result := open64( PChar( FileName ), OpenFlags );
end;
function FileClose(Handle: THandle): boolean;
begin
Result := FALSE;
if Handle = INVALID_HANDLE_VALUE then Exit;
__close( Handle );
Result := TRUE;
end;
function FileExists( const FileName : String ) : Boolean;
var st: TStatBuf;
begin
Result := FALSE;
if stat(PChar(FileName), st) = 0 then
Result := st.st_mtime <> -1;
end;
function FileSeek( Handle: THandle; MoveTo: integer; MoveMethod: TMoveMethod): DWord;
var Temp: Int64;
begin
Temp := MoveTo;
Result := lseek64(Handle, Temp, Integer( MoveMethod ));
end;
function FileFarSeek(Handle: THandle; MoveTo: Int64; MoveMethod: TMoveMethod): DWord;
var Temp: Int64;
begin
Temp := MoveTo;
Result := lseek64(Handle, Temp, Integer( MoveMethod ));
end;
function FileRead(Handle: THandle; var Buffer; Count: DWord): DWord;
begin
Result := __read(Handle, Buffer, Count);
end;
function GetFileSize( Handle: THandle; HiSize: PDWORD ): DWORD;
var Pos: Int64;
Len: Int64;
begin
Pos := FileSeek( Handle, 0, spCurrent );
Len := FileSeek( Handle, 0, spEnd );
FileSeek( Handle, Pos, spBegin );
Result := I64( Len ).Lo;
if HiSize <> nil then HiSize^ := I64( Len ).Hi;
end;
function FileWrite( Handle: THandle; const Buffer; Count: DWord): DWord;
begin
Result := __write(Handle, Buffer, Count);
end;
// the only way for a file name to be not complete in Unix:
// it is located in current working directory (CWD) ??????
function FileFullPath( const FileName: String ) : String;
var wd: String;
buffer: array[ 0.._POSIX_PATH_MAX+1 ] of Char;
begin
Result := FileName;
wd := '';
if getwd( buffer ) <> nil then
wd := buffer;
if StrIsStartingFrom( PChar( FileName ), PChar( wd ) ) then Exit;
Result := IncludeTrailingPathDelimiter( wd ) + Filename;
if not FileExists( Result ) then Result := FileName;
end;
function Find_Next(var F: TFindFileData): Boolean;
var
PtrDirEnt: PDirEnt;
Scratch: TDirEnt;
StatBuf: TStatBuf;
LinkStatBuf: TStatBuf;
FName: string;
Attr: Integer;
Mode: mode_t;
Sz: Int64;
begin
Result := FALSE;
PtrDirEnt := nil;
if readdir_r(F.FindHandle, @Scratch, PtrDirEnt) <> 0 then
Exit;
while PtrDirEnt <> nil do
begin
if fnmatch(PChar(F.Pattern), PtrDirEnt.d_name, 0) = 0 then
begin // F.PathOnly must include trailing backslash
FName := F.PathOnly + string(PtrDirEnt.d_name);
if lstat(PChar(FName), StatBuf) = 0 then
begin
Attr := 0;
Mode := StatBuf.st_mode;
if S_ISDIR(Mode) then
Attr := Attr or FILE_ATTRIBUTE_DIRECTORY
else
if not S_ISREG(Mode) then // directories shouldn't be treated as system files
begin
if S_ISLNK(Mode) then
begin
Attr := Attr or FILE_ATTRIBUTE_SYMLINK;
if (stat(PChar(FName), LinkStatBuf) = 0) and
S_ISDIR(LinkStatBuf.st_mode) then
Attr := Attr or FILE_ATTRIBUTE_DIRECTORY
end;
Attr := Attr or FILE_ATTRIBUTE_SYSTEM;
end;
if (PtrDirEnt.d_name[0] = '.') and (PtrDirEnt.d_name[1] <> #0) then
begin
if not ((PtrDirEnt.d_name[1] = '.') and (PtrDirEnt.d_name[2] = #0)) then
Attr := Attr or FILE_ATTRIBUTE_HIDDEN;
end;
if euidaccess(PChar(FName), W_OK) <> 0 then
Attr := Attr or FILE_ATTRIBUTE_READONLY;
if Attr and F.ExcludeAttr = 0 then
begin
Sz := StatBuf.st_size;
F.nFileSizeLow := I64(Sz).Lo;
F.nFileSizeHigh := I64(Sz).Hi;
F.dwFileAttributes := Attr;
F.Mode := StatBuf.st_mode;
StrCopy( F.cFileName, PtrDirEnt.d_name );
F.ftCreationTime := StatBuf.st_mtime;
F.ftLastWriteTime := StatBuf.st_mtime;
F.ftLastAccessTime := StatBuf.st_atime;
Result := TRUE;
Break;
end;
end;
end;
Result := FALSE;
if readdir_r(F.FindHandle, @Scratch, PtrDirEnt) <> 0 then
Break;
end // End of While
end;
procedure Find_Close(var F: TFindFileData);
begin
if F.FindHandle <> nil then
begin
closedir(F.FindHandle);
F.FindHandle := nil;
end;
F.PathOnly := ''; // in Kylix this is not done (memory leak bug?)
F.Pattern := '';
end;
function Find_First( const FilePathName: String; var F: TFindFileData ): Boolean;
begin
FillChar( F, Sizeof( F ), 0 );
F.ExcludeAttr := FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_SYSTEM or
FILE_ATTRIBUTE_VOLUME; // or FILE_ATTRIBUTE_DIRECTORY;
F.PathOnly := ExtractFilePath(FilePathName);
F.Pattern := ExtractFileName(FilePathName);
if F.PathOnly = '' then
F.PathOnly := GetWorkDir;
F.FindHandle := opendir(PChar(F.PathOnly));
if F.FindHandle <> nil then
begin
if not Find_Next(F) then
begin
Find_Close(F);
Result := FALSE;
Exit;
end;
end;
Result:= TRUE;
end;
function FileSize( const Path : String ) : Int64;
var F: TFindFileData;
begin
Result := 0;
if Find_First( Path, F ) then
begin
Result := PInt64( @ F.nFileSizeLow )^;
Find_Close( F );
end;
end;
function FileTimeCompare( const FT1, FT2 : TFileTime ) : Integer;
begin
Result := Sgn( FT1 - FT2 );
end;
function DirectoryExists(const Name: string): Boolean;
var st: TStatBuf;
begin
if stat(PChar(Name), st) = 0 then
Result := S_ISDIR(st.st_mode)
else
Result := False;
end;
function GetWorkDir : string;
var
DirBuf: array[0..MAX_PATH] of Char;
begin
getcwd(DirBuf, sizeof(DirBuf));
Result := string(DirBuf);
end;
//[function GetModuleFileName] // grabbed form Kylix/system.pas
function GetModuleFileName(Module: HMODULE; Buffer: PChar; BufLen: Integer): Integer;
var
Addr: Pointer;
Info: TDLInfo;
FoundInModule: HMODULE;
Temp: Integer;
begin
Result := 0;
if BufLen <= 0 then Exit;
if (Module = MainInstance) or (Module = 0) then
begin
// First, try the dlsym approach.
// dladdr fails to return the name of the main executable
// in glibc prior to 2.1.91
{ Look for a dynamic symbol exported from this program.
_DYNAMIC is not required in a main program file.
If the main program is compiled with Delphi, it will always
have a resource section, named @Sysinit@ResSym.
If the main program is not compiled with Delphi, dlsym
will search the global name space, potentially returning
the address of a symbol in some other shared object library
loaded by the program. To guard against that, we check
that the address of the symbol found is within the
main program address range. }
dlerror; // clear error state; dlsym doesn't
Addr := dlsym(Pointer( Module ), '@Sysinit@ResSym');
if (Addr <> nil) and (dlerror = nil)
and (dladdr(Addr, Info) <> 0)
and (Info.{FileName}dli_fname <> nil)
and (Info.{BaseAddress}dli_fbase = ExeBaseAddress) then
begin
Result := StrLen(Info.{FileName}dli_fname);
if Result >= BufLen then Result := BufLen-1;
// dlinfo may not give a full path. Compare to /proc/self/exe,
// take longest result.
Temp := readlink('/proc/self/exe', Buffer, BufLen);
if Temp >= BufLen then Temp := BufLen-1;
if Temp > Result then
Result := Temp
else
Move(Info.{FileName}dli_fname^, Buffer^, Result);
Buffer[Result] := #0;
Exit;
end;
// Try inspecting the /proc/ virtual file system
// to find the program filename in the process info
Result := readlink('/proc/self/exe', Buffer, BufLen);
if Result <> -1 then
begin
if Result >= BufLen then Result := BufLen-1;
Buffer[Result] := #0;
end;
{$IFDEF AllowParamStrModuleName}
{ Using ParamStr(0) to obtain a module name presents a potential
security hole. Resource modules are loaded based upon the filename
of a given module. We use dlopen() to load resource modules, which
means the .init code of the resource module will be executed.
Normally, resource modules contain no code at all - they're just
carriers of resource data.
An unpriviledged user program could launch our trusted,
priviledged program with a bogus parameter list, tricking us
into loading a module that contains malicious code in its
.init section.
Without this ParamStr(0) section, GetModuleFilename cannot be
misdirected by unpriviledged code (unless the system program loader
or the /proc file system or system root directory has been compromised).
Resource modules are always loaded from the same directory as the
given module. Trusted code (programs, packages, and libraries)
should reside in directories that unpriviledged code cannot alter.
If you need GetModuleFilename to have a chance of working on systems
where glibc < 2.1.91 and /proc is not available, and your
program will not run as a priviledged user (or you don't care),
you can define AllowParamStrModuleNames and rebuild the System unit
and baseCLX package. Note that even with ParamStr(0) support
enabled, GetModuleFilename can still fail to find the name of
a module. C'est la Unix. }
if Result = -1 then // couldn't access the /proc filesystem
begin // return less accurate ParamStr(0)
{ ParamStr(0) returns the name of the link used
to launch the app, not the name of the app itself.
Also, if this app was launched by some other program,
there is no guarantee that the launching program has set
up our environment at all. (example: Apache CGI) }
if (ArgValues = nil) or (ArgValues^ = nil) or
(PCharArray(ArgValues^)[0] = nil) then
begin
Result := 0;
Exit;
end;
Result := _strlen(PCharArray(ArgValues^)[0]);
if Result >= BufLen then Result := BufLen-1;
Move(PCharArray(ArgValues^)[0]^, Buffer^, Result);
Buffer[Result] := #0;
end;
{$ENDIF}
end
else
begin
{ For shared object libraries, we can rely on the dlsym technique.
Look for a dynamic symbol in the requested module.
Don't assume the module was compiled with Delphi.
We look for a dynamic symbol with the name _DYNAMIC. This
exists in all ELF shared object libraries that export
or import symbols; If someone has a shared object library that
contains no imports or exports of any kind, this will probably fail.
If dlsym can't find the requested symbol in the given module, it
will search the global namespace and could return the address
of a symbol from some other module that happens to be loaded
into this process. That would be bad, so we double check
that the module handle of the symbol found matches the
module handle we asked about.}
dlerror; // clear error state; dlsym doesn't
Addr := dlsym(Pointer( Module ), '_DYNAMIC');
if (Addr <> nil) and (dlerror = nil)
and (dladdr(Addr, Info) <> 0) then
begin
if Info.{BaseAddress}dli_fbase = ExeBaseAddress then
Info.{FileName}dli_fname := nil;
FoundInModule := HMODULE(dlopen(Info.{FileName}dli_fname, RTLD_LAZY));
if FoundInModule <> 0 then
dlclose(Pointer( FoundInModule ));
if Module = FoundInModule then
begin
if Assigned(Info.{FileName}dli_fname) then
begin
Result := StrLen(Info.{FileName}dli_fname);
if Result >= BufLen then Result := BufLen-1;
Move(Info.{FileName}dli_fname^, Buffer^, Result);
end
else
Result := 0;
Buffer[Result] := #0;
end;
end;
end;
if Result < 0 then Result := 0;
end;
//[END GetModuleFileName]
function CreateTempFile( const DirPath, Prefix: String ): String;
var i: Integer;
begin
i := 0;
REPEAT
Result := DirPath + Prefix + Int2Str( i );
inc( i );
UNTIL not FileExists( Result );
end;
function DeleteFile(lpFileName: PChar): Boolean;
begin
Result := remove( lpFileName ) = 0;
end;
{--- TTimer ---}
(*)
procedure TTimer.SetEnabled(const Value: Boolean);
begin
if FEnabled = Value then Exit;
fEnabled := Value;
if Value then
begin
fTV.it_interval.tv_sec := fInterval div 1000;
fTV.it_interval.tv_usec := (fInterval mod 1000) * 1000;
setitimer( fTimerKind, )
fHandle := SetTimer( {$IFDEF TIMER_APPLETWND} Applet.GetWindowHandle
{$ELSE} TimerOwnerWnd.GetWindowHandle
{$ENDIF}, Integer( @Self ),
fInterval, @TimerProc );
end
else
begin
if fHandle <> 0 then
begin
KillTimer( TimerOwnerWnd.fHandle, fHandle );
fHandle := 0;
end;
end;
end;
(*)
{$ENDIF implementation}