<commit_before>// Red-Black tree implementation. Each tree node (entry) contains Key and Value.
// Entries in the RbTree are always ordered according to the key value, so
// it can be used as ordered set (std::set in C++) or ordered map (std::map).
// Note: all methods are not goroutine-safe.
package rbt

type RbTree struct {
	less       LessFunc
	root       *RbTreeNode
	size       int
}

// Red-black tree node, contains key and value. It is safe to overwrite Value
// in-place.
type RbTreeNode struct {
	left, right, parent *RbTreeNode
	// key
	key          interface{}
	Value        interface{}
	isred        bool         // true == red, false == black
}

// LessFunc is a key comparsion function.
// Must return true if k1 < k2, false otherwise
type LessFunc func(k1, k2 interface{}) bool

// Create new RbTree with provided comparsion function. The latter must
// return true if k1 < k2, false otherwise.
func NewRbTree(lessFunc LessFunc) *RbTree {
	return &RbTree{ less: lessFunc }
}

// Find node by key and return its Value, returns nil if key not found.
func (t *RbTree) Find(key interface{}) interface{} {
	n := t.FindNode(key)
	if n != nil {
		return n.Value
	}
	return nil
}

// Find a node by key, returns nil if not found
func (t *RbTree) FindNode(key interface{}) *RbTreeNode {
	x := t.root
	for x != nil {
		if t.less(x.key, key) {
			x = x.left
		} else {
			if t.less(key, x.key) {
				x = x.right
			} else {
				return x
			}
		}
	}
	return nil
}

// Get last node in the tree (with highest key value).
func (t *RbTree) Last() *RbTreeNode {
    if nil == t.root {
        return nil
    }
	return t.root.min()
}

// Get first node in the tree (with lowest key value).
func (t *RbTree) First() *RbTreeNode {
    if nil == t.root {
        return nil
    }
    return t.root.max()
}

// Returns previous node in the tree, in descending key value order.
func (x *RbTreeNode) Prev() *RbTreeNode {
	if x.right != nil {
		return x.right.min()
	}
	y := x.parent
	for y != nil && x == y.right {
		x = y
		y = y.parent
	}
	return y
}

// Returns key associated with tree node.
func (x *RbTreeNode) Key() interface{} {
	return x.key
}

// Returns next node in the tree, in ascending key value order.
func (x *RbTreeNode) Next() *RbTreeNode {
	if x.left != nil {
		return x.left.max()
	}
	y := x.parent
	for y != nil && x == y.left {
		x = y
		y = y.parent
	}
	return y
}

// Returns number of entries in the tree. This function returns internal
// counter, therefore it is fast and safe to use in loops.
func (t *RbTree) Size() int {
	return t.size
}

// Remove all entries in the tree.
func (t *RbTree) Clear() {
	t.root = nil
	t.size = 0
}

// Insert key and value into the tree. If new entry is created, returns true.
// If key already exists, value gets replaced and Insert returns false.
func (t *RbTree) Insert(key interface{}, value interface{}) bool {
	x := t.root
	var y *RbTreeNode

	for x != nil {
		y = x
		if t.less(x.key, key) {
			x = x.left
		} else if t.less(key, x.key) {
			x = x.right
		} else {
            x.Value = value
            return false // overwrite value
        }
	}
	z := &RbTreeNode{parent: y, isred: true, key: key, Value: value}
	if y == nil {
		t.root = z
	} else {
        if t.less(y.key, key) {
    		y.left = z
        } else {
            y.right = z
        }
    }
	t.rb_insert_fixup(z)
	t.size++
	return true
}

// Delete tree node by key.
func (t *RbTree) Delete(key interface{}) {
    if z := t.FindNode(key); z != nil {
	    t.DeleteNode(z)
    }
}

// Delete tree node.
func (t *RbTree) DeleteNode(z *RbTreeNode) {
	var x, y, parent *RbTreeNode
	y, y_original_isred, parent := z, z.isred, z.parent
	if z.left == nil {
		x = z.right
		t.rbtransplant(z, z.right)
	} else if z.right == nil {
		x = z.left
		t.rbtransplant(z, z.left)
	} else {
		y = z.right.min()
		y_original_isred, x = y.isred, y.right
		if y.parent == z {
			if x == nil {
				parent = y
			} else {
				x.parent = y
			}
		} else {
			t.rbtransplant(y, y.right)
			y.right = z.right
			y.right.parent = y
		}
		t.rbtransplant(z, y)
		y.left = z.left
		y.left.parent, y.isred = y, z.isred
	}
	if !y_original_isred && x != nil {
		t.rb_delete_fixup(x, parent)
	}
	t.size--
}

func (t *RbTree) rb_insert_fixup(x *RbTreeNode) {
	var y *RbTreeNode
	for isRed(x.parent) {
		if x.parent == x.parent.parent.left {
			y = x.parent.parent.right
			if isRed(y) {
				x.parent.isred, y.isred = false, false
				x.parent.parent.isred = true
				x = x.parent.parent
			} else {
				if x == x.parent.right {
					x = x.parent
					t.left_rotate(x)
				}
				x.parent.isred, x.parent.parent.isred = false, true
				t.right_rotate(x.parent.parent)
			}
		} else {
			y = x.parent.parent.left
			if isRed(y) {
				x.parent.isred, y.isred = false, false
				x.parent.parent.isred = true
				x = x.parent.parent
			} else {
				if x == x.parent.left {
					x = x.parent
					t.right_rotate(x)
				}
				x.parent.isred, x.parent.parent.isred = false, true
				t.left_rotate(x.parent.parent)
			}
		}
	}
	t.root.isred = false
}

func (t *RbTree) rb_delete_fixup(x, parent *RbTreeNode) {
	var w *RbTreeNode
	for x != t.root && isBlack(x) {
		if x == parent.left {
			w = parent.right
			if isRed(w) {
				w.isred, parent.isred = false, true
				t.left_rotate(parent)
				w = parent.right
			}
			if w != nil && isBlack(w.left) && isBlack(w.right) {
				w.isred = true
				x = parent
                parent = x.parent
			} else {
				if w != nil && isBlack(w.right) {
					if w.left != nil {
						w.left.isred = false
					}
					w.isred = true
					t.right_rotate(w)
					w = parent.right
				}
                if w != nil {
				    w.isred = parent.isred
				    if w.right != nil {
                        w.right.isred = false
                    }
                }
                parent.isred = false
				t.left_rotate(parent)
				x = t.root
			}
		} else {
			w := parent.left
			if isRed(w) {
				w.isred, parent.isred = false, true
				t.right_rotate(parent)
				w = parent.left
			}
			if w != nil && isBlack(w.left) && isBlack(w.right) {
				w.isred = true
				x = parent
                parent = x.parent
			} else {
				if w != nil && isBlack(w.left) {
                    w.isred = true
					if w.right != nil {
						w.right.isred = false
					}
					t.left_rotate(w)
					w = parent.left
				}
                if w != nil {
				    w.isred = parent.isred
    				if w.left != nil {
	       				w.left.isred = false
			     	}
                }
                parent.isred = false
				t.right_rotate(parent)
				x = t.root
			}
		}
	}
	if x != nil {
		x.isred = false
	}
}

func (n *RbTreeNode) min() *RbTreeNode {
	for n.left != nil {
		n = n.left
	}
	return n
}

func (n *RbTreeNode) max() *RbTreeNode {
	for n.right != nil {
		n = n.right
	}
	return n
}

func (t *RbTree) left_rotate(x *RbTreeNode) {
	y := x.right
	x.right = y.left
	if y.left != nil {
		y.left.parent = x
	}
	y.parent = x.parent
	if x.parent == nil {
		t.root = y
	} else {
        if x == x.parent.left {
		  x.parent.left = y
	   } else {
		  x.parent.right = y
	   }
    }
	y.left, x.parent = x, y
}

func (t *RbTree) right_rotate(y *RbTreeNode) {
	x := y.left
	y.left = x.right
	if x.right != nil {
		x.right.parent = y
	}
	x.parent = y.parent
	if y.parent == nil {
		t.root = x
	} else {
        if y == y.parent.left {
		  y.parent.left = x
	   } else {
		  y.parent.right = x
	   }
    }
	x.right, y.parent = y, x
}

func (t *RbTree) rbtransplant(u, v *RbTreeNode) {
	if u.parent == nil {
		t.root = v
	} else if u == u.parent.left {
		u.parent.left = v
	} else {
		u.parent.right = v
	}
	if v == nil {
		return
	}
	v.parent = u.parent
}

func isBlack(n *RbTreeNode) bool {
	return nil == n || !n.isred
}

func isRed(n *RbTreeNode) bool {
	return nil != n && n.isred
}

/*
// uncomment for debugging only
func (n *RbTreeNode) Dump(indent int, tag string) {
	idn := strings.Repeat(" ", indent*4)
	fmt.Printf("%s%s[%v:%v]%d\n", idn, tag, n.Key, n.Value, n.isred)
	if n.left != nil {
		n.left.Dump(indent + 1, "L:")
	}
	if n.right != nil {
		n.right.Dump(indent + 1, "R:")
	}
}

func (t *RbTree) Dump() {
	if t.root == nil {
		fmt.Printf("<NULL TREE>\n")
	} else {
		t.root.Dump(0, "*")
	}
}
*/<commit_msg>cosmetic changes<commit_after>// Red-Black tree implementation. Each tree node (entry) contains Key and Value.
// Entries in the RbTree are always ordered according to the key value, so
// it can be used as ordered set (std::set in C++) or ordered map (std::map).
// Note: all methods are not goroutine-safe.
package rbt

type RbTree struct {
    less       LessFunc
    root       *RbTreeNode
    size       int
}

// Red-black tree node, contains key and value. It is safe to overwrite Value
// in-place.
type RbTreeNode struct {
    left, right, parent *RbTreeNode
    // key
    key          interface{}
    Value        interface{}
    isred        bool         // true == red, false == black
}

// LessFunc is a key comparsion function.
// Must return true if k1 < k2, false otherwise
type LessFunc func(k1, k2 interface{}) bool

// Create new RbTree with provided comparsion function. The latter must
// return true if k1 < k2, false otherwise.
func NewRbTree(lessFunc LessFunc) *RbTree {
    return &RbTree{ less: lessFunc }
}

// Find node by key and return its Value, returns nil if key not found.
func (t *RbTree) Find(key interface{}) interface{} {
    n := t.FindNode(key)
    if n != nil {
        return n.Value
    }
    return nil
}

// Find a node by key, returns nil if not found
func (t *RbTree) FindNode(key interface{}) *RbTreeNode {
    x := t.root
    for x != nil {
        if t.less(x.key, key) {
            x = x.left
        } else {
            if t.less(key, x.key) {
                x = x.right
            } else {
                return x
            }
        }
    }
    return nil
}

// Get last node in the tree (with highest key value).
func (t *RbTree) Last() *RbTreeNode {
    if nil == t.root {
        return nil
    }
    return t.root.min()
}

// Get first node in the tree (with lowest key value).
func (t *RbTree) First() *RbTreeNode {
    if nil == t.root {
        return nil
    }
    return t.root.max()
}

// Returns previous node in the tree, in descending key value order.
func (x *RbTreeNode) Prev() *RbTreeNode {
    if x.right != nil {
        return x.right.min()
    }
    y := x.parent
    for y != nil && x == y.right {
        x = y
        y = y.parent
    }
    return y
}

// Returns key associated with tree node.
func (x *RbTreeNode) Key() interface{} {
    return x.key
}

// Returns next node in the tree, in ascending key value order.
func (x *RbTreeNode) Next() *RbTreeNode {
    if x.left != nil {
        return x.left.max()
    }
    y := x.parent
    for y != nil && x == y.left {
        x = y
        y = y.parent
    }
    return y
}

// Returns number of entries in the tree. This function returns internal
// counter, therefore it is fast and safe to use in loops.
func (t *RbTree) Size() int {
    return t.size
}

// Remove all entries in the tree.
func (t *RbTree) Clear() {
    t.root = nil
    t.size = 0
}

// Insert key and value into the tree. If new entry is created, returns true.
// If key already exists, value gets replaced and Insert returns false.
func (t *RbTree) Insert(key interface{}, value interface{}) bool {
    x := t.root
    var y *RbTreeNode

    for x != nil {
        y = x
        if t.less(x.key, key) {
            x = x.left
        } else if t.less(key, x.key) {
            x = x.right
        } else {
            x.Value = value
            return false // overwrite value
        }
    }
    z := &RbTreeNode{parent: y, isred: true, key: key, Value: value}
    if y == nil {
        t.root = z
    } else {
        if t.less(y.key, key) {
            y.left = z
        } else {
            y.right = z
        }
    }
    t.rb_insert_fixup(z)
    t.size++
    return true
}

