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

package stanza

import (
	// Register parsers and transformers for stanza-based log receivers
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/json"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/regex"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/severity"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/time"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/trace"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/metadata"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/recombine"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/restructure"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/router"
)
<commit_msg>Add copy operator to internal/stanza used by filelogreceiver (#4188)<commit_after>// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package stanza

import (
	// Register parsers and transformers for stanza-based log receivers
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/json"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/regex"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/severity"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/time"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/parser/trace"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/copy"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/metadata"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/recombine"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/restructure"
	_ "github.com/open-telemetry/opentelemetry-log-collection/operator/builtin/transformer/router"
)
<|endoftext|>


<commit_before>package trafo

import (
	. "github.com/101loops/bdd"
	"github.com/101loops/hrd/internal/fixture"
	"github.com/101loops/hrd/internal/types"
)

var _ = Describe("DocList", func() {

	var (
		kind     *types.Kind
		entities []*fixture.EntityWithNumID
	)

	type UnknownModel struct{}
	type InvalidModel struct{}

	BeforeEach(func() {
		CodecSet.AddMust(&InvalidModel{})
		kind = types.NewKind(ctx, "my-kind")

		entities = make([]*fixture.EntityWithNumID, 4)
		for i := int64(0); i < 4; i++ {
			entity := &fixture.EntityWithNumID{}
			entity.SetID(i + 1)
			entities[i] = entity
		}
	})

	Context("create readable list", func() {

		It("from struct pointer", func() {
			list, err := NewReadableDocList(kind, entities[0])
			Check(err, IsNil)
			Check(list, NotNil)
			Check(list.list, HasLen, 1)
		})

		It("from slice of struct pointers", func() {
			list, err := NewReadableDocList(kind, entities[0:2])
			Check(err, IsNil)
			Check(list, NotNil)
			Check(list.list, HasLen, 2)
		})

		It("but not from nil value", func() {
			list, err := NewReadableDocList(kind, nil)
			Check(list, IsNil)
			Check(err, NotNil).And(Contains, "value must be non-nil")
		})

		It("but not from unknown struct pointer", func() {
			list, err := NewReadableDocList(kind, &UnknownModel{})
			Check(list, IsNil)
			Check(err, NotNil).And(Contains, "no registered codec found for type 'trafo.UnknownModel'")
		})

		It("but not from invalid entity pointer", func() {
			list, err := NewReadableDocList(kind, &InvalidModel{})
			Check(list, IsNil)
			Check(err, NotNil).And(Contains, `value type "*trafo.InvalidModel" does not provide ID()`)
		})
	})
})
<commit_msg>add more tests<commit_after>package trafo

import (
	. "github.com/101loops/bdd"
	"github.com/101loops/hrd/internal/fixture"
	"github.com/101loops/hrd/internal/types"

	ds "appengine/datastore"
)

var _ = Describe("DocList", func() {

	var (
		kind     *types.Kind
		keys     []*types.Key
		entities []*fixture.EntityWithNumID
	)

	type UnknownModel struct{}
	type InvalidModel struct{}

	BeforeEach(func() {
		CodecSet.AddMust(&InvalidModel{})
		kind = types.NewKind(ctx, "my-kind")

		keys = make([]*types.Key, 4)
		entities = make([]*fixture.EntityWithNumID, 4)
		for i := int64(0); i < 4; i++ {
			entity := &fixture.EntityWithNumID{}
			entity.SetID(i + 1)
			entities[i] = entity
			keys[i] = types.NewKey(ds.NewKey(ctx, kind.Name, "", entity.ID(), nil))
		}
	})

	Context("create readable list", func() {

		It("from struct pointer", func() {
			list, err := NewReadableDocList(kind, entities[0])
			Check(err, IsNil)
			Check(list, NotNil)
			Check(list.list, HasLen, 1)
		})

		It("from slice of struct pointers", func() {
			list, err := NewReadableDocList(kind, entities[0:2])
			Check(err, IsNil)
			Check(list, NotNil)
			Check(list.list, HasLen, 2)
		})

		It("but not from nil value", func() {
			list, err := NewReadableDocList(kind, nil)
			Check(list, IsNil)
			Check(err, NotNil).And(Contains, "value must be non-nil")
		})

		It("but not from unknown struct pointer", func() {
			list, err := NewReadableDocList(kind, &UnknownModel{})
			Check(list, IsNil)
			Check(err, NotNil).And(Contains, "no registered codec found for type 'trafo.UnknownModel'")
		})

		It("but not from invalid entity pointer", func() {
			list, err := NewReadableDocList(kind, &InvalidModel{})
			Check(list, IsNil)
			Check(err, NotNil).And(Contains, `value type "*trafo.InvalidModel" does not provide ID()`)
		})
	})

	Context("create writeable list", func() {

		It("from struct pointer", func() {
			list, err := NewWriteableDocList(&(entities[0]), keys[0:1], false)
			Check(err, IsNil)
			Check(list, NotNil)
			Check(list.list, HasLen, 1)
		})

		It("from slice", func() {
			var entitySlice []*fixture.EntityWithNumID
			list, err := NewWriteableDocList(&entitySlice, keys, true)

			Check(err, IsNil)
			Check(list, NotNil)
			Check(list.list, HasLen, 4)
		})

		It("from map", func() {
			var entityMap map[string]*fixture.EntityWithNumID
			list, err := NewWriteableDocList(&entityMap, keys, true)

			Check(err, IsNil)
			Check(list, NotNil)
			Check(list.list, HasLen, 4)
		})

		It("from map", func() {
			var entityMap map[string]*fixture.EntityWithNumID
			list, err := NewWriteableDocList(&entityMap, keys, true)

			Check(err, IsNil)
			Check(list, NotNil)
			Check(list.list, HasLen, 4)
		})

		It("but not from non-pointer", func() {
			list, err := NewWriteableDocList(*(entities[0]), keys[0:1], false)
			Check(list, IsNil)
			Check(err, NotNil).And(Contains, `invalid value kind "struct" (wanted non-nil pointer)`)
		})

		It("but not from nil pointer", func() {
			var entity *fixture.EntityWithNumID
			list, err := NewWriteableDocList(entity, keys[0:1], false)
			Check(list, IsNil)
			Check(err, NotNil).And(Contains, `invalid value kind "ptr" (wanted non-nil pointer)`)
		})
	})
})
<|endoftext|>


<commit_before>package tcp

import (
	"bufio"
	"net"
	"sync"
	"time"

	log "github.com/cihub/seelog"
	"github.com/pkg/errors"
	"github.com/schollz/croc/src/comm"
	"github.com/schollz/croc/src/logger"
	"github.com/schollz/croc/src/models"
)

type roomInfo struct {
	receiver *comm.Comm
	opened   time.Time
}

type roomMap struct {
	rooms map[string]roomInfo
	sync.Mutex
}

var rooms roomMap

// Run starts a tcp listener, run async
func Run(debugLevel, port string) {
	logger.SetLogLevel(debugLevel)
	rooms.Lock()
	rooms.rooms = make(map[string]roomInfo)
	rooms.Unlock()
	err := run(port)
	if err != nil {
		log.Error(err)
	}

	// TODO:
	// delete old rooms
}

func run(port string) (err error) {
	log.Debugf("starting TCP server on " + port)
	rAddr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+port)
	if err != nil {
		panic(err)
	}
	server, err := net.ListenTCP("tcp", rAddr)
	if err != nil {
		return errors.Wrap(err, "Error listening on :"+port)
	}
	defer server.Close()
	// spawn a new goroutine whenever a client connects
	for {
		connection, err := server.Accept()
		if err != nil {
			return errors.Wrap(err, "problem accepting connection")
		}
		log.Debugf("client %s connected", connection.RemoteAddr().String())
		go func(port string, connection net.Conn) {
			errCommunication := clientCommuncation(port, comm.New(connection))
			if errCommunication != nil {
				log.Warnf("relay-%s: %s", connection.RemoteAddr().String(), errCommunication.Error())
			}
		}(port, connection)
	}
}

func clientCommuncation(port string, c *comm.Comm) (err error) {
	// send ok to tell client they are connected
	err = c.Send("ok")
	if err != nil {
		return
	}

	// wait for client to tell me which room they want
	room, err := c.Receive()
	if err != nil {
		return
	}

	rooms.Lock()
	// first connection is always the receiver
	if _, ok := rooms.rooms[room]; !ok {
		rooms.rooms[room] = roomInfo{
			receiver: c,
			opened:   time.Now(),
		}
		rooms.Unlock()
		// tell the client that they got the room
		err = c.Send("recipient")
		if err != nil {
			return
		}
		return nil
	}
	receiver := rooms.rooms[room].receiver
	rooms.Unlock()

	// second connection is the sender, time to staple connections
	var wg sync.WaitGroup
	wg.Add(1)

	// start piping
	go func(com1, com2 *comm.Comm, wg *sync.WaitGroup) {
		log.Debug("starting pipes")
		pipe(com1.Connection(), com2.Connection())
		wg.Done()
		log.Debug("done piping")
	}(c, receiver, &wg)

	// tell the sender everything is ready
	err = c.Send("sender")
	if err != nil {
		return
	}
	wg.Wait()

	// delete room
	rooms.Lock()
	log.Debugf("deleting room: %s", room)
	delete(rooms.rooms, room)
	rooms.Unlock()
	return nil
}

// chanFromConn creates a channel from a Conn object, and sends everything it
//  Read()s from the socket to the channel.
func chanFromConn(conn net.Conn) chan []byte {
	c := make(chan []byte)
	reader := bufio.NewReader(conn)

	go func() {
		for {
			b := make([]byte, models.TCP_BUFFER_SIZE)
			n, err := reader.Read(b)
			if n > 0 {
				// c <- b[:n]
				res := make([]byte, n)
				// Copy the buffer so it doesn't get changed while read by the recipient.
				copy(res, b[:n])
				c <- res
			}
			if err != nil {
				c <- nil
				break
			}
		}
	}()

	return c
}

// pipe creates a full-duplex pipe between the two sockets and
// transfers data from one to the other.
func pipe(conn1 net.Conn, conn2 net.Conn) {
	chan1 := chanFromConn(conn1)
	// chan2 := chanFromConn(conn2)
	// writer1 := bufio.NewWriter(conn1)
	writer2 := bufio.NewWriter(conn2)

	for {
		select {
		case b1 := <-chan1:
			if b1 == nil {
				return
			}
			writer2.Write(b1)
			writer2.Flush()
		default:

			// case b2 := <-chan2:
			// 	if b2 == nil {
			// 		return
			// 	}
			// 	writer1.Write(b2)
			// 	writer1.Flush()
			// }
		}
	}
}
<commit_msg>try fix<commit_after>package tcp

import (
	"bufio"
	"net"
	"sync"
	"time"

	log "github.com/cihub/seelog"
	"github.com/pkg/errors"
	"github.com/schollz/croc/src/comm"
	"github.com/schollz/croc/src/logger"
	"github.com/schollz/croc/src/models"
)

type roomInfo struct {
	receiver *comm.Comm
	opened   time.Time
}

type roomMap struct {
	rooms map[string]roomInfo
	sync.Mutex
}

var rooms roomMap

// Run starts a tcp listener, run async
func Run(debugLevel, port string) {
	logger.SetLogLevel(debugLevel)
	rooms.Lock()
	rooms.rooms = make(map[string]roomInfo)
	rooms.Unlock()
	err := run(port)
	if err != nil {
		log.Error(err)
	}

	// TODO:
	// delete old rooms
}

func run(port string) (err error) {
	log.Debugf("starting TCP server on " + port)
	rAddr, err := net.ResolveTCPAddr("tcp", "0.0.0.0:"+port)
	if err != nil {
		panic(err)
	}
	server, err := net.ListenTCP("tcp", rAddr)
	if err != nil {
		return errors.Wrap(err, "Error listening on :"+port)
	}
	defer server.Close()
	// spawn a new goroutine whenever a client connects
	for {
		connection, err := server.Accept()
		if err != nil {
			return errors.Wrap(err, "problem accepting connection")
		}
		log.Debugf("client %s connected", connection.RemoteAddr().String())
		go func(port string, connection net.Conn) {
			errCommunication := clientCommuncation(port, comm.New(connection))
			if errCommunication != nil {
				log.Warnf("relay-%s: %s", connection.RemoteAddr().String(), errCommunication.Error())
			}
		}(port, connection)
	}
}

