home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / vp21beta.zip / ARTLSRC.RAR / VPSYSLNX.PAS < prev    next >
Pascal/Delphi Source File  |  2000-08-15  |  94KB  |  3,591 lines

  1. //█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█
  2. //█                                                       █
  3. //█      Virtual Pascal Runtime Library.  Version 2.1.    █
  4. //█      System interface layer Linux                     █
  5. //█      ─────────────────────────────────────────────────█
  6. //█      Copyright (C) 1995-2000 vpascal.com              █
  7. //█      Initial Port to Linux (C) 1999 Jörg Pleumann     █
  8. //█                                                       █
  9. //▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
  10.  
  11. function IntToStr(I: Integer): string;
  12. begin
  13.   Str(I, Result);
  14. end;
  15.  
  16. procedure TrmDone; forward;
  17.  
  18. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ DATE/TIME CONVERSION FUNCTIONS ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  19.  
  20. const
  21.   // The number of seconds in a day.
  22.   SecsPerDay  = 24 * 60 * 60;
  23.  
  24.   // The number of days from (assumed) date 31-Dec-0000 to UTC base
  25.   // day 01-Jan-1970.
  26.   UTCBaseDay  = 719163;
  27.  
  28.   // The number of days that have passed since 01-Jan to the beginning
  29.   // of a given month. Two variants, one for non-leap years, the other
  30.   // for leap years.
  31.   DaysPassed: array[False..True, 1..13] of Integer =
  32.     ((0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365),
  33.      (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366));
  34.  
  35. type
  36.   // A record holding all the fields needed for a date/time
  37.   // conversion.
  38.   TDateTime = record
  39.     Year,Month,Day,DayOfWeek,Hour,Min,Sec: LongInt;
  40.   end;
  41.  
  42. // Packs a TDateTime record to a single UTC date/time value. No
  43. // timezone adjustment is performed.
  44. function PackUTCTime(DateTime: TDateTime): LongInt;
  45. var
  46.   Date, Time: LongInt;
  47. begin
  48.   with DateTime do
  49.   begin
  50.     if Month > 2 then
  51.       Dec(Month, 3)
  52.     else
  53.       begin
  54.         Inc (Month, 9);
  55.         Dec (Year);
  56.       end;
  57.  
  58.     Date := (146097 * (Year div 100)) shr 2
  59.           + (1461 * (Year mod 100)) shr 2
  60.           + (153 * Month + 2) div 5 + Day - 306;
  61.  
  62.     Time := (Hour * 60 + Min) * 60 + Sec;
  63.  
  64.     Result := (Date - UTCBaseDay) * SecsPerDay + Time;
  65.   end;
  66. end;
  67.  
  68. // Unpacks a UTC date/time value to a TDateTime record. No timezone
  69. // adjustment is performed.
  70. function UnpackUTCTime(Value: LongInt): TDateTime;
  71. const
  72.   Days400 = 146097;
  73.   Days100 = 36524;
  74.   Days4   = 1461;
  75. var
  76.   Count, DayNum: LongInt;
  77.   LeapYear: Boolean;
  78. begin
  79.   with Result do
  80.   begin
  81.     DayNum := Value div SecsPerDay + UTCBaseDay;
  82.  
  83.     DayOfWeek := DayNum mod 7;
  84.  
  85.     Year := 1;
  86.  
  87.     while DayNum > Days400 do
  88.     begin
  89.       Inc(Year, 400);
  90.       Dec(DayNum, Days400);
  91.     end;
  92.  
  93.     Count := 0;
  94.     while (DayNum > Days100) and (Count < 3) do
  95.     begin
  96.       Inc(Year, 100);
  97.       Dec(DayNum, Days100);
  98.       Inc(Count);
  99.     end;
  100.  
  101.     while DayNum > Days4 do
  102.     begin
  103.       Inc(Year, 4);
  104.       Dec(DayNum, Days4);
  105.     end;
  106.  
  107.     Count := 0;
  108.     while (DayNum > 365) and (Count < 3) do
  109.     begin
  110.       Inc(Year);
  111.       Dec(DayNum, 365);
  112.       Inc(Count);
  113.     end;
  114.  
  115.     LeapYear := (Year mod 4 = 0) and not (Year mod 100 = 0) or (Year mod 400 = 0);
  116.  
  117.     Month := 0;
  118.     while DaysPassed[LeapYear, Month + 1] < DayNum do Inc(Month);
  119.  
  120.     Day := DayNum - DaysPassed[LeapYear, Month];
  121.  
  122.     Sec := Value mod SecsPerDay;
  123.     Min := Sec div 60;
  124.     Sec := Sec mod 60;
  125.     Hour := Min div 60;
  126.     Min := Min mod 60;
  127.   end;
  128. end;
  129.  
  130. // Packs a TDateTime record to a DOS time value. Taken from the DOS
  131. // unit.
  132. procedure PackDosTime(var T: TDateTime; var P: Longint);
  133. var
  134.   FDateTime: TDateTimeRec absolute P;
  135. begin
  136.   with T,FDateTime do
  137.   begin
  138.     FDate := (Year - 1980) shl 9 + Month shl 5 + Day;
  139.     FTime := Hour shl 11 + Min shl 5 + (Sec div 2);
  140.   end;
  141. end;
  142.  
  143. // Unpacks a DOS time value to a TDateTime record. Taken from the DOS
  144. // unit.
  145. procedure UnpackDosTime(P: Longint; var T: TDateTime);
  146. var
  147.   FDateTime: TDateTimeRec absolute P;
  148. begin
  149.   with T,FDateTime do
  150.   begin
  151.     Year  := (FDate and $FE00) shr 9 + 1980;
  152.     Month := (FDate and $01E0) shr 5;
  153.     Day   := (FDate and $001F);
  154.     Hour  := (FTime and $F800) shr 11;
  155.     Min   := (FTime and $07E0) shr 5;
  156.     Sec   := (FTime and $001F) * 2;
  157.   end;
  158. end;
  159.  
  160. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ FILENAME CONVERSION FUNCTIONS ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  161.  
  162.  
  163. type
  164.   // A buffer for file names. TFileNameBuf
  165.   TFileNameBuf = array[0..511] of Char;
  166.  
  167. // Converts file name given given in Source according to the source
  168. // and destination file systems given in SourceFS and DestFS. The
  169. // result is written to the Dest buffer, and Dest is returned. In
  170. // case no conversion is necessary, the function returns Source
  171. // and the Dest buffer stays unchanged.
  172. function SysConvertFileName(Dest, Source: PChar; DestFS, SourceFS: TFileSystem): PChar;
  173. var
  174.   SourceChar, DestChar: Char;
  175.   P: PChar;
  176. begin
  177.   if DestFS = SourceFS then
  178.     begin
  179.       Result := Source;
  180.       Exit;
  181.     end;
  182.  
  183.   if DestFS = fsUnix then
  184.     begin
  185.       if (Source[0] <> #0) and (Source[1] = ':') then
  186.         Inc(Source, 2);
  187.  
  188.       SourceChar := '\';
  189.       DestChar := '/';
  190.     end
  191.   else
  192.     begin
  193.       SourceChar := '/';
  194.       DestChar := '\';
  195.     end;
  196.  
  197.   StrCopy(Dest, Source);
  198.  
  199.   if SourceFS = fsDosUpper then
  200.     StrUpper(Dest)
  201.   else if SourceFS = fsDosLower then
  202.     StrLower(Dest);
  203.  
  204.   P := StrScan(Dest, SourceChar);
  205.   while P <> nil do
  206.     begin
  207.       P^ := DestChar;
  208.       P := StrScan(P, SourceChar);
  209.     end;
  210.  
  211.   Result := Dest;
  212. end;
  213.  
  214. // Checks whether a file name is valid for the given file system.
  215. function SysIsValidFileName(FileName: PChar; FileSystem: TFileSystem): Boolean;
  216. var
  217.   P: PChar;
  218. begin
  219.   Result := False;
  220.  
  221.   P := FileName;
  222.   while P[0] <> #0 do
  223.     begin
  224.       case P[0] of
  225.         '\', ':': if FileSystem = fsUnix then Exit;
  226.         '/'     : if FileSystem <> fsUnix then Exit;
  227.         'a'..'z': if (FileSystem = fsDosUpper) and (P[1] <> ':') then Exit;
  228.         'A'..'Z': if (FileSystem = fsDosLower) and (P[1] <> ':') then Exit;
  229.       end;
  230.       Inc(P);
  231.     end;
  232.  
  233.   Result := True;
  234. end;
  235.  
  236. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ OTHER HELPER FUNCTIONS ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  237.  
  238. procedure Unimplemented(const S: string);
  239. begin
  240.   WriteLn('Fatal error: Function "', S,'" not implemented yet.');
  241.   Halt(255);
  242. end;
  243.  
  244. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ BASIC FILE FUNCTIONS ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  245.  
  246. // Please refer to the online help for VpSysLow for details
  247.  
  248. function SysFileStdIn: Longint;
  249. begin
  250.   Result := STDIN_FILENO;
  251. end;
  252.  
  253. function SysFileStdOut: Longint;
  254. begin
  255.   Result := STDOUT_FILENO;
  256. end;
  257.  
  258. function SysFileStdErr: Longint;
  259. begin
  260.   Result := STDERR_FILENO;
  261. end;
  262.  
  263. function SysFileOpen_Create(Open: Boolean;FileName: PChar; Mode,Attr,Action: Longint; var Handle: Longint): Longint;
  264. var
  265.   Buffer: TFileNameBuf;
  266.   LnxMode, LnxAttr: Longint;
  267. begin
  268.   FileName := SysConvertFileName(Buffer, FileName, fsUnix, FileSystem);
  269.  
  270.   if Open then
  271.     begin
  272.       case Action of
  273.         Open_FailIfNew:          LnxMode := 0;
  274.         Open_CreateIfNew:        LnxMode := O_CREAT;
  275.         Open_TruncateIfExists:   LnxMode := O_TRUNC;
  276.       end;
  277.     end
  278.   else
  279.     begin
  280.       case Action of
  281.         Create_FailIfExists:     LnxMode := O_CREAT or O_EXCL;
  282.         Create_TruncateIfExists: LnxMode := O_CREAT or O_TRUNC;
  283.       end;
  284.     end;
  285.  
  286.   LnxMode := LnxMode or Mode and O_ACCMODE;
  287.   LnxAttr := S_IRWXU or S_IRWXG or S_IRWXO;
  288.  
  289.   if Attr and 1 = 1 then
  290.     LnxAttr := LnxAttr and not (S_IWUSR or S_IWGRP or S_IWOTH);
  291.  
  292.   Result := LnxOpen(FileName, LnxMode, LnxAttr);
  293.   if Result < 0 then
  294.     Result := -Result
  295.   else
  296.     begin
  297.       Handle := Result;
  298.       Result := 0;
  299.     end;
  300. end;
  301.  
  302. function SysFileOpen(FileName: PChar; Mode: Longint; var Handle: Longint): Longint;
  303. var
  304.   Buffer: TFileNameBuf;
  305.   LnxMode: Longint;
  306. begin
  307.   FileName := SysConvertFileName(Buffer, FileName, fsUnix, FileSystem);
  308.  
  309.   LnxMode := Mode and O_ACCMODE;
  310.  
  311.   Result := LnxOpen(FileName, LnxMode, 0);
  312.   if Result < 0 then
  313.     Result := -Result
  314.   else
  315.     begin
  316.       Handle := Result;
  317.       Result := 0;
  318.     end;
  319. end;
  320.  
  321. function SysFileCreate(FileName: PChar; Mode,Attr: Longint; var Handle: Longint): Longint;
  322. var
  323.   Buffer: TFileNameBuf;
  324.   LnxMode, LnxAttr: Longint;
  325. begin
  326.   FileName := SysConvertFileName(Buffer, FileName, fsUnix, FileSystem);
  327.  
  328.   LnxMode := Mode and O_ACCMODE;
  329.   LnxAttr := S_IRWXU or S_IRWXG or S_IRWXO;
  330.  
  331.   if Attr and 1 = 1 then
  332.     LnxAttr := LnxAttr and not (S_IWUSR or S_IWGRP or S_IWOTH);
  333.  
  334.   Result := LnxCreat(FileName, LnxMode or LnxAttr);
  335.   if Result < 0 then
  336.     Result := -Result
  337.   else
  338.     begin
  339.       Handle := Result;
  340.       Result := 0;
  341.     end;
  342. end;
  343.  
  344. function SysFileCopy(_Old, _New: PChar; _Overwrite: Boolean): Boolean;
  345. var
  346.   Attr, Src, Dst, Error, Actual: Longint;
  347.   Buffer: array[0..1023] of Char;
  348. begin
  349.   Result := False;
  350.  
  351.   SysGetFileAttr(_Old, Attr);
  352.  
  353.   Error := SysFileOpen(_Old, Open_Access_ReadOnly, Src);
  354.   if Error = 0 then
  355.     begin
  356.       if _Overwrite then
  357.         Error := SysFileCreate(_New, Open_Access_ReadWrite, Attr, Dst)
  358.       else
  359.         Error := SysFileOpen_Create(False, _New, Open_Access_ReadWrite, Attr, 0, Dst);
  360.  
  361.       if Error = 0 then
  362.         begin
  363.           Actual := 1;
  364.           while (Error = 0) and (Actual > 0) do
  365.             begin
  366.               Error := SysFileRead(Src, Buffer, SizeOf(Buffer), Actual);
  367.               if Error = 0 then
  368.                 Error := SysFileWrite(Dst, Buffer, Actual, Actual);
  369.             end;
  370.  
  371.           Result := SysFileClose(Dst) = 0;
  372.         end
  373.       else
  374.         SysFileClose(Src);
  375.     end;
  376. end;
  377.  
  378. function SysFileSeek(Handle, Distance, Method: Longint; var Actual: Longint): Longint;
  379. begin
  380.   Result := LnxLSeek(Handle, Distance, Method);
  381.   if Result < 0 then
  382.     Result := -Result
  383.   else
  384.     begin
  385.       Actual := Result;
  386.       Result := 0;
  387.     end;
  388. end;
  389.  
  390. function SysFileRead(Handle: Longint; var Buffer; Count: Longint; var Actual: Longint): Longint;
  391. begin
  392.   Result := LnxRead(Handle, Buffer, Count);
  393.   if Result < 0 then
  394.     Result := -Result
  395.   else
  396.     begin
  397.       Actual := Result;
  398.       Result := 0;
  399.     end;
  400. end;
  401.  
  402. function SysFileWrite(Handle: Longint; const Buffer; Count: Longint; var Actual: Longint): Longint;
  403. begin
  404.   Result := LnxWrite(Handle, Buffer, Count);
  405.   if Result < 0 then
  406.     Result := -Result
  407.   else
  408.     begin
  409.       Actual := Result;
  410.       Result := 0;
  411.     end;
  412. end;
  413.  
  414. function SysFileSetSize(Handle,NewSize: Longint): Longint;
  415. begin
  416.   Result := -LnxFTruncate(Handle, NewSize);
  417.  
  418. end;
  419.  
  420. function SysFileClose(Handle: Longint): Longint;
  421. begin
  422.   Result := -LnxClose(Handle);
  423. end;
  424.  
  425. function SysFileFlushBuffers(Handle: Longint): Longint;
  426. begin
  427.   Result := -LnxFSync(Handle);
  428. end;
  429.  
  430. function SysFileDelete(FileName: PChar): Longint;
  431. var
  432.   Buffer: TFileNameBuf;
  433. begin
  434.   FileName := SysConvertFileName(@Buffer, FileName, fsUnix, FileSystem);
  435.  
  436.   Result := -LnxUnlink(FileName);
  437. end;
  438.  
  439. function SysFileMove(OldName,NewName: PChar): Longint;
  440. var
  441.   OldBuffer, NewBuffer: TFileNameBuf;
  442. begin
  443.   OldName := SysConvertFileName(@OldBuffer, OldName, fsUnix, FileSystem);
  444.   NewName := SysConvertFileName(@NewBuffer, NewName, fsUnix, FileSystem);
  445.  
  446.   Result := -LnxRename(OldName, NewName);
  447. end;
  448.  
  449. function SysFileIsDevice(Handle: Longint): Longint;
  450. var
  451.   Stat: TStat;
  452. begin
  453.   Result := -LnxFStat(Handle, Stat);
  454.     if Result = 0 then
  455.     begin
  456.       if Stat.st_rdev and S_IFCHR <> 0 then
  457.         Result := 1
  458.       else if Stat.st_rdev and S_IFIFO <> 0 then
  459.         Result := 2;
  460.     end;
  461. end;
  462.  
  463. // Retrieve current directory via the proc file system
  464. function GetCwdViaProc(Buffer: PChar): Longint;
  465. begin
  466.   Result := LnxReadLink('/proc/self/cwd', Buffer, SizeOf(TFileNameBuf) - 1);
  467.   if Result > 0 then
  468.     Buffer[Result] := #0;
  469. end;
  470.  
  471. // Retrieve the current directory through FS
  472. function GetCwdViaFS(Path: PChar): Longint;
  473. var
  474.   Root, This, RootDev, ThisDev: Longint;
  475.   Temp, TempDev, Find, FindDev, Handle, Count: LongInt;
  476.   Stat: TStat;
  477.   DirEnt: TDirEnt;
  478.   Name, Buffer: TFileNameBuf;
  479.   MountPoint: Boolean;
  480.   NameBeg: PChar;
  481. begin
  482.   Result := -1;
  483.  
  484.   // Get INode of root directory
  485.   LnxStat('/', Stat);
  486.   Root := Stat.st_Ino;
  487.   RootDev := Stat.st_Dev;
  488.  
  489.   // Get INode of current directory
  490.   LnxStat('.', Stat);
  491.  
  492.   This := Stat.st_Ino;
  493.   ThisDev := Stat.st_Dev;
  494.  
  495.   Find := This;
  496.   FindDev := ThisDev;
  497.  
  498.   // Initialze the buffers
  499.   StrCopy(Path, '');
  500.   StrCopy(@Name, '..');
  501.   StrCopy(@Buffer, '/');
  502.  
  503.   { As long as the current directory is not the root  }
  504.   { directory, we go one directory upwards and search }
  505.   { for an entry whose INode is equal to the one of   }
  506.   { our current directory.                            }
  507.   while (This <> Root) or (ThisDev <> RootDev) do
  508.     begin
  509.       if SysFileOpen(@Name, OPEN_ACCESS_READONLY, Handle) = 0 then
  510.         begin
  511.           // Get stats of parent directory
  512.           LnxFStat(Handle, Stat);
  513.           Temp := Stat.st_Ino;
  514.           TempDev := Stat.st_Dev;
  515.  
  516.           MountPoint := TempDev <> ThisDev;
  517.  
  518.           // Find INode of this directory in parent directory
  519.           while LnxReadDir(Handle, DirEnt, 1) = 1 do
  520.             begin
  521.               if DirEnt.d_Name[0] = '.' then
  522.                 if (DirEnt.d_Name[1] = #0) or ((DirEnt.d_Name[1] = '.') and (DirEnt.d_Name[2] = #0)) then
  523.                   Continue;
  524.  
  525.               if MountPoint or (DirEnt.d_Ino = This) then
  526.                 begin
  527.                   if MountPoint then
  528.                     begin
  529.                       NameBeg := StrECopy(StrECopy(@Buffer[1], @Name), '/');
  530.                       StrCopy(StrECopy(NameBeg, @DirEnt.d_Name), Path);
  531.  
  532.                       if LnxStat(@Buffer, Stat) <> 0 then Continue;
  533.                       if (Stat.st_INo <> Find) or (Stat.st_Dev <> FindDev) then Continue;
  534.  
  535.                       StrCopy(@Buffer[1], NameBeg);
  536.                     end
  537.                   else
  538.                     StrCopy(StrECopy(@Buffer[1], @DirEnt.d_Name), Path);
  539.  
  540.                   StrCopy(Path, @Buffer);
  541.  
  542.                   This := Temp;
  543.                   ThisDev := TempDev;
  544.  
  545.                   Break;
  546.                 end;
  547.               end;
  548.           SysFileClose(Handle);
  549.  
  550.           if ThisDev <> TempDev then
  551.             begin
  552.               // Not found
  553.               StrCopy(Path, '');
  554.               Exit;
  555.             end;
  556.         end
  557.       else
  558.         begin
  559.           // File could not be opened
  560.           StrCopy(Path, '');
  561.           Exit;
  562.         end;
  563.  
  564.       StrCat(@Name, '/..');
  565.     end; // While
  566.  
  567.   if StrLen(Path) = 0 then
  568.     StrCopy(Path, '/');
  569.  
  570.   Result := StrLen(Path);
  571. end;
  572.  
  573. function SysDirGetCurrent(Drive: Longint; Path: PChar): Longint;
  574. var
  575.   Buffer: TFileNameBuf;
  576. begin
  577.   Buffer[0] := 'c';
  578.   Buffer[1] := ':';
  579.  
  580.   if (Drive <> 0) and (Drive <> 3) then
  581.   begin
  582.     Result := -1;
  583.     Exit;
  584.   end;
  585.  
  586.   if GetCwdViaProc(@Buffer[2]) < 1 then
  587.     if GetCwdViaFs(@Buffer[2]) < 1 then
  588.     begin
  589.       Result := -1;
  590.       Exit;
  591.     end;
  592.  
  593.   if FileSystem = fsUnix then
  594.     begin
  595.       if SysConvertFileName(Path, @Buffer[2], FileSystem, fsUnix) <> Path then
  596.         StrCopy(Path, @Buffer[2]);
  597.     end
  598.   else
  599.     begin
  600.       if SysConvertFileName(Path, @Buffer, FileSystem, fsUnix) <> Path then
  601.         StrCopy(Path, @Buffer);
  602.     end;
  603.  
  604.   Result := 0;
  605. end;
  606.  
  607. function SysDirSetCurrent(Path: PChar): Longint;
  608. var
  609.   Buffer: TFileNameBuf;
  610. begin
  611.   Path := SysConvertFileName(@Buffer, Path, fsUnix, FileSystem);
  612.  
  613.   Result := LnxChDir(Path);
  614. end;
  615.  
  616. function SysDirCreate(Path: PChar): Longint;
  617. var
  618.   Buffer: TFileNameBuf;
  619. begin
  620.   Path := SysConvertFileName(@Buffer, Path, fsUnix, FileSystem);
  621.  
  622.   Result := LnxMkDir(Path, S_IRWXU or S_IRWXG or S_IRWXO);
  623. end;
  624.  
  625. function SysDirDelete(Path: PChar): Longint;
  626. var
  627.   Buffer: TFileNameBuf;
  628. begin
  629.   Path := SysConvertFileName(@Buffer, Path, fsUnix, FileSystem);
  630.  
  631.   Result := LnxRmDir(Path);
  632. end;
  633.  
  634. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ MEMORY MANAGEMENT ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  635.  
  636. // Memory management stuff. Since the Linux munmap call needs to
  637. // know the size of the block to be disposed, but Virtual Pascal
  638. // doesn't pass it to the functions (OS/2 and NT don't need this),
  639. // we have to store the size of each kernel-allocated memory block
  640. // in a special list. This is quite some unnecessary overhead in
  641. // memory management, but at least it should work.
  642.  
  643. type
  644.   (**
  645.    * An entry of the memory block list.
  646.   *)
  647.  
  648.   PMemBlock = ^TMemBlock;
  649.   TMemBlock = record
  650.     FAddress: Pointer;
  651.     FSize:    LongInt;
  652.   end;
  653.  
  654.   (*
  655.    * The list of memory blocks.
  656.   *)
  657.  
  658.   PMemBlockList = ^TMemBlockList;
  659.   TMemBlockList = array[0..MaxInt div SizeOf(TMemBlock) - 1] of TMemBlock;
  660.  
  661. var
  662.   (**
  663.    * Points to the list of currently
  664.    * allocated memory blocks.
  665.   *)
  666.  
  667.   MemBlockList:  PMemBlockList = nil;
  668.  
  669.   (**
  670.    * Holds the number of currently
  671.    * allocated memory blocks.
  672.   *)
  673.  
  674.   MemBlockCount: LongInt = 0;
  675.  
  676.   (**
  677.    * Holds the current size of the
  678.    * memory block list.
  679.   *)
  680.  
  681.   MemBlockLimit: LongInt = 0;
  682.  
  683. const
  684.   (**
  685.    * The growth of the memory block
  686.    * list. 512 * 8 bytes is exactly
  687.    * one page.
  688.   *)
  689.  
  690.   MemBlockDelta = 4096 div SizeOf(TMemBlock);
  691.  
  692. (**
  693.  * Adds a block to the list of currrently
  694.  * allocated memory blocks.
  695.   *)
  696.  
  697. procedure SysMemAddBlock(Address: Pointer; Size: LongInt);
  698. var
  699.   TmpList: Pointer;
  700.   TmpLimit: LongInt;
  701. begin
  702.   if MemBlockCount = MemBlockLimit then
  703.   begin
  704.     TmpLimit := MemBlockLimit + MemBlockDelta;
  705.     TmpList := LnxMMap(nil, TmpLimit * SizeOf(TMemBlock), HeapAllocFlags, MAP_ANON or MAP_COPY, 0, 0);
  706.  
  707.     if (LongInt(TmpList) >= -4095) and (LongInt(TmpList) <= 0) then
  708.     begin
  709.       WriteLn('Internal error in SysMemAddBlock: mmap failed.');
  710.       Halt(255);
  711.     end;
  712.  
  713.     if MemBlockLimit <> 0 then
  714.     begin
  715.       Move(MemBlockList^, TmpList^, MemBlockLimit * SizeOf(TMemBlock));
  716.       if LnxMUnmap(MemBlockList, MemBlockLimit * SizeOf(TMemBlock)) <> 0 then
  717.       begin
  718.         WriteLn('Internal error in SysMemAddBlock: munmap failed.');
  719.         Halt(255);
  720.       end;
  721.     end;
  722.  
  723.     MemBlockList := TmpList;
  724.     MemBlockLimit := TmpLimit;
  725.   end;
  726.  
  727.   with MemBlockList^[MemBlockCount] do
  728.   begin
  729.     FAddress := Address;
  730.     FSize := Size;
  731.   end;
  732.  
  733.   // Write('AddBlock(', MemBlockCount, ', ', LongInt(Address), ', ', Size, ')', #10);
  734.  
  735.   Inc(MemBlockCount);
  736. //  Write(MemBlockCount, ' ');
  737. end;
  738.  
  739. (**
  740.  * Deletes a block from the list of currrently
  741.  * allocated memory blocks. Returns its size.
  742.   *)
  743.  
  744. function SysMemDeleteBlock(Address: Pointer): LongInt;
  745. var
  746.   I: LongInt;
  747. begin
  748.   I := MemBlockCount - 1;
  749.  
  750.   while (I <> -1) and (MemBlockList^[I].FAddress <> Address) do
  751.     Dec(I);
  752.  
  753.   if I <> - 1 then
  754.   begin
  755.     Result := MemBlockList^[I].FSize;
  756.     Move(MemBlockList^[I + 1], MemBlockList^[I], (MemBlockCount - I - 1) * SizeOf(TMemBlock));
  757.     Dec(MemBlockCount);
  758.   end
  759.   else
  760.   begin
  761.     WriteLn('Internal error in SysMemDeleteBlock: block ', LongInt(Address), ' not found.');
  762.     Halt(255);
  763.   end;
  764. end;
  765.  
  766. function SysMemAvail: Longint;
  767. var
  768.   Info: TSysInfo;
  769. begin
  770.   LnxSysInfo(Info);
  771.   Result := Info.FreeRam + Info.FreeSwap;
  772. end;
  773.  
  774. function SysMemAlloc(Size,Flags: Longint; var MemPtr: Pointer): Longint;
  775. begin
  776.   Result := LongInt(LnxMMap(nil, Size, Flags, MAP_ANON or MAP_COPY, 0, 0));
  777.   if (Result < -4095) or (Result > 0) then
  778.   begin
  779.     MemPtr := Pointer(Result);
  780.     SysMemAddBlock(MemPtr, Size);
  781.     Result := 0;
  782.   end
  783.   else
  784.   begin
  785.     Result := -Result;
  786.     MemPtr := nil;
  787.   end;
  788. end;
  789.  
  790. function SysMemFree(MemPtr: Pointer): Longint;
  791. begin
  792.   Result := -LnxMUnmap(MemPtr, SysMemDeleteBlock(MemPtr));
  793. end;
  794.  
  795. function SysSysMsCount: Longint;
  796. var
  797.   TimeVal: TTimeVal;
  798.   TimeZone: TTimeZone;
  799. begin
  800.   if LnxGetTimeOfDay(TimeVal, TimeZone) = 0 then
  801.     Result := TimeVal.tv_Sec*1000 + TimeVal.tv_USec div 1000
  802.   else
  803.     Result := 0;
  804. end;
  805.  
  806. procedure SysSysSelToFlat(var P: Pointer);
  807. begin
  808.   // Nothing to do.
  809. end;
  810.  
  811. procedure SysSysFlatToSel(var P: Pointer);
  812. begin
  813.   // Nothing to do.
  814. end;
  815.  
  816. function SysCtrlSelfAppType: Longint;
  817. begin
  818.   // Hardcoded: Text mode
  819.   Result := 2;
  820. end;
  821.  
  822. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ THREAD MANAGEMENT ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  823.  
  824. // Since Linux does not have thread IDs starting from 1, but
  825. // assigns each thread a unique process ID instead, we need
  826. // a mapping between TIDs and PIDs.
  827.  
  828. const
  829.   MaxThread    = 256; // Maximum number of threads
  830.  
  831.   tsRunning    =   0; // Thread is up and running
  832.   tsSuspended  =   1; // Thread has been suspended
  833.   tsTerminated =   2; // Thread has (been) terminated
  834.  
  835. type
  836.   PThreadInfo = ^TThreadInfo;
  837.   TThreadInfo = record    // Thread information structure
  838.     ExceptChain: Pointer; // Head of exception registration chain
  839.     Stack:       Pointer; // Lower limit of stack
  840.     StackLimit:  Pointer; // Upper limit of stack
  841.     Handle:      LongInt; // One-based thread handle
  842.     ThreadPid:   LongInt; // PID of thread itself
  843.     ProcessPid:  LongInt; // PID of process to which thread belongs
  844.     State:       LongInt; // State of thread
  845.     TibSelector: LongInt; // Selector pointing to thread information block
  846.   end;
  847.  
  848. var
  849.   Threads: array[1..MaxThread] of PThreadInfo; // List of threads
  850.  
  851.   MainThread: TThreadInfo = ( // Thread info block for main thread
  852.     ExceptChain: nil;
  853.     Stack:       Pointer($C0000000);
  854.     StackLimit:  Pointer($C0000000);
  855.     Handle:      1;
  856.     ThreadPid:   0;
  857.     ProcessPid:  0;
  858.     State:       tsRunning;
  859.     TibSelector: 0
  860.   );
  861.  
  862.   ThreadSemaphore: LongInt = 0; // Thread info list access semaphore
  863.  
  864. (**
  865.  * Adds a thread, returns the info block or -1, if no more threads can
  866.  * be created.
  867.   *)
  868.  
  869. function AddThreadInfo(StackSize, Flags: LongInt): PThreadInfo;
  870. var
  871.   Index: LongInt;
  872. begin
  873.   Index := 1;
  874.  
  875.   while Index <= MaxThread do
  876.   begin
  877.     if Threads[Index] = nil then
  878.     begin
  879.       New(Result);
  880.       Threads[Index] := Result;
  881.  
  882.       with Result do
  883.       begin
  884.         ExceptChain := nil;
  885.         if StackSize <> 0 then
  886.         begin
  887.           GetMem(Stack, StackSize);
  888.           StackLimit := Stack;
  889.           Inc(LongInt(StackLimit), StackSize);
  890.         end;
  891.         Handle := Index;
  892.         ThreadPid := 0;
  893.         ProcessPid := LnxGetPid;
  894.         State := Flags and 1;
  895.         TibSelector := 0;
  896.       end;
  897.  
  898.       Exit;
  899.     end;
  900.     Inc(Index);
  901.   end;
  902.  
  903.   Result := nil;
  904. end;
  905.  
  906. (**
  907.  * Removes a thread.
  908.   *)
  909.  
  910. procedure RemoveThreadInfo(Thread: PThreadInfo);
  911. var
  912.   Handle: LongInt;
  913. begin
  914.   if Thread.Stack <> nil then FreeMem(Thread.Stack);
  915.   Handle := Thread.Handle;
  916.   Dispose(Threads[Handle]);
  917.   Threads[Handle] := nil;
  918. end;
  919.  
  920. function GetThread(Handle: LongInt): PThreadInfo;
  921. begin
  922.   SysSysWaitSem(ThreadSemaphore);
  923.  
  924.   if (Handle < 1) or (Handle > MaxThread) or (Threads[Handle] = nil) then
  925.     Result := nil
  926.   else
  927.     Result := Threads[Handle];
  928.  
  929.   ThreadSemaphore := 0;
  930. end;
  931.  
  932. // State signal handler
  933. procedure HandleStateSignal(SigNum: LongInt); cdecl; {&Frame-}
  934. asm
  935.           @@LOOP:
  936.             mov     eax, fs:[0].TThreadInfo.State
  937.             cmp     eax, tsRunning
  938.             je      @@RET
  939.             cmp     eax, tsTerminated
  940.             je      SysCtrlExitThread
  941.             mov     eax, esp
  942.             push    0
  943.             call    LnxSigSuspend
  944.             pop     eax
  945.  
  946.           @@RET:
  947. end;
  948.  
  949. // Child signal handler
  950. procedure HandleChildSignal(SigNum: LongInt); cdecl; {&Frame-}
  951. var
  952.   I: LongInt;
  953. begin
  954.   // Make sure all child signals go to the main thread.
  955.   if GetThreadID <> 1 then
  956.     begin
  957.       LnxKill(MainThread.ProcessPid, SIGCHLD);
  958.       Exit;
  959.     end;
  960.  
  961.   // Walk the thread list and remove the blocks
  962.   // of all terminated threads.
  963.   SysSysWaitSem(ThreadSemaphore);
  964.  
  965.   for I := 2 to MaxThread do
  966.     begin
  967.       if Threads[I] <> nil then
  968.         if Threads[I].State = tsTerminated then
  969.           RemoveThreadInfo(Threads[I]);
  970.     end;
  971.  
  972.   ThreadSemaphore := 0;
  973. end;
  974.  
  975. function SysCtrlGetTlsMapMem: Pointer;
  976. begin
  977.   // Implementation using normal memory, for the time being.
  978.   // Shared memory  will have to be used later, in order for DLLs
  979.   // to work with TLS.
  980.   SysMemAlloc(SharedMemSize, $06, Result);
  981.   FillChar(Result^, SharedMemSize, $FF);
  982.   FillChar(Result^, SizeOf(TSharedMem), 0);
  983.   with PSharedMem(Result)^ do
  984.     begin
  985.       // Set up pointers to functions to use when allocating memory
  986.       TlsMemMgr := System.GetPMemoryManager;
  987.       // Set up pointer to function managing the TlsSemaphore
  988.       TlsSemMgr := @SysSysWaitSem;
  989.       // Initialise the TlsSemaphore
  990.       TlsSemaphore := 0;
  991.     end;
  992. end;
  993.  
  994. function SysCtrlKillThread(Handle: Longint): Longint;
  995. var
  996.   Thread: PThreadInfo;
  997. begin
  998.   Thread := GetThread(Handle);
  999.   if Thread <> nil then
  1000.     begin
  1001.       Thread.State := tsTerminated;
  1002.       Result := -LnxKill(Thread.ThreadPid, SIGUSR1);
  1003.     end
  1004.   else
  1005.     Result := ESRCH;
  1006. end;
  1007.  
  1008. function SysCtrlSuspendThread(Handle: Longint): Longint;
  1009. var
  1010.   Thread: PThreadInfo;
  1011. begin
  1012.   Thread := GetThread(Handle);
  1013.   if Thread <> nil then
  1014.     begin
  1015.       Thread.State := tsSuspended;
  1016.       Result := -LnxKill(Thread.ThreadPid, SIGUSR1);
  1017.     end
  1018.   else
  1019.     Result := ESRCH;
  1020. end;
  1021.  
  1022. function SysCtrlResumeThread(Handle: Longint): Longint;
  1023. var
  1024.   Thread: PThreadInfo;
  1025. begin
  1026.   Thread := GetThread(Handle);
  1027.   if Thread <> nil then
  1028.     begin
  1029.       Thread.State := tsRunning;
  1030.       Result := -LnxKill(Thread.ThreadPid, SIGUSR1);
  1031.     end
  1032.   else
  1033.     Result := ESRCH;
  1034. end;
  1035.  
  1036. procedure SysCtrlExitThread(ExitCode: Longint);
  1037. begin
  1038.   asm
  1039.     mov fs:[0].TThreadInfo.State, tsTerminated
  1040.   end;
  1041.  
  1042.   // If the main thread terminates, this is also
  1043.   // the termiantion of the whole process.
  1044.   if GetThreadID = 1 then
  1045.     SysCtrlExitProcess(ExitCode);
  1046.  
  1047.   LnxExit(ExitCode);
  1048. end;
  1049.  
  1050. procedure SysCtrlExitProcess(ExitCode: Longint);
  1051. var
  1052.   I, J: LongInt;
  1053. begin
  1054.   I := GetThreadID;
  1055.  
  1056.   // Kill all threads except the current one.
  1057.   for J := 1 to MaxThread do
  1058.     if (I <> J) and (Threads[J] <> nil) then
  1059.       KillThread(J);
  1060.  
  1061.   TrmDone;
  1062.  
  1063.   LnxExit(ExitCode);
  1064. end;
  1065.  
  1066. // Creates a new selector in the process' local descriptor table
  1067. // and returns it. * If the result is zero, something went wrong.
  1068. function GetNewSelector(Index: LongInt; Address: Pointer; Size: LongInt): Word;
  1069. var
  1070.   LDT: TModifyLDT;
  1071. begin
  1072.   LDT.Index := Index;
  1073.   LDT.Base  := Address;
  1074.   LDT.Limit := Size - 1;
  1075.   LDT.Flags := 64;                           // 64: Segment is usable
  1076.   if LnxModifyLDT(1, LDT, SizeOf(LDT)) = 0 then
  1077.     Result := Index shl 3 or 7               // 7: LDT entry, user priveleges
  1078.   else
  1079.     Result := 0;
  1080. end;
  1081.  
  1082. function SysGetThreadId: Longint;
  1083. asm
  1084.     mov     eax, fs:[0].TThreadInfo.ThreadPid
  1085. end;
  1086.  
  1087. function SysCtrlCreateThread(Attrs: Pointer; StackSize: Longint; Func,Param: Pointer; Flags: Longint; var Tid: Longint): Longint;
  1088. const
  1089.   CloneFlags = CLONE_VM or CLONE_FS or CLONE_FILES or SIGCHLD;
  1090. var
  1091.   Thread: PThreadInfo;
  1092. begin
  1093.   SysSysWaitSem(ThreadSemaphore);
  1094.  
  1095.   // Try to get a new thread handle
  1096.   Thread := AddThreadInfo(StackSize, Flags and 1);
  1097.   if Thread = nil then
  1098.   begin
  1099.     Result := -1;
  1100.     ThreadSemaphore := 0;
  1101.     Exit;
  1102.   end;
  1103.  
  1104.   // Create thread
  1105.   asm
  1106.     mov edx, Thread;
  1107.     mov ecx, [edx].TThreadInfo.StackLimit;
  1108.     mov eax, Param;
  1109.     sub ecx, 4;
  1110.     mov [ecx], eax;
  1111.     mov eax, Func;
  1112.     sub ecx, 4;
  1113.     mov [ecx], eax;
  1114.  
  1115.     mov eax, [edx].TThreadInfo.Handle // LDT entry = thread handle
  1116.     sub ecx, 4;
  1117.     mov DWORD [ecx], eax;
  1118.     sub ecx, 4;
  1119.     mov [ecx], edx;
  1120.     sub ecx, 4;
  1121.     mov DWORD [ecx], TYPE TThreadInfo;
  1122.  
  1123.     // Create the new thread
  1124.     mov eax, 120;
  1125.     mov ebx, CloneFlags;
  1126.     int $80;
  1127.  
  1128.     // Both threads land here. Check whether we deal with
  1129.     // the parent (EAX=new PID) or the child (EAX=0).
  1130.     or eax, eax;
  1131.     jnz @Parent;
  1132.  
  1133.     // Create FS selector for new thread. The arguments
  1134.     // are already on the stack.
  1135.     call GetNewSelector;
  1136.     mov fs, ax;
  1137.     mov fs:[0].TThreadInfo.TibSelector, fs;
  1138.  
  1139.     // Let the thread wait until the parent has
  1140.     // finished the initialization of the thread
  1141.     // control block.
  1142.     push OFFSET ThreadSemaphore
  1143.     call SysSysWaitSem
  1144.     btr ThreadSemaphore, 0
  1145.  
  1146.     // Call handle state signal to hold back
  1147.     // those threads that shall be created in
  1148.     // suspended state. Clean up stack, since
  1149.     // this is a C function.
  1150.     push SIGUSR1;
  1151.     call HandleStateSignal;
  1152.     pop eax;
  1153.  
  1154.     // Call real thread function for child thread.
  1155.     pop eax;
  1156.     call eax;
  1157.  
  1158.     // Terminate child thread. Normally, this should be
  1159.     // done by code from the System unit, but just in case...
  1160.     mov ebx, eax;
  1161.     mov eax, 1;
  1162.     int $80;
  1163.  
  1164.     @Parent:
  1165.  
  1166.     // Store the new PID
  1167.     mov [edx].TThreadInfo.ThreadPid, eax;
  1168.   end;
  1169.  
  1170.   if Thread.ThreadPid < 1 then
  1171.     begin
  1172.       Result := -Thread.ThreadPid;
  1173.       RemoveThreadInfo(Thread);
  1174.       ThreadSemaphore := 0;
  1175.       Exit;
  1176.     end;
  1177.  
  1178.   Tid := Thread.Handle;
  1179.   Result := 0;
  1180.  
  1181.   ThreadSemaphore := 0;
  1182. end;
  1183.  
  1184. function SysCtrlGetModuleName(Handle: Longint; Buffer: PChar): Longint;
  1185. begin
  1186.   Unimplemented('SysCtrlGetModuleName');
  1187. end;
  1188.  
  1189. procedure SysCtrlEnterCritSec;
  1190. begin
  1191.   Unimplemented('SysCtrlEnterCritSec');
  1192. end;
  1193.  
  1194. procedure SysCtrlLeaveCritSec;
  1195. begin
  1196.   Unimplemented('SysCtrlLeaveCritSec');
  1197. end;
  1198.  
  1199. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ ENVIRONMENT ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  1200.  
  1201. type
  1202.   TPCharArray = array[0..1023] of PChar;
  1203.   PPCharArray = ^TPCharArray;
  1204.  
  1205. var
  1206.   Env:  PPCharArray;
  1207.   Argv: PPCharArray;
  1208.   Argc: LongInt;
  1209.  
  1210. function SysCmdln: PChar;
  1211. begin
  1212.   Result := Argv^[0];
  1213. end;
  1214.  
  1215. function SysCmdlnCount: Longint;
  1216. begin
  1217.   Result := Argc - 1;
  1218. end;
  1219.  
  1220. procedure SysCmdlnParam(Index: Longint; var Param: ShortString);
  1221. var
  1222.   Buffer1, Buffer2: TFileNameBuf;
  1223.   P: PChar;
  1224.   L: LongInt;
  1225. begin
  1226.   if (Index < 0) or (Index >= Argc) then
  1227.     Param := ''
  1228.   else
  1229.     if Index = 0 then
  1230.       begin
  1231.         L := LnxReadLink('/proc/self/exe', @Buffer1, SizeOf(Buffer1) - 1);
  1232.         if L > 0 then
  1233.           begin
  1234.             Buffer1[L] := #0;
  1235.             P := @Buffer1;
  1236.           end
  1237.         else
  1238.           P := Argv^[0];
  1239.  
  1240.         Param := StrPas(SysConvertFileName(@Buffer2, P, FileSystem, fsUnix));
  1241.       end
  1242.     else
  1243.       Param := StrPas(Argv^[Index]);
  1244. end;
  1245.  
  1246. function SysGetEnvironment: PChar;
  1247. begin
  1248.   Result := Env^[0];
  1249. end;
  1250.  
  1251. function SysGetEnvString(EnvVar, Default: PChar): PChar;
  1252. var
  1253.   P: PChar;
  1254.   L: Word;
  1255. begin
  1256.   L := StrLen(EnvVar);
  1257.   P := SysGetEnvironment;
  1258.   while P^ <> #0 do
  1259.     begin
  1260.       if (StrLIComp(P, EnvVar, L) = 0) and (P[L] = '=') then
  1261.         begin
  1262.           Result := P + L + 1;
  1263.           Exit;
  1264.         end;
  1265.       Inc(P, StrLen(P) + 1);
  1266.     end;
  1267.   Result := Default;
  1268. end;
  1269.  
  1270. function SysOsVersion: Longint;
  1271. var
  1272.   Handle, Actual, Error, Dot, VerLo, VerHi: LongInt;
  1273.   Buffer: ShortString;
  1274. begin
  1275.   Result := 0;
  1276.  
  1277.   if SysFileOpen('/proc/version', OPEN_ACCESS_READONLY, Handle) = 0 then
  1278.     begin
  1279.       Error := SysFileRead(Handle, Buffer[1], 255, Actual);
  1280.       SysFileClose(Handle);
  1281.  
  1282.       if Error = 0 then
  1283.         begin
  1284.           SetLength(Buffer, Actual);
  1285.  
  1286.           Dot := Pos('version ', Buffer);
  1287.           Delete(Buffer, 1, Dot + 7);
  1288.           Dot := Pos('.', Buffer + '.');
  1289.           Val(Copy(Buffer, 1, Dot - 1), VerHi, Error);
  1290.           Delete(Buffer, 1, Dot);
  1291.           Dot := Pos('.', Buffer + '.');
  1292.           Val(Copy(Buffer, 1, Dot - 1), VerLo, Error);
  1293.           Delete(Buffer, 1, Dot);
  1294.  
  1295.           Result := VerLo shl 8 + VerHi;
  1296.         end;
  1297.     end;
  1298. end;
  1299.  
  1300. function SysPlatformID: Longint;
  1301. begin
  1302.   Result := -3;
  1303. end;
  1304.  
  1305. procedure SysGetDateTime(Year,Month,Day,DayOfWeek,Hour,Minute,Second,MSec: PLongint);
  1306. var
  1307.   TimeVal: TTimeVal;
  1308.   TimeZone: TTimeZone;
  1309.   DateTime: TDateTime;
  1310. begin
  1311.   LnxGetTimeOfDay(TimeVal, TimeZone);
  1312.   DateTime := UnpackUTCTime(TimeVal.tv_Sec - TimeZone.tz_MinutesWest * 60);
  1313.  
  1314.   if Year <> nil then Year^ := DateTime.Year;
  1315.   if Month <> nil then Month^ := DateTime.Month;
  1316.   if Day <> nil then Day^ := DateTime.Day;
  1317.   if DayOfWeek <> nil then DayOfWeek^ := DateTime.DayOfWeek;
  1318.   if Hour <> nil then Hour^ := DateTime.Hour;
  1319.   if Minute <> nil then Minute^ := DateTime.Min;
  1320.   if Second <> nil then Second^ := DateTime.Sec;
  1321.   if MSec <> nil then MSec^ := TimeVal.tv_USec div 1000;
  1322. end;
  1323.  
  1324. procedure SysSetDateTime(Year,Month,Day,Hour,Minute,Second,MSec: PLongint);
  1325. var
  1326.   TimeVal: TTimeVal;
  1327.   TimeZone: TTimeZone;
  1328.   DateTime: TDateTime;
  1329. begin
  1330.   LnxGetTimeOfDay(TimeVal, TimeZone);
  1331.   DateTime := UnpackUTCTime(TimeVal.tv_Sec - TimeZone.tz_MinutesWest * 60);
  1332.  
  1333.   if Year <> nil then DateTime.Year := Year^;
  1334.   if Month <> nil then DateTime.Month := Month^;
  1335.   if Day <> nil then DateTime.Day := Day^;
  1336.   if Hour <> nil then DateTime.Hour := Hour^;
  1337.   if Minute <> nil then DateTime.Min := Minute^;
  1338.   if Second <> nil then DateTime.Sec := Second^;
  1339.   if MSec <> nil then TimeVal.tv_USec := 1000 * MSec^;
  1340.  
  1341.   TimeVal.tv_Sec := PackUTCTime(DateTime);
  1342.   Inc(TimeVal.tv_Sec, TimeZone.tz_MinutesWest * 60);
  1343.  
  1344.   LnxSetTimeOfDay(TimeVal, TimeZone);
  1345. end;
  1346.  
  1347. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ DISK FUNCTIONS ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  1348.  
  1349. const
  1350.   VerifyFlag: Boolean = False;
  1351.  
  1352. function SysVerify(SetValue: Boolean; Value: Boolean): Boolean;
  1353. begin
  1354.   Result := VerifyFlag;
  1355.   if SetValue then
  1356.     VerifyFlag := Value;
  1357. end;
  1358.  
  1359. function SysDiskFreeLong(Drive: Byte): TQuad;
  1360. var
  1361.   Buffer: TStatFS;
  1362. begin
  1363.   if (Drive <> 0) and (Drive <> 3) then
  1364.     begin
  1365.       Result := -1;
  1366.       Exit;
  1367.     end;
  1368.  
  1369.   if LnxStatFS('/', Buffer) = 0 then
  1370.     Result := 1.0 * Buffer.f_BSize * Buffer.f_BAvail
  1371.   else
  1372.     Result := -1;
  1373. end;
  1374.  
  1375. function SysDiskSizeLong(Drive: Byte): TQuad;
  1376. var
  1377.   Buffer: TStatFS;
  1378. begin
  1379.   if (Drive <> 0) and (Drive <> 3) then
  1380.     begin
  1381.       Result := -1;
  1382.       Exit;
  1383.     end;
  1384.  
  1385.   if LnxStatFS('/', Buffer) = 0 then
  1386.     Result := 1.0 * Buffer.f_BSize * Buffer.f_Blocks
  1387.   else
  1388.     Result := -1;
  1389. end;
  1390.  
  1391. function SysGetFileAttr(FileName: PChar; var Attr: Longint): Longint;
  1392. var
  1393.   Stat: TStat;
  1394.   Buffer: TFileNameBuf;
  1395. begin
  1396.   FileName := SysConvertFileName(@Buffer, FileName, fsUnix, FileSystem);
  1397.  
  1398.   Result := -LnxStat(FileName, Stat);
  1399.   Attr := 0;
  1400.   if Stat.st_Mode and S_IFDIR <> 0 then Attr := Attr or $10;
  1401.   if Stat.st_Mode and S_IWUSR = 0 then Attr := Attr or $01;
  1402.   if FileName[0] = '.' then Attr := Attr or $02;
  1403. end;
  1404.  
  1405. function SysSetFileAttr(FileName: PChar; Attr: Longint): Longint;
  1406. var
  1407.   Stat: TStat;
  1408.   Buffer: TFileNameBuf;
  1409. begin
  1410.   FileName := SysConvertFileName(@Buffer, FileName, fsUnix, FileSystem);
  1411.  
  1412.   Result := -LnxStat(FileName, Stat);
  1413.   if Result <> 0 then Exit;
  1414.  
  1415.   if Attr and $10 <> 0 then
  1416.     Stat.st_Mode := Stat.st_Mode or S_IFDIR
  1417.   else
  1418.     Stat.st_Mode := Stat.st_Mode and not S_IFDIR;
  1419.  
  1420.   if Attr and $01 = 0 then
  1421.     Stat.st_Mode := Stat.st_Mode or S_IWUSR
  1422.   else
  1423.     Stat.st_Mode := Stat.st_Mode and not S_IWUSR;
  1424.  
  1425.   Result := -LnxChMod(FileName, Stat.st_Mode)
  1426. end;
  1427.  
  1428. function SysGetFileTime(Handle: Longint; var Time: Longint): Longint;
  1429. var
  1430.   Stat: TStat;
  1431.   TimeVal: TTimeVal;
  1432.   TimeZone: TTimeZone;
  1433.   DateTime: TDateTime;
  1434. begin
  1435.   Result := -LnxFStat(Handle, Stat);
  1436.  
  1437.   LnxGetTimeOfDay(TimeVal, TimeZone);
  1438.   DateTime := UnpackUTCTime(Stat.st_Mtime - TimeZone.tz_MinutesWest * 60);
  1439.   PackDosTime(DateTime, Time);
  1440. end;
  1441.  
  1442. function SysSetFileTime(Handle: Longint; Time: Longint): Longint;
  1443. var
  1444.   Stat: TStat;
  1445.   TimeVal: TTimeVal;
  1446.   TimeZone: TTimeZone;
  1447.   DateTime: TDateTime;
  1448.   Buf: TUTimBuf;
  1449.   FileName: string;
  1450. begin
  1451.   LnxFStat(Handle, Stat);
  1452.   LnxGetTimeOfDay(TimeVal, TimeZone);
  1453.   UnpackDosTime(Time, DateTime);
  1454.   Buf.modtime := PackUTCTime(DateTime);
  1455.   Inc(Buf.modtime, TimeZone.tz_MinutesWest * 60);
  1456.   buf.actime := Stat.st_ATime;
  1457.  
  1458.   Str(Handle, FileName);
  1459.   FileName := '/proc/self/fd/' + FileName + #0;
  1460.  
  1461.   Result := -LnxUTime(@FileName[1], Buf);
  1462. end;
  1463.  
  1464. // Compare a string with a pattern.  The pattern can contain any
  1465. // combination of * and ? characters.  The string can be up to 253
  1466. // characters in length, and the pattern up to 252.
  1467. // The text must not contain ascii 0 characters.
  1468.  
  1469. function MatchStr(Pat, Txt: string): Boolean;
  1470. var
  1471.   SubLen, ComPos, NextStar, SubPos: LongInt;
  1472. begin
  1473.   // First make sure that the pattern doesn't start with *, and always
  1474.   // ends with *.  Change the text accordingly.
  1475.   Pat := #0 + Pat + #0 + '*';
  1476.   Txt := #0 + Txt    + #0;
  1477.  
  1478.   Result := True;
  1479.  
  1480.   while (Pat <> '') and Result do
  1481.     begin
  1482.       // Look for the first *.  At least 1 character before this will be
  1483.       // a normal character, i.e. neither ? nor *
  1484.       NextStar := Pos('*', Pat);
  1485.  
  1486.       SubLen := NextStar - 1;
  1487.  
  1488.       // Ignore double-*
  1489.       while (NextStar < Length(Pat)) and (Pat[NextStar + 1] = '*') do
  1490.         Inc(NextStar);
  1491.  
  1492.       SubPos := 0;
  1493.  
  1494.       repeat
  1495.         Inc(SubPos);
  1496.         Result := True;
  1497.         ComPos := 0;
  1498.         while (ComPos < SubLen) and Result do
  1499.           begin
  1500.             if (Txt[SubPos + ComPos] <> Pat[ComPos + 1]) and
  1501.                (Pat[ComPos + 1] <> '?') then
  1502.               Result := False;
  1503.  
  1504.             Inc(ComPos);
  1505.           end;
  1506.       until (SubPos + SubLen > Length(Txt)) or Result;
  1507.  
  1508.       // When a match is found, cut a piece off the text and continue.
  1509.       if Result then
  1510.         begin
  1511.           Delete(Txt, 1, SubPos + SubLen - 1);
  1512.           Delete(Pat, 1, NextStar);
  1513.         end;
  1514.     end;
  1515. end;
  1516.  
  1517. function DoFindFile(var F: TOSSearchRec): Longint;
  1518. var
  1519.   Buffer: TDirEnt;
  1520.   Stat: TStat;
  1521.   FileName: TFileNameBuf;
  1522.   DateTime: TDateTime;
  1523.   TimeVal: TTimeVal;
  1524.   TimeZone: TTimeZone;
  1525.   Ok: Boolean;
  1526.   I: LongInt;
  1527. begin
  1528.   repeat
  1529.     Result := -LnxReadDir(F.Handle, Buffer, 1);
  1530.  
  1531.     case Result of
  1532.       -1: Result := 0;
  1533.  
  1534.        0: begin
  1535.             Result := 254;
  1536.             Exit;
  1537.           end;
  1538.  
  1539.       else
  1540.         Exit;
  1541.     end;
  1542.  
  1543.     F.Name := StrPas(@Buffer.d_Name);
  1544.     StrCopy(@FileName, @F.FindDir);
  1545.     StrCopy(StrEnd(@FileName), '/');
  1546.     StrCopy(StrEnd(@FileName), @Buffer.d_Name);
  1547.  
  1548.     LnxStat(@FileName, Stat);
  1549.     F.Size := Stat.st_Size;
  1550.     F.Attr := 0;
  1551.  
  1552.     if Stat.st_Mode and S_IFDIR <> 0 then F.Attr := F.Attr or $10;
  1553.     if Stat.st_Mode and S_IWUSR =  0 then F.Attr := F.Attr or $01;
  1554.  
  1555.     LnxGetTimeOfDay(TimeVal, TimeZone);
  1556.     DateTime := UnpackUTCTime(Stat.st_Mtime - TimeZone.tz_MinutesWest * 60);
  1557.     PackDosTime(DateTime, F.Time);
  1558.  
  1559.     Ok := (F.FindAttr and F.Attr = F.Attr)
  1560.       and (MatchStr(F.FindName, F.Name) or (F.FindName = '*') or (F.FindName = '*.*'))
  1561.       and SysIsValidFileName(@Buffer.d_Name, FileSystem);
  1562.  
  1563.   until Ok;
  1564. end;
  1565.  
  1566. function SysFindFirst(Path: PChar; Attr: Longint; var F: TOSSearchRec; IsPChar: Boolean): Longint;
  1567. var
  1568.   P, Q: LongInt;
  1569.   Buffer: TFileNameBuf;
  1570. begin
  1571.   Path := SysConvertFileName(@Buffer, Path, fsUnix, FileSystem);
  1572.  
  1573.   Q := StrLen(Path);
  1574.   P := Q;
  1575.   while (P > -1) and (Path[P] <> '/') do
  1576.     Dec(P);
  1577.  
  1578.   if P <> Q then
  1579.     SetString(F.FindName, @Path[P + 1], Q - P - 1)
  1580.   else
  1581.     F.FindName := '*';
  1582.  
  1583.   if P <> -1 then
  1584.     begin
  1585.       if Path[P] = '/' then Dec(P);
  1586.       Move(Path[0], F.FindDir, P + 1);
  1587.       F.FindDir[P + 1] := #0;
  1588.     end
  1589.   else
  1590.     begin
  1591.       F.FindDir[0] := '.';
  1592.       F.FindDir[1] := #0;
  1593.     end;
  1594.  
  1595.   F.FindAttr := Attr;
  1596.  
  1597.   Result := SysFileOpen(@F.FindDir, OPEN_ACCESS_READONLY, F.Handle);
  1598.   if Result = 0 then
  1599.     Result := DoFindFile(F);
  1600. end;
  1601.  
  1602. function SysFindNext(var F: TOSSearchRec; IsPChar: Boolean): Longint;
  1603. begin
  1604.   Result := DoFindFile(F);
  1605. end;
  1606.  
  1607. function SysFindClose(var F: TOSSearchRec): Longint;
  1608. begin
  1609.   Result := SysFileClose(F.Handle);
  1610. end;
  1611.  
  1612. // Check if file exists; if it does, update FileName parameter
  1613. // to include correct case of existing file
  1614. function SysFileAsOS(FileName: PChar): Boolean;
  1615. var
  1616.   Buffer: TFileNameBuf;
  1617. begin
  1618.   FileName := SysConvertFileName(@Buffer, FileName, fsUnix, FileSystem);
  1619.  
  1620.   Result := (LnxAccess(@FileName, F_OK) = 0);
  1621. end;
  1622.  
  1623. function SysFileSearch(Dest,Name,List: PChar): PChar;
  1624. var
  1625.   I, P, L: Integer;
  1626.   Buffer, NameBuffer, ListBuffer: TFileNameBuf;
  1627. begin
  1628.   Name := SysConvertFileName(@NameBuffer, Name, fsUnix, FileSystem);
  1629.   List := SysConvertFileName(@ListBuffer, List, fsUnix, FileSystem);
  1630.  
  1631.   Result := Dest;
  1632.   StrCopy(Buffer, Name);
  1633.   P := 0;
  1634.   L := StrLen(List);
  1635.   while True do
  1636.     begin
  1637.       if LnxAccess(@Buffer, F_OK) = 0 then
  1638.         begin
  1639.           if SysConvertFileName(@NameBuffer, @Buffer, FileSystem, fsUnix) = @Buffer then
  1640.             begin
  1641.               StrCopy(@NameBuffer, @Buffer);
  1642.               SysFileExpand(Dest, @NameBuffer);
  1643.             end
  1644.           else
  1645.             SysFileExpand(Dest, @NameBuffer);
  1646.  
  1647.           Exit;
  1648.         end;
  1649.  
  1650.       while (P < L) and (List[P] in [':', ';']) do
  1651.         Inc(P);
  1652.       if P >= L then
  1653.         Break;
  1654.       I := P;
  1655.       while (P < L) and not (List[P] in [':', ';']) do
  1656.         Inc(P);
  1657.       StrLCopy(Buffer, List + I, P - I);
  1658.       if not (List[P-1] = '/') then
  1659.         StrLCat(Buffer, '/', 259);
  1660.       StrLCat(Buffer, Name, 259);
  1661.     end;
  1662.   Dest^ := #0;
  1663. end;
  1664.  
  1665. function SysFileExpand(Dest,Name: PChar): PChar;
  1666. begin
  1667.   Result := Dest;
  1668.  
  1669.   if FileSystem <> fsUnix then
  1670.     begin
  1671.       if (Name[0] <> #0) and (Name[1] = ':') then
  1672.         Inc(Name, 2);
  1673.  
  1674.       if Name[0] <> '\' then
  1675.         begin
  1676.           SysDirGetCurrent(0, Dest);
  1677.           if (Dest[0] = '\') and (Dest[1] = #0) then Dest[0] := #0;
  1678.           StrCopy(StrECopy(StrEnd(Dest), '\'), Name);
  1679.         end
  1680.       else
  1681.         begin
  1682.           Dest[0] := 'c';
  1683.           Dest[1] := ':';
  1684.           StrCopy(Dest + 2, Name);
  1685.         end;
  1686.     end
  1687.   else
  1688.     begin
  1689.       if Name[0] <> '/' then
  1690.         begin
  1691.           SysDirGetCurrent(0, Dest);
  1692.           if (Dest[0] = '/') and (Dest[1] = #0) then
  1693.             Dest[0] := #0;
  1694.           StrCopy(StrECopy(StrEnd(Dest), '/'), Name);
  1695.         end
  1696.       else
  1697.         StrCopy(Dest, Name);
  1698.     end;
  1699. end;
  1700.  
  1701. threadvar
  1702.   ExecProcID: LongInt;
  1703.   ExecResult: LongInt;
  1704.   ExecAsync:  Boolean;
  1705.  
  1706. function SysExecute(Path,CmdLine,Env: PChar; Async: Boolean; PID: PLongint; StdIn,StdOut,StdErr: Longint): Longint;
  1707.   procedure MakeArgList(Source: PChar; var Dest: TPCharArray);
  1708.   var
  1709.     I, J, K: LongInt;
  1710.     SQ, DQ: Boolean;
  1711.   begin
  1712.     I := 0;
  1713.     K := 0;
  1714.  
  1715.     SQ := False;
  1716.     DQ := False;
  1717.  
  1718.     while Source[I] <> #0 do
  1719.       begin
  1720.         J := I;
  1721.         while True do
  1722.           begin
  1723.             case Source[J] of
  1724.               '"':  if not SQ then DQ := not DQ;
  1725.               '''': if not DQ then SQ := not SQ;
  1726.               ' ':  if not (SQ or DQ) then Break;
  1727.               #0:   Break;
  1728.             end;
  1729.  
  1730.             Inc(J);
  1731.           end;
  1732.  
  1733.         if J > I then
  1734.           begin
  1735.             Source[J] := #0;
  1736.             Dest[K] := @Source[I];
  1737.             Inc(K);
  1738.           end;
  1739.  
  1740.         I := J;
  1741.         Inc(I);
  1742.       end;
  1743.  
  1744.     Dest[K] := nil;
  1745.   end;
  1746.  
  1747.   procedure MakeEnvList(Source: PChar; var Dest: TPCharArray);
  1748.   var
  1749.     I, J, K: LongInt;
  1750.   begin
  1751.     I := 0;
  1752.     K := 0;
  1753.  
  1754.     while Source[I] <> #0 do
  1755.       begin
  1756.         J := I;
  1757.         while Source[J] <> #0 do Inc(J);
  1758.  
  1759.         if J > I then
  1760.           begin
  1761.             // WriteLn('>', Source + I, '<');
  1762.             Dest[K] := @Source[I];
  1763.             Inc(K);
  1764.           end;
  1765.  
  1766.         I := J;
  1767.         Inc(I);
  1768.       end;
  1769.  
  1770.     Dest[K] := nil;
  1771.   end;
  1772.  
  1773. var
  1774.   Buffer: TFileNameBuf;
  1775.   ArgBuf: array[0..1023] of Char;
  1776.   ArgLst, EnvLst: TPCharArray;
  1777.   P: PChar;
  1778. begin
  1779.   Path := SysConvertFileName(@Buffer, Path, fsUnix, FileSystem);
  1780.  
  1781.   P := StrECopy(@ArgBuf, Path);
  1782.   P^ := #0;
  1783.   Inc(P);
  1784.   StrCopy(P, CmdLine);
  1785.  
  1786.   MakeArgList(ArgBuf, ArgLst);
  1787.  
  1788.   if Env <> nil then
  1789.     MakeEnvList(Env, EnvLst);
  1790.  
  1791.   ExecProcID := 0;
  1792.   ExecProcID := LnxFork;
  1793.   if ExecProcID = 0 then
  1794.     begin
  1795.       // This is what happens in the child process after the fork
  1796.       if Env <> nil then
  1797.         Result := LnxExecve(Path, @ArgLst, @EnvLst)
  1798.       else
  1799.         Result := LnxExecve(Path, @ArgLst, VpSysLow.Env);
  1800.  
  1801.       Halt(254);
  1802.     end
  1803.   else
  1804.     begin
  1805.       // This is what happens in the parent process after the fork
  1806.       if ExecProcID < 0 then
  1807.         begin
  1808.           Result := -ExecProcID;
  1809.           Exit;
  1810.         end;
  1811.  
  1812.       ExecAsync := Async;
  1813.       if PID <> nil then
  1814.         PID^ := ExecProcID;
  1815.       if not Async then
  1816.         LnxWaitPID(ExecProcID, ExecResult, 0);
  1817.     end;
  1818. end;
  1819.  
  1820. function SysExitCode: Longint;
  1821. begin
  1822.   if ExecAsync then
  1823.     LnxWaitPID(ExecProcID, ExecResult, 0);
  1824.   Result := ExecResult;
  1825. end;
  1826.  
  1827. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ STRING HANDLING ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  1828.  
  1829. type
  1830.   TCharCaseTable = array[0..255] of Char;
  1831. var
  1832.   UpperCaseTable: TCharCaseTable;
  1833.   LowerCaseTable: TCharCaseTable;
  1834.   AnsiUpperCaseTable: TCharCaseTable;
  1835.   AnsiLowerCaseTable: TCharCaseTable;
  1836.   WeightTable: TCharCaseTable;
  1837.  
  1838. const
  1839.   CaseTablesInitialized: Boolean = False;
  1840.  
  1841. procedure InitCaseTables;
  1842. var
  1843.   I,J: Integer;
  1844. begin
  1845.   for I := 0 to 255 do
  1846.     begin
  1847.       UpperCaseTable[I] := Chr(I);
  1848.       LowerCaseTable[I] := Chr(I);
  1849.       AnsiUpperCaseTable[I] := Chr(I);
  1850.       AnsiLowerCaseTable[I] := Chr(I);
  1851.       if I in [Ord('A')..Ord('Z')] then
  1852.         LowerCaseTable[I] := Chr(I + (Ord('a')-Ord('A')));
  1853.       if I in [Ord('a')..Ord('z')] then
  1854.         UpperCaseTable[I] := Chr(I - (Ord('a')-Ord('A')));
  1855.     end;
  1856.   SysGetCaseMap(SizeOf(AnsiUpperCaseTable), AnsiUpperCaseTable);
  1857.   for I := 0 to 255 do
  1858.     begin
  1859.       J := Ord(AnsiUpperCaseTable[I]);
  1860.       if (J <> I) {and (AnsiLowerCaseTable[J] <> chr(J))} then
  1861.         AnsiLowerCaseTable[J] := Chr(I);
  1862.     end;
  1863.   SysGetWeightTable(SizeOf(WeightTable), WeightTable);
  1864.   CaseTablesInitialized := True;
  1865. end;
  1866.  
  1867. procedure ConvertCase(S1,S2: PChar; Count: Integer; var Table: TCharCaseTable); {&USES esi,edi} {&FRAME-}
  1868. asm
  1869.                 cmp     CaseTablesInitialized,0
  1870.                 jne     @@1
  1871.                 Call    InitCaseTables
  1872.               @@1:
  1873.                 xor     eax,eax
  1874.                 mov     esi,S1
  1875.                 mov     edi,S2
  1876.                 mov     ecx,Count
  1877.                 mov     edx,Table
  1878.                 jecxz   @@3
  1879.               @@2:
  1880.                 dec     ecx
  1881.                 mov     al,[esi+ecx]
  1882.                 mov     al,[edx+eax]
  1883.                 mov     [edi+ecx],al
  1884.                 jnz     @@2
  1885.               @@3:
  1886. end;
  1887.  
  1888. procedure SysChangeCase(Source, Dest: PChar; Len: Longint; NewCase: TCharCase);
  1889. begin
  1890.   case NewCase of
  1891.     ccLower:     ConvertCase(Source, Dest, Len, LowerCaseTable);
  1892.     ccUpper:     ConvertCase(Source, Dest, Len, UpperCaseTable);
  1893.     ccAnsiLower: ConvertCase(Source, Dest, Len, AnsiLowerCaseTable);
  1894.     ccAnsiUpper: ConvertCase(Source, Dest, Len, AnsiUpperCaseTable);
  1895.   end;
  1896. end;
  1897.  
  1898. function SysLowerCase(s: PChar): PChar;
  1899. begin
  1900.   ConvertCase(s, s, strlen(s), AnsiLowerCaseTable);
  1901.   Result := s;
  1902. end;
  1903.  
  1904. function SysUpperCase(s: PChar): PChar;
  1905. begin
  1906.   ConvertCase(s, s, strlen(s), AnsiUpperCaseTable);
  1907.   Result := s;
  1908. end;
  1909.  
  1910. function MemComp(P1,P2: Pointer; L1,L2: Integer; T1,T2: PChar): Integer; {&USES ebx,esi,edi,ebp} {&FRAME-}
  1911. asm
  1912.                 cmp     CaseTablesInitialized,0
  1913.                 jne     @@0
  1914.                 Call    InitCaseTables
  1915.               @@0:
  1916.                 mov     ecx,L1
  1917.                 mov     eax,L2
  1918.                 mov     esi,P1
  1919.                 mov     edi,P2
  1920.                 cmp     ecx,eax
  1921.                 jbe     @@1
  1922.                 mov     ecx,eax
  1923.               @@1:
  1924.                 mov     ebx,T1
  1925.                 mov     ebp,T2
  1926.                 xor     eax,eax
  1927.                 xor     edx,edx
  1928.                 test    ecx,ecx
  1929.                 jz      @@5
  1930.               @@2:
  1931.                 mov     al,[esi]
  1932.                 mov     dl,[edi]
  1933.                 inc     esi
  1934.                 inc     edi
  1935.                 test    ebp,ebp
  1936.                 mov     al,[ebx+eax]    // Table1
  1937.                 mov     dl,[ebx+edx]
  1938.                 jz      @@3
  1939.                 mov     al,[ebp+eax]    // Table2
  1940.                 mov     dl,[ebp+edx]
  1941.               @@3:
  1942.                 cmp     al,dl
  1943.                 jne     @@RET
  1944.                 dec     ecx
  1945.                 jnz     @@2
  1946.               @@5:
  1947.                 mov     eax,L1
  1948.                 mov     edx,L2
  1949.               @@RET:
  1950.                 sub     eax,edx
  1951. end;
  1952.  
  1953. function SysCompareStrings(s1, s2: PChar; l1, l2: Longint; IgnoreCase: Boolean): Longint;
  1954. begin
  1955.   if IgnoreCase then
  1956.     Result := MemComp(s1, s2, l1, l2, @WeightTable, nil)
  1957.   else
  1958.     Result := MemComp(s1, s2, l1, l2, @AnsiUpperCaseTable, @WeightTable);
  1959. end;
  1960.  
  1961. procedure SysGetCaseMap(TblLen: Longint; Tbl: PChar );
  1962. var
  1963.   I: LongInt;
  1964. begin
  1965.   for I := 0 to TblLen - 1 do
  1966.     Tbl[I] := UpCase(Tbl[I]);
  1967. end;
  1968.  
  1969. procedure SysGetWeightTable(TblLen: Longint; WeightTable: PChar);
  1970. var
  1971.   I: LongInt;
  1972. begin
  1973.   for I := 0 to TblLen - 1 do
  1974.     WeightTable[I] := Chr(I);
  1975. end;
  1976.  
  1977. function SysGetCodePage: Longint;
  1978. begin
  1979.   Result := 1004; // ISO-Latin-1
  1980. end;
  1981.  
  1982. procedure SysCtrlSetCBreakHandler;
  1983. begin
  1984.   // Unimplemented('SysCtrlSetCBreakHandler');
  1985. end;
  1986.  
  1987. function SysFileIncHandleCount(Count: Longint): Longint;
  1988. begin
  1989.   Result := 0;
  1990. end;
  1991.  
  1992. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ SCREEN AND KEYBOARD ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  1993.  
  1994. var
  1995.   // Terminal in/out handle
  1996.   TrmHandle: LongInt = -1;
  1997.  
  1998.   // Saved terminal attributes
  1999.   TrmSaveAttr: TTermios;
  2000.  
  2001. function TrmInit: string;
  2002. var
  2003.   Attr: TTermios;
  2004.   Ctrl: string;
  2005. begin
  2006.   // Get terminal name
  2007.   Result := StrPas(SysGetEnvString('TERM', 'unknown'));
  2008.  
  2009.   // If already initialized, return immediately
  2010.   if TrmHandle <> -1 then Exit;
  2011.  
  2012.   // Open device
  2013.   TrmHandle := LnxOpen('/dev/tty', O_RDWR, 0);
  2014.  
  2015.   // Get terminal information and store it
  2016.   LnxIoCtl(TrmHandle, TCGETS, @Attr);
  2017.   TrmSaveAttr := Attr;
  2018.  
  2019.   // Change some flags
  2020.   with Attr do
  2021.   begin
  2022.     c_lflag := c_lflag and not (ECHO  or ICANON or IEXTEN);
  2023.     c_iflag := c_iflag and not (INPCK or ISTRIP or IXON);
  2024.     c_cflag := c_cflag and not (CSIZE or PARENB);
  2025.     c_cflag := c_cflag or CS8;
  2026.     c_oflag := c_oflag and not (OPOST);
  2027.     c_cc[VMIN]  := 1;
  2028.     c_cc[VTIME] := 0;
  2029.   end;
  2030.  
  2031.   // Activate the new terminal settings
  2032.   LnxIoCtl(TrmHandle, TCSETS, @Attr);
  2033.  
  2034.   // Enter XMIT mode
  2035.   Ctrl := #27'[?1h'; // #27'='; // #27'[?7l';
  2036.   LnxWrite(TrmHandle, Ctrl[1], Length(Ctrl));
  2037. end;
  2038.  
  2039. procedure TrmDone;
  2040. var
  2041.   Ctrl: string;
  2042. begin
  2043.   // Reset old terminal settings
  2044.   if TrmHandle <> -1 then
  2045.     begin
  2046.       LnxIoCtl(TrmHandle, TCSETS, @TrmSaveAttr);
  2047.  
  2048.       // Enter LOCAL mode
  2049.       Ctrl := #27'[?1l'; // #27'>'; // #27'[?7h';
  2050.       LnxWrite(TrmHandle, Ctrl[1], Length(Ctrl));
  2051.  
  2052.       // Reset all attributes, activate normal character set
  2053.       Ctrl := #27'[0m'#27'(B';
  2054.       LnxWrite(TrmHandle, Ctrl[1], Length(Ctrl));
  2055.  
  2056.       // Free terminal handle
  2057.       LnxClose(TrmHandle);
  2058.       TrmHandle := -1;
  2059.     end;
  2060. end;
  2061.  
  2062. function TrmRead(var Buffer; Count: Integer): Integer;
  2063. begin
  2064.   Result := LnxRead(TrmHandle, Buffer, Count);
  2065. end;
  2066.  
  2067. function TrmWrite(const Buffer; Count: Integer): Integer;
  2068. begin
  2069.   Result := LnxWrite(TrmHandle, Buffer, Count);
  2070. end;
  2071.  
  2072. const
  2073.   { Video modes }
  2074.   MON1          = $FE;          { Monochrome, ASCII chars only }
  2075.   MON2          = $FD;          { Monochrome, graphics chars   }
  2076.   COL1          = $FC;          { Color, ASCII chars only      }
  2077.   COL2          = $FB;          { Color, graphics chars        }
  2078.  
  2079. type
  2080.   // A single cell on the screen
  2081.   TScrCell = record
  2082.     Chr: Char; // Character
  2083.     Att: Byte; // Attribute
  2084.   end;
  2085.  
  2086.   // A buffer for the whole screen
  2087.   TScrBuffer = array[0..8191] of TScrCell;
  2088.   PScrBuffer = ^TScrBuffer;
  2089.  
  2090. var
  2091.   // Current screen mode
  2092.   ScrMode: Integer;
  2093.  
  2094.   // Screen buffer
  2095.   ScrBuffer: PScrBuffer;
  2096.  
  2097.   // Screen size and coordinates
  2098.   ScrWidth, ScrHeight, ScrColors, ScrSize, ScrRow, ScrColumn: Integer;
  2099.  
  2100.   // True if Cursor is visible
  2101.   ScrCursor: Boolean = True;
  2102.  
  2103.   // Color table
  2104.   ScrPalette: array[0..7] of Byte;
  2105.  
  2106.   // Graphics character table
  2107.   ScrGraphs: array[#00..#31] of Char;
  2108.  
  2109. const
  2110.   // --- Table for mapping 'ESC <0..9>' to scancodes --------------------
  2111.   KbdScanCtlNum: array['0'..'9'] of SmallWord =
  2112.   // 0      1      2      3      4      5      6      7      8      9
  2113.     ($8100, $7800, $7900, $7A00, $7B00, $7C00, $7D00, $7E00, $7F00, $8000);
  2114.  
  2115.   // --- Table for mapping 'ESC <0..9>' to scancodes --------------------
  2116.   KbdScanAltNum: array['0'..'9'] of SmallWord =
  2117.   // 0      1      2      3      4      5      6      7      8      9
  2118.     ($8100, $7800, $7900, $7A00, $7B00, $7C00, $7D00, $7E00, $7F00, $8000);
  2119.  
  2120.   // --- Table for mapping 'ESC <A..Z>' to scancodes --------------------
  2121.   KbdScanAltChr: array['A'..'Z'] of SmallWord =
  2122.   // A      B      C      D      E      F      G      H      I      J
  2123.     ($1E00, $3000, $2E00, $2000, $1200, $2100, $2200, $2300, $1700, $2400,
  2124.   // K      L      M      N      O      P      Q      R      S      T
  2125.      $2500, $2600, $3200, $3100, $1800, $1900, $1000, $1300, $1F00, $1400,
  2126.   // U      V      W      X      Y      Z
  2127.      $1600, $2F00, $1100, $2D00, $1500, $2C00);
  2128.  
  2129.   // --- Table for mapping 'ESC O <A..Z>' to scancodes ------------------
  2130.   KbdScanNrmFn1: array['A'..'Z'] of SmallWord =
  2131.   // UP     DOWN   RIGHT  LEFT   -----  END    -----  HOME   -----  -----
  2132.     ($4800, $5000, $4D00, $4B00, $0000, $4F00, $0000, $4700, $0000, $0000,
  2133.   // -----  -----  ENTER  -----  -----  F1     F2     F3     F4     -----
  2134.      $0000, $0000, $1C0D, $0000, $0000, $3B00, $3C00, $3D00, $3E00, $0000,
  2135.   // -----  -----  -----  -----  -----  -----
  2136.      $0000, $0000, $0000, $0000, $0000, $0000);
  2137.  
  2138.   KbdScanSftFn1: array['A'..'Z'] of SmallWord =
  2139.   // UP     DOWN   RIGHT  LEFT   -----  END    -----  HOME   -----  -----
  2140.     ($4800, $5000, $4D00, $4B00, $0000, $4F00, $0000, $4700, $0000, $0000,
  2141.   // -----  -----  ENTER  -----  -----  F1     F2     F3     F4     -----
  2142.      $0000, $0000, $1C0D, $0000, $0000, $5400, $5500, $5600, $5700, $0000,
  2143.   // -----  -----  -----  -----  -----  -----
  2144.      $0000, $0000, $0000, $0000, $0000, $0000);
  2145.  
  2146.   KbdScanCtlFn1: array['A'..'Z'] of SmallWord =
  2147.   // UP     DOWN   RIGHT  LEFT   -----  END    -----  HOME   -----  -----
  2148.     ($8D00, $9100, $7400, $7300, $0000, $7500, $0000, $7700, $0000, $0000,
  2149.   // -----  -----  ENTER  -----  -----  F1     F2     F3     F4     -----
  2150.      $0000, $0000, $1C0A, $0000, $0000, $5E00, $5F00, $6000, $6100, $0000,
  2151.   // -----  -----  -----  -----  -----  -----
  2152.      $0000, $0000, $0000, $0000, $0000, $0000);
  2153.  
  2154.   // --- Table for mapping 'ESC ESC O <A..Z>' to scancodes --------------
  2155.   KbdScanAltFn1: array['A'..'Z'] of SmallWord =
  2156.   // UP     DOWN   RIGHT  LEFT   -----  END    -----  HOME   -----  -----
  2157.     ($9800, $A000, $9D00, $9B00, $0000, $9F00, $0000, $9700, $0000, $0000,
  2158.   // -----  -----  ENTER  -----  -----  F1     F2     F3     F4     -----
  2159.      $0000, $0000, $1C00, $0000, $0000, $6800, $6900, $6A00, $6B00, $0000,
  2160.   // -----  -----  -----  -----  -----  -----
  2161.      $0000, $0000, $0000, $0000, $0000, $0000);
  2162.  
  2163.   // --- Table for mapping 'ESC O <a..z>' to scancodes ------------------
  2164.   KbdScanNrmFn2: array['a'..'z'] of SmallWord =
  2165.   // -----  -----  -----  -----  -----  -----  -----  -----  -----  -----
  2166.     ($0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000,
  2167.   // -----  -----  -----  -----  -----  -----  -----  -----  -----  F5
  2168.      $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $3F00,
  2169.   // F6     F7     F8     F9     F10    -----
  2170.      $4000, $4100, $4200, $4300, $4400, $0000);
  2171.  
  2172.   // --- Table for mapping 'ESC O <a..z>' to scancodes ------------------
  2173.   KbdScanSftFn2: array['a'..'z'] of SmallWord =
  2174.   // -----  -----  -----  -----  -----  -----  -----  -----  -----  -----
  2175.     ($0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000,
  2176.   // -----  -----  -----  -----  -----  -----  -----  -----  -----  F5
  2177.      $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $5800,
  2178.   // F6     F7     F8     F9     F10    -----
  2179.      $5900, $5A00, $5B00, $5C00, $5D00, $0000);
  2180.  
  2181.   // --- Table for mapping 'ESC O <a..z>' to scancodes ------------------
  2182.   KbdScanCtlFn2: array['a'..'z'] of SmallWord =
  2183.   // -----  -----  -----  -----  -----  -----  -----  -----  -----  -----
  2184.     ($0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000,
  2185.   // -----  -----  -----  -----  -----  -----  -----  -----  -----  F5
  2186.      $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $6200,
  2187.   // F6     F7     F8     F9     F10    -----
  2188.      $6300, $6400, $6500, $6600, $6700, $0000);
  2189.  
  2190.   // --- Table for mapping 'ESC ESC O <a..z>' to scancodes --------------
  2191.   KbdScanAltFn2: array['a'..'z'] of SmallWord =
  2192.   // -----  -----  -----  -----  -----  -----  -----  -----  -----  -----
  2193.     ($0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000,
  2194.   // -----  -----  -----  -----  -----  -----  -----  -----  -----  F5
  2195.      $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000, $6C00,
  2196.   // F6     F7     F8     F9     F10    -----
  2197.      $6D00, $6E00, $6F00, $7000, $7100, $0000);
  2198.  
  2199.   // --- Table for mapping 'ESC [ <1..26> ~' to scancodes ---------------
  2200.   KbdScanNrmFn3: array[1..26] of SmallWord =
  2201.   // HOME   INS    DEL    END    PGUP   PGDN   -----  -----  -----  -----
  2202.     ($4700, $5200, $5300, $4F00, $4900, $5100, $0000, $0000, $0000, $0000,
  2203.   // F1     F2     F3     F4     F5     -----  F6     F7     F8     F9
  2204.      $3B00, $3C00, $3D00, $3E00, $3F00, $0000, $4000, $4100, $4200, $4300,
  2205.   // F10    -----  F11    F12    -----  -----
  2206.      $4400, $0000, $8500, $8600, $0000, $0000);
  2207.  
  2208.   // --- Table for mapping 'ESC [ <1..26> ~' to scancodes ---------------
  2209.   KbdScanSftFn3: array[1..26] of SmallWord =
  2210.   // HOME   INS    DEL    END    PGUP   PGDN   -----  -----  -----  -----
  2211.     ($4700, $0500, $0700, $4F00, $4900, $5100, $0000, $0000, $0000, $0000,
  2212.   // F1     F2     F3     F4     F5     -----  F6     F7     F8     F9
  2213.      $5400, $5500, $5600, $5700, $5800, $0000, $5900, $5A00, $5B00, $5C00,
  2214.   // F5D    -----  F11    F12    -----  -----
  2215.      $4400, $0000, $8700, $8800, $0000, $0000);
  2216.  
  2217.   // --- Table for mapping 'ESC [ <1..26> ~' to scancodes ---------------
  2218.   KbdScanCtlFn3: array[1..26] of SmallWord =
  2219.   // HOME   INS    DEL    END    PGUP   PGDN   -----  -----  -----  -----
  2220.     ($7700, $0400, $0600, $7500, $8400, $7600, $0000, $0000, $0000, $0000,
  2221.   // F1     F2     F3     F4     F5     -----  F6     F7     F8     F9
  2222.      $5E00, $5F00, $6000, $6100, $6200, $0000, $6300, $6500, $6600, $6700,
  2223.   // F10    -----  F11    F12    -----  -----
  2224.      $6800, $0000, $8900, $9000, $0000, $0000);
  2225.  
  2226.   // --- Table for mapping 'ESC ESC [ <1..26> ~' to scancodes -----------
  2227.   KbdScanAltFn3: array[1..26] of SmallWord =
  2228.   // HOME   INS    DEL    END    PGUP   PGDN   -----  -----  -----  -----
  2229.     ($9700, $A200, $A300, $9F00, $9900, $A100, $0000, $0000, $0000, $0000,
  2230.   // F1     F2     F3     F4     F5     -----  F6     F7     F8     F9
  2231.      $6800, $6900, $6A00, $6B00, $6C00, $0000, $6D00, $6E00, $6F00, $7000,
  2232.   // F10    -----  F11    F12    -----  -----
  2233.      $7100, $0000, $8B00, $8C00, $0000, $0000);
  2234.  
  2235.   // --- Table for mapping 'ESC [ [ <A..E>' to scancodes ----------------
  2236.   KbdScanNrmFn5: array['A'..'E'] of SmallWord =
  2237.   // F1     F2     F3     F4     F5
  2238.     ($3B00, $3C00, $3D00, $3E00, $3F00);
  2239.  
  2240.   // --- Table for mapping 'ESC [ [ <A..E>' to scancodes ----------------
  2241.   KbdScanSftFn5: array['A'..'E'] of SmallWord =
  2242.   // F1     F2     F3     F4     F5
  2243.     ($5400, $5500, $5600, $5700, $5800);
  2244.  
  2245.   // --- Table for mapping 'ESC [ [ <A..E>' to scancodes ----------------
  2246.   KbdScanCtlFn5: array['A'..'E'] of SmallWord =
  2247.   // F1     F2     F3     F4     F5
  2248.     ($5E00, $5F00, $6000, $6100, $6200);
  2249.  
  2250.   // --- Table for mapping 'ESC ESC [ [ <A..E>' to scancodes ------------
  2251.   KbdScanAltFn5: array['A'..'E'] of SmallWord =
  2252.   // F1     F2     F3     F4     F5
  2253.     ($6800, $6900, $6A00, $6B00, $6C00);
  2254.  
  2255. var
  2256.   // Thread handle of keyboard thread
  2257.   KbdThreadID: LongInt = 0;
  2258.  
  2259.   // Keyboard buffer
  2260.   KbdBuffer: TPipe;
  2261.  
  2262.   // Number of characters in the keyboard buffer
  2263.   KbdBufferCount: LongInt = 0;
  2264.  
  2265.   // Semaphore for accessing the keyboard buffer counter
  2266.   KbdBufferMutex: LongInt = 0;
  2267.  
  2268.   // Next keyboard event to be read from keyboard - needed for TV
  2269.   KbdNextKey: TSysKeyEvent = (skeKeyCode: 0; skeShiftState: 0);
  2270.  
  2271. // Keyboard worker thread function for use in terminal
  2272. function KbdTerminalThread(Args: Pointer): LongInt;
  2273. var
  2274.   I, L: SmallWord;
  2275.   Buffer: array[0..15] of Char;
  2276.   Key: TSysKeyEvent;
  2277.  
  2278.   // Decode 'ESC <single character>'
  2279.   procedure DecodeEscChr(C: Char);
  2280.   begin
  2281.     // ALT and a normal key
  2282.     case C of
  2283.       '0'..'9':
  2284.       begin
  2285.         Key.skeKeyCode := KbdScanAltNum[C];
  2286.         Key.skeShiftState := Key.skeShiftState or 8; // ALT
  2287.       end;
  2288.  
  2289.       'A'..'Z':
  2290.       begin
  2291.         Key.skeKeyCode := KbdScanAltChr[C];
  2292.         Key.skeShiftState := Key.skeShiftState or 8; // SHIFT+ALT
  2293.       end;
  2294.  
  2295.       'a'..'z':
  2296.       begin
  2297.         Key.skeKeyCode := KbdScanAltChr[UpCase(C)];
  2298.         Key.skeShiftState := Key.skeShiftState or 8; // ALT
  2299.       end;
  2300.  
  2301.       #27:
  2302.       begin
  2303.         Key.skeKeyCode := $011B; // ESC ESC means ESC itself
  2304.       end;
  2305.     end;
  2306.   end;
  2307.  
  2308.   // Decode 'ESC <character sequence>'
  2309.   procedure DecodeEscSeq(P: PChar);
  2310.   var
  2311.     X: Integer;
  2312.     A: Boolean;
  2313.   begin
  2314.     if P[0] = #27 then
  2315.     begin
  2316.       Key.skeShiftState := Key.skeShiftState or 8; // ALT
  2317.       Inc(P);
  2318.     end;
  2319.  
  2320.     if (P[0] = 'O') and (P[2] = #0) then
  2321.     begin
  2322.       case P[1] of
  2323.         'A'..'Z':
  2324.         begin
  2325.           if Key.skeShiftState and 8 = 8 then
  2326.             Key.skeKeyCode := KbdScanAltFn1[P[1]]
  2327.           else if Key.skeShiftState and 4 = 4 then
  2328.             Key.skeKeyCode := KbdScanCtlFn1[P[1]]
  2329.           else if Key.skeShiftState and 2 = 2 then
  2330.             Key.skeKeyCode := KbdScanSftFn1[P[1]]
  2331.           else
  2332.             Key.skeKeyCode := KbdScanNrmFn1[P[1]]
  2333.         end;
  2334.  
  2335.         'a'..'z':
  2336.         begin
  2337.           if Key.skeShiftState and 8 = 8 then
  2338.             Key.skeKeyCode := KbdScanAltFn2[P[1]]
  2339.           else if Key.skeShiftState and 4 = 4 then
  2340.             Key.skeKeyCode := KbdScanCtlFn2[P[1]]
  2341.           else if Key.skeShiftState and 2 = 2 then
  2342.             Key.skeKeyCode := KbdScanSftFn2[P[1]]
  2343.           else
  2344.             Key.skeKeyCode := KbdScanNrmFn2[P[1]]
  2345.         end;
  2346.       end;
  2347.     end
  2348.     else if P[0] = '[' then
  2349.     begin
  2350.       if P[1] in ['0'..'9'] then
  2351.       begin
  2352.         X := Ord(P[1]) - Ord('0');
  2353.         if P[2] in ['0'..'9'] then
  2354.         begin
  2355.           X := 10 * X + Ord(P[2]) - Ord('0');
  2356.           if P[3] <> '~' then X := 0;
  2357.         end
  2358.         else if P[2] <> '~' then X := 0;
  2359.  
  2360.         if X in [1..26] then
  2361.         begin
  2362.           if Key.skeShiftState and 8 = 8 then
  2363.             Key.skeKeyCode := KbdScanAltFn3[X]
  2364.           else if Key.skeShiftState and 4 = 4 then
  2365.             Key.skeKeyCode := KbdScanCtlFn3[X]
  2366.           else if Key.skeShiftState and 2 = 2 then
  2367.             Key.skeKeyCode := KbdScanSftFn3[X]
  2368.           else
  2369.             Key.skeKeyCode := KbdScanNrmFn3[X];
  2370.         end;
  2371.       end
  2372.       else if P[1] in ['A'..'D'] then
  2373.       begin
  2374.         if Key.skeShiftState and 8 = 8 then
  2375.           Key.skeKeyCode := KbdScanAltFn1[P[1]]
  2376.         else if Key.skeShiftState and 4 = 4 then
  2377.           Key.skeKeyCode := KbdScanCtlFn1[P[1]]
  2378.         else if Key.skeShiftState and 2 = 2 then
  2379.           Key.skeKeyCode := KbdScanSftFn1[P[1]]
  2380.         else
  2381.           Key.skeKeyCode := KbdScanNrmFn1[P[1]];
  2382.       end
  2383.       else if (P[1] = '[') and (P[2] in ['A'..'E']) then
  2384.       begin
  2385.         if Key.skeShiftState and 8 = 8 then
  2386.           Key.skeKeyCode := KbdScanAltFn5[P[2]]
  2387.         else if Key.skeShiftState and 4 = 4 then
  2388.           Key.skeKeyCode := KbdScanCtlFn5[P[2]]
  2389.         else if Key.skeShiftState and 2 = 2 then
  2390.           Key.skeKeyCode := KbdScanSftFn5[P[2]]
  2391.         else
  2392.           Key.skeKeyCode := KbdScanNrmFn5[P[2]];
  2393.       end;
  2394.     end;
  2395.   end;
  2396.  
  2397. begin
  2398.   while True do
  2399.   begin
  2400.     TrmRead(Buffer, 1);
  2401.     Buffer[1] := #0;
  2402.  
  2403.     // ALT simulation via ESC
  2404.     if Buffer[0] = #27 then
  2405.     begin
  2406.       L := 1 + TrmRead(Buffer[1], 14);
  2407.       Buffer[L] := #0;
  2408.     end;
  2409.  
  2410.     Key.skeKeyCode := 0;
  2411.     Key.skeShiftState := SysTVGetShiftState;;
  2412.  
  2413.     // Decode key
  2414.     if (Buffer[0] = #27) and (Buffer[1] <> #0) then
  2415.     begin
  2416.       if Buffer[2] = #0 then
  2417.         DecodeEscChr(Buffer[1])
  2418.       else
  2419.         DecodeEscSeq(@Buffer[1]);
  2420.     end
  2421.     else
  2422.     begin
  2423.       Key.skeKeyCode := Ord(Buffer[0]);
  2424.       if (Key.skeKeyCode >= 1) and (Key.skeKeyCode <= 27) then
  2425.       begin
  2426.         case Key.skeKeyCode of
  2427.           $09: Key.skeKeyCode := $0F09; // TAB
  2428.           $0A: Key.skeKeyCode := $1C0D; // CR (instead of LF)
  2429.           $1B: Key.skeKeyCode := $011B; // ESC
  2430.         else
  2431.           Key.skeShiftState := Key.skeShiftState or 4 // Ctrl
  2432.         end;
  2433.       end;
  2434.     end;
  2435.  
  2436.     if Key.skeKeyCode <> 0 then
  2437.     begin
  2438.       LnxWrite(KbdBuffer.WrFile, Key, SizeOf(Key));
  2439.       SysSysWaitSem(KbdBufferMutex);
  2440.       Inc(KbdBufferCount);
  2441.       KbdBufferMutex := 0;
  2442.     end;
  2443.   end;
  2444. end;
  2445.  
  2446. function SysKeyPressed: Boolean;
  2447. var
  2448.   C: Char;
  2449. begin
  2450.   Result := SysPeekKey(C);
  2451. end;
  2452.  
  2453. function SysPeekKey(Var Ch: Char): Boolean;
  2454. begin
  2455.   if KbdNextKey.skeKeyCode = 0 then
  2456.     begin
  2457.       if KbdBufferCount <> 0 then
  2458.         begin
  2459.           LnxRead(KbdBuffer.RdFile, KbdNextKey, SizeOf(KbdNextKey));
  2460.           Ch := Chr(Lo(KbdNextKey.skeKeyCode));
  2461.           SysSysWaitSem(KbdBufferMutex);
  2462.           Dec(KbdBufferCount);
  2463.           KbdBufferMutex := 0;
  2464.           Result := True;
  2465.         end
  2466.       else
  2467.         Result := False;
  2468.     end
  2469.   else
  2470.     begin
  2471.       Ch := Chr(Lo(KbdNextKey.skeKeyCode));
  2472.       Result := True;
  2473.     end;
  2474. end;
  2475.  
  2476. function SysReadKey: Char;
  2477. begin
  2478.   if KbdNextKey.skeKeyCode = 0 then
  2479.     begin
  2480.       LnxRead(KbdBuffer.RdFile, KbdNextKey, SizeOf(KbdNextKey));
  2481.       Result := Chr(Lo(KbdNextKey.skeKeyCode));
  2482.       SysSysWaitSem(KbdBufferMutex);
  2483.       Dec(KbdBufferCount);
  2484.       KbdBufferMutex := 0;
  2485.     end
  2486.   else
  2487.     Result := Chr(Lo(KbdNextKey.skeKeyCode));
  2488.  
  2489.   if Result = #0 then
  2490.     KbdNextKey.skeKeyCode := KbdNextKey.skeKeyCode shr 8
  2491.   else
  2492.     KbdNextKey.skeKeyCode := 0;
  2493. end;
  2494.  
  2495. procedure SysFlushKeyBuf;
  2496. var
  2497.   I: Integer;
  2498. begin
  2499.   SysSysWaitSem(KbdBufferMutex);
  2500.  
  2501.   for I := 0 to KbdBufferCount - 1 do
  2502.     LnxRead(KbdBuffer.RdFile, KbdNextKey, SizeOf(KbdNextKey));
  2503.  
  2504.   KbdNextKey.skeKeyCode := 0;
  2505.   KbdBufferMutex := 0;
  2506. end;
  2507.  
  2508. procedure SysWrtCharStrAtt(CharStr: Pointer; Len, X, Y: SmallWord; var Attr: Byte);
  2509. var
  2510.   Src: PChar;
  2511.   Dst, I: LongInt;
  2512. begin
  2513.   Src := CharStr;
  2514.   Dst := Y * ScrWidth + X;
  2515.  
  2516.   for I := 0 to Len - 1 do
  2517.     begin
  2518.       ScrBuffer^[Dst + I].Chr := Src[I];
  2519.       ScrBuffer^[Dst + I].Att := Attr;
  2520.     end;
  2521.  
  2522.   SysTVShowBuf(Dst * 2, Len * 2);
  2523. end;
  2524.  
  2525. function SysReadAttributesAt(x,y: SmallWord): Byte;
  2526. begin
  2527.   Result := ScrBuffer^[Y * ScrWidth + X].Att;
  2528. end;
  2529.  
  2530. function SysReadCharAt(x,y: SmallWord): Char;
  2531. begin
  2532.   Result := ScrBuffer^[Y * ScrWidth + X].Chr;
  2533. end;
  2534.  
  2535. procedure SysScrollUp(X1,Y1,X2,Y2,Lines,Cell: SmallWord);
  2536. var
  2537.   I, J, Src, Dst, Len: Integer;
  2538.   FullScreen: Boolean;
  2539.   Ctrl: string;
  2540. begin
  2541.   if Lines > Y2 - Y1 + 1 then Lines := Y2 - Y1 + 1;
  2542.  
  2543. //  FullScreen := (Lines = 1) and (X1 = 0) and (Y1 = 0) and (X2 = ScrWidth - 1) and (Y2 = ScrHeight - 1);
  2544.  
  2545.   Src := ScrWidth * (Y1 + Lines) + X1;
  2546.   Dst := ScrWidth * Y1 + X1;
  2547.   Len := X2 - X1 + 1;
  2548.  
  2549.   for I := Y1 to Y2 - Lines do
  2550.     begin
  2551.       Move(ScrBuffer^[Src], ScrBuffer^[Dst], Len * 2);
  2552.       SysTVShowBuf(Dst * 2, Len * 2);
  2553.  
  2554.       Inc(Src, ScrWidth);
  2555.       Inc(Dst, ScrWidth);
  2556.     end;
  2557.  
  2558.   for I := 1 to Lines do
  2559.     begin
  2560.       for J := 0 to Len - 1 do
  2561.         begin
  2562.           ScrBuffer^[Dst + J].Chr := Chr(Cell and $FF);
  2563.           ScrBuffer^[Dst + J].Att := Cell shr $08;
  2564.         end;
  2565.  
  2566.       SysTVShowBuf(Dst * 2, Len * 2);
  2567.  
  2568.       Inc(Src, ScrWidth);
  2569.       Inc(Dst, ScrWidth);
  2570.     end;
  2571.  
  2572. //  if FullScreen then
  2573.   begin
  2574.     Ctrl := #27'D';
  2575.     TrmWrite(Ctrl[1], Length(Ctrl));
  2576.     SysTVShowBuf(ScrWidth * (ScrHeight - 1) * 2, Len * 2);
  2577.   end;
  2578. end;
  2579.  
  2580. procedure SysScrollDn(X1,Y1,X2,Y2,Lines,Cell: SmallWord);
  2581. var
  2582.   I, J, Src, Dst, Len: Integer;
  2583. begin
  2584.   if Lines > Y2 - Y1 + 1 then Lines := Y2 - Y1 + 1;
  2585.  
  2586.   Src := ScrWidth * (Y2 - Lines) + X1;
  2587.   Dst := ScrWidth * Y2 + X1;
  2588.   Len := X2 - X1 + 1;
  2589.  
  2590.   for I := Y1 to Y2 - Lines do
  2591.     begin
  2592.       Move(ScrBuffer^[Src], ScrBuffer^[Dst], Len * 2);
  2593.       SysTVShowBuf(Dst * 2, Len * 2);
  2594.  
  2595.       Dec(Src, ScrWidth);
  2596.       Dec(Dst, ScrWidth);
  2597.     end;
  2598.  
  2599.   for I := 1 to Lines do
  2600.     begin
  2601.       for J := 0 to Len - 1 do
  2602.         begin
  2603.           ScrBuffer^[Dst + J].Chr := Chr(Cell and $FF);
  2604.           ScrBuffer^[Dst + J].Att := Cell shr $08;
  2605.         end;
  2606.  
  2607.       SysTVShowBuf(Dst * 2, Len * 2);
  2608.  
  2609.       Dec(Src, ScrWidth);
  2610.       Dec(Dst, ScrWidth);
  2611.     end;
  2612. end;
  2613.  
  2614. procedure SysGetCurPos(var X,Y: SmallWord);
  2615. begin
  2616.   X := ScrColumn;
  2617.   Y := ScrRow;
  2618. end;
  2619.  
  2620. function SysTVDetectMouse: Longint;
  2621. begin
  2622.   Result := 0;
  2623. end;
  2624.  
  2625. procedure SysTVInitMouse(var X,Y: Integer);
  2626. begin
  2627. end;
  2628.  
  2629. procedure SysTVDoneMouse(Close: Boolean);
  2630. begin
  2631. end;
  2632.  
  2633. procedure SysTVShowMouse;
  2634. begin
  2635. end;
  2636.  
  2637. procedure SysTVHideMouse;
  2638. begin
  2639. end;
  2640.  
  2641. procedure SysTVUpdateMouseWhere(var X,Y: Integer);
  2642. begin
  2643. end;
  2644.  
  2645. function SysTVGetMouseEvent(var Event: TSysMouseEvent): Boolean;
  2646. begin
  2647.   Result := False;
  2648. end;
  2649.  
  2650. procedure SysTVKbdInit;
  2651. begin
  2652.   // Get a pipe for the keyboard buffer
  2653.   LnxPipe(KbdBuffer);
  2654.  
  2655.   // Start keyboard converter thread
  2656.   SysCtrlCreateThread(nil, 1024, @KbdTerminalThread, nil, 0, KbdThreadID);
  2657. end;
  2658.  
  2659. function SysTVGetKeyEvent(var Event: TSysKeyEvent): Boolean;
  2660. begin
  2661.   if KbdNextKey.skeKeyCode = 0 then
  2662.     begin
  2663.       SysSysWaitSem(KbdBufferMutex);
  2664.  
  2665.       if KbdBufferCount <> 0 then
  2666.         begin
  2667.           LnxRead(KbdBuffer.RdFile, Event, SizeOf(KbdNextKey));
  2668.           Dec(KbdBufferCount);
  2669.           Result := True;
  2670.         end
  2671.       else
  2672.         Result := False;
  2673.  
  2674.       KbdBufferMutex := 0;
  2675.     end
  2676.   else
  2677.     begin
  2678.       Event := KbdNextKey;
  2679.       KbdNextKey.skeKeyCode := 0;
  2680.       Result := True;
  2681.     end;
  2682. end;
  2683.  
  2684. function SysTVPeekKeyEvent(var Event: TSysKeyEvent): Boolean;
  2685. begin
  2686.   if KbdNextKey.skeKeyCode = 0 then
  2687.     begin
  2688.       SysSysWaitSem(KbdBufferMutex);
  2689.  
  2690.       if KbdBufferCount <> 0 then
  2691.         begin
  2692.           LnxRead(KbdBuffer.RdFile, KbdNextKey, SizeOf(KbdNextKey));
  2693.           Event := KbdNextKey;
  2694.           Dec(KbdBufferCount);
  2695.           Result := True;
  2696.         end
  2697.       else
  2698.         Result := False;
  2699.  
  2700.       KbdBufferMutex := 0;
  2701.     end
  2702.   else
  2703.     begin
  2704.       Event := KbdNextKey;
  2705.       Result := True;
  2706.     end;
  2707. end;
  2708.  
  2709. function SysTVGetShiftState: Byte;
  2710. var
  2711.   B: Byte;
  2712. begin
  2713.   B := 6;
  2714.   if LnxIoCtl(TrmHandle, TIOCLINUX, @B) < 0 then
  2715.     B := 0;
  2716.   Result := (B and 12) or (B and 1) shl 1;
  2717. end;
  2718.  
  2719. procedure SysTVSetCurPos(X,Y: Integer);
  2720. var
  2721.   S: string;
  2722. begin
  2723.   ScrColumn := X;
  2724.   ScrRow := Y;
  2725.   S := #27'[' + IntToStr(Y + 1) + ';' + IntToStr(X + 1) + 'H';
  2726.   TrmWrite(S[1], Length(S));
  2727. end;
  2728.  
  2729. procedure SysTVSetCurType(Y1,Y2: Integer; Show: Boolean);
  2730. var
  2731.   Ctrl: string;
  2732. begin
  2733.   ScrCursor := Show;
  2734.   if Show then
  2735.     Ctrl := #27'[?25h' else Ctrl := #27'[?25l';
  2736.   TrmWrite(Ctrl[1], Length(Ctrl));
  2737. end;
  2738.  
  2739. procedure SysTVGetCurType(var Y1,Y2: Integer; var Visible: Boolean);
  2740. begin
  2741.   Y1 := 0;
  2742.   Y2 := 0;
  2743.   Visible := ScrCursor;
  2744. end;
  2745.  
  2746. procedure SysTVShowBuf(Pos,Size: Integer);
  2747. var
  2748.   Attr, LastAttr: Byte;
  2749.   Mode: Boolean;
  2750.   Ctrl, Data: string;
  2751.   J, X, Y: Integer;
  2752. begin
  2753.   if Odd(Pos) then
  2754.     begin
  2755.       Dec(Pos);
  2756.       Inc(Size);
  2757.     end;
  2758.  
  2759.   if Odd(Size) then
  2760.     Inc(Size);
  2761.  
  2762.   if ScrCursor then
  2763.     Ctrl := #27'[?25l'#27'7'
  2764.   else
  2765.     Ctrl := #27'7';
  2766.   TrmWrite(Ctrl[1], Length(Ctrl));
  2767.  
  2768.   Y := Pos div (2 * ScrWidth);
  2769.   X := (Pos mod (2 * ScrWidth)) div 2;
  2770.  
  2771.   Ctrl := #27'[' + IntToStr(Y + 1) + ';' + IntToStr(X + 1) + 'H';
  2772.   TrmWrite(Ctrl[1], Length(Ctrl));
  2773.  
  2774.   LastAttr := 0;
  2775.   Mode := False;
  2776.   Data := '';
  2777.  
  2778.   for J := 0 to Size div 2 - 1 do
  2779.     begin
  2780.       Attr := ScrBuffer^[Pos div 2 + J].Att;
  2781.  
  2782.       if Attr <> LastAttr then
  2783.       begin
  2784.         TrmWrite(Data[1], Length(Data));
  2785.         Data := '';
  2786.         LastAttr := Attr;
  2787.  
  2788.         Ctrl := #27'[0';
  2789.  
  2790.         if (Attr and $80) <> 0 then Ctrl := Ctrl + ';5';
  2791.         if (Attr and $08) <> 0 then Ctrl := Ctrl + ';1';
  2792.  
  2793.         Attr := Attr and $77;
  2794.  
  2795.         if ScrColors > 2 then
  2796.         begin
  2797.           Ctrl := Ctrl + ';3' + IntToStr(ScrPalette[Attr and $0F])
  2798.                        + ';4' + IntToStr(ScrPalette[Attr shr $04]) + 'm';
  2799.         end;
  2800.  
  2801.         TrmWrite(Ctrl[1], Length(Ctrl));
  2802.       end;
  2803.  
  2804.       case ScrBuffer^[Pos div 2 + J].Chr of
  2805.         #1..#6:
  2806.         begin
  2807.           if Mode then
  2808.           begin
  2809.             Data := Data + #27'(B';
  2810.             Mode := False;
  2811.           end;
  2812.  
  2813.           Data := Data + ScrGraphs[ScrBuffer^[Pos div 2 + J].Chr];
  2814.         end;
  2815.  
  2816.         #7..#31:
  2817.         begin
  2818.           if not (Mode or (ScrMode = MON1) or (ScrMode = COL1)) then
  2819.           begin
  2820.             Data := Data + #27'(0';
  2821.             Mode := True;
  2822.           end;
  2823.  
  2824.           Data := Data + ScrGraphs[ScrBuffer^[Pos div 2 + J].Chr];
  2825.         end;
  2826.  
  2827.         #0, #127..#159:
  2828.         begin
  2829.           Data := Data + ' ';
  2830.         end;
  2831.  
  2832.       else
  2833.         if Mode then
  2834.         begin
  2835.           Data := Data + #27'(B';
  2836.           Mode := False;
  2837.         end;
  2838.  
  2839.         Data := Data + ScrBuffer^[Pos div 2 + J].Chr;
  2840.       end;
  2841.  
  2842.       if Length(Data) > 127 then
  2843.       begin
  2844.         TrmWrite(Data[1], Length(Data));
  2845.         Data := '';
  2846.       end;
  2847.     end;
  2848.  
  2849.   if Mode then
  2850.     Data := Data + #27'(B';
  2851.  
  2852.   TrmWrite(Data[1], Length(Data));
  2853.  
  2854.   if ScrCursor then
  2855.     Ctrl := #27'[?25h'#27'8'
  2856.   else
  2857.     Ctrl := #27'8';
  2858.   TrmWrite(Ctrl[1], Length(Ctrl));
  2859. end;
  2860.  
  2861. procedure SysTVClrScr;
  2862. var
  2863.   I: LongInt;
  2864. begin
  2865.   for I := 0 to ScrSize div 2 - 1 do
  2866.     begin
  2867.       ScrBuffer^[I].Chr := ' ';
  2868.       ScrBuffer^[I].Att := 0;
  2869.     end;
  2870.  
  2871.   for I := 0 to ScrHeight - 1 do
  2872.     SysTVShowBuf(I * 2 * ScrWidth, 2 * ScrWidth);
  2873.  
  2874.   SysTVSetCurPos(0, 0);
  2875. end;
  2876.  
  2877. function SysTVGetScrMode(Size: PSysPoint): Integer;
  2878. begin
  2879.   if Size <> nil then
  2880.     begin
  2881.       Size^.X := ScrWidth;
  2882.       Size^.Y := ScrHeight;
  2883.     end;
  2884.  
  2885.   Result := ScrMode;
  2886. end;
  2887.  
  2888. procedure SysTVSetScrMode(Mode: Integer);
  2889. begin
  2890.   // Set color mapping
  2891.   case Mode of
  2892.     MON1, MON2:
  2893.     begin
  2894.       ScrColors := 2;
  2895.  
  2896.       ScrPalette[0] := 0; ScrPalette[1] := 0;
  2897.       ScrPalette[2] := 0; ScrPalette[3] := 0;
  2898.       ScrPalette[4] := 0; ScrPalette[5] := 0;
  2899.       ScrPalette[6] := 0; ScrPalette[7] := 0;
  2900.     end;
  2901.  
  2902.     COL1, COL2:
  2903.     begin
  2904.       ScrColors := 8;
  2905.  
  2906.       ScrPalette[0] := 0; ScrPalette[1] := 4;
  2907.       ScrPalette[2] := 2; ScrPalette[3] := 6;
  2908.       ScrPalette[4] := 1; ScrPalette[5] := 5;
  2909.       ScrPalette[6] := 3; ScrPalette[7] := 7;
  2910.     end;
  2911.  
  2912.   else
  2913.     Exit;
  2914.   end;
  2915.  
  2916.   // Set mapping of graphics characters
  2917.   case Mode of
  2918.     MON1, COL1: ScrGraphs := #032#094#086#060#062#043#045#079
  2919.                           +  #032#032#091#093#035#061#032#043
  2920.                           +  #043#043#043#045#124#043#043#043
  2921.                           +  #043#043#043#043#043#043#045#124;
  2922.  
  2923.     MON2, COL2: ScrGraphs := #032#094#086#060#062#043#177#096
  2924.                           +  #032#048#048#048#048#104#097#108
  2925.                           +  #107#106#109#113#120#118#119#117
  2926.                           +  #116#110#108#107#106#109#113#120;
  2927.   else
  2928.     Exit;
  2929.   end;
  2930.  
  2931.   ScrMode := Mode;
  2932. end;
  2933.  
  2934. function SysTVGetSrcBuf: Pointer;
  2935. begin
  2936.   Result := ScrBuffer;
  2937. end;
  2938.  
  2939. function StrToIntDef(const S: string; Default: Integer): Integer;
  2940. var
  2941.   Error: LongInt;
  2942. begin
  2943.   Val(S, Result, Error);
  2944.   if Error <> 0 then
  2945.     Result := Default;
  2946. end;
  2947.  
  2948. procedure SysTVInitCursor;
  2949. var
  2950.   Term: string;
  2951.   Size: TWinSize;
  2952. begin
  2953.   // Initialize terminal
  2954.   Term := TrmInit;
  2955.  
  2956.   // Get window size, calculate usable screen, get buffer
  2957.   if LnxIoCtl(TrmHandle, TIOCGWINSZ, @Size) = 0 then
  2958.     begin
  2959.       ScrWidth  := Size.ws_Col;
  2960.       ScrHeight := Size.ws_Row;
  2961.     end;
  2962.  
  2963.   ScrSize := ScrWidth * ScrHeight * 2;
  2964.   if ScrSize > 16384 then
  2965.     begin
  2966.       ScrHeight := 16384 div (2 * ScrWidth);
  2967.       ScrSize   := ScrWidth * ScrHeight * 2;
  2968.     end;
  2969.  
  2970.   GetMem(ScrBuffer, ScrSize);
  2971.  
  2972.   // Try to default to a reasonable video mode
  2973.   if (Term = 'xterm') or (Term = 'linux') then
  2974.     SysTVSetScrMode(COL2)
  2975.   else if (Term = 'vt100') then
  2976.     SysTVSetScrMode(MON2)
  2977.   else
  2978.     SysTVSetScrMode(MON1);
  2979.  
  2980.   // Clear the screen
  2981.   SysTVClrScr;
  2982. end;
  2983.  
  2984. procedure SysCtrlSleep(Delay: Integer);
  2985. var
  2986.   Req, Rem: TTimeSpec;
  2987.   Result: LongInt;
  2988. begin
  2989.   Req.tv_Sec := Delay div 1000;
  2990.   Req.tv_NSec := (Delay mod 1000) * 1000000;
  2991.   repeat
  2992.     Result := -LnxNanoSleep(Req, Rem);
  2993.     Req := Rem;
  2994.   until Result <> EAGAIN;
  2995. end;
  2996.  
  2997. function SysGetValidDrives: Longint;
  2998. begin
  2999.   Result := 4; // 000..000100 -- drive C: only
  3000. end;
  3001.  
  3002. procedure SysDisableHardErrors;
  3003. begin
  3004.   // nop
  3005. end;
  3006.  
  3007. function SysKillProcess(Process: Longint): Longint;
  3008. begin
  3009.   Result := -LnxKill(Process, SIGKILL);
  3010. end;
  3011.  
  3012. function SysAllocSharedMem(Size: Longint; var MemPtr: Pointer): Longint;
  3013. begin
  3014.   Unimplemented('SysAllocSharedMem');
  3015. end;
  3016.  
  3017. function SysGiveSharedMem(MemPtr: Pointer): Longint;
  3018. begin
  3019.   Unimplemented('SysGiveSharedMem');
  3020. end;
  3021.  
  3022. function SysPipeCreate(var ReadHandle,WriteHandle: Longint; Size: Longint): Longint;
  3023. begin
  3024.   Unimplemented('SysPipeCreate');
  3025. end;
  3026.  
  3027. function SysPipePeek(Pipe: Longint; Buffer: Pointer; BufSize: Longint; var BytesRead: Longint; var IsClosing: Boolean): Longint;
  3028. begin
  3029.   Unimplemented('SysPipePeek');
  3030. end;
  3031.  
  3032. function SysPipeClose(Pipe: Longint): Longint;
  3033. begin
  3034.   Unimplemented('SysPipeClose');
  3035. end;
  3036.  
  3037. function SysLoadResourceString(ID: Longint; Buffer: PChar; BufSize: Longint): PChar;
  3038. var
  3039.   p: PChar;
  3040.   Len: Longint;
  3041. begin
  3042.   Buffer^ := #0;
  3043.   p := PChar( LnxGetResourceStringAddress(ID) );
  3044.   if assigned(p) then
  3045.     begin
  3046.       Len := pSmallWord(p)^;
  3047.       if Len > BufSize then
  3048.         Len := BufSize;
  3049.  
  3050.       StrLCopy(Buffer, p+2, Len);
  3051.     end;
  3052.   Result := Buffer;
  3053. end;
  3054.  
  3055. function SysFileUNCExpand(Dest,Name: PChar): PChar;
  3056. begin
  3057.   Unimplemented('SysFileUNCExpand');
  3058. end;
  3059.  
  3060. function SysGetSystemError(Code: Longint; Buffer: PChar; BufSize: Longint;var MsgLen: Longint): PChar;
  3061. begin
  3062.   Result := SysLoadResourceString(57344 + Code, Buffer, BufSize);
  3063.   MsgLen := StrLen(Buffer);
  3064. end;
  3065.  
  3066. procedure SysGetCurrencyFormat(CString: PChar; var CFormat, CNegFormat, CDecimals: Byte; var CThousandSep, CDecimalSep: Char);
  3067. begin
  3068.   StrCopy(CString, '$');
  3069.   CFormat := 0;
  3070.   CNegFormat := 0;
  3071.   CThousandSep := ',';
  3072.   CDecimalSep := '.';
  3073.   CDecimals := 2;
  3074. end;
  3075.  
  3076. procedure SysGetDateFormat(var DateSeparator: Char; ShortDateFormat,LongDateFormat: PChar);
  3077. begin
  3078.   DateSeparator := '/';
  3079.   StrCopy(ShortDateFormat, 'mm/dd/yy');
  3080.   StrCopy(LongDateFormat, 'mmmm d, yyyy');
  3081. end;
  3082.  
  3083. procedure SysGetTimeFormat(var TimeSeparator: Char; TimeAMString,TimePMString,ShortTimeFormat,LongTimeFormat: PChar);
  3084. begin
  3085.   TimeSeparator := ':';
  3086.   StrCopy(TimeAmString, 'am');
  3087.   StrCopy(TimePmString, 'pm');
  3088.   StrCopy(ShortTimeFormat, 'hh:mm');
  3089.   StrCopy(LongTimeFormat, 'hh:mm:ss');
  3090. end;
  3091.  
  3092. function SysGetModuleName(var Address: Pointer; Buffer: PChar; BufSize: Longint): PChar;
  3093. var
  3094.   ModuleName, Temp: PChar;
  3095. begin
  3096.   StrCopy(Buffer, 'UNKNOWN');
  3097.   Result := Buffer;
  3098. //
  3099.   ModuleName := Argv^[0];
  3100.   Temp := StrRScan(ModuleName, '/');
  3101.   if Temp = nil then Temp := ModuleName else Temp := Temp + 1;
  3102.   StrLCopy(Buffer,  Temp, BufSize - 1);
  3103.   Result := Buffer;
  3104.  
  3105. end;
  3106.  
  3107. procedure SysDisplayConsoleError(PopupErrors: Boolean; Title, Msg: PChar);
  3108. var
  3109.   Count: Longint;
  3110. begin
  3111.   SysFileWrite(SysFileStdErr, Msg^, StrLen(Msg), Count);
  3112. end;
  3113.  
  3114. procedure SysDisplayGUIError(Title, Msg: PChar);
  3115. begin
  3116.   Unimplemented('SysDisplayGUIError');
  3117. end;
  3118.  
  3119. procedure SysBeep;
  3120. begin
  3121.   Unimplemented('SysBeep');
  3122. end;
  3123.  
  3124. procedure SysBeepEx(Freq,Dur: Longint);
  3125. begin
  3126.   Unimplemented('SysBeepEx');
  3127. end;
  3128.  
  3129. function SysGetVolumeLabel(Drive: Char): ShortString;
  3130. begin
  3131.   Result := '';
  3132. end;
  3133.  
  3134. function SysSetVolumeLabel(Drive: Char; _Label: ShortString): Boolean;
  3135. begin
  3136.   Result := False;
  3137. end;
  3138.  
  3139. function SysGetForegroundProcessId: Longint;
  3140. begin
  3141.   Unimplemented('SysGetForegroundProcessId');
  3142. end;
  3143.  
  3144. function SysGetBootDrive: Char;
  3145. begin
  3146.   if FileSystem = fsDosUpper then Result := 'C' else Result := 'c';
  3147. end;
  3148.  
  3149. function SysGetDriveType(Drive: Char): TDriveType;
  3150. var
  3151.   StatFS: TStatFS;
  3152. begin
  3153.   if (Drive <> 'C') and (Drive <> 'c') then
  3154.   begin
  3155.     Result := dtInvalid;
  3156.     Exit;
  3157.   end;
  3158.  
  3159.   LnxStatFS('/', StatFS);
  3160.   with StatFS do
  3161.   begin
  3162.     if f_fsid[0] = $00004D44 then
  3163.       Result := dtHDFAT
  3164.     else if f_fsid[0] = $F995E849 then
  3165.       Result := dtHDHPFS
  3166.     else if (f_fsid[0] = $0000EF51) or (f_fsid[0] = $0000EF53) then
  3167.       Result := dtHDEXT2
  3168.     else
  3169.       Result := dtInvalid;
  3170.   end;
  3171. end;
  3172.  
  3173. function SysGetVideoModeInfo( Var Cols, Rows, Colours : Word ): Boolean;
  3174. begin
  3175.   Cols := ScrWidth;
  3176.   Rows := ScrHeight;
  3177.   if (ScrMode = COL1) or (ScrMode = COL2) then
  3178.     Colours := 8
  3179.   else
  3180.     Colours := 2;
  3181.  
  3182.   Result := True;
  3183. end;
  3184.  
  3185. function SysGetVisibleLines( var Top, Bottom: Longint ): Boolean;
  3186. var
  3187.   Cols, Rows, Colours: Word;
  3188. begin
  3189.   if SysGetVideoModeInfo( Cols, Rows, Colours ) then
  3190.   begin
  3191.     Result := True;
  3192.     Top := 1;
  3193.     Bottom := Rows;
  3194.   end
  3195.   else
  3196.     Result := False;
  3197. end;
  3198.  
  3199. function SysSetVideoMode( Cols, Rows: Word ): Boolean;
  3200. begin
  3201.   Unimplemented('SysSetVideoMode');
  3202. end;
  3203.  
  3204. //▒▒▒▒▒▒▒▒▒▒▒▒▒▒[ SEMPAHORE FUNCTIONS ]▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
  3205.  
  3206. function SemCreateEvent(_Name: pChar; _Shared, _State: Boolean): TSemHandle;
  3207. begin
  3208.   Unimplemented('SemCreateEvent');
  3209. end;
  3210.  
  3211. function SemAccessEvent(_Name: PChar): TSemHandle;
  3212. begin
  3213.   Unimplemented('SemAccessEvent');
  3214. end;
  3215.  
  3216. function SemPostEvent(_Handle: TSemhandle): Boolean;
  3217. begin
  3218.   Unimplemented('SemPostEvent');
  3219. end;
  3220.  
  3221. function SemWaitEvent(_Handle: TSemHandle; _TimeOut: Longint): Boolean;
  3222. begin
  3223.   Unimplemented('SemWaitEvent');
  3224. end;
  3225.  
  3226. function SemCreateMutex(_Name: PChar; _Shared, _State: Boolean): TSemHandle;
  3227. begin
  3228.   Unimplemented('SemCreateMutex');
  3229. end;
  3230.  
  3231. function SemRequestMutex(_Handle: TSemHandle; _TimeOut: Longint): Boolean;
  3232. begin
  3233.   Unimplemented('SemRequestMutex');
  3234. end;
  3235.  
  3236. function SemAccessMutex(_Name: PChar): TSemHandle;
  3237. begin
  3238.   Unimplemented('SemAccessMutex');
  3239. end;
  3240.  
  3241. function SemReleaseMutex(_Handle: TSemHandle): Boolean;
  3242. begin
  3243.   Unimplemented('SemReleaseMutex');
  3244. end;
  3245.  
  3246. procedure SemCloseEvent(_Handle: TSemHandle);
  3247. begin
  3248.   Unimplemented('SemCloseEvent');
  3249. end;
  3250.  
  3251. procedure SemCloseMutex(_Handle: TSemHandle);
  3252. begin
  3253.   Unimplemented('SemCloseMutex');
  3254. end;
  3255.  
  3256. function SysMemInfo(_Base: Pointer; _Size: Longint; var _Flags: Longint): Boolean;
  3257. begin
  3258.   // Doesn't seem to be supported. Could be emulated by storing the
  3259.   // access flags in the list of allocated mmap memory blocks and
  3260.   // getting the flags from this list.
  3261.   _Flags := sysmem_read or sysmem_execute;
  3262.   Result := False;
  3263. end;
  3264.  
  3265. function SysSetMemProtection(_Base: Pointer; _Size: Longint; _Flags: Longint): Boolean;
  3266. begin
  3267.   Result := (LnxMProtect(_Base, _Size, _Flags) = 0);
  3268. end;
  3269.  
  3270. procedure SysMessageBox(_Msg, _Title: PChar; _Error: Boolean);
  3271. begin
  3272.   Unimplemented('SysMessageBox');
  3273. end;
  3274.  
  3275. function SysClipCanPaste: Boolean;
  3276. begin
  3277.   Result := False;
  3278. end;
  3279.  
  3280. function SysClipCopy(P: PChar; Size: Longint): Boolean;
  3281. begin
  3282.   Result := False;
  3283. end;
  3284.  
  3285. function SysClipPaste(var Size: Integer): Pointer;
  3286. begin
  3287.   Result := nil;
  3288. end;
  3289.  
  3290. // Retrieve various system settings, bitmapped:
  3291. // 0: Enhanced keyboard installed
  3292.  
  3293. function SysGetSystemSettings: Longint;
  3294. begin
  3295.   Result := 1;
  3296. end;
  3297.  
  3298. type
  3299.   PFpReg = ^TFpReg;
  3300.   TFpReg = record
  3301.     losig:   LongInt;
  3302.     hisig:   LongInt;
  3303.     signexp: SmallWord;
  3304.   end;
  3305.  
  3306.   PContextRecord = ^TContextRecord;
  3307.   TContextRecord = record
  3308.     ContextFlags: LongInt;
  3309.     ctx_env:    array [0..6] of LongInt;
  3310.     ctx_stack:  array [0..7] of TFpReg;
  3311.     ctx_SegGs:  LongInt;
  3312.     ctx_SegFs:  LongInt;
  3313.     ctx_SegEs:  LongInt;
  3314.     ctx_SegDs:  LongInt;
  3315.     ctx_RegEdi: LongInt;
  3316.     ctx_RegEsi: LongInt;
  3317.     ctx_RegEax: LongInt;
  3318.     ctx_RegEbx: LongInt;
  3319.     ctx_RegEcx: LongInt;
  3320.     ctx_RegEdx: LongInt;
  3321.     ctx_RegEbp: LongInt;
  3322.     ctx_RegEip: LongInt;
  3323.     ctx_SegCs:  LongInt;
  3324.     ctx_EFlags: LongInt;
  3325.     ctx_RegEsp: LongInt;
  3326.     ctx_SegSs:  LongInt;
  3327.   end;
  3328.  
  3329.   PExcFrame = ^TExcFrame;
  3330.   TExcFrame = record
  3331.     Next: PExcFrame;
  3332.     Desc: Pointer;
  3333.   end;
  3334.  
  3335.   PSignalInfoBlock = ^TSignalInfoBlock;
  3336.   TSignalInfoBlock = record
  3337.     Number:  LongInt;
  3338.     Report:  TXcptReportRecord;
  3339.     Context: TContextRecord;
  3340.     Next:    PSignalInfoBlock;
  3341.   end;
  3342.  
  3343. procedure GetContextRecord(Context: PContextRecord); {&frame-} {&uses none}
  3344. asm
  3345.                 push    eax
  3346.                 push    edi
  3347.                 mov     edi, [esp + 12]
  3348.                 mov     [edi].TContextRecord.ContextFlags, 7
  3349.  
  3350.                 mov     [edi].TContextRecord.ctx_RegEax, eax
  3351.                 mov     [edi].TContextRecord.ctx_RegEbx, ebx
  3352.                 mov     [edi].TContextRecord.ctx_RegEcx, ecx
  3353.                 mov     [edi].TContextRecord.ctx_RegEdx, edx
  3354.                 mov     [edi].TContextRecord.ctx_RegEsi, esi
  3355.                 mov     eax, [esp + 4]
  3356.                 mov     [edi].TContextRecord.ctx_RegEdi, eax
  3357.  
  3358.                 mov     [edi].TContextRecord.ctx_RegEbp, ebp
  3359.                 mov     eax, [esp + 16]
  3360.                 mov     [edi].TContextRecord.ctx_RegEsp, eax
  3361.                 mov     eax,  [esp + 8]
  3362.                 mov     [edi].TContextRecord.ctx_RegEip, eax
  3363.  
  3364.                 pushfd
  3365.                 pop     eax
  3366.                 mov     [edi].TContextRecord.ctx_EFlags, eax
  3367.  
  3368.                 pop     edi
  3369.                 pop     eax
  3370. end;
  3371.  
  3372. function SysRaiseException(Xcpt: PXcptReportRecord): LongInt; stdcall; orgname; {&uses ecx,edx} {&frame-}
  3373. asm
  3374.                 mov     ecx, fs:[0]               // First exception frame
  3375.                 mov     edx, Xcpt                 // Xcpt
  3376.  
  3377.               @@LOOP:
  3378.                 pushad
  3379.                 sub     esp, 8                    // Two unused parameters
  3380.                 push    ecx                       // Registration
  3381.                 push    edx                       // Report
  3382.                 call    [ecx].TExcFrame.Desc      // Call handler
  3383.                 add     esp, 16                   // Cleanup stack
  3384.                 or      eax, eax                  // XCPT_CONTINUE_SEARCH ?
  3385.                 popad
  3386.  
  3387.                 jnz     @@RET
  3388.  
  3389.                 mov     ecx, [ecx].TExcFrame.Next // Get previous frame
  3390.                 jmp     @@LOOP
  3391.  
  3392.               @@RET:
  3393.                 xor     eax, eax
  3394. end;
  3395.  
  3396. function SysUnwindException(Handler: PExcFrame; TargetIP: Pointer; Xcpt: PXcptReportRecord): LongInt; stdcall; orgname; {&uses ecx,edx} {&Frame-}
  3397. asm
  3398.                 mov     eax, TargetIP             // Get TargetIP
  3399.                 mov     [esp+@locals+@uses], eax; // And store it as return address
  3400.  
  3401.                 mov     ecx, fs:[0]               // First exception frame
  3402.                 mov     eax, Handler              // Handler
  3403.                 mov     edx, Xcpt                 // Xcpt
  3404. //                or      [edx].TXcptReportRecord.fHandlerFlags, $02
  3405.  
  3406.               @@LOOP:
  3407.                 cmp     ecx, eax                  // Target handler reached ?
  3408.                 je      @@RET                     // If so, return
  3409.  
  3410.                 pushad
  3411.                 sub     esp, 8                    // Two unused parameters
  3412.                 push    ecx                       // Registration
  3413.                 push    edx                       // Report
  3414.                 call    [ecx].TExcFrame.Desc      // Call handler
  3415.                 add     esp, 16                   // Cleanup stack
  3416.                 popad
  3417.  
  3418.                 mov     ecx, [ecx].TExcFrame.Next // Get previous frame
  3419.                 mov     fs:[0], ecx               // Remove current frame
  3420.                 jmp     @@LOOP
  3421.  
  3422.               @@RET:
  3423.                 xor     eax, eax
  3424. end;
  3425.  
  3426. var
  3427.   P: PSignalInfoBlock;
  3428.  
  3429. procedure RaiseSignalException;
  3430. begin
  3431.   SysRaiseException(@P.Report);
  3432. end;
  3433.  
  3434. // Except signal handler
  3435. procedure HandleExceptSignal(SigNum: LongInt; _Context: LongInt); cdecl;
  3436. var
  3437.   SigContext: TSigContext absolute _Context;
  3438.   Signal: TSignalInfoBlock;
  3439. begin
  3440.   FillChar(Signal, SizeOf(Signal), 0);
  3441.  
  3442.   with Signal do
  3443.   begin
  3444.     Number := SigNum;
  3445.  
  3446.     with Report do
  3447.     begin
  3448.       { Linux exception code are $C00xyyzz, with...
  3449.  
  3450.          x: Signal number, see SIG* constants in Linux.pas for details
  3451.         yy: i386 Trap code (for signals which are caused by a trap)
  3452.         zz: Lower 7 bit of coprocessor status (for signals which are
  3453.             caused by a floating point fault)                        }
  3454.  
  3455.       ExceptionNum := $C0000000 or (SigNum shl 16);
  3456.  
  3457.       if SigNum in [SIGBUS, SIGFPE, SIGHUP, SIGSEGV, SIGTERM, SIGTRAP] then
  3458.         ExceptionNum := ExceptionNum or (SigContext.TrapNo shl 8);
  3459.  
  3460.       case ExceptionNum of
  3461.         xcpt_Float_Generic:
  3462.           ExceptionNum := ExceptionNum or (SigContext.FpState.Status and $7F);
  3463.  
  3464.         xcpt_In_Page_Error, xcpt_Access_Violation:
  3465.         begin
  3466.           cParameters := 2;
  3467.           ExceptionInfo[0] := SigContext.err and $02;
  3468.           ExceptionInfo[1] := SigContext.cr2;
  3469.         end;
  3470.       end;
  3471.  
  3472.       ExceptionNum := ExceptionNum and $F0FFFFFF;
  3473.  
  3474.       ExceptionAddress := Pointer(SigContext.Eip);
  3475.     end;
  3476.  
  3477.     with Context, SigContext do
  3478.     begin
  3479.       ctx_SegSs  := Ss;
  3480.       ctx_SegGs  := Gs;
  3481.       ctx_SegFs  := Fs;
  3482.       ctx_SegEs  := Es;
  3483.       ctx_SegDs  := Ds;
  3484.       ctx_SegCs  := Cs;
  3485.  
  3486.       ctx_RegEdi := Edi;
  3487.       ctx_RegEsi := Esi;
  3488.       ctx_RegEdx := Edx;
  3489.       ctx_RegEcx := Ecx;
  3490.       ctx_RegEbx := Ebx;
  3491.       ctx_RegEax := Eax;
  3492.  
  3493.       ctx_RegEbp := Ebp;
  3494.       ctx_RegEsp := Esp;
  3495.       ctx_RegEip := Eip;
  3496.  
  3497.       ctx_EFlags := EFlags;
  3498.     end;
  3499.   end;
  3500.  
  3501.   // Xcpt.Next := GetThreadInfoBlock.ExceptReports;
  3502.   // GetThreadInfoBlock.ExceptReports := Xcpt;
  3503.  
  3504.   // SigContext.eip := LongInt(@RaiseSignalException);
  3505.  
  3506.   SysRaiseException(@Signal.Report);
  3507. end;
  3508.  
  3509. procedure SetSignalHandlers;
  3510. const
  3511.   OtherSignals: array[1..21] of LongInt =
  3512.     (SIGABRT, SIGALRM, SIGBUS,  SIGFPE,    SIGHUP,    SIGILL,  SIGINT,
  3513.      SIGIO,   SIGIOT,  SIGKILL, SIGPIPE,   SIGPOLL,   SIGPWR,  SIGQUIT,
  3514.      SIGSEGV, SIGTERM, SIGTRAP, SIGUSR2,   SIGVTALRM, SIGXCPU, SIGXFSZ);
  3515. var
  3516.   Act, Old: TSigAction;
  3517.   I: LongInt;
  3518. begin
  3519.   FillChar(Act, SizeOf(Act), 0);
  3520.  
  3521.   // Set handler for SIGUSR1 - needed for
  3522.   // supending and restarting threads
  3523.   Act.sa_Handler := @HandleStateSignal;
  3524.   LnxSigAction(SIGUSR1, Act, Old);
  3525.  
  3526.   // Set handler for SIGCHLD - needed for
  3527.   // notifying the main thread when a
  3528.   // child thread terminates.
  3529.   Act.sa_Handler := @HandleChildSignal;
  3530.   LnxSigAction(SIGCHLD, Act, Old);
  3531.  
  3532.   // Set other handlers - needed for
  3533.   // mapping of signals to exceptions.
  3534.   for I := Low(OtherSignals) to High(OtherSignals) do
  3535.   begin
  3536.     Act.sa_Handler := @HandleExceptSignal;
  3537.     Act.sa_Flags := SA_NODEFER;
  3538.     LnxSigAction(OtherSignals[I], Act, Old);
  3539.   end;
  3540. end;
  3541.  
  3542. procedure SysLowInit; {&USES All} {&FRAME-}
  3543. asm
  3544.                 // Adjust stack bottom
  3545.                 sub     MainThread.Stack, eax
  3546.  
  3547.                 // Get process ID
  3548.                 call    LnxGetPid
  3549.                 mov     MainThread.ThreadPid, eax
  3550.                 mov     MainThread.ProcessPid, eax
  3551.  
  3552.                 // Create FS selector for main thread
  3553.                 push    1 // LDT entry #1
  3554.                 push    OFFSET MainThread
  3555.                 push    TYPE MainThread
  3556.                 call    GetNewSelector
  3557.                 mov     fs, ax
  3558.                 mov     MainThread.TibSelector, fs
  3559.  
  3560.                 // Clear exception handler chain
  3561.                 xor     eax, eax
  3562.                 mov     MainThread.ExceptChain, eax
  3563.  
  3564.                 // Initialize thread info table
  3565.                 mov     edi, OFFSET Threads
  3566.                 mov     DWORD [edi], OFFSET MainThread
  3567.                 add     edi, 4
  3568.                 mov     ecx, TYPE Threads / 4 - 4
  3569.                 repnz   stosw
  3570.  
  3571.                 // Get argument values
  3572.                 mov ebx, esp
  3573.                 add ebx, @uses+32
  3574.                 mov Argv, ebx
  3575.  
  3576.                 // Get argument count
  3577.                 mov ebx, [esp+28+@uses]
  3578.                 mov Argc, ebx
  3579.  
  3580.                 // Get environment strings
  3581.                 shl ebx, 2
  3582.                 add ebx, esp
  3583.                 add ebx, 36+@uses;
  3584.                 mov Env, ebx;
  3585.                 mov Environment, ebx;
  3586.  
  3587.                 // Set needed signal handlers
  3588.                 call SetSignalHandlers;
  3589. end;
  3590.  
  3591.