;;; RPM updates ;;; ;;; Copyright (C) 1998-2002 by Sam Steingold ;;; This is Free Software, covered by the GNU GPL (v2) ;;; See http://www.gnu.org/copyleft/gpl.html ;;; ;;; $Id: rpm.lisp,v 2.12 2002/04/21 20:02:40 sds Exp $ ;;; $Source: /cvsroot/clocc/clocc/src/cllib/rpm.lisp,v $ (eval-when (compile load eval) (require :cllib-base (translate-logical-pathname "clocc:src;cllib;base")) ;; `url' - generic function (require :cllib-url (translate-logical-pathname "cllib:url")) ;; `with-open-pipe', `pipe-input' (require :port-shell (translate-logical-pathname "port:shell"))) (in-package :cllib) (export '(*rpm-locations* rpm show-rpms rpm-to-be-installed rpm-get-list rpm-get-new-rpms rpm-list-rpm rpm-clean-up)) ;;; ;;; download data ;;; (eval-when (compile load eval) ; CMUCL (defstruct (download-data (:conc-name dld-) #+cmu (:print-function print-struct-object)) (url +bad-url+ :type url) ; location (err nil :type (or null error)) ; was there an error? (all nil :type list) ; all files at the location (fls nil :type list)) ; files found at the location ) (defmethod url ((dld download-data)) (dld-url dld)) (defun rpm-list-size (rpms) "Sum the sizes of the RPMs in the list." (reduce #'+ rpms :key (safe-fun1 rpm-size rpm-p 0))) (defmethod print-object ((dld download-data) (out stream)) (when *print-readably* (return-from print-object (call-next-method))) (write (dld-url dld) :stream out) (cond ((dld-err dld) (format out "~% - ~a" (dld-err dld))) ((dld-all dld) (format out " [~:d RPMs, ~:d bytes]" (length (dld-all dld)) (rpm-list-size (dld-all dld))))) (when (dld-fls dld) (format out " [~:d RPMs, ~:d bytes]: ~{~<~%~10t ~1,74:; ~a~>~^,~}." (length (dld-fls dld)) (rpm-list-size (dld-fls dld)) (dld-fls dld)))) (defun dld-reset (dld) "Reset ERR and FLS to NIL." (declare (type download-data dld)) (setf (dld-err dld) nil (dld-fls dld) nil (dld-all dld) nil)) (defcustom *rpm-present* simple-vector (make-array 0) "The list of the present packages.") (defcustom *rpm-locations* list (macrolet ((mk (&rest args) `(make-download-data :url (make-url ,@args)))) (list (mk :prot :ftp :host "rawhide.redhat.com" :path "/pub/rawhide/i386/RedHat/RPMS/") (mk :prot :ftp :host "ftp.rge.com" :path "/pub/systems/linux/redhat/rawhide/i386/RedHat/RPMS/") (mk :prot :ftp :host "ftp.redhat.com" :path "/pub/redhat/current/i386/RedHat/RPMS/") (mk :prot :ftp :host "updates.redhat.com" :path "/current/i386/") (mk :prot :ftp :host "updates.redhat.com" :path "/current/noarch/") (mk :prot :ftp :host "contrib.redhat.com" :path "/contrib/libc6/i386/") (mk :prot :ftp :host "contrib.redhat.com" :path "/contrib/noarch/noarch/"))) ;;(mk :prot :ftp :host "developer.redhat.com" ;; :path "/pub/rhcn/RPMS/i386/") ;;(mk :prot :ftp :host "developer.redhat.com" ;; :path "/pub/rhcn/RPMS/noarch/") ;;(mk :prot :ftp :host "ftp.inconnect.com" ; gnome ;; :path "/pub/unix/linux/redhat-6.0/contrib-updates/") ;; (mk :prot :ftp :host "ftp.suse.com" :path ; Xserver ;; "/pub/suse_update/XFree86-3.3.3.1-SuSE/glibc2/") ;; (mk :prot :ftp :host "ftp.cc.gatech.edu" :path ;; "/pub/linux/distributions/suse/suse_update/XFree86-3.3.3.1-SuSE/glibc2/") ;; (mk :prot :ftp :host "ftp.dosemu.org" :path "/dosemu/") "*The list of `download-data' structs where to look for RPMs.") (defcustom *rpm-locations-timeout* integer (* 3600 6) "*The number of seconds after which `*rpm-locations*' is updated.") (defcustom *rpm-local-paths* list '(#p"/var/tmp/RPMS/") "The paths to the local RPMs.") (defcustom *rpm-local-target* pathname #p"/var/tmp/" "The local path where to download stuff.") (defcustom *rpm-command-line* simple-string "rpm --queryformat \"%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH} %{BUILDTIME} %{INSTALLTIME} %{SIZE}\\n\"" "The rpm(1) command line to print all the installed packages. See `rpm --querytags' for more tags") (defcustom *rpm-timeout* index-t 600 "*The default timeout (in seconds) for RPM network operations.") (defcustom *rpm-max-retry* (or null index-t) 10 "*The default value of max-retry for RPM. If nil, retry ad infinitum, otherwise a positive fixnum.") ;;; ;;; RPM ;;; (eval-when (compile load eval) ; CMUCL (defstruct (rpm #+cmu (:print-function print-struct-object)) (name "name?" :type simple-string) (vers "vers?" :type simple-string) (rels "rels?" :type simple-string) (arch "arch?" :type simple-string) (btime nil :type (or null integer)) ; build time (itime nil :type (or null integer)) ; install time (ftime nil :type (or null integer)) ; file time (size nil :type (or null integer)) (note nil)) ) (defconst +bad-rpm+ rpm (make-rpm) "*The convenient constant for init.") (defmethod print-object ((rpm rpm) (out stream)) (if *print-readably* (call-next-method) (format out "~:[~;\"~]~a-~a-~a.~a~:[~;\"~]" *print-escape* (rpm-name rpm) (rpm-vers rpm) (rpm-rels rpm) (rpm-arch rpm) *print-escape*))) (defsubst rpm-pos (name) "Find the position of the RPM in `*rpm-present*'." (declare (simple-string name)) (binary-pos name *rpm-present* :test #'string< :key #'rpm-name)) (defun rpm-skip-p (rpm) "Do not download such an RPM." (declare (type rpm rpm)) (or (string-beg-with "pre" (rpm-vers rpm)) (and (search "kernel" (rpm-name rpm) :test #'char=) (or (notevery #'digit-char-p (rpm-rels rpm)) (string= (rpm-vers rpm) (rpm-vers (svref *rpm-present* (rpm-pos (rpm-name rpm))))))))) (defcustom *rpm-skip* (or (function (rpm) boolean) string null) #'rpm-skip-p "Skip these RPMs.") (defun short-string-to-rpm (name) "Convert the short string like `pack-1.2-3.i386.rpm' to RPM. Do not use it!!! Use the generic function `rpm' instead!!!" (declare (simple-string name)) (let* ((len (length name)) (p0 (position #\- name :from-end t)) (p1 (position #\- name :from-end t :end (1- p0))) (p2 (if (string= ".rpm" name :start2 (- len 4)) (- len 4) len)) (p3 (or (position #\. name :from-end t :start p0 :end p2) p2))) (make-rpm :name (subseq name 0 p1) :vers (subseq name (1+ p1) p0) :rels (subseq name (1+ p0) p3) :arch (subseq name (1+ p3) p2)))) (eval-when (compile load eval) (fmakunbound 'rpm)) (defgeneric rpm (obj) (:documentation "Convert to RPM.") (:method ((obj rpm)) obj) (:method ((obj symbol)) (unintern obj) (short-string-to-rpm (symbol-name obj))) (:method ((path pathname)) (let ((rr (short-string-to-rpm (pathname-name path)))) (setf (rpm-note rr) (list path) (rpm-size rr) (file-size path) (rpm-ftime rr) (file-write-date path)) rr))) (declaim (ftype (function (t) rpm) rpm)) (defmethod rpm ((obj string)) ;; should work with strings like: ;; "pack-1.2-3.i386.rpm" ;; "-rw-r--r-- 1 sds sds 220876 Mar 20 19:29 mgetty-voice-1.1.20-1.i386.rpm" ;; "rpm-2.92-14.i386 922075412 922585615 1667226" (let* ((toks (string-tokens (nsubstitute #\- #\: (string-trim +whitespace+ obj)))) (rpm (ignore-errors (rpm (car toks))))) (assert (symbolp (car toks)) () "The first token in ~s is not a symbol: cannot convert to RPM" obj) (cond (rpm (setf (rpm-btime rpm) (if (cadr toks) (+ +unix-epoch+ (cadr toks))) (rpm-itime rpm) (if (caddr toks) (+ +unix-epoch+ (caddr toks))) (rpm-size rpm) (cadddr toks)) (return-from rpm rpm)) ((char/= #\- (schar (symbol-name (car toks)) 0)) (error "special file cannot be an RPM: ~s" obj)) ((numberp (fourth toks)) (setq toks (cdddr toks))) ((numberp (fifth toks)) (setq toks (cddddr toks)))) (assert (symbolp (fifth toks)) () "~s: ~s is not a symbol: cannot convert to RPM" obj (fifth toks)) (setf rpm (rpm (fifth toks)) (rpm-size rpm) (first toks) (rpm-ftime rpm) (let ((day (third toks)) (mon (infer-month (second toks)))) (if (numberp (fourth toks)) (encode-universal-time 0 0 0 day mon (fourth toks)) (let* ((str (symbol-name (fourth toks))) (mi (parse-integer str :start 3)) (ho (parse-integer str :end 2)) (now (get-universal-time)) (ye (nth-value 5 (decode-universal-time now))) (ft (encode-universal-time 0 mi ho day mon ye))) (if (< ft now) ft (encode-universal-time 0 mi ho day mon (1- ye))))))) rpm)) (defun rpm< (r0 r1) "Sorting order on RPMs." (declare (type rpm r0 r1)) (or (string< (rpm-name r0) (rpm-name r1)) (and (string= (rpm-name r0) (rpm-name r1)) (or (version< (rpm-vers r0) (rpm-vers r1)) (and (string= (rpm-vers r0) (rpm-vers r1)) (or (release< (rpm-rels r0) (rpm-rels r1)) (and (string= (rpm-rels r0) (rpm-rels r1)) (string< (rpm-arch r0) (rpm-arch r1))))))))) (defun version< (v0 v1) "Sorting order for versions." (declare (simple-string v0 v1)) (do* ((b0 0 (1+ e0)) (b1 0 (1+ e1)) (e0 (position #\. v0) (position #\. v0 :start b0)) (e1 (position #\. v1) (position #\. v1 :start b1))) ((or (null e0) (null e1) (string/= v0 v1 :start1 b0 :start2 b1 :end1 e0 :end2 e1)) (or (release< (subseq v0 b0 e0) (subseq v1 b1 e1)) (and (string= v0 v1 :start1 b0 :start2 b1 :end1 e0 :end2 e1) (null e0) e1))))) (defun release< (v0 v1) "Sorting order for releases." (declare (simple-string v0 v1)) (let ((l0 (or (position-if-not #'digit-char-p v0) (length v0))) (l1 (or (position-if-not #'digit-char-p v1) (length v1)))) (declare (type index-t l0 l1)) (cond ((and (= l0 (length v0)) (< l1 (length v1)) ; v1 - beta (char-equal #\b (schar v1 l1))) nil) ((and (= l1 (length v1)) (< l0 (length v0)) ; v0 - beta (char-equal #\b (schar v0 l0))) t) ((> l0 l1) (string< v0 (format nil "~v,,,'0@a" l0 v1))) ((< l0 l1) (string< (format nil "~v,,,'0@a" l1 v0) v1)) ((string< v0 v1))))) (defun rpm-merge-notes (r0 r1) "Put the notes from r1 into r0." (declare (type rpm r0 r1)) (setf (rpm-note r0) (nconc (rpm-note r0) (rpm-note r1) (when (rpm-itime r1) (list (cons (time2date (rpm-itime r1)) (time2date (rpm-btime r1))))))) r0) (defun rpm-read (in) "Read RPMs from the stream till EOF. Close the stream." (declare (stream in)) (sort (loop :for line :of-type (or null simple-string) = (read-line in nil nil) :for rr :of-type (or null rpm) = (ignore-errors (rpm line)) :while line :when rr :collect rr :finally (close in)) #'rpm<)) (defun rpm-installed () "Return the list of RPMs installed." (with-open-pipe (st (pipe-input *rpm-command-line* "-qa")) (rpm-read st))) (defun rpm-path-valid-p (path) "Check for validity of the RPM." (declare (type pathname path)) (with-open-pipe (st (pipe-input *rpm-command-line* "-qp" path)) (read-line st nil nil))) (defun rpm-downloaded () "Return the list of RPMs already downloaded." (sort (mapcar #'rpm (delete-if-not #'rpm-path-valid-p (mapcan (lambda (pp) (directory (merge-pathnames "*.rpm" pp))) (adjoin *rpm-local-target* *rpm-local-paths* :test #'equalp)))) #'rpm<)) (defun rpm-present () "Return the simple vector of RPMs installed and already downloaded." (delete-duplicates (merge 'simple-vector (rpm-downloaded) (rpm-installed) (lambda (r0 r1) (let ((res (rpm< r0 r1))) (when (string= (rpm-name r0) (rpm-name r1)) (if res (rpm-merge-notes r1 r0) (rpm-merge-notes r0 r1))) res))) :test #'string= :key #'rpm-name)) (defun rpm-available (url &key (out *standard-output*) (err *error-output*) ((:max-retry *url-max-retry*) *rpm-max-retry*) ((:timeout *url-timeout*) *rpm-timeout*) (retry 2)) "Return the list of all available RPMs." (declare (type url url) (type (or null stream) err out) (type index-t retry)) (handler-case (with-open-url (sock url :err err) (let ((data (ftp-get-passive-socket sock err nil))) (when (ignore-errors (url-ask sock err :list "list *.rpm")) ; 150 (prog1 (map-in (lambda (rr) (setf (rpm-note rr) (list url)) rr) (rpm-read data)) (url-ask sock err :list))))) ; 226 ((or path login) (co) (mesg :log err " * rpm-available [~a]: Cannot login:~% - ~a~%" url co) (error co)) (network (co) (mesg :log err " * rpm-available [~a]:~% - ~a~%" url co) (cond ((plusp retry) (mesg :err err " * rpm-available: ~r more attempt~:p~%" retry) (rpm-available url :out out :err err :retry (1- retry))) (t (error co)))))) (defun rpm-prune-list (rpms) "Remove the elements in the list which are better in `*rpm-present*'. The elements matching `*rpm-skip*' are removed, too." (declare (list rpms)) (remove-if-not (lambda (rpm) (let ((pos (rpm-pos (rpm-name rpm)))) (and pos (rpm< (svref *rpm-present* pos) rpm) (not (typecase *rpm-skip* (string (search *rpm-skip* (rpm-name rpm) :test #'char=)) (function (funcall *rpm-skip* rpm)) (null nil) (t (error 'case-error :proc 'rpm-prune-list :args (list '*rpm-skip* *rpm-skip* 'function 'string nil)))))))) rpms)) (defun rpm-print (rpm &optional idx (out *standard-output*)) "Print RPM in a full form." (declare (type rpm rpm) (stream out)) (format out "~@[~3d. ~]~a [~@[~:d bytes, ~]~a/~a/~a]:~{~%~10t~a~}~%" idx rpm (rpm-size rpm) (if (rpm-btime rpm) (time2date (rpm-btime rpm))) (if (rpm-itime rpm) (time2date (rpm-itime rpm))) (if (rpm-ftime rpm) (time2date (rpm-ftime rpm))) (rpm-note rpm))) (defun rpm-get-available (&key force (out *standard-output*) ((:max-retry *url-max-retry*) *rpm-max-retry*) ((:timeout *url-timeout*) *rpm-timeout*) (err *error-output*)) "Get the list of all RPMs in `*rpm-locations*' and put it there. Then generate the list to download." (declare (type (or null stream) out err)) (when (or force (notany #'dld-all *rpm-locations*) (null (get '*rpm-locations* 'updated)) (> (- (get-universal-time) (get '*rpm-locations* 'updated)) *rpm-locations-timeout*)) (format out " *** Getting the list of new packages...~%") (with-timing (:out out) (let ((na 0) (le 0) #+clisp (#+lisp=cl ext:*pprint-first-newline* #-lisp=cl lisp:*pprint-first-newline* nil)) (declare (type index-t na le)) (dolist (dld *rpm-locations*) (declare (type download-data dld)) (dld-reset dld) (format out " *** processing `~a'...~%" dld) (handler-case (with-timing (:out out) (setf (dld-all dld) (rpm-available (dld-url dld) :out out :err err) (dld-fls dld) (rpm-prune-list (dld-all dld)) le (length (dld-fls dld))) (incf na le) (format out " *** ~d new RPM~:p (out of ~d):~% --> ~a~% * " le (length (dld-all dld)) dld)) (error (co) (setf (dld-err dld) co) (format out " *** failed:~% - ~a~%" co)))) (format out " *** ~d new RPM~:p in ~r URL~:p " na (length *rpm-locations*)))) (setf (get '*rpm-locations* 'updated) (get-universal-time)) (dolist (dld *rpm-locations*) (when (dld-fls dld) (format out " * ~a~%" dld))))) ;;;###autoload (defun show-rpms (&optional (what "") (local t)) "Print some RPMs." (declare (simple-string what)) (if local (rpm-get-present) (rpm-get-available)) (let ((nrpm (if local (length *rpm-present*) (reduce #'+ *rpm-locations* :key (compose length dld-all)))) #+clisp (#+lisp=cl ext:*pprint-first-newline* #-lisp=cl lisp:*pprint-first-newline* nil) (nf 0)) (declare (type index-t nf)) (if (zerop (length what)) (if local (format t "~& *** ~d RPM~:p~%" nrpm) (format t "~& *** ~d RPM~:p in ~r URL~:p~%" nrpm (length *rpm-locations*))) (if local (format t "~&Searching ~d RPM~:p for `~a'~%" nrpm what) (format t "~&Searching ~d RPM~:p (~r URL~:p) for `~a'~%" nrpm (length *rpm-locations*) what))) (dolist (elt (if local (list *rpm-present*) *rpm-locations*)) (unless local (format t " *** ~a~%" elt)) (map nil (lambda (rpm) (declare (type rpm rpm)) (when (search what (rpm-name rpm) :test #'char-equal) (rpm-print rpm (incf nf)))) (if local elt (dld-all elt)))) (format t "~[None~:;~:*~d~] found.~%" nf) nf)) (defun delete-rpms (what) "Delete some RPMs from the vector `*rpm-present*'." (declare (simple-string what)) (flet ((killrpm (what where) (delete what where :key #'rpm-name :test (lambda (wh nm) (search wh nm :test #'char-equal))))) (when (and (/= 0 (show-rpms what)) (yes-or-no-p "Delete all these? ")) (setq *rpm-present* (killrpm what *rpm-present*)) (dolist (dld *rpm-locations*) (setf (dld-fls dld) (killrpm what (dld-fls dld))) (format t " -> ~a~%" dld)) (show-rpms what)))) (defun rpm-current (name) "Return the rpm corresponding to this name." (declare (simple-string name)) (with-open-pipe (st (pipe-input *rpm-command-line* "-q" name)) (let ((ln (read-line st nil nil))) (when ln (rpm ln))))) (defun rpm-to-be-installed () "Print RPMs which are downloaded but not yet installed." (loop :for rpm :of-type rpm :across *rpm-present* :with ii :of-type index-t = 0 :unless (rpm-itime rpm) :do (rpm-print rpm (incf ii)))) (defun rpm-get-present (&optional force (out *standard-output*)) "Make sure `*rpm-present*' is initialized." (declare (type (or null stream) out)) (when (or force (zerop (length *rpm-present*))) (with-timing (:out out) (mesg t out " * Finding the RPMs...") (setq *rpm-present* (rpm-present)) (mesg t out "done [~d package~:p]" (length *rpm-present*))))) ;;;###autoload (defun rpm-get-list (url rpms &key (out *standard-output*) (err *error-output*) ((:max-retry *url-max-retry*) *rpm-max-retry*) ((:timeout *url-timeout*) *rpm-timeout*) (len (length rpms)) (retry 2)) "Get the list of RPMS from URL." (declare (type url url) (list rpms) (type (or null stream) out err) (type index-t len retry)) (handler-case (with-open-url (sock url :err err) (url-ask sock err :type "type i") ; 200 (loop :for rpm :in rpms :and ii :of-type index-t :upfrom 1 :for pos = (rpm-pos (rpm-name rpm)) :and all :of-type file-size-t = (reduce #'+ rpms :key #'rpm-size) :then (- all (rpm-size rpm)) :with got :of-type file-size-t = 0 :do (format out " *** [~d/~d] ~a~@[ [~:d bytes~@[, ~/pr-secs/~]~ ~@[/~/pr-secs/~]]~]~%" ii len rpm (rpm-size rpm) (url-eta (rpm-size rpm)) (url-eta all)) :if (rpm< (svref *rpm-present* pos) rpm) :do (multiple-value-bind (tot el st path) (ftp-get-file sock (format nil "~a.rpm" rpm) *rpm-local-target* :err err :bin t :out out) (declare (type file-size-t tot) (double-float el) (simple-string st) (type pathname path)) (cond ((rpm-path-valid-p path) (incf got tot) (format out " *** done [~:d bytes, ~a, ~:d bytes/sec] *** replacing ~a with ~a in `*rpm-present*'~%" tot st (round tot el) (svref *rpm-present* pos) rpm) (setf (svref *rpm-present* pos) (rpm-merge-notes rpm (svref *rpm-present* pos)))) (t (format out " *** file `~a' is corrupted:~%" path) (with-open-pipe (st (pipe-input "rpm -K" path))) (delete-file path)))) :else :do (format out " *** already have~25t`~a'.~%" rpm) :end :finally (return got))) (network (co) (mesg :log err "rpm-get-list [~a]:~% - ~a~%" url co) (cond ((plusp retry) (mesg :err err "rpm-get-list: ~r more attempt~:p~%" retry) (rpm-get-list url rpms :out out :err err :retry (1- retry) :len len)) (0))))) ;;;###autoload (defun rpm-get-new-rpms (&key force (out *standard-output*) ((:max-retry *url-max-retry*) *rpm-max-retry*) ((:timeout *url-timeout*) *rpm-timeout*) (err *error-output*)) ;; (rpm-get-new-rpms) "Download the RPMs from `*rpm-locations*'." (declare (type (or null stream) err out)) (format out "~& *** [~a] `rpm-get-new-rpms' started.~%" (current-time nil)) (rpm-get-present force out) (format out " *** ~d package~:p present~%" (length *rpm-present*)) (rpm-get-available :force force :out out :err err) (let ((bt (get-float-time nil)) (glob 0) #+clisp (#+lisp=cl ext:*pprint-first-newline* #-lisp=cl lisp:*pprint-first-newline* nil)) (declare (double-float bt) (type file-size-t glob)) (dolist (dld *rpm-locations*) (declare (type download-data dld)) (cond ((dld-fls dld) (let ((len (length (dld-fls dld)))) (format out " *** getting ~d file~:p: ~a~%" len dld) (incf glob (rpm-get-list (dld-url dld) (dld-fls dld) :out out :err err :len len)) (format out " *** Pruning [~d]..." len) (force-output) (setf (dld-fls dld) (rpm-prune-list (dld-fls dld))) (format out "done [~d left]~%" (length (dld-fls dld))))) ((dld-err dld) (format out " *** ~a~%" dld)) (t (format out " *** ~a:~60tno new packages~%" dld)))) (format out " *** [~a] done" (current-time nil)) (if (zerop glob) (format out ".~%") (multiple-value-bind (el st) (elapsed bt nil t) (format out " [~:d bytes, ~a, ~:d bytes/sec]~%" glob st (round glob el)))) glob)) ;;;###autoload (defun rpm-list-rpm (name &key (out *standard-output*) (err *error-output*) ((:max-retry *url-max-retry*) *rpm-max-retry*) ((:timeout *url-timeout*) *rpm-timeout*) local) "Look for the RPM on all sites. If `local' keyword argument is non-nil, use only the data already available in `*rpm-locations*'." (dolist (dld *rpm-locations*) (if local (loop :initially (format out " *** ~a:~%" dld) :for rpm :in (dld-all dld) :with ii :of-type index-t = 0 :when (search name (rpm-name rpm)) :do (format out "~3d: ~a~%" (incf ii) rpm)) (ignore-errors (with-open-url (sock (dld-url dld) :err err) (format out " *** ~a:~%" dld) (ftp-list sock :name name :err err :out out)))))) ;;(defgeneric rpm-clean-up (&optional (dirs *rpm-local-paths*)) ;; (:documentation "Remove old RPM files.")) ;; (rpm-clean-up "/var/tmp/") ;; (rpm-clean-up) ;; (rpm-clean-up "/var/tmp/o/") ;;;###autoload (defun rpm-clean-up (&optional (dirs (remove *rpm-local-target* *rpm-local-paths* :test #'equalp)) (out *standard-output*)) "Remove old RPM files." (declare (stream out)) (etypecase dirs ((or string pathname) (format out " ***** Cleaning up `~a'~%" dirs) (flet ((to-file (rpm) (merge-pathnames (format nil "~a.rpm" rpm) dirs))) (do* ((fl (sort (map-in #'rpm (directory (merge-pathnames "*.rpm" dirs))) #'rpm<) (cdr fl)) (nn 0) (tot 0 (1+ tot)) (fs 0) (tfs 0) (sz 0) nfile (rm nil nil) (file (to-file (car fl)) nfile) (good (rpm-path-valid-p file))) ((null fl) (format out " ***** ~:d/~:d file~:p (~:d/~:d byte~:p) deleted~%" nn tot fs tfs) (values nn fs tot tfs)) (declare (type index-t nn tot) (type file-size-t sz fs tfs)) (setq sz (file-size file) tfs (+ sz tfs)) (unless good (setq rm "corruption")) (when (cdr fl) (setq nfile (to-file (cadr fl)) good (rpm-path-valid-p nfile)) (unless good (with-open-pipe (st (pipe-input "rpm -K" nfile)))) (when (and good (string= (rpm-name (car fl)) (rpm-name (cadr fl)))) (setq rm (cadr fl)))) (when rm (format out " ~3d * removing ~a (~:d bytes) because of ~a..." (incf nn) (car fl) sz rm) (multiple-value-bind (tt co) (ignore-errors (delete-file file)) (format out "~:[failed: ~a~;done~*~]~%" tt co)) (incf fs sz))))) (sequence (let ((nnt 0) (fst 0) (tott 0) (tfst 0)) (declare (type index-t nnt tott) (type file-size-t fst tfst)) (map nil (lambda (dd) (multiple-value-bind (nn fs tot tfs) (rpm-clean-up dd out) (declare (type index-t nn tot) (type file-size-t fs tfs)) (incf nnt nn) (incf fst fs) (incf tott tot) (incf tfst tfs))) dirs) (format out " Total ~:d/~:d file~:p (~:d/~:d byte~:p) deleted~%" nnt tott fst tfst) (values nnt fst tott tfst))))) ;;; active mode ftp - doesn't work - why?! #+nil ;; (or clisp allegro) (progn (defun local-host (sock) #+clisp (let ((ho (ext:socket-stream-local sock))) (subseq ho 0 (position #\Space ho :test #'char=))) #+allegro (socket:ipaddr-to-dotted (socket:local-host sock))) (defun local-port (serv) (#+clisp ext:socket-server-port #+allegro socket:local-port serv)) (defun ftp-port-command (sock serv &optional (out *standard-output*)) (let ((port (local-port serv))) (declare (type index-t port)) (url-ask sock out :port "port ~a,~d,~d" ; 200 (substitute #\, #\. (local-host sock)) (logand #xff (ash port -8)) (logand #xff port)))) ) #+nil (progn (setq sock (open-url (url "ftp://ftp.gnu.org/pub/gnu/"))) (setq sock (open-url (url "ftp://ftp.nyu.edu/pub/"))) (setq serv (open-socket-server sock)) (ftp-port-command sock serv) (read-line sock) (progn ;; (format sock "retr clisp.README~%") (format sock "list~%") (setq sk (socket-accept serv))) (read-line sk) (close sock) (close serv) (close sk) (setq serv (ext:socket-server 3288)) (ext:socket-server-close serv) (ext:socket-stream-handle sock) (ext:socket-stream-host sock) (ext:socket-stream-port sock) (ext:socket-stream-peer sock) (ext:socket-stream-local sock) (ftp-get-file sock "wn.README" "/var/tmp/" ) (ftp-list sock) (url-ask sock *standard-output* :pwd "pwd") ; 257 (url-ask sock *standard-output* :help "help ~a" "type") ; 214 (url-ask sock *standard-output* :help "help") ; 214 (url-ask sock *standard-output* :cwd "cwd gnu") ; 250 (ftp-list sock) (show-rpms "print") (setq serv (ext:socket-server 0)) (ext:socket-server-close serv) (setq sock (ext:socket-connect 21 "ftp.gnu.org")) (setq serv (ext:socket-server sock)) (setq sock (ext:socket-connect 21 "mute")) ;socket.d:fill_hostname ; printf ("#(%lu \"%s\" \"%s\" %u)\n", hd->host, hd->hostname, ; hd->truename, hd->port); ) (provide :cllib-rpm) ;;; file rpm.lisp ends here