diff --git a/src/bin/dotify b/src/bin/dotify new file mode 100644 index 0000000..c5c9143 --- /dev/null +++ b/src/bin/dotify @@ -0,0 +1,68 @@ +#! /usr/bin/env bash +set -e +[ -n "$DOTIFY_DEBUG" ] && set -x + +# dotify {{VERSION}} +# +# 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. + + +source "../lib/arguments.sh" +source "../lib/trim.sh" +source "../lib/expand-path.sh" +source "../lib/internals.sh" + +source "../lib/dotify-version.sh" +source "../lib/dotify-help.sh" +source "../lib/dotify-info.sh" +source "../lib/dotify-run.sh" + +# +# Command Parsing +# + +# If arguments include "--help" or "-h" display help and exit. +if has-argument help h "$@"; then + dotify-help "$@" + exit +fi + +# Command is first argument that does not start with a dash. +for arg in "$@"; do + if [[ "$arg" != "-"* ]]; then + command="$arg" + break + fi +done + +case "$command" in + "help" ) + dotify-help "$@" + ;; + "info" ) + dotify-info "$@" + ;; + "" | "link" | "symlink" ) + dotify-symlink "$@" + ;; +esac + +exit "$?" diff --git a/src/lib/arguments.sh b/src/lib/arguments.sh new file mode 100644 index 0000000..31c58e8 --- /dev/null +++ b/src/lib/arguments.sh @@ -0,0 +1,66 @@ +# 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 and echos value if argument was found, returns 1 otherwise. +has-argument() { + local long short args arg next_arg + + 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 +} diff --git a/src/lib/dotify-help.sh b/src/lib/dotify-help.sh new file mode 100644 index 0000000..aba554f --- /dev/null +++ b/src/lib/dotify-help.sh @@ -0,0 +1,3 @@ +dotify-help() { + echo "dotify $(dotify-version)" +} diff --git a/src/lib/dotify-info.sh b/src/lib/dotify-info.sh new file mode 100644 index 0000000..62fbec6 --- /dev/null +++ b/src/lib/dotify-info.sh @@ -0,0 +1,12 @@ +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" +} diff --git a/src/lib/dotify-run.sh b/src/lib/dotify-run.sh new file mode 100644 index 0000000..a1af050 --- /dev/null +++ b/src/lib/dotify-run.sh @@ -0,0 +1,10 @@ +dotify-run() { + 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 "$?" +} diff --git a/src/lib/dotify-version.sh b/src/lib/dotify-version.sh new file mode 100644 index 0000000..5fc0358 --- /dev/null +++ b/src/lib/dotify-version.sh @@ -0,0 +1,3 @@ +dotify-version() { + echo "0.0.1" +} diff --git a/src/lib/expand-path.sh b/src/lib/expand-path.sh new file mode 100644 index 0000000..6963de4 --- /dev/null +++ b/src/lib/expand-path.sh @@ -0,0 +1,12 @@ +# Expands given path. +# +# Example: +# +# $ expand-path "~/Projects" +# /Users/jimeh/Projects +# $ expand-path "config/*.json" +# config/application.json config/database.yml +# +expand-path() { + echo $(eval echo "$@") +} diff --git a/src/lib/internals.sh b/src/lib/internals.sh new file mode 100644 index 0000000..9e9af93 --- /dev/null +++ b/src/lib/internals.sh @@ -0,0 +1,133 @@ +# +# Internal functions +# + +locate-dotfile() { + local dotfile + if has-argument dotfile f "$@"; then + dotfile="$(parse-argument dotfile f "$@")" + dotfile="$(expand-path "$dotfile")" + 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 + + echo "$dotfile" +} + +locate-target() { + local target + if has-argument target t "$@"; then + target="$(parse-argument target t "$@")" + target="$(expand-path "$target")" + if [ ! -d "$target" ]; then + echo "ERROR: Target \"$target\" is not a directory." + return 1 + fi + elif [ -n "$HOME" ] && [ -d "$HOME" ]; then + target="$HOME" + elif [ -d "$(expand-path "~")" ]; then + target="$HOME" + else + echo "ERROR: Your \$HOME folder could not be found." + return 1 + fi + + echo "$target" +} + +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="$(expand-path "$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" +} diff --git a/src/lib/trim.sh b/src/lib/trim.sh new file mode 100644 index 0000000..5860d91 --- /dev/null +++ b/src/lib/trim.sh @@ -0,0 +1,13 @@ +# 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" +}