diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2022-04-16 22:43:54 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2022-04-17 15:44:24 -0700 |
commit | 10fc6c4b8c3679a8b5a42d9ba051be9f626708cf (patch) | |
tree | 09b5d4ca1acf40bab9c6f4a592779f858fae0514 /lib-src | |
parent | a8e8f6c291d7414ebb3856a957b33d64223c994d (diff) | |
download | dotfiles-10fc6c4b8c3679a8b5a42d9ba051be9f626708cf.tar.gz |
tidy away lib/
Diffstat (limited to 'lib-src')
-rw-r--r-- | lib-src/certs/fastmail.crt | 39 | ||||
-rw-r--r-- | lib-src/img/60degree_gray.png | bin | 0 -> 583 bytes | |||
-rw-r--r-- | lib-src/img/diagmonds_2X.png | bin | 0 -> 6245 bytes | |||
-rw-r--r-- | lib-src/img/green_gobbler.png | bin | 0 -> 1199 bytes | |||
-rw-r--r-- | lib-src/img/hoffman.png | bin | 0 -> 95 bytes | |||
-rw-r--r-- | lib-src/img/triangular.png | bin | 0 -> 210 bytes | |||
-rw-r--r-- | lib-src/mr/dgit | 92 | ||||
-rw-r--r-- | lib-src/mr/stow | 273 | ||||
-rwxr-xr-x | lib-src/stow/chkstow | 113 | ||||
-rwxr-xr-x | lib-src/stow/stow | 665 | ||||
-rwxr-xr-x | lib-src/tputfs.sh | 15 |
11 files changed, 1197 insertions, 0 deletions
diff --git a/lib-src/certs/fastmail.crt b/lib-src/certs/fastmail.crt new file mode 100644 index 00000000..45cfa5de --- /dev/null +++ b/lib-src/certs/fastmail.crt @@ -0,0 +1,39 @@ +-----BEGIN CERTIFICATE----- +MIIGwzCCBaugAwIBAgIQD5xq1d7BbAitZ5E4+BdHsTANBgkqhkiG9w0BAQsFADBP +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMSkwJwYDVQQDEyBE +aWdpQ2VydCBUTFMgUlNBIFNIQTI1NiAyMDIwIENBMTAeFw0yMTEwMDUwMDAwMDBa +Fw0yMjEwMjcyMzU5NTlaMGgxCzAJBgNVBAYTAkFVMREwDwYDVQQIEwhWaWN0b3Jp +YTESMBAGA1UEBxMJTWVsYm91cm5lMRkwFwYDVQQKExBGYXN0TWFpbCBQdHkgTHRk +MRcwFQYDVQQDDA4qLmZhc3RtYWlsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAMgrttOYWQPfMO8UqlPqHYJGtOApr5ydnvNOhFBV1ERdT7ANqpxx +ZuKEFHSwaFh6hR+qSNB+sUzKor8WsYQH9lbLEDHqrkcWcFBO5BWrYJ1/TyWj54t0 +Yg6X6iNXY5UgD4LzzP89++Pw96qjXdv8ukqhYPGwwR5HUJ9bfJegRWL9q8hzZTTK +hnph+oZD1T9BfaBAwPUaoDUEiIJ/FSvttnG0DIvX3L5nzsS0v10YZMFX4aOH8PIB +MybLWTAtyhldPcaeJ6rZ6eypE5TGlDZjRTuLDDnmRTzoKXH0qdEqqxYkbBsu2lD4 +0UVvIML/m1NNHqI1t604Zw8DrGmexsWAjhMCAwEAAaOCA4AwggN8MB8GA1UdIwQY +MBaAFLdrouqoqoSMeeq02g+YssWVdrn0MB0GA1UdDgQWBBQD1AeksGVJwDyP6JoI +h8q1x2Ln+jAnBgNVHREEIDAegg4qLmZhc3RtYWlsLmNvbYIMZmFzdG1haWwuY29t +MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +gY8GA1UdHwSBhzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp +Z2lDZXJ0VExTUlNBU0hBMjU2MjAyMENBMS00LmNybDBAoD6gPIY6aHR0cDovL2Ny +bDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VExTUlNBU0hBMjU2MjAyMENBMS00LmNy +bDA+BgNVHSAENzA1MDMGBmeBDAECAjApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3 +LmRpZ2ljZXJ0LmNvbS9DUFMwfwYIKwYBBQUHAQEEczBxMCQGCCsGAQUFBzABhhho +dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wSQYIKwYBBQUHMAKGPWh0dHA6Ly9jYWNl +cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRMU1JTQVNIQTI1NjIwMjBDQTEtMS5j +cnQwDAYDVR0TAQH/BAIwADCCAX8GCisGAQQB1nkCBAIEggFvBIIBawFpAHcAKXm+ +8J45OSHwVnOfY6V35b5XfZxgCvj5TV0mXCVdx4QAAAF8TdvgDwAABAMASDBGAiEA +gGFYgtT9rxf1ppvPyDMJ6ZNyjqnJgish3UDoimlf9GICIQCpsI9wTwhsvCKysb9Z +0X9TPbiI2u63ndSTV6UtXsHDbQB2AFGjsPX9AXmcVm24N3iPDKR6zBsny/eeiEKa +Df7UiwXlAAABfE3b4HQAAAQDAEcwRQIgLUC3aTBvYDhGSyA+VCnuho3dGVEb+bGm +Yyd1UzxXJpsCIQDjBJ13HJAWtgbAAB6AnDrH8Bk2ANmoa+n1jlH1vbNbxAB2AEHI +yrHfIkZKEMahOglCh15OMYsbA+vrS8do8JBilgb2AAABfE3b3/4AAAQDAEcwRQIh +AJC1Yti5EpZewIBfVW+KP0bQkyg6rudeTu2u2v2LVjP4AiBfOMTkEFNnNOUguReI +2bdgBdiHjvJW7pEf8CxUylTS+TANBgkqhkiG9w0BAQsFAAOCAQEAqOLSZu52qSEK +5Tu3BPj8uAEF+AOs4V+8bqZEmAy7QPookkw7SX9QtAF3zEIsaoMAEt5IWsQJsTiD +dwvilfK7x6n2XRwBwIwgLBZGHpqx63gcZd2b7dZUI/GW84IL3NxhK8wBE/L9vpql +wyrCEPgGEL2j5SN2sK8oZIAS8PzzHUWzlW2JXLT0qGUXkbEhpsnqfhjVzQxz0LIm +aljEwKGyA6mLvZKXWAjMx301FQqI8+82UYtNOjrka+H8gKtm6mze3s1txCT7SM/l +zXyXAgwVGpCH0dp+KueHtaAH2YxvQldTtXjKYc81UEZVuuZadIWP00BNmK/CxG0A +Nl3/5l+WIg== +-----END CERTIFICATE----- diff --git a/lib-src/img/60degree_gray.png b/lib-src/img/60degree_gray.png Binary files differnew file mode 100644 index 00000000..e4eebaec --- /dev/null +++ b/lib-src/img/60degree_gray.png diff --git a/lib-src/img/diagmonds_2X.png b/lib-src/img/diagmonds_2X.png Binary files differnew file mode 100644 index 00000000..51a3bdc3 --- /dev/null +++ b/lib-src/img/diagmonds_2X.png diff --git a/lib-src/img/green_gobbler.png b/lib-src/img/green_gobbler.png Binary files differnew file mode 100644 index 00000000..ed78631f --- /dev/null +++ b/lib-src/img/green_gobbler.png diff --git a/lib-src/img/hoffman.png b/lib-src/img/hoffman.png Binary files differnew file mode 100644 index 00000000..05978122 --- /dev/null +++ b/lib-src/img/hoffman.png diff --git a/lib-src/img/triangular.png b/lib-src/img/triangular.png Binary files differnew file mode 100644 index 00000000..7f41795c --- /dev/null +++ b/lib-src/img/triangular.png diff --git a/lib-src/mr/dgit b/lib-src/mr/dgit new file mode 100644 index 00000000..6a26d065 --- /dev/null +++ b/lib-src/mr/dgit @@ -0,0 +1,92 @@ +# A module for working with dgit(1) repositories +# +# Copyright (C) 2017, 2020 Sean Whitton +# +# Maintainer: Sean Whitton <spwhitton@spwhitton.name> + +# DEFINITION +# +# A dgit repository is a git repository containing special remote refs +# of the form `remotes/dgit/dgit/*` and possibly also branches of the +# form `dgit/*`. See dgit(7) for more details. +# +# USAGE +# +# To make mr use this file, add a line like this inside the [DEFAULT] +# section of your ~/.mrconfig: +# +# include = cat /usr/share/mr/dgit +# +# With this module active, +# +# `mr update` will: +# +# (1) if the current branch is named `dgit/SUITE`, call `dgit pull`. +# +# (2) use `dgit fetch` to update all remotes/dgit/dgit/*` refs. +# +# `mr register` will add a post_checkout hook that calls `dgit fetch`. +# +# DESIGN +# +# A dgit repository may have been obtained with `dgit clone`, or `git +# clone` followed by `dgit fetch`. It is not possible for us to +# determine which of these methods was used after the fact. +# +# For this reason, we do not define dgit_register and dgit_test so as +# to generate .mrconfig lines like +# +# [foo] +# checkout = dgit clone foo +# +# because that would fail to fetch branches not published to +# dgit-repos, such as branches published to alioth.debian.org. +# +# Instead, we extend git_update and git_register to obtain updates +# from dgit-repos. + +# we obtain the source package name from git and pass it to dgit with +# the latter's -p option, because the currently checked out branch may +# not contain debian/control, which would cause dgit fetch without -p +# to fail +lib = + set_source () { + if [ -z "$source" ]; then + source=$(git cat-file blob dgit/dgit/$1:debian/control \ + | awk -F': ' '$1 ~ /Source/ { print $2 }') + fi + } + +# We can't use git_update_append because mr's built-in default `git +# pull` will often fail on a `dgit/SUITE` branch. After a `dgit +# clone`, (i) `dgit/SUITE` has no upstream tracking branch, and (ii) +# the 'origin' remote might not exist on the remote end (it depends on +# whether the package has ever been uploaded with `dgit push`) +git_update = + current_branch="$(git rev-parse --abbrev-ref HEAD)" + if [ "$(echo $current_branch | cut -s -d/ -f1)" = "dgit" ]; then + # (1) above + dgit pull + else + # mr's built-in git_update + git pull "$@" + fi + # (2) above + for suite in $(git show-ref | grep refs/remotes/dgit/dgit | cut -d/ -f5 ); do + # skip if we just called `dgit pull` on this suite + if ! [ "$current_branch" = "dgit/$suite" ]; then + set_source $suite + dgit -p$source fetch $suite + fi + done + +git_register_append = + lines="cd '$MR_REPO'" + for suite in $(git show-ref | grep refs/remotes/dgit/dgit | cut -d/ -f5 ); do + set_source $suite + echo "Registering dgit suite: $suite in $MR_CONFIG" + lines="$lines; dgit -p$source fetch $suite" + done + if ! [ "$lines" = "cd '$MR_REPO'" ]; then + mr -c "$MR_CONFIG" config "`pwd`" post_checkout="$lines" + fi diff --git a/lib-src/mr/stow b/lib-src/mr/stow new file mode 100644 index 00000000..254bd0c3 --- /dev/null +++ b/lib-src/mr/stow @@ -0,0 +1,273 @@ +# Plug-in to use GNU Stow to manage symlinks whose targets lie in a +# repository managed with myrepos +# +# The standard use case is for managing dotfiles inside one's home +# directory. +# +# Original author (2011): +# Adam Spiers <mr@adamspiers.org> +# +# This version reworked (2016, 2017) & maintained (2017) by: +# Sean Whitton <spwhitton@spwhitton.name> + +# BASIC USAGE INSTRUCTIONS +# +# To make mr use this file, add a line like this inside the [DEFAULT] +# section of your ~/.mrconfig: +# +# include = cat /usr/share/mr/stow +# +# and then inside each [repo] section of your ~/.mrconfig for +# which you want the contents to be stowed, add this line: +# +# stowable = true +# +# You must have at least version 2.1.0 of stow available. [1] +# +# If stow is not in your $PATH, you can export STOW_COMMAND to tell +# this plug-in where it is. +# +# The default behaviour is to stow on checkout, and restow on update. +# The manual actions 'stow', 'restow', 'unstow' and 'adopt' are also +# available. +# +# By default, ~/.STOW is used as the stow directory, and ~ as the +# target directory. You can export STOW_DIR and STOW_TARGET to +# override these defaults. +# +# DEALING WITH APPLICATIONS THAT MISTREAT SYMLINKS +# +# Some programs will replace a symlink to a stowed file with a regular +# copy of the file, and a subset of these will do this even if they +# haven't edited the file. This will cause stow operations to fail. +# +# To deal with this, run 'mr adopt'. This will move the modified file +# into your repository, and restore the usual symlink. Then you can +# use your VCS tools ('git diff', 'hg diff') to decide whether you +# want to keep the changes. +# +# FOLDING +# +# By default, this library passes --no-folding to stow. This allows +# you to have more than one repository stowing files into a single +# subdirectory in your home directory. For example, you might have a +# private and a public repository both stowing into ~/.gnupg. If you +# don't want this behaviour, set MR_FOLD. For example, in a +# repository's myrepos config section or in [DEFAULT]: +# +# lib = MR_FOLD= +# +# FIXUPS THAT CREATE FILES TO BE STOWED +# +# Stowing is automatically performed via post_checkout, and restowing +# via post_update, as can be seen from below (search for 'Automatic +# actions'). Note that these run before fixups, which allows fixups +# to refer to stowed files, but isn't ideal if the fixups are +# responsible for creating the stow package's installation image, +# e.g. via a typical './configure && make install' sequence. Here's a +# suggested mrconfig chunk to handle this particular use case: +# +# stowable = true +# lib = +# STOW_PKG_TYPE=directory +# STOW_NO_AUTOMATIC_ACTIONS=yes +# mr_pre_unstow () { +# install-info --delete --info-dir=$HOME/share/info $STOW_PKG_PATH/share/info/*.info +# } +# mr_post_stow () { +# install-info --info-dir=$HOME/share/info $STOW_PKG_PATH/share/info/*.info +# } +# fixups = +# if ! [ -e configure ]; then +# bash ./autogen.sh +# fi +# set_stow_common_opts +# ./configure --prefix=$STOW_PKG_PATH +# make install prefix=$STOW_PKG_PATH +# rm $STOW_PKG_PATH/share/info/dir +# mr_restow_regardless +# +# [1] Older versions could create a frankenstein ~/.git/ directory +# containing symlinks to multiple .git/ sub-directories in different +# stow packages! 2.1.0 onwards does not have this problem - it +# supports local per-directory .stow-local-ignore and global +# ~/.stow-global-ignore files, and even without configuration of +# these, it chooses sensible default ignore lists which prevent +# stowing of a package's .git/ sub-directory. These ignore lists are +# also ideal if you only want to stow a subset of a stow package's +# contents. + +lib = + : ${STOW_DIR:=$HOME/.STOW} + : ${STOW_TARGET:=$HOME} + STOW_NAME=$(echo "$MR_REPO" | tr / _) + # + if ! [ -d "$STOW_TARGET" ]; then mkdir -p "$STOW_TARGET"; fi + if ! [ -d "$STOW_DIR" ]; then mkdir -p "$STOW_DIR" ; fi + if ! [ -f "$STOW_DIR/.stow" ]; then touch "$STOW_DIR/.stow"; fi + # + #MR_STOWABLE=no + is_stowable () { + [ -z "$MR_DISABLE_STOW" ] && + ( cd "$MR_REPO" && mr stowable >/dev/null 2>&1 ) + #[ "$MR_STOWABLE" = yes ] + } + stowable_then_continue () { + if is_stowable; then + return 0 + else + if [ -n "$1" ]; then + info "$STOW_NAME isn't stowable; skipping $MR_ACTION" + fi + return 1 + fi + } + # + set_stow_common_opts () { + : ${STOW_PKG_TYPE:=symlink} + STOW_PKG_PATH="$STOW_DIR/$STOW_NAME" + # canonicalise -t and -d params with readlink if available + # stow can fail if they aren't canonical + if which readlink >/dev/null 2>&1; then + stow_common_opts="-t $(readlink -f $STOW_TARGET) -d $(readlink -f $STOW_DIR)" + else + stow_common_opts="-t $STOW_TARGET -d $STOW_DIR" + fi + STOW="${STOW_COMMAND:-$HOME/src/dotfiles/lib-src/stow/stow}" + case "`$STOW --version`" in + 'version 1.*') + stow_common_opts="$stow_common_opts -p" + ;; + *) + ;; + esac + if [ -n "$MR_STOW_OPTIONS" ]; then + stow_common_opts="$stow_common_opts $MR_STOW_OPTIONS" + fi + if [ -n "$MR_STOW_OVER" ]; then + stow_common_opts="$stow_common_opts --override=$MR_STOW_OVER" + fi + if ! (: "${MR_FOLD?}") 2>/dev/null; then + stow_common_opts="$stow_common_opts --no-folding" + fi + } + # + mr_stow () { + stowable_then_continue || return 0 + set_stow_common_opts + ensure_package_exists + command "$STOW" $stow_common_opts "$@" "$STOW_NAME" + mr_post_stow + info "Stowed $STOW_NAME" + } + mr_restow_if_already_stowed () { + stowable_then_continue || return 0 + if ! [ -L "$STOW_PKG_PATH" ]; then + info "$MR_REPO wasn't stowed yet; won't restow." + return + fi + mr_restow_regardless "$@" + } + mr_restow_regardless () { + stowable_then_continue || return 0 + set_stow_common_opts + ensure_package_exists + mr_pre_unstow + command "$STOW" -R $stow_common_opts "$@" "$STOW_NAME" + mr_post_stow + info "Restowed $STOW_NAME" + } + mr_pre_unstow () { + : # This can be "overridden" by the lib section of a repo definition + #info "no mr_pre_unstow hook" + } + mr_post_stow () { + : # This can be "overridden" by the lib section of a repo definition + #info "no mr_post_stow hook" + } + mr_unstow () { + stowable_then_continue || return 0 + set_stow_common_opts + if ! [ -d "$STOW_PKG_PATH" ]; then + info "$MR_REPO wasn't stowed yet in $STOW_PKG_PATH; can't unstow." + return + fi + mr_pre_unstow + command "$STOW" -D $stow_common_opts "$@" "$STOW_NAME" + if [ "$STOW_PKG_TYPE" = 'symlink' ]; then + rm -f "$STOW_PKG_PATH" + fi + info "Unstowed $STOW_NAME" + } + # + ensure_symlink_exists () { + [ $# = 2 ] || error "CONFIG BUG: Usage: ensure_symlink_exists SYMLINK TARGET" + symlink="$1" + required_target="$2" + if [ -L "$symlink" ]; then + actual_target="`readlink $symlink`" + if [ "$actual_target" = "$required_target" ]; then + return + else + error "Symlink $symlink already points to $actual_target, cannot point to $required_target; aborting." + fi + fi + if [ -e "$symlink" ]; then + error "Cannot create symlink $symlink - already exists; aborting." + fi + ln -s "$required_target" "$symlink" + } + # + mr_adopt () { + stowable_then_continue || return 0 + set_stow_common_opts + ensure_package_exists + mr_pre_unstow + command "$STOW" --adopt $stow_common_opts "$@" "$STOW_NAME" + mr_post_stow + info "Stowed $STOW_NAME with adoption" + } + # + ensure_package_exists () { + case "$STOW_PKG_TYPE" in + symlink) + ensure_symlink_exists "$STOW_PKG_PATH" "$MR_REPO" + ;; + directory) + [ -e "$STOW_PKG_PATH" ] || mkdir "$STOW_PKG_PATH" + [ -d "$STOW_PKG_PATH" ] || error "Expected $STOW_PKG_PATH to be a directory; aborting." + if [ -L "$STOW_PKG_PATH" ]; then + error "Didn't expect $STOW_PKG_PATH to be a symlink; aborting." + fi + ;; + *) + error "Unrecognised value '$STOW_PKG_TYPE' for \$STOW_PKG_TYPE; aborting." + ;; + esac + } + +#stowable = is_stowable +stowable = false +showstowable = + if is_stowable; then + echo "$STOW_NAME is stowable" + else + echo "$STOW_NAME is not stowable" + fi + +# Automatic actions +post_checkout_append = [ -n "$STOW_NO_AUTOMATIC_ACTIONS" ] || mr_stow +#post_update_append = mr_restow_if_already_stowed +post_update_append = [ -n "$STOW_NO_AUTOMATIC_ACTIONS" ] || mr_restow_regardless + +# Manual actions +stow = mr_stow "$@" +stowover = MR_STOW_OVER=. mr_stow "$@" +unstow = mr_unstow "$@" +restow = mr_restow_regardless "$@" +restowover = MR_STOW_OVER=. mr_restow_regardless "$@" +adopt = mr_adopt "$@" + +# Local variables: +# mode: sh +# End: diff --git a/lib-src/stow/chkstow b/lib-src/stow/chkstow new file mode 100755 index 00000000..a74d1b90 --- /dev/null +++ b/lib-src/stow/chkstow @@ -0,0 +1,113 @@ +#!/usr/bin/env perl + +use strict; +use warnings; + +require 5.006_001; + +use File::Find; +use Getopt::Long; + +my $DEFAULT_TARGET = '/usr/local/'; + +our $Wanted = \&bad_links; +our %Package = (); +our $Stow_dir = ''; +our $Target = $DEFAULT_TARGET; + +# put the main loop into a block so that tests can load this as a module +if ( not caller() ) { + if (@ARGV == 0) { + usage(); + } + process_options(); + #check_stow($Target, $Wanted); + check_stow(); +} + +sub process_options { + GetOptions( + 'b|badlinks' => sub { $Wanted = \&bad_links }, + 'a|aliens' => sub { $Wanted = \&aliens }, + 'l|list' => sub { $Wanted = \&list }, + 't|target=s' => \$Target, + ) or usage(); + return; +} + +sub usage { + print <<"EOT"; +USAGE: chkstow [options] + +Options: + -t DIR, --target=DIR Set the target directory to DIR + (default is $DEFAULT_TARGET) + -b, --badlinks Report symlinks that point to non-existent files + -a, --aliens Report non-symlinks in the target directory + -l, --list List packages in the target directory + +--badlinks is the default mode. +EOT + exit(0); +} + +sub check_stow { + #my ($Target, $Wanted) = @_; + + my (%options) = ( + wanted => $Wanted, + preprocess => \&skip_dirs, + ); + + find(\%options, $Target); + + if ($Wanted == \&list) { + delete $Package{''}; + delete $Package{'..'}; + + if (keys %Package) { + print map "$_\n", sort(keys %Package); + } + } + return; +} + +sub skip_dirs { + # skip stow source and unstowed targets + if (-e ".stow" || -e ".notstowed" ) { + warn "skipping $File::Find::dir\n"; + return (); + } + else { + return @_; + } +} + +# checking for files that do not link to anything +sub bad_links { + -l && !-e && print "Bogus link: $File::Find::name\n"; +} + +# checking for files that are not owned by stow +sub aliens { + !-l && !-d && print "Unstowed file: $File::Find::name\n"; +} + +# just list the packages in the the target directory +# FIXME: what if the stow dir is not called 'stow'? +sub list { + if (-l) { + $_ = readlink; + s{\A(?:\.\./)+stow/}{}g; + s{/.*}{}g; + $Package{$_} = 1; + } +} + +1; # Hey, it's a module! + +# Local variables: +# mode: perl +# cperl-indent-level: 4 +# End: +# vim: ft=perl diff --git a/lib-src/stow/stow b/lib-src/stow/stow new file mode 100755 index 00000000..b94dc88d --- /dev/null +++ b/lib-src/stow/stow @@ -0,0 +1,665 @@ +#!/usr/bin/env perl + +# GNU Stow - manage the installation of multiple software packages +# Copyright (C) 1993, 1994, 1995, 1996 by Bob Glickstein +# Copyright (C) 2000, 2001 Guillaume Morin +# Copyright (C) 2007 Kahlil Hodgson +# Copyright (C) 2011 Adam Spiers +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. + +=head1 NAME + +stow - software package installation manager + +=head1 SYNOPSIS + +stow [ options ] package ... + +=head1 DESCRIPTION + +This manual page describes GNU Stow 2.2.2, a program for managing +the installation of software packages. This is not the definitive +documentation for stow; for that, see the info manual. + +Stow is a tool for managing the installation of multiple software +packages in the same run-time directory tree. One historical +difficulty of this task has been the need to administer, upgrade, +install, and remove files in independent packages without confusing +them with other files sharing the same filesystem space. For instance, +it is common to install Perl and Emacs in F</usr/local>. When one +does so, one winds up (as of Perl 4.036 and Emacs 19.22) with the +following files in F</usr/local/man/man1>: F<a2p.1>; F<ctags.1>; +F<emacs.1>; F<etags.1>; F<h2ph.1>; F<perl.1>; and F<s2p.1>. Now +suppose it's time to uninstall Perl. Which man pages get removed? +Obviously F<perl.1> is one of them, but it should not be the +administrator's responsibility to memorize the ownership of individual +files by separate packages. + +The approach used by Stow is to install each package into its own +tree, then use symbolic links to make it appear as though the files +are installed in the common tree. Administration can be performed in +the package's private tree in isolation from clutter from other +packages. Stow can then be used to update the symbolic links. The +structure of each private tree should reflect the desired structure in +the common tree; i.e. (in the typical case) there should be a F<bin> +directory containing executables, a F<man/man1> directory containing +section 1 man pages, and so on. + +Stow was inspired by Carnegie Mellon's Depot program, but is +substantially simpler and safer. Whereas Depot required database files +to keep things in sync, Stow stores no extra state between runs, so +there's no danger (as there was in Depot) of mangling directories when +file hierarchies don't match the database. Also unlike Depot, Stow +will never delete any files, directories, or links that appear in a +Stow directory (e.g., F</usr/local/stow/emacs>), so it's always +possible to rebuild the target tree (e.g., F</usr/local>). + +=head1 TERMINOLOGY + +A "package" is a related collection of files and directories that +you wish to administer as a unit -- e.g., Perl or Emacs -- and that +needs to be installed in a particular directory structure -- e.g., +with F<bin>, F<lib>, and F<man> subdirectories. + +A "target directory" is the root of a tree in which one or more +packages wish to B<appear> to be installed. A common, but by no means +the only such location is F</usr/local>. The examples in this manual +page will use F</usr/local> as the target directory. + +A "stow directory" is the root of a tree containing separate +packages in private subtrees. When Stow runs, it uses the current +directory as the default stow directory. The examples in this manual +page will use F</usr/local/stow> as the stow directory, so that +individual packages will be, for example, F</usr/local/stow/perl> and +F</usr/local/stow/emacs>. + +An "installation image" is the layout of files and directories +required by a package, relative to the target directory. Thus, the +installation image for Perl includes: a F<bin> directory containing +F<perl> and F<a2p> (among others); an F<info> directory containing +Texinfo documentation; a F<lib/perl> directory containing Perl +libraries; and a F<man/man1> directory containing man pages. + +A "package directory" is the root of a tree containing the +installation image for a particular package. Each package directory +must reside in a stow directory -- e.g., the package directory +F</usr/local/stow/perl> must reside in the stow directory +F</usr/local/stow>. The "name" of a package is the name of its +directory within the stow directory -- e.g., F<perl>. + +Thus, the Perl executable might reside in +F</usr/local/stow/perl/bin/perl>, where F</usr/local> is the target +directory, F</usr/local/stow> is the stow directory, +F</usr/local/stow/perl> is the package directory, and F<bin/perl> +within is part of the installation image. + +A "symlink" is a symbolic link. A symlink can be "relative" or +"absolute". An absolute symlink names a full path; that is, one +starting from F</>. A relative symlink names a relative path; that +is, one not starting from F</>. The target of a relative symlink is +computed starting from the symlink's own directory. Stow only creates +relative symlinks. + +=head1 OPTIONS + +The stow directory is assumed to be the value of the C<STOW_DIR> +environment variable or if unset the current directory, and the target +directory is assumed to be the parent of the current directory (so it +is typical to execute F<stow> from the directory F</usr/local/stow>). +Each F<package> given on the command line is the name of a package in +the stow directory (e.g., F<perl>). By default, they are installed +into the target directory (but they can be deleted instead using +C<-D>). + +=over 4 + +=item -n + +=item --no + +Do not perform any operations that modify the filesystem; merely show +what would happen. + +=item -d DIR + +=item --dir=DIR + +Set the stow directory to C<DIR> instead of the current directory. +This also has the effect of making the default target directory be the +parent of C<DIR>. + +=item -t DIR + +=item --target=DIR + +Set the target directory to C<DIR> instead of the parent of the stow +directory. + +=item -v + +=item --verbose[=N] + +Send verbose output to standard error describing what Stow is +doing. Verbosity levels are 0, 1, 2, 3, and 4; 0 is the default. +Using C<-v> or C<--verbose> increases the verbosity by one; using +`--verbose=N' sets it to N. + +=item -S + +=item --stow + +Stow the packages that follow this option into the target directory. +This is the default action and so can be omitted if you are only +stowing packages rather than performing a mixture of +stow/delete/restow actions. + +=item -D + +=item --delete + +Unstow the packages that follow this option from the target directory rather +than installing them. + +=item -R + +=item --restow + +Restow packages (first unstow, then stow again). This is useful +for pruning obsolete symlinks from the target tree after updating +the software in a package. + +=item --adopt + +B<Warning!> This behaviour is specifically intended to alter the +contents of your stow directory. If you do not want that, this option +is not for you. + +When stowing, if a target is encountered which already exists but is a +plain file (and hence not owned by any existing stow package), then +normally Stow will register this as a conflict and refuse to proceed. +This option changes that behaviour so that the file is moved to the +same relative place within the package's installation image within the +stow directory, and then stowing proceeds as before. So effectively, +the file becomes adopted by the stow package, without its contents +changing. + +=item --no-folding + +Disable folding of newly stowed directories when stowing, and +refolding of newly foldable directories when unstowing. + +=item --ignore=REGEX + +Ignore files ending in this Perl regex. + +=item --defer=REGEX + +Don't stow files beginning with this Perl regex if the file is already +stowed to another package. + +=item --override=REGEX + +Force stowing files beginning with this Perl regex if the file is +already stowed to another package. + +=item -V + +=item --version + +Show Stow version number, and exit. + +=item -h + +=item --help + +Show Stow command syntax, and exit. + +=back + +=head1 INSTALLING PACKAGES + +The default action of Stow is to install a package. This means +creating symlinks in the target tree that point into the package tree. +Stow attempts to do this with as few symlinks as possible; in other +words, if Stow can create a single symlink that points to an entire +subtree within the package tree, it will choose to do that rather than +create a directory in the target tree and populate it with symlinks. + +For example, suppose that no packages have yet been installed in +F</usr/local>; it's completely empty (except for the F<stow> +subdirectory, of course). Now suppose the Perl package is installed. +Recall that it includes the following directories in its installation +image: F<bin>; F<info>; F<lib/perl>; F<man/man1>. Rather than +creating the directory F</usr/local/bin> and populating it with +symlinks to F<../stow/perl/bin/perl> and F<../stow/perl/bin/a2p> (and +so on), Stow will create a single symlink, F</usr/local/bin>, which +points to F<stow/perl/bin>. In this way, it still works to refer to +F</usr/local/bin/perl> and F</usr/local/bin/a2p>, and fewer symlinks +have been created. This is called "tree folding", since an entire +subtree is "folded" into a single symlink. + +To complete this example, Stow will also create the symlink +F</usr/local/info> pointing to F<stow/perl/info>; the symlink +F</usr/local/lib> pointing to F<stow/perl/lib>; and the symlink +F</usr/local/man> pointing to F<stow/perl/man>. + +Now suppose that instead of installing the Perl package into an empty +target tree, the target tree is not empty to begin with. Instead, it +contains several files and directories installed under a different +system-administration philosophy. In particular, F</usr/local/bin> +already exists and is a directory, as are F</usr/local/lib> and +F</usr/local/man/man1>. In this case, Stow will descend into +F</usr/local/bin> and create symlinks to F<../stow/perl/bin/perl> and +F<../stow/perl/bin/a2p> (etc.), and it will descend into +F</usr/local/lib> and create the tree-folding symlink F<perl> pointing +to F<../stow/perl/lib/perl>, and so on. As a rule, Stow only descends +as far as necessary into the target tree when it can create a +tree-folding symlink. + +The time often comes when a tree-folding symlink has to be undone +because another package uses one or more of the folded subdirectories +in its installation image. This operation is called "splitting open" +a folded tree. It involves removing the original symlink from the +target tree, creating a true directory in its place, and then +populating the new directory with symlinks to the newly-installed +package B<and> to the old package that used the old symlink. For +example, suppose that after installing Perl into an empty +F</usr/local>, we wish to install Emacs. Emacs's installation image +includes a F<bin> directory containing the F<emacs> and F<etags> +executables, among others. Stow must make these files appear to be +installed in F</usr/local/bin>, but presently F</usr/local/bin> is a +symlink to F<stow/perl/bin>. Stow therefore takes the following +steps: the symlink F</usr/local/bin> is deleted; the directory +F</usr/local/bin> is created; links are made from F</usr/local/bin> to +F<../stow/emacs/bin/emacs> and F<../stow/emacs/bin/etags>; and links +are made from F</usr/local/bin> to F<../stow/perl/bin/perl> and +F<../stow/perl/bin/a2p>. + +When splitting open a folded tree, Stow makes sure that the symlink +it is about to remove points inside a valid package in the current stow +directory. + +=head2 Stow will never delete anything that it doesn't own. + +Stow "owns" everything living in the target tree that points into a +package in the stow directory. Anything Stow owns, it can recompute if +lost. Note that by this definition, Stow doesn't "own" anything +B<in> the stow directory or in any of the packages. + +If Stow needs to create a directory or a symlink in the target tree +and it cannot because that name is already in use and is not owned by +Stow, then a conflict has arisen. See the "Conflicts" section in the +info manual. + +=head1 DELETING PACKAGES + +When the C<-D> option is given, the action of Stow is to delete a +package from the target tree. Note that Stow will not delete anything +it doesn't "own". Deleting a package does B<not> mean removing it from +the stow directory or discarding the package tree. + +To delete a package, Stow recursively scans the target tree, skipping +over the stow directory (since that is usually a subdirectory of the +target tree) and any other stow directories it encounters (see +"Multiple stow directories" in the info manual). Any symlink it +finds that points into the package being deleted is removed. Any +directory that contained only symlinks to the package being deleted is +removed. Any directory that, after removing symlinks and empty +subdirectories, contains only symlinks to a single other package, is +considered to be a previously "folded" tree that was "split open." +Stow will re-fold the tree by removing the symlinks to the surviving +package, removing the directory, then linking the directory back to +the surviving package. + +=head1 SEE ALSO + +The full documentation for F<stow> is maintained as a Texinfo manual. +If the F<info> and F<stow> programs are properly installed at your site, the command + + info stow + +should give you access to the complete manual. + +=head1 BUGS + +Please report bugs in Stow using the Debian bug tracking system. + +Currently known bugs include: + +=over 4 + +=item * The empty-directory problem. + +If package F<foo> includes an empty directory -- say, F<foo/bar> -- +then if no other package has a F<bar> subdirectory, everything's fine. +If another stowed package F<quux>, has a F<bar> subdirectory, then +when stowing, F<targetdir/bar> will be "split open" and the contents +of F<quux/bar> will be individually stowed. So far, so good. But when +unstowing F<quux>, F<targetdir/bar> will be removed, even though +F<foo/bar> needs it to remain. A workaround for this problem is to +create a file in F<foo/bar> as a placeholder. If you name that file +F<.placeholder>, it will be easy to find and remove such files when +this bug is fixed. + +=item * + +When using multiple stow directories (see "Multiple stow directories" +in the info manual), Stow fails to "split open" tree-folding symlinks +(see "Installing packages" in the info manual) that point into a stow +directory which is not the one in use by the current Stow +command. Before failing, it should search the target of the link to +see whether any element of the path contains a F<.stow> file. If it +finds one, it can "learn" about the cooperating stow directory to +short-circuit the F<.stow> search the next time it encounters a +tree-folding symlink. + +=back + +=head1 AUTHOR + +This man page was originally constructed by Charles Briscoe-Smith from +parts of Stow's info manual, and then converted to POD format by Adam +Spiers. The info manual contains the following notice, which, as it +says, applies to this manual page, too. The text of the section +entitled "GNU General Public License" can be found in the file +F</usr/share/common-licenses/GPL> on any Debian GNU/Linux system. If +you don't have access to a Debian system, or the GPL is not there, +write to the Free Software Foundation, Inc., 59 Temple Place, Suite +330, Boston, MA, 02111-1307, USA. + +=head1 COPYRIGHT + +Copyright (C) +1993, 1994, 1995, 1996 by Bob Glickstein <bobg+stow@zanshin.com>; +2000, 2001 by Guillaume Morin; +2007 by Kahlil Hodgson; +2011 by Adam Spiers; +and others. + +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided also that +the section entitled "GNU General Public License" is included with the +modified manual, and provided that the entire resulting derived work +is distributed under the terms of a permission notice identical to +this one. + +Permission is granted to copy and distribute translations of this +manual into another language, under the above conditions for modified +versions, except that this permission notice may be stated in a +translation approved by the Free Software Foundation. + +=cut + +use strict; +use warnings; + +require 5.006_001; + +use POSIX qw(getcwd); +use Getopt::Long; + +use lib "$ENV{HOME}/src/dotfiles/perl5"; +use Stow; +use Stow::Util qw(parent error); + +my $ProgramName = $0; +$ProgramName =~ s{.*/}{}; + +main() unless caller(); + +sub main { + my ($options, $pkgs_to_unstow, $pkgs_to_stow) = process_options(); + + my $stow = new Stow(%$options); + # current dir is now the target directory + + $stow->plan_unstow(@$pkgs_to_unstow); + $stow->plan_stow (@$pkgs_to_stow); + + my %conflicts = $stow->get_conflicts; + + if (%conflicts) { + foreach my $action ('unstow', 'stow') { + next unless $conflicts{$action}; + foreach my $package (sort keys %{ $conflicts{$action} }) { + warn "WARNING! ${action}ing $package would cause conflicts:\n"; + #if $stow->get_action_count > 1; + foreach my $message (sort @{ $conflicts{$action}{$package} }) { + warn " * $message\n"; + } + } + } + warn "All operations aborted.\n"; + exit 1; + } + else { + if ($options->{simulate}) { + warn "WARNING: in simulation mode so not modifying filesystem.\n"; + return; + } + + $stow->process_tasks(); + } +} + + +#===== SUBROUTINE =========================================================== +# Name : process_options() +# Purpose : parse command line options +# Parameters: none +# Returns : (\%options, \@pkgs_to_unstow, \@pkgs_to_stow) +# Throws : a fatal error if a bad command line option is given +# Comments : checks @ARGV for valid package names +#============================================================================ +sub process_options { + my %options = (); + my @pkgs_to_unstow = (); + my @pkgs_to_stow = (); + my $action = 'stow'; + + unshift @ARGV, get_config_file_options(); + #$,="\n"; print @ARGV,"\n"; # for debugging rc file + + Getopt::Long::config('no_ignore_case', 'bundling', 'permute'); + GetOptions( + \%options, + 'verbose|v:+', 'help|h', 'simulate|n|no', + 'version|V', 'compat|p', 'dir|d=s', 'target|t=s', + 'adopt', 'no-folding', + + # clean and pre-compile any regex's at parse time + 'ignore=s' => + sub { + my $regex = $_[1]; + push @{$options{ignore}}, qr($regex\z); + }, + + 'override=s' => + sub { + my $regex = $_[1]; + push @{$options{override}}, qr(\A$regex); + }, + + 'defer=s' => + sub { + my $regex = $_[1]; + push @{$options{defer}}, qr(\A$regex); + }, + + # a little craziness so we can do different actions on the same line: + # a -D, -S, or -R changes the action that will be performed on the + # package arguments that follow it. + 'D|delete' => sub { $action = 'unstow' }, + 'S|stow' => sub { $action = 'stow' }, + 'R|restow' => sub { $action = 'restow' }, + + # Handler for non-option arguments + '<>' => + sub { + if ($action eq 'restow') { + push @pkgs_to_unstow, $_[0]; + push @pkgs_to_stow, $_[0]; + } + elsif ($action eq 'unstow') { + push @pkgs_to_unstow, $_[0]; + } + else { + push @pkgs_to_stow, $_[0]; + } + }, + ) or usage(); + + usage() if $options{help}; + version() if $options{version}; + + sanitize_path_options(\%options); + check_packages(\@pkgs_to_unstow, \@pkgs_to_stow); + + return (\%options, \@pkgs_to_unstow, \@pkgs_to_stow); +} + +sub sanitize_path_options { + my ($options) = @_; + + if (exists $options->{dir}) { + $options->{dir} =~ s/\A +//; + $options->{dir} =~ s/ +\z//; + } + else { + $options->{dir} = exists $ENV{STOW_DIR} ? $ENV{STOW_DIR} : getcwd(); + } + + if (exists $options->{target}) { + $options->{target} =~ s/\A +//; + $options->{target} =~ s/ +\z//; + } + else { + $options->{target} = parent($options->{dir}) || '.'; + } +} + +sub check_packages { + my ($pkgs_to_stow, $pkgs_to_unstow) = @_; + + if (not @$pkgs_to_stow and not @$pkgs_to_unstow) { + usage("No packages to stow or unstow"); + } + + # check package arguments + for my $package (@$pkgs_to_stow, @$pkgs_to_unstow) { + $package =~ s{/+$}{}; # delete trailing slashes + if ($package =~ m{/}) { + error("Slashes are not permitted in package names"); + } + } +} + + +#===== SUBROUTINE ============================================================ +# Name : get_config_file_options() +# Purpose : search for default settings in any .stowrc files +# Parameters: none +# Returns : a list of default options +# Throws : no exceptions +# Comments : prepends the contents of '~/.stowrc' and '.stowrc' to the command +# : line so they get parsed just like normal arguments. (This was +# : hacked in so that Emil and I could set different preferences). +#============================================================================= +sub get_config_file_options { + my @defaults = (); + for my $file ("$ENV{HOME}/.stowrc", '.stowrc') { + if (-r $file) { + warn "Loading defaults from $file\n"; + open my $FILE, '<', $file + or die "Could not open $file for reading\n"; + while (my $line = <$FILE>){ + chomp $line; + push @defaults, split " ", $line; + } + close $FILE or die "Could not close open file: $file\n"; + } + } + return @defaults; +} + +#===== SUBROUTINE =========================================================== +# Name : usage() +# Purpose : print program usage message and exit +# Parameters: $msg => string to prepend to the usage message +# Returns : n/a +# Throws : n/a +# Comments : if 'msg' is given, then exit with non-zero status +#============================================================================ +sub usage { + my ($msg) = @_; + + if ($msg) { + print "$ProgramName: $msg\n\n"; + } + + print <<"EOT"; +$ProgramName (GNU Stow) version $Stow::VERSION + +SYNOPSIS: + + $ProgramName [OPTION ...] [-D|-S|-R] PACKAGE ... [-D|-S|-R] PACKAGE ... + +OPTIONS: + + -d DIR, --dir=DIR Set stow dir to DIR (default is current dir) + -t DIR, --target=DIR Set target to DIR (default is parent of stow dir) + + -S, --stow Stow the package names that follow this option + -D, --delete Unstow the package names that follow this option + -R, --restow Restow (like stow -D followed by stow -S) + + --ignore=REGEX Ignore files ending in this Perl regex + --defer=REGEX Don't stow files beginning with this Perl regex + if the file is already stowed to another package + --override=REGEX Force stowing files beginning with this Perl regex + if the file is already stowed to another package + --adopt (Use with care!) Import existing files into stow package + from target. Please read docs before using. + -p, --compat Use legacy algorithm for unstowing + + -n, --no, --simulate Do not actually make any filesystem changes + -v, --verbose[=N] Increase verbosity (levels are 0,1,2,3; + -v or --verbose adds 1; --verbose=N sets level) + -V, --version Show stow version number + -h, --help Show this help + +Report bugs to: bug-stow\@gnu.org +Stow home page: <http://www.gnu.org/software/stow/> +General help using GNU software: <http://www.gnu.org/gethelp/> +EOT + exit defined $msg ? 1 : 0; +} + +sub version { + print "$ProgramName (GNU Stow) version $Stow::VERSION\n"; + exit 0; +} + +1; # This file is required by t/stow.t + +# Local variables: +# mode: perl +# cperl-indent-level: 4 +# end: +# vim: ft=perl diff --git a/lib-src/tputfs.sh b/lib-src/tputfs.sh new file mode 100755 index 00000000..ab3c3c3a --- /dev/null +++ b/lib-src/tputfs.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +# shell scripting functions using tput + +status () +{ + echo -n "[" + test "$TERM" = "dumb" || tput setaf 3 + echo -n $(basename $0) + test "$TERM" = "dumb" || tput setaf 7 + echo -n "] " + test "$TERM" = "dumb" || tput bold + echo $@ + test "$TERM" = "dumb" || tput sgr0 +} |