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

// The oauth package provides support for making
// OAuth2-authenticated HTTP requests.
package oauth

// TODO(adg): Documentation.

// BUG(adg): doesn't support refreshing Credentials when expired.

import (
	"http"
	"json"
	"log"
	"os"
	"time"
)

type Config struct {
	ClientId     string
	ClientSecret string
	Scope        string
	AuthURL      string
	TokenURL     string
	RedirectURL  string // Defaults to out-of-band mode if empty.

	// Transport is the HTTP transport to use.
	// It will default to http.DefaultTransport if nil.
	Transport http.RoundTripper
}

type Credentials struct {
	AccessToken  string "access_token"
	RefreshToken string "refresh_token"
	TokenExpiry  int64  "expires_in"
}

type Transport struct {
	*Config
	*Credentials
}

// AuthURL returns a URL that the end-user should be redirected to,
// so that they may obtain an code (that will be provided to Exchange).
func AuthURL(c *Config) string {
	url, err := http.ParseURL(c.AuthURL)
	if err != nil {
		panic("AuthURL malformed: " + err.String())
	}
	q := http.EncodeQuery(map[string][]string{
		"response_type": {"code"},
		"client_id":     {c.ClientId},
		"redirect_uri":  {c.redirectURL()},
		"scope":         {c.Scope},
	})
	if url.RawQuery == "" {
		url.RawQuery = q
	} else {
		url.RawQuery += "&" + q
	}
	return url.String()
}

// Exchange takes a code and gets access Credentials from the remote server.
// If successful, the Credentials will be stored in the Transport so that
// it may be used immediately to make authenticated requests as an
// http.RoundTripper.
func Exchange(c *Config, code string) (*Credentials, os.Error) {
	form := map[string]string{
		"grant_type":    "authorization_code",
		"client_id":     c.ClientId,
		"client_secret": c.ClientSecret,
		"redirect_uri":  c.redirectURL(),
		"scop":          c.Scope,
		"code":          code,
	}
	resp, err := (&http.Client{c.transport()}).PostForm(c.TokenURL, form)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, os.NewError("invalid response: " + resp.Status)
	}
	cred := new(Credentials)
	defer resp.Body.Close()
	if err = json.NewDecoder(resp.Body).Decode(cred); err != nil {
		return nil, err
	}
	if cred.TokenExpiry != 0 {
		cred.TokenExpiry = time.Seconds() + cred.TokenExpiry
	}
	return cred, nil
}

// RoundTrip executes a single HTTP transaction using the Transport's
// Credentials as authorization headers.
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err os.Error) {
	if t.Credentials == nil {
		return nil, os.NewError("no Credentials supplied")
	}

	// Set OAuth header
	req.Header.Set("Authorization", "OAuth "+t.AccessToken)

	// Make the HTTP request
	if resp, err = t.transport().RoundTrip(req); err != nil {
		return
	}

	if resp.StatusCode == 401 {
		// TODO(adg): Refresh credentials if we get a 401
		log.Println("Token refresh required")
	}

	return
}

func (c *Config) transport() http.RoundTripper {
	if c.Transport != nil {
		return c.Transport
	}
	return http.DefaultTransport
}

func (c *Config) redirectURL() string {
	if c.RedirectURL != "" {
		return c.RedirectURL
	}
	return "oob"
}
<commit_msg>implemented refresh, added docs, some reorg<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The oauth package provides support for making
// OAuth2-authenticated HTTP requests.
package oauth

// TODO(adg): A means of automatically saving credentials when updated.

import (
	"http"
	"json"
	"os"
	"time"
)

// Config is the configuration of an OAuth consumer.
type Config struct {
	ClientId     string
	ClientSecret string
	Scope        string
	AuthURL      string
	TokenURL     string
	RedirectURL  string // Defaults to out-of-band mode if empty.

	// Transport is the HTTP transport to use when making requests.
	// It will default to http.DefaultTransport if nil.
	// (It should never be an oauth.Transport.)
	Transport http.RoundTripper
}

// Credentials contain an end-user's tokens.
// This is the data you must store to persist authentication.
type Credentials struct {
	AccessToken  string "access_token"
	RefreshToken string "refresh_token"
	TokenExpiry  int64  "expires_in"
}

// Transport implements http.RoundTripper. When configured with a valid
// Config and Credentials it can be used to make authenticated HTTP requests.
//
//	t := &oauth.Transport{config, credentials}
//	c := &http.Client{t}
//	r, _, err := c.Get("http://example.org/url/requiring/auth")
//	// etc
//
// It will automatically refresh the Credentials if it can,
// updating the supplied Credentials in place.
type Transport struct {
	*Config
	*Credentials
}

// AuthURL returns a URL that the end-user should be redirected to,
// so that they may obtain an authorization code.
func AuthURL(c *Config) string {
	url, err := http.ParseURL(c.AuthURL)
	if err != nil {
		panic("AuthURL malformed: " + err.String())
	}
	q := http.EncodeQuery(map[string][]string{
		"response_type": {"code"},
		"client_id":     {c.ClientId},
		"redirect_uri":  {c.redirectURL()},
		"scope":         {c.Scope},
	})
	if url.RawQuery == "" {
		url.RawQuery = q
	} else {
		url.RawQuery += "&" + q
	}
	return url.String()
}

// Exchange takes a code and gets access Credentials from the remote server.
func Exchange(c *Config, code string) (*Credentials, os.Error) {
	form := map[string]string{
		"grant_type":    "authorization_code",
		"client_id":     c.ClientId,
		"client_secret": c.ClientSecret,
		"redirect_uri":  c.redirectURL(),
		"scope":         c.Scope,
		"code":          code,
	}
	resp, err := (&http.Client{c.transport()}).PostForm(c.TokenURL, form)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != 200 {
		return nil, os.NewError("invalid response: " + resp.Status)
	}
	cred := new(Credentials)
	defer resp.Body.Close()
	if err = json.NewDecoder(resp.Body).Decode(cred); err != nil {
		return nil, err
	}
	if cred.TokenExpiry != 0 {
		cred.TokenExpiry = time.Seconds() + cred.TokenExpiry
	}
	return cred, nil
}

// RoundTrip executes a single HTTP transaction using the Transport's
// Credentials as authorization headers.
func (t *Transport) RoundTrip(req *http.Request) (resp *http.Response, err os.Error) {
	if t.Config == nil {
		return nil, os.NewError("no Config supplied")
	}
	if t.Credentials == nil {
		return nil, os.NewError("no Credentials supplied")
	}

	// Make the HTTP request
	req.Header.Set("Authorization", "OAuth "+t.AccessToken)
	if resp, err = t.transport().RoundTrip(req); err != nil {
		return
	}

	// Refresh credentials if they're stale
	if resp.StatusCode == 401 {
		if err = t.refresh(); err != nil {
			return
		}
		resp, err = t.transport().RoundTrip(req)
	}

	return
}

func (t *Transport) refresh() os.Error {
	form := map[string]string{
		"grant_type":    "refresh_token",
		"client_id":     t.ClientId,
		"client_secret": t.ClientSecret,
		"refresh_token": t.RefreshToken,
	}
	resp, err := (&http.Client{t.transport()}).PostForm(t.TokenURL, form)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		return os.NewError("invalid response: " + resp.Status)
	}
	defer resp.Body.Close()
	if err = json.NewDecoder(resp.Body).Decode(t.Credentials); err != nil {
		return err
	}
	if t.TokenExpiry != 0 {
		t.TokenExpiry = time.Seconds() + t.TokenExpiry
	}
	return nil

}

func (c *Config) transport() http.RoundTripper {
	if c.Transport != nil {
		return c.Transport
	}
	return http.DefaultTransport
}

func (c *Config) redirectURL() string {
	if c.RedirectURL != "" {
		return c.RedirectURL
	}
	return "oob"
}
<|endoftext|>


<commit_before>package oauth

import (
	"fmt"
	"net/http"
	"os"
	"reflect"
	"strings"

	"github.com/go-chi/chi"
	"github.com/gocontrib/auth"
	"github.com/gocontrib/request"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	log "github.com/sirupsen/logrus"
)

type providerFactory = func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider

var providerNames []string

// WithProviders registers OAuth providers
// Example:
// import "github.com/markbates/goth/providers/vk"
// oauth.WithProviders("vk", vk.New)
func WithProviders(config *auth.Config, providers ...interface{}) {
	providerNames = []string{}
	var gothProviders []goth.Provider
	for i := 0; i+1 < len(providers); i += 2 {
		name := providers[i].(string)
		factory := makeProviderFactory(providers[i+1])
		provider := makeProvider(config, name, factory)
		if provider != nil {
			providerNames = append(providerNames, name)
			gothProviders = append(gothProviders, provider)
		}
	}
	if len(gothProviders) > 0 {
		goth.UseProviders(gothProviders...)
	} else {
		log.Warning("no oauth providers")
	}
}

func makeProviderFactory(v interface{}) providerFactory {
	return func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
		f := reflect.ValueOf(v)
		// f := reflect.FuncOf(v)
		args := []reflect.Value{
			reflect.ValueOf(clientKey),
			reflect.ValueOf(secret),
			reflect.ValueOf(callbackURL),
		}
		for _, scope := range scopes {
			args = append(args, reflect.ValueOf(scope))
		}
		result := f.Call(args)
		return result[0].Interface().(goth.Provider)
	}
}

func makeProvider(config *auth.Config, provider string, factory providerFactory) goth.Provider {
	p := strings.ToUpper(provider)
	key := os.Getenv(p + "_KEY")
	secret := os.Getenv(p + "_SECRET")
	if len(key) == 0 || len(secret) == 0 {
		log.Warningf("%s has no keys\n", provider)
		return nil
	}

	baseURL := strings.TrimRight(getBaseURL(config), "/")
	callbackURL := baseURL + "/api/oauth/callback/" + provider
	log.Debugf("%s callback: %s\n", provider, callbackURL)

	// TODO support scopes via env
	return factory(key, secret, callbackURL)
}

func getBaseURL(config *auth.Config) string {
	if len(config.ServerURL) > 0 {
		return config.ServerURL
	}
	if config.ServerPort != 0 {
		return fmt.Sprintf("http://localhost:%d", config.ServerPort)
	}
	hostname, err := os.Hostname()
	if err == nil {
		return fmt.Sprintf("http://%s", hostname)
	}
	return "http://localhost:4200"
}

// Router interface to allow use of any router
type Router interface {
	Get(pattern string, h http.HandlerFunc)
}

// RegisterAPI registers OAuth HTTP handlers
func RegisterAPI(r Router, config *auth.Config) {
	config = config.SetDefaults()

	userStore := config.UserStoreEx
	if userStore == nil {
		return
	}

	defaultGetProviderName := gothic.GetProviderName
	gothic.GetProviderName = func(r *http.Request) (string, error) {
		providerName := chi.URLParam(r, "provider")
		if len(providerName) > 0 {
			return providerName, nil
		}
		return defaultGetProviderName(r)
	}

	r.Get("/oauth/success", func(w http.ResponseWriter, r *http.Request) {
		// TODO print nice html page
		fmt.Fprintf(w, "Hey, buddy!")
	})

	r.Get("/oauth/error", func(w http.ResponseWriter, r *http.Request) {
		// TODO print nice html error page
		fmt.Fprintf(w, "Oops, your OAuth failed! Please try again later")
	})

	r.Get("/api/oauth/providers", func(w http.ResponseWriter, r *http.Request) {
		auth.SendJSON(w, providerNames)
	})

	r.Get("/api/oauth/login/{provider}", func(w http.ResponseWriter, r *http.Request) {
		// try to get the user without re-authenticating
		if user, err := gothic.CompleteUserAuth(w, r); err == nil {
			completeOAuthFlow(w, r, config, user)
		} else {
			gothic.BeginAuthHandler(w, r)
		}
	})

	r.Get("/api/oauth/logout/{provider}", func(w http.ResponseWriter, r *http.Request) {
		gothic.Logout(w, r)
		w.Header().Set("Location", "/")
		w.WriteHeader(http.StatusTemporaryRedirect)
	})

	r.Get("/api/oauth/callback/{provider}", func(w http.ResponseWriter, r *http.Request) {
		user, err := gothic.CompleteUserAuth(w, r)
		if err != nil {
			oauthError(w, r, err)
			return
		}
		completeOAuthFlow(w, r, config, user)
	})
}

