<commit_before>package osmpbf

import (
	"fmt"
	"io"
	"os"
	"reflect"
	"runtime"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

const (
	// originally downloaded from http://download.geofabrik.de/europe/great-britain/england/greater-london.html
	London    = "greater-london-140324.osm.pbf"
	LondonURL = "https://googledrive.com/host/0B8pisLiGtmqDR3dOR3hrWUpRTVE"
)

var (
	IDsExpectedOrder = []string{
		"node/44", "node/47", "node/52", "node/58", "node/60", // start of dense nodes
		"node/79",                                                                                // just because way/79 is already there
		"node/2740703694", "node/2740703695", "node/2740703697", "node/2740703699", "node/2740703701", // end of dense nodes
		"way/73", "way/74", "way/75", "way/79", "way/482", // start of ways
		"way/268745428", "way/268745431", "way/268745434", "way/268745436", "way/268745439", // end of ways
		"relation/69", "relation/94", "relation/152", "relation/245", "relation/332", // start of relations
		"relation/3593436", "relation/3595575", "relation/3595798", "relation/3599126", "relation/3599127", // end of relations
	}

	IDs map[string]bool

	enc uint64 = 2729006
	ewc uint64 = 459055
	erc uint64 = 12833

	en = &Node{
		ID:  18088578,
		Lat: 51.5442632,
		Lon: -0.2010027,
		Tags: map[string]string{
			"alt_name":   "The King's Head",
			"amenity":    "pub",
			"created_by": "JOSM",
			"name":       "The Luminaire",
			"note":       "Live music venue too",
		},
	}

	ew = &Way{
		ID:      4257116,
		NodeIDs: []int64{21544864, 333731851, 333731852, 333731850, 333731855, 333731858, 333731854, 108047, 769984352, 21544864},
		Tags: map[string]string{
			"area":    "yes",
			"highway": "pedestrian",
			"name":    "Fitzroy Square",
		},
	}

	er = &Relation{
		ID: 7677,
		Members: []Member{
			Member{ID: 4875932, Type: WayType, Role: "outer"},
			Member{ID: 4894305, Type: WayType, Role: "inner"},
		},
		Tags: map[string]string{
			"created_by": "Potlatch 0.9c",
			"type":       "multipolygon",
		},
	}
)

func init() {
	_, err := os.Stat(London)
	if os.IsNotExist(err) {
		panic(fmt.Sprintf("\nDownload %s from %s.\nFor example: 'wget -O %s %s'", London, LondonURL, London, LondonURL))
	}

	IDs = make(map[string]bool)
	for _, id := range IDsExpectedOrder {
		IDs[id] = false
	}
}

func TestDecode(t *testing.T) {
	f, err := os.Open(London)
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	d := NewDecoder(f)
	err = d.Start(runtime.GOMAXPROCS(-1))
	if err != nil {
		t.Fatal(err)
	}

	var n *Node
	var w *Way
	var r *Relation
	var nc, wc, rc uint64
	var id string
	idsOrder := make([]string, 0, len(IDsExpectedOrder))
	for {
		if v, err := d.Decode(); err == io.EOF {
			break
		} else if err != nil {
			t.Fatal(err)
		} else {
			switch v := v.(type) {
			case *Node:
				nc++
				if v.ID == en.ID {
					n = v
				}
				id = fmt.Sprintf("node/%d", v.ID)
				if _, ok := IDs[id]; ok {
					idsOrder = append(idsOrder, id)
				}
			case *Way:
				wc++
				if v.ID == ew.ID {
					w = v
				}
				id = fmt.Sprintf("way/%d", v.ID)
				if _, ok := IDs[id]; ok {
					idsOrder = append(idsOrder, id)
				}
			case *Relation:
				rc++
				if v.ID == er.ID {
					r = v
				}
				id = fmt.Sprintf("relation/%d", v.ID)
				if _, ok := IDs[id]; ok {
					idsOrder = append(idsOrder, id)
				}
			default:
				t.Fatalf("unknown type %T", v)
			}
		}
	}

	if !reflect.DeepEqual(en, n) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", en, n)
	}
	if !reflect.DeepEqual(ew, w) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", ew, w)
	}
	if !reflect.DeepEqual(er, r) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", er, r)
	}
	if enc != nc || ewc != wc || erc != rc {
		t.Errorf("\nExpected %7d nodes, %7d ways, %7d relations\nGot      %7d nodes, %7d ways, %7d relations", enc, ewc, erc, nc, wc, rc)
	}
	if !reflect.DeepEqual(IDsExpectedOrder, idsOrder) {
		t.Errorf("\nExpected: %v\nGot:      %v", IDsExpectedOrder, idsOrder)
	}
}

func TestDecodeConcurrent(t *testing.T) {
	f, err := os.Open(London)
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	d := NewDecoder(f)
	err = d.Start(runtime.GOMAXPROCS(-1))
	if err != nil {
		t.Fatal(err)
	}

	var n *Node
	var w *Way
	var r *Relation
	var nc, wc, rc uint64
	var wg sync.WaitGroup
	for i := 0; i < 4; i++ {
		wg.Add(1)

		go func() {
			for {
				if v, err := d.Decode(); err == io.EOF {
					break
				} else if err != nil {
					t.Fatal(err)
				} else {
					switch v := v.(type) {
					case *Node:
						atomic.AddUint64(&nc, 1)
						if v.ID == en.ID {
							n = v
						}
					case *Way:
						atomic.AddUint64(&wc, 1)
						if v.ID == ew.ID {
							w = v
						}
					case *Relation:
						atomic.AddUint64(&rc, 1)
						if v.ID == er.ID {
							r = v
						}
					default:
						t.Fatalf("unknown type %T", v)
					}
				}
			}

			wg.Done()
		}()
	}
	wg.Wait()

	if !reflect.DeepEqual(en, n) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", en, n)
	}
	if !reflect.DeepEqual(ew, w) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", ew, w)
	}
	if !reflect.DeepEqual(er, r) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", er, r)
	}
	if enc != nc || ewc != wc || erc != rc {
		t.Errorf("\nExpected %7d nodes, %7d ways, %7d relations\nGot      %7d nodes, %7d ways, %7d relations", enc, ewc, erc, nc, wc, rc)
	}
}

func BenchmarkDecode(b *testing.B) {
	file := os.Getenv("OSMPBF_BENCHMARK_FILE")
	if file == "" {
		file = London
	}
	f, err := os.Open(file)
	if err != nil {
		b.Fatal(err)
	}
	defer f.Close()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		f.Seek(0, 0)

		d := NewDecoder(f)
		err = d.Start(runtime.GOMAXPROCS(-1))
		if err != nil {
			b.Fatal(err)
		}

		var nc, wc, rc uint64
		start := time.Now()
		for {
			if v, err := d.Decode(); err == io.EOF {
				break
			} else if err != nil {
				b.Fatal(err)
			} else {
				switch v := v.(type) {
				case *Node:
					nc++
				case *Way:
					wc++
				case *Relation:
					rc++
				default:
					b.Fatalf("unknown type %T", v)
				}
			}
		}

		b.Logf("Done in %.3f seconds. Nodes: %d, Ways: %d, Relations: %d\n",
			time.Now().Sub(start).Seconds(), nc, wc, rc)
	}
}
<commit_msg>Add benchmark for concurrent decode.<commit_after>package osmpbf

import (
	"fmt"
	"io"
	"os"
	"reflect"
	"runtime"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

const (
	// originally downloaded from http://download.geofabrik.de/europe/great-britain/england/greater-london.html
	London    = "greater-london-140324.osm.pbf"
	LondonURL = "https://googledrive.com/host/0B8pisLiGtmqDR3dOR3hrWUpRTVE"
)

var (
	IDsExpectedOrder = []string{
		"node/44", "node/47", "node/52", "node/58", "node/60", // start of dense nodes
		"node/79",                                                                                // just because way/79 is already there
		"node/2740703694", "node/2740703695", "node/2740703697", "node/2740703699", "node/2740703701", // end of dense nodes
		"way/73", "way/74", "way/75", "way/79", "way/482", // start of ways
		"way/268745428", "way/268745431", "way/268745434", "way/268745436", "way/268745439", // end of ways
		"relation/69", "relation/94", "relation/152", "relation/245", "relation/332", // start of relations
		"relation/3593436", "relation/3595575", "relation/3595798", "relation/3599126", "relation/3599127", // end of relations
	}

	IDs map[string]bool

	enc uint64 = 2729006
	ewc uint64 = 459055
	erc uint64 = 12833

	en = &Node{
		ID:  18088578,
		Lat: 51.5442632,
		Lon: -0.2010027,
		Tags: map[string]string{
			"alt_name":   "The King's Head",
			"amenity":    "pub",
			"created_by": "JOSM",
			"name":       "The Luminaire",
			"note":       "Live music venue too",
		},
	}

	ew = &Way{
		ID:      4257116,
		NodeIDs: []int64{21544864, 333731851, 333731852, 333731850, 333731855, 333731858, 333731854, 108047, 769984352, 21544864},
		Tags: map[string]string{
			"area":    "yes",
			"highway": "pedestrian",
			"name":    "Fitzroy Square",
		},
	}

	er = &Relation{
		ID: 7677,
		Members: []Member{
			Member{ID: 4875932, Type: WayType, Role: "outer"},
			Member{ID: 4894305, Type: WayType, Role: "inner"},
		},
		Tags: map[string]string{
			"created_by": "Potlatch 0.9c",
			"type":       "multipolygon",
		},
	}
)

func init() {
	_, err := os.Stat(London)
	if os.IsNotExist(err) {
		panic(fmt.Sprintf("\nDownload %s from %s.\nFor example: 'wget -O %s %s'", London, LondonURL, London, LondonURL))
	}

	IDs = make(map[string]bool)
	for _, id := range IDsExpectedOrder {
		IDs[id] = false
	}
}

func TestDecode(t *testing.T) {
	f, err := os.Open(London)
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	d := NewDecoder(f)
	err = d.Start(runtime.GOMAXPROCS(-1))
	if err != nil {
		t.Fatal(err)
	}

	var n *Node
	var w *Way
	var r *Relation
	var nc, wc, rc uint64
	var id string
	idsOrder := make([]string, 0, len(IDsExpectedOrder))
	for {
		if v, err := d.Decode(); err == io.EOF {
			break
		} else if err != nil {
			t.Fatal(err)
		} else {
			switch v := v.(type) {
			case *Node:
				nc++
				if v.ID == en.ID {
					n = v
				}
				id = fmt.Sprintf("node/%d", v.ID)
				if _, ok := IDs[id]; ok {
					idsOrder = append(idsOrder, id)
				}
			case *Way:
				wc++
				if v.ID == ew.ID {
					w = v
				}
				id = fmt.Sprintf("way/%d", v.ID)
				if _, ok := IDs[id]; ok {
					idsOrder = append(idsOrder, id)
				}
			case *Relation:
				rc++
				if v.ID == er.ID {
					r = v
				}
				id = fmt.Sprintf("relation/%d", v.ID)
				if _, ok := IDs[id]; ok {
					idsOrder = append(idsOrder, id)
				}
			default:
				t.Fatalf("unknown type %T", v)
			}
		}
	}

	if !reflect.DeepEqual(en, n) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", en, n)
	}
	if !reflect.DeepEqual(ew, w) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", ew, w)
	}
	if !reflect.DeepEqual(er, r) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", er, r)
	}
	if enc != nc || ewc != wc || erc != rc {
		t.Errorf("\nExpected %7d nodes, %7d ways, %7d relations\nGot      %7d nodes, %7d ways, %7d relations", enc, ewc, erc, nc, wc, rc)
	}
	if !reflect.DeepEqual(IDsExpectedOrder, idsOrder) {
		t.Errorf("\nExpected: %v\nGot:      %v", IDsExpectedOrder, idsOrder)
	}
}

