<commit_before>/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * 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 bazel

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"regexp"

	xapb "kythe.io/third_party/bazel/extra_actions_base_go_proto"
)

// Settings control the construction of an extractor config from common path
// and name filtering settings.
type Settings struct {
	Corpus      string // the corpus label to assign (required)
	Language    string // the language label to assign (required)
	ExtraAction string // path of blaze.ExtraActionInfo file (required)
	VNameRules  string // path of vnames.json file (optional)
	Include     string // include files matching this RE2 ("" includes all files)
	Exclude     string // exclude files matching this RE2 ("" excludes no files)
	SourceFiles string // mark files matching this RE2 as sources ("" marks none)
	SourceArgs  string // mark arguments matching this RE2 as sources ("" marks none)

	Scoped  bool // only match source paths within the target package
	Verbose bool // enable verbose per-file logging
}

// SetFlags adds flags to f for each of the fields of s.  The specified prefix
// is prepended to each base flag name. If f == nil, flag.CommandLine is used.
//
// The returned function provides a default usage message that can be used to
// populate flag.Usage or discarded at the caller's discretion.
func (s *Settings) SetFlags(f *flag.FlagSet, prefix string) func() {
	if f == nil {
		f = flag.CommandLine
	}
	p := func(s string) string { return prefix + s }
	f.StringVar(&s.Corpus, p("corpus"), "", "Corpus label to assign (required)")
	f.StringVar(&s.Language, p("language"), "", "Language label to assign (required)")
	f.StringVar(&s.ExtraAction, p("extra_action"), "",
		"Path of blaze.ExtraActionInfo file (required)")
	f.StringVar(&s.VNameRules, p("rules"), "",
		"Path of vnames.json file (optional)")
	f.StringVar(&s.Include, p("include"), "",
		`RE2 matching files to include (if "", include all files)`)
	f.StringVar(&s.Exclude, p("exclude"), "",
		`RE2 matching files to exclude (if "", exclude none)`)
	f.StringVar(&s.SourceFiles, p("source"), "",
		`RE2 matching source files (if "", select none)`)
	f.StringVar(&s.SourceArgs, p("args"), "",
		`RE2 matching arguments to consider source files (if "", select none)`)
	f.BoolVar(&s.Scoped, p("scoped"), false,
		"Only match source paths within the target package")
	f.BoolVar(&s.Verbose, p("v"), false,
		"Enable verbose (per-file) logging")

	// A default usage message the caller may use to populate flag.Usage.
	return func() {
		fmt.Fprintf(os.Stderr,
			`Usage: %s [options] -corpus C -language L -extra_action f.xa -output f.kindex

Read an ExtraActionInput protobuf message from the designated -extra_action
file[*] and generate a Kythe compilation record in the specified -output file.
The -corpus and -language labels are required to be non-empty.  This only works
for "spawn" actions; any other action will cause the extraction to fail.

By default, all input files listed in the XA are captured in the output.
To capture only files matching a RE2 regexp, use -include.
To explicitly exclude otherwise-matched files, use -exclude.
If both are given, -exclude applies to files selected by -include.

To designate paths as source inputs, set -source to a RE2 regexp matching them,
or set -args to a RE2 regexp matching command-line arguments that should be
considered source paths.  At least one of these must be set, and it is safe to
set both; the results will be merged.

[*] https://bazel.build/versions/master/docs/be/extra-actions.html

Options:
`, filepath.Base(os.Args[0]))
		f.PrintDefaults()
	}
}

func (s *Settings) validate() error {
	switch {
	case s.Corpus == "":
		return errors.New("you must provide a non-empty corpus label")
	case s.Language == "":
		return errors.New("you must provide a non-empty language label")
	case s.ExtraAction == "":
		return errors.New("you must provide a non-empty extra action filename")
	case s.SourceFiles == "" && s.SourceArgs == "":
		return errors.New("you must set an expression for source files or source args")
	}
	return nil
}

// NewFromSettings constructs a new *Config from the given settings, and
// returns the extra action info fetched from the corresponding file.
//
// An error is reported if the settings are invalid, or the inputs to the
// config could not be loaded.
func NewFromSettings(s Settings) (*Config, *xapb.ExtraActionInfo, error) {
	if err := s.validate(); err != nil {
		return nil, nil, err
	}

	// Ensure the extra action can be loaded.
	info, err := LoadAction(s.ExtraAction)
	if err != nil {
		return nil, nil, fmt.Errorf("loading extra action: %v", err)
	}
	pkg := PackageName(info.GetOwner())
	log.Printf("Extra action for target %q (package %q)", info.GetOwner(), pkg)

	rules, err := LoadRules(s.VNameRules)
	if err != nil {
		return nil, nil, fmt.Errorf("loading rules: %v", err)
	}

	config := &Config{
		Corpus:     s.Corpus,
		Language:   s.Language,
		Rules:      rules,
		Verbose:    s.Verbose,
		CheckInput: func(path string) (string, bool) { return path, true },
	}

	// If there is a file inclusion regexp, replace the input filter with it.
	if s.Include != "" {
		r, err := regexp.Compile(s.Include)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid inclusion regexp: %v", err)
		}
		config.CheckInput = func(path string) (string, bool) {
			return path, r.MatchString(path)
		}
	}

	// If there is a file exclusion regexp, add it to the input filter.  We
	// know there is already a callback in place (either a match or the trivial
	// default).
	if s.Exclude != "" {
		r, err := regexp.Compile(s.Exclude)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid exclusion regexp: %v", err)
		}

		base := config.CheckInput
		config.CheckInput = func(path string) (string, bool) {
			if path, ok := base(path); ok {
				return path, !r.MatchString(path)
			}
			return "", false
		}
	}

	// If there is a source matching regexp, set a filter for it.
	if s.SourceFiles != "" {
		r, err := regexp.Compile(s.SourceFiles)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid source file regexp: %v", err)
		}
		if s.Scoped {
			config.IsSource = func(path string) bool {
				return r.MatchString(path) && PathInPackage(path, pkg)
			}
		} else {
			config.IsSource = r.MatchString
		}
	}

	// If we have been asked to include source files from the argument list,
	// add a fixup to do that at the end.
	if s.SourceArgs != "" {
		r, err := regexp.Compile(s.SourceArgs)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid source args regexp: %v", err)
		}
		config.Fixup = FindSourceArgs(r)
	}

	return config, info, nil
}
<commit_msg>Rename flag -v to -verbose.<commit_after>/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * 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 bazel

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"regexp"

	xapb "kythe.io/third_party/bazel/extra_actions_base_go_proto"
)

// Settings control the construction of an extractor config from common path
// and name filtering settings.
type Settings struct {
	Corpus      string // the corpus label to assign (required)
	Language    string // the language label to assign (required)
	ExtraAction string // path of blaze.ExtraActionInfo file (required)
	VNameRules  string // path of vnames.json file (optional)
	Include     string // include files matching this RE2 ("" includes all files)
	Exclude     string // exclude files matching this RE2 ("" excludes no files)
	SourceFiles string // mark files matching this RE2 as sources ("" marks none)
	SourceArgs  string // mark arguments matching this RE2 as sources ("" marks none)

	Scoped  bool // only match source paths within the target package
	Verbose bool // enable verbose per-file logging
}

// SetFlags adds flags to f for each of the fields of s.  The specified prefix
// is prepended to each base flag name. If f == nil, flag.CommandLine is used.
//
// The returned function provides a default usage message that can be used to
// populate flag.Usage or discarded at the caller's discretion.
func (s *Settings) SetFlags(f *flag.FlagSet, prefix string) func() {
	if f == nil {
		f = flag.CommandLine
	}
	p := func(s string) string { return prefix + s }
	f.StringVar(&s.Corpus, p("corpus"), "", "Corpus label to assign (required)")
	f.StringVar(&s.Language, p("language"), "", "Language label to assign (required)")
	f.StringVar(&s.ExtraAction, p("extra_action"), "",
		"Path of blaze.ExtraActionInfo file (required)")
	f.StringVar(&s.VNameRules, p("rules"), "",
		"Path of vnames.json file (optional)")
	f.StringVar(&s.Include, p("include"), "",
		`RE2 matching files to include (if "", include all files)`)
	f.StringVar(&s.Exclude, p("exclude"), "",
		`RE2 matching files to exclude (if "", exclude none)`)
	f.StringVar(&s.SourceFiles, p("source"), "",
		`RE2 matching source files (if "", select none)`)
	f.StringVar(&s.SourceArgs, p("args"), "",
		`RE2 matching arguments to consider source files (if "", select none)`)
	f.BoolVar(&s.Scoped, p("scoped"), false,
		"Only match source paths within the target package")
	f.BoolVar(&s.Verbose, p("verbose"), false,
		"Enable verbose (per-file) logging")

	// A default usage message the caller may use to populate flag.Usage.
	return func() {
		fmt.Fprintf(os.Stderr,
			`Usage: %s [options] -corpus C -language L -extra_action f.xa -output f.kindex

Read an ExtraActionInput protobuf message from the designated -extra_action
file[*] and generate a Kythe compilation record in the specified -output file.
The -corpus and -language labels are required to be non-empty.  This only works
for "spawn" actions; any other action will cause the extraction to fail.

By default, all input files listed in the XA are captured in the output.
To capture only files matching a RE2 regexp, use -include.
To explicitly exclude otherwise-matched files, use -exclude.
If both are given, -exclude applies to files selected by -include.

To designate paths as source inputs, set -source to a RE2 regexp matching them,
or set -args to a RE2 regexp matching command-line arguments that should be
considered source paths.  At least one of these must be set, and it is safe to
set both; the results will be merged.

[*] https://bazel.build/versions/master/docs/be/extra-actions.html

Options:
`, filepath.Base(os.Args[0]))
		f.PrintDefaults()
	}
}

func (s *Settings) validate() error {
	switch {
	case s.Corpus == "":
		return errors.New("you must provide a non-empty corpus label")
	case s.Language == "":
		return errors.New("you must provide a non-empty language label")
	case s.ExtraAction == "":
		return errors.New("you must provide a non-empty extra action filename")
	case s.SourceFiles == "" && s.SourceArgs == "":
		return errors.New("you must set an expression for source files or source args")
	}
	return nil
}