func completeOAuthFlow(w http.ResponseWriter, r *http.Request, config *auth.Config, account goth.User) {
	ctx := r.Context()
	userStore := config.UserStoreEx
	if userStore == nil {
		return
	}

	user, err := userStore.FindUserByEmail(ctx, account.Email)
	if err != nil {
		// create user and link with external account
		data := auth.UserData{
			RawData:           account.RawData,
			Provider:          account.Provider,
			Email:             account.Email,
			Name:              account.Name,
			FirstName:         account.FirstName,
			LastName:          account.LastName,
			NickName:          account.NickName,
			Description:       account.Description,
			UserID:            account.UserID,
			AvatarURL:         account.AvatarURL,
			Location:          account.Location,
			AccessToken:       account.AccessToken,
			AccessTokenSecret: account.AccessTokenSecret,
			RefreshToken:      account.RefreshToken,
			ExpiresAt:         account.ExpiresAt,
		}
		user, err = userStore.CreateUser(ctx, data)
		if err != nil {
			oauthError(w, r, err)
			return
		}
	}

	token := auth.MakeToken(r, config, user)
	tokenString, err3 := token.Encode(config)
	if err3 != nil {
		oauthError(w, r, err3)
		return
	}

	request.SetCookie(w, r, config.TokenCookie, tokenString)

	// TODO support return_url, absolute url if needed
	http.Redirect(w, r, "/oauth/success?token="+tokenString, http.StatusFound)
}

func oauthError(w http.ResponseWriter, r *http.Request, err error) {
	http.Redirect(w, r, "/oauth/error?message="+err.Error(), http.StatusInternalServerError)
}
<commit_msg>support custom hostname<commit_after>package oauth

import (
	"fmt"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"strings"

	"github.com/go-chi/chi"
	"github.com/gocontrib/auth"
	"github.com/gocontrib/request"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	log "github.com/sirupsen/logrus"
)

type providerFactory = func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider

var providerNames []string

// WithProviders registers OAuth providers
// Example:
// import "github.com/markbates/goth/providers/vk"
// oauth.WithProviders("vk", vk.New)
func WithProviders(config *auth.Config, providers ...interface{}) {
	providerNames = []string{}
	var gothProviders []goth.Provider
	for i := 0; i+1 < len(providers); i += 2 {
		name := providers[i].(string)
		factory := makeProviderFactory(providers[i+1])
		provider := makeProvider(config, name, factory)
		if provider != nil {
			providerNames = append(providerNames, name)
			gothProviders = append(gothProviders, provider)
		}
	}
	if len(gothProviders) > 0 {
		goth.UseProviders(gothProviders...)
	} else {
		log.Warning("no oauth providers")
	}
}

func makeProviderFactory(v interface{}) providerFactory {
	return func(clientKey, secret, callbackURL string, scopes ...string) goth.Provider {
		f := reflect.ValueOf(v)
		// f := reflect.FuncOf(v)
		args := []reflect.Value{
			reflect.ValueOf(clientKey),
			reflect.ValueOf(secret),
			reflect.ValueOf(callbackURL),
		}
		for _, scope := range scopes {
			args = append(args, reflect.ValueOf(scope))
		}
		result := f.Call(args)
		return result[0].Interface().(goth.Provider)
	}
}

func makeProvider(config *auth.Config, provider string, factory providerFactory) goth.Provider {
	p := strings.ToUpper(provider)
	key := os.Getenv(p + "_KEY")
	secret := os.Getenv(p + "_SECRET")
	if len(key) == 0 || len(secret) == 0 {
		log.Warningf("%s has no keys\n", provider)
		return nil
	}

	baseURL := strings.TrimRight(getBaseURL(config), "/")
	callbackURL := baseURL + "/api/oauth/callback/" + provider
	log.Debugf("%s callback: %s\n", provider, callbackURL)

	// TODO support scopes via env
	return factory(key, secret, callbackURL)
}

func getBaseURL(config *auth.Config) string {
	if len(config.ServerURL) > 0 {
		return config.ServerURL
	}
	if config.ServerPort != 0 {
		return fmt.Sprintf("http://localhost:%d", config.ServerPort)
	}
	hostname := Hostname()
	if port, err := strconv.ParseInt(os.Getenv("HTTP_PORT"), 10, 64); err == nil {
		if port == 80 {
			return fmt.Sprintf("http://%s", hostname)
		}
		return fmt.Sprintf("http://%s:%d", hostname, port)
	}
	return fmt.Sprintf("http://%s", hostname)
}

// Hostname reads HOSTNAME env var or os.Hostname used for your app
func Hostname() string {
	hostname := os.Getenv("HOSTNAME")
	if len(hostname) > 0 {
		return hostname
	}
	hostname, err := os.Hostname()
	if err == nil {
		return hostname
	}
	return "localhost"
}

// Router interface to allow use of any router
type Router interface {
	Get(pattern string, h http.HandlerFunc)
}

// RegisterAPI registers OAuth HTTP handlers
func RegisterAPI(r Router, config *auth.Config) {
	config = config.SetDefaults()

	userStore := config.UserStoreEx
	if userStore == nil {
		return
	}

	defaultGetProviderName := gothic.GetProviderName
	gothic.GetProviderName = func(r *http.Request) (string, error) {
		providerName := chi.URLParam(r, "provider")
		if len(providerName) > 0 {
			return providerName, nil
		}
		return defaultGetProviderName(r)
	}

	r.Get("/oauth/success", func(w http.ResponseWriter, r *http.Request) {
		// TODO print nice html page
		fmt.Fprintf(w, "Hey, buddy!")
	})

	r.Get("/oauth/error", func(w http.ResponseWriter, r *http.Request) {
		// TODO print nice html error page
		fmt.Fprintf(w, "Oops, your OAuth failed! Please try again later")
	})

	r.Get("/api/oauth/providers", func(w http.ResponseWriter, r *http.Request) {
		auth.SendJSON(w, providerNames)
	})

	r.Get("/api/oauth/login/{provider}", func(w http.ResponseWriter, r *http.Request) {
		// try to get the user without re-authenticating
		if user, err := gothic.CompleteUserAuth(w, r); err == nil {
			completeOAuthFlow(w, r, config, user)
		} else {
			gothic.BeginAuthHandler(w, r)
		}
	})

	r.Get("/api/oauth/logout/{provider}", func(w http.ResponseWriter, r *http.Request) {
		gothic.Logout(w, r)
		w.Header().Set("Location", "/")
		w.WriteHeader(http.StatusTemporaryRedirect)
	})

	r.Get("/api/oauth/callback/{provider}", func(w http.ResponseWriter, r *http.Request) {
		user, err := gothic.CompleteUserAuth(w, r)
		if err != nil {
			oauthError(w, r, err)
			return
		}
		completeOAuthFlow(w, r, config, user)
	})
}

func completeOAuthFlow(w http.ResponseWriter, r *http.Request, config *auth.Config, account goth.User) {
	ctx := r.Context()
	userStore := config.UserStoreEx
	if userStore == nil {
		return
	}

	user, err := userStore.FindUserByEmail(ctx, account.Email)
	if err != nil {
		// create user and link with external account
		data := auth.UserData{
			RawData:           account.RawData,
			Provider:          account.Provider,
			Email:             account.Email,
			Name:              account.Name,
			FirstName:         account.FirstName,
			LastName:          account.LastName,
			NickName:          account.NickName,
			Description:       account.Description,
			UserID:            account.UserID,
			AvatarURL:         account.AvatarURL,
			Location:          account.Location,
			AccessToken:       account.AccessToken,
			AccessTokenSecret: account.AccessTokenSecret,
			RefreshToken:      account.RefreshToken,
			ExpiresAt:         account.ExpiresAt,
		}
		user, err = userStore.CreateUser(ctx, data)
		if err != nil {
			oauthError(w, r, err)
			return
		}
	}

	token := auth.MakeToken(r, config, user)
	tokenString, err3 := token.Encode(config)
	if err3 != nil {
		oauthError(w, r, err3)
		return
	}

	request.SetCookie(w, r, config.TokenCookie, tokenString)

	// TODO support return_url, absolute url if needed
	http.Redirect(w, r, "/oauth/success?token="+tokenString, http.StatusFound)
}

func oauthError(w http.ResponseWriter, r *http.Request, err error) {
	http.Redirect(w, r, "/oauth/error?message="+err.Error(), http.StatusInternalServerError)
}
<|endoftext|>


<commit_before>package oauth

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/go-chi/chi"
	"github.com/gocontrib/auth"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/facebook"
)

// TODO support more providers
func init() {
	providers := filterNilProviders([]goth.Provider{
		makeProvider("facebook"),
	})
	goth.UseProviders(providers...)
}

func filterNilProviders(in []goth.Provider) []goth.Provider {
	var out []goth.Provider
	for _, v := range in {
		if v != nil {
			out = append(out, v)
		}
	}
	return out
}

func makeProvider(provider string) goth.Provider {
	// TODO get host from config
	host := "http://localhost:4201"
	callback := host + "/api/oauth/callback/" + provider
	p := strings.ToUpper(provider)
	key := os.Getenv(p + "_KEY")
	secret := os.Getenv(p + "_SECRET")
	if len(key) == 0 || len(secret) == 0 {
		return nil
	}

	switch provider {
	case "facebook":
		return facebook.New(key, secret, callback)
	}
	panic("invalid provider")
}

func OAuthAPI(mux chi.Router, config *auth.Config) {
	userStore := config.UserStoreEx
	if userStore == nil {
		return
	}

	mux.Get("/api/oauth/login/:provider", func(w http.ResponseWriter, r *http.Request) {
		// try to get the user without re-authenticating
		if user, err := gothic.CompleteUserAuth(w, r); err == nil {
			completeOAuthFlow(w, r, config, user)
		} else {
			gothic.BeginAuthHandler(w, r)
		}
	})

	mux.Get("/api/oauth/logout/:provider", func(w http.ResponseWriter, r *http.Request) {
		gothic.Logout(w, r)
		w.Header().Set("Location", "/")
		w.WriteHeader(http.StatusTemporaryRedirect)
	})

	mux.Get("/api/oauth/callback/:provider", func(w http.ResponseWriter, r *http.Request) {
		user, err := gothic.CompleteUserAuth(w, r)
		if err != nil {
			fmt.Fprintln(w, err)
			return
		}
		completeOAuthFlow(w, r, config, user)
	})
}

func completeOAuthFlow(w http.ResponseWriter, r *http.Request, config *auth.Config, account goth.User) {
	ctx := r.Context()
	userStore := config.UserStoreEx
	if userStore == nil {
		return
	}

	user, err := userStore.FindUserByEmail(ctx, account.Email)
	if err != nil {
		user, err = userStore.CreateUser(ctx, account)
		if err != nil {
			fmt.Fprintln(w, err)
			return
		}
	}

	// TODO link external account to the user

	auth.WriteLoginResponse(w, r, config, user)
}
<commit_msg>support any router<commit_after>package oauth

import (
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/gocontrib/auth"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/facebook"
)

// TODO support more providers
func init() {
	providers := filterNilProviders([]goth.Provider{
		makeProvider("facebook"),
	})
	goth.UseProviders(providers...)
}

func filterNilProviders(in []goth.Provider) []goth.Provider {
	var out []goth.Provider
	for _, v := range in {
		if v != nil {
			out = append(out, v)
		}
	}
	return out
}

func makeProvider(provider string) goth.Provider {
	// TODO get host from config
	host := "http://localhost:4201"
	callback := host + "/api/oauth/callback/" + provider
	p := strings.ToUpper(provider)
	key := os.Getenv(p + "_KEY")
	secret := os.Getenv(p + "_SECRET")
	if len(key) == 0 || len(secret) == 0 {
		return nil
	}

	switch provider {
	case "facebook":
		return facebook.New(key, secret, callback)
	}
	panic("invalid provider")
}

