cmd/wfnconvert/options.go (188 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"errors"
"flag"
"fmt"
"strconv"
"strings"
"github.com/facebookincubator/nvdtools/wfn"
)
type intFields []int
// Set is part of flag.Value interface
func (f *intFields) Set(s string) error {
if f == nil {
return errors.New("Set called on nil reciever")
}
for _, t := range strings.Split(s, ",") {
i, err := strconv.Atoi(t)
if err != nil {
return fmt.Errorf("failed to parse fields from %q: %v", s, err)
}
if i <= 0 {
return fmt.Errorf("illegal CSV index %d: %q", i, s)
}
*f = append(*f, i-1)
}
return nil
}
// String is part of flag.Value interface
func (f *intFields) String() string {
if f == nil {
return ""
}
var sb strings.Builder
for i, n := range *f {
if i != 0 {
sb.WriteByte(',')
}
fmt.Fprintf(&sb, "%d", n+1)
}
return sb.String()
}
type strFields map[string]bool
// Set is part of flag.Value interface
func (f *strFields) Set(s string) error {
if *f == nil {
*f = make(strFields)
}
for _, s := range strings.Split(s, ",") {
(*f)[s] = true
}
return nil
}
// String is part of flag.Value interface
func (f *strFields) String() string {
if f == nil {
return ""
}
var sb strings.Builder
i := 0
for k := range *f {
if i != 0 {
sb.WriteByte(',')
}
sb.WriteString(k)
i++
}
return sb.String()
}
type options struct {
outBinding string
attributes strFields
invertAttributes bool
any2na, na2any bool
csvFields intFields
csvComma string
}
func (o *options) addFlags() {
flag.StringVar(&o.outBinding, "b", "fstr", "ouput bindings, one of\n"+
"'uri'\te.g. cpe:/a:foo:bar:1.1)\n"+
"'fstr'\te.g. cpe:2.3:a:foo:bar:1.1:*:*:*:*:*:*:*)\n"+
"'str'\te.g. "+`wfn:[part="a",vendor="foo",product="bar", version="1\.1"])`+"\n")
flag.Var(&o.attributes, "attr", "comma-separated list of WFN attributes to manipulate; CPE 2.3 defines following attributes:\n"+
"part, vendor, product, version, update, edition, language, sw_edition, target_sw, target_hw, other\n"+
"additionally a special value 'all' is accepted")
flag.BoolVar(&o.invertAttributes, "v", false, "invert attributes: process attributes not matched by -v flag")
flag.BoolVar(&o.any2na, "any2na", false, "assign logical value N/A to all attributes with logical value ANY")
flag.BoolVar(&o.na2any, "na2any", false, "assign logical value ANY to all attributes with logical value N/A")
flag.Var(&o.csvFields, "csv", "comma-separated list of fields to read CPEs from, starting at 1;\n"+
"assumes input as CSV, fields not specified in the list are passed unchanged")
flag.StringVar(&o.csvComma, "d", ",", "if csv flag is set, sets the CSV delimiter, otherwise this flag is ignored")
}
func (o *options) validate() error {
if err := validateAttrNames(o.attributes); err != nil {
return err
}
if o.any2na && o.na2any {
return fmt.Errorf("-any2na and -na2any flags are mutually exclusive")
}
switch o.outBinding {
case "uri", "fstr", "str":
break
default:
return fmt.Errorf("-b: invalid value %q", o.outBinding)
}
return nil
}
func (o *options) shouldProcess(name string) bool {
do := o.attributes["all"] || o.attributes[name]
if o.invertAttributes {
return !do
}
return do
}
func (o *options) processAttributes(attr *wfn.Attributes, f func(*string) error) error {
if o.shouldProcess("part") {
if err := f(&attr.Part); err != nil {
return err
}
}
if o.shouldProcess("vendor") {
if err := f(&attr.Vendor); err != nil {
return err
}
}
if o.shouldProcess("product") {
if err := f(&attr.Product); err != nil {
return err
}
}
if o.shouldProcess("version") {
if err := f(&attr.Version); err != nil {
return err
}
}
if o.shouldProcess("update") {
if err := f(&attr.Update); err != nil {
return err
}
}
if o.shouldProcess("edition") {
if err := f(&attr.Edition); err != nil {
return err
}
}
if o.shouldProcess("sw_edition") {
if err := f(&attr.SWEdition); err != nil {
return err
}
}
if o.shouldProcess("target_sw") {
if err := f(&attr.TargetSW); err != nil {
return err
}
}
if o.shouldProcess("target_hw") {
if err := f(&attr.TargetHW); err != nil {
return err
}
}
if o.shouldProcess("other") {
if err := f(&attr.Other); err != nil {
return err
}
}
if o.shouldProcess("language") {
if err := f(&attr.Language); err != nil {
return err
}
}
return nil
}
func validateAttrNames(names strFields) error {
valid := map[string]bool{
"part": true,
"vendor": true,
"product": true,
"version": true,
"update": true,
"edition": true,
"language": true,
"sw_edition": true,
"target_sw": true,
"target_hw": true,
"other": true,
}
for n := range names {
if n != "all" && !valid[n] {
return fmt.Errorf("bad attribute name: %q", n)
}
}
return nil
}