---
layout: ../Site.layout.js
---
# [Embeddable Common Lisp](https://ecl.common-lisp.dev/) External Program Asyncronously - cat(1) echo server

For example if we want to use unix [`cat`(1)](https://man.openbsd.org/cat.1) without starting a shell as an echo server.

## My eev/emacs/ecl setup.

```
#|
 (setq inferior-lisp-program "ecl")
 (slime)
 (setq eepitch-buffer-name "*slime-repl ECL*")
|#
```

# `ext:run-program "cat" '() :wait nil`

in so many words.

```
(locally
    (declare (special *2way* *exit* *proc*))
  (multiple-value-setq
      (*2way* *exit* *proc*)
    (ext:run-program "cat" '() :wait nil)))
```

We need to preserve the process to interactively `ext:external-process-wait` for it to finish when we close it.

## The process's input is what we output to it and vice versa

I always find this confusing, but it is clearly correct.

```
(two-way-stream-output-stream *2way*)
(print "This is only a
test" *)
```

And let's `read-line` from that:

```
(two-way-stream-input-stream *2way*)
(read-line *) ; ⮕ ""
(read-line **) ; ⮕ "\"This is only a"
(read-line ***) ; ⮕ (waits for stream) C-c C-c ABORT <ret>
```
Note that the `cat`(1) survives us interactively aborting `read-line` in lisp. However, partially reading that line loses us the characters for next time we try to read. Instead

# Using [`read-char-no-hang`](https://www.lispworks.com/documentation/HyperSpec/Body/f_rd_c_1.htm) instead

```
(two-way-stream-output-stream *2way*)
(print "foo
bar
baz
" *)

(two-way-stream-input-stream *2way*)
(loop :for ch := (read-char-no-hang *)
      :while ch
      :collect ch :into chars
      :finally (return (coerce chars 'string)))
#|
" 
\"foo
bar
baz
\""
|#
```
as we may have expected (I had a stray newline from before).

# Killing the process

```
(ext:terminate-process *proc*)
(ext:external-process-wait *proc*)
(multiple-value-setq
      (*2way* *exit* *proc*)
    (values))
```

- [`ext:terminate-process` and `ext:external-process-wait`](https://ecl.common-lisp.dev/static/manual/Operating-System-Interface.html#External-processes)
- Clean up references to them

# Conclusions

So an embeddable common lisp `ext:external-process` running `cat`(1) functions as a kind of in-memory echo server.

I have always tended to use embeddable common lisp's multiprocessing and external-process extensions directly when available. They are meant to be similar to lisp machine useages, which I imagine is preserved across compilers. Technically I guess 'semi-portable' [`uiop:launch-program`](https://asdf.common-lisp.dev/uiop.html#UIOP_002fLAUNCH_002dPROGRAM) is going to work the same, except arguements to the program are written into the program's invocation as though one was using a shell (which we are not) instead of ecl's list of flags. Confusingly [`uiop:run-program`](https://asdf.common-lisp.dev/uiop.html#UIOP_002fRUN_002dPROGRAM) is reserved for syncronous external processes.

I kind of want to use embeddable common lisp anyway, because [my insane fairy kitten godmother idea](/momentary/screwlisps-cl-kitten-future-theory/) is to arbitrarily transform all C++ programs into [kitten](https://kitten.small-web.org/)s.

# Fin.

Well, how do you like to use external processes in common lisp - [in the mastodon thread](https://gamerplus.org/@screwlisp/114696779918088755).

Please do share this embeddable common lisp example however occurs to you and in whatever way. Since I sometimes stumble around here when I have forgotten how to use arbitrary external programs from inside my lisp image like this, so I imagine you might find it helpful.