From 63b50643b0ffd287d0070e494625056a05081ce8 Mon Sep 17 00:00:00 2001 From: Mike Burns Date: Sun, 4 Aug 2013 10:35:14 -0400 Subject: Introduce exclusion patterns The lsrc(1), rcup(1), and rcdn(1) commands now take any number of `-e` flags, used to specify an exclusion pattern. This can also be controlled via rcrc(5), the `EXCLUDES` variable. An exclusion pattern specifies a file glob to skip. In the case of lsrc(1), any file matching the glob is not listed; in rcup(1) it is not symlinked; and in rcdn(1) it is not removed. The file glob can be preceded by the name of a dotfiles directory (separated from the file glob by a colon) to increase the specificity. Useful for: rcdn -e rcrc rcup -d work-dotfiles -e bashrc rcup -d ~/.dotfiles -d wife-dotfiles -d sys-dotfiles -e wife-dotfiles:tigrc --- bin/Makefile.in | 4 +-- bin/lsrc | 86 +++++++++++++++++++++++++++++++++++++++++++++---------- bin/rcdn | 7 ++++- bin/rcup | 7 ++++- man/Makefile.in | 4 +-- man/lsrc.1 | 44 ++++++++++++++++++++++++---- man/rcdn.1 | 21 +++++++++----- man/rcm.7 | 8 ++++-- man/rcrc.5 | 18 ++++++++---- man/rcup.1 | 27 ++++++++++------- share/Makefile.in | 4 +-- share/rcm.sh.in | 10 ++++++- 12 files changed, 184 insertions(+), 56 deletions(-) diff --git a/bin/Makefile.in b/bin/Makefile.in index cdf3377..42d9875 100644 --- a/bin/Makefile.in +++ b/bin/Makefile.in @@ -180,9 +180,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign bin/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu bin/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign bin/Makefile + $(AUTOMAKE) --gnu bin/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/bin/lsrc b/bin/lsrc index a7eca40..6267f03 100755 --- a/bin/lsrc +++ b/bin/lsrc @@ -34,13 +34,16 @@ link_dir() { local dest_dir=$2 local dotfiles_dir=$3 local dotted=$4 + local exclude_file_globs="$5" local dest_path=`build_path $dest_dir $dir $dotted` + $DEBUG "link_dir $1 $2 $3 $4 $5" + $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 + handle_file $f $dest_path $dotfiles_dir/$dir 1 "$exclude_file_globs" done popdir } @@ -64,21 +67,68 @@ handle_file() { local dest_dir=$2 local dotfiles_dir=$3 local dotted=$4 + local exclude_file_globs="$5" - $DEBUG handle_file $1 $2 $3 $4 + $DEBUG "handle_file $1 $2 $3 $4 $5" if [ ! -e $file ]; then $VERBOSE "skipping non-existent file $file" + elif is_excluded $file "$exclude_file_globs"; then + $VERBOSE "skipping excluded file $file" elif [ -d $file ]; then - link_dir $file $dest_dir $dotfiles_dir $dotted + link_dir $file $dest_dir $dotfiles_dir $dotted "$exclude_file_globs" else - dest_file=`build_path $dest_dir $file $dotted` link_file $file $dest_dir $dotfiles_dir $dotted fi } -metafile() { - [ x$2 = 'xhost-' -o x$3 = 'xtag-' ] +is_metafile() { + host_portion=`echo $1 | sed -e 's/host-.*/host-/'` + tag_portion=`echo $1 | sed -e 's/tag-.*/tag-/'` + + [ x$host_portion = 'xhost-' -o x$tag_portion = 'xtag-' ] +} + +dotfiles_dir_excludes() { + local dotfiles_dir=$1 + + $DEBUG "dotfiles_dir_excludes $dotfiles_dir" + $DEBUG " with EXCLUDES: $EXCLUDES" + + for exclude in $EXCLUDES; do + if echo $exclude | grep -q :; then + dotfiles_dir_pat=`echo $exclude | sed 's/:.*//'` + file_glob=`echo $exclude | sed 's/.*://'` + + if [ "x$dotfiles_dir_pat" != "x*" ] && is_relative $dotfiles_dir_pat; then + dotfiles_dir_pat=$PWD/$dotfiles_dir_pat + fi + + if [ "x$dotfiles_dir_pat" = "x*" -o "x$dotfiles_dir_pat" = "x$dotfiles_dir" ]; then + echo $file_glob + fi + else + echo $exclude + fi + done +} + +is_excluded() { + local file=$1 + local exclude_file_globs="$2" + + $DEBUG "is_excluded $file $exclude_file_globs" + + for file_glob in $exclude_file_globs; do + $DEBUG "file_glob: $file_glob" + $DEBUG "file: $file" + + case $file in + $file_glob) return 0;; + esac + done + + return 1 } handle_command_line() { @@ -86,9 +136,11 @@ handle_command_line() { local verbosity=0 local version=0 local dotfiles_dirs= + local excludes= - while getopts Vqvt:d: opt; do + while getopts Vqve:t:d: opt; do case "$opt" in + e) excludes="$excludes $OPTARG";; t) arg_tags="$arg_tags $OPTARG";; v) verbosity=$(($verbosity + 1));; q) verbosity=$(($verbosity - 1));; @@ -101,6 +153,7 @@ handle_command_line() { handle_common_flags lsrc $version $verbosity TAGS=${arg_tags:-$TAGS} DOTFILES_DIRS=${dotfiles_dirs:-$DOTFILES_DIRS} + EXCLUDES=${excludes:-$EXCLUDES} FILES=$@ $DEBUG "TAGS: $TAGS" @@ -119,8 +172,7 @@ handle_command_line $* $DEBUG "DOTFILES_DIRS: $DOTFILES_DIRS" for DOTFILES_DIR in $DOTFILES_DIRS; do - - if echo $DOTFILES_DIR | grep -vq '^/'; then + if is_relative $DOTFILES_DIR; then DOTFILES_DIR=$PWD/$DOTFILES_DIR fi @@ -129,15 +181,18 @@ for DOTFILES_DIR in $DOTFILES_DIRS; do continue fi + exclude_file_globs=`dotfiles_dir_excludes $DOTFILES_DIR` + $DEBUG "exclude_file_globs: $exclude_file_globs" + cd $DOTFILES_DIR DIR_STACK=":$DOTFILES_DIR" for file in ${FILES:-*}; do - host_portion=`echo $file | sed -e 's/host-.*/host-/'` - tag_portion=`echo $file | sed -e 's/tag-.*/tag-/'` - if ! metafile $file $host_portion $tag_portion; then - handle_file $file $DEST_DIR $DOTFILES_DIR 0 + if is_metafile $file; then + continue fi + + handle_file $file $DEST_DIR $DOTFILES_DIR 0 "$exclude_file_globs" done cd $DOTFILES_DIR @@ -146,7 +201,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 + handle_file $file $DEST_DIR $host_files 0 "$exclude_file_globs" done popdir fi @@ -157,7 +212,8 @@ for DOTFILES_DIR in $DOTFILES_DIRS; do if [ -d tag-$tag ]; then pushdir `basename tag-$tag` for file in ${FILES:-*}; do - handle_file $file $DEST_DIR $DOTFILES_DIR/tag-$tag 0 + $DEBUG "TAG: $tag, exclude_file_globs: $exclude_file_globs" + handle_file $file $DEST_DIR $DOTFILES_DIR/tag-$tag 0 "$exclude_file_globs" done popdir fi diff --git a/bin/rcdn b/bin/rcdn index 8695c63..66e064f 100755 --- a/bin/rcdn +++ b/bin/rcdn @@ -9,9 +9,11 @@ handle_command_line() { local version=0 local dotfiles_dirs= local files= + local excludes= - while getopts Vqvt:d: opt; do + while getopts Vqve:t:d: opt; do case "$opt" in + e) excludes="$excludes $OPTARG";; t) arg_tags="$arg_tags $OPTARG" ;; v) verbosity=$(($verbosity + 1));; q) verbosity=$(($verbosity - 1));; @@ -33,6 +35,9 @@ handle_command_line() { for dotfiles_dir in $dotfiles_dirs; do LS_ARGS="$LS_ARGS -d $dotfiles_dir" done + for exclude in $excludes; do + LS_ARGS="$LS_ARGS -e $exclude" + done LS_ARGS="$LS_ARGS $files" $DEBUG "LS_ARGS: $LS_ARGS" diff --git a/bin/rcup b/bin/rcup index 3aa5fa6..7c1cf16 100755 --- a/bin/rcup +++ b/bin/rcup @@ -76,11 +76,13 @@ handle_command_line() { local version=0 local dotfiles_dirs= local files= + local excludes= REPLACE_ALL=0 - while getopts Vqvfit:d: opt; do + while getopts Vqvfie:t:d: opt; do case "$opt" in + e) excludes="$excludes $OPTARG";; f) REPLACE_ALL=1 ;; i) REPLACE_ALL=0 ;; t) arg_tags="$arg_tags $OPTARG" ;; @@ -104,6 +106,9 @@ handle_command_line() { for dotfiles_dir in $dotfiles_dirs; do LS_ARGS="$LS_ARGS -d $dotfiles_dir" done + for exclude in $excludes; do + LS_ARGS="$LS_ARGS -e $exclude" + done LS_ARGS="$LS_ARGS $files" $DEBUG "LS_ARGS: $LS_ARGS" diff --git a/man/Makefile.in b/man/Makefile.in index ed1e0db..51cc9de 100644 --- a/man/Makefile.in +++ b/man/Makefile.in @@ -184,9 +184,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign man/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu man/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign man/Makefile + $(AUTOMAKE) --gnu man/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/man/lsrc.1 b/man/lsrc.1 index ff67a1f..863ceb8 100644 --- a/man/lsrc.1 +++ b/man/lsrc.1 @@ -9,6 +9,7 @@ .Op Fl vq .Op Fl t Ar tag .Op Fl d Ar dir +.Op Fl e Ar excl_pat .Op files ... .Sh DESCRIPTION This program lists all configuration files, both the sources in the @@ -23,21 +24,52 @@ section, for details on the directory layout. It supports these options: . .Bl -tag -.It Fl v -increase verbosity. This can be repeated for extra verbosity. -. -.It Fl q -decrease verbosity -. .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 e Ar excl_pat +Exclude the files that matches the given pattern. See +.Sx EXCLUDE PATTERN +for more details. This option can be repeated. +. +.It Fl v +increase verbosity. This can be repeated for extra verbosity. +. +.It Fl q +decrease verbosity +. .It Ar files ... only list the specified file(s) .El +.Sh EXCLUDE PATTERN +The exclude pattern specifies a colon-separated pair of dotfiles +directory and file glob. The dotfiles directory is optional and, if +omitted, defaults to +.Li * , +which is a special token that matches any dotfiles directory. The file +glob is relative to the dotfiles directory, ignoring meta directories. A +colon combines them. +. +.Pp +For example, to ignore all emacs-related items from the +.Pa thoughtbot-dotfiles +directory, use the exclude pattern: +.Pp +.Dl thoughtbot-dotfiles:*emacs* +.Pp +To ignore any +.Pa bash_profile +file, use the pattern: +.Pp +.Dl *:bash_profile +.Pp +Or more simply: +.Pp +.Dl bash_profile +.Pp .Sh FILES .Pa ~/.dotfiles .Pa ~/.rcrc diff --git a/man/rcdn.1 b/man/rcdn.1 index 65c9bf0..c6f8e9f 100644 --- a/man/rcdn.1 +++ b/man/rcdn.1 @@ -19,17 +19,24 @@ and .Fl d flags. .Bl -tag -.It Fl v -increase verbosity. This can be repeated for extra verbosity. +.It Fl d Ar DIR +remove rc files from the +.Ar DIR . +This can be specified multiple times. +.It Fl e Ar EXCL_PAT +do not remove rc files that match +.Ar EXCL_PAT . +This can be repeated with additional patterns. See +.Xr lsrc 1 , +.Sx EXCLUDE PATTERN , +for more details. .It Fl q decrease verbosity .It Fl t Ar TAG remove dotfiles according to .Ar TAG -.It Fl d Ar DIR -remove dotfiles from the -.Ar DIR . -This can be specified multiple times. +.It Fl v +increase verbosity. This can be repeated for extra verbosity. .It Ar files only remove the specified file(s) .El @@ -39,8 +46,8 @@ only remove the specified file(s) .Dl rcdn zshrc .Dl rcdn -t python .Dl rcdn -d ~/corporate-dotfiles +.Dl rcdn -e '*:.zshrc' .Sh FILES -.Pa ~/.dotfiles .Pa ~/.rcrc .Sh SEE ALSO .Xr lsrc 1 , diff --git a/man/rcm.7 b/man/rcm.7 index 69ea809..3a9f659 100644 --- a/man/rcm.7 +++ b/man/rcm.7 @@ -83,10 +83,9 @@ in the directory directory. This will cause a or .Pa ~/.Makefile symlink to be created in your home -directory. The best option here is to move that file outside of your -dotfiles directory or remove it entirely. +directory. Use an exclusion pattern to ignore these. .Pp -.Dl rm -f install Rakefile Makefile install.sh +.Dl rcup -e install -e Rakefile -e Makefile -e install.sh .Ss COMMON PROBLEM: DOTTED FILENAMES IN DOTFILES DIRECTORY A less common situation is for all the filenames in your dotfiles directory to be prefixed with a period. These files are skipped by the @@ -201,6 +200,9 @@ will take precedence over .Pp .Dl rcup -d .dotfiles -d marriage-dotfiles -d thoughtbot-dotfiles .Pp +An exclusion pattern can be tied to a specific dotfiles directory. +.Pp +.Dl rcup -d .dotfiles -d work-dotfiles -e 'work-dotfiles:powrc' . .Sh HOST-SPECIFIC DOTFILES You can also mark host-specific files. This will go by the hostname. The diff --git a/man/rcrc.5 b/man/rcrc.5 index c66e718..f14eecf 100644 --- a/man/rcrc.5 +++ b/man/rcrc.5 @@ -17,30 +17,36 @@ .Sh DESCRIPTION The rcm dotfile manager can be configured using a .Pa .rcrc -file in your home directory. The format is POSIX shell, and it is +file in your home directory. The format is POSIX shell. It is sourced in by the .Xr lsrc 1 , .Xr mkrc 1 , +.Xr rcdn 1 , and .Xr rcup 1 programs. .Pp It supports these variables: .Bl -tag -.It Va TAGS -the default tags used by -.Nm rcup .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 ~/.dotfiles .Pa ~/.rcrc .Sh EXAMPLES -.Dl TAGS="freebsd development email git laptop gmail notmuch" .Dl DOTFILES_DIRS="/home/mike/.dotfiles /usr/share/dotfiles" +.Dl EXCLUDES="irbrc *:*emacs* dotfiles:python*" +.Dl TAGS="freebsd development email git laptop gmail notmuch" .Sh SEE ALSO .Xr lsrc 1 , .Xr mkrc 1 , diff --git a/man/rcup.1 b/man/rcup.1 index a0921b2..16c818c 100644 --- a/man/rcup.1 +++ b/man/rcup.1 @@ -6,9 +6,9 @@ .Nd update and install dotfiles .Sh SYNOPSIS .Nm rcup -.Op Fl vqfi -.Op Fl t Ar tag +.Op Fl fiqv .Op Fl d Ar dir +.Op Fl t Ar tag .Op Ar files ... .Sh DESCRIPTION This is a program to update and install personal dotfiles. These @@ -23,25 +23,32 @@ for details on the directory layout. .Pp It supports these options: .Bl -tag -.It f +.It Fl d Ar DIR +install dotfiles from the +.Ar DIR +\&. This can be specified multiple times. +.It Fl e Ar EXCL_PAT +do not install rc files that match +.Ar EXCL_PAT . +This can be repeated with additional patterns. See +.Xr lsrc 1 , +.Sx EXCLUDE PATTERN , +for more details. +.It Fl f If the rc file already exists in your home directory but does not match the file in your dotfiles directory, remove the rc file then create the symlink -.It i +.It Fl i If the rc file already exists in your home directory but does not match the file in your dotfiles directory, prompt for how to handle it. This is the default .It Fl t Ar TAG install dotfiles according to .Ar TAG -.It Fl d Ar DIR -install dotfiles from the -.Ar DIR -\&. This can be specified multiple times. -.It Fl v -increase verbosity. This can be repeated for extra verbosity. .It Fl q decrease verbosity +.It Fl v +increase verbosity. This can be repeated for extra verbosity. .It Ar files only install the specified file(s) .El diff --git a/share/Makefile.in b/share/Makefile.in index 8b2661d..448b28c 100644 --- a/share/Makefile.in +++ b/share/Makefile.in @@ -181,9 +181,9 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign share/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu share/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --foreign share/Makefile + $(AUTOMAKE) --gnu share/Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ diff --git a/share/rcm.sh.in b/share/rcm.sh.in index f25665b..6dae15b 100644 --- a/share/rcm.sh.in +++ b/share/rcm.sh.in @@ -28,6 +28,14 @@ echo_error() { exit $exit_status } +echo_stderr() { + echo $* >&2 +} + +is_relative() { + echo $1 | grep -vq '^/' +} + version() { cat << EOV $1 (rcm) $VERSION @@ -47,7 +55,7 @@ handle_common_flags() { version $prog_name exit 0 elif [ $verbosity -ge 2 ]; then - DEBUG=echo + DEBUG=echo_stderr VERBOSE=echo PRINT=echo MV="$MV -v" -- cgit v1.2.3