func clientCommuncation(port string, c *comm.Comm) (err error) {
	// send ok to tell client they are connected
	err = c.Send("ok")
	if err != nil {
		return
	}

	// wait for client to tell me which room they want
	room, err := c.Receive()
	if err != nil {
		return
	}

	rooms.Lock()
	// first connection is always the receiver
	if _, ok := rooms.rooms[room]; !ok {
		rooms.rooms[room] = roomInfo{
			receiver: c,
			opened:   time.Now(),
		}
		rooms.Unlock()
		// tell the client that they got the room
		err = c.Send("recipient")
		if err != nil {
			return
		}
		return nil
	}
	receiver := rooms.rooms[room].receiver
	rooms.Unlock()

	// second connection is the sender, time to staple connections
	var wg sync.WaitGroup
	wg.Add(1)

	// start piping
	go func(com1, com2 *comm.Comm, wg *sync.WaitGroup) {
		log.Debug("starting pipes")
		pipe(com1.Connection(), com2.Connection())
		wg.Done()
		log.Debug("done piping")
	}(c, receiver, &wg)

	// tell the sender everything is ready
	err = c.Send("sender")
	if err != nil {
		return
	}
	wg.Wait()

	// delete room
	rooms.Lock()
	log.Debugf("deleting room: %s", room)
	delete(rooms.rooms, room)
	rooms.Unlock()
	return nil
}

// chanFromConn creates a channel from a Conn object, and sends everything it
//  Read()s from the socket to the channel.
func chanFromConn(conn net.Conn) chan []byte {
	c := make(chan []byte)
	reader := bufio.NewReader(conn)

	go func() {
		for {
			b := make([]byte, models.TCP_BUFFER_SIZE)
			n, err := reader.Read(b)
			if n > 0 {
				// c <- b[:n]
				res := make([]byte, n)
				// Copy the buffer so it doesn't get changed while read by the recipient.
				copy(res, b[:n])
				c <- res
			}
			if err != nil {
				c <- nil
				break
			}
		}
	}()

	return c
}

// pipe creates a full-duplex pipe between the two sockets and
// transfers data from one to the other.
func pipe(conn1 net.Conn, conn2 net.Conn) {
	chan1 := chanFromConn(conn1)
	// chan2 := chanFromConn(conn2)
	// writer1 := bufio.NewWriter(conn1)
	writer2 := bufio.NewWriter(conn2)

	for {
		 b1 := <-chan1:
			if b1 == nil {
				return
			}
			writer2.Write(b1)
			writer2.Flush()

			// case b2 := <-chan2:
			// 	if b2 == nil {
			// 		return
			// 	}
			// 	writer1.Write(b2)
			// 	writer1.Flush()
			// }
		}
	}
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013 Dave Collins <dave@davec.name>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
Package spew implements a deep pretty printer for Go data structures to aid in
debugging.

A quick overview of the additional features spew provides over the built-in
printing facilities for Go data types are as follows:

	* Pointers are dereferenced and followed
	* Circular data structures are detected and handled properly
	* Custom Stringer/error interfaces are optionally invoked, including
	  on unexported types
	* Custom types which only implement the Stringer/error interfaces via
	  a pointer receiver are optionally invoked when passing non-pointer
	  variables

There are two different approaches spew allows for dumping Go data structures:

	* Dump style which prints with newlines, customizable indentation,
	  and additional debug information such as types and all pointer addresses
	  used to indirect to the final value
	* A custom Formatter interface that integrates cleanly with the standard fmt
	  package and replaces %v and %+v to provide inline printing similar
	  to the default %v while providing the additional functionality outlined
	  above and passing unsupported format verb/flag combinations such a %x,
	  %q, and %#v along to fmt

Quick Start

This section demonstrates how to quickly get started with spew.  See the
sections below for further details on formatting and configuration options.

To dump a variable with full newlines, indentation, type, and pointer
information use Dump or Fdump:
	spew.Dump(myVar1, myVar2, ...)
	spew.Fdump(someWriter, myVar1, myVar2, ...)

Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
%#+v (adds types and pointer addresses):
	spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
	spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
	spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
	spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)

Configuration Options

Configuration of spew is handled by fields in the ConfigState type.  For
convenience, all of the top-level functions use a global state available
via the spew.Config global.

It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions.  This allows concurrent configuration
options.  See the ConfigState documentation for more details.

The following configuration options are available:
	* MaxDepth
		Maximum number of levels to descend into nested data structures.
		There is no limit by default.

	* Indent
		String to use for each indentation level for Dump functions.
		It is a single space by default.  A popular alternative is "\t".

	* DisableMethods
		Disables invocation of error and Stringer interface methods.
		Method invocation is enabled by default.

	* DisablePointerMethods
		Disables invocation of error and Stringer interface methods on types
		which only accept pointer receivers from non-pointer variables.
		Pointer method invocation is enabled by default.

Dump Usage

Simply call spew.Dump with a list of variables you want to dump:

	spew.Dump(myVar1, myVar2, ...)

You may also call spew.Fdump if you would prefer to output to an arbitrary
io.Writer.  For example, to dump to standard error:

	spew.Fdump(os.Stderr, myVar1, myVar2, ...)

Sample Dump Output

See the Dump example for details on the setup of the types and variables being
shown here.

	(main.Foo) {
	 unexportedField: (*main.Bar)(0xf84002e210)({
	  flag: (main.Flag) flagTwo,
	  data: (uintptr) <nil>
	 }),
	 ExportedField: (map[interface {}]interface {}) {
	  (string) "one": (bool) true
	 }
	}

Custom Formatter

Spew provides a custom formatter the implements the fmt.Formatter interface
so that it integrates cleanly with standard fmt package printing functions. The
formatter is useful for inline printing of smaller data types similar to the
standard %v format specifier.

The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations.  Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting.  In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).

Custom Formatter Usage

The simplest way to make use of the spew custom formatter is to call one of the
convenience functions such as spew.Printf, spew.Println, or spew.Printf.  The
functions have syntax you are most likely already familiar with:

	spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
	spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
	spew.Println(myVar, myVar2)
	spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
	spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)

See the Index for the full list convenience functions.

Sample Formatter Output

Double pointer to a uint8:
	  %v: <**>5
	 %+v: <**>(0xf8400420d0->0xf8400420c8)5
	 %#v: (**uint8)5
	%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5

Pointer to circular struct with a uint8 field and a pointer to itself:
	  %v: <*>{1 <*><shown>}
	 %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
	 %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
	%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}

See the Printf example for details on the setup of variables being shown
here.

Errors

Since it is possible for custom Stringer/error interfaces to panic, spew
detects them and handles them internally by printing the panic information
inline with the output.  Since spew is intended to provide deep pretty printing
capabilities on structures, it intentionally does not return any errors.
*/
package spew
<commit_msg>Make overview ConfigState options order consistent.<commit_after>/*
 * Copyright (c) 2013 Dave Collins <dave@davec.name>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
Package spew implements a deep pretty printer for Go data structures to aid in
debugging.

A quick overview of the additional features spew provides over the built-in
printing facilities for Go data types are as follows:

	* Pointers are dereferenced and followed
	* Circular data structures are detected and handled properly
	* Custom Stringer/error interfaces are optionally invoked, including
	  on unexported types
	* Custom types which only implement the Stringer/error interfaces via
	  a pointer receiver are optionally invoked when passing non-pointer
	  variables

There are two different approaches spew allows for dumping Go data structures:

	* Dump style which prints with newlines, customizable indentation,
	  and additional debug information such as types and all pointer addresses
	  used to indirect to the final value
	* A custom Formatter interface that integrates cleanly with the standard fmt
	  package and replaces %v and %+v to provide inline printing similar
	  to the default %v while providing the additional functionality outlined
	  above and passing unsupported format verb/flag combinations such a %x,
	  %q, and %#v along to fmt

Quick Start

This section demonstrates how to quickly get started with spew.  See the
sections below for further details on formatting and configuration options.

To dump a variable with full newlines, indentation, type, and pointer
information use Dump or Fdump:
	spew.Dump(myVar1, myVar2, ...)
	spew.Fdump(someWriter, myVar1, myVar2, ...)

Alternatively, if you would prefer to use format strings with a compacted inline
printing style, use the convenience wrappers Printf, Fprintf, etc with
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
%#+v (adds types and pointer addresses):
	spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
	spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
	spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
	spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)

Configuration Options

Configuration of spew is handled by fields in the ConfigState type.  For
convenience, all of the top-level functions use a global state available
via the spew.Config global.

It is also possible to create a ConfigState instance that provides methods
equivalent to the top-level functions.  This allows concurrent configuration
options.  See the ConfigState documentation for more details.

The following configuration options are available:
	* Indent
		String to use for each indentation level for Dump functions.
		It is a single space by default.  A popular alternative is "\t".

	* MaxDepth
		Maximum number of levels to descend into nested data structures.
		There is no limit by default.

	* DisableMethods
		Disables invocation of error and Stringer interface methods.
		Method invocation is enabled by default.

	* DisablePointerMethods
		Disables invocation of error and Stringer interface methods on types
		which only accept pointer receivers from non-pointer variables.
		Pointer method invocation is enabled by default.

Dump Usage

Simply call spew.Dump with a list of variables you want to dump:

	spew.Dump(myVar1, myVar2, ...)

You may also call spew.Fdump if you would prefer to output to an arbitrary
io.Writer.  For example, to dump to standard error:

	spew.Fdump(os.Stderr, myVar1, myVar2, ...)

Sample Dump Output

See the Dump example for details on the setup of the types and variables being
shown here.

	(main.Foo) {
	 unexportedField: (*main.Bar)(0xf84002e210)({
	  flag: (main.Flag) flagTwo,
	  data: (uintptr) <nil>
	 }),
	 ExportedField: (map[interface {}]interface {}) {
	  (string) "one": (bool) true
	 }
	}

Custom Formatter

Spew provides a custom formatter the implements the fmt.Formatter interface
so that it integrates cleanly with standard fmt package printing functions. The
formatter is useful for inline printing of smaller data types similar to the
standard %v format specifier.

The custom formatter only responds to the %v (most compact), %+v (adds pointer
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
combinations.  Any other verbs such as %x and %q will be sent to the the
standard fmt package for formatting.  In addition, the custom formatter ignores
the width and precision arguments (however they will still work on the format
specifiers not handled by the custom formatter).

Custom Formatter Usage

The simplest way to make use of the spew custom formatter is to call one of the
convenience functions such as spew.Printf, spew.Println, or spew.Printf.  The
functions have syntax you are most likely already familiar with:

	spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
	spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
	spew.Println(myVar, myVar2)
	spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
	spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)

See the Index for the full list convenience functions.

Sample Formatter Output

Double pointer to a uint8:
	  %v: <**>5
	 %+v: <**>(0xf8400420d0->0xf8400420c8)5
	 %#v: (**uint8)5
	%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5

Pointer to circular struct with a uint8 field and a pointer to itself:
	  %v: <*>{1 <*><shown>}
	 %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
	 %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
	%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}

See the Printf example for details on the setup of variables being shown
here.

Errors

Since it is possible for custom Stringer/error interfaces to panic, spew
detects them and handles them internally by printing the panic information
inline with the output.  Since spew is intended to provide deep pretty printing
capabilities on structures, it intentionally does not return any errors.
*/
package spew
<|endoftext|>


<commit_before>package history

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/mattn/go-isatty"
	"github.com/zetamatta/nyagos/texts"
)

var Mark = "!"

var DisableMarks = "\"'"