// Delete tree node by key.
func (t *RbTree) Delete(key interface{}) {
    if z := t.FindNode(key); z != nil {
        t.DeleteNode(z)
    }
}

// Delete tree node.
func (t *RbTree) DeleteNode(z *RbTreeNode) {
    var x, y, parent *RbTreeNode
    y, y_original_isred, parent := z, z.isred, z.parent
    if z.left == nil {
        x = z.right
        t.rbtransplant(z, z.right)
    } else if z.right == nil {
        x = z.left
        t.rbtransplant(z, z.left)
    } else {
        y = z.right.min()
        y_original_isred, x = y.isred, y.right
        if y.parent == z {
            if x == nil {
                parent = y
            } else {
                x.parent = y
            }
        } else {
            t.rbtransplant(y, y.right)
            y.right = z.right
            y.right.parent = y
        }
        t.rbtransplant(z, y)
        y.left = z.left
        y.left.parent, y.isred = y, z.isred
    }
    if !y_original_isred && x != nil {
        t.rb_delete_fixup(x, parent)
    }
    t.size--
}

func (t *RbTree) rb_insert_fixup(x *RbTreeNode) {
    var y *RbTreeNode
    for isRed(x.parent) {
        if x.parent == x.parent.parent.left {
            y = x.parent.parent.right
            if isRed(y) {
                x.parent.isred, y.isred = false, false
                x.parent.parent.isred = true
                x = x.parent.parent
            } else {
                if x == x.parent.right {
                    x = x.parent
                    t.left_rotate(x)
                }
                x.parent.isred, x.parent.parent.isred = false, true
                t.right_rotate(x.parent.parent)
            }
        } else {
            y = x.parent.parent.left
            if isRed(y) {
                x.parent.isred, y.isred = false, false
                x.parent.parent.isred = true
                x = x.parent.parent
            } else {
                if x == x.parent.left {
                    x = x.parent
                    t.right_rotate(x)
                }
                x.parent.isred, x.parent.parent.isred = false, true
                t.left_rotate(x.parent.parent)
            }
        }
    }
    t.root.isred = false
}

func (t *RbTree) rb_delete_fixup(x, parent *RbTreeNode) {
    var w *RbTreeNode
    for x != t.root && isBlack(x) {
        if x == parent.left {
            w = parent.right
            if isRed(w) {
                w.isred, parent.isred = false, true
                t.left_rotate(parent)
                w = parent.right
            }
            if w != nil && isBlack(w.left) && isBlack(w.right) {
                w.isred = true
                x = parent
                parent = x.parent
            } else {
                if w != nil && isBlack(w.right) {
                    if w.left != nil {
                        w.left.isred = false
                    }
                    w.isred = true
                    t.right_rotate(w)
                    w = parent.right
                }
                if w != nil {
                    w.isred = parent.isred
                    if w.right != nil {
                        w.right.isred = false
                    }
                }
                parent.isred = false
                t.left_rotate(parent)
                x = t.root
            }
        } else {
            w := parent.left
            if isRed(w) {
                w.isred, parent.isred = false, true
                t.right_rotate(parent)
                w = parent.left
            }
            if w != nil && isBlack(w.left) && isBlack(w.right) {
                w.isred = true
                x = parent
                parent = x.parent
            } else {
                if w != nil && isBlack(w.left) {
                    w.isred = true
                    if w.right != nil {
                        w.right.isred = false
                    }
                    t.left_rotate(w)
                    w = parent.left
                }
                if w != nil {
                    w.isred = parent.isred
                    if w.left != nil {
                        w.left.isred = false
                    }
                }
                parent.isred = false
                t.right_rotate(parent)
                x = t.root
            }
        }
    }
    if x != nil {
        x.isred = false
    }
}

func (n *RbTreeNode) min() *RbTreeNode {
    for n.left != nil {
        n = n.left
    }
    return n
}

func (n *RbTreeNode) max() *RbTreeNode {
    for n.right != nil {
        n = n.right
    }
    return n
}

func (t *RbTree) left_rotate(x *RbTreeNode) {
    y := x.right
    x.right = y.left
    if y.left != nil {
        y.left.parent = x
    }
    y.parent = x.parent
    if x.parent == nil {
        t.root = y
    } else {
        if x == x.parent.left {
          x.parent.left = y
       } else {
          x.parent.right = y
       }
    }
    y.left, x.parent = x, y
}

func (t *RbTree) right_rotate(y *RbTreeNode) {
    x := y.left
    y.left = x.right
    if x.right != nil {
        x.right.parent = y
    }
    x.parent = y.parent
    if y.parent == nil {
        t.root = x
    } else {
        if y == y.parent.left {
          y.parent.left = x
       } else {
          y.parent.right = x
       }
    }
    x.right, y.parent = y, x
}

func (t *RbTree) rbtransplant(u, v *RbTreeNode) {
    if u.parent == nil {
        t.root = v
    } else if u == u.parent.left {
        u.parent.left = v
    } else {
        u.parent.right = v
    }
    if v == nil {
        return
    }
    v.parent = u.parent
}

func isBlack(n *RbTreeNode) bool {
    return nil == n || !n.isred
}

func isRed(n *RbTreeNode) bool {
    return nil != n && n.isred
}

/*
// uncomment for debugging only
func (n *RbTreeNode) Dump(indent int, tag string) {
    idn := strings.Repeat(" ", indent*4)
    fmt.Printf("%s%s[%v:%v]%d\n", idn, tag, n.Key, n.Value, n.isred)
    if n.left != nil {
        n.left.Dump(indent + 1, "L:")
    }
    if n.right != nil {
        n.right.Dump(indent + 1, "R:")
    }
}

func (t *RbTree) Dump() {
    if t.root == nil {
        fmt.Printf("<NULL TREE>\n")
    } else {
        t.root.Dump(0, "*")
    }
}
*/<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/url"
	"os"
	"strconv"
	"time"
)

var commands = map[string]struct {
	nargs  int
	f      func(url string, args []string)
	argstr string
}{
	"upload":  {-1, uploadCommand, "/src/dir /dest/dir"},
	"ls":      {0, lsCommand, "[path]"},
	"rm":      {0, rmCommand, "path"},
	"getconf": {0, getConfCommand, ""},
	"setconf": {2, setConfCommand, "prop value"},
}

func init() {
	log.SetFlags(log.Lmicroseconds)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
			"Usage of %s cmd http://cbfs:8484/ [-opts] cmdargs\n",
			os.Args[0])

		fmt.Fprintf(os.Stderr, "\nCommands:\n")

		for k, v := range commands {
			fmt.Fprintf(os.Stderr, "  %s %s\n", k, v.argstr)
		}

		fmt.Fprintf(os.Stderr, "\n---- Subcommannd Options ----\n")

		fmt.Fprintf(os.Stderr, "\nls:\n")
		lsFlags.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nrm:\n")
		rmFlags.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nupload:\n")
		uploadFlags.PrintDefaults()
		os.Exit(1)
	}

}

func relativeUrl(u, path string) string {
	du, err := url.Parse(u)
	if err != nil {
		log.Fatalf("Error parsing url: %v", err)
	}

	du.Path = path
	if du.Path[0] != '/' {
		du.Path = "/" + du.Path
	}

	return du.String()
}

func parseDuration(s string) time.Duration {
	d, err := time.ParseDuration(s)
	if err != nil {
		log.Fatalf("Unable to parse duration: %v", err)
	}
	return d
}

func parseInt(s string) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		log.Fatalf("Error parsing int: %v", err)
	}
	return i
}

func main() {
	flag.Parse()

	if flag.NArg() < 2 {
		flag.Usage()
	}

	u := flag.Arg(0)

	cmdName := flag.Arg(1)
	cmd, ok := commands[cmdName]
	if !ok {
		fmt.Fprintf(os.Stderr, "Unknown command: %v\n", cmdName)
		flag.Usage()
	}
	if cmd.nargs == 0 {
	} else if cmd.nargs < 0 {
		reqargs := -cmd.nargs
		if flag.NArg()-2 < reqargs {
			fmt.Fprintf(os.Stderr, "Incorrect arguments for %v\n", cmdName)
			flag.Usage()
		}
	} else {
		if flag.NArg()-2 != cmd.nargs {
			fmt.Fprintf(os.Stderr, "Incorrect arguments for %v\n", cmdName)
			flag.Usage()
		}
	}

	cmd.f(u, flag.Args()[2:])
}
<commit_msg>More accurate help text for cbfsclient.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/url"
	"os"
	"strconv"
	"time"
)

var commands = map[string]struct {
	nargs  int
	f      func(url string, args []string)
	argstr string
}{
	"upload":  {-1, uploadCommand, "/src/dir /dest/dir"},
	"ls":      {0, lsCommand, "[path]"},
	"rm":      {0, rmCommand, "path"},
	"getconf": {0, getConfCommand, ""},
	"setconf": {2, setConfCommand, "prop value"},
}

func init() {
	log.SetFlags(log.Lmicroseconds)

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr,
			"Usage:\n  %s http://cbfs:8484/ cmd [-opts] cmdargs\n",
			os.Args[0])

		fmt.Fprintf(os.Stderr, "\nCommands:\n")

		for k, v := range commands {
			fmt.Fprintf(os.Stderr, "  %s %s\n", k, v.argstr)
		}

		fmt.Fprintf(os.Stderr, "\n---- Subcommannd Options ----\n")

		fmt.Fprintf(os.Stderr, "\nls:\n")
		lsFlags.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nrm:\n")
		rmFlags.PrintDefaults()
		fmt.Fprintf(os.Stderr, "\nupload:\n")
		uploadFlags.PrintDefaults()
		os.Exit(1)
	}

}

func relativeUrl(u, path string) string {
	du, err := url.Parse(u)
	if err != nil {
		log.Fatalf("Error parsing url: %v", err)
	}

	du.Path = path
	if du.Path[0] != '/' {
		du.Path = "/" + du.Path
	}

	return du.String()
}

func parseDuration(s string) time.Duration {
	d, err := time.ParseDuration(s)
	if err != nil {
		log.Fatalf("Unable to parse duration: %v", err)
	}
	return d
}

func parseInt(s string) int {
	i, err := strconv.Atoi(s)
	if err != nil {
		log.Fatalf("Error parsing int: %v", err)
	}
	return i
}

