mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-23 21:51:30 +01:00
checkpoint on compgen
This commit is contained in:
parent
848f7164a3
commit
055dc7c8ac
@ -20,10 +20,12 @@ import (
|
||||
"github.com/scripthaus-dev/mshell/pkg/base"
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
"github.com/scripthaus-dev/mshell/pkg/shexec"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/comp"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/remote"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/scbase"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/scpacket"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/sstore"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/utilfn"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -32,6 +34,11 @@ const (
|
||||
HistoryTypeGlobal = "global"
|
||||
)
|
||||
|
||||
func init() {
|
||||
comp.RegisterSimpleCompFn("meta", simpleCompMeta)
|
||||
comp.RegisterSimpleCompFn("command+meta", simpleCompCommandMeta)
|
||||
}
|
||||
|
||||
const DefaultUserId = "sawka"
|
||||
const MaxNameLen = 50
|
||||
const MaxRemoteAliasLen = 50
|
||||
@ -1043,50 +1050,6 @@ func updateHistoryContext(ctx context.Context, line *sstore.LineType, cmd *sstor
|
||||
}
|
||||
}
|
||||
|
||||
func getStrArr(v interface{}, field string) []string {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
m, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
fieldVal := m[field]
|
||||
if fieldVal == nil {
|
||||
return nil
|
||||
}
|
||||
iarr, ok := fieldVal.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
var sarr []string
|
||||
for _, iv := range iarr {
|
||||
if sv, ok := iv.(string); ok {
|
||||
sarr = append(sarr, sv)
|
||||
}
|
||||
}
|
||||
return sarr
|
||||
}
|
||||
|
||||
func getBool(v interface{}, field string) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
m, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
fieldVal := m[field]
|
||||
if fieldVal == nil {
|
||||
return false
|
||||
}
|
||||
bval, ok := fieldVal.(bool)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return bval
|
||||
}
|
||||
|
||||
func makeInfoFromComps(compType string, comps []string, hasMore bool) sstore.UpdatePacket {
|
||||
sort.Slice(comps, func(i int, j int) bool {
|
||||
c1 := comps[i]
|
||||
@ -1156,6 +1119,23 @@ func longestPrefix(root string, comps []string) string {
|
||||
return lcp
|
||||
}
|
||||
|
||||
func simpleCompMeta(ctx context.Context, prefix string, compCtx comp.CompContext, args []interface{}) (*comp.CompReturn, error) {
|
||||
compsCmd, _ := comp.DoSimpleComp(ctx, "command", prefix, compCtx, nil)
|
||||
compsMeta, _ := simpleCompCommandMeta(ctx, prefix, compCtx, nil)
|
||||
return comp.CombineCompReturn(compsCmd, compsMeta), nil
|
||||
}
|
||||
|
||||
func simpleCompCommandMeta(ctx context.Context, prefix string, compCtx comp.CompContext, args []interface{}) (*comp.CompReturn, error) {
|
||||
rtn := comp.CompReturn{}
|
||||
validCommands := getValidCommands()
|
||||
for _, cmd := range validCommands {
|
||||
if strings.HasPrefix(cmd, prefix) {
|
||||
rtn.Entries = append(rtn.Entries, comp.CompEntry{Word: cmd, IsMetaCmd: true})
|
||||
}
|
||||
}
|
||||
return &rtn, nil
|
||||
}
|
||||
|
||||
func doMetaCompGen(ctx context.Context, pk *scpacket.FeCommandPacketType, prefix string, forDisplay bool) ([]string, bool, error) {
|
||||
ids, err := resolveUiIds(ctx, pk, 0) // best effort
|
||||
var comps []string
|
||||
@ -1202,8 +1182,8 @@ func doCompGen(ctx context.Context, pk *scpacket.FeCommandPacketType, prefix str
|
||||
if err = resp.Err(); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
comps := getStrArr(resp.Data, "comps")
|
||||
hasMore := getBool(resp.Data, "hasmore")
|
||||
comps := utilfn.GetStrArr(resp.Data, "comps")
|
||||
hasMore := utilfn.GetBool(resp.Data, "hasmore")
|
||||
return comps, hasMore, nil
|
||||
}
|
||||
|
||||
@ -1743,7 +1723,7 @@ func displayStateUpdateDiff(buf *bytes.Buffer, oldState packet.ShellState, newSt
|
||||
if newVal.IsExport() {
|
||||
exportStr = "export "
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%s%s=%s\n", exportStr, key, ShellQuote(newVal.Value, false, 50)))
|
||||
buf.WriteString(fmt.Sprintf("%s%s=%s\n", exportStr, key, utilfn.ShellQuote(newVal.Value, false, 50)))
|
||||
}
|
||||
}
|
||||
for key, _ := range oldEnvMap {
|
||||
|
@ -6,9 +6,9 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/alessio/shellescape"
|
||||
"github.com/scripthaus-dev/mshell/pkg/shexec"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/scpacket"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/utilfn"
|
||||
"mvdan.cc/sh/v3/expand"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
@ -116,31 +116,6 @@ func onlyRawArgs(metaCmd string, metaSubCmd string) bool {
|
||||
return metaCmd == "run" || metaCmd == "comment"
|
||||
}
|
||||
|
||||
// minimum maxlen=6
|
||||
func ShellQuote(val string, forceQuote bool, maxLen int) string {
|
||||
if maxLen < 6 {
|
||||
maxLen = 6
|
||||
}
|
||||
rtn := shellescape.Quote(val)
|
||||
if strings.HasPrefix(rtn, "\"") || strings.HasPrefix(rtn, "'") {
|
||||
if len(rtn) > maxLen {
|
||||
return rtn[0:maxLen-4] + "..." + rtn[0:1]
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
if forceQuote {
|
||||
if len(rtn) > maxLen-2 {
|
||||
return "\"" + rtn[0:maxLen-5] + "...\""
|
||||
}
|
||||
return "\"" + rtn + "\""
|
||||
} else {
|
||||
if len(rtn) > maxLen {
|
||||
return rtn[0:maxLen-3] + "..."
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
}
|
||||
|
||||
func setBracketArgs(argMap map[string]string, bracketStr string) error {
|
||||
bracketStr = strings.TrimSpace(bracketStr)
|
||||
if bracketStr == "" {
|
||||
@ -161,7 +136,7 @@ func setBracketArgs(argMap map[string]string, bracketStr string) error {
|
||||
varVal = litStr[eqIdx+1:]
|
||||
}
|
||||
if !shexec.IsValidBashIdentifier(varName) {
|
||||
wordErr = fmt.Errorf("invalid identifier %s in bracket args", ShellQuote(varName, true, 20))
|
||||
wordErr = fmt.Errorf("invalid identifier %s in bracket args", utilfn.ShellQuote(varName, true, 20))
|
||||
return false
|
||||
}
|
||||
if varVal == "" {
|
||||
|
169
pkg/comp/comp.go
169
pkg/comp/comp.go
@ -2,17 +2,23 @@
|
||||
package comp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
"github.com/scripthaus-dev/mshell/pkg/shexec"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/sstore"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/utilfn"
|
||||
"mvdan.cc/sh/v3/syntax"
|
||||
)
|
||||
|
||||
const MaxCompQuoteLen = 5000
|
||||
|
||||
const (
|
||||
SimpleCompGenTypeFile = "file"
|
||||
SimpleCompGenTypeDir = "dir"
|
||||
@ -24,19 +30,31 @@ const (
|
||||
SimpleCompGenTypeVariable = "variable"
|
||||
)
|
||||
|
||||
const (
|
||||
QuoteTypeLiteral = ""
|
||||
QuoteTypeDQ = "\""
|
||||
QuoteTypeANSI = "$'"
|
||||
QuoteTypeSQ = "'"
|
||||
)
|
||||
|
||||
type CompContext struct {
|
||||
RemotePtr sstore.RemotePtrType
|
||||
State *packet.ShellState
|
||||
ForDisplay bool
|
||||
}
|
||||
|
||||
type SimpleCompGenFnType = func(ctx context.Context, point SimpleCompPoint, compCtx CompContext, args []interface{}) (*CompReturn, error)
|
||||
|
||||
type SimpleCompPoint struct {
|
||||
Word string
|
||||
Pos int
|
||||
}
|
||||
|
||||
type fullCompPrefix struct {
|
||||
RawStr string
|
||||
RawPos int
|
||||
CompPrefix string
|
||||
QuoteTypePref string
|
||||
}
|
||||
|
||||
type ParsedWord struct {
|
||||
Offset int
|
||||
Word *syntax.Word
|
||||
@ -53,6 +71,75 @@ type CompPoint struct {
|
||||
Suffix string
|
||||
}
|
||||
|
||||
// directories will have a trailing "/"
|
||||
type CompEntry struct {
|
||||
Word string
|
||||
IsMetaCmd bool
|
||||
}
|
||||
|
||||
type CompReturn struct {
|
||||
Entries []CompEntry
|
||||
HasMore bool
|
||||
}
|
||||
|
||||
func compQuoteDQString(s string, close bool) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('"')
|
||||
for _, ch := range s {
|
||||
if ch == '"' || ch == '\\' || ch == '$' || ch == '`' {
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteRune(ch)
|
||||
continue
|
||||
}
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
if close {
|
||||
buf.WriteByte('"')
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func compQuoteString(s string, quoteType string, close bool) string {
|
||||
if quoteType != QuoteTypeANSI {
|
||||
for _, ch := range s {
|
||||
if ch > unicode.MaxASCII || !unicode.IsPrint(ch) || ch == '!' {
|
||||
quoteType = QuoteTypeANSI
|
||||
break
|
||||
}
|
||||
if ch == '\'' {
|
||||
if quoteType == QuoteTypeSQ || quoteType == QuoteTypeLiteral {
|
||||
quoteType = QuoteTypeANSI
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if quoteType == QuoteTypeANSI {
|
||||
rtn := strconv.QuoteToASCII(s)
|
||||
rtn = "$'" + strings.ReplaceAll(rtn[1:len(rtn)-1], "'", "\\'")
|
||||
if close {
|
||||
rtn = rtn + "'"
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
if quoteType == QuoteTypeLiteral {
|
||||
rtn := utilfn.ShellQuote(s, false, MaxCompQuoteLen)
|
||||
if len(rtn) > 0 && rtn[0] == '\'' && !close {
|
||||
rtn = rtn[0 : len(rtn)-1]
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
if quoteType == QuoteTypeSQ {
|
||||
rtn := utilfn.ShellQuote(s, true, MaxCompQuoteLen)
|
||||
if !close {
|
||||
rtn = rtn[0 : len(rtn)-1]
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
// QuoteTypeDQ
|
||||
return compQuoteDQString(s, close)
|
||||
}
|
||||
|
||||
func (p *CompPoint) wordAsStr(w ParsedWord) string {
|
||||
if w.Word != nil {
|
||||
return p.StmtStr[w.Word.Pos().Offset():w.Word.End().Offset()]
|
||||
@ -63,16 +150,29 @@ func (p *CompPoint) wordAsStr(w ParsedWord) string {
|
||||
func (p *CompPoint) simpleExpandWord(w ParsedWord) string {
|
||||
ectx := shexec.SimpleExpandContext{}
|
||||
if w.Word != nil {
|
||||
return SimpleExpandWord(ectx, w.Word, p.StmtStr)
|
||||
return shexec.SimpleExpandWord(ectx, w.Word, p.StmtStr)
|
||||
}
|
||||
return SimpleExpandPartialWord(ectx, p.PartialWord, false)
|
||||
return shexec.SimpleExpandPartialWord(ectx, w.PartialWord, false)
|
||||
}
|
||||
|
||||
func (p *CompPoint) compPrefix() string {
|
||||
pword := p.Words[p.CompWord]
|
||||
func getQuoteTypePref(str string) string {
|
||||
if strings.HasPrefix(str, QuoteTypeANSI) {
|
||||
return QuoteTypeANSI
|
||||
}
|
||||
if strings.HasPrefix(str, QuoteTypeDQ) {
|
||||
return QuoteTypeDQ
|
||||
}
|
||||
if strings.HasPrefix(str, QuoteTypeSQ) {
|
||||
return QuoteTypeSQ
|
||||
}
|
||||
return QuoteTypeLiteral
|
||||
}
|
||||
|
||||
func (p *CompPoint) getCompPrefix() string {
|
||||
if p.CompWordPos == 0 {
|
||||
return ""
|
||||
}
|
||||
pword := p.Words[p.CompWord]
|
||||
wordStr := p.wordAsStr(pword)
|
||||
if p.CompWordPos == len(wordStr) {
|
||||
return p.simpleExpandWord(pword)
|
||||
@ -82,7 +182,21 @@ func (p *CompPoint) compPrefix() string {
|
||||
// and a partial on just the current part. this is an uncommon case though
|
||||
// and has very little upside (even bash does not expand multipart words correctly)
|
||||
partialWordStr := wordStr[:p.CompWordPos]
|
||||
return SimpleExpandPartialWord(shexec.SimpleExpandContext{}, partialWordStr, false)
|
||||
return shexec.SimpleExpandPartialWord(shexec.SimpleExpandContext{}, partialWordStr, false)
|
||||
}
|
||||
|
||||
func (p *CompPoint) extendWord(newWord string, newWordComplete bool) (string, int) {
|
||||
pword := p.Words[p.CompWord]
|
||||
wordStr := p.wordAsStr(pword)
|
||||
quotePref := getQuoteTypePref(wordStr)
|
||||
needsClose := newWordComplete && (len(wordStr) == p.CompWordPos)
|
||||
wordSuffix := wordStr[p.CompWordPos:]
|
||||
newQuotedStr := compQuoteString(newWord, quotePref, needsClose)
|
||||
if needsClose && wordSuffix == "" {
|
||||
newQuotedStr = newQuotedStr + " "
|
||||
}
|
||||
newPos := len(newQuotedStr)
|
||||
return newQuotedStr + wordSuffix, newPos
|
||||
}
|
||||
|
||||
func (p *CompPoint) dump() {
|
||||
@ -107,17 +221,6 @@ func (p *CompPoint) dump() {
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
|
||||
// directories will have a trailing "/"
|
||||
type CompEntry struct {
|
||||
Word string
|
||||
IsMetaCmd bool
|
||||
}
|
||||
|
||||
type CompReturn struct {
|
||||
Entries []CompEntry
|
||||
HasMore bool
|
||||
}
|
||||
|
||||
var SimpleCompGenFns map[string]SimpleCompGenFnType
|
||||
|
||||
func strWithCursor(str string, pos int) string {
|
||||
@ -229,7 +332,6 @@ func ParseCompPoint(fullCmdStr string, pos int) (*CompPoint, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
rtnPoint.dump()
|
||||
return &rtnPoint, nil
|
||||
}
|
||||
|
||||
@ -253,3 +355,32 @@ func splitCompWord(p *CompPoint) {
|
||||
func DoCompGen(ctx context.Context, point CompPoint, rptr sstore.RemotePtrType, state *packet.ShellState) (*CompReturn, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func SortCompReturnEntries(c *CompReturn) {
|
||||
sort.Slice(c.Entries, func(i int, j int) bool {
|
||||
e1 := c.Entries[i]
|
||||
e2 := c.Entries[j]
|
||||
if e1.Word < e2.Word {
|
||||
return true
|
||||
}
|
||||
if e1.Word == e2.Word && e1.IsMetaCmd && !e2.IsMetaCmd {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func CombineCompReturn(c1 *CompReturn, c2 *CompReturn) *CompReturn {
|
||||
if c1 == nil {
|
||||
return c2
|
||||
}
|
||||
if c2 == nil {
|
||||
return c1
|
||||
}
|
||||
var rtn CompReturn
|
||||
rtn.HasMore = c1.HasMore || c2.HasMore
|
||||
rtn.Entries = append([]CompEntry{}, c1.Entries...)
|
||||
rtn.Entries = append(rtn.Entries, c2.Entries...)
|
||||
SortCompReturnEntries(&rtn)
|
||||
return &rtn
|
||||
}
|
||||
|
@ -3,12 +3,47 @@ package comp
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/scripthaus-dev/mshell/pkg/packet"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/remote"
|
||||
"github.com/scripthaus-dev/sh2-server/pkg/utilfn"
|
||||
)
|
||||
|
||||
var globalLock = &sync.Mutex{}
|
||||
var simpleCompMap = map[string]SimpleCompGenFnType{
|
||||
"file": simpleCompFile,
|
||||
"directory": simpleCompDir,
|
||||
"variable": simpleCompVar,
|
||||
"command": simpleCompCommand,
|
||||
}
|
||||
|
||||
type SimpleCompGenFnType = func(ctx context.Context, prefix string, compCtx CompContext, args []interface{}) (*CompReturn, error)
|
||||
|
||||
func RegisterSimpleCompFn(compType string, fn SimpleCompGenFnType) {
|
||||
globalLock.Lock()
|
||||
defer globalLock.Unlock()
|
||||
if _, ok := simpleCompMap[compType]; ok {
|
||||
panic(fmt.Sprintf("simpleCompFn %q already registered", compType))
|
||||
}
|
||||
simpleCompMap[compType] = fn
|
||||
}
|
||||
|
||||
func getSimpleCompFn(compType string) SimpleCompGenFnType {
|
||||
globalLock.Lock()
|
||||
defer globalLock.Unlock()
|
||||
return simpleCompMap[compType]
|
||||
}
|
||||
|
||||
func DoSimpleComp(ctx context.Context, compType string, prefix string, compCtx CompContext, args []interface{}) (*CompReturn, error) {
|
||||
compFn := getSimpleCompFn(compType)
|
||||
if compFn == nil {
|
||||
return nil, fmt.Errorf("no simple comp fn for %q", compType)
|
||||
}
|
||||
return compFn(ctx, prefix, compCtx, args)
|
||||
}
|
||||
|
||||
func compsToCompReturn(comps []string, hasMore bool) *CompReturn {
|
||||
var rtn CompReturn
|
||||
rtn.HasMore = hasMore
|
||||
@ -18,13 +53,13 @@ func compsToCompReturn(comps []string, hasMore bool) *CompReturn {
|
||||
return &rtn
|
||||
}
|
||||
|
||||
func doCompGen(ctx context.Context, compCtx CompContext, prefix string, compType string, forDisplay bool) (*CompReturn, error) {
|
||||
func doCompGen(ctx context.Context, prefix string, compType string, compCtx CompContext) (*CompReturn, error) {
|
||||
if !packet.IsValidCompGenType(compType) {
|
||||
return nil, false, fmt.Errorf("/compgen invalid type '%s'", compType)
|
||||
return nil, fmt.Errorf("/compgen invalid type '%s'", compType)
|
||||
}
|
||||
msh := remote.GetRemoteById(compCtx.RemotePtr.RemoteId)
|
||||
if msh == nil {
|
||||
return nil, false, fmt.Errorf("invalid remote '%s', not found", compCtx.RemotePtr)
|
||||
return nil, fmt.Errorf("invalid remote '%s', not found", compCtx.RemotePtr)
|
||||
}
|
||||
cgPacket := packet.MakeCompGenPacket()
|
||||
cgPacket.ReqId = uuid.New().String()
|
||||
@ -32,23 +67,29 @@ func doCompGen(ctx context.Context, compCtx CompContext, prefix string, compType
|
||||
cgPacket.Prefix = prefix
|
||||
cgPacket.Cwd = compCtx.State.Cwd
|
||||
resp, err := msh.PacketRpc(ctx, cgPacket)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if err = resp.Err(); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
comps := getStrArr(resp.Data, "comps")
|
||||
hasMore := getBool(resp.Data, "hasmore")
|
||||
return compsToCompReturn(conmps, hasMore), nil
|
||||
}
|
||||
|
||||
func SimpleCompFile(ctx context.Context, point SimpleCompPoint, compCtx CompContext, args []interface{}) (*CompReturn, error) {
|
||||
pword := point.Words[p.CompWord]
|
||||
prefix := ""
|
||||
crtn, err := doCompGen(ctx, prefix, "file")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return crtn, nil
|
||||
if err = resp.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
comps := utilfn.GetStrArr(resp.Data, "comps")
|
||||
hasMore := utilfn.GetBool(resp.Data, "hasmore")
|
||||
return compsToCompReturn(comps, hasMore), nil
|
||||
}
|
||||
|
||||
func simpleCompFile(ctx context.Context, prefix string, compCtx CompContext, args []interface{}) (*CompReturn, error) {
|
||||
return doCompGen(ctx, prefix, "file", compCtx)
|
||||
}
|
||||
|
||||
func simpleCompDir(ctx context.Context, prefix string, compCtx CompContext, args []interface{}) (*CompReturn, error) {
|
||||
return doCompGen(ctx, prefix, "directory", compCtx)
|
||||
}
|
||||
|
||||
func simpleCompVar(ctx context.Context, prefix string, compCtx CompContext, args []interface{}) (*CompReturn, error) {
|
||||
return doCompGen(ctx, prefix, "variable", compCtx)
|
||||
}
|
||||
|
||||
func simpleCompCommand(ctx context.Context, prefix string, compCtx CompContext, args []interface{}) (*CompReturn, error) {
|
||||
return doCompGen(ctx, prefix, "command", compCtx)
|
||||
}
|
||||
|
76
pkg/utilfn/utilfn.go
Normal file
76
pkg/utilfn/utilfn.go
Normal file
@ -0,0 +1,76 @@
|
||||
package utilfn
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/alessio/shellescape"
|
||||
)
|
||||
|
||||
func GetStrArr(v interface{}, field string) []string {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
m, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
fieldVal := m[field]
|
||||
if fieldVal == nil {
|
||||
return nil
|
||||
}
|
||||
iarr, ok := fieldVal.([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
var sarr []string
|
||||
for _, iv := range iarr {
|
||||
if sv, ok := iv.(string); ok {
|
||||
sarr = append(sarr, sv)
|
||||
}
|
||||
}
|
||||
return sarr
|
||||
}
|
||||
|
||||
func GetBool(v interface{}, field string) bool {
|
||||
if v == nil {
|
||||
return false
|
||||
}
|
||||
m, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
fieldVal := m[field]
|
||||
if fieldVal == nil {
|
||||
return false
|
||||
}
|
||||
bval, ok := fieldVal.(bool)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return bval
|
||||
}
|
||||
|
||||
// minimum maxlen=6
|
||||
func ShellQuote(val string, forceQuote bool, maxLen int) string {
|
||||
if maxLen < 6 {
|
||||
maxLen = 6
|
||||
}
|
||||
rtn := shellescape.Quote(val)
|
||||
if strings.HasPrefix(rtn, "\"") || strings.HasPrefix(rtn, "'") {
|
||||
if len(rtn) > maxLen {
|
||||
return rtn[0:maxLen-4] + "..." + rtn[0:1]
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
if forceQuote {
|
||||
if len(rtn) > maxLen-2 {
|
||||
return "\"" + rtn[0:maxLen-5] + "...\""
|
||||
}
|
||||
return "\"" + rtn + "\""
|
||||
} else {
|
||||
if len(rtn) > maxLen {
|
||||
return rtn[0:maxLen-3] + "..."
|
||||
}
|
||||
return rtn
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user