From: Bart W on
Dear all,

I'd like to run an external program (an image conversion program) and
give it some binary data (a JPEG image) as standard input, and save the
result in a file.

I'm using CMUCL 20a, whose manual entry for extensions:run-program at
http://common-lisp.net/project/cmucl/doc/cmu-user/extensions.html#toc45
says

----

:input ... If specified as :stream, then the process-input slot contains
an output stream. Anything written to this stream goes to the program as
input.

----

Now

(let ((proc (ext:run-program "/bin/cat" nil
:wait nil
:input :stream
:output "file.jpg")))
(stream-element-type (ext:process-input proc)))

prints CHARACTER. So the output stream, to which I'm supposed to write
the bytes, is a character stream.

So the question is: how do I get the /binary/ input (which I have as an
array of unsigned bytes) into the program's input?

Any advice appreciated.

Bart



From: Raymond Toy on
Bart W wrote:
Bart W wrote:
> Dear all,
>
> I'd like to run an external program (an image conversion program) and
> give it some binary data (a JPEG image) as standard input, and save the
> result in a file.
>
> I'm using CMUCL 20a, whose manual entry for extensions:run-program at
> http://common-lisp.net/project/cmucl/doc/cmu-user/extensions.html#toc45
> says
>
> ----
>
> :input ... If specified as :stream, then the process-input slot contains
> an output stream. Anything written to this stream goes to the program as
> input.
>
> ----
>
> Now
>
> (let ((proc (ext:run-program "/bin/cat" nil
> :wait nil
> :input :stream
> :output "file.jpg")))
> (stream-element-type (ext:process-input proc)))
>
> prints CHARACTER. So the output stream, to which I'm supposed to write
> the bytes, is a character stream.
>
> So the question is: how do I get the /binary/ input (which I have as an
> array of unsigned bytes) into the program's input?
>
> Any advice appreciated.
>
> Bart
>
>
>

> Dear all,
>
> I'd like to run an external program (an image conversion program) and
> give it some binary data (a JPEG image) as standard input, and save the
> result in a file.
>
> I'm using CMUCL 20a, whose manual entry for extensions:run-program at
> http://common-lisp.net/project/cmucl/doc/cmu-user/extensions.html#toc45
> says
>
> ----
>
> :input ... If specified as :stream, then the process-input slot contains
> an output stream. Anything written to this stream goes to the program as
> input.
>
> ----
>
> Now
>
> (let ((proc (ext:run-program "/bin/cat" nil
> :wait nil
> :input :stream
> :output "file.jpg")))
> (stream-element-type (ext:process-input proc)))
>
> prints CHARACTER. So the output stream, to which I'm supposed to write
> the bytes, is a character stream.
>
> So the question is: how do I get the /binary/ input (which I have as an
> array of unsigned bytes) into the program's input?

Good question. One possible way is to take each byte and use code-char
and write out the individual characters (or strings). If you're using
the unicode version, be sure *default-external-format* is :latin1 (or
:iso8859-1). Or instead of specifying :stream, create a binary stream
and pass that as the :input parameter. (Not sure if that will work or not.)

Ray
From: Bart Wage on
>>>>> "Raymond" == Raymond Toy <toy.raymond(a)gmail.com> writes:

Raymond> Bart W wrote:
>> Dear all,
>>
>> I'd like to run an external program (an image conversion program)
>> and give it some binary data (a JPEG image) as standard input,
>> and save the result in a file.
>>
>> I'm using CMUCL 20a, whose manual entry for
>> extensions:run-program at
>> http://common-lisp.net/project/cmucl/doc/cmu-user/extensions.html#toc45
>> says
>>
>> ----
>>
>> :input ... If specified as :stream, then the process-input slot
>> contains an output stream. Anything written to this stream goes
>> to the program as input.
>>
>> ----
>>
>> Now
>>
>> (let ((proc (ext:run-program "/bin/cat" nil :wait nil :input
>> :stream :output "file.jpg"))) (stream-element-type
>> (ext:process-input proc)))
>>
>> prints CHARACTER. So the output stream, to which I'm supposed to
>> write the bytes, is a character stream.
>>
>> So the question is: how do I get the /binary/ input (which I have
>> as an array of unsigned bytes) into the program's input?
>>

Raymond> Good question. One possible way is to take each byte and
Raymond> use code-char and write out the individual characters (or
Raymond> strings). If you're using the unicode version, be sure
Raymond> *default-external-format* is :latin1 (or :iso8859-1). Or
Raymond> instead of specifying :stream, create a binary stream and
Raymond> pass that as the :input parameter. (Not sure if that will
Raymond> work or not.)

Hello Raymond,

Thanks for the suggestions. Unfortunately, they do not work.

As for the first one:

(defun in-out (byte-array output-file program &optional arguments)
(let ((*default-external-format* :latin1))
(with-input-from-string (input-stream (with-output-to-string (s)
(loop for b across byte-array
do (write-char (code-char b) s))))
(ext:run-program program arguments
:input input-stream
:output output-file
:if-output-exists :supersede))))

(in-out (kmrcl:read-file-to-usb8-array "binary-input-file")
"binary-output-file"
"/bin/cat")

File binary-output-file is written, but contains quite different bytes
than the binary-input-file.

As for the second one: I haven't got the code at hand, but I tried it
and Lisp complains about not being able to do READ-LINE on a binary
stream.

Any other suggestions? Surely it must be possible to pass a stream of
bytes to a program somehow?

Thanks, Bart
From: conrad on
Bart Wage <b.wage(a)xs4all.nl> writes:
> Any other suggestions? Surely it must be possible to pass a stream of
> bytes to a program somehow?

I have code to do this. It is SBCL specific though but maybe it will
help. I had to hack up RUN-PROGRAM.

From: Raymond Toy on
Bart Wage wrote:
>
> Any other suggestions? Surely it must be possible to pass a stream of
> bytes to a program somehow?


Sorry for the delay. I see now that if you specify a stream, cmucl will
basically write out the data more or less directly from the string,
which contains 16-bit characters. This isn't what we want.

run-program needs to be updated somehow.

But until then, perhaps some variant of the following will work. I
tested it very briefly.

(defun in-out2 (byte-array output-file program &optional arguments)
(let ((proc (ext:run-program program arguments
:input :stream
:output output-file :wait nil
:if-output-exists :supersede)))
(loop for x across byte-array
do (write-char (code-char x) (process-input proc)))
(close (process-input proc))
(process-close proc)))


Ray