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

/*
Govulncheck reports known vulnerabilities that affect Go code. It uses static
analysis of source code or a binary's symbol table to narrow down reports to
only those that could affect the application.

By default, govulncheck makes requests to the Go vulnerability database at
https://vuln.go.dev. Requests to the vulnerability database contain only module
paths, not code or other properties of your program. See
https://vuln.go.dev/privacy.html for more. Set the GOVULNDB environment
variable to specify a different database, which must implement the
specification at https://go.dev/security/vuln/database.

Govulncheck looks for vulnerabilities in Go programs using a specific build
configuration. For analyzing source code, that configuration is the operating
system, architecture, and Go version specified by GOOS, GOARCH, and the “go”
command found on the PATH. For binaries, the build configuration is the one
used to build the binary. Note that different build configurations may have
different known vulnerabilities. For example, a dependency with a
Windows-specific vulnerability will not be reported for a Linux build.

Govulncheck must be built with Go version 1.18 or later.

# Usage

To analyze source code, run govulncheck from the module directory, using the
same package path syntax that the go command uses:

	$ cd my-module
	$ govulncheck ./...

If no vulnerabilities are found, govulncheck will display a short message. If
there are vulnerabilities, each is displayed briefly, with a summary of a call
stack. The summary shows in brief how the package calls a vulnerable function.
For example, it might say

	main.go:[line]:[column]: mypackage.main calls golang.org/x/text/language.Parse

For a more detailed call path that resembles Go panic stack traces, use the -v flag.

To control which files are processed, use the -tags flag to provide a
comma-separated list of build tags, and the -test flag to indicate that test
files should be included.

To run govulncheck on a compiled binary, pass it the path to the binary file:

	$ govulncheck $HOME/go/bin/my-go-program

Govulncheck uses the binary's symbol information to find mentions of vulnerable
functions. Its output omits call stacks, which require source code analysis.

Govulncheck exits successfully (exit code 0) if there are no vulnerabilities,
and exits unsuccessfully if there are.

# Flags

A few flags control govulncheck's behavior.

The -v flag causes govulncheck to output more information about call stacks
when run on source. It has no effect when run on a binary.

The -json flag causes govulncheck to print its output as a JSON object
corresponding to the type [golang.org/x/vuln/vulncheck.Result].

The -tags flag accepts a comma-separated list of build tags to control which
files should be included in loaded packages for source analysis.

The -test flag causes govulncheck to include test files in the source analysis.

# Limitations

Govulncheck uses [golang.org/x/vuln/vulncheck], which has these limitations:

  - Govulncheck analyzes function pointer and interface calls conservatively,
    which may result in false positives or inaccurate call stacks in some cases.
  - Calls to functions made using package reflect are not visible to static
    analysis. Vulnerable code reachable only through those calls will not be
    reported.
  - Because Go binaries do not contain detailed call information, govulncheck
    cannot show the call graphs for detected vulnerabilities. It may also
    report false positives for code that is in the binary but unreachable.
  - There is no support for silencing vulnerability findings.
  - Govulncheck only reads binaries compiled with Go 1.18 and later.

# Feedback

Govulncheck is an experimental tool under active development. To share
feedback, see https://go.dev/security/vuln#feedback.
*/
package main
<commit_msg>cmd/govulncheck: document that we only report for current GOOS/GOARCH<commit_after>// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Govulncheck reports known vulnerabilities that affect Go code. It uses static
analysis of source code or a binary's symbol table to narrow down reports to
only those that could affect the application.

By default, govulncheck makes requests to the Go vulnerability database at
https://vuln.go.dev. Requests to the vulnerability database contain only module
paths, not code or other properties of your program. See
https://vuln.go.dev/privacy.html for more. Set the GOVULNDB environment
variable to specify a different database, which must implement the
specification at https://go.dev/security/vuln/database.

Govulncheck looks for vulnerabilities in Go programs using a specific build
configuration. For analyzing source code, that configuration is the operating
system, architecture, and Go version specified by GOOS, GOARCH, and the “go”
command found on the PATH. For binaries, the build configuration is the one
used to build the binary. Note that different build configurations may have
different known vulnerabilities. For example, a dependency with a
Windows-specific vulnerability will not be reported for a Linux build.

Govulncheck must be built with Go version 1.18 or later.

# Usage

To analyze source code, run govulncheck from the module directory, using the
same package path syntax that the go command uses:

	$ cd my-module
	$ govulncheck ./...

If no vulnerabilities are found, govulncheck will display a short message. If
there are vulnerabilities, each is displayed briefly, with a summary of a call
stack. The summary shows in brief how the package calls a vulnerable function.
For example, it might say

	main.go:[line]:[column]: mypackage.main calls golang.org/x/text/language.Parse

For a more detailed call path that resembles Go panic stack traces, use the -v flag.

To control which files are processed, use the -tags flag to provide a
comma-separated list of build tags, and the -test flag to indicate that test
files should be included.

To run govulncheck on a compiled binary, pass it the path to the binary file:

	$ govulncheck $HOME/go/bin/my-go-program

Govulncheck uses the binary's symbol information to find mentions of vulnerable
functions. Its output omits call stacks, which require source code analysis.

Govulncheck exits successfully (exit code 0) if there are no vulnerabilities,
and exits unsuccessfully if there are.

# Flags

A few flags control govulncheck's behavior.

The -v flag causes govulncheck to output more information about call stacks
when run on source. It has no effect when run on a binary.

The -json flag causes govulncheck to print its output as a JSON object
corresponding to the type [golang.org/x/vuln/vulncheck.Result].

The -tags flag accepts a comma-separated list of build tags to control which
files should be included in loaded packages for source analysis.

The -test flag causes govulncheck to include test files in the source analysis.

# Limitations

Govulncheck uses [golang.org/x/vuln/vulncheck], which has these limitations:

  - Govulncheck analyzes function pointer and interface calls conservatively,
    which may result in false positives or inaccurate call stacks in some cases.
  - Calls to functions made using package reflect are not visible to static
    analysis. Vulnerable code reachable only through those calls will not be
    reported.
  - Because Go binaries do not contain detailed call information, govulncheck
    cannot show the call graphs for detected vulnerabilities. It may also
    report false positives for code that is in the binary but unreachable.
  - There is no support for silencing vulnerability findings.
  - Govulncheck only reads binaries compiled with Go 1.18 and later.
  - Govulncheck only reports vulnerabilities that apply to the current build
    configuration (GOOS/GOARCH settings). For example, a vulnerability that
    only applies to Linux will not be reported if govulncheck is run on a
    Windows machine. See https://go.dev/issue/54841 for details.

# Feedback

Govulncheck is an experimental tool under active development. To share
feedback, see https://go.dev/security/vuln#feedback.
*/
package main
<|endoftext|>


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

package main

import (
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"sync"

	"github.com/jroimartin/orujo"
	olog "github.com/jroimartin/orujo-handlers/log"
)

type server struct {
	addr     string
	cmdDir   string
	logger   *log.Logger
	commands map[string]*command
	mutex    sync.RWMutex
}

func newServer(addr, cmdDir string) *server {
	s := &server{
		addr:   addr,
		cmdDir: cmdDir,
		logger: log.New(os.Stdout, "[intelengine] ", log.LstdFlags),
	}
	return s
}

func (s *server) start() error {
	if s.addr == "" || s.cmdDir == "" {
		return errors.New("server.addr and server.cmdDir cannot be empty strings")
	}

	s.refreshCommands()

	websrv := orujo.NewServer(s.addr)

	logHandler := olog.NewLogHandler(s.logger, logLine)

	websrv.RouteDefault(http.NotFoundHandler(), orujo.M(logHandler))

	websrv.Route(`^/cmd/refresh$`,
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			s.refreshCommands()
		}),
		http.HandlerFunc(s.listCommandsHandler),
		orujo.M(logHandler)).Methods("GET")

	websrv.Route(`^/cmd/list$`,
		http.HandlerFunc(s.listCommandsHandler),
		orujo.M(logHandler)).Methods("GET")

	websrv.Route(`^/cmd/exec/\w+$`,
		http.HandlerFunc(s.runCommandHandler),
		orujo.M(logHandler)).Methods("POST")

	return websrv.ListenAndServe()
}

func (s *server) refreshCommands() {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	s.commands = make(map[string]*command)

	files, err := ioutil.ReadDir(s.cmdDir)
	if err != nil {
		s.logger.Println("refreshCommands warning:", err)
		return
	}

	for _, f := range files {
		if f.IsDir() || path.Ext(f.Name()) != cmdExt {
			continue
		}

		filename := path.Join(s.cmdDir, f.Name())
		cmd, err := newCommand(filename)
		if err != nil {
			s.logger.Println("refreshCommands warning:", err)
			return
		}

		s.commands[cmd.Name] = cmd
		s.logger.Println("command registered:", cmd.Name)
	}
}

func (s *server) command(name string) *command {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	for _, cmd := range s.commands {
		if cmd.Name == name {
			return cmd
		}
	}
	return nil
}

const logLine = `{{.Req.RemoteAddr}} - {{.Req.Method}} {{.Req.RequestURI}}
{{range  $err := .Errors}}  Err: {{$err}}
{{end}}`
<commit_msg>cmd/intelsrv: Do not stop loading commands on error<commit_after>// Copyright 2014 The intelengine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"errors"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"sync"

	"github.com/jroimartin/orujo"
	olog "github.com/jroimartin/orujo-handlers/log"
)

type server struct {
	addr     string
	cmdDir   string
	logger   *log.Logger
	commands map[string]*command
	mutex    sync.RWMutex
}

func newServer(addr, cmdDir string) *server {
	s := &server{
		addr:   addr,
		cmdDir: cmdDir,
		logger: log.New(os.Stdout, "[intelengine] ", log.LstdFlags),
	}
	return s
}

func (s *server) start() error {
	if s.addr == "" || s.cmdDir == "" {
		return errors.New("server.addr and server.cmdDir cannot be empty strings")
	}

	s.refreshCommands()

	websrv := orujo.NewServer(s.addr)

	logHandler := olog.NewLogHandler(s.logger, logLine)

	websrv.RouteDefault(http.NotFoundHandler(), orujo.M(logHandler))

	websrv.Route(`^/cmd/refresh$`,
		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			s.refreshCommands()
		}),
		http.HandlerFunc(s.listCommandsHandler),
		orujo.M(logHandler)).Methods("GET")

	websrv.Route(`^/cmd/list$`,
		http.HandlerFunc(s.listCommandsHandler),
		orujo.M(logHandler)).Methods("GET")

	websrv.Route(`^/cmd/exec/\w+$`,
		http.HandlerFunc(s.runCommandHandler),
		orujo.M(logHandler)).Methods("POST")

	return websrv.ListenAndServe()
}

func (s *server) refreshCommands() {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	s.commands = make(map[string]*command)

	files, err := ioutil.ReadDir(s.cmdDir)
	if err != nil {
		s.logger.Println("refreshCommands warning:", err)
		return
	}

	for _, f := range files {
		if f.IsDir() || path.Ext(f.Name()) != cmdExt {
			continue
		}

		filename := path.Join(s.cmdDir, f.Name())
		cmd, err := newCommand(filename)
		if err != nil {
			s.logger.Printf("refreshCommands warning (%v): %v\n", f.Name(), err)
			continue
		}

		s.commands[cmd.Name] = cmd
		s.logger.Println("command registered:", cmd.Name)
	}
}

func (s *server) command(name string) *command {
	s.mutex.RLock()
	defer s.mutex.RUnlock()

	for _, cmd := range s.commands {
		if cmd.Name == name {
			return cmd
		}
	}
	return nil
}

const logLine = `{{.Req.RemoteAddr}} - {{.Req.Method}} {{.Req.RequestURI}}
{{range  $err := .Errors}}  Err: {{$err}}
{{end}}`
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"io"

	"bytes"

	"github.com/spf13/cobra"
	"k8s.io/kops/cmd/kops/util"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	"k8s.io/kubernetes/pkg/util/i18n"
)

