-
Notifications
You must be signed in to change notification settings - Fork 0
/
wrappers.lisp
192 lines (163 loc) · 7.32 KB
/
wrappers.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
;; Copyright (C) 2003-2008 Shawn Betts
;;
;; This file is part of thesiswm.
;;
;; thesiswm is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; thesiswm is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this software; see the file COPYING. If not, see
;; <http://www.gnu.org/licenses/>.
;; Commentary:
;;
;; portability wrappers. Any code that must run different code for
;; different lisps should be wrapped up in a function and put here.
;;
;; Code:
(in-package #:thesiswm)
(export '(getenv))
(define-condition not-implemented (thesiswm-error)
() (:documentation "A function has been called that is not implemented yet."))
(defun screen-display-string (screen &optional (assign t))
(format nil
(if assign "DISPLAY=~a:~d.~d" "~a:~d.~d")
(screen-host screen)
(xlib:display-display *display*)
(screen-id screen)))
(defun run-prog (prog &rest opts &key args output (wait t) &allow-other-keys)
"Common interface to shell. Does not return anything useful."
(remf opts :args)
(remf opts :output)
(remf opts :wait)
(let ((env (sb-ext:posix-environ)))
(when (current-screen)
(setf env (cons (screen-display-string (current-screen) t)
(remove-if (lambda (str)
(string= "DISPLAY=" str
:end2 (min 8 (length str))))
env))))
(apply #'sb-ext:run-program prog args :output (if output output t)
:error t :wait wait :environment env opts)))
(defun run-prog-collect-output (prog &rest args)
"run a command and read its output."
(with-output-to-string (s)
(run-prog prog :args args :output s :wait t)))
(defun getenv (var)
"Return the value of the environment variable."
(sb-posix:getenv (string var)))
(defun (setf getenv) (val var)
"Set the value of the environment variable, @var{var} to @var{val}."
(sb-posix:putenv (format nil "~A=~A" (string var) (string val))))
(defun pathname-is-executable-p (pathname)
"Return T if the pathname describes an executable file."
(let ((filename (coerce (sb-ext:native-namestring pathname) 'string)))
(and (or (pathname-name pathname)
(pathname-type pathname))
(sb-unix:unix-access filename sb-unix:x_ok))))
(defun probe-path (path)
"Return the truename of a supplied path, or nil if it does not exist."
(handler-case
(truename
(let ((pathname (pathname path)))
;; If there is neither a type nor a name, we have a directory
;; pathname already. Otherwise make a valid one.
(if (and (not (pathname-name pathname))
(not (pathname-type pathname)))
pathname
(make-pathname
:directory (append (or (pathname-directory pathname)
(list :relative))
(list (file-namestring pathname)))
:name nil :type nil :defaults pathname))))
(file-error () nil)))
(defun print-backtrace (&optional (frames 100))
"print a backtrace of FRAMES number of frames to standard-output"
(sb-debug:print-backtrace :count frames :stream *standard-output*))
(defun bytes-to-string (data)
"Convert a list of bytes into a string."
(handler-bind
((sb-impl::octet-decoding-error #'(lambda (c)
(declare (ignore c))
(invoke-restart 'use-value "?"))))
(sb-ext:octets-to-string
(make-array (length data) :element-type '(unsigned-byte 8) :initial-contents data))))
(defun utf8-to-string (octets)
"Convert the list of octets to a string."
(let ((octets (coerce octets '(vector (unsigned-byte 8)))))
(handler-bind
((sb-impl::octet-decoding-error #'(lambda (c)
(declare (ignore c))
(invoke-restart 'use-value "?"))))
(sb-ext:octets-to-string octets :external-format :utf-8))))
(defun directory-no-deref (pathspec)
"Call directory without dereferencing symlinks in the results"
(directory pathspec :resolve-symlinks nil))
;;; On GNU/Linux some contribs use sysfs to figure out useful info for
;;; the user. SBCL upto at least 1.0.16 (but probably much later) has
;;; a problem handling files in sysfs caused by SBCL's slightly
;;; unusual handling of files in general and Linux' sysfs violating
;;; POSIX. When this situation is resolved, this function may be removed.
#+ linux
(export '(read-line-from-sysfs))
#+ linux
(defun read-line-from-sysfs (stream &optional (blocksize 80))
"READ-LINE, but with a workaround for a known SBCL/Linux bug
regarding files in sysfs. Data is read in chunks of BLOCKSIZE bytes."
(let ((buf (make-array blocksize
:element-type '(unsigned-byte 8)
:initial-element 0))
(fd (sb-sys:fd-stream-fd stream))
(string-filled 0)
(string (make-string blocksize))
bytes-read
pos
(stringlen blocksize))
(loop
;; Read in the raw bytes
(setf bytes-read
(sb-unix:unix-read fd (sb-sys:vector-sap buf) blocksize))
;; Why does SBCL return NIL when an error occurs?
(when (or (null bytes-read)
(< bytes-read 0))
(error "UNIX-READ failed."))
;; This is # bytes both read and in the correct line.
(setf pos (or (position (char-code #\Newline) buf) bytes-read))
;; Resize the string if necessary.
(when (> (+ pos string-filled) stringlen)
(setf stringlen (max (+ pos string-filled)
(* 2 stringlen)))
(let ((new (make-string stringlen)))
(replace new string)
(setq string new)))
;; Translate read bytes to string
(setf (subseq string string-filled)
(sb-ext:octets-to-string (subseq buf 0 pos)))
(incf string-filled pos)
(if (< pos blocksize)
(return (subseq string 0 string-filled))))))
(defun execv (program &rest arguments)
(declare (ignorable program arguments))
;; FIXME: seems like there should be a way to do this in sbcl the way it's done in clisp. -sabetts
(sb-alien:with-alien ((prg sb-alien:c-string program)
(argv (array sb-alien:c-string 256)))
(loop
for i in arguments
for j below 255
do (setf (sb-alien:deref argv j) i))
(setf (sb-alien:deref argv (length arguments)) nil)
(sb-alien:alien-funcall (sb-alien:extern-alien "execv" (function sb-alien:int sb-alien:c-string (* sb-alien:c-string)))
prg (sb-alien:cast argv (* sb-alien:c-string)))))
(defun open-pipe (&key (element-type '(unsigned-byte 8)))
"Create a pipe and return two streams. The first value is the input
stream, and the second value is the output stream."
(multiple-value-bind (in-fd out-fd)
(sb-posix:pipe)
(let ((in-stream (sb-sys:make-fd-stream in-fd :input t :element-type element-type))
(out-stream (sb-sys:make-fd-stream out-fd :output t :element-type element-type)))
(values in-stream out-stream))))
;;; EOF