<commit_before>package main

import (
	"os/exec"
	"testing"
)

func TestNoArgs (t *testing.T) {
	cmd := exec.Command("../godl")
	err := cmd.Run()
	if err == nil { // exit code 0
		t.Error("Running godl without arguments should exit with error")
	}
}

func TestNoUrls (t *testing.T) {
	cmd := exec.Command("../godl", "-n 10")
	err := cmd.Run()
	if err == nil { // exit code 0
		t.Error("Running godl without an URL should exit with error")
	}
}

func TestWrongUrl (t *testing.T) {
	cmd := exec.Command("../godl", "http://example.com/nothing")
	err := cmd.Run()
	if err == nil { // exit code 0
		t.Error("Running godl with a wrong URL should exit with error")
	}
}

func TestUrl (t *testing.T) {
	cmd := exec.Command("../godl", "https://raw.githubusercontent.com/alvatar/multipart-downloader/master/Makefile")
	err := cmd.Run()
	if err != nil {
		t.Error("Running godl with an URL should be successful")
	}
}
<commit_msg>Improved godl tests<commit_after>package main

import (
	"os"
	"os/exec"
	"testing"
)

func TestNoArgs (t *testing.T) {
	cmd := exec.Command("../godl")
	err := cmd.Run()
	if err == nil { // exit code 0
		t.Error("Running godl without arguments should exit with error")
	}
}

func TestNoUrls (t *testing.T) {
	cmd := exec.Command("../godl", "-n", "10")
	err := cmd.Run()
	if err == nil { // exit code 0
		t.Error("Running godl without an URL should exit with error")
	}
}

func TestWrongUrl (t *testing.T) {
	cmd := exec.Command("../godl", "http://example.com/nothing")
	err := cmd.Run()
	if err == nil { // exit code 0
		t.Error("Running godl with a wrong URL should exit with error")
	}
}

func TestUrl (t *testing.T) {
	cmd := exec.Command("../godl", "-o", "tmp_file", "https://raw.githubusercontent.com/alvatar/multipart-downloader/master/LICENSE")
	err := cmd.Run()
	if err != nil {
		t.Error("Running godl with -o output_file and an URL should be successful")
		return
	}
	if _, err := os.Stat("tmp_file"); os.IsNotExist(err) {
		t.Error("The file wasn't properly downloaded")
		return
	}
	os.Remove("tmp_file")
}
<|endoftext|>


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

/*

Godoc extracts and generates documentation for Go programs.

It has two modes.

Without the -http flag, it runs in command-line mode and prints plain text
documentation to standard output and exits. If both a library package and
a command with the same name exists, using the prefix cmd/ will force
documentation on the command rather than the library package. If the -src
flag is specified, godoc prints the exported interface of a package in Go
source form, or the implementation of a specific exported language entity:

	godoc fmt                # documentation for package fmt
	godoc fmt Printf         # documentation for fmt.Printf
	godoc cmd/go             # force documentation for the go command
	godoc -src fmt           # fmt package interface in Go source form
	godoc -src fmt Printf    # implementation of fmt.Printf

In command-line mode, the -q flag enables search queries against a godoc running
as a webserver. If no explicit server address is specified with the -server flag,
godoc first tries localhost:6060 and then http://golang.org.

	godoc -q Reader
	godoc -q math.Sin
	godoc -server=:6060 -q sin

With the -http flag, it runs as a web server and presents the documentation as a
web page.

	godoc -http=:6060

Usage:
	godoc [flag] package [name ...]

The flags are:
	-v
		verbose mode
	-q
		arguments are considered search queries: a legal query is a
		single identifier (such as ToLower) or a qualified identifier
		(such as math.Sin).
	-src
		print (exported) source in command-line mode
	-tabwidth=4
		width of tabs in units of spaces
	-timestamps=true
		show timestamps with directory listings
	-index
		enable identifier and full text search index
		(no search box is shown if -index is not set)
	-index_files=""
		glob pattern specifying index files; if not empty,
		the index is read from these files in sorted order
	-index_throttle=0.75
		index throttle value; a value of 0 means no time is allocated
		to the indexer (the indexer will never finish), a value of 1.0
		means that index creation is running at full throttle (other
		goroutines may get no time while the index is built)
	-links=true:
		link identifiers to their declarations
	-write_index=false
		write index to a file; the file name must be specified with
		-index_files
	-maxresults=10000
		maximum number of full text search results shown
		(no full text index is built if maxresults <= 0)
	-notes="BUG"
		regular expression matching note markers to show
		(e.g., "BUG|TODO", ".*")
	-html
		print HTML in command-line mode
	-goroot=$GOROOT
		Go root directory
	-http=addr
		HTTP service address (e.g., '127.0.0.1:6060' or just ':6060')
	-server=addr
		webserver address for command line searches
	-templates=""
		directory containing alternate template files; if set,
		the directory may provide alternative template files
		for the files in $GOROOT/lib/godoc
	-url=path
		print to standard output the data that would be served by
		an HTTP request for path
	-zip=""
		zip file providing the file system to serve; disabled if empty

By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set).
This behavior can be altered by providing an alternative $GOROOT with the -goroot
flag.

When godoc runs as a web server and -index is set, a search index is maintained.
The index is created at startup.

The index contains both identifier and full text search information (searchable
via regular expressions). The maximum number of full text search results shown
can be set with the -maxresults flag; if set to 0, no full text results are
shown, and only an identifier index but no full text search index is created.

The presentation mode of web pages served by godoc can be controlled with the
"m" URL parameter; it accepts a comma-separated list of flag names as value:

	all	show documentation for all declarations, not just the exported ones
	methods	show all embedded methods, not just those of unexported anonymous fields
	src	show the original source code rather then the extracted documentation
	text	present the page in textual (command-line) form rather than HTML
	flat	present flat (not indented) directory listings using full paths

For instance, http://golang.org/pkg/math/big/?m=all,text shows the documentation
for all (not just the exported) declarations of package big, in textual form (as
it would appear when using godoc from the command line: "godoc -src math/big .*").

By default, godoc serves files from the file system of the underlying OS.
Instead, a .zip file may be provided via the -zip flag, which contains
the file system to serve. The file paths stored in the .zip file must use
slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot)
must be set to the .zip file directory path containing the Go root directory.
For instance, for a .zip file created by the command:

	zip go.zip $HOME/go

one may run godoc as follows:

	godoc -http=:6060 -zip=go.zip -goroot=$HOME/go

See "Godoc: documenting Go code" for how to write good comments for godoc:
http://golang.org/doc/articles/godoc_documenting_go_code.html

*/
package main
<commit_msg>go.tools/cmd/godoc: point at doc.ToHTML documentation for formatting rules<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*

Godoc extracts and generates documentation for Go programs.

It has two modes.

Without the -http flag, it runs in command-line mode and prints plain text
documentation to standard output and exits. If both a library package and
a command with the same name exists, using the prefix cmd/ will force
documentation on the command rather than the library package. If the -src
flag is specified, godoc prints the exported interface of a package in Go
source form, or the implementation of a specific exported language entity:

	godoc fmt                # documentation for package fmt
	godoc fmt Printf         # documentation for fmt.Printf
	godoc cmd/go             # force documentation for the go command
	godoc -src fmt           # fmt package interface in Go source form
	godoc -src fmt Printf    # implementation of fmt.Printf

In command-line mode, the -q flag enables search queries against a godoc running
as a webserver. If no explicit server address is specified with the -server flag,
godoc first tries localhost:6060 and then http://golang.org.

	godoc -q Reader
	godoc -q math.Sin
	godoc -server=:6060 -q sin

With the -http flag, it runs as a web server and presents the documentation as a
web page.

	godoc -http=:6060

Usage:
	godoc [flag] package [name ...]

The flags are:
	-v
		verbose mode
	-q
		arguments are considered search queries: a legal query is a
		single identifier (such as ToLower) or a qualified identifier
		(such as math.Sin).
	-src
		print (exported) source in command-line mode
	-tabwidth=4
		width of tabs in units of spaces
	-timestamps=true
		show timestamps with directory listings
	-index
		enable identifier and full text search index
		(no search box is shown if -index is not set)
	-index_files=""
		glob pattern specifying index files; if not empty,
		the index is read from these files in sorted order
	-index_throttle=0.75
		index throttle value; a value of 0 means no time is allocated
		to the indexer (the indexer will never finish), a value of 1.0
		means that index creation is running at full throttle (other
		goroutines may get no time while the index is built)
	-links=true:
		link identifiers to their declarations
	-write_index=false
		write index to a file; the file name must be specified with
		-index_files
	-maxresults=10000
		maximum number of full text search results shown
		(no full text index is built if maxresults <= 0)
	-notes="BUG"
		regular expression matching note markers to show
		(e.g., "BUG|TODO", ".*")
	-html
		print HTML in command-line mode
	-goroot=$GOROOT
		Go root directory
	-http=addr
		HTTP service address (e.g., '127.0.0.1:6060' or just ':6060')
	-server=addr
		webserver address for command line searches
	-templates=""
		directory containing alternate template files; if set,
		the directory may provide alternative template files
		for the files in $GOROOT/lib/godoc
	-url=path
		print to standard output the data that would be served by
		an HTTP request for path
	-zip=""
		zip file providing the file system to serve; disabled if empty

By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set).
This behavior can be altered by providing an alternative $GOROOT with the -goroot
flag.

When godoc runs as a web server and -index is set, a search index is maintained.
The index is created at startup.

The index contains both identifier and full text search information (searchable
via regular expressions). The maximum number of full text search results shown
can be set with the -maxresults flag; if set to 0, no full text results are
shown, and only an identifier index but no full text search index is created.

The presentation mode of web pages served by godoc can be controlled with the
"m" URL parameter; it accepts a comma-separated list of flag names as value:

	all	show documentation for all declarations, not just the exported ones
	methods	show all embedded methods, not just those of unexported anonymous fields
	src	show the original source code rather then the extracted documentation
	text	present the page in textual (command-line) form rather than HTML
	flat	present flat (not indented) directory listings using full paths

For instance, http://golang.org/pkg/math/big/?m=all,text shows the documentation
for all (not just the exported) declarations of package big, in textual form (as
it would appear when using godoc from the command line: "godoc -src math/big .*").

By default, godoc serves files from the file system of the underlying OS.
Instead, a .zip file may be provided via the -zip flag, which contains
the file system to serve. The file paths stored in the .zip file must use
slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot)
must be set to the .zip file directory path containing the Go root directory.
For instance, for a .zip file created by the command:

	zip go.zip $HOME/go

one may run godoc as follows:

	godoc -http=:6060 -zip=go.zip -goroot=$HOME/go

Godoc documentation is converted to HTML or to text using the go/doc package;
see http://golang.org/pkg/go/doc/#ToHTML for the exact rules.
See "Godoc: documenting Go code" for how to write good comments for godoc:
http://golang.org/doc/articles/godoc_documenting_go_code.html

*/
package main
<|endoftext|>


<commit_before>// Copyright 2020 Anapaya Systems
//
// 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 pathhealth monitors paths to different ASes. Call Monitor.Register()
// to start monitoring paths to a remote AS using a chosen path policy. The call
// returns a registration object which can be used to obtain the best path.
package pathhealth

import (
	"net"
	"sync"
	"time"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/snet"
)

// defaultProbeInterval specifies how often should path probes be sent.
const defaultProbeInterval = 500 * time.Millisecond

// RemoteWatcher watches multiple paths to a given remote.
type RemoteWatcher interface {
	// UpdatePaths gets new paths from the SCION daemon. This method may block.
	UpdatePaths(snet.Router)
	// SendProbes sends probes on all the monitored paths.
	SendProbes(conn snet.PacketConn, localAddr snet.SCIONAddress)
	// HandleProbeReply handles a single probe reply packet.
	HandleProbeReply(id, seq uint16)
	// Cleanup stops monitoring paths that are not being used anymore.
	Cleanup()
	// Watchers returns a list of all active PathWatchers
	Watchers() []PathWatcher
}

// RemoteWatcherFactory creates RemoteWatchers.
type RemoteWatcherFactory interface {
	New(remote addr.IA) RemoteWatcher
}

// RevocationStore keeps track of revocations.
type RevocationStore interface {
	// AddRevocation adds a revocation.
	AddRevocation(rev *path_mgmt.RevInfo)
	// IsRevoked returns true if there is at least one revoked interface on the path.
	IsRevoked(path snet.Path) bool
	// Cleanup removes all expired revocations.
	Cleanup()
}

