<commit_before>package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/gizak/termui"
)

// TODO:
// - Different chart types
// - Check sizing

func drawTermChart(label []string, data []int) {
	// Determine width of chart
	dataPoints := len(data)
	barWidth := 4
	barGap := 1
	width := (barWidth * dataPoints) + (barGap * (dataPoints - 2))

	// If we can fit it all in one screen
	if width < 200 {
		bc := termui.NewBarChart()
		bclabels := label
		bc.Data = data
		bc.Height = 30
		bc.BarWidth = barWidth
		bc.BarGap = barGap
		bc.DataLabels = bclabels
		bc.TextColor = termui.ColorWhite
		bc.BarColor = termui.ColorBlue
		bc.NumColor = termui.ColorWhite
		termui.Body.AddRows(
			termui.NewRow(
				termui.NewCol(12, 0, bc),
			))
		termui.Body.Align()
		termui.Render(termui.Body)
	} else {
		requiredCharts := int(math.Ceil(float64(width) / 200.0))
		fmt.Println("Number of charts required:", requiredCharts)
		var datas = make([][]int, requiredCharts)
		var labels = make([][]string, requiredCharts)
		// Split the data
		for i := 0; i < dataPoints; i++ {
			chartNo := (i % requiredCharts)
			datas[chartNo] = append(datas[chartNo], data[i])
			labels[chartNo] = append(labels[chartNo], label[i])
		}
		// Create the charts
		barCharts := make([]termui.Bufferer, requiredCharts)
		for z := 0; z < requiredCharts; z++ {
			bc := termui.NewBarChart()
			bclabels := labels[z]
			bc.Data = datas[z]
			bc.Height = 30
			bc.BarWidth = int(float64(barWidth) * 1.5)
			bc.BarGap = barGap
			bc.DataLabels = bclabels
			bc.TextColor = termui.ColorWhite
			bc.BarColor = termui.ColorBlue
			bc.NumColor = termui.ColorWhite
			bc.SetY(z * 30)
			barCharts[z] = bc
			termui.Body.AddRows(
				termui.NewRow(
					termui.NewCol(12, 0, bc),
				),
			)
		}
		termui.Body.Align()
		termui.Render(termui.Body)
	}

	termui.Handle("/sys/kbd/q", func(termui.Event) {
		termui.StopLoop()
	})
	termui.Loop()

}

func tryDetectDelimeter(l string) string {
	var bd string
	md := 0
	ds := []string{",", "|", "\t", "."}
	for _, d := range ds {
		c := strings.Split(l, d)
		if len(c) > md {
			md = len(c)
			bd = d
		}
	}
	return bd
}

func tryDetectColTypes(l string, d string) []string {
	cols := strings.Split(l, d)
	colTypes := make([]string, len(cols))
	for i, col := range cols {
		_, err := strconv.ParseInt(col, 10, 64)
		if err == nil {
			colTypes[i] = "int"
			continue
		}
		_, err = strconv.ParseFloat(col, 10)
		if err == nil {
			colTypes[i] = "float"
			continue
		}
		_, err = strconv.ParseBool(col)
		if err == nil {
			colTypes[i] = "bool"
			continue
		}
		colTypes[i] = "string"
	}
	return colTypes
}

func main() {
	log.SetFormatter(&log.JSONFormatter{})
	if err := termui.Init(); err != nil {
		log.WithField("value", err).Fatal("Failed to start termui")
	}
	defer termui.Close()

	var (
		data   []int
		labels []string
		delim  string
		rows   int
	)
	x := bufio.NewScanner(os.Stdin)
	for {
		z := x.Scan()
		if z == false {
			break
		}

		line := x.Text()
		if rows == 0 {
			delim = tryDetectDelimeter(line)
		}
		values := strings.Split(line, delim)
		intVal, err := strconv.ParseInt(values[1], 10, 64)
		if err != nil {
			log.WithField("value", values[1]).Fatal("Failed to convert input data to int")
		}

		labels = append(labels, values[0])
		data = append(data, int(intVal))

		rows++
	}

	drawTermChart(labels, data)
}
<commit_msg>Fixing merge conflict<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"math"
	"os"
	"strconv"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/gizak/termui"
)

var (
	colorForeground = termui.ColorWhite
)

// TODO:
// - Different chart types
// - Check sizing

func drawTermChart(label []string, data []int) {
	// Determine width of chart
	dataPoints := len(data)
	barWidth := 4
	barGap := 1
	width := (barWidth * dataPoints) + (barGap * (dataPoints - 2))

	// If we can fit it all in one screen
	if width < 200 {
		bc := termui.NewBarChart()
		bclabels := label
		bc.Data = data
		bc.Height = 30
		bc.BarWidth = barWidth
		bc.BarGap = barGap
		bc.DataLabels = bclabels
		bc.TextColor = colorForeground
		bc.BarColor = termui.ColorBlue
		bc.NumColor = colorForeground
		termui.Body.AddRows(
			termui.NewRow(
				termui.NewCol(12, 0, bc),
			))
		termui.Body.Align()
		termui.Render(termui.Body)
	} else {
		requiredCharts := int(math.Ceil(float64(width) / 200.0))
		fmt.Println("Number of charts required:", requiredCharts)
		var datas = make([][]int, requiredCharts)
		var labels = make([][]string, requiredCharts)
		// Split the data
		for i := 0; i < dataPoints; i++ {
			chartNo := (i % requiredCharts)
			datas[chartNo] = append(datas[chartNo], data[i])
			labels[chartNo] = append(labels[chartNo], label[i])
		}
		// Create the charts
		barCharts := make([]termui.Bufferer, requiredCharts)
		for z := 0; z < requiredCharts; z++ {
			bc := termui.NewBarChart()
			bclabels := labels[z]
			bc.Data = datas[z]
			bc.Height = 30
			bc.BarWidth = int(float64(barWidth) * 1.5)
			bc.BarGap = barGap
			bc.DataLabels = bclabels
			bc.TextColor = colorForeground
			bc.BarColor = termui.ColorBlue
			bc.NumColor = colorForeground
			bc.SetY(z * 30)
			barCharts[z] = bc
			termui.Body.AddRows(
				termui.NewRow(
					termui.NewCol(12, 0, bc),
				),
			)
		}
		termui.Body.Align()
		termui.Render(termui.Body)
	}

	termui.Handle("/sys/kbd/q", func(termui.Event) {
		termui.StopLoop()
	})
	termui.Loop()

}

func tryDetectDelimeter(l string) string {
	var bd string
	md := 0
	ds := []string{",", "|", "\t", "."}
	for _, d := range ds {
		c := strings.Split(l, d)
		if len(c) > md {
			md = len(c)
			bd = d
		}
	}
	return bd
}

func tryDetectColTypes(l string, d string) []string {
	cols := strings.Split(l, d)
	colTypes := make([]string, len(cols))
	for i, col := range cols {
		_, err := strconv.ParseInt(col, 10, 64)
		if err == nil {
			colTypes[i] = "int"
			continue
		}
		_, err = strconv.ParseFloat(col, 10)
		if err == nil {
			colTypes[i] = "float"
			continue
		}
		_, err = strconv.ParseBool(col)
		if err == nil {
			colTypes[i] = "bool"
			continue
		}
		colTypes[i] = "string"
	}
	return colTypes
}

func mustParseFlags() {
	var theme string
	flag.StringVar(&theme, "theme", "dark", "color theme to use; one of: light, dark")
	flag.Parse()
	if theme != "light" && theme != "dark" {
		log.WithFields(log.Fields{"value": theme}).Fatal("unsupported theme name")
	}

	if theme == "light" {
		colorForeground = termui.ColorBlack
	}
}

func main() {
	log.SetFormatter(&log.JSONFormatter{})
	if err := termui.Init(); err != nil {
		log.WithField("value", err).Fatal("Failed to start termui")
	}
	defer termui.Close()

	var (
		data   []int
		labels []string
		delim  string
		rows   int
	)
	mustParseFlags()

	x := bufio.NewScanner(os.Stdin)
	for {
		z := x.Scan()
		if z == false {
			break
		}

		line := x.Text()
		if rows == 0 {
			delim = tryDetectDelimeter(line)
		}
		values := strings.Split(line, delim)
		intVal, err := strconv.ParseInt(values[1], 10, 64)
		if err != nil {
			log.WithField("value", values[1]).Fatal("Failed to convert input data to int")
		}

		labels = append(labels, values[0])
		data = append(data, int(intVal))

		rows++
	}

	drawTermChart(labels, data)
}
<|endoftext|>


<commit_before>package k8s

import (
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"encoding/json"

	"github.com/pkg/errors"
	"github.com/rancher/go-rancher/v3"
)

type Lookup struct {
	httpClient           http.Client
	accessKey, secretKey string
	clusterURL           string
}

func NewLookup(clusterURL, accessKey, secretKey string) *Lookup {
	return &Lookup{
		httpClient: http.Client{
			Timeout: 5 * time.Second,
		},
		accessKey:  accessKey,
		secretKey:  secretKey,
		clusterURL: clusterURL,
	}
}

func (c *Lookup) Lookup(input *http.Request) (*client.Cluster, bool, error) {
	clusterID := getClusterID(input)
	if clusterID == "" {
		return nil, false, nil
	}

	req, err := http.NewRequest("GET", c.clusterURL+"/"+clusterID, nil)
	if err != nil {
		return nil, false, err
	}

	req.Header.Set("Authorization", getAuthorizationHeader(req))

	cookie := getTokenCookie(input)
	if cookie != nil {
		req.AddCookie(cookie)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, false, err
	}
	defer close(resp)

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		c, err := parseCluster(resp)
		return c, true, err
	}

	auth := input.Header.Get("Authorization")
	if !strings.HasPrefix(auth, "Bearer ") {
		return nil, false, nil
	}

	auth = strings.TrimPrefix(auth, "Bearer ")

	req.Header.Del("Authorization")
	req.SetBasicAuth(c.accessKey, c.secretKey)
	resp2, err := c.httpClient.Do(req)
	if err != nil {
		return nil, false, err
	}
	defer close(resp2)

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, false, nil
	}

	cluster, err := parseCluster(resp2)
	if err != nil || cluster.K8sClientConfig == nil || cluster.K8sClientConfig.BearerToken != auth {
		return nil, false, err
	}

	return cluster, false, nil
}

func parseCluster(resp *http.Response) (*client.Cluster, error) {
	cluster := &client.Cluster{}
	if err := json.NewDecoder(resp.Body).Decode(cluster); err != nil {
		return nil, errors.Wrap(err, "Parsing clusters response")
	}
	return cluster, nil
}

func getClusterID(req *http.Request) string {
	parts := strings.Split(req.URL.Path, "/")
	if len(parts) > 3 && strings.HasPrefix(parts[2], "cluster") {
		return parts[3]
	}

	return ""
}

func getAuthorizationHeader(req *http.Request) string {
	return req.Header.Get("Authorization")
}

func getTokenCookie(req *http.Request) *http.Cookie {
	for _, cookie := range req.Cookies() {
		if cookie.Name == "token" {
			return cookie
		}
	}

	return nil
}

func close(resp *http.Response) error {
	io.Copy(ioutil.Discard, resp.Body)
	return resp.Body.Close()
}
<commit_msg>use the right key<commit_after>package k8s

import (
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"encoding/json"

	"github.com/pkg/errors"
	"github.com/rancher/go-rancher/v3"
)

type Lookup struct {
	httpClient           http.Client
	accessKey, secretKey string
	clusterURL           string
}

func NewLookup(clusterURL, accessKey, secretKey string) *Lookup {
	return &Lookup{
		httpClient: http.Client{
			Timeout: 5 * time.Second,
		},
		accessKey:  accessKey,
		secretKey:  secretKey,
		clusterURL: clusterURL,
	}
}

func (c *Lookup) Lookup(input *http.Request) (*client.Cluster, bool, error) {
	clusterID := getClusterID(input)
	if clusterID == "" {
		return nil, false, nil
	}

	req, err := http.NewRequest("GET", c.clusterURL+"/"+clusterID, nil)
	if err != nil {
		return nil, false, err
	}

	req.SetBasicAuth(c.accessKey, c.secretKey)

	cookie := getTokenCookie(input)
	if cookie != nil {
		req.AddCookie(cookie)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, false, err
	}
	defer close(resp)

	if resp.StatusCode >= 200 && resp.StatusCode < 300 {
		c, err := parseCluster(resp)
		return c, true, err
	}

	auth := input.Header.Get("Authorization")
	if !strings.HasPrefix(auth, "Bearer ") {
		return nil, false, nil
	}

	auth = strings.TrimPrefix(auth, "Bearer ")

	req.Header.Del("Authorization")
	req.SetBasicAuth(c.accessKey, c.secretKey)
	resp2, err := c.httpClient.Do(req)
	if err != nil {
		return nil, false, err
	}
	defer close(resp2)

	if resp.StatusCode < 200 || resp.StatusCode > 299 {
		return nil, false, nil
	}

	cluster, err := parseCluster(resp2)
	if err != nil || cluster.K8sClientConfig == nil || cluster.K8sClientConfig.BearerToken != auth {
		return nil, false, err
	}

	return cluster, false, nil
}