func (hisObj *Container) Replace(line string) (string, bool, error) {
	var mark rune
	for _, c := range Mark {
		mark = c
		break
	}

	var buffer strings.Builder
	isReplaced := false
	reader := strings.NewReader(line)
	history_count := hisObj.Len()

	quotedChar := '\000'

	for reader.Len() > 0 {
		ch, _, _ := reader.ReadRune()
		if quotedChar == '\000' && strings.IndexRune(DisableMarks, ch) >= 0 {
			quotedChar = ch
			buffer.WriteRune(ch)
			continue
		} else if ch == quotedChar {
			quotedChar = '\000'
			buffer.WriteRune(ch)
			continue
		}
		if ch != mark || reader.Len() <= 0 || quotedChar != '\000' {
			buffer.WriteRune(ch)
			continue
		}
		ch, _, _ = reader.ReadRune()
		if unicode.IsSpace(ch) {
			buffer.WriteRune('!')
			buffer.WriteRune(ch)
			continue
		}
		if n := strings.IndexRune("^$:*", ch); n >= 0 {
			reader.UnreadRune()
			if history_count >= 1 {
				line := hisObj.At(history_count - 1)
				expandMacro(&buffer, reader, line)
				isReplaced = true
			}
			continue
		}
		if ch == mark { // !!
			if history_count >= 1 {
				line := hisObj.At(history_count - 1)
				expandMacro(&buffer, reader, line)
				isReplaced = true
				continue
			} else {
				return "", false, errors.New("!!: event not found")
			}
		}
		if unicode.IsDigit(ch) { // !n
			reader.UnreadRune()
			var backno int
			fmt.Fscan(reader, &backno)
			if 0 <= backno && backno < history_count {
				line := hisObj.At(backno)
				expandMacro(&buffer, reader, line)
				isReplaced = true
			} else {
				return "", false, fmt.Errorf("!%d: event not found", backno)
			}
			continue
		}
		if ch == '-' && reader.Len() > 0 { // !-n
			var number int
			if _, err := fmt.Fscan(reader, &number); err == nil {
				backno := history_count - number
				if 0 <= backno && backno < history_count {
					line := hisObj.At(backno)
					expandMacro(&buffer, reader, line)
					isReplaced = true
				} else {
					return "", false, fmt.Errorf("!-%d: event not found", number)
				}
				continue
			} else {
				reader.UnreadRune() // next char of '-'
			}
		}
		if ch == '?' { // !?str?
			var seekStrBuf strings.Builder
			lastCharIsQuestionMark := false
			for reader.Len() > 0 {
				ch, _, _ := reader.ReadRune()
				if ch == '?' {
					lastCharIsQuestionMark = true
					break
				}
				seekStrBuf.WriteRune(ch)
			}
			seekStr := seekStrBuf.String()
			found := false
			for i := history_count - 1; i >= 0; i-- {
				his1 := hisObj.At(i)
				if strings.Contains(his1, seekStr) {
					expandMacro(&buffer, reader, his1)
					isReplaced = true
					found = true
					break
				}
			}
			if !found {
				if lastCharIsQuestionMark {
					return "", false, fmt.Errorf("?%s?: event not found", seekStr)
				} else {
					return "", false, fmt.Errorf("?%s: event not found", seekStr)
				}
			}
			continue
		}
		// !str
		var seekStrBuf strings.Builder
		seekStrBuf.WriteRune(ch)
		for reader.Len() > 0 {
			ch, _, _ := reader.ReadRune()
			if unicode.IsSpace(ch) || ch == ':' {
				reader.UnreadRune()
				break
			}
			seekStrBuf.WriteRune(ch)
		}
		seekStr := seekStrBuf.String()
		found := false
		for i := history_count - 1; i >= 0; i-- {
			his1 := hisObj.At(i)
			if strings.HasPrefix(his1, seekStr) {
				expandMacro(&buffer, reader, his1)
				isReplaced = true
				found = true
				break
			}
		}
		if !found {
			return "", false, fmt.Errorf("%c%s: event not found", mark, seekStr)
		}
	}
	return buffer.String(), isReplaced, nil
}

func expandMacro(buffer *strings.Builder, reader *strings.Reader, line string) {
	ch, siz, _ := reader.ReadRune()
	if siz > 0 && ch == ':' {
		ch, siz, _ = reader.ReadRune()
	}
	if siz > 0 && ch == '^' {
		if words := texts.SplitLikeShellString(line); len(words) >= 2 {
			buffer.WriteString(words[1])
		}
	} else if siz > 0 && ch == '$' {
		if words := texts.SplitLikeShellString(line); len(words) >= 2 {
			buffer.WriteString(words[len(words)-1])
		}
	} else if siz > 0 && ch == '*' {
		if words := texts.SplitLikeShellString(line); len(words) >= 2 {
			buffer.WriteString(strings.Join(words[1:], " "))
		}
	} else if siz > 0 && unicode.IsDigit(ch) {
		var n int
		reader.UnreadRune()
		fmt.Fscan(reader, &n)
		words := texts.SplitLikeShellString(line)
		if n < len(words) {
			buffer.WriteString(words[n])
		}
	} else {
		if siz > 0 {
			reader.UnreadRune()
		}
		buffer.WriteString(line)
	}
}

type Param interface {
	Arg(int) string
	Args() []string
	Out() io.Writer
	Err() io.Writer
}

func CmdHistory(ctx context.Context, cmd Param) (int, error) {
	if ctx == nil {
		fmt.Fprintln(cmd.Err(), "history not found (case1)")
		return 1, nil
	}
	var num int
	if len(cmd.Args()) >= 2 {
		num64, err := strconv.ParseInt(cmd.Arg(1), 0, 32)
		if err != nil {
			switch err.(type) {
			case *strconv.NumError:
				return 0, fmt.Errorf(
					"history: %s not a number", cmd.Arg(1))
			default:
				return 0, err
			}
		}
		num = int(num64)
		if num < 0 {
			num = -num
		}
	} else {
		num = 10
	}
	start := 0

	historyObj_ := ctx.Value(PackageId)
	if historyObj, ok := historyObj_.(*Container); ok {
		if f, ok := cmd.Out().(*os.File); ok && isatty.IsTerminal(f.Fd()) && historyObj.Len() > num {

			start = historyObj.Len() - num
		}
		home := os.Getenv("USERPROFILE")
		for i := start; i < historyObj.Len(); i++ {
			row := historyObj.rows[i]
			dir := row.Dir
			if strings.HasPrefix(strings.ToUpper(dir), strings.ToUpper(home)) {
				dir = "~" + dir[len(home):]
			}
			dir = filepath.ToSlash(dir)
			fmt.Fprintf(cmd.Out(), "%4d  %s [%d] %-s (%s)\n",
				i,
				row.Stamp.Format("Jan _2 15:04:05"),
				row.Pid,
				row.Text,
				dir)
		}
	} else {
		fmt.Fprintln(cmd.Err(), "history not found (case 2)")
	}
	return 0, nil
}

const max_histories = 1000

func (hisObj *Container) SaveViaWriter(w io.Writer) {
	i := 0
	if len(hisObj.rows) > max_histories {
		i = len(hisObj.rows) - max_histories
	}
	bw := bufio.NewWriter(w)
	for ; i < len(hisObj.rows); i++ {
		fmt.Fprintln(bw, hisObj.rows[i].String())
	}
	bw.Flush()
}

func (hisObj *Container) Save(path string) error {
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	hisObj.SaveViaWriter(fd)
	return fd.Close()
}

func (hisObj *Container) LoadViaReader(reader io.Reader) {
	sc := bufio.NewScanner(reader)
	list := make([][]string, 0, 2000)
	hash := make(map[string]int)
	for sc.Scan() {
		line := sc.Text()
		if lnum, ok := hash[line]; ok {
			// delete duplicated record (marking)
			list[lnum] = nil
		}
		hash[line] = len(list)

		p := strings.Split(line, "\t")
		list = append(list, p)
	}
	for _, p := range list {
		// push only not duplicated record.
		if p != nil {
			var stamp time.Time
			var dir string
			pid := 0
			if len(p) >= 3 {
				dir = p[1]
				stamp, _ = time.Parse("2006-01-02 15:04:05", p[2])
				if len(p) >= 4 {
					pid, _ = strconv.Atoi(p[3])
				}
			}
			hisObj.PushLine(Line{
				Text:  p[0],
				Dir:   dir,
				Stamp: stamp,
				Pid:   pid})
		}
	}
	sort.Slice(hisObj.rows, func(i, j int) bool {
		p := hisObj.rows[i]
		q := hisObj.rows[j]
		if p.Stamp != q.Stamp {
			return p.Stamp.Before(q.Stamp)
		}
		return p.Text < q.Text
	})
}

func (hisObj *Container) Load(path string) error {
	fd, err := os.Open(path)
	if err != nil {
		return err
	}
	hisObj.LoadViaReader(fd)
	return fd.Close()
}
<commit_msg>Changed the error message when the command `history` is called in _nyagos<commit_after>package history

import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"
	"unicode"

	"github.com/mattn/go-isatty"
	"github.com/zetamatta/nyagos/texts"
)

var Mark = "!"

var DisableMarks = "\"'"

func (hisObj *Container) Replace(line string) (string, bool, error) {
	var mark rune
	for _, c := range Mark {
		mark = c
		break
	}

	var buffer strings.Builder
	isReplaced := false
	reader := strings.NewReader(line)
	history_count := hisObj.Len()

	quotedChar := '\000'

	for reader.Len() > 0 {
		ch, _, _ := reader.ReadRune()
		if quotedChar == '\000' && strings.IndexRune(DisableMarks, ch) >= 0 {
			quotedChar = ch
			buffer.WriteRune(ch)
			continue
		} else if ch == quotedChar {
			quotedChar = '\000'
			buffer.WriteRune(ch)
			continue
		}
		if ch != mark || reader.Len() <= 0 || quotedChar != '\000' {
			buffer.WriteRune(ch)
			continue
		}
		ch, _, _ = reader.ReadRune()
		if unicode.IsSpace(ch) {
			buffer.WriteRune('!')
			buffer.WriteRune(ch)
			continue
		}
		if n := strings.IndexRune("^$:*", ch); n >= 0 {
			reader.UnreadRune()
			if history_count >= 1 {
				line := hisObj.At(history_count - 1)
				expandMacro(&buffer, reader, line)
				isReplaced = true
			}
			continue
		}
		if ch == mark { // !!
			if history_count >= 1 {
				line := hisObj.At(history_count - 1)
				expandMacro(&buffer, reader, line)
				isReplaced = true
				continue
			} else {
				return "", false, errors.New("!!: event not found")
			}
		}
		if unicode.IsDigit(ch) { // !n
			reader.UnreadRune()
			var backno int
			fmt.Fscan(reader, &backno)
			if 0 <= backno && backno < history_count {
				line := hisObj.At(backno)
				expandMacro(&buffer, reader, line)
				isReplaced = true
			} else {
				return "", false, fmt.Errorf("!%d: event not found", backno)
			}
			continue
		}
		if ch == '-' && reader.Len() > 0 { // !-n
			var number int
			if _, err := fmt.Fscan(reader, &number); err == nil {
				backno := history_count - number
				if 0 <= backno && backno < history_count {
					line := hisObj.At(backno)
					expandMacro(&buffer, reader, line)
					isReplaced = true
				} else {
					return "", false, fmt.Errorf("!-%d: event not found", number)
				}
				continue
			} else {
				reader.UnreadRune() // next char of '-'
			}
		}
		if ch == '?' { // !?str?
			var seekStrBuf strings.Builder
			lastCharIsQuestionMark := false
			for reader.Len() > 0 {
				ch, _, _ := reader.ReadRune()
				if ch == '?' {
					lastCharIsQuestionMark = true
					break
				}
				seekStrBuf.WriteRune(ch)
			}
			seekStr := seekStrBuf.String()
			found := false
			for i := history_count - 1; i >= 0; i-- {
				his1 := hisObj.At(i)
				if strings.Contains(his1, seekStr) {
					expandMacro(&buffer, reader, his1)
					isReplaced = true
					found = true
					break
				}
			}
			if !found {
				if lastCharIsQuestionMark {
					return "", false, fmt.Errorf("?%s?: event not found", seekStr)
				} else {
					return "", false, fmt.Errorf("?%s: event not found", seekStr)
				}
			}
			continue
		}
		// !str
		var seekStrBuf strings.Builder
		seekStrBuf.WriteRune(ch)
		for reader.Len() > 0 {
			ch, _, _ := reader.ReadRune()
			if unicode.IsSpace(ch) || ch == ':' {
				reader.UnreadRune()
				break
			}
			seekStrBuf.WriteRune(ch)
		}
		seekStr := seekStrBuf.String()
		found := false
		for i := history_count - 1; i >= 0; i-- {
			his1 := hisObj.At(i)
			if strings.HasPrefix(his1, seekStr) {
				expandMacro(&buffer, reader, his1)
				isReplaced = true
				found = true
				break
			}
		}
		if !found {
			return "", false, fmt.Errorf("%c%s: event not found", mark, seekStr)
		}
	}
	return buffer.String(), isReplaced, nil
}