func TestDecodeConcurrent(t *testing.T) {
	f, err := os.Open(London)
	if err != nil {
		t.Fatal(err)
	}
	defer f.Close()

	d := NewDecoder(f)
	err = d.Start(runtime.GOMAXPROCS(-1))
	if err != nil {
		t.Fatal(err)
	}

	var n *Node
	var w *Way
	var r *Relation
	var nc, wc, rc uint64
	var wg sync.WaitGroup
	for i := 0; i < 4; i++ {
		wg.Add(1)

		go func() {
			for {
				if v, err := d.Decode(); err == io.EOF {
					break
				} else if err != nil {
					t.Fatal(err)
				} else {
					switch v := v.(type) {
					case *Node:
						atomic.AddUint64(&nc, 1)
						if v.ID == en.ID {
							n = v
						}
					case *Way:
						atomic.AddUint64(&wc, 1)
						if v.ID == ew.ID {
							w = v
						}
					case *Relation:
						atomic.AddUint64(&rc, 1)
						if v.ID == er.ID {
							r = v
						}
					default:
						t.Fatalf("unknown type %T", v)
					}
				}
			}

			wg.Done()
		}()
	}
	wg.Wait()

	if !reflect.DeepEqual(en, n) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", en, n)
	}
	if !reflect.DeepEqual(ew, w) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", ew, w)
	}
	if !reflect.DeepEqual(er, r) {
		t.Errorf("\nExpected: %#v\nActual:   %#v", er, r)
	}
	if enc != nc || ewc != wc || erc != rc {
		t.Errorf("\nExpected %7d nodes, %7d ways, %7d relations\nGot      %7d nodes, %7d ways, %7d relations", enc, ewc, erc, nc, wc, rc)
	}
}

func BenchmarkDecode(b *testing.B) {
	file := os.Getenv("OSMPBF_BENCHMARK_FILE")
	if file == "" {
		file = London
	}
	f, err := os.Open(file)
	if err != nil {
		b.Fatal(err)
	}
	defer f.Close()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		f.Seek(0, 0)

		d := NewDecoder(f)
		err = d.Start(runtime.GOMAXPROCS(-1))
		if err != nil {
			b.Fatal(err)
		}

		var nc, wc, rc uint64
		start := time.Now()
		for {
			if v, err := d.Decode(); err == io.EOF {
				break
			} else if err != nil {
				b.Fatal(err)
			} else {
				switch v := v.(type) {
				case *Node:
					nc++
				case *Way:
					wc++
				case *Relation:
					rc++
				default:
					b.Fatalf("unknown type %T", v)
				}
			}
		}

		b.Logf("Done in %.3f seconds. Nodes: %d, Ways: %d, Relations: %d\n",
			time.Now().Sub(start).Seconds(), nc, wc, rc)
	}
}

func BenchmarkDecodeConcurrent(b *testing.B) {
	file := os.Getenv("OSMPBF_BENCHMARK_FILE")
	if file == "" {
		file = London
	}
	f, err := os.Open(file)
	if err != nil {
		b.Fatal(err)
	}
	defer f.Close()

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		f.Seek(0, 0)

		d := NewDecoder(f)
		err = d.Start(runtime.GOMAXPROCS(-1))
		if err != nil {
			b.Fatal(err)
		}

		var nc, wc, rc uint64
		start := time.Now()
		var wg sync.WaitGroup
		for i := 0; i < 4; i++ {
			wg.Add(1)

			go func() {
				for {
					if v, err := d.Decode(); err == io.EOF {
						break
					} else if err != nil {
						b.Fatal(err)
					} else {
						switch v := v.(type) {
						case *Node:
							atomic.AddUint64(&nc, 1)
						case *Way:
							atomic.AddUint64(&wc, 1)
						case *Relation:
							atomic.AddUint64(&rc, 1)
						default:
							b.Fatalf("unknown type %T", v)
						}
					}
				}

				wg.Done()
			}()
		}
		wg.Wait()

		b.Logf("Done in %.3f seconds. Nodes: %d, Ways: %d, Relations: %d\n",
			time.Now().Sub(start).Seconds(), nc, wc, rc)
	}
}
<|endoftext|>


<commit_before>// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package astview

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/visualfc/gotools/command"
	"github.com/visualfc/gotools/pkgutil"
)

var Command = &command.Command{
	Run:       runAstView,
	UsageLine: "astview [-stdin] files...",
	Short:     "print go files astview",
	Long:      `print go files astview`,
}

var astViewStdin bool

func init() {
	Command.Flag.BoolVar(&astViewStdin, "stdin", false, "input from stdin")
}

func runAstView(cmd *command.Command, args []string) error {
	if len(args) == 0 {
		cmd.Usage()
		return os.ErrInvalid
	}
	if astViewStdin {
		view, err := NewFilePackageSource(args[0], os.Stdin, true)
		if err != nil {
			fmt.Fprintf(os.Stderr, "astview: %s", err)
			command.SetExitStatus(3)
			command.Exit()
		}
		view.PrintTree(os.Stdout)
	} else {
		err := PrintFilesTree(args, os.Stdout, true)
		if err != nil {
			fmt.Fprintf(os.Stderr, "astview:%s", err)
			command.SetExitStatus(3)
			command.Exit()
		}
	}
	return nil
}

const (
	tag_package        = "p"
	tag_imports_folder = "+m"
	tag_import         = "mm"
	tag_type           = "t"
	tag_struct         = "s"
	tag_interface      = "i"
	tag_value          = "v"
	tag_const          = "c"
	tag_func           = "f"
	tag_value_folder   = "+v"
	tag_const_folder   = "+c"
	tag_func_folder    = "+f"
	tag_factor_folder  = "+tf"
	tag_type_method    = "tm"
	tag_type_factor    = "tf"
	tag_type_value     = "tv"
)

type PackageView struct {
	fset *token.FileSet
	pdoc *PackageDoc
	pkg  *ast.Package
	expr bool
}

var AllFiles []string

func (p *PackageView) posFileIndex(pos token.Position) int {
	var index = -1
	for i := 0; i < len(AllFiles); i++ {
		if AllFiles[i] == pos.Filename {
			index = i
			break
		}
	}
	if index == -1 {
		AllFiles = append(AllFiles, pos.Filename)
		index = len(AllFiles) - 1
	}
	return index
}

func (p *PackageView) posText(pos token.Position) (s string) {
	index := p.posFileIndex(pos)
	return fmt.Sprintf("%d:%d:%d", index, pos.Line, pos.Column)
}

func NewFilePackage(filename string) (*PackageView, error) {
	p := new(PackageView)
	p.fset = token.NewFileSet()
	file, err := parser.ParseFile(p.fset, filename, nil, parser.AllErrors)
	if file == nil {
		return nil, err
	}
	m := make(map[string]*ast.File)
	m[filename] = file
	pkg, err := ast.NewPackage(p.fset, m, nil, nil)
	if err != nil {
		return nil, err
	}
	p.pkg = pkg
	p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
	return p, nil
}

func NewPackageView(pkg *ast.Package, fset *token.FileSet, expr bool) (*PackageView, error) {
	p := new(PackageView)
	p.fset = fset
	p.pkg = pkg
	p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
	p.expr = expr
	return p, nil
}

func ParseFiles(fset *token.FileSet, filenames []string, mode parser.Mode) (pkgs map[string]*ast.Package, pkgsfiles []string, first error) {
	pkgs = make(map[string]*ast.Package)
	for _, filename := range filenames {
		if src, err := parser.ParseFile(fset, filename, nil, mode); src != nil {
			name := src.Name.Name
			pkg, found := pkgs[name]
			if !found {
				pkg = &ast.Package{
					Name:  name,
					Files: make(map[string]*ast.File),
				}
				pkgs[name] = pkg
			}
			pkg.Files[filename] = src
			pkgsfiles = append(pkgsfiles, filename)
		} else {
			first = err
			return
		}
	}
	return
}

func PrintFilesTree(filenames []string, w io.Writer, expr bool) error {
	fset := token.NewFileSet()
	pkgs, pkgsfiles, err := ParseFiles(fset, filenames, parser.AllErrors)
	if err != nil {
		return err
	}
	AllFiles = pkgsfiles
	for i := 0; i < len(AllFiles); i++ {
		fmt.Fprintf(w, "@%s\n", AllFiles[i])
	}
	for _, pkg := range pkgs {
		view, err := NewPackageView(pkg, fset, expr)
		if err != nil {
			return err
		}
		view.PrintTree(w)
	}
	return nil
}

func NewFilePackageSource(filename string, f *os.File, expr bool) (*PackageView, error) {
	src, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}
	p := new(PackageView)
	p.fset = token.NewFileSet()
	p.expr = expr
	file, err := parser.ParseFile(p.fset, filename, src, 0)
	if err != nil {
		return nil, err
	}
	m := make(map[string]*ast.File)
	m[filename] = file
	pkg, err := ast.NewPackage(p.fset, m, nil, nil)
	if err != nil {
		return nil, err
	}

	p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
	return p, nil
}

func (p *PackageView) printFuncsHelper(w io.Writer, funcs []*FuncDoc, level int, tag string, tag_folder string) {
	for _, f := range funcs {
		pos := p.fset.Position(f.Decl.Pos())
		if p.expr {
			fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, f.Name, p.posText(pos), types.ExprString(f.Decl.Type))
		} else {
			fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, f.Name, p.posText(pos))
		}
	}
}

func (p *PackageView) PrintVars(w io.Writer, vars []*ValueDoc, level int, tag string, tag_folder string) {
	if len(tag_folder) > 0 && len(vars) > 0 {
		if tag_folder == tag_value_folder {
			fmt.Fprintf(w, "%d,%s,Variables\n", level, tag_folder)
		} else if tag_folder == tag_const_folder {
			fmt.Fprintf(w, "%d,%s,Constants\n", level, tag_folder)
		}
		level++
	}
	for _, v := range vars {
		if v.Decl == nil {
			continue
		}
		for _, s := range v.Decl.Specs {
			if m, ok := s.(*ast.ValueSpec); ok {
				pos := p.fset.Position(m.Pos())
				for i := 0; i < len(m.Names); i++ {
					if p.expr && m.Type != nil {
						fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, m.Names[i], p.posText(pos), types.ExprString(m.Type))
					} else {
						fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, m.Names[i], p.posText(pos))
					}
				}
			}
		}
	}
}
func (p *PackageView) PrintTypes(w io.Writer, types []*TypeDoc, level int) {
	for _, d := range types {
		if d.Decl == nil {
			continue
		}
		typespec := d.Decl.Specs[0].(*ast.TypeSpec)
		var tag = tag_type
		if _, ok := typespec.Type.(*ast.InterfaceType); ok {
			tag = tag_interface
		} else if _, ok := typespec.Type.(*ast.StructType); ok {
			tag = tag_struct
		}
		pos := p.fset.Position(d.Decl.Pos())
		fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, d.Type.Name, p.posText(pos))
		p.printFuncsHelper(w, d.Funcs, level+1, tag_type_factor, "")
		p.printFuncsHelper(w, d.Methods, level+1, tag_type_method, "")
		p.PrintTypeFields(w, d.Decl, level+1)
		//p.PrintVars(w, d.Consts, level+1, tag_const, "")
		//p.PrintVars(w, d.Vars, level+1, tag_value, "")
	}
}

func (p *PackageView) PrintTypeFields(w io.Writer, decl *ast.GenDecl, level int) {
	spec, ok := decl.Specs[0].(*ast.TypeSpec)
	if ok == false {
		return
	}
	switch d := spec.Type.(type) {
	case *ast.StructType:
		for _, list := range d.Fields.List {
			if list.Names == nil {
				continue
			}
			for _, m := range list.Names {
				pos := p.fset.Position(m.Pos())
				if list.Type != nil {
					fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag_type_value, m.Name, p.posText(pos), types.ExprString(list.Type))
				} else {
					fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_value, m.Name, p.posText(pos))
				}
			}
		}
	case *ast.InterfaceType:
		for _, list := range d.Methods.List {
			if list.Names == nil {
				continue
			}
			for _, m := range list.Names {
				pos := p.fset.Position(m.Pos())
				fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_method, m.Name, p.posText(pos))
			}
		}
	}
}

func (p *PackageView) PrintHeader(w io.Writer, level int) {
	fmt.Fprintf(w, "%d,%s,%s\n", level, tag_package, p.pdoc.PackageName)
}

