#! /usr/bin/env bash set -e [ -n "$DOTIFY_DEBUG" ] && set -x # dotify 0.0.1 # https://github.com/jimeh/dotify # # Copyright (c) 2013 Jim Myhrberg. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # # Helper functions # # Checks for specified argument. # # Example: # # $ has-argument help h "-t none" # > returns 1 # $ has-argument help h "-t none --help" # > returns 0 # $ has-argument help h "-t none -h" # > returns 0 # # Returns 0 if argument was found, returns 1 otherwise. has-argument() { local long short long="--$1" short="-$2" shift 2 if [[ " $@ " == *" $long "* ]] || [[ " $@ " == *" $long="* ]]; then return 0 elif [[ " $@ " == *" $short "* ]] || [[ " $@ " == *" $short="* ]]; then return 0 fi return 1 } # Parses and echos value of specified argument. # # Example: # # $ parse-argument file f -t none --file /tmp/foobar.txt # /tmp/foobar.txt # $ parse-argument file f -t none --file="/tmp/foo bar.txt" # /tmp/foo bar.txt # $ parse-argument file f -t none -f /tmp/foobar.txt # /tmp/foobar.txt # $ parse-argument file f -t none -f=/tmp/foo\ bar.txt # /tmp/foo bar.txt # # Returns 0 and echos value if argument was found, returns 1 otherwise. parse-argument() { local long short arg next_arg long="--$1" short="-$2" shift 2 for arg in "$@"; do if [ -n "$next_arg" ]; then echo "$arg" return 0 elif [[ " $arg " == *" $long "* ]] || [[ " $arg " == *" $short "* ]]; then next_arg="yes" elif [[ " $arg " == *" $long="* ]]; then arg="${arg/#$long=/}" echo "$arg" return 0 elif [[ " $arg " == *" $short="* ]]; then arg="${arg/#$short=/}" echo "$arg" return 0 fi done return 1 } # Trim leading and trailing whitespace. # # Example: # # $ trim " foo bar " # foo bar # trim() { local string="$@" string="${string#"${string%%[![:space:]]*}"}" string="${string%"${string##*[![:space:]]}"}" echo -n "$string" } # # Internal functions # locate-dotfile() { if [ -n "$DOTFILE" ]; then if [ ! -f "$DOTFILE" ]; then echo "ERROR: \"$DOTFILE\" does not exist." >&2 return 1 fi elif [ -f "$(pwd)/Dotfile" ]; then DOTFILE="$(pwd)/Dotfile" else echo "ERROR: \"$(pwd)\" does not have a Dotfile." >&2 return 1 fi } locate-target() { if [ -n "$TARGET" ]; then if [ ! -d "$TARGET" ]; then echo "ERROR: Target \"$TARGET\" is not a directory." >&2 return 1 fi elif [ -n "$HOME" ] && [ -d "$HOME" ]; then TARGET="$HOME" elif [ -d ~ ]; then TARGET=~ else echo "ERROR: Your \$HOME folder could not be found." >&2 return 1 fi } create-rootlink() { local root="$1" local rootlink="$2" } create-symlink() { local source="$1" local target="$2" } parse-dotfile() { local dotfile="$1" local target="$2" local rootlink="$(parse-dotfile-rootlink "$dotfile")" local rootdir="$(dirname "$dotfile")" local cwd="$(pwd)" cd "$rootdir" create-rootlink "$rootdir" "$target/$rootlink" if [ -n "$?" ]; then return 1; fi while read line; do parse-dotfile-line "$dotfile" "$target" "$rootdir" "$rootlink" "$line" if [ -n "$?" ]; then return 1; fi done < "$dotfile" cd "$cwd" } parse-dotfile-rootlink() { local dotfile="$1" local rootlink while read line; do if [[ "$line" == "root_link "* ]]; then rootlink=${line/#root_link /} break fi done < "$dotfile" if [ -z "$rootlink" ]; then rootlink=".dotfiles"; fi echo "$root_link" } parse-dotfile-line() { local dotfile="$1" local target="$2" local rootdir="$3" local rootlink="$4" local line="$5" # Ignore comment lines starting with "#". if [[ "$line" == "#"* ]]; then return 0; fi # Ignore root link command. if [[ "$line" == "root_link "* ]]; then return 0; fi # Handle include command. if [[ "$line" == "include "* ]]; then include-dotfile "${line/#include /}" "$target" "$rootdir" "$rootlink" return "$?" fi echo "$line" } include-dotfile() { local dotfile="$1" local target="$2" local rootdir="$3" local rootlink="$4" local cwd="$(pwd)" cd "$rootdir" if [ ! -f "$dotfile" ]; then echo "ERROR: Can not include \"$dotfile\", it does not exist." >&2 return 1 fi while read line; do parse-dotfile-line "$dotfile" "$target" "$rootdir" "$rootlink" "$line" if [ -n "$?" ]; then return 1; fi done < "$dotfile" cd "$cwd" } # # Command functions # dotify-version() { echo "0.0.1" } dotify-print-version() { echo "dotify $(dotify-version)" } dotify-help() { echo "$(dotify-print-version)" echo "usage: dotify []" } dotify-info() { local dotfile="$(locate-dotfile "$@")" if [ -z "$dotfile" ]; then return 1; fi local target="$(locate-target "$@")" if [ -z "$target" ]; then return 1; fi echo "dotify $(dotify-version)" echo " Dotfile: $dotfile" echo " Root: $(dirname "$dotfile")" echo " Target: $target" } dotify-install() { local dotfile="$(locate-dotfile)" if [ -z "$dotfile" ]; then return 1; fi local target="$(locate-target)" if [ -z "$target" ]; then return 1; fi parse-dotfile "$dotfile" "$target" return $? } # # Argument Parsing # DOTFILE="" # --dotfile / -f TARGET="" # --target / -t HELP="" # --help / -h VERSION="" # --version / -v if has-argument dotfile f "$@"; then DOTFILE="$(parse-argument dotfile f "$@")" fi if has-argument target t "$@"; then TARGET="$(parse-argument target t "$@")" fi if has-argument help h "$@"; then HELP="1" fi if has-argument version v "$@"; then VERSION="1" fi # # Command Parsing # # Command is first argument that does not start with a dash or plus. for arg in "$@"; do if [[ "$arg" != "-"* ]] && [[ "$arg" != "+"* ]]; then command="$arg" break fi done # Show help and exit if help arguments or command are given. if [ -n "$HELP" ] || [ "$command" == "help" ]; then dotify-help exit fi # Show version info and exit if version arguments or command are given. if [ -n "$VERSION" ] || [ "$command" == "version" ]; then dotify-help | head -1 exit fi # Deal with the commands. case "$command" in "info" ) dotify-info ;; "" | "install" ) dotify-install ;; esac exit $?