func expandMacro(buffer *strings.Builder, reader *strings.Reader, line string) {
	ch, siz, _ := reader.ReadRune()
	if siz > 0 && ch == ':' {
		ch, siz, _ = reader.ReadRune()
	}
	if siz > 0 && ch == '^' {
		if words := texts.SplitLikeShellString(line); len(words) >= 2 {
			buffer.WriteString(words[1])
		}
	} else if siz > 0 && ch == '$' {
		if words := texts.SplitLikeShellString(line); len(words) >= 2 {
			buffer.WriteString(words[len(words)-1])
		}
	} else if siz > 0 && ch == '*' {
		if words := texts.SplitLikeShellString(line); len(words) >= 2 {
			buffer.WriteString(strings.Join(words[1:], " "))
		}
	} else if siz > 0 && unicode.IsDigit(ch) {
		var n int
		reader.UnreadRune()
		fmt.Fscan(reader, &n)
		words := texts.SplitLikeShellString(line)
		if n < len(words) {
			buffer.WriteString(words[n])
		}
	} else {
		if siz > 0 {
			reader.UnreadRune()
		}
		buffer.WriteString(line)
	}
}

type Param interface {
	Arg(int) string
	Args() []string
	Out() io.Writer
	Err() io.Writer
}

func CmdHistory(ctx context.Context, cmd Param) (int, error) {
	if ctx == nil {
		fmt.Fprintln(cmd.Err(), "history not found (case1)")
		return 1, nil
	}
	var num int
	if len(cmd.Args()) >= 2 {
		num64, err := strconv.ParseInt(cmd.Arg(1), 0, 32)
		if err != nil {
			switch err.(type) {
			case *strconv.NumError:
				return 0, fmt.Errorf(
					"history: %s not a number", cmd.Arg(1))
			default:
				return 0, err
			}
		}
		num = int(num64)
		if num < 0 {
			num = -num
		}
	} else {
		num = 10
	}
	start := 0

	historyObj, ok := ctx.Value(PackageId).(*Container)
	if !ok {
		return -1, errors.New("history: not available in startup script")
	}
	if f, ok := cmd.Out().(*os.File); ok && isatty.IsTerminal(f.Fd()) && historyObj.Len() > num {
		start = historyObj.Len() - num
	}
	home := os.Getenv("USERPROFILE")
	for i := start; i < historyObj.Len(); i++ {
		row := historyObj.rows[i]
		dir := row.Dir
		if strings.HasPrefix(strings.ToUpper(dir), strings.ToUpper(home)) {
			dir = "~" + dir[len(home):]
		}
		dir = filepath.ToSlash(dir)
		fmt.Fprintf(cmd.Out(), "%4d  %s [%d] %-s (%s)\n",
			i,
			row.Stamp.Format("Jan _2 15:04:05"),
			row.Pid,
			row.Text,
			dir)
	}
	return 0, nil
}

const max_histories = 1000

func (hisObj *Container) SaveViaWriter(w io.Writer) {
	i := 0
	if len(hisObj.rows) > max_histories {
		i = len(hisObj.rows) - max_histories
	}
	bw := bufio.NewWriter(w)
	for ; i < len(hisObj.rows); i++ {
		fmt.Fprintln(bw, hisObj.rows[i].String())
	}
	bw.Flush()
}

func (hisObj *Container) Save(path string) error {
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	hisObj.SaveViaWriter(fd)
	return fd.Close()
}

func (hisObj *Container) LoadViaReader(reader io.Reader) {
	sc := bufio.NewScanner(reader)
	list := make([][]string, 0, 2000)
	hash := make(map[string]int)
	for sc.Scan() {
		line := sc.Text()
		if lnum, ok := hash[line]; ok {
			// delete duplicated record (marking)
			list[lnum] = nil
		}
		hash[line] = len(list)

		p := strings.Split(line, "\t")
		list = append(list, p)
	}
	for _, p := range list {
		// push only not duplicated record.
		if p != nil {
			var stamp time.Time
			var dir string
			pid := 0
			if len(p) >= 3 {
				dir = p[1]
				stamp, _ = time.Parse("2006-01-02 15:04:05", p[2])
				if len(p) >= 4 {
					pid, _ = strconv.Atoi(p[3])
				}
			}
			hisObj.PushLine(Line{
				Text:  p[0],
				Dir:   dir,
				Stamp: stamp,
				Pid:   pid})
		}
	}
	sort.Slice(hisObj.rows, func(i, j int) bool {
		p := hisObj.rows[i]
		q := hisObj.rows[j]
		if p.Stamp != q.Stamp {
			return p.Stamp.Before(q.Stamp)
		}
		return p.Text < q.Text
	})
}

func (hisObj *Container) Load(path string) error {
	fd, err := os.Open(path)
	if err != nil {
		return err
	}
	hisObj.LoadViaReader(fd)
	return fd.Close()
}
<|endoftext|>


<commit_before>package history

import "bufio"
import "bytes"
import "fmt"
import "os"
import "strconv"
import "strings"

import "../conio/readline"
import "../interpreter"

var histories = make([]string, 0)
var pointor = 0

func Get(n int) string {
	if n < 0 {
		n = len(histories) + n
	}
	if n >= len(histories) {
		return ""
	} else {
		return histories[n]
	}
}

func Len() int {
	return len(histories)
}

func LastHistory() string {
	if len(histories) <= 0 {
		return ""
	} else {
		return histories[len(histories)-1]
	}
}

func KeyFuncHistoryUp(this *readline.Buffer) readline.Result {
	if pointor <= 0 {
		pointor = len(histories)
	}
	pointor -= 1
	readline.KeyFuncClear(this)
	if pointor >= 0 {
		this.InsertString(0, histories[pointor])
		this.ViewStart = 0
		this.Cursor = 0
		readline.KeyFuncTail(this)
	}
	return readline.CONTINUE
}

func KeyFuncHistoryDown(this *readline.Buffer) readline.Result {
	pointor += 1
	if pointor >= len(histories) {
		pointor = 0
	}
	readline.KeyFuncClear(this)
	if pointor < len(histories) {
		this.InsertString(0, histories[pointor])
		this.ViewStart = 0
		this.Cursor = 0
		readline.KeyFuncTail(this)
	}
	return readline.CONTINUE
}

func Push(input string) {
	histories = append(histories, input)
	pointor = len(histories)
}

func eventDesignerStrBegin(reader *strings.Reader) (string, bool) {
	var buf bytes.Buffer
	for reader.Len() > 0 {
		ch, _, _ := reader.ReadRune()
		if ch == ' ' {
			reader.UnreadRune()
			break
		}
		buf.WriteRune(ch)
	}
	str := buf.String()
	for i := len(histories) - 1; i >= 0; i-- {
		if strings.HasPrefix(histories[i], str) {
			return histories[i], true
		}
	}
	return "", false
}

func Replace(line string) (string, bool) {
	var buffer bytes.Buffer
	var isReplaced = false
	reader := strings.NewReader(line)

	for reader.Len() > 0 {
		ch, _, _ := reader.ReadRune()
		if ch != '!' || reader.Len() <= 0 {
			buffer.WriteRune(ch)
			continue
		}
		ch, _, _ = reader.ReadRune()
		if n := strings.IndexRune("^$:*", ch); n >= 0 {
			reader.UnreadRune()
			if len(histories) > 0 {
				insertHisotry(&buffer, reader, histories[len(histories)-1])
				isReplaced = true
			}
			continue
		}
		if ch == '!' { // !!
			if len(histories) > 0 {
				insertHisotry(&buffer, reader, histories[len(histories)-1])
				isReplaced = true
				continue
			} else {
				buffer.WriteRune('!')
				continue
			}
		}
		if n := strings.IndexRune("0123456789", ch); n >= 0 { // !n
			backno := n
			for reader.Len() > 0 {
				ch, _, _ = reader.ReadRune()
				if n = strings.IndexRune("0123456789", ch); n >= 0 {
					backno = backno*10 + n
				} else {
					reader.UnreadRune()
					break
				}
			}
			backno = backno % len(histories)
			if 0 <= backno && backno < len(histories) {
				insertHisotry(&buffer, reader, histories[backno])
				isReplaced = true
			}
			continue
		}
		if ch == '-' && reader.Len() > 0 { // !-n
			ch, _, _ := reader.ReadRune()
			n := strings.IndexRune("0123456789", ch)
			if n >= 0 {
				number := n
				for reader.Len() > 0 {
					ch, _, _ = reader.ReadRune()
					n = strings.IndexRune("0123456789", ch)
					if n < 0 {
						reader.UnreadRune()
						break
					}
					number = number*10 + n
				}
				backno := len(histories) - number
				for backno < 0 {
					backno += len(histories)
				}
				if 0 <= backno && backno < len(histories) {
					insertHisotry(&buffer, reader, histories[backno])
					isReplaced = true
				} else {
					buffer.WriteString("!-0")
				}
			} else {
				buffer.WriteString("!-")
				buffer.WriteRune(ch)
			}
		} else { // !str
			reader.UnreadRune() // "-"
			str, ok := eventDesignerStrBegin(reader)
			if ok {
				buffer.WriteString(str)
				isReplaced = true
			} else {
				buffer.WriteRune('!')
				buffer.WriteRune(ch)
			}
		}
	}
	return buffer.String(), isReplaced
}

func splitQ(s string) []string {
	args := make([]string, 0)
	reader := strings.NewReader(s)
	for reader.Len() > 0 {
		var buffer bytes.Buffer
		for {
			if reader.Len() <= 0 {
				return args
			}
			ch, _, _ := reader.ReadRune()
			if ch != ' ' {
				reader.UnreadRune()
				break
			}
		}
		quote := false
		for reader.Len() > 0 {
			ch, _, _ := reader.ReadRune()
			if ch == '"' {
				quote = !quote
			}
			if ch == ' ' && !quote {
				break
			}
			buffer.WriteRune(ch)
		}
		s := buffer.String()
		if s != "" {
			args = append(args, s)
		}
	}
	return args
}

func insertHisotry(buffer *bytes.Buffer, reader *strings.Reader, history1 string) {
	ch, siz, _ := reader.ReadRune()
	if siz > 0 && ch == '^' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(args[1])
		}
	} else if siz > 0 && ch == '$' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(args[len(args)-1])
		}
	} else if siz > 0 && ch == '*' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(strings.Join(args[1:], " "))
		}
	} else if siz > 0 && ch == ':' {
		args := splitQ(history1)
		n := 0
		count := 0
		for reader.Len() > 0 {
			ch, _, _ = reader.ReadRune()
			index := strings.IndexRune("0123456789", ch)
			if index >= 0 {
				n = n*10 + index
				count++
			} else {
				reader.UnreadRune()
				break
			}
		}
		if count <= 0 {
			buffer.WriteRune(':')
		} else if n < len(args) {
			buffer.WriteString(args[n])
		}
	} else {
		if siz > 0 {
			reader.UnreadRune()
		}
		buffer.WriteString(history1)
	}
}

func CmdHistory(cmd *interpreter.Interpreter) (interpreter.NextT, error) {
	var num int
	if len(cmd.Args) >= 2 {
		num64, err := strconv.ParseInt(cmd.Args[1], 0, 32)
		if err != nil {
			return interpreter.CONTINUE, err
		}
		num = int(num64)
	} else {
		num = 10
	}
	var start int
	if len(histories) > num {
		start = len(histories) - num
	} else {
		start = 0
	}
	for i, s := range histories[start:] {
		fmt.Fprintf(cmd.Stdout, "%3d : %-s\n", start+i, s)
	}
	return interpreter.CONTINUE, nil
}

const max_histories = 2000

func Save(path string) error {
	var hist_ []string
	if len(histories) > max_histories {
		hist_ = histories[(len(histories) - max_histories):]
	} else {
		hist_ = histories
	}
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	for _, s := range hist_ {
		fmt.Fprintln(fd, s)
	}
	return nil
}

