Răsfoiți Sursa

improved fpc linux compatibility

Unknown 7 ani în urmă
părinte
comite
349a336cb9

+ 2 - 0
Quick.AppService.pas

@@ -41,7 +41,9 @@ interface
 {$ENDIF}
 
 uses
+  {$IFDEF MSWINDOWS}
   Windows,
+  {$ENDIF}
   SysUtils,
   {$IFNDEF FPC}
   WinSvc,

+ 65 - 10
Quick.Commons.pas

@@ -7,7 +7,7 @@
   Author      : Kike Pérez
   Version     : 1.4
   Created     : 14/07/2017
-  Modified    : 29/03/2018
+  Modified    : 16/05/2018
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -44,6 +44,9 @@ interface
     {$ENDIF MSWINDOWS}
     {$IFDEF FPC}
     Quick.Files,
+      {$IFDEF LINUX}
+      FileInfo,
+      {$ENDIF}
     {$ELSE}
     IOUtils,
     {$ENDIF}
@@ -104,6 +107,9 @@ type
   {$ENDIF}
 
   {$IFDEF FPC}
+    {$IFDEF LINUX}
+    UINT = cardinal;
+    {$ENDIF}
   PLASTINPUTINFO = ^LASTINPUTINFO;
   tagLASTINPUTINFO = record
     cbSize: UINT;
@@ -163,7 +169,9 @@ type
   //checks if is a console app
   function IsConsole : Boolean;
   //checks if compiled in debug mode
+  {$ENDIF}
   function IsDebug : Boolean;
+  {$IFDEF MSWINDOWS}
   //checks if running as a service
   function IsService : Boolean;
   //gets number of seconds without user interaction (mouse, keyboard)
@@ -197,12 +205,10 @@ type
   //Upper case for first letter
   function Capitalize(s: string): string;
   function CapitalizeWords(s: string): string;
-  {$IFDEF MSWINDOWS}
   //returns current logged user
   function GetLoggedUserName : string;
   //returns computer name
   function GetComputerName : string;
-  {$ENDIF}
   //Changes incorrect delims in path
   function NormalizePathDelim(const cPath : string; const Delim : Char) : string;
   //Removes last segment of a path
@@ -211,16 +217,18 @@ type
   function ParamFindSwitch(const Switch : string) : Boolean;
   //gets value for a switch if exists
   function ParamGetSwitch(const Switch : string; var cvalue : string) : Boolean;
-  {$IFDEF MSWINDOWS}
+  //returns app name (filename based)
+  function GetAppName : string;
   //returns app version (major & minor)
   function GetAppVersionStr: string;
   //returns app version full (major, minor, release & compiled)
   function GetAppVersionFullStr: string;
-  {$ENDIF}
   //UTC DateTime to Local DateTime
   function UTCToLocalTime(GMTTime: TDateTime): TDateTime;
   //Local DateTime to UTC DateTime
   function LocalTimeToUTC(LocalTime : TDateTime): TDateTime;
+  //return GTM time string
+  function DateTimeToGMT(aDate : TDateTime) : string;
   //count number of digits of a Integer
   function CountDigits(anInt: Cardinal): Cardinal; inline;
   //save stream to file
@@ -231,7 +239,7 @@ type
   //get last error message
   function GetLastOSError : String;
   {$ENDIF}
-  {$IFDEF FPC}
+  {$IF DEFINED(FPC) AND DEFINED(MSWINDOWS)}
   function GetLastInputInfo(var plii: TLastInputInfo): BOOL;stdcall; external 'user32' name 'GetLastInputInfo';
   {$ENDIF}
 
@@ -432,6 +440,7 @@ begin
     Result := False;
   {$ENDIF CONSOLE}
 end;
+{$ENDIF}
 
 function IsDebug: Boolean;
 begin
@@ -442,6 +451,7 @@ begin
   {$ENDIF DEBUG}
 end;
 
+{$IFDEF MSWINDOWS}
 function IsService : Boolean;
 begin
   //only working with my Quick.AppService unit
@@ -550,7 +560,6 @@ var
   match : Boolean;
   wildcard : Boolean;
   CurrentPattern : Char;
-  aux : string;
 begin
   Result := False;
   wildcard := False;
@@ -572,7 +581,6 @@ begin
 
     if wildcard then
     begin
-      aux := Copy(Pattern,i+1,Pattern.Length);
       n := Pos(Copy(Pattern,i+1,Pattern.Length),cText);
       if (n > i) or (Pattern.Length = i) then
       begin
@@ -611,8 +619,8 @@ begin
   end;
 end;
 
-{$IFDEF MSWINDOWS}
 function GetLoggedUserName : string;
+{$IFDEF MSWINDOWS}
 const
   cnMaxUserNameLen = 254;
 var
@@ -625,8 +633,16 @@ begin
   SetLength( sUserName, dwUserNameLen );
   Result := sUserName;
 end;
+{$ENDIF}
+{$IF DEFINED(FPC) AND DEFINED(LINUX)}
+begin
+  Result := GetEnvironmentVariable('USERNAME');
+end;
+
+{$ENDIF}
 
 function GetComputerName : string;
+{$IFDEF MSWINDOWS}
 var
   dwLength: dword;
 begin
@@ -635,6 +651,12 @@ begin
   if not Windows.GetComputerName(pchar(result), dwLength) then Result := 'Not detected!';
   Result := pchar(result);
 end;
+{$ENDIF}
+{$IF DEFINED(FPC) AND DEFINED(LINUX)}
+begin
+  Result := GetEnvironmentVariable('COMPUTERNAME');
+end;
+
 {$ENDIF}
 
 function NormalizePathDelim(const cPath : string; const Delim : Char) : string;
