<commit_before>package main

import (
        "encoding/json"
        "flag"
        "fmt"
        "io/ioutil"
        "net/http"
        "strings"
        "time"
        "os"
)

type Stop struct {
        V            int     `json:"__v,int"`
        Id           string  `json:"_id"`
        City         string
        Name         string
        Sort_id      int
        Destinations []Destination
}

type Destination struct {
        V            int     `json:"__v,int"`
        Id           string  `json:"_id"`
        City         string
        Name         string
        Sort_id      int
        Destinations []string
}

type Time struct {
        FromId  string
        From    string
        ToId    string
        To      string
        Date    string
        //Arrival string
        Route   int
        Busstop string
        Hash    string
        Id      string  `json:"_id"`
        V       int     `json:"__v"`
}

func main() {

        busStopsURL := "http://rutebuss.no/stops"

        resp, err := http.Get(busStopsURL)
        if err != nil {
                fmt.Print("Bus stops could not be downlaoded...", err)
                return
        }

        body, err := ioutil.ReadAll(resp.Body)

        if err != nil {
                fmt.Println("Could not read body ", err)
                return
        }

        stops := make([]Stop, 100)

        err = json.Unmarshal(body, &stops)

        if err != nil {
                fmt.Print("Error contacting rutebuss.no")
                return
        }

        fromString := flag.String("from", "UiT",
                "The bus stop you are traveling from")
        toString := flag.String("to", "Sentrum",
                "The bus stop you are traveling to")

        invert := flag.Bool("i", false,
            "Switch the from and to values with each other")

        flag.Usage = func(){
            fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
            flag.PrintDefaults()
            fmt.Fprintf(os.Stderr, "\nAvailible stops: \n")//+availibleStops)
            for i, _ := range stops {
                if i%2 == 0 {
                    if i == len(stops) - 1 {
                        fmt.Fprintln(os.Stderr, stops[i].Name)
                    } else {
                        fmt.Fprintf(os.Stderr, "%-20s%s\n", stops[i].Name, stops[i+1].Name)
                    }
                }
            }
        }

        flag.Parse()
        if *invert {
            tmp := fromString
            fromString = toString
            toString = tmp
        }

        fmt.Println("From", *fromString, "to", *toString)

        var from, to string

        for _, stop := range stops {
                if strings.ToLower(stop.Name) == strings.ToLower(*fromString) {
                        from = stop.Id
                }
                if strings.ToLower(stop.Name) == strings.ToLower(*toString) {
                        to = stop.Id
                }
        }

        if from == "" && to == "" {
                fmt.Println("Bus route ", from, "-", to, "does not exist")
                return
        }

        t := time.Now()
        timeString := t.UTC().Format(time.RFC3339)

        travelURL :=
                "http://rutebuss.no/departure?from=" + from + "&to=" + to + "&date=" + timeString

        resp, err = http.Get(travelURL)
        if err != nil {
                fmt.Print("Bus times could not be downlaoded...", err)
                return
        }

        body, err = ioutil.ReadAll(resp.Body)

        if err != nil {
                fmt.Println("Could not read body ", err)
                return
        }

        times := make([]Time, 10)

        err = json.Unmarshal(body, &times)

        if err != nil {
                fmt.Println("Bus route ", *fromString, "-", *toString, "does not exist")
                return
        }

        if len(times) == 0 {
                fmt.Println("Bus route ", *fromString, "-", *toString, "does not exist")
        }

        for _, t := range times {
                departure, err := time.Parse(time.RFC3339, t.Date)
                if err != nil {
                        fmt.Println("Parsing of date went horrible... ", err)
                }
                untilDeparture := departure.Sub(time.Now())
                untilDString := untilDeparture.String()
                untilDString = strings.Split(untilDString, ".")[0]
                untilDString = untilDString + "s"

                hour := time.Hour

                departure = departure.Add(hour)

                tm := departure.Format(time.Kitchen)
                fmt.Println("Bus", t.Route, "leaves at", tm, "in",
                        untilDString, "from", t.Busstop)
        }

}
<commit_msg>users can now specify time of departure. Thanks to our good friend and power user Michael Kampffmeyer for the suggestion<commit_after>package main

import (
        "encoding/json"
        "flag"
        "fmt"
        "io/ioutil"
        "net/http"
        "strings"
        "time"
        "os"
)

type Stop struct {
        V            int     `json:"__v,int"`
        Id           string  `json:"_id"`
        City         string
        Name         string
        Sort_id      int
        Destinations []Destination
}

type Destination struct {
        V            int     `json:"__v,int"`
        Id           string  `json:"_id"`
        City         string
        Name         string
        Sort_id      int
        Destinations []string
}

type Time struct {
        FromId  string
        From    string
        ToId    string
        To      string
        Date    string
        //Arrival string
        Route   int
        Busstop string
        Hash    string
        Id      string  `json:"_id"`
        V       int     `json:"__v"`
}

func main() {

        busStopsURL := "http://rutebuss.no/stops"

        resp, err := http.Get(busStopsURL)
        if err != nil {
                fmt.Print("Bus stops could not be downlaoded...", err)
                return
        }

        body, err := ioutil.ReadAll(resp.Body)

        if err != nil {
                fmt.Println("Could not read body ", err)
                return
        }

        stops := make([]Stop, 100)

        err = json.Unmarshal(body, &stops)

        if err != nil {
                fmt.Print("Error contacting rutebuss.no")
                return
        }

        fromString := flag.String("from", "UiT",
                "The bus stop you are traveling from")
        toString := flag.String("to", "Sentrum",
                "The bus stop you are traveling to")

        invert := flag.Bool("i", false,
            "Switch the from and to values with each other")

        specificTime := flag.String("time", "now",
                "Specific time of departure. Formatted as hour:minute")

        flag.Usage = func(){
            fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
            flag.PrintDefaults()
            fmt.Fprintf(os.Stderr, "\nAvailible stops: \n")//+availibleStops)
            for i, _ := range stops {
                if i%2 == 0 {
                    if i == len(stops) - 1 {
                        fmt.Fprintln(os.Stderr, stops[i].Name)
                    } else {
                        fmt.Fprintf(os.Stderr, "%-20s%s\n", stops[i].Name, stops[i+1].Name)
                    }
                }
            }
        }

        flag.Parse()
        if *invert {
            tmp := fromString
            fromString = toString
            toString = tmp
        }

        fmt.Println("From", *fromString, "to", *toString)

        var from, to string

        for _, stop := range stops {
                if strings.ToLower(stop.Name) == strings.ToLower(*fromString) {
                        from = stop.Id
                }
                if strings.ToLower(stop.Name) == strings.ToLower(*toString) {
                        to = stop.Id
                }
        }

        if from == "" && to == "" {
                fmt.Println("Bus route ", from, "-", to, "does not exist")
                return
        }

        var t time.Time
        var timeString string

        if *specificTime != "now" {
            const layout = "15:04"
            t, err = time.Parse(layout, *specificTime)

            if err != nil {
                fmt.Println("Error: Could not parse time:", *specificTime)
                fmt.Println(t)
                //flag.Usage()
                return
            }

            t_now := time.Now()
            y,m,d := t_now.Date()

            // date() gave day and month +1 :(
            d = d - 1
            mnth := int(m) - 1

            // since t object is 0000-00-00Thour:minute we need to add today's
            // date and everything.
            t = t.AddDate(y,mnth,d)

            timeString = t.UTC().Format(time.RFC3339)
        } else {
            t = time.Now()
            timeString = t.UTC().Format(time.RFC3339)
        }

        travelURL :=
                "http://rutebuss.no/departure?from=" + from + "&to=" + to + "&date=" + timeString

        resp, err = http.Get(travelURL)
        if err != nil {
                fmt.Print("Bus times could not be downlaoded...", err)
                return
        }

        body, err = ioutil.ReadAll(resp.Body)

        if err != nil {
                fmt.Println("Could not read body ", err)
                return
        }

        times := make([]Time, 10)

        err = json.Unmarshal(body, &times)

        if err != nil {
                fmt.Println("Bus route ", *fromString, "-", *toString, "does not exist")
                return
        }

        if len(times) == 0 {
                fmt.Println("Bus route ", *fromString, "-", *toString, "does not exist")
        }

        for _, departure_time := range times {

                departure, err := time.Parse(time.RFC3339, departure_time.Date)

                if err != nil {
                        fmt.Println("Parsing of date went horrible... ", err)
                }

                untilDeparture := departure.Sub(t)
                untilDString := untilDeparture.String()
                untilDString = strings.Split(untilDString, ".")[0]
                untilDString = untilDString + "s"

                hour := time.Hour
                departure = departure.Add(hour)

                tm := departure.Format(time.Kitchen)
                fmt.Println("Bus", departure_time.Route, "leaves at", tm, "in",
                        untilDString, "from", departure_time.Busstop)
        }

}
<|endoftext|>


<commit_before>// Copyright 2015-2016, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 codegen generates Go code and is only used in development.

Configuration

If you would like to configure the generation process because of custom EAV and attribute models
please create a new file in this folder called `config_*.go` where `*` can be any name.
@todo rethink that process and provide a better solution.

You must then use the `init()` function to append new values or change existing values
of the configuration variables.

All defined variables in the file `config.go` can be changed. File is documented.

Why the init() function? https://golang.org/doc/effective_go.html#initialization

TODO(CS) Use https://github.com/AOEpeople/MageTestStand for TravisCI
*/
package codegen
<commit_msg>codegen: Add todo<commit_after>// Copyright 2015-2016, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 codegen generates Go code and is only used in development.

Configuration

If you would like to configure the generation process because of custom EAV and attribute models
please create a new file in this folder called `config_*.go` where `*` can be any name.
@todo rethink that process and provide a better solution.

You must then use the `init()` function to append new values or change existing values
of the configuration variables.

All defined variables in the file `config.go` can be changed. File is documented.

Why the init() function? https://golang.org/doc/effective_go.html#initialization

TODO(CS) Use https://github.com/AOEpeople/MageTestStand for TravisCI
TODO(CS): Generate also JSONSchema https://github.com/xeipuuv/gojsonschema along with the Table* types
*/
package codegen
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

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 govmomi

import (
	"errors"
	"fmt"
	"reflect"
	"regexp"
	"strings"

	"github.com/vmware/govmomi/vim25/types"
)

// VirtualDeviceList provides helper methods for working with a list of virtual devices.
type VirtualDeviceList []types.BaseVirtualDevice

// Select returns a new list containing all elements of the list for which the given func returns true.
func (l VirtualDeviceList) Select(f func(device types.BaseVirtualDevice) bool) VirtualDeviceList {
	var found VirtualDeviceList

	for _, device := range l {
		if f(device) {
			found = append(found, device)
		}
	}

	return found
}

// SelectByType returns a new list with devices that are equal to or extend the given types.
func (l VirtualDeviceList) SelectByType(deviceType types.BaseVirtualDevice) VirtualDeviceList {
	dtype := reflect.TypeOf(deviceType)
	dname := dtype.Elem().Name()

	return l.Select(func(device types.BaseVirtualDevice) bool {
		t := reflect.TypeOf(device)

		if t == dtype {
			return true
		}

		_, ok := t.Elem().FieldByName(dname)

		return ok
	})
}

// Find returns the device matching the given name.
func (l VirtualDeviceList) Find(name string) types.BaseVirtualDevice {
	for _, device := range l {
		if l.Name(device) == name {
			return device
		}
	}
	return nil
}

// FindByKey returns the device matching the given key.
func (l VirtualDeviceList) FindByKey(key int) types.BaseVirtualDevice {
	for _, device := range l {
		if device.GetVirtualDevice().Key == key {
			return device
		}
	}
	return nil
}

// FindIDEController will find the named IDE controller if given, otherwise will pick an available controller.
// An error is returned if the named controller is not found or not an IDE controller.  Or, if name is not
// given and no available controller can be found.
func (l VirtualDeviceList) FindIDEController(name string) (*types.VirtualIDEController, error) {
	if name != "" {
		d := l.Find(name)
		if d == nil {
			return nil, fmt.Errorf("device '%s' not found", name)
		}
		if c, ok := d.(*types.VirtualIDEController); ok {
			return c, nil
		}
		return nil, fmt.Errorf("%s is not an IDE controller", name)
	}

	c := l.PickController((*types.VirtualIDEController)(nil))
	if c == nil {
		return nil, errors.New("no available IDE controller")
	}

	return c.(*types.VirtualIDEController), nil
}