// NewFromSettings constructs a new *Config from the given settings, and
// returns the extra action info fetched from the corresponding file.
//
// An error is reported if the settings are invalid, or the inputs to the
// config could not be loaded.
func NewFromSettings(s Settings) (*Config, *xapb.ExtraActionInfo, error) {
	if err := s.validate(); err != nil {
		return nil, nil, err
	}

	// Ensure the extra action can be loaded.
	info, err := LoadAction(s.ExtraAction)
	if err != nil {
		return nil, nil, fmt.Errorf("loading extra action: %v", err)
	}
	pkg := PackageName(info.GetOwner())
	log.Printf("Extra action for target %q (package %q)", info.GetOwner(), pkg)

	rules, err := LoadRules(s.VNameRules)
	if err != nil {
		return nil, nil, fmt.Errorf("loading rules: %v", err)
	}

	config := &Config{
		Corpus:     s.Corpus,
		Language:   s.Language,
		Rules:      rules,
		Verbose:    s.Verbose,
		CheckInput: func(path string) (string, bool) { return path, true },
	}

	// If there is a file inclusion regexp, replace the input filter with it.
	if s.Include != "" {
		r, err := regexp.Compile(s.Include)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid inclusion regexp: %v", err)
		}
		config.CheckInput = func(path string) (string, bool) {
			return path, r.MatchString(path)
		}
	}

	// If there is a file exclusion regexp, add it to the input filter.  We
	// know there is already a callback in place (either a match or the trivial
	// default).
	if s.Exclude != "" {
		r, err := regexp.Compile(s.Exclude)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid exclusion regexp: %v", err)
		}

		base := config.CheckInput
		config.CheckInput = func(path string) (string, bool) {
			if path, ok := base(path); ok {
				return path, !r.MatchString(path)
			}
			return "", false
		}
	}

	// If there is a source matching regexp, set a filter for it.
	if s.SourceFiles != "" {
		r, err := regexp.Compile(s.SourceFiles)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid source file regexp: %v", err)
		}
		if s.Scoped {
			config.IsSource = func(path string) bool {
				return r.MatchString(path) && PathInPackage(path, pkg)
			}
		} else {
			config.IsSource = r.MatchString
		}
	}

	// If we have been asked to include source files from the argument list,
	// add a fixup to do that at the end.
	if s.SourceArgs != "" {
		r, err := regexp.Compile(s.SourceArgs)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid source args regexp: %v", err)
		}
		config.Fixup = FindSourceArgs(r)
	}

	return config, info, nil
}
<|endoftext|>


<commit_before>package apidoc

import (
	"bufio"
	"bytes"
	"os"
)

const (
	outputFileName      = ".apidoc_gen.go"
)

type (
	ApiDocument struct {
		OutputFileName           string
		PackageName          string
		ApiDefines         []ApiDefine
	}
)

func NewDocument(packageName string) ApiDocument {
	os.Remove(outputFileName)
	return ApiDocument{
		PackageName: packageName,
		OutputFileName: outputFileName,
	}
}

func (doc *ApiDocument) New(name string) ApiDefine {
	return ApiDefine{
		Name: name,
	}
}
func (doc *ApiDocument) Add(define ApiDefine) {
	doc.ApiDefines = append(doc.ApiDefines, define)
}

func (doc *ApiDocument) Write() error {

	var bt bytes.Buffer

	// Header
	writeApidocHeader(doc.PackageName, &bt)

	// Define items
	for _, define := range doc.ApiDefines {
		define.WriteBytes(&bt)
	}

	err := writeFile(doc.OutputFileName, bt.Bytes())
	if err != nil {
		return err
	}
	return nil
}

// -------------------------------------
// PRIVATE METHOD
// -------------------------------------

func writeFile(fileName string, contents []byte) error {

	writeFile, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		return err
	}

	writer := bufio.NewWriter(writeFile)
	if _, err := writer.Write(contents); err != nil {
		return err
	}
	writer.Flush()
	return nil
}

func writeApidocHeader(packageName string, b *bytes.Buffer) {
	b.Write([]byte("package " + packageName))
	b.Write(bbreak)
	b.Write(bbreak)
}
<commit_msg>Added clear method by apiDocument<commit_after>package apidoc

import (
	"bufio"
	"bytes"
	"os"
)

const (
	outputFileName      = ".apidoc_gen.go"
)

type (
	ApiDocument struct {
		OutputFileName           string
		PackageName          string
		ApiDefines         []ApiDefine
		writeCount int
	}
)

func NewDocument(packageName string) ApiDocument {
	os.Remove(outputFileName)
	return ApiDocument{
		PackageName: packageName,
		OutputFileName: outputFileName,
	}
}

func (doc *ApiDocument) New(name string) ApiDefine {
	return ApiDefine{
		Name: name,
	}
}

func (doc *ApiDocument) Add(define ApiDefine) {
	doc.ApiDefines = append(doc.ApiDefines, define)
}

func (doc *ApiDocument) Clear() {
	doc.ApiDefines = []ApiDefine{}
}

func (doc *ApiDocument) Write() error {

	var bt bytes.Buffer

	// Header
	if doc.writeCount == 0 {
		writeApidocHeader(doc.PackageName, &bt)
	}
	doc.writeCount++

	// Define items
	for _, define := range doc.ApiDefines {
		define.WriteBytes(&bt)
	}

	err := writeFile(doc.OutputFileName, bt.Bytes())
	if err != nil {
		return err
	}
	return nil
}

// -------------------------------------
// PRIVATE METHOD
// -------------------------------------

func writeFile(fileName string, contents []byte) error {

	writeFile, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		return err
	}

	writer := bufio.NewWriter(writeFile)
	if _, err := writer.Write(contents); err != nil {
		return err
	}
	writer.Flush()
	return nil
}

func writeApidocHeader(packageName string, b *bytes.Buffer) {
	b.Write([]byte("package " + packageName))
	b.Write(bbreak)
	b.Write(bbreak)
}
<|endoftext|>


<commit_before>package irmaserver

import (
	"encoding/base64"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/go-errors/errors"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/server"
)

// Authenticator instances authenticate incoming session requests. Given details of the HTTP
// post done by the requestor, it is checked whether or not the requestor is known and
// allowed to submit session requests.
type Authenticator interface {
	// Initialize is called once on server startup for each requestor that uses this authentication method.
	// Used to parse keys or populate caches for later use.
	Initialize(name string, requestor Requestor) error

	// Authenticate checks, given the HTTP header and POST body, if the authenticator is known
	// and allowed to submit session requests. It returns whether or not the current authenticator
	// is applicable to this sesion requests; the request itself; the name of the requestor;
	// or an error (which is only non-nil if applies is true; i.e. this authenticator applies but
	// it was not able to successfully authenticate the request).
	Authenticate(
		headers http.Header, body []byte,
	) (applies bool, request irma.SessionRequest, requestor string, err *irma.RemoteError)
}

type AuthenticationMethod string

// Currently supported requestor authentication methods
const (
	AuthenticationMethodHmac      = "hmac"
	AuthenticationMethodPublicKey = "publickey"
	AuthenticationMethodToken     = "token"
	AuthenticationMethodNone      = "none"
)

type HmacAuthenticator struct {
	hmackeys map[string]interface{}
}
type PublicKeyAuthenticator struct {
	publickeys map[string]interface{}
}
type PresharedKeyAuthenticator struct {
	presharedkeys map[string]string
}
type NilAuthenticator struct{}

var authenticators map[AuthenticationMethod]Authenticator

func (NilAuthenticator) Authenticate(
	headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) {
	if headers.Get("Authentication") != "" || !strings.HasPrefix(headers.Get("Content-Type"), "application/json") {
		return false, nil, "", nil
	}
	request, err := server.ParseSessionRequest(body)
	if err != nil {
		return true, nil, "", server.RemoteError(server.ErrorInvalidRequest, err.Error())
	}
	return true, request, "", nil
}

func (NilAuthenticator) Initialize(name string, requestor Requestor) error {
	return nil
}

func (hauth *HmacAuthenticator) Authenticate(
	headers http.Header, body []byte,
) (applies bool, request irma.SessionRequest, requestor string, err *irma.RemoteError) {
	return jwtAuthenticate(headers, body, jwt.SigningMethodHS256.Name, hauth.hmackeys)
}

func (hauth *HmacAuthenticator) Initialize(name string, requestor Requestor) error {
	if requestor.AuthenticationKey == "" {
		return errors.Errorf("Requestor %s had no authentication key")
	}
	if bts, err := base64.StdEncoding.DecodeString(requestor.AuthenticationKey); err != nil {
		return err
	} else {
		hauth.hmackeys[name] = bts
		return nil
	}
}

func (pkauth *PublicKeyAuthenticator) Authenticate(
	headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) {
	return jwtAuthenticate(headers, body, jwt.SigningMethodRS256.Name, pkauth.publickeys)
}

func (pkauth *PublicKeyAuthenticator) Initialize(name string, requestor Requestor) error {
	var bts []byte
	var err error
	if strings.HasPrefix(requestor.AuthenticationKey, "-----BEGIN") {
		bts = []byte(requestor.AuthenticationKey)
	}
	if _, err := os.Stat(requestor.AuthenticationKey); err == nil {
		bts, err = ioutil.ReadFile(requestor.AuthenticationKey)
		if err != nil {
			return err
		}
	}
	if len(bts) == 0 {
		return errors.Errorf("Requestor %s has invalid public key", name)
	}
	pk, err := jwt.ParseRSAPublicKeyFromPEM(bts)
	if err != nil {
		return err
	}
	pkauth.publickeys[name] = pk

	return nil
}

func (pskauth *PresharedKeyAuthenticator) Authenticate(
	headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) {
	auth := headers.Get("Authentication")
	if auth == "" || !strings.HasPrefix(headers.Get("Content-Type"), "application/json") {
		return false, nil, "", nil
	}
	requestor, ok := pskauth.presharedkeys[auth]
	if !ok {
		return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "")
	}
	request, err := server.ParseSessionRequest(body)
	if err != nil {
		return true, nil, "", server.RemoteError(server.ErrorInvalidRequest, err.Error())
	}
	return true, request, requestor, nil
}

func (pskauth *PresharedKeyAuthenticator) Initialize(name string, requestor Requestor) error {
	if requestor.AuthenticationKey == "" {
		return errors.Errorf("Requestor %s had no authentication key")
	}
	pskauth.presharedkeys[requestor.AuthenticationKey] = name
	return nil
}

// Helper functions

// Given an (unauthenticated) jwt, return the key against which it should be verified using the "kid" header
func jwtKeyExtractor(publickeys map[string]interface{}) func(token *jwt.Token) (interface{}, error) {
	return func(token *jwt.Token) (interface{}, error) {
		var ok bool
		kid, ok := token.Header["kid"]
		if !ok {
			return nil, errors.New("No kid jwt header found")
		}
		requestor, ok := kid.(string)
		if !ok {
			return nil, errors.New("kid jwt header was not a string")
		}
		if pk, ok := publickeys[requestor]; ok {
			return pk, nil
		}
		return nil, errors.Errorf("Unknown requestor: %s", requestor)
	}
}