@@ -729,8 +751,13 @@ begin
 end;
 
 
-{$IFDEF MSWINDOWS}
+function GetAppName : string;
+begin
+  Result := ExtractFilenameWithoutExt(ParamStr(0));
+end;
+
 function GetAppVersionStr: string;
+{$IFDEF MSWINDOWS}
 var
   Rec: LongRec;
   ver : Cardinal;
@@ -743,8 +770,18 @@ begin
   end
   else Result := '';
 end;
+{$ENDIF}
+{$IF DEFINED(FPC) AND DEFINED(LINUX)}
+var
+  version : TProgramVersion;
+begin
+  if GetProgramVersion(version) then Result := Format('%d.%d', [version.Major, version.Minor])
+    else Result := '';
+end;
+{$ENDIF}
 
 function GetAppVersionFullStr: string;
+{$IFDEF MSWINDOWS}
 var
   Exe: string;
   Size, Handle: DWORD;
@@ -788,6 +825,14 @@ begin
   end;
 end;
 {$ENDIF}
+{$IF DEFINED(FPC) AND DEFINED(LINUX)}
+var
+  version : TProgramVersion;
+begin
+  if GetProgramVersion(version) then Result := Format('%d.%d.%d.%d', [version.Major, version.Minor, version.Revision, version.Build])
+    else Result := '';
+end;
+{$ENDIF}
 
 function UTCToLocalTime(GMTTime: TDateTime): TDateTime;
 begin
@@ -807,6 +852,16 @@ begin
   {$ENDIF}
 end;
 
+function DateTimeToGMT(aDate : TDateTime) : string;
+var
+  FmtSettings : TFormatSettings;
+begin
+  FmtSettings.DateSeparator := '-';
+  FmtSettings.TimeSeparator := ':';
+  FmtSettings.ShortDateFormat := 'YYYY-MM-DD"T"HH:NN:SS.ZZZ" GMT"';
+  Result := DateTimeToStr(aDate,FmtSettings);
+end;
+
 function CountDigits(anInt: Cardinal): Cardinal; inline;
 var
   cmp: Cardinal;

+ 149 - 4
Quick.Console.pas

@@ -49,8 +49,14 @@ interface
 
 uses
   Classes,
+  {$IFDEF MSWINDOWS}
   Windows,
   Messages,
+  {$ELSE}
+    {$IFDEF FPC}
+    crt,
+    {$ENDIF}
+  {$ENDIF}
   SysUtils,
   Quick.Commons,
   Quick.Log;
@@ -87,8 +93,22 @@ type
   {$ELSE}
   TOutputProc<T> = procedure(const aLine : T) of object;
   TExecuteProc = procedure of object;
+    {$IFDEF LINUX}
+    TCoord = record
+      X : tcrtcoord;
+      Y : tcrtcoord;
+    end;
+
+    TSmallRect = record
+      Left : Byte;
+      Top : Byte;
+      Right : Byte;
+      Bottom : Byte;
+    end;
+    {$ENDIF}
   {$ENDIF}
 
+  {$IFDEF MSWINDOWS}
   TConsoleMenuOption = record
   private
     fCaption : string;
@@ -116,6 +136,7 @@ type
     procedure Refresh(aClearScreen : Boolean = False);
     procedure WaitForKeys;
   end;
+  {$ENDIF}
 
   procedure cout(const cMsg : Integer; cEventType : TLogEventType); overload;
   procedure cout(const cMsg : Double; cEventType : TLogEventType); overload;
@@ -136,7 +157,9 @@ type
   procedure TextBackground(Color: TConsoleColor); overload;
   procedure TextBackground(Color: Byte); overload;
   procedure ResetColors;
+  {$IFDEF MSWINDOWS}
   procedure ConsoleResize(Width, Height : Integer);
+  {$ENDIF}
   procedure ClearScreen;
   procedure ClearLine; overload;
   procedure ClearLine(Y : Integer); overload;
@@ -145,11 +168,16 @@ type
   function GetCursorX: Integer; {$IFDEF INLINES}inline;{$ENDIF}
   function GetCursorY: Integer; {$IFDEF INLINES}inline;{$ENDIF}
   function GetCursorMaxBottom : Integer;
-  procedure SetCursorPos(NewCoord : TCoord);
+  procedure SetCursorPos(NewCoord : TCoord); overload;
+  procedure SetCursorPos(x ,y : Integer); overload;
+  {$IFDEF MSWINDOWS}
   procedure ProcessMessages;
+  {$ENDIF}
   procedure ConsoleWaitForEnterKey;
+  {$IFDEF MSWINDOWS}
   procedure RunConsoleCommand(const aCommand, aParameters : String; CallBack : TOutputProc<PAnsiChar> = nil; OutputLines : TStrings = nil);
   procedure InitConsole;
+  {$ENDIF}
 
 
 var
@@ -161,8 +189,10 @@ var
   hStdOut: THandle;
   hStdErr: THandle;
   ConsoleRect: TSmallRect;
+  {$IFDEF MSWINDOWS}
   ScreenBufInfo : TConsoleScreenBufferInfo;
   CursorInfo : TConsoleCursorInfo;
+  {$ENDIF}
 
 implementation
 
@@ -205,7 +235,9 @@ begin
   begin
     EnterCriticalSection(CSConsole);
     try
+      {$IFDEF MSWINDOWS}
       if hStdOut <> 0 then
+      {$ENDIF}
       begin
         case cEventType of
           etError : TextColor(ccLightRed);
@@ -216,7 +248,7 @@ begin
           etTrace : TextColor(ccLightMagenta);
           else TextColor(ccWhite);
         end;