type Router interface {
	Get(pattern string, h http.HandlerFunc)
}

func RegisterAPI(mux Router, config *auth.Config) {
	userStore := config.UserStoreEx
	if userStore == nil {
		return
	}

	mux.Get("/api/oauth/login/:provider", func(w http.ResponseWriter, r *http.Request) {
		// try to get the user without re-authenticating
		if user, err := gothic.CompleteUserAuth(w, r); err == nil {
			completeOAuthFlow(w, r, config, user)
		} else {
			gothic.BeginAuthHandler(w, r)
		}
	})

	mux.Get("/api/oauth/logout/:provider", func(w http.ResponseWriter, r *http.Request) {
		gothic.Logout(w, r)
		w.Header().Set("Location", "/")
		w.WriteHeader(http.StatusTemporaryRedirect)
	})

	mux.Get("/api/oauth/callback/:provider", func(w http.ResponseWriter, r *http.Request) {
		user, err := gothic.CompleteUserAuth(w, r)
		if err != nil {
			fmt.Fprintln(w, err)
			return
		}
		completeOAuthFlow(w, r, config, user)
	})
}

func completeOAuthFlow(w http.ResponseWriter, r *http.Request, config *auth.Config, account goth.User) {
	ctx := r.Context()
	userStore := config.UserStoreEx
	if userStore == nil {
		return
	}

	user, err := userStore.FindUserByEmail(ctx, account.Email)
	if err != nil {
		user, err = userStore.CreateUser(ctx, account)
		if err != nil {
			fmt.Fprintln(w, err)
			return
		}
	}

	// TODO link external account to the user

	auth.WriteLoginResponse(w, r, config, user)
}
<|endoftext|>


<commit_before>package hpcloud

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"time"
)

/*
 ObjectStoreUpload allows you to upload a file onto the HPCloud, it will
 hash the file and check the returned hash to ensure end-to-end integrity.

 It also takes an optional header which will have it's contents added
 to the request.
*/
func (a Access) ObjectStoreUpload(filename, container, as string, header *http.Header) error {
	f, err := OpenAndHashFile(filename)
	if err != nil {
		return err
	}
	client := &http.Client{}
	path := fmt.Sprintf("%s%s/%s/%s", OBJECT_STORE, a.TenantID, container, as)
	req, err := http.NewRequest("PUT", path, f)
	if err != nil {
		return err
	}
	req.Header.Add("Etag", f.Hash())
	req.Header.Add("X-Auth-Token", a.AuthToken())
	if err != nil {
		return err
	}
	if header != nil {
		for key, value := range *header {
			for _, s := range value {
				req.Header.Add(key, s)
			}
		}
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.Header.Get("Etag") != f.Hash() {
		return errors.New("MD5 hashes do not match. Integrity not guaranteed.")
	}
	if resp.StatusCode != http.StatusCreated {
		return errors.New(fmt.Sprintf("Non-201 status code: %d", resp.StatusCode))
	}
	return nil
}

func (a Access) ObjectStoreDelete(filename string) error {
	client := &http.Client{}
	path := fmt.Sprintf("%s%s/%s", OBJECT_STORE, a.TenantID, filename)
	req, err := http.NewRequest("DELETE", path, nil)
	if err != nil {
		return err
	}
	req.Header.Add("X-Auth-Token", a.AuthToken())
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New(fmt.Sprintf("Non-204 status code: %d", resp.StatusCode))
	}
	return nil
}

func (a Access) ListObjects(directory string) (*FileList, error) {
	path := fmt.Sprintf("%s%s/%s", OBJECT_STORE, a.TenantID, directory)
	client := &http.Client{}
	req, err := http.NewRequest("GET", path, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Auth-Token", a.AuthToken())
	req.Header.Add("Accept", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	fl := &FileList{}
	err = json.Unmarshal(b, fl)
	if err != nil {
		return nil, err
	}
	// TODO: Put in the date parsing here.
	return fl, nil
}

/*
 TemporaryURL will generate the temporary URL for the supplied filename.
*/
func (a Access) TemporaryURL(filename, expires string) string {
	hmac_path := fmt.Sprintf("/v1.0/%s/%s", a.TenantID, filename)
	hmac_body := fmt.Sprintf("%s\n%s\n%s", "GET", expires, hmac_path)
	return fmt.Sprintf("%s%s/%s?temp_url_sig=%s&temp_url_expires=%s",
		OBJECT_STORE, a.TenantID, filename, a.HMAC(a.SecretKey, a.TenantID, hmac_body),
		expires,
	)
}

type File struct {
	Hash            string `json:"hash"`
	StrLastModified string `json:"last_modified"`
	LastModified    *time.Time
	Bytes           int64  `json:"bytes"`
	Name            string `json:"name"`
	ContentType     string `json:"content_type"`
}

type FileList []File
<commit_msg>Add the Content-type when uploading a file.<commit_after>package hpcloud

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"mime"
	"net/http"
	"path/filepath"
	"time"
)

/*
 ObjectStoreUpload allows you to upload a file onto the HPCloud, it will
 hash the file and check the returned hash to ensure end-to-end integrity.

 It also takes an optional header which will have it's contents added
 to the request.
*/
func (a Access) ObjectStoreUpload(filename, container, as string, header *http.Header) error {
	f, err := OpenAndHashFile(filename)
	if err != nil {
		return err
	}
	client := &http.Client{}
	path := fmt.Sprintf("%s%s/%s/%s", OBJECT_STORE, a.TenantID, container, as)
	req, err := http.NewRequest("PUT", path, f)
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", mime.TypeByExtension(filepath.Ext(filename)))
	req.Header.Add("Etag", f.Hash())
	req.Header.Add("X-Auth-Token", a.AuthToken())
	if err != nil {
		return err
	}
	if header != nil {
		for key, value := range *header {
			for _, s := range value {
				req.Header.Add(key, s)
			}
		}
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.Header.Get("Etag") != f.Hash() {
		return errors.New("MD5 hashes do not match. Integrity not guaranteed.")
	}
	if resp.StatusCode != http.StatusCreated {
		return errors.New(fmt.Sprintf("Non-201 status code: %d", resp.StatusCode))
	}
	return nil
}

func (a Access) ObjectStoreDelete(filename string) error {
	client := &http.Client{}
	path := fmt.Sprintf("%s%s/%s", OBJECT_STORE, a.TenantID, filename)
	req, err := http.NewRequest("DELETE", path, nil)
	if err != nil {
		return err
	}
	req.Header.Add("X-Auth-Token", a.AuthToken())
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return errors.New(fmt.Sprintf("Non-204 status code: %d", resp.StatusCode))
	}
	return nil
}

func (a Access) ListObjects(directory string) (*FileList, error) {
	path := fmt.Sprintf("%s%s/%s", OBJECT_STORE, a.TenantID, directory)
	client := &http.Client{}
	req, err := http.NewRequest("GET", path, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Add("X-Auth-Token", a.AuthToken())
	req.Header.Add("Accept", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	fl := &FileList{}
	err = json.Unmarshal(b, fl)
	if err != nil {
		return nil, err
	}
	// TODO: Put in the date parsing here.
	return fl, nil
}

/*
 TemporaryURL will generate the temporary URL for the supplied filename.
*/
func (a Access) TemporaryURL(filename, expires string) string {
	hmac_path := fmt.Sprintf("/v1.0/%s/%s", a.TenantID, filename)
	hmac_body := fmt.Sprintf("%s\n%s\n%s", "GET", expires, hmac_path)
	return fmt.Sprintf("%s%s/%s?temp_url_sig=%s&temp_url_expires=%s",
		OBJECT_STORE, a.TenantID, filename, a.HMAC(a.SecretKey, a.TenantID, hmac_body),
		expires,
	)
}

type File struct {
	Hash            string `json:"hash"`
	StrLastModified string `json:"last_modified"`
	LastModified    *time.Time
	Bytes           int64  `json:"bytes"`
	Name            string `json:"name"`
	ContentType     string `json:"content_type"`
}

type FileList []File
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"code.google.com/p/go.tools/astutil"
)

// importToGroup is a list of functions which map from an import path to
// a group number.
var importToGroup = []func(importPath string) (num int, ok bool){
	func(importPath string) (num int, ok bool) {
		if strings.HasPrefix(importPath, "appengine") {
			return 2, true
		}
		return
	},
	func(importPath string) (num int, ok bool) {
		if strings.Contains(importPath, ".") {
			return 1, true
		}
		return
	},
}

func importGroup(importPath string) int {
	for _, fn := range importToGroup {
		if n, ok := fn(importPath); ok {
			return n
		}
	}
	return 0
}

func fixImports(f *ast.File) (added []string, err error) {
	// refs are a set of possible package references currently unsatisified by imports.
	// first key: either base package (e.g. "fmt") or renamed package
	// second key: referenced package symbol (e.g. "Println")
	refs := make(map[string]map[string]bool)

	// decls are the current package imports. key is base package or renamed package.
	decls := make(map[string]*ast.ImportSpec)

	// collect potential uses of packages.
	var visitor visitFn
	visitor = visitFn(func(node ast.Node) ast.Visitor {
		if node == nil {
			return visitor
		}
		switch v := node.(type) {
		case *ast.ImportSpec:
			if v.Name != nil {
				decls[v.Name.Name] = v
			} else {
				local := importPathToName(strings.Trim(v.Path.Value, `\"`))
				decls[local] = v
			}
		case *ast.SelectorExpr:
			xident, ok := v.X.(*ast.Ident)
			if !ok {
				break
			}
			if xident.Obj != nil {
				// if the parser can resolve it, it's not a package ref
				break
			}
			pkgName := xident.Name
			if refs[pkgName] == nil {
				refs[pkgName] = make(map[string]bool)
			}
			if decls[pkgName] == nil {
				refs[pkgName][v.Sel.Name] = true
			}
		}
		return visitor
	})
	ast.Walk(visitor, f)

	// Search for imports matching potential package references.
	searches := 0
	type result struct {
		ipath string
		err   error
	}
	results := make(chan result)
	for pkgName, symbols := range refs {
		if len(symbols) == 0 {
			continue // skip over packages already imported
		}
		go func(pkgName string, symbols map[string]bool) {
			ipath, err := findImport(pkgName, symbols)
			results <- result{ipath, err}
		}(pkgName, symbols)
		searches++
	}
	for i := 0; i < searches; i++ {
		result := <-results
		if result.err != nil {
			return nil, result.err
		}
		if result.ipath != "" {
			astutil.AddImport(f, result.ipath)
			added = append(added, result.ipath)
		}
	}

	// Nil out any unused ImportSpecs, to be removed in following passes
	unusedImport := map[string]bool{}
	for pkg, is := range decls {
		if refs[pkg] == nil && pkg != "_" && pkg != "." {
			unusedImport[strings.Trim(is.Path.Value, `"`)] = true
		}
	}
	for ipath := range unusedImport {
		if ipath == "C" {
			// Don't remove cgo stuff.
			continue
		}
		astutil.DeleteImport(f, ipath)
	}

	return added, nil
}

// importPathToName returns the package name for the given import path.
var importPathToName = importPathToNameGoPath

// importPathToNameBasic assumes the package name is the base of import path.
func importPathToNameBasic(importPath string) (packageName string) {
	return path.Base(importPath)
}

// importPathToNameGoPath finds out the actual package name, as declared in its .go files.
// If there's a problem, it falls back to using importPathToNameBasic.
func importPathToNameGoPath(importPath string) (packageName string) {
	if buildPkg, err := build.Import(importPath, "", 0); err == nil {
		return buildPkg.Name
	} else {
		return importPathToNameBasic(importPath)
	}
}

type pkg struct {
	importpath string // full pkg import path, e.g. "net/http"
	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
}

var pkgIndexOnce sync.Once

var pkgIndex struct {
	sync.Mutex
	m map[string][]pkg // shortname => []pkg, e.g "http" => "net/http"
}

func loadPkgIndex() {
	pkgIndex.Lock()
	pkgIndex.m = make(map[string][]pkg)
	pkgIndex.Unlock()

	var wg sync.WaitGroup
	for _, path := range build.Default.SrcDirs() {
		f, err := os.Open(path)
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		children, err := f.Readdir(-1)
		f.Close()
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		for _, child := range children {
			if child.IsDir() {
				wg.Add(1)
				go func(path, name string) {
					defer wg.Done()
					loadPkg(&wg, path, name)
				}(path, child.Name())
			}
		}
	}
	wg.Wait()
}

var fset = token.NewFileSet()

func loadPkg(wg *sync.WaitGroup, root, pkgrelpath string) {
	importpath := filepath.ToSlash(pkgrelpath)
	shortName := importPathToName(importpath)

	dir := filepath.Join(root, importpath)
	pkgIndex.Lock()
	pkgIndex.m[shortName] = append(pkgIndex.m[shortName], pkg{
		importpath: importpath,
		dir:        dir,
	})
	pkgIndex.Unlock()

	pkgDir, err := os.Open(dir)
	if err != nil {
		return
	}
	children, err := pkgDir.Readdir(-1)
	pkgDir.Close()
	if err != nil {
		return
	}
	for _, child := range children {
		name := child.Name()
		if name == "" {
			continue
		}
		if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
			continue
		}
		if child.IsDir() {
			wg.Add(1)
			go func(root, name string) {
				defer wg.Done()
				loadPkg(wg, root, name)
			}(root, filepath.Join(importpath, name))
		}
	}
}

// loadExports returns a list exports for a package.
var loadExports = loadExportsGoPath

func loadExportsGoPath(dir string) map[string]bool {
	exports := make(map[string]bool)
	buildPkg, err := build.ImportDir(dir, 0)
	if err != nil {
		if strings.Contains(err.Error(), "no buildable Go source files in") {
			return nil
		}
		fmt.Fprintf(os.Stderr, "could not import %q: %v", dir, err)
		return nil
	}
	for _, file := range buildPkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not parse %q: %v", file, err)
			continue
		}
		for name := range f.Scope.Objects {
			if ast.IsExported(name) {
				exports[name] = true
			}
		}
	}
	return exports
}