// jwtAuthenticate is a helper function for JWT-based authenticators that verifies and parses JWTs.
func jwtAuthenticate(
	headers http.Header, body []byte, signatureAlg string, keys map[string]interface{},
) (bool, irma.SessionRequest, string, *irma.RemoteError) {
	// Read JWT and check its type
	if headers.Get("Authorization") != "" || !strings.HasPrefix(headers.Get("Content-Type"), "text/plain") {
		return false, nil, "", nil
	}
	requestorJwt := string(body)
	alg, err := jwtSignatureAlg(requestorJwt)
	if err != nil || alg != signatureAlg {
		// If err != nil, ie. we failed to determine the JWT signature algorithm, we assume that the
		// request is not meant for this authenticator. So we don't return err
		return false, nil, "", nil
	}

	// Verify JWT signature
	claims := &jwt.StandardClaims{}
	token, err := jwt.ParseWithClaims(requestorJwt, claims, jwtKeyExtractor(keys))
	if err != nil {
		return true, nil, "", server.RemoteError(server.ErrorInvalidRequest, err.Error())
	}
	if !claims.VerifyIssuedAt(time.Now().Unix(), true) {
		return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "jwt not yet valid")
	}
	if time.Unix(claims.IssuedAt, 0).Add(10 * time.Minute).Before(time.Now()) { // TODO make configurable
		return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "jwt too old")
	}

	// Read JWT contents
	parsedJwt, err := irma.ParseRequestorJwt(claims.Subject, requestorJwt)
	if err != nil {
		return true, nil, "", server.RemoteError(server.ErrorInvalidRequest, err.Error())
	}

	requestor := token.Header["kid"].(string) // presence in Header and type is already checked by jwtKeyExtractor
	return true, parsedJwt.SessionRequest(), requestor, nil
}

func jwtSignatureAlg(j string) (string, error) {
	var (
		alg    string
		header map[string]interface{}
		i      interface{}
		ok     bool
		bts    []byte
		err    error
	)

	segments := strings.Split(j, ".")
	if len(segments) == 0 {
		return "", errors.New("invalid jwt, not enough segments")
	}
	if bts, err = jwt.DecodeSegment(segments[0]); err != nil {
		return "", errors.WrapPrefix(err, "failed to base64-decode jwt header", 0)
	}
	if err := json.Unmarshal(bts, &header); err != nil {
		return "", errors.WrapPrefix(err, "failed to json-deserialize jwt header", 0)
	}
	if i, ok = header["alg"]; !ok {
		return "", errors.New("alg field not found in jwt header")
	}
	if alg, ok = i.(string); !ok {
		return "", errors.New("alg field in jwt was not a string")
	}
	return alg, nil
}
<commit_msg>Simplify JWT signature detection function<commit_after>package irmaserver

import (
	"encoding/base64"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/go-errors/errors"
	"github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/server"
)

// Authenticator instances authenticate incoming session requests. Given details of the HTTP
// post done by the requestor, it is checked whether or not the requestor is known and
// allowed to submit session requests.
type Authenticator interface {
	// Initialize is called once on server startup for each requestor that uses this authentication method.
	// Used to parse keys or populate caches for later use.
	Initialize(name string, requestor Requestor) error

	// Authenticate checks, given the HTTP header and POST body, if the authenticator is known
	// and allowed to submit session requests. It returns whether or not the current authenticator
	// is applicable to this sesion requests; the request itself; the name of the requestor;
	// or an error (which is only non-nil if applies is true; i.e. this authenticator applies but
	// it was not able to successfully authenticate the request).
	Authenticate(
		headers http.Header, body []byte,
	) (applies bool, request irma.SessionRequest, requestor string, err *irma.RemoteError)
}

type AuthenticationMethod string

// Currently supported requestor authentication methods
const (
	AuthenticationMethodHmac      = "hmac"
	AuthenticationMethodPublicKey = "publickey"
	AuthenticationMethodToken     = "token"
	AuthenticationMethodNone      = "none"
)

type HmacAuthenticator struct {
	hmackeys map[string]interface{}
}
type PublicKeyAuthenticator struct {
	publickeys map[string]interface{}
}
type PresharedKeyAuthenticator struct {
	presharedkeys map[string]string
}
type NilAuthenticator struct{}

var authenticators map[AuthenticationMethod]Authenticator

func (NilAuthenticator) Authenticate(
	headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) {
	if headers.Get("Authentication") != "" || !strings.HasPrefix(headers.Get("Content-Type"), "application/json") {
		return false, nil, "", nil
	}
	request, err := server.ParseSessionRequest(body)
	if err != nil {
		return true, nil, "", server.RemoteError(server.ErrorInvalidRequest, err.Error())
	}
	return true, request, "", nil
}

func (NilAuthenticator) Initialize(name string, requestor Requestor) error {
	return nil
}

func (hauth *HmacAuthenticator) Authenticate(
	headers http.Header, body []byte,
) (applies bool, request irma.SessionRequest, requestor string, err *irma.RemoteError) {
	return jwtAuthenticate(headers, body, jwt.SigningMethodHS256.Name, hauth.hmackeys)
}

func (hauth *HmacAuthenticator) Initialize(name string, requestor Requestor) error {
	if requestor.AuthenticationKey == "" {
		return errors.Errorf("Requestor %s had no authentication key")
	}
	if bts, err := base64.StdEncoding.DecodeString(requestor.AuthenticationKey); err != nil {
		return err
	} else {
		hauth.hmackeys[name] = bts
		return nil
	}
}

func (pkauth *PublicKeyAuthenticator) Authenticate(
	headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) {
	return jwtAuthenticate(headers, body, jwt.SigningMethodRS256.Name, pkauth.publickeys)
}

func (pkauth *PublicKeyAuthenticator) Initialize(name string, requestor Requestor) error {
	var bts []byte
	var err error
	if strings.HasPrefix(requestor.AuthenticationKey, "-----BEGIN") {
		bts = []byte(requestor.AuthenticationKey)
	}
	if _, err := os.Stat(requestor.AuthenticationKey); err == nil {
		bts, err = ioutil.ReadFile(requestor.AuthenticationKey)
		if err != nil {
			return err
		}
	}
	if len(bts) == 0 {
		return errors.Errorf("Requestor %s has invalid public key", name)
	}
	pk, err := jwt.ParseRSAPublicKeyFromPEM(bts)
	if err != nil {
		return err
	}
	pkauth.publickeys[name] = pk

	return nil
}

func (pskauth *PresharedKeyAuthenticator) Authenticate(
	headers http.Header, body []byte,
) (bool, irma.SessionRequest, string, *irma.RemoteError) {
	auth := headers.Get("Authentication")
	if auth == "" || !strings.HasPrefix(headers.Get("Content-Type"), "application/json") {
		return false, nil, "", nil
	}
	requestor, ok := pskauth.presharedkeys[auth]
	if !ok {
		return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "")
	}
	request, err := server.ParseSessionRequest(body)
	if err != nil {
		return true, nil, "", server.RemoteError(server.ErrorInvalidRequest, err.Error())
	}
	return true, request, requestor, nil
}

func (pskauth *PresharedKeyAuthenticator) Initialize(name string, requestor Requestor) error {
	if requestor.AuthenticationKey == "" {
		return errors.Errorf("Requestor %s had no authentication key")
	}
	pskauth.presharedkeys[requestor.AuthenticationKey] = name
	return nil
}

// Helper functions

// Given an (unauthenticated) jwt, return the key against which it should be verified using the "kid" header
func jwtKeyExtractor(publickeys map[string]interface{}) func(token *jwt.Token) (interface{}, error) {
	return func(token *jwt.Token) (interface{}, error) {
		var ok bool
		kid, ok := token.Header["kid"]
		if !ok {
			return nil, errors.New("No kid jwt header found")
		}
		requestor, ok := kid.(string)
		if !ok {
			return nil, errors.New("kid jwt header was not a string")
		}
		if pk, ok := publickeys[requestor]; ok {
			return pk, nil
		}
		return nil, errors.Errorf("Unknown requestor: %s", requestor)
	}
}

// jwtAuthenticate is a helper function for JWT-based authenticators that verifies and parses JWTs.
func jwtAuthenticate(
	headers http.Header, body []byte, signatureAlg string, keys map[string]interface{},
) (bool, irma.SessionRequest, string, *irma.RemoteError) {
	// Read JWT and check its type
	if headers.Get("Authorization") != "" || !strings.HasPrefix(headers.Get("Content-Type"), "text/plain") {
		return false, nil, "", nil
	}
	requestorJwt := string(body)

	// We need to establish the signature method with which the JWT was signed. We do this by just
	// inspecting the JWT header here, before the signature is verified (which is done below). I suppose
	// it would be more idiomatic to have the KeyFunc which is fed to jwt.ParseWithClaims() perform this
	// task, but then the KeyFunc would need access to all public keys here instead of the ones belonging
	// to the signature algorithm we are expecting (specified by signatureAlg). Security-wise it makes no
	// difference: either way the alg header is examined before the signature is verified.
	alg, err := jwtSignatureAlg(requestorJwt)
	if err != nil || alg != signatureAlg {
		// If err != nil, ie. we failed to determine the JWT signature algorithm, we assume that the
		// request is not meant for this authenticator. So we don't return err
		return false, nil, "", nil
	}

	// Verify JWT signature. We do not yet store the JWT contents here, because we need to know the session type first
	// before we can construct a struct instance of the appropriate type into which to unmarshal the JWT contents.
	claims := &jwt.StandardClaims{}
	token, err := jwt.ParseWithClaims(requestorJwt, claims, jwtKeyExtractor(keys))
	if err != nil {
		return true, nil, "", server.RemoteError(server.ErrorInvalidRequest, err.Error())
	}
	if !claims.VerifyIssuedAt(time.Now().Unix(), true) {
		return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "jwt not yet valid")
	}
	if time.Unix(claims.IssuedAt, 0).Add(10 * time.Minute).Before(time.Now()) { // TODO make configurable
		return true, nil, "", server.RemoteError(server.ErrorUnauthorized, "jwt too old")
	}

	// Read JWT contents
	parsedJwt, err := irma.ParseRequestorJwt(claims.Subject, requestorJwt)
	if err != nil {
		return true, nil, "", server.RemoteError(server.ErrorInvalidRequest, err.Error())
	}

	requestor := token.Header["kid"].(string) // presence in Header and type is already checked by jwtKeyExtractor
	return true, parsedJwt.SessionRequest(), requestor, nil
}

func jwtSignatureAlg(j string) (string, error) {
	token, _, err := new(jwt.Parser).ParseUnverified(j, &jwt.StandardClaims{})
	if err != nil {
		return "", err
	}
	return token.Method.Alg(), nil
}
<|endoftext|>


<commit_before>package main

import "C"
import (
	"github.com/unigornel/go-tcpip/ethernet"
	"github.com/unigornel/go-tcpip/icmp"
	"github.com/unigornel/go-tcpip/ipv4"
)

func main() {}