-        Writeln(cMsg);
+        Writeln(cMsg{$IFDEF LINUX} +#13{$ENDIF});
         TextColor(LastMode);
       end;
     finally
@@ -230,10 +262,12 @@ procedure cout(const cMsg : string; cColor : TConsoleColor);
 begin
   EnterCriticalSection(CSConsole);
   try
-    if hStdOut <> 0 then
+    {$IFDEF MSWINDOWS}
+      if hStdOut <> 0 then
+    {$ENDIF}
     begin
       TextColor(cColor);
-      Writeln(cMsg);
+      Writeln(cMsg{$IFDEF LINUX} +#13{$ENDIF});
       TextColor(LastMode);
     end;
   finally
@@ -247,32 +281,63 @@ begin
 end;
 
 function GetCursorX: Integer; {$IFDEF INLINES}inline;{$ENDIF}
+{$IFDEF MSWINDOWS}
 var
   BufferInfo: TConsoleScreenBufferInfo;
 begin
   GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
   Result := BufferInfo.dwCursorPosition.X;
 end;
+{$ELSE}
+begin
+  Result := WhereX;
+end;
+{$ENDIF}
 
 function GetCursorY: Integer; {$IFDEF INLINES}inline;{$ENDIF}
+{$IFDEF MSWINDOWS}
 var
   BufferInfo: TConsoleScreenBufferInfo;
 begin
   GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
   Result := BufferInfo.dwCursorPosition.Y;
 end;
+{$ELSE}
+begin
+  Result := WhereY;
+end;
+{$ENDIF}
 
 function GetCursorMaxBottom : Integer;
+{$IFDEF MSWINDOWS}
 var
   BufferInfo: TConsoleScreenBufferInfo;
 begin
   GetConsoleSCreenBufferInfo(hStdOut, BufferInfo);
   Result := BufferInfo.srWindow.Bottom;
 end;
+{$ELSE}
+begin
+  Result := 80;
+end;
+{$ENDIF}
 
 procedure SetCursorPos(NewCoord : TCoord);
 begin
+  {$IFDEF MSWINDOWS}
   SetConsoleCursorPosition(hStdOut, NewCoord);
+  {$ELSE}
+  GotoXY(NewCoord.X,NewCoord.Y);
+  {$ENDIF}
+end;
+
+procedure SetCursorPos(x ,y : Integer);
+var
+  NewCoord : TCoord;
+begin
+  NewCoord.X := x;
+  NewCoord.Y := y;
+  SetCursorPos(NewCoord);
 end;
 
 procedure coutXY(x,y : Integer; const cMsg : string; cEventType : TLogEventType);
@@ -280,7 +345,9 @@ var
  NewCoord : TCoord;
  LastCoord : TCoord;
 begin
+  {$IFDEF MSWINDOWS}
   if hStdOut = 0 then Exit;
+  {$ENDIF}
   LastCoord.X := GetCursorX;
   LastCoord.Y := GetCursorY;
   NewCoord.X := x;
@@ -299,7 +366,9 @@ var
  NewCoord : TCoord;
  LastCoord : TCoord;
 begin
+  {$IFDEF MSWINDOWS}
   if hStdOut = 0 then Exit;
+  {$ENDIF}
   LastCoord.X := GetCursorX;
   LastCoord.Y := GetCursorY;
   NewCoord.X := x;
@@ -355,10 +424,14 @@ end;
 
 procedure TextColor(Color: Byte);
 begin
+  {$IFDEF MSWINDOWS}
   if hStdOut = 0 then Exit;
   LastMode := TextAttr;
   TextAttr := (TextAttr and $F0) or (Color and $0F);
   if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
+  {$ELSE}
+  crt.TextColor(Color);
+  {$ENDIF}
 end;
 
 procedure TextBackground(Color: TConsoleColor);
@@ -368,18 +441,28 @@ end;
 
 procedure TextBackground(Color: Byte);
 begin
+  {$IFDEF MSWINDOWS}
   if hStdOut = 0 then Exit;
   LastMode := TextAttr;
   TextAttr := (TextAttr and $0F) or ((Color shl 4) and $F0);
   if TextAttr <> LastMode then SetConsoleTextAttribute(hStdOut, TextAttr);
+  {$ELSE}
+  crt.TextBackground(Color);
+  {$ENDIF}
 end;
 
 procedure ResetColors;
 begin
+  {$IFDEF MSWINDOWS}
   SetConsoleTextAttribute(hStdOut, DefConsoleColor);
   TextAttr := DefConsoleColor;
+  {$ELSE}
+  TextColor(DefConsoleColor);
+  TextBackground(ccBlack);
+  {$ENDIF}
 end;
 
+{$IFDEF MSWINDOWS}
 procedure ConsoleResize(Width, Height : Integer);
 var
   Rect: TSmallRect;
@@ -394,8 +477,10 @@ begin
   SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), Coord);
   SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), True, Rect);
 end;
+{$ENDIF}
 
 procedure ClearScreen;
+{$IFDEF MSWINDOWS}
 var
   stdout: THandle;
   bufinfo: TConsoleScreenBufferInfo;
@@ -415,6 +500,11 @@ begin
     SetConsoleCursorPosition(stdout, Origin);
   end;
 end;
+{$ELSE}
+begin
+  ClrScr;
+end;
+{$ENDIF}
 
 procedure ClearLine;
 begin
@@ -422,6 +512,7 @@ begin
 end;
 
 procedure ClearLine(Y : Integer);
+{$IFDEF MSWINDOWS}
 var
  dwWriteCoord: TCoord;
  dwCount, dwSize: DWord;
