summaryrefslogtreecommitdiff
path: root/bin/lsrc.in
diff options
context:
space:
mode:
Diffstat (limited to 'bin/lsrc.in')
-rwxr-xr-xbin/lsrc.in328
1 files changed, 328 insertions, 0 deletions
diff --git a/bin/lsrc.in b/bin/lsrc.in
new file mode 100755
index 0000000..1402fec
--- /dev/null
+++ b/bin/lsrc.in
@@ -0,0 +1,328 @@
+#!@SHELL@
+
+: ${RCM_LIB:=$(dirname "$0")/../share/rcm}
+. "$RCM_LIB/rcm.sh"
+
+pushdir() {
+ DIR_STACK="$DIR_STACK:$PWD/$1"
+ $DEBUG "cd'ing to $1 from $PWD with stack $DIR_STACK"
+ cd "$1"
+}
+
+popdir() {
+ current="$(echo "$DIR_STACK" | sed -e 's/.*://g')"
+ prior="$(echo "$DIR_STACK" | sed -e "s|:$current$||" | sed -e 's/.*://g')"
+ DIR_STACK="$(echo "$DIR_STACK" | sed -e 's/:[^:]*$//')"
+ $DEBUG "cd'ing to $prior from $PWD with stack $DIR_STACK"
+ cd "$prior"
+}
+
+build_path() {
+ local dest="$1"
+ local file="$2"
+ local dotted=$3
+
+ if [ $dotted -eq 1 ]; then
+ echo "$dest/$file"
+ else
+ echo "$dest/.$file"
+ fi
+}
+
+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 dotfiles_subdir="$4"
+ local dotted=$5
+ local exclude_file_globs="$6"
+ local include_file_globs="$7"
+ local symlink_dirs_file_globs="$8"
+ local dest_path="$(build_path "$dest_dir" "$dir" $dotted)"
+
+ $DEBUG "show_dir $1 $2 $3 $4 $5 $6 $7 $8"
+
+ $VERBOSE "recurring on $dest_path"
+
+ pushdir "$dir"
+ for f in *; do
+ $DEBUG "handling the file $f"
+ next_dir="$(file_join "$dotfiles_subdir" "$dir")"
+ handle_file "$f" "$dest_path" "$dotfiles_dir" "$next_dir" 1 "$exclude_file_globs" "$include_file_globs" "$symlink_dirs_file_globs"
+ done
+ popdir
+}
+
+sigil_for() {
+ local file="$1"
+ local symlink_dirs_file_globs="$2"
+ local copy_always=0
+ local symlink_dirs=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 [ -n "$symlink_dirs_file_globs" ]; then
+ symlink_dirs=1
+ fi
+
+ if [ $copy_always -eq 1 ]; then
+ echo 'X'
+ elif [ $symlink_dirs -eq 1 ]; then
+ echo '$'
+ else
+ echo '@'
+ fi
+}
+
+show_file() {
+ local file="$1"
+ local dest_dir="$2"
+ local dotfiles_dir="$3"
+ local dotfiles_subdir="$4"
+ local dotted=$5
+ local symlink_dirs_file_globs="$6"
+ local dest_file="$(build_path "$dest_dir" "$file" $dotted)"
+
+ if echo "$DEST_STACK" | grep -vq ":$dest_file"; then
+ DEST_STACK="$DEST_STACK:$dest_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" "$symlink_dirs_file_globs")"
+ output="$output:$sigil"
+ fi
+
+ $PRINT "$output"
+ fi
+}
+
+handle_file() {
+ local file="$1"
+ local dest_dir="$2"
+ local dotfiles_dir="$3"
+ local dotfiles_subdir="$4"
+ local dotted=$5
+ local exclude_file_globs="$6"
+ local include_file_globs="$7"
+ local symlink_dirs_file_globs="$8"
+
+ $DEBUG "handle_file $1 $2 $3 $4 $5 $6 $7 $8"
+
+ 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" ] && is_excluded $file "$symlink_dirs_file_globs"; then
+ show_file "$file" "$dest_dir" "$dotfiles_dir" "$dotfiles_subdir" $dotted "$symlink_dirs_file_globs"
+ elif [ -d "$file" ]; then
+ show_dir "$file" "$dest_dir" "$dotfiles_dir" "$dotfiles_subdir" $dotted "$exclude_file_globs" "$include_file_globs" "$symlink_dirs_file_globs"
+ else
+ show_file "$file" "$dest_dir" "$dotfiles_dir" "$dotfiles_subdir" $dotted
+ fi
+}
+
+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-' -o "x$1" = "xhooks" ]
+}
+
+dotfiles_dir_excludes() {
+ local dotfiles_dir="$1"
+ local excludes="$2"
+
+ $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"
+ local include_file_globs="$3"
+
+ $DEBUG "is_excluded $file $exclude_file_globs $include_file_globs"
+
+ for exclude_file_glob in $exclude_file_globs; do
+ $DEBUG "file: $file"
+ $DEBUG "exclude_file_glob: $exclude_file_glob"
+
+ case "$file" in
+ $exclude_file_glob)
+ for include_file_glob in $include_file_globs; do
+ case "$file" in
+ $include_file_glob) return 1;;
+ esac
+ done
+
+ return 0
+ ;;
+ esac
+ done
+
+ return 1
+}
+
+show_help() {
+ local exit_code=${1:-0}
+
+ $PRINT "Usage: lsrc [-FVqvh] [-I EXCL_PAT] [-x EXCL_PAT] [-N EXCL_PAT ] [-t TAG] [-d DOT_DIR]"
+ $PRINT "see lsrc(1) and rcm(5) for more details"
+
+ exit $exit_code
+}
+
+handle_command_line() {
+ local arg_tags=
+ local verbosity=0
+ local version=0
+ local show_sigils=0
+ local dotfiles_dirs=
+ local excludes=
+ local includes=
+ local symlink_dirs=
+
+ while getopts FVqvhI:x:S:t:d: opt; do
+ case "$opt" in
+ F) show_sigils=1;;
+ h) show_help ;;
+ I) includes="$includes $OPTARG";;
+ t) arg_tags="$arg_tags $OPTARG";;
+ v) verbosity=$(($verbosity + 1));;
+ q) verbosity=$(($verbosity - 1));;
+ d) dotfiles_dirs="$dotfiles_dirs $OPTARG";;
+ V) version=1;;
+ x) excludes="$excludes $OPTARG";;
+ S) symlink_dirs="$symlink_dirs $OPTARG";;
+ esac
+ done
+ 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}"
+ INCLUDES="${includes:-$INCLUDES}"
+ SYMLINK_DIRS="${symlink_dirs:-$SYMLINK_DIRS}"
+ FILES="$@"
+
+ $DEBUG "TAGS: $TAGS"
+ $DEBUG "DOTFILES_DIRS: $DOTFILES_DIRS"
+}
+
+DEST_STACK=
+
+handle_command_line "$@"
+
+: ${DOTFILES_DIRS:=$DOTFILES_DIRS $DEFAULT_DOTFILES_DIR}
+$DEBUG "DOTFILES_DIRS: $DOTFILES_DIRS"
+
+: ${COPY_ALWAYS:=""}
+$DEBUG "COPY_ALWAYS: $COPY_ALWAYS"
+
+: ${SYMLINK_DIRS:=""}
+$DEBUG "SYMLINK_DIRS: $SYMLINK_DIRS"
+
+relative_root_dir="$PWD"
+
+for DOTFILES_DIR in $DOTFILES_DIRS; do
+ cd "$relative_root_dir"
+
+ if is_relative $DOTFILES_DIR; then
+ DOTFILES_DIR="$PWD/$DOTFILES_DIR"
+ fi
+
+ if [ ! -d "$DOTFILES_DIR" ]; then
+ $VERBOSE "skipping non-existent directory: $DOTFILES_DIR"
+ continue
+ fi
+
+ exclude_file_globs="$(dotfiles_dir_excludes "$DOTFILES_DIR" "$EXCLUDES")"
+ $DEBUG "exclude_file_globs: $exclude_file_globs"
+ include_file_globs="$(dotfiles_dir_excludes "$DOTFILES_DIR" "$INCLUDES")"
+ symlink_dirs_file_globs="$(dotfiles_dir_excludes "$DOTFILES_DIR" "$SYMLINK_DIRS")"
+ $DEBUG "symlink_dirs_file_globs: $symlink_dirs_file_globs"
+
+ cd "$DOTFILES_DIR"
+ DIR_STACK=":$DOTFILES_DIR"
+
+ for file in ${FILES:-*}; do
+ if is_metafile "$file"; then
+ continue
+ fi
+
+ handle_file "$file" "$DEST_DIR" "$DOTFILES_DIR" . 0 "$exclude_file_globs" "$include_file_globs" "$symlink_dirs_file_globs"
+ done
+
+ cd "$DOTFILES_DIR"
+
+ host_files="$DOTFILES_DIR/host-$HOSTNAME"
+ 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" "$symlink_dirs_file_globs"
+ done
+ popdir
+ fi
+
+ cd "$DOTFILES_DIR"
+
+ for tag in $TAGS; do
+ if [ -d "tag-$tag" ]; then
+ 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" "$symlink_dirs_file_globs"
+ done
+ popdir
+ fi
+ done
+
+done