Alain Picard <Alain.Picard-please-no-spam@memetrics.com> wrote:
+---------------
| Joe Marshall <prunesquallor@comcast.net> writes:
| > Allow me to echo this sentiment. There are a few utilities that
| > you'll want to write, but once you have them, the rest becomes a lot
| > easier than you'd think.
|
| Actually, since I spent the day "scripting in CL", as it was
| just too painful to try to remember all the @#$#@%! /bin/sh syntax,
| perhaps these common utilities should be packaged up and submitted,
| say, to CLIKI? Heck, just posting them to this group would be
| a great start.
+---------------
Bear in mind that the utilities you end up needing/using are typically
*very* platform-specific [and by "platform" I include both the CL
implementation and the operating system] and application-specific.
But just to get us started, here's one small example...
ANSI CL provides no standard way to read a single TTY character without
echoing it or requiring a newline, something one often needs to do when
coding the usual "more"-like pager or "curses"-like menu interaction.
Here's one cut at it that works well enough for me in CMUCL-18e on
FreeBSD 4.x:
#| What "less" does:
s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
s.c_oflag |= (OPOST|ONLCR|TAB3);
s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
s.c_cc[VMIN] = 1;
s.c_cc[VTIME] = 0;
|#
(use-package :alien)
(use-package :unix)
(defun read-char-no-echo-cbreak (&optional (stream *query-io*))
(with-alien ((old (struct termios))
(new (struct termios)))
(let ((e0 (unix-tcgetattr 0 old))
(e1 (unix-tcgetattr 0 new))
(bits (logior tty-icanon tty-echo tty-echoe tty-echok tty-echonl)))
(declare (ignorable e0 e1))
(unwind-protect
(progn
(setf (slot new 'c-lflag) (logandc2 (slot old 'c-lflag) bits))
(setf (deref (slot new 'c-cc) vmin) 1)
(setf (deref (slot new 'c-cc) vtime) 0)
(unix-tcsetattr 0 tcsadrain new)
(read-char stream))
And a helper routine that uses it:
(defun space-or-q-p (&optional (prompt "[More... (q to quit)] "))
(format *query-io* "~A" prompt)
(force-output *query-io*)
(let* ((c (read-char-no-echo-cbreak *query-io*))
(bs (make-string (1+ (length prompt)) :initial-element #\backspace))
(sp (make-string (1+ (length prompt)) :initial-element #\space)))
(format *query-io* "~A~A~A" bs sp bs) ; Erase the prompt.
(force-output *query-io*)
(char-equal c #\q)))
which would be used in a "pager" something like this [untested fragment]:
(loop for line = (get-next-line)
and line-num from 1
do (format t "~a~%" line)
(when (and (zerop (mod line-num page-length))
(space-or-q-p))
(return (values))))
The above code conses excessively in a couple of places, and could
certainly be optimized and/or cleaned up further [e.g., LOAD-TIME-VALUE
in a couple of places would help].
-Rob
-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607