// PickController returns a controller of the given type(s).
// If no controllers are found or have no available slots, then nil is returned.
func (l VirtualDeviceList) PickController(kind types.BaseVirtualController) types.BaseVirtualController {
	l = l.SelectByType(kind.(types.BaseVirtualDevice)).Select(func(device types.BaseVirtualDevice) bool {
		num := len(device.(types.BaseVirtualController).GetVirtualController().Device)

		switch device.(type) {
		case types.BaseVirtualSCSIController:
			return num < 15
		default:
			return num < 2
		}
	})

	if len(l) == 0 {
		return nil
	}

	return l[0].(types.BaseVirtualController)
}

// newUnitNumber returns the unit number to use for attaching a new device to the given controller.
func (l VirtualDeviceList) newUnitNumber(c types.BaseVirtualController) int {
	key := c.GetVirtualController().Key
	max := -1

	for _, device := range l {
		d := device.GetVirtualDevice()

		if d.ControllerKey == key {
			if d.UnitNumber > max {
				max = d.UnitNumber
			}
		}
	}

	return max + 1
}

// AssignController assigns a device to a controller.
func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c types.BaseVirtualController) {
	d := device.GetVirtualDevice()
	d.ControllerKey = c.GetVirtualController().Key
	d.UnitNumber = l.newUnitNumber(c)
	d.Key = -1
}

func (l VirtualDeviceList) connectivity(device types.BaseVirtualDevice, v bool) error {
	c := device.GetVirtualDevice().Connectable
	if c == nil {
		return fmt.Errorf("%s is not connectable", l.Name(device))
	}

	c.Connected = v
	c.StartConnected = v

	return nil
}

// Connect changes the device to connected, returns an error if the device is not connectable.
func (l VirtualDeviceList) Connect(device types.BaseVirtualDevice) error {
	return l.connectivity(device, true)
}

// Disconnect changes the device to disconnected, returns an error if the device is not connectable.
func (l VirtualDeviceList) Disconnect(device types.BaseVirtualDevice) error {
	return l.connectivity(device, false)
}

// FindCdrom finds a cdrom device with the given name, defaulting to the first cdrom device if any.
func (l VirtualDeviceList) FindCdrom(name string) (*types.VirtualCdrom, error) {
	if name != "" {
		d := l.Find(name)
		if d == nil {
			return nil, fmt.Errorf("device '%s' not found", name)
		}
		if c, ok := d.(*types.VirtualCdrom); ok {
			return c, nil
		}
		return nil, fmt.Errorf("%s is not a cdrom device", name)
	}

	c := l.SelectByType((*types.VirtualCdrom)(nil))
	if len(c) == 0 {
		return nil, errors.New("no cdrom device found")
	}

	return c[0].(*types.VirtualCdrom), nil
}

// CreateCdrom creates a new VirtualCdrom device which can be added to a VM.
func (l VirtualDeviceList) CreateCdrom(c *types.VirtualIDEController) (*types.VirtualCdrom, error) {
	device := &types.VirtualCdrom{}

	l.AssignController(device, c)

	l.setDefaultCdromBacking(device)

	device.Connectable = &types.VirtualDeviceConnectInfo{
		AllowGuestControl: true,
		Connected:         true,
		StartConnected:    true,
	}

	return device, nil
}

// InsertIso changes the cdrom device backing to use the given iso file.
func (l VirtualDeviceList) InsertIso(device *types.VirtualCdrom, iso string) *types.VirtualCdrom {
	device.Backing = &types.VirtualCdromIsoBackingInfo{
		VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
			FileName: iso,
		},
	}

	return device
}

// EjectIso removes the iso file based backing and replaces with the default cdrom backing.
func (l VirtualDeviceList) EjectIso(device *types.VirtualCdrom) *types.VirtualCdrom {
	l.setDefaultCdromBacking(device)
	return device
}

func (l VirtualDeviceList) setDefaultCdromBacking(device *types.VirtualCdrom) {
	device.Backing = &types.VirtualCdromAtapiBackingInfo{
		VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
			DeviceName:    fmt.Sprintf("cdrom-%d-%d", device.ControllerKey, device.UnitNumber),
			UseAutoDetect: false,
		},
	}
}

// PrimaryMacAddress returns the MacAddress field of the primary VirtualEthernetCard
func (l VirtualDeviceList) PrimaryMacAddress() string {
	eth0 := l.Find("ethernet-0")

	if eth0 == nil {
		return ""
	}

	return eth0.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard().MacAddress
}

// TypeName returns the vmodl type name of the device
func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string {
	return reflect.TypeOf(device).Elem().Name()
}