func main() {
	flag.Parse()

	if flag.NArg() < 2 {
		flag.Usage()
	}

	u := flag.Arg(0)

	cmdName := flag.Arg(1)
	cmd, ok := commands[cmdName]
	if !ok {
		fmt.Fprintf(os.Stderr, "Unknown command: %v\n", cmdName)
		flag.Usage()
	}
	if cmd.nargs == 0 {
	} else if cmd.nargs < 0 {
		reqargs := -cmd.nargs
		if flag.NArg()-2 < reqargs {
			fmt.Fprintf(os.Stderr, "Incorrect arguments for %v\n", cmdName)
			flag.Usage()
		}
	} else {
		if flag.NArg()-2 != cmd.nargs {
			fmt.Fprintf(os.Stderr, "Incorrect arguments for %v\n", cmdName)
			flag.Usage()
		}
	}

	cmd.f(u, flag.Args()[2:])
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2016 Laurent Moussault. All rights reserved.
// Licensed under a simplified BSD license (see LICENSE file).

package glam

//------------------------------------------------------------------------------

import (
	"fmt"
	"os"

	"errors"
	"github.com/drakmaniso/glam/basic"
	"github.com/drakmaniso/glam/gfx"
	"github.com/drakmaniso/glam/internal"
	"github.com/drakmaniso/glam/internal/events"
	"github.com/drakmaniso/glam/internal/microtext"
	"github.com/drakmaniso/glam/key"
	"github.com/drakmaniso/glam/mouse"
	"github.com/drakmaniso/glam/pixel"
	"github.com/drakmaniso/glam/window"
)

//------------------------------------------------------------------------------

func Setup() {
	s := pixel.Coord{internal.Window.Width, internal.Window.Height}
	gfx.Viewport(pixel.Coord{X: 0, Y: 0}, s)

	// Setup mtx
	microtext.Setup()
	microtext.WindowResized(s, 0)

	isSetUp = true

	//TODO: error handling?
}

var isSetUp bool

//------------------------------------------------------------------------------

// A Looper implements a game loop.
type Looper interface {
	Update(t, dt float64)
	Draw(interpolation float64)
}

// Loop is the current game loop.
//
// It can be changed while the loop is running, but must never be nil.
var Loop Looper

//------------------------------------------------------------------------------

// Run opens the game window and runs the Loop, until Stop() is called.
//
// Important: must be called from main.main, or at least from a function that is
// known to run on the main OS thread.
func Run() error {
	if internal.InitError != nil {
		return internal.InitError
	}
	defer internal.SDLQuit()
	defer internal.DestroyWindow()

	if !isSetUp {
		return errors.New("glam.Setup must be called before glam.Run")
	}

	// Setup Fallback Handlers
	if window.Handle == nil {
		window.Handle = basic.WindowHandler{}
	}
	if mouse.Handle == nil {
		mouse.Handle = basic.MouseHandler{}
	}
	if key.Handle == nil {
		key.Handle = basic.KeyHandler{}
	}

	// Process events once before the first time step
	{
		s := pixel.Coord{internal.Window.Width, internal.Window.Height}
		window.Handle.WindowResized(s, 0)
		events.Process()
	}

	// Main Loop

	then := internal.GetSeconds()
	stepNow := then
	remain := 0.0

	for !internal.QuitRequested {
		now = internal.GetSeconds()
		frameTime = now - then

		// Compute smoothed frame time
		ftSmoothed = (ftSmoothed * ftSmoothing) + frameTime*(1.0-ftSmoothing)
		// Compute average frame time
		ftCount++
		ftSum += frameTime
		if frameTime > xrunThreshold {
			xrunCount++
		}
		if ftSum >= ftInterval {
			ftAverage = ftSum / float64(ftCount)
			xrunPrevious = xrunCount
			microtext.PrintFrameTime(ftAverage, xrunCount)
			ftSum = 0
			ftCount = 0
			xrunCount = 0
		}

		//TODO: clamp frameTime ?

		// Fixed time step for logic and physics updates
		remain += frameTime
		for remain >= TimeStep {
			remain -= TimeStep
			stepNow += TimeStep
			events.Process()
			Loop.Update(stepNow, TimeStep)
		}

		Loop.Draw(remain / TimeStep)
		microtext.Draw()
		internal.SwapWindow()

		then = now
	}
	return nil
}

// now is the current time
var now float64

// TimeStep is the fixed interval between each call to Update.
var TimeStep = 1.0 / 50.0

//------------------------------------------------------------------------------

var frameTime float64

// FrameTime returns the duration of the last frame
func FrameTime() float64 {
	return frameTime
}

// AverageFrameTime returns the average durations of frames; it is updated 4
// times per second.
func AverageFrameTime() float64 {
	return ftAverage
}

var ftAverage float64
var ftSum float64
var ftCount int

const ftInterval = 1.0 / 4.0

// Overruns returns the number of overruns (i.e. frame time longer than the
// threshold) during the last measurment interval.
func Overruns() int {
	return xrunPrevious
}

var xrunCount, xrunPrevious int

const xrunThreshold float64 = 17 / 1000.0

// SmoothedFrameTime returns the frame time smoothed over time.
func SmoothedFrameTime() float64 {
	return ftSmoothed
}

var ftSmoothing = 0.995
var ftSmoothed float64

//------------------------------------------------------------------------------

// Stop request the game loop to stop.
func Stop() {
	internal.QuitRequested = true
}

//------------------------------------------------------------------------------

// Path returns the executable path.
func Path() string {
	return internal.Path
}

//------------------------------------------------------------------------------

// ErrorDialog displays an error dialog box.
func ErrorDialog(e error) {
	internal.Log("ErrorDialog: %s", e)
	err := internal.ErrorDialog(e.Error())
	if err != nil {
		fmt.Fprintln(os.Stderr, e.Error())
	}
}

//------------------------------------------------------------------------------
<commit_msg>Fix event processing timing<commit_after>// Copyright (c) 2013-2016 Laurent Moussault. All rights reserved.
// Licensed under a simplified BSD license (see LICENSE file).

package glam

//------------------------------------------------------------------------------

import (
	"fmt"
	"os"

	"errors"

	"github.com/drakmaniso/glam/basic"
	"github.com/drakmaniso/glam/gfx"
	"github.com/drakmaniso/glam/internal"
	"github.com/drakmaniso/glam/internal/events"
	"github.com/drakmaniso/glam/internal/microtext"
	"github.com/drakmaniso/glam/key"
	"github.com/drakmaniso/glam/mouse"
	"github.com/drakmaniso/glam/pixel"
	"github.com/drakmaniso/glam/window"
)

//------------------------------------------------------------------------------

func Setup() {
	s := pixel.Coord{internal.Window.Width, internal.Window.Height}
	gfx.Viewport(pixel.Coord{X: 0, Y: 0}, s)

	// Setup mtx
	microtext.Setup()
	microtext.WindowResized(s, 0)

	isSetUp = true

	//TODO: error handling?
}

var isSetUp bool

//------------------------------------------------------------------------------

// A Looper implements a game loop.
type Looper interface {
	Update(t, dt float64)
	Draw(interpolation float64)
}

// Loop is the current game loop.
//
// It can be changed while the loop is running, but must never be nil.
var Loop Looper

//------------------------------------------------------------------------------

// Run opens the game window and runs the Loop, until Stop() is called.
//
// Important: must be called from main.main, or at least from a function that is
// known to run on the main OS thread.
func Run() error {
	if internal.InitError != nil {
		return internal.InitError
	}
	defer internal.SDLQuit()
	defer internal.DestroyWindow()

	if !isSetUp {
		return errors.New("glam.Setup must be called before glam.Run")
	}

	// Setup Fallback Handlers
	if window.Handle == nil {
		window.Handle = basic.WindowHandler{}
	}
	if mouse.Handle == nil {
		mouse.Handle = basic.MouseHandler{}
	}
	if key.Handle == nil {
		key.Handle = basic.KeyHandler{}
	}

	// First, send a fake resize window event
	{
		s := pixel.Coord{internal.Window.Width, internal.Window.Height}
		window.Handle.WindowResized(s, 0)
	}

	// Main Loop

	then := internal.GetSeconds()
	stepNow := then
	remain := 0.0

	for !internal.QuitRequested {
		now = internal.GetSeconds()
		frameTime = now - then

		// Compute smoothed frame time
		ftSmoothed = (ftSmoothed * ftSmoothing) + frameTime*(1.0-ftSmoothing)
		// Compute average frame time
		ftCount++
		ftSum += frameTime
		if frameTime > xrunThreshold {
			xrunCount++
		}
		if ftSum >= ftInterval {
			ftAverage = ftSum / float64(ftCount)
			xrunPrevious = xrunCount
			microtext.PrintFrameTime(ftAverage, xrunCount)
			ftSum = 0
			ftCount = 0
			xrunCount = 0
		}

		//TODO: clamp frameTime ?

		// Process events
		events.Process()

		// Fixed time step for logic and physics updates
		remain += frameTime
		for remain >= TimeStep {
			remain -= TimeStep
			stepNow += TimeStep
			Loop.Update(stepNow, TimeStep)
		}

		Loop.Draw(remain / TimeStep)
		microtext.Draw()
		internal.SwapWindow()

		then = now
	}
	return nil
}

// now is the current time
var now float64

// TimeStep is the fixed interval between each call to Update.
var TimeStep = 1.0 / 50.0

//------------------------------------------------------------------------------

var frameTime float64

// FrameTime returns the duration of the last frame
func FrameTime() float64 {
	return frameTime
}

// AverageFrameTime returns the average durations of frames; it is updated 4
// times per second.
func AverageFrameTime() float64 {
	return ftAverage
}

var ftAverage float64
var ftSum float64
var ftCount int

const ftInterval = 1.0 / 4.0

// Overruns returns the number of overruns (i.e. frame time longer than the
// threshold) during the last measurment interval.
func Overruns() int {
	return xrunPrevious
}

var xrunCount, xrunPrevious int

const xrunThreshold float64 = 17 / 1000.0

// SmoothedFrameTime returns the frame time smoothed over time.
func SmoothedFrameTime() float64 {
	return ftSmoothed
}

var ftSmoothing = 0.995
var ftSmoothed float64

//------------------------------------------------------------------------------

// Stop request the game loop to stop.
func Stop() {
	internal.QuitRequested = true
}

//------------------------------------------------------------------------------

// Path returns the executable path.
func Path() string {
	return internal.Path
}

//------------------------------------------------------------------------------

// ErrorDialog displays an error dialog box.
func ErrorDialog(e error) {
	internal.Log("ErrorDialog: %s", e)
	err := internal.ErrorDialog(e.Error())
	if err != nil {
		fmt.Fprintln(os.Stderr, e.Error())
	}
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>// Copyright 2013 bee 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 main

import (
	"io/ioutil"
	"os"
	path "path/filepath"
	"runtime"
	"strings"
)

var cmdRun = &Command{
	UsageLine: "run [appname] [watchall] [-main=*.go] [-downdoc=true]  [-gendoc=true] [-vendor=true] [-e=folderToExclude]  [-tags=goBuildTags] [-runmode=BEEGO_RUNMODE]",
	Short:     "Run the application by starting a local development server",
	Long: `
Run command will supervise the filesystem of the application for any changes, and recompile/restart it.

`,
	PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
	Run:    runApp,
}

var (
	mainFiles ListOpts
	downdoc   docValue
	gendoc    docValue
	// The flags list of the paths excluded from watching
	excludedPaths strFlags
	// Pass through to -tags arg of "go build"
	buildTags string
	// Application path
	currpath string
	// Application name
	appname string
	// Channel to signal an Exit
	exit chan bool
	// Flag to watch the vendor folder
	vendorWatch bool
	// Current user workspace
	currentGoPath string
	// Current runmode
	runmode string
)

func init() {
	cmdRun.Flag.Var(&mainFiles, "main", "Specify main go files.")
	cmdRun.Flag.Var(&gendoc, "gendoc", "Enable auto-generate the docs.")
	cmdRun.Flag.Var(&downdoc, "downdoc", "Enable auto-download of the swagger file if it does not exist.")
	cmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.")
	cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.")
	cmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/")
	cmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.")
	exit = make(chan bool)
}

func runApp(cmd *Command, args []string) int {
	if len(args) == 0 || args[0] == "watchall" {
		currpath, _ = os.Getwd()

		if found, _gopath, _ := SearchGOPATHs(currpath); found {
			appname = path.Base(currpath)
			currentGoPath = _gopath
		} else {
			logger.Fatalf("No application '%s' found in your GOPATH", currpath)
		}
	} else {
		// Check if passed Bee application path/name exists in the GOPATH(s)
		if found, _gopath, _path := SearchGOPATHs(args[0]); found {
			currpath = _path
			currentGoPath = _gopath
			appname = path.Base(currpath)
		} else {
			logger.Fatalf("No application '%s' found in your GOPATH", args[0])
		}

		if strings.HasSuffix(appname, ".go") && isExist(currpath) {
			logger.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname)
			logger.Info("Do you want to overwrite it? [yes|no] ")
			if !askForConfirmation() {
				return 0
			}
		}
	}

	logger.Infof("Using '%s' as 'appname'", appname)

	logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath)

	if runmode == "prod" || runmode == "dev" {
		os.Setenv("BEEGO_RUNMODE", runmode)
		logger.Infof("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
	} else if runmode != "" {
		os.Setenv("BEEGO_RUNMODE", runmode)
		logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
	} else if os.Getenv("BEEGO_RUNMODE") != "" {
		logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
	}

	err := loadConfig()
	if err != nil {
		logger.Fatalf("Failed to load configuration: %s", err)
	}

	var paths []string
	readAppDirectories(currpath, &paths)

	// Because monitor files has some issues, we watch current directory
	// and ignore non-go files.
	for _, p := range conf.DirStruct.Others {
		paths = append(paths, strings.Replace(p, "$GOPATH", currentGoPath, -1))
	}

	files := []string{}
	for _, arg := range mainFiles {
		if len(arg) > 0 {
			files = append(files, arg)
		}
	}
	if downdoc == "true" {
		if _, err := os.Stat(path.Join(currpath, "swagger", "index.html")); err != nil {
			if os.IsNotExist(err) {
				downloadFromURL(swaggerlink, "swagger.zip")
				unzipAndDelete("swagger.zip")
			}
		}
	}
	if gendoc == "true" {
		NewWatcher(paths, files, true)
		AutoBuild(files, true)
	} else {
		NewWatcher(paths, files, false)
		AutoBuild(files, false)
	}

	for {
		select {
		case <-exit:
			runtime.Goexit()
		}
	}
}

func readAppDirectories(directory string, paths *[]string) {
	fileInfos, err := ioutil.ReadDir(directory)
	if err != nil {
		return
	}

	useDirectory := false
	for _, fileInfo := range fileInfos {
		if strings.HasSuffix(fileInfo.Name(), "docs") {
			continue
		}
		if strings.HasSuffix(fileInfo.Name(), "swagger") {
			continue
		}

		if !vendorWatch && strings.HasSuffix(fileInfo.Name(), "vendor") {
			continue
		}

		if isExcluded(path.Join(directory, fileInfo.Name())) {
			continue
		}

		if fileInfo.IsDir() == true && fileInfo.Name()[0] != '.' {
			readAppDirectories(directory+"/"+fileInfo.Name(), paths)
			continue
		}

		if useDirectory == true {
			continue
		}

		if path.Ext(fileInfo.Name()) == ".go" {
			*paths = append(*paths, directory)
			useDirectory = true
		}
	}
	return
}

// If a file is excluded
func isExcluded(filePath string) bool {
	for _, p := range excludedPaths {
		absP, err := path.Abs(p)
		if err != nil {
			logger.Errorf("Cannot get absolute path of '%s'", p)
			continue
		}
		absFilePath, err := path.Abs(filePath)
		if err != nil {
			logger.Errorf("Cannot get absolute path of '%s'", filePath)
			break
		}
		if strings.HasPrefix(absFilePath, absP) {
			logger.Infof("'%s' is not being watched", filePath)
			return true
		}
	}
	return false
}
<commit_msg>Start the Reload server (if enabled)<commit_after>// Copyright 2013 bee 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 main

import (
	"io/ioutil"
	"os"
	path "path/filepath"
	"runtime"
	"strings"
)

var cmdRun = &Command{
	UsageLine: "run [appname] [watchall] [-main=*.go] [-downdoc=true]  [-gendoc=true] [-vendor=true] [-e=folderToExclude]  [-tags=goBuildTags] [-runmode=BEEGO_RUNMODE]",
	Short:     "Run the application by starting a local development server",
	Long: `
Run command will supervise the filesystem of the application for any changes, and recompile/restart it.

`,
	PreRun: func(cmd *Command, args []string) { ShowShortVersionBanner() },
	Run:    runApp,
}

var (
	mainFiles ListOpts
	downdoc   docValue
	gendoc    docValue
	// The flags list of the paths excluded from watching
	excludedPaths strFlags
	// Pass through to -tags arg of "go build"
	buildTags string
	// Application path
	currpath string
	// Application name
	appname string
	// Channel to signal an Exit
	exit chan bool
	// Flag to watch the vendor folder
	vendorWatch bool
	// Current user workspace
	currentGoPath string
	// Current runmode
	runmode string
)

func init() {
	cmdRun.Flag.Var(&mainFiles, "main", "Specify main go files.")
	cmdRun.Flag.Var(&gendoc, "gendoc", "Enable auto-generate the docs.")
	cmdRun.Flag.Var(&downdoc, "downdoc", "Enable auto-download of the swagger file if it does not exist.")
	cmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.")
	cmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.")
	cmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/")
	cmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.")
	exit = make(chan bool)
}

func runApp(cmd *Command, args []string) int {
	if len(args) == 0 || args[0] == "watchall" {
		currpath, _ = os.Getwd()

		if found, _gopath, _ := SearchGOPATHs(currpath); found {
			appname = path.Base(currpath)
			currentGoPath = _gopath
		} else {
			logger.Fatalf("No application '%s' found in your GOPATH", currpath)
		}
	} else {
		// Check if passed Bee application path/name exists in the GOPATH(s)
		if found, _gopath, _path := SearchGOPATHs(args[0]); found {
			currpath = _path
			currentGoPath = _gopath
			appname = path.Base(currpath)
		} else {
			logger.Fatalf("No application '%s' found in your GOPATH", args[0])
		}

		if strings.HasSuffix(appname, ".go") && isExist(currpath) {
			logger.Warnf("The appname is in conflict with file's current path. Do you want to build appname as '%s'", appname)
			logger.Info("Do you want to overwrite it? [yes|no] ")
			if !askForConfirmation() {
				return 0
			}
		}
	}

	logger.Infof("Using '%s' as 'appname'", appname)

	logger.Debugf("Current path: %s", __FILE__(), __LINE__(), currpath)

	if runmode == "prod" || runmode == "dev" {
		os.Setenv("BEEGO_RUNMODE", runmode)
		logger.Infof("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
	} else if runmode != "" {
		os.Setenv("BEEGO_RUNMODE", runmode)
		logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
	} else if os.Getenv("BEEGO_RUNMODE") != "" {
		logger.Warnf("Using '%s' as 'runmode'", os.Getenv("BEEGO_RUNMODE"))
	}

	err := loadConfig()
	if err != nil {
		logger.Fatalf("Failed to load configuration: %s", err)
	}

	var paths []string
	readAppDirectories(currpath, &paths)

	// Because monitor files has some issues, we watch current directory
	// and ignore non-go files.
	for _, p := range conf.DirStruct.Others {
		paths = append(paths, strings.Replace(p, "$GOPATH", currentGoPath, -1))
	}

	files := []string{}
	for _, arg := range mainFiles {
		if len(arg) > 0 {
			files = append(files, arg)
		}
	}
	if downdoc == "true" {
		if _, err := os.Stat(path.Join(currpath, "swagger", "index.html")); err != nil {
			if os.IsNotExist(err) {
				downloadFromURL(swaggerlink, "swagger.zip")
				unzipAndDelete("swagger.zip")
			}
		}
	}
	if gendoc == "true" {
		NewWatcher(paths, files, true)
		AutoBuild(files, true)
	} else {
		NewWatcher(paths, files, false)
		AutoBuild(files, false)
	}

	// Start the Reload server (if enabled)
	if conf.EnableReload {
		startReloadServer()
	}

	for {
		select {
		case <-exit:
			runtime.Goexit()
		}
	}
}

func readAppDirectories(directory string, paths *[]string) {
	fileInfos, err := ioutil.ReadDir(directory)
	if err != nil {
		return
	}

	useDirectory := false
	for _, fileInfo := range fileInfos {
		if strings.HasSuffix(fileInfo.Name(), "docs") {
			continue
		}
		if strings.HasSuffix(fileInfo.Name(), "swagger") {
			continue
		}

		if !vendorWatch && strings.HasSuffix(fileInfo.Name(), "vendor") {
			continue
		}

		if isExcluded(path.Join(directory, fileInfo.Name())) {
			continue
		}

		if fileInfo.IsDir() == true && fileInfo.Name()[0] != '.' {
			readAppDirectories(directory+"/"+fileInfo.Name(), paths)
			continue
		}

		if useDirectory == true {
			continue
		}

		if path.Ext(fileInfo.Name()) == ".go" {
			*paths = append(*paths, directory)
			useDirectory = true
		}
	}
	return
}

// If a file is excluded
func isExcluded(filePath string) bool {
	for _, p := range excludedPaths {
		absP, err := path.Abs(p)
		if err != nil {
			logger.Errorf("Cannot get absolute path of '%s'", p)
			continue
		}
		absFilePath, err := path.Abs(filePath)
		if err != nil {
			logger.Errorf("Cannot get absolute path of '%s'", filePath)
			break
		}
		if strings.HasPrefix(absFilePath, absP) {
			logger.Infof("'%s' is not being watched", filePath)
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"net/http"

	"github.com/prometheus/common/log"

	"github.com/weaveworks/cortex/distributor"
	"github.com/weaveworks/cortex/ring"
	"github.com/weaveworks/cortex/server"
	"github.com/weaveworks/cortex/util"
)

func main() {
	// The pattern for main functions is a series of config objects, which are
	// registered for command line flags, and then a series of components that
	// are instantiated and composed.  Some rules of thumb:
	// - Config types should only contain 'simple' types (ints, strings, urls etc).
	// - Flag validation should be done by the flag; use a flag.Var where
	//   appropriate.
	// - Config types should map 1:1 with a component type.
	// - Config types should define flags with a common prefix.
	// - Its fine to nest configs within configs, but this should match the nesting
	//   of components within components.
	// - Limit as much is possible sharing of configuration between config types.
	//   Where nessecary, use a pointer for this - avoid repetition.
	// - Where a nesting of components its not obvious, its fine to pass
	//   references to other components constructors to compose them.
	// - First argument for a components contructor should be its matching config
	//   object.

	var (
		serverConfig      server.Config
		ringConfig        ring.Config
		distributorConfig distributor.Config
	)
	util.RegisterFlags(&serverConfig, &ringConfig, &distributorConfig)
	flag.Parse()

	r, err := ring.New(ringConfig)
	if err != nil {
		log.Fatalf("Error initializing ring: %v", err)
	}
	defer r.Stop()

	dist, err := distributor.New(distributorConfig, r)
	if err != nil {
		log.Fatalf("Error initializing distributor: %v", err)
	}

	server := server.New(serverConfig, r)
	server.HTTP.Handle("/api/prom/push", http.HandlerFunc(dist.PushHandler))
	defer server.Stop()

	server.Run()
}
<commit_msg>s/flag.Var/flag.Value<commit_after>package main

import (
	"flag"
	"net/http"

	"github.com/prometheus/common/log"

	"github.com/weaveworks/cortex/distributor"
	"github.com/weaveworks/cortex/ring"
	"github.com/weaveworks/cortex/server"
	"github.com/weaveworks/cortex/util"
)

func main() {
	// The pattern for main functions is a series of config objects, which are
	// registered for command line flags, and then a series of components that
	// are instantiated and composed.  Some rules of thumb:
	// - Config types should only contain 'simple' types (ints, strings, urls etc).
	// - Flag validation should be done by the flag; use a flag.Value where
	//   appropriate.
	// - Config types should map 1:1 with a component type.
	// - Config types should define flags with a common prefix.
	// - Its fine to nest configs within configs, but this should match the nesting
	//   of components within components.
	// - Limit as much is possible sharing of configuration between config types.
	//   Where nessecary, use a pointer for this - avoid repetition.
	// - Where a nesting of components its not obvious, its fine to pass
	//   references to other components constructors to compose them.
	// - First argument for a components contructor should be its matching config
	//   object.

	var (
		serverConfig      server.Config
		ringConfig        ring.Config
		distributorConfig distributor.Config
	)
	util.RegisterFlags(&serverConfig, &ringConfig, &distributorConfig)
	flag.Parse()

	r, err := ring.New(ringConfig)
	if err != nil {
		log.Fatalf("Error initializing ring: %v", err)
	}
	defer r.Stop()

	dist, err := distributor.New(distributorConfig, r)
	if err != nil {
		log.Fatalf("Error initializing distributor: %v", err)
	}

	server := server.New(serverConfig, r)
	server.HTTP.Handle("/api/prom/push", http.HandlerFunc(dist.PushHandler))
	defer server.Stop()

	server.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func ita8paste() {
	_, body, err := req("GET", nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(body)
}

func ita8copy() {
	_, body, err := req("PUT", os.Stdin)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(body)
}

func req(method string, r io.Reader) (resp *http.Response, body string, err error) {
	client := &http.Client{}
	req, err := http.NewRequest(method, "http://127.0.0.1:4567/", r)
	if err != nil {
		return
	}
	resp, err = client.Do(req)
	if err != nil {
		return
	}

	if r == nil {
		return
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	body = string(b)
	return
}
<commit_msg>fix invalid return<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func ita8paste() {
	_, body, err := req("GET", nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(body)
}

func ita8copy() {
	_, body, err := req("PUT", os.Stdin)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(body)
}

func req(method string, r io.Reader) (resp *http.Response, body string, err error) {
	client := &http.Client{}
	req, err := http.NewRequest(method, "http://127.0.0.1:4567/", r)
	if err != nil {
		return
	}
	resp, err = client.Do(req)
	if err != nil {
		return
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	body = string(b)
	return
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"context"
	stdjson "encoding/json"
	"fmt"
	"io"
	"time"

	. "github.com/polydawn/go-errcat"
	"github.com/polydawn/refmt/json"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/repeatr"
	"go.polydawn.net/repeatr/batch"
	"go.polydawn.net/rio/fs"
)

func BatchCmd(
	ctx context.Context,
	executorName string,
	bastingPath string,
	stdout, stderr io.Writer,
	memoDir *fs.AbsolutePath,
) (err error) {
	defer RequireErrorHasCategory(&err, repeatr.ErrorCategory(""))

	printer := ansi{stdout, stderr} // todo: switch

	// Load basting & compute evaluation order.
	basting, err := loadBasting(bastingPath)
	if err != nil {
		return err
	}
	stepOrder, err := batch.OrderSteps(*basting)
	if err != nil {
		return Errorf(repeatr.ErrUsage, "structurally invalid basting: %s", err)
	}
	fmt.Printf("orden: %s\n", stepOrder)

	// Run stuff!  In order.
	//  This is placeholder implementation quality.  We should be
	//  exec'ing each of these, and thereby also be ready to parallelize.
	runRecords := map[string]api.RunRecord{}
	for stepNum, stepName := range stepOrder {
		formula, imports, formulaCtx := basting.Steps[stepName].Formula,
			basting.Steps[stepName].Imports, basting.Contexts[stepName]
		for path, imp := range imports {
			if imp.CatalogName == "wire" {
				formula.Inputs[path] = runRecords[string(imp.ReleaseName)].Results[api.AbsPath(imp.ItemName)]
				printer.printLog(repeatr.Event_Log{
					Time:  time.Now(),
					Level: repeatr.LogInfo,
					Msg:   "wire import resolved",
					Detail: [][2]string{
						{"stepName", stepName},
						{"stepNum", fmt.Sprintf("%d/%d", stepNum+1, len(stepOrder))},
						{"path", string(path)},
						{"import", imp.String()},
						{"resolved", formula.Inputs[path].String()},
					},
				})
				break
			}
		}
		rr, err := Run(ctx, executorName, formula, formulaCtx, printer, memoDir)
		if err != nil {
			printer.printLog(repeatr.Event_Log{
				Time:  time.Now(),
				Level: repeatr.LogError,
				Msg:   "executor reports error",
				Detail: [][2]string{
					{"err", err.Error()},
				},
			})
			return err
		}
		runRecords[stepName] = *rr
	}

	// Now that we've finished all steps, we can print all the final export wares:
	//  (FUTURE: this format should be something almost ready to pipe into a `hitch commit` command!)
	exports := map[api.ItemName]api.WareID{}
	for itemName, wire := range basting.Exports {
		exports[itemName] = runRecords[string(wire.ReleaseName)].Results[api.AbsPath(wire.ItemName)]
	}
	printBatchResults(stdout, stderr, exports)

	return nil
}

func printBatchResults(stdout, stderr io.Writer, exports map[api.ItemName]api.WareID) {
	// Buffer rather than go direct to stdout so we can flush with linebreaks at the same time.
	//  This makes output slightly more readable (otherwise a stderr write can get stuck
	//  dangling after the runrecord...).
	var buf bytes.Buffer
	if err := json.NewMarshallerAtlased(&buf, api.HitchAtlas).Marshal(exports); err != nil {
		fmt.Fprintf(stderr, "%s\n", err)
	}
	// Oh yey we need a second buffer to prittyprint (we should make refmt do this ffs)
	var buf2 bytes.Buffer
	stdjson.Indent(&buf2, buf.Bytes(), "", "\t")
	buf2.Write([]byte{'\n'})
	stdout.Write(buf2.Bytes())
}
<commit_msg>also the graph computation comments<commit_after>package main

import (
	"bytes"
	"context"
	stdjson "encoding/json"
	"fmt"
	"io"
	"time"

	. "github.com/polydawn/go-errcat"
	"github.com/polydawn/refmt/json"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/repeatr"
	"go.polydawn.net/repeatr/batch"
	"go.polydawn.net/rio/fs"
)

func BatchCmd(
	ctx context.Context,
	executorName string,
	bastingPath string,
	stdout, stderr io.Writer,
	memoDir *fs.AbsolutePath,
) (err error) {
	defer RequireErrorHasCategory(&err, repeatr.ErrorCategory(""))

	printer := ansi{stdout, stderr} // todo: switch

	// Load basting & compute evaluation order.
	basting, err := loadBasting(bastingPath)
	if err != nil {
		return err
	}
	printer.printLog(repeatr.Event_Log{
		Time:  time.Now(),
		Level: repeatr.LogInfo,
		Msg:   "calculating evaluation dependency order...",
		Detail: [][2]string{
			{"graphSize", fmt.Sprintf("%d", len(basting.Steps))},
		},
	})
	stepOrder, err := batch.OrderSteps(*basting)
	if err != nil {
		return Errorf(repeatr.ErrUsage, "structurally invalid basting: %s", err)
	}
	printer.printLog(repeatr.Event_Log{
		Time:  time.Now(),
		Level: repeatr.LogInfo,
		Msg:   "calculated evaluation order!",
		Detail: [][2]string{
			{"graphSize", fmt.Sprintf("%d", len(basting.Steps))},
			{"order", fmt.Sprintf("%s", stepOrder)},
		},
	})

	// Run stuff!  In order.
	//  This is placeholder implementation quality.  We should be
	//  exec'ing each of these, and thereby also be ready to parallelize.
	runRecords := map[string]api.RunRecord{}
	for stepNum, stepName := range stepOrder {
		formula, imports, formulaCtx := basting.Steps[stepName].Formula,
			basting.Steps[stepName].Imports, basting.Contexts[stepName]
		for path, imp := range imports {
			if imp.CatalogName == "wire" {
				formula.Inputs[path] = runRecords[string(imp.ReleaseName)].Results[api.AbsPath(imp.ItemName)]
				printer.printLog(repeatr.Event_Log{
					Time:  time.Now(),
					Level: repeatr.LogInfo,
					Msg:   "wire import resolved",
					Detail: [][2]string{
						{"stepName", stepName},
						{"stepNum", fmt.Sprintf("%d/%d", stepNum+1, len(stepOrder))},
						{"path", string(path)},
						{"import", imp.String()},
						{"resolved", formula.Inputs[path].String()},
					},
				})
				break
			}
		}
		rr, err := Run(ctx, executorName, formula, formulaCtx, printer, memoDir)
		if err != nil {
			printer.printLog(repeatr.Event_Log{
				Time:  time.Now(),
				Level: repeatr.LogError,
				Msg:   "executor reports error",
				Detail: [][2]string{
					{"err", err.Error()},
				},
			})
			return err
		}
		runRecords[stepName] = *rr
	}

	// Now that we've finished all steps, we can print all the final export wares:
	//  (FUTURE: this format should be something almost ready to pipe into a `hitch commit` command!)
	exports := map[api.ItemName]api.WareID{}
	for itemName, wire := range basting.Exports {
		exports[itemName] = runRecords[string(wire.ReleaseName)].Results[api.AbsPath(wire.ItemName)]
	}
	printBatchResults(stdout, stderr, exports)

	return nil
}

func printBatchResults(stdout, stderr io.Writer, exports map[api.ItemName]api.WareID) {
	// Buffer rather than go direct to stdout so we can flush with linebreaks at the same time.
	//  This makes output slightly more readable (otherwise a stderr write can get stuck
	//  dangling after the runrecord...).
	var buf bytes.Buffer
	if err := json.NewMarshallerAtlased(&buf, api.HitchAtlas).Marshal(exports); err != nil {
		fmt.Fprintf(stderr, "%s\n", err)
	}
	// Oh yey we need a second buffer to prittyprint (we should make refmt do this ffs)
	var buf2 bytes.Buffer
	stdjson.Indent(&buf2, buf.Bytes(), "", "\t")
	buf2.Write([]byte{'\n'})
	stdout.Write(buf2.Bytes())
}
<|endoftext|>


<commit_before>package main

import (
	_ "embed"
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/aggregators"
	"github.com/influxdata/telegraf/plugins/inputs"
	"github.com/influxdata/telegraf/plugins/outputs"
	"github.com/influxdata/telegraf/plugins/processors"
)

var (
	// Default sections
	sectionDefaults = []string{"global_tags", "agent", "outputs",
		"processors", "aggregators", "inputs"}

	// Default input plugins
	inputDefaults = []string{"cpu", "mem", "swap", "system", "kernel",
		"processes", "disk", "diskio"}

	// Default output plugins
	outputDefaults = []string{}
)

var header = `# Telegraf Configuration
#
# Telegraf is entirely plugin driven. All metrics are gathered from the
# declared inputs, and sent to the declared outputs.
#
# Plugins must be declared in here to be active.
# To deactivate a plugin, comment out the name and any variables.
#
# Use 'telegraf -config telegraf.conf -test' to see what metrics a config
# file would generate.
#
# Environment variables can be used anywhere in this config file, simply surround
# them with ${}. For strings the variable must be within quotes (ie, "${STR_VAR}"),
# for numbers and booleans they should be plain (ie, ${INT_VAR}, ${BOOL_VAR})

`
var globalTagsConfig = `
# Global tags can be specified here in key="value" format.
[global_tags]
  # dc = "us-east-1" # will tag all metrics with dc=us-east-1
  # rack = "1a"
  ## Environment variables can be used as tags, and throughout the config file
  # user = "$USER"

`

// DO NOT REMOVE THE NEXT TWO LINES! This is required to embed the agentConfig data.
//
//go:embed agent.conf
var agentConfig string

var outputHeader = `
###############################################################################
#                            OUTPUT PLUGINS                                   #
###############################################################################

`

var processorHeader = `
###############################################################################
#                            PROCESSOR PLUGINS                                #
###############################################################################

`

var aggregatorHeader = `
###############################################################################
#                            AGGREGATOR PLUGINS                               #
###############################################################################

`

var inputHeader = `
###############################################################################
#                            INPUT PLUGINS                                    #
###############################################################################

`

var serviceInputHeader = `
###############################################################################
#                            SERVICE INPUT PLUGINS                            #
###############################################################################

`

func sliceContains(name string, list []string) bool {
	for _, b := range list {
		if b == name {
			return true
		}
	}
	return false
}

// printSampleConfig prints the sample config
func printSampleConfig(
	outputBuffer io.Writer,
	sectionFilters []string,
	inputFilters []string,
	outputFilters []string,
	aggregatorFilters []string,
	processorFilters []string,
) {
	// print headers
	outputBuffer.Write([]byte(header))

	if len(sectionFilters) == 0 {
		sectionFilters = sectionDefaults
	}
	printFilteredGlobalSections(sectionFilters, outputBuffer)

	// print output plugins
	if sliceContains("outputs", sectionFilters) {
		if len(outputFilters) != 0 {
			if len(outputFilters) >= 3 && outputFilters[1] != "none" {
				outputBuffer.Write([]byte(outputHeader))
			}
			printFilteredOutputs(outputFilters, false, outputBuffer)
		} else {
			outputBuffer.Write([]byte(outputHeader))
			printFilteredOutputs(outputDefaults, false, outputBuffer)
			// Print non-default outputs, commented
			var pnames []string
			for pname := range outputs.Outputs {
				if !sliceContains(pname, outputDefaults) {
					pnames = append(pnames, pname)
				}
			}
			printFilteredOutputs(pnames, true, outputBuffer)
		}
	}

	// print processor plugins
	if sliceContains("processors", sectionFilters) {
		if len(processorFilters) != 0 {
			if len(processorFilters) >= 3 && processorFilters[1] != "none" {
				outputBuffer.Write([]byte(processorHeader))
			}
			printFilteredProcessors(processorFilters, false, outputBuffer)
		} else {
			outputBuffer.Write([]byte(processorHeader))
			pnames := []string{}
			for pname := range processors.Processors {
				pnames = append(pnames, pname)
			}
			printFilteredProcessors(pnames, true, outputBuffer)
		}
	}

	// print aggregator plugins
	if sliceContains("aggregators", sectionFilters) {
		if len(aggregatorFilters) != 0 {
			if len(aggregatorFilters) >= 3 && aggregatorFilters[1] != "none" {
				outputBuffer.Write([]byte(aggregatorHeader))
			}
			printFilteredAggregators(aggregatorFilters, false, outputBuffer)
		} else {
			outputBuffer.Write([]byte(aggregatorHeader))
			pnames := []string{}
			for pname := range aggregators.Aggregators {
				pnames = append(pnames, pname)
			}
			printFilteredAggregators(pnames, true, outputBuffer)
		}
	}

	// print input plugins
	if sliceContains("inputs", sectionFilters) {
		if len(inputFilters) != 0 {
			if len(inputFilters) >= 3 && inputFilters[1] != "none" {
				outputBuffer.Write([]byte(inputHeader))
			}
			printFilteredInputs(inputFilters, false, outputBuffer)
		} else {
			outputBuffer.Write([]byte(inputHeader))
			printFilteredInputs(inputDefaults, false, outputBuffer)
			// Print non-default inputs, commented
			var pnames []string
			for pname := range inputs.Inputs {
				if !sliceContains(pname, inputDefaults) {
					pnames = append(pnames, pname)
				}
			}
			printFilteredInputs(pnames, true, outputBuffer)
		}
	}
}

func printFilteredProcessors(processorFilters []string, commented bool, outputBuffer io.Writer) {
	// Filter processors
	var pnames []string
	for pname := range processors.Processors {
		if sliceContains(pname, processorFilters) {
			pnames = append(pnames, pname)
		}
	}
	sort.Strings(pnames)

	// Print Outputs
	for _, pname := range pnames {
		creator := processors.Processors[pname]
		output := creator()
		printConfig(pname, output, "processors", commented, processors.Deprecations[pname], outputBuffer)
	}
}

func printFilteredAggregators(aggregatorFilters []string, commented bool, outputBuffer io.Writer) {
	// Filter outputs
	var anames []string
	for aname := range aggregators.Aggregators {
		if sliceContains(aname, aggregatorFilters) {
			anames = append(anames, aname)
		}
	}
	sort.Strings(anames)

	// Print Outputs
	for _, aname := range anames {
		creator := aggregators.Aggregators[aname]
		output := creator()
		printConfig(aname, output, "aggregators", commented, aggregators.Deprecations[aname], outputBuffer)
	}
}

func printFilteredInputs(inputFilters []string, commented bool, outputBuffer io.Writer) {
	// Filter inputs
	var pnames []string
	for pname := range inputs.Inputs {
		if sliceContains(pname, inputFilters) {
			pnames = append(pnames, pname)
		}
	}
	sort.Strings(pnames)

	// cache service inputs to print them at the end
	servInputs := make(map[string]telegraf.ServiceInput)
	// for alphabetical looping:
	servInputNames := []string{}

	// Print Inputs
	for _, pname := range pnames {
		// Skip inputs that are registered twice for backward compatibility
		switch pname {
		case "cisco_telemetry_gnmi", "io", "KNXListener":
			continue
		}
		creator := inputs.Inputs[pname]
		input := creator()

		if p, ok := input.(telegraf.ServiceInput); ok {
			servInputs[pname] = p
			servInputNames = append(servInputNames, pname)
			continue
		}

		printConfig(pname, input, "inputs", commented, inputs.Deprecations[pname], outputBuffer)
	}

	// Print Service Inputs
	if len(servInputs) == 0 {
		return
	}
	sort.Strings(servInputNames)

	outputBuffer.Write([]byte(serviceInputHeader))
	for _, name := range servInputNames {
		printConfig(name, servInputs[name], "inputs", commented, inputs.Deprecations[name], outputBuffer)
	}
}

func printFilteredOutputs(outputFilters []string, commented bool, outputBuffer io.Writer) {
	// Filter outputs
	var onames []string
	for oname := range outputs.Outputs {
		if sliceContains(oname, outputFilters) {
			onames = append(onames, oname)
		}
	}
	sort.Strings(onames)

	// Print Outputs
	for _, oname := range onames {
		creator := outputs.Outputs[oname]
		output := creator()
		printConfig(oname, output, "outputs", commented, outputs.Deprecations[oname], outputBuffer)
	}
}

func printFilteredGlobalSections(sectionFilters []string, outputBuffer io.Writer) {
	if sliceContains("global_tags", sectionFilters) {
		outputBuffer.Write([]byte(globalTagsConfig))
	}

	if sliceContains("agent", sectionFilters) {
		outputBuffer.Write([]byte(agentConfig))
	}
}

func printConfig(name string, p telegraf.PluginDescriber, op string, commented bool, di telegraf.DeprecationInfo, outputBuffer io.Writer) {
	comment := ""
	if commented {
		comment = "# "
	}

	if di.Since != "" {
		removalNote := ""
		if di.RemovalIn != "" {
			removalNote = " and will be removed in " + di.RemovalIn
		}
		outputBuffer.Write([]byte(fmt.Sprintf("\n%s ## DEPRECATED: The '%s' plugin is deprecated in version %s%s, %s.", comment, name, di.Since, removalNote, di.Notice)))
	}

	sample := p.SampleConfig()
	if sample == "" {
		outputBuffer.Write([]byte(fmt.Sprintf("\n#[[%s.%s]]", op, name)))
		outputBuffer.Write([]byte(fmt.Sprintf("\n%s  # no configuration\n\n", comment)))
	} else {
		lines := strings.Split(sample, "\n")
		outputBuffer.Write([]byte("\n"))
		for i, line := range lines {
			if i == len(lines)-1 {
				outputBuffer.Write([]byte("\n"))
				continue
			}
			outputBuffer.Write([]byte(strings.TrimRight(comment+line, " ") + "\n"))
		}
	}
}

// PrintInputConfig prints the config usage of a single input.
func PrintInputConfig(name string, outputBuffer io.Writer) error {
	creator, ok := inputs.Inputs[name]
	if !ok {
		return fmt.Errorf("input %s not found", name)
	}

	printConfig(name, creator(), "inputs", false, inputs.Deprecations[name], outputBuffer)
	return nil
}

// PrintOutputConfig prints the config usage of a single output.
func PrintOutputConfig(name string, outputBuffer io.Writer) error {
	creator, ok := outputs.Outputs[name]
	if !ok {
		return fmt.Errorf("output %s not found", name)
	}

	printConfig(name, creator(), "outputs", false, outputs.Deprecations[name], outputBuffer)
	return nil
}
<commit_msg>chore: Move influxdb_v2 to be first in sample config (#12163)<commit_after>package main

import (
	_ "embed"
	"fmt"
	"io"
	"sort"
	"strings"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/plugins/aggregators"
	"github.com/influxdata/telegraf/plugins/inputs"
	"github.com/influxdata/telegraf/plugins/outputs"
	"github.com/influxdata/telegraf/plugins/processors"
)

var (
	// Default sections
	sectionDefaults = []string{"global_tags", "agent", "outputs",
		"processors", "aggregators", "inputs"}

	// Default input plugins
	inputDefaults = []string{"cpu", "mem", "swap", "system", "kernel",
		"processes", "disk", "diskio"}

	// Default output plugins
	outputDefaults = []string{}
)

var header = `# Telegraf Configuration
#
# Telegraf is entirely plugin driven. All metrics are gathered from the
# declared inputs, and sent to the declared outputs.
#
# Plugins must be declared in here to be active.
# To deactivate a plugin, comment out the name and any variables.
#
# Use 'telegraf -config telegraf.conf -test' to see what metrics a config
# file would generate.
#
# Environment variables can be used anywhere in this config file, simply surround
# them with ${}. For strings the variable must be within quotes (ie, "${STR_VAR}"),
# for numbers and booleans they should be plain (ie, ${INT_VAR}, ${BOOL_VAR})

`
var globalTagsConfig = `
# Global tags can be specified here in key="value" format.
[global_tags]
  # dc = "us-east-1" # will tag all metrics with dc=us-east-1
  # rack = "1a"
  ## Environment variables can be used as tags, and throughout the config file
  # user = "$USER"

`

// DO NOT REMOVE THE NEXT TWO LINES! This is required to embed the agentConfig data.
//
//go:embed agent.conf
var agentConfig string

var outputHeader = `
###############################################################################
#                            OUTPUT PLUGINS                                   #
###############################################################################

`

var processorHeader = `
###############################################################################
#                            PROCESSOR PLUGINS                                #
###############################################################################

`

var aggregatorHeader = `
###############################################################################
#                            AGGREGATOR PLUGINS                               #
###############################################################################

`

var inputHeader = `
###############################################################################
#                            INPUT PLUGINS                                    #
###############################################################################

`

var serviceInputHeader = `
###############################################################################
#                            SERVICE INPUT PLUGINS                            #
###############################################################################

`

func sliceContains(name string, list []string) bool {
	for _, b := range list {
		if b == name {
			return true
		}
	}
	return false
}

// printSampleConfig prints the sample config
func printSampleConfig(
	outputBuffer io.Writer,
	sectionFilters []string,
	inputFilters []string,
	outputFilters []string,
	aggregatorFilters []string,
	processorFilters []string,
) {
	// print headers
	outputBuffer.Write([]byte(header))

	if len(sectionFilters) == 0 {
		sectionFilters = sectionDefaults
	}
	printFilteredGlobalSections(sectionFilters, outputBuffer)

	// print output plugins
	if sliceContains("outputs", sectionFilters) {
		if len(outputFilters) != 0 {
			if len(outputFilters) >= 3 && outputFilters[1] != "none" {
				outputBuffer.Write([]byte(outputHeader))
			}
			printFilteredOutputs(outputFilters, false, outputBuffer)
		} else {
			outputBuffer.Write([]byte(outputHeader))
			printFilteredOutputs(outputDefaults, false, outputBuffer)
			// Print non-default outputs, commented
			var pnames []string
			for pname := range outputs.Outputs {
				if !sliceContains(pname, outputDefaults) {
					pnames = append(pnames, pname)
				}
			}
			printFilteredOutputs(pnames, true, outputBuffer)
		}
	}

	// print processor plugins
	if sliceContains("processors", sectionFilters) {
		if len(processorFilters) != 0 {
			if len(processorFilters) >= 3 && processorFilters[1] != "none" {
				outputBuffer.Write([]byte(processorHeader))
			}
			printFilteredProcessors(processorFilters, false, outputBuffer)
		} else {
			outputBuffer.Write([]byte(processorHeader))
			pnames := []string{}
			for pname := range processors.Processors {
				pnames = append(pnames, pname)
			}
			printFilteredProcessors(pnames, true, outputBuffer)
		}
	}

	// print aggregator plugins
	if sliceContains("aggregators", sectionFilters) {
		if len(aggregatorFilters) != 0 {
			if len(aggregatorFilters) >= 3 && aggregatorFilters[1] != "none" {
				outputBuffer.Write([]byte(aggregatorHeader))
			}
			printFilteredAggregators(aggregatorFilters, false, outputBuffer)
		} else {
			outputBuffer.Write([]byte(aggregatorHeader))
			pnames := []string{}
			for pname := range aggregators.Aggregators {
				pnames = append(pnames, pname)
			}
			printFilteredAggregators(pnames, true, outputBuffer)
		}
	}

	// print input plugins
	if sliceContains("inputs", sectionFilters) {
		if len(inputFilters) != 0 {
			if len(inputFilters) >= 3 && inputFilters[1] != "none" {
				outputBuffer.Write([]byte(inputHeader))
			}
			printFilteredInputs(inputFilters, false, outputBuffer)
		} else {
			outputBuffer.Write([]byte(inputHeader))
			printFilteredInputs(inputDefaults, false, outputBuffer)
			// Print non-default inputs, commented
			var pnames []string
			for pname := range inputs.Inputs {
				if !sliceContains(pname, inputDefaults) {
					pnames = append(pnames, pname)
				}
			}
			printFilteredInputs(pnames, true, outputBuffer)
		}
	}
}

func printFilteredProcessors(processorFilters []string, commented bool, outputBuffer io.Writer) {
	// Filter processors
	var pnames []string
	for pname := range processors.Processors {
		if sliceContains(pname, processorFilters) {
			pnames = append(pnames, pname)
		}
	}
	sort.Strings(pnames)

	// Print Outputs
	for _, pname := range pnames {
		creator := processors.Processors[pname]
		output := creator()
		printConfig(pname, output, "processors", commented, processors.Deprecations[pname], outputBuffer)
	}
}

func printFilteredAggregators(aggregatorFilters []string, commented bool, outputBuffer io.Writer) {
	// Filter outputs
	var anames []string
	for aname := range aggregators.Aggregators {
		if sliceContains(aname, aggregatorFilters) {
			anames = append(anames, aname)
		}
	}
	sort.Strings(anames)

	// Print Outputs
	for _, aname := range anames {
		creator := aggregators.Aggregators[aname]
		output := creator()
		printConfig(aname, output, "aggregators", commented, aggregators.Deprecations[aname], outputBuffer)
	}
}

func printFilteredInputs(inputFilters []string, commented bool, outputBuffer io.Writer) {
	// Filter inputs
	var pnames []string
	for pname := range inputs.Inputs {
		if sliceContains(pname, inputFilters) {
			pnames = append(pnames, pname)
		}
	}
	sort.Strings(pnames)

	// cache service inputs to print them at the end
	servInputs := make(map[string]telegraf.ServiceInput)
	// for alphabetical looping:
	servInputNames := []string{}

	// Print Inputs
	for _, pname := range pnames {
		// Skip inputs that are registered twice for backward compatibility
		switch pname {
		case "cisco_telemetry_gnmi", "io", "KNXListener":
			continue
		}
		creator := inputs.Inputs[pname]
		input := creator()

		if p, ok := input.(telegraf.ServiceInput); ok {
			servInputs[pname] = p
			servInputNames = append(servInputNames, pname)
			continue
		}

		printConfig(pname, input, "inputs", commented, inputs.Deprecations[pname], outputBuffer)
	}

	// Print Service Inputs
	if len(servInputs) == 0 {
		return
	}
	sort.Strings(servInputNames)

	outputBuffer.Write([]byte(serviceInputHeader))
	for _, name := range servInputNames {
		printConfig(name, servInputs[name], "inputs", commented, inputs.Deprecations[name], outputBuffer)
	}
}

func printFilteredOutputs(outputFilters []string, commented bool, outputBuffer io.Writer) {
	// Filter outputs
	var onames []string
	var influxdbV2 string

	for oname := range outputs.Outputs {
		if sliceContains(oname, outputFilters) {
			// Make influxdb_v2 the exception and have it be first in the list
			// Store it and add it later
			if oname == "influxdb_v2" {
				influxdbV2 = oname
				continue
			}

			onames = append(onames, oname)
		}
	}
	sort.Strings(onames)

	if influxdbV2 != "" {
		onames = append([]string{influxdbV2}, onames...)
	}

	// Print Outputs
	for _, oname := range onames {
		creator := outputs.Outputs[oname]
		output := creator()
		printConfig(oname, output, "outputs", commented, outputs.Deprecations[oname], outputBuffer)
	}
}

func printFilteredGlobalSections(sectionFilters []string, outputBuffer io.Writer) {
	if sliceContains("global_tags", sectionFilters) {
		outputBuffer.Write([]byte(globalTagsConfig))
	}

	if sliceContains("agent", sectionFilters) {
		outputBuffer.Write([]byte(agentConfig))
	}
}

func printConfig(name string, p telegraf.PluginDescriber, op string, commented bool, di telegraf.DeprecationInfo, outputBuffer io.Writer) {
	comment := ""
	if commented {
		comment = "# "
	}

	if di.Since != "" {
		removalNote := ""
		if di.RemovalIn != "" {
			removalNote = " and will be removed in " + di.RemovalIn
		}
		outputBuffer.Write([]byte(fmt.Sprintf("\n%s ## DEPRECATED: The '%s' plugin is deprecated in version %s%s, %s.", comment, name, di.Since, removalNote, di.Notice)))
	}

	sample := p.SampleConfig()
	if sample == "" {
		outputBuffer.Write([]byte(fmt.Sprintf("\n#[[%s.%s]]", op, name)))
		outputBuffer.Write([]byte(fmt.Sprintf("\n%s  # no configuration\n\n", comment)))
	} else {
		lines := strings.Split(sample, "\n")
		outputBuffer.Write([]byte("\n"))
		for i, line := range lines {
			if i == len(lines)-1 {
				outputBuffer.Write([]byte("\n"))
				continue
			}
			outputBuffer.Write([]byte(strings.TrimRight(comment+line, " ") + "\n"))
		}
	}
}

// PrintInputConfig prints the config usage of a single input.
func PrintInputConfig(name string, outputBuffer io.Writer) error {
	creator, ok := inputs.Inputs[name]
	if !ok {
		return fmt.Errorf("input %s not found", name)
	}

	printConfig(name, creator(), "inputs", false, inputs.Deprecations[name], outputBuffer)
	return nil
}

// PrintOutputConfig prints the config usage of a single output.
func PrintOutputConfig(name string, outputBuffer io.Writer) error {
	creator, ok := outputs.Outputs[name]
	if !ok {
		return fmt.Errorf("output %s not found", name)
	}

	printConfig(name, creator(), "outputs", false, outputs.Deprecations[name], outputBuffer)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"expvar"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/anacrolix/log"
	"github.com/anacrolix/tagflag"
	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	pp "github.com/anacrolix/torrent/peer_protocol"
	"github.com/anacrolix/torrent/storage"
	"github.com/davecgh/go-spew/spew"
	"github.com/dustin/go-humanize"
	"golang.org/x/time/rate"
)

func torrentBar(t *torrent.Torrent, pieceStates bool) {
	go func() {
		start := time.Now()
		if t.Info() == nil {
			fmt.Printf("%v: getting torrent info for %q\n", time.Since(start), t.Name())
			<-t.GotInfo()
		}
		lastStats := t.Stats()
		var lastLine string
		interval := 3 * time.Second
		for range time.Tick(interval) {
			var completedPieces, partialPieces int
			psrs := t.PieceStateRuns()
			for _, r := range psrs {
				if r.Complete {
					completedPieces += r.Length
				}
				if r.Partial {
					partialPieces += r.Length
				}
			}
			stats := t.Stats()
			byteRate := int64(time.Second)
			byteRate *= stats.BytesReadUsefulData.Int64() - lastStats.BytesReadUsefulData.Int64()
			byteRate /= int64(interval)
			line := fmt.Sprintf(
				"%v: downloading %q: %s/%s, %d/%d pieces completed (%d partial): %v/s\n",
				time.Since(start),
				t.Name(),
				humanize.Bytes(uint64(t.BytesCompleted())),
				humanize.Bytes(uint64(t.Length())),
				completedPieces,
				t.NumPieces(),
				partialPieces,
				humanize.Bytes(uint64(byteRate)),
			)
			if line != lastLine {
				lastLine = line
				os.Stdout.WriteString(line)
			}
			if pieceStates {
				fmt.Println(psrs)
			}
			lastStats = stats
		}
	}()
}

type stringAddr string

func (stringAddr) Network() string   { return "" }
func (me stringAddr) String() string { return string(me) }

func resolveTestPeers(addrs []string) (ret []torrent.PeerInfo) {
	for _, ta := range addrs {
		ret = append(ret, torrent.PeerInfo{
			Addr: stringAddr(ta),
		})
	}
	return
}

func addTorrents(ctx context.Context, client *torrent.Client, flags downloadFlags, wg *sync.WaitGroup) error {
	testPeers := resolveTestPeers(flags.TestPeer)
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, fmt.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, fmt.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, fmt.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, fmt.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, fmt.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, fmt.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return fmt.Errorf("adding torrent for %q: %w", arg, err)
		}
		if flags.Progress {
			torrentBar(t, flags.PieceStates)
		}
		t.AddPeers(testPeers)
		wg.Add(1)
		go func() {
			defer wg.Done()
			select {
			case <-ctx.Done():
				return
			case <-t.GotInfo():
			}
			if flags.SaveMetainfos {
				path := fmt.Sprintf("%v.torrent", t.InfoHash().HexString())
				err := writeMetainfoToFile(t.Metainfo(), path)
				if err == nil {
					log.Printf("wrote %q", path)
				} else {
					log.Printf("error writing %q: %v", path, err)
				}
			}
			if len(flags.File) == 0 {
				t.DownloadAll()
				wg.Add(1)
				go func() {
					defer wg.Done()
					waitForPieces(ctx, t, 0, t.NumPieces())
				}()
				if flags.LinearDiscard {
					r := t.NewReader()
					io.Copy(io.Discard, r)
					r.Close()
				}
			} else {
				for _, f := range t.Files() {
					for _, fileArg := range flags.File {
						if f.DisplayPath() == fileArg {
							wg.Add(1)
							go func() {
								defer wg.Done()
								waitForPieces(ctx, t, f.BeginPieceIndex(), f.EndPieceIndex())
							}()
							f.Download()
							if flags.LinearDiscard {
								r := f.NewReader()
								go func() {
									defer r.Close()
									io.Copy(io.Discard, r)
								}()
							}
						}
					}
				}
			}
		}()
	}
	return nil
}

func waitForPieces(ctx context.Context, t *torrent.Torrent, beginIndex, endIndex int) {
	sub := t.SubscribePieceStateChanges()
	defer sub.Close()
	pending := make(map[int]struct{})
	for i := beginIndex; i < endIndex; i++ {
		pending[i] = struct{}{}
	}
	expected := storage.Completion{
		Complete: true,
		Ok:       true,
	}
	for {
		select {
		case ev := <-sub.Values:
			if ev.Completion == expected {
				delete(pending, ev.Index)
			}
			if len(pending) == 0 {
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

func writeMetainfoToFile(mi metainfo.MetaInfo, path string) error {
	f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	defer f.Close()
	err = mi.Write(f)
	if err != nil {
		return err
	}
	return f.Close()
}

type downloadFlags struct {
	Debug bool
	DownloadCmd
}

type DownloadCmd struct {
	SaveMetainfos      bool
	Mmap               bool           `help:"memory-map torrent data"`
	Seed               bool           `help:"seed after download is complete"`
	Addr               string         `help:"network listen addr"`
	MaxUnverifiedBytes tagflag.Bytes  `help:"maximum number bytes to have pending verification"`
	UploadRate         *tagflag.Bytes `help:"max piece bytes to send per second"`
	DownloadRate       *tagflag.Bytes `help:"max bytes per second down from peers"`
	PackedBlocklist    string
	PublicIP           net.IP
	Progress           bool `default:"true"`
	PieceStates        bool `help:"Output piece state runs at progress intervals."`
	Quiet              bool `help:"discard client logging"`
	Stats              bool `help:"print stats at termination"`
	Dht                bool `default:"true"`
	PortForward        bool `default:"true"`

	TcpPeers        bool `default:"true"`
	UtpPeers        bool `default:"true"`
	Webtorrent      bool `default:"true"`
	DisableWebseeds bool
	// Don't progress past handshake for peer connections where the peer doesn't offer the fast
	// extension.
	RequireFastExtension bool

	Ipv4 bool `default:"true"`
	Ipv6 bool `default:"true"`
	Pex  bool `default:"true"`

	LinearDiscard bool     `help:"Read and discard selected regions from start to finish. Useful for testing simultaneous Reader and static file prioritization."`
	TestPeer      []string `help:"addresses of some starting peers"`

	File    []string
	Torrent []string `arity:"+" help:"torrent file path or magnet uri" arg:"positional"`
}

func statsEnabled(flags downloadFlags) bool {
	return flags.Stats
}

func downloadErr(flags downloadFlags) error {
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.DisableWebseeds = flags.DisableWebseeds
	clientConfig.DisableTCP = !flags.TcpPeers
	clientConfig.DisableUTP = !flags.UtpPeers
	clientConfig.DisableIPv4 = !flags.Ipv4
	clientConfig.DisableIPv6 = !flags.Ipv6
	clientConfig.DisableAcceptRateLimiting = true
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP
	clientConfig.PublicIp6 = flags.PublicIP
	clientConfig.DisablePEX = !flags.Pex
	clientConfig.DisableWebtorrent = !flags.Webtorrent
	clientConfig.NoDefaultPortForwarding = !flags.PortForward
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return fmt.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != nil {
		// TODO: I think the upload rate limit could be much lower.
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(*flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != nil {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(*flags.DownloadRate), 1<<16)
	}
	{
		logger := log.Default.WithNames("main", "client")
		if flags.Quiet {
			logger = logger.FilterLevel(log.Critical)
		}
		clientConfig.Logger = logger
	}
	if flags.RequireFastExtension {
		clientConfig.MinPeerExtensions.SetBit(pp.ExtensionBitFast, true)
	}
	clientConfig.MaxUnverifiedBytes = flags.MaxUnverifiedBytes.Int64()

	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer cancel()

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return fmt.Errorf("creating client: %w", err)
	}
	defer client.Close()

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	var wg sync.WaitGroup
	err = addTorrents(ctx, client, flags, &wg)
	if err != nil {
		return fmt.Errorf("adding torrents: %w", err)
	}
	started := time.Now()
	defer outputStats(client, flags)
	wg.Wait()
	if ctx.Err() == nil {
		log.Print("downloaded ALL the torrents")
	} else {
		err = errors.New("y u no complete torrents?!")
	}
	clientConnStats := client.ConnStats()
	log.Printf("average download rate: %v",
		humanize.Bytes(uint64(
			time.Duration(
				clientConnStats.BytesReadUsefulData.Int64(),
			)*time.Second/time.Since(started),
		)))
	if flags.Seed {
		if len(client.Torrents()) == 0 {
			log.Print("no torrents to seed")
		} else {
			outputStats(client, flags)
			<-ctx.Done()
		}
	}
	spew.Dump(expvar.Get("torrent").(*expvar.Map).Get("chunks received"))
	spew.Dump(client.ConnStats())
	clStats := client.ConnStats()
	sentOverhead := clStats.BytesWritten.Int64() - clStats.BytesWrittenData.Int64()
	log.Printf(
		"client read %v, %.1f%% was useful data. sent %v non-data bytes",
		humanize.Bytes(uint64(clStats.BytesRead.Int64())),
		100*float64(clStats.BytesReadUsefulData.Int64())/float64(clStats.BytesRead.Int64()),
		humanize.Bytes(uint64(sentOverhead)))
	return err
}

func outputStats(cl *torrent.Client, args downloadFlags) {
	if !statsEnabled(args) {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<commit_msg>cmd/torrent: Fix panic with IPv6 public IP<commit_after>package main

import (
	"context"
	"errors"
	"expvar"
	"fmt"
	"io"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/anacrolix/log"
	"github.com/anacrolix/tagflag"
	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/iplist"
	"github.com/anacrolix/torrent/metainfo"
	pp "github.com/anacrolix/torrent/peer_protocol"
	"github.com/anacrolix/torrent/storage"
	"github.com/davecgh/go-spew/spew"
	"github.com/dustin/go-humanize"
	"golang.org/x/time/rate"
)

func torrentBar(t *torrent.Torrent, pieceStates bool) {
	go func() {
		start := time.Now()
		if t.Info() == nil {
			fmt.Printf("%v: getting torrent info for %q\n", time.Since(start), t.Name())
			<-t.GotInfo()
		}
		lastStats := t.Stats()
		var lastLine string
		interval := 3 * time.Second
		for range time.Tick(interval) {
			var completedPieces, partialPieces int
			psrs := t.PieceStateRuns()
			for _, r := range psrs {
				if r.Complete {
					completedPieces += r.Length
				}
				if r.Partial {
					partialPieces += r.Length
				}
			}
			stats := t.Stats()
			byteRate := int64(time.Second)
			byteRate *= stats.BytesReadUsefulData.Int64() - lastStats.BytesReadUsefulData.Int64()
			byteRate /= int64(interval)
			line := fmt.Sprintf(
				"%v: downloading %q: %s/%s, %d/%d pieces completed (%d partial): %v/s\n",
				time.Since(start),
				t.Name(),
				humanize.Bytes(uint64(t.BytesCompleted())),
				humanize.Bytes(uint64(t.Length())),
				completedPieces,
				t.NumPieces(),
				partialPieces,
				humanize.Bytes(uint64(byteRate)),
			)
			if line != lastLine {
				lastLine = line
				os.Stdout.WriteString(line)
			}
			if pieceStates {
				fmt.Println(psrs)
			}
			lastStats = stats
		}
	}()
}

type stringAddr string

func (stringAddr) Network() string   { return "" }
func (me stringAddr) String() string { return string(me) }

func resolveTestPeers(addrs []string) (ret []torrent.PeerInfo) {
	for _, ta := range addrs {
		ret = append(ret, torrent.PeerInfo{
			Addr: stringAddr(ta),
		})
	}
	return
}

func addTorrents(ctx context.Context, client *torrent.Client, flags downloadFlags, wg *sync.WaitGroup) error {
	testPeers := resolveTestPeers(flags.TestPeer)
	for _, arg := range flags.Torrent {
		t, err := func() (*torrent.Torrent, error) {
			if strings.HasPrefix(arg, "magnet:") {
				t, err := client.AddMagnet(arg)
				if err != nil {
					return nil, fmt.Errorf("error adding magnet: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
				response, err := http.Get(arg)
				if err != nil {
					return nil, fmt.Errorf("Error downloading torrent file: %s", err)
				}

				metaInfo, err := metainfo.Load(response.Body)
				defer response.Body.Close()
				if err != nil {
					return nil, fmt.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, fmt.Errorf("adding torrent: %w", err)
				}
				return t, nil
			} else if strings.HasPrefix(arg, "infohash:") {
				t, _ := client.AddTorrentInfoHash(metainfo.NewHashFromHex(strings.TrimPrefix(arg, "infohash:")))
				return t, nil
			} else {
				metaInfo, err := metainfo.LoadFromFile(arg)
				if err != nil {
					return nil, fmt.Errorf("error loading torrent file %q: %s\n", arg, err)
				}
				t, err := client.AddTorrent(metaInfo)
				if err != nil {
					return nil, fmt.Errorf("adding torrent: %w", err)
				}
				return t, nil
			}
		}()
		if err != nil {
			return fmt.Errorf("adding torrent for %q: %w", arg, err)
		}
		if flags.Progress {
			torrentBar(t, flags.PieceStates)
		}
		t.AddPeers(testPeers)
		wg.Add(1)
		go func() {
			defer wg.Done()
			select {
			case <-ctx.Done():
				return
			case <-t.GotInfo():
			}
			if flags.SaveMetainfos {
				path := fmt.Sprintf("%v.torrent", t.InfoHash().HexString())
				err := writeMetainfoToFile(t.Metainfo(), path)
				if err == nil {
					log.Printf("wrote %q", path)
				} else {
					log.Printf("error writing %q: %v", path, err)
				}
			}
			if len(flags.File) == 0 {
				t.DownloadAll()
				wg.Add(1)
				go func() {
					defer wg.Done()
					waitForPieces(ctx, t, 0, t.NumPieces())
				}()
				if flags.LinearDiscard {
					r := t.NewReader()
					io.Copy(io.Discard, r)
					r.Close()
				}
			} else {
				for _, f := range t.Files() {
					for _, fileArg := range flags.File {
						if f.DisplayPath() == fileArg {
							wg.Add(1)
							go func() {
								defer wg.Done()
								waitForPieces(ctx, t, f.BeginPieceIndex(), f.EndPieceIndex())
							}()
							f.Download()
							if flags.LinearDiscard {
								r := f.NewReader()
								go func() {
									defer r.Close()
									io.Copy(io.Discard, r)
								}()
							}
						}
					}
				}
			}
		}()
	}
	return nil
}

func waitForPieces(ctx context.Context, t *torrent.Torrent, beginIndex, endIndex int) {
	sub := t.SubscribePieceStateChanges()
	defer sub.Close()
	pending := make(map[int]struct{})
	for i := beginIndex; i < endIndex; i++ {
		pending[i] = struct{}{}
	}
	expected := storage.Completion{
		Complete: true,
		Ok:       true,
	}
	for {
		select {
		case ev := <-sub.Values:
			if ev.Completion == expected {
				delete(pending, ev.Index)
			}
			if len(pending) == 0 {
				return
			}
		case <-ctx.Done():
			return
		}
	}
}

func writeMetainfoToFile(mi metainfo.MetaInfo, path string) error {
	f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	defer f.Close()
	err = mi.Write(f)
	if err != nil {
		return err
	}
	return f.Close()
}

type downloadFlags struct {
	Debug bool
	DownloadCmd
}

type DownloadCmd struct {
	SaveMetainfos      bool
	Mmap               bool           `help:"memory-map torrent data"`
	Seed               bool           `help:"seed after download is complete"`
	Addr               string         `help:"network listen addr"`
	MaxUnverifiedBytes tagflag.Bytes  `help:"maximum number bytes to have pending verification"`
	UploadRate         *tagflag.Bytes `help:"max piece bytes to send per second"`
	DownloadRate       *tagflag.Bytes `help:"max bytes per second down from peers"`
	PackedBlocklist    string
	PublicIP           net.IP
	Progress           bool `default:"true"`
	PieceStates        bool `help:"Output piece state runs at progress intervals."`
	Quiet              bool `help:"discard client logging"`
	Stats              bool `help:"print stats at termination"`
	Dht                bool `default:"true"`
	PortForward        bool `default:"true"`

	TcpPeers        bool `default:"true"`
	UtpPeers        bool `default:"true"`
	Webtorrent      bool `default:"true"`
	DisableWebseeds bool
	// Don't progress past handshake for peer connections where the peer doesn't offer the fast
	// extension.
	RequireFastExtension bool

	Ipv4 bool `default:"true"`
	Ipv6 bool `default:"true"`
	Pex  bool `default:"true"`

	LinearDiscard bool     `help:"Read and discard selected regions from start to finish. Useful for testing simultaneous Reader and static file prioritization."`
	TestPeer      []string `help:"addresses of some starting peers"`

	File    []string
	Torrent []string `arity:"+" help:"torrent file path or magnet uri" arg:"positional"`
}

func statsEnabled(flags downloadFlags) bool {
	return flags.Stats
}

func downloadErr(flags downloadFlags) error {
	clientConfig := torrent.NewDefaultClientConfig()
	clientConfig.DisableWebseeds = flags.DisableWebseeds
	clientConfig.DisableTCP = !flags.TcpPeers
	clientConfig.DisableUTP = !flags.UtpPeers
	clientConfig.DisableIPv4 = !flags.Ipv4
	clientConfig.DisableIPv6 = !flags.Ipv6
	clientConfig.DisableAcceptRateLimiting = true
	clientConfig.NoDHT = !flags.Dht
	clientConfig.Debug = flags.Debug
	clientConfig.Seed = flags.Seed
	clientConfig.PublicIp4 = flags.PublicIP.To4()
	clientConfig.PublicIp6 = flags.PublicIP
	clientConfig.DisablePEX = !flags.Pex
	clientConfig.DisableWebtorrent = !flags.Webtorrent
	clientConfig.NoDefaultPortForwarding = !flags.PortForward
	if flags.PackedBlocklist != "" {
		blocklist, err := iplist.MMapPackedFile(flags.PackedBlocklist)
		if err != nil {
			return fmt.Errorf("loading blocklist: %v", err)
		}
		defer blocklist.Close()
		clientConfig.IPBlocklist = blocklist
	}
	if flags.Mmap {
		clientConfig.DefaultStorage = storage.NewMMap("")
	}
	if flags.Addr != "" {
		clientConfig.SetListenAddr(flags.Addr)
	}
	if flags.UploadRate != nil {
		// TODO: I think the upload rate limit could be much lower.
		clientConfig.UploadRateLimiter = rate.NewLimiter(rate.Limit(*flags.UploadRate), 256<<10)
	}
	if flags.DownloadRate != nil {
		clientConfig.DownloadRateLimiter = rate.NewLimiter(rate.Limit(*flags.DownloadRate), 1<<16)
	}
	{
		logger := log.Default.WithNames("main", "client")
		if flags.Quiet {
			logger = logger.FilterLevel(log.Critical)
		}
		clientConfig.Logger = logger
	}
	if flags.RequireFastExtension {
		clientConfig.MinPeerExtensions.SetBit(pp.ExtensionBitFast, true)
	}
	clientConfig.MaxUnverifiedBytes = flags.MaxUnverifiedBytes.Int64()

	ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer cancel()

	client, err := torrent.NewClient(clientConfig)
	if err != nil {
		return fmt.Errorf("creating client: %w", err)
	}
	defer client.Close()

	// Write status on the root path on the default HTTP muxer. This will be bound to localhost
	// somewhere if GOPPROF is set, thanks to the envpprof import.
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		client.WriteStatus(w)
	})
	var wg sync.WaitGroup
	err = addTorrents(ctx, client, flags, &wg)
	if err != nil {
		return fmt.Errorf("adding torrents: %w", err)
	}
	started := time.Now()
	defer outputStats(client, flags)
	wg.Wait()
	if ctx.Err() == nil {
		log.Print("downloaded ALL the torrents")
	} else {
		err = errors.New("y u no complete torrents?!")
	}
	clientConnStats := client.ConnStats()
	log.Printf("average download rate: %v",
		humanize.Bytes(uint64(
			time.Duration(
				clientConnStats.BytesReadUsefulData.Int64(),
			)*time.Second/time.Since(started),
		)))
	if flags.Seed {
		if len(client.Torrents()) == 0 {
			log.Print("no torrents to seed")
		} else {
			outputStats(client, flags)
			<-ctx.Done()
		}
	}
	spew.Dump(expvar.Get("torrent").(*expvar.Map).Get("chunks received"))
	spew.Dump(client.ConnStats())
	clStats := client.ConnStats()
	sentOverhead := clStats.BytesWritten.Int64() - clStats.BytesWrittenData.Int64()
	log.Printf(
		"client read %v, %.1f%% was useful data. sent %v non-data bytes",
		humanize.Bytes(uint64(clStats.BytesRead.Int64())),
		100*float64(clStats.BytesReadUsefulData.Int64())/float64(clStats.BytesRead.Int64()),
		humanize.Bytes(uint64(sentOverhead)))
	return err
}

func outputStats(cl *torrent.Client, args downloadFlags) {
	if !statsEnabled(args) {
		return
	}
	expvar.Do(func(kv expvar.KeyValue) {
		fmt.Printf("%s: %s\n", kv.Key, kv.Value)
	})
	cl.WriteStatus(os.Stdout)
}
<|endoftext|>