// Monitor monitors paths to a set of remote ASes.
type Monitor struct {
	// LocalIA is the ID of the local AS.
	LocalIA addr.IA
	// LocalIP is the IP address of the local host.
	LocalIP net.IP
	// Conn is the underlying conn for sending probes
	Conn net.PacketConn
	// RevocationHandler is the revocation handler.
	RevocationHandler snet.RevocationHandler
	// Router is the path manager connected to the SCION daemon.
	Router snet.Router
	// Probeinterval defines the interval at which probes are sent. If it is not
	// set a default is used.
	ProbeInterval time.Duration
	// RevocationStore keeps track of the revocations.
	RevocationStore RevocationStore
	// RemoteWatcherFactory creates a RemoteWatcher for the specified remote.
	RemoteWatcherFactory RemoteWatcherFactory
	// Logger is the logger. If nil, nothing is logged.
	Logger log.Logger

	// conn is the snet connection used to exchange the probes.
	conn snet.PacketConn
	// stopChannel transports stop signal to the worker goroutine.
	stopChannel chan struct{}

	mutex sync.Mutex
	// remoteWatchers is a map of all monitored IAs.
	remoteWatchers map[addr.IA]*remoteWatcherItem
	pktChan        <-chan traceroutePkt
}

// Run starts the monitor and blocks until Close is called.
func (m *Monitor) Run() {
	if m.stopChannel != nil {
		panic("monitor must only be started once")
	}

	if m.ProbeInterval == 0 {
		m.ProbeInterval = defaultProbeInterval
	}
	m.stopChannel = make(chan struct{})
	m.remoteWatchers = make(map[addr.IA]*remoteWatcherItem)
	pktChan := make(chan traceroutePkt, 10)
	m.pktChan = pktChan
	m.conn = snet.NewSCIONPacketConn(m.Conn,
		scmpHandler{
			wrappedHandler: snet.DefaultSCMPHandler{RevocationHandler: m.RevocationHandler},
			pkts:           pktChan,
		},
		true,
	)

	log.SafeInfo(m.Logger, "Started PathMonitor")
	go func() {
		defer log.HandlePanic()
		m.handleProbeReplies()
	}()
	go func() {
		defer log.HandlePanic()
		m.drainConn()
	}()
	m.run()
}

// Register starts monitoring given AS under the specified selector.
func (m *Monitor) Register(remote addr.IA, selector PathSelector) *Registration {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	// If a monitor for the given AS does not exist, create it.
	// Otherwise, increase its reference count.
	remoteWatcher := m.remoteWatchers[remote]
	if remoteWatcher == nil {
		remoteWatcher = &remoteWatcherItem{
			RemoteWatcher: m.RemoteWatcherFactory.New(remote),
			refCount:      1,
		}
		m.remoteWatchers[remote] = remoteWatcher
		remoteWatcher.UpdatePaths(m.Router)
	} else {
		remoteWatcher.refCount++
	}
	return &Registration{
		monitor:       m,
		remoteWatcher: remoteWatcher,
		pathSelector:  selector,
	}
}

// Close stops the path monitor.
func (m *Monitor) Close() {
	close(m.stopChannel)
}

func (m *Monitor) unregister(remoteWatcher *remoteWatcherItem) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	// If the monitor for the IA is not needed any more, remove it.
	remoteWatcher.refCount--
	if remoteWatcher.refCount == 0 {
		delete(m.remoteWatchers, remoteWatcher.remote)
	}
}

// run triggers periodical tasks on the pathmonitor.
func (m *Monitor) run() {
	probeTicker := time.NewTicker(m.ProbeInterval)
	defer probeTicker.Stop()
	defer m.conn.Close() // This kills the handleProbeReply goroutine.
	defer log.SafeInfo(m.Logger, "Terminated PathMonitor")

	for {
		select {
		case <-probeTicker.C:
			m.updatePaths()
			m.cleanup()
			m.sendProbes()
		case <-m.stopChannel:
			return
		}
	}
}

// updatePaths gets new set of paths from SCION daemon.
func (m *Monitor) updatePaths() {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	for _, remoteWatcher := range m.remoteWatchers {
		remoteWatcher.UpdatePaths(m.Router)
	}
}

// sendProbes sends probes through all the monitored paths.
func (m *Monitor) sendProbes() {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	localAddr := snet.SCIONAddress{IA: m.LocalIA, Host: addr.HostFromIP(m.LocalIP)}
	for _, remoteWatcher := range m.remoteWatchers {
		remoteWatcher.SendProbes(m.conn, localAddr)
	}
}

// cleanup deletes all unused paths from monitoring as well as all expired revocations.
func (m *Monitor) cleanup() {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	for _, remoteWatcher := range m.remoteWatchers {
		remoteWatcher.Cleanup()
	}
	m.RevocationStore.Cleanup()
}

func (m *Monitor) drainConn() {
	closing := func() bool {
		select {
		case <-m.stopChannel:
			return true
		default:
			return false
		}
	}

	for {
		var pkt snet.Packet
		var ov net.UDPAddr

		err := m.conn.ReadFrom(&pkt, &ov)
		// This avoids logging errors for closing connections.
		if closing() {
			return
		}
		if err != nil {
			opErr, ok := err.(*snet.OpError)
			if ok {
				m.handleRevocation(opErr.RevInfo())
				continue
			}
			log.SafeInfo(m.Logger, "Unexpected error when reading probe replies", "err", err)
		}
	}
}

// handleProbeReplies reads incoming probe replies and dispatches them as needed.
func (m *Monitor) handleProbeReplies() {
	for {
		select {
		case pkt := <-m.pktChan:
			m.handleProbeReply(pkt)
		case <-m.stopChannel:
			return
		}
	}
}

// handleRevocation deals with a received revocation
func (m *Monitor) handleRevocation(rev *path_mgmt.RevInfo) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	m.RevocationStore.AddRevocation(rev)
}

// handleProbeReply dispatches a single probe reply packet.
func (m *Monitor) handleProbeReply(pkt traceroutePkt) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	remoteWatcher := m.remoteWatchers[pkt.Remote]
	if remoteWatcher == nil {
		log.SafeDebug(m.Logger, "Unsolicited reply (ISD-AS no longer monitored)",
			"remote", pkt.Remote)
		// Reply from an IA that is no longer monitored.
		return
	}
	remoteWatcher.HandleProbeReply(pkt.Identifier, pkt.Sequence)
}

// remoteWatcherItem is a helper structure that augments RemoteWatcher with
// Monitor specific metadata.
type remoteWatcherItem struct {
	RemoteWatcher
	remote addr.IA
	// refCount keeps track of how many references to this object there are.
	refCount int
}
<commit_msg>gateway: throttle  logging when dispatcher is down<commit_after>// Copyright 2020 Anapaya Systems
//
// 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 pathhealth monitors paths to different ASes. Call Monitor.Register()
// to start monitoring paths to a remote AS using a chosen path policy. The call
// returns a registration object which can be used to obtain the best path.
package pathhealth

import (
	"errors"
	"io"
	"net"
	"sync"
	"time"

	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/ctrl/path_mgmt"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/snet"
)

// defaultProbeInterval specifies how often should path probes be sent.
const defaultProbeInterval = 500 * time.Millisecond

// RemoteWatcher watches multiple paths to a given remote.
type RemoteWatcher interface {
	// UpdatePaths gets new paths from the SCION daemon. This method may block.
	UpdatePaths(snet.Router)
	// SendProbes sends probes on all the monitored paths.
	SendProbes(conn snet.PacketConn, localAddr snet.SCIONAddress)
	// HandleProbeReply handles a single probe reply packet.
	HandleProbeReply(id, seq uint16)
	// Cleanup stops monitoring paths that are not being used anymore.
	Cleanup()
	// Watchers returns a list of all active PathWatchers
	Watchers() []PathWatcher
}

// RemoteWatcherFactory creates RemoteWatchers.
type RemoteWatcherFactory interface {
	New(remote addr.IA) RemoteWatcher
}

// RevocationStore keeps track of revocations.
type RevocationStore interface {
	// AddRevocation adds a revocation.
	AddRevocation(rev *path_mgmt.RevInfo)
	// IsRevoked returns true if there is at least one revoked interface on the path.
	IsRevoked(path snet.Path) bool
	// Cleanup removes all expired revocations.
	Cleanup()
}

// Monitor monitors paths to a set of remote ASes.
type Monitor struct {
	// LocalIA is the ID of the local AS.
	LocalIA addr.IA
	// LocalIP is the IP address of the local host.
	LocalIP net.IP
	// Conn is the underlying conn for sending probes
	Conn net.PacketConn
	// RevocationHandler is the revocation handler.
	RevocationHandler snet.RevocationHandler
	// Router is the path manager connected to the SCION daemon.
	Router snet.Router
	// Probeinterval defines the interval at which probes are sent. If it is not
	// set a default is used.
	ProbeInterval time.Duration
	// RevocationStore keeps track of the revocations.
	RevocationStore RevocationStore
	// RemoteWatcherFactory creates a RemoteWatcher for the specified remote.
	RemoteWatcherFactory RemoteWatcherFactory
	// Logger is the logger. If nil, nothing is logged.
	Logger log.Logger

	// conn is the snet connection used to exchange the probes.
	conn snet.PacketConn
	// stopChannel transports stop signal to the worker goroutine.
	stopChannel chan struct{}

	mutex sync.Mutex
	// remoteWatchers is a map of all monitored IAs.
	remoteWatchers map[addr.IA]*remoteWatcherItem
	pktChan        <-chan traceroutePkt
}

// Run starts the monitor and blocks until Close is called.
func (m *Monitor) Run() {
	if m.stopChannel != nil {
		panic("monitor must only be started once")
	}

	if m.ProbeInterval == 0 {
		m.ProbeInterval = defaultProbeInterval
	}
	m.stopChannel = make(chan struct{})
	m.remoteWatchers = make(map[addr.IA]*remoteWatcherItem)
	pktChan := make(chan traceroutePkt, 10)
	m.pktChan = pktChan
	m.conn = snet.NewSCIONPacketConn(m.Conn,
		scmpHandler{
			wrappedHandler: snet.DefaultSCMPHandler{RevocationHandler: m.RevocationHandler},
			pkts:           pktChan,
		},
		true,
	)

	log.SafeInfo(m.Logger, "Started PathMonitor")
	go func() {
		defer log.HandlePanic()
		m.handleProbeReplies()
	}()
	go func() {
		defer log.HandlePanic()
		m.drainConn()
	}()
	m.run()
}

// Register starts monitoring given AS under the specified selector.
func (m *Monitor) Register(remote addr.IA, selector PathSelector) *Registration {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	// If a monitor for the given AS does not exist, create it.
	// Otherwise, increase its reference count.
	remoteWatcher := m.remoteWatchers[remote]
	if remoteWatcher == nil {
		remoteWatcher = &remoteWatcherItem{
			RemoteWatcher: m.RemoteWatcherFactory.New(remote),
			refCount:      1,
		}
		m.remoteWatchers[remote] = remoteWatcher
		remoteWatcher.UpdatePaths(m.Router)
	} else {
		remoteWatcher.refCount++
	}
	return &Registration{
		monitor:       m,
		remoteWatcher: remoteWatcher,
		pathSelector:  selector,
	}
}

// Close stops the path monitor.
func (m *Monitor) Close() {
	close(m.stopChannel)
}

func (m *Monitor) unregister(remoteWatcher *remoteWatcherItem) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	// If the monitor for the IA is not needed any more, remove it.
	remoteWatcher.refCount--
	if remoteWatcher.refCount == 0 {
		delete(m.remoteWatchers, remoteWatcher.remote)
	}
}

// run triggers periodical tasks on the pathmonitor.
func (m *Monitor) run() {
	probeTicker := time.NewTicker(m.ProbeInterval)
	defer probeTicker.Stop()
	defer m.conn.Close() // This kills the handleProbeReply goroutine.
	defer log.SafeInfo(m.Logger, "Terminated PathMonitor")

	for {
		select {
		case <-probeTicker.C:
			m.updatePaths()
			m.cleanup()
			m.sendProbes()
		case <-m.stopChannel:
			return
		}
	}
}

// updatePaths gets new set of paths from SCION daemon.
func (m *Monitor) updatePaths() {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	for _, remoteWatcher := range m.remoteWatchers {
		remoteWatcher.UpdatePaths(m.Router)
	}
}

