Lazarus

Free Pascal => General => Topic started by: Fred vS on April 22, 2021, 09:22:33 pm

Title: freopen and fpc?
Post by: Fred vS on April 22, 2021, 09:22:33 pm
Hello.

Does have fpc the equivalent of that C method?
(It is about to redirect messages from stderr.

Code: C  [Select][+][-]
  1. #include <stdio.h>
  2. freopen("error.log", "w", stderr);

Thanks.

Fre;D
Title: Re: freopen and fpc?
Post by: devEric69 on April 22, 2021, 10:48:15 pm
Hello,

I don't have the answer, but there are tracks... ( source: http://www.delphigroups.info/2/db/506471.html (http://www.delphigroups.info/2/db/506471.html), http://www.delphigroups.info/2/32/118674.html (http://www.delphigroups.info/2/32/118674.html) ):
- on Linux, we should have to look for code with 'stderr' (the string, of the homonymous file name stored in the /dev directory, apparently);
- on Windows, we should have to use the MS API GetStdhandle or SetStdhandle;
... which leads to the FpRedir.pas unit, and functions like ChangeRedirError. But, I've never used them.
Title: Re: freopen and fpc?
Post by: winni on April 22, 2021, 10:51:36 pm
Hi!

What do you want to achieve?

You  know that there are system.StdOut, system.StdIn and system.StdErr?

Code: Pascal  [Select][+][-]
  1. writeln (StdErr,'Error 123');

Winni
Title: Re: freopen and fpc?
Post by: Fred vS on April 22, 2021, 11:43:46 pm
Hi!

What do you want to achieve?

You  know that there are system.StdOut, system.StdIn and system.StdErr?

Code: Pascal  [Select][+][-]
  1. writeln (StdErr,'Error 123');

Winni

Hello Winni.

Some libraries, for example PortAudio, generate lot of debug-warnings when running it via console.

For example: Audacity, running it via terminal:

Code: Bash  [Select][+][-]
  1. fred@fiens ~> audacity

Gives this on the terminal:
Quote
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2565:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_route.c:869:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:869:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:869:(find_matching_chmap) Found no matching channel map
ALSA lib pcm_route.c:869:(find_matching_chmap) Found no matching channel map
Cannot connect to server socket err = Aucun fichier ou dossier de ce type
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
Cannot connect to server socket err = Aucun fichier ou dossier de ce type
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
ALSA lib pcm_oss.c:377:(_snd_pcm_oss_open) Unknown field port
ALSA lib pcm_oss.c:377:(_snd_pcm_oss_open) Unknown field port
ALSA lib pcm_a52.c:823:(_snd_pcm_a52_open) a52 is only for playback
ALSA lib pcm_usb_stream.c:486:(_snd_pcm_usb_stream_open) Invalid type for card
ALSA lib pcm_usb_stream.c:486:(_snd_pcm_usb_stream_open) Invalid type for card
Cannot connect to server socket err = Aucun fichier ou dossier de ce type
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for -1, skipping unlock
Expression 'AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 1904
Expression 'PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2171
Expression 'PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate, framesPerBuffer, callback, streamFlags, userData )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2840
Expression 'ret' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 1736
Expression 'AlsaOpen( &alsaApi->baseHostApiRep, params, streamDir, &self->pcm )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 1904
Expression 'PaAlsaStreamComponent_Initialize( &self->capture, alsaApi, inParams, StreamDirection_In, NULL != callback )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2171
Expression 'PaAlsaStream_Initialize( stream, alsaHostApi, inputParameters, outputParameters, sampleRate, framesPerBuffer, callback, streamFlags, userData )' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 2840
Expression 'ret' failed in 'src/hostapi/alsa/pa_linux_alsa.c', line: 1736

Of course you may redirect the output to a file using that parameter:

Code: Bash  [Select][+][-]
  1. fred@fiens ~> audacity 2>> error.log

And now the goal:
In C you may use at init of your program this:

Code: C  [Select][+][-]
  1.     #include <stdio.h>
  2.     freopen("error.log", "w", stderr);

In that case, all the StdErr messages, even from library used, will be redirected to error.log.
It is the same as running in terminal "myprog 2>> error.log" but it is instructed by the program.

How to do that with fpc?

Fre;D
Title: Re: freopen and fpc?
Post by: Warfley on April 23, 2021, 03:24:23 am
You can use AssignFile:
Code: Pascal  [Select][+][-]
  1.   AssignFile(StdErr, 'stderr.txt');
  2.   Rewrite(StdErr); // Don't know if required
  3.   WriteLn(StdErr, 'Hello World'); // will be written to stderr.txt

Alternatively, using from unit StreamIO  (https://lazarus-ccr.sourceforge.io/docs/fcl/streamio/index.html)the function AssignStream  (https://lazarus-ccr.sourceforge.io/docs/fcl/streamio/assignstream.html)you can redirect the file directly to any stream you like:
Code: Pascal  [Select][+][-]
  1. uses
  2.   Classes, StreamIO;
  3.  
  4. var
  5.   errFile: TFileStream;
  6. begin
  7.   errFile := TFileStream.Create('stderr.txt', fmOpenReadWrite or fmCreate);
  8.   try
  9.     AssignStream(StdErr, errFile);
  10.     Rewrite(StdErr);
  11.     WriteLn(StdErr, 'Hello stderr');
  12.     Flush(StdErr);
  13.   finally
  14.     errFile.Free;
  15.   end;
This way you can also redirect it into a memory stream, a string stream, or your own stream class that writes it to the GUI.
Title: Re: freopen and fpc?
Post by: MarkMLl on April 23, 2021, 08:57:19 am
In that case, all the StdErr messages, even from library used, will be redirected to error.log.
It is the same as running in terminal "myprog 2>> error.log" but it is instructed by the program.

OK, so trying to focus on what I think at least one other person has missed: it's concatenating to an existing text file.

Checking working code here:

Code: Pascal  [Select][+][-]
  1.     AssignFile(statusText, filename);
  2.     if FileExists(filename) then
  3.       Append(statusText)
  4.     else
  5.       Rewrite(statusText);
  6.     if IOResult <> 0 then
  7.       statusText := ErrOutput
  8.  

There isn't an fpreopen(), which arguably could be reported as a bug.

MarkMLl
Title: Re: freopen and fpc?
Post by: Fred vS on April 23, 2021, 01:51:18 pm
@devEric69 : thanks for the infos!

@Warfley: I try this:

Code: Pascal  [Select][+][-]
  1. program errorlog;
  2. ...
  3. begin
  4.    Assign(StdErr, 'error.log');
  5.    Rewrite(StdErr);
  6.   ...
  7.    Application.run;
  8.    Close(StdErr);
  9.  end.

A empty error.log is created but console-messages are still there.

@MarkMLI: 
Quote
There isn't an fpreopen()

Imho it should make things easier.

Fre;D
Title: Re: freopen and fpc?
Post by: Thaddy on April 23, 2021, 02:07:53 pm
If you want to keep the info, do not just use rewrite, but eof().
Rewrite resets pointer to the begin of a file, amongst other issues.
Title: Re: freopen and fpc?
Post by: Fred vS on April 23, 2021, 02:11:20 pm
Quote
OK, so trying to focus on what I think at least one other person has missed: it's concatenating to an existing text file.

No, I wanted to know how to hide the error message that appear in terminal and redirect it to a text file.
Title: Re: freopen and fpc?
Post by: Fred vS on April 23, 2021, 02:18:25 pm
If you want to keep the info, do not just use rewrite, but eof().
Rewrite resets pointer to the begin of a file, amongst other issues.

Hello Thaddy.

Please try this in terminal and see the result on the terminal:

Code: Pascal  [Select][+][-]
  1. $ audacity

Then try this:

Code: Pascal  [Select][+][-]
  1. $ audacity 2>> error.log

You will not see message on console but a log file created with all the infos.

In C they do like this:

Code: C  [Select][+][-]
  1. #include <stdio.h>
  2.     freopen("error.log", "w", stderr);

How to do that in Pascal code.
Title: Re: freopen and fpc?
Post by: trev on April 24, 2021, 03:41:03 am
Fred, just a thought: are you sure the console text you want to redirect is being sent to StdErr?
Title: Re: freopen and fpc?
Post by: Fred vS on April 24, 2021, 04:09:06 am
Fred, just a thought: are you sure the console text you want to redirect is being sent to StdErr?

Hello Trev.

Yes.

Doing this:

Code: Pascal  [Select][+][-]
  1. $ audacity 2>> log.txt

Redirect all StdErr generated by the application, included the StdErr generated by the dependencies and libraries.

For code, to do this, c++ on gcc uses the method freopen() from stdio.h
https://www.cplusplus.com/reference/cstdio/freopen/

Sadly, freopen() is not implemented by fpc.

So I would be very happy if somebody gives me light how to implement it.
It seems that it comes from the standard C library.

Fre;D
Title: Re: freopen and fpc?
Post by: devEric69 on April 24, 2021, 02:46:25 pm
Hello @all,

Just as an anecdote, I sometimes use the tee (https://fr.wikipedia.org/wiki/Tee_(Unix)) command (available for nix* OSes), in bash scripts, which allows to redirect stdin and stdout and stderr to each others, or towards a third text file. So, wondering me how this could be written in Lazarus, I'm interested in any possible answers to this question.
Title: Re: freopen and fpc?
Post by: lucamar on April 24, 2021, 03:20:52 pm
Just as an anecdote, I sometimes use the tee (https://fr.wikipedia.org/wiki/Tee_(Unix)) command (available for nix* OSes), in bash scripts, which allows to redirect stdin and stdout and stderr to each others, or towards a third text file. So, wondering me how this could be written in Lazarus, I'm interested in any possible answers to this question.

That's relatively easy. Open two streams, one for whatever you want to redirect  and another for the file, and write the same to both streams.

Just a small correction: what the standard tee does is to read stdin and write to both stdout and a file. Basically something like:

Code: Pascal  [Select][+][-]
  1. while not eof(Input) do begin
  2.   ReadLn(whatever); {or: Read(somestring)}
  3.   WriteLn(whatever); {or: Write(somestring)}
  4.   WriteLn(AFile, whatever); {or: Write(AFile, somestring);}
  5. end;

Any other "redirection" is done by the shell itself, which is an expert on that ;)

That's why tee is most often used as:
Code: Bash  [Select][+][-]
  1. somecommand | tee somefile.log
Title: Re: freopen and fpc?
Post by: Warfley on April 24, 2021, 05:19:51 pm
I think the problem is the following, when using Assign, AssignFile or AssignStream, what happens is that the handling of the "Text" object in pascal is changed. The problem is that the other code seems to send the data to stderr not via the pascal Text object, but rather via the filedescriptor and the operating system commands (e.g. fwrite on posix), thereby circumventing the Text handler completely.

So to target this problem you need to change the file handling on the OS level. I don't know much about the windows API with regards to this topic, but on POSIX systems you have the dup2 command available: Link (https://www.man7.org/linux/man-pages/man2/dup.2.html). dup2 can be thought of as a form of redirect meaning dup2(filedescriptor, stderr) will make every write to stderr be a write to the same file as filedescriptor.

So you can try out something like this:
Code: Pascal  [Select][+][-]
  1. uses BaseUnix;
  2. ...
  3. var errFileHandle: Integer;
  4.  
  5. function MapStdErrToFile(const FileName: String): Boolean;
  6. begin
  7.   errFileHandle := FpOpen(FileName, O_Creat, O_WrOnly);
  8.   Result := FpDup2(errFileHandle, StdErrorHandle) >= 0;
  9. end;
Title: Re: freopen and fpc?
Post by: Soner on April 24, 2021, 06:43:29 pm
@Warfley: I try this:

Code: Pascal  [Select][+][-]
  1. program errorlog;
  2. ...
  3. begin
  4.    Assign(StdErr, 'error.log');
  5.    Rewrite(StdErr);
  6.   ...
  7.    Application.run;
  8.    Close(StdErr);
  9.  end.

A empty error.log is created but console-messages are still there.


Warfleys recommendation works on Windows. I see no messages on console:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  7.   cthreads,
  8.   {$ENDIF}{$ENDIF}
  9.   Classes, SysUtils
  10.   { you can add units after this };
  11.  
  12. {$DEFINE ERRLOGFILE}  //<--undefine for errors on console
  13. begin
  14.   try
  15.   {$IfDef ERRLOGFILE}
  16.   AssignFile(StdErr, 'stderr.txt');  //<-- CHANGE PATH
  17.   Rewrite(StdErr);
  18.   {$endif}
  19.  
  20.   WriteLn(StdErr,'error');
  21.  
  22.   {$IfDef ERRLOGFILE}
  23.   CloseFile(StdErr);
  24.   {$endif}
  25.  
  26.   except
  27.     on E: EInOutError do
  28.         Writeln('File handling error occurred. Details: ', E.ClassName, '/', E.Message);
  29.   end;
  30.  
  31.  
  32.  {$IfNDef ERRLOGFILE}
  33.   Writeln(StdOut,'Look at Errorfile');
  34.  {$endif}
  35.   readln;
  36. end.
  37.  
Title: Re: freopen and fpc?
Post by: Fred vS on April 24, 2021, 07:59:52 pm
@Warfley: Thanks, I will study your code, write you later.

@Soner: Thanks too but what is the difference with my original code?
Like explained, of course this will work for the errors generate by fpc himself, but all other errors from dependencies or libraries will not be redirected and you will see them on console, for Windows and Unix too.

Fre;D
Title: Re: freopen and fpc?
Post by: Fred vS on April 25, 2021, 11:42:13 am
I think the problem is the following, when using Assign, AssignFile or AssignStream, what happens is that the handling of the "Text" object in pascal is changed. The problem is that the other code seems to send the data to stderr not via the pascal Text object, but rather via the filedescriptor and the operating system commands (e.g. fwrite on posix), thereby circumventing the Text handler completely.

So to target this problem you need to change the file handling on the OS level. I don't know much about the windows API with regards to this topic, but on POSIX systems you have the dup2 command available: Link (https://www.man7.org/linux/man-pages/man2/dup.2.html). dup2 can be thought of as a form of redirect meaning dup2(filedescriptor, stderr) will make every write to stderr be a write to the same file as filedescriptor.

So you can try out something like this:
Code: Pascal  [Select][+][-]
  1. uses BaseUnix;
  2. ...
  3. var errFileHandle: Integer;
  4.  
  5. function MapStdErrToFile(const FileName: String): Boolean;
  6. begin
  7.  
  8.  errFileHandle := FpOpen(FileName, O_Creat, O_WrOnly);
  9.   Result := FpDup2(errFileHandle, StdErrorHandle) >= 0;
  10. end;


Hello Warfley.

I tested your code: there are excellent news (and other less good).

Doing this:

Code: Pascal  [Select][+][-]
  1. program errorlog;
  2.     ...
  3.     var
  4.      errFileHandle: Integer;
  5.     begin
  6.        if fileexists('error.log') then deletefile('error.log');  // needed otherwise works only once
  7.  
  8.       errFileHandle := FpOpen('error.log', O_Creat, O_WrOnly);
  9.        FpDup2(errFileHandle, StdErrorHandle);
  10.         ...
  11.        Application.run;
  12.     end.

Does not show any message on console.  ;D

But the error.log file is empty and cannot be accessed (no permission).

I did change with FpOpen('error.log', O_Creat, O_RDWR); but did not help, the file is still empty.

Also it works only once, when 'error.log' was created it must be deleted before to re-run the application otherwise message appears again.

Fre;D





Title: Re: freopen and fpc?
Post by: Warfley on April 25, 2021, 04:25:28 pm
Ok, fpOpen seems to be weird, but you can use filestreams an grab the handle:
Code: Pascal  [Select][+][-]
  1. uses
  2.   Classes, BaseUnix;
  3.  
  4. var
  5.   fs: TFileStream;
  6. begin
  7.   fs := TFileStream.Create('error.txt', fmOpenReadWrite or fmCreate);
  8.   try
  9.     FpDup2(fs.Handle, StdErrorHandle);
  10.     FpWrite(StdErrorHandle, 'Hello World'#10, 12);
  11.   finally
  12.     fs.Free;
  13.   end;
  14. end.
  15.  

Works fine on my Linux
Title: Re: freopen and fpc?
Post by: Fred vS on April 25, 2021, 04:57:21 pm
Ok, fpOpen seems to be weird, but you can use filestreams an grab the handle:
Code: Pascal  [Select][+][-]
  1. uses
  2.   Classes, BaseUnix;
  3.  
  4. var
  5.   fs: TFileStream;
  6. begin
  7.   fs := TFileStream.Create('error.txt', fmOpenReadWrite or fmCreate);
  8.   try
  9.     FpDup2(fs.Handle, StdErrorHandle);
  10.     FpWrite(StdErrorHandle, 'Hello World'#10, 12);
  11.   finally
  12.     fs.Free;
  13.   end;
  14. end.
  15.  

Works fine on my Linux

Hello Warfley.

Yep, you are still the Greatest Champion.

with this:

Code: Pascal  [Select][+][-]
  1. program errorlog;
  2.         ...
  3.         var
  4.          fs: TFileStream;
  5.         begin
  6.            fs := TFileStream.Create('error.log', fmOpenReadWrite or fmCreate);
  7.            FpDup2(fs.Handle, StdErrorHandle);
  8.            ...
  9.            Application.run;
  10.            fs.free;
  11.         end.

No more error messages on console but a beautiful error.log file with all the messages from libraries and dependencies included, no memory leak.

That's the way I like it.  ;)

Many thanks Warfley.

Fre;D
TinyPortal © 2005-2018