github.com/fumiama/NanoBot@v0.0.0-20231122134259-c22d8183efca/shell.go (about)

     1  package nano
     2  
     3  import (
     4  	"flag"
     5  	"reflect"
     6  	"strings"
     7  )
     8  
     9  func isSpace(r rune) bool {
    10  	switch r {
    11  	case ' ', '\t', '\r', '\n':
    12  		return true
    13  	}
    14  	return false
    15  }
    16  
    17  type argType int
    18  
    19  const (
    20  	argNo argType = iota
    21  	argSingle
    22  	argQuoted
    23  )
    24  
    25  // ParseShell 将指令转换为指令参数.
    26  // modified from https://github.com/mattn/go-shellwords
    27  func ParseShell(s string) []string {
    28  	var args []string
    29  	buf := strings.Builder{}
    30  	var escaped, doubleQuoted, singleQuoted, backQuote bool
    31  	backtick := ""
    32  
    33  	got := argNo
    34  
    35  	for _, r := range s {
    36  		if escaped {
    37  			buf.WriteRune(r)
    38  			escaped = false
    39  			got = argSingle
    40  			continue
    41  		}
    42  
    43  		if r == '\\' {
    44  			if singleQuoted {
    45  				buf.WriteRune(r)
    46  			} else {
    47  				escaped = true
    48  			}
    49  			continue
    50  		}
    51  
    52  		if isSpace(r) {
    53  			if singleQuoted || doubleQuoted || backQuote {
    54  				buf.WriteRune(r)
    55  				backtick += string(r)
    56  			} else if got != argNo {
    57  				args = append(args, buf.String())
    58  				buf.Reset()
    59  				got = argNo
    60  			}
    61  			continue
    62  		}
    63  
    64  		switch r {
    65  		case '`':
    66  			if !singleQuoted && !doubleQuoted {
    67  				backtick = ""
    68  				backQuote = !backQuote
    69  			}
    70  		case '"':
    71  			if !singleQuoted {
    72  				if doubleQuoted {
    73  					got = argQuoted
    74  				}
    75  				doubleQuoted = !doubleQuoted
    76  			}
    77  		case '\'':
    78  			if !doubleQuoted {
    79  				if singleQuoted {
    80  					got = argSingle
    81  				}
    82  				singleQuoted = !singleQuoted
    83  			}
    84  		default:
    85  			got = argSingle
    86  			buf.WriteRune(r)
    87  			if backQuote {
    88  				backtick += string(r)
    89  			}
    90  		}
    91  	}
    92  
    93  	if got != argNo {
    94  		args = append(args, buf.String())
    95  	}
    96  
    97  	return args
    98  }
    99  
   100  var (
   101  	boolType    = reflect.TypeOf(false)
   102  	intType     = reflect.TypeOf(0)
   103  	stringType  = reflect.TypeOf("")
   104  	float64Type = reflect.TypeOf(float64(0))
   105  )
   106  
   107  func registerFlag(t reflect.Type, v reflect.Value) *flag.FlagSet {
   108  	v = v.Elem()
   109  	fs := flag.NewFlagSet("", flag.ContinueOnError)
   110  	for i := 0; i < t.NumField(); i++ {
   111  		field := t.Field(i)
   112  		name := field.Tag.Get("flag")
   113  		if name == "" {
   114  			continue
   115  		}
   116  		help := field.Tag.Get("help")
   117  		switch field.Type {
   118  		case boolType:
   119  			fs.BoolVar(v.Field(i).Addr().Interface().(*bool), name, false, help)
   120  		case intType:
   121  			fs.IntVar(v.Field(i).Addr().Interface().(*int), name, 0, help)
   122  		case stringType:
   123  			fs.StringVar(v.Field(i).Addr().Interface().(*string), name, "", help)
   124  		case float64Type:
   125  			fs.Float64Var(v.Field(i).Addr().Interface().(*float64), name, 0, help)
   126  		default:
   127  			panic("unsupported type")
   128  		}
   129  	}
   130  	return fs
   131  }