1 | (in-package #:asdf-install) |
---|
2 | |
---|
3 | (defvar *temporary-files*) |
---|
4 | |
---|
5 | (defparameter *shell-path* "/bin/sh" |
---|
6 | "The path to a Bourne compatible command shell in physical pathname notation.") |
---|
7 | |
---|
8 | (eval-when (:load-toplevel :compile-toplevel :execute) |
---|
9 | #+:allegro |
---|
10 | (require :osi) |
---|
11 | #+:allegro |
---|
12 | (require :socket) |
---|
13 | #+:digitool |
---|
14 | (require :opentransport) |
---|
15 | #+:ecl |
---|
16 | (require :sockets) |
---|
17 | #+:lispworks |
---|
18 | (require "comm") |
---|
19 | ) |
---|
20 | |
---|
21 | (defun get-env-var (name) |
---|
22 | #+:allegro (sys:getenv name) |
---|
23 | #+:clisp (ext:getenv name) |
---|
24 | #+:cmu (cdr (assoc (intern (substitute #\_ #\- name) |
---|
25 | :keyword) |
---|
26 | ext:*environment-list*)) |
---|
27 | #+:ecl (ext:getenv name) |
---|
28 | #+:lispworks (lw:environment-variable name) |
---|
29 | #+(or :mcl :openmcl) (ccl::getenv name) |
---|
30 | #+:sbcl (sb-ext:posix-getenv name) |
---|
31 | #+:scl (cdr (assoc name ext:*environment-list* :test #'string=)) |
---|
32 | #+abcl (ext:getenv name) |
---|
33 | ) |
---|
34 | |
---|
35 | #-:digitool |
---|
36 | (defun system-namestring (pathname) |
---|
37 | (namestring (truename pathname))) |
---|
38 | |
---|
39 | #+:digitool |
---|
40 | (defvar *start-up-volume* |
---|
41 | (second (pathname-directory (truename "ccl:")))) |
---|
42 | |
---|
43 | #+:digitool |
---|
44 | (defun system-namestring (pathname) |
---|
45 | ;; this tries to adjust the root directory to eliminate the spurious |
---|
46 | ;; volume name for the boot file system; it also avoids use of |
---|
47 | ;; TRUENAME as some applications are for not yet existent files |
---|
48 | (let ((truename (probe-file pathname))) |
---|
49 | (unless truename |
---|
50 | (setf truename |
---|
51 | (translate-logical-pathname |
---|
52 | (merge-pathnames pathname *default-pathname-defaults*)))) |
---|
53 | (let ((directory (pathname-directory truename))) |
---|
54 | (flet ((string-or-nil (value) (when (stringp value) value)) |
---|
55 | (absolute-p (directory) (eq (first directory) :absolute)) |
---|
56 | (root-volume-p (directory) |
---|
57 | (equal *start-up-volume* (second directory)))) |
---|
58 | (format nil "~:[~;/~]~{~a/~}~@[~a~]~@[.~a~]" |
---|
59 | (absolute-p directory) |
---|
60 | (if (root-volume-p directory) (cddr directory) (cdr directory)) |
---|
61 | (string-or-nil (pathname-name truename)) |
---|
62 | (string-or-nil (pathname-type truename))))))) |
---|
63 | |
---|
64 | #+:digitool |
---|
65 | (progn |
---|
66 | (defun |read-linefeed-eol-comment| |
---|
67 | (stream char &optional (eol '(#\return #\linefeed))) |
---|
68 | (loop (setf char (read-char stream nil nil)) |
---|
69 | (unless char (return)) |
---|
70 | (when (find char eol) (return))) |
---|
71 | (values)) |
---|
72 | |
---|
73 | (set-syntax-from-char #\linefeed #\space) |
---|
74 | (set-macro-character #\; #'|read-linefeed-eol-comment| nil *readtable*)) |
---|
75 | |
---|
76 | ;; for non-SBCL we just steal this from SB-EXECUTABLE |
---|
77 | #-(or :digitool) |
---|
78 | (defvar *stream-buffer-size* 8192) |
---|
79 | #-(or :digitool) |
---|
80 | (defun copy-stream (from to) |
---|
81 | "Copy into TO from FROM until end of the input stream, in blocks of |
---|
82 | *stream-buffer-size*. The streams should have the same element type." |
---|
83 | (unless (subtypep (stream-element-type to) (stream-element-type from)) |
---|
84 | (error "Incompatible streams ~A and ~A." from to)) |
---|
85 | (let ((buf (make-array *stream-buffer-size* |
---|
86 | :element-type (stream-element-type from)))) |
---|
87 | (loop |
---|
88 | (let ((pos #-(or :clisp :cmu) (read-sequence buf from) |
---|
89 | #+:clisp (ext:read-byte-sequence buf from :no-hang nil) |
---|
90 | #+:cmu (sys:read-n-bytes from buf 0 *stream-buffer-size* nil))) |
---|
91 | (when (zerop pos) (return)) |
---|
92 | (write-sequence buf to :end pos))))) |
---|
93 | |
---|
94 | #+:digitool |
---|
95 | (defun copy-stream (from to) |
---|
96 | "Perform copy and map EOL mode." |
---|
97 | (multiple-value-bind (reader reader-arg) (ccl::stream-reader from) |
---|
98 | (multiple-value-bind (writer writer-arg) (ccl::stream-writer to) |
---|
99 | (let ((datum nil)) |
---|
100 | (loop (unless (setf datum (funcall reader reader-arg)) |
---|
101 | (return)) |
---|
102 | (funcall writer writer-arg datum)))))) |
---|
103 | |
---|
104 | (defun make-stream-from-url (url) |
---|
105 | #+(or :sbcl :ecl) |
---|
106 | (let ((s (make-instance 'sb-bsd-sockets:inet-socket |
---|
107 | :type :stream |
---|
108 | :protocol :tcp))) |
---|
109 | (sb-bsd-sockets:socket-connect |
---|
110 | s (car (sb-bsd-sockets:host-ent-addresses |
---|
111 | (sb-bsd-sockets:get-host-by-name (url-host url)))) |
---|
112 | (url-port url)) |
---|
113 | (sb-bsd-sockets:socket-make-stream |
---|
114 | s |
---|
115 | :input t |
---|
116 | :output t |
---|
117 | :buffering :full |
---|
118 | :external-format :iso-8859-1)) |
---|
119 | #+:cmu |
---|
120 | (sys:make-fd-stream (ext:connect-to-inet-socket (url-host url) (url-port url)) |
---|
121 | :input t :output t :buffering :full) |
---|
122 | #+:scl |
---|
123 | (sys:make-fd-stream (ext:connect-to-inet-socket (url-host url) (url-port url)) |
---|
124 | :input t :output t :buffering :full |
---|
125 | :external-format :iso-8859-1) |
---|
126 | #+:lispworks |
---|
127 | (comm:open-tcp-stream (url-host url) (url-port url) |
---|
128 | #+(and :lispworks :win32) :element-type |
---|
129 | #+(and :lispworks :win32) '(unsigned-byte 8)) |
---|
130 | #+:allegro |
---|
131 | (socket:make-socket :remote-host (url-host url) |
---|
132 | :remote-port (url-port url)) |
---|
133 | #+:clisp |
---|
134 | (socket:socket-connect (url-port url) (url-host url) |
---|
135 | :external-format |
---|
136 | (ext:make-encoding :charset 'charset:iso-8859-1 :line-terminator :unix)) |
---|
137 | #+:openmcl |
---|
138 | (ccl:make-socket :remote-host (url-host url) |
---|
139 | :remote-port (url-port url)) |
---|
140 | #+:digitool |
---|
141 | (ccl::open-tcp-stream (url-host url) (url-port url) |
---|
142 | :element-type 'unsigned-byte) |
---|
143 | |
---|
144 | #+:abcl |
---|
145 | (let ((socket |
---|
146 | (ext:make-socket (url-host url) (url-port url)))) |
---|
147 | (ext:get-socket-stream socket))) |
---|
148 | |
---|
149 | |
---|
150 | #+:sbcl |
---|
151 | (defun return-output-from-program (program args) |
---|
152 | (with-output-to-string (out-stream) |
---|
153 | (let ((proc (sb-ext:run-program |
---|
154 | program |
---|
155 | args |
---|
156 | :output out-stream |
---|
157 | :search t |
---|
158 | :wait t))) |
---|
159 | (when (or (null proc) |
---|
160 | (and (member (sb-ext:process-status proc) '(:exited :signaled)) |
---|
161 | (not (zerop (sb-ext:process-exit-code proc))))) |
---|
162 | (return-from return-output-from-program nil))))) |
---|
163 | |
---|
164 | #+(or :cmu :scl) |
---|
165 | (defun return-output-from-program (program args) |
---|
166 | (with-output-to-string (out-stream) |
---|
167 | (let ((proc (ext:run-program |
---|
168 | program |
---|
169 | args |
---|
170 | :output out-stream |
---|
171 | :wait t))) |
---|
172 | (when (or (null proc) |
---|
173 | (and (member (ext:process-status proc) '(:exited :signaled)) |
---|
174 | (not (zerop (ext:process-exit-code proc))))) |
---|
175 | (return-from return-output-from-program nil))))) |
---|
176 | |
---|
177 | #+:lispworks |
---|
178 | (defun return-output-from-program (program args) |
---|
179 | (with-output-to-string (out-stream) |
---|
180 | (unless (zerop (sys:call-system-showing-output |
---|
181 | (format nil #-:win32 "~A~{ '~A'~}" |
---|
182 | #+:win32 "~A~{ ~A~}" |
---|
183 | program args) |
---|
184 | :prefix "" |
---|
185 | :show-cmd nil |
---|
186 | :output-stream out-stream)) |
---|
187 | (return-from return-output-from-program nil)))) |
---|
188 | |
---|
189 | #+(and :clisp (not :win32)) |
---|
190 | (defun return-output-from-program (program args) |
---|
191 | (with-output-to-string (out-stream) |
---|
192 | (let ((stream |
---|
193 | (ext:run-program program |
---|
194 | :arguments args |
---|
195 | :output :stream |
---|
196 | :wait nil))) |
---|
197 | (loop for line = (read-line stream nil) |
---|
198 | while line |
---|
199 | do (write-line line out-stream))))) |
---|
200 | |
---|
201 | #+(and :clisp :win32) |
---|
202 | (defun return-output-from-program (program args) |
---|
203 | (with-output-to-string (out-stream) |
---|
204 | (let ((stream |
---|
205 | (ext:run-shell-command |
---|
206 | (format nil "~A~{ ~A~}" program args |
---|
207 | :output :stream |
---|
208 | :wait nil)))) |
---|
209 | (loop for line = (ignore-errors (read-line stream nil)) |
---|
210 | while line |
---|
211 | do (write-line line out-stream))))) |
---|
212 | |
---|
213 | #+:allegro |
---|
214 | (defun return-output-from-program (program args) |
---|
215 | (with-output-to-string (out-stream) |
---|
216 | (let ((stream |
---|
217 | (excl:run-shell-command |
---|
218 | #-:mswindows |
---|
219 | (concatenate 'vector |
---|
220 | (list program) |
---|
221 | (cons program args)) |
---|
222 | #+:mswindows |
---|
223 | (format nil "~A~{ ~A~}" program args) |
---|
224 | :output :stream |
---|
225 | :wait nil))) |
---|
226 | (loop for line = (read-line stream nil) |
---|
227 | while line |
---|
228 | do (write-line line out-stream))))) |
---|
229 | |
---|
230 | #+:ecl |
---|
231 | (defun return-output-from-program (program args) |
---|
232 | (with-output-to-string (out-stream) |
---|
233 | (let ((stream (ext:run-program program args :output :stream))) |
---|
234 | (when stream |
---|
235 | (loop for line = (ignore-errors (read-line stream nil)) |
---|
236 | while line |
---|
237 | do (write-line line out-stream)))))) |
---|
238 | |
---|
239 | #+:openmcl |
---|
240 | (defun return-output-from-program (program args) |
---|
241 | (with-output-to-string (out-stream) |
---|
242 | (let ((proc (ccl:run-program program args |
---|
243 | :input nil |
---|
244 | :output :stream |
---|
245 | :wait nil))) |
---|
246 | (loop for line = (read-line |
---|
247 | (ccl:external-process-output-stream proc) nil nil nil) |
---|
248 | while line |
---|
249 | do (write-line line out-stream))))) |
---|
250 | |
---|
251 | #+:digitool |
---|
252 | (defun return-output-from-program (program args) |
---|
253 | (ccl::call-system (format nil "~A~{ '~A'~} 2>&1" program args))) |
---|
254 | |
---|
255 | #+:abcl |
---|
256 | (defun return-output-from-program (program args) |
---|
257 | (let ((command (format nil "~A ~{ '~A' ~}" program args))) |
---|
258 | (with-output-to-string (out-stream) |
---|
259 | (ext:run-shell-command command :output out-stream)))) |
---|
260 | |
---|
261 | |
---|
262 | (defun unlink-file (pathname) |
---|
263 | ;; 20070208 gwking@metabang.com - removed lisp-specific os-level calls |
---|
264 | ;; in favor of a simple delete |
---|
265 | (delete-file pathname)) |
---|
266 | |
---|
267 | (defun symlink-files (old new) |
---|
268 | (let* ((old (#-scl namestring #+scl ext:unix-namestring old)) |
---|
269 | (new (#-scl namestring #+scl ext:unix-namestring new #+scl nil)) |
---|
270 | ;; 20070811 - thanks to Juan Jose Garcia-Ripoll for pointing |
---|
271 | ;; that ~a would wreck havoc if the working directory had a space |
---|
272 | ;; in the pathname |
---|
273 | (command (format nil "ln -s ~s ~s" old new))) |
---|
274 | (format t "~S~%" command) |
---|
275 | (shell-command command))) |
---|
276 | |
---|
277 | (defun maybe-symlink-sysfile (system sysfile) |
---|
278 | (declare (ignorable system sysfile)) |
---|
279 | #-(or :win32 :mswindows) |
---|
280 | (let ((target (merge-pathnames |
---|
281 | (make-pathname :name (pathname-name sysfile) |
---|
282 | :type (pathname-type sysfile)) |
---|
283 | system))) |
---|
284 | (when (probe-file target) |
---|
285 | (unlink-file target)) |
---|
286 | (symlink-files sysfile target))) |
---|
287 | |
---|
288 | ;;; --------------------------------------------------------------------------- |
---|
289 | ;;; read-header-line |
---|
290 | ;;; --------------------------------------------------------------------------- |
---|
291 | |
---|
292 | #-:digitool |
---|
293 | (defun read-header-line (stream) |
---|
294 | (read-line stream)) |
---|
295 | |
---|
296 | #+:digitool |
---|
297 | (defun read-header-line (stream &aux (line (make-array 16 |
---|
298 | :element-type 'character |
---|
299 | :adjustable t |
---|
300 | :fill-pointer 0)) |
---|
301 | (byte nil)) |
---|
302 | (print (multiple-value-bind (reader arg) |
---|
303 | (ccl::stream-reader stream) |
---|
304 | (loop (setf byte (funcall reader arg)) |
---|
305 | (case byte |
---|
306 | ((nil) |
---|
307 | (return)) |
---|
308 | ((#.(char-code #\Return) |
---|
309 | #.(char-code #\Linefeed)) |
---|
310 | (case (setf byte (funcall reader arg)) |
---|
311 | ((nil #.(char-code #\Return) #.(char-code #\Linefeed))) |
---|
312 | (t (ccl:stream-untyi stream byte))) |
---|
313 | (return)) |
---|
314 | (t |
---|
315 | (vector-push-extend (code-char byte) line)))) |
---|
316 | (when (or byte (plusp (length line))) |
---|
317 | line)))) |
---|
318 | |
---|
319 | (defun open-file-arguments () |
---|
320 | (append |
---|
321 | #+sbcl |
---|
322 | '(:external-format :latin1) |
---|
323 | #+:scl |
---|
324 | '(:external-format :iso-8859-1) |
---|
325 | #+(or :clisp :digitool (and :lispworks :win32)) |
---|
326 | '(:element-type (unsigned-byte 8)))) |
---|
327 | |
---|
328 | (defun download-url-to-file (url file-name) |
---|
329 | "Resolves url and then downloads it to file-name; returns the url actually used." |
---|
330 | (multiple-value-bind (response headers stream) |
---|
331 | (loop |
---|
332 | (destructuring-bind (response headers stream) |
---|
333 | (url-connection url) |
---|
334 | (unless (member response '(301 302)) |
---|
335 | (return (values response headers stream))) |
---|
336 | (close stream) |
---|
337 | (setf url (header-value :location headers)))) |
---|
338 | (when (>= response 400) |
---|
339 | (error 'download-error :url url :response response)) |
---|
340 | (let ((length (parse-integer (or (header-value :content-length headers) "") |
---|
341 | :junk-allowed t))) |
---|
342 | (installer-msg t "Downloading ~A bytes from ~A to ~A ..." |
---|
343 | (or length "some unknown number of") |
---|
344 | url |
---|
345 | file-name) |
---|
346 | (force-output) |
---|
347 | #+:clisp (setf (stream-element-type stream) |
---|
348 | '(unsigned-byte 8)) |
---|
349 | (let ((ok? nil) (o nil)) |
---|
350 | (unwind-protect |
---|
351 | (progn |
---|
352 | (setf o (apply #'open file-name |
---|
353 | :direction :output :if-exists :supersede |
---|
354 | (open-file-arguments))) |
---|
355 | #+(or :cmu :digitool) |
---|
356 | (copy-stream stream o) |
---|
357 | #-(or :cmu :digitool) |
---|
358 | (if length |
---|
359 | (let ((buf (make-array length |
---|
360 | :element-type |
---|
361 | (stream-element-type stream)))) |
---|
362 | #-:clisp (read-sequence buf stream) |
---|
363 | #+:clisp (ext:read-byte-sequence buf stream :no-hang nil) |
---|
364 | (write-sequence buf o)) |
---|
365 | (copy-stream stream o)) |
---|
366 | (setf ok? t)) |
---|
367 | (when o (close o :abort (null ok?)))))) |
---|
368 | (close stream)) |
---|
369 | (values url)) |
---|
370 | |
---|
371 | (defun download-url-to-temporary-file (url) |
---|
372 | "Attempts to download url to a new, temporary file. Returns the resolved url and the file name \(as multiple values\)." |
---|
373 | (let ((tmp (temp-file-name url))) |
---|
374 | (pushnew tmp *temporary-files*) |
---|
375 | (values (download-url-to-file url tmp) tmp))) |
---|
376 | |
---|
377 | (defun gpg-results (package signature) |
---|
378 | (let ((tags nil)) |
---|
379 | (with-input-from-string |
---|
380 | (gpg-stream |
---|
381 | (shell-command (format nil "~s --status-fd 1 --verify ~s ~s" |
---|
382 | *gpg-command* |
---|
383 | (namestring signature) (namestring package)))) |
---|
384 | (loop for l = (read-line gpg-stream nil nil) |
---|
385 | while l |
---|
386 | do (print l) |
---|
387 | when (> (mismatch l "[GNUPG:]") 6) |
---|
388 | do (destructuring-bind (_ tag &rest data) |
---|
389 | (split-sequence-if (lambda (x) |
---|
390 | (find x '(#\Space #\Tab))) |
---|
391 | l) |
---|
392 | (declare (ignore _)) |
---|
393 | (pushnew (cons (intern (string-upcase tag) :keyword) |
---|
394 | data) tags))) |
---|
395 | tags))) |
---|
396 | |
---|
397 | #+allegro |
---|
398 | (defun shell-command (command) |
---|
399 | (multiple-value-bind (output error status) |
---|
400 | (excl.osi:command-output command :whole t) |
---|
401 | (values output error status))) |
---|
402 | |
---|
403 | #+clisp |
---|
404 | (defun shell-command (command) |
---|
405 | ;; BUG: CLisp doesn't allow output to user-specified stream |
---|
406 | (values |
---|
407 | nil |
---|
408 | nil |
---|
409 | (ext:run-shell-command command :output :terminal :wait t))) |
---|
410 | |
---|
411 | #+(or :cmu :scl) |
---|
412 | (defun shell-command (command) |
---|
413 | (let* ((process (ext:run-program |
---|
414 | *shell-path* |
---|
415 | (list "-c" command) |
---|
416 | :input nil :output :stream :error :stream)) |
---|
417 | (output (file-to-string-as-lines (ext::process-output process))) |
---|
418 | (error (file-to-string-as-lines (ext::process-error process)))) |
---|
419 | (close (ext::process-output process)) |
---|
420 | (close (ext::process-error process)) |
---|
421 | (values |
---|
422 | output |
---|
423 | error |
---|
424 | (ext::process-exit-code process)))) |
---|
425 | |
---|
426 | #+ecl |
---|
427 | (defun shell-command (command) |
---|
428 | ;; If we use run-program, we do not get exit codes |
---|
429 | (values nil nil (ext:system command))) |
---|
430 | |
---|
431 | #+lispworks |
---|
432 | (defun shell-command (command) |
---|
433 | ;; BUG: Lispworks combines output and error streams |
---|
434 | (let ((output (make-string-output-stream))) |
---|
435 | (unwind-protect |
---|
436 | (let ((status |
---|
437 | (system:call-system-showing-output |
---|
438 | command |
---|
439 | :prefix "" |
---|
440 | :show-cmd nil |
---|
441 | :output-stream output))) |
---|
442 | (values (get-output-stream-string output) nil status)) |
---|
443 | (close output)))) |
---|
444 | |
---|
445 | #+openmcl |
---|
446 | (defun shell-command (command) |
---|
447 | (let* ((process (create-shell-process command t)) |
---|
448 | (output (file-to-string-as-lines |
---|
449 | (ccl::external-process-output-stream process))) |
---|
450 | (error (file-to-string-as-lines |
---|
451 | (ccl::external-process-error-stream process)))) |
---|
452 | (close (ccl::external-process-output-stream process)) |
---|
453 | (close (ccl::external-process-error-stream process)) |
---|
454 | (values output |
---|
455 | error |
---|
456 | (process-exit-code process)))) |
---|
457 | |
---|
458 | #+openmcl |
---|
459 | (defun create-shell-process (command wait) |
---|
460 | (ccl:run-program |
---|
461 | *shell-path* |
---|
462 | (list "-c" command) |
---|
463 | :input nil :output :stream :error :stream |
---|
464 | :wait wait)) |
---|
465 | |
---|
466 | #+openmcl |
---|
467 | (defun process-exit-code (process) |
---|
468 | (nth-value 1 (ccl:external-process-status process))) |
---|
469 | |
---|
470 | #+digitool |
---|
471 | (defun shell-command (command) |
---|
472 | ;; BUG: I have no idea what this returns |
---|
473 | (ccl::call-system command)) |
---|
474 | |
---|
475 | #+sbcl |
---|
476 | (defun shell-command (command) |
---|
477 | (let* ((process (sb-ext:run-program |
---|
478 | *shell-path* |
---|
479 | (list "-c" command) |
---|
480 | :input nil :output :stream :error :stream)) |
---|
481 | (output (file-to-string-as-lines (sb-impl::process-output process))) |
---|
482 | (error (file-to-string-as-lines (sb-impl::process-error process)))) |
---|
483 | (close (sb-impl::process-output process)) |
---|
484 | (close (sb-impl::process-error process)) |
---|
485 | (values |
---|
486 | output |
---|
487 | error |
---|
488 | (sb-impl::process-exit-code process)))) |
---|
489 | |
---|
490 | #+:abcl |
---|
491 | (defun shell-command (command) |
---|
492 | (let* ((output (make-string-output-stream)) |
---|
493 | (status |
---|
494 | (ext:run-shell-command command :output output))) |
---|
495 | (values (get-output-stream-string output) nil (format nil "~A" status)))) |
---|
496 | |
---|
497 | (defgeneric file-to-string-as-lines (pathname) |
---|
498 | (:documentation "")) |
---|
499 | |
---|
500 | (defmethod file-to-string-as-lines ((pathname pathname)) |
---|
501 | (with-open-file (stream pathname :direction :input) |
---|
502 | (file-to-string-as-lines stream))) |
---|
503 | |
---|
504 | (defmethod file-to-string-as-lines ((stream stream)) |
---|
505 | (with-output-to-string (s) |
---|
506 | (loop for line = (read-line stream nil :eof nil) |
---|
507 | until (eq line :eof) do |
---|
508 | (princ line s) |
---|
509 | (terpri s)))) |
---|
510 | |
---|
511 | ;; copied from ASDF |
---|
512 | (defun pathname-sans-name+type (pathname) |
---|
513 | "Returns a new pathname with same HOST, DEVICE, DIRECTORY as PATHNAME, |
---|
514 | and NIL NAME and TYPE components" |
---|
515 | (make-pathname :name nil :type nil :defaults pathname)) |
---|
516 | |
---|