// sendProbes sends probes through all the monitored paths.
func (m *Monitor) sendProbes() {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	localAddr := snet.SCIONAddress{IA: m.LocalIA, Host: addr.HostFromIP(m.LocalIP)}
	for _, remoteWatcher := range m.remoteWatchers {
		remoteWatcher.SendProbes(m.conn, localAddr)
	}
}

// cleanup deletes all unused paths from monitoring as well as all expired revocations.
func (m *Monitor) cleanup() {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	for _, remoteWatcher := range m.remoteWatchers {
		remoteWatcher.Cleanup()
	}
	m.RevocationStore.Cleanup()
}

func (m *Monitor) drainConn() {
	closing := func() bool {
		select {
		case <-m.stopChannel:
			return true
		default:
			return false
		}
	}

	for {
		var pkt snet.Packet
		var ov net.UDPAddr

		err := m.conn.ReadFrom(&pkt, &ov)
		// This avoids logging errors for closing connections.
		if closing() {
			return
		}
		if errors.Is(err, io.EOF) {
			// dispatcher is currently down so back off.
			time.Sleep(500 * time.Millisecond)
			continue
		}
		if err != nil {
			opErr, ok := err.(*snet.OpError)
			if ok {
				m.handleRevocation(opErr.RevInfo())
				continue
			}
			log.SafeInfo(m.Logger, "Unexpected error when reading probe replies", "err", err)
		}
	}
}

// handleProbeReplies reads incoming probe replies and dispatches them as needed.
func (m *Monitor) handleProbeReplies() {
	for {
		select {
		case pkt := <-m.pktChan:
			m.handleProbeReply(pkt)
		case <-m.stopChannel:
			return
		}
	}
}

// handleRevocation deals with a received revocation
func (m *Monitor) handleRevocation(rev *path_mgmt.RevInfo) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	m.RevocationStore.AddRevocation(rev)
}

// handleProbeReply dispatches a single probe reply packet.
func (m *Monitor) handleProbeReply(pkt traceroutePkt) {
	m.mutex.Lock()
	defer m.mutex.Unlock()
	remoteWatcher := m.remoteWatchers[pkt.Remote]
	if remoteWatcher == nil {
		log.SafeDebug(m.Logger, "Unsolicited reply (ISD-AS no longer monitored)",
			"remote", pkt.Remote)
		// Reply from an IA that is no longer monitored.
		return
	}
	remoteWatcher.HandleProbeReply(pkt.Identifier, pkt.Sequence)
}

// remoteWatcherItem is a helper structure that augments RemoteWatcher with
// Monitor specific metadata.
type remoteWatcherItem struct {
	RemoteWatcher
	remote addr.IA
	// refCount keeps track of how many references to this object there are.
	refCount int
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"reflect"
	"sync"
	"time"

	"github.com/andlabs/ui"
	"github.com/cryptix/gohn"
	"github.com/skratchdot/open-golang/open"
)

type displayItem struct {
	Idx                        int
	Type, Title, Text, URL, By string
}

var (
	window ui.Window
	status ui.Label
	hn     *gohn.Client

	newItem chan *displayItem
)

func main() {

	go ui.Do(func() {
		reload := ui.NewButton("Reload")
		status = ui.NewStandaloneLabel("")

		table := ui.NewTable(reflect.TypeOf(displayItem{}))
		table.OnSelected(func() {
			idx := table.Selected()
			if idx >= 0 {
				table.RLock()
				td := table.Data().(*[]displayItem)
				open.Start((*td)[idx].URL)
				table.RUnlock()
			}
		})

		go func() {
			newItem = make(chan *displayItem)

			table.Lock()
			td := table.Data().(*[]displayItem)
			*td = make([]displayItem, 100)
			table.Unlock()

			for item := range newItem {
				status.SetText(fmt.Sprintf("got item %d", item.Idx))
				time.Sleep(30 * time.Millisecond)
				table.Lock()
				td := table.Data().(*[]displayItem)
				(*td)[item.Idx] = *item
				table.Unlock()
			}
		}()

		stack := ui.NewVerticalStack(
			reload,
			status,
			table)
		stack.SetStretchy(2)

		window = ui.NewWindow("HNui - TopStories", 200, 500, stack)

		reload.OnClicked(func() {
			go updateItems(newItem)
		})

		window.OnClosing(func() bool {
			ui.Stop()
			return true
		})

		window.Show()
	})

	hn = gohn.NewClient(nil)

	err := ui.Go()
	if err != nil {
		panic(err)
	}
}

func updateItems(ichan chan<- *displayItem) {
	status.SetText("Update started...")
	ids, err := hn.Items.TopStoryIDs()
	if err != nil {
		status.SetText(fmt.Sprint("TopStoryIDs() Err: ", err.Error()))
		return
	}

	var wg sync.WaitGroup
	for idx, id := range ids {
		wg.Add(1)
		go updateItem(&wg, ichan, idx, id)
	}

	wg.Wait()
	status.SetText("Update done!")
}

func updateItem(wg *sync.WaitGroup, ichan chan<- *displayItem, i, id int) {
	defer wg.Done()

	item, err := hn.Items.Item(id)
	if err != nil {
		fmt.Println("Items() Err:", err)
		ichan <- &displayItem{
			Idx:   i,
			Type:  "Err",
			Title: item.Title,
			Text:  err.Error(),
		}

		// reload beachballs
		wg.Add(1)
		go updateItem(wg, ichan, i, id)
		return
	}

	ichan <- &displayItem{
		Idx:   i,
		Type:  item.Type,
		Title: item.Title,
		Text:  item.Text,
		URL:   item.URL,
		By:    item.By,
	}
}
<commit_msg>removed sleep from displayItem chan, doesnt beachball anymore somehow...<commit_after>package main

import (
	"fmt"
	"reflect"
	"sync"

	"github.com/andlabs/ui"
	"github.com/cryptix/gohn"
	"github.com/skratchdot/open-golang/open"
)

type displayItem struct {
	Idx                        int
	Type, Title, Text, URL, By string
}

var (
	window ui.Window
	status ui.Label
	hn     *gohn.Client

	newItem chan *displayItem
)

func main() {

	go ui.Do(func() {
		reload := ui.NewButton("Reload")
		status = ui.NewStandaloneLabel("")

		table := ui.NewTable(reflect.TypeOf(displayItem{}))

		// on click > open url of selected item
		table.OnSelected(func() {
			idx := table.Selected()
			if idx >= 0 {
				table.RLock()
				td := table.Data().(*[]displayItem)
				open.Start((*td)[idx].URL)
				table.RUnlock()
			}
		})

		// iterates over chan and updates items in the table
		go func() {
			newItem = make(chan *displayItem)

			table.Lock()
			td := table.Data().(*[]displayItem)
			*td = make([]displayItem, 100)
			table.Unlock()

			for item := range newItem {
				status.SetText(fmt.Sprintf("got item %d", item.Idx))
				table.Lock()
				td := table.Data().(*[]displayItem)
				(*td)[item.Idx] = *item
				table.Unlock()
			}
		}()

		// construct main view stack
		stack := ui.NewVerticalStack(
			reload,
			status,
			table)
		stack.SetStretchy(2)

		window = ui.NewWindow("HNui - TopStories", 200, 500, stack)

		reload.OnClicked(func() {
			go updateItems(newItem)
		})

		window.OnClosing(func() bool {
			ui.Stop()
			return true
		})

		window.Show()
	})

	hn = gohn.NewClient(nil)

	err := ui.Go()
	if err != nil {
		panic(err)
	}
}

func updateItems(ichan chan<- *displayItem) {
	status.SetText("Update started...")

	// get the current top IDs
	ids, err := hn.Items.TopStoryIDs()
	if err != nil {
		status.SetText(fmt.Sprint("TopStoryIDs() Err: ", err.Error()))
		return
	}

	// start a go routine for each ID and push them to the displayItem channel
	var wg sync.WaitGroup
	for idx, id := range ids {
		wg.Add(1)
		go updateItem(&wg, ichan, idx, id)
	}

	wg.Wait()
	status.SetText("Update done!")
}

func updateItem(wg *sync.WaitGroup, ichan chan<- *displayItem, i, id int) {
	defer wg.Done()

	item, err := hn.Items.Item(id)
	// try again if it fails (EOF, timeout, ...)
	if err != nil {
		fmt.Println("Items() Err:", err)
		ichan <- &displayItem{
			Idx:   i,
			Type:  "Err",
			Title: item.Title,
			Text:  err.Error(),
		}

		wg.Add(1)
		go updateItem(wg, ichan, i, id)
		return
	}

	ichan <- &displayItem{
		Idx:   i,
		Type:  item.Type,
		Title: item.Title,
		Text:  item.Text,
		URL:   item.URL,
		By:    item.By,
	}
}
<|endoftext|>


<commit_before>package util_test

import (
	"fmt"
	"testing"

	"github.com/ello/ello-go/common/util"
)

func ExampleValidateInt() {
	value, err := util.ValidateInt("15", 10) // validates, returns parsed val

	fmt.Printf("%v | %v | ", value, err)

	value, err = util.ValidateInt("1a5", 10) // fails to validate, returns default

	fmt.Printf("%v | %v", value, err)
	// Output: 15 | <nil> | 10 | strconv.ParseInt: parsing "1a5": invalid syntax
}

func TestValidateInt(t *testing.T) {
	testVal, err := util.ValidateInt("15", 10)

	if err != nil {
		t.Error("'15' should pass validation")
	}
	if 15 != testVal {
		t.Error("validation should return 15")
	}

	testVal, err = util.ValidateInt("1a5", 10)

	if err == nil {
		t.Error("'1a5' should fail validation")
	}
	if 10 != testVal {
		t.Error("validation should return 10 (default value)")
	}

	testVal, err = util.ValidateInt("", 10)

	if err != nil {
		t.Error("'' should pass validation (empty string)")
	}
	if 10 != testVal {
		t.Error("validation should return 10 (default value)")
	}
}
<commit_msg>Updated examples to pass<commit_after>package util_test

import (
	"fmt"
	"testing"

	"github.com/ello/ello-go/common/util"
)

func ExampleValidateInt() {
	value, err := util.ValidateInt("1a5", 10) // fails to validate, returns default
	if err != nil {
		// if you care that validation failed, otherwise value == default (10)
	}

	value, err = util.ValidateInt("15", 10) // validates, returns parsed val

	fmt.Printf("%v | %v | ", value, err) // Output: 15 | <nil>
}

func TestValidateInt(t *testing.T) {
	testVal, err := util.ValidateInt("15", 10)

	if err != nil {
		t.Error("'15' should pass validation")
	}
	if 15 != testVal {
		t.Error("validation should return 15")
	}

	testVal, err = util.ValidateInt("1a5", 10)

	if err == nil {
		t.Error("'1a5' should fail validation")
	}
	if 10 != testVal {
		t.Error("validation should return 10 (default value)")
	}

	testVal, err = util.ValidateInt("", 10)

	if err != nil {
		t.Error("'' should pass validation (empty string)")
	}
	if 10 != testVal {
		t.Error("validation should return 10 (default value)")
	}
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	jwt "github.com/dgrijalva/jwt-go"
	konfig "github.com/koding/kite/config"
	"github.com/koding/kite/kitekey"
)

// KonfigCache is a default konfig.bolt configuration.
//
// The konfig.bolt stores user configuration for
// KD / Klient apps.
var KonfigCache = &CacheOptions{
	File: filepath.Join(KodingHome(), "konfig.bolt"),
	BoltDB: &bolt.Options{
		Timeout: 5 * time.Second,
	},
	Bucket: []byte("konfig"),
}

// Endpoints represents a configuration of Koding
// endpoints, which are used by KD / Klient.
type Endpoints struct {
	// Koding base endpoint.
	Koding *Endpoint `json:"koding,omitempty"`

	// Tunnel / proxy environment endpoints.
	Tunnel  *Endpoint `json:"tunnel,omitempty"`
	IP      *Endpoint `json:"ip,omitempty"`
	IPCheck *Endpoint `json:"ipCheck,omitempty"`

	// Klient / KD endpoints.
	KlientLatest *Endpoint `json:"klientLatest,omitempty"`
	KDLatest     *Endpoint `json:"kdLatest,omitempty"`
	Klient       *Endpoint `json:"klient,omitempty"`
}

// Kloud gives an endpoint for Kloud kite.
func (e *Endpoints) Kloud() *Endpoint {
	return e.Koding.WithPath("/kloud/kite")
}