const boilerPlate = `
# Copyright 2016 The Kubernetes 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.
`

var (
	completion_shells = map[string]func(out io.Writer, cmd *cobra.Command) error{
		"bash": runCompletionBash,
		"zsh":  runCompletionZsh,
	}
)

type CompletionOptions struct {
	Shell string
}

var (
	completion_long = templates.LongDesc(i18n.T(`
	Output shell completion code for the specified shell (bash or zsh).
	The shell code must be evalutated to provide interactive
	completion of kops commands.  This can be done by sourcing it from
	the .bash_profile.

	Note: this requires the bash-completion framework, which is not installed
	by default on Mac. Once installed, bash_completion must be evaluated.  This can be done by adding the
	following line to the .bash_profile

	Note for zsh users: zsh completions are only supported in versions of zsh >= 5.2`))

	completion_example = templates.Examples(i18n.T(`
	# For OSX users install bash completion using homebrew
	brew install bash-completion
	source $(brew --prefix)/etc/bash_completion

	# Bash completion support
	printf "source $(brew --prefix)/etc/bash_completion\n" >> $HOME/.bash_profile
	source $HOME/.bash_profile
	source <(kops completion bash)
	kops completion bash > ~/.kops/completion.bash.inc
	chmod +x $HOME/.kops/completion.bash.inc

	# kops shell completion
	printf "$HOME/.kops/completion.bash.inc\n" >> $HOME/.bash_profile
	source $HOME/.bash_profile

	# Load the kops completion code for zsh[1] into the current shell
	source <(kops completion zsh)`))

	completion_short = i18n.T("Output shell completion code for the given shell (bash or zsh).")
)

func NewCmdCompletion(f *util.Factory, out io.Writer) *cobra.Command {
	options := &CompletionOptions{}

	cmd := &cobra.Command{
		Use:     "completion",
		Short:   completion_short,
		Long:    completion_long,
		Example: completion_example,
		Run: func(cmd *cobra.Command, args []string) {
			err := RunCompletion(f, cmd, args, out, options)
			if err != nil {
				exitWithError(err)
			}
		},
	}

	cmd.Flags().StringVar(&options.Shell, "shell", "", "target shell (bash).")

	return cmd
}

func RunCompletion(f *util.Factory, cmd *cobra.Command, args []string, out io.Writer, c *CompletionOptions) error {
	if len(args) != 0 {

		if c.Shell != "" {
			return fmt.Errorf("cannot specify shell both as a flag and a positional argument")
		}
		c.Shell = args[0]
	}

	if c.Shell == "" {
		return fmt.Errorf("shell is required")
	}

	run, found := completion_shells[args[0]]
	if !found {
		return fmt.Errorf("Unsupported shell type %q.", args[0])
	}

	if _, err := out.Write([]byte(boilerPlate)); err != nil {
		return err
	}

	return run(out, cmd.Parent())
}

func runCompletionBash(out io.Writer, cmd *cobra.Command) error {
	return cmd.GenBashCompletion(out)
}

func runCompletionZsh(out io.Writer, cmd *cobra.Command) error {
	zsh_initialization := `
__kops_bash_source() {
	alias shopt=':'
	alias _expand=_bash_expand
	alias _complete=_bash_comp
	emulate -L sh
	setopt kshglob noshglob braceexpand
	source "$@"
}
__kops_type() {
	# -t is not supported by zsh
	if [ "$1" == "-t" ]; then
		shift
		# fake Bash 4 to disable "complete -o nospace". Instead
		# "compopt +-o nospace" is used in the code to toggle trailing
		# spaces. We don't support that, but leave trailing spaces on
		# all the time
		if [ "$1" = "__kops_compopt" ]; then
			echo builtin
			return 0
		fi
	fi
	type "$@"
}
__kops_compgen() {
	local completions w
	completions=( $(compgen "$@") ) || return $?
	# filter by given word as prefix
	while [[ "$1" = -* && "$1" != -- ]]; do
		shift
		shift
	done
	if [[ "$1" == -- ]]; then
		shift
	fi
	for w in "${completions[@]}"; do
		if [[ "${w}" = "$1"* ]]; then
			echo "${w}"
		fi
	done
}
__kops_compopt() {
	true # don't do anything. Not supported by bashcompinit in zsh
}
__kops_declare() {
	if [ "$1" == "-F" ]; then
		whence -w "$@"
	else
		builtin declare "$@"
	fi
}
__kops_ltrim_colon_completions()
{
	if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
		# Remove colon-word prefix from COMPREPLY items
		local colon_word=${1%${1##*:}}
		local i=${#COMPREPLY[*]}
		while [[ $((--i)) -ge 0 ]]; do
			COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
		done
	fi
}
__kops_get_comp_words_by_ref() {
	cur="${COMP_WORDS[COMP_CWORD]}"
	prev="${COMP_WORDS[${COMP_CWORD}-1]}"
	words=("${COMP_WORDS[@]}")
	cword=("${COMP_CWORD[@]}")
}
__kops_filedir() {
	local RET OLD_IFS w qw
	__debug "_filedir $@ cur=$cur"
	if [[ "$1" = \~* ]]; then
		# somehow does not work. Maybe, zsh does not call this at all
		eval echo "$1"
		return 0
	fi
	OLD_IFS="$IFS"
	IFS=$'\n'
	if [ "$1" = "-d" ]; then
		shift
		RET=( $(compgen -d) )
	else
		RET=( $(compgen -f) )
	fi
	IFS="$OLD_IFS"
	IFS="," __debug "RET=${RET[@]} len=${#RET[@]}"
	for w in ${RET[@]}; do
		if [[ ! "${w}" = "${cur}"* ]]; then
			continue
		fi
		if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
			qw="$(__kops_quote "${w}")"
			if [ -d "${w}" ]; then
				COMPREPLY+=("${qw}/")
			else
				COMPREPLY+=("${qw}")
			fi
		fi
	done
}
__kops_quote() {
    if [[ $1 == \'* || $1 == \"* ]]; then
        # Leave out first character
        printf %q "${1:1}"
    else
    	printf %q "$1"
    fi
}
autoload -U +X bashcompinit && bashcompinit
# use word boundary patterns for BSD or GNU sed
LWORD='[[:<:]]'
RWORD='[[:>:]]'
if sed --help 2>&1 | grep -q GNU; then
	LWORD='\<'
	RWORD='\>'
fi
__kops_convert_bash_to_zsh() {
	sed \
	-e 's/declare -F/whence -w/' \
	-e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \
	-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
	-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
	-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
	-e "s/${LWORD}_filedir${RWORD}/__kops_filedir/g" \
	-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__kops_get_comp_words_by_ref/g" \
	-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__kops_ltrim_colon_completions/g" \
	-e "s/${LWORD}compgen${RWORD}/__kops_compgen/g" \
	-e "s/${LWORD}compopt${RWORD}/__kops_compopt/g" \
	-e "s/${LWORD}declare${RWORD}/__kops_declare/g" \
	-e "s/\\\$(type${RWORD}/\$(__kops_type/g" \
	<<'BASH_COMPLETION_EOF'
`
	out.Write([]byte(zsh_initialization))

	buf := new(bytes.Buffer)
	cmd.GenBashCompletion(buf)
	out.Write(buf.Bytes())

	zsh_tail := `
BASH_COMPLETION_EOF
}
__kops_bash_source <(__kops_convert_bash_to_zsh)
`
	out.Write([]byte(zsh_tail))
	return nil
}
<commit_msg>fixing bug with using shell flag<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"fmt"
	"io"

	"bytes"

	"github.com/spf13/cobra"
	"k8s.io/kops/cmd/kops/util"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	"k8s.io/kubernetes/pkg/util/i18n"
)

const boilerPlate = `
# Copyright 2016 The Kubernetes 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.
`

var (
	completion_shells = map[string]func(out io.Writer, cmd *cobra.Command) error{
		"bash": runCompletionBash,
		"zsh":  runCompletionZsh,
	}
)

type CompletionOptions struct {
	Shell string
}

var (
	completion_long = templates.LongDesc(i18n.T(`
	Output shell completion code for the specified shell (bash or zsh).
	The shell code must be evalutated to provide interactive
	completion of kops commands.  This can be done by sourcing it from
	the .bash_profile.

	Note: this requires the bash-completion framework, which is not installed
	by default on Mac. Once installed, bash_completion must be evaluated.  This can be done by adding the
	following line to the .bash_profile

	Note for zsh users: zsh completions are only supported in versions of zsh >= 5.2`))

	completion_example = templates.Examples(i18n.T(`
	# For OSX users install bash completion using homebrew
	brew install bash-completion
	source $(brew --prefix)/etc/bash_completion

	# Bash completion support
	printf "source $(brew --prefix)/etc/bash_completion\n" >> $HOME/.bash_profile
	source $HOME/.bash_profile
	source <(kops completion bash)
	kops completion bash > ~/.kops/completion.bash.inc
	chmod +x $HOME/.kops/completion.bash.inc

	# kops shell completion
	printf "$HOME/.kops/completion.bash.inc\n" >> $HOME/.bash_profile
	source $HOME/.bash_profile

	# Load the kops completion code for zsh[1] into the current shell
	source <(kops completion zsh)`))

	completion_short = i18n.T("Output shell completion code for the given shell (bash or zsh).")
)

func NewCmdCompletion(f *util.Factory, out io.Writer) *cobra.Command {
	options := &CompletionOptions{}

	cmd := &cobra.Command{
		Use:     "completion",
		Short:   completion_short,
		Long:    completion_long,
		Example: completion_example,
		Run: func(cmd *cobra.Command, args []string) {
			err := RunCompletion(f, cmd, args, out, options)
			if err != nil {
				exitWithError(err)
			}
		},
	}

	cmd.Flags().StringVar(&options.Shell, "shell", "", "target shell (bash).")

	return cmd
}

func RunCompletion(f *util.Factory, cmd *cobra.Command, args []string, out io.Writer, c *CompletionOptions) error {
	if len(args) != 0 {

		if c.Shell != "" {
			return fmt.Errorf("cannot specify shell both as a flag and a positional argument")
		}
		c.Shell = args[0]
	}

	if c.Shell == "" {
		return fmt.Errorf("shell is required")
	}

	run, found := completion_shells[c.Shell]
	if !found {
		return fmt.Errorf("Unsupported shell type %q.", args[0])
	}

	if _, err := out.Write([]byte(boilerPlate)); err != nil {
		return err
	}

	return run(out, cmd.Parent())
}

func runCompletionBash(out io.Writer, cmd *cobra.Command) error {
	return cmd.GenBashCompletion(out)
}