func (p *PackageView) PrintImports(w io.Writer, level int, tag, tag_folder string) {
	if tag_folder != "" && len(p.pdoc.Imports) > 0 {
		fmt.Fprintf(w, "%d,%s,%s\n", level, tag_folder, "Imports")
		level++
	}
	var parentPkg *pkgutil.Package
	if pkgutil.IsVendorExperiment() {
		for filename, _ := range p.pkg.Files {
			if !filepath.IsAbs(filename) {
				name, err := filepath.Abs(filename)
				if err == nil {
					filename = name
				}
			}
			parentPkg = pkgutil.ImportFile(filename)
			break
		}
	}
	for _, name := range p.pdoc.Imports {
		vname := "\"" + name + "\""
		var ps []string
		for _, file := range p.pkg.Files {
			for _, v := range file.Imports {
				if v.Path.Value == vname {
					pos := p.fset.Position(v.Pos())
					ps = append(ps, p.posText(pos))
				}
			}
		}
		if parentPkg != nil {
			name = pkgutil.VendoredImportPath(parentPkg, name)
		}
		fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, name, strings.Join(ps, ";"))
	}
}

func (p *PackageView) PrintFuncs(w io.Writer, level int, tag_folder string) {
	hasFolder := false
	if len(p.pdoc.Funcs) > 0 || len(p.pdoc.Factorys) > 0 {
		hasFolder = true
	}
	if !hasFolder {
		return
	}
	if len(tag_folder) > 0 {
		fmt.Fprintf(w, "%d,%s,Functions\n", level, tag_folder)
		level++
	}
	p.printFuncsHelper(w, p.pdoc.Factorys, level, tag_type_factor, tag_func_folder)
	p.printFuncsHelper(w, p.pdoc.Funcs, level, tag_func, tag_func_folder)
}

func (p *PackageView) PrintPackage(w io.Writer, level int) {
	p.PrintHeader(w, level)
	level++
	p.PrintImports(w, level, tag_import, tag_imports_folder)
	p.PrintVars(w, p.pdoc.Vars, level, tag_value, tag_value_folder)
	p.PrintVars(w, p.pdoc.Consts, level, tag_const, tag_const_folder)
	p.PrintFuncs(w, level, tag_func_folder)
	p.PrintTypes(w, p.pdoc.Types, level)
}

// level,tag,pos@info
func (p *PackageView) PrintTree(w io.Writer) {
	p.PrintPackage(w, 0)
}
<commit_msg>astview add -end flag<commit_after>// Copyright 2011-2015 visualfc <visualfc@gmail.com>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package astview

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/visualfc/gotools/command"
	"github.com/visualfc/gotools/pkgutil"
)

var Command = &command.Command{
	Run:       runAstView,
	UsageLine: "astview [-stdin] files...",
	Short:     "print go files astview",
	Long:      `print go files astview`,
}

var astViewStdin bool
var astViewShowEndPos bool

func init() {
	Command.Flag.BoolVar(&astViewStdin, "stdin", false, "input from stdin")
	Command.Flag.BoolVar(&astViewShowEndPos, "end", false, "show decl end pos")
}

func runAstView(cmd *command.Command, args []string) error {
	if len(args) == 0 {
		cmd.Usage()
		return os.ErrInvalid
	}
	if astViewStdin {
		view, err := NewFilePackageSource(args[0], os.Stdin, true)
		if err != nil {
			fmt.Fprintf(os.Stderr, "astview: %s", err)
			command.SetExitStatus(3)
			command.Exit()
		}
		view.PrintTree(os.Stdout)
	} else {
		err := PrintFilesTree(args, os.Stdout, true)
		if err != nil {
			fmt.Fprintf(os.Stderr, "astview:%s", err)
			command.SetExitStatus(3)
			command.Exit()
		}
	}
	return nil
}

const (
	tag_package        = "p"
	tag_imports_folder = "+m"
	tag_import         = "mm"
	tag_type           = "t"
	tag_struct         = "s"
	tag_interface      = "i"
	tag_value          = "v"
	tag_const          = "c"
	tag_func           = "f"
	tag_value_folder   = "+v"
	tag_const_folder   = "+c"
	tag_func_folder    = "+f"
	tag_factor_folder  = "+tf"
	tag_type_method    = "tm"
	tag_type_factor    = "tf"
	tag_type_value     = "tv"
)

type PackageView struct {
	fset *token.FileSet
	pdoc *PackageDoc
	pkg  *ast.Package
	expr bool
}

var AllFiles []string

func (p *PackageView) posFileIndex(pos token.Position) int {
	var index = -1
	for i := 0; i < len(AllFiles); i++ {
		if AllFiles[i] == pos.Filename {
			index = i
			break
		}
	}
	if index == -1 {
		AllFiles = append(AllFiles, pos.Filename)
		index = len(AllFiles) - 1
	}
	return index
}

func (p *PackageView) posText(pos token.Position, end token.Position) (s string) {
	index := p.posFileIndex(pos)
	if astViewShowEndPos {
		return fmt.Sprintf("%d:%d:%d:%d:%d", index, pos.Line, pos.Column, end.Line, end.Column)
	}
	return fmt.Sprintf("%d:%d:%d", index, pos.Line, pos.Column)
}

func NewFilePackage(filename string) (*PackageView, error) {
	p := new(PackageView)
	p.fset = token.NewFileSet()
	file, err := parser.ParseFile(p.fset, filename, nil, parser.AllErrors)
	if file == nil {
		return nil, err
	}
	m := make(map[string]*ast.File)
	m[filename] = file
	pkg, err := ast.NewPackage(p.fset, m, nil, nil)
	if err != nil {
		return nil, err
	}
	p.pkg = pkg
	p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
	return p, nil
}

func NewPackageView(pkg *ast.Package, fset *token.FileSet, expr bool) (*PackageView, error) {
	p := new(PackageView)
	p.fset = fset
	p.pkg = pkg
	p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
	p.expr = expr
	return p, nil
}

func ParseFiles(fset *token.FileSet, filenames []string, mode parser.Mode) (pkgs map[string]*ast.Package, pkgsfiles []string, first error) {
	pkgs = make(map[string]*ast.Package)
	for _, filename := range filenames {
		if src, err := parser.ParseFile(fset, filename, nil, mode); src != nil {
			name := src.Name.Name
			pkg, found := pkgs[name]
			if !found {
				pkg = &ast.Package{
					Name:  name,
					Files: make(map[string]*ast.File),
				}
				pkgs[name] = pkg
			}
			pkg.Files[filename] = src
			pkgsfiles = append(pkgsfiles, filename)
		} else {
			first = err
			return
		}
	}
	return
}

func PrintFilesTree(filenames []string, w io.Writer, expr bool) error {
	fset := token.NewFileSet()
	pkgs, pkgsfiles, err := ParseFiles(fset, filenames, parser.AllErrors)
	if err != nil {
		return err
	}
	AllFiles = pkgsfiles
	for i := 0; i < len(AllFiles); i++ {
		fmt.Fprintf(w, "@%s\n", AllFiles[i])
	}
	for _, pkg := range pkgs {
		view, err := NewPackageView(pkg, fset, expr)
		if err != nil {
			return err
		}
		view.PrintTree(w)
	}
	return nil
}

func NewFilePackageSource(filename string, f *os.File, expr bool) (*PackageView, error) {
	src, err := ioutil.ReadAll(f)
	if err != nil {
		return nil, err
	}
	p := new(PackageView)
	p.fset = token.NewFileSet()
	p.expr = expr
	file, err := parser.ParseFile(p.fset, filename, src, 0)
	if err != nil {
		return nil, err
	}
	m := make(map[string]*ast.File)
	m[filename] = file
	pkg, err := ast.NewPackage(p.fset, m, nil, nil)
	if err != nil {
		return nil, err
	}

	p.pdoc = NewPackageDoc(pkg, pkg.Name, true)
	return p, nil
}

func (p *PackageView) printFuncsHelper(w io.Writer, funcs []*FuncDoc, level int, tag string, tag_folder string) {
	for _, f := range funcs {
		pos := p.fset.Position(f.Decl.Pos())
		end := p.fset.Position(f.Decl.End())
		if p.expr {
			fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, f.Name, p.posText(pos, end), types.ExprString(f.Decl.Type))
		} else {
			fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, f.Name, p.posText(pos, end))
		}
	}
}

func (p *PackageView) PrintVars(w io.Writer, vars []*ValueDoc, level int, tag string, tag_folder string) {
	if len(tag_folder) > 0 && len(vars) > 0 {
		if tag_folder == tag_value_folder {
			fmt.Fprintf(w, "%d,%s,Variables\n", level, tag_folder)
		} else if tag_folder == tag_const_folder {
			fmt.Fprintf(w, "%d,%s,Constants\n", level, tag_folder)
		}
		level++
	}
	for _, v := range vars {
		if v.Decl == nil {
			continue
		}
		for _, s := range v.Decl.Specs {
			if m, ok := s.(*ast.ValueSpec); ok {
				pos := p.fset.Position(m.Pos())
				end := p.fset.Position(m.End())
				for i := 0; i < len(m.Names); i++ {
					if p.expr && m.Type != nil {
						fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag, m.Names[i], p.posText(pos, end), types.ExprString(m.Type))
					} else {
						fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, m.Names[i], p.posText(pos, end))
					}
				}
			}
		}
	}
}
func (p *PackageView) PrintTypes(w io.Writer, types []*TypeDoc, level int) {
	for _, d := range types {
		if d.Decl == nil {
			continue
		}
		typespec := d.Decl.Specs[0].(*ast.TypeSpec)
		var tag = tag_type
		if _, ok := typespec.Type.(*ast.InterfaceType); ok {
			tag = tag_interface
		} else if _, ok := typespec.Type.(*ast.StructType); ok {
			tag = tag_struct
		}
		pos := p.fset.Position(d.Decl.Pos())
		end := p.fset.Position(d.Decl.End())
		fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, d.Type.Name, p.posText(pos, end))
		p.printFuncsHelper(w, d.Funcs, level+1, tag_type_factor, "")
		p.printFuncsHelper(w, d.Methods, level+1, tag_type_method, "")
		p.PrintTypeFields(w, d.Decl, level+1)
		//p.PrintVars(w, d.Consts, level+1, tag_const, "")
		//p.PrintVars(w, d.Vars, level+1, tag_value, "")
	}
}

func (p *PackageView) PrintTypeFields(w io.Writer, decl *ast.GenDecl, level int) {
	spec, ok := decl.Specs[0].(*ast.TypeSpec)
	if ok == false {
		return
	}
	switch d := spec.Type.(type) {
	case *ast.StructType:
		for _, list := range d.Fields.List {
			if list.Names == nil {
				continue
			}
			for _, m := range list.Names {
				pos := p.fset.Position(m.Pos())
				end := p.fset.Position(m.End())
				if list.Type != nil {
					fmt.Fprintf(w, "%d,%s,%s,%s@%s\n", level, tag_type_value, m.Name, p.posText(pos, end), types.ExprString(list.Type))
				} else {
					fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_value, m.Name, p.posText(pos, end))
				}
			}
		}
	case *ast.InterfaceType:
		for _, list := range d.Methods.List {
			if list.Names == nil {
				continue
			}
			for _, m := range list.Names {
				pos := p.fset.Position(m.Pos())
				end := p.fset.Position(m.End())
				fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag_type_method, m.Name, p.posText(pos, end))
			}
		}
	}
}

func (p *PackageView) PrintHeader(w io.Writer, level int) {
	fmt.Fprintf(w, "%d,%s,%s\n", level, tag_package, p.pdoc.PackageName)
}

func (p *PackageView) PrintImports(w io.Writer, level int, tag, tag_folder string) {
	if tag_folder != "" && len(p.pdoc.Imports) > 0 {
		fmt.Fprintf(w, "%d,%s,%s\n", level, tag_folder, "Imports")
		level++
	}
	var parentPkg *pkgutil.Package
	if pkgutil.IsVendorExperiment() {
		for filename, _ := range p.pkg.Files {
			if !filepath.IsAbs(filename) {
				name, err := filepath.Abs(filename)
				if err == nil {
					filename = name
				}
			}
			parentPkg = pkgutil.ImportFile(filename)
			break
		}
	}
	for _, name := range p.pdoc.Imports {
		vname := "\"" + name + "\""
		var ps []string
		for _, file := range p.pkg.Files {
			for _, v := range file.Imports {
				if v.Path.Value == vname {
					pos := p.fset.Position(v.Pos())
					end := p.fset.Position(v.End())
					ps = append(ps, p.posText(pos, end))
				}
			}
		}
		if parentPkg != nil {
			name = pkgutil.VendoredImportPath(parentPkg, name)
		}
		fmt.Fprintf(w, "%d,%s,%s,%s\n", level, tag, name, strings.Join(ps, ";"))
	}
}