func parseCluster(resp *http.Response) (*client.Cluster, error) {
	cluster := &client.Cluster{}
	if err := json.NewDecoder(resp.Body).Decode(cluster); err != nil {
		return nil, errors.Wrap(err, "Parsing clusters response")
	}
	return cluster, nil
}

func getClusterID(req *http.Request) string {
	parts := strings.Split(req.URL.Path, "/")
	if len(parts) > 3 && strings.HasPrefix(parts[2], "cluster") {
		return parts[3]
	}

	return ""
}

func getAuthorizationHeader(req *http.Request) string {
	return req.Header.Get("Authorization")
}

func getTokenCookie(req *http.Request) *http.Cookie {
	for _, cookie := range req.Cookies() {
		if cookie.Name == "token" {
			return cookie
		}
	}

	return nil
}

func close(resp *http.Response) error {
	io.Copy(ioutil.Discard, resp.Body)
	return resp.Body.Close()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"sort"
	"strings"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/lxc/lxd/shared/termios"
)

// Lists
const (
	listFormatCSV   = "csv"
	listFormatJSON  = "json"
	listFormatTable = "table"
	listFormatYAML  = "yaml"
)

type stringList [][]string

func (a stringList) Len() int {
	return len(a)
}

func (a stringList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a stringList) Less(i, j int) bool {
	x := 0
	for x = range a[i] {
		if a[i][x] != a[j][x] {
			break
		}
	}

	if a[i][x] == "" {
		return false
	}

	if a[j][x] == "" {
		return true
	}

	return a[i][x] < a[j][x]
}

// Container name sorting
type byName [][]string

func (a byName) Len() int {
	return len(a)
}

func (a byName) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byName) Less(i, j int) bool {
	if a[i][0] == "" {
		return false
	}

	if a[j][0] == "" {
		return true
	}

	return a[i][0] < a[j][0]
}

// Storage volume sorting
type byNameAndType [][]string

func (a byNameAndType) Len() int {
	return len(a)
}

func (a byNameAndType) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byNameAndType) Less(i, j int) bool {
	if a[i][0] != a[j][0] {
		return a[i][0] < a[j][0]
	}

	if a[i][1] == "" {
		return false
	}

	if a[j][1] == "" {
		return true
	}

	return a[i][1] < a[j][1]
}

// Batch operations
type batchResult struct {
	err  error
	name string
}

func runBatch(names []string, action func(name string) error) []batchResult {
	chResult := make(chan batchResult, len(names))

	for _, name := range names {
		go func(name string) {
			chResult <- batchResult{action(name), name}
		}(name)
	}

	results := []batchResult{}
	for range names {
		results = append(results, <-chResult)
	}

	return results
}

// Add a device to a container
func containerDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the container entry
	container, etag, err := client.GetContainer(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := container.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	container.Devices[devName] = dev

	op, err := client.UpdateContainer(name, container.Writable(), etag)
	if err != nil {
		return err
	}

	return op.Wait()
}

// Add a device to a profile
func profileDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the profile entry
	profile, profileEtag, err := client.GetProfile(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := profile.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	// Add the device to the container
	profile.Devices[devName] = dev

	err = client.UpdateProfile(name, profile.Writable(), profileEtag)
	if err != nil {
		return err
	}

	return nil
}

// Create the specified image alises, updating those that already exist
func ensureImageAliases(client lxd.ContainerServer, aliases []api.ImageAlias, fingerprint string) error {
	if len(aliases) == 0 {
		return nil
	}

	names := make([]string, len(aliases))
	for i, alias := range aliases {
		names[i] = alias.Name
	}
	sort.Strings(names)

	resp, err := client.GetImageAliases()
	if err != nil {
		return err
	}

	// Delete existing aliases that match provided ones
	for _, alias := range GetExistingAliases(names, resp) {
		err := client.DeleteImageAlias(alias.Name)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to remove alias %s"), alias.Name))
		}
	}
	// Create new aliases
	for _, alias := range aliases {
		aliasPost := api.ImageAliasesPost{}
		aliasPost.Name = alias.Name
		aliasPost.Target = fingerprint
		err := client.CreateImageAlias(aliasPost)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to create alias %s"), alias.Name))
		}
	}
	return nil
}

// GetExistingAliases returns the intersection between a list of aliases and all the existing ones.
func GetExistingAliases(aliases []string, allAliases []api.ImageAliasesEntry) []api.ImageAliasesEntry {
	existing := []api.ImageAliasesEntry{}
	for _, alias := range allAliases {
		name := alias.Name
		pos := sort.SearchStrings(aliases, name)
		if pos < len(aliases) && aliases[pos] == name {
			existing = append(existing, alias)
		}
	}
	return existing
}

func getConfig(args ...string) (map[string]string, error) {
	if len(args) == 2 && !strings.Contains(args[0], "=") {
		if args[1] == "-" && !termios.IsTerminal(getStdinFd()) {
			buf, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				return nil, errors.Wrap(err, i18n.G("Can't read from stdin: %s"))
			}

			args[1] = string(buf[:])
		}

		return map[string]string{args[0]: args[1]}, nil
	}

	values := map[string]string{}

	for _, arg := range args {
		fields := strings.SplitN(arg, "=", 2)
		if len(fields) != 2 {
			return nil, fmt.Errorf("Invalid key=value configuration: %s", arg)
		}

		if fields[1] == "-" && !termios.IsTerminal(getStdinFd()) {
			buf, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				return nil, fmt.Errorf(i18n.G("Can't read from stdin: %s"), err)
			}

			fields[1] = string(buf[:])
		}

		values[fields[0]] = fields[1]
	}

	return values, nil
}
<commit_msg>lxc: Implement generic renderTable function<commit_after>package main

import (
	"encoding/csv"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"sort"
	"strings"

	"github.com/olekukonko/tablewriter"
	"github.com/pkg/errors"
	"gopkg.in/yaml.v2"

	"github.com/lxc/lxd/client"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/i18n"
	"github.com/lxc/lxd/shared/termios"
)

// Lists
const (
	listFormatCSV   = "csv"
	listFormatJSON  = "json"
	listFormatTable = "table"
	listFormatYAML  = "yaml"
)

type stringList [][]string

func (a stringList) Len() int {
	return len(a)
}

func (a stringList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a stringList) Less(i, j int) bool {
	x := 0
	for x = range a[i] {
		if a[i][x] != a[j][x] {
			break
		}
	}

	if a[i][x] == "" {
		return false
	}

	if a[j][x] == "" {
		return true
	}

	return a[i][x] < a[j][x]
}

// Container name sorting
type byName [][]string

func (a byName) Len() int {
	return len(a)
}

func (a byName) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byName) Less(i, j int) bool {
	if a[i][0] == "" {
		return false
	}

	if a[j][0] == "" {
		return true
	}

	return a[i][0] < a[j][0]
}

// Storage volume sorting
type byNameAndType [][]string

func (a byNameAndType) Len() int {
	return len(a)
}

func (a byNameAndType) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a byNameAndType) Less(i, j int) bool {
	if a[i][0] != a[j][0] {
		return a[i][0] < a[j][0]
	}

	if a[i][1] == "" {
		return false
	}

	if a[j][1] == "" {
		return true
	}

	return a[i][1] < a[j][1]
}

// Batch operations
type batchResult struct {
	err  error
	name string
}

func runBatch(names []string, action func(name string) error) []batchResult {
	chResult := make(chan batchResult, len(names))

	for _, name := range names {
		go func(name string) {
			chResult <- batchResult{action(name), name}
		}(name)
	}

	results := []batchResult{}
	for range names {
		results = append(results, <-chResult)
	}

	return results
}

// Add a device to a container
func containerDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the container entry
	container, etag, err := client.GetContainer(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := container.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	container.Devices[devName] = dev

	op, err := client.UpdateContainer(name, container.Writable(), etag)
	if err != nil {
		return err
	}

	return op.Wait()
}

// Add a device to a profile
func profileDeviceAdd(client lxd.ContainerServer, name string, devName string, dev map[string]string) error {
	// Get the profile entry
	profile, profileEtag, err := client.GetProfile(name)
	if err != nil {
		return err
	}

	// Check if the device already exists
	_, ok := profile.Devices[devName]
	if ok {
		return fmt.Errorf(i18n.G("Device already exists: %s"), devName)
	}

	// Add the device to the container
	profile.Devices[devName] = dev

	err = client.UpdateProfile(name, profile.Writable(), profileEtag)
	if err != nil {
		return err
	}

	return nil
}

// Create the specified image alises, updating those that already exist
func ensureImageAliases(client lxd.ContainerServer, aliases []api.ImageAlias, fingerprint string) error {
	if len(aliases) == 0 {
		return nil
	}

	names := make([]string, len(aliases))
	for i, alias := range aliases {
		names[i] = alias.Name
	}
	sort.Strings(names)

	resp, err := client.GetImageAliases()
	if err != nil {
		return err
	}

	// Delete existing aliases that match provided ones
	for _, alias := range GetExistingAliases(names, resp) {
		err := client.DeleteImageAlias(alias.Name)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to remove alias %s"), alias.Name))
		}
	}
	// Create new aliases
	for _, alias := range aliases {
		aliasPost := api.ImageAliasesPost{}
		aliasPost.Name = alias.Name
		aliasPost.Target = fingerprint
		err := client.CreateImageAlias(aliasPost)
		if err != nil {
			fmt.Println(fmt.Sprintf(i18n.G("Failed to create alias %s"), alias.Name))
		}
	}
	return nil
}

// GetExistingAliases returns the intersection between a list of aliases and all the existing ones.
func GetExistingAliases(aliases []string, allAliases []api.ImageAliasesEntry) []api.ImageAliasesEntry {
	existing := []api.ImageAliasesEntry{}
	for _, alias := range allAliases {
		name := alias.Name
		pos := sort.SearchStrings(aliases, name)
		if pos < len(aliases) && aliases[pos] == name {
			existing = append(existing, alias)
		}
	}
	return existing
}

func getConfig(args ...string) (map[string]string, error) {
	if len(args) == 2 && !strings.Contains(args[0], "=") {
		if args[1] == "-" && !termios.IsTerminal(getStdinFd()) {
			buf, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				return nil, errors.Wrap(err, i18n.G("Can't read from stdin: %s"))
			}

			args[1] = string(buf[:])
		}

		return map[string]string{args[0]: args[1]}, nil
	}

	values := map[string]string{}

	for _, arg := range args {
		fields := strings.SplitN(arg, "=", 2)
		if len(fields) != 2 {
			return nil, fmt.Errorf("Invalid key=value configuration: %s", arg)
		}

		if fields[1] == "-" && !termios.IsTerminal(getStdinFd()) {
			buf, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				return nil, fmt.Errorf(i18n.G("Can't read from stdin: %s"), err)
			}

			fields[1] = string(buf[:])
		}

		values[fields[0]] = fields[1]
	}

	return values, nil
}