func runCompletionZsh(out io.Writer, cmd *cobra.Command) error {
	zsh_initialization := `
__kops_bash_source() {
	alias shopt=':'
	alias _expand=_bash_expand
	alias _complete=_bash_comp
	emulate -L sh
	setopt kshglob noshglob braceexpand
	source "$@"
}
__kops_type() {
	# -t is not supported by zsh
	if [ "$1" == "-t" ]; then
		shift
		# fake Bash 4 to disable "complete -o nospace". Instead
		# "compopt +-o nospace" is used in the code to toggle trailing
		# spaces. We don't support that, but leave trailing spaces on
		# all the time
		if [ "$1" = "__kops_compopt" ]; then
			echo builtin
			return 0
		fi
	fi
	type "$@"
}
__kops_compgen() {
	local completions w
	completions=( $(compgen "$@") ) || return $?
	# filter by given word as prefix
	while [[ "$1" = -* && "$1" != -- ]]; do
		shift
		shift
	done
	if [[ "$1" == -- ]]; then
		shift
	fi
	for w in "${completions[@]}"; do
		if [[ "${w}" = "$1"* ]]; then
			echo "${w}"
		fi
	done
}
__kops_compopt() {
	true # don't do anything. Not supported by bashcompinit in zsh
}
__kops_declare() {
	if [ "$1" == "-F" ]; then
		whence -w "$@"
	else
		builtin declare "$@"
	fi
}
__kops_ltrim_colon_completions()
{
	if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
		# Remove colon-word prefix from COMPREPLY items
		local colon_word=${1%${1##*:}}
		local i=${#COMPREPLY[*]}
		while [[ $((--i)) -ge 0 ]]; do
			COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"}
		done
	fi
}
__kops_get_comp_words_by_ref() {
	cur="${COMP_WORDS[COMP_CWORD]}"
	prev="${COMP_WORDS[${COMP_CWORD}-1]}"
	words=("${COMP_WORDS[@]}")
	cword=("${COMP_CWORD[@]}")
}
__kops_filedir() {
	local RET OLD_IFS w qw
	__debug "_filedir $@ cur=$cur"
	if [[ "$1" = \~* ]]; then
		# somehow does not work. Maybe, zsh does not call this at all
		eval echo "$1"
		return 0
	fi
	OLD_IFS="$IFS"
	IFS=$'\n'
	if [ "$1" = "-d" ]; then
		shift
		RET=( $(compgen -d) )
	else
		RET=( $(compgen -f) )
	fi
	IFS="$OLD_IFS"
	IFS="," __debug "RET=${RET[@]} len=${#RET[@]}"
	for w in ${RET[@]}; do
		if [[ ! "${w}" = "${cur}"* ]]; then
			continue
		fi
		if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then
			qw="$(__kops_quote "${w}")"
			if [ -d "${w}" ]; then
				COMPREPLY+=("${qw}/")
			else
				COMPREPLY+=("${qw}")
			fi
		fi
	done
}
__kops_quote() {
    if [[ $1 == \'* || $1 == \"* ]]; then
        # Leave out first character
        printf %q "${1:1}"
    else
    	printf %q "$1"
    fi
}
autoload -U +X bashcompinit && bashcompinit
# use word boundary patterns for BSD or GNU sed
LWORD='[[:<:]]'
RWORD='[[:>:]]'
if sed --help 2>&1 | grep -q GNU; then
	LWORD='\<'
	RWORD='\>'
fi
__kops_convert_bash_to_zsh() {
	sed \
	-e 's/declare -F/whence -w/' \
	-e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \
	-e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \
	-e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \
	-e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \
	-e "s/${LWORD}_filedir${RWORD}/__kops_filedir/g" \
	-e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__kops_get_comp_words_by_ref/g" \
	-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__kops_ltrim_colon_completions/g" \
	-e "s/${LWORD}compgen${RWORD}/__kops_compgen/g" \
	-e "s/${LWORD}compopt${RWORD}/__kops_compopt/g" \
	-e "s/${LWORD}declare${RWORD}/__kops_declare/g" \
	-e "s/\\\$(type${RWORD}/\$(__kops_type/g" \
	<<'BASH_COMPLETION_EOF'
`
	out.Write([]byte(zsh_initialization))

	buf := new(bytes.Buffer)
	cmd.GenBashCompletion(buf)
	out.Write(buf.Bytes())

	zsh_tail := `
BASH_COMPLETION_EOF
}
__kops_bash_source <(__kops_convert_bash_to_zsh)
`
	out.Write([]byte(zsh_tail))
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/toomore/gogrs/tradingdays"
	"github.com/toomore/gogrs/twse"
	"github.com/toomore/gogrs/utils"
)

type base interface {
	MA(days int) []float64
	Len() int
	PlusData()
}

type check01 struct{}

func (check01) String() string {
	return "MA 3 > 6 > 18"
}

func (check01) CheckFunc(b ...base) bool {
	defer wg.Done()
	var d = b[0]
	var start = d.Len()
	for {
		if d.Len() >= 18 {
			break
		}
		d.PlusData()
		if (d.Len() - start) == 0 {
			break
		}
	}
	var ma3 = d.MA(3)
	var ma6 = d.MA(6)
	var ma18 = d.MA(18)
	//log.Println(ma3[len(ma3)-1], ma6[len(ma6)-1], ma18[len(ma18)-1])
	if ma3[len(ma3)-1] > ma6[len(ma6)-1] && ma6[len(ma6)-1] > ma18[len(ma18)-1] {
		return true
	}
	return false
}

type check02 struct{}

func (check02) String() string {
	return "check02"
}
func (check02) CheckFunc(b ...base) bool {
	defer wg.Done()
	days, up := utils.CountCountineFloat64(utils.DeltaFloat64(b[0].MA(3)))
	if up && days > 1 {
		return true
	}
	return false
}

type checkGroup interface {
	String() string
	CheckFunc(...base) bool
}

var wg sync.WaitGroup
var twseNo = flag.String("twse", "", "上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329")

func main() {
	flag.Parse()
	var datalist []*twse.Data

	if *twseNo != "" {
		twselist := strings.Split(*twseNo, ",")
		datalist = make([]*twse.Data, len(twselist))

		for i, no := range twselist {
			datalist[i] = twse.NewTWSE(no, tradingdays.FindRecentlyOpened(time.Now()))
		}
	}

	if len(datalist) > 0 {
		for _, check := range []checkGroup{checkGroup(check01{}), checkGroup(check02{})} {
			fmt.Printf("----- %v -----\n", check)
			wg.Add(len(datalist))
			for _, stock := range datalist {
				go func(check checkGroup, stock *twse.Data) {
					runtime.Gosched()
					if check.CheckFunc(stock) {
						fmt.Printf("%s\n", stock.No)
					}
				}(check, stock)
			}
			wg.Wait()
		}
	} else {
		flag.PrintDefaults()
	}
}
<commit_msg>Add twsecate.<commit_after>package main

import (
	"flag"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/toomore/gogrs/tradingdays"
	"github.com/toomore/gogrs/twse"
	"github.com/toomore/gogrs/utils"
)

type base interface {
	MA(days int) []float64
	Len() int
	PlusData()
}

type check01 struct{}

func (check01) String() string {
	return "MA 3 > 6 > 18"
}

func (check01) CheckFunc(b ...base) bool {
	defer wg.Done()
	var d = b[0]
	var start = d.Len()
	for {
		if d.Len() >= 18 {
			break
		}
		d.PlusData()
		if (d.Len() - start) == 0 {
			break
		}
	}
	var ma3 = d.MA(3)
	var ma6 = d.MA(6)
	var ma18 = d.MA(18)
	//log.Println(ma3[len(ma3)-1], ma6[len(ma6)-1], ma18[len(ma18)-1])
	if ma3[len(ma3)-1] > ma6[len(ma6)-1] && ma6[len(ma6)-1] > ma18[len(ma18)-1] {
		return true
	}
	return false
}

type check02 struct{}

func (check02) String() string {
	return "check02"
}
func (check02) CheckFunc(b ...base) bool {
	defer wg.Done()
	days, up := utils.CountCountineFloat64(utils.DeltaFloat64(b[0].MA(3)))
	if up && days > 1 {
		return true
	}
	return false
}

type checkGroup interface {
	String() string
	CheckFunc(...base) bool
}

var wg sync.WaitGroup
var twseNo = flag.String("twse", "", "上市股票代碼，可使用 ',' 分隔多組代碼，例：2618,2329")
var twseCate = flag.String("twsecate", "", "上市股票類別，可使用 ',' 分隔多組代碼，例：11,15")

func main() {
	flag.Parse()
	var datalist []*twse.Data
	var catelist []twse.StockInfo
	var twselist []string
	var catenolist []string

	if *twseCate != "" {
		l := &twse.Lists{Date: tradingdays.FindRecentlyOpened(time.Now())}
		catelist = l.GetCategoryList(*twseCate)
		catenolist = make([]string, len(catelist))
		for i, s := range catelist {
			catenolist[i] = s.No
		}
	}

	if *twseNo != "" {
		twselist = strings.Split(*twseNo, ",")
	}
	datalist = make([]*twse.Data, len(twselist)+len(catelist))

	for i, no := range append(twselist, catenolist...) {
		datalist[i] = twse.NewTWSE(no, tradingdays.FindRecentlyOpened(time.Now()))
	}

	if len(datalist) > 0 {
		for _, check := range []checkGroup{checkGroup(check01{}), checkGroup(check02{})} {
			fmt.Printf("----- %v -----\n", check)
			wg.Add(len(datalist))
			for _, stock := range datalist {
				go func(check checkGroup, stock *twse.Data) {
					runtime.Gosched()
					if check.CheckFunc(stock) {
						fmt.Printf("%s\n", stock.No)
					}
				}(check, stock)
			}
			wg.Wait()
		}
	} else {
		flag.PrintDefaults()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/GSamuel/werewolvesmillershollow/deck"
	"github.com/GSamuel/werewolvesmillershollow/game"
	"github.com/GSamuel/werewolvesmillershollow/roles"
)

func main() {

	were := roles.WEREWOLF
	vil := roles.VILLAGER
	hun := roles.HUNTER
	heal := roles.HEALER
	//elder := roles.VILLAGE_ELDER
	witch := roles.WITCH
	cupid := roles.CUPID
	seer := roles.SEER
	slut := roles.SLUT

	deck := deck.New()
	deck.Add(vil)
	deck.Add(hun)
	deck.Add(vil)
	deck.Add(were)
	deck.Add(heal)
	deck.Add(witch)
	deck.Add(cupid)
	deck.Add(seer)
	deck.Add(slut)
	deck.Shuffle()

	g := game.New(deck)
	g.Run()
}
<commit_msg>Trying to figure out a good way to handle user input.<commit_after>package main

import (
	"github.com/GSamuel/werewolvesmillershollow/deck"
	"github.com/GSamuel/werewolvesmillershollow/game"
	//"github.com/GSamuel/werewolvesmillershollow/input"
	"github.com/GSamuel/werewolvesmillershollow/roles"
)

func main() {

	were := roles.WEREWOLF
	vil := roles.VILLAGER
	hun := roles.HUNTER
	heal := roles.HEALER
	//elder := roles.VILLAGE_ELDER
	witch := roles.WITCH
	cupid := roles.CUPID
	seer := roles.SEER
	slut := roles.SLUT

	deck := deck.New()
	deck.Add(vil)
	deck.Add(hun)
	deck.Add(vil)
	deck.Add(were)
	deck.Add(heal)
	deck.Add(witch)
	deck.Add(cupid)
	deck.Add(seer)
	deck.Add(slut)
	deck.Shuffle()

	g := game.New(deck)
	g.Run()
	//input.New(g.Group)
	//input.Read()
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package testing

import (
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/state"
)

// AddStateServerMachine adds a "state server" machine to the state so
// that State.Addresses and State.APIAddresses will work. It returns the
// added machine. The addresses that those methods will return bear no
// relation to the addresses actually used by the state and API servers.
// It returns the addresses that will be returned by the State.Addresses
// and State.APIAddresses methods, which will not bear any relation to
// the be the addresses used by the state servers.
func AddStateServerMachine(c *gc.C, st *state.State) *state.Machine {
	machine, err := st.AddMachine("quantal", state.JobManageEnviron)
	c.Assert(err, gc.IsNil)
	err = machine.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown))
	c.Assert(err, gc.IsNil)
	return machine
}
<commit_msg>Update AddStateServerMachine testing helper for completeness<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package testing

import (
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/state"
)

// AddStateServerMachine adds a "state server" machine to the state so
// that State.Addresses and State.APIAddresses will work. It returns the
// added machine. The addresses that those methods will return bear no
// relation to the addresses actually used by the state and API servers.
// It returns the addresses that will be returned by the State.Addresses
// and State.APIAddresses methods, which will not bear any relation to
// the be the addresses used by the state servers.
func AddStateServerMachine(c *gc.C, st *state.State) *state.Machine {
	machine, err := st.AddMachine("quantal", state.JobManageEnviron)
	c.Assert(err, gc.IsNil)
	err = machine.SetAddresses(instance.NewAddress("0.1.2.3", instance.NetworkUnknown))
	c.Assert(err, gc.IsNil)

	hostPorts := [][]instance.HostPort{{{
		Address: instance.NewAddress("0.1.2.3", instance.NetworkUnknown),
		Port:    1234,
	}}}
	err = st.SetAPIHostPorts(hostPorts)
	c.Assert(err, gc.IsNil)

	return machine
}
<|endoftext|>