// findImport searches for a package with the given symbols.
// If no package is found, findImport returns "".
// Declared as a variable rather than a function so goimports can be easily
// extended by adding a file with an init function.
var findImport = findImportGoPath

func findImportGoPath(pkgName string, symbols map[string]bool) (string, error) {

	pkgIndexOnce.Do(loadPkgIndex)

	// Collect exports for packages with matching names.
	var wg sync.WaitGroup
	var pkgsMu sync.Mutex // guards pkgs
	// full importpath => exported symbol => True
	// e.g. "net/http" => "Client" => True
	pkgs := make(map[string]map[string]bool)
	pkgIndex.Lock()
	for _, pkg := range pkgIndex.m[pkgName] {
		wg.Add(1)
		go func(importpath, dir string) {
			defer wg.Done()
			exports := loadExports(dir)
			if exports != nil {
				pkgsMu.Lock()
				pkgs[importpath] = exports
				pkgsMu.Unlock()
			}
		}(pkg.importpath, pkg.dir)
	}
	pkgIndex.Unlock()
	wg.Wait()

	// Filter out packages missing required exported symbols.
	for symbol := range symbols {
		for importpath, exports := range pkgs {
			if !exports[symbol] {
				delete(pkgs, importpath)
			}
		}
	}
	if len(pkgs) == 0 {
		return "", nil
	}

	// If there are multiple candidate packages, the shortest one wins.
	// This is a heuristic to prefer the standard library (e.g. "bytes")
	// over e.g. "github.com/foo/bar/bytes".
	shortest := ""
	for importPath := range pkgs {
		if shortest == "" || len(importPath) < len(shortest) {
			shortest = importPath
		}
	}
	return shortest, nil
}

type visitFn func(node ast.Node) ast.Visitor

func (fn visitFn) Visit(node ast.Node) ast.Visitor {
	return fn(node)
}
<commit_msg>Pass fileset to astutil. API change from astutil.<commit_after>package main

import (
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"

	"code.google.com/p/go.tools/astutil"
)

// importToGroup is a list of functions which map from an import path to
// a group number.
var importToGroup = []func(importPath string) (num int, ok bool){
	func(importPath string) (num int, ok bool) {
		if strings.HasPrefix(importPath, "appengine") {
			return 2, true
		}
		return
	},
	func(importPath string) (num int, ok bool) {
		if strings.Contains(importPath, ".") {
			return 1, true
		}
		return
	},
}

func importGroup(importPath string) int {
	for _, fn := range importToGroup {
		if n, ok := fn(importPath); ok {
			return n
		}
	}
	return 0
}

func fixImports(f *ast.File) (added []string, err error) {
	// refs are a set of possible package references currently unsatisified by imports.
	// first key: either base package (e.g. "fmt") or renamed package
	// second key: referenced package symbol (e.g. "Println")
	refs := make(map[string]map[string]bool)

	// decls are the current package imports. key is base package or renamed package.
	decls := make(map[string]*ast.ImportSpec)

	// collect potential uses of packages.
	var visitor visitFn
	visitor = visitFn(func(node ast.Node) ast.Visitor {
		if node == nil {
			return visitor
		}
		switch v := node.(type) {
		case *ast.ImportSpec:
			if v.Name != nil {
				decls[v.Name.Name] = v
			} else {
				local := importPathToName(strings.Trim(v.Path.Value, `\"`))
				decls[local] = v
			}
		case *ast.SelectorExpr:
			xident, ok := v.X.(*ast.Ident)
			if !ok {
				break
			}
			if xident.Obj != nil {
				// if the parser can resolve it, it's not a package ref
				break
			}
			pkgName := xident.Name
			if refs[pkgName] == nil {
				refs[pkgName] = make(map[string]bool)
			}
			if decls[pkgName] == nil {
				refs[pkgName][v.Sel.Name] = true
			}
		}
		return visitor
	})
	ast.Walk(visitor, f)

	// Search for imports matching potential package references.
	searches := 0
	type result struct {
		ipath string
		err   error
	}
	results := make(chan result)
	for pkgName, symbols := range refs {
		if len(symbols) == 0 {
			continue // skip over packages already imported
		}
		go func(pkgName string, symbols map[string]bool) {
			ipath, err := findImport(pkgName, symbols)
			results <- result{ipath, err}
		}(pkgName, symbols)
		searches++
	}
	for i := 0; i < searches; i++ {
		result := <-results
		if result.err != nil {
			return nil, result.err
		}
		if result.ipath != "" {
			astutil.AddImport(fset, f, result.ipath)
			added = append(added, result.ipath)
		}
	}

	// Nil out any unused ImportSpecs, to be removed in following passes
	unusedImport := map[string]bool{}
	for pkg, is := range decls {
		if refs[pkg] == nil && pkg != "_" && pkg != "." {
			unusedImport[strings.Trim(is.Path.Value, `"`)] = true
		}
	}
	for ipath := range unusedImport {
		if ipath == "C" {
			// Don't remove cgo stuff.
			continue
		}
		astutil.DeleteImport(fset, f, ipath)
	}

	return added, nil
}

// importPathToName returns the package name for the given import path.
var importPathToName = importPathToNameGoPath

// importPathToNameBasic assumes the package name is the base of import path.
func importPathToNameBasic(importPath string) (packageName string) {
	return path.Base(importPath)
}

// importPathToNameGoPath finds out the actual package name, as declared in its .go files.
// If there's a problem, it falls back to using importPathToNameBasic.
func importPathToNameGoPath(importPath string) (packageName string) {
	if buildPkg, err := build.Import(importPath, "", 0); err == nil {
		return buildPkg.Name
	} else {
		return importPathToNameBasic(importPath)
	}
}

type pkg struct {
	importpath string // full pkg import path, e.g. "net/http"
	dir        string // absolute file path to pkg directory e.g. "/usr/lib/go/src/fmt"
}

var pkgIndexOnce sync.Once

var pkgIndex struct {
	sync.Mutex
	m map[string][]pkg // shortname => []pkg, e.g "http" => "net/http"
}

func loadPkgIndex() {
	pkgIndex.Lock()
	pkgIndex.m = make(map[string][]pkg)
	pkgIndex.Unlock()

	var wg sync.WaitGroup
	for _, path := range build.Default.SrcDirs() {
		f, err := os.Open(path)
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		children, err := f.Readdir(-1)
		f.Close()
		if err != nil {
			fmt.Fprint(os.Stderr, err)
			continue
		}
		for _, child := range children {
			if child.IsDir() {
				wg.Add(1)
				go func(path, name string) {
					defer wg.Done()
					loadPkg(&wg, path, name)
				}(path, child.Name())
			}
		}
	}
	wg.Wait()
}

var fset = token.NewFileSet()

func loadPkg(wg *sync.WaitGroup, root, pkgrelpath string) {
	importpath := filepath.ToSlash(pkgrelpath)
	shortName := importPathToName(importpath)

	dir := filepath.Join(root, importpath)
	pkgIndex.Lock()
	pkgIndex.m[shortName] = append(pkgIndex.m[shortName], pkg{
		importpath: importpath,
		dir:        dir,
	})
	pkgIndex.Unlock()

	pkgDir, err := os.Open(dir)
	if err != nil {
		return
	}
	children, err := pkgDir.Readdir(-1)
	pkgDir.Close()
	if err != nil {
		return
	}
	for _, child := range children {
		name := child.Name()
		if name == "" {
			continue
		}
		if c := name[0]; c == '.' || ('0' <= c && c <= '9') {
			continue
		}
		if child.IsDir() {
			wg.Add(1)
			go func(root, name string) {
				defer wg.Done()
				loadPkg(wg, root, name)
			}(root, filepath.Join(importpath, name))
		}
	}
}

// loadExports returns a list exports for a package.
var loadExports = loadExportsGoPath

func loadExportsGoPath(dir string) map[string]bool {
	exports := make(map[string]bool)
	buildPkg, err := build.ImportDir(dir, 0)
	if err != nil {
		if strings.Contains(err.Error(), "no buildable Go source files in") {
			return nil
		}
		fmt.Fprintf(os.Stderr, "could not import %q: %v", dir, err)
		return nil
	}
	for _, file := range buildPkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(dir, file), nil, 0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "could not parse %q: %v", file, err)
			continue
		}
		for name := range f.Scope.Objects {
			if ast.IsExported(name) {
				exports[name] = true
			}
		}
	}
	return exports
}

// findImport searches for a package with the given symbols.
// If no package is found, findImport returns "".
// Declared as a variable rather than a function so goimports can be easily
// extended by adding a file with an init function.
var findImport = findImportGoPath

func findImportGoPath(pkgName string, symbols map[string]bool) (string, error) {

	pkgIndexOnce.Do(loadPkgIndex)

	// Collect exports for packages with matching names.
	var wg sync.WaitGroup
	var pkgsMu sync.Mutex // guards pkgs
	// full importpath => exported symbol => True
	// e.g. "net/http" => "Client" => True
	pkgs := make(map[string]map[string]bool)
	pkgIndex.Lock()
	for _, pkg := range pkgIndex.m[pkgName] {
		wg.Add(1)
		go func(importpath, dir string) {
			defer wg.Done()
			exports := loadExports(dir)
			if exports != nil {
				pkgsMu.Lock()
				pkgs[importpath] = exports
				pkgsMu.Unlock()
			}
		}(pkg.importpath, pkg.dir)
	}
	pkgIndex.Unlock()
	wg.Wait()

	// Filter out packages missing required exported symbols.
	for symbol := range symbols {
		for importpath, exports := range pkgs {
			if !exports[symbol] {
				delete(pkgs, importpath)
			}
		}
	}
	if len(pkgs) == 0 {
		return "", nil
	}

	// If there are multiple candidate packages, the shortest one wins.
	// This is a heuristic to prefer the standard library (e.g. "bytes")
	// over e.g. "github.com/foo/bar/bytes".
	shortest := ""
	for importPath := range pkgs {
		if shortest == "" || len(importPath) < len(shortest) {
			shortest = importPath
		}
	}
	return shortest, nil
}

