go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/processes/flag.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package processes 5 6 import ( 7 "errors" 8 "fmt" 9 "strings" 10 11 "github.com/kballard/go-shellquote" 12 ) 13 14 // Flagset is derived from Go's internal flagset, licensed MIT 15 type FlagSet struct { 16 name string 17 parsed bool 18 actual map[string]string 19 args []string // arguments after flags 20 } 21 22 func (f *FlagSet) ParseCommand(cmd string) error { 23 if cmd == "" { 24 return errors.New("no command provided") 25 } 26 words, err := shellquote.Split(cmd) 27 if err != nil { 28 return err 29 } 30 args := words[1:] 31 32 // NOTE: it is impossible to do flag parsing correct without having the context of the binary 33 // - `--name=x` is pretty clear where name is the key and x is the value 34 // `--name x` here name is the key, but x could be the value or an arg, if name is a boolean flag x will not be the value 35 // 36 // Therefore we work with the assumption that if `--arg` is followed by a flag without `-` prefix, we count this as a value 37 preparedArgs := []string{} 38 n := len(args) 39 for i := 0; i < n; i++ { 40 key := args[i] 41 if strings.HasPrefix(key, "-") { 42 if i+1 < n && !strings.HasPrefix(args[i+1], "-") { 43 preparedArgs = append(preparedArgs, key+"="+args[i+1]) 44 i++ 45 continue 46 } 47 } 48 preparedArgs = append(preparedArgs, key) 49 } 50 51 return f.Parse(preparedArgs) 52 } 53 54 func (f *FlagSet) Parse(args []string) error { 55 f.parsed = true 56 f.args = args 57 58 if f.actual == nil { 59 f.actual = make(map[string]string) 60 } 61 62 for { 63 seen, err := f.parseOneArg() 64 if seen { 65 continue 66 } 67 if err == nil { 68 break 69 } 70 return err 71 } 72 return nil 73 } 74 75 func (f *FlagSet) parseOneArg() (bool, error) { 76 if len(f.args) == 0 { 77 return false, nil 78 } 79 s := f.args[0] 80 81 if len(s) < 2 || s[0] != '-' { 82 f.args = f.args[1:] 83 f.actual[s] = "" 84 return true, nil 85 } 86 numMinuses := 1 87 if s[1] == '-' { 88 numMinuses++ 89 if len(s) == 2 { // "--" terminates the flags 90 f.args = f.args[1:] 91 return false, nil 92 } 93 } 94 name := s[numMinuses:] 95 if len(name) == 0 || name[0] == '-' || name[0] == '=' { 96 return false, fmt.Errorf("bad flag syntax: %s", s) 97 } 98 99 // it's a flag. does it have an argument? 100 f.args = f.args[1:] 101 value := "" 102 for i := 1; i < len(name); i++ { // equals cannot be first 103 if name[i] == '=' { 104 value = name[i+1:] 105 name = name[0:i] 106 break 107 } 108 } 109 name = strings.ToLower(name) 110 f.actual[name] = value 111 return true, nil 112 } 113 114 func (f *FlagSet) Map() map[string]string { 115 return f.actual 116 }