On Tue, May 13, 2003 at 05:45:45AM +0900, Simon Strandgaard wrote:
> BOOM = system+backquote outputs to stdout/stderr,  interfering with the
> output of the C/C++ application.
> 
> This interference is what to avoid. I want to be able to
> redirect Rubys output elsewhere: logfile/statusbox/text-buffer.

But the process you run with system/backquote is not Ruby, and therefore its
output is not Ruby's output. It's a completely different program, run using
fork + exec, but run in the same Unix environment as your main program (as
it should be: fork simply duplicates the existing process)

You can redirect Ruby's output elsewhere ($defout), but it's the child's
output that you are asking about.

Now, you can always replace 'system' with a function with runs a child,
captures its output, and forwards it to a Ruby IO object. Matz suggested
putting something like 'system2' in the the Ruby library for this purpose.

But then you say that you want to do this without modifying existing Ruby
code. You can always replace 'system' and 'backtick' with your own versions
if you wish. But I still don't think it makes sense.

The environment in which a Unix program runs looks, at its simplest, like
this:

                 MAIN
               +---------------------------------+
               |                                 |
               | FD                              |
stdin -------> | 0                               |
stdout<------- | 1                               |
stderr<------- | 2                               |
               |                                 |
               |                                 |
               +---------------------------------+

It can ignore stdin/stdout/stderr of course - as it would if it were a GUI
program, say.

Now, from this main program, you want to call a function, which happens to
be written in Ruby.

It must be true that either:

* This function makes no use of stdin/stdout/stderr at all. In that case
  there is no problem, agreed?

* This function *does* communicate on stdin/stdout/stderr.

So I think you are saying in the second case you want to run it in a
'sandbox', a simulated Unix environment where it gets its own private
stdin/stdout/stderr:

               +---------------------------------+
               |                                 |
               |               +-----------+     |
stdin -------> | 0      obj0-->| 0 Sandbox |     |
stdout<------- | 1      obj1<--| 1         |     |
stderr<------- | 2      obj2<--| 2         |     |
               |               +-----------+     |
               |                                 |
               +---------------------------------+

I think you are asking for the Ruby interpreter to be the sandbox itself,
and to trap every single possible case where output might be written to
stdout/stderr, and have it redirect to Ruby IO calls instead. In the general
case, this is simply not possible whilst the Sandbox is in the same process
as you, because it shares fd0/1/2 with the the main program (because both
Ruby and C *are* part of the same program), and therefore the program has a
way to write to fd0/1/2.

Rewriting 'system' and backtick would catch some cases, but certainly not
all.

The only way to guarantee trapping of all output on fd 1 in the Sandbox into
some string object 'obj1' is to do it at the Unix API layer. This means
creating a new pipe and pass one end of it to the code running in the
Sandbox as fd 1. This then *forces* you to use a separate process, because
it must be possible for the Sandbox to be doing work and writing to one end
of the pipe whilst the main program is reading the other end of the pipe.
(You could also use native threads, but Ruby doesn't play safely with
threads)

So to summarise:

Case 1: the Ruby code doesn't use stdout/stderr at all

  Solution 1:
  Trivial, nothing to do.
  (Most well-written libraries will be in this category)

Case 2: the Ruby code writes to stdout/stderr from Ruby, but does not in
turn run any C or external programs which would write directly to Unix fd
1/2 (which includes exec'ing another program using fork/exec, system,
backtick etc)

  Solution 2:
  Just reset $defout and now $deferr (thanks, Matz!)

Case 3: the Ruby code calls other programs (via embedded C calls or exec)
which in turn write directly to Unix stdout/stderr

  Solution 3a:
  Modify the Ruby code so that it captures that output itself at the 
  appropriate points (e.g. using IO.popen) if possible
or
  Solution 3b:
  When you call the Ruby code, first fork a subprocess which gets a whole
  new stdin/stdout/stderr on pipes (and therefore any C or external
  programs it calls will also communicate on those pipes). That's the
  solution 'capture_output' I gave before.

> The problem is fairly simple, but I doubt anyone understands me ???

If I do understand you, then you are asking for something impossible. I
think you would realise this if you understood the Unix execution
environment a little better.

Regards,

Brian.