github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/builtins/core/management/which.go (about)

     1  package management
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strings"
     8  
     9  	"github.com/lmorg/murex/builtins/docs"
    10  	"github.com/lmorg/murex/lang"
    11  	"github.com/lmorg/murex/lang/types"
    12  	"github.com/lmorg/murex/shell/hintsummary"
    13  	"github.com/lmorg/murex/utils/escape"
    14  	"github.com/lmorg/murex/utils/man"
    15  	"github.com/lmorg/murex/utils/which"
    16  )
    17  
    18  func init() {
    19  	lang.DefineFunction("which", cmdWhich, types.String)
    20  }
    21  
    22  func cmdWhich(p *lang.Process) error {
    23  	if p.Stdout.IsTTY() {
    24  		return cmdWhichTty(p)
    25  	}
    26  
    27  	return cmdWhichFunction(p)
    28  }
    29  
    30  func cmdWhichTty(p *lang.Process) error {
    31  	p.Stdout.SetDataType(types.String)
    32  	cmds := p.Parameters.StringArray()
    33  	if len(cmds) == 0 {
    34  		return whichUsage(p)
    35  	}
    36  
    37  	for i := range cmds {
    38  		s := whichTtyString(p, cmds[i])
    39  		if len(s) == 0 {
    40  			p.Stdout.Writeln([]byte(fmt.Sprintf("%s => unknown", cmds[i])))
    41  			p.ExitNum = 1
    42  			continue
    43  		}
    44  
    45  		p.Stdout.Writeln([]byte(fmt.Sprintf("%s %s", cmds[i], s)))
    46  	}
    47  
    48  	return nil
    49  }
    50  
    51  const whichUsageMessage = "no commands specified"
    52  
    53  func whichUsage(p *lang.Process) error {
    54  	_, err := p.Stderr.Writeln([]byte(whichUsageMessage))
    55  	p.ExitNum = 1
    56  	return err
    57  }
    58  
    59  /////
    60  
    61  func whichTtyString(p *lang.Process, cmd string) (s string) {
    62  	summary := hintsummary.Summary.Get(cmd)
    63  	var (
    64  		aliasPrefix string
    65  		exists      bool
    66  		t           = typeOf(p, cmd)
    67  	)
    68  
    69  	if t == typeAlias {
    70  		a := lang.GlobalAliases.Get(cmd)
    71  		alias := make([]string, len(a))
    72  		copy(alias, a)
    73  		escape.CommandLine(alias)
    74  		args := strings.Join(alias, " ")
    75  		aliasPrefix = fmt.Sprintf("=> (%s) %s ", t, args)
    76  		cmd = alias[0]
    77  		exists = true
    78  	}
    79  
    80  	if t == typePrivate {
    81  		if summary == "" {
    82  			summary, _ = lang.PrivateFunctions.Summary(cmd, p.FileRef)
    83  		}
    84  		if summary == "" {
    85  			summary = "no summary written"
    86  		}
    87  		return fmt.Sprintf("%s=> (%s) %s", aliasPrefix, t, summary)
    88  	}
    89  
    90  	if t == typeFunction {
    91  		if summary == "" {
    92  			summary, _ = lang.MxFunctions.Summary(cmd)
    93  		}
    94  		if summary == "" {
    95  			summary = "no summary written"
    96  		}
    97  		return fmt.Sprintf("%s=> (%s) %s", aliasPrefix, t, summary)
    98  	}
    99  
   100  	if t == typeBuiltin {
   101  		if summary == "" {
   102  			synonym := docs.Synonym[cmd]
   103  			summary = docs.Summary[synonym]
   104  		}
   105  		if summary == "" {
   106  			summary = "no doc written"
   107  		}
   108  		return fmt.Sprintf("%s=> (%s) %s", aliasPrefix, t, summary)
   109  	}
   110  
   111  	path := which.Which(cmd)
   112  	if path != "" {
   113  		if summary == "" {
   114  			summary = man.ParseSummary(man.GetManPages(cmd))
   115  		}
   116  		if summary == "" {
   117  			summary = "no man page found"
   118  		}
   119  		path = filepath.Clean(path)
   120  		if resolved, err := os.Readlink(path); err == nil {
   121  			path = path + " -> " + filepath.Clean(resolved)
   122  		}
   123  		return fmt.Sprintf("%s=> (%s) %s", aliasPrefix, path, summary)
   124  	}
   125  
   126  	if exists {
   127  		return aliasPrefix
   128  	}
   129  	return ""
   130  }
   131  
   132  /////
   133  
   134  func cmdWhichFunction(p *lang.Process) error {
   135  	p.Stdout.SetDataType(types.String)
   136  
   137  	cmds := p.Parameters.StringArray()
   138  	if len(cmds) == 0 {
   139  		return whichUsage(p)
   140  	}
   141  
   142  	var success bool
   143  	for i := range cmds {
   144  		s := whichFunctionString(p, cmds[i])
   145  		p.Stdout.Writeln([]byte(s))
   146  		if s != typeUnknown {
   147  			success = true
   148  		}
   149  	}
   150  
   151  	if !success {
   152  		p.ExitNum = 1
   153  	}
   154  	return nil
   155  }
   156  
   157  func whichFunctionString(p *lang.Process, cmd string) (s string) {
   158  	switch {
   159  	case lang.GlobalAliases.Exists(cmd):
   160  		return typeAlias
   161  
   162  	case lang.PrivateFunctions.Exists(cmd, p.FileRef):
   163  		return typePrivate
   164  
   165  	case lang.MxFunctions.Exists(cmd):
   166  		return typeFunction
   167  
   168  	case lang.GoFunctions[cmd] != nil:
   169  		return typeBuiltin
   170  
   171  	default:
   172  		path := which.Which(cmd)
   173  		if path != "" {
   174  			return path
   175  		}
   176  		return typeUnknown
   177  	}
   178  }