// Kontrl gives an endpoint for Kontrol kite.
func (e *Endpoints) Kontrol() *Endpoint {
	return e.Koding.WithPath("/kontrol/kite")
}

// Remtoe gives an endpoint for remote.api.
func (e *Endpoints) Remote() *Endpoint {
	return e.Koding.WithPath("/remote.api")
}

// Social gives an endpoint for social.api.
func (e *Endpoints) Social() *Endpoint {
	return e.Koding.WithPath("/api/social")
}

// Local describes configuration of local paths.
type Local struct {
	// Mount is a default home path of mounted directories.
	//
	// If empty, defaults to ~/koding/mnt/.
	MountHome string `json:"mountHome,omitempty"`

	// Mounts maps named mounts to local paths.
	//
	// The Mounts["default"] export is used as
	// a default one when caller does not specify
	// a mount path.
	//
	// The Mounts["default"] defaults to $HOME.
	Mounts map[string]string `json:"mounts,omitempty"`
}

// MountPath gives a path for the named mount.
//
// If the named mount does not exist, it returns false.
//
// If the path contains '~' - it is expended to
// a home directory of a current user.
func (l *Local) MountPath(name string) (string, bool) {
	if l == nil {
		return "", false
	}

	if dir, ok := l.Mounts[name]; ok {
		return expandHome(dir), true
	}

	return "", false
}

// Konfig represents a single configuration stored
// in a konfig.bolt database.
type Konfig struct {
	Endpoints *Endpoints `json:"endpoints,omitempty"`

	KontrolURL string `json:"kontrolURL,omitempty"` // deprecated / read-only
	TunnelURL  string `json:"tunnelURL,omitempty"`  // deprecated / read-only

	// Kite configuration.
	Environment string `json:"environment,omitempty"`
	KiteKeyFile string `json:"kiteKeyFile,omitempty"`
	KiteKey     string `json:"kiteKey,omitempty"`

	// Local describes configuration of local paths.
	Local *Local `json:"local,omitempty"`

	// Koding networking configuration.
	//
	// TODO(rjeczalik): store command line flags in konfig.bolt
	// per Koding executable (KD / Klient).
	TunnelID string `json:"tunnelID,omitempty"`

	// Public S3 bucket for writing logs.
	PublicBucketName   string `json:"publicBucketName,omitempty"`
	PublicBucketRegion string `json:"publicBucketRegion,omitempty"`

	Debug bool `json:"debug,string,omitempty"`
}

// KiteHome gives directory of the kite.key file.
//
// Deprecated: Use KiteKeyFile instead.
func (k *Konfig) KiteHome() string {
	return filepath.Dir(k.KiteKeyFile)
}

// KiteConfig build *kite.Config value which is used
// to initialize new kite.Kite values.
func (k *Konfig) KiteConfig() *konfig.Config {
	return k.buildKiteConfig()
}

// KlientGzURL gives an URL for Klient binary.
//
// TODO(rjeczalik): Rework into lookup and move to kloud/metadata package.
func (k *Konfig) KlientGzURL() string {
	u := *k.Endpoints.KlientLatest.Public.URL
	u.Path = path.Join(path.Dir(u.Path), "latest", "klient.gz")
	return u.String()
}

// Valid implements the stack.Validator interface.
func (k *Konfig) Valid() error {
	// TODO(rjeczalik): remove when KontrolURL is gone
	if _, err := url.Parse(k.KontrolURL); err == nil && k.KontrolURL != "" {
		return nil
	}

	if k.Endpoints == nil {
		return errors.New("endpoints are nil")
	}
	if k.Endpoints.Koding == nil {
		return errors.New("koding base endpoint is nil")
	}
	if k.Endpoints.Koding.Public == nil {
		return errors.New("public URL for koding base endpoint is nil")
	}
	return nil
}

// ID gives an identifier of the Konfig value.
//
// The konfig.bolt stores multiple configuration, one for each
// baseurl. The ID is unique per baseurl.
func (k *Konfig) ID() string {
	return ID(k.KodingPublic().String())
}

// ID create an identifier for the given Koding base URL.
func ID(kodingURL string) string {
	if kodingURL == "" {
		return ""
	}
	if u, err := url.Parse(kodingURL); err == nil {
		// Since id is input sensitive we clean the path so "example.com/koding"
		// "example.com/koding/" are effectively the same urls.
		u.Path = strings.TrimRight(path.Clean(u.Path), "/")
		kodingURL = u.String()
	}
	hash := sha1.Sum([]byte(kodingURL))
	return hex.EncodeToString(hash[:4])
}

// KodingPublic is here for backward-compatibility purposes.
//
// Old klient and kd deployments may not have .Endpoints configuration
// on a first run, this is why we fallback to old KontrolURL field.
//
// Which we should eventually get rid of.
//
// Deprecated: Use k.Endpoints.Koding.Public instead.
func (k *Konfig) KodingPublic() *url.URL {
	if e := k.Endpoints; e != nil && e.Koding != nil && e.Koding.Public != nil {
		return e.Koding.Public.URL
	}

	if u, err := url.Parse(k.KontrolURL); err == nil {
		u.Path = ""
		return u
	}

	return nil
}

func (k *Konfig) buildKiteConfig() *konfig.Config {
	kiteKey := k.KiteKey

	if kiteKey == "" && k.KiteKeyFile != "" {
		if p, err := ioutil.ReadFile(k.KiteKeyFile); err == nil && len(p) != 0 {
			kiteKey = string(bytes.TrimSpace(p))
		}
	}

	if kiteKey != "" {
		tok, err := jwt.ParseWithClaims(kiteKey, &kitekey.KiteClaims{}, kitekey.GetKontrolKey)
		if err == nil {
			cfg := NewKiteConfig(k.Debug)

			if err = cfg.ReadToken(tok); err == nil {
				return cfg
			}
		}
	}

	if cfg, err := ReadKiteConfig(k.Debug); err == nil {
		return cfg
	}

	return NewKiteConfig(k.Debug)
}

// NewKonfigURL gives new configuration for the given base URL.
func NewKonfigURL(koding *url.URL) *Konfig {
	return &Konfig{
		Endpoints: &Endpoints{
			Koding: NewEndpointURL(koding),
		},
	}
}

type Konfigs map[string]*Konfig