@@ -433,22 +524,38 @@ begin
   FillConsoleOutputAttribute(hStdOut, TextAttr, dwSize, dwWriteCoord, dwCount);
   FillConsoleOutputCharacter(hStdOut, ' ', dwSize, dwWriteCoord, dwCount);
 end;
+{$ELSE}
+begin
+  GotoXY(1,Y);
+  DelLine;
+  GotoXY(1,Y);
+end;
+{$ENDIF}
 
 procedure ShowCursor;
 begin
+  {$IFDEF MSWINDOWS}
   GetConsoleCursorInfo(hStdOut,CursorInfo);
   CursorInfo.bVisible := True;
   SetConsoleCursorInfo(hStdOut,CursorInfo);
+  {$ELSE}
+  CursorOn;
+  {$ENDIF}
 end;
 
 procedure HideCursor;
 begin
+  {$IFDEF MSWINDOWS}
   GetConsoleCursorInfo(hStdOut,CursorInfo);
   CursorInfo.bVisible := False;
   SetConsoleCursorInfo(hStdOut,CursorInfo);
+  {$ELSE}
+  CursorOff;
+  {$ENDIF}
 end;
 
 function ConsoleKeyPressed(ExpectedKey: Word): Boolean;
+{$IFDEF MSWINDOWS}
 var
   lpNumberOfEvents: DWORD;
   lpBuffer: TInputRecord;
@@ -473,8 +580,19 @@ begin
     end;
   end;
 end;
+{$ELSE}
+var
+  kp : Char;
+begin
+  repeat
+    kp := Readkey;
+  until kp = Char(ExpectedKey);
+  Result := True;
+end;
+{$ENDIF}
 
 function GetConsoleKeyPressed : Word;
+{$IFDEF MSWINDOWS}
 var
   lpNumberOfEvents: DWORD;
   lpBuffer: TInputRecord;
@@ -499,7 +617,13 @@ begin
     end;
   end;
 end;
+{$ELSE}
+begin
+  Result := Ord(ReadKey);
+end;
+{$ENDIF}
 
+{$IFDEF MSWINDOWS}
 procedure ProcessMessages;
 var
   Msg: TMsg;
@@ -509,6 +633,7 @@ begin
     DispatchMessage(Msg);
   end;
 end;
+{$ENDIF}
 
 {$IFDEF MSWINDOWS}
 procedure ConsoleWaitForEnterKey;
@@ -548,6 +673,7 @@ begin
 end;
 {$ENDIF}
 
+{$IFDEF MSWINDOWS}
 procedure RunConsoleCommand(const aCommand, aParameters : String; CallBack : TOutputProc<PAnsiChar> = nil; OutputLines : TStrings = nil);
 const
   CReadBuffer = 2400;
@@ -615,6 +741,9 @@ begin
   end
   else raise Exception.Create('Can''t create pipe!');
 end;
+{$ENDIF}
+
+{$IFDEF MSWINDOWS}
 
 procedure InitConsole;
 var
@@ -644,9 +773,15 @@ begin
   DefConsoleColor := TextAttr;
   LastMode := 3; //CO80;
 end;
+{$ELSE}
+  //AssignCrt(stderr);
+  //Rewrite(stderr);
+{$ENDIF}
+
 
 { TConsoleMenu }
 
+{$IFDEF MSWINDOWS}
 procedure TConsoleMenu.AddMenu(const cMenuCaption: string; const cMenuKey: Word; MenuAction: TExecuteProc);
 var
   conmenu : TConsoleMenuOption;
@@ -798,13 +933,23 @@ procedure TConsoleMenuOption.DoKeyPressed;
 begin
   if Assigned(fOnKeyPressed) then fOnKeyPressed;
 end;
+{$ENDIF}
 
 initialization
+
+{$IF DEFINED(FPC) AND DEFINED(LINUX)}
+InitCriticalSection(CSConsole);
+{$ELSE}
 InitializeCriticalSection(CSConsole);
 //init stdout if not a service
 if GetStdHandle(STD_OUTPUT_HANDLE) <> 0 then InitConsole;
+{$ENDIF}
 
 finalization
+{$IF DEFINED(FPC) AND DEFINED(LINUX)}
+DoneCriticalsection(CSConsole);
+{$ELSE}
 DeleteCriticalSection(CSConsole);
+{$ENDIF}
 
 end.

+ 84 - 3
Quick.Files.pas

@@ -36,11 +36,16 @@ interface
 uses
   Classes,
   SysUtils,
+  {$IFDEF MSWINDOWS}
+  WindowS,
+  {$ENDIF}
   {$IFDEF FPC}
   strutils,
-  DateUtils,
+    {$IFDEF LINUX}
+    baseunix,
+    {$ENDIF}
+    DateUtils;
   {$ENDIF}
-  Windows;
 
 {$IFDEF FPC}
 resourcestring
@@ -82,6 +87,10 @@ type
       procedure Close;
       property EOF: Boolean read GetEOF;
   end;
+  {$ELSE}
+    {$IFDEF LINUX}
+    TFILETIME = LongInt;
+    {$ENDIF}
   {$ENDIF}
 
   {$IFDEF FPC}
@@ -154,7 +163,6 @@ type
     procedure WriteLine(Value: Single); overload; virtual; abstract;
     procedure WriteLine(const Value: string); overload; virtual; abstract;
     procedure WriteLine(const aFormat: string; Args: array of const); overload; virtual; abstract;
-    procedure WriteLine(const Value: TCharArray; Index, Count: Integer); overload; virtual; abstract;
   end;
 
   TStreamWriter = class(TTextWriter)
@@ -279,7 +287,11 @@ end;
 constructor EFileStreamError.Create(ResStringRec: PResStringRec;
   const FileName: string);
 begin
