diff --git a/src/bin/dotify b/src/bin/dotify index 7ec8107..f992bd2 100755 --- a/src/bin/dotify +++ b/src/bin/dotify @@ -37,8 +37,11 @@ source "../lib/trim.sh" # Internal functions # -source "../lib/parse-dotfile-options.sh" -source "../lib/internals.sh" +source "../lib/internals/compile-dotfile.sh" +source "../lib/internals/create-symlink.sh" +source "../lib/internals/locate-dotfile.sh" +source "../lib/internals/locate-target.sh" +source "../lib/internals/parse-dotfile-options.sh" # # Command functions diff --git a/src/lib/internals.sh b/src/lib/internals.sh deleted file mode 100644 index dba2ce8..0000000 --- a/src/lib/internals.sh +++ /dev/null @@ -1,93 +0,0 @@ -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-symlink() { - local source="$1" - local target="$2" -} - -execute-dotfile() { - parse-dotfile-options - local root_dir="$(dirname "$DOTFILE")" - - local cwd="$(pwd)" - cd "$root_dir" - - create-symlink "$root_dir" "$TARGET/$OPT_ROOT_LINK" - if [ -n "$?" ]; then return 1; fi - - while read line; do - parse-dotfile-line "$line" - if [ -n "$?" ]; then return 1; fi - done < "$DOTFILE" - - cd "$cwd" -} - -parse-dotfile-line() { - local line="$(trim "$1")" - local dotfile="$DOTFILE" - if [ -n "$2" ]; then dotfile="$2"; fi - - # Ignore comment lines starting with "#". - if [[ "$line" == "#"* ]]; then return 0; fi - - # Ignore Dotfile options. - if [[ "$line" == "root_link "* ]]; then return 0; fi - if [[ "$line" == "default_action "* ]]; then return 0; fi - - # Handle include command. - if [[ "$line" == "include "* ]]; then - include-dotfile "$(trim "${line/#include /}")" - return "$?" - fi - - echo "$line" -} - -include-dotfile() { - local dotfile="$1" - local root_dir="$(dirname "$dotfile")" - - local cwd="$(pwd)" - cd "$root_dir" - - 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 "$line" "$dotfile" - if [ -n "$?" ]; then return 1; fi - done < "$dotfile" - - cd "$cwd" -} diff --git a/src/lib/internals/compile-dotfile.sh b/src/lib/internals/compile-dotfile.sh new file mode 100644 index 0000000..7cb1c6d --- /dev/null +++ b/src/lib/internals/compile-dotfile.sh @@ -0,0 +1,31 @@ +compile-dotfile() { + local dotfile="$1" + if [ -z "$dotfile" ]; then dotfile="$DOTFILE"; fi + + local output="" + local line="" + while IFS= read line; do + # Ignore comments and blank lines. + if [[ "$line" =~ ^(\ *\#.*|\ *)$ ]]; then + continue + + # Parse ": -> " lines. + elif [[ "$line" =~ ^(\ +)?([a-zA-Z0-9_-]+):\ (.+)\ +-[\>]\ +(.+)$ ]]; then + output="${output}${BASH_REMATCH[1]}dotify-action ${BASH_REMATCH[2]} " + output="${output}$(trim "${BASH_REMATCH[3]}") " + output="${output}$(trim "${BASH_REMATCH[4]}")\n" + + # Parse " -> " lines. + elif [[ "$line" =~ ^(\ +)?(.+)\ -[\>]\ (.+)$ ]]; then + output="${output}${BASH_REMATCH[1]}dotify-action default " + output="${output}$(trim "${BASH_REMATCH[2]}") " + output="${output}$(trim "${BASH_REMATCH[3]}")\n" + + # Append line without modifications. + else + output="${output}${line}\n" + fi + done < "$dotfile" + + echo -e "$output" +} diff --git a/src/lib/internals/create-symlink.sh b/src/lib/internals/create-symlink.sh new file mode 100644 index 0000000..d16a54a --- /dev/null +++ b/src/lib/internals/create-symlink.sh @@ -0,0 +1,18 @@ +create-symlink() { + local source="$1" + local target="$2" + + if [ ! -e "$target" ] && [ ! -h "$target" ]; then + ln -s "$source" "$target" + return 0 + elif [ -h "$target" ]; then + if [ "$(readlink "$target")" != "$source" ]; then + echo "ERROR: \"$target\" exists, is a symlink to:" \ + "$(readlink "$target")" >&2 + return 1 + fi + else + echo "ERROR: \"$target\" exists" >&2 + return 1 + fi +} diff --git a/src/lib/internals/locate-dotfile.sh b/src/lib/internals/locate-dotfile.sh new file mode 100644 index 0000000..b4713b4 --- /dev/null +++ b/src/lib/internals/locate-dotfile.sh @@ -0,0 +1,13 @@ +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 +} diff --git a/src/lib/internals/locate-target.sh b/src/lib/internals/locate-target.sh new file mode 100644 index 0000000..f4a6bd0 --- /dev/null +++ b/src/lib/internals/locate-target.sh @@ -0,0 +1,15 @@ +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 +} diff --git a/src/lib/parse-dotfile-options.sh b/src/lib/internals/parse-dotfile-options.sh similarity index 80% rename from src/lib/parse-dotfile-options.sh rename to src/lib/internals/parse-dotfile-options.sh index ba54349..850e727 100644 --- a/src/lib/parse-dotfile-options.sh +++ b/src/lib/internals/parse-dotfile-options.sh @@ -1,7 +1,17 @@ # Parse Dotfile options and set relevant global variables. parse-dotfile-options() { - OPT_ROOT_LINK="$(parse-dotfile-root_link-option)" - OPT_DEFAULT_ACTION="$(parse-dotfile-default_action-option)" + DOTIFY_OPT_ROOT_LINK="$(parse-dotfile-root_link-option)" + DOTIFY_OPT_DEFAULT_ACTION="$(parse-dotfile-default_action-option)" +} + +# Parse root_link option. +parse-dotfile-root_link-option() { + echo "$(parse-dotfile-option "root_link" ".dotfiles" "$1")" +} + +# Parse default_action option. +parse-dotfile-default_action-option() { + echo "$(parse-dotfile-option "default_action" "link" "$1")" } # Extract a specific option from Dotfile. @@ -17,6 +27,7 @@ parse-dotfile-option() { local dotfile="$3" if [ -z "$dotfile" ]; then dotfile="$DOTFILE"; fi + local line="" while read line; do if [[ "$line" == "$name "* ]]; then value="$(trim "${line/#$name }")" @@ -26,11 +37,3 @@ parse-dotfile-option() { echo "$value" } - -parse-dotfile-root_link-option() { - echo "$(parse-dotfile-option "root_link" ".dotfiles" "$1")" -} - -parse-dotfile-default_action-option() { - echo "$(parse-dotfile-option "default_action" "link" "$1")" -} diff --git a/test/lib/internals/create-symlink-test.sh b/test/lib/internals/create-symlink-test.sh new file mode 100755 index 0000000..4164895 --- /dev/null +++ b/test/lib/internals/create-symlink-test.sh @@ -0,0 +1,50 @@ +#! /usr/bin/env bash +source "../../test-helper.sh" +source "../../../src/lib/internals.sh" + +# +# create-symlink() tests +# + +# Create temp files/folders used for create-symlink() tests. +mkdir -p "tmp/source" "tmp/target" +touch "tmp/source/profile" + +# Creates a normal symlink. +assert_raises 'create-symlink ../source tmp/target/.dotfiles' 0 +assert 'readlink tmp/target/.dotfiles' "../source" +rm "tmp/target/.dotfiles" + +# When target exists an is a symlink to the same source. +ln -s "../source" "tmp/target/.dotfiles" +assert_raises 'create-symlink ../source tmp/target/.dotfiles' 0 +assert 'create-symlink ../source tmp/target/.dotfiles 2>&1' "" +rm "tmp/target/.dotfiles" + +# When target exists and is a symlink to a different source. +ln -s "../other" "tmp/target/.dotfiles" +assert_raises 'create-symlink ../source tmp/target/.dotfiles' 1 +assert 'create-symlink ../source tmp/target/.dotfiles' "" +assert 'create-symlink ../source tmp/target/.dotfiles 2>&1' \ + "ERROR: \"tmp/target/.dotfiles\" exists, is a symlink to: ../other" +rm "tmp/target/.dotfiles" + +# When target exists and is a file. +touch "tmp/target/.profile" +assert_raises 'create-symlink ../source/profile tmp/target/.profile' 1 +assert 'create-symlink ../source/profile tmp/target/.profile' "" +assert 'create-symlink ../source/profile tmp/target/.profile 2>&1' \ + "ERROR: \"tmp/target/.profile\" exists" +rm "tmp/target/.profile" + +# When target exists and is a directory. +assert_raises 'create-symlink ../source tmp/target' 1 +assert 'create-symlink ../source tmp/target' "" +assert 'create-symlink ../source tmp/target 2>&1' \ + "ERROR: \"tmp/target\" exists" + +# Remove temp files/folders used for locate-dotfile() tests. +rm "tmp/source/profile" +rmdir "tmp/source" "tmp/target" "tmp" + +assert_end 'create-symlink()' diff --git a/test/lib/internals-test.sh b/test/lib/internals/locate-dotfile-test.sh similarity index 59% rename from test/lib/internals-test.sh rename to test/lib/internals/locate-dotfile-test.sh index 75c46dd..f2f6562 100755 --- a/test/lib/internals-test.sh +++ b/test/lib/internals/locate-dotfile-test.sh @@ -1,9 +1,9 @@ #! /usr/bin/env bash -source "../test-helper.sh" -source "../../src/lib/internals.sh" +source "../../test-helper.sh" +source "../../../src/lib/internals.sh" # -# locate-dotfile() +# locate-dotfile() tests # # Create temp files/folders used for locate-dotfile() tests. @@ -49,37 +49,3 @@ assert_raises "test -d test-tmp" 1 # End of locate-dotfile() tests. assert_end 'locate-dotfile()' - - -# -# locate-target() -# - -# When $TARGET is empty. -TARGET="" -assert_raises 'locate-target' 0 -assert 'locate-target; echo "$TARGET"' "$HOME" -unset TARGET - -# When $TARGET is not empty and is a directory. -TARGET="$(pwd)" -assert_raises 'locate-target' 0 -assert 'locate-target; echo "$TARGET"' "$(pwd)" -unset TARGET - -# When $TARGET is not empty and is not a directory. -TARGET="/tmp/this/does/not/exist" -assert_raises 'locate-target' 1 -assert 'locate-target 2>&1' "ERROR: Target \"$TARGET\" is not a directory." -assert 'locate-target; echo "$TARGET"' "$TARGET" -unset TARGET - -# If neither $TARGET or $HOME is set, ~ is expanded to find home folder -original_home="$HOME" -unset HOME -assert_raises 'locate-target' 0 -assert 'locate-target; echo "$TARGET"' "$original_home" -HOME="$original_home" - -# End of locate-target() tests. -assert_end 'locate-target()' diff --git a/test/lib/internals/locate-target-test.sh b/test/lib/internals/locate-target-test.sh new file mode 100755 index 0000000..89d7323 --- /dev/null +++ b/test/lib/internals/locate-target-test.sh @@ -0,0 +1,36 @@ +#! /usr/bin/env bash +source "../../test-helper.sh" +source "../../../src/lib/internals.sh" + +# +# locate-target() tests +# + +# When $TARGET is empty. +TARGET="" +assert_raises 'locate-target' 0 +assert 'locate-target; echo "$TARGET"' "$HOME" +unset TARGET + +# When $TARGET is not empty and is a directory. +TARGET="$(pwd)" +assert_raises 'locate-target' 0 +assert 'locate-target; echo "$TARGET"' "$(pwd)" +unset TARGET + +# When $TARGET is not empty and is not a directory. +TARGET="/tmp/this/does/not/exist" +assert_raises 'locate-target' 1 +assert 'locate-target 2>&1' "ERROR: Target \"$TARGET\" is not a directory." +assert 'locate-target; echo "$TARGET"' "$TARGET" +unset TARGET + +# If neither $TARGET or $HOME is set, ~ is expanded to find home folder +original_home="$HOME" +unset HOME +assert_raises 'locate-target' 0 +assert 'locate-target; echo "$TARGET"' "$original_home" +HOME="$original_home" + +# End of locate-target() tests. +assert_end 'locate-target()' diff --git a/test/lib/parse-dotfile-options-test.sh b/test/lib/internals/parse-dotfile-options-test.sh similarity index 94% rename from test/lib/parse-dotfile-options-test.sh rename to test/lib/internals/parse-dotfile-options-test.sh index 8157a81..0dc054e 100755 --- a/test/lib/parse-dotfile-options-test.sh +++ b/test/lib/internals/parse-dotfile-options-test.sh @@ -4,7 +4,7 @@ source "../../src/lib/parse-dotfile-options.sh" source "../../src/lib/trim.sh" # -# parse-dotfile-options() +# parse-dotfile-options() tests # stub "parse-dotfile-root_link-option" @@ -24,7 +24,7 @@ assert_end "parse-dotfile-options()" # -# parse-dotfile-option() +# parse-dotfile-option() tests # # Create temp files/folders used for locate-dotfile() tests. @@ -62,7 +62,7 @@ assert_end "parse-dotfile-option()" # -# parse-dotfile-root_link-option() +# parse-dotfile-root_link-option() tests # stub "parse-dotfile-option" @@ -76,7 +76,7 @@ assert_end "parse-dotfile-root_link-option()" # -# parse-dotfile-default_action-option() +# parse-dotfile-default_action-option() tests # stub "parse-dotfile-option"