func (kfg Konfigs) Slice() []*Konfig {
	keys := make([]string, 0, len(kfg))
	for k := range kfg {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	slice := make([]*Konfig, 0, len(kfg))
	for _, key := range keys {
		slice = append(slice, kfg[key])
	}
	return slice
}

// Environment is a hacky workaround for kd <-> klient environments.
// The managed klient expects to have kd from production channel,
// and devmanaged klient - from development. Depending from which
// app we load the the default Koding configuration, we need
// to cross-map the environments.
//
// TODO(rjeczalik): This should be fixed by removing managed / devmanaged
// channels - that is to improve "connect your vm" modal to
// not depend on special klient environment.
type Environments struct {
	Env       string
	KlientEnv string // Env is used if empty
	KDEnv     string // Env is used if empty
}

func (e *Environments) klientEnv() string {
	if e.KlientEnv != "" {
		return e.KlientEnv
	}
	return e.Env
}

func (e *Environments) kdEnv() string {
	if e.KDEnv != "" {
		return e.KDEnv
	}
	return e.Env
}

// NewKonfig creates new configuration by reading
// embedded kites/config/config.json file.
func NewKonfig(e *Environments) *Konfig {
	return &Konfig{
		Environment: e.Env,
		Endpoints: &Endpoints{
			Koding:       Builtin.Endpoints.KodingBase.Copy(),
			Tunnel:       Builtin.Endpoints.TunnelServer.Copy(),
			IP:           Builtin.Endpoints.IP.Copy(),
			IPCheck:      Builtin.Endpoints.IPCheck.Copy(),
			KlientLatest: ReplaceEnv(Builtin.Endpoints.KlientLatest, e.klientEnv()),
			KDLatest:     ReplaceEnv(Builtin.Endpoints.KDLatest, RmManaged(e.kdEnv())),
			Klient: &Endpoint{
				Private: &URL{&url.URL{
					Scheme: "http",
					Host:   "127.0.0.1:56789",
					Path:   "/kite",
				}},
			},
		},
		Local: &Local{
			MountHome: filepath.Join(CurrentUser.HomeDir, "koding", "mnt"),
			Mounts: map[string]string{
				"default": CurrentUser.HomeDir,
			},
		},
		PublicBucketName:   Builtin.Buckets.PublicLogs.Name,
		PublicBucketRegion: Builtin.Buckets.PublicLogs.Region,
		Debug:              false,
	}
}

func expandHome(path string) string {
	const home = "~" + string(os.PathSeparator)

	switch {
	case path == "~":
		return CurrentUser.HomeDir
	case strings.HasPrefix(path, home):
		return filepath.Join(CurrentUser.HomeDir, path[len(home):])
	default:
		return path
	}
}
<commit_msg>kites/config: add template.file key<commit_after>package config

import (
	"bytes"
	"crypto/sha1"
	"encoding/hex"
	"errors"
	"io/ioutil"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	jwt "github.com/dgrijalva/jwt-go"
	konfig "github.com/koding/kite/config"
	"github.com/koding/kite/kitekey"
)

// KonfigCache is a default konfig.bolt configuration.
//
// The konfig.bolt stores user configuration for
// KD / Klient apps.
var KonfigCache = &CacheOptions{
	File: filepath.Join(KodingHome(), "konfig.bolt"),
	BoltDB: &bolt.Options{
		Timeout: 5 * time.Second,
	},
	Bucket: []byte("konfig"),
}

// Endpoints represents a configuration of Koding
// endpoints, which are used by KD / Klient.
type Endpoints struct {
	// Koding base endpoint.
	Koding *Endpoint `json:"koding,omitempty"`

	// Tunnel / proxy environment endpoints.
	Tunnel  *Endpoint `json:"tunnel,omitempty"`
	IP      *Endpoint `json:"ip,omitempty"`
	IPCheck *Endpoint `json:"ipCheck,omitempty"`

	// Klient / KD endpoints.
	KlientLatest *Endpoint `json:"klientLatest,omitempty"`
	KDLatest     *Endpoint `json:"kdLatest,omitempty"`
	Klient       *Endpoint `json:"klient,omitempty"`
}

// Kloud gives an endpoint for Kloud kite.
func (e *Endpoints) Kloud() *Endpoint {
	return e.Koding.WithPath("/kloud/kite")
}

// Kontrl gives an endpoint for Kontrol kite.
func (e *Endpoints) Kontrol() *Endpoint {
	return e.Koding.WithPath("/kontrol/kite")
}

// Remtoe gives an endpoint for remote.api.
func (e *Endpoints) Remote() *Endpoint {
	return e.Koding.WithPath("/remote.api")
}

// Social gives an endpoint for social.api.
func (e *Endpoints) Social() *Endpoint {
	return e.Koding.WithPath("/api/social")
}

// Local describes configuration of local paths.
type Local struct {
	// Mount is a default home path of mounted directories.
	//
	// If empty, defaults to ~/koding/mnt/.
	MountHome string `json:"mountHome,omitempty"`

	// Mounts maps named mounts to local paths.
	//
	// The Mounts["default"] export is used as
	// a default one when caller does not specify
	// a mount path.
	//
	// The Mounts["default"] defaults to $HOME.
	Mounts map[string]string `json:"mounts,omitempty"`
}

// MountPath gives a path for the named mount.
//
// If the named mount does not exist, it returns false.
//
// If the path contains '~' - it is expended to
// a home directory of a current user.
func (l *Local) MountPath(name string) (string, bool) {
	if l == nil {
		return "", false
	}

	if dir, ok := l.Mounts[name]; ok {
		return expandHome(dir), true
	}

	return "", false
}

// Template represents a KD template configuration.
type Template struct {
	File string `json:"file,omitempty"`
}

// Konfig represents a single configuration stored
// in a konfig.bolt database.
type Konfig struct {
	Endpoints *Endpoints `json:"endpoints,omitempty"`

	KontrolURL string `json:"kontrolURL,omitempty"` // deprecated / read-only
	TunnelURL  string `json:"tunnelURL,omitempty"`  // deprecated / read-only

	// Kite configuration.
	Environment string `json:"environment,omitempty"`
	KiteKeyFile string `json:"kiteKeyFile,omitempty"`
	KiteKey     string `json:"kiteKey,omitempty"`

	// Local describes configuration of local paths.
	Local *Local `json:"local,omitempty"`

	// Template describes configuration of KD template.
	Template *Template `json:"template,omitempty"`

	// Koding networking configuration.
	//
	// TODO(rjeczalik): store command line flags in konfig.bolt
	// per Koding executable (KD / Klient).
	TunnelID string `json:"tunnelID,omitempty"`

	// Public S3 bucket for writing logs.
	PublicBucketName   string `json:"publicBucketName,omitempty"`
	PublicBucketRegion string `json:"publicBucketRegion,omitempty"`

	Debug bool `json:"debug,string,omitempty"`
}

// KiteHome gives directory of the kite.key file.
//
// Deprecated: Use KiteKeyFile instead.
func (k *Konfig) KiteHome() string {
	return filepath.Dir(k.KiteKeyFile)
}

// KiteConfig build *kite.Config value which is used
// to initialize new kite.Kite values.
func (k *Konfig) KiteConfig() *konfig.Config {
	return k.buildKiteConfig()
}

// KlientGzURL gives an URL for Klient binary.
//
// TODO(rjeczalik): Rework into lookup and move to kloud/metadata package.
func (k *Konfig) KlientGzURL() string {
	u := *k.Endpoints.KlientLatest.Public.URL
	u.Path = path.Join(path.Dir(u.Path), "latest", "klient.gz")
	return u.String()
}

// Valid implements the stack.Validator interface.
func (k *Konfig) Valid() error {
	// TODO(rjeczalik): remove when KontrolURL is gone
	if _, err := url.Parse(k.KontrolURL); err == nil && k.KontrolURL != "" {
		return nil
	}

	if k.Endpoints == nil {
		return errors.New("endpoints are nil")
	}
	if k.Endpoints.Koding == nil {
		return errors.New("koding base endpoint is nil")
	}
	if k.Endpoints.Koding.Public == nil {
		return errors.New("public URL for koding base endpoint is nil")
	}
	return nil
}

// ID gives an identifier of the Konfig value.
//
// The konfig.bolt stores multiple configuration, one for each
// baseurl. The ID is unique per baseurl.
func (k *Konfig) ID() string {
	return ID(k.KodingPublic().String())
}

// ID create an identifier for the given Koding base URL.
func ID(kodingURL string) string {
	if kodingURL == "" {
		return ""
	}
	if u, err := url.Parse(kodingURL); err == nil {
		// Since id is input sensitive we clean the path so "example.com/koding"
		// "example.com/koding/" are effectively the same urls.
		u.Path = strings.TrimRight(path.Clean(u.Path), "/")
		kodingURL = u.String()
	}
	hash := sha1.Sum([]byte(kodingURL))
	return hex.EncodeToString(hash[:4])
}

// KodingPublic is here for backward-compatibility purposes.
//
// Old klient and kd deployments may not have .Endpoints configuration
// on a first run, this is why we fallback to old KontrolURL field.
//
// Which we should eventually get rid of.
//
// Deprecated: Use k.Endpoints.Koding.Public instead.
func (k *Konfig) KodingPublic() *url.URL {
	if e := k.Endpoints; e != nil && e.Koding != nil && e.Koding.Public != nil {
		return e.Koding.Public.URL
	}

	if u, err := url.Parse(k.KontrolURL); err == nil {
		u.Path = ""
		return u
	}

	return nil
}

func (k *Konfig) buildKiteConfig() *konfig.Config {
	kiteKey := k.KiteKey

	if kiteKey == "" && k.KiteKeyFile != "" {
		if p, err := ioutil.ReadFile(k.KiteKeyFile); err == nil && len(p) != 0 {
			kiteKey = string(bytes.TrimSpace(p))
		}
	}

	if kiteKey != "" {
		tok, err := jwt.ParseWithClaims(kiteKey, &kitekey.KiteClaims{}, kitekey.GetKontrolKey)
		if err == nil {
			cfg := NewKiteConfig(k.Debug)

			if err = cfg.ReadToken(tok); err == nil {
				return cfg
			}
		}
	}

	if cfg, err := ReadKiteConfig(k.Debug); err == nil {
		return cfg
	}

	return NewKiteConfig(k.Debug)
}

// NewKonfigURL gives new configuration for the given base URL.
func NewKonfigURL(koding *url.URL) *Konfig {
	return &Konfig{
		Endpoints: &Endpoints{
			Koding: NewEndpointURL(koding),
		},
	}
}

type Konfigs map[string]*Konfig

func (kfg Konfigs) Slice() []*Konfig {
	keys := make([]string, 0, len(kfg))
	for k := range kfg {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	slice := make([]*Konfig, 0, len(kfg))
	for _, key := range keys {
		slice = append(slice, kfg[key])
	}
	return slice
}

// Environment is a hacky workaround for kd <-> klient environments.
// The managed klient expects to have kd from production channel,
// and devmanaged klient - from development. Depending from which
// app we load the the default Koding configuration, we need
// to cross-map the environments.
//
// TODO(rjeczalik): This should be fixed by removing managed / devmanaged
// channels - that is to improve "connect your vm" modal to
// not depend on special klient environment.
type Environments struct {
	Env       string
	KlientEnv string // Env is used if empty
	KDEnv     string // Env is used if empty
}

func (e *Environments) klientEnv() string {
	if e.KlientEnv != "" {
		return e.KlientEnv
	}
	return e.Env
}

func (e *Environments) kdEnv() string {
	if e.KDEnv != "" {
		return e.KDEnv
	}
	return e.Env
}

// NewKonfig creates new configuration by reading
// embedded kites/config/config.json file.
func NewKonfig(e *Environments) *Konfig {
	return &Konfig{
		Environment: e.Env,
		Endpoints: &Endpoints{
			Koding:       Builtin.Endpoints.KodingBase.Copy(),
			Tunnel:       Builtin.Endpoints.TunnelServer.Copy(),
			IP:           Builtin.Endpoints.IP.Copy(),
			IPCheck:      Builtin.Endpoints.IPCheck.Copy(),
			KlientLatest: ReplaceEnv(Builtin.Endpoints.KlientLatest, e.klientEnv()),
			KDLatest:     ReplaceEnv(Builtin.Endpoints.KDLatest, RmManaged(e.kdEnv())),
			Klient: &Endpoint{
				Private: &URL{&url.URL{
					Scheme: "http",
					Host:   "127.0.0.1:56789",
					Path:   "/kite",
				}},
			},
		},
		Local: &Local{
			MountHome: filepath.Join(CurrentUser.HomeDir, "koding", "mnt"),
			Mounts: map[string]string{
				"default": CurrentUser.HomeDir,
			},
		},
		Template: &Template{
			File: "kd.yaml",
		},
		PublicBucketName:   Builtin.Buckets.PublicLogs.Name,
		PublicBucketRegion: Builtin.Buckets.PublicLogs.Region,
		Debug:              false,
	}
}

func expandHome(path string) string {
	const home = "~" + string(os.PathSeparator)

	switch {
	case path == "~":
		return CurrentUser.HomeDir
	case strings.HasPrefix(path, home):
		return filepath.Join(CurrentUser.HomeDir, path[len(home):])
	default:
		return path
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"runtime/pprof"

	flag "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag"
	commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"

	config "github.com/jbenet/go-ipfs/config"
	core "github.com/jbenet/go-ipfs/core"
	daemon "github.com/jbenet/go-ipfs/daemon"
	updates "github.com/jbenet/go-ipfs/updates"
	u "github.com/jbenet/go-ipfs/util"
)

// The IPFS command tree. It is an instance of `commander.Command`.
var CmdIpfs = &commander.Command{
	UsageLine: "ipfs [<flags>] <command> [<args>]",
	Short:     "global versioned p2p merkledag file system",
	Long: `ipfs - global versioned p2p merkledag file system

Basic commands:

    init          Initialize ipfs local configuration.
    add <path>    Add an object to ipfs.
    cat <ref>     Show ipfs object data.
    ls <ref>      List links from an object.
    refs <ref>    List link hashes from an object.

Tool commands:

    config        Manage configuration.
    version       Show ipfs version information.
    commands      List all available commands.

Advanced Commands:

    mount         Mount an ipfs read-only mountpoint.
    serve         Serve an interface to ipfs.

	net-diag	  Print network diagnostic

Use "ipfs help <command>" for more information about a command.
`,
	Run: ipfsCmd,
	Subcommands: []*commander.Command{
		cmdIpfsAdd,
		cmdIpfsCat,
		cmdIpfsLs,
		cmdIpfsRefs,
		cmdIpfsConfig,
		cmdIpfsVersion,
		cmdIpfsCommands,
		cmdIpfsMount,
		cmdIpfsInit,
		cmdIpfsServe,
		cmdIpfsRun,
		cmdIpfsName,
		cmdIpfsBootstrap,
		cmdIpfsDiag,
		cmdIpfsBlock,
		cmdIpfsLog,
	},
	Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
}

// log is the command logger
var log = u.Logger("cmd/ipfs")

func init() {
	config, err := config.PathRoot()
	if err != nil {
		log.Error("Failure initializing the default Config Directory: ", err)
		os.Exit(1)
	}
	CmdIpfs.Flag.String("c", config, "specify config directory")
}

func ipfsCmd(c *commander.Command, args []string) error {
	u.POut(c.Long)
	return nil
}

func main() {
	u.Debug = false

	// setup logging
	// u.SetupLogging() done in an init() block now.

	// if debugging, setup profiling.
	if u.Debug {
		ofi, err := os.Create("cpu.prof")
		if err != nil {
			fmt.Println(err)
			return
		}
		pprof.StartCPUProfile(ofi)
		defer ofi.Close()
		defer pprof.StopCPUProfile()
	}

	err := CmdIpfs.Dispatch(os.Args[1:])
	if err != nil {
		if len(err.Error()) > 0 {
			log.Error("ipfs %s: %v\n", os.Args[1], err)
		}
		os.Exit(1)
	}
	return
}

// localNode constructs a node
func localNode(confdir string, online bool) (*core.IpfsNode, error) {
	filename, err := config.Filename(confdir)
	if err != nil {
		return nil, err
	}

	cfg, err := config.Load(filename)
	if err != nil {
		return nil, err
	}

	if cfg.Version.ShouldCheckForUpdate() {
		obsolete := updates.CheckForUpdates()
		if obsolete != nil {
			if cfg.Version.Check == config.CheckError {
				return nil, obsolete
			}

			// when "warn" version.check mode we just show warning message
			log.Warning(fmt.Sprintf("%v", obsolete))
		} else {
			// update most recent check timestamp in config
			config.RecordUpdateCheck(cfg, filename)
		}
	}

	return core.NewIpfsNode(cfg, online)
}

// Gets the config "-c" flag from the command, or returns
// the default configuration root directory
func getConfigDir(c *commander.Command) (string, error) {

	// use the root cmd (that's where config is specified)
	for ; c.Parent != nil; c = c.Parent {
	}

	// flag should be defined on root.
	param := c.Flag.Lookup("c").Value.Get().(string)
	if param != "" {
		return u.TildeExpansion(param)
	}

	return config.PathRoot()
}

func getConfig(c *commander.Command) (*config.Config, error) {
	confdir, err := getConfigDir(c)
	if err != nil {
		return nil, err
	}

	filename, err := config.Filename(confdir)
	if err != nil {
		return nil, err
	}

	return config.Load(filename)
}

// cmdContext is a wrapper structure that keeps a node, a daemonlistener, and
// a config directory together. These three are needed for most commands.
type cmdContext struct {
	node      *core.IpfsNode
	daemon    *daemon.DaemonListener
	configDir string
}

// setupCmdContext initializes a cmdContext structure from a given command.
func setupCmdContext(c *commander.Command, online bool) (cc cmdContext, err error) {
	rootCmd := c
	for ; rootCmd.Parent != nil; rootCmd = rootCmd.Parent {
	}

	cc.configDir, err = getConfigDir(rootCmd)
	if err != nil {
		return
	}

	cc.node, err = localNode(cc.configDir, online)
	if err != nil {
		return
	}

	cc.daemon, err = setupDaemon(cc.configDir, cc.node)
	if err != nil {
		return
	}

	return
}

// setupDaemon sets up the daemon corresponding to given node.
func setupDaemon(confdir string, node *core.IpfsNode) (*daemon.DaemonListener, error) {
	if node.Config.Addresses.API == "" {
		return nil, errors.New("no config.Addresses.API endpoint supplied")
	}

	maddr, err := ma.NewMultiaddr(node.Config.Addresses.API)
	if err != nil {
		return nil, err
	}

	dl, err := daemon.NewDaemonListener(node, maddr, confdir)
	if err != nil {
		return nil, err
	}
	go dl.Listen()
	return dl, nil
}
<commit_msg>output these errors directly to the user<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"runtime/pprof"

	flag "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag"
	commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"

	config "github.com/jbenet/go-ipfs/config"
	core "github.com/jbenet/go-ipfs/core"
	daemon "github.com/jbenet/go-ipfs/daemon"
	updates "github.com/jbenet/go-ipfs/updates"
	u "github.com/jbenet/go-ipfs/util"
)

// The IPFS command tree. It is an instance of `commander.Command`.
var CmdIpfs = &commander.Command{
	UsageLine: "ipfs [<flags>] <command> [<args>]",
	Short:     "global versioned p2p merkledag file system",
	Long: `ipfs - global versioned p2p merkledag file system

Basic commands:

    init          Initialize ipfs local configuration.
    add <path>    Add an object to ipfs.
    cat <ref>     Show ipfs object data.
    ls <ref>      List links from an object.
    refs <ref>    List link hashes from an object.

Tool commands:

    config        Manage configuration.
    version       Show ipfs version information.
    commands      List all available commands.

Advanced Commands:

    mount         Mount an ipfs read-only mountpoint.
    serve         Serve an interface to ipfs.

	net-diag	  Print network diagnostic

Use "ipfs help <command>" for more information about a command.
`,
	Run: ipfsCmd,
	Subcommands: []*commander.Command{
		cmdIpfsAdd,
		cmdIpfsCat,
		cmdIpfsLs,
		cmdIpfsRefs,
		cmdIpfsConfig,
		cmdIpfsVersion,
		cmdIpfsCommands,
		cmdIpfsMount,
		cmdIpfsInit,
		cmdIpfsServe,
		cmdIpfsRun,
		cmdIpfsName,
		cmdIpfsBootstrap,
		cmdIpfsDiag,
		cmdIpfsBlock,
		cmdIpfsLog,
	},
	Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
}

// log is the command logger
var log = u.Logger("cmd/ipfs")

func init() {
	config, err := config.PathRoot()
	if err != nil {
		fmt.Fprintln(os.Stderr, "Failure initializing the default Config Directory: ", err)
		os.Exit(1)
	}
	CmdIpfs.Flag.String("c", config, "specify config directory")
}

func ipfsCmd(c *commander.Command, args []string) error {
	u.POut(c.Long)
	return nil
}

func main() {
	u.Debug = false

	// setup logging
	// u.SetupLogging() done in an init() block now.

	// if debugging, setup profiling.
	if u.Debug {
		ofi, err := os.Create("cpu.prof")
		if err != nil {
			fmt.Println(err)
			return
		}
		pprof.StartCPUProfile(ofi)
		defer ofi.Close()
		defer pprof.StopCPUProfile()
	}

	err := CmdIpfs.Dispatch(os.Args[1:])
	if err != nil {
		if len(err.Error()) > 0 {
			fmt.Fprintf(os.Stderr, "ipfs %s: %v\n", os.Args[1], err)
		}
		os.Exit(1)
	}
	return
}

// localNode constructs a node
func localNode(confdir string, online bool) (*core.IpfsNode, error) {
	filename, err := config.Filename(confdir)
	if err != nil {
		return nil, err
	}

	cfg, err := config.Load(filename)
	if err != nil {
		return nil, err
	}

	if cfg.Version.ShouldCheckForUpdate() {
		obsolete := updates.CheckForUpdates()
		if obsolete != nil {
			if cfg.Version.Check == config.CheckError {
				return nil, obsolete
			}

			// when "warn" version.check mode we just show warning message
			log.Warning(fmt.Sprintf("%v", obsolete))
		} else {
			// update most recent check timestamp in config
			config.RecordUpdateCheck(cfg, filename)
		}
	}

	return core.NewIpfsNode(cfg, online)
}

// Gets the config "-c" flag from the command, or returns
// the default configuration root directory
func getConfigDir(c *commander.Command) (string, error) {

	// use the root cmd (that's where config is specified)
	for ; c.Parent != nil; c = c.Parent {
	}

	// flag should be defined on root.
	param := c.Flag.Lookup("c").Value.Get().(string)
	if param != "" {
		return u.TildeExpansion(param)
	}

	return config.PathRoot()
}

func getConfig(c *commander.Command) (*config.Config, error) {
	confdir, err := getConfigDir(c)
	if err != nil {
		return nil, err
	}

	filename, err := config.Filename(confdir)
	if err != nil {
		return nil, err
	}

	return config.Load(filename)
}

// cmdContext is a wrapper structure that keeps a node, a daemonlistener, and
// a config directory together. These three are needed for most commands.
type cmdContext struct {
	node      *core.IpfsNode
	daemon    *daemon.DaemonListener
	configDir string
}

// setupCmdContext initializes a cmdContext structure from a given command.
func setupCmdContext(c *commander.Command, online bool) (cc cmdContext, err error) {
	rootCmd := c
	for ; rootCmd.Parent != nil; rootCmd = rootCmd.Parent {
	}

	cc.configDir, err = getConfigDir(rootCmd)
	if err != nil {
		return
	}

	cc.node, err = localNode(cc.configDir, online)
	if err != nil {
		return
	}

	cc.daemon, err = setupDaemon(cc.configDir, cc.node)
	if err != nil {
		return
	}

	return
}

// setupDaemon sets up the daemon corresponding to given node.
func setupDaemon(confdir string, node *core.IpfsNode) (*daemon.DaemonListener, error) {
	if node.Config.Addresses.API == "" {
		return nil, errors.New("no config.Addresses.API endpoint supplied")
	}

	maddr, err := ma.NewMultiaddr(node.Config.Addresses.API)
	if err != nil {
		return nil, err
	}

	dl, err := daemon.NewDaemonListener(node, maddr, confdir)
	if err != nil {
		return nil, err
	}
	go dl.Listen()
	return dl, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/cyberdelia/lzo"
	"io"
	"os"
)

var (
	uncompress = flag.Bool("decompress", false, "Decompress.")
	level      = flag.Int("level", 3, "Compression level.")
)

func decompress(path string) error {
	input, err := os.Open(path)
	if err != nil {
		return err
	}
	decompressor, err := lzo.NewReader(input)
	if err != nil {
		return err
	}
	output, err := os.Create(decompressor.Name)
	if err != nil {
		return err
	}
	_, err = io.Copy(output, decompressor)
	if err != nil {
		return err
	}
	return nil
}

func compress(level int, path string) error {
	if level > lzo.BestCompression {
		level = lzo.BestCompression
	} else if level < lzo.BestSpeed {
		level = lzo.BestSpeed
	} else {
		level = lzo.DefaultCompression
	}
	input, err := os.Open(path)
	if err != nil {
		return err
	}
	output, err := os.Create(path + ".lzo")
	if err != nil {
		return err
	}
	compressor, err := lzo.NewWriterLevel(output, level)
	defer compressor.Close()
	compressor.Name = input.Name()
	if err != nil {
		return err
	}
	_, err = io.Copy(compressor, input)
	if err != nil {
		return err
	}
	return nil
}

func main() {
	flag.Parse()
	path := flag.Arg(0)
	var err error
	if *uncompress == true {
		err = decompress(path)
	} else {
		err = compress(*level, path)
	}
	if err != nil {
		fmt.Println("lzop:", err)
	}
}
<commit_msg>order imports<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/cyberdelia/lzo"
)

var (
	uncompress = flag.Bool("decompress", false, "Decompress.")
	level      = flag.Int("level", 3, "Compression level.")
)

func decompress(path string) error {
	input, err := os.Open(path)
	if err != nil {
		return err
	}
	decompressor, err := lzo.NewReader(input)
	if err != nil {
		return err
	}
	output, err := os.Create(decompressor.Name)
	if err != nil {
		return err
	}
	_, err = io.Copy(output, decompressor)
	if err != nil {
		return err
	}
	return nil
}

func compress(level int, path string) error {
	if level > lzo.BestCompression {
		level = lzo.BestCompression
	} else if level < lzo.BestSpeed {
		level = lzo.BestSpeed
	} else {
		level = lzo.DefaultCompression
	}
	input, err := os.Open(path)
	if err != nil {
		return err
	}
	output, err := os.Create(path + ".lzo")
	if err != nil {
		return err
	}
	compressor, err := lzo.NewWriterLevel(output, level)
	defer compressor.Close()
	compressor.Name = input.Name()
	if err != nil {
		return err
	}
	_, err = io.Copy(compressor, input)
	if err != nil {
		return err
	}
	return nil
}

func main() {
	flag.Parse()
	path := flag.Arg(0)
	var err error
	if *uncompress == true {
		err = decompress(path)
	} else {
		err = compress(*level, path)
	}
	if err != nil {
		fmt.Println("lzop:", err)
	}
}
<|endoftext|>


<commit_before>// Package s3backupstorage implements the BackupStorage interface for AWS S3.
//
// AWS access credentials are configured via standard AWS means, such as:
// - AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables
// - credentials file at ~/.aws/credentials
// - if running on an EC2 instance, an IAM role
// See details at http://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs
package s3backupstorage

import (
	"flag"
	"fmt"
	"io"
	"sort"
	"strings"
	"sync"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/vt/concurrency"
	"github.com/youtube/vitess/go/vt/mysqlctl/backupstorage"
)

var (
	// AWS API region
	region = flag.String("s3_backup_aws_region", "us-east-1", "AWS region to use")

	// bucket is where the backups will go.
	bucket = flag.String("s3_backup_storage_bucket", "", "S3 bucket to use for backups")

	// root is a prefix added to all object names.
	root = flag.String("s3_backup_storage_root", "", "root prefix for all backup-related object names")

	// path component delimiter
	delimiter = "/"
)

// S3BackupHandle implements the backupstorage.BackupHandle interface.
type S3BackupHandle struct {
	client    *s3.S3
	bs        *S3BackupStorage
	dir       string
	name      string
	readOnly  bool
	errors    concurrency.AllErrorRecorder
	waitGroup sync.WaitGroup
}

// Directory is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) Directory() string {
	return bh.dir
}

