Subject: pkg_stow
To: NetBSD Packages Technical Discussion List <tech-pkg@NetBSD.ORG>
From: Johnny C. Lam <jlam@pkgsrc.org>
List: tech-pkg
Date: 03/07/2007 16:52:36
This is a multi-part message in MIME format.
--------------040605050304020809030501
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
I've attached a script called "pkg_stow" that does roughly what GNU stow
does, but implemented using system tools. Probably only works right now
on NetBSD because I'm using the "readlink" command to compute the target
of a symlink. There's probably a way to use "ls" to approximate it, but
that's not too important to me right now. I'm not looking for
feedback... just thought I'd share something I was working on with
others that might see a use for it.
Cheers,
-- Johnny Lam <jlam@pkgsrc.org>
--------------040605050304020809030501
Content-Type: text/plain;
name="pkg_stow"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="pkg_stow"
#!/bin/sh
#
# Copyright (c) 2007 Johnny C. Lam.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# 3. Neither the name of The NetBSD Foundation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
progname=pkg_stow
usage() {
echo 1>&2 "usage: pkg_stow [-Dv] destdir srcdir1 [srcdir2 ...]"
echo 1>&2 " -D Unstow instead of stow"
echo 1>&2 " -v Be verbose about actions taken"
echo 1>&2 " destdir Destination directory of stowed entries"
echo 1>&2 " srcdirN Source directory of entries to stow"
exit 1
}
stow_dir() {
local _src _dest _pointee
_src="$1"; _dest="$2"
if [ ! -e $_dest ]; then
$echo /bin/ln -s $_src $_dest
/bin/ln -s $_src $_dest
return 0
fi
if [ -d $_dest -a -h $_dest ]; then
_pointee=`/usr/bin/readlink $_dest`
case $_pointee in
$_src/*) return 0 ;;
esac
$echo /bin/rm $_dest
/bin/rm $_dest
$echo /bin/mkdir $_dest
/bin/mkdir $_dest
stow_dircontents $_pointee $_dest
fi
stow_dircontents $_src $_dest
}
stow_dircontents() {
local _src _dest
_src="$1"; _dest="$2"
/bin/ls -1a $_src |
while read _entry; do
if [ "$_entry" = "." -o "$_entry" = ".." ]; then
continue
elif [ -d $_src/$_entry -a ! -h $_src/$_entry ]; then
stow_dir $_src/$_entry $_dest/$_entry
else
$echo /bin/ln -s $_src/$_entry $_dest/$_entry
/bin/ln -s $_src/$_entry $_dest/$_entry
fi
done
}
opt_verbose=0
opt_delete=0
while [ $# -gt 0 ]; do
opt="$1"
case "$opt" in
-D|--delete) opt_delete=1; shift ;;
-v|--verbose) opt_verbose=1; shift ;;
--) shift; break ;;
-*) usage ;;
*) break ;;
esac
done
# We require at least two more arguments: the srcdir and the destdir.
[ $# -ge 2 ] || usage
case $opt_verbose in
1|yes) echo="echo" ;;
*) echo=: ;;
esac
# The first required argument is the destdir.
pwd=`/bin/pwd`
case "$1" in
/*) dest="$1" ;;
*) dest="$pwd/$1" ;;
esac
shift
# The remaining arguments are package directories to stow.
for srcdir; do
case "$srcdir" in
/*) src="$srcdir" ;;
*) src="$pwd/$srcdir" ;;
esac
case $opt_delete in
0|no)
if [ ! -d "$dest" ]; then
$echo /bin/mkdir -p $dest
/bin/mkdir -p $dest || exit 1
fi
stow_dircontents "$src" "$dest"
;;
1|yes)
/usr/bin/find "$dest" -type d | /usr/bin/sort -r |
/usr/bin/awk -v src="$src" -v dest="$dest" \
-v opt_verbose="$opt_verbose" '
{
# For each directory, inspect each symlink in the
# directory. If it points within $src, then remove
# it. If the remaining links point within the same
# parent directory, and the directory only contains
# those symlinks, then remove the symlinks and
# create a symlink to that parent directory in
# place of the existing directory.
#
dir = $0
# Count the number of entries in the directory (we
# substract two to deal with . and ..)
#
cmd = "/bin/ls -1a " dir " | /usr/bin/wc -l"
cmd | getline nentries
close(cmd)
nentries -= 2
# Keep track of whether this directory was already
# empty at the start. We purposely avoid removing
# these directories during tree-folding.
#
if (nentries == 0)
is_empty = 1
else
is_empty = 0
cmd = "/usr/bin/find " dir " -type l -maxdepth 1"
nparents = 0
nsymlinks = 0
while (cmd | getline entry) {
readlink_cmd = "/usr/bin/readlink " entry
readlink_cmd | getline pointee
if (match(pointee, "^" src "\/") != 0) {
rm_cmd = "/bin/rm " entry
if (opt_verbose) print rm_cmd
system(rm_cmd)
nentries--
continue
}
# Save the symlinks and their parents for
# later inspection to perform tree-folding.
#
parent = pointee; sub("\/[^\/]*$", "", parent)
parents[parent]++
if (parents[parent] == 1)
nparents++
symlinks[entry] = entry
nsymlinks++
}
close(cmd)
# Tree-fold
if ((dir != dest) && (is_empty == 0) && \
(nparents == 1)) {
if (nsymlinks == nentries) {
for (link in symlinks) {
cmd = "/bin/rm " link
if (opt_verbose) print cmd
system(cmd)
}
cmd = "rmdir " dir
if (opt_verbose) print cmd
system(cmd)
for (parent in parents) {
cmd = "ln -s " parent " " dir
if (opt_verbose) print cmd
system(cmd)
}
}
}
delete parents
delete symlinks
}'
;;
esac
done
--------------040605050304020809030501--