type visitFn func(node ast.Node) ast.Visitor

func (fn visitFn) Visit(node ast.Node) ast.Visitor {
	return fn(node)
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package libvmi

import (
	kvirtv1 "kubevirt.io/api/core/v1"

	cd "kubevirt.io/kubevirt/tests/containerdisk"
	"kubevirt.io/kubevirt/tests/framework/checks"
	"kubevirt.io/kubevirt/tests/testsuite"
)

// Default VMI values
const (
	DefaultTestGracePeriod int64 = 0
	DefaultVmiName               = "testvmi"
)

// NewFedora instantiates a new Fedora based VMI configuration,
// building its extra properties based on the specified With* options.
// This image has tooling for the guest agent, stress, SR-IOV and more.
func NewFedora(opts ...Option) *kvirtv1.VirtualMachineInstance {
	fedoraOptions := []Option{
		WithTerminationGracePeriod(DefaultTestGracePeriod),
		WithResourceMemory("512M"),
		WithRng(),
		WithContainerImage(cd.ContainerDiskFor(cd.ContainerDiskFedoraTestTooling)),
	}
	opts = append(fedoraOptions, opts...)
	return New(RandName(DefaultVmiName), opts...)
}

// NewCirros instantiates a new CirrOS based VMI configuration
func NewCirros(opts ...Option) *kvirtv1.VirtualMachineInstance {
	// Supplied with no user data, Cirros image takes 230s to allow login
	withNonEmptyUserData := WithCloudInitNoCloudUserData("#!/bin/bash\necho hello\n", true)

	cirrosOpts := []Option{
		WithContainerImage(cd.ContainerDiskFor(cd.ContainerDiskCirros)),
		withNonEmptyUserData,
		WithResourceMemory(cirrosMemory()),
		WithTerminationGracePeriod(DefaultTestGracePeriod),
	}
	cirrosOpts = append(cirrosOpts, opts...)
	return New(RandName(DefaultVmiName), cirrosOpts...)
}

// NewAlpine instantiates a new Alpine based VMI configuration
func NewAlpine(opts ...Option) *kvirtv1.VirtualMachineInstance {
	alpineOpts := []Option{
		WithContainerImage(cd.ContainerDiskFor(cd.ContainerDiskAlpine)),
		WithResourceMemory("128Mi"),
		WithRng(),
		WithTerminationGracePeriod(DefaultTestGracePeriod),
	}
	alpineOpts = append(alpineOpts, opts...)
	return New(RandName(DefaultVmiName), alpineOpts...)
}

func NewAlpineWithTestTooling(opts ...Option) *kvirtv1.VirtualMachineInstance {
	alpineOpts := []Option{
		WithContainerImage(cd.ContainerDiskFor(cd.ContainerDiskAlpineTestTooling)),
		WithResourceMemory("128Mi"),
		WithRng(),
		WithTerminationGracePeriod(DefaultTestGracePeriod),
	}
	alpineOpts = append(alpineOpts, opts...)
	return New(RandName(DefaultVmiName), alpineOpts...)
}

func cirrosMemory() string {
	if checks.IsARM64(testsuite.Arch) {
		return "256Mi"
	}
	return "128Mi"
}
<commit_msg>tests, libvmi: Support Alpine VM/s on ARM64<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package libvmi

import (
	kvirtv1 "kubevirt.io/api/core/v1"

	cd "kubevirt.io/kubevirt/tests/containerdisk"
	"kubevirt.io/kubevirt/tests/framework/checks"
	"kubevirt.io/kubevirt/tests/testsuite"
)

// Default VMI values
const (
	DefaultTestGracePeriod int64 = 0
	DefaultVmiName               = "testvmi"
)

// NewFedora instantiates a new Fedora based VMI configuration,
// building its extra properties based on the specified With* options.
// This image has tooling for the guest agent, stress, SR-IOV and more.
func NewFedora(opts ...Option) *kvirtv1.VirtualMachineInstance {
	fedoraOptions := []Option{
		WithTerminationGracePeriod(DefaultTestGracePeriod),
		WithResourceMemory("512M"),
		WithRng(),
		WithContainerImage(cd.ContainerDiskFor(cd.ContainerDiskFedoraTestTooling)),
	}
	opts = append(fedoraOptions, opts...)
	return New(RandName(DefaultVmiName), opts...)
}

// NewCirros instantiates a new CirrOS based VMI configuration
func NewCirros(opts ...Option) *kvirtv1.VirtualMachineInstance {
	// Supplied with no user data, Cirros image takes 230s to allow login
	withNonEmptyUserData := WithCloudInitNoCloudUserData("#!/bin/bash\necho hello\n", true)

	cirrosOpts := []Option{
		WithContainerImage(cd.ContainerDiskFor(cd.ContainerDiskCirros)),
		withNonEmptyUserData,
		WithResourceMemory(cirrosMemory()),
		WithTerminationGracePeriod(DefaultTestGracePeriod),
	}
	cirrosOpts = append(cirrosOpts, opts...)
	return New(RandName(DefaultVmiName), cirrosOpts...)
}

// NewAlpine instantiates a new Alpine based VMI configuration
func NewAlpine(opts ...Option) *kvirtv1.VirtualMachineInstance {
	alpineMemory := cirrosMemory
	alpineOpts := []Option{
		WithContainerImage(cd.ContainerDiskFor(cd.ContainerDiskAlpine)),
		WithResourceMemory(alpineMemory()),
		WithRng(),
		WithTerminationGracePeriod(DefaultTestGracePeriod),
	}
	alpineOpts = append(alpineOpts, opts...)
	return New(RandName(DefaultVmiName), alpineOpts...)
}

func NewAlpineWithTestTooling(opts ...Option) *kvirtv1.VirtualMachineInstance {
	alpineMemory := cirrosMemory
	alpineOpts := []Option{
		WithContainerImage(cd.ContainerDiskFor(cd.ContainerDiskAlpineTestTooling)),
		WithResourceMemory(alpineMemory()),
		WithRng(),
		WithTerminationGracePeriod(DefaultTestGracePeriod),
	}
	alpineOpts = append(alpineOpts, opts...)
	return New(RandName(DefaultVmiName), alpineOpts...)
}

func cirrosMemory() string {
	if checks.IsARM64(testsuite.Arch) {
		return "256Mi"
	}
	return "128Mi"
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	fmtFlag   = flag.NewFlagSet("goimps fmt flags", 2)
	write     = fmtFlag.Bool("w", false, "write result to (source) file instead of stdout")
	diff      = fmtFlag.Bool("d", false, "display diffs instead of rewriting files")
	list      = fmtFlag.Bool("l", false, "list files whose formatting differs from gofmt's")
	allErrors = fmtFlag.Bool("e", false, "report all errors (not just the first 10 on different lines")
	comments  = fmtFlag.Bool("comments", true, "print comments")
	useTab    = fmtFlag.Bool("tabs", true, "indent with tabs")
	tabWidth  = fmtFlag.Int("tabwidth", 8, "tab width")
	autodrop  = fmtFlag.Bool("D", true, "Automatically drop unused imports")
)

func cmdFmt(stdin io.Reader, stdout, stderr io.Writer, args []string) int {
	defer func() {
		*write = false
		*diff = false
		*list = false
		*allErrors = false
		*comments = true
		*useTab = true
		*tabWidth = 8
	}()

	fmtFlag.Parse(args)

	if fmtFlag.NArg() == 0 {
		err := doFmtFile("", stdin, stdout)
		if err != nil {
			fmt.Fprintln(stderr, err.Error())
			return 1
		}
		return 0
	}

	for i := 0; i < fmtFlag.NArg(); i++ {
		p := fmtFlag.Arg(i)
		fi, err := os.Stat(p)
		if err != nil {
			fmt.Fprintln(stderr, err.Error())
			return 1
		}

		if fi.IsDir() {
			err := doFmtDir(p, stdout)
			if err != nil {
				fmt.Fprintln(stderr, err.Error())
				return 1
			}
		} else {
			err := doFmtFile(p, nil, stdout)
			if err != nil {
				fmt.Fprintln(stdout, err.Error())
				return 1
			}
		}
	}

	return 0
}

func doFmtDir(root string, stdout io.Writer) error {
	return filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
		if fi.IsDir() {
			if root == path {
				return nil
			}

			return filepath.SkipDir
		}

		if filepath.Ext(fi.Name()) == ".go" && !strings.HasPrefix(fi.Name(), ".") {
			return doFmtFile(path, nil, stdout)
		}

		return nil
	})
}

func doFmtFile(filename string, stdin io.Reader, stdout io.Writer) error {
	var in io.Reader
	if filename == "" && stdin != nil {
		filename = "<standard input>"
		in = stdin
	} else {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	// Parse file
	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	fset := token.NewFileSet()
	parserMode := parser.Mode(0)
	if *comments {
		parserMode |= parser.ParseComments
	}
	if *allErrors {
		parserMode |= parser.AllErrors
	}
	f, err := parser.ParseFile(fset, filename, src, parserMode)
	if err != nil {
		return err
	}

	if *autodrop {
	// Drop unused imports
	unused, err := getUnused(filename, src)
	if err != nil {
		return err
	}

	ast.SortImports(fset, f)
	for i, decl := range f.Decls {
		gen, ok := decl.(*ast.GenDecl)
		if !ok || gen.Tok != token.IMPORT {
			continue
		}

		for _, u := range unused {
			deleteImportSpec(fset, gen, u.path)
		}

		if len(gen.Specs) == 0 {
			f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
		}
	}
	}

	// Print file
	printerMode := printer.UseSpaces
	if *useTab {
		printerMode |= printer.TabIndent
	}
	cfg := &printer.Config{
		Mode:     printerMode,
		Tabwidth: *tabWidth,
	}
	var buf bytes.Buffer
	err = cfg.Fprint(&buf, fset, f)
	if err != nil {
		return err
	}

	res := buf.Bytes()

	if !bytes.Equal(src, res) {
		// always output to stdout
		if *list {
			fmt.Fprintln(stdout, filename)
		}

		// always output to stdout
		if *diff {
			// Format
			// ------
			// $ gofmt -d
			// package     main
			//
			// diff <standard input> gofmt/<standard input>
			// --- /var/folders/f8/0gm3xlgn1q12_zt7kxmzfj480000gn/T/gofmt406409093     2014-08-11 22:34:31.000000000 +0900
			// +++ /var/folders/f8/0gm3xlgn1q12_zt7kxmzfj480000gn/T/gofmt119974176     2014-08-11 22:34:31.000000000 +0900
			// @@ -1,2 +1 @@
			// -package     main
			// -
			// +package main
			_diff, err := diffBytes(src, res)
			if err != nil {
				return err
			}
			fmt.Fprintf(stdout, "diff %s gofmt/%s\n", filename, filename)
			stdout.Write(_diff)
		}

		if *write {
			err = ioutil.WriteFile(filename, res, 0)
			if err != nil {
				return err
			}
		}
	}

	if !*list && !*diff && !*write {
		stdout.Write(res)
	}

	return nil
}

func deleteImportSpec(fset *token.FileSet, gen *ast.GenDecl, path string) {
	for j, spec := range gen.Specs {
		impspec := spec.(*ast.ImportSpec)

		if strings.Trim(impspec.Path.Value, "`"+`"`) != path {
			continue
		}

		// Drop
		gen.Specs = append(gen.Specs[:j], gen.Specs[j+1:]...)

		// Align
		if j == 0 {
			return
		}
		lastImpspec := gen.Specs[j-1].(*ast.ImportSpec)
		lastLine := fset.Position(lastImpspec.Path.ValuePos).Line
		line := fset.Position(impspec.Path.ValuePos).Line
		if line == lastLine+1 { // there is no blank line between last and current
			// import (
			//     "fmt" // EndPos=2
			//     "os"  // EndPos=3
			// )
			// ->
			// import (
			//     "fmt" // EndPos=3
			// )
			lastImpspec.EndPos = impspec.End()
		}

		return
	}
}

func diffBytes(a, b []byte) ([]byte, error) {
	f1, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return nil, err
	}
	defer os.Remove(f1.Name())
	defer f1.Close()

	f2, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return nil, err
	}
	defer os.Remove(f2.Name())
	defer f2.Close()

	f1.Write(a)
	f2.Write(b)

	return cmdDiff(f1.Name(), f2.Name())
}

