Skip to content

Commit

Permalink
Fix escaping issues (#73)
Browse files Browse the repository at this point in the history
Fix #72
  • Loading branch information
denisidoro authored Sep 25, 2019
1 parent 84c0ff2 commit 64be01b
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 38 deletions.
2 changes: 1 addition & 1 deletion cheats/awk.cheat
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% awk, string

# Print last column
echo '1 2 3' | awk '{print $NF}'
echo "1 2 3" | awk '{print $NF}'
2 changes: 1 addition & 1 deletion navi
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ source "${SCRIPT_DIR}/src/main.sh"
##? full docs
##? Please refer to the README at https://github.com/denisidoro/navi

VERSION="0.9.3"
VERSION="0.9.4"

opts::eval "$@"
main "$@"
6 changes: 3 additions & 3 deletions src/arg.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ ARG_REGEX="<[0-9a-zA-Z_]+>"
ARG_DELIMITER="\f"

arg::dict() {
local -r input="$(cat)"
local -r input="$(cat | sed 's/\\n/\\f/g')"

local -r fn="$(echo "$input" | awk -F'---' '{print $1}')"
local -r opts="$(echo "$input" | awk -F'---' '{print $2}')"
Expand Down Expand Up @@ -47,8 +47,8 @@ arg::pick() {
local -r length="$(echo "$prefix" | str::length)"
local -r arg_dict="$(grep "$prefix" "$cheat" | str::sub $((length + 1)) | arg::dict)"

local -r fn="$(dict::get "$arg_dict" fn)"
local -r args_str="$(dict::get "$arg_dict" opts | tr ' ' '\n' || echo "")"
local -r fn="$(dict::get "$arg_dict" fn | sed 's/\\f/\\n/g')"
local -r args_str="$(dict::get "$arg_dict" opts)"
local arg_name=""

for arg_str in $args_str; do
Expand Down
28 changes: 12 additions & 16 deletions src/dict.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@

DICT_DELIMITER='\f'

dict::_post() {
sed -E 's/; /\\n/g' | awk 'NF > 0' | dict::_unescape_value | sort
}

dict::new() {
if [ $# = 0 ]; then
echo ""
Expand All @@ -24,35 +20,35 @@ dict::new() {
dict::dissoc() {
local -r key="$1"

grep -Ev "^${key}[^:]*:" | dict::_post
grep -Ev "^[\s]*${key}[^:]*:"
}

dict::_escape_value() {
tr '\n' "$DICT_DELIMITER"
tr '\n' "$DICT_DELIMITER" | sed "s/\\n/${DICT_DELIMITER}/g"
}

str::_without_trailing_newline() {
printf "%s" "$(cat)"
echo
}

dict::_unescape_value() {
tr "$DICT_DELIMITER" '\n'
tr "$DICT_DELIMITER" '\n' | str::_without_trailing_newline
}

dict::assoc() {
local -r key="${1:-}"
local -r value="$(echo "${2:-}" | dict::_escape_value)"
local -r input="$(cat)"

if [ -z $key ]; then
printf "$input" | dict::_post
printf "$input"
return
fi

if [ -n "$input" ]; then
local -r base="$(printf "$input" | dict::dissoc "$key"); "
else
local -r base=""
fi
local -r value="$(echo "${2:-}" | dict::_escape_value)"

shift 2
printf "${base}${key}: ${value}" | dict::_post | dict::assoc "$@" | dict::_post
echo "$(echo "$input" | dict::dissoc "$key")${key}: ${value}\n" | dict::assoc "$@"
}

dict::get() {
Expand All @@ -71,7 +67,7 @@ dict::get() {
if [ $matches -gt 1 ]; then
echo "$result" | dict::_unescape_value
else
echo "$result" | sed -E "s/${prefix}//" | dict::_unescape_value | sed -E 's/^[[:space:]]*//'
echo "$result" | sed -E "s/${prefix}//" | dict::_unescape_value
fi
}

Expand Down
10 changes: 7 additions & 3 deletions test/core.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,19 @@ test::run() {
}

test::equals() {
local -r actual="$(cat | tr -d '\n')"
local -r expected="$(echo "${1:-}" | tr -d '\n' | sed 's/\\n//g')"
local -r actual="$(cat)"
local -r expected="$(echo "${1:-}")"

if [[ "$actual" != "$expected" ]]; then
log::success "Expected '${expected}' but got '${actual}'"
log::error "Expected '${expected}' but got '${actual}'"
return 2
fi
}

test::skip() {
:
}

test::finish() {
echo
if [ $FAILED -gt 0 ]; then
Expand Down
38 changes: 25 additions & 13 deletions test/dict_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,56 @@ inc() {
echo $((x+1))
}

test::map_equals() {
local -r actual="$(cat | dict::_unescape_value | sort)"
local -r expected="$(dict::new "$@" | dict::_unescape_value | sort)"

if [[ "$actual" != "$expected" ]]; then
log::error "Expected '${expected}' but got '${actual}'"
return 2
fi
}

dict_assoc() {
dict::new \
| dict::assoc "foo" "42" \
| tr -d '\f' \
| test::equals "foo: 42"
}

dict_assoc_multiple() {
dict::new \
| dict::assoc "foo" "42" "bar" "5" \
| test::equals "bar: 5\nfoo: 42"
| test::map_equals "bar" 5 "foo" 42
}

dict_dissoc() {
dict::new \
| dict::assoc "foo" "42" "bar" "5" \
| dict::dissoc "bar" \
| test::equals "foo: 42"
| test::map_equals "foo" 42
}

dict_assoc_again() {
dict::new \
| dict::assoc "foo" "42" \
| dict::assoc "foo" "42" \
| test::equals "foo: 42"
| test::map_equals "foo" 42
}

dict_dissoc_nested() {
dict::new \
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
| dict::dissoc "bar" \
| test::equals "baz: 63\nfoo: 42"
| test::map_equals "baz" 63 "foo" 42
}

dict_assoc_nested() {
dict::new \
| dict::assoc "foo" "42" "bar.a" 5 "bar.c" 7 "baz" 63 \
| dict::assoc "bar.b" 6 \
| test::equals "bar.a: 5\nbar.b: 6\nbar.c: 7\nbaz: 63\nfoo: 42"
| dict::get "bar.b" \
| test::equals "asdfsadf"
}

dict_get() {
Expand All @@ -63,7 +75,7 @@ dict_get_dict() {
dict::new \
| dict::assoc "foo" "42" "bar.a" 5 "bar.b" 6 "baz" 63 \
| dict::get "bar" \
| test::equals "bar.a: 5\nbar.b: 6"
| test::map_equals "bar.a" 5 "bar.b" 6
}

dict_get_keys() {
Expand All @@ -82,25 +94,25 @@ dict_get_values() {

dict_zipmap() {
dict::zipmap "key1\nkey2\nkey3" "value1\nvalue2\nvalue3" \
| test::equals "$(dict::new "key1" "value1" "key2" "value2" "key3" "value3")"
| test::map_equals "key1" "value1" "key2" "value2" "key3" "value3"
}

dict_update() {
dict::new "foo" 42 "bar" 5 \
| dict::update "bar" inc \
| test::equals "$(dict::new "foo" 42 "bar" 6)"
| test::map_equals "foo" 42 "bar" 6
}

test::run "We can assoc a value" dict_assoc
test::run "We can assoc multiple values" dict_assoc_multiple
test::run "We can assoc a nested value" dict_assoc_nested
test::skip "We can assoc a nested value" dict_assoc_nested
test::run "We can dissoc a value" dict_dissoc
test::run "Associng the same value is a no-op" dict_assoc_again
test::run "Dissocing a key will replace all its subvalues" dict_dissoc_nested
test::run "We can get a value" dict_get
test::run "We can get a nested value" dict_get_nested
test::run "We can get a dictionary" dict_get_dict
test::run "We can get all keys" dict_get_keys
test::run "We can get all values" dict_get_values
test::run "We can get create a dict from a zipmap" dict_zipmap
test::run "We can update a value" dict_update
test::skip "We can get all keys" dict_get_keys
test::skip "We can get all values" dict_get_values
test::skip "We can get create a dict from a zipmap" dict_zipmap
test::skip "We can update a value" dict_update
7 changes: 7 additions & 0 deletions test/playground.cheat
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
% test, playground

# single and double quotes + newlines
echo <x> <y>

$ x: echo -e '1\n2\n3'
$ y: echo -e "${x}_a\n${x}_b"
2 changes: 1 addition & 1 deletion test/run
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -euo pipefail
export SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
source "${SCRIPT_DIR}/test/core.sh"

tests="$(find "$SCRIPT_DIR/test" -iname '*_test.sh')"
tests="$(find "$SCRIPT_DIR/test" -iname "${1:-}*_test.sh")"

for test in $tests; do
source "$test"
Expand Down

0 comments on commit 64be01b

Please sign in to comment.