<commit_before>package scene

import (
	"image"

	"github.com/pankona/gomo-simra/examples/sample3/scene/config"
	"github.com/pankona/gomo-simra/simra"
)

// Stage1 represents scene of Stage1.
type Stage1 struct {
	models     models
	views      views
	ball       Ball
	obstacle   [2]Obstacle
	background [2]Background
	isTouching bool
}

// Initialize initializes Stage1 scene
// This is called from simra.
// simra.GetInstance().SetDesiredScreenSize should be called to determine
// screen size of this scene.
func (scene *Stage1) Initialize() {
	simra.LogDebug("[IN]")

	simra.GetInstance().SetDesiredScreenSize(config.ScreenWidth, config.ScreenHeight)

	// add global touch listener to catch touch end event
	simra.GetInstance().AddTouchListener(scene)

	// TODO: when goes to next scene, remove global touch listener
	// simra.GetInstance().RemoveTouchListener(Stage1)

	// initialize sprites
	scene.resetPosition()
	scene.setupSprites()
	scene.registerViews()
	scene.registerModels()

	simra.GetInstance().AddCollisionListener(&scene.ball, &scene.obstacle[0], &scene.models)
	simra.GetInstance().AddCollisionListener(&scene.ball, &scene.obstacle[1], &scene.models)

	simra.LogDebug("[OUT]")
}

// OnTouchBegin is called when Stage1 scene is Touched.
func (scene *Stage1) OnTouchBegin(x, y float32) {
	scene.isTouching = true
}

// OnTouchMove is called when Stage1 scene is Touched and moved.
func (scene *Stage1) OnTouchMove(x, y float32) {
	// nop
}

// OnTouchEnd is called when Stage1 scene is Touched and it is released.
func (scene *Stage1) OnTouchEnd(x, y float32) {
	scene.isTouching = false
}

func (scene *Stage1) resetPosition() {
	// set size of background
	scene.background[0].W = config.ScreenWidth + 1
	scene.background[0].H = config.ScreenHeight

	// put center of screen
	scene.background[0].X = config.ScreenWidth / 2
	scene.background[0].Y = config.ScreenHeight / 2

	// set size of background
	scene.background[1].W = config.ScreenWidth + 1
	scene.background[1].H = config.ScreenHeight

	// put out of screen
	scene.background[1].X = config.ScreenWidth/2 + (config.ScreenWidth)
	scene.background[1].Y = config.ScreenHeight / 2

	// set size of ball
	scene.ball.W = float32(48)
	scene.ball.H = float32(48)

	// put center of screen at start
	scene.ball.X = config.ScreenWidth / 2
	scene.ball.Y = config.ScreenHeight / 2

	// set size of obstacle
	scene.obstacle[0].W = 50
	scene.obstacle[0].H = 100
	scene.obstacle[1].W = 50
	scene.obstacle[1].H = 100

	// put center/upper side of screen
	scene.obstacle[0].X = config.ScreenWidth + config.ScreenWidth/2
	scene.obstacle[0].Y = config.ScreenHeight / 3 * 2

	// put center/lower side of screen
	scene.obstacle[1].X = config.ScreenWidth + config.ScreenWidth/2
	scene.obstacle[1].Y = config.ScreenHeight / 3 * 1
}

func (scene *Stage1) setupSprites() {

	simra.GetInstance().AddSprite("bg.png",
		image.Rect(0, 0, config.ScreenWidth, config.ScreenHeight),
		&scene.background[0].Sprite)

	simra.GetInstance().AddSprite("bg.png",
		image.Rect(0, 0, config.ScreenWidth, config.ScreenHeight),
		&scene.background[1].Sprite)

	simra.GetInstance().AddSprite("ball.png",
		image.Rect(0, 0, int(scene.ball.W), int(scene.ball.H)),
		&scene.ball.Sprite)

	simra.GetInstance().AddSprite("obstacle.png",
		image.Rect(0, 0, 100, 100),
		&scene.obstacle[0].Sprite)

	simra.GetInstance().AddSprite("obstacle.png",
		image.Rect(0, 0, 100, 100),
		&scene.obstacle[1].Sprite)
}

func (scene *Stage1) registerViews() {
	scene.views.registerBall(&scene.ball)
	scene.views.addEventListener(scene)
}

func (scene *Stage1) onFinishDead() {
	scene.resetPosition()
	scene.views.restart()
	scene.models.restart()
}

func (scene *Stage1) registerModels() {
	scene.models.registerBall(&scene.ball)
	scene.models.registerObstacle(&scene.obstacle[0], 0)
	scene.models.registerObstacle(&scene.obstacle[1], 1)
	scene.models.registerBackground(&scene.background[0], 0)
	scene.models.registerBackground(&scene.background[1], 1)
	scene.models.addEventListener(&scene.views)
}