func cmdDiff(f1, f2 string) ([]byte, error) {
	d, err := exec.Command("diff", "-u", f1, f2).CombinedOutput()
	if err != nil && len(d) == 0 {
		return nil, err
	}

	return d, nil
}
<commit_msg>gofmt<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

var (
	fmtFlag   = flag.NewFlagSet("goimps fmt flags", 2)
	write     = fmtFlag.Bool("w", false, "write result to (source) file instead of stdout")
	diff      = fmtFlag.Bool("d", false, "display diffs instead of rewriting files")
	list      = fmtFlag.Bool("l", false, "list files whose formatting differs from gofmt's")
	allErrors = fmtFlag.Bool("e", false, "report all errors (not just the first 10 on different lines")
	comments  = fmtFlag.Bool("comments", true, "print comments")
	useTab    = fmtFlag.Bool("tabs", true, "indent with tabs")
	tabWidth  = fmtFlag.Int("tabwidth", 8, "tab width")
	autodrop  = fmtFlag.Bool("D", true, "Automatically drop unused imports")
)

func cmdFmt(stdin io.Reader, stdout, stderr io.Writer, args []string) int {
	defer func() {
		*write = false
		*diff = false
		*list = false
		*allErrors = false
		*comments = true
		*useTab = true
		*tabWidth = 8
	}()

	fmtFlag.Parse(args)

	if fmtFlag.NArg() == 0 {
		err := doFmtFile("", stdin, stdout)
		if err != nil {
			fmt.Fprintln(stderr, err.Error())
			return 1
		}
		return 0
	}

	for i := 0; i < fmtFlag.NArg(); i++ {
		p := fmtFlag.Arg(i)
		fi, err := os.Stat(p)
		if err != nil {
			fmt.Fprintln(stderr, err.Error())
			return 1
		}

		if fi.IsDir() {
			err := doFmtDir(p, stdout)
			if err != nil {
				fmt.Fprintln(stderr, err.Error())
				return 1
			}
		} else {
			err := doFmtFile(p, nil, stdout)
			if err != nil {
				fmt.Fprintln(stdout, err.Error())
				return 1
			}
		}
	}

	return 0
}

func doFmtDir(root string, stdout io.Writer) error {
	return filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
		if fi.IsDir() {
			if root == path {
				return nil
			}

			return filepath.SkipDir
		}

		if filepath.Ext(fi.Name()) == ".go" && !strings.HasPrefix(fi.Name(), ".") {
			return doFmtFile(path, nil, stdout)
		}

		return nil
	})
}

func doFmtFile(filename string, stdin io.Reader, stdout io.Writer) error {
	var in io.Reader
	if filename == "" && stdin != nil {
		filename = "<standard input>"
		in = stdin
	} else {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	// Parse file
	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	fset := token.NewFileSet()
	parserMode := parser.Mode(0)
	if *comments {
		parserMode |= parser.ParseComments
	}
	if *allErrors {
		parserMode |= parser.AllErrors
	}
	f, err := parser.ParseFile(fset, filename, src, parserMode)
	if err != nil {
		return err
	}

	if *autodrop {
		// Drop unused imports
		unused, err := getUnused(filename, src)
		if err != nil {
			return err
		}

		ast.SortImports(fset, f)
		for i, decl := range f.Decls {
			gen, ok := decl.(*ast.GenDecl)
			if !ok || gen.Tok != token.IMPORT {
				continue
			}

			for _, u := range unused {
				deleteImportSpec(fset, gen, u.path)
			}

			if len(gen.Specs) == 0 {
				f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
			}
		}
	}

	// Print file
	printerMode := printer.UseSpaces
	if *useTab {
		printerMode |= printer.TabIndent
	}
	cfg := &printer.Config{
		Mode:     printerMode,
		Tabwidth: *tabWidth,
	}
	var buf bytes.Buffer
	err = cfg.Fprint(&buf, fset, f)
	if err != nil {
		return err
	}

	res := buf.Bytes()

	if !bytes.Equal(src, res) {
		// always output to stdout
		if *list {
			fmt.Fprintln(stdout, filename)
		}

		// always output to stdout
		if *diff {
			// Format
			// ------
			// $ gofmt -d
			// package     main
			//
			// diff <standard input> gofmt/<standard input>
			// --- /var/folders/f8/0gm3xlgn1q12_zt7kxmzfj480000gn/T/gofmt406409093     2014-08-11 22:34:31.000000000 +0900
			// +++ /var/folders/f8/0gm3xlgn1q12_zt7kxmzfj480000gn/T/gofmt119974176     2014-08-11 22:34:31.000000000 +0900
			// @@ -1,2 +1 @@
			// -package     main
			// -
			// +package main
			_diff, err := diffBytes(src, res)
			if err != nil {
				return err
			}
			fmt.Fprintf(stdout, "diff %s gofmt/%s\n", filename, filename)
			stdout.Write(_diff)
		}

		if *write {
			err = ioutil.WriteFile(filename, res, 0)
			if err != nil {
				return err
			}
		}
	}

	if !*list && !*diff && !*write {
		stdout.Write(res)
	}

	return nil
}

func deleteImportSpec(fset *token.FileSet, gen *ast.GenDecl, path string) {
	for j, spec := range gen.Specs {
		impspec := spec.(*ast.ImportSpec)

		if strings.Trim(impspec.Path.Value, "`"+`"`) != path {
			continue
		}

		// Drop
		gen.Specs = append(gen.Specs[:j], gen.Specs[j+1:]...)

		// Align
		if j == 0 {
			return
		}
		lastImpspec := gen.Specs[j-1].(*ast.ImportSpec)
		lastLine := fset.Position(lastImpspec.Path.ValuePos).Line
		line := fset.Position(impspec.Path.ValuePos).Line
		if line == lastLine+1 { // there is no blank line between last and current
			// import (
			//     "fmt" // EndPos=2
			//     "os"  // EndPos=3
			// )
			// ->
			// import (
			//     "fmt" // EndPos=3
			// )
			lastImpspec.EndPos = impspec.End()
		}

		return
	}
}

func diffBytes(a, b []byte) ([]byte, error) {
	f1, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return nil, err
	}
	defer os.Remove(f1.Name())
	defer f1.Close()

	f2, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return nil, err
	}
	defer os.Remove(f2.Name())
	defer f2.Close()

	f1.Write(a)
	f2.Write(b)

	return cmdDiff(f1.Name(), f2.Name())
}

func cmdDiff(f1, f2 string) ([]byte, error) {
	d, err := exec.Command("diff", "-u", f1, f2).CombinedOutput()
	if err != nil && len(d) == 0 {
		return nil, err
	}

	return d, nil
}
<|endoftext|>


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

//
// GoGo library functions
//

package libgogo

var Argv [255]string;
var Argc uint64 = 0;

func GetArgv() {
    var fd uint64;
    var errno uint64;
    var char string = "#";
    var inArgv uint64 = 0;

    fd = FileOpen("/proc/self/cmdline", 0);
    if fd == 0 {
        ExitError("Error opening /proc/self/cmdline. Currently GoGo is only supported on systems with /proc enabled.", 1);
    }

    for errno = Read(fd, char, 1) ; errno != 0 ; errno = Read(fd, char, 1) {
        if char[0] == 0 {
            inArgv = 1;
            Argc = Argc + 1;
        } else {
            if inArgv == 1 {
                Argv[Argc] += string(char[0]); // (SC) TODO: Remove cast, str append
            }
        }
    }

    errno = FileClose(fd);
    if errno != 0 {
        ExitError("Error closing file /proc/self/cmdline",1);
    }
}

func StringAppend(str *string, char byte) { //TODO (SC): Get rid of magic string concatenation (needs memory management!) and cast operator
  *str += string(char);
}

func Min(a uint64, b uint64) uint64 {
    var result uint64 = b;
    if a < b {
        result = a;
    }
    return result;
}

func StringLength(str string) uint64;

func StringCompare(str1 string, str2 string) uint64 {
    var i uint64;
    var equal uint64 = 0;
    var strlen1 uint64 = StringLength(str1);
    var strlen2 uint64 = StringLength(str2);
    if strlen1 != strlen2 {
       equal = 1;
    } else {
        for i = 0; i < strlen1; i = i +1 {
            if str1[i] != str2[i] {
                equal = 1;
            }
        }
    }
    return equal;
}

func ToIntFromByte(b byte) uint64;

func ToByteFromInt(i uint64) byte;

func StringToInt(str string) uint64 {
    var n uint64 = StringLength(str);
    var i uint64;
    var val uint64 = 0;
    for i = 0; i < n ; i = i +1 {
        val = val * 10;
        val = val + ToIntFromByte(str[i]) - 48;
    }
    return val;
}

func Exit(code uint64);

func ExitError(msg string, code uint64) {
    PrintString(msg);
    PrintChar('\n');
    Exit(code);
}

func Write(fd uint64, text string, length uint64) uint64;

func PrintString(msg string) {
    Write(1, msg, StringLength(msg));
}

func PrintChar(char byte);

func PrintNumber(num uint64) {
    var i uint64;
    var buf [255]byte;
    for i = 0; num != 0; i = i +1 {
        buf[i] = ToByteFromInt(num - (num / 10) * 10 + 48);
        num = num / 10;
    }
    if i == 0 { //Special case: 0
        buf[0] = 48;
        i = 1;
    } else {
        i = i -1;
    }
    for ; i != 0; i = i -1 {
        PrintChar(buf[i]);
    }
    PrintChar(buf[0]);
}

func Read(fd uint64, buffer string, buffer_size uint64) uint64;

func GetChar(fd uint64) byte;

func FileOpen(filename string, flags uint64) uint64;

func FileClose(fd uint64) uint64;
<commit_msg>libgogo: Fix GetArgv (Argc index was wrong)<commit_after>// Copyright 2009 The GoGo Authors. All rights reserved.
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

//
// GoGo library functions
//

package libgogo

var Argv [255]string;
var Argc uint64 = 0;

func GetArgv() {
    var fd uint64;
    var errno uint64;
    var char string = "#";

    fd = FileOpen("/proc/self/cmdline", 0);
    if fd == 0 {
        ExitError("Error opening /proc/self/cmdline. Currently GoGo is only supported on systems with /proc enabled.", 1);
    }

    for errno = Read(fd, char, 1) ; errno != 0 ; errno = Read(fd, char, 1) {
        if char[0] == 0 {
            Argc = Argc + 1;
        } else {
            Argv[Argc] += string(char[0]); // (SC) TODO: Remove cast, str append
        }
    }

    errno = FileClose(fd);
    if errno != 0 {
        ExitError("Error closing file /proc/self/cmdline",1);
    }
}

func StringAppend(str *string, char byte) { //TODO (SC): Get rid of magic string concatenation (needs memory management!) and cast operator
  *str += string(char);
}

func Min(a uint64, b uint64) uint64 {
    var result uint64 = b;
    if a < b {
        result = a;
    }
    return result;
}

func StringLength(str string) uint64;

func StringCompare(str1 string, str2 string) uint64 {
    var i uint64;
    var equal uint64 = 0;
    var strlen1 uint64 = StringLength(str1);
    var strlen2 uint64 = StringLength(str2);
    if strlen1 != strlen2 {
       equal = 1;
    } else {
        for i = 0; i < strlen1; i = i +1 {
            if str1[i] != str2[i] {
                equal = 1;
            }
        }
    }
    return equal;
}

func ToIntFromByte(b byte) uint64;

func ToByteFromInt(i uint64) byte;

func StringToInt(str string) uint64 {
    var n uint64 = StringLength(str);
    var i uint64;
    var val uint64 = 0;
    for i = 0; i < n ; i = i +1 {
        val = val * 10;
        val = val + ToIntFromByte(str[i]) - 48;
    }
    return val;
}