func (p *PackageView) PrintFuncs(w io.Writer, level int, tag_folder string) {
	hasFolder := false
	if len(p.pdoc.Funcs) > 0 || len(p.pdoc.Factorys) > 0 {
		hasFolder = true
	}
	if !hasFolder {
		return
	}
	if len(tag_folder) > 0 {
		fmt.Fprintf(w, "%d,%s,Functions\n", level, tag_folder)
		level++
	}
	p.printFuncsHelper(w, p.pdoc.Factorys, level, tag_type_factor, tag_func_folder)
	p.printFuncsHelper(w, p.pdoc.Funcs, level, tag_func, tag_func_folder)
}

func (p *PackageView) PrintPackage(w io.Writer, level int) {
	p.PrintHeader(w, level)
	level++
	p.PrintImports(w, level, tag_import, tag_imports_folder)
	p.PrintVars(w, p.pdoc.Vars, level, tag_value, tag_value_folder)
	p.PrintVars(w, p.pdoc.Consts, level, tag_const, tag_const_folder)
	p.PrintFuncs(w, level, tag_func_folder)
	p.PrintTypes(w, p.pdoc.Types, level)
}

// level,tag,pos@info
func (p *PackageView) PrintTree(w io.Writer) {
	p.PrintPackage(w, 0)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/hanwen/termite/cba"
	"github.com/hanwen/termite/termite"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"os/user"
	"runtime"
	"runtime/pprof"
	"syscall"
)

var _ = log.Printf

func handleStop(daemon *termite.Worker) {
	for {
		sig := <-signal.Incoming
		switch sig.(os.UnixSignal) {
		case syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT:
			log.Println("got signal: ", sig)
			req := termite.ShutdownRequest{Kill: true}
			rep := termite.ShutdownResponse{}
			daemon.Shutdown(&req, &rep)
		case syscall.SIGHUP:
			daemon.Report()
		}
	}
}

func main() {
	cachedir := flag.String("cachedir", "/var/cache/termite/worker-cache", "content cache")
	tmpdir := flag.String("tmpdir", "/var/tmp",
		"where to create FUSE mounts; should be on same partition as cachedir.")
	secretFile := flag.String("secret", "secret.txt", "file containing password.")
	port := flag.Int("port", 1232, "Start of port to try.")
	portRetry := flag.Int("port-retry", 10, "How many other ports to try.")
	coordinator := flag.String("coordinator", "", "Where to register the worker.")
	jobs := flag.Int("jobs", 1, "Max number of jobs to run.")
	reapcount := flag.Int("reap-count", 1, "Number of jobs per filesystem.")
	userFlag := flag.String("user", "nobody", "Run as this user.")
	memcache := flag.Int("filecache", 1024, "number of <128k files to cache in memory")
	logfile := flag.String("logfile", "", "Output log file to use.")
	paranoia := flag.Bool("paranoia", false, "Check attribute cache.")
	cpus := flag.Int("cpus", 1, "Number of CPUs to use.")
	heap := flag.Int("heap-size", 0, "Maximum heap size in MB.")
	cpuprofile := flag.String("profile", "", "File to write profile output to.")
	flag.Parse()

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if os.Geteuid() != 0 {
		log.Fatal("This program must run as root")
	}
	secret, err := ioutil.ReadFile(*secretFile)
	if err != nil {
		log.Fatal("ReadFile", err)
	}

	if *logfile != "" {
		f, err := os.OpenFile(*logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			log.Fatal("Could not open log file.", err)
		}
		log.Println("Log output to", *logfile)
		log.SetOutput(f)
	} else {
		log.SetPrefix("W")
	}

	opts := termite.WorkerOptions{
		Secret:      secret,
		TempDir:     *tmpdir,
		Jobs:        *jobs,
		Paranoia:    *paranoia,
		ReapCount:   *reapcount,
		LogFileName: *logfile,
		StoreOptions: cba.StoreOptions{
			Dir:      *cachedir,
			MemCount: *memcache,
		},
		HeapLimit:   uint64(*heap) * (1 << 20),
		Coordinator: *coordinator,
		Port:        *port,
		PortRetry:   *portRetry,
	}
	if os.Geteuid() == 0 {
		nobody, err := user.Lookup(*userFlag)
		if err != nil {
			log.Fatalf("can't lookup %q: %v", *userFlag, err)
		}
		opts.User = nobody
	}

	daemon := termite.NewWorker(&opts)
	if *cpus > 0 {
		runtime.GOMAXPROCS(*cpus)
	}
	log.Printf("%s on %d CPUs", termite.Version(), runtime.GOMAXPROCS(0))
	go handleStop(daemon)
	daemon.RunWorkerServer()
}
<commit_msg>Redirect stderr to a file.<commit_after>package main

import (
	"flag"
	"github.com/hanwen/termite/cba"
	"github.com/hanwen/termite/termite"
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"os/user"
	"runtime"
	"runtime/pprof"
	"syscall"
)

var _ = log.Printf

func handleStop(daemon *termite.Worker) {
	for {
		sig := <-signal.Incoming
		switch sig.(os.UnixSignal) {
		case syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT:
			log.Println("got signal: ", sig)
			req := termite.ShutdownRequest{Kill: true}
			rep := termite.ShutdownResponse{}
			daemon.Shutdown(&req, &rep)
		case syscall.SIGHUP:
			daemon.Report()
		}
	}
}

func main() {
	cachedir := flag.String("cachedir", "/var/cache/termite/worker-cache", "content cache")
	tmpdir := flag.String("tmpdir", "/var/tmp",
		"where to create FUSE mounts; should be on same partition as cachedir.")
	secretFile := flag.String("secret", "secret.txt", "file containing password.")
	port := flag.Int("port", 1232, "Start of port to try.")
	portRetry := flag.Int("port-retry", 10, "How many other ports to try.")
	coordinator := flag.String("coordinator", "", "Where to register the worker.")
	jobs := flag.Int("jobs", 1, "Max number of jobs to run.")
	reapcount := flag.Int("reap-count", 1, "Number of jobs per filesystem.")
	userFlag := flag.String("user", "nobody", "Run as this user.")
	memcache := flag.Int("filecache", 1024, "number of <128k files to cache in memory")
	logfile := flag.String("logfile", "", "Output log file to use.")
	stderrFile := flag.String("stderr", "", "File to write stderr output to.")
	paranoia := flag.Bool("paranoia", false, "Check attribute cache.")
	cpus := flag.Int("cpus", 1, "Number of CPUs to use.")
	heap := flag.Int("heap-size", 0, "Maximum heap size in MB.")
	cpuprofile := flag.String("profile", "", "File to write profile output to.")
	flag.Parse()

	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	if os.Geteuid() != 0 {
		log.Fatal("This program must run as root")
	}
	secret, err := ioutil.ReadFile(*secretFile)
	if err != nil {
		log.Fatal("ReadFile", err)
	}

	if *logfile != "" {
		f, err := os.OpenFile(*logfile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			log.Fatal("Could not open log file.", err)
		}
		log.Println("Log output to", *logfile)
		log.SetOutput(f)
	} else {
		log.SetPrefix("W")
	}

	if *stderrFile != "" {
		stderr, err := os.OpenFile(*stderrFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
		if err != nil {
			log.Fatal("Could not open stderr file", err)
		}
		err = syscall.Close(2)
		if err != nil {
			log.Fatalf("close stderr: %v", err)
		}
		_, err = syscall.Dup(stderr.Fd())
		if err != nil {
			log.Fatalf("dup: %v", err)
		}
		stderr.Close()
	}

	opts := termite.WorkerOptions{
		Secret:      secret,
		TempDir:     *tmpdir,
		Jobs:        *jobs,
		Paranoia:    *paranoia,
		ReapCount:   *reapcount,
		LogFileName: *logfile,
		StoreOptions: cba.StoreOptions{
			Dir:      *cachedir,
			MemCount: *memcache,
		},
		HeapLimit:   uint64(*heap) * (1 << 20),
		Coordinator: *coordinator,
		Port:        *port,
		PortRetry:   *portRetry,
	}
	if os.Geteuid() == 0 {
		nobody, err := user.Lookup(*userFlag)
		if err != nil {
			log.Fatalf("can't lookup %q: %v", *userFlag, err)
		}
		opts.User = nobody
	}

	daemon := termite.NewWorker(&opts)
	if *cpus > 0 {
		runtime.GOMAXPROCS(*cpus)
	}
	log.Printf("%s on %d CPUs", termite.Version(), runtime.GOMAXPROCS(0))
	go handleStop(daemon)
	daemon.RunWorkerServer()
}
<|endoftext|>


<commit_before>package version

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	path "path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/beego/bee/cmd/commands"
	beeLogger "github.com/beego/bee/logger"
	"github.com/beego/bee/logger/colors"
	"gopkg.in/yaml.v2"
)

const verboseVersionBanner string = `%s%s______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v{{ .BeeVersion }}%s
%s%s
├── Beego     : {{ .BeegoVersion }}
├── GoVersion : {{ .GoVersion }}
├── GOOS      : {{ .GOOS }}
├── GOARCH    : {{ .GOARCH }}
├── NumCPU    : {{ .NumCPU }}
├── GOPATH    : {{ .GOPATH }}
├── GOROOT    : {{ .GOROOT }}
├── Compiler  : {{ .Compiler }}
└── Date      : {{ Now "Monday, 2 Jan 2006" }}%s
`

const shortVersionBanner = `______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v{{ .BeeVersion }}
`

var CmdVersion = &commands.Command{
	UsageLine: "version",
	Short:     "Prints the current Bee version",
	Long: `
Prints the current Bee, Beego and Go version alongside the platform information.
`,
	Run: versionCmd,
}
var outputFormat string

const version = "1.8.1"

func init() {
	fs := flag.NewFlagSet("version", flag.ContinueOnError)
	fs.StringVar(&outputFormat, "o", "", "Set the output format. Either json or yaml.")
	CmdVersion.Flag = *fs
	commands.AvailableCommands = append(commands.AvailableCommands, CmdVersion)
}

func versionCmd(cmd *commands.Command, args []string) int {

	cmd.Flag.Parse(args)
	stdout := cmd.Out()

	if outputFormat != "" {
		runtimeInfo := RuntimeInfo{
			GetGoVersion(),
			runtime.GOOS,
			runtime.GOARCH,
			runtime.NumCPU(),
			os.Getenv("GOPATH"),
			runtime.GOROOT(),
			runtime.Compiler,
			version,
			GetBeegoVersion(),
		}
		switch outputFormat {
		case "json":
			{
				b, err := json.MarshalIndent(runtimeInfo, "", "    ")
				if err != nil {
					beeLogger.Log.Error(err.Error())
				}
				fmt.Println(string(b))
				return 0
			}
		case "yaml":
			{
				b, err := yaml.Marshal(&runtimeInfo)
				if err != nil {
					beeLogger.Log.Error(err.Error())
				}
				fmt.Println(string(b))
				return 0
			}
		}
	}

	coloredBanner := fmt.Sprintf(verboseVersionBanner, "\x1b[35m", "\x1b[1m",
		"\x1b[0m", "\x1b[32m", "\x1b[1m", "\x1b[0m")
	InitBanner(stdout, bytes.NewBufferString(coloredBanner))
	return 0
}

// ShowShortVersionBanner prints the short version banner.
func ShowShortVersionBanner() {
	output := colors.NewColorWriter(os.Stdout)
	InitBanner(output, bytes.NewBufferString(colors.MagentaBold(shortVersionBanner)))
}

func GetBeegoVersion() string {
	gopath := os.Getenv("GOPATH")
	re, err := regexp.Compile(`VERSION = "([0-9.]+)"`)
	if err != nil {
		return ""
	}
	if gopath == "" {
		beeLogger.Log.Error("You need to set GOPATH environment variable")
		return ""
	}
	wgopath := path.SplitList(gopath)
	for _, wg := range wgopath {
		wg, _ = path.EvalSymlinks(path.Join(wg, "src", "github.com", "astaxie", "beego"))
		filename := path.Join(wg, "beego.go")
		_, err := os.Stat(filename)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			beeLogger.Log.Error("Error while getting stats of 'beego.go'")
		}
		fd, err := os.Open(filename)
		if err != nil {
			beeLogger.Log.Error("Error while reading 'beego.go'")
			continue
		}
		reader := bufio.NewReader(fd)
		for {
			byteLine, _, er := reader.ReadLine()
			if er != nil && er != io.EOF {
				return ""
			}
			if er == io.EOF {
				break
			}
			line := string(byteLine)
			s := re.FindStringSubmatch(line)
			if len(s) >= 2 {
				return s[1]
			}
		}

	}
	return "Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego"
}

