(in-package #:asdf-install) ;;; 'port.lisp' is loaded before 'variables.lisp' primarily for the ;;; definiton of GET-ENV-VAR, but still needs the following specials ;;; which would otherwise be in 'variables.lisp'. (defparameter *shell-path* "/bin/sh" "The path to a Bourne compatible command shell in physical pathname notation.") (defvar *gpg-command* "gpg" "Location of the gpg binary, if for some reason, it does not appear in the default path for /bin/sh.") ;;; End variables (defvar *temporary-files*) (eval-when (:load-toplevel :compile-toplevel :execute) #+:allegro (require :osi) #+:allegro (require :socket) #+:digitool (require :opentransport) #+:ecl (require :sockets) #+:lispworks (require "comm") ) (defun get-env-var (name) #+:allegro (sys:getenv name) #+:clisp (ext:getenv name) #+:cmu (cdr (assoc (intern (substitute #\_ #\- name) :keyword) ext:*environment-list*)) #+:ecl (ext:getenv name) #+:lispworks (lw:environment-variable name) #+(or :mcl :openmcl) (ccl::getenv name) #+:sbcl (sb-ext:posix-getenv name) #+:scl (cdr (assoc name ext:*environment-list* :test #'string=)) #+abcl (ext:getenv name) ) #-:digitool (defun system-namestring (pathname) (namestring (truename pathname))) #+:digitool (defvar *start-up-volume* (second (pathname-directory (truename "ccl:")))) #+:digitool (defun system-namestring (pathname) ;; this tries to adjust the root directory to eliminate the spurious ;; volume name for the boot file system; it also avoids use of ;; TRUENAME as some applications are for not yet existent files (let ((truename (probe-file pathname))) (unless truename (setf truename (translate-logical-pathname (merge-pathnames pathname *default-pathname-defaults*)))) (let ((directory (pathname-directory truename))) (flet ((string-or-nil (value) (when (stringp value) value)) (absolute-p (directory) (eq (first directory) :absolute)) (root-volume-p (directory) (equal *start-up-volume* (second directory)))) (format nil "~:[~;/~]~{~a/~}~@[~a~]~@[.~a~]" (absolute-p directory) (if (root-volume-p directory) (cddr directory) (cdr directory)) (string-or-nil (pathname-name truename)) (string-or-nil (pathname-type truename))))))) #+:digitool (progn (defun |read-linefeed-eol-comment| (stream char &optional (eol '(#\return #\linefeed))) (loop (setf char (read-char stream nil nil)) (unless char (return)) (when (find char eol) (return))) (values)) (set-syntax-from-char #\linefeed #\space) (set-macro-character #\; #'|read-linefeed-eol-comment| nil *readtable*)) ;; for non-SBCL we just steal this from SB-EXECUTABLE #-(or :digitool) (defvar *stream-buffer-size* 8192) #-(or :digitool) (defun copy-stream (from to) "Copy into TO from FROM until end of the input stream, in blocks of *stream-buffer-size*. The streams should have the same element type." (unless (subtypep (stream-element-type to) (stream-element-type from)) (error "Incompatible streams ~A and ~A." from to)) (let ((buf (make-array *stream-buffer-size* :element-type (stream-element-type from)))) (loop (let ((pos #-(or :clisp :cmu) (read-sequence buf from) #+:clisp (ext:read-byte-sequence buf from :no-hang nil) #+:cmu (sys:read-n-bytes from buf 0 *stream-buffer-size* nil))) (when (zerop pos) (return)) (write-sequence buf to :end pos))))) #+:digitool (defun copy-stream (from to) "Perform copy and map EOL mode." (multiple-value-bind (reader reader-arg) (ccl::stream-reader from) (multiple-value-bind (writer writer-arg) (ccl::stream-writer to) (let ((datum nil)) (loop (unless (setf datum (funcall reader reader-arg)) (return)) (funcall writer writer-arg datum)))))) (defun make-stream-from-url (url) #+(or :sbcl :ecl) (let ((s (make-instance 'sb-bsd-sockets:inet-socket :type :stream :protocol :tcp))) (sb-bsd-sockets:socket-connect s (car (sb-bsd-sockets:host-ent-addresses (sb-bsd-sockets:get-host-by-name (url-host url)))) (url-port url)) (sb-bsd-sockets:socket-make-stream s :input t :output t :buffering :full :external-format :iso-8859-1)) #+:cmu (sys:make-fd-stream (ext:connect-to-inet-socket (url-host url) (url-port url)) :input t :output t :buffering :full) #+:scl (sys:make-fd-stream (ext:connect-to-inet-socket (url-host url) (url-port url)) :input t :output t :buffering :full :external-format :iso-8859-1) #+:lispworks (comm:open-tcp-stream (url-host url) (url-port url) #+(and :lispworks :win32) :element-type #+(and :lispworks :win32) '(unsigned-byte 8)) #+:allegro (socket:make-socket :remote-host (url-host url) :remote-port (url-port url)) #+:clisp (socket:socket-connect (url-port url) (url-host url) :external-format (ext:make-encoding :charset 'charset:iso-8859-1 :line-terminator :unix)) #+:openmcl (ccl:make-socket :remote-host (url-host url) :remote-port (url-port url)) #+:digitool (ccl::open-tcp-stream (url-host url) (url-port url) :element-type 'unsigned-byte) #+:abcl (let ((socket (ext:make-socket (url-host url) (url-port url)))) (ext:get-socket-stream socket :external-format :iso-8859-1))) #+:sbcl (defun return-output-from-program (program args) (with-output-to-string (out-stream) (let ((proc (sb-ext:run-program program args :output out-stream :search t :wait t))) (when (or (null proc) (and (member (sb-ext:process-status proc) '(:exited :signaled)) (not (zerop (sb-ext:process-exit-code proc))))) (return-from return-output-from-program nil))))) #+(or :cmu :scl) (defun return-output-from-program (program args) (with-output-to-string (out-stream) (let ((proc (ext:run-program program args :output out-stream :wait t))) (when (or (null proc) (and (member (ext:process-status proc) '(:exited :signaled)) (not (zerop (ext:process-exit-code proc))))) (return-from return-output-from-program nil))))) #+:lispworks (defun return-output-from-program (program args) (with-output-to-string (out-stream) (unless (zerop (sys:call-system-showing-output (format nil #-:win32 "~A~{ '~A'~}" #+:win32 "~A~{ ~A~}" program args) :prefix "" :show-cmd nil :output-stream out-stream)) (return-from return-output-from-program nil)))) #+(and :clisp (not :win32)) (defun return-output-from-program (program args) (with-output-to-string (out-stream) (let ((stream (ext:run-program program :arguments args :output :stream :wait nil))) (loop for line = (read-line stream nil) while line do (write-line line out-stream))))) #+(and :clisp :win32) (defun return-output-from-program (program args) (with-output-to-string (out-stream) (let ((stream (ext:run-shell-command (format nil "~A~{ ~A~}" program args :output :stream :wait nil)))) (loop for line = (ignore-errors (read-line stream nil)) while line do (write-line line out-stream))))) #+:allegro (defun return-output-from-program (program args) (with-output-to-string (out-stream) (let ((stream (excl:run-shell-command #-:mswindows (concatenate 'vector (list program) (cons program args)) #+:mswindows (format nil "~A~{ ~A~}" program args) :output :stream :wait nil))) (loop for line = (read-line stream nil) while line do (write-line line out-stream))))) #+:ecl (defun return-output-from-program (program args) (with-output-to-string (out-stream) (let ((stream (ext:run-program program args :output :stream))) (when stream (loop for line = (ignore-errors (read-line stream nil)) while line do (write-line line out-stream)))))) #+:openmcl (defun return-output-from-program (program args) (with-output-to-string (out-stream) (let ((proc (ccl:run-program program args :input nil :output :stream :wait nil))) (loop for line = (read-line (ccl:external-process-output-stream proc) nil nil nil) while line do (write-line line out-stream))))) #+:digitool (defun return-output-from-program (program args) (ccl::call-system (format nil "~A~{ '~A'~} 2>&1" program args))) #+:abcl (defun return-output-from-program (program args) (let ((command (format nil "~A ~{ '~A' ~}" program args))) (with-output-to-string (out-stream) (ext:run-shell-command command :output out-stream)))) (defun unlink-file (pathname) ;; 20070208 gwking@metabang.com - removed lisp-specific os-level calls ;; in favor of a simple delete (delete-file pathname)) (defun symlink-files (old new) (let* ((old (#-scl namestring #+scl ext:unix-namestring old)) (new (#-scl namestring #+scl ext:unix-namestring new #+scl nil)) ;; 20070811 - thanks to Juan Jose Garcia-Ripoll for pointing ;; that ~a would wreck havoc if the working directory had a space ;; in the pathname (command (format nil "ln -s ~s ~s" old new))) (format t "~S~%" command) (shell-command command))) (defun maybe-symlink-sysfile (system sysfile) (declare (ignorable system sysfile)) #-(or :win32 :mswindows) (let ((target (merge-pathnames (make-pathname :name (pathname-name sysfile) :type (pathname-type sysfile)) system))) (when (probe-file target) (unlink-file target)) (symlink-files sysfile target))) ;;; --------------------------------------------------------------------------- ;;; read-header-line ;;; --------------------------------------------------------------------------- #-:digitool (defun read-header-line (stream) (read-line stream)) #+:digitool (defun read-header-line (stream &aux (line (make-array 16 :element-type 'character :adjustable t :fill-pointer 0)) (byte nil)) (print (multiple-value-bind (reader arg) (ccl::stream-reader stream) (loop (setf byte (funcall reader arg)) (case byte ((nil) (return)) ((#.(char-code #\Return) #.(char-code #\Linefeed)) (case (setf byte (funcall reader arg)) ((nil #.(char-code #\Return) #.(char-code #\Linefeed))) (t (ccl:stream-untyi stream byte))) (return)) (t (vector-push-extend (code-char byte) line)))) (when (or byte (plusp (length line))) line)))) (defun open-file-arguments () (append #+sbcl '(:external-format :latin1) #+:scl '(:external-format :iso-8859-1) #+abcl '(:external-format :iso-8859-1) #+(or :clisp :digitool (and :lispworks :win32)) '(:element-type (unsigned-byte 8)))) (defun download-url-to-file (url file-name) "Resolves url and then downloads it to file-name; returns the url actually used." (multiple-value-bind (response headers stream) (loop (destructuring-bind (response headers stream) (url-connection url) (unless (member response '(301 302)) (return (values response headers stream))) (close stream) (setf url (header-value :location headers)))) (when (>= response 400) (error 'download-error :url url :response response)) (let ((length (parse-integer (or (header-value :content-length headers) "") :junk-allowed t))) (installer-msg t "Downloading ~A bytes from ~A to ~A ..." (or length "some unknown number of") url file-name) (force-output) #+:clisp (setf (stream-element-type stream) '(unsigned-byte 8)) (let ((ok? nil) (o nil)) (unwind-protect (progn (setf o (apply #'open file-name :direction :output :if-exists :supersede (open-file-arguments))) #+(or :cmu :digitool) (copy-stream stream o) #-(or :cmu :digitool) (if length (let ((buf (make-array length :element-type (stream-element-type stream)))) #-:clisp (read-sequence buf stream) #+:clisp (ext:read-byte-sequence buf stream :no-hang nil) (write-sequence buf o)) (copy-stream stream o)) (setf ok? t)) (when o (close o :abort (null ok?)))))) (close stream)) (values url)) (defun download-url-to-temporary-file (url) "Attempts to download url to a new, temporary file. Returns the resolved url and the file name \(as multiple values\)." (let ((tmp (temp-file-name url))) (pushnew tmp *temporary-files*) (values (download-url-to-file url tmp) tmp))) (defun gpg-command () (find-shell-command *gpg-command*)) (defun gpg-results (package signature) (let ((tags nil)) (with-input-from-string (gpg-stream (shell-command (format nil "~s --status-fd 1 --verify ~s ~s" (gpg-command) (namestring signature) (namestring package)))) (loop for l = (read-line gpg-stream nil nil) while l do (print l) when (> (mismatch l "[GNUPG:]") 6) do (destructuring-bind (_ tag &rest data) (split-sequence-if (lambda (x) (find x '(#\Space #\Tab))) l) (declare (ignore _)) (pushnew (cons (intern (string-upcase tag) :keyword) data) tags))) tags))) #+allegro (defun shell-command (command) (multiple-value-bind (output error status) (excl.osi:command-output command :whole t) (values output error status))) #+clisp (defun shell-command (command) ;; BUG: CLisp doesn't allow output to user-specified stream (values nil nil (ext:run-shell-command command :output :terminal :wait t))) #+(or :cmu :scl) (defun shell-command (command) (let* ((process (ext:run-program *shell-path* (list "-c" command) :input nil :output :stream :error :stream)) (output (file-to-string-as-lines (ext::process-output process))) (error (file-to-string-as-lines (ext::process-error process)))) (close (ext::process-output process)) (close (ext::process-error process)) (values output error (ext::process-exit-code process)))) #+ecl (defun shell-command (command) ;; If we use run-program, we do not get exit codes (values nil nil (ext:system command))) #+lispworks (defun shell-command (command) ;; BUG: Lispworks combines output and error streams (let ((output (make-string-output-stream))) (unwind-protect (let ((status (system:call-system-showing-output command :prefix "" :show-cmd nil :output-stream output))) (values (get-output-stream-string output) nil status)) (close output)))) #+openmcl (defun shell-command (command) (let* ((process (create-shell-process command t)) (output (file-to-string-as-lines (ccl::external-process-output-stream process))) (error (file-to-string-as-lines (ccl::external-process-error-stream process)))) (close (ccl::external-process-output-stream process)) (close (ccl::external-process-error-stream process)) (values output error (process-exit-code process)))) #+openmcl (defun create-shell-process (command wait) (ccl:run-program *shell-path* (list "-c" command) :input nil :output :stream :error :stream :wait wait)) #+openmcl (defun process-exit-code (process) (nth-value 1 (ccl:external-process-status process))) #+digitool (defun shell-command (command) ;; BUG: I have no idea what this returns (ccl::call-system command)) #+sbcl (defun shell-command (command) (let* ((process (sb-ext:run-program *shell-path* (list "-c" command) :input nil :output :stream :error :stream)) (output (file-to-string-as-lines (sb-impl::process-output process))) (error (file-to-string-as-lines (sb-impl::process-error process)))) (close (sb-impl::process-output process)) (close (sb-impl::process-error process)) (values output error (sb-impl::process-exit-code process)))) #+:abcl (defun shell-command (command) (let* ((output (make-string-output-stream)) (status (ext:run-shell-command command :output output))) (values (get-output-stream-string output) nil (format nil "~A" status)))) (defgeneric file-to-string-as-lines (pathname) (:documentation "")) (defmethod file-to-string-as-lines ((pathname pathname)) (with-open-file (stream pathname :direction :input) (file-to-string-as-lines stream))) (defmethod file-to-string-as-lines ((stream stream)) (with-output-to-string (s) (loop for line = (read-line stream nil :eof nil) until (eq line :eof) do (princ line s) (terpri s)))) ;; copied from ASDF (defun pathname-sans-name+type (pathname) "Returns a new pathname with same HOST, DEVICE, DIRECTORY as PATHNAME, and NIL NAME and TYPE components" (make-pathname :name nil :type nil :defaults pathname))