func Exit(code uint64);

func ExitError(msg string, code uint64) {
    PrintString(msg);
    PrintChar('\n');
    Exit(code);
}

func Write(fd uint64, text string, length uint64) uint64;

func PrintString(msg string) {
    Write(1, msg, StringLength(msg));
}

func PrintChar(char byte);

func PrintNumber(num uint64) {
    var i uint64;
    var buf [255]byte;
    for i = 0; num != 0; i = i +1 {
        buf[i] = ToByteFromInt(num - (num / 10) * 10 + 48);
        num = num / 10;
    }
    if i == 0 { //Special case: 0
        buf[0] = 48;
        i = 1;
    } else {
        i = i -1;
    }
    for ; i != 0; i = i -1 {
        PrintChar(buf[i]);
    }
    PrintChar(buf[0]);
}

func Read(fd uint64, buffer string, buffer_size uint64) uint64;

func GetChar(fd uint64) byte;

func FileOpen(filename string, flags uint64) uint64;

func FileClose(fd uint64) uint64;
<|endoftext|>


<commit_before>// Modeling of columns

package sqlbuilder

import (
	"bytes"
	"regexp"

	"github.com/dropbox/godropbox/errors"
)

// XXX: Maybe add UIntColumn

// Representation of a table for query generation
type Column interface {
	isProjectionInterface

	Name() string
	// Serialization for use in column lists
	SerializeSqlForColumnList(out *bytes.Buffer) error
	// Serialization for use in an expression (Clause)
	SerializeSql(out *bytes.Buffer) error

	// Internal function for tracking table that a column belongs to
	// for the purpose of serialization
	setTableName(table string) error
}

// A column that can be refer to outside of the projection list
type NonAliasColumn interface {
	Column
	isOrderByClauseInterface
	isExpressionInterface
}

type Collation string

const (
	UTF8CaseInsensitive Collation = "utf8_unicode_ci"
	UTF8CaseSensitive   Collation = "utf8_unicode"
)

// Representation of MySQL charsets
type Charset string

const (
	UTF8 Charset = "utf8"
)

// The base type for real materialized columns.
type baseColumn struct {
	isProjection
	isExpression
	name     string
	nullable bool
	table    string
}

func (c *baseColumn) Name() string {
	return c.name
}

func (c *baseColumn) setTableName(table string) error {
	c.table = table
	return nil
}

func (c *baseColumn) SerializeSqlForColumnList(out *bytes.Buffer) error {
	if c.table != "" {
		out.WriteByte('`')
		out.WriteString(c.table)
		out.WriteString("`.")
	}
	out.WriteString("`")
	out.WriteString(c.name)
	out.WriteByte('`')
	return nil
}

func (c *baseColumn) SerializeSql(out *bytes.Buffer) error {
	return c.SerializeSqlForColumnList(out)
}

type bytesColumn struct {
	baseColumn
	isExpression
}

// Representation of VARBINARY/BLOB columns
// This function will panic if name is not valid
func BytesColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in bytes column")
	}
	bc := &bytesColumn{}
	bc.name = name
	bc.nullable = nullable
	return bc
}

type stringColumn struct {
	baseColumn
	isExpression
	charset   Charset
	collation Collation
}

// Representation of VARCHAR/TEXT columns
// This function will panic if name is not valid
func StrColumn(
	name string,
	charset Charset,
	collation Collation,
	nullable bool) NonAliasColumn {

	if !validIdentifierName(name) {
		panic("Invalid column name in str column")
	}
	sc := &stringColumn{charset: charset, collation: collation}
	sc.name = name
	sc.nullable = nullable
	return sc
}

type dateTimeColumn struct {
	baseColumn
	isExpression
}

// Representation of DateTime columns
// This function will panic if name is not valid
func DateTimeColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in datetime column")
	}
	dc := &dateTimeColumn{}
	dc.name = name
	dc.nullable = nullable
	return dc
}

type integerColumn struct {
	baseColumn
	isExpression
}

// Representation of any integer column
// This function will panic if name is not valid
func IntColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in int column")
	}
	ic := &integerColumn{}
	ic.name = name
	ic.nullable = nullable
	return ic
}

type doubleColumn struct {
	baseColumn
	isExpression
}

// Representation of any double column
// This function will panic if name is not valid
func DoubleColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in int column")
	}
	ic := &doubleColumn{}
	ic.name = name
	ic.nullable = nullable
	return ic
}

type booleanColumn struct {
	baseColumn
	isExpression

	// XXX: Maybe allow isBoolExpression (for now, not included because
	// the deferred lookup equivalent can never be isBoolExpression)
}

// Representation of TINYINT used as a bool
// This function will panic if name is not valid
func BoolColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in bool column")
	}
	bc := &booleanColumn{}
	bc.name = name
	bc.nullable = nullable
	return bc
}

type aliasColumn struct {
	baseColumn
	expression Expression
}

func (c *aliasColumn) SerializeSql(out *bytes.Buffer) error {
	out.WriteByte('`')
	out.WriteString(c.name)
	out.WriteByte('`')
	return nil
}

func (c *aliasColumn) SerializeSqlForColumnList(out *bytes.Buffer) error {
	if !validIdentifierName(c.name) {
		return errors.Newf(
			"Invalid alias name `%s`.  Generated sql: %s",
			c.name,
			out.String())
	}
	if c.expression == nil {
		return errors.Newf(
			"Cannot alias a nil expression.  Generated sql: %s",
			out.String())
	}

	out.WriteByte('(')
	if c.expression == nil {
		return errors.Newf("nil alias clause.  Generate sql: %s", out.String())
	}
	if err := c.expression.SerializeSql(out); err != nil {
		return err
	}
	out.WriteString(") AS `")
	out.WriteString(c.name)
	out.WriteByte('`')
	return nil
}

func (c *aliasColumn) setTableName(table string) error {
	return errors.Newf(
		"Alias column '%s' should never have setTableName called on it",
		c.name)
}

// Representation of aliased clauses (expression AS name)
func Alias(name string, c Expression) Column {
	ac := &aliasColumn{}
	ac.name = name
	ac.expression = c
	return ac
}

// This is a strict subset of the actual restrictions, because
// teisenberger is lazy
var validIdentifierRegexp *regexp.Regexp = regexp.MustCompile("^[a-zA-Z_]\\w*$")

// Returns true if the given string is suitable as an identifier.
func validIdentifierName(name string) bool {
	return validIdentifierRegexp.MatchString(name)
}

// Pseudo Column type returned by table.C(name)
type deferredLookupColumn struct {
	isProjection
	isExpression
	table    *Table
	col_name string

	cachedColumn NonAliasColumn
}

func (c *deferredLookupColumn) Name() string {
	return c.col_name
}

func (c *deferredLookupColumn) SerializeSqlForColumnList(
	out *bytes.Buffer) error {

	return c.SerializeSql(out)
}

func (c *deferredLookupColumn) SerializeSql(out *bytes.Buffer) error {
	if c.cachedColumn != nil {
		return c.cachedColumn.SerializeSql(out)
	}

	col, err := c.table.getColumn(c.col_name)
	if err != nil {
		return err
	}

	c.cachedColumn = col
	return col.SerializeSql(out)
}

func (c *deferredLookupColumn) setTableName(table string) error {
	return errors.Newf(
		"Lookup column '%s' should never have setTableName called on it",
		c.col_name)
}
<commit_msg>[sqlbuilder] Fix misleading comment<commit_after>// Modeling of columns

package sqlbuilder

import (
	"bytes"
	"regexp"

	"github.com/dropbox/godropbox/errors"
)

// XXX: Maybe add UIntColumn

// Representation of a table for query generation
type Column interface {
	isProjectionInterface

	Name() string
	// Serialization for use in column lists
	SerializeSqlForColumnList(out *bytes.Buffer) error
	// Serialization for use in an expression (Clause)
	SerializeSql(out *bytes.Buffer) error

	// Internal function for tracking table that a column belongs to
	// for the purpose of serialization
	setTableName(table string) error
}

// A column that can be refer to outside of the projection list
type NonAliasColumn interface {
	Column
	isOrderByClauseInterface
	isExpressionInterface
}

type Collation string

const (
	UTF8CaseInsensitive Collation = "utf8_unicode_ci"
	UTF8CaseSensitive   Collation = "utf8_unicode"
)

// Representation of MySQL charsets
type Charset string

const (
	UTF8 Charset = "utf8"
)

// The base type for real materialized columns.
type baseColumn struct {
	isProjection
	isExpression
	name     string
	nullable bool
	table    string
}

func (c *baseColumn) Name() string {
	return c.name
}

func (c *baseColumn) setTableName(table string) error {
	c.table = table
	return nil
}

func (c *baseColumn) SerializeSqlForColumnList(out *bytes.Buffer) error {
	if c.table != "" {
		out.WriteByte('`')
		out.WriteString(c.table)
		out.WriteString("`.")
	}
	out.WriteString("`")
	out.WriteString(c.name)
	out.WriteByte('`')
	return nil
}

func (c *baseColumn) SerializeSql(out *bytes.Buffer) error {
	return c.SerializeSqlForColumnList(out)
}

type bytesColumn struct {
	baseColumn
	isExpression
}

// Representation of VARBINARY/BLOB columns
// This function will panic if name is not valid
func BytesColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in bytes column")
	}
	bc := &bytesColumn{}
	bc.name = name
	bc.nullable = nullable
	return bc
}

type stringColumn struct {
	baseColumn
	isExpression
	charset   Charset
	collation Collation
}

// Representation of VARCHAR/TEXT columns
// This function will panic if name is not valid
func StrColumn(
	name string,
	charset Charset,
	collation Collation,
	nullable bool) NonAliasColumn {

	if !validIdentifierName(name) {
		panic("Invalid column name in str column")
	}
	sc := &stringColumn{charset: charset, collation: collation}
	sc.name = name
	sc.nullable = nullable
	return sc
}

type dateTimeColumn struct {
	baseColumn
	isExpression
}

// Representation of DateTime columns
// This function will panic if name is not valid
func DateTimeColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in datetime column")
	}
	dc := &dateTimeColumn{}
	dc.name = name
	dc.nullable = nullable
	return dc
}

type integerColumn struct {
	baseColumn
	isExpression
}

// Representation of any integer column
// This function will panic if name is not valid
func IntColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in int column")
	}
	ic := &integerColumn{}
	ic.name = name
	ic.nullable = nullable
	return ic
}

type doubleColumn struct {
	baseColumn
	isExpression
}

// Representation of any double column
// This function will panic if name is not valid
func DoubleColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in int column")
	}
	ic := &doubleColumn{}
	ic.name = name
	ic.nullable = nullable
	return ic
}

type booleanColumn struct {
	baseColumn
	isExpression

	// XXX: Maybe allow isBoolExpression (for now, not included because
	// the deferred lookup equivalent can never be isBoolExpression)
}

// Representation of TINYINT used as a bool
// This function will panic if name is not valid
func BoolColumn(name string, nullable bool) NonAliasColumn {
	if !validIdentifierName(name) {
		panic("Invalid column name in bool column")
	}
	bc := &booleanColumn{}
	bc.name = name
	bc.nullable = nullable
	return bc
}

type aliasColumn struct {
	baseColumn
	expression Expression
}

func (c *aliasColumn) SerializeSql(out *bytes.Buffer) error {
	out.WriteByte('`')
	out.WriteString(c.name)
	out.WriteByte('`')
	return nil
}

func (c *aliasColumn) SerializeSqlForColumnList(out *bytes.Buffer) error {
	if !validIdentifierName(c.name) {
		return errors.Newf(
			"Invalid alias name `%s`.  Generated sql: %s",
			c.name,
			out.String())
	}
	if c.expression == nil {
		return errors.Newf(
			"Cannot alias a nil expression.  Generated sql: %s",
			out.String())
	}

	out.WriteByte('(')
	if c.expression == nil {
		return errors.Newf("nil alias clause.  Generate sql: %s", out.String())
	}
	if err := c.expression.SerializeSql(out); err != nil {
		return err
	}
	out.WriteString(") AS `")
	out.WriteString(c.name)
	out.WriteByte('`')
	return nil
}