func GetGoVersion() string {
	var (
		cmdOut []byte
		err    error
	)

	if cmdOut, err = exec.Command("go", "version").Output(); err != nil {
		beeLogger.Log.Fatalf("There was an error running 'go version' command: %s", err)
	}
	return strings.Split(string(cmdOut), " ")[2]
}
<commit_msg>update to v1.8.2<commit_after>package version

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	path "path/filepath"
	"regexp"
	"runtime"
	"strings"

	"github.com/beego/bee/cmd/commands"
	beeLogger "github.com/beego/bee/logger"
	"github.com/beego/bee/logger/colors"
	"gopkg.in/yaml.v2"
)

const verboseVersionBanner string = `%s%s______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v{{ .BeeVersion }}%s
%s%s
├── Beego     : {{ .BeegoVersion }}
├── GoVersion : {{ .GoVersion }}
├── GOOS      : {{ .GOOS }}
├── GOARCH    : {{ .GOARCH }}
├── NumCPU    : {{ .NumCPU }}
├── GOPATH    : {{ .GOPATH }}
├── GOROOT    : {{ .GOROOT }}
├── Compiler  : {{ .Compiler }}
└── Date      : {{ Now "Monday, 2 Jan 2006" }}%s
`

const shortVersionBanner = `______
| ___ \
| |_/ /  ___   ___
| ___ \ / _ \ / _ \
| |_/ /|  __/|  __/
\____/  \___| \___| v{{ .BeeVersion }}
`

var CmdVersion = &commands.Command{
	UsageLine: "version",
	Short:     "Prints the current Bee version",
	Long: `
Prints the current Bee, Beego and Go version alongside the platform information.
`,
	Run: versionCmd,
}
var outputFormat string

const version = "1.8.2"

func init() {
	fs := flag.NewFlagSet("version", flag.ContinueOnError)
	fs.StringVar(&outputFormat, "o", "", "Set the output format. Either json or yaml.")
	CmdVersion.Flag = *fs
	commands.AvailableCommands = append(commands.AvailableCommands, CmdVersion)
}

func versionCmd(cmd *commands.Command, args []string) int {

	cmd.Flag.Parse(args)
	stdout := cmd.Out()

	if outputFormat != "" {
		runtimeInfo := RuntimeInfo{
			GetGoVersion(),
			runtime.GOOS,
			runtime.GOARCH,
			runtime.NumCPU(),
			os.Getenv("GOPATH"),
			runtime.GOROOT(),
			runtime.Compiler,
			version,
			GetBeegoVersion(),
		}
		switch outputFormat {
		case "json":
			{
				b, err := json.MarshalIndent(runtimeInfo, "", "    ")
				if err != nil {
					beeLogger.Log.Error(err.Error())
				}
				fmt.Println(string(b))
				return 0
			}
		case "yaml":
			{
				b, err := yaml.Marshal(&runtimeInfo)
				if err != nil {
					beeLogger.Log.Error(err.Error())
				}
				fmt.Println(string(b))
				return 0
			}
		}
	}

	coloredBanner := fmt.Sprintf(verboseVersionBanner, "\x1b[35m", "\x1b[1m",
		"\x1b[0m", "\x1b[32m", "\x1b[1m", "\x1b[0m")
	InitBanner(stdout, bytes.NewBufferString(coloredBanner))
	return 0
}

// ShowShortVersionBanner prints the short version banner.
func ShowShortVersionBanner() {
	output := colors.NewColorWriter(os.Stdout)
	InitBanner(output, bytes.NewBufferString(colors.MagentaBold(shortVersionBanner)))
}

func GetBeegoVersion() string {
	gopath := os.Getenv("GOPATH")
	re, err := regexp.Compile(`VERSION = "([0-9.]+)"`)
	if err != nil {
		return ""
	}
	if gopath == "" {
		beeLogger.Log.Error("You need to set GOPATH environment variable")
		return ""
	}
	wgopath := path.SplitList(gopath)
	for _, wg := range wgopath {
		wg, _ = path.EvalSymlinks(path.Join(wg, "src", "github.com", "astaxie", "beego"))
		filename := path.Join(wg, "beego.go")
		_, err := os.Stat(filename)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			beeLogger.Log.Error("Error while getting stats of 'beego.go'")
		}
		fd, err := os.Open(filename)
		if err != nil {
			beeLogger.Log.Error("Error while reading 'beego.go'")
			continue
		}
		reader := bufio.NewReader(fd)
		for {
			byteLine, _, er := reader.ReadLine()
			if er != nil && er != io.EOF {
				return ""
			}
			if er == io.EOF {
				break
			}
			line := string(byteLine)
			s := re.FindStringSubmatch(line)
			if len(s) >= 2 {
				return s[1]
			}
		}

	}
	return "Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego"
}

func GetGoVersion() string {
	var (
		cmdOut []byte
		err    error
	)

	if cmdOut, err = exec.Command("go", "version").Output(); err != nil {
		beeLogger.Log.Fatalf("There was an error running 'go version' command: %s", err)
	}
	return strings.Split(string(cmdOut), " ")[2]
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/nfnt/resize"
	_ "golang.org/x/image/tiff"
	"image"
	"image/draw"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"os"
)

// SimpleImage implements IIIFImage for reading non-JP2 image types.  These can
// only handle a basic "read it all, then crop/resize" flow, and thus should
// have very careful load testing.
type SimpleImage struct {
	file         *os.File
	conf         image.Config
	decodeWidth  int
	decodeHeight int
	scaleFactor  float64
	decodeArea   image.Rectangle
}

func NewSimpleImage(filename string) (*SimpleImage, error) {
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	i := &SimpleImage{file: file}
	i.conf, _, err = image.DecodeConfig(file)
	if err != nil {
		return nil, err
	}
	i.file.Seek(0, 0)

	return i, nil
}

// SetResizeWH sets the image to scale to the given width and height.  If one
// dimension is 0, the decoded image will preserve the aspect ratio while
// scaling to the non-zero dimension.
func (i *SimpleImage) SetResizeWH(width, height int) {
	i.decodeWidth = width
	i.decodeHeight = height
}

func (i *SimpleImage) SetCrop(r image.Rectangle) {
	i.decodeArea = r
}

// DecodeImage returns an image.Image that holds the decoded image data,
// resized and cropped if resizing or cropping was requested.  Both cropping
// and resizing happen here due to the nature of openjpeg, so SetScale,
// SetResizeWH, and SetCrop must be called before this function.
func (i *SimpleImage) DecodeImage() (image.Image, error) {
	img, _, err := image.Decode(i.file)
	if err != nil {
		return nil, err
	}

	if i.decodeArea == image.ZR {
		i.decodeArea = image.Rect(0, 0, i.conf.Width, i.conf.Height)
	}

	if i.decodeWidth == 0 && i.decodeHeight == 0 {
		i.decodeWidth = i.conf.Width
		i.decodeHeight = i.conf.Height
	}

	// Draw a new image of the requested size if the decode area isn't the same
	// rectangle as the source image
	if i.decodeArea != img.Bounds() {
		srcB := img.Bounds()
		dstB := i.decodeArea
		dst := image.NewRGBA(image.Rect(0, 0, dstB.Dx(), dstB.Dy()))
		draw.Draw(dst, srcB, img, dstB.Min, draw.Src)
		img = dst
	}

	if i.decodeWidth != i.decodeArea.Dx() || i.decodeHeight != i.decodeArea.Dy() {
		img = resize.Resize(uint(i.decodeWidth), uint(i.decodeHeight), img, resize.Bilinear)
	}

	return img, nil
}

// GetWidth returns the image width
func (i *SimpleImage) GetWidth() int {
	return i.conf.Width
}

// GetHeight returns the image height
func (i *SimpleImage) GetHeight() int {
	return i.conf.Height
}
<commit_msg>Remove dead code<commit_after><|endoftext|>


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

// Command make_builtin_vdlroot runs at jiri go generate time. It emits a Go
// source file called builtin_vdlroot.go containing a gzip'd version of all the
// core VDL types required by the VDL tool. This allows the VDL tool to run
// 'standalone' (i.e. without access to the Vanadium source code).
//
// +build ignored

package main

import (
	"archive/tar"
	"compress/gzip"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

const (
	outputPreamble = `// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file is automatically generated, your changes will be lost.
// See make_builtin_vdlroot.go.

package main

//go:generate jiri go run make_builtin_vdlroot.go

const (
	// builtinVdlrootData contains a base64-encoded gzip'd tar file. This file
	// contains all of the VDL files required to run the VDL tool.
	builtinVdlrootData = ` + "`"
	outputFooter = "`" + `
)
`
)

// writeBytes writes the given bytes to the given writer, returning an error if
// the underlying Write fails or if the number of bytes written is not equal to
// len(content).
func writeBytes(out io.Writer, content []byte) error {
	n, err := out.Write(content)
	if err == nil && n != len(content) {
		err = fmt.Errorf("wrote an unexpected number of bytes, wanted %d, wrote %d", len(content), n)
	}
	return err
}

// writeString writes the given string to the given writer, returning an error
// if the underlying Write fails or if the number of bytes written is not equal
// to len(content).
func writeString(out io.Writer, content string) error {
	return writeBytes(out, []byte(content))
}

type wrapWriter struct {
	writer       io.Writer
	totalWritten int
}

// Writes b to the underlying writer but inserts a \n every 78 characters. The
// returned bytes-written count includes the \n characters.
func (w *wrapWriter) Write(b []byte) (int, error) {
	const max = 78
	n := 0
	for len(b) > 0 {
		newline := true
		chunk := max - w.totalWritten%max
		if len(b) < chunk {
			chunk = len(b)
			newline = false
		}
		if err := writeBytes(w.writer, b[:chunk]); err != nil {
			return n, err
		}
		n += chunk
		w.totalWritten += chunk
		b = b[chunk:]
		if newline {
			if err := writeString(w.writer, "\n"); err != nil {
				return n, err
			}
			n++
		}
	}
	return n, nil
}

// writeVdlrootData creates a gzip'd tar file containing all of the VDL files
// in vdlroot. The data is encoded as base64. Does not close out.
func writeVdlrootData(out io.Writer) error {
	jiriRoot := os.Getenv("JIRI_ROOT")
	if jiriRoot == "" {
		return fmt.Errorf("JIRI_ROOT is not set")
	}
	vdlroot := filepath.Join(v23root, "release", "go", "src", "v.io", "v23", "vdlroot")
	wrapWriter := &wrapWriter{
		writer:       out,
		totalWritten: 0,
	}
	base64writer := base64.NewEncoder(base64.StdEncoding, wrapWriter)
	gzipWriter := gzip.NewWriter(base64writer)
	tarWriter := tar.NewWriter(gzipWriter)
	walkFn := func(path string, info os.FileInfo, err error) error {
		if strings.HasSuffix(path, ".vdl") {
			content, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}
			relPath, err := filepath.Rel(vdlroot, path)
			if err != nil {
				return err
			}
			header := tar.Header{
				Mode: int64(0644),
				Name: relPath,
				Size: int64(len(content)),
			}
			if err := tarWriter.WriteHeader(&header); err != nil {
				return err
			}
			return writeBytes(tarWriter, content)
		}
		return nil
	}
	if err := filepath.Walk(vdlroot, walkFn); err != nil {
		log.Printf("Walk() failed: %v", err)
	}
	if err := tarWriter.Close(); err != nil {
		log.Printf("Close() of tar file failed: %v", err)
		return err
	}
	if err := gzipWriter.Close(); err != nil {
		log.Printf("Close() of gzip file failed: %v", err)
		return err
	}
	if err := base64writer.Close(); err != nil {
		log.Printf("Close() of base64 file failed: %v", err)
		return err
	}
	return nil
}