func Load(path string) error {
	fd, err := os.Open(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	sc := bufio.NewScanner(fd)
	for sc.Scan() {
		histories = append(histories, sc.Text())
	}
	return nil
}
<commit_msg>history: !-XXX (XXX は非数字)を !str の str==「-XXX」相当に扱うようにした<commit_after>package history

import "bufio"
import "bytes"
import "fmt"
import "os"
import "strconv"
import "strings"

import "../conio/readline"
import "../interpreter"

var histories = make([]string, 0)
var pointor = 0

func Get(n int) string {
	if n < 0 {
		n = len(histories) + n
	}
	if n >= len(histories) {
		return ""
	} else {
		return histories[n]
	}
}

func Len() int {
	return len(histories)
}

func LastHistory() string {
	if len(histories) <= 0 {
		return ""
	} else {
		return histories[len(histories)-1]
	}
}

func KeyFuncHistoryUp(this *readline.Buffer) readline.Result {
	if pointor <= 0 {
		pointor = len(histories)
	}
	pointor -= 1
	readline.KeyFuncClear(this)
	if pointor >= 0 {
		this.InsertString(0, histories[pointor])
		this.ViewStart = 0
		this.Cursor = 0
		readline.KeyFuncTail(this)
	}
	return readline.CONTINUE
}

func KeyFuncHistoryDown(this *readline.Buffer) readline.Result {
	pointor += 1
	if pointor >= len(histories) {
		pointor = 0
	}
	readline.KeyFuncClear(this)
	if pointor < len(histories) {
		this.InsertString(0, histories[pointor])
		this.ViewStart = 0
		this.Cursor = 0
		readline.KeyFuncTail(this)
	}
	return readline.CONTINUE
}

func Push(input string) {
	histories = append(histories, input)
	pointor = len(histories)
}

func Replace(line string) (string, bool) {
	var buffer bytes.Buffer
	var isReplaced = false
	reader := strings.NewReader(line)

	for reader.Len() > 0 {
		ch, _, _ := reader.ReadRune()
		if ch != '!' || reader.Len() <= 0 {
			buffer.WriteRune(ch)
			continue
		}
		ch, _, _ = reader.ReadRune()
		if n := strings.IndexRune("^$:*", ch); n >= 0 {
			reader.UnreadRune()
			if len(histories) > 0 {
				insertHisotry(&buffer, reader, histories[len(histories)-1])
				isReplaced = true
			}
			continue
		}
		if ch == '!' { // !!
			if len(histories) > 0 {
				insertHisotry(&buffer, reader, histories[len(histories)-1])
				isReplaced = true
				continue
			} else {
				buffer.WriteRune('!')
				continue
			}
		}
		if n := strings.IndexRune("0123456789", ch); n >= 0 { // !n
			backno := n
			for reader.Len() > 0 {
				ch, _, _ = reader.ReadRune()
				if n = strings.IndexRune("0123456789", ch); n >= 0 {
					backno = backno*10 + n
				} else {
					reader.UnreadRune()
					break
				}
			}
			backno = backno % len(histories)
			if 0 <= backno && backno < len(histories) {
				insertHisotry(&buffer, reader, histories[backno])
				isReplaced = true
			}
			continue
		}
		if ch == '-' && reader.Len() > 0 { // !-n
			ch, _, _ := reader.ReadRune()
			n := strings.IndexRune("0123456789", ch)
			if n >= 0 {
				number := n
				for reader.Len() > 0 {
					ch, _, _ = reader.ReadRune()
					n = strings.IndexRune("0123456789", ch)
					if n < 0 {
						reader.UnreadRune()
						break
					}
					number = number*10 + n
				}
				backno := len(histories) - number
				for backno < 0 {
					backno += len(histories)
				}
				if 0 <= backno && backno < len(histories) {
					insertHisotry(&buffer, reader, histories[backno])
					isReplaced = true
				} else {
					buffer.WriteString("!-0")
				}
				continue
			} else {
				reader.UnreadRune() // next char of '-'
			}
		}
		var seekStrBuf bytes.Buffer
		seekStrBuf.WriteRune(ch)
		for reader.Len() > 0 {
			ch, _, _ := reader.ReadRune()
			if ch == ' ' {
				reader.UnreadRune()
				break
			}
			seekStrBuf.WriteRune(ch)
		}
		seekStr := seekStrBuf.String()
		found := false
		for i := len(histories) - 1; i >= 0; i-- {
			if strings.HasPrefix(histories[i], seekStr) {
				buffer.WriteString(histories[i])
				isReplaced = true
				found = true
				break
			}
		}
		if !found {
			buffer.WriteRune('!')
			buffer.WriteRune(ch)
		}
	}
	return buffer.String(), isReplaced
}

func splitQ(s string) []string {
	args := make([]string, 0)
	reader := strings.NewReader(s)
	for reader.Len() > 0 {
		var buffer bytes.Buffer
		for {
			if reader.Len() <= 0 {
				return args
			}
			ch, _, _ := reader.ReadRune()
			if ch != ' ' {
				reader.UnreadRune()
				break
			}
		}
		quote := false
		for reader.Len() > 0 {
			ch, _, _ := reader.ReadRune()
			if ch == '"' {
				quote = !quote
			}
			if ch == ' ' && !quote {
				break
			}
			buffer.WriteRune(ch)
		}
		s := buffer.String()
		if s != "" {
			args = append(args, s)
		}
	}
	return args
}

func insertHisotry(buffer *bytes.Buffer, reader *strings.Reader, history1 string) {
	ch, siz, _ := reader.ReadRune()
	if siz > 0 && ch == '^' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(args[1])
		}
	} else if siz > 0 && ch == '$' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(args[len(args)-1])
		}
	} else if siz > 0 && ch == '*' {
		args := splitQ(history1)
		if len(args) >= 2 {
			buffer.WriteString(strings.Join(args[1:], " "))
		}
	} else if siz > 0 && ch == ':' {
		args := splitQ(history1)
		n := 0
		count := 0
		for reader.Len() > 0 {
			ch, _, _ = reader.ReadRune()
			index := strings.IndexRune("0123456789", ch)
			if index >= 0 {
				n = n*10 + index
				count++
			} else {
				reader.UnreadRune()
				break
			}
		}
		if count <= 0 {
			buffer.WriteRune(':')
		} else if n < len(args) {
			buffer.WriteString(args[n])
		}
	} else {
		if siz > 0 {
			reader.UnreadRune()
		}
		buffer.WriteString(history1)
	}
}

func CmdHistory(cmd *interpreter.Interpreter) (interpreter.NextT, error) {
	var num int
	if len(cmd.Args) >= 2 {
		num64, err := strconv.ParseInt(cmd.Args[1], 0, 32)
		if err != nil {
			return interpreter.CONTINUE, err
		}
		num = int(num64)
	} else {
		num = 10
	}
	var start int
	if len(histories) > num {
		start = len(histories) - num
	} else {
		start = 0
	}
	for i, s := range histories[start:] {
		fmt.Fprintf(cmd.Stdout, "%3d : %-s\n", start+i, s)
	}
	return interpreter.CONTINUE, nil
}

const max_histories = 2000

func Save(path string) error {
	var hist_ []string
	if len(histories) > max_histories {
		hist_ = histories[(len(histories) - max_histories):]
	} else {
		hist_ = histories
	}
	fd, err := os.Create(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	for _, s := range hist_ {
		fmt.Fprintln(fd, s)
	}
	return nil
}

func Load(path string) error {
	fd, err := os.Open(path)
	if err != nil {
		return err
	}
	defer fd.Close()
	sc := bufio.NewScanner(fd)
	for sc.Scan() {
		histories = append(histories, sc.Text())
	}
	return nil
}
<|endoftext|>


<commit_before>package shredis

// All replies are build of the types:
//   - string
//   - int
//   - error
//   - interface{} arrays of the above types

import (
	"bufio"
	"errors"
	"io"
)

var (
	// ErrProtocolError is returned on unexpected replies.
	ErrProtocolError = errors.New("shredis: protocol error")
)

type replyReader struct {
	buf     *bufio.Reader
	scratch []byte
}

func newReplyReader(r io.Reader) *replyReader {
	return &replyReader{
		buf: bufio.NewReader(r),
	}
}

func (r *replyReader) Next() (interface{}, error) {
	c, err := r.buf.ReadByte()
	if err != nil {
		return nil, err
	}
	switch c {
	case '+':
		return r.simpleString()
	case ':':
		return r.integer()
	case '$':
		return r.bulk()
	case '-':
		return r.error()
	case '*':
		return r.array()
	default:
		return nil, ErrProtocolError
	}
}

func (r *replyReader) readString() (string, error) {
	p, err := r.buf.ReadSlice('\n')
	if err != nil {
		return "", err
	}
	return string(p[:len(p)-2]), nil
}

func (r *replyReader) readInt() (int, error) {
	p, err := r.buf.ReadSlice('\n')
	if err != nil {
		return 0, err
	}
	negate := false
	n := 0
	for i, c := range p[:len(p)-2] {
		switch c {
		case '-':
			if i != 0 {
				return 0, ErrProtocolError
			}
			negate = true
		case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
			n *= 10
			n += int(c - '0')
		default:
			return 0, ErrProtocolError
		}
	}
	if negate {
		n *= -1
	}
	return n, nil
}

func (r *replyReader) simpleString() (string, error) {
	return r.readString()
}

func (r *replyReader) integer() (int, error) {
	return r.readInt()
}

func (r *replyReader) error() (error, error) {
	s, err := r.readString()
	if err != nil {
		return nil, err
	}
	return errors.New(s), nil
}

func (r *replyReader) bulk() (interface{}, error) {
	n, err := r.readInt()
	if err != nil || n < 0 {
		return nil, err
	}

	if len(r.scratch) < n+2 {
		r.scratch = make([]byte, n+2)
	}
	_, err = io.ReadFull(r.buf, r.scratch[:n+2])
	if err != nil {
		return nil, err
	}
	return string(r.scratch[:n]), nil
}

func (r *replyReader) array() (interface{}, error) {
	n, err := r.readInt()
	if err != nil || n < 0 {
		return nil, err
	}
	res := make([]interface{}, n)
	for i := range res {
		res[i], err = r.Next()
		if err != nil {
			return nil, err
		}
	}
	return res, nil
}
<commit_msg>cleaner switch<commit_after>package shredis

// All replies are build of the types:
//   - string
//   - int
//   - error
//   - interface{} arrays of the above types

import (
	"bufio"
	"errors"
	"io"
)

var (
	// ErrProtocolError is returned on unexpected replies.
	ErrProtocolError = errors.New("shredis: protocol error")
)

type replyReader struct {
	buf     *bufio.Reader
	scratch []byte
}

func newReplyReader(r io.Reader) *replyReader {
	return &replyReader{
		buf: bufio.NewReader(r),
	}
}

func (r *replyReader) Next() (interface{}, error) {
	c, err := r.buf.ReadByte()
	if err != nil {
		return nil, err
	}
	switch c {
	case '+':
		return r.simpleString()
	case ':':
		return r.integer()
	case '$':
		return r.bulk()
	case '-':
		return r.error()
	case '*':
		return r.array()
	default:
		return nil, ErrProtocolError
	}
}

func (r *replyReader) readString() (string, error) {
	p, err := r.buf.ReadSlice('\n')
	if err != nil {
		return "", err
	}
	return string(p[:len(p)-2]), nil
}

func (r *replyReader) readInt() (int, error) {
	p, err := r.buf.ReadSlice('\n')
	if err != nil {
		return 0, err
	}
	negate := false
	n := 0
	for i, c := range p[:len(p)-2] {
		switch {
		case c >= '0' && c <= '9':
			n *= 10
			n += int(c - '0')
		case i == 0 && c == '-':
			negate = true
		default:
			return 0, ErrProtocolError
		}
	}
	if negate {
		n *= -1
	}
	return n, nil
}

func (r *replyReader) simpleString() (string, error) {
	return r.readString()
}

func (r *replyReader) integer() (int, error) {
	return r.readInt()
}

func (r *replyReader) error() (error, error) {
	s, err := r.readString()
	if err != nil {
		return nil, err
	}
	return errors.New(s), nil
}

func (r *replyReader) bulk() (interface{}, error) {
	n, err := r.readInt()
	if err != nil || n < 0 {
		return nil, err
	}

	if len(r.scratch) < n+2 {
		r.scratch = make([]byte, n+2)
	}
	_, err = io.ReadFull(r.buf, r.scratch[:n+2])
	if err != nil {
		return nil, err
	}
	return string(r.scratch[:n]), nil
}

func (r *replyReader) array() (interface{}, error) {
	n, err := r.readInt()
	if err != nil || n < 0 {
		return nil, err
	}
	res := make([]interface{}, n)
	for i := range res {
		res[i], err = r.Next()
		if err != nil {
			return nil, err
		}
	}
	return res, nil
}
<|endoftext|>


<commit_before>package service

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"time"

	"github.com/builderscon/octav/octav/db"
	"github.com/builderscon/octav/octav/internal/errors"
	"github.com/builderscon/octav/octav/model"
	"github.com/builderscon/octav/octav/tools"
	"github.com/lestrrat/go-pdebug"
	"golang.org/x/net/context"
	"golang.org/x/sync/errgroup"
	"google.golang.org/cloud/storage"
)