+  {$IFNDEF LINUX}
   inherited CreateResFmt(ResStringRec, [ExpandFileName(FileName), SysErrorMessage(GetLastError)]);
+  {$ELSE}
+  inherited CreateResFmt(ResStringRec, [ExpandFileName(FileName), SysErrorMessage(errno)]);
+  {$ENDIF}
 end;
 
 { TPath }
@@ -363,7 +375,11 @@ end;
 
 class function TFile.Move(const SourceFileName, DestFileName: string) : Boolean;
 begin
+  {$IFNDEF LINUX}
   Result := MoveFile(PChar(SourceFileName),PChar(DestFileName));
+  {$ELSE}
+  Result := RenameFile(PChar(SourceFileName),PChar(DestFileName));
+  {$ENDIF}
 end;
 
 class function TFile.IsInUse(const Path : string) : Boolean;
@@ -371,6 +387,7 @@ begin
   Result := IsFileInUse(Path);
 end;
 
+
 class function TFile.GetSize(const Path : string) : Int64;
 var
   f : File of Byte;
@@ -726,6 +743,7 @@ begin
 end;
 
 function IsFileInUse(const aFileName : string) : Boolean;
+{$IFNDEF LINUX}
 var
   HFileRes: HFILE;
 begin
@@ -749,6 +767,20 @@ begin
     Result := True;
   end;
 end;
+{$ELSE}
+var
+  fs : TFileStream;
+begin
+  try
+    fs := TFileStream.Create(aFileName, fmOpenReadWrite, fmShareExclusive);
+    Result := True;
+    fs.Free;
+  except
+    Result := False;
+  end;
+
+end;
+{$ENDIF}
 
 procedure FileReplaceText(const aFileName, aSearchText, AReplaceText : string);
 var
@@ -852,6 +884,7 @@ begin
   end;
 end;
 
+{$IFDEF MSWINDOWS}
 function GetLastAccessTime(const aFileName: string): TDateTime;
 var
   ffd: TWin32FindData;
@@ -914,6 +947,40 @@ begin
     Result := FileDateToDateTime(dft);
   end;
 end;
+{$ELSE}
+function GetLastAccessTime(const aFileName: string): TDateTime;
+var
+  info : stat;
+begin
+  Result := 0;
+  if fpstat(aFileName,info) <> 0 then
+  begin
+    Result := info.st_atime;
+  end;
+end;
+
+function GetCreationTime(const aFilename : string): TDateTime;
+var
+  info : stat;
+begin
+  Result := 0;
+  if fpstat(aFileName,info) <> 0 then
+  begin
+    Result := info.st_ctime;
+  end;
+end;
+
+function GetLastWriteTime(const aFileName : string): TDateTime;
+var
+  info : stat;
+begin
+  Result := 0;
+  if fpstat(aFileName,info) <> 0 then
+  begin
+    Result := info.st_mtime;
+  end;
+end;
+{$ENDIF}
 
 {$IFDEF FPC}
 function FindDelimiter(const Delimiters, S: string; StartIdx: Integer = 1): Integer;
@@ -954,6 +1021,16 @@ begin
       Result := LFileTime;
 end;
 {$ENDIF}
+{$If Defined(FPC) AND Defined(LINUX)}
+function ConvertDateTimeToFileTime(const DateTime: TDateTime; const UseLocalTimeZone: Boolean): TFileTime;
+begin
+  { Use the time zone if necessary }
+  if not UseLocalTimeZone then
+    Result := DateTimeToFileDate(DateTime)
+  else
+    Result := DateTimeToFileDate(DateTime);
+end;
+{$ENDIF}
 {$IFDEF POSIX}
 class function TDirectory.ConvertDateTimeToFileTime(const DateTime: TDateTime;
   const UseLocalTimeZone: Boolean): time_t;
@@ -1072,5 +1149,9 @@ begin
   end;
 end;
 {$ENDIF}
+{$if Defined(FPC) AND Defined(LINUX)}
+begin
+end;
+{$ENDIF}
 
 end.

+ 7 - 2
Quick.JSONUtils.pas

@@ -7,7 +7,7 @@
   Author      : Kike Pérez
   Version     : 1.1
   Created     : 27/01/2017
-  Modified    : 13/02/2018
+  Modified    : 09/05/2018
 
   This file is part of QuickLib: https://github.com/exilon/QuickLib
 
@@ -29,6 +29,8 @@
 
 unit Quick.JSONUtils;
 
+{$i QuickLib.inc}
+
 interface
 
 uses
@@ -58,6 +60,9 @@ type
   function IncludeJsonBraces(const json : string) : string;
   function RemoveJsonBraces(const json : string) : string;
 
+var
+  GlobalJsonIdenter : Boolean = True;
+
 
 implementation
 
@@ -72,7 +77,7 @@ begin
     {$IFDEF DELPHIRX102_UP}
       Serializer := TJsonSerializer.Create;
       try
-        Serializer.Formatting := TJsonFormatting.Indented;
+        if GlobalJsonIdenter then Serializer.Formatting := TJsonFormatting.Indented;
         Result := Serializer.Serialize<TObject>(Self);
       finally
         Serializer.Free;

+ 18 - 4
Quick.Log.pas

@@ -34,14 +34,19 @@ unit Quick.Log;
 interface
 
 uses
+  {$IFDEF MSWINDOWS}
   Windows,
+  {$ENDIF}
   Classes,
   Quick.Commons,
-  {$ifndef FPC}
+  {$IFNDEF FPC}
   System.IOUtils,
-  {$else}
+  {$ELSE}
   Quick.Files,
-  {$endif}
+    {$IFDEF LINUX}
+    syncObjs,
+    {$ENDIF}
+  {$ENDIF}
   SysUtils;
 
 type
