slight restructuring
authorRalph Ronnquist <ralph.ronnquist@gmail.com>
Sat, 8 Jan 2022 23:52:25 +0000 (10:52 +1100)
committerRalph Ronnquist <ralph.ronnquist@gmail.com>
Sat, 8 Jan 2022 23:52:25 +0000 (10:52 +1100)
autorecommended.lsp

index 1028a5e186bc1c42abb3bafaedbc406fe6452868..755123620fa789d5c7988174cecc40ab7d027a62 100755 (executable)
@@ -1,85 +1,97 @@
 #!/usr/bin/newlisp
 
-; Strip away arch tail if any
-(define (noarch X) (if (find ":" X) (0 $it X) X))
+; Strip away the arch tail if any of a package
+(define (noarch P) (if (find ":" P) (0 $it P) P))
 
-; Strip away all version specs from a dependency line
-(define (noversions X)
-  (replace " \\([^)]*\\)" (copy X) "" 0))
-
-; Return list of "manually installed"
-(define (manual) (map noarch (exec "apt-mark showmanual")))
-
-; Return list of "all installed"
-(define (installed) (map noarch (exec "apt-mark showinstall")))
+; Determine the list of "all installed"
+(setf INSTALLED (map noarch (exec "apt-mark showinstall")))
+(write-line 2 (format "There are %d installed packages" (length INSTALLED)))
 
-; Return the provided package, if any, of a given package, or nil
-(define (provides P)
-  (if (exec (format "dpkg-query -W -f '${Provides}' %s" P)) ($it 0) nil))
-
-; Return the "raw dependency line" for a package
-(define (raw-depends P)
-  (exec (format "dpkg-query -W -f '${Pre-Depends} ${Depends}' %s" P)))
+; Determine the list of "manually installed"
+(setf MANUAL (map noarch (exec "apt-mark showmanual")))
+(write-line 2 (format "There are %d manual packages" (length MANUAL)))
 
 ; Set up hash table of provided packages by currently installed. This
 ; is needed for following up dependencies, which for an option
 ; dependency only automatically resolves the first of options.
+(write 2 "Initializing 'provided-by' ")
+(setf PROVFMT "dpkg-query -W -f '${Provides}' %s")
 (define PROV:PROV nil)
-(write 2 "Initializing ")
-(dolist (P (installed))
-  (let ((PV (provides P)))
-    (if (= (% (inc COUNT) 100)) (write 2 "."))
-    (if (PROV P) (push P (PROV P)) (PROV P (list P)))
-    (if (PROV PV) (push P (PROV PV)) (PROV PV (list P)))))
-(dolist (P (PROV))
-  (PROV (P 0) (sort (unique (P 1)))))
+(dolist (P INSTALLED)
+  (if (= (% (inc COUNT) 100)) (write 2 "."))
+  (if (PROV P) (push P (PROV P)) (PROV P (list P)))
+  (let (PV (if (exec (format PROVFMT P)) ($it 0)))
+    (when PV (if (PROV PV) (push P (PROV PV)) (PROV PV (list P))))))
+(dolist (P (PROV)) (PROV (P 0) (sort (unique (P 1)))))
 (write-line 2 " done")
 
-; Look up the list of packages that providing the P package
-(define (provided P) (PROV P))
+; set up a hashtable of recommenders
+(write 2 "Initializing 'recommended-by' ")
+(setf COUNT 0)
+(setf RECFMT "dpkg-query -W -f '${Recommends}' %s")
+(define REC:REC nil)
+(dolist (P INSTALLED)
+  (if (= (% (inc COUNT) 100)) (write 2 "."))
+  (dolist (R (exec (format RECFMT P)))
+    (if (REC R) (push P (REC R)) (REC R (list P)))))
+(dolist (P (REC)) (REC (P 0) (sort (unique (P 1)))))
+(write-line 2 " done")
 
-; Return the "depends choice" of autmatically dependent package
-; considering the options of providing that package as currently
-; installed. Only the first of providers is an automatic dependency.
-; Thus, return a) the depedent package itself if it's the first of its
-; providers, or b) that first of providers if the dependent package is
-; not among the providers (a fully virtual package), or c) nil if the
-; depedent package is a provider but not the first.
+; ========================================
+; Set up hashtable for dependencies without those not installed or
+; shadowed by non-default choice
+
+; Returns the "depends choice" of an automatically dependent package
+; as supported by currently installed packages. Only the first of
+; providers is an automatic dependency. Thus, it returns either a) the
+; depedent package itself if it's the first of its providers, or b)
+; that first of providers if the dependent package is not among the
+; providers (a fully virtual package), or c) nil if the depedent
+; package is a provider but not the first.
 (define (depends-choice P)
   (let ((Y (if (PROV P) (first $it) nil)))
     (if (null? Y) nil (member P (PROV P)) (and (= P Y) P) Y)))
 
-; Resolve a depedency item with respect to package provisions, where
-; an option depedency only automatically pulls in its first option.
-; May return null
+; Strip away all version specs from a dependency line
+(define (noversions X) (replace " \\([^)]*\\)" (copy X) "" 0))
+
+; Resolves a depedency list with respect to installed package
+; provisions, where in particular an option depedency only
+; automatically pulls in its first option. This may return nil, which
+; is for a choice dependency with installed non-default provisioning.
 (define (depends-choices X)
   (depends-choice (trim (first (parse (noversions X) "|")))))
 
-; Fix up a dependency line into list of packages, after cleanup
+; Returns the fixed up a dependency line as list of packages.
 (define (depends-fix X)
   (clean null? (map depends-choices (clean empty? (map trim (parse X ","))))))
 
-; Return dependencies of a package, with caching.
+(write 2 "Initializing actual 'depends' ")
+(setf COUNT 0)
+(setf DEPFMT "dpkg-query -W -f '${Pre-Depends} ${Depends}' %s")
 (define DEP:DEP nil)
-(define (depends X)
-  (if (DEP X) $it (DEP X (flat (map depends-fix (raw-depends X))))))
+(dolist (P INSTALLED)
+  (if (= (% (inc COUNT) 100)) (write 2 "."))
+  (DEP P (flat (map depends-fix (exec (format DEPFMT P))))))
+(write-line 2 " done")
+
+; Return the dependencies for installed package of empty list otherwise
+(define (depends P) (or (DEP P) '()))
 
-; Expand a list of packages to include the closure of dependencies
+; Expands a list of packages to include the closure of its dependencies
 (define (closure L)
-  (let ((X (sort L)))
-    (write-line 2 (format "checking %d manual packages" (length X)))
-    (while (!= X (setf L (sort (union X (apply union (map depends X))))))
-      (setf X L)
-      (write-line 2 (format " %d manual+dependent packages so far" (length X)))
+  (let ((P (sort L)))
+    (write-line 2 (format "checking %d manual packages" (length P)))
+    (while (!= P (setf L (sort (union P (apply union (map depends P))))))
+      (setf P L)
+      (write-line 2 (format " %d with dependencies" (length P)))
       )
-    X))
+    P))
 
 ## main: report the set difference between "all installed" and the
 ## dependency closure of "manually installed"
 
-(let ((X (installed)) (Y nil))
-  (write-line 2 (format "There are %d installed packages" (length X)))
-  (setf Y (difference (installed) (closure (manual))))
+(let ((Y (difference INSTALLED (closure MANUAL))))
   (write-line 2 (format "=> %d automatic recommended packages" (length Y)))
   (map println Y))