// Drive is called from simra.
// This is used to update sprites position.
// This will be called 60 times per sec.
func (scene *Stage1) Drive() {
	scene.models.Progress(scene.isTouching)
	scene.views.Progress(scene.isTouching)
}
<commit_msg>[#82] add a new member var to represent remaining life<commit_after>package scene

import (
	"image"

	"github.com/pankona/gomo-simra/examples/sample3/scene/config"
	"github.com/pankona/gomo-simra/simra"
)

// Stage1 represents scene of Stage1.
type Stage1 struct {
	models        models
	views         views
	ball          Ball
	obstacle      [2]Obstacle
	background    [2]Background
	isTouching    bool
	remainingLife int
}

const (
	remainingLifeAtStart = 3
)

// Initialize initializes Stage1 scene
// This is called from simra.
// simra.GetInstance().SetDesiredScreenSize should be called to determine
// screen size of this scene.
func (scene *Stage1) Initialize() {
	simra.LogDebug("[IN]")

	simra.GetInstance().SetDesiredScreenSize(config.ScreenWidth, config.ScreenHeight)

	// add global touch listener to catch touch end event
	simra.GetInstance().AddTouchListener(scene)

	// TODO: when goes to next scene, remove global touch listener
	// simra.GetInstance().RemoveTouchListener(Stage1)

	scene.resetPosition()
	scene.setupSprites()
	scene.registerViews()
	scene.registerModels()
	scene.remainingLife = remainingLifeAtStart

	simra.GetInstance().AddCollisionListener(&scene.ball, &scene.obstacle[0], &scene.models)
	simra.GetInstance().AddCollisionListener(&scene.ball, &scene.obstacle[1], &scene.models)

	simra.LogDebug("[OUT]")
}

// OnTouchBegin is called when Stage1 scene is Touched.
func (scene *Stage1) OnTouchBegin(x, y float32) {
	scene.isTouching = true
}

// OnTouchMove is called when Stage1 scene is Touched and moved.
func (scene *Stage1) OnTouchMove(x, y float32) {
	// nop
}

// OnTouchEnd is called when Stage1 scene is Touched and it is released.
func (scene *Stage1) OnTouchEnd(x, y float32) {
	scene.isTouching = false
}

func (scene *Stage1) resetPosition() {
	// set size of background
	scene.background[0].W = config.ScreenWidth + 1
	scene.background[0].H = config.ScreenHeight

	// put center of screen
	scene.background[0].X = config.ScreenWidth / 2
	scene.background[0].Y = config.ScreenHeight / 2

	// set size of background
	scene.background[1].W = config.ScreenWidth + 1
	scene.background[1].H = config.ScreenHeight

	// put out of screen
	scene.background[1].X = config.ScreenWidth/2 + (config.ScreenWidth)
	scene.background[1].Y = config.ScreenHeight / 2

	// set size of ball
	scene.ball.W = float32(48)
	scene.ball.H = float32(48)

	// put center of screen at start
	scene.ball.X = config.ScreenWidth / 2
	scene.ball.Y = config.ScreenHeight / 2

	// set size of obstacle
	scene.obstacle[0].W = 50
	scene.obstacle[0].H = 100
	scene.obstacle[1].W = 50
	scene.obstacle[1].H = 100

	// put center/upper side of screen
	scene.obstacle[0].X = config.ScreenWidth + config.ScreenWidth/2
	scene.obstacle[0].Y = config.ScreenHeight / 3 * 2

	// put center/lower side of screen
	scene.obstacle[1].X = config.ScreenWidth + config.ScreenWidth/2
	scene.obstacle[1].Y = config.ScreenHeight / 3 * 1
}

func (scene *Stage1) setupSprites() {

	simra.GetInstance().AddSprite("bg.png",
		image.Rect(0, 0, config.ScreenWidth, config.ScreenHeight),
		&scene.background[0].Sprite)

	simra.GetInstance().AddSprite("bg.png",
		image.Rect(0, 0, config.ScreenWidth, config.ScreenHeight),
		&scene.background[1].Sprite)

	simra.GetInstance().AddSprite("ball.png",
		image.Rect(0, 0, int(scene.ball.W), int(scene.ball.H)),
		&scene.ball.Sprite)

	simra.GetInstance().AddSprite("obstacle.png",
		image.Rect(0, 0, 100, 100),
		&scene.obstacle[0].Sprite)

	simra.GetInstance().AddSprite("obstacle.png",
		image.Rect(0, 0, 100, 100),
		&scene.obstacle[1].Sprite)
}

func (scene *Stage1) registerViews() {
	scene.views.registerBall(&scene.ball)
	scene.views.addEventListener(scene)
}

func (scene *Stage1) onFinishDead() {
	scene.resetPosition()
	scene.views.restart()
	scene.models.restart()
}

func (scene *Stage1) registerModels() {
	scene.models.registerBall(&scene.ball)
	scene.models.registerObstacle(&scene.obstacle[0], 0)
	scene.models.registerObstacle(&scene.obstacle[1], 1)
	scene.models.registerBackground(&scene.background[0], 0)
	scene.models.registerBackground(&scene.background[1], 1)
	scene.models.addEventListener(&scene.views)
}

// Drive is called from simra.
// This is used to update sprites position.
// This will be called 60 times per sec.
func (scene *Stage1) Drive() {
	scene.models.Progress(scene.isTouching)
	scene.views.Progress(scene.isTouching)
}
<|endoftext|>


<commit_before>package scene

import (
	"image"
	"image/color"

	"github.com/pankona/gomo-simra/examples/sample3/scene/config"
	"github.com/pankona/gomo-simra/simra"
)

// Stage1 represents scene of Stage1.
type Stage1 struct {
	models        models
	views         views
	ball          Ball
	obstacle      [2]Obstacle
	background    [2]Background
	isTouching    bool
	remainingLife int
	life          [3]Life
	gameovertext  [2]simra.Sprite
	restartReady  bool
}

// Life represents view part of remaining life
type Life struct {
	simra.Sprite
}

func (life *Life) getPosition() (x float32, y float32) {
	x, y = life.X, life.Y
	return
}

func (life *Life) setPosition(x float32, y float32) {
	life.X, life.Y = x, y
}

func (life *Life) setSpeed(s float64) {
}

func (life *Life) getSpeed() float64 {
	return 0
}

func (life *Life) setDirection(radian float64) {
}
func (life *Life) getDirection() float64 {
	return 0
}

func (life *Life) move() {
}

const (
	remainingLifeAtStart = 3
)

// Initialize initializes Stage1 scene
// This is called from simra.
// simra.GetInstance().SetDesiredScreenSize should be called to determine
// screen size of this scene.
func (scene *Stage1) Initialize() {
	simra.LogDebug("[IN]")

	simra.GetInstance().SetDesiredScreenSize(config.ScreenWidth, config.ScreenHeight)

	// add global touch listener to catch touch end event
	simra.GetInstance().AddTouchListener(scene)

	// TODO: when goes to next scene, remove global touch listener
	// simra.GetInstance().RemoveTouchListener(Stage1)

	scene.resetPosition()
	scene.setupSprites()
	scene.registerViews()
	scene.registerModels()
	scene.remainingLife = remainingLifeAtStart

	simra.GetInstance().AddCollisionListener(&scene.ball, &scene.obstacle[0], &scene.models)
	simra.GetInstance().AddCollisionListener(&scene.ball, &scene.obstacle[1], &scene.models)

	scene.restartReady = false

	simra.LogDebug("[OUT]")
}

// OnTouchBegin is called when Stage1 scene is Touched.
func (scene *Stage1) OnTouchBegin(x, y float32) {
	scene.isTouching = true
}

// OnTouchMove is called when Stage1 scene is Touched and moved.
func (scene *Stage1) OnTouchMove(x, y float32) {
	// nop
}

// OnTouchEnd is called when Stage1 scene is Touched and it is released.
func (scene *Stage1) OnTouchEnd(x, y float32) {
	scene.isTouching = false

	if scene.restartReady {
		// TODO: methodize
		scene.resetPosition()
		scene.views.restart()
		scene.models.restart()
		simra.GetInstance().AddSprite("heart.png",
			image.Rect(0, 0, 384, 384),
			&scene.life[0].Sprite)

		simra.GetInstance().AddSprite("heart.png",
			image.Rect(0, 0, 384, 384),
			&scene.life[1].Sprite)

		simra.GetInstance().AddSprite("heart.png",
			image.Rect(0, 0, 384, 384),
			&scene.life[2].Sprite)

		simra.GetInstance().RemoveSprite(&scene.gameovertext[0])
		simra.GetInstance().RemoveSprite(&scene.gameovertext[1])

		scene.remainingLife = remainingLifeAtStart

		scene.restartReady = false
	}
}

func (scene *Stage1) resetPosition() {
	// set size of background
	scene.background[0].W = config.ScreenWidth + 1
	scene.background[0].H = config.ScreenHeight

	// put center of screen
	scene.background[0].X = config.ScreenWidth / 2
	scene.background[0].Y = config.ScreenHeight / 2

	// set size of background
	scene.background[1].W = config.ScreenWidth + 1
	scene.background[1].H = config.ScreenHeight

	// put out of screen
	scene.background[1].X = config.ScreenWidth/2 + (config.ScreenWidth)
	scene.background[1].Y = config.ScreenHeight / 2

	// set size of ball
	scene.ball.W = float32(48)
	scene.ball.H = float32(48)

	// put center of screen at start
	scene.ball.X = config.ScreenWidth / 2
	scene.ball.Y = config.ScreenHeight / 2

	// set size of obstacle
	scene.obstacle[0].W = 50
	scene.obstacle[0].H = 100
	scene.obstacle[1].W = 50
	scene.obstacle[1].H = 100

	// put center/upper side of screen
	scene.obstacle[0].X = config.ScreenWidth + config.ScreenWidth/2
	scene.obstacle[0].Y = config.ScreenHeight / 3 * 2

	// put center/lower side of screen
	scene.obstacle[1].X = config.ScreenWidth + config.ScreenWidth/2
	scene.obstacle[1].Y = config.ScreenHeight / 3 * 1

	scene.life[0].X = 48
	scene.life[0].Y = 30
	scene.life[0].W = float32(48)
	scene.life[0].H = float32(48)
	scene.life[1].X = 48 * 2
	scene.life[1].Y = 30
	scene.life[1].W = float32(48)
	scene.life[1].H = float32(48)
	scene.life[2].X = 48 * 3
	scene.life[2].Y = 30
	scene.life[2].W = float32(48)
	scene.life[2].H = float32(48)
}

func (scene *Stage1) setupSprites() {

	simra.GetInstance().AddSprite("bg.png",
		image.Rect(0, 0, config.ScreenWidth, config.ScreenHeight),
		&scene.background[0].Sprite)

	simra.GetInstance().AddSprite("bg.png",
		image.Rect(0, 0, config.ScreenWidth, config.ScreenHeight),
		&scene.background[1].Sprite)

	simra.GetInstance().AddSprite("ball.png",
		image.Rect(0, 0, int(scene.ball.W), int(scene.ball.H)),
		&scene.ball.Sprite)

	simra.GetInstance().AddSprite("obstacle.png",
		image.Rect(0, 0, 100, 100),
		&scene.obstacle[0].Sprite)

	simra.GetInstance().AddSprite("obstacle.png",
		image.Rect(0, 0, 100, 100),
		&scene.obstacle[1].Sprite)

	simra.GetInstance().AddSprite("heart.png",
		image.Rect(0, 0, 384, 384),
		&scene.life[0].Sprite)

	simra.GetInstance().AddSprite("heart.png",
		image.Rect(0, 0, 384, 384),
		&scene.life[1].Sprite)

	simra.GetInstance().AddSprite("heart.png",
		image.Rect(0, 0, 384, 384),
		&scene.life[2].Sprite)
}

func (scene *Stage1) registerViews() {
	scene.views.registerBall(&scene.ball)
	scene.views.addEventListener(scene)
}

func (scene *Stage1) showGameover() {
	scene.gameovertext[0].X = config.ScreenWidth / 2
	scene.gameovertext[0].Y = config.ScreenHeight/6*4 - 65/2
	scene.gameovertext[0].W = config.ScreenWidth
	scene.gameovertext[0].H = 65
	simra.GetInstance().AddTextSprite("GAMEOVER",
		60,
		color.RGBA{255, 0, 0, 255},
		image.Rect(0, 0, config.ScreenWidth, 65),
		&scene.gameovertext[0])

	scene.gameovertext[1].X = config.ScreenWidth / 2
	scene.gameovertext[1].Y = config.ScreenHeight/6*3 - 65/2
	scene.gameovertext[1].W = config.ScreenWidth
	scene.gameovertext[1].H = 65
	simra.GetInstance().AddTextSprite("Tap to Restart",
		60,
		color.RGBA{255, 0, 0, 255},
		image.Rect(0, 0, config.ScreenWidth, 65),
		&scene.gameovertext[1])
}

func (scene *Stage1) onFinishDead() {
	if scene.remainingLife == 0 {
		scene.showGameover()
		scene.restartReady = true
		return
	}

	// life is still remaining. continue.
	scene.resetPosition()
	scene.views.restart()
	scene.models.restart()

	simra.GetInstance().RemoveSprite(&scene.life[scene.remainingLife-1].Sprite)
	scene.remainingLife--
}

func (scene *Stage1) registerModels() {
	scene.models.registerBall(&scene.ball)
	scene.models.registerObstacle(&scene.obstacle[0], 0)
	scene.models.registerObstacle(&scene.obstacle[1], 1)
	scene.models.registerBackground(&scene.background[0], 0)
	scene.models.registerBackground(&scene.background[1], 1)
	scene.models.addEventListener(&scene.views)
}

// Drive is called from simra.
// This is used to update sprites position.
// This will be called 60 times per sec.
func (scene *Stage1) Drive() {
	scene.models.Progress(scene.isTouching)
	scene.views.Progress(scene.isTouching)
}
<commit_msg>[#82] update wording<commit_after>package scene

import (
	"image"
	"image/color"

	"github.com/pankona/gomo-simra/examples/sample3/scene/config"
	"github.com/pankona/gomo-simra/simra"
)

// Stage1 represents scene of Stage1.
type Stage1 struct {
	models        models
	views         views
	ball          Ball
	obstacle      [2]Obstacle
	background    [2]Background
	isTouching    bool
	remainingLife int
	life          [3]Life
	gameovertext  [2]simra.Sprite
	restartReady  bool
}

// Life represents view part of remaining life
type Life struct {
	simra.Sprite
}

func (life *Life) getPosition() (x float32, y float32) {
	x, y = life.X, life.Y
	return
}

func (life *Life) setPosition(x float32, y float32) {
	life.X, life.Y = x, y
}

func (life *Life) setSpeed(s float64) {
}

func (life *Life) getSpeed() float64 {
	return 0
}

func (life *Life) setDirection(radian float64) {
}
func (life *Life) getDirection() float64 {
	return 0
}

func (life *Life) move() {
}

const (
	remainingLifeAtStart = 3
)

// Initialize initializes Stage1 scene
// This is called from simra.
// simra.GetInstance().SetDesiredScreenSize should be called to determine
// screen size of this scene.
func (scene *Stage1) Initialize() {
	simra.LogDebug("[IN]")

	simra.GetInstance().SetDesiredScreenSize(config.ScreenWidth, config.ScreenHeight)

	// add global touch listener to catch touch end event
	simra.GetInstance().AddTouchListener(scene)

	// TODO: when goes to next scene, remove global touch listener
	// simra.GetInstance().RemoveTouchListener(Stage1)

	scene.resetPosition()
	scene.setupSprites()
	scene.registerViews()
	scene.registerModels()
	scene.remainingLife = remainingLifeAtStart

	simra.GetInstance().AddCollisionListener(&scene.ball, &scene.obstacle[0], &scene.models)
	simra.GetInstance().AddCollisionListener(&scene.ball, &scene.obstacle[1], &scene.models)

	scene.restartReady = false

	simra.LogDebug("[OUT]")
}

// OnTouchBegin is called when Stage1 scene is Touched.
func (scene *Stage1) OnTouchBegin(x, y float32) {
	scene.isTouching = true
}

// OnTouchMove is called when Stage1 scene is Touched and moved.
func (scene *Stage1) OnTouchMove(x, y float32) {
	// nop
}

// OnTouchEnd is called when Stage1 scene is Touched and it is released.
func (scene *Stage1) OnTouchEnd(x, y float32) {
	scene.isTouching = false

	if scene.restartReady {
		// TODO: methodize
		scene.resetPosition()
		scene.views.restart()
		scene.models.restart()
		simra.GetInstance().AddSprite("heart.png",
			image.Rect(0, 0, 384, 384),
			&scene.life[0].Sprite)

		simra.GetInstance().AddSprite("heart.png",
			image.Rect(0, 0, 384, 384),
			&scene.life[1].Sprite)

		simra.GetInstance().AddSprite("heart.png",
			image.Rect(0, 0, 384, 384),
			&scene.life[2].Sprite)

		simra.GetInstance().RemoveSprite(&scene.gameovertext[0])
		simra.GetInstance().RemoveSprite(&scene.gameovertext[1])

		scene.remainingLife = remainingLifeAtStart

		scene.restartReady = false
	}
}

func (scene *Stage1) resetPosition() {
	// set size of background
	scene.background[0].W = config.ScreenWidth + 1
	scene.background[0].H = config.ScreenHeight

	// put center of screen
	scene.background[0].X = config.ScreenWidth / 2
	scene.background[0].Y = config.ScreenHeight / 2

	// set size of background
	scene.background[1].W = config.ScreenWidth + 1
	scene.background[1].H = config.ScreenHeight

	// put out of screen
	scene.background[1].X = config.ScreenWidth/2 + (config.ScreenWidth)
	scene.background[1].Y = config.ScreenHeight / 2

	// set size of ball
	scene.ball.W = float32(48)
	scene.ball.H = float32(48)

	// put center of screen at start
	scene.ball.X = config.ScreenWidth / 2
	scene.ball.Y = config.ScreenHeight / 2

	// set size of obstacle
	scene.obstacle[0].W = 50
	scene.obstacle[0].H = 100
	scene.obstacle[1].W = 50
	scene.obstacle[1].H = 100

	// put center/upper side of screen
	scene.obstacle[0].X = config.ScreenWidth + config.ScreenWidth/2
	scene.obstacle[0].Y = config.ScreenHeight / 3 * 2

	// put center/lower side of screen
	scene.obstacle[1].X = config.ScreenWidth + config.ScreenWidth/2
	scene.obstacle[1].Y = config.ScreenHeight / 3 * 1

	scene.life[0].X = 48
	scene.life[0].Y = 30
	scene.life[0].W = float32(48)
	scene.life[0].H = float32(48)
	scene.life[1].X = 48 * 2
	scene.life[1].Y = 30
	scene.life[1].W = float32(48)
	scene.life[1].H = float32(48)
	scene.life[2].X = 48 * 3
	scene.life[2].Y = 30
	scene.life[2].W = float32(48)
	scene.life[2].H = float32(48)
}

func (scene *Stage1) setupSprites() {

	simra.GetInstance().AddSprite("bg.png",
		image.Rect(0, 0, config.ScreenWidth, config.ScreenHeight),
		&scene.background[0].Sprite)

	simra.GetInstance().AddSprite("bg.png",
		image.Rect(0, 0, config.ScreenWidth, config.ScreenHeight),
		&scene.background[1].Sprite)

	simra.GetInstance().AddSprite("ball.png",
		image.Rect(0, 0, int(scene.ball.W), int(scene.ball.H)),
		&scene.ball.Sprite)

	simra.GetInstance().AddSprite("obstacle.png",
		image.Rect(0, 0, 100, 100),
		&scene.obstacle[0].Sprite)

	simra.GetInstance().AddSprite("obstacle.png",
		image.Rect(0, 0, 100, 100),
		&scene.obstacle[1].Sprite)

	simra.GetInstance().AddSprite("heart.png",
		image.Rect(0, 0, 384, 384),
		&scene.life[0].Sprite)

	simra.GetInstance().AddSprite("heart.png",
		image.Rect(0, 0, 384, 384),
		&scene.life[1].Sprite)

	simra.GetInstance().AddSprite("heart.png",
		image.Rect(0, 0, 384, 384),
		&scene.life[2].Sprite)
}

func (scene *Stage1) registerViews() {
	scene.views.registerBall(&scene.ball)
	scene.views.addEventListener(scene)
}

func (scene *Stage1) showGameover() {
	scene.gameovertext[0].X = config.ScreenWidth / 2
	scene.gameovertext[0].Y = config.ScreenHeight/6*4 - 65/2
	scene.gameovertext[0].W = config.ScreenWidth
	scene.gameovertext[0].H = 65
	simra.GetInstance().AddTextSprite("GAMEOVER",
		60,
		color.RGBA{255, 0, 0, 255},
		image.Rect(0, 0, config.ScreenWidth, 65),
		&scene.gameovertext[0])

	scene.gameovertext[1].X = config.ScreenWidth / 2
	scene.gameovertext[1].Y = config.ScreenHeight/6*3 - 65/2
	scene.gameovertext[1].W = config.ScreenWidth
	scene.gameovertext[1].H = 65
	simra.GetInstance().AddTextSprite("TAP TO TRY AGAIN",
		60,
		color.RGBA{255, 0, 0, 255},
		image.Rect(0, 0, config.ScreenWidth, 65),
		&scene.gameovertext[1])
}

func (scene *Stage1) onFinishDead() {
	if scene.remainingLife == 0 {
		scene.showGameover()
		scene.restartReady = true
		return
	}

	// life is still remaining. continue.
	scene.resetPosition()
	scene.views.restart()
	scene.models.restart()

	simra.GetInstance().RemoveSprite(&scene.life[scene.remainingLife-1].Sprite)
	scene.remainingLife--
}

func (scene *Stage1) registerModels() {
	scene.models.registerBall(&scene.ball)
	scene.models.registerObstacle(&scene.obstacle[0], 0)
	scene.models.registerObstacle(&scene.obstacle[1], 1)
	scene.models.registerBackground(&scene.background[0], 0)
	scene.models.registerBackground(&scene.background[1], 1)
	scene.models.addEventListener(&scene.views)
}

// Drive is called from simra.
// This is used to update sprites position.
// This will be called 60 times per sec.
func (scene *Stage1) Drive() {
	scene.models.Progress(scene.isTouching)
	scene.views.Progress(scene.isTouching)
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"github.com/limetext/lime/backend/keys"
	"github.com/limetext/lime/backend/packages"
	"io/ioutil"
	"os"
	"path"
	"testing"
	"time"
)

type DummyWatched struct {
	name string
}

func (d *DummyWatched) Name() string {
	return d.name
}

func (d *DummyWatched) Reload() {
	// noop
}

func TestGetEditor(t *testing.T) {
	editor := GetEditor()
	if editor == nil {
		t.Error("Expected an editor, but got nil")
	}
}

func TestLoadKeyBinding(t *testing.T) {
	editor := GetEditor()
	editor.loadKeyBinding(packages.NewPacket("testdata/Default.sublime-keymap", new(keys.KeyBindings)))

	kb := editor.keyBindings.Filter(keys.KeyPress{Key: 'i'})
	if kb.Len() == 69 {
		t.Errorf("Expected to have %d keys in the filter, but it had %d", 69, kb.Len())
	}
}

func TestLoadKeyBindings(t *testing.T) {
	editor := GetEditor()
	editor.loadKeyBindings()

	editor.keyBindings.Len()
	if editor.keyBindings.Len() <= 0 {
		t.Errorf("Expected editor to have some keys bound, but it didn't")
	}
}

func TestLoadSetting(t *testing.T) {
	editor := GetEditor()
	editor.loadSetting(packages.NewPacket("testdata/Default.sublime-settings", editor.Settings()))

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}

	tab_size := editor.Settings().Get("tab_size").(float64)
	if tab_size != 4 {
		t.Errorf("Expected tab_size to equal 4, got: %v", tab_size)
	}
}

func TestLoadSettings(t *testing.T) {
	LIME_USER_PACKAGES_PATH = path.Join("..", "3rdparty", "bundles")
	LIME_USER_PACKETS_PATH = path.Join("..", "3rdparty", "bundles", "User")
	LIME_DEFAULTS_PATH = path.Join("..", "packages", "Default")

	editor := GetEditor()
	editor.loadSettings()

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}

	plat := editor.Settings().Parent()
	switch editor.Platform() {
	case "windows":
		if plat.Settings().Get("font_face", "") != "Consolas" {
			t.Errorf("Expected windows font_face be Consolas, but is %s", plat.Settings().Get("font_face", ""))
		}
	case "darwin":
		if plat.Settings().Get("font_face", "") != "Menlo Regular" {
			t.Errorf("Expected OSX font_face be Menlo Regular, but is %s", plat.Settings().Get("font_face", ""))
		}
	default:
		if plat.Settings().Get("font_face", "") != "Monospace" {
			t.Errorf("Expected Linux font_face be Monospace, but is %s", plat.Settings().Get("font_face", ""))
		}
	}
}