@@ -191,9 +196,10 @@ begin
       Self.WriteLog(Format('Host        : %s',[GetComputerName]));
       Self.WriteLog(Format('Username    : %s',[Trim(GetLoggedUserName)]));
       Self.WriteLog(Format('Started     : %s',[NowStr]));
+      {$IFDEF MSWINDOWS}
       if IsService then Self.WriteLog('AppType     : Service')
         else if System.IsConsole then Self.WriteLog('AppType     : Console');
-
+      {$ENDIF}
       if IsDebug then Self.WriteLog('Debug mode  : On');
       Self.WriteLog(FillStr('-',70));
     except
@@ -280,9 +286,17 @@ begin
 end;
 
 initialization
+{$IF DEFINED(FPC) AND DEFINED(LINUX)}
+InitCriticalSection(CS);
+{$ELSE}
 InitializeCriticalSection(CS);
+{$ENDIF}
 
 finalization
+{$IF DEFINED(FPC) AND DEFINED(LINUX)}
+DoneCriticalsection(CS);
+{$ELSE}
 DeleteCriticalSection(CS);
+{$ENDIF}
 
 end.

+ 3 - 1
README.md

@@ -6,7 +6,9 @@
 
 
 
-Small delphi/fpc library containing interesting and quick to implement functions, created to simplify application development.
+Small delphi/fpc library containing interesting and quick to implement functions, created to simplify application development. 
+
+* NEW: Improved Linux compatibility.
 
 ----------
 **Quick.AppService:** Allow a console app to run as console mode or service mode with same code simplifying debug tasks.

+ 7 - 6
samples/fpc/QuickConsole/ConsoleOut/ConsoleOut.lpi

@@ -24,11 +24,6 @@
       <FormatVersion Value="2"/>
       <Modes Count="0"/>
     </RunParams>
-    <RequiredPackages Count="1">
-      <Item1>
-        <PackageName Value="QuickLib"/>
-      </Item1>
-    </RequiredPackages>
     <Units Count="1">
       <Unit0>
         <Filename Value="ConsoleOut.pas"/>
@@ -40,11 +35,17 @@
     <Version Value="11"/>
     <PathDelim Value="\"/>
     <Target>
-      <Filename Value="ConsoleOut"/>
+      <Filename Value="bin\$(TargetCPU)-$(TargetOS)\ConsoleOut"/>
     </Target>
     <SearchPaths>
+      <IncludeFiles Value="$(ProjOutDir)"/>
+      <OtherUnitFiles Value="..\..\..\..\..\QuickLib"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
+    <CodeGeneration>
+      <TargetCPU Value="x86_64"/>
+      <TargetOS Value="linux"/>
+    </CodeGeneration>
   </CompilerOptions>
   <Debugging>
     <Exceptions Count="3">

+ 24 - 3
samples/fpc/QuickConsole/ConsoleOut/ConsoleOut.lps

@@ -4,15 +4,36 @@
     <PathDelim Value="\"/>
     <Version Value="11"/>
     <BuildModes Active="Default"/>
-    <Units Count="1">
+    <Units Count="4">
       <Unit0>
         <Filename Value="ConsoleOut.pas"/>
         <IsPartOfProject Value="True"/>
         <IsVisibleTab Value="True"/>
         <EditorIndex Value="-1"/>
-        <CursorPos Y="4"/>
-        <UsageCount Value="20"/>
+        <CursorPos X="25" Y="10"/>
+        <UsageCount Value="25"/>
       </Unit0>
+      <Unit1>
+        <Filename Value="..\..\..\..\Quick.AppService.pas"/>
+        <EditorIndex Value="-1"/>
+        <TopLine Value="40"/>
+        <CursorPos X="29" Y="60"/>
+        <UsageCount Value="12"/>
+      </Unit1>
+      <Unit2>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <EditorIndex Value="-1"/>
+        <TopLine Value="372"/>
+        <CursorPos Y="372"/>
+        <UsageCount Value="13"/>
+      </Unit2>
+      <Unit3>
+        <Filename Value="D:\Lazarus\fpcsrc\packages\rtl-console\src\inc\crth.inc"/>
+        <EditorIndex Value="-1"/>
+        <TopLine Value="63"/>
+        <CursorPos X="11" Y="90"/>
+        <UsageCount Value="11"/>
+      </Unit3>
     </Units>
     <JumpHistory HistoryIndex="-1"/>
     <RunParams>

+ 12 - 5
samples/fpc/QuickConsole/ConsoleOut/ConsoleOut.pas

@@ -1,6 +1,8 @@
 program ConsoleOut;
 
+{$IFDEF MSWINDOWS}
 {$APPTYPE CONSOLE}
+{$ENDIF}
 
 {$MODE DELPHI}
 
@@ -12,16 +14,21 @@ uses
   Quick.Console;
 
 begin
+  {$IFDEF MSWINDOWS}
+  Application.Title:='ConsoleDemo';
+  {$ENDIF}
   try
-    coutXY(20,10,'this line will be replaced by the next',etInfo);
-    coutXY(20,10,'this line replaces previous',etSuccess);
+    Console.LogVerbose := LOG_DEBUG;
+    writeln('Console Out Example');
+    coutXY(10,10,'this line will be replaced by the next',etInfo);
+    coutXY(10,10,'this line replaces previous',etSuccess);
     cout('Normal line 1',etInfo);
     coutBL('bottom line: 1',etInfo);
-    cout('Normal line 2',etInfo);
+    cout('Normal line 2',etDebug);
     coutXY(10,5,'I''m here',etSuccess);
+    cout('Normal line 3',etSuccess);
     coutBL('bottom line: 2',etInfo);
