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 }