//export Main
func Main(unused int) {
	ip := [4]byte{10, 0, 100, 2}

	nic := ethernet.NewNIC()
	ethDemux := ethernet.NewDemux(nic.Receive(), ethernet.DemuxLog)

	arp := ipv4.NewARP(nic.GetMAC(), ip, nic.Send())
	arp.Bind(ethDemux)

	ipl := ipv4.NewLayer([4]byte{10, 0, 100, 2}, arp, nic.Send())
	ipDemux := ipv4.NewDemux(ipl.Receive(), ipv4.DemuxLog)
	ipl.Bind(ethDemux)

	icmpl := icmp.NewLayer(ipl.Send())
	icmpl.Bind(ipDemux)

	m := make(chan int)
	m <- 0
}
<commit_msg>Use new network layer structure<commit_after>package main

import "C"
import (
	"github.com/unigornel/go-tcpip/ethernet"
	"github.com/unigornel/go-tcpip/icmp"
	"github.com/unigornel/go-tcpip/ipv4"
)

func main() {}

//export Main
func Main(unused int) {
	sourceIP := [4]byte{10, 0, 100, 2}

	nic := ethernet.NewNIC()
	eth := ethernet.NewLayer(nic)
	arp := ipv4.NewARP(nic.GetMAC(), sourceIP, eth)
	ip := ipv4.NewLayer(sourceIP, arp, eth)
	icmp.NewLayer(ip)

	nic.Start()

	m := make(chan int)
	m <- 0
}
<|endoftext|>


<commit_before>package markov

import (
	"math/rand"
	"sync"
	"time"

	"github.com/high-moctane/go-mecab_slice"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type DataMap struct {
	Order       int
	FirstPrefix prefix
	Chain       map[string][]mecabs.MorphemeString
	mutex       *sync.Mutex
}

type prefix mecabs.PhraseString

func (p prefix) shift(ms mecabs.MorphemeString) {
	copy(p, p[1:])
	p[len(p)-1] = ms
}

func (p prefix) string() (ans string) {
	for i, ms := range p {
		if i > 0 {
			ans += "\n"
		}
		ans += string(ms)
	}
	return
}

func NewDataMap(order int) DataMap {
	return DataMap{
		Order:       order,
		FirstPrefix: genFirstPrefix(order),
		Chain:       map[string][]mecabs.MorphemeString{},
		mutex:       new(sync.Mutex),
	}
}

func genFirstPrefix(order int) prefix {
	p := make(prefix, order)
	for i, _ := range p {
		p[i] = mecabs.BOMS
	}
	return p
}

func (d *DataMap) firstPrefix() prefix {
	ans := make(prefix, len(d.FirstPrefix))
	copy(ans, d.FirstPrefix)
	return ans
}

func (d *DataMap) Add(ps mecabs.PhraseString) {
	var ms mecabs.MorphemeString
	prefix := d.firstPrefix()
	for _, ms = range ps {
		d.mutex.Lock()
		d.Chain[prefix.string()] = append(d.Chain[prefix.string()], ms)
		d.mutex.Unlock()
		prefix.shift(ms)
	}
	d.mutex.Lock()
	d.Chain[prefix.string()] = append(d.Chain[prefix.string()], mecabs.EOMS)
	d.mutex.Unlock()
}

func (d *DataMap) Generate(length int) mecabs.PhraseString {
	ans := make(mecabs.PhraseString, 0, length)
	prefix := d.firstPrefix()
	for i := 0; i < length; i++ {
		candidate := d.Chain[prefix.string()]
		next := candidate[rand.Intn(len(candidate))]
		if next == mecabs.EOMS {
			return ans
		}
		ans = append(ans, next)
		prefix.shift(next)
	}
	return ans
}
<commit_msg>[Generate()] suffix がみつからなかった場合にやり直す<commit_after>package markov

import (
	"math/rand"
	"sync"
	"time"

	"github.com/high-moctane/go-mecab_slice"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type DataMap struct {
	Order       int
	FirstPrefix prefix
	Chain       map[string][]mecabs.MorphemeString
	mutex       *sync.Mutex
}

type prefix mecabs.PhraseString

func (p prefix) shift(ms mecabs.MorphemeString) {
	copy(p, p[1:])
	p[len(p)-1] = ms
}

func (p prefix) string() (ans string) {
	for i, ms := range p {
		if i > 0 {
			ans += "\n"
		}
		ans += string(ms)
	}
	return
}

func NewDataMap(order int) DataMap {
	return DataMap{
		Order:       order,
		FirstPrefix: genFirstPrefix(order),
		Chain:       map[string][]mecabs.MorphemeString{},
		mutex:       new(sync.Mutex),
	}
}

func genFirstPrefix(order int) prefix {
	p := make(prefix, order)
	for i, _ := range p {
		p[i] = mecabs.BOMS
	}
	return p
}

func (d *DataMap) firstPrefix() prefix {
	ans := make(prefix, len(d.FirstPrefix))
	copy(ans, d.FirstPrefix)
	return ans
}

func (d *DataMap) Add(ps mecabs.PhraseString) {
	var ms mecabs.MorphemeString
	prefix := d.firstPrefix()
	for _, ms = range ps {
		d.mutex.Lock()
		d.Chain[prefix.string()] = append(d.Chain[prefix.string()], ms)
		d.mutex.Unlock()
		prefix.shift(ms)
	}
	d.mutex.Lock()
	d.Chain[prefix.string()] = append(d.Chain[prefix.string()], mecabs.EOMS)
	d.mutex.Unlock()
}

func (d *DataMap) Generate(length int) mecabs.PhraseString {
again:
	ans := make(mecabs.PhraseString, 0, length)
	prefix := d.firstPrefix()
	for i := 0; i < length; i++ {
		candidate, ok := d.Chain[prefix.string()]
		if !ok {
			/*
				NOTE:
				本来ならcandidateが存在しないということはないが、
				go-rapbotでのバグを防ぐために仕方なく導入した
			*/
			goto again
		}
		next := candidate[rand.Intn(len(candidate))]
		if next == mecabs.EOMS {
			return ans
		}
		ans = append(ans, next)
		prefix.shift(next)
	}
	return ans
}
<|endoftext|>


<commit_before>package ipset

import (
	"os/exec"
	"strings"

	"github.com/pkg/errors"
)

type Name string

type Type string

const (
	ListSet = Type("list:set")
	HashIP  = Type("hash:ip")
)

type Interface interface {
	Create(ipsetName Name, ipsetType Type) error
	AddEntry(ipsetName Name, entry string) error
	DelEntry(ipsetName Name, entry string) error
	Flush(ipsetName Name) error
	Destroy(ipsetName Name) error

	List(prefix string) ([]Name, error)

	FlushAll() error
	DestroyAll() error
}

type ipset struct {
	refCount
}

func New() Interface {
	return &ipset{refCount: newRefCount()}
}

func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
	err, _ := doExec("create", string(ipsetName), string(ipsetType))
	return err
}

func (i *ipset) AddEntry(ipsetName Name, entry string) error {
	if i.inc(ipsetName, entry) > 1 { // already in the set
		return nil
	}
	err, _ := doExec("add", string(ipsetName), entry)
	return err
}

func (i *ipset) DelEntry(ipsetName Name, entry string) error {
	if i.dec(ipsetName, entry) > 0 { // still needed
		return nil
	}
	err, _ := doExec("del", string(ipsetName), entry)
	return err
}

func (i *ipset) Flush(ipsetName Name) error {
	i.removeSet(ipsetName)
	err, _ := doExec("flush", string(ipsetName))
	return err
}

func (i *ipset) FlushAll() error {
	i.refCount = newRefCount()
	err, _ := doExec("flush")
	return err
}

func (i *ipset) Destroy(ipsetName Name) error {
	i.removeSet(ipsetName)
	err, _ := doExec("destroy", string(ipsetName))
	return err
}

func (i *ipset) DestroyAll() error {
	i.refCount = newRefCount()
	err, _ := doExec("destroy")
	return err
}

// Fetch a list of all existing sets with a given prefix
func (i *ipset) List(prefix string) ([]Name, error) {
	err, output := doExec("list","-name","-output","plain")
	if err != nil {
		return nil, err
	}

	var selected []Name
	sets := strings.Split(string(output), "\n")
	for _, v := range sets {
		if strings.HasPrefix(v, prefix) {
			selected = append(selected, Name(v))
		}
	}

	return selected, err
}

func doExec(args ...string) (error, []byte) {
	output, err := exec.Command("ipset", args...).CombinedOutput()
	if err != nil {
		return errors.Wrapf(err, "ipset %v failed: %s", args, output), output
	}
	return nil, output
}

// Reference-counting
type key struct {
	ipsetName Name
	entry     string
}

// note no locking is required as all operations are serialised in the controller
type refCount struct {
	ref map[key]int
}

func newRefCount() refCount {
	return refCount{ref: make(map[key]int)}
}

func (rc *refCount) inc(ipsetName Name, entry string) int {
	k := key{ipsetName, entry}
	rc.ref[k]++
	return rc.ref[k]
}

func (rc *refCount) dec(ipsetName Name, entry string) int {
	k := key{ipsetName, entry}
	rc.ref[k]--
	return rc.ref[k]
}

func (rc *refCount) removeSet(ipsetName Name) {
	for k := range rc.ref {
		if k.ipsetName == ipsetName {
			delete(rc.ref, k)
		}
	}
}
<commit_msg>Unwind changes to doExec and handle List() error differently<commit_after>package ipset

import (
	"os/exec"
	"strings"

	"github.com/pkg/errors"
)

type Name string

type Type string

const (
	ListSet = Type("list:set")
	HashIP  = Type("hash:ip")
)

type Interface interface {
	Create(ipsetName Name, ipsetType Type) error
	AddEntry(ipsetName Name, entry string) error
	DelEntry(ipsetName Name, entry string) error
	Flush(ipsetName Name) error
	Destroy(ipsetName Name) error

	List(prefix string) ([]Name, error)

	FlushAll() error
	DestroyAll() error
}

type ipset struct {
	refCount
}

func New() Interface {
	return &ipset{refCount: newRefCount()}
}

func (i *ipset) Create(ipsetName Name, ipsetType Type) error {
	return doExec("create", string(ipsetName), string(ipsetType))
}

func (i *ipset) AddEntry(ipsetName Name, entry string) error {
	if i.inc(ipsetName, entry) > 1 { // already in the set
		return nil
	}
	return doExec("add", string(ipsetName), entry)
}

func (i *ipset) DelEntry(ipsetName Name, entry string) error {
	if i.dec(ipsetName, entry) > 0 { // still needed
		return nil
	}
	return doExec("del", string(ipsetName), entry)
}

func (i *ipset) Flush(ipsetName Name) error {
	i.removeSet(ipsetName)
	return doExec("flush", string(ipsetName))
}

func (i *ipset) FlushAll() error {
	i.refCount = newRefCount()
	return doExec("flush")
}

func (i *ipset) Destroy(ipsetName Name) error {
	i.removeSet(ipsetName)
	return doExec("destroy", string(ipsetName))
}

func (i *ipset) DestroyAll() error {
	i.refCount = newRefCount()
	return doExec("destroy")
}

// Fetch a list of all existing sets with a given prefix
func (i *ipset) List(prefix string) ([]Name, error) {
	output, err := exec.Command("ipset", "list", "-name", "-output", "plain").Output()
	if err != nil {
		return nil, err
	}

	var selected []Name
	sets := strings.Split(string(output), "\n")
	for _, v := range sets {
		if strings.HasPrefix(v, prefix) {
			selected = append(selected, Name(v))
		}
	}

	return selected, err
}

func doExec(args ...string) error {
	if output, err := exec.Command("ipset", args...).CombinedOutput(); err != nil {
		return errors.Wrapf(err, "ipset %v failed: %s", args, output)
	}
	return nil
}

// Reference-counting
type key struct {
	ipsetName Name
	entry     string
}

// note no locking is required as all operations are serialised in the controller
type refCount struct {
	ref map[key]int
}

func newRefCount() refCount {
	return refCount{ref: make(map[key]int)}
}

func (rc *refCount) inc(ipsetName Name, entry string) int {
	k := key{ipsetName, entry}
	rc.ref[k]++
	return rc.ref[k]
}

func (rc *refCount) dec(ipsetName Name, entry string) int {
	k := key{ipsetName, entry}
	rc.ref[k]--
	return rc.ref[k]
}

func (rc *refCount) removeSet(ipsetName Name) {
	for k := range rc.ref {
		if k.ipsetName == ipsetName {
			delete(rc.ref, k)
		}
	}
}
<|endoftext|>


<commit_before>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
	"errors"
"fmt"
)