-    cout('Normal line 3',etInfo);
-    coutBL('bottomline: 3',etInfo);
+    coutBL('bottom line: 3',etInfo);
     ConsoleWaitForEnterKey;
   except
     on E: Exception do

+ 89 - 6
samples/fpc/QuickConsole/ConsoleOut/backup/ConsoleOut.lps

@@ -4,21 +4,95 @@
     <PathDelim Value="\"/>
     <Version Value="11"/>
     <BuildModes Active="Default"/>
-    <Units Count="1">
+    <Units Count="4">
       <Unit0>
         <Filename Value="ConsoleOut.pas"/>
         <IsPartOfProject Value="True"/>
         <IsVisibleTab Value="True"/>
-        <CursorPos Y="4"/>
-        <UsageCount Value="20"/>
+        <CursorPos Y="15"/>
+        <UsageCount Value="25"/>
         <Loaded Value="True"/>
       </Unit0>
+      <Unit1>
+        <Filename Value="..\..\..\..\Quick.AppService.pas"/>
+        <EditorIndex Value="-1"/>
+        <TopLine Value="40"/>
+        <CursorPos X="29" Y="60"/>
+        <UsageCount Value="12"/>
+      </Unit1>
+      <Unit2>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <EditorIndex Value="1"/>
+        <TopLine Value="372"/>
+        <CursorPos Y="372"/>
+        <UsageCount Value="13"/>
+        <Loaded Value="True"/>
+      </Unit2>
+      <Unit3>
+        <Filename Value="D:\Lazarus\fpcsrc\packages\rtl-console\src\inc\crth.inc"/>
+        <EditorIndex Value="-1"/>
+        <TopLine Value="63"/>
+        <CursorPos X="11" Y="90"/>
+        <UsageCount Value="11"/>
+      </Unit3>
     </Units>
-    <JumpHistory Count="1">
+    <JumpHistory Count="14" HistoryIndex="13">
       <Position1>
-        <Filename Value="ConsoleOut.pas"/>
-        <Caret Line="12" Column="12"/>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="329" Column="50" TopLine="315"/>
       </Position1>
+      <Position2>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="251" Column="25" TopLine="223"/>
+      </Position2>
+      <Position3>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="270" Column="23" TopLine="227"/>
+      </Position3>
+      <Position4>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="251" Column="16" TopLine="227"/>
+      </Position4>
+      <Position5>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="270" Column="23" TopLine="227"/>
+      </Position5>
+      <Position6>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="241" Column="52" TopLine="227"/>
+      </Position6>
+      <Position7>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="255" Column="46" TopLine="238"/>
+      </Position7>
+      <Position8>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="247" Column="57" TopLine="226"/>
+      </Position8>
+      <Position9>
+        <Filename Value="ConsoleOut.pas"/>
+        <Caret Line="32" Column="15"/>
+      </Position9>
+      <Position10>
+        <Filename Value="ConsoleOut.pas"/>
+        <Caret Line="29" Column="44"/>
+      </Position10>
+      <Position11>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="149" Column="13" TopLine="124"/>
+      </Position11>
+      <Position12>
+        <Filename Value="ConsoleOut.pas"/>
+        <Caret Line="31"/>
+      </Position12>
+      <Position13>
+        <Filename Value="ConsoleOut.pas"/>
+        <Caret Line="32"/>
+      </Position13>
+      <Position14>
+        <Filename Value="..\..\..\..\Quick.Console.pas"/>
+        <Caret Line="349" TopLine="341"/>
+      </Position14>
     </JumpHistory>
     <RunParams>
       <FormatVersion Value="2"/>
@@ -26,6 +100,15 @@
     </RunParams>
   </ProjectSession>
   <Debugging>
+    <BreakPoints Count="1">
+      <Item1>
+        <Kind Value="bpkSource"/>
+        <WatchScope Value="wpsLocal"/>
+        <WatchKind Value="wpkWrite"/>
+        <Source Value="ConsoleOut.pas"/>
+        <Line Value="22"/>
+      </Item1>
+    </BreakPoints>
     <Watches Count="1">
       <Item1>
         <Expression Value="EventType"/>

+ 23 - 7
samples/fpc/QuickConsole/ConsoleOut/backup/ConsoleOut.pas

@@ -1,10 +1,10 @@
 program ConsoleOut;
 
+{$IFDEF MSWINDOWS}
 {$APPTYPE CONSOLE}
+{$ENDIF}
 
-{$IFDEF FPC}
 {$MODE DELPHI}
-{$ENDIF}
 
 {$R *.res}
 
@@ -13,17 +13,33 @@ uses
   Quick.Commons,
   Quick.Console;
 
+var
+  coord : TCoord;
+  x : Integer;
+  y : Integer;
 begin
+  {$IFDEF MSWINDOWS}
+  Application.Title:='ConsoleDemo';
+  {$ENDIF}
   try
-    coutXY(20,10,'this line will be replaced by the next',etInfo);
-    coutXY(20,10,'this line replaces previous',etSuccess);
+    Console.LogVerbose := LOG_DEBUG;
+    //x := 10;
+    //y := 10;
+    //coord.x := x;
+    //coord.y := y;
+    //SetCursorPos(coord);
+    writeln('Console Out Example');
+    coutxy(1,1,'hola',etinfo);
+    readln;
+    coutXY(10,10,'this line will be replaced by the next',etInfo);
+    coutXY(10,10,'this line replaces previous',etSuccess);
     cout('Normal line 1',etInfo);
     coutBL('bottom line: 1',etInfo);
-    cout('Normal line 2',etInfo);
+    cout('Normal line 2',etDebug);
     coutXY(10,5,'I''m here',etSuccess);