func (v *Sponsor) getMediaBucketName() string {
	v.bucketOnce.Do(func() {
		if v.MediaBucketName == "" {
			v.MediaBucketName = os.Getenv("GOOGLE_STORAGE_MEDIA_BUCKET")
		}
	})
	return v.MediaBucketName
}

func (v *Sponsor) getStorageClient(ctx context.Context) *storage.Client {
	v.storageOnce.Do(func() {
		if v.Storage == nil {
			client, err := defaultStorageClient(ctx)
			if err != nil {
				panic(err.Error())
			}
			v.Storage = client
		}
	})
	return v.Storage
}

func (v *Sponsor) populateRowForCreate(vdb *db.Sponsor, payload model.CreateSponsorRequest) error {
	vdb.EID = tools.UUID()

	vdb.ConferenceID = payload.ConferenceID
	vdb.Name = payload.Name
	vdb.URL = payload.URL
	vdb.GroupName = payload.GroupName
	vdb.SortOrder = payload.SortOrder

	return nil
}

func (v *Sponsor) populateRowForUpdate(vdb *db.Sponsor, payload model.UpdateSponsorRequest) error {
	if payload.Name.Valid() {
		vdb.Name = payload.Name.String
	}

	if payload.LogoURL1.Valid() {
		vdb.LogoURL1.Valid = true
		vdb.LogoURL1.String = payload.LogoURL1.String
	}

	if payload.LogoURL2.Valid() {
		vdb.LogoURL2.Valid = true
		vdb.LogoURL2.String = payload.LogoURL2.String
	}

	if payload.LogoURL3.Valid() {
		vdb.LogoURL3.Valid = true
		vdb.LogoURL3.String = payload.LogoURL3.String
	}

	if payload.URL.Valid() {
		vdb.URL = payload.URL.String
	}

	if payload.GroupName.Valid() {
		vdb.GroupName = payload.GroupName.String
	}

	if payload.SortOrder.Valid() {
		vdb.SortOrder = int(payload.SortOrder.Int)
	}

	return nil
}

type finalizeFunc func() error

func (ff finalizeFunc) FinalizeFunc() func() error {
	return ff
}

// Ignorable always returns true, otherwise the caller will have to
// bail out immediately
func (ff finalizeFunc) Ignorable() bool {
	return true
}

func (ff finalizeFunc) Error() string {
	return "operation needs finalization"
}

func (v *Sponsor) UploadImagesFromPayload(ctx context.Context, tx *db.Tx, row *db.Sponsor, payload *model.UpdateSponsorRequest) (err error) {
	if pdebug.Enabled {
		g := pdebug.Marker("service.Sponsor.UploadImagesFromPayload").BindError(&err)
		defer g.End()
	}

	// There's nothing to do
	if payload.MultipartForm == nil || payload.MultipartForm.File == nil {
		return nil
	}

	bucketName := v.getMediaBucketName()
	prefix := "http://storage.googleapis.com/" + bucketName

	finalizers := make([]func() error, 0, 3)
	for _, field := range []string{"logo1", "logo2", "logo3"} {
		fhs := payload.MultipartForm.File[field]
		if len(fhs) == 0 {
			continue
		}

		var imgf multipart.File
		imgf, err = fhs[0].Open()
		if err != nil {
			return errors.Wrap(err, "failed to open logo file from multipart form")
		}

		var imgbuf bytes.Buffer
		if _, err := io.Copy(&imgbuf, imgf); err != nil {
			return errors.Wrap(err, "failed to copy logo image data to memory")
		}
		imgtyp := http.DetectContentType(imgbuf.Bytes())

		// Only work with image/png or image/jpeg
		var suffix string
		switch imgtyp {
		case "image/png":
			suffix = "png"
		case "image/jpeg":
			suffix = "jpeg"
		default:
			return errors.Errorf("Unsupported image type %s", imgtyp)
		}

		// TODO: Validate the image
		// TODO: Avoid Google Storage hardcoding?
		// Upload this to a temporary location, then upon successful write to DB
		// rename it to $conference_id/$sponsor_id
		storagecl := v.getStorageClient(ctx)
		tmpname := time.Now().UTC().Format("2006-01-02") + "/" + tools.RandomString(64) + "." + suffix
		wc := storagecl.Bucket(bucketName).Object(tmpname).NewWriter(ctx)
		wc.ContentType = imgtyp
		wc.ACL = []storage.ACLRule{{storage.AllUsers, storage.RoleReader}}

		if pdebug.Enabled {
			pdebug.Printf("Writing '%s' to %s", field, tmpname)
		}

		if _, err := io.Copy(wc, &imgbuf); err != nil {
			return errors.Wrap(err, "failed to write image to temporary location")
		}
		// Note: DO NOT defer wc.Close(), as it's part of the write operation.
		// If wc.Close() does not complete w/o errors. the write failed
		if err := wc.Close(); err != nil {
			return errors.Wrap(err, "failed to write image to temporary location")
		}

		dstname := "conferences/" + row.ConferenceID + "/" + row.EID + "-" + field + "." + suffix
		switch field {
		case "logo1":
			payload.LogoURL1.Set(prefix + "/" + dstname)
		case "logo2":
			payload.LogoURL2.Set(prefix + "/" + dstname)
		case "logo3":
			payload.LogoURL3.Set(prefix + "/" + dstname)
		}

		finalizers = append(finalizers, func() (err error) {
			if pdebug.Enabled {
				g := pdebug.Marker("Finalizer for service.Sponsor.UploadImagesFromPayload").BindError(&err)
				defer g.End()
			}

			src := storagecl.Bucket(bucketName).Object(tmpname)
			dst := storagecl.Bucket(bucketName).Object(dstname)

			if pdebug.Enabled {
				pdebug.Printf("Copying %s to %s", tmpname, dstname)
			}
			if _, err = src.CopyTo(ctx, dst, nil); err != nil {
				return errors.Wrapf(err, "failed to copy from '%s' to '%s'", tmpname, dstname)
			}
			if pdebug.Enabled {
				pdebug.Printf("Deleting %s", tmpname)
			}
			if err := src.Delete(ctx); err != nil {
				return errors.Wrapf(err, "failed to delete '%s'", tmpname)
			}

			return nil
		})
	}

	if len(finalizers) == 0 {
		return nil
	}

	return finalizeFunc(func() error {
		var g errgroup.Group
		for _, f := range finalizers {
			g.Go(f)
		}
		return g.Wait()
	})
}

func (v *Sponsor) CreateFromPayload(ctx context.Context, tx *db.Tx, payload model.AddSponsorRequest, result *model.Sponsor) (err error) {
	if pdebug.Enabled {
		g := pdebug.Marker("service.Sponsor.CreateFromPayload").BindError(&err)
		defer g.End()
	}

	su := User{}
	if err := su.IsConferenceAdministrator(tx, payload.ConferenceID, payload.UserID); err != nil {
		return errors.Wrap(err, "creating a featured sponsor requires conference administrator privilege")
	}

	vdb := db.Sponsor{}
	if err := v.Create(tx, &vdb, model.CreateSponsorRequest{payload}); err != nil {
		return errors.Wrap(err, "failed to store in database")
	}

	c := model.Sponsor{}
	if err := c.FromRow(vdb); err != nil {
		return errors.Wrap(err, "failed to populate model from database")
	}

	*result = c
	return nil
}

func (v *Sponsor) UpdateFromPayload(ctx context.Context, tx *db.Tx, payload model.UpdateSponsorRequest) (err error) {
	if pdebug.Enabled {
		g := pdebug.Marker("service.Sponsor.UpdateFromPayload").BindError(&err)
		defer g.End()
	}

	vdb := db.Sponsor{}
	if err := vdb.LoadByEID(tx, payload.ID); err != nil {
		return errors.Wrap(err, "failed to load featured sponsor from database")
	}

	su := User{}
	if err := su.IsConferenceAdministrator(tx, vdb.ConferenceID, payload.UserID); err != nil {
		return errors.Wrap(err, "updating a featured sponsor requires conference administrator privilege")
	}

	var uploadErr error
	if uploadErr := v.UploadImagesFromPayload(ctx, tx, &vdb, &payload); !errors.IsIgnorable(uploadErr) {
		return errors.Wrap(uploadErr, "failed to process image uploads")
	}

	if err := v.Update(tx, &vdb, payload); err != nil {
		return errors.Wrap(err, "failed to load featured sponsor from database")
	}

	if cb, ok := errors.IsFinalizationRequired(uploadErr); ok {
		return errors.Wrap(cb(), "failed to finalize image upload")
	}
	return nil

}

func (v *Sponsor) DeleteFromPayload(tx *db.Tx, payload model.DeleteSponsorRequest) error {
	var m db.Sponsor
	if err := m.LoadByEID(tx, payload.ID); err != nil {
		return errors.Wrap(err, "failed to load featured sponsor from database")
	}

	su := User{}
	if err := su.IsConferenceAdministrator(tx, m.ConferenceID, payload.UserID); err != nil {
		return errors.Wrap(err, "deleting venues require administrator privileges")
	}

	return errors.Wrap(v.Delete(tx, m.EID), "failed to delete from database")
}

func (v *Sponsor) ListFromPayload(tx *db.Tx, result *model.SponsorList, payload model.ListSponsorsRequest) error {
	var vdbl db.SponsorList
	if err := vdbl.LoadByConferenceSinceEID(tx, payload.ConferenceID, payload.Since.String, int(payload.Limit.Int)); err != nil {
		return errors.Wrap(err, "failed to load featured sponsor from database")
	}

	l := make(model.SponsorList, len(vdbl))
	for i, vdb := range vdbl {
		if err := (l[i]).FromRow(vdb); err != nil {
			return errors.Wrap(err, "failed to populate model from database")
		}

		if err := v.Decorate(tx, &l[i], payload.Lang.String); err != nil {
			return errors.Wrap(err, "failed to decorate venue with associated data")
		}
	}

	*result = l
	return nil
}

func (v *Sponsor) Decorate(tx *db.Tx, sponsor *model.Sponsor, lang string) error {
	if sponsor.LogoURL1 == "" {
		sponsor.LogoURL1 = "http://storage.googleapis.com/media-builderscon-1248/system/nophoto_600.png"
	}

	if sponsor.LogoURL2 == "" {
		sponsor.LogoURL2 = "http://storage.googleapis.com/media-builderscon-1248/system/nophoto_400.png"
	}

	if sponsor.LogoURL3 == "" {
		sponsor.LogoURL3 = "http://storage.googleapis.com/media-builderscon-1248/system/nophoto_200.png"
	}

	if lang == "" {
		return nil
	}

	if err := v.ReplaceL10NStrings(tx, sponsor, lang); err != nil {
		return errors.Wrap(err, "failed to replace L10N strings")
	}

	return nil
}
<commit_msg>Try to copy attributes<commit_after>package service

import (
	"bytes"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"time"

	"github.com/builderscon/octav/octav/db"
	"github.com/builderscon/octav/octav/internal/errors"
	"github.com/builderscon/octav/octav/model"
	"github.com/builderscon/octav/octav/tools"
	"github.com/lestrrat/go-pdebug"
	"golang.org/x/net/context"
	"golang.org/x/sync/errgroup"
	"google.golang.org/cloud/storage"
)

func (v *Sponsor) getMediaBucketName() string {
	v.bucketOnce.Do(func() {
		if v.MediaBucketName == "" {
			v.MediaBucketName = os.Getenv("GOOGLE_STORAGE_MEDIA_BUCKET")
		}
	})
	return v.MediaBucketName
}

func (v *Sponsor) getStorageClient(ctx context.Context) *storage.Client {
	v.storageOnce.Do(func() {
		if v.Storage == nil {
			client, err := defaultStorageClient(ctx)
			if err != nil {
				panic(err.Error())
			}
			v.Storage = client
		}
	})
	return v.Storage
}

func (v *Sponsor) populateRowForCreate(vdb *db.Sponsor, payload model.CreateSponsorRequest) error {
	vdb.EID = tools.UUID()

	vdb.ConferenceID = payload.ConferenceID
	vdb.Name = payload.Name
	vdb.URL = payload.URL
	vdb.GroupName = payload.GroupName
	vdb.SortOrder = payload.SortOrder

	return nil
}

