Pretty decent overhaul of the elisp tools.
[pkgusr] / lisp / pkgusr.el
index e975c86..43ff0de 100644 (file)
@@ -1,11 +1,11 @@
 ;; pkgusr.el --- elisp tools for LFS pkgusr package management   -*- Emacs-Lisp -*-
 
-;; Copyright (C) 2007 Steve Youngs
+;; Copyright (C) 2007 - 2014 Steve Youngs
 
 ;; Author:     Steve Youngs <steve@sxemacs.org>
 ;; Maintainer: Steve Youngs <steve@sxemacs.org>
 ;; Created:    <2007-07-13>
-;; Time-stamp: <Wednesday Mar 12, 2014 17:01:00 steve>
+;; Time-stamp: <Sunday Mar 16, 2014 01:10:12 steve>
 ;; Homepage:   N/A
 ;; Keywords:   utils package-management
 
 
 ;;; Todo:
 ;;
-;;     
-
-;;; ChangeLog:
+;;   o Reduce code duplication through use of macros
+;;   o View build logs
+;;   o Run builds
 ;;
-;;  This is just a place holder so `pkgusr-commentary' will work
-;;  properly.  See the ChangeLog file for changes.
 
 ;;; Code:
 (defvar pkgusr-pkg-history nil
 (defun pkgusr-all-pkgs ()
   "Return a list of all installed packages."
   (let ((lst (with-temp-buffer
-              (erase-buffer)
               (insert-file-contents "/etc/group")
               (re-search-forward "^install:x:9999:" nil t)
               (narrow-to-region (point) (point-at-eol))
               (split-string-by-char (buffer-string) ?,))))
     lst))
 
-(defun pkgusr-pkgs-count ()
-  "Return the number of installed packages."
-  (length (pkgusr-all-pkgs)))
+(defvar pkgusr-all-pkgs (pkgusr-all-pkgs)
+  "A list of all installed packages.")
+
+(defun pkgusr-all-pkgs-update ()
+  "Update the list of installed packages."
+  (setq pkgusr-all-pkgs (pkgusr-all-pkgs)))
+
+(defun pkgusr-pkgs-count (&optional upd-list)
+  "Return the number of installed packages.
+
+With optional argument UPD-LIST force an update of the packages list."
+  (and upd-list
+       (pkgusr-all-pkgs-update))
+  (length pkgusr-all-pkgs))
 
-(defun pkgusr-list-pkgs-regexp (regexp)
-  "Return a list of packages matching REGEXP."
-  (interactive "sRegexp: ")
-  (let ((pkgs (pkgusr-all-pkgs))
-       (case-fold-search t)
+(defun pkgusr-list-pkgs-regexp (regexp &optional upd-list)
+  "Return a list of packages matching REGEXP.
+
+With 1 prefix arg  insert the result into the current buffer at point.
+With 2 prefix args force update of the packages list.
+With 3 prefix args force update and insert into buffer.
+
+With optional argument UPD-LIST, force update of the packages list."
+  (interactive "sRegexp: \np")
+  (and (or (eq upd-list 16)
+          (eq upd-list 64))
+       (pkgusr-all-pkgs-update))
+  (let ((case-fold-search t)
        res)
     (mapcar
      #'(lambda (pkg)
         (when (string-match regexp pkg)
-          (push pkg res)))
-     pkgs)
+          (setq res (append res (list pkg)))))
+     pkgusr-all-pkgs)
     (if (interactive-p)
-       (message "%S" (nreverse res))
-      (nreverse res))))
+       (cond
+        ((or (eq upd-list 4)
+             (eq upd-list 64))
+         (insert (mapconcat #'identity res " ")))
+        (t
+         (message "[Matches for \"%s\"]: %s" regexp
+                  (mapconcat #'identity res " "))))
+      res)))
 
 (defconst pkgusr-url-regexp
   (concat
        (set-extent-property extent 'balloon-help echo)
        (set-extent-property extent 'duplicable t)))))
 
-(defun pkgusr-show-pkg (&optional pkg)
-  "Display a buffer of package details for PKG."
-  (interactive)
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (pkg (or pkg (completing-read "Show Package: "
-                                      (mapcar #'list allpkgs)
-                                      nil t nil pkgusr-pkg-history)))
-        (buf (get-buffer-create (format "*Package Details: %s*" pkg)))
-        (detail (shell-command-to-string (format "pinky -l %s" pkg))))
+(defun pkgusr-pkg-details (pkg)
+  (let ((buf (get-buffer-create (format "*Package Details: %s*" pkg)))
+       (detail (shell-command-to-string (format "pinky -l %s" pkg))))
     (with-current-buffer buf
       (erase-buffer)
       (insert detail)
                   (kill-buffer b)
                   (pop-window-configuration)))))
 
-(defun pkgusr-pkg-install-notes (&optional pkg)
-  "Display the install notes of a PKG."
+(defun pkgusr-show-pkg ()
+  "Display filelist and other details for a package.
+
+With a prefix arg, force update of the packages list."
+  (interactive)
+  (and current-prefix-arg
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (completing-read "Show Package: "
+                             (mapcar #'list pkgusr-all-pkgs)
+                             nil t nil pkgusr-pkg-history)))
+    (pkgusr-pkg-details pkg)))
+
+(defun pkgusr-pkg-install-notes ()
+  "Display the install notes of a package.
+
+With a prefix arg, force update of the packages list."
   (interactive)
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (pkg (or pkg (completing-read "Show Package: "
-                                      (mapcar #'list allpkgs)
-                                      nil t nil pkgusr-pkg-history)))
-        start end)
-    (pkgusr-show-pkg pkg)
+  (and current-prefix-arg
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (completing-read "Package: "
+                             (mapcar #'list pkgusr-all-pkgs)
+                             nil t nil pkgusr-pkg-history))
+       start end)
+    (pkgusr-pkg-details pkg)
     (re-search-forward "^Install Notes:$" nil t)
     (setq start (point-at-bol))
     (re-search-forward "^General Notes:$" nil t)
     (narrow-to-region start end)
     (goto-char (point-min))))
 
-(defun pkgusr-pkg-general-notes (&optional pkg)
-  "Display the general notes of a PKG."
+(defun pkgusr-pkg-general-notes ()
+  "Display the general notes of a package.
+
+With a prefix arg, force update of the packages list."
   (interactive)
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (pkg (or pkg (completing-read "Show Package: "
-                                      (mapcar #'list allpkgs)
-                                      nil t nil pkgusr-pkg-history)))
-        start end)
-    (pkgusr-show-pkg pkg)
+  (and current-prefix-arg
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (completing-read "Package: "
+                             (mapcar #'list pkgusr-all-pkgs)
+                             nil t nil pkgusr-pkg-history))
+       start end)
+    (pkgusr-pkg-details pkg)
     (re-search-forward "^General Notes:$" nil t)
     (setq start (point-at-bol))
     (re-search-forward "^CONTENTS:$" nil t)
     (narrow-to-region start end)
     (goto-char (point-min))))
 
+;;; FIXME: doesn't work for sub-groups.
 (defun pkgusr-cmd-pkg (cmd)
   "Display the package name \(user:group\) which contains CMD.
 
+With a prefix arg, insert into the current buffer at point.
+
 If non-interactive, return a list whose car is user and cdr is group."
   (interactive "sCommand: ")
-  (unless (executable-find cmd)
-    (error 'pkgusr-unknown-cmd cmd))
-  (let* ((cmd (executable-find cmd))
+  (let* ((cmd (or (executable-find cmd)
+                 (error 'pkgusr-unknown-cmd cmd)))
         (user (user-login-name
                (nth 2 (file-attributes cmd))))
         (group (user-login-name
                 (nth 3 (file-attributes cmd)))))
+    (unless (or (member user pkgusr-all-pkgs)
+               (member group pkgusr-all-pkgs))
+      (error 'pkgusr-unknown-pkg user))
     (if (interactive-p)
-       (message "Command: %s is from the \"%s\" package \(%s:%2$s\)"
-                cmd group user)
+       (if current-prefix-arg
+           (insert (format "%s: (%s:%s)" cmd user group))
+         (message "Command: %s is from the \"%s\" package (%2$s:%s)"
+                  cmd user group))
       (list user group))))
 
 (defun pkgusr-file-pkg (file)
   "Display the pkg name \(user:group\) which contains FILE.
 
+With a prefix arg, insert into current buffer at point.
+
 If non-interactive, return a list whose car is user and cdr is group."
   (interactive "fFile: ")
-  (let* ((user (user-login-name
-               (nth 2 (file-attributes file))))
-        (group (user-login-name
-                (nth 3 (file-attributes file)))))
-    (if (member user (pkgusr-all-pkgs))
-       (if (interactive-p)
-           (message "File: %s is from the \"%s\" package \(%s:%2$s\)"
-                    file group user)
-         (list user group))
-      (error 'pkgusr-unknown-file file))))
-
-(defun pkgusr-pkg-url (&optional pkg)
-  "Return the URL of PKG as a string."
+  (let ((user (user-login-name
+              (nth 2 (file-attributes file))))
+       (group (user-login-name
+               (nth 3 (file-attributes file)))))
+    (unless (or (member user pkgusr-all-pkgs)
+               (member group pkgusr-all-pkgs))
+      (error 'pkgusr-unknown-pkg user))
+    (if (interactive-p)
+       (if current-prefix-arg
+           (insert (format "%s: (%s:%s)" file user group))
+         (message "File: %s is from the \"%s\" package (%2$s:%s)"
+                  file user group)
+         (list user group)))))
+
+(defun pkgusr-project-file (pkg)
+  "Return the .project file for PKG."
+  (let ((dir (paths-construct-path (list "/usr" "src" pkg))))
+    (expand-file-name ".project" dir)))
+
+(defun pkgusr-pkg-url (&optional pkg upd-list)
+  "Return the URL of PKG as a string.
+
+With 1 prefix arg, insert into current buffer at point.
+With 2 prefix args, force update of the packages list.
+With 3 prefix args, force update and insert into buffer.
+With optional arg UPD-LIST, force update of the packages list."
   (interactive)
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (pkg (or pkg (completing-read "Package: "
-                                      (mapcar #'list allpkgs)
-                                      nil t nil pkgusr-pkg-history)))
-        (pkgfile (format "/usr/src/%s/.project" pkg)))
-    (when (member pkg allpkgs)
-      (with-temp-buffer
-       (erase-buffer)
-       (insert-file-contents pkgfile)
-       (goto-char (point-min))
-       (re-search-forward "Web_Site: <\\(.*\\)>$" nil t)
-       (if (interactive-p)
-           (message "[%s URL] %s" pkg (match-string 1))
-         (match-string 1))))))
-
-(defun pkgusr-pkg-repo (&optional pkg)
-  "Return the repo URI of PKG as a string."
+  (and (or (eq (car current-prefix-arg) 16)
+          (eq (car current-prefix-arg) 64)
+          upd-list)
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (or pkg
+                (completing-read "Package: "
+                                 (mapcar #'list pkgusr-all-pkgs)
+                                 nil t nil pkgusr-pkg-history)))
+       url)
+    (unless (member pkg pkgusr-all-pkgs)
+      (error 'pkgusr-unknown-pkg pkg))
+    (with-temp-buffer
+      (insert-file-contents (pkgusr-project-file pkg))
+      (goto-char (point-min))
+      (re-search-forward "Web_Site: <\\(.*\\)>$" nil t)
+      (setq url (match-string 1)))
+    (if (interactive-p)
+       (cond
+        ((or (eq (car current-prefix-arg) 4)
+             (eq (car current-prefix-arg) 64))
+         (insert url))
+        (t
+         (message "[URL (%s)]: %s" pkg url)))
+      url)))
+
+(defun pkgusr-pkg-repo (&optional pkg upd-list)
+  "Return the repo URI of PKG as a string.
+
+With 1 prefix arg, insert into current buffer at point.
+With 2 prefix args, force update of the packages list.
+With 3 prefix args, force update and insert into buffer.
+With optional arg UPD-LIST, force update of the packages list."
   (interactive)
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (pkg (or pkg (completing-read "Package: "
-                                      (mapcar #'list allpkgs)
-                                      nil t nil pkgusr-pkg-history)))
-        (pkgfile (format "/usr/src/%s/.project" pkg))
-        repo type)
-    (when (member pkg allpkgs)
-      (with-temp-buffer
-       (erase-buffer)
-       (insert-file-contents pkgfile)
-       (goto-char (point-min))
-       (re-search-forward "Repo_Type: <?\\(.*\\)>?$" nil t)
-       (setq type (match-string 1))
-       (re-search-forward "Repo_Location: <\\(.*\\)>" nil t)
-       (setq repo (match-string 1)))
-      (if (interactive-p)
-         (message "[%s Repo] %s (%s)" pkg repo type)
-       repo))))
-
-(defun pkgusr-pkg-version (&optional pkg)
-  "Return the version of PKG as a string."
+  (and (or (eq (car current-prefix-arg) 16)
+          (eq (car current-prefix-arg) 64)
+          upd-list)
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (or pkg (completing-read "Package: "
+                                     (mapcar #'list pkgusr-all-pkgs)
+                                     nil t nil pkgusr-pkg-history)))
+       repo type)
+    (unless (member pkg pkgusr-all-pkgs)
+      (error 'pkgusr-unknown-pkg pkg))
+    (with-temp-buffer
+      (insert-file-contents (pkgusr-project-file pkg))
+      (goto-char (point-min))
+      (re-search-forward "Repo_Type: <\\(.*\\)>$" nil t)
+      (setq type (match-string 1))
+      (re-search-forward "Repo_Location: <\\(.*\\)>" nil t)
+      (setq repo (match-string 1)))
+    (if (interactive-p)
+       (cond
+        ((or (eq (car current-prefix-arg) 4)
+             (eq (car current-prefix-arg) 64))
+         (insert repo))
+        (t
+         (message "[Repo (%s)]: %s (%s)" pkg repo type)))
+      repo)))
+
+(defun pkgusr-pkg-version (&optional pkg upd-list)
+  "Return the version of PKG as a string.
+
+With 1 prefix arg, insert into current buffer at point.
+With 2 prefix args, force update of the packages list.
+With 3 prefix args, force update and insert into buffer.
+With optional arg UPD-LIST, force update of the packages list."
   (interactive)
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (pkg (or pkg (completing-read "Package: "
-                                      (mapcar #'list allpkgs)
-                                      nil t nil pkgusr-pkg-history)))
-        (pkgfile (format "/usr/src/%s/.project" pkg)))
-    (if (member pkg allpkgs)
-       (with-temp-buffer
-         (erase-buffer)
-         (insert-file-contents pkgfile)
-         (goto-char (point-min))
-         (re-search-forward "Version: \\(.*$\\)" nil t)
-         (if (interactive-p)
-             (message "[%s Ver] %s" pkg (match-string 1))
-           (match-string 1)))
-      (error 'pkgusr-unknown-pkg pkg))))
-
-(defun pkgusr-pkg-description (&optional pkg)
-  "Return the description of PKG as a string."
+  (and (or (eq (car current-prefix-arg) 16)
+          (eq (car current-prefix-arg) 64)
+          upd-list)
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (or pkg (completing-read "Package: "
+                                     (mapcar #'list pkgusr-all-pkgs)
+                                     nil t nil pkgusr-pkg-history)))
+       version)
+    (unless (member pkg pkgusr-all-pkgs)
+      (error 'pkgusr-unknown-pkg pkg))
+    (with-temp-buffer
+      (insert-file-contents (pkgusr-project-file pkg))
+      (goto-char (point-min))
+      (re-search-forward "Version: \\(.*$\\)" nil t)
+      (setq version (match-string 1)))
+    (if (interactive-p)
+       (cond
+        ((or (eq (car current-prefix-arg) 4)
+             (eq (car current-prefix-arg) 64))
+         (insert version))
+        (t
+         (message "[Version (%s)]: %s" pkg version)))
+      version)))
+
+(defun pkgusr-pkg-description (&optional pkg upd-list)
+  "Return the description of PKG as a string.
+
+With 1 prefix arg, insert into current buffer at point.
+With 2 prefix args, force update of the packages list.
+With 3 prefix args, force update and insert into buffer.
+With optional arg UPD-LIST, force update of the packages list."
   (interactive)
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (pkg (or pkg (completing-read "Package: "
-                                      (mapcar #'list allpkgs)
-                                      nil t nil pkgusr-pkg-history)))
-        (pkgfile (format "/usr/src/%s/.project" pkg)))
-    (if (member pkg allpkgs)
-       (with-temp-buffer
-         (erase-buffer)
-         (insert-file-contents pkgfile)
-         (goto-char (point-min))
-         (re-search-forward "Description: \\(.*$\\)" nil t)
-         (if (interactive-p)
-             (message "[%s Desc] %s" pkg (match-string 1))
-           (match-string 1)))
-      (error 'pkgusr-unknown-pkg pkg))))
-
-(defun pkgusr-pkg-deps (&optional pkg)
-  "Return the dependencies of PKG as a string."
+  (and (or (eq (car current-prefix-arg) 16)
+          (eq (car current-prefix-arg) 64)
+          upd-list)
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (or pkg (completing-read "Package: "
+                                     (mapcar #'list pkgusr-all-pkgs)
+                                     nil t nil pkgusr-pkg-history)))
+       desc)
+    (unless (member pkg pkgusr-all-pkgs)
+      (error 'pkgusr-unknown-pkg pkg))
+    (with-temp-buffer
+      (insert-file-contents (pkgusr-project-file pkg))
+      (goto-char (point-min))
+      (re-search-forward "Description: \\(.*$\\)" nil t)
+      (setq desc (match-string 1)))
+    (if (interactive-p)
+       (cond
+        ((or (eq (car current-prefix-arg) 4)
+             (eq (car current-prefix-arg) 64))
+         (insert desc))
+        (t
+         (message "[Description (%s)]: %s" pkg desc)))
+      desc)))
+
+(defun pkgusr-pkg-deps (&optional pkg upd-list)
+  "Return a list of dependencies of PKG.
+
+With 1 prefix arg, insert into current buffer at point.
+With 2 prefix args, force update of the packages list.
+With 3 prefix args, force update and insert into buffer.
+With optional arg UPD-LIST, force update of the packages list."
   (interactive)
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (pkg (or pkg (completing-read "Package: "
-                                      (mapcar #'list allpkgs)
-                                      nil t nil pkgusr-pkg-history)))
-        (pkgfile (format "/usr/src/%s/.project" pkg)))
-    (if (member pkg allpkgs)
-       (with-temp-buffer
-         (erase-buffer)
-         (insert-file-contents pkgfile)
-         (goto-char (point-min))
-         (re-search-forward "Deps: \\(.*$\\)" nil t)
-         (if (interactive-p)
-             (message "[%s Deps] %s" pkg (match-string 1))
-           (match-string 1)))
-      (error 'pkgusr-unknown-pkg pkg))))
+  (and (or (eq (car current-prefix-arg) 16)
+          (eq (car current-prefix-arg) 64)
+          upd-list)
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (or pkg (completing-read "Package: "
+                                     (mapcar #'list pkgusr-all-pkgs)
+                                     nil t nil pkgusr-pkg-history)))
+       deps)
+    (unless (member pkg pkgusr-all-pkgs)
+      (error 'pkgusr-unknown-pkg pkg))
+    (with-temp-buffer
+      (insert-file-contents (pkgusr-project-file pkg))
+      (goto-char (point-min))
+      (re-search-forward "Deps: \\(.*$\\)" nil t)
+      (setq deps (match-string 1)))
+    (if (interactive-p)
+       (cond
+        ((or (eq (car current-prefix-arg) 4)
+             (eq (car current-prefix-arg) 64))
+         (insert deps))
+        (t
+         (message "[Deps (%s)]: %s" pkg deps)))
+      (split-string-by-char deps ?\ ))))
+
+;;; FIXME: This churns like you wouldn't believe, can we make it more
+;;; efficient?
+(defun pkgusr-pkg-rdeps (&optional pkg upd-list)
+  "Return a list of packages which cite PKG as a dependency.
+
+With 1 prefix arg, insert into current buffer at point.
+With 2 prefix args, force update of the packages list.
+With 3 prefix args, force update and insert into buffer.
+With optional arg UPD-LIST, force update of the packages list."
+  (interactive)
+  (and (or (eq (car current-prefix-arg) 16)
+          (eq (car current-prefix-arg) 64)
+          upd-list)
+       (pkgusr-all-pkgs-update))
+  (let ((pkg (or pkg (completing-read "Package: "
+                                     (mapcar #'list pkgusr-all-pkgs)
+                                     nil t nil pkgusr-pkg-history)))
+       result)
+    (unless (member pkg pkgusr-all-pkgs)
+      (error 'pkgusr-unknown-pkg pkg))
+    (mapcar
+     #'(lambda (maybe-pkg)
+        (let ((deplist (pkgusr-pkg-deps maybe-pkg)))
+          (mapcar
+           #'(lambda (dep)
+               (and (string-match pkg dep)
+                    (setq result (append result (list maybe-pkg)))))
+           deplist)))
+     pkgusr-all-pkgs)
+    (sort result #'string<)
+    (if (interactive-p)
+       (cond
+        ((or (eq (car current-prefix-arg) 4)
+             (eq (car current-prefix-arg) 64))
+         (insert (mapconcat #'identity result " ")))
+        (t
+         (message "[Reverse Deps (%s)]: %s" pkg
+                  (mapconcat #'identity result " "))))
+      result)))
 
 ;; A little bogus perhaps, but it works.  `pkgusr-find-file' is
 ;; something that only I can use because it ssh's through root to