+    cout('Normal line 3',etSuccess);
     coutBL('bottom line: 2',etInfo);
-    cout('Normal line 3',etInfo);
-    coutBL('bottomline: 3',etInfo);
+    coutBL('bottom line: 3',etInfo);
     ConsoleWaitForEnterKey;
   except
     on E: Exception do

BIN
samples/fpc/QuickConsole/ConsoleOut/bin/x86_64-linux/ConsoleOut


BIN
samples/fpc/QuickConsole/ConsoleOut/lib/x86_64-linux/ConsoleOut.or


BIN
samples/fpc/QuickConsole/ConsoleOut/lib/x86_64-linux/ConsoleOut.res


+ 17 - 0
samples/fpc/QuickConsole/ConsoleOut/lib/x86_64-linux/Quick.Files.rsj

@@ -0,0 +1,17 @@
+{"version":1,"strings":[
+{"hash":137863719,"name":"quick.files.spathtoolong","sourcebytes":[84,104,101,32,115,112,101,99,105,102,105,101,100,32,112,97,116,104,32,105,115,32,116,111,111,32,108,111,110,103],"value":"The specified path is too long"},
+{"hash":185322788,"name":"quick.files.spathnotfound","sourcebytes":[84,104,101,32,115,112,101,99,105,102,105,101,100,32,112,97,116,104,32,119,97,115,32,110,111,116,32,102,111,117,110,100],"value":"The specified path was not found"},
+{"hash":244048660,"name":"quick.files.spathformatnotsupported","sourcebytes":[84,104,101,32,112,97,116,104,32,102,111,114,109,97,116,32,105,115,32,110,111,116,32,115,117,112,112,111,114,116,101,100],"value":"The path format is not supported"},
+{"hash":11731129,"name":"quick.files.sdirectorynotempty","sourcebytes":[84,104,101,32,115,112,101,99,105,102,105,101,100,32,100,105,114,101,99,116,111,114,121,32,105,115,32,110,111,116,32,101,109,112,116,121],"value":"The specified directory is not empty"},
+{"hash":152573107,"name":"quick.files.sdirectoryalreadyexists","sourcebytes":[84,104,101,32,115,112,101,99,105,102,105,101,100,32,100,105,114,101,99,116,111,114,121,32,97,108,114,101,97,100,121,32,101,120,105,115,116,115],"value":"The specified directory already exists"},
+{"hash":168635988,"name":"quick.files.sdirectoryinvalid","sourcebytes":[84,104,101,32,115,112,101,99,105,102,105,101,100,32,100,105,114,101,99,116,111,114,121,32,110,97,109,101,32,105,115,32,105,110,118,97,108,105,100],"value":"The specified directory name is invalid"},
+{"hash":39108953,"name":"quick.files.ssourcedirisdestdir","sourcebytes":[84,104,101,32,115,111,117,114,99,101,32,100,105,114,101,99,116,111,114,121,32,105,115,32,116,104,101,32,115,97,109,101,32,97,115,32,116,104,101,32,100,101,115,116,105,110,97,116,105,111,110,32,100,105,114,101,99,116,111,114,121],"value":"The source directory is the same as the destination directory"},
+{"hash":49776149,"name":"quick.files.ssourcefileisdestfile","sourcebytes":[84,104,101,32,115,111,117,114,99,101,32,102,105,108,101,32,105,115,32,116,104,101,32,115,97,109,101,32,97,115,32,116,104,101,32,100,101,115,116,105,110,97,116,105,111,110,32,102,105,108,101],"value":"The source file is the same as the destination file"},
+{"hash":52974101,"name":"quick.files.spathtofileneeded","sourcebytes":[84,104,101,32,112,97,116,104,32,109,117,115,116,32,115,112,101,99,105,102,121,32,97,32,102,105,108,101],"value":"The path must specify a file"},
+{"hash":71878101,"name":"quick.files.ssamerootdrive","sourcebytes":[84,104,101,32,115,111,117,114,99,101,32,97,110,100,32,100,101,115,116,105,110,97,116,105,111,110,32,112,97,116,104,115,32,109,117,115,116,32,99,111,110,116,97,105,110,32,116,104,101,32,115,97,109,101,32,114,111,111,116,32,100,114,105,118,101],"value":"The source and destination paths must contain the same root drive"},
+{"hash":135991396,"name":"quick.files.sdrivenotfound","sourcebytes":[84,104,101,32,100,114,105,118,101,32,99,97,110,110,111,116,32,98,101,32,102,111,117,110,100],"value":"The drive cannot be found"},
+{"hash":179128356,"name":"quick.files.sfilenotfound","sourcebytes":[84,104,101,32,115,112,101,99,105,102,105,101,100,32,102,105,108,101,32,119,97,115,32,110,111,116,32,102,111,117,110,100],"value":"The specified file was not found"},
+{"hash":18055283,"name":"quick.files.sfilealreadyexists","sourcebytes":[84,104,101,32,115,112,101,99,105,102,105,101,100,32,102,105,108,101,32,97,108,114,101,97,100,121,32,101,120,105,115,116,115],"value":"The specified file already exists"},
+{"hash":134045656,"name":"quick.files.sinvalidcharsinpath","sourcebytes":[73,110,118,97,108,105,100,32,99,104,97,114,97,99,116,101,114,115,32,105,110,32,112,97,116,104],"value":"Invalid characters in path"},
+{"hash":84576677,"name":"quick.files.sinvalidcharsinfilename","sourcebytes":[73,110,118,97,108,105,100,32,99,104,97,114,97,99,116,101,114,115,32,105,110,32,102,105,108,101,32,110,97,109,101],"value":"Invalid characters in file name"}
+]}