var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|Device|Controller)?$`)

// Name returns a stable, human-readable name for the given device
func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
	typeName := l.TypeName(device)
	d := device.GetVirtualDevice()

	switch device.(type) {
	case types.BaseVirtualEthernetCard:
		return fmt.Sprintf("ethernet-%d", d.UnitNumber-7)
	case *types.ParaVirtualSCSIController:
		return fmt.Sprintf("pvscsi-%d", d.Key)
	case *types.VirtualDisk:
		return fmt.Sprintf("disk-%d-%d", d.ControllerKey, d.UnitNumber)
	default:
		name := "device"
		m := deviceNameRegexp.FindStringSubmatch(typeName)
		if len(m) == 2 {
			name = strings.ToLower(m[1])
		}
		return fmt.Sprintf("%s-%d", name, d.Key)
	}
}
<commit_msg>Add VirtualDeviceList.Type method<commit_after>/*
Copyright (c) 2014 VMware, Inc. All Rights Reserved.

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 govmomi

import (
	"errors"
	"fmt"
	"reflect"
	"regexp"
	"strings"

	"github.com/vmware/govmomi/vim25/types"
)

// VirtualDeviceList provides helper methods for working with a list of virtual devices.
type VirtualDeviceList []types.BaseVirtualDevice

// Select returns a new list containing all elements of the list for which the given func returns true.
func (l VirtualDeviceList) Select(f func(device types.BaseVirtualDevice) bool) VirtualDeviceList {
	var found VirtualDeviceList

	for _, device := range l {
		if f(device) {
			found = append(found, device)
		}
	}

	return found
}

// SelectByType returns a new list with devices that are equal to or extend the given types.
func (l VirtualDeviceList) SelectByType(deviceType types.BaseVirtualDevice) VirtualDeviceList {
	dtype := reflect.TypeOf(deviceType)
	dname := dtype.Elem().Name()

	return l.Select(func(device types.BaseVirtualDevice) bool {
		t := reflect.TypeOf(device)

		if t == dtype {
			return true
		}

		_, ok := t.Elem().FieldByName(dname)

		return ok
	})
}

// Find returns the device matching the given name.
func (l VirtualDeviceList) Find(name string) types.BaseVirtualDevice {
	for _, device := range l {
		if l.Name(device) == name {
			return device
		}
	}
	return nil
}

// FindByKey returns the device matching the given key.
func (l VirtualDeviceList) FindByKey(key int) types.BaseVirtualDevice {
	for _, device := range l {
		if device.GetVirtualDevice().Key == key {
			return device
		}
	}
	return nil
}

// FindIDEController will find the named IDE controller if given, otherwise will pick an available controller.
// An error is returned if the named controller is not found or not an IDE controller.  Or, if name is not
// given and no available controller can be found.
func (l VirtualDeviceList) FindIDEController(name string) (*types.VirtualIDEController, error) {
	if name != "" {
		d := l.Find(name)
		if d == nil {
			return nil, fmt.Errorf("device '%s' not found", name)
		}
		if c, ok := d.(*types.VirtualIDEController); ok {
			return c, nil
		}
		return nil, fmt.Errorf("%s is not an IDE controller", name)
	}

	c := l.PickController((*types.VirtualIDEController)(nil))
	if c == nil {
		return nil, errors.New("no available IDE controller")
	}

	return c.(*types.VirtualIDEController), nil
}

// PickController returns a controller of the given type(s).
// If no controllers are found or have no available slots, then nil is returned.
func (l VirtualDeviceList) PickController(kind types.BaseVirtualController) types.BaseVirtualController {
	l = l.SelectByType(kind.(types.BaseVirtualDevice)).Select(func(device types.BaseVirtualDevice) bool {
		num := len(device.(types.BaseVirtualController).GetVirtualController().Device)

		switch device.(type) {
		case types.BaseVirtualSCSIController:
			return num < 15
		default:
			return num < 2
		}
	})

	if len(l) == 0 {
		return nil
	}

	return l[0].(types.BaseVirtualController)
}

// newUnitNumber returns the unit number to use for attaching a new device to the given controller.
func (l VirtualDeviceList) newUnitNumber(c types.BaseVirtualController) int {
	key := c.GetVirtualController().Key
	max := -1

	for _, device := range l {
		d := device.GetVirtualDevice()

		if d.ControllerKey == key {
			if d.UnitNumber > max {
				max = d.UnitNumber
			}
		}
	}

	return max + 1
}

// AssignController assigns a device to a controller.
func (l VirtualDeviceList) AssignController(device types.BaseVirtualDevice, c types.BaseVirtualController) {
	d := device.GetVirtualDevice()
	d.ControllerKey = c.GetVirtualController().Key
	d.UnitNumber = l.newUnitNumber(c)
	d.Key = -1
}

func (l VirtualDeviceList) connectivity(device types.BaseVirtualDevice, v bool) error {
	c := device.GetVirtualDevice().Connectable
	if c == nil {
		return fmt.Errorf("%s is not connectable", l.Name(device))
	}

	c.Connected = v
	c.StartConnected = v

	return nil
}

// Connect changes the device to connected, returns an error if the device is not connectable.
func (l VirtualDeviceList) Connect(device types.BaseVirtualDevice) error {
	return l.connectivity(device, true)
}

// Disconnect changes the device to disconnected, returns an error if the device is not connectable.
func (l VirtualDeviceList) Disconnect(device types.BaseVirtualDevice) error {
	return l.connectivity(device, false)
}

// FindCdrom finds a cdrom device with the given name, defaulting to the first cdrom device if any.
func (l VirtualDeviceList) FindCdrom(name string) (*types.VirtualCdrom, error) {
	if name != "" {
		d := l.Find(name)
		if d == nil {
			return nil, fmt.Errorf("device '%s' not found", name)
		}
		if c, ok := d.(*types.VirtualCdrom); ok {
			return c, nil
		}
		return nil, fmt.Errorf("%s is not a cdrom device", name)
	}

	c := l.SelectByType((*types.VirtualCdrom)(nil))
	if len(c) == 0 {
		return nil, errors.New("no cdrom device found")
	}

	return c[0].(*types.VirtualCdrom), nil
}

// CreateCdrom creates a new VirtualCdrom device which can be added to a VM.
func (l VirtualDeviceList) CreateCdrom(c *types.VirtualIDEController) (*types.VirtualCdrom, error) {
	device := &types.VirtualCdrom{}

	l.AssignController(device, c)

	l.setDefaultCdromBacking(device)

	device.Connectable = &types.VirtualDeviceConnectInfo{
		AllowGuestControl: true,
		Connected:         true,
		StartConnected:    true,
	}

	return device, nil
}

// InsertIso changes the cdrom device backing to use the given iso file.
func (l VirtualDeviceList) InsertIso(device *types.VirtualCdrom, iso string) *types.VirtualCdrom {
	device.Backing = &types.VirtualCdromIsoBackingInfo{
		VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
			FileName: iso,
		},
	}

	return device
}

// EjectIso removes the iso file based backing and replaces with the default cdrom backing.
func (l VirtualDeviceList) EjectIso(device *types.VirtualCdrom) *types.VirtualCdrom {
	l.setDefaultCdromBacking(device)
	return device
}

func (l VirtualDeviceList) setDefaultCdromBacking(device *types.VirtualCdrom) {
	device.Backing = &types.VirtualCdromAtapiBackingInfo{
		VirtualDeviceDeviceBackingInfo: types.VirtualDeviceDeviceBackingInfo{
			DeviceName:    fmt.Sprintf("cdrom-%d-%d", device.ControllerKey, device.UnitNumber),
			UseAutoDetect: false,
		},
	}
}

// PrimaryMacAddress returns the MacAddress field of the primary VirtualEthernetCard
func (l VirtualDeviceList) PrimaryMacAddress() string {
	eth0 := l.Find("ethernet-0")

	if eth0 == nil {
		return ""
	}

	return eth0.(types.BaseVirtualEthernetCard).GetVirtualEthernetCard().MacAddress
}

// TypeName returns the vmodl type name of the device
func (l VirtualDeviceList) TypeName(device types.BaseVirtualDevice) string {
	return reflect.TypeOf(device).Elem().Name()
}

var deviceNameRegexp = regexp.MustCompile(`(?:Virtual)?(?:Machine)?(\w+?)(?:Card|Device|Controller)?$`)

// Type returns a human-readable name for the given device
func (l VirtualDeviceList) Type(device types.BaseVirtualDevice) string {
	switch device.(type) {
	case types.BaseVirtualEthernetCard:
		return "ethernet"
	case *types.ParaVirtualSCSIController:
		return "pvscsi"
	default:
		name := "device"
		typeName := l.TypeName(device)
		m := deviceNameRegexp.FindStringSubmatch(typeName)
		if len(m) == 2 {
			name = strings.ToLower(m[1])
		}
		return name
	}
}

// Name returns a stable, human-readable name for the given device
func (l VirtualDeviceList) Name(device types.BaseVirtualDevice) string {
	var key string
	d := device.GetVirtualDevice()
	dtype := l.Type(device)

	switch dtype {
	case "ethernet":
		key = fmt.Sprintf("%d", d.UnitNumber-7)
	case "disk":
		key = fmt.Sprintf("%d-%d", d.ControllerKey, d.UnitNumber)
	default:
		key = fmt.Sprintf("%d", d.Key)
	}

	return fmt.Sprintf("%s-%s", dtype, key)
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"io"

	"github.com/alecthomas/kingpin"
	"github.com/minodisk/qiitactl/api"
)

type Command struct {
	Client api.Client
	Out    io.Writer
	Error  io.Writer

	Application  *kingpin.Application
	Generate     *kingpin.CmdClause
	GenerateFile *kingpin.CmdClause
	Create       *kingpin.CmdClause
	CreatePost   *kingpin.CmdClause
	Show         *kingpin.CmdClause
	ShowPost     *kingpin.CmdClause
	ShowPosts    *kingpin.CmdClause
	Fetch        *kingpin.CmdClause
	FetchPost    *kingpin.CmdClause
	FetchPosts   *kingpin.CmdClause
	Update       *kingpin.CmdClause
	UpdatePost   *kingpin.CmdClause
	Delete       *kingpin.CmdClause
	DeletePost   *kingpin.CmdClause

	GlobalOptions      GlobalOptions
	GenerateFileRunner GenerateFileRunner
	CreatePostRunner   CreatePostRunner
	ShowPostRunner     ShowPostRunner
	ShowPostsRunner    ShowPostsRunner
	FetchPostRunner    FetchPostRunner
	FetchPostsRunner   FetchPostsRunner
	UpdatePostRunner   UpdatePostRunner
	DeletePostRunner   DeletePostRunner
}

type GlobalOptions struct {
	Debug *bool
}

func New(client api.Client, out io.Writer, err io.Writer) (c Command) {
	c.Client = client
	c.Out = out
	c.Error = err

	c.Application = kingpin.New("qiitactl", "A command-line chat application.")
	c.GlobalOptions = GlobalOptions{
		Debug: c.Application.Flag("debug", "Enable debug mode.").Bool(),
	}

	c.Generate = c.Application.Command("generate", "Generate something in your local.")
	c.GenerateFile = c.Generate.Command("file", "Generate a new markdown file for a new post.")
	c.GenerateFileRunner = GenerateFileRunner{
		Title: c.GenerateFile.Arg("title", "The title of a new post.").Required().String(),
		Team:  c.GenerateFile.Flag("team", "The name of a team, when you post to the team.").Short('t').String(),
	}

	c.Create = c.Application.Command("create", "Create resources from current working directory to Qiita.")
	c.CreatePost = c.Create.Command("post", "Create a post in Qiita.")
	c.CreatePostRunner = CreatePostRunner{
		File:  c.CreatePost.Arg("filename", "The filename of the post to be created").Required().File(),
		Tweet: c.CreatePost.Flag("tweet", "Tweet the created post in Twitter.").Short('t').Bool(),
		Gist:  c.CreatePost.Flag("gist", "Post codes in the created post to GitHub Gist.").Short('g').Bool(),
	}

	c.Show = c.Application.Command("show", "Display resources.")
	c.ShowPost = c.Show.Command("post", "Display detail of a post in Qitta.")
	c.ShowPostRunner = ShowPostRunner{
		ID:   c.ShowPost.Flag("id", "The ID of the post to be printed detail.").Short('i').String(),
		File: c.ShowPost.Flag("filename", "The filename of the post to be created.").Short('f').File(),
	}
	c.ShowPosts = c.Show.Command("posts", "Display posts in Qiita.")
	c.ShowPostsRunner = ShowPostsRunner{}

	c.Fetch = c.Application.Command("fetch", "Download resources from Qiita to current working directory.")
	c.FetchPost = c.Fetch.Command("post", "Download a post as a file.")
	c.FetchPostRunner = FetchPostRunner{
		ID:   c.FetchPost.Flag("id", "The ID of the post to be downloaded.").Short('i').String(),
		File: c.FetchPost.Flag("filename", "The filename of the post to be created.").Short('f').File(),
	}
	c.FetchPosts = c.Fetch.Command("posts", "Download posts as files.")
	c.FetchPostsRunner = FetchPostsRunner{}

	c.Update = c.Application.Command("update", "Update resources from current working directory to Qiita.")
	c.UpdatePost = c.Update.Command("post", "Update a post in Qiita.")
	c.UpdatePostRunner = UpdatePostRunner{
		File: c.UpdatePost.Arg("filename", "The filename of the post to be updated.").Required().File(),
	}

	c.Delete = c.Application.Command("delete", "Delete resources from current working directory to Qiita.")
	c.DeletePost = c.Delete.Command("post", "Delete a post in Qiita.")
	c.DeletePostRunner = DeletePostRunner{
		File: c.DeletePost.Arg("filename", "The filename of the post to be deleted.").Required().File(),
	}

	return
}

func (c Command) Run(args []string) {
	var err error

	cmd, err := c.Application.Parse(args[1:])
	fmt.Println(*c.GlobalOptions.Debug)
	c.Client.DebugMode(*c.GlobalOptions.Debug)

	switch kingpin.MustParse(cmd, err) {
	case c.GenerateFile.FullCommand():
		err = c.GenerateFileRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.CreatePost.FullCommand():
		err = c.CreatePostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.ShowPost.FullCommand():
		err = c.ShowPostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.ShowPosts.FullCommand():
		err = c.ShowPostsRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.FetchPost.FullCommand():
		err = c.FetchPostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.FetchPosts.FullCommand():
		err = c.FetchPostsRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.UpdatePost.FullCommand():
		err = c.UpdatePostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.DeletePost.FullCommand():
		err = c.DeletePostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	}

	if err != nil {
		fmt.Fprintf(c.Error, "%s\n", err)
	}
}
<commit_msg>Remove trace<commit_after>package command

import (
	"fmt"
	"io"

	"github.com/alecthomas/kingpin"
	"github.com/minodisk/qiitactl/api"
)

type Command struct {
	Client api.Client
	Out    io.Writer
	Error  io.Writer

	Application  *kingpin.Application
	Generate     *kingpin.CmdClause
	GenerateFile *kingpin.CmdClause
	Create       *kingpin.CmdClause
	CreatePost   *kingpin.CmdClause
	Show         *kingpin.CmdClause
	ShowPost     *kingpin.CmdClause
	ShowPosts    *kingpin.CmdClause
	Fetch        *kingpin.CmdClause
	FetchPost    *kingpin.CmdClause
	FetchPosts   *kingpin.CmdClause
	Update       *kingpin.CmdClause
	UpdatePost   *kingpin.CmdClause
	Delete       *kingpin.CmdClause
	DeletePost   *kingpin.CmdClause

	GlobalOptions      GlobalOptions
	GenerateFileRunner GenerateFileRunner
	CreatePostRunner   CreatePostRunner
	ShowPostRunner     ShowPostRunner
	ShowPostsRunner    ShowPostsRunner
	FetchPostRunner    FetchPostRunner
	FetchPostsRunner   FetchPostsRunner
	UpdatePostRunner   UpdatePostRunner
	DeletePostRunner   DeletePostRunner
}

type GlobalOptions struct {
	Debug *bool
}

func New(client api.Client, out io.Writer, err io.Writer) (c Command) {
	c.Client = client
	c.Out = out
	c.Error = err

	c.Application = kingpin.New("qiitactl", "A command-line chat application.")
	c.GlobalOptions = GlobalOptions{
		Debug: c.Application.Flag("debug", "Enable debug mode.").Bool(),
	}

	c.Generate = c.Application.Command("generate", "Generate something in your local.")
	c.GenerateFile = c.Generate.Command("file", "Generate a new markdown file for a new post.")
	c.GenerateFileRunner = GenerateFileRunner{
		Title: c.GenerateFile.Arg("title", "The title of a new post.").Required().String(),
		Team:  c.GenerateFile.Flag("team", "The name of a team, when you post to the team.").Short('t').String(),
	}

	c.Create = c.Application.Command("create", "Create resources from current working directory to Qiita.")
	c.CreatePost = c.Create.Command("post", "Create a post in Qiita.")
	c.CreatePostRunner = CreatePostRunner{
		File:  c.CreatePost.Arg("filename", "The filename of the post to be created").Required().File(),
		Tweet: c.CreatePost.Flag("tweet", "Tweet the created post in Twitter.").Short('t').Bool(),
		Gist:  c.CreatePost.Flag("gist", "Post codes in the created post to GitHub Gist.").Short('g').Bool(),
	}

	c.Show = c.Application.Command("show", "Display resources.")
	c.ShowPost = c.Show.Command("post", "Display detail of a post in Qitta.")
	c.ShowPostRunner = ShowPostRunner{
		ID:   c.ShowPost.Flag("id", "The ID of the post to be printed detail.").Short('i').String(),
		File: c.ShowPost.Flag("filename", "The filename of the post to be created.").Short('f').File(),
	}
	c.ShowPosts = c.Show.Command("posts", "Display posts in Qiita.")
	c.ShowPostsRunner = ShowPostsRunner{}

	c.Fetch = c.Application.Command("fetch", "Download resources from Qiita to current working directory.")
	c.FetchPost = c.Fetch.Command("post", "Download a post as a file.")
	c.FetchPostRunner = FetchPostRunner{
		ID:   c.FetchPost.Flag("id", "The ID of the post to be downloaded.").Short('i').String(),
		File: c.FetchPost.Flag("filename", "The filename of the post to be created.").Short('f').File(),
	}
	c.FetchPosts = c.Fetch.Command("posts", "Download posts as files.")
	c.FetchPostsRunner = FetchPostsRunner{}

	c.Update = c.Application.Command("update", "Update resources from current working directory to Qiita.")
	c.UpdatePost = c.Update.Command("post", "Update a post in Qiita.")
	c.UpdatePostRunner = UpdatePostRunner{
		File: c.UpdatePost.Arg("filename", "The filename of the post to be updated.").Required().File(),
	}

	c.Delete = c.Application.Command("delete", "Delete resources from current working directory to Qiita.")
	c.DeletePost = c.Delete.Command("post", "Delete a post in Qiita.")
	c.DeletePostRunner = DeletePostRunner{
		File: c.DeletePost.Arg("filename", "The filename of the post to be deleted.").Required().File(),
	}

	return
}

func (c Command) Run(args []string) {
	var err error

	cmd, err := c.Application.Parse(args[1:])
	c.Client.DebugMode(*c.GlobalOptions.Debug)

	switch kingpin.MustParse(cmd, err) {
	case c.GenerateFile.FullCommand():
		err = c.GenerateFileRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.CreatePost.FullCommand():
		err = c.CreatePostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.ShowPost.FullCommand():
		err = c.ShowPostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.ShowPosts.FullCommand():
		err = c.ShowPostsRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.FetchPost.FullCommand():
		err = c.FetchPostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.FetchPosts.FullCommand():
		err = c.FetchPostsRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.UpdatePost.FullCommand():
		err = c.UpdatePostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	case c.DeletePost.FullCommand():
		err = c.DeletePostRunner.Run(c.Client, c.GlobalOptions, c.Out)
	}

	if err != nil {
		fmt.Fprintf(c.Error, "%s\n", err)
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/codegangsta/cli"
	"github.com/docker/machine/libmachine"
	"github.com/docker/machine/log"
)

// FilterOptions -
type FilterOptions struct {
	SwarmName  []string
	DriverName []string
	State      []string
}

func cmdLs(c *cli.Context) {
	quiet := c.Bool("quiet")
	filters, err := parseFilters(c.StringSlice("filter"))
	if err != nil {
		log.Fatal(err)
	}

	mcn := getDefaultMcn(c)
	hostList, err := mcn.List()
	if err != nil {
		log.Fatal(err)
	}

	hostList = filterHosts(hostList, filters)

	// Just print out the names if we're being quiet
	if quiet {
		for _, host := range hostList {
			fmt.Println(host.Name)
		}
		return
	}

	swarmMasters := make(map[string]string)
	swarmInfo := make(map[string]string)

	w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
	fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL\tSWARM")

	for _, host := range hostList {
		swarmOptions := host.HostOptions.SwarmOptions
		if swarmOptions.Master {
			swarmMasters[swarmOptions.Discovery] = host.Name
		}

		if swarmOptions.Discovery != "" {
			swarmInfo[host.Name] = swarmOptions.Discovery
		}
	}

	items := libmachine.GetHostListItems(hostList)

	sortHostListItemsByName(items)

	for _, item := range items {
		activeString := ""
		if item.Active {
			activeString = "*"
		}

		swarmInfo := ""

		if item.SwarmOptions.Discovery != "" {
			swarmInfo = swarmMasters[item.SwarmOptions.Discovery]
			if item.SwarmOptions.Master {
				swarmInfo = fmt.Sprintf("%s (master)", swarmInfo)
			}
		}
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
			item.Name, activeString, item.DriverName, item.State, item.URL, swarmInfo)
	}

	w.Flush()
}

func parseFilters(filters []string) (FilterOptions, error) {
	options := FilterOptions{}
	for _, f := range filters {
		kv := strings.SplitN(f, "=", 2)
		if len(kv) != 2 {
			return options, fmt.Errorf("Unupported filter syntax, please use help ls for details on correct usage.")
		}
		key, value := kv[0], kv[1]

		switch key {
		case "swarm":
			options.SwarmName = append(options.SwarmName, value)
		case "driver":
			options.DriverName = append(options.DriverName, value)
		case "state":
			options.State = append(options.State, value)
		default:
			return options, fmt.Errorf("Unsupported filter key '%s'", key)
		}
	}
	return options, nil
}

func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine.Host {
	if len(filters.SwarmName) == 0 &&
		len(filters.DriverName) == 0 &&
		len(filters.State) == 0 {
		return hosts
	}

	filteredHosts := []*libmachine.Host{}
	swarmMasters := getSwarmMasters(hosts)

	for _, h := range hosts {
		if filterHost(h, filters, swarmMasters) {
			filteredHosts = append(filteredHosts, h)
		}
	}
	return filteredHosts
}

func getSwarmMasters(hosts []*libmachine.Host) map[string]string {
	swarmMasters := make(map[string]string)
	for _, h := range hosts {
		swarmOptions := h.HostOptions.SwarmOptions
		if swarmOptions != nil && swarmOptions.Master {
			swarmMasters[swarmOptions.Discovery] = h.Name
		}
	}
	return swarmMasters
}

func filterHost(host *libmachine.Host, filters FilterOptions, swarmMasters map[string]string) bool {
	swarmMatches := matchesSwarmName(host, filters.SwarmName, swarmMasters)
	driverMatches := matchesDriverName(host, filters.DriverName)
	stateMatches := matchesState(host, filters.State)

	return swarmMatches && driverMatches && stateMatches
}

func matchesSwarmName(host *libmachine.Host, swarmNames []string, swarmMasters map[string]string) bool {
	if len(swarmNames) == 0 {
		return true
	}
	for _, n := range swarmNames {
		if host.HostOptions.SwarmOptions != nil {
			if n == swarmMasters[host.HostOptions.SwarmOptions.Discovery] {
				return true
			}
		}
	}
	return false
}

func matchesDriverName(host *libmachine.Host, driverNames []string) bool {
	if len(driverNames) == 0 {
		return true
	}
	for _, n := range driverNames {
		if host.DriverName == n {
			return true
		}
	}
	return false
}

func matchesState(host *libmachine.Host, states []string) bool {
	if len(states) == 0 {
		return true
	}
	for _, n := range states {
		s, err := host.Driver.GetState()
		if err != nil {
			log.Warn(err)
		}
		if n == s.String() {
			return true
		}
	}
	return false
}
<commit_msg>Change error message<commit_after>package commands

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/codegangsta/cli"
	"github.com/docker/machine/libmachine"
	"github.com/docker/machine/log"
)

// FilterOptions -
type FilterOptions struct {
	SwarmName  []string
	DriverName []string
	State      []string
}

func cmdLs(c *cli.Context) {
	quiet := c.Bool("quiet")
	filters, err := parseFilters(c.StringSlice("filter"))
	if err != nil {
		log.Fatal(err)
	}

	mcn := getDefaultMcn(c)
	hostList, err := mcn.List()
	if err != nil {
		log.Fatal(err)
	}

	hostList = filterHosts(hostList, filters)

	// Just print out the names if we're being quiet
	if quiet {
		for _, host := range hostList {
			fmt.Println(host.Name)
		}
		return
	}

	swarmMasters := make(map[string]string)
	swarmInfo := make(map[string]string)

	w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0)
	fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL\tSWARM")

	for _, host := range hostList {
		swarmOptions := host.HostOptions.SwarmOptions
		if swarmOptions.Master {
			swarmMasters[swarmOptions.Discovery] = host.Name
		}

		if swarmOptions.Discovery != "" {
			swarmInfo[host.Name] = swarmOptions.Discovery
		}
	}

	items := libmachine.GetHostListItems(hostList)

	sortHostListItemsByName(items)

	for _, item := range items {
		activeString := ""
		if item.Active {
			activeString = "*"
		}

		swarmInfo := ""

		if item.SwarmOptions.Discovery != "" {
			swarmInfo = swarmMasters[item.SwarmOptions.Discovery]
			if item.SwarmOptions.Master {
				swarmInfo = fmt.Sprintf("%s (master)", swarmInfo)
			}
		}
		fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n",
			item.Name, activeString, item.DriverName, item.State, item.URL, swarmInfo)
	}

	w.Flush()
}

func parseFilters(filters []string) (FilterOptions, error) {
	options := FilterOptions{}
	for _, f := range filters {
		kv := strings.SplitN(f, "=", 2)
		if len(kv) != 2 {
			return options, fmt.Errorf("Unsupported filter syntax.")
		}
		key, value := kv[0], kv[1]

		switch key {
		case "swarm":
			options.SwarmName = append(options.SwarmName, value)
		case "driver":
			options.DriverName = append(options.DriverName, value)
		case "state":
			options.State = append(options.State, value)
		default:
			return options, fmt.Errorf("Unsupported filter key '%s'", key)
		}
	}
	return options, nil
}

func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine.Host {
	if len(filters.SwarmName) == 0 &&
		len(filters.DriverName) == 0 &&
		len(filters.State) == 0 {
		return hosts
	}

	filteredHosts := []*libmachine.Host{}
	swarmMasters := getSwarmMasters(hosts)

	for _, h := range hosts {
		if filterHost(h, filters, swarmMasters) {
			filteredHosts = append(filteredHosts, h)
		}
	}
	return filteredHosts
}

func getSwarmMasters(hosts []*libmachine.Host) map[string]string {
	swarmMasters := make(map[string]string)
	for _, h := range hosts {
		swarmOptions := h.HostOptions.SwarmOptions
		if swarmOptions != nil && swarmOptions.Master {
			swarmMasters[swarmOptions.Discovery] = h.Name
		}
	}
	return swarmMasters
}

func filterHost(host *libmachine.Host, filters FilterOptions, swarmMasters map[string]string) bool {
	swarmMatches := matchesSwarmName(host, filters.SwarmName, swarmMasters)
	driverMatches := matchesDriverName(host, filters.DriverName)
	stateMatches := matchesState(host, filters.State)

	return swarmMatches && driverMatches && stateMatches
}

func matchesSwarmName(host *libmachine.Host, swarmNames []string, swarmMasters map[string]string) bool {
	if len(swarmNames) == 0 {
		return true
	}
	for _, n := range swarmNames {
		if host.HostOptions.SwarmOptions != nil {
			if n == swarmMasters[host.HostOptions.SwarmOptions.Discovery] {
				return true
			}
		}
	}
	return false
}

func matchesDriverName(host *libmachine.Host, driverNames []string) bool {
	if len(driverNames) == 0 {
		return true
	}
	for _, n := range driverNames {
		if host.DriverName == n {
			return true
		}
	}
	return false
}

func matchesState(host *libmachine.Host, states []string) bool {
	if len(states) == 0 {
		return true
	}
	for _, n := range states {
		s, err := host.Driver.GetState()
		if err != nil {
			log.Warn(err)
		}
		if n == s.String() {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package gsort

import (
	"bufio"
	"fmt"
	"io/ioutil"

	gzip "github.com/klauspost/pgzip"

	"container/heap"
	"io"
	"log"
	"os"
	"runtime"
	"sort"
)

type LineDeco struct {
	i    int // used internally to indicate which file it came from.
	line []byte
	Cols []int
}

type Lines []LineDeco

func (l *Lines) Len() int {
	return len(*l)
}

func (l *Lines) Less(i, j int) bool {

	for k := 0; k < len((*l)[i].Cols); k++ {
		if (*l)[j].Cols[k] == (*l)[i].Cols[k] {
			continue
		}
		return (*l)[i].Cols[k] < (*l)[j].Cols[k]
	}
	return false
}
func (l *Lines) Swap(i, j int) {
	if i < len(*l) {
		(*l)[j], (*l)[i] = (*l)[i], (*l)[j]
	}
}

// for Heap

func (l *Lines) Push(i interface{}) {
	*l = append(*l, i.(LineDeco))
}

func (l *Lines) Pop() interface{} {
	n := len(*l)
	if n == 0 {
		return nil
	}
	v := (*l)[n-1]
	*l = (*l)[:n-1]
	return v
}

type Processor func(line []byte) []int

func Sort(rdr io.Reader, wtr io.Writer, preprocess Processor, memMB int) error {

	brdr, bwtr := bufio.NewReader(rdr), bufio.NewWriter(wtr)
	defer bwtr.Flush()

	err := writeHeader(bwtr, brdr)
	if err != nil {
		return err
	}
	ch := make(chan Lines, runtime.GOMAXPROCS(-1))
	go readLines(ch, brdr, memMB)
	fileNames := writeChunks(ch, preprocess)
	for _, f := range fileNames {
		defer os.Remove(f)
	}

	if len(fileNames) == 1 {
		return writeOne(fileNames[0], bwtr)
	}
	return merge(fileNames, bwtr, preprocess)
}

func readLines(ch chan Lines, rdr *bufio.Reader, memMb int) {

	mem := 1000000 * memMb / runtime.GOMAXPROCS(-1)

	processed := make(Lines, 0, 500)
	var line []byte
	var err error

	sum := 0

	for {

		line, err = rdr.ReadBytes('\n')
		if err != nil && err != io.EOF {
			log.Fatal(err)
		}

		if len(line) > 0 {
			processed = append(processed, LineDeco{line: line})
			sum += len(line)
		}

		if len(line) == 0 || err == io.EOF {
			np := len(processed)
			last := processed[np-1].line
			if len(last) == 0 || last[len(last)-1] != '\n' {
				processed[np-1].line = append(last, '\n')
			}
			ch <- processed
			break
		}

		if sum >= mem {
			ch <- processed
			processed = make(Lines, 0, 500)
			sum = 0
		}
	}
	close(ch)
}

func writeHeader(wtr *bufio.Writer, rdr *bufio.Reader) error {
	for {
		b, err := rdr.Peek(1)
		if err != nil {
			return err
		}
		if b[0] != '#' {
			break
		}
		line, err := rdr.ReadBytes('\n')
		if err != nil {
			return err
		}
		wtr.Write(line)
	}
	return nil
}

// fast path where we don't use merge if it all fit in memory.
func writeOne(fname string, wtr io.Writer) error {
	rdr, err := os.Open(fname)
	if err != nil {
		return err
	}
	defer rdr.Close()
	gz, err := gzip.NewReader(rdr)
	if err != nil {
		return err
	}
	_, err = io.Copy(wtr, gz)
	return err
}

func merge(fileNames []string, wtr io.Writer, process Processor) error {
	fhs := make([]*bufio.Reader, len(fileNames))

	cache := make(Lines, len(fileNames))

	for i, fn := range fileNames {
		fh, err := os.Open(fn)
		if err != nil {
			return err
		}
		defer fh.Close()
		gz, err := gzip.NewReader(fh)
		if err != nil {
			return err
		}
		defer gz.Close()
		fhs[i] = bufio.NewReader(gz)

		line, err := fhs[i].ReadBytes('\n')
		if len(line) > 0 {
			cache[i] = LineDeco{line: line, Cols: process(line), i: i}
		} else if err == io.EOF {
			continue
		} else if err != nil {
			return err
		}
	}

	heap.Init(&cache)

	for {
		o := heap.Pop(&cache)

		if o == nil {
			break
		}
		c := o.(LineDeco)
		// refill from same file
		line, err := fhs[c.i].ReadBytes('\n')
		if err != io.EOF && err != nil {
			return err
		}
		if len(line) != 0 {
			next := LineDeco{line: line, Cols: process(line), i: c.i}
			heap.Push(&cache, next)
		} else {
			os.Remove(fileNames[c.i])
		}
		wtr.Write(c.line)

	}
	return nil
}

func writeChunks(ch chan Lines, process Processor) (fileNames []string) {
	fileNames = make([]string, 0, 20)
	for chunk := range ch {
		f, err := ioutil.TempFile("", fmt.Sprintf("gsort.%d.", len(fileNames)))
		if err != nil {
			log.Fatal(err)
		}
		gz := gzip.NewWriter(f)
		for _, c := range chunk {
			c.Cols = process(c.line)
		}

		L := Lines(chunk)
		sort.Sort(&L)
		wtr := bufio.NewWriter(gz)
		for _, dl := range chunk {
			wtr.Write(dl.line)
		}
		wtr.Flush()
		gz.Close()
		f.Close()
		fileNames = append(fileNames, f.Name())
	}
	return fileNames
}
<commit_msg>make filehandles outside so we can clean up<commit_after>package gsort

import (
	"bufio"
	"fmt"
	"io/ioutil"

	gzip "github.com/klauspost/pgzip"

	"container/heap"
	"io"
	"log"
	"os"
	"runtime"
	"sort"
)

type LineDeco struct {
	i    int // used internally to indicate which file it came from.
	line []byte
	Cols []int
}

type Lines []LineDeco

func (l *Lines) Len() int {
	return len(*l)
}

func (l *Lines) Less(i, j int) bool {

	for k := 0; k < len((*l)[i].Cols); k++ {
		if (*l)[j].Cols[k] == (*l)[i].Cols[k] {
			continue
		}
		return (*l)[i].Cols[k] < (*l)[j].Cols[k]
	}
	return false
}
func (l *Lines) Swap(i, j int) {
	if i < len(*l) {
		(*l)[j], (*l)[i] = (*l)[i], (*l)[j]
	}
}

// for Heap

func (l *Lines) Push(i interface{}) {
	*l = append(*l, i.(LineDeco))
}

func (l *Lines) Pop() interface{} {
	n := len(*l)
	if n == 0 {
		return nil
	}
	v := (*l)[n-1]
	*l = (*l)[:n-1]
	return v
}

type Processor func(line []byte) []int

func Sort(rdr io.Reader, wtr io.Writer, preprocess Processor, memMB int) error {

	brdr, bwtr := bufio.NewReader(rdr), bufio.NewWriter(wtr)
	defer bwtr.Flush()

	err := writeHeader(bwtr, brdr)
	if err != nil {
		return err
	}
	ch := make(chan Lines, runtime.GOMAXPROCS(-1))
	go readLines(ch, brdr, memMB)
	fileNames := make([]string, 0, 20)
	writeChunks(ch, preprocess, fileNames)

	for _, f := range fileNames {
		defer os.Remove(f)
	}

	if len(fileNames) == 1 {
		return writeOne(fileNames[0], bwtr)
	}
	return merge(fileNames, bwtr, preprocess)
}

func readLines(ch chan Lines, rdr *bufio.Reader, memMb int) {

	mem := 1000000 * memMb / runtime.GOMAXPROCS(-1)

	processed := make(Lines, 0, 500)
	var line []byte
	var err error

	sum := 0

	for {

		line, err = rdr.ReadBytes('\n')
		if err != nil && err != io.EOF {
			log.Fatal(err)
		}

		if len(line) > 0 {
			processed = append(processed, LineDeco{line: line})
			sum += len(line)
		}

		if len(line) == 0 || err == io.EOF {
			np := len(processed)
			last := processed[np-1].line
			if len(last) == 0 || last[len(last)-1] != '\n' {
				processed[np-1].line = append(last, '\n')
			}
			ch <- processed
			break
		}

		if sum >= mem {
			ch <- processed
			processed = make(Lines, 0, 500)
			sum = 0
		}
	}
	close(ch)
}

func writeHeader(wtr *bufio.Writer, rdr *bufio.Reader) error {
	for {
		b, err := rdr.Peek(1)
		if err != nil {
			return err
		}
		if b[0] != '#' {
			break
		}
		line, err := rdr.ReadBytes('\n')
		if err != nil {
			return err
		}
		wtr.Write(line)
	}
	return nil
}

// fast path where we don't use merge if it all fit in memory.
func writeOne(fname string, wtr io.Writer) error {
	rdr, err := os.Open(fname)
	if err != nil {
		return err
	}
	defer rdr.Close()
	gz, err := gzip.NewReader(rdr)
	if err != nil {
		return err
	}
	_, err = io.Copy(wtr, gz)
	return err
}

func merge(fileNames []string, wtr io.Writer, process Processor) error {
	fhs := make([]*bufio.Reader, len(fileNames))

	cache := make(Lines, len(fileNames))

	for i, fn := range fileNames {
		fh, err := os.Open(fn)
		if err != nil {
			return err
		}
		defer fh.Close()
		gz, err := gzip.NewReader(fh)
		if err != nil {
			return err
		}
		defer gz.Close()
		fhs[i] = bufio.NewReader(gz)

		line, err := fhs[i].ReadBytes('\n')
		if len(line) > 0 {
			cache[i] = LineDeco{line: line, Cols: process(line), i: i}
		} else if err == io.EOF {
			continue
		} else if err != nil {
			return err
		}
	}

	heap.Init(&cache)

	for {
		o := heap.Pop(&cache)

		if o == nil {
			break
		}
		c := o.(LineDeco)
		// refill from same file
		line, err := fhs[c.i].ReadBytes('\n')
		if err != io.EOF && err != nil {
			return err
		}
		if len(line) != 0 {
			next := LineDeco{line: line, Cols: process(line), i: c.i}
			heap.Push(&cache, next)
		} else {
			os.Remove(fileNames[c.i])
		}
		wtr.Write(c.line)

	}
	return nil
}

func writeChunks(ch chan Lines, process Processor, fileNames []string) {
	for chunk := range ch {
		f, err := ioutil.TempFile("", fmt.Sprintf("gsort.%d.", len(fileNames)))
		if err != nil {
			log.Fatal(err)
		}
		gz := gzip.NewWriter(f)
		for _, c := range chunk {
			c.Cols = process(c.line)
		}

		L := Lines(chunk)
		sort.Sort(&L)
		wtr := bufio.NewWriter(gz)
		for _, dl := range chunk {
			wtr.Write(dl.line)
		}
		wtr.Flush()
		gz.Close()
		f.Close()
		fileNames = append(fileNames, f.Name())
	}
}
<|endoftext|>


<commit_before>package wrp

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/ugorji/go/codec"
)

//go:generate stringer -type=Format

// Format indicates which format is desired.
// The zero value indicates Msgpack, which means by default other
// infrastructure can assume msgpack-formatted data.
type Format int

const (
	Msgpack Format = iota
	JSON
	lastFormat
)

// AllFormats returns a distinct slice of all supported formats.
func AllFormats() []Format {
	return []Format{Msgpack, JSON}
}

var (
	jsonHandle = codec.JsonHandle{
		BasicHandle: codec.BasicHandle{
			TypeInfos: codec.NewTypeInfos([]string{"wrp"}),
		},
		IntegerAsString: 'L',
	}

	msgpackHandle = codec.MsgpackHandle{
		WriteExt:    true,
		RawToString: true,
		BasicHandle: codec.BasicHandle{
			TypeInfos: codec.NewTypeInfos([]string{"wrp"}),
		},
	}
)

// ContentType returns the MIME type associated with this format
func (f Format) ContentType() string {
	switch f {
	case Msgpack:
		return "application/msgpack"
	case JSON:
		return "application/json"
	default:
		return "application/octet-stream"
	}
}

// FormatFromContentType examines the Content-Type value and returns
// the appropriate Format.  This function returns an error if the given
// Content-Type did not map to a WRP format.
func FormatFromContentType(contentType string) (Format, error) {
	if strings.Contains(contentType, "json") {
		return JSON, nil
	} else if strings.Contains(contentType, "msgpack") {
		return Msgpack, nil
	}

	return Format(-1), fmt.Errorf("Invalid WRP content type: %s", contentType)
}

// handle looks up the appropriate codec.Handle for this format constant.
// This method panics if the format is not a valid value.
func (f Format) handle() codec.Handle {
	switch f {
	case Msgpack:
		return &msgpackHandle
	case JSON:
		return &jsonHandle
	}

	panic(fmt.Errorf("Invalid format constant: %d", f))
}

// EncodeListener can be implemented on any type passed to an Encoder in order
// to get notified when an encoding happens.  This interface is useful to set
// mandatory fields, such as message type.
type EncodeListener interface {
	BeforeEncode() error
}

// Encoder represents the underlying ugorji behavior that WRP supports
type Encoder interface {
	Encode(interface{}) error
	Reset(io.Writer)
	ResetBytes(*[]byte)
}

// encoderDecorator wraps a ugorji Encoder and implements the wrp.Encoder interface.
type encoderDecorator struct {
	*codec.Encoder
}

// Encode checks to see if value implements EncoderTo and if it does, uses the
// value.EncodeTo() method.  Otherwise, the value is passed as is to the decorated
// ugorji Encoder.
func (ed *encoderDecorator) Encode(value interface{}) error {
	if listener, ok := value.(EncodeListener); ok {
		if err := listener.BeforeEncode(); err != nil {
			return err
		}
	}

	return ed.Encoder.Encode(value)
}

// Decoder represents the underlying ugorji behavior that WRP supports
type Decoder interface {
	Decode(interface{}) error
	Reset(io.Reader)
	ResetBytes([]byte)
}

// NewEncoder produces a ugorji Encoder using the appropriate WRP configuration
// for the given format
func NewEncoder(output io.Writer, f Format) Encoder {
	return &encoderDecorator{
		codec.NewEncoder(output, f.handle()),
	}
}

// NewEncoderBytes produces a ugorji Encoder using the appropriate WRP configuration
// for the given format
func NewEncoderBytes(output *[]byte, f Format) Encoder {
	return &encoderDecorator{
		codec.NewEncoderBytes(output, f.handle()),
	}
}

// NewDecoder produces a ugorji Decoder using the appropriate WRP configuration
// for the given format
func NewDecoder(input io.Reader, f Format) Decoder {
	return codec.NewDecoder(input, f.handle())
}

// NewDecoderBytes produces a ugorji Decoder using the appropriate WRP configuration
// for the given format
func NewDecoderBytes(input []byte, f Format) Decoder {
	return codec.NewDecoderBytes(input, f.handle())
}

// TranscodeMessage converts a WRP message of any type from one format into another,
// e.g. from JSON into Msgpack.  The intermediate, generic Message used to hold decoded
// values is returned in addition to any error.  If a decode error occurs, this function
// will not perform the encoding step.
func TranscodeMessage(target Encoder, source Decoder) (msg *Message, err error) {
	msg = new(Message)
	if err = source.Decode(msg); err == nil {
		err = target.Encode(msg)
	}

	return
}

// MustEncode is a convenience function that attempts to encode a given message.  A panic
// is raised on any error.  This function is handy for package initialization.
func MustEncode(message interface{}, f Format) []byte {
	var (
		output  bytes.Buffer
		encoder = NewEncoder(&output, f)
	)

	if err := encoder.Encode(message); err != nil {
		panic(err)
	}

	return output.Bytes()
}
<commit_msg>documented the WriteExt/RawToString issue<commit_after>package wrp

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/ugorji/go/codec"
)

//go:generate stringer -type=Format

// Format indicates which format is desired.
// The zero value indicates Msgpack, which means by default other
// infrastructure can assume msgpack-formatted data.
type Format int

const (
	Msgpack Format = iota
	JSON
	lastFormat
)

// AllFormats returns a distinct slice of all supported formats.
func AllFormats() []Format {
	return []Format{Msgpack, JSON}
}

var (
	jsonHandle = codec.JsonHandle{
		BasicHandle: codec.BasicHandle{
			TypeInfos: codec.NewTypeInfos([]string{"wrp"}),
		},
		IntegerAsString: 'L',
	}

	// msgpackHandle uses the configuration required for the updated msgpack spec.
	// this is what's required to ensure that the Payload field is encoded and decoded properly.
	// See: http://ugorji.net/blog/go-codec-primer#format-specific-runtime-configuration
	msgpackHandle = codec.MsgpackHandle{
		WriteExt:    true,
		RawToString: true,
		BasicHandle: codec.BasicHandle{
			TypeInfos: codec.NewTypeInfos([]string{"wrp"}),
		},
	}
)

// ContentType returns the MIME type associated with this format
func (f Format) ContentType() string {
	switch f {
	case Msgpack:
		return "application/msgpack"
	case JSON:
		return "application/json"
	default:
		return "application/octet-stream"
	}
}

// FormatFromContentType examines the Content-Type value and returns
// the appropriate Format.  This function returns an error if the given
// Content-Type did not map to a WRP format.
func FormatFromContentType(contentType string) (Format, error) {
	if strings.Contains(contentType, "json") {
		return JSON, nil
	} else if strings.Contains(contentType, "msgpack") {
		return Msgpack, nil
	}

	return Format(-1), fmt.Errorf("Invalid WRP content type: %s", contentType)
}

// handle looks up the appropriate codec.Handle for this format constant.
// This method panics if the format is not a valid value.
func (f Format) handle() codec.Handle {
	switch f {
	case Msgpack:
		return &msgpackHandle
	case JSON:
		return &jsonHandle
	}

	panic(fmt.Errorf("Invalid format constant: %d", f))
}

// EncodeListener can be implemented on any type passed to an Encoder in order
// to get notified when an encoding happens.  This interface is useful to set
// mandatory fields, such as message type.
type EncodeListener interface {
	BeforeEncode() error
}

// Encoder represents the underlying ugorji behavior that WRP supports
type Encoder interface {
	Encode(interface{}) error
	Reset(io.Writer)
	ResetBytes(*[]byte)
}

// encoderDecorator wraps a ugorji Encoder and implements the wrp.Encoder interface.
type encoderDecorator struct {
	*codec.Encoder
}

// Encode checks to see if value implements EncoderTo and if it does, uses the
// value.EncodeTo() method.  Otherwise, the value is passed as is to the decorated
// ugorji Encoder.
func (ed *encoderDecorator) Encode(value interface{}) error {
	if listener, ok := value.(EncodeListener); ok {
		if err := listener.BeforeEncode(); err != nil {
			return err
		}
	}

	return ed.Encoder.Encode(value)
}

// Decoder represents the underlying ugorji behavior that WRP supports
type Decoder interface {
	Decode(interface{}) error
	Reset(io.Reader)
	ResetBytes([]byte)
}

// NewEncoder produces a ugorji Encoder using the appropriate WRP configuration
// for the given format
func NewEncoder(output io.Writer, f Format) Encoder {
	return &encoderDecorator{
		codec.NewEncoder(output, f.handle()),
	}
}

// NewEncoderBytes produces a ugorji Encoder using the appropriate WRP configuration
// for the given format
func NewEncoderBytes(output *[]byte, f Format) Encoder {
	return &encoderDecorator{
		codec.NewEncoderBytes(output, f.handle()),
	}
}

// NewDecoder produces a ugorji Decoder using the appropriate WRP configuration
// for the given format
func NewDecoder(input io.Reader, f Format) Decoder {
	return codec.NewDecoder(input, f.handle())
}

// NewDecoderBytes produces a ugorji Decoder using the appropriate WRP configuration
// for the given format
func NewDecoderBytes(input []byte, f Format) Decoder {
	return codec.NewDecoderBytes(input, f.handle())
}

// TranscodeMessage converts a WRP message of any type from one format into another,
// e.g. from JSON into Msgpack.  The intermediate, generic Message used to hold decoded
// values is returned in addition to any error.  If a decode error occurs, this function
// will not perform the encoding step.
func TranscodeMessage(target Encoder, source Decoder) (msg *Message, err error) {
	msg = new(Message)
	if err = source.Decode(msg); err == nil {
		err = target.Encode(msg)
	}

	return
}

// MustEncode is a convenience function that attempts to encode a given message.  A panic
// is raised on any error.  This function is handy for package initialization.
func MustEncode(message interface{}, f Format) []byte {
	var (
		output  bytes.Buffer
		encoder = NewEncoder(&output, f)
	)

	if err := encoder.Encode(message); err != nil {
		panic(err)
	}

	return output.Bytes()
}
<|endoftext|>


<commit_before>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package vulncheck detects uses of known vulnerabilities
// in Go binaries and source code.
package vulncheck

import (
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"strings"

	"golang.org/x/tools/go/packages"
	"golang.org/x/vuln/client"
	"golang.org/x/vuln/osv"
)

// Config is used for configuring vulncheck algorithms.
type Config struct {
	// ImportsOnly flag, if true, signals vulncheck to analyze import chains only.
	// Otherwise, call chains are analyzed too.
	ImportsOnly bool
	// Client is used for querying data from a vulnerability database.
	Client client.Client
}

// Package models Go package for vulncheck analysis. A version
// of packages.Package trimmed down to reduce memory consumption.
type Package struct {
	Name      string
	PkgPath   string
	Imports   []*Package
	Pkg       *types.Package
	Fset      *token.FileSet
	Syntax    []*ast.File
	TypesInfo *types.Info
	Module    *Module
}

// Module models Go module for vulncheck analysis.
type Module struct {
	Path    string
	Version string
	Dir     string
	Replace *Module
}

// Convert converts a slice of packages.Package to
// a slice of corresponding vulncheck.Package.
func Convert(pkgs []*packages.Package) []*Package {
	convertMod := newModuleConverter()
	ps := make(map[*packages.Package]*Package)
	var pkg func(*packages.Package) *Package
	pkg = func(p *packages.Package) *Package {
		if vp, ok := ps[p]; ok {
			return vp
		}

		vp := &Package{
			Name:      p.Name,
			PkgPath:   p.PkgPath,
			Pkg:       p.Types,
			Fset:      p.Fset,
			Syntax:    p.Syntax,
			TypesInfo: p.TypesInfo,
			Module:    convertMod(p.Module),
		}
		ps[p] = vp

		for _, i := range p.Imports {
			vp.Imports = append(vp.Imports, pkg(i))
		}
		return vp
	}

	var vpkgs []*Package
	for _, p := range pkgs {
		vpkgs = append(vpkgs, pkg(p))
	}
	return vpkgs
}

// Result contains information on which vulnerabilities are potentially affecting
// user code and how are they affecting it via call graph, package imports graph,
// and module requires graph.
type Result struct {
	// Calls is a call graph whose roots are program entry functions/methods and
	// sinks are vulnerable functions/methods. Empty when Config.ImportsOnly=true
	// or when no vulnerable symbols are reachable via program call graph.
	Calls *CallGraph
	// Imports is a package dependency graph whose roots are entry user packages
	// and sinks are the packages with some vulnerable symbols. Empty when no
	// packages with some vulnerabilities are imported in the program.
	Imports *ImportGraph
	// Requires is a module dependency graph whose roots are entry user modules
	// and sinks are modules with some vulnerable packages. Empty when no modules
	// with some vulnerabilities are required by the program.
	Requires *RequireGraph

	// Vulns contains information on detected vulnerabilities and their place in
	// the above graphs. Only vulnerabilities whose symbols are reachable in Calls,
	// or whose packages are imported in Imports, or whose modules are required in
	// Requires, have an entry in Vulns.
	Vulns []*Vuln

	// Modules are the modules that comprise the user code.
	Modules []*Module
}

// Vuln provides information on how a vulnerability is affecting user code by
// connecting it to the Result.{Calls,Imports,Requires} graphs. Vulnerabilities
// detected in Go binaries do not have a place in the Result graphs.
type Vuln struct {
	// The next four fields identify a vulnerability. Note that *osv.Entry
	// describes potentially multiple symbols from multiple packages.

	// OSV contains information on detected vulnerability in the shared
	// vulnerability format.
	OSV *osv.Entry
	// Symbol is the name of the detected vulnerable function or method.
	Symbol string
	// PkgPath is the package path of the detected Symbol.
	PkgPath string
	// ModPath is the module path corresponding to PkgPath.
	ModPath string

	// CallSink is the ID of the sink node in Calls graph corresponding to
	// the use of Symbol. ID is not available (denoted with 0) in binary mode,
	// or if Symbol is not reachable, or if Config.ImportsOnly=true.
	CallSink int
	// ImportSink is the ID of the sink node in the Imports graph corresponding
	// to the import of PkgPath. ID is not available (denoted with 0) in binary
	// mode or if PkgPath is not imported.
	ImportSink int
	// RequireSink is the ID of the sink node in Requires graph corresponding
	// to the require statement of ModPath. ID is not available (denoted with 0)
	// in binary mode.
	RequireSink int
}

// CallGraph is a slice of a full program call graph whose sinks are conceptually
// vulnerable functions and sources are entry points of user packages. In order to
// support succinct traversal of the slice related to a particular vulnerability,
// CallGraph is technically backwards directed, i.e., from a vulnerable function
// towards the program entry functions (see FuncNode).
type CallGraph struct {
	// Functions contains all call graph nodes as a map: func node id -> func node.
	Functions map[int]*FuncNode
	// Entries are IDs of a subset of Functions representing vulncheck entry points.
	Entries []int
}

type FuncNode struct {
	ID   int
	Name string
	// RecvType is the receiver object type of this function, if any.
	RecvType string
	PkgPath  string
	Pos      *token.Position
	// CallSites is a set of call sites where this function is called.
	CallSites []*CallSite
}

func (fn *FuncNode) String() string {
	if fn.RecvType == "" {
		return fmt.Sprintf("%s.%s", fn.PkgPath, fn.Name)
	}
	return fmt.Sprintf("%s.%s", fn.RecvType, fn.Name)
}

type CallSite struct {
	// Parent is ID of the enclosing function where the call is made.
	Parent int
	// Name stands for the name of the function (variable) being called.
	Name string
	// RecvType is the full path of the receiver object type, if any.
	RecvType string
	Pos      *token.Position
	// Resolved indicates if the called function can be statically resolved.
	Resolved bool
}

// RequireGraph is a slice of a full program module requires graph whose sinks
// are conceptually modules with some known vulnerabilities and sources are modules
// of user entry packages. In order to support succinct traversal of the slice
// related to a particular vulnerability, RequireGraph is technically backwards
// directed, i.e., from a vulnerable module towards the program entry modules (see ModNode).
type RequireGraph struct {
	// Modules contains all module nodes as a map: module node id -> module node.
	Modules map[int]*ModNode
	// Entries are IDs of a subset of Modules representing modules of vulncheck entry points.
	Entries []int
}

type ModNode struct {
	ID      int
	Path    string
	Version string
	// Replace is the ID of the replacement module node, if any.
	Replace int
	// RequiredBy contains IDs of the modules requiring this module.
	RequiredBy []int
}

// ImportGraph is a slice of a full program package import graph whose sinks are
// conceptually packages with some known vulnerabilities and sources are user
// specified packages. In order to support succinct traversal of the slice related
// to a particular vulnerability, ImportGraph is technically backwards directed,
// i.e., from a vulnerable package towards the program entry packages (see PkgNode).
type ImportGraph struct {
	// Packages contains all package nodes as a map: package node id -> package node.
	Packages map[int]*PkgNode
	// Entries are IDs of a subset of Packages representing packages of vulncheck entry points.
	Entries []int
}

type PkgNode struct {
	ID int
	// Name is the package identifier as it appears in the source code.
	Name string
	Path string
	// Module holds ID of the corresponding module (node) in Requires graph.
	Module int
	// ImportedBy contains IDs of packages directly importing this package.
	ImportedBy []int

	// pkg is used for connecting package node to module and call graph nodes.
	pkg *Package
}

// moduleVulnerabilities is an internal structure for
// holding and querying vulnerabilities provided by a
// vulnerability database client.
type moduleVulnerabilities []modVulns

// modVulns groups vulnerabilities per module.
type modVulns struct {
	mod   *Module
	vulns []*osv.Entry
}

func (mv moduleVulnerabilities) filter(os, arch string) moduleVulnerabilities {
	var filteredMod moduleVulnerabilities
	for _, mod := range mv {
		module := mod.mod
		modVersion := module.Version
		if module.Replace != nil {
			modVersion = module.Replace.Version
		}
		// TODO(https://golang.org/issues/49264): if modVersion == "", try vcs?
		var filteredVulns []*osv.Entry
		for _, v := range mod.vulns {
			var filteredAffected []osv.Affected
			for _, a := range v.Affected {
				// A module version is affected if
				//  - it is included in one of the affected version ranges
				//  - and module version is not ""
				//  The latter means the module version is not available, so
				//  we don't want to spam users with potential false alarms.
				//  TODO: issue warning for "" cases above?
				affected := modVersion != "" && a.Ranges.AffectsSemver(modVersion) && matchesPlatform(os, arch, a.EcosystemSpecific)
				if affected {
					filteredAffected = append(filteredAffected, a)
				}
			}
			if len(filteredAffected) == 0 {
				continue
			}
			// save the non-empty vulnerability with only
			// affected symbols.
			newV := *v
			newV.Affected = filteredAffected
			filteredVulns = append(filteredVulns, &newV)
		}
		filteredMod = append(filteredMod, modVulns{
			mod:   module,
			vulns: filteredVulns,
		})
	}
	return filteredMod
}

func matchesPlatform(os, arch string, e osv.EcosystemSpecific) bool {
	matchesOS := len(e.GOOS) == 0
	matchesArch := len(e.GOARCH) == 0
	for _, o := range e.GOOS {
		if os == o {
			matchesOS = true
			break
		}
	}
	for _, a := range e.GOARCH {
		if arch == a {
			matchesArch = true
			break
		}
	}
	return matchesOS && matchesArch
}

// vulnsForPackage returns the vulnerabilities for the module which is the most
// specific prefix of importPath, or nil if there is no matching module with
// vulnerabilities.
func (mv moduleVulnerabilities) vulnsForPackage(importPath string) []*osv.Entry {
	var mostSpecificMod *modVulns
	for _, mod := range mv {
		md := mod
		if strings.HasPrefix(importPath, md.mod.Path) {
			if mostSpecificMod == nil || len(mostSpecificMod.mod.Path) < len(md.mod.Path) {
				mostSpecificMod = &md
			}
		}
	}

	if mostSpecificMod == nil {
		return nil
	}

	if mostSpecificMod.mod.Replace != nil {
		importPath = fmt.Sprintf("%s%s", mostSpecificMod.mod.Replace.Path, strings.TrimPrefix(importPath, mostSpecificMod.mod.Path))
	}
	vulns := mostSpecificMod.vulns
	packageVulns := []*osv.Entry{}
	for _, v := range vulns {
		for _, a := range v.Affected {
			if a.Package.Name == importPath {
				packageVulns = append(packageVulns, v)
				break
			}
		}
	}
	return packageVulns
}

// vulnsForSymbol returns vulnerabilities for `symbol` in `mv.VulnsForPackage(importPath)`.
func (mv moduleVulnerabilities) vulnsForSymbol(importPath, symbol string) []*osv.Entry {
	vulns := mv.vulnsForPackage(importPath)
	if vulns == nil {
		return nil
	}

	symbolVulns := []*osv.Entry{}
	for _, v := range vulns {
	vulnLoop:
		for _, a := range v.Affected {
			if a.Package.Name != importPath {
				continue
			}
			if len(a.EcosystemSpecific.Symbols) == 0 {
				symbolVulns = append(symbolVulns, v)
				continue vulnLoop
			}
			for _, s := range a.EcosystemSpecific.Symbols {
				if s == symbol {
					symbolVulns = append(symbolVulns, v)
					continue vulnLoop
				}
			}
		}
	}
	return symbolVulns
}

func newModuleConverter() func(m *packages.Module) *Module {
	pmap := map[*packages.Module]*Module{}
	var convert func(m *packages.Module) *Module
	convert = func(m *packages.Module) *Module {
		if m == nil {
			return nil
		}
		if vm, ok := pmap[m]; ok {
			return vm
		}
		vm := &Module{
			Path:    m.Path,
			Version: m.Version,
			Dir:     m.Dir,
			Replace: convert(m.Replace),
		}
		pmap[m] = vm
		return vm
	}
	return convert
}
<commit_msg>vulncheck: clarify documentation<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package vulncheck detects uses of known vulnerabilities
// in Go binaries and source code.
package vulncheck

import (
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"strings"

	"golang.org/x/tools/go/packages"
	"golang.org/x/vuln/client"
	"golang.org/x/vuln/osv"
)

// Config is used for configuring vulncheck algorithms.
type Config struct {
	// If ImportsOnly is true, vulncheck analyzes import chains only.
	// Otherwise, call chains are analyzed too.
	ImportsOnly bool
	// Client is used for querying data from a vulnerability database.
	Client client.Client
}

// Package is a Go package for vulncheck analysis. It is a version of
// packages.Package trimmed down to reduce memory consumption.
type Package struct {
	Name      string
	PkgPath   string
	Imports   []*Package
	Pkg       *types.Package
	Fset      *token.FileSet
	Syntax    []*ast.File
	TypesInfo *types.Info
	Module    *Module
}

// Module is a Go module for vulncheck analysis.
type Module struct {
	Path    string
	Version string
	Dir     string
	Replace *Module
}

// Convert converts a slice of packages.Package to
// a slice of corresponding vulncheck.Package.
func Convert(pkgs []*packages.Package) []*Package {
	convertMod := newModuleConverter()
	ps := make(map[*packages.Package]*Package)
	var pkg func(*packages.Package) *Package
	pkg = func(p *packages.Package) *Package {
		if vp, ok := ps[p]; ok {
			return vp
		}

		vp := &Package{
			Name:      p.Name,
			PkgPath:   p.PkgPath,
			Pkg:       p.Types,
			Fset:      p.Fset,
			Syntax:    p.Syntax,
			TypesInfo: p.TypesInfo,
			Module:    convertMod(p.Module),
		}
		ps[p] = vp

		for _, i := range p.Imports {
			vp.Imports = append(vp.Imports, pkg(i))
		}
		return vp
	}

	var vpkgs []*Package
	for _, p := range pkgs {
		vpkgs = append(vpkgs, pkg(p))
	}
	return vpkgs
}

// Result contains information on which vulnerabilities are potentially
// affecting user code and how are they affecting it via a call graph, package
// imports graph, and module requires graph.
type Result struct {
	// Calls is a call graph whose roots are program entry functions/methods and
	// sinks are vulnerable functions/methods. It is empty when
	// Config.ImportsOnly is true or when no vulnerable symbols are reachable
	// via the program call graph.
	Calls *CallGraph
	// Imports is a package dependency graph whose roots are entry user packages
	// and sinks are packages with some vulnerable symbols. It is empty when no
	// packages with vulnerabilities are imported in the program.
	Imports *ImportGraph
	// Requires is a module dependency graph whose roots are entry user modules
	// and sinks are modules with some vulnerable packages. It is empty when no
	// modules with vulnerabilities are required by the program.
	Requires *RequireGraph

	// Vulns contains information on detected vulnerabilities and their place in
	// the above graphs. Only vulnerabilities whose symbols are reachable in Calls,
	// or whose packages are imported in Imports, or whose modules are required in
	// Requires, have an entry in Vulns.
	Vulns []*Vuln

	// Modules are the modules that comprise the user code.
	Modules []*Module
}

// Vuln provides information on how a vulnerability is affecting user code by
// connecting it to the Result.{Calls,Imports,Requires} graphs. Vulnerabilities
// detected in Go binaries do not appear in the Result graphs.
type Vuln struct {
	// The next four fields identify a vulnerability. Note that *osv.Entry
	// describes potentially multiple symbols from multiple packages.

	// OSV contains information on the detected vulnerability in the shared
	// vulnerability format.
	OSV *osv.Entry
	// Symbol is the name of the detected vulnerable function or method.
	Symbol string
	// PkgPath is the package path of the detected Symbol.
	PkgPath string
	// ModPath is the module path corresponding to PkgPath.
	ModPath string

	// CallSink is the ID of the sink node in the Calls graph corresponding to
	// the use of Symbol. ID is not available (denoted with 0) in binary mode,
	// or if Symbol is not reachable, or if Config.ImportsOnly is true.
	CallSink int
	// ImportSink is the ID of the sink node in the Imports graph corresponding
	// to the import of PkgPath. ID is not available (denoted with 0) in binary
	// mode or if PkgPath is not imported.
	ImportSink int
	// RequireSink is the ID of the sink node in Requires graph corresponding
	// to the require statement of ModPath. ID is not available (denoted with 0)
	// in binary mode.
	RequireSink int
}

// CallGraph is a slice of a full program call graph whose sinks are vulnerable
// functions and sources are entry points of user packages. In order to support
// succinct traversal of the slice related to a particular vulnerability,
// CallGraph is directed from vulnerable functions towards program entry
// functions (see FuncNode).
type CallGraph struct {
	// Functions contains all call graph nodes as a map: func node id -> func node.
	Functions map[int]*FuncNode
	// Entries are IDs of a subset of Functions representing vulncheck entry points.
	Entries []int
}

// A FuncNode describes a function in the call graph.
type FuncNode struct {
	ID   int
	Name string
	// RecvType is the receiver object type of this function, if any.
	RecvType string
	PkgPath  string
	Pos      *token.Position
	// CallSites is a set of call sites where this function is called.
	CallSites []*CallSite
}

func (fn *FuncNode) String() string {
	if fn.RecvType == "" {
		return fmt.Sprintf("%s.%s", fn.PkgPath, fn.Name)
	}
	return fmt.Sprintf("%s.%s", fn.RecvType, fn.Name)
}

// A CallSite describes a function call.
type CallSite struct {
	// Parent is ID of the enclosing function where the call is made.
	Parent int
	// Name stands for the name of the function (variable) being called.
	Name string
	// RecvType is the full path of the receiver object type, if any.
	RecvType string
	Pos      *token.Position
	// Resolved indicates if the called function can be statically resolved.
	Resolved bool
}

// RequireGraph is a slice of a full program module requires graph whose sinks
// are modules with known vulnerabilities and sources are modules of user entry
// packages. In order to support succinct traversal of the slice related to a
// particular vulnerability, RequireGraph is directed from a vulnerable module
// towards the program entry modules (see ModNode).
type RequireGraph struct {
	// Modules contains all module nodes as a map: module node id -> module node.
	Modules map[int]*ModNode
	// Entries are IDs of a subset of Modules representing modules of vulncheck entry points.
	Entries []int
}

// A ModNode describes a module in the requires graph.
type ModNode struct {
	ID      int
	Path    string
	Version string
	// Replace is the ID of the replacement module node.
	// A zero value means there is no replacement.
	Replace int
	// RequiredBy contains IDs of the modules requiring this module.
	RequiredBy []int
}

// ImportGraph is a slice of a full program package import graph whose sinks are
// packages with some known vulnerabilities and sources are user specified
// packages. In order to support succinct traversal of the slice related to a
// particular vulnerability, ImportGraph is directed from a vulnerable package
// towards the program entry packages (see PkgNode).
type ImportGraph struct {
	// Packages contains all package nodes as a map: package node id -> package node.
	Packages map[int]*PkgNode
	// Entries are IDs of a subset of Packages representing packages of vulncheck entry points.
	Entries []int
}

// A PkgNode describes a package in the import graph.
type PkgNode struct {
	ID int
	// Name is the package identifier as it appears in the source code.
	Name string
	Path string
	// Module holds ID of the corresponding module (node) in the Requires graph.
	Module int
	// ImportedBy contains IDs of packages directly importing this package.
	ImportedBy []int

	// pkg is used for connecting package node to module and call graph nodes.
	pkg *Package
}

// moduleVulnerabilities is an internal structure for
// holding and querying vulnerabilities provided by a
// vulnerability database client.
type moduleVulnerabilities []modVulns

// modVulns groups vulnerabilities per module.
type modVulns struct {
	mod   *Module
	vulns []*osv.Entry
}

func (mv moduleVulnerabilities) filter(os, arch string) moduleVulnerabilities {
	var filteredMod moduleVulnerabilities
	for _, mod := range mv {
		module := mod.mod
		modVersion := module.Version
		if module.Replace != nil {
			modVersion = module.Replace.Version
		}
		// TODO(https://golang.org/issues/49264): if modVersion == "", try vcs?
		var filteredVulns []*osv.Entry
		for _, v := range mod.vulns {
			var filteredAffected []osv.Affected
			for _, a := range v.Affected {
				// A module version is affected if
				//  - it is included in one of the affected version ranges
				//  - and module version is not ""
				//  The latter means the module version is not available, so
				//  we don't want to spam users with potential false alarms.
				//  TODO: issue warning for "" cases above?
				affected := modVersion != "" && a.Ranges.AffectsSemver(modVersion) && matchesPlatform(os, arch, a.EcosystemSpecific)
				if affected {
					filteredAffected = append(filteredAffected, a)
				}
			}
			if len(filteredAffected) == 0 {
				continue
			}
			// save the non-empty vulnerability with only
			// affected symbols.
			newV := *v
			newV.Affected = filteredAffected
			filteredVulns = append(filteredVulns, &newV)
		}
		filteredMod = append(filteredMod, modVulns{
			mod:   module,
			vulns: filteredVulns,
		})
	}
	return filteredMod
}

func matchesPlatform(os, arch string, e osv.EcosystemSpecific) bool {
	matchesOS := len(e.GOOS) == 0
	matchesArch := len(e.GOARCH) == 0
	for _, o := range e.GOOS {
		if os == o {
			matchesOS = true
			break
		}
	}
	for _, a := range e.GOARCH {
		if arch == a {
			matchesArch = true
			break
		}
	}
	return matchesOS && matchesArch
}

// vulnsForPackage returns the vulnerabilities for the module which is the most
// specific prefix of importPath, or nil if there is no matching module with
// vulnerabilities.
func (mv moduleVulnerabilities) vulnsForPackage(importPath string) []*osv.Entry {
	var mostSpecificMod *modVulns
	for _, mod := range mv {
		md := mod
		if strings.HasPrefix(importPath, md.mod.Path) {
			if mostSpecificMod == nil || len(mostSpecificMod.mod.Path) < len(md.mod.Path) {
				mostSpecificMod = &md
			}
		}
	}

	if mostSpecificMod == nil {
		return nil
	}

	if mostSpecificMod.mod.Replace != nil {
		importPath = fmt.Sprintf("%s%s", mostSpecificMod.mod.Replace.Path, strings.TrimPrefix(importPath, mostSpecificMod.mod.Path))
	}
	vulns := mostSpecificMod.vulns
	packageVulns := []*osv.Entry{}
	for _, v := range vulns {
		for _, a := range v.Affected {
			if a.Package.Name == importPath {
				packageVulns = append(packageVulns, v)
				break
			}
		}
	}
	return packageVulns
}

// vulnsForSymbol returns vulnerabilities for `symbol` in `mv.VulnsForPackage(importPath)`.
func (mv moduleVulnerabilities) vulnsForSymbol(importPath, symbol string) []*osv.Entry {
	vulns := mv.vulnsForPackage(importPath)
	if vulns == nil {
		return nil
	}

	symbolVulns := []*osv.Entry{}
	for _, v := range vulns {
	vulnLoop:
		for _, a := range v.Affected {
			if a.Package.Name != importPath {
				continue
			}
			if len(a.EcosystemSpecific.Symbols) == 0 {
				symbolVulns = append(symbolVulns, v)
				continue vulnLoop
			}
			for _, s := range a.EcosystemSpecific.Symbols {
				if s == symbol {
					symbolVulns = append(symbolVulns, v)
					continue vulnLoop
				}
			}
		}
	}
	return symbolVulns
}

func newModuleConverter() func(m *packages.Module) *Module {
	pmap := map[*packages.Module]*Module{}
	var convert func(m *packages.Module) *Module
	convert = func(m *packages.Module) *Module {
		if m == nil {
			return nil
		}
		if vm, ok := pmap[m]; ok {
			return vm
		}
		vm := &Module{
			Path:    m.Path,
			Version: m.Version,
			Dir:     m.Dir,
			Replace: convert(m.Replace),
		}
		pmap[m] = vm
		return vm
	}
	return convert
}
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package adb

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

const (
	port = 5037
)

var (
	ErrInternalError = errors.New("internal error")
	ErrUnknown       = errors.New("unknown error")
)

func IsServerRunning() bool {
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", "127.0.0.1", port))
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

func StartServer() error {
	cmd := exec.Command("adb", "start-server")
	err := cmd.Run()
	if err != nil {
		return err
	}
	return cmd.Wait()
}

type Device struct {
	Id      string
	Usb     string
	Product string
	Model   string
	Device  string
}

var deviceRegex = regexp.MustCompile(`^([^\s]+)\s+device(.*)$`)

func Devices() ([]*Device, error) {
	cmd := exec.Command("adb", "devices", "-l")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	var devices []*Device
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		line := scanner.Text()
		m := deviceRegex.FindStringSubmatch(line)
		if m == nil {
			continue
		}
		device := &Device{
			Id: m[1],
		}
		extras := m[2]
		for _, extra := range strings.Split(extras, " ") {
			sp := strings.SplitN(extra, ":", 2)
			if len(sp) < 2 {
				continue
			}
			switch sp[0] {
			case "usb":
				device.Usb = sp[1]
			case "product":
				device.Product = sp[1]
			case "model":
				device.Model = sp[1]
			case "device":
				device.Device = sp[1]
			}
		}
		devices = append(devices, device)
	}
	return devices, nil
}

func (d *Device) AdbCmd(args ...string) *exec.Cmd {
	cmdArgs := append([]string{"-s", d.Id}, args...)
	return exec.Command("adb", cmdArgs...)
}

func (d *Device) AdbShell(args ...string) *exec.Cmd {
	shellArgs := append([]string{"shell"}, args...)
	return d.AdbCmd(shellArgs...)
}

func (d *Device) Install(path string) error {
	cmd := d.AdbCmd("install", path)
	if err := cmd.Start(); err != nil {
		return err
	}
	return cmd.Wait()
}

func getLine(out io.ReadCloser) string {
	scanner := bufio.NewScanner(out)
	if !scanner.Scan() {
		return ""
	}
	return scanner.Text()
}

func (d *Device) Uninstall(pkg string) error {
	cmd := d.AdbCmd("uninstall", pkg)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	line := getLine(stdout)
	if line == "Success" {
		return nil
	}
	switch line {
	case "Failure [DELETE_FAILED_INTERNAL_ERROR]":
		return ErrInternalError
	}
	return ErrUnknown
}

type Package struct {
	ID    string
	VCode int
	VName string
}

var (
	packageRegex = regexp.MustCompile(`^  Package \[([^\s]+)\]`)
	verCodeRegex = regexp.MustCompile(`^    versionCode=([0-9]+)`)
	verNameRegex = regexp.MustCompile(`^    versionName=(.+)`)
)

func (d *Device) Installed() (map[string]Package, error) {
	cmd := d.AdbShell("dumpsys", "package", "packages")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	packages := make(map[string]Package)
	scanner := bufio.NewScanner(stdout)
	var cur Package
	first := true
	for scanner.Scan() {
		l := scanner.Text()
		if m := packageRegex.FindStringSubmatch(l); m != nil {
			if first {
				first = false
			} else {
				packages[cur.ID] = cur
				cur = Package{}
			}
			cur.ID = m[1]
		} else if m := verCodeRegex.FindStringSubmatch(l); m != nil {
			n, err := strconv.Atoi(m[1])
			if err != nil {
				panic(err)
			}
			cur.VCode = n
		} else if m := verNameRegex.FindStringSubmatch(l); m != nil {
			cur.VName = m[1]
		}
	}
	if !first {
		packages[cur.ID] = cur
	}
	return packages, nil
}
<commit_msg>Add all possible uninstall errors<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package adb

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"net"
	"os/exec"
	"regexp"
	"strconv"
	"strings"
)

const (
	port = 5037
)

var (
	ErrInternalError       = errors.New("internal error")
	ErrDevicePolicyManager = errors.New("device policy manager")
	ErrUserRestricted      = errors.New("user restricted")
	ErrOwnerBlocked        = errors.New("owner blocked")
	ErrAborted             = errors.New("aborted")
)

func IsServerRunning() bool {
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", "127.0.0.1", port))
	if err != nil {
		return false
	}
	conn.Close()
	return true
}

func StartServer() error {
	cmd := exec.Command("adb", "start-server")
	err := cmd.Run()
	if err != nil {
		return err
	}
	return cmd.Wait()
}

type Device struct {
	Id      string
	Usb     string
	Product string
	Model   string
	Device  string
}

var deviceRegex = regexp.MustCompile(`^([^\s]+)\s+device(.*)$`)

func Devices() ([]*Device, error) {
	cmd := exec.Command("adb", "devices", "-l")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	var devices []*Device
	scanner := bufio.NewScanner(stdout)
	for scanner.Scan() {
		line := scanner.Text()
		m := deviceRegex.FindStringSubmatch(line)
		if m == nil {
			continue
		}
		device := &Device{
			Id: m[1],
		}
		extras := m[2]
		for _, extra := range strings.Split(extras, " ") {
			sp := strings.SplitN(extra, ":", 2)
			if len(sp) < 2 {
				continue
			}
			switch sp[0] {
			case "usb":
				device.Usb = sp[1]
			case "product":
				device.Product = sp[1]
			case "model":
				device.Model = sp[1]
			case "device":
				device.Device = sp[1]
			}
		}
		devices = append(devices, device)
	}
	return devices, nil
}

func (d *Device) AdbCmd(args ...string) *exec.Cmd {
	cmdArgs := append([]string{"-s", d.Id}, args...)
	return exec.Command("adb", cmdArgs...)
}

func (d *Device) AdbShell(args ...string) *exec.Cmd {
	shellArgs := append([]string{"shell"}, args...)
	return d.AdbCmd(shellArgs...)
}

func (d *Device) Install(path string) error {
	cmd := d.AdbCmd("install", path)
	if err := cmd.Start(); err != nil {
		return err
	}
	return cmd.Wait()
}

func getLine(out io.ReadCloser) string {
	scanner := bufio.NewScanner(out)
	if !scanner.Scan() {
		return ""
	}
	return scanner.Text()
}

func (d *Device) Uninstall(pkg string) error {
	cmd := d.AdbCmd("uninstall", pkg)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}
	if err := cmd.Start(); err != nil {
		return err
	}
	line := getLine(stdout)
	if line == "Success" {
		return nil
	}
	switch line {
	case "Failure [DELETE_FAILED_INTERNAL_ERROR]":
		return ErrInternalError
	case "Failure [DELETE_FAILED_DEVICE_POLICY_MANAGER]":
		return ErrDevicePolicyManager
	case "Failure [DELETE_FAILED_USER_RESTRICTED]":
		return ErrUserRestricted
	case "Failure [DELETE_FAILED_OWNER_BLOCKED]":
		return ErrOwnerBlocked
	case "Failure [DELETE_FAILED_ABORTED]":
		return ErrAborted
	}
	return errors.New("unknown error: " + line)
}

type Package struct {
	ID    string
	VCode int
	VName string
}

var (
	packageRegex = regexp.MustCompile(`^  Package \[([^\s]+)\]`)
	verCodeRegex = regexp.MustCompile(`^    versionCode=([0-9]+)`)
	verNameRegex = regexp.MustCompile(`^    versionName=(.+)`)
)

func (d *Device) Installed() (map[string]Package, error) {
	cmd := d.AdbShell("dumpsys", "package", "packages")
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	packages := make(map[string]Package)
	scanner := bufio.NewScanner(stdout)
	var cur Package
	first := true
	for scanner.Scan() {
		l := scanner.Text()
		if m := packageRegex.FindStringSubmatch(l); m != nil {
			if first {
				first = false
			} else {
				packages[cur.ID] = cur
				cur = Package{}
			}
			cur.ID = m[1]
		} else if m := verCodeRegex.FindStringSubmatch(l); m != nil {
			n, err := strconv.Atoi(m[1])
			if err != nil {
				panic(err)
			}
			cur.VCode = n
		} else if m := verNameRegex.FindStringSubmatch(l); m != nil {
			cur.VName = m[1]
		}
	}
	if !first {
		packages[cur.ID] = cur
	}
	return packages, nil
}
<|endoftext|>