View Issue Details

IDProjectCategoryView StatusLast Update
0036145LazarusIDEpublic2019-10-21 11:53
ReporterCudaText manAssigned ToJuha Manninen 
PrioritynormalSeverityminorReproducibilityalways
Status assignedResolutionreopened 
Product Version2.1 (SVN)Product Build 
Target VersionFixed in Version 
Summary0036145: IDE has 2 functions to read '/proc/NN/cmdline'
DescriptionFind in Files-> finds 2 places of '/cmdline'.
/home/user/lazarus/debugger/debugattachdialog.pas
/home/user/lazarus/debugger/debugattachdialog.pas (113,60) S := TFileStreamUTF8.Create('/proc/' + IntToStr(Pid) + '/cmdline', fmOpenRead or...
/home/user/lazarus/ide/ideguicmdline.pas
/home/user/lazarus/ide/ideguicmdline.pas (92,39) Filename:='/proc/'+IntToStr(aPID)+'/cmdline';

2 funcs do the same. One reads file via AssignFile, other reads file via TStringList.LoadFromFile.
DRY.
TagsNo tags attached.
Fixed in Revision
LazTarget-
Widgetset
Attached Files
  • smp.diff (3,093 bytes)
    Index: debugger/debugattachdialog.pas
    ===================================================================
    --- debugger/debugattachdialog.pas	(revision 62005)
    +++ debugger/debugattachdialog.pas	(working copy)
    @@ -101,23 +101,13 @@
     {$else}
     {$ifdef linux}
     uses
    -  LazUTF8Classes;
    +  LazUTF8Classes, FileUtil;
     
     function EnumerateProcesses(AList: TRunningProcessInfoList): boolean;
     
       function GetProcName(Pid: THandle): String;
    -  var
    -    S: TStream;
    -    Sz: Integer;
       begin
    -    S := TFileStreamUTF8.Create('/proc/' + IntToStr(Pid) + '/cmdline', fmOpenRead or fmShareDenyNone);
    -    try
    -      SetLength(Result, 255);
    -      Sz := S.Read(Result[1], 255);
    -      SetLength(Result, Sz);
    -    finally
    -      S.Free;
    -    end;
    +    Result := ReadFileToString('/proc/' + IntToStr(Pid) + '/cmdline');
       end;
     
     var
    Index: ide/ideguicmdline.pas
    ===================================================================
    --- ide/ideguicmdline.pas	(revision 62005)
    +++ ide/ideguicmdline.pas	(working copy)
    @@ -42,7 +42,7 @@
     uses
       Classes, SysUtils, math,
       // LazUtils
    -  LazUTF8Classes, LazUtilities, LazFileUtils,
    +  LazUTF8Classes, LazUtilities, LazFileUtils, FileUtil,
       // Codetools
       FileProcs,
       // IDE
    @@ -85,24 +85,10 @@
       {$IFDEF UseProcFileSystem}
       function CheckProcFileSystem: boolean;
       var
    -    sl: TStringList;
         Filename: String;
       begin
    -    Result:=false;
    -    Filename:='/proc/'+IntToStr(aPID)+'/cmdline';
    -    if not FileExists(Filename) then exit;
    -    sl:=TStringList.Create;
    -    try
    -      try
    -        sl.LoadFromFile(Filename);
    -        if sl.Count=0 then exit;
    -        if Pos('lazarus',lowercase(sl[0]))<1 then exit;
    -        Result:=true;
    -      except
    -      end;
    -    finally
    -      sl.Free;
    -    end;
    +    Filename := '/proc/'+IntToStr(aPID)+'/cmdline';
    +    Result := Pos('lazarus', LowerCase(ReadFileToString(Filename)))>0;
       end;
       {$ENDIF}
     
    Index: lcl/interfaces/gtk2/unitywsctrls.pas
    ===================================================================
    --- lcl/interfaces/gtk2/unitywsctrls.pas	(revision 62005)
    +++ lcl/interfaces/gtk2/unitywsctrls.pas	(working copy)
    @@ -10,7 +10,7 @@
     uses
       GLib2, Gtk2, Gdk2Pixbuf,
       Classes, SysUtils, dynlibs,
    -  Graphics, Controls, Forms, ExtCtrls, WSExtCtrls, LCLType, LazUTF8;
    +  Graphics, Controls, Forms, ExtCtrls, WSExtCtrls, LCLType, LazUTF8, FileUtil;
     
     { Changed priority, now use libappindicator_3 if available.  ~_3 is, nominally
       a Unity thing but Ubuntu and several other distros ship it (and support it) with
    @@ -254,18 +254,11 @@
     
     function NeedAppIndicator: boolean;
     var
    -  DeskTop,  VersionSt : String;
    -  ProcFile: TextFile;
    +  DeskTop, VersionSt: String;
     begin
    -  AssignFile(ProcFile, '/proc/version');
    -  try
    -    reset(ProcFile);
    -    readln(ProcFile, VersionSt);
    -  finally
    -    CloseFile(ProcFile)
    -  end;
       DeskTop := GetEnvironmentVariableUTF8('XDG_CURRENT_DESKTOP');
       if DeskTop = 'Unity' then exit(True);
    +  VersionSt := ReadFileToString('/proc/version');
       if (DeskTop = 'GNOME') and
         ( (pos('mageia', VersionSt) > 0) or
           (pos('Red Hat', VersionSt) > 0) or
    
    smp.diff (3,093 bytes)
  • new.diff (4,558 bytes)
    Index: components/lazutils/fileutil.inc
    ===================================================================
    --- components/lazutils/fileutil.inc	(revision 62005)
    +++ components/lazutils/fileutil.inc	(working copy)
    @@ -381,6 +381,35 @@
       end;
     end;
     
    +function ReadFileToStringFromVirtualFS(const Filename: string; MaxLineCount: integer=MaxInt): string;
    +const
    +  sEol = #10;
    +var
    +  f: TextFile;
    +  s: string;
    +  cnt: integer;
    +begin
    +  Result := '';
    +  {$Push}
    +  {$IOChecks off}
    +  AssignFile(f, Filename);
    +  Reset(f);
    +  if IOResult<>0 then exit;
    +  {$Pop}
    +  try
    +    cnt := 0;
    +    while not EOF(f) do
    +    begin
    +      ReadLn(f, s);
    +      Result := Result+s+sEol;
    +      Inc(cnt);
    +      if cnt>=MaxLineCount then Break;
    +    end;
    +  finally
    +    CloseFile(f);
    +  end;
    +end;
    +
     function SearchFileInPath(const Filename, BasePath, SearchPath,
       Delimiter: string; Flags: TSearchFileInPathFlags): string;
     var
    Index: components/lazutils/fileutil.pas
    ===================================================================
    --- components/lazutils/fileutil.pas	(revision 62005)
    +++ components/lazutils/fileutil.pas	(working copy)
    @@ -72,6 +72,7 @@
     function GetAllFilesMask: string; inline;
     function GetExeExt: string; inline;
     function ReadFileToString(const Filename: string): string;
    +function ReadFileToStringFromVirtualFS(const Filename: string; MaxLineCount: integer=MaxInt): string;
     
     // file search
     type
    Index: debugger/debugattachdialog.pas
    ===================================================================
    --- debugger/debugattachdialog.pas	(revision 62005)
    +++ debugger/debugattachdialog.pas	(working copy)
    @@ -101,23 +101,13 @@
     {$else}
     {$ifdef linux}
     uses
    -  LazUTF8Classes;
    +  LazUTF8Classes, FileUtil;
     
     function EnumerateProcesses(AList: TRunningProcessInfoList): boolean;
     
       function GetProcName(Pid: THandle): String;
    -  var
    -    S: TStream;
    -    Sz: Integer;
       begin
    -    S := TFileStreamUTF8.Create('/proc/' + IntToStr(Pid) + '/cmdline', fmOpenRead or fmShareDenyNone);
    -    try
    -      SetLength(Result, 255);
    -      Sz := S.Read(Result[1], 255);
    -      SetLength(Result, Sz);
    -    finally
    -      S.Free;
    -    end;
    +    Result := ReadFileToStringFromVirtualFS('/proc/' + IntToStr(Pid) + '/cmdline', 1);
       end;
     
     var
    Index: ide/ideguicmdline.pas
    ===================================================================
    --- ide/ideguicmdline.pas	(revision 62005)
    +++ ide/ideguicmdline.pas	(working copy)
    @@ -42,7 +42,7 @@
     uses
       Classes, SysUtils, math,
       // LazUtils
    -  LazUTF8Classes, LazUtilities, LazFileUtils,
    +  LazUTF8Classes, LazUtilities, LazFileUtils, FileUtil,
       // Codetools
       FileProcs,
       // IDE
    @@ -85,24 +85,10 @@
       {$IFDEF UseProcFileSystem}
       function CheckProcFileSystem: boolean;
       var
    -    sl: TStringList;
         Filename: String;
       begin
    -    Result:=false;
    -    Filename:='/proc/'+IntToStr(aPID)+'/cmdline';
    -    if not FileExists(Filename) then exit;
    -    sl:=TStringList.Create;
    -    try
    -      try
    -        sl.LoadFromFile(Filename);
    -        if sl.Count=0 then exit;
    -        if Pos('lazarus',lowercase(sl[0]))<1 then exit;
    -        Result:=true;
    -      except
    -      end;
    -    finally
    -      sl.Free;
    -    end;
    +    Filename := '/proc/'+IntToStr(aPID)+'/cmdline';
    +    Result := Pos('lazarus', LowerCase(ReadFileToStringFromVirtualFS(Filename, 1)))>0;
       end;
       {$ENDIF}
     
    Index: lcl/interfaces/gtk2/unitywsctrls.pas
    ===================================================================
    --- lcl/interfaces/gtk2/unitywsctrls.pas	(revision 62005)
    +++ lcl/interfaces/gtk2/unitywsctrls.pas	(working copy)
    @@ -10,7 +10,7 @@
     uses
       GLib2, Gtk2, Gdk2Pixbuf,
       Classes, SysUtils, dynlibs,
    -  Graphics, Controls, Forms, ExtCtrls, WSExtCtrls, LCLType, LazUTF8;
    +  Graphics, Controls, Forms, ExtCtrls, WSExtCtrls, LCLType, LazUTF8, FileUtil;
     
     { Changed priority, now use libappindicator_3 if available.  ~_3 is, nominally
       a Unity thing but Ubuntu and several other distros ship it (and support it) with
    @@ -254,18 +254,11 @@
     
     function NeedAppIndicator: boolean;
     var
    -  DeskTop,  VersionSt : String;
    -  ProcFile: TextFile;
    +  DeskTop, VersionSt: String;
     begin
    -  AssignFile(ProcFile, '/proc/version');
    -  try
    -    reset(ProcFile);
    -    readln(ProcFile, VersionSt);
    -  finally
    -    CloseFile(ProcFile)
    -  end;
       DeskTop := GetEnvironmentVariableUTF8('XDG_CURRENT_DESKTOP');
       if DeskTop = 'Unity' then exit(True);
    +  VersionSt := ReadFileToStringFromVirtualFS('/proc/version', 1);
       if (DeskTop = 'GNOME') and
         ( (pos('mageia', VersionSt) > 0) or
           (pos('Red Hat', VersionSt) > 0) or
    
    new.diff (4,558 bytes)
  • tst-ReadFileToString.zip (2,368 bytes)
  • tst-ReadFileToString2.zip (2,334 bytes)

Activities

CudaText man

2019-10-08 08:44

reporter   ~0118404

Here is the patch which uses FileUtil.ReadFileToString.

smp.diff (3,093 bytes)
Index: debugger/debugattachdialog.pas
===================================================================
--- debugger/debugattachdialog.pas	(revision 62005)
+++ debugger/debugattachdialog.pas	(working copy)
@@ -101,23 +101,13 @@
 {$else}
 {$ifdef linux}
 uses
-  LazUTF8Classes;
+  LazUTF8Classes, FileUtil;
 
 function EnumerateProcesses(AList: TRunningProcessInfoList): boolean;
 
   function GetProcName(Pid: THandle): String;
-  var
-    S: TStream;
-    Sz: Integer;
   begin
-    S := TFileStreamUTF8.Create('/proc/' + IntToStr(Pid) + '/cmdline', fmOpenRead or fmShareDenyNone);
-    try
-      SetLength(Result, 255);
-      Sz := S.Read(Result[1], 255);
-      SetLength(Result, Sz);
-    finally
-      S.Free;
-    end;
+    Result := ReadFileToString('/proc/' + IntToStr(Pid) + '/cmdline');
   end;
 
 var
Index: ide/ideguicmdline.pas
===================================================================
--- ide/ideguicmdline.pas	(revision 62005)
+++ ide/ideguicmdline.pas	(working copy)
@@ -42,7 +42,7 @@
 uses
   Classes, SysUtils, math,
   // LazUtils
-  LazUTF8Classes, LazUtilities, LazFileUtils,
+  LazUTF8Classes, LazUtilities, LazFileUtils, FileUtil,
   // Codetools
   FileProcs,
   // IDE
@@ -85,24 +85,10 @@
   {$IFDEF UseProcFileSystem}
   function CheckProcFileSystem: boolean;
   var
-    sl: TStringList;
     Filename: String;
   begin
-    Result:=false;
-    Filename:='/proc/'+IntToStr(aPID)+'/cmdline';
-    if not FileExists(Filename) then exit;
-    sl:=TStringList.Create;
-    try
-      try
-        sl.LoadFromFile(Filename);
-        if sl.Count=0 then exit;
-        if Pos('lazarus',lowercase(sl[0]))<1 then exit;
-        Result:=true;
-      except
-      end;
-    finally
-      sl.Free;
-    end;
+    Filename := '/proc/'+IntToStr(aPID)+'/cmdline';
+    Result := Pos('lazarus', LowerCase(ReadFileToString(Filename)))>0;
   end;
   {$ENDIF}
 
Index: lcl/interfaces/gtk2/unitywsctrls.pas
===================================================================
--- lcl/interfaces/gtk2/unitywsctrls.pas	(revision 62005)
+++ lcl/interfaces/gtk2/unitywsctrls.pas	(working copy)
@@ -10,7 +10,7 @@
 uses
   GLib2, Gtk2, Gdk2Pixbuf,
   Classes, SysUtils, dynlibs,
-  Graphics, Controls, Forms, ExtCtrls, WSExtCtrls, LCLType, LazUTF8;
+  Graphics, Controls, Forms, ExtCtrls, WSExtCtrls, LCLType, LazUTF8, FileUtil;
 
 { Changed priority, now use libappindicator_3 if available.  ~_3 is, nominally
   a Unity thing but Ubuntu and several other distros ship it (and support it) with
@@ -254,18 +254,11 @@
 
 function NeedAppIndicator: boolean;
 var
-  DeskTop,  VersionSt : String;
-  ProcFile: TextFile;
+  DeskTop, VersionSt: String;
 begin
-  AssignFile(ProcFile, '/proc/version');
-  try
-    reset(ProcFile);
-    readln(ProcFile, VersionSt);
-  finally
-    CloseFile(ProcFile)
-  end;
   DeskTop := GetEnvironmentVariableUTF8('XDG_CURRENT_DESKTOP');
   if DeskTop = 'Unity' then exit(True);
+  VersionSt := ReadFileToString('/proc/version');
   if (DeskTop = 'GNOME') and
     ( (pos('mageia', VersionSt) > 0) or
       (pos('Red Hat', VersionSt) > 0) or
smp.diff (3,093 bytes)

CudaText man

2019-10-08 08:50

reporter   ~0118405

Last edited: 2019-10-08 08:50

View 2 revisions

Don't apply the patch yet-- on my Ubuntu 18.x, FileUtil.ReadFileToString('/proc/version') gets empty str. Seems FS gets filesize=0, so ReadFileToString gets empty.

Bart Broersma

2019-10-08 13:43

developer   ~0118413

Does it really matter that the same result is obtained in 2 different (but related) ways?

CudaText man

2019-10-08 14:35

reporter   ~0118418

I added new func FileUtil.ReadFileToStringFromVirtualFS - to use it with '/proc/version' and '/proc/N/cmdline'.
I tested it also with multiline file '/proc/meminfo'.
New.diff is ok, seems.

new.diff (4,558 bytes)
Index: components/lazutils/fileutil.inc
===================================================================
--- components/lazutils/fileutil.inc	(revision 62005)
+++ components/lazutils/fileutil.inc	(working copy)
@@ -381,6 +381,35 @@
   end;
 end;
 
+function ReadFileToStringFromVirtualFS(const Filename: string; MaxLineCount: integer=MaxInt): string;
+const
+  sEol = #10;
+var
+  f: TextFile;
+  s: string;
+  cnt: integer;
+begin
+  Result := '';
+  {$Push}
+  {$IOChecks off}
+  AssignFile(f, Filename);
+  Reset(f);
+  if IOResult<>0 then exit;
+  {$Pop}
+  try
+    cnt := 0;
+    while not EOF(f) do
+    begin
+      ReadLn(f, s);
+      Result := Result+s+sEol;
+      Inc(cnt);
+      if cnt>=MaxLineCount then Break;
+    end;
+  finally
+    CloseFile(f);
+  end;
+end;
+
 function SearchFileInPath(const Filename, BasePath, SearchPath,
   Delimiter: string; Flags: TSearchFileInPathFlags): string;
 var
Index: components/lazutils/fileutil.pas
===================================================================
--- components/lazutils/fileutil.pas	(revision 62005)
+++ components/lazutils/fileutil.pas	(working copy)
@@ -72,6 +72,7 @@
 function GetAllFilesMask: string; inline;
 function GetExeExt: string; inline;
 function ReadFileToString(const Filename: string): string;
+function ReadFileToStringFromVirtualFS(const Filename: string; MaxLineCount: integer=MaxInt): string;
 
 // file search
 type
Index: debugger/debugattachdialog.pas
===================================================================
--- debugger/debugattachdialog.pas	(revision 62005)
+++ debugger/debugattachdialog.pas	(working copy)
@@ -101,23 +101,13 @@
 {$else}
 {$ifdef linux}
 uses
-  LazUTF8Classes;
+  LazUTF8Classes, FileUtil;
 
 function EnumerateProcesses(AList: TRunningProcessInfoList): boolean;
 
   function GetProcName(Pid: THandle): String;
-  var
-    S: TStream;
-    Sz: Integer;
   begin
-    S := TFileStreamUTF8.Create('/proc/' + IntToStr(Pid) + '/cmdline', fmOpenRead or fmShareDenyNone);
-    try
-      SetLength(Result, 255);
-      Sz := S.Read(Result[1], 255);
-      SetLength(Result, Sz);
-    finally
-      S.Free;
-    end;
+    Result := ReadFileToStringFromVirtualFS('/proc/' + IntToStr(Pid) + '/cmdline', 1);
   end;
 
 var
Index: ide/ideguicmdline.pas
===================================================================
--- ide/ideguicmdline.pas	(revision 62005)
+++ ide/ideguicmdline.pas	(working copy)
@@ -42,7 +42,7 @@
 uses
   Classes, SysUtils, math,
   // LazUtils
-  LazUTF8Classes, LazUtilities, LazFileUtils,
+  LazUTF8Classes, LazUtilities, LazFileUtils, FileUtil,
   // Codetools
   FileProcs,
   // IDE
@@ -85,24 +85,10 @@
   {$IFDEF UseProcFileSystem}
   function CheckProcFileSystem: boolean;
   var
-    sl: TStringList;
     Filename: String;
   begin
-    Result:=false;
-    Filename:='/proc/'+IntToStr(aPID)+'/cmdline';
-    if not FileExists(Filename) then exit;
-    sl:=TStringList.Create;
-    try
-      try
-        sl.LoadFromFile(Filename);
-        if sl.Count=0 then exit;
-        if Pos('lazarus',lowercase(sl[0]))<1 then exit;
-        Result:=true;
-      except
-      end;
-    finally
-      sl.Free;
-    end;
+    Filename := '/proc/'+IntToStr(aPID)+'/cmdline';
+    Result := Pos('lazarus', LowerCase(ReadFileToStringFromVirtualFS(Filename, 1)))>0;
   end;
   {$ENDIF}
 
Index: lcl/interfaces/gtk2/unitywsctrls.pas
===================================================================
--- lcl/interfaces/gtk2/unitywsctrls.pas	(revision 62005)
+++ lcl/interfaces/gtk2/unitywsctrls.pas	(working copy)
@@ -10,7 +10,7 @@
 uses
   GLib2, Gtk2, Gdk2Pixbuf,
   Classes, SysUtils, dynlibs,
-  Graphics, Controls, Forms, ExtCtrls, WSExtCtrls, LCLType, LazUTF8;
+  Graphics, Controls, Forms, ExtCtrls, WSExtCtrls, LCLType, LazUTF8, FileUtil;
 
 { Changed priority, now use libappindicator_3 if available.  ~_3 is, nominally
   a Unity thing but Ubuntu and several other distros ship it (and support it) with
@@ -254,18 +254,11 @@
 
 function NeedAppIndicator: boolean;
 var
-  DeskTop,  VersionSt : String;
-  ProcFile: TextFile;
+  DeskTop, VersionSt: String;
 begin
-  AssignFile(ProcFile, '/proc/version');
-  try
-    reset(ProcFile);
-    readln(ProcFile, VersionSt);
-  finally
-    CloseFile(ProcFile)
-  end;
   DeskTop := GetEnvironmentVariableUTF8('XDG_CURRENT_DESKTOP');
   if DeskTop = 'Unity' then exit(True);
+  VersionSt := ReadFileToStringFromVirtualFS('/proc/version', 1);
   if (DeskTop = 'GNOME') and
     ( (pos('mageia', VersionSt) > 0) or
       (pos('Red Hat', VersionSt) > 0) or
new.diff (4,558 bytes)

Juha Manninen

2019-10-10 18:05

developer   ~0118469

Your function ReadFileToStringFromVirtualFS has some serious issues.

1. It potentially changes data instead of just reading it in. It reads line by line and appends a Unix style sEol = 0000010 to each line.

2. The function assumes newlines as separator. It reads line by line and has a MaxLineCount parameter. However text format does not imply newlines. For example XML does not care about them. Some XML files have newlines for readability, some don't. Think how your function would behave then.
Name ReadLineBasedFileToString would fit better for it. Besides, TStringList already provides a good interface for reading line based data.

3. The name has "FromVirtualFS" although the function has nothing to do with virtual file systems. Extremely confusing and misleading! Uhhh...

4. The motive for this function was to circumvent a bug in function ReadFileToString. Why not fix it instead? Name AnotherReadFileToStringInsteadOfFixingOriginal would fit better then. :)

So please fix function ReadFileToString. I understood the problem is the FileSize() call which returns 0 for virtual files. It can be eliminated by reading big chunks of data inside a loop and adjusting the string's length dynamically. After the last cycle it would be adjusted to its final length. Most files would fit into one chunk.
There should be a MaxSize parameter with rather big default value, maybe 100k or 1M bytes. Some log etc. files are huge. A user would have to provide a big MaxSize if he wants it all.
This change is backwards compatible because the new parameter has a default value. The function is not used anywhere in Lazarus sources but may be used in user code.

In general, please pay attention to intuitive API design, including function interfaces and names.

CudaText man

2019-10-10 20:30

reporter   ~0118475

Okaaay. I made the new function, and tested it on my Ubuntu. On single line and multiline files from /proc/..... It works. See the demo, and if Ok, push it together with helper func. Then we'll continue

tst-ReadFileToString.zip (2,368 bytes)

CudaText man

2019-10-10 20:36

reporter   ~0118477

Btw, i could not make this func using "file of byte" or "file" types for BlockRead, in that cases i always got IOResult=5 (Access denied) so i 've used TextFile type (IOResult is ok).

Juha Manninen

2019-10-12 22:02

developer   ~0118531

Your function looks extremely slow. It calls Realloc() for a buffer before every byte of a file. Did you test the speed?
Why do you use TextFile + TStream instead of TFileStream?

This all seems rather useless. There is no bug to fix. I believe somebody already has an optimized version of ReadFileToString. We can use such if found.
Otherwise let's concentrate on actual bugs and features. There are plenty.
Resolving...

CudaText man

2019-10-13 10:22

reporter   ~0118553

Here is another trying. seems OK now?

tst-ReadFileToString2.zip (2,334 bytes)

Issue History

Date Modified Username Field Change
2019-10-08 08:17 CudaText man New Issue
2019-10-08 08:44 CudaText man File Added: smp.diff
2019-10-08 08:44 CudaText man Note Added: 0118404
2019-10-08 08:50 CudaText man Note Added: 0118405
2019-10-08 08:50 CudaText man Note Edited: 0118405 View Revisions
2019-10-08 13:43 Bart Broersma Note Added: 0118413
2019-10-08 14:35 CudaText man File Added: new.diff
2019-10-08 14:35 CudaText man Note Added: 0118418
2019-10-10 18:05 Juha Manninen Note Added: 0118469
2019-10-10 20:30 CudaText man File Added: tst-ReadFileToString.zip
2019-10-10 20:30 CudaText man Note Added: 0118475
2019-10-10 20:36 CudaText man Note Added: 0118477
2019-10-12 22:02 Juha Manninen Note Added: 0118531
2019-10-12 22:03 Juha Manninen Assigned To => Juha Manninen
2019-10-12 22:03 Juha Manninen Status new => resolved
2019-10-12 22:03 Juha Manninen Resolution open => no change required
2019-10-12 22:03 Juha Manninen LazTarget => -
2019-10-13 10:22 CudaText man File Added: tst-ReadFileToString2.zip
2019-10-13 10:22 CudaText man Note Added: 0118553
2019-10-13 10:22 CudaText man Status resolved => assigned
2019-10-13 10:22 CudaText man Resolution no change required => reopened