summaryrefslogtreecommitdiff
path: root/lib-src
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2022-11-11 23:32:12 -0700
committerSean Whitton <spwhitton@spwhitton.name>2022-11-12 11:23:53 -0700
commitf223f38fcab3c94402603d1fadb2d6fa0ac3d05a (patch)
tree8036d67f8e96d335368d8b1ff19d3c98770fd754 /lib-src
parent74585ec4711667c76ecdad7eb53590cb912501ba (diff)
downloaddotfiles-f223f38fcab3c94402603d1fadb2d6fa0ac3d05a.tar.gz
GNU Stow -> hstow, and follow-up tidying & simplifications
Diffstat (limited to 'lib-src')
-rw-r--r--lib-src/mr/config175
-rw-r--r--lib-src/mr/stow273
-rwxr-xr-xlib-src/stow/chkstow113
-rwxr-xr-xlib-src/stow/stow665
4 files changed, 25 insertions, 1201 deletions
diff --git a/lib-src/mr/config b/lib-src/mr/config
index 06af1b6e..9cd84fe2 100644
--- a/lib-src/mr/config
+++ b/lib-src/mr/config
@@ -23,6 +23,11 @@ git_isclean = git is-clean
git_clean = git clean -xdff
+stow = hstow stow "$MR_REPO"
+unstow = hstow unstow "$MR_REPO"
+restow = hstow restow "$MR_REPO"
+adopt = hstow adopt "$MR_REPO"
+
# --- Plugin for dgit repos
# actually shipped with upstream mr, but use an include command that
@@ -40,17 +45,6 @@ include = cat ~/src/dotfiles/lib-src/mr/dgit
# after we include the dgit lib, which also redefines git_update)
git_update = git pull-safe
-# --- Adam Spiers' plugin for managing dotfile symlinks with mr
-
-# actually shipped with upstream mr, but use an include command that
-# will always work
-include =
- # stow is not available on Windows
- if [ -e "$HOME/src/dotfiles/lib-src/mr/stow" ] \
- && ! [ "$(perl -e 'print $^O')" = "msys" ]; then
- cat "$HOME/src/dotfiles/lib-src/mr/stow"
- fi
-
# --- joeyh's code for specifying what machine we're on for repo skip
# --- tests, plus my code for detecting Git-on-Windows
@@ -106,66 +100,6 @@ lib =
# --- standard procedures
lib =
- homedir_mkdirs() {
- (
- cd $HOME
- mkdir -p \
- .ssh \
- tmp \
- src \
- lib \
- mnt \
- local/mutt \
- local/src \
- local/bin \
- local/big \
- local/lib \
- local/log \
- local/pub \
- local/tmp \
- local/auth \
- local/info
- # lib/athena \
- # lib/backup \
- # local/anacron/spool \
- chmod 700 local/auth
- # [ -L "src/build-area" ] || ln -s -T /tmp/debuild src/build-area
- [ -e "Downloads" ] || ln -s tmp Downloads
- # clean up after additions to .stow-local-ignore
- find bin lib -type l 2>/dev/null | while read -r link; do
- if readlink "$link" | grep --quiet "^[../]*/.STOW/"; then
- rm "$link"
- fi
- done
- # cleanup some old dirs if they're empty
- find \
- bin \
- lib/aid \
- lib/backup \
- lib/perl5 \
- lib/hooks \
- lib/athena \
- lib/bins \
- lib/img \
- lib/mr \
- lib/src \
- local/anacron/spool \
- -type d -empty -delete 2>/dev/null ||:
- )
- }
- # specify files that should automatically be adopted because
- # programs convert them from symlinks to regular files. Arguments
- # to this function should be paths relative to the stow target
- # (usually $HOME)
- always_adopt () {
- for f in $@; do
- if ! [ -L "$STOW_TARGET/$f" ]; then
- # ignore errors; if it doesn't work, the user will
- # have to fix up manually
- mv 2>/dev/null "$STOW_TARGET/$f" "$MR_REPO/$f" || true
- fi
- done
- }
# export plain text Org agenda in post_ hooks of ~/doc repo (not currently used)
export_org_agenda () {
if on athena; then
@@ -179,72 +113,18 @@ lib =
# --- primary dotfiles repository
-# TODO we need to unstow before switching branches, and stow
-# afterwards, or else do some sort of automatic cleanup of dangling
-# symlinks before a restow, so a broken situation is easy to fix?
-# Also see kill-broken-stowed-symlink() in .bashrc.
-#
-# Note that the scan is expensive. So actually we probably don't want
-# the cleanup to happen automatically. Also, it should exclude
-# lib/annex, src/*/ (but not src/) because I don't stow files into
-# those dirs.
-#
-# Maybe just run it as part of sysmaint
-#
-# Hmm. Situation is not as bad as I thought. stow manages to clean
-# up quite a few of the symlinks. So running it as part of sysmaint
-# seems like a sufficient fix
-
[src/dotfiles]
checkout = git clone https://git.spwhitton.name/dotfiles.git dotfiles
-stowable = true
-# we have a script to update master, and all other branches should
-# only be checked out and committed to on a single host
+# We have a script to update master, and all other branches should be checked
+# out and committed to on only a single host, so no need to pull them, and
+# they'll always be rebaseable.
update = git dotfiles-update-master
-push = git push origin master
-# use `git dotfiles-rebase` instead
-# rebase =
-# # usual rebasing pattern. Per dotfiles repo policy (excluding
-# # win32 case), the branch being rebased will always be rebaseable
-# # on master, since it is only checked out and committed to on this
-# # host
-# branch="$(git rev-parse --abbrev-ref HEAD)"
-# hostname="$(hostname -s)"
-# if [ "$branch" = "win32" -o "$branch" = "$hostname" -o "$branch" = "develacc-$hostname" ]; then
-# git rebase master
-# fi
-fixups =
- # Use a rebase workflow as I'm the only committer
- git config pull.rebase true
- # Pushing and pulling are always done explicitly
- for head in $(git for-each-ref --format='%(refname)' refs/heads/); do
- branch=$(echo "$head" | cut -d/ -f3)
- git branch --unset-upstream "$branch" 2>/dev/null || true
- done
- git config push.default nothing
- # this is just for M-x magit-status
- git config remote.pushDefault origin
- #
- homedir_mkdirs
- chmod -Rf u+rwX,go= $HOME/local/auth/* || true
- # eventually move the following two lines from fixups to post_checkout
- install-git-hooks dotfiles
- git config user.signingkey 8DC2487E51ABDD90B5C4753F0F56D0553B6D411B
- # eventually drop this
- rm -f .git/hooks/post-checkout{,_01gpgsign}
-
-# clean-ups so that initial stow will be successful
-pre_stow =
- homedir_mkdirs
- $HOME/src/dotfiles/bin/unskel
- # these will often end up created, e.g. by insinuate-dotfiles script
- rm -f $HOME/.gnupg/{gpg.conf,gpg-agent.conf,dirmngr.conf,.gpg-v21-migrated}
-
-# this file frequently gets desymlinked
-pre_unstow_append = always_adopt .config/mimeapps.list
-pre_restow_append = always_adopt .config/mimeapps.list
-pre_stow_append = always_adopt .config/mimeapps.list
-pre_update_append = always_adopt .config/mimeapps.list
+push = git dotfiles-rebase
+# Restowing is expensive, and most dangling symlinks into ~/.STOW do no harm,
+# so we leave it to be run manually -- bstraph stows but does not restow.
+# Possibly restowing could be done by locmaint, or we could have hstow skip
+# annex/ and src/, into which I don't stow anything.
+fixups = bstraph
# --- private dotfiles repositories
@@ -252,7 +132,9 @@ pre_update_append = always_adopt .config/mimeapps.list
checkout = git clone athenag:libpriv.git priv
update = git annex sync --content cloud origin
push = git annex sync --content cloud origin
-stowable = true
+post_update =
+ hstow stow ~/lib/priv
+ load-trustdb
sync = mr autoci && git annex sync --no-commit --content cloud origin
skip = lazy
@@ -261,6 +143,11 @@ post_checkout =
git annex init
git annex enableremote cloud
git annex group . backup
+ # Delete any pubring.kbx created by INSINUATE-DOTFILES Consfigurator
+ # property / 'insinuate-dotfiles' shell script: don't want to adopt it.
+ rm -f ~/.gnupg/pubring.kbx
+ hstow stow ~/lib/priv
+ load-trustdb
fixups =
chmod 600 .passwddb.pet \
@@ -282,21 +169,18 @@ fixups =
git config mrrepo.review-unused false
autoci =
+ hstow stow ~/lib/priv # to perform adoptions
git annex add .passwddb.pet .labbook.gpg .gnupg/pubring.kbx
git commit -a -m \
"auto passwddb, pubring and labbook commit on $(hostname -s)" || true
pre_update = mr autoci
-# since dotfiles repo also stows into ~/.gnupg, and athpriv repo stows
-# into ~/.duply, make the dirs first
-pre_stow = homedir_mkdirs
-post_stow = load-trustdb
-
[src/athpriv]
checkout = git clone demeterp:athpriv athpriv
pre_update = on athena || git annex sync origin athenah
pre_push = on athena || git annex sync --content origin athenah
-stowable = true
+post_update = hstow stow ~/src/athpriv
+post_checkout = hstow stow ~/src/athpriv
skip = ! mine
post_checkout =
@@ -321,15 +205,6 @@ autoci =
git annex add News/*
git commit News -m"auto commit of Gnus score files" ||:
-# since priv repo also stows into ~/.duply, make the dir first
-pre_stow = homedir_mkdirs
-
-# r2e always desymlinks this file
-pre_stow_append = always_adopt .config/rss2email.cfg
-pre_unstow_append = always_adopt .config/rss2email.cfg
-pre_restow_append = always_adopt .config/rss2email.cfg
-pre_update_append = always_adopt .config/rss2email.cfg
-
# --- hosts configuration
[src/propellor]
diff --git a/lib-src/mr/stow b/lib-src/mr/stow
deleted file mode 100644
index 254bd0c3..00000000
--- a/lib-src/mr/stow
+++ /dev/null
@@ -1,273 +0,0 @@
-# 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
deleted file mode 100755
index a74d1b90..00000000
--- a/lib-src/stow/chkstow
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/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
deleted file mode 100755
index b94dc88d..00000000
--- a/lib-src/stow/stow
+++ /dev/null
@@ -1,665 +0,0 @@
-#!/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