var errNoSuchGame = errors.New("no such game")
var errGameNotFound = errors.New("game not found")

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func getgame(gamename string) (*Game, error) {
	g, ok := games[gamename]
	if !ok {				// not a valid game
		return nil, errNoSuchGame
	}
//	ret := make(chan string)
//	zipRequests <- zipRequest{
//		Game:	gamename,
//		Return:	ret,
//	}
//	zipname := <-ret
//	close(ret)
//	if zipname == "" {		// none given
	good, err := g.Find()
	if err != nil {
		return nil, err
	}
	if !good {
		return nil, errGameNotFound
	}
	return g, nil
}

func getgame_fuseerr(gamename string) (*Game, fuse.Status) {
	g, err := getgame(gamename)
	if err == errNoSuchGame {
		return nil, fuse.EINVAL
	} else if err == errGameNotFound {
		return nil, fuse.ENOENT
	} else if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return g, fuse.OK
}

func getloopbackfile(filename string) (*fuse.LoopbackFile, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
	return &fuse.LoopbackFile{
		File:	f,
	}, nil
}

func getloopbackfile_fuseerr(filename string) (*fuse.LoopbackFile, fuse.Status) {
	loopfile, err := getloopbackfile(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return loopfile, fuse.OK
}

func getattr(filename string) (*fuse.Attr, fuse.Status) {
	stat, err := os.Stat(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return fuse.ToAttr(stat), fuse.OK
}

func getchdparts(basename string) (gamename string, chdname string) {
fmt.Print(basename, " ")
	basename = basename[:len(basename) - 4]
	gamename, chdname = filepath.Split(basename)	// split out CHD filename
	_, gamename = filepath.Split(gamename)			// and game name
fmt.Println(gamename, " ", chdname)
	return
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.ROMLoc)
		return a, err
	case ".chd":
		gamename, chdname := getchdparts(basename)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.CHDLoc[chdname])
		return a, err
	}
	return nil, fuse.ENOENT		// any other file is invalid
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.ROMLoc)
	case ".chd":				// CHD
		gamename, chdname := getchdparts(basename)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.CHDLoc[chdname])
	case "":					// folder
		// ...
	// TODO root directory?
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// TODO
	return nil, fuse.ENOENT
}<commit_msg>Derp I was using basename... but turns out that was just a lingering bug because MAME was using the basename too argh<commit_after>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
	"errors"
"fmt"
)

var errNoSuchGame = errors.New("no such game")
var errGameNotFound = errors.New("game not found")

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func getgame(gamename string) (*Game, error) {
	g, ok := games[gamename]
	if !ok {				// not a valid game
		return nil, errNoSuchGame
	}
//	ret := make(chan string)
//	zipRequests <- zipRequest{
//		Game:	gamename,
//		Return:	ret,
//	}
//	zipname := <-ret
//	close(ret)
//	if zipname == "" {		// none given
	good, err := g.Find()
	if err != nil {
		return nil, err
	}
	if !good {
		return nil, errGameNotFound
	}
	return g, nil
}

func getgame_fuseerr(gamename string) (*Game, fuse.Status) {
	g, err := getgame(gamename)
	if err == errNoSuchGame {
		return nil, fuse.EINVAL
	} else if err == errGameNotFound {
		return nil, fuse.ENOENT
	} else if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return g, fuse.OK
}

func getloopbackfile(filename string) (*fuse.LoopbackFile, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
	return &fuse.LoopbackFile{
		File:	f,
	}, nil
}

func getloopbackfile_fuseerr(filename string) (*fuse.LoopbackFile, fuse.Status) {
	loopfile, err := getloopbackfile(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return loopfile, fuse.OK
}

func getattr(filename string) (*fuse.Attr, fuse.Status) {
	stat, err := os.Stat(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return fuse.ToAttr(stat), fuse.OK
}

func getchdparts(name string) (gamename string, chdname string) {
fmt.Print(name, " ")
	gamename, chdname = filepath.Split(name)	// split out CHD filename
	_, gamename = filepath.Split(gamename)		// and game name
	chdname = chdname[:len(chdname) - 4]		// strip extension
	return
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.ROMLoc)
		return a, err
	case ".chd":
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.CHDLoc[chdname])
		return a, err
	}
	return nil, fuse.ENOENT		// any other file is invalid
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.ROMLoc)
	case ".chd":				// CHD
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.CHDLoc[chdname])
	case "":					// folder
		// ...
	// TODO root directory?
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// TODO
	return nil, fuse.ENOENT
}<|endoftext|>


<commit_before>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
	"strings"
	"errors"
)

var errNoSuchGame = errors.New("no such game")
var errGameNotFound = errors.New("game not found")

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func getgame(gamename string) (*Game, error) {
	g, ok := games[gamename]
	if !ok {				// not a valid game
		return nil, errNoSuchGame
	}
//	ret := make(chan string)
//	zipRequests <- zipRequest{
//		Game:	gamename,
//		Return:	ret,
//	}
//	zipname := <-ret
//	close(ret)
//	if zipname == "" {		// none given
	good, err := g.Find()
	if err != nil {
		return nil, err
	}
	if !good {
		return nil, errGameNotFound
	}
	return g, nil
}

func getgame_fuseerr(gamename string) (*Game, fuse.Status) {
	g, err := getgame(gamename)
	if err == errNoSuchGame {
		return nil, fuse.EINVAL
	} else if err == errGameNotFound {
		return nil, fuse.ENOENT
	} else if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return g, fuse.OK
}

func getloopbackfile(filename string) (*fuse.LoopbackFile, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
	return &fuse.LoopbackFile{
		File:	f,
	}, nil
}

func getloopbackfile_fuseerr(filename string) (*fuse.LoopbackFile, fuse.Status) {
	loopfile, err := getloopbackfile(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return loopfile, fuse.OK
}

func getattr(filename string) (*fuse.Attr, fuse.Status) {
	stat, err := os.Stat(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return fuse.ToAttr(stat), fuse.OK
}

// to avoid recreating the string each time getchdparts() is called
var sepstr = string(filepath.Separator)

func getchdparts(name string) (gamename string, chdname string) {
	// I know MAME won't hand me pathnames that aren't well-formed but Clean() should make them safe to split like this in general...
	parts := strings.Split(filepath.Clean(name), sepstr)
	if len(parts) < 2 {	// invalid
		return "", ""
	}
	gamename = parts[len(parts) - 2]
	chdname = parts[len(parts) - 1]
	chdname = chdname[:len(chdname) - 4]		// strip .chd
	return
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.ROMLoc)
		return a, err
	case ".chd":
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.CHDLoc[chdname])
		return a, err
	default:
		// is it a folder that stores CHDs?
		if _, ok := games[basename]; ok {		// yes
			return &fuse.Attr{
				Mode:	fuse.S_IFDIR | 0755,
			}, fuse.OK
		}
		// no; fall out
	}
	return nil, fuse.ENOENT		// any other file is invalid
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.ROMLoc)
	case ".chd":				// CHD
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.CHDLoc[chdname])
	case "":					// folder
		// ...
	// TODO root directory?
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// TODO
	return nil, fuse.ENOENT
}<commit_msg>Er oops forgot stuff (2/2)<commit_after>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
	"strings"
	"errors"
)

var errNoSuchGame = errors.New("no such game")
var errGameNotFound = errors.New("game not found")

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func getgame(gamename string) (*Game, error) {
	g, ok := games[gamename]
	if !ok {				// not a valid game
		return nil, errNoSuchGame
	}
	good, err := g.Find()
	if err != nil {
		return nil, err
	}
	if !good {
		return nil, errGameNotFound
	}
	return g, nil
}

func getgame_fuseerr(gamename string) (*Game, fuse.Status) {
	g, err := getgame(gamename)
	if err == errNoSuchGame {
		return nil, fuse.EINVAL
	} else if err == errGameNotFound {
		return nil, fuse.ENOENT
	} else if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return g, fuse.OK
}

func getloopbackfile(filename string) (*fuse.LoopbackFile, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
	return &fuse.LoopbackFile{
		File:	f,
	}, nil
}

func getloopbackfile_fuseerr(filename string) (*fuse.LoopbackFile, fuse.Status) {
	loopfile, err := getloopbackfile(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return loopfile, fuse.OK
}

func getattr(filename string) (*fuse.Attr, fuse.Status) {
	stat, err := os.Stat(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return fuse.ToAttr(stat), fuse.OK
}

// to avoid recreating the string each time getchdparts() is called
var sepstr = string(filepath.Separator)

func getchdparts(name string) (gamename string, chdname string) {
	// I know MAME won't hand me pathnames that aren't well-formed but Clean() should make them safe to split like this in general...
	parts := strings.Split(filepath.Clean(name), sepstr)
	if len(parts) < 2 {	// invalid
		return "", ""
	}
	gamename = parts[len(parts) - 2]
	chdname = parts[len(parts) - 1]
	chdname = chdname[:len(chdname) - 4]		// strip .chd
	return
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.ROMLoc)
		return a, err
	case ".chd":
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.CHDLoc[chdname])
		return a, err
	default:
		// is it a folder that stores CHDs?
		if _, ok := games[basename]; ok {		// yes
			return &fuse.Attr{
				Mode:	fuse.S_IFDIR | 0755,
			}, fuse.OK
		}
		// no; fall out
	}
	return nil, fuse.ENOENT		// any other file is invalid
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.ROMLoc)
	case ".chd":				// CHD
		gamename, chdname := getchdparts(name)
		if gamename == "" {		// we need a game name to disambiguate
			return nil, fuse.ENOENT
		}
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.CHDLoc[chdname])
	case "":					// folder
		// ...
	// TODO root directory?
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// TODO
	return nil, fuse.ENOENT
}<|endoftext|>