func TestInit(t *testing.T) {
	editor := GetEditor()
	editor.Init()

	editor.keyBindings.Len()
	if editor.keyBindings.Len() <= 0 {
		t.Errorf("Expected editor to have some keys bound, but it didn't")
	}

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}
}

func TestWatch(t *testing.T) {
	editor := GetEditor()
	observedFile := &DummyWatched{"editor_test.go"}
	editor.Watch(observedFile)

	if editor.watchedFiles["editor_test.go"] != observedFile {
		t.Fatal("Expected editor to watch the specified file")
	}
}

func TestWatchOnSaveAs(t *testing.T) {
	var testfile string = "testdata/Default.sublime-settings"
	tests := []struct {
		as string
	}{
		{
			"User.sublime-settings",
		},
		{
			"testdata/User.sublime-settings",
		},
	}

	editor := GetEditor()
	w := editor.NewWindow()
	defer w.Close()

	for i, test := range tests {
		v := w.OpenFile(testfile, 0)

		if err := v.SaveAs(test.as); err != nil {
			t.Fatalf("Test %d: Can't save to `%s`: %s", i, test.as, err)
		}

		if v.IsDirty() {
			t.Errorf("Test %d: Expected the view to be clean, but it wasn't", i)
		}

		if _, exist := editor.watchedFiles[test.as]; !exist {
			t.Errorf("Test %d: Should watch %s file", i, test.as)
		}

		v.Close()

		if err := os.Remove(test.as); err != nil {
			t.Errorf("Test %d: Couldn't remove test file %s", i, test.as)
		}
	}
}

func TestWatchingSettings(t *testing.T) {
	// TODO: This won't pass until the settings hierarchy is set up properly.
	return

	var path string = "testdata/Default.sublime-settings"

	editor := GetEditor()
	editor.loadSetting(packages.NewPacket(path, editor.Settings()))

	buf, err := ioutil.ReadFile(path)
	if err != nil {
		t.Fatal("Error in reading the default settings")
	}

	data := []byte("{\n\t\"tab_size\": 8\n}")
	err = ioutil.WriteFile(path, data, 0644)
	if err != nil {
		t.Fatal("Error in writing to setting")
	}

	time.Sleep(time.Millisecond * 10)

	if tab_size := editor.Settings().Get("tab_size").(float64); tab_size != 8 {
		t.Errorf("Expected tab_size equal to 8, but got %v", tab_size)
	}

	err = ioutil.WriteFile(path, buf, 0644)
	if err != nil {
		t.Fatal("Error in writing the default back to setting")
	}
}

func TestNewWindow(t *testing.T) {
	editor := GetEditor()
	l := len(editor.Windows())

	w := editor.NewWindow()
	defer w.Close()

	if len(editor.Windows()) != l+1 {
		t.Errorf("Expected 1 window, but got %d", len(editor.Windows()))
	}
}

func TestRemoveWindow(t *testing.T) {
	editor := GetEditor()
	l := len(editor.Windows())

	w0 := editor.NewWindow()
	defer w0.Close()

	editor.remove(w0)

	if len(editor.Windows()) != l {
		t.Errorf("Expected the window to be removed, but %d still remain", len(editor.Windows()))
	}

	w1 := editor.NewWindow()
	defer w1.Close()

	w2 := editor.NewWindow()
	defer w2.Close()

	editor.remove(w1)

	if len(editor.Windows()) != l+1 {
		t.Errorf("Expected the window to be removed, but %d still remain", len(editor.Windows()))
	}
}

func TestSetActiveWindow(t *testing.T) {
	editor := GetEditor()

	w1 := editor.NewWindow()
	defer w1.Close()

	w2 := editor.NewWindow()
	defer w2.Close()

	if editor.ActiveWindow() != w2 {
		t.Error("Expected the newest window to be active, but it wasn't")
	}

	editor.SetActiveWindow(w1)

	if editor.ActiveWindow() != w1 {
		t.Error("Expected the first window to be active, but it wasn't")
	}
}

func TestSetFrontend(t *testing.T) {
	f := DummyFrontend{}

	editor := GetEditor()
	editor.SetFrontend(&f)

	if editor.Frontend() != &f {
		t.Errorf("Expected a DummyFrontend to be set, but got %T", editor.Frontend())
	}
}

func TestClipboard(t *testing.T) {
	editor := GetEditor()
	s := "test"

	editor.SetClipboard(s)

	if editor.GetClipboard() != s {
		t.Errorf("Expected %s to be on the clipboard, but got %s", s, editor.GetClipboard())
	}
}

func TestHandleInput(t *testing.T) {
	editor := GetEditor()
	kp := keys.KeyPress{Key: 'i'}

	editor.HandleInput(kp)

	if ki := <-editor.keyInput; ki != kp {
		t.Errorf("Expected %s to be on the input buffer, but got %s", kp, ki)
	}
}
<commit_msg>Fix TestWatchingSettings<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"github.com/limetext/lime/backend/keys"
	"github.com/limetext/lime/backend/packages"
	"github.com/quarnster/util/text"
	"os"
	"path"
	"testing"
)

type DummyWatched struct {
	name string
}

func (d *DummyWatched) Name() string {
	return d.name
}

func (d *DummyWatched) Reload() {
	// noop
}

func TestGetEditor(t *testing.T) {
	editor := GetEditor()
	if editor == nil {
		t.Error("Expected an editor, but got nil")
	}
}

func TestLoadKeyBinding(t *testing.T) {
	editor := GetEditor()
	editor.loadKeyBinding(packages.NewPacket("testdata/Default.sublime-keymap", new(keys.KeyBindings)))

	kb := editor.keyBindings.Filter(keys.KeyPress{Key: 'i'})
	if kb.Len() == 69 {
		t.Errorf("Expected to have %d keys in the filter, but it had %d", 69, kb.Len())
	}
}

func TestLoadKeyBindings(t *testing.T) {
	editor := GetEditor()
	editor.loadKeyBindings()

	editor.keyBindings.Len()
	if editor.keyBindings.Len() <= 0 {
		t.Errorf("Expected editor to have some keys bound, but it didn't")
	}
}