func main() {
	filename := "builtin_vdlroot.go"
	f, err := os.Create(filename)
	if err != nil {
		log.Printf("Create(%q) failed: %v", filename, err)
		os.Exit(1)
	}
	defer f.Close()
	if err := writeString(f, outputPreamble); err != nil {
		os.Exit(1)
	}
	if err := writeVdlrootData(f); err != nil {
		os.Exit(1)
	}
	if err := writeString(f, outputFooter); err != nil {
		os.Exit(1)
	}
}
<commit_msg>TBR: Fix build error in vdl tool.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command make_builtin_vdlroot runs at jiri go generate time. It emits a Go
// source file called builtin_vdlroot.go containing a gzip'd version of all the
// core VDL types required by the VDL tool. This allows the VDL tool to run
// 'standalone' (i.e. without access to the Vanadium source code).
//
// +build ignored

package main

import (
	"archive/tar"
	"compress/gzip"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

const (
	outputPreamble = `// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file is automatically generated, your changes will be lost.
// See make_builtin_vdlroot.go.

package main

//go:generate jiri go run make_builtin_vdlroot.go

const (
	// builtinVdlrootData contains a base64-encoded gzip'd tar file. This file
	// contains all of the VDL files required to run the VDL tool.
	builtinVdlrootData = ` + "`"
	outputFooter = "`" + `
)
`
)

// writeBytes writes the given bytes to the given writer, returning an error if
// the underlying Write fails or if the number of bytes written is not equal to
// len(content).
func writeBytes(out io.Writer, content []byte) error {
	n, err := out.Write(content)
	if err == nil && n != len(content) {
		err = fmt.Errorf("wrote an unexpected number of bytes, wanted %d, wrote %d", len(content), n)
	}
	return err
}

// writeString writes the given string to the given writer, returning an error
// if the underlying Write fails or if the number of bytes written is not equal
// to len(content).
func writeString(out io.Writer, content string) error {
	return writeBytes(out, []byte(content))
}

type wrapWriter struct {
	writer       io.Writer
	totalWritten int
}

// Writes b to the underlying writer but inserts a \n every 78 characters. The
// returned bytes-written count includes the \n characters.
func (w *wrapWriter) Write(b []byte) (int, error) {
	const max = 78
	n := 0
	for len(b) > 0 {
		newline := true
		chunk := max - w.totalWritten%max
		if len(b) < chunk {
			chunk = len(b)
			newline = false
		}
		if err := writeBytes(w.writer, b[:chunk]); err != nil {
			return n, err
		}
		n += chunk
		w.totalWritten += chunk
		b = b[chunk:]
		if newline {
			if err := writeString(w.writer, "\n"); err != nil {
				return n, err
			}
			n++
		}
	}
	return n, nil
}

// writeVdlrootData creates a gzip'd tar file containing all of the VDL files
// in vdlroot. The data is encoded as base64. Does not close out.
func writeVdlrootData(out io.Writer) error {
	jiriRoot := os.Getenv("JIRI_ROOT")
	if jiriRoot == "" {
		return fmt.Errorf("JIRI_ROOT is not set")
	}
	vdlroot := filepath.Join(jiriRoot, "release", "go", "src", "v.io", "v23", "vdlroot")
	wrapWriter := &wrapWriter{
		writer:       out,
		totalWritten: 0,
	}
	base64writer := base64.NewEncoder(base64.StdEncoding, wrapWriter)
	gzipWriter := gzip.NewWriter(base64writer)
	tarWriter := tar.NewWriter(gzipWriter)
	walkFn := func(path string, info os.FileInfo, err error) error {
		if strings.HasSuffix(path, ".vdl") {
			content, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}
			relPath, err := filepath.Rel(vdlroot, path)
			if err != nil {
				return err
			}
			header := tar.Header{
				Mode: int64(0644),
				Name: relPath,
				Size: int64(len(content)),
			}
			if err := tarWriter.WriteHeader(&header); err != nil {
				return err
			}
			return writeBytes(tarWriter, content)
		}
		return nil
	}
	if err := filepath.Walk(vdlroot, walkFn); err != nil {
		log.Printf("Walk() failed: %v", err)
	}
	if err := tarWriter.Close(); err != nil {
		log.Printf("Close() of tar file failed: %v", err)
		return err
	}
	if err := gzipWriter.Close(); err != nil {
		log.Printf("Close() of gzip file failed: %v", err)
		return err
	}
	if err := base64writer.Close(); err != nil {
		log.Printf("Close() of base64 file failed: %v", err)
		return err
	}
	return nil
}

func main() {
	filename := "builtin_vdlroot.go"
	f, err := os.Create(filename)
	if err != nil {
		log.Printf("Create(%q) failed: %v", filename, err)
		os.Exit(1)
	}
	defer f.Close()
	if err := writeString(f, outputPreamble); err != nil {
		os.Exit(1)
	}
	if err := writeVdlrootData(f); err != nil {
		os.Exit(1)
	}
	if err := writeString(f, outputFooter); err != nil {
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>//
// Copyright 2018, Sander van Harmelen
//
// 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 gitlab

import (
	"fmt"
	"time"
)

// DeploymentsService handles communication with the deployment related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html
type DeploymentsService struct {
	client *Client
}

// Deployment represents the Gitlab deployment
type Deployment struct {
	ID          int          `json:"id"`
	IID         int          `json:"iid"`
	Ref         string       `json:"ref"`
	SHA         string       `json:"sha"`
	CreatedAt   *time.Time   `json:"created_at"`
	UpdatedAt   *time.Time   `json:"updated_at"`
	User        *ProjectUser `json:"user"`
	Environment *Environment `json:"environment"`
	Deployable  struct {
		ID         int        `json:"id"`
		Status     string     `json:"status"`
		Stage      string     `json:"stage"`
		Name       string     `json:"name"`
		Ref        string     `json:"ref"`
		Tag        bool       `json:"tag"`
		Coverage   float64    `json:"coverage"`
		CreatedAt  *time.Time `json:"created_at"`
		StartedAt  *time.Time `json:"started_at"`
		FinishedAt *time.Time `json:"finished_at"`
		Duration   float64    `json:"duration"`
		User       *User      `json:"user"`
		Commit     *Commit    `json:"commit"`
		Pipeline   struct {
			ID        int        `json:"id"`
			SHA       string     `json:"sha"`
			Ref       string     `json:"ref"`
			Status    string     `json:"status"`
			CreatedAt *time.Time `json:"created_at"`
			UpdatedAt *time.Time `json:"updated_at"`
		} `json:"pipeline"`
		Runner *Runner `json:"runner"`
	} `json:"deployable"`
}

// ListProjectDeploymentsOptions represents the available ListProjectDeployments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
type ListProjectDeploymentsOptions struct {
	ListOptions
	OrderBy       *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort          *string    `url:"sort,omitempty" json:"sort,omitempty"`
	UpdatedAfter  *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"`
	Environment   *string    `url:"environment,omitempty" json:"environment,omitempty"`
	Status        *string    `url:"status,omitempty" json:"status,omitempty"`
}

// ListProjectDeployments gets a list of deployments in a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...RequestOptionFunc) ([]*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Deployment
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, err
}

// GetProjectDeployment get a deployment for a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment
func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}

// CreateProjectDeploymentOptions represents the available
// CreateProjectDeployment() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
type CreateProjectDeploymentOptions struct {
	Environment *string                `url:"environment,omitempty" json:"environment,omitempty"`
	Ref         *string                `url:"ref,omitempty" json:"ref,omitempty"`
	SHA         *string                `url:"sha,omitempty" json:"sha,omitempty"`
	Tag         *bool                  `url:"tag,omitempty" json:"tag,omitempty"`
	Status      *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// CreateProjectDeployment creates a project deployment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
func (s *DeploymentsService) CreateProjectDeployment(pid interface{}, opt *CreateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}

// UpdateProjectDeploymentOptions represents the available
// UpdateProjectDeployment() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
type UpdateProjectDeploymentOptions struct {
	Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// UpdateProjectDeployment updates a project deployment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
func (s *DeploymentsService) UpdateProjectDeployment(pid interface{}, deployment int, opt *UpdateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}
<commit_msg>add top level Status field to Deployment<commit_after>//
// Copyright 2018, Sander van Harmelen
//
// 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 gitlab

import (
	"fmt"
	"time"
)

// DeploymentsService handles communication with the deployment related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html
type DeploymentsService struct {
	client *Client
}

// Deployment represents the Gitlab deployment
type Deployment struct {
	ID          int          `json:"id"`
	IID         int          `json:"iid"`
	Ref         string       `json:"ref"`
	SHA         string       `json:"sha"`
	Status      string       `json:"status"`
	CreatedAt   *time.Time   `json:"created_at"`
	UpdatedAt   *time.Time   `json:"updated_at"`
	User        *ProjectUser `json:"user"`
	Environment *Environment `json:"environment"`
	Deployable  struct {
		ID         int        `json:"id"`
		Status     string     `json:"status"`
		Stage      string     `json:"stage"`
		Name       string     `json:"name"`
		Ref        string     `json:"ref"`
		Tag        bool       `json:"tag"`
		Coverage   float64    `json:"coverage"`
		CreatedAt  *time.Time `json:"created_at"`
		StartedAt  *time.Time `json:"started_at"`
		FinishedAt *time.Time `json:"finished_at"`
		Duration   float64    `json:"duration"`
		User       *User      `json:"user"`
		Commit     *Commit    `json:"commit"`
		Pipeline   struct {
			ID        int        `json:"id"`
			SHA       string     `json:"sha"`
			Ref       string     `json:"ref"`
			Status    string     `json:"status"`
			CreatedAt *time.Time `json:"created_at"`
			UpdatedAt *time.Time `json:"updated_at"`
		} `json:"pipeline"`
		Runner *Runner `json:"runner"`
	} `json:"deployable"`
}

// ListProjectDeploymentsOptions represents the available ListProjectDeployments() options.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
type ListProjectDeploymentsOptions struct {
	ListOptions
	OrderBy       *string    `url:"order_by,omitempty" json:"order_by,omitempty"`
	Sort          *string    `url:"sort,omitempty" json:"sort,omitempty"`
	UpdatedAfter  *time.Time `url:"updated_after,omitempty" json:"updated_after,omitempty"`
	UpdatedBefore *time.Time `url:"update_before,omitempty" json:"updated_before,omitempty"`
	Environment   *string    `url:"environment,omitempty" json:"environment,omitempty"`
	Status        *string    `url:"status,omitempty" json:"status,omitempty"`
}

// ListProjectDeployments gets a list of deployments in a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#list-project-deployments
func (s *DeploymentsService) ListProjectDeployments(pid interface{}, opts *ListProjectDeploymentsOptions, options ...RequestOptionFunc) ([]*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var ds []*Deployment
	resp, err := s.client.Do(req, &ds)
	if err != nil {
		return nil, resp, err
	}

	return ds, resp, err
}

// GetProjectDeployment get a deployment for a project.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/deployments.html#get-a-specific-deployment
func (s *DeploymentsService) GetProjectDeployment(pid interface{}, deployment int, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}

// CreateProjectDeploymentOptions represents the available
// CreateProjectDeployment() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
type CreateProjectDeploymentOptions struct {
	Environment *string                `url:"environment,omitempty" json:"environment,omitempty"`
	Ref         *string                `url:"ref,omitempty" json:"ref,omitempty"`
	SHA         *string                `url:"sha,omitempty" json:"sha,omitempty"`
	Tag         *bool                  `url:"tag,omitempty" json:"tag,omitempty"`
	Status      *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// CreateProjectDeployment creates a project deployment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#create-a-deployment
func (s *DeploymentsService) CreateProjectDeployment(pid interface{}, opt *CreateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments", pathEscape(project))

	req, err := s.client.NewRequest("POST", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}

// UpdateProjectDeploymentOptions represents the available
// UpdateProjectDeployment() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
type UpdateProjectDeploymentOptions struct {
	Status *DeploymentStatusValue `url:"status,omitempty" json:"status,omitempty"`
}

// UpdateProjectDeployment updates a project deployment.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/deployments.html#updating-a-deployment
func (s *DeploymentsService) UpdateProjectDeployment(pid interface{}, deployment int, opt *UpdateProjectDeploymentOptions, options ...RequestOptionFunc) (*Deployment, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/deployments/%d", pathEscape(project), deployment)

	req, err := s.client.NewRequest("PUT", u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	d := new(Deployment)
	resp, err := s.client.Do(req, &d)
	if err != nil {
		return nil, resp, err
	}

	return d, resp, err
}
<|endoftext|>


<commit_before>package binding

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"net/http"
	"reflect"
	"strings"
)

type (
	Binding interface {
		Bind(*http.Request, interface{}) error
	}

	// JSON binding
	jsonBinding struct{}

	// XML binding
	xmlBinding struct{}

	// // form binding
	formBinding struct{}
)

var (
	JSON = jsonBinding{}
	XML  = xmlBinding{}
	Form = formBinding{} // todo
)

func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := xml.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
	return nil
}

func Validate(obj interface{}) error {

	typ := reflect.TypeOf(obj)
	val := reflect.ValueOf(obj)

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		val = val.Elem()
	}

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		fieldValue := val.Field(i).Interface()
		zero := reflect.Zero(field.Type).Interface()

		// Validate nested and embedded structs (if pointer, only do so if not nil)
		if field.Type.Kind() == reflect.Struct ||
			(field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) {
			if err := Validate(fieldValue); err != nil {
				return err
			}
		}

		if strings.Index(field.Tag.Get("binding"), "required") > -1 {
			if reflect.DeepEqual(zero, fieldValue) {
				name := field.Name
				if j := field.Tag.Get("json"); j != "" {
					name = j
				} else if f := field.Tag.Get("form"); f != "" {
					name = f
				}
				return errors.New("Required " + name)
			}
		}
	}
	return nil
}
<commit_msg>Adds FORM bindings<commit_after>package binding

import (
	"encoding/json"
	"encoding/xml"
	"errors"
	"net/http"
	"reflect"
	"strconv"
	"strings"
)

type (
	Binding interface {
		Bind(*http.Request, interface{}) error
	}

	// JSON binding
	jsonBinding struct{}

	// XML binding
	xmlBinding struct{}

	// // form binding
	formBinding struct{}
)

var (
	JSON = jsonBinding{}
	XML  = xmlBinding{}
	Form = formBinding{} // todo
)

func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
	decoder := xml.NewDecoder(req.Body)
	if err := decoder.Decode(obj); err == nil {
		return Validate(obj)
	} else {
		return err
	}
}

