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  }