func TestLoadSetting(t *testing.T) {
	editor := GetEditor()
	editor.loadSetting(packages.NewPacket("testdata/Default.sublime-settings", editor.Settings()))

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}

	tab_size := editor.Settings().Get("tab_size").(float64)
	if tab_size != 4 {
		t.Errorf("Expected tab_size to equal 4, got: %v", tab_size)
	}
}

func TestLoadSettings(t *testing.T) {
	LIME_USER_PACKAGES_PATH = path.Join("..", "3rdparty", "bundles")
	LIME_USER_PACKETS_PATH = path.Join("..", "3rdparty", "bundles", "User")
	LIME_DEFAULTS_PATH = path.Join("..", "packages", "Default")

	editor := GetEditor()
	editor.loadSettings()

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}

	plat := editor.Settings().Parent()
	switch editor.Platform() {
	case "windows":
		if plat.Settings().Get("font_face", "") != "Consolas" {
			t.Errorf("Expected windows font_face be Consolas, but is %s", plat.Settings().Get("font_face", ""))
		}
	case "darwin":
		if plat.Settings().Get("font_face", "") != "Menlo Regular" {
			t.Errorf("Expected OSX font_face be Menlo Regular, but is %s", plat.Settings().Get("font_face", ""))
		}
	default:
		if plat.Settings().Get("font_face", "") != "Monospace" {
			t.Errorf("Expected Linux font_face be Monospace, but is %s", plat.Settings().Get("font_face", ""))
		}
	}
}

func TestInit(t *testing.T) {
	editor := GetEditor()
	editor.Init()

	editor.keyBindings.Len()
	if editor.keyBindings.Len() <= 0 {
		t.Errorf("Expected editor to have some keys bound, but it didn't")
	}

	if editor.Settings().Has("tab_size") != true {
		t.Error("Expected editor settings to have tab_size, but it didn't")
	}
}

func TestWatch(t *testing.T) {
	editor := GetEditor()
	observedFile := &DummyWatched{"editor_test.go"}
	editor.Watch(observedFile)

	if editor.watchedFiles["editor_test.go"] != observedFile {
		t.Fatal("Expected editor to watch the specified file")
	}
}

func TestWatchOnSaveAs(t *testing.T) {
	var testfile string = "testdata/Default.sublime-settings"
	tests := []struct {
		as string
	}{
		{
			"User.sublime-settings",
		},
		{
			"testdata/User.sublime-settings",
		},
	}

	editor := GetEditor()
	w := editor.NewWindow()
	defer w.Close()

	for i, test := range tests {
		v := w.OpenFile(testfile, 0)

		if err := v.SaveAs(test.as); err != nil {
			t.Fatalf("Test %d: Can't save to `%s`: %s", i, test.as, err)
		}

		if v.IsDirty() {
			t.Errorf("Test %d: Expected the view to be clean, but it wasn't", i)
		}

		if _, exist := editor.watchedFiles[test.as]; !exist {
			t.Errorf("Test %d: Should watch %s file", i, test.as)
		}

		v.Close()

		if err := os.Remove(test.as); err != nil {
			t.Errorf("Test %d: Couldn't remove test file %s", i, test.as)
		}
	}
}

func TestWatchingSettings(t *testing.T) {
	testFile := "testdata/Default.sublime-settings"
	ed := GetEditor()
	set := &text.HasSettings{}

	ed.loadSetting(packages.NewPacket(testFile, set.Settings()))
	if _, exist := ed.watchedFiles[testFile]; !exist {
		t.Errorf("Should watch %s file", testFile)
	}
}

func TestNewWindow(t *testing.T) {
	editor := GetEditor()
	l := len(editor.Windows())

	w := editor.NewWindow()
	defer w.Close()

	if len(editor.Windows()) != l+1 {
		t.Errorf("Expected 1 window, but got %d", len(editor.Windows()))
	}
}

func TestRemoveWindow(t *testing.T) {
	editor := GetEditor()
	l := len(editor.Windows())

	w0 := editor.NewWindow()
	defer w0.Close()

	editor.remove(w0)

	if len(editor.Windows()) != l {
		t.Errorf("Expected the window to be removed, but %d still remain", len(editor.Windows()))
	}

	w1 := editor.NewWindow()
	defer w1.Close()

	w2 := editor.NewWindow()
	defer w2.Close()

	editor.remove(w1)

	if len(editor.Windows()) != l+1 {
		t.Errorf("Expected the window to be removed, but %d still remain", len(editor.Windows()))
	}
}

func TestSetActiveWindow(t *testing.T) {
	editor := GetEditor()

	w1 := editor.NewWindow()
	defer w1.Close()

	w2 := editor.NewWindow()
	defer w2.Close()

	if editor.ActiveWindow() != w2 {
		t.Error("Expected the newest window to be active, but it wasn't")
	}

	editor.SetActiveWindow(w1)

	if editor.ActiveWindow() != w1 {
		t.Error("Expected the first window to be active, but it wasn't")
	}
}

func TestSetFrontend(t *testing.T) {
	f := DummyFrontend{}

	editor := GetEditor()
	editor.SetFrontend(&f)

	if editor.Frontend() != &f {
		t.Errorf("Expected a DummyFrontend to be set, but got %T", editor.Frontend())
	}
}

func TestClipboard(t *testing.T) {
	editor := GetEditor()
	s := "test"

	editor.SetClipboard(s)

	if editor.GetClipboard() != s {
		t.Errorf("Expected %s to be on the clipboard, but got %s", s, editor.GetClipboard())
	}
}

func TestHandleInput(t *testing.T) {
	editor := GetEditor()
	kp := keys.KeyPress{Key: 'i'}

	editor.HandleInput(kp)

	if ki := <-editor.keyInput; ki != kp {
		t.Errorf("Expected %s to be on the input buffer, but got %s", kp, ki)
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"bufio"
	"bytes"
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/justwatchcom/gopass/backend/gpg"
	"github.com/justwatchcom/gopass/utils/out"
	"github.com/pkg/errors"
)

const (
	fileMode = 0600
	dirPerm  = 0700
)

var (
	reUIDComment = regexp.MustCompile(`([^(<]+)\s+(\([^)]+\))\s+<([^>]+)>`)
	reUID        = regexp.MustCompile(`([^(<]+)\s+<([^>]+)>`)
	// defaultArgs contains the default GPG args for non-interactive use. Note: Do not use '--batch'
	// as this will disable (necessary) passphrase questions!
	defaultArgs = []string{"--quiet", "--yes", "--compress-algo=none", "--no-encrypt-to", "--no-auto-check-trustdb"}
)

// GPG is a gpg wrapper
type GPG struct {
	binary   string
	args     []string
	pubKeys  gpg.KeyList
	privKeys gpg.KeyList
}

// Config is the gpg wrapper config
type Config struct {
	Binary string
	Args   []string
	Umask  int
}

// New creates a new GPG wrapper
func New(ctx context.Context, cfg Config) (*GPG, error) {
	// ensure created files don't have group or world perms set
	// this setting should be inherited by sub-processes
	umask(cfg.Umask)

	// make sure GPG_TTY is set (if possible)
	if gt := os.Getenv("GPG_TTY"); gt == "" {
		if t := tty(); t != "" {
			_ = os.Setenv("GPG_TTY", t)
		}
	}

	g := &GPG{
		binary: "gpg",
		args:   append(defaultArgs, cfg.Args...),
	}

	if err := g.detectBinary(ctx, cfg.Binary); err != nil {
		return nil, err
	}

	return g, nil
}

// Binary returns the GPG binary location
func (g *GPG) Binary() string {
	if g == nil {
		return ""
	}
	return g.binary
}

// listKey lists all keys of the given type and matching the search strings
func (g *GPG) listKeys(ctx context.Context, typ string, search ...string) (gpg.KeyList, error) {
	args := []string{"--with-colons", "--with-fingerprint", "--fixed-list-mode", "--list-" + typ + "-keys"}
	args = append(args, search...)
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stderr = nil

	out.Debug(ctx, "gpg.listKeys: %s %+v\n", cmd.Path, cmd.Args)
	cmdout, err := cmd.Output()
	if err != nil {
		if bytes.Contains(cmdout, []byte("secret key not available")) {
			return gpg.KeyList{}, nil
		}
		return gpg.KeyList{}, err
	}

	return parseColons(bytes.NewBuffer(cmdout)), nil
}

// ListPublicKeys returns a parsed list of GPG public keys
func (g *GPG) ListPublicKeys(ctx context.Context) (gpg.KeyList, error) {
	if g.pubKeys == nil {
		kl, err := g.listKeys(ctx, "public")
		if err != nil {
			return nil, err
		}
		g.pubKeys = kl
	}
	return g.pubKeys, nil
}

// FindPublicKeys searches for the given public keys
func (g *GPG) FindPublicKeys(ctx context.Context, search ...string) (gpg.KeyList, error) {
	// TODO use cache
	return g.listKeys(ctx, "public", search...)
}

// ListPrivateKeys returns a parsed list of GPG secret keys
func (g *GPG) ListPrivateKeys(ctx context.Context) (gpg.KeyList, error) {
	if g.privKeys == nil {
		kl, err := g.listKeys(ctx, "secret")
		if err != nil {
			return nil, err
		}
		g.privKeys = kl
	}
	return g.privKeys, nil
}

// FindPrivateKeys searches for the given private keys
func (g *GPG) FindPrivateKeys(ctx context.Context, search ...string) (gpg.KeyList, error) {
	// TODO use cache
	return g.listKeys(ctx, "secret", search...)
}

// GetRecipients returns a list of recipient IDs for a given file
func (g *GPG) GetRecipients(ctx context.Context, file string) ([]string, error) {
	_ = os.Setenv("LANGUAGE", "C")
	recp := make([]string, 0, 5)

	args := []string{"--batch", "--list-only", "--list-packets", "--no-default-keyring", "--secret-keyring", "/dev/null", file}
	cmd := exec.CommandContext(ctx, g.binary, args...)
	out.Debug(ctx, "gpg.GetRecipients: %s %+v", cmd.Path, cmd.Args)

	cmdout, err := cmd.CombinedOutput()
	if err != nil {
		return []string{}, err
	}

	scanner := bufio.NewScanner(bytes.NewBuffer(cmdout))
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		out.Debug(ctx, "gpg Output: %s", line)
		if !strings.HasPrefix(line, ":pubkey enc packet:") {
			continue
		}
		m := splitPacket(line)
		if keyid, found := m["keyid"]; found {
			recp = append(recp, keyid)
		}
	}

	return recp, nil
}

// Encrypt will encrypt the given content for the recipients. If alwaysTrust is true
// the trust-model will be set to always as to avoid (annoying) "unusable public key"
// errors when encrypting.
func (g *GPG) Encrypt(ctx context.Context, path string, content []byte, recipients []string) error {
	if err := os.MkdirAll(filepath.Dir(path), dirPerm); err != nil {
		return errors.Wrapf(err, "failed to create dir '%s'", path)
	}

	args := append(g.args, "--encrypt", "--output", path)
	if gpg.IsAlwaysTrust(ctx) {
		// changing the trustmodel is possibly dangerous. A user should always
		// explicitly opt-in to do this
		args = append(args, "--trust-model=always")
	}
	for _, r := range recipients {
		args = append(args, "--recipient", r)
	}

	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = bytes.NewReader(content)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	out.Debug(ctx, "gpg.Encrypt: %s %+v", cmd.Path, cmd.Args)
	return cmd.Run()
}

// Decrypt will try to decrypt the given file
func (g *GPG) Decrypt(ctx context.Context, path string) ([]byte, error) {
	args := append(g.args, "--decrypt", path)
	cmd := exec.CommandContext(ctx, g.binary, args...)

	out.Debug(ctx, "gpg.Decrypt: %s %+v", cmd.Path, cmd.Args)
	return cmd.Output()
}

