From: jcipar
Subject: sb-ext:run-program
Date: 
Message-ID: <8e1bcc99-f1cb-4da7-9ebe-653e862bccfd@r60g2000hsc.googlegroups.com>
I'm trying to run an external program and connect to its inputs and
outputs.  I can run simple programs like "ls" where there is only
output, but I can't get the input to work.  For example, I wrote a
simple test program:

#include <stdio.h>
int main(void)
{
  char name[1024] = "no name";
  printf("I'm a computer, what is your name?\n");
  fflush(stdout);
  scanf("%s", name);
  printf("well hello %s.  have a good day\n");
  fflush(stdout);
  return 0;
}

And I am trying to run it with this:

(defun run-test-program ()
  (run-program "/home/jim/test"
	       '() :wait nil :input :stream :output :stream))

(defvar *test-process* (run-test-program))

I can read the first line of output like this:
(read-line (process-output *test-process*))

And, judging by the manual, I should be able to write like this:
(write-line "jim" (process-input *test-process*))

But after doing so, subsequent read-line calls will just hang.  I have
tried calling terpri to send a newline character to the processes
input, but that doesn't help either.

Any suggestions, or better (especially more portable) ways to do this
kind of thing?

From: ··········@hotmail.com
Subject: Re: sb-ext:run-program
Date: 
Message-ID: <5687a8e4-e1b8-417f-ab1b-5accc0e91f26@i29g2000prf.googlegroups.com>
Suspect buffering.
this works here:

#!/usr/bin/perl -w
use strict;
syswrite STDOUT, "type!\n";
my $a = <STDIN>;
syswrite STDOUT, "got: $a\n";

---------------------------------------

(defun run-test-program ()
  (run-program "./a.pl" nil
               :wait nil :input :stream :output :stream))
(defvar *test-process* (run-test-program))
(format t "from: ~a~%" (read-line (process-output *test-process*) nil
nil))
(write-line "ho" (process-input *test-process*))
(format t "end~%")
From: Rob Warnock
Subject: Re: sb-ext:run-program
Date: 
Message-ID: <5padnd6MH5lbj8TanZ2dnUVZ_gKdnZ2d@speakeasy.net>
jcipar  <······@gmail.com> wrote:
+---------------
| I'm trying to run an external program and connect to its inputs and
| outputs.  I can run simple programs like "ls" where there is only
| output, but I can't get the input to work.
...
| And I am trying to run it with this:
| (defun run-test-program ()
|   (run-program "/home/jim/test"
| 	       '() :wait nil :input :stream :output :stream))
| (defvar *test-process* (run-test-program))
| 
| I can read the first line of output like this:
| (read-line (process-output *test-process*))
| 
| And, judging by the manual, I should be able to write like this:
| (write-line "jim" (process-input *test-process*))
| 
| But after doing so, subsequent read-line calls will just hang. ...
+---------------

I was able to duplicate your problem using CMUCL[1], and was
able to fix it by adding a FORCE-OUTPUT after the WRITE-LINE:

    cmu> (defvar *test-process* (run-test-program))

    *TEST-PROCESS*
    cmu> (read-line (process-output *test-process*))

    "I'm a computer, what is your name?"
    NIL
    cmu> (write-line "jim" (process-input *test-process*))

    "jim"
    cmu> (force-output (process-input *test-process*))

    NIL
    cmu> (read-line (process-output *test-process*))

    "well hello, jim, have a good day"
    NIL
    cmu> 

Here is the key hint:

    cmu> (describe (process-input *test-process*))

    #<Stream for descriptor 6> is a structure of type FD-STREAM.
    IN-BUFFER: NIL.
    ...
    OUT: #<Function LISP::OUTPUT-CHAR-FULL-BUFFERED {104E7DF1}>.
    ...
    ELEMENT-SIZE: 1.
    ELEMENT-TYPE: CHARACTER.
    FD: 6.
    BUFFERING: :FULL.
    ...
    OBUF-LENGTH: 4096.
    ...
    cmu> 

Apparently, RUN-PROGRAM creates its PROCESS-INPUT streams with
:BUFFERING :FULL by default, rather than :BUFFERING :LINE
(which is usually the default for streams going to a terminal).
Unlike output streams with :BUFFERING :LINE, output streams with
:BUFFERING :FULL are *not* automatically flushed just because
you write a newline, so you need to do an explicit FORCE-OUTPUT
to push the data out to the child process.


-Rob

[1] Yes, as others have reminded me quite forcefully, SBCL has
    diverged very significantly since its fork from CMUCL, and
    one should not assume that such details still carry over.
    But since in this case I was able to exactly duplicate your
    problem, there is a very good chance the fix is the same as well.

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: jcipar
Subject: Re: sb-ext:run-program
Date: 
Message-ID: <95e5c66a-1ae4-4ba5-8371-e03be74c3dce@y43g2000hsy.googlegroups.com>
> Here is the key hint:
>
>     cmu> (describe (process-input *test-process*))
>
>     #<Stream for descriptor 6> is a structure of type FD-STREAM.
>     IN-BUFFER: NIL.
>     ...
>     OUT: #<Function LISP::OUTPUT-CHAR-FULL-BUFFERED {104E7DF1}>.
>     ...
>     ELEMENT-SIZE: 1.
>     ELEMENT-TYPE: CHARACTER.
>     FD: 6.
>     BUFFERING: :FULL.
>     ...
>     OBUF-LENGTH: 4096.
>     ...
>     cmu>
>
> Apparently, RUN-PROGRAM creates its PROCESS-INPUT streams with
> :BUFFERING :FULL by default, rather than :BUFFERING :LINE
> (which is usually the default for streams going to a terminal).
> Unlike output streams with :BUFFERING :LINE, output streams with
> :BUFFERING :FULL are *not* automatically flushed just because
> you write a newline, so you need to do an explicit FORCE-OUTPUT
> to push the data out to the child process.
>
> -Rob

(force-output) works well.  Thanks. Now I can move on to something
that isn't completely trivial.

I suspected that there was buffering going on, but I couldn't
figure out how to fix it.  I'm generally a C programmer, so I was
searching for the word "flush".  I also tried stream-* but
stream-force-output caused an error.

Is there any way to change the buffering mode of a stream that
has already been created?


> Suspect buffering.
> this works here:

> #!/usr/bin/perl -w
> use strict;
> syswrite STDOUT, "type!\n";
> my $a = <STDIN>;
> syswrite STDOUT, "got: $a\n";

> ---------------------------------------

> (defun run-test-program ()
>  (run-program "./a.pl" nil
>                :wait nil :input :stream :output :stream))
> (defvar *test-process* (run-test-program))
> (format t "from: ~a~%" (read-line (process-output *test-process*) nil
> nil))
> (write-line "ho" (process-input *test-process*))
> (format t "end~%")

This much did work for me.  The problem occurred when trying to read
the last line of output from the other program.