func (v *Sponsor) populateRowForUpdate(vdb *db.Sponsor, payload model.UpdateSponsorRequest) error {
	if payload.Name.Valid() {
		vdb.Name = payload.Name.String
	}

	if payload.LogoURL1.Valid() {
		vdb.LogoURL1.Valid = true
		vdb.LogoURL1.String = payload.LogoURL1.String
	}

	if payload.LogoURL2.Valid() {
		vdb.LogoURL2.Valid = true
		vdb.LogoURL2.String = payload.LogoURL2.String
	}

	if payload.LogoURL3.Valid() {
		vdb.LogoURL3.Valid = true
		vdb.LogoURL3.String = payload.LogoURL3.String
	}

	if payload.URL.Valid() {
		vdb.URL = payload.URL.String
	}

	if payload.GroupName.Valid() {
		vdb.GroupName = payload.GroupName.String
	}

	if payload.SortOrder.Valid() {
		vdb.SortOrder = int(payload.SortOrder.Int)
	}

	return nil
}

type finalizeFunc func() error

func (ff finalizeFunc) FinalizeFunc() func() error {
	return ff
}

// Ignorable always returns true, otherwise the caller will have to
// bail out immediately
func (ff finalizeFunc) Ignorable() bool {
	return true
}

func (ff finalizeFunc) Error() string {
	return "operation needs finalization"
}

func (v *Sponsor) UploadImagesFromPayload(ctx context.Context, tx *db.Tx, row *db.Sponsor, payload *model.UpdateSponsorRequest) (err error) {
	if pdebug.Enabled {
		g := pdebug.Marker("service.Sponsor.UploadImagesFromPayload").BindError(&err)
		defer g.End()
	}

	// There's nothing to do
	if payload.MultipartForm == nil || payload.MultipartForm.File == nil {
		return nil
	}

	bucketName := v.getMediaBucketName()
	prefix := "http://storage.googleapis.com/" + bucketName

	finalizers := make([]func() error, 0, 3)
	for _, field := range []string{"logo1", "logo2", "logo3"} {
		fhs := payload.MultipartForm.File[field]
		if len(fhs) == 0 {
			continue
		}

		var imgf multipart.File
		imgf, err = fhs[0].Open()
		if err != nil {
			return errors.Wrap(err, "failed to open logo file from multipart form")
		}

		var imgbuf bytes.Buffer
		if _, err := io.Copy(&imgbuf, imgf); err != nil {
			return errors.Wrap(err, "failed to copy logo image data to memory")
		}
		imgtyp := http.DetectContentType(imgbuf.Bytes())

		// Only work with image/png or image/jpeg
		var suffix string
		switch imgtyp {
		case "image/png":
			suffix = "png"
		case "image/jpeg":
			suffix = "jpeg"
		default:
			return errors.Errorf("Unsupported image type %s", imgtyp)
		}

		// TODO: Validate the image
		// TODO: Avoid Google Storage hardcoding?
		// Upload this to a temporary location, then upon successful write to DB
		// rename it to $conference_id/$sponsor_id
		storagecl := v.getStorageClient(ctx)
		tmpname := time.Now().UTC().Format("2006-01-02") + "/" + tools.RandomString(64) + "." + suffix
		wc := storagecl.Bucket(bucketName).Object(tmpname).NewWriter(ctx)
		wc.ContentType = imgtyp
		wc.ACL = []storage.ACLRule{{storage.AllUsers, storage.RoleReader}}

		if pdebug.Enabled {
			pdebug.Printf("Writing '%s' to %s", field, tmpname)
		}

		if _, err := io.Copy(wc, &imgbuf); err != nil {
			return errors.Wrap(err, "failed to write image to temporary location")
		}
		// Note: DO NOT defer wc.Close(), as it's part of the write operation.
		// If wc.Close() does not complete w/o errors. the write failed
		if err := wc.Close(); err != nil {
			return errors.Wrap(err, "failed to write image to temporary location")
		}

		dstname := "conferences/" + row.ConferenceID + "/" + row.EID + "-" + field + "." + suffix
		switch field {
		case "logo1":
			payload.LogoURL1.Set(prefix + "/" + dstname)
		case "logo2":
			payload.LogoURL2.Set(prefix + "/" + dstname)
		case "logo3":
			payload.LogoURL3.Set(prefix + "/" + dstname)
		}

		finalizers = append(finalizers, func() (err error) {
			if pdebug.Enabled {
				g := pdebug.Marker("Finalizer for service.Sponsor.UploadImagesFromPayload").BindError(&err)
				defer g.End()
			}

			src := storagecl.Bucket(bucketName).Object(tmpname)
			dst := storagecl.Bucket(bucketName).Object(dstname)

			if pdebug.Enabled {
				pdebug.Printf("Copying %s to %s", tmpname, dstname)
			}

			attrs, err := src.Attrs(ctx)
			if err != nil {
				return errors.Wrapf(err, "failed to fetch object attrs for '%s'", tmpname)
			}

			if _, err = src.CopyTo(ctx, dst, attrs); err != nil {
				return errors.Wrapf(err, "failed to copy from '%s' to '%s'", tmpname, dstname)
			}

			if pdebug.Enabled {
				pdebug.Printf("Deleting %s", tmpname)
			}
			if err := src.Delete(ctx); err != nil {
				return errors.Wrapf(err, "failed to delete '%s'", tmpname)
			}

			return nil
		})
	}

	if len(finalizers) == 0 {
		return nil
	}

	return finalizeFunc(func() error {
		var g errgroup.Group
		for _, f := range finalizers {
			g.Go(f)
		}
		return g.Wait()
	})
}

func (v *Sponsor) CreateFromPayload(ctx context.Context, tx *db.Tx, payload model.AddSponsorRequest, result *model.Sponsor) (err error) {
	if pdebug.Enabled {
		g := pdebug.Marker("service.Sponsor.CreateFromPayload").BindError(&err)
		defer g.End()
	}

	su := User{}
	if err := su.IsConferenceAdministrator(tx, payload.ConferenceID, payload.UserID); err != nil {
		return errors.Wrap(err, "creating a featured sponsor requires conference administrator privilege")
	}

	vdb := db.Sponsor{}
	if err := v.Create(tx, &vdb, model.CreateSponsorRequest{payload}); err != nil {
		return errors.Wrap(err, "failed to store in database")
	}

	c := model.Sponsor{}
	if err := c.FromRow(vdb); err != nil {
		return errors.Wrap(err, "failed to populate model from database")
	}

	*result = c
	return nil
}

func (v *Sponsor) UpdateFromPayload(ctx context.Context, tx *db.Tx, payload model.UpdateSponsorRequest) (err error) {
	if pdebug.Enabled {
		g := pdebug.Marker("service.Sponsor.UpdateFromPayload").BindError(&err)
		defer g.End()
	}

	vdb := db.Sponsor{}
	if err := vdb.LoadByEID(tx, payload.ID); err != nil {
		return errors.Wrap(err, "failed to load featured sponsor from database")
	}

	su := User{}
	if err := su.IsConferenceAdministrator(tx, vdb.ConferenceID, payload.UserID); err != nil {
		return errors.Wrap(err, "updating a featured sponsor requires conference administrator privilege")
	}

	var uploadErr error
	if uploadErr := v.UploadImagesFromPayload(ctx, tx, &vdb, &payload); !errors.IsIgnorable(uploadErr) {
		return errors.Wrap(uploadErr, "failed to process image uploads")
	}

	if err := v.Update(tx, &vdb, payload); err != nil {
		return errors.Wrap(err, "failed to load featured sponsor from database")
	}

	if cb, ok := errors.IsFinalizationRequired(uploadErr); ok {
		return errors.Wrap(cb(), "failed to finalize image upload")
	}
	return nil

}

func (v *Sponsor) DeleteFromPayload(tx *db.Tx, payload model.DeleteSponsorRequest) error {
	var m db.Sponsor
	if err := m.LoadByEID(tx, payload.ID); err != nil {
		return errors.Wrap(err, "failed to load featured sponsor from database")
	}

	su := User{}
	if err := su.IsConferenceAdministrator(tx, m.ConferenceID, payload.UserID); err != nil {
		return errors.Wrap(err, "deleting venues require administrator privileges")
	}

	return errors.Wrap(v.Delete(tx, m.EID), "failed to delete from database")
}

func (v *Sponsor) ListFromPayload(tx *db.Tx, result *model.SponsorList, payload model.ListSponsorsRequest) error {
	var vdbl db.SponsorList
	if err := vdbl.LoadByConferenceSinceEID(tx, payload.ConferenceID, payload.Since.String, int(payload.Limit.Int)); err != nil {
		return errors.Wrap(err, "failed to load featured sponsor from database")
	}

	l := make(model.SponsorList, len(vdbl))
	for i, vdb := range vdbl {
		if err := (l[i]).FromRow(vdb); err != nil {
			return errors.Wrap(err, "failed to populate model from database")
		}

		if err := v.Decorate(tx, &l[i], payload.Lang.String); err != nil {
			return errors.Wrap(err, "failed to decorate venue with associated data")
		}
	}

	*result = l
	return nil
}

func (v *Sponsor) Decorate(tx *db.Tx, sponsor *model.Sponsor, lang string) error {
	if sponsor.LogoURL1 == "" {
		sponsor.LogoURL1 = "http://storage.googleapis.com/media-builderscon-1248/system/nophoto_600.png"
	}

	if sponsor.LogoURL2 == "" {
		sponsor.LogoURL2 = "http://storage.googleapis.com/media-builderscon-1248/system/nophoto_400.png"
	}

	if sponsor.LogoURL3 == "" {
		sponsor.LogoURL3 = "http://storage.googleapis.com/media-builderscon-1248/system/nophoto_200.png"
	}

	if lang == "" {
		return nil
	}

	if err := v.ReplaceL10NStrings(tx, sponsor, lang); err != nil {
		return errors.Wrap(err, "failed to replace L10N strings")
	}

	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/github/hub/cmd"
	"github.com/github/hub/git"
	"github.com/github/hub/ui"
	"github.com/github/hub/utils"
)

var cmdHelp = &Command{
	Run:          runHelp,
	GitExtension: true,
	Usage:        "help [<COMMAND>]",
	Long:         `Show the help page for a command.`,
}

func init() {
	CmdRunner.Use(cmdHelp, "--help")
}

func runHelp(helpCmd *Command, args *Args) {
	if args.IsParamsEmpty() {
		printUsage()
		os.Exit(0)
	}

	command := args.FirstParam()

	if command == "hub" {
		man := cmd.New("man")
		man.WithArg("hub")
		err := man.Run()
		if err == nil {
			os.Exit(0)
		} else {
			os.Exit(1)
		}
	}

	c := CmdRunner.Lookup(command)
	if c != nil && !c.GitExtension {
		ui.Println(c.HelpText())
		os.Exit(0)
	} else if c == nil {
		if args.HasFlags("-a", "--all") {
			args.After("echo", "\nhub custom commands\n")
			args.After("echo", " ", strings.Join(customCommands(), "  "))
		}
	}
}

func customCommands() []string {
	cmds := []string{}
	for n, c := range CmdRunner.All() {
		if !c.GitExtension && !strings.HasPrefix(n, "--") {
			cmds = append(cmds, n)
		}
	}

	sort.Sort(sort.StringSlice(cmds))

	return cmds
}

var helpText = `
These GitHub commands are provided by hub:

   pull-request   Open a pull request on GitHub
   fork           Make a fork of a remote repository on GitHub and add as remote
   create         Create this repository on GitHub and add GitHub as origin
   browse         Open a GitHub page in the default browser
   compare        Open a compare page on GitHub
   release        List or create releases (beta)
   issue          List or create issues (beta)
   ci-status      Show the CI status of a commit
`

func printUsage() {
	err := git.ForwardGitHelp()
	utils.Check(err)
	fmt.Print(helpText)
}
<commit_msg>Allow displaying help for extended hub commands: `hub help hub-<CMD>`<commit_after>package commands

import (
	"fmt"
	"os"
	"sort"
	"strings"

	"github.com/github/hub/cmd"
	"github.com/github/hub/git"
	"github.com/github/hub/ui"
	"github.com/github/hub/utils"
)

var cmdHelp = &Command{
	Run:          runHelp,
	GitExtension: true,
	Usage:        "help [<COMMAND>]",
	Long:         `Show the help page for a command.`,
}