// Name is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) Name() string {
	return bh.name
}

// AddFile is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) AddFile(ctx context.Context, filename string) (io.WriteCloser, error) {
	if bh.readOnly {
		return nil, fmt.Errorf("AddFile cannot be called on read-only backup")
	}

	reader, writer := io.Pipe()
	bh.waitGroup.Add(1)

	go func() {
		defer bh.waitGroup.Done()
		uploader := s3manager.NewUploaderWithClient(bh.client)
		object := objName(bh.dir, bh.name, filename)
		_, err := uploader.Upload(&s3manager.UploadInput{
			Bucket: bucket,
			Key:    object,
			Body:   reader,
		})
		if err != nil {
			reader.CloseWithError(err)
			bh.errors.RecordError(err)
		}
	}()

	return writer, nil
}

// EndBackup is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) EndBackup(ctx context.Context) error {
	if bh.readOnly {
		return fmt.Errorf("EndBackup cannot be called on read-only backup")
	}
	bh.waitGroup.Wait()
	return bh.errors.Error()
}

// AbortBackup is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) AbortBackup(ctx context.Context) error {
	if bh.readOnly {
		return fmt.Errorf("AbortBackup cannot be called on read-only backup")
	}
	return bh.bs.RemoveBackup(ctx, bh.dir, bh.name)
}

// ReadFile is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) ReadFile(ctx context.Context, filename string) (io.ReadCloser, error) {
	if !bh.readOnly {
		return nil, fmt.Errorf("ReadFile cannot be called on read-write backup")
	}
	object := objName(bh.dir, bh.name, filename)
	out, err := bh.client.GetObject(&s3.GetObjectInput{
		Bucket: bucket,
		Key:    object,
	})
	if err != nil {
		return nil, err
	}
	return out.Body, nil
}

var _ backupstorage.BackupHandle = (*S3BackupHandle)(nil)

// S3BackupStorage implements the backupstorage.BackupStorage interface.
type S3BackupStorage struct {
	_client *s3.S3
	mu      sync.Mutex
}

