Skip to content
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
58 changes: 36 additions & 22 deletions which_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,47 @@ EXAMPLES

// WhichExec implements the 'which' command.
func WhichExec(e *Entrypoint, args, _ []string) error {
if cmd, _, err := e.Identify(args); err != nil {
identifyArgs, willResolveSymlinks := splitWhichArgs(args)

cmd, _, err := e.Identify(identifyArgs)
if err != nil {
return err
} else if IsNull(cmd) {
return exit.ErrUnknownSubcommand
} else {
willResolveSymlinks := false

for _, arg := range args {
if arg == "--" {
break
} else if arg == "--follow-symlinks" || arg == "-s" {
willResolveSymlinks = true
}
}
}

path := cmd.Path()
if willResolveSymlinks {
resolvedPath, err := filepath.EvalSymlinks(path)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Unable to follow symlinks in %s\n", path)
return err
} else {
path = resolvedPath
}
path := cmd.Path()
if willResolveSymlinks {
resolvedPath, err := filepath.EvalSymlinks(path)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: Unable to follow symlinks in %s\n", path)
return err
}
path = resolvedPath
}

fmt.Println(path)
return nil
fmt.Println(path)
return nil
}

// splitWhichArgs separates which's own --follow-symlinks/-s flags from the
// arguments that identify the command. The flags are consumed here rather than
// forwarded to Identify: left in, a flag becomes a trailing argument and causes
// Identify to resolve the command's default subcommand instead of the command
// itself. Everything after a `--` terminator is left untouched for the command.
func splitWhichArgs(args []string) (identifyArgs []string, followSymlinks bool) {
identifyArgs = make([]string, 0, len(args))

for i, arg := range args {
if arg == "--" {
identifyArgs = append(identifyArgs, args[i:]...)
break
} else if arg == "--follow-symlinks" || arg == "-s" {
followSymlinks = true
} else {
identifyArgs = append(identifyArgs, arg)
}
}

return identifyArgs, followSymlinks
}
34 changes: 34 additions & 0 deletions which_cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package exoskeleton

import (
"testing"

"github.com/stretchr/testify/assert"
)

// -s/--follow-symlinks are which's own flags. They must be consumed by which
// and not forwarded to Identify -- regardless of whether they appear before or
// after the command name -- since a trailing flag would make Identify resolve
// the command's default subcommand instead of the command itself.
func TestWhichConsumesFollowSymlinksFlag(t *testing.T) {
scenarios := []struct {
args []string
wantIdentifyArgs []string
wantFollowSymlinks bool
}{
{[]string{"tool"}, []string{"tool"}, false},
{[]string{"tool", "-s"}, []string{"tool"}, true},
{[]string{"tool", "--follow-symlinks"}, []string{"tool"}, true},
{[]string{"-s", "tool"}, []string{"tool"}, true},
{[]string{"--follow-symlinks", "tool"}, []string{"tool"}, true},

// Everything after `--` is left for the command, flag or not.
{[]string{"tool", "--", "-s"}, []string{"tool", "--", "-s"}, false},
}

for _, s := range scenarios {
identifyArgs, followSymlinks := splitWhichArgs(s.args)
assert.Equal(t, s.wantIdentifyArgs, identifyArgs, "which %v", s.args)
assert.Equal(t, s.wantFollowSymlinks, followSymlinks, "which %v", s.args)
}
}
Loading