func (c *aliasColumn) setTableName(table string) error {
	return errors.Newf(
		"Alias column '%s' should never have setTableName called on it",
		c.name)
}

// Representation of aliased clauses (expression AS name)
func Alias(name string, c Expression) Column {
	ac := &aliasColumn{}
	ac.name = name
	ac.expression = c
	return ac
}

// This is a strict subset of the actual allowed identifiers
var validIdentifierRegexp *regexp.Regexp = regexp.MustCompile("^[a-zA-Z_]\\w*$")

// Returns true if the given string is suitable as an identifier.
func validIdentifierName(name string) bool {
	return validIdentifierRegexp.MatchString(name)
}

// Pseudo Column type returned by table.C(name)
type deferredLookupColumn struct {
	isProjection
	isExpression
	table    *Table
	col_name string

	cachedColumn NonAliasColumn
}

func (c *deferredLookupColumn) Name() string {
	return c.col_name
}

func (c *deferredLookupColumn) SerializeSqlForColumnList(
	out *bytes.Buffer) error {

	return c.SerializeSql(out)
}

func (c *deferredLookupColumn) SerializeSql(out *bytes.Buffer) error {
	if c.cachedColumn != nil {
		return c.cachedColumn.SerializeSql(out)
	}

	col, err := c.table.getColumn(c.col_name)
	if err != nil {
		return err
	}

	c.cachedColumn = col
	return col.SerializeSql(out)
}

func (c *deferredLookupColumn) setTableName(table string) error {
	return errors.Newf(
		"Lookup column '%s' should never have setTableName called on it",
		c.col_name)
}
<|endoftext|>


<commit_before>//go:build go1.5
// +build go1.5

/*
Package jsonptrerror extends encoding/json.Decoder to return unmarshal errors
located with JSON Pointer (RFC 6901).

Requires Go 1.5 because it adds an 'Offset' field to type UnmarshalTypeError.
*/
package jsonptrerror

import (
	"bytes"
	"encoding/json"
	"io"

	"github.com/dolmen-go/jsonptr"
)

// UnmarshalTypeError is an extension of encoding/json.UnmarshalTypeError
// that also includes the error location as a JSON Pointer (RFC 6901).
type UnmarshalTypeError struct {
	json.UnmarshalTypeError
	Pointer jsonptr.Pointer
}

func (e UnmarshalTypeError) Error() string {
	return e.Pointer.String() + ": cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}

// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
//
// json.UnmarshalTypeError is translated to the extended jsonptrerror.UnmarshalTypeError.
func Unmarshal(document []byte, v interface{}) error {
	err := json.Unmarshal(document, v)
	return translateError(document, err)
}

// Decoder is the same as encoding/json.Decoder, except Decode returns
// our UnmarshalTypeError (providing a JSON Pointer) instead of encoding/json.UnmarshalTypeError.
type Decoder struct {
	decoder
	input bytes.Buffer
	err   error
}

// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	var d Decoder
	d.decoder = json.NewDecoder(io.TeeReader(r, &d.input))
	return &d
}

// Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
//
// The current implementation keeps a duplicate copy of the JSON document in memory.
func (d *Decoder) Decode(v interface{}) error {
	if d.err != nil {
		return d.err
	}
	d.err = d.decoder.Decode(v)
	if d.err != nil {
		d.err = translateError(d.input.Bytes(), d.err)
		d.decoder = nil
		d.input = bytes.Buffer{}
	}
	return d.err
}

func (d *Decoder) Token() (json.Token, error) {
	if d.err != nil {
		return nil, d.err
	}
	var tok json.Token
	tok, d.err = d.decoder.Token()
	if d.err != nil {
		d.err = translateError(d.input.Bytes(), d.err)
		d.decoder = nil
		d.input = bytes.Buffer{}
	}
	return tok, d.err
}

func translateError(document []byte, err error) error {
	if e, ok := err.(*json.UnmarshalTypeError); ok && e != nil {
		err = &UnmarshalTypeError{*e, pointerAtOffset(document, int(e.Offset))}
	}
	return err
}

// pointerAtOffset extracts the JSON Pointer at the start of a value in a *valid* JSON document.
func pointerAtOffset(input []byte, offset int) jsonptr.Pointer {
	var ptr jsonptr.Pointer
	i := 0
	type elem struct {
		index    int // -1 means the element is an object
		property []byte
	}
	var elemStack []elem
	var expectKey bool
	for {
		if i >= offset {
			for _, e := range elemStack {
				if e.index == -1 {
					var name string
					json.Unmarshal(e.property, &name)
					ptr.Property(name)
				} else {
					ptr.Index(e.index)
				}
			}
			break
		}
		switch input[i] {
		case '{':
			// push state of the new current object on the stack
			elemStack = append(elemStack, elem{index: -1})
			expectKey = true
		case '[':
			// push state of the new current array on the stack
			elemStack = append(elemStack, elem{index: 0})
		case '}', ']':
			elemStack = elemStack[:len(elemStack)-1] // pop
		case '"':
			j := i
		str:
			for {
				i++
				switch input[i] {
				case '\\':
					i++
				case '"':
					break str
				}
			}
			if expectKey {
				elemStack[len(elemStack)-1].property = input[j : i+1]
				expectKey = false
			}
		case ',':
			if elemStack[len(elemStack)-1].index == -1 {
				expectKey = true
			} else {
				elemStack[len(elemStack)-1].index++
			}
		}
		i++
	}
	return ptr
}
<commit_msg>doc: add doc links<commit_after>//go:build go1.5
// +build go1.5

/*
Package jsonptrerror extends [encoding/json.Decoder] to return unmarshal errors
located with JSON Pointer (RFC 6901).

Requires Go 1.5 because it adds an 'Offset' field to type [encoding/json.UnmarshalTypeError].
*/
package jsonptrerror

import (
	"bytes"
	"encoding/json"
	"io"

	"github.com/dolmen-go/jsonptr"
)

// UnmarshalTypeError is an extension of [encoding/json.UnmarshalTypeError]
// that also includes the error location as a JSON Pointer (RFC 6901).
type UnmarshalTypeError struct {
	json.UnmarshalTypeError
	Pointer jsonptr.Pointer
}

func (e UnmarshalTypeError) Error() string {
	return e.Pointer.String() + ": cannot unmarshal " + e.Value + " into Go value of type " + e.Type.String()
}

// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
//
// [encoding/json.UnmarshalTypeError] is translated to the extended [UnmarshalTypeError].
func Unmarshal(document []byte, v interface{}) error {
	err := json.Unmarshal(document, v)
	return translateError(document, err)
}

// Decoder is the same as [encoding/json.Decoder], except Decode returns
// our [UnmarshalTypeError] (providing a JSON Pointer) instead of [encoding/json.UnmarshalTypeError].
type Decoder struct {
	decoder
	input bytes.Buffer
	err   error
}

// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	var d Decoder
	d.decoder = json.NewDecoder(io.TeeReader(r, &d.input))
	return &d
}

// Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
//
// The current implementation keeps a duplicate copy of the JSON document in memory.
func (d *Decoder) Decode(v interface{}) error {
	if d.err != nil {
		return d.err
	}
	d.err = d.decoder.Decode(v)
	if d.err != nil {
		d.err = translateError(d.input.Bytes(), d.err)
		d.decoder = nil
		d.input = bytes.Buffer{}
	}
	return d.err
}

func (d *Decoder) Token() (json.Token, error) {
	if d.err != nil {
		return nil, d.err
	}
	var tok json.Token
	tok, d.err = d.decoder.Token()
	if d.err != nil {
		d.err = translateError(d.input.Bytes(), d.err)
		d.decoder = nil
		d.input = bytes.Buffer{}
	}
	return tok, d.err
}

func translateError(document []byte, err error) error {
	if e, ok := err.(*json.UnmarshalTypeError); ok && e != nil {
		err = &UnmarshalTypeError{*e, pointerAtOffset(document, int(e.Offset))}
	}
	return err
}

// pointerAtOffset extracts the JSON Pointer at the start of a value in a *valid* JSON document.
func pointerAtOffset(input []byte, offset int) jsonptr.Pointer {
	var ptr jsonptr.Pointer
	i := 0
	type elem struct {
		index    int // -1 means the element is an object
		property []byte
	}
	var elemStack []elem
	var expectKey bool
	for {
		if i >= offset {
			for _, e := range elemStack {
				if e.index == -1 {
					var name string
					json.Unmarshal(e.property, &name)
					ptr.Property(name)
				} else {
					ptr.Index(e.index)
				}
			}
			break
		}
		switch input[i] {
		case '{':
			// push state of the new current object on the stack
			elemStack = append(elemStack, elem{index: -1})
			expectKey = true
		case '[':
			// push state of the new current array on the stack
			elemStack = append(elemStack, elem{index: 0})
		case '}', ']':
			elemStack = elemStack[:len(elemStack)-1] // pop
		case '"':
			j := i
		str:
			for {
				i++
				switch input[i] {
				case '\\':
					i++
				case '"':
					break str
				}
			}
			if expectKey {
				elemStack[len(elemStack)-1].property = input[j : i+1]
				expectKey = false
			}
		case ',':
			if elemStack[len(elemStack)-1].index == -1 {
				expectKey = true
			} else {
				elemStack[len(elemStack)-1].index++
			}
		}
		i++
	}
	return ptr
}
<|endoftext|>


<commit_before>package ics

import (
	"errors"
	"io"
)

type Decoder struct {
	p *parser
}

func NewDecoder(r io.Reader) Decoder {
	return Decoder{
		p: newParser(r),
	}
}

func (d Decoder) Decode() (*Calendar, error) {
	p, err := d.p.GetProperty()
	if err != nil {
		return nil, err
	}
	if c, ok := p.(begin); ok && c == vCalendar {
		cal := new(Calendar)
		err = cal.decode(d)
		if err != nil {
			return nil, err
		}
		return cal, nil
	}
	return nil, ErrInvalidStart
}

func (d Decoder) readUnknownComponent(name string) error {
	for {
		p, err := d.p.GetProperty()
		if err != nil {
			return err
		}
		switch p := p.(type) {
		case begin:
			err = d.readUnknownComponent(string(p))
			if err != nil {
				return nil
			}
		case end:
			if string(p) == name {
				return nil
			}
			return ErrInvalidEnd
		}
	}
}

// Errors

var (
	ErrInvalidStart    = errors.New("invalid start component")
	ErrInvalidEnd      = errors.New("invalid end component")
	ErrMultipleUnique  = errors.New("multiple of a unique property")
	ErrRequiredMissing = errors.New("required property missing")
)
<commit_msg>added error<commit_after>package ics

import (
	"errors"
	"io"
)

type Decoder struct {
	p *parser
}

func NewDecoder(r io.Reader) Decoder {
	return Decoder{
		p: newParser(r),
	}
}

func (d Decoder) Decode() (*Calendar, error) {
	p, err := d.p.GetProperty()
	if err != nil {
		return nil, err
	}
	if c, ok := p.(begin); ok && c == vCalendar {
		cal := new(Calendar)
		err = cal.decode(d)
		if err != nil {
			return nil, err
		}
		return cal, nil
	}
	return nil, ErrInvalidStart
}

func (d Decoder) readUnknownComponent(name string) error {
	for {
		p, err := d.p.GetProperty()
		if err != nil {
			return err
		}
		switch p := p.(type) {
		case begin:
			err = d.readUnknownComponent(string(p))
			if err != nil {
				return nil
			}
		case end:
			if string(p) == name {
				return nil
			}
			return ErrInvalidEnd
		}
	}
}

// Errors

var (
	ErrInvalidStart                = errors.New("invalid start component")
	ErrInvalidEnd                  = errors.New("invalid end component")
	ErrMultipleUnique              = errors.New("multiple of a unique property")
	ErrRequiredMissing             = errors.New("required property missing")
	ErrInvalidComponentCombination = errors.New("invalid component combination")
)
<|endoftext|>