// ListBackups is part of the backupstorage.BackupStorage interface.
func (bs *S3BackupStorage) ListBackups(ctx context.Context, dir string) ([]backupstorage.BackupHandle, error) {
	c, err := bs.client()
	if err != nil {
		return nil, err
	}

	searchPrefix := objName(dir, "")
	query := &s3.ListObjectsV2Input{
		Bucket:    bucket,
		Delimiter: &delimiter,
		Prefix:    searchPrefix,
	}

	var subdirs []string
	for {
		objs, err := c.ListObjectsV2(query)
		if err != nil {
			return nil, err
		}
		for _, prefix := range objs.CommonPrefixes {
			subdir := strings.TrimPrefix(*prefix.Prefix, *searchPrefix)
			subdir = strings.TrimSuffix(subdir, delimiter)
			subdirs = append(subdirs, subdir)
		}

		if objs.NextContinuationToken == nil {
			break
		}
		query.ContinuationToken = objs.NextContinuationToken
	}

	// Backups must be returned in order, oldest first.
	sort.Strings(subdirs)

	result := make([]backupstorage.BackupHandle, 0, len(subdirs))
	for _, subdir := range subdirs {
		result = append(result, &S3BackupHandle{
			client:   c,
			bs:       bs,
			dir:      dir,
			name:     subdir,
			readOnly: true,
		})
	}
	return result, nil
}

// StartBackup is part of the backupstorage.BackupStorage interface.
func (bs *S3BackupStorage) StartBackup(ctx context.Context, dir, name string) (backupstorage.BackupHandle, error) {
	c, err := bs.client()
	if err != nil {
		return nil, err
	}

	return &S3BackupHandle{
		client:   c,
		bs:       bs,
		dir:      dir,
		name:     name,
		readOnly: false,
	}, nil
}

// RemoveBackup is part of the backupstorage.BackupStorage interface.
func (bs *S3BackupStorage) RemoveBackup(ctx context.Context, dir, name string) error {
	c, err := bs.client()
	if err != nil {
		return err
	}

	query := &s3.ListObjectsV2Input{
		Bucket: bucket,
		Prefix: objName(dir, ""),
	}

	for {
		objs, err := c.ListObjectsV2(query)
		if err != nil {
			return err
		}

		objIds := make([]*s3.ObjectIdentifier, 0, len(objs.Contents))
		for _, obj := range objs.Contents {
			objIds = append(objIds, &s3.ObjectIdentifier{
				Key: obj.Key,
			})
		}

		quiet := true // return less in the Delete response
		out, err := c.DeleteObjects(&s3.DeleteObjectsInput{
			Bucket: bucket,
			Delete: &s3.Delete{
				Objects: objIds,
				Quiet:   &quiet,
			},
		})

		if err != nil {
			return err
		}

		for _, objError := range out.Errors {
			return fmt.Errorf(objError.String())
		}

		if objs.NextContinuationToken == nil {
			break
		}

		query.ContinuationToken = objs.NextContinuationToken
	}

	return nil
}

// Close is part of the backupstorage.BackupStorage interface.
func (bs *S3BackupStorage) Close() error {
	bs.mu.Lock()
	defer bs.mu.Unlock()
	bs._client = nil
	return nil
}

var _ backupstorage.BackupStorage = (*S3BackupStorage)(nil)

func (bs *S3BackupStorage) client() (*s3.S3, error) {
	bs.mu.Lock()
	defer bs.mu.Unlock()
	if bs._client == nil {
		bs._client = s3.New(session.New(), &aws.Config{Region: aws.String(*region)})

		if len(*bucket) == 0 {
			return nil, fmt.Errorf("-s3_backup_storage_bucket required")
		}

		if _, err := bs._client.HeadBucket(&s3.HeadBucketInput{Bucket: bucket}); err != nil {
			return nil, err
		}
	}
	return bs._client, nil
}

func objName(parts ...string) *string {
	res := ""
	if *root != "" {
		res += *root + delimiter
	}
	res += strings.Join(parts, delimiter)
	return &res
}

func init() {
	backupstorage.BackupStorageMap["s3"] = &S3BackupStorage{}
}
<commit_msg>only delete the named backup<commit_after>// Package s3backupstorage implements the BackupStorage interface for AWS S3.
//
// AWS access credentials are configured via standard AWS means, such as:
// - AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables
// - credentials file at ~/.aws/credentials
// - if running on an EC2 instance, an IAM role
// See details at http://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs
package s3backupstorage

import (
	"flag"
	"fmt"
	"io"
	"sort"
	"strings"
	"sync"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/vt/concurrency"
	"github.com/youtube/vitess/go/vt/mysqlctl/backupstorage"
)

var (
	// AWS API region
	region = flag.String("s3_backup_aws_region", "us-east-1", "AWS region to use")

	// bucket is where the backups will go.
	bucket = flag.String("s3_backup_storage_bucket", "", "S3 bucket to use for backups")

	// root is a prefix added to all object names.
	root = flag.String("s3_backup_storage_root", "", "root prefix for all backup-related object names")

	// path component delimiter
	delimiter = "/"
)

// S3BackupHandle implements the backupstorage.BackupHandle interface.
type S3BackupHandle struct {
	client    *s3.S3
	bs        *S3BackupStorage
	dir       string
	name      string
	readOnly  bool
	errors    concurrency.AllErrorRecorder
	waitGroup sync.WaitGroup
}

// Directory is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) Directory() string {
	return bh.dir
}

// Name is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) Name() string {
	return bh.name
}

// AddFile is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) AddFile(ctx context.Context, filename string) (io.WriteCloser, error) {
	if bh.readOnly {
		return nil, fmt.Errorf("AddFile cannot be called on read-only backup")
	}

	reader, writer := io.Pipe()
	bh.waitGroup.Add(1)

	go func() {
		defer bh.waitGroup.Done()
		uploader := s3manager.NewUploaderWithClient(bh.client)
		object := objName(bh.dir, bh.name, filename)
		_, err := uploader.Upload(&s3manager.UploadInput{
			Bucket: bucket,
			Key:    object,
			Body:   reader,
		})
		if err != nil {
			reader.CloseWithError(err)
			bh.errors.RecordError(err)
		}
	}()

	return writer, nil
}

// EndBackup is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) EndBackup(ctx context.Context) error {
	if bh.readOnly {
		return fmt.Errorf("EndBackup cannot be called on read-only backup")
	}
	bh.waitGroup.Wait()
	return bh.errors.Error()
}

// AbortBackup is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) AbortBackup(ctx context.Context) error {
	if bh.readOnly {
		return fmt.Errorf("AbortBackup cannot be called on read-only backup")
	}
	return bh.bs.RemoveBackup(ctx, bh.dir, bh.name)
}

// ReadFile is part of the backupstorage.BackupHandle interface.
func (bh *S3BackupHandle) ReadFile(ctx context.Context, filename string) (io.ReadCloser, error) {
	if !bh.readOnly {
		return nil, fmt.Errorf("ReadFile cannot be called on read-write backup")
	}
	object := objName(bh.dir, bh.name, filename)
	out, err := bh.client.GetObject(&s3.GetObjectInput{
		Bucket: bucket,
		Key:    object,
	})
	if err != nil {
		return nil, err
	}
	return out.Body, nil
}

var _ backupstorage.BackupHandle = (*S3BackupHandle)(nil)

// S3BackupStorage implements the backupstorage.BackupStorage interface.
type S3BackupStorage struct {
	_client *s3.S3
	mu      sync.Mutex
}

// ListBackups is part of the backupstorage.BackupStorage interface.
func (bs *S3BackupStorage) ListBackups(ctx context.Context, dir string) ([]backupstorage.BackupHandle, error) {
	c, err := bs.client()
	if err != nil {
		return nil, err
	}

	searchPrefix := objName(dir, "")
	query := &s3.ListObjectsV2Input{
		Bucket:    bucket,
		Delimiter: &delimiter,
		Prefix:    searchPrefix,
	}

	var subdirs []string
	for {
		objs, err := c.ListObjectsV2(query)
		if err != nil {
			return nil, err
		}
		for _, prefix := range objs.CommonPrefixes {
			subdir := strings.TrimPrefix(*prefix.Prefix, *searchPrefix)
			subdir = strings.TrimSuffix(subdir, delimiter)
			subdirs = append(subdirs, subdir)
		}

		if objs.NextContinuationToken == nil {
			break
		}
		query.ContinuationToken = objs.NextContinuationToken
	}

	// Backups must be returned in order, oldest first.
	sort.Strings(subdirs)

	result := make([]backupstorage.BackupHandle, 0, len(subdirs))
	for _, subdir := range subdirs {
		result = append(result, &S3BackupHandle{
			client:   c,
			bs:       bs,
			dir:      dir,
			name:     subdir,
			readOnly: true,
		})
	}
	return result, nil
}

// StartBackup is part of the backupstorage.BackupStorage interface.
func (bs *S3BackupStorage) StartBackup(ctx context.Context, dir, name string) (backupstorage.BackupHandle, error) {
	c, err := bs.client()
	if err != nil {
		return nil, err
	}

	return &S3BackupHandle{
		client:   c,
		bs:       bs,
		dir:      dir,
		name:     name,
		readOnly: false,
	}, nil
}

// RemoveBackup is part of the backupstorage.BackupStorage interface.
func (bs *S3BackupStorage) RemoveBackup(ctx context.Context, dir, name string) error {
	c, err := bs.client()
	if err != nil {
		return err
	}

	query := &s3.ListObjectsV2Input{
		Bucket: bucket,
		Prefix: objName(dir, name),
	}

	for {
		objs, err := c.ListObjectsV2(query)
		if err != nil {
			return err
		}

		objIds := make([]*s3.ObjectIdentifier, 0, len(objs.Contents))
		for _, obj := range objs.Contents {
			objIds = append(objIds, &s3.ObjectIdentifier{
				Key: obj.Key,
			})
		}

		quiet := true // return less in the Delete response
		out, err := c.DeleteObjects(&s3.DeleteObjectsInput{
			Bucket: bucket,
			Delete: &s3.Delete{
				Objects: objIds,
				Quiet:   &quiet,
			},
		})

		if err != nil {
			return err
		}

		for _, objError := range out.Errors {
			return fmt.Errorf(objError.String())
		}

		if objs.NextContinuationToken == nil {
			break
		}

		query.ContinuationToken = objs.NextContinuationToken
	}

	return nil
}

// Close is part of the backupstorage.BackupStorage interface.
func (bs *S3BackupStorage) Close() error {
	bs.mu.Lock()
	defer bs.mu.Unlock()
	bs._client = nil
	return nil
}

var _ backupstorage.BackupStorage = (*S3BackupStorage)(nil)

func (bs *S3BackupStorage) client() (*s3.S3, error) {
	bs.mu.Lock()
	defer bs.mu.Unlock()
	if bs._client == nil {
		bs._client = s3.New(session.New(), &aws.Config{Region: aws.String(*region)})

		if len(*bucket) == 0 {
			return nil, fmt.Errorf("-s3_backup_storage_bucket required")
		}

		if _, err := bs._client.HeadBucket(&s3.HeadBucketInput{Bucket: bucket}); err != nil {
			return nil, err
		}
	}
	return bs._client, nil
}

func objName(parts ...string) *string {
	res := ""
	if *root != "" {
		res += *root + delimiter
	}
	res += strings.Join(parts, delimiter)
	return &res
}

func init() {
	backupstorage.BackupStorageMap["s3"] = &S3BackupStorage{}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/naoina/migu"
)

var (
	dryRunMarker = "dry-run "
)

type sync struct {
	GeneralOption

	DryRun bool `long:"dry-run"`
	Quiet  bool `short:"q" long:"quiet"`
}

func (s *sync) Usage() string {
	return fmt.Sprintf(`Usage: %s sync [OPTIONS] DATABASE [FILE]

Options:
      --dry-run          Print the results with no changes
  -q, --quiet            Suppress non-error messages
%s
With no FILE, or when FILE is -, read standard input.
`, progName, s.GeneralOption.Usage())
}

func (s *sync) Execute(args []string) error {
	var dbname string
	var path string
	switch len(args) {
	case 0:
		return &usageError{
			err: fmt.Errorf("too few arguments"),
		}
	case 1:
		dbname = args[0]
	case 2:
		dbname, path = args[0], args[1]
	default:
		return &usageError{
			err: fmt.Errorf("too many arguments"),
		}
	}
	db, err := database(s.Host, s.User, s.Password, dbname)
	if err != nil {
		return err
	}
	defer db.Close()
	if !s.DryRun {
		dryRunMarker = ""
	}
	fInfo, err := os.Stat(path)
	if err != nil {
		return err
	}
	filenames := []string{}
	if fInfo.IsDir() {
		if filenames, err = walk(path); err != nil {
			return err
		}
	} else {
		filenames = append(filenames, path)
	}
	return s.run(db, filenames)
}