<commit_before>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
"fmt"
)

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
fmt.Print("getattr " + name + " ")
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
fmt.Println("game:" + gamename, " ")
		if _, ok := games[gamename]; !ok {		// not a valid game
fmt.Println()
			return nil, fuse.ENOENT
		}
//		ret := make(chan string)
/*		zipRequests <- zipRequest{
			Game:	gamename,
			Return:	ret,
		}
*///		zipname := <-ret
//		close(ret)
//		if zipname == "" {		// none given
		good, err := games[gamename].Find()
		if err != nil || !good {
			// TODO handle error
			return nil, fuse.ENOENT
		}
//		f, err := os.Open(zipname)
		f, err := os.Open(games[gamename].ROMLoc)
		if err != nil {
			// TODO report error
			return nil, fuse.EIO	// TODO proper error
		}
		// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
		loopfile := &fuse.LoopbackFile{
			File:	f,
		}
		var a fuse.Attr
		status := loopfile.GetAttr(&a)
		loopfile.Release()
fmt.Println(a, " ", status)
		return &a, status
	}
fmt.Println()
	return nil, fuse.ENOENT		// any other file is invalid
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
fmt.Println(name)
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
//		ret := make(chan string)
/*		zipRequests <- zipRequest{
			Game:	gamename,
			Return:	ret,
		}
*///		zipname := <-ret
//		close(ret)
//		if zipname == "" {		// none given
		good, err := games[gamename].Find()
		if err != nil || !good {
			// TODO handle error
			return nil, fuse.ENOENT
		}
//		f, err := os.Open(zipname)
		f, err := os.Open(games[gamename].ROMLoc)
		if err != nil {
			// TODO report error
			return nil, fuse.EIO	// TODO proper error
		}
		// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
		loopfile := &fuse.LoopbackFile{
			File:	f,
		}
		return loopfile, fuse.OK
	case ".chd":				// CHD
		// ...
	case "":					// folder
		// ...
	// TODO root directory?
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
fmt.Println("opendir " + name)
	if name == "" {
		return []fuse.DirEntry{
			{ Name: "test" },
		}, fuse.OK
	}
	return nil, fuse.ENOENT
}<commit_msg>Cleaned up fuse.go.<commit_after>// 30 december 2012
package main

import (
	"os"
	"github.com/hanwen/go-fuse/fuse"
	"path/filepath"
	"errors"
)

var errNoSuchGame = errors.New("no such game")
var errGameNotFound = errors.New("game not found")

// note to self: this needs to be an embed in a struct as DefaultFileSystem will implement the  methods I don't override here and have them return fuse.ENOSYS
type mamefuse struct {
	fuse.DefaultFileSystem
}

func getgame(gamename string) (*Game, error) {
	g, ok := games[gamename]
	if !ok {				// not a valid game
		return nil, errNoSuchGame
	}
//	ret := make(chan string)
//	zipRequests <- zipRequest{
//		Game:	gamename,
//		Return:	ret,
//	}
//	zipname := <-ret
//	close(ret)
//	if zipname == "" {		// none given
	good, err := g.Find()
	if err != nil {
		return nil, err
	}
	if !good {
		return nil, errGameNotFound
	}
	return g, nil
}

func getgame_fuseerr(gamename string) (*Game, fuse.Status) {
	g, err := getgame(gamename)
	if err == errNoSuchGame {
		return nil, fuse.EINVAL
	} else if err == errGameNotFound {
		return nil, fuse.ENOENT
	} else if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return g, fuse.OK
}

func getloopbackfile(filename string) (*fuse.LoopbackFile, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	// according to the go-fuse source (fuse/file.go), fuse.LoopbackFile will take ownership of our *os.FIle, calling Close() on it itself
	return &fuse.LoopbackFile{
		File:	f,
	}, nil
}

func getloopbackfile_fuseerr(filename string) (*fuse.LoopbackFile, fuse.Status) {
	loopfile, err := getloopbackfile(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return loopfile, fuse.OK
}

func getattr(filename string) (*fuse.Attr, fuse.Status) {
	stat, err := os.Stat(filename)
	if err != nil {
		// TODO report error
		return nil, fuse.EIO
	}
	return fuse.ToAttr(stat), fuse.OK
}

func (fs *mamefuse) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
//		return getattr(g.ROMLoc)
		// TODO merely returning getattr() always results in
		// 2012/12/31 12:13:27 writer: Write/Writev failed, err: 22=invalid argument. opcode: LOOKUP
		// but this works
		a, err := getattr(g.ROMLoc)
		return a, err
	}
	return nil, fuse.ENOENT		// any other file is invalid
}

func (fs *mamefuse) Open(name string, flags uint32, context *fuse.Context) (file fuse.File, code fuse.Status) {
	basename := filepath.Base(name)
	switch filepath.Ext(basename) {
	case ".zip":				// ROM set
		gamename := basename[:len(basename) - 4]
		g, err := getgame_fuseerr(gamename)
		if err != fuse.OK {
			return nil, err
		}
		// TODO worry about closing the file?
		return getloopbackfile_fuseerr(g.ROMLoc)
	case ".chd":				// CHD
		// ...
	case "":					// folder
		// ...
	// TODO root directory?
	}
	return nil, fuse.ENOENT		// otherwise 404
}

func (fs *mamefuse) OpenDir(name string, context *fuse.Context) (c []fuse.DirEntry, code fuse.Status) {
	// TODO
	return nil, fuse.ENOENT
}<|endoftext|>


<commit_before>// Package acquire manages the flow and status of an acquire game. It acts
// like a finite state machine (FSM), in which received inputs modify
// machine state
package acquire

import (
	"errors"
	"github.com/svera/acquire/interfaces"
)

const (
	// ActionNotAllowed is an error returned when action not allowed at current state
	ActionNotAllowed = "action_not_allowed"
	// StockSharesNotBuyable is an error returned when stock shares from a corporation not on board are not buyable
	StockSharesNotBuyable = "stock_shares_not_buyable"
	// NotEnoughStockShares is an error returned when not enough stock shares of a corporation to buy
	NotEnoughStockShares = "not_enough_stock_shares"
	// TileTemporaryUnplayable is an error returned when tile temporarily unplayable
	TileTemporaryUnplayable = "tile_temporary_unplayable"
	// TilePermanentlyUnplayable is an error returned when tile permanently unplayable
	TilePermanentlyUnplayable = "tile_permanently_unplayable"
	// NotEnoughCash is an error returned when player has not enough cash to buy stock shares
	NotEnoughCash = "not_enough_cash"
	// TooManyStockSharesToBuy is an error returned when player can not buy more than 3 stock shares per turn
	TooManyStockSharesToBuy = "too_many_stock_shares_to_buy"
	// CorpNamesNotUnique is an error returned when some corporation names are repeated
	CorpNamesNotUnique = "corp_names_not_unique"
	// WrongNumberCorpsClass is an error returned when corporations classes do not fit rules
	WrongNumberCorpsClass = "wrong_number_corps_class"
	// CorporationAlreadyOnBoard is an error returned when corporation is already on board and cannot be founded
	CorporationAlreadyOnBoard = "corporation_already_on_board"
	// WrongNumberPlayers is an error returned when there must be between 3 and 6 players
	WrongNumberPlayers = "wrong_number_players"
	// NoCorporationSharesOwned is an error returned when player does not own stock shares of a certain corporation
	NoCorporationSharesOwned = "no_corporation_shares_owned"
	// NotEnoughCorporationSharesOwned is an error returned when player does not own enough stock shares of a certain corporation
	NotEnoughCorporationSharesOwned = "not_enough_corporation_shares_owned"
	// TileNotOnHand is an error returned when player does not have tile on hand
	TileNotOnHand = "tile_not_on_hand"
	// NotAnAcquirerCorporation is an error returned when corporation is not the acquirer in a merge
	NotAnAcquirerCorporation = "not_an_acquirer_corporation"
	// TradeAmountNotEven is an error returned when number of stock shares is not even in a trade
	TradeAmountNotEven = "trade_amount_not_even"

	totalCorporations      = 7
	endGameCorporationSize = 41
)

type sortablePlayers struct {
	players []interfaces.Player
	corp    interfaces.Corporation
}

func (s sortablePlayers) Len() int { return len(s.players) }
func (s sortablePlayers) Less(i, j int) bool {
	return s.players[i].Shares(s.corp) < s.players[j].Shares(s.corp)
}
func (s sortablePlayers) Swap(i, j int) { s.players[i], s.players[j] = s.players[j], s.players[i] }

// Game stores state of game elements and provides methods to control game flow
type Game struct {
	board               interfaces.Board
	state               interfaces.State
	players             []interfaces.Player
	corporations        [7]interfaces.Corporation
	tileset             interfaces.Tileset
	currentPlayerNumber int
	newCorpTiles        []interfaces.Tile
	mergeCorps          map[string][]interfaces.Corporation
	sellTradePlayers    []int
	lastPlayedTile      interfaces.Tile
	turn                int
	lastTurn            bool
	// When in sell_trade state, the current player is stored here temporary as the turn
	// is passed to all defunct corporations stockholders
	frozenPlayer int
}

// New initialises a new Acquire game
func New(
	board interfaces.Board,
	players []interfaces.Player,
	corporations [7]interfaces.Corporation,
	tileset interfaces.Tileset,
	state interfaces.State,
) (*Game, error) {
	if len(players) < 3 || len(players) > 6 {
		return nil, errors.New(WrongNumberPlayers)
	}
	if !areNamesUnique(corporations) {
		return nil, errors.New(CorpNamesNotUnique)
	}
	if !isNumberOfCorpsPerClassRight(corporations) {
		return nil, errors.New(WrongNumberCorpsClass)
	}
	gm := Game{
		board:               board,
		players:             players,
		corporations:        corporations,
		tileset:             tileset,
		currentPlayerNumber: 0,
		turn:                1,
		state:               state,
		lastTurn:            false,
	}
	for _, pl := range gm.players {
		gm.giveInitialTileset(pl)
	}

	return &gm, nil
}

