1 ;;; termio.lisp --- Terminal-related utilities
3 ;; Copyright (C) 2012 Didier Verna.
5 ;; Author: Didier Verna <didier.verna@gmail.com>
6 ;; Maintainer: Didier Verna <didier.verna@gmail.com>
8 ;; This file is part of Clon.
10 ;; Permission to use, copy, modify, and distribute this software for any
11 ;; purpose with or without fee is hereby granted, provided that the above
12 ;; copyright notice and this permission notice appear in all copies.
14 ;; THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
15 ;; WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 ;; MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
17 ;; ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 ;; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 ;; ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
20 ;; OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 ;; Contents management by FCM version 0.1.
30 (in-package :com.dvlsoft.clon
)
31 (in-readtable :com.dvlsoft.clon
)
34 ;; Preamble C code needed for ECL's FD-LINE-WIDTH function.
38 #include <sys/ioctl.h>")
42 (defgeneric stream-file-stream
(stream &optional direction
)
43 (:documentation
"Convert STREAM to a file-stream.")
44 (:method
((stream file-stream
) &optional direction
)
45 (declare (ignore direction
))
47 (:method
((stream synonym-stream
) &optional direction
)
48 (declare (ignore direction
))
49 (stream-file-stream (symbol-value (synonym-stream-symbol stream
))))
50 (:method
((stream two-way-stream
) &optional direction
)
53 (:input
(two-way-stream-input-stream stream
))
54 (:output
(two-way-stream-output-stream stream
))
56 (error "Cannot extract file-stream from TWO-WAY-STREAM ~A:
57 invalid direction: ~S"
62 (defun fd-line-width (fd)
63 "Get the line width for FD (file descriptor).
65 - the line width, or -1 if it can't be computed
66 (typically when FD does not denote a tty),
67 - an error message if the operation failed."
68 (ffi:c-inline
(fd) (:int
) (values :int
:cstring
) "{
74 struct winsize window;
75 if (ioctl (fd, TIOCGWINSZ, &window) == -1)
78 msg = strerror (errno);
81 cols = (int) window.ws_col;
87 ;; #### NOTE: ABCL doesn't appear below because this module (termio) is never
89 (defun stream-line-width (stream)
90 "Get STREAM's line width.
92 - the stream's line width, or nil if it can't be computed
93 (typically when the stream does not denote a tty),
94 - an error message if the operation failed."
95 ;; #### NOTE: doing a TIOCGWINSZ ioctl here is a convenient way to both know
96 ;; whether we're connected to a tty, and getting the terminal width at the
97 ;; same time. In case the ioctl fails, we need to distinguish between and
98 ;; ENOTTY error, which simply means that we're not connected to a terminal,
99 ;; and the other which are real errors and need to be reported.
102 (locally (declare (sb-ext:muffle-conditions sb-ext
:compiler-note
))
104 (with-winsize winsize
()
105 (sb-posix:ioctl
(stream-file-stream stream
:output
)
108 (winsize-ws-col winsize
))
109 (sb-posix:syscall-error
(error)
110 (unless (= (sb-posix:syscall-errno error
) sb-posix
:enotty
)
111 (values nil error
)))))
113 (locally (declare (optimize (ext:inhibit-warnings
3)))
114 (alien:with-alien
((winsize (alien:struct unix
:winsize
)))
115 (multiple-value-bind (success error-number
)
117 (system:fd-stream-fd
(stream-file-stream stream
:output
))
121 (alien:slot winsize
'unix
:ws-col
)
122 (unless (= error-number unix
:enotty
)
123 (values nil
(unix:get-unix-error-msg error-number
)))))))
125 (ccl:rlet
((winsize :winsize
))
128 (#_ioctl
(ccl::stream-device stream
:output
)
132 (ccl:pref winsize
:winsize.ws_col
)
133 (unless (= result
(- #$ENOTTY
))
134 (values nil
(ccl::%strerror
(- result
)))))))
136 (multiple-value-bind (cols msg
)
137 (fd-line-width (ext:file-stream-fd stream
))
138 (values (unless (= cols -
1) cols
) msg
))
140 (multiple-value-bind (input-fd output-fd
)
141 (ext:stream-handles stream
)
142 (declare (ignore input-fd
))
144 (cffi:with-foreign-object
(winsize 'winsize
)
145 (let ((result (cffi:foreign-funcall
"ioctl"
151 (unless (= +errno
+ +enotty
+)
153 (cffi:foreign-funcall
"strerror"
154 :int
+errno
+ :string
)))
155 (cffi:with-foreign-slots
((ws-col) winsize winsize
)
158 ;;; termio.lisp ends here