func init() {
	CmdRunner.Use(cmdHelp, "--help")
}

func runHelp(helpCmd *Command, args *Args) {
	if args.IsParamsEmpty() {
		printUsage()
		os.Exit(0)
	}

	if args.HasFlags("-a", "--all") {
		args.After("echo", "\nhub custom commands\n")
		args.After("echo", " ", strings.Join(customCommands(), "  "))
		return
	}

	command := args.FirstParam()

	if command == "hub" {
		man := cmd.New("man")
		man.WithArg("hub")
		err := man.Run()
		if err == nil {
			os.Exit(0)
		} else {
			os.Exit(1)
		}
	}

	if c := lookupCmd(command); c != nil {
		ui.Println(c.HelpText())
		os.Exit(0)
	}
}

func lookupCmd(name string) *Command {
	if strings.HasPrefix(name, "hub-") {
		return CmdRunner.Lookup(strings.TrimPrefix(name, "hub-"))
	} else {
		cmd := CmdRunner.Lookup(name)
		if cmd != nil && !cmd.GitExtension {
			return cmd
		} else {
			return nil
		}
	}
}

func customCommands() []string {
	cmds := []string{}
	for n, c := range CmdRunner.All() {
		if !c.GitExtension && !strings.HasPrefix(n, "--") {
			cmds = append(cmds, n)
		}
	}

	sort.Sort(sort.StringSlice(cmds))

	return cmds
}

var helpText = `
These GitHub commands are provided by hub:

   pull-request   Open a pull request on GitHub
   fork           Make a fork of a remote repository on GitHub and add as remote
   create         Create this repository on GitHub and add GitHub as origin
   browse         Open a GitHub page in the default browser
   compare        Open a compare page on GitHub
   release        List or create releases (beta)
   issue          List or create issues (beta)
   ci-status      Show the CI status of a commit
`

func printUsage() {
	err := git.ForwardGitHelp()
	utils.Check(err)
	fmt.Print(helpText)
}
<|endoftext|>


<commit_before>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	"container/list"
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"

	"github.com/syndtr/goleveldb/leveldb/iterator"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/util"
)

type snapshotElement struct {
	seq uint64
	ref int
	e   *list.Element
}

// Acquires a snapshot, based on latest sequence.
func (db *DB) acquireSnapshot() *snapshotElement {
	db.snapsMu.Lock()
	defer db.snapsMu.Unlock()

	seq := db.getSeq()

	if e := db.snapsList.Back(); e != nil {
		se := e.Value.(*snapshotElement)
		if se.seq == seq {
			se.ref++
			return se
		} else if seq < se.seq {
			panic("leveldb: sequence number is not increasing")
		}
	}
	se := &snapshotElement{seq: seq, ref: 1}
	se.e = db.snapsList.PushBack(se)
	return se
}

// Releases given snapshot element.
func (db *DB) releaseSnapshot(se *snapshotElement) {
	db.snapsMu.Lock()
	defer db.snapsMu.Unlock()

	se.ref--
	if se.ref == 0 {
		db.snapsList.Remove(se.e)
		se.e = nil
	} else if se.ref < 0 {
		panic("leveldb: Snapshot: negative element reference")
	}
}

// Gets minimum sequence that not being snapshotted.
func (db *DB) minSeq() uint64 {
	db.snapsMu.Lock()
	defer db.snapsMu.Unlock()

	if e := db.snapsList.Front(); e != nil {
		return e.Value.(*snapshotElement).seq
	}

	return db.getSeq()
}

// Snapshot is a DB snapshot.
type Snapshot struct {
	db       *DB
	elem     *snapshotElement
	mu       sync.RWMutex
	released bool
}

// Creates new snapshot object.
func (db *DB) newSnapshot() *Snapshot {
	snap := &Snapshot{
		db:   db,
		elem: db.acquireSnapshot(),
	}
	atomic.AddInt32(&db.aliveSnaps, 1)
	runtime.SetFinalizer(snap, (*Snapshot).Release)
	return snap
}

func (snap *Snapshot) String() string {
	return fmt.Sprintf("leveldb.Snapshot{%d}", snap.elem.seq)
}

// Get gets the value for the given key. It returns ErrNotFound if
// the DB does not contains the key.
//
// The caller should not modify the contents of the returned slice, but
// it is safe to modify the contents of the argument after Get returns.
func (snap *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
	err = snap.db.ok()
	if err != nil {
		return
	}
	snap.mu.RLock()
	defer snap.mu.RUnlock()
	if snap.released {
		err = ErrSnapshotReleased
		return
	}
	return snap.db.get(nil, nil, key, snap.elem.seq, ro)
}

// Has returns true if the DB does contains the given key.
//
// It is safe to modify the contents of the argument after Get returns.
func (snap *Snapshot) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
	err = snap.db.ok()
	if err != nil {
		return
	}
	snap.mu.RLock()
	defer snap.mu.RUnlock()
	if snap.released {
		err = ErrSnapshotReleased
		return
	}
	return snap.db.has(nil, nil, key, snap.elem.seq, ro)
}

// NewIterator returns an iterator for the snapshot of the underlying DB.
// The returned iterator is not safe for concurrent use, but it is safe to use
// multiple iterators concurrently, with each in a dedicated goroutine.
// It is also safe to use an iterator concurrently with modifying its
// underlying DB. The resultant key/value pairs are guaranteed to be
// consistent.
//
// Slice allows slicing the iterator to only contains keys in the given
// range. A nil Range.Start is treated as a key before all keys in the
// DB. And a nil Range.Limit is treated as a key after all keys in
// the DB.
//
// WARNING: Any slice returned by interator (e.g. slice returned by calling
// Iterator.Key() or Iterator.Value() methods), its content should not be
// modified unless noted otherwise.
//
// The iterator must be released after use, by calling Release method.
// Releasing the snapshot doesn't mean releasing the iterator too, the
// iterator would be still valid until released.
//
// Also read Iterator documentation of the leveldb/iterator package.
func (snap *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
	if err := snap.db.ok(); err != nil {
		return iterator.NewEmptyIterator(err)
	}
	snap.mu.Lock()
	defer snap.mu.Unlock()
	if snap.released {
		return iterator.NewEmptyIterator(ErrSnapshotReleased)
	}
	// Since iterator already hold version ref, it doesn't need to
	// hold snapshot ref.
	return snap.db.newIterator(nil, nil, snap.elem.seq, slice, ro)
}

// Release releases the snapshot. This will not release any returned
// iterators, the iterators would still be valid until released or the
// underlying DB is closed.
//
// Other methods should not be called after the snapshot has been released.
func (snap *Snapshot) Release() {
	snap.mu.Lock()
	defer snap.mu.Unlock()

	if !snap.released {
		// Clear the finalizer.
		runtime.SetFinalizer(snap, nil)

		snap.released = true
		snap.db.releaseSnapshot(snap.elem)
		atomic.AddInt32(&snap.db.aliveSnaps, -1)
		snap.db = nil
		snap.elem = nil
	}
}
<commit_msg>leveldb: Don't crash when using a released snapshot (#320)<commit_after>// Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
// All rights reserved.
//
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package leveldb

import (
	"container/list"
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"

	"github.com/syndtr/goleveldb/leveldb/iterator"
	"github.com/syndtr/goleveldb/leveldb/opt"
	"github.com/syndtr/goleveldb/leveldb/util"
)

type snapshotElement struct {
	seq uint64
	ref int
	e   *list.Element
}

// Acquires a snapshot, based on latest sequence.
func (db *DB) acquireSnapshot() *snapshotElement {
	db.snapsMu.Lock()
	defer db.snapsMu.Unlock()

	seq := db.getSeq()

	if e := db.snapsList.Back(); e != nil {
		se := e.Value.(*snapshotElement)
		if se.seq == seq {
			se.ref++
			return se
		} else if seq < se.seq {
			panic("leveldb: sequence number is not increasing")
		}
	}
	se := &snapshotElement{seq: seq, ref: 1}
	se.e = db.snapsList.PushBack(se)
	return se
}

// Releases given snapshot element.
func (db *DB) releaseSnapshot(se *snapshotElement) {
	db.snapsMu.Lock()
	defer db.snapsMu.Unlock()

	se.ref--
	if se.ref == 0 {
		db.snapsList.Remove(se.e)
		se.e = nil
	} else if se.ref < 0 {
		panic("leveldb: Snapshot: negative element reference")
	}
}

// Gets minimum sequence that not being snapshotted.
func (db *DB) minSeq() uint64 {
	db.snapsMu.Lock()
	defer db.snapsMu.Unlock()

	if e := db.snapsList.Front(); e != nil {
		return e.Value.(*snapshotElement).seq
	}

	return db.getSeq()
}

// Snapshot is a DB snapshot.
type Snapshot struct {
	db       *DB
	elem     *snapshotElement
	mu       sync.RWMutex
	released bool
}

// Creates new snapshot object.
func (db *DB) newSnapshot() *Snapshot {
	snap := &Snapshot{
		db:   db,
		elem: db.acquireSnapshot(),
	}
	atomic.AddInt32(&db.aliveSnaps, 1)
	runtime.SetFinalizer(snap, (*Snapshot).Release)
	return snap
}

func (snap *Snapshot) String() string {
	return fmt.Sprintf("leveldb.Snapshot{%d}", snap.elem.seq)
}

// Get gets the value for the given key. It returns ErrNotFound if
// the DB does not contains the key.
//
// The caller should not modify the contents of the returned slice, but
// it is safe to modify the contents of the argument after Get returns.
func (snap *Snapshot) Get(key []byte, ro *opt.ReadOptions) (value []byte, err error) {
	snap.mu.RLock()
	defer snap.mu.RUnlock()
	if snap.released {
		err = ErrSnapshotReleased
		return
	}
	err = snap.db.ok()
	if err != nil {
		return
	}
	return snap.db.get(nil, nil, key, snap.elem.seq, ro)
}

// Has returns true if the DB does contains the given key.
//
// It is safe to modify the contents of the argument after Get returns.
func (snap *Snapshot) Has(key []byte, ro *opt.ReadOptions) (ret bool, err error) {
	snap.mu.RLock()
	defer snap.mu.RUnlock()
	if snap.released {
		err = ErrSnapshotReleased
		return
	}
	err = snap.db.ok()
	if err != nil {
		return
	}
	return snap.db.has(nil, nil, key, snap.elem.seq, ro)
}

// NewIterator returns an iterator for the snapshot of the underlying DB.
// The returned iterator is not safe for concurrent use, but it is safe to use
// multiple iterators concurrently, with each in a dedicated goroutine.
// It is also safe to use an iterator concurrently with modifying its
// underlying DB. The resultant key/value pairs are guaranteed to be
// consistent.
//
// Slice allows slicing the iterator to only contains keys in the given
// range. A nil Range.Start is treated as a key before all keys in the
// DB. And a nil Range.Limit is treated as a key after all keys in
// the DB.
//
// WARNING: Any slice returned by interator (e.g. slice returned by calling
// Iterator.Key() or Iterator.Value() methods), its content should not be
// modified unless noted otherwise.
//
// The iterator must be released after use, by calling Release method.
// Releasing the snapshot doesn't mean releasing the iterator too, the
// iterator would be still valid until released.
//
// Also read Iterator documentation of the leveldb/iterator package.
func (snap *Snapshot) NewIterator(slice *util.Range, ro *opt.ReadOptions) iterator.Iterator {
	snap.mu.Lock()
	defer snap.mu.Unlock()
	if snap.released {
		return iterator.NewEmptyIterator(ErrSnapshotReleased)
	}
	if err := snap.db.ok(); err != nil {
		return iterator.NewEmptyIterator(err)
	}
	// Since iterator already hold version ref, it doesn't need to
	// hold snapshot ref.
	return snap.db.newIterator(nil, nil, snap.elem.seq, slice, ro)
}

// Release releases the snapshot. This will not release any returned
// iterators, the iterators would still be valid until released or the
// underlying DB is closed.
//
// Other methods should not be called after the snapshot has been released.
func (snap *Snapshot) Release() {
	snap.mu.Lock()
	defer snap.mu.Unlock()

	if !snap.released {
		// Clear the finalizer.
		runtime.SetFinalizer(snap, nil)

		snap.released = true
		snap.db.releaseSnapshot(snap.elem)
		atomic.AddInt32(&snap.db.aliveSnaps, -1)
		snap.db = nil
		snap.elem = nil
	}
}
<|endoftext|>