// Check that the passed corporations have unique names
func areNamesUnique(corporations [7]interfaces.Corporation) bool {
	for i, corp1 := range corporations {
		if i < len(corporations)-1 {
			for _, corp2 := range corporations[i+1:] {
				if corp1.Name() == corp2.Name() {
					return false
				}
			}
		}
	}
	return true
}

// Check that the number of corporations per class is right
func isNumberOfCorpsPerClassRight(corporations [7]interfaces.Corporation) bool {
	corpsPerClass := [3]int{0, 0, 0}
	for _, corp := range corporations {
		corpsPerClass[corp.Class()]++
	}
	if corpsPerClass[0] != 2 || corpsPerClass[1] != 3 || corpsPerClass[2] != 2 {
		return false
	}
	return true
}

// Initialises player hand of tiles
func (g *Game) giveInitialTileset(plyr interfaces.Player) {
	for i := 0; i < 6; i++ {
		tile, _ := g.tileset.Draw()
		plyr.PickTile(tile)
	}
}

// AreEndConditionsReached checks if game end conditions are reached
func (g *Game) AreEndConditionsReached() bool {
	active := g.activeCorporations()
	if len(active) == 0 {
		return false
	}
	for _, corp := range active {
		if corp.Size() >= endGameCorporationSize {
			return true
		}
		if !corp.IsSafe() {
			return false
		}
	}
	return true
}

// ActiveCorporations returns all corporations on the board
func (g *Game) activeCorporations() []interfaces.Corporation {
	return g.findCorporationsByActiveState(true)
}

func (g *Game) findCorporationsByActiveState(value bool) []interfaces.Corporation {
	result := []interfaces.Corporation{}
	for _, corp := range g.corporations {
		if corp.IsActive() == value {
			result = append(result, corp)
		}
	}
	return result
}

// IsCorporationDefunct return true if the passed corporation is in a merge process
// and will dissapear from the board after that merge is complete, false otherwise
func (g *Game) IsCorporationDefunct(corp interfaces.Corporation) bool {
	for _, defunct := range g.mergeCorps["defunct"] {
		if corp == defunct {
			return true
		}
	}
	return false
}

// IsTilePlayable returns false if the passed tile is either
// temporary or permanently unplayable, true otherwise
func (g *Game) IsTilePlayable(tl interfaces.Tile) bool {
	if g.isTileTemporaryUnplayable(tl) || g.isTilePermanentlyUnplayable(tl) {
		return false
	}
	return true
}

// Returns true if a tile is permanently unplayable, that is,
// that putting it on the board would merge two or more safe corporations
func (g *Game) isTilePermanentlyUnplayable(tl interfaces.Tile) bool {
	adjacents := g.board.AdjacentCells(tl.Number(), tl.Letter())
	safeNeighbours := 0
	for _, adjacent := range adjacents {
		if adjacent.Type() == "corporation" && adjacent.(interfaces.Corporation).IsSafe() {
			safeNeighbours++
		}
		if safeNeighbours == 2 {
			return true
		}
	}
	return false
}

// Returns true if a tile is temporarily unplayable, that is,
// that putting it on the board would create an 8th corporation
func (g *Game) isTileTemporaryUnplayable(tl interfaces.Tile) bool {
	if len(g.activeCorporations()) < totalCorporations {
		return false
	}
	adjacents := g.board.AdjacentCells(tl.Number(), tl.Letter())
	for _, adjacent := range adjacents {
		if adjacent.Type() == "unincorporated" {
			return true
		}
	}
	return false
}

// Player returns player with passed number
func (g *Game) Player(playerNumber int) interfaces.Player {
	return g.players[playerNumber]
}

// CurrentPlayer returns player currently in play
func (g *Game) CurrentPlayer() interfaces.Player {
	return g.players[g.currentPlayerNumber]
}

// CurrentPlayerNumber returns the number of the player currently in play
func (g *Game) CurrentPlayerNumber() int {
	return g.currentPlayerNumber
}

// PlayTile puts the given tile on board and triggers related actions
func (g *Game) PlayTile(tl interfaces.Tile) error {
	if err := g.checkTile(tl); err != nil {
		return err
	}

	g.CurrentPlayer().DiscardTile(tl)
	g.lastPlayedTile = tl

	if merge, mergeCorps := g.board.TileMergeCorporations(tl); merge {
		g.startMerge(tl, mergeCorps)
	} else if corpFounded, tiles := g.board.TileFoundCorporation(tl); corpFounded {
		g.board.PutTile(tl)
		g.state = g.state.ToFoundCorp()
		g.newCorpTiles = tiles
	} else if grow, tiles, corp := g.board.TileGrowCorporation(tl); grow {
		g.growCorporation(corp, tiles)
		g.state = g.state.ToBuyStock()
	} else {
		if err := g.putUnincorporatedTile(tl); err != nil {
			return err
		}
	}
	return nil
}

func (g *Game) checkTile(tl interfaces.Tile) error {
	if g.state.Name() != interfaces.PlayTileStateName {
		return errors.New(ActionNotAllowed)
	}
	if g.isTileTemporaryUnplayable(tl) {
		return errors.New(TileTemporaryUnplayable)
	}
	if !g.CurrentPlayer().HasTile(tl) {
		return errors.New(TileNotOnHand)
	}
	return nil
}

func (g *Game) putUnincorporatedTile(tl interfaces.Tile) error {
	g.board.PutTile(tl)
	if g.existActiveCorporations() {
		g.state = g.state.ToBuyStock()
	} else {
		if err := g.drawTile(); err != nil {
			return err
		}
		g.nextPlayer()
	}
	return nil
}

func (g *Game) existActiveCorporations() bool {
	for _, corp := range g.corporations {
		if corp.IsActive() {
			return true
		}
	}
	return false
}

// Sets player currently in play
func (g *Game) setCurrentPlayer(number int) *Game {
	g.currentPlayerNumber = number
	return g
}

// FoundCorporation founds a new corporation
func (g *Game) FoundCorporation(corp interfaces.Corporation) error {
	if g.state.Name() != interfaces.FoundCorpStateName {
		return errors.New(ActionNotAllowed)
	}
	if corp.IsActive() {
		return errors.New(CorporationAlreadyOnBoard)
	}
	g.board.SetOwner(corp, g.newCorpTiles)
	corp.Grow(len(g.newCorpTiles))
	g.newCorpTiles = []interfaces.Tile{}
	g.getFounderStockShare(g.CurrentPlayer(), corp)
	g.state = g.state.ToBuyStock()
	return nil
}

// Receive a free stock share from a rencently found corporation, if it has
// remaining shares available
// TODO this should trigger an event warning that no founder stock share will be given
// of the founded corporation has no stock shares left
func (g *Game) getFounderStockShare(pl interfaces.Player, corp interfaces.Corporation) {
	if corp.Stock() > 0 {
		corp.RemoveStock(1)
		pl.AddShares(corp, 1)
	}
}

// Makes a corporation grow with the passed tiles
func (g *Game) growCorporation(corp interfaces.Corporation, tiles []interfaces.Tile) {
	g.board.SetOwner(corp, tiles)
	corp.Grow(len(tiles))
}

// Increases the number which specifies the current player
func (g *Game) nextPlayer() {
	g.currentPlayerNumber++
	if g.currentPlayerNumber == len(g.players) {
		g.currentPlayerNumber = 0
		g.turn++
	}
}

// Turn returns the current turn number
func (g *Game) Turn() int {
	return g.turn
}

// ClaimEndGame allows the current player to claim end game
// This can be done at any time. After announcing that the game is over,
// the player may finish the turn.
func (g *Game) ClaimEndGame() *Game {
	if g.AreEndConditionsReached() {
		g.lastTurn = true
	}
	return g
}

// LastTurn returns if the current turn will be the last one or not
func (g *Game) LastTurn() bool {
	return g.lastTurn
}

// Classification returns the players list ordered by cash,
// which is the metric used to know game's final classification
/*
func (g *Game) Classification() []interfaces.Player {
	var classification []interfaces.Player

	cashDesc := func(pl1, pl2 interfaces.Player) bool {
		return pl1.Cash() > pl2.Cash()
	}

	for _, pl := range g.players {
		classification = append(classification, pl)
	}
	player.By(cashDesc).Sort(classification)
	return classification
}
*/

// Board returns game's board instance
func (g *Game) Board() interfaces.Board {
	return g.board
}

// GameStateName returns game's current state
func (g *Game) GameStateName() string {
	return g.state.Name()
}
<commit_msg>Changed method and property names<commit_after>// Package acquire manages the flow and status of an acquire game. It acts
// like a finite state machine (FSM), in which received inputs modify
// machine state
package acquire

import (
	"errors"
	"github.com/svera/acquire/interfaces"
)

const (
	// ActionNotAllowed is an error returned when action not allowed at current state
	ActionNotAllowed = "action_not_allowed"
	// StockSharesNotBuyable is an error returned when stock shares from a corporation not on board are not buyable
	StockSharesNotBuyable = "stock_shares_not_buyable"
	// NotEnoughStockShares is an error returned when not enough stock shares of a corporation to buy
	NotEnoughStockShares = "not_enough_stock_shares"
	// TileTemporaryUnplayable is an error returned when tile temporarily unplayable
	TileTemporaryUnplayable = "tile_temporary_unplayable"
	// TilePermanentlyUnplayable is an error returned when tile permanently unplayable
	TilePermanentlyUnplayable = "tile_permanently_unplayable"
	// NotEnoughCash is an error returned when player has not enough cash to buy stock shares
	NotEnoughCash = "not_enough_cash"
	// TooManyStockSharesToBuy is an error returned when player can not buy more than 3 stock shares per turn
	TooManyStockSharesToBuy = "too_many_stock_shares_to_buy"
	// CorpNamesNotUnique is an error returned when some corporation names are repeated
	CorpNamesNotUnique = "corp_names_not_unique"
	// WrongNumberCorpsClass is an error returned when corporations classes do not fit rules
	WrongNumberCorpsClass = "wrong_number_corps_class"
	// CorporationAlreadyOnBoard is an error returned when corporation is already on board and cannot be founded
	CorporationAlreadyOnBoard = "corporation_already_on_board"
	// WrongNumberPlayers is an error returned when there must be between 3 and 6 players
	WrongNumberPlayers = "wrong_number_players"
	// NoCorporationSharesOwned is an error returned when player does not own stock shares of a certain corporation
	NoCorporationSharesOwned = "no_corporation_shares_owned"
	// NotEnoughCorporationSharesOwned is an error returned when player does not own enough stock shares of a certain corporation
	NotEnoughCorporationSharesOwned = "not_enough_corporation_shares_owned"
	// TileNotOnHand is an error returned when player does not have tile on hand
	TileNotOnHand = "tile_not_on_hand"
	// NotAnAcquirerCorporation is an error returned when corporation is not the acquirer in a merge
	NotAnAcquirerCorporation = "not_an_acquirer_corporation"
	// TradeAmountNotEven is an error returned when number of stock shares is not even in a trade
	TradeAmountNotEven = "trade_amount_not_even"

	totalCorporations      = 7
	endGameCorporationSize = 41
)