func (_ formBinding) Bind(req *http.Request, obj interface{}) error {
	if err := req.ParseForm(); err != nil {
		return err
	}
	if err := mapForm(obj, req.Form); err != nil {
		return err
	}
	return Validate(obj)
}

func mapForm(ptr interface{}, form map[string][]string) error {
	typ := reflect.TypeOf(ptr).Elem()
	formStruct := reflect.ValueOf(ptr).Elem()
	for i := 0; i < typ.NumField(); i++ {
		typeField := typ.Field(i)
		if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
			structField := formStruct.Field(i)
			if !structField.CanSet() {
				continue
			}

			inputValue, exists := form[inputFieldName]
			if !exists {
				continue
			}
			numElems := len(inputValue)
			if structField.Kind() == reflect.Slice && numElems > 0 {
				sliceOf := structField.Type().Elem().Kind()
				slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
				for i := 0; i < numElems; i++ {
					if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
						return err
					}
				}
				formStruct.Elem().Field(i).Set(slice)
			} else {
				if err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
					return err
				}
			}
		}
	}
	return nil
}

func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
	switch valueKind {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		if val == "" {
			val = "0"
		}
		intVal, err := strconv.Atoi(val)
		if err != nil {
			return err
		} else {
			structField.SetInt(int64(intVal))
		}
	case reflect.Bool:
		if val == "" {
			val = "false"
		}
		boolVal, err := strconv.ParseBool(val)
		if err != nil {
			return err
		} else {
			structField.SetBool(boolVal)
		}
	case reflect.Float32:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 32)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.Float64:
		if val == "" {
			val = "0.0"
		}
		floatVal, err := strconv.ParseFloat(val, 64)
		if err != nil {
			return err
		} else {
			structField.SetFloat(floatVal)
		}
	case reflect.String:
		structField.SetString(val)
	}
	return nil
}

// Don't pass in pointers to bind to. Can lead to bugs. See:
// https://github.com/codegangsta/martini-contrib/issues/40
// https://github.com/codegangsta/martini-contrib/pull/34#issuecomment-29683659
func ensureNotPointer(obj interface{}) {
	if reflect.TypeOf(obj).Kind() == reflect.Ptr {
		panic("Pointers are not accepted as binding models")
	}
}

func Validate(obj interface{}) error {

	typ := reflect.TypeOf(obj)
	val := reflect.ValueOf(obj)

	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
		val = val.Elem()
	}

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		fieldValue := val.Field(i).Interface()
		zero := reflect.Zero(field.Type).Interface()

		// Validate nested and embedded structs (if pointer, only do so if not nil)
		if field.Type.Kind() == reflect.Struct ||
			(field.Type.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, fieldValue)) {
			if err := Validate(fieldValue); err != nil {
				return err
			}
		}

		if strings.Index(field.Tag.Get("binding"), "required") > -1 {
			if reflect.DeepEqual(zero, fieldValue) {
				name := field.Name
				if j := field.Tag.Get("json"); j != "" {
					name = j
				} else if f := field.Tag.Get("form"); f != "" {
					name = f
				}
				return errors.New("Required " + name)
			}
		}
	}
	return nil
}
<|endoftext|>


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

// +build linux

package main

import (
	"flag"
	"fmt"
	"golang.org/x/sys/unix"
	"io"
	"log"
	"os"
	"path/filepath"
	"syscall"
)

var (
	help    = flag.Bool("h", false, "Help")
	version = flag.Bool("V", false, "Version")
)

func usage() string {
	return "switch_root [-h] [-V]\nswitch_root newroot init"
}

// getDev returns the device (as returned by the FSTAT syscall) for the given file descriptor.
func getDev(fd int) (dev uint64, err error) {
	var stat unix.Stat_t

	if err := unix.Fstat(fd, &stat); err != nil {
		return 0, err
	}

	return stat.Dev, nil
}

// recursiveDelete deletes a directory identified by `fd` and everything in it.
//
// This function allows deleting directories no longer referenceable by
// any file name. This function does not descend into mounts.
func recursiveDelete(fd int) error {
	parentDev, err := getDev(fd)
	if err != nil {
		return err
	}

  // The file descriptor is already open, but allocating a os.File
	// here makes reading the files in the dir so much nicer.
	dir := os.NewFile(uintptr(fd), "__ignored__")
	defer dir.Close()
	names, err := dir.Readdirnames(-1)
	if err != nil {
		return err
	}

	for _, name := range names {
		// Loop here, but handle loop in separate function to make defer work as expected.
		if err := recusiveDeleteInner(fd, parentDev, name); err != nil {
			return err
		}
	}
	return nil
}

// recusiveDeleteInner is called from recursiveDelete and either deletes
// or recurses into the given file or directory
//
// There should be no need to call this function directly.
func recusiveDeleteInner(parentFd int, parentDev uint64, childName string) error {
	// O_DIRECTORY and O_NOFOLLOW make this open fail for all files and all symlinks (even when pointing to a dir).
	// We need to filter out symlinks because getDev later follows them.
	childFd, err := unix.Openat(parentFd, childName, unix.O_DIRECTORY | unix.O_NOFOLLOW, unix.O_RDWR)
	if err != nil {
		// childName points to either a file or a symlink, delete in any case.
		if err := unix.Unlinkat(parentFd, childName, 0); err != nil {
			return err
		}
	} else {
		// Open succeeded, which means childName points to a real directory.
		defer unix.Close(childFd)

		// Don't descent into other file systems.
		if childFdDev, err := getDev(childFd); err != nil {
			return err
		} else if childFdDev != parentDev {
			// This means continue in recursiveDelete.
			return nil
		}

		if err:= recursiveDelete(childFd); err != nil {
			return err
		}
		// Back from recursion, the directory is now empty, delete.
		if err := unix.Unlinkat(parentFd, childName, unix.AT_REMOVEDIR); err != nil {
			return err
		}
	}
	return nil
}

// execCommand execs into the given command.
//
// In order to preserve whatever PID this program is running with,
// the implementation does an actual EXEC syscall without forking.
func execCommand(path string) error {
	return syscall.Exec(path, []string{path}, []string{})
}

// isEmpty returns true if the directory with the given path is empty.
func isEmpty(name string) (bool, error) {
	f, err := os.Open(name)
	if err != nil {
		return false, err
	}
	defer f.Close()

	if _, err := f.Readdirnames(1); err == io.EOF {
		return true, nil
	}
	return false, err
}

// moveMount moves mount
//
// This function is just a wrapper around the MOUNT syscall with the
// MOVE flag supplied.
func moveMount(oldPath string, newPath string) error {
	return unix.Mount(oldPath, newPath, "", unix.MS_MOVE, "")
}

// specialFS moves the 'special' mounts to the given target path
//
// 'special' in this context refers to the following non-blockdevice backed
// mounts that are almost always used: /dev, /proc, /sys, and /run.
// This function will create the target directories, if necessary.
// If the target directories already exists, they must be empty.
// This function skips missing mounts.
func specialFS(newRoot string) error {
	var mounts = []string{"/dev", "/proc", "/sys", "/run"}

	for _, mount := range mounts {
		path := filepath.Join(newRoot, mount)
		// Skip all mounting if the directory does not exists.
		if _, err := os.Stat(mount); os.IsNotExist(err) {
			fmt.Println("switch_root: Skipping", mount)
			continue
		} else if err != nil {
			return err
		}
		// Make sure the target dir exists and is empty.
		if _, err := os.Stat(path); os.IsNotExist(err) {
			if err := unix.Mkdir(path, 0); err != nil {
				return err
			}
		} else if err != nil {
			return err
		}
		if empty, err := isEmpty(path); err != nil {
			return err
		} else if !empty {
			return fmt.Errorf("%v must be empty", path)
		}
		if err := moveMount(mount, path); err != nil {
			return err
		}
	}
	return nil
}

// switchroot moves special mounts (dev, proc, sys, run) to the new directory,
// then does a chroot, moves the root mount to the new directory and finally
// DELETES EVERYTHING in the old root and execs the given init.
func switchRoot(newRoot string, init string) error {
	log.Printf("switch_root: moving mounts")
	if err := specialFS(newRoot); err != nil {
		return fmt.Errorf("switch_root: moving mounts failed %v", err)
	}

	log.Printf("switch_root: Changing directory")
	if err := unix.Chdir(newRoot); err != nil {
		return fmt.Errorf("switch_root: failed change directory to new_root %v", err)
	}

	// Open "/" now, we need the file descriptor later.
	oldRoot, err := os.Open("/")
	if err != nil {
		return err
	}
	defer oldRoot.Close()

	log.Printf("switch_root: Moving /")
	if err := moveMount(newRoot, "/"); err != nil {
		return err
	}

	log.Printf("switch_root: Changing root!")
	if err := unix.Chroot("."); err != nil {
		return fmt.Errorf("switch_root: fatal chroot error %v", err)
	}

	log.Printf("switch_root: Deleting old /")
	if err := recursiveDelete(int(oldRoot.Fd())); err != nil {
		panic(err)
	}

	log.Printf("switch_root: executing init")
	if err := execCommand(init); err != nil {
		return fmt.Errorf("switch_root: exec failed %v", err)
	}

	return nil
}

func main() {
	flag.Parse()

	if len(flag.Args()) == 0 {
		fmt.Println(usage())
		os.Exit(0)
	}

	if *help {
		fmt.Println(usage())
		os.Exit(0)
	}

	if *version {
		fmt.Println("Version XX")
		os.Exit(0)
	}

	newRoot := flag.Args()[0]
	init := flag.Args()[1]

	if err := switchRoot(newRoot, init); err != nil {
		log.Fatalf("switch_root failed %v\n", err)
	}
}
<commit_msg>cmds/switch_root: go fmt<commit_after>// Copyright 2012-2017 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build linux

package main

import (
	"flag"
	"fmt"
	"golang.org/x/sys/unix"
	"io"
	"log"
	"os"
	"path/filepath"
	"syscall"
)

var (
	help    = flag.Bool("h", false, "Help")
	version = flag.Bool("V", false, "Version")
)

