Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow limiting scope of grep to keys or values #66

Merged
merged 1 commit into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ cp <from-path> <to-path>
append <from-secret> <to-secret> [flag]
rm <dir-path or filel-path>
ls <dir-path // optional>
grep <search> <path> [-e|--regexp]
grep <search> <path> [-e|--regexp] [-k|--keys] [-v|--values]
cd <dir-path>
cat <file-path>
```
Expand Down Expand Up @@ -131,7 +131,7 @@ tree=oak

### grep

`grep` recursively searches the given substring in key and value pairs. To treat the search string as a regular-expression, add `-e` or `--regexp` to the end of the command.
`grep` recursively searches the given substring in key and value pairs. To treat the search string as a regular-expression, add `-e` or `--regexp` to the end of the command. By default, both keys and values will be searched. If you would like to limit the search, you may add `-k` or `--keys` to the end of the command to search only a path's keys, or `-v` or `--values` to search only a path's values.
If you are looking for copies or just trying to find the path to a certain string, this command might come in handy.

## Setting the vault token
Expand Down
77 changes: 55 additions & 22 deletions cli/grep.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ import (
"github.com/fishi0x01/vsh/log"
)

// GrepMode defines the scope of which parts of a path to search (keys and/or values)
type GrepMode int

const (
// ModeKeys only searches keys
ModeKeys GrepMode = 1
// ModeValues only searches values
ModeValues GrepMode = 2
)

// GrepCommand container for all 'grep' parameters
type GrepCommand struct {
name string
Expand All @@ -23,6 +33,7 @@ type GrepCommand struct {
Path string
Search string
Regexp *regexp.Regexp
Mode GrepMode
}

// Match structure to keep indices of matched terms
Expand Down Expand Up @@ -58,7 +69,12 @@ func (cmd *GrepCommand) IsSane() bool {

// PrintUsage print command usage
func (cmd *GrepCommand) PrintUsage() {
log.UserInfo("Usage:\ngrep <term-string> <path> [-e|--regexp]")
log.UserInfo("Usage:\ngrep <search> <path> [-e|--regexp] [-k|--keys] [-v|--values]")
}

// IsMode returns true if the specified mode is enabled
func (cmd *GrepCommand) IsMode(mode GrepMode) bool {
return cmd.Mode&mode == mode
}

// Parse given arguments and return status
Expand All @@ -78,8 +94,18 @@ func (cmd *GrepCommand) Parse(args []string) error {
return fmt.Errorf("cannot parse regex pattern")
}
cmd.Regexp = re
case "-k", "--keys":
cmd.Mode |= ModeKeys
case "-v", "--values":
cmd.Mode |= ModeValues
default:
return fmt.Errorf("invalid flag: %s", v)
}
}
if cmd.Mode == 0 {
cmd.Mode = ModeKeys + ModeValues
}

return nil
}

Expand Down Expand Up @@ -139,32 +165,35 @@ func (cmd *GrepCommand) grepFile(search string, path string) (matches []*Match,

func (cmd *GrepCommand) doMatch(path string, k string, v string, search string) (m []*Match) {
if cmd.Regexp != nil {
return regexpMatch(path, k, v, cmd.Regexp)
return cmd.regexpMatch(path, k, v, cmd.Regexp)
}
return substrMatch(path, k, v, search)
return cmd.substrMatch(path, k, v, search)
}

// find all indices for matches in key and value
func substrMatch(path string, k string, v string, substr string) (m []*Match) {
keyIndex := suffixarray.New([]byte(k))
keyMatches := keyIndex.Lookup([]byte(substr), -1)
sort.Ints(keyMatches)

valueIndex := suffixarray.New([]byte(v))
valueMatches := valueIndex.Lookup([]byte(substr), -1)
sort.Ints(valueMatches)

func (cmd *GrepCommand) substrMatch(path string, k string, v string, substr string) (m []*Match) {
substrLength := len(substr)
keyMatchPairs := make([][]int, 0)
for _, offset := range keyMatches {
keyMatchPairs = append(keyMatchPairs, []int{offset, substrLength})
if cmd.IsMode(ModeKeys) {
keyIndex := suffixarray.New([]byte(k))
keyMatches := keyIndex.Lookup([]byte(substr), -1)
sort.Ints(keyMatches)
for _, offset := range keyMatches {
keyMatchPairs = append(keyMatchPairs, []int{offset, substrLength})
}
}

valueMatchPairs := make([][]int, 0)
for _, offset := range valueMatches {
valueMatchPairs = append(valueMatchPairs, []int{offset, substrLength})
if cmd.IsMode(ModeValues) {
valueIndex := suffixarray.New([]byte(v))
valueMatches := valueIndex.Lookup([]byte(substr), -1)
sort.Ints(valueMatches)
for _, offset := range valueMatches {
valueMatchPairs = append(valueMatchPairs, []int{offset, substrLength})
}
}

if len(keyMatches) > 0 || len(valueMatches) > 0 {
if len(keyMatchPairs) > 0 || len(valueMatchPairs) > 0 {
m = []*Match{
{
path: path,
Expand All @@ -176,13 +205,18 @@ func substrMatch(path string, k string, v string, substr string) (m []*Match) {
},
}
}

return m
}

func regexpMatch(path string, k string, v string, pattern *regexp.Regexp) (m []*Match) {
keyMatches := pattern.FindAllIndex([]byte(k), -1)
valueMatches := pattern.FindAllIndex([]byte(v), -1)
func (cmd *GrepCommand) regexpMatch(path string, k string, v string, pattern *regexp.Regexp) (m []*Match) {
keyMatches := make([][]int, 0)
if cmd.IsMode(ModeKeys) {
keyMatches = pattern.FindAllIndex([]byte(k), -1)
}
valueMatches := make([][]int, 0)
if cmd.IsMode(ModeValues) {
valueMatches = pattern.FindAllIndex([]byte(v), -1)
}

if len(keyMatches) > 0 || len(valueMatches) > 0 {
m = []*Match{
Expand All @@ -196,7 +230,6 @@ func regexpMatch(path string, k string, v string, pattern *regexp.Regexp) (m []*
},
}
}

return m
}

Expand Down
2 changes: 1 addition & 1 deletion completer/completer.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (c *Completer) commandSuggestions(arg string) (result []prompt.Suggest) {
{Text: "append", Description: "append <from> <to> [-f|--force] | [-s|--skip] | [-r|--rename] | -s is default"},
{Text: "rm", Description: "rm <path> | -r is implied"},
{Text: "mv", Description: "mv <from> <to>"},
{Text: "grep", Description: "grep <search> <path> [-e|--regexp]"},
{Text: "grep", Description: "grep <search> <path> [-e|--regexp] [-k|--keys] [-v|--values]"},
{Text: "cat", Description: "cat <path>"},
{Text: "ls", Description: "ls <path>"},
{Text: "toggle-auto-completion", Description: "toggle path auto-completion on/off"},
Expand Down
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ func executor(in string) {
}

if err != nil && cmd != nil {
log.UserError("%v", err)
cmd.PrintUsage()
}

Expand Down
34 changes: 34 additions & 0 deletions test/suites/commands/grep.bats
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ load ../../bin/plugins/bats-assert/load
assert_line --partial "/${KV_BACKEND}/src/dev/1"
assert_line --partial "/${KV_BACKEND}/src/ambivalence/1"

#######################################
echo "==== case: fails on invalid regex pattern ===="
run ${APP_BIN} -c "grep '][' ${KV_BACKEND}/src/dev -e"
assert_line --partial "cannot parse regex"
assert_failure 1

#######################################
echo "==== case: pattern with spaces ===="
run ${APP_BIN} -c "grep 'a spaced val' ${KV_BACKEND}/src/spaces"
Expand All @@ -56,6 +62,34 @@ load ../../bin/plugins/bats-assert/load
run ${APP_BIN} -c "grep \"steve's\" ${KV_BACKEND}/src/apostrophe"
assert_line --partial "/${KV_BACKEND}/src/apostrophe"

#######################################
echo "==== case: no match when only searching keys ===="
run ${APP_BIN} -c "grep 'apple' ${KV_BACKEND}/src/dev/1 -k"
refute_line --partial "/${KV_BACKEND}/src/dev/1"

#######################################
echo "==== case: no match when only searching values ===="
run ${APP_BIN} -c "grep 'fruit' ${KV_BACKEND}/src/dev/1 -v"
refute_line --partial "/${KV_BACKEND}/src/dev/1"

#######################################
echo "==== case: match when only searching keys ===="
run ${APP_BIN} -c "grep 'fruit' ${KV_BACKEND}/src/dev -k"
assert_line --partial "/${KV_BACKEND}/src/dev/1"
assert_line --partial "/${KV_BACKEND}/src/dev/2"
assert_line --partial "/${KV_BACKEND}/src/dev/3"

#######################################
echo "==== case: match when only searching values ===="
run ${APP_BIN} -c "grep 'apple' ${KV_BACKEND}/src/dev -v"
assert_line --partial "/${KV_BACKEND}/src/dev/1"

#######################################
echo "==== case: fails on invalid flag ===="
run ${APP_BIN} -c "grep 'apple' ${KV_BACKEND}/src/dev --foo"
assert_line --partial "invalid flag"
assert_failure 1

#######################################
echo "==== TODO case: grep term on directory with reduced permissions ===="

Expand Down