summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Mike Burns <mike@mike-burns.com>2013-08-11 17:29:01 +0200
committerGravatar Mike Burns <mike@mike-burns.com>2013-08-11 21:18:53 +0200
commit8d7f6c94a3458328b339b6582592b6c1fecec950 (patch)
treeff9ea4a2a83fafb910d0c604435d648bf01cedbf
parent0fbc27dbe296e03b4001586a7e29780328cbc657 (diff)
Add the COPY_ALWAYS option
The suite now honors the `COPY_ALWAYS` option in rcrc(5). This can be set to a space-separated list of file globs. Any file matching a glob is copied instead of symlinked. This is handy both for secure programs (`netrc`, `ssh/id_*`) and for programs that oddly re-write files (`weechat/*`). To always copy everything, use the `*` glob. This is reflected throughout the suite as follows: * lsrc now has a `-F` option which shows a symbol to indicate whether it is a symlink (`@`) or a copy (`X`). * rcdn only removes symlinks unless the file under question matches a `COPY_ALWAYS` glob, in which case it is removed regardless of whether it is a symlink. * rcup will copy instead of symlinking any file that matches any `COPY_ALWAYS` glob.
-rw-r--r--NEWS.md.in4
-rwxr-xr-xbin/lsrc96
-rwxr-xr-xbin/rcdn15
-rwxr-xr-xbin/rcup47
-rw-r--r--man/lsrc.121
-rw-r--r--man/rcdn.113
-rw-r--r--man/rcrc.57
-rw-r--r--man/rcup.18
-rw-r--r--share/rcm.sh.in4
9 files changed, 173 insertions, 42 deletions
diff --git a/NEWS.md.in b/NEWS.md.in
index 4c4ebb9..058971e 100644
--- a/NEWS.md.in
+++ b/NEWS.md.in
@@ -1,7 +1,9 @@
rcm (@PACKAGE_VERSION@) unstable; urgency=low
- * Exclusion and inclusion with -e and -I.
+ * Exclusion and inclusion with -x and -I.
* Copy instead of symlink with -C.
+ * Always copy the files listed in COPY_ALWAYS.
+ * Show whether the file is a copy or symlink using lsrc -F.
-- Mike Burns <mburns@thoughtbot.com> Mon, 05 Aug 2013 16:43:33 +0200
diff --git a/bin/lsrc b/bin/lsrc
index 880bf05..f7f4826 100755
--- a/bin/lsrc
+++ b/bin/lsrc
@@ -29,37 +29,88 @@ build_path() {
fi
}
-link_dir() {
+file_join() {
+ local result=
+
+ for file; do
+ if [ "x$file" != "x." ]; then
+ if [ "x$result" = "x" ]; then
+ result=$file
+ else
+ result="$result/$file"
+ fi
+ fi
+ done
+
+ echo $result
+}
+
+show_dir() {
local dir=$1
local dest_dir=$2
local dotfiles_dir=$3
- local dotted=$4
- local exclude_file_globs="$5"
- local include_file_globs="$6"
+ local dotfiles_subdir=$4
+ local dotted=$5
+ local exclude_file_globs="$6"
+ local include_file_globs="$7"
local dest_path=`build_path $dest_dir $dir $dotted`
- $DEBUG "link_dir $1 $2 $3 $4 $5 $6"
+ $DEBUG "show_dir $1 $2 $3 $4 $5 $6 $7"
$VERBOSE "recurring on $dest_path"
pushdir $dir
for f in *; do
$DEBUG "handling the file $f"
- handle_file $f $dest_path $dotfiles_dir/$dir 1 "$exclude_file_globs" "$include_file_globs"
+ next_dir=`file_join $dotfiles_subdir $dir`
+ handle_file $f $dest_path $dotfiles_dir $next_dir 1 "$exclude_file_globs" "$include_file_globs"
done
popdir
}
-link_file() {
+sigil_for() {
+ local file=$1
+ local copy_always=0
+
+ for copy_file in $COPY_ALWAYS; do
+ $DEBUG "copy_file: $copy_file"
+ $DEBUG "file: $file"
+
+ case $file in
+ $copy_file)
+ copy_always=1
+ break
+ ;;
+ esac
+ done
+
+ if [ $copy_always -eq 1 ]; then
+ echo 'X'
+ else
+ echo '@'
+ fi
+}
+
+show_file() {
local file=$1
local dest_dir=$2
local dotfiles_dir=$3
- local dotted=$4
+ local dotfiles_subdir=$4
+ local dotted=$5
local dest_file=`build_path $dest_dir $file $dotted`
if echo $DEST_STACK | grep -vq ":$dest_file"; then
DEST_STACK="$DEST_STACK:$dest_file"
- $PRINT $dest_file:$dotfiles_dir/$file
+ src_file=`file_join $dotfiles_subdir $file`
+ abs_src_file=`file_join $dotfiles_dir $src_file`
+ output=$dest_file:$abs_src_file
+
+ if [ $SHOW_SIGILS -eq 1 ]; then
+ sigil=`sigil_for $src_file`
+ output="$output:$sigil"
+ fi
+
+ $PRINT $output
fi
}
@@ -67,20 +118,21 @@ handle_file() {
local file=$1
local dest_dir=$2
local dotfiles_dir=$3
- local dotted=$4
- local exclude_file_globs="$5"
- local include_file_globs="$6"
+ local dotfiles_subdir=$4
+ local dotted=$5
+ local exclude_file_globs="$6"
+ local include_file_globs="$7"
- $DEBUG "handle_file $1 $2 $3 $4 $5 $6"
+ $DEBUG "handle_file $1 $2 $3 $4 $5 $6 $7"
if [ ! -e $file ]; then
$VERBOSE "skipping non-existent file $file"
elif is_excluded $file "$exclude_file_globs" "$include_file_globs"; then
$VERBOSE "skipping excluded file $file"
elif [ -d $file ]; then
- link_dir $file $dest_dir $dotfiles_dir $dotted "$exclude_file_globs" "$include_file_globs"
+ show_dir $file $dest_dir $dotfiles_dir $dotfiles_subdir $dotted "$exclude_file_globs" "$include_file_globs"
else
- link_file $file $dest_dir $dotfiles_dir $dotted
+ show_file $file $dest_dir $dotfiles_dir $dotfiles_subdir $dotted
fi
}
@@ -147,12 +199,14 @@ handle_command_line() {
local arg_tags=
local verbosity=0
local version=0
+ local show_sigils=0
local dotfiles_dirs=
local excludes=
local includes=
- while getopts VqvI:x:t:d: opt; do
+ while getopts FVqvI:x:t:d: opt; do
case "$opt" in
+ F) show_sigils=1;;
I) includes="$includes $OPTARG";;
t) arg_tags="$arg_tags $OPTARG";;
v) verbosity=$(($verbosity + 1));;
@@ -165,6 +219,7 @@ handle_command_line() {
shift $(($OPTIND-1))
handle_common_flags lsrc $version $verbosity
+ SHOW_SIGILS=$show_sigils
TAGS=${arg_tags:-$TAGS}
DOTFILES_DIRS=${dotfiles_dirs:-$DOTFILES_DIRS}
EXCLUDES=${excludes:-$EXCLUDES}
@@ -186,6 +241,9 @@ handle_command_line $*
: ${DOTFILES_DIRS:=$DOTFILES_DIRS $DEFAULT_DOTFILES_DIR}
$DEBUG "DOTFILES_DIRS: $DOTFILES_DIRS"
+: ${COPY_ALWAYS:=""}
+$DEBUG "COPY_ALWAYS: $COPY_ALWAYS"
+
for DOTFILES_DIR in $DOTFILES_DIRS; do
if is_relative $DOTFILES_DIR; then
DOTFILES_DIR=$PWD/$DOTFILES_DIR
@@ -208,7 +266,7 @@ for DOTFILES_DIR in $DOTFILES_DIRS; do
continue
fi
- handle_file $file $DEST_DIR $DOTFILES_DIR 0 "$exclude_file_globs" "$include_file_globs"
+ handle_file $file $DEST_DIR $DOTFILES_DIR . 0 "$exclude_file_globs" "$include_file_globs"
done
cd $DOTFILES_DIR
@@ -217,7 +275,7 @@ for DOTFILES_DIR in $DOTFILES_DIRS; do
if [ -d $host_files ]; then
pushdir `basename $host_files`
for file in ${FILES:-*}; do
- handle_file $file $DEST_DIR $host_files 0 "$exclude_file_globs" "$include_file_globs"
+ handle_file $file $DEST_DIR $host_files . 0 "$exclude_file_globs" "$include_file_globs"
done
popdir
fi
@@ -229,7 +287,7 @@ for DOTFILES_DIR in $DOTFILES_DIRS; do
pushdir `basename tag-$tag`
for file in ${FILES:-*}; do
$DEBUG "TAG: $tag, exclude_file_globs: $exclude_file_globs"
- handle_file $file $DEST_DIR $DOTFILES_DIR/tag-$tag 0 "$exclude_file_globs" "$include_file_globs"
+ handle_file $file $DEST_DIR $DOTFILES_DIR/tag-$tag . 0 "$exclude_file_globs" "$include_file_globs"
done
popdir
fi
diff --git a/bin/rcdn b/bin/rcdn
index df882e8..d809796 100755
--- a/bin/rcdn
+++ b/bin/rcdn
@@ -6,10 +6,11 @@
remove_link() {
local dest=$1
local original=$2
+ local sigil=$3
if [ x$dest = "x/" ]; then
$VERBOSE "not a symlink, skipping: $original"
- elif [ -L $dest ]; then
+ elif [ -L $dest -o "x$sigil" = "xX" ]; then
$RM -rf $dest
rmdir -p `dirname $original` 2>/dev/null
else
@@ -62,6 +63,8 @@ handle_command_line() {
$DEBUG "LS_ARGS: $LS_ARGS"
}
+LS_ARGS=-F
+
if [ -e $HOME/.rcrc ]; then
. $HOME/.rcrc
fi
@@ -69,8 +72,12 @@ fi
handle_command_line $*
for dest_and_src in `lsrc $LS_ARGS`; do
- dest=`echo $dest_and_src | sed 's/:.*//'`
- src=`echo $dest_and_src | sed 's/.*://'`
+ saved_ifs=$IFS
+ IFS=:
+ set $dest_and_src
+ IFS=$saved_ifs
+ dest=$1
+ sigil=$3
- remove_link $dest $dest
+ remove_link $dest $dest $sigil
done
diff --git a/bin/rcup b/bin/rcup
index af7a012..5f51192 100755
--- a/bin/rcup
+++ b/bin/rcup
@@ -3,25 +3,40 @@
: ${RCM_LIB:=`dirname $0`/../share/rcm}
. $RCM_LIB/rcm.sh
+link_or_copy() {
+ $DEBUG "link_or_copy $1"
+ local sigil=$1
+
+ if [ "x$sigil" = "xX" ]; then
+ echo $CP
+ else
+ echo $LN
+ fi
+}
+
link_file() {
local src=$1
local dest=$2
+ local sigil=$3
if [ -h $dest ]; then
$RM -f $dest
fi
- $LN $src $dest
+ action=`link_or_copy $sigil`
+ $DEBUG "$action $src $dest"
+ $action $src $dest
}
replace_file() {
local src=$1
local dest=$2
+ local sigil=$3
- $DEBUG replace_file $1 $2
+ $DEBUG replace_file $1 $2 $3
$RM -rf $dest
- link_file $src $dest
+ link_file $src $dest $sigil
}
is_nested() {
@@ -44,28 +59,29 @@ handle_dir() {
handle_file() {
local src=$1
local dest=$2
+ local sigil=$3
- $DEBUG handle_file $1 $2
+ $DEBUG handle_file $1 $2 $3
if [ -e "$dest" ]; then
if is_identical $src $dest; then
$VERBOSE "identical $dest"
elif [ $REPLACE_ALL -eq 1 ]; then
- replace_file $src $dest
+ replace_file $src $dest $sigil
else
$PROMPT "overwrite ${dest}? [ynaq]"
read overwrite
case $overwrite in
a) REPLACE_ALL=1
- replace_file $src $dest
+ replace_file $src $dest $sigil
;;
- y) replace_file $src $dest ;;
+ y) replace_file $src $dest $sigil ;;
q) exit 1 ;;
*) $VERBOSE "skipping $dest" ;;
esac
fi
else
- link_file $src $dest
+ link_file $src $dest $sigil
fi
}
@@ -97,7 +113,7 @@ handle_command_line() {
shift $(($OPTIND-1))
if [ $always_copy -eq 1 ]; then
- LN=cp
+ LN=$CP
fi
handle_common_flags rcup $version $verbosity
@@ -123,6 +139,8 @@ handle_command_line() {
$DEBUG "LS_ARGS: $LS_ARGS"
}
+LS_ARGS=-F
+
if [ -e $HOME/.rcrc ]; then
. $HOME/.rcrc
fi
@@ -130,12 +148,17 @@ fi
handle_command_line $*
for dest_and_src in `lsrc $LS_ARGS`; do
- dest=`echo $dest_and_src | sed 's/:.*//'`
- src=`echo $dest_and_src | sed 's/.*://'`
+ saved_ifs=$IFS
+ IFS=:
+ set $dest_and_src
+ IFS=$saved_ifs
+ dest=$1
+ src=$2
+ sigil=$3
if is_nested $dest; then
handle_dir $dest
fi
- handle_file $src $dest
+ handle_file $src $dest $sigil
done
diff --git a/man/lsrc.1 b/man/lsrc.1
index f8e675c..b733873 100644
--- a/man/lsrc.1
+++ b/man/lsrc.1
@@ -6,7 +6,7 @@
.Nd show configuration files
.Sh SYNOPSIS
.Nm lsrc
-.Op Fl vq
+.Op Fl Fvq
.Op Fl d Ar dir
.Op Fl I Ar excl_pat
.Op Fl t Ar tag
@@ -25,12 +25,21 @@ section, for details on the directory layout.
It supports these options:
.
.Bl -tag
-.It Fl t Ar TAG
-list dotfiles according to TAG
-.
.It Fl d Ar DIR
list dotfiles from the DIR. This can be specified multiple times.
.
+.It Fl F
+show symbols next to each file indicating status information. Supported
+symbols are
+.Li @
+which indicates that the file is a symlink, and
+.Li X
+to indicate that the file is a copy. More details on copied files can be
+found in
+.Xr rcrc 5 ,
+under the documentation about
+.Va COPY_ALWAYS .
+.
.It Fl I Ar excl_pat
include the files that match the given pattern. This is applied after
any
@@ -40,6 +49,10 @@ options. It uses the same pattern language as
more details are in the
.Sx EXCLUDE PATTERN
section.
+.
+.It Fl t Ar TAG
+list dotfiles according to TAG
+.
.It Fl v
increase verbosity. This can be repeated for extra verbosity.
.
diff --git a/man/rcdn.1 b/man/rcdn.1
index 2981bbc..6c641c9 100644
--- a/man/rcdn.1
+++ b/man/rcdn.1
@@ -13,13 +13,24 @@
.Op Fl x Ar excl_pat
.Op Ar files ...
.Sh DESCRIPTION
-This program will remove all the rc file symlinks that the
+This program will remove all the rc files that the
.Xr rcm 7
suite knows about. This can be further controlled with the
.Fl t
and
.Fl d
flags.
+.Pp
+The files that are removed are symlinks. However, the
+.Va COPY_ALWAYS
+setting in
+.Xr rcrc 5
+modifies this. If a rc file is not a symlink but an ancestor directory
+is, that directory is removed. If a rc file is not a symlink but is
+listed in
+.Va COPY_ALWAYS
+the file is removed.
+.
.Bl -tag
.It Fl d Ar DIR
remove rc files from the
diff --git a/man/rcrc.5 b/man/rcrc.5
index f14eecf..c7f300a 100644
--- a/man/rcrc.5
+++ b/man/rcrc.5
@@ -28,22 +28,29 @@ programs.
.Pp
It supports these variables:
.Bl -tag
+.It Va COPY_ALWAYS
+always copy files that match the listed globs, never symlink them.
+.
.It Va DOTFILES_DIRS
the source directories for dotfiles. The first in the list is the
canonical source. The default value is
.Li ~/.dotfiles
+.
.It Va EXCLUDES
a space-separated list of exclude patterns. Exclude patterns are
explained in detail in
.Xr lsrc 1
under the section
.Sx EXCLUDE PATTERN .
+.
.It Va TAGS
the default tags.
.El
.Sh FILES
.Pa ~/.rcrc
.Sh EXAMPLES
+.Dl COPY_ALWAYS="ssh/id_* weechat/* netrc"
+.Dl COPY_ALWAYS="*"
.Dl DOTFILES_DIRS="/home/mike/.dotfiles /usr/share/dotfiles"
.Dl EXCLUDES="irbrc *:*emacs* dotfiles:python*"
.Dl TAGS="freebsd development email git laptop gmail notmuch"
diff --git a/man/rcup.1 b/man/rcup.1
index 0b265ab..99fa364 100644
--- a/man/rcup.1
+++ b/man/rcup.1
@@ -76,7 +76,13 @@ will be installed into
\&.
.Pp
Files are installed as symlinks. Directories are installed by making
-directories.
+directories. The
+.Fl C
+flag causes files to be installed as copies instead of symlinks. The
+.Va COPY_ALWAYS
+option in
+.Xr rcrc 5
+can be used to list files that must only be copied.
.Pp
Two meta files are supported: host-specific files and tagged files.
.Pp
diff --git a/share/rcm.sh.in b/share/rcm.sh.in
index 186f3a2..ea35e47 100644
--- a/share/rcm.sh.in
+++ b/share/rcm.sh.in
@@ -10,6 +10,7 @@ ERROR=echo_error
VERBOSE=:
MKDIR=mkdir
LN="ln -s"
+CP=cp
RM=rm
DEFAULT_DOTFILES_DIR=$HOME/.dotfiles
MV=mv
@@ -61,6 +62,7 @@ handle_common_flags() {
MV="$MV -v"
RM="$RM -v"
LN="$LN -v"
+ CP="$CP -v"
INSTALL="$INSTALL -vv"
elif [ $verbosity -eq 1 ]; then
DEBUG=:
@@ -69,6 +71,7 @@ handle_common_flags() {
MV="$MV -v"
RM="$RM -v"
LN="$LN -v"
+ CP="$CP -v"
INSTALL="$INSTALL -v"
elif [ $verbosity -eq 0 ]; then
DEBUG=:
@@ -77,6 +80,7 @@ handle_common_flags() {
MV="$MV -v"
RM="$RM -v"
LN="$LN -v"
+ CP="$CP -v"
else
DEBUG=:
VERBOSE=: