<commit_before>// Package frame implements access to FLAC audio frames.
package frame

import "io"

// A Frame contains the header and subframes of an audio frame. It holds the
// encoded samples from a block (a part) of the audio stream. Each subframe
// holding the samples from one of its channel.
//
// ref: https://www.xiph.org/flac/format.html#frame
type Frame struct {
	// Audio frame header.
	Header
	// One subframe per channel, containing encoded audio samples.
	Subframes []*Subframe
	// Underlying io.Reader.
	r io.Reader
}

// New creates a new Frame for accessing the audio samples of r. It reads and
// parses an audio frame header. Call Frame.Parse to parse the audio samples of
// its subframes, and Frame.Next to gain access to the next audio frame.
func New(r io.Reader) (frame *Frame, err error) {
	frame = &Frame{r: r}
	err = frame.parseHeader()
	if err != nil {
		return nil, err
	}
	panic("not yet implemented.")
}

// Next returns access to the next audio frame. It reads and parses an audio
// frame header. Call Frame.Parse to parse the audio samples of its subframes.
func (frame *Frame) Next() (next *Frame, err error) {
	panic("not yet implemented.")
}

// Parse reads and parses the audio samples from each subframe of the frame. If
// the samples are interchannel correlated between the subframes, it
// decorrelates them.
//
// ref: https://www.xiph.org/flac/format.html#interchannel
func (frame *Frame) Parse() error {
	panic("not yet implemented.")
}

// A Header contains the basic properties of an audio frame, such as its sample
// rate and channel count. To facilitate random access decoding each frame
// header starts with a sync-code. This allows the decoder to synchronize and
// locate the start of a frame header.
//
// ref: https://www.xiph.org/flac/format.html#frame_header
type Header struct {
	// Specifies if the block size is fixed or variable.
	HasFixedBlockSize bool
	// Block size in inter-channel samples, i.e. the number of audio samples in
	// each subframe.
	BlockSize uint16
	// Sample rate in Hz; a 0 value implies unknown, get sample rate from
	// StreamInfo.
	SampleRate uint32
	// Specifies the number of channels (subframes) that exist in the frame,
	// their order and possible interchannel correlation.
	Channels Channels
	// Sample size in bits-per-sample; a 0 value implies unknown, get sample size
	// from StreamInfo.
	BitsPerSample uint8
	// Specifies the frame number if the block size is fixed, and the first
	// sample number in the frame otherwise. When using fixed block size, the
	// first sample number in the frame can be derived by multiplying the frame
	// number with the block size (in samples).
	Num uint64
}

// parseHeader reads and parses the header of an audio frame.
func (frame *Frame) parseHeader() error {
	panic("not yet implemented.")
}

// Channels specifies the number of channels (subframes) that exist in a frame,
// their order and possible interchannel correlation.
type Channels uint8

// Channel assignments. The following abbreviations are used:
//    C:   center (directly in front)
//    R:   right (standard stereo)
//    Sr:  side right (directly to the right)
//    Rs:  right surround (back right)
//    Cs:  center surround (rear center)
//    Ls:  left surround (back left)
//    Sl:  side left (directly to the left)
//    L:   left (standard stereo)
//    Lfe: low-frequency effect (placed according to room acoustics)
//
// The first 6 channel constants follow the SMPTE/ITU-R channel order:
//    L R C Lfe Ls Rs
const (
	ChannelsMono           Channels = iota // 1 channel: mono
	ChannelsLR                             // 2 channels: left, right
	ChannelsLRC                            // 3 channels: left, right, center
	ChannelsLRLsRs                         // 4 channels: left, right, left surround, right surround
	ChannelsLRCLsRs                        // 5 channels: left, right, center, left surround, right surround
	ChannelsLRCLfeLsRs                     // 6 channels: left, right, center, LFE, left surround, right surround
	ChannelsLRCLfeCsSlSr                   // 7 channels: left, right, center, LFE, center surround, side left, side right
	ChannelsLRCLfeLsRsSlSr                 // 8 channels: left, right, center, LFE, left surround, right surround, side left, side right
	ChannelsLeftSide                       // 2 channels: left, side; using interchannel correlation
	ChannelsSideRight                      // 2 channels: side, right; using interchannel correlation
	ChannelsMidSide                        // 2 channels: mid, side; using interchannel correlation
)

// nChannels specifies the number of channels used by each channel assignment.
var nChannels = [...]int{
	ChannelsMono:           1,
	ChannelsLR:             2,
	ChannelsLRC:            3,
	ChannelsLRLsRs:         4,
	ChannelsLRCLsRs:        5,
	ChannelsLRCLfeLsRs:     6,
	ChannelsLRCLfeCsSlSr:   7,
	ChannelsLRCLfeLsRsSlSr: 8,
	ChannelsLeftSide:       2,
	ChannelsSideRight:      2,
	ChannelsMidSide:        2,
}

// Count returns the number of channels (subframes) used by the provided channel
// assignment.
func (channels Channels) Count() int {
	return nChannels[channels]
}
<commit_msg>frame: Implement frame header decoding. This is the first piece of the new frame API. Updates issue #4.<commit_after>// Package frame implements access to FLAC audio frames.
package frame

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"

	"github.com/mewkiz/pkg/bit"
	"github.com/mewkiz/pkg/hashutil/crc8"
)

// A Frame contains the header and subframes of an audio frame. It holds the
// encoded samples from a block (a part) of the audio stream. Each subframe
// holding the samples from one of its channel.
//
// ref: https://www.xiph.org/flac/format.html#frame
type Frame struct {
	// Audio frame header.
	Header
	// One subframe per channel, containing encoded audio samples.
	Subframes []*Subframe
	// Underlying io.Reader.
	r io.Reader
}

// New creates a new Frame for accessing the audio samples of r. It reads and
// parses an audio frame header. Call Frame.Parse to parse the audio samples of
// its subframes, and Frame.Next to gain access to the next audio frame.
func New(r io.Reader) (frame *Frame, err error) {
	frame = &Frame{r: r}
	err = frame.parseHeader()
	if err != nil {
		return nil, err
	}
	panic("not yet implemented.")
}

// Next returns access to the next audio frame. It reads and parses an audio
// frame header. Call Frame.Parse to parse the audio samples of its subframes.
func (frame *Frame) Next() (next *Frame, err error) {
	panic("not yet implemented.")
}

// Parse reads and parses the audio samples from each subframe of the frame. If
// the samples are interchannel correlated between the subframes, it
// decorrelates them.
//
// ref: https://www.xiph.org/flac/format.html#interchannel
func (frame *Frame) Parse() error {
	panic("not yet implemented.")
}

// A Header contains the basic properties of an audio frame, such as its sample
// rate and channel count. To facilitate random access decoding each frame
// header starts with a sync-code. This allows the decoder to synchronize and
// locate the start of a frame header.
//
// ref: https://www.xiph.org/flac/format.html#frame_header
type Header struct {
	// Specifies if the block size is fixed or variable.
	HasFixedBlockSize bool
	// Block size in inter-channel samples, i.e. the number of audio samples in
	// each subframe.
	BlockSize uint16
	// Sample rate in Hz; a 0 value implies unknown, get sample rate from
	// StreamInfo.
	SampleRate uint32
	// Specifies the number of channels (subframes) that exist in the frame,
	// their order and possible interchannel correlation.
	Channels Channels
	// Sample size in bits-per-sample; a 0 value implies unknown, get sample size
	// from StreamInfo.
	BitsPerSample uint8
	// Specifies the frame number if the block size is fixed, and the first
	// sample number in the frame otherwise. When using fixed block size, the
	// first sample number in the frame can be derived by multiplying the frame
	// number with the block size (in samples).
	Num uint64
}

// Errors returned by Frame.parseHeader.
var (
	ErrInvalidSync = errors.New("frame.Frame.parseHeader: invalid sync-code")
)

// parseHeader reads and parses the header of an audio frame.
func (frame *Frame) parseHeader() error {
	// Create a new hash reader which adds the data from all read operations to a
	// running hash.
	h := crc8.NewATM()
	hr := io.TeeReader(frame.r, h)

	// 14 bits: sync-code (11111111111110)
	br := bit.NewReader(hr)
	x, err := br.Read(14)
	if err != nil {
		return err
	}
	if x != 0x3FFE {
		return ErrInvalidSync
	}

	// 1 bit: reserved.
	x, err = br.Read(1)
	if err != nil {
		return err
	}
	if x != 0 {
		return errors.New("frame.Frame.parseHeader: non-zero reserved value")
	}

	// 1 bit: HasFixedBlockSize.
	x, err = br.Read(1)
	if err != nil {
		return err
	}
	if x != 0 {
		frame.HasFixedBlockSize = true
	}

	// 4 bits: BlockSize. The block size parsing is simplified by deferring it to
	// the end of the header.
	blockSize, err := br.Read(4)
	if err != nil {
		return err
	}

	// 4 bits: SampleRate. The sample rate parsing is simplified by deferring it
	// to the end of the header.
	sampleRate, err := br.Read(4)
	if err != nil {
		return err
	}

	// 4 bits: Channels.
	//
	// The 4 bits are used to specify the channels as follows:
	//    0000: (1 channel) mono
	//    0001: (2 channels) left, right
	//    0010: (3 channels) left, right, center
	//    0011: (4 channels) left, right, left surround, right surround
	//    0100: (5 channels) left, right, center, left surround, right surround
	//    0101: (6 channels) left, right, center, LFE, left surround, right surround
	//    0110: (7 channels) left, right, center, LFE, center surround, side left, side right
	//    0111: (8 channels) left, right, center, LFE, left surround, right surround, side left, side right
	//    1000: (2 channels) left, side; using interchannel correlation
	//    1001: (2 channels) side, right; using interchannel correlation
	//    1010: (2 channels) mid, side; using interchannel correlation
	//    1011: reserved.
	//    1100: reserved.
	//    1101: reserved.
	//    1111: reserved.
	x, err = br.Read(4)
	if err != nil {
		return err
	}
	if x >= 0xB {
		return fmt.Errorf("frame.Frame.parseHeader: reserved channels bit pattern (%04b)", x)
	}
	frame.Channels = Channels(x)

	// 3 bits: BitsPerSample.
	x, err = br.Read(3)
	if err != nil {
		return err
	}
	// The 3 bits are used to specify the sample size as follows:
	//    000: unknown sample size; get from StreamInfo.
	//    001: 8 bits-per-sample.
	//    010: 12 bits-per-sample.
	//    011: reserved.
	//    100: 16 bits-per-sample.
	//    101: 20 bits-per-sample.
	//    110: 24 bits-per-sample.
	//    111: reserved.
	switch x {
	case 0x0:
		// 000: unknown bits-per-sample; get from StreamInfo.
	case 0x1:
		// 001: 8 bits-per-sample.
		frame.BitsPerSample = 8
	case 0x2:
		// 010: 12 bits-per-sample.
		frame.BitsPerSample = 12
	case 0x4:
		// 100: 16 bits-per-sample.
		frame.BitsPerSample = 16
	case 0x5:
		// 101: 20 bits-per-sample.
		frame.BitsPerSample = 20
	case 0x6:
		// 110: 24 bits-per-sample.
		frame.BitsPerSample = 24
	default:
		// 011: reserved.
		// 111: reserved.
		return fmt.Errorf("frame.Frame.parseHeader: reserved sample size bit pattern (%03b)", x)
	}

	// 1 bit: reserved.
	x, err = br.Read(1)
	if err != nil {
		return err
	}
	if x != 0 {
		return errors.New("frame.Frame.parseHeader: non-zero reserved value")
	}

	// if (fixed block size)
	//    1-6 bytes: UTF-8 encoded frame number.
	// else
	//    1-7 bytes: UTF-8 encoded sample number.
	frame.Num, err = decodeUTF8Int(hr)
	if err != nil {
		return err
	}

	// Parse block size.
	//
	// The 4 bits of n are used to specify the block size as follows:
	//    0000: reserved.
	//    0001: 192 samples.
	//    0010-0101: 576 * 2^(n-2) samples.
	//    0110: get 8 bit (block size)-1 from the end of the header.
	//    0111: get 16 bit (block size)-1 from the end of the header.
	//    1000-1111: 256 * 2^(n-8) samples.
	n := blockSize
	switch {
	case n == 0x0:
		// 0000: reserved.
		return errors.New("frame.Frame.parseHeader: reserved block size bit pattern (0000)")
	case n == 0x1:
		// 0001: 192 samples.
		frame.BlockSize = 192
	case n >= 0x2 && n <= 0x5:
		// 0010-0101: 576 * 2^(n-2) samples.
		frame.BlockSize = 576 * (1 << (n - 2))
	case n == 0x6:
		// 0110: get 8 bit (block size)-1 from the end of the header.
		x, err = br.Read(8)
		if err != nil {
			return err
		}
		frame.BlockSize = uint16(x + 1)
	case n == 0x7:
		// 0111: get 16 bit (block size)-1 from the end of the header.
		x, err = br.Read(16)
		if err != nil {
			return err
		}
		frame.BlockSize = uint16(x + 1)
	default:
		//    1000-1111: 256 * 2^(n-8) samples.
		frame.BlockSize = 256 * (1 << (n - 8))
	}

	// Parse sample rate.
	//
	// The 4 bits are used to specify the sample rate as follows:
	//    0000: unknown sample rate; get from StreamInfo.
	//    0001: 88.2 kHz.
	//    0010: 176.4 kHz.
	//    0011: 192 kHz.
	//    0100: 8 kHz.
	//    0101: 16 kHz.
	//    0110: 22.05 kHz.
	//    0111: 24 kHz.
	//    1000: 32 kHz.
	//    1001: 44.1 kHz.
	//    1010: 48 kHz.
	//    1011: 96 kHz.
	//    1100: get 8 bit sample rate (in kHz) from the end of the header.
	//    1101: get 16 bit sample rate (in Hz) from the end of the header.
	//    1110: get 16 bit sample rate (in daHz) from the end of the header.
	//    1111: invalid.
	switch sampleRate {
	case 0x0:
		// 0000: unknown sample rate; get from StreamInfo.
	case 0x1:
		// 0001: 88.2 kHz.
		frame.SampleRate = 88200
	case 0x2:
		// 0010: 176.4 kHz.
		frame.SampleRate = 176400
	case 0x3:
		// 0011: 192 kHz.
		frame.SampleRate = 192000
	case 0x4:
		// 0100: 8 kHz.
		frame.SampleRate = 8000
	case 0x5:
		// 0101: 16 kHz.
		frame.SampleRate = 16000
	case 0x6:
		// 0110: 22.05 kHz.
		frame.SampleRate = 22050
	case 0x7:
		// 0111: 24 kHz.
		frame.SampleRate = 24000
	case 0x8:
		// 1000: 32 kHz.
		frame.SampleRate = 32000
	case 0x9:
		// 1001: 44.1 kHz.
		frame.SampleRate = 44100
	case 0xA:
		// 1010: 48 kHz.
		frame.SampleRate = 48000
	case 0xB:
		// 1011: 96 kHz.
		frame.SampleRate = 96000
	case 0xC:
		// 1100: get 8 bit sample rate (in kHz) from the end of the header.
		x, err = br.Read(8)
		if err != nil {
			return err
		}
		frame.SampleRate = uint32(x * 1000)
	case 0xD:
		// 1101: get 16 bit sample rate (in Hz) from the end of the header.
		x, err = br.Read(16)
		if err != nil {
			return err
		}
		frame.SampleRate = uint32(x)
	case 0xE:
		// 1110: get 16 bit sample rate (in daHz) from the end of the header.
		x, err = br.Read(16)
		if err != nil {
			return err
		}
		frame.SampleRate = uint32(x * 10)
	default:
		// 1111: invalid.
		return errors.New("frame.Frame.parseHeader: invalid sample rate bit pattern (1111)")
	}

	// 1 byte: CRC-8 checksum.
	var want uint8
	err = binary.Read(frame.r, binary.BigEndian, &want)
	if err != nil {
		return err
	}
	got := h.Sum8()
	if got != want {
		return fmt.Errorf("frame.Frame.parseHeader: CRC-8 checksum mismatch; expected %v, got %v", want, got)
	}

	return nil
}

// Channels specifies the number of channels (subframes) that exist in a frame,
// their order and possible interchannel correlation.
type Channels uint8

// Channel assignments. The following abbreviations are used:
//    C:   center (directly in front)
//    R:   right (standard stereo)
//    Sr:  side right (directly to the right)
//    Rs:  right surround (back right)
//    Cs:  center surround (rear center)
//    Ls:  left surround (back left)
//    Sl:  side left (directly to the left)
//    L:   left (standard stereo)
//    Lfe: low-frequency effect (placed according to room acoustics)
//
// The first 6 channel constants follow the SMPTE/ITU-R channel order:
//    L R C Lfe Ls Rs
const (
	ChannelsMono           Channels = iota // 1 channel: mono
	ChannelsLR                             // 2 channels: left, right
	ChannelsLRC                            // 3 channels: left, right, center
	ChannelsLRLsRs                         // 4 channels: left, right, left surround, right surround
	ChannelsLRCLsRs                        // 5 channels: left, right, center, left surround, right surround
	ChannelsLRCLfeLsRs                     // 6 channels: left, right, center, LFE, left surround, right surround
	ChannelsLRCLfeCsSlSr                   // 7 channels: left, right, center, LFE, center surround, side left, side right
	ChannelsLRCLfeLsRsSlSr                 // 8 channels: left, right, center, LFE, left surround, right surround, side left, side right
	ChannelsLeftSide                       // 2 channels: left, side; using interchannel correlation
	ChannelsSideRight                      // 2 channels: side, right; using interchannel correlation
	ChannelsMidSide                        // 2 channels: mid, side; using interchannel correlation
)

// nChannels specifies the number of channels used by each channel assignment.
var nChannels = [...]int{
	ChannelsMono:           1,
	ChannelsLR:             2,
	ChannelsLRC:            3,
	ChannelsLRLsRs:         4,
	ChannelsLRCLsRs:        5,
	ChannelsLRCLfeLsRs:     6,
	ChannelsLRCLfeCsSlSr:   7,
	ChannelsLRCLfeLsRsSlSr: 8,
	ChannelsLeftSide:       2,
	ChannelsSideRight:      2,
	ChannelsMidSide:        2,
}

// Count returns the number of channels (subframes) used by the provided channel
// assignment.
func (channels Channels) Count() int {
	return nChannels[channels]
}
<|endoftext|>


<commit_before>package leaf

import (
	"context"
	"errors"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/google/go-cmp/cmp"
	"github.com/gopasspw/gopass/internal/out"
	"github.com/gopasspw/gopass/internal/recipients"
	"github.com/gopasspw/gopass/internal/store"
	"github.com/gopasspw/gopass/pkg/ctxutil"
	"github.com/gopasspw/gopass/pkg/debug"
)

const (
	keyDir    = ".public-keys"
	oldKeyDir = ".gpg-keys"
)

// Recipients returns the list of recipients of this store.
func (s *Store) Recipients(ctx context.Context) []string {
	rs, err := s.GetRecipients(ctx, "")
	if err != nil {
		out.Errorf(ctx, "failed to read recipient list: %s", err)
	}
	return rs
}

// RecipientsTree returns a mapping of secrets to recipients.
func (s *Store) RecipientsTree(ctx context.Context) map[string][]string {
	idfs := s.idFiles(ctx)
	out := make(map[string][]string, len(idfs))

	root := s.Recipients(ctx)
	for _, idf := range idfs {
		if strings.HasPrefix(idf, ".") {
			continue
		}
		srs, err := s.getRecipients(ctx, idf)
		if err != nil {
			debug.Log("failed to list recipients: %s", err)
			continue
		}
		if cmp.Equal(out[""], srs) {
			debug.Log("root recipients equal secret recipients from %s", idf)
			continue
		}
		dir := filepath.Dir(idf)
		debug.Log("adding recipients %+v for %s", srs, dir)
		out[dir] = srs
	}
	out[""] = root
	return out
}

// AddRecipient adds a new recipient to the list.
func (s *Store) AddRecipient(ctx context.Context, id string) error {
	rs, err := s.GetRecipients(ctx, "")
	if err != nil {
		return fmt.Errorf("failed to read recipient list: %w", err)
	}

	debug.Log("new recipient: %q - existing: %+v", id, rs)
	for _, k := range rs {
		if k == id {
			return fmt.Errorf("recipient already in store")
		}
	}

	rs = append(rs, id)

	if err := s.saveRecipients(ctx, rs, "Added Recipient "+id); err != nil {
		return fmt.Errorf("failed to save recipients: %w", err)
	}

	out.Printf(ctx, "Reencrypting existing secrets. This may take some time ...")
	return s.reencrypt(ctxutil.WithCommitMessage(ctx, "Added Recipient "+id))
}

// SaveRecipients persists the current recipients on disk.
func (s *Store) SaveRecipients(ctx context.Context) error {
	rs, err := s.GetRecipients(ctx, "")
	if err != nil {
		return fmt.Errorf("failed to get recipients: %w", err)
	}
	return s.saveRecipients(ctx, rs, "Save Recipients")
}

// SetRecipients will update the stored recipients and the associated checksum.
func (s *Store) SetRecipients(ctx context.Context, rs []string) error {
	return s.saveRecipients(ctx, rs, "Set Recipients")
}

// RemoveRecipient will remove the given recipient from the store
// but if this key is not available on this machine we
// just try to remove it literally.
func (s *Store) RemoveRecipient(ctx context.Context, id string) error {
	keys, err := s.crypto.FindRecipients(ctx, id)
	if err != nil {
		out.Printf(ctx, "Warning: Failed to get GPG Key Info for %s: %s", id, err)
	}

	rs, err := s.GetRecipients(ctx, "")
	if err != nil {
		return fmt.Errorf("failed to read recipient list: %w", err)
	}

	nk := make([]string, 0, len(rs)-1)
RECIPIENTS:
	for _, k := range rs {
		if k == id {
			continue RECIPIENTS
		}
		// if the key is available locally we can also match the id against
		// the fingerprint
		for _, key := range keys {
			if strings.HasSuffix(key, k) {
				continue RECIPIENTS
			}
		}
		nk = append(nk, k)
	}

	if len(rs) == len(nk) {
		return fmt.Errorf("recipient not in store")
	}

	if err := s.saveRecipients(ctx, nk, "Removed Recipient "+id); err != nil {
		return fmt.Errorf("failed to save recipients: %w", err)
	}

	return s.reencrypt(ctxutil.WithCommitMessage(ctx, "Removed Recipient "+id))
}

func (s *Store) ensureOurKeyID(ctx context.Context, rs []string) []string {
	ourID := s.OurKeyID(ctx)
	if ourID == "" {
		return rs
	}
	for _, r := range rs {
		if r == ourID {
			return rs
		}
	}
	rs = append(rs, ourID)
	return rs
}

// OurKeyID returns the key fingprint this user can use to access the store
// (if any).
func (s *Store) OurKeyID(ctx context.Context) string {
	for _, r := range s.Recipients(ctx) {
		kl, err := s.crypto.FindIdentities(ctx, r)
		if err != nil || len(kl) < 1 {
			continue
		}
		return kl[0]
	}
	return ""
}

// GetRecipients will load all Recipients from the .gpg-id file for the given
// secret path.
func (s *Store) GetRecipients(ctx context.Context, name string) ([]string, error) {
	return s.getRecipients(ctx, s.idFile(ctx, name))
}

func (s *Store) getRecipients(ctx context.Context, idf string) ([]string, error) {
	buf, err := s.storage.Get(ctx, idf)
	if err != nil {
		return nil, fmt.Errorf("failed to get recipients from %q: %w", idf, err)
	}

	return recipients.Unmarshal(buf), nil
}

type keyExporter interface {
	ExportPublicKey(ctx context.Context, id string) ([]byte, error)
}

// ExportMissingPublicKeys will export any possibly missing public keys to the
// stores .public-keys directory.
func (s *Store) ExportMissingPublicKeys(ctx context.Context, rs []string) (bool, error) {
	exp, ok := s.crypto.(keyExporter)
	if !ok {
		debug.Log("not exporting public keys for %T", s.crypto)
		return false, nil
	}

	var failed, exported bool
	for _, r := range rs {
		if r == "" {
			continue
		}
		path, err := s.exportPublicKey(ctx, exp, r)
		if err != nil {
			failed = true
			out.Errorf(ctx, "failed to export public key for %q: %s", r, err)
			continue
		}
		if path == "" {
			continue
		}
		// at least one key has been exported
		exported = true
		if err := s.storage.Add(ctx, path); err != nil {
			if errors.Is(err, store.ErrGitNotInit) {
				continue
			}
			failed = true
			out.Errorf(ctx, "failed to add public key for %q to git: %s", r, err)
			continue
		}
		if err := s.storage.Commit(ctx, fmt.Sprintf("Exported Public Keys %s", r)); err != nil && err != store.ErrGitNothingToCommit {
			failed = true
			out.Errorf(ctx, "Failed to git commit: %s", err)
			continue
		}
	}
	if failed {
		return exported, fmt.Errorf("some keys failed")
	}
	return exported, nil
}

// Save all Recipients in memory to the recipients file on disk.
func (s *Store) saveRecipients(ctx context.Context, rs []string, msg string) error {
	if len(rs) < 1 {
		return fmt.Errorf("can not remove all recipients")
	}

	idf := s.idFile(ctx, "")
	buf := recipients.Marshal(rs)
	if err := s.storage.Set(ctx, idf, buf); err != nil {
		return fmt.Errorf("failed to write recipients file: %w", err)
	}

	if err := s.storage.Add(ctx, idf); err != nil {
		if err != store.ErrGitNotInit {
			return fmt.Errorf("failed to add file %q to git: %w", idf, err)
		}
	}

	if err := s.storage.Commit(ctx, msg); err != nil {
		if err != store.ErrGitNotInit && err != store.ErrGitNothingToCommit {
			return fmt.Errorf("failed to commit changes to git: %w", err)
		}
	}

	// save all recipients public keys to the repo
	if ctxutil.IsExportKeys(ctx) {
		if _, err := s.ExportMissingPublicKeys(ctx, rs); err != nil {
			out.Errorf(ctx, "Failed to export missing public keys: %s", err)
		}
	}

	// push to remote repo
	if err := s.storage.Push(ctx, "", ""); err != nil {
		if errors.Is(err, store.ErrGitNotInit) {
			return nil
		}
		if errors.Is(err, store.ErrGitNoRemote) {
			msg := "Warning: git has no remote. Ignoring auto-push option\n" +
				"Run: gopass git remote add origin ..."
			debug.Log(msg)
			return nil
		}
		return fmt.Errorf("failed to push changes to git: %w", err)
	}

	return nil
}
<commit_msg>Depending on how the .gpg-ids file was written, we may not match against the string itself. (#2147)<commit_after>package leaf

import (
	"context"
	"errors"
	"fmt"
	"path/filepath"
	"strings"

	"github.com/google/go-cmp/cmp"
	"github.com/gopasspw/gopass/internal/out"
	"github.com/gopasspw/gopass/internal/recipients"
	"github.com/gopasspw/gopass/internal/store"
	"github.com/gopasspw/gopass/pkg/ctxutil"
	"github.com/gopasspw/gopass/pkg/debug"
)

const (
	keyDir    = ".public-keys"
	oldKeyDir = ".gpg-keys"
)

// Recipients returns the list of recipients of this store.
func (s *Store) Recipients(ctx context.Context) []string {
	rs, err := s.GetRecipients(ctx, "")
	if err != nil {
		out.Errorf(ctx, "failed to read recipient list: %s", err)
	}
	return rs
}

// RecipientsTree returns a mapping of secrets to recipients.
func (s *Store) RecipientsTree(ctx context.Context) map[string][]string {
	idfs := s.idFiles(ctx)
	out := make(map[string][]string, len(idfs))

	root := s.Recipients(ctx)
	for _, idf := range idfs {
		if strings.HasPrefix(idf, ".") {
			continue
		}
		srs, err := s.getRecipients(ctx, idf)
		if err != nil {
			debug.Log("failed to list recipients: %s", err)
			continue
		}
		if cmp.Equal(out[""], srs) {
			debug.Log("root recipients equal secret recipients from %s", idf)
			continue
		}
		dir := filepath.Dir(idf)
		debug.Log("adding recipients %+v for %s", srs, dir)
		out[dir] = srs
	}
	out[""] = root
	return out
}

// AddRecipient adds a new recipient to the list.
func (s *Store) AddRecipient(ctx context.Context, id string) error {
	rs, err := s.GetRecipients(ctx, "")
	if err != nil {
		return fmt.Errorf("failed to read recipient list: %w", err)
	}

	debug.Log("new recipient: %q - existing: %+v", id, rs)
	for _, k := range rs {
		if k == id {
			return fmt.Errorf("recipient already in store")
		}
	}

	rs = append(rs, id)

	if err := s.saveRecipients(ctx, rs, "Added Recipient "+id); err != nil {
		return fmt.Errorf("failed to save recipients: %w", err)
	}

	out.Printf(ctx, "Reencrypting existing secrets. This may take some time ...")
	return s.reencrypt(ctxutil.WithCommitMessage(ctx, "Added Recipient "+id))
}

// SaveRecipients persists the current recipients on disk.
func (s *Store) SaveRecipients(ctx context.Context) error {
	rs, err := s.GetRecipients(ctx, "")
	if err != nil {
		return fmt.Errorf("failed to get recipients: %w", err)
	}
	return s.saveRecipients(ctx, rs, "Save Recipients")
}

// SetRecipients will update the stored recipients and the associated checksum.
func (s *Store) SetRecipients(ctx context.Context, rs []string) error {
	return s.saveRecipients(ctx, rs, "Set Recipients")
}

// RemoveRecipient will remove the given recipient from the store
// but if this key is not available on this machine we
// just try to remove it literally.
func (s *Store) RemoveRecipient(ctx context.Context, id string) error {
	keys, err := s.crypto.FindRecipients(ctx, id)
	if err != nil {
		out.Warningf(ctx, "Warning: Failed to get GPG Key Info for %s: %s", id, err)
	}

	rs, err := s.GetRecipients(ctx, "")
	if err != nil {
		return fmt.Errorf("failed to read recipient list: %w", err)
	}

	nk := make([]string, 0, len(rs)-1)
RECIPIENTS:
	for _, k := range rs {

		// First lets try a simple match of the stored ids
		if k == id {
			debug.Log("removing recipient based on id match %s", k)
			continue RECIPIENTS
		}

		// If we don't match immediately, we may need to loop through the recipient keys to try and match.
		// To do this though, we need to ensure that we also do a FindRecipients on the id name from the stored ids.
		recipientIds, err := s.crypto.FindRecipients(ctx, k)
		if err != nil {
			out.Warningf(ctx, "Warning: Failed to get GPG Key Info for %s: %s", k, err)
		}
		debug.Log("returned the following ids for recipient %s: %s", k, recipientIds)

		// if the key is available locally we can also match the id against
		// the fingerprint or failing that we can try against the recipientIds
		for _, key := range keys {
			if strings.HasSuffix(key, k) {
				debug.Log("removing recipient based on id suffix match: %s %s", key, k)
				continue RECIPIENTS
			}

			for _, recipientID := range recipientIds {
				if recipientID == key {
					debug.Log("removing recipient based on recipient id match %s", recipientID)
					continue RECIPIENTS
				}
			}
		}

		nk = append(nk, k)
	}

	if len(rs) == len(nk) {
		return fmt.Errorf("recipient not in store")
	}

	if err := s.saveRecipients(ctx, nk, "Removed Recipient "+id); err != nil {
		return fmt.Errorf("failed to save recipients: %w", err)
	}

	return s.reencrypt(ctxutil.WithCommitMessage(ctx, "Removed Recipient "+id))
}

func (s *Store) ensureOurKeyID(ctx context.Context, rs []string) []string {
	ourID := s.OurKeyID(ctx)
	if ourID == "" {
		return rs
	}
	for _, r := range rs {
		if r == ourID {
			return rs
		}
	}
	rs = append(rs, ourID)
	return rs
}

// OurKeyID returns the key fingprint this user can use to access the store
// (if any).
func (s *Store) OurKeyID(ctx context.Context) string {
	for _, r := range s.Recipients(ctx) {
		kl, err := s.crypto.FindIdentities(ctx, r)
		if err != nil || len(kl) < 1 {
			continue
		}
		return kl[0]
	}
	return ""
}

// GetRecipients will load all Recipients from the .gpg-id file for the given
// secret path.
func (s *Store) GetRecipients(ctx context.Context, name string) ([]string, error) {
	return s.getRecipients(ctx, s.idFile(ctx, name))
}

func (s *Store) getRecipients(ctx context.Context, idf string) ([]string, error) {
	buf, err := s.storage.Get(ctx, idf)
	if err != nil {
		return nil, fmt.Errorf("failed to get recipients from %q: %w", idf, err)
	}

	return recipients.Unmarshal(buf), nil
}

type keyExporter interface {
	ExportPublicKey(ctx context.Context, id string) ([]byte, error)
}

// ExportMissingPublicKeys will export any possibly missing public keys to the
// stores .public-keys directory.
func (s *Store) ExportMissingPublicKeys(ctx context.Context, rs []string) (bool, error) {
	exp, ok := s.crypto.(keyExporter)
	if !ok {
		debug.Log("not exporting public keys for %T", s.crypto)
		return false, nil
	}

	var failed, exported bool
	for _, r := range rs {
		if r == "" {
			continue
		}
		path, err := s.exportPublicKey(ctx, exp, r)
		if err != nil {
			failed = true
			out.Errorf(ctx, "failed to export public key for %q: %s", r, err)
			continue
		}
		if path == "" {
			continue
		}
		// at least one key has been exported
		exported = true
		if err := s.storage.Add(ctx, path); err != nil {
			if errors.Is(err, store.ErrGitNotInit) {
				continue
			}
			failed = true
			out.Errorf(ctx, "failed to add public key for %q to git: %s", r, err)
			continue
		}
		if err := s.storage.Commit(ctx, fmt.Sprintf("Exported Public Keys %s", r)); err != nil && err != store.ErrGitNothingToCommit {
			failed = true
			out.Errorf(ctx, "Failed to git commit: %s", err)
			continue
		}
	}
	if failed {
		return exported, fmt.Errorf("some keys failed")
	}
	return exported, nil
}

// Save all Recipients in memory to the recipients file on disk.
func (s *Store) saveRecipients(ctx context.Context, rs []string, msg string) error {
	if len(rs) < 1 {
		return fmt.Errorf("can not remove all recipients")
	}

	idf := s.idFile(ctx, "")
	buf := recipients.Marshal(rs)
	if err := s.storage.Set(ctx, idf, buf); err != nil {
		return fmt.Errorf("failed to write recipients file: %w", err)
	}

	if err := s.storage.Add(ctx, idf); err != nil {
		if err != store.ErrGitNotInit {
			return fmt.Errorf("failed to add file %q to git: %w", idf, err)
		}
	}

	if err := s.storage.Commit(ctx, msg); err != nil {
		if err != store.ErrGitNotInit && err != store.ErrGitNothingToCommit {
			return fmt.Errorf("failed to commit changes to git: %w", err)
		}
	}

	// save all recipients public keys to the repo
	if ctxutil.IsExportKeys(ctx) {
		if _, err := s.ExportMissingPublicKeys(ctx, rs); err != nil {
			out.Errorf(ctx, "Failed to export missing public keys: %s", err)
		}
	}

	// push to remote repo
	if err := s.storage.Push(ctx, "", ""); err != nil {
		if errors.Is(err, store.ErrGitNotInit) {
			return nil
		}
		if errors.Is(err, store.ErrGitNoRemote) {
			msg := "Warning: git has no remote. Ignoring auto-push option\n" +
				"Run: gopass git remote add origin ..."
			debug.Log(msg)
			return nil
		}
		return fmt.Errorf("failed to push changes to git: %w", err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Hajime Hoshi
//
// 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.

//go:build (dragonfly || freebsd || linux || netbsd || openbsd || solaris) && !android
// +build dragonfly freebsd linux netbsd openbsd solaris
// +build !android

package glfw

import (
	"math"
	"runtime"

	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/glfw"
	"github.com/jezek/xgb"
	"github.com/jezek/xgb/randr"
	"github.com/jezek/xgb/xproto"
)

type videoModeScaleCacheKey struct{ X, Y int }

var videoModeScaleCache = map[videoModeScaleCacheKey]float64{}

// clearVideoModeScaleCache must be called from the main thread.
func clearVideoModeScaleCache() {
	for k := range videoModeScaleCache {
		delete(videoModeScaleCache, k)
	}
}

// videoModeScale must be called from the main thread.
func videoModeScale(m *glfw.Monitor) float64 {
	// Caching wrapper for videoModeScaleUncached as
	// videoModeScaleUncached may be expensive (uses blocking calls on X connection)
	// and public ScreenSizeInFullscreen API needs the videoModeScale.
	monitorX, monitorY := m.GetPos()
	cacheKey := videoModeScaleCacheKey{X: monitorX, Y: monitorY}
	if cached, ok := videoModeScaleCache[cacheKey]; ok {
		return cached
	}
	scale := videoModeScaleUncached(m, monitorX, monitorY)
	videoModeScaleCache[cacheKey] = scale
	return scale
}

// videoModeScaleUncached must be called from the main thread.
func videoModeScaleUncached(m *glfw.Monitor, monitorX, monitorY int) float64 {
	// TODO: if https://github.com/glfw/glfw/issues/1961 gets fixed, this function may need revising.
	// In case GLFW decides to switch to returning logical pixels, we can just return 1.

	// Note: GLFW currently returns physical pixel sizes,
	// but we need to predict the window system-side size of the fullscreen window
	// for our `ScreenSizeInFullscreen` public API.
	// Also at the moment we need this prior to switching to fullscreen, but that might be replacable.
	// So this function computes the ratio of physical per logical pixels.
	xconn, err := xgb.NewConn()
	defer xconn.Close()
	if err != nil {
		// No X11 connection?
		// Assume we're on pure Wayland then.
		// GLFW/Wayland shouldn't be having this issue.
		return 1
	}
	if err = randr.Init(xconn); err != nil {
		// No RANDR extension?
		// No problem.
		return 1
	}
	root := xproto.Setup(xconn).DefaultScreen(xconn).Root
	res, err := randr.GetScreenResourcesCurrent(xconn, root).Reply()
	if err != nil {
		// Likely means RANDR is not working.
		// No problem.
		return 1
	}
	for _, crtc := range res.Crtcs[:res.NumCrtcs] {
		info, err := randr.GetCrtcInfo(xconn, crtc, res.ConfigTimestamp).Reply()
		if err != nil {
			// This Crtc is bad. Maybe just got disconnected?
			continue
		}
		if info.NumOutputs == 0 {
			// This Crtc is not connected to any output.
			// In other words, a disabled monitor.
			continue
		}
		if int(info.X) == monitorX && int(info.Y) == monitorY {
			xWidth, xHeight := info.Width, info.Height
			vm := m.GetVideoMode()
			physWidth, physHeight := vm.Width, vm.Height
			// Return one scale, even though there may be separate X and Y scales.
			// Return the _larger_ scale, as this would yield a letterboxed display on mismatch, rather than a cut-off one.
			scale := math.Max(float64(physWidth)/float64(xWidth), float64(physHeight)/float64(xHeight))
			return scale
		}
	}
	// Monitor not known to XRandR. Weird.
	return 1
}

// fromGLFWMonitorPixel must be called from the main thread.
func (u *UserInterface) fromGLFWMonitorPixel(x float64, videoModeScale float64) float64 {
	return x / (videoModeScale * u.deviceScaleFactor())
}

// fromGLFWPixel must be called from the main thread.
func (u *UserInterface) fromGLFWPixel(x float64) float64 {
	return x / u.deviceScaleFactor()
}

// toGLFWPixel must be called from the main thread.
func (u *UserInterface) toGLFWPixel(x float64) float64 {
	return x * u.deviceScaleFactor()
}

func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) {
	return x, y
}

func currentMonitorByOS(_ *glfw.Window) *glfw.Monitor {
	// TODO: Implement this correctly. (#1119).
	return nil
}

func (u *UserInterface) nativeWindow() uintptr {
	// TODO: Implement this.
	return 0
}

func (u *UserInterface) isNativeFullscreen() bool {
	return false
}

func (u *UserInterface) setNativeCursor(shape driver.CursorShape) {
	// TODO: Use native API in the future (#1571)
	u.window.SetCursor(glfwSystemCursors[shape])
}

func (u *UserInterface) isNativeFullscreenAvailable() bool {
	return false
}

func (u *UserInterface) setNativeFullscreen(fullscreen bool) {
	panic(fmt.Sprintf("glfw: setNativeFullscreen is not implemented in this environment: %s", runtime.GOOS))
}
<commit_msg>internal/uidriver: Bug fix: Compile error on Linux/UNIX<commit_after>// Copyright 2016 Hajime Hoshi
//
// 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.

//go:build (dragonfly || freebsd || linux || netbsd || openbsd || solaris) && !android
// +build dragonfly freebsd linux netbsd openbsd solaris
// +build !android

package glfw

import (
	"fmt"
	"math"
	"runtime"

	"github.com/hajimehoshi/ebiten/v2/internal/driver"
	"github.com/hajimehoshi/ebiten/v2/internal/glfw"
	"github.com/jezek/xgb"
	"github.com/jezek/xgb/randr"
	"github.com/jezek/xgb/xproto"
)

type videoModeScaleCacheKey struct{ X, Y int }

var videoModeScaleCache = map[videoModeScaleCacheKey]float64{}

// clearVideoModeScaleCache must be called from the main thread.
func clearVideoModeScaleCache() {
	for k := range videoModeScaleCache {
		delete(videoModeScaleCache, k)
	}
}

// videoModeScale must be called from the main thread.
func videoModeScale(m *glfw.Monitor) float64 {
	// Caching wrapper for videoModeScaleUncached as
	// videoModeScaleUncached may be expensive (uses blocking calls on X connection)
	// and public ScreenSizeInFullscreen API needs the videoModeScale.
	monitorX, monitorY := m.GetPos()
	cacheKey := videoModeScaleCacheKey{X: monitorX, Y: monitorY}
	if cached, ok := videoModeScaleCache[cacheKey]; ok {
		return cached
	}
	scale := videoModeScaleUncached(m, monitorX, monitorY)
	videoModeScaleCache[cacheKey] = scale
	return scale
}

// videoModeScaleUncached must be called from the main thread.
func videoModeScaleUncached(m *glfw.Monitor, monitorX, monitorY int) float64 {
	// TODO: if https://github.com/glfw/glfw/issues/1961 gets fixed, this function may need revising.
	// In case GLFW decides to switch to returning logical pixels, we can just return 1.

	// Note: GLFW currently returns physical pixel sizes,
	// but we need to predict the window system-side size of the fullscreen window
	// for our `ScreenSizeInFullscreen` public API.
	// Also at the moment we need this prior to switching to fullscreen, but that might be replacable.
	// So this function computes the ratio of physical per logical pixels.
	xconn, err := xgb.NewConn()
	defer xconn.Close()
	if err != nil {
		// No X11 connection?
		// Assume we're on pure Wayland then.
		// GLFW/Wayland shouldn't be having this issue.
		return 1
	}
	if err = randr.Init(xconn); err != nil {
		// No RANDR extension?
		// No problem.
		return 1
	}
	root := xproto.Setup(xconn).DefaultScreen(xconn).Root
	res, err := randr.GetScreenResourcesCurrent(xconn, root).Reply()
	if err != nil {
		// Likely means RANDR is not working.
		// No problem.
		return 1
	}
	for _, crtc := range res.Crtcs[:res.NumCrtcs] {
		info, err := randr.GetCrtcInfo(xconn, crtc, res.ConfigTimestamp).Reply()
		if err != nil {
			// This Crtc is bad. Maybe just got disconnected?
			continue
		}
		if info.NumOutputs == 0 {
			// This Crtc is not connected to any output.
			// In other words, a disabled monitor.
			continue
		}
		if int(info.X) == monitorX && int(info.Y) == monitorY {
			xWidth, xHeight := info.Width, info.Height
			vm := m.GetVideoMode()
			physWidth, physHeight := vm.Width, vm.Height
			// Return one scale, even though there may be separate X and Y scales.
			// Return the _larger_ scale, as this would yield a letterboxed display on mismatch, rather than a cut-off one.
			scale := math.Max(float64(physWidth)/float64(xWidth), float64(physHeight)/float64(xHeight))
			return scale
		}
	}
	// Monitor not known to XRandR. Weird.
	return 1
}

// fromGLFWMonitorPixel must be called from the main thread.
func (u *UserInterface) fromGLFWMonitorPixel(x float64, videoModeScale float64) float64 {
	return x / (videoModeScale * u.deviceScaleFactor())
}

// fromGLFWPixel must be called from the main thread.
func (u *UserInterface) fromGLFWPixel(x float64) float64 {
	return x / u.deviceScaleFactor()
}

// toGLFWPixel must be called from the main thread.
func (u *UserInterface) toGLFWPixel(x float64) float64 {
	return x * u.deviceScaleFactor()
}

func (u *UserInterface) adjustWindowPosition(x, y int) (int, int) {
	return x, y
}

func currentMonitorByOS(_ *glfw.Window) *glfw.Monitor {
	// TODO: Implement this correctly. (#1119).
	return nil
}

func (u *UserInterface) nativeWindow() uintptr {
	// TODO: Implement this.
	return 0
}

func (u *UserInterface) isNativeFullscreen() bool {
	return false
}

func (u *UserInterface) setNativeCursor(shape driver.CursorShape) {
	// TODO: Use native API in the future (#1571)
	u.window.SetCursor(glfwSystemCursors[shape])
}

func (u *UserInterface) isNativeFullscreenAvailable() bool {
	return false
}

func (u *UserInterface) setNativeFullscreen(fullscreen bool) {
	panic(fmt.Sprintf("glfw: setNativeFullscreen is not implemented in this environment: %s", runtime.GOOS))
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

// The trie in this file is used to associate the first full character
// in an UTF-8 string to a collation element.
// All but the last byte in a UTF-8 byte sequence are
// used to lookup offsets in the index table to be used for the next byte.
// The last byte is used to index into a table of collation elements.
// For a full description, see exp/locale/collate/build/trie.go.

package colltab

const blockSize = 64

type trie struct {
	index0  []uint16 // index for first byte (0xC0-0xFF)
	values0 []uint32 // index for first byte (0x00-0x7F)
	index   []uint16
	values  []uint32
}

const (
	t1 = 0x00 // 0000 0000
	tx = 0x80 // 1000 0000
	t2 = 0xC0 // 1100 0000
	t3 = 0xE0 // 1110 0000
	t4 = 0xF0 // 1111 0000
	t5 = 0xF8 // 1111 1000
	t6 = 0xFC // 1111 1100
	te = 0xFE // 1111 1110
)

func (t *trie) lookupValue(n uint16, b byte) Elem {
	return Elem(t.values[int(n)<<6+int(b)])
}

// lookup returns the trie value for the first UTF-8 encoding in s and
// the width in bytes of this encoding. The size will be 0 if s does not
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
func (t *trie) lookup(s []byte) (v Elem, sz int) {
	c0 := s[0]
	switch {
	case c0 < tx:
		return Elem(t.values0[c0]), 1
	case c0 < t2:
		return 0, 1
	case c0 < t3:
		if len(s) < 2 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		return t.lookupValue(i, c1), 2
	case c0 < t4:
		if len(s) < 3 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		o := int(i)<<6 + int(c1)
		i = t.index[o]
		c2 := s[2]
		if c2 < tx || t2 <= c2 {
			return 0, 2
		}
		return t.lookupValue(i, c2), 3
	case c0 < t5:
		if len(s) < 4 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		o := int(i)<<6 + int(c1)
		i = t.index[o]
		c2 := s[2]
		if c2 < tx || t2 <= c2 {
			return 0, 2
		}
		o = int(i)<<6 + int(c2)
		i = t.index[o]
		c3 := s[3]
		if c3 < tx || t2 <= c3 {
			return 0, 3
		}
		return t.lookupValue(i, c3), 4
	}
	// Illegal rune
	return 0, 1
}

// The body of lookupString is a verbatim copy of that of lookup.
func (t *trie) lookupString(s string) (v Elem, sz int) {
	c0 := s[0]
	switch {
	case c0 < tx:
		return Elem(t.values0[c0]), 1
	case c0 < t2:
		return 0, 1
	case c0 < t3:
		if len(s) < 2 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		return t.lookupValue(i, c1), 2
	case c0 < t4:
		if len(s) < 3 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		o := int(i)<<6 + int(c1)
		i = t.index[o]
		c2 := s[2]
		if c2 < tx || t2 <= c2 {
			return 0, 2
		}
		return t.lookupValue(i, c2), 3
	case c0 < t5:
		if len(s) < 4 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		o := int(i)<<6 + int(c1)
		i = t.index[o]
		c2 := s[2]
		if c2 < tx || t2 <= c2 {
			return 0, 2
		}
		o = int(i)<<6 + int(c2)
		i = t.index[o]
		c3 := s[3]
		if c3 < tx || t2 <= c3 {
			return 0, 3
		}
		return t.lookupValue(i, c3), 4
	}
	// Illegal rune
	return 0, 1
}
<commit_msg>go.text/collate/colltab: doc update.<commit_after>// Copyright 2012 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.

// The trie in this file is used to associate the first full character in an
// UTF-8 string to a collation element. All but the last byte in a UTF-8 byte
// sequence are used to lookup offsets in the index table to be used for the
// next byte. The last byte is used to index into a table of collation elements.
// For a full description, see go.text/collate/build/trie.go.

package colltab

const blockSize = 64

type trie struct {
	index0  []uint16 // index for first byte (0xC0-0xFF)
	values0 []uint32 // index for first byte (0x00-0x7F)
	index   []uint16
	values  []uint32
}

const (
	t1 = 0x00 // 0000 0000
	tx = 0x80 // 1000 0000
	t2 = 0xC0 // 1100 0000
	t3 = 0xE0 // 1110 0000
	t4 = 0xF0 // 1111 0000
	t5 = 0xF8 // 1111 1000
	t6 = 0xFC // 1111 1100
	te = 0xFE // 1111 1110
)

func (t *trie) lookupValue(n uint16, b byte) Elem {
	return Elem(t.values[int(n)<<6+int(b)])
}

// lookup returns the trie value for the first UTF-8 encoding in s and
// the width in bytes of this encoding. The size will be 0 if s does not
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
func (t *trie) lookup(s []byte) (v Elem, sz int) {
	c0 := s[0]
	switch {
	case c0 < tx:
		return Elem(t.values0[c0]), 1
	case c0 < t2:
		return 0, 1
	case c0 < t3:
		if len(s) < 2 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		return t.lookupValue(i, c1), 2
	case c0 < t4:
		if len(s) < 3 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		o := int(i)<<6 + int(c1)
		i = t.index[o]
		c2 := s[2]
		if c2 < tx || t2 <= c2 {
			return 0, 2
		}
		return t.lookupValue(i, c2), 3
	case c0 < t5:
		if len(s) < 4 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		o := int(i)<<6 + int(c1)
		i = t.index[o]
		c2 := s[2]
		if c2 < tx || t2 <= c2 {
			return 0, 2
		}
		o = int(i)<<6 + int(c2)
		i = t.index[o]
		c3 := s[3]
		if c3 < tx || t2 <= c3 {
			return 0, 3
		}
		return t.lookupValue(i, c3), 4
	}
	// Illegal rune
	return 0, 1
}

// The body of lookupString is a verbatim copy of that of lookup.
func (t *trie) lookupString(s string) (v Elem, sz int) {
	c0 := s[0]
	switch {
	case c0 < tx:
		return Elem(t.values0[c0]), 1
	case c0 < t2:
		return 0, 1
	case c0 < t3:
		if len(s) < 2 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		return t.lookupValue(i, c1), 2
	case c0 < t4:
		if len(s) < 3 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		o := int(i)<<6 + int(c1)
		i = t.index[o]
		c2 := s[2]
		if c2 < tx || t2 <= c2 {
			return 0, 2
		}
		return t.lookupValue(i, c2), 3
	case c0 < t5:
		if len(s) < 4 {
			return 0, 0
		}
		i := t.index0[c0]
		c1 := s[1]
		if c1 < tx || t2 <= c1 {
			return 0, 1
		}
		o := int(i)<<6 + int(c1)
		i = t.index[o]
		c2 := s[2]
		if c2 < tx || t2 <= c2 {
			return 0, 2
		}
		o = int(i)<<6 + int(c2)
		i = t.index[o]
		c3 := s[3]
		if c3 < tx || t2 <= c3 {
			return 0, 3
		}
		return t.lookupValue(i, c3), 4
	}
	// Illegal rune
	return 0, 1
}
<|endoftext|>


<commit_before>
package atourofgo

import (
	"fmt"
	"errors"
)

/**
*	Different logging levels
*/
const (
	Info = iota
	Warning = iota
	Error = iota
)

/**
*	A logger. This is an example which uses `fmt.Printf` to log
*	to the standard output stream.
*
*	I realise that there is already a standard API for dealing with
*	logging in GoLang, but its really just for demonstrational purposes.
*/
type StdOutLogger struct {

	/**
	*	A string that the logs are all prefixed with. Defaults to
	* 	'Logger:'
	*
	*	@public
	*/
	LogPrefix string
}

/**
*	Initialise a new StdOutLogger instance
*
*	@public
*/
func NewStdOutLogger() *StdOutLogger {

	logger := &StdOutLogger{
		logPrefix: "Logger:"
	}

	return logger
}

/**
*	Builds the logging prefix from both the LogPrefix property and
*	the given log level.
*
*	@param 	level	Should be either Info, Warning or Error
*	@return	string	The constructed logging prefix
*	@return error	An error or nil if no error occured
*	@private
*/
func (logger *StdOutLogger) buildPrefix(level int) (prefix string, err error){

	logLevelMap = map[int] string{
		Info: "<I>",
		Warning: "<W>",
		Error: "<E>",
	}

	formattedPrefix string
	err error

	levelStr, levelValid := logLevelMap

	if !levelValid{
		err = errors.New("Log level is invalid")
	}
	else{
		formattedPrefix = fmt.Fprintf("%q: %q ...", logger.LogPrefix, levelStr)
	}

	return formattedPrefix, err
}

/**
*	Logs with the 'Info' prefix
*
*	@public
*/
func (logger *StdOutLogger) LogInfo() {

}

/**
*	Logs with the 'Warning' prefix
*
*	@public
*/
func (logger *StdOutLogger) LogWarn() {

}

/**
*	Logs with the 'Error' prefix
*
*	@public
*/
func (logger *StdOutLogger) LogErr() {

}
<commit_msg>My logger struct compiles<commit_after>
package atourofgo

import (
	"fmt"
	"errors"
)

/**
*	Different logging levels
*/
const (
	Info = iota
	Warning = iota
	Error = iota
)

/**
*	A logger. This is an example which uses `fmt.Printf` to log
*	to the standard output stream.
*
*	I realise that there is already a standard API for dealing with
*	logging in GoLang, but its really just for demonstrational purposes.
*/
type StdOutLogger struct {

	/**
	*	A string that the logs are all prefixed with. Defaults to
	* 	'Logger:'
	*
	*	@public
	*/
	LogPrefix string
}

/**
*	Initialise a new StdOutLogger instance
*
*	@public
*/
func NewStdOutLogger() *StdOutLogger {

	logger := &StdOutLogger{
		LogPrefix: "Logger:",
	}

	return logger
}

/**
*	Builds the logging prefix from both the LogPrefix property and
*	the given log level.
*
*	@param 	level	Should be either Info, Warning or Error
*	@return	string	The constructed logging prefix
*	@return error	An error or nil if no error occured
*	@private
*/
func (logger *StdOutLogger) buildPrefix(level int) (string, error){

	logLevelMap := map[int] string{
		Info: "<I>",
		Warning: "<W>",
		Error: "<E>",
	}

	var formattedPrefix string
	var err error

	levelStr, levelValid := logLevelMap[level]

	if !levelValid {
		err = errors.New("Log level is invalid")
	} else {
		formattedPrefix = fmt.Sprintf("%q: %q ...", logger.LogPrefix, levelStr)
	}

	return formattedPrefix, err
}

/**
*	Creates a formatted log string from the given log level.
*/
func (logger *StdOutLogger) formattedLogStr(log string, logLevel int) (formattedLog string, err error) {
	formattedLog, err = logger.buildPrefix(logLevel)
	return
}

/**
*	Logs with the 'Info' prefix
*
*	@public
*/
func (logger *StdOutLogger) LogInfo(log string, params ...string) error {

	formattedLog, err := logger.formattedLogStr(log, Info)

	if err != nil {
		fmt.Printf(formattedLog, params)
	}

	return err
}

/**
*	Logs with the 'Warning' prefix
*
*	@public
*/
func (logger *StdOutLogger) LogWarn(log string, params ...string) error {

	formattedLog, err := logger.formattedLogStr(log, Warning)

	if err != nil {
		fmt.Printf(formattedLog, params)
	}

	return err
}

/**
*	Logs with the 'Error' prefix
*
*	@public
*/
func (logger *StdOutLogger) LogErr(log string, params ...string) error {

	formattedLog, err := logger.formattedLogStr(log, Error)

	if err != nil {
		fmt.Printf(formattedLog, params)
	}

	return err
}
<|endoftext|>


<commit_before>// This is free and unencumbered software released into the public
// domain.  For more information, see <http://unlicense.org> or the
// accompanying UNLICENSE file.
package commander

import (
	"github.com/nelsam/gxui"
	"github.com/nelsam/gxui/themes/basic"
	"github.com/nelsam/vidar/controller"
)

type FileOpener struct {
	file  *FSLocator
	input <-chan gxui.Focusable
}

func NewFileOpener(driver gxui.Driver, theme *basic.Theme) controller.Command {
	fileOpener := new(FileOpener)
	fileOpener.Init(driver, theme)
	return fileOpener
}

func (f *FileOpener) Init(driver gxui.Driver, theme *basic.Theme) {
	f.file = NewFSLocator(driver, theme)
}

func (f *FileOpener) Name() string {
	return "open-file"
}

func (f *FileOpener) Start(control gxui.Control) gxui.Control {
	f.file.loadEditorDir(control)
	input := make(chan gxui.Focusable)
	f.input = input
	input <- f.file
	close(input)
	return nil
}

func (f *FileOpener) Next() gxui.Focusable {
	return <-f.input
}

func (f *FileOpener) Exec(element interface{}) (consume bool) {
	if editor, ok := element.(controller.Editor); ok {
		editor.Open(f.file.Path())
		return true
	}
	return false
}
<commit_msg>Buffer input channel<commit_after>// This is free and unencumbered software released into the public
// domain.  For more information, see <http://unlicense.org> or the
// accompanying UNLICENSE file.
package commander

import (
	"github.com/nelsam/gxui"
	"github.com/nelsam/gxui/themes/basic"
	"github.com/nelsam/vidar/controller"
)

type FileOpener struct {
	file  *FSLocator
	input <-chan gxui.Focusable
}

func NewFileOpener(driver gxui.Driver, theme *basic.Theme) controller.Command {
	fileOpener := new(FileOpener)
	fileOpener.Init(driver, theme)
	return fileOpener
}

func (f *FileOpener) Init(driver gxui.Driver, theme *basic.Theme) {
	f.file = NewFSLocator(driver, theme)
}

func (f *FileOpener) Name() string {
	return "open-file"
}

func (f *FileOpener) Start(control gxui.Control) gxui.Control {
	f.file.loadEditorDir(control)
	input := make(chan gxui.Focusable, 1)
	f.input = input
	input <- f.file
	close(input)
	return nil
}

func (f *FileOpener) Next() gxui.Focusable {
	return <-f.input
}

func (f *FileOpener) Exec(element interface{}) (consume bool) {
	if editor, ok := element.(controller.Editor); ok {
		editor.Open(f.file.Path())
		return true
	}
	return false
}
<|endoftext|>


<commit_before>// Package errors is a drop-in replacement for Golang lib 'errors'.
package errors

import (
	"fmt"
	"strings"

	"v2ray.com/core/common/serial"
)

type Severity int

const (
	SeverityDebug Severity = iota
	SeverityInfo
	SeverityWarning
	SeverityError
)

type hasInnerError interface {
	// Inner returns the underlying error of this one.
	Inner() error
}

type hasSeverity interface {
	Severity() Severity
}

// Error is an error object with underlying error.
type Error struct {
	message  string
	inner    error
	severity Severity
	path     []string
}

// Error implements error.Error().
func (v *Error) Error() string {
	msg := v.message
	if v.inner != nil {
		msg += " > " + v.inner.Error()
	}
	if len(v.path) > 0 {
		msg = strings.Join(v.path, "|") + ": " + msg
	}
	return msg
}

// Inner implements hasInnerError.Inner()
func (v *Error) Inner() error {
	if v.inner == nil {
		return nil
	}
	return v.inner
}

func (v *Error) Base(err error) *Error {
	v.inner = err
	return v
}

func (v *Error) atSeverity(s Severity) *Error {
	v.severity = s
	return v
}

func (v *Error) Severity() Severity {
	if v.inner == nil {
		return v.severity
	}

	if s, ok := v.inner.(hasSeverity); ok {
		as := s.Severity()
		if as > v.severity {
			return as
		}
	}

	return v.severity
}

func (v *Error) AtDebug() *Error {
	return v.atSeverity(SeverityDebug)
}

func (v *Error) AtInfo() *Error {
	return v.atSeverity(SeverityInfo)
}

func (v *Error) AtWarning() *Error {
	return v.atSeverity(SeverityWarning)
}

func (v *Error) AtError() *Error {
	return v.atSeverity(SeverityError)
}

func (v *Error) Path(path ...string) *Error {
	v.path = path
	return v
}

// New returns a new error object with message formed from given arguments.
func New(msg ...interface{}) *Error {
	return &Error{
		message:  serial.Concat(msg...),
		severity: SeverityInfo,
	}
}

func Format(format string, values ...interface{}) *Error {
	return New(fmt.Sprintf(format, values...))
}

// Cause returns the root cause of this error.
func Cause(err error) error {
	if err == nil {
		return nil
	}
	for {
		inner, ok := err.(hasInnerError)
		if !ok || inner.Inner() == nil {
			break
		}
		err = inner.Inner()
	}
	return err
}

func GetSeverity(err error) Severity {
	if s, ok := err.(hasSeverity); ok {
		return s.Severity()
	}
	return SeverityInfo
}
<commit_msg>laze init of error messages<commit_after>// Package errors is a drop-in replacement for Golang lib 'errors'.
package errors

import (
	"fmt"
	"strings"

	"v2ray.com/core/common/serial"
)

type Severity int

const (
	SeverityDebug Severity = iota
	SeverityInfo
	SeverityWarning
	SeverityError
)

type hasInnerError interface {
	// Inner returns the underlying error of this one.
	Inner() error
}

type hasSeverity interface {
	Severity() Severity
}

// Error is an error object with underlying error.
type Error struct {
	format   string
	message  []interface{}
	inner    error
	severity Severity
	path     []string
}

func (v *Error) formMessage() string {
	if len(v.format) == 0 {
		return serial.Concat(v.message)
	}
	return fmt.Sprintf(v.format, v.message...)
}

// Error implements error.Error().
func (v *Error) Error() string {
	msg := v.formMessage()
	if v.inner != nil {
		msg += " > " + v.inner.Error()
	}
	if len(v.path) > 0 {
		msg = strings.Join(v.path, "|") + ": " + msg
	}
	return msg
}

// Inner implements hasInnerError.Inner()
func (v *Error) Inner() error {
	if v.inner == nil {
		return nil
	}
	return v.inner
}

func (v *Error) Base(err error) *Error {
	v.inner = err
	return v
}

func (v *Error) atSeverity(s Severity) *Error {
	v.severity = s
	return v
}

func (v *Error) Severity() Severity {
	if v.inner == nil {
		return v.severity
	}

	if s, ok := v.inner.(hasSeverity); ok {
		as := s.Severity()
		if as > v.severity {
			return as
		}
	}

	return v.severity
}

func (v *Error) AtDebug() *Error {
	return v.atSeverity(SeverityDebug)
}

func (v *Error) AtInfo() *Error {
	return v.atSeverity(SeverityInfo)
}

func (v *Error) AtWarning() *Error {
	return v.atSeverity(SeverityWarning)
}

func (v *Error) AtError() *Error {
	return v.atSeverity(SeverityError)
}

func (v *Error) Path(path ...string) *Error {
	v.path = path
	return v
}

// New returns a new error object with message formed from given arguments.
func New(msg ...interface{}) *Error {
	return &Error{
		message:  msg,
		severity: SeverityInfo,
	}
}

func Format(format string, values ...interface{}) *Error {
	return &Error{
		format:   format,
		message:  values,
		severity: SeverityInfo,
	}
}

// Cause returns the root cause of this error.
func Cause(err error) error {
	if err == nil {
		return nil
	}
	for {
		inner, ok := err.(hasInnerError)
		if !ok || inner.Inner() == nil {
			break
		}
		err = inner.Inner()
	}
	return err
}

func GetSeverity(err error) Severity {
	if s, ok := err.(hasSeverity); ok {
		return s.Severity()
	}
	return SeverityInfo
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package cluster

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"math"
	"net"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"

	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach/util/log"
)

// Retrieve the IP address of docker itself.
func dockerIP() net.IP {
	if host := os.Getenv("DOCKER_HOST"); host != "" {
		u, err := url.Parse(host)
		if err != nil {
			panic(err)
		}
		h, _, err := net.SplitHostPort(u.Host)
		if err != nil {
			panic(err)
		}
		return net.ParseIP(h)
	}
	if runtime.GOOS == "linux" {
		return net.IPv4zero
	}
	panic("unable to determine docker ip address")
}

// Container provides the programmatic interface for a single docker
// container.
type Container struct {
	id      string
	name    string
	cluster *LocalCluster
}

// Name returns the container's name.
func (c Container) Name() string {
	return c.name
}

func pullImage(l *LocalCluster, options types.ImagePullOptions) error {
	log.Infof("ImagePull %s:%s starting", options.ImageID, options.Tag)
	defer log.Infof("ImagePull %s:%s complete", options.ImageID, options.Tag)

	rc, err := l.client.ImagePull(context.Background(), options, nil)
	if err != nil {
		return err
	}
	defer rc.Close()
	dec := json.NewDecoder(rc)
	for {
		// Using `interface{}` to avoid dependency on github.com/docker/docker. See
		// https://github.com/docker/engine-api/issues/89.
		var message interface{}
		if err := dec.Decode(&message); err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		// The message is a status bar.
		if log.V(2) {
			log.Infof("ImagePull response: %s", message)
		} else {
			_, _ = fmt.Fprintf(os.Stderr, ".")
		}
	}
}

// createContainer creates a new container using the specified options. Per the
// docker API, the created container is not running and must be started
// explicitly.
func createContainer(l *LocalCluster, containerConfig container.Config, hostConfig container.HostConfig, containerName string) (*Container, error) {
	resp, err := l.client.ContainerCreate(&containerConfig, &hostConfig, nil, containerName)
	if err != nil {
		return nil, err
	}
	return &Container{
		id:      resp.ID,
		name:    containerName,
		cluster: l,
	}, nil
}

func maybePanic(err error) {
	if err != nil {
		panic(err)
	}
}

// Remove removes the container from docker. It is an error to remove a running
// container.
func (c *Container) Remove() error {
	err := c.cluster.client.ContainerRemove(types.ContainerRemoveOptions{
		ContainerID:   c.id,
		RemoveVolumes: true,
	})

	if os.Getenv("CIRCLECI") == "true" {
		// HACK: Removal of docker containers on circleci reports the error:
		// "Driver btrfs failed to remove root filesystem". So if we're running on
		// circleci, just ignore the error, the containers are still removed.
		return nil
	}
	return err
}

// Kill stops a running container, without removing it.
func (c *Container) Kill() error {
	// Paused containers cannot be killed. Attempt to unpause it first
	// (which might fail) before killing.
	_ = c.Unpause()
	if err := c.cluster.client.ContainerKill(c.id, "9"); err != nil && !strings.Contains(err.Error(), "is not running") {
		return err
	}
	c.cluster.expectEvent(c, eventDie)
	return nil
}

// Start starts a non-running container.
//
// TODO(pmattis): Generalize the setting of parameters here.
func (c *Container) Start() error {
	return c.cluster.client.ContainerStart(c.id)
}

// Pause pauses a running container.
func (c *Container) Pause() error {
	return c.cluster.client.ContainerPause(c.id)
}

// Unpause resumes a paused container.
func (c *Container) Unpause() error {
	return c.cluster.client.ContainerUnpause(c.id)
}

// Restart restarts a running container.
// Container will be killed after 'timeout' seconds if it fails to stop.
func (c *Container) Restart(timeoutSeconds int) error {
	var exp []string
	if ci, err := c.Inspect(); err != nil {
		return err
	} else if ci.State.Running {
		exp = append(exp, eventDie)
	}
	if err := c.cluster.client.ContainerRestart(c.id, timeoutSeconds); err != nil {
		return err
	}
	c.cluster.expectEvent(c, append(exp, eventRestart)...)
	return nil
}

// Stop a running container.
func (c *Container) Stop(timeoutSeconds int) error {
	if err := c.cluster.client.ContainerStop(c.id, timeoutSeconds); err != nil {
		return err
	}
	c.cluster.expectEvent(c, eventDie)
	return nil
}

// Wait waits for a running container to exit.
func (c *Container) Wait() error {
	exitCode, err := c.cluster.client.ContainerWait(context.Background(), c.id)
	if err != nil {
		return err
	}
	if exitCode != 0 {
		if err := c.Logs(os.Stderr); err != nil {
			log.Warning(err)
		}
		return fmt.Errorf("non-zero exit code: %d", exitCode)
	}
	return nil
}

// Logs outputs the containers logs to the given io.Writer.
func (c *Container) Logs(w io.Writer) error {
	rc, err := c.cluster.client.ContainerLogs(context.Background(), types.ContainerLogsOptions{
		ContainerID: c.id,
		ShowStdout:  true,
		ShowStderr:  true,
	})
	if err != nil {
		return err
	}
	defer rc.Close()
	// The docker log output is not quite plaintext: each line has a
	// prefix consisting of one byte file descriptor (stdout vs stderr),
	// three bytes padding, four byte length. We could use this to
	// disentangle stdout and stderr if we wanted to output them into
	// separate streams, but we don't really care.
	for {
		var header uint64
		if err := binary.Read(rc, binary.BigEndian, &header); err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		size := header & math.MaxUint32
		if _, err := io.CopyN(w, rc, int64(size)); err != nil {
			return err
		}
	}
	return nil
}

// Inspect retrieves detailed info about a container.
func (c *Container) Inspect() (types.ContainerJSON, error) {
	return c.cluster.client.ContainerInspect(c.id)
}

// Addr returns the TCP address to connect to.
func (c *Container) Addr() *net.TCPAddr {
	containerInfo, err := c.Inspect()
	if err != nil {
		return nil
	}
	bindings, ok := containerInfo.NetworkSettings.Ports[defaultTCP]
	if !ok || len(bindings) == 0 {
		return nil
	}
	port, err := strconv.Atoi(bindings[0].HostPort)
	if err != nil {
		return nil
	}
	return &net.TCPAddr{
		IP:   dockerIP(),
		Port: port,
	}
}
<commit_msg>acceptance: remove containers harder<commit_after>// Copyright 2015 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Peter Mattis (peter@cockroachlabs.com)

package cluster

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"math"
	"net"
	"net/url"
	"os"
	"runtime"
	"strconv"
	"strings"

	"github.com/docker/engine-api/types"
	"github.com/docker/engine-api/types/container"
	"golang.org/x/net/context"

	"github.com/cockroachdb/cockroach/util/log"
)

// Retrieve the IP address of docker itself.
func dockerIP() net.IP {
	if host := os.Getenv("DOCKER_HOST"); host != "" {
		u, err := url.Parse(host)
		if err != nil {
			panic(err)
		}
		h, _, err := net.SplitHostPort(u.Host)
		if err != nil {
			panic(err)
		}
		return net.ParseIP(h)
	}
	if runtime.GOOS == "linux" {
		return net.IPv4zero
	}
	panic("unable to determine docker ip address")
}

// Container provides the programmatic interface for a single docker
// container.
type Container struct {
	id      string
	name    string
	cluster *LocalCluster
}

// Name returns the container's name.
func (c Container) Name() string {
	return c.name
}

func pullImage(l *LocalCluster, options types.ImagePullOptions) error {
	log.Infof("ImagePull %s:%s starting", options.ImageID, options.Tag)
	defer log.Infof("ImagePull %s:%s complete", options.ImageID, options.Tag)

	rc, err := l.client.ImagePull(context.Background(), options, nil)
	if err != nil {
		return err
	}
	defer rc.Close()
	dec := json.NewDecoder(rc)
	for {
		// Using `interface{}` to avoid dependency on github.com/docker/docker. See
		// https://github.com/docker/engine-api/issues/89.
		var message interface{}
		if err := dec.Decode(&message); err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		// The message is a status bar.
		if log.V(2) {
			log.Infof("ImagePull response: %s", message)
		} else {
			_, _ = fmt.Fprintf(os.Stderr, ".")
		}
	}
}

// createContainer creates a new container using the specified options. Per the
// docker API, the created container is not running and must be started
// explicitly.
func createContainer(l *LocalCluster, containerConfig container.Config, hostConfig container.HostConfig, containerName string) (*Container, error) {
	resp, err := l.client.ContainerCreate(&containerConfig, &hostConfig, nil, containerName)
	if err != nil {
		return nil, err
	}
	return &Container{
		id:      resp.ID,
		name:    containerName,
		cluster: l,
	}, nil
}

func maybePanic(err error) {
	if err != nil {
		panic(err)
	}
}

// Remove removes the container from docker. It is an error to remove a running
// container.
func (c *Container) Remove() error {
	err := c.cluster.client.ContainerRemove(types.ContainerRemoveOptions{
		ContainerID:   c.id,
		RemoveVolumes: true,
		Force:         true,
	})

	if os.Getenv("CIRCLECI") == "true" {
		// HACK: Removal of docker containers on circleci reports the error:
		// "Driver btrfs failed to remove root filesystem". So if we're running on
		// circleci, just ignore the error, the containers are still removed.
		return nil
	}
	return err
}

// Kill stops a running container, without removing it.
func (c *Container) Kill() error {
	// Paused containers cannot be killed. Attempt to unpause it first
	// (which might fail) before killing.
	_ = c.Unpause()
	if err := c.cluster.client.ContainerKill(c.id, "9"); err != nil && !strings.Contains(err.Error(), "is not running") {
		return err
	}
	c.cluster.expectEvent(c, eventDie)
	return nil
}

// Start starts a non-running container.
//
// TODO(pmattis): Generalize the setting of parameters here.
func (c *Container) Start() error {
	return c.cluster.client.ContainerStart(c.id)
}

// Pause pauses a running container.
func (c *Container) Pause() error {
	return c.cluster.client.ContainerPause(c.id)
}

// Unpause resumes a paused container.
func (c *Container) Unpause() error {
	return c.cluster.client.ContainerUnpause(c.id)
}

// Restart restarts a running container.
// Container will be killed after 'timeout' seconds if it fails to stop.
func (c *Container) Restart(timeoutSeconds int) error {
	var exp []string
	if ci, err := c.Inspect(); err != nil {
		return err
	} else if ci.State.Running {
		exp = append(exp, eventDie)
	}
	if err := c.cluster.client.ContainerRestart(c.id, timeoutSeconds); err != nil {
		return err
	}
	c.cluster.expectEvent(c, append(exp, eventRestart)...)
	return nil
}

// Stop a running container.
func (c *Container) Stop(timeoutSeconds int) error {
	if err := c.cluster.client.ContainerStop(c.id, timeoutSeconds); err != nil {
		return err
	}
	c.cluster.expectEvent(c, eventDie)
	return nil
}

// Wait waits for a running container to exit.
func (c *Container) Wait() error {
	exitCode, err := c.cluster.client.ContainerWait(context.Background(), c.id)
	if err != nil {
		return err
	}
	if exitCode != 0 {
		if err := c.Logs(os.Stderr); err != nil {
			log.Warning(err)
		}
		return fmt.Errorf("non-zero exit code: %d", exitCode)
	}
	return nil
}

// Logs outputs the containers logs to the given io.Writer.
func (c *Container) Logs(w io.Writer) error {
	rc, err := c.cluster.client.ContainerLogs(context.Background(), types.ContainerLogsOptions{
		ContainerID: c.id,
		ShowStdout:  true,
		ShowStderr:  true,
	})
	if err != nil {
		return err
	}
	defer rc.Close()
	// The docker log output is not quite plaintext: each line has a
	// prefix consisting of one byte file descriptor (stdout vs stderr),
	// three bytes padding, four byte length. We could use this to
	// disentangle stdout and stderr if we wanted to output them into
	// separate streams, but we don't really care.
	for {
		var header uint64
		if err := binary.Read(rc, binary.BigEndian, &header); err == io.EOF {
			break
		} else if err != nil {
			return err
		}
		size := header & math.MaxUint32
		if _, err := io.CopyN(w, rc, int64(size)); err != nil {
			return err
		}
	}
	return nil
}

// Inspect retrieves detailed info about a container.
func (c *Container) Inspect() (types.ContainerJSON, error) {
	return c.cluster.client.ContainerInspect(c.id)
}

// Addr returns the TCP address to connect to.
func (c *Container) Addr() *net.TCPAddr {
	containerInfo, err := c.Inspect()
	if err != nil {
		return nil
	}
	bindings, ok := containerInfo.NetworkSettings.Ports[defaultTCP]
	if !ok || len(bindings) == 0 {
		return nil
	}
	port, err := strconv.Atoi(bindings[0].HostPort)
	if err != nil {
		return nil
	}
	return &net.TCPAddr{
		IP:   dockerIP(),
		Port: port,
	}
}
<|endoftext|>


<commit_before>// +build ignore

// Run tests for all the remotes
//
// Run with go run test_all.go
package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"time"

	"github.com/ncw/rclone/fs"
	_ "github.com/ncw/rclone/fs/all" // import all fs
	"github.com/ncw/rclone/fstest"
)

type remoteConfig struct {
	Name     string
	SubDir   bool
	FastList bool
}

var (
	remotes = []remoteConfig{
		{
			Name:     "TestAmazonCloudDrive:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestB2:",
			SubDir:   true,
			FastList: true,
		},
		{
			Name:     "TestCryptDrive:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestCryptSwift:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestDrive:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestDropbox:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestGoogleCloudStorage:",
			SubDir:   true,
			FastList: true,
		},
		{
			Name:     "TestHubic:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestOneDrive:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestS3:",
			SubDir:   true,
			FastList: true,
		},
		{
			Name:     "TestSftp:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestSwift:",
			SubDir:   true,
			FastList: true,
		},
		{
			Name:     "TestYandex:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestFTP:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestBox:",
			SubDir:   false,
			FastList: false,
		},
	}
	binary = "fs.test"
	// Flags
	maxTries = flag.Int("maxtries", 5, "Number of times to try each test")
	runTests = flag.String("remotes", "", "Comma separated list of remotes to test, eg 'TestSwift:,TestS3'")
	verbose  = flag.Bool("verbose", false, "Run the tests with -v")
	clean    = flag.Bool("clean", false, "Instead of testing, clean all left over test directories")
	runOnly  = flag.String("run-only", "", "Run only those tests matching the regexp supplied")
)

// test holds info about a running test
type test struct {
	remote      string
	subdir      bool
	cmdLine     []string
	cmdString   string
	try         int
	err         error
	output      []byte
	failedTests []string
	runFlag     string
}

// newTest creates a new test
func newTest(remote string, subdir bool, fastlist bool) *test {
	t := &test{
		remote:  remote,
		subdir:  subdir,
		cmdLine: []string{"./" + binary, "-remote", remote},
		try:     1,
	}
	if *verbose {
		t.cmdLine = append(t.cmdLine, "-test.v")
		fs.Config.LogLevel = fs.LogLevelDebug
	}
	if *runOnly != "" {
		t.cmdLine = append(t.cmdLine, "-test.run", *runOnly)
	}
	if subdir {
		t.cmdLine = append(t.cmdLine, "-subdir")
	}
	if fastlist {
		t.cmdLine = append(t.cmdLine, "-fast-list")
	}
	t.cmdString = toShell(t.cmdLine)
	return t
}

// dumpOutput prints the error output
func (t *test) dumpOutput() {
	log.Println("------------------------------------------------------------")
	log.Printf("---- %q ----", t.cmdString)
	log.Println(string(t.output))
	log.Println("------------------------------------------------------------")
}

var failRe = regexp.MustCompile(`(?m)^--- FAIL: (Test\w*) \(`)

// findFailures looks for all the tests which failed
func (t *test) findFailures() {
	oldFailedTests := t.failedTests
	t.failedTests = nil
	for _, matches := range failRe.FindAllSubmatch(t.output, -1) {
		t.failedTests = append(t.failedTests, string(matches[1]))
	}
	if len(t.failedTests) != 0 {
		t.runFlag = "^(" + strings.Join(t.failedTests, "|") + ")$"
	} else {
		t.runFlag = ""
	}
	if t.passed() && len(t.failedTests) != 0 {
		log.Printf("%q - Expecting no errors but got: %v", t.cmdString, t.failedTests)
		t.dumpOutput()
	} else if !t.passed() && len(t.failedTests) == 0 {
		log.Printf("%q - Expecting errors but got none: %v", t.cmdString, t.failedTests)
		t.dumpOutput()
		t.failedTests = oldFailedTests
	}
}

// nextCmdLine returns the next command line
func (t *test) nextCmdLine() []string {
	cmdLine := t.cmdLine[:]
	if t.runFlag != "" {
		cmdLine = append(cmdLine, "-test.run", t.runFlag)
	}
	return cmdLine
}

// if matches then is definitely OK in the shell
var shellOK = regexp.MustCompile("^[A-Za-z0-9./_:-]+$")

// converts a argv style input into a shell command
func toShell(args []string) (result string) {
	for _, arg := range args {
		if result != "" {
			result += " "
		}
		if shellOK.MatchString(arg) {
			result += arg
		} else {
			result += "'" + arg + "'"
		}
	}
	return result
}

// trial runs a single test
func (t *test) trial() {
	cmdLine := t.nextCmdLine()
	cmdString := toShell(cmdLine)
	log.Printf("%q - Starting (try %d/%d)", cmdString, t.try, *maxTries)
	cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
	start := time.Now()
	t.output, t.err = cmd.CombinedOutput()
	duration := time.Since(start)
	t.findFailures()
	if t.passed() {
		log.Printf("%q - Finished OK in %v (try %d/%d)", cmdString, duration, t.try, *maxTries)
	} else {
		log.Printf("%q - Finished ERROR in %v (try %d/%d): %v: Failed %v", cmdString, duration, t.try, *maxTries, t.err, t.failedTests)
	}
}

// cleanFs runs a single clean fs for left over directories
func (t *test) cleanFs() error {
	f, err := fs.NewFs(t.remote)
	if err != nil {
		return err
	}
	entries, err := fs.ListDirSorted(f, true, "")
	if err != nil {
		return err
	}
	return entries.ForDirError(func(dir fs.Directory) error {
		remote := dir.Remote()
		if fstest.MatchTestRemote.MatchString(remote) {
			log.Printf("Purging %s%s", t.remote, remote)
			dir, err := fs.NewFs(t.remote + remote)
			if err != nil {
				return err
			}
			return fs.Purge(dir)
		}
		return nil
	})
}

// clean runs a single clean on a fs for left over directories
func (t *test) clean() {
	log.Printf("%q - Starting clean (try %d/%d)", t.remote, t.try, *maxTries)
	start := time.Now()
	t.err = t.cleanFs()
	if t.err != nil {
		log.Printf("%q - Failed to purge %v", t.remote, t.err)
	}
	duration := time.Since(start)
	if t.passed() {
		log.Printf("%q - Finished OK in %v (try %d/%d)", t.cmdString, duration, t.try, *maxTries)
	} else {
		log.Printf("%q - Finished ERROR in %v (try %d/%d): %v", t.cmdString, duration, t.try, *maxTries, t.err)
	}
}

// passed returns true if the test passed
func (t *test) passed() bool {
	return t.err == nil
}

// run runs all the trials for this test
func (t *test) run(result chan<- *test) {
	for t.try = 1; t.try <= *maxTries; t.try++ {
		if *clean {
			if !t.subdir {
				t.clean()
			}
		} else {
			t.trial()
		}
		if t.passed() {
			break
		}
	}
	if !t.passed() {
		t.dumpOutput()
	}
	result <- t
}

// makeTestBinary makes the binary we will run
func makeTestBinary() {
	if runtime.GOOS == "windows" {
		binary += ".exe"
	}
	log.Printf("Making test binary %q", binary)
	err := exec.Command("go", "test", "-c", "-o", binary).Run()
	if err != nil {
		log.Fatalf("Failed to make test binary: %v", err)
	}
	if _, err := os.Stat(binary); err != nil {
		log.Fatalf("Couldn't find test binary %q", binary)
	}
}

// removeTestBinary removes the binary made in makeTestBinary
func removeTestBinary() {
	err := os.Remove(binary) // Delete the binary when finished
	if err != nil {
		log.Printf("Error removing test binary %q: %v", binary, err)
	}
}

func main() {
	flag.Parse()
	if *runTests != "" {
		newRemotes := []remoteConfig{}
		for _, name := range strings.Split(*runTests, ",") {
			for i := range remotes {
				if remotes[i].Name == name {
					newRemotes = append(newRemotes, remotes[i])
					goto found
				}
			}
			log.Printf("Remote %q not found - inserting with default flags", name)
			newRemotes = append(newRemotes, remoteConfig{Name: name})
		found:
		}
		remotes = newRemotes
	}
	var names []string
	for _, remote := range remotes {
		names = append(names, remote.Name)
	}
	log.Printf("Testing remotes: %s", strings.Join(names, ", "))

	start := time.Now()
	if *clean {
		fs.LoadConfig()
	} else {
		makeTestBinary()
		defer removeTestBinary()
	}

	// start the tests
	results := make(chan *test, 8)
	awaiting := 0
	for _, remote := range remotes {
		for _, subdir := range []bool{false, true} {
			for _, fastlist := range []bool{false, true} {
				if (!subdir || subdir && remote.SubDir) && (!fastlist || fastlist && remote.FastList) {
					go newTest(remote.Name, subdir, fastlist).run(results)
					awaiting++
				}
			}
		}
	}

	// Wait for the tests to finish
	var failed []*test
	for ; awaiting > 0; awaiting-- {
		t := <-results
		if !t.passed() {
			failed = append(failed, t)
		}
	}
	duration := time.Since(start)

	// Summarise results
	if len(failed) == 0 {
		log.Printf("PASS: All tests finished OK in %v", duration)
	} else {
		log.Printf("FAIL: %d tests failed in %v", len(failed), duration)
		for _, t := range failed {
			log.Printf("  * %s", toShell(t.nextCmdLine()))
			log.Printf("    * Failed tests: %v", t.failedTests)
		}
		os.Exit(1)
	}
}
<commit_msg>fs/test_all: fix after fstest factorisation<commit_after>// +build ignore

// Run tests for all the remotes
//
// Run with go run test_all.go
package main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strings"
	"time"

	"github.com/ncw/rclone/fs"
	_ "github.com/ncw/rclone/fs/all" // import all fs
	"github.com/ncw/rclone/fstest"
)

type remoteConfig struct {
	Name     string
	SubDir   bool
	FastList bool
}

var (
	remotes = []remoteConfig{
		{
			Name:     "TestAmazonCloudDrive:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestB2:",
			SubDir:   true,
			FastList: true,
		},
		{
			Name:     "TestCryptDrive:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestCryptSwift:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestDrive:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestDropbox:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestGoogleCloudStorage:",
			SubDir:   true,
			FastList: true,
		},
		{
			Name:     "TestHubic:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestOneDrive:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestS3:",
			SubDir:   true,
			FastList: true,
		},
		{
			Name:     "TestSftp:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestSwift:",
			SubDir:   true,
			FastList: true,
		},
		{
			Name:     "TestYandex:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestFTP:",
			SubDir:   false,
			FastList: false,
		},
		{
			Name:     "TestBox:",
			SubDir:   false,
			FastList: false,
		},
	}
	binary = "fs.test"
	// Flags
	maxTries = flag.Int("maxtries", 5, "Number of times to try each test")
	runTests = flag.String("remotes", "", "Comma separated list of remotes to test, eg 'TestSwift:,TestS3'")
	clean    = flag.Bool("clean", false, "Instead of testing, clean all left over test directories")
	runOnly  = flag.String("run-only", "", "Run only those tests matching the regexp supplied")
)

// test holds info about a running test
type test struct {
	remote      string
	subdir      bool
	cmdLine     []string
	cmdString   string
	try         int
	err         error
	output      []byte
	failedTests []string
	runFlag     string
}

// newTest creates a new test
func newTest(remote string, subdir bool, fastlist bool) *test {
	t := &test{
		remote:  remote,
		subdir:  subdir,
		cmdLine: []string{"./" + binary, "-remote", remote},
		try:     1,
	}
	if *fstest.Verbose {
		t.cmdLine = append(t.cmdLine, "-test.v")
		fs.Config.LogLevel = fs.LogLevelDebug
	}
	if *runOnly != "" {
		t.cmdLine = append(t.cmdLine, "-test.run", *runOnly)
	}
	if subdir {
		t.cmdLine = append(t.cmdLine, "-subdir")
	}
	if fastlist {
		t.cmdLine = append(t.cmdLine, "-fast-list")
	}
	t.cmdString = toShell(t.cmdLine)
	return t
}

// dumpOutput prints the error output
func (t *test) dumpOutput() {
	log.Println("------------------------------------------------------------")
	log.Printf("---- %q ----", t.cmdString)
	log.Println(string(t.output))
	log.Println("------------------------------------------------------------")
}

var failRe = regexp.MustCompile(`(?m)^--- FAIL: (Test\w*) \(`)

// findFailures looks for all the tests which failed
func (t *test) findFailures() {
	oldFailedTests := t.failedTests
	t.failedTests = nil
	for _, matches := range failRe.FindAllSubmatch(t.output, -1) {
		t.failedTests = append(t.failedTests, string(matches[1]))
	}
	if len(t.failedTests) != 0 {
		t.runFlag = "^(" + strings.Join(t.failedTests, "|") + ")$"
	} else {
		t.runFlag = ""
	}
	if t.passed() && len(t.failedTests) != 0 {
		log.Printf("%q - Expecting no errors but got: %v", t.cmdString, t.failedTests)
		t.dumpOutput()
	} else if !t.passed() && len(t.failedTests) == 0 {
		log.Printf("%q - Expecting errors but got none: %v", t.cmdString, t.failedTests)
		t.dumpOutput()
		t.failedTests = oldFailedTests
	}
}

// nextCmdLine returns the next command line
func (t *test) nextCmdLine() []string {
	cmdLine := t.cmdLine[:]
	if t.runFlag != "" {
		cmdLine = append(cmdLine, "-test.run", t.runFlag)
	}
	return cmdLine
}

// if matches then is definitely OK in the shell
var shellOK = regexp.MustCompile("^[A-Za-z0-9./_:-]+$")

// converts a argv style input into a shell command
func toShell(args []string) (result string) {
	for _, arg := range args {
		if result != "" {
			result += " "
		}
		if shellOK.MatchString(arg) {
			result += arg
		} else {
			result += "'" + arg + "'"
		}
	}
	return result
}

// trial runs a single test
func (t *test) trial() {
	cmdLine := t.nextCmdLine()
	cmdString := toShell(cmdLine)
	log.Printf("%q - Starting (try %d/%d)", cmdString, t.try, *maxTries)
	cmd := exec.Command(cmdLine[0], cmdLine[1:]...)
	start := time.Now()
	t.output, t.err = cmd.CombinedOutput()
	duration := time.Since(start)
	t.findFailures()
	if t.passed() {
		log.Printf("%q - Finished OK in %v (try %d/%d)", cmdString, duration, t.try, *maxTries)
	} else {
		log.Printf("%q - Finished ERROR in %v (try %d/%d): %v: Failed %v", cmdString, duration, t.try, *maxTries, t.err, t.failedTests)
	}
}

// cleanFs runs a single clean fs for left over directories
func (t *test) cleanFs() error {
	f, err := fs.NewFs(t.remote)
	if err != nil {
		return err
	}
	entries, err := fs.ListDirSorted(f, true, "")
	if err != nil {
		return err
	}
	return entries.ForDirError(func(dir fs.Directory) error {
		remote := dir.Remote()
		if fstest.MatchTestRemote.MatchString(remote) {
			log.Printf("Purging %s%s", t.remote, remote)
			dir, err := fs.NewFs(t.remote + remote)
			if err != nil {
				return err
			}
			return fs.Purge(dir)
		}
		return nil
	})
}

// clean runs a single clean on a fs for left over directories
func (t *test) clean() {
	log.Printf("%q - Starting clean (try %d/%d)", t.remote, t.try, *maxTries)
	start := time.Now()
	t.err = t.cleanFs()
	if t.err != nil {
		log.Printf("%q - Failed to purge %v", t.remote, t.err)
	}
	duration := time.Since(start)
	if t.passed() {
		log.Printf("%q - Finished OK in %v (try %d/%d)", t.cmdString, duration, t.try, *maxTries)
	} else {
		log.Printf("%q - Finished ERROR in %v (try %d/%d): %v", t.cmdString, duration, t.try, *maxTries, t.err)
	}
}

// passed returns true if the test passed
func (t *test) passed() bool {
	return t.err == nil
}

// run runs all the trials for this test
func (t *test) run(result chan<- *test) {
	for t.try = 1; t.try <= *maxTries; t.try++ {
		if *clean {
			if !t.subdir {
				t.clean()
			}
		} else {
			t.trial()
		}
		if t.passed() {
			break
		}
	}
	if !t.passed() {
		t.dumpOutput()
	}
	result <- t
}

// makeTestBinary makes the binary we will run
func makeTestBinary() {
	if runtime.GOOS == "windows" {
		binary += ".exe"
	}
	log.Printf("Making test binary %q", binary)
	err := exec.Command("go", "test", "-c", "-o", binary).Run()
	if err != nil {
		log.Fatalf("Failed to make test binary: %v", err)
	}
	if _, err := os.Stat(binary); err != nil {
		log.Fatalf("Couldn't find test binary %q", binary)
	}
}

// removeTestBinary removes the binary made in makeTestBinary
func removeTestBinary() {
	err := os.Remove(binary) // Delete the binary when finished
	if err != nil {
		log.Printf("Error removing test binary %q: %v", binary, err)
	}
}

func main() {
	flag.Parse()
	if *runTests != "" {
		newRemotes := []remoteConfig{}
		for _, name := range strings.Split(*runTests, ",") {
			for i := range remotes {
				if remotes[i].Name == name {
					newRemotes = append(newRemotes, remotes[i])
					goto found
				}
			}
			log.Printf("Remote %q not found - inserting with default flags", name)
			newRemotes = append(newRemotes, remoteConfig{Name: name})
		found:
		}
		remotes = newRemotes
	}
	var names []string
	for _, remote := range remotes {
		names = append(names, remote.Name)
	}
	log.Printf("Testing remotes: %s", strings.Join(names, ", "))

	start := time.Now()
	if *clean {
		fs.LoadConfig()
	} else {
		makeTestBinary()
		defer removeTestBinary()
	}

	// start the tests
	results := make(chan *test, 8)
	awaiting := 0
	for _, remote := range remotes {
		for _, subdir := range []bool{false, true} {
			for _, fastlist := range []bool{false, true} {
				if (!subdir || subdir && remote.SubDir) && (!fastlist || fastlist && remote.FastList) {
					go newTest(remote.Name, subdir, fastlist).run(results)
					awaiting++
				}
			}
		}
	}

	// Wait for the tests to finish
	var failed []*test
	for ; awaiting > 0; awaiting-- {
		t := <-results
		if !t.passed() {
			failed = append(failed, t)
		}
	}
	duration := time.Since(start)

	// Summarise results
	if len(failed) == 0 {
		log.Printf("PASS: All tests finished OK in %v", duration)
	} else {
		log.Printf("FAIL: %d tests failed in %v", len(failed), duration)
		for _, t := range failed {
			log.Printf("  * %s", toShell(t.nextCmdLine()))
			log.Printf("    * Failed tests: %v", t.failedTests)
		}
		os.Exit(1)
	}
}
<|endoftext|>