Coverage report: /home/ellis/comp/core/lib/cli/shell.lisp
Kind | Covered | All | % |
expression | 0 | 215 | 0.0 |
branch | 0 | 22 | 0.0 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; lib/cli/shell.lisp --- shell utils
3
;; utils for working with shells in different environments
9
;; A read macro is accessible in the named readtable :SHELL. It has
10
;; three modes of operation: read, compile, and eval. In read mode,
11
;; input is parsed and embedded lisp forms are expanded. The string is
12
;; returned as is. In eval mode, embedded lisp forms are expanded and
13
;; the resulting string is wrapped in a call to
14
;; SB-EXT:RUN-PROGRAM. Finally, in eval mode the compiled function is
15
;; called with default arguments and the result of that call is
19
(in-package :cli/shell)
22
(defparameter *shell* "/bin/bash")
23
(defparameter *shell-input* nil)
24
(defparameter *shell-output* t)
26
(deftype %shell-state () '(member :sh :dolla :pound))
28
(defun plain-shell-reader (stream)
29
(let (out (state :sh))
30
(declare (type %shell-state state))
31
(loop for c = (read-char stream)
35
(#\$ (setq state :dolla))
36
(#\# (setq state :pound))
39
;; TODO 2025-01-20: we should consider an alternative method
40
;; to remove the explicit evals.
42
;; check for lisp-mode character
44
((char= c #\,) ;; check for splice-mode character
45
(if (char= (peek-char nil stream) #\@)
49
;; eval and push each form individually.
50
(let ((form (read stream nil nil)))
53
(format nil "~{~A~^ ~}" (compile-and-eval form))
56
;; unconditionally read in a single sexp and eval.
57
(push (coerce (format nil "~A " (compile-and-eval (read stream nil nil)))
60
((or (char= c #\+) (char= c #\-))
62
(if (sb-int:featurep (let ((*package* sb-int:*keyword-package*)
63
(sb-impl::*reader-package* nil)
64
(*read-suppress* nil))
65
(read stream t nil t)))
68
(push (coerce (format nil "~A " (eval (read stream t nil t))) 'list) out)
69
(let ((*read-suppress* t))
85
(flatten (nreverse out)))))
87
(defmacro define-process-output-handler (type &body body)
88
"Define a new function which handles the result of a SB-EXT:PROCESS in
89
the context of the $#-reader macro."
90
(declare (ignore type body)))
92
(defun |#$-reader| (stream sub-char numarg)
93
"Switch on the shell reader, parsing STREAM and returning a
94
shell program or executing it. In other words, this is an
95
implementation of the lazy version of SHCL's #$-reader.
97
Similar to shcl, we add some reader extensions to enable embedding
98
lisp forms and other goodies.
105
KLUDGE: an escaped SYMBOL can't be immediately followed by the closing tag
106
'$#' - this causes the reader to consume those characters as part of the
107
symbol name. One thing we might end up doing is checking for those characters
108
in the input and unreading those 2 chars.
110
An escaped form with parens like the following works fine:
112
#0$echo #,(+ 2 2)$# ;; => 4"
113
(declare (ignore sub-char) ((or (integer 0 9) null) numarg))
114
(let ((str (plain-shell-reader stream)))
118
(let ((args (list "-c" (format nil "~a" str))))
119
(lambda (&key input (output *standard-output*) (wait t) (status-hook))
121
(:string (string-right-trim
123
(with-output-to-string (s)
124
(sb-ext:run-program *shell* args
125
:directory *default-pathname-defaults*
129
:status-hook status-hook))))
130
(:integer (parse-integer
133
(with-output-to-string (s)
134
(sb-ext:run-program *shell* args
135
:directory *default-pathname-defaults*
139
:status-hook status-hook)))))
140
(t (sb-ext:run-program *shell*
142
:directory *default-pathname-defaults*
146
:status-hook status-hook))))))
148
(string-right-trim '(#\Newline)
149
(with-output-to-string (s)
150
(sb-ext:run-program *shell*
151
(list "-c" (format nil "~a" str))
152
:directory *default-pathname-defaults*
154
:input *shell-input*))))
156
`(sb-ext:run-program *shell*
157
(list "-c" (format nil "~a" ,str))
158
:directory *default-pathname-defaults*
160
:output *shell-output*))))
163
"The shell readtable"
165
(:dispatch-macro-char #\# #\$ #'|#$-reader|))