type sortablePlayers struct {
	players []interfaces.Player
	corp    interfaces.Corporation
}

func (s sortablePlayers) Len() int { return len(s.players) }
func (s sortablePlayers) Less(i, j int) bool {
	return s.players[i].Shares(s.corp) < s.players[j].Shares(s.corp)
}
func (s sortablePlayers) Swap(i, j int) { s.players[i], s.players[j] = s.players[j], s.players[i] }

// Game stores state of game elements and provides methods to control game flow
type Game struct {
	board               interfaces.Board
	state               interfaces.State
	players             []interfaces.Player
	corporations        [7]interfaces.Corporation
	tileset             interfaces.Tileset
	currentPlayerNumber int
	newCorpTiles        []interfaces.Tile
	mergeCorps          map[string][]interfaces.Corporation
	sellTradePlayers    []int
	lastPlayedTile      interfaces.Tile
	turn                int
	isLastTurn          bool
	// When in sell_trade state, the current player is stored here temporary as the turn
	// is passed to all defunct corporations stockholders
	frozenPlayer int
}

// New initialises a new Acquire game
func New(
	board interfaces.Board,
	players []interfaces.Player,
	corporations [7]interfaces.Corporation,
	tileset interfaces.Tileset,
	state interfaces.State,
) (*Game, error) {
	if len(players) < 3 || len(players) > 6 {
		return nil, errors.New(WrongNumberPlayers)
	}
	if !areNamesUnique(corporations) {
		return nil, errors.New(CorpNamesNotUnique)
	}
	if !isNumberOfCorpsPerClassRight(corporations) {
		return nil, errors.New(WrongNumberCorpsClass)
	}
	gm := Game{
		board:               board,
		players:             players,
		corporations:        corporations,
		tileset:             tileset,
		currentPlayerNumber: 0,
		turn:                1,
		state:               state,
		lastTurn:            false,
	}
	for _, pl := range gm.players {
		gm.giveInitialTileset(pl)
	}

	return &gm, nil
}

// Check that the passed corporations have unique names
func areNamesUnique(corporations [7]interfaces.Corporation) bool {
	for i, corp1 := range corporations {
		if i < len(corporations)-1 {
			for _, corp2 := range corporations[i+1:] {
				if corp1.Name() == corp2.Name() {
					return false
				}
			}
		}
	}
	return true
}

// Check that the number of corporations per class is right
func isNumberOfCorpsPerClassRight(corporations [7]interfaces.Corporation) bool {
	corpsPerClass := [3]int{0, 0, 0}
	for _, corp := range corporations {
		corpsPerClass[corp.Class()]++
	}
	if corpsPerClass[0] != 2 || corpsPerClass[1] != 3 || corpsPerClass[2] != 2 {
		return false
	}
	return true
}

// Initialises player hand of tiles
func (g *Game) giveInitialTileset(plyr interfaces.Player) {
	for i := 0; i < 6; i++ {
		tile, _ := g.tileset.Draw()
		plyr.PickTile(tile)
	}
}

// AreEndConditionsReached checks if game end conditions are reached
func (g *Game) AreEndConditionsReached() bool {
	active := g.activeCorporations()
	if len(active) == 0 {
		return false
	}
	for _, corp := range active {
		if corp.Size() >= endGameCorporationSize {
			return true
		}
		if !corp.IsSafe() {
			return false
		}
	}
	return true
}

// ActiveCorporations returns all corporations on the board
func (g *Game) activeCorporations() []interfaces.Corporation {
	return g.findCorporationsByActiveState(true)
}

func (g *Game) findCorporationsByActiveState(value bool) []interfaces.Corporation {
	result := []interfaces.Corporation{}
	for _, corp := range g.corporations {
		if corp.IsActive() == value {
			result = append(result, corp)
		}
	}
	return result
}

// IsCorporationDefunct return true if the passed corporation is in a merge process
// and will dissapear from the board after that merge is complete, false otherwise
func (g *Game) IsCorporationDefunct(corp interfaces.Corporation) bool {
	for _, defunct := range g.mergeCorps["defunct"] {
		if corp == defunct {
			return true
		}
	}
	return false
}

// IsTilePlayable returns false if the passed tile is either
// temporary or permanently unplayable, true otherwise
func (g *Game) IsTilePlayable(tl interfaces.Tile) bool {
	if g.isTileTemporaryUnplayable(tl) || g.isTilePermanentlyUnplayable(tl) {
		return false
	}
	return true
}

// Returns true if a tile is permanently unplayable, that is,
// that putting it on the board would merge two or more safe corporations
func (g *Game) isTilePermanentlyUnplayable(tl interfaces.Tile) bool {
	adjacents := g.board.AdjacentCells(tl.Number(), tl.Letter())
	safeNeighbours := 0
	for _, adjacent := range adjacents {
		if adjacent.Type() == "corporation" && adjacent.(interfaces.Corporation).IsSafe() {
			safeNeighbours++
		}
		if safeNeighbours == 2 {
			return true
		}
	}
	return false
}

// Returns true if a tile is temporarily unplayable, that is,
// that putting it on the board would create an 8th corporation
func (g *Game) isTileTemporaryUnplayable(tl interfaces.Tile) bool {
	if len(g.activeCorporations()) < totalCorporations {
		return false
	}
	adjacents := g.board.AdjacentCells(tl.Number(), tl.Letter())
	for _, adjacent := range adjacents {
		if adjacent.Type() == "unincorporated" {
			return true
		}
	}
	return false
}

// Player returns player with passed number
func (g *Game) Player(playerNumber int) interfaces.Player {
	return g.players[playerNumber]
}

// CurrentPlayer returns player currently in play
func (g *Game) CurrentPlayer() interfaces.Player {
	return g.players[g.currentPlayerNumber]
}

// CurrentPlayerNumber returns the number of the player currently in play
func (g *Game) CurrentPlayerNumber() int {
	return g.currentPlayerNumber
}

// PlayTile puts the given tile on board and triggers related actions
func (g *Game) PlayTile(tl interfaces.Tile) error {
	if err := g.checkTile(tl); err != nil {
		return err
	}

	g.CurrentPlayer().DiscardTile(tl)
	g.lastPlayedTile = tl

	if merge, mergeCorps := g.board.TileMergeCorporations(tl); merge {
		g.startMerge(tl, mergeCorps)
	} else if corpFounded, tiles := g.board.TileFoundCorporation(tl); corpFounded {
		g.board.PutTile(tl)
		g.state = g.state.ToFoundCorp()
		g.newCorpTiles = tiles
	} else if grow, tiles, corp := g.board.TileGrowCorporation(tl); grow {
		g.growCorporation(corp, tiles)
		g.state = g.state.ToBuyStock()
	} else {
		if err := g.putUnincorporatedTile(tl); err != nil {
			return err
		}
	}
	return nil
}

func (g *Game) checkTile(tl interfaces.Tile) error {
	if g.state.Name() != interfaces.PlayTileStateName {
		return errors.New(ActionNotAllowed)
	}
	if g.isTileTemporaryUnplayable(tl) {
		return errors.New(TileTemporaryUnplayable)
	}
	if !g.CurrentPlayer().HasTile(tl) {
		return errors.New(TileNotOnHand)
	}
	return nil
}

func (g *Game) putUnincorporatedTile(tl interfaces.Tile) error {
	g.board.PutTile(tl)
	if g.existActiveCorporations() {
		g.state = g.state.ToBuyStock()
	} else {
		if err := g.drawTile(); err != nil {
			return err
		}
		g.nextPlayer()
	}
	return nil
}

func (g *Game) existActiveCorporations() bool {
	for _, corp := range g.corporations {
		if corp.IsActive() {
			return true
		}
	}
	return false
}

// Sets player currently in play
func (g *Game) setCurrentPlayer(number int) *Game {
	g.currentPlayerNumber = number
	return g
}

// FoundCorporation founds a new corporation
func (g *Game) FoundCorporation(corp interfaces.Corporation) error {
	if g.state.Name() != interfaces.FoundCorpStateName {
		return errors.New(ActionNotAllowed)
	}
	if corp.IsActive() {
		return errors.New(CorporationAlreadyOnBoard)
	}
	g.board.SetOwner(corp, g.newCorpTiles)
	corp.Grow(len(g.newCorpTiles))
	g.newCorpTiles = []interfaces.Tile{}
	g.getFounderStockShare(g.CurrentPlayer(), corp)
	g.state = g.state.ToBuyStock()
	return nil
}

// Receive a free stock share from a rencently found corporation, if it has
// remaining shares available
// TODO this should trigger an event warning that no founder stock share will be given
// of the founded corporation has no stock shares left
func (g *Game) getFounderStockShare(pl interfaces.Player, corp interfaces.Corporation) {
	if corp.Stock() > 0 {
		corp.RemoveStock(1)
		pl.AddShares(corp, 1)
	}
}

// Makes a corporation grow with the passed tiles
func (g *Game) growCorporation(corp interfaces.Corporation, tiles []interfaces.Tile) {
	g.board.SetOwner(corp, tiles)
	corp.Grow(len(tiles))
}

// Increases the number which specifies the current player
func (g *Game) nextPlayer() {
	g.currentPlayerNumber++
	if g.currentPlayerNumber == len(g.players) {
		g.currentPlayerNumber = 0
		g.turn++
	}
}

// Turn returns the current turn number
func (g *Game) Turn() int {
	return g.turn
}

// ClaimEndGame allows the current player to claim end game
// This can be done at any time. After announcing that the game is over,
// the player may finish the turn.
func (g *Game) ClaimEndGame() *Game {
	if g.AreEndConditionsReached() {
		g.lastTurn = true
	}
	return g
}

// LastTurn returns if the current turn will be the last one or not
func (g *Game) IsLastTurn() bool {
	return g.isLastTurn
}

// Classification returns the players list ordered by cash,
// which is the metric used to know game's final classification
/*
func (g *Game) Classification() []interfaces.Player {
	var classification []interfaces.Player

	cashDesc := func(pl1, pl2 interfaces.Player) bool {
		return pl1.Cash() > pl2.Cash()
	}

	for _, pl := range g.players {
		classification = append(classification, pl)
	}
	player.By(cashDesc).Sort(classification)
	return classification
}
*/

// Board returns game's board instance
func (g *Game) Board() interfaces.Board {
	return g.board
}

// GameStateName returns game's current state
func (g *Game) GameStateName() string {
	return g.state.Name()
}
<|endoftext|>