From a4bdb434af8dcdcf5ef4617ff10203c71041cfc4 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 14:07:08 +0200 Subject: [PATCH 01/12] kiss: initial alternatives system --- kiss | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/kiss b/kiss index d3fee4a..c7eb6df 100755 --- a/kiss +++ b/kiss @@ -745,9 +745,8 @@ pkg_conflicts() { tar xf "$1" -O "./$pkg_db/$2/manifest" | while read -r file; do case $file in */) continue; esac - printf '%s\n' "$file" - - readlink -f "$KISS_ROOT/$file" ||: + printf '%s/%s\n' \ + "$(readlink -f "$KISS_ROOT/${file%/*}")" "${file##*/}" done > "$cac_dir/$pid-m" p_name=$2 @@ -772,9 +771,52 @@ pkg_conflicts() { # Use 'grep' to list matching lines between the to # be installed package's manifest and the above filtered # list. - [ -s "$cac_dir/$pid-m" ] && - "$grep" -Fxf "$cac_dir/$pid-m" -- "$@" && - die "Package '$p_name' conflicts with another package" + [ -s "$cac_dir/$pid-m" ] && { + "$grep" -Fxf "$cac_dir/$pid-m" -- "$@" | + + # This is a novel way of offering an "alternatives" system. + # It is entirely dynamic and all "choices" are created and + # destroyed on the fly. + # + # When a conflict is found between two packages, the file + # is moved to a directory called "choices" and its name + # changed to store its parent package and its intended + # location. + # + # The package's manifest is then updated to reflect this + # new location. + # + # The 'kiss choices' command parses this directory and + # offers you the CHOICE of *swapping* entries in this + # directory for those on the filesystem. + # + # The choices command does the same thing we do here, + # it rewrites manifests and moves files around to make + # this work. + # + # Pretty nifty huh? + while IFS=: read -r pro bin || [ "$pro" ]; do + log "$p_name" "Found conflict ($bin), adding choice" + + # Create the "choices" directory inside of the tarball. + # This directory will store the conflicting file. + mkdir -p "$tar_dir/$p_name/${cho_dir:=var/db/kiss/choices}" + + # Construct the file name of the "db" entry of the + # conflicting file. (pkg_name>usr>bin>ls) + bin_name=$(echo "$bin" | sed 's|/|>|g') + + # Move the conflicting file to the choices directory + # and name it according to the format above. + mv -f "$tar_dir/$p_name/$bin" \ + "$tar_dir/$p_name/$cho_dir/$p_name$bin_name" + + # Rewrite the package's manifest to update its location + # to its new spot (and name) in the choices directory. + sed -i "s|$bin|/$cho_dir/$p_name$bin_name|" \ + "$tar_dir/$p_name/$pkg_db/$p_name/manifest" + done + } set -e } @@ -860,8 +902,6 @@ pkg_install() { pkg_name=${pkg_name%/*} pkg_name=${pkg_name##*/} - pkg_conflicts "$tar_file" "$pkg_name" - mkdir -p "$tar_dir/$pkg_name" # Extract the tar-ball to catch any errors before installation begins. @@ -882,6 +922,8 @@ pkg_install() { [ "$install_dep" ] && die "$1" "Package requires ${install_dep%, }" + pkg_conflicts "$tar_file" "$pkg_name" + log "$pkg_name" "Installing package incrementally" # Block being able to abort the script with Ctrl+C during installation. From 99532a705b9309b27d83a8d442ffaae1ccbed642 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 14:13:26 +0200 Subject: [PATCH 02/12] kiss: Fix shellcheck false-positive --- kiss | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kiss b/kiss index c7eb6df..6d677ba 100755 --- a/kiss +++ b/kiss @@ -795,8 +795,8 @@ pkg_conflicts() { # this work. # # Pretty nifty huh? - while IFS=: read -r pro bin || [ "$pro" ]; do - log "$p_name" "Found conflict ($bin), adding choice" + while IFS=: read -r pro con || [ "$pro" ]; do + log "$p_name" "Found conflict ($con), adding choice" # Create the "choices" directory inside of the tarball. # This directory will store the conflicting file. @@ -804,16 +804,16 @@ pkg_conflicts() { # Construct the file name of the "db" entry of the # conflicting file. (pkg_name>usr>bin>ls) - bin_name=$(echo "$bin" | sed 's|/|>|g') + con_name=$(echo "$con" | sed 's|/|>|g') # Move the conflicting file to the choices directory # and name it according to the format above. - mv -f "$tar_dir/$p_name/$bin" \ - "$tar_dir/$p_name/$cho_dir/$p_name$bin_name" + mv -f "$tar_dir/$p_name/$con" \ + "$tar_dir/$p_name/$cho_dir/$p_name$con_name" # Rewrite the package's manifest to update its location # to its new spot (and name) in the choices directory. - sed -i "s|$bin|/$cho_dir/$p_name$bin_name|" \ + sed -i "s|$con|/$cho_dir/$p_name$con_name|" \ "$tar_dir/$p_name/$pkg_db/$p_name/manifest" done } From 4e99ad6135c851a04b1604cd25435cc40470ef86 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 14:16:01 +0200 Subject: [PATCH 03/12] kiss: initial a arg --- kiss | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/kiss b/kiss index 6d677ba..d17e32a 100755 --- a/kiss +++ b/kiss @@ -1168,6 +1168,10 @@ args() { # Actions can be abbreviated to their first letter. This saves # keystrokes once you memorize the commands. case $action in + a|alternatives) + + ;; + b|build) # If no arguments were passed, rebuild all packages. [ "$1" ] || { @@ -1248,15 +1252,16 @@ args() { ;; h|help|-h|--help|'') - log 'kiss [b|c|i|l|r|s|u|v] [pkg] [pkg] [pkg]' - log 'build: Build a package' - log 'checksum: Generate checksums' - log 'install: Install a package' - log 'list: List installed packages' - log 'remove: Remove a package' - log 'search: Search for a package' - log 'update: Check for updates' - log 'version: Package manager version' + log 'kiss [a|b|c|i|l|r|s|u|v] [pkg] [pkg] [pkg]' + log 'alternatives: List and swap to alternatives' + log 'build: Build a package' + log 'checksum: Generate checksums' + log 'install: Install a package' + log 'list: List installed packages' + log 'remove: Remove a package' + log 'search: Search for a package' + log 'update: Check for updates' + log 'version: Package manager version' ;; *) die "'kiss $action' is not a valid command" ;; From 4fcb12ce9f687c1b2ff259c99882a756ec70c6bd Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 14:24:37 +0200 Subject: [PATCH 04/12] kiss: initial a arg --- kiss | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/kiss b/kiss index d17e32a..676fd43 100755 --- a/kiss +++ b/kiss @@ -1169,7 +1169,24 @@ args() { # keystrokes once you memorize the commands. case $action in a|alternatives) + if [ "$1" ]; then + : + else + log "Alternatives:" + # Go to the choices directory and hide errors + # as there is nothing to list if the directory + # doesn't exist. + cd "$sys_db/../choices" 2>/dev/null + set +f + + # Go over each alternative and format the file + # name for listing. (pkg_name>usr>bin>ls) + for pkg in *; do + [ -f "$pkg" ] && + printf '%s\n' "$pkg" | sed 's|>|: /|;s|>|/|g' + done + fi ;; b|build) From d25c4bd5e1769df2dc96ad0d210eac90de98a13e Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 14:29:15 +0200 Subject: [PATCH 05/12] kiss: initial a arg --- kiss | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kiss b/kiss index 676fd43..87fc41e 100755 --- a/kiss +++ b/kiss @@ -1170,7 +1170,11 @@ args() { case $action in a|alternatives) if [ "$1" ]; then - : + # Check to see if the package is installed. This + # will exit with an error if it is not. + pkg_list "$1" >/dev/null + + [ "$2" ] || die "No alternative chosen" else log "Alternatives:" From 596c64d4478122735f6693f6863833c018f13893 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 15:07:11 +0200 Subject: [PATCH 06/12] kiss: initial a arg --- kiss | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/kiss b/kiss index 87fc41e..a6a013d 100755 --- a/kiss +++ b/kiss @@ -821,6 +821,17 @@ pkg_conflicts() { set -e } +pkg_swap() { + # Check to see if the package is installed. This + # will exit with an error if it is not. + pkg_list "$1" >/dev/null + + alt=$(printf %s "$1$2" | sed 's|/|>|g') + + [ -f "$sys_db/../choices/$alt" ] || + die "Alternative '$1 $2' doesn't exist" +} + pkg_remove() { # Remove a package and all of its files. The '/etc' directory # is handled differently and configuration files are *not* @@ -1170,11 +1181,8 @@ args() { case $action in a|alternatives) if [ "$1" ]; then - # Check to see if the package is installed. This - # will exit with an error if it is not. - pkg_list "$1" >/dev/null + pkg_swap "$@" - [ "$2" ] || die "No alternative chosen" else log "Alternatives:" @@ -1188,7 +1196,7 @@ args() { # name for listing. (pkg_name>usr>bin>ls) for pkg in *; do [ -f "$pkg" ] && - printf '%s\n' "$pkg" | sed 's|>|: /|;s|>|/|g' + printf '%s\n' "$pkg" | sed 's|>| /|;s|>|/|g' done fi ;; From f63e1acb642ecc170180d6a26b68720631777f72 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 15:41:11 +0200 Subject: [PATCH 07/12] kiss: alternatives: initial working solution --- kiss | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/kiss b/kiss index a6a013d..a1ba40f 100755 --- a/kiss +++ b/kiss @@ -822,14 +822,47 @@ pkg_conflicts() { } pkg_swap() { + # Swap between package alternatives. + # Check to see if the package is installed. This # will exit with an error if it is not. pkg_list "$1" >/dev/null alt=$(printf %s "$1$2" | sed 's|/|>|g') + cd "$sys_db/../choices" - [ -f "$sys_db/../choices/$alt" ] || + [ -f "$alt" ] || [ -h "$alt" ] || die "Alternative '$1 $2' doesn't exist" + + if [ -f "$2" ]; then + # Figure out which package owns the file we are going to + # swap for another package's. + # + # Print the full path to the manifest file which contains + # the match to our search. + pkg_owns=$(set +f; "$grep" -lFx "$2" "$sys_db/"*/manifest) ||: + + # Extract the package name from the path above. + pkg_owns=${pkg_owns%/*} + pkg_owns=${pkg_owns##*/} + + [ "$pkg_owns" ] || + die "File '$2' exists on filesystem but isn't owned" + + log "Swapping '$2' from '$pkg_owns' to '$1'" + + # Convert the current owner to an alternative and rewrite + # its manifest file to reflect this. + dosu mv -f "'$2'" "'$pkg_owns>${alt#*>}'" + dosu sed -i "'s|$2|$PWD/$pkg_owns>${alt#*>}|'" \ + "'../installed/$pkg_owns/manifest'" + fi + + # Convert the desired alternative to a real file and rewrite + # the manifest file to reflect this. The reverse of above. + dosu mv -f "'$alt'" "'$2'" + dosu sed -i "'s|$PWD/$alt|$2|'" \ + "'../installed/$1/manifest'" } pkg_remove() { @@ -1195,7 +1228,7 @@ args() { # Go over each alternative and format the file # name for listing. (pkg_name>usr>bin>ls) for pkg in *; do - [ -f "$pkg" ] && + [ "$pkg" = '*' ] || printf '%s\n' "$pkg" | sed 's|>| /|;s|>|/|g' done fi From b8ea154406ec6c620c6fece7b61dca0c9d45144c Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 16:41:19 +0200 Subject: [PATCH 08/12] kiss: awful --- kiss | 51 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/kiss b/kiss index a1ba40f..d8d1685 100755 --- a/kiss +++ b/kiss @@ -752,7 +752,7 @@ pkg_conflicts() { p_name=$2 # Generate a list of all installed package manifests. - set +ef + set +f set -f -- "$sys_db"/*/manifest # Filter the manifest list and remove the previously @@ -761,9 +761,9 @@ pkg_conflicts() { i_name=${pkg%/*} i_name=${i_name##*/} - shift + shift "$(($# ? 1 : 0))" - [ "$p_name" = "$i_name" ] && continue + [ "$p_name" != "$i_name" ] || continue set -- "$@" "$pkg" done @@ -806,6 +806,11 @@ pkg_conflicts() { # conflicting file. (pkg_name>usr>bin>ls) con_name=$(echo "$con" | sed 's|/|>|g') + # Escape the required patch characters in the file name + # as it will be passed along to 'sed' below. + sea=$(echo "$con" | sed 's/[]\/$*.^[]/\\&/g') + rep=$(echo "/$cho_dir/$p_name$con_name" | sed 's/[\/&]/\\&/g') + # Move the conflicting file to the choices directory # and name it according to the format above. mv -f "$tar_dir/$p_name/$con" \ @@ -813,12 +818,10 @@ pkg_conflicts() { # Rewrite the package's manifest to update its location # to its new spot (and name) in the choices directory. - sed -i "s|$con|/$cho_dir/$p_name$con_name|" \ - "$tar_dir/$p_name/$pkg_db/$p_name/manifest" + sed -i "s/$sea/$rep/" \ + "$tar_dir/$p_name/$pkg_db/$p_name/manifest" done } - - set -e } pkg_swap() { @@ -851,18 +854,26 @@ pkg_swap() { log "Swapping '$2' from '$pkg_owns' to '$1'" + # Escape the required patch characters in the file name + # as it will be passed along to 'sed' below. + sea=$(echo "$2" | sed 's/[]\/$*.^[]/\\&/g') + rep=$(echo "$PWD/$pkg_owns>${alt#*>}" | sed 's/[\/&]/\\&/g') + # Convert the current owner to an alternative and rewrite # its manifest file to reflect this. - dosu mv -f "'$2'" "'$pkg_owns>${alt#*>}'" - dosu sed -i "'s|$2|$PWD/$pkg_owns>${alt#*>}|'" \ - "'../installed/$pkg_owns/manifest'" + dosu mv -f "'$2'" "'$pkg_owns>${alt#*>}'" + dosu sed -i "'s/$sea/$rep/'" "'../installed/$pkg_owns/manifest'" fi + # Escape the required patch characters in the file name + # as it will be passed along to 'sed' below. + sea=$(echo "$PWD/$alt" | sed 's/[]\/$*.^[]/\\&/g') + rep=$(echo "$2" | sed 's/[\/&]/\\&/g') + # Convert the desired alternative to a real file and rewrite # the manifest file to reflect this. The reverse of above. dosu mv -f "'$alt'" "'$2'" - dosu sed -i "'s|$PWD/$alt|$2|'" \ - "'../installed/$1/manifest'" + dosu sed -i "'s/$sea/$rep/'" "'../installed/$1/manifest'" } pkg_remove() { @@ -1195,12 +1206,16 @@ args() { # # This handles the globbing characters '*', '!', '[' and ']' as per: # https://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html - [ "$action" != search ] && [ "$action" != s ] && - case $@ in - *'*'*|*'!'*|*'['*|*']'*) - die "Arguments contain invalid characters: '!*[]'" - ;; - esac + case $action in + a|alternatives|s|search) ;; + + *) + case $@ in + *'*'*|*'!'*|*'['*|*']'*) + die "Arguments contain invalid characters: '!*[]'" + ;; + esac + esac # Parse some arguments earlier to remove the need to duplicate code. case $action in From e97c78ae260ed1487ded7e0698bbba314b636e05 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 16:46:29 +0200 Subject: [PATCH 09/12] kiss: Move regex escape to func --- kiss | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/kiss b/kiss index d8d1685..4377345 100755 --- a/kiss +++ b/kiss @@ -148,6 +148,14 @@ dosu() { fi } +regex_escape() { + # Escape all required characters in both the search and + # replace portions of two strings for use in a 'sed' call + # as "plain-text". + sea=$(echo "$1" | sed 's/[]\/$*.^[]/\\&/g') + rep=$(echo "$2" | sed 's/[\/&]/\\&/g') +} + pkg_lint() { # Check that each mandatory file in the package entry exists. log "$1" "Checking repository files" @@ -806,10 +814,7 @@ pkg_conflicts() { # conflicting file. (pkg_name>usr>bin>ls) con_name=$(echo "$con" | sed 's|/|>|g') - # Escape the required patch characters in the file name - # as it will be passed along to 'sed' below. - sea=$(echo "$con" | sed 's/[]\/$*.^[]/\\&/g') - rep=$(echo "/$cho_dir/$p_name$con_name" | sed 's/[\/&]/\\&/g') + regex_escape "$con" "/$cho_dir/$p_name$con_name" # Move the conflicting file to the choices directory # and name it according to the format above. @@ -854,10 +859,7 @@ pkg_swap() { log "Swapping '$2' from '$pkg_owns' to '$1'" - # Escape the required patch characters in the file name - # as it will be passed along to 'sed' below. - sea=$(echo "$2" | sed 's/[]\/$*.^[]/\\&/g') - rep=$(echo "$PWD/$pkg_owns>${alt#*>}" | sed 's/[\/&]/\\&/g') + regex_escape "$2" "$PWD/$pkg_owns>${alt#*>}" # Convert the current owner to an alternative and rewrite # its manifest file to reflect this. @@ -865,10 +867,7 @@ pkg_swap() { dosu sed -i "'s/$sea/$rep/'" "'../installed/$pkg_owns/manifest'" fi - # Escape the required patch characters in the file name - # as it will be passed along to 'sed' below. - sea=$(echo "$PWD/$alt" | sed 's/[]\/$*.^[]/\\&/g') - rep=$(echo "$2" | sed 's/[\/&]/\\&/g') + regex_escape "$PWD/$alt" "$2" # Convert the desired alternative to a real file and rewrite # the manifest file to reflect this. The reverse of above. From 2eaafc78b0a75a696551f9ae9ccab0cd3fcfa3ba Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 16:50:11 +0200 Subject: [PATCH 10/12] kiss: clean --- kiss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kiss b/kiss index 4377345..214a037 100755 --- a/kiss +++ b/kiss @@ -814,13 +814,13 @@ pkg_conflicts() { # conflicting file. (pkg_name>usr>bin>ls) con_name=$(echo "$con" | sed 's|/|>|g') - regex_escape "$con" "/$cho_dir/$p_name$con_name" - # Move the conflicting file to the choices directory # and name it according to the format above. mv -f "$tar_dir/$p_name/$con" \ "$tar_dir/$p_name/$cho_dir/$p_name$con_name" + regex_escape "$con" "/$cho_dir/$p_name$con_name" + # Rewrite the package's manifest to update its location # to its new spot (and name) in the choices directory. sed -i "s/$sea/$rep/" \ From 0a1791ca470d8b3696741e5e4b3d97f144ea1f51 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 17:00:29 +0200 Subject: [PATCH 11/12] kiss: Make alternatives opt in --- kiss | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/kiss b/kiss index 214a037..66daefd 100755 --- a/kiss +++ b/kiss @@ -771,15 +771,17 @@ pkg_conflicts() { shift "$(($# ? 1 : 0))" - [ "$p_name" != "$i_name" ] || continue + [ "$p_name" = "$i_name" ] && continue set -- "$@" "$pkg" done + [ -s "$cac_dir/$pid-m" ] || return 0 + # Use 'grep' to list matching lines between the to # be installed package's manifest and the above filtered # list. - [ -s "$cac_dir/$pid-m" ] && { + if [ "$KISS_CHOICE" ]; then "$grep" -Fxf "$cac_dir/$pid-m" -- "$@" | # This is a novel way of offering an "alternatives" system. @@ -826,7 +828,13 @@ pkg_conflicts() { sed -i "s/$sea/$rep/" \ "$tar_dir/$p_name/$pkg_db/$p_name/manifest" done - } + else + if "$grep" -Fxf "$cac_dir/$pid-m" -- "$@"; then + log "Package '$p_name' conflicts with another package" "" "!>" + log "Run 'KISS_CHOICE=1 kiss i $p_name' to add conflicts" "" "!>" + die "as alternatives." + fi + fi } pkg_swap() { From f5d3f19febf858b35d6bd9b698cd2147021c29d6 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Tue, 28 Jan 2020 17:22:48 +0200 Subject: [PATCH 12/12] kiss: pull in fix from master --- kiss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kiss b/kiss index 66daefd..c309ef4 100755 --- a/kiss +++ b/kiss @@ -967,7 +967,7 @@ pkg_install() { mkdir -p "$tar_dir/$pkg_name" # Extract the tar-ball to catch any errors before installation begins. - tar pxf "$tar_file" -C "$tar_dir/$pkg_name" || + dosu tar pxf "'$tar_file'" -C "'$tar_dir/$pkg_name'" || die "$pkg_name" "Failed to extract tar-ball" log "$pkg_name" "Checking that all dependencies are installed"