@@ -336,14 +493,17 @@ after the setq.  Be bold and hard code it in pkgusr.el itself.")
 (defvar-when-pkgmgr pkgusr-file-history nil
   "History for pkgusr-find-file.")
 
-(defun-when-pkgmgr pkgusr-find-file (pkgusr file)
-  "Using Tramp, find PKGUSR's FILE."
-  (interactive "i\ni")
+(defun-when-pkgmgr pkgusr-find-file (&optional pkgusr file)
+  "Using Tramp, find PKGUSR's FILE.
+
+With a prefix arg, force update of the packages list."
+  (interactive)
   (unless (interactive-p)
     (error 'invalid-operation "Trying to call interactive-only command"))
-  (let* ((allpkgs (pkgusr-all-pkgs))
-        (puser (completing-read "Package User: "
-                                (mapcar #'list allpkgs)
+  (and current-prefix-arg
+       (pkgusr-all-pkgs-update))
+  (let* ((puser (completing-read "Package User: "
+                                (mapcar #'list pkgusr-all-pkgs)
                                 nil t nil pkgusr-pkg-history))
         (file (read-file-name (format "[%s] find file: " puser)
                               (file-name-as-directory
@@ -365,6 +525,7 @@ after the setq.  Be bold and hard code it in pkgusr.el itself.")
 (global-set-key [(hyper c) g] #'pkgusr-pkg-general-notes)
 (global-set-key [(hyper c) i] #'pkgusr-pkg-install-notes)
 (global-set-key [(hyper c) r] #'pkgusr-pkg-repo)
+(global-set-key [(hyper c) R] #'pkgusr-pkg-rdeps)
 (global-set-key [(hyper c) u] #'pkgusr-pkg-url)
 (global-set-key [(hyper c) v] #'pkgusr-pkg-version)
 (global-set-key [(hyper c) s] #'pkgusr-show-pkg)