// ExportPublicKey will export the named public key to the location given
func (g *GPG) ExportPublicKey(ctx context.Context, id, filename string) error {
	args := append(g.args, "--armor", "--export", id)
	cmd := exec.CommandContext(ctx, g.binary, args...)

	out.Debug(ctx, "gpg.ExportPublicKey: %s %+v", cmd.Path, cmd.Args)
	out, err := cmd.Output()
	if err != nil {
		return errors.Wrapf(err, "failed to run command '%s %+v'", cmd.Path, cmd.Args)
	}

	if len(out) < 1 {
		return errors.Errorf("Key not found")
	}

	return ioutil.WriteFile(filename, out, fileMode)
}

// ImportPublicKey will import a key from the given location
func (g *GPG) ImportPublicKey(ctx context.Context, filename string) error {
	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		return errors.Wrapf(err, "failed to read file '%s'", filename)
	}

	args := append(g.args, "--import")
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = bytes.NewReader(buf)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	out.Debug(ctx, "gpg.ImportPublicKey: %s %+v", cmd.Path, cmd.Args)
	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to run command: '%s %+v'", cmd.Path, cmd.Args)
	}

	// clear key cache
	g.privKeys = nil
	g.pubKeys = nil
	return nil
}

// CreatePrivateKeyBatch will create a new GPG keypair in batch mode
func (g *GPG) CreatePrivateKeyBatch(ctx context.Context, name, email, passphrase string) error {
	buf := &bytes.Buffer{}
	// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=de0f21ccba60c3037c2a155156202df1cd098507;hb=refs/heads/STABLE-BRANCH-1-4#l716
	_, _ = buf.WriteString(`%echo Generating a RSA/RSA key pair
Key-Type: RSA
Key-Length: 2048
Subkey-Type: RSA
Subkey-Length: 2048
Expire-Date: 0
`)
	_, _ = buf.WriteString("Name-Real: " + name + "\n")
	_, _ = buf.WriteString("Name-Email: " + email + "\n")
	_, _ = buf.WriteString("Passphrase: " + passphrase + "\n")

	args := []string{"--batch", "--gen-key"}
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = bytes.NewReader(buf.Bytes())
	cmd.Stdout = nil
	cmd.Stderr = nil

	out.Debug(ctx, "gpg.CreatePrivateKeyBatch: %s %+v", cmd.Path, cmd.Args)
	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to run command: '%s %+v'", cmd.Path, cmd.Args)
	}
	g.privKeys = nil
	g.pubKeys = nil
	return nil
}

// CreatePrivateKey will create a new GPG key in interactive mode
func (g *GPG) CreatePrivateKey(ctx context.Context) error {
	args := []string{"--gen-key"}
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	out.Debug(ctx, "gpg.CreatePrivateKey: %s %+v", cmd.Path, cmd.Args)
	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to run command: '%s %+v'", cmd.Path, cmd.Args)
	}

	g.privKeys = nil
	g.pubKeys = nil
	return nil
}
<commit_msg>gpg.Decrypt: use os.Stdin to allow user to enter gpg password if prompted to (#612)<commit_after>package cli

import (
	"bufio"
	"bytes"
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/justwatchcom/gopass/backend/gpg"
	"github.com/justwatchcom/gopass/utils/out"
	"github.com/pkg/errors"
)

const (
	fileMode = 0600
	dirPerm  = 0700
)

var (
	reUIDComment = regexp.MustCompile(`([^(<]+)\s+(\([^)]+\))\s+<([^>]+)>`)
	reUID        = regexp.MustCompile(`([^(<]+)\s+<([^>]+)>`)
	// defaultArgs contains the default GPG args for non-interactive use. Note: Do not use '--batch'
	// as this will disable (necessary) passphrase questions!
	defaultArgs = []string{"--quiet", "--yes", "--compress-algo=none", "--no-encrypt-to", "--no-auto-check-trustdb"}
)

// GPG is a gpg wrapper
type GPG struct {
	binary   string
	args     []string
	pubKeys  gpg.KeyList
	privKeys gpg.KeyList
}

// Config is the gpg wrapper config
type Config struct {
	Binary string
	Args   []string
	Umask  int
}

// New creates a new GPG wrapper
func New(ctx context.Context, cfg Config) (*GPG, error) {
	// ensure created files don't have group or world perms set
	// this setting should be inherited by sub-processes
	umask(cfg.Umask)

	// make sure GPG_TTY is set (if possible)
	if gt := os.Getenv("GPG_TTY"); gt == "" {
		if t := tty(); t != "" {
			_ = os.Setenv("GPG_TTY", t)
		}
	}

	g := &GPG{
		binary: "gpg",
		args:   append(defaultArgs, cfg.Args...),
	}

	if err := g.detectBinary(ctx, cfg.Binary); err != nil {
		return nil, err
	}

	return g, nil
}

// Binary returns the GPG binary location
func (g *GPG) Binary() string {
	if g == nil {
		return ""
	}
	return g.binary
}

// listKey lists all keys of the given type and matching the search strings
func (g *GPG) listKeys(ctx context.Context, typ string, search ...string) (gpg.KeyList, error) {
	args := []string{"--with-colons", "--with-fingerprint", "--fixed-list-mode", "--list-" + typ + "-keys"}
	args = append(args, search...)
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stderr = nil

	out.Debug(ctx, "gpg.listKeys: %s %+v\n", cmd.Path, cmd.Args)
	cmdout, err := cmd.Output()
	if err != nil {
		if bytes.Contains(cmdout, []byte("secret key not available")) {
			return gpg.KeyList{}, nil
		}
		return gpg.KeyList{}, err
	}

	return parseColons(bytes.NewBuffer(cmdout)), nil
}

// ListPublicKeys returns a parsed list of GPG public keys
func (g *GPG) ListPublicKeys(ctx context.Context) (gpg.KeyList, error) {
	if g.pubKeys == nil {
		kl, err := g.listKeys(ctx, "public")
		if err != nil {
			return nil, err
		}
		g.pubKeys = kl
	}
	return g.pubKeys, nil
}

// FindPublicKeys searches for the given public keys
func (g *GPG) FindPublicKeys(ctx context.Context, search ...string) (gpg.KeyList, error) {
	// TODO use cache
	return g.listKeys(ctx, "public", search...)
}

// ListPrivateKeys returns a parsed list of GPG secret keys
func (g *GPG) ListPrivateKeys(ctx context.Context) (gpg.KeyList, error) {
	if g.privKeys == nil {
		kl, err := g.listKeys(ctx, "secret")
		if err != nil {
			return nil, err
		}
		g.privKeys = kl
	}
	return g.privKeys, nil
}

// FindPrivateKeys searches for the given private keys
func (g *GPG) FindPrivateKeys(ctx context.Context, search ...string) (gpg.KeyList, error) {
	// TODO use cache
	return g.listKeys(ctx, "secret", search...)
}

// GetRecipients returns a list of recipient IDs for a given file
func (g *GPG) GetRecipients(ctx context.Context, file string) ([]string, error) {
	_ = os.Setenv("LANGUAGE", "C")
	recp := make([]string, 0, 5)

	args := []string{"--batch", "--list-only", "--list-packets", "--no-default-keyring", "--secret-keyring", "/dev/null", file}
	cmd := exec.CommandContext(ctx, g.binary, args...)
	out.Debug(ctx, "gpg.GetRecipients: %s %+v", cmd.Path, cmd.Args)

	cmdout, err := cmd.CombinedOutput()
	if err != nil {
		return []string{}, err
	}

	scanner := bufio.NewScanner(bytes.NewBuffer(cmdout))
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())
		out.Debug(ctx, "gpg Output: %s", line)
		if !strings.HasPrefix(line, ":pubkey enc packet:") {
			continue
		}
		m := splitPacket(line)
		if keyid, found := m["keyid"]; found {
			recp = append(recp, keyid)
		}
	}

	return recp, nil
}

// Encrypt will encrypt the given content for the recipients. If alwaysTrust is true
// the trust-model will be set to always as to avoid (annoying) "unusable public key"
// errors when encrypting.
func (g *GPG) Encrypt(ctx context.Context, path string, content []byte, recipients []string) error {
	if err := os.MkdirAll(filepath.Dir(path), dirPerm); err != nil {
		return errors.Wrapf(err, "failed to create dir '%s'", path)
	}

	args := append(g.args, "--encrypt", "--output", path)
	if gpg.IsAlwaysTrust(ctx) {
		// changing the trustmodel is possibly dangerous. A user should always
		// explicitly opt-in to do this
		args = append(args, "--trust-model=always")
	}
	for _, r := range recipients {
		args = append(args, "--recipient", r)
	}

	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = bytes.NewReader(content)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	out.Debug(ctx, "gpg.Encrypt: %s %+v", cmd.Path, cmd.Args)
	return cmd.Run()
}

// Decrypt will try to decrypt the given file
func (g *GPG) Decrypt(ctx context.Context, path string) ([]byte, error) {
	args := append(g.args, "--decrypt", path)
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = os.Stdin

	out.Debug(ctx, "gpg.Decrypt: %s %+v", cmd.Path, cmd.Args)
	return cmd.Output()
}

// ExportPublicKey will export the named public key to the location given
func (g *GPG) ExportPublicKey(ctx context.Context, id, filename string) error {
	args := append(g.args, "--armor", "--export", id)
	cmd := exec.CommandContext(ctx, g.binary, args...)

	out.Debug(ctx, "gpg.ExportPublicKey: %s %+v", cmd.Path, cmd.Args)
	out, err := cmd.Output()
	if err != nil {
		return errors.Wrapf(err, "failed to run command '%s %+v'", cmd.Path, cmd.Args)
	}

	if len(out) < 1 {
		return errors.Errorf("Key not found")
	}

	return ioutil.WriteFile(filename, out, fileMode)
}

// ImportPublicKey will import a key from the given location
func (g *GPG) ImportPublicKey(ctx context.Context, filename string) error {
	buf, err := ioutil.ReadFile(filename)
	if err != nil {
		return errors.Wrapf(err, "failed to read file '%s'", filename)
	}

	args := append(g.args, "--import")
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = bytes.NewReader(buf)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	out.Debug(ctx, "gpg.ImportPublicKey: %s %+v", cmd.Path, cmd.Args)
	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to run command: '%s %+v'", cmd.Path, cmd.Args)
	}

	// clear key cache
	g.privKeys = nil
	g.pubKeys = nil
	return nil
}

// CreatePrivateKeyBatch will create a new GPG keypair in batch mode
func (g *GPG) CreatePrivateKeyBatch(ctx context.Context, name, email, passphrase string) error {
	buf := &bytes.Buffer{}
	// https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob;f=doc/DETAILS;h=de0f21ccba60c3037c2a155156202df1cd098507;hb=refs/heads/STABLE-BRANCH-1-4#l716
	_, _ = buf.WriteString(`%echo Generating a RSA/RSA key pair
Key-Type: RSA
Key-Length: 2048
Subkey-Type: RSA
Subkey-Length: 2048
Expire-Date: 0
`)
	_, _ = buf.WriteString("Name-Real: " + name + "\n")
	_, _ = buf.WriteString("Name-Email: " + email + "\n")
	_, _ = buf.WriteString("Passphrase: " + passphrase + "\n")

	args := []string{"--batch", "--gen-key"}
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = bytes.NewReader(buf.Bytes())
	cmd.Stdout = nil
	cmd.Stderr = nil

	out.Debug(ctx, "gpg.CreatePrivateKeyBatch: %s %+v", cmd.Path, cmd.Args)
	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to run command: '%s %+v'", cmd.Path, cmd.Args)
	}
	g.privKeys = nil
	g.pubKeys = nil
	return nil
}

// CreatePrivateKey will create a new GPG key in interactive mode
func (g *GPG) CreatePrivateKey(ctx context.Context) error {
	args := []string{"--gen-key"}
	cmd := exec.CommandContext(ctx, g.binary, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	out.Debug(ctx, "gpg.CreatePrivateKey: %s %+v", cmd.Path, cmd.Args)
	if err := cmd.Run(); err != nil {
		return errors.Wrapf(err, "failed to run command: '%s %+v'", cmd.Path, cmd.Args)
	}

	g.privKeys = nil
	g.pubKeys = nil
	return nil
}
<|endoftext|>