func walk(root string) ([]string, error) {
	filenames := []string{}
	if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		filenames = append(filenames, path)
		return nil
	}); err != nil {
		return nil, err
	}
	return filenames, nil
}

func (s *sync) run(db *sql.DB, filenames []string) error {
	var src io.Reader
	switch len(filenames) {
	case 1:
		switch filenames[0] {
		case "", "-":
			filenames = []string{""}
			src = os.Stdin
		}
	}
	sqls, err := migu.Diff(db, filenames, src)
	if err != nil {
		return err
	}
	var tx *sql.Tx
	if !s.DryRun {
		if tx, err = db.Begin(); err != nil {
			return err
		}
	}
	for _, sql := range sqls {
		s.printf("--------%sapplying--------\n", dryRunMarker)
		s.printf("  %s\n", strings.Replace(sql, "\n", "\n  ", -1))
		start := time.Now()
		if !s.DryRun {
			if _, err := tx.Exec(sql); err != nil {
				tx.Rollback()
				return err
			}
		}
		d := time.Since(start)
		s.printf("--------%sdone %.3fs--------\n", dryRunMarker, d.Seconds()/time.Second.Seconds())
	}
	if s.DryRun {
		return nil
	} else {
		return tx.Commit()
	}
}

func (s *sync) printf(format string, a ...interface{}) (int, error) {
	if s.Quiet {
		return 0, nil
	}
	return fmt.Printf(format, a...)
}
<commit_msg>Change import package<commit_after>package main

import (
	"database/sql"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/hatajoe/migu"
)

var (
	dryRunMarker = "dry-run "
)

type sync struct {
	GeneralOption

	DryRun bool `long:"dry-run"`
	Quiet  bool `short:"q" long:"quiet"`
}

func (s *sync) Usage() string {
	return fmt.Sprintf(`Usage: %s sync [OPTIONS] DATABASE [FILE]

Options:
      --dry-run          Print the results with no changes
  -q, --quiet            Suppress non-error messages
%s
With no FILE, or when FILE is -, read standard input.
`, progName, s.GeneralOption.Usage())
}

func (s *sync) Execute(args []string) error {
	var dbname string
	var path string
	switch len(args) {
	case 0:
		return &usageError{
			err: fmt.Errorf("too few arguments"),
		}
	case 1:
		dbname = args[0]
	case 2:
		dbname, path = args[0], args[1]
	default:
		return &usageError{
			err: fmt.Errorf("too many arguments"),
		}
	}
	db, err := database(s.Host, s.User, s.Password, dbname)
	if err != nil {
		return err
	}
	defer db.Close()
	if !s.DryRun {
		dryRunMarker = ""
	}
	fInfo, err := os.Stat(path)
	if err != nil {
		return err
	}
	filenames := []string{}
	if fInfo.IsDir() {
		if filenames, err = walk(path); err != nil {
			return err
		}
	} else {
		filenames = append(filenames, path)
	}
	return s.run(db, filenames)
}

func walk(root string) ([]string, error) {
	filenames := []string{}
	if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			return nil
		}
		filenames = append(filenames, path)
		return nil
	}); err != nil {
		return nil, err
	}
	return filenames, nil
}

func (s *sync) run(db *sql.DB, filenames []string) error {
	var src io.Reader
	switch len(filenames) {
	case 1:
		switch filenames[0] {
		case "", "-":
			filenames = []string{""}
			src = os.Stdin
		}
	}
	sqls, err := migu.Diff(db, filenames, src)
	if err != nil {
		return err
	}
	var tx *sql.Tx
	if !s.DryRun {
		if tx, err = db.Begin(); err != nil {
			return err
		}
	}
	for _, sql := range sqls {
		s.printf("--------%sapplying--------\n", dryRunMarker)
		s.printf("  %s\n", strings.Replace(sql, "\n", "\n  ", -1))
		start := time.Now()
		if !s.DryRun {
			if _, err := tx.Exec(sql); err != nil {
				tx.Rollback()
				return err
			}
		}
		d := time.Since(start)
		s.printf("--------%sdone %.3fs--------\n", dryRunMarker, d.Seconds()/time.Second.Seconds())
	}
	if s.DryRun {
		return nil
	} else {
		return tx.Commit()
	}
}

func (s *sync) printf(format string, a ...interface{}) (int, error) {
	if s.Quiet {
		return 0, nil
	}
	return fmt.Printf(format, a...)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"syscall"

	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/peco"
	"github.com/nsf/termbox-go"
)

var version = "v0.1.2"

func showHelp() {
	const v = `
Usage: peco [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --version             print the version and exit
  --rcfile=RCFILE       path to the settings file
  --query=QUERY         pre-input query
  --no-ignore-case      start in case-sensitive mode
`
	os.Stderr.Write([]byte(v))
}

type cmdOptions struct {
	Help         bool   `short:"h" long:"help" description:"show this help message and exit"`
	TTY          string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	Query        string `long:"query"`
	Rcfile       string `long:"rcfile" descriotion:"path to the settings file"`
	NoIgnoreCase bool   `long:"no-ignore-case" description:"start in case-sensitive-mode" default:"false"`
	Version      bool   `long:"version" description:"print the version and exit"`
}

func main() {
	var err error
	var st int

	defer func() { os.Exit(st) }()

	opts := &cmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse()
	if err != nil {
		showHelp()
		st = 1
		return
	}

	if opts.Help {
		showHelp()
		return
	}

	if opts.Version {
		fmt.Fprintf(os.Stderr, "peco: %s\n", version)
		return
	}

	var in *os.File

	// receive in from either a file or Stdin
	if len(args) > 0 {
		in, err = os.Open(args[0])
		if err != nil {
			st = 1
			return
		}
	} else if !peco.IsTty() {
		in = os.Stdin
	}

	ctx := peco.NewCtx()
	defer func() {
		if err := recover(); err != nil {
			st = 1
			fmt.Fprintf(os.Stderr, "Error:\n%s", err)
		}

		if result := ctx.Result(); result != nil {
			for _, line := range result {
				if line[len(line)-1] != '\n' {
					line = line + "\n"
				}
				fmt.Fprint(os.Stdout, line)
			}
		}
	}()

	if opts.Rcfile == "" {
		user, err := user.Current()
		if err == nil { // silently ignore failure for user.Current()
			file := filepath.Join(user.HomeDir, ".peco", "config.json")
			_, err := os.Stat(file)
			if err == nil {
				opts.Rcfile = file
			}
		}
	}

	// Default matcher is IgnoreCase
	ctx.SetCurrentMatcher(peco.IgnoreCaseMatch)

	if opts.Rcfile != "" {
		err = ctx.ReadConfig(opts.Rcfile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			st = 1
			return
		}
	}

	if opts.NoIgnoreCase {
		ctx.SetCurrentMatcher(peco.CaseSensitiveMatch)
	}

	if err = ctx.ReadBuffer(in); err != nil {
		// Nothing to process, bail out
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		st = 1
		return
	}

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer peco.TtyTerm()

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer termbox.Close()

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()

	// AddWaitGroup must be called in this main thread
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

	ctx.AddWaitGroup(4)
	go view.Loop()
	go filter.Loop()
	go input.Loop()
	go ctx.SignalHandlerLoop(sigCh)

	if len(opts.Query) > 0 {
		ctx.SetQuery([]rune(opts.Query))
		ctx.ExecQuery(opts.Query)
	} else {
		view.Refresh()
	}

	ctx.WaitDone()

	st = ctx.ExitStatus
}
<commit_msg>Respect XDG base directory structure.<commit_after>package main

import (
	"fmt"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"strings"
	"syscall"

	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/peco"
	"github.com/nsf/termbox-go"
)

var version = "v0.1.2"

func showHelp() {
	const v = `
Usage: peco [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --version             print the version and exit
  --rcfile=RCFILE       path to the settings file
  --query=QUERY         pre-input query
  --no-ignore-case      start in case-sensitive mode
`
	os.Stderr.Write([]byte(v))
}

type cmdOptions struct {
	Help         bool   `short:"h" long:"help" description:"show this help message and exit"`
	TTY          string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	Query        string `long:"query"`
	Rcfile       string `long:"rcfile" descriotion:"path to the settings file"`
	NoIgnoreCase bool   `long:"no-ignore-case" description:"start in case-sensitive-mode" default:"false"`
	Version      bool   `long:"version" description:"print the version and exit"`
}

func locateRcfileIn(dir string) (string, error) {
	const basename = "config.json"
	file := filepath.Join(dir, basename)
	if _, err := os.Stat(file); err != nil {
		return "", err
	}
	return file, nil
}

func locateRcfile() (string, error) {
	// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
	//
	// Try in this order:
	//	  $XDG_CONFIG_HOME/peco/config.json
	//    $XDG_CONFIG_DIR/peco/config.json (where XDG_CONFIG_DIR is listed in $XDG_CONFIG_DIRS)
	//	  ~/.peco/config.json

	// Try dir supplied via env var
	if dir := os.Getenv("XDG_CONFIG_HOME"); dir != "" {
		file, err := locateRcfileIn(filepath.Join(dir, "peco"))
		if err == nil {
			return file, nil
		}
	}

	// Try "default" XDG location, is user is available
	user, uErr := user.Current()
	if uErr == nil { // silently ignore failure for user.Current()
		file, err := locateRcfileIn(filepath.Join(user.HomeDir, ".config", "peco"))
		if err == nil {
			return file, nil
		}
	}

	// this standard does not take into consideration windows (duh)
	// while the spec says use ":" as the separator, Go provides us
	// with filepath.ListSeparator, so use it
	if dirs := os.Getenv("XDG_CONFIG_DIRS"); dirs != "" {
		for _, dir := range strings.Split(dirs, fmt.Sprintf("%c", filepath.ListSeparator)) {
			file, err := locateRcfileIn(dir)
			if err == nil {
				return file, nil
			}
		}
	}

	if uErr == nil { // silently ignore failure for user.Current()
		file, err := locateRcfileIn(filepath.Join(user.HomeDir, ".peco"))
		if err == nil {
			return file, nil
		}
	}

	return "", fmt.Errorf("Config file not found")
}

func main() {
	var err error
	var st int

	defer func() { os.Exit(st) }()

	opts := &cmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse()
	if err != nil {
		showHelp()
		st = 1
		return
	}

	if opts.Help {
		showHelp()
		return
	}

	if opts.Version {
		fmt.Fprintf(os.Stderr, "peco: %s\n", version)
		return
	}

	var in *os.File

	// receive in from either a file or Stdin
	if len(args) > 0 {
		in, err = os.Open(args[0])
		if err != nil {
			st = 1
			return
		}
	} else if !peco.IsTty() {
		in = os.Stdin
	}

	ctx := peco.NewCtx()
	defer func() {
		if err := recover(); err != nil {
			st = 1
			fmt.Fprintf(os.Stderr, "Error:\n%s", err)
		}

		if result := ctx.Result(); result != nil {
			for _, line := range result {
				if line[len(line)-1] != '\n' {
					line = line + "\n"
				}
				fmt.Fprint(os.Stdout, line)
			}
		}
	}()

	if opts.Rcfile == "" {
		file, err := locateRcfile()
		if err == nil {
			opts.Rcfile = file
		}
	}

	// Default matcher is IgnoreCase
	ctx.SetCurrentMatcher(peco.IgnoreCaseMatch)

	if opts.Rcfile != "" {
		err = ctx.ReadConfig(opts.Rcfile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			st = 1
			return
		}
	}

	if opts.NoIgnoreCase {
		ctx.SetCurrentMatcher(peco.CaseSensitiveMatch)
	}

	if err = ctx.ReadBuffer(in); err != nil {
		// Nothing to process, bail out
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		st = 1
		return
	}

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer peco.TtyTerm()

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer termbox.Close()

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()

	// AddWaitGroup must be called in this main thread
	sigCh := make(chan os.Signal, 1)
	signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

	ctx.AddWaitGroup(4)
	go view.Loop()
	go filter.Loop()
	go input.Loop()
	go ctx.SignalHandlerLoop(sigCh)

	if len(opts.Query) > 0 {
		ctx.SetQuery([]rune(opts.Query))
		ctx.ExecQuery(opts.Query)
	} else {
		view.Refresh()
	}

	ctx.WaitDone()

	st = ctx.ExitStatus
}
<|endoftext|>