func usage() string {
	return "switch_root [-h] [-V]\nswitch_root newroot init"
}

// getDev returns the device (as returned by the FSTAT syscall) for the given file descriptor.
func getDev(fd int) (dev uint64, err error) {
	var stat unix.Stat_t

	if err := unix.Fstat(fd, &stat); err != nil {
		return 0, err
	}

	return stat.Dev, nil
}

// recursiveDelete deletes a directory identified by `fd` and everything in it.
//
// This function allows deleting directories no longer referenceable by
// any file name. This function does not descend into mounts.
func recursiveDelete(fd int) error {
	parentDev, err := getDev(fd)
	if err != nil {
		return err
	}

	// The file descriptor is already open, but allocating a os.File
	// here makes reading the files in the dir so much nicer.
	dir := os.NewFile(uintptr(fd), "__ignored__")
	defer dir.Close()
	names, err := dir.Readdirnames(-1)
	if err != nil {
		return err
	}

	for _, name := range names {
		// Loop here, but handle loop in separate function to make defer work as expected.
		if err := recusiveDeleteInner(fd, parentDev, name); err != nil {
			return err
		}
	}
	return nil
}

// recusiveDeleteInner is called from recursiveDelete and either deletes
// or recurses into the given file or directory
//
// There should be no need to call this function directly.
func recusiveDeleteInner(parentFd int, parentDev uint64, childName string) error {
	// O_DIRECTORY and O_NOFOLLOW make this open fail for all files and all symlinks (even when pointing to a dir).
	// We need to filter out symlinks because getDev later follows them.
	childFd, err := unix.Openat(parentFd, childName, unix.O_DIRECTORY|unix.O_NOFOLLOW, unix.O_RDWR)
	if err != nil {
		// childName points to either a file or a symlink, delete in any case.
		if err := unix.Unlinkat(parentFd, childName, 0); err != nil {
			return err
		}
	} else {
		// Open succeeded, which means childName points to a real directory.
		defer unix.Close(childFd)

		// Don't descent into other file systems.
		if childFdDev, err := getDev(childFd); err != nil {
			return err
		} else if childFdDev != parentDev {
			// This means continue in recursiveDelete.
			return nil
		}

		if err := recursiveDelete(childFd); err != nil {
			return err
		}
		// Back from recursion, the directory is now empty, delete.
		if err := unix.Unlinkat(parentFd, childName, unix.AT_REMOVEDIR); err != nil {
			return err
		}
	}
	return nil
}

// execCommand execs into the given command.
//
// In order to preserve whatever PID this program is running with,
// the implementation does an actual EXEC syscall without forking.
func execCommand(path string) error {
	return syscall.Exec(path, []string{path}, []string{})
}

// isEmpty returns true if the directory with the given path is empty.
func isEmpty(name string) (bool, error) {
	f, err := os.Open(name)
	if err != nil {
		return false, err
	}
	defer f.Close()

	if _, err := f.Readdirnames(1); err == io.EOF {
		return true, nil
	}
	return false, err
}

// moveMount moves mount
//
// This function is just a wrapper around the MOUNT syscall with the
// MOVE flag supplied.
func moveMount(oldPath string, newPath string) error {
	return unix.Mount(oldPath, newPath, "", unix.MS_MOVE, "")
}

// specialFS moves the 'special' mounts to the given target path
//
// 'special' in this context refers to the following non-blockdevice backed
// mounts that are almost always used: /dev, /proc, /sys, and /run.
// This function will create the target directories, if necessary.
// If the target directories already exists, they must be empty.
// This function skips missing mounts.
func specialFS(newRoot string) error {
	var mounts = []string{"/dev", "/proc", "/sys", "/run"}

	for _, mount := range mounts {
		path := filepath.Join(newRoot, mount)
		// Skip all mounting if the directory does not exists.
		if _, err := os.Stat(mount); os.IsNotExist(err) {
			fmt.Println("switch_root: Skipping", mount)
			continue
		} else if err != nil {
			return err
		}
		// Make sure the target dir exists and is empty.
		if _, err := os.Stat(path); os.IsNotExist(err) {
			if err := unix.Mkdir(path, 0); err != nil {
				return err
			}
		} else if err != nil {
			return err
		}
		if empty, err := isEmpty(path); err != nil {
			return err
		} else if !empty {
			return fmt.Errorf("%v must be empty", path)
		}
		if err := moveMount(mount, path); err != nil {
			return err
		}
	}
	return nil
}

// switchroot moves special mounts (dev, proc, sys, run) to the new directory,
// then does a chroot, moves the root mount to the new directory and finally
// DELETES EVERYTHING in the old root and execs the given init.
func switchRoot(newRoot string, init string) error {
	log.Printf("switch_root: moving mounts")
	if err := specialFS(newRoot); err != nil {
		return fmt.Errorf("switch_root: moving mounts failed %v", err)
	}

	log.Printf("switch_root: Changing directory")
	if err := unix.Chdir(newRoot); err != nil {
		return fmt.Errorf("switch_root: failed change directory to new_root %v", err)
	}

	// Open "/" now, we need the file descriptor later.
	oldRoot, err := os.Open("/")
	if err != nil {
		return err
	}
	defer oldRoot.Close()

	log.Printf("switch_root: Moving /")
	if err := moveMount(newRoot, "/"); err != nil {
		return err
	}

	log.Printf("switch_root: Changing root!")
	if err := unix.Chroot("."); err != nil {
		return fmt.Errorf("switch_root: fatal chroot error %v", err)
	}

	log.Printf("switch_root: Deleting old /")
	if err := recursiveDelete(int(oldRoot.Fd())); err != nil {
		panic(err)
	}

	log.Printf("switch_root: executing init")
	if err := execCommand(init); err != nil {
		return fmt.Errorf("switch_root: exec failed %v", err)
	}

	return nil
}

func main() {
	flag.Parse()

	if len(flag.Args()) == 0 {
		fmt.Println(usage())
		os.Exit(0)
	}

	if *help {
		fmt.Println(usage())
		os.Exit(0)
	}

	if *version {
		fmt.Println("Version XX")
		os.Exit(0)
	}

	newRoot := flag.Args()[0]
	init := flag.Args()[1]

	if err := switchRoot(newRoot, init); err != nil {
		log.Fatalf("switch_root failed %v\n", err)
	}
}
<|endoftext|>


<commit_before>package rtcp

// A CompoundPacket is a collection of RTCP packets transmitted as a single packet with
// the underlying protocol (for example UDP).
//
// To maximize the resolution of receiption statistics, the first Packet in a CompoundPacket
// must always be either a SenderReport or a ReceiverReport.  This is true even if no data
// has been sent or received, in which case an empty ReceiverReport must be sent, and even
// if the only other RTCP packet in the compound packet is a Goodbye.
//
// Next, a SourceDescription containing a CNAME item must be included in each CompoundPacket
// to identify the source and to begin associating media for purposes such as lip-sync.
//
// Other RTCP packet types may follow in any order. Packet types may appear more than once.
type CompoundPacket []Packet

// Validate returns an error if this is not an RFC-compliant CompoundPacket.
func (c CompoundPacket) Validate() error {
	if len(c) == 0 {
		return errEmptyCompound
	}

	firstHdr := c[0].Header()

	// padding isn't allowed in the first packet in a compound datagram
	if firstHdr.Padding {
		return errInvalidPadding
	}
	// SenderReport and ReceiverReport are the only types that
	// are allowed to be the first packet in a compound datagram
	if (firstHdr.Type != TypeSenderReport) && (firstHdr.Type != TypeReceiverReport) {
		return errBadFirstPacket
	}

	for _, pkt := range c[1:] {
		switch p := pkt.(type) {
		// If the number of RecetpionReports exceeds 31 additional ReceiverReports
		// can be included here.
		case *ReceiverReport:
			continue

		// A SourceDescription containing a CNAME must be included in every
		// CompoundPacket.
		case *SourceDescription:
			var hasCNAME bool
			for _, c := range p.Chunks {
				for _, it := range c.Items {
					if it.Type == SDESCNAME {
						hasCNAME = true
					}
				}
			}

			if !hasCNAME {
				return errMissingCNAME
			}

			return nil

		// Other packets are not permitted before the CNAME
		default:
			return errPacketBeforeCNAME
		}
	}

	// CNAME never reached
	return errMissingCNAME
}

// Marshal encodes the CompoundPacket as binary.
func (c CompoundPacket) Marshal() ([]byte, error) {
	if err := c.Validate(); err != nil {
		return nil, err
	}

	out := make([]byte, 0)
	for _, p := range c {
		data, err := p.Marshal()
		if err != nil {
			return nil, err
		}
		out = append(out, data...)
	}
	return out, nil
}

// Unmarshal takes an entire udp datagram (which may consist of multiple RTCP packets) and returns
// an unmarshalled array of packets.
func Unmarshal(rawData []byte) (CompoundPacket, error) {
	var out CompoundPacket

	for len(rawData) != 0 {
		p, processed, err := unmarshal(rawData)

		if err != nil {
			return nil, err
		}

		out = append(out, p)
		rawData = rawData[processed:]
	}

	if err := out.Validate(); err != nil {
		return out, err
	}

	return out, nil
}
<commit_msg>Relax validity check on Unmarshal<commit_after>package rtcp

// A CompoundPacket is a collection of RTCP packets transmitted as a single packet with
// the underlying protocol (for example UDP).
//
// To maximize the resolution of receiption statistics, the first Packet in a CompoundPacket
// must always be either a SenderReport or a ReceiverReport.  This is true even if no data
// has been sent or received, in which case an empty ReceiverReport must be sent, and even
// if the only other RTCP packet in the compound packet is a Goodbye.
//
// Next, a SourceDescription containing a CNAME item must be included in each CompoundPacket
// to identify the source and to begin associating media for purposes such as lip-sync.
//
// Other RTCP packet types may follow in any order. Packet types may appear more than once.
type CompoundPacket []Packet

func (c CompoundPacket) validateFirstPacket() error {
	if len(c) == 0 {
		return errEmptyCompound
	}

	firstHdr := c[0].Header()

	// padding isn't allowed in the first packet in a compound datagram
	if firstHdr.Padding {
		return errInvalidPadding
	}
	// SenderReport and ReceiverReport are the only types that
	// are allowed to be the first packet in a compound datagram
	if (firstHdr.Type != TypeSenderReport) && (firstHdr.Type != TypeReceiverReport) {
		return errBadFirstPacket
	}

	return nil
}

// Validate returns an error if this is not an RFC-compliant CompoundPacket.
func (c CompoundPacket) Validate() error {
	if err := c.validateFirstPacket(); err != nil {
		return err
	}

	for _, pkt := range c[1:] {
		switch p := pkt.(type) {
		// If the number of RecetpionReports exceeds 31 additional ReceiverReports
		// can be included here.
		case *ReceiverReport:
			continue

		// A SourceDescription containing a CNAME must be included in every
		// CompoundPacket.
		case *SourceDescription:
			var hasCNAME bool
			for _, c := range p.Chunks {
				for _, it := range c.Items {
					if it.Type == SDESCNAME {
						hasCNAME = true
					}
				}
			}

			if !hasCNAME {
				return errMissingCNAME
			}

			return nil

		// Other packets are not permitted before the CNAME
		default:
			return errPacketBeforeCNAME
		}
	}

	// CNAME never reached
	return errMissingCNAME
}

// Marshal encodes the CompoundPacket as binary.
func (c CompoundPacket) Marshal() ([]byte, error) {
	if err := c.Validate(); err != nil {
		return nil, err
	}

	out := make([]byte, 0)
	for _, p := range c {
		data, err := p.Marshal()
		if err != nil {
			return nil, err
		}
		out = append(out, data...)
	}
	return out, nil
}

// Unmarshal takes an entire udp datagram (which may consist of multiple RTCP packets) and returns
// an unmarshalled array of packets.
func Unmarshal(rawData []byte) (CompoundPacket, error) {
	var out CompoundPacket

	for len(rawData) != 0 {
		p, processed, err := unmarshal(rawData)

		if err != nil {
			return nil, err
		}

		out = append(out, p)
		rawData = rawData[processed:]
	}

	if err := out.validateFirstPacket(); err != nil {
		return out, err
	}

	return out, nil
}
<|endoftext|>