func renderTable(format string, header []string, data [][]string, raw interface{}) error {
	switch format {
	case listFormatTable:
		table := tablewriter.NewWriter(os.Stdout)
		table.SetAutoWrapText(false)
		table.SetAlignment(tablewriter.ALIGN_LEFT)
		table.SetRowLine(true)
		table.SetHeader(header)
		sort.Sort(byName(data))
		table.AppendBulk(data)
		table.Render()
	case listFormatCSV:
		sort.Sort(byName(data))
		data = append(data, []string{})
		copy(data[1:], data[0:])
		data[0] = header
		w := csv.NewWriter(os.Stdout)
		w.WriteAll(data)

		err := w.Error()
		if err != nil {
			return err
		}
	case listFormatJSON:
		enc := json.NewEncoder(os.Stdout)

		err := enc.Encode(raw)
		if err != nil {
			return err
		}
	case listFormatYAML:
		out, err := yaml.Marshal(raw)
		if err != nil {
			return err
		}

		fmt.Printf("%s", out)
	default:
		return fmt.Errorf(i18n.G("Invalid format %q"), format)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
//
// 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 table

import (
	"github.com/osrg/gobgp/packet/bgp"
	"reflect"
)

type AdjRib struct {
	id       string
	accepted map[bgp.RouteFamily]int
	table    map[bgp.RouteFamily]map[string]*Path
}

func NewAdjRib(id string, rfList []bgp.RouteFamily) *AdjRib {
	table := make(map[bgp.RouteFamily]map[string]*Path)
	for _, rf := range rfList {
		table[rf] = make(map[string]*Path)
	}
	return &AdjRib{
		id:       id,
		table:    table,
		accepted: make(map[bgp.RouteFamily]int),
	}
}

func (adj *AdjRib) Update(pathList []*Path) {
	for _, path := range pathList {
		if path == nil || path.IsEOR() {
			continue
		}
		rf := path.GetRouteFamily()
		key := path.getPrefix()

		old, found := adj.table[rf][key]
		if path.IsWithdraw {
			if found {
				delete(adj.table[rf], key)
				if old.Filtered(adj.id) != POLICY_DIRECTION_IN {
					adj.accepted[rf]--
				}
			}
		} else {
			n := path.Filtered(adj.id)
			if found {
				o := old.Filtered(adj.id)
				if o == POLICY_DIRECTION_IN && n == POLICY_DIRECTION_NONE {
					adj.accepted[rf]++
				} else if o == POLICY_DIRECTION_NONE && n == POLICY_DIRECTION_IN {
					adj.accepted[rf]--
				}
			} else {
				if n == POLICY_DIRECTION_NONE {
					adj.accepted[rf]++
				}
			}
			if found && reflect.DeepEqual(old.GetPathAttrs(), path.GetPathAttrs()) {
				path.setTimestamp(old.GetTimestamp())
			}
			adj.table[rf][key] = path
		}
	}
}

func (adj *AdjRib) RefreshAcceptedNumber(rfList []bgp.RouteFamily) {
	for _, rf := range rfList {
		adj.accepted[rf] = 0
		for _, p := range adj.table[rf] {
			if p.Filtered(adj.id) != POLICY_DIRECTION_IN {
				adj.accepted[rf]++
			}
		}
	}
}

func (adj *AdjRib) PathList(rfList []bgp.RouteFamily, accepted bool) []*Path {
	pathList := make([]*Path, 0, adj.Count(rfList))
	for _, rf := range rfList {
		for _, rr := range adj.table[rf] {
			if accepted && rr.Filtered(adj.id) > POLICY_DIRECTION_NONE {
				continue
			}
			pathList = append(pathList, rr)
		}
	}
	return pathList
}

func (adj *AdjRib) Count(rfList []bgp.RouteFamily) int {
	count := 0
	for _, rf := range rfList {
		if table, ok := adj.table[rf]; ok {
			count += len(table)
		}
	}
	return count
}

func (adj *AdjRib) Accepted(rfList []bgp.RouteFamily) int {
	count := 0
	for _, rf := range rfList {
		if n, ok := adj.accepted[rf]; ok {
			count += n
		}
	}
	return count
}

func (adj *AdjRib) Drop(rfList []bgp.RouteFamily) {
	for _, rf := range rfList {
		if _, ok := adj.table[rf]; ok {
			adj.table[rf] = make(map[string]*Path)
			adj.accepted[rf] = 0
		}
	}
}

func (adj *AdjRib) DropStale(rfList []bgp.RouteFamily) []*Path {
	pathList := make([]*Path, 0, adj.Count(rfList))
	for _, rf := range rfList {
		if table, ok := adj.table[rf]; ok {
			for _, p := range table {
				if p.IsStale() {
					delete(table, p.getPrefix())
					if p.Filtered(adj.id) == POLICY_DIRECTION_NONE {
						adj.accepted[rf]--
					}
					pathList = append(pathList, p.Clone(true))
				}
			}
		}
	}
	return pathList
}

func (adj *AdjRib) StaleAll(rfList []bgp.RouteFamily) {
	for _, rf := range rfList {
		if table, ok := adj.table[rf]; ok {
			for _, p := range table {
				p.MarkStale(true)
			}
		}
	}
}
<commit_msg>table: fix bug in AdjRIb.PathList()<commit_after>// Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
//
// 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 table

import (
	"github.com/osrg/gobgp/packet/bgp"
	"reflect"
)

type AdjRib struct {
	id       string
	accepted map[bgp.RouteFamily]int
	table    map[bgp.RouteFamily]map[string]*Path
}

func NewAdjRib(id string, rfList []bgp.RouteFamily) *AdjRib {
	table := make(map[bgp.RouteFamily]map[string]*Path)
	for _, rf := range rfList {
		table[rf] = make(map[string]*Path)
	}
	return &AdjRib{
		id:       id,
		table:    table,
		accepted: make(map[bgp.RouteFamily]int),
	}
}

func (adj *AdjRib) Update(pathList []*Path) {
	for _, path := range pathList {
		if path == nil || path.IsEOR() {
			continue
		}
		rf := path.GetRouteFamily()
		key := path.getPrefix()

		old, found := adj.table[rf][key]
		if path.IsWithdraw {
			if found {
				delete(adj.table[rf], key)
				if old.Filtered(adj.id) != POLICY_DIRECTION_IN {
					adj.accepted[rf]--
				}
			}
		} else {
			n := path.Filtered(adj.id)
			if found {
				o := old.Filtered(adj.id)
				if o == POLICY_DIRECTION_IN && n == POLICY_DIRECTION_NONE {
					adj.accepted[rf]++
				} else if o == POLICY_DIRECTION_NONE && n == POLICY_DIRECTION_IN {
					adj.accepted[rf]--
				}
			} else {
				if n == POLICY_DIRECTION_NONE {
					adj.accepted[rf]++
				}
			}
			if found && reflect.DeepEqual(old.GetPathAttrs(), path.GetPathAttrs()) {
				path.setTimestamp(old.GetTimestamp())
			}
			adj.table[rf][key] = path
		}
	}
}

func (adj *AdjRib) RefreshAcceptedNumber(rfList []bgp.RouteFamily) {
	for _, rf := range rfList {
		adj.accepted[rf] = 0
		for _, p := range adj.table[rf] {
			if p.Filtered(adj.id) != POLICY_DIRECTION_IN {
				adj.accepted[rf]++
			}
		}
	}
}

func (adj *AdjRib) PathList(rfList []bgp.RouteFamily, accepted bool) []*Path {
	pathList := make([]*Path, 0, adj.Count(rfList))
	for _, rf := range rfList {
		for _, rr := range adj.table[rf] {
			if accepted && rr.Filtered(adj.id) == POLICY_DIRECTION_IN {
				continue
			}
			pathList = append(pathList, rr)
		}
	}
	return pathList
}

func (adj *AdjRib) Count(rfList []bgp.RouteFamily) int {
	count := 0
	for _, rf := range rfList {
		if table, ok := adj.table[rf]; ok {
			count += len(table)
		}
	}
	return count
}

func (adj *AdjRib) Accepted(rfList []bgp.RouteFamily) int {
	count := 0
	for _, rf := range rfList {
		if n, ok := adj.accepted[rf]; ok {
			count += n
		}
	}
	return count
}

func (adj *AdjRib) Drop(rfList []bgp.RouteFamily) {
	for _, rf := range rfList {
		if _, ok := adj.table[rf]; ok {
			adj.table[rf] = make(map[string]*Path)
			adj.accepted[rf] = 0
		}
	}
}

func (adj *AdjRib) DropStale(rfList []bgp.RouteFamily) []*Path {
	pathList := make([]*Path, 0, adj.Count(rfList))
	for _, rf := range rfList {
		if table, ok := adj.table[rf]; ok {
			for _, p := range table {
				if p.IsStale() {
					delete(table, p.getPrefix())
					if p.Filtered(adj.id) == POLICY_DIRECTION_NONE {
						adj.accepted[rf]--
					}
					pathList = append(pathList, p.Clone(true))
				}
			}
		}
	}
	return pathList
}

func (adj *AdjRib) StaleAll(rfList []bgp.RouteFamily) {
	for _, rf := range rfList {
		if table, ok := adj.table[rf]; ok {
			for _, p := range table {
				p.MarkStale(true)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/thermo-smart/TemperatureSensorController"
)

func main() {
	var temp, error = TemperatureSensorController.ReadTemperature()
	if error != nil {
		fmt.Println(error)
		return
	}

	fmt.Println(temp)
}
<commit_msg>Fixed import path to TemperatureController to comply with the Go conventions.<commit_after>package main

import (
	"fmt"

	"github.com/gaeshi-gaeshi/thermo-smart/TemperatureSensorController"
)

func main() {
	var temp, error = TemperatureSensorController.ReadTemperature()
	if error != nil {
		fmt.Println(error)
		return
	}

	fmt.Println(temp)
}
<|endoftext|>


<commit_before>package raymondhelpers

import (
	"regexp"
	"strings"
	"time"

	"github.com/aymerick/raymond"
	"github.com/grokify/gotilla/time/timeutil"
)

// RegisterAll registers helpers for the Raymond Handlebars template
// engine.
func RegisterAll() {
	raymond.RegisterHelper("timeRfc3339", func(t time.Time) string {
		return t.Format(time.RFC3339)
	})
	raymond.RegisterHelper("timeRfc3339ymd", func(t time.Time) string {
		return t.Format(timeutil.RFC3339YMD)
	})
	raymond.RegisterHelper("spaceToHyphen", func(s string) string {
		return regexp.MustCompile(`[\s-]+`).ReplaceAllString(s, "-")
	})
	raymond.RegisterHelper("spaceToUnderscore", func(s string) string {
		return regexp.MustCompile(`[\s_]+`).ReplaceAllString(s, "_")
	})
	raymond.RegisterHelper("toLower", func(s string) string {
		return strings.ToLower(s)
	})
}
<commit_msg>update raymondhelpers<commit_after>package raymondhelpers

import (
	"regexp"
	"strings"
	"time"

	"github.com/aymerick/raymond"
	"github.com/grokify/gotilla/time/timeutil"
)

// RegisterAll registers helpers for the Raymond Handlebars template
// engine.
func RegisterAll() {
	RegisterString()
	RegisterTime()
}

func RegisterTime() {
	raymond.RegisterHelper("timeRfc3339", func(t time.Time) string {
		return t.Format(time.RFC3339)
	})
	raymond.RegisterHelper("timeRfc3339ymd", func(t time.Time) string {
		return t.Format(timeutil.RFC3339YMD)
	})
}

func RegisterString() {
	raymond.RegisterHelper("spaceToHyphen", func(s string) string {
		return regexp.MustCompile(`[\s-]+`).ReplaceAllString(s, "-")
	})
	raymond.RegisterHelper("spaceToUnderscore", func(s string) string {
		return regexp.MustCompile(`[\s_]+`).ReplaceAllString(s, "_")
	})
	raymond.RegisterHelper("toLower", func(s string) string {
		return strings.ToLower(s)
	})
}
<|endoftext|>


<commit_before>// Package icmp provides icmp enhanced methods based on the golang icmp package
package icmp

import (
	"errors"
	"fmt"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
	"log"
	"math/rand"
	"net"
	"os"
	"os/signal"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/mehrdadrad/mylg/cli"
)

// IANA ICMP
const (
	ProtocolIPv4ICMP = 1  // IANA ICMP for IPv4
	ProtocolIPv6ICMP = 58 // IANA ICMP for IPv6
)

// packet represents ping packet
type packet struct {
	bytes []byte
	addr  net.Addr
	err   error
}

// Ping represents ping request
type Ping struct {
	m         icmp.Message
	id        int
	seq       int
	pSize     int
	count     int
	addr      *net.IPAddr
	addrs     []net.IP
	target    string
	isV4Avail bool
	isV6Avail bool
	forceV4   bool
	forceV6   bool
	network   string
	source    string
	timeout   time.Duration
	interval  time.Duration
	MaxRTT    time.Duration
}

// Response represent ping response
type Response struct {
	RTT      float64
	Size     int
	Sequence int
	Addr     string
	Timeout  bool
	Error    error
}

// NewPing creates a new ping object
func NewPing(args string) (*Ping, error) {
	var (
		timeout  time.Duration
		interval time.Duration
		err      error
	)
	target, flag := cli.Flag(args)

	// show help
	if _, ok := flag["help"]; ok || len(target) < 3 {
		help()
		return nil, nil
	}
	// resolve host
	ips, err := net.LookupIP(target)
	if err != nil {
		return nil, err
	}
	// timeout
	timeoutStr := cli.SetFlag(flag, "t", "2s").(string)
	timeoutStr = NormalizeDuration(timeoutStr)
	if timeout, err = time.ParseDuration(timeoutStr); err != nil {
		return nil, fmt.Errorf("timeout options is not valid")
	}
	// interval
	intervalStr := cli.SetFlag(flag, "i", "1s").(string)
	intervalStr = NormalizeDuration(intervalStr)
	if interval, err = time.ParseDuration(intervalStr); err != nil {
		return nil, fmt.Errorf("interval options is not valid")
	}

	p := Ping{
		id:        rand.Intn(0xffff),
		seq:       -1,
		pSize:     64,
		timeout:   timeout,
		interval:  interval,
		isV4Avail: false,
		isV6Avail: false,
		count:     cli.SetFlag(flag, "c", 4).(int),
		forceV4:   cli.SetFlag(flag, "4", false).(bool),
		forceV6:   cli.SetFlag(flag, "6", false).(bool),
		network:   "ip",
		source:    "",
		MaxRTT:    time.Second,
	}

	p.addrs = ips
	p.target = target
	if err := p.SetIP(ips); err != nil {
		return nil, err
	}

	return &p, nil
}

// Run loops the ping and print it out
func (p *Ping) Run() chan Response {
	var r = make(chan Response, 1)
	go func() {
		for n := 0; n < p.count; n++ {
			p.Ping(r)
			if n != p.count-1 {
				time.Sleep(p.interval)
			}
		}
		close(r)
	}()
	return r
}

// IsIPv4 returns true if ip version is v4
func IsIPv4(ip net.IP) bool {
	return len(ip.To4()) == net.IPv4len
}

// IsIPv6 returns true if ip version is v6
func IsIPv6(ip net.IP) bool {
	if r := strings.Index(ip.String(), ":"); r != -1 {
		return true
	}
	return false
}

func (p *Ping) parseMessage(m *packet) (*ipv4.Header, *icmp.Message, error) {
	var proto = ProtocolIPv4ICMP
	if p.isV6Avail {
		proto = ProtocolIPv6ICMP
	}
	msg, err := icmp.ParseMessage(proto, m.bytes)
	if err != nil {
		return nil, nil, err
	}
	bytes, _ := msg.Body.Marshal(msg.Type.Protocol())
	h, err := icmp.ParseIPv4Header(bytes)
	return h, msg, err
}

// SetIP set ip address
func (p *Ping) SetIP(ips []net.IP) error {
	for _, ip := range ips {
		if IsIPv4(ip) && !p.forceV6 {
			p.addr = &net.IPAddr{IP: ip}
			p.isV4Avail = true
			return nil
		} else if IsIPv6(ip) && !p.forceV4 {
			p.addr = &net.IPAddr{IP: ip}
			p.isV6Avail = true
			return nil
		}
	}
	return fmt.Errorf("there is not  A or AAAA record")
}

// DelIP removes ip adrress
func (p *Ping) DelIP(ipAddr string) {
	//todo
}

// PacketSize set packet size
func (p *Ping) PacketSize(s int) {
	p.pSize = s
}

// listen starts to listen incoming icmp
func (p *Ping) listen(network string) *icmp.PacketConn {
	c, err := icmp.ListenPacket(network, p.source)
	if err != nil {
		log.Fatalf("listen err, %s", err)
	}
	return c
}

// recv reads icmp message
func (p *Ping) recv(conn *icmp.PacketConn, rcvdChan chan<- *packet) {
	var err error
	bytes := make([]byte, 1500)
	conn.SetReadDeadline(time.Now().Add(p.timeout))
	n, dest, err := conn.ReadFrom(bytes)
	if err != nil {
		if neterr, ok := err.(*net.OpError); ok {
			if neterr.Timeout() {
				err = errors.New("Request timeout")
			}
		}
	}

	bytes = bytes[:n]
	select {
	case rcvdChan <- &packet{bytes: bytes, addr: dest, err: err}:
	}
}

func (p *Ping) payload() []byte {
	timeBytes := make([]byte, 8)
	ts := time.Now().UnixNano()
	for i := uint8(0); i < 8; i++ {
		timeBytes[i] = byte((ts >> (i * 8)) & 0xff)
	}
	payload := make([]byte, p.pSize-16)
	payload = append(payload, timeBytes...)
	return payload
}
func (p *Ping) send(conn *icmp.PacketConn) {
	var (
		wg sync.WaitGroup
	)
	var icmpType icmp.Type
	if IsIPv4(p.addr.IP) {
		icmpType = ipv4.ICMPTypeEcho
	} else if IsIPv6(p.addr.IP) {
		icmpType = ipv6.ICMPTypeEchoRequest
	}

	p.seq++
	bytes, err := (&icmp.Message{
		Type: icmpType, Code: 0,
		Body: &icmp.Echo{
			ID:   p.id,
			Seq:  p.seq,
			Data: p.payload(),
		},
	}).Marshal(nil)
	if err != nil {
		println(err.Error())
	}

	wg.Add(1)
	go func(conn *icmp.PacketConn, dest net.Addr, b []byte) {
		defer wg.Done()
		for {
			if _, err := conn.WriteTo(bytes, dest); err != nil {
				println(err.Error())
				if neterr, ok := err.(*net.OpError); ok {
					if neterr.Err == syscall.ENOBUFS {
						continue
					}
				}
			}
			break
		}
	}(conn, p.addr, bytes)

	wg.Wait()
}

// Ping tries to send and receive a packet
func (p *Ping) Ping(out chan Response) {
	var (
		conn     *icmp.PacketConn
		rcvdChan chan *packet = make(chan *packet, 1)
	)

	if p.isV4Avail {
		if conn = p.listen("ip4:icmp"); conn == nil {
			return
		}
		defer conn.Close()
	}

	if p.isV6Avail {
		if conn = p.listen("ip6:ipv6-icmp"); conn == nil {
			return
		}
		defer conn.Close()
	}

	p.send(conn)
	p.recv(conn, rcvdChan)
	rm := <-rcvdChan

	if rm.err != nil {
		out <- Response{Error: rm.err, Sequence: p.seq}
		return
	}
	_, m, err := p.parseMessage(rm)
	if err != nil {
		out <- Response{Error: err, Sequence: p.seq}
		return
	}

	switch m.Body.(type) {
	case *icmp.TimeExceeded:
		out <- Response{Error: fmt.Errorf("time exceeded"), Sequence: p.seq}
	case *icmp.PacketTooBig:
		out <- Response{Error: fmt.Errorf("packet too big"), Sequence: p.seq}
	case *icmp.DstUnreach:
		out <- Response{Error: fmt.Errorf("destination unreachable"), Sequence: p.seq}
	case *icmp.Echo:
		rtt := float64(time.Now().UnixNano()-getTimeStamp(rm.bytes)) / 1000000
		out <- Response{
			Size:     len(rm.bytes),
			Addr:     rm.addr.String(),
			RTT:      rtt,
			Sequence: p.seq,
			Error:    nil,
		}
	default:
		out <- Response{Error: fmt.Errorf("ICMP error"), Sequence: p.seq}
	}
}

// PrintPretty prints out the result pretty format
func (p *Ping) PrintPretty() {
	var (
		loop          = true
		sigCh         = make(chan os.Signal, 1)
		pFmt          = "%d bytes from %s icmp_seq=%d time=%f ms"
		eFmt          = "%s icmp_seq=%d"
		sFmt          = "%d packets transmitted,  %d packets received, %d%% packet loss\n"
		resp          = p.Run()
		msg           string
		min, max, avg float64
		c             = map[string]int{"tx": 0, "err": 0, "pl": 0}
	)

	// capture interrupt w/ s channel
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	fmt.Printf("PING %s (%s): %d data bytes\n", p.target, p.addr, p.pSize-8)
	for loop {
		select {
		case r, ok := <-resp:
			if !ok {
				loop = false
				break
			}
			c["tx"]++
			if r.Error != nil {
				c["err"]++
				msg = fmt.Sprintf(eFmt, r.Error.Error(), r.Sequence)
				println(msg)
				continue
			}

			min = Min(r.RTT, min)
			max = Max(r.RTT, max)
			avg = Avg(r.RTT, avg)

			msg = fmt.Sprintf(pFmt, r.Size, r.Addr, r.Sequence, r.RTT)
			println(msg)
		case <-sigCh:
			loop = false
		}
	}

	if c["tx"] == 0 {
		return
	}
	// packet loss
	c["pl"] = c["err"] * 100 / c["tx"]

	fmt.Printf("\n\n--- %s ping statistics ---\n", p.target)
	fmt.Printf(sFmt, c["tx"], c["tx"]-c["err"], c["pl"])

	if c["pl"] == 100 {
		return
	}

	fmt.Printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n", min, avg, max)
}

// Max handles maximum delay
func Max(x, y float64) float64 {
	if x > y {
		return x
	}
	return y
}

// Min handles minimum delay
func Min(x, y float64) float64 {
	if x < y || y == 0 {
		return x
	}
	return y
}

// Avg handles average delay
func Avg(x, y float64) float64 {
	if y == 0 {
		return x
	}
	return (x + y) / 2
}

// getTimeStamp
func getTimeStamp(m []byte) int64 {
	var ts int64
	for i := uint(0); i < 8; i++ {
		ts += int64(m[uint(len(m))-8+i]) << (i * 8)
	}
	return ts
}

// NormalizeDuration adds default unit (seconds) as needed
func NormalizeDuration(d string) string {
	if match, _ := regexp.MatchString(`^\d+\.{0,1}\d*$`, d); match {
		return d + "s"
	}
	return d
}

// help represents ping help
func help() {
	fmt.Println(`
    usage:
          ping IP address / domain name [options]
    options:
          -c count       Send 'count' requests (default: 4)
          -t timeout     Specify a timeout in format "ms", "s", "m" (default: 2s)
          -i interval    Wait interval between sending each packet (default: 1s)
          -4             Forces the ping command to use IPv4 (target should be hostname)
          -6             Forces the ping command to use IPv6 (target should be hostname)
    Example:
          ping 8.8.8.8
          ping 8.8.8.8 -c 10
          ping google.com -6
          ping mylg.io -i 5s
	`)
}
<commit_msg>optimized code to detect CIDR and seperated run method from print<commit_after>// Package icmp provides icmp enhanced methods based on the golang icmp package
package icmp

import (
	"errors"
	"fmt"
	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
	"log"
	"math/rand"
	"net"
	"os"
	"os/signal"
	"regexp"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/mehrdadrad/mylg/cli"
)

// IANA ICMP
const (
	ProtocolIPv4ICMP = 1  // IANA ICMP for IPv4
	ProtocolIPv6ICMP = 58 // IANA ICMP for IPv6
)

// packet represents ping packet
type packet struct {
	bytes []byte
	addr  net.Addr
	err   error
}

// Ping represents ping request
type Ping struct {
	m         icmp.Message
	id        int
	seq       int
	pSize     int
	count     int
	addr      *net.IPAddr
	addrs     []net.IP
	target    string
	isV4Avail bool
	isV6Avail bool
	isCIDR    bool
	forceV4   bool
	forceV6   bool
	network   string
	source    string
	timeout   time.Duration
	interval  time.Duration
	MaxRTT    time.Duration
}

// Response represent ping response
type Response struct {
	RTT      float64
	Size     int
	Sequence int
	Addr     string
	Timeout  bool
	Error    error
}

// NewPing creates a new ping object
func NewPing(args string) (*Ping, error) {
	var (
		err error
	)
	target, flag := cli.Flag(args)

	// show help
	if _, ok := flag["help"]; ok || len(target) < 3 {
		help()
		return nil, nil
	}

	p := Ping{
		id:        rand.Intn(0xffff),
		seq:       -1,
		pSize:     64,
		target:    target,
		isV4Avail: false,
		isV6Avail: false,
		isCIDR:    isCIDR(target),
		count:     cli.SetFlag(flag, "c", 4).(int),
		forceV4:   cli.SetFlag(flag, "4", false).(bool),
		forceV6:   cli.SetFlag(flag, "6", false).(bool),
		network:   "ip",
		source:    "",
		MaxRTT:    time.Second,
	}

	if !p.isCIDR {
		// resolve host
		ips, err := net.LookupIP(target)
		if err != nil {
			return nil, err
		}
		p.addrs = ips
		if err := p.SetIP(ips); err != nil {
			return nil, err
		}

	}
	// set timeout
	timeoutStr := cli.SetFlag(flag, "t", "2s").(string)
	timeoutStr = NormalizeDuration(timeoutStr)
	if p.timeout, err = time.ParseDuration(timeoutStr); err != nil {
		return nil, fmt.Errorf("timeout options is not valid")
	}
	// set interval
	intervalStr := cli.SetFlag(flag, "i", "1s").(string)
	intervalStr = NormalizeDuration(intervalStr)
	if p.interval, err = time.ParseDuration(intervalStr); err != nil {
		return nil, fmt.Errorf("interval options is not valid")
	}

	return &p, nil
}

// Run loops the ping and print it out
func (p *Ping) Run() chan Response {
	var r = make(chan Response, 1)
	go func() {
		for n := 0; n < p.count; n++ {
			p.Ping(r)
			if n != p.count-1 {
				time.Sleep(p.interval)
			}
		}
		close(r)
	}()
	return r
}

// IsIPv4 returns true if ip version is v4
func IsIPv4(ip net.IP) bool {
	return len(ip.To4()) == net.IPv4len
}

// IsIPv6 returns true if ip version is v6
func IsIPv6(ip net.IP) bool {
	if r := strings.Index(ip.String(), ":"); r != -1 {
		return true
	}
	return false
}

func (p *Ping) parseMessage(m *packet) (*ipv4.Header, *icmp.Message, error) {
	var proto = ProtocolIPv4ICMP
	if p.isV6Avail {
		proto = ProtocolIPv6ICMP
	}
	msg, err := icmp.ParseMessage(proto, m.bytes)
	if err != nil {
		return nil, nil, err
	}
	bytes, _ := msg.Body.Marshal(msg.Type.Protocol())
	h, err := icmp.ParseIPv4Header(bytes)
	return h, msg, err
}

// SetIP set ip address
func (p *Ping) SetIP(ips []net.IP) error {
	for _, ip := range ips {
		if IsIPv4(ip) && !p.forceV6 {
			p.addr = &net.IPAddr{IP: ip}
			p.isV4Avail = true
			return nil
		} else if IsIPv6(ip) && !p.forceV4 {
			p.addr = &net.IPAddr{IP: ip}
			p.isV6Avail = true
			return nil
		}
	}
	return fmt.Errorf("there is not  A or AAAA record")
}

// DelIP removes ip adrress
func (p *Ping) DelIP(ipAddr string) {
	//todo
}

// PacketSize set packet size
func (p *Ping) PacketSize(s int) {
	p.pSize = s
}

// listen starts to listen incoming icmp
func (p *Ping) listen(network string) *icmp.PacketConn {
	c, err := icmp.ListenPacket(network, p.source)
	if err != nil {
		log.Fatalf("listen err, %s", err)
	}
	return c
}

// recv reads icmp message
func (p *Ping) recv(conn *icmp.PacketConn, rcvdChan chan<- *packet) {
	var err error
	bytes := make([]byte, 1500)
	conn.SetReadDeadline(time.Now().Add(p.timeout))
	n, dest, err := conn.ReadFrom(bytes)
	if err != nil {
		if neterr, ok := err.(*net.OpError); ok {
			if neterr.Timeout() {
				err = errors.New("Request timeout")
			}
		}
	}

	bytes = bytes[:n]
	select {
	case rcvdChan <- &packet{bytes: bytes, addr: dest, err: err}:
	}
}

func (p *Ping) payload() []byte {
	timeBytes := make([]byte, 8)
	ts := time.Now().UnixNano()
	for i := uint8(0); i < 8; i++ {
		timeBytes[i] = byte((ts >> (i * 8)) & 0xff)
	}
	payload := make([]byte, p.pSize-16)
	payload = append(payload, timeBytes...)
	return payload
}
func (p *Ping) send(conn *icmp.PacketConn) {
	var (
		wg sync.WaitGroup
	)
	var icmpType icmp.Type
	if IsIPv4(p.addr.IP) {
		icmpType = ipv4.ICMPTypeEcho
	} else if IsIPv6(p.addr.IP) {
		icmpType = ipv6.ICMPTypeEchoRequest
	}

	p.seq++
	bytes, err := (&icmp.Message{
		Type: icmpType, Code: 0,
		Body: &icmp.Echo{
			ID:   p.id,
			Seq:  p.seq,
			Data: p.payload(),
		},
	}).Marshal(nil)
	if err != nil {
		println(err.Error())
	}

	wg.Add(1)
	go func(conn *icmp.PacketConn, dest net.Addr, b []byte) {
		defer wg.Done()
		for {
			if _, err := conn.WriteTo(bytes, dest); err != nil {
				println(err.Error())
				if neterr, ok := err.(*net.OpError); ok {
					if neterr.Err == syscall.ENOBUFS {
						continue
					}
				}
			}
			break
		}
	}(conn, p.addr, bytes)

	wg.Wait()
}

// Ping tries to send and receive a packet
func (p *Ping) Ping(out chan Response) {
	var (
		conn     *icmp.PacketConn
		rcvdChan chan *packet = make(chan *packet, 1)
	)

	if p.isV4Avail {
		if conn = p.listen("ip4:icmp"); conn == nil {
			return
		}
		defer conn.Close()
	}

	if p.isV6Avail {
		if conn = p.listen("ip6:ipv6-icmp"); conn == nil {
			return
		}
		defer conn.Close()
	}

	p.send(conn)
	p.recv(conn, rcvdChan)
	rm := <-rcvdChan

	if rm.err != nil {
		out <- Response{Error: rm.err, Sequence: p.seq}
		return
	}
	_, m, err := p.parseMessage(rm)
	if err != nil {
		out <- Response{Error: err, Sequence: p.seq}
		return
	}

	switch m.Body.(type) {
	case *icmp.TimeExceeded:
		out <- Response{Error: fmt.Errorf("time exceeded"), Sequence: p.seq}
	case *icmp.PacketTooBig:
		out <- Response{Error: fmt.Errorf("packet too big"), Sequence: p.seq}
	case *icmp.DstUnreach:
		out <- Response{Error: fmt.Errorf("destination unreachable"), Sequence: p.seq}
	case *icmp.Echo:
		rtt := float64(time.Now().UnixNano()-getTimeStamp(rm.bytes)) / 1000000
		out <- Response{
			Size:     len(rm.bytes),
			Addr:     rm.addr.String(),
			RTT:      rtt,
			Sequence: p.seq,
			Error:    nil,
		}
	default:
		out <- Response{Error: fmt.Errorf("ICMP error"), Sequence: p.seq}
	}
}

// PrintPretty prints out the result pretty format
func (p *Ping) PrintPretty(resp chan Response) {
	var (
		loop          = true
		sigCh         = make(chan os.Signal, 1)
		pFmt          = "%d bytes from %s icmp_seq=%d time=%f ms"
		eFmt          = "%s icmp_seq=%d"
		sFmt          = "%d packets transmitted,  %d packets received, %d%% packet loss\n"
		msg           string
		min, max, avg float64
		c             = map[string]int{"tx": 0, "err": 0, "pl": 0}
	)

	// capture interrupt w/ s channel
	signal.Notify(sigCh, os.Interrupt)
	defer signal.Stop(sigCh)

	fmt.Printf("PING %s (%s): %d data bytes\n", p.target, p.addr, p.pSize-8)
	for loop {
		select {
		case r, ok := <-resp:
			if !ok {
				loop = false
				break
			}
			c["tx"]++
			if r.Error != nil {
				c["err"]++
				msg = fmt.Sprintf(eFmt, r.Error.Error(), r.Sequence)
				println(msg)
				continue
			}

			min = Min(r.RTT, min)
			max = Max(r.RTT, max)
			avg = Avg(r.RTT, avg)

			msg = fmt.Sprintf(pFmt, r.Size, r.Addr, r.Sequence, r.RTT)
			println(msg)
		case <-sigCh:
			loop = false
		}
	}

	if c["tx"] == 0 {
		return
	}
	// packet loss
	c["pl"] = c["err"] * 100 / c["tx"]

	fmt.Printf("\n--- %s ping statistics ---\n", p.target)
	fmt.Printf(sFmt, c["tx"], c["tx"]-c["err"], c["pl"])

	if c["pl"] == 100 {
		return
	}

	fmt.Printf("round-trip min/avg/max = %.3f/%.3f/%.3f ms\n", min, avg, max)
}

// IsCIDR
func (p *Ping) IsCIDR() bool {
	return p.isCIDR
}

// Max handles maximum delay
func Max(x, y float64) float64 {
	if x > y {
		return x
	}
	return y
}

// Min handles minimum delay
func Min(x, y float64) float64 {
	if x < y || y == 0 {
		return x
	}
	return y
}

// Avg handles average delay
func Avg(x, y float64) float64 {
	if y == 0 {
		return x
	}
	return (x + y) / 2
}

// getTimeStamp
func getTimeStamp(m []byte) int64 {
	var ts int64
	for i := uint(0); i < 8; i++ {
		ts += int64(m[uint(len(m))-8+i]) << (i * 8)
	}
	return ts
}

// isCIDR
func isCIDR(s string) bool {
	if _, _, err := net.ParseCIDR(s); err != nil {
		return false
	}
	return true
}

// NormalizeDuration adds default unit (seconds) as needed
func NormalizeDuration(d string) string {
	if match, _ := regexp.MatchString(`^\d+\.{0,1}\d*$`, d); match {
		return d + "s"
	}
	return d
}

// help represents ping help
func help() {
	fmt.Println(`
    usage:
          ping IP address / domain name [options]
    options:
          -c count       Send 'count' requests (default: 4)
          -t timeout     Specify a timeout in format "ms", "s", "m" (default: 2s)
          -i interval    Wait interval between sending each packet (default: 1s)
          -4             Forces the ping command to use IPv4 (target should be hostname)
          -6             Forces the ping command to use IPv6 (target should be hostname)
    Example:
          ping 8.8.8.8
          ping 8.8.8.8 -c 10
          ping google.com -6
          ping mylg.io -i 5s
	`)
}
<|endoftext|>


<commit_before>package feedloggr

const tmplCSS string = `
body {
	margin: 30px 5%;
	line-height: 1.5;
	font-size: 13px;
	font-family: monospace;
	background-color: #FFF;
	color: #444;
}
a, a:visited {
	color: #444;
	text-decoration: none;
}
a:hover {
	color: #000;
	text-decoration: underline;
}

nav {
	text-align: center;
	margin-bottom: 20px;
}
nav > a:hover {
	background-color: #E6E6E6;
	border-color: #ADADAD;
}
section {
	background-color: #FFF;
	margin-bottom: 20px;
}
section > h1, nav > a {
	border: 1px solid #DDD;
	border-radius: 3px;
	padding: 10px;
	margin: 0;
	font-size: 16px;
	font-weight: bold;
	text-align: center;
}
section > h1 {
	background-color: #EEE;
}
section > ul, p {
	padding: 0;
	list-style: "-" outside;
}
section > ul > li {
        margin-bottom: 5px;
}
section > ul > li > a:visited {
	color: #AAA;
}
footer {
	text-align: center;
	font-size: 12px;
}
`

const tmplPage string = `
<!doctype html>

	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>{{.CurrentDate}} | Feedloggr</title>
	<link href="./style.css" rel="stylesheet" type="text/css">

		<
		Latest
		>

	{{range .Feeds}}

{{.Title}}

		{{if .Error}}

Error while updating feed:
{{.Error}}

		{{else}}

{{range .Items}}

{{.Title}}

		{{end}}

		{{end}}

	{{else}}

Sorry, no news for today!

	{{end}}

		Generated with Feedloggr

`
<commit_msg>Gaah yet another css fix<commit_after>package feedloggr

const tmplCSS string = `
body {
	margin: 30px 5%;
	line-height: 1.5;
	font-size: 13px;
	font-family: monospace;
	background-color: #FFF;
	color: #444;
}
a, a:visited {
	color: #444;
	text-decoration: none;
}
a:hover {
	color: #000;
	text-decoration: underline;
}

nav {
	text-align: center;
	margin-bottom: 20px;
}
nav > a:hover {
	background-color: #E6E6E6;
	border-color: #ADADAD;
}
section {
	background-color: #FFF;
	margin-bottom: 20px;
}
section > h1, nav > a {
	border: 1px solid #DDD;
	border-radius: 3px;
	padding: 10px;
	margin: 0;
	font-size: 16px;
	font-weight: bold;
	text-align: center;
}
section > h1 {
	background-color: #EEE;
}
section > ul, p {
	padding: 0;
	margin: 20px 0;
	list-style: "-" outside;
}
section > ul > li {
        margin-bottom: 5px;
}
section > ul > li > a:visited {
	color: #AAA;
}
footer {
	text-align: center;
	font-size: 12px;
}
`

const tmplPage string = `
<!doctype html>

	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>{{.CurrentDate}} | Feedloggr</title>
	<link href="./style.css" rel="stylesheet" type="text/css">

		<
		Latest
		>

	{{range .Feeds}}

{{.Title}}

		{{if .Error}}

Error while updating feed:
{{.Error}}

		{{else}}

{{range .Items}}

{{.Title}}

		{{end}}

		{{end}}

	{{else}}

Sorry, no news for today!

	{{end}}

		Generated with Feedloggr

`
<|endoftext|>


<commit_before>// Copyright 2015-2018 the u-root 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 main

var templates = map[string][]string{
	"all": {
		"github.com/u-root/u-root/cmds/core/*",
		"github.com/u-root/u-root/cmds/boot/*",
		"github.com/u-root/u-root/cmds/nichrome/*",
	},
	"boot": {
		"github.com/u-root/u-root/cmds/boot/boot",
		"github.com/u-root/u-root/cmds/boot/boot2",
		"github.com/u-root/u-root/cmds/boot/fbnetboot",
		"github.com/u-root/u-root/cmds/boot/pxeboot",
		"github.com/u-root/u-root/cmds/boot/localboot",
		"github.com/u-root/u-root/cmds/exp/esxiboot",
	},
	"systemboot": {
		"github.com/u-root/u-root/cmds/boot/boot",
		"github.com/u-root/u-root/cmds/boot/boot2",
		"github.com/u-root/u-root/cmds/boot/fbnetboot",
		"github.com/u-root/u-root/cmds/boot/pxeboot",
		"github.com/u-root/u-root/cmds/boot/localboot",
		"github.com/u-root/u-root/cmds/exp/esxiboot",
		"github.com/u-root/u-root/cmds/boot/uinit",
	},
	// Core should be things you don't want to live without.
	"core": {
		"github.com/u-root/u-root/cmds/core/*",
	},
	// Minimal should be things you can't live without.
	"minimal": {
		"github.com/u-root/u-root/cmds/core/cat",
		"github.com/u-root/u-root/cmds/core/chmod",
		"github.com/u-root/u-root/cmds/core/cmp",
		"github.com/u-root/u-root/cmds/core/cp",
		"github.com/u-root/u-root/cmds/core/date",
		"github.com/u-root/u-root/cmds/core/dd",
		"github.com/u-root/u-root/cmds/core/df",
		"github.com/u-root/u-root/cmds/core/dhclient",
		"github.com/u-root/u-root/cmds/core/dmesg",
		"github.com/u-root/u-root/cmds/core/echo",
		"github.com/u-root/u-root/cmds/core/elvish",
		"github.com/u-root/u-root/cmds/core/find",
		"github.com/u-root/u-root/cmds/core/free",
		"github.com/u-root/u-root/cmds/core/gpgv",
		"github.com/u-root/u-root/cmds/core/grep",
		"github.com/u-root/u-root/cmds/core/gzip",
		"github.com/u-root/u-root/cmds/core/hostname",
		"github.com/u-root/u-root/cmds/core/id",
		"github.com/u-root/u-root/cmds/core/init",
		"github.com/u-root/u-root/cmds/core/insmod",
		"github.com/u-root/u-root/cmds/core/installcommand",
		"github.com/u-root/u-root/cmds/core/io",
		"github.com/u-root/u-root/cmds/core/ip",
		"github.com/u-root/u-root/cmds/core/kexec",
		"github.com/u-root/u-root/cmds/core/kill",
		"github.com/u-root/u-root/cmds/core/ln",
		"github.com/u-root/u-root/cmds/core/losetup",
		"github.com/u-root/u-root/cmds/core/ls",
		"github.com/u-root/u-root/cmds/core/lsmod",
		"github.com/u-root/u-root/cmds/core/mkdir",
		"github.com/u-root/u-root/cmds/core/mknod",
		"github.com/u-root/u-root/cmds/core/mount",
		"github.com/u-root/u-root/cmds/core/msr",
		"github.com/u-root/u-root/cmds/core/mv",
		"github.com/u-root/u-root/cmds/core/pci",
		"github.com/u-root/u-root/cmds/core/ping",
		"github.com/u-root/u-root/cmds/core/printenv",
		"github.com/u-root/u-root/cmds/core/ps",
		"github.com/u-root/u-root/cmds/core/pwd",
		"github.com/u-root/u-root/cmds/core/readlink",
		"github.com/u-root/u-root/cmds/core/rm",
		"github.com/u-root/u-root/cmds/core/rmmod",
		"github.com/u-root/u-root/cmds/core/seq",
		"github.com/u-root/u-root/cmds/core/shutdown",
		"github.com/u-root/u-root/cmds/core/sleep",
		"github.com/u-root/u-root/cmds/core/sync",
		"github.com/u-root/u-root/cmds/core/tail",
		"github.com/u-root/u-root/cmds/core/tee",
		"github.com/u-root/u-root/cmds/core/truncate",
		"github.com/u-root/u-root/cmds/core/umount",
		"github.com/u-root/u-root/cmds/core/uname",
		"github.com/u-root/u-root/cmds/core/unshare",
		"github.com/u-root/u-root/cmds/core/wc",
		"github.com/u-root/u-root/cmds/core/wget",
		"github.com/u-root/u-root/cmds/core/which",
	},
	// coreboot-app minimal environment
	"coreboot-app": {
		"github.com/u-root/u-root/cmds/core/cat",
		"github.com/u-root/u-root/cmds/core/cbmem",
		"github.com/u-root/u-root/cmds/core/chroot",
		"github.com/u-root/u-root/cmds/core/cp",
		"github.com/u-root/u-root/cmds/core/dd",
		"github.com/u-root/u-root/cmds/core/dhclient",
		"github.com/u-root/u-root/cmds/core/dmesg",
		"github.com/u-root/u-root/cmds/core/elvish",
		"github.com/u-root/u-root/cmds/core/find",
		"github.com/u-root/u-root/cmds/core/grep",
		"github.com/u-root/u-root/cmds/core/id",
		"github.com/u-root/u-root/cmds/core/init",
		"github.com/u-root/u-root/cmds/core/insmod",
		"github.com/u-root/u-root/cmds/core/ip",
		"github.com/u-root/u-root/cmds/core/kill",
		"github.com/u-root/u-root/cmds/core/ls",
		"github.com/u-root/u-root/cmds/core/mount",
		"github.com/u-root/u-root/cmds/core/pci",
		"github.com/u-root/u-root/cmds/core/ping",
		"github.com/u-root/u-root/cmds/core/ps",
		"github.com/u-root/u-root/cmds/core/pwd",
		"github.com/u-root/u-root/cmds/core/rm",
		"github.com/u-root/u-root/cmds/core/rmmod",
		"github.com/u-root/u-root/cmds/core/shutdown",
		"github.com/u-root/u-root/cmds/core/sshd",
		"github.com/u-root/u-root/cmds/core/switch_root",
		"github.com/u-root/u-root/cmds/core/tail",
		"github.com/u-root/u-root/cmds/core/tee",
		"github.com/u-root/u-root/cmds/core/uname",
		"github.com/u-root/u-root/cmds/core/wget",
	},
}
<commit_msg>Make coreboot-app template buildable again.<commit_after>// Copyright 2015-2018 the u-root 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 main

var templates = map[string][]string{
	"all": {
		"github.com/u-root/u-root/cmds/core/*",
		"github.com/u-root/u-root/cmds/boot/*",
		"github.com/u-root/u-root/cmds/nichrome/*",
	},
	"boot": {
		"github.com/u-root/u-root/cmds/boot/boot",
		"github.com/u-root/u-root/cmds/boot/boot2",
		"github.com/u-root/u-root/cmds/boot/fbnetboot",
		"github.com/u-root/u-root/cmds/boot/pxeboot",
		"github.com/u-root/u-root/cmds/boot/localboot",
		"github.com/u-root/u-root/cmds/exp/esxiboot",
	},
	"systemboot": {
		"github.com/u-root/u-root/cmds/boot/boot",
		"github.com/u-root/u-root/cmds/boot/boot2",
		"github.com/u-root/u-root/cmds/boot/fbnetboot",
		"github.com/u-root/u-root/cmds/boot/pxeboot",
		"github.com/u-root/u-root/cmds/boot/localboot",
		"github.com/u-root/u-root/cmds/exp/esxiboot",
		"github.com/u-root/u-root/cmds/boot/uinit",
	},
	// Core should be things you don't want to live without.
	"core": {
		"github.com/u-root/u-root/cmds/core/*",
	},
	// Minimal should be things you can't live without.
	"minimal": {
		"github.com/u-root/u-root/cmds/core/cat",
		"github.com/u-root/u-root/cmds/core/chmod",
		"github.com/u-root/u-root/cmds/core/cmp",
		"github.com/u-root/u-root/cmds/core/cp",
		"github.com/u-root/u-root/cmds/core/date",
		"github.com/u-root/u-root/cmds/core/dd",
		"github.com/u-root/u-root/cmds/core/df",
		"github.com/u-root/u-root/cmds/core/dhclient",
		"github.com/u-root/u-root/cmds/core/dmesg",
		"github.com/u-root/u-root/cmds/core/echo",
		"github.com/u-root/u-root/cmds/core/elvish",
		"github.com/u-root/u-root/cmds/core/find",
		"github.com/u-root/u-root/cmds/core/free",
		"github.com/u-root/u-root/cmds/core/gpgv",
		"github.com/u-root/u-root/cmds/core/grep",
		"github.com/u-root/u-root/cmds/core/gzip",
		"github.com/u-root/u-root/cmds/core/hostname",
		"github.com/u-root/u-root/cmds/core/id",
		"github.com/u-root/u-root/cmds/core/init",
		"github.com/u-root/u-root/cmds/core/insmod",
		"github.com/u-root/u-root/cmds/core/installcommand",
		"github.com/u-root/u-root/cmds/core/io",
		"github.com/u-root/u-root/cmds/core/ip",
		"github.com/u-root/u-root/cmds/core/kexec",
		"github.com/u-root/u-root/cmds/core/kill",
		"github.com/u-root/u-root/cmds/core/ln",
		"github.com/u-root/u-root/cmds/core/losetup",
		"github.com/u-root/u-root/cmds/core/ls",
		"github.com/u-root/u-root/cmds/core/lsmod",
		"github.com/u-root/u-root/cmds/core/mkdir",
		"github.com/u-root/u-root/cmds/core/mknod",
		"github.com/u-root/u-root/cmds/core/mount",
		"github.com/u-root/u-root/cmds/core/msr",
		"github.com/u-root/u-root/cmds/core/mv",
		"github.com/u-root/u-root/cmds/core/pci",
		"github.com/u-root/u-root/cmds/core/ping",
		"github.com/u-root/u-root/cmds/core/printenv",
		"github.com/u-root/u-root/cmds/core/ps",
		"github.com/u-root/u-root/cmds/core/pwd",
		"github.com/u-root/u-root/cmds/core/readlink",
		"github.com/u-root/u-root/cmds/core/rm",
		"github.com/u-root/u-root/cmds/core/rmmod",
		"github.com/u-root/u-root/cmds/core/seq",
		"github.com/u-root/u-root/cmds/core/shutdown",
		"github.com/u-root/u-root/cmds/core/sleep",
		"github.com/u-root/u-root/cmds/core/sync",
		"github.com/u-root/u-root/cmds/core/tail",
		"github.com/u-root/u-root/cmds/core/tee",
		"github.com/u-root/u-root/cmds/core/truncate",
		"github.com/u-root/u-root/cmds/core/umount",
		"github.com/u-root/u-root/cmds/core/uname",
		"github.com/u-root/u-root/cmds/core/unshare",
		"github.com/u-root/u-root/cmds/core/wc",
		"github.com/u-root/u-root/cmds/core/wget",
		"github.com/u-root/u-root/cmds/core/which",
	},
	// coreboot-app minimal environment
	"coreboot-app": {
		"github.com/u-root/u-root/cmds/core/cat",
		"github.com/u-root/u-root/cmds/exp/cbmem",
		"github.com/u-root/u-root/cmds/core/chroot",
		"github.com/u-root/u-root/cmds/core/cp",
		"github.com/u-root/u-root/cmds/core/dd",
		"github.com/u-root/u-root/cmds/core/dhclient",
		"github.com/u-root/u-root/cmds/core/dmesg",
		"github.com/u-root/u-root/cmds/core/elvish",
		"github.com/u-root/u-root/cmds/core/find",
		"github.com/u-root/u-root/cmds/core/grep",
		"github.com/u-root/u-root/cmds/core/id",
		"github.com/u-root/u-root/cmds/core/init",
		"github.com/u-root/u-root/cmds/core/insmod",
		"github.com/u-root/u-root/cmds/core/ip",
		"github.com/u-root/u-root/cmds/core/kill",
		"github.com/u-root/u-root/cmds/core/ls",
		"github.com/u-root/u-root/cmds/core/mount",
		"github.com/u-root/u-root/cmds/core/pci",
		"github.com/u-root/u-root/cmds/core/ping",
		"github.com/u-root/u-root/cmds/core/ps",
		"github.com/u-root/u-root/cmds/core/pwd",
		"github.com/u-root/u-root/cmds/core/rm",
		"github.com/u-root/u-root/cmds/core/rmmod",
		"github.com/u-root/u-root/cmds/core/shutdown",
		"github.com/u-root/u-root/cmds/core/sshd",
		"github.com/u-root/u-root/cmds/core/switch_root",
		"github.com/u-root/u-root/cmds/core/tail",
		"github.com/u-root/u-root/cmds/core/tee",
		"github.com/u-root/u-root/cmds/core/uname",
		"github.com/u-root/u-root/cmds/core/wget",
	},
}
<|endoftext|>


<commit_before>// Copyright 2020 The LUCI Authors.
//
// 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 eval

import (
	"context"
	"fmt"
	"sort"
	"strconv"
	"time"

	"golang.org/x/time/rate"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"go.chromium.org/luci/common/errors"
	gerritpb "go.chromium.org/luci/common/proto/gerrit"
	"go.chromium.org/luci/common/retry"
	"go.chromium.org/luci/common/retry/transient"
	"go.chromium.org/luci/grpc/grpcutil"

	evalpb "go.chromium.org/luci/rts/presubmit/eval/proto"
)

var psNotFound = &errors.BoolTag{Key: errors.NewTagKey("patchset not found")}

type gerritClient struct {
	// listFilesRPC makes a Gerrit RPC to fetch the list of changed files.
	// Mockable.
	listFilesRPC  func(ctx context.Context, host string, req *gerritpb.ListFilesRequest) (*gerritpb.ListFilesResponse, error)
	limiter       *rate.Limiter
	fileListCache cache
}

type changedFiles struct {
	Names []string `json:"names"`
}

// ChangedFiles returns the list of files changed in the given patchset.
func (c *gerritClient) ChangedFiles(ctx context.Context, ps *evalpb.GerritPatchset) ([]string, error) {
	cacheKey := fmt.Sprintf("%s-%d-%d", ps.Change.Host, ps.Change.Number, ps.Patchset)

	value, err := c.fileListCache.GetOrCreate(ctx, cacheKey, func() (interface{}, error) {
		res, err := c.fetchChangedFiles(ctx, ps)
		if err != nil {
			return nil, err
		}

		names := make([]string, 0, len(res.Files))
		for name := range res.Files {
			names = append(names, name)
		}
		sort.Strings(names)
		return &changedFiles{Names: names}, nil
	})
	if err != nil {
		return nil, err
	}
	return value.(*changedFiles).Names, nil
}

func (c *gerritClient) fetchChangedFiles(ctx context.Context, ps *evalpb.GerritPatchset) (*gerritpb.ListFilesResponse, error) {
	var res *gerritpb.ListFilesResponse
	err := retry.Retry(ctx, transient.Only(retry.Default), func() (err error) {
		res, err = c.listFilesWithQuotaErrorsRetries(ctx, ps.Change.Host, &gerritpb.ListFilesRequest{
			Project:    ps.Change.Project,
			Number:     int64(ps.Change.Number),
			RevisionId: strconv.Itoa(int(ps.Patchset)),
		})
		if grpcutil.IsTransientCode(statusCode(err)) {
			err = transient.Tag.Apply(err)
		}
		return
	}, retry.LogCallback(ctx, fmt.Sprintf("read %s", psURL(ps))))

	if err != nil {
		if statusCode(err) == codes.NotFound {
			err = psNotFound.Apply(err)
		}
		return nil, err
	}

	return res, nil
}

// listFilesWithQuotaErrorsRetries fetches the list of changed files.
// If the request fails with quota exhaustion, retries the request in a second,
// up to 5 times.
// Does not retry other transient errors, e.g. internal errors.
func (c *gerritClient) listFilesWithQuotaErrorsRetries(ctx context.Context, host string, req *gerritpb.ListFilesRequest) (*gerritpb.ListFilesResponse, error) {
	// Retry ResourceExhausted errors with an increased delay.
	iter := func() retry.Iterator {
		base := retry.Limited{
			Delay:   5 * time.Second, // short-term quota resets at most every 5s.
			Retries: 5,
		}
		return retry.NewIterator(func(ctx context.Context, err error) time.Duration {
			if statusCode(err) == codes.ResourceExhausted {
				return base.Next(ctx, err)
			}
			return retry.Stop
		})
	}

	var ret *gerritpb.ListFilesResponse
	err := retry.Retry(ctx, iter, func() (err error) {
		if err := c.limiter.Wait(ctx); err != nil {
			return err
		}
		ret, err = c.listFilesRPC(ctx, host, req)
		return
	}, nil)
	return ret, err
}

func statusCode(err error) codes.Code {
	return status.Code(errors.Unwrap(err))
}

// psURL returns the patchset URL.
func psURL(p *evalpb.GerritPatchset) string {
	return fmt.Sprintf("https://%s/c/%d/%d", p.Change.Host, p.Change.Number, p.Patchset)
}
<commit_msg>[rts] Warn about cold Gerrit cache<commit_after>// Copyright 2020 The LUCI Authors.
//
// 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 eval

import (
	"context"
	"fmt"
	"sort"
	"strconv"
	"sync"
	"time"

	"golang.org/x/time/rate"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	gerritpb "go.chromium.org/luci/common/proto/gerrit"
	"go.chromium.org/luci/common/retry"
	"go.chromium.org/luci/common/retry/transient"
	"go.chromium.org/luci/grpc/grpcutil"

	evalpb "go.chromium.org/luci/rts/presubmit/eval/proto"
)

var psNotFound = &errors.BoolTag{Key: errors.NewTagKey("patchset not found")}

type gerritClient struct {
	// listFilesRPC makes a Gerrit RPC to fetch the list of changed files.
	// Mockable.
	listFilesRPC    func(ctx context.Context, host string, req *gerritpb.ListFilesRequest) (*gerritpb.ListFilesResponse, error)
	limiter         *rate.Limiter
	fileListCache   cache
	reportColdCache sync.Once
}

type changedFiles struct {
	Names []string `json:"names"`
}

// ChangedFiles returns the list of files changed in the given patchset.
func (c *gerritClient) ChangedFiles(ctx context.Context, ps *evalpb.GerritPatchset) ([]string, error) {
	cacheKey := fmt.Sprintf("%s-%d-%d", ps.Change.Host, ps.Change.Number, ps.Patchset)

	value, err := c.fileListCache.GetOrCreate(ctx, cacheKey, func() (interface{}, error) {
		res, err := c.fetchChangedFiles(ctx, ps)
		if err != nil {
			return nil, err
		}

		names := make([]string, 0, len(res.Files))
		for name := range res.Files {
			names = append(names, name)
		}
		sort.Strings(names)
		return &changedFiles{Names: names}, nil
	})
	if err != nil {
		return nil, err
	}
	return value.(*changedFiles).Names, nil
}

func (c *gerritClient) fetchChangedFiles(ctx context.Context, ps *evalpb.GerritPatchset) (*gerritpb.ListFilesResponse, error) {
	var res *gerritpb.ListFilesResponse
	err := retry.Retry(ctx, transient.Only(retry.Default), func() (err error) {
		res, err = c.listFilesWithQuotaErrorsRetries(ctx, ps.Change.Host, &gerritpb.ListFilesRequest{
			Project:    ps.Change.Project,
			Number:     int64(ps.Change.Number),
			RevisionId: strconv.Itoa(int(ps.Patchset)),
		})
		if grpcutil.IsTransientCode(statusCode(err)) {
			err = transient.Tag.Apply(err)
		}
		return
	}, retry.LogCallback(ctx, fmt.Sprintf("read %s", psURL(ps))))

	if err != nil {
		if statusCode(err) == codes.NotFound {
			err = psNotFound.Apply(err)
		}
		return nil, err
	}

	return res, nil
}

// listFilesWithQuotaErrorsRetries fetches the list of changed files.
// If the request fails with quota exhaustion, retries the request in a second,
// up to 5 times.
// Does not retry other transient errors, e.g. internal errors.
func (c *gerritClient) listFilesWithQuotaErrorsRetries(ctx context.Context, host string, req *gerritpb.ListFilesRequest) (*gerritpb.ListFilesResponse, error) {
	// Retry ResourceExhausted errors with an increased delay.
	iter := func() retry.Iterator {
		base := retry.Limited{
			Delay:   5 * time.Second, // short-term quota resets at most every 5s.
			Retries: 5,
		}
		return retry.NewIterator(func(ctx context.Context, err error) time.Duration {
			if statusCode(err) == codes.ResourceExhausted {
				return base.Next(ctx, err)
			}
			return retry.Stop
		})
	}

	var ret *gerritpb.ListFilesResponse
	err := retry.Retry(ctx, iter, func() (err error) {
		ret, err = c.callListFiles(ctx, host, req)
		return
	}, nil)
	return ret, err
}

// callListFiles makes a ListFiles RPC.
func (c *gerritClient) callListFiles(ctx context.Context, host string, req *gerritpb.ListFilesRequest) (*gerritpb.ListFilesResponse, error) {
	if err := c.limiter.Wait(ctx); err != nil {
		return nil, err
	}
	ret, err := c.listFilesRPC(ctx, host, req)

	// Report cold cache only on success because deleted CLs are not cached.
	if err == nil {
		c.reportColdCache.Do(func() {
			logging.Infof(ctx, "The Gerrit cache is cold. It will take time some to fetch Gerrit info. The next evaluation will be faster.")
		})
	}

	return ret, err
}

func statusCode(err error) codes.Code {
	return status.Code(errors.Unwrap(err))
}

// psURL returns the patchset URL.
func psURL(p *evalpb.GerritPatchset) string {
	return fmt.Sprintf("https://%s/c/%d/%d", p.Change.Host, p.Change.Number, p.Patchset)
}
<|endoftext|>


<commit_before>/*
Terse is an html templating language inspired by slim, but
written for the features of multitemplate. The minimal logic
marks were inspired by mustache, but I'm not going to pretend
they're not template logic. Note that the logic syntax is the
same as the template logic syntax in the Go standard library.
Terse is not an acronym, it is a description of the design of
the language.

Features

- Significant Whitespace

- Auto-closing tags and functions

- Integrates with other multitemplate languages

- Uses same function calling syntax as stdlib templates

Using Terse

Active terse as a multitemplate languages by importing the
terse library like

    import _ "github.com/acsellers/multitemplate/terse"

At that point, Parse will now take a "terse" parser for templates
and ParseFiles/ParseGlob will detect files with a .terse extension
as terse formatted files.

An example terse file:

    #content
      .title
        h1= .Title
        = link_to "Home" (url_for "home")

The resulting HTML would look like:

Welcome

      Home

Security and XSS Protection

Terse uses the built-in protection of html/template via multitemplate.

Simple HTML

Most of HTML documents is static text. Terse makes writing this static
text as simple as possible. There are three ways to write HTML tags, the
automatic way, the manual way, and the verbatim way. The automatic way
will detect tags by name, and will do a case sensitive search on the
elements listed in the ValidElements map. The manual way will detect
elements by a percentage sign before the element name, which is the same
behavior as Haml and Bham. Finally, you can just add verbatim HTML code,
and it wil be passed through to the final output. This is why the character
'<' has no special meaning in terse.

  // Source
  article

My Title

  // Output

My Title

Class and Id Shorthand

Id's and Class attributes may be specified in shorthand. If you do
not specify an html element, terse will assume that you want to use
the div element. Multiple class attributes will be combined into the
class attribute, while multiple id attributes will take the final id
attribute. Classes are similar to css declarations, where '.'s precede
class attributes and '#'s precede id attributes. You can still provide
id and class attributes using the standard HTML attributes.

  // Source
  #sidebar
    ul#sidebar_list.vertical_list
      li.sidebar_element.active Thing

  // Output

Thing

HTML Attributes

There are two ways to specify attributes, one which allows for multiple
lines with attributes and another that requires all attributes to be
specied on the same line. By specifying the attribute name, then an
equals sign, then either a string, a $variable, a RenderArg which is
a . followed by words. Finally, if you just specify a helper function.
Note that if the helper function needs arguments, you should wrap the
function and arguments in parentheses. Boolean HTML attributes, that is
attributes that have no value, either require parentheses or can have
the value 'true' or 'false'.

  // Single Line
  div id="special_thing" class=$DivIds special thing content
  // Output

    special thing content

  // Multi Line
  input(
    name="is_active"
    type="checkbox"
    value="yes"
    checked=.User.Active
  )
  // Output
  <input type="is_active" type="checkbox" value="yes" checkbox="{{.User.Active}}" />

  // All the ways to specify attributes
  input(
    name=$inputName
    type="checkbox"
    value=.User.Name
    checked=(if_print "checked" .User.ShowName)
    class=.Check.User.Class
  )

  // Boolean Attribute Option 1
  .content ng-view=true
  // Boolean Attribute Option 2
  .content(ng-view)
  // Output

Doctype

Doctype lines are prefixed with two exclamation points (!!). There is
the default doctype, which is a line with just !!, terse will assume
you want to use the HTML5 Doctype (<!DOCTYPE html>). You can modify
the terse.Doctypes variable to change to another default by changing
the empty string key. Terse includes 8 different Doctypes by default,
which are Transitional (XHTML 1.0 Transitional), Strict (XHTML 1.0
Strict), Frameset (XHTML 1.0 Frameset), 5 (HTML5), 1.1 (XHTML 1.1),
Basic (XHTML 1.1 Basic), Mobile (XHTML Mobile 1.2), and RDFa
(XHTML+RDFa 1.0). These are the same as haml's Doctypes, except
that we don't worry about a Format option.

  // Source
  !!
  // Output
  <!DOCTYPE html>

Comments

By prefixing lines with a double slash (//), that line and any nested
lines will be marked as a comment. Comment lines do not show up in the
rendered template in any way.

Executing Code

There are preferred methods for if, range, with, and block functions,
but for template functions, there is a generic way to use code in
your templates. By adding an = at the start of the line, you can
mark that line as executable, for multiple lines there is a continuation
marker (/=) which will collapse multiple lines into a single line of code,
so long as the continuation lines are nested in the first code line. Note
that the syntax is the same as text/template in the standard library,
and the behavior about inserting code results into the output is same as
the text/template code as well.

  // Source
  = print .User
  // Output
  {{ print .User }}

  // Source
  = form_tag (url_for "user.new") (attrs
    /= "enctype" "multipart/form-data"
    /= "method" "PUT")
  // Output
  <form action="{{ url_for "user.new" }}" enctype="multipart/form-data" method="PUT">

Auto Closing Functions

When lines are nested, terse will look a function another function
that has the same name as the function that has the nested name, but
with the prefix 'end_'.

  // Source
  = fieldset_tag "Visibility"
    = checkbox_tag "admins"
    = checkbox_tag "users"
    = checkbox_tag "visitors"

  // Output
  {{ fieldset_tag "Visibility" }} {{ checkbox_tag "admins" }}
    {{ checkbox_tag "users" }}
    {{ checkbox_tag "visitors" }}
  {{ end_fieldset_tag }}

If and Else Statements

If lines begin with a question mark (?), then a statement to be evaluated. The
method of evaluation is the same as text/template, where if the statement
evaluates to false, 0, nil, or anything with a length of 0, the statement
is not executed. Else is an exclamation mark followed by a question mark,
or "not the question".

  // Source
  ?.User
    Welcome
    = .User.Name
  !?
    Please Login

  // Output
  {{ if .User }}
    Welcome
  {{ else }}
    Please Login
  {{ end }}

Range Statements

Range statements begin with an ampersand (&). Else statements for ranges
are an exclamation mark followed by an ampersand (!&). Variables for the
range statement are specified using colons after the statement you will
be ranging over.

  // Source
  &.Users:$user:$index
    {{ $index }}. {{ $user.Name }}
  !&
    No matching users.

  // Output
  {{ range $user, $index := .Users }}
    {{ $index }}. {{ $user.Name }}
  {{ else }}
    No matchin users.
  {{ end }}

Block Statements

Add text here

With Statements

Add implementation (>pipeline) and text here

Interpolation

In addition to the function lines, you can also embed functions into
regular lines, using the Delimeters set on terse (same as the standard
library delimeters by default).

  // Source
  Welcome {{ .User.Name }}.

  // Output (same)
  Welcome {{ .User.Name}}.

Filters

Filters are started with a :, then the name of a registered filter.
Filters can have interpolated code within them, if the filter may
have interpolated code, it needs to return true, else if it returns
false, the filtered text will not be checked for code.

  // Source
  :js
    $(document).ready(function{ setup_ajax(); });

  // Output

Notes

The format of the documentation and some examples were inspired by
Haml's REFERENCE file.

*/
package terse
<commit_msg>Add more documentation for terse, blocks, yields, etc.<commit_after>/*
Terse is an html templating language inspired by slim, but
written for the features of multitemplate. The minimal logic
marks were inspired by mustache, but I'm not going to pretend
they're not template logic. Note that the logic syntax is the
same as the template logic syntax in the Go standard library.
Terse is not an acronym, it is a description of the design of
the language.

Features

- Significant Whitespace

- Auto-closing tags and functions

- Integrates with other multitemplate languages

- Uses same function calling syntax as stdlib templates

Using Terse

Active terse as a multitemplate languages by importing the
terse library like

    import _ "github.com/acsellers/multitemplate/terse"

At that point, Parse will now take a "terse" parser for templates
and ParseFiles/ParseGlob will detect files with a .terse extension
as terse formatted files.

An example terse file:

    #content
      .title
        h1= .Title
        = link_to "Home" (url_for "home")

The resulting HTML would look like:

Welcome

      Home

Security and XSS Protection

Terse uses the built-in protection of html/template via multitemplate.

Simple HTML

Most of HTML documents is static text. Terse makes writing this static
text as simple as possible. There are three ways to write HTML tags, the
automatic way, the manual way, and the verbatim way. The automatic way
will detect tags by name, and will do a case sensitive search on the
elements listed in the ValidElements map. The manual way will detect
elements by a percentage sign before the element name, which is the same
behavior as Haml and Bham. Finally, you can just add verbatim HTML code,
and it wil be passed through to the final output. This is why the character
'<' has no special meaning in terse.

  // Source
  article

My Title

  // Output

My Title

Class and Id Shorthand

Id's and Class attributes may be specified in shorthand. If you do
not specify an html element, terse will assume that you want to use
the div element. Multiple class attributes will be combined into the
class attribute, while multiple id attributes will take the final id
attribute. Classes are similar to css declarations, where '.'s precede
class attributes and '#'s precede id attributes. You can still provide
id and class attributes using the standard HTML attributes.

  // Source
  #sidebar
    ul#sidebar_list.vertical_list
      li.sidebar_element.active Thing

  // Output

Thing

HTML Attributes

There are two ways to specify attributes, one which allows for multiple
lines with attributes and another that requires all attributes to be
specied on the same line. By specifying the attribute name, then an
equals sign, then either a string, a $variable, a RenderArg which is
a . followed by words. Finally, if you just specify a helper function.
Note that if the helper function needs arguments, you should wrap the
function and arguments in parentheses. Boolean HTML attributes, that is
attributes that have no value, either require parentheses or can have
the value 'true' or 'false'.

  // Single Line
  div id="special_thing" class=$DivIds special thing content
  // Output

    special thing content

  // Multi Line
  input(
    name="is_active"
    type="checkbox"
    value="yes"
    checked=.User.Active
  )
  // Output
  <input type="is_active" type="checkbox" value="yes" checkbox="{{.User.Active}}" />

  // All the ways to specify attributes
  input(
    name=$inputName
    type="checkbox"
    value=.User.Name
    checked=(if_print "checked" .User.ShowName)
    class=.Check.User.Class
  )

  // Boolean Attribute Option 1
  .content ng-view=true
  // Boolean Attribute Option 2
  .content(ng-view)
  // Output

Doctype

Doctype lines are prefixed with two exclamation points (!!). There is
the default doctype, which is a line with just !!, terse will assume
you want to use the HTML5 Doctype (<!DOCTYPE html>). You can modify
the terse.Doctypes variable to change to another default by changing
the empty string key. Terse includes 8 different Doctypes by default,
which are Transitional (XHTML 1.0 Transitional), Strict (XHTML 1.0
Strict), Frameset (XHTML 1.0 Frameset), 5 (HTML5), 1.1 (XHTML 1.1),
Basic (XHTML 1.1 Basic), Mobile (XHTML Mobile 1.2), and RDFa
(XHTML+RDFa 1.0). These are the same as haml's Doctypes, except
that we don't worry about a Format option.

  // Source
  !!
  // Output
  <!DOCTYPE html>

Comments

By prefixing lines with a double slash (//), that line and any nested
lines will be marked as a comment. Comment lines do not show up in the
rendered template in any way.

Executing Code

There are preferred methods for if, range, with, and block functions,
but for template functions, there is a generic way to use code in
your templates. By adding an = at the start of the line, you can
mark that line as executable, for multiple lines there is a continuation
marker (/=) which will collapse multiple lines into a single line of code,
so long as the continuation lines are nested in the first code line. Note
that the syntax is the same as text/template in the standard library,
and the behavior about inserting code results into the output is same as
the text/template code as well.

  // Source
  = print .User
  // Output
  {{ print .User }}

  // Source
  = form_tag (url_for "user.new") (attrs
    /= "enctype" "multipart/form-data"
    /= "method" "PUT")
  // Output
  <form action="{{ url_for "user.new" }}" enctype="multipart/form-data" method="PUT">

Auto Closing Functions

When lines are nested, terse will look a function another function
that has the same name as the function that has the nested name, but
with the prefix 'end_'.

  // Source
  = fieldset_tag "Visibility"
    = checkbox_tag "admins"
    = checkbox_tag "users"
    = checkbox_tag "visitors"

  // Output
  {{ fieldset_tag "Visibility" }} {{ checkbox_tag "admins" }}
    {{ checkbox_tag "users" }}
    {{ checkbox_tag "visitors" }}
  {{ end_fieldset_tag }}

If and Else Statements

If lines begin with a question mark (?), then a statement to be evaluated. The
method of evaluation is the same as text/template, where if the statement
evaluates to false, 0, nil, or anything with a length of 0, the statement
is not executed. Else is an exclamation mark followed by a question mark,
or "not the question".

  // Source
  ?.User
    Welcome
    = .User.Name
  !?
    Please Login

  // Output
  {{ if .User }}
    Welcome
  {{ else }}
    Please Login
  {{ end }}

Range Statements

Range statements begin with an ampersand (&). Else statements for ranges
are an exclamation mark followed by an ampersand (!&). Variables for the
range statement are specified using colons after the statement you will
be ranging over.

  // Source
  &.Users:$user:$index
    {{ $index }}. {{ $user.Name }}
  !&
    No matching users.

  // Output
  {{ range $user, $index := .Users }}
    {{ $index }}. {{ $user.Name }}
  {{ else }}
    No matching users.
  {{ end }}

Block Statements

Blocks are central to multitemplate and terse features
them in a first-class manner. The simple block call is [name] where
name is the name you are using for that block. If you a special
form of the block statement, just replace the opening '['. For an
exec block you use a $, while define block uses a ^. The mnemonic
for those is that exec is the end of a block, so it is a regex endline
symbol ($), while define-block is more a beginning statement, and the
regex character for the beginning of line is ^. Blocks are automatically
closed in terse.

  // Source
  [content]
    h1
      Welcome
      = .User.Name

  // Output
  {{ block "content" }}

Welcome {{.User.Name }}

  {{ end_block }}

  // Source
  ^content]
    I'm defining this block and not outputting here.
  // Output
  {{ define_block "content" }}
    I'm defining this block and not outputting here.
  {{ end_block }}

  // Source
  $content]
    We will definitely put some block content here.
  // Output
  {{ exec_block "content" }}
    We will definitely put some block content here.
  {{ end_block }}

Yield Statements

Yield statements (for blocks or templates) are written as @name,
where name is the name you are using for that block or template.

  // Source
  #content
    @content

  // Output

    {{ yield "content" }}

Extend Statements

Extend statements (for inheriting from other templates) are written
using @@ followed by the template name (without quotation marks).

  // Source
  @@layouts/app.html

  // Output
  {{ extend "layouts/app.html" }}

With Statements

Add implementation (>pipeline) and text here

Interpolation

In addition to the function lines, you can also embed functions into
regular lines, using the Delimeters set on terse (same as the standard
library delimeters by default).

  // Source
  Welcome {{ .User.Name }}.

  // Output (same)
  Welcome {{ .User.Name}}.

Filters

Filters are started with a :, then the name of a registered filter.
Filters can have interpolated code within them, if the filter may
have interpolated code, it needs to return true, else if it returns
false, the filtered text will not be checked for code.

  // Source
  :js
    $(document).ready(function{ setup_ajax(); });

  // Output

Notes

The format of the documentation and some examples were inspired by
Haml's REFERENCE file.

*/
package terse
<|endoftext|>